dobo 2.38.0 → 2.39.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.
Files changed (160) hide show
  1. package/.bootorder +0 -0
  2. package/.github/FUNDING.yml +0 -0
  3. package/.github/workflows/repo-lockdown.yml +0 -0
  4. package/.jsdoc.conf.json +0 -0
  5. package/LICENSE +0 -0
  6. package/README.md +0 -0
  7. package/docs/Dobo.html +0 -0
  8. package/docs/DoboAction.html +0 -0
  9. package/docs/DoboAdapter.html +0 -0
  10. package/docs/DoboConnection.html +0 -0
  11. package/docs/DoboFeature.html +0 -0
  12. package/docs/DoboModel.html +0 -0
  13. package/docs/data/search.json +0 -0
  14. package/docs/extend_bajo_hook-docs.js.html +0 -0
  15. package/docs/external-Tools.html +0 -0
  16. package/docs/fonts/Inconsolata-Regular.ttf +0 -0
  17. package/docs/fonts/OpenSans-Regular.ttf +0 -0
  18. package/docs/fonts/WorkSans-Bold.ttf +0 -0
  19. package/docs/global.html +0 -0
  20. package/docs/index.html +0 -0
  21. package/docs/index.js.html +0 -0
  22. package/docs/lib_factory_action.js.html +0 -0
  23. package/docs/lib_factory_adapter.js.html +0 -0
  24. package/docs/lib_factory_connection.js.html +0 -0
  25. package/docs/lib_factory_feature.js.html +0 -0
  26. package/docs/lib_factory_model.js.html +0 -0
  27. package/docs/lib_factory_model_build.js.html +0 -0
  28. package/docs/lib_factory_model_clear-record.js.html +0 -0
  29. package/docs/lib_factory_model_count-record.js.html +0 -0
  30. package/docs/lib_factory_model_create-record.js.html +0 -0
  31. package/docs/lib_factory_model_drop.js.html +0 -0
  32. package/docs/lib_factory_model_exists.js.html +0 -0
  33. package/docs/lib_factory_model_find-all-record.js.html +0 -0
  34. package/docs/lib_factory_model_find-one-record.js.html +0 -0
  35. package/docs/lib_factory_model_find-record.js.html +0 -0
  36. package/docs/lib_factory_model_get-record.js.html +0 -0
  37. package/docs/lib_factory_model_helper.js.html +0 -0
  38. package/docs/lib_factory_model_remove-record.js.html +0 -0
  39. package/docs/lib_factory_model_sanitize-body.js.html +0 -0
  40. package/docs/lib_factory_model_sanitize-fixture.js.html +0 -0
  41. package/docs/lib_factory_model_sanitize-record.js.html +0 -0
  42. package/docs/lib_factory_model_update-record.js.html +0 -0
  43. package/docs/lib_factory_model_upsert-record.js.html +0 -0
  44. package/docs/lib_factory_model_validate.js.html +0 -0
  45. package/docs/lib_helper.js.html +0 -0
  46. package/docs/module-Helper.html +0 -0
  47. package/docs/module-Helper_Model.html +0 -0
  48. package/docs/module-Hook.html +0 -0
  49. package/docs/scripts/core.js +0 -0
  50. package/docs/scripts/core.min.js +0 -0
  51. package/docs/scripts/resize.js +0 -0
  52. package/docs/scripts/search.js +0 -0
  53. package/docs/scripts/search.min.js +5 -5
  54. package/docs/scripts/third-party/Apache-License-2.0.txt +0 -0
  55. package/docs/scripts/third-party/fuse.js +0 -0
  56. package/docs/scripts/third-party/hljs-line-num-original.js +0 -0
  57. package/docs/scripts/third-party/hljs-line-num.js +0 -0
  58. package/docs/scripts/third-party/hljs-original.js +0 -0
  59. package/docs/scripts/third-party/hljs.js +0 -0
  60. package/docs/scripts/third-party/popper.js +0 -0
  61. package/docs/scripts/third-party/tippy.js +0 -0
  62. package/docs/scripts/third-party/tocbot.js +0 -0
  63. package/docs/scripts/third-party/tocbot.min.js +0 -0
  64. package/docs/static/bitcoin.jpeg +0 -0
  65. package/docs/static/home.md +0 -0
  66. package/docs/static/logo-ecosystem.png +0 -0
  67. package/docs/static/logo.png +0 -0
  68. package/docs/styles/clean-jsdoc-theme-base.css +0 -0
  69. package/docs/styles/clean-jsdoc-theme-dark.css +0 -0
  70. package/docs/styles/clean-jsdoc-theme-light.css +0 -0
  71. package/docs/styles/clean-jsdoc-theme-scrollbar.css +0 -0
  72. package/docs/styles/clean-jsdoc-theme-without-scrollbar.min.css +0 -0
  73. package/docs/styles/clean-jsdoc-theme.min.css +0 -0
  74. package/extend/bajo/hook.js +236 -236
  75. package/extend/bajo/intl/en-US.json +0 -0
  76. package/extend/bajo/intl/id.json +0 -0
  77. package/extend/bajoCli/applet/aggregate.js +0 -0
  78. package/extend/bajoCli/applet/clear-record.js +0 -0
  79. package/extend/bajoCli/applet/connection.js +0 -0
  80. package/extend/bajoCli/applet/count-record.js +0 -0
  81. package/extend/bajoCli/applet/create-record.js +0 -0
  82. package/extend/bajoCli/applet/find-record.js +0 -0
  83. package/extend/bajoCli/applet/get-record.js +0 -0
  84. package/extend/bajoCli/applet/histogram.js +0 -0
  85. package/extend/bajoCli/applet/lib/post-process.js +0 -0
  86. package/extend/bajoCli/applet/model.js +0 -0
  87. package/extend/bajoCli/applet/rebuild-model.js +0 -0
  88. package/extend/bajoCli/applet/remove-record.js +0 -0
  89. package/extend/bajoCli/applet/update-record.js +0 -0
  90. package/extend/bajoCli/applet.js +0 -0
  91. package/extend/dobo/adapter/memory.js +175 -175
  92. package/extend/dobo/feature/created-at.js +0 -0
  93. package/extend/dobo/feature/dt.js +0 -0
  94. package/extend/dobo/feature/image.js +0 -0
  95. package/extend/dobo/feature/immutable.js +0 -0
  96. package/extend/dobo/feature/removed-at.js +0 -0
  97. package/extend/dobo/feature/unique.js +0 -0
  98. package/extend/dobo/feature/updated-at.js +0 -0
  99. package/extend/waibuMpa/route/attachment/@model/@id/@field/@file.js +0 -0
  100. package/extend/waibuStatic/virtual.json +0 -0
  101. package/index.js +0 -0
  102. package/lib/factory/action.js +287 -287
  103. package/lib/factory/adapter.js +1055 -1055
  104. package/lib/factory/connection.js +109 -109
  105. package/lib/factory/feature.js +52 -52
  106. package/lib/factory/model/aggregate.js +23 -23
  107. package/lib/factory/model/build.js +25 -25
  108. package/lib/factory/model/bulk-create-record.js +36 -36
  109. package/lib/factory/model/clear-record.js +30 -30
  110. package/lib/factory/model/count-record.js +42 -42
  111. package/lib/factory/model/create-attachment.js +36 -36
  112. package/lib/factory/model/create-record.js +49 -49
  113. package/lib/factory/model/drop.js +26 -26
  114. package/lib/factory/model/exists.js +26 -26
  115. package/lib/factory/model/find-all-record.js +112 -112
  116. package/lib/factory/model/find-attachment.js +28 -28
  117. package/lib/factory/model/find-one-record.js +38 -38
  118. package/lib/factory/model/find-record.js +82 -82
  119. package/lib/factory/model/get-attachment.js +15 -15
  120. package/lib/factory/model/get-record.js +52 -52
  121. package/lib/factory/model/helper.js +575 -576
  122. package/lib/factory/model/histogram.js +23 -23
  123. package/lib/factory/model/list-attachment.js +40 -40
  124. package/lib/factory/model/load-fixtures.js +63 -63
  125. package/lib/factory/model/remove-attachment.js +24 -24
  126. package/lib/factory/model/remove-record.js +44 -44
  127. package/lib/factory/model/sanitize-body.js +57 -57
  128. package/lib/factory/model/sanitize-fixture.js +56 -56
  129. package/lib/factory/model/sanitize-record.js +80 -80
  130. package/lib/factory/model/transaction.js +15 -15
  131. package/lib/factory/model/update-attachment.js +9 -9
  132. package/lib/factory/model/update-record.js +58 -58
  133. package/lib/factory/model/upsert-record.js +71 -71
  134. package/lib/factory/model/validate.js +0 -0
  135. package/lib/factory/model.js +503 -503
  136. package/lib/helper.js +463 -463
  137. package/package.json +1 -1
  138. package/test/e2e/_run.js +12 -12
  139. package/test/e2e/e2e-factory-process.test.js +25 -25
  140. package/test/e2e/e2e-model-adapter-process.test.js +52 -52
  141. package/test/integration/aspect-01-start-connections.test.js +33 -33
  142. package/test/integration/aspect-02-query-regex.test.js +34 -34
  143. package/test/integration/aspect-03-model-adapter-flow.test.js +88 -88
  144. package/test/unit/_stub.js +244 -244
  145. package/test/unit/dobo-action.test.js +58 -58
  146. package/test/unit/dobo-connection.test.js +37 -37
  147. package/test/unit/dobo-core.test.js +136 -136
  148. package/test/unit/dobo-feature.test.js +25 -25
  149. package/test/unit/dobo-model.test.js +115 -115
  150. package/test/unit/helper-sanitize-all.test.js +40 -40
  151. package/test/unit/helper-sanitize-ref.test.js +64 -64
  152. package/wiki/APPLETS.md +0 -0
  153. package/wiki/CHANGES.md +477 -469
  154. package/wiki/CONFIG.md +0 -0
  155. package/wiki/CONTRIBUTING.md +0 -0
  156. package/wiki/DEV-GUIDE.md +0 -0
  157. package/wiki/ECOSYSTEM.md +0 -0
  158. package/wiki/GETTING-STARTED.md +0 -0
  159. package/wiki/QUERY-LANGUAGE.md +0 -0
  160. package/wiki/USER-GUIDE.md +0 -0
