dobo 2.0.1 → 2.2.0

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 +291 -366
  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 +319 -0
  91. package/lib/factory/action.js +161 -0
  92. package/lib/factory/connection.js +62 -0
  93. package/lib/factory/driver.js +358 -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 +62 -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 +46 -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,12 +0,0 @@
1
- async function get (name, id, field, file, options = {}) {
2
- name = this.attachmentPreCheck(name)
3
- if (!name) return
4
- const { find } = this.app.lib._
5
- const all = await this.attachmentFind(name, id, options)
6
- if (field === 'null') field = null
7
- const data = find(all, { field, file })
8
- if (!data) throw this.error('notFound', { statusCode: 404 })
9
- return data
10
- }
11
-
12
- export default get
@@ -1,9 +0,0 @@
1
- function preCheck (name) {
2
- const { pascalCase } = this.app.lib.aneka
3
- name = pascalCase(name)
4
- const schema = this.getSchema(name)
5
- if (!schema.attachment) return false
6
- return name
7
- }
8
-
9
- export default preCheck
@@ -1,11 +0,0 @@
1
- async function remove (name, id, field, file, options = {}) {
2
- const { fs } = this.app.lib
3
- name = this.attachmentPreCheck(name)
4
- if (!name) return
5
- const path = await this.attachmentGetPath(name, id, field, file)
6
- const { req } = options
7
- await fs.remove(path)
8
- if (req && req.flash) req.flash('notify', req.t('attachmentRemoved'))
9
- }
10
-
11
- export default remove
@@ -1,7 +0,0 @@
1
- import create from './create.js'
2
-
3
- async function update (name, id, options = {}) {
4
- return create.call(this, name, id, options)
5
- }
6
-
7
- export default update
@@ -1,46 +0,0 @@
1
- import buildBulkAction from '../../lib/build-bulk-action.js'
2
- import execValidation from '../../lib/exec-validation.js'
3
- import execFeatureHook from '../../lib/exec-feature-hook.js'
4
-
5
- async function create (name, inputs, options) {
6
- const { isSet } = this.app.lib.aneka
7
- const { generateId, runHook } = this.app.bajo
8
- const { clearModel } = this.cache ?? {}
9
- const { find } = this.app.lib._
10
- options.dataOnly = options.dataOnly ?? true
11
- options.truncateString = options.truncateString ?? true
12
- const { noHook, noValidation } = options
13
- await this.modelExists(name, true)
14
- const { handler, schema } = await buildBulkAction.call(this, name, 'create', options)
15
- const idField = find(schema.properties, { name: 'id' })
16
- const bodies = [...inputs]
17
- for (let b of bodies) {
18
- b.id = b.id ?? generateId(idField.type === 'integer' ? 'int' : undefined)
19
- b = await this.sanitizeBody({ body: b, schema, strict: true })
20
- if (!noValidation) b = await execValidation.call(this, { noHook, name, b, options })
21
- }
22
- if (!noHook) {
23
- await runHook(`${this.ns}:beforeBulkCreate`, name, bodies, options)
24
- await runHook(`${this.ns}.${name}:beforeBulkCreate`, bodies, options)
25
- }
26
- for (const idx in bodies) {
27
- await execFeatureHook.call(this, 'beforeCreate', { schema, body: bodies[idx] })
28
- // TODO: check unique?
29
- for (const k in bodies[idx]) {
30
- if (bodies[idx][k] === undefined) continue
31
- const prop = find(schema.properties, { name: k })
32
- if (options.truncateString && isSet(bodies[idx][k]) && prop && ['string', 'text'].includes(prop.type)) bodies[idx][k] = bodies[idx][k].slice(0, prop.maxLength)
33
- }
34
- }
35
- await handler.call(this, { schema, bodies, options })
36
- for (const idx in bodies) {
37
- await execFeatureHook.call(this, 'afterCreate', { schema, body: bodies[idx] })
38
- }
39
- if (!noHook) {
40
- await runHook(`${this.ns}.${name}:afterBulkCreate`, bodies, options)
41
- await runHook(`${this.ns}:afterBulkCreate`, name, bodies, options)
42
- }
43
- if (clearModel) await clearModel({ model: name })
44
- }
45
-
46
- export default create
@@ -1,22 +0,0 @@
1
- import resolveMethod from '../../lib/resolve-method.js'
2
-
3
- async function clear (name, options = {}) {
4
- const { runHook } = this.app.bajo
5
- const { camelCase } = this.app.lib._
6
-
7
- await this.modelExists(name, true)
8
- const { noHook } = options
9
- const { handler, schema, driver } = await resolveMethod.call(this, name, 'model-clear', options)
10
- if (!noHook) {
11
- await runHook(`${this.ns}:beforeModelClear`, schema, options)
12
- await runHook(`${this.ns}.${camelCase(name)}:beforeModelClear`, options)
13
- }
14
- const resp = await handler.call(this.app[driver.ns], { schema, options })
15
- if (!noHook) {
16
- await runHook(`${this.ns}.${camelCase(name)}:afterModelClear`, options, resp)
17
- await runHook(`${this.ns}:afterModelClear`, schema, options, resp)
18
- }
19
- return resp
20
- }
21
-
22
- export default clear
@@ -1,32 +0,0 @@
1
- import resolveMethod from '../../lib/resolve-method.js'
2
-
3
- /**
4
- * Create a new model:
5
- * - read corresponding schema
6
- * - attempt to create table/database/collection accordingly
7
- *
8
- * @method
9
- * @memberof Dobo
10
- * @async
11
- * @instance
12
- * @name modelCreate
13
- * @param {string} name - Model's name
14
- * @param {Object} [options={}] - Options object
15
- */
16
- async function create (name, options = {}) {
17
- const { runHook } = this.app.bajo
18
- const { camelCase } = this.app.lib._
19
-
20
- const { handler, schema, driver } = await resolveMethod.call(this, name, 'model-create', options)
21
- if (!options.noHook) {
22
- await runHook(`${this.ns}:beforeModelCreate`, schema, options)
23
- await runHook(`${this.ns}.${camelCase(name)}:beforeModelCreate`, options)
24
- }
25
- await handler.call(this.app[driver.ns], { schema, options })
26
- if (!options.noHook) {
27
- await runHook(`${this.ns}.${camelCase(name)}:afterModelCreate`, options)
28
- await runHook(`${this.ns}:afterModelCreate`, schema, options)
29
- }
30
- }
31
-
32
- export default create
@@ -1,31 +0,0 @@
1
- import resolveMethod from '../../lib/resolve-method.js'
2
-
3
- /**
4
- * Drop database model
5
- *
6
- * @method
7
- * @memberof Dobo
8
- * @async
9
- * @instance
10
- * @name modelDrop
11
- * @param {string} name - Model's name
12
- * @param {Object} [options={}] - Options object
13
- */
14
-
15
- async function drop (name, options = {}) {
16
- const { runHook } = this.app.bajo
17
- const { camelCase } = this.app.lib._
18
- const { handler, schema, driver } = await resolveMethod.call(this, name, 'model-drop', options)
19
-
20
- if (!options.noHook) {
21
- await runHook(`${this.ns}:beforeModelDrop`, schema, options)
22
- await runHook(`${this.ns}.${camelCase(name)}:beforeModelDrop`, options)
23
- }
24
- await handler.call(this.app[driver.ns], { schema, options })
25
- if (!options.noHook) {
26
- await runHook(`${this.ns}.${camelCase(name)}:afterModelDrop`, options)
27
- await runHook(`${this.ns}:afterModelDrop`, schema, options)
28
- }
29
- }
30
-
31
- export default drop
@@ -1,37 +0,0 @@
1
- import resolveMethod from '../../lib/resolve-method.js'
2
-
3
- const cache = {}
4
-
5
- /**
6
- * Check if model exists already
7
- *
8
- * @method
9
- * @memberof Dobo
10
- * @async
11
- * @instance
12
- * @name modelExists
13
- * @param {string} name - Model's name
14
- * @param {boolean} [thrown=false] - If ```true``` throw error if not exists instead of just silent
15
- * @param {Object} [options={}] - Options object
16
- * @returns {boolean}
17
- */
18
- async function exists (name, thrown, options = {}) {
19
- if (cache[name]) return cache[name]
20
- const { runHook } = this.app.bajo
21
- const { camelCase } = this.app.lib._
22
- const { handler, schema, driver } = await resolveMethod.call(this, name, 'model-exists', options)
23
- if (!options.noHook) {
24
- await runHook(`${this.ns}:beforeModelExists`, schema, options)
25
- await runHook(`${this.ns}.${camelCase(name)}:beforeModelExists`, options)
26
- }
27
- const exist = await handler.call(this.app[driver.ns], { schema, options })
28
- if (!options.noHook) {
29
- await runHook(`${this.ns}.${camelCase(name)}:afterModelExists`, exist, options)
30
- await runHook(`${this.ns}:afterModelExists`, schema, exist, options)
31
- }
32
- if (!exist && thrown) throw this.error('modelNotExists%s', name)
33
- cache[name] = exist
34
- return exist
35
- }
36
-
37
- export default exists
@@ -1,24 +0,0 @@
1
- import resolveMethod from '../../lib/resolve-method.js'
2
-
3
- async function clear (name, opts = {}) {
4
- const { runHook } = this.app.bajo
5
- await this.modelExists(name, true)
6
- const { cloneDeep, camelCase, omit } = this.app.lib._
7
- const options = cloneDeep(omit(opts, ['req', 'reply']))
8
- options.req = opts.req
9
- options.reply = opts.reply
10
- const { noHook } = options
11
- const { handler, schema, driver } = await resolveMethod.call(this, name, 'record-clear', options)
12
- if (!noHook) {
13
- await runHook(`${this.ns}:beforeRecordClear`, name, options)
14
- await runHook(`${this.ns}.${camelCase(name)}:beforeRecordClear`, options)
15
- }
16
- const resp = await handler.call(this.app[driver.ns], { schema, options })
17
- if (!noHook) {
18
- await runHook(`${this.ns}.${camelCase(name)}:afterRecordClear`, options, resp)
19
- await runHook(`${this.ns}:afterRecordClear`, name, options, resp)
20
- }
21
- return resp
22
- }
23
-
24
- export default clear
@@ -1,66 +0,0 @@
1
- import resolveMethod from '../../lib/resolve-method.js'
2
- import execFeatureHook from '../../lib/exec-feature-hook.js'
3
-
4
- /**
5
- * @typedef {Object} TRecordCountOptions
6
- * @see Dobo#recordCount
7
- * @property {boolean} [dataOnly=true] - If ```true``` (default) returns array of records. Otherwise {@link TFindRecordResult}
8
- * @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
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
- */
12
-
13
- /**
14
- * Return the number of records found by given filter
15
- *
16
- * @method
17
- * @memberof Dobo
18
- * @async
19
- * @instance
20
- * @name recordCount
21
- * @param {string} name - Model's name
22
- * @param {TRecordFilter} [filter={}] - Filter object
23
- * @param {TRecordCountOptions} [options={}]
24
- * @returns {(TRecordCountResult|number)} Return ```number``` of records if ```options.dataOnly``` is set. {@link TRecordCountResult} otherwise
25
- */
26
- async function count (name, filter = {}, opts = {}) {
27
- const { runHook } = this.app.bajo
28
- const { get, set } = this.cache ?? {}
29
- const { cloneDeep, camelCase, omit } = this.app.lib._
30
- delete opts.record
31
- const options = cloneDeep(omit(opts, ['req', 'reply']))
32
- options.req = opts.req
33
- options.reply = opts.reply
34
- options.dataOnly = options.dataOnly ?? true
35
- let { dataOnly, noHook, noCache, noFeatureHook } = options
36
- options.dataOnly = false
37
- await this.modelExists(name, true)
38
- const { handler, schema, driver } = await resolveMethod.call(this, name, 'record-count', options)
39
- if (!schema.cacheable) noCache = true
40
- filter.query = this.buildQuery({ filter, schema, options }) ?? {}
41
- if (options.queryHandler) filter.query = await options.queryHandler.call(opts.req ? this.app[opts.req.ns] : this, filter.query, opts.req)
42
- filter.match = this.buildMatch({ input: filter.match, schema, options }) ?? {}
43
- if (!noHook) {
44
- await runHook(`${this.ns}:beforeRecordCount`, name, filter, options)
45
- await runHook(`${this.ns}.${camelCase(name)}:beforeRecordCount`, filter, options)
46
- }
47
- if (!noFeatureHook) await execFeatureHook.call(this, 'beforeCount', { schema, filter, options })
48
- if (get && !noCache && !options.record) {
49
- const cachedResult = await get({ model: name, filter, options })
50
- if (cachedResult) {
51
- cachedResult.cached = true
52
- return dataOnly ? cachedResult.data : cachedResult
53
- }
54
- }
55
- const record = options.record ?? (await handler.call(this.app[driver.ns], { schema, filter, options }))
56
- delete options.record
57
- if (!noHook) {
58
- await runHook(`${this.ns}.${camelCase(name)}:afterRecordCount`, filter, options, record)
59
- await runHook(`${this.ns}:afterRecordCount`, name, filter, options, record)
60
- }
61
- if (set && !noCache) await set({ model: name, filter, options, record })
62
- if (!noFeatureHook) await execFeatureHook.call(this, 'afterCount', { schema, filter, options, record })
63
- return dataOnly ? record.data : record
64
- }
65
-
66
- export default count
@@ -1,111 +0,0 @@
1
- import crypto from 'crypto'
2
- import resolveMethod from '../../lib/resolve-method.js'
3
- import checkUnique from '../../lib/check-unique.js'
4
- import handleAttachmentUpload from '../../lib/handle-attachment-upload.js'
5
- import execValidation from '../../lib/exec-validation.js'
6
- import execFeatureHook from '../../lib/exec-feature-hook.js'
7
- import singleRelRows from '../../lib/single-rel-rows.js'
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
- */
49
- async function create (name, input, opts = {}) {
50
- const { generateId, runHook } = this.app.bajo
51
- const { isSet } = this.app.lib.aneka
52
- const { clearModel } = this.cache ?? {}
53
- const { find, forOwn, cloneDeep, camelCase, omit, get, pick } = 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, hidden, forceNoHidden } = options
61
- options.truncateString = options.truncateString ?? true
62
- options.dataOnly = false
63
- await this.modelExists(name, true)
64
- const { handler, schema, driver } = await resolveMethod.call(this, name, 'record-create', options)
65
- const idField = find(schema.properties, { name: 'id' })
66
- const extFields = get(options, 'validation.extFields', [])
67
- let body = noSanitize ? cloneDeep(input) : await this.sanitizeBody({ body: input, schema, extFields, strict: true })
68
- if (!noHook) {
69
- await runHook(`${this.ns}:beforeRecordCreate`, name, body, options)
70
- await runHook(`${this.ns}.${camelCase(name)}:beforeRecordCreate`, body, options)
71
- }
72
- if (!isSet(body.id)) {
73
- if (idField.type === 'string') {
74
- if (!options.checksumId) body.id = generateId()
75
- else {
76
- if (options.checksumId === true) options.checksumId = Object.keys(body)
77
- const checksum = pick(body, options.checksumId)
78
- body.id = crypto.createHash('md5').update(JSON.stringify(checksum)).digest('hex')
79
- }
80
- } else if (['integer', 'smallint'].includes(idField.type) && !idField.autoInc) input.id = generateId('int')
81
- }
82
- if (!noValidation) body = await execValidation.call(this, { name, body, options })
83
- if (isSet(body.id) && !noCheckUnique) await checkUnique.call(this, { schema, body })
84
- const nbody = {}
85
- forOwn(body, (v, k) => {
86
- if (v === undefined) return undefined
87
- const prop = find(schema.properties, { name: k })
88
- if (!prop) return undefined
89
- if (options.truncateString && isSet(v) && ['string', 'text'].includes(prop.type)) v = v.slice(0, prop.maxLength)
90
- nbody[k] = v
91
- })
92
- if (!noFeatureHook) await execFeatureHook.call(this, 'beforeCreate', { schema, body: nbody, options })
93
- const record = options.record ?? (await handler.call(this.app[driver.ns], { schema, body: nbody, options }))
94
- delete options.record
95
- if (isSet(options.rels)) await singleRelRows.call(this, { schema, record: record.data, options })
96
- if (options.req) {
97
- if (options.req.file) await handleAttachmentUpload.call(this, { name: schema.name, id: body.id, body, options, action: 'create' })
98
- if (options.req.flash && !options.noFlash) options.req.flash('notify', options.req.t('recordCreated'))
99
- }
100
- if (clearModel) await clearModel({ model: name, body: nbody, options, record })
101
- if (noResult) return
102
- record.data = await this.pickRecord({ record: record.data, fields, schema, hidden, forceNoHidden })
103
- if (!noHook) {
104
- await runHook(`${this.ns}.${camelCase(name)}:afterRecordCreate`, nbody, options, record)
105
- await runHook(`${this.ns}:afterRecordCreate`, name, nbody, options, record)
106
- }
107
- if (!noFeatureHook) await execFeatureHook.call(this, 'afterCreate', { schema, body: nbody, options, record })
108
- return dataOnly ? record.data : record
109
- }
110
-
111
- export default create
@@ -1,41 +0,0 @@
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
- */
17
- async function findAll (name, filter = {}, options = {}) {
18
- const { maxLimit, hardLimit } = this.config.default.filter
19
- filter.page = 1
20
- filter.limit = maxLimit
21
- options.dataOnly = true
22
- const match = filter.match
23
- const all = []
24
- let count = 0
25
- for (;;) {
26
- filter.match = match
27
- const results = await this.recordFind(name, filter, options)
28
- if (results.length === 0) break
29
- if (count + results.length > hardLimit) {
30
- const sliced = results.slice(0, hardLimit - count)
31
- all.push(...sliced)
32
- break
33
- }
34
- all.push(...results)
35
- count = count + results.length
36
- filter.page++
37
- }
38
- return all
39
- }
40
-
41
- export default findAll
@@ -1,70 +0,0 @@
1
- import resolveMethod from '../../lib/resolve-method.js'
2
- import singleRelRows from '../../lib/single-rel-rows.js'
3
- import execFeatureHook from '../../lib/exec-feature-hook.js'
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
- */
19
- async function findOne (name, filter = {}, opts = {}) {
20
- const { isSet } = this.app.lib.aneka
21
- const { runHook } = this.app.bajo
22
- const { get, set } = this.cache ?? {}
23
- const { cloneDeep, camelCase, omit, pick } = this.app.lib._
24
- delete opts.record
25
- const options = cloneDeep(omit(opts, ['req', 'reply']))
26
- options.req = opts.req
27
- options.reply = opts.reply
28
- options.dataOnly = options.dataOnly ?? true
29
- let { fields, dataOnly, noHook, noCache, noFeatureHook, hidden, forceNoHidden } = options
30
- options.count = false
31
- options.dataOnly = false
32
- await this.modelExists(name, true)
33
- filter.limit = 1
34
- filter.page = 1
35
- const { handler, schema, driver } = await resolveMethod.call(this, name, 'record-find', options)
36
- if (!schema.cacheable) noCache = true
37
- filter.query = this.buildQuery({ filter, schema, options }) ?? {}
38
- if (options.queryHandler) filter.query = await options.queryHandler.call(opts.req ? this.app[opts.req.ns] : this, filter.query, opts.req)
39
- filter.match = this.buildMatch({ input: filter.match, schema, options }) ?? {}
40
- if (!noHook) {
41
- await runHook(`${this.ns}:beforeRecordFindOne`, name, filter, options)
42
- await runHook(`${this.ns}.${camelCase(name)}:beforeRecordFindOne`, filter, options)
43
- }
44
- if (!noFeatureHook) await execFeatureHook.call(this, 'beforeFindOne', { schema, filter, options })
45
- if (get && !noCache && !options.record) {
46
- const cachedResult = await get({ model: name, filter, options })
47
- if (cachedResult) {
48
- cachedResult.cached = true
49
- return dataOnly ? cachedResult.data : cachedResult
50
- }
51
- }
52
- filter.limit = 1
53
- filter.page = 1
54
- let record = options.record ?? (await handler.call(this.app[driver.ns], { schema, filter, options }))
55
- delete options.record
56
- record.data = record.data[0]
57
-
58
- if (isSet(options.rels)) await singleRelRows.call(this, { schema, record: record.data, options })
59
- record.data = await this.pickRecord({ record: record.data, fields, schema, hidden, forceNoHidden })
60
- record = pick(record, ['data'])
61
- if (!noHook) {
62
- await runHook(`${this.ns}.${camelCase(name)}:afterRecordFindOne`, filter, options, record)
63
- await runHook(`${this.ns}:afterRecordFindOne`, name, filter, options, record)
64
- }
65
- if (set && !noCache) await set({ model: name, filter, options, record })
66
- if (!noFeatureHook) await execFeatureHook.call(this, 'afterFindOne', { schema, filter, options, record })
67
- return dataOnly ? record.data : record
68
- }
69
-
70
- export default findOne
@@ -1,89 +0,0 @@
1
- import resolveMethod from '../../lib/resolve-method.js'
2
- import singleRelRows from '../../lib/single-rel-rows.js'
3
- import execFeatureHook from '../../lib/exec-feature-hook.js'
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
-
47
- async function get (name, id, opts = {}) {
48
- const { isSet } = this.app.lib.aneka
49
- const { runHook } = this.app.bajo
50
- const { get, set } = this.cache ?? {}
51
- const { cloneDeep, camelCase, omit } = this.app.lib._
52
- delete opts.record
53
- const options = cloneDeep(omit(opts, ['req', 'reply']))
54
- options.req = opts.req
55
- options.reply = opts.reply
56
- options.dataOnly = options.dataOnly ?? true
57
- let { fields, dataOnly, noHook, noCache, noFeatureHook, hidden = [], forceNoHidden } = options
58
- await this.modelExists(name, true)
59
- const { handler, schema, driver } = await resolveMethod.call(this, name, 'record-get', options)
60
- if (!schema.cacheable) noCache = true
61
- id = this.sanitizeId(id, schema)
62
- options.dataOnly = false
63
- if (!noHook) {
64
- await runHook(`${this.ns}:beforeRecordGet`, name, id, options)
65
- await runHook(`${this.ns}.${camelCase(name)}:beforeRecordGet`, id, options)
66
- }
67
- if (!noFeatureHook) await execFeatureHook.call(this, 'beforeGet', { schema, id, options })
68
- if (get && !noCache && !options.record) {
69
- const cachedResult = await get({ model: name, id, options })
70
- if (cachedResult) {
71
- cachedResult.cached = true
72
- if (!noFeatureHook) await execFeatureHook.call(this, 'afterGet', { schema, id, options, record: cachedResult })
73
- return dataOnly ? cachedResult.data : cachedResult
74
- }
75
- }
76
- const record = options.record ?? (await handler.call(this.app[driver.ns], { schema, id, options }))
77
- delete options.record
78
- if (isSet(options.rels)) await singleRelRows.call(this, { schema, record: record.data, options })
79
- record.data = await this.pickRecord({ record: record.data, fields, schema, hidden, forceNoHidden })
80
- if (!noHook) {
81
- await runHook(`${this.ns}.${camelCase(name)}:afterRecordGet`, id, options, record)
82
- await runHook(`${this.ns}:afterRecordGet`, name, id, options, record)
83
- }
84
- if (set && !noCache) await set({ model: name, id, options, record })
85
- if (!noFeatureHook) await execFeatureHook.call(this, 'afterGet', { schema, id, options, record })
86
- return dataOnly ? record.data : record
87
- }
88
-
89
- export default get