dobo 2.0.0 → 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 +13 -0
- package/.github/workflows/repo-lockdown.yml +24 -0
- package/.jsdoc.conf.json +45 -0
- package/LICENSE +1 -1
- package/README.md +38 -19
- package/docs/Dobo.html +26 -0
- package/docs/data/search.json +1 -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 +7 -0
- package/docs/index.html +3 -0
- package/docs/index.js.html +578 -0
- package/docs/lib_collect-connections.js.html +39 -0
- package/docs/lib_collect-drivers.js.html +52 -0
- package/docs/lib_collect-features.js.html +36 -0
- package/docs/lib_collect-schemas.js.html +94 -0
- package/docs/lib_index.js.html +6 -0
- package/docs/method_model_create.js.html +35 -0
- package/docs/method_model_drop.js.html +34 -0
- package/docs/method_model_exists.js.html +40 -0
- package/docs/method_record_count.js.html +69 -0
- package/docs/method_record_create.js.html +114 -0
- package/docs/method_record_find-all.js.html +44 -0
- package/docs/method_record_find-one.js.html +73 -0
- package/docs/method_record_find.js.html +118 -0
- package/docs/method_record_get.js.html +92 -0
- package/docs/method_record_remove.js.html +75 -0
- package/docs/method_record_update.js.html +107 -0
- package/docs/method_record_upsert.js.html +54 -0
- package/docs/method_sanitize_body.js.html +88 -0
- package/docs/method_sanitize_date.js.html +30 -0
- package/docs/method_sanitize_id.js.html +20 -0
- package/docs/method_validate.js.html +249 -0
- package/docs/module-Lib.html +3 -0
- package/docs/scripts/core.js +725 -0
- package/docs/scripts/core.min.js +23 -0
- package/docs/scripts/resize.js +90 -0
- package/docs/scripts/search.js +265 -0
- package/docs/scripts/search.min.js +6 -0
- package/docs/scripts/third-party/Apache-License-2.0.txt +202 -0
- package/docs/scripts/third-party/fuse.js +9 -0
- package/docs/scripts/third-party/hljs-line-num-original.js +366 -0
- package/docs/scripts/third-party/hljs-line-num.js +1 -0
- package/docs/scripts/third-party/hljs-original.js +5164 -0
- package/docs/scripts/third-party/hljs.js +1 -0
- package/docs/scripts/third-party/popper.js +5 -0
- package/docs/scripts/third-party/tippy.js +1 -0
- package/docs/scripts/third-party/tocbot.js +671 -0
- package/docs/scripts/third-party/tocbot.min.js +1 -0
- package/docs/static/bitcoin.jpeg +0 -0
- package/docs/static/home.md +25 -0
- package/docs/static/logo-ecosystem.png +0 -0
- package/docs/static/logo.png +0 -0
- package/docs/styles/clean-jsdoc-theme-base.css +1159 -0
- package/docs/styles/clean-jsdoc-theme-dark.css +412 -0
- package/docs/styles/clean-jsdoc-theme-light.css +482 -0
- package/docs/styles/clean-jsdoc-theme-scrollbar.css +30 -0
- package/docs/styles/clean-jsdoc-theme-without-scrollbar.min.css +1 -0
- package/docs/styles/clean-jsdoc-theme.min.css +1 -0
- package/extend/bajo/intl/en-US.json +69 -30
- package/extend/bajo/intl/id.json +58 -29
- package/extend/bajoCli/applet/clear-record.js +22 -0
- package/extend/bajoCli/applet/connection.js +5 -5
- 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 +25 -26
- 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 +10 -8
- 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 +35 -57
- package/extend/dobo/feature/updated-at.js +14 -12
- package/extend/waibuMpa/route/attachment/@model/@id/@field/@file.js +5 -9
- package/extend/waibuStatic/virtual.json +0 -0
- package/index.js +420 -337
- package/lib/collect-connections.js +60 -21
- package/lib/collect-drivers.js +29 -35
- package/lib/collect-features.js +40 -0
- 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/lib/factory/model/find-record.js +103 -0
- 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/lib/factory/model/validate.js +232 -0
- package/lib/factory/model.js +150 -0
- package/lib/index.js +3 -0
- package/package.json +45 -36
- package/wiki/APPLETS.md +57 -0
- package/wiki/CHANGES.md +46 -0
- package/wiki/CONFIG.md +25 -0
- package/wiki/CONTRIBUTING.md +5 -0
- package/wiki/DEV-GUIDE.md +1 -0
- package/wiki/ECOSYSTEM.md +20 -0
- package/wiki/GETTING-STARTED.md +166 -0
- package/{docs/query-language.md → wiki/QUERY-LANGUAGE.md} +0 -0
- package/wiki/USER-GUIDE.md +1 -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 -41
- package/extend/bajoCli/applet/record-find.js +0 -27
- 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-feature.js +0 -25
- package/lib/collect-schemas.js +0 -83
- package/lib/exec-feature-hook.js +0 -13
- package/lib/exec-validation.js +0 -21
- package/lib/generic-prop-sanitizer.js +0 -31
- 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 -197
- 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 -19
- package/method/model/drop.js +0 -19
- package/method/model/exists.js +0 -24
- package/method/record/clear.js +0 -24
- package/method/record/count.js +0 -44
- package/method/record/create.js +0 -71
- package/method/record/find-all.js +0 -25
- package/method/record/find-one.js +0 -56
- package/method/record/find.js +0 -52
- package/method/record/get.js +0 -47
- package/method/record/remove.js +0 -41
- package/method/record/update.js +0 -63
- package/method/record/upsert.js +0 -35
- package/method/sanitize/body.js +0 -70
- package/method/sanitize/date.js +0 -14
- package/method/sanitize/id.js +0 -7
- package/method/stat/aggregate.js +0 -23
- package/method/stat/histogram.js +0 -26
- package/method/validate.js +0 -157
|
@@ -1,18 +1,16 @@
|
|
|
1
|
-
const conns = []
|
|
2
|
-
|
|
3
1
|
async function postProcess ({ handler, params, path, processMsg, noConfirmation } = {}) {
|
|
4
|
-
const {
|
|
5
|
-
const {
|
|
6
|
-
const {
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
params.push({ fields: this.config.fields, dataOnly: !this.config.full })
|
|
2
|
+
const { importPkg } = this.app.bajo
|
|
3
|
+
const { generateId } = this.app.lib.aneka
|
|
4
|
+
const { writeOutput } = this.app.bajoCli
|
|
5
|
+
const { get, isEmpty } = this.app.lib._
|
|
6
|
+
const confirm = await importPkg('bajoCli:@inquirer/confirm')
|
|
10
7
|
|
|
11
|
-
const
|
|
12
|
-
|
|
8
|
+
const name = params.shift()
|
|
9
|
+
const model = this.getModel(name)
|
|
10
|
+
if (!model) return this.print.fatal('notFound%s', this.t('field.model'))
|
|
13
11
|
let cont = true
|
|
14
12
|
if (!noConfirmation) {
|
|
15
|
-
const answer = await confirm({ message: this.print.
|
|
13
|
+
const answer = await confirm({ message: this.print.buildText('sureContinue'), default: false })
|
|
16
14
|
if (!answer) {
|
|
17
15
|
this.print.fail('aborted')
|
|
18
16
|
cont = false
|
|
@@ -20,28 +18,29 @@ async function postProcess ({ handler, params, path, processMsg, noConfirmation
|
|
|
20
18
|
}
|
|
21
19
|
if (!cont) return
|
|
22
20
|
const spin = this.print.spinner().start(`${processMsg}...`)
|
|
23
|
-
|
|
24
|
-
if (!conns.includes(connection.name)) {
|
|
25
|
-
await this.start(connection.name)
|
|
26
|
-
conns.push(connection.name)
|
|
27
|
-
}
|
|
21
|
+
await this.start([model.connection.name])
|
|
28
22
|
try {
|
|
29
|
-
const resp = await
|
|
23
|
+
const resp = await model[handler](...params)
|
|
24
|
+
if (isEmpty(resp)) {
|
|
25
|
+
spin.warn('noResultFound')
|
|
26
|
+
return false
|
|
27
|
+
}
|
|
30
28
|
spin.succeed('done')
|
|
31
|
-
|
|
32
|
-
if (this.config.save) {
|
|
33
|
-
const id = resp.id ?? get(resp, 'data.id') ?? get(resp, 'oldData.id')
|
|
29
|
+
let actionPath = path
|
|
30
|
+
if (this.app.bajo.config.save) {
|
|
31
|
+
const id = resp.id ?? get(resp, 'data.id') ?? get(resp, 'oldData.id') ?? generateId()
|
|
34
32
|
const base = path === 'recordFind' ? params[0] : (params[0] + '/' + id)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
actionPath += `/${base}`
|
|
34
|
+
}
|
|
35
|
+
await writeOutput(resp, actionPath, true)
|
|
36
|
+
return true
|
|
38
37
|
} catch (err) {
|
|
39
|
-
if (this.config.log.applet) {
|
|
38
|
+
if (this.app.bajo.config.log.applet) {
|
|
40
39
|
spin.stop()
|
|
41
40
|
console.error(err)
|
|
42
|
-
} else spin.fail('error%s', err.message)
|
|
41
|
+
} else spin.fail('error%s', err.detailsMessage ?? err.message)
|
|
42
|
+
return false
|
|
43
43
|
}
|
|
44
|
-
process.exit()
|
|
45
44
|
}
|
|
46
45
|
|
|
47
46
|
export default postProcess
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
async function model (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.models)) return this.print.fail('notFound%s', this.t('field.model'), { exit: this.app.applet })
|
|
8
|
+
let name = args[0]
|
|
9
|
+
if (isEmpty(name)) {
|
|
10
|
+
const choices = map(this.models, s => ({ value: s.name }))
|
|
11
|
+
name = await select({
|
|
12
|
+
message: this.print.buildText('selectModel'),
|
|
13
|
+
choices
|
|
14
|
+
})
|
|
15
|
+
}
|
|
16
|
+
const result = find(this.models, { name })
|
|
17
|
+
if (!result) return this.print.fail('cantFind%s%s', this.t('model'), name, { exit: this.app.applet })
|
|
18
|
+
this.print.info('done')
|
|
19
|
+
await writeOutput(result, path, format)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default model
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
async function modelRebuild (path, ...args) {
|
|
2
|
+
const { importPkg } = this.app.bajo
|
|
3
|
+
const { outmatch } = this.app.lib
|
|
4
|
+
const { isEmpty, map, trim, without } = this.app.lib._
|
|
5
|
+
const [input, confirm, boxen] = await importPkg('bajoCli:@inquirer/input',
|
|
6
|
+
'bajoCli:@inquirer/confirm', 'bajoCli:boxen')
|
|
7
|
+
const models = map(this.models, 'name')
|
|
8
|
+
let names = args.join(' ')
|
|
9
|
+
if (isEmpty(models)) return this.print.fail('notFound%s', 'model', { exit: this.app.applet })
|
|
10
|
+
if (isEmpty(names)) {
|
|
11
|
+
names = await input({
|
|
12
|
+
message: this.print.buildText('enterModelName'),
|
|
13
|
+
default: '*'
|
|
14
|
+
})
|
|
15
|
+
}
|
|
16
|
+
const isMatch = outmatch(map(names.split(' '), m => trim(m)))
|
|
17
|
+
names = models.filter(isMatch)
|
|
18
|
+
if (names.length === 0) return this.print.fail('No model matched', true, { exit: this.app.applet })
|
|
19
|
+
console.log(boxen(names.join(' '), { title: this.t('model%d', names.length), padding: 0.5, borderStyle: 'round' }))
|
|
20
|
+
const answer = await confirm({
|
|
21
|
+
message: this.print.buildText('modelsWillBeRebuiltContinue'),
|
|
22
|
+
default: false
|
|
23
|
+
})
|
|
24
|
+
if (!answer) return this.print.fail('aborted', { exit: this.app.applet })
|
|
25
|
+
/*
|
|
26
|
+
const conns = []
|
|
27
|
+
for (const s of names) {
|
|
28
|
+
const { connection } = this.getInfo(s)
|
|
29
|
+
if (!conns.includes(connection.name)) conns.push(connection.name)
|
|
30
|
+
}
|
|
31
|
+
*/
|
|
32
|
+
await this.start('all')
|
|
33
|
+
const result = { succed: 0, failed: 0, skipped: 0 }
|
|
34
|
+
const skipped = []
|
|
35
|
+
for (const s of names) {
|
|
36
|
+
const model = this.getModel(s)
|
|
37
|
+
const spin = this.print.spinner().start('rebuilding%s', model.name)
|
|
38
|
+
if (model.driver.memory) {
|
|
39
|
+
spin.warn('memoryDbSkipped%s', model.name)
|
|
40
|
+
continue
|
|
41
|
+
}
|
|
42
|
+
const exists = await model.exists({ spinner: spin })
|
|
43
|
+
if (exists) {
|
|
44
|
+
if (this.app.bajo.config.force) {
|
|
45
|
+
try {
|
|
46
|
+
await model.drop({ spinner: spin })
|
|
47
|
+
spin.setText('modelDropped%s', model.name)
|
|
48
|
+
} catch (err) {
|
|
49
|
+
spin.fail('errorDroppingModel%s%s', model.name, err.message)
|
|
50
|
+
result.failed++
|
|
51
|
+
continue
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
spin.fail('modelExistsNeedForce%s', model.name)
|
|
55
|
+
result.failed++
|
|
56
|
+
continue
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
await model.build({ spinner: spin })
|
|
61
|
+
spin.succeed('modelCreated%s', model.name)
|
|
62
|
+
result.succed++
|
|
63
|
+
} catch (err) {
|
|
64
|
+
if (this.app.bajo.config.log.applet) console.error(err)
|
|
65
|
+
spin.fail('errorCreatingModel%s%s', model.name, err.message)
|
|
66
|
+
result.failed++
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
this.print.info('succeedFailSkip%d%d%d', result.succed, result.failed, result.skipped)
|
|
70
|
+
if (result.failed > 0) this.print.fatal('cantContinueAddFixture')
|
|
71
|
+
for (const s of without(names, ...skipped)) {
|
|
72
|
+
const model = this.getModel(s)
|
|
73
|
+
const spin = this.print.spinner().start('addingFixture%s', model.name)
|
|
74
|
+
if (model.driver.memory) {
|
|
75
|
+
spin.warn('memoryDbSkipped%s', model.name)
|
|
76
|
+
continue
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
const fixture = await model.loadFixtures({ spinner: spin })
|
|
80
|
+
spin.succeed('fixtureAdded%s%s%s', model.name, fixture.success, fixture.failed)
|
|
81
|
+
result.succed++
|
|
82
|
+
} catch (err) {
|
|
83
|
+
if (this.app.bajo.config.log.applet) console.error(err)
|
|
84
|
+
spin.fail('errorAddingFixture%s%s', model.name, err.message)
|
|
85
|
+
result.failed++
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
this.app.exit()
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export default modelRebuild
|
|
@@ -0,0 +1,27 @@
|
|
|
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 { parseKvString } = this.app.lib.aneka
|
|
7
|
+
const [input, select] = await importPkg('bajoCli:@inquirer/input', 'bajoCli:@inquirer/select')
|
|
8
|
+
if (isEmpty(this.models)) return this.print.fail('notFound%s', this.t('field.model'), { exit: this.app.applet })
|
|
9
|
+
let [model, id, options] = args
|
|
10
|
+
options = isEmpty(options) ? {} : parseKvString(options)
|
|
11
|
+
if (isEmpty(model)) {
|
|
12
|
+
model = await select({
|
|
13
|
+
message: this.print.buildText('selectModel'),
|
|
14
|
+
choices: map(this.models, s => ({ value: s.name }))
|
|
15
|
+
})
|
|
16
|
+
}
|
|
17
|
+
if (isEmpty(id + '')) {
|
|
18
|
+
id = await input({
|
|
19
|
+
message: this.print.buildText('enterRecordId'),
|
|
20
|
+
validate: text => isEmpty(text) ? this.t('idIsRequired') : true
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
await postProcess.call(this, { noConfirmation: options.noConfirmation, handler: 'removeRecord', params: [model, id, options], path, processMsg: 'Removing record' })
|
|
24
|
+
this.app.exit()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default removeRecord
|
|
@@ -0,0 +1,44 @@
|
|
|
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 } = this.app.lib._
|
|
6
|
+
const { parseKvString } = this.app.lib.aneka
|
|
7
|
+
const [input, select, boxen] = await importPkg('bajoCli:@inquirer/input',
|
|
8
|
+
'bajoCli:@inquirer/select', 'bajoCli:boxen')
|
|
9
|
+
if (isEmpty(this.models)) return this.print.fail('notFound%s', this.t('field.model'), { exit: this.app.applet })
|
|
10
|
+
let [model, id, body, options] = args
|
|
11
|
+
options = isEmpty(options) ? {} : parseKvString(options)
|
|
12
|
+
if (isEmpty(model)) {
|
|
13
|
+
model = await select({
|
|
14
|
+
message: this.print.buildText('selectModel'),
|
|
15
|
+
choices: map(this.models, s => ({ value: s.name }))
|
|
16
|
+
})
|
|
17
|
+
}
|
|
18
|
+
if (isEmpty(id + '')) {
|
|
19
|
+
id = await input({
|
|
20
|
+
message: this.print.buildText('enterRecordId'),
|
|
21
|
+
validate: text => isEmpty(text) ? this.t('idIsRequired') : true
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
if (isEmpty(body)) {
|
|
25
|
+
body = await input({
|
|
26
|
+
message: this.print.buildText('enterPayload'),
|
|
27
|
+
validate: text => {
|
|
28
|
+
if (isEmpty(text)) return this.t('payloadRequired')
|
|
29
|
+
return true
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
let payload
|
|
34
|
+
try {
|
|
35
|
+
payload = body[0] === '{' ? JSON.parse(body) : parseKvString(body)
|
|
36
|
+
} catch (err) {
|
|
37
|
+
return this.print.fail('invalidPayloadSyntax', { exit: this.app.applet })
|
|
38
|
+
}
|
|
39
|
+
console.log(boxen(JSON.stringify(payload, null, 2), { title: model, padding: 0.5, borderStyle: 'round' }))
|
|
40
|
+
await postProcess.call(this, { noConfirmation: options.noConfirmation, handler: 'updateRecord', params: [model, id, payload, options], path, processMsg: 'Updating record' })
|
|
41
|
+
this.app.exit()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export default updateRecord
|
package/extend/bajoCli/applet.js
CHANGED
|
File without changes
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { Query } from 'mingo'
|
|
2
|
+
|
|
3
|
+
async function memoryDriverFactory () {
|
|
4
|
+
const { DoboDriver } = this.app.baseClass
|
|
5
|
+
const { findIndex, pullAt, omit, has } = this.app.lib._
|
|
6
|
+
const { defaultsDeep } = this.app.lib.aneka
|
|
7
|
+
|
|
8
|
+
class DoboMemoryDriver extends DoboDriver {
|
|
9
|
+
constructor (plugin, name, options) {
|
|
10
|
+
super(plugin, name, options)
|
|
11
|
+
this.idGenerator = 'ulid'
|
|
12
|
+
this.saving = true
|
|
13
|
+
this.memory = true
|
|
14
|
+
this.autoSave = []
|
|
15
|
+
this.storage = {}
|
|
16
|
+
this.support = {
|
|
17
|
+
propType: {
|
|
18
|
+
object: true,
|
|
19
|
+
array: true
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async _loadFromFile (model, dir) {
|
|
25
|
+
const { fs } = this.app.lib
|
|
26
|
+
this.autoSave.push(model.name)
|
|
27
|
+
const file = `${dir}/${model.name}.json`
|
|
28
|
+
if (!fs.existsSync(file)) return
|
|
29
|
+
try {
|
|
30
|
+
const data = fs.readFileSync(file, 'utf8')
|
|
31
|
+
this.storage[model.name] = JSON.parse(data)
|
|
32
|
+
} catch (err) {
|
|
33
|
+
this.fatal('cantLoad%s%s', model.name, err.message)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async sanitizeConnection (conn) {
|
|
38
|
+
await super.sanitizeConnection(conn)
|
|
39
|
+
conn.memory = true
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async connect (connection, noRebuild) {
|
|
43
|
+
const conn = this.plugin.getConnection('memory')
|
|
44
|
+
const models = this.plugin.getModelsByConnection(conn.name)
|
|
45
|
+
const { getPluginDataDir } = this.app.bajo
|
|
46
|
+
const { fs } = this.app.lib
|
|
47
|
+
const pdir = `${getPluginDataDir(this.plugin.ns)}/memDb/data` // persistence dir
|
|
48
|
+
fs.ensureDirSync(pdir)
|
|
49
|
+
conn.autoSave = conn.autoSave ?? []
|
|
50
|
+
for (const model of models) {
|
|
51
|
+
this.storage[model.name] = this.storage[model.name] ?? [] // init empty model
|
|
52
|
+
if (conn.autoSave.includes(model.name)) await this._loadFromFile(model, pdir)
|
|
53
|
+
await model.loadFixtures()
|
|
54
|
+
}
|
|
55
|
+
if (conn.autoSave.length === 0) return
|
|
56
|
+
setInterval(() => {
|
|
57
|
+
if (!this.saving) return
|
|
58
|
+
this.saving = true
|
|
59
|
+
for (const item of conn.autoSave) {
|
|
60
|
+
const data = this.storage[item]
|
|
61
|
+
fs.writeFileSync(`${pdir}/${item}.json`, JSON.stringify(data), 'utf8')
|
|
62
|
+
}
|
|
63
|
+
this.saving = false
|
|
64
|
+
}, this.plugin.config.memDb.autoSaveDur)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async _getOldRecord (model, id, options = {}) {
|
|
68
|
+
const idx = findIndex(this.storage[model.name], { _id: id })
|
|
69
|
+
const oldData = this.storage[model.name][idx]
|
|
70
|
+
return { idx, oldData }
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async modelExists (model, options = {}) {
|
|
74
|
+
return { data: has(this.storage, model.name) }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async buildModel (model, options = {}) {
|
|
78
|
+
if (has(this.storage, model.name)) throw this.plugin.error('exist%s%s', this.plugin.t('model'), model.name)
|
|
79
|
+
this.storage[model.name] = []
|
|
80
|
+
if (options.noResult) return
|
|
81
|
+
return { data: true }
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async dropModel (model, options = {}) {
|
|
85
|
+
if (!has(this.storage, model.name)) throw this.plugin.error('notFound%s%s', this.plugin.t('model'), model.name)
|
|
86
|
+
delete this.storage[model.name]
|
|
87
|
+
if (options.noResult) return
|
|
88
|
+
return { data: true }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async createRecord (model, body = {}, options = {}) {
|
|
92
|
+
this.storage[model.name].push(body)
|
|
93
|
+
return { data: body }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async getRecord (model, id, options = {}) {
|
|
97
|
+
const { oldData: data } = await this._getOldRecord(model, id)
|
|
98
|
+
return { data }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async updateRecord (model, id, body = {}, options = {}) {
|
|
102
|
+
const { idx, oldData } = await this._getOldRecord(model, id)
|
|
103
|
+
const data = defaultsDeep(body, oldData)
|
|
104
|
+
this.storage[model.name][idx] = data
|
|
105
|
+
return { oldData, data }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async removeRecord (model, id, options = {}) {
|
|
109
|
+
const { idx, oldData } = await this._getOldRecord(model, id)
|
|
110
|
+
pullAt(this.storage[model.name], idx)
|
|
111
|
+
return { oldData }
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async clearRecord (model, options = {}) {
|
|
115
|
+
this.storage[model.name] = []
|
|
116
|
+
if (options.noResult) return
|
|
117
|
+
return { data: true }
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async findRecord (model, filter = {}, options = {}) {
|
|
121
|
+
const { limit, skip, sort, page } = filter
|
|
122
|
+
const { data: count = 0 } = await this.countRecord(model, filter, options)
|
|
123
|
+
const cursor = this._getCursor(model, filter)
|
|
124
|
+
if (sort) cursor.sort(sort)
|
|
125
|
+
if (!options.noLimit) cursor.skip(skip).limit(limit)
|
|
126
|
+
let result = { data: cursor.all(), page, limit, count, pages: Math.ceil(count / limit) }
|
|
127
|
+
if (!options.count) result = omit(result, ['count', 'pages'])
|
|
128
|
+
return result
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async findAllRecord (model, filter = {}, options = {}) {
|
|
132
|
+
const { sort } = filter
|
|
133
|
+
const { data: count = 0 } = await this.countRecord(model, filter, options)
|
|
134
|
+
const cursor = this._getCursor(model, filter)
|
|
135
|
+
if (sort) cursor.sort(sort)
|
|
136
|
+
let result = { data: cursor.all(), count }
|
|
137
|
+
if (!options.count) result = omit(result, ['count'])
|
|
138
|
+
return result
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async countRecord (model, filter = {}, options = {}) {
|
|
142
|
+
const cursor = this._getCursor(model, filter)
|
|
143
|
+
const data = cursor.all().length
|
|
144
|
+
return { data }
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async createAggregate (model, filter = {}, params = {}, options = {}) {
|
|
148
|
+
const item = await this.findAllRecord(model, filter, options)
|
|
149
|
+
const result = this.app.dobo.calcAggregate({ data: item.data, ...params })
|
|
150
|
+
return { data: result }
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async createHistogram (model, filter = {}, params = {}, options = {}) {
|
|
154
|
+
const item = await this.findAllRecord(model, filter, options)
|
|
155
|
+
const result = this.app.dobo.calcHistogram({ data: item.data, ...params })
|
|
156
|
+
return { data: result }
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
_getCursor (model, filter) {
|
|
160
|
+
const criteria = filter.query ?? {}
|
|
161
|
+
const q = new Query(criteria, { idKey: '_id' })
|
|
162
|
+
return q.find(this.storage[model.name])
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
this.app.baseClass.DoboMemoryDriver = DoboMemoryDriver
|
|
167
|
+
return DoboMemoryDriver
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export default memoryDriverFactory
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
async function createdAt (opts = {}) {
|
|
2
2
|
opts.fieldName = opts.fieldName ?? 'createdAt'
|
|
3
|
+
opts.noOverwrite = opts.noOverwrite ?? false
|
|
3
4
|
return {
|
|
4
|
-
properties: {
|
|
5
|
+
properties: [{
|
|
5
6
|
name: opts.fieldName,
|
|
6
7
|
type: 'datetime',
|
|
7
8
|
index: true
|
|
8
|
-
},
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
if (opts.
|
|
9
|
+
}],
|
|
10
|
+
hooks: [{
|
|
11
|
+
name: 'beforeCreateRecord',
|
|
12
|
+
handler: async function (body, options) {
|
|
13
|
+
const { isSet } = this.app.lib.aneka
|
|
14
|
+
if (opts.noOverwrite) body[opts.fieldName] = new Date()
|
|
15
|
+
else if (!isSet(body[opts.fieldName])) body[opts.fieldName] = new Date()
|
|
14
16
|
}
|
|
15
|
-
}
|
|
17
|
+
}]
|
|
16
18
|
}
|
|
17
19
|
}
|
|
18
20
|
|
|
File without changes
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
async function beforeRemoveRecord (id, opts) {
|
|
2
|
+
const { get } = this.app.lib._
|
|
3
|
+
const record = await this.driver.getRecord(this, id)
|
|
4
|
+
const immutable = get(record.data, opts.fieldName)
|
|
5
|
+
if (immutable) throw this.plugin.error('recordImmutable%s%s', id, this.name, { statusCode: 423 })
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
async function immutable (opts = {}) {
|
|
9
|
+
opts.fieldName = opts.fieldName ?? '_immutable'
|
|
10
|
+
return {
|
|
11
|
+
properties: {
|
|
12
|
+
name: opts.fieldName,
|
|
13
|
+
type: 'boolean',
|
|
14
|
+
hidden: true
|
|
15
|
+
},
|
|
16
|
+
hooks: [{
|
|
17
|
+
name: 'beforeUpdateRecord',
|
|
18
|
+
handler: async function (id, body, options) {
|
|
19
|
+
await beforeRemoveRecord.call(this, id, opts)
|
|
20
|
+
}
|
|
21
|
+
}, {
|
|
22
|
+
name: 'beforeRemoveRecord',
|
|
23
|
+
handler: async function (id, options) {
|
|
24
|
+
await beforeRemoveRecord.call(this, id, opts)
|
|
25
|
+
}
|
|
26
|
+
}]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export default immutable
|
|
File without changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
async function
|
|
1
|
+
async function beforeFindRecord ({ filter = {} }, opts) {
|
|
2
2
|
filter.query = filter.query ?? {}
|
|
3
|
-
const { isEmpty, set } = this.lib._
|
|
3
|
+
const { isEmpty, set } = this.app.lib._
|
|
4
4
|
const q = { $and: [] }
|
|
5
5
|
if (!isEmpty(filter.query)) {
|
|
6
6
|
if (filter.query.$and) q.$and.push(...filter.query.$and)
|
|
@@ -10,75 +10,53 @@ async function beforeFind ({ filter = {}, options }, opts) {
|
|
|
10
10
|
filter.query = q
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
async function
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
async function afterGet ({ schema, id, record }, opts) {
|
|
20
|
-
const { isEmpty } = this.lib._
|
|
21
|
-
if (!isEmpty(record.data[opts.fieldName])) throw this.error('recordNotFound%s%s', id, schema.name, { statusCode: 404 })
|
|
22
|
-
delete record.data[opts.fieldName]
|
|
13
|
+
async function afterGetRecord ({ id, record = {} }, opts) {
|
|
14
|
+
const { isEmpty } = this.app.lib._
|
|
15
|
+
if (!isEmpty(record.data[opts.fieldName])) throw this.error('recordNotFound%s%s', id, this.name, { statusCode: 404 })
|
|
23
16
|
}
|
|
24
17
|
|
|
25
|
-
async function
|
|
18
|
+
async function beforeCreateRecord ({ body = {} }, opts) {
|
|
26
19
|
delete body[opts.fieldName]
|
|
27
20
|
}
|
|
28
21
|
|
|
29
|
-
async function afterCreate ({ record }, opts) {
|
|
30
|
-
delete record.data[opts.fieldName]
|
|
31
|
-
if (record.oldData) delete record.oldData[opts.fieldName]
|
|
32
|
-
}
|
|
33
|
-
|
|
34
22
|
async function removedAt (opts = {}) {
|
|
35
|
-
opts.fieldName = opts.fieldName ?? '
|
|
23
|
+
opts.fieldName = opts.fieldName ?? '_removedAt'
|
|
36
24
|
return {
|
|
37
25
|
properties: {
|
|
38
26
|
name: opts.fieldName,
|
|
39
27
|
type: 'datetime',
|
|
40
|
-
index: true
|
|
28
|
+
index: true,
|
|
29
|
+
hidden: true
|
|
41
30
|
},
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
await
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
},
|
|
67
|
-
afterUpdate: async function ({ record }) {
|
|
68
|
-
await afterCreate.call(this, { record }, opts)
|
|
69
|
-
},
|
|
70
|
-
beforeRemove: async function ({ schema, id, options }) {
|
|
71
|
-
const { recordUpdate, recordGet } = this.app.dobo
|
|
72
|
-
await recordGet(schema.name, id, options)
|
|
73
|
-
const { set } = this.lib._
|
|
31
|
+
hooks: [{
|
|
32
|
+
name: 'beforeFindRecord',
|
|
33
|
+
handler: async function (filter, options) {
|
|
34
|
+
await beforeFindRecord.call(this, { filter, options }, opts)
|
|
35
|
+
}
|
|
36
|
+
}, {
|
|
37
|
+
name: 'afterGetRecord',
|
|
38
|
+
handler: async function (id, record, options) {
|
|
39
|
+
await afterGetRecord.call(this, { record }, opts)
|
|
40
|
+
}
|
|
41
|
+
}, {
|
|
42
|
+
name: 'beforeCreateRecord',
|
|
43
|
+
handler: async function (body, options) {
|
|
44
|
+
await beforeCreateRecord.call(this, { body }, opts)
|
|
45
|
+
}
|
|
46
|
+
}, {
|
|
47
|
+
name: 'beforeUpdateRecord',
|
|
48
|
+
handler: async function (id, body, options) {
|
|
49
|
+
await beforeCreateRecord.call(this, { body }, opts)
|
|
50
|
+
}
|
|
51
|
+
}, {
|
|
52
|
+
name: 'beforeRemoveRecord',
|
|
53
|
+
handler: async function (id, options) {
|
|
54
|
+
const { set } = this.app.lib._
|
|
74
55
|
const body = set({}, opts.fieldName, new Date())
|
|
75
|
-
const record = await recordUpdate(
|
|
56
|
+
const record = await this.driver.recordUpdate(this, id, body, { noResult: false })
|
|
76
57
|
options.record = { oldData: record.oldData }
|
|
77
|
-
},
|
|
78
|
-
afterRemove: async function ({ record }) {
|
|
79
|
-
delete record.oldData[opts.fieldName]
|
|
80
58
|
}
|
|
81
|
-
}
|
|
59
|
+
}]
|
|
82
60
|
}
|
|
83
61
|
}
|
|
84
62
|
|
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
async function updatedAt (opts = {}) {
|
|
2
|
+
const { isSet } = this.app.lib.aneka
|
|
2
3
|
opts.fieldName = opts.fieldName ?? 'updatedAt'
|
|
3
|
-
opts.
|
|
4
|
+
opts.noOverwrite = opts.noOverwrite ?? false
|
|
4
5
|
return {
|
|
5
6
|
properties: {
|
|
6
7
|
name: opts.fieldName,
|
|
7
8
|
type: 'datetime',
|
|
8
9
|
index: true
|
|
9
10
|
},
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
if (
|
|
15
|
-
},
|
|
16
|
-
beforeUpdate: async function ({ body }) {
|
|
17
|
-
const { isSet } = this.lib.aneka
|
|
18
|
-
const now = new Date()
|
|
19
|
-
if (opts.overwrite || !isSet(body[opts.fieldName])) body[opts.fieldName] = now
|
|
11
|
+
hooks: [{
|
|
12
|
+
name: 'beforeCreateRecord',
|
|
13
|
+
handler: async function (body, options) {
|
|
14
|
+
if (opts.noOverwrite) body[opts.fieldName] = new Date()
|
|
15
|
+
else if (!isSet(body[opts.fieldName])) body[opts.fieldName] = new Date()
|
|
20
16
|
}
|
|
21
|
-
}
|
|
17
|
+
}, {
|
|
18
|
+
name: 'beforeUpdateRecord',
|
|
19
|
+
handler: async function (id, body, options) {
|
|
20
|
+
if (opts.noOverwrite) body[opts.fieldName] = new Date()
|
|
21
|
+
else if (!isSet(body[opts.fieldName])) body[opts.fieldName] = new Date()
|
|
22
|
+
}
|
|
23
|
+
}]
|
|
22
24
|
}
|
|
23
25
|
}
|
|
24
26
|
|