dobo 2.38.0 → 2.39.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 (160) hide show
  1. package/.bootorder +0 -0
  2. package/.github/FUNDING.yml +0 -0
  3. package/.github/workflows/repo-lockdown.yml +0 -0
  4. package/.jsdoc.conf.json +0 -0
  5. package/LICENSE +0 -0
  6. package/README.md +0 -0
  7. package/docs/Dobo.html +0 -0
  8. package/docs/DoboAction.html +0 -0
  9. package/docs/DoboAdapter.html +0 -0
  10. package/docs/DoboConnection.html +0 -0
  11. package/docs/DoboFeature.html +0 -0
  12. package/docs/DoboModel.html +0 -0
  13. package/docs/data/search.json +0 -0
  14. package/docs/extend_bajo_hook-docs.js.html +0 -0
  15. package/docs/external-Tools.html +0 -0
  16. package/docs/fonts/Inconsolata-Regular.ttf +0 -0
  17. package/docs/fonts/OpenSans-Regular.ttf +0 -0
  18. package/docs/fonts/WorkSans-Bold.ttf +0 -0
  19. package/docs/global.html +0 -0
  20. package/docs/index.html +0 -0
  21. package/docs/index.js.html +0 -0
  22. package/docs/lib_factory_action.js.html +0 -0
  23. package/docs/lib_factory_adapter.js.html +0 -0
  24. package/docs/lib_factory_connection.js.html +0 -0
  25. package/docs/lib_factory_feature.js.html +0 -0
  26. package/docs/lib_factory_model.js.html +0 -0
  27. package/docs/lib_factory_model_build.js.html +0 -0
  28. package/docs/lib_factory_model_clear-record.js.html +0 -0
  29. package/docs/lib_factory_model_count-record.js.html +0 -0
  30. package/docs/lib_factory_model_create-record.js.html +0 -0
  31. package/docs/lib_factory_model_drop.js.html +0 -0
  32. package/docs/lib_factory_model_exists.js.html +0 -0
  33. package/docs/lib_factory_model_find-all-record.js.html +0 -0
  34. package/docs/lib_factory_model_find-one-record.js.html +0 -0
  35. package/docs/lib_factory_model_find-record.js.html +0 -0
  36. package/docs/lib_factory_model_get-record.js.html +0 -0
  37. package/docs/lib_factory_model_helper.js.html +0 -0
  38. package/docs/lib_factory_model_remove-record.js.html +0 -0
  39. package/docs/lib_factory_model_sanitize-body.js.html +0 -0
  40. package/docs/lib_factory_model_sanitize-fixture.js.html +0 -0
  41. package/docs/lib_factory_model_sanitize-record.js.html +0 -0
  42. package/docs/lib_factory_model_update-record.js.html +0 -0
  43. package/docs/lib_factory_model_upsert-record.js.html +0 -0
  44. package/docs/lib_factory_model_validate.js.html +0 -0
  45. package/docs/lib_helper.js.html +0 -0
  46. package/docs/module-Helper.html +0 -0
  47. package/docs/module-Helper_Model.html +0 -0
  48. package/docs/module-Hook.html +0 -0
  49. package/docs/scripts/core.js +0 -0
  50. package/docs/scripts/core.min.js +0 -0
  51. package/docs/scripts/resize.js +0 -0
  52. package/docs/scripts/search.js +0 -0
  53. package/docs/scripts/search.min.js +5 -5
  54. package/docs/scripts/third-party/Apache-License-2.0.txt +0 -0
  55. package/docs/scripts/third-party/fuse.js +0 -0
  56. package/docs/scripts/third-party/hljs-line-num-original.js +0 -0
  57. package/docs/scripts/third-party/hljs-line-num.js +0 -0
  58. package/docs/scripts/third-party/hljs-original.js +0 -0
  59. package/docs/scripts/third-party/hljs.js +0 -0
  60. package/docs/scripts/third-party/popper.js +0 -0
  61. package/docs/scripts/third-party/tippy.js +0 -0
  62. package/docs/scripts/third-party/tocbot.js +0 -0
  63. package/docs/scripts/third-party/tocbot.min.js +0 -0
  64. package/docs/static/bitcoin.jpeg +0 -0
  65. package/docs/static/home.md +0 -0
  66. package/docs/static/logo-ecosystem.png +0 -0
  67. package/docs/static/logo.png +0 -0
  68. package/docs/styles/clean-jsdoc-theme-base.css +0 -0
  69. package/docs/styles/clean-jsdoc-theme-dark.css +0 -0
  70. package/docs/styles/clean-jsdoc-theme-light.css +0 -0
  71. package/docs/styles/clean-jsdoc-theme-scrollbar.css +0 -0
  72. package/docs/styles/clean-jsdoc-theme-without-scrollbar.min.css +0 -0
  73. package/docs/styles/clean-jsdoc-theme.min.css +0 -0
  74. package/extend/bajo/hook.js +236 -236
  75. package/extend/bajo/intl/en-US.json +0 -0
  76. package/extend/bajo/intl/id.json +0 -0
  77. package/extend/bajoCli/applet/aggregate.js +0 -0
  78. package/extend/bajoCli/applet/clear-record.js +0 -0
  79. package/extend/bajoCli/applet/connection.js +0 -0
  80. package/extend/bajoCli/applet/count-record.js +0 -0
  81. package/extend/bajoCli/applet/create-record.js +0 -0
  82. package/extend/bajoCli/applet/find-record.js +0 -0
  83. package/extend/bajoCli/applet/get-record.js +0 -0
  84. package/extend/bajoCli/applet/histogram.js +0 -0
  85. package/extend/bajoCli/applet/lib/post-process.js +0 -0
  86. package/extend/bajoCli/applet/model.js +0 -0
  87. package/extend/bajoCli/applet/rebuild-model.js +0 -0
  88. package/extend/bajoCli/applet/remove-record.js +0 -0
  89. package/extend/bajoCli/applet/update-record.js +0 -0
  90. package/extend/bajoCli/applet.js +0 -0
  91. package/extend/dobo/adapter/memory.js +175 -175
  92. package/extend/dobo/feature/created-at.js +0 -0
  93. package/extend/dobo/feature/dt.js +0 -0
  94. package/extend/dobo/feature/image.js +0 -0
  95. package/extend/dobo/feature/immutable.js +0 -0
  96. package/extend/dobo/feature/removed-at.js +0 -0
  97. package/extend/dobo/feature/unique.js +0 -0
  98. package/extend/dobo/feature/updated-at.js +0 -0
  99. package/extend/waibuMpa/route/attachment/@model/@id/@field/@file.js +0 -0
  100. package/extend/waibuStatic/virtual.json +0 -0
  101. package/index.js +0 -0
  102. package/lib/factory/action.js +287 -287
  103. package/lib/factory/adapter.js +1055 -1055
  104. package/lib/factory/connection.js +109 -109
  105. package/lib/factory/feature.js +52 -52
  106. package/lib/factory/model/aggregate.js +23 -23
  107. package/lib/factory/model/build.js +25 -25
  108. package/lib/factory/model/bulk-create-record.js +36 -36
  109. package/lib/factory/model/clear-record.js +30 -30
  110. package/lib/factory/model/count-record.js +42 -42
  111. package/lib/factory/model/create-attachment.js +36 -36
  112. package/lib/factory/model/create-record.js +49 -49
  113. package/lib/factory/model/drop.js +26 -26
  114. package/lib/factory/model/exists.js +26 -26
  115. package/lib/factory/model/find-all-record.js +112 -112
  116. package/lib/factory/model/find-attachment.js +28 -28
  117. package/lib/factory/model/find-one-record.js +38 -38
  118. package/lib/factory/model/find-record.js +82 -82
  119. package/lib/factory/model/get-attachment.js +15 -15
  120. package/lib/factory/model/get-record.js +52 -52
  121. package/lib/factory/model/helper.js +575 -576
  122. package/lib/factory/model/histogram.js +23 -23
  123. package/lib/factory/model/list-attachment.js +40 -40
  124. package/lib/factory/model/load-fixtures.js +63 -63
  125. package/lib/factory/model/remove-attachment.js +24 -24
  126. package/lib/factory/model/remove-record.js +44 -44
  127. package/lib/factory/model/sanitize-body.js +57 -57
  128. package/lib/factory/model/sanitize-fixture.js +56 -56
  129. package/lib/factory/model/sanitize-record.js +80 -80
  130. package/lib/factory/model/transaction.js +15 -15
  131. package/lib/factory/model/update-attachment.js +9 -9
  132. package/lib/factory/model/update-record.js +58 -58
  133. package/lib/factory/model/upsert-record.js +71 -71
  134. package/lib/factory/model/validate.js +0 -0
  135. package/lib/factory/model.js +503 -503
  136. package/lib/helper.js +463 -463
  137. package/package.json +1 -1
  138. package/test/e2e/_run.js +12 -12
  139. package/test/e2e/e2e-factory-process.test.js +25 -25
  140. package/test/e2e/e2e-model-adapter-process.test.js +52 -52
  141. package/test/integration/aspect-01-start-connections.test.js +33 -33
  142. package/test/integration/aspect-02-query-regex.test.js +34 -34
  143. package/test/integration/aspect-03-model-adapter-flow.test.js +88 -88
  144. package/test/unit/_stub.js +244 -244
  145. package/test/unit/dobo-action.test.js +58 -58
  146. package/test/unit/dobo-connection.test.js +37 -37
  147. package/test/unit/dobo-core.test.js +136 -136
  148. package/test/unit/dobo-feature.test.js +25 -25
  149. package/test/unit/dobo-model.test.js +115 -115
  150. package/test/unit/helper-sanitize-all.test.js +40 -40
  151. package/test/unit/helper-sanitize-ref.test.js +64 -64
  152. package/wiki/APPLETS.md +0 -0
  153. package/wiki/CHANGES.md +477 -469
  154. package/wiki/CONFIG.md +0 -0
  155. package/wiki/CONTRIBUTING.md +0 -0
  156. package/wiki/DEV-GUIDE.md +0 -0
  157. package/wiki/ECOSYSTEM.md +0 -0
  158. package/wiki/GETTING-STARTED.md +0 -0
  159. package/wiki/QUERY-LANGUAGE.md +0 -0
  160. package/wiki/USER-GUIDE.md +0 -0
