leistungsbilanz-ts/src/domain/models/project-settings-project-command.model.ts

158 lines
4.8 KiB
TypeScript

import type { SerializedProjectCommand } from "./project-command.model.js";
import {
distributionBoardSupplyTypes,
type DistributionBoardSupplyType,
} from "../../shared/constants/distribution-board.js";
export const projectSettingsUpdateCommandType =
"project.update-settings" as const;
export const projectSettingsUpdateCommandSchemaVersion = 2 as const;
const previousProjectSettingsUpdateCommandSchemaVersion = 1 as const;
export interface ProjectSettingsValues {
name: string;
internalProjectNumber: string | null;
externalProjectNumber: string | null;
buildingOwner: string | null;
description: string | null;
isPublicBuilding: boolean;
singlePhaseVoltageV: number;
threePhaseVoltageV: number;
enabledDistributionBoardSupplyTypes: DistributionBoardSupplyType[];
}
type PreviousProjectSettingsValues = Omit<
ProjectSettingsValues,
"isPublicBuilding"
>;
export interface CurrentProjectSettingsUpdateProjectCommand
extends SerializedProjectCommand<ProjectSettingsValues> {
schemaVersion: typeof projectSettingsUpdateCommandSchemaVersion;
type: typeof projectSettingsUpdateCommandType;
}
interface PreviousProjectSettingsUpdateProjectCommand
extends SerializedProjectCommand<PreviousProjectSettingsValues> {
schemaVersion: typeof previousProjectSettingsUpdateCommandSchemaVersion;
type: typeof projectSettingsUpdateCommandType;
}
export type ProjectSettingsUpdateProjectCommand =
| CurrentProjectSettingsUpdateProjectCommand
| PreviousProjectSettingsUpdateProjectCommand;
export function createProjectSettingsUpdateProjectCommand(
values: ProjectSettingsValues
): CurrentProjectSettingsUpdateProjectCommand {
const command: CurrentProjectSettingsUpdateProjectCommand = {
schemaVersion: projectSettingsUpdateCommandSchemaVersion,
type: projectSettingsUpdateCommandType,
payload: { ...values },
};
assertProjectSettingsUpdateProjectCommand(command);
return command;
}
export function assertProjectSettingsUpdateProjectCommand(
command: SerializedProjectCommand<unknown>
): asserts command is ProjectSettingsUpdateProjectCommand {
if (
command.type !== projectSettingsUpdateCommandType ||
(command.schemaVersion !== projectSettingsUpdateCommandSchemaVersion &&
command.schemaVersion !== previousProjectSettingsUpdateCommandSchemaVersion)
) {
throw new Error("Unsupported project settings update command.");
}
if (!isPlainObject(command.payload)) {
throw new Error(
"Project settings update command contains invalid values."
);
}
if (
!isTrimmedString(command.payload.name, 1, 200) ||
!isNullableTrimmedString(command.payload.internalProjectNumber, 100) ||
!isNullableTrimmedString(command.payload.externalProjectNumber, 100) ||
!isNullableTrimmedString(command.payload.buildingOwner, 200) ||
!isNullableTrimmedString(command.payload.description, 2000) ||
!isPositiveFiniteNumber(command.payload.singlePhaseVoltageV) ||
!isPositiveFiniteNumber(command.payload.threePhaseVoltageV)
) {
throw new Error(
"Project settings update command contains invalid values."
);
}
if (
!isValidSupplyTypes(
command.payload.enabledDistributionBoardSupplyTypes
)
) {
throw new Error(
"Project settings update command contains invalid supply types."
);
}
const isPreviousSchema =
command.schemaVersion === previousProjectSettingsUpdateCommandSchemaVersion;
if (
(isPreviousSchema && Object.keys(command.payload).length !== 8) ||
(!isPreviousSchema &&
(typeof command.payload.isPublicBuilding !== "boolean" ||
Object.keys(command.payload).length !== 9))
) {
throw new Error(
"Project settings update command contains invalid values."
);
}
}
function isValidSupplyTypes(
value: unknown
): value is DistributionBoardSupplyType[] {
return (
Array.isArray(value) &&
value.length > 0 &&
new Set(value).size === value.length &&
value.every(
(entry) =>
typeof entry === "string" &&
distributionBoardSupplyTypes.includes(
entry as DistributionBoardSupplyType
)
)
);
}
function isPositiveFiniteNumber(value: unknown): value is number {
return (
typeof value === "number" &&
Number.isFinite(value) &&
value > 0
);
}
function isPlainObject(value: unknown): value is Record<string, unknown> {
return value !== null && typeof value === "object" && !Array.isArray(value);
}
function isTrimmedString(
value: unknown,
minimumLength: number,
maximumLength: number
): value is string {
return (
typeof value === "string" &&
value === value.trim() &&
value.length >= minimumLength &&
value.length <= maximumLength
);
}
function isNullableTrimmedString(
value: unknown,
maximumLength: number
): value is string | null {
return (
value === null ||
(isTrimmedString(value, 1, maximumLength))
);
}