@jsreport/jsreport-core 3.9.0 → 3.11.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/README.md +23 -0
- package/lib/main/folders/validateDuplicatedName.js +1 -0
- package/lib/main/profiler.js +76 -52
- package/lib/main/reporter.js +25 -1
- package/lib/main/settings.js +1 -35
- package/lib/main/templates.js +2 -2
- package/lib/shared/normalizeMetaFromLogs.js +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -282,6 +282,29 @@ jsreport.documentStore.collection('templates')
|
|
|
282
282
|
|
|
283
283
|
## Changelog
|
|
284
284
|
|
|
285
|
+
### 3.11.0
|
|
286
|
+
|
|
287
|
+
- log when worker returns bad res.content
|
|
288
|
+
- fix profiler leaks
|
|
289
|
+
- remove settings sync API and avoid loading all items to memory
|
|
290
|
+
- throw weak error when validating duplicated entity
|
|
291
|
+
- ensure we end with profiles with error state when there is server or req timeout
|
|
292
|
+
|
|
293
|
+
### 3.10.0
|
|
294
|
+
|
|
295
|
+
- `mainReporter.executeWorkerAction` now supports cancellation with `AbortController.signal`
|
|
296
|
+
- add support for specifying what are the main document properties of templates entitySet
|
|
297
|
+
|
|
298
|
+
### 3.9.0
|
|
299
|
+
|
|
300
|
+
- add more store methods `collection.findAdmin`, `collection.findOneAdmin`, `reporter.adminRequest` to easily allow execure store queries without taking into account permissions
|
|
301
|
+
- improve logging for child requests and user level logs
|
|
302
|
+
- differentiate between template not found errors and permissions related errors (it is now more clean what is the cause of specific error)
|
|
303
|
+
- normalize to error when non-errors are throw (like throw "string")
|
|
304
|
+
- improve errors in helpers (it now includes the helper name)
|
|
305
|
+
- improve error message when template was not found in child request
|
|
306
|
+
- improve error handling in sandbox
|
|
307
|
+
|
|
285
308
|
### 3.8.1
|
|
286
309
|
|
|
287
310
|
- update vm2 for fix security issue
|
|
@@ -74,6 +74,7 @@ async function validateDuplicatedName (reporter, c, doc, originalIdValue, req) {
|
|
|
74
74
|
|
|
75
75
|
throw reporter.createError(msg, {
|
|
76
76
|
statusCode: 400,
|
|
77
|
+
weak: true,
|
|
77
78
|
code: 'DUPLICATED_ENTITY',
|
|
78
79
|
existingEntity: existingEntity.entity,
|
|
79
80
|
existingEntityEntitySet: existingEntity.entitySet
|
package/lib/main/profiler.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
const EventEmitter = require('events')
|
|
2
|
-
const
|
|
2
|
+
const Transport = require('winston-transport')
|
|
3
3
|
const extend = require('node.extend.without.arrays')
|
|
4
4
|
const generateRequestId = require('../shared/generateRequestId')
|
|
5
5
|
const fs = require('fs/promises')
|
|
6
|
-
|
|
6
|
+
const { SPLAT } = require('triple-beam')
|
|
7
7
|
module.exports = (reporter) => {
|
|
8
8
|
reporter.documentStore.registerEntityType('ProfileType', {
|
|
9
9
|
templateShortid: { type: 'Edm.String', referenceTo: 'templates' },
|
|
@@ -12,7 +12,8 @@ module.exports = (reporter) => {
|
|
|
12
12
|
state: { type: 'Edm.String' },
|
|
13
13
|
error: { type: 'Edm.String' },
|
|
14
14
|
mode: { type: 'Edm.String', schema: { enum: ['full', 'standard', 'disabled'] } },
|
|
15
|
-
blobName: { type: 'Edm.String' }
|
|
15
|
+
blobName: { type: 'Edm.String' },
|
|
16
|
+
timeout: { type: 'Edm.Int32' }
|
|
16
17
|
})
|
|
17
18
|
|
|
18
19
|
reporter.documentStore.registerEntitySet('profiles', {
|
|
@@ -23,7 +24,6 @@ module.exports = (reporter) => {
|
|
|
23
24
|
const profilersMap = new Map()
|
|
24
25
|
const profilerOperationsChainsMap = new Map()
|
|
25
26
|
const profilerRequestMap = new Map()
|
|
26
|
-
const profilerLogRequestMap = new Map()
|
|
27
27
|
|
|
28
28
|
function runInProfilerChain (fnOrOptions, req) {
|
|
29
29
|
if (req.context.profiling.mode === 'disabled') {
|
|
@@ -89,7 +89,7 @@ module.exports = (reporter) => {
|
|
|
89
89
|
for (const m of events) {
|
|
90
90
|
if (m.type === 'log') {
|
|
91
91
|
if (log) {
|
|
92
|
-
reporter.logger[m.level](m.message, { ...req, ...m.meta, timestamp: m.timestamp,
|
|
92
|
+
reporter.logger[m.level](m.message, { ...req, ...m.meta, timestamp: m.timestamp, logged: true })
|
|
93
93
|
}
|
|
94
94
|
} else {
|
|
95
95
|
lastOperation = m
|
|
@@ -167,7 +167,8 @@ module.exports = (reporter) => {
|
|
|
167
167
|
_id: reporter.documentStore.generateId(),
|
|
168
168
|
timestamp: new Date(),
|
|
169
169
|
state: 'queued',
|
|
170
|
-
mode: req.context.profiling.mode
|
|
170
|
+
mode: req.context.profiling.mode,
|
|
171
|
+
timeout: reporter.options.enableRequestReportTimeout && req.options.timeout ? req.options.timeout : reporter.options.reportTimeout
|
|
171
172
|
}
|
|
172
173
|
|
|
173
174
|
const { pathToFile } = await reporter.writeTempFile((uuid) => `${uuid}.log`, '')
|
|
@@ -325,49 +326,40 @@ module.exports = (reporter) => {
|
|
|
325
326
|
}
|
|
326
327
|
})
|
|
327
328
|
|
|
329
|
+
// we want to add to profiles also log messages from the main
|
|
328
330
|
const configuredPreviously = reporter.logger.__profilerConfigured__ === true
|
|
329
|
-
|
|
330
331
|
if (!configuredPreviously) {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
332
|
+
// we emit from winston transport, so winston formatters can still format message
|
|
333
|
+
class EmittingProfilesTransport extends Transport {
|
|
334
|
+
log (info, callback) {
|
|
335
|
+
setImmediate(() => {
|
|
336
|
+
this.emit('logged', info)
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
if (info[SPLAT]) {
|
|
340
|
+
const [req] = info[SPLAT]
|
|
341
|
+
|
|
342
|
+
if (req && req.context && req.logged !== true) {
|
|
343
|
+
emitProfiles({
|
|
344
|
+
events: [createProfileMessage({
|
|
345
|
+
type: 'log',
|
|
346
|
+
level: info.level,
|
|
347
|
+
message: info.message,
|
|
348
|
+
previousOperationId: req.context.profiling.lastOperationId
|
|
349
|
+
}, req)],
|
|
350
|
+
log: false
|
|
351
|
+
}, req)
|
|
352
|
+
}
|
|
353
|
+
}
|
|
336
354
|
|
|
337
|
-
|
|
338
|
-
profilerLogRequestMap.set(meta.context.rootId, meta)
|
|
355
|
+
callback()
|
|
339
356
|
}
|
|
340
|
-
|
|
341
|
-
return originalLog.call(this, level, msg, ...splat)
|
|
342
357
|
}
|
|
343
358
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
emitProfiles({
|
|
350
|
-
events: [createProfileMessage({
|
|
351
|
-
type: 'log',
|
|
352
|
-
level: info.level,
|
|
353
|
-
message: info.message,
|
|
354
|
-
previousOperationId: req.context.profiling.lastOperationId
|
|
355
|
-
}, req)],
|
|
356
|
-
log: false
|
|
357
|
-
}, req)
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
if (info.fromEmitProfile != null) {
|
|
361
|
-
delete info.fromEmitProfile
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
return info
|
|
365
|
-
})
|
|
366
|
-
|
|
367
|
-
reporter.logger.format = winston.format.combine(
|
|
368
|
-
reporter.logger.format,
|
|
369
|
-
mainLogsToProfile()
|
|
370
|
-
)
|
|
359
|
+
reporter.logger.add(new EmittingProfilesTransport({
|
|
360
|
+
format: reporter.logger.format,
|
|
361
|
+
level: 'debug'
|
|
362
|
+
}))
|
|
371
363
|
|
|
372
364
|
reporter.logger.__profilerConfigured__ = true
|
|
373
365
|
}
|
|
@@ -410,7 +402,6 @@ module.exports = (reporter) => {
|
|
|
410
402
|
profilersMap.clear()
|
|
411
403
|
profilerOperationsChainsMap.clear()
|
|
412
404
|
profilerRequestMap.clear()
|
|
413
|
-
profilerLogRequestMap.clear()
|
|
414
405
|
})
|
|
415
406
|
|
|
416
407
|
let profilesCleanupRunning = false
|
|
@@ -422,11 +413,13 @@ module.exports = (reporter) => {
|
|
|
422
413
|
|
|
423
414
|
profilesCleanupRunning = true
|
|
424
415
|
|
|
425
|
-
let
|
|
416
|
+
let lastError
|
|
426
417
|
|
|
427
418
|
try {
|
|
428
|
-
const
|
|
429
|
-
|
|
419
|
+
const profilesToRemove = await reporter.documentStore.collection('profiles')
|
|
420
|
+
.find({}, { _id: 1 }).sort({ timestamp: -1 })
|
|
421
|
+
.skip(reporter.options.profiler.maxProfilesHistory)
|
|
422
|
+
.toArray()
|
|
430
423
|
|
|
431
424
|
for (const profile of profilesToRemove) {
|
|
432
425
|
if (reporter.closed || reporter.closing) {
|
|
@@ -438,7 +431,40 @@ module.exports = (reporter) => {
|
|
|
438
431
|
_id: profile._id
|
|
439
432
|
})
|
|
440
433
|
} catch (e) {
|
|
441
|
-
|
|
434
|
+
lastError = e
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const notFinishedProfiles = await reporter.documentStore.collection('profiles')
|
|
439
|
+
.find({ $or: [{ state: 'running' }, { state: 'queued' }] }, { _id: 1, timeout: 1, timestamp: 1 })
|
|
440
|
+
.toArray()
|
|
441
|
+
|
|
442
|
+
for (const profile of notFinishedProfiles) {
|
|
443
|
+
if (reporter.closed || reporter.closing) {
|
|
444
|
+
return
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
if (!profile.timeout) {
|
|
448
|
+
continue
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const whenShouldBeFinished = profile.timestamp + profile.timeout + reporter.options.reportTimeoutMargin * 2
|
|
452
|
+
if (whenShouldBeFinished < new Date().getTime()) {
|
|
453
|
+
continue
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
try {
|
|
457
|
+
await reporter.documentStore.collection('profiles').update({
|
|
458
|
+
_id: profile._id
|
|
459
|
+
}, {
|
|
460
|
+
$set: {
|
|
461
|
+
state: 'error',
|
|
462
|
+
finishedOn: new Date(),
|
|
463
|
+
error: 'The server did not update the report profile before its timeout. This can happen when the server is unexpectedly stopped.'
|
|
464
|
+
}
|
|
465
|
+
})
|
|
466
|
+
} catch (e) {
|
|
467
|
+
lastError = e
|
|
442
468
|
}
|
|
443
469
|
}
|
|
444
470
|
} catch (e) {
|
|
@@ -447,8 +473,8 @@ module.exports = (reporter) => {
|
|
|
447
473
|
profilesCleanupRunning = false
|
|
448
474
|
}
|
|
449
475
|
|
|
450
|
-
if (
|
|
451
|
-
reporter.logger.warn('Profile cleanup failed for some entities, last error:',
|
|
476
|
+
if (lastError) {
|
|
477
|
+
reporter.logger.warn('Profile cleanup failed for some entities, last error:', lastError)
|
|
452
478
|
}
|
|
453
479
|
}
|
|
454
480
|
|
|
@@ -461,7 +487,6 @@ module.exports = (reporter) => {
|
|
|
461
487
|
profilersMap.delete(req.context.rootId)
|
|
462
488
|
profilerOperationsChainsMap.delete(req.context.rootId)
|
|
463
489
|
profilerRequestMap.delete(req.context.rootId)
|
|
464
|
-
profilerLogRequestMap.delete(req.context.rootId)
|
|
465
490
|
return
|
|
466
491
|
}
|
|
467
492
|
|
|
@@ -471,7 +496,6 @@ module.exports = (reporter) => {
|
|
|
471
496
|
profilersMap.delete(req.context.rootId)
|
|
472
497
|
profilerOperationsChainsMap.delete(req.context.rootId)
|
|
473
498
|
profilerRequestMap.delete(req.context.rootId)
|
|
474
|
-
profilerLogRequestMap.delete(req.context.rootId)
|
|
475
499
|
}
|
|
476
500
|
}, req)
|
|
477
501
|
}
|
package/lib/main/reporter.js
CHANGED
|
@@ -449,6 +449,7 @@ class MainReporter extends Reporter {
|
|
|
449
449
|
} catch (err) {
|
|
450
450
|
await this._handleRenderError(req, res, err).catch((e) => {})
|
|
451
451
|
} finally {
|
|
452
|
+
this._cleanProfileInRequest(req)
|
|
452
453
|
if (!workerAborted) {
|
|
453
454
|
await worker.release(req)
|
|
454
455
|
}
|
|
@@ -477,6 +478,10 @@ class MainReporter extends Reporter {
|
|
|
477
478
|
|
|
478
479
|
await this.afterRenderListeners.fire(req, res)
|
|
479
480
|
|
|
481
|
+
if (!res.content) {
|
|
482
|
+
this.logger.error('Worker didnt return render res.content, returned:' + JSON.stringify(responseResult), req)
|
|
483
|
+
}
|
|
484
|
+
|
|
480
485
|
res.stream = Readable.from(res.content)
|
|
481
486
|
|
|
482
487
|
this._cleanProfileInRequest(req)
|
|
@@ -557,6 +562,16 @@ class MainReporter extends Reporter {
|
|
|
557
562
|
timeout
|
|
558
563
|
})
|
|
559
564
|
|
|
565
|
+
const handleAbortSignal = () => {
|
|
566
|
+
if (worker) {
|
|
567
|
+
worker.release(req).catch((e) => this.logger.error('Failed to release worker ' + e))
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
if (options.signal && !options.worker) {
|
|
572
|
+
options.signal.addEventListener('abort', handleAbortSignal, { once: true })
|
|
573
|
+
}
|
|
574
|
+
|
|
560
575
|
try {
|
|
561
576
|
const result = await worker.execute({
|
|
562
577
|
actionName,
|
|
@@ -579,7 +594,16 @@ class MainReporter extends Reporter {
|
|
|
579
594
|
return result
|
|
580
595
|
} finally {
|
|
581
596
|
if (!options.worker) {
|
|
582
|
-
|
|
597
|
+
let shouldRelease = true
|
|
598
|
+
|
|
599
|
+
if (options.signal) {
|
|
600
|
+
options.signal.removeEventListener('abort', handleAbortSignal)
|
|
601
|
+
shouldRelease = options.signal.aborted !== true
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
if (shouldRelease) {
|
|
605
|
+
await worker.release(req)
|
|
606
|
+
}
|
|
583
607
|
}
|
|
584
608
|
}
|
|
585
609
|
}
|
package/lib/main/settings.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
const Request = require('../shared/request')
|
|
7
7
|
|
|
8
8
|
const Settings = module.exports = function () {
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
Settings.prototype.add = function (key, value, req) {
|
|
@@ -15,15 +15,9 @@ Settings.prototype.add = function (key, value, req) {
|
|
|
15
15
|
value: typeof value !== 'string' ? JSON.stringify(value) : value
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
this._collection.push(settingItem)
|
|
19
|
-
|
|
20
18
|
return this.documentStore.collection('settings').insert(settingItem, localReqWithoutAuthorization(req))
|
|
21
19
|
}
|
|
22
20
|
|
|
23
|
-
Settings.prototype.get = function (key) {
|
|
24
|
-
return this._collection.find((s) => s.key === key)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
21
|
Settings.prototype.findValue = async function (key, req) {
|
|
28
22
|
const res = await this.documentStore.collection('settings').find({ key: key }, localReqWithoutAuthorization(req))
|
|
29
23
|
if (res.length !== 1) {
|
|
@@ -36,8 +30,6 @@ Settings.prototype.findValue = async function (key, req) {
|
|
|
36
30
|
Settings.prototype.set = function (key, avalue, req) {
|
|
37
31
|
const value = typeof avalue !== 'string' ? JSON.stringify(avalue) : avalue
|
|
38
32
|
|
|
39
|
-
this.get(key).value = value
|
|
40
|
-
|
|
41
33
|
return this.documentStore.collection('settings').update({
|
|
42
34
|
key: key
|
|
43
35
|
}, {
|
|
@@ -58,8 +50,6 @@ Settings.prototype.addOrSet = async function (key, avalue, req) {
|
|
|
58
50
|
Settings.prototype.init = async function (documentStore, authorization) {
|
|
59
51
|
this.documentStore = documentStore
|
|
60
52
|
|
|
61
|
-
const incompatibleSettingsToRemove = []
|
|
62
|
-
|
|
63
53
|
if (authorization != null) {
|
|
64
54
|
const col = documentStore.collection('settings')
|
|
65
55
|
|
|
@@ -95,30 +85,6 @@ Settings.prototype.init = async function (documentStore, authorization) {
|
|
|
95
85
|
}
|
|
96
86
|
})
|
|
97
87
|
}
|
|
98
|
-
|
|
99
|
-
const res = await documentStore.collection('settings').find({})
|
|
100
|
-
|
|
101
|
-
res.forEach((v) => {
|
|
102
|
-
if (typeof v.value !== 'string') {
|
|
103
|
-
return this._collection.push({
|
|
104
|
-
key: v.key,
|
|
105
|
-
value: v.value
|
|
106
|
-
})
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
try {
|
|
110
|
-
return this._collection.push({
|
|
111
|
-
key: v.key,
|
|
112
|
-
value: JSON.parse(v.value)
|
|
113
|
-
})
|
|
114
|
-
} catch (e) {
|
|
115
|
-
incompatibleSettingsToRemove.push(v._id)
|
|
116
|
-
}
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
if (incompatibleSettingsToRemove.length) {
|
|
120
|
-
return documentStore.collection('settings').remove({ _id: { $in: incompatibleSettingsToRemove } })
|
|
121
|
-
}
|
|
122
88
|
}
|
|
123
89
|
|
|
124
90
|
Settings.prototype.registerEntity = function (documentStore) {
|
package/lib/main/templates.js
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
module.exports = (reporter) => {
|
|
3
3
|
reporter.documentStore.registerEntityType('TemplateType', {
|
|
4
4
|
name: { type: 'Edm.String' },
|
|
5
|
-
content: { type: 'Edm.String', document: { extension: 'html', engine: true } },
|
|
5
|
+
content: { type: 'Edm.String', document: { main: true, extension: 'html', engine: true } },
|
|
6
6
|
recipe: { type: 'Edm.String' },
|
|
7
|
-
helpers: { type: 'Edm.String', document: { extension: 'js' }, schema: { type: 'object' } },
|
|
7
|
+
helpers: { type: 'Edm.String', document: { main: true, extension: 'js' }, schema: { type: 'object' } },
|
|
8
8
|
engine: { type: 'Edm.String' }
|
|
9
9
|
}, true)
|
|
10
10
|
|
|
@@ -13,7 +13,7 @@ module.exports = (level, msg, meta) => {
|
|
|
13
13
|
|
|
14
14
|
// TODO adding cancel looks bad, its before script is adding req.cancel()
|
|
15
15
|
// excluding non relevant properties for the log
|
|
16
|
-
const newMeta = Object.assign({}, omit(meta, ['rawContent', 'template', 'options', 'data', 'context', 'timestamp', 'cancel']))
|
|
16
|
+
const newMeta = Object.assign({}, omit(meta, ['logged', 'rawContent', 'template', 'options', 'data', 'context', 'timestamp', 'cancel']))
|
|
17
17
|
|
|
18
18
|
if (newMeta.rootId == null && meta.context.rootId != null) {
|
|
19
19
|
newMeta.rootId = meta.context.rootId
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsreport/jsreport-core",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.11.0",
|
|
4
4
|
"description": "javascript based business reporting",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"report",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"@babel/parser": "7.14.4",
|
|
41
41
|
"@babel/traverse": "7.12.9",
|
|
42
42
|
"@colors/colors": "1.5.0",
|
|
43
|
-
"@jsreport/advanced-workers": "1.2.
|
|
43
|
+
"@jsreport/advanced-workers": "1.2.4",
|
|
44
44
|
"@jsreport/mingo": "2.4.1",
|
|
45
45
|
"@jsreport/reap": "0.1.0",
|
|
46
46
|
"ajv": "6.12.6",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"diff-match-patch": "1.0.5",
|
|
55
55
|
"enhanced-resolve": "5.8.3",
|
|
56
56
|
"has-own-deep": "1.1.0",
|
|
57
|
-
"isbinaryfile": "
|
|
57
|
+
"isbinaryfile": "5.0.0",
|
|
58
58
|
"listener-collection": "2.0.0",
|
|
59
59
|
"lodash.get": "4.4.2",
|
|
60
60
|
"lodash.groupby": "4.6.0",
|