Persist circuit protection updates
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
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
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user