@@ -1,23 +1,23 @@
1
- import { getFilterAndOptions, execHook, execModelHook, execDynHook } from './helper.js'
2
- const action = 'histogram'
3
-
4
- async function histogram (...args) {
5
- if (args.length === 0) return this.action(action, ...args)
6
- const [_filter = {}, params = {}, opts = {}] = args
7
- opts.dataOnly = opts.dataOnly ?? true
8
- const { dataOnly } = opts
9
- const { filter, options } = await getFilterAndOptions.call(this, _filter, opts, action)
10
- const { getDefaultValues } = this.app.dobo
11
- await execHook.call(this, 'beforeHistogram', filter, params, options)
12
- await execModelHook.call(this, 'beforeHistogram', filter, params, options)
13
- await execDynHook.call(this, 'beforeHistogram', filter, params, options)
14
- const result = (await this.adapter._histogram(this, filter, params, options)) ?? {}
15
- const { warnings } = getDefaultValues(options)
16
- if (!warnings) delete result.warnings
17
- await execDynHook.call(this, 'afterHistogram', filter, params, result, options)
18
- await execModelHook.call(this, 'afterHistogram', filter, params, result, options)
19
- await execHook.call(this, 'afterHistogram', filter, params, result, options)
20
- return dataOnly ? result.data : result
21
- }
22
-
23
- export default histogram
1
+ import { getFilterAndOptions, execHook, execModelHook, execDynHook } from './helper.js'
2
+ const action = 'histogram'
3
+
4
+ async function histogram (...args) {
5
+ if (args.length === 0) return this.action(action, ...args)
6
+ const [_filter = {}, params = {}, opts = {}] = args
7
+ opts.dataOnly = opts.dataOnly ?? true
8
+ const { dataOnly } = opts
9
+ const { filter, options } = await getFilterAndOptions.call(this, _filter, opts, action)
10
+ const { getDefaultValues } = this.app.dobo
11
+ await execHook.call(this, 'beforeHistogram', filter, params, options)
12
+ await execModelHook.call(this, 'beforeHistogram', filter, params, options)
13
+ await execDynHook.call(this, 'beforeHistogram', filter, params, options)
14
+ const result = (await this.adapter._histogram(this, filter, params, options)) ?? {}
15
+ const { warnings } = getDefaultValues(options)
16
+ if (!warnings) delete result.warnings
17
+ await execDynHook.call(this, 'afterHistogram', filter, params, result, options)
18
+ await execModelHook.call(this, 'afterHistogram', filter, params, result, options)
19
+ await execHook.call(this, 'afterHistogram', filter, params, result, options)
20
+ return dataOnly ? result.data : result
21
+ }
22
+
23
+ export default histogram
@@ -1,40 +1,40 @@
1
- import path from 'path'
2
- const action = 'listAttachment'
3
-
4
- async function listAttachment (...args) {
5
- if (!this.options.attachment || !this.app.waibu) return []
6
- if (args.length === 0) return this.action(action, ...args)
7
- const [params = {}, opts = {}] = args
8
- const { map, kebabCase } = this.app.lib._
9
- const { importPkg } = this.app.bajo
10
- const mime = await importPkg('waibu:mime')
11
- const { fastGlob } = this.app.lib
12
-
13
- const { id = '*', field = '*', file = '*', type } = params
14
- const { uriEncoded = true } = opts
15
- const root = `${this.app.getPluginDataDir('dobo')}/attachment`
16
- let pattern = `${root}/${this.name}/${id}/${field}/${file}`
17
- if (type === 'image') pattern += '.{jpg,jpeg,gif,png,webp,avif,svg}'
18
- else if (type === 'video') pattern += '.{mp4,m4v,webm,mov,qt,mkv,ogg,ogv}'
19
- else if (type) pattern += `.${type}`
20
- if (uriEncoded) pattern = pattern.split('/').map(p => decodeURI(p)).join('/')
21
- return map(await fastGlob(pattern), f => {
22
- const mimeType = mime.getType(path.extname(f)) ?? ''
23
- const fullPath = f.replace(root, '')
24
- const row = {
25
- file: f,
26
- fileName: path.basename(fullPath),
27
- fullPath,
28
- mimeType,
29
- params: { model: this.name, id, field, file }
30
- }
31
- if (this.app.waibuMpa) {
32
- const { routePath } = this.app.waibu
33
- const [, _model, _id, _field, _file] = fullPath.split('/')
34
- row.url = routePath(`dobo:/attachment/${kebabCase(_model)}/${_id}/${_field}/${_file}`)
35
- }
36
- return row
37
- })
38
- }
39
-
40
- export default listAttachment
1
+ import path from 'path'
2
+ const action = 'listAttachment'
3
+
4
+ async function listAttachment (...args) {
5
+ if (!this.options.attachment || !this.app.waibu) return []
6
+ if (args.length === 0) return this.action(action, ...args)
7
+ const [params = {}, opts = {}] = args
8
+ const { map, kebabCase } = this.app.lib._
9
+ const { importPkg } = this.app.bajo
10
+ const mime = await importPkg('waibu:mime')
11
+ const { fastGlob } = this.app.lib
12
+
13
+ const { id = '*', field = '*', file = '*', type } = params
14
+ const { uriEncoded = true } = opts
15
+ const root = `${this.app.getPluginDataDir('dobo')}/attachment`
16
+ let pattern = `${root}/${this.name}/${id}/${field}/${file}`
17
+ if (type === 'image') pattern += '.{jpg,jpeg,gif,png,webp,avif,svg}'
18
+ else if (type === 'video') pattern += '.{mp4,m4v,webm,mov,qt,mkv,ogg,ogv}'
19
+ else if (type) pattern += `.${type}`
20
+ if (uriEncoded) pattern = pattern.split('/').map(p => decodeURI(p)).join('/')
21
+ return map(await fastGlob(pattern), f => {
22
+ const mimeType = mime.getType(path.extname(f)) ?? ''
23
+ const fullPath = f.replace(root, '')
24
+ const row = {
25
+ file: f,
26
+ fileName: path.basename(fullPath),
27
+ fullPath,
28
+ mimeType,
29
+ params: { model: this.name, id, field, file }
30
+ }
31
+ if (this.app.waibuMpa) {
32
+ const { routePath } = this.app.waibu
33
+ const [, _model, _id, _field, _file] = fullPath.split('/')
34
+ row.url = routePath(`dobo:/attachment/${kebabCase(_model)}/${_id}/${_field}/${_file}`)
35
+ }
36
+ return row
37
+ })
38
+ }
39
+
40
+ export default listAttachment
@@ -1,63 +1,63 @@
1
- import path from 'path'
2
-
3
- async function exec ({ body, spinner, options, result, bodies } = {}) {
4
- const { isArray, isString } = this.app.lib._
5
- const { fs } = this.app.lib
6
-
7
- await this.transaction(async (trx) => {
8
- const resp = await this.createRecord(body, { ...options, trx })
9
- if (isArray(body._attachments) && body._attachments.length > 0) {
10
- for (let att of body._attachments) {
11
- if (isString(att)) att = { field: 'file', file: att }
12
- const fname = path.basename(att.file)
13
- if (fs.existsSync(att.file)) {
14
- const dest = `${this.app.getPluginDataDir(this.plugin.ns)}/${resp.id}/${att.field}/${fname}`
15
- try {
16
- fs.copySync(att.file, dest)
17
- } catch (err) {}
18
- }
19
- }
20
- }
21
- })
22
- result.success++
23
- if (spinner) spinner.setText('recordsAdded%s%d%d', this.name, result.success, bodies.length)
24
- }
25
-
26
- async function loadFixtures ({ spinner, ignoreError = true, collectItems = false, noLookup = false } = {}, options = {}) {
27
- const { readConfig } = this.app.bajo
28
- const { isEmpty } = this.app.lib._
29
- if (this.connection.proxy) {
30
- this.log.warn('proxiedConnBound%s', this.name)
31
- return
32
- }
33
- const result = { success: 0, failed: 0 }
34
- const bodies = await readConfig(`${this.plugin.ns}:/extend/dobo/fixture/${this.baseName}.*`, { ns: this.plugin.ns, baseNs: 'dobo', checkOverride: true, defValue: [] })
35
- const opts = {
36
- ...options,
37
- noModelHook: false,
38
- noHook: true,
39
- noDynHook: true,
40
- noValidation: false,
41
- noCache: true
42
- }
43
- for (const body of bodies) {
44
- await this.sanitizeFixture({ body, noLookup }, options)
45
- }
46
- if (collectItems) return bodies
47
- if (isEmpty(bodies)) return result
48
- for (const body of bodies) {
49
- if (ignoreError) {
50
- try {
51
- await exec.call(this, { body, spinner, options: opts, result, bodies })
52
- } catch (err) {
53
- if (this.app.bajo.config.log.applet) console.error(err)
54
- err.model = this.name
55
- if (this.app.applet) this.plugin.print.fail(this.app.dobo.validationErrorMessage(err))
56
- result.failed++
57
- }
58
- } else await exec.call(this, { body, spinner, options: opts, result, bodies })
59
- }
60
- return result
61
- }
62
-
63
- export default loadFixtures
1
+ import path from 'path'
2
+
3
+ async function exec ({ body, spinner, options, result, bodies } = {}) {
4
+ const { isArray, isString } = this.app.lib._
5
+ const { fs } = this.app.lib
6
+
7
+ await this.transaction(async (trx) => {
8
+ const resp = await this.createRecord(body, { ...options, trx })
9
+ if (isArray(body._attachments) && body._attachments.length > 0) {
10
+ for (let att of body._attachments) {
11
+ if (isString(att)) att = { field: 'file', file: att }
12
+ const fname = path.basename(att.file)
13
+ if (fs.existsSync(att.file)) {
14
+ const dest = `${this.app.getPluginDataDir(this.plugin.ns)}/${resp.id}/${att.field}/${fname}`
15
+ try {
16
+ fs.copySync(att.file, dest)
17
+ } catch (err) {}
18
+ }
19
+ }
20
+ }
21
+ })
22
+ result.success++
23
+ if (spinner) spinner.setText('recordsAdded%s%d%d', this.name, result.success, bodies.length)
24
+ }
25
+
26
+ async function loadFixtures ({ spinner, ignoreError = true, collectItems = false, noLookup = false } = {}, options = {}) {
27
+ const { readConfig } = this.app.bajo
28
+ const { isEmpty } = this.app.lib._
29
+ if (this.connection.proxy) {
30
+ this.log.warn('proxiedConnBound%s', this.name)
31
+ return
32
+ }
33
+ const result = { success: 0, failed: 0 }
34
+ const bodies = await readConfig(`${this.plugin.ns}:/extend/dobo/fixture/${this.baseName}.*`, { ns: this.plugin.ns, baseNs: 'dobo', checkOverride: true, defValue: [] })
35
+ const opts = {
36
+ ...options,
37
+ noModelHook: false,
38
+ noHook: true,
39
+ noDynHook: true,
40
+ noValidation: false,
41
+ noCache: true
42
+ }
43
+ for (const body of bodies) {
44
+ await this.sanitizeFixture({ body, noLookup }, options)
45
+ }
46
+ if (collectItems) return bodies
47
+ if (isEmpty(bodies)) return result
48
+ for (const body of bodies) {
49
+ if (ignoreError) {
50
+ try {
51
+ await exec.call(this, { body, spinner, options: opts, result, bodies })
52
+ } catch (err) {
53
+ if (this.app.bajo.config.log.applet) console.error(err)
54
+ err.model = this.name
55
+ if (this.app.applet) this.plugin.print.fail(this.app.dobo.validationErrorMessage(err))
56
+ result.failed++
57
+ }
58
+ } else await exec.call(this, { body, spinner, options: opts, result, bodies })
59
+ }
60
+ return result
61
+ }
62
+
63
+ export default loadFixtures
@@ -1,24 +1,24 @@
1
- import { getAttachmentPath } from './helper.js'
2
- import path from 'path'
3
- const action = 'removeAttachment'
4
-
5
- async function removeAttachment (...args) {
6
- if (!this.options.attachment) return
7
- if (args.length === 0) return this.action(action, ...args)
8
- const [id, field, file, opts = {}] = args
9
- const { fs, fastGlob } = this.app.lib
10
- const fullPath = await getAttachmentPath.call(this, id, field, file)
11
- const { req } = opts
12
- await fs.remove(fullPath)
13
- const dir = path.dirname(fullPath)
14
- const ext = path.extname(fullPath)
15
- const base = path.basename(fullPath, ext)
16
- const pattern = `${dir}/_tn/${base}-*.*`
17
- const files = await fastGlob(pattern)
18
- for (const f of files) {
19
- await fs.remove(f)
20
- }
21
- if (!opts.noFlash && req && req.flash) req.flash('notify', req.t('attachmentRemoved'))
22
- }
23
-
24
- export default removeAttachment
1
+ import { getAttachmentPath } from './helper.js'
2
+ import path from 'path'
3
+ const action = 'removeAttachment'
4
+
5
+ async function removeAttachment (...args) {
6
+ if (!this.options.attachment) return
7
+ if (args.length === 0) return this.action(action, ...args)
8
+ const [id, field, file, opts = {}] = args
9
+ const { fs, fastGlob } = this.app.lib
10
+ const fullPath = await getAttachmentPath.call(this, id, field, file)
11
+ const { req } = opts
12
+ await fs.remove(fullPath)
13
+ const dir = path.dirname(fullPath)
14
+ const ext = path.extname(fullPath)
15
+ const base = path.basename(fullPath, ext)
16
+ const pattern = `${dir}/_tn/${base}-*.*`
17
+ const files = await fastGlob(pattern)
18
+ for (const f of files) {
19
+ await fs.remove(f)
20
+ }
21
+ if (!opts.noFlash && req && req.flash) req.flash('notify', req.t('attachmentRemoved'))
22
+ }
23
+
24
+ export default removeAttachment
@@ -1,44 +1,44 @@
1
- import { getFilterAndOptions, execHook, execModelHook, execDynHook, getRefs, handleReq, clearCache } from './helper.js'
2
- const action = 'removeRecord'
3
-
4
- /**
5
- * Removes a record from the model's underlying data store based on the provided ID.
6
- *
7
- * If no arguments are provided, it automatically turns into a chainable {@link DoboAction} object.
8
- * @async
9
- * @memberof DoboModel
10
- * @method
11
- * @param {string|number} id - The ID of the record to be removed.
12
- * @param {DoboModel.TOptions} [opts={}] - Options object.
13
- * @see {@link module:Hook.beforeRemoveRecord}
14
- * @see {@link module:Hook.afterRemoveRecord}
15
- * @returns {DoboAction|DoboModel.TResultRemoveRecord|DoboModel.TRecord}
16
- */
17
- async function removeRecord (...args) {
18
- if (args.length === 0) return this.action(action, ...args)
19
- let [id, opts = {}] = args
20
- const { getDefaultValues } = this.app.dobo
21
- const { isSet } = this.app.lib.aneka
22
- opts.dataOnly = opts.dataOnly ?? true
23
- const { dataOnly } = opts
24
- const { options } = await getFilterAndOptions.call(this, null, opts, action)
25
- const { noResult, noResultSanitizer } = options
26
- id = this.sanitizeId(id)
27
- await execHook.call(this, 'beforeRemoveRecord', id, options)
28
- await execModelHook.call(this, 'beforeRemoveRecord', id, options)
29
- await execDynHook.call(this, 'beforeRemoveRecord', id, options)
30
- const result = options.record ?? (await this.adapter._removeRecord(this, id, options)) ?? {}
31
- if (noResult) return
32
- await handleReq.call(this, result.oldData.id, 'removed', options)
33
- await clearCache.call(this, id)
34
- const { warnings } = getDefaultValues(options)
35
- if (!warnings) delete result.warnings
36
- if (!noResultSanitizer) result.oldData = await this.sanitizeRecord(result.oldData, options)
37
- if (isSet(options.refs)) await getRefs.call(this, [result.data], options)
38
- await execDynHook.call(this, 'afterRemoveRecord', id, result, options)
39
- await execModelHook.call(this, 'afterRemoveRecord', id, result, options)
40
- await execHook.call(this, 'afterRemoveRecord', id, result, options)
41
- return dataOnly ? result.oldData : result
42
- }
43
-
44
- export default removeRecord
1
+ import { getFilterAndOptions, execHook, execModelHook, execDynHook, getRefs, handleReq, clearCache } from './helper.js'
2
+ const action = 'removeRecord'
3
+
4
+ /**
5
+ * Removes a record from the model's underlying data store based on the provided ID.
6
+ *
7
+ * If no arguments are provided, it automatically turns into a chainable {@link DoboAction} object.
8
+ * @async
9
+ * @memberof DoboModel
10
+ * @method
11
+ * @param {string|number} id - The ID of the record to be removed.
12
+ * @param {DoboModel.TOptions} [opts={}] - Options object.
13
+ * @see {@link module:Hook.beforeRemoveRecord}
14
+ * @see {@link module:Hook.afterRemoveRecord}
15
+ * @returns {DoboAction|DoboModel.TResultRemoveRecord|DoboModel.TRecord}
16
+ */
17
+ async function removeRecord (...args) {
18
+ if (args.length === 0) return this.action(action, ...args)
19
+ let [id, opts = {}] = args
20
+ const { getDefaultValues } = this.app.dobo
21
+ const { isSet } = this.app.lib.aneka
22
+ opts.dataOnly = opts.dataOnly ?? true
23
+ const { dataOnly } = opts
24
+ const { options } = await getFilterAndOptions.call(this, null, opts, action)
25
+ const { noResult, noResultSanitizer } = options
26
+ id = this.sanitizeId(id)
27
+ await execHook.call(this, 'beforeRemoveRecord', id, options)
28
+ await execModelHook.call(this, 'beforeRemoveRecord', id, options)
29
+ await execDynHook.call(this, 'beforeRemoveRecord', id, options)
30
+ const result = options.record ?? (await this.adapter._removeRecord(this, id, options)) ?? {}
31
+ if (noResult) return
32
+ await handleReq.call(this, result.oldData.id, 'removed', options)
33
+ await clearCache.call(this, id)
34
+ const { warnings } = getDefaultValues(options)
35
+ if (!warnings) delete result.warnings
36
+ if (!noResultSanitizer) result.oldData = await this.sanitizeRecord(result.oldData, options)
37
+ if (isSet(options.refs)) await getRefs.call(this, [result.data], options)
38
+ await execDynHook.call(this, 'afterRemoveRecord', id, result, options)
39
+ await execModelHook.call(this, 'afterRemoveRecord', id, result, options)
40
+ await execHook.call(this, 'afterRemoveRecord', id, result, options)
41
+ return dataOnly ? result.oldData : result
42
+ }
43
+
44
+ export default removeRecord
@@ -1,57 +1,57 @@
1
- /**
2
- * Sanitize the payload body based on the model's properties and rules.
3
- * @method
4
- * @memberof DoboModel
5
- * @async
6
- * @param {Object} [options={}] - Options object
7
- * @param {Object} [options.body={}] - Body object to sanitize
8
- * @param {boolean} [options.partial=false] - If `true`, only the fields present in the body will be sanitized
9
- * @param {boolean} [options.strict=false] - If `true`, only the fields defined in the model's properties will be included in the sanitized body
10
- * @param {Array} [options.extFields=[]] - Additional fields to include in the sanitized body
11
- * @param {boolean} [options.truncateString=false] - If `true`, string fields will be truncated to their maximum length defined in the model's properties
12
- * @param {string} [options.action] - The action being performed (e.g., 'createRecord', 'updateRecord', etc.)
13
- * @param {boolean} [options.allProps=false] - If `true`, all properties including virtual ones will be considered for sanitization
14
- * @returns {Object} Returns sanitized body object
15
- */
16
- async function sanitizeBody (options = {}) {
17
- const { body = {}, partial, strict, extFields = [], truncateString, action, allProps } = options
18
- const { isSet } = this.app.lib.aneka
19
- const { sanitizeByType } = this.app.dobo
20
- const { omit, has } = this.app.lib._
21
- const result = {}
22
-
23
- const sanitize = (name, type) => {
24
- const opts = { strict, action, model: this.name, outputFormat: 'native' }
25
- result[name] = sanitizeByType(result[name], type, opts)
26
- if (['updateRecord', 'upsertRecord'].includes(action) && type === 'string' && result[name] === '') result[name] = null
27
- }
28
-
29
- const omitted = []
30
- const details = []
31
- const props = allProps ? this.properties : this.getNonVirtualProperties()
32
- const properties = [...props, ...extFields]
33
- for (const prop of properties) {
34
- try {
35
- if (partial && !has(body, prop.name)) {
36
- // if (prop.type === 'array') result[prop.name] = null
37
- continue
38
- }
39
- result[prop.name] = body[prop.name]
40
- if (prop.type === 'array' && isSet(result[prop.name]) && !Array.isArray(result[prop.name])) result[prop.name] = [result[prop.name]]
41
- if (isSet(result[prop.name])) sanitize(prop.name, prop.type)
42
- if (result[prop.name] === null) continue
43
- if (truncateString && isSet(result[prop.name]) && ['string', 'text'].includes(prop.type)) result[prop.name] = result[prop.name].slice(0, prop.maxLength)
44
- if (prop.name.endsWith('Id') && isSet(result[prop.name]) && prop.type === 'string' && ['smallint', 'integer'].includes(this.adapter.idField.type)) result[prop.name] = result[prop.name] + ''
45
- if (result[prop.name] === undefined) omitted.push(prop.name)
46
- } catch (err) {
47
- details.push({ field: prop.name, error: err.message, value: body[prop.name], ext: { type: prop.type } })
48
- }
49
- }
50
- if (details.length > 0) {
51
- const payload = { details, statusCode: 422, code: 'BODY_SANITIZATION', model: this.name }
52
- throw this.plugin.error('sanitizeBodyError', payload)
53
- }
54
- return omit(result, omitted)
55
- }
56
-
57
- export default sanitizeBody
1
+ /**
2
+ * Sanitize the payload body based on the model's properties and rules.
3
+ * @method
4
+ * @memberof DoboModel
5
+ * @async
6
+ * @param {Object} [options={}] - Options object
7
+ * @param {Object} [options.body={}] - Body object to sanitize
8
+ * @param {boolean} [options.partial=false] - If `true`, only the fields present in the body will be sanitized
9
+ * @param {boolean} [options.strict=false] - If `true`, only the fields defined in the model's properties will be included in the sanitized body
10
+ * @param {Array} [options.extFields=[]] - Additional fields to include in the sanitized body
11
+ * @param {boolean} [options.truncateString=false] - If `true`, string fields will be truncated to their maximum length defined in the model's properties
12
+ * @param {string} [options.action] - The action being performed (e.g., 'createRecord', 'updateRecord', etc.)
13
+ * @param {boolean} [options.allProps=false] - If `true`, all properties including virtual ones will be considered for sanitization
14
+ * @returns {Object} Returns sanitized body object
15
+ */
16
+ async function sanitizeBody (options = {}) {
17
+ const { body = {}, partial, strict, extFields = [], truncateString, action, allProps } = options
18
+ const { isSet } = this.app.lib.aneka
19
+ const { sanitizeByType } = this.app.dobo
20
+ const { omit, has } = this.app.lib._
21
+ const result = {}
22
+
23
+ const sanitize = (name, type) => {
24
+ const opts = { strict, action, model: this.name, outputFormat: 'native' }
25
+ result[name] = sanitizeByType(result[name], type, opts)
26
+ if (['updateRecord', 'upsertRecord'].includes(action) && type === 'string' && result[name] === '') result[name] = null
27
+ }
28
+
29
+ const omitted = []
30
+ const details = []
31
+ const props = allProps ? this.properties : this.getNonVirtualProperties()
32
+ const properties = [...props, ...extFields]
33
+ for (const prop of properties) {
34
+ try {
35
+ if (partial && !has(body, prop.name)) {
36
+ // if (prop.type === 'array') result[prop.name] = null
37
+ continue
38
+ }
39
+ result[prop.name] = body[prop.name]
40
+ if (prop.type === 'array' && isSet(result[prop.name]) && !Array.isArray(result[prop.name])) result[prop.name] = [result[prop.name]]
41
+ if (isSet(result[prop.name])) sanitize(prop.name, prop.type)
42
+ if (result[prop.name] === null) continue
43
+ if (truncateString && isSet(result[prop.name]) && ['string', 'text'].includes(prop.type)) result[prop.name] = result[prop.name].slice(0, prop.maxLength)
44
+ if (prop.name.endsWith('Id') && isSet(result[prop.name]) && prop.type === 'string' && ['smallint', 'integer'].includes(this.adapter.idField.type)) result[prop.name] = result[prop.name] + ''
45
+ if (result[prop.name] === undefined) omitted.push(prop.name)
46
+ } catch (err) {
47
+ details.push({ field: prop.name, error: err.message, value: body[prop.name], ext: { type: prop.type } })
48
+ }
49
+ }
50
+ if (details.length > 0) {
51
+ const payload = { details, statusCode: 422, code: 'BODY_SANITIZATION', model: this.name }
52
+ throw this.plugin.error('sanitizeBodyError', payload)
53
+ }
54
+ return omit(result, omitted)
55
+ }
56
+
57
+ export default sanitizeBody
@@ -1,56 +1,56 @@
1
- /**
2
- * Sanitize a fixture object by resolving lookup values and ensuring proper data types.
3
- * @method
4
- * @async
5
- * @memberof DoboModel
6
- * @param {Object} [params={}] - Parameters object
7
- * @param {Object} [params.body={}] - Body object to sanitize
8
- * @param {Object} [params.lookupValue={}] - Lookup values for resolving references in the body
9
- * @param {boolean} [params.noLookup=false] - If `true`, skip lookup resolution
10
- * @param {DoboModel.TOptions} [opts={}] - Options object for additional configurations
11
- * @returns {Object} Returns sanitized body object
12
- */
13
- async function sanitizeFixture (params = {}, opts = {}) {
14
- const { body = {}, lookupValue = {}, noLookup } = params
15
- const { isString, isArray, pullAt, cloneDeep } = this.app.lib._
16
- const { isSet } = this.app.lib.aneka
17
- const lv = cloneDeep(lookupValue)
18
- const deleted = {}
19
- const options = {
20
- ...opts,
21
- noModelHook: false,
22
- noHook: true,
23
- noDynHook: true,
24
- noValidation: false,
25
- noCache: true
26
- }
27
- for (const key in body) {
28
- const val = body[key]
29
- deleted[key] = deleted[key] ?? []
30
- if (!noLookup) {
31
- if (isString(val) && val.slice(0, 2) === '?:') {
32
- body[key] = await this._simpleLookup(val.slice(2), lv, options)
33
- lv[key] = body[key]
34
- } else if (isArray(val)) {
35
- for (const idx in val) {
36
- if (isString(val[idx]) && val[idx].slice(0, 2) === '?:') {
37
- body[key][idx] = await this._simpleLookup(val[idx].slice(2), lv, opts)
38
- if (isSet(body[key][idx])) body[key][idx] += ''
39
- else deleted[key].push(idx)
40
- lv[`${key}.${idx}`] = body[key][idx]
41
- }
42
- }
43
- if (deleted[key].length > 0) pullAt(body[key], deleted[key])
44
- }
45
- }
46
- delete deleted[key]
47
- if (val === null) body[key] = undefined
48
- else {
49
- const prop = this.properties.find(item => item.name === key)
50
- if (prop && ['string', 'text'].includes(prop.type) && isSet(body[key])) body[key] += ''
51
- }
52
- }
53
- return body
54
- }
55
-
56
- export default sanitizeFixture
1
+ /**
2
+ * Sanitize a fixture object by resolving lookup values and ensuring proper data types.
3
+ * @method
4
+ * @async
5
+ * @memberof DoboModel
6
+ * @param {Object} [params={}] - Parameters object
7
+ * @param {Object} [params.body={}] - Body object to sanitize
8
+ * @param {Object} [params.lookupValue={}] - Lookup values for resolving references in the body
9
+ * @param {boolean} [params.noLookup=false] - If `true`, skip lookup resolution
10
+ * @param {DoboModel.TOptions} [opts={}] - Options object for additional configurations
11
+ * @returns {Object} Returns sanitized body object
12
+ */
13
+ async function sanitizeFixture (params = {}, opts = {}) {
14
+ const { body = {}, lookupValue = {}, noLookup } = params
15
+ const { isString, isArray, pullAt, cloneDeep } = this.app.lib._
16
+ const { isSet } = this.app.lib.aneka
17
+ const lv = cloneDeep(lookupValue)
18
+ const deleted = {}
19
+ const options = {
20
+ ...opts,
21
+ noModelHook: false,
22
+ noHook: true,
23
+ noDynHook: true,
24
+ noValidation: false,
25
+ noCache: true
26
+ }
27
+ for (const key in body) {
28
+ const val = body[key]
29
+ deleted[key] = deleted[key] ?? []
30
+ if (!noLookup) {
31
+ if (isString(val) && val.slice(0, 2) === '?:') {
32
+ body[key] = await this._simpleLookup(val.slice(2), lv, options)
33
+ lv[key] = body[key]
34
+ } else if (isArray(val)) {
35
+ for (const idx in val) {
36
+ if (isString(val[idx]) && val[idx].slice(0, 2) === '?:') {
37
+ body[key][idx] = await this._simpleLookup(val[idx].slice(2), lv, opts)
38
+ if (isSet(body[key][idx])) body[key][idx] += ''
39
+ else deleted[key].push(idx)
40
+ lv[`${key}.${idx}`] = body[key][idx]
41
+ }
42
+ }
43
+ if (deleted[key].length > 0) pullAt(body[key], deleted[key])
44
+ }
45
+ }
46
+ delete deleted[key]
47
+ if (val === null) body[key] = undefined
48
+ else {
49
+ const prop = this.properties.find(item => item.name === key)
50
+ if (prop && ['string', 'text'].includes(prop.type) && isSet(body[key])) body[key] += ''
51
+ }
52
+ }
53
+ return body
54
+ }
55
+
56
+ export default sanitizeFixture