dobo 1.0.2 → 1.0.3

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 (71) hide show
  1. package/bajo/.alias +1 -0
  2. package/bajo/config.json +12 -2
  3. package/bajo/init.js +14 -3
  4. package/bajo/method/attachment/create.js +1 -1
  5. package/bajo/method/attachment/get-path.js +1 -1
  6. package/bajo/method/attachment/get.js +1 -1
  7. package/bajo/method/attachment/remove.js +1 -1
  8. package/bajo/method/build-query.js +1 -0
  9. package/bajo/method/bulk/create.js +6 -6
  10. package/bajo/method/get-connection.js +6 -0
  11. package/bajo/method/get-info.js +2 -2
  12. package/bajo/method/get-schema.js +2 -2
  13. package/bajo/method/model/clear.js +7 -5
  14. package/bajo/method/model/create.js +10 -2
  15. package/bajo/method/model/drop.js +10 -2
  16. package/bajo/method/model/exists.js +9 -2
  17. package/bajo/method/pick-record.js +14 -8
  18. package/bajo/method/prep-pagination.js +5 -9
  19. package/bajo/method/record/clear.js +8 -7
  20. package/bajo/method/record/create.js +26 -28
  21. package/bajo/method/record/find-one.js +10 -9
  22. package/bajo/method/record/find.js +11 -9
  23. package/bajo/method/record/get.js +10 -9
  24. package/bajo/method/record/remove.js +13 -12
  25. package/bajo/method/record/update.js +22 -25
  26. package/bajo/method/record/upsert.js +4 -3
  27. package/bajo/method/sanitize/body.js +8 -9
  28. package/bajo/method/stat/aggregate.js +6 -6
  29. package/bajo/method/stat/histogram.js +5 -5
  30. package/bajo/method/validate.js +23 -20
  31. package/bajo/start.js +6 -2
  32. package/bajoCli/applet/connection.js +1 -1
  33. package/bajoCli/applet/lib/post-process.js +13 -13
  34. package/bajoCli/applet/model-clear.js +2 -2
  35. package/bajoCli/applet/model-rebuild.js +12 -9
  36. package/bajoCli/applet/record-create.js +2 -2
  37. package/bajoCli/applet/record-find.js +2 -2
  38. package/bajoCli/applet/record-get.js +2 -2
  39. package/bajoCli/applet/record-remove.js +2 -2
  40. package/bajoCli/applet/record-update.js +2 -2
  41. package/bajoCli/applet/schema.js +1 -1
  42. package/bajoCli/applet/stat-count.js +2 -2
  43. package/bajoI18N/resource/en-US.json +28 -27
  44. package/bajoI18N/resource/id.json +60 -27
  45. package/lib/add-fixtures.js +4 -4
  46. package/lib/check-unique.js +2 -2
  47. package/lib/collect-connections.js +3 -4
  48. package/lib/collect-drivers.js +11 -3
  49. package/lib/collect-feature.js +6 -5
  50. package/lib/collect-schemas.js +9 -7
  51. package/lib/exec-validation.js +8 -14
  52. package/lib/generic-prop-sanitizer.js +1 -1
  53. package/lib/mem-db/conn-sanitizer.js +8 -0
  54. package/lib/mem-db/instantiate.js +41 -0
  55. package/lib/mem-db/method/model/clear.js +6 -0
  56. package/lib/mem-db/method/model/create.js +5 -0
  57. package/lib/mem-db/method/model/drop.js +5 -0
  58. package/lib/mem-db/method/model/exists.js +5 -0
  59. package/lib/mem-db/method/record/create.js +12 -0
  60. package/lib/mem-db/method/record/find.js +20 -0
  61. package/lib/mem-db/method/record/get.js +9 -0
  62. package/lib/mem-db/method/record/remove.js +13 -0
  63. package/lib/mem-db/method/record/update.js +15 -0
  64. package/lib/mem-db/method/stat/count.js +11 -0
  65. package/lib/mem-db/start.js +25 -0
  66. package/lib/resolve-method.js +4 -3
  67. package/lib/sanitize-schema.js +21 -9
  68. package/package.json +4 -3
  69. package/bajo/hook/bajoI18N@before-init.js +0 -6
  70. package/bajoCli/applet/shell.js +0 -48
  71. /package/bajo/hook/{bajoI18N.db@before-resource-merge.js → bajo-i18n.db@before-resource-merge.js} +0 -0
