Compare commits

..

No commits in common. "main" and "v2.4.0" have entirely different histories.
main ... v2.4.0

37 changed files with 1233 additions and 1544 deletions

View File

@ -4,7 +4,7 @@ SERVICE_PORT=5003
HEALTH_SERVICE_PORT=6003 HEALTH_SERVICE_PORT=6003
# MESSAGE BROKER # MESSAGE BROKER
MESSAGE_BROKER_URI=amqp://mobicoop:mobicoop@v3-broker:5672 MESSAGE_BROKER_URI=amqp://v3-broker:5672
MESSAGE_BROKER_EXCHANGE=mobicoop MESSAGE_BROKER_EXCHANGE=mobicoop
MESSAGE_BROKER_EXCHANGE_DURABILITY=true MESSAGE_BROKER_EXCHANGE_DURABILITY=true
@ -15,10 +15,6 @@ REDIS_PORT=6379
# DEFAULT CONFIGURATION # DEFAULT CONFIGURATION
# AUTH
# encryption algorithm : BCRYPT / ARGON2I / ARGON2D / ARGON2ID
ENCRYPTION_ALGORITHM=ARGON2ID
# CARPOOL # CARPOOL
# default carpool departure time margin (in seconds) # default carpool departure time margin (in seconds)
DEPARTURE_TIME_MARGIN=900 DEPARTURE_TIME_MARGIN=900
@ -54,35 +50,7 @@ MAX_DETOUR_DURATION_RATIO=0.3
# GEOROUTER # GEOROUTER
GEOROUTER_TYPE=graphhopper GEOROUTER_TYPE=graphhopper
GEOROUTER_URL=http://localhost:8989 GEOROUTER_URL=http://51.210.249.175:8989
# GEOCODER
# default language
GEOCODER_LANG=fr
# minimal score to consider a result as valid
GEOCODER_MIN_CONFIDENCE=0.5
# max number of results per provider (input results)
GEOCODER_MAX_RESULTS_PER_PROVIDER=5
# max number of results per address type (output results)
GEOCODER_MAX_RESULTS_PER_TYPE=5
# sanitize results ?
GEOCODER_SANITIZE=true
# consolidate results ?
GEOCODER_CONSOLIDATE=true
# max distance in metres between 2 points to consider a duplicate
GEOCODER_PROXIMITY=5000
# population vs distance prioritizer coef
# "boost" population weight for results with a short distance
# => multiply the population by COEF / distance (in km)
GEOCODER_POPULATION_PRIORITIZER_COEF=100
# a json array for main providers, see Geocoder service for detail about the providers
GEOCODER_PROVIDERS='[{"name":"provider1","type":"providerType","baseUrl":"http://localhost","countryRestriction":"countryRestrictionString"}]'
# a json array for fallback providers
GEOCODER_PROVIDERS_FALLBACK='[{"name":"provider1","type":"providerType","apiKey":"anApiKey"}]'
# a json array for prioritizers, see Geocoder service for detail about the prioritizers
GEOCODER_PRIORITIZERS='[{"country": "countryName","addressTypes":[{"type":"addressType","prioritizers":[{"position":0,"name":"prioritizerName1"}]}]}]'
# a json array for consolidators, see Geocoder service for detail about the consolidators
GEOCODER_CONSOLIDATORS='[{"country":"countryName","addressTypes":[{"type":"addressType","name":"consolidatorName"}]}]'
# PAGINATION # PAGINATION
# number of results per page # number of results per page

View File

@ -4,10 +4,6 @@ stages:
- test - test
- build - build
include:
- template: Security/SAST.gitlab-ci.yml
- template: Security/Secret-Detection.gitlab-ci.yml
############## ##############
# TEST STAGE # # TEST STAGE #
############## ##############

View File

@ -1,55 +0,0 @@
_Replace italic text by your own description_
## Feature Merge Request
### Why this Merge Request
_This merge request addresses, and describe the problem or user story being addressed._
### What is implemented, what is the chosen solution
_Explain the fix or solution implemented. Which other solution have been envisaged._
### Related issues and impact on other project in codebase
_Provide links to the related issues, feature requests and merge request (from Gitlab and Redmine)._
_And Link to other project Impacted._
### Other Information
_Include any extra information or considerations for reviewers._
## Checklists
### Merge Request
- [ ] Target branch identified.
- [ ] Code based on last version of target branch.
- [ ] Description filled.
- [ ] Impact on other project codebase identified.
- [ ] Documentation reflects the changes made.
- [ ] Test run in gitlab pipeline and locally.
- [ ] One or more reviewer is defined
### Code Review
- [ ] Code follows project coding guidelines.
- [ ] Code follows project designed architecture.
- [ ] Code is easily readable.
- [ ] Everything new have an explicit and pertinent name (variable, method, file ...)
- [ ] No redundant/duplicate code (unless explain by architecture choice)
- [ ] Commit are all related to MR and well written (Atomic commit).
- [ ] New code is tested and covered by automated test.
- [ ] No useless logging or debugging code.
- [ ] No code can be replaced by library or framework code.
### TODO before merge
- [ ] _add any task here_
- [ ] ...
### TODO after merge
- [ ] _add any task here_
- [ ] ...

View File

@ -1,62 +0,0 @@
_Replace italic text by your own description_
## Release Merge Request
### Why this Merge Request
_This merge request addresses, and describe the problem or user story being addressed._
### What is implemented, what is the chosen solution
_Explain the fix or solution implemented. Which other solution have been envisaged._
### Related issues and impact on other project in codebase
_Provide links to the related issues, feature requests and merge request (from Gitlab and Redmine)._
_And Link to other project Impacted._
### Other Information
_Include any extra information or considerations for reviewers._
## Checklists
### Merge Request
- [ ] Target branch identified.
- [ ] Code based on last version of target branch.
- [ ] Description filled.
- [ ] Impact on other project codebase identified.
- [ ] Documentation reflects the changes made.
- [ ] Test run in gitlab pipeline and locally.
- [ ] One or more reviewer is defined
### Code Review
- [ ] Code follows project coding guidelines.
- [ ] Code follows project designed architecture.
- [ ] Code is easily readable.
- [ ] Everything new have an explicit and pertinent name (variable, method, file ...)
- [ ] No redundant/duplicate code (unless explain by architecture choice)
- [ ] Commit are all related to MR and well written (Atomic commit).
- [ ] New code is tested and covered by automated test.
- [ ] No useless logging or debugging code.
- [ ] No code can be replaced by library or framework code.
### Change Management
- [ ] Release is planned
- [ ] Merge Request to be included are identified
- [ ] Concerned Team are aware of the change
- [ ] No other change on the same day (if possible)
### TODO before merge
- [ ] _add any task here_
- [ ] ...
### TODO after merge
- [ ] _add any task here_
- [ ] ...

View File

