Add Linear XLSX initial import
This commit is contained in:
parent
c633e78cd4
commit
bfdd048269
20 changed files with 932 additions and 85 deletions
|
|
@ -0,0 +1,105 @@
|
|||
import type { ExternalModelStateSnapshot } from "../../external-model/domain/external-model-contracts.js";
|
||||
import { assertExternalModelConfiguration } from "../../external-model/configuration/external-model-configuration.js";
|
||||
import { parseExternalModelStateSnapshot } from "./project-state-snapshot.model.js";
|
||||
import type { ExternalModelConfigurationSnapshot } from "./external-model-configuration-project-command.model.js";
|
||||
import type { SerializedProjectCommand } from "./project-command.model.js";
|
||||
|
||||
export const externalInitialImportCommandType = "external-model.apply-initial-import" as const;
|
||||
export const externalInitialImportCommandSchemaVersion = 1 as const;
|
||||
|
||||
export interface ExternalInitialImportPayload {
|
||||
expectedConfiguration: ExternalModelConfigurationSnapshot | null;
|
||||
targetConfiguration: ExternalModelConfigurationSnapshot | null;
|
||||
expectedState: ExternalModelStateSnapshot;
|
||||
targetState: ExternalModelStateSnapshot;
|
||||
}
|
||||
|
||||
export interface ExternalInitialImportProjectCommand extends SerializedProjectCommand<ExternalInitialImportPayload> {
|
||||
schemaVersion: typeof externalInitialImportCommandSchemaVersion;
|
||||
type: typeof externalInitialImportCommandType;
|
||||
}
|
||||
|
||||
export function createExternalInitialImportProjectCommand(
|
||||
expectedConfiguration: ExternalModelConfigurationSnapshot | null,
|
||||
targetConfiguration: ExternalModelConfigurationSnapshot | null,
|
||||
expectedState: ExternalModelStateSnapshot,
|
||||
targetState: ExternalModelStateSnapshot
|
||||
): ExternalInitialImportProjectCommand {
|
||||
const command: ExternalInitialImportProjectCommand = {
|
||||
schemaVersion: externalInitialImportCommandSchemaVersion,
|
||||
type: externalInitialImportCommandType,
|
||||
payload: { expectedConfiguration, targetConfiguration, expectedState, targetState },
|
||||
};
|
||||
assertExternalInitialImportProjectCommand(command);
|
||||
return command;
|
||||
}
|
||||
|
||||
export function assertExternalInitialImportProjectCommand(
|
||||
command: SerializedProjectCommand<unknown>
|
||||
): asserts command is ExternalInitialImportProjectCommand {
|
||||
if (command.schemaVersion !== 1 || command.type !== externalInitialImportCommandType || !isRecord(command.payload)) {
|
||||
throw new Error("Nicht unterstützter Revit-Erstimport-Command.");
|
||||
}
|
||||
const keys = Object.keys(command.payload).sort().join(",");
|
||||
if (keys !== "expectedConfiguration,expectedState,targetConfiguration,targetState") {
|
||||
throw new Error("Der Revit-Erstimport-Command ist unvollständig.");
|
||||
}
|
||||
assertConfigurationSnapshot(command.payload.expectedConfiguration);
|
||||
assertConfigurationSnapshot(command.payload.targetConfiguration);
|
||||
const expected = parseExternalModelStateSnapshot(command.payload.expectedState);
|
||||
const target = parseExternalModelStateSnapshot(command.payload.targetState);
|
||||
if (isEmptyExternalModelState(expected) === isEmptyExternalModelState(target)) {
|
||||
throw new Error("Der Erstimport muss zwischen leerem und gefülltem Modellzustand wechseln.");
|
||||
}
|
||||
assertPopulatedInitialState(isEmptyExternalModelState(expected) ? target : expected);
|
||||
}
|
||||
|
||||
export function createEmptyExternalModelState(): ExternalModelStateSnapshot {
|
||||
return { source: null, importBatches: [], roomMappings: [], objects: [] };
|
||||
}
|
||||
|
||||
export function isEmptyExternalModelState(state: ExternalModelStateSnapshot) {
|
||||
return state.source === null && state.importBatches.length === 0 && state.roomMappings.length === 0 && state.objects.length === 0;
|
||||
}
|
||||
|
||||
function assertConfigurationSnapshot(value: unknown): asserts value is ExternalModelConfigurationSnapshot | null {
|
||||
if (value === null) return;
|
||||
if (!isRecord(value) || typeof value.id !== "string" || typeof value.projectId !== "string") {
|
||||
throw new Error("Ungültiger Konfigurationszustand im Revit-Erstimport.");
|
||||
}
|
||||
assertExternalModelConfiguration(value.configuration);
|
||||
}
|
||||
|
||||
function assertPopulatedInitialState(state: ExternalModelStateSnapshot) {
|
||||
if (!state.source || state.source.sourceType !== "revit_xlsx" || state.importBatches.length !== 1 || state.objects.length === 0) {
|
||||
throw new Error("Der Erstimport benötigt eine Quelle, einen Batch und mindestens ein Objekt.");
|
||||
}
|
||||
const source = state.source;
|
||||
const batch = state.importBatches[0];
|
||||
if (batch.importKind !== "initial" || batch.projectId !== source.projectId || batch.sourceId !== source.id || batch.configurationSnapshot === null || batch.originalContentBase64 === null || batch.document === null || batch.objectCount !== state.objects.length) {
|
||||
throw new Error("Der Erstimport-Batch ist unvollständig.");
|
||||
}
|
||||
const mappingIds = uniqueIds(state.roomMappings);
|
||||
const roomKeys = new Set<string>();
|
||||
for (const mapping of state.roomMappings) {
|
||||
if (mapping.projectId !== source.projectId || mapping.sourceId !== source.id || roomKeys.has(mapping.normalizedSourceRoomKey)) throw new Error("Ungültige oder doppelte Quellraumzuordnung.");
|
||||
roomKeys.add(mapping.normalizedSourceRoomKey);
|
||||
}
|
||||
uniqueIds(state.objects);
|
||||
const worksharingIds = new Set<string>();
|
||||
for (const object of state.objects) {
|
||||
if (object.projectId !== source.projectId || object.sourceId !== source.id || object.lastSeenImportBatchId !== batch.id || object.lastAcceptedImportBatchId !== batch.id) throw new Error("Ungültige Objektidentität im Erstimport.");
|
||||
if (worksharingIds.has(object.worksharingId)) throw new Error("Doppelte Worksharing-ID im Erstimport.");
|
||||
worksharingIds.add(object.worksharingId);
|
||||
if (object.externalRoomMappingId !== null && !mappingIds.has(object.externalRoomMappingId)) throw new Error("Ungültige Quellraumzuordnung am Revit-Objekt.");
|
||||
if (object.circuitDeviceRowId !== null) throw new Error("Der Erstimport darf keine Gerätezeile zuordnen.");
|
||||
if (object.presenceStatus !== "present") throw new Error("Erstimportobjekte müssen vorhanden sein.");
|
||||
}
|
||||
}
|
||||
|
||||
function uniqueIds(entries: ReadonlyArray<{ id: string }>) {
|
||||
const ids = new Set<string>();
|
||||
for (const entry of entries) { if (ids.has(entry.id)) throw new Error("Doppelte ID im Revit-Erstimport."); ids.add(entry.id); }
|
||||
return ids;
|
||||
}
|
||||
function isRecord(value: unknown): value is Record<string, unknown> { return value !== null && typeof value === "object" && !Array.isArray(value); }
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
import type { ExternalInitialImportProjectCommand } from "../models/external-initial-import-project-command.model.js";
|
||||
import type { AppendedProjectRevision, ProjectRevisionSource } from "./project-revision.store.js";
|
||||
|
||||
export interface ExecuteExternalInitialImportCommandInput {
|
||||
projectId: string;
|
||||
expectedRevision: number;
|
||||
source: ProjectRevisionSource;
|
||||
description?: string;
|
||||
actorId?: string;
|
||||
historyTargetChangeSetId?: string;
|
||||
command: ExternalInitialImportProjectCommand;
|
||||
}
|
||||
export interface ExternalInitialImportProjectCommandStore {
|
||||
execute(input: ExecuteExternalInitialImportCommandInput): { revision: AppendedProjectRevision; inverse: ExternalInitialImportProjectCommand };
|
||||
}
|
||||
|
|
@ -177,6 +177,11 @@ import {
|
|||
externalModelConfigurationUpdateCommandType,
|
||||
} from "../models/external-model-configuration-project-command.model.js";
|
||||
import type { ExternalModelConfigurationProjectCommandStore } from "../ports/external-model-configuration-project-command.store.js";
|
||||
import {
|
||||
assertExternalInitialImportProjectCommand,
|
||||
externalInitialImportCommandType,
|
||||
} from "../models/external-initial-import-project-command.model.js";
|
||||
import type { ExternalInitialImportProjectCommandStore } from "../ports/external-initial-import-project-command.store.js";
|
||||
import {
|
||||
assertExternalObjectRowAssignmentProjectCommand,
|
||||
externalObjectRowAssignmentCommandType,
|
||||
|
|
@ -242,7 +247,8 @@ export class ProjectCommandService implements ProjectCommandExecutor {
|
|||
private readonly externalObjectRowAssignmentStore?: ExternalObjectRowAssignmentProjectCommandStore,
|
||||
private readonly externalObjectNewRowStore?: ExternalObjectNewRowProjectCommandStore,
|
||||
private readonly externalObjectNewCircuitStore?: ExternalObjectNewCircuitProjectCommandStore,
|
||||
private readonly estimateBalanceStore?: EstimateBalanceProjectCommandStore
|
||||
private readonly estimateBalanceStore?: EstimateBalanceProjectCommandStore,
|
||||
private readonly externalInitialImportStore?: ExternalInitialImportProjectCommandStore
|
||||
) {}
|
||||
|
||||
executeUser(
|
||||
|
|
@ -631,6 +637,16 @@ export class ProjectCommandService implements ProjectCommandExecutor {
|
|||
command: input.command,
|
||||
}).revision;
|
||||
}
|
||||
case externalInitialImportCommandType: {
|
||||
assertExternalInitialImportProjectCommand(input.command);
|
||||
if (!this.externalInitialImportStore) {
|
||||
throw new Error("External initial import store is not available.");
|
||||
}
|
||||
return this.externalInitialImportStore.execute({
|
||||
...input,
|
||||
command: input.command,
|
||||
}).revision;
|
||||
}
|
||||
case externalObjectRowAssignmentCommandType: {
|
||||
assertExternalObjectRowAssignmentProjectCommand(input.command);
|
||||
if (!this.externalObjectRowAssignmentStore) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue