UNPKG

dobo

Version:

DBMS for Bajo Framework

288 lines (273 loc) 9.27 kB
/** * Dobo model's chainable methods. Only the keys listed here are allowed to be called in chainable methods. * The values are the only allowed arguments for the corresponding chainable methods. * @typedef {Object} TMethods * @type {Object} * @memberof DoboAction * @property {string[]} [createRecord=['body']] * @property {string[]} [updateRecord=['id', 'body']] * @property {string[]} [upsertRecord=['body']] * @property {string[]} [removeRecord=['id']] * @property {string[]} [getRecord=['id']] * @property {string[]} [findRecord=['filter']] * @property {string[]} [findOneRecord=['filter']] * @property {string[]} [findAllRecord=['filter']] * @property {string[]} [findAllRecords=['filter']] - Alias of `findAllRecord` * @property {string[]} [countRecord=['params']] * @property {string[]} [aggregate=['params']] * @property {string[]} [histogram=['params']] * @property {string[]} [createAttachment=['id']] * @property {string[]} [findAttachment=['id']] * @property {string[]} [getAttachment=['id', 'field', 'file']] * @property {string[]} [listAttachment=['params']] * @property {string[]} [removeAttachment=['id', 'field', 'file']] * @property {string[]} [updateAttachment=['id']] */ const methods = { createRecord: ['body'], updateRecord: ['id', 'body'], upsertRecord: ['body'], removeRecord: ['id'], getRecord: ['id'], findRecord: ['filter'], findOneRecord: ['filter'], findAllRecord: ['filter'], findAllRecords: ['filter'], countRecord: ['params'], aggregate: ['params'], histogram: ['params'], createAttachment: ['id'], findAttachment: ['id'], getAttachment: ['id', 'field', 'file'], listAttachment: ['params'], removeAttachment: ['id', 'field', 'file'], updateAttachment: ['id'] } /** * Allowed options for Dobo model's chainable methods. Only the keys listed here are allowed to be called * in chainable methods. The values are the default values for the corresponding options. * @typedef {Object} TOptions * @type {Object} * @memberof DoboAction * @property {boolean} [truncateString=true] * @property {boolean} [noResult=false] * @property {boolean} [noBodySanitizer=false] * @property {boolean} [noResultSanitizer=false] * @property {boolean} [noValidation=false] * @property {boolean} [dataOnly=true] * @property {boolean} [noHook=false] * @property {boolean} [noModelHook=false] * @property {boolean} [noDynHook=false] * @property {Array} [extFields=[]] * @property {Array} [fields=[]] * @property {boolean} [noFlash=false] * @property {Array} [hidden=[]] * @property {Array} [refs=[]] * @property {Array} [types=[]] * @property {string|undefined} [type=undefined] * @property {string|undefined} [group=undefined] * @property {Array} [aggregates=[]] * @property {string|undefined} [field=undefined] * @property {string|undefined} [queryHandler=undefined] * @property {boolean} [count=true] * @property {boolean} [noCache=true] * @property {boolean} [partial=true] * @property {boolean} [mimeType=true] * @property {boolean} [fullPath=true] * @property {boolean} [stats=true] * @property {boolean} [dirOnly=true] * @property {string|undefined} [setField=undefined] * @property {string|undefined} [setFile=undefined] * @property {string|undefined} [source=undefined] * @property {boolean} [uriEncoded=true] */ const options = { truncateString: true, noResult: false, noBodySanitizer: false, noResultSanitizer: false, noValidation: false, dataOnly: true, noHook: false, noModelHook: false, noDynHook: false, extFields: [], fields: [], noFlash: false, hidden: [], refs: [], types: [], type: undefined, group: undefined, aggregates: [], field: undefined, queryHandler: undefined, count: true, noCache: true, partial: true, // attachment mimeType: true, fullPath: true, stats: true, dirOnly: true, setField: undefined, setFile: undefined, source: undefined, uriEncoded: true } /** * Action factory function * * @async * @returns {Promise<void>} */ async function actionFactory () { const { Tools } = this.app.baseClass /** * Action class enables you to call model's method with all parameters and options in chainable methods * in addition of the normal call. * * Action class is created by calling model method without any parameter. * The resulted class instance can be used to call model's method with all parameters and options * in chainable methods. * * Action class can also be created by calling `model.action()` method, with or without parameters. * Please see the example below: * * You always need to call the `run()` method as the last step. The `run()` method will return the result * of the model's method call. * * @class * @example * // method 1: * const action = await model.getRecord() // Important: no parameter passed * const result = action.id('rec-id').noHook().dataOnly(false).run() * // method 2: * const result = await model.action().getRecord('rec-id').noHook().dataOnly(false).run() * // method 3: * const result = await model.action('getRecord').id('rec-id').noHook().dataOnly(false).run() * // method 4: * const result = await model.action('getRecord', 'rec-id').noHook().dataOnly(false).run() * // Instead of chaining options as methods, you can also pass options object as the parameter to the run() method * const result = await model.action('getRecord', 'rec-id').run({ noHook: true, dataOnly: false }) */ class DoboAction extends Tools { constructor (model, name, ...args) { super(model.plugin) this.model = model this.name = name this._options = {} if (name) this._setArgs(name, args) // create methods for (const method in methods) { this[method] = (...args) => { this.name = method this._setArgs(method, args) this._options = {} return this } } // create options builder methods for (const option in options) { this[option] = (value) => { this._options[option] = value ?? options[option] return this } } } /** * Internal method to set the arguments to the instance properties based on the method name * and the arguments passed. * @internal * @method * @param {string} method - The name of the method being called * @param {Array} args - The arguments passed to the method * @returns {void} */ _setArgs = (method, args) => { if (args.length === 0) return for (const idx in methods[method]) { this['_' + methods[method][idx]] = args[idx] } } /** * Set the ID for the corresponding record. You need this for `updateRecord` or `removeRecord` * operations. This method is chainable. * @method * @param {string|number} value - The ID value to set * @returns {DoboAction} The current instance for chaining */ id = value => { this._id = value return this } /** * Set the body for the corresponding record. You need this for `createRecord`, `updateRecord` * or `upsertRecord` operations. This method is chainable. * @method * @param {object} value - The body value to set * @returns {DoboAction} The current instance for chaining */ body = value => { this._body = value return this } /** * Set the filter for the corresponding record. You optionally need this for `findRecord`, * `findOneRecord` or `findAllRecord` operations. This method is chainable. * @method * @param {object} value - The filter value to set * @returns {DoboAction} The current instance for chaining */ filter = value => { this._filter = value return this } field = value => { this._field = value return this } file = value => { this._file = value return this } /** * Set the options for the corresponding method. This method is chainable. * @method * @param {DoboAction.TOptions} value - The options to set * @returns {DoboAction} The current instance for chaining */ options = (value = {}) => { for (const k in value) { this._options[k] = value } return this } /** * Execute the corresponding method with the set arguments and options. This method is chainable. * @async * @method * @param {DoboAction.TOptions} value - If set, it will override the options set by the `options()` method. This is useful if you want to set options at the last step. * @returns {Promise<*>} The result of the method execution */ run = async (value = {}) => { this.options(value) const args = methods[this.name].map(item => this['_' + item]) args.push(this._options) return await this.model[this.name](...args) } /** * Dispose the instance and clean up resources. * @async * @method * @returns {Promise<void>} */ dispose = async () => { await super.dispose() this.model = null this._options = null } } this.app.baseClass.DoboAction = DoboAction } export default actionFactory