import { Injectable } from '@nestjs/common'; import type { AnyDomainEventEnvelope } from '../domain-events/domain-event-types'; import { OnDomainEvent } from '../domain-events/on-domain-event.decorator'; import { Prisma } from '../../generated/prisma'; import { CalendarSyncService } from './calendar-sync.service'; /** * Puts calendar-relevant records into the M365 outbox. Pure DB writer: the * Graph call itself happens in CalendarSyncService.processOutbox (worker * loop). Errors propagate, so a failed delivery is retried. */ @Injectable() export class CalendarSyncEventHandlers { constructor(private readonly calendarSync: CalendarSyncService) {} @OnDomainEvent( ['InspectionRecorded', 'InspectionCorrected'], 'calendar-sync.inspection', ) async inspection( event: AnyDomainEventEnvelope<'InspectionRecorded' | 'InspectionCorrected'>, tx: Prisma.TransactionClient, ): Promise { await this.calendarSync.enqueueIn(tx, 'INSPECTION', event.aggregateId); } @OnDomainEvent( ['TreatmentRecorded', 'TreatmentCorrected'], 'calendar-sync.treatment', ) async treatment( event: AnyDomainEventEnvelope<'TreatmentRecorded' | 'TreatmentCorrected'>, tx: Prisma.TransactionClient, ): Promise { await this.calendarSync.enqueueIn(tx, 'TREATMENT', event.aggregateId); } // The same events, a second entry: the withdrawal span of the treatment. // Enqueued also when the treatment has no withdrawal days (any more): the // worker then removes an event created earlier. @OnDomainEvent( ['TreatmentRecorded', 'TreatmentCorrected'], 'calendar-sync.withdrawal', ) async withdrawal( event: AnyDomainEventEnvelope<'TreatmentRecorded' | 'TreatmentCorrected'>, tx: Prisma.TransactionClient, ): Promise { await this.calendarSync.enqueueIn(tx, 'WITHDRAWAL', event.aggregateId); } @OnDomainEvent( ['FeedingRecorded', 'FeedingCorrected'], 'calendar-sync.feeding', ) async feeding( event: AnyDomainEventEnvelope<'FeedingRecorded' | 'FeedingCorrected'>, tx: Prisma.TransactionClient, ): Promise { await this.calendarSync.enqueueIn(tx, 'FEEDING', event.aggregateId); } @OnDomainEvent( ['HarvestRecorded', 'HarvestCorrected'], 'calendar-sync.harvest', ) async harvest( event: AnyDomainEventEnvelope<'HarvestRecorded' | 'HarvestCorrected'>, tx: Prisma.TransactionClient, ): Promise { await this.calendarSync.enqueueIn(tx, 'HARVEST', event.aggregateId); } // Updated and completed tasks are enqueued even without a due date or // when done: the worker then removes an event created earlier. @OnDomainEvent( ['TaskCreated', 'TaskUpdated', 'TaskCompleted'], 'calendar-sync.task', ) async task( event: AnyDomainEventEnvelope< 'TaskCreated' | 'TaskUpdated' | 'TaskCompleted' >, tx: Prisma.TransactionClient, ): Promise { if (event.type === 'TaskCreated' && event.payload.dueDate === null) { return; } await this.calendarSync.enqueueIn(tx, 'TASK', event.aggregateId); } // The aggregate is the queen; the calendar entry belongs to the history row. @OnDomainEvent(['QueenAssigned'], 'calendar-sync.queen-assignment') async queenAssignment( event: AnyDomainEventEnvelope<'QueenAssigned'>, tx: Prisma.TransactionClient, ): Promise { await this.calendarSync.enqueueIn( tx, 'QUEEN_ASSIGNMENT', event.payload.historyId, ); } }