4 Commits
1.6.0 ... 1.7.0

Author SHA1 Message Date
Romain Thouvenin
d48d01f051 Implement distributed event handler to propagate ad deletion 2024-04-25 16:06:59 +02:00
Romain Thouvenin
1701fbbeb1 Implement a DeleteAdCommand 2024-04-25 16:06:59 +02:00
Romain Thouvenin
a7c281d740 fix retrieval of DB records not including schedule or waypoints 2024-04-25 15:14:35 +02:00
Romain Thouvenin
01ebac7e74 DRY the pax/driver schedule adjustment logic 2024-04-12 11:33:17 +02:00
15 changed files with 354 additions and 205 deletions

View File

@@ -15,6 +15,9 @@ export const MATCHER_AD_CREATION_FAILED_ROUTING_KEY =
export const AD_CREATED_MESSAGE_HANDLER = 'adCreated';
export const AD_CREATED_ROUTING_KEY = 'ad.created';
export const AD_CREATED_QUEUE = 'matcher.ad.created';
export const AD_DELETED_MESSAGE_HANDLER = 'adDeleted';
export const AD_DELETED_ROUTING_KEY = 'ad.deleted';
export const AD_DELETED_QUEUE = 'matcher.ad.deleted';
// health
export const GRPC_HEALTH_PACKAGE_NAME = 'health';

View File

@@ -1,19 +1,19 @@
import { ExtendedMapper } from '@mobicoop/ddd-library';
import { DirectionEncoderPort } from '@modules/geography/core/application/ports/direction-encoder.port';
import { Inject, Injectable } from '@nestjs/common';
import { AdEntity } from './core/domain/ad.entity';
import {
AdWriteModel,
AdReadModel,
ScheduleItemModel,
AdWriteExtraModel,
} from './infrastructure/ad.repository';
import { v4 } from 'uuid';
import { AD_DIRECTION_ENCODER } from './ad.di-tokens';
import { AdEntity } from './core/domain/ad.entity';
import {
ScheduleItem,
ScheduleItemProps,
} from './core/domain/value-objects/schedule-item.value-object';
import { DirectionEncoderPort } from '@modules/geography/core/application/ports/direction-encoder.port';
import { AD_DIRECTION_ENCODER } from './ad.di-tokens';
import { ExtendedMapper } from '@mobicoop/ddd-library';
import {
AdReadModel,
AdWriteExtraModel,
AdWriteModel,
ScheduleItemModel,
} from './infrastructure/ad.repository';
/**
* Mapper constructs objects that are used in different layers:
@@ -97,7 +97,7 @@ export class AdMapper
frequency: record.frequency,
fromDate: record.fromDate.toISOString().split('T')[0],
toDate: record.toDate.toISOString().split('T')[0],
schedule: record.schedule.map(
schedule: record.schedule?.map(
(scheduleItem: ScheduleItemModel) =>
new ScheduleItem({
day: scheduleItem.day,
@@ -111,12 +111,14 @@ export class AdMapper
margin: scheduleItem.margin,
}),
),
waypoints: this.directionEncoder
.decode(record.waypoints)
.map((coordinates, index) => ({
position: index,
...coordinates,
})),
waypoints: record.waypoints
? this.directionEncoder
.decode(record.waypoints)
.map((coordinates, index) => ({
position: index,
...coordinates,
}))
: [],
fwdAzimuth: record.fwdAzimuth,
backAzimuth: record.backAzimuth,
points: [],

View File

@@ -1,49 +1,51 @@
import { Module, Provider } from '@nestjs/common';
import { CqrsModule } from '@nestjs/cqrs';
import {
AD_MESSAGE_PUBLISHER,
AD_REPOSITORY,
AD_DIRECTION_ENCODER,
AD_ROUTE_PROVIDER,
TIMEZONE_FINDER,
TIME_CONVERTER,
INPUT_DATETIME_TRANSFORMER,
OUTPUT_DATETIME_TRANSFORMER,
MATCHING_REPOSITORY,
AD_CONFIGURATION_REPOSITORY,
GEOGRAPHY_PACKAGE,
} from './ad.di-tokens';
import { ConfigurationRepository } from '@mobicoop/configuration-module';
import { MessageBrokerPublisher } from '@mobicoop/message-broker-module';
import { AdRepository } from './infrastructure/ad.repository';
import { PrismaService } from './infrastructure/prisma.service';
import { AdMapper } from './ad.mapper';
import { AdCreatedMessageHandler } from './interface/message-handlers/ad-created.message-handler';
import { PostgresDirectionEncoder } from '@modules/geography/infrastructure/postgres-direction-encoder';
import { GeographyModule } from '@modules/geography/geography.module';
import { CreateAdService } from './core/application/commands/create-ad/create-ad.service';
import { MatchGrpcController } from './interface/grpc-controllers/match.grpc-controller';
import { MatchQueryHandler } from './core/application/queries/match/match.query-handler';
import { TimezoneFinder } from './infrastructure/timezone-finder';
import { TimeConverter } from './infrastructure/time-converter';
import { InputDateTimeTransformer } from './infrastructure/input-datetime-transformer';
import { MatchMapper } from './match.mapper';
import { OutputDateTimeTransformer } from './infrastructure/output-datetime-transformer';
import { MatchingRepository } from './infrastructure/matching.repository';
import { MatchingMapper } from './matching.mapper';
import { PostgresDirectionEncoder } from '@modules/geography/infrastructure/postgres-direction-encoder';
import { CacheModule } from '@nestjs/cache-manager';
import { Module, Provider } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { redisStore } from 'cache-manager-ioredis-yet';
import { CqrsModule } from '@nestjs/cqrs';
import { ClientsModule, Transport } from '@nestjs/microservices';
import {
RedisClientOptions,
RedisModule,
RedisModuleOptions,
} from '@songkeys/nestjs-redis';
import { ConfigurationRepository } from '@mobicoop/configuration-module';
import { PublishMessageWhenMatcherAdIsCreatedDomainEventHandler } from './core/application/event-handlers/publish-message-when-matcher-ad-is-created.domain-event-handler';
import { Georouter } from './infrastructure/georouter';
import { ClientsModule, Transport } from '@nestjs/microservices';
import { GRPC_GEOGRAPHY_PACKAGE_NAME } from '@src/app.constants';
import { redisStore } from 'cache-manager-ioredis-yet';
import { join } from 'path';
import {
AD_CONFIGURATION_REPOSITORY,
AD_DIRECTION_ENCODER,
AD_MESSAGE_PUBLISHER,
AD_REPOSITORY,
AD_ROUTE_PROVIDER,
GEOGRAPHY_PACKAGE,
INPUT_DATETIME_TRANSFORMER,
MATCHING_REPOSITORY,
OUTPUT_DATETIME_TRANSFORMER,
TIMEZONE_FINDER,
TIME_CONVERTER,
} from './ad.di-tokens';
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 { PublishMessageWhenMatcherAdIsCreatedDomainEventHandler } from './core/application/event-handlers/publish-message-when-matcher-ad-is-created.domain-event-handler';
import { MatchQueryHandler } from './core/application/queries/match/match.query-handler';
import { AdRepository } from './infrastructure/ad.repository';
import { Georouter } from './infrastructure/georouter';
import { InputDateTimeTransformer } from './infrastructure/input-datetime-transformer';
import { MatchingRepository } from './infrastructure/matching.repository';
import { OutputDateTimeTransformer } from './infrastructure/output-datetime-transformer';
import { PrismaService } from './infrastructure/prisma.service';
import { TimeConverter } from './infrastructure/time-converter';
import { TimezoneFinder } from './infrastructure/timezone-finder';
import { MatchGrpcController } from './interface/grpc-controllers/match.grpc-controller';
import { AdCreatedMessageHandler } from './interface/message-handlers/ad-created.message-handler';
import { AdDeletedMessageHandler } from './interface/message-handlers/ad-deleted.message-handler';
import { MatchMapper } from './match.mapper';
import { MatchingMapper } from './matching.mapper';
const imports = [
CqrsModule,
@@ -96,13 +98,13 @@ const imports = [
const grpcControllers = [MatchGrpcController];
const messageHandlers = [AdCreatedMessageHandler];
const messageHandlers = [AdCreatedMessageHandler, AdDeletedMessageHandler];
const eventHandlers: Provider[] = [
PublishMessageWhenMatcherAdIsCreatedDomainEventHandler,
];
const commandHandlers: Provider[] = [CreateAdService];
const commandHandlers: Provider[] = [CreateAdService, DeleteAdService];
const queryHandlers: Provider[] = [MatchQueryHandler];

View File

@@ -0,0 +1,7 @@
import { Command, CommandProps } from '@mobicoop/ddd-library';
export class DeleteAdCommand extends Command {
constructor(props: CommandProps<DeleteAdCommand>) {
super(props);
}
}

View File

@@ -0,0 +1,18 @@
import { AD_REPOSITORY } from '@modules/ad/ad.di-tokens';
import { Inject } from '@nestjs/common';
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';
import { AdRepositoryPort } from '../../ports/ad.repository.port';
import { DeleteAdCommand } from './delete-ad.command';
@CommandHandler(DeleteAdCommand)
export class DeleteAdService implements ICommandHandler {
constructor(
@Inject(AD_REPOSITORY) private readonly adRepository: AdRepositoryPort,
) {}
async execute(command: DeleteAdCommand): Promise<boolean> {
const ad = await this.adRepository.findOneById(command.id);
ad.delete();
return this.adRepository.delete(ad);
}
}

View File

@@ -1,5 +1,6 @@
import { AggregateRoot, AggregateID } from '@mobicoop/ddd-library';
import { AggregateID, AggregateRoot } from '@mobicoop/ddd-library';
import { AdProps, CreateAdProps } from './ad.types';
import { AdDeletedDomainEvent } from './events/ad-delete.domain-event';
import { MatcherAdCreatedDomainEvent } from './events/matcher-ad-created.domain-event';
export class AdEntity extends AggregateRoot<AdProps> {
@@ -26,6 +27,14 @@ export class AdEntity extends AggregateRoot<AdProps> {
return ad;
};
delete(): void {
this.addEvent(
new AdDeletedDomainEvent({
aggregateId: this.id,
}),
);
}
validate(): void {
// entity business rules validation to protect it's invariant before saving entity to a database
}

View File

@@ -2,12 +2,14 @@ import {
AggregateID,
AggregateRoot,
ArgumentInvalidException,
ValueObject,
} from '@mobicoop/ddd-library';
import { Role } from './ad.types';
import { CalendarTools } from './calendar-tools.service';
import {
CandidateProps,
CreateCandidateProps,
DateInterval,
Target,
} from './candidate.types';
import { ActorTime } from './value-objects/actor-time.value-object';
@@ -101,57 +103,13 @@ export class CandidateEntity extends AggregateRoot<CandidateProps> {
* Create the driver schedule based on the passenger schedule
*/
private _createDriverSchedule = (): void => {
let driverSchedule: Array<ScheduleItemProps | undefined> =
this.props.passengerSchedule!.map(
(scheduleItemProps: ScheduleItemProps) => ({
day: scheduleItemProps.day,
time: scheduleItemProps.time,
margin: scheduleItemProps.margin,
}),
);
// adjust the driver theoretical schedule :
// we guess the ideal driver departure time based on the duration to
// reach the passenger starting point from the driver starting point
driverSchedule = driverSchedule
.map((scheduleItemProps: ScheduleItemProps) => {
try {
const driverDate: Date = CalendarTools.firstDate(
scheduleItemProps.day,
this.props.dateInterval,
);
const driverStartDatetime: Date = CalendarTools.datetimeWithSeconds(
driverDate,
scheduleItemProps.time,
-this._passengerStartDuration(),
);
return <ScheduleItemProps>{
day: driverDate.getUTCDay(),
margin: scheduleItemProps.margin,
time: `${driverStartDatetime
.getUTCHours()
.toString()
.padStart(2, '0')}:${driverStartDatetime
.getUTCMinutes()
.toString()
.padStart(2, '0')}`,
};
} catch (e) {
// no possible driver date or time
// TODO : find a test case !
return undefined;
}
})
.filter(
(scheduleItemProps: ScheduleItemProps | undefined) =>
scheduleItemProps !== undefined,
);
this.props.driverSchedule = driverSchedule.map(
(scheduleItemProps: ScheduleItemProps) => ({
day: scheduleItemProps.day,
time: scheduleItemProps.time,
margin: scheduleItemProps.margin,
}),
const passengerSchedule = new Schedule(
this.props.passengerSchedule!,
this.props.dateInterval,
);
this.props.driverSchedule = passengerSchedule
.adjust(-this._passengerStartDuration())
.unpack().items;
};
/**
@@ -174,57 +132,14 @@ export class CandidateEntity extends AggregateRoot<CandidateProps> {
* Create the passenger schedule based on the driver schedule
*/
private _createPassengerSchedule = (): void => {
let passengerSchedule: Array<ScheduleItemProps | undefined> =
this.props.driverSchedule!.map(
(scheduleItemProps: ScheduleItemProps) => ({
day: scheduleItemProps.day,
time: scheduleItemProps.time,
margin: scheduleItemProps.margin,
}),
);
// adjust the passenger theoretical schedule :
// we guess the ideal passenger departure time based on the duration to
// reach the passenger starting point from the driver starting point
passengerSchedule = passengerSchedule
.map((scheduleItemProps: ScheduleItemProps) => {
try {
const passengerDate: Date = CalendarTools.firstDate(
scheduleItemProps.day,
this.props.dateInterval,
);
const passengeStartDatetime: Date = CalendarTools.datetimeWithSeconds(
passengerDate,
scheduleItemProps.time,
this._passengerStartDuration(),
);
return {
day: passengerDate.getUTCDay(),
margin: scheduleItemProps.margin,
time: `${passengeStartDatetime
.getUTCHours()
.toString()
.padStart(2, '0')}:${passengeStartDatetime
.getUTCMinutes()
.toString()
.padStart(2, '0')}`,
};
} catch (e) {
// no possible passenger date or time
// TODO : find a test case !
return undefined;
}
})
.filter(
(scheduleItemProps: ScheduleItemProps | undefined) =>
scheduleItemProps !== undefined,
);
this.props.passengerSchedule = passengerSchedule.map(
(scheduleItemProps: ScheduleItemProps) => ({
day: scheduleItemProps.day,
time: scheduleItemProps.time,
margin: scheduleItemProps.margin,
}),
const driverSchedule = new Schedule(
this.props.driverSchedule!,
this.props.dateInterval,
);
this.props.passengerSchedule = driverSchedule
.adjust(this._passengerStartDuration())
.unpack().items;
};
private _createJourney = (driverScheduleItem: ScheduleItem): Journey =>
@@ -407,6 +322,60 @@ export class CandidateEntity extends AggregateRoot<CandidateProps> {
}
}
//TODO Use this class as part of the CandidateEntity aggregate
class Schedule extends ValueObject<{
items: ScheduleItemProps[];
dateInterval: DateInterval;
}> {
constructor(items: ScheduleItemProps[], dateInterval: DateInterval) {
super({ items, dateInterval });
}
protected validate(): void {}
/**
* Add the given duration to each schedule item
* unless the expected new datetime is not possible,
* in which case the item is removed from the adjusted schedule
* @param duration time increment in seconds (can be negative)
* @returns the new adjusted schedule
*/
adjust(duration: number): Schedule {
const newItems = this.props.items.reduce((acc, scheduleItemProps) => {
try {
const itemDate: Date = CalendarTools.firstDate(
scheduleItemProps.day,
this.props.dateInterval,
);
const driverStartDatetime: Date = CalendarTools.datetimeWithSeconds(
itemDate,
scheduleItemProps.time,
duration,
);
acc.push({
day: itemDate.getUTCDay(),
margin: scheduleItemProps.margin,
time: this._formatTime(driverStartDatetime),
});
} catch (e) {
// no possible driver date or time
// TODO : find a test case !
}
return acc;
}, new Array<ScheduleItemProps>());
return new Schedule(newItems, this.props.dateInterval);
}
private _formatTime(dateTime: Date) {
return (
dateTime.getUTCHours().toString().padStart(2, '0') +
':' +
dateTime.getUTCMinutes().toString().padStart(2, '0')
);
}
}
type ScheduleItemRange = {
scheduleItem: ScheduleItem;
range: Date[];

View File

@@ -0,0 +1,7 @@
import { DomainEvent, DomainEventProps } from '@mobicoop/ddd-library';
export class AdDeletedDomainEvent extends DomainEvent {
constructor(props: DomainEventProps<AdDeletedDomainEvent>) {
super(props);
}
}

View File

@@ -0,0 +1,32 @@
import { RabbitSubscribe } from '@mobicoop/message-broker-module';
import { DeleteAdCommand } from '@modules/ad/core/application/commands/delete-ad/delete-ad.command';
import { Injectable } from '@nestjs/common';
import { CommandBus } from '@nestjs/cqrs';
import {
AD_DELETED_MESSAGE_HANDLER,
AD_DELETED_ROUTING_KEY,
} from '@src/app.constants';
import { AdReference } from './ad.types';
@Injectable()
export class AdDeletedMessageHandler {
constructor(private readonly commandBus: CommandBus) {}
@RabbitSubscribe({
name: AD_DELETED_MESSAGE_HANDLER,
routingKey: AD_DELETED_ROUTING_KEY,
})
public async adDeleted(message: string): Promise<void> {
try {
const deletedAd: AdReference = JSON.parse(message);
await this.commandBus.execute(
new DeleteAdCommand({
id: deletedAd.aggregateId,
}),
);
} catch (error: any) {
// do not throw error to acknowledge incoming message
// error handling should be done in the command handler, if relevant
}
}
}

View File

@@ -1,7 +1,10 @@
import { Frequency } from '@modules/ad/core/domain/ad.types';
export type Ad = {
export type AdReference = {
aggregateId: string;
};
export type Ad = AdReference & {
driver: boolean;
passenger: boolean;
frequency: Frequency;

View File

@@ -1,52 +1,17 @@
import { AdEntity } from '@modules/ad/core/domain/ad.entity';
import { CreateAdProps, Frequency } from '@modules/ad/core/domain/ad.types';
import { PointProps } from '@modules/ad/core/domain/value-objects/point.value-object';
const originPointProps: PointProps = {
lat: 48.689445,
lon: 6.17651,
};
const destinationPointProps: PointProps = {
lat: 48.8566,
lon: 2.3522,
};
const createAdProps: CreateAdProps = {
id: 'e8fe64b1-4c33-49e1-9f69-4db48b21df36',
driver: true,
passenger: true,
fromDate: '2023-06-21',
toDate: '2023-06-21',
schedule: [
{
day: 3,
time: '08:30',
margin: 900,
},
],
frequency: Frequency.PUNCTUAL,
seatsProposed: 3,
seatsRequested: 1,
strict: false,
waypoints: [originPointProps, destinationPointProps],
driverDistance: 23000,
driverDuration: 900,
passengerDistance: 23000,
passengerDuration: 900,
fwdAzimuth: 283,
backAzimuth: 93,
points: [],
};
import { createAdProps } from './ad.fixtures';
describe('Ad entity create', () => {
it('should create a new entity', async () => {
const ad: AdEntity = AdEntity.create(createAdProps);
expect(ad.id.length).toBe(36);
expect(ad.getProps().schedule.length).toBe(1);
expect(ad.getProps().schedule[0].day).toBe(3);
expect(ad.getProps().schedule[0].time).toBe('08:30');
expect(ad.getProps().driver).toBeTruthy();
expect(ad.getProps().passenger).toBeTruthy();
expect(ad.getProps().driverDistance).toBe(23000);
describe('create', () => {
it('should create a new entity', async () => {
const ad: AdEntity = AdEntity.create(createAdProps());
expect(ad.id.length).toBe(36);
expect(ad.getProps().schedule.length).toBe(1);
expect(ad.getProps().schedule[0].day).toBe(3);
expect(ad.getProps().schedule[0].time).toBe('08:30');
expect(ad.getProps().driver).toBeTruthy();
expect(ad.getProps().passenger).toBeTruthy();
expect(ad.getProps().driverDistance).toBe(23000);
});
});
});

View File

@@ -0,0 +1,40 @@
import { CreateAdProps, Frequency } from '@modules/ad/core/domain/ad.types';
import { PointProps } from '@modules/ad/core/domain/value-objects/point.value-object';
const originPointProps: PointProps = {
lat: 48.689445,
lon: 6.17651,
};
const destinationPointProps: PointProps = {
lat: 48.8566,
lon: 2.3522,
};
export function createAdProps(): CreateAdProps {
return {
id: 'e8fe64b1-4c33-49e1-9f69-4db48b21df36',
driver: true,
passenger: true,
fromDate: '2023-06-21',
toDate: '2023-06-21',
schedule: [
{
day: 3,
time: '08:30',
margin: 900,
},
],
frequency: Frequency.PUNCTUAL,
seatsProposed: 3,
seatsRequested: 1,
strict: false,
waypoints: [originPointProps, destinationPointProps],
driverDistance: 23000,
driverDuration: 900,
passengerDistance: 23000,
passengerDuration: 900,
fwdAzimuth: 283,
backAzimuth: 93,
points: [],
};
}

View File

@@ -0,0 +1,41 @@
import { AD_REPOSITORY } from '@modules/ad/ad.di-tokens';
import { DeleteAdCommand } from '@modules/ad/core/application/commands/delete-ad/delete-ad.command';
import { DeleteAdService } from '@modules/ad/core/application/commands/delete-ad/delete-ad.service';
import { AdEntity } from '@modules/ad/core/domain/ad.entity';
import { Test, TestingModule } from '@nestjs/testing';
import { createAdProps } from './ad.fixtures';
const ad: AdEntity = AdEntity.create(createAdProps());
const mockAdRepository = {
findOneById: jest.fn().mockImplementation(() => ad),
delete: jest.fn(),
};
describe('DeleteAdService', () => {
let deleteAdService: DeleteAdService;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
{
provide: AD_REPOSITORY,
useValue: mockAdRepository,
},
DeleteAdService,
],
}).compile();
deleteAdService = module.get<DeleteAdService>(DeleteAdService);
});
it('should be defined', () => {
expect(deleteAdService).toBeDefined();
});
it('should execute the delete logic and delete the ad from the repository', async () => {
jest.spyOn(ad, 'delete');
await deleteAdService.execute(new DeleteAdCommand(ad.id));
expect(ad.delete).toHaveBeenCalled();
expect(mockAdRepository.delete).toHaveBeenCalledWith(ad);
});
});

View File

@@ -0,0 +1,44 @@
import { AdDeletedMessageHandler } from '@modules/ad/interface/message-handlers/ad-deleted.message-handler';
import { CommandBus } from '@nestjs/cqrs';
import { Test, TestingModule } from '@nestjs/testing';
const adDeletedMessage =
'{"aggregateId":"4eb6a6af-ecfd-41c3-9118-473a507014d4"}';
const mockCommandBus = {
execute: jest.fn(),
};
describe('Ad Deleted Message Handler', () => {
let adDeletedMessageHandler: AdDeletedMessageHandler;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
{
provide: CommandBus,
useValue: mockCommandBus,
},
AdDeletedMessageHandler,
],
}).compile();
adDeletedMessageHandler = module.get<AdDeletedMessageHandler>(
AdDeletedMessageHandler,
);
});
afterEach(async () => {
jest.clearAllMocks();
});
it('should be defined', () => {
expect(adDeletedMessageHandler).toBeDefined();
});
it('should call the delete command', async () => {
jest.spyOn(mockCommandBus, 'execute');
await adDeletedMessageHandler.adDeleted(adDeletedMessage);
expect(mockCommandBus.execute).toHaveBeenCalledTimes(1);
});
});

View File

@@ -1,17 +1,20 @@
import { Module, Provider } from '@nestjs/common';
import { MESSAGE_PUBLISHER } from './messager.di-tokens';
import {
MessageBrokerModule,
MessageBrokerModuleOptions,
MessageBrokerPublisher,
} from '@mobicoop/message-broker-module';
import { Module, Provider } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import {
AD_CREATED_MESSAGE_HANDLER,
AD_CREATED_QUEUE,
AD_CREATED_ROUTING_KEY,
AD_DELETED_MESSAGE_HANDLER,
AD_DELETED_QUEUE,
AD_DELETED_ROUTING_KEY,
SERVICE_NAME,
} from '@src/app.constants';
import { MESSAGE_PUBLISHER } from './messager.di-tokens';
const imports = [
MessageBrokerModule.forRootAsync({
@@ -33,6 +36,10 @@ const imports = [
routingKey: AD_CREATED_ROUTING_KEY,
queue: AD_CREATED_QUEUE,
},
[AD_DELETED_MESSAGE_HANDLER]: {
routingKey: AD_DELETED_ROUTING_KEY,
queue: AD_DELETED_QUEUE,
},
},
}),
}),