import { maintenance, maintenanceUpdate } from "@openstatus/db/src/schema"; import { emitAudit } from "../audit"; import { requireScope } from "../auth"; import { type ServiceContext, tryGetActorUserId, withTransaction, } from "../context"; import { ConflictError } from "../errors"; import type { Maintenance, MaintenanceUpdate } from "../types"; import { assertPageInWorkspace, updatePageComponentAssociations, validatePageComponentIds, } from "./internal"; import { CreateMaintenanceInput } from "./schemas"; export type CreateMaintenanceResult = { maintenance: Maintenance; initialUpdate: MaintenanceUpdate; }; export async function createMaintenance(args: { ctx: ServiceContext; input: CreateMaintenanceInput; }): Promise { const { ctx } = args; requireScope(ctx, "write"); const input = CreateMaintenanceInput.parse(args.input); const actorUserId = tryGetActorUserId(ctx.actor); return withTransaction(ctx, async (tx) => { await assertPageInWorkspace({ tx, pageId: input.pageId, workspaceId: ctx.workspace.id, }); const validated = await validatePageComponentIds({ tx, workspaceId: ctx.workspace.id, pageComponentIds: input.pageComponentIds, }); if (validated.pageId !== null && validated.pageId !== input.pageId) { throw new ConflictError( `pageId ${input.pageId} does not match the page (${validated.pageId}) of the selected components.`, ); } const record = await tx .insert(maintenance) .values({ workspaceId: ctx.workspace.id, pageId: input.pageId, title: input.title, // deprecated column: mirrors the first update until it is dropped message: input.message, from: input.from, to: input.to, createdBy: actorUserId, updatedBy: actorUserId, }) .returning() .get(); await updatePageComponentAssociations({ tx, maintenanceId: record.id, componentIds: validated.componentIds, }); const initialUpdate = await tx .insert(maintenanceUpdate) .values({ maintenanceId: record.id, message: input.message, date: input.date ?? new Date(), createdBy: actorUserId, updatedBy: actorUserId, }) .returning() .get(); await emitAudit(tx, ctx, { action: "maintenance.create", entityType: "maintenance", entityId: record.id, after: record, }); await emitAudit(tx, ctx, { action: "maintenance_update.create", entityType: "maintenance_update", entityId: initialUpdate.id, after: initialUpdate, metadata: { maintenanceId: record.id }, }); return { maintenance: record, initialUpdate }; }); }