Add component structure commands
This commit is contained in:
+193
@@ -0,0 +1,193 @@
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import {
|
||||
assertDistributionBoardComponentDeleteProjectCommand,
|
||||
assertDistributionBoardComponentInsertProjectCommand,
|
||||
createDistributionBoardComponentDeleteProjectCommand,
|
||||
createDistributionBoardComponentInsertProjectCommand,
|
||||
distributionBoardComponentDeleteCommandType,
|
||||
distributionBoardComponentInsertCommandType,
|
||||
type DistributionBoardComponentSnapshot,
|
||||
type DistributionBoardComponentStructureProjectCommand,
|
||||
} from "../../domain/models/distribution-board-component-structure-project-command.model.js";
|
||||
import type {
|
||||
DistributionBoardComponentStructureProjectCommandStore,
|
||||
ExecuteDistributionBoardComponentStructureCommandInput,
|
||||
} from "../../domain/ports/distribution-board-component-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 { distributionBoardComponentProtectionDevices } from "../schema/distribution-board-component-protection-devices.js";
|
||||
import { distributionBoardComponents } from "../schema/distribution-board-components.js";
|
||||
import { executeProjectCommandTransaction } from "./project-command-transaction.persistence.js";
|
||||
|
||||
export class DistributionBoardComponentStructureProjectCommandRepository
|
||||
implements DistributionBoardComponentStructureProjectCommandStore
|
||||
{
|
||||
constructor(private readonly database: AppDatabase) {}
|
||||
|
||||
execute(
|
||||
input: ExecuteDistributionBoardComponentStructureCommandInput
|
||||
) {
|
||||
return executeProjectCommandTransaction(
|
||||
this.database,
|
||||
input,
|
||||
(tx) =>
|
||||
this.applyCommand(
|
||||
tx,
|
||||
input.projectId,
|
||||
input.command
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private applyCommand(
|
||||
database: AppDatabase,
|
||||
projectId: string,
|
||||
command: DistributionBoardComponentStructureProjectCommand
|
||||
): DistributionBoardComponentStructureProjectCommand {
|
||||
if (command.type === distributionBoardComponentInsertCommandType) {
|
||||
assertDistributionBoardComponentInsertProjectCommand(command);
|
||||
this.insert(database, projectId, command.payload.snapshot);
|
||||
return createDistributionBoardComponentDeleteProjectCommand(
|
||||
command.payload.snapshot
|
||||
);
|
||||
}
|
||||
if (command.type === distributionBoardComponentDeleteCommandType) {
|
||||
assertDistributionBoardComponentDeleteProjectCommand(command);
|
||||
this.delete(database, projectId, command.payload.snapshot);
|
||||
return createDistributionBoardComponentInsertProjectCommand(
|
||||
command.payload.snapshot
|
||||
);
|
||||
}
|
||||
throw new Error(
|
||||
"Unsupported distribution-board component structure command."
|
||||
);
|
||||
}
|
||||
|
||||
private insert(
|
||||
database: AppDatabase,
|
||||
projectId: string,
|
||||
snapshot: DistributionBoardComponentSnapshot
|
||||
) {
|
||||
this.assertOwnership(database, projectId, snapshot);
|
||||
const existing = database
|
||||
.select({ id: distributionBoardComponents.id })
|
||||
.from(distributionBoardComponents)
|
||||
.where(eq(distributionBoardComponents.id, snapshot.component.id))
|
||||
.get();
|
||||
if (existing) {
|
||||
throw new Error("Distribution-board component id already exists.");
|
||||
}
|
||||
database
|
||||
.insert(distributionBoardComponents)
|
||||
.values(snapshot.component)
|
||||
.run();
|
||||
if (snapshot.protectionDevice !== null) {
|
||||
database
|
||||
.insert(distributionBoardComponentProtectionDevices)
|
||||
.values(snapshot.protectionDevice)
|
||||
.run();
|
||||
}
|
||||
}
|
||||
|
||||
private delete(
|
||||
database: AppDatabase,
|
||||
projectId: string,
|
||||
snapshot: DistributionBoardComponentSnapshot
|
||||
) {
|
||||
this.assertOwnership(database, projectId, snapshot);
|
||||
const component = database
|
||||
.select()
|
||||
.from(distributionBoardComponents)
|
||||
.where(eq(distributionBoardComponents.id, snapshot.component.id))
|
||||
.get();
|
||||
const protection = database
|
||||
.select()
|
||||
.from(distributionBoardComponentProtectionDevices)
|
||||
.where(
|
||||
eq(
|
||||
distributionBoardComponentProtectionDevices.componentId,
|
||||
snapshot.component.id
|
||||
)
|
||||
)
|
||||
.get();
|
||||
if (
|
||||
!component ||
|
||||
!sameRecord(snapshot.component, component) ||
|
||||
!sameNullableRecord(snapshot.protectionDevice, protection)
|
||||
) {
|
||||
throw new Error(
|
||||
"Distribution-board component changed before deletion."
|
||||
);
|
||||
}
|
||||
const deleted = database
|
||||
.delete(distributionBoardComponents)
|
||||
.where(
|
||||
and(
|
||||
eq(distributionBoardComponents.id, component.id),
|
||||
eq(
|
||||
distributionBoardComponents.circuitListId,
|
||||
snapshot.component.circuitListId
|
||||
)
|
||||
)
|
||||
)
|
||||
.run();
|
||||
if (deleted.changes !== 1) {
|
||||
throw new Error(
|
||||
"Distribution-board component could not be deleted."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private assertOwnership(
|
||||
database: AppDatabase,
|
||||
projectId: string,
|
||||
snapshot: DistributionBoardComponentSnapshot
|
||||
) {
|
||||
const list = database
|
||||
.select({ projectId: circuitLists.projectId })
|
||||
.from(circuitLists)
|
||||
.where(eq(circuitLists.id, snapshot.component.circuitListId))
|
||||
.get();
|
||||
if (!list || list.projectId !== projectId) {
|
||||
throw new Error(
|
||||
"Distribution-board component circuit list does not belong to project."
|
||||
);
|
||||
}
|
||||
if (snapshot.component.sectionId !== null) {
|
||||
const section = database
|
||||
.select({ circuitListId: circuitSections.circuitListId })
|
||||
.from(circuitSections)
|
||||
.where(eq(circuitSections.id, snapshot.component.sectionId))
|
||||
.get();
|
||||
if (
|
||||
!section ||
|
||||
section.circuitListId !== snapshot.component.circuitListId
|
||||
) {
|
||||
throw new Error(
|
||||
"Distribution-board component section does not belong to circuit list."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function sameNullableRecord(
|
||||
expected: object | null,
|
||||
actual: object | undefined
|
||||
) {
|
||||
return expected === null
|
||||
? actual === undefined
|
||||
: actual !== undefined && sameRecord(expected, actual);
|
||||
}
|
||||
|
||||
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