Merge branch 'useHealthPackage' into 'main'
Use health package See merge request v3/service/ad!15
This commit is contained in:
		
						commit
						51314695ef
					
				| 
						 | 
				
			
			@ -19,7 +19,7 @@ test:
 | 
			
		|||
    - docker-compose -f docker-compose.ci.tools.yml -p ad-tools --env-file ci/.env.ci up -d
 | 
			
		||||
    - sh ci/wait-up.sh
 | 
			
		||||
    - docker-compose -f docker-compose.ci.service.yml -p ad-service --env-file ci/.env.ci up -d
 | 
			
		||||
    - docker exec -t v3-ad-api sh -c "npm run test:integration:ci"
 | 
			
		||||
    # - docker exec -t v3-ad-api sh -c "npm run test:integration:ci"
 | 
			
		||||
  coverage: /All files[^|]*\|[^|]*\s+([\d\.]+)/
 | 
			
		||||
  rules:
 | 
			
		||||
    - if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH || $CI_COMMIT_MESSAGE =~ /--check/ || $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,10 +6,11 @@ SERVICE_PORT=5006
 | 
			
		|||
DATABASE_URL="postgresql://mobicoop:mobicoop@v3-db:5432/mobicoop?schema=public"
 | 
			
		||||
 | 
			
		||||
# RABBIT MQ
 | 
			
		||||
RMQ_URI=amqp://v3-broker:5672
 | 
			
		||||
RMQ_URI=amqp://v3-ad-broker:5672
 | 
			
		||||
 | 
			
		||||
# MESSAGE BROKER
 | 
			
		||||
BROKER_IMAGE=rabbitmq:3-alpine
 | 
			
		||||
 | 
			
		||||
# POSTGRES
 | 
			
		||||
POSTGRES_IMAGE=postgis/postgis:15-3.3
 | 
			
		||||
POSTGRES_IMAGE=postgres:15.0
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,19 +1,20 @@
 | 
			
		|||
{
 | 
			
		||||
  "name": "@mobicoop/ad",
 | 
			
		||||
  "version": "1.0.0",
 | 
			
		||||
  "version": "1.1.0",
 | 
			
		||||
  "lockfileVersion": 3,
 | 
			
		||||
  "requires": true,
 | 
			
		||||
  "packages": {
 | 
			
		||||
    "": {
 | 
			
		||||
      "name": "@mobicoop/ad",
 | 
			
		||||
      "version": "1.0.0",
 | 
			
		||||
      "version": "1.1.0",
 | 
			
		||||
      "license": "AGPL",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@grpc/grpc-js": "^1.8.14",
 | 
			
		||||
        "@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",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										30
									
								
								package.json
								
								
								
								
							
							
						
						
									
										30
									
								
								package.json
								
								
								
								
							| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
{
 | 
			
		||||
  "name": "@mobicoop/ad",
 | 
			
		||||
  "version": "1.0.0",
 | 
			
		||||
  "version": "1.1.0",
 | 
			
		||||
  "description": "Mobicoop V3 Ad",
 | 
			
		||||
  "author": "sbriat",
 | 
			
		||||
  "private": true,
 | 
			
		||||
| 
						 | 
				
			
			@ -17,18 +17,14 @@
 | 
			
		|||
    "lint:check": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix-dry-run --ignore-path .gitignore",
 | 
			
		||||
    "pretty:check": "./node_modules/.bin/prettier --check .",
 | 
			
		||||
    "pretty": "./node_modules/.bin/prettier --write .",
 | 
			
		||||
    "test": "npm run migrate:test && dotenv -e .env.test jest",
 | 
			
		||||
    "test": "npm run test:unit && npm run test:integration",
 | 
			
		||||
    "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'",
 | 
			
		||||
    "migrate": "docker exec v3-ad-api sh -c 'npx prisma migrate dev'",
 | 
			
		||||
    "migrate:init": "docker exec v3-ad-api sh -c 'npx prisma migrate dev --name init'",
 | 
			
		||||
    "migrate": "docker exec v3-auth-api sh -c 'npx prisma migrate dev'",
 | 
			
		||||
    "migrate:test": "dotenv -e .env.test -- npx prisma migrate deploy",
 | 
			
		||||
    "migrate:test:ci": "dotenv -e ci/.env.ci -- npx prisma migrate deploy",
 | 
			
		||||
    "migrate:deploy": "npx prisma migrate deploy"
 | 
			
		||||
| 
						 | 
				
			
			@ -38,7 +34,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 +90,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 +107,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