WIP ad creation
This commit is contained in:
parent
bf08260403
commit
5ed2c562d5
|
@ -10,13 +10,13 @@ CREATE TABLE "ad" (
|
|||
"frequency" "Frequency" NOT NULL,
|
||||
"fromDate" DATE NOT NULL,
|
||||
"toDate" DATE NOT NULL,
|
||||
"monTime" TIMESTAMPTZ,
|
||||
"tueTime" TIMESTAMPTZ,
|
||||
"wedTime" TIMESTAMPTZ,
|
||||
"thuTime" TIMESTAMPTZ,
|
||||
"friTime" TIMESTAMPTZ,
|
||||
"satTime" TIMESTAMPTZ,
|
||||
"sunTime" TIMESTAMPTZ,
|
||||
"monTime" TEXT,
|
||||
"tueTime" TEXT,
|
||||
"wedTime" TEXT,
|
||||
"thuTime" TEXT,
|
||||
"friTime" TEXT,
|
||||
"satTime" TEXT,
|
||||
"sunTime" TEXT,
|
||||
"monMargin" INTEGER NOT NULL,
|
||||
"tueMargin" INTEGER NOT NULL,
|
||||
"wedMargin" INTEGER NOT NULL,
|
||||
|
@ -45,8 +45,7 @@ CREATE TABLE "address" (
|
|||
"street" TEXT,
|
||||
"locality" TEXT,
|
||||
"postalCode" TEXT,
|
||||
"country" TEXT,
|
||||
"countryCode" TEXT,
|
||||
"country" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
|
@ -18,13 +18,13 @@ model Ad {
|
|||
frequency Frequency
|
||||
fromDate DateTime @db.Date
|
||||
toDate DateTime @db.Date
|
||||
monTime DateTime? @db.Timestamptz()
|
||||
tueTime DateTime? @db.Timestamptz()
|
||||
wedTime DateTime? @db.Timestamptz()
|
||||
thuTime DateTime? @db.Timestamptz()
|
||||
friTime DateTime? @db.Timestamptz()
|
||||
satTime DateTime? @db.Timestamptz()
|
||||
sunTime DateTime? @db.Timestamptz()
|
||||
monTime String?
|
||||
tueTime String?
|
||||
wedTime String?
|
||||
thuTime String?
|
||||
friTime String?
|
||||
satTime String?
|
||||
sunTime String?
|
||||
monMargin Int
|
||||
tueMargin Int
|
||||
wedMargin Int
|
||||
|
|
|
@ -9,6 +9,7 @@ import { AdsRepository } from './adapters/secondaries/ads.repository';
|
|||
import { Messager } from './adapters/secondaries/messager';
|
||||
import { FindAdByUuidUseCase } from './domain/usecases/find-ad-by-uuid.usecase';
|
||||
import { CreateAdUseCase } from './domain/usecases/create-ad.usecase';
|
||||
import { DefaultParamsProvider } from './adapters/secondaries/default-params.provider';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
@ -36,6 +37,10 @@ import { CreateAdUseCase } from './domain/usecases/create-ad.usecase';
|
|||
Messager,
|
||||
FindAdByUuidUseCase,
|
||||
CreateAdUseCase,
|
||||
{
|
||||
provide: 'ParamsProvider',
|
||||
useClass: DefaultParamsProvider,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class AdModule {}
|
||||
|
|
|
@ -10,7 +10,7 @@ import { FindAdByUuidQuery } from '../../queries/find-ad-by-uuid.query';
|
|||
import { Ad } from '../../domain/entities/ad';
|
||||
import { CreateAdRequest } from '../../domain/dtos/create-ad.request';
|
||||
import { CreateAdCommand } from '../../commands/create-ad.command';
|
||||
import { DatabaseException } from 'src/modules/database/exceptions/database.exception';
|
||||
import { DatabaseException } from '../../../database/exceptions/database.exception';
|
||||
|
||||
@UsePipes(
|
||||
new RpcValidationPipe({
|
||||
|
@ -41,10 +41,10 @@ export class AdController {
|
|||
|
||||
@GrpcMethod('AdsService', 'Create')
|
||||
async createAd(data: CreateAdRequest): Promise<AdPresenter> {
|
||||
console.log('presenter');
|
||||
console.log(data);
|
||||
console.log('*******************************');
|
||||
try {
|
||||
console.log('controler');
|
||||
console.log(data);
|
||||
console.log('-----------------------------------------');
|
||||
const ad = await this._commandBus.execute(new CreateAdCommand(data));
|
||||
return this._mapper.map(ad, Ad, AdPresenter);
|
||||
} catch (e) {
|
||||
|
|
|
@ -1,8 +1,34 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { AdRepository } from '../../../database/domain/ad-repository';
|
||||
import { Ad } from '../../domain/entities/ad';
|
||||
|
||||
import { DatabaseException } from '../../../database/exceptions/database.exception';
|
||||
import { Prisma } from '@prisma/client';
|
||||
@Injectable()
|
||||
export class AdsRepository extends AdRepository<Ad> {
|
||||
protected _model = 'ad';
|
||||
|
||||
async create(entity: Partial<Ad> | any, include?: any): Promise<Ad> {
|
||||
console.log('custom ad repo ');
|
||||
console.log(entity);
|
||||
console.log('*****************************************');
|
||||
try {
|
||||
const res = await this._prisma[this._model].create({
|
||||
data: entity,
|
||||
include: include,
|
||||
});
|
||||
return res;
|
||||
} catch (e) {
|
||||
console.log('repo error ');
|
||||
console.log(e);
|
||||
if (e instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseException(
|
||||
Prisma.PrismaClientKnownRequestError.name,
|
||||
e.code,
|
||||
e.message,
|
||||
);
|
||||
} else {
|
||||
throw new DatabaseException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { DefaultParams } from '../../domain/types/default-params.type';
|
||||
import { IProvideParams } from '../../domain/interfaces/param-provider.interface';
|
||||
|
||||
@Injectable()
|
||||
export class DefaultParamsProvider implements IProvideParams {
|
||||
constructor(private readonly configService: ConfigService) {}
|
||||
//TODO adding config service
|
||||
getParams = (): DefaultParams => {
|
||||
return {
|
||||
MON_MARGIN: 900,
|
||||
TUE_MARGIN: 900,
|
||||
WED_MARGIN: 900,
|
||||
THU_MARGIN: 900,
|
||||
FRI_MARGIN: 900,
|
||||
SAT_MARGIN: 900,
|
||||
SUN_MARGIN: 900,
|
||||
DRIVER: false,
|
||||
DRIVER_SEAT: 0,
|
||||
PASSENGER: true,
|
||||
PASSENGER_SEATS: 1,
|
||||
STRICT: false,
|
||||
};
|
||||
};
|
||||
}
|
|
@ -5,6 +5,5 @@ export class CreateAdCommand {
|
|||
|
||||
constructor(request: CreateAdRequest) {
|
||||
this.createAdRequest = request;
|
||||
console.log('req creation');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@ import { mappingKeyToFrequency } from './utils/frequency.mapping';
|
|||
import { MarginDTO } from './create.margin.dto';
|
||||
import { ScheduleDTO } from './create.schedule.dto';
|
||||
import { AddressDTO } from './create.address.dto';
|
||||
import { HasProperPassengerSeats } from './has-passenger-seats.validator';
|
||||
import { HasProperDriverSeats } from './has-driver-seats.validator';
|
||||
import { HasProperPassengerSeats } from './utils/has-passenger-seats.validator';
|
||||
import { HasProperDriverSeats } from './utils/has-driver-seats.validator';
|
||||
|
||||
export class CreateAdRequest {
|
||||
@IsString()
|
||||
|
|
|
@ -7,9 +7,6 @@ import {
|
|||
// TODO refactor ??
|
||||
// TODO propely set driver max limit
|
||||
export function hasProperDriverSeats(value: any, args: ValidationArguments) {
|
||||
console.log('here ');
|
||||
console.log(value);
|
||||
console.log(args.object);
|
||||
if (value === true && typeof args.object['seatsDriver'] === 'number')
|
||||
return args.object['seatsDriver'] > 0;
|
||||
else if (
|
|
@ -7,9 +7,6 @@ import {
|
|||
// TODO refactor ??
|
||||
// TODO propely set passenger max limit
|
||||
export function hasProperPassengerSeats(value: any, args: ValidationArguments) {
|
||||
console.log('here ');
|
||||
console.log(value);
|
||||
console.log(args.object);
|
||||
if (value === true && typeof args.object['seatsPassenger'] === 'number')
|
||||
return args.object['seatsPassenger'] > 0;
|
||||
else if (
|
|
@ -6,9 +6,12 @@ import {
|
|||
IsDate,
|
||||
IsInt,
|
||||
IsEnum,
|
||||
ValidateNested,
|
||||
ArrayMinSize,
|
||||
} from 'class-validator';
|
||||
import { Address } from '../entities/address';
|
||||
import { Frequency } from '../types/frequency.enum';
|
||||
import { Type } from 'class-transformer';
|
||||
|
||||
export class Ad {
|
||||
@IsString()
|
||||
|
@ -21,11 +24,11 @@ export class Ad {
|
|||
|
||||
@IsBoolean()
|
||||
@AutoMap()
|
||||
driver?: boolean;
|
||||
driver: boolean;
|
||||
|
||||
@IsBoolean()
|
||||
@AutoMap()
|
||||
passenger?: boolean;
|
||||
passenger: boolean;
|
||||
|
||||
@IsEnum(Frequency)
|
||||
@AutoMap()
|
||||
|
@ -110,6 +113,10 @@ export class Ad {
|
|||
@AutoMap()
|
||||
seatsPassenger: number;
|
||||
|
||||
@IsBoolean()
|
||||
@AutoMap()
|
||||
strict: boolean;
|
||||
|
||||
@IsDate()
|
||||
@AutoMap()
|
||||
createdAt: Date;
|
||||
|
@ -118,6 +125,9 @@ export class Ad {
|
|||
@AutoMap()
|
||||
updatedAt?: Date;
|
||||
|
||||
@Type(() => Address)
|
||||
@ArrayMinSize(2)
|
||||
@ValidateNested({ each: true })
|
||||
@AutoMap()
|
||||
addresses?: Array<Address>;
|
||||
addresses: Address[];
|
||||
}
|
||||
|
|
|
@ -1,3 +1,14 @@
|
|||
export type DefaultParams = {
|
||||
DEFAULT_MARGIN: number;
|
||||
MON_MARGIN: number;
|
||||
TUE_MARGIN: number;
|
||||
WED_MARGIN: number;
|
||||
THU_MARGIN: number;
|
||||
FRI_MARGIN: number;
|
||||
SAT_MARGIN: number;
|
||||
SUN_MARGIN: number;
|
||||
DRIVER: boolean;
|
||||
DRIVER_SEAT: number;
|
||||
PASSENGER: boolean;
|
||||
PASSENGER_SEATS: number;
|
||||
STRICT: boolean;
|
||||
};
|
||||
|
|
|
@ -1,19 +1,28 @@
|
|||
import { Mapper } from '@automapper/core';
|
||||
import { InjectMapper } from '@automapper/nestjs';
|
||||
import { Inject } from '@nestjs/common';
|
||||
import { CommandHandler } from '@nestjs/cqrs';
|
||||
import { Messager } from '../../adapters/secondaries/messager';
|
||||
import { AdsRepository } from '../../adapters/secondaries/ads.repository';
|
||||
import { CreateAdCommand } from '../../commands/create-ad.command';
|
||||
import { CreateAdRequest } from '../dtos/create-ad.request';
|
||||
import { Ad } from '../entities/ad';
|
||||
import { IProvideParams } from '../interfaces/param-provider.interface';
|
||||
import { DefaultParams } from '../types/default-params.type';
|
||||
|
||||
@CommandHandler(CreateAdCommand)
|
||||
export class CreateAdUseCase {
|
||||
private readonly defaultParams: DefaultParams;
|
||||
private ad: Ad;
|
||||
constructor(
|
||||
private readonly _repository: AdsRepository,
|
||||
private readonly _messager: Messager,
|
||||
@InjectMapper() private readonly _mapper: Mapper,
|
||||
) {}
|
||||
@Inject('ParamsProvider')
|
||||
private readonly defaultParamsProvider: IProvideParams,
|
||||
) {
|
||||
this.defaultParams = defaultParamsProvider.getParams();
|
||||
}
|
||||
|
||||
async execute(command: CreateAdCommand): Promise<Ad> {
|
||||
const entity: Ad = this._mapper.map(
|
||||
|
@ -21,11 +30,38 @@ export class CreateAdUseCase {
|
|||
CreateAdRequest,
|
||||
Ad,
|
||||
);
|
||||
console.log('usecase');
|
||||
console.log(entity.addresses[0]);
|
||||
console.log('-----------------------------------------');
|
||||
typeof entity.monMargin === 'number'
|
||||
? entity.monMargin
|
||||
: (entity.monMargin = this.defaultParams.MON_MARGIN);
|
||||
typeof entity.tueMargin === 'number'
|
||||
? entity.tueMargin
|
||||
: (entity.tueMargin = this.defaultParams.TUE_MARGIN);
|
||||
typeof entity.wedMargin === 'number'
|
||||
? entity.wedMargin
|
||||
: (entity.wedMargin = this.defaultParams.WED_MARGIN);
|
||||
typeof entity.thuMargin === 'number'
|
||||
? entity.thuMargin
|
||||
: (entity.thuMargin = this.defaultParams.THU_MARGIN);
|
||||
typeof entity.friMargin === 'number'
|
||||
? entity.friMargin
|
||||
: (entity.friMargin = this.defaultParams.FRI_MARGIN);
|
||||
typeof entity.satMargin === 'number'
|
||||
? entity.satMargin
|
||||
: (entity.satMargin = this.defaultParams.SAT_MARGIN);
|
||||
typeof entity.sunMargin === 'number'
|
||||
? entity.sunMargin
|
||||
: (entity.sunMargin = this.defaultParams.SUN_MARGIN);
|
||||
typeof entity.strict === 'boolean'
|
||||
? entity.strict
|
||||
: (entity.strict = this.defaultParams.STRICT);
|
||||
try {
|
||||
const ad = await this._repository.create(entity);
|
||||
this.ad = await this._repository.create(entity);
|
||||
// this._messager.publish('ad.create', JSON.stringify(ad));
|
||||
// this._messager.publish('logging.ad.create.info', JSON.stringify(ad));
|
||||
return ad;
|
||||
return this.ad;
|
||||
} catch (error) {
|
||||
let key = 'logging.ad.create.crit';
|
||||
if (error.message.includes('Already exists')) {
|
||||
|
|
|
@ -74,6 +74,10 @@ export class AdProfile extends AutomapperProfile {
|
|||
(destination) => destination.sunTime,
|
||||
mapFrom((source) => source.schedule.sun),
|
||||
),
|
||||
forMember(
|
||||
(destination) => destination.addresses,
|
||||
mapFrom((source) => source.addresses),
|
||||
),
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -10,8 +10,7 @@ import { classes } from '@automapper/classes';
|
|||
import { Frequency } from '../../domain/types/frequency.enum';
|
||||
import { Ad } from '../../domain/entities/ad';
|
||||
import { AdProfile } from '../../mappers/ad.profile';
|
||||
import { ScheduleDTO } from '../../domain/dtos/create.schedule.dto';
|
||||
import { ObjectUnsubscribedError } from 'rxjs';
|
||||
import { DefaultParams } from '../../domain/types/default-params.type';
|
||||
|
||||
const mockAddress1: Address = {
|
||||
position: 0,
|
||||
|
@ -31,7 +30,27 @@ const mockAddress2: Address = {
|
|||
postalCode: '75000',
|
||||
country: 'France',
|
||||
};
|
||||
|
||||
const minimalReccurentAdREquest: CreateAdRequest = {
|
||||
userUuid: '224e0000-0000-4000-a000-000000000000',
|
||||
driver: undefined,
|
||||
passenger: undefined,
|
||||
frequency: Frequency.RECURRENT,
|
||||
fromDate: new Date('01-05-2023'),
|
||||
toDate: new Date('01-05-2024'),
|
||||
schedule: {
|
||||
mon: '08:00',
|
||||
},
|
||||
marginDurations: {
|
||||
mon: undefined,
|
||||
tue: undefined,
|
||||
wed: undefined,
|
||||
thu: undefined,
|
||||
fri: undefined,
|
||||
sat: undefined,
|
||||
sun: undefined,
|
||||
},
|
||||
addresses: [mockAddress1, mockAddress2],
|
||||
};
|
||||
const newAdRequest: CreateAdRequest = {
|
||||
userUuid: '113e0000-0000-4000-a000-000000000000',
|
||||
driver: true,
|
||||
|
@ -56,12 +75,28 @@ const newAdRequest: CreateAdRequest = {
|
|||
addresses: [mockAddress1, mockAddress2],
|
||||
};
|
||||
|
||||
const newAdCommand = new CreateAdCommand(newAdRequest);
|
||||
|
||||
const mockMessager = {
|
||||
publish: jest.fn().mockImplementation(),
|
||||
};
|
||||
const mockAdRepository = {
|
||||
const mockDefaultParamsProvider = {
|
||||
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,
|
||||
DRIVER_SEAT: 0,
|
||||
PASSENGER: true,
|
||||
PASSENGER_SEATS: 1,
|
||||
STRICT: false,
|
||||
};
|
||||
},
|
||||
};
|
||||
let mockAdRepository = {
|
||||
create: jest
|
||||
.fn()
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
|
@ -72,9 +107,10 @@ const mockAdRepository = {
|
|||
createdAt: new Date('01-05-2023'),
|
||||
});
|
||||
})
|
||||
.mockImplementation(() => {
|
||||
.mockImplementationOnce(() => {
|
||||
throw new Error('Already exists');
|
||||
}),
|
||||
})
|
||||
.mockImplementation(),
|
||||
};
|
||||
describe('CreateAdUseCase', () => {
|
||||
let createAdUseCase: CreateAdUseCase;
|
||||
|
@ -92,6 +128,10 @@ describe('CreateAdUseCase', () => {
|
|||
},
|
||||
CreateAdUseCase,
|
||||
AdProfile,
|
||||
{
|
||||
provide: 'ParamsProvider',
|
||||
useValue: mockDefaultParamsProvider,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
|
@ -100,11 +140,8 @@ describe('CreateAdUseCase', () => {
|
|||
it('should be defined', () => {
|
||||
expect(createAdUseCase).toBeDefined();
|
||||
});
|
||||
|
||||
// describe('Ad parameters validation', () => {
|
||||
// it('should throw an error if ');
|
||||
// });
|
||||
describe('execution', () => {
|
||||
const newAdCommand = new CreateAdCommand(newAdRequest);
|
||||
it('should create an new ad', async () => {
|
||||
const newAd: Ad = await createAdUseCase.execute(newAdCommand);
|
||||
expect(newAd.userUuid).toBe(newAdRequest.userUuid);
|
||||
|
@ -116,4 +153,19 @@ describe('CreateAdUseCase', () => {
|
|||
).rejects.toBeInstanceOf(Error);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Ad parameter default setting ', () => {
|
||||
const newAdCommand = new CreateAdCommand(minimalReccurentAdREquest);
|
||||
it('should define mimimal ad as 1 passager add', async () => {
|
||||
const newAd: Ad = await createAdUseCase.execute(newAdCommand);
|
||||
expect(mockAdRepository).toBeCalledWith({
|
||||
userUuid: '113e0000-0000-4000-a000-000000000000',
|
||||
driver: true,
|
||||
passenger: false,
|
||||
frequency: Frequency.RECURRENT,
|
||||
fromDate: new Date('01-05-2023'),
|
||||
toDate: new Date('20-08-2023'),
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { hasProperDriverSeats } from '../../domain/dtos/has-driver-seats.validator';
|
||||
import { hasProperDriverSeats } from '../../domain/dtos/utils/has-driver-seats.validator';
|
||||
|
||||
describe('driver and/or driver seats validator', () => {
|
||||
it('should validate if driver and drivers seats is not provided ', () => {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { hasProperPassengerSeats } from '../../domain/dtos/has-passenger-seats.validator';
|
||||
import { hasProperPassengerSeats } from '../../domain/dtos/utils/has-passenger-seats.validator';
|
||||
|
||||
describe('driver and/or passenger seats validator', () => {
|
||||
it('should validate if passenger and passengers seats is not provided ', () => {
|
||||
|
|
|
@ -82,12 +82,14 @@ export abstract class PrismaRepository<T> implements IRepository<T> {
|
|||
async create(entity: Partial<T> | any, include?: any): Promise<T> {
|
||||
console.log('repo entity ');
|
||||
console.log(entity);
|
||||
console.log('-----------------------------------------');
|
||||
try {
|
||||
const res = await this._prisma[this._model].create({
|
||||
data: entity,
|
||||
include: include,
|
||||
});
|
||||
|
||||
console.log('result');
|
||||
console.log(res);
|
||||
return res;
|
||||
} catch (e) {
|
||||
console.log('repo error ');
|
||||
|
|
Loading…
Reference in New Issue