dobo
Version:
DBMS for Bajo Framework
101 lines (95 loc) • 4.88 kB
JavaScript
import { getFilterAndOptions, execHook, execModelHook, execDynHook, execValidation, getRefs, handleReq, clearCache } from './_util.js'
const action = 'upsertRecord'
async function native (body = {}, opts = {}) {
const { isSet } = this.app.lib.aneka
const { getDefaultValues } = this.app.dobo
const { cloneDeep, get } = this.app.lib._
opts.dataOnly = opts.dataOnly ?? true
const { dataOnly } = opts
const { options } = await getFilterAndOptions.call(this, null, opts, action)
const { truncateString, noResult, noBodySanitizer, noResultSanitizer, noValidation } = options
const extFields = get(options, 'validation.extFields', [])
let input = noBodySanitizer ? cloneDeep(body) : await this.sanitizeBody({ body, extFields, strict: true, truncateString, action })
if (!noValidation) input = await execValidation.call(this, input, options)
await execHook.call(this, 'beforeUpsertRecord', input, options)
await execModelHook.call(this, 'beforeUpsertRecord', input, options)
await execDynHook.call(this, 'beforeUpsertRecord', input, options)
const result = options.record ?? (await this.driver._upsertRecord(this, input, options)) ?? {}
if (noResult) return
await handleReq.call(this, result.data.id, 'upserted', options)
await clearCache.call(this, body.id)
const { warnings } = getDefaultValues(options)
if (!warnings) delete result.warnings
if (isSet(options.refs)) await getRefs.call(this, [result.data], options)
if (!noResultSanitizer) result.data = await this.sanitizeRecord(result.data, options)
await execDynHook.call(this, 'afterUpsertRecord', input, result, options)
await execModelHook.call(this, 'afterUpsertRecord', input, result, options)
await execHook.call(this, 'afterUpsertRecord', input, result, options)
return dataOnly ? result.data : result
}
async function manual (body = {}, options = {}) {
const { isSet } = this.app.lib.aneka
const { get, omit } = this.app.lib._
if (isSet(body.id)) body.id = this.sanitizeId(body.id)
let old = false
if (isSet(body.id)) {
try {
old = await this.driver._getRecord(this, body.id, { noHook: true, noModelHook: true })
} catch (err) {
}
}
const id = get(old, 'data.id')
if (id) return await this.updateRecord(old.data.id, omit(body, ['id']), options)
return await this.createRecord(body, options)
}
/**
* @typedef {Object} TRecordUpdateOptions
* @see Model#updateRecord
* @property {boolean} [dataOnly=true] - If ```true``` (default) returns record's object. Otherwise {@link TRecordUpdateResult}
* @property {boolean} [noHook=false] - If ```true```, no model's hook will be executed
* @property {boolean} [noModelHook=false] - If ```true```, no model's hook will be executed
* @property {boolean} [noValidation=false] - If ```true```, no validation of data payload performed
* @property {boolean} [noCheckUnique=false] - If ```true```, no unique validation for ID performed
* @property {boolean} [noBodySanitizer=false] - If ```true```, accept data payload as is without sanitization
* @property {boolean} [noRecordSanitizer=false] - If ```true```, accept result payload as is without sanitization
* @property {boolean} [noResult=false] - If ```true```, returns nothing
* @property {boolean} [truncateString=true] - If ```true``` (default), string is truncated to its model's ```maxLength```
* @property {boolean} [partial=true] - If ```true``` (default), only updated values are saved. Otherwise replace all existing values with given payload
* @property {boolean} [fields=[]] - If not empty, return only these fields EXCLUDING hidden fields
* @property {boolean} [hidden=[]] - Additional fields to hide, in addition the one set in model's model
* @property {boolean} [forceNoHidden=false] - If ```true```, hidden fields will be ignored and ALL fields will be returned
*/
/**
* Update a record by it's ID and body payload
*
* Example:
* ```javascript
* const { recordUpdate } = this.app.dobo
* const { body } = {
* name: 'Republic of Indonesia',
* phoneCode: '+62'
* }
* const result = await recordUpdate('CdbCountry', 'ID', body)
* ```
*
* @method
* @memberof Dobo
* @async
* @instance
* @name recordUpdate
* @param {string} name - Model's name
* @param {(string|number)} id - Record's ID
* @param {Object} body - Body payload
* @param {TRecordUpdateOptions} [options={}]
* @returns {(TRecordUpdateResult|Object)} Returns updated record if ```options.dataOnly``` is set. {@link TRecordUpdateResult} otherwise
*/
async function upsertRecord (...args) {
if (args.length === 0) return this.action(action, ...args)
const [body = {}, opts = {}] = args
if (this.driver.upsertRecord) {
const { options } = await getFilterAndOptions.call(this, null, opts, action)
return await native.call(this, body, options)
}
return await manual.call(this, body, opts)
}
export default upsertRecord