Merge branch 'removeConfigurationPackageV3' into 'main'
Update default configuration management See merge request v3/service/matcher!14
This commit is contained in:
commit
62e4015ea7
40
.env.dist
40
.env.dist
|
@ -21,43 +21,3 @@ REDIS_MATCHING_TTL=900
|
||||||
|
|
||||||
# CACHE
|
# CACHE
|
||||||
CACHE_TTL=5000
|
CACHE_TTL=5000
|
||||||
|
|
||||||
# DEFAULT CONFIGURATION
|
|
||||||
|
|
||||||
# algorithm type
|
|
||||||
ALGORITHM=PASSENGER_ORIENTED
|
|
||||||
# max distance in metres between driver
|
|
||||||
# route and passenger pick-up / drop-off
|
|
||||||
REMOTENESS=15000
|
|
||||||
# use passenger proportion
|
|
||||||
USE_PROPORTION=true
|
|
||||||
# minimal driver proportion
|
|
||||||
PROPORTION=0.3
|
|
||||||
# use azimuth calculation
|
|
||||||
USE_AZIMUTH=true
|
|
||||||
# azimuth margin
|
|
||||||
AZIMUTH_MARGIN=10
|
|
||||||
# margin duration in seconds
|
|
||||||
MARGIN_DURATION=900
|
|
||||||
# default validity duration (in days) for recurrent proposals
|
|
||||||
VALIDITY_DURATION=365
|
|
||||||
# max detour ratio
|
|
||||||
MAX_DETOUR_DISTANCE_RATIO=0.3
|
|
||||||
MAX_DETOUR_DURATION_RATIO=0.3
|
|
||||||
# gerouter type
|
|
||||||
GEOROUTER_TYPE=graphhopper
|
|
||||||
# georouter url
|
|
||||||
GEOROUTER_URL=http://localhost:8989
|
|
||||||
# default carpool departure time margin (in seconds)
|
|
||||||
DEPARTURE_TIME_MARGIN=900
|
|
||||||
# default role
|
|
||||||
ROLE=passenger
|
|
||||||
# seats proposes as driver / requested as passenger
|
|
||||||
SEATS_PROPOSED=3
|
|
||||||
SEATS_REQUESTED=1
|
|
||||||
# accept only same frequency requests
|
|
||||||
STRICT_FREQUENCY=false
|
|
||||||
# default timezone
|
|
||||||
TIMEZONE=Europe/Paris
|
|
||||||
# number of matching results per page
|
|
||||||
PER_PAGE=10
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
34
package.json
34
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@mobicoop/matcher",
|
"name": "@mobicoop/matcher",
|
||||||
"version": "1.2.0",
|
"version": "1.3.0",
|
||||||
"description": "Mobicoop V3 Matcher",
|
"description": "Mobicoop V3 Matcher",
|
||||||
"author": "sbriat",
|
"author": "sbriat",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
@ -30,14 +30,14 @@
|
||||||
"migrate:deploy": "npx prisma migrate deploy"
|
"migrate:deploy": "npx prisma migrate deploy"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@grpc/grpc-js": "^1.9.6",
|
"@grpc/grpc-js": "^1.9.9",
|
||||||
"@grpc/proto-loader": "^0.7.10",
|
"@grpc/proto-loader": "^0.7.10",
|
||||||
"@songkeys/nestjs-redis": "^10.0.0",
|
"@songkeys/nestjs-redis": "^10.0.0",
|
||||||
"@mobicoop/configuration-module": "^3.0.0",
|
"@mobicoop/configuration-module": "^7.2.1",
|
||||||
"@mobicoop/ddd-library": "^2.0.0",
|
"@mobicoop/ddd-library": "^2.1.1",
|
||||||
"@mobicoop/health-module": "^2.3.1",
|
"@mobicoop/health-module": "^2.3.1",
|
||||||
"@mobicoop/message-broker-module": "^2.1.1",
|
"@mobicoop/message-broker-module": "^2.1.1",
|
||||||
"@nestjs/axios": "^3.0.0",
|
"@nestjs/axios": "^3.0.1",
|
||||||
"@nestjs/cache-manager": "^2.1.0",
|
"@nestjs/cache-manager": "^2.1.0",
|
||||||
"@nestjs/common": "^10.2.7",
|
"@nestjs/common": "^10.2.7",
|
||||||
"@nestjs/config": "^3.1.1",
|
"@nestjs/config": "^3.1.1",
|
||||||
|
@ -47,8 +47,8 @@
|
||||||
"@nestjs/microservices": "^10.2.7",
|
"@nestjs/microservices": "^10.2.7",
|
||||||
"@nestjs/platform-express": "^10.2.7",
|
"@nestjs/platform-express": "^10.2.7",
|
||||||
"@nestjs/terminus": "^10.1.1",
|
"@nestjs/terminus": "^10.1.1",
|
||||||
"@prisma/client": "^5.4.2",
|
"@prisma/client": "^5.5.2",
|
||||||
"axios": "^1.5.1",
|
"axios": "^1.6.0",
|
||||||
"cache-manager": "^5.2.4",
|
"cache-manager": "^5.2.4",
|
||||||
"cache-manager-ioredis-yet": "^1.2.2",
|
"cache-manager-ioredis-yet": "^1.2.2",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
|
@ -63,23 +63,23 @@
|
||||||
"timezonecomplete": "^5.12.4"
|
"timezonecomplete": "^5.12.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nestjs/cli": "^10.1.18",
|
"@nestjs/cli": "^10.2.1",
|
||||||
"@nestjs/schematics": "^10.0.2",
|
"@nestjs/schematics": "^10.0.3",
|
||||||
"@nestjs/testing": "^10.2.7",
|
"@nestjs/testing": "^10.2.7",
|
||||||
"@types/express": "^4.17.20",
|
"@types/express": "^4.17.20",
|
||||||
"@types/jest": "29.5.6",
|
"@types/jest": "29.5.7",
|
||||||
"@types/node": "20.8.6",
|
"@types/node": "20.8.10",
|
||||||
"@types/supertest": "^2.0.14",
|
"@types/supertest": "^2.0.15",
|
||||||
"@types/uuid": "^9.0.5",
|
"@types/uuid": "^9.0.6",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.8.0",
|
"@typescript-eslint/eslint-plugin": "^6.9.1",
|
||||||
"@typescript-eslint/parser": "^6.8.0",
|
"@typescript-eslint/parser": "^6.9.1",
|
||||||
"dotenv-cli": "^7.3.0",
|
"dotenv-cli": "^7.3.0",
|
||||||
"eslint": "^8.51.0",
|
"eslint": "^8.52.0",
|
||||||
"eslint-config-prettier": "^9.0.0",
|
"eslint-config-prettier": "^9.0.0",
|
||||||
"eslint-plugin-prettier": "^5.0.1",
|
"eslint-plugin-prettier": "^5.0.1",
|
||||||
"jest": "29.7.0",
|
"jest": "29.7.0",
|
||||||
"prettier": "^3.0.3",
|
"prettier": "^3.0.3",
|
||||||
"prisma": "^5.4.2",
|
"prisma": "^5.5.2",
|
||||||
"source-map-support": "^0.5.21",
|
"source-map-support": "^0.5.21",
|
||||||
"supertest": "^6.3.3",
|
"supertest": "^6.3.3",
|
||||||
"ts-jest": "29.1.1",
|
"ts-jest": "29.1.1",
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
import { ConfigModule } from '@nestjs/config';
|
||||||
import { AdModule } from './modules/ad/ad.module';
|
import { AdModule } from './modules/ad/ad.module';
|
||||||
import {
|
|
||||||
ConfigurationModule,
|
|
||||||
ConfigurationModuleOptions,
|
|
||||||
} from '@mobicoop/configuration-module';
|
|
||||||
import { EventEmitterModule } from '@nestjs/event-emitter';
|
import { EventEmitterModule } from '@nestjs/event-emitter';
|
||||||
import { RequestContextModule } from 'nestjs-request-context';
|
import { RequestContextModule } from 'nestjs-request-context';
|
||||||
import { MessagerModule } from '@modules/messager/messager.module';
|
import { MessagerModule } from '@modules/messager/messager.module';
|
||||||
|
@ -17,9 +13,6 @@ import { GeographyModule } from '@modules/geography/geography.module';
|
||||||
import {
|
import {
|
||||||
HEALTH_AD_REPOSITORY,
|
HEALTH_AD_REPOSITORY,
|
||||||
HEALTH_CRITICAL_LOGGING_KEY,
|
HEALTH_CRITICAL_LOGGING_KEY,
|
||||||
SERVICE_CONFIGURATION_DELETE_QUEUE,
|
|
||||||
SERVICE_CONFIGURATION_PROPAGATE_QUEUE,
|
|
||||||
SERVICE_CONFIGURATION_SET_QUEUE,
|
|
||||||
SERVICE_NAME,
|
SERVICE_NAME,
|
||||||
} from './app.constants';
|
} from './app.constants';
|
||||||
|
|
||||||
|
@ -28,36 +21,6 @@ import {
|
||||||
ConfigModule.forRoot({ isGlobal: true }),
|
ConfigModule.forRoot({ isGlobal: true }),
|
||||||
EventEmitterModule.forRoot(),
|
EventEmitterModule.forRoot(),
|
||||||
RequestContextModule,
|
RequestContextModule,
|
||||||
ConfigurationModule.forRootAsync({
|
|
||||||
imports: [ConfigModule],
|
|
||||||
inject: [ConfigService],
|
|
||||||
useFactory: async (
|
|
||||||
configService: ConfigService,
|
|
||||||
): Promise<ConfigurationModuleOptions> => ({
|
|
||||||
domain: configService.get<string>(
|
|
||||||
'SERVICE_CONFIGURATION_DOMAIN',
|
|
||||||
) as string,
|
|
||||||
messageBroker: {
|
|
||||||
uri: configService.get<string>('MESSAGE_BROKER_URI') as string,
|
|
||||||
exchange: {
|
|
||||||
name: configService.get<string>(
|
|
||||||
'MESSAGE_BROKER_EXCHANGE',
|
|
||||||
) as string,
|
|
||||||
durable: configService.get<boolean>(
|
|
||||||
'MESSAGE_BROKER_EXCHANGE_DURABILITY',
|
|
||||||
) as boolean,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
redis: {
|
|
||||||
host: configService.get<string>('REDIS_HOST') as string,
|
|
||||||
password: configService.get<string>('REDIS_PASSWORD'),
|
|
||||||
port: configService.get<number>('REDIS_PORT') as number,
|
|
||||||
},
|
|
||||||
setConfigurationQueue: SERVICE_CONFIGURATION_SET_QUEUE,
|
|
||||||
deleteConfigurationQueue: SERVICE_CONFIGURATION_DELETE_QUEUE,
|
|
||||||
propagateConfigurationQueue: SERVICE_CONFIGURATION_PROPAGATE_QUEUE,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
HealthModule.forRootAsync({
|
HealthModule.forRootAsync({
|
||||||
imports: [AdModule, MessagerModule],
|
imports: [AdModule, MessagerModule],
|
||||||
inject: [AD_REPOSITORY, MESSAGE_PUBLISHER],
|
inject: [AD_REPOSITORY, MESSAGE_PUBLISHER],
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
import {
|
||||||
|
ConfigurationDomainGet,
|
||||||
|
ConfigurationType,
|
||||||
|
} from '@mobicoop/configuration-module';
|
||||||
|
|
||||||
|
export const CARPOOL_CONFIG_ROLE = 'role';
|
||||||
|
export const CARPOOL_CONFIG_SEATS_PROPOSED = 'seatsProposed';
|
||||||
|
export const CARPOOL_CONFIG_SEATS_REQUESTED = 'seatsRequested';
|
||||||
|
export const CARPOOL_CONFIG_DEPARTURE_TIME_MARGIN = 'departureTimeMargin';
|
||||||
|
export const CARPOOL_CONFIG_STRICT_FREQUENCY = 'strictFrequency';
|
||||||
|
|
||||||
|
export const CarpoolConfig: ConfigurationDomainGet[] = [
|
||||||
|
{
|
||||||
|
key: CARPOOL_CONFIG_ROLE,
|
||||||
|
type: ConfigurationType.STRING,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: CARPOOL_CONFIG_SEATS_PROPOSED,
|
||||||
|
type: ConfigurationType.INT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: CARPOOL_CONFIG_SEATS_REQUESTED,
|
||||||
|
type: ConfigurationType.INT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: CARPOOL_CONFIG_DEPARTURE_TIME_MARGIN,
|
||||||
|
type: ConfigurationType.INT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: CARPOOL_CONFIG_STRICT_FREQUENCY,
|
||||||
|
type: ConfigurationType.BOOLEAN,
|
||||||
|
},
|
||||||
|
];
|
|
@ -9,10 +9,12 @@ export const AD_GET_DETAILED_ROUTE_CONTROLLER = Symbol(
|
||||||
'AD_GET_DETAILED_ROUTE_CONTROLLER',
|
'AD_GET_DETAILED_ROUTE_CONTROLLER',
|
||||||
);
|
);
|
||||||
export const AD_ROUTE_PROVIDER = Symbol('AD_ROUTE_PROVIDER');
|
export const AD_ROUTE_PROVIDER = Symbol('AD_ROUTE_PROVIDER');
|
||||||
export const PARAMS_PROVIDER = Symbol('PARAMS_PROVIDER');
|
|
||||||
export const TIMEZONE_FINDER = Symbol('TIMEZONE_FINDER');
|
export const TIMEZONE_FINDER = Symbol('TIMEZONE_FINDER');
|
||||||
export const TIME_CONVERTER = Symbol('TIME_CONVERTER');
|
export const TIME_CONVERTER = Symbol('TIME_CONVERTER');
|
||||||
export const INPUT_DATETIME_TRANSFORMER = Symbol('INPUT_DATETIME_TRANSFORMER');
|
export const INPUT_DATETIME_TRANSFORMER = Symbol('INPUT_DATETIME_TRANSFORMER');
|
||||||
export const OUTPUT_DATETIME_TRANSFORMER = Symbol(
|
export const OUTPUT_DATETIME_TRANSFORMER = Symbol(
|
||||||
'OUTPUT_DATETIME_TRANSFORMER',
|
'OUTPUT_DATETIME_TRANSFORMER',
|
||||||
);
|
);
|
||||||
|
export const AD_CONFIGURATION_REPOSITORY = Symbol(
|
||||||
|
'AD_CONFIGURATION_REPOSITORY',
|
||||||
|
);
|
||||||
|
|
|
@ -6,13 +6,13 @@ import {
|
||||||
AD_DIRECTION_ENCODER,
|
AD_DIRECTION_ENCODER,
|
||||||
AD_ROUTE_PROVIDER,
|
AD_ROUTE_PROVIDER,
|
||||||
AD_GET_BASIC_ROUTE_CONTROLLER,
|
AD_GET_BASIC_ROUTE_CONTROLLER,
|
||||||
PARAMS_PROVIDER,
|
|
||||||
TIMEZONE_FINDER,
|
TIMEZONE_FINDER,
|
||||||
TIME_CONVERTER,
|
TIME_CONVERTER,
|
||||||
INPUT_DATETIME_TRANSFORMER,
|
INPUT_DATETIME_TRANSFORMER,
|
||||||
AD_GET_DETAILED_ROUTE_CONTROLLER,
|
AD_GET_DETAILED_ROUTE_CONTROLLER,
|
||||||
OUTPUT_DATETIME_TRANSFORMER,
|
OUTPUT_DATETIME_TRANSFORMER,
|
||||||
MATCHING_REPOSITORY,
|
MATCHING_REPOSITORY,
|
||||||
|
AD_CONFIGURATION_REPOSITORY,
|
||||||
} from './ad.di-tokens';
|
} from './ad.di-tokens';
|
||||||
import { MessageBrokerPublisher } from '@mobicoop/message-broker-module';
|
import { MessageBrokerPublisher } from '@mobicoop/message-broker-module';
|
||||||
import { AdRepository } from './infrastructure/ad.repository';
|
import { AdRepository } from './infrastructure/ad.repository';
|
||||||
|
@ -26,7 +26,6 @@ import { GeographyModule } from '@modules/geography/geography.module';
|
||||||
import { CreateAdService } from './core/application/commands/create-ad/create-ad.service';
|
import { CreateAdService } from './core/application/commands/create-ad/create-ad.service';
|
||||||
import { MatchGrpcController } from './interface/grpc-controllers/match.grpc-controller';
|
import { MatchGrpcController } from './interface/grpc-controllers/match.grpc-controller';
|
||||||
import { MatchQueryHandler } from './core/application/queries/match/match.query-handler';
|
import { MatchQueryHandler } from './core/application/queries/match/match.query-handler';
|
||||||
import { DefaultParamsProvider } from './infrastructure/default-params-provider';
|
|
||||||
import { TimezoneFinder } from './infrastructure/timezone-finder';
|
import { TimezoneFinder } from './infrastructure/timezone-finder';
|
||||||
import { TimeConverter } from './infrastructure/time-converter';
|
import { TimeConverter } from './infrastructure/time-converter';
|
||||||
import { InputDateTimeTransformer } from './infrastructure/input-datetime-transformer';
|
import { InputDateTimeTransformer } from './infrastructure/input-datetime-transformer';
|
||||||
|
@ -38,7 +37,12 @@ import { MatchingMapper } from './matching.mapper';
|
||||||
import { CacheModule } from '@nestjs/cache-manager';
|
import { CacheModule } from '@nestjs/cache-manager';
|
||||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||||
import { redisStore } from 'cache-manager-ioredis-yet';
|
import { redisStore } from 'cache-manager-ioredis-yet';
|
||||||
import { RedisClientOptions } from '@songkeys/nestjs-redis';
|
import {
|
||||||
|
RedisClientOptions,
|
||||||
|
RedisModule,
|
||||||
|
RedisModuleOptions,
|
||||||
|
} from '@songkeys/nestjs-redis';
|
||||||
|
import { ConfigurationRepository } from '@mobicoop/configuration-module';
|
||||||
|
|
||||||
const imports = [
|
const imports = [
|
||||||
CqrsModule,
|
CqrsModule,
|
||||||
|
@ -54,6 +58,21 @@ const imports = [
|
||||||
}),
|
}),
|
||||||
inject: [ConfigService],
|
inject: [ConfigService],
|
||||||
}),
|
}),
|
||||||
|
RedisModule.forRootAsync({
|
||||||
|
imports: [ConfigModule],
|
||||||
|
inject: [ConfigService],
|
||||||
|
useFactory: async (
|
||||||
|
configService: ConfigService,
|
||||||
|
): Promise<RedisModuleOptions> => {
|
||||||
|
return {
|
||||||
|
config: {
|
||||||
|
host: configService.get<string>('REDIS_HOST') as string,
|
||||||
|
port: configService.get<number>('REDIS_PORT') as number,
|
||||||
|
password: configService.get<string>('REDIS_PASSWORD'),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}),
|
||||||
GeographyModule,
|
GeographyModule,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -76,6 +95,10 @@ const repositories: Provider[] = [
|
||||||
provide: MATCHING_REPOSITORY,
|
provide: MATCHING_REPOSITORY,
|
||||||
useClass: MatchingRepository,
|
useClass: MatchingRepository,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: AD_CONFIGURATION_REPOSITORY,
|
||||||
|
useClass: ConfigurationRepository,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const messagePublishers: Provider[] = [
|
const messagePublishers: Provider[] = [
|
||||||
|
@ -104,10 +127,6 @@ const adapters: Provider[] = [
|
||||||
provide: AD_GET_DETAILED_ROUTE_CONTROLLER,
|
provide: AD_GET_DETAILED_ROUTE_CONTROLLER,
|
||||||
useClass: GetDetailedRouteController,
|
useClass: GetDetailedRouteController,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
provide: PARAMS_PROVIDER,
|
|
||||||
useClass: DefaultParamsProvider,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
provide: TIMEZONE_FINDER,
|
provide: TIMEZONE_FINDER,
|
||||||
useClass: TimezoneFinder,
|
useClass: TimezoneFinder,
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
import { DefaultParams } from './default-params.type';
|
|
||||||
|
|
||||||
export interface DefaultParamsProviderPort {
|
|
||||||
getParams(): DefaultParams;
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
import { AlgorithmType } from '../types/algorithm.types';
|
|
||||||
|
|
||||||
export type DefaultParams = {
|
|
||||||
DRIVER: boolean;
|
|
||||||
PASSENGER: boolean;
|
|
||||||
SEATS_PROPOSED: number;
|
|
||||||
SEATS_REQUESTED: number;
|
|
||||||
DEPARTURE_TIME_MARGIN: number;
|
|
||||||
STRICT: boolean;
|
|
||||||
TIMEZONE: string;
|
|
||||||
ALGORITHM_TYPE: AlgorithmType;
|
|
||||||
REMOTENESS: number;
|
|
||||||
USE_PROPORTION: boolean;
|
|
||||||
PROPORTION: number;
|
|
||||||
USE_AZIMUTH: boolean;
|
|
||||||
AZIMUTH_MARGIN: number;
|
|
||||||
MAX_DETOUR_DISTANCE_RATIO: number;
|
|
||||||
MAX_DETOUR_DURATION_RATIO: number;
|
|
||||||
PER_PAGE: number;
|
|
||||||
};
|
|
|
@ -1,3 +1,3 @@
|
||||||
export interface TimezoneFinderPort {
|
export interface TimezoneFinderPort {
|
||||||
timezones(lon: number, lat: number, defaultTimezone?: string): string[];
|
timezones(lon: number, lat: number): string[];
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,58 +6,113 @@ import { AlgorithmType } from '../../types/algorithm.types';
|
||||||
import { Inject } from '@nestjs/common';
|
import { Inject } from '@nestjs/common';
|
||||||
import { AdRepositoryPort } from '@modules/ad/core/application/ports/ad.repository.port';
|
import { AdRepositoryPort } from '@modules/ad/core/application/ports/ad.repository.port';
|
||||||
import {
|
import {
|
||||||
|
AD_CONFIGURATION_REPOSITORY,
|
||||||
AD_REPOSITORY,
|
AD_REPOSITORY,
|
||||||
INPUT_DATETIME_TRANSFORMER,
|
INPUT_DATETIME_TRANSFORMER,
|
||||||
MATCHING_REPOSITORY,
|
MATCHING_REPOSITORY,
|
||||||
PARAMS_PROVIDER,
|
|
||||||
} from '@modules/ad/ad.di-tokens';
|
} from '@modules/ad/ad.di-tokens';
|
||||||
import { MatchEntity } from '@modules/ad/core/domain/match.entity';
|
import { MatchEntity } from '@modules/ad/core/domain/match.entity';
|
||||||
import { DefaultParamsProviderPort } from '../../ports/default-params-provider.port';
|
|
||||||
import { DefaultParams } from '../../ports/default-params.type';
|
|
||||||
import { DateTimeTransformerPort } from '../../ports/datetime-transformer.port';
|
import { DateTimeTransformerPort } from '../../ports/datetime-transformer.port';
|
||||||
import { Paginator } from '@mobicoop/ddd-library';
|
import { Paginator } from '@mobicoop/ddd-library';
|
||||||
import { MatchingEntity } from '@modules/ad/core/domain/matching.entity';
|
import { MatchingEntity } from '@modules/ad/core/domain/matching.entity';
|
||||||
import { MatchingRepositoryPort } from '../../ports/matching.repository.port';
|
import { MatchingRepositoryPort } from '../../ports/matching.repository.port';
|
||||||
|
import {
|
||||||
|
ConfigurationDomain,
|
||||||
|
Configurator,
|
||||||
|
GetConfigurationRepositoryPort,
|
||||||
|
} from '@mobicoop/configuration-module';
|
||||||
|
import {
|
||||||
|
CARPOOL_CONFIG_DEPARTURE_TIME_MARGIN,
|
||||||
|
CARPOOL_CONFIG_ROLE,
|
||||||
|
CARPOOL_CONFIG_SEATS_PROPOSED,
|
||||||
|
CARPOOL_CONFIG_SEATS_REQUESTED,
|
||||||
|
CARPOOL_CONFIG_STRICT_FREQUENCY,
|
||||||
|
CarpoolConfig,
|
||||||
|
} from '@modules/ad/ad.constants';
|
||||||
|
import {
|
||||||
|
MATCH_CONFIG_ALGORITHM,
|
||||||
|
MATCH_CONFIG_AZIMUTH_MARGIN,
|
||||||
|
MATCH_CONFIG_MAX_DETOUR_DISTANCE_RATIO,
|
||||||
|
MATCH_CONFIG_MAX_DETOUR_DURATION_RATIO,
|
||||||
|
MATCH_CONFIG_PROPORTION,
|
||||||
|
MATCH_CONFIG_REMOTENESS,
|
||||||
|
MATCH_CONFIG_USE_AZIMUTH,
|
||||||
|
MATCH_CONFIG_USE_PROPORTION,
|
||||||
|
MatchConfig,
|
||||||
|
PAGINATION_CONFIG_PER_PAGE,
|
||||||
|
PaginationConfig,
|
||||||
|
} from '@modules/ad/match.constants';
|
||||||
|
|
||||||
@QueryHandler(MatchQuery)
|
@QueryHandler(MatchQuery)
|
||||||
export class MatchQueryHandler implements IQueryHandler {
|
export class MatchQueryHandler implements IQueryHandler {
|
||||||
private readonly _defaultParams: DefaultParams;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(PARAMS_PROVIDER)
|
@Inject(AD_CONFIGURATION_REPOSITORY)
|
||||||
private readonly defaultParamsProvider: DefaultParamsProviderPort,
|
private readonly configurationRepository: GetConfigurationRepositoryPort,
|
||||||
@Inject(AD_REPOSITORY) private readonly adRepository: AdRepositoryPort,
|
@Inject(AD_REPOSITORY) private readonly adRepository: AdRepositoryPort,
|
||||||
@Inject(MATCHING_REPOSITORY)
|
@Inject(MATCHING_REPOSITORY)
|
||||||
private readonly matchingRepository: MatchingRepositoryPort,
|
private readonly matchingRepository: MatchingRepositoryPort,
|
||||||
@Inject(INPUT_DATETIME_TRANSFORMER)
|
@Inject(INPUT_DATETIME_TRANSFORMER)
|
||||||
private readonly datetimeTransformer: DateTimeTransformerPort,
|
private readonly datetimeTransformer: DateTimeTransformerPort,
|
||||||
) {
|
) {}
|
||||||
this._defaultParams = defaultParamsProvider.getParams();
|
|
||||||
}
|
|
||||||
|
|
||||||
execute = async (query: MatchQuery): Promise<MatchingResult> => {
|
execute = async (query: MatchQuery): Promise<MatchingResult> => {
|
||||||
|
const carpoolConfigurator: Configurator =
|
||||||
|
await this.configurationRepository.mget(
|
||||||
|
ConfigurationDomain.CARPOOL,
|
||||||
|
CarpoolConfig,
|
||||||
|
);
|
||||||
|
const matchConfigurator: Configurator =
|
||||||
|
await this.configurationRepository.mget(
|
||||||
|
ConfigurationDomain.MATCH,
|
||||||
|
MatchConfig,
|
||||||
|
);
|
||||||
|
const paginationConfigurator: Configurator =
|
||||||
|
await this.configurationRepository.mget(
|
||||||
|
ConfigurationDomain.PAGINATION,
|
||||||
|
PaginationConfig,
|
||||||
|
);
|
||||||
query
|
query
|
||||||
.setMissingMarginDurations(this._defaultParams.DEPARTURE_TIME_MARGIN)
|
.setMissingMarginDurations(
|
||||||
.setMissingStrict(this._defaultParams.STRICT)
|
carpoolConfigurator.get<number>(CARPOOL_CONFIG_DEPARTURE_TIME_MARGIN),
|
||||||
|
)
|
||||||
|
.setMissingStrict(
|
||||||
|
carpoolConfigurator.get<boolean>(CARPOOL_CONFIG_STRICT_FREQUENCY),
|
||||||
|
)
|
||||||
.setDefaultDriverAndPassengerParameters({
|
.setDefaultDriverAndPassengerParameters({
|
||||||
driver: this._defaultParams.DRIVER,
|
driver:
|
||||||
passenger: this._defaultParams.PASSENGER,
|
carpoolConfigurator.get<string>(CARPOOL_CONFIG_ROLE) === 'driver',
|
||||||
seatsProposed: this._defaultParams.SEATS_PROPOSED,
|
passenger:
|
||||||
seatsRequested: this._defaultParams.SEATS_REQUESTED,
|
carpoolConfigurator.get<string>(CARPOOL_CONFIG_ROLE) === 'passenger',
|
||||||
|
seatsProposed: carpoolConfigurator.get<number>(
|
||||||
|
CARPOOL_CONFIG_SEATS_PROPOSED,
|
||||||
|
),
|
||||||
|
seatsRequested: carpoolConfigurator.get<number>(
|
||||||
|
CARPOOL_CONFIG_SEATS_REQUESTED,
|
||||||
|
),
|
||||||
})
|
})
|
||||||
.setDefaultAlgorithmParameters({
|
.setDefaultAlgorithmParameters({
|
||||||
algorithmType: this._defaultParams.ALGORITHM_TYPE,
|
algorithmType: matchConfigurator.get<AlgorithmType>(
|
||||||
remoteness: this._defaultParams.REMOTENESS,
|
MATCH_CONFIG_ALGORITHM,
|
||||||
useProportion: this._defaultParams.USE_PROPORTION,
|
),
|
||||||
proportion: this._defaultParams.PROPORTION,
|
remoteness: matchConfigurator.get<number>(MATCH_CONFIG_REMOTENESS),
|
||||||
useAzimuth: this._defaultParams.USE_AZIMUTH,
|
useProportion: matchConfigurator.get<boolean>(
|
||||||
azimuthMargin: this._defaultParams.AZIMUTH_MARGIN,
|
MATCH_CONFIG_USE_PROPORTION,
|
||||||
maxDetourDistanceRatio: this._defaultParams.MAX_DETOUR_DISTANCE_RATIO,
|
),
|
||||||
maxDetourDurationRatio: this._defaultParams.MAX_DETOUR_DURATION_RATIO,
|
proportion: matchConfigurator.get<number>(MATCH_CONFIG_PROPORTION),
|
||||||
|
useAzimuth: matchConfigurator.get<boolean>(MATCH_CONFIG_USE_AZIMUTH),
|
||||||
|
azimuthMargin: matchConfigurator.get<number>(
|
||||||
|
MATCH_CONFIG_AZIMUTH_MARGIN,
|
||||||
|
),
|
||||||
|
maxDetourDistanceRatio: matchConfigurator.get<number>(
|
||||||
|
MATCH_CONFIG_MAX_DETOUR_DISTANCE_RATIO,
|
||||||
|
),
|
||||||
|
maxDetourDurationRatio: matchConfigurator.get<number>(
|
||||||
|
MATCH_CONFIG_MAX_DETOUR_DURATION_RATIO,
|
||||||
|
),
|
||||||
})
|
})
|
||||||
.setDefaultPagination({
|
.setDefaultPagination({
|
||||||
page: 1,
|
page: 1,
|
||||||
perPage: this._defaultParams.PER_PAGE,
|
perPage: paginationConfigurator.get<number>(PAGINATION_CONFIG_PER_PAGE),
|
||||||
})
|
})
|
||||||
.setDatesAndSchedule(this.datetimeTransformer);
|
.setDatesAndSchedule(this.datetimeTransformer);
|
||||||
let matchingEntity: MatchingEntity | undefined = await this._cachedMatching(
|
let matchingEntity: MatchingEntity | undefined = await this._cachedMatching(
|
||||||
|
|
|
@ -1,90 +0,0 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import { ConfigService } from '@nestjs/config';
|
|
||||||
import { DefaultParamsProviderPort } from '../core/application/ports/default-params-provider.port';
|
|
||||||
import { DefaultParams } from '../core/application/ports/default-params.type';
|
|
||||||
import { AlgorithmType } from '../core/application/types/algorithm.types';
|
|
||||||
|
|
||||||
const DRIVER = false;
|
|
||||||
const PASSENGER = true;
|
|
||||||
const SEATS_PROPOSED = 3;
|
|
||||||
const SEATS_REQUESTED = 1;
|
|
||||||
const DEPARTURE_TIME_MARGIN = 900;
|
|
||||||
const TIMEZONE = 'Europe/Paris';
|
|
||||||
const ALGORITHM_TYPE = 'PASSENGER_ORIENTED';
|
|
||||||
const REMOTENESS = 15000;
|
|
||||||
const USE_PROPORTION = true;
|
|
||||||
const PROPORTION = 0.3;
|
|
||||||
const USE_AZIMUTH = true;
|
|
||||||
const AZIMUTH_MARGIN = 10;
|
|
||||||
const MAX_DETOUR_DISTANCE_RATIO = 0.3;
|
|
||||||
const MAX_DETOUR_DURATION_RATIO = 0.3;
|
|
||||||
const PER_PAGE = 10;
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class DefaultParamsProvider implements DefaultParamsProviderPort {
|
|
||||||
constructor(private readonly _configService: ConfigService) {}
|
|
||||||
getParams = (): DefaultParams => ({
|
|
||||||
DRIVER:
|
|
||||||
this._configService.get('ROLE') !== undefined
|
|
||||||
? this._configService.get('ROLE') == 'driver'
|
|
||||||
: DRIVER,
|
|
||||||
SEATS_PROPOSED:
|
|
||||||
this._configService.get('SEATS_PROPOSED') !== undefined
|
|
||||||
? parseInt(this._configService.get('SEATS_PROPOSED') as string)
|
|
||||||
: SEATS_PROPOSED,
|
|
||||||
PASSENGER:
|
|
||||||
this._configService.get('ROLE') !== undefined
|
|
||||||
? this._configService.get('ROLE') == 'passenger'
|
|
||||||
: PASSENGER,
|
|
||||||
SEATS_REQUESTED:
|
|
||||||
this._configService.get('SEATS_REQUESTED') !== undefined
|
|
||||||
? parseInt(this._configService.get('SEATS_REQUESTED') as string)
|
|
||||||
: SEATS_REQUESTED,
|
|
||||||
DEPARTURE_TIME_MARGIN:
|
|
||||||
this._configService.get('DEPARTURE_TIME_MARGIN') !== undefined
|
|
||||||
? parseInt(this._configService.get('DEPARTURE_TIME_MARGIN') as string)
|
|
||||||
: DEPARTURE_TIME_MARGIN,
|
|
||||||
STRICT: this._configService.get('STRICT_FREQUENCY') == 'true',
|
|
||||||
TIMEZONE: this._configService.get('TIMEZONE') ?? TIMEZONE,
|
|
||||||
ALGORITHM_TYPE:
|
|
||||||
AlgorithmType[
|
|
||||||
this._configService.get('ALGORITHM_TYPE') as AlgorithmType
|
|
||||||
] ?? AlgorithmType[ALGORITHM_TYPE],
|
|
||||||
REMOTENESS:
|
|
||||||
this._configService.get('REMOTENESS') !== undefined
|
|
||||||
? parseInt(this._configService.get('REMOTENESS') as string)
|
|
||||||
: REMOTENESS,
|
|
||||||
USE_PROPORTION:
|
|
||||||
this._configService.get('USE_PROPORTION') !== undefined
|
|
||||||
? this._configService.get('USE_PROPORTION') == 'true'
|
|
||||||
: USE_PROPORTION,
|
|
||||||
PROPORTION:
|
|
||||||
this._configService.get('PROPORTION') !== undefined
|
|
||||||
? parseFloat(this._configService.get('PROPORTION') as string)
|
|
||||||
: PROPORTION,
|
|
||||||
USE_AZIMUTH:
|
|
||||||
this._configService.get('USE_AZIMUTH') !== undefined
|
|
||||||
? this._configService.get('USE_AZIMUTH') == 'true'
|
|
||||||
: USE_AZIMUTH,
|
|
||||||
AZIMUTH_MARGIN:
|
|
||||||
this._configService.get('AZIMUTH_MARGIN') !== undefined
|
|
||||||
? parseInt(this._configService.get('AZIMUTH_MARGIN') as string)
|
|
||||||
: AZIMUTH_MARGIN,
|
|
||||||
MAX_DETOUR_DISTANCE_RATIO:
|
|
||||||
this._configService.get('MAX_DETOUR_DISTANCE_RATIO') !== undefined
|
|
||||||
? parseFloat(
|
|
||||||
this._configService.get('MAX_DETOUR_DISTANCE_RATIO') as string,
|
|
||||||
)
|
|
||||||
: MAX_DETOUR_DISTANCE_RATIO,
|
|
||||||
MAX_DETOUR_DURATION_RATIO:
|
|
||||||
this._configService.get('MAX_DETOUR_DURATION_RATIO') !== undefined
|
|
||||||
? parseFloat(
|
|
||||||
this._configService.get('MAX_DETOUR_DURATION_RATIO') as string,
|
|
||||||
)
|
|
||||||
: MAX_DETOUR_DURATION_RATIO,
|
|
||||||
PER_PAGE:
|
|
||||||
this._configService.get('PER_PAGE') !== undefined
|
|
||||||
? parseInt(this._configService.get('PER_PAGE') as string)
|
|
||||||
: PER_PAGE,
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -5,26 +5,16 @@ import {
|
||||||
GeoDateTime,
|
GeoDateTime,
|
||||||
} from '../core/application/ports/datetime-transformer.port';
|
} from '../core/application/ports/datetime-transformer.port';
|
||||||
import { TimeConverterPort } from '../core/application/ports/time-converter.port';
|
import { TimeConverterPort } from '../core/application/ports/time-converter.port';
|
||||||
import {
|
import { TIMEZONE_FINDER, TIME_CONVERTER } from '../ad.di-tokens';
|
||||||
PARAMS_PROVIDER,
|
|
||||||
TIMEZONE_FINDER,
|
|
||||||
TIME_CONVERTER,
|
|
||||||
} from '../ad.di-tokens';
|
|
||||||
import { TimezoneFinderPort } from '../core/application/ports/timezone-finder.port';
|
import { TimezoneFinderPort } from '../core/application/ports/timezone-finder.port';
|
||||||
import { DefaultParamsProviderPort } from '../core/application/ports/default-params-provider.port';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class InputDateTimeTransformer implements DateTimeTransformerPort {
|
export class InputDateTimeTransformer implements DateTimeTransformerPort {
|
||||||
private readonly _defaultTimezone: string;
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(PARAMS_PROVIDER)
|
|
||||||
private readonly defaultParamsProvider: DefaultParamsProviderPort,
|
|
||||||
@Inject(TIMEZONE_FINDER)
|
@Inject(TIMEZONE_FINDER)
|
||||||
private readonly timezoneFinder: TimezoneFinderPort,
|
private readonly timezoneFinder: TimezoneFinderPort,
|
||||||
@Inject(TIME_CONVERTER) private readonly timeConverter: TimeConverterPort,
|
@Inject(TIME_CONVERTER) private readonly timeConverter: TimeConverterPort,
|
||||||
) {
|
) {}
|
||||||
this._defaultTimezone = defaultParamsProvider.getParams().TIMEZONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute the fromDate : if an ad is punctual, the departure date
|
* Compute the fromDate : if an ad is punctual, the departure date
|
||||||
|
@ -39,7 +29,6 @@ export class InputDateTimeTransformer implements DateTimeTransformerPort {
|
||||||
this.timezoneFinder.timezones(
|
this.timezoneFinder.timezones(
|
||||||
geoFromDate.coordinates.lon,
|
geoFromDate.coordinates.lon,
|
||||||
geoFromDate.coordinates.lat,
|
geoFromDate.coordinates.lat,
|
||||||
this._defaultTimezone,
|
|
||||||
)[0],
|
)[0],
|
||||||
)
|
)
|
||||||
.toISOString()
|
.toISOString()
|
||||||
|
@ -76,7 +65,6 @@ export class InputDateTimeTransformer implements DateTimeTransformerPort {
|
||||||
this.timezoneFinder.timezones(
|
this.timezoneFinder.timezones(
|
||||||
geoFromDate.coordinates.lon,
|
geoFromDate.coordinates.lon,
|
||||||
geoFromDate.coordinates.lat,
|
geoFromDate.coordinates.lat,
|
||||||
this._defaultTimezone,
|
|
||||||
)[0],
|
)[0],
|
||||||
);
|
);
|
||||||
return new Date(this.fromDate(geoFromDate, frequency)).getUTCDay();
|
return new Date(this.fromDate(geoFromDate, frequency)).getUTCDay();
|
||||||
|
@ -92,7 +80,6 @@ export class InputDateTimeTransformer implements DateTimeTransformerPort {
|
||||||
this.timezoneFinder.timezones(
|
this.timezoneFinder.timezones(
|
||||||
geoFromDate.coordinates.lon,
|
geoFromDate.coordinates.lon,
|
||||||
geoFromDate.coordinates.lat,
|
geoFromDate.coordinates.lat,
|
||||||
this._defaultTimezone,
|
|
||||||
)[0],
|
)[0],
|
||||||
);
|
);
|
||||||
return this.timeConverter
|
return this.timeConverter
|
||||||
|
@ -102,7 +89,6 @@ export class InputDateTimeTransformer implements DateTimeTransformerPort {
|
||||||
this.timezoneFinder.timezones(
|
this.timezoneFinder.timezones(
|
||||||
geoFromDate.coordinates.lon,
|
geoFromDate.coordinates.lon,
|
||||||
geoFromDate.coordinates.lat,
|
geoFromDate.coordinates.lat,
|
||||||
this._defaultTimezone,
|
|
||||||
)[0],
|
)[0],
|
||||||
)
|
)
|
||||||
.toISOString()
|
.toISOString()
|
||||||
|
|
|
@ -4,13 +4,5 @@ import { TimezoneFinderPort } from '../core/application/ports/timezone-finder.po
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TimezoneFinder implements TimezoneFinderPort {
|
export class TimezoneFinder implements TimezoneFinderPort {
|
||||||
timezones = (
|
timezones = (lon: number, lat: number): string[] => find(lat, lon);
|
||||||
lon: number,
|
|
||||||
lat: number,
|
|
||||||
defaultTimezone?: string,
|
|
||||||
): string[] => {
|
|
||||||
const foundTimezones = find(lat, lon);
|
|
||||||
if (defaultTimezone && foundTimezones.length == 0) return [defaultTimezone];
|
|
||||||
return foundTimezones;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,8 @@ export class AdCreatedMessageHandler {
|
||||||
waypoints: createdAd.waypoints,
|
waypoints: createdAd.waypoints,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
} catch (e: any) {}
|
} catch (e: any) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
import {
|
||||||
|
ConfigurationDomainGet,
|
||||||
|
ConfigurationType,
|
||||||
|
} from '@mobicoop/configuration-module';
|
||||||
|
|
||||||
|
export const MATCH_CONFIG_ALGORITHM = 'algorithm';
|
||||||
|
export const MATCH_CONFIG_REMOTENESS = 'remoteness';
|
||||||
|
export const MATCH_CONFIG_USE_PROPORTION = 'useProportion';
|
||||||
|
export const MATCH_CONFIG_PROPORTION = 'proportion';
|
||||||
|
export const MATCH_CONFIG_USE_AZIMUTH = 'useAzimuth';
|
||||||
|
export const MATCH_CONFIG_AZIMUTH_MARGIN = 'azimuthMargin';
|
||||||
|
export const MATCH_CONFIG_MAX_DETOUR_DISTANCE_RATIO = 'maxDetourDistanceRatio';
|
||||||
|
export const MATCH_CONFIG_MAX_DETOUR_DURATION_RATIO = 'maxDetourDurationRatio';
|
||||||
|
export const PAGINATION_CONFIG_PER_PAGE = 'perPage';
|
||||||
|
|
||||||
|
export const MatchConfig: ConfigurationDomainGet[] = [
|
||||||
|
{
|
||||||
|
key: MATCH_CONFIG_ALGORITHM,
|
||||||
|
type: ConfigurationType.STRING,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: MATCH_CONFIG_REMOTENESS,
|
||||||
|
type: ConfigurationType.INT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: MATCH_CONFIG_USE_PROPORTION,
|
||||||
|
type: ConfigurationType.BOOLEAN,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: MATCH_CONFIG_PROPORTION,
|
||||||
|
type: ConfigurationType.FLOAT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: MATCH_CONFIG_USE_AZIMUTH,
|
||||||
|
type: ConfigurationType.BOOLEAN,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: MATCH_CONFIG_AZIMUTH_MARGIN,
|
||||||
|
type: ConfigurationType.INT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: MATCH_CONFIG_MAX_DETOUR_DISTANCE_RATIO,
|
||||||
|
type: ConfigurationType.FLOAT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: MATCH_CONFIG_MAX_DETOUR_DURATION_RATIO,
|
||||||
|
type: ConfigurationType.FLOAT,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const PaginationConfig: ConfigurationDomainGet[] = [
|
||||||
|
{
|
||||||
|
key: PAGINATION_CONFIG_PER_PAGE,
|
||||||
|
type: ConfigurationType.INT,
|
||||||
|
},
|
||||||
|
];
|
|
@ -53,6 +53,7 @@ const mockAdRepository: AdRepositoryPort = {
|
||||||
insertExtra: jest.fn(),
|
insertExtra: jest.fn(),
|
||||||
findOneById: jest.fn(),
|
findOneById: jest.fn(),
|
||||||
findOne: jest.fn(),
|
findOne: jest.fn(),
|
||||||
|
findAll: jest.fn(),
|
||||||
insert: jest.fn(),
|
insert: jest.fn(),
|
||||||
update: jest.fn(),
|
update: jest.fn(),
|
||||||
updateWhere: jest.fn(),
|
updateWhere: jest.fn(),
|
||||||
|
|
|
@ -1,11 +1,23 @@
|
||||||
import {
|
import {
|
||||||
|
ConfigurationDomain,
|
||||||
|
ConfigurationDomainGet,
|
||||||
|
Configurator,
|
||||||
|
GetConfigurationRepositoryPort,
|
||||||
|
} from '@mobicoop/configuration-module';
|
||||||
|
import {
|
||||||
|
CARPOOL_CONFIG_DEPARTURE_TIME_MARGIN,
|
||||||
|
CARPOOL_CONFIG_ROLE,
|
||||||
|
CARPOOL_CONFIG_SEATS_PROPOSED,
|
||||||
|
CARPOOL_CONFIG_SEATS_REQUESTED,
|
||||||
|
CARPOOL_CONFIG_STRICT_FREQUENCY,
|
||||||
|
} from '@modules/ad/ad.constants';
|
||||||
|
import {
|
||||||
|
AD_CONFIGURATION_REPOSITORY,
|
||||||
AD_REPOSITORY,
|
AD_REPOSITORY,
|
||||||
INPUT_DATETIME_TRANSFORMER,
|
INPUT_DATETIME_TRANSFORMER,
|
||||||
MATCHING_REPOSITORY,
|
MATCHING_REPOSITORY,
|
||||||
PARAMS_PROVIDER,
|
|
||||||
} from '@modules/ad/ad.di-tokens';
|
} from '@modules/ad/ad.di-tokens';
|
||||||
import { DateTimeTransformerPort } from '@modules/ad/core/application/ports/datetime-transformer.port';
|
import { DateTimeTransformerPort } from '@modules/ad/core/application/ports/datetime-transformer.port';
|
||||||
import { DefaultParamsProviderPort } from '@modules/ad/core/application/ports/default-params-provider.port';
|
|
||||||
import { MatchingRepositoryPort } from '@modules/ad/core/application/ports/matching.repository.port';
|
import { MatchingRepositoryPort } from '@modules/ad/core/application/ports/matching.repository.port';
|
||||||
import { RouteProviderPort } from '@modules/ad/core/application/ports/route-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 { MatchQuery } from '@modules/ad/core/application/queries/match/match.query';
|
||||||
|
@ -19,6 +31,17 @@ import { Frequency, Role } from '@modules/ad/core/domain/ad.types';
|
||||||
import { Target } from '@modules/ad/core/domain/candidate.types';
|
import { Target } from '@modules/ad/core/domain/candidate.types';
|
||||||
import { MatchEntity } from '@modules/ad/core/domain/match.entity';
|
import { MatchEntity } from '@modules/ad/core/domain/match.entity';
|
||||||
import { MatchingEntity } from '@modules/ad/core/domain/matching.entity';
|
import { MatchingEntity } from '@modules/ad/core/domain/matching.entity';
|
||||||
|
import {
|
||||||
|
MATCH_CONFIG_ALGORITHM,
|
||||||
|
MATCH_CONFIG_AZIMUTH_MARGIN,
|
||||||
|
MATCH_CONFIG_MAX_DETOUR_DISTANCE_RATIO,
|
||||||
|
MATCH_CONFIG_MAX_DETOUR_DURATION_RATIO,
|
||||||
|
MATCH_CONFIG_PROPORTION,
|
||||||
|
MATCH_CONFIG_REMOTENESS,
|
||||||
|
MATCH_CONFIG_USE_AZIMUTH,
|
||||||
|
MATCH_CONFIG_USE_PROPORTION,
|
||||||
|
PAGINATION_CONFIG_PER_PAGE,
|
||||||
|
} from '@modules/ad/match.constants';
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
|
||||||
const originWaypoint: Waypoint = {
|
const originWaypoint: Waypoint = {
|
||||||
|
@ -231,27 +254,94 @@ const mockMatchingRepository: MatchingRepositoryPort = {
|
||||||
save: jest.fn(),
|
save: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockDefaultParamsProvider: DefaultParamsProviderPort = {
|
const mockConfigurationRepository: GetConfigurationRepositoryPort = {
|
||||||
getParams: () => {
|
get: jest.fn(),
|
||||||
return {
|
mget: jest.fn().mockImplementation(
|
||||||
DEPARTURE_TIME_MARGIN: 900,
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
DRIVER: false,
|
(domain: ConfigurationDomain, configs: ConfigurationDomainGet[]) => {
|
||||||
SEATS_PROPOSED: 3,
|
switch (domain) {
|
||||||
PASSENGER: true,
|
case ConfigurationDomain.CARPOOL:
|
||||||
SEATS_REQUESTED: 1,
|
return new Configurator(ConfigurationDomain.CARPOOL, [
|
||||||
STRICT: false,
|
{
|
||||||
TIMEZONE: 'Europe/Paris',
|
domain: ConfigurationDomain.CARPOOL,
|
||||||
ALGORITHM_TYPE: AlgorithmType.PASSENGER_ORIENTED,
|
key: CARPOOL_CONFIG_DEPARTURE_TIME_MARGIN,
|
||||||
REMOTENESS: 15000,
|
value: 900,
|
||||||
USE_PROPORTION: true,
|
},
|
||||||
PROPORTION: 0.3,
|
{
|
||||||
USE_AZIMUTH: true,
|
domain: ConfigurationDomain.CARPOOL,
|
||||||
AZIMUTH_MARGIN: 10,
|
key: CARPOOL_CONFIG_ROLE,
|
||||||
MAX_DETOUR_DISTANCE_RATIO: 0.3,
|
value: 'passenger',
|
||||||
MAX_DETOUR_DURATION_RATIO: 0.3,
|
},
|
||||||
PER_PAGE: 10,
|
{
|
||||||
};
|
domain: ConfigurationDomain.CARPOOL,
|
||||||
},
|
key: CARPOOL_CONFIG_SEATS_PROPOSED,
|
||||||
|
value: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: ConfigurationDomain.CARPOOL,
|
||||||
|
key: CARPOOL_CONFIG_SEATS_REQUESTED,
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: ConfigurationDomain.CARPOOL,
|
||||||
|
key: CARPOOL_CONFIG_STRICT_FREQUENCY,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
case ConfigurationDomain.MATCH:
|
||||||
|
return new Configurator(ConfigurationDomain.MATCH, [
|
||||||
|
{
|
||||||
|
domain: ConfigurationDomain.MATCH,
|
||||||
|
key: MATCH_CONFIG_ALGORITHM,
|
||||||
|
value: 'PASSENGER_ORIENTED',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: ConfigurationDomain.MATCH,
|
||||||
|
key: MATCH_CONFIG_REMOTENESS,
|
||||||
|
value: 15000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: ConfigurationDomain.MATCH,
|
||||||
|
key: MATCH_CONFIG_USE_PROPORTION,
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: ConfigurationDomain.MATCH,
|
||||||
|
key: MATCH_CONFIG_PROPORTION,
|
||||||
|
value: 0.3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: ConfigurationDomain.MATCH,
|
||||||
|
key: MATCH_CONFIG_USE_AZIMUTH,
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: ConfigurationDomain.MATCH,
|
||||||
|
key: MATCH_CONFIG_AZIMUTH_MARGIN,
|
||||||
|
value: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: ConfigurationDomain.MATCH,
|
||||||
|
key: MATCH_CONFIG_MAX_DETOUR_DISTANCE_RATIO,
|
||||||
|
value: 0.3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: ConfigurationDomain.MATCH,
|
||||||
|
key: MATCH_CONFIG_MAX_DETOUR_DURATION_RATIO,
|
||||||
|
value: 0.3,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
case ConfigurationDomain.PAGINATION:
|
||||||
|
return new Configurator(ConfigurationDomain.PAGINATION, [
|
||||||
|
{
|
||||||
|
domain: ConfigurationDomain.PAGINATION,
|
||||||
|
key: PAGINATION_CONFIG_PER_PAGE,
|
||||||
|
value: 10,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockInputDateTimeTransformer: DateTimeTransformerPort = {
|
const mockInputDateTimeTransformer: DateTimeTransformerPort = {
|
||||||
|
@ -289,8 +379,8 @@ describe('Match Query Handler', () => {
|
||||||
useValue: mockMatchingRepository,
|
useValue: mockMatchingRepository,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: PARAMS_PROVIDER,
|
provide: AD_CONFIGURATION_REPOSITORY,
|
||||||
useValue: mockDefaultParamsProvider,
|
useValue: mockConfigurationRepository,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: INPUT_DATETIME_TRANSFORMER,
|
provide: INPUT_DATETIME_TRANSFORMER,
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { DateTimeTransformerPort } from '@modules/ad/core/application/ports/datetime-transformer.port';
|
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 { RouteProviderPort } from '@modules/ad/core/application/ports/route-provider.port';
|
||||||
import { MatchQuery } from '@modules/ad/core/application/queries/match/match.query';
|
import { MatchQuery } from '@modules/ad/core/application/queries/match/match.query';
|
||||||
import { AlgorithmType } from '@modules/ad/core/application/types/algorithm.types';
|
import { AlgorithmType } from '@modules/ad/core/application/types/algorithm.types';
|
||||||
|
@ -33,14 +32,13 @@ const intermediateWaypoint: Waypoint = {
|
||||||
country: 'France',
|
country: 'France',
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultParams: DefaultParams = {
|
const defaultConfig = {
|
||||||
DEPARTURE_TIME_MARGIN: 900,
|
DEPARTURE_TIME_MARGIN: 900,
|
||||||
DRIVER: false,
|
DRIVER: false,
|
||||||
SEATS_PROPOSED: 3,
|
SEATS_PROPOSED: 3,
|
||||||
PASSENGER: true,
|
PASSENGER: true,
|
||||||
SEATS_REQUESTED: 1,
|
SEATS_REQUESTED: 1,
|
||||||
STRICT: false,
|
STRICT: false,
|
||||||
TIMEZONE: 'Europe/Paris',
|
|
||||||
ALGORITHM_TYPE: AlgorithmType.PASSENGER_ORIENTED,
|
ALGORITHM_TYPE: AlgorithmType.PASSENGER_ORIENTED,
|
||||||
REMOTENESS: 15000,
|
REMOTENESS: 15000,
|
||||||
USE_PROPORTION: true,
|
USE_PROPORTION: true,
|
||||||
|
@ -125,23 +123,23 @@ describe('Match Query', () => {
|
||||||
mockRouteProvider,
|
mockRouteProvider,
|
||||||
);
|
);
|
||||||
matchQuery
|
matchQuery
|
||||||
.setMissingMarginDurations(defaultParams.DEPARTURE_TIME_MARGIN)
|
.setMissingMarginDurations(defaultConfig.DEPARTURE_TIME_MARGIN)
|
||||||
.setMissingStrict(defaultParams.STRICT)
|
.setMissingStrict(defaultConfig.STRICT)
|
||||||
.setDefaultDriverAndPassengerParameters({
|
.setDefaultDriverAndPassengerParameters({
|
||||||
driver: defaultParams.DRIVER,
|
driver: defaultConfig.DRIVER,
|
||||||
passenger: defaultParams.PASSENGER,
|
passenger: defaultConfig.PASSENGER,
|
||||||
seatsProposed: defaultParams.SEATS_PROPOSED,
|
seatsProposed: defaultConfig.SEATS_PROPOSED,
|
||||||
seatsRequested: defaultParams.SEATS_REQUESTED,
|
seatsRequested: defaultConfig.SEATS_REQUESTED,
|
||||||
})
|
})
|
||||||
.setDefaultAlgorithmParameters({
|
.setDefaultAlgorithmParameters({
|
||||||
algorithmType: defaultParams.ALGORITHM_TYPE,
|
algorithmType: defaultConfig.ALGORITHM_TYPE,
|
||||||
remoteness: defaultParams.REMOTENESS,
|
remoteness: defaultConfig.REMOTENESS,
|
||||||
useProportion: defaultParams.USE_PROPORTION,
|
useProportion: defaultConfig.USE_PROPORTION,
|
||||||
proportion: defaultParams.PROPORTION,
|
proportion: defaultConfig.PROPORTION,
|
||||||
useAzimuth: defaultParams.USE_AZIMUTH,
|
useAzimuth: defaultConfig.USE_AZIMUTH,
|
||||||
azimuthMargin: defaultParams.AZIMUTH_MARGIN,
|
azimuthMargin: defaultConfig.AZIMUTH_MARGIN,
|
||||||
maxDetourDistanceRatio: defaultParams.MAX_DETOUR_DISTANCE_RATIO,
|
maxDetourDistanceRatio: defaultConfig.MAX_DETOUR_DISTANCE_RATIO,
|
||||||
maxDetourDurationRatio: defaultParams.MAX_DETOUR_DURATION_RATIO,
|
maxDetourDurationRatio: defaultConfig.MAX_DETOUR_DURATION_RATIO,
|
||||||
})
|
})
|
||||||
.setDatesAndSchedule(mockInputDateTimeTransformer);
|
.setDatesAndSchedule(mockInputDateTimeTransformer);
|
||||||
expect(matchQuery.strict).toBeFalsy();
|
expect(matchQuery.strict).toBeFalsy();
|
||||||
|
@ -181,10 +179,10 @@ describe('Match Query', () => {
|
||||||
mockRouteProvider,
|
mockRouteProvider,
|
||||||
);
|
);
|
||||||
matchQuery.setDefaultDriverAndPassengerParameters({
|
matchQuery.setDefaultDriverAndPassengerParameters({
|
||||||
driver: defaultParams.DRIVER,
|
driver: defaultConfig.DRIVER,
|
||||||
passenger: defaultParams.PASSENGER,
|
passenger: defaultConfig.PASSENGER,
|
||||||
seatsProposed: defaultParams.SEATS_PROPOSED,
|
seatsProposed: defaultConfig.SEATS_PROPOSED,
|
||||||
seatsRequested: defaultParams.SEATS_REQUESTED,
|
seatsRequested: defaultConfig.SEATS_REQUESTED,
|
||||||
});
|
});
|
||||||
expect(matchQuery.seatsProposed).toBe(3);
|
expect(matchQuery.seatsProposed).toBe(3);
|
||||||
expect(matchQuery.seatsRequested).toBe(1);
|
expect(matchQuery.seatsRequested).toBe(1);
|
||||||
|
|
|
@ -54,6 +54,7 @@ const mockMatcherRepository: AdRepositoryPort = {
|
||||||
insertExtra: jest.fn(),
|
insertExtra: jest.fn(),
|
||||||
findOneById: jest.fn(),
|
findOneById: jest.fn(),
|
||||||
findOne: jest.fn(),
|
findOne: jest.fn(),
|
||||||
|
findAll: jest.fn(),
|
||||||
insert: jest.fn(),
|
insert: jest.fn(),
|
||||||
update: jest.fn(),
|
update: jest.fn(),
|
||||||
updateWhere: jest.fn(),
|
updateWhere: jest.fn(),
|
||||||
|
|
|
@ -99,6 +99,7 @@ const mockMatcherRepository: AdRepositoryPort = {
|
||||||
insertExtra: jest.fn(),
|
insertExtra: jest.fn(),
|
||||||
findOneById: jest.fn(),
|
findOneById: jest.fn(),
|
||||||
findOne: jest.fn(),
|
findOne: jest.fn(),
|
||||||
|
findAll: jest.fn(),
|
||||||
insert: jest.fn(),
|
insert: jest.fn(),
|
||||||
update: jest.fn(),
|
update: jest.fn(),
|
||||||
updateWhere: jest.fn(),
|
updateWhere: jest.fn(),
|
||||||
|
|
|
@ -1,121 +0,0 @@
|
||||||
import { DefaultParams } from '@modules/ad/core/application/ports/default-params.type';
|
|
||||||
import { DefaultParamsProvider } from '@modules/ad/infrastructure/default-params-provider';
|
|
||||||
import { ConfigService } from '@nestjs/config';
|
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
|
||||||
|
|
||||||
const mockConfigServiceWithDefaults = {
|
|
||||||
get: jest.fn().mockImplementation((value: string) => {
|
|
||||||
switch (value) {
|
|
||||||
case 'DEPARTURE_TIME_MARGIN':
|
|
||||||
return 600;
|
|
||||||
case 'ROLE':
|
|
||||||
return 'passenger';
|
|
||||||
case 'SEATS_PROPOSED':
|
|
||||||
return 2;
|
|
||||||
case 'SEATS_REQUESTED':
|
|
||||||
return 1;
|
|
||||||
case 'STRICT_FREQUENCY':
|
|
||||||
return 'false';
|
|
||||||
case 'TIMEZONE':
|
|
||||||
return 'Europe/Paris';
|
|
||||||
case 'ALGORITHM_TYPE':
|
|
||||||
return 'PASSENGER_ORIENTED';
|
|
||||||
case 'REMOTENESS':
|
|
||||||
return 10000;
|
|
||||||
case 'USE_PROPORTION':
|
|
||||||
return 'true';
|
|
||||||
case 'PROPORTION':
|
|
||||||
return 0.4;
|
|
||||||
case 'USE_AZIMUTH':
|
|
||||||
return 'true';
|
|
||||||
case 'AZIMUTH_MARGIN':
|
|
||||||
return 15;
|
|
||||||
case 'MAX_DETOUR_DISTANCE_RATIO':
|
|
||||||
return 0.5;
|
|
||||||
case 'MAX_DETOUR_DURATION_RATIO':
|
|
||||||
return 0.6;
|
|
||||||
case 'PER_PAGE':
|
|
||||||
return 15;
|
|
||||||
default:
|
|
||||||
return 'some_default_value';
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
const mockConfigServiceWithoutDefaults = {
|
|
||||||
get: jest.fn(),
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('DefaultParamsProvider', () => {
|
|
||||||
let defaultParamsProviderWithDefaults: DefaultParamsProvider;
|
|
||||||
let defaultParamsProviderWithoutDefaults: DefaultParamsProvider;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
const moduleWithDefaults: TestingModule = await Test.createTestingModule({
|
|
||||||
imports: [],
|
|
||||||
providers: [
|
|
||||||
DefaultParamsProvider,
|
|
||||||
{
|
|
||||||
provide: ConfigService,
|
|
||||||
useValue: mockConfigServiceWithDefaults,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}).compile();
|
|
||||||
|
|
||||||
defaultParamsProviderWithDefaults =
|
|
||||||
moduleWithDefaults.get<DefaultParamsProvider>(DefaultParamsProvider);
|
|
||||||
|
|
||||||
const moduleWithoutDefault: TestingModule = await Test.createTestingModule({
|
|
||||||
imports: [],
|
|
||||||
providers: [
|
|
||||||
DefaultParamsProvider,
|
|
||||||
{
|
|
||||||
provide: ConfigService,
|
|
||||||
useValue: mockConfigServiceWithoutDefaults,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}).compile();
|
|
||||||
|
|
||||||
defaultParamsProviderWithoutDefaults =
|
|
||||||
moduleWithoutDefault.get<DefaultParamsProvider>(DefaultParamsProvider);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be defined', () => {
|
|
||||||
expect(defaultParamsProviderWithDefaults).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should provide default params if defaults are set', async () => {
|
|
||||||
const params: DefaultParams = defaultParamsProviderWithDefaults.getParams();
|
|
||||||
expect(params.DEPARTURE_TIME_MARGIN).toBe(600);
|
|
||||||
expect(params.PASSENGER).toBeTruthy();
|
|
||||||
expect(params.DRIVER).toBeFalsy();
|
|
||||||
expect(params.TIMEZONE).toBe('Europe/Paris');
|
|
||||||
expect(params.ALGORITHM_TYPE).toBe('PASSENGER_ORIENTED');
|
|
||||||
expect(params.REMOTENESS).toBe(10000);
|
|
||||||
expect(params.USE_PROPORTION).toBeTruthy();
|
|
||||||
expect(params.PROPORTION).toBe(0.4);
|
|
||||||
expect(params.USE_AZIMUTH).toBeTruthy();
|
|
||||||
expect(params.AZIMUTH_MARGIN).toBe(15);
|
|
||||||
expect(params.MAX_DETOUR_DISTANCE_RATIO).toBe(0.5);
|
|
||||||
expect(params.MAX_DETOUR_DURATION_RATIO).toBe(0.6);
|
|
||||||
expect(params.PER_PAGE).toBe(15);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should provide default params if defaults are not set', async () => {
|
|
||||||
const params: DefaultParams =
|
|
||||||
defaultParamsProviderWithoutDefaults.getParams();
|
|
||||||
expect(params.DEPARTURE_TIME_MARGIN).toBe(900);
|
|
||||||
expect(params.PASSENGER).toBeTruthy();
|
|
||||||
expect(params.DRIVER).toBeFalsy();
|
|
||||||
expect(params.TIMEZONE).toBe('Europe/Paris');
|
|
||||||
expect(params.ALGORITHM_TYPE).toBe('PASSENGER_ORIENTED');
|
|
||||||
expect(params.REMOTENESS).toBe(15000);
|
|
||||||
expect(params.USE_PROPORTION).toBeTruthy();
|
|
||||||
expect(params.PROPORTION).toBe(0.3);
|
|
||||||
expect(params.USE_AZIMUTH).toBeTruthy();
|
|
||||||
expect(params.AZIMUTH_MARGIN).toBe(10);
|
|
||||||
expect(params.MAX_DETOUR_DISTANCE_RATIO).toBe(0.3);
|
|
||||||
expect(params.MAX_DETOUR_DURATION_RATIO).toBe(0.3);
|
|
||||||
expect(params.PER_PAGE).toBe(10);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,39 +1,10 @@
|
||||||
import {
|
import { TIMEZONE_FINDER, TIME_CONVERTER } from '@modules/ad/ad.di-tokens';
|
||||||
PARAMS_PROVIDER,
|
|
||||||
TIMEZONE_FINDER,
|
|
||||||
TIME_CONVERTER,
|
|
||||||
} from '@modules/ad/ad.di-tokens';
|
|
||||||
import { Frequency } from '@modules/ad/core/application/ports/datetime-transformer.port';
|
import { Frequency } from '@modules/ad/core/application/ports/datetime-transformer.port';
|
||||||
import { DefaultParamsProviderPort } from '@modules/ad/core/application/ports/default-params-provider.port';
|
|
||||||
import { TimeConverterPort } from '@modules/ad/core/application/ports/time-converter.port';
|
import { TimeConverterPort } from '@modules/ad/core/application/ports/time-converter.port';
|
||||||
import { TimezoneFinderPort } from '@modules/ad/core/application/ports/timezone-finder.port';
|
import { TimezoneFinderPort } from '@modules/ad/core/application/ports/timezone-finder.port';
|
||||||
import { AlgorithmType } from '@modules/ad/core/application/types/algorithm.types';
|
|
||||||
import { InputDateTimeTransformer } from '@modules/ad/infrastructure/input-datetime-transformer';
|
import { InputDateTimeTransformer } from '@modules/ad/infrastructure/input-datetime-transformer';
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
|
||||||
const mockDefaultParamsProvider: DefaultParamsProviderPort = {
|
|
||||||
getParams: () => {
|
|
||||||
return {
|
|
||||||
DEPARTURE_TIME_MARGIN: 900,
|
|
||||||
DRIVER: false,
|
|
||||||
SEATS_PROPOSED: 3,
|
|
||||||
PASSENGER: true,
|
|
||||||
SEATS_REQUESTED: 1,
|
|
||||||
STRICT: false,
|
|
||||||
TIMEZONE: 'Europe/Paris',
|
|
||||||
ALGORITHM_TYPE: AlgorithmType.PASSENGER_ORIENTED,
|
|
||||||
REMOTENESS: 15000,
|
|
||||||
USE_PROPORTION: true,
|
|
||||||
PROPORTION: 0.3,
|
|
||||||
USE_AZIMUTH: true,
|
|
||||||
AZIMUTH_MARGIN: 10,
|
|
||||||
MAX_DETOUR_DISTANCE_RATIO: 0.3,
|
|
||||||
MAX_DETOUR_DURATION_RATIO: 0.3,
|
|
||||||
PER_PAGE: 10,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const mockTimezoneFinder: TimezoneFinderPort = {
|
const mockTimezoneFinder: TimezoneFinderPort = {
|
||||||
timezones: jest.fn().mockImplementation(() => ['Europe/Paris']),
|
timezones: jest.fn().mockImplementation(() => ['Europe/Paris']),
|
||||||
};
|
};
|
||||||
|
@ -66,10 +37,6 @@ describe('Input Datetime Transformer', () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
providers: [
|
providers: [
|
||||||
{
|
|
||||||
provide: PARAMS_PROVIDER,
|
|
||||||
useValue: mockDefaultParamsProvider,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
provide: TIMEZONE_FINDER,
|
provide: TIMEZONE_FINDER,
|
||||||
useValue: mockTimezoneFinder,
|
useValue: mockTimezoneFinder,
|
||||||
|
|
|
@ -1,39 +1,10 @@
|
||||||
import {
|
import { TIMEZONE_FINDER, TIME_CONVERTER } from '@modules/ad/ad.di-tokens';
|
||||||
PARAMS_PROVIDER,
|
|
||||||
TIMEZONE_FINDER,
|
|
||||||
TIME_CONVERTER,
|
|
||||||
} from '@modules/ad/ad.di-tokens';
|
|
||||||
import { Frequency } from '@modules/ad/core/application/ports/datetime-transformer.port';
|
import { Frequency } from '@modules/ad/core/application/ports/datetime-transformer.port';
|
||||||
import { DefaultParamsProviderPort } from '@modules/ad/core/application/ports/default-params-provider.port';
|
|
||||||
import { TimeConverterPort } from '@modules/ad/core/application/ports/time-converter.port';
|
import { TimeConverterPort } from '@modules/ad/core/application/ports/time-converter.port';
|
||||||
import { TimezoneFinderPort } from '@modules/ad/core/application/ports/timezone-finder.port';
|
import { TimezoneFinderPort } from '@modules/ad/core/application/ports/timezone-finder.port';
|
||||||
import { AlgorithmType } from '@modules/ad/core/application/types/algorithm.types';
|
|
||||||
import { OutputDateTimeTransformer } from '@modules/ad/infrastructure/output-datetime-transformer';
|
import { OutputDateTimeTransformer } from '@modules/ad/infrastructure/output-datetime-transformer';
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
|
||||||
const mockDefaultParamsProvider: DefaultParamsProviderPort = {
|
|
||||||
getParams: () => {
|
|
||||||
return {
|
|
||||||
DEPARTURE_TIME_MARGIN: 900,
|
|
||||||
DRIVER: false,
|
|
||||||
SEATS_PROPOSED: 3,
|
|
||||||
PASSENGER: true,
|
|
||||||
SEATS_REQUESTED: 1,
|
|
||||||
STRICT: false,
|
|
||||||
TIMEZONE: 'Europe/Paris',
|
|
||||||
ALGORITHM_TYPE: AlgorithmType.PASSENGER_ORIENTED,
|
|
||||||
REMOTENESS: 15000,
|
|
||||||
USE_PROPORTION: true,
|
|
||||||
PROPORTION: 0.3,
|
|
||||||
USE_AZIMUTH: true,
|
|
||||||
AZIMUTH_MARGIN: 10,
|
|
||||||
MAX_DETOUR_DISTANCE_RATIO: 0.3,
|
|
||||||
MAX_DETOUR_DURATION_RATIO: 0.3,
|
|
||||||
PER_PAGE: 10,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const mockTimezoneFinder: TimezoneFinderPort = {
|
const mockTimezoneFinder: TimezoneFinderPort = {
|
||||||
timezones: jest.fn().mockImplementation(() => ['Europe/Paris']),
|
timezones: jest.fn().mockImplementation(() => ['Europe/Paris']),
|
||||||
};
|
};
|
||||||
|
@ -66,10 +37,6 @@ describe('Output Datetime Transformer', () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
providers: [
|
providers: [
|
||||||
{
|
|
||||||
provide: PARAMS_PROVIDER,
|
|
||||||
useValue: mockDefaultParamsProvider,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
provide: TIMEZONE_FINDER,
|
provide: TIMEZONE_FINDER,
|
||||||
useValue: mockTimezoneFinder,
|
useValue: mockTimezoneFinder,
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
import { DefaultParams } from '../types/default-params.type';
|
|
||||||
|
|
||||||
export interface DefaultParamsProviderPort {
|
|
||||||
getParams(): DefaultParams;
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
export type DefaultParams = {
|
|
||||||
GEOROUTER_TYPE?: string;
|
|
||||||
GEOROUTER_URL?: string;
|
|
||||||
};
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
import {
|
||||||
|
ConfigurationDomainGet,
|
||||||
|
ConfigurationType,
|
||||||
|
} from '@mobicoop/configuration-module';
|
||||||
|
|
||||||
|
export const GEOGRAPHY_CONFIG_GEOROUTER_TYPE = 'georouterType';
|
||||||
|
export const GEOGRAPHY_CONFIG_GEOROUTER_URL = 'georouterUrl';
|
||||||
|
|
||||||
|
export const GeographyConfig: ConfigurationDomainGet[] = [
|
||||||
|
{
|
||||||
|
key: GEOGRAPHY_CONFIG_GEOROUTER_TYPE,
|
||||||
|
type: ConfigurationType.STRING,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: GEOGRAPHY_CONFIG_GEOROUTER_URL,
|
||||||
|
type: ConfigurationType.STRING,
|
||||||
|
},
|
||||||
|
];
|
|
@ -1,4 +1,6 @@
|
||||||
export const PARAMS_PROVIDER = Symbol('PARAMS_PROVIDER');
|
|
||||||
export const DIRECTION_ENCODER = Symbol('DIRECTION_ENCODER');
|
export const DIRECTION_ENCODER = Symbol('DIRECTION_ENCODER');
|
||||||
export const GEOROUTER = Symbol('GEOROUTER');
|
export const GEOROUTER = Symbol('GEOROUTER');
|
||||||
export const GEODESIC = Symbol('GEODESIC');
|
export const GEODESIC = Symbol('GEODESIC');
|
||||||
|
export const GEOGRAPHY_CONFIGURATION_REPOSITORY = Symbol(
|
||||||
|
'GEOGRAPHY_CONFIGURATION_REPOSITORY',
|
||||||
|
);
|
||||||
|
|
|
@ -3,10 +3,9 @@ import { CqrsModule } from '@nestjs/cqrs';
|
||||||
import {
|
import {
|
||||||
DIRECTION_ENCODER,
|
DIRECTION_ENCODER,
|
||||||
GEODESIC,
|
GEODESIC,
|
||||||
|
GEOGRAPHY_CONFIGURATION_REPOSITORY,
|
||||||
GEOROUTER,
|
GEOROUTER,
|
||||||
PARAMS_PROVIDER,
|
|
||||||
} from './geography.di-tokens';
|
} from './geography.di-tokens';
|
||||||
import { DefaultParamsProvider } from './infrastructure/default-params-provider';
|
|
||||||
import { PostgresDirectionEncoder } from './infrastructure/postgres-direction-encoder';
|
import { PostgresDirectionEncoder } from './infrastructure/postgres-direction-encoder';
|
||||||
import { GetBasicRouteController } from './interface/controllers/get-basic-route.controller';
|
import { GetBasicRouteController } from './interface/controllers/get-basic-route.controller';
|
||||||
import { RouteMapper } from './route.mapper';
|
import { RouteMapper } from './route.mapper';
|
||||||
|
@ -15,6 +14,7 @@ import { GraphhopperGeorouter } from './infrastructure/graphhopper-georouter';
|
||||||
import { HttpModule } from '@nestjs/axios';
|
import { HttpModule } from '@nestjs/axios';
|
||||||
import { GetRouteQueryHandler } from './core/application/queries/get-route/get-route.query-handler';
|
import { GetRouteQueryHandler } from './core/application/queries/get-route/get-route.query-handler';
|
||||||
import { GetDetailedRouteController } from './interface/controllers/get-detailed-route.controller';
|
import { GetDetailedRouteController } from './interface/controllers/get-detailed-route.controller';
|
||||||
|
import { ConfigurationRepository } from '@mobicoop/configuration-module';
|
||||||
|
|
||||||
const queryHandlers: Provider[] = [GetRouteQueryHandler];
|
const queryHandlers: Provider[] = [GetRouteQueryHandler];
|
||||||
|
|
||||||
|
@ -22,8 +22,8 @@ const mappers: Provider[] = [RouteMapper];
|
||||||
|
|
||||||
const adapters: Provider[] = [
|
const adapters: Provider[] = [
|
||||||
{
|
{
|
||||||
provide: PARAMS_PROVIDER,
|
provide: GEOGRAPHY_CONFIGURATION_REPOSITORY,
|
||||||
useClass: DefaultParamsProvider,
|
useClass: ConfigurationRepository,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: DIRECTION_ENCODER,
|
provide: DIRECTION_ENCODER,
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import { ConfigService } from '@nestjs/config';
|
|
||||||
import { DefaultParamsProviderPort } from '../core/application/ports/default-params-provider.port';
|
|
||||||
import { DefaultParams } from '../core/application/types/default-params.type';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class DefaultParamsProvider implements DefaultParamsProviderPort {
|
|
||||||
constructor(private readonly configService: ConfigService) {}
|
|
||||||
getParams = (): DefaultParams => ({
|
|
||||||
GEOROUTER_TYPE: this.configService.get('GEOROUTER_TYPE'),
|
|
||||||
GEOROUTER_URL: this.configService.get('GEOROUTER_URL'),
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -3,8 +3,10 @@ import { HttpService } from '@nestjs/axios';
|
||||||
import { GeorouterPort } from '../core/application/ports/georouter.port';
|
import { GeorouterPort } from '../core/application/ports/georouter.port';
|
||||||
import { GeorouterSettings } from '../core/application/types/georouter-settings.type';
|
import { GeorouterSettings } from '../core/application/types/georouter-settings.type';
|
||||||
import { Route, Step, Point } from '../core/domain/route.types';
|
import { Route, Step, Point } from '../core/domain/route.types';
|
||||||
import { DefaultParamsProviderPort } from '../core/application/ports/default-params-provider.port';
|
import {
|
||||||
import { GEODESIC, PARAMS_PROVIDER } from '../geography.di-tokens';
|
GEODESIC,
|
||||||
|
GEOGRAPHY_CONFIGURATION_REPOSITORY,
|
||||||
|
} from '../geography.di-tokens';
|
||||||
import { catchError, lastValueFrom, map } from 'rxjs';
|
import { catchError, lastValueFrom, map } from 'rxjs';
|
||||||
import { AxiosError, AxiosResponse } from 'axios';
|
import { AxiosError, AxiosResponse } from 'axios';
|
||||||
import {
|
import {
|
||||||
|
@ -12,6 +14,15 @@ import {
|
||||||
RouteNotFoundException,
|
RouteNotFoundException,
|
||||||
} from '../core/domain/route.errors';
|
} from '../core/domain/route.errors';
|
||||||
import { GeodesicPort } from '../core/application/ports/geodesic.port';
|
import { GeodesicPort } from '../core/application/ports/geodesic.port';
|
||||||
|
import {
|
||||||
|
ConfigurationDomain,
|
||||||
|
Configurator,
|
||||||
|
GetConfigurationRepositoryPort,
|
||||||
|
} from '@mobicoop/configuration-module';
|
||||||
|
import {
|
||||||
|
GEOGRAPHY_CONFIG_GEOROUTER_URL,
|
||||||
|
GeographyConfig,
|
||||||
|
} from '../geography.constants';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class GraphhopperGeorouter implements GeorouterPort {
|
export class GraphhopperGeorouter implements GeorouterPort {
|
||||||
|
@ -20,20 +31,24 @@ export class GraphhopperGeorouter implements GeorouterPort {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly httpService: HttpService,
|
private readonly httpService: HttpService,
|
||||||
@Inject(PARAMS_PROVIDER)
|
@Inject(GEOGRAPHY_CONFIGURATION_REPOSITORY)
|
||||||
private readonly defaultParamsProvider: DefaultParamsProviderPort,
|
private readonly configurationRepository: GetConfigurationRepositoryPort,
|
||||||
@Inject(GEODESIC) private readonly geodesic: GeodesicPort,
|
@Inject(GEODESIC) private readonly geodesic: GeodesicPort,
|
||||||
) {
|
) {}
|
||||||
this.url = [
|
|
||||||
defaultParamsProvider.getParams().GEOROUTER_URL,
|
|
||||||
'/route?',
|
|
||||||
].join('');
|
|
||||||
}
|
|
||||||
|
|
||||||
route = async (
|
route = async (
|
||||||
waypoints: Point[],
|
waypoints: Point[],
|
||||||
settings: GeorouterSettings,
|
settings: GeorouterSettings,
|
||||||
): Promise<Route> => {
|
): Promise<Route> => {
|
||||||
|
const geographyConfigurator: Configurator =
|
||||||
|
await this.configurationRepository.mget(
|
||||||
|
ConfigurationDomain.GEOGRAPHY,
|
||||||
|
GeographyConfig,
|
||||||
|
);
|
||||||
|
this.url = [
|
||||||
|
geographyConfigurator.get<string>(GEOGRAPHY_CONFIG_GEOROUTER_URL),
|
||||||
|
'/route?',
|
||||||
|
].join('');
|
||||||
this._setDefaultUrlArgs();
|
this._setDefaultUrlArgs();
|
||||||
this._setSettings(settings);
|
this._setSettings(settings);
|
||||||
return this._getRoute(waypoints);
|
return this._getRoute(waypoints);
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
import { DefaultParams } from '@modules/geography/core/application/types/default-params.type';
|
|
||||||
import { DefaultParamsProvider } from '@modules/geography/infrastructure/default-params-provider';
|
|
||||||
import { ConfigService } from '@nestjs/config';
|
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
|
||||||
|
|
||||||
const mockConfigService = {
|
|
||||||
get: jest.fn().mockImplementation((value: string) => {
|
|
||||||
switch (value) {
|
|
||||||
case 'GEOROUTER_TYPE':
|
|
||||||
return 'graphhopper';
|
|
||||||
case 'GEOROUTER_URL':
|
|
||||||
return 'http://localhost:8989';
|
|
||||||
default:
|
|
||||||
return 'some_default_value';
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('DefaultParamsProvider', () => {
|
|
||||||
let defaultParamsProvider: DefaultParamsProvider;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
|
||||||
imports: [],
|
|
||||||
providers: [
|
|
||||||
DefaultParamsProvider,
|
|
||||||
{
|
|
||||||
provide: ConfigService,
|
|
||||||
useValue: mockConfigService,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}).compile();
|
|
||||||
|
|
||||||
defaultParamsProvider = module.get<DefaultParamsProvider>(
|
|
||||||
DefaultParamsProvider,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be defined', () => {
|
|
||||||
expect(defaultParamsProvider).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should provide default params', async () => {
|
|
||||||
const params: DefaultParams = defaultParamsProvider.getParams();
|
|
||||||
expect(params.GEOROUTER_TYPE).toBe('graphhopper');
|
|
||||||
expect(params.GEOROUTER_URL).toBe('http://localhost:8989');
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,13 +1,19 @@
|
||||||
import { DefaultParamsProviderPort } from '@modules/geography/core/application/ports/default-params-provider.port';
|
import {
|
||||||
|
ConfigurationDomain,
|
||||||
|
ConfigurationDomainGet,
|
||||||
|
Configurator,
|
||||||
|
GetConfigurationRepositoryPort,
|
||||||
|
} from '@mobicoop/configuration-module';
|
||||||
import { GeodesicPort } from '@modules/geography/core/application/ports/geodesic.port';
|
import { GeodesicPort } from '@modules/geography/core/application/ports/geodesic.port';
|
||||||
import {
|
import {
|
||||||
GeorouterUnavailableException,
|
GeorouterUnavailableException,
|
||||||
RouteNotFoundException,
|
RouteNotFoundException,
|
||||||
} from '@modules/geography/core/domain/route.errors';
|
} from '@modules/geography/core/domain/route.errors';
|
||||||
import { Route, Step } from '@modules/geography/core/domain/route.types';
|
import { Route, Step } from '@modules/geography/core/domain/route.types';
|
||||||
|
import { GEOGRAPHY_CONFIG_GEOROUTER_URL } from '@modules/geography/geography.constants';
|
||||||
import {
|
import {
|
||||||
GEODESIC,
|
GEODESIC,
|
||||||
PARAMS_PROVIDER,
|
GEOGRAPHY_CONFIGURATION_REPOSITORY,
|
||||||
} from '@modules/geography/geography.di-tokens';
|
} from '@modules/geography/geography.di-tokens';
|
||||||
import { GraphhopperGeorouter } from '@modules/geography/infrastructure/graphhopper-georouter';
|
import { GraphhopperGeorouter } from '@modules/geography/infrastructure/graphhopper-georouter';
|
||||||
import { HttpService } from '@nestjs/axios';
|
import { HttpService } from '@nestjs/axios';
|
||||||
|
@ -262,10 +268,23 @@ const mockGeodesic: GeodesicPort = {
|
||||||
distance: jest.fn().mockImplementation(() => 50000),
|
distance: jest.fn().mockImplementation(() => 50000),
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockDefaultParamsProvider: DefaultParamsProviderPort = {
|
const mockConfigurationRepository: GetConfigurationRepositoryPort = {
|
||||||
getParams: jest.fn().mockImplementation(() => ({
|
get: jest.fn(),
|
||||||
GEOROUTER_URL: 'http://localhost:8989',
|
mget: jest.fn().mockImplementation(
|
||||||
})),
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
(domain: ConfigurationDomain, configs: ConfigurationDomainGet[]) => {
|
||||||
|
switch (domain) {
|
||||||
|
case ConfigurationDomain.GEOGRAPHY:
|
||||||
|
return new Configurator(ConfigurationDomain.GEOGRAPHY, [
|
||||||
|
{
|
||||||
|
domain: ConfigurationDomain.GEOGRAPHY,
|
||||||
|
key: GEOGRAPHY_CONFIG_GEOROUTER_URL,
|
||||||
|
value: 'http://localhost:8989',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('Graphhopper Georouter', () => {
|
describe('Graphhopper Georouter', () => {
|
||||||
|
@ -281,8 +300,8 @@ describe('Graphhopper Georouter', () => {
|
||||||
useValue: mockHttpService,
|
useValue: mockHttpService,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: PARAMS_PROVIDER,
|
provide: GEOGRAPHY_CONFIGURATION_REPOSITORY,
|
||||||
useValue: mockDefaultParamsProvider,
|
useValue: mockConfigurationRepository,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: GEODESIC,
|
provide: GEODESIC,
|
||||||
|
|
Loading…
Reference in New Issue