dobo 1.1.0 → 1.1.2
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/bajo/intl/en-US.json +5 -1
- package/bajo/intl/id.json +0 -1
- package/bajoCli/applet/connection.js +1 -1
- package/bajoCli/applet/lib/post-process.js +1 -1
- package/bajoCli/applet/model-clear.js +1 -1
- package/bajoCli/applet/model-rebuild.js +27 -7
- package/bajoCli/applet/record-create.js +1 -1
- package/bajoCli/applet/record-find.js +1 -1
- package/bajoCli/applet/record-get.js +1 -1
- package/bajoCli/applet/record-remove.js +1 -1
- package/bajoCli/applet/record-update.js +1 -1
- package/bajoCli/applet/schema.js +1 -1
- package/bajoCli/applet/stat-count.js +1 -1
- package/dobo/feature/removed-at.js +85 -0
- package/lib/add-fixtures.js +1 -1
- package/lib/build-bulk-action.js +1 -1
- package/lib/check-unique.js +5 -5
- package/lib/collect-connections.js +1 -1
- package/lib/collect-drivers.js +1 -1
- package/lib/collect-feature.js +1 -1
- package/lib/collect-schemas.js +3 -3
- package/lib/exec-feature-hook.js +4 -3
- package/lib/exec-validation.js +1 -1
- package/lib/generic-prop-sanitizer.js +1 -1
- package/lib/handle-attachment-upload.js +1 -1
- package/lib/mem-db/conn-sanitizer.js +1 -1
- package/lib/mem-db/instantiate.js +2 -2
- package/lib/mem-db/method/record/find.js +1 -1
- package/lib/mem-db/method/record/get.js +1 -1
- package/lib/mem-db/method/record/remove.js +1 -1
- package/lib/mem-db/method/record/update.js +1 -1
- package/lib/mem-db/start.js +1 -1
- package/lib/merge-attachment-info.js +2 -2
- package/lib/resolve-method.js +2 -2
- package/lib/sanitize-schema.js +3 -3
- package/package.json +1 -1
- package/plugin/factory.js +324 -0
- package/plugin/method/attachment/copy-uploaded.js +1 -1
- package/plugin/method/attachment/create.js +1 -1
- package/plugin/method/attachment/find.js +1 -1
- package/plugin/method/attachment/get-path.js +1 -1
- package/plugin/method/attachment/get.js +1 -1
- package/plugin/method/attachment/remove.js +1 -1
- package/plugin/method/bulk/create.js +1 -1
- package/plugin/method/model/clear.js +1 -1
- package/plugin/method/model/create.js +1 -1
- package/plugin/method/model/drop.js +1 -1
- package/plugin/method/model/exists.js +1 -1
- package/plugin/method/record/clear.js +1 -1
- package/plugin/method/record/count.js +13 -8
- package/plugin/method/record/create.js +9 -8
- package/plugin/method/record/find-one.js +11 -4
- package/plugin/method/record/find.js +10 -4
- package/plugin/method/record/get.js +10 -4
- package/plugin/method/record/remove.js +9 -4
- package/plugin/method/record/update.js +6 -4
- package/plugin/method/record/upsert.js +20 -6
- package/plugin/method/sanitize/body.js +1 -1
- package/plugin/method/sanitize/date.js +1 -1
- package/plugin/method/validate.js +2 -2
- package/waibuMpa/route/attachment/@model/@id/@field/@file.js +9 -2
- package/plugin/.alias +0 -1
- package/plugin/config.json +0 -36
- package/plugin/init.js +0 -29
- package/plugin/method/aggregate-types.js +0 -1
- package/plugin/method/build-match.js +0 -34
- package/plugin/method/build-query.js +0 -14
- package/plugin/method/get-connection.js +0 -6
- package/plugin/method/get-info.js +0 -14
- package/plugin/method/get-schema.js +0 -10
- package/plugin/method/pick-record.js +0 -36
- package/plugin/method/prep-pagination.js +0 -63
- package/plugin/method/prop-type.js +0 -43
- package/plugin/method/validation-error-message.js +0 -12
- package/plugin/start.js +0 -20
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import collectConnections from '../lib/collect-connections.js'
|
|
2
|
+
import collectDrivers from '../lib/collect-drivers.js'
|
|
3
|
+
import collectFeature from '../lib/collect-feature.js'
|
|
4
|
+
import collectSchemas from '../lib/collect-schemas.js'
|
|
5
|
+
import memDbStart from '../lib/mem-db/start.js'
|
|
6
|
+
import memDbInstantiate from '../lib/mem-db/instantiate.js'
|
|
7
|
+
import nql from '@tryghost/nql'
|
|
8
|
+
|
|
9
|
+
async function factory (pkgName) {
|
|
10
|
+
const me = this
|
|
11
|
+
|
|
12
|
+
return class Dobo extends this.lib.BajoPlugin {
|
|
13
|
+
constructor () {
|
|
14
|
+
super(pkgName, me.app)
|
|
15
|
+
this.alias = 'db'
|
|
16
|
+
this.config = {
|
|
17
|
+
connections: [],
|
|
18
|
+
mergeProps: ['connections'],
|
|
19
|
+
validationParams: {
|
|
20
|
+
abortEarly: false,
|
|
21
|
+
convert: false,
|
|
22
|
+
allowUnknown: true
|
|
23
|
+
},
|
|
24
|
+
default: {
|
|
25
|
+
property: {
|
|
26
|
+
text: {
|
|
27
|
+
kind: 'text'
|
|
28
|
+
},
|
|
29
|
+
string: {
|
|
30
|
+
length: 50
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
filter: {
|
|
34
|
+
limit: 25,
|
|
35
|
+
maxLimit: 200,
|
|
36
|
+
sort: ['dt:-1', 'updatedAt:-1', 'updated_at:-1', 'createdAt:-1', 'createdAt:-1', 'ts:-1', 'username', 'name']
|
|
37
|
+
},
|
|
38
|
+
idField: {
|
|
39
|
+
type: 'string',
|
|
40
|
+
maxLength: 50,
|
|
41
|
+
required: true,
|
|
42
|
+
index: { type: 'primary' }
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
memDb: {
|
|
46
|
+
createDefConnAtStart: true,
|
|
47
|
+
persistence: {
|
|
48
|
+
syncPeriod: 1
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
this.aggregateTypes = ['count', 'avg', 'min', 'max', 'sum']
|
|
53
|
+
this.propType = {
|
|
54
|
+
integer: {
|
|
55
|
+
validator: 'number'
|
|
56
|
+
},
|
|
57
|
+
smallint: {
|
|
58
|
+
validator: 'number'
|
|
59
|
+
},
|
|
60
|
+
text: {
|
|
61
|
+
validator: 'string',
|
|
62
|
+
kind: 'text',
|
|
63
|
+
choices: ['text', 'mediumtext', 'longtext']
|
|
64
|
+
},
|
|
65
|
+
string: {
|
|
66
|
+
validator: 'string',
|
|
67
|
+
maxLength: 255,
|
|
68
|
+
minLength: 0
|
|
69
|
+
},
|
|
70
|
+
float: {
|
|
71
|
+
validator: 'number'
|
|
72
|
+
},
|
|
73
|
+
double: {
|
|
74
|
+
validator: 'number'
|
|
75
|
+
},
|
|
76
|
+
boolean: {
|
|
77
|
+
validator: 'boolean'
|
|
78
|
+
},
|
|
79
|
+
date: {
|
|
80
|
+
validator: 'date'
|
|
81
|
+
},
|
|
82
|
+
datetime: {
|
|
83
|
+
validator: 'date'
|
|
84
|
+
},
|
|
85
|
+
time: {
|
|
86
|
+
validator: 'date'
|
|
87
|
+
},
|
|
88
|
+
timestamp: {
|
|
89
|
+
validator: 'timestamp'
|
|
90
|
+
},
|
|
91
|
+
object: {},
|
|
92
|
+
array: {}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
init = async () => {
|
|
97
|
+
const { buildCollections } = this.app.bajo
|
|
98
|
+
const { fs } = this.lib
|
|
99
|
+
const checkType = async (item, items) => {
|
|
100
|
+
const { filter } = this.lib._
|
|
101
|
+
const existing = filter(items, { type: 'dobo:memory' })
|
|
102
|
+
if (existing.length > 1) this.fatal('onlyOneConnType%s', item.type)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
fs.ensureDirSync(`${this.dir.data}/attachment`)
|
|
106
|
+
await collectDrivers.call(this)
|
|
107
|
+
if (this.config.memDb.createDefConnAtStart) {
|
|
108
|
+
this.config.connections.push({
|
|
109
|
+
type: 'dobo:memory',
|
|
110
|
+
name: 'memory'
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
this.connections = await buildCollections({ ns: this.name, container: 'connections', handler: collectConnections, dupChecks: ['name', checkType] })
|
|
114
|
+
if (this.connections.length === 0) this.log.warn('notFound%s', this.print.write('connection'))
|
|
115
|
+
await collectFeature.call(this)
|
|
116
|
+
await collectSchemas.call(this)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
start = async (conns = 'all', noRebuild = true) => {
|
|
120
|
+
const { importModule, breakNsPath } = this.app.bajo
|
|
121
|
+
const { find, filter, isString, map } = this.lib._
|
|
122
|
+
if (conns === 'all') conns = this.connections
|
|
123
|
+
else if (isString(conns)) conns = filter(this.connections, { name: conns })
|
|
124
|
+
else conns = map(conns, c => find(this.connections, { name: c }))
|
|
125
|
+
for (const c of conns) {
|
|
126
|
+
const { ns } = breakNsPath(c.type)
|
|
127
|
+
const schemas = filter(this.schemas, { connection: c.name })
|
|
128
|
+
const mod = c.type === 'dobo:memory' ? memDbInstantiate : await importModule(`${ns}:/${this.name}/boot/instantiate.js`)
|
|
129
|
+
await mod.call(this.app[ns], { connection: c, noRebuild, schemas })
|
|
130
|
+
this.log.trace('driverInstantiated%s%s', c.driver, c.name)
|
|
131
|
+
}
|
|
132
|
+
await memDbStart.call(this)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
pickRecord = async ({ record, fields, schema = {}, hidden = [], forceNoHidden } = {}) => {
|
|
136
|
+
const { isArray, pick, clone, isEmpty, omit } = this.lib._
|
|
137
|
+
const { dayjs } = this.lib
|
|
138
|
+
|
|
139
|
+
const transform = async ({ record, schema, hidden = [], forceNoHidden } = {}) => {
|
|
140
|
+
if (record._id) {
|
|
141
|
+
record.id = record._id
|
|
142
|
+
delete record._id
|
|
143
|
+
}
|
|
144
|
+
const defHidden = [...schema.hidden, ...hidden]
|
|
145
|
+
let result = {}
|
|
146
|
+
for (const p of schema.properties) {
|
|
147
|
+
if (!forceNoHidden && defHidden.includes(p.name)) continue
|
|
148
|
+
result[p.name] = record[p.name] ?? null
|
|
149
|
+
if (record[p.name] === null) continue
|
|
150
|
+
switch (p.type) {
|
|
151
|
+
case 'boolean': result[p.name] = !!result[p.name]; break
|
|
152
|
+
case 'time': result[p.name] = dayjs(record[p.name]).format('HH:mm:ss'); break
|
|
153
|
+
case 'date': result[p.name] = dayjs(record[p.name]).format('YYYY-MM-DD'); break
|
|
154
|
+
case 'datetime': result[p.name] = dayjs(record[p.name]).toISOString(); break
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
result = await this.sanitizeBody({ body: result, schema, partial: true, ignoreNull: true })
|
|
158
|
+
if (record._rel) result._rel = record._rel
|
|
159
|
+
return result
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (isEmpty(record)) return record
|
|
163
|
+
if (hidden.length > 0) record = omit(record, hidden)
|
|
164
|
+
if (!isArray(fields)) return await transform.call(this, { record, schema, hidden, forceNoHidden })
|
|
165
|
+
const fl = clone(fields)
|
|
166
|
+
if (!fl.includes('id')) fl.unshift('id')
|
|
167
|
+
if (record._rel) fl.push('_rel')
|
|
168
|
+
return pick(await transform.call(this, { record, schema, hidden, forceNoHidden }), fl)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
prepPagination = async (filter = {}, schema, options = {}) => {
|
|
172
|
+
const buildPageSkipLimit = (filter) => {
|
|
173
|
+
let limit = parseInt(filter.limit) || this.config.default.filter.limit
|
|
174
|
+
if (limit === -1) limit = this.config.default.filter.maxLimit
|
|
175
|
+
if (limit > this.config.default.filter.maxLimit) limit = this.config.default.filter.maxLimit
|
|
176
|
+
if (limit < 1) limit = 1
|
|
177
|
+
let page = parseInt(filter.page) || 1
|
|
178
|
+
if (page < 1) page = 1
|
|
179
|
+
let skip = (page - 1) * limit
|
|
180
|
+
if (filter.skip) {
|
|
181
|
+
skip = parseInt(filter.skip) || skip
|
|
182
|
+
page = undefined
|
|
183
|
+
}
|
|
184
|
+
if (skip < 0) skip = 0
|
|
185
|
+
return { page, skip, limit }
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const buildSort = (input, schema, allowSortUnindexed) => {
|
|
189
|
+
const { isEmpty, map, each, isPlainObject, isString, trim, keys } = this.lib._
|
|
190
|
+
let sort
|
|
191
|
+
if (schema && isEmpty(input)) {
|
|
192
|
+
const columns = map(schema.properties, 'name')
|
|
193
|
+
each(this.config.default.filter.sort, s => {
|
|
194
|
+
const [col] = s.split(':')
|
|
195
|
+
if (columns.includes(col)) {
|
|
196
|
+
input = s
|
|
197
|
+
return false
|
|
198
|
+
}
|
|
199
|
+
})
|
|
200
|
+
}
|
|
201
|
+
if (!isEmpty(input)) {
|
|
202
|
+
if (isPlainObject(input)) sort = input
|
|
203
|
+
else if (isString(input)) {
|
|
204
|
+
const item = {}
|
|
205
|
+
each(input.split('+'), text => {
|
|
206
|
+
let [col, dir] = map(trim(text).split(':'), i => trim(i))
|
|
207
|
+
dir = (dir ?? '').toUpperCase()
|
|
208
|
+
dir = dir === 'DESC' ? -1 : parseInt(dir) || 1
|
|
209
|
+
item[col] = dir / Math.abs(dir)
|
|
210
|
+
})
|
|
211
|
+
sort = item
|
|
212
|
+
}
|
|
213
|
+
if (schema) {
|
|
214
|
+
const items = keys(sort)
|
|
215
|
+
each(items, i => {
|
|
216
|
+
if (!schema.sortables.includes(i) && !allowSortUnindexed) throw this.error('sortOnUnindexedField%s%s', i, schema.name)
|
|
217
|
+
// if (schema.fullText.fields.includes(i)) throw this.error('Can\'t sort on full-text index: \'%s@%s\'', i, schema.name)
|
|
218
|
+
})
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return sort
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const { page, skip, limit } = buildPageSkipLimit.call(this, filter)
|
|
225
|
+
let sortInput = filter.sort
|
|
226
|
+
try {
|
|
227
|
+
sortInput = JSON.parse(sortInput)
|
|
228
|
+
} catch (err) {}
|
|
229
|
+
const sort = buildSort.call(this, sortInput, schema, options.allowSortUnindexed)
|
|
230
|
+
return { limit, page, skip, sort }
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
buildMatch = ({ input = '', schema, options }) => {
|
|
234
|
+
const { isPlainObject, trim } = this.lib._
|
|
235
|
+
const split = (value, schema) => {
|
|
236
|
+
let [field, val] = value.split(':').map(i => i.trim())
|
|
237
|
+
if (!val) {
|
|
238
|
+
val = field
|
|
239
|
+
field = '*'
|
|
240
|
+
}
|
|
241
|
+
return { field, value: val }
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
input = trim(input)
|
|
245
|
+
let items = {}
|
|
246
|
+
if (isPlainObject(input)) items = input
|
|
247
|
+
else if (input[0] === '{') items = JSON.parse(input)
|
|
248
|
+
else {
|
|
249
|
+
for (const item of input.split('+').map(i => i.trim())) {
|
|
250
|
+
const part = split(item, schema)
|
|
251
|
+
if (!items[part.field]) items[part.field] = []
|
|
252
|
+
items[part.field].push(...part.value.split(' ').filter(v => ![''].includes(v)))
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
const matcher = {}
|
|
256
|
+
for (const f of schema.fullText.fields) {
|
|
257
|
+
const value = []
|
|
258
|
+
if (typeof items[f] === 'string') items[f] = [items[f]]
|
|
259
|
+
if (Object.prototype.hasOwnProperty.call(items, f)) value.push(...items[f])
|
|
260
|
+
matcher[f] = value
|
|
261
|
+
}
|
|
262
|
+
if (Object.prototype.hasOwnProperty.call(items, '*')) matcher['*'] = items['*']
|
|
263
|
+
return matcher
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
buildQuery = async ({ filter, schema, options = {} } = {}) => {
|
|
267
|
+
const { trim, isString, isPlainObject } = this.lib._
|
|
268
|
+
let query = {}
|
|
269
|
+
if (isString(filter.query)) {
|
|
270
|
+
filter.oquery = filter.query
|
|
271
|
+
if (trim(filter.query).startsWith('{')) query = JSON.parse(filter.query)
|
|
272
|
+
else query = nql(filter.query).parse()
|
|
273
|
+
} else if (isPlainObject(filter.query)) query = filter.query
|
|
274
|
+
return query
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
validationErrorMessage = (err) => {
|
|
278
|
+
let text = err.message
|
|
279
|
+
if (err.details) {
|
|
280
|
+
text += ' -> '
|
|
281
|
+
text += this.app.bajo.join(err.details.map((d, idx) => {
|
|
282
|
+
return `${d.field}@${err.model}: ${d.error} (${d.value})`
|
|
283
|
+
}))
|
|
284
|
+
}
|
|
285
|
+
return text
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
getConnection = (name) => {
|
|
289
|
+
const { find } = this.lib._
|
|
290
|
+
return find(this.connections, { name })
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
getInfo = (name) => {
|
|
294
|
+
const { breakNsPath } = this.app.bajo
|
|
295
|
+
const { find, map } = this.lib._
|
|
296
|
+
const schema = this.getSchema(name)
|
|
297
|
+
const conn = this.getConnection(schema.connection)
|
|
298
|
+
const { ns, path: type } = breakNsPath(conn.type)
|
|
299
|
+
const driver = find(this.drivers, { type, ns, driver: conn.driver })
|
|
300
|
+
const instance = find(this.app[driver.ns].instances, { name: schema.connection })
|
|
301
|
+
const opts = conn.type === 'mssql' ? { includeTriggerModifications: true } : undefined
|
|
302
|
+
const returning = [map(schema.properties, 'name'), opts]
|
|
303
|
+
return { instance, driver, connection: conn, returning, schema }
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
getSchema = (input, cloned = true) => {
|
|
307
|
+
const { find, isPlainObject, cloneDeep } = this.lib._
|
|
308
|
+
let name = isPlainObject(input) ? input.name : input
|
|
309
|
+
name = this.app.bajo.pascalCase(name)
|
|
310
|
+
const schema = find(this.schemas, { name })
|
|
311
|
+
if (!schema) throw this.error('unknownModelSchema%s', name)
|
|
312
|
+
return cloned ? cloneDeep(schema) : schema
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
getMemdbStorage = (name, fields = []) => {
|
|
316
|
+
const { map, pick } = this.lib._
|
|
317
|
+
const all = this.memDb.storage[name] ?? []
|
|
318
|
+
if (fields.length === 0) return all
|
|
319
|
+
return map(all, item => pick(item, fields))
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export default factory
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
2
|
|
|
3
3
|
async function copyUploaded (name, id, options = {}) {
|
|
4
|
-
const { fs } = this.
|
|
4
|
+
const { fs } = this.lib
|
|
5
5
|
const { req, setField, setFile, mimeType, stats, silent = true } = options
|
|
6
6
|
name = this.attachmentPreCheck(name)
|
|
7
7
|
if (!name) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import mergeAttachmentInfo from '../../../lib/merge-attachment-info.js'
|
|
2
2
|
|
|
3
3
|
async function create (name, id, options = {}) {
|
|
4
|
-
const { fs } = this.
|
|
4
|
+
const { fs } = this.lib
|
|
5
5
|
name = this.attachmentPreCheck(name)
|
|
6
6
|
if (!name) return
|
|
7
7
|
const { source, field, file } = options
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import mergeAttachmentInfo from '../../../lib/merge-attachment-info.js'
|
|
2
2
|
|
|
3
3
|
async function find (name, id, options = {}) {
|
|
4
|
-
const { fastGlob, fs } = this.
|
|
4
|
+
const { fastGlob, fs } = this.lib
|
|
5
5
|
const { getPluginDataDir } = this.app.bajo
|
|
6
6
|
name = this.attachmentPreCheck(name)
|
|
7
7
|
if (!name) return
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
async function getPath (name, id, field, file, options = {}) {
|
|
2
2
|
const { pascalCase, getPluginDataDir } = this.app.bajo
|
|
3
|
-
const { fs } = this.
|
|
3
|
+
const { fs } = this.lib
|
|
4
4
|
const dir = `${getPluginDataDir(this.name)}/attachment/${pascalCase(name)}/${id}`
|
|
5
5
|
if (options.dirOnly) return dir
|
|
6
6
|
const path = field ? `${dir}/${field}/${file}` : `${dir}/${file}`
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
async function get (name, id, field, file, options = {}) {
|
|
2
2
|
name = this.attachmentPreCheck(name)
|
|
3
3
|
if (!name) return
|
|
4
|
-
const { find } = this.
|
|
4
|
+
const { find } = this.lib._
|
|
5
5
|
const all = await this.attachmentFind(name, id, options)
|
|
6
6
|
if (field === 'null') field = null
|
|
7
7
|
const data = find(all, { field, file })
|
|
@@ -5,7 +5,7 @@ import execFeatureHook from '../../../lib/exec-feature-hook.js'
|
|
|
5
5
|
async function create (name, inputs, options) {
|
|
6
6
|
const { generateId, runHook, isSet } = this.app.bajo
|
|
7
7
|
const { clearModel } = this.cache ?? {}
|
|
8
|
-
const { find } = this.
|
|
8
|
+
const { find } = this.lib._
|
|
9
9
|
options.dataOnly = options.dataOnly ?? true
|
|
10
10
|
options.truncateString = options.truncateString ?? true
|
|
11
11
|
const { noHook, noValidation } = options
|
|
@@ -2,7 +2,7 @@ import resolveMethod from '../../../lib/resolve-method.js'
|
|
|
2
2
|
|
|
3
3
|
async function clear (name, options = {}) {
|
|
4
4
|
const { runHook } = this.app.bajo
|
|
5
|
-
const { camelCase } = this.
|
|
5
|
+
const { camelCase } = this.lib._
|
|
6
6
|
|
|
7
7
|
await this.modelExists(name, true)
|
|
8
8
|
const { noHook } = options
|
|
@@ -2,7 +2,7 @@ import resolveMethod from '../../../lib/resolve-method.js'
|
|
|
2
2
|
|
|
3
3
|
async function create (name, options = {}) {
|
|
4
4
|
const { runHook } = this.app.bajo
|
|
5
|
-
const { camelCase } = this.
|
|
5
|
+
const { camelCase } = this.lib._
|
|
6
6
|
|
|
7
7
|
const { handler, schema } = await resolveMethod.call(this, name, 'model-create', options)
|
|
8
8
|
if (!options.noHook) {
|
|
@@ -2,7 +2,7 @@ import resolveMethod from '../../../lib/resolve-method.js'
|
|
|
2
2
|
|
|
3
3
|
async function drop (name, options = {}) {
|
|
4
4
|
const { runHook } = this.app.bajo
|
|
5
|
-
const { camelCase } = this.
|
|
5
|
+
const { camelCase } = this.lib._
|
|
6
6
|
const { handler, schema } = await resolveMethod.call(this, name, 'model-drop', options)
|
|
7
7
|
|
|
8
8
|
if (!options.noHook) {
|
|
@@ -5,7 +5,7 @@ const cache = {}
|
|
|
5
5
|
async function exists (name, thrown, options = {}) {
|
|
6
6
|
if (cache[name]) return cache[name]
|
|
7
7
|
const { runHook } = this.app.bajo
|
|
8
|
-
const { camelCase } = this.
|
|
8
|
+
const { camelCase } = this.lib._
|
|
9
9
|
const { handler, schema } = await resolveMethod.call(this, name, 'model-exists', options)
|
|
10
10
|
if (!options.noHook) {
|
|
11
11
|
await runHook(`${this.name}:beforeModelExists`, schema, options)
|
|
@@ -3,7 +3,7 @@ import resolveMethod from '../../../lib/resolve-method.js'
|
|
|
3
3
|
async function clear (name, opts = {}) {
|
|
4
4
|
const { runHook } = this.app.bajo
|
|
5
5
|
await this.modelExists(name, true)
|
|
6
|
-
const { cloneDeep, camelCase, omit } = this.
|
|
6
|
+
const { cloneDeep, camelCase, omit } = this.lib._
|
|
7
7
|
const options = cloneDeep(omit(opts, ['req', 'reply']))
|
|
8
8
|
options.req = opts.req
|
|
9
9
|
options.reply = opts.reply
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import resolveMethod from '../../../lib/resolve-method.js'
|
|
2
|
+
import execFeatureHook from '../../../lib/exec-feature-hook.js'
|
|
2
3
|
|
|
3
4
|
async function count (name, filter = {}, opts = {}) {
|
|
4
5
|
const { runHook } = this.app.bajo
|
|
5
6
|
const { get, set } = this.cache ?? {}
|
|
6
|
-
const { cloneDeep, camelCase, omit } = this.
|
|
7
|
+
const { cloneDeep, camelCase, omit } = this.lib._
|
|
8
|
+
delete opts.record
|
|
7
9
|
const options = cloneDeep(omit(opts, ['req', 'reply']))
|
|
8
10
|
options.req = opts.req
|
|
9
11
|
options.reply = opts.reply
|
|
10
12
|
options.dataOnly = options.dataOnly ?? true
|
|
11
|
-
let { dataOnly, noHook, noCache } = options
|
|
13
|
+
let { dataOnly, noHook, noCache, noFeatureHook } = options
|
|
12
14
|
options.dataOnly = false
|
|
13
15
|
await this.modelExists(name, true)
|
|
14
16
|
const { handler, schema, driver } = await resolveMethod.call(this, name, 'record-count', options)
|
|
@@ -20,20 +22,23 @@ async function count (name, filter = {}, opts = {}) {
|
|
|
20
22
|
await runHook(`${this.name}:beforeRecordCount`, name, filter, options)
|
|
21
23
|
await runHook(`${this.name}.${camelCase(name)}:beforeRecordCount`, filter, options)
|
|
22
24
|
}
|
|
23
|
-
if (
|
|
25
|
+
if (!noFeatureHook) await execFeatureHook.call(this, 'beforeCount', { schema, filter, options })
|
|
26
|
+
if (get && !noCache && !options.record) {
|
|
24
27
|
const cachedResult = await get({ model: name, filter, options })
|
|
25
28
|
if (cachedResult) {
|
|
26
29
|
cachedResult.cached = true
|
|
27
30
|
return dataOnly ? cachedResult.data : cachedResult
|
|
28
31
|
}
|
|
29
32
|
}
|
|
30
|
-
const
|
|
33
|
+
const record = options.record ?? (await handler.call(this.app[driver.ns], { schema, filter, options }))
|
|
34
|
+
delete options.record
|
|
31
35
|
if (!noHook) {
|
|
32
|
-
await runHook(`${this.name}.${camelCase(name)}:afterRecordCount`, filter, options,
|
|
33
|
-
await runHook(`${this.name}:afterRecordCount`, name, filter, options,
|
|
36
|
+
await runHook(`${this.name}.${camelCase(name)}:afterRecordCount`, filter, options, record)
|
|
37
|
+
await runHook(`${this.name}:afterRecordCount`, name, filter, options, record)
|
|
34
38
|
}
|
|
35
|
-
if (set && !noCache) await set({ model: name, filter, options,
|
|
36
|
-
|
|
39
|
+
if (set && !noCache) await set({ model: name, filter, options, record })
|
|
40
|
+
if (!noFeatureHook) await execFeatureHook.call(this, 'afterCount', { schema, filter, options, record })
|
|
41
|
+
return dataOnly ? record.data : record
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
export default count
|
|
@@ -9,7 +9,8 @@ import singleRelRows from '../../../lib/single-rel-rows.js'
|
|
|
9
9
|
async function create (name, input, opts = {}) {
|
|
10
10
|
const { generateId, runHook, isSet } = this.app.bajo
|
|
11
11
|
const { clearModel } = this.cache ?? {}
|
|
12
|
-
const { find, forOwn, cloneDeep, camelCase, omit, get, pick } = this.
|
|
12
|
+
const { find, forOwn, cloneDeep, camelCase, omit, get, pick } = this.lib._
|
|
13
|
+
delete opts.record
|
|
13
14
|
const options = cloneDeep(omit(opts, ['req', 'reply']))
|
|
14
15
|
options.req = opts.req
|
|
15
16
|
options.reply = opts.reply
|
|
@@ -37,10 +38,8 @@ async function create (name, input, opts = {}) {
|
|
|
37
38
|
}
|
|
38
39
|
} else if (['integer', 'smallint'].includes(idField.type) && !idField.autoInc) input.id = generateId('int')
|
|
39
40
|
}
|
|
40
|
-
if (!noFeatureHook) await execFeatureHook.call(this, 'beforeCreate', { schema, body })
|
|
41
41
|
if (!noValidation) body = await execValidation.call(this, { name, body, options })
|
|
42
42
|
if (isSet(body.id) && !noCheckUnique) await checkUnique.call(this, { schema, body })
|
|
43
|
-
let record = {}
|
|
44
43
|
const nbody = {}
|
|
45
44
|
forOwn(body, (v, k) => {
|
|
46
45
|
if (v === undefined) return undefined
|
|
@@ -49,20 +48,22 @@ async function create (name, input, opts = {}) {
|
|
|
49
48
|
if (options.truncateString && isSet(v) && ['string', 'text'].includes(prop.type)) v = v.slice(0, prop.maxLength)
|
|
50
49
|
nbody[k] = v
|
|
51
50
|
})
|
|
52
|
-
|
|
51
|
+
if (!noFeatureHook) await execFeatureHook.call(this, 'beforeCreate', { schema, body: nbody, options })
|
|
52
|
+
const record = options.record ?? (await handler.call(this.app[driver.ns], { schema, body: nbody, options }))
|
|
53
|
+
delete options.record
|
|
53
54
|
if (isSet(options.rels)) await singleRelRows.call(this, { schema, record: record.data, options })
|
|
54
55
|
if (options.req) {
|
|
55
56
|
if (options.req.file) await handleAttachmentUpload.call(this, { name: schema.name, id: body.id, body, options, action: 'create' })
|
|
56
57
|
if (options.req.flash && !options.noFlash) options.req.flash('notify', options.req.t('recordCreated'))
|
|
57
58
|
}
|
|
58
|
-
if (!noFeatureHook) await execFeatureHook.call(this, 'afterCreate', { schema, body, record })
|
|
59
59
|
if (!noHook) {
|
|
60
|
-
await runHook(`${this.name}.${camelCase(name)}:afterRecordCreate`,
|
|
61
|
-
await runHook(`${this.name}:afterRecordCreate`, name,
|
|
60
|
+
await runHook(`${this.name}.${camelCase(name)}:afterRecordCreate`, nbody, options, record)
|
|
61
|
+
await runHook(`${this.name}:afterRecordCreate`, name, nbody, options, record)
|
|
62
62
|
}
|
|
63
|
-
if (clearModel) await clearModel({ model: name, body, options, record })
|
|
63
|
+
if (clearModel) await clearModel({ model: name, body: nbody, options, record })
|
|
64
64
|
if (noResult) return
|
|
65
65
|
record.data = await this.pickRecord({ record: record.data, fields, schema, hidden, forceNoHidden })
|
|
66
|
+
if (!noFeatureHook) await execFeatureHook.call(this, 'afterCreate', { schema, body: nbody, options, record })
|
|
66
67
|
return dataOnly ? record.data : record
|
|
67
68
|
}
|
|
68
69
|
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import resolveMethod from '../../../lib/resolve-method.js'
|
|
2
2
|
import singleRelRows from '../../../lib/single-rel-rows.js'
|
|
3
|
+
import execFeatureHook from '../../../lib/exec-feature-hook.js'
|
|
3
4
|
|
|
4
5
|
async function findOne (name, filter = {}, opts = {}) {
|
|
5
6
|
const { runHook, isSet } = this.app.bajo
|
|
6
7
|
const { get, set } = this.cache ?? {}
|
|
7
|
-
const { cloneDeep, camelCase, omit } = this.
|
|
8
|
+
const { cloneDeep, camelCase, omit } = this.lib._
|
|
9
|
+
delete opts.record
|
|
8
10
|
const options = cloneDeep(omit(opts, ['req', 'reply']))
|
|
9
11
|
options.req = opts.req
|
|
10
12
|
options.reply = opts.reply
|
|
11
13
|
options.dataOnly = options.dataOnly ?? true
|
|
12
|
-
let { fields, dataOnly, noHook, noCache, hidden, forceNoHidden } = options
|
|
14
|
+
let { fields, dataOnly, noHook, noCache, noFeatureHook, hidden, forceNoHidden } = options
|
|
13
15
|
options.count = false
|
|
14
16
|
options.dataOnly = false
|
|
15
17
|
await this.modelExists(name, true)
|
|
@@ -23,15 +25,19 @@ async function findOne (name, filter = {}, opts = {}) {
|
|
|
23
25
|
await runHook(`${this.name}:beforeRecordFindOne`, name, filter, options)
|
|
24
26
|
await runHook(`${this.name}.${camelCase(name)}:beforeRecordFindOne`, filter, options)
|
|
25
27
|
}
|
|
26
|
-
if (
|
|
28
|
+
if (!noFeatureHook) await execFeatureHook.call(this, 'beforeFindOne', { schema, filter, options })
|
|
29
|
+
if (get && !noCache && !options.record) {
|
|
27
30
|
const cachedResult = await get({ model: name, filter, options })
|
|
28
31
|
if (cachedResult) {
|
|
29
32
|
cachedResult.cached = true
|
|
30
33
|
return dataOnly ? cachedResult.data : cachedResult
|
|
31
34
|
}
|
|
32
35
|
}
|
|
33
|
-
|
|
36
|
+
filter.limit = 1
|
|
37
|
+
const record = options.record ?? (await handler.call(this.app[driver.ns], { schema, filter, options }))
|
|
38
|
+
delete options.record
|
|
34
39
|
record.data = record.data[0]
|
|
40
|
+
|
|
35
41
|
if (isSet(options.rels)) await singleRelRows.call(this, { schema, record: record.data, options })
|
|
36
42
|
if (!noHook) {
|
|
37
43
|
await runHook(`${this.name}.${camelCase(name)}:afterRecordFindOne`, filter, options, record)
|
|
@@ -39,6 +45,7 @@ async function findOne (name, filter = {}, opts = {}) {
|
|
|
39
45
|
}
|
|
40
46
|
record.data = await this.pickRecord({ record: record.data, fields, schema, hidden, forceNoHidden })
|
|
41
47
|
if (set && !noCache) await set({ model: name, filter, options, record })
|
|
48
|
+
if (!noFeatureHook) await execFeatureHook.call(this, 'afterFindOne', { schema, filter, options, record })
|
|
42
49
|
return dataOnly ? record.data : record
|
|
43
50
|
}
|
|
44
51
|
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import resolveMethod from '../../../lib/resolve-method.js'
|
|
2
2
|
import multiRelRows from '../../../lib/multi-rel-rows.js'
|
|
3
|
+
import execFeatureHook from '../../../lib/exec-feature-hook.js'
|
|
3
4
|
|
|
4
5
|
async function find (name, filter = {}, opts = {}) {
|
|
5
6
|
const { runHook, isSet } = this.app.bajo
|
|
6
7
|
const { get, set } = this.cache ?? {}
|
|
7
|
-
const { cloneDeep, camelCase, omit } = this.
|
|
8
|
+
const { cloneDeep, camelCase, omit } = this.lib._
|
|
9
|
+
delete opts.records
|
|
8
10
|
const options = cloneDeep(omit(opts, ['req', 'reply']))
|
|
9
11
|
options.req = opts.req
|
|
10
12
|
options.reply = opts.reply
|
|
11
13
|
options.dataOnly = options.dataOnly ?? true
|
|
12
|
-
let { fields, dataOnly, noHook, noCache, hidden, forceNoHidden } = options
|
|
14
|
+
let { fields, dataOnly, noHook, noCache, noFeatureHook, hidden, forceNoHidden } = options
|
|
13
15
|
options.count = options.count ?? false
|
|
14
16
|
options.dataOnly = false
|
|
15
17
|
await this.modelExists(name, true)
|
|
@@ -22,14 +24,17 @@ async function find (name, filter = {}, opts = {}) {
|
|
|
22
24
|
await runHook(`${this.name}:beforeRecordFind`, name, filter, options)
|
|
23
25
|
await runHook(`${this.name}.${camelCase(name)}:beforeRecordFind`, filter, options)
|
|
24
26
|
}
|
|
25
|
-
if (
|
|
27
|
+
if (!noFeatureHook) await execFeatureHook.call(this, 'beforeFind', { schema, filter, options })
|
|
28
|
+
if (get && !noCache && !options.records) {
|
|
26
29
|
const cachedResult = await get({ model: name, filter, options })
|
|
27
30
|
if (cachedResult) {
|
|
28
31
|
cachedResult.cached = true
|
|
32
|
+
if (!noFeatureHook) await execFeatureHook.call(this, 'afterFind', { schema, filter, options, records: cachedResult })
|
|
29
33
|
return dataOnly ? cachedResult.data : cachedResult
|
|
30
34
|
}
|
|
31
35
|
}
|
|
32
|
-
const records = await handler.call(this.app[driver.ns], { schema, filter, options })
|
|
36
|
+
const records = options.records ?? (await handler.call(this.app[driver.ns], { schema, filter, options }))
|
|
37
|
+
delete options.records
|
|
33
38
|
if (isSet(options.rels)) await multiRelRows.call(this, { schema, records: records.data, options })
|
|
34
39
|
if (!noHook) {
|
|
35
40
|
await runHook(`${this.name}.${camelCase(name)}:afterRecordFind`, filter, options, records)
|
|
@@ -39,6 +44,7 @@ async function find (name, filter = {}, opts = {}) {
|
|
|
39
44
|
records.data[idx] = await this.pickRecord({ record: records.data[idx], fields, schema, hidden, forceNoHidden })
|
|
40
45
|
}
|
|
41
46
|
if (set && !noCache) await set({ model: name, filter, options, records })
|
|
47
|
+
if (!noFeatureHook) await execFeatureHook.call(this, 'afterFind', { schema, filter, options, records })
|
|
42
48
|
return dataOnly ? records.data : records
|
|
43
49
|
}
|
|
44
50
|
|