@jsreport/jsreport-core 3.8.1 → 3.9.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/lib/main/folders/validateDuplicatedName.js +2 -10
- package/lib/main/profiler.js +1 -0
- package/lib/main/reporter.js +3 -2
- package/lib/main/store/checkDuplicatedId.js +2 -10
- package/lib/main/store/collection.js +38 -10
- package/lib/main/store/mainActions.js +21 -2
- package/lib/main/store/setupValidateShortid.js +2 -9
- package/lib/shared/adminRequest.js +17 -0
- package/lib/shared/createError.js +31 -9
- package/lib/shared/normalizeError.js +31 -0
- package/lib/shared/reporter.js +3 -1
- package/lib/shared/request.js +9 -1
- package/lib/worker/documentStore.js +19 -8
- package/lib/worker/render/executeEngine.js +44 -9
- package/lib/worker/render/profiler.js +2 -1
- package/lib/worker/render/render.js +30 -4
- package/lib/worker/sandbox/createSandbox.js +5 -1
- package/lib/worker/sandbox/runInSandbox.js +39 -39
- package/lib/worker/templates.js +37 -6
- package/package.json +1 -1
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const omit = require('lodash.omit')
|
|
2
1
|
const resolveEntityPath = require('../../shared/folders/resolveEntityPath')
|
|
3
2
|
|
|
4
3
|
async function findEntity (reporter, name, folder, req) {
|
|
@@ -7,18 +6,11 @@ async function findEntity (reporter, name, folder, req) {
|
|
|
7
6
|
continue
|
|
8
7
|
}
|
|
9
8
|
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
// we should validate against all entities without caring about permissions
|
|
13
|
-
if (localReq) {
|
|
14
|
-
localReq.context = localReq.context ? omit(localReq.context, 'user') : localReq.context
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const allEntities = await reporter.documentStore.collection(c).find({
|
|
9
|
+
const allEntities = await reporter.documentStore.collection(c).findAdmin({
|
|
18
10
|
folder
|
|
19
11
|
}, {
|
|
20
12
|
name: 1
|
|
21
|
-
},
|
|
13
|
+
}, req)
|
|
22
14
|
|
|
23
15
|
const existingEntity = allEntities.find((entity) => {
|
|
24
16
|
if (entity.name) {
|
package/lib/main/profiler.js
CHANGED
|
@@ -211,6 +211,7 @@ module.exports = (reporter) => {
|
|
|
211
211
|
profilerRequestMap.set(req.context.rootId, req)
|
|
212
212
|
|
|
213
213
|
const template = await reporter.templates.resolveTemplate(req)
|
|
214
|
+
|
|
214
215
|
if (template && template._id) {
|
|
215
216
|
req.context.resolvedTemplate = extend(true, {}, template)
|
|
216
217
|
|
package/lib/main/reporter.js
CHANGED
|
@@ -345,13 +345,14 @@ class MainReporter extends Reporter {
|
|
|
345
345
|
}
|
|
346
346
|
|
|
347
347
|
if (err.code === 'WORKER_ABORTED') {
|
|
348
|
-
err.message = 'Report cancelled'
|
|
348
|
+
err.message = 'Report cancelled by the client or closed network'
|
|
349
349
|
err.weak = true
|
|
350
350
|
}
|
|
351
351
|
|
|
352
352
|
if (!err.logged) {
|
|
353
353
|
const logFn = err.weak ? this.logger.warn : this.logger.error
|
|
354
|
-
|
|
354
|
+
const errorMessage = this.createError('Report render failed', { original: err }).message
|
|
355
|
+
logFn(`${errorMessage}${err.stack != null ? '\n' + err.stack : ''}`, req)
|
|
355
356
|
}
|
|
356
357
|
await this.renderErrorListeners.fire(req, res, err)
|
|
357
358
|
throw err
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
const omit = require('lodash.omit')
|
|
2
|
-
const Request = require('../../shared/request')
|
|
3
1
|
|
|
4
2
|
module.exports = async function checkDuplicatedId (store, collectionName, idValue, req) {
|
|
5
3
|
if (idValue == null) {
|
|
@@ -12,16 +10,10 @@ module.exports = async function checkDuplicatedId (store, collectionName, idValu
|
|
|
12
10
|
}
|
|
13
11
|
|
|
14
12
|
async function findEntity (store, collectionName, idValue, req) {
|
|
15
|
-
const localReq = req ? Request(req) : req
|
|
16
|
-
|
|
17
13
|
// we should validate without caring about permissions
|
|
18
|
-
|
|
19
|
-
localReq.context = localReq.context ? omit(localReq.context, 'user') : localReq.context
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const existingEntity = await store.collection(collectionName).findOne({
|
|
14
|
+
const existingEntity = await store.collection(collectionName).findOneAdmin({
|
|
23
15
|
_id: idValue
|
|
24
|
-
},
|
|
16
|
+
}, req)
|
|
25
17
|
|
|
26
18
|
return existingEntity
|
|
27
19
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
const createListenerCollection = require('../../shared/listenerCollection')
|
|
2
1
|
const { resolvePropDefinition } = require('./typeUtils')
|
|
2
|
+
const adminRequest = require('../../shared/adminRequest')
|
|
3
|
+
const createListenerCollection = require('../../shared/listenerCollection')
|
|
3
4
|
const createError = require('../../shared/createError')
|
|
5
|
+
const Request = require('../../shared/request')
|
|
4
6
|
const validateEntityName = require('../validateEntityName')
|
|
5
7
|
|
|
6
8
|
module.exports = (entitySet, provider, model, validator, encryption, transactions) => ({
|
|
@@ -74,6 +76,41 @@ module.exports = (entitySet, provider, model, validator, encryption, transaction
|
|
|
74
76
|
})
|
|
75
77
|
},
|
|
76
78
|
|
|
79
|
+
findAdmin (q, p, req) {
|
|
80
|
+
if (p && p.__isJsreportRequest__ === true) {
|
|
81
|
+
req = p
|
|
82
|
+
p = {}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
p = p || {}
|
|
86
|
+
|
|
87
|
+
req = adminRequest(req, Request)
|
|
88
|
+
|
|
89
|
+
return this.find(q, p, req)
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
async findOne (...args) {
|
|
93
|
+
const res = await this.find(...args)
|
|
94
|
+
if (res.length > 0) {
|
|
95
|
+
return res[0]
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return null
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
findOneAdmin (q, p, req) {
|
|
102
|
+
if (p && p.__isJsreportRequest__ === true) {
|
|
103
|
+
req = p
|
|
104
|
+
p = {}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
p = p || {}
|
|
108
|
+
|
|
109
|
+
req = adminRequest(req, Request)
|
|
110
|
+
|
|
111
|
+
return this.findOne(q, p, req)
|
|
112
|
+
},
|
|
113
|
+
|
|
77
114
|
count (...args) {
|
|
78
115
|
return this.find(...args).count()
|
|
79
116
|
},
|
|
@@ -154,15 +191,6 @@ module.exports = (entitySet, provider, model, validator, encryption, transaction
|
|
|
154
191
|
})
|
|
155
192
|
},
|
|
156
193
|
|
|
157
|
-
async findOne (...args) {
|
|
158
|
-
const res = await this.find(...args)
|
|
159
|
-
if (res.length > 0) {
|
|
160
|
-
return res[0]
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return null
|
|
164
|
-
},
|
|
165
|
-
|
|
166
194
|
async serializeProperties (docs, customTypeDef) {
|
|
167
195
|
let typeDef
|
|
168
196
|
|
|
@@ -1,15 +1,34 @@
|
|
|
1
|
+
|
|
1
2
|
module.exports = (reporter) => {
|
|
2
3
|
reporter.registerMainAction('documentStore.collection.find', async (spec, originalReq) => {
|
|
3
4
|
const localReq = reporter.Request(originalReq)
|
|
5
|
+
|
|
4
6
|
localReq.context.userFindCall = true
|
|
5
|
-
|
|
7
|
+
|
|
8
|
+
const collection = reporter.documentStore.collection(spec.collection)
|
|
9
|
+
let method = 'find'
|
|
10
|
+
|
|
11
|
+
if (spec.admin) {
|
|
12
|
+
method = 'findAdmin'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const res = await collection[method](spec.query, localReq)
|
|
6
16
|
return res
|
|
7
17
|
})
|
|
8
18
|
|
|
9
19
|
reporter.registerMainAction('documentStore.collection.findOne', async (spec, originalReq) => {
|
|
10
20
|
const localReq = reporter.Request(originalReq)
|
|
21
|
+
|
|
11
22
|
localReq.context.userFindCall = true
|
|
12
|
-
|
|
23
|
+
|
|
24
|
+
const collection = reporter.documentStore.collection(spec.collection)
|
|
25
|
+
let method = 'findOne'
|
|
26
|
+
|
|
27
|
+
if (spec.admin) {
|
|
28
|
+
method = 'findOneAdmin'
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const res = await collection[method](spec.query, localReq)
|
|
13
32
|
return res
|
|
14
33
|
})
|
|
15
34
|
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const omit = require('lodash.omit')
|
|
2
1
|
|
|
3
2
|
module.exports = (reporter) => {
|
|
4
3
|
reporter.initializeListeners.add('core-validate-shortid', () => {
|
|
@@ -60,16 +59,10 @@ async function validateShortid (reporter, collectionName, doc, originalIdValue,
|
|
|
60
59
|
}
|
|
61
60
|
|
|
62
61
|
async function findEntity (reporter, collectionName, shortid, req) {
|
|
63
|
-
const localReq = req ? reporter.Request(req) : req
|
|
64
|
-
|
|
65
62
|
// we should validate without caring about permissions
|
|
66
|
-
|
|
67
|
-
localReq.context = localReq.context ? omit(localReq.context, 'user') : localReq.context
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const existingEntity = await reporter.documentStore.collection(collectionName).findOne({
|
|
63
|
+
const existingEntity = await reporter.documentStore.collection(collectionName).findOneAdmin({
|
|
71
64
|
shortid
|
|
72
|
-
},
|
|
65
|
+
}, req)
|
|
73
66
|
|
|
74
67
|
return existingEntity
|
|
75
68
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const omit = require('lodash.omit')
|
|
2
|
+
|
|
3
|
+
module.exports = function adminRequest (req, Request) {
|
|
4
|
+
if (req == null) {
|
|
5
|
+
return req
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
let targetReq = req
|
|
9
|
+
|
|
10
|
+
if (Request != null) {
|
|
11
|
+
targetReq = Request(targetReq)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
targetReq.context = targetReq.context ? omit(targetReq.context, 'user') : targetReq.context
|
|
15
|
+
|
|
16
|
+
return targetReq
|
|
17
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const normalizeError = require('./normalizeError')
|
|
2
2
|
|
|
3
3
|
module.exports = function (message, options = {}) {
|
|
4
4
|
const { original } = options
|
|
@@ -10,23 +10,37 @@ module.exports = function (message, options = {}) {
|
|
|
10
10
|
error = new Error(message)
|
|
11
11
|
|
|
12
12
|
if (original != null) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
// because in js you can throw <anything>, not specifically Error
|
|
14
|
+
const originalNormalized = normalizeError(original)
|
|
15
|
+
|
|
16
|
+
error.entity = originalNormalized.entity
|
|
17
|
+
error.lineNumber = originalNormalized.lineNumber
|
|
18
|
+
error.property = originalNormalized.property
|
|
19
|
+
|
|
20
|
+
if (originalNormalized.statusCode != null) {
|
|
21
|
+
error.statusCode = originalNormalized.statusCode
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (originalNormalized.weak != null) {
|
|
25
|
+
error.weak = originalNormalized.weak
|
|
26
|
+
}
|
|
16
27
|
|
|
17
28
|
if (error.message == null || error.message === '') {
|
|
18
|
-
error.message = `${
|
|
29
|
+
error.message = `${originalNormalized.message}`
|
|
19
30
|
} else {
|
|
20
|
-
error.message +=
|
|
31
|
+
error.message += `\n(because) ${lowerCaseFirstLetter(originalNormalized.message)}`
|
|
21
32
|
}
|
|
22
33
|
|
|
23
|
-
|
|
24
|
-
|
|
34
|
+
// stack is printed in reverse order (from cause to more high level error)
|
|
35
|
+
if (error.stack != null && originalNormalized.stack != null) {
|
|
36
|
+
error.stack = `${originalNormalized.stack}\nwrapped by:\n${error.stack}`
|
|
25
37
|
}
|
|
26
38
|
}
|
|
27
39
|
}
|
|
28
40
|
|
|
29
|
-
|
|
41
|
+
const { original: originalInOptions, ...restOfOptions } = options
|
|
42
|
+
|
|
43
|
+
Object.assign(error, restOfOptions)
|
|
30
44
|
|
|
31
45
|
if ((error.statusCode === 400 || error.statusCode === 404) && error.weak == null) {
|
|
32
46
|
error.weak = true
|
|
@@ -34,3 +48,11 @@ module.exports = function (message, options = {}) {
|
|
|
34
48
|
|
|
35
49
|
return error
|
|
36
50
|
}
|
|
51
|
+
|
|
52
|
+
function lowerCaseFirstLetter (str) {
|
|
53
|
+
if (str === '' || typeof str !== 'string') {
|
|
54
|
+
return str
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return str.charAt(0).toLowerCase() + str.slice(1)
|
|
58
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
|
|
2
|
+
module.exports = function normalizeError (errValue, normalizedErrorPrefix = 'User code threw with non-Error: ') {
|
|
3
|
+
let newError
|
|
4
|
+
|
|
5
|
+
const isErrorObj = (
|
|
6
|
+
typeof errValue === 'object' &&
|
|
7
|
+
typeof errValue.hasOwnProperty === 'function' &&
|
|
8
|
+
Object.prototype.hasOwnProperty.call(errValue, 'message')
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
const isValidError = (
|
|
12
|
+
isErrorObj ||
|
|
13
|
+
typeof errValue === 'string'
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
if (!isValidError) {
|
|
17
|
+
if (Object.prototype.toString.call(errValue) === '[object Object]') {
|
|
18
|
+
newError = new Error(`${normalizedErrorPrefix}${JSON.stringify(errValue)}`)
|
|
19
|
+
} else {
|
|
20
|
+
newError = new Error(`${normalizedErrorPrefix}${errValue}`)
|
|
21
|
+
}
|
|
22
|
+
} else {
|
|
23
|
+
if (typeof errValue === 'string') {
|
|
24
|
+
newError = new Error(errValue)
|
|
25
|
+
} else {
|
|
26
|
+
newError = errValue
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return newError
|
|
31
|
+
}
|
package/lib/shared/reporter.js
CHANGED
|
@@ -6,7 +6,8 @@ const Folders = require('./folders')
|
|
|
6
6
|
const createOrExtendError = require('./createError')
|
|
7
7
|
const tempFilesHandler = require('./tempFilesHandler')
|
|
8
8
|
const encryption = require('./encryption')
|
|
9
|
-
const generateRequestId = require('
|
|
9
|
+
const generateRequestId = require('./generateRequestId')
|
|
10
|
+
const adminRequest = require('./adminRequest')
|
|
10
11
|
|
|
11
12
|
class Reporter extends EventEmitter {
|
|
12
13
|
constructor (options) {
|
|
@@ -14,6 +15,7 @@ class Reporter extends EventEmitter {
|
|
|
14
15
|
|
|
15
16
|
this.options = options || {}
|
|
16
17
|
this.Request = Request
|
|
18
|
+
this.adminRequest = adminRequest
|
|
17
19
|
|
|
18
20
|
// since `reporter` instance will be used for other extensions,
|
|
19
21
|
// it will quickly reach the limit of `10` listeners,
|
package/lib/shared/request.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const extend = require('node.extend.without.arrays')
|
|
2
2
|
const omit = require('lodash.omit')
|
|
3
|
+
const createError = require('./createError')
|
|
3
4
|
|
|
4
5
|
module.exports = (obj, parent) => {
|
|
5
6
|
if (parent && !parent.__isJsreportRequest__) {
|
|
@@ -57,7 +58,14 @@ module.exports = (obj, parent) => {
|
|
|
57
58
|
|
|
58
59
|
function normalizeJSONData (data) {
|
|
59
60
|
if (typeof data === 'string') {
|
|
60
|
-
|
|
61
|
+
try {
|
|
62
|
+
return JSON.parse(data)
|
|
63
|
+
} catch (parseError) {
|
|
64
|
+
throw createError('Unable to parse request data', {
|
|
65
|
+
weak: true,
|
|
66
|
+
original: parseError
|
|
67
|
+
})
|
|
68
|
+
}
|
|
61
69
|
}
|
|
62
70
|
|
|
63
71
|
return data
|
|
@@ -2,14 +2,10 @@ module.exports = ({ model, collections }, executeMainAction) => {
|
|
|
2
2
|
const store = {
|
|
3
3
|
model,
|
|
4
4
|
collection: (name) => ({
|
|
5
|
-
find: (
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
findOne: (q, req) => executeMainAction('documentStore.collection.findOne', {
|
|
10
|
-
query: q,
|
|
11
|
-
collection: name
|
|
12
|
-
}, req),
|
|
5
|
+
find: findMethod('documentStore.collection.find', name, false, executeMainAction),
|
|
6
|
+
findOne: findMethod('documentStore.collection.findOne', name, false, executeMainAction),
|
|
7
|
+
findAdmin: findMethod('documentStore.collection.find', name, true, executeMainAction),
|
|
8
|
+
findOneAdmin: findMethod('documentStore.collection.findOne', name, true, executeMainAction),
|
|
13
9
|
insert: async (doc, req) => {
|
|
14
10
|
const entity = await executeMainAction('documentStore.collection.insert', {
|
|
15
11
|
doc,
|
|
@@ -47,3 +43,18 @@ module.exports = ({ model, collections }, executeMainAction) => {
|
|
|
47
43
|
|
|
48
44
|
return store
|
|
49
45
|
}
|
|
46
|
+
|
|
47
|
+
function findMethod (actionName, collectionName, admin, executeMainAction) {
|
|
48
|
+
return async (q, req) => {
|
|
49
|
+
const payload = {
|
|
50
|
+
query: q,
|
|
51
|
+
collection: collectionName
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (admin) {
|
|
55
|
+
payload.admin = admin
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return executeMainAction(actionName, payload, req)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -203,10 +203,13 @@ module.exports = (reporter) => {
|
|
|
203
203
|
const wrappedTopLevelFunctions = {}
|
|
204
204
|
|
|
205
205
|
for (const h of Object.keys(topLevelFunctions)) {
|
|
206
|
+
// extra wrapping for enhance the error with the helper name
|
|
207
|
+
wrappedTopLevelFunctions[h] = wrapHelperForHelperNameWhenError(topLevelFunctions[h], h)
|
|
208
|
+
|
|
206
209
|
if (engine.getWrappingHelpersEnabled && engine.getWrappingHelpersEnabled(req) === false) {
|
|
207
|
-
wrappedTopLevelFunctions[h] = engine.wrapHelper(
|
|
210
|
+
wrappedTopLevelFunctions[h] = engine.wrapHelper(wrappedTopLevelFunctions[h], { context })
|
|
208
211
|
} else {
|
|
209
|
-
wrappedTopLevelFunctions[h] = wrapHelperForAsyncSupport(
|
|
212
|
+
wrappedTopLevelFunctions[h] = wrapHelperForAsyncSupport(wrappedTopLevelFunctions[h], asyncResultMap)
|
|
210
213
|
}
|
|
211
214
|
}
|
|
212
215
|
|
|
@@ -279,10 +282,14 @@ module.exports = (reporter) => {
|
|
|
279
282
|
const nestedErrorWithEntity = e.entity != null
|
|
280
283
|
|
|
281
284
|
const templatePath = req.template._id ? await reporter.folders.resolveEntityPath(req.template, 'templates', req) : 'anonymous'
|
|
285
|
+
|
|
286
|
+
const newError = reporter.createError(`Error when evaluating engine ${engine.name} for template ${templatePath}`, { original: e })
|
|
287
|
+
|
|
282
288
|
if (templatePath !== 'anonymous' && !nestedErrorWithEntity) {
|
|
283
289
|
const templateFound = await reporter.folders.resolveEntityFromPath(templatePath, 'templates', req)
|
|
290
|
+
|
|
284
291
|
if (templateFound != null) {
|
|
285
|
-
|
|
292
|
+
newError.entity = {
|
|
286
293
|
shortid: templateFound.entity.shortid,
|
|
287
294
|
name: templateFound.entity.name,
|
|
288
295
|
content
|
|
@@ -290,24 +297,29 @@ module.exports = (reporter) => {
|
|
|
290
297
|
}
|
|
291
298
|
}
|
|
292
299
|
|
|
293
|
-
e.message = `Error when evaluating engine ${engine.name} for template ${templatePath}\n` + e.message
|
|
294
|
-
|
|
295
300
|
if (!nestedErrorWithEntity && e.property !== 'content') {
|
|
296
|
-
|
|
301
|
+
newError.property = 'helpers'
|
|
297
302
|
}
|
|
298
303
|
|
|
299
304
|
if (nestedErrorWithEntity) {
|
|
300
305
|
// errors from nested assets evals needs an unwrap for some reason
|
|
301
|
-
|
|
306
|
+
newError.entity = { ...e.entity }
|
|
302
307
|
}
|
|
303
308
|
|
|
304
|
-
|
|
309
|
+
// we remove the decoratedSuffix (created from sandbox) from the stack trace (if it is there) because it
|
|
310
|
+
// just creates noise and duplication when printing the error,
|
|
311
|
+
// we just want the decoration on the message not in the stack trace
|
|
312
|
+
if (e.decoratedSuffix != null && newError.stack.includes(e.decoratedSuffix)) {
|
|
313
|
+
newError.stack = newError.stack.replace(e.decoratedSuffix, '')
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
throw newError
|
|
305
317
|
}
|
|
306
318
|
}
|
|
307
319
|
|
|
308
320
|
function wrapHelperForAsyncSupport (fn, asyncResultMap) {
|
|
309
321
|
return function (...args) {
|
|
310
|
-
|
|
322
|
+
// important to call the helper with the current this to preserve the same behavior
|
|
311
323
|
const fnResult = fn.call(this, ...args)
|
|
312
324
|
|
|
313
325
|
if (fnResult == null || typeof fnResult.then !== 'function') {
|
|
@@ -320,4 +332,27 @@ module.exports = (reporter) => {
|
|
|
320
332
|
return `{#asyncHelperResult ${asyncResultId}}`
|
|
321
333
|
}
|
|
322
334
|
}
|
|
335
|
+
|
|
336
|
+
function wrapHelperForHelperNameWhenError (fn, helperName) {
|
|
337
|
+
return function (...args) {
|
|
338
|
+
let fnResult
|
|
339
|
+
|
|
340
|
+
const getEnhancedHelperError = (e) => reporter.createError(`"${helperName}" helper call failed`, { original: e })
|
|
341
|
+
|
|
342
|
+
try {
|
|
343
|
+
// important to call the helper with the current this to preserve the same behavior
|
|
344
|
+
fnResult = fn.call(this, ...args)
|
|
345
|
+
} catch (syncError) {
|
|
346
|
+
throw getEnhancedHelperError(syncError)
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (fnResult == null || typeof fnResult.then !== 'function') {
|
|
350
|
+
return fnResult
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return fnResult.catch((asyncError) => {
|
|
354
|
+
throw getEnhancedHelperError(asyncError)
|
|
355
|
+
})
|
|
356
|
+
}
|
|
357
|
+
}
|
|
323
358
|
}
|
|
@@ -126,8 +126,9 @@ class Profiler {
|
|
|
126
126
|
|
|
127
127
|
if (parentReq) {
|
|
128
128
|
template = await this.reporter.templates.resolveTemplate(req)
|
|
129
|
+
|
|
129
130
|
// store a copy to prevent side-effects
|
|
130
|
-
req.context.resolvedTemplate = extend(true, {}, template)
|
|
131
|
+
req.context.resolvedTemplate = template != null ? extend(true, {}, template) : template
|
|
131
132
|
} else {
|
|
132
133
|
template = req.context.resolvedTemplate
|
|
133
134
|
}
|
|
@@ -130,7 +130,7 @@ module.exports = (reporter) => {
|
|
|
130
130
|
reporter.requestModulesCache.set(request.context.rootId, Object.create(null))
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
reporter.logger.info(`Starting rendering request
|
|
133
|
+
reporter.logger.info(`Starting rendering${childMsgDecorate(request)} request${counterMsgDecorate(request)}${userMsgDecorate(request)}`, request)
|
|
134
134
|
|
|
135
135
|
// TODO
|
|
136
136
|
/* if (reporter.entityTypeValidator.getSchema('TemplateType') != null) {
|
|
@@ -147,7 +147,7 @@ module.exports = (reporter) => {
|
|
|
147
147
|
await invokeRender(reporter, request, response)
|
|
148
148
|
await afterRender(reporter, request, response)
|
|
149
149
|
|
|
150
|
-
reporter.logger.info(`Rendering request
|
|
150
|
+
reporter.logger.info(`Rendering${childMsgDecorate(request)} request${counterMsgDecorate(request)} finished in ${(new Date().getTime() - request.context.startTimestamp)} ms`, request)
|
|
151
151
|
|
|
152
152
|
response.meta.logs = request.context.logs
|
|
153
153
|
|
|
@@ -164,9 +164,11 @@ module.exports = (reporter) => {
|
|
|
164
164
|
|
|
165
165
|
const logFn = e.weak ? reporter.logger.warn : reporter.logger.error
|
|
166
166
|
|
|
167
|
-
|
|
167
|
+
const errorMessage = reporter.createError(`Error when processing${childMsgDecorate(request)} render request${counterMsgDecorate(request)}`, { original: e }).message
|
|
168
168
|
|
|
169
|
-
logFn(
|
|
169
|
+
logFn(`${errorMessage}${e.stack != null ? '\n' + e.stack : ''}`, request)
|
|
170
|
+
|
|
171
|
+
logFn(`Rendering${childMsgDecorate(request)} request${counterMsgDecorate(request)} finished with error in ${(new Date().getTime() - request.context.startTimestamp)} ms`, request)
|
|
170
172
|
|
|
171
173
|
if (
|
|
172
174
|
parentReq &&
|
|
@@ -196,3 +198,27 @@ module.exports = (reporter) => {
|
|
|
196
198
|
}
|
|
197
199
|
}
|
|
198
200
|
}
|
|
201
|
+
|
|
202
|
+
function childMsgDecorate (req) {
|
|
203
|
+
if (!req.context.isChildRequest) {
|
|
204
|
+
return ''
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return ' (child)'
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function counterMsgDecorate (req) {
|
|
211
|
+
if (req.context.isChildRequest) {
|
|
212
|
+
return ''
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return ` ${req.context.reportCounter}`
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function userMsgDecorate (req) {
|
|
219
|
+
if (!req.context.user) {
|
|
220
|
+
return ''
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return ` (user: ${req.context.user.name})`
|
|
224
|
+
}
|
|
@@ -372,7 +372,11 @@ function decorateErrorMessage (e, sourceFilesInfo) {
|
|
|
372
372
|
}
|
|
373
373
|
|
|
374
374
|
if (suffix !== '') {
|
|
375
|
-
|
|
375
|
+
suffix = `\n\n${suffix}`
|
|
376
|
+
e.message = `${e.message}${suffix}`
|
|
377
|
+
// we store the suffix we added to the message so we can use it later
|
|
378
|
+
// to detect if we need to strip this from the stack or not
|
|
379
|
+
e.decoratedSuffix = suffix
|
|
376
380
|
}
|
|
377
381
|
}
|
|
378
382
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
const LRU = require('lru-cache')
|
|
2
2
|
const stackTrace = require('stack-trace')
|
|
3
3
|
const { customAlphabet } = require('nanoid')
|
|
4
|
-
const createSandbox = require('./createSandbox')
|
|
5
4
|
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 10)
|
|
5
|
+
const createSandbox = require('./createSandbox')
|
|
6
|
+
const normalizeError = require('../../shared/normalizeError')
|
|
6
7
|
|
|
7
8
|
module.exports = (reporter) => {
|
|
8
9
|
const functionsCache = LRU(reporter.options.sandbox.cache)
|
|
@@ -36,7 +37,16 @@ module.exports = (reporter) => {
|
|
|
36
37
|
onLog: (log) => {
|
|
37
38
|
// we mark any log done in sandbox as userLevel: true, this allows us to detect which logs belongs to user
|
|
38
39
|
// and can potentially contain sensitive information
|
|
39
|
-
|
|
40
|
+
|
|
41
|
+
let consoleType = log.level
|
|
42
|
+
|
|
43
|
+
if (consoleType === 'debug') {
|
|
44
|
+
consoleType = 'log'
|
|
45
|
+
} else if (consoleType === 'warn') {
|
|
46
|
+
consoleType = 'warning'
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
reporter.logger.debug(`(console:${consoleType}) ${log.message}`, { ...req, timestamp: log.timestamp, userLevel: true })
|
|
40
50
|
},
|
|
41
51
|
formatError: (error, moduleName) => {
|
|
42
52
|
error.message += ` To be able to require custom modules you need to add to configuration { "trustUserCode": true } or enable just specific module using { sandbox: { allowedModules": ["${moduleName}"] }`
|
|
@@ -135,10 +145,14 @@ module.exports = (reporter) => {
|
|
|
135
145
|
const initScriptInfo = await initFn(_getTopLevelFunctions, compileScript)
|
|
136
146
|
|
|
137
147
|
if (initScriptInfo) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
148
|
+
try {
|
|
149
|
+
await run(initScriptInfo.script, {
|
|
150
|
+
filename: initScriptInfo.filename || 'sandbox-init.js',
|
|
151
|
+
source: initScriptInfo.source
|
|
152
|
+
})
|
|
153
|
+
} catch (e) {
|
|
154
|
+
handleError(reporter, e)
|
|
155
|
+
}
|
|
142
156
|
}
|
|
143
157
|
}
|
|
144
158
|
|
|
@@ -162,43 +176,29 @@ module.exports = (reporter) => {
|
|
|
162
176
|
})
|
|
163
177
|
}).catch(__handleError);`
|
|
164
178
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
179
|
+
try {
|
|
180
|
+
return await run(executionCode, {
|
|
181
|
+
filename: 'sandbox.js',
|
|
182
|
+
source: userCode,
|
|
183
|
+
errorLineNumberOffset
|
|
184
|
+
})
|
|
185
|
+
} catch (e) {
|
|
186
|
+
handleError(reporter, e)
|
|
187
|
+
}
|
|
170
188
|
}
|
|
171
189
|
}
|
|
172
190
|
|
|
173
191
|
function handleError (reporter, errValue) {
|
|
174
|
-
let newError
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
typeof errValue === 'string'
|
|
185
|
-
)
|
|
186
|
-
|
|
187
|
-
if (!isValidError) {
|
|
188
|
-
if (Object.prototype.toString.call(errValue) === '[object Object]') {
|
|
189
|
-
newError = new Error(`User code threw with non-Error: ${JSON.stringify(errValue)}`)
|
|
190
|
-
} else {
|
|
191
|
-
newError = new Error(`User code threw with non-Error: ${errValue}`)
|
|
192
|
-
}
|
|
193
|
-
} else {
|
|
194
|
-
if (typeof errValue === 'string') {
|
|
195
|
-
newError = new Error(errValue)
|
|
196
|
-
} else {
|
|
197
|
-
newError = new Error(errValue.message)
|
|
198
|
-
Object.assign(newError, errValue)
|
|
199
|
-
if (errValue.stack) {
|
|
200
|
-
newError.stack = errValue.stack
|
|
201
|
-
}
|
|
192
|
+
let newError = normalizeError(errValue)
|
|
193
|
+
|
|
194
|
+
if (newError === errValue) {
|
|
195
|
+
// here it means the original error was valid in the first place,
|
|
196
|
+
// so it was not normalized
|
|
197
|
+
newError = new Error(errValue.message)
|
|
198
|
+
Object.assign(newError, errValue)
|
|
199
|
+
|
|
200
|
+
if (errValue.stack) {
|
|
201
|
+
newError.stack = errValue.stack
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
204
|
|
package/lib/worker/templates.js
CHANGED
|
@@ -28,12 +28,43 @@ module.exports = (reporter) => {
|
|
|
28
28
|
const template = req.context.resolvedTemplate
|
|
29
29
|
|
|
30
30
|
if (!template && !req.template.content) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
let error
|
|
32
|
+
|
|
33
|
+
if (
|
|
34
|
+
req.template._id ||
|
|
35
|
+
req.template.shortid ||
|
|
36
|
+
req.template.name
|
|
37
|
+
) {
|
|
38
|
+
const query = {}
|
|
39
|
+
|
|
40
|
+
if (req.template._id) {
|
|
41
|
+
query._id = req.template._id
|
|
42
|
+
} else if (req.template.shortid) {
|
|
43
|
+
query.shortid = req.template.shortid
|
|
44
|
+
} else if (req.template.name) {
|
|
45
|
+
query.name = req.template.name
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const templateFromLocal = await reporter.documentStore.collection('templates').findOneAdmin(query, req)
|
|
49
|
+
|
|
50
|
+
if (templateFromLocal == null) {
|
|
51
|
+
error = reporter.createError(`Unable to find specified template (${
|
|
52
|
+
(req.template.name || req.template.shortid || req.template._id)
|
|
53
|
+
})`, {
|
|
54
|
+
weak: true,
|
|
55
|
+
statusCode: 404
|
|
56
|
+
})
|
|
57
|
+
} else {
|
|
58
|
+
error = reporter.createError(`User does not have permissions to read template (${
|
|
59
|
+
(req.template.name || req.template.shortid || req.template._id)
|
|
60
|
+
})`, {
|
|
61
|
+
weak: true,
|
|
62
|
+
statusCode: 403
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
throw error
|
|
37
68
|
}
|
|
38
69
|
|
|
39
70
|
// store a copy to prevent side-effects, we ignore name from the req.template because it can be path "/path/to/template"
|