import { IQueryHandler, Query, QueryHandler } from '@nestjs/cqrs'; import { DEFAULT_PROJECT_SETTINGS, ProjectWorkflow, } from '@task-stream/api/tracker/domain'; import { FindIssuesRepository } from '../ports/find-issues.repository'; import { FindProjectsRepository } from '../ports/find-projects.repository'; import { FindReleasesRepository } from '../ports/find-releases.repository'; import { draftReleaseNotes } from '../releases/release-notes'; import { ReadAccess } from './read-access'; /** A Markdown draft of a release's notes, from its finished tickets. */ export class GetReleaseNotesDraftQuery extends Query { constructor( public readonly viewerId: string | null, public readonly releaseId: string, ) { super(); } } @QueryHandler(GetReleaseNotesDraftQuery) export class GetReleaseNotesDraftQueryHandler implements IQueryHandler { constructor( private readonly findReleases: FindReleasesRepository, private readonly findIssues: FindIssuesRepository, private readonly findProjects: FindProjectsRepository, private readonly access: ReadAccess, ) {} async execute(query: GetReleaseNotesDraftQuery): Promise { const release = this.access.assertFound( await this.findReleases.findById(query.releaseId), 'Release', query.releaseId, ); await this.access.assertSeesWholeProject( release.projectId, query.viewerId, { entity: 'Release', ref: query.releaseId, }, ); const [page, project] = await Promise.all([ this.findIssues.search({ projectId: release.projectId, releaseId: release.id, limit: 500, offset: 0, }), this.findProjects.findById(release.projectId), ]); return draftReleaseNotes({ release, issues: page.items, workflow: new ProjectWorkflow( project?.settings ?? DEFAULT_PROJECT_SETTINGS, ), }); } }