Просмотр исходного кода

refactor(core): Refactor internals of ConfigurableOperationDef args

Relates to #414. This refactor sets things up for adding some new features such as list support.
Michael Bromley 5 лет назад
Родитель
Сommit
b5b25ff324
36 измененных файлов с 218 добавлено и 196 удалено
  1. 3 5
      packages/admin-ui/src/lib/core/src/common/generated-types.ts
  2. 0 1
      packages/admin-ui/src/lib/core/src/data/definitions/settings-definitions.ts
  3. 0 1
      packages/admin-ui/src/lib/core/src/data/definitions/shared-definitions.ts
  4. 1 3
      packages/asset-server-plugin/e2e/graphql/generated-e2e-asset-server-plugin-types.ts
  5. 1 3
      packages/common/src/generated-shop-types.ts
  6. 1 3
      packages/common/src/generated-types.ts
  7. 0 4
      packages/core/e2e/__snapshots__/collection.e2e-spec.ts.snap
  8. 0 5
      packages/core/e2e/__snapshots__/promotion.e2e-spec.ts.snap
  9. 0 18
      packages/core/e2e/collection.e2e-spec.ts
  10. 0 4
      packages/core/e2e/default-search-plugin.e2e-spec.ts
  11. 0 1
      packages/core/e2e/graphql/fragments.ts
  12. 2 4
      packages/core/e2e/graphql/generated-e2e-admin-types.ts
  13. 1 3
      packages/core/e2e/graphql/generated-e2e-shop-types.ts
  14. 11 11
      packages/core/e2e/order-promotion.e2e-spec.ts
  15. 4 6
      packages/core/e2e/promotion.e2e-spec.ts
  16. 1 3
      packages/core/e2e/shipping-method.e2e-spec.ts
  17. 0 2
      packages/core/e2e/shop-catalog.e2e-spec.ts
  18. 3 2
      packages/core/src/api/api-internal-modules.ts
  19. 97 0
      packages/core/src/api/common/configurable-operation-codec.ts
  20. 0 45
      packages/core/src/api/common/id-codec.service.ts
  21. 9 4
      packages/core/src/api/resolvers/admin/collection.resolver.ts
  22. 35 8
      packages/core/src/api/resolvers/admin/promotion.resolver.ts
  23. 0 2
      packages/core/src/api/schema/common/common-types.graphql
  24. 33 27
      packages/core/src/common/configurable-operation.ts
  25. 3 4
      packages/core/src/config/collection/collection-filter.ts
  26. 3 5
      packages/core/src/config/payment-method/payment-method-handler.ts
  27. 2 3
      packages/core/src/config/promotion/promotion-action.ts
  28. 1 3
      packages/core/src/config/promotion/promotion-condition.ts
  29. 1 2
      packages/core/src/config/shipping-method/shipping-calculator.ts
  30. 2 3
      packages/core/src/config/shipping-method/shipping-eligibility-checker.ts
  31. 3 5
      packages/core/src/data-import/providers/populator/populator.ts
  32. 0 1
      packages/core/src/service/services/payment-method.service.ts
  33. 0 2
      packages/elasticsearch-plugin/e2e/elasticsearch-plugin.e2e-spec.ts
  34. 1 3
      packages/elasticsearch-plugin/e2e/graphql/generated-e2e-elasticsearch-plugin-types.ts
  35. 0 0
      schema-admin.json
  36. 0 0
      schema-shop.json

+ 3 - 5
packages/admin-ui/src/lib/core/src/common/generated-types.ts