@ -1,37 +0,0 @@
_Replace italic text by your own description_
## Small Fix Merge Request
### Why this Merge Request
_This merge request addresses, and describe the problem or user story being addressed._
### What is implemented, what is the chosen solution
_Explain the fix or solution implemented. Which other solution have been envisaged._
### Related issues and impact on other project in codebase
_Provide links to the related issues, feature requests and merge request (from Gitlab and Redmine)._
_And Link to other project Impacted._
### Other Information
_Include any extra information or considerations for reviewers._
## Checklists
### Merge Request
- [ ] Target branch identified.
- [ ] Code based on last version of target branch.
- [ ] Description filled.
- [ ] Impact on other project codebase identified.
- [ ] Test run in gitlab pipeline and locally.
### Code Review
- [ ] Code is easily readable.
- [ ] Commit are all related to MR and well written (Atomic commit).
- [ ] No useless logging or debugging code.

View File

@ -4,4 +4,3 @@ node_modules
dist dist
coverage coverage
.prettierrc.json .prettierrc.json
.gitlab

View File

@ -2,7 +2,7 @@
# BUILD FOR LOCAL DEVELOPMENT # BUILD FOR LOCAL DEVELOPMENT
################### ###################
FROM docker.io/node:lts-hydrogen As development FROM node:18-alpine3.16 As development
# Create app directory # Create app directory
WORKDIR /usr/src/app WORKDIR /usr/src/app
@ -25,7 +25,7 @@ USER node
# BUILD FOR PRODUCTION # BUILD FOR PRODUCTION
################### ###################
FROM docker.io/node:lts-hydrogen As build FROM node:18-alpine3.16 As build
WORKDIR /usr/src/app WORKDIR /usr/src/app
@ -56,7 +56,7 @@ USER node
# PRODUCTION # PRODUCTION
################### ###################
FROM docker.io/node:lts-hydrogen As production FROM node:18-alpine3.16 As production
# Copy package.json to be able to execute migration command # Copy package.json to be able to execute migration command
COPY --chown=node:node package*.json ./ COPY --chown=node:node package*.json ./

View File

