adba
Version:
Any DataBase to API
206 lines (205 loc) • 9.8 kB
JavaScript
;
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.generatePostgreSQLModels = generatePostgreSQLModels;
exports.mapPostgresTypeToJsonType = mapPostgresTypeToJsonType;
exports.mapPostgresTypeToJsonFormat = mapPostgresTypeToJsonFormat;
const objection_1 = require("objection");
const dbl_utils_1 = require("dbl-utils");
const model_utilities_1 = require("./model-utilities");
/**
* Generates PostgreSQL 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 generatePostgreSQLModels(knexInstance_1) {
return __awaiter(this, arguments, void 0, function* (knexInstance, opts = {}) {
const models = {};
const { relationsFunc, squemaFixings, parseFunc, formatFunc } = opts;
try {
// Query table and view structures from the database
const tables = yield knexInstance('information_schema.tables')
.where('table_schema', 'public')
.select('table_name AS name', knexInstance.raw(`'table' as type`));
const structures = [...tables];
for (const { name: structureName, type } of structures) {
const columns = yield knexInstance('information_schema.columns')
.where('table_schema', 'public')
.andWhere('table_name', structureName)
.select('column_name', 'data_type', 'is_nullable', 'column_default');
const foreignKeys = yield knexInstance.raw(`
SELECT
ccu.column_name AS "from",
ctu.table_name AS "table",
ccu_pk.column_name AS "to"
FROM
information_schema.table_constraints tc
JOIN information_schema.key_column_usage ccu
ON tc.constraint_name = ccu.constraint_name
AND tc.table_schema = ccu.table_schema
JOIN information_schema.referential_constraints rc
ON tc.constraint_name = rc.constraint_name
AND tc.table_schema = rc.constraint_schema
JOIN information_schema.key_column_usage ccu_pk
ON rc.unique_constraint_name = ccu_pk.constraint_name
AND rc.unique_constraint_schema = ccu_pk.constraint_schema
JOIN information_schema.columns ctu
ON ctu.table_name = ccu_fk.table_name
AND ctu.column_name = ccu_fk.column_name
WHERE
ctu.table_schema = 'public'
AND tc.table_name = ?
`, [structureName]).then(res => res.rows);
// 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() {
const requiredFields = [];
const schemaProperties = {};
for (const column of columns) {
const format = mapPostgresTypeToJsonFormat(column.data_type, column.column_name);
const type = mapPostgresTypeToJsonType(column.data_type);
const property = {
type: type !== 'buffer' ? type : undefined
};
if (column.is_nullable === 'NO' && !column.column_default) {
requiredFields.push(column.column_name);
}
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;
}
/**
* 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 PostgreSQL data types to corresponding JSON Schema types.
* @param postgresType - The PostgreSQL data type.
* @returns The JSON Schema type.
*/
function mapPostgresTypeToJsonType(postgresType) {
const baseType = postgresType.toUpperCase();
const typeMap = {
BOOLEAN: 'boolean',
BYTEA: 'buffer',
BIGINT: 'integer',
INT: 'integer',
INTEGER: 'integer',
SMALLINT: 'integer',
DECIMAL: 'number',
DOUBLE: 'number',
FLOAT: 'number',
NUMERIC: 'number',
REAL: 'number',
CHAR: 'string',
VARCHAR: 'string',
TEXT: 'string',
DATE: 'string',
TIMESTAMP: 'string',
TIMESTAMPTZ: 'string',
TIME: 'string',
};
return typeMap[baseType] || 'string';
}
/**
* Maps PostgreSQL data types to corresponding JSON Schema formats.
* @param postgresType - The PostgreSQL data type.
* @param colName - The column name for potential additional format inference.
* @returns The JSON Schema format if applicable.
*/
function mapPostgresTypeToJsonFormat(postgresType, colName) {
const baseType = postgresType.toUpperCase();
const typeMap = {
DATE: 'date',
TIMESTAMP: 'datetime',
TIMESTAMPTZ: 'datetime',
TIME: 'time',
};
return typeMap[baseType] || undefined;
}