ml-testing-toolkit 18.18.0 → 18.19.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/.grype.yaml +11 -15
- package/CHANGELOG.md +7 -0
- package/examples/collections/dfsp/p2p_fx_happy_path.json +34 -6
- package/examples/collections/dfsp/p2p_happy_path.json +39 -4
- package/examples/environments/dfsp_local_environment.json +6 -6
- package/package.json +1 -1
- package/spec_files/rules_callback/default.json +5 -5
- package/src/index.js +0 -16
- package/src/lib/api-management.js +1 -7
- package/src/lib/api-routes/config.js +0 -22
- package/src/lib/api-server.js +0 -35
- package/src/lib/arrayStore.js +1 -14
- package/src/lib/httpAgentStore.js +2 -22
- package/src/lib/mocking/middleware-functions/ilpModel.js +5 -5
- package/src/lib/mocking/openApiMockHandler.js +1 -7
- package/src/lib/notificationEmitter.js +0 -7
- package/src/lib/objectStore.js +1 -14
- package/src/lib/requestLogger.js +1 -2
- package/src/lib/scripting-engines/postman-sandbox.js +9 -114
- package/src/lib/scripting-engines/vm-javascript-sandbox.js +1 -115
- package/src/lib/test-outbound/outbound-initiator.js +4 -12
- package/src/lib/performanceOptimizer.js +0 -102
package/.grype.yaml
CHANGED
|
@@ -1,28 +1,24 @@
|
|
|
1
1
|
ignore:
|
|
2
|
+
# fast-xml-parser vulnerability
|
|
2
3
|
- vulnerability: GHSA-37qj-frw5-hhjh
|
|
3
|
-
|
|
4
|
+
# lodash vulnerabilities
|
|
4
5
|
- vulnerability: GHSA-xxjr-mmjv-4gpg
|
|
5
|
-
|
|
6
|
+
# @isaacs/brace-expansion vulnerability
|
|
6
7
|
- vulnerability: GHSA-7h2j-956f-4vf2
|
|
7
|
-
|
|
8
|
+
# busybox vulnerabilities
|
|
8
9
|
- vulnerability: CVE-2025-60876
|
|
9
|
-
|
|
10
|
+
# glob vulnerabilities
|
|
10
11
|
- vulnerability: GHSA-5j98-mcp5-4vw2
|
|
11
|
-
|
|
12
|
-
reason: "glob 10.4.5→10.5.0 and 11.0.3→11.1.0"
|
|
12
|
+
# tar vulnerabilities
|
|
13
13
|
- vulnerability: GHSA-34x7-hfp2-rc4v
|
|
14
|
-
reason: "tar 7.5.1→7.5.7"
|
|
15
|
-
- vulnerability: GHSA-73rr-hh4g-fpgx
|
|
16
|
-
reason: "diff 8.0.2→8.0.3"
|
|
17
14
|
- vulnerability: GHSA-r6q2-hw4h-h46w
|
|
18
|
-
reason: "tar 7.5.1→7.5.4"
|
|
19
|
-
- vulnerability: GHSA-3966-f6p6-2qr9
|
|
20
|
-
reason: "npm 11.6.2"
|
|
21
15
|
- vulnerability: GHSA-8qq5-rm4j-mr97
|
|
22
|
-
reason: "tar 7.5.1→7.5.3"
|
|
23
16
|
- vulnerability: GHSA-29xp-372q-xqph
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
# diff vulnerability
|
|
18
|
+
- vulnerability: GHSA-73rr-hh4g-fpgx
|
|
19
|
+
# npm vulnerability
|
|
20
|
+
- vulnerability: GHSA-3966-f6p6-2qr9
|
|
21
|
+
# Set output format defaults
|
|
26
22
|
output:
|
|
27
23
|
- "table"
|
|
28
24
|
- "json"
|
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
# Changelog: [mojaloop/thirdparty-api-svc](https://github.com/mojaloop/thirdparty-api-svc)
|
|
2
|
+
## [18.19.0](https://github.com/mojaloop/ml-testing-toolkit/compare/v18.18.0...v18.19.0) (2026-02-06)
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
* revert performance-optimization ([#367](https://github.com/mojaloop/ml-testing-toolkit/issues/367)) ([5b7c6c5](https://github.com/mojaloop/ml-testing-toolkit/commit/5b7c6c52d8ffe949ca16fa8374bea3fbc08c1a0c)), closes [#366](https://github.com/mojaloop/ml-testing-toolkit/issues/366)
|
|
8
|
+
|
|
2
9
|
## [18.18.0](https://github.com/mojaloop/ml-testing-toolkit/compare/v18.17.3...v18.18.0) (2026-02-06)
|
|
3
10
|
|
|
4
11
|
|
|
@@ -159,6 +159,13 @@
|
|
|
159
159
|
"expect(callback.headers['Content-Length']).to.not.equal('0')"
|
|
160
160
|
]
|
|
161
161
|
},
|
|
162
|
+
{
|
|
163
|
+
"id": 4,
|
|
164
|
+
"description": "Callback FSP Destination equal to request FSP Source",
|
|
165
|
+
"exec": [
|
|
166
|
+
"expect(callback.headers['fspiop-destination']).to.equal('{$request.headers['FSPIOP-Source']}')"
|
|
167
|
+
]
|
|
168
|
+
},
|
|
162
169
|
{
|
|
163
170
|
"id": 5,
|
|
164
171
|
"description": "Callback body should contain conversionTerms",
|
|
@@ -204,8 +211,8 @@
|
|
|
204
211
|
"operationPath": "/quotes",
|
|
205
212
|
"method": "post",
|
|
206
213
|
"headers": {
|
|
207
|
-
"Accept": "
|
|
208
|
-
"Content-Type": "
|
|
214
|
+
"Accept": "application/vnd.interoperability.quotes+json;version=1.0",
|
|
215
|
+
"Content-Type": "application/vnd.interoperability.quotes+json;version=1.0",
|
|
209
216
|
"Date": "{$function.generic.curDate}",
|
|
210
217
|
"FSPIOP-Source": "{$inputs.fromFspId}",
|
|
211
218
|
"FSPIOP-Destination": "{$prev.1.callback.body.party.partyIdInfo.fspId}"
|
|
@@ -269,6 +276,13 @@
|
|
|
269
276
|
"expect(callback.headers['Content-Length']).to.not.equal('0')"
|
|
270
277
|
]
|
|
271
278
|
},
|
|
279
|
+
{
|
|
280
|
+
"id": 4,
|
|
281
|
+
"description": "Callback FSP Destination equal to request FSP Source",
|
|
282
|
+
"exec": [
|
|
283
|
+
"expect(callback.headers['fspiop-destination']).to.equal('{$request.headers['FSPIOP-Source']}')"
|
|
284
|
+
]
|
|
285
|
+
},
|
|
272
286
|
{
|
|
273
287
|
"id": 5,
|
|
274
288
|
"description": "Callback body should contain transferAmount",
|
|
@@ -334,8 +348,8 @@
|
|
|
334
348
|
"path": "/fxTransfers",
|
|
335
349
|
"method": "post",
|
|
336
350
|
"headers": {
|
|
337
|
-
"Accept": "application/vnd.interoperability.fxTransfers+json;version=
|
|
338
|
-
"Content-Type": "application/vnd.interoperability.fxTransfers+json;version=
|
|
351
|
+
"Accept": "application/vnd.interoperability.fxTransfers+json;version=1.0",
|
|
352
|
+
"Content-Type": "application/vnd.interoperability.fxTransfers+json;version=1.0",
|
|
339
353
|
"Date": "{$function.generic.curDate}",
|
|
340
354
|
"FSPIOP-Source": "{$inputs.fromFspId}"
|
|
341
355
|
},
|
|
@@ -378,6 +392,13 @@
|
|
|
378
392
|
"exec": [
|
|
379
393
|
"expect(callback.headers['Content-Length']).to.not.equal('0')"
|
|
380
394
|
]
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
"id": 4,
|
|
398
|
+
"description": "Callback FSP Destination equal to request FSP Source",
|
|
399
|
+
"exec": [
|
|
400
|
+
"expect(callback.headers['fspiop-destination']).to.equal('{$request.headers['FSPIOP-Source']}')"
|
|
401
|
+
]
|
|
381
402
|
}
|
|
382
403
|
]
|
|
383
404
|
},
|
|
@@ -395,8 +416,8 @@
|
|
|
395
416
|
"operationPath": "/transfers",
|
|
396
417
|
"method": "post",
|
|
397
418
|
"headers": {
|
|
398
|
-
"Accept": "
|
|
399
|
-
"Content-Type": "
|
|
419
|
+
"Accept": "application/vnd.interoperability.transfers+json;version=1.0",
|
|
420
|
+
"Content-Type": "application/vnd.interoperability.transfers+json;version=1.0",
|
|
400
421
|
"Date": "{$function.generic.curDate}",
|
|
401
422
|
"FSPIOP-Source": "{$inputs.fromFspId}"
|
|
402
423
|
},
|
|
@@ -435,6 +456,13 @@
|
|
|
435
456
|
"expect(callback.headers['Content-Length']).to.not.equal('0')"
|
|
436
457
|
]
|
|
437
458
|
},
|
|
459
|
+
{
|
|
460
|
+
"id": 4,
|
|
461
|
+
"description": "Callback FSP Destination equal to request FSP Source",
|
|
462
|
+
"exec": [
|
|
463
|
+
"expect(callback.headers['fspiop-destination']).to.equal('{$request.headers['FSPIOP-Source']}')"
|
|
464
|
+
]
|
|
465
|
+
},
|
|
438
466
|
{
|
|
439
467
|
"id": 5,
|
|
440
468
|
"description": "Callback transferState to be COMMITTED",
|
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dfsp-p2p-tests",
|
|
3
|
+
"inputValues": {
|
|
4
|
+
"fromIdType": "MSISDN",
|
|
5
|
+
"fromIdValue": "44123456789",
|
|
6
|
+
"fromFirstName": "Firstname-Test",
|
|
7
|
+
"fromLastName": "Lastname-Test",
|
|
8
|
+
"fromDOB": "1984-01-01",
|
|
9
|
+
"note": "Test Payment",
|
|
10
|
+
"currency": "USD",
|
|
11
|
+
"amount": "100",
|
|
12
|
+
"homeTransactionId": "123ABC",
|
|
13
|
+
"fromFspId": "testingtoolkitdfsp",
|
|
14
|
+
"accept": "application/vnd.interoperability.parties+json;version=1.0",
|
|
15
|
+
"contentType": "application/vnd.interoperability.parties+json;version=1.0",
|
|
16
|
+
"toIdValue": "9876543210",
|
|
17
|
+
"toIdType": "MSISDN",
|
|
18
|
+
"toFspId": "userdfsp",
|
|
19
|
+
"acceptQuotes": "application/vnd.interoperability.quotes+json;version=1.0",
|
|
20
|
+
"contentTypeQuotes": "application/vnd.interoperability.quotes+json;version=1.0",
|
|
21
|
+
"acceptTransfers": "application/vnd.interoperability.transfers+json;version=1.0",
|
|
22
|
+
"contentTransfers": "application/vnd.interoperability.transfers+json;version=1.0"
|
|
23
|
+
},
|
|
3
24
|
"test_cases": [
|
|
4
25
|
{
|
|
5
26
|
"id": 1,
|
|
@@ -100,8 +121,8 @@
|
|
|
100
121
|
"operationPath": "/quotes",
|
|
101
122
|
"method": "post",
|
|
102
123
|
"headers": {
|
|
103
|
-
"Accept": "
|
|
104
|
-
"Content-Type": "
|
|
124
|
+
"Accept": "application/vnd.interoperability.quotes+json;version=1.0",
|
|
125
|
+
"Content-Type": "application/vnd.interoperability.quotes+json;version=1.0",
|
|
105
126
|
"Date": "{$function.generic.curDate}",
|
|
106
127
|
"FSPIOP-Source": "{$inputs.fromFspId}",
|
|
107
128
|
"FSPIOP-Destination": "{$prev.1.callback.body.party.partyIdInfo.fspId}"
|
|
@@ -187,6 +208,13 @@
|
|
|
187
208
|
"expect(callback.body.transferAmount.currency).to.equal('{$request.body.amount.currency}')"
|
|
188
209
|
]
|
|
189
210
|
},
|
|
211
|
+
{
|
|
212
|
+
"id": 7,
|
|
213
|
+
"description": "Callback content-type to be quotes",
|
|
214
|
+
"exec": [
|
|
215
|
+
"expect(callback.headers['content-type']).to.equal('application/vnd.interoperability.quotes+json;version=1.0')"
|
|
216
|
+
]
|
|
217
|
+
},
|
|
190
218
|
{
|
|
191
219
|
"id": 8,
|
|
192
220
|
"description": "Request amountType to be SEND",
|
|
@@ -230,8 +258,8 @@
|
|
|
230
258
|
"operationPath": "/transfers",
|
|
231
259
|
"method": "post",
|
|
232
260
|
"headers": {
|
|
233
|
-
"Accept": "
|
|
234
|
-
"Content-Type": "
|
|
261
|
+
"Accept": "application/vnd.interoperability.transfers+json;version=1.0",
|
|
262
|
+
"Content-Type": "application/vnd.interoperability.transfers+json;version=1.0",
|
|
235
263
|
"Date": "{$function.generic.curDate}",
|
|
236
264
|
"FSPIOP-Source": "{$inputs.fromFspId}"
|
|
237
265
|
},
|
|
@@ -284,6 +312,13 @@
|
|
|
284
312
|
"expect(callback.body.transferState).to.equal('COMMITTED')"
|
|
285
313
|
]
|
|
286
314
|
},
|
|
315
|
+
{
|
|
316
|
+
"id": 6,
|
|
317
|
+
"description": "Callback content-type to be transfers",
|
|
318
|
+
"exec": [
|
|
319
|
+
"expect(callback.headers['content-type']).to.equal('application/vnd.interoperability.transfers+json;version=1.0')"
|
|
320
|
+
]
|
|
321
|
+
},
|
|
287
322
|
{
|
|
288
323
|
"id": 7,
|
|
289
324
|
"description": "Request transferId same as quote request transferId",
|
|
@@ -9,15 +9,15 @@
|
|
|
9
9
|
"HOST_SIMULATOR": "http://moja-simulator.local",
|
|
10
10
|
"HOST_TRANSACTION_REQUESTS_SERVICE": "http://transaction-request-service.local",
|
|
11
11
|
"HUB_OPERATOR_BEARER_TOKEN": "NOT_APPLICABLE",
|
|
12
|
-
"accept": "application/vnd.interoperability.parties+json;version=
|
|
13
|
-
"acceptQuotes": "application/vnd.interoperability.quotes+json;version=
|
|
14
|
-
"acceptTransfers": "application/vnd.interoperability.transfers+json;version=
|
|
12
|
+
"accept": "application/vnd.interoperability.parties+json;version=1.0",
|
|
13
|
+
"acceptQuotes": "application/vnd.interoperability.quotes+json;version=1.0",
|
|
14
|
+
"acceptTransfers": "application/vnd.interoperability.transfers+json;version=1.0",
|
|
15
15
|
"accountId": "6",
|
|
16
16
|
"amount": "100",
|
|
17
17
|
"condition": "HOr22-H3AfTDHrSkPjJtVPRdKouuMkDXTR4ejlQa8Ks",
|
|
18
|
-
"contentTransfers": "application/vnd.interoperability.transfers+json;version=
|
|
19
|
-
"contentType": "application/vnd.interoperability.parties+json;version=
|
|
20
|
-
"contentTypeQuotes": "application/vnd.interoperability.quotes+json;version=
|
|
18
|
+
"contentTransfers": "application/vnd.interoperability.transfers+json;version=1.0",
|
|
19
|
+
"contentType": "application/vnd.interoperability.parties+json;version=1.0",
|
|
20
|
+
"contentTypeQuotes": "application/vnd.interoperability.quotes+json;version=1.0",
|
|
21
21
|
"currency": "USD",
|
|
22
22
|
"fromDOB": "1984-01-01",
|
|
23
23
|
"fromFirstName": "Firstname-Test",
|
package/package.json
CHANGED
|
@@ -2665,12 +2665,12 @@
|
|
|
2665
2665
|
"counterPartyFsp": "fxp_123",
|
|
2666
2666
|
"amountType": "RECEIVE",
|
|
2667
2667
|
"sourceAmount": {
|
|
2668
|
-
"currency": "
|
|
2669
|
-
"amount": "
|
|
2668
|
+
"currency": "USD",
|
|
2669
|
+
"amount": "105.23"
|
|
2670
2670
|
},
|
|
2671
2671
|
"targetAmount": {
|
|
2672
|
-
"currency": "
|
|
2673
|
-
"amount": "
|
|
2672
|
+
"currency": "EUR",
|
|
2673
|
+
"amount": "100"
|
|
2674
2674
|
},
|
|
2675
2675
|
"expiration": "2016-05-24T08:38:08.699-04:00",
|
|
2676
2676
|
"charges": [
|
|
@@ -2690,7 +2690,7 @@
|
|
|
2690
2690
|
}
|
|
2691
2691
|
}
|
|
2692
2692
|
},
|
|
2693
|
-
"type": "
|
|
2693
|
+
"type": "MOCK_CALLBACK"
|
|
2694
2694
|
},
|
|
2695
2695
|
"type": "callback",
|
|
2696
2696
|
"version": 1
|
package/src/index.js
CHANGED
|
@@ -64,20 +64,4 @@ const init = async () => {
|
|
|
64
64
|
RequestLogger.logMessage('info', 'Toolkit Initialization completed.', { notification: false, additionalData: welcomeMessage })
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
// Graceful shutdown handler
|
|
68
|
-
const shutdown = async (signal) => {
|
|
69
|
-
RequestLogger.logMessage('info', `${signal} received. Starting graceful shutdown...`, { notification: false })
|
|
70
|
-
|
|
71
|
-
// Stop servers
|
|
72
|
-
await apiServer.stopServer()
|
|
73
|
-
await server.stop()
|
|
74
|
-
|
|
75
|
-
RequestLogger.logMessage('info', 'Graceful shutdown completed', { notification: false })
|
|
76
|
-
process.exit(0)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Register shutdown handlers
|
|
80
|
-
process.on('SIGTERM', () => shutdown('SIGTERM'))
|
|
81
|
-
process.on('SIGINT', () => shutdown('SIGINT'))
|
|
82
|
-
|
|
83
67
|
init()
|
|
@@ -40,13 +40,7 @@ const apiDefinitionsPath = 'spec_files/api_definitions/'
|
|
|
40
40
|
const validateDefinition = async (apiFilePath) => {
|
|
41
41
|
const newApi = new OpenApiBackend({
|
|
42
42
|
definition: await Utils.checkUrl(path.join(apiFilePath)),
|
|
43
|
-
customizeAjv: ajv =>
|
|
44
|
-
addFormats(ajv)
|
|
45
|
-
// Enable caching and optimization
|
|
46
|
-
ajv.opts.cache = new Map()
|
|
47
|
-
ajv.opts.serialize = false
|
|
48
|
-
return ajv
|
|
49
|
-
},
|
|
43
|
+
customizeAjv: ajv => addFormats(ajv),
|
|
50
44
|
strict: true,
|
|
51
45
|
quick: true
|
|
52
46
|
})
|
|
@@ -80,26 +80,4 @@ router.put('/user', [
|
|
|
80
80
|
}
|
|
81
81
|
})
|
|
82
82
|
|
|
83
|
-
// Get cache statistics
|
|
84
|
-
router.get('/cache', async (req, res, next) => {
|
|
85
|
-
try {
|
|
86
|
-
const perfOptimizer = require('../performanceOptimizer')
|
|
87
|
-
const stats = perfOptimizer.getCacheStats()
|
|
88
|
-
res.status(200).json(stats)
|
|
89
|
-
} catch (err) {
|
|
90
|
-
res.status(500).json({ error: err && err.message })
|
|
91
|
-
}
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
// Clear all caches
|
|
95
|
-
router.post('/cache/clear', async (req, res, next) => {
|
|
96
|
-
try {
|
|
97
|
-
const perfOptimizer = require('../performanceOptimizer')
|
|
98
|
-
perfOptimizer.clearAllCaches()
|
|
99
|
-
res.status(200).json({ status: 'OK', message: 'All caches cleared' })
|
|
100
|
-
} catch (err) {
|
|
101
|
-
res.status(500).json({ error: err && err.message })
|
|
102
|
-
}
|
|
103
|
-
})
|
|
104
|
-
|
|
105
83
|
module.exports = router
|
package/src/lib/api-server.js
CHANGED
|
@@ -97,41 +97,6 @@ const startServer = port => {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
const stopServer = port => {
|
|
100
|
-
// Clean up performance caches and context pools
|
|
101
|
-
try {
|
|
102
|
-
const perfOptimizer = require('./performanceOptimizer')
|
|
103
|
-
perfOptimizer.clearAllCaches()
|
|
104
|
-
|
|
105
|
-
// Clean up context pools
|
|
106
|
-
const postmanContext = require('./scripting-engines/postman-sandbox')
|
|
107
|
-
if (postmanContext.cleanupContextPool) {
|
|
108
|
-
postmanContext.cleanupContextPool()
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const javascriptContext = require('./scripting-engines/vm-javascript-sandbox')
|
|
112
|
-
if (javascriptContext.cleanupContextPool) {
|
|
113
|
-
javascriptContext.cleanupContextPool()
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Stop interval timers
|
|
117
|
-
const arrayStore = require('./arrayStore')
|
|
118
|
-
if (arrayStore.stopArrayStore) {
|
|
119
|
-
arrayStore.stopArrayStore()
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const objectStore = require('./objectStore')
|
|
123
|
-
if (objectStore.stopObjectStore) {
|
|
124
|
-
objectStore.stopObjectStore()
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const httpAgentStore = require('./httpAgentStore')
|
|
128
|
-
if (httpAgentStore.stop) {
|
|
129
|
-
httpAgentStore.stop()
|
|
130
|
-
}
|
|
131
|
-
} catch (err) {
|
|
132
|
-
customLogger.logMessage('warn', 'Error during cleanup', { additionalData: err.message })
|
|
133
|
-
}
|
|
134
|
-
|
|
135
100
|
http.close()
|
|
136
101
|
customLogger.logMessage('info', 'API Server stopped', { notification: false })
|
|
137
102
|
}
|
package/src/lib/arrayStore.js
CHANGED
|
@@ -88,27 +88,14 @@ const clearOldObjects = () => {
|
|
|
88
88
|
clear('callbacksHistory', interval)
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
let cleanupIntervalId = null
|
|
92
|
-
|
|
93
91
|
const initArrayStore = () => {
|
|
94
|
-
|
|
95
|
-
clearInterval(cleanupIntervalId)
|
|
96
|
-
}
|
|
97
|
-
cleanupIntervalId = setInterval(clearOldObjects, 1000)
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const stopArrayStore = () => {
|
|
101
|
-
if (cleanupIntervalId) {
|
|
102
|
-
clearInterval(cleanupIntervalId)
|
|
103
|
-
cleanupIntervalId = null
|
|
104
|
-
}
|
|
92
|
+
setInterval(clearOldObjects, 1000)
|
|
105
93
|
}
|
|
106
94
|
|
|
107
95
|
module.exports = {
|
|
108
96
|
get,
|
|
109
97
|
reset,
|
|
110
98
|
initArrayStore,
|
|
111
|
-
stopArrayStore,
|
|
112
99
|
push,
|
|
113
100
|
clear
|
|
114
101
|
}
|
|
@@ -122,34 +122,14 @@ const _clearAgents = (unUsedAgentsExpiryMs) => {
|
|
|
122
122
|
_clear(httpAgentStore, unUsedAgentsExpiryMs)
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
let cleanupIntervalId = null
|
|
126
|
-
|
|
127
125
|
const init = () => {
|
|
128
126
|
const timerInterval = (Config.getSystemConfig().HTTP_CLIENT && Config.getSystemConfig().HTTP_CLIENT.UNUSED_AGENTS_CHECK_TIMER_MS !== undefined) ? Config.getSystemConfig().HTTP_CLIENT.UNUSED_AGENTS_CHECK_TIMER_MS : (5 * 60 * 1000) // Check for the cleanup every 5min
|
|
129
127
|
const unUsedAgentsExpiryMs = (Config.getSystemConfig().HTTP_CLIENT && Config.getSystemConfig().HTTP_CLIENT.UNUSED_AGENTS_EXPIRY_MS !== undefined) ? Config.getSystemConfig().HTTP_CLIENT.UNUSED_AGENTS_EXPIRY_MS : (30 * 60 * 1000) // Clear http agents not being used for more this time
|
|
130
|
-
|
|
131
|
-
clearInterval(cleanupIntervalId)
|
|
132
|
-
}
|
|
133
|
-
cleanupIntervalId = setInterval(_clearAgents, timerInterval, unUsedAgentsExpiryMs)
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const stop = () => {
|
|
137
|
-
if (cleanupIntervalId) {
|
|
138
|
-
clearInterval(cleanupIntervalId)
|
|
139
|
-
cleanupIntervalId = null
|
|
140
|
-
}
|
|
141
|
-
// Destroy all agents
|
|
142
|
-
Object.values(httpsAgentStore).forEach(agentInfo => {
|
|
143
|
-
if (agentInfo.agent) agentInfo.agent.destroy()
|
|
144
|
-
})
|
|
145
|
-
Object.values(httpAgentStore).forEach(agentInfo => {
|
|
146
|
-
if (agentInfo.agent) agentInfo.agent.destroy()
|
|
147
|
-
})
|
|
128
|
+
setInterval(_clearAgents, timerInterval, unUsedAgentsExpiryMs)
|
|
148
129
|
}
|
|
149
130
|
|
|
150
131
|
module.exports = {
|
|
151
132
|
getHttpAgent,
|
|
152
133
|
getHttpsAgent,
|
|
153
|
-
init
|
|
154
|
-
stop
|
|
134
|
+
init
|
|
155
135
|
}
|
|
@@ -142,23 +142,23 @@ const handleTransferIlp = (context, response) => {
|
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
customLogger.logMessage('debug', 'Generated callback body', { additionalData: { context, response }
|
|
145
|
+
customLogger.logMessage('debug', 'Generated callback body', { additionalData: { context, response } })
|
|
146
146
|
if (context.request.method === 'get' && response.method === 'put' && pathMatch.test(response.path)) {
|
|
147
147
|
const transferId = response.path.match(pathMatch)[1]
|
|
148
148
|
// Use objectStore to get the stored transfer
|
|
149
149
|
const storedTransfer = objectStore.get('storedTransfers', transferId)
|
|
150
|
-
customLogger.logMessage('debug', 'Stored transfer fetched for fulfilment', { additionalData: { transferId }
|
|
150
|
+
customLogger.logMessage('debug', 'Stored transfer fetched for fulfilment', { additionalData: { transferId } })
|
|
151
151
|
if (storedTransfer) {
|
|
152
152
|
if (storedTransfer.data?.request?.ilpPacket) {
|
|
153
|
-
customLogger.logMessage('debug', 'Stored transfer has ilpPacket. Generating fulfilment.', { additionalData: { transferId, ilpPacket: storedTransfer.data.request.ilpPacket }
|
|
153
|
+
customLogger.logMessage('debug', 'Stored transfer has ilpPacket. Generating fulfilment.', { additionalData: { transferId, ilpPacket: storedTransfer.data.request.ilpPacket } })
|
|
154
154
|
const generatedFulfilment = ilpObj.calculateFulfil(storedTransfer.data.request.ilpPacket).replace('"', '')
|
|
155
155
|
response.body.fulfilment = generatedFulfilment
|
|
156
156
|
} else if (storedTransfer.data?.request?.CdtTrfTxInf?.VrfctnOfTerms?.IlpV4PrepPacket) {
|
|
157
|
-
customLogger.logMessage('debug', 'Stored transfer has IlpV4PrepPacket. Generating fulfilment.', { additionalData: { transferId }
|
|
157
|
+
customLogger.logMessage('debug', 'Stored transfer has IlpV4PrepPacket. Generating fulfilment.', { additionalData: { transferId } })
|
|
158
158
|
const generatedFulfilment = ilpV4Obj.calculateFulfil(storedTransfer.data.request.CdtTrfTxInf.VrfctnOfTerms.IlpV4PrepPacket).replace('"', '')
|
|
159
159
|
response.body.TxInfAndSts.ExctnConf = generatedFulfilment
|
|
160
160
|
} else {
|
|
161
|
-
customLogger.logMessage('warn', 'No ILP packet or IlpV4PrepPacket found in stored transfer request', { additionalData: { transferId }
|
|
161
|
+
customLogger.logMessage('warn', 'No ILP packet or IlpV4PrepPacket found in stored transfer request', { additionalData: { transferId } })
|
|
162
162
|
}
|
|
163
163
|
// Remove the stored transfer from objectStore regardless of success or failure
|
|
164
164
|
objectStore.deleteObject('storedTransfers', transferId)
|
|
@@ -67,13 +67,7 @@ module.exports.initilizeMockHandler = async () => {
|
|
|
67
67
|
apis = await Promise.all(apiDefinitions.map(async item => {
|
|
68
68
|
const tempObj = new OpenApiBackend({
|
|
69
69
|
definition: await utils.checkUrl(path.join(item.specFile)),
|
|
70
|
-
customizeAjv: ajv =>
|
|
71
|
-
addFormats(ajv)
|
|
72
|
-
// Enable caching and optimization to avoid repeated schema compilation
|
|
73
|
-
ajv.opts.cache = new Map()
|
|
74
|
-
ajv.opts.serialize = false
|
|
75
|
-
return ajv
|
|
76
|
-
},
|
|
70
|
+
customizeAjv: ajv => addFormats(ajv),
|
|
77
71
|
strict: true,
|
|
78
72
|
quick: true,
|
|
79
73
|
handlers: {
|
|
@@ -32,13 +32,6 @@ const SocketServer = require('./socket-server')
|
|
|
32
32
|
const broadcast = (log, sessionID = null, type) => {
|
|
33
33
|
const io = SocketServer.getIO()
|
|
34
34
|
if (io) {
|
|
35
|
-
// Performance optimization: Only broadcast if clients are connected
|
|
36
|
-
// This prevents expensive encoding operations when no one is listening
|
|
37
|
-
const hasClients = io.engine?.clientsCount > 0
|
|
38
|
-
if (!hasClients) {
|
|
39
|
-
return
|
|
40
|
-
}
|
|
41
|
-
|
|
42
35
|
io.emit(type, log)
|
|
43
36
|
if (sessionID) {
|
|
44
37
|
io.emit(`${type}/` + sessionID, log)
|
package/src/lib/objectStore.js
CHANGED
|
@@ -121,30 +121,17 @@ const clearOldObjects = () => {
|
|
|
121
121
|
clear('storedTransfers', interval)
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
let cleanupIntervalId = null
|
|
125
|
-
|
|
126
124
|
const initObjectStore = (initConfig = null) => {
|
|
127
125
|
if (initConfig) {
|
|
128
126
|
_.merge(storedObject.data, initConfig)
|
|
129
127
|
}
|
|
130
|
-
|
|
131
|
-
clearInterval(cleanupIntervalId)
|
|
132
|
-
}
|
|
133
|
-
cleanupIntervalId = setInterval(clearOldObjects, 1000)
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const stopObjectStore = () => {
|
|
137
|
-
if (cleanupIntervalId) {
|
|
138
|
-
clearInterval(cleanupIntervalId)
|
|
139
|
-
cleanupIntervalId = null
|
|
140
|
-
}
|
|
128
|
+
setInterval(clearOldObjects, 1000)
|
|
141
129
|
}
|
|
142
130
|
|
|
143
131
|
module.exports = {
|
|
144
132
|
set,
|
|
145
133
|
get,
|
|
146
134
|
initObjectStore,
|
|
147
|
-
stopObjectStore,
|
|
148
135
|
init,
|
|
149
136
|
push,
|
|
150
137
|
clear,
|
package/src/lib/requestLogger.js
CHANGED
|
@@ -121,8 +121,7 @@ const logResponse = async (request, user) => {
|
|
|
121
121
|
const logMessage = (verbosity, message, externalData = {}) => {
|
|
122
122
|
const data = {
|
|
123
123
|
additionalData: externalData.additionalData,
|
|
124
|
-
|
|
125
|
-
notification: typeof externalData.notification !== 'undefined' ? externalData.notification : (verbosity !== 'debug'),
|
|
124
|
+
notification: typeof externalData.notification !== 'undefined' ? externalData.notification : true,
|
|
126
125
|
messageType: externalData.messageType || 'generic',
|
|
127
126
|
request: externalData.request || null,
|
|
128
127
|
user: externalData.user
|
|
@@ -33,15 +33,10 @@ const axios = require('axios').default
|
|
|
33
33
|
const customLogger = require('../requestLogger')
|
|
34
34
|
const UniqueIdGenerator = require('../../lib/uniqueIdGenerator')
|
|
35
35
|
|
|
36
|
-
// Context pool for reusing sandbox contexts
|
|
37
|
-
const CONTEXT_POOL_SIZE = 5
|
|
38
|
-
const contextPool = []
|
|
39
|
-
let contextPoolInitialized = false
|
|
40
|
-
|
|
41
36
|
// const createContextAsync = util.promisify(Sandbox.createContext)
|
|
42
|
-
const createContextAsync = (
|
|
37
|
+
const createContextAsync = () => {
|
|
43
38
|
return new Promise((resolve, reject) => {
|
|
44
|
-
Sandbox.createContext(
|
|
39
|
+
Sandbox.createContext(function (err, ctx) {
|
|
45
40
|
if (err) {
|
|
46
41
|
reject(err)
|
|
47
42
|
return console.error(err)
|
|
@@ -51,72 +46,13 @@ const createContextAsync = (options) => {
|
|
|
51
46
|
})
|
|
52
47
|
}
|
|
53
48
|
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
if (contextPoolInitialized) return
|
|
57
|
-
contextPoolInitialized = true
|
|
58
|
-
|
|
59
|
-
customLogger.logMessage('info', `Initializing sandbox context pool with ${CONTEXT_POOL_SIZE} contexts`, { notification: false })
|
|
60
|
-
const promises = []
|
|
61
|
-
for (let i = 0; i < CONTEXT_POOL_SIZE; i++) {
|
|
62
|
-
promises.push(createContextAsync())
|
|
63
|
-
}
|
|
64
|
-
const contexts = await Promise.all(promises)
|
|
65
|
-
|
|
66
|
-
contexts.forEach(ctx => {
|
|
67
|
-
ctx.executeAsync = util.promisify(ctx.execute)
|
|
68
|
-
ctx.on('error', function (cursor, err) {
|
|
69
|
-
// log the error in postman sandbox
|
|
70
|
-
console.log(cursor, err)
|
|
71
|
-
})
|
|
72
|
-
contextPool.push({ ctx, inUse: false })
|
|
73
|
-
})
|
|
74
|
-
customLogger.logMessage('info', 'Sandbox context pool initialized', { notification: false })
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Get context from pool or create new one
|
|
78
|
-
const acquireContext = async () => {
|
|
79
|
-
await initializeContextPool()
|
|
80
|
-
|
|
81
|
-
// Try to find an available context in the pool
|
|
82
|
-
const available = contextPool.find(item => !item.inUse)
|
|
83
|
-
if (available) {
|
|
84
|
-
available.inUse = true
|
|
85
|
-
return available.ctx
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// If pool is exhausted, create a new context (temporary)
|
|
89
|
-
customLogger.logMessage('debug', 'Context pool exhausted, creating temporary context', { notification: false })
|
|
90
|
-
const ctx = await createContextAsync()
|
|
49
|
+
const generateContextObj = async (environment = {}) => {
|
|
50
|
+
const ctx = await createContextAsync({ timeout: 30000 })
|
|
91
51
|
ctx.executeAsync = util.promisify(ctx.execute)
|
|
92
52
|
ctx.on('error', function (cursor, err) {
|
|
53
|
+
// log the error in postman sandbox
|
|
93
54
|
console.log(cursor, err)
|
|
94
55
|
})
|
|
95
|
-
return ctx
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Release context back to pool
|
|
99
|
-
const releaseContext = (ctx) => {
|
|
100
|
-
const poolItem = contextPool.find(item => item.ctx === ctx)
|
|
101
|
-
if (poolItem) {
|
|
102
|
-
// Context is being returned to pool
|
|
103
|
-
// executeAsync already cleans up execution-specific listeners in its finally block
|
|
104
|
-
// so we don't need to do additional cleanup here
|
|
105
|
-
poolItem.inUse = false
|
|
106
|
-
} else {
|
|
107
|
-
// This was a temporary context (not in pool), dispose it
|
|
108
|
-
try {
|
|
109
|
-
if (ctx && typeof ctx.dispose === 'function') {
|
|
110
|
-
ctx.dispose()
|
|
111
|
-
}
|
|
112
|
-
} catch (err) {
|
|
113
|
-
customLogger.logMessage('warn', 'Failed to dispose temporary context', { additionalData: err.message })
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const generateContextObj = async (environment = {}) => {
|
|
119
|
-
const ctx = await acquireContext()
|
|
120
56
|
const transformerObj = {
|
|
121
57
|
transformer: null,
|
|
122
58
|
transformerName: null,
|
|
@@ -125,37 +61,11 @@ const generateContextObj = async (environment = {}) => {
|
|
|
125
61
|
const contextObj = {
|
|
126
62
|
ctx,
|
|
127
63
|
environment,
|
|
128
|
-
transformerObj
|
|
129
|
-
_release: () => releaseContext(ctx),
|
|
130
|
-
_isPooled: contextPool.some(item => item.ctx === ctx)
|
|
64
|
+
transformerObj
|
|
131
65
|
}
|
|
132
66
|
return contextObj
|
|
133
67
|
}
|
|
134
68
|
|
|
135
|
-
// Cleanup function for graceful shutdown
|
|
136
|
-
const cleanupContextPool = () => {
|
|
137
|
-
customLogger.logMessage('info', 'Cleaning up sandbox context pool', { notification: false })
|
|
138
|
-
contextPool.forEach(({ ctx }) => {
|
|
139
|
-
try {
|
|
140
|
-
ctx.removeAllListeners()
|
|
141
|
-
ctx.dispose()
|
|
142
|
-
} catch (err) {
|
|
143
|
-
customLogger.logMessage('warn', 'Failed to dispose context', { additionalData: err.message })
|
|
144
|
-
}
|
|
145
|
-
})
|
|
146
|
-
contextPool.length = 0
|
|
147
|
-
contextPoolInitialized = false
|
|
148
|
-
customLogger.logMessage('info', 'Context pool cleanup complete', { notification: false })
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Register cleanup with performance optimizer
|
|
152
|
-
try {
|
|
153
|
-
const perfOptimizer = require('../performanceOptimizer')
|
|
154
|
-
perfOptimizer.registerCache(cleanupContextPool, 'Sandbox Context Pool')
|
|
155
|
-
} catch (err) {
|
|
156
|
-
// Performance optimizer not available
|
|
157
|
-
}
|
|
158
|
-
|
|
159
69
|
const executeAsync = async (script, data, contextObj) => {
|
|
160
70
|
let consoleLog = []
|
|
161
71
|
const uniqueId = UniqueIdGenerator.generateUniqueId()
|
|
@@ -166,12 +76,9 @@ const executeAsync = async (script, data, contextObj) => {
|
|
|
166
76
|
data.context.ctx = contextObj.ctx
|
|
167
77
|
data.context.environment = Object.entries(contextObj.environment || {}).map((item) => { return { type: 'any', key: item[0], value: item[1] } })
|
|
168
78
|
|
|
169
|
-
|
|
170
|
-
const consoleHandler = function () {
|
|
79
|
+
contextObj.ctx.on('console', function () {
|
|
171
80
|
consoleLog.push(Array.from(arguments))
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
contextObj.ctx.on('console', consoleHandler)
|
|
81
|
+
})
|
|
175
82
|
|
|
176
83
|
contextObj.ctx.on(`execution.request.${data.id}`, async (cursor, id, requestId, req) => {
|
|
177
84
|
const host = `${req.url.protocol}://${req.url.host.join('.')}${req.url.port ? ':' + req.url.port : ''}/`
|
|
@@ -220,16 +127,6 @@ const executeAsync = async (script, data, contextObj) => {
|
|
|
220
127
|
contextObj.environment = resp.environment.values.reduce((envObj, item) => { envObj[item.key] = item.value; return envObj }, {})
|
|
221
128
|
} catch (err) {
|
|
222
129
|
// consoleLog.push([{execution: 0}, 'executionError', err.message])
|
|
223
|
-
} finally {
|
|
224
|
-
// Clean up event listeners to prevent memory leaks
|
|
225
|
-
try {
|
|
226
|
-
contextObj.ctx.removeListener('console', consoleHandler)
|
|
227
|
-
contextObj.ctx.removeAllListeners(`execution.request.${data.id}`)
|
|
228
|
-
contextObj.ctx.removeAllListeners(`execution.response.${data.id}`)
|
|
229
|
-
contextObj.ctx.removeAllListeners(`execution.error.${data.id}`)
|
|
230
|
-
} catch (cleanupErr) {
|
|
231
|
-
// Ignore cleanup errors
|
|
232
|
-
}
|
|
233
130
|
}
|
|
234
131
|
const result = {
|
|
235
132
|
consoleLog,
|
|
@@ -241,7 +138,5 @@ const executeAsync = async (script, data, contextObj) => {
|
|
|
241
138
|
|
|
242
139
|
module.exports = {
|
|
243
140
|
generateContextObj,
|
|
244
|
-
executeAsync
|
|
245
|
-
releaseContext,
|
|
246
|
-
cleanupContextPool
|
|
141
|
+
executeAsync
|
|
247
142
|
}
|
|
@@ -40,109 +40,6 @@ const UniqueIdGenerator = require('../../lib/uniqueIdGenerator')
|
|
|
40
40
|
const customLogger = require('../requestLogger')
|
|
41
41
|
const { getHeader, urlToPath } = require('../utils')
|
|
42
42
|
|
|
43
|
-
const CONTEXT_POOL_SIZE = 5
|
|
44
|
-
const contextPool = []
|
|
45
|
-
|
|
46
|
-
// Initialize the context pool at startup
|
|
47
|
-
const initializeContextPool = async () => {
|
|
48
|
-
if (contextPool.length === 0) {
|
|
49
|
-
for (let i = 0; i < CONTEXT_POOL_SIZE; i++) {
|
|
50
|
-
const ctx = await generateContextObj()
|
|
51
|
-
ctx._inUse = false
|
|
52
|
-
contextPool.push(ctx)
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Acquire a context from the pool
|
|
58
|
-
const acquireContext = async (environmentObj = {}) => {
|
|
59
|
-
// Initialize pool on first use
|
|
60
|
-
if (contextPool.length === 0) {
|
|
61
|
-
await initializeContextPool()
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Find available context
|
|
65
|
-
const availableContext = contextPool.find(ctx => !ctx._inUse)
|
|
66
|
-
|
|
67
|
-
if (availableContext) {
|
|
68
|
-
availableContext._inUse = true
|
|
69
|
-
// Update environment for this execution
|
|
70
|
-
availableContext.environment = { ...environmentObj }
|
|
71
|
-
return availableContext
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// If no available context, create temporary one
|
|
75
|
-
const tempContext = await generateContextObj(environmentObj)
|
|
76
|
-
tempContext._temporary = true
|
|
77
|
-
tempContext._inUse = true
|
|
78
|
-
return tempContext
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Release a context back to the pool
|
|
82
|
-
const releaseContext = (contextObj) => {
|
|
83
|
-
if (!contextObj) return
|
|
84
|
-
|
|
85
|
-
if (contextObj._temporary) {
|
|
86
|
-
// Dispose temporary context
|
|
87
|
-
try {
|
|
88
|
-
if (contextObj.websocket && contextObj.websocket.destroy) {
|
|
89
|
-
contextObj.websocket.destroy()
|
|
90
|
-
}
|
|
91
|
-
if (contextObj.inboundEvent && contextObj.inboundEvent.destroy) {
|
|
92
|
-
// Destroy all event listeners
|
|
93
|
-
Object.keys(contextObj.inboundEvent.eventListeners || {}).forEach(clientName => {
|
|
94
|
-
contextObj.inboundEvent.destroy(clientName)
|
|
95
|
-
})
|
|
96
|
-
// Remove all listeners from emitter
|
|
97
|
-
if (contextObj.inboundEvent.emitter) {
|
|
98
|
-
contextObj.inboundEvent.emitter.removeAllListeners('newInbound')
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
} catch (err) {
|
|
102
|
-
customLogger.logMessage('warn', 'Failed to cleanup temporary context', { additionalData: err.message })
|
|
103
|
-
}
|
|
104
|
-
return
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Clean up and reset context state
|
|
108
|
-
clearConsole(contextObj.consoleOutObj)
|
|
109
|
-
contextObj.requestVariables = {}
|
|
110
|
-
contextObj.environment = {}
|
|
111
|
-
|
|
112
|
-
// Note: WebSocketClientManager and InboundEventListener manage their own
|
|
113
|
-
// event listeners internally, no need to clean them up here
|
|
114
|
-
|
|
115
|
-
// Reset transformer
|
|
116
|
-
contextObj.transformerObj.transformer = null
|
|
117
|
-
contextObj.transformerObj.transformerName = null
|
|
118
|
-
contextObj.transformerObj.options = {}
|
|
119
|
-
|
|
120
|
-
// Mark as available
|
|
121
|
-
contextObj._inUse = false
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Clean up all contexts in the pool
|
|
125
|
-
const cleanupContextPool = () => {
|
|
126
|
-
contextPool.forEach(ctx => {
|
|
127
|
-
try {
|
|
128
|
-
if (ctx.websocket && ctx.websocket.destroy) ctx.websocket.destroy()
|
|
129
|
-
if (ctx.inboundEvent && ctx.inboundEvent.destroy) {
|
|
130
|
-
// Destroy all event listeners
|
|
131
|
-
Object.keys(ctx.inboundEvent.eventListeners || {}).forEach(clientName => {
|
|
132
|
-
ctx.inboundEvent.destroy(clientName)
|
|
133
|
-
})
|
|
134
|
-
// Remove all listeners from emitter
|
|
135
|
-
if (ctx.inboundEvent.emitter) {
|
|
136
|
-
ctx.inboundEvent.emitter.removeAllListeners('newInbound')
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
} catch (err) {
|
|
140
|
-
customLogger.logMessage('warn', 'Failed to cleanup context', { additionalData: err.message })
|
|
141
|
-
}
|
|
142
|
-
})
|
|
143
|
-
contextPool.length = 0
|
|
144
|
-
}
|
|
145
|
-
|
|
146
43
|
const registerAxiosRequestInterceptor = (userConfig, axios, transformerObj) => {
|
|
147
44
|
// istanbul ignore next
|
|
148
45
|
axios.interceptors.request.use(async config => {
|
|
@@ -246,7 +143,6 @@ const customWrapperFn = (requestVariables, consoleFn) => {
|
|
|
246
143
|
consoleFn.log(`Setting transformer '${transformerName}' if exists...`)
|
|
247
144
|
if (!transformerName) {
|
|
248
145
|
requestVariables.TRANSFORM = undefined
|
|
249
|
-
return
|
|
250
146
|
}
|
|
251
147
|
requestVariables.TRANSFORM = {
|
|
252
148
|
transformerName,
|
|
@@ -368,14 +264,7 @@ const executeAsync = async (script, data, contextObj) => {
|
|
|
368
264
|
consoleLog.push([{ execution: 0 }, 'log', ...contextObj.consoleOutObj.stdOut[i]])
|
|
369
265
|
}
|
|
370
266
|
consoleLog.push([{ execution: 0 }, 'executionError', err.toString()])
|
|
371
|
-
} finally {
|
|
372
|
-
// Clean up context-specific properties after execution
|
|
373
|
-
delete contextObj.request
|
|
374
|
-
delete contextObj.response
|
|
375
|
-
delete contextObj.callback
|
|
376
|
-
delete contextObj.collectionVariables
|
|
377
267
|
}
|
|
378
|
-
|
|
379
268
|
const result = {
|
|
380
269
|
consoleLog,
|
|
381
270
|
environment: { ...contextObj.environment }
|
|
@@ -401,8 +290,5 @@ async function _runScript (code, context = {}, options = {}) {
|
|
|
401
290
|
module.exports = {
|
|
402
291
|
registerAxiosRequestInterceptor,
|
|
403
292
|
generateContextObj,
|
|
404
|
-
|
|
405
|
-
releaseContext,
|
|
406
|
-
executeAsync,
|
|
407
|
-
cleanupContextPool
|
|
293
|
+
executeAsync
|
|
408
294
|
}
|
|
@@ -330,15 +330,7 @@ const processTestCase = async (
|
|
|
330
330
|
if (convertedRequest.scriptingEngine && convertedRequest.scriptingEngine === 'javascript') {
|
|
331
331
|
context = javascriptContext
|
|
332
332
|
}
|
|
333
|
-
|
|
334
|
-
if (context.acquireContext) {
|
|
335
|
-
contextObj = await context.acquireContext(variableData.environment)
|
|
336
|
-
// Add release method
|
|
337
|
-
contextObj._release = () => context.releaseContext(contextObj)
|
|
338
|
-
} else {
|
|
339
|
-
// Fallback for engines without pooling support
|
|
340
|
-
contextObj = await context.generateContextObj(variableData.environment)
|
|
341
|
-
}
|
|
333
|
+
contextObj = await context.generateContextObj(variableData.environment)
|
|
342
334
|
}
|
|
343
335
|
|
|
344
336
|
// Get transformer if specified
|
|
@@ -436,9 +428,9 @@ const processTestCase = async (
|
|
|
436
428
|
convertedRequest.retry === retries
|
|
437
429
|
)
|
|
438
430
|
} finally {
|
|
439
|
-
if (contextObj
|
|
440
|
-
|
|
441
|
-
contextObj.
|
|
431
|
+
if (contextObj) {
|
|
432
|
+
contextObj.ctx.dispose()
|
|
433
|
+
contextObj.ctx = null
|
|
442
434
|
}
|
|
443
435
|
if (request.appended?.assertionResults?.isFailed) {
|
|
444
436
|
if (convertedRequest.retry < retries) {
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
/*****
|
|
2
|
-
License
|
|
3
|
-
--------------
|
|
4
|
-
Copyright © 2020-2025 Mojaloop Foundation
|
|
5
|
-
The Mojaloop files are made available by the Mojaloop Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at
|
|
6
|
-
|
|
7
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
|
|
9
|
-
Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
|
10
|
-
|
|
11
|
-
Contributors
|
|
12
|
-
--------------
|
|
13
|
-
This is the official list of the Mojaloop project contributors for this file.
|
|
14
|
-
Names of the original copyright holders (individuals or organizations)
|
|
15
|
-
should be listed with a '*' in the first column. People who have
|
|
16
|
-
contributed from an organization can be listed under the organization
|
|
17
|
-
that actually holds the copyright for their contributions (see the
|
|
18
|
-
Mojaloop Foundation for an example). Those individuals should have
|
|
19
|
-
their names indented and be marked with a '-'. Email address can be added
|
|
20
|
-
optionally within square brackets <email>.
|
|
21
|
-
|
|
22
|
-
* Mojaloop Foundation
|
|
23
|
-
- Name Surname <name.surname@mojaloop.io>
|
|
24
|
-
--------------
|
|
25
|
-
******/
|
|
26
|
-
|
|
27
|
-
'use strict'
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Performance Optimizer Module
|
|
31
|
-
*
|
|
32
|
-
* This module provides utilities for managing caches and optimizing
|
|
33
|
-
* performance across the application.
|
|
34
|
-
*
|
|
35
|
-
* Key Optimizations:
|
|
36
|
-
* 1. Postman Sandbox Context Pooling - Reuse postman-sandbox contexts
|
|
37
|
-
* 2. JavaScript Sandbox Context Pooling - Reuse vm-javascript-sandbox contexts
|
|
38
|
-
* 3. Schema Compilation Caching - Cache AJV compiled schemas
|
|
39
|
-
*/
|
|
40
|
-
|
|
41
|
-
const customLogger = require('./requestLogger')
|
|
42
|
-
|
|
43
|
-
// Registry of cache management functions
|
|
44
|
-
const cacheManagers = []
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Register a cache management function
|
|
48
|
-
* @param {Function} clearFn - Function to clear/manage the cache
|
|
49
|
-
* @param {String} name - Name of the cache for logging
|
|
50
|
-
*/
|
|
51
|
-
const registerCache = (clearFn, name) => {
|
|
52
|
-
cacheManagers.push({ clearFn, name })
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Clear all registered caches
|
|
57
|
-
* Use this when you need to reset all caches (e.g., configuration reload)
|
|
58
|
-
*/
|
|
59
|
-
const clearAllCaches = () => {
|
|
60
|
-
customLogger.logMessage('info', 'Clearing all performance caches', { notification: false })
|
|
61
|
-
cacheManagers.forEach(({ clearFn, name }) => {
|
|
62
|
-
try {
|
|
63
|
-
clearFn()
|
|
64
|
-
customLogger.logMessage('debug', `Cleared cache: ${name}`, { notification: false })
|
|
65
|
-
} catch (err) {
|
|
66
|
-
customLogger.logMessage('error', `Failed to clear cache: ${name}`, { additionalData: err.message, notification: false })
|
|
67
|
-
}
|
|
68
|
-
})
|
|
69
|
-
customLogger.logMessage('info', `Cleared ${cacheManagers.length} caches`, { notification: false })
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Get cache statistics
|
|
74
|
-
* @returns {Object} Statistics about registered caches
|
|
75
|
-
*/
|
|
76
|
-
const getCacheStats = () => {
|
|
77
|
-
return {
|
|
78
|
-
totalCaches: cacheManagers.length,
|
|
79
|
-
caches: cacheManagers.map(({ name }) => name)
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Setup periodic cache cleanup
|
|
85
|
-
* @param {Number} intervalMs - Interval in milliseconds (default: 1 hour)
|
|
86
|
-
*/
|
|
87
|
-
const setupPeriodicCleanup = (intervalMs = 3600000) => {
|
|
88
|
-
setInterval(() => {
|
|
89
|
-
customLogger.logMessage('debug', 'Running periodic cache cleanup', { notification: false })
|
|
90
|
-
// Trigger garbage collection hint if available
|
|
91
|
-
if (global.gc) {
|
|
92
|
-
global.gc()
|
|
93
|
-
}
|
|
94
|
-
}, intervalMs)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
module.exports = {
|
|
98
|
-
registerCache,
|
|
99
|
-
clearAllCaches,
|
|
100
|
-
getCacheStats,
|
|
101
|
-
setupPeriodicCleanup
|
|
102
|
-
}
|