forked from jappel/leistungsbilanz-ts
Harden Linear XLSX end to end
This commit is contained in:
parent
2581beee3c
commit
4ff01b2ccd
26 changed files with 522 additions and 246 deletions
15
src/db/repositories/batched-insert.persistence.ts
Normal file
15
src/db/repositories/batched-insert.persistence.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import type { AppDatabase } from "../database-context.js";
|
||||
|
||||
const defaultInsertBatchSize = 200;
|
||||
|
||||
export function insertManyInBatches<TTable extends Parameters<AppDatabase["insert"]>[0]>(
|
||||
database: AppDatabase,
|
||||
table: TTable,
|
||||
values: readonly unknown[],
|
||||
batchSize = defaultInsertBatchSize
|
||||
) {
|
||||
if (!Number.isSafeInteger(batchSize) || batchSize < 1) throw new Error("Insert batch size must be a positive integer.");
|
||||
for (let index = 0; index < values.length; index += batchSize) {
|
||||
database.insert(table).values(values.slice(index, index + batchSize) as never).run();
|
||||
}
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@ import { rooms } from "../schema/rooms.js";
|
|||
import { toCircuitDeviceRowSnapshot } from "./circuit-device-row-structure.persistence.js";
|
||||
import { ExternalModelStateRepository } from "./external-model-state.repository.js";
|
||||
import { executeProjectCommandTransaction } from "./project-command-transaction.persistence.js";
|
||||
import { insertManyInBatches } from "./batched-insert.persistence.js";
|
||||
|
||||
export class ExternalFollowUpImportProjectCommandRepository implements ExternalFollowUpImportProjectCommandStore {
|
||||
constructor(private readonly database: AppDatabase) {}
|
||||
|
|
@ -80,7 +81,7 @@ function assertRows(database: AppDatabase, projectId: string, target: ExternalMo
|
|||
}
|
||||
}
|
||||
function replaceConfiguration(database: AppDatabase, projectId: string, target: typeof externalModelConfigurations.$inferInsert) { database.delete(externalModelConfigurations).where(eq(externalModelConfigurations.projectId, projectId)).run(); database.insert(externalModelConfigurations).values(target).run(); }
|
||||
function replaceState(database: AppDatabase, projectId: string, target: ExternalModelStateSnapshot) { database.delete(externalModelSources).where(eq(externalModelSources.projectId, projectId)).run(); if (!target.source) return; database.insert(externalModelSources).values(target.source).run(); database.insert(externalImportBatches).values(target.importBatches.map(({ originalContentBase64, ...batch }) => ({ ...batch, originalBytes: originalContentBase64 === null ? null : Buffer.from(originalContentBase64, "base64") }))).run(); if (target.roomMappings.length) database.insert(externalRoomMappings).values(target.roomMappings).run(); if (target.objects.length) database.insert(externalModelObjects).values(target.objects).run(); }
|
||||
function replaceState(database: AppDatabase, projectId: string, target: ExternalModelStateSnapshot) { database.delete(externalModelSources).where(eq(externalModelSources.projectId, projectId)).run(); if (!target.source) return; database.insert(externalModelSources).values(target.source).run(); insertManyInBatches(database, externalImportBatches, target.importBatches.map(({ originalContentBase64, ...batch }) => ({ ...batch, originalBytes: originalContentBase64 === null ? null : Buffer.from(originalContentBase64, "base64") }))); insertManyInBatches(database, externalRoomMappings, target.roomMappings); insertManyInBatches(database, externalModelObjects, target.objects); }
|
||||
function ids(entries: Array<{ id: string }>) { return new Set(entries.map((entry) => entry.id)); }
|
||||
function same(left: unknown, right: unknown) { return canonical(left) === canonical(right); }
|
||||
function canonical(value: unknown): string { if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`; if (value !== null && typeof value === "object") { const record = value as Record<string, unknown>; return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${canonical(record[key])}`).join(",")}}`; } return JSON.stringify(value); }
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import { projects } from "../schema/projects.js";
|
|||
import { rooms } from "../schema/rooms.js";
|
||||
import { executeProjectCommandTransaction } from "./project-command-transaction.persistence.js";
|
||||
import { ExternalModelStateRepository } from "./external-model-state.repository.js";
|
||||
import { insertManyInBatches } from "./batched-insert.persistence.js";
|
||||
|
||||
export class ExternalInitialImportProjectCommandRepository implements ExternalInitialImportProjectCommandStore {
|
||||
constructor(private readonly database: AppDatabase) {}
|
||||
|
|
@ -76,9 +77,9 @@ function replaceState(database: AppDatabase, projectId: string, target: External
|
|||
database.delete(externalModelSources).where(eq(externalModelSources.projectId, projectId)).run();
|
||||
if (!target.source) return;
|
||||
database.insert(externalModelSources).values(target.source).run();
|
||||
database.insert(externalImportBatches).values(target.importBatches.map(({ originalContentBase64, ...batch }) => ({ ...batch, originalBytes: originalContentBase64 === null ? null : Buffer.from(originalContentBase64, "base64") }))).run();
|
||||
if (target.roomMappings.length) database.insert(externalRoomMappings).values(target.roomMappings).run();
|
||||
if (target.objects.length) database.insert(externalModelObjects).values(target.objects).run();
|
||||
insertManyInBatches(database, externalImportBatches, target.importBatches.map(({ originalContentBase64, ...batch }) => ({ ...batch, originalBytes: originalContentBase64 === null ? null : Buffer.from(originalContentBase64, "base64") })));
|
||||
insertManyInBatches(database, externalRoomMappings, target.roomMappings);
|
||||
insertManyInBatches(database, externalModelObjects, target.objects);
|
||||
}
|
||||
|
||||
function decodeCanonicalBase64(value: string) { const bytes = Buffer.from(value, "base64"); if (bytes.toString("base64") !== value) throw new Error("Der XLSX-Inhalt ist nicht kanonisch Base64-kodiert."); return bytes; }
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ import { executeProjectCommandTransaction } from "./project-command-transaction.
|
|||
import {
|
||||
readProjectStateSnapshot,
|
||||
} from "./project-state-snapshot.persistence.js";
|
||||
import { insertManyInBatches } from "./batched-insert.persistence.js";
|
||||
|
||||
export class ProjectStateRestoreCommandRepository
|
||||
implements ProjectStateRestoreCommandStore
|
||||
|
|
@ -428,11 +429,5 @@ function insertMany<TTable extends Parameters<AppDatabase["insert"]>[0]>(
|
|||
table: TTable,
|
||||
values: unknown[]
|
||||
) {
|
||||
if (values.length === 0) {
|
||||
return;
|
||||
}
|
||||
database
|
||||
.insert(table)
|
||||
.values(values as never)
|
||||
.run();
|
||||
insertManyInBatches(database, table, values);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import {
|
|||
readProjectStateSnapshot,
|
||||
} from "./project-state-snapshot.persistence.js";
|
||||
import { serializeProjectStateSnapshot } from "../../domain/models/project-state-snapshot.model.js";
|
||||
import { insertManyInBatches } from "./batched-insert.persistence.js";
|
||||
|
||||
export class ProjectTransferRepository implements ProjectTransferStore {
|
||||
constructor(private readonly database: AppDatabase) {}
|
||||
|
|
@ -256,7 +257,5 @@ function insertMany<TTable extends Parameters<AppDatabase["insert"]>[0]>(
|
|||
table: TTable,
|
||||
values: unknown[]
|
||||
) {
|
||||
if (values.length > 0) {
|
||||
database.insert(table).values(values as never).run();
|
||||
}
|
||||
insertManyInBatches(database, table, values);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue