import { EventsHandler, IEventHandler } from '@nestjs/cqrs'; import { DEFAULT_PROJECT_SETTINGS, IssueCreatedEvent, IssueReleaseChangedEvent, IssueStatusChangedEvent, ProjectWorkflow, TrackerEvent, } from '@task-stream/api/tracker/domain'; import { FindIssuesRepository } from '../ports/find-issues.repository'; import { FindProjectsRepository } from '../ports/find-projects.repository'; import { UpsertMaterializedTimelineRepository } from '../ports/upsert-materialized-timeline.repository'; import { versionOf } from './read-model-notifier'; /** * The life of every ticket, step by step: where it went (status + category) * and which release it joined. Statistics are computed from it. */ @EventsHandler( IssueCreatedEvent, IssueStatusChangedEvent, IssueReleaseChangedEvent, ) export class IssueTimelineEventHandler implements IEventHandler { constructor( private readonly timeline: UpsertMaterializedTimelineRepository, private readonly findProjects: FindProjectsRepository, private readonly findIssues: FindIssuesRepository, ) {} async handle(event: TrackerEvent) { const base = { id: `${event.aggregateId}:${versionOf(event)}`, issueId: event.aggregateId, projectId: event.projectId, at: event.occurredAt, }; if (event instanceof IssueCreatedEvent) { const { issue } = event; await this.timeline.record({ ...base, kind: 'status', status: issue.status, category: (await this.workflowOf(event)).categoryOf(issue.status), assigneeId: issue.assigneeId, }); return; } if (event instanceof IssueStatusChangedEvent) { const issue = await this.findIssues.findById(event.aggregateId); await this.timeline.record({ ...base, kind: 'status', status: event.to, category: event.meta?.toCategory ?? (await this.workflowOf(event)).categoryOf(event.to), assigneeId: issue?.assignee?.id ?? null, }); return; } if (event instanceof IssueReleaseChangedEvent) { await this.timeline.record({ ...base, kind: 'release', releaseId: event.releaseId, }); } } private async workflowOf(event: TrackerEvent): Promise { const project = await this.findProjects.findById(event.projectId); return new ProjectWorkflow(project?.settings ?? DEFAULT_PROJECT_SETTINGS); } }