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.
- 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 +2 -2
- package/docs/Dobo.html +0 -0
- package/docs/data/search.json +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_collect-connections.js.html +0 -0
- package/docs/lib_collect-drivers.js.html +0 -0
- package/docs/lib_collect-features.js.html +0 -0
- package/docs/lib_collect-schemas.js.html +0 -0
- package/docs/lib_index.js.html +0 -0
- package/docs/method_model_create.js.html +0 -0
- package/docs/method_model_drop.js.html +0 -0
- package/docs/method_model_exists.js.html +0 -0
- package/docs/method_record_count.js.html +0 -0
- package/docs/method_record_create.js.html +0 -0
- package/docs/method_record_find-all.js.html +0 -0
- package/docs/method_record_find-one.js.html +0 -0
- package/docs/method_record_find.js.html +0 -0
- package/docs/method_record_get.js.html +0 -0
- package/docs/method_record_remove.js.html +0 -0
- package/docs/method_record_update.js.html +0 -0
- package/docs/method_record_upsert.js.html +0 -0
- package/docs/method_sanitize_body.js.html +0 -0
- package/docs/method_sanitize_date.js.html +0 -0
- package/docs/method_sanitize_id.js.html +0 -0
- package/docs/method_validate.js.html +0 -0
- package/docs/module-Lib.html +0 -0
- package/docs/scripts/core.js +476 -477
- package/docs/scripts/core.min.js +0 -0
- package/docs/scripts/resize.js +36 -36
- package/docs/scripts/search.js +105 -105
- package/docs/scripts/search.min.js +0 -0
- package/docs/scripts/third-party/Apache-License-2.0.txt +0 -0
- package/docs/scripts/third-party/fuse.js +1 -1
- package/docs/scripts/third-party/hljs-line-num-original.js +282 -285
- package/docs/scripts/third-party/hljs-line-num.js +1 -1
- package/docs/scripts/third-party/hljs-original.js +1195 -1202
- package/docs/scripts/third-party/hljs.js +1 -1
- package/docs/scripts/third-party/popper.js +1 -1
- package/docs/scripts/third-party/tippy.js +1 -1
- package/docs/scripts/third-party/tocbot.js +508 -509
- 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/intl/en-US.json +66 -28
- package/extend/bajo/intl/id.json +55 -27
- package/extend/bajoCli/applet/clear-record.js +22 -0
- package/extend/bajoCli/applet/connection.js +0 -0
- package/extend/bajoCli/applet/count-record.js +27 -0
- package/extend/bajoCli/applet/create-aggregate.js +33 -0
- package/extend/bajoCli/applet/create-histogram.js +33 -0
- package/extend/bajoCli/applet/create-record.js +39 -0
- package/extend/bajoCli/applet/find-record.js +27 -0
- package/extend/bajoCli/applet/get-record.js +27 -0
- package/extend/bajoCli/applet/lib/post-process.js +10 -17
- package/extend/bajoCli/applet/model.js +22 -0
- package/extend/bajoCli/applet/rebuild-model.js +91 -0
- package/extend/bajoCli/applet/remove-record.js +27 -0
- package/extend/bajoCli/applet/update-record.js +44 -0
- package/extend/bajoCli/applet.js +0 -0
- package/extend/dobo/driver/memory.js +170 -0
- package/extend/dobo/feature/created-at.js +9 -7
- package/extend/dobo/feature/dt.js +0 -0
- package/extend/dobo/feature/immutable.js +30 -0
- package/extend/dobo/feature/int-id.js +0 -0
- package/extend/dobo/feature/removed-at.js +32 -54
- package/extend/dobo/feature/updated-at.js +14 -12
- package/extend/waibuMpa/route/attachment/@model/@id/@field/@file.js +2 -6
- package/extend/waibuStatic/virtual.json +0 -0
- package/index.js +291 -366
- package/lib/collect-connections.js +49 -21
- package/lib/collect-drivers.js +19 -33
- package/lib/collect-features.js +24 -17
- package/lib/collect-models.js +319 -0
- package/lib/factory/action.js +161 -0
- package/lib/factory/connection.js +62 -0
- package/lib/factory/driver.js +358 -0
- package/lib/factory/feature.js +33 -0
- package/lib/factory/model/_util.js +402 -0
- package/lib/factory/model/build.js +15 -0
- package/lib/factory/model/clear-record.js +17 -0
- package/lib/factory/model/count-record.js +17 -0
- package/lib/factory/model/create-aggregate.js +17 -0
- package/lib/factory/model/create-attachment.js +29 -0
- package/lib/factory/model/create-histogram.js +17 -0
- package/lib/factory/model/create-record.js +35 -0
- package/lib/factory/model/drop.js +15 -0
- package/lib/factory/model/exists.js +21 -0
- package/lib/factory/model/find-all-record.js +71 -0
- package/lib/factory/model/find-attachment.js +29 -0
- package/lib/factory/model/find-one-record.js +19 -0
- package/{method/record/find.js → lib/factory/model/find-record.js} +103 -115
- package/lib/factory/model/get-attachment.js +15 -0
- package/lib/factory/model/get-record.js +79 -0
- package/lib/factory/model/list-attachment.js +37 -0
- package/lib/{add-fixtures.js → factory/model/load-fixtures.js} +69 -67
- package/lib/factory/model/remove-attachment.js +15 -0
- package/lib/factory/model/remove-record.js +59 -0
- package/lib/factory/model/sanitize-body.js +62 -0
- package/lib/factory/model/sanitize-id.js +7 -0
- package/lib/factory/model/sanitize-record.js +26 -0
- package/lib/factory/model/update-attachment.js +9 -0
- package/lib/factory/model/update-record.js +81 -0
- package/lib/factory/model/upsert-record.js +95 -0
- package/{method → lib/factory/model}/validate.js +38 -52
- package/lib/factory/model.js +150 -0
- package/lib/index.js +0 -0
- package/package.json +8 -4
- package/wiki/APPLETS.md +0 -0
- package/wiki/CHANGES.md +46 -0
- 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 +10 -10
- package/wiki/QUERY-LANGUAGE.md +0 -0
- package/wiki/USER-GUIDE.md +0 -0
- package/extend/bajoCli/applet/model-clear.js +0 -11
- package/extend/bajoCli/applet/model-rebuild.js +0 -101
- package/extend/bajoCli/applet/record-create.js +0 -43
- package/extend/bajoCli/applet/record-find.js +0 -28
- package/extend/bajoCli/applet/record-get.js +0 -24
- package/extend/bajoCli/applet/record-remove.js +0 -24
- package/extend/bajoCli/applet/record-update.js +0 -47
- package/extend/bajoCli/applet/schema.js +0 -22
- package/extend/bajoCli/applet/stat-count.js +0 -24
- package/lib/build-bulk-action.js +0 -12
- package/lib/check-unique.js +0 -39
- package/lib/collect-schemas.js +0 -91
- package/lib/exec-feature-hook.js +0 -13
- package/lib/exec-validation.js +0 -21
- package/lib/generic-prop-sanitizer.js +0 -32
- package/lib/handle-attachment-upload.js +0 -16
- package/lib/mem-db/conn-sanitizer.js +0 -8
- package/lib/mem-db/instantiate.js +0 -41
- package/lib/mem-db/method/model/clear.js +0 -6
- package/lib/mem-db/method/model/create.js +0 -5
- package/lib/mem-db/method/model/drop.js +0 -5
- package/lib/mem-db/method/model/exists.js +0 -5
- package/lib/mem-db/method/record/create.js +0 -12
- package/lib/mem-db/method/record/find.js +0 -20
- package/lib/mem-db/method/record/get.js +0 -9
- package/lib/mem-db/method/record/remove.js +0 -13
- package/lib/mem-db/method/record/update.js +0 -15
- package/lib/mem-db/method/stat/count.js +0 -11
- package/lib/mem-db/start.js +0 -25
- package/lib/merge-attachment-info.js +0 -16
- package/lib/multi-rel-rows.js +0 -42
- package/lib/resolve-method.js +0 -16
- package/lib/sanitize-schema.js +0 -198
- package/lib/single-rel-rows.js +0 -38
- package/method/attachment/copy-uploaded.js +0 -34
- package/method/attachment/create.js +0 -29
- package/method/attachment/find.js +0 -27
- package/method/attachment/get-path.js +0 -12
- package/method/attachment/get.js +0 -12
- package/method/attachment/pre-check.js +0 -9
- package/method/attachment/remove.js +0 -11
- package/method/attachment/update.js +0 -7
- package/method/bulk/create.js +0 -46
- package/method/model/clear.js +0 -22
- package/method/model/create.js +0 -32
- package/method/model/drop.js +0 -31
- package/method/model/exists.js +0 -37
- package/method/record/clear.js +0 -24
- package/method/record/count.js +0 -66
- package/method/record/create.js +0 -111
- package/method/record/find-all.js +0 -41
- package/method/record/find-one.js +0 -70
- package/method/record/get.js +0 -89
- package/method/record/remove.js +0 -72
- package/method/record/update.js +0 -104
- package/method/record/upsert.js +0 -51
- package/method/sanitize/body.js +0 -85
- package/method/sanitize/date.js +0 -27
- package/method/sanitize/id.js +0 -17
- package/method/stat/aggregate.js +0 -23
- package/method/stat/histogram.js +0 -26
package/wiki/CHANGES.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Changes
|
|
2
|
+
|
|
3
|
+
## 2026-01-11
|
|
4
|
+
|
|
5
|
+
- [2.2.0] Any driver that support memory DB can now declare itself as in-memory DB and be handled as such
|
|
6
|
+
- [2.2.0] ```driver.init()``` is removed, and driver should solely use ```driver.createClient()``` instead
|
|
7
|
+
- [2.2.0] Bug fixes
|
|
8
|
+
|
|
9
|
+
## 2026-01-07
|
|
10
|
+
|
|
11
|
+
- [2.2.0] Add ```immutable``` feature
|
|
12
|
+
- [2.2.0] Lots of bug fixes
|
|
13
|
+
|
|
14
|
+
## 2025-12-28
|
|
15
|
+
|
|
16
|
+
- [2.2.0] Add ```calcAggregate()``` & ```calcHistogram()``` for array of data objects
|
|
17
|
+
- [2.2.0] Implement ```createAggregate()``` & ```createHistogram()``` to the built-in memory database driver
|
|
18
|
+
- [2.2.0] If no ```default``` connection found, all models automatically bound to ```memory``` connection
|
|
19
|
+
|
|
20
|
+
## 2025-12-22
|
|
21
|
+
|
|
22
|
+
- [2.2.0] Introduce Action class that enables you to work on models with chainable methods
|
|
23
|
+
- [2.2.0] All base class definitions moved now to ```this.baseClass``` instead of ```this.lib```
|
|
24
|
+
|
|
25
|
+
## 2025-12-16
|
|
26
|
+
|
|
27
|
+
- [2.2.0] Upgrade mingo to 7.1.0
|
|
28
|
+
|
|
29
|
+
## 2025-12-14
|
|
30
|
+
|
|
31
|
+
- [2.2.0] Drop Model class, replace as Model class
|
|
32
|
+
|
|
33
|
+
## 2025-12-10
|
|
34
|
+
|
|
35
|
+
- [2.2.0] Rewrite the whole thing into class based modules: Connection, Driver, Feature, Model. Dobo will solely serve as DB Manager in the future
|
|
36
|
+
|
|
37
|
+
## 2025-12-05
|
|
38
|
+
- [2.2.0] Connection now saved in ```this.connections``` as ```Connection``` instance
|
|
39
|
+
|
|
40
|
+
## 2025-12-03
|
|
41
|
+
|
|
42
|
+
- [2.1.0] Upgrade joi to 18.0.2
|
|
43
|
+
- [2.1.0] Upgrade mingo to 7.0.2
|
|
44
|
+
- [2.1.0] Feature now saved in ```this.features``` as ```Feature``` instance
|
|
45
|
+
- [2.1.0] Driver now saved in ```this.drivers``` as ```Driver``` instance
|
|
46
|
+
- [2.1.0] Add ```this.getDriver()```. Accept short name or NsPath format
|
package/wiki/CONFIG.md
CHANGED
|
File without changes
|
package/wiki/CONTRIBUTING.md
CHANGED
|
File without changes
|
package/wiki/DEV-GUIDE.md
CHANGED
|
File without changes
|
package/wiki/ECOSYSTEM.md
CHANGED
|
File without changes
|
package/wiki/GETTING-STARTED.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
Here is some basic knowledge about Dobo you need to be familiar with:
|
|
8
8
|
|
|
9
9
|
- All record-related actions mimic REST API methods: *find* records, *get* a particular record by its ID, *create* a new record, *update* an existing record by ID and payload, and *remove* an existing record by its ID.
|
|
10
|
-
- A Dobo model requires a predefined
|
|
10
|
+
- A Dobo model requires a predefined model. Even if you use a NoSQL database, you still need to write a model.
|
|
11
11
|
- There are two main groups of methods to be familiar with:
|
|
12
12
|
- ```dobo.model{Action}``` methods manage everything related to model management, such as table creation or deletion.
|
|
13
13
|
- ```dobo.record{Action}``` methods handle record manipulation.
|
|
@@ -29,10 +29,10 @@ Don't forget to add ```dobo``` and ```dobo-knex``` to the ```data/config/.plugin
|
|
|
29
29
|
|
|
30
30
|
### Model
|
|
31
31
|
|
|
32
|
-
Let's pretend we're building an address book with fields like name, age, phone, etc. This entity needs to be modeled with a
|
|
32
|
+
Let's pretend we're building an address book with fields like name, age, phone, etc. This entity needs to be modeled with a model and then "connected" to a database:
|
|
33
33
|
|
|
34
|
-
1. Create ```main/extend/dobo/
|
|
35
|
-
2. Enter the following
|
|
34
|
+
1. Create ```main/extend/dobo/model/address-book.json``` file.
|
|
35
|
+
2. Enter the following model:
|
|
36
36
|
```json
|
|
37
37
|
{
|
|
38
38
|
"properties": [{
|
|
@@ -68,7 +68,7 @@ Let's pretend we're building an address book with fields like name, age, phone,
|
|
|
68
68
|
"phone": "+1-0000001"
|
|
69
69
|
}]
|
|
70
70
|
```
|
|
71
|
-
4. By default, all
|
|
71
|
+
4. By default, all models are connected to a database connection named ```default```. Now let's create this connection by creating ```data/config/dobo.json``` file:
|
|
72
72
|
|
|
73
73
|
```json
|
|
74
74
|
{
|
|
@@ -85,19 +85,19 @@ Let's pretend we're building an address book with fields like name, age, phone,
|
|
|
85
85
|
```
|
|
86
86
|
$ node index.js -a dobo:modelRebuild MainAddressBook
|
|
87
87
|
ℹ App runs in applet mode
|
|
88
|
-
╭
|
|
88
|
+
╭ Model (1) ──────╮
|
|
89
89
|
│ MainAddressBook │
|
|
90
90
|
╰──────────────────╯
|
|
91
|
-
✔ The above mentioned
|
|
91
|
+
✔ The above mentioned model(s) will be rebuilt as model. Continue? Yes
|
|
92
92
|
✔ Model 'MainAddressBook' successfully created
|
|
93
93
|
ℹ Done! Succeded: 1, failed: 0, skipped: 0
|
|
94
94
|
✔ Fixture on 'MainAddressBook': added 2, rejected: 0
|
|
95
95
|
```
|
|
96
96
|
6. Done!
|
|
97
97
|
|
|
98
|
-
Note: Although you can use YAML or TOML for
|
|
98
|
+
Note: Although you can use YAML or TOML for models/fixtures, it's recommended to stick with JSON because it's always supported and doesn't require an extra plugin.
|
|
99
99
|
|
|
100
|
-
Dobo models are by default always named with ```{Alias}{ModelName}```, which is a pascal-cased plugin alias and base name from your
|
|
100
|
+
Dobo models are by default always named with ```{Alias}{ModelName}```, which is a pascal-cased plugin alias and base name from your model file. For field names, Dobo use camel-cased names as a convention. You can change this behavior to match your needs, but it is suggested that you're keeping these conventions at least for this tutorial.
|
|
101
101
|
|
|
102
102
|
### Applets
|
|
103
103
|
|
|
@@ -160,7 +160,7 @@ $ node index.js -a dobo:recordCreate MainAddressBook
|
|
|
160
160
|
└───────────┴──────────────────────────┘
|
|
161
161
|
```
|
|
162
162
|
|
|
163
|
-
As you can see, Dobo is smart enough to reject any payload that isn't right. In this case, we forgot to include the phone number since according to the
|
|
163
|
+
As you can see, Dobo is smart enough to reject any payload that isn't right. In this case, we forgot to include the phone number since according to the model, this field is defined as required.
|
|
164
164
|
|
|
165
165
|
You can now try all of Dobo's other applets. [This page](https://github.com/ardhi/dobo/tutorials/applets) provides its complete list.
|
|
166
166
|
|
package/wiki/QUERY-LANGUAGE.md
CHANGED
|
File without changes
|
package/wiki/USER-GUIDE.md
CHANGED
|
File without changes
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import postProcess from './lib/post-process.js'
|
|
2
|
-
|
|
3
|
-
async function modelClear (path, ...args) {
|
|
4
|
-
const { print } = this.app.bajo
|
|
5
|
-
const { isEmpty } = this.app.lib._
|
|
6
|
-
if (isEmpty(this.schemas)) return print.fail('notFound%s', 'schema', { exit: this.app.applet })
|
|
7
|
-
const [schema] = args
|
|
8
|
-
await postProcess.call(this, { handler: path, params: [schema], path, processMsg: 'Clear records' })
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export default modelClear
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import addFixtures from '../../../lib/add-fixtures.js'
|
|
2
|
-
|
|
3
|
-
async function modelRebuild (path, ...args) {
|
|
4
|
-
const { importPkg } = this.app.bajo
|
|
5
|
-
const { outmatch } = this.app.lib
|
|
6
|
-
const { isEmpty, map, trim, without } = this.app.lib._
|
|
7
|
-
const [input, confirm, boxen] = await importPkg('bajoCli:@inquirer/input',
|
|
8
|
-
'bajoCli:@inquirer/confirm', 'bajoCli:boxen')
|
|
9
|
-
const schemas = map(this.schemas, 'name')
|
|
10
|
-
let names = args.join(' ')
|
|
11
|
-
if (isEmpty(schemas)) return this.print.fail('notFound%s', 'schema', { exit: this.app.applet })
|
|
12
|
-
if (isEmpty(names)) {
|
|
13
|
-
names = await input({
|
|
14
|
-
message: this.print.buildText('enterSchemaName'),
|
|
15
|
-
default: '*'
|
|
16
|
-
})
|
|
17
|
-
}
|
|
18
|
-
const isMatch = outmatch(map(names.split(' '), m => trim(m)))
|
|
19
|
-
names = schemas.filter(isMatch)
|
|
20
|
-
if (names.length === 0) return this.print.fail('No schema matched', true, { exit: this.app.applet })
|
|
21
|
-
console.log(boxen(names.join(' '), { title: this.t('schema%d', names.length), padding: 0.5, borderStyle: 'round' }))
|
|
22
|
-
const answer = await confirm({
|
|
23
|
-
message: this.print.buildText('schemasWillBeRebuiltContinue'),
|
|
24
|
-
default: false
|
|
25
|
-
})
|
|
26
|
-
if (!answer) return this.print.fail('aborted', { exit: this.app.applet })
|
|
27
|
-
/*
|
|
28
|
-
const conns = []
|
|
29
|
-
for (const s of names) {
|
|
30
|
-
const { connection } = this.getInfo(s)
|
|
31
|
-
if (!conns.includes(connection.name)) conns.push(connection.name)
|
|
32
|
-
}
|
|
33
|
-
*/
|
|
34
|
-
await this.start('all')
|
|
35
|
-
const result = { succed: 0, failed: 0, skipped: 0 }
|
|
36
|
-
const skipped = []
|
|
37
|
-
for (const s of names) {
|
|
38
|
-
const { schema, instance } = this.getInfo(s)
|
|
39
|
-
const spin = this.print.spinner().start('rebuilding%s', schema.name)
|
|
40
|
-
if (!instance) {
|
|
41
|
-
spin.warn('clientInstanceNotConnected%s', schema.connection, schema.name)
|
|
42
|
-
skipped.push(schema.name)
|
|
43
|
-
result.skipped++
|
|
44
|
-
continue
|
|
45
|
-
}
|
|
46
|
-
/*
|
|
47
|
-
if (connection.memory) {
|
|
48
|
-
spin.warn('memoryDbSkipped%s', schema.name)
|
|
49
|
-
continue
|
|
50
|
-
}
|
|
51
|
-
*/
|
|
52
|
-
const exists = await this.modelExists(schema.name, false, { spinner: spin })
|
|
53
|
-
if (exists) {
|
|
54
|
-
if (this.app.bajo.config.force) {
|
|
55
|
-
try {
|
|
56
|
-
await this.modelDrop(schema.name, { spinner: spin })
|
|
57
|
-
spin.setText('modelDropped%s', schema.name)
|
|
58
|
-
} catch (err) {
|
|
59
|
-
spin.fail('errorDroppingModel%s%s', schema.name, err.message)
|
|
60
|
-
result.failed++
|
|
61
|
-
continue
|
|
62
|
-
}
|
|
63
|
-
} else {
|
|
64
|
-
spin.fail('modelExistsNeedForce%s', schema.name)
|
|
65
|
-
result.failed++
|
|
66
|
-
continue
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
try {
|
|
70
|
-
await this.modelCreate(schema.name, { spinner: spin })
|
|
71
|
-
spin.succeed('modelCreated%s', schema.name)
|
|
72
|
-
result.succed++
|
|
73
|
-
} catch (err) {
|
|
74
|
-
if (this.app.bajo.config.log.applet && this.app.bajo.config.log.level === 'trace') console.error(err)
|
|
75
|
-
spin.fail('errorCreatingModel%s%s', schema.name, err.message)
|
|
76
|
-
result.failed++
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
this.print.info('succeedFailSkip%d%d%d', result.succed, result.failed, result.skipped)
|
|
80
|
-
if (result.failed > 0) this.print.fatal('cantContinueAddFixture')
|
|
81
|
-
for (const s of without(names, ...skipped)) {
|
|
82
|
-
const { schema, connection } = this.getInfo(s)
|
|
83
|
-
const spin = this.print.spinner().start('addingFixture%s', schema.name)
|
|
84
|
-
if (connection.memory) {
|
|
85
|
-
spin.warn('memoryDbSkipped%s', schema.name)
|
|
86
|
-
continue
|
|
87
|
-
}
|
|
88
|
-
try {
|
|
89
|
-
const fixture = await addFixtures.call(this, schema.name, { spinner: spin })
|
|
90
|
-
spin.succeed('fixtureAdded%s%s%s', schema.name, fixture.success, fixture.failed)
|
|
91
|
-
result.succed++
|
|
92
|
-
} catch (err) {
|
|
93
|
-
if (this.app.bajo.config.log.applet && this.app.bajo.config.log.level === 'trace') console.error(err)
|
|
94
|
-
spin.fail('errorAddingFixture%s%s', schema.name, err.message)
|
|
95
|
-
result.failed++
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
this.app.exit()
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export default modelRebuild
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import postProcess from './lib/post-process.js'
|
|
2
|
-
|
|
3
|
-
async function createRecord (path, ...args) {
|
|
4
|
-
const { importPkg } = this.app.bajo
|
|
5
|
-
const { isEmpty, map, isPlainObject } = this.app.lib._
|
|
6
|
-
const [input, select, boxen] = await importPkg('bajoCli:@inquirer/input',
|
|
7
|
-
'bajoCli:@inquirer/select', 'bajoCli:boxen')
|
|
8
|
-
if (isEmpty(this.schemas)) return this.print.fail('notFound%s', this.t('field.schema'), { exit: this.app.applet })
|
|
9
|
-
let [schema, body] = args
|
|
10
|
-
if (isEmpty(schema)) {
|
|
11
|
-
schema = await select({
|
|
12
|
-
message: this.print.buildText('selectSchema'),
|
|
13
|
-
choices: map(this.schemas, s => ({ value: s.name }))
|
|
14
|
-
})
|
|
15
|
-
}
|
|
16
|
-
if (isEmpty(body)) {
|
|
17
|
-
body = await input({
|
|
18
|
-
message: this.print.buildText('enterJsonPayload'),
|
|
19
|
-
validate: text => {
|
|
20
|
-
if (isEmpty(text)) return this.t('payloadRequired')
|
|
21
|
-
try {
|
|
22
|
-
const parsed = JSON.parse(text)
|
|
23
|
-
if (!isPlainObject(parsed)) throw new Error()
|
|
24
|
-
} catch (err) {
|
|
25
|
-
return this.t('payloadMustBeJson')
|
|
26
|
-
}
|
|
27
|
-
return true
|
|
28
|
-
}
|
|
29
|
-
})
|
|
30
|
-
}
|
|
31
|
-
let payload
|
|
32
|
-
try {
|
|
33
|
-
payload = JSON.parse(body)
|
|
34
|
-
} catch (err) {
|
|
35
|
-
return this.print.fail('invalidPayloadSyntax', { exit: this.app.applet })
|
|
36
|
-
}
|
|
37
|
-
console.log(boxen(JSON.stringify(payload, null, 2), { title: schema, padding: 0.5, borderStyle: 'round' }))
|
|
38
|
-
const result = await postProcess.call(this, { handler: 'recordCreate', params: [schema, payload], path, processMsg: 'Creating record' })
|
|
39
|
-
if (!result) await createRecord.call(this, path, ...args)
|
|
40
|
-
this.app.exit()
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export default createRecord
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import postProcess from './lib/post-process.js'
|
|
2
|
-
|
|
3
|
-
async function findRecord (path, ...args) {
|
|
4
|
-
const { importPkg } = this.app.bajo
|
|
5
|
-
const { isEmpty, map, pick } = this.app.lib._
|
|
6
|
-
const [select, input] = await importPkg('bajoCli:@inquirer/select', 'bajoCli:@inquirer/input')
|
|
7
|
-
if (isEmpty(this.schemas)) return this.print.fail('notFound%s', this.t('field.schema'), { exit: this.app.applet })
|
|
8
|
-
let [schema, query] = args
|
|
9
|
-
if (isEmpty(schema)) {
|
|
10
|
-
schema = await select({
|
|
11
|
-
message: this.print.buildText('selectSchema'),
|
|
12
|
-
choices: map(this.schemas, s => ({ value: s.name }))
|
|
13
|
-
})
|
|
14
|
-
}
|
|
15
|
-
if (isEmpty(query)) {
|
|
16
|
-
query = await input({
|
|
17
|
-
message: this.print.buildText('enterQueryIfAny')
|
|
18
|
-
})
|
|
19
|
-
}
|
|
20
|
-
if (isEmpty(query)) query = {}
|
|
21
|
-
const filter = pick(this.app.bajo.config, ['page', 'offset', 'pageSize', 'sort', 'limit'])
|
|
22
|
-
filter.pageSize = filter.pageSize ?? filter.limit
|
|
23
|
-
filter.query = query
|
|
24
|
-
const resp = await postProcess.call(this, { noConfirmation: true, handler: 'recordFind', params: [schema, filter], path, processMsg: 'Finding record(s)' })
|
|
25
|
-
if (!resp) await findRecord.call(this, path, ...args)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export default findRecord
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import postProcess from './lib/post-process.js'
|
|
2
|
-
|
|
3
|
-
async function getRecord (path, ...args) {
|
|
4
|
-
const { importPkg } = this.app.bajo
|
|
5
|
-
const { isEmpty, map } = this.app.lib._
|
|
6
|
-
const [input, select] = await importPkg('bajoCli:@inquirer/input', 'bajoCli:@inquirer/select')
|
|
7
|
-
if (isEmpty(this.schemas)) return this.print.fail('notFound%s', this.t('field.schema'), { exit: this.app.applet })
|
|
8
|
-
let [schema, id] = args
|
|
9
|
-
if (isEmpty(schema)) {
|
|
10
|
-
schema = await select({
|
|
11
|
-
message: this.print.buildText('Please select a schema:'),
|
|
12
|
-
choices: map(this.schemas, s => ({ value: s.name }))
|
|
13
|
-
})
|
|
14
|
-
}
|
|
15
|
-
if (isEmpty(id)) {
|
|
16
|
-
id = await input({
|
|
17
|
-
message: this.print.buildText('Enter record ID:'),
|
|
18
|
-
validate: text => isEmpty(text) ? this.t('ID is required') : true
|
|
19
|
-
})
|
|
20
|
-
}
|
|
21
|
-
await postProcess.call(this, { noConfirmation: true, handler: 'recordGet', params: [schema, id], path, processMsg: 'Getting record' })
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export default getRecord
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import postProcess from './lib/post-process.js'
|
|
2
|
-
|
|
3
|
-
async function removeRecord (path, ...args) {
|
|
4
|
-
const { importPkg } = this.app.bajo
|
|
5
|
-
const { isEmpty, map } = this.app.lib._
|
|
6
|
-
const [input, select] = await importPkg('bajoCli:@inquirer/input', 'bajoCli:@inquirer/select')
|
|
7
|
-
if (isEmpty(this.schemas)) return this.print.fail('notFound%s', this.t('field.schema'), { exit: this.app.applet })
|
|
8
|
-
let [schema, id] = args
|
|
9
|
-
if (isEmpty(schema)) {
|
|
10
|
-
schema = await select({
|
|
11
|
-
message: this.print.buildText('Please select a schema:'),
|
|
12
|
-
choices: map(this.schemas, s => ({ value: s.name }))
|
|
13
|
-
})
|
|
14
|
-
}
|
|
15
|
-
if (isEmpty(id)) {
|
|
16
|
-
id = await input({
|
|
17
|
-
message: this.print.buildText('Enter record ID:'),
|
|
18
|
-
validate: text => isEmpty(text) ? this.t('ID is required') : true
|
|
19
|
-
})
|
|
20
|
-
}
|
|
21
|
-
await postProcess.call(this, { handler: 'recordRemove', params: [schema, id], path, processMsg: 'Removing record' })
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export default removeRecord
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import postProcess from './lib/post-process.js'
|
|
2
|
-
|
|
3
|
-
async function updateRecord (path, ...args) {
|
|
4
|
-
const { importPkg } = this.app.bajo
|
|
5
|
-
const { isEmpty, map, isPlainObject } = this.app.lib._
|
|
6
|
-
const [input, select, boxen] = await importPkg('bajoCli:@inquirer/input',
|
|
7
|
-
'bajoCli:@inquirer/select', 'bajoCli:boxen')
|
|
8
|
-
if (isEmpty(this.schemas)) return this.print.fail('notFound%s', this.t('field.schema'), { exit: this.app.applet })
|
|
9
|
-
let [schema, id, body] = args
|
|
10
|
-
if (isEmpty(schema)) {
|
|
11
|
-
schema = await select({
|
|
12
|
-
message: this.print.buildText('selectSchema'),
|
|
13
|
-
choices: map(this.schemas, s => ({ value: s.name }))
|
|
14
|
-
})
|
|
15
|
-
}
|
|
16
|
-
if (isEmpty(id)) {
|
|
17
|
-
id = await input({
|
|
18
|
-
message: this.print.buildText('enterRecordId'),
|
|
19
|
-
validate: text => isEmpty(text) ? this.t('idIsRequired') : true
|
|
20
|
-
})
|
|
21
|
-
}
|
|
22
|
-
if (isEmpty(body)) {
|
|
23
|
-
body = await input({
|
|
24
|
-
message: this.print.buildText('enterJsonPayload'),
|
|
25
|
-
validate: text => {
|
|
26
|
-
if (isEmpty(text)) return this.t('payloadRequired')
|
|
27
|
-
try {
|
|
28
|
-
const parsed = JSON.parse(text)
|
|
29
|
-
if (!isPlainObject(parsed)) throw new Error()
|
|
30
|
-
} catch (err) {
|
|
31
|
-
return this.t('payloadMustBeJson')
|
|
32
|
-
}
|
|
33
|
-
return true
|
|
34
|
-
}
|
|
35
|
-
})
|
|
36
|
-
}
|
|
37
|
-
let payload
|
|
38
|
-
try {
|
|
39
|
-
payload = JSON.parse(body)
|
|
40
|
-
} catch (err) {
|
|
41
|
-
return this.print.fail('invalidPayloadSyntax', { exit: this.app.applet })
|
|
42
|
-
}
|
|
43
|
-
console.log(boxen(JSON.stringify(payload, null, 2), { title: schema, padding: 0.5, borderStyle: 'round' }))
|
|
44
|
-
await postProcess.call(this, { handler: 'recordUpdate', params: [schema, id, payload], path, processMsg: 'Updating record' })
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export default updateRecord
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
async function schema (path, ...args) {
|
|
2
|
-
const { importPkg } = this.app.bajo
|
|
3
|
-
const { isEmpty, map, find } = this.app.lib._
|
|
4
|
-
const { getOutputFormat, writeOutput } = this.app.bajoCli
|
|
5
|
-
const select = await importPkg('bajoCli:@inquirer/select')
|
|
6
|
-
const format = getOutputFormat()
|
|
7
|
-
if (isEmpty(this.schemas)) return this.print.fail('notFound%s', this.t('field.schema'), { exit: this.app.applet })
|
|
8
|
-
let name = args[0]
|
|
9
|
-
if (isEmpty(name)) {
|
|
10
|
-
const choices = map(this.schemas, s => ({ value: s.name }))
|
|
11
|
-
name = await select({
|
|
12
|
-
message: this.print.buildText('selectSchema'),
|
|
13
|
-
choices
|
|
14
|
-
})
|
|
15
|
-
}
|
|
16
|
-
const result = find(this.schemas, { name })
|
|
17
|
-
if (!result) return this.print.fail('cantFindSchema%s', this.t('schema'), name, { exit: this.app.applet })
|
|
18
|
-
this.print.info('done')
|
|
19
|
-
await writeOutput(result, path, format)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export default schema
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import postProcess from './lib/post-process.js'
|
|
2
|
-
|
|
3
|
-
async function statCount (path, ...args) {
|
|
4
|
-
const { importPkg } = this.app.bajo
|
|
5
|
-
const { isEmpty, map } = this.app.lib._
|
|
6
|
-
const [select, input] = await importPkg('bajoCli:@inquirer/select', 'bajoCli:@inquirer/input')
|
|
7
|
-
if (isEmpty(this.schemas)) return this.print.fail('notFound%s', this.t('field.schema'), { exit: this.app.applet })
|
|
8
|
-
let [schema, query] = args
|
|
9
|
-
if (isEmpty(schema)) {
|
|
10
|
-
schema = await select({
|
|
11
|
-
message: this.print.buildText('selectSchema'),
|
|
12
|
-
choices: map(this.schemas, s => ({ value: s.name }))
|
|
13
|
-
})
|
|
14
|
-
}
|
|
15
|
-
if (isEmpty(query)) {
|
|
16
|
-
query = await input({
|
|
17
|
-
message: this.print.buildText('enterQueryIfAny')
|
|
18
|
-
})
|
|
19
|
-
}
|
|
20
|
-
const filter = { query }
|
|
21
|
-
await postProcess.call(this, { noConfirmation: true, handler: 'statCount', params: [schema, filter], path, processMsg: 'Counting record(s)' })
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export default statCount
|
package/lib/build-bulk-action.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
async function buildBulkAction (name, action, options = {}) {
|
|
2
|
-
const { fs, importModule } = this.app.bajo
|
|
3
|
-
const { camelCase } = this.app.lib._
|
|
4
|
-
const { schema, driver, connection } = await this.getInfo(name)
|
|
5
|
-
if (!options.force && (schema.disabled ?? []).includes(action)) throw this.error('methodIsDisabled%s%s', camelCase('bulk ' + action), name)
|
|
6
|
-
const file = `${driver.plugin}:/extend/${this.ns}/method/bulk/${action}.js`
|
|
7
|
-
if (!fs.existsSync(file)) throw this.error('methodUnsupported%s%s', camelCase('bulk ' + action), name)
|
|
8
|
-
const handler = await importModule(file)
|
|
9
|
-
return { handler, schema, driver, connection }
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export default buildBulkAction
|
package/lib/check-unique.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
async function checkUnique ({ schema, body, id }) {
|
|
2
|
-
const { isSet } = this.app.lib.aneka
|
|
3
|
-
const { filter, map, set } = this.app.lib._
|
|
4
|
-
const singles = map(filter(schema.properties, p => (p.index ?? {}).type === 'unique'), 'name')
|
|
5
|
-
const opts = { noHook: true, noCache: true, thrownNotFound: false, forceNoHidden: true }
|
|
6
|
-
let old = {}
|
|
7
|
-
if (id) old = (await this.recordGet(schema.name, id, opts)) ?? {}
|
|
8
|
-
for (const s of singles) {
|
|
9
|
-
if (!isSet(body[s])) continue
|
|
10
|
-
if (id && body[s] === old[s]) continue
|
|
11
|
-
const query = set({}, s, body[s])
|
|
12
|
-
const resp = await this.recordFind(schema.name, { query, limit: 1 }, opts)
|
|
13
|
-
if (resp.length !== 0) {
|
|
14
|
-
const details = [{ field: s, error: 'uniqueConstraintError', value: id }]
|
|
15
|
-
throw this.error('uniqueConstraintError', { details, body })
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
const multis = filter(schema.indexes, i => i.type === 'unique')
|
|
19
|
-
for (const m of multis) {
|
|
20
|
-
const query = {}
|
|
21
|
-
let empty = true
|
|
22
|
-
let same = true
|
|
23
|
-
for (const f of m.fields) {
|
|
24
|
-
if (body[f]) empty = false
|
|
25
|
-
if (body[f] !== old[f]) same = false
|
|
26
|
-
query[f] = body[f]
|
|
27
|
-
}
|
|
28
|
-
if (empty || same) continue
|
|
29
|
-
const resp = await this.recordFind(schema.name, { query, limit: 1 }, { noHook: true, noCache: true, forceNoHidden: true })
|
|
30
|
-
if (resp.length !== 0) {
|
|
31
|
-
const details = map(m.fields, f => {
|
|
32
|
-
return { field: f, error: 'Unique constraint error' }
|
|
33
|
-
})
|
|
34
|
-
throw this.error('Unique constraint error', { details, body })
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export default checkUnique
|
package/lib/collect-schemas.js
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import path from 'path'
|
|
2
|
-
import sanitizeSchema from './sanitize-schema.js'
|
|
3
|
-
|
|
4
|
-
async function handler ({ file }) {
|
|
5
|
-
const { ns, alias } = this
|
|
6
|
-
const { readConfig, eachPlugins } = this.app.bajo
|
|
7
|
-
const { pascalCase } = this.app.lib.aneka
|
|
8
|
-
const { get, isPlainObject, each, find, has, isArray, forOwn, isString, merge } = this.app.lib._
|
|
9
|
-
const { fastGlob } = this.app.lib
|
|
10
|
-
|
|
11
|
-
const base = path.basename(file, path.extname(file))
|
|
12
|
-
const defName = pascalCase(`${alias} ${base}`)
|
|
13
|
-
const mod = await readConfig(file, { ns, ignoreError: true })
|
|
14
|
-
if (!isPlainObject(mod)) this.fatal('invalidSchema%s', defName)
|
|
15
|
-
const forcedConn = get(this, `app.${ns}.config.dobo.schemaConnection.${base}`)
|
|
16
|
-
if (forcedConn) mod.connection = forcedConn
|
|
17
|
-
if (!mod.connection) mod.connection = 'default'
|
|
18
|
-
mod.name = mod.name ?? defName
|
|
19
|
-
mod.file = file
|
|
20
|
-
mod.ns = ns
|
|
21
|
-
mod.attachment = mod.attachment ?? true
|
|
22
|
-
mod.feature = mod.feature ?? []
|
|
23
|
-
mod.buildLevel = mod.buildLevel ?? 999
|
|
24
|
-
const feats = []
|
|
25
|
-
if (isArray(mod.feature)) {
|
|
26
|
-
each(mod.feature, f => {
|
|
27
|
-
if (isString(f)) feats.push({ name: f })
|
|
28
|
-
else if (isPlainObject(f)) feats.push(f)
|
|
29
|
-
})
|
|
30
|
-
} else if (isPlainObject(mod.feature)) {
|
|
31
|
-
forOwn(mod.feature, (v, k) => {
|
|
32
|
-
feats.push(merge({}, v, { name: k }))
|
|
33
|
-
})
|
|
34
|
-
}
|
|
35
|
-
mod.feature = feats
|
|
36
|
-
mod.properties = mod.properties ?? []
|
|
37
|
-
// if ((mod.properties ?? []).length === 0) this.fatal('noPropsFoundOnSchema%s', mod.name)
|
|
38
|
-
// schema extender
|
|
39
|
-
await eachPlugins(async function ({ dir }) {
|
|
40
|
-
const { ns } = this
|
|
41
|
-
const glob = `${dir}/extend/dobo/extend/${mod.ns}/schema/${base}.*`
|
|
42
|
-
const files = await fastGlob(glob)
|
|
43
|
-
for (const file of files) {
|
|
44
|
-
const extender = await readConfig(file, { ns, ignoreError: true })
|
|
45
|
-
if (!isPlainObject(extender)) return undefined
|
|
46
|
-
each(extender.properties ?? [], p => {
|
|
47
|
-
if (isString(p) && mod.properties.includes(p)) return undefined
|
|
48
|
-
else if (find(mod.properties, { name: p.name })) return undefined
|
|
49
|
-
mod.properties.push(p)
|
|
50
|
-
})
|
|
51
|
-
const feats = []
|
|
52
|
-
if (isArray(extender.feature)) {
|
|
53
|
-
each(extender.feature, f => {
|
|
54
|
-
if (isString(f)) feats.push({ name: f })
|
|
55
|
-
else if (isPlainObject(f)) feats.push(f)
|
|
56
|
-
})
|
|
57
|
-
} else if (isPlainObject(extender.feature)) {
|
|
58
|
-
forOwn(extender.feature, (v, k) => {
|
|
59
|
-
feats.push(merge({}, v, { name: k }))
|
|
60
|
-
})
|
|
61
|
-
}
|
|
62
|
-
if (feats.length > 0) mod.feature.push(...feats)
|
|
63
|
-
if (ns === this.app.mainNs) {
|
|
64
|
-
each(['connection', 'name'], i => {
|
|
65
|
-
if (has(extender, i)) mod[i] = extender[i]
|
|
66
|
-
})
|
|
67
|
-
}
|
|
68
|
-
mod.extender = mod.extender ?? []
|
|
69
|
-
mod.extender.push(ns)
|
|
70
|
-
}
|
|
71
|
-
})
|
|
72
|
-
return mod
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Collect all database schemas from loaded plugins
|
|
77
|
-
*
|
|
78
|
-
* @name collectSchemas
|
|
79
|
-
* @memberof module:Lib
|
|
80
|
-
* @async
|
|
81
|
-
* @see Dobo#init
|
|
82
|
-
*/
|
|
83
|
-
async function collectSchemas () {
|
|
84
|
-
const { eachPlugins } = this.app.bajo
|
|
85
|
-
const { isEmpty } = this.app.lib._
|
|
86
|
-
const result = await eachPlugins(handler, { glob: 'schema/*.*', prefix: this.ns })
|
|
87
|
-
if (isEmpty(result)) this.log.warn('notFound%s', this.t('schema'))
|
|
88
|
-
else await sanitizeSchema.call(this, result)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export default collectSchemas
|
package/lib/exec-feature-hook.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
async function execFeatureHook (name, params = {}) {
|
|
2
|
-
const { get } = this.app.lib._
|
|
3
|
-
const { schema } = params
|
|
4
|
-
for (const f of schema.feature) {
|
|
5
|
-
const fn = get(this.feature, f.name)
|
|
6
|
-
if (!fn) continue
|
|
7
|
-
const input = await fn.call(this, f)
|
|
8
|
-
const hook = get(input, 'hook.' + name)
|
|
9
|
-
if (hook) await hook.call(this, params)
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export default execFeatureHook
|
package/lib/exec-validation.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
async function execValidation ({ name, body, options, partial }) {
|
|
2
|
-
const { runHook } = this.app.bajo
|
|
3
|
-
const { keys, camelCase } = this.app.lib._
|
|
4
|
-
const { noHook } = options
|
|
5
|
-
if (!noHook) {
|
|
6
|
-
await runHook(`${this.ns}:beforeRecordValidation`, name, body, options)
|
|
7
|
-
await runHook(`${this.ns}.${camelCase(name)}:beforeRecordValidation`, body, options)
|
|
8
|
-
}
|
|
9
|
-
const { validation = {} } = options
|
|
10
|
-
if (partial) {
|
|
11
|
-
validation.fields = keys(body)
|
|
12
|
-
}
|
|
13
|
-
body = await this.validate(body, name, validation)
|
|
14
|
-
if (!noHook) {
|
|
15
|
-
await runHook(`${this.ns}:afterRecordValidation`, name, body, options)
|
|
16
|
-
await runHook(`${this.ns}.${camelCase(name)}:afterRecordValidation`, body, options)
|
|
17
|
-
}
|
|
18
|
-
return body
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export default execValidation
|