Merge branch 'refactor' into 'main'

refactor and fixes

See merge request v3/service/ad!6
This commit is contained in:
Sylvain Briat 2023-06-06 09:13:25 +00:00
commit 2754c36132
11 changed files with 51 additions and 99 deletions

View File

@ -4,5 +4,5 @@ import { Ad } from '../../domain/entities/ad';
//TODO : properly implement mutate operation to prisma //TODO : properly implement mutate operation to prisma
@Injectable() @Injectable()
export class AdsRepository extends AdRepository<Ad> { export class AdsRepository extends AdRepository<Ad> {
protected _model = 'ad'; protected model = 'ad';
} }

View File

@ -6,20 +6,18 @@ import { IProvideParams } from '../../domain/interfaces/param-provider.interface
@Injectable() @Injectable()
export class DefaultParamsProvider implements IProvideParams { export class DefaultParamsProvider implements IProvideParams {
constructor(private readonly configService: ConfigService) {} constructor(private readonly configService: ConfigService) {}
getParams = (): DefaultParams => { getParams = (): DefaultParams => ({
return { MON_MARGIN: parseInt(this.configService.get('DEPARTURE_MARGIN')),
MON_MARGIN: parseInt(this.configService.get('DEPARTURE_MARGIN')), TUE_MARGIN: parseInt(this.configService.get('DEPARTURE_MARGIN')),
TUE_MARGIN: parseInt(this.configService.get('DEPARTURE_MARGIN')), WED_MARGIN: parseInt(this.configService.get('DEPARTURE_MARGIN')),
WED_MARGIN: parseInt(this.configService.get('DEPARTURE_MARGIN')), THU_MARGIN: parseInt(this.configService.get('DEPARTURE_MARGIN')),
THU_MARGIN: parseInt(this.configService.get('DEPARTURE_MARGIN')), FRI_MARGIN: parseInt(this.configService.get('DEPARTURE_MARGIN')),
FRI_MARGIN: parseInt(this.configService.get('DEPARTURE_MARGIN')), SAT_MARGIN: parseInt(this.configService.get('DEPARTURE_MARGIN')),
SAT_MARGIN: parseInt(this.configService.get('DEPARTURE_MARGIN')), SUN_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' ? true : false, SEATS_PROVIDED: parseInt(this.configService.get('SEATS_PROVIDED')),
SEATS_PROVIDED: parseInt(this.configService.get('SEATS_PROVIDED')), PASSENGER: this.configService.get('ROLE') == 'passenger',
PASSENGER: this.configService.get('ROLE') == 'passenger' ? true : false, SEATS_REQUESTED: parseInt(this.configService.get('SEATS_REQUESTED')),
SEATS_REQUESTED: parseInt(this.configService.get('SEATS_REQUESTED')), STRICT: this.configService.get('STRICT_FREQUENCY') == 'true',
STRICT: false, });
};
};
} }

View File

