dobo 2.21.0 → 2.22.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/extend/bajo/intl/en-US.json +4 -2
- package/extend/bajo/intl/id.json +4 -2
- package/extend/dobo/feature/image.js +25 -0
- package/extend/waibuMpa/route/attachment/@model/@id/@field/@file.js +29 -7
- package/index.js +4 -1
- package/lib/collect-models.js +7 -6
- package/lib/factory/driver.js +2 -2
- package/lib/factory/model/_util.js +2 -2
- package/lib/factory/model/create-attachment.js +5 -0
- package/lib/factory/model/create-record.js +2 -2
- package/lib/factory/model/find-all-record.js +1 -1
- package/lib/factory/model/find-record.js +1 -2
- package/lib/factory/model/get-record.js +1 -1
- package/lib/factory/model/remove-attachment.js +12 -3
- package/lib/factory/model/remove-record.js +1 -1
- package/lib/factory/model/sanitize-record.js +2 -2
- package/lib/factory/model/update-record.js +2 -2
- package/lib/factory/model/upsert-record.js +2 -2
- package/package.json +1 -1
- package/wiki/CHANGES.md +19 -0
|
@@ -150,7 +150,7 @@
|
|
|
150
150
|
"maxPageError%s%s": "Page number (%s) above the allowed threshold (%s)",
|
|
151
151
|
"duplicateRefKeys%s%s": "Duplicate reference keys found in '%s' (%s)",
|
|
152
152
|
"sanitizeBodyError": "Error sanitizing body",
|
|
153
|
-
"virtualFieldIn%s%s": "Virtual field can't be used in '%s' on %s",
|
|
153
|
+
"virtualFieldIn%s%s%s": "Virtual field '%s' can't be used in '%s' on %s",
|
|
154
154
|
"field": {
|
|
155
155
|
"id": "ID",
|
|
156
156
|
"code": "Kode",
|
|
@@ -215,7 +215,9 @@
|
|
|
215
215
|
"type": "Type",
|
|
216
216
|
"model": "Model",
|
|
217
217
|
"age": "Age",
|
|
218
|
-
"provider": "Provider"
|
|
218
|
+
"provider": "Provider",
|
|
219
|
+
"image": "Image",
|
|
220
|
+
"attachment": "Attachment"
|
|
219
221
|
},
|
|
220
222
|
"validationError": "Validation Error",
|
|
221
223
|
"validation": {
|
package/extend/bajo/intl/id.json
CHANGED
|
@@ -148,7 +148,7 @@
|
|
|
148
148
|
"maxPageError%s%s": "Nomor halaman (%s) melampaui batas yang diijinkan (%s)",
|
|
149
149
|
"duplicateRefKeys%s%s": "Ditemukan kunci referensi duplikat di '%s' (%s)",
|
|
150
150
|
"sanitizeBodyError": "Kesalahan saat sanitasi body",
|
|
151
|
-
"virtualFieldIn%s%s": "Kolom virtual tidak bisa digunakan di '%s' pada %s",
|
|
151
|
+
"virtualFieldIn%s%s%s": "Kolom virtual '%s' tidak bisa digunakan di '%s' pada %s",
|
|
152
152
|
"field": {
|
|
153
153
|
"id": "ID",
|
|
154
154
|
"code": "Kode",
|
|
@@ -213,7 +213,9 @@
|
|
|
213
213
|
"type": "Tipe",
|
|
214
214
|
"model": "Model",
|
|
215
215
|
"age": "Usia",
|
|
216
|
-
"provider": "Penyedia"
|
|
216
|
+
"provider": "Penyedia",
|
|
217
|
+
"image": "Gambar",
|
|
218
|
+
"attachment": "Lampiran"
|
|
217
219
|
},
|
|
218
220
|
"validationError": "Kesalahan Validasi",
|
|
219
221
|
"validation": {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
|
|
3
|
+
async function image (opts = {}) {
|
|
4
|
+
opts.field = opts.field ?? 'image'
|
|
5
|
+
opts.baseName = opts.baseName ?? true
|
|
6
|
+
opts.single = opts.single ?? true
|
|
7
|
+
return {
|
|
8
|
+
properties: {
|
|
9
|
+
name: opts.field,
|
|
10
|
+
type: 'string',
|
|
11
|
+
virtual: true,
|
|
12
|
+
getValue: async function (val, rec) {
|
|
13
|
+
const atts = await this.listAttachment({ id: rec.id })
|
|
14
|
+
if (atts.length === 0) return
|
|
15
|
+
let items = atts.filter(att => att.mimeType.startsWith('image/'))
|
|
16
|
+
if (opts.withLink && this.app.waibu) items = items.map(f => `<a href="${f.url}">${opts.baseName ? path.basename(f.file) : f.file}</a>`)
|
|
17
|
+
else if (opts.baseName) items = items.map(f => path.basename(f.file))
|
|
18
|
+
if (opts.single) return items[0]
|
|
19
|
+
return items
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default image
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
2
|
|
|
3
|
+
async function handleNotFound (req, reply) {
|
|
4
|
+
const { isEmpty, isString } = this.app.lib._
|
|
5
|
+
const { routePath } = this.app.waibu
|
|
6
|
+
if (!req.query.notfound) throw this.error('_notFound', { noContent: true })
|
|
7
|
+
const ext = path.extname(req.params.file)
|
|
8
|
+
const replacer = isString(req.query.notfound) ? req.query.notfound : `waibuStatic.asset:/not-found${isEmpty(ext) ? '.png' : ext}`
|
|
9
|
+
return reply.redirectTo(routePath(replacer))
|
|
10
|
+
}
|
|
11
|
+
|
|
3
12
|
async function attachment (req, reply) {
|
|
4
|
-
const {
|
|
13
|
+
const { isEmpty, find, last } = this.app.lib._
|
|
5
14
|
const { pascalCase } = this.app.lib.aneka
|
|
6
|
-
const {
|
|
15
|
+
const { createThumbnail } = this.app.bajoExtra ?? {}
|
|
7
16
|
const { fs } = this.app.lib
|
|
8
17
|
const mdl = this.app.dobo.getModel(req.params.model)
|
|
9
18
|
const type = req.query.type
|
|
@@ -20,11 +29,24 @@ async function attachment (req, reply) {
|
|
|
20
29
|
file === decodeURI(req.params.file)
|
|
21
30
|
})
|
|
22
31
|
}
|
|
23
|
-
if (!item)
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
|
|
32
|
+
if (!item) return await handleNotFound.call(this, req, reply)
|
|
33
|
+
if (req.query.thumbnail) {
|
|
34
|
+
const dir = path.dirname(item.file)
|
|
35
|
+
const ext = path.extname(item.file)
|
|
36
|
+
const base = path.basename(item.file, ext)
|
|
37
|
+
const dest = `${dir}/_tn/${base}-${req.query.thumbnail}.png`
|
|
38
|
+
if (createThumbnail && !fs.existsSync(dest)) {
|
|
39
|
+
const opts = {
|
|
40
|
+
dir: `${dir}/_tn`,
|
|
41
|
+
size: req.query.thumbnail,
|
|
42
|
+
silent: true,
|
|
43
|
+
format: '.png'
|
|
44
|
+
}
|
|
45
|
+
await createThumbnail(item.file, opts)
|
|
46
|
+
}
|
|
47
|
+
if (!fs.existsSync(dest)) await handleNotFound.call(this, req, reply)
|
|
48
|
+
item.mimeType = 'image/png'
|
|
49
|
+
item.file = dest
|
|
28
50
|
}
|
|
29
51
|
if (!isEmpty(item.mimeType)) reply.header('Content-Type', item.mimeType)
|
|
30
52
|
const stream = fs.createReadStream(item.file)
|
package/index.js
CHANGED
package/lib/collect-models.js
CHANGED
|
@@ -29,10 +29,11 @@ async function sanitizeProp (model, prop, indexes) {
|
|
|
29
29
|
})
|
|
30
30
|
} else if (!isString(prop.values)) delete prop.values
|
|
31
31
|
if (prop.hidden) model.hidden.push(prop.name)
|
|
32
|
+
if (prop.scanable) model.scanables.push(prop.scanable)
|
|
32
33
|
if (prop.virtual) {
|
|
33
34
|
const keys = Object.keys(propType)
|
|
34
35
|
if (!keys.includes(prop.type)) this.fatal('unknownPropType%s%s', `${prop.name}:${prop.type}`, model.name)
|
|
35
|
-
for (const key of ['required', 'rules', 'index', 'validator', 'ref', 'rulesMsg', 'immutable'
|
|
36
|
+
for (const key of ['required', 'rules', 'index', 'validator', 'ref', 'rulesMsg', 'immutable']) {
|
|
36
37
|
delete prop[key]
|
|
37
38
|
}
|
|
38
39
|
model.properties.push(prop)
|
|
@@ -160,7 +161,7 @@ export async function sanitizeRef (model, models) {
|
|
|
160
161
|
}
|
|
161
162
|
ref.field = ref.field ?? 'id'
|
|
162
163
|
ref.type = ref.type ?? '1:1'
|
|
163
|
-
ref.searchField = ref.searchField ??
|
|
164
|
+
ref.searchField = ref.searchField ?? ref.field
|
|
164
165
|
ref.labelField = ref.labelField ?? ref.searchField
|
|
165
166
|
const rModel = find(models, { name: ref.model })
|
|
166
167
|
if (!rModel) {
|
|
@@ -318,7 +319,7 @@ async function collectModels () {
|
|
|
318
319
|
|
|
319
320
|
const base = path.basename(file, path.extname(file))
|
|
320
321
|
const defName = pascalCase(`${this.alias} ${base}`)
|
|
321
|
-
const item = await readConfig(file, { ns: this.ns, baseNs: me.ns })
|
|
322
|
+
const item = await readConfig(file, { ns: this.ns, baseNs: me.ns, merge: true })
|
|
322
323
|
if (isEmpty(item)) return undefined
|
|
323
324
|
if (!isPlainObject(item)) me.fatal('invalidModel%s', defName)
|
|
324
325
|
item.name = item.name ?? defName
|
|
@@ -345,12 +346,12 @@ async function collectModels () {
|
|
|
345
346
|
for (const item of model.indexes) {
|
|
346
347
|
for (const field of item.fields) {
|
|
347
348
|
const prop = model.properties.find(p => p.name === field)
|
|
348
|
-
if (!prop || (prop && prop.virtual)) throw this.error('virtualFieldIn%s%s', 'index', model.name)
|
|
349
|
+
if (!prop || (prop && prop.virtual)) throw this.error('virtualFieldIn%s%s%s', field, 'index', model.name)
|
|
349
350
|
}
|
|
350
351
|
}
|
|
351
|
-
for (const field of model.
|
|
352
|
+
for (const field of model.scanables) {
|
|
352
353
|
const prop = model.properties.find(p => p.name === field)
|
|
353
|
-
if (!prop || (prop && prop.virtual)) throw this.error('virtualFieldIn%s%s', 'scanable', model.name)
|
|
354
|
+
if (!prop || (prop && prop.virtual)) throw this.error('virtualFieldIn%s%s%s', field, 'scanable', model.name)
|
|
354
355
|
}
|
|
355
356
|
}
|
|
356
357
|
this.log.debug('collected%s%d', this.t('model'), this.models.length)
|
package/lib/factory/driver.js
CHANGED
|
@@ -272,7 +272,7 @@ async function driverFactory () {
|
|
|
272
272
|
}
|
|
273
273
|
|
|
274
274
|
async _updateRecord (model, id, input = {}, options = {}) {
|
|
275
|
-
let body = omit(input, this.getVirtualFields())
|
|
275
|
+
let body = omit(input, this.getVirtualFields(model))
|
|
276
276
|
if (!options.noUniqueCheck) {
|
|
277
277
|
if (!this.support.uniqueIndex) await this._checkUnique(model, body, options)
|
|
278
278
|
}
|
|
@@ -292,7 +292,7 @@ async function driverFactory () {
|
|
|
292
292
|
}
|
|
293
293
|
|
|
294
294
|
async _upsertRecord (model, input = {}, options = {}) {
|
|
295
|
-
let body = omit(input, this.getVirtualFields())
|
|
295
|
+
let body = omit(input, this.getVirtualFields(model))
|
|
296
296
|
if (!options.noUniqueCheck) {
|
|
297
297
|
if (!this.uniqueIndexSupport) await this._checkUnique(model, body, options)
|
|
298
298
|
}
|
|
@@ -232,7 +232,7 @@ export async function getMultiRefs (records = [], options = {}) {
|
|
|
232
232
|
if (!rModel) return
|
|
233
233
|
let matches = []
|
|
234
234
|
for (const r of records) {
|
|
235
|
-
matches.push(rModel.sanitizeId(r[prop.name]))
|
|
235
|
+
matches.push(prop.name === 'id' ? rModel.sanitizeId(r[prop.name]) : r[prop.name])
|
|
236
236
|
}
|
|
237
237
|
matches = uniq(without(matches, undefined, null, NaN)).map(i => i + '')
|
|
238
238
|
let query = {}
|
|
@@ -438,7 +438,7 @@ export function preparePagination (filter = {}, options = {}) {
|
|
|
438
438
|
}
|
|
439
439
|
|
|
440
440
|
export async function clearCache (id) {
|
|
441
|
-
const { clear } = this.app.bajoCache
|
|
441
|
+
const { clear } = this.app.bajoCache ?? {}
|
|
442
442
|
if (!clear) return
|
|
443
443
|
await clear({ key: `dobo|${this.name}|getRecord|${id}` })
|
|
444
444
|
await clear({ key: `dobo|${this.name}|findRecord` })
|
|
@@ -2,6 +2,8 @@ import { mergeAttachmentInfo, getAttachmentPath } from './_util.js'
|
|
|
2
2
|
const action = 'createAttachment'
|
|
3
3
|
|
|
4
4
|
async function createAttachment (...args) {
|
|
5
|
+
const { createThumbnail } = this.app.bajoExtra
|
|
6
|
+
const { thumbSizes: size } = this.app.dobo.config.default.attachment
|
|
5
7
|
if (!this.attachment) return
|
|
6
8
|
if (args.length === 0) return this.action(action, ...args)
|
|
7
9
|
const [id, opts = {}] = args
|
|
@@ -18,6 +20,9 @@ async function createAttachment (...args) {
|
|
|
18
20
|
const dest = `${dir}/${file}`.replaceAll('//', '/')
|
|
19
21
|
await fs.ensureDir(dir)
|
|
20
22
|
await fs.copy(source, dest)
|
|
23
|
+
try {
|
|
24
|
+
if (createThumbnail) await createThumbnail(dest, { dir: `${dir}/_tn`, size, format: ['.png'] })
|
|
25
|
+
} catch (err) {}
|
|
21
26
|
const rec = {
|
|
22
27
|
field: field === '' ? undefined : field,
|
|
23
28
|
dir,
|
|
@@ -20,13 +20,13 @@ async function createRecord (...args) {
|
|
|
20
20
|
await execDynHook.call(this, 'beforeCreateRecord', input, options)
|
|
21
21
|
if (!noValidation) await execValidation.call(this, input, options)
|
|
22
22
|
let result = options.record ?? (await this.driver._createRecord(this, input, options)) ?? {}
|
|
23
|
+
await handleReq.call(this, result.data.id, 'created', options)
|
|
23
24
|
if (noResult) return
|
|
24
25
|
result = result ?? {}
|
|
25
26
|
const { warnings } = getDefaultValues(options)
|
|
26
27
|
if (!warnings) delete result.warnings
|
|
27
|
-
if (!noResultSanitizer) result.data = await this.sanitizeRecord(result.data, options)
|
|
28
28
|
if (isSet(options.refs)) await getSingleRef.call(this, result.data, options)
|
|
29
|
-
await
|
|
29
|
+
if (!noResultSanitizer) result.data = await this.sanitizeRecord(result.data, options)
|
|
30
30
|
await execDynHook.call(this, 'afterCreateRecord', input, result, options)
|
|
31
31
|
await execModelHook.call(this, 'afterCreateRecord', input, result, options)
|
|
32
32
|
await execHook.call(this, 'afterCreateRecord', input, result, options)
|
|
@@ -37,12 +37,12 @@ async function native (...args) {
|
|
|
37
37
|
result.pages = options.count ? Math.ceil(result.count / filter.limit) : undefined
|
|
38
38
|
if (!warnings) delete result.warnings
|
|
39
39
|
|
|
40
|
+
if (isSet(options.refs)) await getMultiRefs.call(this, result.data, options)
|
|
40
41
|
if (!noResultSanitizer) {
|
|
41
42
|
for (const idx in result.data) {
|
|
42
43
|
result.data[idx] = await this.sanitizeRecord(result.data[idx], options)
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
|
-
if (isSet(options.refs)) await getMultiRefs.call(this, result.data, options)
|
|
46
46
|
await execDynHook.call(this, 'afterFindRecord', filter, result, options)
|
|
47
47
|
await execModelHook.call(this, 'afterFindRecord', filter, result, options)
|
|
48
48
|
await execHook.call(this, 'afterFindRecord', filter, result, options)
|
|
@@ -101,13 +101,12 @@ async function findRecord (...args) {
|
|
|
101
101
|
}
|
|
102
102
|
result.pages = options.count ? Math.ceil(result.count / filter.limit) : undefined
|
|
103
103
|
if (!warnings) delete result.warnings
|
|
104
|
-
|
|
104
|
+
if (isSet(options.refs)) await getMultiRefs.call(this, result.data, options)
|
|
105
105
|
if (!noResultSanitizer) {
|
|
106
106
|
for (const idx in result.data) {
|
|
107
107
|
result.data[idx] = await this.sanitizeRecord(result.data[idx], options)
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
|
-
if (isSet(options.refs)) await getMultiRefs.call(this, result.data, options)
|
|
111
110
|
await execDynHook.call(this, 'afterFindRecord', filter, result, options)
|
|
112
111
|
await execModelHook.call(this, 'afterFindRecord', filter, result, options)
|
|
113
112
|
await execHook.call(this, 'afterFindRecord', filter, result, options)
|
|
@@ -68,8 +68,8 @@ async function getRecord (...args) {
|
|
|
68
68
|
const { warnings } = getDefaultValues(options)
|
|
69
69
|
if (!warnings) delete result.warnings
|
|
70
70
|
if (isEmpty(result.data) && !options.throwNotFound) return dataOnly ? undefined : { data: undefined }
|
|
71
|
-
if (!noResultSanitizer) result.data = await this.sanitizeRecord(result.data, options)
|
|
72
71
|
if (isSet(options.refs)) await getSingleRef.call(this, result.data, options)
|
|
72
|
+
if (!noResultSanitizer) result.data = await this.sanitizeRecord(result.data, options)
|
|
73
73
|
await execDynHook.call(this, 'afterGetRecord', id, result, options)
|
|
74
74
|
await execModelHook.call(this, 'afterGetRecord', id, result, options)
|
|
75
75
|
await execHook.call(this, 'afterGetRecord', id, result, options)
|
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
import { getAttachmentPath } from './_util.js'
|
|
2
|
+
import path from 'path'
|
|
2
3
|
const action = 'removeAttachment'
|
|
3
4
|
|
|
4
5
|
async function removeAttachment (...args) {
|
|
5
6
|
if (!this.attachment) return
|
|
6
7
|
if (args.length === 0) return this.action(action, ...args)
|
|
7
8
|
const [id, field, file, opts = {}] = args
|
|
8
|
-
const { fs } = this.app.lib
|
|
9
|
-
const
|
|
9
|
+
const { fs, fastGlob } = this.app.lib
|
|
10
|
+
const fullPath = await getAttachmentPath.call(this, id, field, file)
|
|
10
11
|
const { req } = opts
|
|
11
|
-
await fs.remove(
|
|
12
|
+
await fs.remove(fullPath)
|
|
13
|
+
const dir = path.dirname(fullPath)
|
|
14
|
+
const ext = path.extname(fullPath)
|
|
15
|
+
const base = path.basename(fullPath, ext)
|
|
16
|
+
const pattern = `${dir}/_tn/${base}-*.*`
|
|
17
|
+
const files = await fastGlob(pattern)
|
|
18
|
+
for (const f of files) {
|
|
19
|
+
await fs.remove(f)
|
|
20
|
+
}
|
|
12
21
|
if (!opts.noFlash && req && req.flash) req.flash('notify', req.t('attachmentRemoved'))
|
|
13
22
|
}
|
|
14
23
|
|
|
@@ -45,13 +45,13 @@ async function removeRecord (...args) {
|
|
|
45
45
|
await execModelHook.call(this, 'beforeRemoveRecord', id, options)
|
|
46
46
|
await execDynHook.call(this, 'beforeRemoveRecord', id, options)
|
|
47
47
|
const result = options.record ?? (await this.driver._removeRecord(this, id, options)) ?? {}
|
|
48
|
+
await handleReq.call(this, result.oldData.id, 'removed', options)
|
|
48
49
|
await clearCache.call(this, id)
|
|
49
50
|
if (noResult) return
|
|
50
51
|
const { warnings } = getDefaultValues(options)
|
|
51
52
|
if (!warnings) delete result.warnings
|
|
52
53
|
if (!noResultSanitizer) result.oldData = await this.sanitizeRecord(result.oldData, options)
|
|
53
54
|
if (isSet(options.refs)) await getSingleRef.call(this, result.data, options)
|
|
54
|
-
await handleReq.call(this, result.oldData.id, 'removed', options)
|
|
55
55
|
await execDynHook.call(this, 'afterRemoveRecord', id, result, options)
|
|
56
56
|
await execModelHook.call(this, 'afterRemoveRecord', id, result, options)
|
|
57
57
|
await execHook.call(this, 'afterRemoveRecord', id, result, options)
|
|
@@ -25,13 +25,14 @@ async function sanitizeRecord (record = {}, opts = {}) {
|
|
|
25
25
|
newFields = without(newFields, ...allHidden)
|
|
26
26
|
const body = fillObject(record, newFields, null)
|
|
27
27
|
const newRecord = await this.sanitizeBody({ body, noDefault: true })
|
|
28
|
+
if (record._ref) newRecord._ref = cloneDeep(record._ref)
|
|
28
29
|
for (const key in newRecord) {
|
|
29
30
|
const prop = this.getProperty(key)
|
|
31
|
+
if (!prop) continue
|
|
30
32
|
const val = ['object', 'array'].includes(prop.type) ? cloneDeep(newRecord[key]) : newRecord[key]
|
|
31
33
|
if (isFunction(prop.getValue)) newRecord[key] = await prop.getValue.call(this, val, newRecord, opts)
|
|
32
34
|
else if (isString(prop.getValue)) newRecord[key] = await callHandler(this.plugin, this, val, newRecord, opts)
|
|
33
35
|
}
|
|
34
|
-
|
|
35
36
|
if (opts.fmt) {
|
|
36
37
|
newRecord._fmt = cloneDeep(newRecord)
|
|
37
38
|
for (const key in newRecord) {
|
|
@@ -55,7 +56,6 @@ async function sanitizeRecord (record = {}, opts = {}) {
|
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
58
|
}
|
|
58
|
-
if (record._ref) newRecord._ref = record._ref
|
|
59
59
|
return newRecord
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -67,16 +67,16 @@ async function updateRecord (...args) {
|
|
|
67
67
|
await execDynHook.call(this, 'beforeUpdateRecord', id, input, options)
|
|
68
68
|
if (!noValidation) await execValidation.call(this, input, options)
|
|
69
69
|
const result = options.record ?? (await this.driver._updateRecord(this, id, input, options)) ?? {}
|
|
70
|
+
await handleReq.call(this, result.data.id, 'updated', options)
|
|
70
71
|
await clearCache.call(this, id)
|
|
71
72
|
if (noResult) return
|
|
72
73
|
const { warnings } = getDefaultValues(options)
|
|
73
74
|
if (!warnings) delete result.warnings
|
|
75
|
+
if (isSet(options.refs)) await getSingleRef.call(this, result.data, options)
|
|
74
76
|
if (!noResultSanitizer) {
|
|
75
77
|
result.data = await this.sanitizeRecord(result.data, options)
|
|
76
78
|
result.oldData = await this.sanitizeRecord(result.oldData, options)
|
|
77
79
|
}
|
|
78
|
-
if (isSet(options.refs)) await getSingleRef.call(this, result.data, options)
|
|
79
|
-
await handleReq.call(this, result.data.id, 'updated', options)
|
|
80
80
|
await execDynHook.call(this, 'afterUpdateRecord', id, input, result, options)
|
|
81
81
|
await execModelHook.call(this, 'afterUpdateRecord', id, input, result, options)
|
|
82
82
|
await execHook.call(this, 'afterUpdateRecord', id, input, result, options)
|
|
@@ -16,13 +16,13 @@ async function native (body = {}, opts = {}) {
|
|
|
16
16
|
await execModelHook.call(this, 'beforeUpsertRecord', input, options)
|
|
17
17
|
await execDynHook.call(this, 'beforeUpsertRecord', input, options)
|
|
18
18
|
const result = options.record ?? (await this.driver._upsertRecord(this, input, options)) ?? {}
|
|
19
|
+
await handleReq.call(this, result.data.id, 'upserted', options)
|
|
19
20
|
await clearCache.call(this, body.id)
|
|
20
21
|
if (noResult) return
|
|
21
22
|
const { warnings } = getDefaultValues(options)
|
|
22
23
|
if (!warnings) delete result.warnings
|
|
23
|
-
if (!noResultSanitizer) result.data = await this.sanitizeRecord(result.data, options)
|
|
24
24
|
if (isSet(options.refs)) await getSingleRef.call(this, result.data, options)
|
|
25
|
-
await
|
|
25
|
+
if (!noResultSanitizer) result.data = await this.sanitizeRecord(result.data, options)
|
|
26
26
|
await execDynHook.call(this, 'afterUpsertRecord', input, result, options)
|
|
27
27
|
await execModelHook.call(this, 'afterUpsertRecord', input, result, options)
|
|
28
28
|
await execHook.call(this, 'afterUpsertRecord', input, result, options)
|
package/package.json
CHANGED
package/wiki/CHANGES.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## 2026-05-02
|
|
4
|
+
|
|
5
|
+
- [2.22.0] Add auto thumbnail creation when image attachment is uploaded
|
|
6
|
+
- [2.22.0] Add feature to get the thumbnail instead of attachment file in attachment route
|
|
7
|
+
- [2.22.0] Add ```dobo:image``` feature
|
|
8
|
+
- [2.22.0] Bug fix in ```model.createRecord()```
|
|
9
|
+
- [2.22.0] Bug fix in ```model.updateRecord()```
|
|
10
|
+
- [2.22.0] Bug fix in ```model.upsertRecord()```
|
|
11
|
+
- [2.22.0] Bug fix in ```model.removeRecord()```
|
|
12
|
+
- [2.22.0] Remove attachment now also remove corresponding thumbnails
|
|
13
|
+
|
|
14
|
+
## 2026-04-28
|
|
15
|
+
|
|
16
|
+
- [2.21.1] Bug fix in ```collect-models.js```
|
|
17
|
+
- [2.21.1] Bug fix in ```driver._updateRecord()```
|
|
18
|
+
- [2.21.1] Bug fix in ```util.getMultiRef()```
|
|
19
|
+
- [2.21.1] Bug fix in setting references
|
|
20
|
+
- [2.21.1] Bug fix in ```model.sanitizeRecord()```
|
|
21
|
+
|
|
3
22
|
## 2026-04-25
|
|
4
23
|
|
|
5
24
|
- [2.21.0] Change ```options.formatValue``` to ```options.fmt```
|