From 3d4ff000664af65eed16d1311e6cacc381311db8 Mon Sep 17 00:00:00 2001 From: Romain Thouvenin Date: Mon, 29 Apr 2024 16:02:32 +0200 Subject: [PATCH] publish integration event when an ad is updated --- src/app.constants.ts | 2 +- src/modules/ad/ad.mapper.ts | 1 + src/modules/ad/ad.module.ts | 2 + ...when-ad-is-updated.domain-event-handler.ts | 44 +++++++++++++ ...ad-is-updated.domain-event-handler.spec.ts | 61 +++++++++++++++++++ 5 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 src/modules/ad/core/application/event-handlers/publish-message-when-ad-is-updated.domain-event-handler.ts create mode 100644 tests/unit/ad/core/publish-message-when-ad-is-updated.domain-event-handler.spec.ts diff --git a/src/app.constants.ts b/src/app.constants.ts index 1f72c93..7f2ec77 100644 --- a/src/app.constants.ts +++ b/src/app.constants.ts @@ -7,7 +7,7 @@ export const GRPC_SERVICE_NAME = 'AdService'; // messaging output export const AD_CREATED_ROUTING_KEY = 'ad.created'; -// messaging output +export const AD_UPDATED_ROUTING_KEY = 'ad.updated'; export const AD_DELETED_ROUTING_KEY = 'ad.deleted'; // messaging input diff --git a/src/modules/ad/ad.mapper.ts b/src/modules/ad/ad.mapper.ts index 255f4fa..05daf4e 100644 --- a/src/modules/ad/ad.mapper.ts +++ b/src/modules/ad/ad.mapper.ts @@ -141,6 +141,7 @@ export class AdMapper response.userId = props.userId; response.driver = props.driver as boolean; response.passenger = props.passenger as boolean; + response.strict = props.strict; response.status = props.status; response.frequency = props.frequency; response.fromDate = this.outputDatetimeTransformer.fromDate( diff --git a/src/modules/ad/ad.module.ts b/src/modules/ad/ad.module.ts index 3d5dfe1..8f0e1d9 100644 --- a/src/modules/ad/ad.module.ts +++ b/src/modules/ad/ad.module.ts @@ -17,6 +17,7 @@ import { InvalidateAdService } from './core/application/commands/invalidate-ad/i import { ValidateAdService } from './core/application/commands/validate-ad/validate-ad.service'; import { PublishMessageWhenAdIsDeletedDomainEventHandler } from './core/application/event-handlers/publish-message-when-ad-deleted.domain-event-handler'; import { PublishMessageWhenAdIsCreatedDomainEventHandler } from './core/application/event-handlers/publish-message-when-ad-is-created.domain-event-handler'; +import { PublishMessageWhenAdIsUpdatedDomainEventHandler } from './core/application/event-handlers/publish-message-when-ad-is-updated.domain-event-handler'; import { FindAdByIdQueryHandler } from './core/application/queries/find-ad-by-id/find-ad-by-id.query-handler'; import { FindAdsByIdsQueryHandler } from './core/application/queries/find-ads-by-ids/find-ads-by-ids.query-handler'; import { FindAdsByUserIdQueryHandler } from './core/application/queries/find-ads-by-user-id/find-ads-by-user-id.query-handler'; @@ -51,6 +52,7 @@ const messageHandlers = [ const eventHandlers: Provider[] = [ PublishMessageWhenAdIsCreatedDomainEventHandler, + PublishMessageWhenAdIsUpdatedDomainEventHandler, PublishMessageWhenAdIsDeletedDomainEventHandler, ]; diff --git a/src/modules/ad/core/application/event-handlers/publish-message-when-ad-is-updated.domain-event-handler.ts b/src/modules/ad/core/application/event-handlers/publish-message-when-ad-is-updated.domain-event-handler.ts new file mode 100644 index 0000000..4715a14 --- /dev/null +++ b/src/modules/ad/core/application/event-handlers/publish-message-when-ad-is-updated.domain-event-handler.ts @@ -0,0 +1,44 @@ +import { + IntegrationEvent, + IntegrationEventProps, + MessagePublisherPort, +} from '@mobicoop/ddd-library'; +import { AD_MESSAGE_PUBLISHER } from '@modules/ad/ad.di-tokens'; +import { AdMapper } from '@modules/ad/ad.mapper'; +import { AdResponseDto } from '@modules/ad/interface/dtos/ad.response.dto'; +import { Inject, Injectable } from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; +import { AD_UPDATED_ROUTING_KEY } from '@src/app.constants'; +import { v4 } from 'uuid'; +import { AdUpdatedDomainEvent } from '../../domain/events/ad.domain-event'; + +class AdIntegrationEvent extends IntegrationEvent { + readonly data: AdResponseDto; + + constructor(props: IntegrationEventProps, data: AdResponseDto) { + super(props); + this.data = data; + } +} + +@Injectable() +export class PublishMessageWhenAdIsUpdatedDomainEventHandler { + constructor( + @Inject(AD_MESSAGE_PUBLISHER) + private readonly messagePublisher: MessagePublisherPort, + private readonly mapper: AdMapper, + ) {} + + @OnEvent(AdUpdatedDomainEvent.name, { async: true, promisify: true }) + async handle(event: AdUpdatedDomainEvent): Promise { + this.messagePublisher.publish( + AD_UPDATED_ROUTING_KEY, + JSON.stringify( + new AdIntegrationEvent( + { id: v4(), metadata: event.metadata }, + this.mapper.toResponse(event.ad), + ), + ), + ); + } +} diff --git a/tests/unit/ad/core/publish-message-when-ad-is-updated.domain-event-handler.spec.ts b/tests/unit/ad/core/publish-message-when-ad-is-updated.domain-event-handler.spec.ts new file mode 100644 index 0000000..c6aa48d --- /dev/null +++ b/tests/unit/ad/core/publish-message-when-ad-is-updated.domain-event-handler.spec.ts @@ -0,0 +1,61 @@ +import { + AD_MESSAGE_PUBLISHER, + OUTPUT_DATETIME_TRANSFORMER, + TIMEZONE_FINDER, + TIME_CONVERTER, +} from '@modules/ad/ad.di-tokens'; +import { AdMapper } from '@modules/ad/ad.mapper'; +import { PublishMessageWhenAdIsUpdatedDomainEventHandler } from '@modules/ad/core/application/event-handlers/publish-message-when-ad-is-updated.domain-event-handler'; +import { AdEntity } from '@modules/ad/core/domain/ad.entity'; +import { AdUpdatedDomainEvent } from '@modules/ad/core/domain/events/ad.domain-event'; +import { OutputDateTimeTransformer } from '@modules/ad/infrastructure/output-datetime-transformer'; +import { TimeConverter } from '@modules/ad/infrastructure/time-converter'; +import { TimezoneFinder } from '@modules/ad/infrastructure/timezone-finder'; +import { Test, TestingModule } from '@nestjs/testing'; +import { punctualPassengerCreateAdProps } from './ad.fixtures'; + +const mockMessagePublisher = { + publish: jest.fn(), +}; + +describe('Publish message when ad is updated domain event handler', () => { + let updatedDomainEventHandler: PublishMessageWhenAdIsUpdatedDomainEventHandler; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + { + provide: AD_MESSAGE_PUBLISHER, + useValue: mockMessagePublisher, + }, + { + provide: OUTPUT_DATETIME_TRANSFORMER, + useClass: OutputDateTimeTransformer, + }, + { + provide: TIMEZONE_FINDER, + useClass: TimezoneFinder, + }, + { + provide: TIME_CONVERTER, + useClass: TimeConverter, + }, + AdMapper, + PublishMessageWhenAdIsUpdatedDomainEventHandler, + ], + }).compile(); + + updatedDomainEventHandler = + module.get( + PublishMessageWhenAdIsUpdatedDomainEventHandler, + ); + }); + + it('should publish a message', () => { + expect(updatedDomainEventHandler).toBeDefined(); + const ad = AdEntity.create(punctualPassengerCreateAdProps()); + const adUpdatedDomainEvent = new AdUpdatedDomainEvent(ad); + updatedDomainEventHandler.handle(adUpdatedDomainEvent); + expect(mockMessagePublisher.publish).toHaveBeenCalledTimes(1); + }); +});