transform to output
This commit is contained in:
parent
218595553d
commit
df92245357
|
@ -2,5 +2,8 @@ export const AD_MESSAGE_PUBLISHER = Symbol('AD_MESSAGE_PUBLISHER');
|
|||
export const PARAMS_PROVIDER = Symbol('PARAMS_PROVIDER');
|
||||
export const TIMEZONE_FINDER = Symbol('TIMEZONE_FINDER');
|
||||
export const TIME_CONVERTER = Symbol('TIME_CONVERTER');
|
||||
export const DATETIME_TRANSFORMER = Symbol('DATETIME_TRANSFORMER');
|
||||
export const INPUT_DATETIME_TRANSFORMER = Symbol('INPUT_DATETIME_TRANSFORMER');
|
||||
export const OUTPUT_DATETIME_TRANSFORMER = Symbol(
|
||||
'OUTPUT_DATETIME_TRANSFORMER',
|
||||
);
|
||||
export const AD_REPOSITORY = Symbol('AD_REPOSITORY');
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Mapper } from '@mobicoop/ddd-library';
|
||||
import { AdResponseDto } from './interface/dtos/ad.response.dto';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { AdEntity } from './core/domain/ad.entity';
|
||||
import {
|
||||
AdWriteModel,
|
||||
|
@ -12,6 +12,8 @@ import { Frequency } from './core/domain/ad.types';
|
|||
import { WaypointProps } from './core/domain/value-objects/waypoint.value-object';
|
||||
import { v4 } from 'uuid';
|
||||
import { ScheduleItemProps } from './core/domain/value-objects/schedule-item.value-object';
|
||||
import { OUTPUT_DATETIME_TRANSFORMER } from './ad.di-tokens';
|
||||
import { DateTimeTransformerPort } from './core/application/ports/datetime-transformer.port';
|
||||
|
||||
/**
|
||||
* Mapper constructs objects that are used in different layers:
|
||||
|
@ -24,6 +26,11 @@ import { ScheduleItemProps } from './core/domain/value-objects/schedule-item.val
|
|||
export class AdMapper
|
||||
implements Mapper<AdEntity, AdReadModel, AdWriteModel, AdResponseDto>
|
||||
{
|
||||
constructor(
|
||||
@Inject(OUTPUT_DATETIME_TRANSFORMER)
|
||||
private readonly outputDatetimeTransformer: DateTimeTransformerPort,
|
||||
) {}
|
||||
|
||||
toPersistence = (entity: AdEntity): AdWriteModel => {
|
||||
const copy = entity.getProps();
|
||||
const now = new Date();
|
||||
|
@ -129,12 +136,42 @@ export class AdMapper
|
|||
response.driver = props.driver;
|
||||
response.passenger = props.passenger;
|
||||
response.frequency = props.frequency;
|
||||
response.fromDate = props.fromDate;
|
||||
response.toDate = props.toDate;
|
||||
response.fromDate = this.outputDatetimeTransformer.fromDate(
|
||||
{
|
||||
date: props.fromDate,
|
||||
time: props.schedule[0].time,
|
||||
coordinates: props.waypoints[0].address.coordinates,
|
||||
},
|
||||
props.frequency,
|
||||
);
|
||||
response.toDate = this.outputDatetimeTransformer.toDate(
|
||||
props.toDate,
|
||||
{
|
||||
date: props.fromDate,
|
||||
time: props.schedule[0].time,
|
||||
coordinates: props.waypoints[0].address.coordinates,
|
||||
},
|
||||
props.frequency,
|
||||
);
|
||||
response.schedule = props.schedule.map(
|
||||
(scheduleItem: ScheduleItemProps) => ({
|
||||
day: scheduleItem.day,
|
||||
time: scheduleItem.time,
|
||||
day: this.outputDatetimeTransformer.day(
|
||||
scheduleItem.day,
|
||||
{
|
||||
date: props.fromDate,
|
||||
time: scheduleItem.time,
|
||||
coordinates: props.waypoints[0].address.coordinates,
|
||||
},
|
||||
props.frequency,
|
||||
),
|
||||
time: this.outputDatetimeTransformer.time(
|
||||
{
|
||||
date: props.fromDate,
|
||||
time: scheduleItem.time,
|
||||
coordinates: props.waypoints[0].address.coordinates,
|
||||
},
|
||||
props.frequency,
|
||||
),
|
||||
margin: scheduleItem.margin,
|
||||
}),
|
||||
);
|
||||
|
|
|
@ -4,7 +4,8 @@ import { CqrsModule } from '@nestjs/cqrs';
|
|||
import {
|
||||
AD_MESSAGE_PUBLISHER,
|
||||
AD_REPOSITORY,
|
||||
DATETIME_TRANSFORMER,
|
||||
INPUT_DATETIME_TRANSFORMER,
|
||||
OUTPUT_DATETIME_TRANSFORMER,
|
||||
PARAMS_PROVIDER,
|
||||
TIMEZONE_FINDER,
|
||||
TIME_CONVERTER,
|
||||
|
@ -20,7 +21,8 @@ import { FindAdByIdQueryHandler } from './core/application/queries/find-ad-by-id
|
|||
import { PublishMessageWhenAdIsCreatedDomainEventHandler } from './core/application/event-handlers/publish-message-when-ad-is-created.domain-event-handler';
|
||||
import { PrismaService } from './infrastructure/prisma.service';
|
||||
import { MessageBrokerPublisher } from '@mobicoop/message-broker-module';
|
||||
import { DateTimeTransformer } from './infrastructure/datetime-transformer';
|
||||
import { InputDateTimeTransformer } from './infrastructure/input-datetime-transformer';
|
||||
import { OutputDateTimeTransformer } from './infrastructure/output-datetime-transformer';
|
||||
|
||||
const grpcControllers = [CreateAdGrpcController, FindAdByIdGrpcController];
|
||||
|
||||
|
@ -63,8 +65,12 @@ const adapters: Provider[] = [
|
|||
useClass: TimeConverter,
|
||||
},
|
||||
{
|
||||
provide: DATETIME_TRANSFORMER,
|
||||
useClass: DateTimeTransformer,
|
||||
provide: INPUT_DATETIME_TRANSFORMER,
|
||||
useClass: InputDateTimeTransformer,
|
||||
},
|
||||
{
|
||||
provide: OUTPUT_DATETIME_TRANSFORMER,
|
||||
useClass: OutputDateTimeTransformer,
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import { CreateAdCommand } from './create-ad.command';
|
|||
import { Inject } from '@nestjs/common';
|
||||
import {
|
||||
AD_REPOSITORY,
|
||||
DATETIME_TRANSFORMER,
|
||||
INPUT_DATETIME_TRANSFORMER,
|
||||
PARAMS_PROVIDER,
|
||||
} from '@modules/ad/ad.di-tokens';
|
||||
import { AdEntity } from '@modules/ad/core/domain/ad.entity';
|
||||
|
@ -25,7 +25,7 @@ export class CreateAdService implements ICommandHandler {
|
|||
private readonly repository: AdRepositoryPort,
|
||||
@Inject(PARAMS_PROVIDER)
|
||||
private readonly defaultParamsProvider: DefaultParamsProviderPort,
|
||||
@Inject(DATETIME_TRANSFORMER)
|
||||
@Inject(INPUT_DATETIME_TRANSFORMER)
|
||||
private readonly datetimeTransformer: DateTimeTransformerPort,
|
||||
) {
|
||||
this._defaultParams = defaultParamsProvider.getParams();
|
||||
|
|
|
@ -1,10 +1,18 @@
|
|||
export interface TimeConverterPort {
|
||||
localStringTimeToUtcStringTime(time: string, timezone: string): string;
|
||||
utcStringTimeToLocalStringTime(time: string, timezone: string): string;
|
||||
localStringDateTimeToUtcDate(
|
||||
date: string,
|
||||
time: string,
|
||||
timezone: string,
|
||||
dst?: boolean,
|
||||
): Date;
|
||||
utcStringDateTimeToLocalIsoString(
|
||||
date: string,
|
||||
time: string,
|
||||
timezone: string,
|
||||
dst?: boolean,
|
||||
): string;
|
||||
utcUnixEpochDayFromTime(time: string, timezone: string): number;
|
||||
localUnixEpochDayFromTime(time: string, timezone: string): number;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,9 @@ export class FindAdByIdQueryHandler implements IQueryHandler {
|
|||
private readonly repository: AdRepositoryPort,
|
||||
) {}
|
||||
async execute(query: FindAdByIdQuery): Promise<AdEntity> {
|
||||
return await this.repository.findOneById(query.id, { waypoints: true });
|
||||
return await this.repository.findOneById(query.id, {
|
||||
waypoints: true,
|
||||
schedule: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ import { TimezoneFinderPort } from '../core/application/ports/timezone-finder.po
|
|||
import { DefaultParamsProviderPort } from '../core/application/ports/default-params-provider.port';
|
||||
|
||||
@Injectable()
|
||||
export class DateTimeTransformer implements DateTimeTransformerPort {
|
||||
export class InputDateTimeTransformer implements DateTimeTransformerPort {
|
||||
private readonly _defaultTimezone: string;
|
||||
constructor(
|
||||
@Inject(PARAMS_PROVIDER)
|
|
@ -0,0 +1,116 @@
|
|||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import {
|
||||
DateTimeTransformerPort,
|
||||
Frequency,
|
||||
GeoDateTime,
|
||||
} from '../core/application/ports/datetime-transformer.port';
|
||||
import { TimeConverterPort } from '../core/application/ports/time-converter.port';
|
||||
import { TIMEZONE_FINDER, TIME_CONVERTER } from '../ad.di-tokens';
|
||||
import { TimezoneFinderPort } from '../core/application/ports/timezone-finder.port';
|
||||
|
||||
@Injectable()
|
||||
export class OutputDateTimeTransformer implements DateTimeTransformerPort {
|
||||
constructor(
|
||||
@Inject(TIMEZONE_FINDER)
|
||||
private readonly timezoneFinder: TimezoneFinderPort,
|
||||
@Inject(TIME_CONVERTER) private readonly timeConverter: TimeConverterPort,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Compute the fromDate : if an ad is punctual, the departure date
|
||||
* is converted from UTC to the local date with the time and timezone
|
||||
*/
|
||||
fromDate = (geoFromDate: GeoDateTime, frequency: Frequency): string => {
|
||||
if (frequency === Frequency.RECURRENT) return geoFromDate.date;
|
||||
return this.timeConverter
|
||||
.utcStringDateTimeToLocalIsoString(
|
||||
geoFromDate.date,
|
||||
geoFromDate.time,
|
||||
this.timezoneFinder.timezones(
|
||||
geoFromDate.coordinates.lon,
|
||||
geoFromDate.coordinates.lat,
|
||||
)[0],
|
||||
)
|
||||
.split('T')[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the toDate depending on frequency, time and timezone :
|
||||
* if the ad is punctual, the toDate is equal to the fromDate
|
||||
*/
|
||||
toDate = (
|
||||
toDate: string,
|
||||
geoFromDate: GeoDateTime,
|
||||
frequency: Frequency,
|
||||
): string => {
|
||||
if (frequency === Frequency.RECURRENT) return toDate;
|
||||
return this.fromDate(geoFromDate, frequency);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the day for a schedule item :
|
||||
* - if the ad is punctual, the day is infered from fromDate
|
||||
* - if the ad is recurrent, the day is computed by converting the time from utc to local time
|
||||
*/
|
||||
day = (
|
||||
day: number,
|
||||
geoFromDate: GeoDateTime,
|
||||
frequency: Frequency,
|
||||
): number => {
|
||||
if (frequency === Frequency.RECURRENT)
|
||||
return this.recurrentDay(
|
||||
day,
|
||||
geoFromDate.time,
|
||||
this.timezoneFinder.timezones(
|
||||
geoFromDate.coordinates.lon,
|
||||
geoFromDate.coordinates.lat,
|
||||
)[0],
|
||||
);
|
||||
return new Date(this.fromDate(geoFromDate, frequency)).getDay();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the utc time
|
||||
*/
|
||||
time = (geoFromDate: GeoDateTime, frequency: Frequency): string => {
|
||||
if (frequency === Frequency.RECURRENT)
|
||||
return this.timeConverter.utcStringTimeToLocalStringTime(
|
||||
geoFromDate.time,
|
||||
this.timezoneFinder.timezones(
|
||||
geoFromDate.coordinates.lon,
|
||||
geoFromDate.coordinates.lat,
|
||||
)[0],
|
||||
);
|
||||
return this.timeConverter
|
||||
.utcStringDateTimeToLocalIsoString(
|
||||
geoFromDate.date,
|
||||
geoFromDate.time,
|
||||
this.timezoneFinder.timezones(
|
||||
geoFromDate.coordinates.lon,
|
||||
geoFromDate.coordinates.lat,
|
||||
)[0],
|
||||
)
|
||||
.split('T')[1]
|
||||
.split(':', 2)
|
||||
.join(':');
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the day for a schedule item for a recurrent ad
|
||||
* The day may change when transforming from utc to local timezone
|
||||
*/
|
||||
private recurrentDay = (
|
||||
day: number,
|
||||
time: string,
|
||||
timezone: string,
|
||||
): number => {
|
||||
const unixEpochDay = 4; // 1970-01-01 is a thursday !
|
||||
const localBaseDay = this.timeConverter.localUnixEpochDayFromTime(
|
||||
time,
|
||||
timezone,
|
||||
);
|
||||
if (unixEpochDay == localBaseDay) return day;
|
||||
if (unixEpochDay > localBaseDay) return day > 0 ? day - 1 : 6;
|
||||
return day < 6 ? day + 1 : 0;
|
||||
};
|
||||
}
|
|
@ -17,6 +17,17 @@ export class TimeConverter implements TimeConverterPort {
|
|||
}
|
||||
};
|
||||
|
||||
utcStringTimeToLocalStringTime = (time: string, timezone: string): string => {
|
||||
try {
|
||||
if (!time || !timezone) throw new Error();
|
||||
return new DateTime(`${this.UNIX_EPOCH}T${time}`, TimeZone.zone('UTC'))
|
||||
.convert(TimeZone.zone(timezone))
|
||||
.format('HH:mm');
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
localStringDateTimeToUtcDate = (
|
||||
date: string,
|
||||
time: string,
|
||||
|
@ -24,7 +35,7 @@ export class TimeConverter implements TimeConverterPort {
|
|||
dst = true,
|
||||
): Date => {
|
||||
try {
|
||||
if (!time || !timezone) throw new Error();
|
||||
if (!date || !time || !timezone) throw new Error();
|
||||
return new Date(
|
||||
new DateTime(
|
||||
`${date}T${time}`,
|
||||
|
@ -36,6 +47,22 @@ export class TimeConverter implements TimeConverterPort {
|
|||
}
|
||||
};
|
||||
|
||||
utcStringDateTimeToLocalIsoString = (
|
||||
date: string,
|
||||
time: string,
|
||||
timezone: string,
|
||||
dst?: boolean,
|
||||
): string => {
|
||||
try {
|
||||
if (!date || !time || !timezone) throw new Error();
|
||||
return new DateTime(`${date}T${time}`, TimeZone.zone('UTC'))
|
||||
.convert(TimeZone.zone(timezone, dst))
|
||||
.toIsoString();
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
utcUnixEpochDayFromTime = (time: string, timezone: string): number => {
|
||||
try {
|
||||
if (!time || !timezone) throw new Error();
|
||||
|
@ -52,4 +79,18 @@ export class TimeConverter implements TimeConverterPort {
|
|||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
localUnixEpochDayFromTime = (time: string, timezone: string): number => {
|
||||
try {
|
||||
if (!time || !timezone) throw new Error();
|
||||
return new Date(
|
||||
new DateTime(`${this.UNIX_EPOCH}T${time}`, TimeZone.zone('UTC'))
|
||||
.convert(TimeZone.zone(timezone))
|
||||
.toIsoString()
|
||||
.split('T')[0],
|
||||
).getDay();
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -37,8 +37,8 @@ message ScheduleItem {
|
|||
|
||||
message Waypoint {
|
||||
int32 position = 1;
|
||||
float lon = 2;
|
||||
float lat = 3;
|
||||
double lon = 2;
|
||||
double lat = 3;
|
||||
string name = 4;
|
||||
string houseNumber = 5;
|
||||
string street = 6;
|
||||
|
|
|
@ -1,17 +1,9 @@
|
|||
import { Transform } from 'class-transformer';
|
||||
import { IsLatitude, IsLongitude } from 'class-validator';
|
||||
import { toPrecision } from './transformers/to-precision';
|
||||
|
||||
export class CoordinatesDto {
|
||||
@Transform(({ value }) => toPrecision(value, 6), {
|
||||
toClassOnly: true,
|
||||
})
|
||||
@IsLongitude()
|
||||
lon: number;
|
||||
|
||||
@Transform(({ value }) => toPrecision(value, 6), {
|
||||
toClassOnly: true,
|
||||
})
|
||||
@IsLatitude()
|
||||
lat: number;
|
||||
}
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
import { Frequency } from '@modules/ad/core/domain/ad.types';
|
||||
|
||||
export const intToFrequency = (frequencyAsInt: number): Frequency => {
|
||||
if (frequencyAsInt == 1) return Frequency.PUNCTUAL;
|
||||
if (frequencyAsInt == 2) return Frequency.RECURRENT;
|
||||
throw new Error('Unknown frequency value');
|
||||
};
|
|
@ -1,4 +0,0 @@
|
|||
export const toPrecision = (input: number, precision: number): number => {
|
||||
const multiplier = 10 ** precision;
|
||||
return Math.round((input + Number.EPSILON) * multiplier) / multiplier;
|
||||
};
|
|
@ -23,7 +23,7 @@ export class FindAdByIdGrpcController {
|
|||
private readonly queryBus: QueryBus,
|
||||
) {}
|
||||
|
||||
@GrpcMethod('AdsService', 'FindOneById')
|
||||
@GrpcMethod('AdService', 'FindOneById')
|
||||
async findOnebyId(data: FindAdByIdRequestDto): Promise<AdResponseDto> {
|
||||
try {
|
||||
const ad: AdEntity = await this.queryBus.execute(
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import { OUTPUT_DATETIME_TRANSFORMER } from '@modules/ad/ad.di-tokens';
|
||||
import { AdMapper } from '@modules/ad/ad.mapper';
|
||||
import { DateTimeTransformerPort } from '@modules/ad/core/application/ports/datetime-transformer.port';
|
||||
import { AdEntity } from '@modules/ad/core/domain/ad.entity';
|
||||
import { Frequency } from '@modules/ad/core/domain/ad.types';
|
||||
import {
|
||||
|
@ -111,12 +113,25 @@ const adReadModel: AdReadModel = {
|
|||
updatedAt: now,
|
||||
};
|
||||
|
||||
const mockOutputDatetimeTransformer: DateTimeTransformerPort = {
|
||||
fromDate: jest.fn(),
|
||||
toDate: jest.fn(),
|
||||
day: jest.fn(),
|
||||
time: jest.fn(),
|
||||
};
|
||||
|
||||
describe('Ad Mapper', () => {
|
||||
let adMapper: AdMapper;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
providers: [AdMapper],
|
||||
providers: [
|
||||
AdMapper,
|
||||
{
|
||||
provide: OUTPUT_DATETIME_TRANSFORMER,
|
||||
useValue: mockOutputDatetimeTransformer,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
adMapper = module.get<AdMapper>(AdMapper);
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import {
|
||||
AD_REPOSITORY,
|
||||
DATETIME_TRANSFORMER,
|
||||
INPUT_DATETIME_TRANSFORMER,
|
||||
PARAMS_PROVIDER,
|
||||
} from '@modules/ad/ad.di-tokens';
|
||||
import { WaypointDto } from '@modules/ad/interface/grpc-controllers/dtos/waypoint.dto';
|
||||
|
@ -76,7 +76,7 @@ const mockDefaultParamsProvider: DefaultParamsProviderPort = {
|
|||
},
|
||||
};
|
||||
|
||||
const mockDateTimeTransformer: DateTimeTransformerPort = {
|
||||
const mockInputDateTimeTransformer: DateTimeTransformerPort = {
|
||||
fromDate: jest.fn(),
|
||||
toDate: jest.fn(),
|
||||
day: jest.fn(),
|
||||
|
@ -98,8 +98,8 @@ describe('create-ad.service', () => {
|
|||
useValue: mockDefaultParamsProvider,
|
||||
},
|
||||
{
|
||||
provide: DATETIME_TRANSFORMER,
|
||||
useValue: mockDateTimeTransformer,
|
||||
provide: INPUT_DATETIME_TRANSFORMER,
|
||||
useValue: mockInputDateTimeTransformer,
|
||||
},
|
||||
CreateAdService,
|
||||
],
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import { OUTPUT_DATETIME_TRANSFORMER } from '@modules/ad/ad.di-tokens';
|
||||
import { AdMapper } from '@modules/ad/ad.mapper';
|
||||
import { DateTimeTransformerPort } from '@modules/ad/core/application/ports/datetime-transformer.port';
|
||||
import { AdRepository } from '@modules/ad/infrastructure/ad.repository';
|
||||
import { PrismaService } from '@modules/ad/infrastructure/prisma.service';
|
||||
import { EventEmitter2, EventEmitterModule } from '@nestjs/event-emitter';
|
||||
|
@ -8,6 +10,13 @@ const mockMessagePublisher = {
|
|||
publish: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const mockOutputDatetimeTransformer: DateTimeTransformerPort = {
|
||||
fromDate: jest.fn(),
|
||||
toDate: jest.fn(),
|
||||
day: jest.fn(),
|
||||
time: jest.fn(),
|
||||
};
|
||||
|
||||
describe('Ad repository', () => {
|
||||
let prismaService: PrismaService;
|
||||
let adMapper: AdMapper;
|
||||
|
@ -16,7 +25,14 @@ describe('Ad repository', () => {
|
|||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
imports: [EventEmitterModule.forRoot()],
|
||||
providers: [PrismaService, AdMapper],
|
||||
providers: [
|
||||
PrismaService,
|
||||
AdMapper,
|
||||
{
|
||||
provide: OUTPUT_DATETIME_TRANSFORMER,
|
||||
useValue: mockOutputDatetimeTransformer,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
prismaService = module.get<PrismaService>(PrismaService);
|
||||
|
|
|
@ -7,7 +7,7 @@ import { Frequency } from '@modules/ad/core/application/ports/datetime-transform
|
|||
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 { DateTimeTransformer } from '@modules/ad/infrastructure/datetime-transformer';
|
||||
import { InputDateTimeTransformer } from '@modules/ad/infrastructure/input-datetime-transformer';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
const mockDefaultParamsProvider: DefaultParamsProviderPort = {
|
||||
|
@ -32,12 +32,14 @@ const mockTimeConverter: TimeConverterPort = {
|
|||
localStringTimeToUtcStringTime: jest
|
||||
.fn()
|
||||
.mockImplementationOnce(() => '00:15'),
|
||||
utcStringTimeToLocalStringTime: jest.fn(),
|
||||
localStringDateTimeToUtcDate: jest
|
||||
.fn()
|
||||
.mockImplementationOnce(() => new Date('2023-07-30T06:15:00.000Z'))
|
||||
.mockImplementationOnce(() => new Date('2023-07-20T08:15:00.000Z'))
|
||||
.mockImplementationOnce(() => new Date('2023-07-19T23:15:00.000Z'))
|
||||
.mockImplementationOnce(() => new Date('2023-07-19T23:15:00.000Z')),
|
||||
utcStringDateTimeToLocalIsoString: jest.fn(),
|
||||
utcUnixEpochDayFromTime: jest
|
||||
.fn()
|
||||
.mockImplementationOnce(() => 4)
|
||||
|
@ -45,10 +47,11 @@ const mockTimeConverter: TimeConverterPort = {
|
|||
.mockImplementationOnce(() => 3)
|
||||
.mockImplementationOnce(() => 5)
|
||||
.mockImplementationOnce(() => 5),
|
||||
localUnixEpochDayFromTime: jest.fn(),
|
||||
};
|
||||
|
||||
describe('Datetime Transformer', () => {
|
||||
let datetimeTransformer: DateTimeTransformer;
|
||||
describe('Input Datetime Transformer', () => {
|
||||
let inputDatetimeTransformer: InputDateTimeTransformer;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
|
@ -65,20 +68,22 @@ describe('Datetime Transformer', () => {
|
|||
provide: TIME_CONVERTER,
|
||||
useValue: mockTimeConverter,
|
||||
},
|
||||
DateTimeTransformer,
|
||||
InputDateTimeTransformer,
|
||||
],
|
||||
}).compile();
|
||||
|
||||
datetimeTransformer = module.get<DateTimeTransformer>(DateTimeTransformer);
|
||||
inputDatetimeTransformer = module.get<InputDateTimeTransformer>(
|
||||
InputDateTimeTransformer,
|
||||
);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(datetimeTransformer).toBeDefined();
|
||||
expect(inputDatetimeTransformer).toBeDefined();
|
||||
});
|
||||
|
||||
describe('fromDate', () => {
|
||||
it('should return fromDate as is if frequency is recurrent', () => {
|
||||
const transformedFromDate: string = datetimeTransformer.fromDate(
|
||||
const transformedFromDate: string = inputDatetimeTransformer.fromDate(
|
||||
{
|
||||
date: '2023-07-30',
|
||||
time: '07:15',
|
||||
|
@ -92,7 +97,7 @@ describe('Datetime Transformer', () => {
|
|||
expect(transformedFromDate).toBe('2023-07-30');
|
||||
});
|
||||
it('should return transformed fromDate if frequency is punctual and coordinates are those of Nancy', () => {
|
||||
const transformedFromDate: string = datetimeTransformer.fromDate(
|
||||
const transformedFromDate: string = inputDatetimeTransformer.fromDate(
|
||||
{
|
||||
date: '2023-07-30',
|
||||
time: '07:15',
|
||||
|
@ -109,7 +114,7 @@ describe('Datetime Transformer', () => {
|
|||
|
||||
describe('toDate', () => {
|
||||
it('should return toDate as is if frequency is recurrent', () => {
|
||||
const transformedToDate: string = datetimeTransformer.toDate(
|
||||
const transformedToDate: string = inputDatetimeTransformer.toDate(
|
||||
'2024-07-29',
|
||||
{
|
||||
date: '2023-07-20',
|
||||
|
@ -124,7 +129,7 @@ describe('Datetime Transformer', () => {
|
|||
expect(transformedToDate).toBe('2024-07-29');
|
||||
});
|
||||
it('should return transformed fromDate if frequency is punctual', () => {
|
||||
const transformedToDate: string = datetimeTransformer.toDate(
|
||||
const transformedToDate: string = inputDatetimeTransformer.toDate(
|
||||
'2024-07-30',
|
||||
{
|
||||
date: '2023-07-20',
|
||||
|
@ -142,7 +147,7 @@ describe('Datetime Transformer', () => {
|
|||
|
||||
describe('day', () => {
|
||||
it('should not change day if frequency is recurrent and converted UTC time is on the same day', () => {
|
||||
const day: number = datetimeTransformer.day(
|
||||
const day: number = inputDatetimeTransformer.day(
|
||||
1,
|
||||
{
|
||||
date: '2023-07-24',
|
||||
|
@ -157,7 +162,7 @@ describe('Datetime Transformer', () => {
|
|||
expect(day).toBe(1);
|
||||
});
|
||||
it('should change day if frequency is recurrent and converted UTC time is on the previous day', () => {
|
||||
const day: number = datetimeTransformer.day(
|
||||
const day: number = inputDatetimeTransformer.day(
|
||||
1,
|
||||
{
|
||||
date: '2023-07-24',
|
||||
|
@ -172,7 +177,7 @@ describe('Datetime Transformer', () => {
|
|||
expect(day).toBe(0);
|
||||
});
|
||||
it('should change day if frequency is recurrent and converted UTC time is on the previous day and given day is sunday', () => {
|
||||
const day: number = datetimeTransformer.day(
|
||||
const day: number = inputDatetimeTransformer.day(
|
||||
0,
|
||||
{
|
||||
date: '2023-07-23',
|
||||
|
@ -187,7 +192,7 @@ describe('Datetime Transformer', () => {
|
|||
expect(day).toBe(6);
|
||||
});
|
||||
it('should change day if frequency is recurrent and converted UTC time is on the next day', () => {
|
||||
const day: number = datetimeTransformer.day(
|
||||
const day: number = inputDatetimeTransformer.day(
|
||||
1,
|
||||
{
|
||||
date: '2023-07-24',
|
||||
|
@ -202,7 +207,7 @@ describe('Datetime Transformer', () => {
|
|||
expect(day).toBe(2);
|
||||
});
|
||||
it('should change day if frequency is recurrent and converted UTC time is on the next day and given day is saturday(6)', () => {
|
||||
const day: number = datetimeTransformer.day(
|
||||
const day: number = inputDatetimeTransformer.day(
|
||||
6,
|
||||
{
|
||||
date: '2023-07-29',
|
||||
|
@ -217,7 +222,7 @@ describe('Datetime Transformer', () => {
|
|||
expect(day).toBe(0);
|
||||
});
|
||||
it('should return utc fromDate day if frequency is punctual', () => {
|
||||
const day: number = datetimeTransformer.day(
|
||||
const day: number = inputDatetimeTransformer.day(
|
||||
1,
|
||||
{
|
||||
date: '2023-07-20',
|
||||
|
@ -235,7 +240,7 @@ describe('Datetime Transformer', () => {
|
|||
|
||||
describe('time', () => {
|
||||
it('should transform given time to utc time if frequency is recurrent', () => {
|
||||
const time: string = datetimeTransformer.time(
|
||||
const time: string = inputDatetimeTransformer.time(
|
||||
{
|
||||
date: '2023-07-24',
|
||||
time: '01:15',
|
||||
|
@ -249,7 +254,7 @@ describe('Datetime Transformer', () => {
|
|||
expect(time).toBe('00:15');
|
||||
});
|
||||
it('should return given time to utc time if frequency is punctual', () => {
|
||||
const time: string = datetimeTransformer.time(
|
||||
const time: string = inputDatetimeTransformer.time(
|
||||
{
|
||||
date: '2023-07-24',
|
||||
time: '01:15',
|
|
@ -54,6 +54,54 @@ describe('Time Converter', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('utcStringTimeToLocalStringTime', () => {
|
||||
it('should convert a utc time to a paris time', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const utcTime = '07:00';
|
||||
const parisTime = timeConverter.utcStringTimeToLocalStringTime(
|
||||
utcTime,
|
||||
'Europe/Paris',
|
||||
);
|
||||
expect(parisTime).toBe('08:00');
|
||||
});
|
||||
it('should return undefined if time is invalid', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const utcTime = '27:00';
|
||||
const parisTime = timeConverter.utcStringTimeToLocalStringTime(
|
||||
utcTime,
|
||||
'Europe/Paris',
|
||||
);
|
||||
expect(parisTime).toBeUndefined();
|
||||
});
|
||||
it('should return undefined if time is undefined', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const utcTime = undefined;
|
||||
const parisTime = timeConverter.utcStringTimeToLocalStringTime(
|
||||
utcTime,
|
||||
'Europe/Paris',
|
||||
);
|
||||
expect(parisTime).toBeUndefined();
|
||||
});
|
||||
it('should return undefined if timezone is invalid', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const utcTime = '07:00';
|
||||
const parisTime = timeConverter.utcStringTimeToLocalStringTime(
|
||||
utcTime,
|
||||
'Foo/Bar',
|
||||
);
|
||||
expect(parisTime).toBeUndefined();
|
||||
});
|
||||
it('should return undefined if timezone is undefined', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const utcTime = '07:00';
|
||||
const parisTime = timeConverter.utcStringTimeToLocalStringTime(
|
||||
utcTime,
|
||||
undefined,
|
||||
);
|
||||
expect(parisTime).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('localStringDateTimeToUtcDate', () => {
|
||||
it('should convert a summer paris date and time to a utc date', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
|
@ -111,6 +159,28 @@ describe('Time Converter', () => {
|
|||
);
|
||||
expect(utcDate.toISOString()).toBe('2023-02-03T01:00:00.000Z');
|
||||
});
|
||||
it('should return undefined if date is invalid', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const parisDate = '2023-06-32';
|
||||
const parisTime = '08:00';
|
||||
const utcDate = timeConverter.localStringDateTimeToUtcDate(
|
||||
parisDate,
|
||||
parisTime,
|
||||
'Europe/Paris',
|
||||
);
|
||||
expect(utcDate).toBeUndefined();
|
||||
});
|
||||
it('should return undefined if date is undefined', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const parisDate = undefined;
|
||||
const parisTime = '08:00';
|
||||
const utcDate = timeConverter.localStringDateTimeToUtcDate(
|
||||
parisDate,
|
||||
parisTime,
|
||||
'Europe/Paris',
|
||||
);
|
||||
expect(utcDate).toBeUndefined();
|
||||
});
|
||||
it('should return undefined if time is invalid', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const parisDate = '2023-06-22';
|
||||
|
@ -157,7 +227,132 @@ describe('Time Converter', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('utcBaseDayFromTime', () => {
|
||||
describe('utcStringDateTimeToLocalIsoString', () => {
|
||||
it('should convert a utc string date and time to a summer paris date isostring', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const utcDate = '2023-06-22';
|
||||
const utcTime = '10:00';
|
||||
const localIsoString = timeConverter.utcStringDateTimeToLocalIsoString(
|
||||
utcDate,
|
||||
utcTime,
|
||||
'Europe/Paris',
|
||||
);
|
||||
expect(localIsoString).toBe('2023-06-22T12:00:00.000+02:00');
|
||||
});
|
||||
it('should convert a utc string date and time to a winter paris date isostring', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const utcDate = '2023-02-02';
|
||||
const utcTime = '10:00';
|
||||
const localIsoString = timeConverter.utcStringDateTimeToLocalIsoString(
|
||||
utcDate,
|
||||
utcTime,
|
||||
'Europe/Paris',
|
||||
);
|
||||
expect(localIsoString).toBe('2023-02-02T11:00:00.000+01:00');
|
||||
});
|
||||
it('should convert a utc string date and time to a summer paris date isostring without dst', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const utcDate = '2023-06-22';
|
||||
const utcTime = '10:00';
|
||||
const localIsoString = timeConverter.utcStringDateTimeToLocalIsoString(
|
||||
utcDate,
|
||||
utcTime,
|
||||
'Europe/Paris',
|
||||
false,
|
||||
);
|
||||
expect(localIsoString).toBe('2023-06-22T11:00:00.000+01:00');
|
||||
});
|
||||
it('should convert a utc date to a tonga date isostring', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const utcDate = '2023-02-01';
|
||||
const utcTime = '23:00';
|
||||
const localIsoString = timeConverter.utcStringDateTimeToLocalIsoString(
|
||||
utcDate,
|
||||
utcTime,
|
||||
'Pacific/Tongatapu',
|
||||
);
|
||||
expect(localIsoString).toBe('2023-02-02T12:00:00.000+13:00');
|
||||
});
|
||||
it('should convert a utc date to a papeete date isostring', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const utcDate = '2023-02-03';
|
||||
const utcTime = '01:00';
|
||||
const localIsoString = timeConverter.utcStringDateTimeToLocalIsoString(
|
||||
utcDate,
|
||||
utcTime,
|
||||
'Pacific/Tahiti',
|
||||
);
|
||||
expect(localIsoString).toBe('2023-02-02T15:00:00.000-10:00');
|
||||
});
|
||||
it('should return undefined if date is invalid', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const utcDate = '2023-06-32';
|
||||
const utcTime = '07:00';
|
||||
const parisTime = timeConverter.utcStringDateTimeToLocalIsoString(
|
||||
utcDate,
|
||||
utcTime,
|
||||
'Europe/Paris',
|
||||
);
|
||||
expect(parisTime).toBeUndefined();
|
||||
});
|
||||
it('should return undefined if date is undefined', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const utcDate = undefined;
|
||||
const utcTime = '07:00';
|
||||
const parisTime = timeConverter.utcStringDateTimeToLocalIsoString(
|
||||
utcDate,
|
||||
utcTime,
|
||||
'Europe/Paris',
|
||||
);
|
||||
expect(parisTime).toBeUndefined();
|
||||
});
|
||||
it('should return undefined if time is invalid', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const utcDate = '2023-06-22';
|
||||
const utcTime = '27:00';
|
||||
const parisTime = timeConverter.utcStringDateTimeToLocalIsoString(
|
||||
utcDate,
|
||||
utcTime,
|
||||
'Europe/Paris',
|
||||
);
|
||||
expect(parisTime).toBeUndefined();
|
||||
});
|
||||
it('should return undefined if time is undefined', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const utcDate = '2023-06-22';
|
||||
const utcTime = undefined;
|
||||
const parisTime = timeConverter.utcStringDateTimeToLocalIsoString(
|
||||
utcDate,
|
||||
utcTime,
|
||||
'Europe/Paris',
|
||||
);
|
||||
expect(parisTime).toBeUndefined();
|
||||
});
|
||||
it('should return undefined if timezone is invalid', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const utcDate = '2023-06-22';
|
||||
const utcTime = '07:00';
|
||||
const parisTime = timeConverter.utcStringDateTimeToLocalIsoString(
|
||||
utcDate,
|
||||
utcTime,
|
||||
'Foo/Bar',
|
||||
);
|
||||
expect(parisTime).toBeUndefined();
|
||||
});
|
||||
it('should return undefined if timezone is undefined', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const utcDate = '2023-06-22';
|
||||
const utcTime = '07:00';
|
||||
const parisTime = timeConverter.utcStringDateTimeToLocalIsoString(
|
||||
utcDate,
|
||||
utcTime,
|
||||
undefined,
|
||||
);
|
||||
expect(parisTime).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('utcUnixEpochDayFromTime', () => {
|
||||
it('should get the utc day of paris at 12:00', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
expect(
|
||||
|
@ -201,4 +396,49 @@ describe('Time Converter', () => {
|
|||
).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('localUnixEpochDayFromTime', () => {
|
||||
it('should get the day of paris at 12:00 utc', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
expect(
|
||||
timeConverter.localUnixEpochDayFromTime('12:00', 'Europe/Paris'),
|
||||
).toBe(4);
|
||||
});
|
||||
it('should get the day of paris at 23:00 utc', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
expect(
|
||||
timeConverter.localUnixEpochDayFromTime('23:00', 'Europe/Paris'),
|
||||
).toBe(5);
|
||||
});
|
||||
it('should get the day of papeete at 05:00 utc', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
expect(
|
||||
timeConverter.localUnixEpochDayFromTime('05:00', 'Pacific/Tahiti'),
|
||||
).toBe(3);
|
||||
});
|
||||
it('should return undefined if time is invalid', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
expect(
|
||||
timeConverter.localUnixEpochDayFromTime('28:00', 'Europe/Paris'),
|
||||
).toBeUndefined();
|
||||
});
|
||||
it('should return undefined if time is undefined', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
expect(
|
||||
timeConverter.localUnixEpochDayFromTime(undefined, 'Europe/Paris'),
|
||||
).toBeUndefined();
|
||||
});
|
||||
it('should return undefined if timezone is invalid', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
expect(
|
||||
timeConverter.localUnixEpochDayFromTime('12:00', 'Foo/Bar'),
|
||||
).toBeUndefined();
|
||||
});
|
||||
it('should return undefined if timezone is undefined', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
expect(
|
||||
timeConverter.localUnixEpochDayFromTime('12:00', undefined),
|
||||
).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
import { Frequency } from '@modules/ad/core/domain/ad.types';
|
||||
import { intToFrequency } from '@modules/ad/interface/grpc-controllers/dtos/transformers/int-to-frequency';
|
||||
|
||||
describe('frequency mapping', () => {
|
||||
it('should return punctual if frequency is 1', () => {
|
||||
expect(intToFrequency(1)).toBe(Frequency.PUNCTUAL);
|
||||
});
|
||||
it('should return recurrent if frequency is 2', () => {
|
||||
expect(intToFrequency(2)).toBe(Frequency.RECURRENT);
|
||||
});
|
||||
it('should throw an error if frequency is unknown', () => {
|
||||
expect(() => intToFrequency(0)).toThrow();
|
||||
expect(() => intToFrequency(3)).toThrow();
|
||||
});
|
||||
});
|
|
@ -1,14 +0,0 @@
|
|||
import { toPrecision } from '@modules/ad/interface/grpc-controllers/dtos/transformers/to-precision';
|
||||
|
||||
describe('precision handler', () => {
|
||||
it('should return a 6 digits float number for a 10 digits float input number and 6 as precision', () => {
|
||||
const precised = toPrecision(1.1234567891, 6);
|
||||
const stringPrecised = precised.toString().split('.')[1];
|
||||
expect(stringPrecised.length).toBe(6);
|
||||
});
|
||||
it('should return a 2 digits float number for a 2 digits float input number and 4 as precision', () => {
|
||||
const precised = toPrecision(1.12, 4);
|
||||
const stringPrecised = precised.toString().split('.')[1];
|
||||
expect(stringPrecised.length).toBe(2);
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue