mirror of
https://gitlab.com/mobicoop/v3/service/matcher.git
synced 2026-01-01 04:32:41 +00:00
Compare commits
16 Commits
1.6_checkc
...
1.7.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d48d01f051 | ||
|
|
1701fbbeb1 | ||
|
|
a7c281d740 | ||
|
|
01ebac7e74 | ||
|
|
104559d03d | ||
|
|
173e5ebba5 | ||
|
|
0dc01da2b0 | ||
|
|
945ce80840 | ||
|
|
16ebe8d543 | ||
|
|
0c29e522ed | ||
|
|
c51c368d83 | ||
|
|
0446d267ef | ||
|
|
100fb3487d | ||
|
|
e501bef249 | ||
|
|
71ac97410a | ||
|
|
5696ac57bd |
@@ -8,25 +8,6 @@ include:
|
|||||||
- template: Security/SAST.gitlab-ci.yml
|
- template: Security/SAST.gitlab-ci.yml
|
||||||
- template: Security/Secret-Detection.gitlab-ci.yml
|
- template: Security/Secret-Detection.gitlab-ci.yml
|
||||||
- project: mobicoop/v3/gitlab-templates
|
- project: mobicoop/v3/gitlab-templates
|
||||||
file: /ci/release.build-job.yml
|
file:
|
||||||
|
- /ci/release.build-job.yml
|
||||||
##############
|
- /ci/service.test-job.yml
|
||||||
# TEST STAGE #
|
|
||||||
##############
|
|
||||||
|
|
||||||
test:
|
|
||||||
stage: test
|
|
||||||
image: docker/compose:latest
|
|
||||||
variables:
|
|
||||||
DOCKER_TLS_CERTDIR: ''
|
|
||||||
services:
|
|
||||||
- docker:dind
|
|
||||||
script:
|
|
||||||
- docker-compose -f docker-compose.ci.tools.yml -p matcher-tools --env-file ci/.env.ci up -d
|
|
||||||
- sh ci/wait-up.sh
|
|
||||||
- docker-compose -f docker-compose.ci.service.yml -p matcher-service --env-file ci/.env.ci up -d
|
|
||||||
- docker exec -t v3-matcher-api sh -c "npm run test:integration:ci"
|
|
||||||
coverage: /All files[^|]*\|[^|]*\s+([\d\.]+)/
|
|
||||||
rules:
|
|
||||||
- if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH || $CI_COMMIT_MESSAGE =~ /--check/ || $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
|
||||||
when: always
|
|
||||||
|
|||||||
@@ -167,6 +167,7 @@ The app exposes the following [gRPC](https://grpc.io/) services :
|
|||||||
- **seatsProposed** (integer, optional): number of seats proposed as driver (_default : 3_)
|
- **seatsProposed** (integer, optional): number of seats proposed as driver (_default : 3_)
|
||||||
- **seatsRequested** (integer, optional): number of seats requested as passenger (_default : 1_)
|
- **seatsRequested** (integer, optional): number of seats requested as passenger (_default : 1_)
|
||||||
- **waypoints**: an array of addresses that represent the waypoints of the journey (only first and last waypoints are used for passenger ads). Note that positions are **required** and **must** be consecutives
|
- **waypoints**: an array of addresses that represent the waypoints of the journey (only first and last waypoints are used for passenger ads). Note that positions are **required** and **must** be consecutives
|
||||||
|
- **excludedAdId** (optional): the id of an ad to be excluded from the results (useful to avoid self-matchings)
|
||||||
- **algorithmType** (optional): the type of algorithm to use (as of 2023-09-28, only the default `PASSENGER_ORIENTED` is accepted)
|
- **algorithmType** (optional): the type of algorithm to use (as of 2023-09-28, only the default `PASSENGER_ORIENTED` is accepted)
|
||||||
- **remoteness** (integer, optional): an integer to indicate the maximum flying distance (in metres) between the driver route and the passenger pick-up / drop-off points (_default : 15000_)
|
- **remoteness** (integer, optional): an integer to indicate the maximum flying distance (in metres) between the driver route and the passenger pick-up / drop-off points (_default : 15000_)
|
||||||
- **useProportion** (boolean, optional): a boolean to indicate if the matching algorithm will compare the distance of the passenger route against the distance of the driver route (_default : 1_). Works in combination with **proportion** parameter
|
- **useProportion** (boolean, optional): a boolean to indicate if the matching algorithm will compare the distance of the passenger route against the distance of the driver route (_default : 1_). Works in combination with **proportion** parameter
|
||||||
@@ -218,10 +219,6 @@ If the matching is successful, you will get a result, containing :
|
|||||||
Matching is a time-consuming process, so the results of a matching request are stored in cache before being paginated and returned to the requester.
|
Matching is a time-consuming process, so the results of a matching request are stored in cache before being paginated and returned to the requester.
|
||||||
An id is attributed to the overall results of a request : on further requests (for example to query for different pages of results), the requester can provide this id and get in return the cached data, avoiding another longer process of computing the results from scratch. Obviously, new computing must be done periodically to get fresh new results !
|
An id is attributed to the overall results of a request : on further requests (for example to query for different pages of results), the requester can provide this id and get in return the cached data, avoiding another longer process of computing the results from scratch. Obviously, new computing must be done periodically to get fresh new results !
|
||||||
|
|
||||||
There's also a basic cache to store the results of the _same_ request sent multiple times successively.
|
|
||||||
|
|
||||||
Cache TTLs are customizable in the `.env` file.
|
|
||||||
|
|
||||||
## Tests / ESLint / Prettier
|
## Tests / ESLint / Prettier
|
||||||
|
|
||||||
Tests are run outside the container for ease of use (switching between different environments inside containers using prisma is complicated and error prone).
|
Tests are run outside the container for ease of use (switching between different environments inside containers using prisma is complicated and error prone).
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ export const MATCHER_AD_CREATION_FAILED_ROUTING_KEY =
|
|||||||
export const AD_CREATED_MESSAGE_HANDLER = 'adCreated';
|
export const AD_CREATED_MESSAGE_HANDLER = 'adCreated';
|
||||||
export const AD_CREATED_ROUTING_KEY = 'ad.created';
|
export const AD_CREATED_ROUTING_KEY = 'ad.created';
|
||||||
export const AD_CREATED_QUEUE = 'matcher.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
|
// health
|
||||||
export const GRPC_HEALTH_PACKAGE_NAME = 'health';
|
export const GRPC_HEALTH_PACKAGE_NAME = 'health';
|
||||||
|
|||||||
@@ -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 { 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 { v4 } from 'uuid';
|
||||||
|
import { AD_DIRECTION_ENCODER } from './ad.di-tokens';
|
||||||
|
import { AdEntity } from './core/domain/ad.entity';
|
||||||
import {
|
import {
|
||||||
ScheduleItem,
|
ScheduleItem,
|
||||||
ScheduleItemProps,
|
ScheduleItemProps,
|
||||||
} from './core/domain/value-objects/schedule-item.value-object';
|
} from './core/domain/value-objects/schedule-item.value-object';
|
||||||
import { DirectionEncoderPort } from '@modules/geography/core/application/ports/direction-encoder.port';
|
import {
|
||||||
import { AD_DIRECTION_ENCODER } from './ad.di-tokens';
|
AdReadModel,
|
||||||
import { ExtendedMapper } from '@mobicoop/ddd-library';
|
AdWriteExtraModel,
|
||||||
|
AdWriteModel,
|
||||||
|
ScheduleItemModel,
|
||||||
|
} from './infrastructure/ad.repository';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mapper constructs objects that are used in different layers:
|
* Mapper constructs objects that are used in different layers:
|
||||||
@@ -97,7 +97,7 @@ export class AdMapper
|
|||||||
frequency: record.frequency,
|
frequency: record.frequency,
|
||||||
fromDate: record.fromDate.toISOString().split('T')[0],
|
fromDate: record.fromDate.toISOString().split('T')[0],
|
||||||
toDate: record.toDate.toISOString().split('T')[0],
|
toDate: record.toDate.toISOString().split('T')[0],
|
||||||
schedule: record.schedule.map(
|
schedule: record.schedule?.map(
|
||||||
(scheduleItem: ScheduleItemModel) =>
|
(scheduleItem: ScheduleItemModel) =>
|
||||||
new ScheduleItem({
|
new ScheduleItem({
|
||||||
day: scheduleItem.day,
|
day: scheduleItem.day,
|
||||||
@@ -111,12 +111,14 @@ export class AdMapper
|
|||||||
margin: scheduleItem.margin,
|
margin: scheduleItem.margin,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
waypoints: this.directionEncoder
|
waypoints: record.waypoints
|
||||||
|
? this.directionEncoder
|
||||||
.decode(record.waypoints)
|
.decode(record.waypoints)
|
||||||
.map((coordinates, index) => ({
|
.map((coordinates, index) => ({
|
||||||
position: index,
|
position: index,
|
||||||
...coordinates,
|
...coordinates,
|
||||||
})),
|
}))
|
||||||
|
: [],
|
||||||
fwdAzimuth: record.fwdAzimuth,
|
fwdAzimuth: record.fwdAzimuth,
|
||||||
backAzimuth: record.backAzimuth,
|
backAzimuth: record.backAzimuth,
|
||||||
points: [],
|
points: [],
|
||||||
|
|||||||
@@ -1,49 +1,51 @@
|
|||||||
import { Module, Provider } from '@nestjs/common';
|
import { ConfigurationRepository } from '@mobicoop/configuration-module';
|
||||||
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 { MessageBrokerPublisher } from '@mobicoop/message-broker-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 { GeographyModule } from '@modules/geography/geography.module';
|
||||||
import { CreateAdService } from './core/application/commands/create-ad/create-ad.service';
|
import { PostgresDirectionEncoder } from '@modules/geography/infrastructure/postgres-direction-encoder';
|
||||||
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 { CacheModule } from '@nestjs/cache-manager';
|
import { CacheModule } from '@nestjs/cache-manager';
|
||||||
|
import { Module, Provider } from '@nestjs/common';
|
||||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
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 {
|
import {
|
||||||
RedisClientOptions,
|
RedisClientOptions,
|
||||||
RedisModule,
|
RedisModule,
|
||||||
RedisModuleOptions,
|
RedisModuleOptions,
|
||||||
} from '@songkeys/nestjs-redis';
|
} 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 { GRPC_GEOGRAPHY_PACKAGE_NAME } from '@src/app.constants';
|
||||||
|
import { redisStore } from 'cache-manager-ioredis-yet';
|
||||||
import { join } from 'path';
|
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 = [
|
const imports = [
|
||||||
CqrsModule,
|
CqrsModule,
|
||||||
@@ -96,13 +98,13 @@ const imports = [
|
|||||||
|
|
||||||
const grpcControllers = [MatchGrpcController];
|
const grpcControllers = [MatchGrpcController];
|
||||||
|
|
||||||
const messageHandlers = [AdCreatedMessageHandler];
|
const messageHandlers = [AdCreatedMessageHandler, AdDeletedMessageHandler];
|
||||||
|
|
||||||
const eventHandlers: Provider[] = [
|
const eventHandlers: Provider[] = [
|
||||||
PublishMessageWhenMatcherAdIsCreatedDomainEventHandler,
|
PublishMessageWhenMatcherAdIsCreatedDomainEventHandler,
|
||||||
];
|
];
|
||||||
|
|
||||||
const commandHandlers: Provider[] = [CreateAdService];
|
const commandHandlers: Provider[] = [CreateAdService, DeleteAdService];
|
||||||
|
|
||||||
const queryHandlers: Provider[] = [MatchQueryHandler];
|
const queryHandlers: Provider[] = [MatchQueryHandler];
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { Command, CommandProps } from '@mobicoop/ddd-library';
|
||||||
|
|
||||||
|
export class DeleteAdCommand extends Command {
|
||||||
|
constructor(props: CommandProps<DeleteAdCommand>) {
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,6 +26,7 @@ export class MatchQuery extends QueryBase {
|
|||||||
seatsRequested?: number;
|
seatsRequested?: number;
|
||||||
strict?: boolean;
|
strict?: boolean;
|
||||||
readonly waypoints: Waypoint[];
|
readonly waypoints: Waypoint[];
|
||||||
|
excludedAdId?: string;
|
||||||
algorithmType?: AlgorithmType;
|
algorithmType?: AlgorithmType;
|
||||||
remoteness?: number;
|
remoteness?: number;
|
||||||
useProportion?: boolean;
|
useProportion?: boolean;
|
||||||
@@ -56,6 +57,7 @@ export class MatchQuery extends QueryBase {
|
|||||||
this.seatsRequested = props.seatsRequested;
|
this.seatsRequested = props.seatsRequested;
|
||||||
this.strict = props.strict;
|
this.strict = props.strict;
|
||||||
this.waypoints = props.waypoints;
|
this.waypoints = props.waypoints;
|
||||||
|
this.excludedAdId = props.excludedAdId;
|
||||||
this.algorithmType = props.algorithmType;
|
this.algorithmType = props.algorithmType;
|
||||||
this.remoteness = props.remoteness;
|
this.remoteness = props.remoteness;
|
||||||
this.useProportion = props.useProportion;
|
this.useProportion = props.useProportion;
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ export class PassengerOrientedSelector extends Selector {
|
|||||||
this._whereStrict(),
|
this._whereStrict(),
|
||||||
this._whereDate(),
|
this._whereDate(),
|
||||||
this._whereSchedule(role),
|
this._whereSchedule(role),
|
||||||
|
this._whereExcludedAd(),
|
||||||
this._whereAzimuth(),
|
this._whereAzimuth(),
|
||||||
this._whereProportion(role),
|
this._whereProportion(role),
|
||||||
this._whereRemoteness(role),
|
this._whereRemoteness(role),
|
||||||
@@ -206,6 +207,9 @@ export class PassengerOrientedSelector extends Selector {
|
|||||||
return '';
|
return '';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private _whereExcludedAd = (): string =>
|
||||||
|
this.query.excludedAdId ? `ad.uuid <> '${this.query.excludedAdId}'` : '';
|
||||||
|
|
||||||
private _wherePassengerSchedule = (
|
private _wherePassengerSchedule = (
|
||||||
date: Date,
|
date: Date,
|
||||||
scheduleItem: ScheduleItem,
|
scheduleItem: ScheduleItem,
|
||||||
|
|||||||
@@ -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 { AdProps, CreateAdProps } from './ad.types';
|
||||||
|
import { AdDeletedDomainEvent } from './events/ad-delete.domain-event';
|
||||||
import { MatcherAdCreatedDomainEvent } from './events/matcher-ad-created.domain-event';
|
import { MatcherAdCreatedDomainEvent } from './events/matcher-ad-created.domain-event';
|
||||||
|
|
||||||
export class AdEntity extends AggregateRoot<AdProps> {
|
export class AdEntity extends AggregateRoot<AdProps> {
|
||||||
@@ -26,6 +27,14 @@ export class AdEntity extends AggregateRoot<AdProps> {
|
|||||||
return ad;
|
return ad;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
delete(): void {
|
||||||
|
this.addEvent(
|
||||||
|
new AdDeletedDomainEvent({
|
||||||
|
aggregateId: this.id,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
validate(): void {
|
validate(): void {
|
||||||
// entity business rules validation to protect it's invariant before saving entity to a database
|
// entity business rules validation to protect it's invariant before saving entity to a database
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,30 @@
|
|||||||
import {
|
import {
|
||||||
AggregateRoot,
|
|
||||||
AggregateID,
|
AggregateID,
|
||||||
|
AggregateRoot,
|
||||||
ArgumentInvalidException,
|
ArgumentInvalidException,
|
||||||
|
ValueObject,
|
||||||
} from '@mobicoop/ddd-library';
|
} from '@mobicoop/ddd-library';
|
||||||
|
import { Role } from './ad.types';
|
||||||
|
import { CalendarTools } from './calendar-tools.service';
|
||||||
import {
|
import {
|
||||||
CandidateProps,
|
CandidateProps,
|
||||||
CreateCandidateProps,
|
CreateCandidateProps,
|
||||||
|
DateInterval,
|
||||||
Target,
|
Target,
|
||||||
} from './candidate.types';
|
} from './candidate.types';
|
||||||
|
import { ActorTime } from './value-objects/actor-time.value-object';
|
||||||
|
import { Actor } from './value-objects/actor.value-object';
|
||||||
import {
|
import {
|
||||||
CarpoolPathItem,
|
CarpoolPathItem,
|
||||||
CarpoolPathItemProps,
|
CarpoolPathItemProps,
|
||||||
} from './value-objects/carpool-path-item.value-object';
|
} from './value-objects/carpool-path-item.value-object';
|
||||||
import { Step, StepProps } from './value-objects/step.value-object';
|
import { JourneyItem } from './value-objects/journey-item.value-object';
|
||||||
|
import { Journey, JourneyProps } from './value-objects/journey.value-object';
|
||||||
import {
|
import {
|
||||||
ScheduleItem,
|
ScheduleItem,
|
||||||
ScheduleItemProps,
|
ScheduleItemProps,
|
||||||
} from './value-objects/schedule-item.value-object';
|
} from './value-objects/schedule-item.value-object';
|
||||||
import { Journey } from './value-objects/journey.value-object';
|
import { Step, StepProps } from './value-objects/step.value-object';
|
||||||
import { CalendarTools } from './calendar-tools.service';
|
|
||||||
import { JourneyItem } from './value-objects/journey-item.value-object';
|
|
||||||
import { Actor } from './value-objects/actor.value-object';
|
|
||||||
import { ActorTime } from './value-objects/actor-time.value-object';
|
|
||||||
import { Role } from './ad.types';
|
|
||||||
|
|
||||||
export class CandidateEntity extends AggregateRoot<CandidateProps> {
|
export class CandidateEntity extends AggregateRoot<CandidateProps> {
|
||||||
protected readonly _id: AggregateID;
|
protected readonly _id: AggregateID;
|
||||||
@@ -63,18 +65,23 @@ export class CandidateEntity extends AggregateRoot<CandidateProps> {
|
|||||||
// driver and passenger schedules are eventually mandatory
|
// driver and passenger schedules are eventually mandatory
|
||||||
if (!this.props.driverSchedule) this._createDriverSchedule();
|
if (!this.props.driverSchedule) this._createDriverSchedule();
|
||||||
if (!this.props.passengerSchedule) this._createPassengerSchedule();
|
if (!this.props.passengerSchedule) this._createPassengerSchedule();
|
||||||
|
this.props.journeys = this.props.driverSchedule!.reduce(
|
||||||
|
(accJourneys: JourneyProps[], driverScheduleItem: ScheduleItem) => {
|
||||||
try {
|
try {
|
||||||
this.props.journeys = (this.props.driverSchedule as ScheduleItemProps[])
|
|
||||||
// first we create the journeys
|
// first we create the journeys
|
||||||
.map((driverScheduleItem: ScheduleItem) =>
|
const journey = this._createJourney(driverScheduleItem);
|
||||||
this._createJourney(driverScheduleItem),
|
|
||||||
)
|
|
||||||
// then we filter the ones with invalid pickups
|
// then we filter the ones with invalid pickups
|
||||||
.filter((journey: Journey) => journey.hasValidPickUp());
|
if (journey.hasValidPickUp()) {
|
||||||
|
accJourneys.push(journey);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// irrelevant journeys fall here
|
// irrelevant journeys fall here
|
||||||
// eg. no available day for the given date range
|
// eg. no available day for the given date range
|
||||||
}
|
}
|
||||||
|
return accJourneys;
|
||||||
|
},
|
||||||
|
new Array<JourneyProps>(),
|
||||||
|
);
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -96,47 +103,13 @@ export class CandidateEntity extends AggregateRoot<CandidateProps> {
|
|||||||
* Create the driver schedule based on the passenger schedule
|
* Create the driver schedule based on the passenger schedule
|
||||||
*/
|
*/
|
||||||
private _createDriverSchedule = (): void => {
|
private _createDriverSchedule = (): void => {
|
||||||
let driverSchedule: ScheduleItemProps[] = this.props.passengerSchedule!.map(
|
const passengerSchedule = new Schedule(
|
||||||
(scheduleItemProps: ScheduleItemProps) => ({
|
this.props.passengerSchedule!,
|
||||||
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) => {
|
|
||||||
const driverDate: Date = CalendarTools.firstDate(
|
|
||||||
scheduleItemProps.day,
|
|
||||||
this.props.dateInterval,
|
this.props.dateInterval,
|
||||||
);
|
);
|
||||||
const driverStartDatetime: Date = CalendarTools.datetimeWithSeconds(
|
this.props.driverSchedule = passengerSchedule
|
||||||
driverDate,
|
.adjust(-this._passengerStartDuration())
|
||||||
scheduleItemProps.time,
|
.unpack().items;
|
||||||
-this._passengerStartDuration(),
|
|
||||||
);
|
|
||||||
return {
|
|
||||||
day: driverDate.getUTCDay(),
|
|
||||||
margin: scheduleItemProps.margin,
|
|
||||||
time: `${driverStartDatetime
|
|
||||||
.getUTCHours()
|
|
||||||
.toString()
|
|
||||||
.padStart(2, '0')}:${driverStartDatetime
|
|
||||||
.getUTCMinutes()
|
|
||||||
.toString()
|
|
||||||
.padStart(2, '0')}`,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
);
|
|
||||||
this.props.driverSchedule = driverSchedule.map(
|
|
||||||
(scheduleItemProps: ScheduleItemProps) => ({
|
|
||||||
day: scheduleItemProps.day,
|
|
||||||
time: scheduleItemProps.time,
|
|
||||||
margin: scheduleItemProps.margin,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -159,47 +132,14 @@ export class CandidateEntity extends AggregateRoot<CandidateProps> {
|
|||||||
* Create the passenger schedule based on the driver schedule
|
* Create the passenger schedule based on the driver schedule
|
||||||
*/
|
*/
|
||||||
private _createPassengerSchedule = (): void => {
|
private _createPassengerSchedule = (): void => {
|
||||||
let passengerSchedule: ScheduleItemProps[] = this.props.driverSchedule!.map(
|
const driverSchedule = new Schedule(
|
||||||
(scheduleItemProps: ScheduleItemProps) => ({
|
this.props.driverSchedule!,
|
||||||
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) => {
|
|
||||||
const passengerDate: Date = CalendarTools.firstDate(
|
|
||||||
scheduleItemProps.day,
|
|
||||||
this.props.dateInterval,
|
this.props.dateInterval,
|
||||||
);
|
);
|
||||||
const passengeStartDatetime: Date = CalendarTools.datetimeWithSeconds(
|
|
||||||
passengerDate,
|
this.props.passengerSchedule = driverSchedule
|
||||||
scheduleItemProps.time,
|
.adjust(this._passengerStartDuration())
|
||||||
this._passengerStartDuration(),
|
.unpack().items;
|
||||||
);
|
|
||||||
return {
|
|
||||||
day: passengerDate.getUTCDay(),
|
|
||||||
margin: scheduleItemProps.margin,
|
|
||||||
time: `${passengeStartDatetime
|
|
||||||
.getUTCHours()
|
|
||||||
.toString()
|
|
||||||
.padStart(2, '0')}:${passengeStartDatetime
|
|
||||||
.getUTCMinutes()
|
|
||||||
.toString()
|
|
||||||
.padStart(2, '0')}`,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
);
|
|
||||||
this.props.passengerSchedule = passengerSchedule.map(
|
|
||||||
(scheduleItemProps: ScheduleItemProps) => ({
|
|
||||||
day: scheduleItemProps.day,
|
|
||||||
time: scheduleItemProps.time,
|
|
||||||
margin: scheduleItemProps.margin,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private _createJourney = (driverScheduleItem: ScheduleItem): Journey =>
|
private _createJourney = (driverScheduleItem: ScheduleItem): Journey =>
|
||||||
@@ -382,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 = {
|
type ScheduleItemRange = {
|
||||||
scheduleItem: ScheduleItem;
|
scheduleItem: ScheduleItem;
|
||||||
range: Date[];
|
range: Date[];
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { DomainEvent, DomainEventProps } from '@mobicoop/ddd-library';
|
||||||
|
|
||||||
|
export class AdDeletedDomainEvent extends DomainEvent {
|
||||||
|
constructor(props: DomainEventProps<AdDeletedDomainEvent>) {
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,8 +2,7 @@ import {
|
|||||||
ArgumentOutOfRangeException,
|
ArgumentOutOfRangeException,
|
||||||
ValueObject,
|
ValueObject,
|
||||||
} from '@mobicoop/ddd-library';
|
} from '@mobicoop/ddd-library';
|
||||||
import { Actor, ActorProps } from './actor.value-object';
|
import { ActorProps } from './actor.value-object';
|
||||||
import { Role } from '../ad.types';
|
|
||||||
import { Point, PointProps } from './point.value-object';
|
import { Point, PointProps } from './point.value-object';
|
||||||
|
|
||||||
/** Note:
|
/** Note:
|
||||||
@@ -36,12 +35,5 @@ export class CarpoolPathItem extends ValueObject<CarpoolPathItemProps> {
|
|||||||
});
|
});
|
||||||
if (props.actors.length <= 0)
|
if (props.actors.length <= 0)
|
||||||
throw new ArgumentOutOfRangeException('at least one actor is required');
|
throw new ArgumentOutOfRangeException('at least one actor is required');
|
||||||
if (
|
|
||||||
props.actors.filter((actor: Actor) => actor.role == Role.DRIVER).length >
|
|
||||||
1
|
|
||||||
)
|
|
||||||
throw new ArgumentOutOfRangeException(
|
|
||||||
'a carpoolStep can contain only one driver',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { ResponseBase } from '@mobicoop/ddd-library';
|
import { ResponseBase } from '@mobicoop/ddd-library';
|
||||||
import { JourneyResponseDto } from './journey.response.dto';
|
import { JourneyResponseDto } from './journey.response.dto';
|
||||||
|
import { Frequency } from '@modules/ad/core/domain/ad.types';
|
||||||
|
|
||||||
export class MatchResponseDto extends ResponseBase {
|
export class MatchResponseDto extends ResponseBase {
|
||||||
adId: string;
|
adId: string;
|
||||||
role: string;
|
role: string;
|
||||||
frequency: string;
|
frequency: Frequency;
|
||||||
distance: number;
|
distance: number;
|
||||||
duration: number;
|
duration: number;
|
||||||
initialDistance: number;
|
initialDistance: number;
|
||||||
|
|||||||
@@ -81,6 +81,10 @@ export class MatchRequestDto {
|
|||||||
@ValidateNested({ each: true })
|
@ValidateNested({ each: true })
|
||||||
waypoints: WaypointDto[];
|
waypoints: WaypointDto[];
|
||||||
|
|
||||||
|
@IsUUID()
|
||||||
|
@IsOptional()
|
||||||
|
excludedAdId?: string;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsEnum(AlgorithmType)
|
@IsEnum(AlgorithmType)
|
||||||
algorithmType?: AlgorithmType;
|
algorithmType?: AlgorithmType;
|
||||||
|
|||||||
@@ -5,14 +5,7 @@ import { MatchQuery } from '@modules/ad/core/application/queries/match/match.que
|
|||||||
import { MatchingResult } from '@modules/ad/core/application/queries/match/match.query-handler';
|
import { MatchingResult } from '@modules/ad/core/application/queries/match/match.query-handler';
|
||||||
import { MatchEntity } from '@modules/ad/core/domain/match.entity';
|
import { MatchEntity } from '@modules/ad/core/domain/match.entity';
|
||||||
import { MatchMapper } from '@modules/ad/match.mapper';
|
import { MatchMapper } from '@modules/ad/match.mapper';
|
||||||
import { CacheInterceptor, CacheKey } from '@nestjs/cache-manager';
|
import { Controller, Inject, UseFilters, UsePipes } from '@nestjs/common';
|
||||||
import {
|
|
||||||
Controller,
|
|
||||||
Inject,
|
|
||||||
UseFilters,
|
|
||||||
UseInterceptors,
|
|
||||||
UsePipes,
|
|
||||||
} from '@nestjs/common';
|
|
||||||
import { QueryBus } from '@nestjs/cqrs';
|
import { QueryBus } from '@nestjs/cqrs';
|
||||||
import { GrpcMethod } from '@nestjs/microservices';
|
import { GrpcMethod } from '@nestjs/microservices';
|
||||||
import { LogCauseExceptionFilter } from '@src/log-cause.exception-filter';
|
import { LogCauseExceptionFilter } from '@src/log-cause.exception-filter';
|
||||||
@@ -35,8 +28,6 @@ export class MatchGrpcController {
|
|||||||
private readonly matchMapper: MatchMapper,
|
private readonly matchMapper: MatchMapper,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@CacheKey('MatcherServiceMatch')
|
|
||||||
@UseInterceptors(CacheInterceptor)
|
|
||||||
@GrpcMethod('MatcherService', 'Match')
|
@GrpcMethod('MatcherService', 'Match')
|
||||||
async match(data: MatchRequestDto): Promise<MatchingPaginatedResponseDto> {
|
async match(data: MatchRequestDto): Promise<MatchingPaginatedResponseDto> {
|
||||||
const matchingResult: MatchingResult = await this.queryBus.execute(
|
const matchingResult: MatchingResult = await this.queryBus.execute(
|
||||||
|
|||||||
@@ -16,16 +16,17 @@ message MatchRequest {
|
|||||||
repeated ScheduleItem schedule = 7;
|
repeated ScheduleItem schedule = 7;
|
||||||
bool strict = 8;
|
bool strict = 8;
|
||||||
repeated Waypoint waypoints = 9;
|
repeated Waypoint waypoints = 9;
|
||||||
AlgorithmType algorithmType = 10;
|
string excludedAdId = 10;
|
||||||
int32 remoteness = 11;
|
AlgorithmType algorithmType = 11;
|
||||||
bool useProportion = 12;
|
int32 remoteness = 12;
|
||||||
float proportion = 13;
|
bool useProportion = 13;
|
||||||
bool useAzimuth = 14;
|
float proportion = 14;
|
||||||
int32 azimuthMargin = 15;
|
bool useAzimuth = 15;
|
||||||
float maxDetourDistanceRatio = 16;
|
int32 azimuthMargin = 16;
|
||||||
float maxDetourDurationRatio = 17;
|
float maxDetourDistanceRatio = 17;
|
||||||
optional int32 page = 18;
|
float maxDetourDurationRatio = 18;
|
||||||
optional int32 perPage = 19;
|
optional int32 page = 19;
|
||||||
|
optional int32 perPage = 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ScheduleItem {
|
message ScheduleItem {
|
||||||
@@ -58,15 +59,16 @@ enum AlgorithmType {
|
|||||||
message Match {
|
message Match {
|
||||||
string adId = 1;
|
string adId = 1;
|
||||||
string role = 2;
|
string role = 2;
|
||||||
int32 distance = 3;
|
Frequency frequency = 3;
|
||||||
int32 duration = 4;
|
int32 distance = 4;
|
||||||
int32 initialDistance = 5;
|
int32 duration = 5;
|
||||||
int32 initialDuration = 6;
|
int32 initialDistance = 6;
|
||||||
int32 distanceDetour = 7;
|
int32 initialDuration = 7;
|
||||||
int32 durationDetour = 8;
|
int32 distanceDetour = 8;
|
||||||
double distanceDetourPercentage = 9;
|
int32 durationDetour = 9;
|
||||||
double durationDetourPercentage = 10;
|
double distanceDetourPercentage = 10;
|
||||||
repeated Journey journeys = 11;
|
double durationDetourPercentage = 11;
|
||||||
|
repeated Journey journeys = 12;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Journey {
|
message Journey {
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
import { Frequency } from '@modules/ad/core/domain/ad.types';
|
import { Frequency } from '@modules/ad/core/domain/ad.types';
|
||||||
|
|
||||||
export type Ad = {
|
export type AdReference = {
|
||||||
aggregateId: string;
|
aggregateId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Ad = AdReference & {
|
||||||
driver: boolean;
|
driver: boolean;
|
||||||
passenger: boolean;
|
passenger: boolean;
|
||||||
frequency: Frequency;
|
frequency: Frequency;
|
||||||
|
|||||||
@@ -1,46 +1,10 @@
|
|||||||
import { AdEntity } from '@modules/ad/core/domain/ad.entity';
|
import { AdEntity } from '@modules/ad/core/domain/ad.entity';
|
||||||
import { CreateAdProps, Frequency } from '@modules/ad/core/domain/ad.types';
|
import { createAdProps } from './ad.fixtures';
|
||||||
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: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('Ad entity create', () => {
|
describe('Ad entity create', () => {
|
||||||
|
describe('create', () => {
|
||||||
it('should create a new entity', async () => {
|
it('should create a new entity', async () => {
|
||||||
const ad: AdEntity = AdEntity.create(createAdProps);
|
const ad: AdEntity = AdEntity.create(createAdProps());
|
||||||
expect(ad.id.length).toBe(36);
|
expect(ad.id.length).toBe(36);
|
||||||
expect(ad.getProps().schedule.length).toBe(1);
|
expect(ad.getProps().schedule.length).toBe(1);
|
||||||
expect(ad.getProps().schedule[0].day).toBe(3);
|
expect(ad.getProps().schedule[0].day).toBe(3);
|
||||||
@@ -49,4 +13,5 @@ describe('Ad entity create', () => {
|
|||||||
expect(ad.getProps().passenger).toBeTruthy();
|
expect(ad.getProps().passenger).toBeTruthy();
|
||||||
expect(ad.getProps().driverDistance).toBe(23000);
|
expect(ad.getProps().driverDistance).toBe(23000);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
40
src/modules/ad/tests/unit/core/ad.fixtures.ts
Normal file
40
src/modules/ad/tests/unit/core/ad.fixtures.ts
Normal 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: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -37,7 +37,7 @@ const waypointsSet2: PointProps[] = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const schedule1: ScheduleItemProps[] = [
|
const mondayAt7: ScheduleItemProps[] = [
|
||||||
{
|
{
|
||||||
day: 1,
|
day: 1,
|
||||||
time: '07:00',
|
time: '07:00',
|
||||||
@@ -45,7 +45,7 @@ const schedule1: ScheduleItemProps[] = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const schedule2: ScheduleItemProps[] = [
|
const mondayAt710: ScheduleItemProps[] = [
|
||||||
{
|
{
|
||||||
day: 1,
|
day: 1,
|
||||||
time: '07:10',
|
time: '07:10',
|
||||||
@@ -53,7 +53,7 @@ const schedule2: ScheduleItemProps[] = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const schedule3: ScheduleItemProps[] = [
|
const weekdayMornings: ScheduleItemProps[] = [
|
||||||
{
|
{
|
||||||
day: 1,
|
day: 1,
|
||||||
time: '06:30',
|
time: '06:30',
|
||||||
@@ -81,7 +81,7 @@ const schedule3: ScheduleItemProps[] = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const schedule4: ScheduleItemProps[] = [
|
const schooldayMornings: ScheduleItemProps[] = [
|
||||||
{
|
{
|
||||||
day: 1,
|
day: 1,
|
||||||
time: '06:50',
|
time: '06:50',
|
||||||
@@ -104,7 +104,7 @@ const schedule4: ScheduleItemProps[] = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const schedule5: ScheduleItemProps[] = [
|
const saturdayNightAndMondayMorning: ScheduleItemProps[] = [
|
||||||
{
|
{
|
||||||
day: 0,
|
day: 0,
|
||||||
time: '00:02',
|
time: '00:02',
|
||||||
@@ -117,7 +117,7 @@ const schedule5: ScheduleItemProps[] = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const schedule6: ScheduleItemProps[] = [
|
const mondayAndSaturdayNights: ScheduleItemProps[] = [
|
||||||
{
|
{
|
||||||
day: 1,
|
day: 1,
|
||||||
time: '23:10',
|
time: '23:10',
|
||||||
@@ -130,7 +130,7 @@ const schedule6: ScheduleItemProps[] = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const schedule7: ScheduleItemProps[] = [
|
const thursdayEvening: ScheduleItemProps[] = [
|
||||||
{
|
{
|
||||||
day: 4,
|
day: 4,
|
||||||
time: '19:00',
|
time: '19:00',
|
||||||
@@ -266,8 +266,8 @@ describe('Candidate entity', () => {
|
|||||||
passengerWaypoints: waypointsSet2,
|
passengerWaypoints: waypointsSet2,
|
||||||
driverDistance: 350145,
|
driverDistance: 350145,
|
||||||
driverDuration: 13548,
|
driverDuration: 13548,
|
||||||
driverSchedule: schedule1,
|
driverSchedule: mondayAt7,
|
||||||
passengerSchedule: schedule2,
|
passengerSchedule: mondayAt710,
|
||||||
spacetimeDetourRatio,
|
spacetimeDetourRatio,
|
||||||
});
|
});
|
||||||
expect(candidateEntity.id.length).toBe(36);
|
expect(candidateEntity.id.length).toBe(36);
|
||||||
@@ -286,8 +286,8 @@ describe('Candidate entity', () => {
|
|||||||
passengerWaypoints: waypointsSet2,
|
passengerWaypoints: waypointsSet2,
|
||||||
driverDistance: 350145,
|
driverDistance: 350145,
|
||||||
driverDuration: 13548,
|
driverDuration: 13548,
|
||||||
driverSchedule: schedule1,
|
driverSchedule: mondayAt7,
|
||||||
passengerSchedule: schedule2,
|
passengerSchedule: mondayAt710,
|
||||||
spacetimeDetourRatio,
|
spacetimeDetourRatio,
|
||||||
}).setCarpoolPath(carpoolPath1);
|
}).setCarpoolPath(carpoolPath1);
|
||||||
expect(candidateEntity.getProps().carpoolPath).toHaveLength(2);
|
expect(candidateEntity.getProps().carpoolPath).toHaveLength(2);
|
||||||
@@ -306,8 +306,8 @@ describe('Candidate entity', () => {
|
|||||||
passengerWaypoints: waypointsSet2,
|
passengerWaypoints: waypointsSet2,
|
||||||
driverDistance: 350145,
|
driverDistance: 350145,
|
||||||
driverDuration: 13548,
|
driverDuration: 13548,
|
||||||
driverSchedule: schedule1,
|
driverSchedule: mondayAt7,
|
||||||
passengerSchedule: schedule2,
|
passengerSchedule: mondayAt710,
|
||||||
spacetimeDetourRatio,
|
spacetimeDetourRatio,
|
||||||
}).setMetrics(352688, 14587);
|
}).setMetrics(352688, 14587);
|
||||||
expect(candidateEntity.getProps().distance).toBe(352688);
|
expect(candidateEntity.getProps().distance).toBe(352688);
|
||||||
@@ -328,8 +328,8 @@ describe('Candidate entity', () => {
|
|||||||
passengerWaypoints: waypointsSet2,
|
passengerWaypoints: waypointsSet2,
|
||||||
driverDistance: 350145,
|
driverDistance: 350145,
|
||||||
driverDuration: 13548,
|
driverDuration: 13548,
|
||||||
driverSchedule: schedule1,
|
driverSchedule: mondayAt7,
|
||||||
passengerSchedule: schedule2,
|
passengerSchedule: mondayAt710,
|
||||||
spacetimeDetourRatio,
|
spacetimeDetourRatio,
|
||||||
}).setMetrics(458690, 13980);
|
}).setMetrics(458690, 13980);
|
||||||
expect(candidateEntity.isDetourValid()).toBeFalsy();
|
expect(candidateEntity.isDetourValid()).toBeFalsy();
|
||||||
@@ -347,8 +347,8 @@ describe('Candidate entity', () => {
|
|||||||
passengerWaypoints: waypointsSet2,
|
passengerWaypoints: waypointsSet2,
|
||||||
driverDistance: 350145,
|
driverDistance: 350145,
|
||||||
driverDuration: 13548,
|
driverDuration: 13548,
|
||||||
driverSchedule: schedule1,
|
driverSchedule: mondayAt7,
|
||||||
passengerSchedule: schedule2,
|
passengerSchedule: mondayAt710,
|
||||||
spacetimeDetourRatio,
|
spacetimeDetourRatio,
|
||||||
}).setMetrics(352368, 18314);
|
}).setMetrics(352368, 18314);
|
||||||
expect(candidateEntity.isDetourValid()).toBeFalsy();
|
expect(candidateEntity.isDetourValid()).toBeFalsy();
|
||||||
@@ -369,8 +369,8 @@ describe('Candidate entity', () => {
|
|||||||
passengerWaypoints: waypointsSet2,
|
passengerWaypoints: waypointsSet2,
|
||||||
driverDistance: 350145,
|
driverDistance: 350145,
|
||||||
driverDuration: 13548,
|
driverDuration: 13548,
|
||||||
driverSchedule: schedule1,
|
driverSchedule: mondayAt7,
|
||||||
passengerSchedule: schedule2,
|
passengerSchedule: mondayAt710,
|
||||||
spacetimeDetourRatio,
|
spacetimeDetourRatio,
|
||||||
})
|
})
|
||||||
.setCarpoolPath(carpoolPath2)
|
.setCarpoolPath(carpoolPath2)
|
||||||
@@ -392,7 +392,7 @@ describe('Candidate entity', () => {
|
|||||||
driverDistance: 350145,
|
driverDistance: 350145,
|
||||||
driverDuration: 13548,
|
driverDuration: 13548,
|
||||||
driverSchedule: undefined,
|
driverSchedule: undefined,
|
||||||
passengerSchedule: schedule2,
|
passengerSchedule: mondayAt710,
|
||||||
spacetimeDetourRatio,
|
spacetimeDetourRatio,
|
||||||
})
|
})
|
||||||
.setCarpoolPath(carpoolPath2)
|
.setCarpoolPath(carpoolPath2)
|
||||||
@@ -424,7 +424,7 @@ describe('Candidate entity', () => {
|
|||||||
passengerWaypoints: waypointsSet2,
|
passengerWaypoints: waypointsSet2,
|
||||||
driverDistance: 350145,
|
driverDistance: 350145,
|
||||||
driverDuration: 13548,
|
driverDuration: 13548,
|
||||||
driverSchedule: schedule1,
|
driverSchedule: mondayAt7,
|
||||||
passengerSchedule: undefined,
|
passengerSchedule: undefined,
|
||||||
spacetimeDetourRatio,
|
spacetimeDetourRatio,
|
||||||
})
|
})
|
||||||
@@ -480,8 +480,8 @@ describe('Candidate entity', () => {
|
|||||||
passengerWaypoints: waypointsSet2,
|
passengerWaypoints: waypointsSet2,
|
||||||
driverDistance: 350145,
|
driverDistance: 350145,
|
||||||
driverDuration: 13548,
|
driverDuration: 13548,
|
||||||
driverSchedule: schedule3,
|
driverSchedule: weekdayMornings,
|
||||||
passengerSchedule: schedule4,
|
passengerSchedule: schooldayMornings,
|
||||||
spacetimeDetourRatio,
|
spacetimeDetourRatio,
|
||||||
})
|
})
|
||||||
.setCarpoolPath(carpoolPath2)
|
.setCarpoolPath(carpoolPath2)
|
||||||
@@ -517,8 +517,8 @@ describe('Candidate entity', () => {
|
|||||||
passengerWaypoints: waypointsSet2,
|
passengerWaypoints: waypointsSet2,
|
||||||
driverDistance: 350145,
|
driverDistance: 350145,
|
||||||
driverDuration: 13548,
|
driverDuration: 13548,
|
||||||
driverSchedule: schedule5,
|
driverSchedule: saturdayNightAndMondayMorning,
|
||||||
passengerSchedule: schedule6,
|
passengerSchedule: mondayAndSaturdayNights,
|
||||||
spacetimeDetourRatio,
|
spacetimeDetourRatio,
|
||||||
})
|
})
|
||||||
.setCarpoolPath(carpoolPath2)
|
.setCarpoolPath(carpoolPath2)
|
||||||
@@ -550,6 +550,33 @@ describe('Candidate entity', () => {
|
|||||||
)[0].journeyItems[1].actorTimes[1].firstMinDatetime.getUTCMinutes(),
|
)[0].journeyItems[1].actorTimes[1].firstMinDatetime.getUTCMinutes(),
|
||||||
).toBe(42);
|
).toBe(42);
|
||||||
});
|
});
|
||||||
|
it('should create a journey for a punctual search from a recurrent driver schedule', () => {
|
||||||
|
const candidateEntity: CandidateEntity = CandidateEntity.create({
|
||||||
|
id: '5600ccfb-ab69-4d03-aa30-0fbe84fcedc0',
|
||||||
|
role: Role.PASSENGER,
|
||||||
|
frequency: Frequency.PUNCTUAL,
|
||||||
|
dateInterval: {
|
||||||
|
lowerDate: '2024-04-01', //This is a Monday
|
||||||
|
higherDate: '2024-04-01',
|
||||||
|
},
|
||||||
|
driverWaypoints: waypointsSet1,
|
||||||
|
passengerWaypoints: waypointsSet2,
|
||||||
|
driverDistance: 350145,
|
||||||
|
driverDuration: 13548,
|
||||||
|
driverSchedule: weekdayMornings,
|
||||||
|
passengerSchedule: mondayAt7,
|
||||||
|
spacetimeDetourRatio,
|
||||||
|
})
|
||||||
|
.setCarpoolPath(carpoolPath2)
|
||||||
|
.setSteps(steps)
|
||||||
|
.createJourneys();
|
||||||
|
expect(candidateEntity.getProps().journeys).toHaveLength(1);
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
candidateEntity.getProps().journeys as Journey[]
|
||||||
|
)[0].firstDate.getDate(),
|
||||||
|
).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
it('should not create journeys if dates does not match', () => {
|
it('should not create journeys if dates does not match', () => {
|
||||||
const candidateEntity: CandidateEntity = CandidateEntity.create({
|
const candidateEntity: CandidateEntity = CandidateEntity.create({
|
||||||
@@ -564,8 +591,8 @@ describe('Candidate entity', () => {
|
|||||||
passengerWaypoints: waypointsSet2,
|
passengerWaypoints: waypointsSet2,
|
||||||
driverDistance: 350145,
|
driverDistance: 350145,
|
||||||
driverDuration: 13548,
|
driverDuration: 13548,
|
||||||
driverSchedule: schedule1,
|
driverSchedule: mondayAt7,
|
||||||
passengerSchedule: schedule7,
|
passengerSchedule: thursdayEvening,
|
||||||
spacetimeDetourRatio,
|
spacetimeDetourRatio,
|
||||||
})
|
})
|
||||||
.setCarpoolPath(carpoolPath2)
|
.setCarpoolPath(carpoolPath2)
|
||||||
@@ -588,8 +615,8 @@ describe('Candidate entity', () => {
|
|||||||
passengerWaypoints: waypointsSet2,
|
passengerWaypoints: waypointsSet2,
|
||||||
driverDistance: 350145,
|
driverDistance: 350145,
|
||||||
driverDuration: 13548,
|
driverDuration: 13548,
|
||||||
driverSchedule: schedule1,
|
driverSchedule: mondayAt7,
|
||||||
passengerSchedule: schedule7,
|
passengerSchedule: thursdayEvening,
|
||||||
spacetimeDetourRatio,
|
spacetimeDetourRatio,
|
||||||
})
|
})
|
||||||
.setCarpoolPath(carpoolPath2)
|
.setCarpoolPath(carpoolPath2)
|
||||||
|
|||||||
@@ -33,22 +33,4 @@ describe('Carpool Path Item value object', () => {
|
|||||||
});
|
});
|
||||||
}).toThrow(ArgumentOutOfRangeException);
|
}).toThrow(ArgumentOutOfRangeException);
|
||||||
});
|
});
|
||||||
it('should throw an exception if actors contains more than one driver', () => {
|
|
||||||
expect(() => {
|
|
||||||
new CarpoolPathItem({
|
|
||||||
lat: 48.689445,
|
|
||||||
lon: 6.17651,
|
|
||||||
actors: [
|
|
||||||
new Actor({
|
|
||||||
role: Role.DRIVER,
|
|
||||||
target: Target.NEUTRAL,
|
|
||||||
}),
|
|
||||||
new Actor({
|
|
||||||
role: Role.DRIVER,
|
|
||||||
target: Target.START,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
}).toThrow(ArgumentOutOfRangeException);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
41
src/modules/ad/tests/unit/core/delete-ad.service.spec.ts
Normal file
41
src/modules/ad/tests/unit/core/delete-ad.service.spec.ts
Normal 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);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -47,6 +47,7 @@ const matchQuery = new MatchQuery(
|
|||||||
],
|
],
|
||||||
strict: false,
|
strict: false,
|
||||||
waypoints: [originWaypoint, destinationWaypoint],
|
waypoints: [originWaypoint, destinationWaypoint],
|
||||||
|
excludedAdId: '758618c6-dd82-4199-a548-0205161b04d7',
|
||||||
},
|
},
|
||||||
bareMockGeorouter,
|
bareMockGeorouter,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,17 +1,20 @@
|
|||||||
import { Module, Provider } from '@nestjs/common';
|
|
||||||
import { MESSAGE_PUBLISHER } from './messager.di-tokens';
|
|
||||||
import {
|
import {
|
||||||
MessageBrokerModule,
|
MessageBrokerModule,
|
||||||
MessageBrokerModuleOptions,
|
MessageBrokerModuleOptions,
|
||||||
MessageBrokerPublisher,
|
MessageBrokerPublisher,
|
||||||
} from '@mobicoop/message-broker-module';
|
} from '@mobicoop/message-broker-module';
|
||||||
|
import { Module, Provider } from '@nestjs/common';
|
||||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||||
import {
|
import {
|
||||||
AD_CREATED_MESSAGE_HANDLER,
|
AD_CREATED_MESSAGE_HANDLER,
|
||||||
AD_CREATED_QUEUE,
|
AD_CREATED_QUEUE,
|
||||||
AD_CREATED_ROUTING_KEY,
|
AD_CREATED_ROUTING_KEY,
|
||||||
|
AD_DELETED_MESSAGE_HANDLER,
|
||||||
|
AD_DELETED_QUEUE,
|
||||||
|
AD_DELETED_ROUTING_KEY,
|
||||||
SERVICE_NAME,
|
SERVICE_NAME,
|
||||||
} from '@src/app.constants';
|
} from '@src/app.constants';
|
||||||
|
import { MESSAGE_PUBLISHER } from './messager.di-tokens';
|
||||||
|
|
||||||
const imports = [
|
const imports = [
|
||||||
MessageBrokerModule.forRootAsync({
|
MessageBrokerModule.forRootAsync({
|
||||||
@@ -33,6 +36,10 @@ const imports = [
|
|||||||
routingKey: AD_CREATED_ROUTING_KEY,
|
routingKey: AD_CREATED_ROUTING_KEY,
|
||||||
queue: AD_CREATED_QUEUE,
|
queue: AD_CREATED_QUEUE,
|
||||||
},
|
},
|
||||||
|
[AD_DELETED_MESSAGE_HANDLER]: {
|
||||||
|
routingKey: AD_DELETED_ROUTING_KEY,
|
||||||
|
queue: AD_DELETED_QUEUE,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
|
|||||||
Reference in New Issue
Block a user