This commit is contained in:
sbriat
2023-04-28 15:53:57 +02:00
parent 95310651d8
commit 1f9a9896e9
15 changed files with 129 additions and 65 deletions

View File

@@ -7,6 +7,9 @@ import { CreateAdUseCase } from './domain/usecases/create-ad.usecase';
import { AdRepository } from './adapters/secondaries/ad.repository';
import { DatabaseModule } from '../database/database.module';
import { CqrsModule } from '@nestjs/cqrs';
import { Messager } from './adapters/secondaries/messager';
import { TimezoneFinder } from './adapters/secondaries/timezone-finder';
import { GeoTimezoneFinder } from '../geography/adapters/secondaries/geo-timezone-finder';
@Module({
imports: [
@@ -36,7 +39,14 @@ import { CqrsModule } from '@nestjs/cqrs';
}),
],
controllers: [AdMessagerController],
providers: [AdProfile, AdRepository, CreateAdUseCase],
providers: [
AdProfile,
Messager,
AdRepository,
TimezoneFinder,
GeoTimezoneFinder,
CreateAdUseCase,
],
exports: [],
})
export class AdModule {}

View File

@@ -6,51 +6,59 @@ import { Mapper } from '@automapper/core';
import { CommandBus } from '@nestjs/cqrs';
import { CreateAdCommand } from '../../commands/create-ad.command';
import { CreateAdRequest } from '../../domain/dtos/create-ad.request';
import { ValidationError, validateOrReject } from 'class-validator';
import { validateOrReject } from 'class-validator';
import { Messager } from '../secondaries/messager';
import { GeoTimezoneFinder } from 'src/modules/geography/adapters/secondaries/geo-timezone-finder';
@Controller()
export class AdMessagerController {
constructor(
private readonly messager: Messager,
private readonly commandBus: CommandBus,
@InjectMapper() private readonly mapper: Mapper,
private readonly timezoneFinder: GeoTimezoneFinder,
) {}
@RabbitSubscribe({
name: 'adCreated',
})
async adCreatedHandler(message: string): Promise<void> {
let createAdRequest: CreateAdRequest;
try {
// parse message to conform to CreateAdRequest (not a real instance yet)
const parsedMessage: CreateAdRequest = JSON.parse(message);
console.log(parsedMessage);
// create a real instance of CreateAdRequest from parsed message
// const createAdRequest: CreateAdRequest = this.mapper.map(
// parsedMessage,
// CreateAdRequest,
// CreateAdRequest,
// );
const createAdRequest = new CreateAdRequest();
createAdRequest.originType = parsedMessage.originType;
createAdRequest.destinationType = parsedMessage.destinationType;
createAdRequest.waypoints = parsedMessage.waypoints.map((waypoint) => ({
lon: waypoint.lon,
lat: waypoint.lat,
}));
console.log(createAdRequest);
createAdRequest = this.mapper.map(
parsedMessage,
CreateAdRequest,
CreateAdRequest,
);
// validate instance
await validateOrReject(createAdRequest.waypoints[0]);
await validateOrReject(createAdRequest);
// validate nested objects (fixes direct nested validation bug)
for (const waypoint of createAdRequest.waypoints) {
try {
await validateOrReject(waypoint);
} catch (e) {
throw e;
}
}
createAdRequest.timezone = this.timezoneFinder.timezones(
createAdRequest.waypoints[0].lon,
createAdRequest.waypoints[0].lat,
)[0];
const ad: Ad = await this.commandBus.execute(
new CreateAdCommand(createAdRequest),
);
console.log(ad);
} catch (e) {
if (Array.isArray(e)) {
e.forEach((error) =>
error instanceof ValidationError
? console.log(error.constraints)
: console.log(error),
);
}
this.messager.publish(
'logging.matcher.ad.crit',
JSON.stringify({
createAdRequest,
error: e,
}),
);
}
}
}

View File

@@ -0,0 +1,12 @@
import { Injectable } from '@nestjs/common';
@Injectable()
export abstract class MessageBroker {
exchange: string;
constructor(exchange: string) {
this.exchange = exchange;
}
abstract publish(routingKey: string, message: string): void;
}

View File

