dobo 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/FUNDING.yml +13 -0
- package/.github/workflows/repo-lockdown.yml +24 -0
- package/.jsdoc.conf.json +45 -0
- package/LICENSE +1 -1
- package/README.md +38 -19
- package/docs/Dobo.html +26 -0
- package/docs/data/search.json +1 -0
- package/docs/fonts/Inconsolata-Regular.ttf +0 -0
- package/docs/fonts/OpenSans-Regular.ttf +0 -0
- package/docs/fonts/WorkSans-Bold.ttf +0 -0
- package/docs/global.html +7 -0
- package/docs/index.html +3 -0
- package/docs/index.js.html +578 -0
- package/docs/lib_collect-connections.js.html +39 -0
- package/docs/lib_collect-drivers.js.html +52 -0
- package/docs/lib_collect-features.js.html +36 -0
- package/docs/lib_collect-schemas.js.html +94 -0
- package/docs/lib_index.js.html +6 -0
- package/docs/method_model_create.js.html +35 -0
- package/docs/method_model_drop.js.html +34 -0
- package/docs/method_model_exists.js.html +40 -0
- package/docs/method_record_count.js.html +69 -0
- package/docs/method_record_create.js.html +114 -0
- package/docs/method_record_find-all.js.html +44 -0
- package/docs/method_record_find-one.js.html +73 -0
- package/docs/method_record_find.js.html +118 -0
- package/docs/method_record_get.js.html +92 -0
- package/docs/method_record_remove.js.html +75 -0
- package/docs/method_record_update.js.html +107 -0
- package/docs/method_record_upsert.js.html +54 -0
- package/docs/method_sanitize_body.js.html +88 -0
- package/docs/method_sanitize_date.js.html +30 -0
- package/docs/method_sanitize_id.js.html +20 -0
- package/docs/method_validate.js.html +249 -0
- package/docs/module-Lib.html +3 -0
- package/docs/scripts/core.js +726 -0
- package/docs/scripts/core.min.js +23 -0
- package/docs/scripts/resize.js +90 -0
- package/docs/scripts/search.js +265 -0
- package/docs/scripts/search.min.js +6 -0
- package/docs/scripts/third-party/Apache-License-2.0.txt +202 -0
- package/docs/scripts/third-party/fuse.js +9 -0
- package/docs/scripts/third-party/hljs-line-num-original.js +369 -0
- package/docs/scripts/third-party/hljs-line-num.js +1 -0
- package/docs/scripts/third-party/hljs-original.js +5171 -0
- package/docs/scripts/third-party/hljs.js +1 -0
- package/docs/scripts/third-party/popper.js +5 -0
- package/docs/scripts/third-party/tippy.js +1 -0
- package/docs/scripts/third-party/tocbot.js +672 -0
- package/docs/scripts/third-party/tocbot.min.js +1 -0
- package/docs/static/bitcoin.jpeg +0 -0
- package/docs/static/home.md +25 -0
- package/docs/static/logo-ecosystem.png +0 -0
- package/docs/static/logo.png +0 -0
- package/docs/styles/clean-jsdoc-theme-base.css +1159 -0
- package/docs/styles/clean-jsdoc-theme-dark.css +412 -0
- package/docs/styles/clean-jsdoc-theme-light.css +482 -0
- package/docs/styles/clean-jsdoc-theme-scrollbar.css +30 -0
- package/docs/styles/clean-jsdoc-theme-without-scrollbar.min.css +1 -0
- package/docs/styles/clean-jsdoc-theme.min.css +1 -0
- package/extend/bajo/intl/en-US.json +3 -2
- package/extend/bajo/intl/id.json +3 -2
- package/extend/bajoCli/applet/connection.js +5 -5
- package/extend/bajoCli/applet/lib/post-process.js +22 -16
- package/extend/bajoCli/applet/model-clear.js +4 -4
- package/extend/bajoCli/applet/model-rebuild.js +13 -13
- package/extend/bajoCli/applet/record-create.js +10 -8
- package/extend/bajoCli/applet/record-find.js +6 -5
- package/extend/bajoCli/applet/record-get.js +5 -5
- package/extend/bajoCli/applet/record-remove.js +5 -5
- package/extend/bajoCli/applet/record-update.js +9 -9
- package/extend/bajoCli/applet/schema.js +4 -4
- package/extend/bajoCli/applet/stat-count.js +4 -4
- package/extend/dobo/feature/created-at.js +1 -1
- package/extend/dobo/feature/removed-at.js +3 -3
- package/extend/dobo/feature/updated-at.js +2 -2
- package/extend/waibuMpa/route/attachment/@model/@id/@field/@file.js +3 -3
- package/index.js +230 -72
- package/lib/add-fixtures.js +5 -5
- package/lib/build-bulk-action.js +2 -2
- package/lib/check-unique.js +2 -2
- package/lib/collect-connections.js +14 -3
- package/lib/collect-drivers.js +14 -6
- package/lib/{collect-feature.js → collect-features.js} +12 -4
- package/lib/collect-schemas.js +17 -9
- package/lib/exec-feature-hook.js +1 -1
- package/lib/exec-validation.js +5 -5
- package/lib/generic-prop-sanitizer.js +6 -5
- package/lib/handle-attachment-upload.js +2 -2
- package/lib/index.js +3 -0
- package/lib/mem-db/conn-sanitizer.js +1 -1
- package/lib/mem-db/instantiate.js +4 -4
- package/lib/mem-db/method/record/find.js +1 -1
- package/lib/mem-db/method/record/get.js +1 -1
- package/lib/mem-db/method/record/remove.js +1 -1
- package/lib/mem-db/method/record/update.js +1 -1
- package/lib/mem-db/start.js +1 -1
- package/lib/merge-attachment-info.js +2 -2
- package/lib/multi-rel-rows.js +2 -2
- package/lib/resolve-method.js +3 -3
- package/lib/sanitize-schema.js +8 -7
- package/lib/single-rel-rows.js +2 -2
- package/method/attachment/copy-uploaded.js +2 -2
- package/method/attachment/create.js +3 -3
- package/method/attachment/find.js +2 -2
- package/method/attachment/get-path.js +3 -3
- package/method/attachment/get.js +1 -1
- package/method/attachment/pre-check.js +1 -1
- package/method/attachment/remove.js +1 -1
- package/method/bulk/create.js +6 -6
- package/method/model/clear.js +5 -5
- package/method/model/create.js +18 -5
- package/method/model/drop.js +17 -5
- package/method/model/exists.js +18 -5
- package/method/record/clear.js +5 -5
- package/method/record/count.js +27 -5
- package/method/record/create.js +46 -6
- package/method/record/find-all.js +16 -0
- package/method/record/find-one.js +20 -6
- package/method/record/find.js +69 -6
- package/method/record/get.js +48 -6
- package/method/record/remove.js +36 -5
- package/method/record/update.js +47 -6
- package/method/record/upsert.js +18 -2
- package/method/sanitize/body.js +18 -3
- package/method/sanitize/date.js +20 -7
- package/method/sanitize/id.js +10 -0
- package/method/stat/aggregate.js +4 -4
- package/method/stat/histogram.js +4 -4
- package/method/validate.js +96 -7
- package/package.json +41 -36
- package/wiki/APPLETS.md +57 -0
- package/wiki/CONFIG.md +25 -0
- package/wiki/CONTRIBUTING.md +5 -0
- package/wiki/DEV-GUIDE.md +1 -0
- package/wiki/ECOSYSTEM.md +20 -0
- package/wiki/GETTING-STARTED.md +166 -0
- package/wiki/USER-GUIDE.md +1 -0
- /package/{docs/query-language.md → wiki/QUERY-LANGUAGE.md} +0 -0
package/method/record/create.js
CHANGED
|
@@ -6,11 +6,51 @@ import execValidation from '../../lib/exec-validation.js'
|
|
|
6
6
|
import execFeatureHook from '../../lib/exec-feature-hook.js'
|
|
7
7
|
import singleRelRows from '../../lib/single-rel-rows.js'
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {Object} TRecordCreateOptions
|
|
11
|
+
* @see Dobo#recordCreate
|
|
12
|
+
* @property {boolean} [dataOnly=true] - If ```true``` (default) returns record's object. Otherwise {@link TRecordCreateResult}
|
|
13
|
+
* @property {boolean} [noHook=false] - If ```true```, no model's hook will be executed
|
|
14
|
+
* @property {boolean} [noFeatureHook=false] - If ```true```, no model's feature hook will be executed
|
|
15
|
+
* @property {boolean} [noValidation=false] - If ```true```, no validation of data payload performed
|
|
16
|
+
* @property {boolean} [noCheckUnique=false] - If ```true```, no unique validation for ID performed
|
|
17
|
+
* @property {boolean} [noSanitize=false] - If ```true```, accept data payload as is without sanitization
|
|
18
|
+
* @property {boolean} [noResult=false] - If ```true```, returns nothing
|
|
19
|
+
* @property {boolean} [truncateString=true] - If ```true``` (default), string is truncated to its schema's ```maxLemngth```
|
|
20
|
+
* @property {boolean} [fields=[]] - If not empty, return only these fields EXCLUDING hidden fields
|
|
21
|
+
* @property {boolean} [hidden=[]] - Additional fields to hide, in addition the one set in model's schema
|
|
22
|
+
* @property {boolean} [forceNoHidden=false] - If ```true```, hidden fields will be ignored and ALL fields will be returned
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Create a new record
|
|
27
|
+
*
|
|
28
|
+
* Example:
|
|
29
|
+
* ```javascript
|
|
30
|
+
* const { recordCreate } = this.app.dobo
|
|
31
|
+
* const { body } = {
|
|
32
|
+
* id: 'ID',
|
|
33
|
+
* name: 'Indonesia',
|
|
34
|
+
* iso3: 'IDN'
|
|
35
|
+
* }
|
|
36
|
+
* const result = await recordCreate('CdbCountry', body)
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @method
|
|
40
|
+
* @memberof Dobo
|
|
41
|
+
* @async
|
|
42
|
+
* @instance
|
|
43
|
+
* @name recordCreate
|
|
44
|
+
* @param {string} name - Model's name
|
|
45
|
+
* @param {Object} body - Data to be saved
|
|
46
|
+
* @param {TRecordCreateOptions} [options={}]
|
|
47
|
+
* @returns {(TRecordCreateResult|Object)} Returns newly created record if ```options.dataOnly``` is set. {@link TRecordCreateResult} otherwise
|
|
48
|
+
*/
|
|
9
49
|
async function create (name, input, opts = {}) {
|
|
10
50
|
const { generateId, runHook } = this.app.bajo
|
|
11
|
-
const { isSet } = this.lib.aneka
|
|
51
|
+
const { isSet } = this.app.lib.aneka
|
|
12
52
|
const { clearModel } = this.cache ?? {}
|
|
13
|
-
const { find, forOwn, cloneDeep, camelCase, omit, get, pick } = this.lib._
|
|
53
|
+
const { find, forOwn, cloneDeep, camelCase, omit, get, pick } = this.app.lib._
|
|
14
54
|
delete opts.record
|
|
15
55
|
const options = cloneDeep(omit(opts, ['req', 'reply']))
|
|
16
56
|
options.req = opts.req
|
|
@@ -26,8 +66,8 @@ async function create (name, input, opts = {}) {
|
|
|
26
66
|
const extFields = get(options, 'validation.extFields', [])
|
|
27
67
|
let body = noSanitize ? cloneDeep(input) : await this.sanitizeBody({ body: input, schema, extFields, strict: true })
|
|
28
68
|
if (!noHook) {
|
|
29
|
-
await runHook(`${this.
|
|
30
|
-
await runHook(`${this.
|
|
69
|
+
await runHook(`${this.ns}:beforeRecordCreate`, name, body, options)
|
|
70
|
+
await runHook(`${this.ns}.${camelCase(name)}:beforeRecordCreate`, body, options)
|
|
31
71
|
}
|
|
32
72
|
if (!isSet(body.id)) {
|
|
33
73
|
if (idField.type === 'string') {
|
|
@@ -61,8 +101,8 @@ async function create (name, input, opts = {}) {
|
|
|
61
101
|
if (noResult) return
|
|
62
102
|
record.data = await this.pickRecord({ record: record.data, fields, schema, hidden, forceNoHidden })
|
|
63
103
|
if (!noHook) {
|
|
64
|
-
await runHook(`${this.
|
|
65
|
-
await runHook(`${this.
|
|
104
|
+
await runHook(`${this.ns}.${camelCase(name)}:afterRecordCreate`, nbody, options, record)
|
|
105
|
+
await runHook(`${this.ns}:afterRecordCreate`, name, nbody, options, record)
|
|
66
106
|
}
|
|
67
107
|
if (!noFeatureHook) await execFeatureHook.call(this, 'afterCreate', { schema, body: nbody, options, record })
|
|
68
108
|
return dataOnly ? record.data : record
|
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find all records by model's name and given filter.
|
|
3
|
+
*
|
|
4
|
+
* The total number of records returned is limited by ```hardLimit``` value set in {@tutorial config} file.
|
|
5
|
+
*
|
|
6
|
+
* @see Dobo#recordFind
|
|
7
|
+
* @method
|
|
8
|
+
* @memberof Dobo
|
|
9
|
+
* @async
|
|
10
|
+
* @instance
|
|
11
|
+
* @name recordFindAll
|
|
12
|
+
* @param {string} name - Model's name
|
|
13
|
+
* @param {Object} [filter={}] - Filter object
|
|
14
|
+
* @param {TRecordFindOptions} [options={}]
|
|
15
|
+
* @returns {(TRecordFindResult|Array.<Object>)} Return ```array``` of records if ```options.dataOnly``` is set. {@link TRecordFindResult} otherwise
|
|
16
|
+
*/
|
|
1
17
|
async function findAll (name, filter = {}, options = {}) {
|
|
2
18
|
const { maxLimit, hardLimit } = this.config.default.filter
|
|
3
19
|
filter.page = 1
|
|
@@ -2,11 +2,25 @@ import resolveMethod from '../../lib/resolve-method.js'
|
|
|
2
2
|
import singleRelRows from '../../lib/single-rel-rows.js'
|
|
3
3
|
import execFeatureHook from '../../lib/exec-feature-hook.js'
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Find the first record by model's name and given filter.
|
|
7
|
+
*
|
|
8
|
+
* @see Dobo#recordFind
|
|
9
|
+
* @method
|
|
10
|
+
* @memberof Dobo
|
|
11
|
+
* @async
|
|
12
|
+
* @instance
|
|
13
|
+
* @name recordFindOne
|
|
14
|
+
* @param {string} name - Model's name
|
|
15
|
+
* @param {Object} [filter={}] - Filter object
|
|
16
|
+
* @param {TRecordFindOptions} [options={}]
|
|
17
|
+
* @returns {(TRecordGetResult|Object)} Return record's ```object``` if ```options.dataOnly``` is set. {@link TRecordGetResult} otherwise
|
|
18
|
+
*/
|
|
5
19
|
async function findOne (name, filter = {}, opts = {}) {
|
|
6
|
-
const { isSet } = this.lib.aneka
|
|
20
|
+
const { isSet } = this.app.lib.aneka
|
|
7
21
|
const { runHook } = this.app.bajo
|
|
8
22
|
const { get, set } = this.cache ?? {}
|
|
9
|
-
const { cloneDeep, camelCase, omit, pick } = this.lib._
|
|
23
|
+
const { cloneDeep, camelCase, omit, pick } = this.app.lib._
|
|
10
24
|
delete opts.record
|
|
11
25
|
const options = cloneDeep(omit(opts, ['req', 'reply']))
|
|
12
26
|
options.req = opts.req
|
|
@@ -24,8 +38,8 @@ async function findOne (name, filter = {}, opts = {}) {
|
|
|
24
38
|
if (options.queryHandler) filter.query = await options.queryHandler.call(opts.req ? this.app[opts.req.ns] : this, filter.query, opts.req)
|
|
25
39
|
filter.match = this.buildMatch({ input: filter.match, schema, options }) ?? {}
|
|
26
40
|
if (!noHook) {
|
|
27
|
-
await runHook(`${this.
|
|
28
|
-
await runHook(`${this.
|
|
41
|
+
await runHook(`${this.ns}:beforeRecordFindOne`, name, filter, options)
|
|
42
|
+
await runHook(`${this.ns}.${camelCase(name)}:beforeRecordFindOne`, filter, options)
|
|
29
43
|
}
|
|
30
44
|
if (!noFeatureHook) await execFeatureHook.call(this, 'beforeFindOne', { schema, filter, options })
|
|
31
45
|
if (get && !noCache && !options.record) {
|
|
@@ -45,8 +59,8 @@ async function findOne (name, filter = {}, opts = {}) {
|
|
|
45
59
|
record.data = await this.pickRecord({ record: record.data, fields, schema, hidden, forceNoHidden })
|
|
46
60
|
record = pick(record, ['data'])
|
|
47
61
|
if (!noHook) {
|
|
48
|
-
await runHook(`${this.
|
|
49
|
-
await runHook(`${this.
|
|
62
|
+
await runHook(`${this.ns}.${camelCase(name)}:afterRecordFindOne`, filter, options, record)
|
|
63
|
+
await runHook(`${this.ns}:afterRecordFindOne`, name, filter, options, record)
|
|
50
64
|
}
|
|
51
65
|
if (set && !noCache) await set({ model: name, filter, options, record })
|
|
52
66
|
if (!noFeatureHook) await execFeatureHook.call(this, 'afterFindOne', { schema, filter, options, record })
|
package/method/record/find.js
CHANGED
|
@@ -2,11 +2,74 @@ import resolveMethod from '../../lib/resolve-method.js'
|
|
|
2
2
|
import multiRelRows from '../../lib/multi-rel-rows.js'
|
|
3
3
|
import execFeatureHook from '../../lib/exec-feature-hook.js'
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {Object} TRecordFilter
|
|
7
|
+
* @see Dobo#recordFind
|
|
8
|
+
* @see Dobo#recordFindOne
|
|
9
|
+
* @see Dobo#recordFindAll
|
|
10
|
+
* @property {(string|Object)} [query={}] - Query definition. See {@tutorial query-language} for more
|
|
11
|
+
* @property {number} limit - Max number of records per page
|
|
12
|
+
* @property {number} page - Which page is the returned records currently at
|
|
13
|
+
* @property {number} skip - Records to skip
|
|
14
|
+
* @property {TRecordSort} sort - Sort order info
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @typedef {Object} TRecordFindResult
|
|
19
|
+
* @see Dobo#recordFind
|
|
20
|
+
* @see Dobo#recordFindAll
|
|
21
|
+
* @see Dobo#recordGet
|
|
22
|
+
* @property {Array.<Object>} data - Array of returned records
|
|
23
|
+
* @property {boolean} success - Whether operation is successfull or failed
|
|
24
|
+
* @property {number} page - Which page is the returned records currently at
|
|
25
|
+
* @property {number} limit - Max number of records per page
|
|
26
|
+
* @property {number} count - Total number of records returned
|
|
27
|
+
* @property {number} pages - Total number of pages returned
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @typedef {Object} TRecordFindOptions
|
|
32
|
+
* @see Dobo#recordFind
|
|
33
|
+
* @see Dobo#recordFindOne
|
|
34
|
+
* @see Dobo#recordFindAll
|
|
35
|
+
* @property {boolean} [dataOnly=true] - If ```true``` (default) returns array of records. Otherwise {@link TFindRecordResult}
|
|
36
|
+
* @property {boolean} [count=false] - If ```true``` and ```dataOnly``` is also ```true```, the total number of records found will be returned
|
|
37
|
+
* @property {boolean} [noCache=true] - If ```true``` (default), result set won't be cached. This will overwrite model's ```cacheable``` property. Only applicable if {@link https://github.com/ardhi/bajo-cache|bajo-cache} is loaded
|
|
38
|
+
* @property {boolean} [noHook=false] - If ```true```, no model's hook will be executed
|
|
39
|
+
* @property {boolean} [noFeatureHook=false] - If ```true```, no model's feature hook will be executed
|
|
40
|
+
* @property {boolean} [fields=[]] - If not empty, return only these fields EXCLUDING hidden fields
|
|
41
|
+
* @property {boolean} [hidden=[]] - Additional fields to hide, in addition the one set in model's schema
|
|
42
|
+
* @property {boolean} [forceNoHidden=false] - If ```true```, hidden fields will be ignored and ALL fields will be returned
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Find records by model's name and given filter
|
|
47
|
+
*
|
|
48
|
+
* Example: find records from model **CdbCountry** where its id is 'ID' or 'MY',
|
|
49
|
+
* sorted by ```name``` in ascending order and return only its ```id```, ```name``` and ```iso3```
|
|
50
|
+
* ```javascript
|
|
51
|
+
* const { recordFind } = this.app.dobo
|
|
52
|
+
* const query = { id: { $in: ['ID', 'MY'] } }
|
|
53
|
+
* const sort = { name: 1 }
|
|
54
|
+
* const fields = ['id', 'name', 'iso3']
|
|
55
|
+
* const result = await recordFind('CdbCountry', { query, sort }, { fields })
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* @method
|
|
59
|
+
* @memberof Dobo
|
|
60
|
+
* @async
|
|
61
|
+
* @instance
|
|
62
|
+
* @name recordFind
|
|
63
|
+
* @param {string} name - Model's name
|
|
64
|
+
* @param {Object} [filter={}] - Filter object
|
|
65
|
+
* @param {TRecordFindOptions} [options={}]
|
|
66
|
+
* @returns {(TRecordFindResult|Array.<Object>)} Return ```array``` of records if ```options.dataOnly``` is set. {@link TRecordFindResult} otherwise
|
|
67
|
+
*/
|
|
5
68
|
async function find (name, filter = {}, opts = {}) {
|
|
6
|
-
const { isSet } = this.lib.aneka
|
|
69
|
+
const { isSet } = this.app.lib.aneka
|
|
7
70
|
const { runHook } = this.app.bajo
|
|
8
71
|
const { get, set } = this.cache ?? {}
|
|
9
|
-
const { cloneDeep, camelCase, omit } = this.lib._
|
|
72
|
+
const { cloneDeep, camelCase, omit } = this.app.lib._
|
|
10
73
|
delete opts.records
|
|
11
74
|
const options = cloneDeep(omit(opts, ['req', 'reply']))
|
|
12
75
|
options.req = opts.req
|
|
@@ -22,8 +85,8 @@ async function find (name, filter = {}, opts = {}) {
|
|
|
22
85
|
if (options.queryHandler) filter.query = await options.queryHandler.call(opts.req ? this.app[opts.req.ns] : this, filter.query, opts.req)
|
|
23
86
|
filter.match = this.buildMatch({ input: filter.match, schema, options }) ?? {}
|
|
24
87
|
if (!noHook) {
|
|
25
|
-
await runHook(`${this.
|
|
26
|
-
await runHook(`${this.
|
|
88
|
+
await runHook(`${this.ns}:beforeRecordFind`, name, filter, options)
|
|
89
|
+
await runHook(`${this.ns}.${camelCase(name)}:beforeRecordFind`, filter, options)
|
|
27
90
|
}
|
|
28
91
|
if (!noFeatureHook) await execFeatureHook.call(this, 'beforeFind', { schema, filter, options })
|
|
29
92
|
if (get && !noCache && !options.records) {
|
|
@@ -41,8 +104,8 @@ async function find (name, filter = {}, opts = {}) {
|
|
|
41
104
|
records.data[idx] = await this.pickRecord({ record: records.data[idx], fields, schema, hidden, forceNoHidden })
|
|
42
105
|
}
|
|
43
106
|
if (!noHook) {
|
|
44
|
-
await runHook(`${this.
|
|
45
|
-
await runHook(`${this.
|
|
107
|
+
await runHook(`${this.ns}.${camelCase(name)}:afterRecordFind`, filter, options, records)
|
|
108
|
+
await runHook(`${this.ns}:afterRecordFind`, name, filter, options, records)
|
|
46
109
|
}
|
|
47
110
|
if (set && !noCache) await set({ model: name, filter, options, records })
|
|
48
111
|
if (!noFeatureHook) await execFeatureHook.call(this, 'afterFind', { schema, filter, options, records })
|
package/method/record/get.js
CHANGED
|
@@ -2,11 +2,53 @@ import resolveMethod from '../../lib/resolve-method.js'
|
|
|
2
2
|
import singleRelRows from '../../lib/single-rel-rows.js'
|
|
3
3
|
import execFeatureHook from '../../lib/exec-feature-hook.js'
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {Object} TRecordGetResult
|
|
7
|
+
* @see Dobo#recordGet
|
|
8
|
+
* @see Dobo#recordFindOne
|
|
9
|
+
* @property {Object} data - Returned record
|
|
10
|
+
* @property {boolean} success - Whether operation is successfull or failed
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {Object} TRecordGetOptions
|
|
15
|
+
* @see Dobo#recordGet
|
|
16
|
+
* @property {boolean} [dataOnly=true] - If ```true``` (default) returns array of records. Otherwise {@link TFindRecordResult}
|
|
17
|
+
* @property {boolean} [count=false] - If ```true``` and ```dataOnly``` is also ```true```, the total number of records found will be returned
|
|
18
|
+
* @property {boolean} [noCache=true] - If ```true``` (default), result set won't be cached. This will overwrite model's ```cacheable``` property. Only applicable if {@link https://github.com/ardhi/bajo-cache|bajo-cache} is loaded
|
|
19
|
+
* @property {boolean} [noHook=false] - If ```true```, no model's hook will be executed
|
|
20
|
+
* @property {boolean} [noFeatureHook=false] - If ```true```, no model's feature hook will be executed
|
|
21
|
+
* @property {boolean} [fields=[]] - If not empty, return only these fields EXCLUDING hidden fields
|
|
22
|
+
* @property {boolean} [hidden=[]] - Additional fields to hide, in addition the one set in model's schema
|
|
23
|
+
* @property {boolean} [forceNoHidden=false] - If ```true```, hidden fields will be ignored and ALL fields will be returned
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Get record by model's name and record ID
|
|
28
|
+
*
|
|
29
|
+
* Example:
|
|
30
|
+
* ```javascript
|
|
31
|
+
* const { recordGet } = this.app.dobo
|
|
32
|
+
* const fields = ['id', 'name', 'iso3']
|
|
33
|
+
* const result = await recordGet('CdbCountry', 'ID', { fields })
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @method
|
|
37
|
+
* @memberof Dobo
|
|
38
|
+
* @async
|
|
39
|
+
* @instance
|
|
40
|
+
* @name recordGet
|
|
41
|
+
* @param {string} name - Model's name
|
|
42
|
+
* @param {(string|number)} - Record's ID
|
|
43
|
+
* @param {TRecordGetOptions} [options={}]
|
|
44
|
+
* @returns {(TRecordGetResult|Object)} Return record's ```object``` if ```options.dataOnly``` is set. {@link TRecordGetResult} otherwise
|
|
45
|
+
*/
|
|
46
|
+
|
|
5
47
|
async function get (name, id, opts = {}) {
|
|
6
|
-
const { isSet } = this.lib.aneka
|
|
48
|
+
const { isSet } = this.app.lib.aneka
|
|
7
49
|
const { runHook } = this.app.bajo
|
|
8
50
|
const { get, set } = this.cache ?? {}
|
|
9
|
-
const { cloneDeep, camelCase, omit } = this.lib._
|
|
51
|
+
const { cloneDeep, camelCase, omit } = this.app.lib._
|
|
10
52
|
delete opts.record
|
|
11
53
|
const options = cloneDeep(omit(opts, ['req', 'reply']))
|
|
12
54
|
options.req = opts.req
|
|
@@ -19,8 +61,8 @@ async function get (name, id, opts = {}) {
|
|
|
19
61
|
id = this.sanitizeId(id, schema)
|
|
20
62
|
options.dataOnly = false
|
|
21
63
|
if (!noHook) {
|
|
22
|
-
await runHook(`${this.
|
|
23
|
-
await runHook(`${this.
|
|
64
|
+
await runHook(`${this.ns}:beforeRecordGet`, name, id, options)
|
|
65
|
+
await runHook(`${this.ns}.${camelCase(name)}:beforeRecordGet`, id, options)
|
|
24
66
|
}
|
|
25
67
|
if (!noFeatureHook) await execFeatureHook.call(this, 'beforeGet', { schema, id, options })
|
|
26
68
|
if (get && !noCache && !options.record) {
|
|
@@ -36,8 +78,8 @@ async function get (name, id, opts = {}) {
|
|
|
36
78
|
if (isSet(options.rels)) await singleRelRows.call(this, { schema, record: record.data, options })
|
|
37
79
|
record.data = await this.pickRecord({ record: record.data, fields, schema, hidden, forceNoHidden })
|
|
38
80
|
if (!noHook) {
|
|
39
|
-
await runHook(`${this.
|
|
40
|
-
await runHook(`${this.
|
|
81
|
+
await runHook(`${this.ns}.${camelCase(name)}:afterRecordGet`, id, options, record)
|
|
82
|
+
await runHook(`${this.ns}:afterRecordGet`, name, id, options, record)
|
|
41
83
|
}
|
|
42
84
|
if (set && !noCache) await set({ model: name, id, options, record })
|
|
43
85
|
if (!noFeatureHook) await execFeatureHook.call(this, 'afterGet', { schema, id, options, record })
|
package/method/record/remove.js
CHANGED
|
@@ -2,10 +2,41 @@ import resolveMethod from '../../lib/resolve-method.js'
|
|
|
2
2
|
import handleAttachmentUpload from '../../lib/handle-attachment-upload.js'
|
|
3
3
|
import execFeatureHook from '../../lib/exec-feature-hook.js'
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {Object} TRecordRemoveOptions
|
|
7
|
+
* @see Dobo#recordRemove
|
|
8
|
+
* @property {boolean} [dataOnly=true] - If ```true``` (default) returns deleted record. Otherwise {@link TRecordRemoveResult}
|
|
9
|
+
* @property {boolean} [noHook=false] - If ```true```, no model's hook will be executed
|
|
10
|
+
* @property {boolean} [noFeatureHook=false] - If ```true```, no model's feature hook will be executed
|
|
11
|
+
* @property {boolean} [noResult=false] - If ```true```, returns nothing
|
|
12
|
+
* @property {boolean} [fields=[]] - If not empty, return only these fields EXCLUDING hidden fields
|
|
13
|
+
* @property {boolean} [hidden=[]] - Additional fields to hide, in addition the one set in model's schema
|
|
14
|
+
* @property {boolean} [forceNoHidden=false] - If ```true```, hidden fields will be ignored and ALL fields will be returned
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Remove existing record by it's ID. All attachments bound to this record will also be removed forever.
|
|
19
|
+
*
|
|
20
|
+
* Example:
|
|
21
|
+
* ```javascript
|
|
22
|
+
* const { recordRemove } = this.app.dobo
|
|
23
|
+
* const result = await recordRemove('CdbCountry', 'ID')
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @method
|
|
27
|
+
* @memberof Dobo
|
|
28
|
+
* @async
|
|
29
|
+
* @instance
|
|
30
|
+
* @name recordRemove
|
|
31
|
+
* @param {string} name - Model's name
|
|
32
|
+
* @param {(string|number)} id - Record's ID
|
|
33
|
+
* @param {TRecordRemoveOptions} [options={}]
|
|
34
|
+
* @returns {(TRecordRemoveResult|Object)} Return the removed record if ```options.dataOnly``` is set. {@link TRecordRemoveResult} otherwise
|
|
35
|
+
*/
|
|
5
36
|
async function remove (name, id, opts = {}) {
|
|
6
37
|
const { runHook } = this.app.bajo
|
|
7
38
|
const { clearModel } = this.cache ?? {}
|
|
8
|
-
const { cloneDeep, camelCase, omit } = this.lib._
|
|
39
|
+
const { cloneDeep, camelCase, omit } = this.app.lib._
|
|
9
40
|
delete opts.record
|
|
10
41
|
const options = cloneDeep(omit(opts, ['req', 'reply']))
|
|
11
42
|
options.req = opts.req
|
|
@@ -17,8 +48,8 @@ async function remove (name, id, opts = {}) {
|
|
|
17
48
|
const { handler, schema, driver } = await resolveMethod.call(this, name, 'record-remove', options)
|
|
18
49
|
id = this.sanitizeId(id, schema)
|
|
19
50
|
if (!noHook) {
|
|
20
|
-
await runHook(`${this.
|
|
21
|
-
await runHook(`${this.
|
|
51
|
+
await runHook(`${this.ns}:beforeRecordRemove`, name, id, options)
|
|
52
|
+
await runHook(`${this.ns}.${camelCase(name)}:beforeRecordRemove`, id, options)
|
|
22
53
|
}
|
|
23
54
|
if (!noFeatureHook) await execFeatureHook.call(this, 'beforeRemove', { schema, id, options })
|
|
24
55
|
const record = options.record ?? (await handler.call(this.app[driver.ns], { schema, id, options }))
|
|
@@ -31,8 +62,8 @@ async function remove (name, id, opts = {}) {
|
|
|
31
62
|
if (noResult) return
|
|
32
63
|
record.oldData = options.record ? options.record.oldData : (await this.pickRecord({ record: record.oldData, fields, schema, hidden, forceNoHidden }))
|
|
33
64
|
if (!noHook) {
|
|
34
|
-
await runHook(`${this.
|
|
35
|
-
await runHook(`${this.
|
|
65
|
+
await runHook(`${this.ns}.${camelCase(name)}:afterRecordRemove`, id, options, record)
|
|
66
|
+
await runHook(`${this.ns}:afterRecordRemove`, name, id, options, record)
|
|
36
67
|
}
|
|
37
68
|
if (!noFeatureHook) await execFeatureHook.call(this, 'afterRemove', { schema, id, options, record })
|
|
38
69
|
return dataOnly ? record.oldData : record
|
package/method/record/update.js
CHANGED
|
@@ -5,11 +5,52 @@ import execValidation from '../../lib/exec-validation.js'
|
|
|
5
5
|
import execFeatureHook from '../../lib/exec-feature-hook.js'
|
|
6
6
|
import singleRelRows from '../../lib/single-rel-rows.js'
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {Object} TRecordUpdateOptions
|
|
10
|
+
* @see Dobo#recordUpdate
|
|
11
|
+
* @property {boolean} [dataOnly=true] - If ```true``` (default) returns record's object. Otherwise {@link TRecordUpdateResult}
|
|
12
|
+
* @property {boolean} [noHook=false] - If ```true```, no model's hook will be executed
|
|
13
|
+
* @property {boolean} [noFeatureHook=false] - If ```true```, no model's feature hook will be executed
|
|
14
|
+
* @property {boolean} [noValidation=false] - If ```true```, no validation of data payload performed
|
|
15
|
+
* @property {boolean} [noCheckUnique=false] - If ```true```, no unique validation for ID performed
|
|
16
|
+
* @property {boolean} [noSanitize=false] - If ```true```, accept data payload as is without sanitization
|
|
17
|
+
* @property {boolean} [noResult=false] - If ```true```, returns nothing
|
|
18
|
+
* @property {boolean} [truncateString=true] - If ```true``` (default), string is truncated to its schema's ```maxLemngth```
|
|
19
|
+
* @property {boolean} [partial=true] - If ```true``` (default), only updated values are saved. Otherwise replace all existing values with given payload
|
|
20
|
+
* @property {boolean} [fields=[]] - If not empty, return only these fields EXCLUDING hidden fields
|
|
21
|
+
* @property {boolean} [hidden=[]] - Additional fields to hide, in addition the one set in model's schema
|
|
22
|
+
* @property {boolean} [forceNoHidden=false] - If ```true```, hidden fields will be ignored and ALL fields will be returned
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Update a record by it's ID and body payload
|
|
27
|
+
*
|
|
28
|
+
* Example:
|
|
29
|
+
* ```javascript
|
|
30
|
+
* const { recordUpdate } = this.app.dobo
|
|
31
|
+
* const { body } = {
|
|
32
|
+
* name: 'Republic of Indonesia',
|
|
33
|
+
* phoneCode: '+62'
|
|
34
|
+
* }
|
|
35
|
+
* const result = await recordUpdate('CdbCountry', 'ID', body)
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @method
|
|
39
|
+
* @memberof Dobo
|
|
40
|
+
* @async
|
|
41
|
+
* @instance
|
|
42
|
+
* @name recordUpdate
|
|
43
|
+
* @param {string} name - Model's name
|
|
44
|
+
* @param {(string|number)} id - Record's ID
|
|
45
|
+
* @param {Object} body - Body payload
|
|
46
|
+
* @param {TRecordUpdateOptions} [options={}]
|
|
47
|
+
* @returns {(TRecordUpdateResult|Object)} Returns updated record if ```options.dataOnly``` is set. {@link TRecordUpdateResult} otherwise
|
|
48
|
+
*/
|
|
8
49
|
async function update (name, id, input, opts = {}) {
|
|
9
|
-
const { isSet } = this.lib.aneka
|
|
50
|
+
const { isSet } = this.app.lib.aneka
|
|
10
51
|
const { runHook } = this.app.bajo
|
|
11
52
|
const { clearModel } = this.cache ?? {}
|
|
12
|
-
const { forOwn, find, cloneDeep, camelCase, omit, get } = this.lib._
|
|
53
|
+
const { forOwn, find, cloneDeep, camelCase, omit, get } = this.app.lib._
|
|
13
54
|
delete opts.record
|
|
14
55
|
const options = cloneDeep(omit(opts, ['req', 'reply']))
|
|
15
56
|
options.req = opts.req
|
|
@@ -26,8 +67,8 @@ async function update (name, id, input, opts = {}) {
|
|
|
26
67
|
let body = noSanitize ? input : await this.sanitizeBody({ body: input, schema, partial, strict: true, extFields })
|
|
27
68
|
delete body.id
|
|
28
69
|
if (!noHook) {
|
|
29
|
-
await runHook(`${this.
|
|
30
|
-
await runHook(`${this.
|
|
70
|
+
await runHook(`${this.ns}:beforeRecordUpdate`, name, id, body, options)
|
|
71
|
+
await runHook(`${this.ns}.${camelCase(name)}:beforeRecordUpdate`, id, body, options)
|
|
31
72
|
}
|
|
32
73
|
if (!noValidation) body = await execValidation.call(this, { name, body, options, partial })
|
|
33
74
|
if (!noCheckUnique) await checkUnique.call(this, { schema, body, id })
|
|
@@ -53,8 +94,8 @@ async function update (name, id, input, opts = {}) {
|
|
|
53
94
|
record.oldData = await this.pickRecord({ record: record.oldData, fields, schema, hidden, forceNoHidden })
|
|
54
95
|
record.data = await this.pickRecord({ record: record.data, fields, schema, hidden, forceNoHidden })
|
|
55
96
|
if (!noHook) {
|
|
56
|
-
await runHook(`${this.
|
|
57
|
-
await runHook(`${this.
|
|
97
|
+
await runHook(`${this.ns}.${camelCase(name)}:afterRecordUpdate`, id, nbody, options, record)
|
|
98
|
+
await runHook(`${this.ns}:afterRecordUpdate`, name, id, nbody, options, record)
|
|
58
99
|
}
|
|
59
100
|
if (!noFeatureHook) await execFeatureHook.call(this, 'afterUpdate', { schema, body: nbody, record })
|
|
60
101
|
return dataOnly ? record.data : record
|
package/method/record/upsert.js
CHANGED
|
@@ -1,7 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Update a record by payload's ID. If no record is found by given ID, a new one will be created instead.
|
|
3
|
+
*
|
|
4
|
+
* Missing ID in payload always results a new record creation.
|
|
5
|
+
* ```
|
|
6
|
+
*
|
|
7
|
+
* @method
|
|
8
|
+
* @memberof Dobo
|
|
9
|
+
* @async
|
|
10
|
+
* @instance
|
|
11
|
+
* @name recordUpsert
|
|
12
|
+
* @param {string} name - Model's name
|
|
13
|
+
* @param {Object} body - Body payload
|
|
14
|
+
* @param {TRecordUpsertOptions} [options={}]
|
|
15
|
+
* @returns {(TRecordUpdateResult|TRecordCreateResult|Object)} Returns updated/newly created record if ```options.dataOnly``` is set. {@link TRecordUpdateResult} or {@link TRecordCreateResult} otherwise
|
|
16
|
+
*/
|
|
1
17
|
async function upsert (name, input, opts = {}) {
|
|
2
18
|
const { generateId } = this.app.bajo
|
|
3
|
-
const { find } = this.lib._
|
|
4
|
-
const { cloneDeep, omit, merge } = this.lib._
|
|
19
|
+
const { find } = this.app.lib._
|
|
20
|
+
const { cloneDeep, omit, merge } = this.app.lib._
|
|
5
21
|
const { query, omitOnUpdate = [], omitOnCreate = [] } = opts
|
|
6
22
|
const options = cloneDeep(omit(opts, ['req', 'reply', 'query', 'omitOnUpdate', 'omitOnCreate']))
|
|
7
23
|
options.req = opts.req
|
package/method/sanitize/body.js
CHANGED
|
@@ -1,8 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sanitize payload body against schema
|
|
3
|
+
*
|
|
4
|
+
* @method
|
|
5
|
+
* @memberof Dobo
|
|
6
|
+
* @async
|
|
7
|
+
* @param {Object} [options={}]
|
|
8
|
+
* @param {Object} [options.body={}]
|
|
9
|
+
* @param {Object} [options.schema={}]
|
|
10
|
+
* @param {boolean} [options.partial=false]
|
|
11
|
+
* @param {boolean} [options.strict=false]
|
|
12
|
+
* @param {Array} [options.extFields=[]]
|
|
13
|
+
* @returns {Object}
|
|
14
|
+
*/
|
|
15
|
+
|
|
1
16
|
async function sanitizeBody ({ body = {}, schema = {}, partial, strict, extFields = [] }) {
|
|
2
|
-
const { isSet } = this.lib.aneka
|
|
3
|
-
const { dayjs } = this.lib
|
|
17
|
+
const { isSet } = this.app.lib.aneka
|
|
18
|
+
const { dayjs } = this.app.lib
|
|
4
19
|
const { callHandler } = this.app.bajo
|
|
5
|
-
const { has, isString, isNumber, concat, isNaN } = this.lib._
|
|
20
|
+
const { has, isString, isNumber, concat, isNaN } = this.app.lib._
|
|
6
21
|
const result = {}
|
|
7
22
|
for (const p of concat(schema.properties, extFields)) {
|
|
8
23
|
if (partial && !has(body, p.name)) continue
|
package/method/sanitize/date.js
CHANGED
|
@@ -1,14 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Sanitize value as a date/time value. Parse/format string using {@link https://day.js.org/docs/en/display/format|dayjs format}
|
|
3
|
+
*
|
|
4
|
+
* @method
|
|
5
|
+
* @memberof Dobo
|
|
6
|
+
* @param {(number|string)} value - Value to sanitize
|
|
7
|
+
* @param {Object} [options={}] - Options object
|
|
8
|
+
* @param {boolean} [options.silent=true] - If ```true``` (default) and value isn't valid, returns empty
|
|
9
|
+
* @param {string} [options.inputFormat] - If provided, parse value using this option
|
|
10
|
+
* @param {string} [options.outputFormat] - If not provided or ```native```, returns Javascript Date. Otherwise returns formatted date/time string
|
|
11
|
+
* @returns {(string|Date)}
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
function sanitizeDate (value, { inputFormat, outputFormat, silent = true } = {}) {
|
|
15
|
+
const { dayjs } = this.app.lib
|
|
3
16
|
if (value === 0) return null
|
|
4
|
-
if (!
|
|
5
|
-
const dt = dayjs(value,
|
|
17
|
+
if (!outputFormat) outputFormat = inputFormat
|
|
18
|
+
const dt = dayjs(value, inputFormat)
|
|
6
19
|
if (!dt.isValid()) {
|
|
7
|
-
if (silent) return
|
|
20
|
+
if (silent) return null
|
|
8
21
|
throw this.error('invalidDate')
|
|
9
22
|
}
|
|
10
|
-
if (
|
|
11
|
-
return dt.format(
|
|
23
|
+
if (outputFormat === 'native' || !outputFormat) return dt.toDate()
|
|
24
|
+
return dt.format(outputFormat)
|
|
12
25
|
}
|
|
13
26
|
|
|
14
27
|
export default sanitizeDate
|
package/method/sanitize/id.js
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sanitize id according it's schema
|
|
3
|
+
*
|
|
4
|
+
* @method
|
|
5
|
+
* @memberof Dobo
|
|
6
|
+
* @param {(number|string)} id
|
|
7
|
+
* @param {Object} schema
|
|
8
|
+
* @returns {(number|string)}
|
|
9
|
+
*/
|
|
10
|
+
|
|
1
11
|
function sanitizeId (id, schema) {
|
|
2
12
|
const prop = schema.properties.find(p => p.name === 'id')
|
|
3
13
|
if (prop.type === 'integer') id = parseInt(id)
|
package/method/stat/aggregate.js
CHANGED
|
@@ -7,15 +7,15 @@ async function aggregate (name, filter = {}, options = {}) {
|
|
|
7
7
|
await this.modelExists(name, true)
|
|
8
8
|
const { handler, schema, driver } = await resolveMethod.call(this, name, 'stat-aggregate', options)
|
|
9
9
|
if (!noHook) {
|
|
10
|
-
await runHook(`${this.
|
|
11
|
-
await runHook(`${this.
|
|
10
|
+
await runHook(`${this.ns}:beforeStatAggregate`, name, aggregate, filter, options)
|
|
11
|
+
await runHook(`${this.ns}.${name}:beforeStatAggregate`, aggregate, filter, options)
|
|
12
12
|
}
|
|
13
13
|
filter.query = this.buildQuery({ filter, schema, options }) ?? {}
|
|
14
14
|
filter.match = this.buildMatch({ input: filter.match, schema, options }) ?? {}
|
|
15
15
|
const rec = await handler.call(this.app[driver.ns], { schema, filter, options })
|
|
16
16
|
if (!noHook) {
|
|
17
|
-
await runHook(`${this.
|
|
18
|
-
await runHook(`${this.
|
|
17
|
+
await runHook(`${this.ns}.${name}:afterStatAggregate`, aggregate, filter, options, rec)
|
|
18
|
+
await runHook(`${this.ns}:afterStatAggregate`, name, aggregate, filter, options, rec)
|
|
19
19
|
}
|
|
20
20
|
return dataOnly ? rec.data : rec
|
|
21
21
|
}
|
package/method/stat/histogram.js
CHANGED
|
@@ -12,13 +12,13 @@ async function histogram (name, filter = {}, options = {}) {
|
|
|
12
12
|
filter.query = this.buildQuery({ filter, schema, options }) ?? {}
|
|
13
13
|
filter.match = this.buildMatch({ input: filter.match, schema, options }) ?? {}
|
|
14
14
|
if (!noHook) {
|
|
15
|
-
await runHook(`${this.
|
|
16
|
-
await runHook(`${this.
|
|
15
|
+
await runHook(`${this.ns}:beforeStatHistogram`, name, type, filter, options)
|
|
16
|
+
await runHook(`${this.ns}.${name}:beforeStatHistogram`, type, filter, options)
|
|
17
17
|
}
|
|
18
18
|
const rec = await handler.call(this.app[driver.ns], { schema, type, filter, options })
|
|
19
19
|
if (!noHook) {
|
|
20
|
-
await runHook(`${this.
|
|
21
|
-
await runHook(`${this.
|
|
20
|
+
await runHook(`${this.ns}.${name}:afterStatHistogram`, type, filter, options, rec)
|
|
21
|
+
await runHook(`${this.ns}:afterStatHistogram`, name, type, filter, options, rec)
|
|
22
22
|
}
|
|
23
23
|
return dataOnly ? rec.data : rec
|
|
24
24
|
}
|