UNPKG

pure-orm

Version:

A SQL Toolkit based on pure business objects passed to and from stateful data access objects

256 lines (255 loc) 9.64 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.create = void 0; const core_1 = require("./core"); const pgp_1 = require("./driver-integrations/pgp"); const create = ({ entities: externalEntities, db, logError }) => { const core = (0, core_1.createCore)({ entities: externalEntities }); let orm; if (db.$config.pgp) { orm = (0, pgp_1.createForPGP)({ core, db, logError }); } else { throw new Error(`You're database driver is not yet supported. You can make a PR to add it, or use the \`createCore\` export which doesn't try to abstract over the database driver, and instead you pass the results of the database driver queries to it.`); } const helperPlanByEntity = new Map(); const getHelperPlan = (entity) => { let plan = helperPlanByEntity.get(entity); if (!plan) { const quotedColumns = new Array(entity.columnNames.length); const updateClausePrefixes = new Array(entity.columnNames.length); const wherePositionalPrefixes = new Array(entity.columnNames.length); const whereNamedPrefixes = new Array(entity.columnNames.length); for (let i = 0; i < entity.columnNames.length; i++) { const column = entity.columnNames[i]; quotedColumns[i] = `"${column}"`; updateClausePrefixes[i] = `"${column}" = $`; wherePositionalPrefixes[i] = `"${entity.tableName}"."${column}" = $`; whereNamedPrefixes[i] = `"${entity.tableName}"."${column}" = $(`; } plan = { quotedColumns, updateClausePrefixes, wherePositionalPrefixes, whereNamedPrefixes }; helperPlanByEntity.set(entity, plan); } return plan; }; const getSqlInsertParts = (model) => { const entity = orm.getEntityByModel(model); const { columnNames, propertyNames } = entity; const helperPlan = getHelperPlan(entity); let columns = ''; const values = []; const valuesVar = []; let paramIndex = 1; for (let i = 0; i < columnNames.length; i++) { const val = model[propertyNames[i]]; if (val !== void 0) { if (columns) { columns += ', '; } columns += helperPlan.quotedColumns[i]; values.push(val); valuesVar.push(`$${paramIndex}`); paramIndex++; } } return { columns, values, valuesVar }; }; const getSqlUpdateParts = (model, on = 'id') => { const entity = orm.getEntityByModel(model); const { columnNames, propertyNames } = entity; const helperPlan = getHelperPlan(entity); let clause = ''; const values = []; let paramIndex = 1; for (let i = 0; i < columnNames.length; i++) { const val = model[propertyNames[i]]; if (val !== void 0) { if (clause) { clause += ', '; } clause += helperPlan.updateClausePrefixes[i] + paramIndex; values.push(val); paramIndex++; } } const idVar = `$${paramIndex}`; values.push(model[on]); return { clause, idVar, values }; }; const getMatchingParts = (model) => { const entity = orm.getEntityByModel(model); const { propertyNames, columnNames } = entity; const helperPlan = getHelperPlan(entity); const values = []; let paramIndex = 1; let whereClause = ''; for (let i = 0; i < propertyNames.length; i++) { const val = model[propertyNames[i]]; if (val != null) { if (whereClause) { whereClause += ' AND '; } whereClause += helperPlan.wherePositionalPrefixes[i] + paramIndex; values.push(val); paramIndex++; } } return { whereClause, values }; }; // This one returns an object, which allows it to be more versatile. // To-do: make this one even better and use it instead of the one above. const getMatchingPartsObject = (model) => { const entity = orm.getEntityByModel(model); const { propertyNames, columnNames } = entity; const helperPlan = getHelperPlan(entity); const values = {}; let paramIndex = 1; let whereClause = ''; for (let i = 0; i < propertyNames.length; i++) { const val = model[propertyNames[i]]; if (val != null) { if (whereClause) { whereClause += ' AND '; } whereClause += helperPlan.whereNamedPrefixes[i] + paramIndex + ')'; values[paramIndex] = val; paramIndex++; } } return { whereClause, values }; }; const getNewWith = (model, sqlColumns, values) => { const Constructor = model.constructor; const entity = orm.getEntityByModel(model); const modelData = {}; for (let i = 0; i < sqlColumns.length; i++) { const propertyName = entity.columnToPropertyMap.get(sqlColumns[i]); if (propertyName) { modelData[propertyName] = values[i]; } } return new Constructor(modelData); }; const getValueBySqlColumn = (model, sqlColumn) => { const entity = orm.getEntityByModel(model); const propertyName = entity.columnToPropertyMap.get(sqlColumn); return propertyName ? model[propertyName] : undefined; }; const getSqlColumnForPropertyName = (model, propertyName) => { const entity = orm.getEntityByModel(model); const column = entity.propertyToColumnMap.get(propertyName); return column; }; /* ------------------------------------------------------------------------*/ /* Built-in basic CRUD functions ------------------------------------------*/ /* ------------------------------------------------------------------------*/ // Standard create const create = (model) => { const entity = orm.getEntityByModel(model); const { columns, values, valuesVar } = getSqlInsertParts(model); const query = ` INSERT INTO "${entity.tableName}" ( ${columns} ) VALUES ( ${valuesVar} ) RETURNING ${entity.selectColumnsClause}; `; return orm.one(query, values); }; // Standard update const update = (model, { on = 'id' } = {}) => { const entity = orm.getEntityByModel(model); const { clause, idVar, values } = getSqlUpdateParts(model, on); const query = ` UPDATE "${entity.tableName}" SET ${clause} WHERE "${entity.tableName}".${getSqlColumnForPropertyName(model, on)} = ${idVar} RETURNING ${entity.selectColumnsClause}; `; return orm.one(query, values); }; // Standard delete const _delete = (model) => { const entity = orm.getEntityByModel(model); const id = model.id; const query = ` DELETE FROM "${entity.tableName}" WHERE "${entity.tableName}".id = $(id) `; return orm.none(query, { id }); }; const deleteMatching = (model) => { const entity = orm.getEntityByModel(model); const { whereClause, values } = getMatchingParts(model); const query = ` DELETE FROM "${entity.tableName}" WHERE ${whereClause}; `; return orm.none(query, values); }; const getMatching = (model) => { const entity = orm.getEntityByModel(model); const { whereClause, values } = getMatchingParts(model); const query = ` SELECT ${entity.selectColumnsClause} FROM "${entity.tableName}" WHERE ${whereClause}; `; return orm.one(query, values); }; const getOneOrNoneMatching = (model) => { const entity = orm.getEntityByModel(model); const { whereClause, values } = getMatchingParts(model); const query = ` SELECT ${entity.selectColumnsClause} FROM "${entity.tableName}" WHERE ${whereClause}; `; return orm.oneOrNone(query, values); }; const getAnyMatching = (model) => { const entity = orm.getEntityByModel(model); const { whereClause, values } = getMatchingParts(model); const query = ` SELECT ${entity.selectColumnsClause} FROM "${entity.tableName}" WHERE ${whereClause}; `; return orm.any(query, values); }; const getAllMatching = (model) => { const entity = orm.getEntityByModel(model); const { whereClause, values } = getMatchingParts(model); const query = ` SELECT ${entity.selectColumnsClause} FROM "${entity.tableName}" WHERE ${whereClause}; `; return orm.many(query, values); }; return Object.assign({}, orm, { // Built-in basic CRUD functions create, update, delete: _delete, deleteMatching, getMatching, getOneOrNoneMatching, getAnyMatching, getAllMatching, // Helper Utility functions getSqlInsertParts, getSqlUpdateParts, getMatchingParts, getMatchingPartsObject, getNewWith, getValueBySqlColumn, getSqlColumnForPropertyName }); }; exports.create = create;