소스 검색

feat(core): Expose entityCustomFields query

Relates to #1848. This format allows us to support arbitrary entity names
when getting the custom field config in the Admin UI
Michael Bromley 2 년 전
부모
커밋
01f9d44753

+ 21 - 1
packages/admin-ui/src/lib/core/src/common/generated-types.ts

@@ -137,7 +137,12 @@ export type AdministratorPaymentInput = {
 };
 
 export type AdministratorRefundInput = {
-  amount: Scalars['Money']['input'];
+  /**
+   * The amount to be refunded to this particular Payment. This was introduced in
+   * v2.2.0 as the preferred way to specify the refund amount. The `lines`, `shipping` and `adjustment`
+   * fields will be removed in a future version.
+   */
+  amount?: InputMaybe<Scalars['Money']['input']>;
   paymentId: Scalars['ID']['input'];
   reason?: InputMaybe<Scalars['String']['input']>;
 };
@@ -1318,6 +1323,10 @@ export type CustomField = {
 
 export type CustomFieldConfig = BooleanCustomFieldConfig | DateTimeCustomFieldConfig | FloatCustomFieldConfig | IntCustomFieldConfig | LocaleStringCustomFieldConfig | LocaleTextCustomFieldConfig | RelationCustomFieldConfig | StringCustomFieldConfig | TextCustomFieldConfig;
 
+/**
+ * This type is deprecated in v2.2 in favor of the EntityCustomFields type,
+ * which allows custom fields to be defined on user-supplies entities.
+ */
 export type CustomFields = {
   __typename?: 'CustomFields';
   Address: Array<CustomFieldConfig>;
@@ -1563,6 +1572,12 @@ export type EmptyOrderLineSelectionError = ErrorResult & {
   message: Scalars['String']['output'];
 };
 
+export type EntityCustomFields = {
+  __typename?: 'EntityCustomFields';
+  customFields: Array<CustomFieldConfig>;
+  entityName: Scalars['String']['output'];
+};
+
 export enum ErrorCode {
   ALREADY_REFUNDED_ERROR = 'ALREADY_REFUNDED_ERROR',
   CANCEL_ACTIVE_ORDER_ERROR = 'CANCEL_ACTIVE_ORDER_ERROR',
@@ -5714,7 +5729,12 @@ export type SellerSortParameter = {
 
 export type ServerConfig = {
   __typename?: 'ServerConfig';
+  /**
+   * This field is deprecated in v2.2 in favor of the entityCustomFields field,
+   * which allows custom fields to be defined on user-supplies entities.
+   */
   customFieldConfig: CustomFields;
+  entityCustomFields: Array<EntityCustomFields>;
   moneyStrategyPrecision: Scalars['Int']['output'];
   orderProcess: Array<OrderProcessState>;
   permissions: Array<PermissionDefinition>;

+ 20 - 1
packages/asset-server-plugin/e2e/graphql/generated-e2e-asset-server-plugin-types.ts

@@ -129,7 +129,12 @@ export type AdministratorPaymentInput = {
 };
 
 export type AdministratorRefundInput = {
-  amount: Scalars['Money']['input'];
+  /**
+   * The amount to be refunded to this particular Payment. This was introduced in
+   * v2.2.0 as the preferred way to specify the refund amount. The `lines`, `shipping` and `adjustment`
+   * fields will be removed in a future version.
+   */
+  amount?: InputMaybe<Scalars['Money']['input']>;
   paymentId: Scalars['ID']['input'];
   reason?: InputMaybe<Scalars['String']['input']>;
 };
@@ -1273,6 +1278,10 @@ export type CustomField = {
 
 export type CustomFieldConfig = BooleanCustomFieldConfig | DateTimeCustomFieldConfig | FloatCustomFieldConfig | IntCustomFieldConfig | LocaleStringCustomFieldConfig | LocaleTextCustomFieldConfig | RelationCustomFieldConfig | StringCustomFieldConfig | TextCustomFieldConfig;
 
+/**
+ * This type is deprecated in v2.2 in favor of the EntityCustomFields type,
+ * which allows custom fields to be defined on user-supplies entities.
+ */
 export type CustomFields = {
   Address: Array<CustomFieldConfig>;
   Administrator: Array<CustomFieldConfig>;
@@ -1508,6 +1517,11 @@ export type EmptyOrderLineSelectionError = ErrorResult & {
   message: Scalars['String']['output'];
 };
 
+export type EntityCustomFields = {
+  customFields: Array<CustomFieldConfig>;
+  entityName: Scalars['String']['output'];
+};
+
 export enum ErrorCode {
   ALREADY_REFUNDED_ERROR = 'ALREADY_REFUNDED_ERROR',
   CANCEL_ACTIVE_ORDER_ERROR = 'CANCEL_ACTIVE_ORDER_ERROR',
@@ -5488,7 +5502,12 @@ export type SellerSortParameter = {
 };
 
 export type ServerConfig = {
+  /**
+   * This field is deprecated in v2.2 in favor of the entityCustomFields field,
+   * which allows custom fields to be defined on user-supplies entities.
+   */
   customFieldConfig: CustomFields;
+  entityCustomFields: Array<EntityCustomFields>;
   moneyStrategyPrecision: Scalars['Int']['output'];
   orderProcess: Array<OrderProcessState>;
   permissions: Array<PermissionDefinition>;

+ 21 - 1
packages/common/src/generated-types.ts

@@ -132,7 +132,12 @@ export type AdministratorPaymentInput = {
 };
 
 export type AdministratorRefundInput = {
-  amount: Scalars['Money']['input'];
+  /**
+   * The amount to be refunded to this particular Payment. This was introduced in
+   * v2.2.0 as the preferred way to specify the refund amount. The `lines`, `shipping` and `adjustment`
+   * fields will be removed in a future version.
+   */
+  amount?: InputMaybe<Scalars['Money']['input']>;
   paymentId: Scalars['ID']['input'];
   reason?: InputMaybe<Scalars['String']['input']>;
 };
@@ -1306,6 +1311,10 @@ export type CustomField = {
 
 export type CustomFieldConfig = BooleanCustomFieldConfig | DateTimeCustomFieldConfig | FloatCustomFieldConfig | IntCustomFieldConfig | LocaleStringCustomFieldConfig | LocaleTextCustomFieldConfig | RelationCustomFieldConfig | StringCustomFieldConfig | TextCustomFieldConfig;
 
+/**
+ * This type is deprecated in v2.2 in favor of the EntityCustomFields type,
+ * which allows custom fields to be defined on user-supplies entities.
+ */
 export type CustomFields = {
   __typename?: 'CustomFields';
   Address: Array<CustomFieldConfig>;
@@ -1551,6 +1560,12 @@ export type EmptyOrderLineSelectionError = ErrorResult & {
   message: Scalars['String']['output'];
 };
 
+export type EntityCustomFields = {
+  __typename?: 'EntityCustomFields';
+  customFields: Array<CustomFieldConfig>;
+  entityName: Scalars['String']['output'];
+};
+
 export enum ErrorCode {
   ALREADY_REFUNDED_ERROR = 'ALREADY_REFUNDED_ERROR',
   CANCEL_ACTIVE_ORDER_ERROR = 'CANCEL_ACTIVE_ORDER_ERROR',
@@ -5636,7 +5651,12 @@ export type SellerSortParameter = {
 
 export type ServerConfig = {
   __typename?: 'ServerConfig';
+  /**
+   * This field is deprecated in v2.2 in favor of the entityCustomFields field,
+   * which allows custom fields to be defined on user-supplies entities.
+   */
   customFieldConfig: CustomFields;
+  entityCustomFields: Array<EntityCustomFields>;
   moneyStrategyPrecision: Scalars['Int']['output'];
   orderProcess: Array<OrderProcessState>;
   permissions: Array<PermissionDefinition>;

+ 63 - 1
packages/core/e2e/custom-fields.e2e-spec.ts

@@ -260,6 +260,68 @@ describe('Custom fields', () => {
         });
     });
 
+    it('globalSettings.serverConfig.entityCustomFields', async () => {
+        const { globalSettings } = await adminClient.query(gql`
+            query {
+                globalSettings {
+                    serverConfig {
+                        entityCustomFields {
+                            entityName
+                            customFields {
+                                ... on CustomField {
+                                    name
+                                    type
+                                    list
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        `);
+
+        const productCustomFields = globalSettings.serverConfig.entityCustomFields.find(
+            e => e.entityName === 'Product',
+        );
+        expect(productCustomFields).toEqual({
+            entityName: 'Product',
+            customFields: [
+                { name: 'nullable', type: 'string', list: false },
+                { name: 'notNullable', type: 'string', list: false },
+                { name: 'stringWithDefault', type: 'string', list: false },
+                { name: 'localeStringWithDefault', type: 'localeString', list: false },
+                { name: 'intWithDefault', type: 'int', list: false },
+                { name: 'floatWithDefault', type: 'float', list: false },
+                { name: 'booleanWithDefault', type: 'boolean', list: false },
+                { name: 'dateTimeWithDefault', type: 'datetime', list: false },
+                { name: 'validateString', type: 'string', list: false },
+                { name: 'validateLocaleString', type: 'localeString', list: false },
+                { name: 'validateInt', type: 'int', list: false },
+                { name: 'validateFloat', type: 'float', list: false },
+                { name: 'validateDateTime', type: 'datetime', list: false },
+                { name: 'validateFn1', type: 'string', list: false },
+                { name: 'validateFn2', type: 'string', list: false },
+                { name: 'validateFn3', type: 'string', list: false },
+                { name: 'validateFn4', type: 'string', list: false },
+                { name: 'validateRelation', type: 'relation', list: false },
+                { name: 'stringWithOptions', type: 'string', list: false },
+                { name: 'nullableStringWithOptions', type: 'string', list: false },
+                { name: 'nonPublic', type: 'string', list: false },
+                { name: 'public', type: 'string', list: false },
+                { name: 'longString', type: 'string', list: false },
+                { name: 'longLocaleString', type: 'localeString', list: false },
+                { name: 'readonlyString', type: 'string', list: false },
+                { name: 'stringList', type: 'string', list: true },
+                { name: 'localeStringList', type: 'localeString', list: true },
+                { name: 'stringListWithDefault', type: 'string', list: true },
+                { name: 'intListWithValidation', type: 'int', list: true },
+                { name: 'uniqueString', type: 'string', list: false },
+                // The internal type should not be exposed at all
+                // { name: 'internalString', type: 'string' },
+            ],
+        });
+    });
+
     it('get nullable with no default', async () => {
         const { product } = await adminClient.query(gql`
             query {
@@ -470,7 +532,7 @@ describe('Custom fields', () => {
                         }
                     }
                 `);
-            }, 'The custom field "stringWithOptions" value ["tiny"] is invalid. Valid options are [\'small\', \'medium\', \'large\']'),
+            }, "The custom field \"stringWithOptions\" value [\"tiny\"] is invalid. Valid options are ['small', 'medium', 'large']"),
         );
 
         it('valid string option', async () => {

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 27 - 8
packages/core/e2e/graphql/generated-e2e-admin-types.ts


+ 14 - 0
packages/core/src/api/config/graphql-custom-fields.ts

@@ -235,15 +235,29 @@ export function addServerConfigCustomFields(
 ) {
     const schema = typeof typeDefsOrSchema === 'string' ? buildSchema(typeDefsOrSchema) : typeDefsOrSchema;
     const customFieldTypeDefs = `
+            """
+            This type is deprecated in v2.2 in favor of the EntityCustomFields type,
+            which allows custom fields to be defined on user-supplies entities.
+            """
             type CustomFields {
                 ${Object.keys(customFieldConfig).reduce(
                     (output, name) => output + name + ': [CustomFieldConfig!]!\n',
                     '',
                 )}
             }
+            
+            type EntityCustomFields {
+                entityName: String!
+                customFields: [CustomFieldConfig!]!
+            }
 
             extend type ServerConfig {
+                """
+                This field is deprecated in v2.2 in favor of the entityCustomFields field,
+                which allows custom fields to be defined on user-supplies entities.
+                """
                 customFieldConfig: CustomFields!
+                entityCustomFields: [EntityCustomFields!]!
             }
         `;
 

+ 29 - 1
packages/core/src/api/resolvers/admin/global-settings.resolver.ts

@@ -1,11 +1,13 @@
 import { Args, Info, Mutation, Query, ResolveField, Resolver } from '@nestjs/graphql';
 import {
     CustomFields as GraphQLCustomFields,
+    EntityCustomFields,
     MutationUpdateGlobalSettingsArgs,
     Permission,
     ServerConfig,
     UpdateGlobalSettingsResult,
 } from '@vendure/common/lib/generated-types';
+import { notNullOrUndefined } from '@vendure/common/lib/shared-utils';
 import {
     GraphQLOutputType,
     GraphQLResolveInfo,
@@ -56,6 +58,7 @@ export class GlobalSettingsResolver {
         ).filter(p => !p.internal);
         return {
             customFieldConfig: this.generateCustomFieldConfig(info),
+            entityCustomFields: this.generateEntityCustomFieldConfig(info),
             orderProcess: this.orderService.getOrderProcessStates(),
             permittedAssetTypes: this.configService.assetOptions.permittedFileTypes,
             permissions,
@@ -88,12 +91,13 @@ export class GlobalSettingsResolver {
         return this.globalSettingsService.updateSettings(ctx, args.input);
     }
 
+    // TODO: Remove in v3
     private generateCustomFieldConfig(info: GraphQLResolveInfo): GraphQLCustomFields {
         const exposedCustomFieldConfig: CustomFields = {};
         for (const [entityType, customFields] of Object.entries(this.configService.customFields)) {
             exposedCustomFieldConfig[entityType as keyof CustomFields] = customFields
                 // Do not expose custom fields marked as "internal".
-                .filter(c => !c.internal)
+                ?.filter(c => !c.internal)
                 .map(c => ({ ...c, list: !!c.list as any }))
                 .map((c: any) => {
                     // In the VendureConfig, the relation entity is specified
@@ -109,6 +113,30 @@ export class GlobalSettingsResolver {
         return exposedCustomFieldConfig as GraphQLCustomFields;
     }
 
+    private generateEntityCustomFieldConfig(info: GraphQLResolveInfo): EntityCustomFields[] {
+        return Object.entries(this.configService.customFields)
+            .map(([entityType, customFields]) => {
+                if (!customFields || !customFields.length) {
+                    return;
+                }
+                const customFieldsConfig = customFields
+                    // Do not expose custom fields marked as "internal".
+                    .filter(c => !c.internal)
+                    .map(c => ({ ...c, list: !!c.list as any }))
+                    .map((c: any) => {
+                        // In the VendureConfig, the relation entity is specified
+                        // as the class, but the GraphQL API exposes it as a string.
+                        if (c.type === 'relation') {
+                            c.entity = c.entity.name;
+                            c.scalarFields = this.getScalarFieldsOfType(info, c.graphQLType || c.entity);
+                        }
+                        return c;
+                    });
+                return { entityName: entityType, customFields: customFieldsConfig };
+            })
+            .filter(notNullOrUndefined);
+    }
+
     private getScalarFieldsOfType(info: GraphQLResolveInfo, typeName: string): string[] {
         const type = info.schema.getType(typeName);
 

+ 20 - 1
packages/elasticsearch-plugin/e2e/graphql/generated-e2e-elasticsearch-plugin-types.ts

@@ -129,7 +129,12 @@ export type AdministratorPaymentInput = {
 };
 
 export type AdministratorRefundInput = {
-  amount: Scalars['Money']['input'];
+  /**
+   * The amount to be refunded to this particular Payment. This was introduced in
+   * v2.2.0 as the preferred way to specify the refund amount. The `lines`, `shipping` and `adjustment`
+   * fields will be removed in a future version.
+   */
+  amount?: InputMaybe<Scalars['Money']['input']>;
   paymentId: Scalars['ID']['input'];
   reason?: InputMaybe<Scalars['String']['input']>;
 };
@@ -1273,6 +1278,10 @@ export type CustomField = {
 
 export type CustomFieldConfig = BooleanCustomFieldConfig | DateTimeCustomFieldConfig | FloatCustomFieldConfig | IntCustomFieldConfig | LocaleStringCustomFieldConfig | LocaleTextCustomFieldConfig | RelationCustomFieldConfig | StringCustomFieldConfig | TextCustomFieldConfig;
 
+/**
+ * This type is deprecated in v2.2 in favor of the EntityCustomFields type,
+ * which allows custom fields to be defined on user-supplies entities.
+ */
 export type CustomFields = {
   Address: Array<CustomFieldConfig>;
   Administrator: Array<CustomFieldConfig>;
@@ -1508,6 +1517,11 @@ export type EmptyOrderLineSelectionError = ErrorResult & {
   message: Scalars['String']['output'];
 };
 
+export type EntityCustomFields = {
+  customFields: Array<CustomFieldConfig>;
+  entityName: Scalars['String']['output'];
+};
+
 export enum ErrorCode {
   ALREADY_REFUNDED_ERROR = 'ALREADY_REFUNDED_ERROR',
   CANCEL_ACTIVE_ORDER_ERROR = 'CANCEL_ACTIVE_ORDER_ERROR',
@@ -5488,7 +5502,12 @@ export type SellerSortParameter = {
 };
 
 export type ServerConfig = {
+  /**
+   * This field is deprecated in v2.2 in favor of the entityCustomFields field,
+   * which allows custom fields to be defined on user-supplies entities.
+   */
   customFieldConfig: CustomFields;
+  entityCustomFields: Array<EntityCustomFields>;
   moneyStrategyPrecision: Scalars['Int']['output'];
   orderProcess: Array<OrderProcessState>;
   permissions: Array<PermissionDefinition>;

+ 20 - 1
packages/payments-plugin/e2e/graphql/generated-admin-types.ts

@@ -129,7 +129,12 @@ export type AdministratorPaymentInput = {
 };
 
 export type AdministratorRefundInput = {
-  amount: Scalars['Money']['input'];
+  /**
+   * The amount to be refunded to this particular Payment. This was introduced in
+   * v2.2.0 as the preferred way to specify the refund amount. The `lines`, `shipping` and `adjustment`
+   * fields will be removed in a future version.
+   */
+  amount?: InputMaybe<Scalars['Money']['input']>;
   paymentId: Scalars['ID']['input'];
   reason?: InputMaybe<Scalars['String']['input']>;
 };
@@ -1273,6 +1278,10 @@ export type CustomField = {
 
 export type CustomFieldConfig = BooleanCustomFieldConfig | DateTimeCustomFieldConfig | FloatCustomFieldConfig | IntCustomFieldConfig | LocaleStringCustomFieldConfig | LocaleTextCustomFieldConfig | RelationCustomFieldConfig | StringCustomFieldConfig | TextCustomFieldConfig;
 
+/**
+ * This type is deprecated in v2.2 in favor of the EntityCustomFields type,
+ * which allows custom fields to be defined on user-supplies entities.
+ */
 export type CustomFields = {
   Address: Array<CustomFieldConfig>;
   Administrator: Array<CustomFieldConfig>;
@@ -1508,6 +1517,11 @@ export type EmptyOrderLineSelectionError = ErrorResult & {
   message: Scalars['String']['output'];
 };
 
+export type EntityCustomFields = {
+  customFields: Array<CustomFieldConfig>;
+  entityName: Scalars['String']['output'];
+};
+
 export enum ErrorCode {
   ALREADY_REFUNDED_ERROR = 'ALREADY_REFUNDED_ERROR',
   CANCEL_ACTIVE_ORDER_ERROR = 'CANCEL_ACTIVE_ORDER_ERROR',
@@ -5488,7 +5502,12 @@ export type SellerSortParameter = {
 };
 
 export type ServerConfig = {
+  /**
+   * This field is deprecated in v2.2 in favor of the entityCustomFields field,
+   * which allows custom fields to be defined on user-supplies entities.
+   */
   customFieldConfig: CustomFields;
+  entityCustomFields: Array<EntityCustomFields>;
   moneyStrategyPrecision: Scalars['Int']['output'];
   orderProcess: Array<OrderProcessState>;
   permissions: Array<PermissionDefinition>;

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
schema-admin.json


이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.