package/bajo/.alias ADDED
@@ -0,0 +1 @@
1
+ db
package/bajo/config.json CHANGED
@@ -1,9 +1,13 @@
1
1
  {
2
- "alias": "db",
3
2
  "connections": [],
4
3
  "dependencies": [],
5
4
  "mergeProps": ["connections"],
6
- "defaults": {
5
+ "validationParams": {
6
+ "abortEarly": false,
7
+ "convert": false,
8
+ "allowUnknown": true
9
+ },
10
+ "default": {
7
11
  "property": {
8
12
  "text": {
9
13
  "kind": "text"
@@ -23,5 +27,11 @@
23
27
  "required": true,
24
28
  "index": { "type": "primary" }
25
29
  }
30
+ },
31
+ "memDb": {
32
+ "createDefConnAtStart": true,
33
+ "persistence": {
34
+ "syncPeriod": 1
35
+ }
26
36
  }
27
37
  }
package/bajo/init.js CHANGED
@@ -3,13 +3,24 @@ import collectDrivers from '../lib/collect-drivers.js'
3
3
  import collectFeature from '../lib/collect-feature.js'
4
4
  import collectSchemas from '../lib/collect-schemas.js'
5
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('There could only be one connection type \'%s\'', item.type)
10
+ }
11
+
6
12
  async function init () {
7
13
  const { buildCollections } = this.app.bajo
8
14
  const { fs } = this.app.bajo.lib
9
- const cfg = this.config
10
- fs.ensureDirSync(`${cfg.dir.data}/attachment`)
15
+ fs.ensureDirSync(`${this.dir.data}/attachment`)
11
16
  await collectDrivers.call(this)
12
- this.connections = await buildCollections({ ns: this.name, handler: collectConnections, dupChecks: ['name'] })
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] })
13
24
  if (this.connections.length === 0) this.log.warn('No %s found!', this.print.write('connection'))
14
25
  await collectFeature.call(this)
15
26
  await collectSchemas.call(this)
@@ -20,7 +20,7 @@ async function create (name, id, options = {}) {
20
20
  file
21
21
  }
22
22
  await mergeAttachmentInfo.call(this, rec, dest, { mimeType, fullPath, stats })
23
- if (req && req.flash) req.flash('dbsuccess', { message: req.i18n.t('File successfully uploaded') })
23
+ if (req && req.flash) req.flash('notify', req.t('Attachment successfully uploaded'))
24
24
  return rec
25
25
  }
26
26
 
