Listen to user.deleted events to delete the corresponding user ads
This commit is contained in:
		
							parent
							
								
									492bb3ca44
								
							
						
					
					
						commit
						9fb7ef2eac
					
				|  | @ -20,6 +20,10 @@ export const MATCHER_AD_CREATION_FAILED_ROUTING_KEY = | |||
|   'matcher-ad.creation-failed'; | ||||
| export const MATCHER_AD_CREATION_FAILED_QUEUE = 'ad.matcher-ad.creation-failed'; | ||||
| 
 | ||||
| export const USER_DELETED_MESSAGE_HANDLER = 'userDeleted'; | ||||
| export const USER_DELETED_ROUTING_KEY = 'user.deleted'; | ||||
| export const USER_DELETED_QUEUE = 'ad.user.deleted'; | ||||
| 
 | ||||
| // configuration
 | ||||
| export const SERVICE_CONFIGURATION_SET_QUEUE = 'ad-configuration-set'; | ||||
| export const SERVICE_CONFIGURATION_DELETE_QUEUE = 'ad-configuration-delete'; | ||||
|  |  | |||
|  | @ -12,6 +12,7 @@ import { | |||
| import { AdMapper } from './ad.mapper'; | ||||
| import { CreateAdService } from './core/application/commands/create-ad/create-ad.service'; | ||||
| import { DeleteAdService } from './core/application/commands/delete-ad/delete-ad.service'; | ||||
| import { DeleteUserAdsService } from './core/application/commands/delete-user-ads/delete-user-ads.service'; | ||||
| import { InvalidateAdService } from './core/application/commands/invalidate-ad/invalidate-ad.service'; | ||||
| 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'; | ||||
|  | @ -32,6 +33,7 @@ import { FindAdsByIdsGrpcController } from './interface/grpc-controllers/find-ad | |||
| import { FindAdsByUserIdGrpcController } from './interface/grpc-controllers/find-ads-by-user-id.grpc.controller'; | ||||
| import { MatcherAdCreatedMessageHandler } from './interface/message-handlers/matcher-ad-created.message-handler'; | ||||
| import { MatcherAdCreationFailedMessageHandler } from './interface/message-handlers/matcher-ad-creation-failed.message-handler'; | ||||
| import { UserDeletedMessageHandler } from './interface/message-handlers/user-deleted.message-handler'; | ||||
| 
 | ||||
| const grpcControllers = [ | ||||
|   CreateAdGrpcController, | ||||
|  | @ -44,6 +46,7 @@ const grpcControllers = [ | |||
| const messageHandlers = [ | ||||
|   MatcherAdCreatedMessageHandler, | ||||
|   MatcherAdCreationFailedMessageHandler, | ||||
|   UserDeletedMessageHandler, | ||||
| ]; | ||||
| 
 | ||||
| const eventHandlers: Provider[] = [ | ||||
|  | @ -54,6 +57,7 @@ const eventHandlers: Provider[] = [ | |||
| const commandHandlers: Provider[] = [ | ||||
|   CreateAdService, | ||||
|   DeleteAdService, | ||||
|   DeleteUserAdsService, | ||||
|   ValidateAdService, | ||||
|   InvalidateAdService, | ||||
| ]; | ||||
|  |  | |||
|  | @ -0,0 +1,7 @@ | |||
| import { Command, CommandProps } from '@mobicoop/ddd-library'; | ||||
| 
 | ||||
| export class DeleteUserAdsCommand extends Command { | ||||
|   constructor(props: CommandProps<DeleteUserAdsCommand>) { | ||||
|     super(props); | ||||
|   } | ||||
| } | ||||
|  | @ -0,0 +1,29 @@ | |||
| import { AdEntity } from '@modules/ad/core/domain/ad.entity'; | ||||
| import { | ||||
|   CommandBus, | ||||
|   CommandHandler, | ||||
|   ICommandHandler, | ||||
|   QueryBus, | ||||
| } from '@nestjs/cqrs'; | ||||
| import { FindAdsByUserIdQuery } from '../../queries/find-ads-by-user-id/find-ads-by-user-id.query'; | ||||
| import { DeleteAdCommand } from '../delete-ad/delete-ad.command'; | ||||
| import { DeleteUserAdsCommand } from './delete-user-ads.command'; | ||||
| 
 | ||||
| @CommandHandler(DeleteUserAdsCommand) | ||||
| export class DeleteUserAdsService implements ICommandHandler { | ||||
|   constructor( | ||||
|     private readonly queryBus: QueryBus, | ||||
|     private readonly commandBus: CommandBus, | ||||
|   ) {} | ||||
| 
 | ||||
|   async execute(command: DeleteUserAdsCommand): Promise<void> { | ||||
|     const ads: AdEntity[] = await this.queryBus.execute( | ||||
|       new FindAdsByUserIdQuery(command.id), | ||||
|     ); | ||||
|     await Promise.all( | ||||
|       ads.map((ad) => | ||||
|         this.commandBus.execute(new DeleteAdCommand({ id: ad.id })), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | @ -0,0 +1,23 @@ | |||
| import { IntegrationEvent } from '@mobicoop/ddd-library'; | ||||
| import { RabbitSubscribe } from '@mobicoop/message-broker-module'; | ||||
| import { DeleteUserAdsCommand } from '@modules/ad/core/application/commands/delete-user-ads/delete-user-ads.command'; | ||||
| import { Injectable } from '@nestjs/common'; | ||||
| import { CommandBus } from '@nestjs/cqrs'; | ||||
| import { USER_DELETED_MESSAGE_HANDLER } from '@src/app.constants'; | ||||
| 
 | ||||
| type UserDeletedEvent = IntegrationEvent; | ||||
| 
 | ||||
| @Injectable() | ||||
| export class UserDeletedMessageHandler { | ||||
|   constructor(private readonly commandBus: CommandBus) {} | ||||
| 
 | ||||
|   @RabbitSubscribe({ | ||||
|     name: USER_DELETED_MESSAGE_HANDLER, | ||||
|   }) | ||||
|   public async userDeleted(message: string) { | ||||
|     const deletedUser: UserDeletedEvent = JSON.parse(message); | ||||
|     await this.commandBus.execute( | ||||
|       new DeleteUserAdsCommand({ id: deletedUser.id }), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | @ -1,6 +1,11 @@ | |||
| import { Module, Provider } from '@nestjs/common'; | ||||
| import { MESSAGE_PUBLISHER } from './messager.di-tokens'; | ||||
| 
 | ||||
| import { | ||||
|   MessageBrokerModule, | ||||
|   MessageBrokerModuleOptions, | ||||
|   MessageBrokerPublisher, | ||||
| } from '@mobicoop/message-broker-module'; | ||||
| import { ConfigModule, ConfigService } from '@nestjs/config'; | ||||
| import { | ||||
|   MATCHER_AD_CREATED_MESSAGE_HANDLER, | ||||
|  | @ -10,12 +15,10 @@ import { | |||
|   MATCHER_AD_CREATION_FAILED_QUEUE, | ||||
|   MATCHER_AD_CREATION_FAILED_ROUTING_KEY, | ||||
|   SERVICE_NAME, | ||||
|   USER_DELETED_MESSAGE_HANDLER, | ||||
|   USER_DELETED_QUEUE, | ||||
|   USER_DELETED_ROUTING_KEY, | ||||
| } from '@src/app.constants'; | ||||
| import { | ||||
|   MessageBrokerModule, | ||||
|   MessageBrokerModuleOptions, | ||||
|   MessageBrokerPublisher, | ||||
| } from '@mobicoop/message-broker-module'; | ||||
| 
 | ||||
| const imports = [ | ||||
|   MessageBrokerModule.forRootAsync({ | ||||
|  | @ -41,6 +44,10 @@ const imports = [ | |||
|           routingKey: MATCHER_AD_CREATION_FAILED_ROUTING_KEY, | ||||
|           queue: MATCHER_AD_CREATION_FAILED_QUEUE, | ||||
|         }, | ||||
|         [USER_DELETED_MESSAGE_HANDLER]: { | ||||
|           routingKey: USER_DELETED_ROUTING_KEY, | ||||
|           queue: USER_DELETED_QUEUE, | ||||
|         }, | ||||
|       }, | ||||
|     }), | ||||
|   }), | ||||
|  |  | |||
|  | @ -0,0 +1,53 @@ | |||
| import { DeleteUserAdsCommand } from '@modules/ad/core/application/commands/delete-user-ads/delete-user-ads.command'; | ||||
| import { DeleteUserAdsService } from '@modules/ad/core/application/commands/delete-user-ads/delete-user-ads.service'; | ||||
| import { AdEntity } from '@modules/ad/core/domain/ad.entity'; | ||||
| import { CommandBus, QueryBus } from '@nestjs/cqrs'; | ||||
| import { Test, TestingModule } from '@nestjs/testing'; | ||||
| import { punctualPassengerCreateAdProps } from './ad.fixtures'; | ||||
| 
 | ||||
| const userAds = [ | ||||
|   AdEntity.create(punctualPassengerCreateAdProps()), | ||||
|   AdEntity.create(punctualPassengerCreateAdProps()), | ||||
| ]; | ||||
| 
 | ||||
| const mockQueryBus = { | ||||
|   execute: jest.fn().mockImplementation(() => userAds), | ||||
| }; | ||||
| 
 | ||||
| const mockCommandBus = { | ||||
|   execute: jest.fn(), | ||||
| }; | ||||
| 
 | ||||
| describe('delete-user-ads.service', () => { | ||||
|   let deleteUserAdsService: DeleteUserAdsService; | ||||
| 
 | ||||
|   beforeAll(async () => { | ||||
|     const module: TestingModule = await Test.createTestingModule({ | ||||
|       providers: [ | ||||
|         { | ||||
|           provide: QueryBus, | ||||
|           useValue: mockQueryBus, | ||||
|         }, | ||||
|         { | ||||
|           provide: CommandBus, | ||||
|           useValue: mockCommandBus, | ||||
|         }, | ||||
|         DeleteUserAdsService, | ||||
|       ], | ||||
|     }).compile(); | ||||
| 
 | ||||
|     deleteUserAdsService = | ||||
|       module.get<DeleteUserAdsService>(DeleteUserAdsService); | ||||
|   }); | ||||
| 
 | ||||
|   it('should be defined', () => { | ||||
|     expect(deleteUserAdsService).toBeDefined(); | ||||
|   }); | ||||
| 
 | ||||
|   it('should call the delete command for each ad returned by the query', async () => { | ||||
|     await deleteUserAdsService.execute( | ||||
|       new DeleteUserAdsCommand({ id: userAds[0].getProps().userId }), | ||||
|     ); | ||||
|     expect(mockCommandBus.execute).toHaveBeenCalledTimes(userAds.length); | ||||
|   }); | ||||
| }); | ||||
|  | @ -0,0 +1,43 @@ | |||
| import { UserDeletedMessageHandler } from '@modules/ad/interface/message-handlers/user-deleted.message-handler'; | ||||
| import { CommandBus } from '@nestjs/cqrs'; | ||||
| import { Test, TestingModule } from '@nestjs/testing'; | ||||
| 
 | ||||
| const mockCommandBus = { | ||||
|   execute: jest.fn(), | ||||
| }; | ||||
| 
 | ||||
| describe('Matcher Ad Created Message Handler', () => { | ||||
|   let userDeletedMessageHandler: UserDeletedMessageHandler; | ||||
| 
 | ||||
|   beforeAll(async () => { | ||||
|     const module: TestingModule = await Test.createTestingModule({ | ||||
|       providers: [ | ||||
|         { | ||||
|           provide: CommandBus, | ||||
|           useValue: mockCommandBus, | ||||
|         }, | ||||
|         UserDeletedMessageHandler, | ||||
|       ], | ||||
|     }).compile(); | ||||
| 
 | ||||
|     userDeletedMessageHandler = module.get<UserDeletedMessageHandler>( | ||||
|       UserDeletedMessageHandler, | ||||
|     ); | ||||
|   }); | ||||
| 
 | ||||
|   afterEach(async () => { | ||||
|     jest.clearAllMocks(); | ||||
|   }); | ||||
| 
 | ||||
|   it('should be defined', () => { | ||||
|     expect(userDeletedMessageHandler).toBeDefined(); | ||||
|   }); | ||||
| 
 | ||||
|   it('should call the command bus', async () => { | ||||
|     const userId = '4eb6a6af-ecfd-41c3-9118-473a507014d4'; | ||||
|     const userDeletedMessage = `{"id":"${userId}"}`; | ||||
|     await userDeletedMessageHandler.userDeleted(userDeletedMessage); | ||||
|     expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); | ||||
|     expect(mockCommandBus.execute.mock.lastCall[0].id).toBe(userId); | ||||
|   }); | ||||
| }); | ||||
		Loading…
	
		Reference in New Issue