Skip to content
This repository was archived by the owner on Mar 20, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Injectable, OnApplicationBootstrap, OnModuleDestroy } from '@nestjs/common';
import { CommandBus } from '@nestjs/cqrs';

import { ApplicationEvent } from '@/module/application-command-events';
import {
RegisterCourseUserApplicationCommand,
RegisterCourseUserCommand,
} from '@/module/commands/register-course-user';
import { UserRegistrationWasCompleted } from '@/module/events/user-registration-was-completed.domain-event';
import { env } from '@/shared/env';
import { ApplicationCommandFactory } from '@/write/shared/application/application-command.factory';
import { EventsSubscription } from '@/write/shared/application/events-subscription/events-subscription';
import { EventsSubscriptionsRegistry } from '@/write/shared/application/events-subscription/events-subscriptions-registry';

@Injectable()
export class UserRegistrationWasCompletedEventHandler implements OnApplicationBootstrap, OnModuleDestroy {
private eventsSubscription: EventsSubscription;

constructor(
private readonly commandBus: CommandBus,
private readonly commandFactory: ApplicationCommandFactory,
private readonly eventsSubscriptionsFactory: EventsSubscriptionsRegistry,
) {}

async onApplicationBootstrap() {
this.eventsSubscription = this.eventsSubscriptionsFactory
.subscription('WhenUserRegistrationWasCompletedThenRegisterCourseUser_Automation_v1')
.onEvent<UserRegistrationWasCompleted>('UserRegistrationWasCompleted', (event) =>
this.onUserRegistrationWasCompleted(event),
)
.build();
await this.eventsSubscription.start();
}

async onModuleDestroy() {
await this.eventsSubscription.stop();
}

async onUserRegistrationWasCompleted(event: ApplicationEvent<UserRegistrationWasCompleted>) {
const command = this.commandFactory.applicationCommand((idGenerator) => ({
class: RegisterCourseUserApplicationCommand,
...RegisterCourseUserCommand({
userId: event.data.userId,
courseUserId: idGenerator.generate(),
courseId: env.CURRENT_COURSE_ID,
}),
metadata: { correlationId: event.metadata.correlationId, causationId: event.id },
}));

await this.commandBus.execute(command);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';

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

import { UserRegistrationWasCompletedEventHandler } from './user-registration-was-completed.event-handler.service';

@Module({
imports: [SharedModule],
providers: [UserRegistrationWasCompletedEventHandler],
})
export class WhenRegistrationCompletedThenRegisterCurrentUserAutomationModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { AsyncReturnType } from 'type-fest';

import { RegisterCourseUser, RegisterCourseUserCommand } from '@/module/commands/register-course-user';
import { userRegistrationWasCompletedEvent } from '@/module/events/user-registration-was-completed.domain-event';
import { EventStreamName } from '@/write/shared/application/event-stream-name.value-object';

import { whenUserRegistrationWasCompletedThenRegisterCurrentEditionCourseUserAutomationTestModule } from './when-registration-completed-then-register-current-edition-course-user.test-module';

describe('RegisterCourseUser when registrationWasCompleted', () => {
let moduleUnderTest: AsyncReturnType<
typeof whenUserRegistrationWasCompletedThenRegisterCurrentEditionCourseUserAutomationTestModule
>;

beforeEach(async () => {
moduleUnderTest = await whenUserRegistrationWasCompletedThenRegisterCurrentEditionCourseUserAutomationTestModule();
});

afterEach(async () => {
await moduleUnderTest.close();
});

it('publishes registerCourseUser command when userRegistrationWasCompletedEvent occurred', async () => {
// Given

const courseId = process.env.CURRENT_COURSE_ID ?? '';

const userId = 'ca63d023-4cbd-40ca-9f53-f19dbb19b0ab';
const fullName = 'test user';
const emailAddress = 'testUser@test.pl';
const hashedPassword = '41c2c1fc8f6cdc15.d5ee8246071726582172f83d569287951a0d727c94dfc35e291fe17abec789c2';

const event = userRegistrationWasCompletedEvent({ userId, fullName, emailAddress, hashedPassword });

// When
await moduleUnderTest.eventOccurred(EventStreamName.from('UserRegistration', userId), event);

// Then
await moduleUnderTest.expectCommandExecutedLastly<RegisterCourseUser>({
...RegisterCourseUserCommand({ courseId, userId, courseUserId: moduleUnderTest.lastGeneratedId() }),
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { WhenRegistrationCompletedThenRegisterCurrentUserAutomationModule } from '@/automation/when-registration-completed-then-register-current-edition-course-user/when-registration-completed-then-register-current-edition-course-user-automation.module';
import { initAutomationTestModule } from '@/shared/test-utils';

export async function whenUserRegistrationWasCompletedThenRegisterCurrentEditionCourseUserAutomationTestModule() {
return initAutomationTestModule([WhenRegistrationCompletedThenRegisterCurrentUserAutomationModule]);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,9 @@ export type RegisterCourseUser = {
};
};

export const RegisterCourseUserCommand = (data: RegisterCourseUser['data']): RegisterCourseUser => ({
type: 'RegisterCourseUser',
data,
});

export class RegisterCourseUserApplicationCommand extends AbstractApplicationCommand<RegisterCourseUser> {}
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,17 @@ export class ApplicationCommandFactory {
const generateId = () => this.idGenerator.generate();
const currentTime = () => this.timeProvider.currentTime();

const id = generateId();
const correlationId = generateId();

const command = builder(this.idGenerator);

return plainToClass(command.class, {
type: command.type,
id: generateId(),
id,
issuedAt: currentTime(),
data: command.data,
metadata: { correlationId: generateId(), ...command.metadata },
metadata: { correlationId, ...command.metadata },
});
}
}