Browse Source

feat(core): Add custom fields to registerCustomerAccount mutation

Closes #388
Michael Bromley 5 years ago
parent
commit
be1f200eb0

+ 22 - 0
packages/core/src/api/config/__snapshots__/graphql-custom-fields.spec.ts.snap

@@ -257,3 +257,25 @@ input OrderLineCustomFieldsInput {
 }
 "
 `;
+
+exports[`addRegisterCustomerCustomFieldsInput() add public writable custom fields to RegisterCustomerInput 1`] = `
+"type Mutation {
+  registerCustomerAccount(input: RegisterCustomerInput!): Boolean!
+}
+
+input RegisterCustomerCustomFieldsInput {
+  isB2B: Boolean
+  message: String
+}
+
+input RegisterCustomerInput {
+  emailAddress: String!
+  title: String
+  firstName: String
+  lastName: String
+  phoneNumber: String
+  password: String
+  customFields: RegisterCustomerCustomFieldsInput
+}
+"
+`;

+ 7 - 3
packages/core/src/api/config/configure-graphql-module.ts

@@ -24,6 +24,7 @@ import { generateListOptions } from './generate-list-options';
 import {
     addGraphQLCustomFields,
     addOrderLineCustomFieldsInput,
+    addRegisterCustomerCustomFieldsInput,
     addServerConfigCustomFields,
 } from './graphql-custom-fields';
 
@@ -158,18 +159,21 @@ async function createGraphQLOptions(
         const customFields = configService.customFields;
         // Paths must be normalized to use forward-slash separators.
         // See https://github.com/nestjs/graphql/issues/336
-        const normalizedPaths = options.typePaths.map(p => p.split(path.sep).join('/'));
+        const normalizedPaths = options.typePaths.map((p) => p.split(path.sep).join('/'));
         const typeDefs = await typesLoader.mergeTypesByPaths(normalizedPaths);
         let schema = buildSchema(typeDefs);
 
         getPluginAPIExtensions(configService.plugins, apiType)
-            .map(e => (typeof e.schema === 'function' ? e.schema() : e.schema))
+            .map((e) => (typeof e.schema === 'function' ? e.schema() : e.schema))
             .filter(notNullOrUndefined)
-            .forEach(documentNode => (schema = extendSchema(schema, documentNode)));
+            .forEach((documentNode) => (schema = extendSchema(schema, documentNode)));
         schema = generateListOptions(schema);
         schema = addGraphQLCustomFields(schema, customFields, apiType === 'shop');
         schema = addServerConfigCustomFields(schema, customFields);
         schema = addOrderLineCustomFieldsInput(schema, customFields.OrderLine || []);
+        if (apiType === 'shop') {
+            schema = addRegisterCustomerCustomFieldsInput(schema, customFields.Customer || []);
+        }
 
         return printSchema(schema);
     }

+ 52 - 7
packages/core/src/api/config/graphql-custom-fields.spec.ts

@@ -2,7 +2,11 @@ import { printSchema } from 'graphql';
 
 import { CustomFieldConfig, CustomFields } from '../../config/custom-field/custom-field-types';
 
-import { addGraphQLCustomFields, addOrderLineCustomFieldsInput } from './graphql-custom-fields';
+import {
+    addGraphQLCustomFields,
+    addOrderLineCustomFieldsInput,
+    addRegisterCustomerCustomFieldsInput,
+} from './graphql-custom-fields';
 
 describe('addGraphQLCustomFields()', () => {
     it('uses JSON scalar if no custom fields defined', () => {
@@ -43,7 +47,10 @@ describe('addGraphQLCustomFields()', () => {
                     }
                 `;
         const customFieldConfig: CustomFields = {
-            Product: [{ name: 'available', type: 'boolean' }, { name: 'shortName', type: 'localeString' }],
+            Product: [
+                { name: 'available', type: 'boolean' },
+                { name: 'shortName', type: 'localeString' },
+            ],
         };
         const result = addGraphQLCustomFields(input, customFieldConfig, false);
         expect(printSchema(result)).toMatchSnapshot();
