dobo 2.9.2 → 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) {
@@ -7,14 +7,14 @@ async function native (filter, options, dataOnly) {
7
7
  if (dataOnly) options.count = false
8
8
  let { noResultSanitizer, noCache } = options
9
9
  if (!this.cacheable) noCache = true
10
- await execHook.call(this, 'beforeFindAllRecord', filter, options)
11
- await execModelHook.call(this, 'beforeFindAllRecord', filter, options)
12
- await execDynHook.call(this, 'beforeFindAllRecord', filter, options)
10
+ await execHook.call(this, 'beforeFindRecord', filter, options)
11
+ await execModelHook.call(this, 'beforeFindRecord', filter, options)
12
+ await execDynHook.call(this, 'beforeFindRecord', filter, options)
13
13
  if (get && !noCache && !options.record) {
14
14
  const cachedResult = await get({ model: this.name, filter, options })
15
15
  if (cachedResult) {
16
16
  cachedResult.cached = true
17
- await execModelHook.call(this, 'afterFindAllRecord', filter, cachedResult, options)
17
+ await execModelHook.call(this, 'afterFindRecord', filter, cachedResult, options)
18
18
  return dataOnly ? cachedResult.data : cachedResult
19
19
  }
20
20
  }
@@ -25,9 +25,9 @@ async function native (filter, options, dataOnly) {
25
25
  }
26
26
  }
27
27
  if (isSet(options.refs)) await getMultiRefs.call(this, result.data, options)
28
- await execDynHook.call(this, 'beforeCreateRecord', filter, result, options)
29
- await execModelHook.call(this, 'afterFindAllRecord', filter, result, options)
30
- await execHook.call(this, 'afterFindAllRecord', filter, result, options)
28
+ await execDynHook.call(this, 'afterFindRecord', filter, result, options)
29
+ await execModelHook.call(this, 'afterFindRecord', filter, result, options)
30
+ await execHook.call(this, 'afterFindRecord', filter, result, options)
31
31
  if (set && !noCache) await set({ model: this.name, filter, options, result })
32
32
  return dataOnly ? result.data : result
33
33
  }
@@ -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.2",
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,13 +1,25 @@
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
+
11
+ ## 2026-03-09
12
+
13
+ - [2.9.3] Bug fix in ```model.findAllRecord```
14
+
3
15
  ## 2026-03-07
4
16
 
5
- - [2.9.2] Bug fix on ```model.loadFixtures()```
6
- - [2.9.2] Bug fix on ```collect-connections.js```
17
+ - [2.9.2] Bug fix in ```model.loadFixtures()```
18
+ - [2.9.2] Bug fix in ```collect-connections.js```
7
19
 
8
20
  ## 2026-03-06
9
21
 
10
- - [2.9.1] Bug fix on ```model.sanitizeBody()```
22
+ - [2.9.1] Bug fix in ```model.sanitizeBody()```
11
23
 
12
24
  ## 2026-03-05
13
25
 
@@ -17,17 +29,17 @@
17
29
 
18
30
  ## 2026-02-25
19
31
 
20
- - [2.8.3] Bug fix on attachment route
32
+ - [2.8.3] Bug fix in attachment route
21
33
 
22
34
  ## 2026-02-24
23
35
 
24
- - [2.8.2] Bug fix on field type ```textType```
25
- - [2.8.2] Bug fix on ```collect-models.js``` when empty model is returned
36
+ - [2.8.2] Bug fix in field type ```textType```
37
+ - [2.8.2] Bug fix in ```collect-models.js``` when empty model is returned
26
38
 
27
39
  ## 2026-02-23
28
40
 
29
- - [2.8.1] Bug fix on ```memory._getCursor()```
30
- - [2.8.1] Bug fix on ```model.upsertRecord()```
41
+ - [2.8.1] Bug fix in ```memory._getCursor()```
42
+ - [2.8.1] Bug fix in ```model.upsertRecord()```
31
43
 
32
44
  ## 2026-02-22
33
45
 
@@ -42,21 +54,21 @@
42
54
 
43
55
  ## 2026-02-17
44
56
 
45
- - [2.6.5] Bug fix on model extender
46
- - [2.6.5] Bug fix on trace logging
47
- - [2.6.6] Bug fix on extender indexes
57
+ - [2.6.5] Bug fix in model extender
58
+ - [2.6.5] Bug fix in trace logging
59
+ - [2.6.6] Bug fix in extender indexes
48
60
 
49
61
  ## 2026-02-05
50
62
 
51
- - [2.6.4] Bug fix on driver options, ```noCheckUnique``` must be perform in ```create```, ```update``` and ```upsert```
63
+ - [2.6.4] Bug fix in driver options, ```noCheckUnique``` must be perform in ```create```, ```update``` and ```upsert```
52
64
 
53
65
  ## 2026-02-03
54
66
 
55
- - [2.6.3] Bug fix on ```model.sanitizeRecord()```
67
+ - [2.6.3] Bug fix in ```model.sanitizeRecord()```
56
68
 
57
69
  ## 2026-02-02
58
70
 
59
- - [2.6.2] Bug fix on query & search resolver: Do not remove old query/search, should always copied to oldQuery/oldSearch
71
+ - [2.6.2] Bug fix in query & search resolver: Do not remove old query/search, should always copied to oldQuery/oldSearch
60
72
 
61
73
  ## 2026-02-01
62
74
 
@@ -69,20 +81,20 @@
69
81
  - [2.5.0] Add feature to push custom ```options._data```. If provided, this will be used instead of auto generated one.
70
82
  - [2.5.0] Remove ```silent``` in ```options``` object
71
83
  - [2.5.1] Driver now support ```this.useUtc``` for database that store values in UTC string
72
- - [2.5.1] Bug fix on ```driver.sanitizeRecord()```
84
+ - [2.5.1] Bug fix in ```driver.sanitizeRecord()```
73
85
 
74
86
  ## 2026-01-29
75
87
 
76
88
  - [2.4.0] Add ```bulkCreateRecords()``` on model & driver
77
89
  - [2.4.0] Add ```execModelHook()```
78
- - [2.4.0] Bug fix on models collection
90
+ - [2.4.0] Bug fix in models collection
79
91
  - [2.4.0] Add ```DoboAction``` to the ```app.baseClass```
80
92
  - [2.4.0] ```findAllRecord()``` can't be called as action
81
93
 
82
94
  ## 2026-01-26
83
95
 
84
96
  - [2.3.1] Bug fix if reference model isn't loaded only yield warning
85
- - [2.3.1] Bug fix on fetching multi references
97
+ - [2.3.1] Bug fix in fetching multi references
86
98
 
87
99
  ## 2026-01-24
88
100
 
@@ -96,16 +108,16 @@
96
108
 
97
109
  ## 2026-01-19
98
110
 
99
- - [2.2.4] Bug fix on route ```dobo:/attachment/...```
111
+ - [2.2.4] Bug fix in route ```dobo:/attachment/...```
100
112
 
101
113
  ## 2026-01-18
102
114
 
103
115
  - [2.2.2] Revert back to ```mingo@6.5.1``` because of bugs in ```skip``` operation
104
- - [2.2.3] Bug fix on driver's ```sanitizeBody()```
116
+ - [2.2.3] Bug fix in driver's ```sanitizeBody()```
105
117
 
106
118
  ## 2026-01-16
107
119
 
108
- - [2.2.1] Bug fix on model references
120
+ - [2.2.1] Bug fix in model references
109
121
 
110
122
  ## 2026-01-11
111
123