WIP create query

This commit is contained in:
sbriat 2023-04-07 18:01:57 +02:00
parent f3ca813dd6
commit d6a12e4d0e
16 changed files with 310 additions and 21 deletions

View File

@ -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<ICollection<Match>> {
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,
});
}
}
}

View File

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

View File

@ -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<Point>;
@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<number>;
@IsOptional()
@IsInt()
@AutoMap()
identifier: number;
}

View File

@ -0,0 +1,11 @@
import { Point } from './point.type';
import { Route } from './route';
export class Geography {
waypoints: Array<Point>;
originType: number;
destinationType: number;
timezone: string;
driverRoute: Route;
passengerRoute: Route;
}

View File

@ -0,0 +1,42 @@
import { MatchRequest } from '../dtos/match.request';
export class Person {
_matchRequest: MatchRequest;
_defaultIdentifier: number;
_defaultMarginDuration: number;
identifier: number;
marginDurations: Array<number>;
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<number>) {
this.marginDurations = marginDurations;
}
}

View File

@ -0,0 +1,4 @@
export class Requirement {
seatsDriver: number;
seatsPassenger: number;
}

View File

@ -0,0 +1 @@
export class Route {}

View File

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

View File

@ -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<Date>;
marginDurations: Array<number>;
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);
}
}

View File

@ -0,0 +1,14 @@
export enum TimingFrequency {
FREQUENCY_PUNCTUAL = 1,
FREQUENCY_RECURRENT = 2,
}
export enum TimingDays {
'mon',
'tue',
'wed',
'thu',
'fri',
'sat',
'sun',
}

View File

@ -0,0 +1,3 @@
export interface Georouter {
type: string;
}

View File

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

View File

@ -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<number>;
time: Time;
geography: Geography;
roles: Array<Role>;
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<number>('DEFAULT_IDENTIFIER'),
this._configService.get<number>('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<number>('MARGIN_DURATION'),
this._configService.get<number>('VALIDITY_DURATION'),
);
this.time.init();
}
}