Create protected board defaults
This commit is contained in:
@@ -2,14 +2,12 @@ import { and, asc, eq, inArray } from "drizzle-orm";
|
||||
import {
|
||||
assertDistributionBoardDeleteProjectCommand,
|
||||
assertDistributionBoardInsertProjectCommand,
|
||||
createDistributionBoardDeleteProjectCommand,
|
||||
createDistributionBoardInsertProjectCommand,
|
||||
distributionBoardDeleteCommandType,
|
||||
distributionBoardInsertCommandType,
|
||||
invertDistributionBoardStructureProjectCommand,
|
||||
normalizeDistributionBoardStructureProjectCommand,
|
||||
type DistributionBoardStructureProjectCommand,
|
||||
type DistributionBoardStructureSnapshot,
|
||||
legacyDistributionBoardStructureCommandSchemaVersion,
|
||||
type NormalizedDistributionBoardStructureSnapshot,
|
||||
} from "../../domain/models/distribution-board-structure-project-command.model.js";
|
||||
import {
|
||||
assertDistributionBoardUpdateProjectCommand,
|
||||
@@ -24,11 +22,11 @@ import type {
|
||||
DistributionBoardStructureProjectCommandStore,
|
||||
ExecuteDistributionBoardStructureCommandInput,
|
||||
} from "../../domain/ports/distribution-board-structure-project-command.store.js";
|
||||
import type { CircuitGroupCategory } from "../../shared/constants/circuit-group.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 { distributionBoards } from "../schema/distribution-boards.js";
|
||||
import { floors } from "../schema/floors.js";
|
||||
import { projects } from "../schema/projects.js";
|
||||
@@ -69,31 +67,15 @@ export class DistributionBoardStructureProjectCommandRepository
|
||||
projectId: string,
|
||||
command: DistributionBoardStructureProjectCommand
|
||||
): DistributionBoardStructureProjectCommand {
|
||||
const normalized =
|
||||
const structure =
|
||||
normalizeDistributionBoardStructureProjectCommand(command);
|
||||
if (normalized.type === distributionBoardInsertCommandType) {
|
||||
assertDistributionBoardInsertProjectCommand(normalized);
|
||||
const inverse = this.insert(
|
||||
database,
|
||||
projectId,
|
||||
normalized.payload.structure
|
||||
);
|
||||
return command.schemaVersion ===
|
||||
legacyDistributionBoardStructureCommandSchemaVersion
|
||||
? toLegacyStructureCommand(inverse)
|
||||
: inverse;
|
||||
if (command.type === distributionBoardInsertCommandType) {
|
||||
this.insert(database, projectId, structure);
|
||||
return invertDistributionBoardStructureProjectCommand(command);
|
||||
}
|
||||
if (normalized.type === distributionBoardDeleteCommandType) {
|
||||
assertDistributionBoardDeleteProjectCommand(normalized);
|
||||
const inverse = this.delete(
|
||||
database,
|
||||
projectId,
|
||||
normalized.payload.structure
|
||||
);
|
||||
return command.schemaVersion ===
|
||||
legacyDistributionBoardStructureCommandSchemaVersion
|
||||
? toLegacyStructureCommand(inverse)
|
||||
: inverse;
|
||||
if (command.type === distributionBoardDeleteCommandType) {
|
||||
this.delete(database, projectId, structure);
|
||||
return invertDistributionBoardStructureProjectCommand(command);
|
||||
}
|
||||
throw new Error("Unsupported distribution-board structure command.");
|
||||
}
|
||||
@@ -101,7 +83,7 @@ export class DistributionBoardStructureProjectCommandRepository
|
||||
private insert(
|
||||
database: AppDatabase,
|
||||
projectId: string,
|
||||
structure: DistributionBoardStructureSnapshot
|
||||
structure: NormalizedDistributionBoardStructureSnapshot
|
||||
) {
|
||||
if (
|
||||
structure.distributionBoard.projectId !== projectId ||
|
||||
@@ -160,7 +142,26 @@ export class DistributionBoardStructureProjectCommandRepository
|
||||
)
|
||||
.limit(1)
|
||||
.get();
|
||||
if (existingBoard || existingList || existingSection) {
|
||||
const existingComponent =
|
||||
structure.components.length === 0
|
||||
? undefined
|
||||
: database
|
||||
.select({ id: distributionBoardComponents.id })
|
||||
.from(distributionBoardComponents)
|
||||
.where(
|
||||
inArray(
|
||||
distributionBoardComponents.id,
|
||||
structure.components.map((component) => component.id)
|
||||
)
|
||||
)
|
||||
.limit(1)
|
||||
.get();
|
||||
if (
|
||||
existingBoard ||
|
||||
existingList ||
|
||||
existingSection ||
|
||||
existingComponent
|
||||
) {
|
||||
throw new Error("Distribution-board structure id already exists.");
|
||||
}
|
||||
|
||||
@@ -171,15 +172,20 @@ export class DistributionBoardStructureProjectCommandRepository
|
||||
database.insert(circuitLists).values(structure.circuitList).run();
|
||||
database
|
||||
.insert(circuitSections)
|
||||
.values(structure.sections.map(withInitialGroupIdentity))
|
||||
.values(structure.sections)
|
||||
.run();
|
||||
return createDistributionBoardDeleteProjectCommand(structure);
|
||||
if (structure.components.length > 0) {
|
||||
database
|
||||
.insert(distributionBoardComponents)
|
||||
.values(structure.components)
|
||||
.run();
|
||||
}
|
||||
}
|
||||
|
||||
private delete(
|
||||
database: AppDatabase,
|
||||
projectId: string,
|
||||
structure: DistributionBoardStructureSnapshot
|
||||
structure: NormalizedDistributionBoardStructureSnapshot
|
||||
) {
|
||||
const board = database
|
||||
.select()
|
||||
@@ -202,11 +208,30 @@ export class DistributionBoardStructureProjectCommandRepository
|
||||
asc(circuitSections.id)
|
||||
)
|
||||
.all();
|
||||
const components = database
|
||||
.select()
|
||||
.from(distributionBoardComponents)
|
||||
.where(
|
||||
eq(
|
||||
distributionBoardComponents.circuitListId,
|
||||
structure.circuitList.id
|
||||
)
|
||||
)
|
||||
.orderBy(
|
||||
asc(distributionBoardComponents.sortOrder),
|
||||
asc(distributionBoardComponents.id)
|
||||
)
|
||||
.all();
|
||||
if (
|
||||
!board ||
|
||||
!list ||
|
||||
board.projectId !== projectId ||
|
||||
!structureMatches(structure, { board, list, sections })
|
||||
!structureMatches(structure, {
|
||||
board,
|
||||
list,
|
||||
sections,
|
||||
components,
|
||||
})
|
||||
) {
|
||||
throw new Error(
|
||||
"Distribution-board structure changed before deletion."
|
||||
@@ -239,7 +264,6 @@ export class DistributionBoardStructureProjectCommandRepository
|
||||
if (deleted.changes !== 1) {
|
||||
throw new Error("Distribution board could not be deleted.");
|
||||
}
|
||||
return createDistributionBoardInsertProjectCommand(structure);
|
||||
}
|
||||
|
||||
private update(
|
||||
@@ -354,28 +378,6 @@ function assertSupplyTypeEnabled(
|
||||
}
|
||||
}
|
||||
|
||||
function toLegacyStructureCommand(
|
||||
command:
|
||||
| ReturnType<typeof createDistributionBoardInsertProjectCommand>
|
||||
| ReturnType<typeof createDistributionBoardDeleteProjectCommand>
|
||||
): DistributionBoardStructureProjectCommand {
|
||||
const {
|
||||
floorId: _floorId,
|
||||
supplyType: _supplyType,
|
||||
...distributionBoard
|
||||
} = command.payload.structure.distributionBoard;
|
||||
return {
|
||||
schemaVersion: legacyDistributionBoardStructureCommandSchemaVersion,
|
||||
type: command.type,
|
||||
payload: {
|
||||
structure: {
|
||||
...command.payload.structure,
|
||||
distributionBoard,
|
||||
},
|
||||
},
|
||||
} as DistributionBoardStructureProjectCommand;
|
||||
}
|
||||
|
||||
function getDistributionBoardFieldValue<
|
||||
TField extends DistributionBoardUpdateField,
|
||||
>(
|
||||
@@ -386,11 +388,12 @@ function getDistributionBoardFieldValue<
|
||||
}
|
||||
|
||||
function structureMatches(
|
||||
expected: DistributionBoardStructureSnapshot,
|
||||
expected: NormalizedDistributionBoardStructureSnapshot,
|
||||
actual: {
|
||||
board: typeof distributionBoards.$inferSelect;
|
||||
list: typeof circuitLists.$inferSelect;
|
||||
sections: Array<typeof circuitSections.$inferSelect>;
|
||||
components: Array<typeof distributionBoardComponents.$inferSelect>;
|
||||
}
|
||||
) {
|
||||
const {
|
||||
@@ -404,7 +407,8 @@ function structureMatches(
|
||||
actualDistributionBoard
|
||||
) ||
|
||||
!sameRecord(expected.circuitList, actual.list) ||
|
||||
expected.sections.length !== actual.sections.length
|
||||
expected.sections.length !== actual.sections.length ||
|
||||
expected.components.length !== actual.components.length
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@@ -413,59 +417,32 @@ function structureMatches(
|
||||
left.sortOrder - right.sortOrder ||
|
||||
left.id.localeCompare(right.id)
|
||||
);
|
||||
return expectedSections.every((section, index) => {
|
||||
const actualSection = actual.sections[index];
|
||||
if (
|
||||
actualSection.category !== initialCategoryBySectionKey(section.key) ||
|
||||
actualSection.groupNumber !== initialGroupNumberBySectionKey(section.key)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
const {
|
||||
category: _category,
|
||||
groupNumber: _groupNumber,
|
||||
...comparableActualSection
|
||||
} = actualSection;
|
||||
return sameRecord(section, comparableActualSection);
|
||||
});
|
||||
}
|
||||
|
||||
function withInitialGroupIdentity(
|
||||
section: DistributionBoardStructureSnapshot["sections"][number]
|
||||
): typeof circuitSections.$inferInsert {
|
||||
return {
|
||||
...section,
|
||||
category: initialCategoryBySectionKey(section.key),
|
||||
groupNumber: initialGroupNumberBySectionKey(section.key),
|
||||
};
|
||||
}
|
||||
|
||||
function initialCategoryBySectionKey(
|
||||
key: string
|
||||
): CircuitGroupCategory | null {
|
||||
if (
|
||||
key === "lighting" ||
|
||||
key === "single_phase" ||
|
||||
key === "three_phase"
|
||||
) {
|
||||
return key;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function initialGroupNumberBySectionKey(key: string): number | null {
|
||||
return initialCategoryBySectionKey(key) === null ? null : 1;
|
||||
}
|
||||
|
||||
function sameRecord(
|
||||
expected: Record<string, unknown>,
|
||||
actual: Record<string, unknown>
|
||||
) {
|
||||
const expectedEntries = Object.entries(expected);
|
||||
const sectionsMatch = expectedSections.every((section, index) =>
|
||||
sameRecord(section, actual.sections[index])
|
||||
);
|
||||
const expectedComponents = [...expected.components].sort(
|
||||
(left, right) =>
|
||||
left.sortOrder - right.sortOrder ||
|
||||
left.id.localeCompare(right.id)
|
||||
);
|
||||
return (
|
||||
expectedEntries.length === Object.keys(actual).length &&
|
||||
expectedEntries.every(
|
||||
([key, value]) => actual[key] === value
|
||||
sectionsMatch &&
|
||||
expectedComponents.every((component, index) =>
|
||||
sameRecord(component, actual.components[index])
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
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