Merge branch 'removeConfigurationPackageV3' into 'main'

Update default configuration management

See merge request v3/service/matcher!14
This commit is contained in:
Sylvain Briat 2023-10-31 15:42:25 +00:00
commit 62e4015ea7
33 changed files with 1997 additions and 2815 deletions

View File

@ -21,43 +21,3 @@ REDIS_MATCHING_TTL=900
# CACHE
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

3775
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "@mobicoop/matcher",
"version": "1.2.0",
"version": "1.3.0",
"description": "Mobicoop V3 Matcher",
"author": "sbriat",
"private": true,
@ -30,14 +30,14 @@
"migrate:deploy": "npx prisma migrate deploy"
},
"dependencies": {
"@grpc/grpc-js": "^1.9.6",
"@grpc/grpc-js": "^1.9.9",
"@grpc/proto-loader": "^0.7.10",
"@songkeys/nestjs-redis": "^10.0.0",
"@mobicoop/configuration-module": "^3.0.0",
"@mobicoop/ddd-library": "^2.0.0",
"@mobicoop/configuration-module": "^7.2.1",
"@mobicoop/ddd-library": "^2.1.1",
"@mobicoop/health-module": "^2.3.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/common": "^10.2.7",
"@nestjs/config": "^3.1.1",
@ -47,8 +47,8 @@
"@nestjs/microservices": "^10.2.7",
"@nestjs/platform-express": "^10.2.7",
"@nestjs/terminus": "^10.1.1",
"@prisma/client": "^5.4.2",
"axios": "^1.5.1",
"@prisma/client": "^5.5.2",
"axios": "^1.6.0",
"cache-manager": "^5.2.4",
"cache-manager-ioredis-yet": "^1.2.2",
"class-transformer": "^0.5.1",
@ -63,23 +63,23 @@
"timezonecomplete": "^5.12.4"
},
"devDependencies": {
"@nestjs/cli": "^10.1.18",
"@nestjs/schematics": "^10.0.2",
"@nestjs/cli": "^10.2.1",
"@nestjs/schematics": "^10.0.3",
"@nestjs/testing": "^10.2.7",
"@types/express": "^4.17.20",
"@types/jest": "29.5.6",
"@types/node": "20.8.6",
"@types/supertest": "^2.0.14",
"@types/uuid": "^9.0.5",
"@typescript-eslint/eslint-plugin": "^6.8.0",
"@typescript-eslint/parser": "^6.8.0",
"@types/jest": "29.5.7",
"@types/node": "20.8.10",
"@types/supertest": "^2.0.15",
"@types/uuid": "^9.0.6",
"@typescript-eslint/eslint-plugin": "^6.9.1",
"@typescript-eslint/parser": "^6.9.1",
"dotenv-cli": "^7.3.0",
"eslint": "^8.51.0",
"eslint": "^8.52.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.1",
"jest": "29.7.0",
"prettier": "^3.0.3",
"prisma": "^5.4.2",
"prisma": "^5.5.2",
"source-map-support": "^0.5.21",
"supertest": "^6.3.3",
"ts-jest": "29.1.1",

View File

@ -1,10 +1,6 @@
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { ConfigModule } from '@nestjs/config';
import { AdModule } from './modules/ad/ad.module';
import {
ConfigurationModule,
ConfigurationModuleOptions,
} from '@mobicoop/configuration-module';
import { EventEmitterModule } from '@nestjs/event-emitter';
import { RequestContextModule } from 'nestjs-request-context';
import { MessagerModule } from '@modules/messager/messager.module';
@ -17,9 +13,6 @@ import { GeographyModule } from '@modules/geography/geography.module';
import {
HEALTH_AD_REPOSITORY,
HEALTH_CRITICAL_LOGGING_KEY,
SERVICE_CONFIGURATION_DELETE_QUEUE,
SERVICE_CONFIGURATION_PROPAGATE_QUEUE,
SERVICE_CONFIGURATION_SET_QUEUE,
SERVICE_NAME,
} from './app.constants';
@ -28,36 +21,6 @@ import {
ConfigModule.forRoot({ isGlobal: true }),
EventEmitterModule.forRoot(),
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({
imports: [AdModule, MessagerModule],
inject: [AD_REPOSITORY, MESSAGE_PUBLISHER],

View File

@ -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,
},
];

View File

@ -9,10 +9,12 @@ export const AD_GET_DETAILED_ROUTE_CONTROLLER = Symbol(
'AD_GET_DETAILED_ROUTE_CONTROLLER',
);
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 TIME_CONVERTER = Symbol('TIME_CONVERTER');
export const INPUT_DATETIME_TRANSFORMER = Symbol('INPUT_DATETIME_TRANSFORMER');
export const OUTPUT_DATETIME_TRANSFORMER = Symbol(
'OUTPUT_DATETIME_TRANSFORMER',
);
export const AD_CONFIGURATION_REPOSITORY = Symbol(
'AD_CONFIGURATION_REPOSITORY',
);

View File

@ -6,13 +6,13 @@ import {
AD_DIRECTION_ENCODER,
AD_ROUTE_PROVIDER,
AD_GET_BASIC_ROUTE_CONTROLLER,
PARAMS_PROVIDER,
TIMEZONE_FINDER,
TIME_CONVERTER,
INPUT_DATETIME_TRANSFORMER,
AD_GET_DETAILED_ROUTE_CONTROLLER,
OUTPUT_DATETIME_TRANSFORMER,
MATCHING_REPOSITORY,
AD_CONFIGURATION_REPOSITORY,
} from './ad.di-tokens';
import { MessageBrokerPublisher } from '@mobicoop/message-broker-module';
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 { MatchGrpcController } from './interface/grpc-controllers/match.grpc-controller';
import { MatchQueryHandler } from './core/application/queries/match/match.query-handler';
import { DefaultParamsProvider } from './infrastructure/default-params-provider';
import { TimezoneFinder } from './infrastructure/timezone-finder';
import { TimeConverter } from './infrastructure/time-converter';
import { InputDateTimeTransformer } from './infrastructure/input-datetime-transformer';
@ -38,7 +37,12 @@ import { MatchingMapper } from './matching.mapper';
import { CacheModule } from '@nestjs/cache-manager';
import { ConfigModule, ConfigService } from '@nestjs/config';
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 = [
CqrsModule,
@ -54,6 +58,21 @@ const imports = [
}),
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,
];
@ -76,6 +95,10 @@ const repositories: Provider[] = [
provide: MATCHING_REPOSITORY,
useClass: MatchingRepository,
},
{
provide: AD_CONFIGURATION_REPOSITORY,
useClass: ConfigurationRepository,
},
];
const messagePublishers: Provider[] = [
@ -104,10 +127,6 @@ const adapters: Provider[] = [
provide: AD_GET_DETAILED_ROUTE_CONTROLLER,
useClass: GetDetailedRouteController,
},
{
provide: PARAMS_PROVIDER,
useClass: DefaultParamsProvider,
},
{
provide: TIMEZONE_FINDER,
useClass: TimezoneFinder,

View File

@ -1,5 +0,0 @@
import { DefaultParams } from './default-params.type';
export interface DefaultParamsProviderPort {
getParams(): DefaultParams;
}

View File

@ -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;
};

View File

@ -1,3 +1,3 @@
export interface TimezoneFinderPort {
timezones(lon: number, lat: number, defaultTimezone?: string): string[];
timezones(lon: number, lat: number): string[];
}

View File

@ -6,58 +6,113 @@ import { AlgorithmType } from '../../types/algorithm.types';
import { Inject } from '@nestjs/common';
import { AdRepositoryPort } from '@modules/ad/core/application/ports/ad.repository.port';
import {
AD_CONFIGURATION_REPOSITORY,
AD_REPOSITORY,
INPUT_DATETIME_TRANSFORMER,
MATCHING_REPOSITORY,
PARAMS_PROVIDER,
} from '@modules/ad/ad.di-tokens';
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 { Paginator } from '@mobicoop/ddd-library';
import { MatchingEntity } from '@modules/ad/core/domain/matching.entity';
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)
export class MatchQueryHandler implements IQueryHandler {
private readonly _defaultParams: DefaultParams;
constructor(
@Inject(PARAMS_PROVIDER)
private readonly defaultParamsProvider: DefaultParamsProviderPort,
@Inject(AD_CONFIGURATION_REPOSITORY)
private readonly configurationRepository: GetConfigurationRepositoryPort,
@Inject(AD_REPOSITORY) private readonly adRepository: AdRepositoryPort,
@Inject(MATCHING_REPOSITORY)
private readonly matchingRepository: MatchingRepositoryPort,
@Inject(INPUT_DATETIME_TRANSFORMER)
private readonly datetimeTransformer: DateTimeTransformerPort,
) {
this._defaultParams = defaultParamsProvider.getParams();
}
) {}
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
.setMissingMarginDurations(this._defaultParams.DEPARTURE_TIME_MARGIN)
.setMissingStrict(this._defaultParams.STRICT)
.setMissingMarginDurations(
carpoolConfigurator.get<number>(CARPOOL_CONFIG_DEPARTURE_TIME_MARGIN),
)
.setMissingStrict(
carpoolConfigurator.get<boolean>(CARPOOL_CONFIG_STRICT_FREQUENCY),
)
.setDefaultDriverAndPassengerParameters({
driver: this._defaultParams.DRIVER,
passenger: this._defaultParams.PASSENGER,
seatsProposed: this._defaultParams.SEATS_PROPOSED,
seatsRequested: this._defaultParams.SEATS_REQUESTED,
driver:
carpoolConfigurator.get<string>(CARPOOL_CONFIG_ROLE) === 'driver',
passenger:
carpoolConfigurator.get<string>(CARPOOL_CONFIG_ROLE) === 'passenger',
seatsProposed: carpoolConfigurator.get<number>(
CARPOOL_CONFIG_SEATS_PROPOSED,
),
seatsRequested: carpoolConfigurator.get<number>(
CARPOOL_CONFIG_SEATS_REQUESTED,
),
})
.setDefaultAlgorithmParameters({
algorithmType: this._defaultParams.ALGORITHM_TYPE,
remoteness: this._defaultParams.REMOTENESS,
useProportion: this._defaultParams.USE_PROPORTION,
proportion: this._defaultParams.PROPORTION,
useAzimuth: this._defaultParams.USE_AZIMUTH,
azimuthMargin: this._defaultParams.AZIMUTH_MARGIN,
maxDetourDistanceRatio: this._defaultParams.MAX_DETOUR_DISTANCE_RATIO,
maxDetourDurationRatio: this._defaultParams.MAX_DETOUR_DURATION_RATIO,
algorithmType: matchConfigurator.get<AlgorithmType>(
MATCH_CONFIG_ALGORITHM,
),
remoteness: matchConfigurator.get<number>(MATCH_CONFIG_REMOTENESS),
useProportion: matchConfigurator.get<boolean>(
MATCH_CONFIG_USE_PROPORTION,
),
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({
page: 1,
perPage: this._defaultParams.PER_PAGE,
perPage: paginationConfigurator.get<number>(PAGINATION_CONFIG_PER_PAGE),
})
.setDatesAndSchedule(this.datetimeTransformer);
let matchingEntity: MatchingEntity | undefined = await this._cachedMatching(

View File

@ -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,
});
}