@@ -0,0 +1,18 @@
import { AmqpConnection } from '@golevelup/nestjs-rabbitmq';
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { MessageBroker } from './message-broker';
@Injectable()
export class Messager extends MessageBroker {
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,11 @@
import { Injectable } from '@nestjs/common';
import { GeoTimezoneFinder } from '../../../geography/adapters/secondaries/geo-timezone-finder';
import { IFindTimezone } from '../../../geography/domain/interfaces/timezone-finder.interface';
@Injectable()
export class TimezoneFinder implements IFindTimezone {
constructor(private readonly geoTimezoneFinder: GeoTimezoneFinder) {}
timezones = (lon: number, lat: number): string[] =>
this.geoTimezoneFinder.timezones(lon, lat);
}

View File

@@ -8,12 +8,10 @@ import {
IsNumber,
IsOptional,
IsString,
ValidateNested,
} from 'class-validator';
import { PointType } from '../../../geography/domain/types/point-type.enum';
import { Frequency } from '../types/frequency.enum';
import { Coordinates } from '../../../geography/domain/types/coordinates.type';
import { Type } from 'class-transformer';
import { Coordinates } from '../../../geography/domain/entities/coordinates';
export class CreateAdRequest {
@IsString()
@@ -114,9 +112,7 @@ export class CreateAdRequest {
destinationType: PointType;
@IsArray()
@ValidateNested({ each: true })
@ArrayMinSize(2)
@Type(() => Coordinates)
@AutoMap(() => [Coordinates])
waypoints: Coordinates[];
@@ -140,4 +136,6 @@ export class CreateAdRequest {
@IsString()
@AutoMap()
updatedAt: string;
timezone: string;
}

View File

@@ -1,6 +1,6 @@
import { AutoMap } from '@automapper/classes';
import { PointType } from '../../../geography/domain/types/point-type.enum';
import { Coordinates } from '../../../geography/domain/types/coordinates.type';
import { Coordinates } from '../../../geography/domain/entities/coordinates';
export class Ad {
@AutoMap()
@@ -22,7 +22,7 @@ export class Ad {
toDate: Date;
@AutoMap()
monTime: string;
monTime: Date;
@AutoMap()
tueTime: string;

View File

@@ -4,7 +4,8 @@ import { Injectable } from '@nestjs/common';
import { Ad } from '../domain/entities/ad';
import { AdPresenter } from '../adapters/primaries/ad.presenter';
import { CreateAdRequest } from '../domain/dtos/create-ad.request';
import { Coordinates } from 'src/modules/geography/domain/types/coordinates.type';
import { Coordinates } from '../../geography/domain/entities/coordinates';
import moment from 'moment-timezone';
@Injectable()
export class AdProfile extends AutomapperProfile {
@@ -21,16 +22,14 @@ export class AdProfile extends AutomapperProfile {
CreateAdRequest,
forMember(
(dest) => dest.waypoints,
mapFrom(
(source) =>
source.waypoints.map(
(waypoint) =>
new Coordinates(
waypoint.lon ?? undefined,
waypoint.lat ?? undefined,
),
),
// .filter((waypoint) => waypoint),
mapFrom((source) =>
source.waypoints.map(
(waypoint) =>
new Coordinates(
waypoint.lon ?? undefined,
waypoint.lat ?? undefined,
),
),
),
),
);
@@ -54,6 +53,18 @@ export class AdProfile extends AutomapperProfile {
(dest) => dest.updatedAt,
mapFrom((source) => new Date(source.updatedAt)),
),
// forMember(
// (dest) => dest.monTime,
// mapFrom((source) =>
// source.monTime
// ? new Date(
// moment
// .tz(`${source.fromDate} ${source.monTime}`, source.timezone)
// .format(),
// )
// : undefined,
// ),
// ),
);
};
}

View File

@@ -1,17 +0,0 @@
import { createMap, Mapper } from '@automapper/core';
import { AutomapperProfile, InjectMapper } from '@automapper/nestjs';
import { Injectable } from '@nestjs/common';
import { Coordinates } from '../../geography/domain/types/coordinates.type';
@Injectable()
export class CoordinatesProfile extends AutomapperProfile {
constructor(@InjectMapper() mapper: Mapper) {
super(mapper);
}
override get profile() {
return (mapper: any) => {
createMap(mapper, Coordinates, Coordinates);
};
}
}