dobo 2.8.3 → 2.9.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.
@@ -35,6 +35,7 @@ async function connectionFactory () {
35
35
  * @type {Object}
36
36
  */
37
37
  this.options = omit(options, ['name', 'driver'])
38
+ this.options.connName = options.name
38
39
  this.options.models = this.options.models ?? []
39
40
  }
40
41
 
@@ -36,7 +36,8 @@ async function driverFactory () {
36
36
  },
37
37
  search: false,
38
38
  uniqueIndex: false,
39
- nullableField: true
39
+ nullableField: true,
40
+ transaction: false
40
41
  }
41
42
  this.useUtc = false
42
43
  this.maxChunkSize = 500
@@ -443,6 +444,10 @@ async function driverFactory () {
443
444
  async createHistogram (model, filter = {}, params = {}, options = {}) {
444
445
  throw this.plugin.error('notSupported%s%s', this.app.t('method'), 'createHistogram')
445
446
  }
447
+
448
+ async transaction (model, handler, ...args) {
449
+ throw this.plugin.error('notSupported%s%s', this.app.t('method'), 'transaction')
450
+ }
446
451
  }
447
452
 
448
453
  this.app.baseClass.DoboDriver = DoboDriver
@@ -1,6 +1,17 @@
1
1
  import path from 'path'
2
2
  import nql from '@tryghost/nql'
3
3
 
