203 lines
6.0 KiB
TypeScript
203 lines
6.0 KiB
TypeScript
import { and, eq } from "drizzle-orm";
|
|
import {
|
|
assertCircuitSectionReorderProjectCommand,
|
|
circuitSectionReorderCommandType,
|
|
createCircuitSectionReorderProjectCommand,
|
|
type CircuitSectionReorderAssignment,
|
|
type CircuitSectionReorderProjectCommand,
|
|
} from "../../domain/models/circuit-section-reorder-project-command.model.js";
|
|
import {
|
|
assertCircuitSectionsReorderProjectCommand,
|
|
circuitSectionsReorderCommandType,
|
|
createCircuitSectionsReorderProjectCommand,
|
|
type CircuitSectionsReorderProjectCommand,
|
|
type CircuitSectionsReorderSection,
|
|
} from "../../domain/models/circuit-sections-reorder-project-command.model.js";
|
|
import type {
|
|
CircuitSectionReorderProjectCommandStore,
|
|
ExecuteCircuitSectionReorderCommandInput,
|
|
} from "../../domain/ports/circuit-section-reorder-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 { executeProjectCommandTransaction } from "./project-command-transaction.persistence.js";
|
|
|
|
type ReorderHistoryCommand =
|
|
| CircuitSectionReorderProjectCommand
|
|
| CircuitSectionsReorderProjectCommand;
|
|
|
|
export class CircuitSectionReorderProjectCommandRepository
|
|
implements CircuitSectionReorderProjectCommandStore
|
|
{
|
|
constructor(private readonly database: AppDatabase) {}
|
|
|
|
execute(input: ExecuteCircuitSectionReorderCommandInput) {
|
|
this.assertSupportedCommand(input.command);
|
|
|
|
return executeProjectCommandTransaction(
|
|
this.database,
|
|
input,
|
|
(tx) => this.applyCommand(tx, input)
|
|
);
|
|
}
|
|
|
|
private applyCommand(
|
|
tx: AppDatabase,
|
|
input: ExecuteCircuitSectionReorderCommandInput
|
|
) {
|
|
const commandSections = this.getCommandSections(input.command);
|
|
const inverseSections = commandSections.map((section) => {
|
|
this.assertCompleteCurrentSection(
|
|
tx,
|
|
input.projectId,
|
|
section
|
|
);
|
|
return {
|
|
sectionId: section.sectionId,
|
|
assignments: section.assignments.map((assignment) => ({
|
|
circuitId: assignment.circuitId,
|
|
expectedSortOrder: assignment.targetSortOrder,
|
|
targetSortOrder: assignment.expectedSortOrder,
|
|
})),
|
|
};
|
|
});
|
|
|
|
for (const section of commandSections) {
|
|
this.applyAssignments(tx, section);
|
|
}
|
|
|
|
const inverse =
|
|
input.command.type === circuitSectionReorderCommandType
|
|
? createCircuitSectionReorderProjectCommand(
|
|
inverseSections[0].sectionId,
|
|
inverseSections[0].assignments
|
|
)
|
|
: createCircuitSectionsReorderProjectCommand(
|
|
inverseSections
|
|
);
|
|
return inverse;
|
|
}
|
|
|
|
private assertSupportedCommand(
|
|
command: ReorderHistoryCommand
|
|
) {
|
|
if (command.type === circuitSectionReorderCommandType) {
|
|
assertCircuitSectionReorderProjectCommand(command);
|
|
return;
|
|
}
|
|
if (command.type === circuitSectionsReorderCommandType) {
|
|
assertCircuitSectionsReorderProjectCommand(command);
|
|
return;
|
|
}
|
|
throw new Error("Unsupported circuit section reorder command.");
|
|
}
|
|
|
|
private getCommandSections(
|
|
command: ReorderHistoryCommand
|
|
): CircuitSectionsReorderSection[] {
|
|
if (command.type === circuitSectionReorderCommandType) {
|
|
return [
|
|
{
|
|
sectionId: command.payload.sectionId,
|
|
assignments: command.payload.assignments,
|
|
},
|
|
];
|
|
}
|
|
return command.payload.sections;
|
|
}
|
|
|
|
private assertCompleteCurrentSection(
|
|
database: AppDatabase,
|
|
projectId: string,
|
|
sectionCommand: CircuitSectionsReorderSection
|
|
) {
|
|
const section = database
|
|
.select({
|
|
id: circuitSections.id,
|
|
circuitListId: circuitSections.circuitListId,
|
|
projectId: circuitLists.projectId,
|
|
})
|
|
.from(circuitSections)
|
|
.innerJoin(
|
|
circuitLists,
|
|
eq(circuitLists.id, circuitSections.circuitListId)
|
|
)
|
|
.where(eq(circuitSections.id, sectionCommand.sectionId))
|
|
.get();
|
|
if (!section || section.projectId !== projectId) {
|
|
throw new Error(
|
|
"Circuit section does not belong to project."
|
|
);
|
|
}
|
|
|
|
const persistedCircuits = database
|
|
.select({
|
|
id: circuits.id,
|
|
circuitListId: circuits.circuitListId,
|
|
sortOrder: circuits.sortOrder,
|
|
})
|
|
.from(circuits)
|
|
.where(eq(circuits.sectionId, section.id))
|
|
.all();
|
|
const assignments = sectionCommand.assignments;
|
|
if (
|
|
persistedCircuits.length !== assignments.length ||
|
|
persistedCircuits.some(
|
|
(circuit) =>
|
|
circuit.circuitListId !== section.circuitListId
|
|
)
|
|
) {
|
|
throw new Error(
|
|
"Circuit reorder must include every circuit in the section."
|
|
);
|
|
}
|
|
const circuitsById = new Map(
|
|
persistedCircuits.map((circuit) => [circuit.id, circuit])
|
|
);
|
|
for (const assignment of assignments) {
|
|
const circuit = circuitsById.get(assignment.circuitId);
|
|
if (
|
|
!circuit ||
|
|
circuit.sortOrder !== assignment.expectedSortOrder
|
|
) {
|
|
throw new Error(
|
|
"Circuit changed before section reorder execution."
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
private applyAssignments(
|
|
database: AppDatabase,
|
|
sectionCommand: {
|
|
sectionId: string;
|
|
assignments: CircuitSectionReorderAssignment[];
|
|
}
|
|
) {
|
|
for (const assignment of sectionCommand.assignments) {
|
|
if (
|
|
assignment.expectedSortOrder === assignment.targetSortOrder
|
|
) {
|
|
continue;
|
|
}
|
|
const updated = database
|
|
.update(circuits)
|
|
.set({ sortOrder: assignment.targetSortOrder })
|
|
.where(
|
|
and(
|
|
eq(circuits.id, assignment.circuitId),
|
|
eq(circuits.sectionId, sectionCommand.sectionId),
|
|
eq(circuits.sortOrder, assignment.expectedSortOrder)
|
|
)
|
|
)
|
|
.run();
|
|
if (updated.changes !== 1) {
|
|
throw new Error(
|
|
"Circuit changed during section reorder execution."
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|