@@ -4,7 +4,7 @@ async function getPath (name, id, field, file, options = {}) {
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}`
7
- if (!fs.existsSync(path)) throw this.error('notfound')
7
+ if (!fs.existsSync(path)) throw this.error('notFound')
8
8
  return path
9
9
  }
10
10
 
@@ -5,7 +5,7 @@ async function get (name, id, field, file, options = {}) {
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 })
8
- if (!data) throw this.error('notfound', { statusCode: 404 })
8
+ if (!data) throw this.error('notFound', { statusCode: 404 })
9
9
  return data
10
10
  }
11
11
 
@@ -5,7 +5,7 @@ async function remove (name, id, field, file, options = {}) {
5
5
  const path = await this.attachmentGetPath(name, id, field, file)
6
6
  const { req } = options
7
7
  await fs.remove(path)
8
- if (req && req.flash) req.flash('dbsuccess', { message: req.i18n.t('File successfully removed') })
8
+ if (req && req.flash) req.flash('notify', req.t('Attachment successfully removed'))
9
9
  }
10
10
 
11
11
  export default remove
@@ -4,6 +4,7 @@ async function buildQuery ({ filter, schema, options = {} } = {}) {
4
4
  const { trim, isString, isPlainObject } = this.app.bajo.lib._
5
5
  let query = {}
6
6
  if (isString(filter.query)) {
7
+ filter.oquery = filter.query
7
8
  if (trim(filter.query).startsWith('{')) query = JSON.parse(filter.query)
8
9
  else query = nql(filter.query).parse()
9
10
  } else if (isPlainObject(filter.query)) query = filter.query
@@ -4,7 +4,7 @@ import execFeatureHook from '../../../lib/exec-feature-hook.js'
4
4
 
5
5
  async function create (name, inputs, options) {
6
6
  const { generateId, runHook, isSet } = this.app.bajo
7
- const { clearColl } = this.cache ?? {}
7
+ const { clearModel } = this.cache ?? {}
8
8
  const { find } = this.app.bajo.lib._
9
9
  options.dataOnly = options.dataOnly ?? true
10
10
  options.truncateString = options.truncateString ?? true
@@ -19,8 +19,8 @@ async function create (name, inputs, options) {
19
19
  if (!noValidation) b = await execValidation.call(this, { noHook, name, b, options })
20
20
  }
21
21
  if (!noHook) {
22
- await runHook(`${this.name}:onBeforeBulkCreate`, name, bodies, options)
23
- await runHook(`${this.name}.${name}:onBeforeBulkCreate`, bodies, options)
22
+ await runHook(`${this.name}:beforeBulkCreate`, name, bodies, options)
23
+ await runHook(`${this.name}.${name}:beforeBulkCreate`, bodies, options)
24
24
  }
25
25
  for (const idx in bodies) {
26
26
  await execFeatureHook.call(this, 'beforeCreate', { schema, body: bodies[idx] })
@@ -36,10 +36,10 @@ async function create (name, inputs, options) {
36
36
  await execFeatureHook.call(this, 'afterCreate', { schema, body: bodies[idx] })
37
37
  }
38
38
  if (!noHook) {
39
- await runHook(`${this.name}.${name}:onAfterBulkCreate`, bodies, options)
40
- await runHook(`${this.name}:onAfterBulkCreate`, name, bodies, options)
39
+ await runHook(`${this.name}.${name}:afterBulkCreate`, bodies, options)
40
+ await runHook(`${this.name}:afterBulkCreate`, name, bodies, options)
41
41
  }
42
- if (clearColl) await clearColl({ model: name })
42
+ if (clearModel) await clearModel({ model: name })
43
43
  }
44
44
 
45
45
  export default create
@@ -0,0 +1,6 @@
1
+ function getConnection (name) {
2
+ const { find } = this.app.bajo.lib._
3
+ return find(this.connections, { name })
4
+ }
5
+
6
+ export default getConnection
@@ -2,8 +2,8 @@ function getInfo (name) {
2
2
  const { breakNsPath } = this.app.bajo
3
3
  const { find, map } = this.app.bajo.lib._
4
4
  const schema = this.getSchema(name)
5
- const conn = find(this.connections, { name: schema.connection })
6
- const [ns, type] = breakNsPath(conn.type)
5
+ const conn = this.getConnection(schema.connection)
6
+ const { ns, path: type } = breakNsPath(conn.type)
7
7
  const driver = find(this.drivers, { type, ns, driver: conn.driver })
8
8
  const instance = find(this.app[driver.ns].instances, { name: schema.connection })
9
9
  const opts = conn.type === 'mssql' ? { includeTriggerModifications: true } : undefined
@@ -1,10 +1,10 @@
1
- function getSchema (input) {
1
+ function getSchema (input, cloned = true) {
2
2
  const { find, isPlainObject, cloneDeep } = this.app.bajo.lib._
3
3
  let name = isPlainObject(input) ? input.name : input
4
4
  name = this.app.bajo.pascalCase(name)
5
5
  const schema = find(this.schemas, { name })
6
6
  if (!schema) throw this.error('Unknown model/schema \'%s\'', name)
7
- return cloneDeep(schema)
7
+ return cloned ? cloneDeep(schema) : schema
8
8
  }
9
9
 
10
10
  export default getSchema
@@ -2,17 +2,19 @@ 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.app.bajo.lib._
6
+
5
7
  await this.modelExists(name, true)
6
8
  const { noHook } = options
7
- const { handler, schema } = await resolveMethod.call(this, name, 'model-clear')
9
+ const { handler, schema } = await resolveMethod.call(this, name, 'model-clear', options)
8
10
  if (!noHook) {
9
- await runHook(`${this.name}:onBeforeCollClear`, name, options)
10
- await runHook(`${this.name}.${name}:onBeforeCollClear`, options)
11
+ await runHook(`${this.name}:beforeModelClear`, schema, options)
12
+ await runHook(`${this.name}.${camelCase(name)}:beforeModelClear`, options)
11
13
  }
12
14
  const resp = await handler.call(this, { schema, options })
13
15
  if (!noHook) {
14
- await runHook(`${this.name}.${name}:onAfterCollClear`, options, resp)
15
- await runHook(`${this.name}:onAfterCollClear`, name, options, resp)
16
+ await runHook(`${this.name}.${camelCase(name)}:afterModelClear`, options, resp)
17
+ await runHook(`${this.name}:afterModelClear`, schema, options, resp)
16
18
  }
17
19
  return resp
18
20
  }
@@ -2,10 +2,18 @@ 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.app.bajo.lib._
6
+
5
7
  const { handler, schema } = await resolveMethod.call(this, name, 'model-create', options)
6
- await runHook(`${this.name}:beforeCollCreate` + name, schema)
8
+ if (!options.noHook) {
9
+ await runHook(`${this.name}:beforeModelCreate`, schema, options)
10
+ await runHook(`${this.name}.${camelCase(name)}:beforeModelCreate`, options)
11
+ }
7
12
  await handler.call(this, { schema, options })
8
- await runHook(`${this.name}:afterCollCreate` + name, schema)
13
+ if (!options.noHook) {
14
+ await runHook(`${this.name}.${camelCase(name)}:afterModelCreate`, options)
15
+ await runHook(`${this.name}:afterModelCreate`, schema, options)
16
+ }
9
17
  }
10
18
 
11
19
  export default create
@@ -2,10 +2,18 @@ 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.app.bajo.lib._
5
6
  const { handler, schema } = await resolveMethod.call(this, name, 'model-drop', options)
6
- await runHook(`${this.name}:beforeCollDrop` + name, schema)
7
+
8
+ if (!options.noHook) {
9
+ await runHook(`${this.name}:beforeModelDrop`, schema, options)
10
+ await runHook(`${this.name}.${camelCase(name)}:beforeModelDrop`, options)
11
+ }
7
12
  await handler.call(this, { schema, options })
8
- await runHook(`${this.name}:afterCollDrop` + name, schema)
13
+ if (!options.noHook) {
14
+ await runHook(`${this.name}.${camelCase(name)}:afterModelDrop`, options)
15
+ await runHook(`${this.name}:afterModelDrop`, schema, options)
16
+ }
9
17
  }
10
18
 
11
19
  export default drop
@@ -5,10 +5,17 @@ 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.app.bajo.lib._
8
9
  const { handler, schema } = await resolveMethod.call(this, name, 'model-exists', options)
9
- await runHook(`${this.name}:beforeCollExists` + name, schema)
10
+ if (!options.noHook) {
11
+ await runHook(`${this.name}:beforeModelExists`, schema, options)
12
+ await runHook(`${this.name}.${camelCase(name)}:beforeModelExists`, options)
13
+ }
10
14
  const exist = await handler.call(this, { schema, options })
11
- await runHook(`${this.name}:afterCollExists` + name, schema, exist)
15
+ if (!options.noHook) {
16
+ await runHook(`${this.name}.${camelCase(name)}:afterModelExists`, exist, options)
17
+ await runHook(`${this.name}:afterModelExists`, schema, exist, options)
18
+ }
12
19
  if (!exist && thrown) throw this.error('Model doesn\'t exist yet. Please do model rebuild first')
13
20
  cache[name] = exist
14
21
  return exist
@@ -1,30 +1,36 @@
1
- async function transform ({ record, schema, hidden = [] } = {}) {
2
- const { dayjs } = this.app.bajo
1
+ async function transform ({ record, schema, hidden = [], forceNoHidden } = {}) {
2
+ const { dayjs } = this.app.bajo.lib
3
3
  if (record._id) {
4
4
  record.id = record._id
5
5
  delete record._id
6
6
  }
7
- const result = {}
7
+ const defHidden = [...schema.hidden, ...hidden]
8
+ let result = {}
8
9
  for (const p of schema.properties) {
9
- if (hidden.includes(p.name)) continue
10
+ if (!forceNoHidden && defHidden.includes(p.name)) continue
10
11
  result[p.name] = record[p.name] ?? null
11
12
  if (record[p.name] === null) continue
12
13
  switch (p.type) {
14
+ case 'boolean': result[p.name] = !!result[p.name]; break
13
15
  case 'time': result[p.name] = dayjs(record[p.name]).format('HH:mm:ss'); break
14
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
15
18
  }
16
19
  }
17
- return await this.sanitizeBody({ body: result, schema, partial: true, ignoreNull: true })
20
+ result = await this.sanitizeBody({ body: result, schema, partial: true, ignoreNull: true })
21
+ if (record._rel) result._rel = record._rel
22
+ return result
18
23
  }
19
24
 
20
- async function pickRecord ({ record, fields, schema = {}, hidden = [] } = {}) {
25
+ async function pickRecord ({ record, fields, schema = {}, hidden = [], forceNoHidden } = {}) {
21
26
  const { isArray, pick, clone, isEmpty, omit } = this.app.bajo.lib._
22
27
  if (isEmpty(record)) return record
23
28
  if (hidden.length > 0) record = omit(record, hidden)
24
- if (!isArray(fields)) return await transform.call(this, { record, schema, hidden })
29
+ if (!isArray(fields)) return await transform.call(this, { record, schema, hidden, forceNoHidden })
25
30
  const fl = clone(fields)
26
31
  if (!fl.includes('id')) fl.unshift('id')
27
- return pick(await transform.call(this, { record, schema, hidden }), fl)
32
+ if (record._rel) fl.push('_rel')
33
+ return pick(await transform.call(this, { record, schema, hidden, forceNoHidden }), fl)
28
34
  }
29
35
 
30
36
  export default pickRecord
@@ -1,6 +1,6 @@
1
1
  function buildPageSkipLimit (filter) {
2
- let limit = parseInt(filter.limit) || this.config.defaults.filter.limit
3
- if (limit > this.config.defaults.filter.maxLimit) limit = this.config.defaults.filter.maxLimit
2
+ let limit = parseInt(filter.limit) || this.config.default.filter.limit
3
+ if (limit > this.config.default.filter.maxLimit) limit = this.config.default.filter.maxLimit
4
4
  if (limit < 1) limit = 1
5
5
  let page = parseInt(filter.page) || 1
6
6
  if (page < 1) page = 1
@@ -14,11 +14,11 @@ function buildPageSkipLimit (filter) {
14
14
  }
15
15
 
16
16
  function buildSort (input, schema, allowSortUnindexed) {
17
- const { isEmpty, map, each, isPlainObject, isString, trim, filter, keys } = this.app.bajo.lib._
17
+ const { isEmpty, map, each, isPlainObject, isString, trim, keys } = this.app.bajo.lib._
18
18
  let sort
19
19
  if (schema && isEmpty(input)) {
20
20
  const columns = map(schema.properties, 'name')
21
- each(this.config.defaults.filter.sort, s => {
21
+ each(this.config.default.filter.sort, s => {
22
22
  const [col] = s.split(':')
23
23
  if (columns.includes(col)) {
24
24
  input = s
@@ -39,13 +39,9 @@ function buildSort (input, schema, allowSortUnindexed) {
39
39
  sort = item
40
40
  }
41
41
  if (schema) {
42
- const indexes = map(filter(schema.properties, p => !!p.index), 'name')
43
- each(schema.indexes, item => {
44
- indexes.push(...item.fields)
45
- })
46
42
  const items = keys(sort)
47
43
  each(items, i => {
48
- if (!indexes.includes(i) && !allowSortUnindexed) throw this.error('Sort on unindexed field: \'%s@%s\'', i, schema.name)
44
+ if (!schema.sortables.includes(i) && !allowSortUnindexed) throw this.error('Sort on unindexed field: \'%s@%s\'', i, schema.name)
49
45
  // if (schema.fullText.fields.includes(i)) throw this.error('Can\'t sort on full-text index: \'%s@%s\'', i, schema.name)
50
46
  })
51
47
  }
@@ -3,18 +3,19 @@ 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 } = this.app.bajo.lib._
7
- const options = cloneDeep(opts)
6
+ const { cloneDeep, camelCase, omit } = this.app.bajo.lib._
7
+ const options = cloneDeep(omit(opts, ['req']))
8
+ options.req = opts.req
8
9
  const { noHook } = options
9
- const { handler, schema, driver } = await resolveMethod.call(this, name, 'record-clear')
10
+ const { handler, schema, driver } = await resolveMethod.call(this, name, 'record-clear', options)
10
11
  if (!noHook) {
11
- await runHook(`${this.name}:onBeforeRecordClear`, name, options)
12
- await runHook(`${this.name}.${name}:onBeforeRecordClear`, options)
12
+ await runHook(`${this.name}:beforeRecordClear`, name, options)
13
+ await runHook(`${this.name}.${camelCase(name)}:beforeRecordClear`, options)
13
14
  }
14
15
  const resp = await handler.call(this.app[driver.ns], { schema, options })
15
16
  if (!noHook) {
16
- await runHook(`${this.name}.${name}:onAfterRecordClear`, options, resp)
17
- await runHook(`${this.name}:onAfterRecordClear`, name, options, resp)
17
+ await runHook(`${this.name}.${camelCase(name)}:afterRecordClear`, options, resp)
18
+ await runHook(`${this.name}:afterRecordClear`, name, options, resp)
18
19
  }
19
20
  return resp
20
21
  }
@@ -6,55 +6,53 @@ import execFeatureHook from '../../../lib/exec-feature-hook.js'
6
6
 
7
7
  async function create (name, input, opts = {}) {
8
8
  const { generateId, runHook, isSet } = this.app.bajo
9
- const { clearColl } = this.cache ?? {}
10
- const { get, find, forOwn, cloneDeep } = this.app.bajo.lib._
11
- const options = cloneDeep(opts)
9
+ const { clearModel } = this.cache ?? {}
10
+ const { find, forOwn, cloneDeep, camelCase, omit, get } = this.app.bajo.lib._
11
+ const options = cloneDeep(omit(opts, ['req']))
12
+ options.req = opts.req
12
13
  options.dataOnly = options.dataOnly ?? true
13
14
  input = cloneDeep(input)
14
- const { fields, dataOnly, noHook, noValidation, noCheckUnique, noFeatureHook, noResult, noSanitize, hidden } = options
15
+ const { fields, dataOnly, noHook, noValidation, noCheckUnique, noFeatureHook, noResult, noSanitize, hidden, forceNoHidden } = options
15
16
  options.truncateString = options.truncateString ?? true
16
17
  options.dataOnly = false
17
18
  await this.modelExists(name, true)
18
19
  const { handler, schema, driver } = await resolveMethod.call(this, name, 'record-create', options)
19
20
  const idField = find(schema.properties, { name: 'id' })
21
+ const extFields = get(options, 'validation.extFields', [])
20
22
  if (!isSet(input.id)) {
21
23
  if (idField.type === 'string') input.id = generateId()
22
24
  else if (['integer', 'smallint'].includes(idField.type) && !idField.autoInc) input.id = generateId('int')
23
25
  }
24
- let body = noSanitize ? input : await this.sanitizeBody({ body: input, schema, strict: true })
26
+ let body = noSanitize ? input : await this.sanitizeBody({ body: input, schema, extFields, strict: true })
25
27
  if (!noHook) {
26
- await runHook(`${this.name}:onBeforeRecordCreate`, name, body, options)
27
- await runHook(`${this.name}.${name}:onBeforeRecordCreate`, body, options)
28
+ await runHook(`${this.name}:beforeRecordCreate`, name, body, options)
29
+ await runHook(`${this.name}.${camelCase(name)}:beforeRecordCreate`, body, options)
28
30
  }
29
31
  if (!noFeatureHook) await execFeatureHook.call(this, 'beforeCreate', { schema, body })
30
- if (!noValidation) body = await execValidation.call(this, { noHook, name, body, options })
32
+ if (!noValidation) body = await execValidation.call(this, { name, body, options })
31
33
  if (isSet(body.id) && !noCheckUnique) await checkUnique.call(this, { schema, body })
32
34
  let record = {}
33
- try {
34
- const nbody = {}
35
- forOwn(body, (v, k) => {
36
- if (v === undefined) return undefined
37
- const prop = find(schema.properties, { name: k })
38
- if (options.truncateString && isSet(v) && prop && ['string', 'text'].includes(prop.type)) v = v.slice(0, prop.maxLength)
39
- nbody[k] = v
40
- })
41
- record = await handler.call(this.app[driver.ns], { schema, body: nbody, options })
42
- if (options.req) {
43
- if (options.req.file) await handleAttachmentUpload.call(this, { name: schema.name, id: body.id, body, options, action: 'create' })
44
- if (options.req.flash) options.req.flash('dbsuccess', { message: this.print.write('Record successfully created'), record })
45
- }
46
- } catch (err) {
47
- if (get(options, 'req.flash')) options.req.flash('dberr', err)
48
- throw err
35
+ const nbody = {}
36
+ forOwn(body, (v, k) => {
37
+ if (v === undefined) return undefined
38
+ const prop = find(schema.properties, { name: k })
39
+ if (!prop) return undefined
40
+ if (options.truncateString && isSet(v) && ['string', 'text'].includes(prop.type)) v = v.slice(0, prop.maxLength)
41
+ nbody[k] = v
42
+ })
43
+ record = await handler.call(this.app[driver.ns], { schema, body: nbody, options })
44
+ if (options.req) {
45
+ if (options.req.file) await handleAttachmentUpload.call(this, { name: schema.name, id: body.id, body, options, action: 'create' })
46
+ if (options.req.flash && !options.noFlash) options.req.flash('notify', options.req.t('Record successfully created'))
49
47
  }
50
48
  if (!noFeatureHook) await execFeatureHook.call(this, 'afterCreate', { schema, body, record })
51
49
  if (!noHook) {
52
- await runHook(`${this.name}.${name}:onAfterRecordCreate`, body, options, record)
53
- await runHook(`${this.name}:onAfterRecordCreate`, name, body, options, record)
50
+ await runHook(`${this.name}.${camelCase(name)}:afterRecordCreate`, body, options, record)
51
+ await runHook(`${this.name}:afterRecordCreate`, name, body, options, record)
54
52
  }
55
- if (clearColl) await clearColl({ model: name, body, options, record })
53
+ if (clearModel) await clearModel({ model: name, body, options, record })
56
54
  if (noResult) return
57
- record.data = await this.pickRecord({ record: record.data, fields, schema, hidden })
55
+ record.data = await this.pickRecord({ record: record.data, fields, schema, hidden, forceNoHidden })
58
56
  return dataOnly ? record.data : record
59
57
  }
60
58
 
@@ -4,18 +4,19 @@ import singleRelRows from '../../../lib/single-rel-rows.js'
4
4
  async function findOne (name, filter = {}, opts = {}) {
5
5
  const { runHook, isSet } = this.app.bajo
6
6
  const { get, set } = this.cache ?? {}
7
- const { cloneDeep } = this.app.bajo.lib._
8
- const options = cloneDeep(opts)
7
+ const { cloneDeep, camelCase, omit } = this.app.bajo.lib._
8
+ const options = cloneDeep(omit(opts, ['req']))
9
+ options.req = opts.req
9
10
  options.dataOnly = options.dataOnly ?? true
10
- const { fields, dataOnly, noHook, noCache, hidden } = options
11
+ const { fields, dataOnly, noHook, noCache, hidden, forceNoHidden } = options
11
12
  await this.modelExists(name, true)
12
13
  filter.limit = 1
13
14
  options.count = false
14
15
  options.dataOnly = false
15
- const { handler, schema, driver } = await resolveMethod.call(this, name, 'record-find')
16
+ const { handler, schema, driver } = await resolveMethod.call(this, name, 'record-find', options)
16
17
  if (!noHook) {
17
- await runHook(`${this.name}:onBeforeRecordFindOne`, name, filter, options)
18
- await runHook(`${this.name}.${name}:onBeforeRecordFindOne`, filter, options)
18
+ await runHook(`${this.name}:beforeRecordFindOne`, name, filter, options)
19
+ await runHook(`${this.name}.${camelCase(name)}:beforeRecordFindOne`, filter, options)
19
20
  }
20
21
  if (get && !noCache) {
21
22
  const cachedResult = await get({ model: name, filter, options })
@@ -27,10 +28,10 @@ async function findOne (name, filter = {}, opts = {}) {
27
28
  const record = await handler.call(this.app[driver.ns], { schema, filter, options })
28
29
  record.data = record.data[0]
29
30
  if (!noHook) {
30
- await runHook(`${this.name}.${name}:onAfterRecordFindOne`, filter, options, record)
31
- await runHook(`${this.name}:onAfterRecordFindOne`, name, filter, options, record)
31
+ await runHook(`${this.name}.${camelCase(name)}:afterRecordFindOne`, filter, options, record)
32
+ await runHook(`${this.name}:afterRecordFindOne`, name, filter, options, record)
32
33
  }
33
- record.data = await this.pickRecord({ record: record.data, fields, schema, hidden })
34
+ record.data = await this.pickRecord({ record: record.data, fields, schema, hidden, forceNoHidden })
34
35
  if (isSet(options.rels)) await singleRelRows.call(this, { schema, record: record.data, options })
35
36
  if (set && !noCache) await set({ model: name, filter, options, record })
36
37
  return dataOnly ? record.data : record
@@ -4,19 +4,21 @@ import multiRelRows from '../../../lib/multi-rel-rows.js'
4
4
  async function find (name, filter = {}, opts = {}) {
5
5
  const { runHook, isSet } = this.app.bajo
6
6
  const { get, set } = this.cache ?? {}
7
- const { cloneDeep } = this.app.bajo.lib._
8
- const options = cloneDeep(opts)
7
+ const { cloneDeep, camelCase, omit } = this.app.bajo.lib._
8
+ const options = cloneDeep(omit(opts, ['req']))
9
+ options.req = opts.req
9
10
  options.dataOnly = options.dataOnly ?? true
10
- const { fields, dataOnly, noHook, noCache, hidden } = options
11
+ const { fields, dataOnly, noHook, noCache, hidden, forceNoHidden } = options
11
12
  options.count = options.count ?? false
12
13
  options.dataOnly = false
13
14
  await this.modelExists(name, true)
14
- const { handler, schema, driver } = await resolveMethod.call(this, name, 'record-find')
15
+ const { handler, schema, driver } = await resolveMethod.call(this, name, 'record-find', options)
15
16
  filter.query = await this.buildQuery({ filter, schema, options }) ?? {}
17
+ if (options.queryHandler) filter.query = await options.queryHandler.call(opts.req ? this.app[opts.req.ns] : this, filter.query, opts.req)
16
18
  filter.match = this.buildMatch({ input: filter.match, schema, options }) ?? {}
17
19
  if (!noHook) {
18
- await runHook(`${this.name}:onBeforeRecordFind`, name, filter, options)
19
- await runHook(`${this.name}.${name}:onBeforeRecordFind`, filter, options)
20
+ await runHook(`${this.name}:beforeRecordFind`, name, filter, options)
21
+ await runHook(`${this.name}.${camelCase(name)}:beforeRecordFind`, filter, options)
20
22
  }
21
23
  if (get && !noCache) {
22
24
  const cachedResult = await get({ model: name, filter, options })
@@ -27,11 +29,11 @@ async function find (name, filter = {}, opts = {}) {
27
29
  }
28
30
  const records = await handler.call(this.app[driver.ns], { schema, filter, options })
29
31
  if (!noHook) {
30
- await runHook(`${this.name}.${name}:onAfterRecordFind`, filter, options, records)
31
- await runHook(`${this.name}:onAfterRecordFind`, name, filter, options, records)
32
+ await runHook(`${this.name}.${camelCase(name)}:afterRecordFind`, filter, options, records)
33
+ await runHook(`${this.name}:afterRecordFind`, name, filter, options, records)
32
34
  }
33
35
  for (const idx in records.data) {
34
- records.data[idx] = await this.pickRecord({ record: records.data[idx], fields, schema, hidden })
36
+ records.data[idx] = await this.pickRecord({ record: records.data[idx], fields, schema, hidden, forceNoHidden })
35
37
  }
36
38
  if (isSet(options.rels)) await multiRelRows.call(this, { schema, records: records.data, options })
37
39
  if (set && !noCache) await set({ model: name, filter, options, records })
@@ -4,17 +4,18 @@ import singleRelRows from '../../../lib/single-rel-rows.js'
4
4
  async function get (name, id, opts = {}) {
5
5
  const { runHook, isSet } = this.app.bajo
6
6
  const { get, set } = this.cache ?? {}
7
- const { cloneDeep } = this.app.bajo.lib._
8
- const options = cloneDeep(opts)
7
+ const { cloneDeep, camelCase, omit } = this.app.bajo.lib._
8
+ const options = cloneDeep(omit(opts, ['req']))
9
+ options.req = opts.req
9
10
  options.dataOnly = options.dataOnly ?? true
10
- const { fields, dataOnly, noHook, noCache, hidden = [] } = options
11
+ const { fields, dataOnly, noHook, noCache, hidden = [], forceNoHidden } = options
11
12
  await this.modelExists(name, true)
12
- const { handler, schema, driver } = await resolveMethod.call(this, name, 'record-get')
13
+ const { handler, schema, driver } = await resolveMethod.call(this, name, 'record-get', options)
13
14
  id = this.sanitizeId(id, schema)
14
15
  options.dataOnly = false
15
16
  if (!noHook) {
16
- await runHook(`${this.name}:onBeforeRecordGet`, name, id, options)
17
- await runHook(`${this.name}.${name}:onBeforeRecordGet`, id, options)
17
+ await runHook(`${this.name}:beforeRecordGet`, name, id, options)
18
+ await runHook(`${this.name}.${camelCase(name)}:beforeRecordGet`, id, options)
18
19
  }
19
20
  if (get && !noCache) {
20
21
  const cachedResult = await get({ model: name, id, options })
@@ -25,10 +26,10 @@ async function get (name, id, opts = {}) {
25
26
  }
26
27
  const record = await handler.call(this.app[driver.ns], { schema, id, options })
27
28
  if (!noHook) {
28
- await runHook(`${this.name}.${name}:onAfterRecordGet`, id, options, record)
29
- await runHook(`${this.name}:onAfterRecordGet`, name, id, options, record)
29
+ await runHook(`${this.name}.${camelCase(name)}:afterRecordGet`, id, options, record)
30
+ await runHook(`${this.name}:afterRecordGet`, name, id, options, record)
30
31
  }
31
- record.data = await this.pickRecord({ record: record.data, fields, schema, hidden })
32
+ record.data = await this.pickRecord({ record: record.data, fields, schema, hidden, forceNoHidden })
32
33
  if (isSet(options.rels)) await singleRelRows.call(this, { schema, record: record.data, options })
33
34
 
34
35
  if (set && !noCache) await set({ model: name, id, options, record })
@@ -3,31 +3,32 @@ import handleAttachmentUpload from '../../../lib/handle-attachment-upload.js'
3
3
 
4
4
  async function remove (name, id, opts = {}) {
5
5
  const { runHook } = this.app.bajo
6
- const { clearColl } = this.cache ?? {}
7
- const { cloneDeep } = this.app.bajo.lib._
8
- const options = cloneDeep(opts)
6
+ const { clearModel } = this.cache ?? {}
7
+ const { cloneDeep, camelCase, omit } = this.app.bajo.lib._
8
+ const options = cloneDeep(omit(opts, ['req']))
9
+ options.req = opts.req
9
10
  options.dataOnly = options.dataOnly ?? true
10
- const { fields, dataOnly, noHook, noResult, hidden } = options
11
+ const { fields, dataOnly, noHook, noResult, hidden, forceNoHidden } = options
11
12
  options.dataOnly = false
12
13
  await this.modelExists(name, true)
13
- const { handler, schema, driver } = await resolveMethod.call(this, name, 'record-remove')
14
+ const { handler, schema, driver } = await resolveMethod.call(this, name, 'record-remove', options)
14
15
  id = this.sanitizeId(id, schema)
15
16
  if (!noHook) {
16
- await runHook(`${this.name}:onBeforeRecordRemove`, name, id, options)
17
- await runHook(`${this.name}.${name}:onBeforeRecordRemove`, id, options)
17
+ await runHook(`${this.name}:beforeRecordRemove`, name, id, options)
18
+ await runHook(`${this.name}.${camelCase(name)}:beforeRecordRemove`, id, options)
18
19
  }
19
20
  const record = await handler.call(this.app[driver.ns], { schema, id, options })
20
21
  if (options.req) {
21
22
  if (options.req.file) await handleAttachmentUpload.call(this, { name: schema.name, id, options, action: 'remove' })
22
- if (options.req.flash) options.req.flash('dbsuccess', { message: this.print.write('Record successfully removed'), record })
23
+ if (options.req.flash && !options.noFlash) options.req.flash('notify', options.req.t('Record successfully removed'))
23
24
  }
24
25
  if (!noHook) {
25
- await runHook(`${this.name}.${name}:onAfterRecordRemove`, id, options, record)
26
- await runHook(`${this.name}:onAfterRecordRemove`, name, id, options, record)
26
+ await runHook(`${this.name}.${camelCase(name)}:afterRecordRemove`, id, options, record)
27
+ await runHook(`${this.name}:afterRecordRemove`, name, id, options, record)
27
28
  }
28
- if (clearColl) await clearColl({ model: name, id, options, record })
29
+ if (clearModel) await clearModel({ model: name, id, options, record })
29
30
  if (noResult) return
30
- record.oldData = await this.pickRecord({ record: record.oldData, fields, schema, hidden })
31
+ record.oldData = await this.pickRecord({ record: record.oldData, fields, schema, hidden, forceNoHidden })
31
32
  return dataOnly ? record.oldData : record
32
33
  }
33
34