Add placeholder move history
This commit is contained in:
@@ -1,8 +1,15 @@
|
||||
import { and, eq, inArray } from "drizzle-orm";
|
||||
import {
|
||||
assertCircuitDeviceRowMoveProjectCommand,
|
||||
assertCircuitDeviceRowMoveWithNewCircuitProjectCommand,
|
||||
circuitDeviceRowMoveCommandType,
|
||||
circuitDeviceRowMoveWithNewCircuitCommandType,
|
||||
createCircuitDeviceRowMoveProjectCommand,
|
||||
createCircuitDeviceRowMoveWithNewCircuitProjectCommand,
|
||||
type CircuitDeviceRowMoveAssignment,
|
||||
type CircuitDeviceRowMoveHistoryProjectCommand,
|
||||
} from "../../domain/models/circuit-device-row-move-project-command.model.js";
|
||||
import type { CircuitSnapshot } from "../../domain/models/circuit-structure-project-command.model.js";
|
||||
import type {
|
||||
CircuitDeviceRowMoveProjectCommandStore,
|
||||
ExecuteCircuitDeviceRowMoveCommandInput,
|
||||
@@ -10,156 +17,31 @@ import type {
|
||||
import type { AppDatabase } from "../database-context.js";
|
||||
import { circuitDeviceRows } from "../schema/circuit-device-rows.js";
|
||||
import { circuitLists } from "../schema/circuit-lists.js";
|
||||
import { circuitSections } from "../schema/circuit-sections.js";
|
||||
import { circuits } from "../schema/circuits.js";
|
||||
import { applyProjectHistoryTransition } from "./project-history.persistence.js";
|
||||
import { appendProjectRevision } from "./project-revision.persistence.js";
|
||||
|
||||
interface PersistedMoveRow {
|
||||
id: string;
|
||||
circuitId: string;
|
||||
sortOrder: number;
|
||||
}
|
||||
|
||||
export class CircuitDeviceRowMoveProjectCommandRepository
|
||||
implements CircuitDeviceRowMoveProjectCommandStore
|
||||
{
|
||||
constructor(private readonly database: AppDatabase) {}
|
||||
|
||||
execute(input: ExecuteCircuitDeviceRowMoveCommandInput) {
|
||||
assertCircuitDeviceRowMoveProjectCommand(input.command);
|
||||
this.assertSupportedCommand(input.command);
|
||||
|
||||
return this.database.transaction((tx) => {
|
||||
const rowIds = input.command.payload.moves.map(
|
||||
(move) => move.rowId
|
||||
const inverse = this.applyCommand(
|
||||
tx,
|
||||
input.projectId,
|
||||
input.command
|
||||
);
|
||||
const persistedRows = tx
|
||||
.select({
|
||||
id: circuitDeviceRows.id,
|
||||
circuitId: circuitDeviceRows.circuitId,
|
||||
sortOrder: circuitDeviceRows.sortOrder,
|
||||
})
|
||||
.from(circuitDeviceRows)
|
||||
.where(inArray(circuitDeviceRows.id, rowIds))
|
||||
.all();
|
||||
if (persistedRows.length !== rowIds.length) {
|
||||
throw new Error(
|
||||
"One or more circuit device rows do not exist."
|
||||
);
|
||||
}
|
||||
const rowsById = new Map(
|
||||
persistedRows.map((row) => [row.id, row])
|
||||
);
|
||||
for (const move of input.command.payload.moves) {
|
||||
const row = rowsById.get(move.rowId);
|
||||
if (
|
||||
!row ||
|
||||
row.circuitId !== move.expectedCircuitId ||
|
||||
row.sortOrder !== move.expectedSortOrder
|
||||
) {
|
||||
throw new Error(
|
||||
"Circuit device row changed before move execution."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const circuitIds = [
|
||||
...new Set(
|
||||
input.command.payload.moves.flatMap((move) => [
|
||||
move.expectedCircuitId,
|
||||
move.targetCircuitId,
|
||||
])
|
||||
),
|
||||
];
|
||||
const participatingCircuits = tx
|
||||
.select({
|
||||
id: circuits.id,
|
||||
circuitListId: circuits.circuitListId,
|
||||
projectId: circuitLists.projectId,
|
||||
})
|
||||
.from(circuits)
|
||||
.innerJoin(
|
||||
circuitLists,
|
||||
eq(circuitLists.id, circuits.circuitListId)
|
||||
)
|
||||
.where(inArray(circuits.id, circuitIds))
|
||||
.all();
|
||||
if (participatingCircuits.length !== circuitIds.length) {
|
||||
throw new Error(
|
||||
"One or more move circuits do not exist."
|
||||
);
|
||||
}
|
||||
if (
|
||||
participatingCircuits.some(
|
||||
(circuit) => circuit.projectId !== input.projectId
|
||||
)
|
||||
) {
|
||||
throw new Error("Move circuit does not belong to project.");
|
||||
}
|
||||
const circuitListIds = new Set(
|
||||
participatingCircuits.map(
|
||||
(circuit) => circuit.circuitListId
|
||||
)
|
||||
);
|
||||
if (circuitListIds.size !== 1) {
|
||||
throw new Error(
|
||||
"All moved rows and targets must belong to one circuit list."
|
||||
);
|
||||
}
|
||||
|
||||
const inverse = createCircuitDeviceRowMoveProjectCommand(
|
||||
input.command.payload.moves.map((move) => {
|
||||
const current = rowsById.get(move.rowId)!;
|
||||
return {
|
||||
rowId: move.rowId,
|
||||
expectedCircuitId: move.targetCircuitId,
|
||||
expectedSortOrder: move.targetSortOrder,
|
||||
targetCircuitId: current.circuitId,
|
||||
targetSortOrder: current.sortOrder,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
for (const move of input.command.payload.moves) {
|
||||
const result = tx
|
||||
.update(circuitDeviceRows)
|
||||
.set({
|
||||
circuitId: move.targetCircuitId,
|
||||
sortOrder: move.targetSortOrder,
|
||||
})
|
||||
.where(
|
||||
and(
|
||||
eq(circuitDeviceRows.id, move.rowId),
|
||||
eq(
|
||||
circuitDeviceRows.circuitId,
|
||||
move.expectedCircuitId
|
||||
),
|
||||
eq(
|
||||
circuitDeviceRows.sortOrder,
|
||||
move.expectedSortOrder
|
||||
)
|
||||
)
|
||||
)
|
||||
.run();
|
||||
if (result.changes !== 1) {
|
||||
throw new Error(
|
||||
"Circuit device row changed during move execution."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (const circuitId of circuitIds) {
|
||||
const remainingRow = tx
|
||||
.select({ id: circuitDeviceRows.id })
|
||||
.from(circuitDeviceRows)
|
||||
.where(eq(circuitDeviceRows.circuitId, circuitId))
|
||||
.limit(1)
|
||||
.get();
|
||||
const updated = tx
|
||||
.update(circuits)
|
||||
.set({ isReserve: remainingRow ? 0 : 1 })
|
||||
.where(eq(circuits.id, circuitId))
|
||||
.run();
|
||||
if (updated.changes !== 1) {
|
||||
throw new Error(
|
||||
"Circuit changed during device-row move execution."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const revision = appendProjectRevision(tx, {
|
||||
projectId: input.projectId,
|
||||
expectedRevision: input.expectedRevision,
|
||||
@@ -178,4 +60,457 @@ export class CircuitDeviceRowMoveProjectCommandRepository
|
||||
return { revision, inverse };
|
||||
});
|
||||
}
|
||||
|
||||
private assertSupportedCommand(
|
||||
command: CircuitDeviceRowMoveHistoryProjectCommand
|
||||
) {
|
||||
if (command.type === circuitDeviceRowMoveCommandType) {
|
||||
assertCircuitDeviceRowMoveProjectCommand(command);
|
||||
return;
|
||||
}
|
||||
if (
|
||||
command.type ===
|
||||
circuitDeviceRowMoveWithNewCircuitCommandType
|
||||
) {
|
||||
assertCircuitDeviceRowMoveWithNewCircuitProjectCommand(
|
||||
command
|
||||
);
|
||||
return;
|
||||
}
|
||||
throw new Error("Unsupported circuit device-row move command.");
|
||||
}
|
||||
|
||||
private applyCommand(
|
||||
database: AppDatabase,
|
||||
projectId: string,
|
||||
command: CircuitDeviceRowMoveHistoryProjectCommand
|
||||
): CircuitDeviceRowMoveHistoryProjectCommand {
|
||||
if (command.type === circuitDeviceRowMoveCommandType) {
|
||||
const rowsById = this.loadExpectedRows(
|
||||
database,
|
||||
command.payload.moves
|
||||
);
|
||||
const circuitIds = this.collectCircuitIds(
|
||||
command.payload.moves
|
||||
);
|
||||
this.assertExistingCircuitsInOneList(
|
||||
database,
|
||||
projectId,
|
||||
circuitIds
|
||||
);
|
||||
const inverse = createCircuitDeviceRowMoveProjectCommand(
|
||||
this.reverseMoves(command.payload.moves, rowsById)
|
||||
);
|
||||
this.applyMoves(database, command.payload.moves);
|
||||
this.updateReserveStates(database, circuitIds);
|
||||
return inverse;
|
||||
}
|
||||
|
||||
assertCircuitDeviceRowMoveWithNewCircuitProjectCommand(
|
||||
command
|
||||
);
|
||||
const { targetCircuit, targetCircuitAction, moves } =
|
||||
command.payload;
|
||||
const rowsById = this.loadExpectedRows(database, moves);
|
||||
this.assertCircuitLocation(database, projectId, targetCircuit);
|
||||
|
||||
if (targetCircuitAction === "create") {
|
||||
const sourceCircuitIds = [
|
||||
...new Set(moves.map((move) => move.expectedCircuitId)),
|
||||
];
|
||||
this.assertExistingCircuitsInOneList(
|
||||
database,
|
||||
projectId,
|
||||
sourceCircuitIds,
|
||||
targetCircuit.circuitListId
|
||||
);
|
||||
this.assertTargetCircuitAvailable(database, targetCircuit);
|
||||
this.insertTargetCircuit(database, targetCircuit);
|
||||
|
||||
const inverse =
|
||||
createCircuitDeviceRowMoveWithNewCircuitProjectCommand(
|
||||
"delete",
|
||||
targetCircuit,
|
||||
this.reverseMoves(moves, rowsById)
|
||||
);
|
||||
this.applyMoves(database, moves);
|
||||
this.updateReserveStates(database, [
|
||||
...sourceCircuitIds,
|
||||
targetCircuit.id,
|
||||
]);
|
||||
return inverse;
|
||||
}
|
||||
|
||||
this.assertTargetCircuitUnchanged(
|
||||
database,
|
||||
targetCircuit,
|
||||
moves.map((move) => move.rowId)
|
||||
);
|
||||
const destinationCircuitIds = [
|
||||
...new Set(moves.map((move) => move.targetCircuitId)),
|
||||
];
|
||||
this.assertExistingCircuitsInOneList(
|
||||
database,
|
||||
projectId,
|
||||
destinationCircuitIds,
|
||||
targetCircuit.circuitListId
|
||||
);
|
||||
const inverse =
|
||||
createCircuitDeviceRowMoveWithNewCircuitProjectCommand(
|
||||
"create",
|
||||
targetCircuit,
|
||||
this.reverseMoves(moves, rowsById)
|
||||
);
|
||||
this.applyMoves(database, moves);
|
||||
this.updateReserveStates(database, [
|
||||
targetCircuit.id,
|
||||
...destinationCircuitIds,
|
||||
]);
|
||||
const deleted = database
|
||||
.delete(circuits)
|
||||
.where(
|
||||
and(
|
||||
eq(circuits.id, targetCircuit.id),
|
||||
eq(
|
||||
circuits.circuitListId,
|
||||
targetCircuit.circuitListId
|
||||
),
|
||||
eq(circuits.isReserve, 1)
|
||||
)
|
||||
)
|
||||
.run();
|
||||
if (deleted.changes !== 1) {
|
||||
throw new Error(
|
||||
"Created target circuit changed before deletion."
|
||||
);
|
||||
}
|
||||
return inverse;
|
||||
}
|
||||
|
||||
private loadExpectedRows(
|
||||
database: AppDatabase,
|
||||
moves: CircuitDeviceRowMoveAssignment[]
|
||||
) {
|
||||
const rowIds = moves.map((move) => move.rowId);
|
||||
const persistedRows = database
|
||||
.select({
|
||||
id: circuitDeviceRows.id,
|
||||
circuitId: circuitDeviceRows.circuitId,
|
||||
sortOrder: circuitDeviceRows.sortOrder,
|
||||
})
|
||||
.from(circuitDeviceRows)
|
||||
.where(inArray(circuitDeviceRows.id, rowIds))
|
||||
.all();
|
||||
if (persistedRows.length !== rowIds.length) {
|
||||
throw new Error(
|
||||
"One or more circuit device rows do not exist."
|
||||
);
|
||||
}
|
||||
|
||||
const rowsById = new Map(
|
||||
persistedRows.map((row) => [row.id, row])
|
||||
);
|
||||
for (const move of moves) {
|
||||
const row = rowsById.get(move.rowId);
|
||||
if (
|
||||
!row ||
|
||||
row.circuitId !== move.expectedCircuitId ||
|
||||
row.sortOrder !== move.expectedSortOrder
|
||||
) {
|
||||
throw new Error(
|
||||
"Circuit device row changed before move execution."
|
||||
);
|
||||
}
|
||||
}
|
||||
return rowsById;
|
||||
}
|
||||
|
||||
private collectCircuitIds(
|
||||
moves: CircuitDeviceRowMoveAssignment[]
|
||||
) {
|
||||
return [
|
||||
...new Set(
|
||||
moves.flatMap((move) => [
|
||||
move.expectedCircuitId,
|
||||
move.targetCircuitId,
|
||||
])
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
private assertExistingCircuitsInOneList(
|
||||
database: AppDatabase,
|
||||
projectId: string,
|
||||
circuitIds: string[],
|
||||
expectedCircuitListId?: string
|
||||
) {
|
||||
const participatingCircuits = database
|
||||
.select({
|
||||
id: circuits.id,
|
||||
circuitListId: circuits.circuitListId,
|
||||
projectId: circuitLists.projectId,
|
||||
})
|
||||
.from(circuits)
|
||||
.innerJoin(
|
||||
circuitLists,
|
||||
eq(circuitLists.id, circuits.circuitListId)
|
||||
)
|
||||
.where(inArray(circuits.id, circuitIds))
|
||||
.all();
|
||||
if (participatingCircuits.length !== circuitIds.length) {
|
||||
throw new Error(
|
||||
"One or more move circuits do not exist."
|
||||
);
|
||||
}
|
||||
if (
|
||||
participatingCircuits.some(
|
||||
(circuit) => circuit.projectId !== projectId
|
||||
)
|
||||
) {
|
||||
throw new Error("Move circuit does not belong to project.");
|
||||
}
|
||||
const circuitListIds = new Set(
|
||||
participatingCircuits.map(
|
||||
(circuit) => circuit.circuitListId
|
||||
)
|
||||
);
|
||||
if (
|
||||
circuitListIds.size !== 1 ||
|
||||
(expectedCircuitListId !== undefined &&
|
||||
!circuitListIds.has(expectedCircuitListId))
|
||||
) {
|
||||
throw new Error(
|
||||
"All moved rows and targets must belong to one circuit list."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private assertCircuitLocation(
|
||||
database: AppDatabase,
|
||||
projectId: string,
|
||||
snapshot: CircuitSnapshot
|
||||
) {
|
||||
const list = database
|
||||
.select({ id: circuitLists.id })
|
||||
.from(circuitLists)
|
||||
.where(
|
||||
and(
|
||||
eq(circuitLists.id, snapshot.circuitListId),
|
||||
eq(circuitLists.projectId, projectId)
|
||||
)
|
||||
)
|
||||
.get();
|
||||
if (!list) {
|
||||
throw new Error("Circuit list does not belong to project.");
|
||||
}
|
||||
const section = database
|
||||
.select({ id: circuitSections.id })
|
||||
.from(circuitSections)
|
||||
.where(
|
||||
and(
|
||||
eq(circuitSections.id, snapshot.sectionId),
|
||||
eq(
|
||||
circuitSections.circuitListId,
|
||||
snapshot.circuitListId
|
||||
)
|
||||
)
|
||||
)
|
||||
.get();
|
||||
if (!section) {
|
||||
throw new Error("Section does not belong to circuit list.");
|
||||
}
|
||||
}
|
||||
|
||||
private assertTargetCircuitAvailable(
|
||||
database: AppDatabase,
|
||||
snapshot: CircuitSnapshot
|
||||
) {
|
||||
const existingCircuit = database
|
||||
.select({ id: circuits.id })
|
||||
.from(circuits)
|
||||
.where(eq(circuits.id, snapshot.id))
|
||||
.get();
|
||||
if (existingCircuit) {
|
||||
throw new Error("Move target circuit id already exists.");
|
||||
}
|
||||
const duplicateIdentifier = database
|
||||
.select({ id: circuits.id })
|
||||
.from(circuits)
|
||||
.where(
|
||||
and(
|
||||
eq(circuits.circuitListId, snapshot.circuitListId),
|
||||
eq(
|
||||
circuits.equipmentIdentifier,
|
||||
snapshot.equipmentIdentifier
|
||||
)
|
||||
)
|
||||
)
|
||||
.get();
|
||||
if (duplicateIdentifier) {
|
||||
throw new Error(
|
||||
"Duplicate equipmentIdentifier in circuit list."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private insertTargetCircuit(
|
||||
database: AppDatabase,
|
||||
snapshot: CircuitSnapshot
|
||||
) {
|
||||
database
|
||||
.insert(circuits)
|
||||
.values({
|
||||
id: snapshot.id,
|
||||
circuitListId: snapshot.circuitListId,
|
||||
sectionId: snapshot.sectionId,
|
||||
equipmentIdentifier: snapshot.equipmentIdentifier,
|
||||
displayName: snapshot.displayName,
|
||||
sortOrder: snapshot.sortOrder,
|
||||
protectionType: snapshot.protectionType,
|
||||
protectionRatedCurrent: snapshot.protectionRatedCurrent,
|
||||
protectionCharacteristic: snapshot.protectionCharacteristic,
|
||||
cableType: snapshot.cableType,
|
||||
cableCrossSection: snapshot.cableCrossSection,
|
||||
cableLength: snapshot.cableLength,
|
||||
rcdAssignment: snapshot.rcdAssignment,
|
||||
terminalDesignation: snapshot.terminalDesignation,
|
||||
voltage: snapshot.voltage,
|
||||
controlRequirement: snapshot.controlRequirement,
|
||||
status: snapshot.status,
|
||||
isReserve: 1,
|
||||
remark: snapshot.remark,
|
||||
})
|
||||
.run();
|
||||
}
|
||||
|
||||
private assertTargetCircuitUnchanged(
|
||||
database: AppDatabase,
|
||||
snapshot: CircuitSnapshot,
|
||||
expectedRowIds: string[]
|
||||
) {
|
||||
const current = database
|
||||
.select()
|
||||
.from(circuits)
|
||||
.where(eq(circuits.id, snapshot.id))
|
||||
.get();
|
||||
if (
|
||||
!current ||
|
||||
current.circuitListId !== snapshot.circuitListId ||
|
||||
current.sectionId !== snapshot.sectionId ||
|
||||
current.equipmentIdentifier !==
|
||||
snapshot.equipmentIdentifier ||
|
||||
current.displayName !== snapshot.displayName ||
|
||||
current.sortOrder !== snapshot.sortOrder ||
|
||||
current.protectionType !== snapshot.protectionType ||
|
||||
current.protectionRatedCurrent !==
|
||||
snapshot.protectionRatedCurrent ||
|
||||
current.protectionCharacteristic !==
|
||||
snapshot.protectionCharacteristic ||
|
||||
current.cableType !== snapshot.cableType ||
|
||||
current.cableCrossSection !== snapshot.cableCrossSection ||
|
||||
current.cableLength !== snapshot.cableLength ||
|
||||
current.rcdAssignment !== snapshot.rcdAssignment ||
|
||||
current.terminalDesignation !==
|
||||
snapshot.terminalDesignation ||
|
||||
current.voltage !== snapshot.voltage ||
|
||||
current.controlRequirement !==
|
||||
snapshot.controlRequirement ||
|
||||
current.status !== snapshot.status ||
|
||||
current.remark !== snapshot.remark ||
|
||||
Boolean(current.isReserve)
|
||||
) {
|
||||
throw new Error(
|
||||
"Created target circuit changed before command execution."
|
||||
);
|
||||
}
|
||||
|
||||
const currentRowIds = database
|
||||
.select({ id: circuitDeviceRows.id })
|
||||
.from(circuitDeviceRows)
|
||||
.where(eq(circuitDeviceRows.circuitId, snapshot.id))
|
||||
.all()
|
||||
.map((row) => row.id);
|
||||
const expectedRowIdSet = new Set(expectedRowIds);
|
||||
if (
|
||||
currentRowIds.length !== expectedRowIds.length ||
|
||||
currentRowIds.some((id) => !expectedRowIdSet.has(id))
|
||||
) {
|
||||
throw new Error(
|
||||
"Created target circuit rows changed before command execution."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private reverseMoves(
|
||||
moves: CircuitDeviceRowMoveAssignment[],
|
||||
rowsById: Map<string, PersistedMoveRow>
|
||||
) {
|
||||
return moves.map((move) => {
|
||||
const current = rowsById.get(move.rowId)!;
|
||||
return {
|
||||
rowId: move.rowId,
|
||||
expectedCircuitId: move.targetCircuitId,
|
||||
expectedSortOrder: move.targetSortOrder,
|
||||
targetCircuitId: current.circuitId,
|
||||
targetSortOrder: current.sortOrder,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private applyMoves(
|
||||
database: AppDatabase,
|
||||
moves: CircuitDeviceRowMoveAssignment[]
|
||||
) {
|
||||
for (const move of moves) {
|
||||
const result = database
|
||||
.update(circuitDeviceRows)
|
||||
.set({
|
||||
circuitId: move.targetCircuitId,
|
||||
sortOrder: move.targetSortOrder,
|
||||
})
|
||||
.where(
|
||||
and(
|
||||
eq(circuitDeviceRows.id, move.rowId),
|
||||
eq(
|
||||
circuitDeviceRows.circuitId,
|
||||
move.expectedCircuitId
|
||||
),
|
||||
eq(
|
||||
circuitDeviceRows.sortOrder,
|
||||
move.expectedSortOrder
|
||||
)
|
||||
)
|
||||
)
|
||||
.run();
|
||||
if (result.changes !== 1) {
|
||||
throw new Error(
|
||||
"Circuit device row changed during move execution."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private updateReserveStates(
|
||||
database: AppDatabase,
|
||||
circuitIds: string[]
|
||||
) {
|
||||
for (const circuitId of new Set(circuitIds)) {
|
||||
const remainingRow = database
|
||||
.select({ id: circuitDeviceRows.id })
|
||||
.from(circuitDeviceRows)
|
||||
.where(eq(circuitDeviceRows.circuitId, circuitId))
|
||||
.limit(1)
|
||||
.get();
|
||||
const updated = database
|
||||
.update(circuits)
|
||||
.set({ isReserve: remainingRow ? 0 : 1 })
|
||||
.where(eq(circuits.id, circuitId))
|
||||
.run();
|
||||
if (updated.changes !== 1) {
|
||||
throw new Error(
|
||||
"Circuit changed during device-row move execution."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
import type { SerializedProjectCommand } from "./project-command.model.js";
|
||||
import {
|
||||
assertCircuitInsertProjectCommand,
|
||||
circuitInsertCommandType,
|
||||
circuitStructureCommandSchemaVersion,
|
||||
type CircuitSnapshot,
|
||||
} from "./circuit-structure-project-command.model.js";
|
||||
|
||||
export const circuitDeviceRowMoveCommandType =
|
||||
"circuit-device-row.move" as const;
|
||||
export const circuitDeviceRowMoveWithNewCircuitCommandType =
|
||||
"circuit-device-row.move-with-new-circuit" as const;
|
||||
export const circuitDeviceRowMoveCommandSchemaVersion = 1 as const;
|
||||
|
||||
export interface CircuitDeviceRowMoveAssignment {
|
||||
@@ -22,6 +30,26 @@ export interface CircuitDeviceRowMoveProjectCommand
|
||||
type: typeof circuitDeviceRowMoveCommandType;
|
||||
}
|
||||
|
||||
export type CircuitDeviceRowMoveTargetCircuitAction =
|
||||
| "create"
|
||||
| "delete";
|
||||
|
||||
export interface CircuitDeviceRowMoveWithNewCircuitCommandPayload {
|
||||
targetCircuitAction: CircuitDeviceRowMoveTargetCircuitAction;
|
||||
targetCircuit: CircuitSnapshot;
|
||||
moves: CircuitDeviceRowMoveAssignment[];
|
||||
}
|
||||
|
||||
export interface CircuitDeviceRowMoveWithNewCircuitProjectCommand
|
||||
extends SerializedProjectCommand<CircuitDeviceRowMoveWithNewCircuitCommandPayload> {
|
||||
schemaVersion: typeof circuitDeviceRowMoveCommandSchemaVersion;
|
||||
type: typeof circuitDeviceRowMoveWithNewCircuitCommandType;
|
||||
}
|
||||
|
||||
export type CircuitDeviceRowMoveHistoryProjectCommand =
|
||||
| CircuitDeviceRowMoveProjectCommand
|
||||
| CircuitDeviceRowMoveWithNewCircuitProjectCommand;
|
||||
|
||||
export function createCircuitDeviceRowMoveProjectCommand(
|
||||
moves: CircuitDeviceRowMoveAssignment[]
|
||||
): CircuitDeviceRowMoveProjectCommand {
|
||||
@@ -34,6 +62,24 @@ export function createCircuitDeviceRowMoveProjectCommand(
|
||||
return command;
|
||||
}
|
||||
|
||||
export function createCircuitDeviceRowMoveWithNewCircuitProjectCommand(
|
||||
targetCircuitAction: CircuitDeviceRowMoveTargetCircuitAction,
|
||||
targetCircuit: CircuitSnapshot,
|
||||
moves: CircuitDeviceRowMoveAssignment[]
|
||||
): CircuitDeviceRowMoveWithNewCircuitProjectCommand {
|
||||
const command: CircuitDeviceRowMoveWithNewCircuitProjectCommand = {
|
||||
schemaVersion: circuitDeviceRowMoveCommandSchemaVersion,
|
||||
type: circuitDeviceRowMoveWithNewCircuitCommandType,
|
||||
payload: {
|
||||
targetCircuitAction,
|
||||
targetCircuit,
|
||||
moves,
|
||||
},
|
||||
};
|
||||
assertCircuitDeviceRowMoveWithNewCircuitProjectCommand(command);
|
||||
return command;
|
||||
}
|
||||
|
||||
export function assertCircuitDeviceRowMoveProjectCommand(
|
||||
command: SerializedProjectCommand<unknown>
|
||||
): asserts command is CircuitDeviceRowMoveProjectCommand {
|
||||
@@ -91,6 +137,76 @@ export function assertCircuitDeviceRowMoveProjectCommand(
|
||||
}
|
||||
}
|
||||
|
||||
export function assertCircuitDeviceRowMoveWithNewCircuitProjectCommand(
|
||||
command: SerializedProjectCommand<unknown>
|
||||
): asserts command is CircuitDeviceRowMoveWithNewCircuitProjectCommand {
|
||||
if (
|
||||
command.schemaVersion !== circuitDeviceRowMoveCommandSchemaVersion ||
|
||||
command.type !== circuitDeviceRowMoveWithNewCircuitCommandType
|
||||
) {
|
||||
throw new Error(
|
||||
"Unsupported circuit device-row move-with-new-circuit command."
|
||||
);
|
||||
}
|
||||
if (!isPlainObject(command.payload)) {
|
||||
throw new Error(
|
||||
"Circuit device-row move-with-new-circuit payload must be an object."
|
||||
);
|
||||
}
|
||||
const { targetCircuitAction, targetCircuit, moves } =
|
||||
command.payload;
|
||||
if (
|
||||
targetCircuitAction !== "create" &&
|
||||
targetCircuitAction !== "delete"
|
||||
) {
|
||||
throw new Error(
|
||||
"Target circuit action must be create or delete."
|
||||
);
|
||||
}
|
||||
|
||||
assertCircuitInsertProjectCommand({
|
||||
schemaVersion: circuitStructureCommandSchemaVersion,
|
||||
type: circuitInsertCommandType,
|
||||
payload: { circuit: targetCircuit },
|
||||
});
|
||||
const validatedTargetCircuit = targetCircuit as CircuitSnapshot;
|
||||
if (
|
||||
validatedTargetCircuit.isReserve !== true ||
|
||||
validatedTargetCircuit.deviceRows.length !== 0
|
||||
) {
|
||||
throw new Error(
|
||||
"Move target circuit snapshot must describe an empty reserve circuit."
|
||||
);
|
||||
}
|
||||
|
||||
assertCircuitDeviceRowMoveProjectCommand({
|
||||
schemaVersion: circuitDeviceRowMoveCommandSchemaVersion,
|
||||
type: circuitDeviceRowMoveCommandType,
|
||||
payload: { moves },
|
||||
});
|
||||
const validatedMoves = moves as CircuitDeviceRowMoveAssignment[];
|
||||
for (const move of validatedMoves) {
|
||||
if (
|
||||
targetCircuitAction === "create" &&
|
||||
(move.expectedCircuitId === validatedTargetCircuit.id ||
|
||||
move.targetCircuitId !== validatedTargetCircuit.id)
|
||||
) {
|
||||
throw new Error(
|
||||
"Create-target moves must move rows into the new circuit."
|
||||
);
|
||||
}
|
||||
if (
|
||||
targetCircuitAction === "delete" &&
|
||||
(move.expectedCircuitId !== validatedTargetCircuit.id ||
|
||||
move.targetCircuitId === validatedTargetCircuit.id)
|
||||
) {
|
||||
throw new Error(
|
||||
"Delete-target moves must move rows out of the created circuit."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function assertNonEmptyString(
|
||||
value: unknown,
|
||||
field: string
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { CircuitDeviceRowMoveProjectCommand } from "../models/circuit-device-row-move-project-command.model.js";
|
||||
import type { CircuitDeviceRowMoveHistoryProjectCommand } from "../models/circuit-device-row-move-project-command.model.js";
|
||||
import type {
|
||||
AppendedProjectRevision,
|
||||
ProjectRevisionSource,
|
||||
@@ -11,12 +11,12 @@ export interface ExecuteCircuitDeviceRowMoveCommandInput {
|
||||
description?: string;
|
||||
actorId?: string;
|
||||
historyTargetChangeSetId?: string;
|
||||
command: CircuitDeviceRowMoveProjectCommand;
|
||||
command: CircuitDeviceRowMoveHistoryProjectCommand;
|
||||
}
|
||||
|
||||
export interface ExecutedCircuitDeviceRowMoveCommand {
|
||||
revision: AppendedProjectRevision;
|
||||
inverse: CircuitDeviceRowMoveProjectCommand;
|
||||
inverse: CircuitDeviceRowMoveHistoryProjectCommand;
|
||||
}
|
||||
|
||||
export interface CircuitDeviceRowMoveProjectCommandStore {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { ProjectHistoryOperationUnavailableError } from "../errors/project-history-operation-unavailable.error.js";
|
||||
import {
|
||||
assertCircuitDeviceRowMoveProjectCommand,
|
||||
assertCircuitDeviceRowMoveWithNewCircuitProjectCommand,
|
||||
circuitDeviceRowMoveCommandType,
|
||||
circuitDeviceRowMoveWithNewCircuitCommandType,
|
||||
} from "../models/circuit-device-row-move-project-command.model.js";
|
||||
import {
|
||||
assertCircuitDeviceRowUpdateProjectCommand,
|
||||
@@ -161,6 +163,15 @@ export class ProjectCommandService implements ProjectCommandExecutor {
|
||||
command: input.command,
|
||||
}).revision;
|
||||
}
|
||||
case circuitDeviceRowMoveWithNewCircuitCommandType: {
|
||||
assertCircuitDeviceRowMoveWithNewCircuitProjectCommand(
|
||||
input.command
|
||||
);
|
||||
return this.deviceRowMoveStore.execute({
|
||||
...input,
|
||||
command: input.command,
|
||||
}).revision;
|
||||
}
|
||||
case circuitInsertCommandType: {
|
||||
assertCircuitInsertProjectCommand(input.command);
|
||||
return this.circuitStructureStore.execute({
|
||||
|
||||
Reference in New Issue
Block a user