Przeglądaj źródła

fix(core): Normalize email address on updating Customer

Fixes #2449
Michael Bromley 2 lat temu
rodzic
commit
957d0ad665

+ 37 - 0
packages/core/e2e/customer.e2e-spec.ts

@@ -525,6 +525,22 @@ describe('Customer resolver', () => {
             expect(createCustomer.message).toBe('The email address is not available.');
             expect(createCustomer.errorCode).toBe(ErrorCode.EMAIL_ADDRESS_CONFLICT_ERROR);
         });
+
+        it('normalizes email address on creation', async () => {
+            const { createCustomer } = await adminClient.query<
+                Codegen.CreateCustomerMutation,
+                Codegen.CreateCustomerMutationVariables
+            >(CREATE_CUSTOMER, {
+                input: {
+                    emailAddress: ' JoeSmith@test.com ',
+                    firstName: 'Joe',
+                    lastName: 'Smith',
+                },
+                password: 'test',
+            });
+            customerErrorGuard.assertSuccess(createCustomer);
+            expect(createCustomer.emailAddress).toBe('joesmith@test.com');
+        });
     });
 
     describe('update', () => {
@@ -566,6 +582,27 @@ describe('Customer resolver', () => {
 
             expect(me?.identifier).toBe('unique-email@test.com');
         });
+
+        // https://github.com/vendure-ecommerce/vendure/issues/2449
+        it('normalizes email address on update', async () => {
+            const { updateCustomer } = await adminClient.query<
+                Codegen.UpdateCustomerMutation,
+                Codegen.UpdateCustomerMutationVariables
+            >(UPDATE_CUSTOMER, {
+                input: {
+                    id: thirdCustomer.id,
+                    emailAddress: ' Another-Address@test.com ',
+                },
+            });
+            customerErrorGuard.assertSuccess(updateCustomer);
+
+            expect(updateCustomer.emailAddress).toBe('another-address@test.com');
+
+            await shopClient.asUserWithCredentials('another-address@test.com', 'test');
+            const { me } = await shopClient.query<Codegen.MeQuery>(ME);
+
+            expect(me?.identifier).toBe('another-address@test.com');
+        });
     });
 
     describe('deletion', () => {

+ 23 - 0
packages/core/e2e/shop-auth.e2e-spec.ts

@@ -1263,6 +1263,29 @@ describe('Updating email address without email verification', () => {
         );
         expect(activeCustomer!.emailAddress).toBe(NEW_EMAIL_ADDRESS);
     });
+
+    it('normalizes updated email address', async () => {
+        await shopClient.asUserWithCredentials(NEW_EMAIL_ADDRESS, 'test');
+        const { requestUpdateCustomerEmailAddress } = await shopClient.query<
+            CodegenShop.RequestUpdateEmailAddressMutation,
+            CodegenShop.RequestUpdateEmailAddressMutationVariables
+        >(REQUEST_UPDATE_EMAIL_ADDRESS, {
+            password: 'test',
+            newEmailAddress: ' Not.Normal@test.com ',
+        });
+        successErrorGuard.assertSuccess(requestUpdateCustomerEmailAddress);
+        // Attempting to fix flakiness possibly caused by race condition on the event
+        // subscriber
+        await new Promise(resolve => setTimeout(resolve, 100));
+        expect(requestUpdateCustomerEmailAddress.success).toBe(true);
+        expect(sendEmailFn).toHaveBeenCalledTimes(1);
+        expect(sendEmailFn.mock.calls[0][0] instanceof IdentifierChangeEvent).toBe(true);
+
+        const { activeCustomer } = await shopClient.query<CodegenShop.GetActiveCustomerQuery>(
+            GET_ACTIVE_CUSTOMER,
+        );
+        expect(activeCustomer!.emailAddress).toBe('not.normal@test.com');
+    });
 });
 
 function getVerificationTokenPromise(): Promise<string> {

+ 10 - 6
packages/core/src/service/services/customer.service.ts

@@ -308,13 +308,16 @@ export class CustomerService {
         });
 
         if (hasEmailAddress(input)) {
+            input.emailAddress = normalizeEmailAddress(input.emailAddress);
             if (input.emailAddress !== customer.emailAddress) {
                 const existingCustomerInChannel = await this.connection
                     .getRepository(ctx, Customer)
                     .createQueryBuilder('customer')
                     .leftJoin('customer.channels', 'channel')
                     .where('channel.id = :channelId', { channelId: ctx.channelId })
-                    .andWhere('customer.emailAddress = :emailAddress', { emailAddress: input.emailAddress })
+                    .andWhere('customer.emailAddress = :emailAddress', {
+                        emailAddress: input.emailAddress,
+                    })
                     .andWhere('customer.id != :customerId', { customerId: input.id })
                     .andWhere('customer.deletedAt is null')
                     .getOne();
@@ -566,6 +569,7 @@ export class CustomerService {
         userId: ID,
         newEmailAddress: string,
     ): Promise<boolean | EmailAddressConflictError> {
+        const normalizedEmailAddress = normalizeEmailAddress(newEmailAddress);
         const userWithConflictingIdentifier = await this.userService.getUserByEmailAddress(
             ctx,
             newEmailAddress,
@@ -588,18 +592,18 @@ export class CustomerService {
             type: HistoryEntryType.CUSTOMER_EMAIL_UPDATE_REQUESTED,
             data: {
                 oldEmailAddress,
-                newEmailAddress,
+                newEmailAddress: normalizedEmailAddress,
             },
         });
         if (this.configService.authOptions.requireVerification) {
-            user.getNativeAuthenticationMethod().pendingIdentifier = newEmailAddress;
+            user.getNativeAuthenticationMethod().pendingIdentifier = normalizedEmailAddress;
             await this.userService.setIdentifierChangeToken(ctx, user);
             this.eventBus.publish(new IdentifierChangeRequestEvent(ctx, user));
             return true;
         } else {
             const oldIdentifier = user.identifier;
-            user.identifier = newEmailAddress;
-            customer.emailAddress = newEmailAddress;
+            user.identifier = normalizedEmailAddress;
+            customer.emailAddress = normalizedEmailAddress;
             await this.connection.getRepository(ctx, User).save(user, { reload: false });
             await this.connection.getRepository(ctx, Customer).save(customer, { reload: false });
             this.eventBus.publish(new IdentifierChangeEvent(ctx, user, oldIdentifier));
@@ -609,7 +613,7 @@ export class CustomerService {
                 type: HistoryEntryType.CUSTOMER_EMAIL_UPDATE_VERIFIED,
                 data: {
                     oldEmailAddress,
-                    newEmailAddress,
+                    newEmailAddress: normalizedEmailAddress,
                 },
             });
             return true;