@@ -60,7 +67,10 @@ describe('addGraphQLCustomFields()', () => {
                     }
                 `;
         const customFieldConfig: CustomFields = {
-            Product: [{ name: 'available', type: 'boolean' }, { name: 'shortName', type: 'localeString' }],
+            Product: [
+                { name: 'available', type: 'boolean' },
+                { name: 'shortName', type: 'localeString' },
+            ],
         };
         const result = addGraphQLCustomFields(input, customFieldConfig, false);
         expect(printSchema(result)).toMatchSnapshot();
@@ -77,7 +87,10 @@ describe('addGraphQLCustomFields()', () => {
                     }
                 `;
         const customFieldConfig: CustomFields = {
-            Product: [{ name: 'available', type: 'boolean' }, { name: 'shortName', type: 'localeString' }],
+            Product: [
+                { name: 'available', type: 'boolean' },
+                { name: 'shortName', type: 'localeString' },
+            ],
         };
         const result = addGraphQLCustomFields(input, customFieldConfig, false);
         expect(printSchema(result)).toMatchSnapshot();
@@ -102,7 +115,10 @@ describe('addGraphQLCustomFields()', () => {
                     }
                 `;
         const customFieldConfig: CustomFields = {
-            Product: [{ name: 'available', type: 'boolean' }, { name: 'shortName', type: 'localeString' }],
+            Product: [
+                { name: 'available', type: 'boolean' },
+                { name: 'shortName', type: 'localeString' },
+            ],
         };
         const result = addGraphQLCustomFields(input, customFieldConfig, false);
         expect(printSchema(result)).toMatchSnapshot();
@@ -124,7 +140,10 @@ describe('addGraphQLCustomFields()', () => {
                     }
                 `;
         const customFieldConfig: CustomFields = {
-            Product: [{ name: 'available', type: 'boolean' }, { name: 'shortName', type: 'localeString' }],
+            Product: [
+                { name: 'available', type: 'boolean' },
+                { name: 'shortName', type: 'localeString' },
+            ],
         };
         const result = addGraphQLCustomFields(input, customFieldConfig, false);
         expect(printSchema(result)).toMatchSnapshot();
@@ -187,7 +206,6 @@ describe('addGraphQLCustomFields()', () => {
 });
 
 describe('addOrderLineCustomFieldsInput()', () => {
-
     it('Modifies the schema when the addItemToOrder & adjustOrderLine mutation is present', () => {
         const input = `
             type Mutation {
@@ -217,3 +235,30 @@ describe('addOrderLineCustomFieldsInput()', () => {
         expect(printSchema(result)).toMatchSnapshot();
     });
 });
+
+describe('addRegisterCustomerCustomFieldsInput()', () => {
+    it('add public writable custom fields to RegisterCustomerInput', () => {
+        const input = `
+            input RegisterCustomerInput {
+                emailAddress: String!
+                title: String
+                firstName: String
+                lastName: String
+                phoneNumber: String
+                password: String
+            }
+
+            type Mutation {
+                registerCustomerAccount(input: RegisterCustomerInput!): Boolean!
+            }
+        `;
+        const customFieldConfig: CustomFieldConfig[] = [
+            { name: 'isB2B', type: 'boolean', nullable: false },
+            { name: 'message', type: 'string' },
+            { name: 'rating', type: 'int', public: false },
+            { name: 'dbRef', type: 'int', internal: true },
+        ];
+        const result = addRegisterCustomerCustomFieldsInput(input, customFieldConfig);
+        expect(printSchema(result)).toMatchSnapshot();
+    });
+});

+ 37 - 6
packages/core/src/api/config/graphql-custom-fields.ts

@@ -32,15 +32,15 @@ export function addGraphQLCustomFields(
 
     for (const entityName of Object.keys(customFieldConfig)) {
         const customEntityFields = (customFieldConfig[entityName as keyof CustomFields] || []).filter(
-            config => {
+            (config) => {
                 return !config.internal && (publicOnly === true ? config.public !== false : true);
             },
         );
 
-        const localeStringFields = customEntityFields.filter(field => field.type === 'localeString');
-        const nonLocaleStringFields = customEntityFields.filter(field => field.type !== 'localeString');
-        const writeableLocaleStringFields = localeStringFields.filter(field => !field.readonly);
-        const writeableNonLocaleStringFields = nonLocaleStringFields.filter(field => !field.readonly);
+        const localeStringFields = customEntityFields.filter((field) => field.type === 'localeString');
+        const nonLocaleStringFields = customEntityFields.filter((field) => field.type !== 'localeString');
+        const writeableLocaleStringFields = localeStringFields.filter((field) => !field.readonly);
+        const writeableNonLocaleStringFields = nonLocaleStringFields.filter((field) => !field.readonly);
 
         if (schema.getType(entityName)) {
             if (customEntityFields.length) {
@@ -184,6 +184,37 @@ export function addServerConfigCustomFields(
     return extendSchema(schema, parse(customFieldTypeDefs));
 }
 
+/**
+ * If CustomFields are defined on the Customer entity, then an extra `customFields` field is added to
+ * the `RegisterCustomerInput` so that public writable custom fields can be set when a new customer
+ * is registered.
+ */
+export function addRegisterCustomerCustomFieldsInput(
+    typeDefsOrSchema: string | GraphQLSchema,
+    customerCustomFields: CustomFieldConfig[],
+): GraphQLSchema {
+    const schema = typeof typeDefsOrSchema === 'string' ? buildSchema(typeDefsOrSchema) : typeDefsOrSchema;
+    if (!customerCustomFields || customerCustomFields.length === 0) {
+        return schema;
+    }
+    const publicWritableCustomFields = customerCustomFields.filter((fieldDef) => {
+        return fieldDef.public !== false && !fieldDef.readonly && !fieldDef.internal;
+    });
+    if (publicWritableCustomFields.length < 1) {
+        return schema;
+    }
+    const customFieldTypeDefs = `
+        input RegisterCustomerCustomFieldsInput {
+            ${mapToFields(publicWritableCustomFields, getGraphQlType)}
+        }
+
+        extend input RegisterCustomerInput {
+            customFields: RegisterCustomerCustomFieldsInput
+        }
+    `;
+    return extendSchema(schema, parse(customFieldTypeDefs));
+}
+
 /**
  * If CustomFields are defined on the OrderLine entity, then an extra `customFields` argument
  * must be added to the `addItemToOrder` and `adjustOrderLine` mutations.
@@ -240,7 +271,7 @@ type GraphQLFieldType = 'DateTime' | 'String' | 'Int' | 'Float' | 'Boolean' | 'I
  * Maps an array of CustomFieldConfig objects into a string of SDL fields.
  */
 function mapToFields(fieldDefs: CustomFieldConfig[], typeFn: (fieldType: CustomFieldType) => string): string {
-    return fieldDefs.map(field => `${field.name}: ${typeFn(field.type)}`).join('\n');
+    return fieldDefs.map((field) => `${field.name}: ${typeFn(field.type)}`).join('\n');
 }
 
 function getFilterOperator(type: CustomFieldType): string {

+ 2 - 0
packages/core/src/service/services/customer.service.ts

@@ -171,12 +171,14 @@ export class CustomerService {
             // If the user has already been verified, do nothing
             return false;
         }
+        const customFields = (input as any).customFields;
         const customer = await this.createOrUpdate({
             emailAddress: input.emailAddress,
             title: input.title || '',
             firstName: input.firstName || '',
             lastName: input.lastName || '',
             phoneNumber: input.phoneNumber || '',
+            ...(customFields ? { customFields } : {}),
         });
         await this.historyService.createHistoryEntryForCustomer({
             customerId: customer.id,