refactor to ddh, first commit
This commit is contained in:
parent
0a6e4c0bf6
commit
ce48890a66
30
.env.dist
30
.env.dist
|
@ -4,6 +4,21 @@ SERVICE_PORT=5005
|
||||||
SERVICE_CONFIGURATION_DOMAIN=MATCHER
|
SERVICE_CONFIGURATION_DOMAIN=MATCHER
|
||||||
HEALTH_SERVICE_PORT=6005
|
HEALTH_SERVICE_PORT=6005
|
||||||
|
|
||||||
|
# PRISMA
|
||||||
|
DATABASE_URL="postgresql://mobicoop:mobicoop@v3-db:5432/mobicoop?schema=matcher"
|
||||||
|
|
||||||
|
# MESSAGE BROKER
|
||||||
|
MESSAGE_BROKER_URI=amqp://v3-broker:5672
|
||||||
|
MESSAGE_BROKER_EXCHANGE=mobicoop
|
||||||
|
|
||||||
|
# REDIS
|
||||||
|
REDIS_HOST=v3-redis
|
||||||
|
REDIS_PASSWORD=redis
|
||||||
|
REDIS_PORT=6379
|
||||||
|
|
||||||
|
# CACHE
|
||||||
|
CACHE_TTL=5000
|
||||||
|
|
||||||
# DEFAULT CONFIGURATION
|
# DEFAULT CONFIGURATION
|
||||||
|
|
||||||
# default identifier used for match requests
|
# default identifier used for match requests
|
||||||
|
@ -41,18 +56,3 @@ MAX_DETOUR_DURATION_RATIO=0.3
|
||||||
GEOROUTER_TYPE=graphhopper
|
GEOROUTER_TYPE=graphhopper
|
||||||
# georouter url
|
# georouter url
|
||||||
GEOROUTER_URL=http://localhost:8989
|
GEOROUTER_URL=http://localhost:8989
|
||||||
|
|
||||||
# PRISMA
|
|
||||||
DATABASE_URL="postgresql://mobicoop:mobicoop@v3-db:5432/mobicoop?schema=matcher"
|
|
||||||
|
|
||||||
# RABBIT MQ
|
|
||||||
RMQ_URI=amqp://v3-broker:5672
|
|
||||||
RMQ_EXCHANGE=mobicoop
|
|
||||||
|
|
||||||
# REDIS
|
|
||||||
REDIS_HOST=v3-redis
|
|
||||||
REDIS_PASSWORD=redis
|
|
||||||
REDIS_PORT=6379
|
|
||||||
|
|
||||||
# CACHE
|
|
||||||
CACHE_TTL=5000
|
|
||||||
|
|
|
@ -44,8 +44,9 @@ GEOROUTER_URL=http://localhost:8989
|
||||||
# PRISMA
|
# PRISMA
|
||||||
DATABASE_URL="postgresql://mobicoop:mobicoop@v3-db:5432/mobicoop?schema=public"
|
DATABASE_URL="postgresql://mobicoop:mobicoop@v3-db:5432/mobicoop?schema=public"
|
||||||
|
|
||||||
# RABBIT MQ
|
# MESSAGE BROKER
|
||||||
RMQ_URI=amqp://v3-broker:5672
|
MESSAGE_BROKER_URI=amqp://v3-broker:5672
|
||||||
|
MESSAGE_BROKER_EXCHANGE=mobicoop
|
||||||
|
|
||||||
# REDIS
|
# REDIS
|
||||||
REDIS_IMAGE=redis:7.0-alpine
|
REDIS_IMAGE=redis:7.0-alpine
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
export const MESSAGE_BROKER_PUBLISHER = Symbol('MESSAGE_BROKER_PUBLISHER');
|
||||||
|
export const MESSAGE_PUBLISHER = Symbol('MESSAGE_PUBLISHER');
|
|
@ -0,0 +1,66 @@
|
||||||
|
import { classes } from '@automapper/classes';
|
||||||
|
import { AutomapperModule } from '@automapper/nestjs';
|
||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||||
|
import { HealthModule } from './modules/health/health.module';
|
||||||
|
import { MatcherModule } from './modules/matcher/matcher.module';
|
||||||
|
import { AdModule } from './modules/ad/ad.module';
|
||||||
|
import {
|
||||||
|
ConfigurationModule,
|
||||||
|
ConfigurationModuleOptions,
|
||||||
|
} from '@mobicoop/configuration-module';
|
||||||
|
import {
|
||||||
|
MessageBrokerModule,
|
||||||
|
MessageBrokerModuleOptions,
|
||||||
|
} from '@mobicoop/message-broker-module';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
ConfigModule.forRoot({ isGlobal: true }),
|
||||||
|
ConfigurationModule.forRootAsync({
|
||||||
|
imports: [ConfigModule],
|
||||||
|
inject: [ConfigService],
|
||||||
|
useFactory: async (
|
||||||
|
configService: ConfigService,
|
||||||
|
): Promise<ConfigurationModuleOptions> => ({
|
||||||
|
domain: configService.get<string>('SERVICE_CONFIGURATION_DOMAIN'),
|
||||||
|
messageBroker: {
|
||||||
|
uri: configService.get<string>('MESSAGE_BROKER_URI'),
|
||||||
|
exchange: configService.get<string>('MESSAGE_BROKER_EXCHANGE'),
|
||||||
|
},
|
||||||
|
redis: {
|
||||||
|
host: configService.get<string>('REDIS_HOST'),
|
||||||
|
password: configService.get<string>('REDIS_PASSWORD'),
|
||||||
|
port: configService.get<number>('REDIS_PORT'),
|
||||||
|
},
|
||||||
|
setConfigurationBrokerQueue: 'matcher-configuration-create-update',
|
||||||
|
deleteConfigurationQueue: 'matcher-configuration-delete',
|
||||||
|
propagateConfigurationQueue: 'matcher-configuration-propagate',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
MessageBrokerModule.forRootAsync({
|
||||||
|
imports: [ConfigModule],
|
||||||
|
inject: [ConfigService],
|
||||||
|
useFactory: async (
|
||||||
|
configService: ConfigService,
|
||||||
|
): Promise<MessageBrokerModuleOptions> => ({
|
||||||
|
uri: configService.get<string>('MESSAGE_BROKER_URI'),
|
||||||
|
exchange: configService.get<string>('MESSAGE_BROKER_EXCHANGE'),
|
||||||
|
handlers: {
|
||||||
|
adCreated: {
|
||||||
|
routingKey: 'ad.created',
|
||||||
|
queue: 'matcher-ad-created',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
name: 'matcher',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
AutomapperModule.forRoot({ strategyInitializer: classes() }),
|
||||||
|
HealthModule,
|
||||||
|
MatcherModule,
|
||||||
|
AdModule,
|
||||||
|
],
|
||||||
|
controllers: [],
|
||||||
|
providers: [],
|
||||||
|
})
|
||||||
|
export class AppModule {}
|
|
@ -0,0 +1,3 @@
|
||||||
|
export interface IPublishMessage {
|
||||||
|
publish(routingKey: string, message: string): void;
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
export const PARAMS_PROVIDER = Symbol();
|
||||||
|
export const GEOROUTER_CREATOR = Symbol();
|
||||||
|
export const TIMEZONE_FINDER = Symbol();
|
||||||
|
export const DIRECTION_ENCODER = Symbol();
|
|
@ -0,0 +1,61 @@
|
||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { AdMessagerService } from './adapters/primaries/ad-messager.service';
|
||||||
|
import { AdProfile } from './mappers/ad.profile';
|
||||||
|
import { CreateAdUseCase } from './domain/usecases/create-ad.usecase';
|
||||||
|
import { AdRepository } from './adapters/secondaries/ad.repository';
|
||||||
|
import { DatabaseModule } from '../database/database.module';
|
||||||
|
import { CqrsModule } from '@nestjs/cqrs';
|
||||||
|
import { GeoTimezoneFinder } from '../geography/adapters/secondaries/geo-timezone-finder';
|
||||||
|
import { DefaultParamsProvider } from './adapters/secondaries/default-params.provider';
|
||||||
|
import { GeorouterCreator } from '../geography/adapters/secondaries/georouter-creator';
|
||||||
|
import { GeographyModule } from '../geography/geography.module';
|
||||||
|
import { HttpModule } from '@nestjs/axios';
|
||||||
|
import { PostgresDirectionEncoder } from '../geography/adapters/secondaries/postgres-direction-encoder';
|
||||||
|
import { MessageBrokerPublisher } from '@mobicoop/message-broker-module';
|
||||||
|
import {
|
||||||
|
MESSAGE_BROKER_PUBLISHER,
|
||||||
|
MESSAGE_PUBLISHER,
|
||||||
|
} from '../../app.constants';
|
||||||
|
import { MessagePublisher } from './adapters/secondaries/message-publisher';
|
||||||
|
import {
|
||||||
|
DIRECTION_ENCODER,
|
||||||
|
GEOROUTER_CREATOR,
|
||||||
|
PARAMS_PROVIDER,
|
||||||
|
TIMEZONE_FINDER,
|
||||||
|
} from './ad.constants';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [GeographyModule, DatabaseModule, CqrsModule, HttpModule],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: PARAMS_PROVIDER,
|
||||||
|
useClass: DefaultParamsProvider,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: GEOROUTER_CREATOR,
|
||||||
|
useClass: GeorouterCreator,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: TIMEZONE_FINDER,
|
||||||
|
useClass: GeoTimezoneFinder,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: DIRECTION_ENCODER,
|
||||||
|
useClass: PostgresDirectionEncoder,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: MESSAGE_BROKER_PUBLISHER,
|
||||||
|
useClass: MessageBrokerPublisher,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: MESSAGE_PUBLISHER,
|
||||||
|
useClass: MessagePublisher,
|
||||||
|
},
|
||||||
|
AdProfile,
|
||||||
|
AdRepository,
|
||||||
|
CreateAdUseCase,
|
||||||
|
AdMessagerService,
|
||||||
|
],
|
||||||
|
exports: [],
|
||||||
|
})
|
||||||
|
export class AdModule {}
|
|
@ -1,20 +1,21 @@
|
||||||
import { RabbitSubscribe } from '@golevelup/nestjs-rabbitmq';
|
import { Controller, Inject } from '@nestjs/common';
|
||||||
import { Controller } from '@nestjs/common';
|
import { CommandBus } from '@nestjs/cqrs';
|
||||||
import { CommandBus, QueryBus } from '@nestjs/cqrs';
|
|
||||||
import { CreateAdCommand } from '../../commands/create-ad.command';
|
import { CreateAdCommand } from '../../commands/create-ad.command';
|
||||||
import { CreateAdRequest } from '../../domain/dtos/create-ad.request';
|
import { CreateAdRequest } from '../../domain/dtos/create-ad.request';
|
||||||
import { validateOrReject } from 'class-validator';
|
import { validateOrReject } from 'class-validator';
|
||||||
import { Messager } from '../secondaries/messager';
|
|
||||||
import { plainToInstance } from 'class-transformer';
|
import { plainToInstance } from 'class-transformer';
|
||||||
import { DatabaseException } from 'src/modules/database/exceptions/database.exception';
|
import { DatabaseException } from 'src/modules/database/exceptions/database.exception';
|
||||||
import { ExceptionCode } from 'src/modules/utils/exception-code.enum';
|
import { ExceptionCode } from 'src/modules/utils/exception-code.enum';
|
||||||
|
import { IPublishMessage } from 'src/interfaces/message-publisher';
|
||||||
|
import { MESSAGE_PUBLISHER } from 'src/app.constants';
|
||||||
|
import { RabbitSubscribe } from '@mobicoop/message-broker-module';
|
||||||
|
|
||||||
@Controller()
|
@Controller()
|
||||||
export class AdMessagerController {
|
export class AdMessagerService {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly messager: Messager,
|
@Inject(MESSAGE_PUBLISHER)
|
||||||
|
private readonly messagePublisher: IPublishMessage,
|
||||||
private readonly commandBus: CommandBus,
|
private readonly commandBus: CommandBus,
|
||||||
private readonly queryBus: QueryBus,
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@RabbitSubscribe({
|
@RabbitSubscribe({
|
||||||
|
@ -28,7 +29,7 @@ export class AdMessagerController {
|
||||||
// validate instance
|
// validate instance
|
||||||
await validateOrReject(createAdRequest);
|
await validateOrReject(createAdRequest);
|
||||||
// validate nested objects (fixes direct nested validation bug)
|
// validate nested objects (fixes direct nested validation bug)
|
||||||
for (const waypoint of createAdRequest.waypoints) {
|
for (const waypoint of createAdRequest.addresses) {
|
||||||
try {
|
try {
|
||||||
await validateOrReject(waypoint);
|
await validateOrReject(waypoint);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -36,7 +37,7 @@ export class AdMessagerController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.messager.publish(
|
this.messagePublisher.publish(
|
||||||
'matcher.ad.crit',
|
'matcher.ad.crit',
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
message: `Can't validate message : ${message}`,
|
message: `Can't validate message : ${message}`,
|
||||||
|
@ -49,7 +50,7 @@ export class AdMessagerController {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof DatabaseException) {
|
if (e instanceof DatabaseException) {
|
||||||
if (e.message.includes('already exists')) {
|
if (e.message.includes('already exists')) {
|
||||||
this.messager.publish(
|
this.messagePublisher.publish(
|
||||||
'matcher.ad.crit',
|
'matcher.ad.crit',
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
code: ExceptionCode.ALREADY_EXISTS,
|
code: ExceptionCode.ALREADY_EXISTS,
|
||||||
|
@ -59,7 +60,7 @@ export class AdMessagerController {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (e.message.includes("Can't reach database server")) {
|
if (e.message.includes("Can't reach database server")) {
|
||||||
this.messager.publish(
|
this.messagePublisher.publish(
|
||||||
'matcher.ad.crit',
|
'matcher.ad.crit',
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
code: ExceptionCode.UNAVAILABLE,
|
code: ExceptionCode.UNAVAILABLE,
|
||||||
|
@ -69,7 +70,7 @@ export class AdMessagerController {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.messager.publish(
|
this.messagePublisher.publish(
|
||||||
'logging.matcher.ad.crit',
|
'logging.matcher.ad.crit',
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
message,
|
message,
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { MESSAGE_BROKER_PUBLISHER } from '../../../../app.constants';
|
||||||
|
import { MessageBrokerPublisher } from '@mobicoop/message-broker-module';
|
||||||
|
import { IPublishMessage } from '../../../../interfaces/message-publisher';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MessagePublisher implements IPublishMessage {
|
||||||
|
constructor(
|
||||||
|
@Inject(MESSAGE_BROKER_PUBLISHER)
|
||||||
|
private readonly messageBrokerPublisher: MessageBrokerPublisher,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
publish = (routingKey: string, message: string): void => {
|
||||||
|
this.messageBrokerPublisher.publish(routingKey, message);
|
||||||
|
};
|
||||||
|
}
|
|
@ -119,7 +119,7 @@ export class CreateAdRequest {
|
||||||
@IsArray()
|
@IsArray()
|
||||||
@ArrayMinSize(2)
|
@ArrayMinSize(2)
|
||||||
@AutoMap(() => [Coordinate])
|
@AutoMap(() => [Coordinate])
|
||||||
waypoints: Coordinate[];
|
addresses: Coordinate[];
|
||||||
|
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@AutoMap()
|
@AutoMap()
|
|
@ -21,22 +21,22 @@ export class Geography {
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
const paths: Path[] = this.getPaths(roles);
|
const paths: Path[] = this.getPaths(roles);
|
||||||
const routes = await georouter.route(paths, settings);
|
const routes = await georouter.route(paths, settings);
|
||||||
if (routes.some((route) => route.key == RouteKey.COMMON)) {
|
if (routes.some((route) => route.key == RouteType.COMMON)) {
|
||||||
this.driverRoute = routes.find(
|
this.driverRoute = routes.find(
|
||||||
(route) => route.key == RouteKey.COMMON,
|
(route) => route.key == RouteType.COMMON,
|
||||||
).route;
|
).route;
|
||||||
this.passengerRoute = routes.find(
|
this.passengerRoute = routes.find(
|
||||||
(route) => route.key == RouteKey.COMMON,
|
(route) => route.key == RouteType.COMMON,
|
||||||
).route;
|
).route;
|
||||||
} else {
|
} else {
|
||||||
if (routes.some((route) => route.key == RouteKey.DRIVER)) {
|
if (routes.some((route) => route.key == RouteType.DRIVER)) {
|
||||||
this.driverRoute = routes.find(
|
this.driverRoute = routes.find(
|
||||||
(route) => route.key == RouteKey.DRIVER,
|
(route) => route.key == RouteType.DRIVER,
|
||||||
).route;
|
).route;
|
||||||
}
|
}
|
||||||
if (routes.some((route) => route.key == RouteKey.PASSENGER)) {
|
if (routes.some((route) => route.key == RouteType.PASSENGER)) {
|
||||||
this.passengerRoute = routes.find(
|
this.passengerRoute = routes.find(
|
||||||
(route) => route.key == RouteKey.PASSENGER,
|
(route) => route.key == RouteType.PASSENGER,
|
||||||
).route;
|
).route;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ export class Geography {
|
||||||
if (this.coordinates.length == 2) {
|
if (this.coordinates.length == 2) {
|
||||||
// 2 points => same route for driver and passenger
|
// 2 points => same route for driver and passenger
|
||||||
const commonPath: Path = {
|
const commonPath: Path = {
|
||||||
key: RouteKey.COMMON,
|
key: RouteType.COMMON,
|
||||||
points: this.coordinates,
|
points: this.coordinates,
|
||||||
};
|
};
|
||||||
paths.push(commonPath);
|
paths.push(commonPath);
|
||||||
|
@ -69,14 +69,14 @@ export class Geography {
|
||||||
|
|
||||||
private createDriverPath = (): Path => {
|
private createDriverPath = (): Path => {
|
||||||
return {
|
return {
|
||||||
key: RouteKey.DRIVER,
|
key: RouteType.DRIVER,
|
||||||
points: this.coordinates,
|
points: this.coordinates,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
private createPassengerPath = (): Path => {
|
private createPassengerPath = (): Path => {
|
||||||
return {
|
return {
|
||||||
key: RouteKey.PASSENGER,
|
key: RouteType.PASSENGER,
|
||||||
points: [
|
points: [
|
||||||
this.coordinates[0],
|
this.coordinates[0],
|
||||||
this.coordinates[this.coordinates.length - 1],
|
this.coordinates[this.coordinates.length - 1],
|
||||||
|
@ -85,7 +85,7 @@ export class Geography {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum RouteKey {
|
export enum RouteType {
|
||||||
COMMON = 'common',
|
COMMON = 'common',
|
||||||
DRIVER = 'driver',
|
DRIVER = 'driver',
|
||||||
PASSENGER = 'passenger',
|
PASSENGER = 'passenger',
|
|
@ -16,6 +16,12 @@ import { Geography } from '../entities/geography';
|
||||||
import { IEncodeDirection } from '../../../geography/domain/interfaces/direction-encoder.interface';
|
import { IEncodeDirection } from '../../../geography/domain/interfaces/direction-encoder.interface';
|
||||||
import { TimeConverter } from '../entities/time-converter';
|
import { TimeConverter } from '../entities/time-converter';
|
||||||
import { Coordinate } from '../../../geography/domain/entities/coordinate';
|
import { Coordinate } from '../../../geography/domain/entities/coordinate';
|
||||||
|
import {
|
||||||
|
DIRECTION_ENCODER,
|
||||||
|
GEOROUTER_CREATOR,
|
||||||
|
PARAMS_PROVIDER,
|
||||||
|
TIMEZONE_FINDER,
|
||||||
|
} from '../../ad.constants';
|
||||||
|
|
||||||
@CommandHandler(CreateAdCommand)
|
@CommandHandler(CreateAdCommand)
|
||||||
export class CreateAdUseCase {
|
export class CreateAdUseCase {
|
||||||
|
@ -29,13 +35,13 @@ export class CreateAdUseCase {
|
||||||
constructor(
|
constructor(
|
||||||
@InjectMapper() private readonly mapper: Mapper,
|
@InjectMapper() private readonly mapper: Mapper,
|
||||||
private readonly adRepository: AdRepository,
|
private readonly adRepository: AdRepository,
|
||||||
@Inject('ParamsProvider')
|
@Inject(PARAMS_PROVIDER)
|
||||||
private readonly defaultParamsProvider: IProvideParams,
|
private readonly defaultParamsProvider: IProvideParams,
|
||||||
@Inject('GeorouterCreator')
|
@Inject(GEOROUTER_CREATOR)
|
||||||
private readonly georouterCreator: ICreateGeorouter,
|
private readonly georouterCreator: ICreateGeorouter,
|
||||||
@Inject('TimezoneFinder')
|
@Inject(TIMEZONE_FINDER)
|
||||||
private readonly timezoneFinder: IFindTimezone,
|
private readonly timezoneFinder: IFindTimezone,
|
||||||
@Inject('DirectionEncoder')
|
@Inject(DIRECTION_ENCODER)
|
||||||
private readonly directionEncoder: IEncodeDirection,
|
private readonly directionEncoder: IEncodeDirection,
|
||||||
) {
|
) {
|
||||||
this.defaultParams = defaultParamsProvider.getParams();
|
this.defaultParams = defaultParamsProvider.getParams();
|
||||||
|
@ -48,8 +54,8 @@ export class CreateAdUseCase {
|
||||||
async execute(command: CreateAdCommand): Promise<Ad> {
|
async execute(command: CreateAdCommand): Promise<Ad> {
|
||||||
try {
|
try {
|
||||||
this.ad = this.mapper.map(command.createAdRequest, CreateAdRequest, Ad);
|
this.ad = this.mapper.map(command.createAdRequest, CreateAdRequest, Ad);
|
||||||
this.setTimezone(command.createAdRequest.waypoints);
|
this.setTimezone(command.createAdRequest.addresses);
|
||||||
this.setGeography(command.createAdRequest.waypoints);
|
this.setGeography(command.createAdRequest.addresses);
|
||||||
this.setRoles(command.createAdRequest);
|
this.setRoles(command.createAdRequest);
|
||||||
await this.geography.createRoutes(this.roles, this.georouter, {
|
await this.geography.createRoutes(this.roles, this.georouter, {
|
||||||
withDistance: false,
|
withDistance: false,
|
||||||
|
@ -97,7 +103,7 @@ export class CreateAdUseCase {
|
||||||
? this.geography.driverRoute.backAzimuth
|
? this.geography.driverRoute.backAzimuth
|
||||||
: this.geography.passengerRoute.backAzimuth;
|
: this.geography.passengerRoute.backAzimuth;
|
||||||
this.ad.waypoints = this.directionEncoder.encode(
|
this.ad.waypoints = this.directionEncoder.encode(
|
||||||
command.createAdRequest.waypoints,
|
command.createAdRequest.addresses,
|
||||||
);
|
);
|
||||||
this.ad.direction = this.geography.driverRoute
|
this.ad.direction = this.geography.driverRoute
|
||||||
? this.directionEncoder.encode(this.geography.driverRoute.points)
|
? this.directionEncoder.encode(this.geography.driverRoute.points)
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { MessagePublisher } from '../../../../adapters/secondaries/message-publisher';
|
||||||
|
import { MESSAGE_BROKER_PUBLISHER } from '../../../../../../app.constants';
|
||||||
|
|
||||||
|
const mockMessageBrokerPublisher = {
|
||||||
|
publish: jest.fn().mockImplementation(),
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Messager', () => {
|
||||||
|
let messagePublisher: MessagePublisher;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
imports: [],
|
||||||
|
providers: [
|
||||||
|
MessagePublisher,
|
||||||
|
{
|
||||||
|
provide: MESSAGE_BROKER_PUBLISHER,
|
||||||
|
useValue: mockMessageBrokerPublisher,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
messagePublisher = module.get<MessagePublisher>(MessagePublisher);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(messagePublisher).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should publish a message', async () => {
|
||||||
|
jest.spyOn(mockMessageBrokerPublisher, 'publish');
|
||||||
|
messagePublisher.publish('ad.info', 'my-test');
|
||||||
|
expect(mockMessageBrokerPublisher.publish).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
|
@ -8,9 +8,15 @@ import { CreateAdCommand } from '../../../commands/create-ad.command';
|
||||||
import { Ad } from '../../../domain/entities/ad';
|
import { Ad } from '../../../domain/entities/ad';
|
||||||
import { AdProfile } from '../../../mappers/ad.profile';
|
import { AdProfile } from '../../../mappers/ad.profile';
|
||||||
import { Frequency } from '../../../domain/types/frequency.enum';
|
import { Frequency } from '../../../domain/types/frequency.enum';
|
||||||
import { RouteKey } from '../../../domain/entities/geography';
|
import { RouteType } from '../../../domain/entities/geography';
|
||||||
import { DatabaseException } from '../../../../database/exceptions/database.exception';
|
import { DatabaseException } from '../../../../database/exceptions/database.exception';
|
||||||
import { Route } from '../../../../geography/domain/entities/route';
|
import { Route } from '../../../../geography/domain/entities/route';
|
||||||
|
import {
|
||||||
|
DIRECTION_ENCODER,
|
||||||
|
GEOROUTER_CREATOR,
|
||||||
|
PARAMS_PROVIDER,
|
||||||
|
TIMEZONE_FINDER,
|
||||||
|
} from '../../../ad.constants';
|
||||||
|
|
||||||
const mockAdRepository = {
|
const mockAdRepository = {
|
||||||
createAd: jest.fn().mockImplementation((ad) => {
|
createAd: jest.fn().mockImplementation((ad) => {
|
||||||
|
@ -23,7 +29,7 @@ const mockGeorouterCreator = {
|
||||||
create: jest.fn().mockImplementation(() => ({
|
create: jest.fn().mockImplementation(() => ({
|
||||||
route: jest.fn().mockImplementation(() => [
|
route: jest.fn().mockImplementation(() => [
|
||||||
{
|
{
|
||||||
key: RouteKey.DRIVER,
|
key: RouteType.DRIVER,
|
||||||
route: <Route>{
|
route: <Route>{
|
||||||
points: [],
|
points: [],
|
||||||
fwdAzimuth: 0,
|
fwdAzimuth: 0,
|
||||||
|
@ -33,7 +39,7 @@ const mockGeorouterCreator = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: RouteKey.PASSENGER,
|
key: RouteType.PASSENGER,
|
||||||
route: <Route>{
|
route: <Route>{
|
||||||
points: [],
|
points: [],
|
||||||
fwdAzimuth: 0,
|
fwdAzimuth: 0,
|
||||||
|
@ -43,7 +49,7 @@ const mockGeorouterCreator = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: RouteKey.COMMON,
|
key: RouteType.COMMON,
|
||||||
route: <Route>{
|
route: <Route>{
|
||||||
points: [],
|
points: [],
|
||||||
fwdAzimuth: 0,
|
fwdAzimuth: 0,
|
||||||
|
@ -94,7 +100,7 @@ const createAdRequest: CreateAdRequest = {
|
||||||
seatsDriver: 3,
|
seatsDriver: 3,
|
||||||
seatsPassenger: 1,
|
seatsPassenger: 1,
|
||||||
strict: false,
|
strict: false,
|
||||||
waypoints: [
|
addresses: [
|
||||||
{ lon: 6, lat: 45 },
|
{ lon: 6, lat: 45 },
|
||||||
{ lon: 6.5, lat: 45.5 },
|
{ lon: 6.5, lat: 45.5 },
|
||||||
],
|
],
|
||||||
|
@ -124,19 +130,19 @@ describe('CreateAdUseCase', () => {
|
||||||
useValue: mockAdRepository,
|
useValue: mockAdRepository,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: 'GeorouterCreator',
|
provide: GEOROUTER_CREATOR,
|
||||||
useValue: mockGeorouterCreator,
|
useValue: mockGeorouterCreator,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: 'ParamsProvider',
|
provide: PARAMS_PROVIDER,
|
||||||
useValue: mockParamsProvider,
|
useValue: mockParamsProvider,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: 'TimezoneFinder',
|
provide: TIMEZONE_FINDER,
|
||||||
useValue: mockTimezoneFinder,
|
useValue: mockTimezoneFinder,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: 'DirectionEncoder',
|
provide: DIRECTION_ENCODER,
|
||||||
useValue: mockDirectionEncoder,
|
useValue: mockDirectionEncoder,
|
||||||
},
|
},
|
||||||
AdProfile,
|
AdProfile,
|
|
@ -5,7 +5,7 @@ import { GraphhopperGeorouter } from './graphhopper-georouter';
|
||||||
import { HttpService } from '@nestjs/axios';
|
import { HttpService } from '@nestjs/axios';
|
||||||
import { Geodesic } from './geodesic';
|
import { Geodesic } from './geodesic';
|
||||||
import { GeographyException } from '../../exceptions/geography.exception';
|
import { GeographyException } from '../../exceptions/geography.exception';
|
||||||
import { ExceptionCode } from '../../..//utils/exception-code.enum';
|
import { ExceptionCode } from '../../../utils/exception-code.enum';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class GeorouterCreator implements ICreateGeorouter {
|
export class GeorouterCreator implements ICreateGeorouter {
|
|
@ -3,12 +3,12 @@ import { IGeorouter } from '../../domain/interfaces/georouter.interface';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { catchError, lastValueFrom, map } from 'rxjs';
|
import { catchError, lastValueFrom, map } from 'rxjs';
|
||||||
import { AxiosError, AxiosResponse } from 'axios';
|
import { AxiosError, AxiosResponse } from 'axios';
|
||||||
import { IGeodesic } from '../../../geography/domain/interfaces/geodesic.interface';
|
import { IGeodesic } from '../../domain/interfaces/geodesic.interface';
|
||||||
import { GeorouterSettings } from '../../domain/types/georouter-settings.type';
|
import { GeorouterSettings } from '../../domain/types/georouter-settings.type';
|
||||||
import { Path } from '../../domain/types/path.type';
|
import { Path } from '../../domain/types/path.type';
|
||||||
import { NamedRoute } from '../../domain/types/named-route';
|
import { NamedRoute } from '../../domain/types/named-route';
|
||||||
import { GeographyException } from '../../exceptions/geography.exception';
|
import { GeographyException } from '../../exceptions/geography.exception';
|
||||||
import { ExceptionCode } from '../../..//utils/exception-code.enum';
|
import { ExceptionCode } from '../../../utils/exception-code.enum';
|
||||||
import { Route } from '../../domain/entities/route';
|
import { Route } from '../../domain/entities/route';
|
||||||
import { SpacetimePoint } from '../../domain/entities/spacetime-point';
|
import { SpacetimePoint } from '../../domain/entities/spacetime-point';
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { Point } from './point.type';
|
||||||
|
|
||||||
|
export type Path = {
|
||||||
|
key: string;
|
||||||
|
points: Point[];
|
||||||
|
};
|
|
@ -1,6 +1,6 @@
|
||||||
import { Controller } from '@nestjs/common';
|
import { Controller } from '@nestjs/common';
|
||||||
import { GrpcMethod } from '@nestjs/microservices';
|
import { GrpcMethod } from '@nestjs/microservices';
|
||||||
import { PrismaHealthIndicatorUseCase } from '../../domain/usecases/prisma.health-indicator.usecase';
|
import { RepositoriesHealthIndicatorUseCase } from '../../domain/usecases/repositories.health-indicator.usecase';
|
||||||
|
|
||||||
enum ServingStatus {
|
enum ServingStatus {
|
||||||
UNKNOWN = 0,
|
UNKNOWN = 0,
|
||||||
|
@ -19,7 +19,7 @@ interface HealthCheckResponse {
|
||||||
@Controller()
|
@Controller()
|
||||||
export class HealthServerController {
|
export class HealthServerController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly prismaHealthIndicatorUseCase: PrismaHealthIndicatorUseCase,
|
private readonly repositoriesHealthIndicatorUseCase: RepositoriesHealthIndicatorUseCase,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@GrpcMethod('Health', 'Check')
|
@GrpcMethod('Health', 'Check')
|
||||||
|
@ -29,12 +29,12 @@ export class HealthServerController {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
metadata: any,
|
metadata: any,
|
||||||
): Promise<HealthCheckResponse> {
|
): Promise<HealthCheckResponse> {
|
||||||
const healthCheck = await this.prismaHealthIndicatorUseCase.isHealthy(
|
const healthCheck = await this.repositoriesHealthIndicatorUseCase.isHealthy(
|
||||||
'prisma',
|
'repositories',
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
status:
|
status:
|
||||||
healthCheck['prisma'].status == 'up'
|
healthCheck['repositories'].status == 'up'
|
||||||
? ServingStatus.SERVING
|
? ServingStatus.SERVING
|
||||||
: ServingStatus.NOT_SERVING,
|
: ServingStatus.NOT_SERVING,
|
||||||
};
|
};
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { Controller, Get, Inject } from '@nestjs/common';
|
||||||
|
import {
|
||||||
|
HealthCheckService,
|
||||||
|
HealthCheck,
|
||||||
|
HealthCheckResult,
|
||||||
|
} from '@nestjs/terminus';
|
||||||
|
import { MESSAGE_PUBLISHER } from 'src/app.constants';
|
||||||
|
import { IPublishMessage } from 'src/interfaces/message-publisher';
|
||||||
|
import { RepositoriesHealthIndicatorUseCase } from '../../domain/usecases/repositories.health-indicator.usecase';
|
||||||
|
|
||||||
|
@Controller('health')
|
||||||
|
export class HealthController {
|
||||||
|
constructor(
|
||||||
|
private readonly repositoriesHealthIndicatorUseCase: RepositoriesHealthIndicatorUseCase,
|
||||||
|
private healthCheckService: HealthCheckService,
|
||||||
|
@Inject(MESSAGE_PUBLISHER)
|
||||||
|
private readonly messagePublisher: IPublishMessage,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
@HealthCheck()
|
||||||
|
async check() {
|
||||||
|
try {
|
||||||
|
return await this.healthCheckService.check([
|
||||||
|
async () =>
|
||||||
|
this.repositoriesHealthIndicatorUseCase.isHealthy('repositories'),
|
||||||
|
]);
|
||||||
|
} catch (error) {
|
||||||
|
const healthCheckResult: HealthCheckResult = error.response;
|
||||||
|
this.messagePublisher.publish(
|
||||||
|
'logging.user.health.crit',
|
||||||
|
JSON.stringify(healthCheckResult.error),
|
||||||
|
);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { MESSAGE_BROKER_PUBLISHER } from '../../../../app.constants';
|
||||||
|
import { MessageBrokerPublisher } from '@mobicoop/message-broker-module';
|
||||||
|
import { IPublishMessage } from 'src/interfaces/message-publisher';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MessagePublisher implements IPublishMessage {
|
||||||
|
constructor(
|
||||||
|
@Inject(MESSAGE_BROKER_PUBLISHER)
|
||||||
|
private readonly messageBrokerPublisher: MessageBrokerPublisher,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
publish = (routingKey: string, message: string): void => {
|
||||||
|
this.messageBrokerPublisher.publish(routingKey, message);
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
export interface ICheckRepository {
|
||||||
|
healthCheck(): Promise<boolean>;
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import {
|
||||||
|
HealthCheckError,
|
||||||
|
HealthIndicator,
|
||||||
|
HealthIndicatorResult,
|
||||||
|
} from '@nestjs/terminus';
|
||||||
|
import { ICheckRepository } from '../interfaces/check-repository.interface';
|
||||||
|
import { AdRepository } from '../../../ad/adapters/secondaries/ad.repository';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class RepositoriesHealthIndicatorUseCase extends HealthIndicator {
|
||||||
|
private checkRepositories: ICheckRepository[];
|
||||||
|
constructor(private readonly adRepository: AdRepository) {
|
||||||
|
super();
|
||||||
|
this.checkRepositories = [adRepository];
|
||||||
|
}
|
||||||
|
isHealthy = async (key: string): Promise<HealthIndicatorResult> => {
|
||||||
|
try {
|
||||||
|
await Promise.all(
|
||||||
|
this.checkRepositories.map(
|
||||||
|
async (checkRepository: ICheckRepository) => {
|
||||||
|
await checkRepository.healthCheck();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return this.getStatus(key, true);
|
||||||
|
} catch (e: any) {
|
||||||
|
throw new HealthCheckError('Repository', {
|
||||||
|
repository: e.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { HealthServerController } from './adapters/primaries/health-server.controller';
|
||||||
|
import { DatabaseModule } from '../database/database.module';
|
||||||
|
import { HealthController } from './adapters/primaries/health.controller';
|
||||||
|
import { TerminusModule } from '@nestjs/terminus';
|
||||||
|
import { MESSAGE_BROKER_PUBLISHER, MESSAGE_PUBLISHER } from 'src/app.constants';
|
||||||
|
import { MessageBrokerPublisher } from '@mobicoop/message-broker-module';
|
||||||
|
import { MessagePublisher } from './adapters/secondaries/message-publisher';
|
||||||
|
import { RepositoriesHealthIndicatorUseCase } from './domain/usecases/repositories.health-indicator.usecase';
|
||||||
|
import { AdRepository } from '../ad/adapters/secondaries/ad.repository';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [TerminusModule, DatabaseModule],
|
||||||
|
controllers: [HealthServerController, HealthController],
|
||||||
|
providers: [
|
||||||
|
RepositoriesHealthIndicatorUseCase,
|
||||||
|
AdRepository,
|
||||||
|
{
|
||||||
|
provide: MESSAGE_BROKER_PUBLISHER,
|
||||||
|
useClass: MessageBrokerPublisher,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: MESSAGE_PUBLISHER,
|
||||||
|
useClass: MessagePublisher,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class HealthModule {}
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { MessagePublisher } from '../../adapters/secondaries/message-publisher';
|
||||||
|
import { MESSAGE_BROKER_PUBLISHER } from '../../../../app.constants';
|
||||||
|
|
||||||
|
const mockMessageBrokerPublisher = {
|
||||||
|
publish: jest.fn().mockImplementation(),
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Messager', () => {
|
||||||
|
let messagePublisher: MessagePublisher;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
imports: [],
|
||||||
|
providers: [
|
||||||
|
MessagePublisher,
|
||||||
|
{
|
||||||
|
provide: MESSAGE_BROKER_PUBLISHER,
|
||||||
|
useValue: mockMessageBrokerPublisher,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
messagePublisher = module.get<MessagePublisher>(MessagePublisher);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(messagePublisher).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should publish a message', async () => {
|
||||||
|
jest.spyOn(mockMessageBrokerPublisher, 'publish');
|
||||||
|
messagePublisher.publish('health.info', 'my-test');
|
||||||
|
expect(mockMessageBrokerPublisher.publish).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,8 +1,7 @@
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
import { PrismaHealthIndicatorUseCase } from '../../domain/usecases/prisma.health-indicator.usecase';
|
|
||||||
import { HealthCheckError, HealthIndicatorResult } from '@nestjs/terminus';
|
import { HealthCheckError, HealthIndicatorResult } from '@nestjs/terminus';
|
||||||
|
import { RepositoriesHealthIndicatorUseCase } from '../../domain/usecases/repositories.health-indicator.usecase';
|
||||||
import { AdRepository } from '../../../ad/adapters/secondaries/ad.repository';
|
import { AdRepository } from '../../../ad/adapters/secondaries/ad.repository';
|
||||||
import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library';
|
|
||||||
|
|
||||||
const mockAdRepository = {
|
const mockAdRepository = {
|
||||||
healthCheck: jest
|
healthCheck: jest
|
||||||
|
@ -11,47 +10,45 @@ const mockAdRepository = {
|
||||||
return Promise.resolve(true);
|
return Promise.resolve(true);
|
||||||
})
|
})
|
||||||
.mockImplementation(() => {
|
.mockImplementation(() => {
|
||||||
throw new PrismaClientKnownRequestError('Service unavailable', {
|
throw new Error('an error occured in the repository');
|
||||||
code: 'code',
|
|
||||||
clientVersion: 'version',
|
|
||||||
});
|
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('PrismaHealthIndicatorUseCase', () => {
|
describe('RepositoriesHealthIndicatorUseCase', () => {
|
||||||
let prismaHealthIndicatorUseCase: PrismaHealthIndicatorUseCase;
|
let repositoriesHealthIndicatorUseCase: RepositoriesHealthIndicatorUseCase;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
providers: [
|
providers: [
|
||||||
|
RepositoriesHealthIndicatorUseCase,
|
||||||
{
|
{
|
||||||
provide: AdRepository,
|
provide: AdRepository,
|
||||||
useValue: mockAdRepository,
|
useValue: mockAdRepository,
|
||||||
},
|
},
|
||||||
PrismaHealthIndicatorUseCase,
|
|
||||||
],
|
],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
prismaHealthIndicatorUseCase = module.get<PrismaHealthIndicatorUseCase>(
|
repositoriesHealthIndicatorUseCase =
|
||||||
PrismaHealthIndicatorUseCase,
|
module.get<RepositoriesHealthIndicatorUseCase>(
|
||||||
|
RepositoriesHealthIndicatorUseCase,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be defined', () => {
|
it('should be defined', () => {
|
||||||
expect(prismaHealthIndicatorUseCase).toBeDefined();
|
expect(repositoriesHealthIndicatorUseCase).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('execute', () => {
|
describe('execute', () => {
|
||||||
it('should check health successfully', async () => {
|
it('should check health successfully', async () => {
|
||||||
const healthIndicatorResult: HealthIndicatorResult =
|
const healthIndicatorResult: HealthIndicatorResult =
|
||||||
await prismaHealthIndicatorUseCase.isHealthy('prisma');
|
await repositoriesHealthIndicatorUseCase.isHealthy('repositories');
|
||||||
|
|
||||||
expect(healthIndicatorResult['prisma'].status).toBe('up');
|
expect(healthIndicatorResult['repositories'].status).toBe('up');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an error if database is unavailable', async () => {
|
it('should throw an error if database is unavailable', async () => {
|
||||||
await expect(
|
await expect(
|
||||||
prismaHealthIndicatorUseCase.isHealthy('prisma'),
|
repositoriesHealthIndicatorUseCase.isHealthy('repositories'),
|
||||||
).rejects.toBeInstanceOf(HealthCheckError);
|
).rejects.toBeInstanceOf(HealthCheckError);
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { MESSAGE_BROKER_PUBLISHER } from '../../../../app.constants';
|
||||||
|
import { MessageBrokerPublisher } from '@mobicoop/message-broker-module';
|
||||||
|
import { IPublishMessage } from '../../../../interfaces/message-publisher';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MessagePublisher implements IPublishMessage {
|
||||||
|
constructor(
|
||||||
|
@Inject(MESSAGE_BROKER_PUBLISHER)
|
||||||
|
private readonly messageBrokerPublisher: MessageBrokerPublisher,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
publish = (routingKey: string, message: string): void => {
|
||||||
|
this.messageBrokerPublisher.publish(routingKey, message);
|
||||||
|
};
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue