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.
- package/.jsdoc.conf.json +1 -1
- package/docs/Dobo.html +1 -24
- package/docs/DoboAction.html +13 -0
- package/docs/DoboAdapter.html +3 -0
- package/docs/DoboConnection.html +3 -0
- package/docs/DoboFeature.html +3 -0
- package/docs/DoboModel.html +51 -0
- package/docs/data/search.json +1 -1
- package/docs/extend_bajo_hook-docs.js.html +241 -0
- package/docs/external-Tools.html +3 -0
- package/docs/global.html +1 -5
- package/docs/index.html +1 -1
- package/docs/index.js.html +434 -380
- package/docs/lib_factory_action.js.html +290 -0
- package/docs/lib_factory_adapter.js.html +1057 -0
- package/docs/lib_factory_connection.js.html +112 -0
- package/docs/lib_factory_feature.js.html +55 -0
- package/docs/lib_factory_model.js.html +503 -0
- package/docs/lib_factory_model_build.js.html +28 -0
- package/docs/lib_factory_model_clear-record.js.html +33 -0
- package/docs/lib_factory_model_count-record.js.html +45 -0
- package/docs/lib_factory_model_create-record.js.html +52 -0
- package/docs/lib_factory_model_drop.js.html +29 -0
- package/docs/lib_factory_model_exists.js.html +29 -0
- package/docs/lib_factory_model_find-all-record.js.html +115 -0
- package/docs/lib_factory_model_find-one-record.js.html +41 -0
- package/docs/lib_factory_model_find-record.js.html +85 -0
- package/docs/lib_factory_model_get-record.js.html +55 -0
- package/docs/lib_factory_model_helper.js.html +579 -0
- package/docs/lib_factory_model_remove-record.js.html +47 -0
- package/docs/lib_factory_model_sanitize-body.js.html +60 -0
- package/docs/lib_factory_model_sanitize-fixture.js.html +59 -0
- package/docs/lib_factory_model_sanitize-record.js.html +73 -0
- package/docs/lib_factory_model_update-record.js.html +61 -0
- package/docs/lib_factory_model_upsert-record.js.html +74 -0
- package/docs/{method_validate.js.html → lib_factory_model_validate.js.html} +109 -73
- package/docs/lib_helper.js.html +466 -0
- package/docs/module-Helper.html +3 -0
- package/docs/module-Helper_Model.html +3 -0
- package/docs/module-Hook.html +15 -0
- package/docs/scripts/core.js +477 -476
- package/docs/scripts/resize.js +36 -36
- package/docs/scripts/search.js +105 -105
- package/docs/scripts/third-party/fuse.js +1 -1
- package/docs/scripts/third-party/hljs-line-num-original.js +285 -282
- package/docs/scripts/third-party/hljs-line-num.js +1 -1
- package/docs/scripts/third-party/hljs-original.js +1202 -1195
- package/docs/scripts/third-party/hljs.js +1 -1
- package/docs/scripts/third-party/popper.js +1 -1
- package/docs/scripts/third-party/tippy.js +1 -1
- package/docs/scripts/third-party/tocbot.js +509 -508
- package/docs/static/home.md +3 -1
- package/extend/bajo/hook-docs.js +238 -0
- package/extend/bajo/intl/en-US.json +9 -9
- package/extend/bajo/intl/id.json +9 -9
- package/extend/bajoCli/applet/rebuild-model.js +2 -2
- package/extend/dobo/{driver → adapter}/memory.js +6 -6
- package/extend/dobo/feature/immutable.js +2 -2
- package/extend/dobo/feature/removed-at.js +1 -1
- package/index.js +33 -30
- package/lib/factory/action.js +145 -18
- package/lib/factory/adapter.js +1054 -0
- package/lib/factory/connection.js +46 -22
- package/lib/factory/feature.js +23 -3
- package/lib/factory/model/build.js +11 -2
- package/lib/factory/model/bulk-create-record.js +2 -2
- package/lib/factory/model/clear-record.js +13 -3
- package/lib/factory/model/count-record.js +13 -2
- package/lib/factory/model/create-aggregate.js +2 -2
- package/lib/factory/model/create-attachment.js +1 -1
- package/lib/factory/model/create-histogram.js +2 -2
- package/lib/factory/model/create-record.js +15 -2
- package/lib/factory/model/drop.js +12 -2
- package/lib/factory/model/exists.js +10 -6
- package/lib/factory/model/find-all-record.js +14 -3
- package/lib/factory/model/find-attachment.js +1 -1
- package/lib/factory/model/find-one-record.js +13 -1
- package/lib/factory/model/find-record.js +17 -53
- package/lib/factory/model/get-record.js +11 -39
- package/lib/factory/model/{_util.js → helper.js} +176 -16
- package/lib/factory/model/remove-attachment.js +1 -1
- package/lib/factory/model/remove-record.js +11 -28
- package/lib/factory/model/sanitize-body.js +14 -12
- package/lib/factory/model/sanitize-fixture.js +17 -4
- package/lib/factory/model/sanitize-record.js +9 -6
- package/lib/factory/model/transaction.js +2 -2
- package/lib/factory/model/update-record.js +12 -40
- package/lib/factory/model/upsert-record.js +13 -42
- package/lib/factory/model/validate.js +47 -14
- package/lib/factory/model.js +301 -23
- package/lib/{collect-models.js → helper.js} +463 -364
- package/package.json +1 -1
- package/wiki/CHANGES.md +17 -7
- package/wiki/ECOSYSTEM.md +7 -7
- package/wiki/GETTING-STARTED.md +3 -3
- package/wiki/QUERY-LANGUAGE.md +1 -1
- package/docs/lib_collect-connections.js.html +0 -39
- package/docs/lib_collect-drivers.js.html +0 -52
- package/docs/lib_collect-features.js.html +0 -36
- package/docs/lib_collect-schemas.js.html +0 -94
- package/docs/lib_index.js.html +0 -6
- package/docs/method_model_create.js.html +0 -35
- package/docs/method_model_drop.js.html +0 -34
- package/docs/method_model_exists.js.html +0 -40
- package/docs/method_record_count.js.html +0 -69
- package/docs/method_record_create.js.html +0 -114
- package/docs/method_record_find-all.js.html +0 -44
- package/docs/method_record_find-one.js.html +0 -73
- package/docs/method_record_find.js.html +0 -118
- package/docs/method_record_get.js.html +0 -92
- package/docs/method_record_remove.js.html +0 -75
- package/docs/method_record_update.js.html +0 -107
- package/docs/method_record_upsert.js.html +0 -54
- package/docs/method_sanitize_body.js.html +0 -88
- package/docs/method_sanitize_date.js.html +0 -30
- package/docs/method_sanitize_id.js.html +0 -20
- package/docs/module-Lib.html +0 -3
- package/lib/collect-connections.js +0 -56
- package/lib/collect-drivers.js +0 -35
- package/lib/collect-features.js +0 -40
- package/lib/factory/driver.js +0 -574
- package/lib/factory/model/sanitize-id.js +0 -7
- package/lib/index.js +0 -3
package/lib/factory/driver.js
DELETED
|
@@ -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
|
package/lib/index.js
DELETED