dobo 2.22.1 → 2.24.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.
- package/extend/bajo/intl/en-US.json +1 -1
- package/extend/bajo/intl/id.json +1 -0
- package/extend/dobo/driver/memory.js +26 -25
- package/extend/dobo/feature/created-at.js +14 -3
- package/extend/dobo/feature/immutable.js +1 -2
- package/extend/dobo/feature/removed-at.js +7 -0
- package/extend/dobo/feature/unique.js +18 -6
- package/extend/dobo/feature/updated-at.js +15 -5
- package/index.js +1 -1
- package/lib/collect-connections.js +3 -11
- package/lib/collect-drivers.js +2 -2
- package/lib/collect-features.js +2 -2
- package/lib/collect-models.js +25 -16
- package/lib/factory/action.js +0 -1
- package/lib/factory/connection.js +26 -4
- package/lib/factory/driver.js +131 -26
- package/lib/factory/feature.js +0 -1
- package/lib/factory/model/_util.js +37 -67
- package/lib/factory/model/{bulk-create-records.js → bulk-create-record.js} +9 -9
- package/lib/factory/model/create-attachment.js +1 -1
- package/lib/factory/model/create-record.js +2 -2
- package/lib/factory/model/find-all-record.js +9 -8
- package/lib/factory/model/find-attachment.js +1 -1
- package/lib/factory/model/find-record.js +3 -2
- package/lib/factory/model/get-attachment.js +1 -1
- package/lib/factory/model/get-record.js +2 -2
- package/lib/factory/model/list-attachment.js +1 -1
- package/lib/factory/model/load-fixtures.js +24 -10
- package/lib/factory/model/remove-attachment.js +1 -1
- package/lib/factory/model/remove-record.js +2 -2
- package/lib/factory/model/sanitize-body.js +11 -6
- package/lib/factory/model/sanitize-record.js +2 -1
- package/lib/factory/model/transaction.js +10 -1
- package/lib/factory/model/update-record.js +3 -3
- package/lib/factory/model/upsert-record.js +2 -2
- package/lib/factory/model.js +18 -5
- package/package.json +1 -1
- package/wiki/CHANGES.md +33 -3
package/lib/factory/driver.js
CHANGED
|
@@ -12,8 +12,9 @@ const defIdField = {
|
|
|
12
12
|
|
|
13
13
|
async function driverFactory () {
|
|
14
14
|
const { Tools } = this.app.baseClass
|
|
15
|
-
const { pick, cloneDeep, has, uniq, without, isEmpty, omit, isFunction } = this.app.lib._
|
|
15
|
+
const { pick, cloneDeep, has, uniq, without, isEmpty, omit, isFunction, camelCase, last } = this.app.lib._
|
|
16
16
|
const { isSet } = this.app.lib.aneka
|
|
17
|
+
const { runHook } = this.app.bajo
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Driver class
|
|
@@ -59,11 +60,13 @@ async function driverFactory () {
|
|
|
59
60
|
sanitizeBody (model, body = {}, partial) {
|
|
60
61
|
const { keys, pick } = this.app.lib._
|
|
61
62
|
const item = cloneDeep(body)
|
|
63
|
+
let newId = false
|
|
62
64
|
if (has(item, 'id') && this.idField.name !== 'id') {
|
|
63
65
|
item[this.idField.name] = item.id
|
|
64
|
-
|
|
66
|
+
newId = true
|
|
65
67
|
}
|
|
66
68
|
for (const prop of model.properties) {
|
|
69
|
+
if (item[prop.name] === 'null') item[prop.name] = null
|
|
67
70
|
if (!isSet(item[prop.name]) && !this.support.nullableField) {
|
|
68
71
|
switch (prop.type) {
|
|
69
72
|
case 'datetime': item[prop.name] = new Date(0); break
|
|
@@ -80,7 +83,9 @@ async function driverFactory () {
|
|
|
80
83
|
else if (['object', 'array'].includes(prop.type)) item[prop.name] = JSON.stringify(item[prop.name])
|
|
81
84
|
}
|
|
82
85
|
}
|
|
83
|
-
|
|
86
|
+
const result = partial ? pick(item, keys(body)) : item
|
|
87
|
+
if (newId) delete result.id
|
|
88
|
+
return result
|
|
84
89
|
}
|
|
85
90
|
|
|
86
91
|
sanitizeRecord (model, record = {}) {
|
|
@@ -107,6 +112,7 @@ async function driverFactory () {
|
|
|
107
112
|
const dt = this.useUtc ? dayjs.utc(item[prop.name]) : dayjs(item[prop.name])
|
|
108
113
|
item[prop.name] = dt.toDate()
|
|
109
114
|
}
|
|
115
|
+
if (prop.type === 'boolean' && isSet(item[prop.name])) item[prop.name] = Boolean(item[prop.name])
|
|
110
116
|
}
|
|
111
117
|
}
|
|
112
118
|
return item
|
|
@@ -120,6 +126,15 @@ async function driverFactory () {
|
|
|
120
126
|
return uniq(items)
|
|
121
127
|
}
|
|
122
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}:${name}`, model, ...args)
|
|
134
|
+
await runHook(`${ns}.${camelCase(model.name)}:${name}`, model, ...args)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
123
138
|
/**
|
|
124
139
|
* Check uniqueness of fields with unique index
|
|
125
140
|
*
|
|
@@ -241,7 +256,11 @@ async function driverFactory () {
|
|
|
241
256
|
if (!isEmpty(resp.data)) throw this.plugin.error('recordExists%s%s', body.id, model.name)
|
|
242
257
|
}
|
|
243
258
|
body = this.sanitizeBody(model, body)
|
|
259
|
+
|
|
260
|
+
await this._attachHook('beforeDriverCreateRecord', model, body, options)
|
|
244
261
|
const result = await this.createRecord(model, body, options)
|
|
262
|
+
await this._attachHook('afterDriverCreateRecord', model, body, result, options)
|
|
263
|
+
|
|
245
264
|
if (options.noResult) return
|
|
246
265
|
result.data = this.sanitizeRecord(model, result.data)
|
|
247
266
|
this._injectMeta(result, options)
|
|
@@ -257,14 +276,20 @@ async function driverFactory () {
|
|
|
257
276
|
await this._prepIdForCreate(model, body, options)
|
|
258
277
|
bodies[idx] = this.sanitizeBody(model, body)
|
|
259
278
|
}
|
|
279
|
+
|
|
280
|
+
await this._attachHook('beforeDriverBulkCreateRecord', model, bodies, options)
|
|
260
281
|
const items = chunk(bodies, chunkSize)
|
|
261
282
|
for (const item of items) {
|
|
262
|
-
await this.
|
|
283
|
+
await this.bulkCreateRecord(model, item, options)
|
|
263
284
|
}
|
|
285
|
+
await this._attachHook('afterDriverBulkCreateRecord', model, bodies, [], options)
|
|
264
286
|
}
|
|
265
287
|
|
|
266
288
|
async _getRecord (model, id, options = {}) {
|
|
289
|
+
await this._attachHook('beforeDriverGetRecord', model, id, options)
|
|
267
290
|
const result = await this.getRecord(model, id, options)
|
|
291
|
+
await this._attachHook('afterDriverGetRecord', model, id, result, options)
|
|
292
|
+
|
|
268
293
|
if (isEmpty(result.data) && options.throwNotFound) throw this.plugin.error('recordNotFound%s%s', id, model.name)
|
|
269
294
|
result.data = this.sanitizeRecord(model, result.data)
|
|
270
295
|
this._injectMeta(result, options)
|
|
@@ -283,7 +308,11 @@ async function driverFactory () {
|
|
|
283
308
|
}
|
|
284
309
|
body = this.sanitizeBody(model, body, true)
|
|
285
310
|
delete body.id
|
|
311
|
+
|
|
312
|
+
await this._attachHook('beforeDriverUpdateRecord', model, id, body, options)
|
|
286
313
|
const result = await this.updateRecord(model, id, body, options)
|
|
314
|
+
await this._attachHook('afterDriverUpdateRecord', model, id, body, result, options)
|
|
315
|
+
|
|
287
316
|
if (options.noResult) return
|
|
288
317
|
result.oldData = this.sanitizeRecord(model, result.oldData)
|
|
289
318
|
result.data = this.sanitizeRecord(model, result.data)
|
|
@@ -304,7 +333,11 @@ async function driverFactory () {
|
|
|
304
333
|
}
|
|
305
334
|
}
|
|
306
335
|
body = this.sanitizeBody(model, body)
|
|
336
|
+
|
|
337
|
+
await this._attachHook('beforeDriverUpsertRecord', model, body, options)
|
|
307
338
|
const result = await this.upsertRecord(model, body, options)
|
|
339
|
+
await this._attachHook('afterDriverUpsertRecord', model, body, result, options)
|
|
340
|
+
|
|
308
341
|
if (options.noResult) return
|
|
309
342
|
if (result.oldData) result.oldData = this.sanitizeRecord(model, result.oldData)
|
|
310
343
|
result.data = this.sanitizeRecord(model, result.data)
|
|
@@ -318,7 +351,11 @@ async function driverFactory () {
|
|
|
318
351
|
if (!resp.data) throw this.plugin.error('recordNotFound%s%s', id, model.name)
|
|
319
352
|
options._data = resp.data
|
|
320
353
|
}
|
|
354
|
+
|
|
355
|
+
await this._attachHook('beforeDriverRemoveRecord', model, id, options)
|
|
321
356
|
const result = await this.removeRecord(model, id, options)
|
|
357
|
+
await this._attachHook('afterDriverRemoveRecord', model, id, result, options)
|
|
358
|
+
|
|
322
359
|
if (options.noResult) return
|
|
323
360
|
result.oldData = this.sanitizeRecord(model, result.oldData)
|
|
324
361
|
this._injectMeta(result, options)
|
|
@@ -326,13 +363,29 @@ async function driverFactory () {
|
|
|
326
363
|
}
|
|
327
364
|
|
|
328
365
|
async _clearRecord (model, options = {}) {
|
|
366
|
+
await this._attachHook('beforeDriverClearRecord', model, options)
|
|
329
367
|
const result = await this.clearRecord(model, options)
|
|
368
|
+
await this._attachHook('afterDriverClearRecord', model, result, options)
|
|
369
|
+
|
|
330
370
|
this._injectMeta(result, options)
|
|
331
371
|
return result
|
|
332
372
|
}
|
|
333
373
|
|
|
334
374
|
async _findRecord (model, filter = {}, options = {}) {
|
|
335
|
-
|
|
375
|
+
let result
|
|
376
|
+
try {
|
|
377
|
+
await this._attachHook('beforeDriverFindRecord', model, filter, options)
|
|
378
|
+
result = await this.findRecord(model, filter, options)
|
|
379
|
+
await this._attachHook('afterDriverFindRecord', model, filter, result, options)
|
|
380
|
+
} catch (err) {
|
|
381
|
+
if (err.message !== '_emptyColumnQuery') throw err
|
|
382
|
+
result = {
|
|
383
|
+
data: [],
|
|
384
|
+
count: 0
|
|
385
|
+
// warnings: [] // TODO: should generate warnings?
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
336
389
|
for (const idx in result.data) {
|
|
337
390
|
result.data[idx] = this.sanitizeRecord(model, result.data[idx])
|
|
338
391
|
}
|
|
@@ -341,7 +394,20 @@ async function driverFactory () {
|
|
|
341
394
|
}
|
|
342
395
|
|
|
343
396
|
async _findAllRecord (model, filter = {}, options = {}) {
|
|
344
|
-
|
|
397
|
+
let result
|
|
398
|
+
try {
|
|
399
|
+
await this._attachHook('beforeDriverFindAllRecord', model, filter, options)
|
|
400
|
+
result = await this.findAllRecord(model, filter, options)
|
|
401
|
+
await this._attachHook('afterDriverFindAllRecord', model, filter, result, options)
|
|
402
|
+
} catch (err) {
|
|
403
|
+
if (err.message !== '_emptyColumnQuery') throw err
|
|
404
|
+
result = {
|
|
405
|
+
data: [],
|
|
406
|
+
count: 0
|
|
407
|
+
// warnings: [] // TODO: should generate warnings?
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
345
411
|
for (const idx in result.data) {
|
|
346
412
|
result.data[idx] = this.sanitizeRecord(model, result.data[idx])
|
|
347
413
|
}
|
|
@@ -350,7 +416,17 @@ async function driverFactory () {
|
|
|
350
416
|
}
|
|
351
417
|
|
|
352
418
|
async _countRecord (model, filter = {}, options = {}) {
|
|
353
|
-
|
|
419
|
+
let result
|
|
420
|
+
try {
|
|
421
|
+
await this._attachHook('beforeDriverCountRecord', model, filter, options)
|
|
422
|
+
result = await this.countRecord(model, filter, options)
|
|
423
|
+
await this._attachHook('afterDriverCountRecord', model, filter, result, options)
|
|
424
|
+
} catch (err) {
|
|
425
|
+
if (err.message !== '_emptyColumnQuery') throw err
|
|
426
|
+
result = { data: 0 }
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
return result
|
|
354
430
|
}
|
|
355
431
|
|
|
356
432
|
async _createAggregate (model, filter = {}, params = {}, options = {}) {
|
|
@@ -367,7 +443,16 @@ async function driverFactory () {
|
|
|
367
443
|
if (!prop) throw this.plugin.error('unknown%s%s', this.plugin.t('field.field'), field)
|
|
368
444
|
// if (!fieldPropTypes.includes(prop.type)) throw this.plugin.error('allowedPropType%s%s', field, fieldPropTypes.join(', '))
|
|
369
445
|
|
|
370
|
-
|
|
446
|
+
let result
|
|
447
|
+
try {
|
|
448
|
+
await this._attachHook('beforeDriverCreateAggregate', model, filter, params, options)
|
|
449
|
+
result = await this.createAggregate(model, filter, params, options)
|
|
450
|
+
await this._attachHook('afterDriverCreateAggregate', model, filter, params, result, options)
|
|
451
|
+
} catch (err) {
|
|
452
|
+
if (err.message !== '_emptyColumnQuery') throw err
|
|
453
|
+
result = { data: [] }
|
|
454
|
+
}
|
|
455
|
+
|
|
371
456
|
for (const idx in result.data) {
|
|
372
457
|
result.data[idx] = this.sanitizeRecord(model, result.data[idx])
|
|
373
458
|
}
|
|
@@ -387,8 +472,17 @@ async function driverFactory () {
|
|
|
387
472
|
|
|
388
473
|
prop = model.properties.find(p => p.name === field)
|
|
389
474
|
if (!prop) throw this.plugin.error('unknown%s%s', this.plugin.t('field.field'), field)
|
|
390
|
-
|
|
391
|
-
|
|
475
|
+
|
|
476
|
+
let result
|
|
477
|
+
try {
|
|
478
|
+
await this._attachHook('beforeDriverCreateHistogram', model, filter, params, options)
|
|
479
|
+
result = await this.createHistogram(model, filter, params, options)
|
|
480
|
+
await this._attachHook('afterDriverCreateHistogram', model, filter, params, result, options)
|
|
481
|
+
} catch (err) {
|
|
482
|
+
if (err.message !== '_emptyColumnQuery') throw err
|
|
483
|
+
result = { data: [] }
|
|
484
|
+
}
|
|
485
|
+
|
|
392
486
|
for (const idx in result.data) {
|
|
393
487
|
result.data[idx] = this.sanitizeRecord(model, result.data[idx])
|
|
394
488
|
}
|
|
@@ -402,64 +496,75 @@ async function driverFactory () {
|
|
|
402
496
|
}
|
|
403
497
|
|
|
404
498
|
async modelExists (model, options = {}) {
|
|
405
|
-
throw this.plugin.error('
|
|
499
|
+
throw this.plugin.error('notSupportedDriver%s%s%s', this.app.t('method'), 'modelExists', this.name)
|
|
406
500
|
}
|
|
407
501
|
|
|
408
502
|
async buildModel (model, options = {}) {
|
|
409
|
-
throw this.plugin.error('
|
|
503
|
+
throw this.plugin.error('notSupportedDriver%s%s%s', this.app.t('method'), 'buildModel', this.name)
|
|
410
504
|
}
|
|
411
505
|
|
|
412
506
|
async dropModel (model, options = {}) {
|
|
413
|
-
throw this.plugin.error('
|
|
507
|
+
throw this.plugin.error('notSupportedDriver%s%s%s', this.app.t('method'), 'dropModel', this.name)
|
|
414
508
|
}
|
|
415
509
|
|
|
416
510
|
async createRecord (model, body = {}, options = {}) {
|
|
417
|
-
throw this.plugin.error('
|
|
511
|
+
throw this.plugin.error('notSupportedDriver%s%s%s', this.app.t('method'), 'createRecord', this.name)
|
|
418
512
|
}
|
|
419
513
|
|
|
420
514
|
async getRecord (model, id, options = {}) {
|
|
421
|
-
throw this.plugin.error('
|
|
515
|
+
throw this.plugin.error('notSupportedDriver%s%s%s', this.app.t('method'), 'getRecord', this.name)
|
|
422
516
|
}
|
|
423
517
|
|
|
424
518
|
async updateRecord (model, id, body = {}, options = {}) {
|
|
425
|
-
throw this.plugin.error('
|
|
519
|
+
throw this.plugin.error('notSupportedDriver%s%s%s', this.app.t('method'), 'updateRecord', this.name)
|
|
426
520
|
}
|
|
427
521
|
|
|
428
522
|
async removeRecord (model, id, options = {}) {
|
|
429
|
-
throw this.plugin.error('
|
|
523
|
+
throw this.plugin.error('notSupportedDriver%s%s%s', this.app.t('method'), 'removeRecord', this.name)
|
|
430
524
|
}
|
|
431
525
|
|
|
432
526
|
async clearRecord (model, options = {}) {
|
|
433
|
-
throw this.plugin.error('
|
|
527
|
+
throw this.plugin.error('notSupportedDriver%s%s%s', this.app.t('method'), 'clearRecord', this.name)
|
|
434
528
|
}
|
|
435
529
|
|
|
436
530
|
async findRecord (model, filter = {}, options = {}) {
|
|
437
|
-
throw this.plugin.error('
|
|
531
|
+
throw this.plugin.error('notSupportedDriver%s%s%s', this.app.t('method'), 'findRecord', this.name)
|
|
438
532
|
}
|
|
439
533
|
|
|
440
|
-
async
|
|
441
|
-
throw this.plugin.error('
|
|
534
|
+
async bulkCreateRecord (model, bodies = [], options = {}) {
|
|
535
|
+
throw this.plugin.error('notSupportedDriver%s%s%s', this.app.t('method'), 'bulkCreateRecord', this.name)
|
|
442
536
|
}
|
|
443
537
|
|
|
444
538
|
async countRecord (model, filter = {}, options = {}) {
|
|
445
|
-
throw this.plugin.error('
|
|
539
|
+
throw this.plugin.error('notSupportedDriver%s%s%s', this.app.t('method'), 'countRecord', this.name)
|
|
446
540
|
}
|
|
447
541
|
|
|
448
542
|
async createAggregate (model, filter = {}, params = {}, options = {}) {
|
|
449
|
-
throw this.plugin.error('
|
|
543
|
+
throw this.plugin.error('notSupportedDriver%s%s%s', this.app.t('method'), 'createAggregate', this.name)
|
|
450
544
|
}
|
|
451
545
|
|
|
452
546
|
async createHistogram (model, filter = {}, params = {}, options = {}) {
|
|
453
|
-
throw this.plugin.error('
|
|
547
|
+
throw this.plugin.error('notSupportedDriver%s%s%s', this.app.t('method'), 'createHistogram', this.name)
|
|
454
548
|
}
|
|
455
549
|
|
|
456
550
|
async transaction (model, handler, ...args) {
|
|
457
|
-
throw this.plugin.error('
|
|
551
|
+
throw this.plugin.error('notSupportedDriver%s%s%s', this.app.t('method'), 'transaction', this.name)
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
async dispose () {
|
|
555
|
+
await super.dispose()
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
class DoboNullDriver extends DoboDriver {
|
|
560
|
+
constructor (plugin, name = 'null', options = {}) {
|
|
561
|
+
super(plugin, name, options)
|
|
562
|
+
this.memory = true
|
|
458
563
|
}
|
|
459
564
|
}
|
|
460
565
|
|
|
461
566
|
this.app.baseClass.DoboDriver = DoboDriver
|
|
462
|
-
|
|
567
|
+
this.app.baseClass.DoboNullDriver = DoboNullDriver
|
|
463
568
|
}
|
|
464
569
|
|
|
465
570
|
export default driverFactory
|
package/lib/factory/feature.js
CHANGED
|
@@ -13,17 +13,22 @@ export function cloneOptions (options = {}) {
|
|
|
13
13
|
|
|
14
14
|
export async function execHook (name, ...args) {
|
|
15
15
|
const { runHook } = this.app.bajo
|
|
16
|
-
const { camelCase, last } = this.app.lib._
|
|
16
|
+
const { camelCase, last, kebabCase } = this.app.lib._
|
|
17
17
|
const { noHook } = last(args)
|
|
18
18
|
const { ns } = this.app.dobo
|
|
19
|
+
let [prefix, ...action] = kebabCase(name).split('-')
|
|
20
|
+
action = camelCase(action.join(' '))
|
|
19
21
|
if (!noHook) {
|
|
22
|
+
if (prefix === 'before') await runHook(`${ns}:beforeAction`, action, this.name, ...args)
|
|
20
23
|
await runHook(`${ns}:${name}`, this.name, ...args)
|
|
24
|
+
if (prefix === 'after') await runHook(`${ns}:afterAction`, action, this.name, ...args)
|
|
25
|
+
if (prefix === 'before') await runHook(`${ns}.${camelCase(this.name)}:beforeAction`, action, ...args)
|
|
21
26
|
await runHook(`${ns}.${camelCase(this.name)}:${name}`, ...args)
|
|
27
|
+
if (prefix === 'after') await runHook(`${ns}.${camelCase(this.name)}:afterAction`, action, ...args)
|
|
22
28
|
}
|
|
23
29
|
}
|
|
24
30
|
|
|
25
31
|
export async function execModelHook (name, ...args) {
|
|
26
|
-
if (['beforeBuildQuery', 'beforeBuildSearch', 'afterBuildQuery', 'afterBuildSearch'].includes(name)) return
|
|
27
32
|
const { last } = this.app.lib._
|
|
28
33
|
const { runModelHook } = this.app.dobo
|
|
29
34
|
const { noModelHook } = last(args)
|
|
@@ -62,7 +67,6 @@ export async function execValidation (body, options = {}) {
|
|
|
62
67
|
*/
|
|
63
68
|
export async function getFilterAndOptions (filter = {}, options = {}, action) {
|
|
64
69
|
const { cloneDeep } = this.app.lib._
|
|
65
|
-
const { runModelHook } = this.app.dobo
|
|
66
70
|
const nFilter = cloneDeep(filter || {})
|
|
67
71
|
const nOptions = cloneOptions.call(this, options)
|
|
68
72
|
if (options.noMagic) {
|
|
@@ -81,12 +85,8 @@ export async function getFilterAndOptions (filter = {}, options = {}, action) {
|
|
|
81
85
|
nOptions.throwNotFound = nOptions.throwNotFound ?? true
|
|
82
86
|
nFilter.orgQuery = nFilter.query
|
|
83
87
|
nFilter.orgSearch = nFilter.search
|
|
84
|
-
if (!nOptions.noModelHook) await runModelHook(this, 'beforeBuildQuery', nFilter.query, nOptions)
|
|
85
88
|
nFilter.query = buildFilterQuery.call(this, nFilter) ?? {}
|
|
86
|
-
if (!nOptions.noModelHook) await runModelHook(this, 'afterBuildQuery', nFilter.query, nOptions)
|
|
87
|
-
if (!nOptions.noModelHook) await runModelHook(this, 'beforeBuilSearch', nFilter.search, nOptions)
|
|
88
89
|
nFilter.search = buildFilterSearch.call(this, nFilter) ?? {}
|
|
89
|
-
if (!nOptions.noModelHook) await runModelHook(this, 'afterBuildSearch', nFilter.search, nOptions)
|
|
90
90
|
const { limit, page, skip, sort } = preparePagination.call(this, nFilter, nOptions)
|
|
91
91
|
nFilter.limit = limit
|
|
92
92
|
nFilter.page = page
|
|
@@ -135,7 +135,7 @@ export async function getAttachmentPath (id, field, file, options = {}) {
|
|
|
135
135
|
|
|
136
136
|
export async function copyAttachment (id, options = {}) {
|
|
137
137
|
if (!this.app.waibu) return
|
|
138
|
-
if (!this.attachment) return
|
|
138
|
+
if (!this.options.attachment) return
|
|
139
139
|
const { fs } = this.app.lib
|
|
140
140
|
const { req, setField, setFile, mimeType, stats } = options
|
|
141
141
|
const { dir, files } = await this.app.waibu.getUploadedFiles(req.id, false, true)
|
|
@@ -158,7 +158,7 @@ export async function copyAttachment (id, options = {}) {
|
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
export async function handleAttachmentUpload (id, trigger, options = {}) {
|
|
161
|
-
if (!this.attachment) return
|
|
161
|
+
if (!this.options.attachment) return
|
|
162
162
|
const { getPluginDataDir } = this.app.bajo
|
|
163
163
|
const { fs } = this.app.lib
|
|
164
164
|
const { req, mimeType, stats, setFile, setField } = options
|
|
@@ -170,53 +170,7 @@ export async function handleAttachmentUpload (id, trigger, options = {}) {
|
|
|
170
170
|
return copyAttachment.call(this, id, { req, mimeType, stats, setFile, setField })
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
async function
|
|
174
|
-
if (!((typeof options.refs === 'string' && ['*', 'all'].includes(options.refs)) || options.refs.includes(key))) return
|
|
175
|
-
if (ref.fields.length === 0) return
|
|
176
|
-
const { fmt } = options
|
|
177
|
-
const fields = [...ref.fields]
|
|
178
|
-
if (!fields.includes(prop.name)) fields.push(prop.name)
|
|
179
|
-
const rOptions = { dataOnly: true, refs: [], fmt, fields }
|
|
180
|
-
const results = await rModel.findRecord(filter, rOptions)
|
|
181
|
-
return { rOptions, results }
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
export async function getSingleRef (record = {}, options = {}) {
|
|
185
|
-
const { isSet } = this.app.lib.aneka
|
|
186
|
-
const { parseQuery } = this.app.dobo
|
|
187
|
-
const { get } = this.app.lib._
|
|
188
|
-
const props = this.properties.filter(p => isSet(p.ref) && !(options.hidden ?? []).includes(p.name))
|
|
189
|
-
const refs = {}
|
|
190
|
-
options.refs = options.refs ?? []
|
|
191
|
-
if (props.length > 0) {
|
|
192
|
-
for (const prop of props) {
|
|
193
|
-
for (const key in prop.ref) {
|
|
194
|
-
try {
|
|
195
|
-
if (get(record, `_ref.${key}`)) continue
|
|
196
|
-
const ref = prop.ref[key]
|
|
197
|
-
const rModel = this.app.dobo.getModel(ref.model, true)
|
|
198
|
-
if (!rModel) return
|
|
199
|
-
let query = {}
|
|
200
|
-
query[ref.field] = record[prop.name]
|
|
201
|
-
if (ref.field === 'id') query[ref.field] = this.sanitizeId(query[ref.field])
|
|
202
|
-
if (ref.query) query = { $and: [query, parseQuery(ref.query, rModel)] }
|
|
203
|
-
const filter = { query }
|
|
204
|
-
const resp = await _getRef.call(this, { ref, rModel, prop, key, options, filter })
|
|
205
|
-
if (!resp) continue
|
|
206
|
-
const { rOptions, results } = resp
|
|
207
|
-
const data = []
|
|
208
|
-
for (const res of results) {
|
|
209
|
-
data.push(await rModel.sanitizeRecord(res, rOptions))
|
|
210
|
-
}
|
|
211
|
-
refs[key] = ['1:1'].includes(ref.type) ? data[0] : data
|
|
212
|
-
} catch (err) {}
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
record._ref = refs
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
export async function getMultiRefs (records = [], options = {}) {
|
|
173
|
+
export async function getRefs (records = [], options = {}) {
|
|
220
174
|
const { isSet } = this.app.lib.aneka
|
|
221
175
|
const { uniq, without, get } = this.app.lib._
|
|
222
176
|
const { parseQuery } = this.app.dobo
|
|
@@ -226,30 +180,46 @@ export async function getMultiRefs (records = [], options = {}) {
|
|
|
226
180
|
for (const prop of props) {
|
|
227
181
|
for (const key in prop.ref) {
|
|
228
182
|
try {
|
|
229
|
-
if (
|
|
183
|
+
if (records.length === 0) return
|
|
184
|
+
const isValues = Array.isArray(records[0][prop.name])
|
|
185
|
+
if (get(records, `0._ref.${key}`)) return
|
|
230
186
|
const ref = prop.ref[key]
|
|
231
187
|
const rModel = this.app.dobo.getModel(ref.model, true)
|
|
232
188
|
if (!rModel) return
|
|
233
189
|
let matches = []
|
|
234
|
-
for (const
|
|
235
|
-
|
|
190
|
+
for (const rec of records) {
|
|
191
|
+
const items = isValues ? [...rec[prop.name]] : [rec[prop.name]]
|
|
192
|
+
matches.push(...items.map(item => prop.name === 'id' ? rModel.sanitizeId(item) : item))
|
|
236
193
|
}
|
|
237
194
|
matches = uniq(without(matches, undefined, null, NaN)).map(i => i + '')
|
|
238
195
|
let query = {}
|
|
239
196
|
query[ref.field] = { $in: matches }
|
|
197
|
+
|
|
198
|
+
const siteIdProp = this.properties.find(item => item.name === 'siteId')
|
|
199
|
+
const siteIdRProp = rModel.properties.find(item => item.name === 'siteId')
|
|
200
|
+
if (siteIdProp && siteIdRProp) query.siteId = records[0].siteId
|
|
201
|
+
|
|
240
202
|
if (ref.query) query = { $and: [query, parseQuery(ref.query, rModel)] }
|
|
241
203
|
const filter = { query, limit: matches.length }
|
|
242
|
-
|
|
243
|
-
if (
|
|
244
|
-
const {
|
|
204
|
+
if (!((typeof options.refs === 'string' && ['*', 'all'].includes(options.refs)) || options.refs.includes(key))) return
|
|
205
|
+
if (ref.fields.length === 0) return
|
|
206
|
+
const { fmt, req } = options
|
|
207
|
+
const fields = [...ref.fields]
|
|
208
|
+
if (!fields.includes(prop.name)) fields.push(prop.name)
|
|
209
|
+
const rOptions = { dataOnly: true, refs: [], fmt, req, fields }
|
|
210
|
+
const results = await rModel.findRecord(filter, rOptions)
|
|
245
211
|
for (const i in records) {
|
|
246
212
|
records[i]._ref = records[i]._ref ?? {}
|
|
247
213
|
const rec = records[i]
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
214
|
+
let items = isValues ? [...rec[prop.name]] : [rec[prop.name]]
|
|
215
|
+
items = items.map(item => item + '')
|
|
216
|
+
const res = results.filter(r => items.includes(r[ref.field] + ''))
|
|
217
|
+
if (res.length === 0) records[i]._ref[key] = isValues ? [] : {}
|
|
218
|
+
else records[i]._ref[key] = isValues ? res : res[0]
|
|
251
219
|
}
|
|
252
|
-
} catch (err) {
|
|
220
|
+
} catch (err) {
|
|
221
|
+
if (this.app.bajo.config.log.level === 'trace') console.error(err)
|
|
222
|
+
}
|
|
253
223
|
}
|
|
254
224
|
}
|
|
255
225
|
}
|
|
@@ -261,7 +231,7 @@ export function buildFilterQuery (filter = {}) {
|
|
|
261
231
|
return sanitizeQuery.call(this, query)
|
|
262
232
|
}
|
|
263
233
|
|
|
264
|
-
function sanitizeQuery (query = {}, parent) {
|
|
234
|
+
export function sanitizeQuery (query = {}, parent) {
|
|
265
235
|
const { isPlainObject, isArray, find, cloneDeep } = this.app.lib._
|
|
266
236
|
const { isSet } = this.app.lib.aneka
|
|
267
237
|
const { dayjs } = this.app.lib
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { getFilterAndOptions, execHook, execValidation, execModelHook, execDynHook } from './_util.js'
|
|
2
2
|
|
|
3
3
|
export const onlyTypes = ['datetime', 'date', 'time', 'timestamp']
|
|
4
|
-
const action = '
|
|
4
|
+
const action = 'bulkCreateRecord'
|
|
5
5
|
|
|
6
|
-
async function
|
|
6
|
+
async function bulkCreateRecord (...args) {
|
|
7
7
|
if (args.length === 0) return this.action(action, ...args)
|
|
8
8
|
const [bodies = [], opts = {}] = args
|
|
9
9
|
const { cloneDeep, get } = this.app.lib._
|
|
@@ -17,9 +17,9 @@ async function bulkCreateRecords (...args) {
|
|
|
17
17
|
inputs[idx] = await this.sanitizeBody({ body: inputs[idx], extFields, strict: true, truncateString, onlyTypes })
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
|
-
await execHook.call(this, '
|
|
21
|
-
await execModelHook.call(this, '
|
|
22
|
-
await execDynHook.call(this, '
|
|
20
|
+
await execHook.call(this, 'beforeBulkCreateRecord', inputs, options)
|
|
21
|
+
await execModelHook.call(this, 'beforeBulkCreateRecord', inputs, options)
|
|
22
|
+
await execDynHook.call(this, 'beforeBulkCreateRecord', inputs, options)
|
|
23
23
|
if (!noValidation) {
|
|
24
24
|
for (const input of inputs) {
|
|
25
25
|
await execValidation.call(this, input, options)
|
|
@@ -27,10 +27,10 @@ async function bulkCreateRecords (...args) {
|
|
|
27
27
|
}
|
|
28
28
|
// TODO: bulk don't return anything currently, it should return at least a stat
|
|
29
29
|
await this.driver._bulkCreateRecords(this, inputs, options)
|
|
30
|
-
await execDynHook.call(this, '
|
|
31
|
-
await execModelHook.call(this, '
|
|
32
|
-
await execHook.call(this, '
|
|
30
|
+
await execDynHook.call(this, 'afterBulkCreateRecord', inputs, options)
|
|
31
|
+
await execModelHook.call(this, 'afterBulkCreateRecord', inputs, options)
|
|
32
|
+
await execHook.call(this, 'afterBulkCreateRecord', inputs, options)
|
|
33
33
|
return []
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
export default
|
|
36
|
+
export default bulkCreateRecord
|
|
@@ -4,7 +4,7 @@ const action = 'createAttachment'
|
|
|
4
4
|
async function createAttachment (...args) {
|
|
5
5
|
const { createThumbnail } = this.app.bajoExtra
|
|
6
6
|
const { thumbSizes: size } = this.app.dobo.config.default.attachment
|
|
7
|
-
if (!this.attachment) return
|
|
7
|
+
if (!this.options.attachment) return
|
|
8
8
|
if (args.length === 0) return this.action(action, ...args)
|
|
9
9
|
const [id, opts = {}] = args
|
|
10
10
|
const { fs } = this.app.lib
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getFilterAndOptions, execHook, execValidation, execModelHook, execDynHook,
|
|
1
|
+
import { getFilterAndOptions, execHook, execValidation, execModelHook, execDynHook, getRefs, handleReq } from './_util.js'
|
|
2
2
|
|
|
3
3
|
export const onlyTypes = ['datetime', 'date', 'time', 'timestamp', 'array', 'object']
|
|
4
4
|
const action = 'createRecord'
|
|
@@ -25,7 +25,7 @@ async function createRecord (...args) {
|
|
|
25
25
|
result = result ?? {}
|
|
26
26
|
const { warnings } = getDefaultValues(options)
|
|
27
27
|
if (!warnings) delete result.warnings
|
|
28
|
-
if (isSet(options.refs)) await
|
|
28
|
+
if (isSet(options.refs)) await getRefs.call(this, [result.data], options)
|
|
29
29
|
if (!noResultSanitizer) result.data = await this.sanitizeRecord(result.data, options)
|
|
30
30
|
await execDynHook.call(this, 'afterCreateRecord', input, result, options)
|
|
31
31
|
await execModelHook.call(this, 'afterCreateRecord', input, result, options)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getRefs, execHook, execModelHook, execDynHook, getFilterAndOptions, cloneOptions, sanitizeQuery } from './_util.js'
|
|
2
2
|
const action = 'findAllRecord'
|
|
3
3
|
|
|
4
4
|
async function native (...args) {
|
|
@@ -13,9 +13,10 @@ async function native (...args) {
|
|
|
13
13
|
const { hardCap, warnings } = getDefaultValues(options)
|
|
14
14
|
if (dataOnly) options.count = false
|
|
15
15
|
const { noResultSanitizer } = options
|
|
16
|
-
await execHook.call(this, '
|
|
17
|
-
await execModelHook.call(this, '
|
|
18
|
-
await execDynHook.call(this, '
|
|
16
|
+
await execHook.call(this, 'beforeFindAllRecord', filter, options)
|
|
17
|
+
await execModelHook.call(this, 'beforeFindAllRecord', filter, options)
|
|
18
|
+
await execDynHook.call(this, 'beforeFindAllRecord', filter, options)
|
|
19
|
+
filter.query = sanitizeQuery.call(this, filter.query)
|
|
19
20
|
const cFilter = cloneDeep(filter)
|
|
20
21
|
if (get) {
|
|
21
22
|
const resp = await get({ model: this, action, filter: cFilter, options })
|
|
@@ -37,15 +38,15 @@ async function native (...args) {
|
|
|
37
38
|
result.pages = options.count ? Math.ceil(result.count / filter.limit) : undefined
|
|
38
39
|
if (!warnings) delete result.warnings
|
|
39
40
|
|
|
40
|
-
if (isSet(options.refs)) await
|
|
41
|
+
if (isSet(options.refs)) await getRefs.call(this, result.data, options)
|
|
41
42
|
if (!noResultSanitizer) {
|
|
42
43
|
for (const idx in result.data) {
|
|
43
44
|
result.data[idx] = await this.sanitizeRecord(result.data[idx], options)
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
|
-
await execDynHook.call(this, '
|
|
47
|
-
await execModelHook.call(this, '
|
|
48
|
-
await execHook.call(this, '
|
|
47
|
+
await execDynHook.call(this, 'afterFindAllRecord', filter, result, options)
|
|
48
|
+
await execModelHook.call(this, 'afterFindAllRecord', filter, result, options)
|
|
49
|
+
await execHook.call(this, 'afterFindAllRecord', filter, result, options)
|
|
49
50
|
if (set) await set({ model: this, action, filter: cFilter, options, result })
|
|
50
51
|
return dataOnly ? result.data : result
|
|
51
52
|
}
|
|
@@ -2,7 +2,7 @@ import { mergeAttachmentInfo } from './_util.js'
|
|
|
2
2
|
const action = 'findAttachment'
|
|
3
3
|
|
|
4
4
|
async function findAttachment (...args) {
|
|
5
|
-
if (!this.attachment) return
|
|
5
|
+
if (!this.options.attachment) return
|
|
6
6
|
if (args.length === 0) return this.action(action, ...args)
|
|
7
7
|
const [id, opts = {}] = args
|
|
8
8
|
const { fastGlob, fs } = this.app.lib
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getFilterAndOptions, execHook, execModelHook, execDynHook,
|
|
1
|
+
import { getFilterAndOptions, execHook, execModelHook, execDynHook, getRefs, sanitizeQuery } from './_util.js'
|
|
2
2
|
const action = 'findRecord'
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -80,6 +80,7 @@ async function findRecord (...args) {
|
|
|
80
80
|
await execHook.call(this, 'beforeFindRecord', filter, options)
|
|
81
81
|
await execModelHook.call(this, 'beforeFindRecord', filter, options)
|
|
82
82
|
await execDynHook.call(this, 'beforeFindRecord', filter, options)
|
|
83
|
+
filter.query = sanitizeQuery.call(this, filter.query)
|
|
83
84
|
const cFilter = cloneDeep(filter)
|
|
84
85
|
if (get) {
|
|
85
86
|
const resp = await get({ model: this, action, filter: cFilter, options })
|
|
@@ -101,7 +102,7 @@ async function findRecord (...args) {
|
|
|
101
102
|
}
|
|
102
103
|
result.pages = options.count ? Math.ceil(result.count / filter.limit) : undefined
|
|
103
104
|
if (!warnings) delete result.warnings
|
|
104
|
-
if (isSet(options.refs)) await
|
|
105
|
+
if (isSet(options.refs)) await getRefs.call(this, result.data, options)
|
|
105
106
|
if (!noResultSanitizer) {
|
|
106
107
|
for (const idx in result.data) {
|
|
107
108
|
result.data[idx] = await this.sanitizeRecord(result.data[idx], options)
|