import { LoggerBase, MessagePublisherPort } from '@mobicoop/ddd-library'; import { ExtendedPrismaRepositoryBase } from '@mobicoop/ddd-library/dist/db/prisma-repository.base'; import { Inject, Injectable, Logger } from '@nestjs/common'; import { EventEmitter2 } from '@nestjs/event-emitter'; import { SERVICE_NAME } from '@src/app.constants'; import { AD_MESSAGE_PUBLISHER } from '../ad.di-tokens'; import { AdMapper } from '../ad.mapper'; import { AdRepositoryPort } from '../core/application/ports/ad.repository.port'; import { AdEntity } from '../core/domain/ad.entity'; import { Frequency } from '../core/domain/ad.types'; import { PrismaService } from './prisma.service'; export type AdModel = { uuid: string; driver: boolean; passenger: boolean; frequency: Frequency; fromDate: Date; toDate: Date; seatsProposed: number; seatsRequested: number; strict: boolean; pause: boolean; driverDuration?: number; driverDistance?: number; passengerDuration?: number; passengerDistance?: number; fwdAzimuth: number; backAzimuth: number; }; /** * The record as returned by the persistence system */ export type AdReadModel = AdModel & { waypoints: string; schedule: ScheduleItemModel[]; createdAt: Date; updatedAt: Date; }; /** * The record ready to be sent to the persistence system */ export type AdWriteModel = AdModel & { schedule: ScheduleWriteModel; }; export type ScheduleWriteModel = { deleteMany?: PastCreatedFilter; create: ScheduleItemModel[]; }; // used to delete records created in the past, // because the order of `create` and `deleteMany` is not guaranteed export type PastCreatedFilter = { createdAt: { lt: Date }; }; export type AdWriteExtraModel = { waypoints: string; direction: string; }; export type ScheduleItem = { day: number; time: Date; margin: number; }; export type ScheduleItemModel = ScheduleItem & { uuid: string; createdAt: Date; updatedAt: Date; }; export type UngroupedAdModel = AdModel & ScheduleItem & { scheduleItemUuid: string; scheduleItemCreatedAt: Date; scheduleItemUpdatedAt: Date; waypoints: string; createdAt: Date; updatedAt: Date; }; export type GroupedAdModel = AdModel & { schedule: ScheduleItemModel[]; waypoints: string; createdAt: Date; updatedAt: Date; }; /** * Repository is used for retrieving/saving domain entities * */ @Injectable() export class AdRepository extends ExtendedPrismaRepositoryBase< AdEntity, AdReadModel, AdWriteModel, AdWriteExtraModel > implements AdRepositoryPort { constructor( prisma: PrismaService, mapper: AdMapper, eventEmitter: EventEmitter2, @Inject(AD_MESSAGE_PUBLISHER) protected readonly messagePublisher: MessagePublisherPort, ) { super( prisma.ad, prisma, mapper, eventEmitter, new LoggerBase({ logger: new Logger(AdRepository.name), domain: SERVICE_NAME, messagePublisher, }), ); } getCandidateAds = async (queryString: string): Promise => this._toAdReadModels( (await this.prismaRaw.$queryRawUnsafe(queryString)) as UngroupedAdModel[], ) .map((adReadModel: AdReadModel) => { if (this.mapper.toDomain) return this.mapper.toDomain(adReadModel); }) .filter( (adEntity: AdEntity | undefined) => adEntity !== undefined, ) as AdEntity[]; private _toAdReadModels = ( ungroupedAds: UngroupedAdModel[], ): AdReadModel[] => { const groupedAdModels: GroupedAdModel[] = ungroupedAds.map( (ungroupedAd: UngroupedAdModel) => ({ uuid: ungroupedAd.uuid, driver: ungroupedAd.driver, passenger: ungroupedAd.passenger, frequency: ungroupedAd.frequency, fromDate: ungroupedAd.fromDate, toDate: ungroupedAd.toDate, schedule: [ { uuid: ungroupedAd.scheduleItemUuid, day: ungroupedAd.day, time: ungroupedAd.time, margin: ungroupedAd.margin, createdAt: ungroupedAd.scheduleItemCreatedAt, updatedAt: ungroupedAd.scheduleItemUpdatedAt, }, ], seatsProposed: ungroupedAd.seatsProposed, seatsRequested: ungroupedAd.seatsRequested, strict: ungroupedAd.strict, pause: ungroupedAd.pause, driverDuration: ungroupedAd.driverDuration, driverDistance: ungroupedAd.driverDistance, passengerDuration: ungroupedAd.passengerDuration, passengerDistance: ungroupedAd.passengerDistance, fwdAzimuth: ungroupedAd.fwdAzimuth, backAzimuth: ungroupedAd.backAzimuth, waypoints: ungroupedAd.waypoints, createdAt: ungroupedAd.createdAt, updatedAt: ungroupedAd.updatedAt, }), ); const adReadModels: AdReadModel[] = []; groupedAdModels.forEach((groupdeAdModel: GroupedAdModel) => { const adReadModel: AdReadModel | undefined = adReadModels.find( (arm: AdReadModel) => arm.uuid == groupdeAdModel.uuid, ); if (adReadModel) { adReadModel.schedule.push(...groupdeAdModel.schedule); } else { adReadModels.push(groupdeAdModel); } }); return adReadModels; }; async update( id: string, entity: AdEntity, identifier?: string, ): Promise { this.updateExtra(id, entity, 'ad', identifier); } }