import { eq } from "drizzle-orm"; import { assertExternalModelConfigurationUpdateProjectCommand, createExternalModelConfigurationUpdateProjectCommand, type ExternalModelConfigurationSnapshot, } from "../../domain/models/external-model-configuration-project-command.model.js"; import type { ExecuteExternalModelConfigurationCommandInput, ExternalModelConfigurationProjectCommandStore, } from "../../domain/ports/external-model-configuration-project-command.store.js"; import type { AppDatabase } from "../database-context.js"; import { externalModelConfigurations } from "../schema/external-model-configurations.js"; import { projects } from "../schema/projects.js"; import { executeProjectCommandTransaction } from "./project-command-transaction.persistence.js"; export class ExternalModelConfigurationProjectCommandRepository implements ExternalModelConfigurationProjectCommandStore { constructor(private readonly database: AppDatabase) {} execute(input: ExecuteExternalModelConfigurationCommandInput) { assertExternalModelConfigurationUpdateProjectCommand(input.command); return executeProjectCommandTransaction(this.database, input, (tx) => { const project = tx.select({ id: projects.id }).from(projects) .where(eq(projects.id, input.projectId)).get(); if (!project) throw new Error("Project not found."); const current = tx.select().from(externalModelConfigurations) .where(eq(externalModelConfigurations.projectId, input.projectId)).get() ?? null; assertSameSnapshot(current, input.command.payload.expected); const target = input.command.payload.target; if (target !== null && target.projectId !== input.projectId) { throw new Error("Die Revit-XLSX-Konfiguration gehört zu einem anderen Projekt."); } if (sameSnapshot(current, target)) { throw new Error("Die Revit-XLSX-Konfiguration wurde nicht geändert."); } if (target === null) { tx.delete(externalModelConfigurations) .where(eq(externalModelConfigurations.projectId, input.projectId)).run(); } else if (current === null) { tx.insert(externalModelConfigurations).values(target).run(); } else { const updated = tx.update(externalModelConfigurations) .set({ configuration: target.configuration }) .where(eq(externalModelConfigurations.id, current.id)).run(); if (updated.changes !== 1) { throw new Error("Die Revit-XLSX-Konfiguration wurde zwischenzeitlich geändert."); } } return createExternalModelConfigurationUpdateProjectCommand(target, current); }); } } function assertSameSnapshot( current: ExternalModelConfigurationSnapshot | null, expected: ExternalModelConfigurationSnapshot | null ) { if (!sameSnapshot(current, expected)) { throw new Error("Die Revit-XLSX-Konfiguration wurde zwischenzeitlich geändert."); } } function sameSnapshot( left: ExternalModelConfigurationSnapshot | null, right: ExternalModelConfigurationSnapshot | null ) { return canonicalJson(left) === canonicalJson(right); } function canonicalJson(value: unknown): string { if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`; if (value !== null && typeof value === "object") { const record = value as Record; return `{${Object.keys(record).sort().map( (key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}` ).join(",")}}`; } return JSON.stringify(value); }