findAdByUuid, functional basic AdModule

This commit is contained in:
sbriat
2023-05-04 12:07:53 +02:00
parent 47e4abf594
commit 5fbc399924
23 changed files with 510 additions and 64 deletions

View File

@@ -3,10 +3,13 @@ import { ConfigModule } from '@nestjs/config';
import { ConfigurationModule } from './modules/configuration/configuration.module';
import { HealthModule } from './modules/health/health.module';
import { AdModule } from './modules/ad/ad.module';
import { AutomapperModule } from '@automapper/nestjs';
import { classes } from '@automapper/classes';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
AutomapperModule.forRoot({ strategyInitializer: classes() }),
ConfigurationModule,
HealthModule,
AdModule,

View File

@@ -11,10 +11,9 @@ async function bootstrap() {
app.connectMicroservice<MicroserviceOptions>({
transport: Transport.GRPC,
options: {
// package: ['ad', 'health'],
package: ['health'],
package: ['ad', 'health'],
protoPath: [
// join(__dirname, 'modules/ad/adapters/primaries/ad.proto'),
join(__dirname, 'modules/ad/adapters/primaries/ad.proto'),
join(__dirname, 'modules/health/adapters/primaries/health.proto'),
],
url: process.env.SERVICE_URL + ':' + process.env.SERVICE_PORT,

View File

@@ -1,8 +1,34 @@
import { Module } from '@nestjs/common';
import { AdController } from './adapters/primaries/ad.controller';
import { DatabaseModule } from '../database/database.module';
import { CqrsModule } from '@nestjs/cqrs';
import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { AdProfile } from './mappers/ad.profile';
import { AdsRepository } from './adapters/secondaries/ads.repository';
import { Messager } from './adapters/secondaries/messager';
import { FindAdByUuidUseCase } from './domain/usecases/find-ad-by-uuid.usecase';
@Module({
imports: [],
controllers: [],
providers: [],
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: [AdController],
providers: [AdProfile, AdsRepository, Messager, FindAdByUuidUseCase],
})
export class AdModule {}

View File

@@ -0,0 +1,38 @@
import { Mapper } from '@automapper/core';
import { InjectMapper } from '@automapper/nestjs';
import { Controller, UsePipes } from '@nestjs/common';
import { QueryBus } from '@nestjs/cqrs';
import { GrpcMethod, RpcException } from '@nestjs/microservices';
import { RpcValidationPipe } from '../../../../utils/pipes/rpc.validation-pipe';
import { FindAdByUuidRequest } from '../../domain/dtos/find-ad-by-uuid.request';
import { AdPresenter } from './ad.presenter';
import { FindAdByUuidQuery } from '../../queries/find-ad-by-uuid.query';
import { Ad } from '../../domain/entities/ad';
@UsePipes(
new RpcValidationPipe({
whitelist: false,
forbidUnknownValues: false,
}),
)
@Controller()
export class AdController {
constructor(
private readonly queryBus: QueryBus,
@InjectMapper() private readonly _mapper: Mapper,
) {}
@GrpcMethod('AdsService', 'FindOneByUuid')
async findOnebyUuid(data: FindAdByUuidRequest): Promise<AdPresenter> {
try {
console.log('ici');
const ad = await this.queryBus.execute(new FindAdByUuidQuery(data));
return this._mapper.map(ad, Ad, AdPresenter);
} catch (e) {
throw new RpcException({
code: e.code,
message: e.message,
});
}
}
}

View File

@@ -0,0 +1,9 @@
import { AutoMap } from '@automapper/classes';
export class AdPresenter {
@AutoMap()
uuid: string;
@AutoMap()
driver: boolean;
}

View File

@@ -0,0 +1,86 @@
syntax = "proto3";
package ad;
service AdsService {
rpc FindOneByUuid(AdByUuid) returns (Ad);
rpc FindAll(AdFilter) returns (Ads);
rpc Create(Ad) returns (Ad);
rpc Update(Ad) returns (Ad);
rpc Delete(AdByUuid) returns (Empty);
}
message AdByUuid {
string uuid = 1;
}
message Ad {
string uuid = 1;
string userUuid = 2;
bool driver = 3;
bool passenger = 4;
int32 frequency = 5;
string fromDate = 6;
string toDate = 7;
Schedule schedule = 8;
MarginDurations marginDurations = 9;
int32 seatsPassenger = 10;
int32 seatsDriver = 11;
bool strict = 12;
Addresses addresses = 13;
}
message Schedule {
string mon = 1;
string tue = 2;
string wed = 3;
string thu = 4;
string fri = 5;
string sat = 6;
string sun = 7;
}
message MarginDurations {
int32 mon = 1;
int32 tue = 2;
int32 wed = 3;
int32 thu = 4;
int32 fri = 5;
int32 sat = 6;
int32 sun = 7;
}
message Addresses {
repeated Address address = 1;
}
message Address {
float lon = 1;
float lat = 2;
string houseNumber = 3;
string street = 4;
string locality = 5;
string postalCode = 6;
string country = 7;
AddressType type = 8;
}
enum AddressType {
HOUSE_NUMBER = 1;
STREET_ADDRESS = 2;
LOCALITY = 3;
VENUE = 4;
OTHER = 5;
}
message AdFilter {
optional int32 page = 1;
optional int32 perPage = 2;
}
message Ads {
repeated Ad data = 1;
int32 total = 2;
}
message Empty {}

View File

@@ -0,0 +1,18 @@
import { AmqpConnection } from '@golevelup/nestjs-rabbitmq';
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { IMessageBroker } from '../../domain/interfaces/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);
}
}

View File

@@ -0,0 +1,7 @@
import { IsNotEmpty, IsString } from 'class-validator';
export class FindAdByUuidRequest {
@IsString()
@IsNotEmpty()
uuid: string;
}

View File

@@ -3,4 +3,7 @@ import { AutoMap } from '@automapper/classes';
export class Ad {
@AutoMap()
uuid: string;
@AutoMap()
driver: boolean;
}

View File

@@ -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;
}

View File

@@ -0,0 +1,31 @@
import { NotFoundException } from '@nestjs/common';
import { QueryHandler } from '@nestjs/cqrs';
import { FindAdByUuidQuery } from '../../queries/find-ad-by-uuid.query';
import { AdsRepository } from '../../adapters/secondaries/ads.repository';
import { Messager } from '../../adapters/secondaries/messager';
import { Ad } from '../entities/ad';
@QueryHandler(FindAdByUuidQuery)
export class FindAdByUuidUseCase {
constructor(
private readonly repository: AdsRepository,
private readonly messager: Messager,
) {}
async execute(findAdByUuid: FindAdByUuidQuery): Promise<Ad> {
try {
const ad = await this.repository.findOneByUuid(findAdByUuid.uuid);
if (!ad) throw new NotFoundException();
return ad;
} catch (error) {
this.messager.publish(
'logging.ad.read.warning',
JSON.stringify({
query: findAdByUuid,
error,
}),
);
throw error;
}
}
}

View File

@@ -0,0 +1,18 @@
import { createMap, Mapper } from '@automapper/core';
import { AutomapperProfile, InjectMapper } from '@automapper/nestjs';
import { Injectable } from '@nestjs/common';
import { Ad } from '../domain/entities/ad';
import { AdPresenter } from '../adapters/primaries/ad.presenter';
@Injectable()
export class AdProfile extends AutomapperProfile {
constructor(@InjectMapper() mapper: Mapper) {
super(mapper);
}
override get profile() {
return (mapper) => {
createMap(mapper, Ad, AdPresenter);
};
}
}

View File

@@ -0,0 +1,9 @@
import { FindAdByUuidRequest } from '../domain/dtos/find-ad-by-uuid.request';
export class FindAdByUuidQuery {
readonly uuid: string;
constructor(findAdByUuidRequest: FindAdByUuidRequest) {
this.uuid = findAdByUuidRequest.uuid;
}
}