98 lines
3.1 KiB
TypeScript
98 lines
3.1 KiB
TypeScript
import { and, eq } from "drizzle-orm";
|
|
import {
|
|
assertCircuitProtectionUpdateProjectCommand,
|
|
createCircuitProtectionUpdateProjectCommand,
|
|
type CircuitProtectionSnapshot,
|
|
} from "../../domain/models/circuit-protection-project-command.model.js";
|
|
import type {
|
|
CircuitProtectionProjectCommandStore,
|
|
ExecuteCircuitProtectionCommandInput,
|
|
} from "../../domain/ports/circuit-protection-project-command.store.js";
|
|
import type { AppDatabase } from "../database-context.js";
|
|
import { circuitLists } from "../schema/circuit-lists.js";
|
|
import { circuitProtectionDevices } from "../schema/circuit-protection-devices.js";
|
|
import { circuits } from "../schema/circuits.js";
|
|
import { executeProjectCommandTransaction } from "./project-command-transaction.persistence.js";
|
|
|
|
export class CircuitProtectionProjectCommandRepository
|
|
implements CircuitProtectionProjectCommandStore
|
|
{
|
|
constructor(private readonly database: AppDatabase) {}
|
|
|
|
execute(input: ExecuteCircuitProtectionCommandInput) {
|
|
return executeProjectCommandTransaction(this.database, input, (tx) => {
|
|
assertCircuitProtectionUpdateProjectCommand(input.command);
|
|
const { circuitId, expected, target } = input.command.payload;
|
|
if (input.source === "user" && target === null) {
|
|
throw new Error("User commands cannot remove circuit protection.");
|
|
}
|
|
this.assertOwnership(tx, input.projectId, circuitId);
|
|
const actual = tx
|
|
.select()
|
|
.from(circuitProtectionDevices)
|
|
.where(eq(circuitProtectionDevices.circuitId, circuitId))
|
|
.get();
|
|
if (!sameNullableSnapshot(expected, actual)) {
|
|
throw new Error("Circuit protection changed before update.");
|
|
}
|
|
if (target === null) {
|
|
tx.delete(circuitProtectionDevices)
|
|
.where(eq(circuitProtectionDevices.circuitId, circuitId))
|
|
.run();
|
|
} else if (actual) {
|
|
tx.update(circuitProtectionDevices)
|
|
.set(target)
|
|
.where(eq(circuitProtectionDevices.circuitId, circuitId))
|
|
.run();
|
|
} else {
|
|
tx.insert(circuitProtectionDevices).values(target).run();
|
|
}
|
|
return createCircuitProtectionUpdateProjectCommand(
|
|
circuitId,
|
|
target,
|
|
expected
|
|
);
|
|
});
|
|
}
|
|
|
|
private assertOwnership(
|
|
database: AppDatabase,
|
|
projectId: string,
|
|
circuitId: string
|
|
) {
|
|
const circuit = database
|
|
.select({ projectId: circuitLists.projectId })
|
|
.from(circuits)
|
|
.innerJoin(
|
|
circuitLists,
|
|
eq(circuitLists.id, circuits.circuitListId)
|
|
)
|
|
.where(
|
|
and(
|
|
eq(circuits.id, circuitId),
|
|
eq(circuitLists.projectId, projectId)
|
|
)
|
|
)
|
|
.get();
|
|
if (!circuit) {
|
|
throw new Error("Circuit protection does not belong to project.");
|
|
}
|
|
}
|
|
}
|
|
|
|
function sameNullableSnapshot(
|
|
expected: CircuitProtectionSnapshot | null,
|
|
actual: typeof circuitProtectionDevices.$inferSelect | undefined
|
|
) {
|
|
if (expected === null) {
|
|
return actual === undefined;
|
|
}
|
|
if (!actual) {
|
|
return false;
|
|
}
|
|
return Object.entries(expected).every(
|
|
([key, value]) =>
|
|
(actual as Record<string, unknown>)[key] === value
|
|
);
|
|
}
|