basic match action without functional behaviour
This commit is contained in:
parent
afca685e3d
commit
3b0f4b8c49
|
@ -84,6 +84,12 @@
|
||||||
"json",
|
"json",
|
||||||
"ts"
|
"ts"
|
||||||
],
|
],
|
||||||
|
"modulePathIgnorePatterns": [
|
||||||
|
".controller.ts",
|
||||||
|
".module.ts",
|
||||||
|
".request.ts",
|
||||||
|
"main.ts"
|
||||||
|
],
|
||||||
"rootDir": "src",
|
"rootDir": "src",
|
||||||
"testRegex": ".*\\.spec\\.ts$",
|
"testRegex": ".*\\.spec\\.ts$",
|
||||||
"transform": {
|
"transform": {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { Module } from '@nestjs/common';
|
||||||
import { ConfigModule } from '@nestjs/config';
|
import { ConfigModule } from '@nestjs/config';
|
||||||
import { ConfigurationModule } from './modules/configuration/configuration.module';
|
import { ConfigurationModule } from './modules/configuration/configuration.module';
|
||||||
import { HealthModule } from './modules/health/health.module';
|
import { HealthModule } from './modules/health/health.module';
|
||||||
|
import { MatcherModule } from './modules/matcher/matcher.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -11,6 +12,7 @@ import { HealthModule } from './modules/health/health.module';
|
||||||
AutomapperModule.forRoot({ strategyInitializer: classes() }),
|
AutomapperModule.forRoot({ strategyInitializer: classes() }),
|
||||||
ConfigurationModule,
|
ConfigurationModule,
|
||||||
HealthModule,
|
HealthModule,
|
||||||
|
MatcherModule,
|
||||||
],
|
],
|
||||||
controllers: [],
|
controllers: [],
|
||||||
providers: [],
|
providers: [],
|
||||||
|
|
27
src/main.ts
27
src/main.ts
|
@ -1,6 +1,6 @@
|
||||||
import { NestFactory } from '@nestjs/core';
|
import { NestFactory } from '@nestjs/core';
|
||||||
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
|
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
|
||||||
// import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { AppModule } from './app.module';
|
import { AppModule } from './app.module';
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
|
@ -8,19 +8,18 @@ async function bootstrap() {
|
||||||
app.connectMicroservice<MicroserviceOptions>({
|
app.connectMicroservice<MicroserviceOptions>({
|
||||||
transport: Transport.TCP,
|
transport: Transport.TCP,
|
||||||
});
|
});
|
||||||
// app.connectMicroservice<MicroserviceOptions>({
|
app.connectMicroservice<MicroserviceOptions>({
|
||||||
// transport: Transport.GRPC,
|
transport: Transport.GRPC,
|
||||||
// options: {
|
options: {
|
||||||
// // package: ['matcher', 'health'],
|
package: ['matcher', 'health'],
|
||||||
// package: ['health'],
|
protoPath: [
|
||||||
// protoPath: [
|
join(__dirname, 'modules/matcher/adapters/primaries/matcher.proto'),
|
||||||
// // join(__dirname, 'modules/matcher/adapters/primaries/matcher.proto'),
|
join(__dirname, 'modules/health/adapters/primaries/health.proto'),
|
||||||
// join(__dirname, 'modules/health/adapters/primaries/health.proto'),
|
],
|
||||||
// ],
|
url: process.env.SERVICE_URL + ':' + process.env.SERVICE_PORT,
|
||||||
// url: process.env.SERVICE_URL + ':' + process.env.SERVICE_PORT,
|
loader: { keepCase: true },
|
||||||
// loader: { keepCase: true },
|
},
|
||||||
// },
|
});
|
||||||
// });
|
|
||||||
|
|
||||||
await app.startAllMicroservices();
|
await app.startAllMicroservices();
|
||||||
await app.listen(process.env.HEALTH_SERVICE_PORT);
|
await app.listen(process.env.HEALTH_SERVICE_PORT);
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
import { PrismaHealthIndicatorUseCase } from '../../domain/usecases/prisma.health-indicator.usecase';
|
import { PrismaHealthIndicatorUseCase } from '../../domain/usecases/prisma.health-indicator.usecase';
|
||||||
import { TerritoriesRepository } from '../../../territory/adapters/secondaries/territories.repository';
|
|
||||||
import { PrismaClientKnownRequestError } from '@prisma/client/runtime';
|
|
||||||
import { HealthCheckError, HealthIndicatorResult } from '@nestjs/terminus';
|
import { HealthCheckError, HealthIndicatorResult } from '@nestjs/terminus';
|
||||||
|
import { AdRepository } from '../../../matcher/adapters/secondaries/ad.repository';
|
||||||
|
import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library';
|
||||||
|
|
||||||
const mockTerritoriesRepository = {
|
const mockAdRepository = {
|
||||||
healthCheck: jest
|
healthCheck: jest
|
||||||
.fn()
|
.fn()
|
||||||
.mockImplementationOnce(() => {
|
.mockImplementationOnce(() => {
|
||||||
|
@ -25,8 +25,8 @@ describe('PrismaHealthIndicatorUseCase', () => {
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
provide: TerritoriesRepository,
|
provide: AdRepository,
|
||||||
useValue: mockTerritoriesRepository,
|
useValue: mockAdRepository,
|
||||||
},
|
},
|
||||||
PrismaHealthIndicatorUseCase,
|
PrismaHealthIndicatorUseCase,
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Mapper } from '@automapper/core';
|
import { Mapper } from '@automapper/core';
|
||||||
import { InjectMapper } from '@automapper/nestjs';
|
import { InjectMapper } from '@automapper/nestjs';
|
||||||
import { Controller, UsePipes } from '@nestjs/common';
|
import { Controller, UsePipes } from '@nestjs/common';
|
||||||
import { CommandBus, QueryBus } from '@nestjs/cqrs';
|
import { QueryBus } from '@nestjs/cqrs';
|
||||||
import { GrpcMethod } from '@nestjs/microservices';
|
import { GrpcMethod } from '@nestjs/microservices';
|
||||||
import { RpcValidationPipe } from 'src/modules/utils/pipes/rpc.validation-pipe';
|
import { RpcValidationPipe } from 'src/modules/utils/pipes/rpc.validation-pipe';
|
||||||
import { MatchRequest } from '../../domain/dtos/match.request';
|
import { MatchRequest } from '../../domain/dtos/match.request';
|
||||||
|
@ -19,7 +19,6 @@ import { MatchPresenter } from '../secondaries/match.presenter';
|
||||||
@Controller()
|
@Controller()
|
||||||
export class MatcherController {
|
export class MatcherController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _commandBus: CommandBus,
|
|
||||||
private readonly _queryBus: QueryBus,
|
private readonly _queryBus: QueryBus,
|
||||||
@InjectMapper() private readonly _mapper: Mapper,
|
@InjectMapper() private readonly _mapper: Mapper,
|
||||||
) {}
|
) {}
|
||||||
|
|
|
@ -7,7 +7,12 @@ service MatcherService {
|
||||||
}
|
}
|
||||||
|
|
||||||
message MatchRequest {
|
message MatchRequest {
|
||||||
string uuid = 1;
|
repeated Point waypoints = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Point {
|
||||||
|
float lon = 1;
|
||||||
|
float lat = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Match {
|
message Match {
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export abstract class IMessageBroker {
|
||||||
|
exchange: string;
|
||||||
|
|
||||||
|
constructor(exchange: string) {
|
||||||
|
this.exchange = exchange;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract publish(routingKey: string, message: string): void;
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { AmqpConnection } from '@golevelup/nestjs-rabbitmq';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { IMessageBroker } from './message-broker';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class Messager extends IMessageBroker {
|
||||||
|
constructor(
|
||||||
|
private readonly _amqpConnection: AmqpConnection,
|
||||||
|
configService: ConfigService,
|
||||||
|
) {
|
||||||
|
super(configService.get<string>('RMQ_EXCHANGE'));
|
||||||
|
}
|
||||||
|
|
||||||
|
publish(routingKey: string, message: string): void {
|
||||||
|
this._amqpConnection.publish(this.exchange, routingKey, message);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { Mapper } from '@automapper/core';
|
||||||
|
import { InjectMapper } from '@automapper/nestjs';
|
||||||
|
import { QueryHandler } from '@nestjs/cqrs';
|
||||||
|
import { Messager } from '../../adapters/secondaries/messager';
|
||||||
|
import { MatchQuery } from '../../queries/match.query';
|
||||||
|
import { AdRepository } from '../../adapters/secondaries/ad.repository';
|
||||||
|
import { Match } from '../entities/match';
|
||||||
|
import { ICollection } from 'src/modules/database/src/interfaces/collection.interface';
|
||||||
|
|
||||||
|
@QueryHandler(MatchQuery)
|
||||||
|
export class MatchUseCase {
|
||||||
|
constructor(
|
||||||
|
private readonly _repository: AdRepository,
|
||||||
|
private readonly _messager: Messager,
|
||||||
|
@InjectMapper() private readonly _mapper: Mapper,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async execute(matchQuery: MatchQuery): Promise<ICollection<Match>> {
|
||||||
|
try {
|
||||||
|
const match = new Match();
|
||||||
|
match.uuid = 'e23f9725-2c19-49a0-9ef6-17d8b9a5ec85';
|
||||||
|
this._messager.publish('matcher.match', 'match !');
|
||||||
|
return {
|
||||||
|
data: [match],
|
||||||
|
total: 1,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
this._messager.publish(
|
||||||
|
'logging.matcher.match.crit',
|
||||||
|
JSON.stringify({
|
||||||
|
matchQuery,
|
||||||
|
error,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq';
|
||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||||
|
import { CqrsModule } from '@nestjs/cqrs';
|
||||||
|
import { DatabaseModule } from '../database/database.module';
|
||||||
|
import { MatcherController } from './adapters/primaries/matcher.controller';
|
||||||
|
import { MatchProfile } from './mappers/match.profile';
|
||||||
|
import { AdRepository } from './adapters/secondaries/ad.repository';
|
||||||
|
import { MatchUseCase } from './domain/usecases/match.usecase';
|
||||||
|
import { Messager } from './adapters/secondaries/messager';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
DatabaseModule,
|
||||||
|
CqrsModule,
|
||||||
|
RabbitMQModule.forRootAsync(RabbitMQModule, {
|
||||||
|
imports: [ConfigModule],
|
||||||
|
useFactory: async (configService: ConfigService) => ({
|
||||||
|
exchanges: [
|
||||||
|
{
|
||||||
|
name: configService.get<string>('RMQ_EXCHANGE'),
|
||||||
|
type: 'topic',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
uri: configService.get<string>('RMQ_URI'),
|
||||||
|
connectionInitOptions: { wait: false },
|
||||||
|
}),
|
||||||
|
inject: [ConfigService],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
controllers: [MatcherController],
|
||||||
|
providers: [MatchProfile, AdRepository, Messager, MatchUseCase],
|
||||||
|
exports: [],
|
||||||
|
})
|
||||||
|
export class MatcherModule {}
|
|
@ -0,0 +1,59 @@
|
||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { Messager } from '../../adapters/secondaries/messager';
|
||||||
|
import { MatchUseCase } from '../../domain/usecases/match.usecase';
|
||||||
|
import { MatchRequest } from '../../domain/dtos/match.request';
|
||||||
|
import { MatchQuery } from '../../queries/match.query';
|
||||||
|
import { AdRepository } from '../../adapters/secondaries/ad.repository';
|
||||||
|
import { AutomapperModule } from '@automapper/nestjs';
|
||||||
|
import { classes } from '@automapper/classes';
|
||||||
|
|
||||||
|
const mockAdRepository = {};
|
||||||
|
|
||||||
|
const mockMessager = {
|
||||||
|
publish: jest.fn().mockImplementation(),
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('MatchUseCase', () => {
|
||||||
|
let matchUseCase: MatchUseCase;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
imports: [AutomapperModule.forRoot({ strategyInitializer: classes() })],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: AdRepository,
|
||||||
|
useValue: mockAdRepository,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: Messager,
|
||||||
|
useValue: mockMessager,
|
||||||
|
},
|
||||||
|
MatchUseCase,
|
||||||
|
],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
matchUseCase = module.get<MatchUseCase>(MatchUseCase);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(matchUseCase).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('execute', () => {
|
||||||
|
it('should return matches', async () => {
|
||||||
|
const matchRequest: MatchRequest = new MatchRequest();
|
||||||
|
matchRequest.waypoints = [
|
||||||
|
{
|
||||||
|
lon: 1.093912,
|
||||||
|
lat: 49.440041,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lat: 50.630992,
|
||||||
|
lon: 3.045432,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const matches = await matchUseCase.execute(new MatchQuery(matchRequest));
|
||||||
|
expect(matches.total).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { AmqpConnection } from '@golevelup/nestjs-rabbitmq';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { Messager } from '../../adapters/secondaries/messager';
|
||||||
|
|
||||||
|
const mockAmqpConnection = {
|
||||||
|
publish: jest.fn().mockImplementation(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockConfigService = {
|
||||||
|
get: jest.fn().mockResolvedValue({
|
||||||
|
RMQ_EXCHANGE: 'mobicoop',
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Messager', () => {
|
||||||
|
let messager: Messager;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
imports: [],
|
||||||
|
providers: [
|
||||||
|
Messager,
|
||||||
|
{
|
||||||
|
provide: AmqpConnection,
|
||||||
|
useValue: mockAmqpConnection,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: ConfigService,
|
||||||
|
useValue: mockConfigService,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
messager = module.get<Messager>(Messager);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(messager).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should publish a message', async () => {
|
||||||
|
jest.spyOn(mockAmqpConnection, 'publish');
|
||||||
|
messager.publish('test.create.info', 'my-test');
|
||||||
|
expect(mockAmqpConnection.publish).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { ArgumentMetadata } from '@nestjs/common';
|
||||||
|
import { RpcValidationPipe } from '../../pipes/rpc.validation-pipe';
|
||||||
|
import { MatchRequest } from '../../../matcher/domain/dtos/match.request';
|
||||||
|
|
||||||
|
describe('RpcValidationPipe', () => {
|
||||||
|
it('should not validate request', async () => {
|
||||||
|
const target: RpcValidationPipe = new RpcValidationPipe({
|
||||||
|
whitelist: true,
|
||||||
|
forbidUnknownValues: false,
|
||||||
|
});
|
||||||
|
const metadata: ArgumentMetadata = {
|
||||||
|
type: 'body',
|
||||||
|
metatype: MatchRequest,
|
||||||
|
data: '',
|
||||||
|
};
|
||||||
|
await target.transform(<MatchRequest>{}, metadata).catch((err) => {
|
||||||
|
expect(err.message).toEqual('Rpc Exception');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue