dobo 1.0.22 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dobo/feature/removed-at.js +85 -0
  2. package/lib/exec-feature-hook.js +3 -2
  3. package/package.json +1 -1
  4. package/plugin/factory.js +317 -0
  5. package/{bajo → plugin}/method/record/count.js +12 -7
  6. package/{bajo → plugin}/method/record/create.js +8 -7
  7. package/{bajo → plugin}/method/record/find-one.js +10 -3
  8. package/{bajo → plugin}/method/record/find.js +9 -3
  9. package/{bajo → plugin}/method/record/get.js +9 -3
  10. package/{bajo → plugin}/method/record/remove.js +8 -3
  11. package/{bajo → plugin}/method/record/update.js +5 -3
  12. package/waibuMpa/route/attachment/@model/@id/@field/@file.js +8 -1
  13. package/bajo/.alias +0 -1
  14. package/bajo/config.json +0 -36
  15. package/bajo/init.js +0 -29
  16. package/bajo/method/aggregate-types.js +0 -1
  17. package/bajo/method/build-match.js +0 -34
  18. package/bajo/method/build-query.js +0 -14
  19. package/bajo/method/get-connection.js +0 -6
  20. package/bajo/method/get-info.js +0 -14
  21. package/bajo/method/get-schema.js +0 -10
  22. package/bajo/method/pick-record.js +0 -36
  23. package/bajo/method/prep-pagination.js +0 -63
  24. package/bajo/method/prop-type.js +0 -43
  25. package/bajo/method/validation-error-message.js +0 -12
  26. /package/{bajo → plugin}/method/attachment/copy-uploaded.js +0 -0
  27. /package/{bajo → plugin}/method/attachment/create.js +0 -0
  28. /package/{bajo → plugin}/method/attachment/find.js +0 -0
  29. /package/{bajo → plugin}/method/attachment/get-path.js +0 -0
  30. /package/{bajo → plugin}/method/attachment/get.js +0 -0
  31. /package/{bajo → plugin}/method/attachment/pre-check.js +0 -0
  32. /package/{bajo → plugin}/method/attachment/remove.js +0 -0
  33. /package/{bajo → plugin}/method/attachment/update.js +0 -0
  34. /package/{bajo → plugin}/method/bulk/create.js +0 -0
  35. /package/{bajo → plugin}/method/model/clear.js +0 -0
  36. /package/{bajo → plugin}/method/model/create.js +0 -0
  37. /package/{bajo → plugin}/method/model/drop.js +0 -0
  38. /package/{bajo → plugin}/method/model/exists.js +0 -0
  39. /package/{bajo → plugin}/method/record/clear.js +0 -0
  40. /package/{bajo → plugin}/method/record/find-all.js +0 -0
  41. /package/{bajo → plugin}/method/record/upsert.js +0 -0
  42. /package/{bajo → plugin}/method/sanitize/body.js +0 -0
  43. /package/{bajo → plugin}/method/sanitize/date.js +0 -0
  44. /package/{bajo → plugin}/method/sanitize/id.js +0 -0
  45. /package/{bajo → plugin}/method/stat/aggregate.js +0 -0
  46. /package/{bajo → plugin}/method/stat/histogram.js +0 -0
  47. /package/{bajo → plugin}/method/validate.js +0 -0
  48. /package/{bajo → plugin}/start.js +0 -0
@@ -0,0 +1,85 @@
1
+ async function beforeFind ({ filter = {}, options }, opts) {
2
+ filter.query = filter.query ?? {}
3
+ const { isEmpty, set } = this.app.bajo.lib._
4
+ const q = { $and: [] }
5
+ if (!isEmpty(filter.query)) {
6
+ if (filter.query.$and) q.$and.push(...filter.query.$and)
7
+ else q.$and.push(filter.query)
8
+ }
9
+ q.$and.push(set({}, opts.fieldName, null))
10
+ filter.query = q
11
+ }
12
+
13
+ async function afterFind ({ records }, opts) {
14
+ for (const rec of records.data) {
15
+ delete rec[opts.fieldName]
16
+ }
17
+ }
18
+
19
+ async function afterGet ({ schema, id, record }, opts) {
20
+ const { isEmpty } = this.app.bajo.lib._
21
+ if (!isEmpty(record.data[opts.fieldName])) throw this.error('recordNotFound%s%s', id, schema.name, { statusCode: 404 })
22
+ delete record.data[opts.fieldName]
23
+ }
24
+
25
+ async function beforeCreate ({ body }, opts) {
26
+ delete body[opts.fieldName]
27
+ }
28
+
29
+ async function afterCreate ({ record }, opts) {
30
+ delete record.data[opts.fieldName]
31
+ if (record.oldData) delete record.oldData[opts.fieldName]
32
+ }
33
+
34
+ async function removedAt (opts = {}) {
35
+ opts.fieldName = opts.fieldName ?? 'removedAt'
36
+ return {
37
+ properties: {
38
+ name: opts.fieldName,
39
+ type: 'datetime',
40
+ index: true
41
+ },
42
+ hook: {
43
+ beforeFind: async function ({ filter, options }) {
44
+ await beforeFind.call(this, { filter, options }, opts)
45
+ },
46
+ beforeFindOne: async function ({ filter, options }) {
47
+ await beforeFind.call(this, { filter, options }, opts)
48
+ },
49
+ afterFind: async function ({ records }) {
50
+ await afterFind.call(this, { records }, opts)
51
+ },
52
+ afterFindOne: async function ({ record }) {
53
+ await afterGet.call(this, { record }, opts)
54
+ },
55
+ afterGet: async function ({ record }) {
56
+ await afterGet.call(this, { record }, opts)
57
+ },
58
+ beforeCreate: async function ({ body }) {
59
+ await beforeCreate.call(this, { body }, opts)
60
+ },
61
+ afterCreate: async function ({ record }) {
62
+ await afterCreate.call(this, { record }, opts)
63
+ },
64
+ beforeUpdate: async function ({ schema, id, body, options }) {
65
+ await beforeCreate.call(this, { body }, opts)
66
+ },
67
+ afterUpdate: async function ({ record }) {
68
+ await afterCreate.call(this, { record }, opts)
69
+ },
70
+ beforeRemove: async function ({ schema, id, options }) {
71
+ const { recordUpdate, recordGet } = this.app.dobo
72
+ await recordGet(schema.name, id, options)
73
+ const { set } = this.app.bajo.lib._
74
+ const body = set({}, opts.fieldName, new Date())
75
+ const record = await recordUpdate(schema.name, id, body, { dataOnly: false, noValidation: true, noFeatureHook: true })
76
+ options.record = { oldData: record.oldData }
77
+ },
78
+ afterRemove: async function ({ record }) {
79
+ delete record.oldData[opts.fieldName]
80
+ }
81
+ }
82
+ }
83
+ }
84
+
85
+ export default removedAt
@@ -1,11 +1,12 @@
1
- async function execFeatureHook (name, { schema, body } = {}) {
1
+ async function execFeatureHook (name, params = {}) {
2
2
  const { get } = this.app.bajo.lib._
3
+ const { schema } = params
3
4
  for (const f of schema.feature) {
4
5
  const fn = get(this.feature, f.name)
5
6
  if (!fn) continue
6
7
  const input = await fn.call(this, f)
7
8
  const hook = get(input, 'hook.' + name)
8
- if (hook) await hook.call(this, { schema, body })
9
+ if (hook) await hook.call(this, params)
9
10
  }
10
11
  }
11
12
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dobo",
3
- "version": "1.0.22",
3
+ "version": "1.1.1",
4
4
  "description": "Database ORM/ODM for Bajo Framework",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -0,0 +1,317 @@
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.app.bajo.lib
99
+ const checkType = async (item, items) => {
100
+ const { filter } = this.app.bajo.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.app.bajo.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.app.bajo.lib._
137
+ const { dayjs } = this.app.bajo.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.app.bajo.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.app.bajo.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.app.bajo.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.app.bajo.lib._
290
+ return find(this.connections, { name })
291
+ }
292
+
293
+ getInfo = (name) => {
294
+ const { breakNsPath } = this.app.bajo
295
+ const { find, map } = this.app.bajo.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.app.bajo.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
+ }
316
+
317
+ export default factory
@@ -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
7
  const { cloneDeep, camelCase, omit } = this.app.bajo.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 (get && !noCache) {
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 count = await handler.call(this.app[driver.ns], { schema, filter, options })
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, count)
33
- await runHook(`${this.name}:afterRecordCount`, name, filter, options, count)
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, count })
36
- return dataOnly ? count.data : count
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
@@ -10,6 +10,7 @@ async function create (name, input, opts = {}) {
10
10
  const { generateId, runHook, isSet } = this.app.bajo
11
11
  const { clearModel } = this.cache ?? {}
12
12
  const { find, forOwn, cloneDeep, camelCase, omit, get, pick } = this.app.bajo.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
- record = await handler.call(this.app[driver.ns], { schema, body: nbody, options })
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`, body, options, record)
61
- await runHook(`${this.name}:afterRecordCreate`, name, body, options, record)
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
8
  const { cloneDeep, camelCase, omit } = this.app.bajo.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 (get && !noCache) {
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
- const record = await handler.call(this.app[driver.ns], { schema, filter, options })
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
8
  const { cloneDeep, camelCase, omit } = this.app.bajo.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 (get && !noCache) {
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
 
@@ -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 get (name, id, opts = {}) {
5
6
  const { runHook, isSet } = this.app.bajo
6
7
  const { get, set } = this.cache ?? {}
7
8
  const { cloneDeep, camelCase, omit } = this.app.bajo.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
  await this.modelExists(name, true)
14
16
  const { handler, schema, driver } = await resolveMethod.call(this, name, 'record-get', options)
15
17
  if (!schema.cacheable) noCache = true
@@ -19,14 +21,17 @@ async function get (name, id, opts = {}) {
19
21
  await runHook(`${this.name}:beforeRecordGet`, name, id, options)
20
22
  await runHook(`${this.name}.${camelCase(name)}:beforeRecordGet`, id, options)
21
23
  }
22
- if (get && !noCache) {
24
+ if (!noFeatureHook) await execFeatureHook.call(this, 'beforeGet', { schema, id, options })
25
+ if (get && !noCache && !options.record) {
23
26
  const cachedResult = await get({ model: name, id, options })
24
27
  if (cachedResult) {
25
28
  cachedResult.cached = true
29
+ if (!noFeatureHook) await execFeatureHook.call(this, 'afterGet', { schema, id, options, record: cachedResult })
26
30
  return dataOnly ? cachedResult.data : cachedResult
27
31
  }
28
32
  }
29
- const record = await handler.call(this.app[driver.ns], { schema, id, options })
33
+ const record = options.record ?? (await handler.call(this.app[driver.ns], { schema, id, options }))
34
+ delete options.record
30
35
  if (isSet(options.rels)) await singleRelRows.call(this, { schema, record: record.data, options })
31
36
  if (!noHook) {
32
37
  await runHook(`${this.name}.${camelCase(name)}:afterRecordGet`, id, options, record)
@@ -35,6 +40,7 @@ async function get (name, id, opts = {}) {
35
40
  record.data = await this.pickRecord({ record: record.data, fields, schema, hidden, forceNoHidden })
36
41
 
37
42
  if (set && !noCache) await set({ model: name, id, options, record })
43
+ if (!noFeatureHook) await execFeatureHook.call(this, 'afterGet', { schema, id, options, record })
38
44
  return dataOnly ? record.data : record
39
45
  }
40
46
 
@@ -1,15 +1,17 @@
1
1
  import resolveMethod from '../../../lib/resolve-method.js'
2
2
  import handleAttachmentUpload from '../../../lib/handle-attachment-upload.js'
3
+ import execFeatureHook from '../../../lib/exec-feature-hook.js'
3
4
 
4
5
  async function remove (name, id, opts = {}) {
5
6
  const { runHook } = this.app.bajo
6
7
  const { clearModel } = this.cache ?? {}
7
8
  const { cloneDeep, camelCase, omit } = this.app.bajo.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
- const { fields, dataOnly, noHook, noResult, hidden, forceNoHidden } = options
14
+ const { fields, dataOnly, noHook, noResult, noFeatureHook, hidden, forceNoHidden } = options
13
15
  options.dataOnly = false
14
16
  await this.modelExists(name, true)
15
17
  const { handler, schema, driver } = await resolveMethod.call(this, name, 'record-remove', options)
@@ -18,7 +20,9 @@ async function remove (name, id, opts = {}) {
18
20
  await runHook(`${this.name}:beforeRecordRemove`, name, id, options)
19
21
  await runHook(`${this.name}.${camelCase(name)}:beforeRecordRemove`, id, options)
20
22
  }
21
- const record = await handler.call(this.app[driver.ns], { schema, id, options })
23
+ if (!noFeatureHook) await execFeatureHook.call(this, 'beforeRemove', { schema, id, options })
24
+ const record = options.record ?? (await handler.call(this.app[driver.ns], { schema, id, options }))
25
+ delete options.record
22
26
  if (options.req) {
23
27
  if (options.req.file) await handleAttachmentUpload.call(this, { name: schema.name, id, options, action: 'remove' })
24
28
  if (options.req.flash && !options.noFlash) options.req.flash('notify', options.req.t('recordRemoved'))
@@ -29,7 +33,8 @@ async function remove (name, id, opts = {}) {
29
33
  }
30
34
  if (clearModel) await clearModel({ model: name, id, options, record })
31
35
  if (noResult) return
32
- record.oldData = await this.pickRecord({ record: record.oldData, fields, schema, hidden, forceNoHidden })
36
+ record.oldData = options.record ? options.record.oldData : (await this.pickRecord({ record: record.oldData, fields, schema, hidden, forceNoHidden }))
37
+ if (!noFeatureHook) await execFeatureHook.call(this, 'afterRemove', { schema, id, options, record })
33
38
  return dataOnly ? record.oldData : record
34
39
  }
35
40
 
@@ -9,6 +9,7 @@ async function update (name, id, input, opts = {}) {
9
9
  const { runHook, isSet } = this.app.bajo
10
10
  const { clearModel } = this.cache ?? {}
11
11
  const { forOwn, find, cloneDeep, camelCase, omit, get } = this.app.bajo.lib._
12
+ delete opts.record
12
13
  const options = cloneDeep(omit(opts, ['req', 'reply']))
13
14
  options.req = opts.req
14
15
  options.reply = opts.reply
@@ -27,7 +28,6 @@ async function update (name, id, input, opts = {}) {
27
28
  await runHook(`${this.name}:beforeRecordUpdate`, name, id, body, options)
28
29
  await runHook(`${this.name}.${camelCase(name)}:beforeRecordUpdate`, id, body, options)
29
30
  }
30
- if (!noFeatureHook) await execFeatureHook.call(this, 'beforeUpdate', { schema, body })
31
31
  if (!noValidation) body = await execValidation.call(this, { name, body, options, partial })
32
32
  if (!noCheckUnique) await checkUnique.call(this, { schema, body, id })
33
33
  const nbody = {}
@@ -39,13 +39,14 @@ async function update (name, id, input, opts = {}) {
39
39
  nbody[k] = v
40
40
  })
41
41
  delete nbody.id
42
- const record = await handler.call(this.app[driver.ns], { schema, id, body: nbody, options })
42
+ if (!noFeatureHook) await execFeatureHook.call(this, 'beforeUpdate', { schema, body: nbody, options })
43
+ const record = options.record ?? (await handler.call(this.app[driver.ns], { schema, id, body: nbody, options }))
44
+ delete options.record
43
45
  if (isSet(options.rels)) await singleRelRows.call(this, { schema, record: record.data, options })
44
46
  if (options.req) {
45
47
  if (options.req.file) await handleAttachmentUpload.call(this, { name: schema.name, id, body, options, action: 'update' })
46
48
  if (options.req.flash && !options.noFlash) options.req.flash('notify', options.req.t('recordUpdated'))
47
49
  }
48
- if (!noFeatureHook) await execFeatureHook.call(this, 'afterUpdate', { schema, body: nbody, record })
49
50
  if (!noHook) {
50
51
  await runHook(`${this.name}.${camelCase(name)}:afterRecordUpdate`, id, nbody, options, record)
51
52
  await runHook(`${this.name}:afterRecordUpdate`, name, id, nbody, options, record)
@@ -54,6 +55,7 @@ async function update (name, id, input, opts = {}) {
54
55
  if (noResult) return
55
56
  record.oldData = await this.pickRecord({ record: record.oldData, fields, schema, hidden, forceNoHidden })
56
57
  record.data = await this.pickRecord({ record: record.data, fields, schema, hidden, forceNoHidden })
58
+ if (!noFeatureHook) await execFeatureHook.call(this, 'afterUpdate', { schema, body: nbody, record })
57
59
  return dataOnly ? record.data : record
58
60
  }
59
61
 
@@ -1,12 +1,19 @@
1
1
  import path from 'path'
2
2
 
3
3
  async function attachment (req, reply) {
4
+ const { isString } = this.app.bajo.lib._
4
5
  const { importPkg, getPluginDataDir, pascalCase } = this.app.bajo
6
+ const { routePath } = this.app.waibu
5
7
  const mime = await importPkg('waibu:mime')
6
8
  const { fs } = this.app.bajo.lib
7
9
  const file = `${getPluginDataDir('dobo')}/attachment/${pascalCase(req.params.model)}/${req.params.id}/${req.params.field}/${req.params.file}`
8
- if (!fs.existsSync(file)) throw this.error('_notFound', { noView: true })
9
10
  const mimeType = mime.getType(path.extname(file))
11
+ if (!fs.existsSync(file)) {
12
+ if (!req.query.notfound) throw this.error('_notFound', { noView: true })
13
+ const [, ext] = mimeType.split('/')
14
+ const replacer = isString(req.query.notfound) ? req.query.notfound : `waibuStatic.asset:/not-found.${ext}`
15
+ return reply.redirectTo(routePath(replacer))
16
+ }
10
17
  reply.header('Content-Type', mimeType)
11
18
  const stream = fs.createReadStream(file)
12
19
  reply.send(stream)
package/bajo/.alias DELETED
@@ -1 +0,0 @@
1
- db
package/bajo/config.json DELETED
@@ -1,36 +0,0 @@
1
- {
2
- "connections": [],
3
- "mergeProps": ["connections"],
4
- "validationParams": {
5
- "abortEarly": false,
6
- "convert": false,
7
- "allowUnknown": true
8
- },
9
- "default": {
10
- "property": {
11
- "text": {
12
- "kind": "text"
13
- },
14
- "string": {
15
- "length": 50
16
- }
17
- },
18
- "filter": {
19
- "limit": 25,
20
- "maxLimit": 200,
21
- "sort": ["dt:-1", "updatedAt:-1", "updated_at:-1", "createdAt:-1", "createdAt:-1", "ts:-1", "username", "name"]
22
- },
23
- "idField": {
24
- "type": "string",
25
- "maxLength": 50,
26
- "required": true,
27
- "index": { "type": "primary" }
28
- }
29
- },
30
- "memDb": {
31
- "createDefConnAtStart": true,
32
- "persistence": {
33
- "syncPeriod": 1
34
- }
35
- }
36
- }
package/bajo/init.js DELETED
@@ -1,29 +0,0 @@
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
-
6
- async function checkType (item, items) {
7
- const { filter } = this.app.bajo.lib._
8
- const existing = filter(items, { type: 'dobo:memory' })
9
- if (existing.length > 1) this.fatal('onlyOneConnType%s', item.type)
10
- }
11
-
12
- async function init () {
13
- const { buildCollections } = this.app.bajo
14
- const { fs } = this.app.bajo.lib
15
- fs.ensureDirSync(`${this.dir.data}/attachment`)
16
- await collectDrivers.call(this)
17
- if (this.config.memDb.createDefConnAtStart) {
18
- this.config.connections.push({
19
- type: 'dobo:memory',
20
- name: 'memory'
21
- })
22
- }
23
- this.connections = await buildCollections({ ns: this.name, container: 'connections', handler: collectConnections, dupChecks: ['name', checkType] })
24
- if (this.connections.length === 0) this.log.warn('notFound%s', this.print.write('connection'))
25
- await collectFeature.call(this)
26
- await collectSchemas.call(this)
27
- }
28
-
29
- export default init
@@ -1 +0,0 @@
1
- export default ['count', 'avg', 'min', 'max', 'sum']
@@ -1,34 +0,0 @@
1
- function split (value, schema) {
2
- let [field, val] = value.split(':').map(i => i.trim())
3
- if (!val) {
4
- val = field
5
- field = '*'
6
- }
7
- return { field, value: val }
8
- }
9
-
10
- function buildMatch ({ input = '', schema, options }) {
11
- const { isPlainObject, trim } = this.app.bajo.lib._
12
- input = trim(input)
13
- let items = {}
14
- if (isPlainObject(input)) items = input
15
- else if (input[0] === '{') items = JSON.parse(input)
16
- else {
17
- for (const item of input.split('+').map(i => i.trim())) {
18
- const part = split.call(this, item, schema)
19
- if (!items[part.field]) items[part.field] = []
20
- items[part.field].push(...part.value.split(' ').filter(v => ![''].includes(v)))
21
- }
22
- }
23
- const matcher = {}
24
- for (const f of schema.fullText.fields) {
25
- const value = []
26
- if (typeof items[f] === 'string') items[f] = [items[f]]
27
- if (Object.prototype.hasOwnProperty.call(items, f)) value.push(...items[f])
28
- matcher[f] = value
29
- }
30
- if (Object.prototype.hasOwnProperty.call(items, '*')) matcher['*'] = items['*']
31
- return matcher
32
- }
33
-
34
- export default buildMatch
@@ -1,14 +0,0 @@
1
- import nql from '@tryghost/nql'
2
-
3
- async function buildQuery ({ filter, schema, options = {} } = {}) {
4
- const { trim, isString, isPlainObject } = this.app.bajo.lib._
5
- let query = {}
6
- if (isString(filter.query)) {
7
- filter.oquery = filter.query
8
- if (trim(filter.query).startsWith('{')) query = JSON.parse(filter.query)
9
- else query = nql(filter.query).parse()
10
- } else if (isPlainObject(filter.query)) query = filter.query
11
- return query
12
- }
13
-
14
- export default buildQuery
@@ -1,6 +0,0 @@
1
- function getConnection (name) {
2
- const { find } = this.app.bajo.lib._
3
- return find(this.connections, { name })
4
- }
5
-
6
- export default getConnection
@@ -1,14 +0,0 @@
1
- function getInfo (name) {
2
- const { breakNsPath } = this.app.bajo
3
- const { find, map } = this.app.bajo.lib._
4
- const schema = this.getSchema(name)
5
- const conn = this.getConnection(schema.connection)
6
- const { ns, path: type } = breakNsPath(conn.type)
7
- const driver = find(this.drivers, { type, ns, driver: conn.driver })
8
- const instance = find(this.app[driver.ns].instances, { name: schema.connection })
9
- const opts = conn.type === 'mssql' ? { includeTriggerModifications: true } : undefined
10
- const returning = [map(schema.properties, 'name'), opts]
11
- return { instance, driver, connection: conn, returning, schema }
12
- }
13
-
14
- export default getInfo
@@ -1,10 +0,0 @@
1
- function getSchema (input, cloned = true) {
2
- const { find, isPlainObject, cloneDeep } = this.app.bajo.lib._
3
- let name = isPlainObject(input) ? input.name : input
4
- name = this.app.bajo.pascalCase(name)
5
- const schema = find(this.schemas, { name })
6
- if (!schema) throw this.error('unknownModelSchema%s', name)
7
- return cloned ? cloneDeep(schema) : schema
8
- }
9
-
10
- export default getSchema
@@ -1,36 +0,0 @@
1
- async function transform ({ record, schema, hidden = [], forceNoHidden } = {}) {
2
- const { dayjs } = this.app.bajo.lib
3
- if (record._id) {
4
- record.id = record._id
5
- delete record._id
6
- }
7
- const defHidden = [...schema.hidden, ...hidden]
8
- let result = {}
9
- for (const p of schema.properties) {
10
- if (!forceNoHidden && defHidden.includes(p.name)) continue
11
- result[p.name] = record[p.name] ?? null
12
- if (record[p.name] === null) continue
13
- switch (p.type) {
14
- case 'boolean': result[p.name] = !!result[p.name]; break
15
- case 'time': result[p.name] = dayjs(record[p.name]).format('HH:mm:ss'); break
16
- case 'date': result[p.name] = dayjs(record[p.name]).format('YYYY-MM-DD'); break
17
- case 'datetime': result[p.name] = dayjs(record[p.name]).toISOString(); break
18
- }
19
- }
20
- result = await this.sanitizeBody({ body: result, schema, partial: true, ignoreNull: true })
21
- if (record._rel) result._rel = record._rel
22
- return result
23
- }
24
-
25
- async function pickRecord ({ record, fields, schema = {}, hidden = [], forceNoHidden } = {}) {
26
- const { isArray, pick, clone, isEmpty, omit } = this.app.bajo.lib._
27
- if (isEmpty(record)) return record
28
- if (hidden.length > 0) record = omit(record, hidden)
29
- if (!isArray(fields)) return await transform.call(this, { record, schema, hidden, forceNoHidden })
30
- const fl = clone(fields)
31
- if (!fl.includes('id')) fl.unshift('id')
32
- if (record._rel) fl.push('_rel')
33
- return pick(await transform.call(this, { record, schema, hidden, forceNoHidden }), fl)
34
- }
35
-
36
- export default pickRecord
@@ -1,63 +0,0 @@
1
- function buildPageSkipLimit (filter) {
2
- let limit = parseInt(filter.limit) || this.config.default.filter.limit
3
- if (limit === -1) limit = this.config.default.filter.maxLimit
4
- if (limit > this.config.default.filter.maxLimit) limit = this.config.default.filter.maxLimit
5
- if (limit < 1) limit = 1
6
- let page = parseInt(filter.page) || 1
7
- if (page < 1) page = 1
8
- let skip = (page - 1) * limit
9
- if (filter.skip) {
10
- skip = parseInt(filter.skip) || skip
11
- page = undefined
12
- }
13
- if (skip < 0) skip = 0
14
- return { page, skip, limit }
15
- }
16
-
17
- function buildSort (input, schema, allowSortUnindexed) {
18
- const { isEmpty, map, each, isPlainObject, isString, trim, keys } = this.app.bajo.lib._
19
- let sort
20
- if (schema && isEmpty(input)) {
21
- const columns = map(schema.properties, 'name')
22
- each(this.config.default.filter.sort, s => {
23
- const [col] = s.split(':')
24
- if (columns.includes(col)) {
25
- input = s
26
- return false
27
- }
28
- })
29
- }
30
- if (!isEmpty(input)) {
31
- if (isPlainObject(input)) sort = input
32
- else if (isString(input)) {
33
- const item = {}
34
- each(input.split('+'), text => {
35
- let [col, dir] = map(trim(text).split(':'), i => trim(i))
36
- dir = (dir ?? '').toUpperCase()
37
- dir = dir === 'DESC' ? -1 : parseInt(dir) || 1
38
- item[col] = dir / Math.abs(dir)
39
- })
40
- sort = item
41
- }
42
- if (schema) {
43
- const items = keys(sort)
44
- each(items, i => {
45
- if (!schema.sortables.includes(i) && !allowSortUnindexed) throw this.error('sortOnUnindexedField%s%s', i, schema.name)
46
- // if (schema.fullText.fields.includes(i)) throw this.error('Can\'t sort on full-text index: \'%s@%s\'', i, schema.name)
47
- })
48
- }
49
- }
50
- return sort
51
- }
52
-
53
- async function prepPagination (filter = {}, schema, options = {}) {
54
- const { page, skip, limit } = buildPageSkipLimit.call(this, filter)
55
- let sortInput = filter.sort
56
- try {
57
- sortInput = JSON.parse(sortInput)
58
- } catch (err) {}
59
- const sort = buildSort.call(this, sortInput, schema, options.allowSortUnindexed)
60
- return { limit, page, skip, sort }
61
- }
62
-
63
- export default prepPagination
@@ -1,43 +0,0 @@
1
- const propType = {
2
- integer: {
3
- validator: 'number'
4
- },
5
- smallint: {
6
- validator: 'number'
7
- },
8
- text: {
9
- validator: 'string',
10
- kind: 'text',
11
- choices: ['text', 'mediumtext', 'longtext']
12
- },
13
- string: {
14
- validator: 'string',
15
- maxLength: 255,
16
- minLength: 0
17
- },
18
- float: {
19
- validator: 'number'
20
- },
21
- double: {
22
- validator: 'number'
23
- },
24
- boolean: {
25
- validator: 'boolean'
26
- },
27
- date: {
28
- validator: 'date'
29
- },
30
- datetime: {
31
- validator: 'date'
32
- },
33
- time: {
34
- validator: 'date'
35
- },
36
- timestamp: {
37
- validator: 'timestamp'
38
- },
39
- object: {},
40
- array: {}
41
- }
42
-
43
- export default propType
@@ -1,12 +0,0 @@
1
- function validationErrorMessage (err) {
2
- let text = err.message
3
- if (err.details) {
4
- text += ' -> '
5
- text += this.app.bajo.join(err.details.map((d, idx) => {
6
- return `${d.field}@${err.model}: ${d.error} (${d.value})`
7
- }))
8
- }
9
- return text
10
- }
11
-
12
- export default validationErrorMessage
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes