dobo 2.9.3 → 2.10.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.
@@ -57,7 +57,7 @@ async function modelRebuild (path, ...args) {
57
57
  }
58
58
  }
59
59
  try {
60
- await model.build({ spinner: spin })
60
+ await model.build()
61
61
  spin.succeed('modelCreated%s', model.name)
62
62
  result.succed++
63
63
  } catch (err) {
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
@@ -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 loadFixtures ({ spinner } = {}) {
4
- const { readConfig, eachPlugins, getPluginDataDir } = this.app.bajo
5
- const { resolvePath } = this.app.lib.aneka
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
- if (isEmpty(items)) return result
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
- try {
33
- for (const k in item) {
34
- const v = item[k]
35
- if (typeof v === 'string' && v.slice(0, 2) === '?:') {
36
- let [, model, field, ...query] = v.split(':')
37
- if (!field) field = 'id'
38
- const ref = this.app.dobo.getModel(model)
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
- const input = noBodySanitizer ? cloneDeep(body) : await this.sanitizeBody({ body, extFields, strict: true, truncateString, partial, onlyTypes })
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)
@@ -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)
115
+ }
116
+
99
117
  build = build
100
118
  exists = exists
101
119
  drop = drop
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dobo",
3
- "version": "2.9.3",
3
+ "version": "2.10.0",
4
4
  "description": "DBMS for Bajo Framework",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/wiki/CHANGES.md CHANGED
@@ -1,8 +1,16 @@
1
1
  # Changes
2
2
 
3
+ ## 2026-03-11
4
+
5
+ - [2.10.0] Add ```immutable``` and ```feature``` to the list of allowed property keys
6
+ - [2.10.0] Set ```property.feature``` to the name of feature name if property is build using ```feature```
7
+ - [2.10.0] Add ```model._simpleLookup()```
8
+ - [2.10.0] Refactor ```model.loadFixtures()```
9
+ - [2.10.0] Add ```property.immutable``` for property that can't be updated
10
+
3
11
  ## 2026-03-09
4
12
 
5
- - [2.9.3] Bug fix in ```findAllRecord```
13
+ - [2.9.3] Bug fix in ```model.findAllRecord```
6
14
 
7
15
  ## 2026-03-07
8
16