Replace Revit CSV backend with Linear XLSX

This commit is contained in:
Julian Appel 2026-09-30 08:46:07 +02:00
parent 52d3b1a270
commit 1cfeb06ec7
65 changed files with 4764 additions and 3717 deletions

View file

@ -0,0 +1,79 @@
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);
}