79 lines
3.4 KiB
TypeScript
79 lines
3.4 KiB
TypeScript
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<string, unknown>;
|
|
return `{${Object.keys(record).sort().map(
|
|
(key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`
|
|
).join(",")}}`;
|
|
}
|
|
return JSON.stringify(value);
|
|
}
|