use health package, upgrade ddd library, improve integration tests
This commit is contained in:
parent
e8c523b689
commit
580ece8e7c
|
@ -13,7 +13,8 @@
|
|||
"@grpc/proto-loader": "^0.7.6",
|
||||
"@liaoliaots/nestjs-redis": "^9.0.5",
|
||||
"@mobicoop/configuration-module": "^1.2.0",
|
||||
"@mobicoop/ddd-library": "^0.0.1",
|
||||
"@mobicoop/ddd-library": "^0.3.0",
|
||||
"@mobicoop/health-module": "^1.1.0",
|
||||
"@mobicoop/message-broker-module": "^1.2.0",
|
||||
"@nestjs/common": "^9.0.0",
|
||||
"@nestjs/config": "^2.3.1",
|
||||
|
@ -1414,9 +1415,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@mobicoop/ddd-library": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@mobicoop/ddd-library/-/ddd-library-0.0.1.tgz",
|
||||
"integrity": "sha512-T6g3pgodrMOZ1yrtNWylgu6EjRz3HPgcD9UoK0cJCvfiq9WjTH9TOZ6wKh9vIijiO+a5KJkZiKHbbjzuJvdwCg==",
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@mobicoop/ddd-library/-/ddd-library-0.3.0.tgz",
|
||||
"integrity": "sha512-MoUDqlrDmJkumCFSyW9FY2DLbguT4rytFrmBt9tVNCr2Es6nlz4Ml3HVBwJTZrlJFU79XmiUQ5WAO0MHJt+nAg==",
|
||||
"dependencies": {
|
||||
"@nestjs/event-emitter": "^1.4.2",
|
||||
"@nestjs/microservices": "^9.4.0",
|
||||
|
@ -1428,6 +1429,22 @@
|
|||
"@nestjs/common": "^9.4.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@mobicoop/health-module": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@mobicoop/health-module/-/health-module-1.1.0.tgz",
|
||||
"integrity": "sha512-tSdvpwHxMOG7U3txm3sQDUkj1cWeGg9K68u8Y2BKgD8ocMRDufiCXY43ScFXKZqWm8jkcOU6XdwJm3pxvUOq4w==",
|
||||
"dependencies": {
|
||||
"@grpc/grpc-js": "^1.8.14",
|
||||
"@grpc/proto-loader": "^0.7.7",
|
||||
"@mobicoop/ddd-library": "^0.3.0",
|
||||
"@mobicoop/message-broker-module": "^1.0.5",
|
||||
"@nestjs/microservices": "^9.4.2",
|
||||
"@nestjs/terminus": "^9.2.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@nestjs/common": "^9.4.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@mobicoop/message-broker-module": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@mobicoop/message-broker-module/-/message-broker-module-1.2.0.tgz",
|
||||
|
|
21
package.json
21
package.json
|
@ -21,9 +21,8 @@
|
|||
"test:unit": "jest --testPathPattern 'tests/unit/' --verbose",
|
||||
"test:unit:watch": "jest --testPathPattern 'tests/unit/' --verbose --watch",
|
||||
"test:unit:ci": "jest --testPathPattern 'tests/unit/' --coverage",
|
||||
"test:integration": "npm run migrate:test && dotenv -e .env.test -- jest --testPathPattern 'tests/integration/' --verbose",
|
||||
"test:integration:watch": "npm run migrate:test && dotenv -e .env.test -- jest --testPathPattern 'tests/integration/' --verbose --watch",
|
||||
"test:integration:ci": "npm run migrate:test:ci && dotenv -e ci/.env.ci -- jest --testPathPattern 'tests/integration/'",
|
||||
"test:integration": "npm run migrate:test && dotenv -e .env.test -- jest --testPathPattern 'tests/integration/' --verbose --runInBand",
|
||||
"test:integration:ci": "npm run migrate:test:ci && dotenv -e ci/.env.ci -- jest --testPathPattern 'tests/integration/' --runInBand",
|
||||
"test:cov": "jest --testPathPattern 'tests/unit/' --coverage",
|
||||
"test:e2e": "jest --config ./test/jest-e2e.json",
|
||||
"generate": "docker exec v3-ad-api sh -c 'npx prisma generate'",
|
||||
|
@ -38,7 +37,8 @@
|
|||
"@grpc/proto-loader": "^0.7.6",
|
||||
"@liaoliaots/nestjs-redis": "^9.0.5",
|
||||
"@mobicoop/configuration-module": "^1.2.0",
|
||||
"@mobicoop/ddd-library": "^0.0.1",
|
||||
"@mobicoop/ddd-library": "^0.3.0",
|
||||
"@mobicoop/health-module": "^1.1.0",
|
||||
"@mobicoop/message-broker-module": "^1.2.0",
|
||||
"@nestjs/common": "^9.0.0",
|
||||
"@nestjs/config": "^2.3.1",
|
||||
|
@ -93,14 +93,10 @@
|
|||
"modulePathIgnorePatterns": [
|
||||
".module.ts",
|
||||
".dto.ts",
|
||||
".constants.ts",
|
||||
".di-tokens.ts",
|
||||
".response.ts",
|
||||
".response.base.ts",
|
||||
".port.ts",
|
||||
"libs/exceptions",
|
||||
"libs/types",
|
||||
"prisma.service.ts",
|
||||
"convert-props-to-object.util.ts",
|
||||
"main.ts"
|
||||
],
|
||||
"rootDir": "src",
|
||||
|
@ -114,19 +110,14 @@
|
|||
"coveragePathIgnorePatterns": [
|
||||
".module.ts",
|
||||
".dto.ts",
|
||||
".constants.ts",
|
||||
".di-tokens.ts",
|
||||
".response.ts",
|
||||
".response.base.ts",
|
||||
".port.ts",
|
||||
"libs/exceptions",
|
||||
"libs/types",
|
||||
"prisma.service.ts",
|
||||
"convert-props-to-object.util.ts",
|
||||
"main.ts"
|
||||
],
|
||||
"coverageDirectory": "../coverage",
|
||||
"moduleNameMapper": {
|
||||
"^@libs(.*)": "<rootDir>/libs/$1",
|
||||
"^@modules(.*)": "<rootDir>/modules/$1",
|
||||
"^@src(.*)": "<rootDir>$1"
|
||||
},
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export const MESSAGE_PUBLISHER = Symbol();
|
|
@ -7,28 +7,21 @@ import {
|
|||
} from '@mobicoop/configuration-module';
|
||||
import { EventEmitterModule } from '@nestjs/event-emitter';
|
||||
import { RequestContextModule } from 'nestjs-request-context';
|
||||
import { HealthModule } from '@modules/health/health.module';
|
||||
import { MessagerModule } from '@modules/messager/messager.module';
|
||||
import { HealthModule } from '@mobicoop/health-module';
|
||||
import { AD_REPOSITORY } from '@modules/ad/ad.di-tokens';
|
||||
import { MESSAGE_PUBLISHER } from '@modules/messager/messager.di-tokens';
|
||||
import {
|
||||
MessageBrokerModule,
|
||||
MessageBrokerModuleOptions,
|
||||
} from '@mobicoop/message-broker-module';
|
||||
HealthModuleOptions,
|
||||
ICheckRepository,
|
||||
} from '@mobicoop/health-module/dist/core/domain/types/health.types';
|
||||
import { MessagePublisherPort } from '@mobicoop/ddd-library';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot({ isGlobal: true }),
|
||||
EventEmitterModule.forRoot(),
|
||||
RequestContextModule,
|
||||
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'),
|
||||
name: 'ad',
|
||||
}),
|
||||
}),
|
||||
ConfigurationModule.forRootAsync({
|
||||
imports: [ConfigModule],
|
||||
inject: [ConfigService],
|
||||
|
@ -50,8 +43,22 @@ import {
|
|||
propagateConfigurationQueue: 'ad-configuration-propagate',
|
||||
}),
|
||||
}),
|
||||
HealthModule,
|
||||
HealthModule.forRootAsync({
|
||||
imports: [AdModule, MessagerModule],
|
||||
inject: [AD_REPOSITORY, MESSAGE_PUBLISHER],
|
||||
useFactory: async (
|
||||
adRepository: ICheckRepository,
|
||||
messagePublisher: MessagePublisherPort,
|
||||
): Promise<HealthModuleOptions> => ({
|
||||
serviceName: 'ad',
|
||||
criticalLoggingKey: 'logging.ad.health.crit',
|
||||
checkRepositories: [adRepository],
|
||||
messagePublisher,
|
||||
}),
|
||||
}),
|
||||
AdModule,
|
||||
MessagerModule,
|
||||
],
|
||||
exports: [AdModule, MessagerModule],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
export const AD_MESSAGE_PUBLISHER = Symbol('AD_MESSAGE_PUBLISHER');
|
||||
export const PARAMS_PROVIDER = Symbol('PARAMS_PROVIDER');
|
||||
export const TIMEZONE_FINDER = Symbol('TIMEZONE_FINDER');
|
||||
export const TIME_CONVERTER = Symbol('TIME_CONVERTER');
|
||||
|
|
|
@ -2,12 +2,12 @@ import { Module, Provider } from '@nestjs/common';
|
|||
import { CreateAdGrpcController } from './interface/grpc-controllers/create-ad.grpc.controller';
|
||||
import { CqrsModule } from '@nestjs/cqrs';
|
||||
import {
|
||||
AD_MESSAGE_PUBLISHER,
|
||||
AD_REPOSITORY,
|
||||
PARAMS_PROVIDER,
|
||||
TIMEZONE_FINDER,
|
||||
TIME_CONVERTER,
|
||||
} from './ad.di-tokens';
|
||||
import { MESSAGE_PUBLISHER } from '@src/app.constants';
|
||||
import { AdRepository } from './infrastructure/ad.repository';
|
||||
import { DefaultParamsProvider } from './infrastructure/default-params-provider';
|
||||
import { AdMapper } from './ad.mapper';
|
||||
|
@ -17,7 +17,6 @@ import { TimeConverter } from './infrastructure/time-converter';
|
|||
import { FindAdByIdGrpcController } from './interface/grpc-controllers/find-ad-by-id.grpc.controller';
|
||||
import { FindAdByIdQueryHandler } from './core/application/queries/find-ad-by-id/find-ad-by-id.query-handler';
|
||||
import { PublishMessageWhenAdIsCreatedDomainEventHandler } from './core/application/event-handlers/publish-message-when-ad-is-created.domain-event-handler';
|
||||
import { PublishLogMessageWhenAdIsCreatedDomainEventHandler } from './core/application/event-handlers/publish-log-message-when-ad-is-created.domain-event-handler';
|
||||
import { PrismaService } from './infrastructure/prisma.service';
|
||||
import { MessageBrokerPublisher } from '@mobicoop/message-broker-module';
|
||||
|
||||
|
@ -25,7 +24,6 @@ const grpcControllers = [CreateAdGrpcController, FindAdByIdGrpcController];
|
|||
|
||||
const eventHandlers: Provider[] = [
|
||||
PublishMessageWhenAdIsCreatedDomainEventHandler,
|
||||
PublishLogMessageWhenAdIsCreatedDomainEventHandler,
|
||||
];
|
||||
|
||||
const commandHandlers: Provider[] = [CreateAdService];
|
||||
|
@ -41,16 +39,15 @@ const repositories: Provider[] = [
|
|||
},
|
||||
];
|
||||
|
||||
const messageBrokers: Provider[] = [
|
||||
const messagePublishers: Provider[] = [
|
||||
{
|
||||
provide: MESSAGE_PUBLISHER,
|
||||
useClass: MessageBrokerPublisher,
|
||||
provide: AD_MESSAGE_PUBLISHER,
|
||||
useExisting: MessageBrokerPublisher,
|
||||
},
|
||||
];
|
||||
|
||||
const orms: Provider[] = [PrismaService];
|
||||
|
||||
const utilities: Provider[] = [
|
||||
const adapters: Provider[] = [
|
||||
{
|
||||
provide: PARAMS_PROVIDER,
|
||||
useClass: DefaultParamsProvider,
|
||||
|
@ -74,9 +71,9 @@ const utilities: Provider[] = [
|
|||
...queryHandlers,
|
||||
...mappers,
|
||||
...repositories,
|
||||
...messageBrokers,
|
||||
...messagePublishers,
|
||||
...orms,
|
||||
...utilities,
|
||||
...adapters,
|
||||
],
|
||||
exports: [
|
||||
PrismaService,
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
import { MESSAGE_PUBLISHER } from '@src/app.constants';
|
||||
import { AdCreatedDomainEvent } from '../../domain/events/ad-created.domain-events';
|
||||
import { MessagePublisherPort } from '@mobicoop/ddd-library';
|
||||
|
||||
@Injectable()
|
||||
export class PublishLogMessageWhenAdIsCreatedDomainEventHandler {
|
||||
constructor(
|
||||
@Inject(MESSAGE_PUBLISHER)
|
||||
private readonly messagePublisher: MessagePublisherPort,
|
||||
) {}
|
||||
|
||||
@OnEvent(AdCreatedDomainEvent.name, { async: true, promisify: true })
|
||||
async handle(event: AdCreatedDomainEvent): Promise<any> {
|
||||
this.messagePublisher.publish(
|
||||
'logging.ad.created.info',
|
||||
JSON.stringify(event),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
import { MESSAGE_PUBLISHER } from '@src/app.constants';
|
||||
import { AdCreatedDomainEvent } from '../../domain/events/ad-created.domain-events';
|
||||
import { MessagePublisherPort } from '@mobicoop/ddd-library';
|
||||
import { AD_MESSAGE_PUBLISHER } from '@modules/ad/ad.di-tokens';
|
||||
|
||||
@Injectable()
|
||||
export class PublishMessageWhenAdIsCreatedDomainEventHandler {
|
||||
constructor(
|
||||
@Inject(MESSAGE_PUBLISHER)
|
||||
@Inject(AD_MESSAGE_PUBLISHER)
|
||||
private readonly messagePublisher: MessagePublisherPort,
|
||||
) {}
|
||||
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { Inject, Injectable, Logger } from '@nestjs/common';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { AdEntity } from '../core/domain/ad.entity';
|
||||
import { AdMapper } from '../ad.mapper';
|
||||
import { AdRepositoryPort } from '../core/application/ports/ad.repository.port';
|
||||
import { PrismaRepositoryBase } from '@mobicoop/ddd-library';
|
||||
import {
|
||||
LoggerBase,
|
||||
MessagePublisherPort,
|
||||
PrismaRepositoryBase,
|
||||
} from '@mobicoop/ddd-library';
|
||||
import { PrismaService } from './prisma.service';
|
||||
import { AD_MESSAGE_PUBLISHER } from '../ad.di-tokens';
|
||||
|
||||
export type AdBaseModel = {
|
||||
uuid: string;
|
||||
|
@ -72,13 +77,19 @@ export class AdRepository
|
|||
prisma: PrismaService,
|
||||
mapper: AdMapper,
|
||||
eventEmitter: EventEmitter2,
|
||||
@Inject(AD_MESSAGE_PUBLISHER)
|
||||
protected readonly messagePublisher: MessagePublisherPort,
|
||||
) {
|
||||
super(
|
||||
prisma.ad,
|
||||
prisma,
|
||||
mapper,
|
||||
eventEmitter,
|
||||
new Logger(AdRepository.name),
|
||||
new LoggerBase({
|
||||
logger: new Logger(AdRepository.name),
|
||||
domain: 'ad',
|
||||
messagePublisher,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
import {
|
||||
AD_MESSAGE_PUBLISHER,
|
||||
AD_REPOSITORY,
|
||||
PARAMS_PROVIDER,
|
||||
TIMEZONE_FINDER,
|
||||
TIME_CONVERTER,
|
||||
} from '@modules/ad/ad.di-tokens';
|
||||
import { AdMapper } from '@modules/ad/ad.mapper';
|
||||
import { AdEntity } from '@modules/ad/core/domain/ad.entity';
|
||||
import {
|
||||
CreateAdProps,
|
||||
DefaultAdProps,
|
||||
Frequency,
|
||||
} from '@modules/ad/core/domain/ad.types';
|
||||
import { AdRepository } from '@modules/ad/infrastructure/ad.repository';
|
||||
import { DefaultParamsProvider } from '@modules/ad/infrastructure/default-params-provider';
|
||||
import { PrismaService } from '@modules/ad/infrastructure/prisma.service';
|
||||
|
@ -48,20 +55,6 @@ describe('Ad Repository', () => {
|
|||
seatsRequested: 0,
|
||||
strict: 'false',
|
||||
};
|
||||
// const passengerAd = {
|
||||
// driver: 'false',
|
||||
// passenger: 'true',
|
||||
// seatsProposed: 0,
|
||||
// seatsRequested: 1,
|
||||
// strict: 'false',
|
||||
// };
|
||||
// const driverAndPassengerAd = {
|
||||
// driver: 'true',
|
||||
// passenger: 'true',
|
||||
// seatsProposed: 3,
|
||||
// seatsRequested: 1,
|
||||
// strict: 'false',
|
||||
// };
|
||||
const punctualAd = {
|
||||
frequency: `'PUNCTUAL'`,
|
||||
fromDate: `'2023-01-01'`,
|
||||
|
@ -81,25 +74,6 @@ describe('Ad Repository', () => {
|
|||
satMargin: 900,
|
||||
sunMargin: 900,
|
||||
};
|
||||
// const recurrentAd = {
|
||||
// frequency: `'RECURRENT'`,
|
||||
// fromDate: `'2023-01-01'`,
|
||||
// toDate: `'2023-12-31'`,
|
||||
// monTime: `'2023-01-01T07:15:00Z'`,
|
||||
// tueTime: `'2023-01-01T07:15:00Z'`,
|
||||
// wedTime: `'2023-01-01T07:05:00Z'`,
|
||||
// thuTime: `'2023-01-01T07:15:00Z'`,
|
||||
// friTime: `'2023-01-01T07:15:00Z'`,
|
||||
// satTime: 'NULL',
|
||||
// sunTime: 'NULL',
|
||||
// monMargin: 900,
|
||||
// tueMargin: 900,
|
||||
// wedMargin: 900,
|
||||
// thuMargin: 900,
|
||||
// friMargin: 900,
|
||||
// satMargin: 900,
|
||||
// sunMargin: 900,
|
||||
// };
|
||||
const originWaypoint = {
|
||||
position: 0,
|
||||
lon: 43.7102,
|
||||
|
@ -140,120 +114,15 @@ describe('Ad Repository', () => {
|
|||
}
|
||||
};
|
||||
|
||||
// const createRecurrentDriverAds = async (nbToCreate = 10) => {
|
||||
// const adToCreate = {
|
||||
// ...baseUuid,
|
||||
// ...baseUserUuid,
|
||||
// ...driverAd,
|
||||
// ...punctualAd,
|
||||
// };
|
||||
// for (let i = 0; i < nbToCreate; i++) {
|
||||
// adToCreate.uuid = getSeed(i, baseUuid.uuid);
|
||||
// await executeInsertCommand('ad', adToCreate);
|
||||
// await executeInsertCommand('waypoint', {
|
||||
// uuid: getSeed(i, baseOriginWaypointUuid.uuid),
|
||||
// adUuid: adToCreate.uuid,
|
||||
// ...originWaypoint,
|
||||
// });
|
||||
// await executeInsertCommand('waypoint', {
|
||||
// uuid: getSeed(i, baseDestinationWaypointUuid.uuid),
|
||||
// adUuid: adToCreate.uuid,
|
||||
// ...destinationWaypoint,
|
||||
// });
|
||||
// }
|
||||
// };
|
||||
const mockMessagePublisher = {
|
||||
publish: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
// const createPunctualPassengerAds = async (nbToCreate = 10) => {
|
||||
// const adToCreate = {
|
||||
// ...baseUuid,
|
||||
// ...baseUserUuid,
|
||||
// ...passengerAd,
|
||||
// ...punctualAd,
|
||||
// };
|
||||
// for (let i = 0; i < nbToCreate; i++) {
|
||||
// adToCreate.uuid = getSeed(i, baseUuid.uuid);
|
||||
// await executeInsertCommand('ad', adToCreate);
|
||||
// await executeInsertCommand('waypoint', {
|
||||
// uuid: getSeed(i, baseOriginWaypointUuid.uuid),
|
||||
// adUuid: adToCreate.uuid,
|
||||
// ...originWaypoint,
|
||||
// });
|
||||
// await executeInsertCommand('waypoint', {
|
||||
// uuid: getSeed(i, baseDestinationWaypointUuid.uuid),
|
||||
// adUuid: adToCreate.uuid,
|
||||
// ...destinationWaypoint,
|
||||
// });
|
||||
// }
|
||||
// };
|
||||
|
||||
// const createRecurrentPassengerAds = async (nbToCreate = 10) => {
|
||||
// const adToCreate = {
|
||||
// ...baseUuid,
|
||||
// ...baseUserUuid,
|
||||
// ...passengerAd,
|
||||
// ...recurrentAd,
|
||||
// };
|
||||
// for (let i = 0; i < nbToCreate; i++) {
|
||||
// adToCreate.uuid = getSeed(i, baseUuid.uuid);
|
||||
// await executeInsertCommand('ad', adToCreate);
|
||||
// await executeInsertCommand('waypoint', {
|
||||
// uuid: getSeed(i, baseOriginWaypointUuid.uuid),
|
||||
// adUuid: adToCreate.uuid,
|
||||
// ...originWaypoint,
|
||||
// });
|
||||
// await executeInsertCommand('waypoint', {
|
||||
// uuid: getSeed(i, baseDestinationWaypointUuid.uuid),
|
||||
// adUuid: adToCreate.uuid,
|
||||
// ...destinationWaypoint,
|
||||
// });
|
||||
// }
|
||||
// };
|
||||
|
||||
// const createPunctualDriverPassengerAds = async (nbToCreate = 10) => {
|
||||
// const adToCreate = {
|
||||
// ...baseUuid,
|
||||
// ...baseUserUuid,
|
||||
// ...driverAndPassengerAd,
|
||||
// ...punctualAd,
|
||||
// };
|
||||
// for (let i = 0; i < nbToCreate; i++) {
|
||||
// adToCreate.uuid = getSeed(i, baseUuid.uuid);
|
||||
// await executeInsertCommand('ad', adToCreate);
|
||||
// await executeInsertCommand('waypoint', {
|
||||
// uuid: getSeed(i, baseOriginWaypointUuid.uuid),
|
||||
// adUuid: adToCreate.uuid,
|
||||
// ...originWaypoint,
|
||||
// });
|
||||
// await executeInsertCommand('waypoint', {
|
||||
// uuid: getSeed(i, baseDestinationWaypointUuid.uuid),
|
||||
// adUuid: adToCreate.uuid,
|
||||
// ...destinationWaypoint,
|
||||
// });
|
||||
// }
|
||||
// };
|
||||
|
||||
// const createRecurrentDriverPassengerAds = async (nbToCreate = 10) => {
|
||||
// const adToCreate = {
|
||||
// ...baseUuid,
|
||||
// ...baseUserUuid,
|
||||
// ...driverAndPassengerAd,
|
||||
// ...recurrentAd,
|
||||
// };
|
||||
// for (let i = 0; i < nbToCreate; i++) {
|
||||
// adToCreate.uuid = getSeed(i, baseUuid.uuid);
|
||||
// await executeInsertCommand('ad', adToCreate);
|
||||
// await executeInsertCommand('waypoint', {
|
||||
// uuid: getSeed(i, baseOriginWaypointUuid.uuid),
|
||||
// adUuid: adToCreate.uuid,
|
||||
// ...originWaypoint,
|
||||
// });
|
||||
// await executeInsertCommand('waypoint', {
|
||||
// uuid: getSeed(i, baseDestinationWaypointUuid.uuid),
|
||||
// adUuid: adToCreate.uuid,
|
||||
// ...destinationWaypoint,
|
||||
// });
|
||||
// }
|
||||
// };
|
||||
const mockLogger = {
|
||||
log: jest.fn(),
|
||||
warn: jest.fn(),
|
||||
error: jest.fn(),
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
|
@ -280,11 +149,20 @@ describe('Ad Repository', () => {
|
|||
provide: TIME_CONVERTER,
|
||||
useClass: TimeConverter,
|
||||
},
|
||||
{
|
||||
provide: AD_MESSAGE_PUBLISHER,
|
||||
useValue: mockMessagePublisher,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
})
|
||||
// disable logging
|
||||
.setLogger(mockLogger)
|
||||
.compile();
|
||||
|
||||
prismaService = module.get<PrismaService>(PrismaService);
|
||||
adRepository = module.get<AdRepository>(AD_REPOSITORY);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await prismaService.$disconnect();
|
||||
});
|
||||
|
@ -292,129 +170,7 @@ describe('Ad Repository', () => {
|
|||
beforeEach(async () => {
|
||||
await prismaService.ad.deleteMany();
|
||||
});
|
||||
// describe('findAll', () => {
|
||||
// it('should return an empty data array', async () => {
|
||||
// const res = await adRepository.findAll();
|
||||
// expect(res).toEqual({
|
||||
// data: [],
|
||||
// total: 0,
|
||||
// });
|
||||
// });
|
||||
|
||||
// describe('drivers', () => {
|
||||
// it('should return a data array with 8 punctual driver ads', async () => {
|
||||
// await createPunctualDriverAds(8);
|
||||
// const ads = await adRepository.findAll();
|
||||
// expect(ads.data.length).toBe(8);
|
||||
// expect(ads.total).toBe(8);
|
||||
// expect(ads.data[0].driver).toBeTruthy();
|
||||
// expect(ads.data[0].passenger).toBeFalsy();
|
||||
// });
|
||||
|
||||
// it('should return a data array limited to 10 punctual driver ads', async () => {
|
||||
// await createPunctualDriverAds(20);
|
||||
// const ads = await adRepository.findAll();
|
||||
// expect(ads.data.length).toBe(10);
|
||||
// expect(ads.total).toBe(20);
|
||||
// expect(ads.data[1].driver).toBeTruthy();
|
||||
// expect(ads.data[1].passenger).toBeFalsy();
|
||||
// });
|
||||
|
||||
// it('should return a data array with 8 recurrent driver ads', async () => {
|
||||
// await createRecurrentDriverAds(8);
|
||||
// const ads = await adRepository.findAll();
|
||||
// expect(ads.data.length).toBe(8);
|
||||
// expect(ads.total).toBe(8);
|
||||
// expect(ads.data[2].driver).toBeTruthy();
|
||||
// expect(ads.data[2].passenger).toBeFalsy();
|
||||
// });
|
||||
|
||||
// it('should return a data array limited to 10 recurrent driver ads', async () => {
|
||||
// await createRecurrentDriverAds(20);
|
||||
// const ads = await adRepository.findAll();
|
||||
// expect(ads.data.length).toBe(10);
|
||||
// expect(ads.total).toBe(20);
|
||||
// expect(ads.data[3].driver).toBeTruthy();
|
||||
// expect(ads.data[3].passenger).toBeFalsy();
|
||||
// });
|
||||
// });
|
||||
|
||||
// describe('passengers', () => {
|
||||
// it('should return a data array with 7 punctual passenger ads', async () => {
|
||||
// await createPunctualPassengerAds(7);
|
||||
// const ads = await adRepository.findAll();
|
||||
// expect(ads.data.length).toBe(7);
|
||||
// expect(ads.total).toBe(7);
|
||||
// expect(ads.data[0].passenger).toBeTruthy();
|
||||
// expect(ads.data[0].driver).toBeFalsy();
|
||||
// });
|
||||
|
||||
// it('should return a data array limited to 10 punctual passenger ads', async () => {
|
||||
// await createPunctualPassengerAds(15);
|
||||
// const ads = await adRepository.findAll();
|
||||
// expect(ads.data.length).toBe(10);
|
||||
// expect(ads.total).toBe(15);
|
||||
// expect(ads.data[1].passenger).toBeTruthy();
|
||||
// expect(ads.data[1].driver).toBeFalsy();
|
||||
// });
|
||||
|
||||
// it('should return a data array with 7 recurrent passenger ads', async () => {
|
||||
// await createRecurrentPassengerAds(7);
|
||||
// const ads = await adRepository.findAll();
|
||||
// expect(ads.data.length).toBe(7);
|
||||
// expect(ads.total).toBe(7);
|
||||
// expect(ads.data[2].passenger).toBeTruthy();
|
||||
// expect(ads.data[2].driver).toBeFalsy();
|
||||
// });
|
||||
|
||||
// it('should return a data array limited to 10 recurrent passenger ads', async () => {
|
||||
// await createRecurrentPassengerAds(15);
|
||||
// const ads = await adRepository.findAll();
|
||||
// expect(ads.data.length).toBe(10);
|
||||
// expect(ads.total).toBe(15);
|
||||
// expect(ads.data[3].passenger).toBeTruthy();
|
||||
// expect(ads.data[3].driver).toBeFalsy();
|
||||
// });
|
||||
// });
|
||||
|
||||
// describe('drivers and passengers', () => {
|
||||
// it('should return a data array with 6 punctual driver and passenger ads', async () => {
|
||||
// await createPunctualDriverPassengerAds(6);
|
||||
// const ads = await adRepository.findAll();
|
||||
// expect(ads.data.length).toBe(6);
|
||||
// expect(ads.total).toBe(6);
|
||||
// expect(ads.data[0].passenger).toBeTruthy();
|
||||
// expect(ads.data[0].driver).toBeTruthy();
|
||||
// });
|
||||
|
||||
// it('should return a data array limited to 10 punctual driver and passenger ads', async () => {
|
||||
// await createPunctualDriverPassengerAds(16);
|
||||
// const ads = await adRepository.findAll();
|
||||
// expect(ads.data.length).toBe(10);
|
||||
// expect(ads.total).toBe(16);
|
||||
// expect(ads.data[1].passenger).toBeTruthy();
|
||||
// expect(ads.data[1].driver).toBeTruthy();
|
||||
// });
|
||||
|
||||
// it('should return a data array with 6 recurrent driver and passenger ads', async () => {
|
||||
// await createRecurrentDriverPassengerAds(6);
|
||||
// const ads = await adRepository.findAll();
|
||||
// expect(ads.data.length).toBe(6);
|
||||
// expect(ads.total).toBe(6);
|
||||
// expect(ads.data[2].passenger).toBeTruthy();
|
||||
// expect(ads.data[2].driver).toBeTruthy();
|
||||
// });
|
||||
|
||||
// it('should return a data array limited to 10 recurrent driver and passenger ads', async () => {
|
||||
// await createRecurrentDriverPassengerAds(16);
|
||||
// const ads = await adRepository.findAll();
|
||||
// expect(ads.data.length).toBe(10);
|
||||
// expect(ads.total).toBe(16);
|
||||
// expect(ads.data[3].passenger).toBeTruthy();
|
||||
// expect(ads.data[3].driver).toBeTruthy();
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
describe('findOneById', () => {
|
||||
it('should return an ad', async () => {
|
||||
await createPunctualDriverAds(1);
|
||||
|
@ -424,80 +180,97 @@ describe('Ad Repository', () => {
|
|||
|
||||
expect(result.id).toBe(baseUuid.uuid);
|
||||
});
|
||||
});
|
||||
|
||||
// it('should return null', async () => {
|
||||
// const ad = await adRepository.findOneById(
|
||||
// '544572be-11fb-4244-8235-587221fc9104',
|
||||
// );
|
||||
// expect(ad).toBeNull();
|
||||
describe('create', () => {
|
||||
it('should create an ad', async () => {
|
||||
const beforeCount = await prismaService.ad.count();
|
||||
|
||||
const createAdProps: CreateAdProps = {
|
||||
userId: 'b4b56444-f8d3-4110-917c-e37bba77f383',
|
||||
driver: true,
|
||||
passenger: false,
|
||||
frequency: Frequency.PUNCTUAL,
|
||||
fromDate: '2023-02-01',
|
||||
toDate: '2023-02-01',
|
||||
schedule: {
|
||||
wed: '12:05',
|
||||
},
|
||||
marginDurations: {
|
||||
wed: 900,
|
||||
},
|
||||
seatsProposed: 3,
|
||||
seatsRequested: 1,
|
||||
strict: false,
|
||||
waypoints: [
|
||||
{
|
||||
position: 0,
|
||||
address: {
|
||||
locality: 'Nice',
|
||||
postalCode: '06000',
|
||||
country: 'France',
|
||||
coordinates: {
|
||||
lon: 43.7102,
|
||||
lat: 7.262,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
position: 1,
|
||||
address: {
|
||||
locality: 'Marseille',
|
||||
postalCode: '13000',
|
||||
country: 'France',
|
||||
coordinates: {
|
||||
lon: 43.2965,
|
||||
lat: 5.3698,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const defaultAdProps: DefaultAdProps = {
|
||||
driver: false,
|
||||
passenger: true,
|
||||
marginDurations: {
|
||||
mon: 900,
|
||||
tue: 900,
|
||||
wed: 900,
|
||||
thu: 900,
|
||||
fri: 900,
|
||||
sat: 900,
|
||||
sun: 900,
|
||||
},
|
||||
seatsProposed: 3,
|
||||
seatsRequested: 1,
|
||||
strict: false,
|
||||
};
|
||||
|
||||
const adToCreate: AdEntity = AdEntity.create(
|
||||
createAdProps,
|
||||
defaultAdProps,
|
||||
);
|
||||
await adRepository.insert(adToCreate);
|
||||
|
||||
const afterCount = await prismaService.ad.count();
|
||||
|
||||
expect(afterCount - beforeCount).toBe(1);
|
||||
});
|
||||
|
||||
// it('should throw a UniqueConstraintException if ad already exists', async () => {
|
||||
// await prismaService.ad.create({
|
||||
// data: {
|
||||
// uuid: uuid,
|
||||
// password: bcrypt.hashSync(`password`, 10),
|
||||
// },
|
||||
// });
|
||||
|
||||
// const authenticationToCreate: AuthenticationEntity =
|
||||
// await AuthenticationEntity.create(createAuthenticationProps);
|
||||
// await expect(
|
||||
// authenticationRepository.insert(authenticationToCreate),
|
||||
// ).rejects.toBeInstanceOf(UniqueConstraintException);
|
||||
// });
|
||||
});
|
||||
// describe('create', () => {
|
||||
// it('should create a punctual ad', async () => {
|
||||
// const beforeCount = await prismaService.ad.count();
|
||||
// const adToCreate: AdDTO = new AdDTO();
|
||||
|
||||
// adToCreate.uuid = 'be459a29-7a41-4c0b-b371-abe90bfb6f00';
|
||||
// adToCreate.userUuid = '4e52b54d-a729-4dbd-9283-f84a11bb2200';
|
||||
// adToCreate.driver = true;
|
||||
// adToCreate.passenger = false;
|
||||
// adToCreate.frequency = Frequency.PUNCTUAL;
|
||||
// adToCreate.fromDate = new Date('05-22-2023 09:36');
|
||||
// adToCreate.toDate = new Date('05-22-2023 09:36');
|
||||
// adToCreate.monTime = '09:36';
|
||||
// adToCreate.monMargin = 900;
|
||||
// adToCreate.tueMargin = 900;
|
||||
// adToCreate.wedMargin = 900;
|
||||
// adToCreate.thuMargin = 900;
|
||||
// adToCreate.friMargin = 900;
|
||||
// adToCreate.satMargin = 900;
|
||||
// adToCreate.sunMargin = 900;
|
||||
// adToCreate.seatsProposed = 3;
|
||||
// adToCreate.seatsRequested = 0;
|
||||
// adToCreate.strict = false;
|
||||
// adToCreate.waypoints = {
|
||||
// create: [originWaypoint as Waypoint, destinationWaypoint as Waypoint],
|
||||
// };
|
||||
// const ad = await adRepository.create(adToCreate);
|
||||
|
||||
// const afterCount = await prismaService.ad.count();
|
||||
|
||||
// expect(afterCount - beforeCount).toBe(1);
|
||||
// expect(ad.uuid).toBe('be459a29-7a41-4c0b-b371-abe90bfb6f00');
|
||||
// });
|
||||
// it('should create a recurrent ad', async () => {
|
||||
// const beforeCount = await prismaService.ad.count();
|
||||
// const adToCreate: AdDTO = new AdDTO();
|
||||
|
||||
// adToCreate.uuid = '137a26fa-4b38-48ba-aecf-1a75f6b20f3d';
|
||||
// adToCreate.userUuid = '4e52b54d-a729-4dbd-9283-f84a11bb2200';
|
||||
// adToCreate.driver = true;
|
||||
// adToCreate.passenger = false;
|
||||
// adToCreate.frequency = Frequency.RECURRENT;
|
||||
// adToCreate.fromDate = new Date('01-15-2023 ');
|
||||
// adToCreate.toDate = new Date('10-31-2023');
|
||||
// adToCreate.monTime = '07:30';
|
||||
// adToCreate.friTime = '07:45';
|
||||
// adToCreate.thuTime = '08:00';
|
||||
// adToCreate.monMargin = 900;
|
||||
// adToCreate.tueMargin = 900;
|
||||
// adToCreate.wedMargin = 900;
|
||||
// adToCreate.thuMargin = 900;
|
||||
// adToCreate.friMargin = 900;
|
||||
// adToCreate.satMargin = 900;
|
||||
// adToCreate.sunMargin = 900;
|
||||
// adToCreate.seatsProposed = 2;
|
||||
// adToCreate.seatsRequested = 0;
|
||||
// adToCreate.strict = false;
|
||||
// adToCreate.waypoints = {
|
||||
// create: [originWaypoint as Waypoint, destinationWaypoint as Waypoint],
|
||||
// };
|
||||
// const ad = await adRepository.create(adToCreate);
|
||||
|
||||
// const afterCount = await prismaService.ad.count();
|
||||
|
||||
// expect(afterCount - beforeCount).toBe(1);
|
||||
// expect(ad.uuid).toBe('137a26fa-4b38-48ba-aecf-1a75f6b20f3d');
|
||||
// });
|
||||
// });
|
||||
});
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
import { Frequency } from '@modules/ad/core/domain/ad.types';
|
||||
import { PublishLogMessageWhenAdIsCreatedDomainEventHandler } from '@modules/ad/core/application/event-handlers/publish-log-message-when-ad-is-created.domain-event-handler';
|
||||
import { AdCreatedDomainEvent } from '@modules/ad/core/domain/events/ad-created.domain-events';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { MESSAGE_PUBLISHER } from '@src/app.constants';
|
||||
|
||||
const mockMessagePublisher = {
|
||||
publish: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
describe('Publish log message when ad is created domain event handler', () => {
|
||||
let publishLogMessageWhenAdIsCreatedDomainEventHandler: PublishLogMessageWhenAdIsCreatedDomainEventHandler;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: MESSAGE_PUBLISHER,
|
||||
useValue: mockMessagePublisher,
|
||||
},
|
||||
PublishLogMessageWhenAdIsCreatedDomainEventHandler,
|
||||
],
|
||||
}).compile();
|
||||
|
||||
publishLogMessageWhenAdIsCreatedDomainEventHandler =
|
||||
module.get<PublishLogMessageWhenAdIsCreatedDomainEventHandler>(
|
||||
PublishLogMessageWhenAdIsCreatedDomainEventHandler,
|
||||
);
|
||||
});
|
||||
|
||||
it('should publish a log message', () => {
|
||||
jest.spyOn(mockMessagePublisher, 'publish');
|
||||
const adCreatedDomainEvent: AdCreatedDomainEvent = {
|
||||
id: 'some-domain-event-id',
|
||||
aggregateId: 'some-aggregate-id',
|
||||
userId: 'some-user-id',
|
||||
driver: false,
|
||||
passenger: true,
|
||||
frequency: Frequency.PUNCTUAL,
|
||||
fromDate: '2023-06-28',
|
||||
toDate: '2023-06-28',
|
||||
monTime: undefined,
|
||||
tueTime: undefined,
|
||||
wedTime: '07:15',
|
||||
thuTime: undefined,
|
||||
friTime: undefined,
|
||||
satTime: undefined,
|
||||
sunTime: undefined,
|
||||
monMarginDuration: 900,
|
||||
tueMarginDuration: 900,
|
||||
wedMarginDuration: 900,
|
||||
thuMarginDuration: 900,
|
||||
friMarginDuration: 900,
|
||||
satMarginDuration: 900,
|
||||
sunMarginDuration: 900,
|
||||
seatsProposed: 3,
|
||||
seatsRequested: 1,
|
||||
strict: false,
|
||||
waypoints: [
|
||||
{
|
||||
position: 0,
|
||||
houseNumber: '5',
|
||||
street: 'Avenue Foch',
|
||||
locality: 'Nancy',
|
||||
postalCode: '54000',
|
||||
country: 'France',
|
||||
lat: 48.689445,
|
||||
lon: 6.1765102,
|
||||
},
|
||||
{
|
||||
position: 1,
|
||||
locality: 'Paris',
|
||||
postalCode: '75000',
|
||||
country: 'France',
|
||||
lat: 48.8566,
|
||||
lon: 2.3522,
|
||||
},
|
||||
],
|
||||
metadata: {
|
||||
timestamp: new Date('2023-06-28T05:00:00Z').getTime(),
|
||||
correlationId: 'some-correlation-id',
|
||||
},
|
||||
};
|
||||
publishLogMessageWhenAdIsCreatedDomainEventHandler.handle(
|
||||
adCreatedDomainEvent,
|
||||
);
|
||||
expect(publishLogMessageWhenAdIsCreatedDomainEventHandler).toBeDefined();
|
||||
expect(mockMessagePublisher.publish).toHaveBeenCalledTimes(1);
|
||||
expect(mockMessagePublisher.publish).toHaveBeenCalledWith(
|
||||
'logging.ad.created.info',
|
||||
'{"id":"some-domain-event-id","aggregateId":"some-aggregate-id","userId":"some-user-id","driver":false,"passenger":true,"frequency":"PUNCTUAL","fromDate":"2023-06-28","toDate":"2023-06-28","wedTime":"07:15","monMarginDuration":900,"tueMarginDuration":900,"wedMarginDuration":900,"thuMarginDuration":900,"friMarginDuration":900,"satMarginDuration":900,"sunMarginDuration":900,"seatsProposed":3,"seatsRequested":1,"strict":false,"waypoints":[{"position":0,"houseNumber":"5","street":"Avenue Foch","locality":"Nancy","postalCode":"54000","country":"France","lat":48.689445,"lon":6.1765102},{"position":1,"locality":"Paris","postalCode":"75000","country":"France","lat":48.8566,"lon":2.3522}],"metadata":{"timestamp":1687928400000,"correlationId":"some-correlation-id"}}',
|
||||
);
|
||||
});
|
||||
});
|
|
@ -2,7 +2,7 @@ import { Frequency } from '@modules/ad/core/domain/ad.types';
|
|||
import { PublishMessageWhenAdIsCreatedDomainEventHandler } from '@modules/ad/core/application/event-handlers/publish-message-when-ad-is-created.domain-event-handler';
|
||||
import { AdCreatedDomainEvent } from '@modules/ad/core/domain/events/ad-created.domain-events';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { MESSAGE_PUBLISHER } from '@src/app.constants';
|
||||
import { AD_MESSAGE_PUBLISHER } from '@modules/ad/ad.di-tokens';
|
||||
|
||||
const mockMessagePublisher = {
|
||||
publish: jest.fn().mockImplementation(),
|
||||
|
@ -15,7 +15,7 @@ describe('Publish message when ad is created domain event handler', () => {
|
|||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: MESSAGE_PUBLISHER,
|
||||
provide: AD_MESSAGE_PUBLISHER,
|
||||
useValue: mockMessagePublisher,
|
||||
},
|
||||
PublishMessageWhenAdIsCreatedDomainEventHandler,
|
||||
|
|
|
@ -50,6 +50,10 @@ const mockTimeConverter: TimeConverterPort = {
|
|||
utcDatetimeToLocalTime: jest.fn(),
|
||||
};
|
||||
|
||||
const mockMessagePublisher = {
|
||||
publish: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
describe('Ad repository', () => {
|
||||
let prismaService: PrismaService;
|
||||
let adMapper: AdMapper;
|
||||
|
@ -82,7 +86,12 @@ describe('Ad repository', () => {
|
|||
});
|
||||
it('should be defined', () => {
|
||||
expect(
|
||||
new AdRepository(prismaService, adMapper, eventEmitter),
|
||||
new AdRepository(
|
||||
prismaService,
|
||||
adMapper,
|
||||
eventEmitter,
|
||||
mockMessagePublisher,
|
||||
),
|
||||
).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
export interface CheckRepositoryPort {
|
||||
healthCheck(): Promise<boolean>;
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import {
|
||||
HealthCheckError,
|
||||
HealthCheckResult,
|
||||
HealthIndicator,
|
||||
HealthIndicatorResult,
|
||||
} from '@nestjs/terminus';
|
||||
import { CheckRepositoryPort } from '../ports/check-repository.port';
|
||||
import { AD_REPOSITORY } from '@modules/health/health.di-tokens';
|
||||
import { AdRepositoryPort } from '@modules/ad/core/application/ports/ad.repository.port';
|
||||
import { MESSAGE_PUBLISHER } from '@src/app.constants';
|
||||
import { LOGGING_AD_HEALTH_CRIT } from '@modules/health/health.constants';
|
||||
import { MessagePublisherPort } from '@mobicoop/ddd-library';
|
||||
|
||||
@Injectable()
|
||||
export class RepositoriesHealthIndicatorUseCase extends HealthIndicator {
|
||||
private _checkRepositories: CheckRepositoryPort[];
|
||||
constructor(
|
||||
@Inject(AD_REPOSITORY)
|
||||
private readonly adRepository: AdRepositoryPort,
|
||||
@Inject(MESSAGE_PUBLISHER)
|
||||
private readonly messagePublisher: MessagePublisherPort,
|
||||
) {
|
||||
super();
|
||||
this._checkRepositories = [adRepository];
|
||||
}
|
||||
isHealthy = async (key: string): Promise<HealthIndicatorResult> => {
|
||||
try {
|
||||
await Promise.all(
|
||||
this._checkRepositories.map(
|
||||
async (checkRepository: CheckRepositoryPort) => {
|
||||
await checkRepository.healthCheck();
|
||||
},
|
||||
),
|
||||
);
|
||||
return this.getStatus(key, true);
|
||||
} catch (error) {
|
||||
const healthCheckResult: HealthCheckResult = error;
|
||||
this.messagePublisher.publish(
|
||||
LOGGING_AD_HEALTH_CRIT,
|
||||
JSON.stringify(healthCheckResult.error),
|
||||
);
|
||||
throw new HealthCheckError('Repository', {
|
||||
repository: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export const LOGGING_AD_HEALTH_CRIT = 'logging.ad.health.crit';
|
|
@ -1 +0,0 @@
|
|||
export const AD_REPOSITORY = Symbol('AD_REPOSITORY');
|
|
@ -1,37 +0,0 @@
|
|||
import { Module, Provider } from '@nestjs/common';
|
||||
import { HealthHttpController } from './interface/http-controllers/health.http.controller';
|
||||
import { TerminusModule } from '@nestjs/terminus';
|
||||
import { MESSAGE_PUBLISHER } from 'src/app.constants';
|
||||
import { RepositoriesHealthIndicatorUseCase } from './core/application/usecases/repositories.health-indicator.usecase';
|
||||
import { AdRepository } from '../ad/infrastructure/ad.repository';
|
||||
import { AD_REPOSITORY } from './health.di-tokens';
|
||||
import { HealthGrpcController } from './interface/grpc-controllers/health.grpc.controller';
|
||||
import { AdModule } from '@modules/ad/ad.module';
|
||||
import { MessageBrokerPublisher } from '@mobicoop/message-broker-module';
|
||||
|
||||
const grpcControllers = [HealthGrpcController];
|
||||
|
||||
const httpControllers = [HealthHttpController];
|
||||
|
||||
const useCases: Provider[] = [RepositoriesHealthIndicatorUseCase];
|
||||
|
||||
const repositories: Provider[] = [
|
||||
{
|
||||
provide: AD_REPOSITORY,
|
||||
useClass: AdRepository,
|
||||
},
|
||||
];
|
||||
|
||||
const messageBrokers: Provider[] = [
|
||||
{
|
||||
provide: MESSAGE_PUBLISHER,
|
||||
useClass: MessageBrokerPublisher,
|
||||
},
|
||||
];
|
||||
|
||||
@Module({
|
||||
imports: [TerminusModule, AdModule],
|
||||
controllers: [...grpcControllers, ...httpControllers],
|
||||
providers: [...useCases, ...repositories, ...messageBrokers],
|
||||
})
|
||||
export class HealthModule {}
|
|
@ -1,42 +0,0 @@
|
|||
import { Controller } from '@nestjs/common';
|
||||
import { GrpcMethod } from '@nestjs/microservices';
|
||||
import { RepositoriesHealthIndicatorUseCase } from '../../core/application/usecases/repositories.health-indicator.usecase';
|
||||
|
||||
export enum ServingStatus {
|
||||
UNKNOWN = 0,
|
||||
SERVING = 1,
|
||||
NOT_SERVING = 2,
|
||||
}
|
||||
|
||||
interface HealthCheckRequest {
|
||||
service: string;
|
||||
}
|
||||
|
||||
interface HealthCheckResponse {
|
||||
status: ServingStatus;
|
||||
}
|
||||
|
||||
@Controller()
|
||||
export class HealthGrpcController {
|
||||
constructor(
|
||||
private readonly repositoriesHealthIndicatorUseCase: RepositoriesHealthIndicatorUseCase,
|
||||
) {}
|
||||
|
||||
@GrpcMethod('Health', 'Check')
|
||||
async check(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
data?: HealthCheckRequest,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
metadata?: any,
|
||||
): Promise<HealthCheckResponse> {
|
||||
const healthCheck = await this.repositoriesHealthIndicatorUseCase.isHealthy(
|
||||
'repositories',
|
||||
);
|
||||
return {
|
||||
status:
|
||||
healthCheck['repositories'].status == 'up'
|
||||
? ServingStatus.SERVING
|
||||
: ServingStatus.NOT_SERVING,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package health;
|
||||
|
||||
|
||||
service Health {
|
||||
rpc Check(HealthCheckRequest) returns (HealthCheckResponse);
|
||||
}
|
||||
|
||||
message HealthCheckRequest {
|
||||
string service = 1;
|
||||
}
|
||||
|
||||
message HealthCheckResponse {
|
||||
enum ServingStatus {
|
||||
UNKNOWN = 0;
|
||||
SERVING = 1;
|
||||
NOT_SERVING = 2;
|
||||
}
|
||||
ServingStatus status = 1;
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
import { RepositoriesHealthIndicatorUseCase } from '@modules/health/core/application/usecases/repositories.health-indicator.usecase';
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import {
|
||||
HealthCheckService,
|
||||
HealthCheck,
|
||||
HealthCheckResult,
|
||||
} from '@nestjs/terminus';
|
||||
|
||||
@Controller('health')
|
||||
export class HealthHttpController {
|
||||
constructor(
|
||||
private readonly repositoriesHealthIndicatorUseCase: RepositoriesHealthIndicatorUseCase,
|
||||
private readonly healthCheckService: HealthCheckService,
|
||||
) {}
|
||||
|
||||
@Get()
|
||||
@HealthCheck()
|
||||
async check(): Promise<HealthCheckResult> {
|
||||
try {
|
||||
return await this.healthCheckService.check([
|
||||
async () =>
|
||||
this.repositoriesHealthIndicatorUseCase.isHealthy('repositories'),
|
||||
]);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
import { RepositoriesHealthIndicatorUseCase } from '@modules/health/core/application/usecases/repositories.health-indicator.usecase';
|
||||
import {
|
||||
HealthGrpcController,
|
||||
ServingStatus,
|
||||
} from '@modules/health/interface/grpc-controllers/health.grpc.controller';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
const mockRepositoriesHealthIndicatorUseCase = {
|
||||
isHealthy: jest
|
||||
.fn()
|
||||
.mockImplementationOnce(() => ({
|
||||
repositories: {
|
||||
status: 'up',
|
||||
},
|
||||
}))
|
||||
.mockImplementationOnce(() => ({
|
||||
repositories: {
|
||||
status: 'down',
|
||||
},
|
||||
})),
|
||||
};
|
||||
|
||||
describe('Health Grpc Controller', () => {
|
||||
let healthGrpcController: HealthGrpcController;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: RepositoriesHealthIndicatorUseCase,
|
||||
useValue: mockRepositoriesHealthIndicatorUseCase,
|
||||
},
|
||||
HealthGrpcController,
|
||||
],
|
||||
}).compile();
|
||||
|
||||
healthGrpcController =
|
||||
module.get<HealthGrpcController>(HealthGrpcController);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(healthGrpcController).toBeDefined();
|
||||
});
|
||||
|
||||
it('should return a Serving status ', async () => {
|
||||
jest.spyOn(mockRepositoriesHealthIndicatorUseCase, 'isHealthy');
|
||||
const servingStatus: { status: ServingStatus } =
|
||||
await healthGrpcController.check();
|
||||
expect(servingStatus).toEqual({
|
||||
status: ServingStatus.SERVING,
|
||||
});
|
||||
expect(
|
||||
mockRepositoriesHealthIndicatorUseCase.isHealthy,
|
||||
).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should return a Not Serving status ', async () => {
|
||||
jest.spyOn(mockRepositoriesHealthIndicatorUseCase, 'isHealthy');
|
||||
const servingStatus: { status: ServingStatus } =
|
||||
await healthGrpcController.check();
|
||||
expect(servingStatus).toEqual({
|
||||
status: ServingStatus.NOT_SERVING,
|
||||
});
|
||||
expect(
|
||||
mockRepositoriesHealthIndicatorUseCase.isHealthy,
|
||||
).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
|
@ -1,90 +0,0 @@
|
|||
import { RepositoriesHealthIndicatorUseCase } from '@modules/health/core/application/usecases/repositories.health-indicator.usecase';
|
||||
import { HealthHttpController } from '@modules/health/interface/http-controllers/health.http.controller';
|
||||
import { HealthCheckResult, HealthCheckService } from '@nestjs/terminus';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
const mockHealthCheckService = {
|
||||
check: jest
|
||||
.fn()
|
||||
.mockImplementationOnce(() => ({
|
||||
status: 'ok',
|
||||
info: {
|
||||
repositories: {
|
||||
status: 'up',
|
||||
},
|
||||
},
|
||||
error: {},
|
||||
details: {
|
||||
repositories: {
|
||||
status: 'up',
|
||||
},
|
||||
},
|
||||
}))
|
||||
.mockImplementationOnce(() => ({
|
||||
status: 'error',
|
||||
info: {},
|
||||
error: {
|
||||
repository:
|
||||
"\nInvalid `prisma.$queryRaw()` invocation:\n\n\nCan't reach database server at `v3-db`:`5432`\n\nPlease make sure your database server is running at `v3-db`:`5432`.",
|
||||
},
|
||||
details: {
|
||||
repository:
|
||||
"\nInvalid `prisma.$queryRaw()` invocation:\n\n\nCan't reach database server at `v3-db`:`5432`\n\nPlease make sure your database server is running at `v3-db`:`5432`.",
|
||||
},
|
||||
})),
|
||||
};
|
||||
|
||||
const mockRepositoriesHealthIndicatorUseCase = {
|
||||
isHealthy: jest.fn(),
|
||||
};
|
||||
|
||||
describe('Health Http Controller', () => {
|
||||
let healthHttpController: HealthHttpController;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: HealthCheckService,
|
||||
useValue: mockHealthCheckService,
|
||||
},
|
||||
{
|
||||
provide: RepositoriesHealthIndicatorUseCase,
|
||||
useValue: mockRepositoriesHealthIndicatorUseCase,
|
||||
},
|
||||
HealthHttpController,
|
||||
],
|
||||
}).compile();
|
||||
|
||||
healthHttpController =
|
||||
module.get<HealthHttpController>(HealthHttpController);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(healthHttpController).toBeDefined();
|
||||
});
|
||||
|
||||
it('should return an HealthCheckResult with Ok status ', async () => {
|
||||
jest.spyOn(mockHealthCheckService, 'check');
|
||||
jest.spyOn(mockRepositoriesHealthIndicatorUseCase, 'isHealthy');
|
||||
|
||||
const healthCheckResult: HealthCheckResult =
|
||||
await healthHttpController.check();
|
||||
expect(healthCheckResult.status).toBe('ok');
|
||||
expect(mockHealthCheckService.check).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should return an HealthCheckResult with Error status ', async () => {
|
||||
jest.spyOn(mockHealthCheckService, 'check');
|
||||
jest.spyOn(mockRepositoriesHealthIndicatorUseCase, 'isHealthy');
|
||||
|
||||
const healthCheckResult: HealthCheckResult =
|
||||
await healthHttpController.check();
|
||||
expect(healthCheckResult.status).toBe('error');
|
||||
expect(mockHealthCheckService.check).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
|
@ -1,66 +0,0 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { HealthCheckError, HealthIndicatorResult } from '@nestjs/terminus';
|
||||
import { RepositoriesHealthIndicatorUseCase } from '../../core/application/usecases/repositories.health-indicator.usecase';
|
||||
import { AD_REPOSITORY } from '@modules/health/health.di-tokens';
|
||||
import { MESSAGE_PUBLISHER } from '@src/app.constants';
|
||||
import { DatabaseErrorException } from '@mobicoop/ddd-library';
|
||||
|
||||
const mockAdRepository = {
|
||||
healthCheck: jest
|
||||
.fn()
|
||||
.mockImplementationOnce(() => {
|
||||
return Promise.resolve(true);
|
||||
})
|
||||
.mockImplementation(() => {
|
||||
throw new DatabaseErrorException('An error occured in the database');
|
||||
}),
|
||||
};
|
||||
|
||||
const mockMessagePublisher = {
|
||||
publish: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
describe('RepositoriesHealthIndicatorUseCase', () => {
|
||||
let repositoriesHealthIndicatorUseCase: RepositoriesHealthIndicatorUseCase;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
RepositoriesHealthIndicatorUseCase,
|
||||
{
|
||||
provide: AD_REPOSITORY,
|
||||
useValue: mockAdRepository,
|
||||
},
|
||||
{
|
||||
provide: MESSAGE_PUBLISHER,
|
||||
useValue: mockMessagePublisher,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
repositoriesHealthIndicatorUseCase =
|
||||
module.get<RepositoriesHealthIndicatorUseCase>(
|
||||
RepositoriesHealthIndicatorUseCase,
|
||||
);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(repositoriesHealthIndicatorUseCase).toBeDefined();
|
||||
});
|
||||
|
||||
describe('execute', () => {
|
||||
it('should check health successfully', async () => {
|
||||
const healthIndicatorResult: HealthIndicatorResult =
|
||||
await repositoriesHealthIndicatorUseCase.isHealthy('repositories');
|
||||
expect(healthIndicatorResult['repositories'].status).toBe('up');
|
||||
});
|
||||
|
||||
it('should throw an error if database is unavailable', async () => {
|
||||
jest.spyOn(mockMessagePublisher, 'publish');
|
||||
await expect(
|
||||
repositoriesHealthIndicatorUseCase.isHealthy('repositories'),
|
||||
).rejects.toBeInstanceOf(HealthCheckError);
|
||||
expect(mockMessagePublisher.publish).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
export const MESSAGE_PUBLISHER = Symbol('MESSAGE_PUBLISHER');
|
|
@ -0,0 +1,36 @@
|
|||
import { Module, Provider } from '@nestjs/common';
|
||||
import { MESSAGE_PUBLISHER } from './messager.di-tokens';
|
||||
import {
|
||||
MessageBrokerModule,
|
||||
MessageBrokerModuleOptions,
|
||||
MessageBrokerPublisher,
|
||||
} from '@mobicoop/message-broker-module';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
|
||||
const imports = [
|
||||
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'),
|
||||
name: 'ad',
|
||||
}),
|
||||
}),
|
||||
];
|
||||
|
||||
const providers: Provider[] = [
|
||||
{
|
||||
provide: MESSAGE_PUBLISHER,
|
||||
useClass: MessageBrokerPublisher,
|
||||
},
|
||||
];
|
||||
|
||||
@Module({
|
||||
imports,
|
||||
providers,
|
||||
exports: [MESSAGE_PUBLISHER],
|
||||
})
|
||||
export class MessagerModule {}
|
Loading…
Reference in New Issue