bette use of value objects
This commit is contained in:
parent
4731020e8a
commit
74fb2c120e
|
@ -14,7 +14,6 @@ import {
|
|||
import { DirectionEncoderPort } from '@modules/geography/core/application/ports/direction-encoder.port';
|
||||
import { AD_DIRECTION_ENCODER } from './ad.di-tokens';
|
||||
import { ExtendedMapper } from '@mobicoop/ddd-library';
|
||||
import { Waypoint } from './core/domain/value-objects/waypoint.value-object';
|
||||
|
||||
/**
|
||||
* Mapper constructs objects that are used in different layers:
|
||||
|
@ -112,13 +111,12 @@ export class AdMapper
|
|||
margin: scheduleItem.margin,
|
||||
}),
|
||||
),
|
||||
waypoints: this.directionEncoder.decode(record.waypoints).map(
|
||||
(coordinates, index) =>
|
||||
new Waypoint({
|
||||
position: index,
|
||||
...coordinates,
|
||||
}),
|
||||
),
|
||||
waypoints: this.directionEncoder
|
||||
.decode(record.waypoints)
|
||||
.map((coordinates, index) => ({
|
||||
position: index,
|
||||
...coordinates,
|
||||
})),
|
||||
fwdAzimuth: record.fwdAzimuth,
|
||||
backAzimuth: record.backAzimuth,
|
||||
points: [],
|
||||
|
|
|
@ -13,8 +13,10 @@ import {
|
|||
PathCreator,
|
||||
PathType,
|
||||
TypedRoute,
|
||||
} from '@modules/ad/core/domain/patch-creator.service';
|
||||
} from '@modules/ad/core/domain/path-creator.service';
|
||||
import { Point } from '../../types/point.type';
|
||||
import { Waypoint } from '../../types/waypoint.type';
|
||||
import { Waypoint as WaypointValueObject } from '@modules/ad/core/domain/value-objects/waypoint.value-object';
|
||||
|
||||
@CommandHandler(CreateAdCommand)
|
||||
export class CreateAdService implements ICommandHandler {
|
||||
|
@ -29,11 +31,21 @@ export class CreateAdService implements ICommandHandler {
|
|||
const roles: Role[] = [];
|
||||
if (command.driver) roles.push(Role.DRIVER);
|
||||
if (command.passenger) roles.push(Role.PASSENGER);
|
||||
const pathCreator: PathCreator = new PathCreator(roles, command.waypoints);
|
||||
const pathCreator: PathCreator = new PathCreator(
|
||||
roles,
|
||||
command.waypoints.map(
|
||||
(waypoint: Waypoint) =>
|
||||
new WaypointValueObject({
|
||||
position: waypoint.position,
|
||||
lon: waypoint.lon,
|
||||
lat: waypoint.lat,
|
||||
}),
|
||||
),
|
||||
);
|
||||
let typedRoutes: TypedRoute[];
|
||||
try {
|
||||
typedRoutes = await Promise.all(
|
||||
pathCreator.getPaths().map(async (path: Path) => ({
|
||||
pathCreator.getBasePaths().map(async (path: Path) => ({
|
||||
type: path.type,
|
||||
route: await this.routeProvider.getBasic(path.waypoints),
|
||||
})),
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Route } from '@modules/geography/core/domain/route.types';
|
||||
import { Waypoint } from '../types/waypoint.type';
|
||||
import { Waypoint } from '../../domain/value-objects/waypoint.value-object';
|
||||
|
||||
export interface RouteProviderPort {
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
import { CandidateEntity } from '@modules/ad/core/domain/candidate.entity';
|
||||
import { Completer } from './completer.abstract';
|
||||
import { Role } from '@modules/ad/core/domain/ad.types';
|
||||
import {
|
||||
Waypoint as WaypointValueObject,
|
||||
WaypointProps,
|
||||
} from '@modules/ad/core/domain/value-objects/waypoint.value-object';
|
||||
import { Waypoint } from '../../../types/waypoint.type';
|
||||
import { WayStepsCreator } from '@modules/ad/core/domain/waysteps-creator.service';
|
||||
|
||||
/**
|
||||
* Complete candidates by setting driver and crew waypoints
|
||||
|
@ -7,7 +14,53 @@ import { Completer } from './completer.abstract';
|
|||
export class PassengerOrientedWaypointsCompleter extends Completer {
|
||||
complete = async (
|
||||
candidates: CandidateEntity[],
|
||||
): Promise<CandidateEntity[]> => candidates;
|
||||
): Promise<CandidateEntity[]> => {
|
||||
candidates.forEach((candidate: CandidateEntity) => {
|
||||
const carpoolPathCreator = new WayStepsCreator(
|
||||
candidate.getProps().role == Role.DRIVER
|
||||
? candidate.getProps().waypoints.map(
|
||||
(waypoint: WaypointProps) =>
|
||||
new WaypointValueObject({
|
||||
position: waypoint.position,
|
||||
lon: waypoint.lon,
|
||||
lat: waypoint.lat,
|
||||
}),
|
||||
)
|
||||
: this.query.waypoints.map(
|
||||
(waypoint: Waypoint) =>
|
||||
new WaypointValueObject({
|
||||
position: waypoint.position,
|
||||
lon: waypoint.lon,
|
||||
lat: waypoint.lat,
|
||||
}),
|
||||
),
|
||||
candidate.getProps().role == Role.PASSENGER
|
||||
? candidate.getProps().waypoints.map(
|
||||
(waypoint: WaypointProps) =>
|
||||
new WaypointValueObject({
|
||||
position: waypoint.position,
|
||||
lon: waypoint.lon,
|
||||
lat: waypoint.lat,
|
||||
}),
|
||||
)
|
||||
: this.query.waypoints.map(
|
||||
(waypoint: Waypoint) =>
|
||||
new WaypointValueObject({
|
||||
position: waypoint.position,
|
||||
lon: waypoint.lon,
|
||||
lat: waypoint.lat,
|
||||
}),
|
||||
),
|
||||
);
|
||||
candidate.setWaySteps(carpoolPathCreator.getCrewCarpoolPath());
|
||||
});
|
||||
// console.log(
|
||||
// candidates[0]
|
||||
// .getProps()
|
||||
// .waySteps?.map((waystep: WayStep) => waystep.actors),
|
||||
// );
|
||||
return candidates;
|
||||
};
|
||||
}
|
||||
|
||||
// complete = async (candidates: Candidate[]): Promise<Candidate[]> => {
|
||||
|
|
|
@ -11,7 +11,8 @@ import {
|
|||
PathCreator,
|
||||
PathType,
|
||||
TypedRoute,
|
||||
} from '@modules/ad/core/domain/patch-creator.service';
|
||||
} from '@modules/ad/core/domain/path-creator.service';
|
||||
import { Waypoint as WaypointValueObject } from '@modules/ad/core/domain/value-objects/waypoint.value-object';
|
||||
|
||||
export class MatchQuery extends QueryBase {
|
||||
driver?: boolean;
|
||||
|
@ -37,6 +38,7 @@ export class MatchQuery extends QueryBase {
|
|||
driverRoute?: Route;
|
||||
passengerRoute?: Route;
|
||||
backAzimuth?: number;
|
||||
private readonly originWaypoint: Waypoint;
|
||||
|
||||
constructor(props: MatchRequestDto) {
|
||||
super();
|
||||
|
@ -60,6 +62,9 @@ export class MatchQuery extends QueryBase {
|
|||
this.maxDetourDurationRatio = props.maxDetourDurationRatio;
|
||||
this.page = props.page ?? 1;
|
||||
this.perPage = props.perPage ?? 10;
|
||||
this.originWaypoint = this.waypoints.filter(
|
||||
(waypoint: Waypoint) => waypoint.position == 0,
|
||||
)[0];
|
||||
}
|
||||
|
||||
setMissingMarginDurations = (defaultMarginDuration: number): MatchQuery => {
|
||||
|
@ -126,8 +131,8 @@ export class MatchQuery extends QueryBase {
|
|||
date: initialFromDate,
|
||||
time: this.schedule[0].time,
|
||||
coordinates: {
|
||||
lon: this.waypoints[0].lon,
|
||||
lat: this.waypoints[0].lat,
|
||||
lon: this.originWaypoint.lon,
|
||||
lat: this.originWaypoint.lat,
|
||||
},
|
||||
},
|
||||
this.frequency,
|
||||
|
@ -138,8 +143,8 @@ export class MatchQuery extends QueryBase {
|
|||
date: initialFromDate,
|
||||
time: this.schedule[0].time,
|
||||
coordinates: {
|
||||
lon: this.waypoints[0].lon,
|
||||
lat: this.waypoints[0].lat,
|
||||
lon: this.originWaypoint.lon,
|
||||
lat: this.originWaypoint.lat,
|
||||
},
|
||||
},
|
||||
this.frequency,
|
||||
|
@ -151,8 +156,8 @@ export class MatchQuery extends QueryBase {
|
|||
date: this.fromDate,
|
||||
time: scheduleItem.time,
|
||||
coordinates: {
|
||||
lon: this.waypoints[0].lon,
|
||||
lat: this.waypoints[0].lat,
|
||||
lon: this.originWaypoint.lon,
|
||||
lat: this.originWaypoint.lat,
|
||||
},
|
||||
},
|
||||
this.frequency,
|
||||
|
@ -162,8 +167,8 @@ export class MatchQuery extends QueryBase {
|
|||
date: this.fromDate,
|
||||
time: scheduleItem.time,
|
||||
coordinates: {
|
||||
lon: this.waypoints[0].lon,
|
||||
lat: this.waypoints[0].lat,
|
||||
lon: this.originWaypoint.lon,
|
||||
lat: this.originWaypoint.lat,
|
||||
},
|
||||
},
|
||||
this.frequency,
|
||||
|
@ -177,11 +182,21 @@ export class MatchQuery extends QueryBase {
|
|||
const roles: Role[] = [];
|
||||
if (this.driver) roles.push(Role.DRIVER);
|
||||
if (this.passenger) roles.push(Role.PASSENGER);
|
||||
const pathCreator: PathCreator = new PathCreator(roles, this.waypoints);
|
||||
const pathCreator: PathCreator = new PathCreator(
|
||||
roles,
|
||||
this.waypoints.map(
|
||||
(waypoint: Waypoint) =>
|
||||
new WaypointValueObject({
|
||||
position: waypoint.position,
|
||||
lon: waypoint.lon,
|
||||
lat: waypoint.lat,
|
||||
}),
|
||||
),
|
||||
);
|
||||
try {
|
||||
(
|
||||
await Promise.all(
|
||||
pathCreator.getPaths().map(async (path: Path) => ({
|
||||
pathCreator.getBasePaths().map(async (path: Path) => ({
|
||||
type: path.type,
|
||||
route: await routeProvider.getBasic(path.waypoints),
|
||||
})),
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { AggregateRoot, AggregateID } from '@mobicoop/ddd-library';
|
||||
import { CandidateProps, CreateCandidateProps } from './candidate.types';
|
||||
import { WayStepProps } from './value-objects/waystep.value-object';
|
||||
|
||||
export class CandidateEntity extends AggregateRoot<CandidateProps> {
|
||||
protected readonly _id: AggregateID;
|
||||
|
@ -9,6 +10,10 @@ export class CandidateEntity extends AggregateRoot<CandidateProps> {
|
|||
return new CandidateEntity({ id: create.id, props });
|
||||
};
|
||||
|
||||
setWaySteps = (waySteps: WayStepProps[]): void => {
|
||||
this.props.waySteps = waySteps;
|
||||
};
|
||||
|
||||
validate(): void {
|
||||
// entity business rules validation to protect it's invariant before saving entity to a database
|
||||
}
|
||||
|
|
|
@ -1,20 +1,29 @@
|
|||
import { Role } from './ad.types';
|
||||
import { WaypointProps } from './value-objects/waypoint.value-object';
|
||||
import { WayStepProps } from './value-objects/waystep.value-object';
|
||||
|
||||
// All properties that a Candidate has
|
||||
export interface CandidateProps {
|
||||
role: Role;
|
||||
waypoints: Waypoint[];
|
||||
waypoints: WaypointProps[]; // waypoints of the original Ad
|
||||
waySteps?: WayStepProps[]; // carpool path for the crew (driver + passenger)
|
||||
}
|
||||
|
||||
// Properties that are needed for a Candidate creation
|
||||
export interface CreateCandidateProps {
|
||||
id: string;
|
||||
role: Role;
|
||||
waypoints: Waypoint[];
|
||||
waypoints: WaypointProps[];
|
||||
}
|
||||
|
||||
export type Waypoint = {
|
||||
lon: number;
|
||||
lat: number;
|
||||
position: number;
|
||||
export type Spacetime = {
|
||||
duration: number;
|
||||
distance?: number;
|
||||
};
|
||||
|
||||
export enum Target {
|
||||
START = 'START',
|
||||
INTERMEDIATE = 'INTERMEDIATE',
|
||||
FINISH = 'FINISH',
|
||||
NEUTRAL = 'NEUTRAL',
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Route } from '@modules/geography/core/domain/route.types';
|
||||
import { Role } from './ad.types';
|
||||
import { Waypoint } from './candidate.types';
|
||||
import { Waypoint } from './value-objects/waypoint.value-object';
|
||||
|
||||
export class PathCreator {
|
||||
constructor(
|
||||
|
@ -8,7 +8,7 @@ export class PathCreator {
|
|||
private readonly waypoints: Waypoint[],
|
||||
) {}
|
||||
|
||||
public getPaths = (): Path[] => {
|
||||
public getBasePaths = (): Path[] => {
|
||||
const paths: Path[] = [];
|
||||
if (
|
||||
this.roles.includes(Role.DRIVER) &&
|
|
@ -0,0 +1,27 @@
|
|||
import { ValueObject } from '@mobicoop/ddd-library';
|
||||
import { Role } from '../ad.types';
|
||||
import { Target } from '../candidate.types';
|
||||
|
||||
/** Note:
|
||||
* Value Objects with multiple properties can contain
|
||||
* other Value Objects inside if needed.
|
||||
* */
|
||||
|
||||
export interface ActorProps {
|
||||
role: Role;
|
||||
target: Target;
|
||||
}
|
||||
|
||||
export class Actor extends ValueObject<ActorProps> {
|
||||
get role(): Role {
|
||||
return this.props.role;
|
||||
}
|
||||
|
||||
get target(): Target {
|
||||
return this.props.target;
|
||||
}
|
||||
|
||||
protected validate(): void {
|
||||
return;
|
||||
}
|
||||
}
|
|
@ -1,18 +1,13 @@
|
|||
import {
|
||||
ArgumentInvalidException,
|
||||
ArgumentOutOfRangeException,
|
||||
ValueObject,
|
||||
} from '@mobicoop/ddd-library';
|
||||
import { ArgumentInvalidException, ValueObject } from '@mobicoop/ddd-library';
|
||||
import { PointProps } from './point.value-object';
|
||||
|
||||
/** Note:
|
||||
* Value Objects with multiple properties can contain
|
||||
* other Value Objects inside if needed.
|
||||
* */
|
||||
|
||||
export interface WaypointProps {
|
||||
export interface WaypointProps extends PointProps {
|
||||
position: number;
|
||||
lon: number;
|
||||
lat: number;
|
||||
}
|
||||
|
||||
export class Waypoint extends ValueObject<WaypointProps> {
|
||||
|
@ -33,9 +28,5 @@ export class Waypoint extends ValueObject<WaypointProps> {
|
|||
throw new ArgumentInvalidException(
|
||||
'position must be greater than or equal to 0',
|
||||
);
|
||||
if (props.lon > 180 || props.lon < -180)
|
||||
throw new ArgumentOutOfRangeException('lon must be between -180 and 180');
|
||||
if (props.lat > 90 || props.lat < -90)
|
||||
throw new ArgumentOutOfRangeException('lat must be between -90 and 90');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
import {
|
||||
ArgumentOutOfRangeException,
|
||||
ValueObject,
|
||||
} from '@mobicoop/ddd-library';
|
||||
import { WaypointProps } from './waypoint.value-object';
|
||||
import { Actor } from './actor.value-object';
|
||||
import { Role } from '../ad.types';
|
||||
|
||||
/** Note:
|
||||
* Value Objects with multiple properties can contain
|
||||
* other Value Objects inside if needed.
|
||||
* */
|
||||
|
||||
export interface WayStepProps extends WaypointProps {
|
||||
actors: Actor[];
|
||||
}
|
||||
|
||||
export class WayStep extends ValueObject<WayStepProps> {
|
||||
get position(): number {
|
||||
return this.props.position;
|
||||
}
|
||||
|
||||
get lon(): number {
|
||||
return this.props.lon;
|
||||
}
|
||||
|
||||
get lat(): number {
|
||||
return this.props.lat;
|
||||
}
|
||||
|
||||
get actors(): Actor[] {
|
||||
return this.props.actors;
|
||||
}
|
||||
|
||||
protected validate(props: WayStepProps): void {
|
||||
if (props.actors.length <= 0)
|
||||
throw new ArgumentOutOfRangeException('at least one actor is required');
|
||||
if (
|
||||
props.actors.filter((actor: Actor) => actor.role == Role.DRIVER).length >
|
||||
1
|
||||
)
|
||||
throw new ArgumentOutOfRangeException(
|
||||
'a waystep can contain only one driver',
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
import { Role } from './ad.types';
|
||||
import { Target } from './candidate.types';
|
||||
import { Actor } from './value-objects/actor.value-object';
|
||||
import { Waypoint } from './value-objects/waypoint.value-object';
|
||||
import { WayStep } from './value-objects/waystep.value-object';
|
||||
|
||||
export class WayStepsCreator {
|
||||
constructor(
|
||||
private readonly driverWaypoints: Waypoint[],
|
||||
private readonly passengerWaypoints: Waypoint[],
|
||||
) {}
|
||||
|
||||
public getCrewCarpoolPath = (): WayStep[] => this._createPassengerWaysteps();
|
||||
|
||||
private _createPassengerWaysteps = (): WayStep[] => {
|
||||
const waysteps: WayStep[] = [];
|
||||
this.passengerWaypoints.forEach((passengerWaypoint: Waypoint) => {
|
||||
const waystep: WayStep = new WayStep({
|
||||
lon: passengerWaypoint.lon,
|
||||
lat: passengerWaypoint.lat,
|
||||
position: passengerWaypoint.position,
|
||||
actors: [
|
||||
new Actor({
|
||||
role: Role.PASSENGER,
|
||||
target: this._getTarget(
|
||||
passengerWaypoint.position,
|
||||
this.passengerWaypoints,
|
||||
),
|
||||
}),
|
||||
],
|
||||
});
|
||||
if (
|
||||
this.driverWaypoints.filter((driverWaypoint: Waypoint) =>
|
||||
this._isSameWaypoint(driverWaypoint, passengerWaypoint),
|
||||
).length > 0
|
||||
) {
|
||||
waystep.actors.push(
|
||||
new Actor({
|
||||
role: Role.DRIVER,
|
||||
target: Target.NEUTRAL,
|
||||
}),
|
||||
);
|
||||
}
|
||||
waysteps.push(waystep);
|
||||
});
|
||||
return waysteps;
|
||||
};
|
||||
|
||||
private _isSameWaypoint = (
|
||||
waypoint1: Waypoint,
|
||||
waypoint2: Waypoint,
|
||||
): boolean =>
|
||||
waypoint1.lon === waypoint2.lon && waypoint1.lat === waypoint2.lat;
|
||||
|
||||
private _getTarget = (position: number, waypoints: Waypoint[]): Target =>
|
||||
position == 0
|
||||
? Target.START
|
||||
: position ==
|
||||
Math.max(...waypoints.map((waypoint: Waypoint) => waypoint.position))
|
||||
? Target.FINISH
|
||||
: Target.INTERMEDIATE;
|
||||
}
|
|
@ -1,9 +1,8 @@
|
|||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { RouteProviderPort } from '../core/application/ports/route-provider.port';
|
||||
import { Waypoint } from '../core/application/types/waypoint.type';
|
||||
import { GetBasicRouteControllerPort } from '@modules/geography/core/application/ports/get-basic-route-controller.port';
|
||||
import { AD_GET_BASIC_ROUTE_CONTROLLER } from '../ad.di-tokens';
|
||||
import { Route } from '@modules/geography/core/domain/route.types';
|
||||
import { Route, Waypoint } from '@modules/geography/core/domain/route.types';
|
||||
|
||||
@Injectable()
|
||||
export class RouteProvider implements RouteProviderPort {
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import { Role } from '@modules/ad/core/domain/ad.types';
|
||||
import { Target } from '@modules/ad/core/domain/candidate.types';
|
||||
import { Actor } from '@modules/ad/core/domain/value-objects/actor.value-object';
|
||||
|
||||
describe('Actor value object', () => {
|
||||
it('should create an actor value object', () => {
|
||||
const actorVO = new Actor({
|
||||
role: Role.DRIVER,
|
||||
target: Target.START,
|
||||
});
|
||||
expect(actorVO.role).toBe(Role.DRIVER);
|
||||
expect(actorVO.target).toBe(Target.START);
|
||||
});
|
||||
});
|
|
@ -40,6 +40,16 @@ const mockAdRepository = {
|
|||
id: 'cc260669-1c6d-441f-80a5-19cd59afb777',
|
||||
getProps: jest.fn().mockImplementation(() => ({
|
||||
role: Role.DRIVER,
|
||||
waypoints: [
|
||||
{
|
||||
lat: 48.68787,
|
||||
lon: 6.165871,
|
||||
},
|
||||
{
|
||||
lat: 48.97878,
|
||||
lon: 2.45787,
|
||||
},
|
||||
],
|
||||
})),
|
||||
},
|
||||
]),
|
||||
|
|
|
@ -63,13 +63,13 @@ const candidates: CandidateEntity[] = [
|
|||
waypoints: [
|
||||
{
|
||||
position: 0,
|
||||
lat: 48.668487,
|
||||
lon: 6.178457,
|
||||
lat: 48.689445,
|
||||
lon: 6.17651,
|
||||
},
|
||||
{
|
||||
position: 1,
|
||||
lat: 48.897457,
|
||||
lon: 2.3688487,
|
||||
lat: 48.8566,
|
||||
lon: 2.3522,
|
||||
},
|
||||
],
|
||||
}),
|
||||
|
|
|
@ -1,26 +1,31 @@
|
|||
import { Role } from '@modules/ad/core/domain/ad.types';
|
||||
import { Waypoint } from '@modules/ad/core/domain/candidate.types';
|
||||
import {
|
||||
Path,
|
||||
PathCreator,
|
||||
PathType,
|
||||
} from '@modules/ad/core/domain/patch-creator.service';
|
||||
} from '@modules/ad/core/domain/path-creator.service';
|
||||
import { Waypoint } from '@modules/ad/core/domain/value-objects/waypoint.value-object';
|
||||
|
||||
const originWaypoint: Waypoint = {
|
||||
const originWaypoint: Waypoint = new Waypoint({
|
||||
position: 0,
|
||||
lat: 48.689445,
|
||||
lon: 6.17651,
|
||||
};
|
||||
const destinationWaypoint: Waypoint = {
|
||||
});
|
||||
const destinationWaypoint: Waypoint = new Waypoint({
|
||||
position: 1,
|
||||
lat: 48.8566,
|
||||
lon: 2.3522,
|
||||
};
|
||||
const intermediateWaypoint: Waypoint = {
|
||||
});
|
||||
const intermediateWaypoint: Waypoint = new Waypoint({
|
||||
position: 1,
|
||||
lat: 48.74488,
|
||||
lon: 4.8972,
|
||||
};
|
||||
});
|
||||
const destinationWaypointWithIntermediateWaypoint: Waypoint = new Waypoint({
|
||||
position: 2,
|
||||
lat: 48.8566,
|
||||
lon: 2.3522,
|
||||
});
|
||||
|
||||
describe('Path Creator Service', () => {
|
||||
it('should create a path for a driver only', () => {
|
||||
|
@ -28,7 +33,7 @@ describe('Path Creator Service', () => {
|
|||
[Role.DRIVER],
|
||||
[originWaypoint, destinationWaypoint],
|
||||
);
|
||||
const paths: Path[] = pathCreator.getPaths();
|
||||
const paths: Path[] = pathCreator.getBasePaths();
|
||||
expect(paths).toHaveLength(1);
|
||||
expect(paths[0].type).toBe(PathType.DRIVER);
|
||||
});
|
||||
|
@ -37,7 +42,7 @@ describe('Path Creator Service', () => {
|
|||
[Role.PASSENGER],
|
||||
[originWaypoint, destinationWaypoint],
|
||||
);
|
||||
const paths: Path[] = pathCreator.getPaths();
|
||||
const paths: Path[] = pathCreator.getBasePaths();
|
||||
expect(paths).toHaveLength(1);
|
||||
expect(paths[0].type).toBe(PathType.PASSENGER);
|
||||
});
|
||||
|
@ -46,7 +51,7 @@ describe('Path Creator Service', () => {
|
|||
[Role.DRIVER, Role.PASSENGER],
|
||||
[originWaypoint, destinationWaypoint],
|
||||
);
|
||||
const paths: Path[] = pathCreator.getPaths();
|
||||
const paths: Path[] = pathCreator.getBasePaths();
|
||||
expect(paths).toHaveLength(1);
|
||||
expect(paths[0].type).toBe(PathType.GENERIC);
|
||||
});
|
||||
|
@ -56,10 +61,10 @@ describe('Path Creator Service', () => {
|
|||
[
|
||||
originWaypoint,
|
||||
intermediateWaypoint,
|
||||
{ ...destinationWaypoint, position: 2 },
|
||||
destinationWaypointWithIntermediateWaypoint,
|
||||
],
|
||||
);
|
||||
const paths: Path[] = pathCreator.getPaths();
|
||||
const paths: Path[] = pathCreator.getBasePaths();
|
||||
expect(paths).toHaveLength(2);
|
||||
expect(
|
||||
paths.filter((path: Path) => path.type == PathType.DRIVER),
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
import { ArgumentOutOfRangeException } from '@mobicoop/ddd-library';
|
||||
import { Role } from '@modules/ad/core/domain/ad.types';
|
||||
import { Target } from '@modules/ad/core/domain/candidate.types';
|
||||
import { Actor } from '@modules/ad/core/domain/value-objects/actor.value-object';
|
||||
import { WayStep } from '@modules/ad/core/domain/value-objects/waystep.value-object';
|
||||
|
||||
describe('WayStep value object', () => {
|
||||
it('should create a waystep value object', () => {
|
||||
const wayStepVO = new WayStep({
|
||||
lat: 48.689445,
|
||||
lon: 6.17651,
|
||||
position: 0,
|
||||
actors: [
|
||||
new Actor({
|
||||
role: Role.DRIVER,
|
||||
target: Target.NEUTRAL,
|
||||
}),
|
||||
new Actor({
|
||||
role: Role.PASSENGER,
|
||||
target: Target.START,
|
||||
}),
|
||||
],
|
||||
});
|
||||
expect(wayStepVO.position).toBe(0);
|
||||
expect(wayStepVO.lon).toBe(6.17651);
|
||||
expect(wayStepVO.lat).toBe(48.689445);
|
||||
expect(wayStepVO.actors).toHaveLength(2);
|
||||
});
|
||||
it('should throw an exception if actors is empty', () => {
|
||||
try {
|
||||
new WayStep({
|
||||
lat: 48.689445,
|
||||
lon: 6.17651,
|
||||
position: 0,
|
||||
actors: [],
|
||||
});
|
||||
} catch (e: any) {
|
||||
expect(e).toBeInstanceOf(ArgumentOutOfRangeException);
|
||||
}
|
||||
});
|
||||
it('should throw an exception if actors contains more than one driver', () => {
|
||||
try {
|
||||
new WayStep({
|
||||
lat: 48.689445,
|
||||
lon: 6.17651,
|
||||
position: 0,
|
||||
actors: [
|
||||
new Actor({
|
||||
role: Role.DRIVER,
|
||||
target: Target.NEUTRAL,
|
||||
}),
|
||||
new Actor({
|
||||
role: Role.DRIVER,
|
||||
target: Target.START,
|
||||
}),
|
||||
],
|
||||
});
|
||||
} catch (e: any) {
|
||||
expect(e).toBeInstanceOf(ArgumentOutOfRangeException);
|
||||
}
|
||||
});
|
||||
});
|
|
@ -1,6 +1,6 @@
|
|||
import { QueryBase } from '@mobicoop/ddd-library';
|
||||
import { Waypoint } from '@modules/geography/core/domain/route.types';
|
||||
import { GeorouterSettings } from '../../types/georouter-settings.type';
|
||||
import { Waypoint } from '@modules/geography/core/domain/route.types';
|
||||
|
||||
export class GetRouteQuery extends QueryBase {
|
||||
readonly waypoints: Waypoint[];
|
||||
|
|
|
@ -20,6 +20,7 @@ export interface CreateRouteProps {
|
|||
georouterSettings: GeorouterSettings;
|
||||
}
|
||||
|
||||
// Types used outside the domain
|
||||
export type Route = {
|
||||
distance: number;
|
||||
duration: number;
|
||||
|
|
|
@ -1,30 +1,17 @@
|
|||
import {
|
||||
ArgumentInvalidException,
|
||||
ArgumentOutOfRangeException,
|
||||
ValueObject,
|
||||
} from '@mobicoop/ddd-library';
|
||||
import { ArgumentInvalidException, ValueObject } from '@mobicoop/ddd-library';
|
||||
import { PointProps } from './point.value-object';
|
||||
|
||||
/** Note:
|
||||
* Value Objects with multiple properties can contain
|
||||
* other Value Objects inside if needed.
|
||||
* */
|
||||
|
||||
export interface StepProps {
|
||||
lon: number;
|
||||
lat: number;
|
||||
export interface StepProps extends PointProps {
|
||||
duration: number;
|
||||
distance: number;
|
||||
}
|
||||
|
||||
export class Step extends ValueObject<StepProps> {
|
||||
get lon(): number {
|
||||
return this.props.lon;
|
||||
}
|
||||
|
||||
get lat(): number {
|
||||
return this.props.lat;
|
||||
}
|
||||
|
||||
get duration(): number {
|
||||
return this.props.duration;
|
||||
}
|
||||
|
@ -33,6 +20,14 @@ export class Step extends ValueObject<StepProps> {
|
|||
return this.props.distance;
|
||||
}
|
||||
|
||||
get lon(): number {
|
||||
return this.props.lon;
|
||||
}
|
||||
|
||||
get lat(): number {
|
||||
return this.props.lat;
|
||||
}
|
||||
|
||||
protected validate(props: StepProps): void {
|
||||
if (props.duration < 0)
|
||||
throw new ArgumentInvalidException(
|
||||
|
@ -42,9 +37,5 @@ export class Step extends ValueObject<StepProps> {
|
|||
throw new ArgumentInvalidException(
|
||||
'distance must be greater than or equal to 0',
|
||||
);
|
||||
if (props.lon > 180 || props.lon < -180)
|
||||
throw new ArgumentOutOfRangeException('lon must be between -180 and 180');
|
||||
if (props.lat > 90 || props.lat < -90)
|
||||
throw new ArgumentOutOfRangeException('lat must be between -90 and 90');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,13 @@
|
|||
import {
|
||||
ArgumentInvalidException,
|
||||
ArgumentOutOfRangeException,
|
||||
ValueObject,
|
||||
} from '@mobicoop/ddd-library';
|
||||
import { ArgumentInvalidException, ValueObject } from '@mobicoop/ddd-library';
|
||||
import { PointProps } from './point.value-object';
|
||||
|
||||
/** Note:
|
||||
* Value Objects with multiple properties can contain
|
||||
* other Value Objects inside if needed.
|
||||
* */
|
||||
|
||||
export interface WaypointProps {
|
||||
export interface WaypointProps extends PointProps {
|
||||
position: number;
|
||||
lon: number;
|
||||
lat: number;
|
||||
}
|
||||
|
||||
export class Waypoint extends ValueObject<WaypointProps> {
|
||||
|
@ -33,9 +28,5 @@ export class Waypoint extends ValueObject<WaypointProps> {
|
|||
throw new ArgumentInvalidException(
|
||||
'position must be greater than or equal to 0',
|
||||
);
|
||||
if (props.lon > 180 || props.lon < -180)
|
||||
throw new ArgumentOutOfRangeException('lon must be between -180 and 180');
|
||||
if (props.lat > 90 || props.lat < -90)
|
||||
throw new ArgumentOutOfRangeException('lat must be between -90 and 90');
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue