UNPKG

json-schema-entity

Version:

Manage a group of tables with a parent child relation in SQL that will be seen as a document, or entity, like a no SQL database

273 lines (263 loc) 7.76 kB
var _ = require('lodash') var assert = require('assert') const debug = require('debug')('entity:adapter:common') var EntityError = require('../entity-error') exports.create = function (record, data, options) { options = options || {} var fields = [] var params = [] _.forEach(data.properties, function (property, name) { if (!property.autoIncrement) { var value = record[name] if ( (value === void 0 || value === null) && property.defaultValue ) { value = property.defaultValue } if (value !== void 0) { var field = property.field || name fields.push(field) if (property.enum) { value = value.substr(0, property.maxLength) } if (property.mapper?.write) { value = property.mapper.write(value, record) } params.push({ value: value, type: property.type, maxLength: property.maxLength, decimals: property.decimals, timezone: property.timezone }) } } }) var index = 1 var insertCommand = data.insertCommand .replace( '<fields>', fields.reduce((fields, field) => { return fields + (fields ? ',' : '') + this.wrap(field) }, '') ) .replace( '<values>', fields.reduce(function (fields) { return fields + (fields ? ',' : '') + '$' + index++ }, '') ) if (options.schema && this.db.dialect === 'postgres') { insertCommand = `INSERT INTO ${ options.schema }.${insertCommand.substr(12)}` } debug(insertCommand, params) return this.db .execute(insertCommand, params, options) .then(function (recordset) { checkRecordsetLength(data, null, recordset.length, 'create') var inserted = recordset[0] _.forEach(data.properties, function (property, name) { var fieldName = property.field || name record[name] = inserted[fieldName] if (property.type === 'date') { if (_.isDate(record[name])) { record[name] = record[name].toISOString() } if (typeof record[name] === 'string') { record[name] = record[name].slice(0, 10) } } if (property.mapper?.read) { record[name] = property.mapper.read(record[name], record) } }) const updatedAtColumnName = exports.getUpdatedAtColumnName(data) if (updatedAtColumnName) { record.updatedAt = inserted[updatedAtColumnName] } return record }) } exports.update = function (record, data, options) { assert(options.where) var fields = [] var params = [] _.forEach(data.properties, function (property, name) { if (!property.autoIncrement && name !== data.foreignKey) { var value = record[name] if (value !== void 0) { var field = property.field || name fields.push(field) if (property.enum) { value = value.substr(0, property.maxLength) } if (property.mapper?.write) { value = property.mapper.write(value, record) } params.push({ value: value, type: property.type, maxLength: property.maxLength, decimals: property.decimals, timezone: property.timezone }) } } }) if (fields.length === 0) { return Promise.resolve(record) } var findKeys = data.primaryKeyFields.map(function (name, index) { const attribute = data.primaryKeyAttributes[index] params.push(options.where[attribute]) return name }) const updatedAtColumnName = exports.getUpdatedAtColumnName(data) if (updatedAtColumnName) { params.push(options.where.updatedAt || null) findKeys.push(updatedAtColumnName) } var index = 0 var updateCommand = data.updateCommand .replace( '<fields-values>', fields.reduce((fields, field) => { return ( fields + (fields ? ',' : '') + this.wrap(field) + '=$' + ++index ) }, '') ) .replace( '<primary-keys>', findKeys.reduce((fields, field) => { return ( fields + (fields ? ' AND ' : '') + this.wrap(field) + (params[index] === null ? params.splice(index, 1) && ' IS NULL' : '=$' + ++index) ) }, '') ) if (options.schema && this.db.dialect === 'postgres') { updateCommand = `UPDATE ${options.schema}.${updateCommand.substr( 7 )}` } debug(updateCommand, params) return this.db .execute(updateCommand, params, options) .then(function (recordset) { checkRecordsetLength( data, options.where, recordset.length, 'update' ) var updated = recordset[0] _.forEach(data.properties, function (property, name) { var fieldName = property.field || name record[name] = updated[fieldName] if (property.type === 'date') { if (_.isDate(record[name])) { record[name] = record[name].toISOString() } if (typeof record[name] === 'string') { record[name] = record[name].slice(0, 10) } } if (property.mapper?.read) { record[name] = property.mapper.read(record[name], record) } }) const updatedAtColumnName = exports.getUpdatedAtColumnName(data) if (updatedAtColumnName) { record.updatedAt = updated[updatedAtColumnName] } return record }) } exports.destroy = function (data, options) { assert(options.where) var params = [] var findKeys = data.primaryKeyFields.map(function (name, index) { const attribute = data.primaryKeyAttributes[index] params.push(options.where[attribute]) return name }) const updatedAtColumnName = exports.getUpdatedAtColumnName(data) if (updatedAtColumnName) { params.push(options.where.updatedAt || null) findKeys.push(updatedAtColumnName) } var index = 0 var deleteCommand = data.deleteCommand.replace( '<find-keys>', findKeys.reduce((fields, field) => { return ( fields + (fields ? ' AND ' : '') + this.wrap(field) + (params[index] === null ? params.splice(index, 1) && ' IS NULL' : '=$' + ++index) ) }, '') ) if (options.schema && this.db.dialect === 'postgres') { deleteCommand = `DELETE FROM ${ options.schema }.${deleteCommand.substr(12)}` } debug(deleteCommand, params) return this.db .execute(deleteCommand, params, options) .then(function (recordset) { checkRecordsetLength( data, options.where, recordset.length, 'delete' ) return recordset.length }) } function checkRecordsetLength(data, key, n, type) { if (n === 0 && key) { throw new EntityError({ type: 'RecordModifiedOrDeleted', message: `Entity '${data.key}' key ${JSON.stringify( key )} not found for ${type}` }) } assert(n === 1, `${n} records have been ${type}d, expected one`) } exports.getUpdatedAtColumnName = function (data) { if (data.timestamps) { let updatedAtColumnName = 'updated_at' if (typeof data.timestamps === 'string') { updatedAtColumnName += data.timestamps } return updatedAtColumnName } } exports.convertToUpdatedAt = function (record, data, target) { const updatedAtColumnName = exports.getUpdatedAtColumnName(data) if (record[updatedAtColumnName]) { if (target) { target.updatedAt = record[updatedAtColumnName] } else { record.updatedAt = record[updatedAtColumnName] delete record[updatedAtColumnName] } } }