Skip to content
This repository was archived by the owner on Mar 20, 2023. It is now read-only.

Commit 6d4dd65

Browse files
authored
feat(api): [UserRegistration] Take into account email confirmation in user registration process (#318) (#365)
* add completeUserRegistration command and handler * Add automation module with test * Change name and add module to appModule
1 parent d9478f2 commit 6d4dd65

16 files changed

+295
-25
lines changed

packages/api/src/app.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { ServeStaticModule } from '@nestjs/serve-static';
44
import { join } from 'path';
55

66
import { SendEmailWhenLearningMaterialsUrlWasGeneratedAutomationModule } from '@/automation/send-email-when-learning-materials-url-was-generated/send-email-when-learning-materials-url-was-generated-automation.module';
7+
import { WhenEmailConfirmationWasApprovedThenCompleteUserRegistrationAutomationModule } from '@/automation/when-email-confirmation-was-approved-then-complete-user-registration/when-email-confirmation-was-approved-then-complete-user-registration-automation.module';
78
import { WhenUserRegistrationWasStartedThenRequestEmailConfirmationAutomationModule } from '@/automation/when-user-registration-was-started-then-request-email-confirmation/when-user-registration-was-started-then-request-email-confirmation-automation.module';
89
import { UserProfileModule } from '@/crud/user-profile/user-profile.module';
910
import { PrismaModule } from '@/prisma/prisma.module';
@@ -33,6 +34,7 @@ const readModules = [LearningMaterialsReadModule, CourseProgressReadModule];
3334
const automationModules = [
3435
SendEmailWhenLearningMaterialsUrlWasGeneratedAutomationModule,
3536
WhenUserRegistrationWasStartedThenRequestEmailConfirmationAutomationModule,
37+
WhenEmailConfirmationWasApprovedThenCompleteUserRegistrationAutomationModule,
3638
];
3739
const eventModelingModules = [...writeModules, ...readModules, ...automationModules];
3840
const crudModules = [UserProfileModule, CoursesModule, AuthModule];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common';
2+
import { CommandBus } from '@nestjs/cqrs';
3+
4+
import { ApplicationEvent } from '@/module/application-command-events';
5+
import {
6+
CompleteUserRegistrationApplicationCommand,
7+
completeUserRegistrationCommand,
8+
} from '@/module/commands/complete-user-registration';
9+
import { EmailConfirmationWasApproved } from '@/module/events/email-confirmation-was-approved.domain.event';
10+
import { ApplicationCommandFactory } from '@/write/shared/application/application-command.factory';
11+
import { EventsSubscription } from '@/write/shared/application/events-subscription/events-subscription';
12+
import { EventsSubscriptionsRegistry } from '@/write/shared/application/events-subscription/events-subscriptions-registry';
13+
14+
@Injectable()
15+
export class EmailConfirmationWasApprovedEventHandler implements OnModuleInit, OnModuleDestroy {
16+
private eventsSubscription: EventsSubscription;
17+
18+
constructor(
19+
private readonly commandBus: CommandBus,
20+
private readonly commandFactory: ApplicationCommandFactory,
21+
private readonly eventsSubscriptionsFactory: EventsSubscriptionsRegistry,
22+
) {}
23+
24+
async onModuleInit() {
25+
this.eventsSubscription = this.eventsSubscriptionsFactory
26+
.subscription('WhenEmailConfirmationWasApprovedThenCompleteUserRegistrationAutomationModule_Automation_v1')
27+
.onEvent<EmailConfirmationWasApproved>('EmailConfirmationWasApproved', (event) =>
28+
this.onEmailRegistrationWasApproved(event),
29+
)
30+
.build();
31+
await this.eventsSubscription.start();
32+
}
33+
34+
async onEmailRegistrationWasApproved(event: ApplicationEvent<EmailConfirmationWasApproved>) {
35+
const command = this.commandFactory.applicationCommand(() => ({
36+
class: CompleteUserRegistrationApplicationCommand,
37+
...completeUserRegistrationCommand({
38+
userId: event.data.userId,
39+
}),
40+
metadata: { correlationId: event.metadata.correlationId, causationId: event.id },
41+
}));
42+
43+
await this.commandBus.execute(command);
44+
}
45+
46+
async onModuleDestroy() {
47+
await this.eventsSubscription.stop();
48+
}
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { Module } from '@nestjs/common';
2+
3+
import { SharedModule } from '@/write/shared/shared.module';
4+
5+
import { EmailConfirmationWasApprovedEventHandler } from './email-confirmation-was-approved-event-handler.service';
6+
7+
@Module({
8+
imports: [SharedModule],
9+
providers: [EmailConfirmationWasApprovedEventHandler],
10+
})
11+
export class WhenEmailConfirmationWasApprovedThenCompleteUserRegistrationAutomationModule {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { AsyncReturnType } from 'type-fest';
2+
3+
import { completeUserRegistrationCommand } from '@/module/commands/complete-user-registration';
4+
import { emailConfirmationWasApprovedEvent } from '@/module/events/email-confirmation-was-approved.domain.event';
5+
import { EventStreamName } from '@/write/shared/application/event-stream-name.value-object';
6+
7+
import { WhenEmailConfirmationWasApprovedThenCompleteUserRegistrationAutomationModuleAutomationTestModule as WhenEmailConfirmationWasApprovedThenCompleteUserRegistrationAutomationTestModule } from './when-email-confirmation-was-approved-then-complete-user-registration.test-module';
8+
9+
describe('CompleteUser registration when emailConfirmationWasApproved', () => {
10+
let moduleUnderTest: AsyncReturnType<
11+
typeof WhenEmailConfirmationWasApprovedThenCompleteUserRegistrationAutomationTestModule
12+
>;
13+
14+
beforeEach(async () => {
15+
moduleUnderTest = await WhenEmailConfirmationWasApprovedThenCompleteUserRegistrationAutomationTestModule();
16+
});
17+
18+
afterEach(async () => {
19+
await moduleUnderTest.close();
20+
});
21+
22+
it('CompleteUser registration when emailConfirmationWasApproved', async () => {
23+
const userId = 'ca63d023-4cbd-40ca-9f53-f19dbb19b0ab';
24+
const confirmationFor = 'user-registration';
25+
const event = emailConfirmationWasApprovedEvent({ userId, confirmationFor });
26+
27+
await moduleUnderTest.eventOccurred(
28+
EventStreamName.from('EmailConfirmation', `${userId}_${confirmationFor}`),
29+
event,
30+
);
31+
32+
moduleUnderTest.expectCommandExecutedLastly(completeUserRegistrationCommand({ userId }));
33+
});
34+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { CommandBus } from '@nestjs/cqrs';
2+
3+
import { commandBusNoFailWithoutHandler, initWriteTestModule } from '@/shared/test-utils';
4+
5+
import { WhenEmailConfirmationWasApprovedThenCompleteUserRegistrationAutomationModule } from './when-email-confirmation-was-approved-then-complete-user-registration-automation.module';
6+
7+
export async function WhenEmailConfirmationWasApprovedThenCompleteUserRegistrationAutomationModuleAutomationTestModule() {
8+
return initWriteTestModule({
9+
modules: [WhenEmailConfirmationWasApprovedThenCompleteUserRegistrationAutomationModule],
10+
configureModule: (app) => app.overrideProvider(CommandBus).useValue(commandBusNoFailWithoutHandler),
11+
});
12+
}

packages/api/src/module/automation/when-user-registration-was-started-then-request-email-confirmation/user-registration-was-started.event-handler.ts renamed to packages/api/src/module/automation/when-user-registration-was-started-then-request-email-confirmation/user-registration-was-started.event-handler.service.ts

File renamed without changes.

packages/api/src/module/automation/when-user-registration-was-started-then-request-email-confirmation/when-user-registration-was-started-then-request-email-confirmation-automation.module.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Module } from '@nestjs/common';
22

33
import { SharedModule } from '@/write/shared/shared.module';
44

5-
import { UserRegistrationWasStartedEventHandler } from './user-registration-was-started.event-handler';
5+
import { UserRegistrationWasStartedEventHandler } from './user-registration-was-started.event-handler.service';
66

77
@Module({
88
imports: [SharedModule],
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { UserId } from '@/shared/domain.types';
2+
3+
import { AbstractApplicationCommand } from '../application-command-events';
4+
5+
export type CompleteUserRegistration = {
6+
type: 'CompleteUserRegistration';
7+
data: {
8+
userId: UserId;
9+
};
10+
};
11+
12+
export const completeUserRegistrationCommand = (data: CompleteUserRegistration['data']): CompleteUserRegistration => ({
13+
type: 'CompleteUserRegistration',
14+
data,
15+
});
16+
17+
export class CompleteUserRegistrationApplicationCommand extends AbstractApplicationCommand<CompleteUserRegistration> {}

packages/api/src/module/shared/events/user-registration-was-completed.domain-event.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,10 @@ export type UserRegistrationWasCompleted = {
77
hashedPassword: string;
88
};
99
};
10+
11+
export const userRegistrationWasCompletedEvent = (
12+
data: UserRegistrationWasCompleted['data'],
13+
): UserRegistrationWasCompleted => ({
14+
type: 'UserRegistrationWasCompleted',
15+
data,
16+
});

packages/api/src/module/shared/events/user-registration-was-started.domain-event.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,10 @@ export type UserRegistrationWasStarted = {
77
hashedPassword: string;
88
};
99
};
10+
11+
export const userRegistrationWasStartedEvent = (
12+
data: UserRegistrationWasStarted['data'],
13+
): UserRegistrationWasStarted => ({
14+
type: 'UserRegistrationWasStarted',
15+
data,
16+
});

0 commit comments

Comments
 (0)