dobo 2.0.1 → 2.2.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.
Files changed (194) hide show
  1. package/.github/FUNDING.yml +0 -0
  2. package/.github/workflows/repo-lockdown.yml +0 -0
  3. package/.jsdoc.conf.json +0 -0
  4. package/LICENSE +0 -0
  5. package/README.md +2 -2
  6. package/docs/Dobo.html +0 -0
  7. package/docs/data/search.json +0 -0
  8. package/docs/fonts/Inconsolata-Regular.ttf +0 -0
  9. package/docs/fonts/OpenSans-Regular.ttf +0 -0
  10. package/docs/fonts/WorkSans-Bold.ttf +0 -0
  11. package/docs/global.html +0 -0
  12. package/docs/index.html +0 -0
  13. package/docs/index.js.html +0 -0
  14. package/docs/lib_collect-connections.js.html +0 -0
  15. package/docs/lib_collect-drivers.js.html +0 -0
  16. package/docs/lib_collect-features.js.html +0 -0
  17. package/docs/lib_collect-schemas.js.html +0 -0
  18. package/docs/lib_index.js.html +0 -0
  19. package/docs/method_model_create.js.html +0 -0
  20. package/docs/method_model_drop.js.html +0 -0
  21. package/docs/method_model_exists.js.html +0 -0
  22. package/docs/method_record_count.js.html +0 -0
  23. package/docs/method_record_create.js.html +0 -0
  24. package/docs/method_record_find-all.js.html +0 -0
  25. package/docs/method_record_find-one.js.html +0 -0
  26. package/docs/method_record_find.js.html +0 -0
  27. package/docs/method_record_get.js.html +0 -0
  28. package/docs/method_record_remove.js.html +0 -0
  29. package/docs/method_record_update.js.html +0 -0
  30. package/docs/method_record_upsert.js.html +0 -0
  31. package/docs/method_sanitize_body.js.html +0 -0
  32. package/docs/method_sanitize_date.js.html +0 -0
  33. package/docs/method_sanitize_id.js.html +0 -0
  34. package/docs/method_validate.js.html +0 -0
  35. package/docs/module-Lib.html +0 -0
  36. package/docs/scripts/core.js +476 -477
  37. package/docs/scripts/core.min.js +0 -0
  38. package/docs/scripts/resize.js +36 -36
  39. package/docs/scripts/search.js +105 -105
  40. package/docs/scripts/search.min.js +0 -0
  41. package/docs/scripts/third-party/Apache-License-2.0.txt +0 -0
  42. package/docs/scripts/third-party/fuse.js +1 -1
  43. package/docs/scripts/third-party/hljs-line-num-original.js +282 -285
  44. package/docs/scripts/third-party/hljs-line-num.js +1 -1
  45. package/docs/scripts/third-party/hljs-original.js +1195 -1202
  46. package/docs/scripts/third-party/hljs.js +1 -1
  47. package/docs/scripts/third-party/popper.js +1 -1
  48. package/docs/scripts/third-party/tippy.js +1 -1
  49. package/docs/scripts/third-party/tocbot.js +508 -509
  50. package/docs/scripts/third-party/tocbot.min.js +0 -0
  51. package/docs/static/bitcoin.jpeg +0 -0
  52. package/docs/static/home.md +0 -0
  53. package/docs/static/logo-ecosystem.png +0 -0
  54. package/docs/static/logo.png +0 -0
  55. package/docs/styles/clean-jsdoc-theme-base.css +0 -0
  56. package/docs/styles/clean-jsdoc-theme-dark.css +0 -0
  57. package/docs/styles/clean-jsdoc-theme-light.css +0 -0
  58. package/docs/styles/clean-jsdoc-theme-scrollbar.css +0 -0
  59. package/docs/styles/clean-jsdoc-theme-without-scrollbar.min.css +0 -0
  60. package/docs/styles/clean-jsdoc-theme.min.css +0 -0
  61. package/extend/bajo/intl/en-US.json +66 -28
  62. package/extend/bajo/intl/id.json +55 -27
  63. package/extend/bajoCli/applet/clear-record.js +22 -0
  64. package/extend/bajoCli/applet/connection.js +0 -0
  65. package/extend/bajoCli/applet/count-record.js +27 -0
  66. package/extend/bajoCli/applet/create-aggregate.js +33 -0
  67. package/extend/bajoCli/applet/create-histogram.js +33 -0
  68. package/extend/bajoCli/applet/create-record.js +39 -0
  69. package/extend/bajoCli/applet/find-record.js +27 -0
  70. package/extend/bajoCli/applet/get-record.js +27 -0
  71. package/extend/bajoCli/applet/lib/post-process.js +10 -17
  72. package/extend/bajoCli/applet/model.js +22 -0
  73. package/extend/bajoCli/applet/rebuild-model.js +91 -0
  74. package/extend/bajoCli/applet/remove-record.js +27 -0
  75. package/extend/bajoCli/applet/update-record.js +44 -0
  76. package/extend/bajoCli/applet.js +0 -0
  77. package/extend/dobo/driver/memory.js +170 -0
  78. package/extend/dobo/feature/created-at.js +9 -7
  79. package/extend/dobo/feature/dt.js +0 -0
  80. package/extend/dobo/feature/immutable.js +30 -0
  81. package/extend/dobo/feature/int-id.js +0 -0
  82. package/extend/dobo/feature/removed-at.js +32 -54
  83. package/extend/dobo/feature/updated-at.js +14 -12
  84. package/extend/waibuMpa/route/attachment/@model/@id/@field/@file.js +2 -6
  85. package/extend/waibuStatic/virtual.json +0 -0
  86. package/index.js +284 -371
  87. package/lib/collect-connections.js +49 -21
  88. package/lib/collect-drivers.js +19 -33
  89. package/lib/collect-features.js +24 -17
  90. package/lib/collect-models.js +321 -0
  91. package/lib/factory/action.js +161 -0
  92. package/lib/factory/connection.js +62 -0
  93. package/lib/factory/driver.js +372 -0
  94. package/lib/factory/feature.js +33 -0
  95. package/lib/factory/model/_util.js +402 -0
  96. package/lib/factory/model/build.js +15 -0
  97. package/lib/factory/model/clear-record.js +17 -0
  98. package/lib/factory/model/count-record.js +17 -0
  99. package/lib/factory/model/create-aggregate.js +17 -0
  100. package/lib/factory/model/create-attachment.js +29 -0
  101. package/lib/factory/model/create-histogram.js +17 -0
  102. package/lib/factory/model/create-record.js +35 -0
  103. package/lib/factory/model/drop.js +15 -0
  104. package/lib/factory/model/exists.js +21 -0
  105. package/lib/factory/model/find-all-record.js +71 -0
  106. package/lib/factory/model/find-attachment.js +29 -0
  107. package/lib/factory/model/find-one-record.js +19 -0
  108. package/{method/record/find.js → lib/factory/model/find-record.js} +103 -115
  109. package/lib/factory/model/get-attachment.js +15 -0
  110. package/lib/factory/model/get-record.js +79 -0
  111. package/lib/factory/model/list-attachment.js +37 -0
  112. package/lib/{add-fixtures.js → factory/model/load-fixtures.js} +69 -67
  113. package/lib/factory/model/remove-attachment.js +15 -0
  114. package/lib/factory/model/remove-record.js +59 -0
  115. package/lib/factory/model/sanitize-body.js +56 -0
  116. package/lib/factory/model/sanitize-id.js +7 -0
  117. package/lib/factory/model/sanitize-record.js +26 -0
  118. package/lib/factory/model/update-attachment.js +9 -0
  119. package/lib/factory/model/update-record.js +81 -0
  120. package/lib/factory/model/upsert-record.js +95 -0
  121. package/{method → lib/factory/model}/validate.js +38 -52
  122. package/lib/factory/model.js +150 -0
  123. package/lib/index.js +0 -0
  124. package/package.json +8 -4
  125. package/wiki/APPLETS.md +0 -0
  126. package/wiki/CHANGES.md +50 -0
  127. package/wiki/CONFIG.md +0 -0
  128. package/wiki/CONTRIBUTING.md +0 -0
  129. package/wiki/DEV-GUIDE.md +0 -0
  130. package/wiki/ECOSYSTEM.md +0 -0
  131. package/wiki/GETTING-STARTED.md +10 -10
  132. package/wiki/QUERY-LANGUAGE.md +0 -0
  133. package/wiki/USER-GUIDE.md +0 -0
  134. package/extend/bajoCli/applet/model-clear.js +0 -11
  135. package/extend/bajoCli/applet/model-rebuild.js +0 -101
  136. package/extend/bajoCli/applet/record-create.js +0 -43
  137. package/extend/bajoCli/applet/record-find.js +0 -28
  138. package/extend/bajoCli/applet/record-get.js +0 -24
  139. package/extend/bajoCli/applet/record-remove.js +0 -24
  140. package/extend/bajoCli/applet/record-update.js +0 -47
  141. package/extend/bajoCli/applet/schema.js +0 -22
  142. package/extend/bajoCli/applet/stat-count.js +0 -24
  143. package/lib/build-bulk-action.js +0 -12
  144. package/lib/check-unique.js +0 -39
  145. package/lib/collect-schemas.js +0 -91
  146. package/lib/exec-feature-hook.js +0 -13
  147. package/lib/exec-validation.js +0 -21
  148. package/lib/generic-prop-sanitizer.js +0 -32
  149. package/lib/handle-attachment-upload.js +0 -16
  150. package/lib/mem-db/conn-sanitizer.js +0 -8
  151. package/lib/mem-db/instantiate.js +0 -41
  152. package/lib/mem-db/method/model/clear.js +0 -6
  153. package/lib/mem-db/method/model/create.js +0 -5
  154. package/lib/mem-db/method/model/drop.js +0 -5
  155. package/lib/mem-db/method/model/exists.js +0 -5
  156. package/lib/mem-db/method/record/create.js +0 -12
  157. package/lib/mem-db/method/record/find.js +0 -20
  158. package/lib/mem-db/method/record/get.js +0 -9
  159. package/lib/mem-db/method/record/remove.js +0 -13
  160. package/lib/mem-db/method/record/update.js +0 -15
  161. package/lib/mem-db/method/stat/count.js +0 -11
  162. package/lib/mem-db/start.js +0 -25
  163. package/lib/merge-attachment-info.js +0 -16
  164. package/lib/multi-rel-rows.js +0 -42
  165. package/lib/resolve-method.js +0 -16
  166. package/lib/sanitize-schema.js +0 -198
  167. package/lib/single-rel-rows.js +0 -38
  168. package/method/attachment/copy-uploaded.js +0 -34
  169. package/method/attachment/create.js +0 -29
  170. package/method/attachment/find.js +0 -27
  171. package/method/attachment/get-path.js +0 -12
  172. package/method/attachment/get.js +0 -12
  173. package/method/attachment/pre-check.js +0 -9
  174. package/method/attachment/remove.js +0 -11
  175. package/method/attachment/update.js +0 -7
  176. package/method/bulk/create.js +0 -46
  177. package/method/model/clear.js +0 -22
  178. package/method/model/create.js +0 -32
  179. package/method/model/drop.js +0 -31
  180. package/method/model/exists.js +0 -37
  181. package/method/record/clear.js +0 -24
  182. package/method/record/count.js +0 -66
  183. package/method/record/create.js +0 -111
  184. package/method/record/find-all.js +0 -41
  185. package/method/record/find-one.js +0 -70
  186. package/method/record/get.js +0 -89
  187. package/method/record/remove.js +0 -72
  188. package/method/record/update.js +0 -104
  189. package/method/record/upsert.js +0 -51
  190. package/method/sanitize/body.js +0 -85
  191. package/method/sanitize/date.js +0 -27
  192. package/method/sanitize/id.js +0 -17
  193. package/method/stat/aggregate.js +0 -23
  194. package/method/stat/histogram.js +0 -26
