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
JavaScript
;
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;