Files
leistungsbilanz-ts/src/domain/models/external-csv-configuration-project-command.model.ts
T

95 lines
3.6 KiB
TypeScript

import { assertExternalCsvConfiguration } from "../../external-model/csv/external-csv-configuration.js";
import type { ExternalCsvConfiguration } from "../../external-model/csv/external-csv-contracts.js";
import type { SerializedProjectCommand } from "./project-command.model.js";
export const externalCsvConfigurationUpdateCommandType =
"external-csv-configuration.update" as const;
export const externalCsvConfigurationCommandSchemaVersion = 1 as const;
export interface ExternalCsvConfigurationSnapshot {
id: string;
projectId: string;
configurationVersion: number;
configuration: ExternalCsvConfiguration;
}
export interface ExternalCsvConfigurationUpdatePayload {
expected: ExternalCsvConfigurationSnapshot | null;
target: ExternalCsvConfigurationSnapshot | null;
}
export interface ExternalCsvConfigurationUpdateProjectCommand
extends SerializedProjectCommand<ExternalCsvConfigurationUpdatePayload> {
schemaVersion: typeof externalCsvConfigurationCommandSchemaVersion;
type: typeof externalCsvConfigurationUpdateCommandType;
}
export function createExternalCsvConfigurationUpdateProjectCommand(
expected: ExternalCsvConfigurationSnapshot | null,
target: ExternalCsvConfigurationSnapshot | null
): ExternalCsvConfigurationUpdateProjectCommand {
const command: ExternalCsvConfigurationUpdateProjectCommand = {
schemaVersion: externalCsvConfigurationCommandSchemaVersion,
type: externalCsvConfigurationUpdateCommandType,
payload: { expected, target },
};
assertExternalCsvConfigurationUpdateProjectCommand(command);
return command;
}
export function assertExternalCsvConfigurationUpdateProjectCommand(
command: SerializedProjectCommand<unknown>
): asserts command is ExternalCsvConfigurationUpdateProjectCommand {
if (
command.schemaVersion !== externalCsvConfigurationCommandSchemaVersion ||
command.type !== externalCsvConfigurationUpdateCommandType ||
!isRecord(command.payload)
) {
throw new Error("Unsupported external CSV configuration update command.");
}
assertSnapshot(command.payload.expected, "expected");
assertSnapshot(command.payload.target, "target");
if (command.payload.expected === null && command.payload.target === null) {
throw new Error("External CSV configuration command must change state.");
}
if (
command.payload.expected !== null &&
command.payload.target !== null &&
(command.payload.expected.id !== command.payload.target.id ||
command.payload.expected.projectId !== command.payload.target.projectId)
) {
throw new Error("External CSV configuration identity must remain stable.");
}
}
function assertSnapshot(
value: unknown,
field: string
): asserts value is ExternalCsvConfigurationSnapshot | null {
if (value === null) {
return;
}
if (!isRecord(value)) {
throw new Error(`${field} external CSV configuration must be an object or null.`);
}
assertId(value.id, `${field}.id`);
assertId(value.projectId, `${field}.projectId`);
if (!Number.isSafeInteger(value.configurationVersion) || (value.configurationVersion as number) < 1) {
throw new Error(`${field}.configurationVersion must be a positive integer.`);
}
assertExternalCsvConfiguration(value.configuration);
if (Object.keys(value).length !== 4) {
throw new Error(`${field} external CSV configuration contains unknown fields.`);
}
}
function assertId(value: unknown, field: string) {
if (typeof value !== "string" || !value.trim()) {
throw new Error(`${field} must be a non-empty string.`);
}
}
function isRecord(value: unknown): value is Record<string, unknown> {
return value !== null && typeof value === "object" && !Array.isArray(value);
}