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