dobo 2.9.3 → 2.10.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/extend/bajoCli/applet/rebuild-model.js +1 -1
- package/index.js +1 -1
- package/lib/collect-models.js +1 -0
- package/lib/factory/model/load-fixtures.js +46 -41
- package/lib/factory/model/update-record.js +4 -2
- package/lib/factory/model.js +18 -0
- package/package.json +1 -1
- package/wiki/CHANGES.md +13 -1
package/index.js
CHANGED
|
@@ -81,7 +81,7 @@ const propertyType = {
|
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
const commonPropertyTypes = ['name', 'type', 'required', 'rules', 'validator', 'ref', 'default', 'values', 'rulesMsg']
|
|
84
|
+
const commonPropertyTypes = ['name', 'type', 'required', 'rules', 'validator', 'ref', 'default', 'values', 'rulesMsg', 'immutable', 'feature']
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
87
|
* Plugin factory
|
package/lib/collect-models.js
CHANGED
|
@@ -82,6 +82,7 @@ async function applyFeature (model, feature, options, indexes, isExtender) {
|
|
|
82
82
|
if (item.rules) model.rules.push(...item.rules)
|
|
83
83
|
if (!isArray(item.properties)) item.properties = [item.properties]
|
|
84
84
|
for (const prop of item.properties) {
|
|
85
|
+
prop.feature = `${feature.plugin.ns}:${feature.name}`
|
|
85
86
|
await sanitizeProp.call(this, model, prop, indexes)
|
|
86
87
|
}
|
|
87
88
|
if (item.hooks) {
|
|
@@ -1,10 +1,30 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
2
|
|
|
3
|
-
async function
|
|
4
|
-
const {
|
|
5
|
-
const {
|
|
6
|
-
const { isEmpty, isArray, isString } = this.app.lib._
|
|
3
|
+
async function exec ({ item, spinner, options, result, items } = {}) {
|
|
4
|
+
const { isArray, isString } = this.app.lib._
|
|
5
|
+
const { getPluginDataDir } = this.app.bajo
|
|
7
6
|
const { fs } = this.app.lib
|
|
7
|
+
const resp = await this.createRecord(item, options)
|
|
8
|
+
if (isArray(item._attachments) && item._attachments.length > 0) {
|
|
9
|
+
for (let att of item._attachments) {
|
|
10
|
+
if (isString(att)) att = { field: 'file', file: att }
|
|
11
|
+
const fname = path.basename(att.file)
|
|
12
|
+
if (fs.existsSync(att.file)) {
|
|
13
|
+
const dest = `${getPluginDataDir(this.plugin.ns)}/${resp.id}/${att.field}/${fname}`
|
|
14
|
+
try {
|
|
15
|
+
fs.copySync(att.file, dest)
|
|
16
|
+
} catch (err) {}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
result.success++
|
|
21
|
+
if (spinner) spinner.setText('recordsAdded%s%d%d', this.name, result.success, items.length)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function loadFixtures ({ spinner, ignoreError = true, collectItems = false, noLookup = false } = {}, options = {}) {
|
|
25
|
+
const { readConfig, eachPlugins } = this.app.bajo
|
|
26
|
+
const { resolvePath } = this.app.lib.aneka
|
|
27
|
+
const { isEmpty, isArray, omit } = this.app.lib._
|
|
8
28
|
if (this.connection.proxy) {
|
|
9
29
|
this.log.warn('proxiedConnBound%s', this.name)
|
|
10
30
|
return
|
|
@@ -26,47 +46,32 @@ async function loadFixtures ({ spinner } = {}) {
|
|
|
26
46
|
const extend = await readConfig(`${dir}/extend/dobo/extend/${me.plugin.ns}/fixture/${base}.*`, { ns, ignoreError: true })
|
|
27
47
|
if (isArray(extend) && !isEmpty(extend)) items.push(...extend)
|
|
28
48
|
})
|
|
29
|
-
|
|
30
|
-
const opts = { noHook: true, noCache: true }
|
|
49
|
+
const opts = { noHook: true, noCache: true, ...(omit(options, ['noHook', 'noCache'])) }
|
|
31
50
|
for (const item of items) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const recs = await ref.findRecord({ query: query.join(':') }, opts)
|
|
40
|
-
item[k] = (recs[0] ?? {})[field]
|
|
41
|
-
}
|
|
42
|
-
if (v === null) item[k] = undefined
|
|
43
|
-
else {
|
|
44
|
-
const prop = this.properties.find(item => item.name === k)
|
|
45
|
-
if (prop && ['string', 'text'].includes(prop.type)) item[k] += ''
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
const resp = await this.createRecord(item, { force: true })
|
|
49
|
-
if (isArray(item._attachments) && item._attachments.length > 0) {
|
|
50
|
-
for (let att of item._attachments) {
|
|
51
|
-
if (isString(att)) att = { field: 'file', file: att }
|
|
52
|
-
const fname = path.basename(att.file)
|
|
53
|
-
if (fs.existsSync(att.file)) {
|
|
54
|
-
const dest = `${getPluginDataDir(this.plugin.ns)}/${resp.id}/${att.field}/${fname}`
|
|
55
|
-
try {
|
|
56
|
-
fs.copySync(att.file, dest)
|
|
57
|
-
} catch (err) {}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
51
|
+
for (const k in item) {
|
|
52
|
+
const v = item[k]
|
|
53
|
+
if (!noLookup && typeof v === 'string' && v.slice(0, 2) === '?:') item[k] = await this._simpleLookup(v.slice(2), opts)
|
|
54
|
+
if (v === null) item[k] = undefined
|
|
55
|
+
else {
|
|
56
|
+
const prop = this.properties.find(item => item.name === k)
|
|
57
|
+
if (prop && ['string', 'text'].includes(prop.type)) item[k] += ''
|
|
60
58
|
}
|
|
61
|
-
result.success++
|
|
62
|
-
if (spinner) spinner.setText('recordsAdded%s%d%d', this.name, result.success, items.length)
|
|
63
|
-
} catch (err) {
|
|
64
|
-
console.log(err)
|
|
65
|
-
err.model = this.name
|
|
66
|
-
if (this.app.applet) this.plugin.print.fail(this.app.dobo.validationErrorMessage(err))
|
|
67
|
-
result.failed++
|
|
68
59
|
}
|
|
69
60
|
}
|
|
61
|
+
if (collectItems) return items
|
|
62
|
+
if (isEmpty(items)) return result
|
|
63
|
+
for (const item of items) {
|
|
64
|
+
if (ignoreError) {
|
|
65
|
+
try {
|
|
66
|
+
await exec.call(this, { item, spinner, opts, options, result, items })
|
|
67
|
+
} catch (err) {
|
|
68
|
+
// console.log(err)
|
|
69
|
+
err.model = this.name
|
|
70
|
+
if (this.app.applet) this.plugin.print.fail(this.app.dobo.validationErrorMessage(err))
|
|
71
|
+
result.failed++
|
|
72
|
+
}
|
|
73
|
+
} else await exec.call(this, { item, spinner, opts, options, result, items })
|
|
74
|
+
}
|
|
70
75
|
return result
|
|
71
76
|
}
|
|
72
77
|
|
|
@@ -49,14 +49,16 @@ async function updateRecord (...args) {
|
|
|
49
49
|
let [id, body = {}, opts = {}] = args
|
|
50
50
|
const { isSet } = this.app.lib.aneka
|
|
51
51
|
const { runHook } = this.app.bajo
|
|
52
|
-
const { cloneDeep, get } = this.app.lib._
|
|
52
|
+
const { cloneDeep, get, omit } = this.app.lib._
|
|
53
53
|
const { dataOnly = true } = opts
|
|
54
54
|
const { options } = await getFilterAndOptions.call(this, null, opts, action)
|
|
55
55
|
options.partial = options.partial ?? true
|
|
56
56
|
const { truncateString, noResult, noBodySanitizer, noResultSanitizer, noValidation, partial } = options
|
|
57
57
|
const extFields = get(options, 'validation.extFields', [])
|
|
58
58
|
id = this.sanitizeId(id)
|
|
59
|
-
|
|
59
|
+
let input = noBodySanitizer ? cloneDeep(body) : await this.sanitizeBody({ body, extFields, strict: true, truncateString, partial, onlyTypes })
|
|
60
|
+
const immutables = this.properties.filter(prop => prop.immutable)
|
|
61
|
+
if (immutables.length > 0) input = omit(input, immutables)
|
|
60
62
|
delete input.id
|
|
61
63
|
await execHook.call(this, 'beforeUpdateRecord', id, input, options)
|
|
62
64
|
await execModelHook.call(this, 'beforeUpdateRecord', id, input, options)
|
package/lib/factory/model.js
CHANGED
|
@@ -96,6 +96,24 @@ async function modelFactory () {
|
|
|
96
96
|
return !!this.getProperty(name)
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
_simpleLookup = async (value, options = {}) => {
|
|
100
|
+
const { get, isEmpty, isString, isPlainObject, isArray } = this.app.lib._
|
|
101
|
+
let model
|
|
102
|
+
let field
|
|
103
|
+
let query
|
|
104
|
+
if (isString(value)) {
|
|
105
|
+
[model, field, ...query] = value.split(':')
|
|
106
|
+
query = query.join(':')
|
|
107
|
+
} else if (isPlainObject(value)) ({ model, field, query } = value)
|
|
108
|
+
else if (isArray(value)) [model, field, query] = value
|
|
109
|
+
else return
|
|
110
|
+
if (isEmpty(field)) field = 'id'
|
|
111
|
+
const { getModel } = this.app.dobo
|
|
112
|
+
const ref = getModel(model)
|
|
113
|
+
const rec = await ref.findOneRecord({ query }, { noHook: true, noCache: true, ...options })
|
|
114
|
+
return get(rec, field, null)
|
|
115
|
+
}
|
|
116
|
+
|
|
99
117
|
build = build
|
|
100
118
|
exists = exists
|
|
101
119
|
drop = drop
|
package/package.json
CHANGED
package/wiki/CHANGES.md
CHANGED
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## 2026-03-12
|
|
4
|
+
|
|
5
|
+
- [2.10.1] Bug fix in ```model._simpleLookup()``` should return ```null``` if query results empty value
|
|
6
|
+
|
|
7
|
+
## 2026-03-11
|
|
8
|
+
|
|
9
|
+
- [2.10.0] Add ```immutable``` and ```feature``` to the list of allowed property keys
|
|
10
|
+
- [2.10.0] Set ```property.feature``` to the name of feature name if property is build using ```feature```
|
|
11
|
+
- [2.10.0] Add ```model._simpleLookup()```
|
|
12
|
+
- [2.10.0] Refactor ```model.loadFixtures()```
|
|
13
|
+
- [2.10.0] Add ```property.immutable``` for property that can't be updated
|
|
14
|
+
|
|
3
15
|
## 2026-03-09
|
|
4
16
|
|
|
5
|
-
- [2.9.3] Bug fix in ```findAllRecord```
|
|
17
|
+
- [2.9.3] Bug fix in ```model.findAllRecord```
|
|
6
18
|
|
|
7
19
|
## 2026-03-07
|
|
8
20
|
|