dobo 2.30.7 → 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 +17 -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 -574
  122. package/lib/factory/model/sanitize-id.js +0 -7
  123. package/lib/index.js +0 -3
@@ -1,574 +0,0 @@
1
- import { ulid } from 'ulid'
2
- import { v4 as uuidv4, v7 as uuidv7 } from 'uuid'
3
- import crypto from 'crypto'
4
-
5
- const defIdField = {
6
- name: '_id',
7
- type: 'string',
8
- maxLength: 50,
9
- required: true,
10
- index: 'primary'
11
- }
12
-
13
- async function driverFactory () {
14
- const { Tools } = this.app.baseClass
15
- const { pick, cloneDeep, has, uniq, without, isEmpty, omit, isFunction, camelCase, last } = this.app.lib._
16
- const { isSet } = this.app.lib.aneka
17
- const { runHook } = this.app.bajo
18
-
19
- /**
20
- * Driver class
21
- *
22
- * ```this.plugin``` should be the one who owned this driver
23
- *
24
- * @class
25
- */
26
- class DoboDriver extends Tools {
27
- constructor (plugin, name, options = {}) {
28
- super(plugin)
29
- this.name = name
30
- this.idField = cloneDeep(defIdField)
31
- this.propertyType = {}
32
- this.support = {
33
- propType: {
34
- object: false,
35
- array: false,
36
- datetime: true
37
- },
38
- search: false,
39
- uniqueIndex: false,
40
- nullableField: true,
41
- transaction: false
42
- }
43
- this.useUtc = false
44
- this.maxChunkSize = 500
45
- this.memory = false
46
- this.options = options
47
- }
48
-
49
- /**
50
- * Sanitize connection object
51
- *
52
- * @method
53
- * @param {Object} conn - Connection object
54
- */
55
- async sanitizeConnection (conn) {
56
- conn.proto = conn.proto ?? 'http' // used by driver that use url based connection
57
- conn.memory = false
58
- }
59
-
60
- sanitizeBody (model, body = {}, partial) {
61
- const { keys, pick } = this.app.lib._
62
- const item = cloneDeep(body)
63
- let newId = false
64
- if (has(item, 'id') && this.idField.name !== 'id') {
65
- item[this.idField.name] = item.id
66
- newId = true
67
- }
68
- for (const prop of model.getNonVirtualProperties()) {
69
- if (item[prop.name] === 'null') item[prop.name] = null
70
- if (!isSet(item[prop.name]) && !this.support.nullableField) {
71
- switch (prop.type) {
72
- case 'datetime': item[prop.name] = new Date(0); break
73
- case 'float':
74
- case 'double': item[prop.name] = 0; break
75
- case 'string':
76
- case 'text': item[prop.name] = ''; break
77
- case 'object': item[prop.name] = {}; break
78
- case 'array': item[prop.name] = []; break
79
- }
80
- }
81
- if (isSet(item[prop.name]) && !this.support.propType[prop.type]) {
82
- if (prop.type === 'datetime') item[prop.name] = item[prop.name].toISOString()
83
- else if (['object', 'array'].includes(prop.type)) item[prop.name] = JSON.stringify(item[prop.name])
84
- }
85
- }
86
- const result = partial ? pick(item, keys(body)) : item
87
- if (newId) delete result.id
88
- return result
89
- }
90
-
91
- sanitizeRecord (model, record = {}) {
92
- const { dayjs } = this.app.lib
93
- const { isString } = this.app.lib._
94
- const item = { ...record }
95
- if (has(item, this.idField.name) && this.idField.name !== 'id') {
96
- item.id = item[this.idField.name]
97
- delete item[this.idField.name]
98
- }
99
- for (const prop of model.properties) {
100
- if (isSet(item[prop.name])) {
101
- if (!this.support.propType[prop.type]) {
102
- try {
103
- if (prop.type === 'datetime') {
104
- const dt = this.useUtc ? dayjs.utc(item[prop.name]) : dayjs(item[prop.name])
105
- item[prop.name] = dt.toDate()
106
- } else if (['object', 'array'].includes(prop.type)) item[prop.name] = JSON.parse(item[prop.name])
107
- } catch (err) {
108
- item[prop.name] = null
109
- }
110
- }
111
- if (prop.type === 'datetime' && isString(item[prop.name])) {
112
- const dt = this.useUtc ? dayjs.utc(item[prop.name]) : dayjs(item[prop.name])
113
- item[prop.name] = dt.toDate()
114
- }
115
- if (prop.type === 'boolean' && isSet(item[prop.name])) item[prop.name] = Boolean(item[prop.name])
116
- }
117
- }
118
- return item
119
- }
120
-
121
- _getReturningFields (model, { fields = [] } = {}) {
122
- if (!this.support.returning) return []
123
- let items = fields.length > 0 ? [...fields] : model.properties.map(prop => prop.name)
124
- if (!items.includes(this.idField.name)) items.unshift(this.idField.name)
125
- if (this.idField.name !== 'id') items = without(items, ['id'])
126
- return uniq(items)
127
- }
128
-
129
- async _attachHook (name, model, ...args) {
130
- const { ns } = this.app.dobo
131
- const options = last(args)
132
- if (!options.noDriverHook) {
133
- await runHook(`${ns}.driver:${name}`, model, ...args)
134
- await runHook(`${ns}.driver.${camelCase(model.name)}:${name}`, model, ...args)
135
- }
136
- }
137
-
138
- /**
139
- * Check uniqueness of fields with unique index
140
- *
141
- * @param {Object} [params]
142
- */
143
- _checkUnique = async (model, body = {}, options = {}) => {
144
- const { isSet } = this.app.lib.aneka
145
- const { filter, map, isEmpty, forOwn } = this.app.lib._
146
- const indexes = filter(model.indexes ?? [], idx => idx.type === 'unique')
147
- for (const index of indexes) {
148
- const query = {}
149
- for (const field of index.fields) {
150
- if (isSet(body[field])) query[field] = body[field]
151
- }
152
- if (isEmpty(query)) continue
153
- const { data } = await model.findOneRecord({ query }, options)
154
- if (!isEmpty(data)) {
155
- if (['updateRecord', 'upsertRecord'].includes(options.action)) {
156
- let eq = true
157
- forOwn(query, (v, k) => {
158
- if (data[k] !== v) eq = false
159
- })
160
- if (!eq) continue
161
- }
162
- const error = this.app.dobo.t('uniqueConstraintError')
163
- const details = map(index.fields, field => {
164
- return { field, error }
165
- })
166
- throw this.app.dobo.error(error, { details, body })
167
- }
168
- }
169
- }
170
-
171
- // Internal calls that will be called by model
172
-
173
- async _modelExists (model, options = {}) {
174
- return await this.modelExists(model, options)
175
- }
176
-
177
- async _buildModel (model, options = {}) {
178
- return await this.buildModel(model, options)
179
- }
180
-
181
- async _dropModel (model, options = {}) {
182
- return await this.dropModel(model, options)
183
- }
184
-
185
- getRealFields (model) {
186
- return model.getProperties({ noVirtual: true, namesOnly: true })
187
- }
188
-
189
- getVirtualFields (model) {
190
- return model.getVirtualProperties({ namesOnly: true })
191
- }
192
-
193
- async _prepBodyForCreate (model, body = {}, options = {}) {
194
- const { callHandler } = this.app.bajo
195
- const { isSet, generateId } = this.app.lib.aneka
196
- for (const prop of model.getProperties({ noVirtual: true })) {
197
- if (isSet(prop.default) && (!options.noDefault) && (!isSet(body[prop.name]) || body[prop.name] === prop.default)) {
198
- if (isFunction(prop.default)) body[prop.name] = await prop.default.call(model)
199
- else if (typeof prop.default !== 'string') body[prop.name] = prop.default
200
- else {
201
- if (['now'].includes(prop.default) && prop.type === 'datetime') {
202
- body[prop.name] = new Date()
203
- } else if (['uuid', 'uuidv4'].includes(prop.default) && prop.type === 'string') {
204
- body[prop.name] = uuidv4().slice(0, prop.maxLength)
205
- } else if (prop.default === 'uuidv7' && prop.type === 'string') {
206
- body[prop.name] = uuidv7().slice(0, prop.maxLength)
207
- } else if (prop.default === 'ulid' && prop.type === 'string') {
208
- body[prop.name] = ulid().slice(0, prop.maxLength)
209
- } else if (prop.default === 'generateid' && prop.type === 'string') {
210
- body[prop.name] = generateId()
211
- } else if (prop.default.startsWith('handler:')) {
212
- const [, ...args] = prop.default.split(':')
213
- if (args.length > 0) body[prop.name] = await callHandler(args.join(':'))
214
- } else if (prop.default.startsWith('md5:') && prop.type === 'string') {
215
- const [, field] = prop.default.split(':')
216
- const fields = field.split(',')
217
- if (model.properties.filter(item => fields.includes(item.name)).length === fields.length) {
218
- const values = fields.map(f => body[f])
219
- body[prop.name] = crypto.createHash('md5').update(values.join(':')).digest('hex')
220
- }
221
- } else {
222
- body[prop.name] = prop.default
223
- }
224
- }
225
- }
226
- }
227
- return pick(body, this.getRealFields(model))
228
- }
229
-
230
- async _prepIdForCreate (model, body = {}, options = {}) {
231
- const { isSet, generateId } = this.app.lib.aneka
232
- const { isFunction } = this.app.lib._
233
- const prop = model.properties.find(p => p.name === 'id')
234
- if (!isSet(body.id) && prop.type === 'string') {
235
- if (this.idGenerator) {
236
- if (['uuid', 'uuidv4'].includes(this.idGenerator)) body.id = uuidv4()
237
- else if (['uuidv7'].includes(this.idGenerator)) body.id = uuidv7()
238
- else if (this.idGenerator === 'generateId') body.id = generateId()
239
- else if (isFunction(this.idGenerator)) body.id = await this.idGenerator(model, body, options)
240
- }
241
- if (!body.id) body.id = ulid()
242
- body.id = body.id.slice(0, prop.maxLength)
243
- }
244
- }
245
-
246
- _injectMeta (result = {}, options = {}) {
247
- result.warnings = result.warnings ?? []
248
- result.warnings.push(...(options.warnings ?? []))
249
- }
250
-
251
- async _createRecord (model, input = {}, options = {}) {
252
- const { isSet } = this.app.lib.aneka
253
- let body = await this._prepBodyForCreate(model, input, options)
254
- await this._prepIdForCreate(model, body, options)
255
- if (!options.noUniqueCheck) {
256
- if (!this.support.uniqueIndex) await this._checkUnique(model, body, options)
257
- }
258
- if (!options.noIdCheck && isSet(body.id)) {
259
- const resp = await this.getRecord(model, body.id, { noMagic: true })
260
- if (!isEmpty(resp.data)) throw this.plugin.error('recordExists%s%s', body.id, model.name)
261
- }
262
- body = this.sanitizeBody(model, body)
263
-
264
- await this._attachHook('beforeCreateRecord', model, body, options)
265
- const result = await this.createRecord(model, body, options)
266
- await this._attachHook('afterCreateRecord', model, body, result, options)
267
-
268
- if (options.noResult) return
269
- result.data = this.sanitizeRecord(model, result.data)
270
- this._injectMeta(result, options)
271
- return result
272
- }
273
-
274
- async _bulkCreateRecords (model, bodies = [], options = {}) {
275
- const { chunk } = this.app.lib._
276
- let { chunkSize = this.maxChunkSize } = options
277
- if (chunkSize > this.maxChunkSize) chunkSize = this.maxChunkSize
278
- for (const idx in bodies) {
279
- const body = await this._prepBodyForCreate(model, bodies[idx], options)
280
- await this._prepIdForCreate(model, body, options)
281
- bodies[idx] = this.sanitizeBody(model, body)
282
- }
283
-
284
- await this._attachHook('beforeBulkCreateRecord', model, bodies, options)
285
- const items = chunk(bodies, chunkSize)
286
- for (const item of items) {
287
- await this.bulkCreateRecord(model, item, options)
288
- }
289
- await this._attachHook('afterBulkCreateRecord', model, bodies, [], options)
290
- }
291
-
292
- async _getRecord (model, id, options = {}) {
293
- await this._attachHook('beforeGetRecord', model, id, options)
294
- const result = await this.getRecord(model, id, options)
295
- await this._attachHook('afterGetRecord', model, id, result, options)
296
-
297
- if (isEmpty(result.data) && options.throwNotFound) throw this.plugin.error('recordNotFound%s%s', id, model.name)
298
- result.data = this.sanitizeRecord(model, result.data)
299
- this._injectMeta(result, options)
300
- return result
301
- }
302
-
303
- async _updateRecord (model, id, input = {}, options = {}) {
304
- let body = omit(input, this.getVirtualFields(model))
305
- if (!options.noUniqueCheck) {
306
- if (!this.support.uniqueIndex) await this._checkUnique(model, body, options)
307
- }
308
- if (!options._data) {
309
- const resp = await this.getRecord(model, id, { noMagic: true })
310
- if (!resp.data) throw this.plugin.error('recordNotFound%s%s', id, model.name)
311
- options._data = resp.data
312
- }
313
- body = this.sanitizeBody(model, body, true)
314
- delete body.id
315
-
316
- await this._attachHook('beforeUpdateRecord', model, id, body, options)
317
- const result = await this.updateRecord(model, id, body, options)
318
- await this._attachHook('afterUpdateRecord', model, id, body, result, options)
319
-
320
- if (options.noResult) return
321
- result.oldData = this.sanitizeRecord(model, result.oldData)
322
- result.data = this.sanitizeRecord(model, result.data)
323
- this._injectMeta(result, options)
324
- return result
325
- }
326
-
327
- async _upsertRecord (model, input = {}, options = {}) {
328
- let body = omit(input, this.getVirtualFields(model))
329
- if (!options.noUniqueCheck) {
330
- if (!this.uniqueIndexSupport) await this._checkUnique(model, body, options)
331
- }
332
- if (isSet(body.id)) {
333
- if (!options._data) {
334
- const resp = await this.getRecord(model, body.id, { noMagic: true })
335
- if (!resp.data) throw this.plugin.error('recordNotFound%s%s', body.id, model.name)
336
- options._data = resp.data
337
- }
338
- }
339
- body = this.sanitizeBody(model, body)
340
-
341
- await this._attachHook('beforeUpsertRecord', model, body, options)
342
- const result = await this.upsertRecord(model, body, options)
343
- await this._attachHook('afterUpsertRecord', model, body, result, options)
344
-
345
- if (options.noResult) return
346
- if (result.oldData) result.oldData = this.sanitizeRecord(model, result.oldData)
347
- result.data = this.sanitizeRecord(model, result.data)
348
- this._injectMeta(result, options)
349
- return result
350
- }
351
-
352
- async _removeRecord (model, id, options = {}) {
353
- if (!options._data) {
354
- const resp = await this.getRecord(model, id, { noMagic: true })
355
- if (!resp.data) throw this.plugin.error('recordNotFound%s%s', id, model.name)
356
- options._data = resp.data
357
- }
358
-
359
- await this._attachHook('beforeRemoveRecord', model, id, options)
360
- const result = await this.removeRecord(model, id, options)
361
- await this._attachHook('afterRemoveRecord', model, id, result, options)
362
-
363
- if (options.noResult) return
364
- result.oldData = this.sanitizeRecord(model, result.oldData)
365
- this._injectMeta(result, options)
366
- return result
367
- }
368
-
369
- async _clearRecord (model, options = {}) {
370
- await this._attachHook('beforeClearRecord', model, options)
371
- const result = await this.clearRecord(model, options)
372
- await this._attachHook('afterClearRecord', model, result, options)
373
-
374
- this._injectMeta(result, options)
375
- return result
376
- }
377
-
378
- async _findRecord (model, filter = {}, options = {}) {
379
- let result
380
- try {
381
- await this._attachHook('beforeFindRecord', model, filter, options)
382
- result = await this.findRecord(model, filter, options)
383
- await this._attachHook('afterFindRecord', model, filter, result, options)
384
- } catch (err) {
385
- if (!['_emptyColumnQuery', '_abortAction'].includes(err.message)) throw err
386
- result = {
387
- data: [],
388
- count: 0
389
- // warnings: [] // TODO: should generate warnings?
390
- }
391
- }
392
-
393
- for (const idx in result.data) {
394
- result.data[idx] = this.sanitizeRecord(model, result.data[idx])
395
- }
396
- this._injectMeta(result, options)
397
- return result
398
- }
399
-
400
- async _findAllRecord (model, filter = {}, options = {}) {
401
- let result
402
- try {
403
- await this._attachHook('beforeFindAllRecord', model, filter, options)
404
- result = await this.findAllRecord(model, filter, options)
405
- await this._attachHook('afterFindAllRecord', model, filter, result, options)
406
- } catch (err) {
407
- if (err.message !== '_emptyColumnQuery') throw err
408
- result = {
409
- data: [],
410
- count: 0
411
- // warnings: [] // TODO: should generate warnings?
412
- }
413
- }
414
-
415
- for (const idx in result.data) {
416
- result.data[idx] = this.sanitizeRecord(model, result.data[idx])
417
- }
418
- this._injectMeta(result, options)
419
- return result
420
- }
421
-
422
- async _countRecord (model, filter = {}, options = {}) {
423
- let result
424
- try {
425
- await this._attachHook('beforeCountRecord', model, filter, options)
426
- result = await this.countRecord(model, filter, options)
427
- await this._attachHook('afterCountRecord', model, filter, result, options)
428
- } catch (err) {
429
- if (err.message !== '_emptyColumnQuery') throw err
430
- result = { data: 0 }
431
- }
432
-
433
- return result
434
- }
435
-
436
- async _createAggregate (model, filter = {}, params = {}, options = {}) {
437
- const fieldPropTypes = ['integer', 'smallint', 'float', 'double']
438
- const groupPropTypes = ['string', ...fieldPropTypes]
439
- this.app.dobo.checkAggregateParams(params)
440
- const { group, field } = params
441
-
442
- let prop = model.properties.find(p => p.name === group)
443
- if (!prop) throw this.plugin.error('unknown%s%s', this.plugin.t('field.field'), group)
444
- if (!groupPropTypes.includes(prop.type)) throw this.plugin.error('allowedPropType%s%s', group, groupPropTypes.join(', '))
445
-
446
- prop = model.properties.find(p => p.name === field)
447
- if (!prop) throw this.plugin.error('unknown%s%s', this.plugin.t('field.field'), field)
448
- // if (!fieldPropTypes.includes(prop.type)) throw this.plugin.error('allowedPropType%s%s', field, fieldPropTypes.join(', '))
449
-
450
- let result
451
- try {
452
- await this._attachHook('beforeCreateAggregate', model, filter, params, options)
453
- result = await this.createAggregate(model, filter, params, options)
454
- await this._attachHook('afterCreateAggregate', model, filter, params, result, options)
455
- } catch (err) {
456
- if (err.message !== '_emptyColumnQuery') throw err
457
- result = { data: [] }
458
- }
459
-
460
- for (const idx in result.data) {
461
- result.data[idx] = this.sanitizeRecord(model, result.data[idx])
462
- }
463
- this._injectMeta(result, options)
464
- return result
465
- }
466
-
467
- async _createHistogram (model, filter = {}, params, options = {}) {
468
- // const fieldPropTypes = ['integer', 'smallint', 'float', 'double']
469
- const groupPropTypes = ['datetime', 'date']
470
- this.app.dobo.checkHistogramParams(params)
471
- const { group, field } = params
472
-
473
- let prop = model.properties.find(p => p.name === group)
474
- if (!prop) throw this.plugin.error('unknown%s%s', this.plugin.t('field.field'), group)
475
- if (!groupPropTypes.includes(prop.type)) throw this.plugin.error('allowedPropType%s%s', group, groupPropTypes.join(', '))
476
-
477
- prop = model.properties.find(p => p.name === field)
478
- if (!prop) throw this.plugin.error('unknown%s%s', this.plugin.t('field.field'), field)
479
-
480
- let result
481
- try {
482
- await this._attachHook('beforeCreateHistogram', model, filter, params, options)
483
- result = await this.createHistogram(model, filter, params, options)
484
- await this._attachHook('afterCreateHistogram', model, filter, params, result, options)
485
- } catch (err) {
486
- if (err.message !== '_emptyColumnQuery') throw err
487
- result = { data: [] }
488
- }
489
-
490
- for (const idx in result.data) {
491
- result.data[idx] = this.sanitizeRecord(model, result.data[idx])
492
- }
493
- this._injectMeta(result, options)
494
- return result
495
- }
496
-
497
- // Public calls that need to be implemented by child drivers
498
-
499
- async connect (connection, noRebuild) {
500
- }
501
-
502
- async modelExists (model, options = {}) {
503
- throw this.plugin.error('notSupportedDriver%s%s%s', this.app.t('method'), 'modelExists', this.name)
504
- }
505
-
506
- async buildModel (model, options = {}) {
507
- throw this.plugin.error('notSupportedDriver%s%s%s', this.app.t('method'), 'buildModel', this.name)
508
- }
509
-
510
- async dropModel (model, options = {}) {
511
- throw this.plugin.error('notSupportedDriver%s%s%s', this.app.t('method'), 'dropModel', this.name)
512
- }
513
-
514
- async createRecord (model, body = {}, options = {}) {
515
- throw this.plugin.error('notSupportedDriver%s%s%s', this.app.t('method'), 'createRecord', this.name)
516
- }
517
-
518
- async getRecord (model, id, options = {}) {
519
- throw this.plugin.error('notSupportedDriver%s%s%s', this.app.t('method'), 'getRecord', this.name)
520
- }
521
-
522
- async updateRecord (model, id, body = {}, options = {}) {
523
- throw this.plugin.error('notSupportedDriver%s%s%s', this.app.t('method'), 'updateRecord', this.name)
524
- }
525
-
526
- async removeRecord (model, id, options = {}) {
527
- throw this.plugin.error('notSupportedDriver%s%s%s', this.app.t('method'), 'removeRecord', this.name)
528
- }
529
-
530
- async clearRecord (model, options = {}) {
531
- throw this.plugin.error('notSupportedDriver%s%s%s', this.app.t('method'), 'clearRecord', this.name)
532
- }
533
-
534
- async findRecord (model, filter = {}, options = {}) {
535
- throw this.plugin.error('notSupportedDriver%s%s%s', this.app.t('method'), 'findRecord', this.name)
536
- }
537
-
538
- async bulkCreateRecord (model, bodies = [], options = {}) {
539
- throw this.plugin.error('notSupportedDriver%s%s%s', this.app.t('method'), 'bulkCreateRecord', this.name)
540
- }
541
-
542
- async countRecord (model, filter = {}, options = {}) {
543
- throw this.plugin.error('notSupportedDriver%s%s%s', this.app.t('method'), 'countRecord', this.name)
544
- }
545
-
546
- async createAggregate (model, filter = {}, params = {}, options = {}) {
547
- throw this.plugin.error('notSupportedDriver%s%s%s', this.app.t('method'), 'createAggregate', this.name)
548
- }
549
-
550
- async createHistogram (model, filter = {}, params = {}, options = {}) {
551
- throw this.plugin.error('notSupportedDriver%s%s%s', this.app.t('method'), 'createHistogram', this.name)
552
- }
553
-
554
- async transaction (model, handler, ...args) {
555
- throw this.plugin.error('notSupportedDriver%s%s%s', this.app.t('method'), 'transaction', this.name)
556
- }
557
-
558
- async dispose () {
559
- await super.dispose()
560
- }
561
- }
562
-
563
- class DoboNullDriver extends DoboDriver {
564
- constructor (plugin, name = 'null', options = {}) {
565
- super(plugin, name, options)
566
- this.memory = true
567
- }
568
- }
569
-
570
- this.app.baseClass.DoboDriver = DoboDriver
571
- this.app.baseClass.DoboNullDriver = DoboNullDriver
572
- }
573
-
574
- export default driverFactory
@@ -1,7 +0,0 @@
1
- function sanitizeId (id) {
2
- const prop = this.properties.find(p => p.name === 'id')
3
- if (prop.type === 'integer') id = parseInt(id)
4
- return id
5
- }
6
-
7
- export default sanitizeId
package/lib/index.js DELETED
@@ -1,3 +0,0 @@
1
- /**
2
- * @module Lib
3
- */