@@ -1,503 +1,503 @@
1
- import { sanitizeAll, sanitizeRef } from '../helper.js'
2
- import clearRecord from './model/clear-record.js'
3
- import countRecord from './model/count-record.js'
4
- import createRecord from './model/create-record.js'
5
- import drop from './model/drop.js'
6
- import findAllRecord from './model/find-all-record.js'
7
- import findOneRecord from './model/find-one-record.js'
8
- import findRecord from './model/find-record.js'
9
- import getRecord from './model/get-record.js'
10
- import histogram from './model/histogram.js'
11
- import aggregate from './model/aggregate.js'
12
- import exists from './model/exists.js'
13
- import build from './model/build.js'
14
- import loadFixtures from './model/load-fixtures.js'
15
- import removeRecord from './model/remove-record.js'
16
- import updateRecord from './model/update-record.js'
17
- import createAttachment from './model/create-attachment.js'
18
- import updateAttachment from './model/update-attachment.js'
19
- import getAttachment from './model/get-attachment.js'
20
- import removeAttachment from './model/remove-attachment.js'
21
- import findAttachment from './model/find-attachment.js'
22
- import sanitizeBody from './model/sanitize-body.js'
23
- import sanitizeRecord from './model/sanitize-record.js'
24
- import sanitizeFixture from './model/sanitize-fixture.js'
25
- import upsertRecord from './model/upsert-record.js'
26
- import bulkCreateRecord from './model/bulk-create-record.js'
27
- import listAttachment from './model/list-attachment.js'
28
- import transaction from './model/transaction.js'
29
- import validate from './model/validate.js'
30
-
31
- /**
32
- * @typedef {Object} TOptions
33
- * @memberof DoboModel
34
- * @type {Object}
35
- * @property {boolean} [noHook=false] - If `true`, no model's hook will be executed
36
- * @property {boolean} [noModelHook=false] - If `true`, no model's hook will be executed
37
- * @property {boolean} [noDynHook=false] - If `true`, no dynamic hook will be executed
38
- * @property {boolean} [noValidation=false] - If `true`, no validation of data payload performed
39
- * @property {boolean} [noCheckUnique=false] - If `true`, no unique validation for ID performed
40
- * @property {boolean} [noBodySanitizer=false] - If `true`, accept data payload as is without sanitization
41
- * @property {boolean} [noRecordSanitizer=false] - If `true`, accept result payload as is without sanitization
42
- * @property {boolean} [noResult=false] - If `true`, returns nothing
43
- * @property {boolean} [truncateString=true] - If `true` (default), string is truncated to its model's `maxLength`
44
- * @property {boolean} [partial] - If `true`, only updated values are saved. Otherwise replace all existing values with given payload. Defaults to `true` for `updateRecord`
45
- * @property {boolean} [dataOnly=true] - If `true` (default) returns only record's object. Otherwise {@link DoboModel.TResult}
46
- */
47
-
48
- /**
49
- * Sort order key used in pagination and sorting of records. It can be either:
50
- * - `1` for ascending order (the default, if none is provided) or
51
- * - `-1` for descending order.
52
- * @typedef {string} TSortOrder
53
- * @memberof DoboModel
54
- */
55
-
56
- /**
57
- * Key value pairs used as sort order in pagination and sorting of records. It is an object where:
58
- * - Key represents model's field name
59
- * - Value represents its sort order
60
- *
61
- * @typedef {Object.<string, DoboModel.TSortOrder>} TSort
62
- * @memberof DoboModel
63
- * @example
64
- * // to sort by firstName (ascending) and lastName (descending)
65
- * const sort = {
66
- * firstName: 1,
67
- * lastName: -1
68
- * }
69
- */
70
-
71
- /**
72
- * @typedef {Object} TPagination
73
- * @memberof DoboModel
74
- * @property {number} limit - Number of records per page
75
- * @property {number} page - Page number
76
- * @property {number} [skip] - Records to skip. If not provided, it will be calculated based on `limit` and `page`
77
- */
78
-
79
- /**
80
- * Property value type definition. Used for building property `values` in a model which is an array of this object type.
81
- * @typedef TPropertyValue
82
- * @memberof DoboModel
83
- * @type {Object}
84
- * @property {string} value - The actual value of the property
85
- * @property {string} text - The display text for the property value, which can be translated based on the request context
86
- */
87
-
88
- /**
89
- * Formatted version of a record's property values. It is an object where:
90
- * - Key represents the property name
91
- * - Value represents its formatted value according to the property's data type or formatting function. Data type is ALWAYS in string.
92
- *
93
- * This record is then wrapped in a `_fmt` property.
94
- * @typedef TRecordFormatted
95
- * @memberof DoboModel
96
- * @type {Object}
97
- * @property {Object} [_fmt={}] - Wrapper
98
- * @example
99
- * {
100
- * _fmt: {
101
- * id: "1",
102
- * item: "Laptop",
103
- * createdAt: "Jan 1, 2024, 12:00 AM",
104
- * isActive: "True",
105
- * price: "2,500.00"
106
- * }
107
- * }
108
- */
109
-
110
- /**
111
- * Reference record related to a record's result. It is an object where:
112
- * - Key represents the name of the referenced record. Normally, it is the referenced model's name in camelCase format.
113
- * - Value represents its reference record. It can be an object or an array of objects, depending on the relationship type.
114
- *
115
- * This record is then wrapped in a `_ref` property in the result object.
116
- * @typedef TRecordRef
117
- * @memberof DoboModel
118
- * @type {Object}
119
- * @property {Object} [_ref={}] - Wrapper
120
- * @property {Object|Array} [_ref.<string>.<DoboModel.TRecord>] - Reference record
121
- * @example
122
- * {
123
- * _ref: {
124
- * user: {
125
- * id: 1,
126
- * name: "John Doe"
127
- * },
128
- * posts: [
129
- * {
130
- * id: 1,
131
- * title: "Post Title"
132
- * },
133
- * {
134
- * id: 2,
135
- * title: "Another Post Title"
136
- * }
137
- * ]
138
- * }
139
- * }
140
- */
141
-
142
- /**
143
- * The record returned from a model's action. It is an object where:
144
- * - Key represents the property name
145
- * - Value represents its value according to the property's data type
146
- *
147
- * This record is then merged with {@link DoboModel.TRecordFormatted} and {@link DoboModel.TRecordRef} to form the final record returned from a model's action.
148
- * @typedef TRecord
149
- * @memberof DoboModel
150
- * @type {Object & DoboModel.TRecordFormatted & DoboModel.TRecordRef}
151
- * @example
152
- * {
153
- * id: 1,
154
- * item: "Laptop",
155
- * createdAt: "2024-01-01T00:00:00.000Z",
156
- * isActive: true,
157
- * price: 2500.00,
158
- * _fmt: {
159
- * id: "1",
160
- * item: "Laptop",
161
- * createdAt: "Jan 1, 2024, 12:00 AM",
162
- * isActive: "True",
163
- * price: "2,500.00"
164
- * },
165
- * _ref: {
166
- * user: {
167
- * id: 1,
168
- * name: "John Doe"
169
- * }
170
- * }
171
- * }
172
- */
173
-
174
- /**
175
- * The result returned from a model's action other than `find*`, `{create|update|remove}Record` if `dataOnly` is set to `false`.
176
- * @typedef TResultRecord
177
- * @memberof DoboModel
178
- * @type {Object}
179
- * @property {Array<DoboModel.TRecord>} data - The data returned from the action
180
- */
181
-
182
- /**
183
- * The result returned from a model's `find*` actions if `dataOnly` is set to `false`.
184
- * @typedef TResultFindRecord
185
- * @memberof DoboModel
186
- * @type {Object}
187
- * @property {Array<DoboModel.TRecord>} data - The data returned from the action
188
- * @property {number} [count] - The total number of records found, if applicable
189
- * @property {boolean} [cached] - Indicates if the result was retrieved from cache, if applicable. Only available if `bajoCache` is used
190
- * @property {Array} [warnings] - Any warnings generated during the action, if applicable
191
- */
192
-
193
- /**
194
- * The result returned from a model's `getRecord` action if `dataOnly` is set to `false`.
195
- * @typedef TResultGetRecord
196
- * @memberof DoboModel
197
- * @type {Object}
198
- * @property {DoboModel.TRecord} data - The data returned from the action
199
- */
200
-
201
- /**
202
- * The result returned from a model's `createRecord` action if `dataOnly` is set to `false`.
203
- * @typedef TResultCreateRecord
204
- * @memberof DoboModel
205
- * @type {Object}
206
- * @property {DoboModel.TRecord} data - The data returned from the action
207
- */
208
-
209
- /**
210
- * The result returned from a model's `updateRecord` action if `dataOnly` is set to `false`.
211
- * @typedef TResultUpdateRecord
212
- * @memberof DoboModel
213
- * @type {Object}
214
- * @property {DoboModel.TRecord} data - The updated data returned from the action
215
- * @property {DoboModel.TRecord} oldData - The previous data before the update
216
- */
217
-
218
- /**
219
- * The result returned from a model's `removeRecord` action if `dataOnly` is set to `false`.
220
- * @typedef TResultRemoveRecord
221
- * @memberof DoboModel
222
- * @type {Object}
223
- * @property {DoboModel.TRecord} oldData - The removed data returned from the action
224
- */
225
-
226
- /**
227
- * Model factory.
228
- *
229
- * @async
230
- * @returns {Promise<void>} Returns nothing
231
- */
232
- async function modelFactory () {
233
- const { Tools } = this.app.baseClass
234
- const { defaults } = this.app.lib._
235
-
236
- /**
237
- * Model class defintion.
238
- *
239
- * @class
240
- */
241
- class DoboModel extends Tools {
242
- /**
243
- * Constructor
244
- */
245
- constructor (plugin, options) {
246
- super(plugin)
247
-
248
- defaults(this, options)
249
-
250
- /**
251
- * Model's adapter name
252
- * @type {string}
253
- */
254
- this.adapter = this.connection.adapter
255
-
256
- this.bindThis(
257
- build,
258
- exists,
259
- drop,
260
-
261
- createRecord,
262
- getRecord,
263
- updateRecord,
264
- upsertRecord,
265
- removeRecord,
266
- clearRecord,
267
- findRecord,
268
- findOneRecord,
269
- findAllRecord,
270
-
271
- transaction,
272
- bulkCreateRecord,
273
-
274
- aggregate,
275
- histogram,
276
- countRecord,
277
-
278
- createAttachment,
279
- getAttachment,
280
- updateAttachment,
281
- removeAttachment,
282
- findAttachment,
283
- listAttachment,
284
-
285
- loadFixtures,
286
- sanitizeRecord,
287
- sanitizeBody,
288
- sanitizeFixture,
289
- validate
290
- )
291
-
292
- // aliases
293
- this.deleteRecord = this.removeRecord
294
- this.clearRecords = this.clearRecord
295
- this.countRecords = this.countRecord
296
- this.findRecords = this.findRecord
297
- this.findAllRecords = this.findAllRecord
298
- this.listAttachments = this.listAttachment
299
- this.bulkCreateRecords = this.bulkCreateRecord
300
-
301
- this.getField = this.getProperty
302
- this.hasField = this.hasProperty
303
- }
304
-
305
- /**
306
- * Invoke a model's action. It will create a new instance of {@link DoboAction} and return it.
307
- * @method
308
- * @memberof DoboModel
309
- * @param {string} name - The name of the action to invoke
310
- * @param {...any} args - Arguments to pass to the action
311
- * @returns {DoboAction} Returns an instance of {@link DoboAction}
312
- */
313
- action = (name, ...args) => {
314
- const action = new this.app.baseClass.DoboAction(this, name, ...args)
315
- return action
316
- }
317
-
318
- sanitizeObjectDef = async (obj) => {
319
- await sanitizeAll.call(this.app.dobo, obj)
320
- }
321
-
322
- /**
323
- * Sanitize a reference object based on the model's properties and rules.
324
- * @method
325
- * @memberof DoboModel
326
- * @async
327
- * @param {*} obj - The reference object to sanitize
328
- * @param {boolean} [fatal=true] - Whether to throw an error if sanitization fails
329
- */
330
- sanitizeRef = async (obj, fatal = true) => {
331
- await sanitizeRef.call(this.app.dobo, obj, this.app.dobo.models, fatal)
332
- }
333
-
334
- /**
335
- * Sanitize the given ID based on the model's properties.
336
- * @method
337
- * @memberof DoboModel
338
- * @param {string|number} id - The ID to sanitize
339
- * @returns {string|number} Returns the sanitized ID
340
- */
341
- sanitizeId = (id) => {
342
- const prop = this.properties.find(p => p.name === 'id')
343
- if (prop.type === 'integer') id = parseInt(id)
344
- return id
345
- }
346
-
347
- /**
348
- * Get a property by its name.
349
- * @method
350
- * @memberof DoboModel
351
- * @param {string} name - The name of the property to retrieve
352
- * @returns {Object|null} Returns the property object if found, otherwise `undefined`
353
- */
354
- getProperty = (name) => {
355
- return this.properties.find(prop => prop.name === name)
356
- }
357
-
358
- /**
359
- * Get the model's properties based on the given options.
360
- * @method
361
- * @memberof DoboModel
362
- * @param {Object} [options={}] - Options object
363
- * @param {boolean} [options.noVirtual=false] - If `true`, exclude virtual properties
364
- * @param {boolean} [options.namesOnly=false] - If `true`, return only property names instead of full property objects
365
- * @returns {Array<object|string>} Returns an array of properties or property names based on the options
366
- */
367
- getProperties = (options = {}) => {
368
- const { noVirtual, namesOnly } = options
369
- const items = noVirtual ? this.properties.filter(prop => !prop.virtual) : this.properties
370
- return namesOnly ? items.map(item => item.name) : items
371
- }
372
-
373
- /**
374
- * Get the model's virtual properties.
375
- * @method
376
- * @memberof DoboModel
377
- * @param {boolean} [namesOnly=false] - If `true`, return only property names instead of full property objects
378
- * @returns {Array<object|string>} Returns an array of virtual properties or property names based on the `namesOnly` option
379
- */
380
- getVirtualProperties = (namesOnly) => {
381
- const items = this.properties.filter(prop => prop.virtual)
382
- return namesOnly ? items.map(item => item.name) : items
383
- }
384
-
385
- /**
386
- * Get the model's non-virtual properties.
387
- * @method
388
- * @memberof DoboModel
389
- * @param {boolean} [namesOnly=false] - If `true`, return only property names instead of full property objects
390
- * @returns {Array<object|string>} Returns an array of non-virtual properties or property names based on the `namesOnly` option
391
- */
392
- getNonVirtualProperties = (namesOnly) => {
393
- const items = this.properties.filter(prop => !prop.virtual)
394
- return namesOnly ? items.map(item => item.name) : items
395
- }
396
-
397
- /**
398
- * Returns the model's indexes.
399
- * @method
400
- * @memberof DoboModel
401
- * @returns {Array<object>} Returns an array of index objects defined in the model
402
- */
403
- getIndexes = () => {
404
- return this.indexes
405
- }
406
-
407
- /**
408
- * Checks if the model has a property with the given name.
409
- * @method
410
- * @memberof DoboModel
411
- * @param {string} name - The name of the property to check
412
- * @returns {boolean} Returns `true` if the property exists, otherwise `false`
413
- */
414
- hasProperty = (name) => {
415
- return !!this.getProperty(name)
416
- }
417
-
418
- syncIdField = (idField) => {
419
- const { cloneDeep, findIndex } = this.app.lib._
420
- if (!this.adapter) return
421
- if (!idField) idField = cloneDeep(this.adapter.idField)
422
- const idx = findIndex(this.properties, { name: 'id' })
423
- if (idx === -1) return
424
- this.properties.splice(idx, 1)
425
- this.properties.unshift(idField)
426
- }
427
-
428
- /**
429
- * Internal method to perform a simple lookup based on the provided value and lookupValue.
430
- * @method
431
- * @internal
432
- * @memberof DoboModel
433
- * @async
434
- * @param {string|object|Array} value - The value to look up
435
- * @param {object} lookupValue - The lookup values to replace in the query
436
- * @param {object} [options={}] - Additional options for the lookup
437
- * @returns {*|null|undefined} Returns the result of the lookup or `null` if not found or `undefined` if the value is not a string, object, or array
438
- */
439
- _simpleLookup = async (value, lookupValue, options = {}) => {
440
- const { get, isEmpty, isString, isPlainObject, isArray } = this.app.lib._
441
- let model
442
- let field
443
- let query
444
- if (isString(value)) {
445
- [model, field, ...query] = value.split(':')
446
- query = query.join(':')
447
- } else if (isPlainObject(value)) ({ model, field, query } = value)
448
- else if (isArray(value)) [model, field, query] = value
449
- else return
450
- for (const key in lookupValue) {
451
- query = query.replaceAll(`{${key}}`, lookupValue[key])
452
- }
453
- if (query.includes('{') && query.includes('}')) return null
454
- if (isEmpty(field)) field = 'id'
455
- const { getModel } = this.app.dobo
456
- const ref = getModel(model)
457
- const opts = { ...options, noCache: true, noMagic: true }
458
- opts.dataOnly = true
459
- const rec = await ref.findOneRecord({ query }, opts)
460
- return get(rec, field, null)
461
- }
462
-
463
- /**
464
- * Builds the values for a given property if `prop.values` is set:
465
- * - If a string, it will be treated as a handler and executed to get the values.
466
- * - If a function, it will be executed to get the values.
467
- * - If an array, it will be used as is.
468
- *
469
- * Values are returned as an array of {@link DoboModel.TPropertyValues}.
470
- * If a request object is provided in the options, the `text` will be translated using
471
- * the request's translation function.
472
- * @async
473
- * @method
474
- * @memberof DoboModel
475
- * @param {object} prop - The property definition
476
- * @param {DoboModel.TOptions} opts - Additional options for building the property values
477
- * @returns {Array<DoboModel.TPropertyValue>} Returns an array of property values
478
- */
479
- buildPropValues = async (prop, opts) => {
480
- const { isString, camelCase, isFunction } = this.app.lib._
481
- const { callHandler } = this.app.bajo
482
- const values = isString(prop.values) || isFunction(prop.values) ? await callHandler(this, prop.values, opts) : [...prop.values]
483
- return values.map(v => {
484
- if (isString(v)) v = { value: v, text: v }
485
- if (opts.req) {
486
- const key = camelCase(`${prop.name} ${v.text}`)
487
- if (opts.req.te(key)) v.text = opts.req.t(key)
488
- }
489
- return v
490
- })
491
- }
492
-
493
- dispose = async () => {
494
- await super.dispose()
495
- this.connection = null
496
- this.adapter = null
497
- }
498
- }
499
-
500
- this.app.baseClass.DoboModel = DoboModel
501
- }
502
-
503
- export default modelFactory
1
+ import { sanitizeAll, sanitizeRef } from '../helper.js'
2
+ import clearRecord from './model/clear-record.js'
3
+ import countRecord from './model/count-record.js'
4
+ import createRecord from './model/create-record.js'
5
+ import drop from './model/drop.js'
6
+ import findAllRecord from './model/find-all-record.js'
7
+ import findOneRecord from './model/find-one-record.js'
8
+ import findRecord from './model/find-record.js'
9
+ import getRecord from './model/get-record.js'
10
+ import histogram from './model/histogram.js'
11
+ import aggregate from './model/aggregate.js'
12
+ import exists from './model/exists.js'
13
+ import build from './model/build.js'
14
+ import loadFixtures from './model/load-fixtures.js'
15
+ import removeRecord from './model/remove-record.js'
16
+ import updateRecord from './model/update-record.js'
17
+ import createAttachment from './model/create-attachment.js'
18
+ import updateAttachment from './model/update-attachment.js'
19
+ import getAttachment from './model/get-attachment.js'
20
+ import removeAttachment from './model/remove-attachment.js'
21
+ import findAttachment from './model/find-attachment.js'
22
+ import sanitizeBody from './model/sanitize-body.js'
23
+ import sanitizeRecord from './model/sanitize-record.js'
24
+ import sanitizeFixture from './model/sanitize-fixture.js'
25
+ import upsertRecord from './model/upsert-record.js'
26
+ import bulkCreateRecord from './model/bulk-create-record.js'
27
+ import listAttachment from './model/list-attachment.js'
28
+ import transaction from './model/transaction.js'
29
+ import validate from './model/validate.js'
30
+
31
+ /**
32
+ * @typedef {Object} TOptions
33
+ * @memberof DoboModel
34
+ * @type {Object}
35
+ * @property {boolean} [noHook=false] - If `true`, no model's hook will be executed
36
+ * @property {boolean} [noModelHook=false] - If `true`, no model's hook will be executed
37
+ * @property {boolean} [noDynHook=false] - If `true`, no dynamic hook will be executed
38
+ * @property {boolean} [noValidation=false] - If `true`, no validation of data payload performed
39
+ * @property {boolean} [noCheckUnique=false] - If `true`, no unique validation for ID performed
40
+ * @property {boolean} [noBodySanitizer=false] - If `true`, accept data payload as is without sanitization
41
+ * @property {boolean} [noRecordSanitizer=false] - If `true`, accept result payload as is without sanitization
42
+ * @property {boolean} [noResult=false] - If `true`, returns nothing
43
+ * @property {boolean} [truncateString=true] - If `true` (default), string is truncated to its model's `maxLength`
44
+ * @property {boolean} [partial] - If `true`, only updated values are saved. Otherwise replace all existing values with given payload. Defaults to `true` for `updateRecord`
45
+ * @property {boolean} [dataOnly=true] - If `true` (default) returns only record's object. Otherwise {@link DoboModel.TResult}
46
+ */
47
+
48
+ /**
49
+ * Sort order key used in pagination and sorting of records. It can be either:
50
+ * - `1` for ascending order (the default, if none is provided) or
51
+ * - `-1` for descending order.
52
+ * @typedef {string} TSortOrder
53
+ * @memberof DoboModel
54
+ */
55
+
56
+ /**
57
+ * Key value pairs used as sort order in pagination and sorting of records. It is an object where:
58
+ * - Key represents model's field name
59
+ * - Value represents its sort order
60
+ *
61
+ * @typedef {Object.<string, DoboModel.TSortOrder>} TSort
62
+ * @memberof DoboModel
63
+ * @example
64
+ * // to sort by firstName (ascending) and lastName (descending)
65
+ * const sort = {
66
+ * firstName: 1,
67
+ * lastName: -1
68
+ * }
69
+ */
70
+
71
+ /**
72
+ * @typedef {Object} TPagination
73
+ * @memberof DoboModel
74
+ * @property {number} limit - Number of records per page
75
+ * @property {number} page - Page number
76
+ * @property {number} [skip] - Records to skip. If not provided, it will be calculated based on `limit` and `page`
77
+ */
78
+
79
+ /**
80
+ * Property value type definition. Used for building property `values` in a model which is an array of this object type.
81
+ * @typedef TPropertyValue
82
+ * @memberof DoboModel
83
+ * @type {Object}
84
+ * @property {string} value - The actual value of the property
85
+ * @property {string} text - The display text for the property value, which can be translated based on the request context
86
+ */
87
+
88
+ /**
89
+ * Formatted version of a record's property values. It is an object where:
90
+ * - Key represents the property name
91
+ * - Value represents its formatted value according to the property's data type or formatting function. Data type is ALWAYS in string.
92
+ *
93
+ * This record is then wrapped in a `_fmt` property.
94
+ * @typedef TRecordFormatted
95
+ * @memberof DoboModel
96
+ * @type {Object}
97
+ * @property {Object} [_fmt={}] - Wrapper
98
+ * @example
99
+ * {
100
+ * _fmt: {
101
+ * id: "1",
102
+ * item: "Laptop",
103
+ * createdAt: "Jan 1, 2024, 12:00 AM",
104
+ * isActive: "True",
105
+ * price: "2,500.00"
106
+ * }
107
+ * }
108
+ */
109
+
110
+ /**
111
+ * Reference record related to a record's result. It is an object where:
112
+ * - Key represents the name of the referenced record. Normally, it is the referenced model's name in camelCase format.
113
+ * - Value represents its reference record. It can be an object or an array of objects, depending on the relationship type.
114
+ *
115
+ * This record is then wrapped in a `_ref` property in the result object.
116
+ * @typedef TRecordRef
117
+ * @memberof DoboModel
118
+ * @type {Object}
119
+ * @property {Object} [_ref={}] - Wrapper
120
+ * @property {Object|Array} [_ref.<string>.<DoboModel.TRecord>] - Reference record
121
+ * @example
122
+ * {
123
+ * _ref: {
124
+ * user: {
125
+ * id: 1,
126
+ * name: "John Doe"
127
+ * },
128
+ * posts: [
129
+ * {
130
+ * id: 1,
131
+ * title: "Post Title"
132
+ * },
133
+ * {
134
+ * id: 2,
135
+ * title: "Another Post Title"
136
+ * }
137
+ * ]
138
+ * }
139
+ * }
140
+ */
141
+
142
+ /**
143
+ * The record returned from a model's action. It is an object where:
144
+ * - Key represents the property name
145
+ * - Value represents its value according to the property's data type
146
+ *
147
+ * This record is then merged with {@link DoboModel.TRecordFormatted} and {@link DoboModel.TRecordRef} to form the final record returned from a model's action.
148
+ * @typedef TRecord
149
+ * @memberof DoboModel
150
+ * @type {Object & DoboModel.TRecordFormatted & DoboModel.TRecordRef}
151
+ * @example
152
+ * {
153
+ * id: 1,
154
+ * item: "Laptop",
155
+ * createdAt: "2024-01-01T00:00:00.000Z",
156
+ * isActive: true,
157
+ * price: 2500.00,
158
+ * _fmt: {
159
+ * id: "1",
160
+ * item: "Laptop",
161
+ * createdAt: "Jan 1, 2024, 12:00 AM",
162
+ * isActive: "True",
163
+ * price: "2,500.00"
164
+ * },
165
+ * _ref: {
166
+ * user: {
167
+ * id: 1,
168
+ * name: "John Doe"
169
+ * }
170
+ * }
171
+ * }
172
+ */
173
+
174
+ /**
175
+ * The result returned from a model's action other than `find*`, `{create|update|remove}Record` if `dataOnly` is set to `false`.
176
+ * @typedef TResultRecord
177
+ * @memberof DoboModel
178
+ * @type {Object}
179
+ * @property {Array<DoboModel.TRecord>} data - The data returned from the action
180
+ */
181
+
182
+ /**
183
+ * The result returned from a model's `find*` actions if `dataOnly` is set to `false`.
184
+ * @typedef TResultFindRecord
185
+ * @memberof DoboModel
186
+ * @type {Object}
187
+ * @property {Array<DoboModel.TRecord>} data - The data returned from the action
188
+ * @property {number} [count] - The total number of records found, if applicable
189
+ * @property {boolean} [cached] - Indicates if the result was retrieved from cache, if applicable. Only available if `bajoCache` is used
190
+ * @property {Array} [warnings] - Any warnings generated during the action, if applicable
191
+ */
192
+
193
+ /**
194
+ * The result returned from a model's `getRecord` action if `dataOnly` is set to `false`.
195
+ * @typedef TResultGetRecord
196
+ * @memberof DoboModel
197
+ * @type {Object}
198
+ * @property {DoboModel.TRecord} data - The data returned from the action
199
+ */
200
+
201
+ /**
202
+ * The result returned from a model's `createRecord` action if `dataOnly` is set to `false`.
203
+ * @typedef TResultCreateRecord
204
+ * @memberof DoboModel
205
+ * @type {Object}
206
+ * @property {DoboModel.TRecord} data - The data returned from the action
207
+ */
208
+
209
+ /**
210
+ * The result returned from a model's `updateRecord` action if `dataOnly` is set to `false`.
211
+ * @typedef TResultUpdateRecord
212
+ * @memberof DoboModel
213
+ * @type {Object}
214
+ * @property {DoboModel.TRecord} data - The updated data returned from the action
215
+ * @property {DoboModel.TRecord} oldData - The previous data before the update
216
+ */
217
+
218
+ /**
219
+ * The result returned from a model's `removeRecord` action if `dataOnly` is set to `false`.
220
+ * @typedef TResultRemoveRecord
221
+ * @memberof DoboModel
222
+ * @type {Object}
223
+ * @property {DoboModel.TRecord} oldData - The removed data returned from the action
224
+ */
225
+
226
+ /**
227
+ * Model factory.
228
+ *
229
+ * @async
230
+ * @returns {Promise<void>} Returns nothing
231
+ */
232
+ async function modelFactory () {
233
+ const { Tools } = this.app.baseClass
234
+ const { defaults } = this.app.lib._
235
+
236
+ /**
237
+ * Model class defintion.
238
+ *
239
+ * @class
240
+ */
241
+ class DoboModel extends Tools {
242
+ /**
243
+ * Constructor
244
+ */
245
+ constructor (plugin, options) {
246
+ super(plugin)
247
+
248
+ defaults(this, options)
249
+
250
+ /**
251
+ * Model's adapter name
252
+ * @type {string}
253
+ */
254
+ this.adapter = this.connection.adapter
255
+
256
+ this.bindThis(
257
+ build,
258
+ exists,
259
+ drop,
260
+
261
+ createRecord,
262
+ getRecord,
263
+ updateRecord,
264
+ upsertRecord,
265
+ removeRecord,
266
+ clearRecord,
267
+ findRecord,
268
+ findOneRecord,
269
+ findAllRecord,
270
+
271
+ transaction,
272
+ bulkCreateRecord,
273
+
274
+ aggregate,
275
+ histogram,
276
+ countRecord,
277
+
278
+ createAttachment,
279
+ getAttachment,
280
+ updateAttachment,
281
+ removeAttachment,
282
+ findAttachment,
283
+ listAttachment,
284
+
285
+ loadFixtures,
286
+ sanitizeRecord,
287
+ sanitizeBody,
288
+ sanitizeFixture,
289
+ validate
290
+ )
291
+
292
+ // aliases
293
+ this.deleteRecord = this.removeRecord
294
+ this.clearRecords = this.clearRecord
295
+ this.countRecords = this.countRecord
296
+ this.findRecords = this.findRecord
297
+ this.findAllRecords = this.findAllRecord
298
+ this.listAttachments = this.listAttachment
299
+ this.bulkCreateRecords = this.bulkCreateRecord
300
+
301
+ this.getField = this.getProperty
302
+ this.hasField = this.hasProperty
303
+ }
304
+
305
+ /**
306
+ * Invoke a model's action. It will create a new instance of {@link DoboAction} and return it.
307
+ * @method
308
+ * @memberof DoboModel
309
+ * @param {string} name - The name of the action to invoke
310
+ * @param {...any} args - Arguments to pass to the action
311
+ * @returns {DoboAction} Returns an instance of {@link DoboAction}
312
+ */
313
+ action = (name, ...args) => {
314
+ const action = new this.app.baseClass.DoboAction(this, name, ...args)
315
+ return action
316
+ }
317
+
318
+ sanitizeObjectDef = async (obj) => {
319
+ await sanitizeAll.call(this.app.dobo, obj)
320
+ }
321
+
322
+ /**
323
+ * Sanitize a reference object based on the model's properties and rules.
324
+ * @method
325
+ * @memberof DoboModel
326
+ * @async
327
+ * @param {*} obj - The reference object to sanitize
328
+ * @param {boolean} [fatal=true] - Whether to throw an error if sanitization fails
329
+ */
330
+ sanitizeRef = async (obj, fatal = true) => {
331
+ await sanitizeRef.call(this.app.dobo, obj, this.app.dobo.models, fatal)
332
+ }
333
+
334
+ /**
335
+ * Sanitize the given ID based on the model's properties.
336
+ * @method
337
+ * @memberof DoboModel
338
+ * @param {string|number} id - The ID to sanitize
339
+ * @returns {string|number} Returns the sanitized ID
340
+ */
341
+ sanitizeId = (id) => {
342
+ const prop = this.properties.find(p => p.name === 'id')
343
+ if (prop.type === 'integer') id = parseInt(id)
344
+ return id
345
+ }
346
+
347
+ /**
348
+ * Get a property by its name.
349
+ * @method
350
+ * @memberof DoboModel
351
+ * @param {string} name - The name of the property to retrieve
352
+ * @returns {Object|null} Returns the property object if found, otherwise `undefined`
353
+ */
354
+ getProperty = (name) => {
355
+ return this.properties.find(prop => prop.name === name)
356
+ }
357
+
358
+ /**
359
+ * Get the model's properties based on the given options.
360
+ * @method
361
+ * @memberof DoboModel
362
+ * @param {Object} [options={}] - Options object
363
+ * @param {boolean} [options.noVirtual=false] - If `true`, exclude virtual properties
364
+ * @param {boolean} [options.namesOnly=false] - If `true`, return only property names instead of full property objects
365
+ * @returns {Array<object|string>} Returns an array of properties or property names based on the options
366
+ */
367
+ getProperties = (options = {}) => {
368
+ const { noVirtual, namesOnly } = options
369
+ const items = noVirtual ? this.properties.filter(prop => !prop.virtual) : this.properties
370
+ return namesOnly ? items.map(item => item.name) : items
371
+ }
372
+
373
+ /**
374
+ * Get the model's virtual properties.
375
+ * @method
376
+ * @memberof DoboModel
377
+ * @param {boolean} [namesOnly=false] - If `true`, return only property names instead of full property objects
378
+ * @returns {Array<object|string>} Returns an array of virtual properties or property names based on the `namesOnly` option
379
+ */
380
+ getVirtualProperties = (namesOnly) => {
381
+ const items = this.properties.filter(prop => prop.virtual)
382
+ return namesOnly ? items.map(item => item.name) : items
383
+ }
384
+
385
+ /**
386
+ * Get the model's non-virtual properties.
387
+ * @method
388
+ * @memberof DoboModel
389
+ * @param {boolean} [namesOnly=false] - If `true`, return only property names instead of full property objects
390
+ * @returns {Array<object|string>} Returns an array of non-virtual properties or property names based on the `namesOnly` option
391
+ */
392
+ getNonVirtualProperties = (namesOnly) => {
393
+ const items = this.properties.filter(prop => !prop.virtual)
394
+ return namesOnly ? items.map(item => item.name) : items
395
+ }
396
+
397
+ /**
398
+ * Returns the model's indexes.
399
+ * @method
400
+ * @memberof DoboModel
401
+ * @returns {Array<object>} Returns an array of index objects defined in the model
402
+ */
403
+ getIndexes = () => {
404
+ return this.indexes
405
+ }
406
+
407
+ /**
408
+ * Checks if the model has a property with the given name.
409
+ * @method
410
+ * @memberof DoboModel
411
+ * @param {string} name - The name of the property to check
412
+ * @returns {boolean} Returns `true` if the property exists, otherwise `false`
413
+ */
414
+ hasProperty = (name) => {
415
+ return !!this.getProperty(name)
416
+ }
417
+
418
+ syncIdField = (idField) => {
419
+ const { cloneDeep, findIndex } = this.app.lib._
420
+ if (!this.adapter) return
421
+ if (!idField) idField = cloneDeep(this.adapter.idField)
422
+ const idx = findIndex(this.properties, { name: 'id' })
423
+ if (idx === -1) return
424
+ this.properties.splice(idx, 1)
425
+ this.properties.unshift(idField)
426
+ }
427
+
428
+ /**
429
+ * Internal method to perform a simple lookup based on the provided value and lookupValue.
430
+ * @method
431
+ * @internal
432
+ * @memberof DoboModel
433
+ * @async
434
+ * @param {string|object|Array} value - The value to look up
435
+ * @param {object} lookupValue - The lookup values to replace in the query
436
+ * @param {object} [options={}] - Additional options for the lookup
437
+ * @returns {*|null|undefined} Returns the result of the lookup or `null` if not found or `undefined` if the value is not a string, object, or array
438
+ */
439
+ _simpleLookup = async (value, lookupValue, options = {}) => {
440
+ const { get, isEmpty, isString, isPlainObject, isArray } = this.app.lib._
441
+ let model
442
+ let field
443
+ let query
444
+ if (isString(value)) {
445
+ [model, field, ...query] = value.split(':')
446
+ query = query.join(':')
447
+ } else if (isPlainObject(value)) ({ model, field, query } = value)
448
+ else if (isArray(value)) [model, field, query] = value
449
+ else return
450
+ for (const key in lookupValue) {
451
+ query = query.replaceAll(`{${key}}`, lookupValue[key])
452
+ }
453
+ if (query.includes('{') && query.includes('}')) return null
454
+ if (isEmpty(field)) field = 'id'
455
+ const { getModel } = this.app.dobo
456
+ const ref = getModel(model)
457
+ const opts = { ...options, noCache: true, noMagic: true }
458
+ opts.dataOnly = true
459
+ const rec = await ref.findOneRecord({ query }, opts)
460
+ return get(rec, field, null)
461
+ }
462
+
463
+ /**
464
+ * Builds the values for a given property if `prop.values` is set:
465
+ * - If a string, it will be treated as a handler and executed to get the values.
466
+ * - If a function, it will be executed to get the values.
467
+ * - If an array, it will be used as is.
468
+ *
469
+ * Values are returned as an array of {@link DoboModel.TPropertyValues}.
470
+ * If a request object is provided in the options, the `text` will be translated using
471
+ * the request's translation function.
472
+ * @async
473
+ * @method
474
+ * @memberof DoboModel
475
+ * @param {object} prop - The property definition
476
+ * @param {DoboModel.TOptions} opts - Additional options for building the property values
477
+ * @returns {Array<DoboModel.TPropertyValue>} Returns an array of property values
478
+ */
479
+ buildPropValues = async (prop, opts) => {
480
+ const { isString, camelCase, isFunction } = this.app.lib._
481
+ const { callHandler } = this.app.bajo
482
+ const values = isString(prop.values) || isFunction(prop.values) ? await callHandler(this, prop.values, opts) : [...prop.values]
483
+ return values.map(v => {
484
+ if (isString(v)) v = { value: v, text: v }
485
+ if (opts.req) {
486
+ const key = camelCase(`${prop.name} ${v.text}`)
487
+ if (opts.req.te(key)) v.text = opts.req.t(key)
488
+ }
489
+ return v
490
+ })
491
+ }
492
+
493
+ dispose = async () => {
494
+ await super.dispose()
495
+ this.connection = null
496
+ this.adapter = null
497
+ }
498
+ }
499
+
500
+ this.app.baseClass.DoboModel = DoboModel
501
+ }
502
+
503
+ export default modelFactory