@jsreport/jsreport-core 3.12.0 → 4.0.1
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 +11 -0
- package/lib/main/optionsSchema.js +263 -271
- package/lib/main/reporter.js +8 -16
- package/lib/main/schemaValidator.js +1 -1
- package/lib/main/settings.js +14 -8
- package/lib/main/store/memoryStoreProvider.js +104 -99
- package/lib/worker/render/executeEngine.js +23 -1
- package/lib/worker/reporter.js +45 -0
- package/lib/worker/sandbox/createSandbox.js +56 -98
- package/lib/worker/sandbox/isolatedRequire.js +1 -21
- package/lib/worker/sandbox/propertiesSandbox.js +2 -2
- package/lib/worker/sandbox/runInSandbox.js +20 -7
- package/package.json +7 -7
- package/test/extensions/validExtensions/listeners/main.js +62 -51
- package/test/extensions/validExtensions/listeners/worker.js +81 -74
- package/test/store/common.js +55 -0
- package/lib/main/migration/resourcesToAssets.js +0 -230
- package/lib/main/migration/xlsxTemplatesToAssets.js +0 -128
|
@@ -21,7 +21,7 @@ module.exports = function createRunInSandbox (reporter) {
|
|
|
21
21
|
}, req) {
|
|
22
22
|
let jsreportProxy = null
|
|
23
23
|
|
|
24
|
-
// we use dynamic name because of the potential nested
|
|
24
|
+
// we use dynamic name because of the potential nested vm execution in the jsreportProxy.assets.require
|
|
25
25
|
// it may turn out it is a bad approach in assets so we gonna delete it here
|
|
26
26
|
const executionFnName = `${nanoid()}_executionFn`
|
|
27
27
|
|
|
@@ -29,11 +29,10 @@ module.exports = function createRunInSandbox (reporter) {
|
|
|
29
29
|
context.__appDirectory = reporter.options.appDirectory
|
|
30
30
|
context.__rootDirectory = reporter.options.rootDirectory
|
|
31
31
|
context.__parentModuleDirectory = reporter.options.parentModuleDirectory
|
|
32
|
-
context.setTimeout = setTimeout
|
|
33
32
|
context.__topLevelFunctions = {}
|
|
34
33
|
context.__handleError = (err) => handleError(reporter, err)
|
|
35
34
|
|
|
36
|
-
const { sourceFilesInfo, run, compileScript, restore, sandbox, sandboxRequire } = createSandbox(context, {
|
|
35
|
+
const { sourceFilesInfo, run, compileScript, restore, sandbox, sandboxRequire } = await createSandbox(context, {
|
|
37
36
|
rootDirectory: reporter.options.rootDirectory,
|
|
38
37
|
onLog: (log) => {
|
|
39
38
|
// we mark any log done in sandbox as userLevel: true, this allows us to detect which logs belongs to user
|
|
@@ -159,9 +158,23 @@ module.exports = function createRunInSandbox (reporter) {
|
|
|
159
158
|
}
|
|
160
159
|
|
|
161
160
|
const functionNames = getTopLevelFunctions(functionsCache, userCode)
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
161
|
+
|
|
162
|
+
// it is better we remove our internal functions so we avoid user having the chance
|
|
163
|
+
// to call them, as long as we force the execution to be truly async (with the await 1)
|
|
164
|
+
// then it is safe to delete __handleError from context, when the execution is truly
|
|
165
|
+
// async then it means the __handleError was already passed to catch handler,
|
|
166
|
+
// therefore safe to delete
|
|
167
|
+
const contextNormalizeCode = [
|
|
168
|
+
'await 1;',
|
|
169
|
+
`const ${executionFnName}_expose = ${executionFnName};`,
|
|
170
|
+
'delete this.__handleError;',
|
|
171
|
+
`delete this['${executionFnName}'];`
|
|
172
|
+
].join('')
|
|
173
|
+
|
|
174
|
+
const functionsCode = `return {topLevelFunctions: {${functionNames.map(h => `"${h}": ${h}`).join(',')}}, fnToExecute: ${executionFnName}_expose}`
|
|
175
|
+
|
|
176
|
+
const executionCode = `;(async () => { ${contextNormalizeCode}${userCode} \n\n;${functionsCode} })()
|
|
177
|
+
.then(({ topLevelFunctions, fnToExecute }) => {
|
|
165
178
|
const mergedTopLevelFunctions = { ...topLevelFunctions, ...__topLevelFunctions }
|
|
166
179
|
|
|
167
180
|
// expose top level functions to the sandbox context
|
|
@@ -170,7 +183,7 @@ module.exports = function createRunInSandbox (reporter) {
|
|
|
170
183
|
this[topLevelFnName] = topLevelFn
|
|
171
184
|
}
|
|
172
185
|
|
|
173
|
-
return
|
|
186
|
+
return fnToExecute({
|
|
174
187
|
topLevelFunctions: mergedTopLevelFunctions,
|
|
175
188
|
require,
|
|
176
189
|
console,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsreport/jsreport-core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.1",
|
|
4
4
|
"description": "javascript based business reporting",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"report",
|
|
@@ -40,9 +40,11 @@
|
|
|
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": "
|
|
43
|
+
"@jsreport/advanced-workers": "2.0.0",
|
|
44
44
|
"@jsreport/mingo": "2.4.1",
|
|
45
45
|
"@jsreport/reap": "0.1.0",
|
|
46
|
+
"@jsreport/serializator": "1.0.0",
|
|
47
|
+
"@jsreport/ses": "1.0.0",
|
|
46
48
|
"ajv": "6.12.6",
|
|
47
49
|
"app-root-path": "3.0.0",
|
|
48
50
|
"bytes": "3.1.2",
|
|
@@ -59,19 +61,17 @@
|
|
|
59
61
|
"lodash.get": "4.4.2",
|
|
60
62
|
"lodash.groupby": "4.6.0",
|
|
61
63
|
"lodash.omit": "4.5.0",
|
|
62
|
-
"lodash.set": "4.3.2",
|
|
63
64
|
"lru-cache": "4.1.1",
|
|
64
65
|
"ms": "2.1.3",
|
|
65
66
|
"nanoid": "3.2.0",
|
|
66
67
|
"nconf": "0.12.0",
|
|
67
68
|
"node.extend.without.arrays": "1.1.6",
|
|
68
|
-
"semver": "7.
|
|
69
|
-
"
|
|
69
|
+
"semver": "7.5.4",
|
|
70
|
+
"set-value": "4.1.0",
|
|
70
71
|
"stack-trace": "0.0.10",
|
|
71
72
|
"triple-beam": "1.3.0",
|
|
72
73
|
"unset-value": "2.0.1",
|
|
73
74
|
"uuid": "8.3.2",
|
|
74
|
-
"vm2": "3.9.19",
|
|
75
75
|
"winston": "3.8.1",
|
|
76
76
|
"winston-transport": "4.5.0",
|
|
77
77
|
"yieldable-json": "2.0.1"
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"winston-loggly-bulk": "3.2.1"
|
|
86
86
|
},
|
|
87
87
|
"engines": {
|
|
88
|
-
"node": ">=
|
|
88
|
+
"node": ">=18.15"
|
|
89
89
|
},
|
|
90
90
|
"standard": {
|
|
91
91
|
"env": {
|
|
@@ -1,51 +1,62 @@
|
|
|
1
|
-
|
|
2
|
-
module.exports = (reporter, definition) => {
|
|
3
|
-
reporter.tests = reporter.tests || {}
|
|
4
|
-
reporter.tests.beforeRenderListeners = reporter.createListenerCollection()
|
|
5
|
-
reporter.tests.afterRenderListeners = reporter.createListenerCollection()
|
|
6
|
-
reporter.tests.validateRenderListeners = reporter.createListenerCollection()
|
|
7
|
-
reporter.tests.afterTemplatingEnginesExecutedListeners = reporter.createListenerCollection()
|
|
8
|
-
|
|
9
|
-
reporter.registerMainAction('test-beforeRender-listeners', async (data, req) => {
|
|
10
|
-
data.req = reporter.Request(data.req)
|
|
11
|
-
await reporter.tests.beforeRenderListeners.fire(data.req, data.res)
|
|
12
|
-
return { req: data.req, res: data.res }
|
|
13
|
-
})
|
|
14
|
-
reporter.registerMainAction('test-afterRender-listeners', async (data, req) => {
|
|
15
|
-
data.req = reporter.Request(data.req)
|
|
16
|
-
await reporter.tests.afterRenderListeners.fire(data.req, data.res)
|
|
17
|
-
return { req: data.req, res: data.res }
|
|
18
|
-
})
|
|
19
|
-
reporter.registerMainAction('test-validateRender-listeners', async (data, req) => {
|
|
20
|
-
data.req = reporter.Request(data.req)
|
|
21
|
-
await reporter.tests.validateRenderListeners.fire(data.req, data.res)
|
|
22
|
-
return { req: data.req, res: data.res }
|
|
23
|
-
})
|
|
24
|
-
reporter.registerMainAction('test-afterTemplatingEnginesExecuted-listeners', async (data, req) => {
|
|
25
|
-
data.req = reporter.Request(data.req)
|
|
26
|
-
await reporter.tests.afterTemplatingEnginesExecutedListeners.fire(data.req, data.res)
|
|
27
|
-
return { req: data.req, res: data.res }
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
let beforeRenderEval
|
|
31
|
-
reporter.tests.beforeRenderEval = (fn) => {
|
|
32
|
-
beforeRenderEval = fn
|
|
33
|
-
}
|
|
34
|
-
reporter.registerMainAction('test-beforeRenderEval', async (data, req) => {
|
|
35
|
-
if (beforeRenderEval == null) {
|
|
36
|
-
return
|
|
37
|
-
}
|
|
38
|
-
return beforeRenderEval.toString()
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
let afterRenderEval
|
|
42
|
-
reporter.tests.afterRenderEval = (fn) => {
|
|
43
|
-
afterRenderEval = fn
|
|
44
|
-
}
|
|
45
|
-
reporter.registerMainAction('test-afterRenderEval', async (data, req) => {
|
|
46
|
-
if (afterRenderEval == null) {
|
|
47
|
-
return
|
|
48
|
-
}
|
|
49
|
-
return afterRenderEval.toString()
|
|
50
|
-
})
|
|
51
|
-
|
|
1
|
+
|
|
2
|
+
module.exports = (reporter, definition) => {
|
|
3
|
+
reporter.tests = reporter.tests || {}
|
|
4
|
+
reporter.tests.beforeRenderListeners = reporter.createListenerCollection()
|
|
5
|
+
reporter.tests.afterRenderListeners = reporter.createListenerCollection()
|
|
6
|
+
reporter.tests.validateRenderListeners = reporter.createListenerCollection()
|
|
7
|
+
reporter.tests.afterTemplatingEnginesExecutedListeners = reporter.createListenerCollection()
|
|
8
|
+
|
|
9
|
+
reporter.registerMainAction('test-beforeRender-listeners', async (data, req) => {
|
|
10
|
+
data.req = reporter.Request(data.req)
|
|
11
|
+
await reporter.tests.beforeRenderListeners.fire(data.req, data.res)
|
|
12
|
+
return { req: data.req, res: data.res }
|
|
13
|
+
})
|
|
14
|
+
reporter.registerMainAction('test-afterRender-listeners', async (data, req) => {
|
|
15
|
+
data.req = reporter.Request(data.req)
|
|
16
|
+
await reporter.tests.afterRenderListeners.fire(data.req, data.res)
|
|
17
|
+
return { req: data.req, res: data.res }
|
|
18
|
+
})
|
|
19
|
+
reporter.registerMainAction('test-validateRender-listeners', async (data, req) => {
|
|
20
|
+
data.req = reporter.Request(data.req)
|
|
21
|
+
await reporter.tests.validateRenderListeners.fire(data.req, data.res)
|
|
22
|
+
return { req: data.req, res: data.res }
|
|
23
|
+
})
|
|
24
|
+
reporter.registerMainAction('test-afterTemplatingEnginesExecuted-listeners', async (data, req) => {
|
|
25
|
+
data.req = reporter.Request(data.req)
|
|
26
|
+
await reporter.tests.afterTemplatingEnginesExecutedListeners.fire(data.req, data.res)
|
|
27
|
+
return { req: data.req, res: data.res }
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
let beforeRenderEval
|
|
31
|
+
reporter.tests.beforeRenderEval = (fn) => {
|
|
32
|
+
beforeRenderEval = fn
|
|
33
|
+
}
|
|
34
|
+
reporter.registerMainAction('test-beforeRenderEval', async (data, req) => {
|
|
35
|
+
if (beforeRenderEval == null) {
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
return beforeRenderEval.toString()
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
let afterRenderEval
|
|
42
|
+
reporter.tests.afterRenderEval = (fn) => {
|
|
43
|
+
afterRenderEval = fn
|
|
44
|
+
}
|
|
45
|
+
reporter.registerMainAction('test-afterRenderEval', async (data, req) => {
|
|
46
|
+
if (afterRenderEval == null) {
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
return afterRenderEval.toString()
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
let afterTemplatingEnginesExecutedEval
|
|
53
|
+
reporter.tests.afterTemplatingEnginesExecutedEval = (fn) => {
|
|
54
|
+
afterTemplatingEnginesExecutedEval = fn
|
|
55
|
+
}
|
|
56
|
+
reporter.registerMainAction('test-afterTemplatingEnginesExecutedEval', async (data, req) => {
|
|
57
|
+
if (afterTemplatingEnginesExecutedEval == null) {
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
return afterTemplatingEnginesExecutedEval.toString()
|
|
61
|
+
})
|
|
62
|
+
}
|
|
@@ -1,74 +1,81 @@
|
|
|
1
|
-
const extend = require('node.extend.without.arrays')
|
|
2
|
-
const vm = require('vm')
|
|
3
|
-
const Module = require('module')
|
|
4
|
-
const path = require('path')
|
|
5
|
-
const process = require('process')
|
|
6
|
-
|
|
7
|
-
module.exports = (reporter, definition) => {
|
|
8
|
-
reporter.initializeListeners.add('test-listeners', () => {
|
|
9
|
-
reporter.beforeRenderListeners.add('listeners', async (req, res) => {
|
|
10
|
-
const result = await reporter.executeMainAction('test-beforeRender-listeners', { req, res }, req)
|
|
11
|
-
extend(true, req, result.req)
|
|
12
|
-
extend(true, res, result.res)
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
reporter.afterRenderListeners.add('listeners', async (req, res) => {
|
|
16
|
-
const result = await reporter.executeMainAction('test-afterRender-listeners', { req, res }, req)
|
|
17
|
-
extend(true, req, result.req)
|
|
18
|
-
extend(true, res, result.res)
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
reporter.validateRenderListeners.add('listeners', async (req, res) => {
|
|
22
|
-
const result = await reporter.executeMainAction('test-validateRender-listeners', { req, res }, req)
|
|
23
|
-
extend(true, req, result.req)
|
|
24
|
-
extend(true, res, result.res)
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
reporter.afterTemplatingEnginesExecutedListeners.add('listeners', async (req, res) => {
|
|
28
|
-
const result = await reporter.executeMainAction('test-afterTemplatingEnginesExecuted-listeners', { req, res }, req)
|
|
29
|
-
extend(true, req, result.req)
|
|
30
|
-
extend(true, res, result.res)
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
const evalInWorker = (code, req, res) => {
|
|
34
|
-
const script = new vm.Script(`
|
|
35
|
-
;(function () {
|
|
36
|
-
return ${code}
|
|
37
|
-
})()
|
|
38
|
-
`)
|
|
39
|
-
|
|
40
|
-
return script.runInThisContext({
|
|
41
|
-
displayErrors: true
|
|
42
|
-
})(req, res, {
|
|
43
|
-
mainModuleFilename: require.main.filename,
|
|
44
|
-
require: (m) => {
|
|
45
|
-
if (Module.builtinModules.includes(m)) {
|
|
46
|
-
return require(m)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
try {
|
|
50
|
-
return require(path.join(process.cwd(), 'node_modules', m))
|
|
51
|
-
} catch (e) {
|
|
52
|
-
// hack, make it working in monorepo as well as normal extension
|
|
53
|
-
return require(path.join(process.cwd(), '../../node_modules', m))
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
reporter
|
|
57
|
-
})
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
reporter.afterRenderListeners.add('eval-listeners', async (req, res) => {
|
|
61
|
-
const code = await reporter.executeMainAction('test-afterRenderEval', {}, req)
|
|
62
|
-
if (code) {
|
|
63
|
-
return evalInWorker(code, req, res)
|
|
64
|
-
}
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
reporter.beforeRenderListeners.insert(0, 'eval-listeners', async (req, res) => {
|
|
68
|
-
const code = await reporter.executeMainAction('test-beforeRenderEval', {}, req)
|
|
69
|
-
if (code) {
|
|
70
|
-
return evalInWorker(code, req, res)
|
|
71
|
-
}
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
|
|
1
|
+
const extend = require('node.extend.without.arrays')
|
|
2
|
+
const vm = require('vm')
|
|
3
|
+
const Module = require('module')
|
|
4
|
+
const path = require('path')
|
|
5
|
+
const process = require('process')
|
|
6
|
+
|
|
7
|
+
module.exports = (reporter, definition) => {
|
|
8
|
+
reporter.initializeListeners.add('test-listeners', () => {
|
|
9
|
+
reporter.beforeRenderListeners.add('listeners', async (req, res) => {
|
|
10
|
+
const result = await reporter.executeMainAction('test-beforeRender-listeners', { req, res }, req)
|
|
11
|
+
extend(true, req, result.req)
|
|
12
|
+
extend(true, res, result.res)
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
reporter.afterRenderListeners.add('listeners', async (req, res) => {
|
|
16
|
+
const result = await reporter.executeMainAction('test-afterRender-listeners', { req, res }, req)
|
|
17
|
+
extend(true, req, result.req)
|
|
18
|
+
extend(true, res, result.res)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
reporter.validateRenderListeners.add('listeners', async (req, res) => {
|
|
22
|
+
const result = await reporter.executeMainAction('test-validateRender-listeners', { req, res }, req)
|
|
23
|
+
extend(true, req, result.req)
|
|
24
|
+
extend(true, res, result.res)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
reporter.afterTemplatingEnginesExecutedListeners.add('listeners', async (req, res) => {
|
|
28
|
+
const result = await reporter.executeMainAction('test-afterTemplatingEnginesExecuted-listeners', { req, res }, req)
|
|
29
|
+
extend(true, req, result.req)
|
|
30
|
+
extend(true, res, result.res)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
const evalInWorker = (code, req, res) => {
|
|
34
|
+
const script = new vm.Script(`
|
|
35
|
+
;(function () {
|
|
36
|
+
return ${code}
|
|
37
|
+
})()
|
|
38
|
+
`)
|
|
39
|
+
|
|
40
|
+
return script.runInThisContext({
|
|
41
|
+
displayErrors: true
|
|
42
|
+
})(req, res, {
|
|
43
|
+
mainModuleFilename: require.main.filename,
|
|
44
|
+
require: (m) => {
|
|
45
|
+
if (Module.builtinModules.includes(m)) {
|
|
46
|
+
return require(m)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
return require(path.join(process.cwd(), 'node_modules', m))
|
|
51
|
+
} catch (e) {
|
|
52
|
+
// hack, make it working in monorepo as well as normal extension
|
|
53
|
+
return require(path.join(process.cwd(), '../../node_modules', m))
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
reporter
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
reporter.afterRenderListeners.add('eval-listeners', async (req, res) => {
|
|
61
|
+
const code = await reporter.executeMainAction('test-afterRenderEval', {}, req)
|
|
62
|
+
if (code) {
|
|
63
|
+
return evalInWorker(code, req, res)
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
reporter.beforeRenderListeners.insert(0, 'eval-listeners', async (req, res) => {
|
|
68
|
+
const code = await reporter.executeMainAction('test-beforeRenderEval', {}, req)
|
|
69
|
+
if (code) {
|
|
70
|
+
return evalInWorker(code, req, res)
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
reporter.afterTemplatingEnginesExecutedListeners.add('eval-listeners', async (req, res) => {
|
|
75
|
+
const code = await reporter.executeMainAction('test-afterTemplatingEnginesExecutedEval', {}, req)
|
|
76
|
+
if (code) {
|
|
77
|
+
return evalInWorker(code, req, res)
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
}
|
package/test/store/common.js
CHANGED
|
@@ -141,6 +141,34 @@ function collectionTests (store, isInternal, runTransactions) {
|
|
|
141
141
|
res[0].phantom.header.should.be.eql('original')
|
|
142
142
|
})
|
|
143
143
|
|
|
144
|
+
it('update should use clones', async () => {
|
|
145
|
+
const colName = !isInternal ? 'templates' : 'internalTemplates'
|
|
146
|
+
|
|
147
|
+
await store().collection('folders').insert({
|
|
148
|
+
name: 'f1',
|
|
149
|
+
shortid: 'f1'
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
await getCollection(colName).insert({
|
|
153
|
+
name: 'test',
|
|
154
|
+
engine: 'none',
|
|
155
|
+
recipe: 'html',
|
|
156
|
+
content: 'original'
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
const set = {
|
|
160
|
+
folder: {
|
|
161
|
+
shortid: 'f1'
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
await getCollection(colName).update({ name: 'test' }, { $set: set })
|
|
166
|
+
set.folder.shortid = 'changing'
|
|
167
|
+
|
|
168
|
+
const res = await getCollection(colName).findOne({})
|
|
169
|
+
res.folder.shortid.should.be.eql('f1')
|
|
170
|
+
})
|
|
171
|
+
|
|
144
172
|
it('skip and limit', async () => {
|
|
145
173
|
const colName = !isInternal ? 'templates' : 'internalTemplates'
|
|
146
174
|
|
|
@@ -443,6 +471,33 @@ function collectionTests (store, isInternal, runTransactions) {
|
|
|
443
471
|
should(found != null).be.True()
|
|
444
472
|
})
|
|
445
473
|
|
|
474
|
+
it('insert with transaction should use clones', async () => {
|
|
475
|
+
const colName = !isInternal ? 'templates' : 'internalTemplates'
|
|
476
|
+
const req = Request({})
|
|
477
|
+
|
|
478
|
+
await store().beginTransaction(req)
|
|
479
|
+
|
|
480
|
+
try {
|
|
481
|
+
const t1 = {
|
|
482
|
+
name: 't1',
|
|
483
|
+
engine: 'none',
|
|
484
|
+
recipe: 'html'
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const newT1 = await getCollection(colName).insert(t1, req)
|
|
488
|
+
|
|
489
|
+
newT1.name = 'fake-t1'
|
|
490
|
+
|
|
491
|
+
await store().commitTransaction(req)
|
|
492
|
+
} catch (e) {
|
|
493
|
+
await store().rollbackTransaction(req)
|
|
494
|
+
throw e
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
const found = await getCollection(colName).findOne({ name: 't1' })
|
|
498
|
+
should(found).not.be.null()
|
|
499
|
+
})
|
|
500
|
+
|
|
446
501
|
it('should be able to rollback (insert)', async () => {
|
|
447
502
|
const colName = !isInternal ? 'templates' : 'internalTemplates'
|
|
448
503
|
const req = Request({})
|
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
const Request = require('../../shared/request')
|
|
2
|
-
|
|
3
|
-
module.exports = async (reporter) => {
|
|
4
|
-
if (
|
|
5
|
-
reporter.options.migrateResourcesToAssets === false ||
|
|
6
|
-
reporter.documentStore.collection('scripts') == null ||
|
|
7
|
-
reporter.documentStore.collection('data') == null
|
|
8
|
-
) {
|
|
9
|
-
return
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const migrated = await reporter.settings.findValue('core-migrated-resources')
|
|
13
|
-
|
|
14
|
-
if (migrated) {
|
|
15
|
-
return
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const req = Request({})
|
|
19
|
-
await reporter.documentStore.beginTransaction(req)
|
|
20
|
-
|
|
21
|
-
try {
|
|
22
|
-
const templateIds = await reporter.documentStore.collection('templates').find({}, { _id: 1 }, req)
|
|
23
|
-
|
|
24
|
-
if (templateIds.length !== 0) {
|
|
25
|
-
reporter.logger.debug('Running migration "resourcesToAssets"')
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const templateToAssetResourcesMap = new Map()
|
|
29
|
-
const dataToAssetMap = new Map()
|
|
30
|
-
const dataEntitiesToRemove = []
|
|
31
|
-
|
|
32
|
-
for (const templateId of templateIds) {
|
|
33
|
-
const template = await reporter.documentStore.collection('templates').findOne({ _id: templateId._id }, req)
|
|
34
|
-
|
|
35
|
-
if (template.resources == null) {
|
|
36
|
-
continue
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (Array.isArray(template.resources.items)) {
|
|
40
|
-
for (const dataItem of template.resources.items) {
|
|
41
|
-
const dataEntity = await reporter.documentStore.collection('data').findOne({ shortid: dataItem.shortid }, req)
|
|
42
|
-
|
|
43
|
-
if (dataEntity) {
|
|
44
|
-
let newAsset
|
|
45
|
-
|
|
46
|
-
if (dataToAssetMap.has(dataEntity._id)) {
|
|
47
|
-
newAsset = dataToAssetMap.get(dataEntity._id)
|
|
48
|
-
} else {
|
|
49
|
-
const assetProps = {
|
|
50
|
-
content: Buffer.from(dataEntity.dataJson || ''),
|
|
51
|
-
folder: dataEntity.folder || null
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (dataEntity.readPermissions != null) {
|
|
55
|
-
assetProps.readPermissions = dataEntity.readPermissions
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (dataEntity.editPermissions != null) {
|
|
59
|
-
assetProps.editPermissions = dataEntity.editPermissions
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
newAsset = await insertUnique(reporter, 'assets', `${dataEntity.name}.json`, assetProps, req)
|
|
63
|
-
|
|
64
|
-
dataToAssetMap.set(dataEntity._id, newAsset)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const assetResources = templateToAssetResourcesMap.get(template._id) || []
|
|
68
|
-
|
|
69
|
-
assetResources.push({
|
|
70
|
-
...newAsset,
|
|
71
|
-
originalName: dataEntity.name
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
templateToAssetResourcesMap.set(template._id, assetResources)
|
|
75
|
-
dataEntitiesToRemove.push(dataEntity._id)
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const templateAssetResources = templateToAssetResourcesMap.get(template._id) || []
|
|
81
|
-
|
|
82
|
-
if (templateAssetResources.length > 0) {
|
|
83
|
-
const scriptProps = {
|
|
84
|
-
content: (
|
|
85
|
-
`
|
|
86
|
-
// THIS SCRIPT WAS GENERATED BY MIGRATION IN V3, IT PROVIDES BACKWARD COMPATIBILITY
|
|
87
|
-
// WITH THE DEPRECATED jsreport-resources https://jsreport.net/learn/resources
|
|
88
|
-
// THE RECOMMENDATION NOW IS TO USE jsreport-localization https://jsreport.net/learn/localization
|
|
89
|
-
// SO WHEN YOU DECIDE TO USE jsreport-localization TO FULLY REPLACE THE jsreport-resources
|
|
90
|
-
// JUST REMOVE THIS SCRIPT FROM YOUR TEMPLATE AND DELETE IT
|
|
91
|
-
const jsreport = require('jsreport-proxy')
|
|
92
|
-
|
|
93
|
-
async function beforeRender (req, res) {
|
|
94
|
-
req.options.language = req.options.language || req.template.localization?.language
|
|
95
|
-
const defaultLanguage = ${template.resources.defaultLanguage != null ? '\'' + template.resources.defaultLanguage + '\'' : 'undefined'}
|
|
96
|
-
const assetsResources = [${templateAssetResources.map(a => `{ name: '${a.originalName}', shortid: '${a.shortid}' }`).join(', ')}]
|
|
97
|
-
|
|
98
|
-
const resources = await Promise.all(assetsResources.map(async (r) => {
|
|
99
|
-
const asset = await jsreport.documentStore.collection('assets').findOne({ shortid: r.shortid })
|
|
100
|
-
|
|
101
|
-
if (asset == null) {
|
|
102
|
-
throw new Error(\`Asset resources with shortid \${r.shortid} was not found (resource lookup)\`)
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
asset.resourceName = r.name
|
|
106
|
-
asset.content = asset.content.toString()
|
|
107
|
-
|
|
108
|
-
return asset
|
|
109
|
-
}))
|
|
110
|
-
|
|
111
|
-
resources.forEach((r) => {
|
|
112
|
-
r.dataJson = r.content
|
|
113
|
-
r.data = JSON.parse(r.content)
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
req.options.resources = resources
|
|
117
|
-
req.data.$resources = resources
|
|
118
|
-
|
|
119
|
-
const resourcesByName = {}
|
|
120
|
-
|
|
121
|
-
resources.forEach((r) => {
|
|
122
|
-
resourcesByName[r.resourceName] = r.data
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
req.options.resource = resourcesByName
|
|
126
|
-
req.data.$resource = resourcesByName
|
|
127
|
-
|
|
128
|
-
const isLocalizedRequest = req.options.language != null || defaultLanguage != null
|
|
129
|
-
|
|
130
|
-
if (isLocalizedRequest) {
|
|
131
|
-
let languageUsed
|
|
132
|
-
let applicableResources = []
|
|
133
|
-
|
|
134
|
-
if (req.options.language) {
|
|
135
|
-
languageUsed = req.options.language
|
|
136
|
-
applicableResources = resources.filter((r) => r.resourceName.startsWith(\`\${languageUsed}-\`))
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (!applicableResources.length && defaultLanguage) {
|
|
140
|
-
languageUsed = defaultLanguage
|
|
141
|
-
applicableResources = resources.filter((r) => r.resourceName.startsWith(\`\${languageUsed}-\`))
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
console.log(\`Found \${applicableResources.length} resources for language "\${languageUsed}"\`)
|
|
145
|
-
|
|
146
|
-
req.options.localizedResources = applicableResources
|
|
147
|
-
req.data.$localizedResources = applicableResources
|
|
148
|
-
|
|
149
|
-
const localizedResourceByName = {}
|
|
150
|
-
|
|
151
|
-
applicableResources.forEach((r) => {
|
|
152
|
-
localizedResourceByName[r.resourceName.substring(\`\${languageUsed}-\`.length)] = r.data
|
|
153
|
-
})
|
|
154
|
-
|
|
155
|
-
req.options.localizedResource = applicableResources.length === 1 ? applicableResources[0].data : localizedResourceByName
|
|
156
|
-
req.data.$localizedResource = req.options.localizedResource
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
`
|
|
160
|
-
),
|
|
161
|
-
folder: template.folder || null
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
if (template.readPermissions != null) {
|
|
165
|
-
scriptProps.readPermissions = template.readPermissions
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
if (template.editPermissions != null) {
|
|
169
|
-
scriptProps.editPermissions = template.editPermissions
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const newScript = await insertUnique(reporter, 'scripts', `${template.name}_resources`, scriptProps, req)
|
|
173
|
-
|
|
174
|
-
template.scripts = template.scripts || []
|
|
175
|
-
|
|
176
|
-
template.scripts.unshift({
|
|
177
|
-
shortid: newScript.shortid
|
|
178
|
-
})
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
template.resources = null
|
|
182
|
-
|
|
183
|
-
await reporter.documentStore.collection('templates').update({ _id: template._id }, { $set: template }, req)
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
for (const dataEntityId of dataEntitiesToRemove) {
|
|
187
|
-
await reporter.documentStore.collection('data').remove({ _id: dataEntityId }, req)
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
if (templateIds.length !== 0) {
|
|
191
|
-
reporter.logger.debug('Migration "resourcesToAssets" finished')
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
await reporter.documentStore.commitTransaction(req)
|
|
195
|
-
|
|
196
|
-
await reporter.settings.addOrSet('core-migrated-resources', true)
|
|
197
|
-
} catch (migrationErr) {
|
|
198
|
-
await reporter.documentStore.rollbackTransaction(req)
|
|
199
|
-
|
|
200
|
-
migrationErr.message = `Migration "resourcesToAssets" failed: ${migrationErr.message}`
|
|
201
|
-
|
|
202
|
-
throw migrationErr
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
async function insertUnique (reporter, collectionName, baseName, entity, req) {
|
|
207
|
-
let newEntity
|
|
208
|
-
let tryCount = 0
|
|
209
|
-
|
|
210
|
-
while (newEntity == null) {
|
|
211
|
-
try {
|
|
212
|
-
const entityName = '_'.repeat(tryCount) + baseName
|
|
213
|
-
|
|
214
|
-
newEntity = await reporter.documentStore.collection(collectionName).insert({
|
|
215
|
-
...entity,
|
|
216
|
-
name: entityName
|
|
217
|
-
}, req)
|
|
218
|
-
|
|
219
|
-
return newEntity
|
|
220
|
-
} catch (insertError) {
|
|
221
|
-
tryCount++
|
|
222
|
-
|
|
223
|
-
if (insertError.code === 'DUPLICATED_ENTITY') {
|
|
224
|
-
continue
|
|
225
|
-
} else {
|
|
226
|
-
throw insertError
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|