@@ -1,72 +0,0 @@
1
- import resolveMethod from '../../lib/resolve-method.js'
2
- import handleAttachmentUpload from '../../lib/handle-attachment-upload.js'
3
- import execFeatureHook from '../../lib/exec-feature-hook.js'
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
- */
36
- async function remove (name, id, opts = {}) {
37
- const { runHook } = this.app.bajo
38
- const { clearModel } = this.cache ?? {}
39
- const { cloneDeep, camelCase, omit } = this.app.lib._
40
- delete opts.record
41
- const options = cloneDeep(omit(opts, ['req', 'reply']))
42
- options.req = opts.req
43
- options.reply = opts.reply
44
- options.dataOnly = options.dataOnly ?? true
45
- const { fields, dataOnly, noHook, noResult, noFeatureHook, hidden, forceNoHidden } = options
46
- options.dataOnly = false
47
- await this.modelExists(name, true)
48
- const { handler, schema, driver } = await resolveMethod.call(this, name, 'record-remove', options)
49
- id = this.sanitizeId(id, schema)
50
- if (!noHook) {
51
- await runHook(`${this.ns}:beforeRecordRemove`, name, id, options)
52
- await runHook(`${this.ns}.${camelCase(name)}:beforeRecordRemove`, id, options)
53
- }
54
- if (!noFeatureHook) await execFeatureHook.call(this, 'beforeRemove', { schema, id, options })
55
- const record = options.record ?? (await handler.call(this.app[driver.ns], { schema, id, options }))
56
- delete options.record
57
- if (options.req) {
58
- if (options.req.file) await handleAttachmentUpload.call(this, { name: schema.name, id, options, action: 'remove' })
59
- if (options.req.flash && !options.noFlash) options.req.flash('notify', options.req.t('recordRemoved'))
60
- }
61
- if (clearModel) await clearModel({ model: name, id, options, record })
62
- if (noResult) return
63
- record.oldData = options.record ? options.record.oldData : (await this.pickRecord({ record: record.oldData, fields, schema, hidden, forceNoHidden }))
64
- if (!noHook) {
65
- await runHook(`${this.ns}.${camelCase(name)}:afterRecordRemove`, id, options, record)
66
- await runHook(`${this.ns}:afterRecordRemove`, name, id, options, record)
67
- }
68
- if (!noFeatureHook) await execFeatureHook.call(this, 'afterRemove', { schema, id, options, record })
69
- return dataOnly ? record.oldData : record
70
- }
71
-
72
- export default remove
@@ -1,104 +0,0 @@
1
- import resolveMethod from '../../lib/resolve-method.js'
2
- import checkUnique from '../../lib/check-unique.js'
3
- import handleAttachmentUpload from '../../lib/handle-attachment-upload.js'
4
- import execValidation from '../../lib/exec-validation.js'
5
- import execFeatureHook from '../../lib/exec-feature-hook.js'
6
- import singleRelRows from '../../lib/single-rel-rows.js'
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
- */
49
- async function update (name, id, input, opts = {}) {
50
- const { isSet } = this.app.lib.aneka
51
- const { runHook } = this.app.bajo
52
- const { clearModel } = this.cache ?? {}
53
- const { forOwn, find, cloneDeep, camelCase, omit, get } = this.app.lib._
54
- delete opts.record
55
- const options = cloneDeep(omit(opts, ['req', 'reply']))
56
- options.req = opts.req
57
- options.reply = opts.reply
58
- options.dataOnly = options.dataOnly ?? true
59
- input = cloneDeep(input)
60
- const { fields, dataOnly, noHook, noValidation, noCheckUnique, noFeatureHook, noResult, noSanitize, partial = true, hidden, forceNoHidden } = options
61
- options.dataOnly = true
62
- options.truncateString = options.truncateString ?? true
63
- await this.modelExists(name, true)
64
- const { handler, schema, driver } = await resolveMethod.call(this, name, 'record-update', options)
65
- id = this.sanitizeId(id, schema)
66
- const extFields = get(options, 'validation.extFields', [])
67
- let body = noSanitize ? input : await this.sanitizeBody({ body: input, schema, partial, strict: true, extFields })
68
- delete body.id
69
- if (!noHook) {
70
- await runHook(`${this.ns}:beforeRecordUpdate`, name, id, body, options)
71
- await runHook(`${this.ns}.${camelCase(name)}:beforeRecordUpdate`, id, body, options)
72
- }
73
- if (!noValidation) body = await execValidation.call(this, { name, body, options, partial })
74
- if (!noCheckUnique) await checkUnique.call(this, { schema, body, id })
75
- const nbody = {}
76
- forOwn(body, (v, k) => {
77
- if (v === undefined) return undefined
78
- const prop = find(schema.properties, { name: k })
79
- if (!prop) return undefined
80
- if (options.truncateString && isSet(v) && ['string', 'text'].includes(prop.type)) v = v.slice(0, prop.maxLength)
81
- nbody[k] = v
82
- })
83
- delete nbody.id
84
- if (!noFeatureHook) await execFeatureHook.call(this, 'beforeUpdate', { schema, body: nbody, options })
85
- const record = options.record ?? (await handler.call(this.app[driver.ns], { schema, id, body: nbody, options }))
86
- delete options.record
87
- if (isSet(options.rels)) await singleRelRows.call(this, { schema, record: record.data, options })
88
- if (options.req) {
89
- if (options.req.file) await handleAttachmentUpload.call(this, { name: schema.name, id, body, options, action: 'update' })
90
- if (options.req.flash && !options.noFlash) options.req.flash('notify', options.req.t('recordUpdated'))
91
- }
92
- if (clearModel) await clearModel({ model: name, id, body: nbody, options, record })
93
- if (noResult) return
94
- record.oldData = await this.pickRecord({ record: record.oldData, fields, schema, hidden, forceNoHidden })
95
- record.data = await this.pickRecord({ record: record.data, fields, schema, hidden, forceNoHidden })
96
- if (!noHook) {
97
- await runHook(`${this.ns}.${camelCase(name)}:afterRecordUpdate`, id, nbody, options, record)
98
- await runHook(`${this.ns}:afterRecordUpdate`, name, id, nbody, options, record)
99
- }
100
- if (!noFeatureHook) await execFeatureHook.call(this, 'afterUpdate', { schema, body: nbody, record })
101
- return dataOnly ? record.data : record
102
- }
103
-
104
- export default update
@@ -1,51 +0,0 @@
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
- */
17
- async function upsert (name, input, opts = {}) {
18
- const { generateId } = this.app.bajo
19
- const { find } = this.app.lib._
20
- const { cloneDeep, omit, merge } = this.app.lib._
21
- const { query, omitOnUpdate = [], omitOnCreate = [] } = opts
22
- const options = cloneDeep(omit(opts, ['req', 'reply', 'query', 'omitOnUpdate', 'omitOnCreate']))
23
- options.req = opts.req
24
- options.reply = opts.reply
25
- options.dataOnly = options.dataOnly ?? true
26
- await this.modelExists(name, true)
27
- const { schema } = this.getInfo(name)
28
- const idField = find(schema.properties, { name: 'id' })
29
- let id
30
- if (idField.type === 'string') id = input.id ?? generateId()
31
- else if (idField.type === 'integer') id = input.id ?? generateId('int')
32
- id = this.sanitizeId(id, schema)
33
- let old
34
- let body
35
- const o = { dataOnly: true, noHook: true, noCache: true, hidden: options.hidden, forceNoHidden: options.forceNoHidden }
36
- if (query) {
37
- old = await this.recordFindOne(name, { query }, o)
38
- } else {
39
- o.thrownNotFound = false
40
- old = await this.recordGet(name, id, o)
41
- }
42
- if (old) {
43
- body = merge(omit(old, ['id', 'createdAt', 'updatedAt', 'removedAt']), omit(input, omitOnUpdate))
44
- return await this.recordUpdate(name, old.id, body, options)
45
- }
46
- if (!query) input.id = id
47
- body = omit(input, omitOnCreate)
48
- return await this.recordCreate(name, body, options)
49
- }
50
-
51
- export default upsert
@@ -1,85 +0,0 @@
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
-
16
- async function sanitizeBody ({ body = {}, schema = {}, partial, strict, extFields = [] }) {
17
- const { isSet } = this.app.lib.aneka
18
- const { dayjs } = this.app.lib
19
- const { callHandler } = this.app.bajo
20
- const { has, isString, isNumber, concat, isNaN } = this.app.lib._
21
- const result = {}
22
- for (const p of concat(schema.properties, extFields)) {
23
- if (partial && !has(body, p.name)) continue
24
- if (['object', 'array'].includes(p.type)) {
25
- if (isString(body[p.name])) {
26
- try {
27
- result[p.name] = JSON.parse(body[p.name])
28
- } catch (err) {
29
- result[p.name] = null
30
- }
31
- } else {
32
- try {
33
- result[p.name] = JSON.parse(JSON.stringify(body[p.name]))
34
- } catch (err) {}
35
- }
36
- } else result[p.name] = body[p.name]
37
- if (isSet(body[p.name])) {
38
- if (p.type === 'boolean') result[p.name] = result[p.name] === null ? null : (['true', true].includes(result[p.name]))
39
- if (['float', 'double'].includes(p.type)) {
40
- if (isNumber(body[p.name])) result[p.name] = body[p.name]
41
- else if (strict) {
42
- result[p.name] = Number(body[p.name])
43
- } else {
44
- result[p.name] = parseFloat(body[p.name]) || null
45
- }
46
- }
47
- if (['integer', 'smallint'].includes(p.type)) {
48
- if (isNumber(body[p.name])) result[p.name] = body[p.name]
49
- else if (strict) {
50
- result[p.name] = Number(body[p.name])
51
- } else {
52
- result[p.name] = parseInt(body[p.name]) || null
53
- }
54
- }
55
- if (p.type === 'timestamp') {
56
- if (!isNumber(body[p.name])) result[p.name] = -1
57
- else {
58
- const dt = dayjs.unix(body[p.name])
59
- result[p.name] = dt.isValid() ? dt.unix() : -1
60
- }
61
- } else {
62
- for (const t of ['datetime', 'date|YYYY-MM-DD', 'time|HH:mm:ss']) {
63
- const [type, input] = t.split('|')
64
- if (p.type === type) result[p.name] = this.sanitizeDate(body[p.name], { input })
65
- }
66
- }
67
- if (['string', 'text'].includes(p.type)) result[p.name] = body[p.name] + ''
68
- } else {
69
- if (isSet(p.default)) {
70
- result[p.name] = p.default
71
- if (isString(p.default) && p.default.startsWith('handler:')) {
72
- const [, ...args] = p.default.split(':')
73
- if (args.length > 0) result[p.name] = await callHandler(args.join(':'))
74
- } else {
75
- if (['float', 'double'].includes(p.type)) result[p.name] = parseFloat(result[p.name])
76
- if (['integer', 'smallint'].includes(p.type)) result[p.name] = parseInt(result[p.name])
77
- if (isNaN(result[p.name])) result[p.name] = null
78
- }
79
- }
80
- }
81
- }
82
- return result
83
- }
84
-
85
- export default sanitizeBody
@@ -1,27 +0,0 @@
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
16
- if (value === 0) return null
17
- if (!outputFormat) outputFormat = inputFormat
18
- const dt = dayjs(value, inputFormat)
19
- if (!dt.isValid()) {
20
- if (silent) return null
21
- throw this.error('invalidDate')
22
- }
23
- if (outputFormat === 'native' || !outputFormat) return dt.toDate()
24
- return dt.format(outputFormat)
25
- }
26
-
27
- export default sanitizeDate
@@ -1,17 +0,0 @@
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
-
11
- function sanitizeId (id, schema) {
12
- const prop = schema.properties.find(p => p.name === 'id')
13
- if (prop.type === 'integer') id = parseInt(id)
14
- return id
15
- }
16
-
17
- export default sanitizeId
@@ -1,23 +0,0 @@
1
- import resolveMethod from '../../lib/resolve-method.js'
2
-
3
- async function aggregate (name, filter = {}, options = {}) {
4
- const { runHook } = this.app.bajo
5
- const { dataOnly = true, noHook, aggregate } = options
6
- options.dataOnly = false
7
- await this.modelExists(name, true)
8
- const { handler, schema, driver } = await resolveMethod.call(this, name, 'stat-aggregate', options)
9
- if (!noHook) {
10
- await runHook(`${this.ns}:beforeStatAggregate`, name, aggregate, filter, options)
11
- await runHook(`${this.ns}.${name}:beforeStatAggregate`, aggregate, filter, options)
12
- }
13
- filter.query = this.buildQuery({ filter, schema, options }) ?? {}
14
- filter.match = this.buildMatch({ input: filter.match, schema, options }) ?? {}
15
- const rec = await handler.call(this.app[driver.ns], { schema, filter, options })
16
- if (!noHook) {
17
- await runHook(`${this.ns}.${name}:afterStatAggregate`, aggregate, filter, options, rec)
18
- await runHook(`${this.ns}:afterStatAggregate`, name, aggregate, filter, options, rec)
19
- }
20
- return dataOnly ? rec.data : rec
21
- }
22
-
23
- export default aggregate
@@ -1,26 +0,0 @@
1
- import resolveMethod from '../../lib/resolve-method.js'
2
-
3
- const types = ['daily', 'monthly', 'yearly']
4
-
5
- async function histogram (name, filter = {}, options = {}) {
6
- const { runHook, join } = this.app.bajo
7
- const { dataOnly = true, noHook, type } = options
8
- options.dataOnly = false
9
- if (!types.includes(type)) throw this.error('histogramTypeMusBe%s', join(types))
10
- await this.modelExists(name, true)
11
- const { handler, schema, driver } = await resolveMethod.call(this, name, 'stat-histogram', options)
12
- filter.query = this.buildQuery({ filter, schema, options }) ?? {}
13
- filter.match = this.buildMatch({ input: filter.match, schema, options }) ?? {}
14
- if (!noHook) {
15
- await runHook(`${this.ns}:beforeStatHistogram`, name, type, filter, options)
16
- await runHook(`${this.ns}.${name}:beforeStatHistogram`, type, filter, options)
17
- }
18
- const rec = await handler.call(this.app[driver.ns], { schema, type, filter, options })
19
- if (!noHook) {
20
- await runHook(`${this.ns}.${name}:afterStatHistogram`, type, filter, options, rec)
21
- await runHook(`${this.ns}:afterStatHistogram`, name, type, filter, options, rec)
22
- }
23
- return dataOnly ? rec.data : rec
24
- }
25
-
26
- export default histogram