import { Injectable } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { FindSprintsRepository, UpsertMaterializedSprintRepository, } from '@task-stream/api/tracker/application'; import { SprintReadModel, SprintStatus } from '@task-stream/api/tracker/domain'; import { Model } from 'mongoose'; import { ReadModels, SprintDoc, fromDoc } from './read-model.schemas'; import { upsertIfNewer } from './upsert-if-newer'; const ORDER: Record = { [SprintStatus.ACTIVE]: 0, [SprintStatus.PLANNED]: 1, [SprintStatus.CLOSED]: 2, }; @Injectable() export class MongoFindSprintsRepository extends FindSprintsRepository { constructor( @InjectModel(ReadModels.Sprint) private readonly sprints: Model, ) { super(); } async findById(id: string): Promise { const doc = await this.sprints.findById(id).lean(); return doc ? fromDoc(doc) : null; } async findByProject(projectId: string): Promise { const docs = await this.sprints.find({ projectId }).lean(); return docs .map(fromDoc) .sort( (a, b) => ORDER[a.status] - ORDER[b.status] || (a.status === SprintStatus.CLOSED ? b.startDate.localeCompare(a.startDate) : a.startDate.localeCompare(b.startDate)) || a.name.localeCompare(b.name), ); } async findActive(projectId: string): Promise { const doc = await this.sprints .findOne({ projectId, status: SprintStatus.ACTIVE }) .lean(); return doc ? fromDoc(doc) : null; } } @Injectable() export class MongoUpsertMaterializedSprintRepository extends UpsertMaterializedSprintRepository { constructor( @InjectModel(ReadModels.Sprint) private readonly sprints: Model, ) { super(); } async upsert({ id, version, ...fields }: Pick & Partial) { await upsertIfNewer(this.sprints, id, version, fields); } async remove(sprintId: string) { await this.sprints.deleteOne({ _id: sprintId }); } }