Add circuit group commands
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import {
|
||||
assertCircuitGroupDeleteProjectCommand,
|
||||
assertCircuitGroupInsertProjectCommand,
|
||||
assertCircuitGroupUpdateProjectCommand,
|
||||
circuitGroupDeleteCommandType,
|
||||
circuitGroupInsertCommandType,
|
||||
circuitGroupUpdateCommandType,
|
||||
createCircuitGroupDeleteProjectCommand,
|
||||
createCircuitGroupInsertProjectCommand,
|
||||
createCircuitGroupUpdateProjectCommand,
|
||||
type CircuitGroupSnapshot,
|
||||
type CircuitGroupStructureProjectCommand,
|
||||
} from "../../domain/models/circuit-group-structure-project-command.model.js";
|
||||
import type {
|
||||
CircuitGroupStructureProjectCommandStore,
|
||||
ExecuteCircuitGroupStructureCommandInput,
|
||||
} from "../../domain/ports/circuit-group-structure-project-command.store.js";
|
||||
import type { AppDatabase } from "../database-context.js";
|
||||
import { circuitLists } from "../schema/circuit-lists.js";
|
||||
import { circuitSections } from "../schema/circuit-sections.js";
|
||||
import { circuits } from "../schema/circuits.js";
|
||||
import { distributionBoardComponents } from "../schema/distribution-board-components.js";
|
||||
import { executeProjectCommandTransaction } from "./project-command-transaction.persistence.js";
|
||||
|
||||
export class CircuitGroupStructureProjectCommandRepository
|
||||
implements CircuitGroupStructureProjectCommandStore
|
||||
{
|
||||
constructor(private readonly database: AppDatabase) {}
|
||||
|
||||
execute(input: ExecuteCircuitGroupStructureCommandInput) {
|
||||
return executeProjectCommandTransaction(
|
||||
this.database,
|
||||
input,
|
||||
(tx) => this.applyCommand(tx, input.projectId, input.command)
|
||||
);
|
||||
}
|
||||
|
||||
private applyCommand(
|
||||
database: AppDatabase,
|
||||
projectId: string,
|
||||
command: CircuitGroupStructureProjectCommand
|
||||
): CircuitGroupStructureProjectCommand {
|
||||
if (command.type === circuitGroupInsertCommandType) {
|
||||
assertCircuitGroupInsertProjectCommand(command);
|
||||
this.insert(database, projectId, command.payload.snapshot);
|
||||
return createCircuitGroupDeleteProjectCommand(
|
||||
command.payload.snapshot
|
||||
);
|
||||
}
|
||||
if (command.type === circuitGroupDeleteCommandType) {
|
||||
assertCircuitGroupDeleteProjectCommand(command);
|
||||
this.delete(database, projectId, command.payload.snapshot);
|
||||
return createCircuitGroupInsertProjectCommand(
|
||||
command.payload.snapshot
|
||||
);
|
||||
}
|
||||
if (command.type === circuitGroupUpdateCommandType) {
|
||||
assertCircuitGroupUpdateProjectCommand(command);
|
||||
this.update(
|
||||
database,
|
||||
projectId,
|
||||
command.payload.expected,
|
||||
command.payload.target
|
||||
);
|
||||
return createCircuitGroupUpdateProjectCommand(
|
||||
command.payload.target,
|
||||
command.payload.expected
|
||||
);
|
||||
}
|
||||
throw new Error("Unsupported circuit-group structure command.");
|
||||
}
|
||||
|
||||
private insert(
|
||||
database: AppDatabase,
|
||||
projectId: string,
|
||||
snapshot: CircuitGroupSnapshot
|
||||
) {
|
||||
this.assertListOwnership(database, projectId, snapshot.circuitListId);
|
||||
const existing = database
|
||||
.select({ id: circuitSections.id })
|
||||
.from(circuitSections)
|
||||
.where(eq(circuitSections.id, snapshot.id))
|
||||
.get();
|
||||
if (existing) {
|
||||
throw new Error("Circuit-group id already exists.");
|
||||
}
|
||||
database.insert(circuitSections).values(snapshot).run();
|
||||
}
|
||||
|
||||
private delete(
|
||||
database: AppDatabase,
|
||||
projectId: string,
|
||||
snapshot: CircuitGroupSnapshot
|
||||
) {
|
||||
this.assertListOwnership(database, projectId, snapshot.circuitListId);
|
||||
const current = this.getCurrent(database, snapshot.id);
|
||||
if (!current || !sameRecord(snapshot, current)) {
|
||||
throw new Error("Circuit group changed before deletion.");
|
||||
}
|
||||
const circuit = database
|
||||
.select({ id: circuits.id })
|
||||
.from(circuits)
|
||||
.where(eq(circuits.sectionId, snapshot.id))
|
||||
.get();
|
||||
const component = database
|
||||
.select({ id: distributionBoardComponents.id })
|
||||
.from(distributionBoardComponents)
|
||||
.where(eq(distributionBoardComponents.sectionId, snapshot.id))
|
||||
.get();
|
||||
if (circuit || component) {
|
||||
throw new Error(
|
||||
"Only empty circuit groups can be deleted by this command."
|
||||
);
|
||||
}
|
||||
const deleted = database
|
||||
.delete(circuitSections)
|
||||
.where(
|
||||
and(
|
||||
eq(circuitSections.id, snapshot.id),
|
||||
eq(circuitSections.circuitListId, snapshot.circuitListId)
|
||||
)
|
||||
)
|
||||
.run();
|
||||
if (deleted.changes !== 1) {
|
||||
throw new Error("Circuit group could not be deleted.");
|
||||
}
|
||||
}
|
||||
|
||||
private update(
|
||||
database: AppDatabase,
|
||||
projectId: string,
|
||||
expected: CircuitGroupSnapshot,
|
||||
target: CircuitGroupSnapshot
|
||||
) {
|
||||
this.assertListOwnership(database, projectId, expected.circuitListId);
|
||||
const current = this.getCurrent(database, expected.id);
|
||||
if (!current || !sameRecord(expected, current)) {
|
||||
throw new Error("Circuit group changed before update.");
|
||||
}
|
||||
const updated = database
|
||||
.update(circuitSections)
|
||||
.set({ displayName: target.displayName })
|
||||
.where(
|
||||
and(
|
||||
eq(circuitSections.id, expected.id),
|
||||
eq(circuitSections.circuitListId, expected.circuitListId)
|
||||
)
|
||||
)
|
||||
.run();
|
||||
if (updated.changes !== 1) {
|
||||
throw new Error("Circuit group could not be updated.");
|
||||
}
|
||||
}
|
||||
|
||||
private assertListOwnership(
|
||||
database: AppDatabase,
|
||||
projectId: string,
|
||||
circuitListId: string
|
||||
) {
|
||||
const list = database
|
||||
.select({ projectId: circuitLists.projectId })
|
||||
.from(circuitLists)
|
||||
.where(eq(circuitLists.id, circuitListId))
|
||||
.get();
|
||||
if (!list || list.projectId !== projectId) {
|
||||
throw new Error("Circuit group does not belong to project.");
|
||||
}
|
||||
}
|
||||
|
||||
private getCurrent(database: AppDatabase, id: string) {
|
||||
return database
|
||||
.select()
|
||||
.from(circuitSections)
|
||||
.where(eq(circuitSections.id, id))
|
||||
.get();
|
||||
}
|
||||
}
|
||||
|
||||
function sameRecord(expected: object, actual: object) {
|
||||
const expectedEntries = Object.entries(expected);
|
||||
const actualRecord = actual as Record<string, unknown>;
|
||||
return (
|
||||
expectedEntries.length === Object.keys(actual).length &&
|
||||
expectedEntries.every(
|
||||
([key, value]) => actualRecord[key] === value
|
||||
)
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user