@ -52,7 +52,7 @@ Redis database is automatically populated at the start of the service. If keys a
The app exposes the following [gRPC](https://grpc.io/) services : The app exposes the following [gRPC](https://grpc.io/) services :
- **Get** : get a configuration item by its domain and key (retrieves the domain, key, value and type) - **Get** : get a configuration item by its domain and key
```json ```json
{ {

View File

@ -3,5 +3,5 @@ SERVICE_URL=0.0.0.0
SERVICE_PORT=5003 SERVICE_PORT=5003
# REDIS # REDIS
REDIS_IMAGE=docker.io/redis:7.0-alpine REDIS_IMAGE=redis:7.0-alpine
REDIS_PASSWORD=redis REDIS_PASSWORD=redis

View File

@ -2,7 +2,7 @@
# BUILD FOR CI TESTING # BUILD FOR CI TESTING
################### ###################
FROM docker.io/node:lts-hydrogen FROM node:18-alpine3.16
# Create app directory # Create app directory
WORKDIR /usr/src/app WORKDIR /usr/src/app

2017
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "@mobicoop/configuration", "name": "@mobicoop/configuration",
"version": "2.8.0", "version": "2.4.0",
"description": "Mobicoop V3 Configuration Service", "description": "Mobicoop V3 Configuration Service",
"author": "sbriat", "author": "sbriat",
"private": true, "private": true,
@ -24,48 +24,48 @@
"test:e2e": "jest --config ./test/jest-e2e.json" "test:e2e": "jest --config ./test/jest-e2e.json"
}, },
"dependencies": { "dependencies": {
"@grpc/grpc-js": "^1.9.13", "@grpc/grpc-js": "^1.9.9",
"@grpc/proto-loader": "^0.7.10", "@grpc/proto-loader": "^0.7.10",
"@mobicoop/configuration-module": "^8.1.2", "@mobicoop/configuration-module": "^7.2.1",
"@mobicoop/ddd-library": "^2.4.2", "@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/common": "^10.3.0", "@nestjs/common": "^10.2.7",
"@nestjs/config": "^3.1.1", "@nestjs/config": "^3.1.1",
"@nestjs/core": "^10.3.0", "@nestjs/core": "^10.2.7",
"@nestjs/cqrs": "^10.2.6", "@nestjs/cqrs": "^10.2.6",
"@nestjs/event-emitter": "^2.0.3", "@nestjs/event-emitter": "^2.0.2",
"@nestjs/microservices": "^10.3.0", "@nestjs/microservices": "^10.2.7",
"@nestjs/platform-express": "^10.3.0", "@nestjs/platform-express": "^10.2.7",
"@nestjs/terminus": "^10.2.0", "@nestjs/terminus": "^10.1.1",
"class-transformer": "^0.5.1", "class-transformer": "^0.5.1",
"class-validator": "^0.14.1", "class-validator": "^0.14.0",
"reflect-metadata": "^0.1.12", "reflect-metadata": "^0.1.13",
"rimraf": "^5.0.5" "rimraf": "^5.0.5"
}, },
"devDependencies": { "devDependencies": {
"@nestjs/cli": "^10.3.0", "@nestjs/cli": "^10.2.1",
"@nestjs/schematics": "^10.1.0", "@nestjs/schematics": "^10.0.3",
"@nestjs/testing": "^10.3.0", "@nestjs/testing": "^10.2.7",
"@types/express": "^4.17.21", "@types/express": "^4.17.20",
"@types/jest": "29.5.11", "@types/jest": "29.5.7",
"@types/node": "^20.11.2", "@types/node": "^20.8.10",
"@types/supertest": "^6.0.2", "@types/supertest": "^2.0.15",
"@typescript-eslint/eslint-plugin": "^6.18.1", "@typescript-eslint/eslint-plugin": "^6.9.1",
"@typescript-eslint/parser": "^6.18.1", "@typescript-eslint/parser": "^6.9.1",
"dotenv-cli": "^7.3.0", "dotenv-cli": "^7.3.0",
"eslint": "^8.56.0", "eslint": "^8.52.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.1.3", "eslint-plugin-prettier": "^5.0.1",
"jest": "29.7.0", "jest": "29.7.0",
"prettier": "^3.2.2", "prettier": "^3.0.3",
"source-map-support": "^0.5.21", "source-map-support": "^0.5.21",
"supertest": "^6.3.4", "supertest": "^6.3.3",
"ts-jest": "29.1.1", "ts-jest": "29.1.1",
"ts-loader": "^9.5.1", "ts-loader": "^9.5.0",
"ts-node": "^10.9.2", "ts-node": "^10.9.1",
"tsconfig-paths": "4.2.0", "tsconfig-paths": "4.2.0",
"typescript": "^5.3.3" "typescript": "^5.2.2"
}, },
"jest": { "jest": {
"moduleFileExtensions": [ "moduleFileExtensions": [

View File

@ -19,14 +19,12 @@ import redisConfig from './config/redis.config';
import { Transport } from '@nestjs/microservices'; import { Transport } from '@nestjs/microservices';
import matchConfig from './config/match.config'; import matchConfig from './config/match.config';
import geographyConfig from './config/geography.config'; import geographyConfig from './config/geography.config';
import authConfig from './config/auth.config';
@Module({ @Module({
imports: [ imports: [
ConfigModule.forRoot({ ConfigModule.forRoot({
isGlobal: true, isGlobal: true,
load: [ load: [
authConfig,
brokerConfig, brokerConfig,
carpoolConfig, carpoolConfig,
geographyConfig, geographyConfig,

View File

@ -1,10 +0,0 @@
import { registerAs } from '@nestjs/config';
import { Config } from './config';
export interface AuthConfig extends Config {
encryptionAlgorithm: string;
}
export default registerAs('auth', () => ({
encryptionAlgorithm: process.env.ENCRYPTION_ALGORITHM ?? 'ARGON2ID',
}));

View File

@ -1,8 +1,7 @@
import { registerAs } from '@nestjs/config'; import { registerAs } from '@nestjs/config';
export default registerAs('broker', () => ({ export default registerAs('broker', () => ({
uri: uri: process.env.MESSAGE_BROKER_URI ?? 'amqp://v3-broker:5672',
process.env.MESSAGE_BROKER_URI ?? 'amqp://mobicoop:mobicoop@v3-broker:5672',
exchange: process.env.MESSAGE_BROKER_EXCHANGE ?? 'mobicoop', exchange: process.env.MESSAGE_BROKER_EXCHANGE ?? 'mobicoop',
durability: process.env.MESSAGE_BROKER_EXCHANGE_DURABILITY durability: process.env.MESSAGE_BROKER_EXCHANGE_DURABILITY
? process.env.MESSAGE_BROKER_EXCHANGE_DURABILITY === 'false' ? process.env.MESSAGE_BROKER_EXCHANGE_DURABILITY === 'false'

View File

@ -1,5 +1,5 @@
import { Domain } from '@mobicoop/configuration-module'; import { ConfigurationDomain } from '@mobicoop/configuration-module';
export interface Config { export interface Config {
domain: Domain; domain: ConfigurationDomain;
} }

View File

@ -4,60 +4,9 @@ import { Config } from './config';
export interface GeographyConfig extends Config { export interface GeographyConfig extends Config {
georouterType: string; georouterType: string;
georouterUrl: string; georouterUrl: string;
geocoderLang: string;
geocoderMinConfidence: number;
geocoderMaxResultsPerProvider: number;
geocoderMaxResultsPerType: number;
geocoderSanitize: boolean;
geocoderConsolidate: boolean;
geocoderProximity: number;
geocoderPopulationPrioritizerCoef: number;
geocoderProviders: object[];
geocoderProvidersFallback: object[];
geocoderPrioritizers: object[];
geocoderConsolidators: object[];
} }
export default registerAs('geography', () => ({ export default registerAs('geography', () => ({
georouterType: process.env.GEOROUTER_TYPE ?? 'graphhopper', georouterType: process.env.GEOROUTER_TYPE ?? 'graphhopper',
georouterUrl: process.env.GEOROUTER_URL ?? 'http://localhost:8989', georouterUrl: process.env.GEOROUTER_URL ?? 'http://localhost:8989',
geocoderLang: process.env.GEOCODER_LANG ?? 'fr',
geocoderMinConfidence: process.env.GEOCODER_MIN_CONFIDENCE
? parseFloat(process.env.GEOCODER_MIN_CONFIDENCE)
: 0.5,
geocoderMaxResultsPerProvider: process.env.GEOCODER_MAX_RESULTS_PER_PROVIDER
? parseInt(process.env.GEOCODER_MAX_RESULTS_PER_PROVIDER)
: 5,
geocoderMaxResultsPerType: process.env.GEOCODER_MAX_RESULTS_PER_TYPE
? parseInt(process.env.GEOCODER_MAX_RESULTS_PER_TYPE)
: 5,
geocoderSanitize: process.env.GEOCODER_SANITIZE
? process.env.GEOCODER_SANITIZE === 'false'
? false
: true
: true,
geocoderConsolidate: process.env.GEOCODER_CONSOLIDATE
? process.env.GEOCODER_CONSOLIDATE === 'false'
? false
: true
: true,
geocoderProximity: process.env.GEOCODER_PROXIMITY
? parseInt(process.env.GEOCODER_PROXIMITY)
: 5000,
geocoderPopulationPrioritizerCoef: process.env
.GEOCODER_POPULATION_PRIORITIZER_COEF
? parseInt(process.env.GEOCODER_POPULATION_PRIORITIZER_COEF)
: 100,
geocoderProviders: process.env.GEOCODER_PROVIDERS
? JSON.parse(process.env.GEOCODER_PROVIDERS)
: [],
geocoderProvidersFallback: process.env.GEOCODER_PROVIDERS_FALLBACK
? JSON.parse(process.env.GEOCODER_PROVIDERS_FALLBACK)
: [],
geocoderPrioritizers: process.env.GEOCODER_PRIORITIZERS
? JSON.parse(process.env.GEOCODER_PRIORITIZERS)
: [],
geocoderConsolidators: process.env.GEOCODER_CONSOLIDATORS
? JSON.parse(process.env.GEOCODER_CONSOLIDATORS)
: [],
})); }));

View File

@ -1,6 +1,9 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { ConfigurationResponseDto } from './interface/dtos/configuration.response.dto'; import { ConfigurationResponseDto } from './interface/dtos/configuration.response.dto';
import { Identifier, Value } from '@mobicoop/configuration-module'; import {
ConfigurationIdentifier,
ConfigurationValue,
} from '@mobicoop/configuration-module';
import { ConfigurationsManagerService } from './core/application/services/configurations-manager.service'; import { ConfigurationsManagerService } from './core/application/services/configurations-manager.service';
@Injectable() @Injectable()
@ -10,14 +13,15 @@ export class ConfigurationMapper {
) {} ) {}
toResponse = ( toResponse = (
identifier: Identifier, configurationIdentifier: ConfigurationIdentifier,
value: Value, configurationValue: ConfigurationValue,
): ConfigurationResponseDto => { ): ConfigurationResponseDto => {
const response = new ConfigurationResponseDto(); const response = new ConfigurationResponseDto();
response.domain = identifier.domain; response.domain = configurationIdentifier.domain;
response.key = identifier.key; response.key = configurationIdentifier.key;
response.value = typeof value === 'object' ? JSON.stringify(value) : value; response.value = configurationValue;
response.type = this.configurationsManager.configurationType(value); response.type =
this.configurationsManager.configurationType(configurationValue);
return response; return response;
}; };
} }

View File

@ -1,13 +1,13 @@
import { Identifier } from '@mobicoop/configuration-module'; import { ConfigurationIdentifier } from '@mobicoop/configuration-module';
import { Command, CommandProps } from '@mobicoop/ddd-library'; import { Command, CommandProps } from '@mobicoop/ddd-library';
export class SetConfigurationCommand extends Command { export class SetConfigurationCommand extends Command {
readonly identifier: Identifier; readonly configurationIdentifier: ConfigurationIdentifier;
readonly value: string | boolean | number; readonly value: string | boolean | number;
constructor(props: CommandProps<SetConfigurationCommand>) { constructor(props: CommandProps<SetConfigurationCommand>) {
super(props); super(props);
this.identifier = props.identifier; this.configurationIdentifier = props.configurationIdentifier;
this.value = props.value; this.value = props.value;
} }
} }

View File

@ -3,8 +3,8 @@ import { Inject } from '@nestjs/common';
import { SetConfigurationCommand } from './set-configuration.command'; import { SetConfigurationCommand } from './set-configuration.command';
import { CONFIGURATION_REPOSITORY } from '@modules/configuration/configuration.di-tokens'; import { CONFIGURATION_REPOSITORY } from '@modules/configuration/configuration.di-tokens';
import { import {
Identifier, ConfigurationIdentifier,
Type, ConfigurationType,
SetConfigurationRepositoryPort, SetConfigurationRepositoryPort,
} from '@mobicoop/configuration-module'; } from '@mobicoop/configuration-module';
import { ConfigurationsManagerService } from '../../services/configurations-manager.service'; import { ConfigurationsManagerService } from '../../services/configurations-manager.service';
@ -18,19 +18,21 @@ export class SetConfigurationService implements ICommandHandler {
private readonly configurationsManager: ConfigurationsManagerService, private readonly configurationsManager: ConfigurationsManagerService,
) {} ) {}
async execute(command: SetConfigurationCommand): Promise<Identifier> { async execute(
const type: Type = this.configurationsManager.identifierType( command: SetConfigurationCommand,
command.identifier, ): Promise<ConfigurationIdentifier> {
); const configurationType: ConfigurationType =
this.configurationsManager.identifierType(
command.configurationIdentifier,
);
const value: any = this.configurationsManager.cast( const value: any = this.configurationsManager.cast(
`${command.value}`, `${command.value}`,
type, configurationType,
); );
if ((type === Type.INT || type === Type.FLOAT) && isNaN(value)) if (isNaN(value)) throw new ArgumentInvalidException('Bad value');
throw new ArgumentInvalidException('Bad value');
return await this.configurationRepository.set( return await this.configurationRepository.set(
command.identifier.domain, command.configurationIdentifier.domain,
command.identifier.key, command.configurationIdentifier.key,
value, value,
); );
} }

View File

@ -3,7 +3,7 @@ import { GetConfigurationQuery } from './get-configuration.query';
import { Inject } from '@nestjs/common'; import { Inject } from '@nestjs/common';
import { CONFIGURATION_REPOSITORY } from '@modules/configuration/configuration.di-tokens'; import { CONFIGURATION_REPOSITORY } from '@modules/configuration/configuration.di-tokens';
import { import {
Value, ConfigurationValue,
GetConfigurationRepositoryPort, GetConfigurationRepositoryPort,
} from '@mobicoop/configuration-module'; } from '@mobicoop/configuration-module';
import { ConfigurationsManagerService } from '../../services/configurations-manager.service'; import { ConfigurationsManagerService } from '../../services/configurations-manager.service';
@ -15,10 +15,11 @@ export class GetConfigurationQueryHandler implements IQueryHandler {
private readonly configurationRepository: GetConfigurationRepositoryPort, private readonly configurationRepository: GetConfigurationRepositoryPort,
private readonly configurationsManager: ConfigurationsManagerService, private readonly configurationsManager: ConfigurationsManagerService,
) {} ) {}
async execute(query: GetConfigurationQuery): Promise<Value> { async execute(query: GetConfigurationQuery): Promise<ConfigurationValue> {
return await this.configurationRepository.get(query.identifier.domain, { return await this.configurationRepository.get(
key: query.identifier.key, query.configurationIdentifier.domain,
type: this.configurationsManager.identifierType(query.identifier), query.configurationIdentifier.key,
}); this.configurationsManager.identifierType(query.configurationIdentifier),
);
} }
} }

View File

@ -1,12 +1,15 @@
import { Domain, Identifier } from '@mobicoop/configuration-module'; import {
ConfigurationDomain,
ConfigurationIdentifier,
} from '@mobicoop/configuration-module';
import { QueryBase } from '@mobicoop/ddd-library'; import { QueryBase } from '@mobicoop/ddd-library';
export class GetConfigurationQuery extends QueryBase { export class GetConfigurationQuery extends QueryBase {
readonly identifier: Identifier; readonly configurationIdentifier: ConfigurationIdentifier;
constructor(domain: Domain, key: string) { constructor(domain: ConfigurationDomain, key: string) {
super(); super();
this.identifier = { this.configurationIdentifier = {
domain, domain,
key, key,
}; };

View File

@ -1,13 +1,12 @@
import { import {
Domain, ConfigurationDomain,
Identifier, ConfigurationIdentifier,
Type, ConfigurationType,
Value, ConfigurationValue,
} from '@mobicoop/configuration-module'; } from '@mobicoop/configuration-module';
import { NotFoundException } from '@mobicoop/ddd-library'; import { NotFoundException } from '@mobicoop/ddd-library';
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { AuthConfig } from '@src/config/auth.config';
import { CarpoolConfig } from '@src/config/carpool.config'; import { CarpoolConfig } from '@src/config/carpool.config';
import { Config } from '@src/config/config'; import { Config } from '@src/config/config';
import { GeographyConfig } from '@src/config/geography.config'; import { GeographyConfig } from '@src/config/geography.config';
@ -20,90 +19,68 @@ export class ConfigurationsManagerService {
list = (): Config[] => { list = (): Config[] => {
return [ return [
{
...(this.configService.get<AuthConfig>('auth') as AuthConfig),
domain: Domain.AUTH,
},
{ {
...(this.configService.get<CarpoolConfig>('carpool') as CarpoolConfig), ...(this.configService.get<CarpoolConfig>('carpool') as CarpoolConfig),
domain: Domain.CARPOOL, domain: ConfigurationDomain.CARPOOL,
}, },
{ {
...(this.configService.get<GeographyConfig>( ...(this.configService.get<GeographyConfig>(
'geography', 'geography',
) as GeographyConfig), ) as GeographyConfig),
domain: Domain.GEOGRAPHY, domain: ConfigurationDomain.GEOGRAPHY,
}, },
{ {
...(this.configService.get<MatchConfig>('match') as MatchConfig), ...(this.configService.get<MatchConfig>('match') as MatchConfig),
domain: Domain.MATCH, domain: ConfigurationDomain.MATCH,
}, },
{ {
...(this.configService.get<PaginationConfig>( ...(this.configService.get<PaginationConfig>(
'pagination', 'pagination',
) as PaginationConfig), ) as PaginationConfig),
domain: Domain.PAGINATION, domain: ConfigurationDomain.PAGINATION,
}, },
]; ];
}; };
identifierType = (identifier: Identifier): Type => { identifierType = (
configurationIdentifier: ConfigurationIdentifier,
): ConfigurationType => {
const configs: Config[] = this.list(); const configs: Config[] = this.list();
const configuration: Config | undefined = configs.find( const configuration: Config | undefined = configs.find(
(config: Config) => (config: Config) =>
config.domain === identifier.domain && config.domain === configurationIdentifier.domain &&
this._hasProperty(identifier.key, config), this._hasProperty(configurationIdentifier.key, config),
); );
if (!configuration) if (!configuration)
throw new NotFoundException('Configuration item not found'); throw new NotFoundException('Configuration item not found');
return this.configurationType( return this.configurationType(
this._getValue(identifier.key, configuration), this._getValue(configurationIdentifier.key, configuration),
); );
}; };
configurationType = (value: any): Type => { configurationType = (value: any): ConfigurationType => {
if (Array.isArray(value)) return this._configurationTypeArray(value);
switch (typeof value) { switch (typeof value) {
case 'number': case 'number':
if (this._isInt(value)) return Type.INT; if (this._isInt(value)) return ConfigurationType.INT;
return Type.FLOAT; return ConfigurationType.FLOAT;
case 'boolean': case 'boolean':
return Type.BOOLEAN; return ConfigurationType.BOOLEAN;
case 'object':
return Type.JSON;
default: default:
if (value.indexOf(',') === -1) return Type.STRING; return ConfigurationType.STRING;
return this._configurationTypeArray(value.split(','));
} }
}; };
_configurationTypeArray = (value: Array<string | number>): Type => { cast = (
return value.every((item) => typeof item === 'number' && this._isInt(item)) value: string,
? Type.INT_ARRAY configurationType: ConfigurationType,
: value.every((item) => typeof item === 'number') ): ConfigurationValue => {
? Type.FLOAT_ARRAY switch (configurationType) {
: value.every((item) => typeof item === 'object') case ConfigurationType.BOOLEAN:
? Type.JSON_ARRAY
: Type.STRING_ARRAY;
};
cast = (value: string, type: Type): Value => {
switch (type) {
case Type.BOOLEAN:
return value === 'true'; return value === 'true';
case Type.INT: case ConfigurationType.INT:
return parseInt(value); return parseInt(value);
case Type.FLOAT: case ConfigurationType.FLOAT:
return parseFloat(value); return parseFloat(value);
case Type.JSON:
case Type.JSON_ARRAY:
return JSON.parse(value);
case Type.INT_ARRAY:
return value.split(',').map((item: string) => parseInt(item));
case Type.FLOAT_ARRAY:
return value.split(',').map((item: string) => parseFloat(item));
case Type.STRING_ARRAY:
return value.split(',');
default: default:
return value; return value;
} }

View File

@ -35,20 +35,17 @@ export class PopulateService implements OnApplicationBootstrap {
for (key in configuration) { for (key in configuration) {
try { try {
if (key !== 'domain') if (key !== 'domain')
await this.getConfigurationRepository.get(config.domain, { await this.getConfigurationRepository.get(
config.domain,
key, key,
type: this.configurationsManager.configurationType( this.configurationsManager.configurationType(configuration[key]),
configuration[key], );
),
});
} catch (error: any) { } catch (error: any) {
if (error instanceof NotFoundException) { if (error instanceof NotFoundException) {
this.setConfigurationRepository.set( this.setConfigurationRepository.set(
config.domain, config.domain,
key, key,
typeof configuration[key] === 'object' `${configuration[key]}`,
? JSON.stringify(configuration[key])
: `${configuration[key]}`,
); );
} }
} }

View File

@ -1,8 +1,8 @@
import { Type } from '@mobicoop/configuration-module'; import { ConfigurationType } from '@mobicoop/configuration-module';
export class ConfigurationResponseDto { export class ConfigurationResponseDto {
domain: string; domain: string;
key: string; key: string;
value: string | boolean | number | object | string[] | number[] | object[]; value: string | boolean | number;
type: Type; type: ConfigurationType;
} }

View File

@ -1,10 +1,10 @@
import { Domain } from '@mobicoop/configuration-module'; import { ConfigurationDomain } from '@mobicoop/configuration-module';
import { IsEnum, IsNotEmpty, IsString } from 'class-validator'; import { IsEnum, IsNotEmpty, IsString } from 'class-validator';
export class GetConfigurationRequestDto { export class GetConfigurationRequestDto {
@IsEnum(Domain) @IsEnum(ConfigurationDomain)
@IsNotEmpty() @IsNotEmpty()
domain: Domain; domain: ConfigurationDomain;
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()

View File

@ -1,10 +1,10 @@
import { Domain } from '@mobicoop/configuration-module'; import { ConfigurationDomain } from '@mobicoop/configuration-module';
import { IsEnum, IsNotEmpty, IsString } from 'class-validator'; import { IsEnum, IsNotEmpty, IsString } from 'class-validator';
export class SetConfigurationRequestDto { export class SetConfigurationRequestDto {
@IsEnum(Domain) @IsEnum(ConfigurationDomain)
@IsNotEmpty() @IsNotEmpty()
domain: Domain; domain: ConfigurationDomain;
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()

View File

@ -11,7 +11,7 @@ import {
RpcExceptionCode, RpcExceptionCode,
RpcValidationPipe, RpcValidationPipe,
} from '@mobicoop/ddd-library'; } from '@mobicoop/ddd-library';
import { Value } from '@mobicoop/configuration-module'; import { ConfigurationValue } from '@mobicoop/configuration-module';
@UsePipes( @UsePipes(
new RpcValidationPipe({ new RpcValidationPipe({
@ -31,10 +31,11 @@ export class GetConfigurationGrpcController {
data: GetConfigurationRequestDto, data: GetConfigurationRequestDto,
): Promise<ConfigurationResponseDto> { ): Promise<ConfigurationResponseDto> {
try { try {
const value: Value = await this.queryBus.execute( const configurationValue: ConfigurationValue =
new GetConfigurationQuery(data.domain, data.key), await this.queryBus.execute(
); new GetConfigurationQuery(data.domain, data.key),
return this.mapper.toResponse(data, value); );
return this.mapper.toResponse(data, configurationValue);
} catch (e) { } catch (e) {
if (e instanceof NotFoundException) { if (e instanceof NotFoundException) {
throw new RpcException({ throw new RpcException({

View File

@ -5,7 +5,7 @@ import { RpcExceptionCode, RpcValidationPipe } from '@mobicoop/ddd-library';
import { GRPC_SERVICE_NAME } from '@src/app.constants'; import { GRPC_SERVICE_NAME } from '@src/app.constants';
import { SetConfigurationRequestDto } from './dtos/set-configuration.request.dto'; import { SetConfigurationRequestDto } from './dtos/set-configuration.request.dto';
import { SetConfigurationCommand } from '@modules/configuration/core/application/commands/set-configuration/set-configuration.command'; import { SetConfigurationCommand } from '@modules/configuration/core/application/commands/set-configuration/set-configuration.command';
import { Identifier } from '@mobicoop/configuration-module'; import { ConfigurationIdentifier } from '@mobicoop/configuration-module';
@UsePipes( @UsePipes(
new RpcValidationPipe({ new RpcValidationPipe({
@ -20,18 +20,19 @@ export class SetConfigurationGrpcController {
@GrpcMethod(GRPC_SERVICE_NAME, 'Set') @GrpcMethod(GRPC_SERVICE_NAME, 'Set')
async set( async set(
setConfigurationRequestDto: SetConfigurationRequestDto, setConfigurationRequestDto: SetConfigurationRequestDto,
): Promise<Identifier> { ): Promise<ConfigurationIdentifier> {
try { try {
const identifier: Identifier = await this.commandBus.execute( const configurationIdentifier: ConfigurationIdentifier =
new SetConfigurationCommand({ await this.commandBus.execute(
identifier: { new SetConfigurationCommand({
domain: setConfigurationRequestDto.domain, configurationIdentifier: {
key: setConfigurationRequestDto.key, domain: setConfigurationRequestDto.domain,
}, key: setConfigurationRequestDto.key,
value: setConfigurationRequestDto.value, },
}), value: setConfigurationRequestDto.value,
); }),
return identifier; );
return configurationIdentifier;
} catch (error: any) { } catch (error: any) {
throw new RpcException({ throw new RpcException({
code: RpcExceptionCode.UNKNOWN, code: RpcExceptionCode.UNKNOWN,

View File

@ -1,11 +1,14 @@
import { Domain, Type } from '@mobicoop/configuration-module'; import {
ConfigurationDomain,
ConfigurationType,
} from '@mobicoop/configuration-module';
import { ConfigurationMapper } from '@modules/configuration/configuration.mapper'; import { ConfigurationMapper } from '@modules/configuration/configuration.mapper';
import { ConfigurationsManagerService } from '@modules/configuration/core/application/services/configurations-manager.service'; import { ConfigurationsManagerService } from '@modules/configuration/core/application/services/configurations-manager.service';
import { ConfigurationResponseDto } from '@modules/configuration/interface/dtos/configuration.response.dto'; import { ConfigurationResponseDto } from '@modules/configuration/interface/dtos/configuration.response.dto';
import { Test } from '@nestjs/testing'; import { Test } from '@nestjs/testing';
const mockConfigurationsManagerService = { const mockConfigurationsManagerService = {
configurationType: jest.fn().mockImplementation(() => Type.INT), configurationType: jest.fn().mockImplementation(() => ConfigurationType.INT),
}; };
describe('Configuration Mapper', () => { describe('Configuration Mapper', () => {
@ -31,7 +34,7 @@ describe('Configuration Mapper', () => {
it('should map configuration to response', async () => { it('should map configuration to response', async () => {
const mapped: ConfigurationResponseDto = configurationMapper.toResponse( const mapped: ConfigurationResponseDto = configurationMapper.toResponse(
{ {
domain: Domain.CARPOOL, domain: ConfigurationDomain.CARPOOL,
key: 'seatsProposed', key: 'seatsProposed',
}, },
'3', '3',

View File

@ -1,4 +1,8 @@
import { Domain, Identifier, Type } from '@mobicoop/configuration-module'; import {
ConfigurationDomain,
ConfigurationIdentifier,
ConfigurationType,
} from '@mobicoop/configuration-module';
import { NotFoundException } from '@mobicoop/ddd-library'; import { NotFoundException } from '@mobicoop/ddd-library';
import { ConfigurationsManagerService } from '@modules/configuration/core/application/services/configurations-manager.service'; import { ConfigurationsManagerService } from '@modules/configuration/core/application/services/configurations-manager.service';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
@ -8,10 +12,6 @@ import { Config } from '@src/config/config';
const mockConfigService = { const mockConfigService = {
get: jest.fn().mockImplementation((domain: string) => { get: jest.fn().mockImplementation((domain: string) => {
switch (domain) { switch (domain) {
case 'auth':
return {
encryptionAlgorithm: 'BCRYPT',
};
case 'carpool': case 'carpool':
return { return {
departureTimeMargin: 900, departureTimeMargin: 900,
@ -22,18 +22,8 @@ const mockConfigService = {
}; };
case 'geography': case 'geography':
return { return {
georouterType: 'graphhopper', type: 'graphhopper',
georouterUrl: 'http://localhost:8989', url: 'http://localhost:8989',
geocoderLang: 'fr',
geocoderMinConfidence: 0.3,
geocoderMaxResultsPerProvider: 5,
geocoderMaxResultsPerType: 5,
geocoderSanitize: true,
geocoderConsolidate: true,
geocoderProximity: 5,
geocoderPopulationPrioritizerCoef: 100,
geocoderProviders: [{ name: 'provider1' }, { name: 'provider2' }],
geocoderProvidersFallback: [{ name: 'provider3' }],
}; };
case 'match': case 'match':
return { return {
@ -79,126 +69,73 @@ describe('Configurations Manager Service', () => {
it('should return the list of configuration elements', () => { it('should return the list of configuration elements', () => {
const list: Config[] = configurationsManagerService.list(); const list: Config[] = configurationsManagerService.list();
expect(list).toHaveLength(5); expect(list).toHaveLength(4);
}); });
describe('identifierType', () => { describe('identifierType', () => {
it('should return the type of a configuration item for a given identifier', () => { it('should return the type of a configuration item for a given identifier', () => {
const identifier: Identifier = { const configurationIdentifier: ConfigurationIdentifier = {
domain: Domain.CARPOOL, domain: ConfigurationDomain.CARPOOL,
key: 'seatsProposed', key: 'seatsProposed',
}; };
const type: Type = const configurationType: ConfigurationType =
configurationsManagerService.identifierType(identifier); configurationsManagerService.identifierType(configurationIdentifier);
expect(type).toBe(Type.INT); expect(configurationType).toBe(ConfigurationType.INT);
}); });
it('should throw if configuration item is not found', () => { it('should throw if configuration item is not found', () => {
const identifier: Identifier = { const configurationIdentifier: ConfigurationIdentifier = {
domain: Domain.MATCH, domain: ConfigurationDomain.MATCH,
key: 'maxDetour', key: 'maxDetour',
}; };
expect(() => { expect(() => {
configurationsManagerService.identifierType(identifier); configurationsManagerService.identifierType(configurationIdentifier);
}).toThrow(NotFoundException); }).toThrow(NotFoundException);
}); });
}); });
describe('Type', () => { describe('configurationType', () => {
it('should return the configuration type of an int', () => { it('should return the configuration type of an int', () => {
expect(configurationsManagerService.configurationType(3)).toBe(Type.INT); expect(configurationsManagerService.configurationType(3)).toBe(
ConfigurationType.INT,
);
}); });
it('should return the configuration type of a float', () => { it('should return the configuration type of a float', () => {
expect(configurationsManagerService.configurationType(3.5)).toBe( expect(configurationsManagerService.configurationType(3.5)).toBe(
Type.FLOAT, ConfigurationType.FLOAT,
); );
}); });
it('should return the configuration type of a boolean', () => { it('should return the configuration type of a boolean', () => {
expect(configurationsManagerService.configurationType(true)).toBe( expect(configurationsManagerService.configurationType(true)).toBe(
Type.BOOLEAN, ConfigurationType.BOOLEAN,
); );
}); });
it('should return the configuration type of a string', () => { it('should return the configuration type of a string', () => {
expect(configurationsManagerService.configurationType('role')).toBe( expect(configurationsManagerService.configurationType('role')).toBe(
Type.STRING, ConfigurationType.STRING,
); );
}); });
it('should return the configuration type of a json object', () => {
expect(
configurationsManagerService.configurationType({ key: 'value' }),
).toBe(Type.JSON);
});
it('should return the configuration type of a string array', () => {
expect(
configurationsManagerService.configurationType(['test', 'test2']),
).toBe(Type.STRING_ARRAY);
expect(configurationsManagerService.configurationType('test,test2')).toBe(
Type.STRING_ARRAY,
);
});
it('should return the configuration type of an int array', () => {
expect(configurationsManagerService.configurationType([2, 3])).toBe(
Type.INT_ARRAY,
);
});
it('should return the configuration type of a float array', () => {
expect(configurationsManagerService.configurationType([1.2, 3.6])).toBe(
Type.FLOAT_ARRAY,
);
});
it('should return the configuration type of a json array', () => {
expect(
configurationsManagerService.configurationType([
{ key1: 'value1' },
{ key2: 'value2' },
]),
).toBe(Type.JSON_ARRAY);
});
}); });
describe('cast', () => { describe('cast', () => {
it('should cast a string to int', () => { it('should cast a string to int', () => {
expect(configurationsManagerService.cast('1', Type.INT)).toBe(1); expect(
configurationsManagerService.cast('1', ConfigurationType.INT),
).toBe(1);
}); });
it('should cast a string to float', () => { it('should cast a string to float', () => {
expect(configurationsManagerService.cast('1.5', Type.FLOAT)).toBe(1.5); expect(
configurationsManagerService.cast('1.5', ConfigurationType.FLOAT),
).toBe(1.5);
}); });
it('should cast a string to boolean', () => { it('should cast a string to boolean', () => {
expect( expect(
configurationsManagerService.cast('true', Type.BOOLEAN), configurationsManagerService.cast('true', ConfigurationType.BOOLEAN),
).toBeTruthy(); ).toBeTruthy();
}); });
it('should not cast a string and return it as is', () => { it('should not cast a string and return it as is', () => {
expect(configurationsManagerService.cast('role', Type.STRING)).toBe(
'role',
);
});
it('should cast a string to json object', () => {
expect( expect(
configurationsManagerService.cast('{"key":"value"}', Type.JSON), configurationsManagerService.cast('role', ConfigurationType.STRING),
).toStrictEqual({ key: 'value' }); ).toBe('role');
});
it('should cast a string to an array of strings', () => {
expect(
configurationsManagerService.cast('test,test2', Type.STRING_ARRAY),
).toStrictEqual(['test', 'test2']);
});
it('should cast a string to an array of ints', () => {
expect(
configurationsManagerService.cast('1,2', Type.INT_ARRAY),
).toStrictEqual([1, 2]);
});
it('should cast a string to an array of floats', () => {
expect(
configurationsManagerService.cast('1.2,2.3', Type.FLOAT_ARRAY),
).toStrictEqual([1.2, 2.3]);
});
it('should cast a string to an array of json objects', () => {
expect(
configurationsManagerService.cast(
'[{"key1":"value1"},{"key2":"value2"}]',
Type.JSON_ARRAY,
),
).toStrictEqual([{ key1: 'value1' }, { key2: 'value2' }]);
}); });
}); });
}); });

View File

@ -2,17 +2,21 @@ import { Test, TestingModule } from '@nestjs/testing';
import { GetConfigurationQueryHandler } from '@modules/configuration/core/application/queries/get-configuration/get-configuration.query-handler'; import { GetConfigurationQueryHandler } from '@modules/configuration/core/application/queries/get-configuration/get-configuration.query-handler';
import { CONFIGURATION_REPOSITORY } from '@modules/configuration/configuration.di-tokens'; import { CONFIGURATION_REPOSITORY } from '@modules/configuration/configuration.di-tokens';
import { GetConfigurationQuery } from '@modules/configuration/core/application/queries/get-configuration/get-configuration.query'; import { GetConfigurationQuery } from '@modules/configuration/core/application/queries/get-configuration/get-configuration.query';
import { Domain, Type, Value } from '@mobicoop/configuration-module'; import {
ConfigurationDomain,
ConfigurationType,
ConfigurationValue,
} from '@mobicoop/configuration-module';
import { ConfigurationsManagerService } from '@modules/configuration/core/application/services/configurations-manager.service'; import { ConfigurationsManagerService } from '@modules/configuration/core/application/services/configurations-manager.service';
const value: Value = '3'; const configurationValue: ConfigurationValue = '3';
const mockConfigurationRepository = { const mockConfigurationRepository = {
get: jest.fn().mockImplementation(() => value), get: jest.fn().mockImplementation(() => configurationValue),
}; };
const mockConfigurationsManagerService = { const mockConfigurationsManagerService = {
identifierType: jest.fn().mockImplementation(() => Type.INT), identifierType: jest.fn().mockImplementation(() => ConfigurationType.INT),
}; };
describe('Get Configuration Query Handler', () => { describe('Get Configuration Query Handler', () => {
@ -45,13 +49,12 @@ describe('Get Configuration Query Handler', () => {
describe('execution', () => { describe('execution', () => {
it('should return a configuration value', async () => { it('should return a configuration value', async () => {
const getConfigurationQuery = new GetConfigurationQuery( const getConfigurationQuery = new GetConfigurationQuery(
Domain.CARPOOL, ConfigurationDomain.CARPOOL,
'seatsProposed', 'seatsProposed',
); );
const value: Value = await getConfigurationQueryHandler.execute( const configurationValue: ConfigurationValue =
getConfigurationQuery, await getConfigurationQueryHandler.execute(getConfigurationQuery);
); expect(configurationValue).toBe('3');
expect(value).toBe('3');
}); });
}); });
}); });

View File

@ -1,4 +1,7 @@
import { Domain, NotFoundException } from '@mobicoop/configuration-module'; import {
ConfigurationDomain,
NotFoundException,
} from '@mobicoop/configuration-module';
import { CONFIGURATION_REPOSITORY } from '@modules/configuration/configuration.di-tokens'; import { CONFIGURATION_REPOSITORY } from '@modules/configuration/configuration.di-tokens';
import { ConfigurationsManagerService } from '@modules/configuration/core/application/services/configurations-manager.service'; import { ConfigurationsManagerService } from '@modules/configuration/core/application/services/configurations-manager.service';
import { PopulateService } from '@modules/configuration/core/application/services/populate.service'; import { PopulateService } from '@modules/configuration/core/application/services/populate.service';
@ -17,7 +20,7 @@ const mockConfigurationRepository = {
const mockConfigurationsManagerService = { const mockConfigurationsManagerService = {
list: jest.fn().mockImplementation(() => [ list: jest.fn().mockImplementation(() => [
{ {
domain: Domain.CARPOOL, domain: ConfigurationDomain.CARPOOL,
departureTimeMargin: 900, departureTimeMargin: 900,
role: 'passenger', role: 'passenger',
seatsProposed: 3, seatsProposed: 3,
@ -25,7 +28,7 @@ const mockConfigurationsManagerService = {
strictFrequency: false, strictFrequency: false,
}, },
{ {
domain: Domain.PAGINATION, domain: ConfigurationDomain.PAGINATION,
perPage: 10, perPage: 10,
}, },
]), ]),

View File

@ -3,11 +3,14 @@ import { SetConfigurationRequestDto } from '@modules/configuration/interface/grp
import { SetConfigurationService } from '@modules/configuration/core/application/commands/set-configuration/set-configuration.service'; import { SetConfigurationService } from '@modules/configuration/core/application/commands/set-configuration/set-configuration.service';
import { CONFIGURATION_REPOSITORY } from '@modules/configuration/configuration.di-tokens'; import { CONFIGURATION_REPOSITORY } from '@modules/configuration/configuration.di-tokens';
import { SetConfigurationCommand } from '@modules/configuration/core/application/commands/set-configuration/set-configuration.command'; import { SetConfigurationCommand } from '@modules/configuration/core/application/commands/set-configuration/set-configuration.command';
import { Domain, Type } from '@mobicoop/configuration-module'; import {
ConfigurationDomain,
ConfigurationType,
} from '@mobicoop/configuration-module';
import { ConfigurationsManagerService } from '@modules/configuration/core/application/services/configurations-manager.service'; import { ConfigurationsManagerService } from '@modules/configuration/core/application/services/configurations-manager.service';
const setConfigurationRequest: SetConfigurationRequestDto = { const setConfigurationRequest: SetConfigurationRequestDto = {
domain: Domain.CARPOOL, domain: ConfigurationDomain.CARPOOL,
key: 'seatsProposed', key: 'seatsProposed',
value: '3', value: '3',
}; };
@ -22,7 +25,7 @@ const mockConfigurationRepository = {
}; };
const mockConfigurationsManagerService = { const mockConfigurationsManagerService = {
identifierType: jest.fn().mockImplementation(() => Type.INT), identifierType: jest.fn().mockImplementation(() => ConfigurationType.INT),
cast: jest.fn().mockImplementation(() => 3), cast: jest.fn().mockImplementation(() => 3),
}; };
@ -55,7 +58,7 @@ describe('Set Configuration Service', () => {
describe('execution', () => { describe('execution', () => {
const setConfigurationCommand = new SetConfigurationCommand({ const setConfigurationCommand = new SetConfigurationCommand({
identifier: { configurationIdentifier: {
domain: setConfigurationRequest.domain, domain: setConfigurationRequest.domain,
key: setConfigurationRequest.key, key: setConfigurationRequest.key,
}, },

View File

@ -1,4 +1,4 @@
import { Domain } from '@mobicoop/configuration-module'; import { ConfigurationDomain } from '@mobicoop/configuration-module';
import { NotFoundException, RpcExceptionCode } from '@mobicoop/ddd-library'; import { NotFoundException, RpcExceptionCode } from '@mobicoop/ddd-library';
import { ConfigurationMapper } from '@modules/configuration/configuration.mapper'; import { ConfigurationMapper } from '@modules/configuration/configuration.mapper';
import { GetConfigurationGrpcController } from '@modules/configuration/interface/grpc-controllers/get-configuration.grpc.controller'; import { GetConfigurationGrpcController } from '@modules/configuration/interface/grpc-controllers/get-configuration.grpc.controller';
@ -20,7 +20,7 @@ const mockQueryBus = {
const mockConfigurationMapper = { const mockConfigurationMapper = {
toResponse: jest.fn().mockImplementationOnce(() => ({ toResponse: jest.fn().mockImplementationOnce(() => ({
domain: Domain.CARPOOL, domain: ConfigurationDomain.CARPOOL,
key: 'seatsProposed', key: 'seatsProposed',
value: '3', value: '3',
})), })),
@ -61,7 +61,7 @@ describe('Get Configuration Grpc Controller', () => {
jest.spyOn(mockQueryBus, 'execute'); jest.spyOn(mockQueryBus, 'execute');
jest.spyOn(mockConfigurationMapper, 'toResponse'); jest.spyOn(mockConfigurationMapper, 'toResponse');
const response = await getConfigurationGrpcController.get({ const response = await getConfigurationGrpcController.get({
domain: Domain.CARPOOL, domain: ConfigurationDomain.CARPOOL,
key: 'seatsProposed', key: 'seatsProposed',
}); });
expect(response.value).toBe('3'); expect(response.value).toBe('3');
@ -75,7 +75,7 @@ describe('Get Configuration Grpc Controller', () => {
expect.assertions(4); expect.assertions(4);
try { try {
await getConfigurationGrpcController.get({ await getConfigurationGrpcController.get({
domain: Domain.CARPOOL, domain: ConfigurationDomain.CARPOOL,
key: 'price', key: 'price',
}); });
} catch (e: any) { } catch (e: any) {
@ -92,7 +92,7 @@ describe('Get Configuration Grpc Controller', () => {
expect.assertions(4); expect.assertions(4);
try { try {
await getConfigurationGrpcController.get({ await getConfigurationGrpcController.get({
domain: Domain.CARPOOL, domain: ConfigurationDomain.CARPOOL,
key: 'someValue', key: 'someValue',
}); });
} catch (e: any) { } catch (e: any) {

View File

@ -1,4 +1,7 @@
import { Domain, Identifier } from '@mobicoop/configuration-module'; import {
ConfigurationDomain,
ConfigurationIdentifier,
} from '@mobicoop/configuration-module';
import { RpcExceptionCode } from '@mobicoop/ddd-library'; import { RpcExceptionCode } from '@mobicoop/ddd-library';
import { SetConfigurationRequestDto } from '@modules/configuration/interface/grpc-controllers/dtos/set-configuration.request.dto'; import { SetConfigurationRequestDto } from '@modules/configuration/interface/grpc-controllers/dtos/set-configuration.request.dto';
import { SetConfigurationGrpcController } from '@modules/configuration/interface/grpc-controllers/set-configuration.grpc.controller'; import { SetConfigurationGrpcController } from '@modules/configuration/interface/grpc-controllers/set-configuration.grpc.controller';
@ -7,7 +10,7 @@ import { RpcException } from '@nestjs/microservices';
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
const setConfigurationRequest: SetConfigurationRequestDto = { const setConfigurationRequest: SetConfigurationRequestDto = {
domain: Domain.CARPOOL, domain: ConfigurationDomain.CARPOOL,
key: 'seatsProposed', key: 'seatsProposed',
value: '3', value: '3',
}; };
@ -16,7 +19,7 @@ const mockCommandBus = {
execute: jest execute: jest
.fn() .fn()
.mockImplementationOnce(() => ({ .mockImplementationOnce(() => ({
domain: Domain.CARPOOL, domain: ConfigurationDomain.CARPOOL,
key: 'seatsProposed', key: 'seatsProposed',
})) }))
.mockImplementationOnce(() => { .mockImplementationOnce(() => {
@ -53,10 +56,9 @@ describe('Set Configuration Grpc Controller', () => {
it('should set a configuration item', async () => { it('should set a configuration item', async () => {
jest.spyOn(mockCommandBus, 'execute'); jest.spyOn(mockCommandBus, 'execute');
const identifier: Identifier = await setConfigurationGrpcController.set( const configurationIdentifier: ConfigurationIdentifier =
setConfigurationRequest, await setConfigurationGrpcController.set(setConfigurationRequest);
); expect(configurationIdentifier.key).toBe('seatsProposed');
expect(identifier.key).toBe('seatsProposed');
expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); expect(mockCommandBus.execute).toHaveBeenCalledTimes(1);
}); });

View File

@ -20,7 +20,7 @@
"paths": { "paths": {
"@libs/*": ["src/libs/*"], "@libs/*": ["src/libs/*"],
"@modules/*": ["src/modules/*"], "@modules/*": ["src/modules/*"],
"@src/*": ["src/*"], "@src/*": ["src/*"]
}, }
}, }
} }