dobo 2.31.0 → 2.32.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.
Files changed (123) hide show
  1. package/.jsdoc.conf.json +1 -1
  2. package/docs/Dobo.html +1 -24
  3. package/docs/DoboAction.html +13 -0
  4. package/docs/DoboAdapter.html +3 -0
  5. package/docs/DoboConnection.html +3 -0
  6. package/docs/DoboFeature.html +3 -0
  7. package/docs/DoboModel.html +51 -0
  8. package/docs/data/search.json +1 -1
  9. package/docs/extend_bajo_hook-docs.js.html +241 -0
  10. package/docs/external-Tools.html +3 -0
  11. package/docs/global.html +1 -5
  12. package/docs/index.html +1 -1
  13. package/docs/index.js.html +434 -380
  14. package/docs/lib_factory_action.js.html +290 -0
  15. package/docs/lib_factory_adapter.js.html +1057 -0
  16. package/docs/lib_factory_connection.js.html +112 -0
  17. package/docs/lib_factory_feature.js.html +55 -0
  18. package/docs/lib_factory_model.js.html +503 -0
  19. package/docs/lib_factory_model_build.js.html +28 -0
  20. package/docs/lib_factory_model_clear-record.js.html +33 -0
  21. package/docs/lib_factory_model_count-record.js.html +45 -0
  22. package/docs/lib_factory_model_create-record.js.html +52 -0
  23. package/docs/lib_factory_model_drop.js.html +29 -0
  24. package/docs/lib_factory_model_exists.js.html +29 -0
  25. package/docs/lib_factory_model_find-all-record.js.html +115 -0
  26. package/docs/lib_factory_model_find-one-record.js.html +41 -0
  27. package/docs/lib_factory_model_find-record.js.html +85 -0
  28. package/docs/lib_factory_model_get-record.js.html +55 -0
  29. package/docs/lib_factory_model_helper.js.html +579 -0
  30. package/docs/lib_factory_model_remove-record.js.html +47 -0
  31. package/docs/lib_factory_model_sanitize-body.js.html +60 -0
  32. package/docs/lib_factory_model_sanitize-fixture.js.html +59 -0
  33. package/docs/lib_factory_model_sanitize-record.js.html +73 -0
  34. package/docs/lib_factory_model_update-record.js.html +61 -0
  35. package/docs/lib_factory_model_upsert-record.js.html +74 -0
  36. package/docs/{method_validate.js.html → lib_factory_model_validate.js.html} +109 -73
  37. package/docs/lib_helper.js.html +466 -0
  38. package/docs/module-Helper.html +3 -0
  39. package/docs/module-Helper_Model.html +3 -0
  40. package/docs/module-Hook.html +15 -0
  41. package/docs/scripts/core.js +477 -476
  42. package/docs/scripts/resize.js +36 -36
  43. package/docs/scripts/search.js +105 -105
  44. package/docs/scripts/third-party/fuse.js +1 -1
  45. package/docs/scripts/third-party/hljs-line-num-original.js +285 -282
  46. package/docs/scripts/third-party/hljs-line-num.js +1 -1
  47. package/docs/scripts/third-party/hljs-original.js +1202 -1195
  48. package/docs/scripts/third-party/hljs.js +1 -1
  49. package/docs/scripts/third-party/popper.js +1 -1
  50. package/docs/scripts/third-party/tippy.js +1 -1
  51. package/docs/scripts/third-party/tocbot.js +509 -508
  52. package/docs/static/home.md +3 -1
  53. package/extend/bajo/hook-docs.js +238 -0
  54. package/extend/bajo/intl/en-US.json +9 -9
  55. package/extend/bajo/intl/id.json +9 -9
  56. package/extend/bajoCli/applet/rebuild-model.js +2 -2
  57. package/extend/dobo/{driver → adapter}/memory.js +6 -6
  58. package/extend/dobo/feature/immutable.js +2 -2
  59. package/extend/dobo/feature/removed-at.js +1 -1
  60. package/index.js +33 -30
  61. package/lib/factory/action.js +145 -18
  62. package/lib/factory/adapter.js +1054 -0
  63. package/lib/factory/connection.js +46 -22
  64. package/lib/factory/feature.js +23 -3
  65. package/lib/factory/model/build.js +11 -2
  66. package/lib/factory/model/bulk-create-record.js +2 -2
  67. package/lib/factory/model/clear-record.js +13 -3
  68. package/lib/factory/model/count-record.js +13 -2
  69. package/lib/factory/model/create-aggregate.js +2 -2
  70. package/lib/factory/model/create-attachment.js +1 -1
  71. package/lib/factory/model/create-histogram.js +2 -2
  72. package/lib/factory/model/create-record.js +15 -2
  73. package/lib/factory/model/drop.js +12 -2
  74. package/lib/factory/model/exists.js +10 -6
  75. package/lib/factory/model/find-all-record.js +14 -3
  76. package/lib/factory/model/find-attachment.js +1 -1
  77. package/lib/factory/model/find-one-record.js +13 -1
  78. package/lib/factory/model/find-record.js +17 -53
  79. package/lib/factory/model/get-record.js +11 -39
  80. package/lib/factory/model/{_util.js → helper.js} +176 -16
  81. package/lib/factory/model/remove-attachment.js +1 -1
  82. package/lib/factory/model/remove-record.js +11 -28
  83. package/lib/factory/model/sanitize-body.js +14 -12
  84. package/lib/factory/model/sanitize-fixture.js +17 -4
  85. package/lib/factory/model/sanitize-record.js +9 -6
  86. package/lib/factory/model/transaction.js +2 -2
  87. package/lib/factory/model/update-record.js +12 -40
  88. package/lib/factory/model/upsert-record.js +13 -42
  89. package/lib/factory/model/validate.js +47 -14
  90. package/lib/factory/model.js +301 -23
  91. package/lib/{collect-models.js → helper.js} +463 -364
  92. package/package.json +1 -1
  93. package/wiki/CHANGES.md +13 -7
  94. package/wiki/ECOSYSTEM.md +7 -7
  95. package/wiki/GETTING-STARTED.md +3 -3
  96. package/wiki/QUERY-LANGUAGE.md +1 -1
  97. package/docs/lib_collect-connections.js.html +0 -39
  98. package/docs/lib_collect-drivers.js.html +0 -52
  99. package/docs/lib_collect-features.js.html +0 -36
  100. package/docs/lib_collect-schemas.js.html +0 -94
  101. package/docs/lib_index.js.html +0 -6
  102. package/docs/method_model_create.js.html +0 -35
  103. package/docs/method_model_drop.js.html +0 -34
  104. package/docs/method_model_exists.js.html +0 -40
  105. package/docs/method_record_count.js.html +0 -69
  106. package/docs/method_record_create.js.html +0 -114
  107. package/docs/method_record_find-all.js.html +0 -44
  108. package/docs/method_record_find-one.js.html +0 -73
  109. package/docs/method_record_find.js.html +0 -118
  110. package/docs/method_record_get.js.html +0 -92
  111. package/docs/method_record_remove.js.html +0 -75
  112. package/docs/method_record_update.js.html +0 -107
  113. package/docs/method_record_upsert.js.html +0 -54
  114. package/docs/method_sanitize_body.js.html +0 -88
  115. package/docs/method_sanitize_date.js.html +0 -30
  116. package/docs/method_sanitize_id.js.html +0 -20
  117. package/docs/module-Lib.html +0 -3
  118. package/lib/collect-connections.js +0 -56
  119. package/lib/collect-drivers.js +0 -35
  120. package/lib/collect-features.js +0 -40
  121. package/lib/factory/driver.js +0 -577
  122. package/lib/factory/model/sanitize-id.js +0 -7
  123. package/lib/index.js +0 -3
