fail faster in path creator
This commit is contained in:
parent
40227be69a
commit
a277a9547f
|
@ -31,6 +31,7 @@ export class CreateAdService implements ICommandHandler {
|
||||||
const roles: Role[] = [];
|
const roles: Role[] = [];
|
||||||
if (command.driver) roles.push(Role.DRIVER);
|
if (command.driver) roles.push(Role.DRIVER);
|
||||||
if (command.passenger) roles.push(Role.PASSENGER);
|
if (command.passenger) roles.push(Role.PASSENGER);
|
||||||
|
|
||||||
const pathCreator: PathCreator = new PathCreator(
|
const pathCreator: PathCreator = new PathCreator(
|
||||||
roles,
|
roles,
|
||||||
command.waypoints.map(
|
command.waypoints.map(
|
||||||
|
@ -41,6 +42,7 @@ export class CreateAdService implements ICommandHandler {
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
let typedRoutes: TypedRoute[];
|
let typedRoutes: TypedRoute[];
|
||||||
try {
|
try {
|
||||||
typedRoutes = await Promise.all(
|
typedRoutes = await Promise.all(
|
||||||
|
@ -60,8 +62,9 @@ export class CreateAdService implements ICommandHandler {
|
||||||
let points: PointValueObject[] | undefined;
|
let points: PointValueObject[] | undefined;
|
||||||
let fwdAzimuth: number | undefined;
|
let fwdAzimuth: number | undefined;
|
||||||
let backAzimuth: number | undefined;
|
let backAzimuth: number | undefined;
|
||||||
|
try {
|
||||||
typedRoutes.forEach((typedRoute: TypedRoute) => {
|
typedRoutes.forEach((typedRoute: TypedRoute) => {
|
||||||
if (typedRoute.type !== PathType.PASSENGER) {
|
if ([PathType.DRIVER, PathType.GENERIC].includes(typedRoute.type)) {
|
||||||
driverDistance = typedRoute.route.distance;
|
driverDistance = typedRoute.route.distance;
|
||||||
driverDuration = typedRoute.route.duration;
|
driverDuration = typedRoute.route.duration;
|
||||||
points = typedRoute.route.points.map(
|
points = typedRoute.route.points.map(
|
||||||
|
@ -74,7 +77,7 @@ export class CreateAdService implements ICommandHandler {
|
||||||
fwdAzimuth = typedRoute.route.fwdAzimuth;
|
fwdAzimuth = typedRoute.route.fwdAzimuth;
|
||||||
backAzimuth = typedRoute.route.backAzimuth;
|
backAzimuth = typedRoute.route.backAzimuth;
|
||||||
}
|
}
|
||||||
if (typedRoute.type !== PathType.DRIVER) {
|
if ([PathType.PASSENGER, PathType.GENERIC].includes(typedRoute.type)) {
|
||||||
passengerDistance = typedRoute.route.distance;
|
passengerDistance = typedRoute.route.distance;
|
||||||
passengerDuration = typedRoute.route.duration;
|
passengerDuration = typedRoute.route.duration;
|
||||||
if (!points)
|
if (!points)
|
||||||
|
@ -89,7 +92,9 @@ export class CreateAdService implements ICommandHandler {
|
||||||
if (!backAzimuth) backAzimuth = typedRoute.route.backAzimuth;
|
if (!backAzimuth) backAzimuth = typedRoute.route.backAzimuth;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (points && fwdAzimuth && backAzimuth) {
|
} catch (error: any) {
|
||||||
|
throw new Error('Invalid route');
|
||||||
|
}
|
||||||
const ad = AdEntity.create({
|
const ad = AdEntity.create({
|
||||||
id: command.id,
|
id: command.id,
|
||||||
driver: command.driver,
|
driver: command.driver,
|
||||||
|
@ -102,13 +107,13 @@ export class CreateAdService implements ICommandHandler {
|
||||||
seatsRequested: command.seatsRequested,
|
seatsRequested: command.seatsRequested,
|
||||||
strict: command.strict,
|
strict: command.strict,
|
||||||
waypoints: command.waypoints,
|
waypoints: command.waypoints,
|
||||||
points,
|
points: points as PointValueObject[],
|
||||||
driverDistance,
|
driverDistance,
|
||||||
driverDuration,
|
driverDuration,
|
||||||
passengerDistance,
|
passengerDistance,
|
||||||
passengerDuration,
|
passengerDuration,
|
||||||
fwdAzimuth,
|
fwdAzimuth: fwdAzimuth as number,
|
||||||
backAzimuth,
|
backAzimuth: backAzimuth as number,
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
await this.repository.insertExtra(ad, 'ad');
|
await this.repository.insertExtra(ad, 'ad');
|
||||||
|
@ -120,6 +125,4 @@ export class CreateAdService implements ICommandHandler {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new Error('Route error');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { Role } from './ad.types';
|
import { Role } from './ad.types';
|
||||||
import { Target } from './candidate.types';
|
import { Target } from './candidate.types';
|
||||||
|
import { CarpoolPathCreatorException } from './match.errors';
|
||||||
import { Actor } from './value-objects/actor.value-object';
|
import { Actor } from './value-objects/actor.value-object';
|
||||||
import { Point } from './value-objects/point.value-object';
|
import { Point } from './value-objects/point.value-object';
|
||||||
import { WayStep } from './value-objects/waystep.value-object';
|
import { WayStep } from './value-objects/waystep.value-object';
|
||||||
|
@ -10,7 +11,16 @@ export class CarpoolPathCreator {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly driverWaypoints: Point[],
|
private readonly driverWaypoints: Point[],
|
||||||
private readonly passengerWaypoints: Point[],
|
private readonly passengerWaypoints: Point[],
|
||||||
) {}
|
) {
|
||||||
|
if (driverWaypoints.length < 2)
|
||||||
|
throw new CarpoolPathCreatorException(
|
||||||
|
new Error('At least 2 driver waypoints must be defined'),
|
||||||
|
);
|
||||||
|
if (passengerWaypoints.length < 2)
|
||||||
|
throw new CarpoolPathCreatorException(
|
||||||
|
new Error('At least 2 passenger waypoints must be defined'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a path (a list of waysteps) between driver waypoints
|
* Creates a path (a list of waysteps) between driver waypoints
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { ExceptionBase } from '@mobicoop/ddd-library';
|
||||||
|
|
||||||
|
export class PathCreatorException extends ExceptionBase {
|
||||||
|
static readonly message = 'Path creator error';
|
||||||
|
|
||||||
|
public readonly code = 'MATCHER.PATH_CREATOR';
|
||||||
|
|
||||||
|
constructor(cause?: Error, metadata?: unknown) {
|
||||||
|
super(PathCreatorException.message, cause, metadata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CarpoolPathCreatorException extends ExceptionBase {
|
||||||
|
static readonly message = 'Carpool path creator error';
|
||||||
|
|
||||||
|
public readonly code = 'MATCHER.CARPOOL_PATH_CREATOR';
|
||||||
|
|
||||||
|
constructor(cause?: Error, metadata?: unknown) {
|
||||||
|
super(CarpoolPathCreatorException.message, cause, metadata);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,22 @@
|
||||||
import { Route } from '@modules/geography/core/domain/route.types';
|
import { Route } from '@modules/geography/core/domain/route.types';
|
||||||
import { Role } from './ad.types';
|
import { Role } from './ad.types';
|
||||||
import { Point } from './value-objects/point.value-object';
|
import { Point } from './value-objects/point.value-object';
|
||||||
|
import { PathCreatorException } from './match.errors';
|
||||||
|
|
||||||
export class PathCreator {
|
export class PathCreator {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly roles: Role[],
|
private readonly roles: Role[],
|
||||||
private readonly waypoints: Point[],
|
private readonly waypoints: Point[],
|
||||||
) {}
|
) {
|
||||||
|
if (roles.length == 0)
|
||||||
|
throw new PathCreatorException(
|
||||||
|
new Error('At least a role must be defined'),
|
||||||
|
);
|
||||||
|
if (waypoints.length < 2)
|
||||||
|
throw new PathCreatorException(
|
||||||
|
new Error('At least 2 waypoints must be defined'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public getBasePaths = (): Path[] => {
|
public getBasePaths = (): Path[] => {
|
||||||
const paths: Path[] = [];
|
const paths: Path[] = [];
|
||||||
|
@ -61,6 +71,15 @@ export type TypedRoute = {
|
||||||
route: Route;
|
route: Route;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PathType id used for route calculation, to reduce the number of routes to compute :
|
||||||
|
* - a single route for a driver only
|
||||||
|
* - a single route for a passenger only
|
||||||
|
* - a single route for a driver and passenger with 2 waypoints given
|
||||||
|
* - two routes for a driver and passenger with more than 2 waypoints given
|
||||||
|
* (all the waypoints as driver, only origin and destination as passenger as
|
||||||
|
* intermediate waypoints doesn't matter in that case)
|
||||||
|
*/
|
||||||
export enum PathType {
|
export enum PathType {
|
||||||
GENERIC = 'generic',
|
GENERIC = 'generic',
|
||||||
DRIVER = 'driver',
|
DRIVER = 'driver',
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { CarpoolPathCreator } from '@modules/ad/core/domain/carpool-path-creator.service';
|
import { CarpoolPathCreator } from '@modules/ad/core/domain/carpool-path-creator.service';
|
||||||
|
import { CarpoolPathCreatorException } from '@modules/ad/core/domain/match.errors';
|
||||||
import { Point } from '@modules/ad/core/domain/value-objects/point.value-object';
|
import { Point } from '@modules/ad/core/domain/value-objects/point.value-object';
|
||||||
import { WayStep } from '@modules/ad/core/domain/value-objects/waystep.value-object';
|
import { WayStep } from '@modules/ad/core/domain/value-objects/waystep.value-object';
|
||||||
|
|
||||||
|
@ -99,4 +100,18 @@ describe('Carpool Path Creator Service', () => {
|
||||||
expect(waysteps[4].actors.length).toBe(1);
|
expect(waysteps[4].actors.length).toBe(1);
|
||||||
expect(waysteps[5].actors.length).toBe(1);
|
expect(waysteps[5].actors.length).toBe(1);
|
||||||
});
|
});
|
||||||
|
it('should throw an exception if less than 2 driver waypoints are given', () => {
|
||||||
|
try {
|
||||||
|
new CarpoolPathCreator([waypoint1], [waypoint3, waypoint4]);
|
||||||
|
} catch (e: any) {
|
||||||
|
expect(e).toBeInstanceOf(CarpoolPathCreatorException);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
it('should throw an exception if less than 2 passenger waypoints are given', () => {
|
||||||
|
try {
|
||||||
|
new CarpoolPathCreator([waypoint1, waypoint6], [waypoint3]);
|
||||||
|
} catch (e: any) {
|
||||||
|
expect(e).toBeInstanceOf(CarpoolPathCreatorException);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -49,6 +49,7 @@ const mockAdRepository = {
|
||||||
insertExtra: jest
|
insertExtra: jest
|
||||||
.fn()
|
.fn()
|
||||||
.mockImplementationOnce(() => ({}))
|
.mockImplementationOnce(() => ({}))
|
||||||
|
.mockImplementationOnce(() => ({}))
|
||||||
.mockImplementationOnce(() => {
|
.mockImplementationOnce(() => {
|
||||||
throw new Error();
|
throw new Error();
|
||||||
})
|
})
|
||||||
|
@ -131,7 +132,7 @@ describe('create-ad.service', () => {
|
||||||
createAdService.execute(createAdCommand),
|
createAdService.execute(createAdCommand),
|
||||||
).rejects.toBeInstanceOf(Error);
|
).rejects.toBeInstanceOf(Error);
|
||||||
});
|
});
|
||||||
it('should create a new ad', async () => {
|
it('should create a new ad as driver and passenger', async () => {
|
||||||
AdEntity.create = jest.fn().mockReturnValue({
|
AdEntity.create = jest.fn().mockReturnValue({
|
||||||
id: '047a6ecf-23d4-4d3e-877c-3225d560a8da',
|
id: '047a6ecf-23d4-4d3e-877c-3225d560a8da',
|
||||||
});
|
});
|
||||||
|
@ -140,6 +141,16 @@ describe('create-ad.service', () => {
|
||||||
);
|
);
|
||||||
expect(result).toBe('047a6ecf-23d4-4d3e-877c-3225d560a8da');
|
expect(result).toBe('047a6ecf-23d4-4d3e-877c-3225d560a8da');
|
||||||
});
|
});
|
||||||
|
it('should create a new ad as passenger', async () => {
|
||||||
|
AdEntity.create = jest.fn().mockReturnValue({
|
||||||
|
id: '047a6ecf-23d4-4d3e-877c-3225d560a8da',
|
||||||
|
});
|
||||||
|
const result: AggregateID = await createAdService.execute({
|
||||||
|
...createAdCommand,
|
||||||
|
driver: false,
|
||||||
|
});
|
||||||
|
expect(result).toBe('047a6ecf-23d4-4d3e-877c-3225d560a8da');
|
||||||
|
});
|
||||||
it('should throw an error if something bad happens', async () => {
|
it('should throw an error if something bad happens', async () => {
|
||||||
await expect(
|
await expect(
|
||||||
createAdService.execute(createAdCommand),
|
createAdService.execute(createAdCommand),
|
||||||
|
|
|
@ -55,7 +55,16 @@ const mockMatcherRepository: AdRepositoryPort = {
|
||||||
{
|
{
|
||||||
id: 'cc260669-1c6d-441f-80a5-19cd59afb777',
|
id: 'cc260669-1c6d-441f-80a5-19cd59afb777',
|
||||||
getProps: jest.fn().mockImplementation(() => ({
|
getProps: jest.fn().mockImplementation(() => ({
|
||||||
waypoints: [],
|
waypoints: [
|
||||||
|
{
|
||||||
|
lat: 48.6645,
|
||||||
|
lon: 6.18457,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lat: 48.7898,
|
||||||
|
lon: 2.36845,
|
||||||
|
},
|
||||||
|
],
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Role } from '@modules/ad/core/domain/ad.types';
|
import { Role } from '@modules/ad/core/domain/ad.types';
|
||||||
|
import { PathCreatorException } from '@modules/ad/core/domain/match.errors';
|
||||||
import {
|
import {
|
||||||
Path,
|
Path,
|
||||||
PathCreator,
|
PathCreator,
|
||||||
|
@ -68,4 +69,21 @@ describe('Path Creator Service', () => {
|
||||||
.waypoints,
|
.waypoints,
|
||||||
).toHaveLength(2);
|
).toHaveLength(2);
|
||||||
});
|
});
|
||||||
|
it('should throw an exception if a role is not given', () => {
|
||||||
|
try {
|
||||||
|
new PathCreator(
|
||||||
|
[],
|
||||||
|
[originWaypoint, intermediateWaypoint, destinationWaypoint],
|
||||||
|
);
|
||||||
|
} catch (e: any) {
|
||||||
|
expect(e).toBeInstanceOf(PathCreatorException);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
it('should throw an exception if less than 2 waypoints are given', () => {
|
||||||
|
try {
|
||||||
|
new PathCreator([Role.DRIVER], [originWaypoint]);
|
||||||
|
} catch (e: any) {
|
||||||
|
expect(e).toBeInstanceOf(PathCreatorException);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue