create ad WIP, extract geography methods to dedicated module
This commit is contained in:
parent
ca693087d2
commit
aeead7fb62
|
@ -1,65 +0,0 @@
|
|||
-- CreateExtension
|
||||
CREATE EXTENSION IF NOT EXISTS "postgis";
|
||||
|
||||
-- Required to use postgis extension :
|
||||
-- set the search_path to both public and territory (where is postgis) AND the current schema
|
||||
SET search_path TO matcher, territory, public;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "ad" (
|
||||
"uuid" UUID NOT NULL,
|
||||
"driver" BOOLEAN NOT NULL,
|
||||
"passenger" BOOLEAN NOT NULL,
|
||||
"frequency" INTEGER NOT NULL,
|
||||
"from_date" DATE NOT NULL,
|
||||
"to_date" DATE NOT NULL,
|
||||
"mon_time" TIMESTAMPTZ NOT NULL,
|
||||
"tue_time" TIMESTAMPTZ NOT NULL,
|
||||
"wed_time" TIMESTAMPTZ NOT NULL,
|
||||
"thu_time" TIMESTAMPTZ NOT NULL,
|
||||
"fri_time" TIMESTAMPTZ NOT NULL,
|
||||
"sat_time" TIMESTAMPTZ NOT NULL,
|
||||
"sun_time" TIMESTAMPTZ NOT NULL,
|
||||
"mon_margin" INTEGER NOT NULL,
|
||||
"tue_margin" INTEGER NOT NULL,
|
||||
"wed_margin" INTEGER NOT NULL,
|
||||
"thu_margin" INTEGER NOT NULL,
|
||||
"fri_margin" INTEGER NOT NULL,
|
||||
"sat_margin" INTEGER NOT NULL,
|
||||
"sun_margin" INTEGER NOT NULL,
|
||||
"driver_duration" INTEGER NOT NULL,
|
||||
"driver_distance" INTEGER NOT NULL,
|
||||
"passenger_duration" INTEGER NOT NULL,
|
||||
"passenger_distance" INTEGER NOT NULL,
|
||||
"origin_type" SMALLINT NOT NULL,
|
||||
"destination_type" SMALLINT NOT NULL,
|
||||
"waypoints" geography(LINESTRING) NOT NULL,
|
||||
"direction" geography(LINESTRING) NOT NULL,
|
||||
"fwd_azimuth" INTEGER NOT NULL,
|
||||
"back_azimuth" INTEGER NOT NULL,
|
||||
"seats_driver" SMALLINT NOT NULL,
|
||||
"seats_passenger" SMALLINT NOT NULL,
|
||||
"seats_used" SMALLINT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "ad_pkey" PRIMARY KEY ("uuid")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "ad_driver_idx" ON "ad"("driver");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "ad_passenger_idx" ON "ad"("passenger");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "ad_from_date_idx" ON "ad"("from_date");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "ad_to_date_idx" ON "ad"("to_date");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "ad_fwd_azimuth_idx" ON "ad"("fwd_azimuth");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "direction_idx" ON "ad" USING GIST ("direction");
|
|
@ -0,0 +1,65 @@
|
|||
-- CreateExtension
|
||||
CREATE EXTENSION IF NOT EXISTS "postgis";
|
||||
|
||||
-- Required to use postgis extension :
|
||||
-- set the search_path to both public and territory (where is postgis) AND the current schema
|
||||
SET search_path TO matcher, territory, public;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "ad" (
|
||||
"uuid" UUID NOT NULL,
|
||||
"driver" BOOLEAN NOT NULL,
|
||||
"passenger" BOOLEAN NOT NULL,
|
||||
"frequency" INTEGER NOT NULL,
|
||||
"fromDate" DATE NOT NULL,
|
||||
"toDate" DATE NOT NULL,
|
||||
"monTime" TIMESTAMPTZ NOT NULL,
|
||||
"tueTime" TIMESTAMPTZ NOT NULL,
|
||||
"wedTime" TIMESTAMPTZ NOT NULL,
|
||||
"thuTime" TIMESTAMPTZ NOT NULL,
|
||||
"friTime" TIMESTAMPTZ NOT NULL,
|
||||
"satTime" TIMESTAMPTZ NOT NULL,
|
||||
"sunTime" TIMESTAMPTZ NOT NULL,
|
||||
"monMargin" INTEGER NOT NULL,
|
||||
"tueMargin" INTEGER NOT NULL,
|
||||
"wedMargin" INTEGER NOT NULL,
|
||||
"thuMargin" INTEGER NOT NULL,
|
||||
"friMargin" INTEGER NOT NULL,
|
||||
"satMargin" INTEGER NOT NULL,
|
||||
"sunMargin" INTEGER NOT NULL,
|
||||
"driverDuration" INTEGER NOT NULL,
|
||||
"driverDistance" INTEGER NOT NULL,
|
||||
"passengerDuration" INTEGER NOT NULL,
|
||||
"passengerDistance" INTEGER NOT NULL,
|
||||
"originType" SMALLINT NOT NULL,
|
||||
"destinationType" SMALLINT NOT NULL,
|
||||
"waypoints" geography(LINESTRING),
|
||||
"direction" geography(LINESTRING),
|
||||
"fwdAzimuth" INTEGER NOT NULL,
|
||||
"backAzimuth" INTEGER NOT NULL,
|
||||
"seatsDriver" SMALLINT NOT NULL,
|
||||
"seatsPassenger" SMALLINT NOT NULL,
|
||||
"seatsUsed" SMALLINT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "ad_pkey" PRIMARY KEY ("uuid")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "ad_driver_idx" ON "ad"("driver");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "ad_passenger_idx" ON "ad"("passenger");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "ad_fromDate_idx" ON "ad"("fromDate");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "ad_toDate_idx" ON "ad"("toDate");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "ad_fwdAzimuth_idx" ON "ad"("fwdAzimuth");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "direction_idx" ON "ad" USING GIST ("direction");
|
|
@ -13,47 +13,47 @@ datasource db {
|
|||
}
|
||||
|
||||
model Ad {
|
||||
uuid String @id @default(uuid()) @db.Uuid
|
||||
driver Boolean
|
||||
passenger Boolean
|
||||
frequency Int
|
||||
from_date DateTime @db.Date
|
||||
to_date DateTime @db.Date
|
||||
mon_time DateTime @db.Timestamptz()
|
||||
tue_time DateTime @db.Timestamptz()
|
||||
wed_time DateTime @db.Timestamptz()
|
||||
thu_time DateTime @db.Timestamptz()
|
||||
fri_time DateTime @db.Timestamptz()
|
||||
sat_time DateTime @db.Timestamptz()
|
||||
sun_time DateTime @db.Timestamptz()
|
||||
mon_margin Int
|
||||
tue_margin Int
|
||||
wed_margin Int
|
||||
thu_margin Int
|
||||
fri_margin Int
|
||||
sat_margin Int
|
||||
sun_margin Int
|
||||
driver_duration Int
|
||||
driver_distance Int
|
||||
passenger_duration Int
|
||||
passenger_distance Int
|
||||
origin_type Int @db.SmallInt
|
||||
destination_type Int @db.SmallInt
|
||||
waypoints Unsupported("geography(LINESTRING)")
|
||||
direction Unsupported("geography(LINESTRING)")
|
||||
fwd_azimuth Int
|
||||
back_azimuth Int
|
||||
seats_driver Int @db.SmallInt
|
||||
seats_passenger Int @db.SmallInt
|
||||
seats_used Int @db.SmallInt
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @default(now()) @updatedAt
|
||||
uuid String @id @default(uuid()) @db.Uuid
|
||||
driver Boolean
|
||||
passenger Boolean
|
||||
frequency Int
|
||||
fromDate DateTime @db.Date
|
||||
toDate DateTime @db.Date
|
||||
monTime DateTime @db.Timestamptz()
|
||||
tueTime DateTime @db.Timestamptz()
|
||||
wedTime DateTime @db.Timestamptz()
|
||||
thuTime DateTime @db.Timestamptz()
|
||||
friTime DateTime @db.Timestamptz()
|
||||
satTime DateTime @db.Timestamptz()
|
||||
sunTime DateTime @db.Timestamptz()
|
||||
monMargin Int
|
||||
tueMargin Int
|
||||
wedMargin Int
|
||||
thuMargin Int
|
||||
friMargin Int
|
||||
satMargin Int
|
||||
sunMargin Int
|
||||
driverDuration Int
|
||||
driverDistance Int
|
||||
passengerDuration Int
|
||||
passengerDistance Int
|
||||
originType Int @db.SmallInt
|
||||
destinationType Int @db.SmallInt
|
||||
waypoints Unsupported("geography(LINESTRING)")?
|
||||
direction Unsupported("geography(LINESTRING)")?
|
||||
fwdAzimuth Int
|
||||
backAzimuth Int
|
||||
seatsDriver Int @db.SmallInt
|
||||
seatsPassenger Int @db.SmallInt
|
||||
seatsUsed Int @db.SmallInt
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @default(now()) @updatedAt
|
||||
|
||||
@@index([driver])
|
||||
@@index([passenger])
|
||||
@@index([from_date])
|
||||
@@index([to_date])
|
||||
@@index([fwd_azimuth])
|
||||
@@index([fromDate])
|
||||
@@index([toDate])
|
||||
@@index([fwdAzimuth])
|
||||
@@index([direction], name: "direction_idx", type: Gist)
|
||||
@@map("ad")
|
||||
}
|
||||
|
|
|
@ -2,9 +2,16 @@ import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq';
|
|||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import { AdMessagerController } from './adapters/primaries/ad-messager.controller';
|
||||
import { AdProfile } from './mappers/ad.profile';
|
||||
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';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
DatabaseModule,
|
||||
CqrsModule,
|
||||
RabbitMQModule.forRootAsync(RabbitMQModule, {
|
||||
imports: [ConfigModule],
|
||||
useFactory: async (configService: ConfigService) => ({
|
||||
|
@ -18,9 +25,7 @@ import { AdMessagerController } from './adapters/primaries/ad-messager.controlle
|
|||
adCreated: {
|
||||
exchange: configService.get<string>('RMQ_EXCHANGE'),
|
||||
routingKey: 'ad.created',
|
||||
queue: `${configService.get<string>(
|
||||
'RMQ_EXCHANGE',
|
||||
)}-matcher-ad-created`,
|
||||
queue: 'matcher-ad-created',
|
||||
},
|
||||
},
|
||||
uri: configService.get<string>('RMQ_URI'),
|
||||
|
@ -31,7 +36,7 @@ import { AdMessagerController } from './adapters/primaries/ad-messager.controlle
|
|||
}),
|
||||
],
|
||||
controllers: [AdMessagerController],
|
||||
providers: [],
|
||||
providers: [AdProfile, AdRepository, CreateAdUseCase],
|
||||
exports: [],
|
||||
})
|
||||
export class AdModule {}
|
||||
|
|
|
@ -1,19 +1,32 @@
|
|||
import { RabbitSubscribe } from '@golevelup/nestjs-rabbitmq';
|
||||
import { Controller } from '@nestjs/common';
|
||||
// import { Ad } from '../../domain/entities/ad';
|
||||
import { Ad } from '../../domain/entities/ad';
|
||||
import { InjectMapper } from '@automapper/nestjs';
|
||||
import { Mapper } from '@automapper/core';
|
||||
import { CommandBus } from '@nestjs/cqrs';
|
||||
import { CreateAdCommand } from '../../commands/create-ad.command';
|
||||
import { AdPresenter } from './ad.presenter';
|
||||
import { CreateAdRequest } from '../../domain/dtos/create-ad.request';
|
||||
|
||||
@Controller()
|
||||
export class AdMessagerController {
|
||||
constructor(
|
||||
private readonly _commandBus: CommandBus,
|
||||
@InjectMapper() private readonly _mapper: Mapper,
|
||||
) {}
|
||||
|
||||
@RabbitSubscribe({
|
||||
name: 'adCreated',
|
||||
})
|
||||
public adCreatedHandler(message: string) {
|
||||
console.log(JSON.parse(message));
|
||||
// try {
|
||||
// const createdAd: Ad = JSON.parse(message);
|
||||
// console.log(createdAd);
|
||||
// } catch (e) {
|
||||
// console.log('error', e);
|
||||
// }
|
||||
async adCreatedHandler(message: string): Promise<AdPresenter> {
|
||||
try {
|
||||
const createAdRequest: CreateAdRequest = JSON.parse(message);
|
||||
const ad: Ad = await this._commandBus.execute(
|
||||
new CreateAdCommand(createAdRequest),
|
||||
);
|
||||
return this._mapper.map(ad, Ad, AdPresenter);
|
||||
} catch (e) {
|
||||
console.log('error', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { AutoMap } from '@automapper/classes';
|
||||
|
||||
export class Ad {
|
||||
export class AdPresenter {
|
||||
@AutoMap()
|
||||
uuid: string;
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { MatcherRepository } from '../../../database/src/domain/matcher-repository';
|
||||
import { Ad } from '../../domain/entities/ad';
|
||||
import { DatabaseException } from '../../../database/src/exceptions/database.exception';
|
||||
|
||||
@Injectable()
|
||||
export class AdRepository extends MatcherRepository<Ad> {
|
||||
protected _model = 'ad';
|
||||
|
||||
async createAd(ad: Partial<Ad>): Promise<Ad> {
|
||||
try {
|
||||
const affectedRowNumber = await this.createWithFields(
|
||||
this.createFields(ad),
|
||||
);
|
||||
if (affectedRowNumber == 1) {
|
||||
return this.findOneByUuid(ad.uuid);
|
||||
}
|
||||
throw new DatabaseException();
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private createFields(ad: Partial<Ad>): Partial<AdFields> {
|
||||
return {
|
||||
uuid: `'${ad.uuid}'`,
|
||||
driver: ad.driver ? 'true' : 'false',
|
||||
passenger: ad.passenger ? 'true' : 'false',
|
||||
frequency: ad.frequency,
|
||||
fromDate: `'${ad.fromDate}'`,
|
||||
toDate: `'${ad.toDate}'`,
|
||||
monTime: `'${ad.monTime}'`,
|
||||
tueTime: `'${ad.tueTime}'`,
|
||||
wedTime: `'${ad.wedTime}'`,
|
||||
thuTime: `'${ad.thuTime}'`,
|
||||
friTime: `'${ad.friTime}'`,
|
||||
satTime: `'${ad.satTime}'`,
|
||||
sunTime: `'${ad.sunTime}'`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
type AdFields = {
|
||||
uuid: string;
|
||||
driver: string;
|
||||
passenger: string;
|
||||
frequency: number;
|
||||
fromDate: string;
|
||||
toDate: string;
|
||||
monTime: string;
|
||||
tueTime: string;
|
||||
wedTime: string;
|
||||
thuTime: string;
|
||||
friTime: string;
|
||||
satTime: string;
|
||||
sunTime: string;
|
||||
monMargin: number;
|
||||
tueMargin: number;
|
||||
wedMargin: number;
|
||||
thuMargin: number;
|
||||
friMargin: number;
|
||||
satMargin: number;
|
||||
sunMargin: number;
|
||||
driverDuration: number;
|
||||
driverDistance: number;
|
||||
passengerDuration: number;
|
||||
passengerDistance: number;
|
||||
originType: number;
|
||||
destinationType: number;
|
||||
waypoints: string;
|
||||
direction: string;
|
||||
fwdAzimuth: number;
|
||||
backAzimuth: number;
|
||||
seatsDriver: number;
|
||||
seatsPassenger: number;
|
||||
seatsUsed: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
|
@ -0,0 +1,9 @@
|
|||
import { CreateAdRequest } from '../domain/dtos/create-ad.request';
|
||||
|
||||
export class CreateAdCommand {
|
||||
readonly createAdRequest: CreateAdRequest;
|
||||
|
||||
constructor(request: CreateAdRequest) {
|
||||
this.createAdRequest = request;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
import { AutoMap } from '@automapper/classes';
|
||||
import { IsBoolean, IsNotEmpty, IsNumber, IsString } from 'class-validator';
|
||||
|
||||
export class CreateAdRequest {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@AutoMap()
|
||||
uuid: string;
|
||||
|
||||
@IsBoolean()
|
||||
@AutoMap()
|
||||
driver: boolean;
|
||||
|
||||
@IsBoolean()
|
||||
@AutoMap()
|
||||
passenger: boolean;
|
||||
|
||||
@IsNumber()
|
||||
@AutoMap()
|
||||
frequency: number;
|
||||
|
||||
@IsString()
|
||||
@AutoMap()
|
||||
fromDate: string;
|
||||
|
||||
@IsString()
|
||||
@AutoMap()
|
||||
toDate: string;
|
||||
|
||||
@IsString()
|
||||
@AutoMap()
|
||||
monTime: string;
|
||||
|
||||
@IsString()
|
||||
@AutoMap()
|
||||
tueTime: string;
|
||||
|
||||
@IsString()
|
||||
@AutoMap()
|
||||
wedTime: string;
|
||||
|
||||
@IsString()
|
||||
@AutoMap()
|
||||
thuTime: string;
|
||||
|
||||
@IsString()
|
||||
@AutoMap()
|
||||
friTime: string;
|
||||
|
||||
@IsString()
|
||||
@AutoMap()
|
||||
satTime: string;
|
||||
|
||||
@IsString()
|
||||
@AutoMap()
|
||||
sunTime: string;
|
||||
|
||||
@IsNumber()
|
||||
@AutoMap()
|
||||
monMargin: number;
|
||||
|
||||
@IsNumber()
|
||||
@AutoMap()
|
||||
tueMargin: number;
|
||||
|
||||
@IsNumber()
|
||||
@AutoMap()
|
||||
wedMargin: number;
|
||||
|
||||
@IsNumber()
|
||||
@AutoMap()
|
||||
thuMargin: number;
|
||||
|
||||
@IsNumber()
|
||||
@AutoMap()
|
||||
friMargin: number;
|
||||
|
||||
@IsNumber()
|
||||
@AutoMap()
|
||||
satMargin: number;
|
||||
|
||||
@IsNumber()
|
||||
@AutoMap()
|
||||
sunMargin: number;
|
||||
|
||||
@IsNumber()
|
||||
@AutoMap()
|
||||
originType: number;
|
||||
|
||||
@IsNumber()
|
||||
@AutoMap()
|
||||
destinationType: number;
|
||||
|
||||
@AutoMap()
|
||||
waypoints: [];
|
||||
|
||||
@IsNumber()
|
||||
@AutoMap()
|
||||
seatsDriver: number;
|
||||
|
||||
@IsNumber()
|
||||
@AutoMap()
|
||||
seatsPassenger: number;
|
||||
|
||||
@IsNumber()
|
||||
@AutoMap()
|
||||
seatsUsed: number;
|
||||
|
||||
@IsString()
|
||||
@AutoMap()
|
||||
createdAt: string;
|
||||
|
||||
@IsString()
|
||||
@AutoMap()
|
||||
updatedAt: string;
|
||||
}
|
|
@ -4,38 +4,105 @@ export class Ad {
|
|||
@AutoMap()
|
||||
uuid: string;
|
||||
|
||||
@AutoMap()
|
||||
driver: boolean;
|
||||
|
||||
@AutoMap()
|
||||
passenger: boolean;
|
||||
|
||||
@AutoMap()
|
||||
frequency: number;
|
||||
|
||||
@AutoMap()
|
||||
fromDate: string;
|
||||
|
||||
@AutoMap()
|
||||
toDate: string;
|
||||
|
||||
@AutoMap()
|
||||
monTime: string;
|
||||
|
||||
@AutoMap()
|
||||
tueTime: string;
|
||||
|
||||
@AutoMap()
|
||||
wedTime: string;
|
||||
|
||||
@AutoMap()
|
||||
thuTime: string;
|
||||
|
||||
@AutoMap()
|
||||
friTime: string;
|
||||
|
||||
@AutoMap()
|
||||
satTime: string;
|
||||
|
||||
@AutoMap()
|
||||
sunTime: string;
|
||||
|
||||
@AutoMap()
|
||||
monMargin: number;
|
||||
|
||||
@AutoMap()
|
||||
tueMargin: number;
|
||||
|
||||
@AutoMap()
|
||||
wedMargin: number;
|
||||
|
||||
@AutoMap()
|
||||
thuMargin: number;
|
||||
|
||||
@AutoMap()
|
||||
friMargin: number;
|
||||
|
||||
@AutoMap()
|
||||
satMargin: number;
|
||||
|
||||
@AutoMap()
|
||||
sunMargin: number;
|
||||
|
||||
@AutoMap()
|
||||
driverDuration: number;
|
||||
|
||||
@AutoMap()
|
||||
driverDistance: number;
|
||||
|
||||
@AutoMap()
|
||||
passengerDuration: number;
|
||||
|
||||
@AutoMap()
|
||||
passengerDistance: number;
|
||||
|
||||
@AutoMap()
|
||||
originType: number;
|
||||
|
||||
@AutoMap()
|
||||
destinationType: number;
|
||||
|
||||
@AutoMap()
|
||||
waypoints: [];
|
||||
|
||||
@AutoMap()
|
||||
direction: string;
|
||||
|
||||
@AutoMap()
|
||||
fwdAzimuth: number;
|
||||
|
||||
@AutoMap()
|
||||
backAzimuth: number;
|
||||
|
||||
@AutoMap()
|
||||
seatsDriver: number;
|
||||
|
||||
@AutoMap()
|
||||
seatsPassenger: number;
|
||||
|
||||
@AutoMap()
|
||||
seatsUsed: number;
|
||||
|
||||
@AutoMap()
|
||||
createdAt: string;
|
||||
|
||||
@AutoMap()
|
||||
updatedAt: string;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import { CommandHandler } from '@nestjs/cqrs';
|
||||
import { CreateAdCommand } from '../../commands/create-ad.command';
|
||||
import { Ad } from '../entities/ad';
|
||||
import { AdRepository } from '../../adapters/secondaries/ad.repository';
|
||||
|
||||
@CommandHandler(CreateAdCommand)
|
||||
export class CreateAdUseCase {
|
||||
constructor(private readonly adRepository: AdRepository) {}
|
||||
|
||||
async execute(command: CreateAdCommand): Promise<Ad> {
|
||||
try {
|
||||
return await this.adRepository.createAd(command.createAdRequest);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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: any) => {
|
||||
createMap(mapper, Ad, AdPresenter);
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { PrismaService } from './src/adapters/secondaries/prisma-service';
|
||||
import { MatcherRepository } from './src/domain/matcher-repository';
|
||||
import { AdRepository } from '../ad/adapters/secondaries/ad.repository';
|
||||
|
||||
@Module({
|
||||
providers: [PrismaService, MatcherRepository],
|
||||
exports: [PrismaService, MatcherRepository],
|
||||
providers: [PrismaService, AdRepository],
|
||||
exports: [PrismaService, AdRepository],
|
||||
})
|
||||
export class DatabaseModule {}
|
||||
|
|
|
@ -202,9 +202,9 @@ export abstract class PrismaRepository<T> implements IRepository<T> {
|
|||
|
||||
async createWithFields(fields: object): Promise<number> {
|
||||
try {
|
||||
const command = `INSERT INTO ${this._model} (${Object.keys(fields).join(
|
||||
',',
|
||||
)}) VALUES (${Object.values(fields).join(',')})`;
|
||||
const command = `INSERT INTO ${this._model} ("${Object.keys(fields).join(
|
||||
'","',
|
||||
)}") VALUES (${Object.values(fields).join(',')})`;
|
||||
return await this._prisma.$executeRawUnsafe(command);
|
||||
} catch (e) {
|
||||
if (e instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
|
@ -219,7 +219,7 @@ export abstract class PrismaRepository<T> implements IRepository<T> {
|
|||
}
|
||||
}
|
||||
|
||||
async updateWithFields(uuid: string, entity: Partial<T>): Promise<number> {
|
||||
async updateWithFields(uuid: string, entity: object): Promise<number> {
|
||||
entity['"updatedAt"'] = `to_timestamp(${Date.now()} / 1000.0)`;
|
||||
const values = Object.keys(entity).map((key) => `${key} = ${entity[key]}`);
|
||||
try {
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { IFindTimezone } from '../../domain/interfaces/timezone-finder.interface';
|
||||
import { find } from 'geo-tz';
|
||||
|
||||
@Injectable()
|
||||
export class GeoTimezoneFinder implements IFindTimezone {
|
||||
timezones = (lon: number, lat: number): Array<string> => find(lat, lon);
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { Geodesic as Geolib, GeodesicClass } from 'geographiclib-geodesic';
|
||||
import { IGeodesic } from '../../domain/interfaces/geodesic.interface';
|
||||
|
||||
@Injectable()
|
||||
export class Geodesic implements IGeodesic {
|
||||
private geod: GeodesicClass;
|
||||
|
||||
constructor() {
|
||||
this.geod = Geolib.WGS84;
|
||||
}
|
||||
|
||||
inverse = (
|
||||
lon1: number,
|
||||
lat1: number,
|
||||
lon2: number,
|
||||
lat2: number,
|
||||
): { azimuth: number; distance: number } => {
|
||||
const { azi2: azimuth, s12: distance } = this.geod.Inverse(
|
||||
lat1,
|
||||
lon1,
|
||||
lat2,
|
||||
lon2,
|
||||
);
|
||||
return { azimuth, distance };
|
||||
};
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
export interface IFindTimezone {
|
||||
timezones(lon: number, lat: number): Array<string>;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { GeoTimezoneFinder } from './adapters/secondaries/geo-timezone-finder';
|
||||
import { Geodesic } from './adapters/secondaries/geodesic';
|
||||
|
||||
@Module({
|
||||
providers: [GeoTimezoneFinder, Geodesic],
|
||||
exports: [GeoTimezoneFinder, Geodesic],
|
||||
})
|
||||
export class GeographyModule {}
|
|
@ -0,0 +1,14 @@
|
|||
import { GeoTimezoneFinder } from '../../adapters/secondaries/geo-timezone-finder';
|
||||
|
||||
describe('Geo TZ Finder', () => {
|
||||
it('should be defined', () => {
|
||||
const timezoneFinder: GeoTimezoneFinder = new GeoTimezoneFinder();
|
||||
expect(timezoneFinder).toBeDefined();
|
||||
});
|
||||
it('should get timezone for Nancy(France) as Europe/Paris', () => {
|
||||
const timezoneFinder: GeoTimezoneFinder = new GeoTimezoneFinder();
|
||||
const timezones = timezoneFinder.timezones(6.179373, 48.687913);
|
||||
expect(timezones.length).toBe(1);
|
||||
expect(timezones[0]).toBe('Europe/Paris');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
import { Geodesic } from '../../adapters/secondaries/geodesic';
|
||||
|
||||
describe('Matcher geodesic', () => {
|
||||
it('should be defined', () => {
|
||||
const geodesic: Geodesic = new Geodesic();
|
||||
expect(geodesic).toBeDefined();
|
||||
});
|
||||
it('should get inverse values', () => {
|
||||
const geodesic: Geodesic = new Geodesic();
|
||||
const inv = geodesic.inverse(0, 0, 1, 1);
|
||||
expect(Math.round(inv.azimuth)).toBe(45);
|
||||
expect(Math.round(inv.distance)).toBe(156900);
|
||||
});
|
||||
});
|
|
@ -4,7 +4,7 @@ import {
|
|||
HealthIndicator,
|
||||
HealthIndicatorResult,
|
||||
} from '@nestjs/terminus';
|
||||
import { AdRepository } from '../../../matcher/adapters/secondaries/ad.repository';
|
||||
import { AdRepository } from '../../../ad/adapters/secondaries/ad.repository';
|
||||
|
||||
@Injectable()
|
||||
export class PrismaHealthIndicatorUseCase extends HealthIndicator {
|
||||
|
|
|
@ -7,7 +7,7 @@ import { TerminusModule } from '@nestjs/terminus';
|
|||
import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import { Messager } from './adapters/secondaries/messager';
|
||||
import { AdRepository } from '../matcher/adapters/secondaries/ad.repository';
|
||||
import { AdRepository } from '../ad/adapters/secondaries/ad.repository';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { PrismaHealthIndicatorUseCase } from '../../domain/usecases/prisma.health-indicator.usecase';
|
||||
import { HealthCheckError, HealthIndicatorResult } from '@nestjs/terminus';
|
||||
import { AdRepository } from '../../../matcher/adapters/secondaries/ad.repository';
|
||||
import { AdRepository } from '../../../ad/adapters/secondaries/ad.repository';
|
||||
import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library';
|
||||
|
||||
const mockAdRepository = {
|
||||
|
|
|
@ -11,6 +11,7 @@ import { MatchPresenter } from '../secondaries/match.presenter';
|
|||
import { DefaultParamsProvider } from '../secondaries/default-params.provider';
|
||||
import { GeorouterCreator } from '../secondaries/georouter-creator';
|
||||
import { Match } from '../../domain/entities/ecosystem/match';
|
||||
import { GeoTimezoneFinder } from '../../../geography/adapters/secondaries/geo-timezone-finder';
|
||||
|
||||
@UsePipes(
|
||||
new RpcValidationPipe({
|
||||
|
@ -21,20 +22,22 @@ import { Match } from '../../domain/entities/ecosystem/match';
|
|||
@Controller()
|
||||
export class MatcherController {
|
||||
constructor(
|
||||
private readonly _queryBus: QueryBus,
|
||||
private readonly _defaultParamsProvider: DefaultParamsProvider,
|
||||
private readonly queryBus: QueryBus,
|
||||
private readonly defaultParamsProvider: DefaultParamsProvider,
|
||||
@InjectMapper() private readonly _mapper: Mapper,
|
||||
private readonly _georouterCreator: GeorouterCreator,
|
||||
private readonly georouterCreator: GeorouterCreator,
|
||||
private readonly timezoneFinder: GeoTimezoneFinder,
|
||||
) {}
|
||||
|
||||
@GrpcMethod('MatcherService', 'Match')
|
||||
async match(data: MatchRequest): Promise<ICollection<Match>> {
|
||||
try {
|
||||
const matchCollection = await this._queryBus.execute(
|
||||
const matchCollection = await this.queryBus.execute(
|
||||
new MatchQuery(
|
||||
data,
|
||||
this._defaultParamsProvider.getParams(),
|
||||
this._georouterCreator,
|
||||
this.defaultParamsProvider.getParams(),
|
||||
this.georouterCreator,
|
||||
this.timezoneFinder,
|
||||
),
|
||||
);
|
||||
return Promise.resolve({
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { MatcherRepository } from '../../../database/src/domain/matcher-repository';
|
||||
import { Ad } from '../../domain/entities/ecosystem/ad';
|
||||
|
||||
@Injectable()
|
||||
export class AdRepository extends MatcherRepository<Ad> {
|
||||
protected _model = 'ad';
|
||||
}
|
|
@ -1,27 +1,16 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { IGeodesic } from '../../domain/interfaces/geodesic.interface';
|
||||
import { Geodesic, GeodesicClass } from 'geographiclib-geodesic';
|
||||
import { Geodesic } from '../../../geography/adapters/secondaries/geodesic';
|
||||
import { IGeodesic } from '../../../geography/domain/interfaces/geodesic.interface';
|
||||
|
||||
@Injectable()
|
||||
export class MatcherGeodesic implements IGeodesic {
|
||||
private geod: GeodesicClass;
|
||||
|
||||
constructor() {
|
||||
this.geod = Geodesic.WGS84;
|
||||
}
|
||||
constructor(private readonly geodesic: Geodesic) {}
|
||||
|
||||
inverse = (
|
||||
lon1: number,
|
||||
lat1: number,
|
||||
lon2: number,
|
||||
lat2: number,
|
||||
): { azimuth: number; distance: number } => {
|
||||
const { azi2: azimuth, s12: distance } = this.geod.Inverse(
|
||||
lat1,
|
||||
lon1,
|
||||
lat2,
|
||||
lon2,
|
||||
);
|
||||
return { azimuth, distance };
|
||||
};
|
||||
): { azimuth: number; distance: number } =>
|
||||
this.geodesic.inverse(lon1, lat1, lon2, lat2);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import { Path } from '../../domain/types/path.type';
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { catchError, lastValueFrom, map } from 'rxjs';
|
||||
import { AxiosError, AxiosResponse } from 'axios';
|
||||
import { IGeodesic } from '../../domain/interfaces/geodesic.interface';
|
||||
import { IGeodesic } from '../../../geography/domain/interfaces/geodesic.interface';
|
||||
import { NamedRoute } from '../../domain/entities/ecosystem/named-route';
|
||||
import { Route } from '../../domain/entities/ecosystem/route';
|
||||
import { SpacetimePoint } from '../../domain/entities/ecosystem/spacetime-point';
|
||||
|
|
|
@ -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): Array<string> =>
|
||||
this.geoTimezoneFinder.timezones(lon, lat);
|
||||
}
|
|
@ -5,7 +5,6 @@ import {
|
|||
import { IRequestGeography } from '../../interfaces/geography-request.interface';
|
||||
import { PointType } from '../../types/geography.enum';
|
||||
import { Point } from '../../types/point.type';
|
||||
import { find } from 'geo-tz';
|
||||
import { Route } from './route';
|
||||
import { Role } from '../../types/role.enum';
|
||||
import { IGeorouter } from '../../interfaces/georouter.interface';
|
||||
|
@ -14,6 +13,8 @@ import { Actor } from './actor';
|
|||
import { Person } from './person';
|
||||
import { Step } from '../../types/step.enum';
|
||||
import { Path } from '../../types/path.type';
|
||||
import { IFindTimezone } from '../../../../geography/domain/interfaces/timezone-finder.interface';
|
||||
import { Timezoner } from './timezoner';
|
||||
|
||||
export class Geography {
|
||||
private geographyRequest: IRequestGeography;
|
||||
|
@ -24,10 +25,11 @@ export class Geography {
|
|||
timezones: Array<string>;
|
||||
driverRoute: Route;
|
||||
passengerRoute: Route;
|
||||
timezoneFinder: IFindTimezone;
|
||||
|
||||
constructor(
|
||||
geographyRequest: IRequestGeography,
|
||||
defaultTimezone: string,
|
||||
timezoner: Timezoner,
|
||||
person: Person,
|
||||
) {
|
||||
this.geographyRequest = geographyRequest;
|
||||
|
@ -35,7 +37,8 @@ export class Geography {
|
|||
this.points = [];
|
||||
this.originType = undefined;
|
||||
this.destinationType = undefined;
|
||||
this.timezones = [defaultTimezone];
|
||||
this.timezones = [timezoner.timezone];
|
||||
this.timezoneFinder = timezoner.finder;
|
||||
}
|
||||
|
||||
init = (): void => {
|
||||
|
@ -147,7 +150,7 @@ export class Geography {
|
|||
};
|
||||
|
||||
private setTimezones = (): void => {
|
||||
this.timezones = find(
|
||||
this.timezones = this.timezoneFinder.timezones(
|
||||
this.geographyRequest.waypoints[0].lat,
|
||||
this.geographyRequest.waypoints[0].lon,
|
||||
);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { IGeodesic } from '../../interfaces/geodesic.interface';
|
||||
import { IGeodesic } from '../../../../geography/domain/interfaces/geodesic.interface';
|
||||
import { Point } from '../../types/point.type';
|
||||
import { SpacetimePoint } from './spacetime-point';
|
||||
import { Waypoint } from './waypoint';
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
import { IFindTimezone } from '../../../../geography/domain/interfaces/timezone-finder.interface';
|
||||
|
||||
export type Timezoner = {
|
||||
timezone: string;
|
||||
finder: IFindTimezone;
|
||||
};
|
|
@ -5,7 +5,6 @@ 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';
|
||||
import { CacheModule } from '@nestjs/cache-manager';
|
||||
|
@ -17,9 +16,13 @@ import { HttpModule } from '@nestjs/axios';
|
|||
import { MatcherGeodesic } from './adapters/secondaries/geodesic';
|
||||
import { Matcher } from './domain/entities/engine/matcher';
|
||||
import { AlgorithmFactoryCreator } from './domain/entities/engine/factory/algorithm-factory-creator';
|
||||
import { TimezoneFinder } from './adapters/secondaries/timezone-finder';
|
||||
import { GeoTimezoneFinder } from '../geography/adapters/secondaries/geo-timezone-finder';
|
||||
import { GeographyModule } from '../geography/geography.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
GeographyModule,
|
||||
DatabaseModule,
|
||||
CqrsModule,
|
||||
HttpModule,
|
||||
|
@ -53,14 +56,15 @@ import { AlgorithmFactoryCreator } from './domain/entities/engine/factory/algori
|
|||
controllers: [MatcherController],
|
||||
providers: [
|
||||
MatchProfile,
|
||||
AdRepository,
|
||||
Messager,
|
||||
DefaultParamsProvider,
|
||||
MatchUseCase,
|
||||
GeorouterCreator,
|
||||
MatcherGeodesic,
|
||||
TimezoneFinder,
|
||||
Matcher,
|
||||
AlgorithmFactoryCreator,
|
||||
GeoTimezoneFinder,
|
||||
],
|
||||
exports: [],
|
||||
})
|
||||
|
|
|
@ -8,11 +8,12 @@ import { Time } from '../domain/entities/ecosystem/time';
|
|||
import { IDefaultParams } from '../domain/types/default-params.type';
|
||||
import { IGeorouter } from '../domain/interfaces/georouter.interface';
|
||||
import { ICreateGeorouter } from '../domain/interfaces/georouter-creator.interface';
|
||||
import { IFindTimezone } from '../../geography/domain/interfaces/timezone-finder.interface';
|
||||
|
||||
export class MatchQuery {
|
||||
private readonly _matchRequest: MatchRequest;
|
||||
private readonly _defaultParams: IDefaultParams;
|
||||
private readonly _georouterCreator: ICreateGeorouter;
|
||||
private readonly matchRequest: MatchRequest;
|
||||
private readonly defaultParams: IDefaultParams;
|
||||
private readonly georouterCreator: ICreateGeorouter;
|
||||
person: Person;
|
||||
roles: Array<Role>;
|
||||
time: Time;
|
||||
|
@ -21,83 +22,89 @@ export class MatchQuery {
|
|||
requirement: Requirement;
|
||||
algorithmSettings: AlgorithmSettings;
|
||||
georouter: IGeorouter;
|
||||
timezoneFinder: IFindTimezone;
|
||||
|
||||
constructor(
|
||||
matchRequest: MatchRequest,
|
||||
defaultParams: IDefaultParams,
|
||||
georouterCreator: ICreateGeorouter,
|
||||
timezoneFinder: IFindTimezone,
|
||||
) {
|
||||
this._matchRequest = matchRequest;
|
||||
this._defaultParams = defaultParams;
|
||||
this._georouterCreator = georouterCreator;
|
||||
this._setPerson();
|
||||
this._setRoles();
|
||||
this._setTime();
|
||||
this._setGeography();
|
||||
this._setRequirement();
|
||||
this._setAlgorithmSettings();
|
||||
this._setExclusions();
|
||||
this.matchRequest = matchRequest;
|
||||
this.defaultParams = defaultParams;
|
||||
this.georouterCreator = georouterCreator;
|
||||
this.timezoneFinder = timezoneFinder;
|
||||
this.setPerson();
|
||||
this.setRoles();
|
||||
this.setTime();
|
||||
this.setGeography();
|
||||
this.setRequirement();
|
||||
this.setAlgorithmSettings();
|
||||
this.setExclusions();
|
||||
}
|
||||
|
||||
createRoutes = (): void => {
|
||||
this.geography.createRoutes(this.roles, this.algorithmSettings.georouter);
|
||||
};
|
||||
|
||||
_setPerson = (): void => {
|
||||
private setPerson = (): void => {
|
||||
this.person = new Person(
|
||||
this._matchRequest,
|
||||
this._defaultParams.DEFAULT_IDENTIFIER,
|
||||
this._defaultParams.MARGIN_DURATION,
|
||||
this.matchRequest,
|
||||
this.defaultParams.DEFAULT_IDENTIFIER,
|
||||
this.defaultParams.MARGIN_DURATION,
|
||||
);
|
||||
this.person.init();
|
||||
};
|
||||
|
||||
_setRoles = (): void => {
|
||||
private setRoles = (): void => {
|
||||
this.roles = [];
|
||||
if (this._matchRequest.driver) this.roles.push(Role.DRIVER);
|
||||
if (this._matchRequest.passenger) this.roles.push(Role.PASSENGER);
|
||||
if (this.matchRequest.driver) this.roles.push(Role.DRIVER);
|
||||
if (this.matchRequest.passenger) this.roles.push(Role.PASSENGER);
|
||||
if (this.roles.length == 0) this.roles.push(Role.PASSENGER);
|
||||
};
|
||||
|
||||
_setTime = (): void => {
|
||||
private setTime = (): void => {
|
||||
this.time = new Time(
|
||||
this._matchRequest,
|
||||
this._defaultParams.MARGIN_DURATION,
|
||||
this._defaultParams.VALIDITY_DURATION,
|
||||
this.matchRequest,
|
||||
this.defaultParams.MARGIN_DURATION,
|
||||
this.defaultParams.VALIDITY_DURATION,
|
||||
);
|
||||
this.time.init();
|
||||
};
|
||||
|
||||
_setGeography = (): void => {
|
||||
private setGeography = (): void => {
|
||||
this.geography = new Geography(
|
||||
this._matchRequest,
|
||||
this._defaultParams.DEFAULT_TIMEZONE,
|
||||
this.matchRequest,
|
||||
{
|
||||
timezone: this.defaultParams.DEFAULT_TIMEZONE,
|
||||
finder: this.timezoneFinder,
|
||||
},
|
||||
this.person,
|
||||
);
|
||||
this.geography.init();
|
||||
};
|
||||
|
||||
_setRequirement = (): void => {
|
||||
private setRequirement = (): void => {
|
||||
this.requirement = new Requirement(
|
||||
this._matchRequest,
|
||||
this._defaultParams.DEFAULT_SEATS,
|
||||
this.matchRequest,
|
||||
this.defaultParams.DEFAULT_SEATS,
|
||||
);
|
||||
};
|
||||
|
||||
_setAlgorithmSettings = (): void => {
|
||||
private setAlgorithmSettings = (): void => {
|
||||
this.algorithmSettings = new AlgorithmSettings(
|
||||
this._matchRequest,
|
||||
this._defaultParams.DEFAULT_ALGORITHM_SETTINGS,
|
||||
this.matchRequest,
|
||||
this.defaultParams.DEFAULT_ALGORITHM_SETTINGS,
|
||||
this.time.frequency,
|
||||
this._georouterCreator,
|
||||
this.georouterCreator,
|
||||
);
|
||||
};
|
||||
|
||||
_setExclusions = (): void => {
|
||||
private setExclusions = (): void => {
|
||||
this.exclusions = [];
|
||||
if (this._matchRequest.identifier)
|
||||
this.exclusions.push(this._matchRequest.identifier);
|
||||
if (this._matchRequest.exclusions)
|
||||
this.exclusions.push(...this._matchRequest.exclusions);
|
||||
if (this.matchRequest.identifier)
|
||||
this.exclusions.push(this.matchRequest.identifier);
|
||||
if (this.matchRequest.exclusions)
|
||||
this.exclusions.push(...this.matchRequest.exclusions);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { TimezoneFinder } from '../../../../adapters/secondaries/timezone-finder';
|
||||
import { GeoTimezoneFinder } from '../../../../../geography/adapters/secondaries/geo-timezone-finder';
|
||||
|
||||
const mockGeoTimezoneFinder = {
|
||||
timezones: jest.fn().mockImplementation(() => ['Europe/Paris']),
|
||||
};
|
||||
|
||||
describe('Geo TZ Finder', () => {
|
||||
let timezoneFinder: TimezoneFinder;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
imports: [],
|
||||
providers: [
|
||||
TimezoneFinder,
|
||||
{
|
||||
provide: GeoTimezoneFinder,
|
||||
useValue: mockGeoTimezoneFinder,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
timezoneFinder = module.get<TimezoneFinder>(TimezoneFinder);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(timezoneFinder).toBeDefined();
|
||||
});
|
||||
it('should get timezone for Nancy(France) as Europe/Paris', () => {
|
||||
const timezones = timezoneFinder.timezones(6.179373, 48.687913);
|
||||
expect(timezones.length).toBe(1);
|
||||
expect(timezones[0]).toBe('Europe/Paris');
|
||||
});
|
||||
});
|
|
@ -1,14 +1,38 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { MatcherGeodesic } from '../../../../adapters/secondaries/geodesic';
|
||||
import { Geodesic } from '../../../../../geography/adapters/secondaries/geodesic';
|
||||
|
||||
const mockGeodesic = {
|
||||
inverse: jest.fn().mockImplementation(() => ({
|
||||
azimuth: 45,
|
||||
distance: 50000,
|
||||
})),
|
||||
};
|
||||
|
||||
describe('Matcher geodesic', () => {
|
||||
let matcherGeodesic: MatcherGeodesic;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
imports: [],
|
||||
providers: [
|
||||
MatcherGeodesic,
|
||||
{
|
||||
provide: Geodesic,
|
||||
useValue: mockGeodesic,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
matcherGeodesic = module.get<MatcherGeodesic>(MatcherGeodesic);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
const geodesic: MatcherGeodesic = new MatcherGeodesic();
|
||||
expect(geodesic).toBeDefined();
|
||||
expect(matcherGeodesic).toBeDefined();
|
||||
});
|
||||
it('should get inverse values', () => {
|
||||
const geodesic: MatcherGeodesic = new MatcherGeodesic();
|
||||
const inv = geodesic.inverse(0, 0, 1, 1);
|
||||
const inv = matcherGeodesic.inverse(0, 0, 1, 1);
|
||||
expect(Math.round(inv.azimuth)).toBe(45);
|
||||
expect(Math.round(inv.distance)).toBe(156900);
|
||||
expect(Math.round(inv.distance)).toBe(50000);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
import { Role } from '../../../../domain/types/role.enum';
|
||||
import { NamedRoute } from '../../../../domain/entities/ecosystem/named-route';
|
||||
import { Route } from '../../../../domain/entities/ecosystem/route';
|
||||
import { IGeodesic } from '../../../../domain/interfaces/geodesic.interface';
|
||||
import { IGeodesic } from '../../../../../geography/domain/interfaces/geodesic.interface';
|
||||
import { PointType } from '../../../../domain/types/geography.enum';
|
||||
|
||||
const person: Person = new Person(
|
||||
|
@ -65,6 +65,10 @@ const mockGeorouter = {
|
|||
}),
|
||||
};
|
||||
|
||||
const mockTimezoneFinder = {
|
||||
timezones: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
describe('Geography entity', () => {
|
||||
it('should be defined', () => {
|
||||
const geography = new Geography(
|
||||
|
@ -80,7 +84,10 @@ describe('Geography entity', () => {
|
|||
},
|
||||
],
|
||||
},
|
||||
'Europe/Paris',
|
||||
{
|
||||
timezone: 'Europe/Paris',
|
||||
finder: mockTimezoneFinder,
|
||||
},
|
||||
person,
|
||||
);
|
||||
expect(geography).toBeDefined();
|
||||
|
@ -103,7 +110,10 @@ describe('Geography entity', () => {
|
|||
},
|
||||
],
|
||||
},
|
||||
'Europe/Paris',
|
||||
{
|
||||
timezone: 'Europe/Paris',
|
||||
finder: mockTimezoneFinder,
|
||||
},
|
||||
person,
|
||||
);
|
||||
geography.init();
|
||||
|
@ -115,7 +125,10 @@ describe('Geography entity', () => {
|
|||
{
|
||||
waypoints: [],
|
||||
},
|
||||
'Europe/Paris',
|
||||
{
|
||||
timezone: 'Europe/Paris',
|
||||
finder: mockTimezoneFinder,
|
||||
},
|
||||
person,
|
||||
);
|
||||
expect(() => geography.init()).toThrow();
|
||||
|
@ -130,7 +143,10 @@ describe('Geography entity', () => {
|
|||
},
|
||||
],
|
||||
},
|
||||
'Europe/Paris',
|
||||
{
|
||||
timezone: 'Europe/Paris',
|
||||
finder: mockTimezoneFinder,
|
||||
},
|
||||
person,
|
||||
);
|
||||
expect(() => geography.init()).toThrow();
|
||||
|
@ -149,7 +165,10 @@ describe('Geography entity', () => {
|
|||
},
|
||||
],
|
||||
},
|
||||
'Europe/Paris',
|
||||
{
|
||||
timezone: 'Europe/Paris',
|
||||
finder: mockTimezoneFinder,
|
||||
},
|
||||
person,
|
||||
);
|
||||
expect(() => geography.init()).toThrow();
|
||||
|
@ -168,7 +187,10 @@ describe('Geography entity', () => {
|
|||
},
|
||||
],
|
||||
},
|
||||
'Europe/Paris',
|
||||
{
|
||||
timezone: 'Europe/Paris',
|
||||
finder: mockTimezoneFinder,
|
||||
},
|
||||
person,
|
||||
);
|
||||
expect(() => geography.init()).toThrow();
|
||||
|
@ -190,7 +212,10 @@ describe('Geography entity', () => {
|
|||
},
|
||||
],
|
||||
},
|
||||
'Europe/Paris',
|
||||
{
|
||||
timezone: 'Europe/Paris',
|
||||
finder: mockTimezoneFinder,
|
||||
},
|
||||
person,
|
||||
);
|
||||
geography.init();
|
||||
|
@ -220,7 +245,10 @@ describe('Geography entity', () => {
|
|||
},
|
||||
],
|
||||
},
|
||||
'Europe/Paris',
|
||||
{
|
||||
timezone: 'Europe/Paris',
|
||||
finder: mockTimezoneFinder,
|
||||
},
|
||||
person,
|
||||
);
|
||||
geography.init();
|
||||
|
@ -246,7 +274,10 @@ describe('Geography entity', () => {
|
|||
},
|
||||
],
|
||||
},
|
||||
'Europe/Paris',
|
||||
{
|
||||
timezone: 'Europe/Paris',
|
||||
finder: mockTimezoneFinder,
|
||||
},
|
||||
person,
|
||||
);
|
||||
geography.init();
|
||||
|
@ -269,7 +300,10 @@ describe('Geography entity', () => {
|
|||
},
|
||||
],
|
||||
},
|
||||
'Europe/Paris',
|
||||
{
|
||||
timezone: 'Europe/Paris',
|
||||
finder: mockTimezoneFinder,
|
||||
},
|
||||
person,
|
||||
);
|
||||
geography.init();
|
||||
|
|
|
@ -9,6 +9,10 @@ const mockGeorouterCreator = {
|
|||
create: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const mockTimezoneFinder = {
|
||||
timezones: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const defaultParams: IDefaultParams = {
|
||||
DEFAULT_IDENTIFIER: 0,
|
||||
MARGIN_DURATION: 900,
|
||||
|
@ -46,6 +50,7 @@ const matchQuery: MatchQuery = new MatchQuery(
|
|||
matchRequest,
|
||||
defaultParams,
|
||||
mockGeorouterCreator,
|
||||
mockTimezoneFinder,
|
||||
);
|
||||
|
||||
describe('AlgorithmFactoryCreator', () => {
|
||||
|
|
|
@ -11,6 +11,10 @@ const mockGeorouterCreator = {
|
|||
create: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const mockTimezoneFinder = {
|
||||
timezones: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const defaultParams: IDefaultParams = {
|
||||
DEFAULT_IDENTIFIER: 0,
|
||||
MARGIN_DURATION: 900,
|
||||
|
@ -48,6 +52,7 @@ const matchQuery: MatchQuery = new MatchQuery(
|
|||
matchRequest,
|
||||
defaultParams,
|
||||
mockGeorouterCreator,
|
||||
mockTimezoneFinder,
|
||||
);
|
||||
|
||||
class FakeSelector extends Selector {
|
||||
|
|
|
@ -9,6 +9,10 @@ const mockGeorouterCreator = {
|
|||
create: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const mockTimezoneFinder = {
|
||||
timezones: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const defaultParams: IDefaultParams = {
|
||||
DEFAULT_IDENTIFIER: 0,
|
||||
MARGIN_DURATION: 900,
|
||||
|
@ -46,6 +50,7 @@ const matchQuery: MatchQuery = new MatchQuery(
|
|||
matchRequest,
|
||||
defaultParams,
|
||||
mockGeorouterCreator,
|
||||
mockTimezoneFinder,
|
||||
);
|
||||
|
||||
describe('ClassicAlgorithmFactory', () => {
|
||||
|
|
|
@ -9,6 +9,10 @@ const mockGeorouterCreator = {
|
|||
create: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const mockTimezoneFinder = {
|
||||
timezones: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const defaultParams: IDefaultParams = {
|
||||
DEFAULT_IDENTIFIER: 0,
|
||||
MARGIN_DURATION: 900,
|
||||
|
@ -46,6 +50,7 @@ const matchQuery: MatchQuery = new MatchQuery(
|
|||
matchRequest,
|
||||
defaultParams,
|
||||
mockGeorouterCreator,
|
||||
mockTimezoneFinder,
|
||||
);
|
||||
|
||||
describe('ClassicGeoFilter', () => {
|
||||
|
|
|
@ -9,6 +9,10 @@ const mockGeorouterCreator = {
|
|||
create: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const mockTimezoneFinder = {
|
||||
timezones: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const defaultParams: IDefaultParams = {
|
||||
DEFAULT_IDENTIFIER: 0,
|
||||
MARGIN_DURATION: 900,
|
||||
|
@ -46,6 +50,7 @@ const matchQuery: MatchQuery = new MatchQuery(
|
|||
matchRequest,
|
||||
defaultParams,
|
||||
mockGeorouterCreator,
|
||||
mockTimezoneFinder,
|
||||
);
|
||||
|
||||
describe('ClassicTimeFilter', () => {
|
||||
|
|
|
@ -9,6 +9,10 @@ const mockGeorouterCreator = {
|
|||
create: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const mockTimezoneFinder = {
|
||||
timezones: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const defaultParams: IDefaultParams = {
|
||||
DEFAULT_IDENTIFIER: 0,
|
||||
MARGIN_DURATION: 900,
|
||||
|
@ -46,6 +50,7 @@ const matchQuery: MatchQuery = new MatchQuery(
|
|||
matchRequest,
|
||||
defaultParams,
|
||||
mockGeorouterCreator,
|
||||
mockTimezoneFinder,
|
||||
);
|
||||
|
||||
describe('ClassicWaypointCompleter', () => {
|
||||
|
|
|
@ -9,6 +9,10 @@ const mockGeorouterCreator = {
|
|||
create: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const mockTimezoneFinder = {
|
||||
timezones: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const defaultParams: IDefaultParams = {
|
||||
DEFAULT_IDENTIFIER: 0,
|
||||
MARGIN_DURATION: 900,
|
||||
|
@ -46,6 +50,7 @@ const matchQuery: MatchQuery = new MatchQuery(
|
|||
matchRequest,
|
||||
defaultParams,
|
||||
mockGeorouterCreator,
|
||||
mockTimezoneFinder,
|
||||
);
|
||||
|
||||
describe('ClassicSelector', () => {
|
||||
|
|
|
@ -9,6 +9,10 @@ const mockGeorouterCreator = {
|
|||
create: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const mockTimezoneFinder = {
|
||||
timezones: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const defaultParams: IDefaultParams = {
|
||||
DEFAULT_IDENTIFIER: 0,
|
||||
MARGIN_DURATION: 900,
|
||||
|
@ -46,6 +50,7 @@ const matchQuery: MatchQuery = new MatchQuery(
|
|||
matchRequest,
|
||||
defaultParams,
|
||||
mockGeorouterCreator,
|
||||
mockTimezoneFinder,
|
||||
);
|
||||
|
||||
class FakeCompleter extends Completer {
|
||||
|
|
|
@ -9,6 +9,10 @@ const mockGeorouterCreator = {
|
|||
create: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const mockTimezoneFinder = {
|
||||
timezones: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const defaultParams: IDefaultParams = {
|
||||
DEFAULT_IDENTIFIER: 0,
|
||||
MARGIN_DURATION: 900,
|
||||
|
@ -46,6 +50,7 @@ const matchQuery: MatchQuery = new MatchQuery(
|
|||
matchRequest,
|
||||
defaultParams,
|
||||
mockGeorouterCreator,
|
||||
mockTimezoneFinder,
|
||||
);
|
||||
|
||||
class FakeFilter extends Filter {
|
||||
|
|
|
@ -9,6 +9,10 @@ const mockGeorouterCreator = {
|
|||
create: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const mockTimezoneFinder = {
|
||||
timezones: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const defaultParams: IDefaultParams = {
|
||||
DEFAULT_IDENTIFIER: 0,
|
||||
MARGIN_DURATION: 900,
|
||||
|
@ -46,6 +50,7 @@ const matchQuery: MatchQuery = new MatchQuery(
|
|||
matchRequest,
|
||||
defaultParams,
|
||||
mockGeorouterCreator,
|
||||
mockTimezoneFinder,
|
||||
);
|
||||
|
||||
describe('JourneyCompleter', () => {
|
||||
|
|
|
@ -21,6 +21,10 @@ const mockGeorouterCreator = {
|
|||
create: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const mockTimezoneFinder = {
|
||||
timezones: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const defaultParams: IDefaultParams = {
|
||||
DEFAULT_IDENTIFIER: 0,
|
||||
MARGIN_DURATION: 900,
|
||||
|
@ -58,6 +62,7 @@ const matchQuery: MatchQuery = new MatchQuery(
|
|||
matchRequest,
|
||||
defaultParams,
|
||||
mockGeorouterCreator,
|
||||
mockTimezoneFinder,
|
||||
);
|
||||
|
||||
describe('Matcher', () => {
|
||||
|
|
|
@ -9,6 +9,10 @@ const mockGeorouterCreator = {
|
|||
create: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const mockTimezoneFinder = {
|
||||
timezones: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const defaultParams: IDefaultParams = {
|
||||
DEFAULT_IDENTIFIER: 0,
|
||||
MARGIN_DURATION: 900,
|
||||
|
@ -46,6 +50,7 @@ const matchQuery: MatchQuery = new MatchQuery(
|
|||
matchRequest,
|
||||
defaultParams,
|
||||
mockGeorouterCreator,
|
||||
mockTimezoneFinder,
|
||||
);
|
||||
|
||||
class FakeProcessor extends Processor {
|
||||
|
|
|
@ -9,6 +9,10 @@ const mockGeorouterCreator = {
|
|||
create: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const mockTimezoneFinder = {
|
||||
timezones: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const defaultParams: IDefaultParams = {
|
||||
DEFAULT_IDENTIFIER: 0,
|
||||
MARGIN_DURATION: 900,
|
||||
|
@ -46,6 +50,7 @@ const matchQuery: MatchQuery = new MatchQuery(
|
|||
matchRequest,
|
||||
defaultParams,
|
||||
mockGeorouterCreator,
|
||||
mockTimezoneFinder,
|
||||
);
|
||||
|
||||
describe('RouteCompleter', () => {
|
||||
|
|
|
@ -9,6 +9,10 @@ const mockGeorouterCreator = {
|
|||
create: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const mockTimezoneFinder = {
|
||||
timezones: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const defaultParams: IDefaultParams = {
|
||||
DEFAULT_IDENTIFIER: 0,
|
||||
MARGIN_DURATION: 900,
|
||||
|
@ -46,6 +50,7 @@ const matchQuery: MatchQuery = new MatchQuery(
|
|||
matchRequest,
|
||||
defaultParams,
|
||||
mockGeorouterCreator,
|
||||
mockTimezoneFinder,
|
||||
);
|
||||
|
||||
class FakeSelector extends Selector {
|
||||
|
|
|
@ -34,6 +34,10 @@ const mockGeorouterCreator = {
|
|||
create: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const mockTimezoneFinder = {
|
||||
timezones: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const defaultParams: IDefaultParams = {
|
||||
DEFAULT_IDENTIFIER: 0,
|
||||
MARGIN_DURATION: 900,
|
||||
|
@ -97,7 +101,12 @@ describe('MatchUseCase', () => {
|
|||
describe('execute', () => {
|
||||
it('should return matches', async () => {
|
||||
const matches = await matchUseCase.execute(
|
||||
new MatchQuery(matchRequest, defaultParams, mockGeorouterCreator),
|
||||
new MatchQuery(
|
||||
matchRequest,
|
||||
defaultParams,
|
||||
mockGeorouterCreator,
|
||||
mockTimezoneFinder,
|
||||
),
|
||||
);
|
||||
expect(matches.total).toBe(3);
|
||||
});
|
||||
|
@ -105,7 +114,12 @@ describe('MatchUseCase', () => {
|
|||
it('should throw an exception when error occurs', async () => {
|
||||
await expect(
|
||||
matchUseCase.execute(
|
||||
new MatchQuery(matchRequest, defaultParams, mockGeorouterCreator),
|
||||
new MatchQuery(
|
||||
matchRequest,
|
||||
defaultParams,
|
||||
mockGeorouterCreator,
|
||||
mockTimezoneFinder,
|
||||
),
|
||||
),
|
||||
).rejects.toBeInstanceOf(MatcherException);
|
||||
});
|
||||
|
|
|
@ -30,6 +30,10 @@ const mockGeorouterCreator = {
|
|||
create: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const mockTimezoneFinder = {
|
||||
timezones: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
describe('Match query', () => {
|
||||
it('should be defined', () => {
|
||||
const matchRequest: MatchRequest = new MatchRequest();
|
||||
|
@ -48,6 +52,7 @@ describe('Match query', () => {
|
|||
matchRequest,
|
||||
defaultParams,
|
||||
mockGeorouterCreator,
|
||||
mockTimezoneFinder,
|
||||
);
|
||||
expect(matchQuery).toBeDefined();
|
||||
});
|
||||
|
@ -71,6 +76,7 @@ describe('Match query', () => {
|
|||
matchRequest,
|
||||
defaultParams,
|
||||
mockGeorouterCreator,
|
||||
mockTimezoneFinder,
|
||||
);
|
||||
expect(matchQuery.exclusions.length).toBe(4);
|
||||
});
|
||||
|
@ -93,6 +99,7 @@ describe('Match query', () => {
|
|||
matchRequest,
|
||||
defaultParams,
|
||||
mockGeorouterCreator,
|
||||
mockTimezoneFinder,
|
||||
);
|
||||
expect(matchQuery.roles).toEqual([Role.DRIVER]);
|
||||
});
|
||||
|
@ -115,6 +122,7 @@ describe('Match query', () => {
|
|||
matchRequest,
|
||||
defaultParams,
|
||||
mockGeorouterCreator,
|
||||
mockTimezoneFinder,
|
||||
);
|
||||
expect(matchQuery.roles).toEqual([Role.PASSENGER]);
|
||||
});
|
||||
|
@ -138,6 +146,7 @@ describe('Match query', () => {
|
|||
matchRequest,
|
||||
defaultParams,
|
||||
mockGeorouterCreator,
|
||||
mockTimezoneFinder,
|
||||
);
|
||||
expect(matchQuery.roles.length).toBe(2);
|
||||
expect(matchQuery.roles).toContain(Role.PASSENGER);
|
||||
|
@ -163,6 +172,7 @@ describe('Match query', () => {
|
|||
matchRequest,
|
||||
defaultParams,
|
||||
mockGeorouterCreator,
|
||||
mockTimezoneFinder,
|
||||
);
|
||||
expect(matchQuery.requirement.seatsDriver).toBe(1);
|
||||
expect(matchQuery.requirement.seatsPassenger).toBe(2);
|
||||
|
@ -194,6 +204,7 @@ describe('Match query', () => {
|
|||
matchRequest,
|
||||
defaultParams,
|
||||
mockGeorouterCreator,
|
||||
mockTimezoneFinder,
|
||||
);
|
||||
expect(matchQuery.algorithmSettings.algorithmType).toBe(
|
||||
AlgorithmType.CLASSIC,
|
||||
|
|
Loading…
Reference in New Issue