matcher with only db selection
This commit is contained in:
parent
e0030aba73
commit
98530af14a
|
@ -54,7 +54,7 @@ export class MatchQueryHandler implements IQueryHandler {
|
|||
maxDetourDurationRatio: this._defaultParams.MAX_DETOUR_DURATION_RATIO,
|
||||
})
|
||||
.setDatesAndSchedule(this.datetimeTransformer);
|
||||
await query.setRoutes(this.routeProvider);
|
||||
await query.setRoute(this.routeProvider);
|
||||
|
||||
let algorithm: Algorithm;
|
||||
switch (query.algorithmType) {
|
||||
|
|
|
@ -112,9 +112,10 @@ export class MatchQuery extends QueryBase {
|
|||
setDatesAndSchedule = (
|
||||
datetimeTransformer: DateTimeTransformerPort,
|
||||
): MatchQuery => {
|
||||
const initialFromDate: string = this.fromDate;
|
||||
this.fromDate = datetimeTransformer.fromDate(
|
||||
{
|
||||
date: this.fromDate,
|
||||
date: initialFromDate,
|
||||
time: this.schedule[0].time,
|
||||
coordinates: {
|
||||
lon: this.waypoints[0].lon,
|
||||
|
@ -126,7 +127,7 @@ export class MatchQuery extends QueryBase {
|
|||
this.toDate = datetimeTransformer.toDate(
|
||||
this.toDate,
|
||||
{
|
||||
date: this.fromDate,
|
||||
date: initialFromDate,
|
||||
time: this.schedule[0].time,
|
||||
coordinates: {
|
||||
lon: this.waypoints[0].lon,
|
||||
|
@ -164,7 +165,7 @@ export class MatchQuery extends QueryBase {
|
|||
return this;
|
||||
};
|
||||
|
||||
setRoutes = async (routeProvider: RouteProviderPort): Promise<MatchQuery> => {
|
||||
setRoute = async (routeProvider: RouteProviderPort): Promise<MatchQuery> => {
|
||||
const roles: Role[] = [];
|
||||
if (this.driver) roles.push(Role.DRIVER);
|
||||
if (this.passenger) roles.push(Role.PASSENGER);
|
||||
|
@ -177,7 +178,7 @@ export class MatchQuery extends QueryBase {
|
|||
};
|
||||
}
|
||||
|
||||
type ScheduleItem = {
|
||||
export type ScheduleItem = {
|
||||
day?: number;
|
||||
time: string;
|
||||
margin?: number;
|
||||
|
|
|
@ -2,22 +2,24 @@ import { Frequency, Role } from '@modules/ad/core/domain/ad.types';
|
|||
import { Candidate } from '../../../types/algorithm.types';
|
||||
import { Selector } from '../algorithm.abstract';
|
||||
import { AdReadModel } from '@modules/ad/infrastructure/ad.repository';
|
||||
import { ScheduleItem } from '../match.query';
|
||||
import { Waypoint } from '../../../types/waypoint.type';
|
||||
import { Coordinates } from '../../../types/coordinates.type';
|
||||
|
||||
export class PassengerOrientedSelector extends Selector {
|
||||
select = async (): Promise<Candidate[]> => {
|
||||
const queryStringRoles: QueryStringRole[] = [];
|
||||
if (this.query.driver)
|
||||
queryStringRoles.push({
|
||||
query: this.createQueryString(Role.DRIVER),
|
||||
query: this._createQueryString(Role.DRIVER),
|
||||
role: Role.DRIVER,
|
||||
});
|
||||
if (this.query.passenger)
|
||||
queryStringRoles.push({
|
||||
query: this.createQueryString(Role.PASSENGER),
|
||||
query: this._createQueryString(Role.PASSENGER),
|
||||
role: Role.PASSENGER,
|
||||
});
|
||||
|
||||
console.log(queryStringRoles);
|
||||
return (
|
||||
await Promise.all(
|
||||
queryStringRoles.map<Promise<AdsRole>>(
|
||||
|
@ -43,66 +45,251 @@ export class PassengerOrientedSelector extends Selector {
|
|||
.flat();
|
||||
};
|
||||
|
||||
private createQueryString = (role: Role): string =>
|
||||
private _createQueryString = (role: Role): string =>
|
||||
[
|
||||
this.createSelect(role),
|
||||
this.createFrom(),
|
||||
this._createSelect(role),
|
||||
this._createFrom(),
|
||||
'WHERE',
|
||||
this.createWhere(role),
|
||||
].join(' ');
|
||||
this._createWhere(role),
|
||||
]
|
||||
.join(' ')
|
||||
.replace(/\s+/g, ' '); // remove duplicate spaces for easy debug !
|
||||
|
||||
private createSelect = (role: Role): string =>
|
||||
private _createSelect = (role: Role): string =>
|
||||
[
|
||||
`SELECT
|
||||
ad.uuid,driver,passenger,frequency,public.st_astext(matcher.ad.waypoints) as waypoints,
|
||||
"fromDate","toDate",
|
||||
"seatsProposed","seatsRequested",
|
||||
strict,
|
||||
"fwdAzimuth","backAzimuth",
|
||||
`SELECT \
|
||||
ad.uuid,driver,passenger,frequency,public.st_astext(ad.waypoints) as waypoints,\
|
||||
"fromDate","toDate",\
|
||||
"seatsProposed","seatsRequested",\
|
||||
strict,\
|
||||
"fwdAzimuth","backAzimuth",\
|
||||
si.day,si.time,si.margin`,
|
||||
role == Role.DRIVER ? this.selectAsDriver() : this.selectAsPassenger(),
|
||||
role == Role.DRIVER ? this._selectAsDriver() : this._selectAsPassenger(),
|
||||
].join();
|
||||
|
||||
private selectAsDriver = (): string =>
|
||||
private _selectAsDriver = (): string =>
|
||||
`${this.query.route?.driverDuration} as duration,${this.query.route?.driverDistance} as distance`;
|
||||
|
||||
private selectAsPassenger = (): string =>
|
||||
private _selectAsPassenger = (): string =>
|
||||
`"driverDuration" as duration,"driverDistance" as distance`;
|
||||
|
||||
private createFrom = (): string =>
|
||||
private _createFrom = (): string =>
|
||||
'FROM ad LEFT JOIN schedule_item si ON ad.uuid = si."adUuid"';
|
||||
|
||||
private createWhere = (role: Role): string =>
|
||||
[this.whereRole(role), this.whereStrict(), this.whereDate()].join(' AND ');
|
||||
private _createWhere = (role: Role): string =>
|
||||
[
|
||||
this._whereRole(role),
|
||||
this._whereStrict(),
|
||||
this._whereDate(),
|
||||
this._whereSchedule(role),
|
||||
this._whereAzimuth(),
|
||||
this._whereProportion(role),
|
||||
this._whereRemoteness(role),
|
||||
]
|
||||
.filter((where: string) => where != '')
|
||||
.join(' AND ');
|
||||
|
||||
private whereRole = (role: Role): string =>
|
||||
private _whereRole = (role: Role): string =>
|
||||
role == Role.PASSENGER ? 'driver=True' : 'passenger=True';
|
||||
|
||||
private whereStrict = (): string =>
|
||||
private _whereStrict = (): string =>
|
||||
this.query.strict
|
||||
? this.query.frequency == Frequency.PUNCTUAL
|
||||
? `frequency='${Frequency.PUNCTUAL}'`
|
||||
: `frequency='${Frequency.RECURRENT}'`
|
||||
: '';
|
||||
|
||||
private whereDate = (): string => {
|
||||
const whereDate = `(
|
||||
(
|
||||
"fromDate" <= '${this.query.fromDate}' and "fromDate" <= '${this.query.toDate}' and
|
||||
"toDate" >= '${this.query.toDate}' and "toDate" >= '${this.query.fromDate}'
|
||||
) OR (
|
||||
"fromDate" >= '${this.query.fromDate}' and "fromDate" <= '${this.query.toDate}' and
|
||||
"toDate" <= '${this.query.toDate}' and "toDate" >= '${this.query.fromDate}'
|
||||
) OR (
|
||||
"fromDate" <= '${this.query.fromDate}' and "fromDate" <= '${this.query.toDate}' and
|
||||
"toDate" <= '${this.query.toDate}' and "toDate" >= '${this.query.fromDate}'
|
||||
) OR (
|
||||
"fromDate" >= '${this.query.fromDate}' and "fromDate" <= '${this.query.toDate}' and
|
||||
"toDate" >= '${this.query.toDate}' and "toDate" >= '${this.query.fromDate}'
|
||||
)
|
||||
private _whereDate = (): string =>
|
||||
`(\
|
||||
(\
|
||||
"fromDate" <= '${this.query.fromDate}' AND "fromDate" <= '${this.query.toDate}' AND\
|
||||
"toDate" >= '${this.query.toDate}' AND "toDate" >= '${this.query.fromDate}'\
|
||||
) OR (\
|
||||
"fromDate" >= '${this.query.fromDate}' AND "fromDate" <= '${this.query.toDate}' AND\
|
||||
"toDate" <= '${this.query.toDate}' AND "toDate" >= '${this.query.fromDate}'\
|
||||
) OR (\
|
||||
"fromDate" <= '${this.query.fromDate}' AND "fromDate" <= '${this.query.toDate}' AND\
|
||||
"toDate" <= '${this.query.toDate}' AND "toDate" >= '${this.query.fromDate}'\
|
||||
) OR (\
|
||||
"fromDate" >= '${this.query.fromDate}' AND "fromDate" <= '${this.query.toDate}' AND\
|
||||
"toDate" >= '${this.query.toDate}' AND "toDate" >= '${this.query.fromDate}'\
|
||||
)\
|
||||
)`;
|
||||
return whereDate;
|
||||
|
||||
private _whereSchedule = (role: Role): string => {
|
||||
const schedule: string[] = [];
|
||||
// we need full dates to compare times, because margins can lead to compare on previous or next day
|
||||
// -first we establish a base calendar (up to a week)
|
||||
const scheduleDates: Date[] = this._datesBetweenBoundaries(
|
||||
this.query.fromDate,
|
||||
this.query.toDate,
|
||||
);
|
||||
// - then we compare each resulting day of the schedule with each day of calendar,
|
||||
// adding / removing margin depending on the role
|
||||
scheduleDates.map((date: Date) => {
|
||||
this.query.schedule
|
||||
.filter(
|
||||
(scheduleItem: ScheduleItem) => date.getDay() == scheduleItem.day,
|
||||
)
|
||||
.map((scheduleItem: ScheduleItem) => {
|
||||
switch (role) {
|
||||
case Role.PASSENGER:
|
||||
schedule.push(this._wherePassengerSchedule(date, scheduleItem));
|
||||
break;
|
||||
case Role.DRIVER:
|
||||
schedule.push(this._whereDriverSchedule(date, scheduleItem));
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
if (schedule.length > 0) {
|
||||
return ['(', schedule.join(' OR '), ')'].join('');
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
private _wherePassengerSchedule = (
|
||||
date: Date,
|
||||
scheduleItem: ScheduleItem,
|
||||
): string => {
|
||||
let maxDepartureDatetime: Date = new Date(date);
|
||||
maxDepartureDatetime.setHours(parseInt(scheduleItem.time.split(':')[0]));
|
||||
maxDepartureDatetime.setMinutes(parseInt(scheduleItem.time.split(':')[1]));
|
||||
maxDepartureDatetime = this._addMargin(
|
||||
maxDepartureDatetime,
|
||||
scheduleItem.margin as number,
|
||||
);
|
||||
// we want the min departure time of the driver to be before the max departure time of the passenger
|
||||
return `make_timestamp(\
|
||||
${maxDepartureDatetime.getFullYear()},\
|
||||
${maxDepartureDatetime.getMonth() + 1},\
|
||||
${maxDepartureDatetime.getDate()},\
|
||||
CAST(EXTRACT(hour from time) as integer),\
|
||||
CAST(EXTRACT(minute from time) as integer),0) - interval '1 second' * margin <=\
|
||||
make_timestamp(\
|
||||
${maxDepartureDatetime.getFullYear()},\
|
||||
${maxDepartureDatetime.getMonth() + 1},\
|
||||
${maxDepartureDatetime.getDate()},${maxDepartureDatetime.getHours()},${maxDepartureDatetime.getMinutes()},0)`;
|
||||
};
|
||||
|
||||
private _whereDriverSchedule = (
|
||||
date: Date,
|
||||
scheduleItem: ScheduleItem,
|
||||
): string => {
|
||||
let minDepartureDatetime: Date = new Date(date);
|
||||
minDepartureDatetime.setHours(parseInt(scheduleItem.time.split(':')[0]));
|
||||
minDepartureDatetime.setMinutes(parseInt(scheduleItem.time.split(':')[1]));
|
||||
minDepartureDatetime = this._addMargin(
|
||||
minDepartureDatetime,
|
||||
-(scheduleItem.margin as number),
|
||||
);
|
||||
// we want the max departure time of the passenger to be after the min departure time of the driver
|
||||
return `make_timestamp(\
|
||||
${minDepartureDatetime.getFullYear()},
|
||||
${minDepartureDatetime.getMonth() + 1},
|
||||
${minDepartureDatetime.getDate()},\
|
||||
CAST(EXTRACT(hour from time) as integer),\
|
||||
CAST(EXTRACT(minute from time) as integer),0) + interval '1 second' * margin >=\
|
||||
make_timestamp(\
|
||||
${minDepartureDatetime.getFullYear()},
|
||||
${minDepartureDatetime.getMonth() + 1},
|
||||
${minDepartureDatetime.getDate()},${minDepartureDatetime.getHours()},${minDepartureDatetime.getMinutes()},0)`;
|
||||
};
|
||||
|
||||
private _whereAzimuth = (): string => {
|
||||
if (!this.query.useAzimuth) return '';
|
||||
const { minAzimuth, maxAzimuth } = this._azimuthRange(
|
||||
this.query.route?.backAzimuth as number,
|
||||
this.query.azimuthMargin as number,
|
||||
);
|
||||
if (minAzimuth <= maxAzimuth)
|
||||
return `("fwdAzimuth" <= ${minAzimuth} OR "fwdAzimuth" >= ${maxAzimuth})`;
|
||||
return `("fwdAzimuth" <= ${minAzimuth} AND "fwdAzimuth" >= ${maxAzimuth})`;
|
||||
};
|
||||
|
||||
private _whereProportion = (role: Role): string => {
|
||||
if (!this.query.useProportion) return '';
|
||||
switch (role) {
|
||||
case Role.PASSENGER:
|
||||
return `(${this.query.route?.passengerDistance}>(${this.query.proportion}*"driverDistance"))`;
|
||||
case Role.DRIVER:
|
||||
return `("passengerDistance">(${this.query.proportion}*${this.query.route?.driverDistance}))`;
|
||||
}
|
||||
};
|
||||
|
||||
private _whereRemoteness = (role: Role): string => {
|
||||
this.query.waypoints.sort(
|
||||
(firstWaypoint: Waypoint, secondWaypoint: Waypoint) =>
|
||||
firstWaypoint.position - secondWaypoint.position,
|
||||
);
|
||||
switch (role) {
|
||||
case Role.PASSENGER:
|
||||
return `\
|
||||
public.st_distance('POINT(${this.query.waypoints[0].lon} ${
|
||||
this.query.waypoints[0].lat
|
||||
})'::public.geography,direction)<\
|
||||
${this.query.remoteness} AND \
|
||||
public.st_distance('POINT(${
|
||||
this.query.waypoints[this.query.waypoints.length - 1].lon
|
||||
} ${
|
||||
this.query.waypoints[this.query.waypoints.length - 1].lat
|
||||
})'::public.geography,direction)<\
|
||||
${this.query.remoteness}`;
|
||||
case Role.DRIVER:
|
||||
const lineStringPoints: string[] = [];
|
||||
this.query.route?.points.forEach((point: Coordinates) =>
|
||||
lineStringPoints.push(
|
||||
`public.st_makepoint(${point.lon},${point.lat})`,
|
||||
),
|
||||
);
|
||||
const lineString = [
|
||||
'public.st_makeline( ARRAY[ ',
|
||||
lineStringPoints.join(','),
|
||||
'] )::public.geography',
|
||||
].join('');
|
||||
return `\
|
||||
public.st_distance( public.st_startpoint(waypoints::public.geometry), ${lineString})<\
|
||||
${this.query.remoteness} AND \
|
||||
public.st_distance( public.st_endpoint(waypoints::public.geometry), ${lineString})<\
|
||||
${this.query.remoteness}`;
|
||||
}
|
||||
};
|
||||
|
||||
private _datesBetweenBoundaries = (
|
||||
firstDate: string,
|
||||
lastDate: string,
|
||||
max = 7,
|
||||
): Date[] => {
|
||||
const fromDate: Date = new Date(firstDate);
|
||||
const toDate: Date = new Date(lastDate);
|
||||
const dates: Date[] = [];
|
||||
let count = 0;
|
||||
for (
|
||||
let date = fromDate;
|
||||
date <= toDate;
|
||||
date.setDate(date.getDate() + 1)
|
||||
) {
|
||||
dates.push(new Date(date));
|
||||
count++;
|
||||
if (count == max) break;
|
||||
}
|
||||
return dates;
|
||||
};
|
||||
|
||||
private _addMargin = (date: Date, marginInSeconds: number): Date => {
|
||||
date.setTime(date.getTime() + marginInSeconds * 1000);
|
||||
return date;
|
||||
};
|
||||
|
||||
private _azimuthRange = (
|
||||
azimuth: number,
|
||||
margin: number,
|
||||
): { minAzimuth: number; maxAzimuth: number } => ({
|
||||
minAzimuth:
|
||||
azimuth - margin < 0 ? azimuth - margin + 360 : azimuth - margin,
|
||||
maxAzimuth:
|
||||
azimuth + margin > 360 ? azimuth + margin - 360 : azimuth + margin,
|
||||
});
|
||||
}
|
||||
|
||||
export type QueryStringRole = {
|
||||
|
|
|
@ -100,10 +100,12 @@ export class AdRepository
|
|||
);
|
||||
}
|
||||
|
||||
getCandidates = async (queryString: string): Promise<AdReadModel[]> =>
|
||||
this.toAdReadModels(
|
||||
getCandidates = async (queryString: string): Promise<AdReadModel[]> => {
|
||||
// console.log(queryString);
|
||||
return this.toAdReadModels(
|
||||
(await this.prismaRaw.$queryRawUnsafe(queryString)) as UngroupedAdModel[],
|
||||
);
|
||||
};
|
||||
|
||||
private toAdReadModels = (
|
||||
ungroupedAds: UngroupedAdModel[],
|
||||
|
|
|
@ -20,7 +20,7 @@ export class TimeConverter implements TimeConverterPort {
|
|||
date: string,
|
||||
time: string,
|
||||
timezone: string,
|
||||
dst = true,
|
||||
dst = false,
|
||||
): Date =>
|
||||
new Date(
|
||||
new DateTime(
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
export class MatchResponseDto {
|
||||
import { ResponseBase } from '@mobicoop/ddd-library';
|
||||
|
||||
export class MatchResponseDto extends ResponseBase {
|
||||
adId: string;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import { Controller, UsePipes } from '@nestjs/common';
|
||||
import { GrpcMethod, RpcException } from '@nestjs/microservices';
|
||||
import { RpcValidationPipe } from '@mobicoop/ddd-library';
|
||||
import { ResponseBase, RpcValidationPipe } from '@mobicoop/ddd-library';
|
||||
import { RpcExceptionCode } from '@mobicoop/ddd-library';
|
||||
import { MatchPaginatedResponseDto } from '../dtos/match.paginated.response.dto';
|
||||
import { QueryBus } from '@nestjs/cqrs';
|
||||
import { MatchRequestDto } from './dtos/match.request.dto';
|
||||
import { MatchQuery } from '@modules/ad/core/application/queries/match/match.query';
|
||||
import { MatchEntity } from '@modules/ad/core/domain/match.entity';
|
||||
|
||||
@UsePipes(
|
||||
new RpcValidationPipe({
|
||||
|
@ -20,13 +21,18 @@ export class MatchGrpcController {
|
|||
@GrpcMethod('MatcherService', 'Match')
|
||||
async match(data: MatchRequestDto): Promise<MatchPaginatedResponseDto> {
|
||||
try {
|
||||
const matches = await this.queryBus.execute(new MatchQuery(data));
|
||||
return {
|
||||
data: matches,
|
||||
const matches: MatchEntity[] = await this.queryBus.execute(
|
||||
new MatchQuery(data),
|
||||
);
|
||||
return new MatchPaginatedResponseDto({
|
||||
data: matches.map((match: MatchEntity) => ({
|
||||
...new ResponseBase(match),
|
||||
adId: match.getProps().adId,
|
||||
})),
|
||||
page: 1,
|
||||
perPage: 5,
|
||||
total: matches.length,
|
||||
};
|
||||
});
|
||||
} catch (e) {
|
||||
throw new RpcException({
|
||||
code: RpcExceptionCode.UNKNOWN,
|
||||
|
|
|
@ -55,6 +55,7 @@ enum AlgorithmType {
|
|||
|
||||
message Match {
|
||||
string id = 1;
|
||||
string adId = 2;
|
||||
}
|
||||
|
||||
message Matches {
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import {
|
||||
AD_REPOSITORY,
|
||||
AD_ROUTE_PROVIDER,
|
||||
INPUT_DATETIME_TRANSFORMER,
|
||||
PARAMS_PROVIDER,
|
||||
} from '@modules/ad/ad.di-tokens';
|
||||
import { DateTimeTransformerPort } from '@modules/ad/core/application/ports/datetime-transformer.port';
|
||||
import { DefaultParamsProviderPort } from '@modules/ad/core/application/ports/default-params-provider.port';
|
||||
import { RouteProviderPort } from '@modules/ad/core/application/ports/route-provider.port';
|
||||
import { MatchQuery } from '@modules/ad/core/application/queries/match/match.query';
|
||||
import { MatchQueryHandler } from '@modules/ad/core/application/queries/match/match.query-handler';
|
||||
import { AlgorithmType } from '@modules/ad/core/application/types/algorithm.types';
|
||||
|
@ -72,6 +74,10 @@ const mockInputDateTimeTransformer: DateTimeTransformerPort = {
|
|||
time: jest.fn(),
|
||||
};
|
||||
|
||||
const mockRouteProvider: RouteProviderPort = {
|
||||
getBasic: jest.fn(),
|
||||
};
|
||||
|
||||
describe('Match Query Handler', () => {
|
||||
let matchQueryHandler: MatchQueryHandler;
|
||||
|
||||
|
@ -91,6 +97,10 @@ describe('Match Query Handler', () => {
|
|||
provide: INPUT_DATETIME_TRANSFORMER,
|
||||
useValue: mockInputDateTimeTransformer,
|
||||
},
|
||||
{
|
||||
provide: AD_ROUTE_PROVIDER,
|
||||
useValue: mockRouteProvider,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { DateTimeTransformerPort } from '@modules/ad/core/application/ports/datetime-transformer.port';
|
||||
import { DefaultParams } from '@modules/ad/core/application/ports/default-params.type';
|
||||
import { RouteProviderPort } from '@modules/ad/core/application/ports/route-provider.port';
|
||||
import { MatchQuery } from '@modules/ad/core/application/queries/match/match.query';
|
||||
import { AlgorithmType } from '@modules/ad/core/application/types/algorithm.types';
|
||||
import { Waypoint } from '@modules/ad/core/application/types/waypoint.type';
|
||||
|
@ -49,6 +50,23 @@ const mockInputDateTimeTransformer: DateTimeTransformerPort = {
|
|||
time: jest.fn().mockImplementation(() => '23:05'),
|
||||
};
|
||||
|
||||
const mockRouteProvider: RouteProviderPort = {
|
||||
getBasic: jest
|
||||
.fn()
|
||||
.mockImplementationOnce(() => ({
|
||||
driverDistance: undefined,
|
||||
driverDuration: undefined,
|
||||
passengerDistance: 150120,
|
||||
passengerDuration: 6540,
|
||||
fwdAzimuth: 276,
|
||||
backAzimuth: 96,
|
||||
points: [],
|
||||
}))
|
||||
.mockImplementationOnce(() => {
|
||||
throw new Error();
|
||||
}),
|
||||
};
|
||||
|
||||
describe('Match Query', () => {
|
||||
it('should set default values', async () => {
|
||||
const matchQuery = new MatchQuery({
|
||||
|
@ -124,4 +142,40 @@ describe('Match Query', () => {
|
|||
expect(matchQuery.seatsProposed).toBe(3);
|
||||
expect(matchQuery.seatsRequested).toBe(1);
|
||||
});
|
||||
|
||||
it('should set route', async () => {
|
||||
const matchQuery = new MatchQuery({
|
||||
frequency: Frequency.PUNCTUAL,
|
||||
fromDate: '2023-08-28',
|
||||
toDate: '2023-08-28',
|
||||
schedule: [
|
||||
{
|
||||
time: '01:05',
|
||||
},
|
||||
],
|
||||
waypoints: [originWaypoint, destinationWaypoint],
|
||||
});
|
||||
await matchQuery.setRoute(mockRouteProvider);
|
||||
expect(matchQuery.route?.driverDistance).toBeUndefined();
|
||||
expect(matchQuery.route?.passengerDistance).toBe(150120);
|
||||
});
|
||||
|
||||
it('should throw an exception if route is not found', async () => {
|
||||
const matchQuery = new MatchQuery({
|
||||
driver: true,
|
||||
passenger: false,
|
||||
frequency: Frequency.PUNCTUAL,
|
||||
fromDate: '2023-08-28',
|
||||
toDate: '2023-08-28',
|
||||
schedule: [
|
||||
{
|
||||
time: '01:05',
|
||||
},
|
||||
],
|
||||
waypoints: [originWaypoint, destinationWaypoint],
|
||||
});
|
||||
await expect(matchQuery.setRoute(mockRouteProvider)).rejects.toBeInstanceOf(
|
||||
Error,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -32,16 +32,44 @@ const matchQuery = new MatchQuery({
|
|||
driver: true,
|
||||
passenger: true,
|
||||
frequency: Frequency.PUNCTUAL,
|
||||
fromDate: '2023-08-28',
|
||||
toDate: '2023-08-28',
|
||||
fromDate: '2023-06-21',
|
||||
toDate: '2023-06-21',
|
||||
useAzimuth: true,
|
||||
azimuthMargin: 10,
|
||||
useProportion: true,
|
||||
proportion: 0.3,
|
||||
schedule: [
|
||||
{
|
||||
day: 3,
|
||||
time: '07:05',
|
||||
margin: 900,
|
||||
},
|
||||
],
|
||||
strict: false,
|
||||
waypoints: [originWaypoint, destinationWaypoint],
|
||||
});
|
||||
matchQuery.route = {
|
||||
driverDistance: 150120,
|
||||
driverDuration: 6540,
|
||||
passengerDistance: 150120,
|
||||
passengerDuration: 6540,
|
||||
fwdAzimuth: 276,
|
||||
backAzimuth: 96,
|
||||
points: [
|
||||
{
|
||||
lat: 48.689445,
|
||||
lon: 6.17651,
|
||||
},
|
||||
{
|
||||
lat: 48.7566,
|
||||
lon: 4.3522,
|
||||
},
|
||||
{
|
||||
lat: 48.8566,
|
||||
lon: 2.3522,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const mockMatcherRepository: AdRepositoryPort = {
|
||||
insertExtra: jest.fn(),
|
||||
|
|
|
@ -60,6 +60,7 @@ describe('Time Converter', () => {
|
|||
parisDate,
|
||||
parisTime,
|
||||
'Europe/Paris',
|
||||
true,
|
||||
);
|
||||
expect(utcDate.toISOString()).toBe('2023-06-22T10:00:00.000Z');
|
||||
});
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { RpcExceptionCode } from '@mobicoop/ddd-library';
|
||||
import { AlgorithmType } from '@modules/ad/core/application/types/algorithm.types';
|
||||
import { Frequency } from '@modules/ad/core/domain/ad.types';
|
||||
import { MatchEntity } from '@modules/ad/core/domain/match.entity';
|
||||
import { MatchRequestDto } from '@modules/ad/interface/grpc-controllers/dtos/match.request.dto';
|
||||
import { WaypointDto } from '@modules/ad/interface/grpc-controllers/dtos/waypoint.dto';
|
||||
import { MatchGrpcController } from '@modules/ad/interface/grpc-controllers/match.grpc-controller';
|
||||
|
@ -49,12 +50,12 @@ const mockQueryBus = {
|
|||
execute: jest
|
||||
.fn()
|
||||
.mockImplementationOnce(() => [
|
||||
{
|
||||
adId: 1,
|
||||
},
|
||||
{
|
||||
adId: 2,
|
||||
},
|
||||
MatchEntity.create({
|
||||
adId: '0cc87f3b-7a27-4eff-9850-a5d642c2a0c3',
|
||||
}),
|
||||
MatchEntity.create({
|
||||
adId: 'e4cc156f-aaa5-4270-bf6f-82f5a230d748',
|
||||
}),
|
||||
])
|
||||
.mockImplementationOnce(() => {
|
||||
throw new Error();
|
||||
|
|
Loading…
Reference in New Issue