@@ -1,9 +1,23 @@
1
1
  import path from 'path'
2
2
 
3
- export const omittedOptionsKeys = ['req', 'reply', 'trx']
3
+ /**
4
+ * Helper functions for model operations in the Dobo framework.
5
+ *
6
+ * @module Helper/Model
7
+ */
4
8
 
9
+ /**
10
+ * Clone options object and omit some keys to avoid reference issues
11
+ *
12
+ * @method
13
+ * @name cloneOptions
14
+ * @memberof module:Helper/Model
15
+ * @param {DoboModel.TOptions} [options={}]
16
+ * @returns {DoboModel.TOptions} Cloned options object
17
+ */
5
18
  export function cloneOptions (options = {}) {
6
19
  const { cloneDeep, omit } = this.app.lib._
20
+ const omittedOptionsKeys = ['req', 'reply', 'trx']
7
21
  const nOptions = cloneDeep(omit(options, omittedOptionsKeys))
8
22
  for (const key of omittedOptionsKeys) {
9
23
  nOptions[key] = options[key]
@@ -11,6 +25,16 @@ export function cloneOptions (options = {}) {
11
25
  return nOptions
12
26
  }
13
27
 
28
+ /**
29
+ * Executes a hook function with the provided name and arguments.
30
+ * @async
31
+ * @memberof module:Helper/Model
32
+ * @method
33
+ * @name execHook
34
+ * @param {string} name - The name of the hook to execute.
35
+ * @param {...any} args - Arguments to pass to the hook function.
36
+ * @returns {Promise<void>}
37
+ */
14
38
  export async function execHook (name, ...args) {
15
39
  const { runHook } = this.app.bajo
16
40
  const { camelCase, last, kebabCase } = this.app.lib._
@@ -28,6 +52,16 @@ export async function execHook (name, ...args) {
28
52
  }
29
53
  }
30
54
 
55
+ /**
56
+ * Executes a model hook function with the provided name and arguments.
57
+ * @async
58
+ * @memberof module:Helper/Model
59
+ * @method
60
+ * @name execModelHook
61
+ * @param {string} name - The name of the model hook to execute.
62
+ * @param {...any} args - Arguments to pass to the model hook function.
63
+ * @returns {Promise<void>}
64
+ */
31
65
  export async function execModelHook (name, ...args) {
32
66
  const { last } = this.app.lib._
33
67
  const { runModelHook } = this.app.dobo
@@ -35,6 +69,16 @@ export async function execModelHook (name, ...args) {
35
69
  if (!noModelHook) await runModelHook(this, name, ...args)
36
70
  }
37
71
 
72
+ /**
73
+ * Executes a dynamic hook function with the provided name and arguments.
74
+ * @async
75
+ * @memberof module:Helper/Model
76
+ * @method
77
+ * @name execDynHook
78
+ * @param {string} name - The name of the dynamic hook to execute.
79
+ * @param {...any} args - Arguments to pass to the dynamic hook function.
80
+ * @returns {Promise<void>}
81
+ */
38
82
  export async function execDynHook (name, ...args) {
39
83
  const { last, orderBy } = this.app.lib._
40
84
  const opts = last(args)
@@ -49,6 +93,16 @@ export async function execDynHook (name, ...args) {
49
93
  return results
50
94
  }
51
95
 
96
+ /**
97
+ * Executes validation on the provided body with the given options.
98
+ * @async
99
+ * @memberof module:Helper/Model
100
+ * @method
101
+ * @name execValidation
102
+ * @param {Object} body - The data to validate.
103
+ * @param {DoboModel.TOptions} [options={}] - Validation options.
104
+ * @returns {Promise<Object>} The result of the validation.
105
+ */
52
106
  export async function execValidation (body, options = {}) {
53
107
  const { uniq } = this.app.lib._
54
108
  const { validation = {} } = options
@@ -62,10 +116,15 @@ export async function execValidation (body, options = {}) {
62
116
  }
63
117
 
64
118
  /**
65
- * Break any reference to the original and get the new options
66
- *
67
- * @param {Object} options
68
- * @returns {Object}
119
+ * Prepares and returns the filter and options for a given action.
120
+ * @async
121
+ * @memberof module:Helper/Model
122
+ * @method
123
+ * @name getFilterAndOptions
124
+ * @param {DoboModel.TFilter} filter - The filter criteria.
125
+ * @param {DoboModel.TOptions} options - The options for the action.
126
+ * @param {string} action - The action being performed.
127
+ * @returns {Promise<{filter: DoboModel.TFilter, options: DoboModel.TOptions}>} The prepared filter and options.
69
128
  */
70
129
  export async function getFilterAndOptions (filter = {}, options = {}, action) {
71
130
  const { cloneDeep } = this.app.lib._
@@ -100,6 +159,17 @@ export async function getFilterAndOptions (filter = {}, options = {}, action) {
100
159
  return { filter: nFilter, options: nOptions }
101
160
  }
102
161
 
162
+ /**
163
+ * Handles a request for a given action trigger.
164
+ * @async
165
+ * @memberof module:Helper/Model
166
+ * @method
167
+ * @name handleReq
168
+ * @param {string|number} id - The ID of the record.
169
+ * @param {string} trigger - The action trigger (e.g., 'created', 'updated', 'removed').
170
+ * @param {DoboModel.TOptions} [options={}] - Additional options for handling the request.
171
+ * @returns {Promise<void>}
172
+ */
103
173
  export async function handleReq (id, trigger, options = {}) {
104
174
  const { upperFirst } = this.app.lib._
105
175
  if (options.req) {
@@ -108,6 +178,17 @@ export async function handleReq (id, trigger, options = {}) {
108
178
  }
109
179
  }
110
180
 
181
+ /**
182
+ * Merges attachment information into the given record.
183
+ * @async
184
+ * @memberof module:Helper/Model
185
+ * @method
186
+ * @name mergeAttachmentInfo
187
+ * @param {Object} rec - The record to merge attachment info into.
188
+ * @param {string} source - The source file path of the attachment.
189
+ * @param {Object} options - Additional options including mimeType, stats, and fullPath.
190
+ * @returns {Promise<void>}
191
+ */
111
192
  export async function mergeAttachmentInfo (rec, source, options = {}) {
112
193
  if (!this.app.waibu) return
113
194
  const { mimeType, stats, fullPath } = options
@@ -124,6 +205,18 @@ export async function mergeAttachmentInfo (rec, source, options = {}) {
124
205
  }
125
206
  }
126
207
 
208
+ /**
209
+ * Gets the attachment path for a given record and field.
210
+ * @async
211
+ * @memberof module:Helper/Model
212
+ * @method
213
+ * @name getAttachmentPath
214
+ * @param {string|number} id - The ID of the record.
215
+ * @param {string} field - The field name of the attachment.
216
+ * @param {string} file - The file name of the attachment.
217
+ * @param {Object} options - Additional options, including dirOnly.
218
+ * @returns {Promise<string>} The path to the attachment.
219
+ */
127
220
  export async function getAttachmentPath (id, field, file, options = {}) {
128
221
  const { fs } = this.app.lib
129
222
  const dir = `${this.app.getPluginDataDir(this.app.dobo.ns)}/attachment/${this.name}/${id}`
@@ -133,6 +226,16 @@ export async function getAttachmentPath (id, field, file, options = {}) {
133
226
  return path
134
227
  }
135
228
 
229
+ /**
230
+ * Copies attachments for a given record.
231
+ * @name copyAttachment
232
+ * @async
233
+ * @memberof module:Helper/Model
234
+ * @method
235
+ * @param {string|number} id - The ID of the record.
236
+ * @param {Object} options - Additional options for copying attachments.
237
+ * @returns {Promise<Array>} The copied attachment records.
238
+ */
136
239
  export async function copyAttachment (id, options = {}) {
137
240
  if (!this.app.waibu) return
138
241
  if (!this.options.attachment) return
@@ -157,6 +260,17 @@ export async function copyAttachment (id, options = {}) {
157
260
  return result
158
261
  }
159
262
 
263
+ /**
264
+ * Handles attachment uploads for a given record and trigger.
265
+ * @async
266
+ * @memberof module:Helper/Model
267
+ * @method
268
+ * @name handleAttachmentUpload
269
+ * @param {string|number} id - The ID of the record.
270
+ * @param {string} trigger - The action trigger (e.g., 'added', 'removed').
271
+ * @param {Object} options - Additional options for handling the upload.
272
+ * @returns {Promise<void>}
273
+ */
160
274
  export async function handleAttachmentUpload (id, trigger, options = {}) {
161
275
  if (!this.options.attachment) return
162
276
  const { fs } = this.app.lib
@@ -169,6 +283,16 @@ export async function handleAttachmentUpload (id, trigger, options = {}) {
169
283
  return copyAttachment.call(this, id, { req, mimeType, stats, setFile, setField })
170
284
  }
171
285
 
286
+ /**
287
+ * Gets reference records for the given records.
288
+ * @async
289
+ * @memberof module:Helper/Model
290
+ * @method
291
+ * @name getRefs
292
+ * @param {Array<Object>} records - The records to get references for.
293
+ * @param {Object} options - Additional options for fetching references.
294
+ * @returns {Promise<void>}
295
+ */
172
296
  export async function getRefs (records = [], options = {}) {
173
297
  const { isSet } = this.app.lib.aneka
174
298
  const { uniq, without, get } = this.app.lib._
@@ -224,12 +348,29 @@ export async function getRefs (records = [], options = {}) {
224
348
  }
225
349
  }
226
350
 
351
+ /**
352
+ * Builds a sanitized filter query for the given filter.
353
+ * @memberof module:Helper/Model
354
+ * @method
355
+ * @name buildFilterQuery
356
+ * @param {Object} filter - The filter object containing query parameters.
357
+ * @returns {Object} The sanitized query object.
358
+ */
227
359
  export function buildFilterQuery (filter = {}) {
228
360
  const { parseQuery } = this.app.dobo
229
361
  const query = parseQuery(filter.query ?? {}, this, false)
230
362
  return sanitizeQuery.call(this, query)
231
363
  }
232
364
 
365
+ /**
366
+ * Sanitizes a query object by ensuring that its fields and values conform to the model's schema.
367
+ * @memberof module:Helper/Model
368
+ * @method
369
+ * @name sanitizeQuery
370
+ * @param {Object} query - The query object to sanitize.
371
+ * @param {string} parent - The parent field name, if applicable.
372
+ * @returns {Object} The sanitized query object.
373
+ */
233
374
  export function sanitizeQuery (query = {}, parent) {
234
375
  const { isPlainObject, isArray, find, cloneDeep } = this.app.lib._
235
376
  const { isSet } = this.app.lib.aneka
@@ -277,6 +418,14 @@ export function sanitizeQuery (query = {}, parent) {
277
418
  return obj
278
419
  }
279
420
 
421
+ /**
422
+ * Builds a search filter from the given filter object.
423
+ * @memberof module:Helper/Model
424
+ * @method
425
+ * @name buildFilterSearch
426
+ * @param {Object} filter - The filter object containing search parameters.
427
+ * @returns {Object} The constructed search filter.
428
+ */
280
429
  export function buildFilterSearch (filter = {}) {
281
430
  const { isPlainObject, trim, has, uniq } = this.app.lib._
282
431
  const search = filter.search ?? {}
@@ -315,8 +464,8 @@ export function buildFilterSearch (filter = {}) {
315
464
  }
316
465
  }
317
466
  if (has(items, '*')) s['*'] = items['*']
318
- if (this.driver.idField.name !== 'id') {
319
- const search = JSON.stringify(s).replaceAll('"id"', `"${this.driver.idField.name}"`)
467
+ if (this.adapter.idField.name !== 'id') {
468
+ const search = JSON.stringify(s).replaceAll('"id"', `"${this.adapter.idField.name}"`)
320
469
  try {
321
470
  s = JSON.parse(search)
322
471
  } catch (err) {}
@@ -325,17 +474,19 @@ export function buildFilterSearch (filter = {}) {
325
474
  }
326
475
 
327
476
  /**
328
- * Prepare records pagination:
329
- * - making sure records limit is obeyed
330
- * - making sure page is a positive value
331
- * - if skip is given, recalculate limit to use skip instead of page number
332
- * - Build sort info
477
+ * Prepare pagination parameters for a query:
478
+ * - Ensures that the limit does not exceed the maximum allowed limit.
479
+ * - Ensures that the page number is within the allowed range.
480
+ * - Calculates the number of records to skip based on the page and limit.
481
+ * - Builds the sort order based on the provided sort input.
333
482
  *
334
- * @method
335
483
  * @async
336
- * @param {Object} [filter={}] - Filter object
337
- * @param {Object} options - Options
338
- * @returns {TRecordPagination}
484
+ * @memberof module:Helper/Model
485
+ * @method
486
+ * @name preparePagination
487
+ * @param {DoboModel.TFilter} filter - The filter object containing pagination parameters.
488
+ * @param {DoboModel.TOptions} options - Additional options for pagination.
489
+ * @returns {Object} The prepared pagination parameters including limit, page, skip, and sort.
339
490
  */
340
491
  export function preparePagination (filter = {}, options = {}) {
341
492
  const { isEmpty, map, each, isPlainObject, isString, trim, keys } = this.app.lib._
@@ -406,6 +557,15 @@ export function preparePagination (filter = {}, options = {}) {
406
557
  return { limit, page, skip, sort }
407
558
  }
408
559
 
560
+ /**
561
+ * Clears the cache for a specific record ID and related find operations.
562
+ * @async
563
+ * @memberof module:Helper/Model
564
+ * @method
565
+ * @name clearCache
566
+ * @param {string|number} id - The ID of the record for which to clear the cache.
567
+ * @returns {Promise<void>}
568
+ */
409
569
  export async function clearCache (id) {
410
570
  const { clear } = this.app.bajoCache ?? {}
411
571
  if (!clear) return
@@ -1,4 +1,4 @@
1
- import { getAttachmentPath } from './_util.js'
1
+ import { getAttachmentPath } from './helper.js'
2
2
  import path from 'path'
3
3
  const action = 'removeAttachment'
4
4
 
@@ -1,35 +1,18 @@
1
- import { getFilterAndOptions, execHook, execModelHook, execDynHook, getRefs, handleReq, clearCache } from './_util.js'
1
+ import { getFilterAndOptions, execHook, execModelHook, execDynHook, getRefs, handleReq, clearCache } from './helper.js'
2
2
  const action = 'removeRecord'
3
3
 
4
4
  /**
5
- * @typedef {Object} TRecordRemoveOptions
6
- * @see Dobo#recordRemove
7
- * @property {boolean} [dataOnly=true] - If ```true``` (default) returns deleted record. Otherwise {@link TRecordRemoveResult}
8
- * @property {boolean} [noHook=false] - If ```true```, no model's hook will be executed
9
- * @property {boolean} [noModelHook=false] - If ```true```, no model's feature hook will be executed
10
- * @property {boolean} [noResult=false] - If ```true```, returns nothing
11
- * @property {boolean} [fields=[]] - If not empty, return only these fields EXCLUDING hidden fields
12
- * @property {boolean} [hidden=[]] - Additional fields to hide, in addition the one set in model's model
13
- * @property {boolean} [forceNoHidden=false] - If ```true```, hidden fields will be ignored and ALL fields will be returned
14
- */
15
-
16
- /**
17
- * Remove existing record by it's ID. All attachments bound to this record will also be removed forever.
5
+ * Removes a record from the model's underlying data store based on the provided ID.
18
6
  *
19
- * Example:
20
- * ```javascript
21
- * const { recordRemove } = this.app.dobo
22
- * const result = await recordRemove('CdbCountry', 'ID')
23
- * ```
24
- *
25
- * @method
26
- * @memberof Model
7
+ * If no arguments are provided, it automatically turns into a chainable {@link DoboAction} object.
27
8
  * @async
28
- * @instance
29
- * @name removeRecord
30
- * @param {(string|number)} id - Record's ID
31
- * @param {TRecordRemoveOptions} [options={}]
32
- * @returns {(TRecordRemoveResult|Object)} Return the removed record if ```options.dataOnly``` is set. {@link TRecordRemoveResult} otherwise
9
+ * @memberof DoboModel
10
+ * @method
11
+ * @param {string|number} id - The ID of the record to be removed.
12
+ * @param {DoboModel.TOptions} [opts={}] - Options object.
13
+ * @see {@link module:Hook.beforeRemoveRecord}
14
+ * @see {@link module:Hook.afterRemoveRecord}
15
+ * @returns {DoboAction|DoboModel.TResultRemoveRecord|DoboModel.TRecord}
33
16
  */
34
17
  async function removeRecord (...args) {
35
18
  if (args.length === 0) return this.action(action, ...args)
@@ -44,7 +27,7 @@ async function removeRecord (...args) {
44
27
  await execHook.call(this, 'beforeRemoveRecord', id, options)
45
28
  await execModelHook.call(this, 'beforeRemoveRecord', id, options)
46
29
  await execDynHook.call(this, 'beforeRemoveRecord', id, options)
47
- const result = options.record ?? (await this.driver._removeRecord(this, id, options)) ?? {}
30
+ const result = options.record ?? (await this.adapter._removeRecord(this, id, options)) ?? {}
48
31
  if (noResult) return
49
32
  await handleReq.call(this, result.oldData.id, 'removed', options)
50
33
  await clearCache.call(this, id)
@@ -1,18 +1,20 @@
1
1
  /**
2
- * Sanitize payload body against its model
3
- *
2
+ * Sanitize the payload body based on the model's properties and rules.
4
3
  * @method
5
- * @memberof Dobo
4
+ * @memberof DoboModel
6
5
  * @async
7
- * @param {Object} [options={}]
8
- * @param {Object} [options.body={}]
9
- * @param {Object} [options.model={}]
10
- * @param {boolean} [options.partial=false]
11
- * @param {boolean} [options.strict=false]
12
- * @param {Array} [options.extFields=[]]
13
- * @returns {Object}
6
+ * @param {Object} [options={}] - Options object
7
+ * @param {Object} [options.body={}] - Body object to sanitize
8
+ * @param {boolean} [options.partial=false] - If `true`, only the fields present in the body will be sanitized
9
+ * @param {boolean} [options.strict=false] - If `true`, only the fields defined in the model's properties will be included in the sanitized body
10
+ * @param {Array} [options.extFields=[]] - Additional fields to include in the sanitized body
11
+ * @param {boolean} [options.truncateString=false] - If `true`, string fields will be truncated to their maximum length defined in the model's properties
12
+ * @param {string} [options.action] - The action being performed (e.g., 'createRecord', 'updateRecord', etc.)
13
+ * @param {boolean} [options.allProps=false] - If `true`, all properties including virtual ones will be considered for sanitization
14
+ * @returns {Object} Returns sanitized body object
14
15
  */
15
- async function sanitizeBody ({ body = {}, partial, strict, extFields = [], noDefault, truncateString, onlyTypes = [], action, allProps } = {}) {
16
+ async function sanitizeBody (options = {}) {
17
+ const { body = {}, partial, strict, extFields = [], truncateString, action, allProps } = options
16
18
  const { isSet } = this.app.lib.aneka
17
19
  const { sanitizeByType } = this.app.dobo
18
20
  const { omit, has } = this.app.lib._
@@ -39,7 +41,7 @@ async function sanitizeBody ({ body = {}, partial, strict, extFields = [], noDef
39
41
  if (isSet(result[prop.name])) sanitize(prop.name, prop.type)
40
42
  if (result[prop.name] === null) continue
41
43
  if (truncateString && isSet(result[prop.name]) && ['string', 'text'].includes(prop.type)) result[prop.name] = result[prop.name].slice(0, prop.maxLength)
42
- if (prop.name.endsWith('Id') && isSet(result[prop.name]) && prop.type === 'string' && ['smallint', 'integer'].includes(this.driver.idField.type)) result[prop.name] = result[prop.name] + ''
44
+ if (prop.name.endsWith('Id') && isSet(result[prop.name]) && prop.type === 'string' && ['smallint', 'integer'].includes(this.adapter.idField.type)) result[prop.name] = result[prop.name] + ''
43
45
  if (result[prop.name] === undefined) omitted.push(prop.name)
44
46
  } catch (err) {
45
47
  details.push({ field: prop.name, error: err.message, value: body[prop.name], ext: { type: prop.type } })
@@ -1,10 +1,23 @@
1
- async function sanitizeFixture ({ body = {}, lookupValue = {}, noLookup } = {}, options = {}) {
1
+ /**
2
+ * Sanitize a fixture object by resolving lookup values and ensuring proper data types.
3
+ * @method
4
+ * @async
5
+ * @memberof DoboModel
6
+ * @param {Object} [params={}] - Parameters object
7
+ * @param {Object} [params.body={}] - Body object to sanitize
8
+ * @param {Object} [params.lookupValue={}] - Lookup values for resolving references in the body
9
+ * @param {boolean} [params.noLookup=false] - If `true`, skip lookup resolution
10
+ * @param {DoboModel.TOptions} [opts={}] - Options object for additional configurations
11
+ * @returns {Object} Returns sanitized body object
12
+ */
13
+ async function sanitizeFixture (params = {}, opts = {}) {
14
+ const { body = {}, lookupValue = {}, noLookup } = params
2
15
  const { isString, isArray, pullAt, cloneDeep } = this.app.lib._
3
16
  const { isSet } = this.app.lib.aneka
4
17
  const lv = cloneDeep(lookupValue)
5
18
  const deleted = {}
6
- const opts = {
7
- ...options,
19
+ const options = {
20
+ ...opts,
8
21
  noModelHook: false,
9
22
  noHook: true,
10
23
  noDynHook: true,
@@ -16,7 +29,7 @@ async function sanitizeFixture ({ body = {}, lookupValue = {}, noLookup } = {},
16
29
  deleted[key] = deleted[key] ?? []
17
30
  if (!noLookup) {
18
31
  if (isString(val) && val.slice(0, 2) === '?:') {
19
- body[key] = await this._simpleLookup(val.slice(2), lv, opts)
32
+ body[key] = await this._simpleLookup(val.slice(2), lv, options)
20
33
  lv[key] = body[key]
21
34
  } else if (isArray(val)) {
22
35
  for (const idx in val) {
@@ -1,13 +1,16 @@
1
1
  /**
2
- * Sanitize record to conform with the model's definition
3
- *
2
+ * Sanitize a record object based on the model's properties and rules.
4
3
  * @method
5
4
  * @async
5
+ * @memberof DoboModel
6
6
  * @param {Object} [record] - Record object
7
- * @param {Array} [options.fields] - Array of field names to be picked
8
- * @param {Object} [options.hidden=[]] - Additional fields to be hidden in addition the one defined in model
9
- * @param {boolean} [options.forceNoHidden] - Force ALL fields to be picked, thus ignoring hidden fields
10
- * @returns {Object}
7
+ * @param {DoboModel.TOptions} [opts={}] - Options object
8
+ * @param {Array} [opts.fields] - If not empty, only these fields will be included in the sanitized record
9
+ * @param {Array<string>} [opts.hidden=[]] - List of fields to hide from the sanitized record
10
+ * @param {boolean} [opts.forceNoHidden=false] - If `true`, force ALL fields to be picked, thus ignoring hidden fields. If an array, force all fields except those in the array to be picked.
11
+ * @param {boolean} [opts.fmt=false] - If `true`, add a `_fmt` property to the sanitized record with formatted values automatically based on the model's properties and rules
12
+ * @param {Object} [opts.req] - Request object, used for formatting values based on request context
13
+ * @returns {Object} Returns sanitized record object
11
14
  */
12
15
  async function sanitizeRecord (record = {}, opts = {}) {
13
16
  const { fields = [], hidden = [], forceNoHidden } = opts
@@ -1,11 +1,11 @@
1
1
  async function transaction (handler, ...args) {
2
- if (!this.driver.support.transaction) return handler.call(this)
2
+ if (!this.adapter.support.transaction) return handler.call(this)
3
3
 
4
4
  const { ns } = this.app.dobo
5
5
  const { camelCase } = this.app.lib._
6
6
  const { runHook } = this.app.bajo
7
7
  const name = 'afterTransaction'
8
- const result = await this.driver.transaction(this, handler, ...args)
8
+ const result = await this.adapter.transaction(this, handler, ...args)
9
9
  const [action, ...params] = args
10
10
  await runHook(`${ns}:${name}`, this.name, action, result, ...params)
11
11
  await runHook(`${ns}.${camelCase(this.name)}:${name}`, action, result, ...params)
@@ -1,48 +1,20 @@
1
- import { getFilterAndOptions, execHook, execValidation, execModelHook, execDynHook, getRefs, handleReq, clearCache } from './_util.js'
1
+ import { getFilterAndOptions, execHook, execValidation, execModelHook, execDynHook, getRefs, handleReq, clearCache } from './helper.js'
2
2
  import { onlyTypes } from './create-record.js'
3
3
  const action = 'updateRecord'
4
4
 
5
5
  /**
6
- * @typedef {Object} TRecordUpdateOptions
7
- * @see Model#updateRecord
8
- * @property {boolean} [dataOnly=true] - If ```true``` (default) returns record's object. Otherwise {@link TRecordUpdateResult}
9
- * @property {boolean} [noHook=false] - If ```true```, no model's hook will be executed
10
- * @property {boolean} [noModelHook=false] - If ```true```, no model's hook will be executed
11
- * @property {boolean} [noValidation=false] - If ```true```, no validation of data payload performed
12
- * @property {boolean} [noCheckUnique=false] - If ```true```, no unique validation for ID performed
13
- * @property {boolean} [noBodySanitizer=false] - If ```true```, accept data payload as is without sanitization
14
- * @property {boolean} [noRecordSanitizer=false] - If ```true```, accept result payload as is without sanitization
15
- * @property {boolean} [noResult=false] - If ```true```, returns nothing
16
- * @property {boolean} [truncateString=true] - If ```true``` (default), string is truncated to its model's ```maxLength```
17
- * @property {boolean} [partial=true] - If ```true``` (default), only updated values are saved. Otherwise replace all existing values with given payload
18
- * @property {boolean} [fields=[]] - If not empty, return only these fields EXCLUDING hidden fields
19
- * @property {boolean} [hidden=[]] - Additional fields to hide, in addition the one set in model's model
20
- * @property {boolean} [forceNoHidden=false] - If ```true```, hidden fields will be ignored and ALL fields will be returned
21
- */
22
-
23
- /**
24
- * Update a record by it's ID and body payload
6
+ * Updates an existing record in the model's underlying data store based on the provided ID and data.
25
7
  *
26
- * Example:
27
- * ```javascript
28
- * const { recordUpdate } = this.app.dobo
29
- * const { body } = {
30
- * name: 'Republic of Indonesia',
31
- * phoneCode: '+62'
32
- * }
33
- * const result = await recordUpdate('CdbCountry', 'ID', body)
34
- * ```
35
- *
36
- * @method
37
- * @memberof Dobo
8
+ * If no arguments are provided, it automatically turns into a chainable {@link DoboAction} object.
38
9
  * @async
39
- * @instance
40
- * @name recordUpdate
41
- * @param {string} name - Model's name
42
- * @param {(string|number)} id - Record's ID
43
- * @param {Object} body - Body payload
44
- * @param {TRecordUpdateOptions} [options={}]
45
- * @returns {(TRecordUpdateResult|Object)} Returns updated record if ```options.dataOnly``` is set. {@link TRecordUpdateResult} otherwise
10
+ * @memberof DoboModel
11
+ * @method
12
+ * @param {string|number} id - The ID of the record to be updated.
13
+ * @param {object} body - The data to update the record with.
14
+ * @param {DoboModel.TOptions} [opts] - Options object.
15
+ * @see {@link module:Hook.beforeUpdateRecord}
16
+ * @see {@link module:Hook.afterUpdateRecord}
17
+ * @returns {DoboAction|DoboModel.TResultUpdateRecord|DoboModel.TRecord}
46
18
  */
47
19
  async function updateRecord (...args) {
48
20
  if (args.length === 0) return this.action(action, ...args)
@@ -66,7 +38,7 @@ async function updateRecord (...args) {
66
38
  await execModelHook.call(this, 'beforeUpdateRecord', id, input, options)
67
39
  await execDynHook.call(this, 'beforeUpdateRecord', id, input, options)
68
40
  if (!noValidation) await execValidation.call(this, input, options)
69
- const result = await this.driver._updateRecord(this, id, input, options)
41
+ const result = await this.adapter._updateRecord(this, id, input, options)
70
42
  if (noResult) return
71
43
  await handleReq.call(this, result.data.id, 'updated', options)
72
44
  await clearCache.call(this, id)
@@ -1,4 +1,4 @@
1
- import { getFilterAndOptions, execHook, execModelHook, execDynHook, execValidation, getRefs, handleReq, clearCache } from './_util.js'
1
+ import { getFilterAndOptions, execHook, execModelHook, execDynHook, execValidation, getRefs, handleReq, clearCache } from './helper.js'
2
2
  const action = 'upsertRecord'
3
3
 
4
4
  async function native (body = {}, opts = {}) {
@@ -15,7 +15,7 @@ async function native (body = {}, opts = {}) {
15
15
  await execHook.call(this, 'beforeUpsertRecord', input, options)
16
16
  await execModelHook.call(this, 'beforeUpsertRecord', input, options)
17
17
  await execDynHook.call(this, 'beforeUpsertRecord', input, options)
18
- const result = options.record ?? (await this.driver._upsertRecord(this, input, options)) ?? {}
18
+ const result = options.record ?? (await this.adapter._upsertRecord(this, input, options)) ?? {}
19
19
  if (noResult) return
20
20
  await handleReq.call(this, result.data.id, 'upserted', options)
21
21
  await clearCache.call(this, body.id)
@@ -36,7 +36,7 @@ async function manual (body = {}, options = {}) {
36
36
  let old = false
37
37
  if (isSet(body.id)) {
38
38
  try {
39
- old = await this.driver._getRecord(this, body.id, { noHook: true, noModelHook: true })
39
+ old = await this.adapter._getRecord(this, body.id, { noHook: true, noModelHook: true })
40
40
  } catch (err) {
41
41
  }
42
42
  }
@@ -46,51 +46,22 @@ async function manual (body = {}, options = {}) {
46
46
  }
47
47
 
48
48
  /**
49
- * @typedef {Object} TRecordUpdateOptions
50
- * @see Model#updateRecord
51
- * @property {boolean} [dataOnly=true] - If ```true``` (default) returns record's object. Otherwise {@link TRecordUpdateResult}
52
- * @property {boolean} [noHook=false] - If ```true```, no model's hook will be executed
53
- * @property {boolean} [noModelHook=false] - If ```true```, no model's hook will be executed
54
- * @property {boolean} [noValidation=false] - If ```true```, no validation of data payload performed
55
- * @property {boolean} [noCheckUnique=false] - If ```true```, no unique validation for ID performed
56
- * @property {boolean} [noBodySanitizer=false] - If ```true```, accept data payload as is without sanitization
57
- * @property {boolean} [noRecordSanitizer=false] - If ```true```, accept result payload as is without sanitization
58
- * @property {boolean} [noResult=false] - If ```true```, returns nothing
59
- * @property {boolean} [truncateString=true] - If ```true``` (default), string is truncated to its model's ```maxLength```
60
- * @property {boolean} [partial=true] - If ```true``` (default), only updated values are saved. Otherwise replace all existing values with given payload
61
- * @property {boolean} [fields=[]] - If not empty, return only these fields EXCLUDING hidden fields
62
- * @property {boolean} [hidden=[]] - Additional fields to hide, in addition the one set in model's model
63
- * @property {boolean} [forceNoHidden=false] - If ```true```, hidden fields will be ignored and ALL fields will be returned
64
- */
65
-
66
- /**
67
- * Update a record by it's ID and body payload
49
+ * Upserts a record in the model's underlying data store. If a record with the specified ID exists, it updates that record; otherwise, it creates a new record.
68
50
  *
69
- * Example:
70
- * ```javascript
71
- * const { recordUpdate } = this.app.dobo
72
- * const { body } = {
73
- * name: 'Republic of Indonesia',
74
- * phoneCode: '+62'
75
- * }
76
- * const result = await recordUpdate('CdbCountry', 'ID', body)
77
- * ```
78
- *
79
- * @method
80
- * @memberof Dobo
51
+ * If no arguments are provided, it automatically turns into a chainable {@link DoboAction} object.
81
52
  * @async
82
- * @instance
83
- * @name recordUpdate
84
- * @param {string} name - Model's name
85
- * @param {(string|number)} id - Record's ID
86
- * @param {Object} body - Body payload
87
- * @param {TRecordUpdateOptions} [options={}]
88
- * @returns {(TRecordUpdateResult|Object)} Returns updated record if ```options.dataOnly``` is set. {@link TRecordUpdateResult} otherwise
53
+ * @memberof DoboModel
54
+ * @method
55
+ * @param {object} body - The data for the record to be upserted.
56
+ * @param {DoboModel.TOptions} [opts] - Options object.
57
+ * @see {@link module:Hook.beforeUpsertRecord}
58
+ * @see {@link module:Hook.afterUpsertRecord}
59
+ * @returns {DoboAction|DoboModel.TResultCreateRecord|DoboModel.TResultUpdateRecord|DoboModel.TRecord}
89
60
  */
90
61
  async function upsertRecord (...args) {
91
62
  if (args.length === 0) return this.action(action, ...args)
92
63
  const [body = {}, opts = {}] = args
93
- if (this.driver.upsertRecord) {
64
+ if (this.adapter.upsertRecord) {
94
65
  const { options } = await getFilterAndOptions.call(this, null, opts, action)
95
66
  return await native.call(this, body, options)
96
67
  }