extract config files

This commit is contained in:
sbriat 2023-10-24 11:21:45 +02:00
parent 376260d903
commit f960299565
35 changed files with 313 additions and 113 deletions

View File

@ -10,3 +10,21 @@ DATABASE_URL="postgresql://mobicoop:mobicoop@v3-db:5432/mobicoop?schema=configur
MESSAGE_BROKER_URI=amqp://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
# DEFAULT CONFIGURATION
# CARPOOL
# default carpool departure time margin (in seconds)
DEPARTURE_TIME_MARGIN=900
# default role
ROLE=passenger
# seats proposed as driver / requested as passenger
SEATS_PROPOSED=3
SEATS_REQUESTED=1
# accept only same frequency requests
STRICT_FREQUENCY=false
# PAGINATION
# number of results per page
PER_PAGE=10

20
package-lock.json generated
View File

@ -2687,6 +2687,12 @@
"pretty-format": "^29.0.0" "pretty-format": "^29.0.0"
} }
}, },
"node_modules/@types/js-yaml": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.8.tgz",
"integrity": "sha512-m6jnPk1VhlYRiLFm3f8X9Uep761f+CK8mHyS65LutH2OhmBF0BeMEjHgg05usH8PLZMWWc/BUR9RPmkvpWnyRA==",
"dev": true
},
"node_modules/@types/json-schema": { "node_modules/@types/json-schema": {
"version": "7.0.14", "version": "7.0.14",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz",
@ -5559,20 +5565,6 @@
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true "dev": true
}, },
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": { "node_modules/function-bind": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",

View File

@ -16,10 +16,24 @@ import { MESSAGE_PUBLISHER } from '@modules/messager/messager.di-tokens';
import { CONFIGURATION_REPOSITORY } from '@modules/configuration/configuration.di-tokens'; import { CONFIGURATION_REPOSITORY } from '@modules/configuration/configuration.di-tokens';
import { ConfigurationModule } from '@modules/configuration/configuration.module'; import { ConfigurationModule } from '@modules/configuration/configuration.module';
import { EventEmitterModule } from '@nestjs/event-emitter'; import { EventEmitterModule } from '@nestjs/event-emitter';
import brokerConfig from './config/broker.config';
import carpoolConfig from './config/carpool.config';
import databaseConfig from './config/database.config';
import paginationConfig from './config/pagination.config';
import serviceConfig from './config/service.config';
@Module({ @Module({
imports: [ imports: [
ConfigModule.forRoot({ isGlobal: true }), ConfigModule.forRoot({
isGlobal: true,
load: [
brokerConfig,
carpoolConfig,
databaseConfig,
paginationConfig,
serviceConfig,
],
}),
EventEmitterModule.forRoot(), EventEmitterModule.forRoot(),
HealthModule.forRootAsync({ HealthModule.forRootAsync({
imports: [ConfigurationModule, MessagerModule], imports: [ConfigurationModule, MessagerModule],

View File

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

View File

@ -0,0 +1,19 @@
import { registerAs } from '@nestjs/config';
export default registerAs('carpool', () => ({
departureTimeMargin: process.env.DEPARTURE_TIME_MARGIN
? parseInt(process.env.DEPARTURE_TIME_MARGIN, 10)
: 900,
role: process.env.ROLE ?? 'passenger',
seatsProposed: process.env.SEATS_PROPOSED
? parseInt(process.env.SEATS_PROPOSED, 10)
: 3,
seatsRequested: process.env.SEATS_REQUESTED
? parseInt(process.env.SEATS_REQUESTED, 10)
: 1,
strictFrequency: process.env.STRICT_FREQUENCY
? process.env.STRICT_FREQUENCY === 'false'
? false
: true
: false,
}));

View File

@ -0,0 +1,7 @@
import { registerAs } from '@nestjs/config';
export default registerAs('database', () => ({
url:
process.env.DATABASE_URL ??
'postgresql://mobicoop:mobicoop@v3-db:5432/mobicoop?schema=configuration',
}));

View File

@ -0,0 +1,5 @@
import { registerAs } from '@nestjs/config';
export default registerAs('pagination', () => ({
perPage: process.env.PER_PAGE ? parseInt(process.env.PER_PAGE, 10) : 10,
}));

View File

@ -0,0 +1,8 @@
import { registerAs } from '@nestjs/config';
export default registerAs('service', () => ({
url: process.env.SERVICE_URL ?? '0.0.0.0',
port: process.env.SERVICE_PORT
? parseInt(process.env.SERVICE_PORT, 10)
: 5003,
}));

View File

@ -6,6 +6,10 @@ import {
ConfigurationWriteModel, ConfigurationWriteModel,
} from './infrastructure/configuration.repository'; } from './infrastructure/configuration.repository';
import { ConfigurationResponseDto } from './interface/dtos/configuration.response.dto'; import { ConfigurationResponseDto } from './interface/dtos/configuration.response.dto';
import {
ConfigurationDomain,
ConfigurationType,
} from './core/domain/configuration.types';
/** /**
* Mapper constructs objects that are used in different layers: * Mapper constructs objects that are used in different layers:
@ -28,9 +32,10 @@ export class ConfigurationMapper
const copy = entity.getProps(); const copy = entity.getProps();
const record: ConfigurationWriteModel = { const record: ConfigurationWriteModel = {
uuid: entity.id, uuid: entity.id,
domain: copy.domain, domain: copy.identifier.domain,
key: copy.key, key: copy.identifier.key,
value: copy.value, value: copy.value,
type: copy.type,
}; };
return record; return record;
}; };
@ -41,9 +46,12 @@ export class ConfigurationMapper
createdAt: new Date(record.createdAt), createdAt: new Date(record.createdAt),
updatedAt: new Date(record.updatedAt), updatedAt: new Date(record.updatedAt),
props: { props: {
domain: record.domain, identifier: {
key: record.key, domain: record.domain as ConfigurationDomain,
key: record.key,
},
value: record.value, value: record.value,
type: record.type as ConfigurationType,
}, },
}); });
return entity; return entity;
@ -52,9 +60,10 @@ export class ConfigurationMapper
toResponse = (entity: ConfigurationEntity): ConfigurationResponseDto => { toResponse = (entity: ConfigurationEntity): ConfigurationResponseDto => {
const props = entity.getProps(); const props = entity.getProps();
const response = new ConfigurationResponseDto(entity); const response = new ConfigurationResponseDto(entity);
response.domain = props.domain; response.domain = props.identifier.domain;
response.key = props.key; response.key = props.identifier.key;
response.value = props.value; response.value = props.value;
response.type = props.type;
return response; return response;
}; };
} }

View File

@ -5,6 +5,10 @@ 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 { ConfigurationRepositoryPort } from '../../ports/configuration.repository.port'; import { ConfigurationRepositoryPort } from '../../ports/configuration.repository.port';
import { ConfigurationEntity } from '@modules/configuration/core/domain/configuration.entity'; import { ConfigurationEntity } from '@modules/configuration/core/domain/configuration.entity';
import {
ConfigurationDomain,
ConfigurationType,
} from '@modules/configuration/core/domain/configuration.types';
@CommandHandler(SetConfigurationCommand) @CommandHandler(SetConfigurationCommand)
export class SetConfigurationService implements ICommandHandler { export class SetConfigurationService implements ICommandHandler {
@ -29,7 +33,14 @@ export class SetConfigurationService implements ICommandHandler {
} catch (error: any) { } catch (error: any) {
if (error instanceof NotFoundException) { if (error instanceof NotFoundException) {
try { try {
const newConfiguration = ConfigurationEntity.create(command); const newConfiguration = ConfigurationEntity.create({
identifier: {
domain: command.domain as ConfigurationDomain,
key: command.key,
},
value: command.value,
type: ConfigurationType.STRING,
});
await this.repository.insert(newConfiguration); await this.repository.insert(newConfiguration);
return newConfiguration.id; return newConfiguration.id;
} catch (error: any) { } catch (error: any) {

View File

@ -21,8 +21,8 @@ export class PublishMessageWhenConfigurationIsDeletedDomainEventHandler {
const configurationDeletedIntegrationEvent = const configurationDeletedIntegrationEvent =
new ConfigurationDeletedIntegrationEvent({ new ConfigurationDeletedIntegrationEvent({
id: event.aggregateId, id: event.aggregateId,
domain: event.domain, domain: event.identifier.domain,
key: event.key, key: event.identifier.key,
metadata: event.metadata, metadata: event.metadata,
}); });
this.messagePublisher.publish( this.messagePublisher.publish(

View File

@ -18,8 +18,8 @@ export class PublishMessageWhenConfigurationIsSetDomainEventHandler {
const configurationSetIntegrationEvent = const configurationSetIntegrationEvent =
new ConfigurationSetIntegrationEvent({ new ConfigurationSetIntegrationEvent({
id: event.aggregateId, id: event.aggregateId,
domain: event.domain, domain: event.identifier.domain,
key: event.key, key: event.identifier.key,
value: event.value, value: event.value,
metadata: event.metadata, metadata: event.metadata,
}); });

View File

@ -0,0 +1,32 @@
import {
ConfigurationDomain,
ConfigurationType,
ConfigurationItems,
} from './configuration.types';
export const configurationItems: ConfigurationItems = {
[ConfigurationDomain.CARPOOL]: {
seatsProposed: {
value: '3',
type: ConfigurationType.NUMBER,
},
seatsRequested: {
value: '1',
type: ConfigurationType.NUMBER,
},
strictFrequency: {
value: 'false',
type: ConfigurationType.BOOLEAN,
},
departureTimeMargin: {
value: '900',
type: ConfigurationType.NUMBER,
},
role: {
value: 'passenger',
type: ConfigurationType.STRING,
enum: ['driver', 'passenger'],
},
},
[ConfigurationDomain.USER]: {},
};

View File

@ -18,9 +18,9 @@ export class ConfigurationEntity extends AggregateRoot<ConfigurationProps> {
configuration.addEvent( configuration.addEvent(
new ConfigurationSetDomainEvent({ new ConfigurationSetDomainEvent({
aggregateId: id, aggregateId: id,
domain: props.domain, identifier: props.identifier,
key: props.key,
value: props.value, value: props.value,
type: props.type,
}), }),
); );
return configuration; return configuration;
@ -31,9 +31,9 @@ export class ConfigurationEntity extends AggregateRoot<ConfigurationProps> {
this.addEvent( this.addEvent(
new ConfigurationSetDomainEvent({ new ConfigurationSetDomainEvent({
aggregateId: this._id, aggregateId: this._id,
domain: this.props.domain, identifier: this.props.identifier,
key: this.props.key,
value: props.value, value: props.value,
type: this.props.type,
}), }),
); );
} }
@ -42,8 +42,7 @@ export class ConfigurationEntity extends AggregateRoot<ConfigurationProps> {
this.addEvent( this.addEvent(
new ConfigurationDeletedDomainEvent({ new ConfigurationDeletedDomainEvent({
aggregateId: this.id, aggregateId: this.id,
domain: this.props.domain, identifier: this.props.identifier,
key: this.props.key,
}), }),
); );
} }

View File

@ -1,23 +1,48 @@
// All properties that a Configuration has // All properties that a Configuration has
export interface ConfigurationProps { export interface ConfigurationProps {
domain: string; identifier: ConfigurationIdentifier;
key: string; value: ConfigurationValue;
value: string; type: ConfigurationType;
} }
// Properties that are needed for a Configuration creation // Properties that are needed for a Configuration creation
export interface CreateConfigurationProps { export interface CreateConfigurationProps {
domain: string; identifier: ConfigurationIdentifier;
key: string; value: ConfigurationValue;
value: string; type: ConfigurationType;
} }
export interface UpdateConfigurationProps { export interface UpdateConfigurationProps {
value: string; value: ConfigurationValue;
} }
export enum Domain { export enum ConfigurationDomain {
AD = 'AD', CARPOOL = 'CARPOOL',
MATCHER = 'MATCHER',
USER = 'USER', USER = 'USER',
} }
export enum ConfigurationType {
BOOLEAN = 'BOOLEAN',
NUMBER = 'NUMBER',
STRING = 'STRING',
}
export type ConfigurationIdentifier = {
domain: ConfigurationDomain;
key: ConfigurationKey;
};
export type ConfigurationKey = string;
export type ConfigurationValue = string;
export type ConfigurationItems = Record<
ConfigurationDomain,
Record<
ConfigurationKey,
{
value: ConfigurationValue;
type: ConfigurationType;
enum?: Array<string>;
}
>
>;

View File

@ -1,12 +1,11 @@
import { DomainEvent, DomainEventProps } from '@mobicoop/ddd-library'; import { DomainEvent, DomainEventProps } from '@mobicoop/ddd-library';
import { ConfigurationIdentifier } from '../configuration.types';
export class ConfigurationDeletedDomainEvent extends DomainEvent { export class ConfigurationDeletedDomainEvent extends DomainEvent {
readonly domain: string; readonly identifier: ConfigurationIdentifier;
readonly key: string;
constructor(props: DomainEventProps<ConfigurationDeletedDomainEvent>) { constructor(props: DomainEventProps<ConfigurationDeletedDomainEvent>) {
super(props); super(props);
this.domain = props.domain; this.identifier = props.identifier;
this.key = props.key;
} }
} }

View File

@ -1,14 +1,19 @@
import { DomainEvent, DomainEventProps } from '@mobicoop/ddd-library'; import { DomainEvent, DomainEventProps } from '@mobicoop/ddd-library';
import {
ConfigurationIdentifier,
ConfigurationType,
ConfigurationValue,
} from '../configuration.types';
export class ConfigurationSetDomainEvent extends DomainEvent { export class ConfigurationSetDomainEvent extends DomainEvent {
readonly domain: string; readonly identifier: ConfigurationIdentifier;
readonly key: string; readonly value: ConfigurationValue;
readonly value: string; readonly type: ConfigurationType;
constructor(props: DomainEventProps<ConfigurationSetDomainEvent>) { constructor(props: DomainEventProps<ConfigurationSetDomainEvent>) {
super(props); super(props);
this.domain = props.domain; this.identifier = props.identifier;
this.key = props.key; this.type = props.type;
this.value = props.value; this.value = props.value;
} }
} }

View File

@ -17,6 +17,7 @@ export type ConfigurationBaseModel = {
domain: string; domain: string;
key: string; key: string;
value: string; value: string;
type: string;
}; };
export type ConfigurationReadModel = ConfigurationBaseModel & { export type ConfigurationReadModel = ConfigurationBaseModel & {

View File

@ -4,4 +4,5 @@ export class ConfigurationResponseDto extends ResponseBase {
domain: string; domain: string;
key: string; key: string;
value: string; value: string;
type: string;
} }

View File

@ -1,10 +1,10 @@
import { Domain } from '@modules/configuration/core/domain/configuration.types'; import { ConfigurationDomain } from '@modules/configuration/core/domain/configuration.types';
import { IsEnum, IsNotEmpty, IsString } from 'class-validator'; import { IsEnum, IsNotEmpty, IsString } from 'class-validator';
export class DeleteConfigurationRequestDto { export class DeleteConfigurationRequestDto {
@IsEnum(Domain) @IsEnum(ConfigurationDomain)
@IsNotEmpty() @IsNotEmpty()
domain: Domain; domain: ConfigurationDomain;
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()

View File

@ -1,10 +1,10 @@
import { Domain } from '@modules/configuration/core/domain/configuration.types'; import { ConfigurationDomain } from '@modules/configuration/core/domain/configuration.types';
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 '@modules/configuration/core/domain/configuration.types'; import { ConfigurationDomain } from '@modules/configuration/core/domain/configuration.types';
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

@ -6,7 +6,7 @@ import { ConfigurationMapper } from '@modules/configuration/configuration.mapper
import { ConfigurationEntity } from '@modules/configuration/core/domain/configuration.entity'; import { ConfigurationEntity } from '@modules/configuration/core/domain/configuration.entity';
import { import {
CreateConfigurationProps, CreateConfigurationProps,
Domain, ConfigurationDomain,
} from '@modules/configuration/core/domain/configuration.types'; } from '@modules/configuration/core/domain/configuration.types';
import { ConfigurationRepository } from '@modules/configuration/infrastructure/configuration.repository'; import { ConfigurationRepository } from '@modules/configuration/infrastructure/configuration.repository';
import { PrismaService } from '@modules/configuration/infrastructure/prisma.service'; import { PrismaService } from '@modules/configuration/infrastructure/prisma.service';
@ -36,7 +36,7 @@ describe('Configuration Repository', () => {
for (let i = 0; i < nbToCreate; i++) { for (let i = 0; i < nbToCreate; i++) {
const configurationToCreate = { const configurationToCreate = {
uuid: getSeed(i, baseUuid.uuid), uuid: getSeed(i, baseUuid.uuid),
domain: Domain.AD, domain: ConfigurationDomain.AD,
key: `key${i}`, key: `key${i}`,
value: `value${i}`, value: `value${i}`,
createdAt: '2023-07-24 13:07:05.000', createdAt: '2023-07-24 13:07:05.000',
@ -98,7 +98,7 @@ describe('Configuration Repository', () => {
it('should return a configuration', async () => { it('should return a configuration', async () => {
await createConfigurations(1); await createConfigurations(1);
const result = await configurationRepository.findOne({ const result = await configurationRepository.findOne({
domain: Domain.AD, domain: ConfigurationDomain.AD,
key: 'key0', key: 'key0',
}); });
expect(result.getProps().value).toBe('value0'); expect(result.getProps().value).toBe('value0');
@ -119,7 +119,7 @@ describe('Configuration Repository', () => {
const beforeCount = await prismaService.configuration.count(); const beforeCount = await prismaService.configuration.count();
const createConfigurationProps: CreateConfigurationProps = { const createConfigurationProps: CreateConfigurationProps = {
domain: Domain.AD, domain: ConfigurationDomain.AD,
key: 'seatsProposed', key: 'seatsProposed',
value: '3', value: '3',
}; };
@ -139,7 +139,7 @@ describe('Configuration Repository', () => {
await createConfigurations(1); await createConfigurations(1);
const configurationToUpdate: ConfigurationEntity = const configurationToUpdate: ConfigurationEntity =
await configurationRepository.findOne({ await configurationRepository.findOne({
domain: Domain.AD, domain: ConfigurationDomain.AD,
key: 'key0', key: 'key0',
}); });
configurationToUpdate.update({ value: 'newValue' }); configurationToUpdate.update({ value: 'newValue' });
@ -149,7 +149,7 @@ describe('Configuration Repository', () => {
); );
const result: ConfigurationEntity = await configurationRepository.findOne( const result: ConfigurationEntity = await configurationRepository.findOne(
{ {
domain: Domain.AD, domain: ConfigurationDomain.AD,
key: 'key0', key: 'key0',
}, },
); );
@ -163,7 +163,7 @@ describe('Configuration Repository', () => {
const beforeCount = await prismaService.configuration.count(); const beforeCount = await prismaService.configuration.count();
const configurationToDelete: ConfigurationEntity = const configurationToDelete: ConfigurationEntity =
await configurationRepository.findOne({ await configurationRepository.findOne({
domain: Domain.AD, domain: ConfigurationDomain.AD,
key: 'key4', key: 'key4',
}); });
await configurationRepository.delete(configurationToDelete); await configurationRepository.delete(configurationToDelete);

View File

@ -1,6 +1,9 @@
import { ConfigurationMapper } from '@modules/configuration/configuration.mapper'; import { ConfigurationMapper } from '@modules/configuration/configuration.mapper';
import { ConfigurationEntity } from '@modules/configuration/core/domain/configuration.entity'; import { ConfigurationEntity } from '@modules/configuration/core/domain/configuration.entity';
import { Domain } from '@modules/configuration/core/domain/configuration.types'; import {
ConfigurationDomain,
ConfigurationType,
} from '@modules/configuration/core/domain/configuration.types';
import { import {
ConfigurationReadModel, ConfigurationReadModel,
ConfigurationWriteModel, ConfigurationWriteModel,
@ -12,9 +15,12 @@ const now = new Date('2023-06-21 06:00:00');
const configurationEntity: ConfigurationEntity = new ConfigurationEntity({ const configurationEntity: ConfigurationEntity = new ConfigurationEntity({
id: 'c160cf8c-f057-4962-841f-3ad68346df44', id: 'c160cf8c-f057-4962-841f-3ad68346df44',
props: { props: {
domain: Domain.AD, identifier: {
key: 'seatsProposed', domain: ConfigurationDomain.CARPOOL,
key: 'seatsProposed',
},
value: '3', value: '3',
type: ConfigurationType.NUMBER,
}, },
createdAt: now, createdAt: now,
updatedAt: now, updatedAt: now,
@ -24,6 +30,7 @@ const configurationReadModel: ConfigurationReadModel = {
domain: 'AD', domain: 'AD',
key: 'seatsProposed', key: 'seatsProposed',
value: '4', value: '4',
type: 'NUMBER',
createdAt: now, createdAt: now,
updatedAt: now, updatedAt: now,
}; };

View File

@ -1,16 +1,20 @@
import { ConfigurationEntity } from '@modules/configuration/core/domain/configuration.entity'; import { ConfigurationEntity } from '@modules/configuration/core/domain/configuration.entity';
import { import {
CreateConfigurationProps, CreateConfigurationProps,
Domain, ConfigurationDomain,
UpdateConfigurationProps, UpdateConfigurationProps,
ConfigurationType,
} from '@modules/configuration/core/domain/configuration.types'; } from '@modules/configuration/core/domain/configuration.types';
import { ConfigurationDeletedDomainEvent } from '@modules/configuration/core/domain/events/configuration-deleted.domain-event'; import { ConfigurationDeletedDomainEvent } from '@modules/configuration/core/domain/events/configuration-deleted.domain-event';
import { ConfigurationSetDomainEvent } from '@modules/configuration/core/domain/events/configuration-set.domain-event'; import { ConfigurationSetDomainEvent } from '@modules/configuration/core/domain/events/configuration-set.domain-event';
const createConfigurationProps: CreateConfigurationProps = { const createConfigurationProps: CreateConfigurationProps = {
domain: Domain.AD, identifier: {
key: 'seatsProposed', domain: ConfigurationDomain.CARPOOL,
key: 'seatsProposed',
},
value: '3', value: '3',
type: ConfigurationType.NUMBER,
}; };
const updateConfigurationProps: UpdateConfigurationProps = { const updateConfigurationProps: UpdateConfigurationProps = {

View File

@ -1,12 +1,12 @@
import { CONFIGURATION_REPOSITORY } from '@modules/configuration/configuration.di-tokens'; import { CONFIGURATION_REPOSITORY } from '@modules/configuration/configuration.di-tokens';
import { DeleteConfigurationCommand } from '@modules/configuration/core/application/commands/delete-configuration/delete-configuration.command'; import { DeleteConfigurationCommand } from '@modules/configuration/core/application/commands/delete-configuration/delete-configuration.command';
import { DeleteConfigurationService } from '@modules/configuration/core/application/commands/delete-configuration/delete-configuration.service'; import { DeleteConfigurationService } from '@modules/configuration/core/application/commands/delete-configuration/delete-configuration.service';
import { Domain } from '@modules/configuration/core/domain/configuration.types'; import { ConfigurationDomain } from '@modules/configuration/core/domain/configuration.types';
import { DeleteConfigurationRequestDto } from '@modules/configuration/interface/grpc-controllers/dtos/delete-configuration.request.dto'; import { DeleteConfigurationRequestDto } from '@modules/configuration/interface/grpc-controllers/dtos/delete-configuration.request.dto';
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
const deleteConfigurationRequest: DeleteConfigurationRequestDto = { const deleteConfigurationRequest: DeleteConfigurationRequestDto = {
domain: Domain.AD, domain: ConfigurationDomain.CARPOOL,
key: 'seatsProposed', key: 'seatsProposed',
}; };

View File

@ -1,6 +1,9 @@
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { ConfigurationEntity } from '@modules/configuration/core/domain/configuration.entity'; import { ConfigurationEntity } from '@modules/configuration/core/domain/configuration.entity';
import { Domain } from '@modules/configuration/core/domain/configuration.types'; import {
ConfigurationDomain,
ConfigurationType,
} from '@modules/configuration/core/domain/configuration.types';
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';
@ -9,9 +12,12 @@ const now = new Date('2023-06-21 06:00:00');
const configuration: ConfigurationEntity = new ConfigurationEntity({ const configuration: ConfigurationEntity = new ConfigurationEntity({
id: 'c160cf8c-f057-4962-841f-3ad68346df44', id: 'c160cf8c-f057-4962-841f-3ad68346df44',
props: { props: {
domain: Domain.AD, identifier: {
key: 'seatsProposed', domain: ConfigurationDomain.CARPOOL,
key: 'seatsProposed',
},
value: '3', value: '3',
type: ConfigurationType.NUMBER,
}, },
createdAt: now, createdAt: now,
updatedAt: now, updatedAt: now,
@ -47,7 +53,7 @@ describe('Get Configuration Query Handler', () => {
describe('execution', () => { describe('execution', () => {
it('should return a configuration item', async () => { it('should return a configuration item', async () => {
const getConfigurationQuery = new GetConfigurationQuery( const getConfigurationQuery = new GetConfigurationQuery(
Domain.AD, ConfigurationDomain.CARPOOL,
'seatsProposed', 'seatsProposed',
); );
const configuration: ConfigurationEntity = const configuration: ConfigurationEntity =

View File

@ -1,5 +1,8 @@
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { Domain } from '@modules/configuration/core/domain/configuration.types'; import {
ConfigurationDomain,
ConfigurationType,
} from '@modules/configuration/core/domain/configuration.types';
import { import {
CONFIGURATION_MESSAGE_PUBLISHER, CONFIGURATION_MESSAGE_PUBLISHER,
CONFIGURATION_REPOSITORY, CONFIGURATION_REPOSITORY,
@ -17,9 +20,12 @@ const configurationEntities = [
new ConfigurationEntity({ new ConfigurationEntity({
id: '047a6ecf-23d4-4d3e-877c-3225d560a8da', id: '047a6ecf-23d4-4d3e-877c-3225d560a8da',
props: { props: {
domain: Domain.AD, identifier: {
key: 'seatsProposed', domain: ConfigurationDomain.CARPOOL,
key: 'seatsProposed',
},
value: '3', value: '3',
type: ConfigurationType.NUMBER,
}, },
createdAt: new Date('2023-10-23T07:00:00Z'), createdAt: new Date('2023-10-23T07:00:00Z'),
updatedAt: new Date('2023-10-23T07:00:00Z'), updatedAt: new Date('2023-10-23T07:00:00Z'),
@ -27,9 +33,12 @@ const configurationEntities = [
new ConfigurationEntity({ new ConfigurationEntity({
id: '047a6ecf-23d4-4d3e-877c-3225d560a8db', id: '047a6ecf-23d4-4d3e-877c-3225d560a8db',
props: { props: {
domain: Domain.AD, identifier: {
key: 'seatsRequested', domain: ConfigurationDomain.CARPOOL,
key: 'seatsRequested',
},
value: '1', value: '1',
type: ConfigurationType.NUMBER,
}, },
createdAt: new Date('2023-10-23T07:00:00Z'), createdAt: new Date('2023-10-23T07:00:00Z'),
updatedAt: new Date('2023-10-23T07:00:00Z'), updatedAt: new Date('2023-10-23T07:00:00Z'),
@ -40,14 +49,16 @@ const mockConfigurationMapper = {
toResponse: jest toResponse: jest
.fn() .fn()
.mockImplementationOnce(() => ({ .mockImplementationOnce(() => ({
domain: Domain.AD, domain: ConfigurationDomain.CARPOOL,
key: 'seatsProposed', key: 'seatsProposed',
value: '3', value: '3',
type: ConfigurationType.NUMBER,
})) }))
.mockImplementationOnce(() => ({ .mockImplementationOnce(() => ({
domain: Domain.AD, domain: ConfigurationDomain.CARPOOL,
key: 'seatsRequested', key: 'seatsRequested',
value: '1', value: '1',
type: ConfigurationType.NUMBER,
})), })),
}; };
@ -93,7 +104,7 @@ describe('Propagate Configurations Service', () => {
expect(mockMessagePublisher.publish).toHaveBeenCalledTimes(1); expect(mockMessagePublisher.publish).toHaveBeenCalledTimes(1);
expect(mockMessagePublisher.publish).toHaveBeenCalledWith( expect(mockMessagePublisher.publish).toHaveBeenCalledWith(
CONFIGURATION_PROPAGATED_ROUTING_KEY, CONFIGURATION_PROPAGATED_ROUTING_KEY,
'[{"domain":"AD","key":"seatsProposed","value":"3"},{"domain":"AD","key":"seatsRequested","value":"1"}]', '[{"domain":"CARPOOL","key":"seatsProposed","value":"3","type":"NUMBER"},{"domain":"CARPOOL","key":"seatsRequested","value":"1","type":"NUMBER"}]',
); );
}); });
}); });

View File

@ -1,6 +1,6 @@
import { CONFIGURATION_MESSAGE_PUBLISHER } from '@modules/configuration/configuration.di-tokens'; import { CONFIGURATION_MESSAGE_PUBLISHER } from '@modules/configuration/configuration.di-tokens';
import { PublishMessageWhenConfigurationIsDeletedDomainEventHandler } from '@modules/configuration/core/application/event-handlers/publish-message-when-configuration-is-deleted.domain-event-handler'; import { PublishMessageWhenConfigurationIsDeletedDomainEventHandler } from '@modules/configuration/core/application/event-handlers/publish-message-when-configuration-is-deleted.domain-event-handler';
import { Domain } from '@modules/configuration/core/domain/configuration.types'; import { ConfigurationDomain } from '@modules/configuration/core/domain/configuration.types';
import { ConfigurationDeletedDomainEvent } from '@modules/configuration/core/domain/events/configuration-deleted.domain-event'; import { ConfigurationDeletedDomainEvent } from '@modules/configuration/core/domain/events/configuration-deleted.domain-event';
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { CONFIGURATION_DELETED_ROUTING_KEY } from '@src/app.constants'; import { CONFIGURATION_DELETED_ROUTING_KEY } from '@src/app.constants';
@ -33,8 +33,10 @@ describe('Publish message when configuration is deleted domain event handler', (
jest.spyOn(mockMessagePublisher, 'publish'); jest.spyOn(mockMessagePublisher, 'publish');
const configurationDeletedDomainEvent: ConfigurationDeletedDomainEvent = { const configurationDeletedDomainEvent: ConfigurationDeletedDomainEvent = {
id: 'some-domain-event-id', id: 'some-domain-event-id',
domain: Domain.AD, identifier: {
key: 'seatsProposed', domain: ConfigurationDomain.CARPOOL,
key: 'seatsProposed',
},
aggregateId: 'some-aggregate-id', aggregateId: 'some-aggregate-id',
metadata: { metadata: {
timestamp: new Date('2023-06-28T05:00:00Z').getTime(), timestamp: new Date('2023-06-28T05:00:00Z').getTime(),
@ -50,7 +52,7 @@ describe('Publish message when configuration is deleted domain event handler', (
expect(mockMessagePublisher.publish).toHaveBeenCalledTimes(1); expect(mockMessagePublisher.publish).toHaveBeenCalledTimes(1);
expect(mockMessagePublisher.publish).toHaveBeenCalledWith( expect(mockMessagePublisher.publish).toHaveBeenCalledWith(
CONFIGURATION_DELETED_ROUTING_KEY, CONFIGURATION_DELETED_ROUTING_KEY,
'{"id":"some-aggregate-id","metadata":{"correlationId":"some-correlation-id","timestamp":1687928400000},"domain":"AD","key":"seatsProposed"}', '{"id":"some-aggregate-id","metadata":{"correlationId":"some-correlation-id","timestamp":1687928400000},"domain":"CARPOOL","key":"seatsProposed"}',
); );
}); });
}); });

View File

@ -1,6 +1,9 @@
import { CONFIGURATION_MESSAGE_PUBLISHER } from '@modules/configuration/configuration.di-tokens'; import { CONFIGURATION_MESSAGE_PUBLISHER } from '@modules/configuration/configuration.di-tokens';
import { PublishMessageWhenConfigurationIsSetDomainEventHandler } from '@modules/configuration/core/application/event-handlers/publish-message-when-configuration-is-set.domain-event-handler'; import { PublishMessageWhenConfigurationIsSetDomainEventHandler } from '@modules/configuration/core/application/event-handlers/publish-message-when-configuration-is-set.domain-event-handler';
import { Domain } from '@modules/configuration/core/domain/configuration.types'; import {
ConfigurationDomain,
ConfigurationType,
} from '@modules/configuration/core/domain/configuration.types';
import { ConfigurationSetDomainEvent } from '@modules/configuration/core/domain/events/configuration-set.domain-event'; import { ConfigurationSetDomainEvent } from '@modules/configuration/core/domain/events/configuration-set.domain-event';
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { CONFIGURATION_SET_ROUTING_KEY } from '@src/app.constants'; import { CONFIGURATION_SET_ROUTING_KEY } from '@src/app.constants';
@ -34,8 +37,11 @@ describe('Publish message when configuration is set domain event handler', () =>
const configurationSetDomainEvent: ConfigurationSetDomainEvent = { const configurationSetDomainEvent: ConfigurationSetDomainEvent = {
id: 'some-domain-event-id', id: 'some-domain-event-id',
aggregateId: 'some-aggregate-id', aggregateId: 'some-aggregate-id',
domain: Domain.AD, identifier: {
key: 'seatsProposed', domain: ConfigurationDomain.CARPOOL,
key: 'seatsProposed',
},
type: ConfigurationType.NUMBER,
value: '3', value: '3',
metadata: { metadata: {
timestamp: new Date('2023-06-28T05:00:00Z').getTime(), timestamp: new Date('2023-06-28T05:00:00Z').getTime(),
@ -51,7 +57,7 @@ describe('Publish message when configuration is set domain event handler', () =>
expect(mockMessagePublisher.publish).toHaveBeenCalledTimes(1); expect(mockMessagePublisher.publish).toHaveBeenCalledTimes(1);
expect(mockMessagePublisher.publish).toHaveBeenCalledWith( expect(mockMessagePublisher.publish).toHaveBeenCalledWith(
CONFIGURATION_SET_ROUTING_KEY, CONFIGURATION_SET_ROUTING_KEY,
'{"id":"some-aggregate-id","metadata":{"correlationId":"some-correlation-id","timestamp":1687928400000},"domain":"AD","key":"seatsProposed","value":"3"}', '{"id":"some-aggregate-id","metadata":{"correlationId":"some-correlation-id","timestamp":1687928400000},"domain":"CARPOOL","key":"seatsProposed","value":"3"}',
); );
}); });
}); });

View File

@ -1,14 +1,17 @@
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { AggregateID, NotFoundException } from '@mobicoop/ddd-library'; import { AggregateID, NotFoundException } 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 { Domain } from '@modules/configuration/core/domain/configuration.types'; import {
ConfigurationDomain,
ConfigurationType,
} from '@modules/configuration/core/domain/configuration.types';
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 { ConfigurationEntity } from '@modules/configuration/core/domain/configuration.entity'; import { ConfigurationEntity } from '@modules/configuration/core/domain/configuration.entity';
const setConfigurationRequest: SetConfigurationRequestDto = { const setConfigurationRequest: SetConfigurationRequestDto = {
domain: Domain.AD, domain: ConfigurationDomain.CARPOOL,
key: 'seatsProposed', key: 'seatsProposed',
value: '3', value: '3',
}; };
@ -16,8 +19,11 @@ const setConfigurationRequest: SetConfigurationRequestDto = {
const existingConfigurationEntity = new ConfigurationEntity({ const existingConfigurationEntity = new ConfigurationEntity({
id: '047a6ecf-23d4-4d3e-877c-3225d560a8da', id: '047a6ecf-23d4-4d3e-877c-3225d560a8da',
props: { props: {
domain: Domain.AD, identifier: {
key: 'seatsProposed', domain: ConfigurationDomain.CARPOOL,
key: 'seatsProposed',
},
type: ConfigurationType.NUMBER,
value: '2', value: '2',
}, },
createdAt: new Date('2023-10-23T07:00:00Z'), createdAt: new Date('2023-10-23T07:00:00Z'),

View File

@ -3,7 +3,7 @@ import {
NotFoundException, NotFoundException,
} from '@mobicoop/ddd-library'; } from '@mobicoop/ddd-library';
import { RpcExceptionCode } from '@mobicoop/ddd-library'; import { RpcExceptionCode } from '@mobicoop/ddd-library';
import { Domain } from '@modules/configuration/core/domain/configuration.types'; import { ConfigurationDomain } from '@modules/configuration/core/domain/configuration.types';
import { DeleteConfigurationGrpcController } from '@modules/configuration/interface/grpc-controllers/delete-configuration.grpc.controller'; import { DeleteConfigurationGrpcController } from '@modules/configuration/interface/grpc-controllers/delete-configuration.grpc.controller';
import { DeleteConfigurationRequestDto } from '@modules/configuration/interface/grpc-controllers/dtos/delete-configuration.request.dto'; import { DeleteConfigurationRequestDto } from '@modules/configuration/interface/grpc-controllers/dtos/delete-configuration.request.dto';
import { CommandBus } from '@nestjs/cqrs'; import { CommandBus } from '@nestjs/cqrs';
@ -11,7 +11,7 @@ import { RpcException } from '@nestjs/microservices';
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
const deleteConfigurationRequest: DeleteConfigurationRequestDto = { const deleteConfigurationRequest: DeleteConfigurationRequestDto = {
domain: Domain.AD, domain: ConfigurationDomain.CARPOOL,
key: 'seatsProposed', key: 'seatsProposed',
}; };

View File

@ -1,7 +1,10 @@
import { NotFoundException } from '@mobicoop/ddd-library'; import { NotFoundException } from '@mobicoop/ddd-library';
import { RpcExceptionCode } from '@mobicoop/ddd-library'; import { RpcExceptionCode } from '@mobicoop/ddd-library';
import { ConfigurationMapper } from '@modules/configuration/configuration.mapper'; import { ConfigurationMapper } from '@modules/configuration/configuration.mapper';
import { Domain } from '@modules/configuration/core/domain/configuration.types'; import {
ConfigurationDomain,
ConfigurationType,
} from '@modules/configuration/core/domain/configuration.types';
import { GetConfigurationGrpcController } from '@modules/configuration/interface/grpc-controllers/get-configuration.grpc.controller'; import { GetConfigurationGrpcController } from '@modules/configuration/interface/grpc-controllers/get-configuration.grpc.controller';
import { QueryBus } from '@nestjs/cqrs'; import { QueryBus } from '@nestjs/cqrs';
import { RpcException } from '@nestjs/microservices'; import { RpcException } from '@nestjs/microservices';
@ -21,9 +24,10 @@ const mockQueryBus = {
const mockConfigurationMapper = { const mockConfigurationMapper = {
toResponse: jest.fn().mockImplementationOnce(() => ({ toResponse: jest.fn().mockImplementationOnce(() => ({
domain: Domain.AD, domain: ConfigurationDomain.CARPOOL,
key: 'seatsProposed', key: 'seatsProposed',
value: '3', value: '3',
type: ConfigurationType.NUMBER,
})), })),
}; };
@ -62,7 +66,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.AD, domain: ConfigurationDomain.CARPOOL,
key: 'seatsProposed', key: 'seatsProposed',
}); });
expect(response.value).toBe('3'); expect(response.value).toBe('3');
@ -76,7 +80,7 @@ describe('Get Configuration Grpc Controller', () => {
expect.assertions(4); expect.assertions(4);
try { try {
await getConfigurationGrpcController.get({ await getConfigurationGrpcController.get({
domain: Domain.AD, domain: ConfigurationDomain.CARPOOL,
key: 'price', key: 'price',
}); });
} catch (e: any) { } catch (e: any) {
@ -93,7 +97,7 @@ describe('Get Configuration Grpc Controller', () => {
expect.assertions(4); expect.assertions(4);
try { try {
await getConfigurationGrpcController.get({ await getConfigurationGrpcController.get({
domain: Domain.AD, domain: ConfigurationDomain.CARPOOL,
key: 'someValue', key: 'someValue',
}); });
} catch (e: any) { } catch (e: any) {

View File

@ -1,6 +1,6 @@
import { IdResponse } from '@mobicoop/ddd-library'; import { IdResponse } from '@mobicoop/ddd-library';
import { RpcExceptionCode } from '@mobicoop/ddd-library'; import { RpcExceptionCode } from '@mobicoop/ddd-library';
import { Domain } from '@modules/configuration/core/domain/configuration.types'; import { ConfigurationDomain } from '@modules/configuration/core/domain/configuration.types';
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';
import { CommandBus } from '@nestjs/cqrs'; import { CommandBus } from '@nestjs/cqrs';
@ -8,7 +8,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.AD, domain: ConfigurationDomain.CARPOOL,
key: 'seatsProposed', key: 'seatsProposed',
value: '3', value: '3',
}; };

View File

@ -16,12 +16,10 @@ const imports = [
useFactory: async ( useFactory: async (
configService: ConfigService, configService: ConfigService,
): Promise<MessageBrokerModuleOptions> => ({ ): Promise<MessageBrokerModuleOptions> => ({
uri: configService.get<string>('MESSAGE_BROKER_URI') as string, uri: configService.get<string>('broker.uri') as string,
exchange: { exchange: {
name: configService.get<string>('MESSAGE_BROKER_EXCHANGE') as string, name: configService.get<string>('broker.exchange') as string,
durable: configService.get<boolean>( durable: configService.get<boolean>('broker.durability') as boolean,
'MESSAGE_BROKER_EXCHANGE_DURABILITY',
) as boolean,
}, },
name: SERVICE_NAME, name: SERVICE_NAME,
}), }),