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.
- package/.bootorder +0 -0
- package/.github/FUNDING.yml +0 -0
- package/.github/workflows/repo-lockdown.yml +0 -0
- package/.jsdoc.conf.json +0 -0
- package/LICENSE +0 -0
- package/README.md +0 -0
- package/docs/Dobo.html +0 -0
- package/docs/DoboAction.html +0 -0
- package/docs/DoboAdapter.html +0 -0
- package/docs/DoboConnection.html +0 -0
- package/docs/DoboFeature.html +0 -0
- package/docs/DoboModel.html +0 -0
- package/docs/data/search.json +0 -0
- package/docs/extend_bajo_hook-docs.js.html +0 -0
- package/docs/external-Tools.html +0 -0
- package/docs/fonts/Inconsolata-Regular.ttf +0 -0
- package/docs/fonts/OpenSans-Regular.ttf +0 -0
- package/docs/fonts/WorkSans-Bold.ttf +0 -0
- package/docs/global.html +0 -0
- package/docs/index.html +0 -0
- package/docs/index.js.html +0 -0
- package/docs/lib_factory_action.js.html +0 -0
- package/docs/lib_factory_adapter.js.html +0 -0
- package/docs/lib_factory_connection.js.html +0 -0
- package/docs/lib_factory_feature.js.html +0 -0
- package/docs/lib_factory_model.js.html +0 -0
- package/docs/lib_factory_model_build.js.html +0 -0
- package/docs/lib_factory_model_clear-record.js.html +0 -0
- package/docs/lib_factory_model_count-record.js.html +0 -0
- package/docs/lib_factory_model_create-record.js.html +0 -0
- package/docs/lib_factory_model_drop.js.html +0 -0
- package/docs/lib_factory_model_exists.js.html +0 -0
- package/docs/lib_factory_model_find-all-record.js.html +0 -0
- package/docs/lib_factory_model_find-one-record.js.html +0 -0
- package/docs/lib_factory_model_find-record.js.html +0 -0
- package/docs/lib_factory_model_get-record.js.html +0 -0
- package/docs/lib_factory_model_helper.js.html +0 -0
- package/docs/lib_factory_model_remove-record.js.html +0 -0
- package/docs/lib_factory_model_sanitize-body.js.html +0 -0
- package/docs/lib_factory_model_sanitize-fixture.js.html +0 -0
- package/docs/lib_factory_model_sanitize-record.js.html +0 -0
- package/docs/lib_factory_model_update-record.js.html +0 -0
- package/docs/lib_factory_model_upsert-record.js.html +0 -0
- package/docs/lib_factory_model_validate.js.html +0 -0
- package/docs/lib_helper.js.html +0 -0
- package/docs/module-Helper.html +0 -0
- package/docs/module-Helper_Model.html +0 -0
- package/docs/module-Hook.html +0 -0
- package/docs/scripts/core.js +0 -0
- package/docs/scripts/core.min.js +0 -0
- package/docs/scripts/resize.js +0 -0
- package/docs/scripts/search.js +0 -0
- package/docs/scripts/search.min.js +5 -5
- package/docs/scripts/third-party/Apache-License-2.0.txt +0 -0
- package/docs/scripts/third-party/fuse.js +0 -0
- package/docs/scripts/third-party/hljs-line-num-original.js +0 -0
- package/docs/scripts/third-party/hljs-line-num.js +0 -0
- package/docs/scripts/third-party/hljs-original.js +0 -0
- package/docs/scripts/third-party/hljs.js +0 -0
- package/docs/scripts/third-party/popper.js +0 -0
- package/docs/scripts/third-party/tippy.js +0 -0
- package/docs/scripts/third-party/tocbot.js +0 -0
- package/docs/scripts/third-party/tocbot.min.js +0 -0
- package/docs/static/bitcoin.jpeg +0 -0
- package/docs/static/home.md +0 -0
- package/docs/static/logo-ecosystem.png +0 -0
- package/docs/static/logo.png +0 -0
- package/docs/styles/clean-jsdoc-theme-base.css +0 -0
- package/docs/styles/clean-jsdoc-theme-dark.css +0 -0
- package/docs/styles/clean-jsdoc-theme-light.css +0 -0
- package/docs/styles/clean-jsdoc-theme-scrollbar.css +0 -0
- package/docs/styles/clean-jsdoc-theme-without-scrollbar.min.css +0 -0
- package/docs/styles/clean-jsdoc-theme.min.css +0 -0
- package/extend/bajo/hook.js +236 -236
- package/extend/bajo/intl/en-US.json +0 -0
- package/extend/bajo/intl/id.json +0 -0
- package/extend/bajoCli/applet/aggregate.js +0 -0
- package/extend/bajoCli/applet/clear-record.js +0 -0
- package/extend/bajoCli/applet/connection.js +0 -0
- package/extend/bajoCli/applet/count-record.js +0 -0
- package/extend/bajoCli/applet/create-record.js +0 -0
- package/extend/bajoCli/applet/find-record.js +0 -0
- package/extend/bajoCli/applet/get-record.js +0 -0
- package/extend/bajoCli/applet/histogram.js +0 -0
- package/extend/bajoCli/applet/lib/post-process.js +0 -0
- package/extend/bajoCli/applet/model.js +0 -0
- package/extend/bajoCli/applet/rebuild-model.js +0 -0
- package/extend/bajoCli/applet/remove-record.js +0 -0
- package/extend/bajoCli/applet/update-record.js +0 -0
- package/extend/bajoCli/applet.js +0 -0
- package/extend/dobo/adapter/memory.js +175 -175
- package/extend/dobo/feature/created-at.js +0 -0
- package/extend/dobo/feature/dt.js +0 -0
- package/extend/dobo/feature/image.js +0 -0
- package/extend/dobo/feature/immutable.js +0 -0
- package/extend/dobo/feature/removed-at.js +0 -0
- package/extend/dobo/feature/unique.js +0 -0
- package/extend/dobo/feature/updated-at.js +0 -0
- package/extend/waibuMpa/route/attachment/@model/@id/@field/@file.js +0 -0
- package/extend/waibuStatic/virtual.json +0 -0
- package/index.js +0 -0
- package/lib/factory/action.js +287 -287
- package/lib/factory/adapter.js +1055 -1055
- package/lib/factory/connection.js +109 -109
- package/lib/factory/feature.js +52 -52
- package/lib/factory/model/aggregate.js +23 -23
- package/lib/factory/model/build.js +25 -25
- package/lib/factory/model/bulk-create-record.js +36 -36
- package/lib/factory/model/clear-record.js +30 -30
- package/lib/factory/model/count-record.js +42 -42
- package/lib/factory/model/create-attachment.js +36 -36
- package/lib/factory/model/create-record.js +49 -49
- package/lib/factory/model/drop.js +26 -26
- package/lib/factory/model/exists.js +26 -26
- package/lib/factory/model/find-all-record.js +112 -112
- package/lib/factory/model/find-attachment.js +28 -28
- package/lib/factory/model/find-one-record.js +38 -38
- package/lib/factory/model/find-record.js +82 -82
- package/lib/factory/model/get-attachment.js +15 -15
- package/lib/factory/model/get-record.js +52 -52
- package/lib/factory/model/helper.js +575 -576
- package/lib/factory/model/histogram.js +23 -23
- package/lib/factory/model/list-attachment.js +40 -40
- package/lib/factory/model/load-fixtures.js +63 -63
- package/lib/factory/model/remove-attachment.js +24 -24
- package/lib/factory/model/remove-record.js +44 -44
- package/lib/factory/model/sanitize-body.js +57 -57
- package/lib/factory/model/sanitize-fixture.js +56 -56
- package/lib/factory/model/sanitize-record.js +80 -80
- package/lib/factory/model/transaction.js +15 -15
- package/lib/factory/model/update-attachment.js +9 -9
- package/lib/factory/model/update-record.js +58 -58
- package/lib/factory/model/upsert-record.js +71 -71
- package/lib/factory/model/validate.js +0 -0
- package/lib/factory/model.js +503 -503
- package/lib/helper.js +463 -463
- package/package.json +1 -1
- package/test/e2e/_run.js +12 -12
- package/test/e2e/e2e-factory-process.test.js +25 -25
- package/test/e2e/e2e-model-adapter-process.test.js +52 -52
- package/test/integration/aspect-01-start-connections.test.js +33 -33
- package/test/integration/aspect-02-query-regex.test.js +34 -34
- package/test/integration/aspect-03-model-adapter-flow.test.js +88 -88
- package/test/unit/_stub.js +244 -244
- package/test/unit/dobo-action.test.js +58 -58
- package/test/unit/dobo-connection.test.js +37 -37
- package/test/unit/dobo-core.test.js +136 -136
- package/test/unit/dobo-feature.test.js +25 -25
- package/test/unit/dobo-model.test.js +115 -115
- package/test/unit/helper-sanitize-all.test.js +40 -40
- package/test/unit/helper-sanitize-ref.test.js +64 -64
- package/wiki/APPLETS.md +0 -0
- package/wiki/CHANGES.md +477 -469
- package/wiki/CONFIG.md +0 -0
- package/wiki/CONTRIBUTING.md +0 -0
- package/wiki/DEV-GUIDE.md +0 -0
- package/wiki/ECOSYSTEM.md +0 -0
- package/wiki/GETTING-STARTED.md +0 -0
- package/wiki/QUERY-LANGUAGE.md +0 -0
- 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
|