@ -1,130 +1,86 @@
import { AutoMap } from '@automapper/classes'; import { AutoMap } from '@automapper/classes';
import {
IsOptional,
IsString,
IsBoolean,
IsDate,
IsInt,
IsEnum,
ValidateNested,
IsUUID,
} from 'class-validator';
import { Frequency } from '../types/frequency.enum'; import { Frequency } from '../types/frequency.enum';
import { Address } from '../entities/address'; import { Address } from '../entities/address';
export class AdCreation { export class AdCreation {
@IsUUID(4)
@AutoMap() @AutoMap()
uuid: string; uuid: string;
@IsUUID(4)
@AutoMap() @AutoMap()
userUuid: string; userUuid: string;
@IsBoolean()
@AutoMap() @AutoMap()
driver: boolean; driver: boolean;
@IsBoolean()
@AutoMap() @AutoMap()
passenger: boolean; passenger: boolean;
@IsEnum(Frequency)
@AutoMap() @AutoMap()
frequency: Frequency; frequency: Frequency;
@IsDate()
@AutoMap() @AutoMap()
fromDate: Date; fromDate: Date;
@IsDate()
@AutoMap() @AutoMap()
toDate: Date; toDate: Date;
@IsOptional()
@IsDate()
@AutoMap() @AutoMap()
monTime?: string; monTime?: string;
@IsOptional()
@IsString()
@AutoMap() @AutoMap()
tueTime?: string; tueTime?: string;
@IsOptional()
@IsString()
@AutoMap() @AutoMap()
wedTime?: string; wedTime?: string;
@IsOptional()
@IsString()
@AutoMap() @AutoMap()
thuTime?: string; thuTime?: string;
@IsOptional()
@IsString()
@AutoMap() @AutoMap()
friTime?: string; friTime?: string;
@IsOptional()
@IsString()
@AutoMap() @AutoMap()
satTime?: string; satTime?: string;
@IsOptional()
@IsString()
@AutoMap() @AutoMap()
sunTime?: string; sunTime?: string;
@IsInt()
@AutoMap() @AutoMap()
monMargin: number; monMargin: number;
@IsInt()
@AutoMap() @AutoMap()
tueMargin: number; tueMargin: number;
@IsInt()
@AutoMap() @AutoMap()
wedMargin: number; wedMargin: number;
@IsInt()
@AutoMap() @AutoMap()
thuMargin: number; thuMargin: number;
@IsInt()
@AutoMap() @AutoMap()
friMargin: number; friMargin: number;
@IsInt()
@AutoMap() @AutoMap()
satMargin: number; satMargin: number;
@IsInt()
@AutoMap() @AutoMap()
sunMargin: number; sunMargin: number;
@IsInt()
@AutoMap() @AutoMap()
seatsDriver: number; seatsDriver: number;
@IsInt()
@AutoMap() @AutoMap()
seatsPassenger: number; seatsPassenger: number;
@IsBoolean()
@AutoMap() @AutoMap()
strict: boolean; strict: boolean;
@IsDate()
@AutoMap() @AutoMap()
createdAt: Date; createdAt: Date;
@IsDate()
@AutoMap() @AutoMap()
updatedAt?: Date; updatedAt?: Date;
@ValidateNested({ each: true })
@AutoMap() @AutoMap()
addresses: { create: Address[] }; addresses: { create: Address[] };
} }

View File

@ -68,6 +68,7 @@ export class CreateAdRequest {
@AutoMap() @AutoMap()
toDate?: Date; toDate?: Date;
@IsOptional()
@Type(() => ScheduleDTO) @Type(() => ScheduleDTO)
@IsPunctualOrRecurrent() @IsPunctualOrRecurrent()
@ValidateNested({ each: true }) @ValidateNested({ each: true })

View File

@ -18,7 +18,7 @@ export const IsPunctualOrRecurrent = (
isPunctualOrRecurrent(args), isPunctualOrRecurrent(args),
defaultMessage: buildMessage( defaultMessage: buildMessage(
() => () =>
`the departure, from date, to date and schedule must be properly set on reccurent or punctual ad`, `the departure, from date, to date and schedule must be properly set on recurrent or punctual ad`,
validationOptions, validationOptions,
), ),
}, },

View File