4
+ export const omittedOptionsKeys = ['req', 'reply', 'trx']
5
+
6
+ export function cloneOptions (options = {}) {
7
+ const { cloneDeep, omit } = this.app.lib._
8
+ const nOptions = cloneDeep(omit(options, omittedOptionsKeys))
9
+ for (const key of omittedOptionsKeys) {
10
+ nOptions[key] = options[key]
11
+ }
12
+ return nOptions
13
+ }
14
+
4
15
  export async function execHook (name, ...args) {
5
16
  const { runHook } = this.app.bajo
6
17
  const { camelCase, last } = this.app.lib._
@@ -36,10 +47,10 @@ export async function execDynHook (name, ...args) {
36
47
 
37
48
  export async function execValidation (body, options = {}) {
38
49
  const { uniq } = this.app.lib._
39
- const { validation = {}, extFields = [], partial, req } = options
50
+ const { validation = {} } = options
40
51
  const fields = uniq([...Object.keys(body), ...(options.fields ?? [])])
41
52
  await execHook.call(this, 'beforeRecordValidation', body, options)
42
- const result = await this.validate(body, validation, { fields, extFields, partial, req })
53
+ const result = await this.validate(body, validation, { fields, ...options })
43
54
  await execHook.call(this, 'afterRecordValidation', body, result, options)
44
55
  return result
45
56
  }
@@ -51,18 +62,14 @@ export async function execValidation (body, options = {}) {
51
62
  * @returns {Object}
52
63
  */
53
64
  export async function getFilterAndOptions (filter = {}, options = {}, action) {
54
- const { cloneDeep, omit } = this.app.lib._
65
+ const { cloneDeep } = this.app.lib._
55
66
  const { runModelHook } = this.app.dobo
56
- const omittedKeys = ['req', 'reply']
57
- const nFilter = cloneDeep(omit(filter, omittedKeys))
58
- const nOptions = cloneDeep(omit(options, omittedKeys))
67
+ const nFilter = cloneDeep(filter || {})
68
+ const nOptions = cloneOptions.call(this, options)
59
69
  nOptions.action = action
60
70
  nOptions.dataOnly = false
61
71
  nOptions.truncateString = nOptions.truncateString ?? false
62
72
  nOptions.throwNotFound = nOptions.throwNotFound ?? true
63
- for (const key of omittedKeys) {
64
- nOptions[key] = options[key]
65
- }
66
73
  nFilter.orgQuery = nFilter.query
67
74
  nFilter.orgSearch = nFilter.search
68
75
  if (!nOptions.noModelHook) await runModelHook(this, 'beforeBuildQuery', nFilter.query, nOptions)
@@ -20,7 +20,7 @@ async function createRecord (...args) {
20
20
  if (!noValidation) await execValidation.call(this, input, options)
21
21
  let result = options.record ?? (await this.driver._createRecord(this, input, options)) ?? {}
22
22
  if (noResult) {
23
- await runHook('cache:clear', this, 'create', body)
23
+ await runHook('cache:clear', this, 'create', body, null, options)
24
24
  return
25
25
  }
26
26
  result = result ?? {}
@@ -30,7 +30,7 @@ async function createRecord (...args) {
30
30
  await execDynHook.call(this, 'afterCreateRecord', input, result, options)
31
31
  await execModelHook.call(this, 'afterCreateRecord', input, result, options)
32
32
  await execHook.call(this, 'afterCreateRecord', input, result, options)
33
- await runHook('cache:clear', this, 'create', body, result)
33
+ await runHook('cache:clear', this, 'create', body, result, options)
34
34
  return dataOnly ? result.data : result
35
35
  }
36
36
 
@@ -1,4 +1,4 @@
1
- import { getMultiRefs, execHook, execModelHook, execDynHook, getFilterAndOptions } from './_util.js'
1
+ import { getMultiRefs, execHook, execModelHook, execDynHook, getFilterAndOptions, cloneOptions } from './_util.js'
2
2
  const action = 'findAllRecord'
3
3
 
4
4
  async function native (filter, options, dataOnly) {
@@ -36,8 +36,8 @@ async function loop (params, opts, dataOnly) {
36
36
  const { cloneDeep } = this.app.lib._
37
37
  const { filter, options } = await getFilterAndOptions.call(this, params, opts, action)
38
38
  const { maxLimit, hardLimit } = this.app.dobo.config.default.filter
39
- const nFilter = cloneDeep(filter)
40
- const nOptions = cloneDeep(options)
39
+ const nFilter = cloneDeep(filter || {})
40
+ const nOptions = cloneOptions.call(this, options)
41
41
  nOptions.count = false
42
42
  nOptions.dataOnly = false
43
43
  nFilter.limit = maxLimit
@@ -7,7 +7,7 @@ async function findAttachment (...args) {
7
7
  const [id, opts = {}] = args
8
8
  const { fastGlob, fs } = this.app.lib
9
9
  const { getPluginDataDir } = this.app.bajo
10
- const dir = `${getPluginDataDir(this.ns)}/attachment/${this.name}/${id}`
10
+ const dir = `${getPluginDataDir(this.plugin.ns)}/attachment/${this.name}/${id}`
11
11
  if (!fs.existsSync(dir)) return []
12
12
  const files = await fastGlob(`${dir}/**/*`)
13
13
  const { fullPath, stats, mimeType } = opts
@@ -1,3 +1,5 @@
1
+ import { cloneOptions } from './_util.js'
2
+
1
3
  const action = 'findOneRecord'
2
4
 
3
5
  async function findOneRecord (...args) {
@@ -6,8 +8,8 @@ async function findOneRecord (...args) {
6
8
  const { cloneDeep } = this.app.lib._
7
9
  const { dataOnly = true } = opts
8
10
  if (dataOnly) opts.count = false
9
- const nFilter = cloneDeep(params)
10
- const nOptions = cloneDeep(opts)
11
+ const nFilter = cloneDeep(params || {})
12
+ const nOptions = cloneOptions.call(this, opts)
11
13
  nOptions.count = false
12
14
  nOptions.dataOnly = false
13
15
  nFilter.limit = 1
@@ -98,7 +98,7 @@ async function findRecord (...args) {
98
98
  await execDynHook.call(this, 'afterFindRecord', filter, result, options)
99
99
  await execModelHook.call(this, 'afterFindRecord', filter, result, options)
100
100
  await execHook.call(this, 'afterFindRecord', filter, result, options)
101
- if (!noCache) await runHook('cache:setByFilter', this, filter, result)
101
+ if (!noCache) await runHook('cache:setByFilter', this, filter, result, options)
102
102
  return dataOnly ? result.data : result
103
103
  }
104
104
 
@@ -74,7 +74,7 @@ async function getRecord (...args) {
74
74
  await execDynHook.call(this, 'afterGetRecord', id, result, options)
75
75
  await execModelHook.call(this, 'afterGetRecord', id, result, options)
76
76
  await execHook.call(this, 'afterGetRecord', id, result, options)
77
- if (!noCache) await runHook('cache:setById', this, id, result)
77
+ if (!noCache) await runHook('cache:setById', this, id, result, options)
78
78
  return dataOnly ? result.data : result
79
79
  }
80
80
 
@@ -54,7 +54,7 @@ async function removeRecord (...args) {
54
54
  await execDynHook.call(this, 'afterRemoveRecord', id, result, options)
55
55
  await execModelHook.call(this, 'afterRemoveRecord', id, result, options)
56
56
  await execHook.call(this, 'afterRemoveRecord', id, result, options)
57
- await runHook('cache:clear', this, 'remove', id, result)
57
+ await runHook('cache:clear', this, 'remove', id, result, options)
58
58
  return dataOnly ? result.oldData : result
59
59
  }
60
60
 
@@ -23,9 +23,9 @@ async function sanitizeBody ({ body = {}, partial, strict, extFields = [], noDef
23
23
  if (onlyTypes.length > 0 && !onlyTypes.includes(type)) return
24
24
  if (['object', 'array'].includes(type)) result[name] = sanitizeObject(result[name])
25
25
  else if (type === 'boolean') result[name] = sanitizeBoolean(result[name])
26
- else if (['float', 'double'].includes(type)) result[name] = sanitizeFloat(result[name], strict)
27
- else if (['integer', 'smallint'].includes(type)) result[name] = sanitizeInt(result[name], strict)
28
- else if (['string', 'text'].includes(type)) result[name] = sanitizeString(result[name], strict)
26
+ else if (['float', 'double'].includes(type)) result[name] = sanitizeFloat(result[name], { strict })
27
+ else if (['integer', 'smallint'].includes(type)) result[name] = sanitizeInt(result[name], { strict })
28
+ else if (['string', 'text'].includes(type)) result[name] = sanitizeString(result[name], { strict })
29
29
  else if (['datetime'].includes(type)) result[name] = sanitizeDate(result[name], { input: 'native' })
30
30
  if (!strict && isNaN(result[name])) result[name] = null
31
31
  if (['updateRecord', 'upsertRecord'].includes(action) && type === 'string' && result[name] === '') result[name] = null
@@ -0,0 +1,6 @@
1
+ async function transaction (handler, ...args) {
2
+ if (!this.driver.support.transaction) return handler.call(this, ...args)
3
+ return await this.driver.transaction(this, handler, ...args)
4
+ }
5
+
6
+ export default transaction
@@ -76,7 +76,7 @@ async function updateRecord (...args) {
76
76
  await execDynHook.call(this, 'afterUpdateRecord', id, input, result, options)
77
77
  await execModelHook.call(this, 'afterUpdateRecord', id, input, result, options)
78
78
  await execHook.call(this, 'afterUpdateRecord', id, input, result, options)
79
- await runHook('cache:clear', this, 'update', id, body, result)
79
+ await runHook('cache:clear', this, 'update', id, body, result, options)
80
80
  return dataOnly ? result.data : result
81
81
  }
82
82
 
@@ -25,7 +25,7 @@ async function native (body = {}, opts = {}) {
25
25
  await execDynHook.call(this, 'afterUpsertRecord', input, result, options)
26
26
  await execModelHook.call(this, 'afterUpsertRecord', input, result, options)
27
27
  await execHook.call(this, 'afterUpsertRecord', input, result, options)
28
- await runHook('cache:clear', this, 'upsert', body, result)
28
+ await runHook('cache:clear', this, 'upsert', body, result, options)
29
29
  return dataOnly ? result.data : result
30
30
  }
31
31
 
@@ -25,6 +25,7 @@ import sanitizeId from './model/sanitize-id.js'
25
25
  import upsertRecord from './model/upsert-record.js'
26
26
  import bulkCreateRecords from './model/bulk-create-records.js'
27
27
  import listAttachment from './model/list-attachment.js'
28
+ import transaction from './model/transaction.js'
28
29
  import validate from './model/validate.js'
29
30
 
30
31
  /**
@@ -109,6 +110,7 @@ async function modelFactory () {
109
110
  findOneRecord = findOneRecord
110
111
  findAllRecord = findAllRecord
111
112
 
113
+ transaction = transaction
112
114
  bulkCreateRecords = bulkCreateRecords
113
115
 
114
116
  createAggregate = createAggregate
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dobo",
3
- "version": "2.8.3",
3
+ "version": "2.9.1",
4
4
  "description": "DBMS for Bajo Framework",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/wiki/CHANGES.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changes
2
2
 
3
+ ## 2026-03-06
4
+
5
+ - [2.9.1] Bug fix on ```model.sanitizeBody()```
6
+
7
+ ## 2026-03-05
8
+
9
+ - [2.9.0] Add transaction support with ```model.transaction()``` wrapper
10
+ - [2.9.0] Add property ```driver.support.transaction``` to indicate a driver is fully support transaction or not
11
+ - [2.9.0] Add property ```connection.options.connName```
12
+
3
13
  ## 2026-02-25
4
14
 
5
15
  - [2.8.3] Bug fix on attachment route