import type { Sequelize } from "sequelize"; import { DataTypes, QueryTypes } from "sequelize"; const fk = (model: string, nullable = false) => ({ type: DataTypes.UUID, allowNull: nullable, references: { model, key: "id" }, onDelete: ["resource_definitions", "access_items"].includes(model) ? "RESTRICT" : "CASCADE", }); const title = { type: DataTypes.STRING(100), allowNull: false }; const position = { type: DataTypes.INTEGER, allowNull: false }; export const definitionColumns = { kind: { type: DataTypes.STRING(12), allowNull: false }, name: title, description: { type: DataTypes.TEXT, allowNull: false }, fieldType: { type: DataTypes.STRING(12), allowNull: true }, unit: { type: DataTypes.STRING(30), allowNull: true }, usage: { type: DataTypes.STRING(15), allowNull: true }, sectionsDefined: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: false }, }; export const inventoryColumns = { name: title, active: { type: DataTypes.BOOLEAN, allowNull: false }, notes: { type: DataTypes.TEXT, allowNull: false }, integrationKind: { type: DataTypes.STRING(10), allowNull: true }, addressFieldId: fk("resource_definitions", true), contextFieldId: fk("resource_definitions", true), mailboxKind: { type: DataTypes.STRING(10), allowNull: true }, accountId: fk("access_items", true), referenceFieldId: fk("resource_definitions", true), resourceTypeId: fk("access_items", true), systemId: fk("access_items", true), environmentId: fk("access_items", true), }; export const specificationColumns = { templateName: title, sectionsDefined: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: false }, }; type Columns = Parameters["createTable"]>[1]; async function create(database: Sequelize, name: string, columns: Columns) { const tables = await database.query<{ name: string }>( "SELECT TABLE_NAME AS name FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=:name", { replacements: { name }, type: QueryTypes.SELECT } ); if (!tables.length) { await database.getQueryInterface().createTable(name, columns); } } export async function initializeResourceCatalogSchema(database: Sequelize) { await create(database, "resource_field_choices", { fieldId: { ...fk("resource_definitions"), primaryKey: true }, position: { ...position, primaryKey: true }, value: title, }); await create(database, "resource_template_sections", { id: { type: DataTypes.UUID, primaryKey: true }, templateId: { ...fk("resource_definitions"), primaryKey: true }, name: title, description: { type: DataTypes.TEXT, allowNull: false }, position, }); await constrainSections(database); } export async function initializeResourceInventorySchema(database: Sequelize) { await create(database, "resource_specification_sections", { resourceId: { ...fk("resources"), primaryKey: true }, id: { type: DataTypes.UUID, primaryKey: true }, name: title, description: { type: DataTypes.TEXT, allowNull: false }, position, }); await create(database, "resource_specification_fields", { resourceId: { ...fk("resources"), primaryKey: true }, fieldId: { ...fk("resource_definitions"), primaryKey: true }, name: title, fieldType: { type: DataTypes.STRING(12), allowNull: false }, unit: { type: DataTypes.STRING(30), allowNull: false }, required: { type: DataTypes.BOOLEAN, allowNull: false }, sectionId: { type: DataTypes.UUID, allowNull: true }, position, }); await create(database, "resource_specification_field_choices", { resourceId: { ...fk("resources"), primaryKey: true }, fieldId: { ...fk("resource_definitions"), primaryKey: true }, position: { ...position, primaryKey: true }, value: title, }); const typedValues = { fieldId: { ...fk("resource_definitions"), primaryKey: true }, valueType: { type: DataTypes.STRING(12), allowNull: false }, textValue: { type: DataTypes.TEXT, allowNull: true }, numberValue: { type: DataTypes.DOUBLE, allowNull: true }, booleanValue: { type: DataTypes.BOOLEAN, allowNull: true }, dateValue: { type: DataTypes.DATEONLY, allowNull: true }, }; await create(database, "resource_characteristics", { resourceId: { ...fk("resources"), primaryKey: true }, ...typedValues, }); await create(database, "resource_revision_characteristics", { revisionId: { ...fk("resource_revisions"), primaryKey: true }, resourceId: fk("resources"), ...typedValues, }); } async function constrainSections(database: Sequelize) { const constraints = await database.query<{ name: string }>( "SELECT CONSTRAINT_NAME AS name FROM information_schema.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='resource_template_fields' AND CONSTRAINT_NAME='resource_template_field_section'", { type: QueryTypes.SELECT } ); if (!constraints.length) { await database.query( "ALTER TABLE resource_template_fields ADD CONSTRAINT resource_template_field_section FOREIGN KEY (templateId,sectionId) REFERENCES resource_template_sections(templateId,id)" ); } } export async function enforceResourceConstraints(database: Sequelize) { await constrainSections(database); }