@jsreport/jsreport-core 3.0.1 → 3.1.2-test.2
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/LICENSE +166 -166
- package/README.md +298 -284
- package/index.js +29 -27
- package/lib/main/blobStorage/blobStorage.js +52 -47
- package/lib/main/blobStorage/inMemoryProvider.js +27 -27
- package/lib/main/blobStorage/mainActions.js +24 -24
- package/lib/main/createDefaultLoggerFormat.js +17 -17
- package/lib/main/defaults.js +14 -14
- package/lib/main/extensions/discover.js +20 -20
- package/lib/main/extensions/extensionsManager.js +264 -265
- package/lib/main/extensions/fileUtils.js +56 -55
- package/lib/main/extensions/findVersion.js +49 -53
- package/lib/main/extensions/locationCache.js +103 -97
- package/lib/main/extensions/sorter.js +10 -10
- package/lib/main/extensions/validateMinimalVersion.js +50 -50
- package/lib/main/folders/cascadeFolderRemove.js +25 -25
- package/lib/main/folders/getEntitiesInFolder.js +53 -53
- package/lib/main/folders/index.js +42 -42
- package/lib/main/folders/moveBetweenFolders.js +354 -354
- package/lib/main/folders/validateDuplicatedName.js +107 -107
- package/lib/main/folders/validateReservedName.js +53 -53
- package/lib/main/logger.js +244 -244
- package/lib/main/migration/resourcesToAssets.js +230 -210
- package/lib/main/migration/xlsxTemplatesToAssets.js +128 -118
- package/lib/main/monitoring.js +91 -91
- package/lib/main/optionsLoad.js +237 -237
- package/lib/main/optionsSchema.js +237 -237
- package/lib/main/reporter.js +579 -578
- package/lib/main/schemaValidator.js +252 -252
- package/lib/main/settings.js +154 -154
- package/lib/main/store/checkDuplicatedId.js +27 -27
- package/lib/main/store/collection.js +329 -329
- package/lib/main/store/documentStore.js +469 -469
- package/lib/main/store/mainActions.js +28 -28
- package/lib/main/store/memoryStoreProvider.js +99 -99
- package/lib/main/store/queue.js +48 -48
- package/lib/main/store/referenceUtils.js +251 -251
- package/lib/main/store/setupValidateId.js +43 -43
- package/lib/main/store/setupValidateShortid.js +71 -71
- package/lib/main/store/transaction.js +69 -69
- package/lib/main/store/typeUtils.js +180 -180
- package/lib/main/templates.js +34 -34
- package/lib/main/validateEntityName.js +62 -62
- package/lib/shared/createError.js +36 -36
- package/lib/shared/encryption.js +114 -114
- package/lib/shared/folders/index.js +11 -11
- package/lib/shared/folders/normalizeEntityPath.js +15 -15
- package/lib/shared/folders/resolveEntityFromPath.js +88 -88
- package/lib/shared/folders/resolveEntityPath.js +46 -46
- package/lib/shared/folders/resolveFolderFromPath.js +38 -38
- package/lib/shared/generateRequestId.js +4 -4
- package/lib/shared/listenerCollection.js +169 -0
- package/lib/shared/normalizeMetaFromLogs.js +30 -30
- package/lib/shared/reporter.js +123 -123
- package/lib/shared/request.js +64 -64
- package/lib/shared/tempFilesHandler.js +81 -81
- package/lib/shared/templates.js +82 -82
- package/lib/static/helpers.js +33 -33
- package/lib/worker/blobStorage.js +34 -34
- package/lib/worker/defaultProxyExtend.js +46 -46
- package/lib/worker/documentStore.js +49 -49
- package/lib/worker/extensionsManager.js +17 -17
- package/lib/worker/logger.js +48 -48
- package/lib/worker/render/diff.js +138 -138
- package/lib/worker/render/executeEngine.js +207 -200
- package/lib/worker/render/htmlRecipe.js +10 -10
- package/lib/worker/render/moduleHelper.js +43 -43
- package/lib/worker/render/noneEngine.js +12 -12
- package/lib/worker/render/profiler.js +158 -158
- package/lib/worker/render/render.js +205 -209
- package/lib/worker/render/resolveReferences.js +60 -60
- package/lib/worker/reporter.js +191 -187
- package/lib/worker/sandbox/runInSandbox.js +13 -4
- package/lib/worker/sandbox/safeSandbox.js +828 -822
- package/lib/worker/templates.js +78 -78
- package/lib/worker/workerHandler.js +54 -54
- package/package.json +92 -92
- package/test/blobStorage/common.js +21 -21
- package/test/store/common.js +1449 -1449
|
@@ -1,118 +1,128 @@
|
|
|
1
|
-
const Request = require('../../shared/request')
|
|
2
|
-
|
|
3
|
-
module.exports = async (reporter) => {
|
|
4
|
-
if (
|
|
5
|
-
reporter.options.migrateXlsxTemplatesToAssets === false ||
|
|
6
|
-
reporter.documentStore.collection('xlsxTemplates') == null ||
|
|
7
|
-
reporter.documentStore.collection('assets') == null
|
|
8
|
-
) {
|
|
9
|
-
return
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const migrated = await reporter.settings.findValue('core-migrated-xlsxTemplates')
|
|
13
|
-
|
|
14
|
-
if (migrated) {
|
|
15
|
-
return
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const req = Request({})
|
|
19
|
-
await reporter.documentStore.beginTransaction(req)
|
|
20
|
-
|
|
21
|
-
try {
|
|
22
|
-
const xlsxTemplateIds = await reporter.documentStore.collection('xlsxTemplates').find({}, { _id: 1 }, req)
|
|
23
|
-
|
|
24
|
-
if (xlsxTemplateIds.length !== 0) {
|
|
25
|
-
reporter.logger.debug('Running migration "xlsxTemplatesToAssets"')
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const xlsxTemplateToAssetMap = new Map()
|
|
29
|
-
|
|
30
|
-
for (const xlsxTemplateId of xlsxTemplateIds) {
|
|
31
|
-
const xlsxTemplate = await reporter.documentStore.collection('xlsxTemplates').findOne({ _id: xlsxTemplateId._id }, req)
|
|
32
|
-
|
|
33
|
-
if (!xlsxTemplateToAssetMap.has(xlsxTemplate.shortid)) {
|
|
34
|
-
let newAsset
|
|
35
|
-
let tryCount = 0
|
|
36
|
-
|
|
37
|
-
while (newAsset == null) {
|
|
38
|
-
try {
|
|
39
|
-
const assetName = `${'_'.repeat(tryCount) + xlsxTemplate.name}.xlsx`
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
name: assetName,
|
|
43
|
-
content: xlsxTemplate.contentRaw,
|
|
44
|
-
folder: xlsxTemplate.folder || null
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
1
|
+
const Request = require('../../shared/request')
|
|
2
|
+
|
|
3
|
+
module.exports = async (reporter) => {
|
|
4
|
+
if (
|
|
5
|
+
reporter.options.migrateXlsxTemplatesToAssets === false ||
|
|
6
|
+
reporter.documentStore.collection('xlsxTemplates') == null ||
|
|
7
|
+
reporter.documentStore.collection('assets') == null
|
|
8
|
+
) {
|
|
9
|
+
return
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const migrated = await reporter.settings.findValue('core-migrated-xlsxTemplates')
|
|
13
|
+
|
|
14
|
+
if (migrated) {
|
|
15
|
+
return
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const req = Request({})
|
|
19
|
+
await reporter.documentStore.beginTransaction(req)
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const xlsxTemplateIds = await reporter.documentStore.collection('xlsxTemplates').find({}, { _id: 1 }, req)
|
|
23
|
+
|
|
24
|
+
if (xlsxTemplateIds.length !== 0) {
|
|
25
|
+
reporter.logger.debug('Running migration "xlsxTemplatesToAssets"')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const xlsxTemplateToAssetMap = new Map()
|
|
29
|
+
|
|
30
|
+
for (const xlsxTemplateId of xlsxTemplateIds) {
|
|
31
|
+
const xlsxTemplate = await reporter.documentStore.collection('xlsxTemplates').findOne({ _id: xlsxTemplateId._id }, req)
|
|
32
|
+
|
|
33
|
+
if (!xlsxTemplateToAssetMap.has(xlsxTemplate.shortid)) {
|
|
34
|
+
let newAsset
|
|
35
|
+
let tryCount = 0
|
|
36
|
+
|
|
37
|
+
while (newAsset == null) {
|
|
38
|
+
try {
|
|
39
|
+
const assetName = `${'_'.repeat(tryCount) + xlsxTemplate.name}.xlsx`
|
|
40
|
+
|
|
41
|
+
const assetProps = {
|
|
42
|
+
name: assetName,
|
|
43
|
+
content: xlsxTemplate.contentRaw,
|
|
44
|
+
folder: xlsxTemplate.folder || null
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (xlsxTemplate.readPermissions != null) {
|
|
48
|
+
assetProps.readPermissions = xlsxTemplate.readPermissions
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (xlsxTemplate.editPermissions != null) {
|
|
52
|
+
assetProps.editPermissions = xlsxTemplate.editPermissions
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
newAsset = await reporter.documentStore.collection('assets').insert(assetProps, req)
|
|
56
|
+
} catch (insertError) {
|
|
57
|
+
tryCount++
|
|
58
|
+
|
|
59
|
+
if (insertError.code === 'DUPLICATED_ENTITY') {
|
|
60
|
+
continue
|
|
61
|
+
} else {
|
|
62
|
+
throw insertError
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
xlsxTemplateToAssetMap.set(xlsxTemplate.shortid, newAsset)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const templateIds = await reporter.documentStore.collection('templates').find({}, { _id: 1 }, req)
|
|
72
|
+
|
|
73
|
+
for (const templateId of templateIds) {
|
|
74
|
+
const template = await reporter.documentStore.collection('templates').findOne({ _id: templateId._id }, req)
|
|
75
|
+
let continueUpdate = false
|
|
76
|
+
|
|
77
|
+
// handle jsreport-xlsx migration
|
|
78
|
+
if (template.xlsxTemplate != null) {
|
|
79
|
+
continueUpdate = true
|
|
80
|
+
|
|
81
|
+
const xlsxTemplateRef = template.xlsxTemplate
|
|
82
|
+
|
|
83
|
+
template.xlsxTemplate = null
|
|
84
|
+
|
|
85
|
+
if (xlsxTemplateRef.shortid != null && xlsxTemplateToAssetMap.has(xlsxTemplateRef.shortid)) {
|
|
86
|
+
template.xlsx = template.xlsx || {}
|
|
87
|
+
template.xlsx.templateAssetShortid = xlsxTemplateToAssetMap.get(xlsxTemplateRef.shortid).shortid
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// handle jsreport-html-to-xlsx migration
|
|
92
|
+
if (template.baseXlsxTemplate != null) {
|
|
93
|
+
continueUpdate = true
|
|
94
|
+
|
|
95
|
+
const baseXlsxTemplateRef = template.baseXlsxTemplate
|
|
96
|
+
|
|
97
|
+
template.baseXlsxTemplate = null
|
|
98
|
+
|
|
99
|
+
if (baseXlsxTemplateRef.shortid != null && xlsxTemplateToAssetMap.has(baseXlsxTemplateRef.shortid)) {
|
|
100
|
+
template.htmlToXlsx = template.htmlToXlsx || {}
|
|
101
|
+
template.htmlToXlsx.templateAssetShortid = xlsxTemplateToAssetMap.get(baseXlsxTemplateRef.shortid).shortid
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (continueUpdate) {
|
|
106
|
+
await reporter.documentStore.collection('templates').update({ _id: template._id }, { $set: template }, req)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
for (const xlsxTemplateId of xlsxTemplateIds) {
|
|
111
|
+
await reporter.documentStore.collection('xlsxTemplates').remove({ _id: xlsxTemplateId._id }, req)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (xlsxTemplateIds.length !== 0) {
|
|
115
|
+
reporter.logger.debug('Migration "xlsxTemplatesToAssets" finished')
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
await reporter.documentStore.commitTransaction(req)
|
|
119
|
+
|
|
120
|
+
await reporter.settings.addOrSet('core-migrated-xlsxTemplates', true)
|
|
121
|
+
} catch (migrationErr) {
|
|
122
|
+
await reporter.documentStore.rollbackTransaction(req)
|
|
123
|
+
|
|
124
|
+
migrationErr.message = `Migration "xlsxTemplatesToAssets" failed: ${migrationErr.message}`
|
|
125
|
+
|
|
126
|
+
throw migrationErr
|
|
127
|
+
}
|
|
128
|
+
}
|
package/lib/main/monitoring.js
CHANGED
|
@@ -1,91 +1,91 @@
|
|
|
1
|
-
const os = require('os')
|
|
2
|
-
|
|
3
|
-
function cpu () {
|
|
4
|
-
// Create function to get CPU information
|
|
5
|
-
function cpuAverage () {
|
|
6
|
-
// Initialise sum of idle and time of cores and fetch CPU info
|
|
7
|
-
let totalIdle = 0; let totalTick = 0
|
|
8
|
-
const cpus = os.cpus()
|
|
9
|
-
|
|
10
|
-
// Loop through CPU cores
|
|
11
|
-
for (let i = 0, len = cpus.length; i < len; i++) {
|
|
12
|
-
// Select CPU core
|
|
13
|
-
const cpu = cpus[i]
|
|
14
|
-
|
|
15
|
-
// Total up the time in the cores tick
|
|
16
|
-
for (const type in cpu.times) {
|
|
17
|
-
totalTick += cpu.times[type]
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Total up the idle time of the core
|
|
21
|
-
totalIdle += cpu.times.idle
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Return the average Idle and Tick times
|
|
25
|
-
return { idle: totalIdle / cpus.length, total: totalTick / cpus.length }
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Grab first CPU Measure
|
|
29
|
-
const startMeasure = cpuAverage()
|
|
30
|
-
|
|
31
|
-
return new Promise((resolve) => {
|
|
32
|
-
// Set delay for second Measure
|
|
33
|
-
setTimeout(function () {
|
|
34
|
-
// Grab second Measure
|
|
35
|
-
const endMeasure = cpuAverage()
|
|
36
|
-
|
|
37
|
-
// Calculate the difference in idle and total time between the measures
|
|
38
|
-
const idleDifference = endMeasure.idle - startMeasure.idle
|
|
39
|
-
const totalDifference = endMeasure.total - startMeasure.total
|
|
40
|
-
|
|
41
|
-
// Calculate the average percentage CPU usage
|
|
42
|
-
const percentageCPU = 100 - ~~(100 * idleDifference / totalDifference)
|
|
43
|
-
|
|
44
|
-
// Output result to console
|
|
45
|
-
resolve(percentageCPU)
|
|
46
|
-
}, 1000)
|
|
47
|
-
})
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
class Monitoring {
|
|
51
|
-
constructor (reporter) {
|
|
52
|
-
this.reporter = reporter
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
async execute () {
|
|
56
|
-
const monitoring = {
|
|
57
|
-
cpu: await cpu(),
|
|
58
|
-
freemem: Math.round(os.freemem() / 1024 / 1024),
|
|
59
|
-
timestamp: new Date(),
|
|
60
|
-
hostname: os.hostname()
|
|
61
|
-
}
|
|
62
|
-
return this.reporter.documentStore.collection('monitoring').insert(monitoring)
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
init () {
|
|
66
|
-
this._interval = setInterval(() => {
|
|
67
|
-
this.execute()
|
|
68
|
-
}, 60000)
|
|
69
|
-
this._interval.unref()
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
close () {
|
|
73
|
-
clearInterval(this._interval)
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
module.exports = (reporter) => {
|
|
78
|
-
reporter.documentStore.registerEntityType('MonitoringType', {
|
|
79
|
-
cpu: { type: 'Edm.Int32' },
|
|
80
|
-
freemem: { type: 'Edm.Int32' },
|
|
81
|
-
timestamp: { type: 'Edm.DateTimeOffset', schema: { type: 'null' } },
|
|
82
|
-
hostname: { type: 'Edm.String' }
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
reporter.documentStore.registerEntitySet('monitoring', {
|
|
86
|
-
entityType: 'jsreport.MonitoringType',
|
|
87
|
-
exportable: false
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
reporter.monitoring = new Monitoring(reporter)
|
|
91
|
-
}
|
|
1
|
+
const os = require('os')
|
|
2
|
+
|
|
3
|
+
function cpu () {
|
|
4
|
+
// Create function to get CPU information
|
|
5
|
+
function cpuAverage () {
|
|
6
|
+
// Initialise sum of idle and time of cores and fetch CPU info
|
|
7
|
+
let totalIdle = 0; let totalTick = 0
|
|
8
|
+
const cpus = os.cpus()
|
|
9
|
+
|
|
10
|
+
// Loop through CPU cores
|
|
11
|
+
for (let i = 0, len = cpus.length; i < len; i++) {
|
|
12
|
+
// Select CPU core
|
|
13
|
+
const cpu = cpus[i]
|
|
14
|
+
|
|
15
|
+
// Total up the time in the cores tick
|
|
16
|
+
for (const type in cpu.times) {
|
|
17
|
+
totalTick += cpu.times[type]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Total up the idle time of the core
|
|
21
|
+
totalIdle += cpu.times.idle
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Return the average Idle and Tick times
|
|
25
|
+
return { idle: totalIdle / cpus.length, total: totalTick / cpus.length }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Grab first CPU Measure
|
|
29
|
+
const startMeasure = cpuAverage()
|
|
30
|
+
|
|
31
|
+
return new Promise((resolve) => {
|
|
32
|
+
// Set delay for second Measure
|
|
33
|
+
setTimeout(function () {
|
|
34
|
+
// Grab second Measure
|
|
35
|
+
const endMeasure = cpuAverage()
|
|
36
|
+
|
|
37
|
+
// Calculate the difference in idle and total time between the measures
|
|
38
|
+
const idleDifference = endMeasure.idle - startMeasure.idle
|
|
39
|
+
const totalDifference = endMeasure.total - startMeasure.total
|
|
40
|
+
|
|
41
|
+
// Calculate the average percentage CPU usage
|
|
42
|
+
const percentageCPU = 100 - ~~(100 * idleDifference / totalDifference)
|
|
43
|
+
|
|
44
|
+
// Output result to console
|
|
45
|
+
resolve(percentageCPU)
|
|
46
|
+
}, 1000)
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
class Monitoring {
|
|
51
|
+
constructor (reporter) {
|
|
52
|
+
this.reporter = reporter
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async execute () {
|
|
56
|
+
const monitoring = {
|
|
57
|
+
cpu: await cpu(),
|
|
58
|
+
freemem: Math.round(os.freemem() / 1024 / 1024),
|
|
59
|
+
timestamp: new Date(),
|
|
60
|
+
hostname: os.hostname()
|
|
61
|
+
}
|
|
62
|
+
return this.reporter.documentStore.collection('monitoring').insert(monitoring)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
init () {
|
|
66
|
+
this._interval = setInterval(() => {
|
|
67
|
+
this.execute().catch((e) => this.reporter.logger.warn('unable to persist monitoring info, but no need to worry, we will retry, details:' + e.stack))
|
|
68
|
+
}, 60000)
|
|
69
|
+
this._interval.unref()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
close () {
|
|
73
|
+
clearInterval(this._interval)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
module.exports = (reporter) => {
|
|
78
|
+
reporter.documentStore.registerEntityType('MonitoringType', {
|
|
79
|
+
cpu: { type: 'Edm.Int32' },
|
|
80
|
+
freemem: { type: 'Edm.Int32' },
|
|
81
|
+
timestamp: { type: 'Edm.DateTimeOffset', schema: { type: 'null' } },
|
|
82
|
+
hostname: { type: 'Edm.String' }
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
reporter.documentStore.registerEntitySet('monitoring', {
|
|
86
|
+
entityType: 'jsreport.MonitoringType',
|
|
87
|
+
exportable: false
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
reporter.monitoring = new Monitoring(reporter)
|
|
91
|
+
}
|