@ -31,7 +31,7 @@ export class CreateAdUseCase {
CreateAdRequest, CreateAdRequest,
AdCreation, AdCreation,
); );
this.setDefaultSchedule(); this.setDefaultMarginDurations();
this.setDefaultAddressesPosition(); this.setDefaultAddressesPosition();
this.setDefaultDriverAndPassengerParameters(); this.setDefaultDriverAndPassengerParameters();
this.setDefaultStrict(); this.setDefaultStrict();
@ -60,7 +60,7 @@ export class CreateAdUseCase {
} }
} }
private setDefaultSchedule = (): void => { private setDefaultMarginDurations = (): void => {
if (this.ad.monMargin === undefined) if (this.ad.monMargin === undefined)
this.ad.monMargin = this.defaultParams.MON_MARGIN; this.ad.monMargin = this.defaultParams.MON_MARGIN;
if (this.ad.tueMargin === undefined) if (this.ad.tueMargin === undefined)
@ -83,21 +83,19 @@ export class CreateAdUseCase {
}; };
private setDefaultDriverAndPassengerParameters = (): void => { private setDefaultDriverAndPassengerParameters = (): void => {
this.ad.driver = !!this.ad.driver;
this.ad.passenger = !!this.ad.passenger;
if (!this.ad.driver && !this.ad.passenger) { if (!this.ad.driver && !this.ad.passenger) {
this.ad.driver = this.defaultParams.DRIVER; this.ad.driver = this.defaultParams.DRIVER;
this.ad.seatsDriver = this.defaultParams.SEATS_PROVIDED; this.ad.seatsDriver = this.defaultParams.SEATS_PROVIDED;
this.ad.passenger = this.defaultParams.PASSENGER; this.ad.passenger = this.defaultParams.PASSENGER;
this.ad.seatsPassenger = this.defaultParams.SEATS_REQUESTED; this.ad.seatsPassenger = this.defaultParams.SEATS_REQUESTED;
} else { return;
if (!this.ad.driver) {
this.ad.driver = false;
this.ad.seatsDriver = 0;
}
if (!this.ad.passenger) {
this.ad.passenger = false;
this.ad.seatsPassenger = 0;
}
} }
if (!this.ad.seatsDriver || this.ad.seatsDriver <= 0)
this.ad.seatsDriver = this.defaultParams.SEATS_PROVIDED;
if (!this.ad.seatsPassenger || this.ad.seatsPassenger <= 0)
this.ad.seatsPassenger = this.defaultParams.SEATS_REQUESTED;
}; };
private setDefaultAddressesPosition = (): void => { private setDefaultAddressesPosition = (): void => {

View File

@ -21,34 +21,33 @@ export class AdProfile extends AutomapperProfile {
mapper, mapper,
CreateAdRequest, CreateAdRequest,
AdCreation, AdCreation,
forMember( forMember(
(destination) => destination.monMargin, (destination) => destination.monMargin,
mapFrom((source) => source.marginDurations.mon), mapFrom((source) => source.marginDurations?.mon),
), ),
forMember( forMember(
(destination) => destination.tueMargin, (destination) => destination.tueMargin,
mapFrom((source) => source.marginDurations.tue), mapFrom((source) => source.marginDurations?.tue),
), ),
forMember( forMember(
(destination) => destination.wedMargin, (destination) => destination.wedMargin,
mapFrom((source) => source.marginDurations.wed), mapFrom((source) => source.marginDurations?.wed),
), ),
forMember( forMember(
(destination) => destination.thuMargin, (destination) => destination.thuMargin,
mapFrom((source) => source.marginDurations.thu), mapFrom((source) => source.marginDurations?.thu),
), ),
forMember( forMember(
(destination) => destination.friMargin, (destination) => destination.friMargin,
mapFrom((source) => source.marginDurations.fri), mapFrom((source) => source.marginDurations?.fri),
), ),
forMember( forMember(
(destination) => destination.satMargin, (destination) => destination.satMargin,
mapFrom((source) => source.marginDurations.sat), mapFrom((source) => source.marginDurations?.sat),
), ),
forMember( forMember(
(destination) => destination.sunMargin, (destination) => destination.sunMargin,
mapFrom((source) => source.marginDurations.sun), mapFrom((source) => source.marginDurations?.sun),
), ),
forMember( forMember(
(destination) => destination.monTime, (destination) => destination.monTime,

View File

@ -5,7 +5,7 @@ describe('frequency mapping function ', () => {
it('should return punctual', () => { it('should return punctual', () => {
expect(intToFrequency(1)).toBe(Frequency.PUNCTUAL); expect(intToFrequency(1)).toBe(Frequency.PUNCTUAL);
}); });
it('should return recurent', () => { it('should return recurrent', () => {
expect(intToFrequency(2)).toBe(Frequency.RECURRENT); expect(intToFrequency(2)).toBe(Frequency.RECURRENT);
}); });
it('should return undefined', () => { it('should return undefined', () => {

View File

@ -1,7 +1,7 @@
import { isPunctualOrRecurrent } from '../../../domain/dtos/validators/is-punctual-or-recurrent'; import { isPunctualOrRecurrent } from '../../../domain/dtos/validators/is-punctual-or-recurrent';
import { Frequency } from '../../../domain/types/frequency.enum'; import { Frequency } from '../../../domain/types/frequency.enum';
describe('punctual or reccurent validators', () => { describe('punctual or recurrent validators', () => {
describe('punctual case ', () => { describe('punctual case ', () => {
describe('valid cases', () => { describe('valid cases', () => {
it('should validate with valid departure and empty schedule ', () => { it('should validate with valid departure and empty schedule ', () => {
@ -55,7 +55,7 @@ describe('punctual or reccurent validators', () => {
}); });
}); });
}); });
describe('reccurent case ', () => { describe('recurrent case ', () => {
describe('valid cases', () => { describe('valid cases', () => {
it('should validate with valid from date, to date and non empty schedule ', () => { it('should validate with valid from date, to date and non empty schedule ', () => {
expect( expect(

View File

@ -6,11 +6,11 @@ import { IRepository } from '../../interfaces/repository.interface';
import { PrismaService } from './prisma-service'; import { PrismaService } from './prisma-service';
/** /**
* Child classes MUST redefined _model property with appropriate model name * Child classes MUST redefined model property with appropriate model name
*/ */
@Injectable() @Injectable()
export abstract class PrismaRepository<T> implements IRepository<T> { export abstract class PrismaRepository<T> implements IRepository<T> {
protected _model: string; protected model: string;
constructor(protected readonly _prisma: PrismaService) {} constructor(protected readonly _prisma: PrismaService) {}
@ -21,13 +21,13 @@ export abstract class PrismaRepository<T> implements IRepository<T> {
include?: any, include?: any,
): Promise<ICollection<T>> { ): Promise<ICollection<T>> {
const [data, total] = await this._prisma.$transaction([ const [data, total] = await this._prisma.$transaction([
this._prisma[this._model].findMany({ this._prisma[this.model].findMany({
where, where,
include, include,
skip: (page - 1) * perPage, skip: (page - 1) * perPage,
take: perPage, take: perPage,
}), }),
this._prisma[this._model].count({ this._prisma[this.model].count({
where, where,
}), }),
]); ]);
@ -39,7 +39,7 @@ export abstract class PrismaRepository<T> implements IRepository<T> {
async findOneByUuid(uuid: string): Promise<T> { async findOneByUuid(uuid: string): Promise<T> {
try { try {
const entity = await this._prisma[this._model].findUnique({ const entity = await this._prisma[this.model].findUnique({
where: { uuid }, where: { uuid },
}); });
@ -59,7 +59,7 @@ export abstract class PrismaRepository<T> implements IRepository<T> {
async findOne(where: any, include?: any): Promise<T> { async findOne(where: any, include?: any): Promise<T> {
try { try {
const entity = await this._prisma[this._model].findFirst({ const entity = await this._prisma[this.model].findFirst({
where: where, where: where,
include: include, include: include,
}); });
@ -81,7 +81,7 @@ export abstract class PrismaRepository<T> implements IRepository<T> {
// TODO : Refactor for good clean architecture ? // TODO : Refactor for good clean architecture ?
async create(entity: Partial<T> | any, include?: any): Promise<T> { async create(entity: Partial<T> | any, include?: any): Promise<T> {
try { try {
const res = await this._prisma[this._model].create({ const res = await this._prisma[this.model].create({
data: entity, data: entity,
include: include, include: include,
}); });
@ -101,7 +101,7 @@ export abstract class PrismaRepository<T> implements IRepository<T> {
async update(uuid: string, entity: Partial<T>): Promise<T> { async update(uuid: string, entity: Partial<T>): Promise<T> {
try { try {
const updatedEntity = await this._prisma[this._model].update({ const updatedEntity = await this._prisma[this.model].update({
where: { uuid }, where: { uuid },
data: entity, data: entity,
}); });
@ -125,7 +125,7 @@ export abstract class PrismaRepository<T> implements IRepository<T> {
include?: any, include?: any,
): Promise<T> { ): Promise<T> {
try { try {
const updatedEntity = await this._prisma[this._model].update({ const updatedEntity = await this._prisma[this.model].update({
where: where, where: where,
data: entity, data: entity,
include: include, include: include,
@ -147,7 +147,7 @@ export abstract class PrismaRepository<T> implements IRepository<T> {
async delete(uuid: string): Promise<T> { async delete(uuid: string): Promise<T> {
try { try {
const entity = await this._prisma[this._model].delete({ const entity = await this._prisma[this.model].delete({
where: { uuid }, where: { uuid },
}); });
@ -167,7 +167,7 @@ export abstract class PrismaRepository<T> implements IRepository<T> {
async deleteMany(where: any): Promise<void> { async deleteMany(where: any): Promise<void> {
try { try {
const entity = await this._prisma[this._model].deleteMany({ const entity = await this._prisma[this.model].deleteMany({
where: where, where: where,
}); });
@ -190,7 +190,7 @@ export abstract class PrismaRepository<T> implements IRepository<T> {
where: string[], where: string[],
): Promise<ICollection<T>> { ): Promise<ICollection<T>> {
const query = `SELECT ${include.join(',')} FROM ${ const query = `SELECT ${include.join(',')} FROM ${
this._model this.model
} WHERE ${where.join(' AND ')}`; } WHERE ${where.join(' AND ')}`;
const data: T[] = await this._prisma.$queryRawUnsafe(query); const data: T[] = await this._prisma.$queryRawUnsafe(query);
return Promise.resolve({ return Promise.resolve({
@ -201,7 +201,7 @@ export abstract class PrismaRepository<T> implements IRepository<T> {
async createWithFields(fields: object): Promise<number> { async createWithFields(fields: object): Promise<number> {
try { try {
const command = `INSERT INTO ${this._model} ("${Object.keys(fields).join( const command = `INSERT INTO ${this.model} ("${Object.keys(fields).join(
'","', '","',
)}") VALUES (${Object.values(fields).join(',')})`; )}") VALUES (${Object.values(fields).join(',')})`;
return await this._prisma.$executeRawUnsafe(command); return await this._prisma.$executeRawUnsafe(command);
@ -222,7 +222,7 @@ export abstract class PrismaRepository<T> implements IRepository<T> {
entity['"updatedAt"'] = `to_timestamp(${Date.now()} / 1000.0)`; entity['"updatedAt"'] = `to_timestamp(${Date.now()} / 1000.0)`;
const values = Object.keys(entity).map((key) => `${key} = ${entity[key]}`); const values = Object.keys(entity).map((key) => `${key} = ${entity[key]}`);
try { try {
const command = `UPDATE ${this._model} SET ${values.join( const command = `UPDATE ${this.model} SET ${values.join(
', ', ', ',
)} WHERE uuid = '${uuid}'`; )} WHERE uuid = '${uuid}'`;
return await this._prisma.$executeRawUnsafe(command); return await this._prisma.$executeRawUnsafe(command);

View File

@ -41,7 +41,7 @@ Array.from({ length: 10 }).forEach(() => {
@Injectable() @Injectable()
class FakePrismaRepository extends PrismaRepository<FakeEntity> { class FakePrismaRepository extends PrismaRepository<FakeEntity> {
protected _model = 'fake'; protected model = 'fake';
} }
class FakePrismaService extends PrismaService { class FakePrismaService extends PrismaService {