wip
This commit is contained in:
		
							parent
							
								
									81cc4c019e
								
							
						
					
					
						commit
						c530bc55f5
					
				|  | @ -16,8 +16,8 @@ REDIS_HOST=v3-redis | ||||||
| REDIS_PASSWORD=redis | REDIS_PASSWORD=redis | ||||||
| REDIS_PORT=6379 | REDIS_PORT=6379 | ||||||
| 
 | 
 | ||||||
| # DEFAULT CARPOOL DEPARTURE MARGIN (in seconds) | # DEFAULT CARPOOL DEPARTURE TIME MARGIN (in seconds) | ||||||
| DEPARTURE_MARGIN=900 | DEPARTURE_TIME_MARGIN=900 | ||||||
| 
 | 
 | ||||||
| # DEFAULT ROLE | # DEFAULT ROLE | ||||||
| ROLE=passenger | ROLE=passenger | ||||||
|  |  | ||||||
|  | @ -24,7 +24,7 @@ | ||||||
|     "test:integration:ci": "npm run migrate:test:ci && dotenv -e ci/.env.ci -- jest --testPathPattern 'tests/integration/' --runInBand", |     "test:integration:ci": "npm run migrate:test:ci && dotenv -e ci/.env.ci -- jest --testPathPattern 'tests/integration/' --runInBand", | ||||||
|     "test:cov": "jest --testPathPattern 'tests/unit/' --coverage", |     "test:cov": "jest --testPathPattern 'tests/unit/' --coverage", | ||||||
|     "test:e2e": "jest --config ./test/jest-e2e.json", |     "test:e2e": "jest --config ./test/jest-e2e.json", | ||||||
|     "migrate": "docker exec v3-auth-api sh -c 'npx prisma migrate dev'", |     "migrate": "docker exec v3-ad-api sh -c 'npx prisma migrate dev'", | ||||||
|     "migrate:test": "dotenv -e .env.test -- npx prisma migrate deploy", |     "migrate:test": "dotenv -e .env.test -- npx prisma migrate deploy", | ||||||
|     "migrate:test:ci": "dotenv -e ci/.env.ci -- npx prisma migrate deploy", |     "migrate:test:ci": "dotenv -e ci/.env.ci -- npx prisma migrate deploy", | ||||||
|     "migrate:deploy": "npx prisma migrate deploy" |     "migrate:deploy": "npx prisma migrate deploy" | ||||||
|  | @ -97,7 +97,7 @@ | ||||||
|       "main.ts" |       "main.ts" | ||||||
|     ], |     ], | ||||||
|     "rootDir": "src", |     "rootDir": "src", | ||||||
|     "testRegex": ".*\\.spec\\.ts$", |     "testRegex": ".converter.*\\.spec\\.ts$", | ||||||
|     "transform": { |     "transform": { | ||||||
|       "^.+\\.(t|j)s$": "ts-jest" |       "^.+\\.(t|j)s$": "ts-jest" | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|  | @ -10,20 +10,6 @@ CREATE TABLE "ad" ( | ||||||
|     "frequency" "Frequency" NOT NULL, |     "frequency" "Frequency" NOT NULL, | ||||||
|     "fromDate" DATE NOT NULL, |     "fromDate" DATE NOT NULL, | ||||||
|     "toDate" DATE NOT NULL, |     "toDate" DATE NOT NULL, | ||||||
|     "monTime" TIMESTAMPTZ, |  | ||||||
|     "tueTime" TIMESTAMPTZ, |  | ||||||
|     "wedTime" TIMESTAMPTZ, |  | ||||||
|     "thuTime" TIMESTAMPTZ, |  | ||||||
|     "friTime" TIMESTAMPTZ, |  | ||||||
|     "satTime" TIMESTAMPTZ, |  | ||||||
|     "sunTime" TIMESTAMPTZ, |  | ||||||
|     "monMargin" INTEGER NOT NULL, |  | ||||||
|     "tueMargin" INTEGER NOT NULL, |  | ||||||
|     "wedMargin" INTEGER NOT NULL, |  | ||||||
|     "thuMargin" INTEGER NOT NULL, |  | ||||||
|     "friMargin" INTEGER NOT NULL, |  | ||||||
|     "satMargin" INTEGER NOT NULL, |  | ||||||
|     "sunMargin" INTEGER NOT NULL, |  | ||||||
|     "seatsProposed" SMALLINT NOT NULL, |     "seatsProposed" SMALLINT NOT NULL, | ||||||
|     "seatsRequested" SMALLINT NOT NULL, |     "seatsRequested" SMALLINT NOT NULL, | ||||||
|     "strict" BOOLEAN NOT NULL, |     "strict" BOOLEAN NOT NULL, | ||||||
|  | @ -33,6 +19,19 @@ CREATE TABLE "ad" ( | ||||||
|     CONSTRAINT "ad_pkey" PRIMARY KEY ("uuid") |     CONSTRAINT "ad_pkey" PRIMARY KEY ("uuid") | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
|  | -- CreateTable | ||||||
|  | CREATE TABLE "schedule_item" ( | ||||||
|  |     "uuid" UUID NOT NULL, | ||||||
|  |     "adUuid" UUID NOT NULL, | ||||||
|  |     "day" INTEGER NOT NULL, | ||||||
|  |     "time" TIME(4) NOT NULL, | ||||||
|  |     "margin" INTEGER NOT NULL, | ||||||
|  |     "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||||||
|  |     "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||||||
|  | 
 | ||||||
|  |     CONSTRAINT "schedule_item_pkey" PRIMARY KEY ("uuid") | ||||||
|  | ); | ||||||
|  | 
 | ||||||
| -- CreateTable | -- CreateTable | ||||||
| CREATE TABLE "waypoint" ( | CREATE TABLE "waypoint" ( | ||||||
|     "uuid" UUID NOT NULL, |     "uuid" UUID NOT NULL, | ||||||
|  | @ -52,5 +51,8 @@ CREATE TABLE "waypoint" ( | ||||||
|     CONSTRAINT "waypoint_pkey" PRIMARY KEY ("uuid") |     CONSTRAINT "waypoint_pkey" PRIMARY KEY ("uuid") | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
|  | -- AddForeignKey | ||||||
|  | ALTER TABLE "schedule_item" ADD CONSTRAINT "schedule_item_adUuid_fkey" FOREIGN KEY ("adUuid") REFERENCES "ad"("uuid") ON DELETE CASCADE ON UPDATE CASCADE; | ||||||
|  | 
 | ||||||
| -- AddForeignKey | -- AddForeignKey | ||||||
| ALTER TABLE "waypoint" ADD CONSTRAINT "waypoint_adUuid_fkey" FOREIGN KEY ("adUuid") REFERENCES "ad"("uuid") ON DELETE CASCADE ON UPDATE CASCADE; | ALTER TABLE "waypoint" ADD CONSTRAINT "waypoint_adUuid_fkey" FOREIGN KEY ("adUuid") REFERENCES "ad"("uuid") ON DELETE CASCADE ON UPDATE CASCADE; | ||||||
|  | @ -19,20 +19,7 @@ model Ad { | ||||||
|   frequency      Frequency |   frequency      Frequency | ||||||
|   fromDate       DateTime       @db.Date |   fromDate       DateTime       @db.Date | ||||||
|   toDate         DateTime       @db.Date |   toDate         DateTime       @db.Date | ||||||
|   monTime        DateTime?  @db.Timestamptz() |   schedule       ScheduleItem[] | ||||||
|   tueTime        DateTime?  @db.Timestamptz() |  | ||||||
|   wedTime        DateTime?  @db.Timestamptz() |  | ||||||
|   thuTime        DateTime?  @db.Timestamptz() |  | ||||||
|   friTime        DateTime?  @db.Timestamptz() |  | ||||||
|   satTime        DateTime?  @db.Timestamptz() |  | ||||||
|   sunTime        DateTime?  @db.Timestamptz() |  | ||||||
|   monMargin      Int |  | ||||||
|   tueMargin      Int |  | ||||||
|   wedMargin      Int |  | ||||||
|   thuMargin      Int |  | ||||||
|   friMargin      Int |  | ||||||
|   satMargin      Int |  | ||||||
|   sunMargin      Int |  | ||||||
|   seatsProposed  Int            @db.SmallInt |   seatsProposed  Int            @db.SmallInt | ||||||
|   seatsRequested Int            @db.SmallInt |   seatsRequested Int            @db.SmallInt | ||||||
|   strict         Boolean |   strict         Boolean | ||||||
|  | @ -43,6 +30,19 @@ model Ad { | ||||||
|   @@map("ad") |   @@map("ad") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | model ScheduleItem { | ||||||
|  |   uuid      String   @id @default(uuid()) @db.Uuid | ||||||
|  |   adUuid    String   @db.Uuid | ||||||
|  |   day       Int | ||||||
|  |   time      DateTime @db.Time(4) | ||||||
|  |   margin    Int | ||||||
|  |   createdAt DateTime @default(now()) | ||||||
|  |   updatedAt DateTime @default(now()) @updatedAt | ||||||
|  |   Ad        Ad       @relation(fields: [adUuid], references: [uuid], onDelete: Cascade) | ||||||
|  | 
 | ||||||
|  |   @@map("schedule_item") | ||||||
|  | } | ||||||
|  | 
 | ||||||
| model Waypoint { | model Waypoint { | ||||||
|   uuid        String   @id @default(uuid()) @db.Uuid |   uuid        String   @id @default(uuid()) @db.Uuid | ||||||
|   adUuid      String   @db.Uuid |   adUuid      String   @db.Uuid | ||||||
|  |  | ||||||
|  | @ -17,7 +17,7 @@ async function bootstrap() { | ||||||
|         join(__dirname, 'health.proto'), |         join(__dirname, 'health.proto'), | ||||||
|       ], |       ], | ||||||
|       url: `${process.env.SERVICE_URL}:${process.env.SERVICE_PORT}`, |       url: `${process.env.SERVICE_URL}:${process.env.SERVICE_PORT}`, | ||||||
|       loader: { keepCase: true }, |       loader: { keepCase: true, enums: String }, | ||||||
|     }, |     }, | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,24 +1,17 @@ | ||||||
| import { Mapper } from '@mobicoop/ddd-library'; | import { Mapper } from '@mobicoop/ddd-library'; | ||||||
| import { AdResponseDto } from './interface/dtos/ad.response.dto'; | import { AdResponseDto } from './interface/dtos/ad.response.dto'; | ||||||
| import { Inject, Injectable } from '@nestjs/common'; | import { Injectable } from '@nestjs/common'; | ||||||
| import { AdEntity } from './core/domain/ad.entity'; | import { AdEntity } from './core/domain/ad.entity'; | ||||||
| import { | import { | ||||||
|   AdWriteModel, |   AdWriteModel, | ||||||
|   AdReadModel, |   AdReadModel, | ||||||
|   WaypointModel, |   WaypointModel, | ||||||
|  |   ScheduleItemModel, | ||||||
| } from './infrastructure/ad.repository'; | } from './infrastructure/ad.repository'; | ||||||
| import { Frequency } from './core/domain/ad.types'; | import { Frequency } from './core/domain/ad.types'; | ||||||
| import { WaypointProps } from './core/domain/value-objects/waypoint.value-object'; | import { WaypointProps } from './core/domain/value-objects/waypoint.value-object'; | ||||||
| import { v4 } from 'uuid'; | import { v4 } from 'uuid'; | ||||||
| import { | import { ScheduleItemProps } from './core/domain/value-objects/schedule-item.value-object'; | ||||||
|   PARAMS_PROVIDER, |  | ||||||
|   TIMEZONE_FINDER, |  | ||||||
|   TIME_CONVERTER, |  | ||||||
| } from './ad.di-tokens'; |  | ||||||
| import { TimezoneFinderPort } from './core/application/ports/timezone-finder.port'; |  | ||||||
| import { DefaultParamsProviderPort } from './core/application/ports/default-params-provider.port'; |  | ||||||
| import { DefaultParams } from './core/application/ports/default-params.type'; |  | ||||||
| import { TimeConverterPort } from './core/application/ports/time-converter.port'; |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Mapper constructs objects that are used in different layers: |  * Mapper constructs objects that are used in different layers: | ||||||
|  | @ -31,27 +24,8 @@ import { TimeConverterPort } from './core/application/ports/time-converter.port' | ||||||
| export class AdMapper | export class AdMapper | ||||||
|   implements Mapper<AdEntity, AdReadModel, AdWriteModel, AdResponseDto> |   implements Mapper<AdEntity, AdReadModel, AdWriteModel, AdResponseDto> | ||||||
| { | { | ||||||
|   private readonly _defaultParams: DefaultParams; |  | ||||||
| 
 |  | ||||||
|   constructor( |  | ||||||
|     @Inject(PARAMS_PROVIDER) |  | ||||||
|     private readonly defaultParamsProvider: DefaultParamsProviderPort, |  | ||||||
|     @Inject(TIMEZONE_FINDER) |  | ||||||
|     private readonly timezoneFinder: TimezoneFinderPort, |  | ||||||
|     @Inject(TIME_CONVERTER) |  | ||||||
|     private readonly timeConverter: TimeConverterPort, |  | ||||||
|   ) { |  | ||||||
|     this._defaultParams = defaultParamsProvider.getParams(); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   toPersistence = (entity: AdEntity): AdWriteModel => { |   toPersistence = (entity: AdEntity): AdWriteModel => { | ||||||
|     const copy = entity.getProps(); |     const copy = entity.getProps(); | ||||||
|     const { lon, lat } = copy.waypoints[0].address.coordinates; |  | ||||||
|     const timezone = this.timezoneFinder.timezones( |  | ||||||
|       lon, |  | ||||||
|       lat, |  | ||||||
|       this._defaultParams.DEFAULT_TIMEZONE, |  | ||||||
|     )[0]; |  | ||||||
|     const now = new Date(); |     const now = new Date(); | ||||||
|     const record: AdWriteModel = { |     const record: AdWriteModel = { | ||||||
|       uuid: copy.id, |       uuid: copy.id, | ||||||
|  | @ -61,62 +35,22 @@ export class AdMapper | ||||||
|       frequency: copy.frequency, |       frequency: copy.frequency, | ||||||
|       fromDate: new Date(copy.fromDate), |       fromDate: new Date(copy.fromDate), | ||||||
|       toDate: new Date(copy.toDate), |       toDate: new Date(copy.toDate), | ||||||
|       monTime: copy.schedule.mon |       schedule: { | ||||||
|         ? this.timeConverter.localDateTimeToUtc( |         create: copy.schedule.map((scheduleItem: ScheduleItemProps) => ({ | ||||||
|             copy.fromDate, |           uuid: v4(), | ||||||
|             copy.schedule.mon, |           day: scheduleItem.day, | ||||||
|             timezone, |           time: new Date( | ||||||
|           ) |             1970, | ||||||
|         : undefined, |             0, | ||||||
|       tueTime: copy.schedule.tue |             1, | ||||||
|         ? this.timeConverter.localDateTimeToUtc( |             parseInt(scheduleItem.time.split(':')[0]), | ||||||
|             copy.fromDate, |             parseInt(scheduleItem.time.split(':')[1]), | ||||||
|             copy.schedule.tue, |           ), | ||||||
|             timezone, |           margin: scheduleItem.margin, | ||||||
|           ) |           createdAt: now, | ||||||
|         : undefined, |           updatedAt: now, | ||||||
|       wedTime: copy.schedule.wed |         })), | ||||||
|         ? this.timeConverter.localDateTimeToUtc( |       }, | ||||||
|             copy.fromDate, |  | ||||||
|             copy.schedule.wed, |  | ||||||
|             timezone, |  | ||||||
|           ) |  | ||||||
|         : undefined, |  | ||||||
|       thuTime: copy.schedule.thu |  | ||||||
|         ? this.timeConverter.localDateTimeToUtc( |  | ||||||
|             copy.fromDate, |  | ||||||
|             copy.schedule.thu, |  | ||||||
|             timezone, |  | ||||||
|           ) |  | ||||||
|         : undefined, |  | ||||||
|       friTime: copy.schedule.fri |  | ||||||
|         ? this.timeConverter.localDateTimeToUtc( |  | ||||||
|             copy.fromDate, |  | ||||||
|             copy.schedule.fri, |  | ||||||
|             timezone, |  | ||||||
|           ) |  | ||||||
|         : undefined, |  | ||||||
|       satTime: copy.schedule.sat |  | ||||||
|         ? this.timeConverter.localDateTimeToUtc( |  | ||||||
|             copy.fromDate, |  | ||||||
|             copy.schedule.sat, |  | ||||||
|             timezone, |  | ||||||
|           ) |  | ||||||
|         : undefined, |  | ||||||
|       sunTime: copy.schedule.sun |  | ||||||
|         ? this.timeConverter.localDateTimeToUtc( |  | ||||||
|             copy.fromDate, |  | ||||||
|             copy.schedule.sun, |  | ||||||
|             timezone, |  | ||||||
|           ) |  | ||||||
|         : undefined, |  | ||||||
|       monMargin: copy.marginDurations.mon, |  | ||||||
|       tueMargin: copy.marginDurations.tue, |  | ||||||
|       wedMargin: copy.marginDurations.wed, |  | ||||||
|       thuMargin: copy.marginDurations.thu, |  | ||||||
|       friMargin: copy.marginDurations.fri, |  | ||||||
|       satMargin: copy.marginDurations.sat, |  | ||||||
|       sunMargin: copy.marginDurations.sun, |  | ||||||
|       seatsProposed: copy.seatsProposed, |       seatsProposed: copy.seatsProposed, | ||||||
|       seatsRequested: copy.seatsRequested, |       seatsRequested: copy.seatsRequested, | ||||||
|       strict: copy.strict, |       strict: copy.strict, | ||||||
|  | @ -143,11 +77,6 @@ export class AdMapper | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   toDomain = (record: AdReadModel): AdEntity => { |   toDomain = (record: AdReadModel): AdEntity => { | ||||||
|     const timezone = this.timezoneFinder.timezones( |  | ||||||
|       record.waypoints[0].lon, |  | ||||||
|       record.waypoints[0].lat, |  | ||||||
|       this._defaultParams.DEFAULT_TIMEZONE, |  | ||||||
|     )[0]; |  | ||||||
|     const entity = new AdEntity({ |     const entity = new AdEntity({ | ||||||
|       id: record.uuid, |       id: record.uuid, | ||||||
|       createdAt: new Date(record.createdAt), |       createdAt: new Date(record.createdAt), | ||||||
|  | @ -159,34 +88,17 @@ export class AdMapper | ||||||
|         frequency: Frequency[record.frequency], |         frequency: Frequency[record.frequency], | ||||||
|         fromDate: record.fromDate.toISOString().split('T')[0], |         fromDate: record.fromDate.toISOString().split('T')[0], | ||||||
|         toDate: record.toDate.toISOString().split('T')[0], |         toDate: record.toDate.toISOString().split('T')[0], | ||||||
|         schedule: { |         schedule: record.schedule.map((scheduleItem: ScheduleItemModel) => ({ | ||||||
|           mon: record.monTime?.toISOString(), |           day: scheduleItem.day, | ||||||
|           tue: record.tueTime?.toISOString(), |           time: `${scheduleItem.time | ||||||
|           wed: record.wedTime |             .getUTCHours() | ||||||
|             ? this.timeConverter.utcDatetimeToLocalTime( |             .toString() | ||||||
|                 record.wedTime.toISOString(), |             .padStart(2, '0')}:${scheduleItem.time | ||||||
|                 timezone, |             .getUTCMinutes() | ||||||
|               ) |             .toString() | ||||||
|             : undefined, |             .padStart(2, '0')}`,
 | ||||||
|           thu: record.thuTime |           margin: scheduleItem.margin, | ||||||
|             ? this.timeConverter.utcDatetimeToLocalTime( |         })), | ||||||
|                 record.thuTime.toISOString(), |  | ||||||
|                 timezone, |  | ||||||
|               ) |  | ||||||
|             : undefined, |  | ||||||
|           fri: record.friTime?.toISOString(), |  | ||||||
|           sat: record.satTime?.toISOString(), |  | ||||||
|           sun: record.sunTime?.toISOString(), |  | ||||||
|         }, |  | ||||||
|         marginDurations: { |  | ||||||
|           mon: record.monMargin, |  | ||||||
|           tue: record.tueMargin, |  | ||||||
|           wed: record.wedMargin, |  | ||||||
|           thu: record.thuMargin, |  | ||||||
|           fri: record.friMargin, |  | ||||||
|           sat: record.satMargin, |  | ||||||
|           sun: record.sunMargin, |  | ||||||
|         }, |  | ||||||
|         seatsProposed: record.seatsProposed, |         seatsProposed: record.seatsProposed, | ||||||
|         seatsRequested: record.seatsRequested, |         seatsRequested: record.seatsRequested, | ||||||
|         strict: record.strict, |         strict: record.strict, | ||||||
|  | @ -219,8 +131,13 @@ export class AdMapper | ||||||
|     response.frequency = props.frequency; |     response.frequency = props.frequency; | ||||||
|     response.fromDate = props.fromDate; |     response.fromDate = props.fromDate; | ||||||
|     response.toDate = props.toDate; |     response.toDate = props.toDate; | ||||||
|     response.schedule = { ...props.schedule }; |     response.schedule = props.schedule.map( | ||||||
|     response.marginDurations = { ...props.marginDurations }; |       (scheduleItem: ScheduleItemProps) => ({ | ||||||
|  |         day: scheduleItem.day, | ||||||
|  |         time: scheduleItem.time, | ||||||
|  |         margin: scheduleItem.margin, | ||||||
|  |       }), | ||||||
|  |     ); | ||||||
|     response.seatsProposed = props.seatsProposed; |     response.seatsProposed = props.seatsProposed; | ||||||
|     response.seatsRequested = props.seatsRequested; |     response.seatsRequested = props.seatsRequested; | ||||||
|     response.waypoints = props.waypoints.map((waypoint: WaypointProps) => ({ |     response.waypoints = props.waypoints.map((waypoint: WaypointProps) => ({ | ||||||
|  | @ -236,12 +153,4 @@ export class AdMapper | ||||||
|     })); |     })); | ||||||
|     return response; |     return response; | ||||||
|   }; |   }; | ||||||
| 
 |  | ||||||
|   /* ^ Data returned to the user is whitelisted to avoid leaks. |  | ||||||
|      If a new property is added, like password or a |  | ||||||
|      credit card number, it won't be returned |  | ||||||
|      unless you specifically allow this. |  | ||||||
|      (avoid blacklisting, which will return everything |  | ||||||
|       but blacklisted items, which can lead to a data leak). |  | ||||||
|   */ |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,5 +1,4 @@ | ||||||
| import { Schedule } from '../../types/schedule'; | import { ScheduleItem } from '../../types/schedule-item'; | ||||||
| import { MarginDurations } from '../../types/margin-durations'; |  | ||||||
| import { Waypoint } from '../../types/waypoint'; | import { Waypoint } from '../../types/waypoint'; | ||||||
| import { Frequency } from '@modules/ad/core/domain/ad.types'; | import { Frequency } from '@modules/ad/core/domain/ad.types'; | ||||||
| import { Command, CommandProps } from '@mobicoop/ddd-library'; | import { Command, CommandProps } from '@mobicoop/ddd-library'; | ||||||
|  | @ -11,8 +10,7 @@ export class CreateAdCommand extends Command { | ||||||
|   readonly frequency?: Frequency; |   readonly frequency?: Frequency; | ||||||
|   readonly fromDate: string; |   readonly fromDate: string; | ||||||
|   readonly toDate: string; |   readonly toDate: string; | ||||||
|   readonly schedule: Schedule; |   readonly schedule: ScheduleItem[]; | ||||||
|   readonly marginDurations?: MarginDurations; |  | ||||||
|   readonly seatsProposed?: number; |   readonly seatsProposed?: number; | ||||||
|   readonly seatsRequested?: number; |   readonly seatsRequested?: number; | ||||||
|   readonly strict?: boolean; |   readonly strict?: boolean; | ||||||
|  | @ -27,7 +25,6 @@ export class CreateAdCommand extends Command { | ||||||
|     this.fromDate = props.fromDate; |     this.fromDate = props.fromDate; | ||||||
|     this.toDate = props.toDate; |     this.toDate = props.toDate; | ||||||
|     this.schedule = props.schedule; |     this.schedule = props.schedule; | ||||||
|     this.marginDurations = props.marginDurations; |  | ||||||
|     this.seatsProposed = props.seatsProposed; |     this.seatsProposed = props.seatsProposed; | ||||||
|     this.seatsRequested = props.seatsRequested; |     this.seatsRequested = props.seatsRequested; | ||||||
|     this.strict = props.strict; |     this.strict = props.strict; | ||||||
|  |  | ||||||
|  | @ -1,7 +1,12 @@ | ||||||
| import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; | import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; | ||||||
| import { CreateAdCommand } from './create-ad.command'; | import { CreateAdCommand } from './create-ad.command'; | ||||||
| import { Inject } from '@nestjs/common'; | import { Inject } from '@nestjs/common'; | ||||||
| import { AD_REPOSITORY, PARAMS_PROVIDER } from '@modules/ad/ad.di-tokens'; | import { | ||||||
|  |   AD_REPOSITORY, | ||||||
|  |   PARAMS_PROVIDER, | ||||||
|  |   TIMEZONE_FINDER, | ||||||
|  |   TIME_CONVERTER, | ||||||
|  | } from '@modules/ad/ad.di-tokens'; | ||||||
| import { AdEntity } from '@modules/ad/core/domain/ad.entity'; | import { AdEntity } from '@modules/ad/core/domain/ad.entity'; | ||||||
| import { Waypoint } from '../../types/waypoint'; | import { Waypoint } from '../../types/waypoint'; | ||||||
| import { DefaultParams } from '../../ports/default-params.type'; | import { DefaultParams } from '../../ports/default-params.type'; | ||||||
|  | @ -9,6 +14,10 @@ import { AdRepositoryPort } from '../../ports/ad.repository.port'; | ||||||
| import { DefaultParamsProviderPort } from '../../ports/default-params-provider.port'; | import { DefaultParamsProviderPort } from '../../ports/default-params-provider.port'; | ||||||
| import { AdAlreadyExistsException } from '@modules/ad/core/domain/ad.errors'; | import { AdAlreadyExistsException } from '@modules/ad/core/domain/ad.errors'; | ||||||
| import { AggregateID, ConflictException } from '@mobicoop/ddd-library'; | import { AggregateID, ConflictException } from '@mobicoop/ddd-library'; | ||||||
|  | import { ScheduleItem } from '../../types/schedule-item'; | ||||||
|  | import { TimeConverterPort } from '../../ports/time-converter.port'; | ||||||
|  | import { TimezoneFinderPort } from '../../ports/timezone-finder.port'; | ||||||
|  | import { Frequency } from '@modules/ad/core/domain/ad.types'; | ||||||
| 
 | 
 | ||||||
| @CommandHandler(CreateAdCommand) | @CommandHandler(CreateAdCommand) | ||||||
| export class CreateAdService implements ICommandHandler { | export class CreateAdService implements ICommandHandler { | ||||||
|  | @ -19,21 +28,55 @@ export class CreateAdService implements ICommandHandler { | ||||||
|     private readonly repository: AdRepositoryPort, |     private readonly repository: AdRepositoryPort, | ||||||
|     @Inject(PARAMS_PROVIDER) |     @Inject(PARAMS_PROVIDER) | ||||||
|     private readonly defaultParamsProvider: DefaultParamsProviderPort, |     private readonly defaultParamsProvider: DefaultParamsProviderPort, | ||||||
|  |     @Inject(TIMEZONE_FINDER) | ||||||
|  |     private readonly timezoneFinder: TimezoneFinderPort, | ||||||
|  |     @Inject(TIME_CONVERTER) | ||||||
|  |     private readonly timeConverter: TimeConverterPort, | ||||||
|   ) { |   ) { | ||||||
|     this._defaultParams = defaultParamsProvider.getParams(); |     this._defaultParams = defaultParamsProvider.getParams(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async execute(command: CreateAdCommand): Promise<AggregateID> { |   async execute(command: CreateAdCommand): Promise<AggregateID> { | ||||||
|  |     const timezone = this.timezoneFinder.timezones( | ||||||
|  |       command.waypoints[0].lon, | ||||||
|  |       command.waypoints[0].lat, | ||||||
|  |       this._defaultParams.DEFAULT_TIMEZONE, | ||||||
|  |     )[0]; | ||||||
|     const ad = AdEntity.create( |     const ad = AdEntity.create( | ||||||
|       { |       { | ||||||
|         userId: command.userId, |         userId: command.userId, | ||||||
|         driver: command.driver, |         driver: command.driver, | ||||||
|         passenger: command.passenger, |         passenger: command.passenger, | ||||||
|         frequency: command.frequency, |         frequency: command.frequency, | ||||||
|         fromDate: command.fromDate, |         fromDate: this.getFromDate( | ||||||
|         toDate: command.toDate, |           command.fromDate, | ||||||
|         schedule: command.schedule, |           command.frequency, | ||||||
|         marginDurations: command.marginDurations, |           command.schedule[0].time, | ||||||
|  |           timezone, | ||||||
|  |         ), | ||||||
|  |         toDate: this.getToDate( | ||||||
|  |           command.fromDate, | ||||||
|  |           command.toDate, | ||||||
|  |           command.frequency, | ||||||
|  |           command.schedule[0].time, | ||||||
|  |           timezone, | ||||||
|  |         ), | ||||||
|  |         schedule: command.schedule.map((scheduleItem: ScheduleItem) => ({ | ||||||
|  |           day: this.getDay( | ||||||
|  |             scheduleItem.day, | ||||||
|  |             command.fromDate, | ||||||
|  |             command.frequency, | ||||||
|  |             scheduleItem.time, | ||||||
|  |             timezone, | ||||||
|  |           ), | ||||||
|  |           time: this.getTime( | ||||||
|  |             command.fromDate, | ||||||
|  |             command.frequency, | ||||||
|  |             scheduleItem.time, | ||||||
|  |             timezone, | ||||||
|  |           ), | ||||||
|  |           margin: scheduleItem.margin, | ||||||
|  |         })), | ||||||
|         seatsProposed: command.seatsProposed, |         seatsProposed: command.seatsProposed, | ||||||
|         seatsRequested: command.seatsRequested, |         seatsRequested: command.seatsRequested, | ||||||
|         strict: command.strict, |         strict: command.strict, | ||||||
|  | @ -56,15 +99,7 @@ export class CreateAdService implements ICommandHandler { | ||||||
|       { |       { | ||||||
|         driver: this._defaultParams.DRIVER, |         driver: this._defaultParams.DRIVER, | ||||||
|         passenger: this._defaultParams.PASSENGER, |         passenger: this._defaultParams.PASSENGER, | ||||||
|         marginDurations: { |         marginDuration: this._defaultParams.DEPARTURE_TIME_MARGIN, | ||||||
|           mon: this._defaultParams.MON_MARGIN, |  | ||||||
|           tue: this._defaultParams.TUE_MARGIN, |  | ||||||
|           wed: this._defaultParams.WED_MARGIN, |  | ||||||
|           thu: this._defaultParams.THU_MARGIN, |  | ||||||
|           fri: this._defaultParams.FRI_MARGIN, |  | ||||||
|           sat: this._defaultParams.SAT_MARGIN, |  | ||||||
|           sun: this._defaultParams.SUN_MARGIN, |  | ||||||
|         }, |  | ||||||
|         strict: this._defaultParams.STRICT, |         strict: this._defaultParams.STRICT, | ||||||
|         seatsProposed: this._defaultParams.SEATS_PROPOSED, |         seatsProposed: this._defaultParams.SEATS_PROPOSED, | ||||||
|         seatsRequested: this._defaultParams.SEATS_REQUESTED, |         seatsRequested: this._defaultParams.SEATS_REQUESTED, | ||||||
|  | @ -81,4 +116,71 @@ export class CreateAdService implements ICommandHandler { | ||||||
|       throw error; |       throw error; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   private getFromDate = ( | ||||||
|  |     fromDate: string, | ||||||
|  |     frequency: Frequency, | ||||||
|  |     time: string, | ||||||
|  |     timezone: string, | ||||||
|  |   ): string => { | ||||||
|  |     if (frequency === Frequency.RECURRENT) return fromDate; | ||||||
|  |     return this.timeConverter | ||||||
|  |       .localStringDateTimeToUtcDate(fromDate, time, timezone) | ||||||
|  |       .toISOString(); | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   private getToDate = ( | ||||||
|  |     fromDate: string, | ||||||
|  |     toDate: string, | ||||||
|  |     frequency: Frequency, | ||||||
|  |     time: string, | ||||||
|  |     timezone: string, | ||||||
|  |   ): string => { | ||||||
|  |     if (frequency === Frequency.RECURRENT) return toDate; | ||||||
|  |     return this.getFromDate(fromDate, frequency, time, timezone); | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   private getDay = ( | ||||||
|  |     day: number, | ||||||
|  |     fromDate: string, | ||||||
|  |     frequency: Frequency, | ||||||
|  |     time: string, | ||||||
|  |     timezone: string, | ||||||
|  |   ): number => { | ||||||
|  |     if (frequency === Frequency.RECURRENT) | ||||||
|  |       return this.getRecurrentDay(day, time, timezone); | ||||||
|  |     return new Date( | ||||||
|  |       this.getFromDate(fromDate, frequency, time, timezone), | ||||||
|  |     ).getDay(); | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   private getTime = ( | ||||||
|  |     fromDate: string, | ||||||
|  |     frequency: Frequency, | ||||||
|  |     time: string, | ||||||
|  |     timezone: string, | ||||||
|  |   ): string => { | ||||||
|  |     if (frequency === Frequency.RECURRENT) | ||||||
|  |       return this.timeConverter.localStringTimeToUtcStringTime(time, timezone); | ||||||
|  |     return new Date( | ||||||
|  |       this.getFromDate(fromDate, frequency, time, timezone), | ||||||
|  |     ).toTimeString(); | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   private getRecurrentDay = ( | ||||||
|  |     day: number, | ||||||
|  |     time: string, | ||||||
|  |     timezone: string, | ||||||
|  |   ): number => { | ||||||
|  |     // continuer ici
 | ||||||
|  |     const baseDate = new Date('1970-01-01T00:00:00Z'); | ||||||
|  |     const hour = parseInt(time.split(':')[0]); | ||||||
|  |     const utcHour = parseInt( | ||||||
|  |       this.timeConverter | ||||||
|  |         .localStringTimeToUtcStringTime(time, timezone) | ||||||
|  |         .split(':')[0], | ||||||
|  |     ); | ||||||
|  |     if (utcHour >= 11 && hour < 13) return day > 0 ? day - 1 : 6; | ||||||
|  |     return day; | ||||||
|  |   }; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,15 +1,9 @@ | ||||||
| export type DefaultParams = { | export type DefaultParams = { | ||||||
|   MON_MARGIN: number; |  | ||||||
|   TUE_MARGIN: number; |  | ||||||
|   WED_MARGIN: number; |  | ||||||
|   THU_MARGIN: number; |  | ||||||
|   FRI_MARGIN: number; |  | ||||||
|   SAT_MARGIN: number; |  | ||||||
|   SUN_MARGIN: number; |  | ||||||
|   DRIVER: boolean; |   DRIVER: boolean; | ||||||
|   SEATS_PROPOSED: number; |  | ||||||
|   PASSENGER: boolean; |   PASSENGER: boolean; | ||||||
|  |   SEATS_PROPOSED: number; | ||||||
|   SEATS_REQUESTED: number; |   SEATS_REQUESTED: number; | ||||||
|  |   DEPARTURE_TIME_MARGIN: number; | ||||||
|   STRICT: boolean; |   STRICT: boolean; | ||||||
|   DEFAULT_TIMEZONE: string; |   DEFAULT_TIMEZONE: string; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
| export interface TimeConverterPort { | export interface TimeConverterPort { | ||||||
|   localDateTimeToUtc( |   localStringTimeToUtcStringTime(time: string, timezone: string): string; | ||||||
|  |   localStringDateTimeToUtcDate( | ||||||
|     date: string, |     date: string, | ||||||
|     time: string, |     time: string, | ||||||
|     timezone: string, |     timezone: string, | ||||||
|  |  | ||||||
|  | @ -1,3 +1,4 @@ | ||||||
| export interface TimezoneFinderPort { | export interface TimezoneFinderPort { | ||||||
|   timezones(lon: number, lat: number, defaultTimezone?: string): string[]; |   timezones(lon: number, lat: number, defaultTimezone?: string): string[]; | ||||||
|  |   offset(timezone: string): number; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,5 @@ | ||||||
|  | export type ScheduleItem = { | ||||||
|  |   day?: number; | ||||||
|  |   time: string; | ||||||
|  |   margin?: number; | ||||||
|  | }; | ||||||
|  | @ -1,9 +0,0 @@ | ||||||
| export type Schedule = { |  | ||||||
|   mon?: string; |  | ||||||
|   tue?: string; |  | ||||||
|   wed?: string; |  | ||||||
|   thu?: string; |  | ||||||
|   fri?: string; |  | ||||||
|   sat?: string; |  | ||||||
|   sun?: string; |  | ||||||
| }; |  | ||||||
|  | @ -2,8 +2,8 @@ import { AggregateRoot, AggregateID } from '@mobicoop/ddd-library'; | ||||||
| import { v4 } from 'uuid'; | import { v4 } from 'uuid'; | ||||||
| import { AdCreatedDomainEvent } from './events/ad-created.domain-events'; | import { AdCreatedDomainEvent } from './events/ad-created.domain-events'; | ||||||
| import { AdProps, CreateAdProps, DefaultAdProps } from './ad.types'; | import { AdProps, CreateAdProps, DefaultAdProps } from './ad.types'; | ||||||
| import { Waypoint } from './value-objects/waypoint.value-object'; | import { ScheduleItemProps } from './value-objects/schedule-item.value-object'; | ||||||
| import { MarginDurationsProps } from './value-objects/margin-durations.value-object'; | import { WaypointProps } from './value-objects/waypoint.value-object'; | ||||||
| 
 | 
 | ||||||
| export class AdEntity extends AggregateRoot<AdProps> { | export class AdEntity extends AggregateRoot<AdProps> { | ||||||
|   protected readonly _id: AggregateID; |   protected readonly _id: AggregateID; | ||||||
|  | @ -15,7 +15,7 @@ export class AdEntity extends AggregateRoot<AdProps> { | ||||||
|     const id = v4(); |     const id = v4(); | ||||||
|     const props: AdProps = { ...create }; |     const props: AdProps = { ...create }; | ||||||
|     const ad = new AdEntity({ id, props }) |     const ad = new AdEntity({ id, props }) | ||||||
|       .setMissingMarginDurations(defaultAdProps.marginDurations) |       .setMissingMarginDurations(defaultAdProps.marginDuration) | ||||||
|       .setMissingStrict(defaultAdProps.strict) |       .setMissingStrict(defaultAdProps.strict) | ||||||
|       .setDefaultDriverAndPassengerParameters({ |       .setDefaultDriverAndPassengerParameters({ | ||||||
|         driver: defaultAdProps.driver, |         driver: defaultAdProps.driver, | ||||||
|  | @ -33,24 +33,15 @@ export class AdEntity extends AggregateRoot<AdProps> { | ||||||
|         frequency: props.frequency, |         frequency: props.frequency, | ||||||
|         fromDate: props.fromDate, |         fromDate: props.fromDate, | ||||||
|         toDate: props.toDate, |         toDate: props.toDate, | ||||||
|         monTime: props.schedule.mon, |         schedule: props.schedule.map((day: ScheduleItemProps) => ({ | ||||||
|         tueTime: props.schedule.tue, |           day: day.day, | ||||||
|         wedTime: props.schedule.wed, |           time: day.time, | ||||||
|         thuTime: props.schedule.thu, |           margin: day.margin, | ||||||
|         friTime: props.schedule.fri, |         })), | ||||||
|         satTime: props.schedule.sat, |  | ||||||
|         sunTime: props.schedule.sun, |  | ||||||
|         monMarginDuration: props.marginDurations.mon, |  | ||||||
|         tueMarginDuration: props.marginDurations.tue, |  | ||||||
|         wedMarginDuration: props.marginDurations.wed, |  | ||||||
|         thuMarginDuration: props.marginDurations.thu, |  | ||||||
|         friMarginDuration: props.marginDurations.fri, |  | ||||||
|         satMarginDuration: props.marginDurations.sat, |  | ||||||
|         sunMarginDuration: props.marginDurations.sun, |  | ||||||
|         seatsProposed: props.seatsProposed, |         seatsProposed: props.seatsProposed, | ||||||
|         seatsRequested: props.seatsRequested, |         seatsRequested: props.seatsRequested, | ||||||
|         strict: props.strict, |         strict: props.strict, | ||||||
|         waypoints: props.waypoints.map((waypoint: Waypoint) => ({ |         waypoints: props.waypoints.map((waypoint: WaypointProps) => ({ | ||||||
|           position: waypoint.position, |           position: waypoint.position, | ||||||
|           name: waypoint.address.name, |           name: waypoint.address.name, | ||||||
|           houseNumber: waypoint.address.houseNumber, |           houseNumber: waypoint.address.houseNumber, | ||||||
|  | @ -67,23 +58,11 @@ export class AdEntity extends AggregateRoot<AdProps> { | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   private setMissingMarginDurations = ( |   private setMissingMarginDurations = ( | ||||||
|     defaultMarginDurations: MarginDurationsProps, |     defaultMarginDuration: number, | ||||||
|   ): AdEntity => { |   ): AdEntity => { | ||||||
|     if (!this.props.marginDurations) this.props.marginDurations = {}; |     this.props.schedule.forEach((day: ScheduleItemProps) => { | ||||||
|     if (!this.props.marginDurations.mon) |       if (day.margin === undefined) day.margin = defaultMarginDuration; | ||||||
|       this.props.marginDurations.mon = defaultMarginDurations.mon; |     }); | ||||||
|     if (!this.props.marginDurations.tue) |  | ||||||
|       this.props.marginDurations.tue = defaultMarginDurations.tue; |  | ||||||
|     if (!this.props.marginDurations.wed) |  | ||||||
|       this.props.marginDurations.wed = defaultMarginDurations.wed; |  | ||||||
|     if (!this.props.marginDurations.thu) |  | ||||||
|       this.props.marginDurations.thu = defaultMarginDurations.thu; |  | ||||||
|     if (!this.props.marginDurations.fri) |  | ||||||
|       this.props.marginDurations.fri = defaultMarginDurations.fri; |  | ||||||
|     if (!this.props.marginDurations.sat) |  | ||||||
|       this.props.marginDurations.sat = defaultMarginDurations.sat; |  | ||||||
|     if (!this.props.marginDurations.sun) |  | ||||||
|       this.props.marginDurations.sun = defaultMarginDurations.sun; |  | ||||||
|     return this; |     return this; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,5 +1,4 @@ | ||||||
| import { MarginDurationsProps } from './value-objects/margin-durations.value-object'; | import { ScheduleItemProps } from './value-objects/schedule-item.value-object'; | ||||||
| import { ScheduleProps } from './value-objects/schedule.value-object'; |  | ||||||
| import { WaypointProps } from './value-objects/waypoint.value-object'; | import { WaypointProps } from './value-objects/waypoint.value-object'; | ||||||
| 
 | 
 | ||||||
| // All properties that an Ad has
 | // All properties that an Ad has
 | ||||||
|  | @ -10,8 +9,7 @@ export interface AdProps { | ||||||
|   frequency: Frequency; |   frequency: Frequency; | ||||||
|   fromDate: string; |   fromDate: string; | ||||||
|   toDate: string; |   toDate: string; | ||||||
|   schedule: ScheduleProps; |   schedule: ScheduleItemProps[]; | ||||||
|   marginDurations: MarginDurationsProps; |  | ||||||
|   seatsProposed: number; |   seatsProposed: number; | ||||||
|   seatsRequested: number; |   seatsRequested: number; | ||||||
|   strict: boolean; |   strict: boolean; | ||||||
|  | @ -26,8 +24,7 @@ export interface CreateAdProps { | ||||||
|   frequency: Frequency; |   frequency: Frequency; | ||||||
|   fromDate: string; |   fromDate: string; | ||||||
|   toDate: string; |   toDate: string; | ||||||
|   schedule: ScheduleProps; |   schedule: ScheduleItemProps[]; | ||||||
|   marginDurations: MarginDurationsProps; |  | ||||||
|   seatsProposed: number; |   seatsProposed: number; | ||||||
|   seatsRequested: number; |   seatsRequested: number; | ||||||
|   strict: boolean; |   strict: boolean; | ||||||
|  | @ -37,7 +34,7 @@ export interface CreateAdProps { | ||||||
| export interface DefaultAdProps { | export interface DefaultAdProps { | ||||||
|   driver: boolean; |   driver: boolean; | ||||||
|   passenger: boolean; |   passenger: boolean; | ||||||
|   marginDurations: MarginDurationsProps; |   marginDuration: number; | ||||||
|   strict: boolean; |   strict: boolean; | ||||||
|   seatsProposed: number; |   seatsProposed: number; | ||||||
|   seatsRequested: number; |   seatsRequested: number; | ||||||
|  |  | ||||||
|  | @ -7,20 +7,7 @@ export class AdCreatedDomainEvent extends DomainEvent { | ||||||
|   readonly frequency: string; |   readonly frequency: string; | ||||||
|   readonly fromDate: string; |   readonly fromDate: string; | ||||||
|   readonly toDate: string; |   readonly toDate: string; | ||||||
|   readonly monTime: string; |   readonly schedule: ScheduleDay[]; | ||||||
|   readonly tueTime: string; |  | ||||||
|   readonly wedTime: string; |  | ||||||
|   readonly thuTime: string; |  | ||||||
|   readonly friTime: string; |  | ||||||
|   readonly satTime: string; |  | ||||||
|   readonly sunTime: string; |  | ||||||
|   readonly monMarginDuration: number; |  | ||||||
|   readonly tueMarginDuration: number; |  | ||||||
|   readonly wedMarginDuration: number; |  | ||||||
|   readonly thuMarginDuration: number; |  | ||||||
|   readonly friMarginDuration: number; |  | ||||||
|   readonly satMarginDuration: number; |  | ||||||
|   readonly sunMarginDuration: number; |  | ||||||
|   readonly seatsProposed: number; |   readonly seatsProposed: number; | ||||||
|   readonly seatsRequested: number; |   readonly seatsRequested: number; | ||||||
|   readonly strict: boolean; |   readonly strict: boolean; | ||||||
|  | @ -34,20 +21,7 @@ export class AdCreatedDomainEvent extends DomainEvent { | ||||||
|     this.frequency = props.frequency; |     this.frequency = props.frequency; | ||||||
|     this.fromDate = props.fromDate; |     this.fromDate = props.fromDate; | ||||||
|     this.toDate = props.toDate; |     this.toDate = props.toDate; | ||||||
|     this.monTime = props.monTime; |     this.schedule = props.schedule; | ||||||
|     this.tueTime = props.tueTime; |  | ||||||
|     this.wedTime = props.wedTime; |  | ||||||
|     this.thuTime = props.thuTime; |  | ||||||
|     this.friTime = props.friTime; |  | ||||||
|     this.satTime = props.satTime; |  | ||||||
|     this.sunTime = props.sunTime; |  | ||||||
|     this.monMarginDuration = props.monMarginDuration; |  | ||||||
|     this.tueMarginDuration = props.tueMarginDuration; |  | ||||||
|     this.wedMarginDuration = props.wedMarginDuration; |  | ||||||
|     this.thuMarginDuration = props.thuMarginDuration; |  | ||||||
|     this.friMarginDuration = props.friMarginDuration; |  | ||||||
|     this.satMarginDuration = props.satMarginDuration; |  | ||||||
|     this.sunMarginDuration = props.sunMarginDuration; |  | ||||||
|     this.seatsProposed = props.seatsProposed; |     this.seatsProposed = props.seatsProposed; | ||||||
|     this.seatsRequested = props.seatsRequested; |     this.seatsRequested = props.seatsRequested; | ||||||
|     this.strict = props.strict; |     this.strict = props.strict; | ||||||
|  | @ -55,6 +29,12 @@ export class AdCreatedDomainEvent extends DomainEvent { | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | export class ScheduleDay { | ||||||
|  |   day: number; | ||||||
|  |   time: string; | ||||||
|  |   margin: number; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| export class Waypoint { | export class Waypoint { | ||||||
|   position: number; |   position: number; | ||||||
|   name?: string; |   name?: string; | ||||||
|  |  | ||||||
|  | @ -1,79 +0,0 @@ | ||||||
| import { ValueObject } from '@mobicoop/ddd-library'; |  | ||||||
| 
 |  | ||||||
| /** Note: |  | ||||||
|  * Value Objects with multiple properties can contain |  | ||||||
|  * other Value Objects inside if needed. |  | ||||||
|  * */ |  | ||||||
| 
 |  | ||||||
| export interface MarginDurationsProps { |  | ||||||
|   mon?: number; |  | ||||||
|   tue?: number; |  | ||||||
|   wed?: number; |  | ||||||
|   thu?: number; |  | ||||||
|   fri?: number; |  | ||||||
|   sat?: number; |  | ||||||
|   sun?: number; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export class MarginDurations extends ValueObject<MarginDurationsProps> { |  | ||||||
|   get mon(): number { |  | ||||||
|     return this.props.mon; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   set mon(margin: number) { |  | ||||||
|     this.props.mon = margin; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   get tue(): number { |  | ||||||
|     return this.props.tue; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   set tue(margin: number) { |  | ||||||
|     this.props.tue = margin; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   get wed(): number { |  | ||||||
|     return this.props.wed; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   set wed(margin: number) { |  | ||||||
|     this.props.wed = margin; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   get thu(): number { |  | ||||||
|     return this.props.thu; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   set thu(margin: number) { |  | ||||||
|     this.props.thu = margin; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   get fri(): number { |  | ||||||
|     return this.props.fri; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   set fri(margin: number) { |  | ||||||
|     this.props.fri = margin; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   get sat(): number { |  | ||||||
|     return this.props.sat; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   set sat(margin: number) { |  | ||||||
|     this.props.sat = margin; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   get sun(): number { |  | ||||||
|     return this.props.sun; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   set sun(margin: number) { |  | ||||||
|     this.props.sun = margin; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   // eslint-disable-next-line @typescript-eslint/no-unused-vars
 |  | ||||||
|   protected validate(props: MarginDurationsProps): void { |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  | @ -0,0 +1,31 @@ | ||||||
|  | import { ValueObject } from '@mobicoop/ddd-library'; | ||||||
|  | 
 | ||||||
|  | /** Note: | ||||||
|  |  * Value Objects with multiple properties can contain | ||||||
|  |  * other Value Objects inside if needed. | ||||||
|  |  * */ | ||||||
|  | 
 | ||||||
|  | export interface ScheduleItemProps { | ||||||
|  |   day: number; | ||||||
|  |   time: string; | ||||||
|  |   margin?: number; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export class ScheduleItem extends ValueObject<ScheduleItemProps> { | ||||||
|  |   get day(): number { | ||||||
|  |     return this.props.day; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   get time(): string { | ||||||
|  |     return this.props.time; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   get margin(): number | undefined { | ||||||
|  |     return this.props.margin; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | ||||||
|  |   protected validate(props: ScheduleItemProps): void { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -1,51 +0,0 @@ | ||||||
| import { ValueObject } from '@mobicoop/ddd-library'; |  | ||||||
| 
 |  | ||||||
| /** Note: |  | ||||||
|  * Value Objects with multiple properties can contain |  | ||||||
|  * other Value Objects inside if needed. |  | ||||||
|  * */ |  | ||||||
| 
 |  | ||||||
| export interface ScheduleProps { |  | ||||||
|   mon?: string; |  | ||||||
|   tue?: string; |  | ||||||
|   wed?: string; |  | ||||||
|   thu?: string; |  | ||||||
|   fri?: string; |  | ||||||
|   sat?: string; |  | ||||||
|   sun?: string; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export class Schedule extends ValueObject<ScheduleProps> { |  | ||||||
|   get mon(): string | undefined { |  | ||||||
|     return this.props.mon; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   get tue(): string | undefined { |  | ||||||
|     return this.props.tue; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   get wed(): string | undefined { |  | ||||||
|     return this.props.wed; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   get thu(): string | undefined { |  | ||||||
|     return this.props.thu; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   get fri(): string | undefined { |  | ||||||
|     return this.props.fri; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   get sat(): string | undefined { |  | ||||||
|     return this.props.sat; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   get sun(): string | undefined { |  | ||||||
|     return this.props.sun; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   // eslint-disable-next-line @typescript-eslint/no-unused-vars
 |  | ||||||
|   protected validate(props: ScheduleProps): void { |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  | @ -19,20 +19,6 @@ export type AdBaseModel = { | ||||||
|   frequency: string; |   frequency: string; | ||||||
|   fromDate: Date; |   fromDate: Date; | ||||||
|   toDate: Date; |   toDate: Date; | ||||||
|   monTime: Date; |  | ||||||
|   tueTime: Date; |  | ||||||
|   wedTime: Date; |  | ||||||
|   thuTime: Date; |  | ||||||
|   friTime: Date; |  | ||||||
|   satTime: Date; |  | ||||||
|   sunTime: Date; |  | ||||||
|   monMargin: number; |  | ||||||
|   tueMargin: number; |  | ||||||
|   wedMargin: number; |  | ||||||
|   thuMargin: number; |  | ||||||
|   friMargin: number; |  | ||||||
|   satMargin: number; |  | ||||||
|   sunMargin: number; |  | ||||||
|   seatsProposed: number; |   seatsProposed: number; | ||||||
|   seatsRequested: number; |   seatsRequested: number; | ||||||
|   strict: boolean; |   strict: boolean; | ||||||
|  | @ -42,12 +28,25 @@ export type AdBaseModel = { | ||||||
| 
 | 
 | ||||||
| export type AdReadModel = AdBaseModel & { | export type AdReadModel = AdBaseModel & { | ||||||
|   waypoints: WaypointModel[]; |   waypoints: WaypointModel[]; | ||||||
|  |   schedule: ScheduleItemModel[]; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export type AdWriteModel = AdBaseModel & { | export type AdWriteModel = AdBaseModel & { | ||||||
|   waypoints: { |   waypoints: { | ||||||
|     create: WaypointModel[]; |     create: WaypointModel[]; | ||||||
|   }; |   }; | ||||||
|  |   schedule: { | ||||||
|  |     create: ScheduleItemModel[]; | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export type ScheduleItemModel = { | ||||||
|  |   uuid: string; | ||||||
|  |   day: number; | ||||||
|  |   time: Date; | ||||||
|  |   margin: number; | ||||||
|  |   createdAt: Date; | ||||||
|  |   updatedAt: Date; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export type WaypointModel = { | export type WaypointModel = { | ||||||
|  |  | ||||||
|  | @ -7,17 +7,13 @@ import { DefaultParams } from '../core/application/ports/default-params.type'; | ||||||
| export class DefaultParamsProvider implements DefaultParamsProviderPort { | export class DefaultParamsProvider implements DefaultParamsProviderPort { | ||||||
|   constructor(private readonly _configService: ConfigService) {} |   constructor(private readonly _configService: ConfigService) {} | ||||||
|   getParams = (): DefaultParams => ({ |   getParams = (): DefaultParams => ({ | ||||||
|     MON_MARGIN: parseInt(this._configService.get('DEPARTURE_MARGIN')), |  | ||||||
|     TUE_MARGIN: parseInt(this._configService.get('DEPARTURE_MARGIN')), |  | ||||||
|     WED_MARGIN: parseInt(this._configService.get('DEPARTURE_MARGIN')), |  | ||||||
|     THU_MARGIN: parseInt(this._configService.get('DEPARTURE_MARGIN')), |  | ||||||
|     FRI_MARGIN: parseInt(this._configService.get('DEPARTURE_MARGIN')), |  | ||||||
|     SAT_MARGIN: parseInt(this._configService.get('DEPARTURE_MARGIN')), |  | ||||||
|     SUN_MARGIN: parseInt(this._configService.get('DEPARTURE_MARGIN')), |  | ||||||
|     DRIVER: this._configService.get('ROLE') == 'driver', |     DRIVER: this._configService.get('ROLE') == 'driver', | ||||||
|     SEATS_PROPOSED: parseInt(this._configService.get('SEATS_PROPOSED')), |     SEATS_PROPOSED: parseInt(this._configService.get('SEATS_PROPOSED')), | ||||||
|     PASSENGER: this._configService.get('ROLE') == 'passenger', |     PASSENGER: this._configService.get('ROLE') == 'passenger', | ||||||
|     SEATS_REQUESTED: parseInt(this._configService.get('SEATS_REQUESTED')), |     SEATS_REQUESTED: parseInt(this._configService.get('SEATS_REQUESTED')), | ||||||
|  |     DEPARTURE_TIME_MARGIN: parseInt( | ||||||
|  |       this._configService.get('DEPARTURE_TIME_MARGIN'), | ||||||
|  |     ), | ||||||
|     STRICT: this._configService.get('STRICT_FREQUENCY') == 'true', |     STRICT: this._configService.get('STRICT_FREQUENCY') == 'true', | ||||||
|     DEFAULT_TIMEZONE: this._configService.get('DEFAULT_TIMEZONE'), |     DEFAULT_TIMEZONE: this._configService.get('DEFAULT_TIMEZONE'), | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|  | @ -4,19 +4,30 @@ import { TimeConverterPort } from '../core/application/ports/time-converter.port | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class TimeConverter implements TimeConverterPort { | export class TimeConverter implements TimeConverterPort { | ||||||
|   localDateTimeToUtc = ( |   private readonly BASE_DATE = '1970-01-01'; | ||||||
|  | 
 | ||||||
|  |   localStringTimeToUtcStringTime = (time: string, timezone: string): string => { | ||||||
|  |     try { | ||||||
|  |       if (!time || !timezone) throw new Error(); | ||||||
|  |       return new DateTime(`${this.BASE_DATE}T${time}`, TimeZone.zone(timezone)) | ||||||
|  |         .convert(TimeZone.zone('UTC')) | ||||||
|  |         .format('HH:mm'); | ||||||
|  |     } catch (e) { | ||||||
|  |       return undefined; | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   localStringDateTimeToUtcDate = ( | ||||||
|     date: string, |     date: string, | ||||||
|     time: string, |     time: string, | ||||||
|     timezone: string, |     timezone: string, | ||||||
|     dst?: boolean, |     dst = true, | ||||||
|   ): Date => { |   ): Date => { | ||||||
|     try { |     try { | ||||||
|       if (!date || !time || !timezone) throw new Error(); |       if (!time || !timezone) throw new Error(); | ||||||
|       return new Date( |       return new DateTime(`${date}T${time}`, TimeZone.zone(timezone, dst)) | ||||||
|         new DateTime(`${date}T${time}`, TimeZone.zone(timezone, dst)) |  | ||||||
|         .convert(TimeZone.zone('UTC')) |         .convert(TimeZone.zone('UTC')) | ||||||
|           .toIsoString(), |         .toDate(); | ||||||
|       ); |  | ||||||
|     } catch (e) { |     } catch (e) { | ||||||
|       return undefined; |       return undefined; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| import { Injectable } from '@nestjs/common'; | import { Injectable } from '@nestjs/common'; | ||||||
| import { find } from 'geo-tz'; | import { find } from 'geo-tz'; | ||||||
| import { TimezoneFinderPort } from '../core/application/ports/timezone-finder.port'; | import { TimezoneFinderPort } from '../core/application/ports/timezone-finder.port'; | ||||||
|  | import { zone } from 'timezonecomplete'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class TimezoneFinder implements TimezoneFinderPort { | export class TimezoneFinder implements TimezoneFinderPort { | ||||||
|  | @ -13,4 +14,7 @@ export class TimezoneFinder implements TimezoneFinderPort { | ||||||
|     if (defaultTimezone && foundTimezones.length == 0) return [defaultTimezone]; |     if (defaultTimezone && foundTimezones.length == 0) return [defaultTimezone]; | ||||||
|     return foundTimezones; |     return foundTimezones; | ||||||
|   }; |   }; | ||||||
|  | 
 | ||||||
|  |   offset = (timezone: string): number => | ||||||
|  |     zone(timezone).offsetForUtc(1970, 1, 1, 0, 0, 0); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -9,23 +9,10 @@ export class AdResponseDto extends ResponseBase { | ||||||
|   fromDate: string; |   fromDate: string; | ||||||
|   toDate: string; |   toDate: string; | ||||||
|   schedule: { |   schedule: { | ||||||
|     mon?: string; |     day: number; | ||||||
|     tue?: string; |     time: string; | ||||||
|     wed?: string; |     margin: number; | ||||||
|     thu?: string; |   }[]; | ||||||
|     fri?: string; |  | ||||||
|     sat?: string; |  | ||||||
|     sun?: string; |  | ||||||
|   }; |  | ||||||
|   marginDurations: { |  | ||||||
|     mon?: number; |  | ||||||
|     tue?: number; |  | ||||||
|     wed?: number; |  | ||||||
|     thu?: number; |  | ||||||
|     fri?: number; |  | ||||||
|     sat?: number; |  | ||||||
|     sun?: number; |  | ||||||
|   }; |  | ||||||
|   seatsProposed: number; |   seatsProposed: number; | ||||||
|   seatsRequested: number; |   seatsRequested: number; | ||||||
|   strict: boolean; |   strict: boolean; | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ syntax = "proto3"; | ||||||
| 
 | 
 | ||||||
| package ad; | package ad; | ||||||
| 
 | 
 | ||||||
| service AdsService { | service AdService { | ||||||
|   rpc FindOneById(AdById) returns (Ad); |   rpc FindOneById(AdById) returns (Ad); | ||||||
|   rpc FindAll(AdFilter) returns (Ads); |   rpc FindAll(AdFilter) returns (Ads); | ||||||
|   rpc Create(Ad) returns (AdById); |   rpc Create(Ad) returns (AdById); | ||||||
|  | @ -22,32 +22,17 @@ message Ad { | ||||||
|   Frequency frequency = 5; |   Frequency frequency = 5; | ||||||
|   string fromDate = 6; |   string fromDate = 6; | ||||||
|   string toDate = 7; |   string toDate = 7; | ||||||
|   Schedule schedule = 8; |   repeated ScheduleItem schedule = 8; | ||||||
|   MarginDurations marginDurations = 9; |   int32 seatsProposed = 9; | ||||||
|   int32 seatsProposed = 10; |   int32 seatsRequested = 10; | ||||||
|   int32 seatsRequested = 11; |   bool strict = 11; | ||||||
|   bool strict = 12; |   repeated Waypoint waypoints = 12; | ||||||
|   repeated Waypoint waypoints = 13; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| message Schedule { | message ScheduleItem { | ||||||
|   string mon = 1; |   int32 day = 1; | ||||||
|   string tue = 2; |   string time = 2; | ||||||
|   string wed = 3; |   int32 margin = 3; | ||||||
|   string thu = 4; |  | ||||||
|   string fri = 5; |  | ||||||
|   string sat = 6; |  | ||||||
|   string sun = 7; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| message MarginDurations { |  | ||||||
|   int32 mon = 1; |  | ||||||
|   int32 tue = 2; |  | ||||||
|   int32 wed = 3; |  | ||||||
|   int32 thu = 4; |  | ||||||
|   int32 fri = 5; |  | ||||||
|   int32 sat = 6; |  | ||||||
|   int32 sun = 7; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| message Waypoint { | message Waypoint { | ||||||
|  |  | ||||||
|  | @ -19,7 +19,7 @@ import { AdAlreadyExistsException } from '@modules/ad/core/domain/ad.errors'; | ||||||
| export class CreateAdGrpcController { | export class CreateAdGrpcController { | ||||||
|   constructor(private readonly commandBus: CommandBus) {} |   constructor(private readonly commandBus: CommandBus) {} | ||||||
| 
 | 
 | ||||||
|   @GrpcMethod('AdsService', 'Create') |   @GrpcMethod('AdService', 'Create') | ||||||
|   async create(data: CreateAdRequestDto): Promise<IdResponse> { |   async create(data: CreateAdRequestDto): Promise<IdResponse> { | ||||||
|     try { |     try { | ||||||
|       const aggregateID: AggregateID = await this.commandBus.execute( |       const aggregateID: AggregateID = await this.commandBus.execute( | ||||||
|  |  | ||||||
|  | @ -9,14 +9,13 @@ import { | ||||||
|   IsArray, |   IsArray, | ||||||
|   IsISO8601, |   IsISO8601, | ||||||
| } from 'class-validator'; | } from 'class-validator'; | ||||||
| import { Transform, Type } from 'class-transformer'; | import { Type } from 'class-transformer'; | ||||||
| import { ScheduleDto } from './schedule.dto'; | import { ScheduleItemDto } from './schedule-item.dto'; | ||||||
| import { MarginDurationsDto } from './margin-durations.dto'; |  | ||||||
| import { WaypointDto } from './waypoint.dto'; | import { WaypointDto } from './waypoint.dto'; | ||||||
| import { intToFrequency } from './transformers/int-to-frequency'; |  | ||||||
| import { IsSchedule } from './validators/decorators/is-schedule.decorator'; |  | ||||||
| import { HasValidPositionIndexes } from './validators/decorators/has-valid-position-indexes.decorator'; | import { HasValidPositionIndexes } from './validators/decorators/has-valid-position-indexes.decorator'; | ||||||
| import { Frequency } from '@modules/ad/core/domain/ad.types'; | import { Frequency } from '@modules/ad/core/domain/ad.types'; | ||||||
|  | import { IsAfterOrEqual } from './validators/decorators/is-after-or-equal.decorator'; | ||||||
|  | import { HasDay } from './validators/decorators/has-day.decorator'; | ||||||
| 
 | 
 | ||||||
| export class CreateAdRequestDto { | export class CreateAdRequestDto { | ||||||
|   @IsUUID(4) |   @IsUUID(4) | ||||||
|  | @ -30,10 +29,10 @@ export class CreateAdRequestDto { | ||||||
|   @IsBoolean() |   @IsBoolean() | ||||||
|   passenger?: boolean; |   passenger?: boolean; | ||||||
| 
 | 
 | ||||||
|   @Transform(({ value }) => intToFrequency(value), { |  | ||||||
|     toClassOnly: true, |  | ||||||
|   }) |  | ||||||
|   @IsEnum(Frequency) |   @IsEnum(Frequency) | ||||||
|  |   @HasDay('schedule', { | ||||||
|  |     message: 'At least a day is required for a recurrent ad', | ||||||
|  |   }) | ||||||
|   frequency: Frequency; |   frequency: Frequency; | ||||||
| 
 | 
 | ||||||
|   @IsISO8601({ |   @IsISO8601({ | ||||||
|  | @ -46,17 +45,16 @@ export class CreateAdRequestDto { | ||||||
|     strict: true, |     strict: true, | ||||||
|     strictSeparator: true, |     strictSeparator: true, | ||||||
|   }) |   }) | ||||||
|  |   @IsAfterOrEqual('fromDate', { | ||||||
|  |     message: 'toDate must be after or equal to fromDate', | ||||||
|  |   }) | ||||||
|   toDate: string; |   toDate: string; | ||||||
| 
 | 
 | ||||||
|   @Type(() => ScheduleDto) |   @Type(() => ScheduleItemDto) | ||||||
|   @IsSchedule() |   @IsArray() | ||||||
|  |   @ArrayMinSize(1) | ||||||
|   @ValidateNested({ each: true }) |   @ValidateNested({ each: true }) | ||||||
|   schedule: ScheduleDto; |   schedule: ScheduleItemDto[]; | ||||||
| 
 |  | ||||||
|   @IsOptional() |  | ||||||
|   @Type(() => MarginDurationsDto) |  | ||||||
|   @ValidateNested({ each: true }) |  | ||||||
|   marginDurations?: MarginDurationsDto; |  | ||||||
| 
 | 
 | ||||||
|   @IsOptional() |   @IsOptional() | ||||||
|   @IsInt() |   @IsInt() | ||||||
|  |  | ||||||
|  | @ -1,31 +0,0 @@ | ||||||
| import { IsInt, IsOptional } from 'class-validator'; |  | ||||||
| 
 |  | ||||||
| export class MarginDurationsDto { |  | ||||||
|   @IsOptional() |  | ||||||
|   @IsInt() |  | ||||||
|   mon?: number; |  | ||||||
| 
 |  | ||||||
|   @IsOptional() |  | ||||||
|   @IsInt() |  | ||||||
|   tue?: number; |  | ||||||
| 
 |  | ||||||
|   @IsOptional() |  | ||||||
|   @IsInt() |  | ||||||
|   wed?: number; |  | ||||||
| 
 |  | ||||||
|   @IsOptional() |  | ||||||
|   @IsInt() |  | ||||||
|   thu?: number; |  | ||||||
| 
 |  | ||||||
|   @IsOptional() |  | ||||||
|   @IsInt() |  | ||||||
|   fri?: number; |  | ||||||
| 
 |  | ||||||
|   @IsOptional() |  | ||||||
|   @IsInt() |  | ||||||
|   sat?: number; |  | ||||||
| 
 |  | ||||||
|   @IsOptional() |  | ||||||
|   @IsInt() |  | ||||||
|   sun?: number; |  | ||||||
| } |  | ||||||
|  | @ -0,0 +1,16 @@ | ||||||
|  | import { IsOptional, IsMilitaryTime, IsInt, Min, Max } from 'class-validator'; | ||||||
|  | 
 | ||||||
|  | export class ScheduleItemDto { | ||||||
|  |   @IsOptional() | ||||||
|  |   @IsInt() | ||||||
|  |   @Min(0) | ||||||
|  |   @Max(6) | ||||||
|  |   day?: number; | ||||||
|  | 
 | ||||||
|  |   @IsMilitaryTime() | ||||||
|  |   time: string; | ||||||
|  | 
 | ||||||
|  |   @IsOptional() | ||||||
|  |   @IsInt() | ||||||
|  |   margin?: number; | ||||||
|  | } | ||||||
|  | @ -1,31 +0,0 @@ | ||||||
| import { IsOptional, IsMilitaryTime } from 'class-validator'; |  | ||||||
| 
 |  | ||||||
| export class ScheduleDto { |  | ||||||
|   @IsOptional() |  | ||||||
|   @IsMilitaryTime() |  | ||||||
|   mon?: string; |  | ||||||
| 
 |  | ||||||
|   @IsOptional() |  | ||||||
|   @IsMilitaryTime() |  | ||||||
|   tue?: string; |  | ||||||
| 
 |  | ||||||
|   @IsOptional() |  | ||||||
|   @IsMilitaryTime() |  | ||||||
|   wed?: string; |  | ||||||
| 
 |  | ||||||
|   @IsOptional() |  | ||||||
|   @IsMilitaryTime() |  | ||||||
|   thu?: string; |  | ||||||
| 
 |  | ||||||
|   @IsOptional() |  | ||||||
|   @IsMilitaryTime() |  | ||||||
|   fri?: string; |  | ||||||
| 
 |  | ||||||
|   @IsOptional() |  | ||||||
|   @IsMilitaryTime() |  | ||||||
|   sat?: string; |  | ||||||
| 
 |  | ||||||
|   @IsOptional() |  | ||||||
|   @IsMilitaryTime() |  | ||||||
|   sun?: string; |  | ||||||
| } |  | ||||||
|  | @ -0,0 +1,34 @@ | ||||||
|  | import { Frequency } from '@modules/ad/core/domain/ad.types'; | ||||||
|  | import { | ||||||
|  |   registerDecorator, | ||||||
|  |   ValidationOptions, | ||||||
|  |   ValidationArguments, | ||||||
|  | } from 'class-validator'; | ||||||
|  | 
 | ||||||
|  | export function HasDay( | ||||||
|  |   property: string, | ||||||
|  |   validationOptions?: ValidationOptions, | ||||||
|  | ) { | ||||||
|  |   return function (object: object, propertyName: string) { | ||||||
|  |     registerDecorator({ | ||||||
|  |       name: 'hasDay', | ||||||
|  |       target: object.constructor, | ||||||
|  |       propertyName: propertyName, | ||||||
|  |       constraints: [property], | ||||||
|  |       options: validationOptions, | ||||||
|  |       validator: { | ||||||
|  |         validate(value: any, args: ValidationArguments) { | ||||||
|  |           const [relatedPropertyName] = args.constraints; | ||||||
|  |           const relatedValue = (args.object as any)[relatedPropertyName]; | ||||||
|  |           return ( | ||||||
|  |             value == Frequency.PUNCTUAL || | ||||||
|  |             (Array.isArray(relatedValue) && | ||||||
|  |               relatedValue.some((scheduleItem) => | ||||||
|  |                 scheduleItem.hasOwnProperty('day'), | ||||||
|  |               )) | ||||||
|  |           ); | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  | @ -0,0 +1,31 @@ | ||||||
|  | import { | ||||||
|  |   registerDecorator, | ||||||
|  |   ValidationOptions, | ||||||
|  |   ValidationArguments, | ||||||
|  | } from 'class-validator'; | ||||||
|  | 
 | ||||||
|  | export function IsAfterOrEqual( | ||||||
|  |   property: string, | ||||||
|  |   validationOptions?: ValidationOptions, | ||||||
|  | ) { | ||||||
|  |   return function (object: object, propertyName: string) { | ||||||
|  |     registerDecorator({ | ||||||
|  |       name: 'isAfterOrEqual', | ||||||
|  |       target: object.constructor, | ||||||
|  |       propertyName: propertyName, | ||||||
|  |       constraints: [property], | ||||||
|  |       options: validationOptions, | ||||||
|  |       validator: { | ||||||
|  |         validate(value: any, args: ValidationArguments) { | ||||||
|  |           const [relatedPropertyName] = args.constraints; | ||||||
|  |           const relatedValue = (args.object as any)[relatedPropertyName]; | ||||||
|  |           return ( | ||||||
|  |             typeof value === 'string' && | ||||||
|  |             typeof relatedValue === 'string' && | ||||||
|  |             value >= relatedValue | ||||||
|  |           ); // you can return a Promise<boolean> here as well, if you want to make async validation
 | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  | @ -1,26 +0,0 @@ | ||||||
| import { |  | ||||||
|   ValidateBy, |  | ||||||
|   ValidationArguments, |  | ||||||
|   ValidationOptions, |  | ||||||
|   buildMessage, |  | ||||||
| } from 'class-validator'; |  | ||||||
| 
 |  | ||||||
| export const IsSchedule = ( |  | ||||||
|   validationOptions?: ValidationOptions, |  | ||||||
| ): PropertyDecorator => |  | ||||||
|   ValidateBy( |  | ||||||
|     { |  | ||||||
|       name: '', |  | ||||||
|       constraints: [], |  | ||||||
|       validator: { |  | ||||||
|         // eslint-disable-next-line @typescript-eslint/no-unused-vars
 |  | ||||||
|         validate: (value, args: ValidationArguments): boolean => |  | ||||||
|           Object.keys(value).length > 0, |  | ||||||
|         defaultMessage: buildMessage( |  | ||||||
|           () => `schedule is invalid`, |  | ||||||
|           validationOptions, |  | ||||||
|         ), |  | ||||||
|       }, |  | ||||||
|     }, |  | ||||||
|     validationOptions, |  | ||||||
|   ); |  | ||||||
|  | @ -1,14 +1,6 @@ | ||||||
| import { |  | ||||||
|   PARAMS_PROVIDER, |  | ||||||
|   TIMEZONE_FINDER, |  | ||||||
|   TIME_CONVERTER, |  | ||||||
| } from '@modules/ad/ad.di-tokens'; |  | ||||||
| import { AdMapper } from '@modules/ad/ad.mapper'; | import { AdMapper } from '@modules/ad/ad.mapper'; | ||||||
| import { AdEntity } from '@modules/ad/core/domain/ad.entity'; | import { AdEntity } from '@modules/ad/core/domain/ad.entity'; | ||||||
| import { Frequency } from '@modules/ad/core/domain/ad.types'; | import { Frequency } from '@modules/ad/core/domain/ad.types'; | ||||||
| import { DefaultParamsProviderPort } from '@modules/ad/core/application/ports/default-params-provider.port'; |  | ||||||
| import { TimeConverterPort } from '@modules/ad/core/application/ports/time-converter.port'; |  | ||||||
| import { TimezoneFinderPort } from '@modules/ad/core/application/ports/timezone-finder.port'; |  | ||||||
| import { | import { | ||||||
|   AdReadModel, |   AdReadModel, | ||||||
|   AdWriteModel, |   AdWriteModel, | ||||||
|  | @ -26,15 +18,13 @@ const adEntity: AdEntity = new AdEntity({ | ||||||
|     frequency: Frequency.PUNCTUAL, |     frequency: Frequency.PUNCTUAL, | ||||||
|     fromDate: '2023-06-21', |     fromDate: '2023-06-21', | ||||||
|     toDate: '2023-06-21', |     toDate: '2023-06-21', | ||||||
|     schedule: { |     schedule: [ | ||||||
|       mon: '07:15', |       { | ||||||
|       tue: '07:15', |         day: 3, | ||||||
|       wed: '07:15', |         time: '07:15', | ||||||
|       thu: '07:15', |         margin: 900, | ||||||
|       fri: '07:15', |  | ||||||
|       sat: '07:15', |  | ||||||
|       sun: '07:15', |  | ||||||
|       }, |       }, | ||||||
|  |     ], | ||||||
|     waypoints: [ |     waypoints: [ | ||||||
|       { |       { | ||||||
|         position: 0, |         position: 0, | ||||||
|  | @ -63,15 +53,6 @@ const adEntity: AdEntity = new AdEntity({ | ||||||
|         }, |         }, | ||||||
|       }, |       }, | ||||||
|     ], |     ], | ||||||
|     marginDurations: { |  | ||||||
|       mon: 600, |  | ||||||
|       tue: 600, |  | ||||||
|       wed: 600, |  | ||||||
|       thu: 600, |  | ||||||
|       fri: 600, |  | ||||||
|       sat: 600, |  | ||||||
|       sun: 600, |  | ||||||
|     }, |  | ||||||
|     strict: false, |     strict: false, | ||||||
|     seatsProposed: 3, |     seatsProposed: 3, | ||||||
|     seatsRequested: 1, |     seatsRequested: 1, | ||||||
|  | @ -87,13 +68,16 @@ const adReadModel: AdReadModel = { | ||||||
|   frequency: Frequency.PUNCTUAL, |   frequency: Frequency.PUNCTUAL, | ||||||
|   fromDate: new Date('2023-06-21'), |   fromDate: new Date('2023-06-21'), | ||||||
|   toDate: new Date('2023-06-21'), |   toDate: new Date('2023-06-21'), | ||||||
|   monTime: undefined, |   schedule: [ | ||||||
|   tueTime: undefined, |     { | ||||||
|   wedTime: new Date('2023-06-21T07:15:00Z'), |       uuid: '3978f3d6-560f-4a8f-83ba-9bf5aa9a2d27', | ||||||
|   thuTime: undefined, |       day: 3, | ||||||
|   friTime: undefined, |       time: new Date('2023-06-21T07:05:00Z'), | ||||||
|   satTime: undefined, |       margin: 900, | ||||||
|   sunTime: undefined, |       createdAt: now, | ||||||
|  |       updatedAt: now, | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
|   waypoints: [ |   waypoints: [ | ||||||
|     { |     { | ||||||
|       uuid: '6f53f55e-2bdb-4c23-b6a9-6d7b498e47b9', |       uuid: '6f53f55e-2bdb-4c23-b6a9-6d7b498e47b9', | ||||||
|  | @ -120,13 +104,6 @@ const adReadModel: AdReadModel = { | ||||||
|       updatedAt: now, |       updatedAt: now, | ||||||
|     }, |     }, | ||||||
|   ], |   ], | ||||||
|   monMargin: 600, |  | ||||||
|   tueMargin: 600, |  | ||||||
|   wedMargin: 600, |  | ||||||
|   thuMargin: 600, |  | ||||||
|   friMargin: 600, |  | ||||||
|   satMargin: 600, |  | ||||||
|   sunMargin: 600, |  | ||||||
|   strict: false, |   strict: false, | ||||||
|   seatsProposed: 3, |   seatsProposed: 3, | ||||||
|   seatsRequested: 1, |   seatsRequested: 1, | ||||||
|  | @ -134,64 +111,12 @@ const adReadModel: AdReadModel = { | ||||||
|   updatedAt: now, |   updatedAt: now, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const mockDefaultParamsProvider: DefaultParamsProviderPort = { |  | ||||||
|   getParams: () => { |  | ||||||
|     return { |  | ||||||
|       MON_MARGIN: 900, |  | ||||||
|       TUE_MARGIN: 900, |  | ||||||
|       WED_MARGIN: 900, |  | ||||||
|       THU_MARGIN: 900, |  | ||||||
|       FRI_MARGIN: 900, |  | ||||||
|       SAT_MARGIN: 900, |  | ||||||
|       SUN_MARGIN: 900, |  | ||||||
|       DRIVER: false, |  | ||||||
|       SEATS_PROPOSED: 3, |  | ||||||
|       PASSENGER: true, |  | ||||||
|       SEATS_REQUESTED: 1, |  | ||||||
|       STRICT: false, |  | ||||||
|       DEFAULT_TIMEZONE: 'Europe/Paris', |  | ||||||
|     }; |  | ||||||
|   }, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const mockTimezoneFinder: TimezoneFinderPort = { |  | ||||||
|   // eslint-disable-next-line @typescript-eslint/no-unused-vars
 |  | ||||||
|   timezones: jest.fn().mockImplementation((lon: number, lat: number) => { |  | ||||||
|     if (lon < 60) return 'Europe/Paris'; |  | ||||||
|     return 'America/New_York'; |  | ||||||
|   }), |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const mockTimeConverter: TimeConverterPort = { |  | ||||||
|   localDateTimeToUtc: jest |  | ||||||
|     .fn() |  | ||||||
|     // eslint-disable-next-line @typescript-eslint/no-unused-vars
 |  | ||||||
|     .mockImplementation((datetime: Date, timezone: string, dst?: boolean) => { |  | ||||||
|       return datetime; |  | ||||||
|     }), |  | ||||||
|   utcDatetimeToLocalTime: jest.fn(), |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| describe('Ad Mapper', () => { | describe('Ad Mapper', () => { | ||||||
|   let adMapper: AdMapper; |   let adMapper: AdMapper; | ||||||
| 
 | 
 | ||||||
|   beforeAll(async () => { |   beforeAll(async () => { | ||||||
|     const module = await Test.createTestingModule({ |     const module = await Test.createTestingModule({ | ||||||
|       providers: [ |       providers: [AdMapper], | ||||||
|         AdMapper, |  | ||||||
|         { |  | ||||||
|           provide: PARAMS_PROVIDER, |  | ||||||
|           useValue: mockDefaultParamsProvider, |  | ||||||
|         }, |  | ||||||
|         { |  | ||||||
|           provide: TIMEZONE_FINDER, |  | ||||||
|           useValue: mockTimezoneFinder, |  | ||||||
|         }, |  | ||||||
|         { |  | ||||||
|           provide: TIME_CONVERTER, |  | ||||||
|           useValue: mockTimeConverter, |  | ||||||
|         }, |  | ||||||
|       ], |  | ||||||
|     }).compile(); |     }).compile(); | ||||||
|     adMapper = module.get<AdMapper>(AdMapper); |     adMapper = module.get<AdMapper>(AdMapper); | ||||||
|   }); |   }); | ||||||
|  | @ -204,6 +129,7 @@ describe('Ad Mapper', () => { | ||||||
|     const mapped: AdWriteModel = adMapper.toPersistence(adEntity); |     const mapped: AdWriteModel = adMapper.toPersistence(adEntity); | ||||||
|     expect(mapped.waypoints.create[0].uuid.length).toBe(36); |     expect(mapped.waypoints.create[0].uuid.length).toBe(36); | ||||||
|     expect(mapped.waypoints.create[1].uuid.length).toBe(36); |     expect(mapped.waypoints.create[1].uuid.length).toBe(36); | ||||||
|  |     expect(mapped.schedule.create.length).toBe(1); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   it('should map persisted data to domain entity', async () => { |   it('should map persisted data to domain entity', async () => { | ||||||
|  | @ -212,6 +138,8 @@ describe('Ad Mapper', () => { | ||||||
|       48.689445, |       48.689445, | ||||||
|     ); |     ); | ||||||
|     expect(mapped.getProps().waypoints[1].address.coordinates.lon).toBe(2.3522); |     expect(mapped.getProps().waypoints[1].address.coordinates.lon).toBe(2.3522); | ||||||
|  |     expect(mapped.getProps().schedule.length).toBe(1); | ||||||
|  |     expect(mapped.getProps().schedule[0].time).toBe('07:05'); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   it('should map domain entity to response', async () => { |   it('should map domain entity to response', async () => { | ||||||
|  |  | ||||||
|  | @ -4,7 +4,6 @@ import { | ||||||
|   DefaultAdProps, |   DefaultAdProps, | ||||||
|   Frequency, |   Frequency, | ||||||
| } from '@modules/ad/core/domain/ad.types'; | } from '@modules/ad/core/domain/ad.types'; | ||||||
| import { MarginDurationsProps } from '@modules/ad/core/domain/value-objects/margin-durations.value-object'; |  | ||||||
| import { WaypointProps } from '@modules/ad/core/domain/value-objects/waypoint.value-object'; | import { WaypointProps } from '@modules/ad/core/domain/value-objects/waypoint.value-object'; | ||||||
| 
 | 
 | ||||||
| const originWaypointProps: WaypointProps = { | const originWaypointProps: WaypointProps = { | ||||||
|  | @ -33,15 +32,7 @@ const destinationWaypointProps: WaypointProps = { | ||||||
|     }, |     }, | ||||||
|   }, |   }, | ||||||
| }; | }; | ||||||
| const marginDurationsProps: MarginDurationsProps = { | 
 | ||||||
|   mon: 600, |  | ||||||
|   tue: 600, |  | ||||||
|   wed: 600, |  | ||||||
|   thu: 600, |  | ||||||
|   fri: 600, |  | ||||||
|   sat: 600, |  | ||||||
|   sun: 600, |  | ||||||
| }; |  | ||||||
| const baseCreateAdProps = { | const baseCreateAdProps = { | ||||||
|   userId: 'e8fe64b1-4c33-49e1-9f69-4db48b21df36', |   userId: 'e8fe64b1-4c33-49e1-9f69-4db48b21df36', | ||||||
|   seatsProposed: 3, |   seatsProposed: 3, | ||||||
|  | @ -52,77 +43,86 @@ const baseCreateAdProps = { | ||||||
| const punctualCreateAdProps = { | const punctualCreateAdProps = { | ||||||
|   fromDate: '2023-06-21', |   fromDate: '2023-06-21', | ||||||
|   toDate: '2023-06-21', |   toDate: '2023-06-21', | ||||||
|   schedule: { |   schedule: [ | ||||||
|     wed: '08:30', |     { | ||||||
|  |       day: 3, | ||||||
|  |       time: '08:30', | ||||||
|     }, |     }, | ||||||
|  |   ], | ||||||
|   frequency: Frequency.PUNCTUAL, |   frequency: Frequency.PUNCTUAL, | ||||||
| }; | }; | ||||||
| const recurrentCreateAdProps = { | const recurrentCreateAdProps = { | ||||||
|   fromDate: '2023-06-21', |   fromDate: '2023-06-21', | ||||||
|   toDate: '2024-06-20', |   toDate: '2024-06-20', | ||||||
|   schedule: { |   schedule: [ | ||||||
|     mon: '08:30', |     { | ||||||
|     tue: '08:30', |       day: 1, | ||||||
|     wed: '08:00', |       time: '08:30', | ||||||
|     thu: '08:30', |       margin: 600, | ||||||
|     fri: '08:30', |  | ||||||
|     }, |     }, | ||||||
|  |     { | ||||||
|  |       day: 2, | ||||||
|  |       time: '08:30', | ||||||
|  |       margin: 600, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       day: 3, | ||||||
|  |       time: '08:00', | ||||||
|  |       margin: 600, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       day: 4, | ||||||
|  |       time: '08:30', | ||||||
|  |       margin: 600, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       day: 5, | ||||||
|  |       time: '08:30', | ||||||
|  |       margin: 600, | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
|   frequency: Frequency.RECURRENT, |   frequency: Frequency.RECURRENT, | ||||||
| }; | }; | ||||||
| const punctualPassengerCreateAdProps: CreateAdProps = { | const punctualPassengerCreateAdProps: CreateAdProps = { | ||||||
|   ...baseCreateAdProps, |   ...baseCreateAdProps, | ||||||
|   ...punctualCreateAdProps, |   ...punctualCreateAdProps, | ||||||
|   marginDurations: marginDurationsProps, |  | ||||||
|   driver: false, |   driver: false, | ||||||
|   passenger: true, |   passenger: true, | ||||||
| }; | }; | ||||||
| const recurrentPassengerCreateAdProps: CreateAdProps = { | const recurrentPassengerCreateAdProps: CreateAdProps = { | ||||||
|   ...baseCreateAdProps, |   ...baseCreateAdProps, | ||||||
|   ...recurrentCreateAdProps, |   ...recurrentCreateAdProps, | ||||||
|   marginDurations: marginDurationsProps, |  | ||||||
|   driver: false, |   driver: false, | ||||||
|   passenger: true, |   passenger: true, | ||||||
| }; | }; | ||||||
| const punctualDriverCreateAdProps: CreateAdProps = { | const punctualDriverCreateAdProps: CreateAdProps = { | ||||||
|   ...baseCreateAdProps, |   ...baseCreateAdProps, | ||||||
|   ...punctualCreateAdProps, |   ...punctualCreateAdProps, | ||||||
|   marginDurations: marginDurationsProps, |  | ||||||
|   driver: true, |   driver: true, | ||||||
|   passenger: false, |   passenger: false, | ||||||
| }; | }; | ||||||
| const recurrentDriverCreateAdProps: CreateAdProps = { | const recurrentDriverCreateAdProps: CreateAdProps = { | ||||||
|   ...baseCreateAdProps, |   ...baseCreateAdProps, | ||||||
|   ...recurrentCreateAdProps, |   ...recurrentCreateAdProps, | ||||||
|   marginDurations: marginDurationsProps, |  | ||||||
|   driver: true, |   driver: true, | ||||||
|   passenger: false, |   passenger: false, | ||||||
| }; | }; | ||||||
| const punctualDriverPassengerCreateAdProps: CreateAdProps = { | const punctualDriverPassengerCreateAdProps: CreateAdProps = { | ||||||
|   ...baseCreateAdProps, |   ...baseCreateAdProps, | ||||||
|   ...punctualCreateAdProps, |   ...punctualCreateAdProps, | ||||||
|   marginDurations: marginDurationsProps, |  | ||||||
|   driver: true, |   driver: true, | ||||||
|   passenger: true, |   passenger: true, | ||||||
| }; | }; | ||||||
| const recurrentDriverPassengerCreateAdProps: CreateAdProps = { | const recurrentDriverPassengerCreateAdProps: CreateAdProps = { | ||||||
|   ...baseCreateAdProps, |   ...baseCreateAdProps, | ||||||
|   ...recurrentCreateAdProps, |   ...recurrentCreateAdProps, | ||||||
|   marginDurations: marginDurationsProps, |  | ||||||
|   driver: true, |   driver: true, | ||||||
|   passenger: true, |   passenger: true, | ||||||
| }; | }; | ||||||
| const defaultAdProps: DefaultAdProps = { | const defaultAdProps: DefaultAdProps = { | ||||||
|   driver: false, |   driver: false, | ||||||
|   passenger: true, |   passenger: true, | ||||||
|   marginDurations: { |   marginDuration: 900, | ||||||
|     mon: 900, |  | ||||||
|     tue: 900, |  | ||||||
|     wed: 900, |  | ||||||
|     thu: 900, |  | ||||||
|     fri: 900, |  | ||||||
|     sat: 900, |  | ||||||
|     sun: 900, |  | ||||||
|   }, |  | ||||||
|   seatsProposed: 3, |   seatsProposed: 3, | ||||||
|   seatsRequested: 1, |   seatsRequested: 1, | ||||||
|   strict: false, |   strict: false, | ||||||
|  | @ -136,8 +136,9 @@ describe('Ad entity create', () => { | ||||||
|         defaultAdProps, |         defaultAdProps, | ||||||
|       ); |       ); | ||||||
|       expect(punctualPassengerAd.id.length).toBe(36); |       expect(punctualPassengerAd.id.length).toBe(36); | ||||||
|       expect(punctualPassengerAd.getProps().schedule.mon).toBeUndefined(); |       expect(punctualPassengerAd.getProps().schedule.length).toBe(1); | ||||||
|       expect(punctualPassengerAd.getProps().schedule.wed).toBe('08:30'); |       expect(punctualPassengerAd.getProps().schedule[0].day).toBe(3); | ||||||
|  |       expect(punctualPassengerAd.getProps().schedule[0].time).toBe('08:30'); | ||||||
|       expect(punctualPassengerAd.getProps().driver).toBeFalsy(); |       expect(punctualPassengerAd.getProps().driver).toBeFalsy(); | ||||||
|       expect(punctualPassengerAd.getProps().passenger).toBeTruthy(); |       expect(punctualPassengerAd.getProps().passenger).toBeTruthy(); | ||||||
|     }); |     }); | ||||||
|  | @ -147,8 +148,9 @@ describe('Ad entity create', () => { | ||||||
|         defaultAdProps, |         defaultAdProps, | ||||||
|       ); |       ); | ||||||
|       expect(punctualDriverAd.id.length).toBe(36); |       expect(punctualDriverAd.id.length).toBe(36); | ||||||
|       expect(punctualDriverAd.getProps().schedule.mon).toBeUndefined(); |       expect(punctualDriverAd.getProps().schedule.length).toBe(1); | ||||||
|       expect(punctualDriverAd.getProps().schedule.wed).toBe('08:30'); |       expect(punctualDriverAd.getProps().schedule[0].day).toBe(3); | ||||||
|  |       expect(punctualDriverAd.getProps().schedule[0].time).toBe('08:30'); | ||||||
|       expect(punctualDriverAd.getProps().driver).toBeTruthy(); |       expect(punctualDriverAd.getProps().driver).toBeTruthy(); | ||||||
|       expect(punctualDriverAd.getProps().passenger).toBeFalsy(); |       expect(punctualDriverAd.getProps().passenger).toBeFalsy(); | ||||||
|     }); |     }); | ||||||
|  | @ -158,8 +160,11 @@ describe('Ad entity create', () => { | ||||||
|         defaultAdProps, |         defaultAdProps, | ||||||
|       ); |       ); | ||||||
|       expect(punctualDriverPassengerAd.id.length).toBe(36); |       expect(punctualDriverPassengerAd.id.length).toBe(36); | ||||||
|       expect(punctualDriverPassengerAd.getProps().schedule.mon).toBeUndefined(); |       expect(punctualDriverPassengerAd.getProps().schedule.length).toBe(1); | ||||||
|       expect(punctualDriverPassengerAd.getProps().schedule.wed).toBe('08:30'); |       expect(punctualDriverPassengerAd.getProps().schedule[0].day).toBe(3); | ||||||
|  |       expect(punctualDriverPassengerAd.getProps().schedule[0].time).toBe( | ||||||
|  |         '08:30', | ||||||
|  |       ); | ||||||
|       expect(punctualDriverPassengerAd.getProps().driver).toBeTruthy(); |       expect(punctualDriverPassengerAd.getProps().driver).toBeTruthy(); | ||||||
|       expect(punctualDriverPassengerAd.getProps().passenger).toBeTruthy(); |       expect(punctualDriverPassengerAd.getProps().passenger).toBeTruthy(); | ||||||
|     }); |     }); | ||||||
|  | @ -169,8 +174,9 @@ describe('Ad entity create', () => { | ||||||
|         defaultAdProps, |         defaultAdProps, | ||||||
|       ); |       ); | ||||||
|       expect(recurrentPassengerAd.id.length).toBe(36); |       expect(recurrentPassengerAd.id.length).toBe(36); | ||||||
|       expect(recurrentPassengerAd.getProps().schedule.mon).toBe('08:30'); |       expect(recurrentPassengerAd.getProps().schedule.length).toBe(5); | ||||||
|       expect(recurrentPassengerAd.getProps().schedule.sat).toBeUndefined(); |       expect(recurrentPassengerAd.getProps().schedule[0].day).toBe(1); | ||||||
|  |       expect(recurrentPassengerAd.getProps().schedule[2].time).toBe('08:00'); | ||||||
|       expect(recurrentPassengerAd.getProps().driver).toBeFalsy(); |       expect(recurrentPassengerAd.getProps().driver).toBeFalsy(); | ||||||
|       expect(recurrentPassengerAd.getProps().passenger).toBeTruthy(); |       expect(recurrentPassengerAd.getProps().passenger).toBeTruthy(); | ||||||
|     }); |     }); | ||||||
|  | @ -180,8 +186,9 @@ describe('Ad entity create', () => { | ||||||
|         defaultAdProps, |         defaultAdProps, | ||||||
|       ); |       ); | ||||||
|       expect(recurrentDriverAd.id.length).toBe(36); |       expect(recurrentDriverAd.id.length).toBe(36); | ||||||
|       expect(recurrentDriverAd.getProps().schedule.mon).toBe('08:30'); |       expect(recurrentDriverAd.getProps().schedule.length).toBe(5); | ||||||
|       expect(recurrentDriverAd.getProps().schedule.sat).toBeUndefined(); |       expect(recurrentDriverAd.getProps().schedule[1].day).toBe(2); | ||||||
|  |       expect(recurrentDriverAd.getProps().schedule[0].time).toBe('08:30'); | ||||||
|       expect(recurrentDriverAd.getProps().driver).toBeTruthy(); |       expect(recurrentDriverAd.getProps().driver).toBeTruthy(); | ||||||
|       expect(recurrentDriverAd.getProps().passenger).toBeFalsy(); |       expect(recurrentDriverAd.getProps().passenger).toBeFalsy(); | ||||||
|     }); |     }); | ||||||
|  | @ -191,10 +198,11 @@ describe('Ad entity create', () => { | ||||||
|         defaultAdProps, |         defaultAdProps, | ||||||
|       ); |       ); | ||||||
|       expect(recurrentDriverPassengerAd.id.length).toBe(36); |       expect(recurrentDriverPassengerAd.id.length).toBe(36); | ||||||
|       expect(recurrentDriverPassengerAd.getProps().schedule.mon).toBe('08:30'); |       expect(recurrentDriverPassengerAd.getProps().schedule.length).toBe(5); | ||||||
|       expect( |       expect(recurrentDriverPassengerAd.getProps().schedule[3].day).toBe(4); | ||||||
|         recurrentDriverPassengerAd.getProps().schedule.sat, |       expect(recurrentDriverPassengerAd.getProps().schedule[4].time).toBe( | ||||||
|       ).toBeUndefined(); |         '08:30', | ||||||
|  |       ); | ||||||
|       expect(recurrentDriverPassengerAd.getProps().driver).toBeTruthy(); |       expect(recurrentDriverPassengerAd.getProps().driver).toBeTruthy(); | ||||||
|       expect(recurrentDriverPassengerAd.getProps().passenger).toBeTruthy(); |       expect(recurrentDriverPassengerAd.getProps().passenger).toBeTruthy(); | ||||||
|     }); |     }); | ||||||
|  | @ -205,7 +213,6 @@ describe('Ad entity create', () => { | ||||||
|       const punctualWithoutRoleCreateAdProps: CreateAdProps = { |       const punctualWithoutRoleCreateAdProps: CreateAdProps = { | ||||||
|         ...baseCreateAdProps, |         ...baseCreateAdProps, | ||||||
|         ...punctualCreateAdProps, |         ...punctualCreateAdProps, | ||||||
|         marginDurations: marginDurationsProps, |  | ||||||
|         driver: false, |         driver: false, | ||||||
|         passenger: false, |         passenger: false, | ||||||
|       }; |       }; | ||||||
|  | @ -214,8 +221,8 @@ describe('Ad entity create', () => { | ||||||
|         defaultAdProps, |         defaultAdProps, | ||||||
|       ); |       ); | ||||||
|       expect(punctualWithoutRoleAd.id.length).toBe(36); |       expect(punctualWithoutRoleAd.id.length).toBe(36); | ||||||
|       expect(punctualWithoutRoleAd.getProps().schedule.mon).toBeUndefined(); |       expect(punctualWithoutRoleAd.getProps().schedule.length).toBe(1); | ||||||
|       expect(punctualWithoutRoleAd.getProps().schedule.wed).toBe('08:30'); |       expect(punctualWithoutRoleAd.getProps().schedule[0].time).toBe('08:30'); | ||||||
|       expect(punctualWithoutRoleAd.getProps().driver).toBeFalsy(); |       expect(punctualWithoutRoleAd.getProps().driver).toBeFalsy(); | ||||||
|       expect(punctualWithoutRoleAd.getProps().passenger).toBeTruthy(); |       expect(punctualWithoutRoleAd.getProps().passenger).toBeTruthy(); | ||||||
|     }); |     }); | ||||||
|  | @ -227,7 +234,6 @@ describe('Ad entity create', () => { | ||||||
|         strict: undefined, |         strict: undefined, | ||||||
|         waypoints: [originWaypointProps, destinationWaypointProps], |         waypoints: [originWaypointProps, destinationWaypointProps], | ||||||
|         ...punctualCreateAdProps, |         ...punctualCreateAdProps, | ||||||
|         marginDurations: marginDurationsProps, |  | ||||||
|         driver: false, |         driver: false, | ||||||
|         passenger: true, |         passenger: true, | ||||||
|       }; |       }; | ||||||
|  | @ -236,8 +242,8 @@ describe('Ad entity create', () => { | ||||||
|         defaultAdProps, |         defaultAdProps, | ||||||
|       ); |       ); | ||||||
|       expect(punctualWithoutStrictAd.id.length).toBe(36); |       expect(punctualWithoutStrictAd.id.length).toBe(36); | ||||||
|       expect(punctualWithoutStrictAd.getProps().schedule.mon).toBeUndefined(); |       expect(punctualWithoutStrictAd.getProps().schedule.length).toBe(1); | ||||||
|       expect(punctualWithoutStrictAd.getProps().schedule.wed).toBe('08:30'); |       expect(punctualWithoutStrictAd.getProps().schedule[0].time).toBe('08:30'); | ||||||
|       expect(punctualWithoutStrictAd.getProps().driver).toBeFalsy(); |       expect(punctualWithoutStrictAd.getProps().driver).toBeFalsy(); | ||||||
|       expect(punctualWithoutStrictAd.getProps().passenger).toBeTruthy(); |       expect(punctualWithoutStrictAd.getProps().passenger).toBeTruthy(); | ||||||
|       expect(punctualWithoutStrictAd.getProps().strict).toBeFalsy(); |       expect(punctualWithoutStrictAd.getProps().strict).toBeFalsy(); | ||||||
|  | @ -250,7 +256,6 @@ describe('Ad entity create', () => { | ||||||
|         strict: false, |         strict: false, | ||||||
|         waypoints: [originWaypointProps, destinationWaypointProps], |         waypoints: [originWaypointProps, destinationWaypointProps], | ||||||
|         ...punctualCreateAdProps, |         ...punctualCreateAdProps, | ||||||
|         marginDurations: marginDurationsProps, |  | ||||||
|         driver: false, |         driver: false, | ||||||
|         passenger: true, |         passenger: true, | ||||||
|       }; |       }; | ||||||
|  | @ -259,10 +264,10 @@ describe('Ad entity create', () => { | ||||||
|         defaultAdProps, |         defaultAdProps, | ||||||
|       ); |       ); | ||||||
|       expect(punctualWithoutSeatsRequestedAd.id.length).toBe(36); |       expect(punctualWithoutSeatsRequestedAd.id.length).toBe(36); | ||||||
|       expect( |       expect(punctualWithoutSeatsRequestedAd.getProps().schedule.length).toBe( | ||||||
|         punctualWithoutSeatsRequestedAd.getProps().schedule.mon, |         1, | ||||||
|       ).toBeUndefined(); |       ); | ||||||
|       expect(punctualWithoutSeatsRequestedAd.getProps().schedule.wed).toBe( |       expect(punctualWithoutSeatsRequestedAd.getProps().schedule[0].time).toBe( | ||||||
|         '08:30', |         '08:30', | ||||||
|       ); |       ); | ||||||
|       expect(punctualWithoutSeatsRequestedAd.getProps().driver).toBeFalsy(); |       expect(punctualWithoutSeatsRequestedAd.getProps().driver).toBeFalsy(); | ||||||
|  | @ -277,7 +282,6 @@ describe('Ad entity create', () => { | ||||||
|         strict: false, |         strict: false, | ||||||
|         waypoints: [originWaypointProps, destinationWaypointProps], |         waypoints: [originWaypointProps, destinationWaypointProps], | ||||||
|         ...punctualCreateAdProps, |         ...punctualCreateAdProps, | ||||||
|         marginDurations: marginDurationsProps, |  | ||||||
|         driver: true, |         driver: true, | ||||||
|         passenger: false, |         passenger: false, | ||||||
|       }; |       }; | ||||||
|  | @ -286,10 +290,8 @@ describe('Ad entity create', () => { | ||||||
|         defaultAdProps, |         defaultAdProps, | ||||||
|       ); |       ); | ||||||
|       expect(punctualWithoutSeatsProposedAd.id.length).toBe(36); |       expect(punctualWithoutSeatsProposedAd.id.length).toBe(36); | ||||||
|       expect( |       expect(punctualWithoutSeatsProposedAd.getProps().schedule.length).toBe(1); | ||||||
|         punctualWithoutSeatsProposedAd.getProps().schedule.mon, |       expect(punctualWithoutSeatsProposedAd.getProps().schedule[0].time).toBe( | ||||||
|       ).toBeUndefined(); |  | ||||||
|       expect(punctualWithoutSeatsProposedAd.getProps().schedule.wed).toBe( |  | ||||||
|         '08:30', |         '08:30', | ||||||
|       ); |       ); | ||||||
|       expect(punctualWithoutSeatsProposedAd.getProps().driver).toBeTruthy(); |       expect(punctualWithoutSeatsProposedAd.getProps().driver).toBeTruthy(); | ||||||
|  | @ -297,56 +299,29 @@ describe('Ad entity create', () => { | ||||||
|       expect(punctualWithoutSeatsProposedAd.getProps().seatsProposed).toBe(3); |       expect(punctualWithoutSeatsProposedAd.getProps().seatsProposed).toBe(3); | ||||||
|     }); |     }); | ||||||
|     it('should create a new punctual driver ad entity with margin durations if margin durations are empty', async () => { |     it('should create a new punctual driver ad entity with margin durations if margin durations are empty', async () => { | ||||||
|       const punctualWithoutMarginDurationsCreateAdProps: CreateAdProps = { |       const punctualWithoutMarginDurationCreateAdProps: CreateAdProps = { | ||||||
|         ...baseCreateAdProps, |         ...baseCreateAdProps, | ||||||
|         waypoints: [originWaypointProps, destinationWaypointProps], |         waypoints: [originWaypointProps, destinationWaypointProps], | ||||||
|         ...punctualCreateAdProps, |         ...punctualCreateAdProps, | ||||||
|         marginDurations: {}, |  | ||||||
|         driver: true, |         driver: true, | ||||||
|         passenger: false, |         passenger: false, | ||||||
|       }; |       }; | ||||||
|       const punctualWithoutMarginDurationsAd: AdEntity = AdEntity.create( |       const punctualWithoutMarginDurationAd: AdEntity = AdEntity.create( | ||||||
|         punctualWithoutMarginDurationsCreateAdProps, |         punctualWithoutMarginDurationCreateAdProps, | ||||||
|         defaultAdProps, |         defaultAdProps, | ||||||
|       ); |       ); | ||||||
|       expect(punctualWithoutMarginDurationsAd.id.length).toBe(36); |       expect(punctualWithoutMarginDurationAd.id.length).toBe(36); | ||||||
|       expect( |       expect(punctualWithoutMarginDurationAd.getProps().schedule.length).toBe( | ||||||
|         punctualWithoutMarginDurationsAd.getProps().schedule.mon, |         1, | ||||||
|       ).toBeUndefined(); |       ); | ||||||
|       expect(punctualWithoutMarginDurationsAd.getProps().schedule.wed).toBe( |       expect(punctualWithoutMarginDurationAd.getProps().schedule[0].time).toBe( | ||||||
|         '08:30', |         '08:30', | ||||||
|       ); |       ); | ||||||
|       expect(punctualWithoutMarginDurationsAd.getProps().driver).toBeTruthy(); |  | ||||||
|       expect(punctualWithoutMarginDurationsAd.getProps().passenger).toBeFalsy(); |  | ||||||
|       expect( |       expect( | ||||||
|         punctualWithoutMarginDurationsAd.getProps().marginDurations.mon, |         punctualWithoutMarginDurationAd.getProps().schedule[0].margin, | ||||||
|       ).toBe(900); |  | ||||||
|     }); |  | ||||||
|     it('should create a new punctual driver ad entity with margin durations if margin durations are undefined', async () => { |  | ||||||
|       const punctualWithoutMarginDurationsCreateAdProps: CreateAdProps = { |  | ||||||
|         ...baseCreateAdProps, |  | ||||||
|         waypoints: [originWaypointProps, destinationWaypointProps], |  | ||||||
|         ...punctualCreateAdProps, |  | ||||||
|         marginDurations: undefined, |  | ||||||
|         driver: true, |  | ||||||
|         passenger: false, |  | ||||||
|       }; |  | ||||||
|       const punctualWithoutMarginDurationsAd: AdEntity = AdEntity.create( |  | ||||||
|         punctualWithoutMarginDurationsCreateAdProps, |  | ||||||
|         defaultAdProps, |  | ||||||
|       ); |  | ||||||
|       expect(punctualWithoutMarginDurationsAd.id.length).toBe(36); |  | ||||||
|       expect( |  | ||||||
|         punctualWithoutMarginDurationsAd.getProps().schedule.mon, |  | ||||||
|       ).toBeUndefined(); |  | ||||||
|       expect(punctualWithoutMarginDurationsAd.getProps().schedule.wed).toBe( |  | ||||||
|         '08:30', |  | ||||||
|       ); |  | ||||||
|       expect(punctualWithoutMarginDurationsAd.getProps().driver).toBeTruthy(); |  | ||||||
|       expect(punctualWithoutMarginDurationsAd.getProps().passenger).toBeFalsy(); |  | ||||||
|       expect( |  | ||||||
|         punctualWithoutMarginDurationsAd.getProps().marginDurations.mon, |  | ||||||
|       ).toBe(900); |       ).toBe(900); | ||||||
|  |       expect(punctualWithoutMarginDurationAd.getProps().driver).toBeTruthy(); | ||||||
|  |       expect(punctualWithoutMarginDurationAd.getProps().passenger).toBeFalsy(); | ||||||
|     }); |     }); | ||||||
|     it('should create a new punctual passenger ad entity with valid positions if positions are missing', async () => { |     it('should create a new punctual passenger ad entity with valid positions if positions are missing', async () => { | ||||||
|       const punctualWithoutPositionsCreateAdProps: CreateAdProps = { |       const punctualWithoutPositionsCreateAdProps: CreateAdProps = { | ||||||
|  | @ -383,7 +358,6 @@ describe('Ad entity create', () => { | ||||||
|           }, |           }, | ||||||
|         ], |         ], | ||||||
|         ...punctualCreateAdProps, |         ...punctualCreateAdProps, | ||||||
|         marginDurations: marginDurationsProps, |  | ||||||
|         driver: false, |         driver: false, | ||||||
|         passenger: false, |         passenger: false, | ||||||
|       }; |       }; | ||||||
|  | @ -392,10 +366,10 @@ describe('Ad entity create', () => { | ||||||
|         defaultAdProps, |         defaultAdProps, | ||||||
|       ); |       ); | ||||||
|       expect(punctualWithoutPositionsAd.id.length).toBe(36); |       expect(punctualWithoutPositionsAd.id.length).toBe(36); | ||||||
|       expect( |       expect(punctualWithoutPositionsAd.getProps().schedule.length).toBe(1); | ||||||
|         punctualWithoutPositionsAd.getProps().schedule.mon, |       expect(punctualWithoutPositionsAd.getProps().schedule[0].time).toBe( | ||||||
|       ).toBeUndefined(); |         '08:30', | ||||||
|       expect(punctualWithoutPositionsAd.getProps().schedule.wed).toBe('08:30'); |       ); | ||||||
|       expect(punctualWithoutPositionsAd.getProps().driver).toBeFalsy(); |       expect(punctualWithoutPositionsAd.getProps().driver).toBeFalsy(); | ||||||
|       expect(punctualWithoutPositionsAd.getProps().passenger).toBeTruthy(); |       expect(punctualWithoutPositionsAd.getProps().passenger).toBeTruthy(); | ||||||
|       expect(punctualWithoutPositionsAd.getProps().waypoints[0].position).toBe( |       expect(punctualWithoutPositionsAd.getProps().waypoints[0].position).toBe( | ||||||
|  |  | ||||||
|  | @ -0,0 +1,14 @@ | ||||||
|  | import { ScheduleItem } from '@modules/ad/core/domain/value-objects/schedule-item.value-object'; | ||||||
|  | 
 | ||||||
|  | describe('Schedule item value object', () => { | ||||||
|  |   it('should create a schedule item value object', () => { | ||||||
|  |     const scheduleItemVO = new ScheduleItem({ | ||||||
|  |       day: 0, | ||||||
|  |       time: '07:00', | ||||||
|  |       margin: 900, | ||||||
|  |     }); | ||||||
|  |     expect(scheduleItemVO.day).toBe(0); | ||||||
|  |     expect(scheduleItemVO.time).toBe('07:00'); | ||||||
|  |     expect(scheduleItemVO.margin).toBe(900); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
|  | @ -1,22 +0,0 @@ | ||||||
| import { Schedule } from '@modules/ad/core/domain/value-objects/schedule.value-object'; |  | ||||||
| 
 |  | ||||||
| describe('Schedule value object', () => { |  | ||||||
|   it('should create a schedule value object', () => { |  | ||||||
|     const scheduleVO = new Schedule({ |  | ||||||
|       mon: '07:00', |  | ||||||
|       tue: '07:05', |  | ||||||
|       wed: '07:10', |  | ||||||
|       thu: '07:15', |  | ||||||
|       fri: '07:20', |  | ||||||
|       sat: '07:25', |  | ||||||
|       sun: '07:30', |  | ||||||
|     }); |  | ||||||
|     expect(scheduleVO.mon).toBe('07:00'); |  | ||||||
|     expect(scheduleVO.tue).toBe('07:05'); |  | ||||||
|     expect(scheduleVO.wed).toBe('07:10'); |  | ||||||
|     expect(scheduleVO.thu).toBe('07:15'); |  | ||||||
|     expect(scheduleVO.fri).toBe('07:20'); |  | ||||||
|     expect(scheduleVO.sat).toBe('07:25'); |  | ||||||
|     expect(scheduleVO.sun).toBe('07:30'); |  | ||||||
|   }); |  | ||||||
| }); |  | ||||||
|  | @ -6,7 +6,7 @@ import { Test, TestingModule } from '@nestjs/testing'; | ||||||
| const mockConfigService = { | const mockConfigService = { | ||||||
|   get: jest.fn().mockImplementation((value: string) => { |   get: jest.fn().mockImplementation((value: string) => { | ||||||
|     switch (value) { |     switch (value) { | ||||||
|       case 'DEPARTURE_MARGIN': |       case 'DEPARTURE_TIME_MARGIN': | ||||||
|         return 900; |         return 900; | ||||||
|       case 'ROLE': |       case 'ROLE': | ||||||
|         return 'passenger'; |         return 'passenger'; | ||||||
|  | @ -50,7 +50,7 @@ describe('DefaultParamsProvider', () => { | ||||||
| 
 | 
 | ||||||
|   it('should provide default params', async () => { |   it('should provide default params', async () => { | ||||||
|     const params: DefaultParams = defaultParamsProvider.getParams(); |     const params: DefaultParams = defaultParamsProvider.getParams(); | ||||||
|     expect(params.SUN_MARGIN).toBe(900); |     expect(params.DEPARTURE_TIME_MARGIN).toBe(900); | ||||||
|     expect(params.PASSENGER).toBeTruthy(); |     expect(params.PASSENGER).toBeTruthy(); | ||||||
|     expect(params.DRIVER).toBeFalsy(); |     expect(params.DRIVER).toBeFalsy(); | ||||||
|     expect(params.DEFAULT_TIMEZONE).toBe('Europe/Paris'); |     expect(params.DEFAULT_TIMEZONE).toBe('Europe/Paris'); | ||||||
|  |  | ||||||
|  | @ -6,35 +6,29 @@ describe('Time Converter', () => { | ||||||
|     expect(timeConverter).toBeDefined(); |     expect(timeConverter).toBeDefined(); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   describe('localDateTimeToUtc', () => { |   describe('localStringTimeToUtcStringTime', () => { | ||||||
|     it('should convert a paris datetime to utc', () => { |     it('should convert a paris time to utc time with dst', () => { | ||||||
|       const timeConverter: TimeConverter = new TimeConverter(); |       const timeConverter: TimeConverter = new TimeConverter(); | ||||||
|       const parisDate = '2023-06-22'; |  | ||||||
|       const parisTime = '08:00'; |       const parisTime = '08:00'; | ||||||
|       const utcDatetime = timeConverter.localDateTimeToUtc( |       const utcDatetime = timeConverter.localStringTimeToUtcStringTime( | ||||||
|         parisDate, |  | ||||||
|         parisTime, |         parisTime, | ||||||
|         'Europe/Paris', |         'Europe/Paris', | ||||||
|       ); |       ); | ||||||
|       expect(utcDatetime.toISOString()).toEqual('2023-06-22T06:00:00.000Z'); |       expect(utcDatetime).toBe('07:00'); | ||||||
|     }); |     }); | ||||||
|     it('should return undefined if date is invalid', () => { |     it('should return undefined if time is invalid', () => { | ||||||
|       const timeConverter: TimeConverter = new TimeConverter(); |       const timeConverter: TimeConverter = new TimeConverter(); | ||||||
|       const parisDate = '2023-16-22'; |       const parisTime = '28:00'; | ||||||
|       const parisTime = '08:00'; |       const utcDatetime = timeConverter.localStringTimeToUtcStringTime( | ||||||
|       const utcDatetime = timeConverter.localDateTimeToUtc( |  | ||||||
|         parisDate, |  | ||||||
|         parisTime, |         parisTime, | ||||||
|         'Europe/Paris', |         'Europe/Paris', | ||||||
|       ); |       ); | ||||||
|       expect(utcDatetime).toBeUndefined(); |       expect(utcDatetime).toBeUndefined(); | ||||||
|     }); |     }); | ||||||
|     it('should return undefined if time is invalid', () => { |     it('should return undefined if time is undefined', () => { | ||||||
|       const timeConverter: TimeConverter = new TimeConverter(); |       const timeConverter: TimeConverter = new TimeConverter(); | ||||||
|       const parisDate = '2023-06-22'; |       const parisTime = undefined; | ||||||
|       const parisTime = '28:00'; |       const utcDatetime = timeConverter.localStringTimeToUtcStringTime( | ||||||
|       const utcDatetime = timeConverter.localDateTimeToUtc( |  | ||||||
|         parisDate, |  | ||||||
|         parisTime, |         parisTime, | ||||||
|         'Europe/Paris', |         'Europe/Paris', | ||||||
|       ); |       ); | ||||||
|  | @ -42,55 +36,51 @@ describe('Time Converter', () => { | ||||||
|     }); |     }); | ||||||
|     it('should return undefined if timezone is invalid', () => { |     it('should return undefined if timezone is invalid', () => { | ||||||
|       const timeConverter: TimeConverter = new TimeConverter(); |       const timeConverter: TimeConverter = new TimeConverter(); | ||||||
|       const parisDate = '2023-06-22'; |       const fooBarTime = '08:00'; | ||||||
|       const parisTime = '08:00'; |       const utcDatetime = timeConverter.localStringTimeToUtcStringTime( | ||||||
|       const utcDatetime = timeConverter.localDateTimeToUtc( |         fooBarTime, | ||||||
|         parisDate, |  | ||||||
|         parisTime, |  | ||||||
|         'Foo/Bar', |         'Foo/Bar', | ||||||
|       ); |       ); | ||||||
|       expect(utcDatetime).toBeUndefined(); |       expect(utcDatetime).toBeUndefined(); | ||||||
|     }); |     }); | ||||||
|     it('should return undefined if date is undefined', () => { |     it('should return undefined if timezone is undefined', () => { | ||||||
|       const timeConverter: TimeConverter = new TimeConverter(); |       const timeConverter: TimeConverter = new TimeConverter(); | ||||||
|       const parisDate = undefined; |       const fooBarTime = '08:00'; | ||||||
|       const parisTime = '08:00'; |       const utcDatetime = timeConverter.localStringTimeToUtcStringTime( | ||||||
|       const utcDatetime = timeConverter.localDateTimeToUtc( |         fooBarTime, | ||||||
|         parisDate, |         undefined, | ||||||
|         parisTime, |  | ||||||
|         'Europe/Paris', |  | ||||||
|       ); |       ); | ||||||
|       expect(utcDatetime).toBeUndefined(); |       expect(utcDatetime).toBeUndefined(); | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   describe('utcDatetimeToLocalTime', () => { |   // describe('utcDatetimeToLocalTime', () => {
 | ||||||
|     it('should convert an utc datetime isostring to a paris local time', () => { |   //   it('should convert an utc datetime isostring to a paris local time', () => {
 | ||||||
|       const timeConverter: TimeConverter = new TimeConverter(); |   //     const timeConverter: TimeConverter = new TimeConverter();
 | ||||||
|       const utcDatetimeIsostring = '2023-06-22T06:25:00.000Z'; |   //     const utcDatetimeIsostring = '2023-06-22T06:25:00.000Z';
 | ||||||
|       const parisTime = timeConverter.utcDatetimeToLocalTime( |   //     const parisTime = timeConverter.utcDatetimeToLocalTime(
 | ||||||
|         utcDatetimeIsostring, |   //       utcDatetimeIsostring,
 | ||||||
|         'Europe/Paris', |   //       'Europe/Paris',
 | ||||||
|       ); |   //     );
 | ||||||
|       expect(parisTime).toBe('08:25'); |   //     expect(parisTime).toBe('08:25');
 | ||||||
|     }); |   //   });
 | ||||||
|     it('should return undefined if isostring input is invalid', () => { |   //   it('should return undefined if isostring input is invalid', () => {
 | ||||||
|       const timeConverter: TimeConverter = new TimeConverter(); |   //     const timeConverter: TimeConverter = new TimeConverter();
 | ||||||
|       const utcDatetimeIsostring = 'not_an_isostring'; |   //     const utcDatetimeIsostring = 'not_an_isostring';
 | ||||||
|       const parisTime = timeConverter.utcDatetimeToLocalTime( |   //     const parisTime = timeConverter.utcDatetimeToLocalTime(
 | ||||||
|         utcDatetimeIsostring, |   //       utcDatetimeIsostring,
 | ||||||
|         'Europe/Paris', |   //       'Europe/Paris',
 | ||||||
|       ); |   //     );
 | ||||||
|       expect(parisTime).toBeUndefined(); |   //     expect(parisTime).toBeUndefined();
 | ||||||
|     }); |   //   });
 | ||||||
|     it('should return undefined if timezone input is invalid', () => { |   //   it('should return undefined if timezone input is invalid', () => {
 | ||||||
|       const timeConverter: TimeConverter = new TimeConverter(); |   //     const timeConverter: TimeConverter = new TimeConverter();
 | ||||||
|       const utcDatetimeIsostring = '2023-06-22T06:25:00.000Z'; |   //     const utcDatetimeIsostring = '2023-06-22T06:25:00.000Z';
 | ||||||
|       const parisTime = timeConverter.utcDatetimeToLocalTime( |   //     const parisTime = timeConverter.utcDatetimeToLocalTime(
 | ||||||
|         utcDatetimeIsostring, |   //       utcDatetimeIsostring,
 | ||||||
|         'Foo/Bar', |   //       'Foo/Bar',
 | ||||||
|       ); |   //     );
 | ||||||
|       expect(parisTime).toBeUndefined(); |   //     expect(parisTime).toBeUndefined();
 | ||||||
|     }); |   //   });
 | ||||||
|   }); |   // });
 | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -1,11 +1,11 @@ | ||||||
| import { ScheduleDto } from '@modules/ad/interface/grpc-controllers/dtos/schedule.dto'; | import { ScheduleItemDto } from '@modules/ad/interface/grpc-controllers/dtos/schedule.dto'; | ||||||
| import { IsSchedule } from '@modules/ad/interface/grpc-controllers/dtos/validators/decorators/is-schedule.decorator'; | import { IsSchedule } from '@modules/ad/interface/grpc-controllers/dtos/validators/decorators/is-schedule.decorator'; | ||||||
| import { Validator } from 'class-validator'; | import { Validator } from 'class-validator'; | ||||||
| 
 | 
 | ||||||
| describe('schedule decorator', () => { | describe('schedule decorator', () => { | ||||||
|   class MyClass { |   class MyClass { | ||||||
|     @IsSchedule() |     @IsSchedule() | ||||||
|     schedule: ScheduleDto; |     schedule: ScheduleItemDto; | ||||||
|   } |   } | ||||||
|   it('should return a property decorator has a function', () => { |   it('should return a property decorator has a function', () => { | ||||||
|     const isSchedule = IsSchedule(); |     const isSchedule = IsSchedule(); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue