UNPKG

adba

Version:
222 lines (221 loc) 11.1 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.generateMySQLModels = generateMySQLModels; exports.mapMySqlTypeToJsonType = mapMySqlTypeToJsonType; exports.mapMySqlTypeToJsonFormat = mapMySqlTypeToJsonFormat; const objection_1 = require("objection"); const dbl_utils_1 = require("dbl-utils"); const model_utilities_1 = require("./model-utilities"); /** * Generates MySQL models dynamically based on database structures. * @param knexInstance - The Knex instance connected to the database. * @param opts - Options including parse and format functions. * @returns A promise that resolves to an object containing all generated models. */ function generateMySQLModels(knexInstance_1) { return __awaiter(this, arguments, void 0, function* (knexInstance, opts = {}) { const models = {}; const { relationsFunc, squemaFixings, columnsFunc, parseFunc, formatFunc } = opts; try { // Query table and view structures from the database const [tables, views] = yield Promise.all([ knexInstance("information_schema.tables") .where("table_schema", knexInstance.client.config.connection.database) .select("table_name AS name", knexInstance.raw(`'table' as type`)), knexInstance("information_schema.views") .where("table_schema", knexInstance.client.config.connection.database) .select("table_name AS name", knexInstance.raw(`'view' as type`)), ]); const structures = [...tables, ...views]; for (const { name: structureName, type } of structures) { const columns = yield knexInstance("information_schema.columns") .where("table_schema", knexInstance.client.config.connection.database) .andWhere("table_name", structureName) .select("column_name", "data_type", "is_nullable", "column_comment", "column_key", "column_default", "extra"); const foreignKeys = yield knexInstance("information_schema.key_column_usage") .where("table_schema", knexInstance.client.config.connection.database) .andWhere("table_name", structureName) .andWhereNot("referenced_table_name", null) .select("column_name AS from", "referenced_table_name AS table", "referenced_column_name AS to"); // Define a dynamic model class const DynamicModel = class extends objection_1.Model { /** * Returns the table name for the model. */ static get tableName() { return structureName; } /** * Constructs the JSON schema based on the table structure. * @returns The JSON schema object for the table. */ static get jsonSchema() { var _a; const requiredFields = []; const schemaProperties = {}; for (const column of columns) { const format = mapMySqlTypeToJsonFormat(column.data_type, column.column_name); const type = mapMySqlTypeToJsonType(column.data_type); const property = { type: type !== "buffer" ? type : undefined, description: column.column_comment || undefined, }; const isNotNullable = column.is_nullable === "NO"; const isAutoIncrement = (_a = column.extra) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes("auto_increment"); const hasDefault = column.column_default !== null; if (isNotNullable && !isAutoIncrement && !hasDefault) { requiredFields.push(column.column_name); } if (/^(INT|INTEGER|REAL)$/i.test(column.data_type) && column.column_key !== "PRI") { property.minimum = 0; } property.$comment = [type, format].filter(Boolean).join("."); const prefix = type === "buffer" ? "x-" : ""; schemaProperties[prefix + column.column_name] = property; } if (typeof squemaFixings === "function") { const r = squemaFixings(structureName, schemaProperties); if (r) (0, dbl_utils_1.deepMerge)(schemaProperties, r); } return { type: "object", properties: schemaProperties, required: requiredFields.length ? requiredFields : undefined, }; } /** * Constructs relation mappings for the model. * @returns An object containing relation mappings. */ static get relationMappings() { const relations = {}; for (const fk of foreignKeys) { const relatedModel = Object.values(models).find((Model) => Model.tableName === fk.table); if (!relatedModel) { throw new Error(`${structureName}: Model for table ${fk.table} not found`); } relations[`${fk.table}`] = { relation: objection_1.Model.BelongsToOneRelation, modelClass: relatedModel, join: { from: `${structureName}.${fk.from}`, to: `${fk.table}.${fk.to}`, }, }; } if (typeof relationsFunc === "function") { const r = relationsFunc(structureName, relations); if (r) Object.assign(relations, r); } return relations; } /** * Columns information derived from the JSON schema. */ static get columns() { const { properties = {}, required = [] } = this.jsonSchema; const cols = (0, model_utilities_1.jsonSchemaToColumns)(properties, required); if (typeof columnsFunc === "function") { const r = columnsFunc(structureName, cols); if (r) (0, dbl_utils_1.deepMerge)(cols, r); } return cols; } /** * Parses the database JSON. * @param json - The JSON object from the database. * @returns The parsed JSON object. */ $parseDatabaseJson(json) { json = super.$parseDatabaseJson(json); return typeof parseFunc === "function" ? parseFunc(structureName, json) : json; } /** * Formats the JSON object for the database. * @param json - The JSON object to be formatted. * @returns The formatted JSON object. */ $formatDatabaseJson(json) { json = super.$formatDatabaseJson(json); return typeof formatFunc === "function" ? formatFunc(structureName, json) : json; } }; const pascalCaseName = (0, model_utilities_1.className)(structureName); const suffix = type === "table" ? "Table" : "View"; const modelName = `${pascalCaseName}${suffix}Model`; Object.defineProperty(DynamicModel, "name", { value: modelName }); DynamicModel.knex(knexInstance); models[modelName] = DynamicModel; } } catch (err) { console.error("Error generating models:", err); } return models; }); } /** * Maps MySQL data types to corresponding JSON Schema types. * @param mysqlType - The MySQL data type. * @returns The JSON Schema type. */ function mapMySqlTypeToJsonType(mysqlType) { const baseType = mysqlType.toUpperCase(); const typeMap = { BOOLEAN: "boolean", BINARY: "buffer", BLOB: "buffer", BIGINT: "integer", INT: "integer", INTEGER: "integer", MEDIUMINT: "integer", SMALLINT: "integer", TINYINT: "integer", DECIMAL: "number", DOUBLE: "number", FLOAT: "number", NUMERIC: "number", REAL: "number", CHAR: "string", VARCHAR: "string", TEXT: "string", DATE: "string", DATETIME: "string", TIMESTAMP: "string", TIME: "string", }; return typeMap[baseType] || "string"; } /** * Maps MySQL data types to corresponding JSON Schema formats. * @param mysqlType - The MySQL data type. * @param colName - The column name for potential additional format inference. * @returns The JSON Schema format if applicable. */ function mapMySqlTypeToJsonFormat(mysqlType, colName) { const baseType = mysqlType.toUpperCase(); const typeMap = { DATE: "date", DATETIME: "datetime", TIMESTAMP: "datetime", TIME: "time", }; return typeMap[baseType] || undefined; }