From d6a12e4d0ebc746a179ba992a4f9a1911de96507 Mon Sep 17 00:00:00 2001 From: sbriat Date: Fri, 7 Apr 2023 18:01:57 +0200 Subject: [PATCH] WIP create query --- .../adapters/primaries/matcher.controller.ts | 29 +++++-- .../matcher/adapters/primaries/matcher.proto | 45 ++++++++++ .../margin-durations.type.ts} | 0 .../matcher/domain/dtos/match.request.ts | 23 ++++-- .../{entities => dtos}/schedule.type.ts | 0 .../matcher/domain/entities/geography.ts | 11 +++ src/modules/matcher/domain/entities/person.ts | 42 ++++++++++ .../matcher/domain/entities/requirement.ts | 4 + .../domain/{dtos => entities}/role.enum.ts | 0 src/modules/matcher/domain/entities/route.ts | 1 + .../matcher/domain/entities/settings.ts | 14 ++++ src/modules/matcher/domain/entities/time.ts | 50 +++++++++++ src/modules/matcher/domain/entities/timing.ts | 14 ++++ .../domain/interfaces/georouter.interface.ts | 3 + .../matcher/exceptions/matcher.exception.ts | 13 +++ src/modules/matcher/queries/match.query.ts | 82 ++++++++++++++++++- 16 files changed, 310 insertions(+), 21 deletions(-) rename src/modules/matcher/domain/{entities/margin_durations.type.ts => dtos/margin-durations.type.ts} (100%) rename src/modules/matcher/domain/{entities => dtos}/schedule.type.ts (100%) create mode 100644 src/modules/matcher/domain/entities/geography.ts create mode 100644 src/modules/matcher/domain/entities/person.ts create mode 100644 src/modules/matcher/domain/entities/requirement.ts rename src/modules/matcher/domain/{dtos => entities}/role.enum.ts (100%) create mode 100644 src/modules/matcher/domain/entities/route.ts create mode 100644 src/modules/matcher/domain/entities/settings.ts create mode 100644 src/modules/matcher/domain/entities/time.ts create mode 100644 src/modules/matcher/domain/entities/timing.ts create mode 100644 src/modules/matcher/domain/interfaces/georouter.interface.ts create mode 100644 src/modules/matcher/exceptions/matcher.exception.ts diff --git a/src/modules/matcher/adapters/primaries/matcher.controller.ts b/src/modules/matcher/adapters/primaries/matcher.controller.ts index a7619fb..1a837a9 100644 --- a/src/modules/matcher/adapters/primaries/matcher.controller.ts +++ b/src/modules/matcher/adapters/primaries/matcher.controller.ts @@ -2,17 +2,18 @@ import { Mapper } from '@automapper/core'; import { InjectMapper } from '@automapper/nestjs'; import { Controller, UsePipes } from '@nestjs/common'; import { QueryBus } from '@nestjs/cqrs'; -import { GrpcMethod } from '@nestjs/microservices'; +import { GrpcMethod, RpcException } from '@nestjs/microservices'; import { RpcValidationPipe } from 'src/modules/utils/pipes/rpc.validation-pipe'; import { MatchRequest } from '../../domain/dtos/match.request'; import { ICollection } from 'src/modules/database/src/interfaces/collection.interface'; import { Match } from '../../domain/entities/match'; import { MatchQuery } from '../../queries/match.query'; import { MatchPresenter } from '../secondaries/match.presenter'; +import { ConfigService } from '@nestjs/config'; @UsePipes( new RpcValidationPipe({ - whitelist: true, + whitelist: false, forbidUnknownValues: false, }), ) @@ -20,17 +21,27 @@ import { MatchPresenter } from '../secondaries/match.presenter'; export class MatcherController { constructor( private readonly _queryBus: QueryBus, + private readonly _configService: ConfigService, @InjectMapper() private readonly _mapper: Mapper, ) {} @GrpcMethod('MatcherService', 'Match') async match(data: MatchRequest): Promise> { - const matchCollection = await this._queryBus.execute(new MatchQuery(data)); - return Promise.resolve({ - data: matchCollection.data.map((match: Match) => - this._mapper.map(match, Match, MatchPresenter), - ), - total: matchCollection.total, - }); + try { + const matchCollection = await this._queryBus.execute( + new MatchQuery(data, this._configService), + ); + return Promise.resolve({ + data: matchCollection.data.map((match: Match) => + this._mapper.map(match, Match, MatchPresenter), + ), + total: matchCollection.total, + }); + } catch (e) { + throw new RpcException({ + code: e.code, + message: e.message, + }); + } } } diff --git a/src/modules/matcher/adapters/primaries/matcher.proto b/src/modules/matcher/adapters/primaries/matcher.proto index fa642d5..f610b21 100644 --- a/src/modules/matcher/adapters/primaries/matcher.proto +++ b/src/modules/matcher/adapters/primaries/matcher.proto @@ -8,6 +8,27 @@ service MatcherService { message MatchRequest { repeated Point waypoints = 1; + string departure = 2; + string fromDate = 3; + Schedule schedule = 4; + bool driver = 5; + bool passenger = 6; + string toDate = 7; + int32 marginDuration = 8; + MarginDurations marginDurations = 9; + int32 seatsPassenger = 10; + int32 seatsDriver = 11; + bool strict = 12; + Algorithm algorithm = 13; + int32 remoteness = 14; + bool useProportion = 15; + int32 proportion = 16; + bool useAzimuth = 17; + int32 azimuthMargin = 18; + int32 maxDetourDistanceRatio = 19; + int32 maxDetourDurationRatio = 20; + repeated int32 exclusions = 21; + int32 identifier = 22; } message Point { @@ -15,6 +36,30 @@ message Point { float lat = 2; } +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; +} + +enum Algorithm { + CLASSIC = 0; +} + message Match { string uuid = 1; } diff --git a/src/modules/matcher/domain/entities/margin_durations.type.ts b/src/modules/matcher/domain/dtos/margin-durations.type.ts similarity index 100% rename from src/modules/matcher/domain/entities/margin_durations.type.ts rename to src/modules/matcher/domain/dtos/margin-durations.type.ts diff --git a/src/modules/matcher/domain/dtos/match.request.ts b/src/modules/matcher/domain/dtos/match.request.ts index 7384689..bbdd274 100644 --- a/src/modules/matcher/domain/dtos/match.request.ts +++ b/src/modules/matcher/domain/dtos/match.request.ts @@ -1,18 +1,18 @@ import { IsArray, IsBoolean, - IsDate, IsEnum, IsInt, IsNumber, IsOptional, + IsString, Max, Min, } from 'class-validator'; import { AutoMap } from '@automapper/classes'; import { Point } from '../entities/point.type'; -import { Schedule } from '../entities/schedule.type'; -import { MarginDurations } from '../entities/margin_durations.type'; +import { Schedule } from './schedule.type'; +import { MarginDurations } from './margin-durations.type'; import { Algorithm } from './algorithm.enum'; export class MatchRequest { @@ -20,15 +20,15 @@ export class MatchRequest { @AutoMap() waypoints: Array; - @IsDate() @IsOptional() + @IsString() @AutoMap() - departure: Date; + departure: number; - @IsDate() @IsOptional() + @IsInt() @AutoMap() - fromDate: Date; + fromDate: number; @IsOptional() @AutoMap() @@ -45,9 +45,9 @@ export class MatchRequest { passenger: boolean; @IsOptional() - @IsDate() + @IsInt() @AutoMap() - toDate: Date; + toDate: number; @IsOptional() @IsInt() @@ -123,4 +123,9 @@ export class MatchRequest { @IsOptional() @IsArray() exclusions: Array; + + @IsOptional() + @IsInt() + @AutoMap() + identifier: number; } diff --git a/src/modules/matcher/domain/entities/schedule.type.ts b/src/modules/matcher/domain/dtos/schedule.type.ts similarity index 100% rename from src/modules/matcher/domain/entities/schedule.type.ts rename to src/modules/matcher/domain/dtos/schedule.type.ts diff --git a/src/modules/matcher/domain/entities/geography.ts b/src/modules/matcher/domain/entities/geography.ts new file mode 100644 index 0000000..4cc5007 --- /dev/null +++ b/src/modules/matcher/domain/entities/geography.ts @@ -0,0 +1,11 @@ +import { Point } from './point.type'; +import { Route } from './route'; + +export class Geography { + waypoints: Array; + originType: number; + destinationType: number; + timezone: string; + driverRoute: Route; + passengerRoute: Route; +} diff --git a/src/modules/matcher/domain/entities/person.ts b/src/modules/matcher/domain/entities/person.ts new file mode 100644 index 0000000..37b2add --- /dev/null +++ b/src/modules/matcher/domain/entities/person.ts @@ -0,0 +1,42 @@ +import { MatchRequest } from '../dtos/match.request'; + +export class Person { + _matchRequest: MatchRequest; + _defaultIdentifier: number; + _defaultMarginDuration: number; + identifier: number; + marginDurations: Array; + + constructor( + matchRequest: MatchRequest, + defaultIdentifier: number, + defaultMarginDuration: number, + ) { + this._matchRequest = matchRequest; + this._defaultIdentifier = defaultIdentifier; + this._defaultMarginDuration = defaultMarginDuration; + } + + init() { + this.setIdentifier( + this._matchRequest.identifier ?? this._defaultIdentifier, + ); + this.setMarginDurations([ + this._defaultMarginDuration, + this._defaultMarginDuration, + this._defaultMarginDuration, + this._defaultMarginDuration, + this._defaultMarginDuration, + this._defaultMarginDuration, + this._defaultMarginDuration, + ]); + } + + setIdentifier(identifier: number) { + this.identifier = identifier; + } + + setMarginDurations(marginDurations: Array) { + this.marginDurations = marginDurations; + } +} diff --git a/src/modules/matcher/domain/entities/requirement.ts b/src/modules/matcher/domain/entities/requirement.ts new file mode 100644 index 0000000..b9c9b7c --- /dev/null +++ b/src/modules/matcher/domain/entities/requirement.ts @@ -0,0 +1,4 @@ +export class Requirement { + seatsDriver: number; + seatsPassenger: number; +} diff --git a/src/modules/matcher/domain/dtos/role.enum.ts b/src/modules/matcher/domain/entities/role.enum.ts similarity index 100% rename from src/modules/matcher/domain/dtos/role.enum.ts rename to src/modules/matcher/domain/entities/role.enum.ts diff --git a/src/modules/matcher/domain/entities/route.ts b/src/modules/matcher/domain/entities/route.ts new file mode 100644 index 0000000..bf729c9 --- /dev/null +++ b/src/modules/matcher/domain/entities/route.ts @@ -0,0 +1 @@ +export class Route {} diff --git a/src/modules/matcher/domain/entities/settings.ts b/src/modules/matcher/domain/entities/settings.ts new file mode 100644 index 0000000..14d53b8 --- /dev/null +++ b/src/modules/matcher/domain/entities/settings.ts @@ -0,0 +1,14 @@ +import { Georouter } from '../interfaces/georouter.interface'; + +export class Settings { + algorithm: Algorithm; + restrict: boolean; + remoteness: number; + useProportion: boolean; + proportion: number; + useAzimuth: boolean; + azimuthMargin: number; + maxDetourDurationRatio: number; + maxDetourDistanceRatio: number; + georouter: Georouter; +} diff --git a/src/modules/matcher/domain/entities/time.ts b/src/modules/matcher/domain/entities/time.ts new file mode 100644 index 0000000..36ae203 --- /dev/null +++ b/src/modules/matcher/domain/entities/time.ts @@ -0,0 +1,50 @@ +import { MatcherException } from '../../exceptions/matcher.exception'; +import { MatchRequest } from '../dtos/match.request'; +import { TimingFrequency } from './timing'; + +export class Time { + _matchRequest: MatchRequest; + _defaultMarginDuration: number; + _defaultValidityDuration: number; + frequency: TimingFrequency; + fromDate: Date; + toDate: Date; + schedule: Array; + marginDurations: Array; + + constructor( + matchRequest: MatchRequest, + defaultMarginDuration: number, + defaultValidityDuration: number, + ) { + this._matchRequest = matchRequest; + this._defaultMarginDuration = defaultMarginDuration; + this._defaultValidityDuration = defaultValidityDuration; + } + + init() { + this._validatePunctualDate(); + this._validateRecurrentDate(); + } + + _validatePunctualDate() { + if (!this._matchRequest.departure && !this._matchRequest.fromDate) { + throw new MatcherException(3, 'departure or fromDate is Required'); + } + if (this._matchRequest.departure) { + this.fromDate = new Date(this._matchRequest.departure); + if (!this._isDate(this.fromDate)) { + throw new MatcherException(3, 'Wrong departure date'); + } + console.log(this.fromDate); + } + } + + _validateRecurrentDate() { + console.log('validate recurrent date'); + } + + _isDate(date: Date) { + return date instanceof Date && isFinite(+date); + } +} diff --git a/src/modules/matcher/domain/entities/timing.ts b/src/modules/matcher/domain/entities/timing.ts new file mode 100644 index 0000000..2efe5b2 --- /dev/null +++ b/src/modules/matcher/domain/entities/timing.ts @@ -0,0 +1,14 @@ +export enum TimingFrequency { + FREQUENCY_PUNCTUAL = 1, + FREQUENCY_RECURRENT = 2, +} + +export enum TimingDays { + 'mon', + 'tue', + 'wed', + 'thu', + 'fri', + 'sat', + 'sun', +} diff --git a/src/modules/matcher/domain/interfaces/georouter.interface.ts b/src/modules/matcher/domain/interfaces/georouter.interface.ts new file mode 100644 index 0000000..e28617d --- /dev/null +++ b/src/modules/matcher/domain/interfaces/georouter.interface.ts @@ -0,0 +1,3 @@ +export interface Georouter { + type: string; +} diff --git a/src/modules/matcher/exceptions/matcher.exception.ts b/src/modules/matcher/exceptions/matcher.exception.ts new file mode 100644 index 0000000..c72c694 --- /dev/null +++ b/src/modules/matcher/exceptions/matcher.exception.ts @@ -0,0 +1,13 @@ +export class MatcherException implements Error { + name: string; + message: string; + + constructor(private _code: number, private _message: string) { + this.name = 'MatcherException'; + this.message = _message; + } + + get code(): number { + return this._code; + } +} diff --git a/src/modules/matcher/queries/match.query.ts b/src/modules/matcher/queries/match.query.ts index 53ac933..4d26d19 100644 --- a/src/modules/matcher/queries/match.query.ts +++ b/src/modules/matcher/queries/match.query.ts @@ -1,9 +1,85 @@ +import { ConfigService } from '@nestjs/config'; import { MatchRequest } from '../domain/dtos/match.request'; +import { Geography } from '../domain/entities/geography'; +import { Person } from '../domain/entities/person'; +import { Requirement } from '../domain/entities/requirement'; +import { Role } from '../domain/entities/role.enum'; +import { Settings } from '../domain/entities/settings'; +import { Time } from '../domain/entities/time'; +import { MatcherException } from '../exceptions/matcher.exception'; export class MatchQuery { - matchRequest: MatchRequest; + private readonly _matchRequest: MatchRequest; + private readonly _configService: ConfigService; + person: Person; + exclusions: Array; + time: Time; + geography: Geography; + roles: Array; + requirement: Requirement; + settings: Settings; - constructor(matchRequest?: MatchRequest) { - this.matchRequest = matchRequest; + constructor(matchRequest: MatchRequest, configService: ConfigService) { + this._matchRequest = matchRequest; + this._configService = configService; + this._validate(); + this._initialize(); + this._setPerson(); + this._setExclusions(); + this._setRoles(); + this._setTime(); + // console.log(this); + } + + _validate() { + if (!this._matchRequest.departure) { + if (!this._matchRequest.fromDate) + throw new MatcherException(3, 'departure or fromDate is Required'); + if (!this._matchRequest.schedule) + throw new MatcherException(3, 'schedule is Required'); + } + } + + _initialize() { + if ( + this._matchRequest.driver === undefined && + this._matchRequest.passenger === undefined + ) + this._matchRequest.passenger = true; + this.geography = new Geography(); + this.requirement = new Requirement(); + this.settings = new Settings(); + } + + _setPerson() { + this.person = new Person( + this._matchRequest, + this._configService.get('DEFAULT_IDENTIFIER'), + this._configService.get('MARGIN_DURATION'), + ); + this.person.init(); + } + + _setExclusions() { + this.exclusions = []; + if (this._matchRequest.identifier) + this.exclusions.push(this._matchRequest.identifier); + if (this._matchRequest.exclusions) + this.exclusions.push(...this._matchRequest.exclusions); + } + + _setRoles() { + this.roles = []; + if (this._matchRequest.driver) this.roles.push(Role.DRIVER); + if (this._matchRequest.passenger) this.roles.push(Role.PASSENGER); + } + + _setTime() { + this.time = new Time( + this._matchRequest, + this._configService.get('MARGIN_DURATION'), + this._configService.get('VALIDITY_DURATION'), + ); + this.time.init(); } }