@@ -312,7 +312,6 @@ export type CollectionTranslation = {
 export type ConfigArg = {
    __typename?: 'ConfigArg';
   name: Scalars['String'];
-  type: Scalars['String'];
   value: Scalars['String'];
 };
 
@@ -327,7 +326,6 @@ export type ConfigArgDefinition = {
 
 export type ConfigArgInput = {
   name: Scalars['String'];
-  type: Scalars['String'];
   value: Scalars['String'];
 };
 
@@ -1094,7 +1092,7 @@ export type DateRange = {
 
 
 /**
- * Expects the same validation formats as the <input type="datetime-local"> HTML element.
+ * Expects the same validation formats as the `<input type="datetime-local">` HTML element.
  * See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/datetime-local#Additional_attributes
  */
 export type DateTimeCustomFieldConfig = CustomField & {
@@ -6106,7 +6104,7 @@ export type PaymentMethodFragment = (
   & Pick<PaymentMethod, 'id' | 'createdAt' | 'updatedAt' | 'code' | 'enabled'>
   & { configArgs: Array<(
     { __typename?: 'ConfigArg' }
-    & Pick<ConfigArg, 'name' | 'type' | 'value'>
+    & Pick<ConfigArg, 'name' | 'value'>
   )> }
 );
 
@@ -6702,7 +6700,7 @@ export type ConfigurableOperationFragment = (
   & Pick<ConfigurableOperation, 'code'>
   & { args: Array<(
     { __typename?: 'ConfigArg' }
-    & Pick<ConfigArg, 'name' | 'type' | 'value'>
+    & Pick<ConfigArg, 'name' | 'value'>
   )> }
 );
 

+ 0 - 1
packages/admin-ui/src/lib/core/src/data/definitions/settings-definitions.ts

@@ -372,7 +372,6 @@ export const PAYMENT_METHOD_FRAGMENT = gql`
         enabled
         configArgs {
             name
-            type
             value
         }
     }

+ 0 - 1
packages/admin-ui/src/lib/core/src/data/definitions/shared-definitions.ts

@@ -4,7 +4,6 @@ export const CONFIGURABLE_OPERATION_FRAGMENT = gql`
     fragment ConfigurableOperation on ConfigurableOperation {
         args {
             name
-            type
             value
         }
         code

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

@@ -311,7 +311,6 @@ export type CollectionTranslation = {
 export type ConfigArg = {
     __typename?: 'ConfigArg';
     name: Scalars['String'];
-    type: Scalars['String'];
     value: Scalars['String'];
 };
 
@@ -326,7 +325,6 @@ export type ConfigArgDefinition = {
 
 export type ConfigArgInput = {
     name: Scalars['String'];
-    type: Scalars['String'];
     value: Scalars['String'];
 };
 
@@ -1088,7 +1086,7 @@ export type DateRange = {
 };
 
 /**
- * Expects the same validation formats as the <input type="datetime-local"> HTML element.
+ * Expects the same validation formats as the `<input type="datetime-local">` HTML element.
  * See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/datetime-local#Additional_attributes
  */
 export type DateTimeCustomFieldConfig = CustomField & {

+ 1 - 3
packages/common/src/generated-shop-types.ts

@@ -226,7 +226,6 @@ export type CollectionTranslation = {
 export type ConfigArg = {
     __typename?: 'ConfigArg';
     name: Scalars['String'];
-    type: Scalars['String'];
     value: Scalars['String'];
 };
 
@@ -241,7 +240,6 @@ export type ConfigArgDefinition = {
 
 export type ConfigArgInput = {
     name: Scalars['String'];
-    type: Scalars['String'];
     value: Scalars['String'];
 };
 
@@ -773,7 +771,7 @@ export type DateRange = {
 };
 
 /**
- * Expects the same validation formats as the <input type="datetime-local"> HTML element.
+ * Expects the same validation formats as the `<input type="datetime-local">` HTML element.
  * See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/datetime-local#Additional_attributes
  */
 export type DateTimeCustomFieldConfig = CustomField & {

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

@@ -311,7 +311,6 @@ export type CollectionTranslation = {
 export type ConfigArg = {
    __typename?: 'ConfigArg';
   name: Scalars['String'];
-  type: Scalars['String'];
   value: Scalars['String'];
 };
 
@@ -326,7 +325,6 @@ export type ConfigArgDefinition = {
 
 export type ConfigArgInput = {
   name: Scalars['String'];
-  type: Scalars['String'];
   value: Scalars['String'];
 };
 
@@ -1086,7 +1084,7 @@ export type DateRange = {
 
 
 /**
- * Expects the same validation formats as the <input type="datetime-local"> HTML element.
+ * Expects the same validation formats as the `<input type="datetime-local">` HTML element.
  * See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/datetime-local#Additional_attributes
  */
 export type DateTimeCustomFieldConfig = CustomField & {

+ 0 - 4
packages/core/e2e/__snapshots__/collection.e2e-spec.ts.snap

@@ -38,12 +38,10 @@ Object {
       "args": Array [
         Object {
           "name": "facetValueIds",
-          "type": "facetValueIds",
           "value": "[\\"T_1\\"]",
         },
         Object {
           "name": "containsAny",
-          "type": "boolean",
           "value": "false",
         },
       ],
@@ -109,12 +107,10 @@ Object {
       "args": Array [
         Object {
           "name": "facetValueIds",
-          "type": "facetValueIds",
           "value": "[\\"T_3\\"]",
         },
         Object {
           "name": "containsAny",
-          "type": "boolean",
           "value": "false",
         },
       ],

+ 0 - 5
packages/core/e2e/__snapshots__/promotion.e2e-spec.ts.snap

@@ -50,7 +50,6 @@ Object {
       "args": Array [
         Object {
           "name": "facetValueIds",
-          "type": "facetValueIds",
           "value": "[\\"T_1\\"]",
         },
       ],
@@ -62,7 +61,6 @@ Object {
       "args": Array [
         Object {
           "name": "arg",
-          "type": "int",
           "value": "500",
         },
       ],
@@ -84,7 +82,6 @@ Object {
       "args": Array [
         Object {
           "name": "facetValueIds",
-          "type": "facetValueIds",
           "value": "[\\"T_1\\"]",
         },
       ],
@@ -96,7 +93,6 @@ Object {
       "args": Array [
         Object {
           "name": "arg",
-          "type": "int",
           "value": "90",
         },
       ],
@@ -106,7 +102,6 @@ Object {
       "args": Array [
         Object {
           "name": "arg",
-          "type": "int",
           "value": "10",
         },
       ],

+ 0 - 18
packages/core/e2e/collection.e2e-spec.ts

@@ -119,12 +119,10 @@ describe('Collection resolver', () => {
                                     {
                                         name: 'facetValueIds',
                                         value: `["${getFacetValueId('electronics')}"]`,
-                                        type: 'facetValueIds',
                                     },
                                     {
                                         name: 'containsAny',
                                         value: `false`,
-                                        type: 'boolean',
                                     },
                                 ],
                             },
@@ -167,12 +165,10 @@ describe('Collection resolver', () => {
                                     {
                                         name: 'facetValueIds',
                                         value: `["${getFacetValueId('computers')}"]`,
-                                        type: 'facetValueIds',
                                     },
                                     {
                                         name: 'containsAny',
                                         value: `false`,
-                                        type: 'boolean',
                                     },
                                 ],
                             },
@@ -200,12 +196,10 @@ describe('Collection resolver', () => {
                                     {
                                         name: 'facetValueIds',
                                         value: `["${getFacetValueId('pear')}"]`,
-                                        type: 'facetValueIds',
                                     },
                                     {
                                         name: 'containsAny',
                                         value: `false`,
-                                        type: 'boolean',
                                     },
                                 ],
                             },
@@ -630,12 +624,10 @@ describe('Collection resolver', () => {
                                     {
                                         name: 'operator',
                                         value: 'contains',
-                                        type: 'string',
                                     },
                                     {
                                         name: 'term',
                                         value: 'laptop',
-                                        type: 'string',
                                     },
                                 ],
                             },
@@ -867,12 +859,10 @@ describe('Collection resolver', () => {
                                         value: `["${getFacetValueId('pear')}", "${getFacetValueId(
                                             'photo',
                                         )}"]`,
-                                        type: 'facetValueIds',
                                     },
                                     {
                                         name: 'containsAny',
                                         value: `false`,
-                                        type: 'boolean',
                                     },
                                 ],
                             },
@@ -914,12 +904,10 @@ describe('Collection resolver', () => {
                                         value: `["${getFacetValueId('pear')}", "${getFacetValueId(
                                             'photo',
                                         )}"]`,
-                                        type: 'facetValueIds',
                                     },
                                     {
                                         name: 'containsAny',
                                         value: `true`,
-                                        type: 'boolean',
                                     },
                                 ],
                             },
@@ -970,12 +958,10 @@ describe('Collection resolver', () => {
                                     {
                                         name: 'facetValueIds',
                                         value: `["${getFacetValueId('pear')}", "${getFacetValueId('bell')}"]`,
-                                        type: 'facetValueIds',
                                     },
                                     {
                                         name: 'containsAny',
                                         value: `true`,
-                                        type: 'boolean',
                                     },
                                 ],
                             },
@@ -1027,12 +1013,10 @@ describe('Collection resolver', () => {
                                     {
                                         name: 'operator',
                                         value: operator,
-                                        type: 'string',
                                     },
                                     {
                                         name: 'term',
                                         value: term,
-                                        type: 'string',
                                     },
                                 ],
                             },
@@ -1261,12 +1245,10 @@ describe('Collection resolver', () => {
                                 {
                                     name: 'facetValueIds',
                                     value: `["${getFacetValueId('pear')}"]`,
-                                    type: 'facetValueIds',
                                 },
                                 {
                                     name: 'containsAny',
                                     value: `false`,
-                                    type: 'boolean',
                                 },
                             ],
                         },

+ 0 - 4
packages/core/e2e/default-search-plugin.e2e-spec.ts

@@ -517,12 +517,10 @@ describe('Default search plugin', () => {
                                         {
                                             name: 'facetValueIds',
                                             value: `["T_4"]`,
-                                            type: 'facetValueIds',
                                         },
                                         {
                                             name: 'containsAny',
                                             value: `false`,
-                                            type: 'boolean',
                                         },
                                     ],
                                 },
@@ -567,12 +565,10 @@ describe('Default search plugin', () => {
                                     {
                                         name: 'facetValueIds',
                                         value: `["T_3"]`,
-                                        type: 'facetValueIds',
                                     },
                                     {
                                         name: 'containsAny',
                                         value: `false`,
-                                        type: 'boolean',
                                     },
                                 ],
                             },

+ 0 - 1
packages/core/e2e/graphql/fragments.ts

@@ -149,7 +149,6 @@ export const CONFIGURABLE_FRAGMENT = gql`
     fragment ConfigurableOperation on ConfigurableOperation {
         args {
             name
-            type
             value
         }
         code

+ 2 - 4
packages/core/e2e/graphql/generated-e2e-admin-types.ts

@@ -311,7 +311,6 @@ export type CollectionTranslation = {
 export type ConfigArg = {
     __typename?: 'ConfigArg';
     name: Scalars['String'];
-    type: Scalars['String'];
     value: Scalars['String'];
 };
 
@@ -326,7 +325,6 @@ export type ConfigArgDefinition = {
 
 export type ConfigArgInput = {
     name: Scalars['String'];
-    type: Scalars['String'];
     value: Scalars['String'];
 };
 
@@ -1088,7 +1086,7 @@ export type DateRange = {
 };
 
 /**
- * Expects the same validation formats as the <input type="datetime-local"> HTML element.
+ * Expects the same validation formats as the `<input type="datetime-local">` HTML element.
  * See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/datetime-local#Additional_attributes
  */
 export type DateTimeCustomFieldConfig = CustomField & {
@@ -4530,7 +4528,7 @@ export type RoleFragment = { __typename?: 'Role' } & Pick<
 export type ConfigurableOperationFragment = { __typename?: 'ConfigurableOperation' } & Pick<
     ConfigurableOperation,
     'code'
-> & { args: Array<{ __typename?: 'ConfigArg' } & Pick<ConfigArg, 'name' | 'type' | 'value'>> };
+> & { args: Array<{ __typename?: 'ConfigArg' } & Pick<ConfigArg, 'name' | 'value'>> };
 
 export type CollectionFragment = { __typename?: 'Collection' } & Pick<
     Collection,

+ 1 - 3
packages/core/e2e/graphql/generated-e2e-shop-types.ts

@@ -226,7 +226,6 @@ export type CollectionTranslation = {
 export type ConfigArg = {
     __typename?: 'ConfigArg';
     name: Scalars['String'];
-    type: Scalars['String'];
     value: Scalars['String'];
 };
 
@@ -241,7 +240,6 @@ export type ConfigArgDefinition = {
 
 export type ConfigArgInput = {
     name: Scalars['String'];
-    type: Scalars['String'];
     value: Scalars['String'];
 };
 
@@ -773,7 +771,7 @@ export type DateRange = {
 };
 
 /**
- * Expects the same validation formats as the <input type="datetime-local"> HTML element.
+ * Expects the same validation formats as the `<input type="datetime-local">` HTML element.
  * See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/datetime-local#Additional_attributes
  */
 export type DateTimeCustomFieldConfig = CustomField & {

+ 11 - 11
packages/core/e2e/order-promotion.e2e-spec.ts

@@ -55,13 +55,13 @@ describe('Promotions applied to Orders', () => {
 
     const freeOrderAction = {
         code: orderPercentageDiscount.code,
-        arguments: [{ name: 'discount', type: 'int', value: '100' }],
+        arguments: [{ name: 'discount', value: '100' }],
     };
     const minOrderAmountCondition = (min: number) => ({
         code: minimumOrderAmount.code,
         arguments: [
-            { name: 'amount', type: 'int', value: min.toString() },
-            { name: 'taxInclusive', type: 'boolean', value: 'true' },
+            { name: 'amount', value: min.toString() },
+            { name: 'taxInclusive', value: 'true' },
         ],
     });
 
@@ -290,8 +290,8 @@ describe('Promotions applied to Orders', () => {
                     {
                         code: atLeastNWithFacets.code,
                         arguments: [
-                            { name: 'minimum', type: 'int', value: '2' },
-                            { name: 'facets', type: 'facetValueIds', value: `["${saleFacetValue.id}"]` },
+                            { name: 'minimum', value: '2' },
+                            { name: 'facets', value: `["${saleFacetValue.id}"]` },
                         ],
                     },
                 ],
@@ -344,7 +344,7 @@ describe('Promotions applied to Orders', () => {
                 actions: [
                     {
                         code: orderPercentageDiscount.code,
-                        arguments: [{ name: 'discount', type: 'int', value: '50' }],
+                        arguments: [{ name: 'discount', value: '50' }],
                     },
                 ],
             });
@@ -386,8 +386,8 @@ describe('Promotions applied to Orders', () => {
                     {
                         code: discountOnItemWithFacets.code,
                         arguments: [
-                            { name: 'discount', type: 'int', value: '50' },
-                            { name: 'facets', type: 'facetValueIds', value: `["${saleFacetValue.id}"]` },
+                            { name: 'discount', value: '50' },
+                            { name: 'facets', value: `["${saleFacetValue.id}"]` },
                         ],
                     },
                 ],
@@ -450,8 +450,8 @@ describe('Promotions applied to Orders', () => {
                     {
                         code: discountOnItemWithFacets.code,
                         arguments: [
-                            { name: 'discount', type: 'int', value: '50' },
-                            { name: 'facets', type: 'facetValueIds', value: `["${saleFacetValue.id}"]` },
+                            { name: 'discount', value: '50' },
+                            { name: 'facets', value: `["${saleFacetValue.id}"]` },
                         ],
                     },
                 ],
@@ -464,7 +464,7 @@ describe('Promotions applied to Orders', () => {
                 actions: [
                     {
                         code: orderPercentageDiscount.code,
-                        arguments: [{ name: 'discount', type: 'int', value: '50' }],
+                        arguments: [{ name: 'discount', value: '50' }],
                     },
                 ],
             });

+ 4 - 6
packages/core/e2e/promotion.e2e-spec.ts

@@ -5,7 +5,7 @@ import gql from 'graphql-tag';
 import path from 'path';
 
 import { initialData } from '../../../e2e-common/e2e-initial-data';
-import { TEST_SETUP_TIMEOUT_MS, testConfig } from '../../../e2e-common/test-config';
+import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-config';
 
 import { PROMOTION_FRAGMENT } from './graphql/fragments';
 import {
@@ -74,7 +74,7 @@ describe('Promotion resolver', () => {
                     conditions: [
                         {
                             code: promoCondition.code,
-                            arguments: [{ name: 'arg', value: '500', type: 'int' }],
+                            arguments: [{ name: 'arg', value: '500' }],
                         },
                     ],
                     actions: [
@@ -84,7 +84,6 @@ describe('Promotion resolver', () => {
                                 {
                                     name: 'facetValueIds',
                                     value: '["T_1"]',
-                                    type: 'facetValueIds',
                                 },
                             ],
                         },
@@ -111,7 +110,6 @@ describe('Promotion resolver', () => {
                                 {
                                     name: 'facetValueIds',
                                     value: '["T_1"]',
-                                    type: 'facetValueIds',
                                 },
                             ],
                         },
@@ -133,11 +131,11 @@ describe('Promotion resolver', () => {
                     conditions: [
                         {
                             code: promoCondition.code,
-                            arguments: [{ name: 'arg', value: '90', type: 'int' }],
+                            arguments: [{ name: 'arg', value: '90' }],
                         },
                         {
                             code: promoCondition2.code,
-                            arguments: [{ name: 'arg', value: '10', type: 'int' }],
+                            arguments: [{ name: 'arg', value: '10' }],
                         },
                     ],
                 },

+ 1 - 3
packages/core/e2e/shipping-method.e2e-spec.ts

@@ -9,7 +9,7 @@ import gql from 'graphql-tag';
 import path from 'path';
 
 import { initialData } from '../../../e2e-common/e2e-initial-data';
-import { TEST_SETUP_TIMEOUT_MS, testConfig } from '../../../e2e-common/test-config';
+import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-config';
 
 import {
     CreateShippingMethod,
@@ -157,7 +157,6 @@ describe('ShippingMethod resolver', () => {
                     arguments: [
                         {
                             name: 'orderMinimum',
-                            type: 'int',
                             value: '0',
                         },
                     ],
@@ -197,7 +196,6 @@ describe('ShippingMethod resolver', () => {
                     arguments: [
                         {
                             name: 'orderMinimum',
-                            type: 'int',
                             value: '0',
                         },
                     ],

+ 0 - 2
packages/core/e2e/shop-catalog.e2e-spec.ts

@@ -248,12 +248,10 @@ describe('Shop catalog', () => {
                                 {
                                     name: 'facetValueIds',
                                     value: `["${sportsEquipment.id}"]`,
-                                    type: 'facetValueIds',
                                 },
                                 {
                                     name: 'containsAny',
                                     value: `false`,
-                                    type: 'boolean',
                                 },
                             ],
                         },

+ 3 - 2
packages/core/src/api/api-internal-modules.ts

@@ -6,6 +6,7 @@ import { JobQueueModule } from '../job-queue/job-queue.module';
 import { createDynamicGraphQlModulesForPlugins } from '../plugin/dynamic-plugin-api.module';
 import { ServiceModule } from '../service/service.module';
 
+import { ConfigurableOperationCodec } from './common/configurable-operation-codec';
 import { IdCodecService } from './common/id-codec.service';
 import { AdministratorResolver } from './resolvers/admin/administrator.resolver';
 import { AssetResolver } from './resolvers/admin/asset.resolver';
@@ -127,8 +128,8 @@ export const adminEntityResolvers = [
  */
 @Module({
     imports: [ConfigModule],
-    providers: [IdCodecService],
-    exports: [IdCodecService, ConfigModule],
+    providers: [IdCodecService, ConfigurableOperationCodec],
+    exports: [IdCodecService, ConfigModule, ConfigurableOperationCodec],
 })
 export class ApiSharedModule {}
 

+ 97 - 0
packages/core/src/api/common/configurable-operation-codec.ts

@@ -0,0 +1,97 @@
+import { Injectable } from '@nestjs/common';
+import { ConfigurableOperation, ConfigurableOperationInput } from '@vendure/common/lib/generated-types';
+import { Type } from '@vendure/common/lib/shared-types';
+
+import { ConfigurableOperationDef } from '../../common/configurable-operation';
+import { InternalServerError } from '../../common/error/errors';
+import {
+    PromotionCondition,
+    PromotionItemAction,
+    PromotionOrderAction,
+    ShippingCalculator,
+    ShippingEligibilityChecker,
+} from '../../config';
+import { CollectionFilter } from '../../config/collection/collection-filter';
+import { ConfigService } from '../../config/config.service';
+import { PaymentMethodHandler } from '../../config/payment-method/payment-method-handler';
+
+import { IdCodecService } from './id-codec.service';
+
+@Injectable()
+export class ConfigurableOperationCodec {
+    constructor(private configService: ConfigService, private idCodecService: IdCodecService) {}
+
+    /**
+     * Decodes any ID type arguments of a ConfigurableOperationDef
+     */
+    decodeConfigurableOperationIds<T extends ConfigurableOperationDef<any>>(
+        defType: Type<ConfigurableOperationDef<any>>,
+        input: ConfigurableOperationInput[],
+    ): ConfigurableOperationInput[] {
+        const availableDefs = this.getAvailableDefsOfType(defType);
+        for (const operationInput of input) {
+            const def = availableDefs.find(d => d.code === operationInput.code);
+            if (!def) {
+                continue;
+            }
+            for (const arg of operationInput.arguments) {
+                const argDef = def.args[arg.name];
+                if (argDef.type === 'facetValueIds' && arg.value) {
+                    const ids = JSON.parse(arg.value) as string[];
+                    const decodedIds = ids.map(id => this.idCodecService.decode(id));
+                    arg.value = JSON.stringify(decodedIds);
+                }
+            }
+        }
+        return input;
+    }
+
+    /**
+     * Encodes any ID type arguments of a ConfigurableOperationDef
+     */
+    encodeConfigurableOperationIds<T extends ConfigurableOperationDef<any>>(
+        defType: Type<ConfigurableOperationDef<any>>,
+        input: ConfigurableOperation[],
+    ): ConfigurableOperation[] {
+        const availableDefs = this.getAvailableDefsOfType(defType);
+        for (const operationInput of input) {
+            const def = availableDefs.find(d => d.code === operationInput.code);
+            if (!def) {
+                continue;
+            }
+            for (const arg of operationInput.args) {
+                const argDef = def.args[arg.name];
+                if (argDef.type === 'facetValueIds' && arg.value) {
+                    const ids = JSON.parse(arg.value) as string[];
+                    const encodedIds = ids.map(id => this.idCodecService.encode(id));
+                    arg.value = JSON.stringify(encodedIds);
+                }
+            }
+        }
+        return input;
+    }
+
+    getAvailableDefsOfType(
+        defType: Type<ConfigurableOperationDef<any>>,
+    ): Array<ConfigurableOperationDef<any>> {
+        switch (defType) {
+            case CollectionFilter:
+                return this.configService.catalogOptions.collectionFilters;
+            case PaymentMethodHandler:
+                return this.configService.paymentOptions.paymentMethodHandlers;
+            case PromotionItemAction:
+            case PromotionOrderAction:
+                return this.configService.promotionOptions.promotionActions || [];
+            case PromotionCondition:
+                return this.configService.promotionOptions.promotionConditions || [];
+            case ShippingEligibilityChecker:
+                return this.configService.shippingOptions.shippingEligibilityCheckers || [];
+            case ShippingCalculator:
+                return this.configService.shippingOptions.shippingCalculators || [];
+            default:
+                throw new InternalServerError('error.unknown-configurable-operation-definition', {
+                    name: defType.name,
+                });
+        }
+    }
+}

+ 0 - 45
packages/core/src/api/common/id-codec.service.ts

@@ -1,5 +1,4 @@
 import { Injectable } from '@nestjs/common';
-import { ConfigurableOperation, ConfigurableOperationInput } from '@vendure/common/lib/generated-types';
 
 import { ConfigService } from '../../config/config.service';
 
@@ -19,48 +18,4 @@ export class IdCodecService {
     decode<T extends string | number | object | undefined>(target: T, transformKeys?: string[]): T {
         return this.idCodec.decode(target, transformKeys);
     }
-
-    /**
-     * Decodes any entity IDs used in ConfigurableOperation arguments, e.g. when specifying
-     * facetValueIds.
-     */
-    decodeConfigurableOperation(input: ConfigurableOperationInput): ConfigurableOperationInput;
-    decodeConfigurableOperation(input: ConfigurableOperationInput[]): ConfigurableOperationInput[];
-    decodeConfigurableOperation(
-        input: ConfigurableOperationInput | ConfigurableOperationInput[],
-    ): ConfigurableOperationInput | ConfigurableOperationInput[] {
-        const inputArray = Array.isArray(input) ? input : [input];
-        for (const operationInput of inputArray) {
-            for (const arg of operationInput.arguments) {
-                if (arg.type === 'facetValueIds' && arg.value) {
-                    const ids = JSON.parse(arg.value) as string[];
-                    const decodedIds = ids.map(id => this.decode(id));
-                    arg.value = JSON.stringify(decodedIds);
-                }
-            }
-        }
-        return Array.isArray(input) ? inputArray : inputArray[0];
-    }
-
-    /**
-     * Encodes any entity IDs used in ConfigurableOperation arguments, e.g. when specifying
-     * facetValueIds.
-     */
-    encodeConfigurableOperation(input: ConfigurableOperation): ConfigurableOperation;
-    encodeConfigurableOperation(input: ConfigurableOperation[]): ConfigurableOperation[];
-    encodeConfigurableOperation(
-        input: ConfigurableOperation | ConfigurableOperation[],
-    ): ConfigurableOperation | ConfigurableOperation[] {
-        const inputArray = Array.isArray(input) ? input : [input];
-        for (const operation of inputArray) {
-            for (const arg of operation.args) {
-                if (arg.type === 'facetValueIds' && arg.value) {
-                    const ids = JSON.parse(arg.value) as string[];
-                    const encoded = ids.map(id => this.encode(id));
-                    arg.value = JSON.stringify(encoded);
-                }
-            }
-        }
-        return Array.isArray(input) ? inputArray : inputArray[0];
-    }
 }

+ 9 - 4
packages/core/src/api/resolvers/admin/collection.resolver.ts

@@ -14,9 +14,11 @@ import { PaginatedList } from '@vendure/common/lib/shared-types';
 
 import { UserInputError } from '../../../common/error/errors';
 import { Translated } from '../../../common/types/locale-types';
+import { CollectionFilter } from '../../../config/collection/collection-filter';
 import { Collection } from '../../../entity/collection/collection.entity';
 import { CollectionService } from '../../../service/services/collection.service';
 import { FacetValueService } from '../../../service/services/facet-value.service';
+import { ConfigurableOperationCodec } from '../../common/configurable-operation-codec';
 import { IdCodecService } from '../../common/id-codec.service';
 import { RequestContext } from '../../common/request-context';
 import { Allow } from '../../decorators/allow.decorator';
@@ -27,7 +29,7 @@ export class CollectionResolver {
     constructor(
         private collectionService: CollectionService,
         private facetValueService: FacetValueService,
-        private idCodecService: IdCodecService,
+        private configurableOperationCodec: ConfigurableOperationCodec,
     ) {}
 
     @Query()
@@ -79,7 +81,7 @@ export class CollectionResolver {
         @Args() args: MutationCreateCollectionArgs,
     ): Promise<Translated<Collection>> {
         const { input } = args;
-        this.idCodecService.decodeConfigurableOperation(input.filters);
+        this.configurableOperationCodec.decodeConfigurableOperationIds(CollectionFilter, input.filters);
         return this.collectionService.create(ctx, input).then(this.encodeFilters);
     }
 
@@ -90,7 +92,7 @@ export class CollectionResolver {
         @Args() args: MutationUpdateCollectionArgs,
     ): Promise<Translated<Collection>> {
         const { input } = args;
-        this.idCodecService.decodeConfigurableOperation(input.filters || []);
+        this.configurableOperationCodec.decodeConfigurableOperationIds(CollectionFilter, input.filters || []);
         return this.collectionService.update(ctx, input).then(this.encodeFilters);
     }
 
@@ -118,7 +120,10 @@ export class CollectionResolver {
      */
     private encodeFilters = <T extends Collection | undefined>(collection: T): T => {
         if (collection) {
-            this.idCodecService.encodeConfigurableOperation(collection.filters);
+            this.configurableOperationCodec.encodeConfigurableOperationIds(
+                CollectionFilter,
+                collection.filters,
+            );
         }
         return collection;
     };

+ 35 - 8
packages/core/src/api/resolvers/admin/promotion.resolver.ts

@@ -10,16 +10,21 @@ import {
 } from '@vendure/common/lib/generated-types';
 import { PaginatedList } from '@vendure/common/lib/shared-types';
 
+import { PromotionItemAction, PromotionOrderAction } from '../../../config/promotion/promotion-action';
+import { PromotionCondition } from '../../../config/promotion/promotion-condition';
 import { Promotion } from '../../../entity/promotion/promotion.entity';
 import { PromotionService } from '../../../service/services/promotion.service';
-import { IdCodecService } from '../../common/id-codec.service';
+import { ConfigurableOperationCodec } from '../../common/configurable-operation-codec';
 import { RequestContext } from '../../common/request-context';
 import { Allow } from '../../decorators/allow.decorator';
 import { Ctx } from '../../decorators/request-context.decorator';
 
 @Resolver('Promotion')
 export class PromotionResolver {
-    constructor(private promotionService: PromotionService, private idCodecService: IdCodecService) {}
+    constructor(
+        private promotionService: PromotionService,
+        private configurableOperationCodec: ConfigurableOperationCodec,
+    ) {}
 
     @Query()
     @Allow(Permission.ReadPromotion)
@@ -57,8 +62,14 @@ export class PromotionResolver {
         @Ctx() ctx: RequestContext,
         @Args() args: MutationCreatePromotionArgs,
     ): Promise<Promotion> {
-        this.idCodecService.decodeConfigurableOperation(args.input.actions);
-        this.idCodecService.decodeConfigurableOperation(args.input.conditions);
+        this.configurableOperationCodec.decodeConfigurableOperationIds(
+            PromotionOrderAction,
+            args.input.actions,
+        );
+        this.configurableOperationCodec.decodeConfigurableOperationIds(
+            PromotionCondition,
+            args.input.conditions,
+        );
         return this.promotionService.createPromotion(ctx, args.input).then(this.encodeConditionsAndActions);
     }
 
@@ -68,8 +79,18 @@ export class PromotionResolver {
         @Ctx() ctx: RequestContext,
         @Args() args: MutationUpdatePromotionArgs,
     ): Promise<Promotion> {
-        this.idCodecService.decodeConfigurableOperation(args.input.actions || []);
-        this.idCodecService.decodeConfigurableOperation(args.input.conditions || []);
+        this.configurableOperationCodec.decodeConfigurableOperationIds(
+            PromotionOrderAction,
+            args.input.actions || [],
+        );
+        this.configurableOperationCodec.decodeConfigurableOperationIds(
+            PromotionItemAction,
+            args.input.actions || [],
+        );
+        this.configurableOperationCodec.decodeConfigurableOperationIds(
+            PromotionCondition,
+            args.input.conditions || [],
+        );
         return this.promotionService.updatePromotion(ctx, args.input).then(this.encodeConditionsAndActions);
     }
 
@@ -84,8 +105,14 @@ export class PromotionResolver {
      */
     private encodeConditionsAndActions = <T extends Promotion | undefined>(collection: T): T => {
         if (collection) {
-            this.idCodecService.encodeConfigurableOperation(collection.conditions);
-            this.idCodecService.encodeConfigurableOperation(collection.actions);
+            this.configurableOperationCodec.encodeConfigurableOperationIds(
+                PromotionOrderAction,
+                collection.actions,
+            );
+            this.configurableOperationCodec.encodeConfigurableOperationIds(
+                PromotionCondition,
+                collection.conditions,
+            );
         }
         return collection;
     };

+ 0 - 2
packages/core/src/api/schema/common/common-types.graphql

@@ -22,7 +22,6 @@ type Adjustment {
 
 type ConfigArg {
     name: String!
-    type: String!
     value: String!
 }
 
@@ -59,7 +58,6 @@ type DeletionResponse {
 
 input ConfigArgInput {
     name: String!
-    type: String!
     value: String!
 }
 

+ 33 - 27
packages/core/src/common/configurable-operation.ts

@@ -174,6 +174,28 @@ export class ConfigurableOperationDef<T extends ConfigArgs<ConfigArgType>> {
             await this.options.destroy();
         }
     }
+
+    /**
+     * Coverts an array of ConfigArgs into a hash object:
+     *
+     * from:
+     * [{ name: 'foo', type: 'string', value: 'bar'}]
+     *
+     * to:
+     * { foo: 'bar' }
+     **/
+    protected argsArrayToHash(args: ConfigArg[]): ConfigArgValues<T> {
+        const output: ConfigArgValues<T> = {} as any;
+        for (const arg of args) {
+            if (arg && arg.value != null) {
+                output[arg.name as keyof ConfigArgValues<T>] = coerceValueToType<T>(
+                    arg.value,
+                    this.args[arg.name],
+                );
+            }
+        }
+        return output;
+    }
 }
 
 /**
@@ -231,42 +253,26 @@ function localizeString(stringArray: LocalizedStringArray, languageCode: Languag
     return match.value;
 }
 
-/**
- * Coverts an array of ConfigArgs into a hash object:
- *
- * from:
- * [{ name: 'foo', type: 'string', value: 'bar'}]
- *
- * to:
- * { foo: 'bar' }
- **/
-export function argsArrayToHash<T extends ConfigArgs<any>>(args: ConfigArg[]): ConfigArgValues<T> {
-    const output: ConfigArgValues<T> = {} as any;
-    for (const arg of args) {
-        if (arg && arg.value != null) {
-            output[arg.name as keyof ConfigArgValues<T>] = coerceValueToType<T>(arg);
-        }
-    }
-    return output;
-}
-
-function coerceValueToType<T extends ConfigArgs<any>>(arg: ConfigArg): ConfigArgValues<T>[keyof T] {
-    switch (arg.type as ConfigArgType) {
+function coerceValueToType<T extends ConfigArgs<any>>(
+    value: string,
+    argDef: ConfigArgDef<any>,
+): ConfigArgValues<T>[keyof T] {
+    switch (argDef.type as ConfigArgType) {
         case 'string':
-            return arg.value as any;
+            return value as any;
         case 'int':
-            return Number.parseInt(arg.value || '', 10) as any;
+            return Number.parseInt(value || '', 10) as any;
         case 'datetime':
-            return Date.parse(arg.value || '') as any;
+            return Date.parse(value || '') as any;
         case 'boolean':
-            return !!(arg.value && (arg.value.toLowerCase() === 'true' || arg.value === '1')) as any;
+            return !!(value && (value.toLowerCase() === 'true' || value === '1')) as any;
         case 'facetValueIds':
             try {
-                return JSON.parse(arg.value as any);
+                return JSON.parse(value as any);
             } catch (err) {
                 throw new InternalServerError(err.message);
             }
         default:
-            return (arg.value as string) as any;
+            return (value as string) as any;
     }
 }

+ 3 - 4
packages/core/src/config/collection/collection-filter.ts

@@ -3,7 +3,6 @@ import { ConfigArgSubset } from '@vendure/common/lib/shared-types';
 import { SelectQueryBuilder } from 'typeorm';
 
 import {
-    argsArrayToHash,
     ConfigArgs,
     ConfigArgValues,
     ConfigurableOperationDef,
@@ -23,7 +22,7 @@ export interface CollectionFilterConfig<T extends CollectionFilterArgs>
     extends ConfigurableOperationDefOptions<T> {
     apply: ApplyCollectionFilterFn<T>;
 }
-
+// tslint:disable:max-line-length
 /**
  * @description
  * A CollectionFilter defines a rule which can be used to associate ProductVariants with a Collection.
@@ -36,14 +35,14 @@ export interface CollectionFilterConfig<T extends CollectionFilterArgs>
  * @docsCategory configuration
  */
 export class CollectionFilter<T extends CollectionFilterArgs = {}> extends ConfigurableOperationDef<T> {
+    // tslint:enable:max-line-length
     private readonly applyFn: ApplyCollectionFilterFn<T>;
-
     constructor(config: CollectionFilterConfig<T>) {
         super(config);
         this.applyFn = config.apply;
     }
 
     apply(qb: SelectQueryBuilder<ProductVariant>, args: ConfigArg[]): SelectQueryBuilder<ProductVariant> {
-        return this.applyFn(qb, argsArrayToHash(args));
+        return this.applyFn(qb, this.argsArrayToHash(args));
     }
 }

+ 3 - 5
packages/core/src/config/payment-method/payment-method-handler.ts

@@ -2,12 +2,10 @@ import { ConfigArg, RefundOrderInput } from '@vendure/common/lib/generated-types
 import { ConfigArgSubset } from '@vendure/common/lib/shared-types';
 
 import {
-    argsArrayToHash,
     ConfigArgs,
     ConfigArgValues,
     ConfigurableOperationDef,
     ConfigurableOperationDefOptions,
-    LocalizedStringArray,
 } from '../../common/configurable-operation';
 import { OnTransitionStartFn, StateMachineConfig } from '../../common/finite-state-machine/types';
 import { Order } from '../../entity/order/order.entity';
@@ -237,7 +235,7 @@ export class PaymentMethodHandler<
      * @internal
      */
     async createPayment(order: Order, args: ConfigArg[], metadata: PaymentMetadata) {
-        const paymentConfig = await this.createPaymentFn(order, argsArrayToHash(args), metadata);
+        const paymentConfig = await this.createPaymentFn(order, this.argsArrayToHash(args), metadata);
         return {
             method: this.code,
             ...paymentConfig,
@@ -251,7 +249,7 @@ export class PaymentMethodHandler<
      * @internal
      */
     async settlePayment(order: Order, payment: Payment, args: ConfigArg[]) {
-        return this.settlePaymentFn(order, payment, argsArrayToHash(args));
+        return this.settlePaymentFn(order, payment, this.argsArrayToHash(args));
     }
 
     /**
@@ -268,7 +266,7 @@ export class PaymentMethodHandler<
         args: ConfigArg[],
     ) {
         return this.createRefundFn
-            ? this.createRefundFn(input, total, order, payment, argsArrayToHash(args))
+            ? this.createRefundFn(input, total, order, payment, this.argsArrayToHash(args))
             : false;
     }
 

+ 2 - 3
packages/core/src/config/promotion/promotion-action.ts

@@ -2,7 +2,6 @@ import { ConfigArg } from '@vendure/common/lib/generated-types';
 import { ConfigArgSubset } from '@vendure/common/lib/shared-types';
 
 import {
-    argsArrayToHash,
     ConfigArgs,
     ConfigArgValues,
     ConfigurableOperationDef,
@@ -126,7 +125,7 @@ export class PromotionItemAction<T extends PromotionActionArgs = {}> extends Pro
 
     /** @internal */
     execute(orderItem: OrderItem, orderLine: OrderLine, args: ConfigArg[], utils: PromotionUtils) {
-        return this.executeFn(orderItem, orderLine, argsArrayToHash(args), utils);
+        return this.executeFn(orderItem, orderLine, this.argsArrayToHash(args), utils);
     }
 }
 
@@ -159,6 +158,6 @@ export class PromotionOrderAction<T extends PromotionActionArgs = {}> extends Pr
 
     /** @internal */
     execute(order: Order, args: ConfigArg[], utils: PromotionUtils) {
-        return this.executeFn(order, argsArrayToHash(args), utils);
+        return this.executeFn(order, this.argsArrayToHash(args), utils);
     }
 }

+ 1 - 3
packages/core/src/config/promotion/promotion-condition.ts

@@ -2,12 +2,10 @@ import { ConfigArg } from '@vendure/common/lib/generated-types';
 import { ConfigArgSubset, ID } from '@vendure/common/lib/shared-types';
 
 import {
-    argsArrayToHash,
     ConfigArgs,
     ConfigArgValues,
     ConfigurableOperationDef,
     ConfigurableOperationDefOptions,
-    LocalizedStringArray,
 } from '../../common/configurable-operation';
 import { OrderLine } from '../../entity';
 import { Order } from '../../entity/order/order.entity';
@@ -76,6 +74,6 @@ export class PromotionCondition<T extends PromotionConditionArgs = {}> extends C
     }
 
     async check(order: Order, args: ConfigArg[], utils: PromotionUtils): Promise<boolean> {
-        return this.checkFn(order, argsArrayToHash<T>(args), utils);
+        return this.checkFn(order, this.argsArrayToHash(args), utils);
     }
 }

+ 1 - 2
packages/core/src/config/shipping-method/shipping-calculator.ts

@@ -2,7 +2,6 @@ import { ConfigArg } from '@vendure/common/lib/generated-types';
 import { ConfigArgSubset } from '@vendure/common/lib/shared-types';
 
 import {
-    argsArrayToHash,
     ConfigArgs,
     ConfigArgValues,
     ConfigurableOperationDef,
@@ -57,7 +56,7 @@ export class ShippingCalculator<T extends ShippingCalculatorArgs = {}> extends C
      * @internal
      */
     calculate(order: Order, args: ConfigArg[]): CalculateShippingFnResult {
-        return this.calculateFn(order, argsArrayToHash(args));
+        return this.calculateFn(order, this.argsArrayToHash(args));
     }
 }
 

+ 2 - 3
packages/core/src/config/shipping-method/shipping-eligibility-checker.ts

@@ -3,11 +3,10 @@ import { ConfigArgSubset } from '@vendure/common/lib/shared-types';
 
 import {
     ConfigArgs,
+    ConfigArgValues,
     ConfigurableOperationDef,
     ConfigurableOperationDefOptions,
-    LocalizedStringArray,
 } from '../../common/configurable-operation';
-import { argsArrayToHash, ConfigArgValues } from '../../common/configurable-operation';
 import { Order } from '../../entity/order/order.entity';
 
 export type ShippingEligibilityCheckerArgType = ConfigArgSubset<'int' | 'float' | 'string' | 'boolean'>;
@@ -56,7 +55,7 @@ export class ShippingEligibilityChecker<
      * @internal
      */
     check(order: Order, args: ConfigArg[]): boolean | Promise<boolean> {
-        return this.checkFn(order, argsArrayToHash(args));
+        return this.checkFn(order, this.argsArrayToHash(args));
     }
 }
 

+ 3 - 5
packages/core/src/data-import/providers/populator/populator.ts

@@ -101,13 +101,11 @@ export class Populator {
                     arguments: [
                         {
                             name: 'facetValueIds',
-                            type: 'facetValueIds',
                             value: JSON.stringify(facetValueIds),
                         },
                         {
                             name: 'containsAny',
                             value: filter.args.containsAny.toString(),
-                            type: 'boolean',
                         },
                     ],
                 };
@@ -201,13 +199,13 @@ export class Populator {
             await this.shippingMethodService.create(ctx, {
                 checker: {
                     code: defaultShippingEligibilityChecker.code,
-                    arguments: [{ name: 'orderMinimum', value: '0', type: 'int' }],
+                    arguments: [{ name: 'orderMinimum', value: '0' }],
                 },
                 calculator: {
                     code: defaultShippingCalculator.code,
                     arguments: [
-                        { name: 'rate', value: method.price.toString(), type: 'int' },
-                        { name: 'taxRate', value: '0', type: 'int' },
+                        { name: 'rate', value: method.price.toString() },
+                        { name: 'taxRate', value: '0' },
                     ],
                 },
                 description: method.name,

+ 0 - 1
packages/core/src/service/services/payment-method.service.ts

@@ -210,7 +210,6 @@ export class PaymentMethodService {
             if (!existingConfigArgs.find(ca => ca.name === name)) {
                 configArgs.push({
                     name,
-                    type: def.type,
                     value: this.getDefaultValue(def.type),
                 });
             }

+ 0 - 2
packages/elasticsearch-plugin/e2e/elasticsearch-plugin.e2e-spec.ts

@@ -562,12 +562,10 @@ describe('Elasticsearch plugin', () => {
                                         {
                                             name: 'facetValueIds',
                                             value: `["T_4"]`,
-                                            type: 'facetValueIds',
                                         },
                                         {
                                             name: 'containsAny',
                                             value: `false`,
-                                            type: 'boolean',
                                         },
                                     ],
                                 },

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

@@ -311,7 +311,6 @@ export type CollectionTranslation = {
 export type ConfigArg = {
     __typename?: 'ConfigArg';
     name: Scalars['String'];
-    type: Scalars['String'];
     value: Scalars['String'];
 };
 
@@ -326,7 +325,6 @@ export type ConfigArgDefinition = {
 
 export type ConfigArgInput = {
     name: Scalars['String'];
-    type: Scalars['String'];
     value: Scalars['String'];
 };
 
@@ -1088,7 +1086,7 @@ export type DateRange = {
 };
 
 /**
- * Expects the same validation formats as the <input type="datetime-local"> HTML element.
+ * Expects the same validation formats as the `<input type="datetime-local">` HTML element.
  * See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/datetime-local#Additional_attributes
  */
 export type DateTimeCustomFieldConfig = CustomField & {

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
schema-admin.json


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
schema-shop.json


Некоторые файлы не были показаны из-за большого количества измененных файлов