dobo 2.0.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/FUNDING.yml +13 -0
- package/.github/workflows/repo-lockdown.yml +24 -0
- package/.jsdoc.conf.json +45 -0
- package/LICENSE +1 -1
- package/README.md +38 -19
- package/docs/Dobo.html +26 -0
- package/docs/data/search.json +1 -0
- package/docs/fonts/Inconsolata-Regular.ttf +0 -0
- package/docs/fonts/OpenSans-Regular.ttf +0 -0
- package/docs/fonts/WorkSans-Bold.ttf +0 -0
- package/docs/global.html +7 -0
- package/docs/index.html +3 -0
- package/docs/index.js.html +578 -0
- package/docs/lib_collect-connections.js.html +39 -0
- package/docs/lib_collect-drivers.js.html +52 -0
- package/docs/lib_collect-features.js.html +36 -0
- package/docs/lib_collect-schemas.js.html +94 -0
- package/docs/lib_index.js.html +6 -0
- package/docs/method_model_create.js.html +35 -0
- package/docs/method_model_drop.js.html +34 -0
- package/docs/method_model_exists.js.html +40 -0
- package/docs/method_record_count.js.html +69 -0
- package/docs/method_record_create.js.html +114 -0
- package/docs/method_record_find-all.js.html +44 -0
- package/docs/method_record_find-one.js.html +73 -0
- package/docs/method_record_find.js.html +118 -0
- package/docs/method_record_get.js.html +92 -0
- package/docs/method_record_remove.js.html +75 -0
- package/docs/method_record_update.js.html +107 -0
- package/docs/method_record_upsert.js.html +54 -0
- package/docs/method_sanitize_body.js.html +88 -0
- package/docs/method_sanitize_date.js.html +30 -0
- package/docs/method_sanitize_id.js.html +20 -0
- package/docs/method_validate.js.html +249 -0
- package/docs/module-Lib.html +3 -0
- package/docs/scripts/core.js +725 -0
- package/docs/scripts/core.min.js +23 -0
- package/docs/scripts/resize.js +90 -0
- package/docs/scripts/search.js +265 -0
- package/docs/scripts/search.min.js +6 -0
- package/docs/scripts/third-party/Apache-License-2.0.txt +202 -0
- package/docs/scripts/third-party/fuse.js +9 -0
- package/docs/scripts/third-party/hljs-line-num-original.js +366 -0
- package/docs/scripts/third-party/hljs-line-num.js +1 -0
- package/docs/scripts/third-party/hljs-original.js +5164 -0
- package/docs/scripts/third-party/hljs.js +1 -0
- package/docs/scripts/third-party/popper.js +5 -0
- package/docs/scripts/third-party/tippy.js +1 -0
- package/docs/scripts/third-party/tocbot.js +671 -0
- package/docs/scripts/third-party/tocbot.min.js +1 -0
- package/docs/static/bitcoin.jpeg +0 -0
- package/docs/static/home.md +25 -0
- package/docs/static/logo-ecosystem.png +0 -0
- package/docs/static/logo.png +0 -0
- package/docs/styles/clean-jsdoc-theme-base.css +1159 -0
- package/docs/styles/clean-jsdoc-theme-dark.css +412 -0
- package/docs/styles/clean-jsdoc-theme-light.css +482 -0
- package/docs/styles/clean-jsdoc-theme-scrollbar.css +30 -0
- package/docs/styles/clean-jsdoc-theme-without-scrollbar.min.css +1 -0
- package/docs/styles/clean-jsdoc-theme.min.css +1 -0
- package/extend/bajo/intl/en-US.json +69 -30
- package/extend/bajo/intl/id.json +58 -29
- package/extend/bajoCli/applet/clear-record.js +22 -0
- package/extend/bajoCli/applet/connection.js +5 -5
- package/extend/bajoCli/applet/count-record.js +27 -0
- package/extend/bajoCli/applet/create-aggregate.js +33 -0
- package/extend/bajoCli/applet/create-histogram.js +33 -0
- package/extend/bajoCli/applet/create-record.js +39 -0
- package/extend/bajoCli/applet/find-record.js +27 -0
- package/extend/bajoCli/applet/get-record.js +27 -0
- package/extend/bajoCli/applet/lib/post-process.js +25 -26
- package/extend/bajoCli/applet/model.js +22 -0
- package/extend/bajoCli/applet/rebuild-model.js +91 -0
- package/extend/bajoCli/applet/remove-record.js +27 -0
- package/extend/bajoCli/applet/update-record.js +44 -0
- package/extend/bajoCli/applet.js +0 -0
- package/extend/dobo/driver/memory.js +170 -0
- package/extend/dobo/feature/created-at.js +10 -8
- package/extend/dobo/feature/dt.js +0 -0
- package/extend/dobo/feature/immutable.js +30 -0
- package/extend/dobo/feature/int-id.js +0 -0
- package/extend/dobo/feature/removed-at.js +35 -57
- package/extend/dobo/feature/updated-at.js +14 -12
- package/extend/waibuMpa/route/attachment/@model/@id/@field/@file.js +5 -9
- package/extend/waibuStatic/virtual.json +0 -0
- package/index.js +420 -337
- package/lib/collect-connections.js +60 -21
- package/lib/collect-drivers.js +29 -35
- package/lib/collect-features.js +40 -0
- package/lib/collect-models.js +319 -0
- package/lib/factory/action.js +161 -0
- package/lib/factory/connection.js +62 -0
- package/lib/factory/driver.js +358 -0
- package/lib/factory/feature.js +33 -0
- package/lib/factory/model/_util.js +402 -0
- package/lib/factory/model/build.js +15 -0
- package/lib/factory/model/clear-record.js +17 -0
- package/lib/factory/model/count-record.js +17 -0
- package/lib/factory/model/create-aggregate.js +17 -0
- package/lib/factory/model/create-attachment.js +29 -0
- package/lib/factory/model/create-histogram.js +17 -0
- package/lib/factory/model/create-record.js +35 -0
- package/lib/factory/model/drop.js +15 -0
- package/lib/factory/model/exists.js +21 -0
- package/lib/factory/model/find-all-record.js +71 -0
- package/lib/factory/model/find-attachment.js +29 -0
- package/lib/factory/model/find-one-record.js +19 -0
- package/lib/factory/model/find-record.js +103 -0
- package/lib/factory/model/get-attachment.js +15 -0
- package/lib/factory/model/get-record.js +79 -0
- package/lib/factory/model/list-attachment.js +37 -0
- package/lib/{add-fixtures.js → factory/model/load-fixtures.js} +69 -67
- package/lib/factory/model/remove-attachment.js +15 -0
- package/lib/factory/model/remove-record.js +59 -0
- package/lib/factory/model/sanitize-body.js +62 -0
- package/lib/factory/model/sanitize-id.js +7 -0
- package/lib/factory/model/sanitize-record.js +26 -0
- package/lib/factory/model/update-attachment.js +9 -0
- package/lib/factory/model/update-record.js +81 -0
- package/lib/factory/model/upsert-record.js +95 -0
- package/lib/factory/model/validate.js +232 -0
- package/lib/factory/model.js +150 -0
- package/lib/index.js +3 -0
- package/package.json +45 -36
- package/wiki/APPLETS.md +57 -0
- package/wiki/CHANGES.md +46 -0
- package/wiki/CONFIG.md +25 -0
- package/wiki/CONTRIBUTING.md +5 -0
- package/wiki/DEV-GUIDE.md +1 -0
- package/wiki/ECOSYSTEM.md +20 -0
- package/wiki/GETTING-STARTED.md +166 -0
- package/{docs/query-language.md → wiki/QUERY-LANGUAGE.md} +0 -0
- package/wiki/USER-GUIDE.md +1 -0
- package/extend/bajoCli/applet/model-clear.js +0 -11
- package/extend/bajoCli/applet/model-rebuild.js +0 -101
- package/extend/bajoCli/applet/record-create.js +0 -41
- package/extend/bajoCli/applet/record-find.js +0 -27
- package/extend/bajoCli/applet/record-get.js +0 -24
- package/extend/bajoCli/applet/record-remove.js +0 -24
- package/extend/bajoCli/applet/record-update.js +0 -47
- package/extend/bajoCli/applet/schema.js +0 -22
- package/extend/bajoCli/applet/stat-count.js +0 -24
- package/lib/build-bulk-action.js +0 -12
- package/lib/check-unique.js +0 -39
- package/lib/collect-feature.js +0 -25
- package/lib/collect-schemas.js +0 -83
- package/lib/exec-feature-hook.js +0 -13
- package/lib/exec-validation.js +0 -21
- package/lib/generic-prop-sanitizer.js +0 -31
- package/lib/handle-attachment-upload.js +0 -16
- package/lib/mem-db/conn-sanitizer.js +0 -8
- package/lib/mem-db/instantiate.js +0 -41
- package/lib/mem-db/method/model/clear.js +0 -6
- package/lib/mem-db/method/model/create.js +0 -5
- package/lib/mem-db/method/model/drop.js +0 -5
- package/lib/mem-db/method/model/exists.js +0 -5
- package/lib/mem-db/method/record/create.js +0 -12
- package/lib/mem-db/method/record/find.js +0 -20
- package/lib/mem-db/method/record/get.js +0 -9
- package/lib/mem-db/method/record/remove.js +0 -13
- package/lib/mem-db/method/record/update.js +0 -15
- package/lib/mem-db/method/stat/count.js +0 -11
- package/lib/mem-db/start.js +0 -25
- package/lib/merge-attachment-info.js +0 -16
- package/lib/multi-rel-rows.js +0 -42
- package/lib/resolve-method.js +0 -16
- package/lib/sanitize-schema.js +0 -197
- package/lib/single-rel-rows.js +0 -38
- package/method/attachment/copy-uploaded.js +0 -34
- package/method/attachment/create.js +0 -29
- package/method/attachment/find.js +0 -27
- package/method/attachment/get-path.js +0 -12
- package/method/attachment/get.js +0 -12
- package/method/attachment/pre-check.js +0 -9
- package/method/attachment/remove.js +0 -11
- package/method/attachment/update.js +0 -7
- package/method/bulk/create.js +0 -46
- package/method/model/clear.js +0 -22
- package/method/model/create.js +0 -19
- package/method/model/drop.js +0 -19
- package/method/model/exists.js +0 -24
- package/method/record/clear.js +0 -24
- package/method/record/count.js +0 -44
- package/method/record/create.js +0 -71
- package/method/record/find-all.js +0 -25
- package/method/record/find-one.js +0 -56
- package/method/record/find.js +0 -52
- package/method/record/get.js +0 -47
- package/method/record/remove.js +0 -41
- package/method/record/update.js +0 -63
- package/method/record/upsert.js +0 -35
- package/method/sanitize/body.js +0 -70
- package/method/sanitize/date.js +0 -14
- package/method/sanitize/id.js +0 -7
- package/method/stat/aggregate.js +0 -23
- package/method/stat/histogram.js +0 -26
- package/method/validate.js +0 -157
package/lib/sanitize-schema.js
DELETED
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
import genericPropSanitizer from './generic-prop-sanitizer.js'
|
|
2
|
-
|
|
3
|
-
async function sanitizeFeature (item) {
|
|
4
|
-
const { get, isPlainObject, mergeWith, isArray } = this.lib._
|
|
5
|
-
for (const f of item.feature) {
|
|
6
|
-
const feature = get(this.feature, f.name) // source from collectFeature
|
|
7
|
-
if (!feature) this.fatal('unknownFeature%s%s', f.name, item.name)
|
|
8
|
-
let [ns, path] = f.name.split('.')
|
|
9
|
-
if (!path) ns = this.name
|
|
10
|
-
const input = await feature.call(this.app[ns], f)
|
|
11
|
-
let props = input.properties
|
|
12
|
-
if (isPlainObject(props)) props = [props]
|
|
13
|
-
if (props.length > 0) item.properties.push(...props)
|
|
14
|
-
item.globalRules = item.globalRules ?? []
|
|
15
|
-
if (input.globalRules) {
|
|
16
|
-
item.globalRules = mergeWith(item.globalRules, input.globalRules, (oval, sval) => {
|
|
17
|
-
if (isArray(oval)) return oval.concat(sval)
|
|
18
|
-
})
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async function sanitizeIndexes (item) {
|
|
24
|
-
const { generateId } = this.app.bajo
|
|
25
|
-
for (const idx of item.indexes) {
|
|
26
|
-
if (!idx.name) idx.name = `${(item.modelName ?? item.name).toUpperCase()}_${generateId()}`
|
|
27
|
-
if (!(typeof idx.fields === 'string' || Array.isArray(idx.fields))) this.fatal('onlyAcceptFields%s%s', 'indexes', item.name)
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
async function sanitizeFullText (item) {
|
|
32
|
-
for (const f of item.fullText.fields ?? []) {
|
|
33
|
-
const prop = item.properties.find(p => p.name === f)
|
|
34
|
-
if (!prop) this.fatal('invalidFieldName%s%s', f, item.name)
|
|
35
|
-
// if (prop.type !== 'text') fatal.call(this, 'Fulltext index only available for field type \'text\' in \'%s@%s\'', f, item.name)
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
async function sanitizeSchema (items) {
|
|
40
|
-
const { defaultsDeep } = this.lib.aneka
|
|
41
|
-
const { freeze, fatal, importModule, join, breakNsPath, runHook } = this.app.bajo
|
|
42
|
-
const { isEmpty, orderBy, map, keys, findIndex, find, each, isString, get, isPlainObject, camelCase, uniq, filter } = this.lib._
|
|
43
|
-
const propTypes = keys(this.propType)
|
|
44
|
-
const schemas = []
|
|
45
|
-
this.log.debug('loadingDbSchemas')
|
|
46
|
-
for (const k in items) {
|
|
47
|
-
this.log.trace('- %s (%d)', k, keys(items[k]).length)
|
|
48
|
-
for (const f in items[k]) {
|
|
49
|
-
const item = items[k][f]
|
|
50
|
-
item.cacheable = item.cacheable ?? true
|
|
51
|
-
await runHook(`dobo.${camelCase(item.name)}:beforeSanitizeSchema`, item)
|
|
52
|
-
const conn = find(this.connections, { name: item.connection })
|
|
53
|
-
if (!conn) fatal.call(this, 'Unknown connection \'%s@%s\'', item.name, item.connection)
|
|
54
|
-
item.fullText = item.fullText ?? { fields: [] }
|
|
55
|
-
item.indexes = item.indexes ?? []
|
|
56
|
-
let { ns, path: type } = breakNsPath(conn.type)
|
|
57
|
-
if (isEmpty(type)) type = conn.type
|
|
58
|
-
const driver = find(this.drivers, { type, ns, driver: conn.driver })
|
|
59
|
-
if (driver.lowerCaseModel) item.name = item.name.toLowerCase()
|
|
60
|
-
let file = `${ns}:/extend/${this.name}/lib/${type}/prop-sanitizer.js`
|
|
61
|
-
let propSanitizer = await importModule(file)
|
|
62
|
-
if (!propSanitizer) propSanitizer = genericPropSanitizer
|
|
63
|
-
for (const idx in item.properties) {
|
|
64
|
-
let prop = item.properties[idx]
|
|
65
|
-
if (isString(prop)) {
|
|
66
|
-
let [name, type, maxLength, index, required] = prop.split(':')
|
|
67
|
-
if (!type) type = 'string'
|
|
68
|
-
maxLength = maxLength ?? 255
|
|
69
|
-
prop = { name, type }
|
|
70
|
-
if (type === 'string') prop.maxLength = parseInt(maxLength) || undefined
|
|
71
|
-
if (!isEmpty(index)) prop.index = { type: index === 'true' ? 'default' : index }
|
|
72
|
-
prop.required = required === 'true'
|
|
73
|
-
item.properties[idx] = prop
|
|
74
|
-
} else {
|
|
75
|
-
if (isString(prop.index)) prop.index = { type: prop.index }
|
|
76
|
-
else if (prop.index === true) prop.index = { type: 'default' }
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
const idx = findIndex(item.properties, { name: 'id' })
|
|
80
|
-
if (idx === -1) item.properties.unshift(driver.idField)
|
|
81
|
-
item.feature = item.feature ?? []
|
|
82
|
-
await sanitizeFeature.call(this, item)
|
|
83
|
-
item.disabled = item.disabled ?? []
|
|
84
|
-
if (item.disabled === 'all') item.disabled = ['find', 'get', 'create', 'update', 'remove']
|
|
85
|
-
else if (item.disabled === 'readonly') item.disabled = ['create', 'update', 'remove']
|
|
86
|
-
for (const idx in item.properties) {
|
|
87
|
-
let prop = item.properties[idx]
|
|
88
|
-
if (!prop.type) {
|
|
89
|
-
prop.type = 'string'
|
|
90
|
-
prop.maxLength = 255
|
|
91
|
-
}
|
|
92
|
-
if (!propTypes.includes(prop.type)) {
|
|
93
|
-
let success = false
|
|
94
|
-
const feature = get(this.feature, isPlainObject(prop.type) ? prop.type.name : prop.type)
|
|
95
|
-
if (feature) {
|
|
96
|
-
let opts = { fieldName: prop.name }
|
|
97
|
-
if (isPlainObject(prop.type)) opts = defaultsDeep(opts, prop.type)
|
|
98
|
-
else opts.name = prop.type
|
|
99
|
-
const feat = find(item.feature, opts)
|
|
100
|
-
if (!feat) item.feature.push(opts)
|
|
101
|
-
const input = await feature.call(this, opts)
|
|
102
|
-
if (input.properties && input.properties.length > 0) {
|
|
103
|
-
prop = input.properties[0]
|
|
104
|
-
success = true
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
if (!success) fatal.call(this, 'Unsupported property type \'%s@%s\' in \'%s\'', prop.type, prop.name, item.name)
|
|
108
|
-
}
|
|
109
|
-
if (prop.index) {
|
|
110
|
-
if (prop.index === 'unique') prop.index = { type: 'unique' }
|
|
111
|
-
else if (prop.index === 'fulltext') prop.index = { type: 'fulltext' }
|
|
112
|
-
else if (prop.index === 'primary') prop.index = { type: 'primary' }
|
|
113
|
-
else if (prop.index === true) prop.index = { type: 'default' }
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
await propSanitizer.call(this, { prop, schema: item, connection: conn, driver })
|
|
117
|
-
if (prop.index && prop.index.type === 'primary' && prop.name !== 'id') fatal.call(this, 'Primary index should only be used for \'id\' field')
|
|
118
|
-
if (prop.index && prop.index.type === 'fulltext') {
|
|
119
|
-
item.fullText.fields.push(prop.name)
|
|
120
|
-
prop.index.type = 'default'
|
|
121
|
-
}
|
|
122
|
-
item.properties[idx] = prop
|
|
123
|
-
}
|
|
124
|
-
await sanitizeIndexes.call(this, item)
|
|
125
|
-
await sanitizeFullText.call(this, item)
|
|
126
|
-
const all = []
|
|
127
|
-
each(item.properties, p => {
|
|
128
|
-
if (!all.includes(p.name)) all.push(p.name)
|
|
129
|
-
else fatal.call(this, 'Field \'%s@%s\' should be used only once', p.name, item.name)
|
|
130
|
-
})
|
|
131
|
-
file = `${ns}:/extend/${this.name}/lib/${type}/schema-sanitizer.js`
|
|
132
|
-
const schemaSanitizer = await importModule(file)
|
|
133
|
-
if (schemaSanitizer) await schemaSanitizer.call(this, { schema: item, connection: conn, driver })
|
|
134
|
-
schemas.push(item)
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
for (const i in schemas) {
|
|
138
|
-
const schema = schemas[i]
|
|
139
|
-
const sortables = []
|
|
140
|
-
const hidden = []
|
|
141
|
-
for (const i2 in schema.properties) {
|
|
142
|
-
const prop = schema.properties[i2]
|
|
143
|
-
if (prop.type !== 'string') delete prop.maxLength
|
|
144
|
-
if (prop.rel) {
|
|
145
|
-
for (const key in prop.rel) {
|
|
146
|
-
let def = prop.rel[key]
|
|
147
|
-
if (isString(def)) {
|
|
148
|
-
const [rschema, rfield] = def.split(':')
|
|
149
|
-
def = { schema: rschema, propName: rfield }
|
|
150
|
-
}
|
|
151
|
-
def.type = def.type ?? 'one-to-one'
|
|
152
|
-
const rel = find(schemas, { name: def.schema })
|
|
153
|
-
if (!rel) {
|
|
154
|
-
fatal.call(this, 'No schema found for relationship \'%s@%s:%s\'', `${def.schema}:${def.propName}`, schema.name, prop.name)
|
|
155
|
-
}
|
|
156
|
-
const rprop = find(rel.properties, { name: def.propName })
|
|
157
|
-
if (!rprop) fatal.call(this, 'No property found for relationship \'%s@%s:%s\'', `${def.schema}:${def.propName}`, schema.name, prop.name)
|
|
158
|
-
def.fields = def.fields ?? '*'
|
|
159
|
-
if (['*', 'all'].includes(def.fields)) {
|
|
160
|
-
const relschema = find(schemas, { name: def.schema })
|
|
161
|
-
def.fields = relschema.properties.map(p => p.name)
|
|
162
|
-
}
|
|
163
|
-
if (def.fields.length > 0 && !def.fields.includes('id')) def.fields.push('id')
|
|
164
|
-
for (const f of def.fields) {
|
|
165
|
-
const p = find(rel.properties, { name: f })
|
|
166
|
-
if (!p) fatal.call(this, 'Unknown property for field \'%s\' in relationship \'%s@%s:%s\'', p, `${def.schema}:${def.propName}`, schema.name, prop.name)
|
|
167
|
-
}
|
|
168
|
-
prop.type = rprop.type
|
|
169
|
-
if (rprop.type === 'string') prop.maxLength = rprop.maxLength
|
|
170
|
-
else {
|
|
171
|
-
delete prop.maxLength
|
|
172
|
-
delete prop.minLength
|
|
173
|
-
}
|
|
174
|
-
prop.rel[key] = def
|
|
175
|
-
}
|
|
176
|
-
// TODO: propSanitizer must be called again
|
|
177
|
-
}
|
|
178
|
-
if (prop.hidden) hidden.push(prop.name)
|
|
179
|
-
if (prop.index) sortables.push(prop.name)
|
|
180
|
-
delete prop.hidden
|
|
181
|
-
schema.properties[i2] = prop
|
|
182
|
-
}
|
|
183
|
-
// sortables
|
|
184
|
-
each(schema.indexes, item => {
|
|
185
|
-
sortables.push(...item.fields)
|
|
186
|
-
})
|
|
187
|
-
// hidden
|
|
188
|
-
schema.hidden = uniq(filter((schema.hidden ?? []).concat(hidden), item => map(schema.properties, 'name').includes(item)))
|
|
189
|
-
schema.sortables = sortables
|
|
190
|
-
await runHook(`dobo.${camelCase(schema.name)}:afterSanitizeSchema`, schema)
|
|
191
|
-
}
|
|
192
|
-
this.schemas = orderBy(schemas, ['buildLevel', 'name'])
|
|
193
|
-
freeze(this.schemas)
|
|
194
|
-
this.log.debug('loadedSchemas%s', join(map(this.schemas, 'name')))
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
export default sanitizeSchema
|
package/lib/single-rel-rows.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
async function singleRelRows ({ schema, record, options = {} }) {
|
|
2
|
-
const { isSet } = this.lib.aneka
|
|
3
|
-
const { find } = this.lib._
|
|
4
|
-
const props = schema.properties.filter(p => isSet(p.rel) && !(options.hidden ?? []).includes(p.name))
|
|
5
|
-
const rels = {}
|
|
6
|
-
options.rels = options.rels ?? []
|
|
7
|
-
if (props.length > 0) {
|
|
8
|
-
for (const prop of props) {
|
|
9
|
-
for (const key in prop.rel) {
|
|
10
|
-
if (!((typeof options.rels === 'string' && ['*', 'all'].includes(options.rels)) || options.rels.includes(key))) continue
|
|
11
|
-
const val = prop.rel[key]
|
|
12
|
-
if (val.fields.length === 0) continue
|
|
13
|
-
const relschema = this.getSchema(val.schema)
|
|
14
|
-
const relFilter = options.filter ?? {}
|
|
15
|
-
const query = {}
|
|
16
|
-
query[val.propName] = record[prop.name]
|
|
17
|
-
if (val.propName === 'id') {
|
|
18
|
-
const idField = find(relschema.properties, { name: 'id' })
|
|
19
|
-
if (idField.type === 'integer') query[val.propName] = parseInt(query[val.propName])
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
relFilter.query = query
|
|
23
|
-
const relOptions = { dataOnly: true, rels: [] }
|
|
24
|
-
const results = await this.recordFind(relschema.name, relFilter, relOptions)
|
|
25
|
-
const fields = [...val.fields]
|
|
26
|
-
if (!fields.includes(prop.name)) fields.push(prop.name)
|
|
27
|
-
const data = []
|
|
28
|
-
for (const res of results) {
|
|
29
|
-
data.push(await this.pickRecord({ record: res, fields, schema: relschema }))
|
|
30
|
-
}
|
|
31
|
-
rels[key] = ['one-to-one'].includes(val.type) ? data[0] : data
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
record._rel = rels
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export default singleRelRows
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import path from 'path'
|
|
2
|
-
|
|
3
|
-
async function copyUploaded (name, id, options = {}) {
|
|
4
|
-
const { fs } = this.lib
|
|
5
|
-
const { req, setField, setFile, mimeType, stats, silent = true } = options
|
|
6
|
-
name = this.attachmentPreCheck(name)
|
|
7
|
-
if (!name) {
|
|
8
|
-
if (silent) return
|
|
9
|
-
throw this.error('isMissing%s', this.print.write('field.name'))
|
|
10
|
-
}
|
|
11
|
-
if (!this.app.waibu) {
|
|
12
|
-
if (silent) return
|
|
13
|
-
throw this.error('missingPlugin%s', 'Waibu')
|
|
14
|
-
}
|
|
15
|
-
const { dir, files } = await this.app.waibu.getUploadedFiles(req.id, false, true)
|
|
16
|
-
const result = []
|
|
17
|
-
if (files.length === 0) return result
|
|
18
|
-
for (const f of files) {
|
|
19
|
-
let [field, ...parts] = path.basename(f).split('@')
|
|
20
|
-
if (parts.length === 0) continue
|
|
21
|
-
field = setField ?? field
|
|
22
|
-
const file = setFile ?? parts.join('@')
|
|
23
|
-
const opts = { source: f, field, file, mimeType, stats, req, silent: true }
|
|
24
|
-
const rec = await this.attachmentCreate(name, id, opts)
|
|
25
|
-
if (!rec) continue
|
|
26
|
-
delete rec.dir
|
|
27
|
-
result.push(rec)
|
|
28
|
-
if (setField || setFile) break
|
|
29
|
-
}
|
|
30
|
-
fs.removeSync(dir)
|
|
31
|
-
return result
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export default copyUploaded
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import mergeAttachmentInfo from '../../lib/merge-attachment-info.js'
|
|
2
|
-
|
|
3
|
-
async function create (name, id, options = {}) {
|
|
4
|
-
const { fs } = this.lib
|
|
5
|
-
const { isEmpty } = this.lib._
|
|
6
|
-
name = this.attachmentPreCheck(name)
|
|
7
|
-
if (!name) return
|
|
8
|
-
const { source, field = 'file', file } = options
|
|
9
|
-
if (isEmpty(file)) return
|
|
10
|
-
if (!source) throw this.error('isMissing%s', this.print.write('field.source'))
|
|
11
|
-
const baseDir = await this.attachmentGetPath(name, id, field, file, { dirOnly: true })
|
|
12
|
-
const { fullPath, stats, mimeType, req } = options
|
|
13
|
-
|
|
14
|
-
let dir = `${baseDir}/${field}`
|
|
15
|
-
if ((field || '').endsWith('[]')) dir = `${baseDir}/${field.replace('[]', '')}`
|
|
16
|
-
const dest = `${dir}/${file}`.replaceAll('//', '/')
|
|
17
|
-
await fs.ensureDir(dir)
|
|
18
|
-
await fs.copy(source, dest)
|
|
19
|
-
const rec = {
|
|
20
|
-
field: field === '' ? undefined : field,
|
|
21
|
-
dir,
|
|
22
|
-
file
|
|
23
|
-
}
|
|
24
|
-
await mergeAttachmentInfo.call(this, rec, dest, { mimeType, fullPath, stats })
|
|
25
|
-
if (!options.silent && req && req.flash) req.flash('notify', req.t('attachmentUploaded'))
|
|
26
|
-
return rec
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export default create
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import mergeAttachmentInfo from '../../lib/merge-attachment-info.js'
|
|
2
|
-
|
|
3
|
-
async function find (name, id, options = {}) {
|
|
4
|
-
const { fastGlob, fs } = this.lib
|
|
5
|
-
const { getPluginDataDir } = this.app.bajo
|
|
6
|
-
name = this.attachmentPreCheck(name)
|
|
7
|
-
if (!name) return
|
|
8
|
-
const dir = `${getPluginDataDir(this.name)}/attachment/${name}/${id}`
|
|
9
|
-
if (!fs.existsSync(dir)) return []
|
|
10
|
-
const files = await fastGlob(`${dir}/**/*`)
|
|
11
|
-
const { fullPath, stats, mimeType } = options
|
|
12
|
-
const recs = []
|
|
13
|
-
for (const f of files) {
|
|
14
|
-
const item = f.replace(dir, '')
|
|
15
|
-
let [, field, file] = item.split('/')
|
|
16
|
-
if (!file) {
|
|
17
|
-
file = field
|
|
18
|
-
field = null
|
|
19
|
-
}
|
|
20
|
-
const rec = { field, file }
|
|
21
|
-
await mergeAttachmentInfo.call(this, rec, f, { mimeType, fullPath, stats })
|
|
22
|
-
recs.push(rec)
|
|
23
|
-
}
|
|
24
|
-
return recs
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export default find
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
async function getPath (name, id, field, file, options = {}) {
|
|
2
|
-
const { getPluginDataDir } = this.app.bajo
|
|
3
|
-
const { pascalCase } = this.lib.aneka
|
|
4
|
-
const { fs } = this.lib
|
|
5
|
-
const dir = `${getPluginDataDir(this.name)}/attachment/${pascalCase(name)}/${id}`
|
|
6
|
-
if (options.dirOnly) return dir
|
|
7
|
-
const path = field ? `${dir}/${field}/${file}` : `${dir}/${file}`
|
|
8
|
-
if (!fs.existsSync(path)) throw this.error('notFound')
|
|
9
|
-
return path
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export default getPath
|
package/method/attachment/get.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
async function get (name, id, field, file, options = {}) {
|
|
2
|
-
name = this.attachmentPreCheck(name)
|
|
3
|
-
if (!name) return
|
|
4
|
-
const { find } = this.lib._
|
|
5
|
-
const all = await this.attachmentFind(name, id, options)
|
|
6
|
-
if (field === 'null') field = null
|
|
7
|
-
const data = find(all, { field, file })
|
|
8
|
-
if (!data) throw this.error('notFound', { statusCode: 404 })
|
|
9
|
-
return data
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export default get
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
async function remove (name, id, field, file, options = {}) {
|
|
2
|
-
const { fs } = this.lib
|
|
3
|
-
name = this.attachmentPreCheck(name)
|
|
4
|
-
if (!name) return
|
|
5
|
-
const path = await this.attachmentGetPath(name, id, field, file)
|
|
6
|
-
const { req } = options
|
|
7
|
-
await fs.remove(path)
|
|
8
|
-
if (req && req.flash) req.flash('notify', req.t('attachmentRemoved'))
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export default remove
|
package/method/bulk/create.js
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import buildBulkAction from '../../lib/build-bulk-action.js'
|
|
2
|
-
import execValidation from '../../lib/exec-validation.js'
|
|
3
|
-
import execFeatureHook from '../../lib/exec-feature-hook.js'
|
|
4
|
-
|
|
5
|
-
async function create (name, inputs, options) {
|
|
6
|
-
const { isSet } = this.lib.aneka
|
|
7
|
-
const { generateId, runHook } = this.app.bajo
|
|
8
|
-
const { clearModel } = this.cache ?? {}
|
|
9
|
-
const { find } = this.lib._
|
|
10
|
-
options.dataOnly = options.dataOnly ?? true
|
|
11
|
-
options.truncateString = options.truncateString ?? true
|
|
12
|
-
const { noHook, noValidation } = options
|
|
13
|
-
await this.modelExists(name, true)
|
|
14
|
-
const { handler, schema } = await buildBulkAction.call(this, name, 'create', options)
|
|
15
|
-
const idField = find(schema.properties, { name: 'id' })
|
|
16
|
-
const bodies = [...inputs]
|
|
17
|
-
for (let b of bodies) {
|
|
18
|
-
b.id = b.id ?? generateId(idField.type === 'integer' ? 'int' : undefined)
|
|
19
|
-
b = await this.sanitizeBody({ body: b, schema, strict: true })
|
|
20
|
-
if (!noValidation) b = await execValidation.call(this, { noHook, name, b, options })
|
|
21
|
-
}
|
|
22
|
-
if (!noHook) {
|
|
23
|
-
await runHook(`${this.name}:beforeBulkCreate`, name, bodies, options)
|
|
24
|
-
await runHook(`${this.name}.${name}:beforeBulkCreate`, bodies, options)
|
|
25
|
-
}
|
|
26
|
-
for (const idx in bodies) {
|
|
27
|
-
await execFeatureHook.call(this, 'beforeCreate', { schema, body: bodies[idx] })
|
|
28
|
-
// TODO: check unique?
|
|
29
|
-
for (const k in bodies[idx]) {
|
|
30
|
-
if (bodies[idx][k] === undefined) continue
|
|
31
|
-
const prop = find(schema.properties, { name: k })
|
|
32
|
-
if (options.truncateString && isSet(bodies[idx][k]) && prop && ['string', 'text'].includes(prop.type)) bodies[idx][k] = bodies[idx][k].slice(0, prop.maxLength)
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
await handler.call(this, { schema, bodies, options })
|
|
36
|
-
for (const idx in bodies) {
|
|
37
|
-
await execFeatureHook.call(this, 'afterCreate', { schema, body: bodies[idx] })
|
|
38
|
-
}
|
|
39
|
-
if (!noHook) {
|
|
40
|
-
await runHook(`${this.name}.${name}:afterBulkCreate`, bodies, options)
|
|
41
|
-
await runHook(`${this.name}:afterBulkCreate`, name, bodies, options)
|
|
42
|
-
}
|
|
43
|
-
if (clearModel) await clearModel({ model: name })
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export default create
|
package/method/model/clear.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import resolveMethod from '../../lib/resolve-method.js'
|
|
2
|
-
|
|
3
|
-
async function clear (name, options = {}) {
|
|
4
|
-
const { runHook } = this.app.bajo
|
|
5
|
-
const { camelCase } = this.lib._
|
|
6
|
-
|
|
7
|
-
await this.modelExists(name, true)
|
|
8
|
-
const { noHook } = options
|
|
9
|
-
const { handler, schema, driver } = await resolveMethod.call(this, name, 'model-clear', options)
|
|
10
|
-
if (!noHook) {
|
|
11
|
-
await runHook(`${this.name}:beforeModelClear`, schema, options)
|
|
12
|
-
await runHook(`${this.name}.${camelCase(name)}:beforeModelClear`, options)
|
|
13
|
-
}
|
|
14
|
-
const resp = await handler.call(this.app[driver.ns], { schema, options })
|
|
15
|
-
if (!noHook) {
|
|
16
|
-
await runHook(`${this.name}.${camelCase(name)}:afterModelClear`, options, resp)
|
|
17
|
-
await runHook(`${this.name}:afterModelClear`, schema, options, resp)
|
|
18
|
-
}
|
|
19
|
-
return resp
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export default clear
|
package/method/model/create.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import resolveMethod from '../../lib/resolve-method.js'
|
|
2
|
-
|
|
3
|
-
async function create (name, options = {}) {
|
|
4
|
-
const { runHook } = this.app.bajo
|
|
5
|
-
const { camelCase } = this.lib._
|
|
6
|
-
|
|
7
|
-
const { handler, schema, driver } = await resolveMethod.call(this, name, 'model-create', options)
|
|
8
|
-
if (!options.noHook) {
|
|
9
|
-
await runHook(`${this.name}:beforeModelCreate`, schema, options)
|
|
10
|
-
await runHook(`${this.name}.${camelCase(name)}:beforeModelCreate`, options)
|
|
11
|
-
}
|
|
12
|
-
await handler.call(this.app[driver.ns], { schema, options })
|
|
13
|
-
if (!options.noHook) {
|
|
14
|
-
await runHook(`${this.name}.${camelCase(name)}:afterModelCreate`, options)
|
|
15
|
-
await runHook(`${this.name}:afterModelCreate`, schema, options)
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export default create
|
package/method/model/drop.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import resolveMethod from '../../lib/resolve-method.js'
|
|
2
|
-
|
|
3
|
-
async function drop (name, options = {}) {
|
|
4
|
-
const { runHook } = this.app.bajo
|
|
5
|
-
const { camelCase } = this.lib._
|
|
6
|
-
const { handler, schema, driver } = await resolveMethod.call(this, name, 'model-drop', options)
|
|
7
|
-
|
|
8
|
-
if (!options.noHook) {
|
|
9
|
-
await runHook(`${this.name}:beforeModelDrop`, schema, options)
|
|
10
|
-
await runHook(`${this.name}.${camelCase(name)}:beforeModelDrop`, options)
|
|
11
|
-
}
|
|
12
|
-
await handler.call(this.app[driver.ns], { schema, options })
|
|
13
|
-
if (!options.noHook) {
|
|
14
|
-
await runHook(`${this.name}.${camelCase(name)}:afterModelDrop`, options)
|
|
15
|
-
await runHook(`${this.name}:afterModelDrop`, schema, options)
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export default drop
|
package/method/model/exists.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import resolveMethod from '../../lib/resolve-method.js'
|
|
2
|
-
|
|
3
|
-
const cache = {}
|
|
4
|
-
|
|
5
|
-
async function exists (name, thrown, options = {}) {
|
|
6
|
-
if (cache[name]) return cache[name]
|
|
7
|
-
const { runHook } = this.app.bajo
|
|
8
|
-
const { camelCase } = this.lib._
|
|
9
|
-
const { handler, schema, driver } = await resolveMethod.call(this, name, 'model-exists', options)
|
|
10
|
-
if (!options.noHook) {
|
|
11
|
-
await runHook(`${this.name}:beforeModelExists`, schema, options)
|
|
12
|
-
await runHook(`${this.name}.${camelCase(name)}:beforeModelExists`, options)
|
|
13
|
-
}
|
|
14
|
-
const exist = await handler.call(this.app[driver.ns], { schema, options })
|
|
15
|
-
if (!options.noHook) {
|
|
16
|
-
await runHook(`${this.name}.${camelCase(name)}:afterModelExists`, exist, options)
|
|
17
|
-
await runHook(`${this.name}:afterModelExists`, schema, exist, options)
|
|
18
|
-
}
|
|
19
|
-
if (!exist && thrown) throw this.error('modelNotExists%s', name)
|
|
20
|
-
cache[name] = exist
|
|
21
|
-
return exist
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export default exists
|
package/method/record/clear.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import resolveMethod from '../../lib/resolve-method.js'
|
|
2
|
-
|
|
3
|
-
async function clear (name, opts = {}) {
|
|
4
|
-
const { runHook } = this.app.bajo
|
|
5
|
-
await this.modelExists(name, true)
|
|
6
|
-
const { cloneDeep, camelCase, omit } = this.lib._
|
|
7
|
-
const options = cloneDeep(omit(opts, ['req', 'reply']))
|
|
8
|
-
options.req = opts.req
|
|
9
|
-
options.reply = opts.reply
|
|
10
|
-
const { noHook } = options
|
|
11
|
-
const { handler, schema, driver } = await resolveMethod.call(this, name, 'record-clear', options)
|
|
12
|
-
if (!noHook) {
|
|
13
|
-
await runHook(`${this.name}:beforeRecordClear`, name, options)
|
|
14
|
-
await runHook(`${this.name}.${camelCase(name)}:beforeRecordClear`, options)
|
|
15
|
-
}
|
|
16
|
-
const resp = await handler.call(this.app[driver.ns], { schema, options })
|
|
17
|
-
if (!noHook) {
|
|
18
|
-
await runHook(`${this.name}.${camelCase(name)}:afterRecordClear`, options, resp)
|
|
19
|
-
await runHook(`${this.name}:afterRecordClear`, name, options, resp)
|
|
20
|
-
}
|
|
21
|
-
return resp
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export default clear
|
package/method/record/count.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import resolveMethod from '../../lib/resolve-method.js'
|
|
2
|
-
import execFeatureHook from '../../lib/exec-feature-hook.js'
|
|
3
|
-
|
|
4
|
-
async function count (name, filter = {}, opts = {}) {
|
|
5
|
-
const { runHook } = this.app.bajo
|
|
6
|
-
const { get, set } = this.cache ?? {}
|
|
7
|
-
const { cloneDeep, camelCase, omit } = this.lib._
|
|
8
|
-
delete opts.record
|
|
9
|
-
const options = cloneDeep(omit(opts, ['req', 'reply']))
|
|
10
|
-
options.req = opts.req
|
|
11
|
-
options.reply = opts.reply
|
|
12
|
-
options.dataOnly = options.dataOnly ?? true
|
|
13
|
-
let { dataOnly, noHook, noCache, noFeatureHook } = options
|
|
14
|
-
options.dataOnly = false
|
|
15
|
-
await this.modelExists(name, true)
|
|
16
|
-
const { handler, schema, driver } = await resolveMethod.call(this, name, 'record-count', options)
|
|
17
|
-
if (!schema.cacheable) noCache = true
|
|
18
|
-
filter.query = this.buildQuery({ filter, schema, options }) ?? {}
|
|
19
|
-
if (options.queryHandler) filter.query = await options.queryHandler.call(opts.req ? this.app[opts.req.ns] : this, filter.query, opts.req)
|
|
20
|
-
filter.match = this.buildMatch({ input: filter.match, schema, options }) ?? {}
|
|
21
|
-
if (!noHook) {
|
|
22
|
-
await runHook(`${this.name}:beforeRecordCount`, name, filter, options)
|
|
23
|
-
await runHook(`${this.name}.${camelCase(name)}:beforeRecordCount`, filter, options)
|
|
24
|
-
}
|
|
25
|
-
if (!noFeatureHook) await execFeatureHook.call(this, 'beforeCount', { schema, filter, options })
|
|
26
|
-
if (get && !noCache && !options.record) {
|
|
27
|
-
const cachedResult = await get({ model: name, filter, options })
|
|
28
|
-
if (cachedResult) {
|
|
29
|
-
cachedResult.cached = true
|
|
30
|
-
return dataOnly ? cachedResult.data : cachedResult
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
const record = options.record ?? (await handler.call(this.app[driver.ns], { schema, filter, options }))
|
|
34
|
-
delete options.record
|
|
35
|
-
if (!noHook) {
|
|
36
|
-
await runHook(`${this.name}.${camelCase(name)}:afterRecordCount`, filter, options, record)
|
|
37
|
-
await runHook(`${this.name}:afterRecordCount`, name, filter, options, record)
|
|
38
|
-
}
|
|
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
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export default count
|
package/method/record/create.js
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import crypto from 'crypto'
|
|
2
|
-
import resolveMethod from '../../lib/resolve-method.js'
|
|
3
|
-
import checkUnique from '../../lib/check-unique.js'
|
|
4
|
-
import handleAttachmentUpload from '../../lib/handle-attachment-upload.js'
|
|
5
|
-
import execValidation from '../../lib/exec-validation.js'
|
|
6
|
-
import execFeatureHook from '../../lib/exec-feature-hook.js'
|
|
7
|
-
import singleRelRows from '../../lib/single-rel-rows.js'
|
|
8
|
-
|
|
9
|
-
async function create (name, input, opts = {}) {
|
|
10
|
-
const { generateId, runHook } = this.app.bajo
|
|
11
|
-
const { isSet } = this.lib.aneka
|
|
12
|
-
const { clearModel } = this.cache ?? {}
|
|
13
|
-
const { find, forOwn, cloneDeep, camelCase, omit, get, pick } = this.lib._
|
|
14
|
-
delete opts.record
|
|
15
|
-
const options = cloneDeep(omit(opts, ['req', 'reply']))
|
|
16
|
-
options.req = opts.req
|
|
17
|
-
options.reply = opts.reply
|
|
18
|
-
options.dataOnly = options.dataOnly ?? true
|
|
19
|
-
input = cloneDeep(input)
|
|
20
|
-
const { fields, dataOnly, noHook, noValidation, noCheckUnique, noFeatureHook, noResult, noSanitize, hidden, forceNoHidden } = options
|
|
21
|
-
options.truncateString = options.truncateString ?? true
|
|
22
|
-
options.dataOnly = false
|
|
23
|
-
await this.modelExists(name, true)
|
|
24
|
-
const { handler, schema, driver } = await resolveMethod.call(this, name, 'record-create', options)
|
|
25
|
-
const idField = find(schema.properties, { name: 'id' })
|
|
26
|
-
const extFields = get(options, 'validation.extFields', [])
|
|
27
|
-
let body = noSanitize ? cloneDeep(input) : await this.sanitizeBody({ body: input, schema, extFields, strict: true })
|
|
28
|
-
if (!noHook) {
|
|
29
|
-
await runHook(`${this.name}:beforeRecordCreate`, name, body, options)
|
|
30
|
-
await runHook(`${this.name}.${camelCase(name)}:beforeRecordCreate`, body, options)
|
|
31
|
-
}
|
|
32
|
-
if (!isSet(body.id)) {
|
|
33
|
-
if (idField.type === 'string') {
|
|
34
|
-
if (!options.checksumId) body.id = generateId()
|
|
35
|
-
else {
|
|
36
|
-
if (options.checksumId === true) options.checksumId = Object.keys(body)
|
|
37
|
-
const checksum = pick(body, options.checksumId)
|
|
38
|
-
body.id = crypto.createHash('md5').update(JSON.stringify(checksum)).digest('hex')
|
|
39
|
-
}
|
|
40
|
-
} else if (['integer', 'smallint'].includes(idField.type) && !idField.autoInc) input.id = generateId('int')
|
|
41
|
-
}
|
|
42
|
-
if (!noValidation) body = await execValidation.call(this, { name, body, options })
|
|
43
|
-
if (isSet(body.id) && !noCheckUnique) await checkUnique.call(this, { schema, body })
|
|
44
|
-
const nbody = {}
|
|
45
|
-
forOwn(body, (v, k) => {
|
|
46
|
-
if (v === undefined) return undefined
|
|
47
|
-
const prop = find(schema.properties, { name: k })
|
|
48
|
-
if (!prop) return undefined
|
|
49
|
-
if (options.truncateString && isSet(v) && ['string', 'text'].includes(prop.type)) v = v.slice(0, prop.maxLength)
|
|
50
|
-
nbody[k] = v
|
|
51
|
-
})
|
|
52
|
-
if (!noFeatureHook) await execFeatureHook.call(this, 'beforeCreate', { schema, body: nbody, options })
|
|
53
|
-
const record = options.record ?? (await handler.call(this.app[driver.ns], { schema, body: nbody, options }))
|
|
54
|
-
delete options.record
|
|
55
|
-
if (isSet(options.rels)) await singleRelRows.call(this, { schema, record: record.data, options })
|
|
56
|
-
if (options.req) {
|
|
57
|
-
if (options.req.file) await handleAttachmentUpload.call(this, { name: schema.name, id: body.id, body, options, action: 'create' })
|
|
58
|
-
if (options.req.flash && !options.noFlash) options.req.flash('notify', options.req.t('recordCreated'))
|
|
59
|
-
}
|
|
60
|
-
if (clearModel) await clearModel({ model: name, body: nbody, options, record })
|
|
61
|
-
if (noResult) return
|
|
62
|
-
record.data = await this.pickRecord({ record: record.data, fields, schema, hidden, forceNoHidden })
|
|
63
|
-
if (!noHook) {
|
|
64
|
-
await runHook(`${this.name}.${camelCase(name)}:afterRecordCreate`, nbody, options, record)
|
|
65
|
-
await runHook(`${this.name}:afterRecordCreate`, name, nbody, options, record)
|
|
66
|
-
}
|
|
67
|
-
if (!noFeatureHook) await execFeatureHook.call(this, 'afterCreate', { schema, body: nbody, options, record })
|
|
68
|
-
return dataOnly ? record.data : record
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export default create
|