Add configurable distribution supply types
This commit is contained in:
@@ -1,11 +1,16 @@
|
||||
import crypto from "node:crypto";
|
||||
import {
|
||||
distributionBoardSupplyTypes,
|
||||
type DistributionBoardSupplyType,
|
||||
} from "../../shared/constants/distribution-board.js";
|
||||
import type { SerializedProjectCommand } from "./project-command.model.js";
|
||||
|
||||
export const distributionBoardInsertCommandType =
|
||||
"distribution-board.insert" as const;
|
||||
export const distributionBoardDeleteCommandType =
|
||||
"distribution-board.delete" as const;
|
||||
export const distributionBoardStructureCommandSchemaVersion = 1 as const;
|
||||
export const legacyDistributionBoardStructureCommandSchemaVersion = 1 as const;
|
||||
export const distributionBoardStructureCommandSchemaVersion = 2 as const;
|
||||
|
||||
export const defaultCircuitSectionDefinitions = [
|
||||
{
|
||||
@@ -34,11 +39,23 @@ export const defaultCircuitSectionDefinitions = [
|
||||
},
|
||||
] as const;
|
||||
|
||||
interface LegacyDistributionBoardStructureSnapshot {
|
||||
distributionBoard: {
|
||||
id: string;
|
||||
projectId: string;
|
||||
name: string;
|
||||
};
|
||||
circuitList: DistributionBoardStructureSnapshot["circuitList"];
|
||||
sections: DistributionBoardStructureSnapshot["sections"];
|
||||
}
|
||||
|
||||
export interface DistributionBoardStructureSnapshot {
|
||||
distributionBoard: {
|
||||
id: string;
|
||||
projectId: string;
|
||||
name: string;
|
||||
floorId: string | null;
|
||||
supplyType: DistributionBoardSupplyType | null;
|
||||
};
|
||||
circuitList: {
|
||||
id: string;
|
||||
@@ -56,29 +73,52 @@ export interface DistributionBoardStructureSnapshot {
|
||||
}>;
|
||||
}
|
||||
|
||||
interface DistributionBoardStructureCommandPayload {
|
||||
structure: DistributionBoardStructureSnapshot;
|
||||
interface DistributionBoardStructureCommandPayload<
|
||||
TStructure = DistributionBoardStructureSnapshot,
|
||||
> {
|
||||
structure: TStructure;
|
||||
}
|
||||
|
||||
interface CurrentDistributionBoardStructureProjectCommand
|
||||
extends SerializedProjectCommand<DistributionBoardStructureCommandPayload> {
|
||||
schemaVersion: typeof distributionBoardStructureCommandSchemaVersion;
|
||||
type:
|
||||
| typeof distributionBoardInsertCommandType
|
||||
| typeof distributionBoardDeleteCommandType;
|
||||
}
|
||||
|
||||
interface LegacyDistributionBoardStructureProjectCommand
|
||||
extends SerializedProjectCommand<
|
||||
DistributionBoardStructureCommandPayload<LegacyDistributionBoardStructureSnapshot>
|
||||
> {
|
||||
schemaVersion: typeof legacyDistributionBoardStructureCommandSchemaVersion;
|
||||
type:
|
||||
| typeof distributionBoardInsertCommandType
|
||||
| typeof distributionBoardDeleteCommandType;
|
||||
}
|
||||
|
||||
export interface DistributionBoardInsertProjectCommand
|
||||
extends SerializedProjectCommand<DistributionBoardStructureCommandPayload> {
|
||||
schemaVersion: typeof distributionBoardStructureCommandSchemaVersion;
|
||||
extends CurrentDistributionBoardStructureProjectCommand {
|
||||
type: typeof distributionBoardInsertCommandType;
|
||||
}
|
||||
|
||||
export interface DistributionBoardDeleteProjectCommand
|
||||
extends SerializedProjectCommand<DistributionBoardStructureCommandPayload> {
|
||||
schemaVersion: typeof distributionBoardStructureCommandSchemaVersion;
|
||||
extends CurrentDistributionBoardStructureProjectCommand {
|
||||
type: typeof distributionBoardDeleteCommandType;
|
||||
}
|
||||
|
||||
export type DistributionBoardStructureProjectCommand =
|
||||
| DistributionBoardInsertProjectCommand
|
||||
| DistributionBoardDeleteProjectCommand;
|
||||
| DistributionBoardDeleteProjectCommand
|
||||
| LegacyDistributionBoardStructureProjectCommand;
|
||||
|
||||
export function createDistributionBoardStructureSnapshot(
|
||||
projectId: string,
|
||||
name: string
|
||||
name: string,
|
||||
input: {
|
||||
floorId?: string | null;
|
||||
supplyType?: DistributionBoardSupplyType | null;
|
||||
} = {}
|
||||
): DistributionBoardStructureSnapshot {
|
||||
const normalizedName = name.trim();
|
||||
assertNonEmptyString(projectId, "projectId");
|
||||
@@ -89,6 +129,8 @@ export function createDistributionBoardStructureSnapshot(
|
||||
id: structureId,
|
||||
projectId,
|
||||
name: normalizedName,
|
||||
floorId: input.floorId ?? null,
|
||||
supplyType: input.supplyType ?? null,
|
||||
},
|
||||
circuitList: {
|
||||
id: structureId,
|
||||
@@ -132,7 +174,7 @@ export function createDistributionBoardDeleteProjectCommand(
|
||||
|
||||
export function assertDistributionBoardInsertProjectCommand(
|
||||
command: SerializedProjectCommand<unknown>
|
||||
): asserts command is DistributionBoardInsertProjectCommand {
|
||||
): asserts command is DistributionBoardStructureProjectCommand {
|
||||
assertDistributionBoardStructureProjectCommand(
|
||||
command,
|
||||
distributionBoardInsertCommandType
|
||||
@@ -141,23 +183,88 @@ export function assertDistributionBoardInsertProjectCommand(
|
||||
|
||||
export function assertDistributionBoardDeleteProjectCommand(
|
||||
command: SerializedProjectCommand<unknown>
|
||||
): asserts command is DistributionBoardDeleteProjectCommand {
|
||||
): asserts command is DistributionBoardStructureProjectCommand {
|
||||
assertDistributionBoardStructureProjectCommand(
|
||||
command,
|
||||
distributionBoardDeleteCommandType
|
||||
);
|
||||
}
|
||||
|
||||
export function normalizeDistributionBoardStructureProjectCommand(
|
||||
command: DistributionBoardStructureProjectCommand
|
||||
): DistributionBoardInsertProjectCommand | DistributionBoardDeleteProjectCommand {
|
||||
if (
|
||||
command.schemaVersion ===
|
||||
legacyDistributionBoardStructureCommandSchemaVersion
|
||||
) {
|
||||
const structure: DistributionBoardStructureSnapshot = {
|
||||
...command.payload.structure,
|
||||
distributionBoard: {
|
||||
...command.payload.structure.distributionBoard,
|
||||
floorId: null,
|
||||
supplyType: null,
|
||||
},
|
||||
};
|
||||
return command.type === distributionBoardInsertCommandType
|
||||
? createDistributionBoardInsertProjectCommand(structure)
|
||||
: createDistributionBoardDeleteProjectCommand(structure);
|
||||
}
|
||||
return command as
|
||||
| DistributionBoardInsertProjectCommand
|
||||
| DistributionBoardDeleteProjectCommand;
|
||||
}
|
||||
|
||||
export function assertDistributionBoardStructureSnapshot(
|
||||
structure: unknown
|
||||
): asserts structure is DistributionBoardStructureSnapshot {
|
||||
assertDistributionBoardStructureSnapshotVersion(structure, false);
|
||||
}
|
||||
|
||||
function assertDistributionBoardStructureProjectCommand(
|
||||
command: SerializedProjectCommand<unknown>,
|
||||
expectedType:
|
||||
| typeof distributionBoardInsertCommandType
|
||||
| typeof distributionBoardDeleteCommandType
|
||||
) {
|
||||
if (
|
||||
command.type !== expectedType ||
|
||||
!isPlainObject(command.payload) ||
|
||||
Object.keys(command.payload).length !== 1
|
||||
) {
|
||||
throw new Error("Unsupported distribution-board structure command.");
|
||||
}
|
||||
if (
|
||||
command.schemaVersion ===
|
||||
legacyDistributionBoardStructureCommandSchemaVersion
|
||||
) {
|
||||
assertDistributionBoardStructureSnapshotVersion(
|
||||
command.payload.structure,
|
||||
true
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (
|
||||
command.schemaVersion !== distributionBoardStructureCommandSchemaVersion
|
||||
) {
|
||||
throw new Error("Unsupported distribution-board structure command.");
|
||||
}
|
||||
assertDistributionBoardStructureSnapshotVersion(
|
||||
command.payload.structure,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
function assertDistributionBoardStructureSnapshotVersion(
|
||||
structure: unknown,
|
||||
legacy: boolean
|
||||
) {
|
||||
if (!isPlainObject(structure) || Object.keys(structure).length !== 3) {
|
||||
throw new Error("Distribution-board structure is invalid.");
|
||||
}
|
||||
const { distributionBoard, circuitList, sections } = structure;
|
||||
if (
|
||||
!isPlainObject(distributionBoard) ||
|
||||
Object.keys(distributionBoard).length !== 3 ||
|
||||
Object.keys(distributionBoard).length !== (legacy ? 3 : 5) ||
|
||||
!isPlainObject(circuitList) ||
|
||||
Object.keys(circuitList).length !== 4 ||
|
||||
!Array.isArray(sections) ||
|
||||
@@ -165,8 +272,15 @@ export function assertDistributionBoardStructureSnapshot(
|
||||
) {
|
||||
throw new Error("Distribution-board structure is incomplete.");
|
||||
}
|
||||
for (const [field, value] of Object.entries(distributionBoard)) {
|
||||
assertNonEmptyString(value, `distributionBoard.${field}`);
|
||||
for (const field of ["id", "projectId", "name"] as const) {
|
||||
assertNonEmptyString(
|
||||
distributionBoard[field],
|
||||
`distributionBoard.${field}`
|
||||
);
|
||||
}
|
||||
if (!legacy) {
|
||||
assertNullableId(distributionBoard.floorId, "distributionBoard.floorId");
|
||||
assertNullableSupplyType(distributionBoard.supplyType);
|
||||
}
|
||||
for (const [field, value] of Object.entries(circuitList)) {
|
||||
assertNonEmptyString(value, `circuitList.${field}`);
|
||||
@@ -174,22 +288,16 @@ export function assertDistributionBoardStructureSnapshot(
|
||||
if (
|
||||
circuitList.projectId !== distributionBoard.projectId ||
|
||||
circuitList.distributionBoardId !== distributionBoard.id ||
|
||||
circuitList.name !==
|
||||
`${distributionBoard.name} Stromkreisliste`
|
||||
circuitList.name !== `${distributionBoard.name} Stromkreisliste`
|
||||
) {
|
||||
throw new Error(
|
||||
"Distribution board and circuit list are inconsistent."
|
||||
);
|
||||
throw new Error("Distribution board and circuit list are inconsistent.");
|
||||
}
|
||||
|
||||
const sectionIds = new Set<string>();
|
||||
for (let index = 0; index < sections.length; index += 1) {
|
||||
const section = sections[index];
|
||||
const expected = defaultCircuitSectionDefinitions[index];
|
||||
if (
|
||||
!isPlainObject(section) ||
|
||||
Object.keys(section).length !== 6
|
||||
) {
|
||||
if (!isPlainObject(section) || Object.keys(section).length !== 6) {
|
||||
throw new Error("Default circuit section is invalid.");
|
||||
}
|
||||
assertNonEmptyString(section.id, "section.id");
|
||||
@@ -211,22 +319,23 @@ export function assertDistributionBoardStructureSnapshot(
|
||||
}
|
||||
}
|
||||
|
||||
function assertDistributionBoardStructureProjectCommand(
|
||||
command: SerializedProjectCommand<unknown>,
|
||||
expectedType:
|
||||
| typeof distributionBoardInsertCommandType
|
||||
| typeof distributionBoardDeleteCommandType
|
||||
) {
|
||||
if (
|
||||
command.schemaVersion !==
|
||||
distributionBoardStructureCommandSchemaVersion ||
|
||||
command.type !== expectedType ||
|
||||
!isPlainObject(command.payload) ||
|
||||
Object.keys(command.payload).length !== 1
|
||||
) {
|
||||
throw new Error("Unsupported distribution-board structure command.");
|
||||
function assertNullableId(value: unknown, field: string) {
|
||||
if (value !== null) {
|
||||
assertNonEmptyString(value, field);
|
||||
}
|
||||
}
|
||||
|
||||
function assertNullableSupplyType(
|
||||
value: unknown
|
||||
): asserts value is DistributionBoardSupplyType | null {
|
||||
if (
|
||||
value !== null &&
|
||||
!distributionBoardSupplyTypes.includes(
|
||||
value as DistributionBoardSupplyType
|
||||
)
|
||||
) {
|
||||
throw new Error("distributionBoard.supplyType is invalid.");
|
||||
}
|
||||
assertDistributionBoardStructureSnapshot(command.payload.structure);
|
||||
}
|
||||
|
||||
function assertNonEmptyString(
|
||||
|
||||
Reference in New Issue
Block a user