Merge branch 'updatePackages' into 'main'

Update packages

See merge request v3/service/matcher!13
This commit is contained in:
Sylvain Briat 2023-10-18 08:36:22 +00:00
commit 0fed45c8b0
17 changed files with 3244 additions and 1874 deletions

View File

@ -10,6 +10,7 @@ DATABASE_URL="postgresql://mobicoop:mobicoop@v3-db:5432/mobicoop?schema=matcher"
# MESSAGE BROKER
MESSAGE_BROKER_URI=amqp://v3-broker:5672
MESSAGE_BROKER_EXCHANGE=mobicoop
MESSAGE_BROKER_EXCHANGE_DURABILITY=true
# REDIS
REDIS_HOST=v3-redis

4869
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "@mobicoop/matcher",
"version": "1.1.0",
"version": "1.2.0",
"description": "Mobicoop V3 Matcher",
"author": "sbriat",
"private": true,
@ -30,63 +30,63 @@
"migrate:deploy": "npx prisma migrate deploy"
},
"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": "^1.5.0",
"@mobicoop/health-module": "^2.0.0",
"@mobicoop/message-broker-module": "^1.2.0",
"@nestjs/axios": "^2.0.0",
"@nestjs/cache-manager": "^1.0.0",
"@nestjs/common": "^9.0.0",
"@nestjs/config": "^2.3.1",
"@nestjs/core": "^9.0.0",
"@nestjs/cqrs": "^9.0.3",
"@nestjs/event-emitter": "^1.4.2",
"@nestjs/microservices": "^9.4.0",
"@nestjs/platform-express": "^9.0.0",
"@nestjs/terminus": "^9.2.2",
"@prisma/client": "^4.13.0",
"axios": "^1.3.5",
"cache-manager": "^5.2.3",
"cache-manager-ioredis-yet": "^1.1.0",
"@grpc/grpc-js": "^1.9.6",
"@grpc/proto-loader": "^0.7.10",
"@songkeys/nestjs-redis": "^10.0.0",
"@mobicoop/configuration-module": "^3.0.0",
"@mobicoop/ddd-library": "^2.0.0",
"@mobicoop/health-module": "^2.3.1",
"@mobicoop/message-broker-module": "^2.1.1",
"@nestjs/axios": "^3.0.0",
"@nestjs/cache-manager": "^2.1.0",
"@nestjs/common": "^10.2.7",
"@nestjs/config": "^3.1.1",
"@nestjs/core": "^10.2.7",
"@nestjs/cqrs": "^10.2.6",
"@nestjs/event-emitter": "^2.0.2",
"@nestjs/microservices": "^10.2.7",
"@nestjs/platform-express": "^10.2.7",
"@nestjs/terminus": "^10.1.1",
"@prisma/client": "^5.4.2",
"axios": "^1.5.1",
"cache-manager": "^5.2.4",
"cache-manager-ioredis-yet": "^1.2.2",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"geo-tz": "^7.0.7",
"geographiclib-geodesic": "^2.0.0",
"got": "^11.8.6",
"got": "^13.0.0",
"ioredis": "^5.3.2",
"nestjs-request-context": "^2.1.0",
"nestjs-request-context": "^3.0.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0",
"rxjs": "^7.8.1",
"timezonecomplete": "^5.12.4"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
"@types/express": "^4.17.13",
"@types/jest": "29.5.0",
"@types/node": "18.15.11",
"@types/supertest": "^2.0.11",
"@types/uuid": "^9.0.2",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"dotenv-cli": "^7.2.1",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "29.5.0",
"prettier": "^2.3.2",
"prisma": "^4.13.0",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "29.0.5",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"@nestjs/cli": "^10.1.18",
"@nestjs/schematics": "^10.0.2",
"@nestjs/testing": "^10.2.7",
"@types/express": "^4.17.20",
"@types/jest": "29.5.6",
"@types/node": "20.8.6",
"@types/supertest": "^2.0.14",
"@types/uuid": "^9.0.5",
"@typescript-eslint/eslint-plugin": "^6.8.0",
"@typescript-eslint/parser": "^6.8.0",
"dotenv-cli": "^7.3.0",
"eslint": "^8.51.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.1",
"jest": "29.7.0",
"prettier": "^3.0.3",
"prisma": "^5.4.2",
"source-map-support": "^0.5.21",
"supertest": "^6.3.3",
"ts-jest": "29.1.1",
"ts-loader": "^9.5.0",
"ts-node": "^10.9.1",
"tsconfig-paths": "4.2.0",
"typescript": "^4.7.4"
"typescript": "^5.2.2"
},
"jest": {
"moduleFileExtensions": [

28
src/app.constants.ts Normal file
View File

@ -0,0 +1,28 @@
// service
export const SERVICE_NAME = 'matcher';
// grpc
export const GRPC_PACKAGE_NAME = 'matcher';
// messaging
export const AD_CREATED_MESSAGE_HANDLER = 'adCreated';
export const AD_CREATED_ROUTING_KEY = 'ad.created';
export const AD_CREATED_QUEUE = 'matcher-ad-created';
export const AD_UPDATED_MESSAGE_HANDLER = 'adUpdated';
export const AD_UPDATED_ROUTING_KEY = 'ad.updated';
export const AD_UPDATED_QUEUE = 'matcher-ad-updated';
export const AD_DELETED_MESSAGE_HANDLER = 'adDeleted';
export const AD_DELETED_ROUTING_KEY = 'ad.deleted';
export const AD_DELETED_QUEUE = 'matcher-ad-deleted';
// configuration
export const SERVICE_CONFIGURATION_SET_QUEUE = 'matcher-configuration-set';
export const SERVICE_CONFIGURATION_DELETE_QUEUE =
'matcher-configuration-delete';
export const SERVICE_CONFIGURATION_PROPAGATE_QUEUE =
'matcher-configuration-propagate';
// health
export const GRPC_HEALTH_PACKAGE_NAME = 'health';
export const HEALTH_AD_REPOSITORY = 'AdRepository';
export const HEALTH_CRITICAL_LOGGING_KEY = 'logging.matcher.health.crit';

View File

@ -14,6 +14,14 @@ import { MESSAGE_PUBLISHER } from '@modules/messager/messager.di-tokens';
import { HealthModuleOptions } from '@mobicoop/health-module/dist/core/domain/types/health.types';
import { MessagePublisherPort } from '@mobicoop/ddd-library';
import { GeographyModule } from '@modules/geography/geography.module';
import {
HEALTH_AD_REPOSITORY,
HEALTH_CRITICAL_LOGGING_KEY,
SERVICE_CONFIGURATION_DELETE_QUEUE,
SERVICE_CONFIGURATION_PROPAGATE_QUEUE,
SERVICE_CONFIGURATION_SET_QUEUE,
SERVICE_NAME,
} from './app.constants';
@Module({
imports: [
@ -31,18 +39,23 @@ import { GeographyModule } from '@modules/geography/geography.module';
) as string,
messageBroker: {
uri: configService.get<string>('MESSAGE_BROKER_URI') as string,
exchange: configService.get<string>(
'MESSAGE_BROKER_EXCHANGE',
) as string,
exchange: {
name: configService.get<string>(
'MESSAGE_BROKER_EXCHANGE',
) as string,
durable: configService.get<boolean>(
'MESSAGE_BROKER_EXCHANGE_DURABILITY',
) as boolean,
},
},
redis: {
host: configService.get<string>('REDIS_HOST') as string,
password: configService.get<string>('REDIS_PASSWORD'),
port: configService.get<number>('REDIS_PORT') as number,
},
setConfigurationBrokerQueue: 'matcher-configuration-create-update',
deleteConfigurationQueue: 'matcher-configuration-delete',
propagateConfigurationQueue: 'matcher-configuration-propagate',
setConfigurationQueue: SERVICE_CONFIGURATION_SET_QUEUE,
deleteConfigurationQueue: SERVICE_CONFIGURATION_DELETE_QUEUE,
propagateConfigurationQueue: SERVICE_CONFIGURATION_PROPAGATE_QUEUE,
}),
}),
HealthModule.forRootAsync({
@ -52,11 +65,11 @@ import { GeographyModule } from '@modules/geography/geography.module';
adRepository: HealthRepositoryPort,
messagePublisher: MessagePublisherPort,
): Promise<HealthModuleOptions> => ({
serviceName: 'matcher',
criticalLoggingKey: 'logging.matcher.health.crit',
serviceName: SERVICE_NAME,
criticalLoggingKey: HEALTH_CRITICAL_LOGGING_KEY,
checkRepositories: [
{
name: 'AdRepository',
name: HEALTH_AD_REPOSITORY,
repository: adRepository,
},
],

View File

@ -2,6 +2,7 @@ import { NestFactory } from '@nestjs/core';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
import { join } from 'path';
import { AppModule } from './app.module';
import { GRPC_HEALTH_PACKAGE_NAME, GRPC_PACKAGE_NAME } from './app.constants';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
@ -11,12 +12,12 @@ async function bootstrap() {
app.connectMicroservice<MicroserviceOptions>({
transport: Transport.GRPC,
options: {
package: ['matcher', 'health'],
package: [GRPC_PACKAGE_NAME, GRPC_HEALTH_PACKAGE_NAME],
protoPath: [
join(__dirname, 'modules/ad/interface/grpc-controllers/matcher.proto'),
join(__dirname, 'health.proto'),
],
url: process.env.SERVICE_URL + ':' + process.env.SERVICE_PORT,
url: `${process.env.SERVICE_URL}:${process.env.SERVICE_PORT}`,
loader: { keepCase: true, enums: String },
},
});

View File

@ -36,9 +36,9 @@ import { OutputDateTimeTransformer } from './infrastructure/output-datetime-tran
import { MatchingRepository } from './infrastructure/matching.repository';
import { MatchingMapper } from './matching.mapper';
import { CacheModule } from '@nestjs/cache-manager';
import { RedisClientOptions } from '@liaoliaots/nestjs-redis';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { redisStore } from 'cache-manager-ioredis-yet';
import { RedisClientOptions } from '@songkeys/nestjs-redis';
const imports = [
CqrsModule,

View File

@ -8,6 +8,7 @@ import { AdEntity } from '../core/domain/ad.entity';
import { AdMapper } from '../ad.mapper';
import { ExtendedPrismaRepositoryBase } from '@mobicoop/ddd-library/dist/db/prisma-repository.base';
import { Frequency } from '../core/domain/ad.types';
import { SERVICE_NAME } from '@src/app.constants';
export type AdModel = {
uuid: string;
@ -38,7 +39,7 @@ export type AdReadModel = AdModel & {
};
/**
* The record ready to be sent to the peristence system
* The record ready to be sent to the persistence system
*/
export type AdWriteModel = AdModel & {
schedule: {
@ -103,7 +104,7 @@ export class AdRepository
eventEmitter,
new LoggerBase({
logger: new Logger(AdRepository.name),
domain: 'matcher',
domain: SERVICE_NAME,
messagePublisher,
}),
);

View File

@ -1,10 +1,10 @@
import { InjectRedis } from '@liaoliaots/nestjs-redis';
import { MatchingRepositoryPort } from '../core/application/ports/matching.repository.port';
import { MatchingEntity } from '../core/domain/matching.entity';
import { Redis } from 'ioredis';
import { MatchingMapper } from '../matching.mapper';
import { ConfigService } from '@nestjs/config';
import { MatchingNotFoundException } from '../core/domain/matching.errors';
import { InjectRedis } from '@songkeys/nestjs-redis';
const REDIS_MATCHING_TTL = 900;
const REDIS_MATCHING_KEY = 'MATCHER:MATCHING';

View File

@ -1,4 +1,4 @@
import { INestApplication, Injectable, OnModuleInit } from '@nestjs/common';
import { Injectable, OnModuleInit } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
@Injectable()
@ -6,10 +6,4 @@ export class PrismaService extends PrismaClient implements OnModuleInit {
async onModuleInit() {
await this.$connect();
}
async enableShutdownHooks(app: INestApplication) {
this.$on('beforeExit', async () => {
await app.close();
});
}
}

View File

@ -3,13 +3,14 @@ import { RabbitSubscribe } from '@mobicoop/message-broker-module';
import { CommandBus } from '@nestjs/cqrs';
import { CreateAdCommand } from '@modules/ad/core/application/commands/create-ad/create-ad.command';
import { Ad } from './ad.types';
import { AD_CREATED_MESSAGE_HANDLER } from '@src/app.constants';
@Injectable()
export class AdCreatedMessageHandler {
constructor(private readonly commandBus: CommandBus) {}
@RabbitSubscribe({
name: 'adCreated',
name: AD_CREATED_MESSAGE_HANDLER,
})
public async adCreated(message: string) {
try {

View File

@ -137,9 +137,8 @@ describe('create-ad.service', () => {
AdEntity.create = jest.fn().mockReturnValue({
id: '047a6ecf-23d4-4d3e-877c-3225d560a8da',
});
const result: AggregateID = await createAdService.execute(
createAdCommand,
);
const result: AggregateID =
await createAdService.execute(createAdCommand);
expect(result).toBe('047a6ecf-23d4-4d3e-877c-3225d560a8da');
});
it('should create a new ad as passenger', async () => {

View File

@ -332,9 +332,8 @@ describe('Match Query Handler', () => {
},
mockRouteProvider,
);
const matching: MatchingResult = await matchQueryHandler.execute(
matchQuery,
);
const matching: MatchingResult =
await matchQueryHandler.execute(matchQuery);
expect(matching.id).toHaveLength(36);
expect(MatchingEntity.create).toHaveBeenCalledTimes(1);
});
@ -362,9 +361,8 @@ describe('Match Query Handler', () => {
},
mockRouteProvider,
);
const matching: MatchingResult = await matchQueryHandler.execute(
matchQuery,
);
const matching: MatchingResult =
await matchQueryHandler.execute(matchQuery);
expect(matching.id).toBe('a3b10efb-121e-4d08-9198-9f57afdb5e2d');
expect(MatchingEntity.create).toHaveBeenCalledTimes(0);
});
@ -392,9 +390,8 @@ describe('Match Query Handler', () => {
},
mockRouteProvider,
);
const matching: MatchingResult = await matchQueryHandler.execute(
matchQuery,
);
const matching: MatchingResult =
await matchQueryHandler.execute(matchQuery);
expect(matching.id).toHaveLength(36);
expect(MatchingEntity.create).toHaveBeenCalledTimes(1);
});

View File

@ -274,9 +274,8 @@ describe('Ad repository', () => {
});
it('should return an empty array of candidates if query does not return Ads', async () => {
const candidates: AdEntity[] = await adRepository.getCandidateAds(
'someQueryString',
);
const candidates: AdEntity[] =
await adRepository.getCandidateAds('someQueryString');
expect(candidates.length).toBe(0);
});
});

View File

@ -1,4 +1,3 @@
import { getRedisToken } from '@liaoliaots/nestjs-redis';
import { Frequency, Role } from '@modules/ad/core/domain/ad.types';
import { Target } from '@modules/ad/core/domain/candidate.types';
import { MatchEntity } from '@modules/ad/core/domain/match.entity';
@ -8,6 +7,7 @@ import { MatchingRepository } from '@modules/ad/infrastructure/matching.reposito
import { MatchingMapper } from '@modules/ad/matching.mapper';
import { ConfigService } from '@nestjs/config';
import { Test, TestingModule } from '@nestjs/testing';
import { getRedisToken } from '@songkeys/nestjs-redis';
const mockConfigService = {
get: jest.fn().mockImplementation((value: string) => {

View File

@ -6,6 +6,18 @@ import {
MessageBrokerPublisher,
} from '@mobicoop/message-broker-module';
import { ConfigModule, ConfigService } from '@nestjs/config';
import {
AD_CREATED_MESSAGE_HANDLER,
AD_CREATED_QUEUE,
AD_CREATED_ROUTING_KEY,
AD_DELETED_MESSAGE_HANDLER,
AD_DELETED_QUEUE,
AD_DELETED_ROUTING_KEY,
AD_UPDATED_MESSAGE_HANDLER,
AD_UPDATED_QUEUE,
AD_UPDATED_ROUTING_KEY,
SERVICE_NAME,
} from '@src/app.constants';
const imports = [
MessageBrokerModule.forRootAsync({
@ -15,20 +27,25 @@ const imports = [
configService: ConfigService,
): Promise<MessageBrokerModuleOptions> => ({
uri: configService.get<string>('MESSAGE_BROKER_URI') as string,
exchange: configService.get<string>('MESSAGE_BROKER_EXCHANGE') as string,
name: 'matcher',
exchange: {
name: configService.get<string>('MESSAGE_BROKER_EXCHANGE') as string,
durable: configService.get<boolean>(
'MESSAGE_BROKER_EXCHANGE_DURABILITY',
) as boolean,
},
name: SERVICE_NAME,
handlers: {
adCreated: {
routingKey: 'ad.created',
queue: 'matcher-ad-created',
[AD_CREATED_MESSAGE_HANDLER]: {
routingKey: AD_CREATED_ROUTING_KEY,
queue: AD_CREATED_QUEUE,
},
adUpdated: {
routingKey: 'ad.updated',
queue: 'matcher-ad-updated',
[AD_UPDATED_MESSAGE_HANDLER]: {
routingKey: AD_UPDATED_ROUTING_KEY,
queue: AD_UPDATED_QUEUE,
},
adDeleted: {
routingKey: 'ad.deleted',
queue: 'matcher-ad-deleted',
[AD_DELETED_MESSAGE_HANDLER]: {
routingKey: AD_DELETED_ROUTING_KEY,
queue: AD_DELETED_QUEUE,
},
},
}),

View File

@ -15,7 +15,7 @@
"strictNullChecks": true,
"noImplicitAny": true,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": false,
"paths": {
"@libs/*": ["src/libs/*"],