View File

@ -5,26 +5,16 @@ import {
GeoDateTime,
} from '../core/application/ports/datetime-transformer.port';
import { TimeConverterPort } from '../core/application/ports/time-converter.port';
import {
PARAMS_PROVIDER,
TIMEZONE_FINDER,
TIME_CONVERTER,
} from '../ad.di-tokens';
import { TIMEZONE_FINDER, TIME_CONVERTER } from '../ad.di-tokens';
import { TimezoneFinderPort } from '../core/application/ports/timezone-finder.port';
import { DefaultParamsProviderPort } from '../core/application/ports/default-params-provider.port';
@Injectable()
export class InputDateTimeTransformer implements DateTimeTransformerPort {
private readonly _defaultTimezone: string;
constructor(
@Inject(PARAMS_PROVIDER)
private readonly defaultParamsProvider: DefaultParamsProviderPort,
@Inject(TIMEZONE_FINDER)
private readonly timezoneFinder: TimezoneFinderPort,
@Inject(TIME_CONVERTER) private readonly timeConverter: TimeConverterPort,
) {
this._defaultTimezone = defaultParamsProvider.getParams().TIMEZONE;
}
) {}
/**
* Compute the fromDate : if an ad is punctual, the departure date
@ -39,7 +29,6 @@ export class InputDateTimeTransformer implements DateTimeTransformerPort {
this.timezoneFinder.timezones(
geoFromDate.coordinates.lon,
geoFromDate.coordinates.lat,
this._defaultTimezone,
)[0],
)
.toISOString()
@ -76,7 +65,6 @@ export class InputDateTimeTransformer implements DateTimeTransformerPort {
this.timezoneFinder.timezones(
geoFromDate.coordinates.lon,
geoFromDate.coordinates.lat,
this._defaultTimezone,
)[0],
);
return new Date(this.fromDate(geoFromDate, frequency)).getUTCDay();
@ -92,7 +80,6 @@ export class InputDateTimeTransformer implements DateTimeTransformerPort {
this.timezoneFinder.timezones(
geoFromDate.coordinates.lon,
geoFromDate.coordinates.lat,
this._defaultTimezone,
)[0],
);
return this.timeConverter
@ -102,7 +89,6 @@ export class InputDateTimeTransformer implements DateTimeTransformerPort {
this.timezoneFinder.timezones(
geoFromDate.coordinates.lon,
geoFromDate.coordinates.lat,
this._defaultTimezone,
)[0],
)
.toISOString()

View File

@ -4,13 +4,5 @@ import { TimezoneFinderPort } from '../core/application/ports/timezone-finder.po
@Injectable()
export class TimezoneFinder implements TimezoneFinderPort {
timezones = (
lon: number,
lat: number,
defaultTimezone?: string,
): string[] => {
const foundTimezones = find(lat, lon);
if (defaultTimezone && foundTimezones.length == 0) return [defaultTimezone];
return foundTimezones;
};
timezones = (lon: number, lat: number): string[] => find(lat, lon);
}

View File

@ -30,6 +30,8 @@ export class AdCreatedMessageHandler {
waypoints: createdAd.waypoints,
}),
);
} catch (e: any) {}
} catch (e: any) {
console.log(e);
}
}
}

View File

@ -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,
},
];

View File

@ -53,6 +53,7 @@ const mockAdRepository: AdRepositoryPort = {
insertExtra: jest.fn(),
findOneById: jest.fn(),
findOne: jest.fn(),
findAll: jest.fn(),
insert: jest.fn(),
update: jest.fn(),
updateWhere: jest.fn(),

View File

@ -1,11 +1,23 @@
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,
INPUT_DATETIME_TRANSFORMER,
MATCHING_REPOSITORY,
PARAMS_PROVIDER,
} from '@modules/ad/ad.di-tokens';
import { DateTimeTransformerPort } from '@modules/ad/core/application/ports/datetime-transformer.port';
import { DefaultParamsProviderPort } from '@modules/ad/core/application/ports/default-params-provider.port';
import { MatchingRepositoryPort } from '@modules/ad/core/application/ports/matching.repository.port';
import { RouteProviderPort } from '@modules/ad/core/application/ports/route-provider.port';
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 { MatchEntity } from '@modules/ad/core/domain/match.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';
const originWaypoint: Waypoint = {
@ -231,27 +254,94 @@ const mockMatchingRepository: MatchingRepositoryPort = {
save: jest.fn(),
};
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 mockConfigurationRepository: GetConfigurationRepositoryPort = {
get: jest.fn(),
mget: jest.fn().mockImplementation(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
(domain: ConfigurationDomain, configs: ConfigurationDomainGet[]) => {
switch (domain) {
case ConfigurationDomain.CARPOOL:
return new Configurator(ConfigurationDomain.CARPOOL, [
{
domain: ConfigurationDomain.CARPOOL,
key: CARPOOL_CONFIG_DEPARTURE_TIME_MARGIN,
value: 900,
},
{
domain: ConfigurationDomain.CARPOOL,
key: CARPOOL_CONFIG_ROLE,
value: 'passenger',
},
{
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 = {
@ -289,8 +379,8 @@ describe('Match Query Handler', () => {
useValue: mockMatchingRepository,
},
{
provide: PARAMS_PROVIDER,
useValue: mockDefaultParamsProvider,
provide: AD_CONFIGURATION_REPOSITORY,
useValue: mockConfigurationRepository,
},
{
provide: INPUT_DATETIME_TRANSFORMER,

View File

@ -1,5 +1,4 @@
import { DateTimeTransformerPort } from '@modules/ad/core/application/ports/datetime-transformer.port';
import { DefaultParams } from '@modules/ad/core/application/ports/default-params.type';
import { RouteProviderPort } from '@modules/ad/core/application/ports/route-provider.port';
import { MatchQuery } from '@modules/ad/core/application/queries/match/match.query';
import { AlgorithmType } from '@modules/ad/core/application/types/algorithm.types';
@ -33,14 +32,13 @@ const intermediateWaypoint: Waypoint = {
country: 'France',
};
const defaultParams: DefaultParams = {
const defaultConfig = {
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,
@ -125,23 +123,23 @@ describe('Match Query', () => {
mockRouteProvider,
);
matchQuery
.setMissingMarginDurations(defaultParams.DEPARTURE_TIME_MARGIN)
.setMissingStrict(defaultParams.STRICT)
.setMissingMarginDurations(defaultConfig.DEPARTURE_TIME_MARGIN)
.setMissingStrict(defaultConfig.STRICT)
.setDefaultDriverAndPassengerParameters({
driver: defaultParams.DRIVER,
passenger: defaultParams.PASSENGER,
seatsProposed: defaultParams.SEATS_PROPOSED,
seatsRequested: defaultParams.SEATS_REQUESTED,
driver: defaultConfig.DRIVER,
passenger: defaultConfig.PASSENGER,
seatsProposed: defaultConfig.SEATS_PROPOSED,
seatsRequested: defaultConfig.SEATS_REQUESTED,
})
.setDefaultAlgorithmParameters({
algorithmType: defaultParams.ALGORITHM_TYPE,
remoteness: defaultParams.REMOTENESS,
useProportion: defaultParams.USE_PROPORTION,
proportion: defaultParams.PROPORTION,
useAzimuth: defaultParams.USE_AZIMUTH,
azimuthMargin: defaultParams.AZIMUTH_MARGIN,
maxDetourDistanceRatio: defaultParams.MAX_DETOUR_DISTANCE_RATIO,
maxDetourDurationRatio: defaultParams.MAX_DETOUR_DURATION_RATIO,
algorithmType: defaultConfig.ALGORITHM_TYPE,
remoteness: defaultConfig.REMOTENESS,
useProportion: defaultConfig.USE_PROPORTION,
proportion: defaultConfig.PROPORTION,
useAzimuth: defaultConfig.USE_AZIMUTH,
azimuthMargin: defaultConfig.AZIMUTH_MARGIN,
maxDetourDistanceRatio: defaultConfig.MAX_DETOUR_DISTANCE_RATIO,
maxDetourDurationRatio: defaultConfig.MAX_DETOUR_DURATION_RATIO,
})
.setDatesAndSchedule(mockInputDateTimeTransformer);
expect(matchQuery.strict).toBeFalsy();
@ -181,10 +179,10 @@ describe('Match Query', () => {
mockRouteProvider,
);
matchQuery.setDefaultDriverAndPassengerParameters({
driver: defaultParams.DRIVER,
passenger: defaultParams.PASSENGER,
seatsProposed: defaultParams.SEATS_PROPOSED,
seatsRequested: defaultParams.SEATS_REQUESTED,
driver: defaultConfig.DRIVER,
passenger: defaultConfig.PASSENGER,
seatsProposed: defaultConfig.SEATS_PROPOSED,
seatsRequested: defaultConfig.SEATS_REQUESTED,
});
expect(matchQuery.seatsProposed).toBe(3);
expect(matchQuery.seatsRequested).toBe(1);

View File

@ -54,6 +54,7 @@ const mockMatcherRepository: AdRepositoryPort = {
insertExtra: jest.fn(),
findOneById: jest.fn(),
findOne: jest.fn(),
findAll: jest.fn(),
insert: jest.fn(),
update: jest.fn(),
updateWhere: jest.fn(),

View File

@ -99,6 +99,7 @@ const mockMatcherRepository: AdRepositoryPort = {
insertExtra: jest.fn(),
findOneById: jest.fn(),
findOne: jest.fn(),
findAll: jest.fn(),
insert: jest.fn(),
update: jest.fn(),
updateWhere: jest.fn(),

View File

@ -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);
});
});

View File

@ -1,39 +1,10 @@
import {
PARAMS_PROVIDER,
TIMEZONE_FINDER,
TIME_CONVERTER,
} from '@modules/ad/ad.di-tokens';
import { TIMEZONE_FINDER, TIME_CONVERTER } from '@modules/ad/ad.di-tokens';
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 { 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 { 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 = {
timezones: jest.fn().mockImplementation(() => ['Europe/Paris']),
};
@ -66,10 +37,6 @@ describe('Input Datetime Transformer', () => {
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
{
provide: PARAMS_PROVIDER,
useValue: mockDefaultParamsProvider,
},
{
provide: TIMEZONE_FINDER,
useValue: mockTimezoneFinder,

View File

@ -1,39 +1,10 @@
import {
PARAMS_PROVIDER,
TIMEZONE_FINDER,
TIME_CONVERTER,
} from '@modules/ad/ad.di-tokens';
import { TIMEZONE_FINDER, TIME_CONVERTER } from '@modules/ad/ad.di-tokens';
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 { 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 { 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 = {
timezones: jest.fn().mockImplementation(() => ['Europe/Paris']),
};
@ -66,10 +37,6 @@ describe('Output Datetime Transformer', () => {
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
{
provide: PARAMS_PROVIDER,
useValue: mockDefaultParamsProvider,
},
{
provide: TIMEZONE_FINDER,
useValue: mockTimezoneFinder,

View File

@ -1,5 +0,0 @@
import { DefaultParams } from '../types/default-params.type';
export interface DefaultParamsProviderPort {
getParams(): DefaultParams;
}

View File

@ -1,4 +0,0 @@
export type DefaultParams = {
GEOROUTER_TYPE?: string;
GEOROUTER_URL?: string;
};

View File

@ -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,
},
];

View File

@ -1,4 +1,6 @@
export const PARAMS_PROVIDER = Symbol('PARAMS_PROVIDER');
export const DIRECTION_ENCODER = Symbol('DIRECTION_ENCODER');
export const GEOROUTER = Symbol('GEOROUTER');
export const GEODESIC = Symbol('GEODESIC');
export const GEOGRAPHY_CONFIGURATION_REPOSITORY = Symbol(
'GEOGRAPHY_CONFIGURATION_REPOSITORY',
);

View File

@ -3,10 +3,9 @@ import { CqrsModule } from '@nestjs/cqrs';
import {
DIRECTION_ENCODER,
GEODESIC,
GEOGRAPHY_CONFIGURATION_REPOSITORY,
GEOROUTER,
PARAMS_PROVIDER,
} from './geography.di-tokens';
import { DefaultParamsProvider } from './infrastructure/default-params-provider';
import { PostgresDirectionEncoder } from './infrastructure/postgres-direction-encoder';
import { GetBasicRouteController } from './interface/controllers/get-basic-route.controller';
import { RouteMapper } from './route.mapper';
@ -15,6 +14,7 @@ import { GraphhopperGeorouter } from './infrastructure/graphhopper-georouter';
import { HttpModule } from '@nestjs/axios';
import { GetRouteQueryHandler } from './core/application/queries/get-route/get-route.query-handler';
import { GetDetailedRouteController } from './interface/controllers/get-detailed-route.controller';
import { ConfigurationRepository } from '@mobicoop/configuration-module';
const queryHandlers: Provider[] = [GetRouteQueryHandler];
@ -22,8 +22,8 @@ const mappers: Provider[] = [RouteMapper];
const adapters: Provider[] = [
{
provide: PARAMS_PROVIDER,
useClass: DefaultParamsProvider,
provide: GEOGRAPHY_CONFIGURATION_REPOSITORY,
useClass: ConfigurationRepository,
},
{
provide: DIRECTION_ENCODER,

View File

@ -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'),
});
}

View File

@ -3,8 +3,10 @@ import { HttpService } from '@nestjs/axios';
import { GeorouterPort } from '../core/application/ports/georouter.port';
import { GeorouterSettings } from '../core/application/types/georouter-settings.type';
import { Route, Step, Point } from '../core/domain/route.types';
import { DefaultParamsProviderPort } from '../core/application/ports/default-params-provider.port';
import { GEODESIC, PARAMS_PROVIDER } from '../geography.di-tokens';
import {
GEODESIC,
GEOGRAPHY_CONFIGURATION_REPOSITORY,
} from '../geography.di-tokens';
import { catchError, lastValueFrom, map } from 'rxjs';
import { AxiosError, AxiosResponse } from 'axios';
import {
@ -12,6 +14,15 @@ import {
RouteNotFoundException,
} from '../core/domain/route.errors';
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()
export class GraphhopperGeorouter implements GeorouterPort {
@ -20,20 +31,24 @@ export class GraphhopperGeorouter implements GeorouterPort {
constructor(
private readonly httpService: HttpService,
@Inject(PARAMS_PROVIDER)
private readonly defaultParamsProvider: DefaultParamsProviderPort,
@Inject(GEOGRAPHY_CONFIGURATION_REPOSITORY)
private readonly configurationRepository: GetConfigurationRepositoryPort,
@Inject(GEODESIC) private readonly geodesic: GeodesicPort,
) {
this.url = [
defaultParamsProvider.getParams().GEOROUTER_URL,
'/route?',
].join('');
}
) {}
route = async (
waypoints: Point[],
settings: GeorouterSettings,
): 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._setSettings(settings);
return this._getRoute(waypoints);

View File

@ -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');
});
});

View File

@ -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 {
GeorouterUnavailableException,
RouteNotFoundException,
} from '@modules/geography/core/domain/route.errors';
import { Route, Step } from '@modules/geography/core/domain/route.types';
import { GEOGRAPHY_CONFIG_GEOROUTER_URL } from '@modules/geography/geography.constants';
import {
GEODESIC,
PARAMS_PROVIDER,
GEOGRAPHY_CONFIGURATION_REPOSITORY,
} from '@modules/geography/geography.di-tokens';
import { GraphhopperGeorouter } from '@modules/geography/infrastructure/graphhopper-georouter';
import { HttpService } from '@nestjs/axios';
@ -262,10 +268,23 @@ const mockGeodesic: GeodesicPort = {
distance: jest.fn().mockImplementation(() => 50000),
};
const mockDefaultParamsProvider: DefaultParamsProviderPort = {
getParams: jest.fn().mockImplementation(() => ({
GEOROUTER_URL: 'http://localhost:8989',
})),
const mockConfigurationRepository: GetConfigurationRepositoryPort = {
get: jest.fn(),
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', () => {
@ -281,8 +300,8 @@ describe('Graphhopper Georouter', () => {
useValue: mockHttpService,
},
{
provide: PARAMS_PROVIDER,
useValue: mockDefaultParamsProvider,
provide: GEOGRAPHY_CONFIGURATION_REPOSITORY,
useValue: mockConfigurationRepository,
},
{
provide: GEODESIC,