postman-runtime 7.45.0 → 7.46.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/CHANGELOG.yaml +14 -0
- package/README.md +37 -1
- package/dist/index.js +1 -1
- package/lib/authorizer/digest.js +26 -3
- package/lib/requester/requester.js +1 -1
- package/lib/runner/extensions/event.command.js +50 -13
- package/lib/runner/extensions/waterfall.command.js +5 -2
- package/lib/runner/index.js +23 -0
- package/lib/runner/nested-request.js +197 -0
- package/lib/runner/run.js +8 -2
- package/package.json +4 -4
package/lib/authorizer/digest.js
CHANGED
|
@@ -136,13 +136,33 @@ _extractField = function (string, regexp) {
|
|
|
136
136
|
* there can be more than one header with the same key. So need to loop over and check each one.
|
|
137
137
|
*
|
|
138
138
|
* @param {VariableList} headers -
|
|
139
|
+
* @param {String} selectedAlgorithm - The user opted algorithm (MD5, SHA-256 etc)
|
|
139
140
|
* @private
|
|
140
141
|
*/
|
|
141
|
-
function _getDigestAuthHeader (headers) {
|
|
142
|
-
|
|
142
|
+
function _getDigestAuthHeader (headers, selectedAlgorithm) {
|
|
143
|
+
const digestAuthHeaders = headers.filter(function (property) {
|
|
143
144
|
return (property.key.toLowerCase() === WWW_AUTHENTICATE) &&
|
|
144
145
|
(_.startsWith(String(property.value).toLowerCase(), DIGEST_PREFIX.toLowerCase()));
|
|
145
146
|
});
|
|
147
|
+
|
|
148
|
+
let headerWithMatchingOptedAlgorithm;
|
|
149
|
+
|
|
150
|
+
if (selectedAlgorithm) {
|
|
151
|
+
const targetAlgorithm = selectedAlgorithm.toLowerCase();
|
|
152
|
+
|
|
153
|
+
headerWithMatchingOptedAlgorithm = digestAuthHeaders.find(function (header) {
|
|
154
|
+
const headerValue = String(header.value).toLowerCase();
|
|
155
|
+
|
|
156
|
+
if (!headerValue.includes('algorithm=')) {
|
|
157
|
+
// This is an MD5 header. Ref: https://datatracker.ietf.org/doc/html/rfc7616
|
|
158
|
+
return targetAlgorithm === 'md5';
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return headerValue.includes(`algorithm=${targetAlgorithm}`);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return headerWithMatchingOptedAlgorithm || digestAuthHeaders[0];
|
|
146
166
|
}
|
|
147
167
|
|
|
148
168
|
/**
|
|
@@ -314,6 +334,7 @@ module.exports = {
|
|
|
314
334
|
|
|
315
335
|
var code,
|
|
316
336
|
nonceCount,
|
|
337
|
+
algorithm,
|
|
317
338
|
realm,
|
|
318
339
|
nonce,
|
|
319
340
|
qop,
|
|
@@ -323,7 +344,9 @@ module.exports = {
|
|
|
323
344
|
|
|
324
345
|
code = response.code;
|
|
325
346
|
nonceCount = auth.get('nonceCount');
|
|
326
|
-
|
|
347
|
+
algorithm = auth.get('algorithm');
|
|
348
|
+
|
|
349
|
+
authHeader = _getDigestAuthHeader(response.headers, algorithm);
|
|
327
350
|
|
|
328
351
|
// If code is forbidden or unauthorized, and an auth header exists,
|
|
329
352
|
// we can extract the realm & the nonce, and replay the request.
|
|
@@ -471,7 +471,7 @@ class Requester extends EventEmitter {
|
|
|
471
471
|
header: responseHeaders,
|
|
472
472
|
stream: resBody,
|
|
473
473
|
responseTime: responseTime,
|
|
474
|
-
downloadedBytes: history.execution.data
|
|
474
|
+
downloadedBytes: history.execution.data.at(-1).response.downloadedBytes
|
|
475
475
|
});
|
|
476
476
|
|
|
477
477
|
onComplete(RESPONSE_END, response, history);
|
|
@@ -15,7 +15,9 @@ var _ = require('lodash'),
|
|
|
15
15
|
'request', 'response'],
|
|
16
16
|
|
|
17
17
|
EXECUTION_REQUEST_EVENT_BASE = 'execution.request.',
|
|
18
|
+
EXECUTION_RUN_REQUEST_EVENT_BASE = 'execution.run_collection_request.',
|
|
18
19
|
EXECUTION_RESPONSE_EVENT_BASE = 'execution.response.',
|
|
20
|
+
EXECUTION_RUN_REQUEST_RESPONSE_EVENT_BASE = 'execution.run_collection_request_response.',
|
|
19
21
|
EXECUTION_ASSERTION_EVENT_BASE = 'execution.assertion.',
|
|
20
22
|
EXECUTION_ERROR_EVENT_BASE = 'execution.error.',
|
|
21
23
|
EXECUTION_COOKIES_EVENT_BASE = 'execution.cookies.',
|
|
@@ -32,6 +34,8 @@ var _ = require('lodash'),
|
|
|
32
34
|
REQUEST_BODY_MODE_FILE = 'file',
|
|
33
35
|
REQUEST_BODY_MODE_FORMDATA = 'formdata',
|
|
34
36
|
|
|
37
|
+
runNestedRequest = require('../nested-request'),
|
|
38
|
+
|
|
35
39
|
getCookieDomain, // fn
|
|
36
40
|
postProcessContext, // fn
|
|
37
41
|
sanitizeFiles; // fn
|
|
@@ -287,17 +291,26 @@ module.exports = {
|
|
|
287
291
|
assertionFailed = [],
|
|
288
292
|
asyncScriptError,
|
|
289
293
|
|
|
294
|
+
isNestedRequest = this.state.nestedRequest !== undefined,
|
|
295
|
+
|
|
296
|
+
rootItemId = isNestedRequest ? this.state.nestedRequest.rootItemId : item.id,
|
|
297
|
+
|
|
290
298
|
// create copy of cursor so we don't leak script ids outside `event.command`
|
|
291
299
|
// and across scripts
|
|
292
|
-
|
|
300
|
+
|
|
301
|
+
// in the case of nested requests, we want to make sure any exceptions, consoles etc
|
|
302
|
+
// are tagged to the parent request's scripts
|
|
303
|
+
scriptCursor = _.clone(isNestedRequest ? this.state.nestedRequest.rootCursor : cursor);
|
|
293
304
|
|
|
294
305
|
// store the execution id in script
|
|
295
306
|
script._lastExecutionId = executionId; // please don't use it anywhere else!
|
|
296
307
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
308
|
+
if (!isNestedRequest) {
|
|
309
|
+
// if we can find an id on script or event we add them to the cursor
|
|
310
|
+
// so logs and errors can be traced back to the script they came from
|
|
311
|
+
event.id && (scriptCursor.eventId = event.id);
|
|
312
|
+
event.script.id && (scriptCursor.scriptId = event.script.id);
|
|
313
|
+
}
|
|
301
314
|
|
|
302
315
|
// trigger the "beforeScript" callback
|
|
303
316
|
this.triggers.beforeScript(null, scriptCursor, script, event, item);
|
|
@@ -394,11 +407,20 @@ module.exports = {
|
|
|
394
407
|
}
|
|
395
408
|
}.bind(this));
|
|
396
409
|
|
|
410
|
+
// Explicitly enable tracking for vault secrets here as this will
|
|
411
|
+
// not be sent to sandbox who otherwise takes care of mutation tracking
|
|
412
|
+
// This is important especially when dealing with nested requests, without this, the parent req might
|
|
413
|
+
// not have a pm.vault.<cmd> call and thus no mutations would bubble up and apply to the parent
|
|
414
|
+
if (vaultSecrets && vaultSecrets.enableTracking) {
|
|
415
|
+
vaultSecrets.enableTracking({ autoCompact: true });
|
|
416
|
+
}
|
|
417
|
+
|
|
397
418
|
this.host.on(EXECUTION_VAULT_BASE + executionId, async function (id, cmd, ...args) {
|
|
398
419
|
if (hasVaultAccess === undefined) {
|
|
399
420
|
try {
|
|
400
421
|
// eslint-disable-next-line require-atomic-updates
|
|
401
|
-
hasVaultAccess = Boolean(
|
|
422
|
+
hasVaultAccess = Boolean(this.state.nestedRequest?.hasVaultAccess ||
|
|
423
|
+
await vaultSecrets?._?.allowScriptAccess(rootItemId));
|
|
402
424
|
}
|
|
403
425
|
catch (_) {
|
|
404
426
|
// eslint-disable-next-line require-atomic-updates
|
|
@@ -406,6 +428,10 @@ module.exports = {
|
|
|
406
428
|
}
|
|
407
429
|
}
|
|
408
430
|
|
|
431
|
+
if (isNestedRequest) {
|
|
432
|
+
this.state.nestedRequest.hasVaultAccess = hasVaultAccess;
|
|
433
|
+
}
|
|
434
|
+
|
|
409
435
|
// Ensure error is string
|
|
410
436
|
// TODO identify why error objects are not being serialized correctly
|
|
411
437
|
const dispatch = (e, r) => { this.host.dispatch(EXECUTION_VAULT_BASE + executionId, id, e, r); };
|
|
@@ -418,10 +444,6 @@ module.exports = {
|
|
|
418
444
|
return dispatch(`Invalid vault command: ${cmd}`);
|
|
419
445
|
}
|
|
420
446
|
|
|
421
|
-
// Explicitly enable tracking for vault secrets here as this will
|
|
422
|
-
// not be sent to sandbox who otherwise takes care of mutation tracking
|
|
423
|
-
vaultSecrets.enableTracking({ autoCompact: true });
|
|
424
|
-
|
|
425
447
|
dispatch(null, vaultSecrets[cmd](...args));
|
|
426
448
|
}.bind(this));
|
|
427
449
|
|
|
@@ -480,6 +502,9 @@ module.exports = {
|
|
|
480
502
|
}.bind(this));
|
|
481
503
|
}.bind(this));
|
|
482
504
|
|
|
505
|
+
this.host.on(EXECUTION_RUN_REQUEST_EVENT_BASE + executionId,
|
|
506
|
+
runNestedRequest({ executionId, isExecutionSkipped, vaultSecrets, item }).bind(this));
|
|
507
|
+
|
|
483
508
|
this.host.on(EXECUTION_SKIP_REQUEST_EVENT_BASE + executionId, function () {
|
|
484
509
|
skippedExecutions.add(executionId);
|
|
485
510
|
shouldSkipExecution = true;
|
|
@@ -499,6 +524,9 @@ module.exports = {
|
|
|
499
524
|
context: _.pick(payload.context, SAFE_CONTEXT_VARIABLES),
|
|
500
525
|
resolvedPackages: resolvedPackages,
|
|
501
526
|
|
|
527
|
+
disabledAPIs: !_.get(this, 'options.script.requestResolver') ?
|
|
528
|
+
['execution.runRequest'] : [],
|
|
529
|
+
|
|
502
530
|
// legacy options
|
|
503
531
|
legacy: {
|
|
504
532
|
_itemId: item.id,
|
|
@@ -510,6 +538,7 @@ module.exports = {
|
|
|
510
538
|
this.host.removeAllListeners(EXECUTION_REQUEST_EVENT_BASE + executionId);
|
|
511
539
|
this.host.removeAllListeners(EXECUTION_ASSERTION_EVENT_BASE + executionId);
|
|
512
540
|
this.host.removeAllListeners(EXECUTION_RESPONSE_EVENT_BASE + executionId);
|
|
541
|
+
this.host.removeAllListeners(EXECUTION_RUN_REQUEST_RESPONSE_EVENT_BASE + executionId);
|
|
513
542
|
this.host.removeAllListeners(EXECUTION_COOKIES_EVENT_BASE + executionId);
|
|
514
543
|
this.host.removeAllListeners(EXECUTION_ERROR_EVENT_BASE + executionId);
|
|
515
544
|
this.host.removeAllListeners(EXECUTION_SKIP_REQUEST_EVENT_BASE + executionId);
|
|
@@ -567,11 +596,13 @@ module.exports = {
|
|
|
567
596
|
result && result.request && (result.request = new sdk.Request(result.request));
|
|
568
597
|
|
|
569
598
|
// vault secrets are not sent to sandbox, thus using the scope from run context.
|
|
570
|
-
if (
|
|
571
|
-
result.vaultSecrets = vaultSecrets;
|
|
572
|
-
|
|
599
|
+
if (vaultSecrets) {
|
|
573
600
|
// Prevent mutations from being carry-forwarded to subsequent events
|
|
574
601
|
vaultSecrets.disableTracking();
|
|
602
|
+
|
|
603
|
+
if (hasVaultAccess || this.state.nestedRequest?.hasVaultAccess) {
|
|
604
|
+
result.vaultSecrets = vaultSecrets;
|
|
605
|
+
}
|
|
575
606
|
}
|
|
576
607
|
|
|
577
608
|
// @note Since postman-sandbox@3.5.2, response object is not included in the execution
|
|
@@ -598,6 +629,12 @@ module.exports = {
|
|
|
598
629
|
// now that this script is done executing, we trigger the event and move to the next script
|
|
599
630
|
this.triggers.script(err || null, scriptCursor, result, script, event, item);
|
|
600
631
|
|
|
632
|
+
if (!isNestedRequest) {
|
|
633
|
+
// Reset for next invocation if there are subsequent executions of items in a single
|
|
634
|
+
// runner via `iterationCount` or multiple `items` passed to a collection.
|
|
635
|
+
delete this.state.nestedRequest;
|
|
636
|
+
}
|
|
637
|
+
|
|
601
638
|
// move to next script and pass on the results for accumulation
|
|
602
639
|
done(((stopOnScriptError || abortOnError || stopOnFailure) && err) ? err : null, _.assign({
|
|
603
640
|
event,
|
|
@@ -127,7 +127,9 @@ module.exports = {
|
|
|
127
127
|
// we procure the coordinates that we have to pick item and data from. the data is
|
|
128
128
|
var coords = payload.static ? payload.coords : this.waterfall.whatnext(payload.coords),
|
|
129
129
|
item = this.state.items[coords.position],
|
|
130
|
-
delay
|
|
130
|
+
delay,
|
|
131
|
+
isNestedRequest = this.state.nestedRequest !== undefined,
|
|
132
|
+
rootRequestCursor = isNestedRequest ? this.state.nestedRequest.rootCursor : coords;
|
|
131
133
|
|
|
132
134
|
// if there is nothing to process, we bail out from here, even before we enter the iteration cycle
|
|
133
135
|
if (coords.empty) {
|
|
@@ -165,7 +167,8 @@ module.exports = {
|
|
|
165
167
|
this.queue('item', {
|
|
166
168
|
item: item,
|
|
167
169
|
coords: coords,
|
|
168
|
-
data: getIterationData(this.state.data,
|
|
170
|
+
data: getIterationData(this.state.data,
|
|
171
|
+
isNestedRequest ? rootRequestCursor.iteration : coords.iteration),
|
|
169
172
|
environment: this.state.environment,
|
|
170
173
|
globals: this.state.globals,
|
|
171
174
|
vaultSecrets: this.state.vaultSecrets,
|
package/lib/runner/index.js
CHANGED
|
@@ -42,6 +42,9 @@ _.assign(Runner.prototype, {
|
|
|
42
42
|
var runOptions = _.merge(_.omit(options,
|
|
43
43
|
['environment', 'globals', 'vaultSecrets', 'data']), this.options.run) || {};
|
|
44
44
|
|
|
45
|
+
// Ensure we have a default value for max invokable nested requests
|
|
46
|
+
!runOptions.maxInvokableNestedRequests && (runOptions.maxInvokableNestedRequests = 5);
|
|
47
|
+
|
|
45
48
|
// start timeout sanitization
|
|
46
49
|
!runOptions.timeout && (runOptions.timeout = {});
|
|
47
50
|
|
|
@@ -66,6 +69,24 @@ _.assign(Runner.prototype, {
|
|
|
66
69
|
* @param {Object} [options.globals] -
|
|
67
70
|
* @param {Object} [options.environment] -
|
|
68
71
|
* @param {Object} [options.vaultSecrets] - Vault Secrets
|
|
72
|
+
* @param {Object} [options.nestedRequest] - State and options used for nested request set by parent request
|
|
73
|
+
* @param {Number} [options.nestedRequest.rootCursor] - The cursor of the root request that spun up this
|
|
74
|
+
* nested request runner. This is recursively passed down to keep track of which execution started the chain
|
|
75
|
+
* and modify cursors for all nested req events for reporters built on top of postman-runtime.
|
|
76
|
+
* @param {Number} [options.nestedRequest.rootItemId] - The id of the root item that spawned this nested request.
|
|
77
|
+
* Used by vault to get consent for root request and determine whether vault access check was performed even once
|
|
78
|
+
* throughout the chain.
|
|
79
|
+
* @param {Number} [options.nestedRequest.hasVaultAccess] - Mutated and set by any nested or parent request
|
|
80
|
+
* to indicate whether vault access check has been performed.
|
|
81
|
+
* @param {Number} [options.nestedRequest.invocationCount] - The number of requests currently accummulated
|
|
82
|
+
* by the nested request chain.
|
|
83
|
+
* @param {Object} [options.requester] - Options specific to the requester
|
|
84
|
+
* @param {Function} [options.script.requestResolver] - Resolver that receives an id from
|
|
85
|
+
* pm.execution.runRequest and returns the JSON for the request collection.
|
|
86
|
+
* Should return a postman-collection compatible collection JSON with `item` containing the request to run,
|
|
87
|
+
* `variable` array containing list of request-specific-collection variables and `event` with scripts to execute.
|
|
88
|
+
* @param {Number} [options.maxInvokableNestedRequests] - The maximum number of nested requests
|
|
89
|
+
* that a script can invoke, combined in total and recursively nested
|
|
69
90
|
* @param {Number} [options.iterationCount] -
|
|
70
91
|
* @param {CertificateList} [options.certificates] -
|
|
71
92
|
* @param {ProxyConfigList} [options.proxies] -
|
|
@@ -119,6 +140,8 @@ _.assign(Runner.prototype, {
|
|
|
119
140
|
environment: options.environment,
|
|
120
141
|
globals: _.has(options, 'globals') ? options.globals : self.options.globals,
|
|
121
142
|
vaultSecrets: options.vaultSecrets,
|
|
143
|
+
// Used for nested request executions
|
|
144
|
+
nestedRequest: options.nestedRequest,
|
|
122
145
|
// @todo Move to item level to support Item and ItemGroup variables
|
|
123
146
|
collectionVariables: collection.variables,
|
|
124
147
|
localVariables: options.localVariables,
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
const _ = require('lodash'),
|
|
2
|
+
sdk = require('postman-collection'),
|
|
3
|
+
serialisedError = require('serialised-error'),
|
|
4
|
+
|
|
5
|
+
SYNCABLE_CONTEXT_VARIABLE_SCOPES = ['_variables', 'collectionVariables', 'environment', 'globals'],
|
|
6
|
+
EXECUTION_RUN_REQUEST_RESPONSE_EVENT_BASE = 'execution.run_collection_request_response.';
|
|
7
|
+
|
|
8
|
+
function runNestedRequest ({ executionId, isExecutionSkipped, vaultSecrets, item }) {
|
|
9
|
+
// Note: Don't forget to bind this function to the runner to make sure `this` can give you the right options & state
|
|
10
|
+
return function (cursor, eventId, requestId, requestToRunId, runRequestOptions = {}, runContext = {}) {
|
|
11
|
+
const self = this,
|
|
12
|
+
requestResolver = _.get(self, 'options.script.requestResolver'),
|
|
13
|
+
runRequestRespEvent = EXECUTION_RUN_REQUEST_RESPONSE_EVENT_BASE + eventId,
|
|
14
|
+
maxInvokableNestedRequests = _.get(self, 'options.maxInvokableNestedRequests');
|
|
15
|
+
|
|
16
|
+
function dispatchErrorToListener (err) {
|
|
17
|
+
const error = serialisedError(err);
|
|
18
|
+
|
|
19
|
+
delete error.stack;
|
|
20
|
+
|
|
21
|
+
return self.host.dispatch(EXECUTION_RUN_REQUEST_RESPONSE_EVENT_BASE + eventId,
|
|
22
|
+
requestId, error);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Prepare nested request object for passing down to child request
|
|
26
|
+
// Have to keep object reference common so any changes made by nested executions is bubbled back to parent exec
|
|
27
|
+
self.state.nestedRequest = _.defaults(self.state.nestedRequest || {}, {
|
|
28
|
+
isNestedRequest: true,
|
|
29
|
+
rootCursor: cursor,
|
|
30
|
+
rootItemId: item.id,
|
|
31
|
+
invocationCount: 0
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
self.state.nestedRequest.invocationCount++;
|
|
35
|
+
|
|
36
|
+
// No more than maxInvokableNestedRequests runRequest calls per script or any of its nested request scripts
|
|
37
|
+
if (self.state.nestedRequest.invocationCount > maxInvokableNestedRequests) {
|
|
38
|
+
return dispatchErrorToListener(new Error('The maximum number of pm.execution.runRequest()' +
|
|
39
|
+
' calls have been reached for this request.'));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Should fetch the request from the consumer of postman-runtime & resolve scripts and variables
|
|
43
|
+
requestResolver(requestToRunId, function (err, collection) {
|
|
44
|
+
if (err) {
|
|
45
|
+
return dispatchErrorToListener(err);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!collection) {
|
|
49
|
+
return dispatchErrorToListener(new Error('Expected collection json with request item ' +
|
|
50
|
+
'to invoke pm.execution.runRequest'));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Prepare variables that have been set inside the parent's pre-req/post-res script
|
|
54
|
+
// so far and pass them to the runner for this request.
|
|
55
|
+
// This is important because for nested requests,
|
|
56
|
+
// variables set by the parent using pm.<variable-form>.set do not reflect
|
|
57
|
+
// in postman-runtime's scope immediately. They are present inside postman-sandbox
|
|
58
|
+
// till the parent request's script execution ends.
|
|
59
|
+
const globals = { values: runContext.globals || [] },
|
|
60
|
+
localVariables = { values: runContext._variables || [] },
|
|
61
|
+
environment = { values: runContext.environment || [] },
|
|
62
|
+
collectionVariables = { values: runContext.collectionVariables || [] };
|
|
63
|
+
|
|
64
|
+
const runner = require('.'),
|
|
65
|
+
variableOverrides = runRequestOptions.variables ?
|
|
66
|
+
Object.entries(runRequestOptions.variables)
|
|
67
|
+
.map(function ([key, value]) {
|
|
68
|
+
return { key, value };
|
|
69
|
+
}) :
|
|
70
|
+
[],
|
|
71
|
+
runnableRefRequestCollection = new sdk.Collection(collection),
|
|
72
|
+
mergedCollectionVariableList = new sdk.VariableList();
|
|
73
|
+
|
|
74
|
+
// Merge parent collection's variables with this collection's variables
|
|
75
|
+
// Why the separate statement? Because we want the referenced request's collection's variables to
|
|
76
|
+
// take precedence over the parent collection's variables if there are any conflicts.
|
|
77
|
+
mergedCollectionVariableList.populate(collectionVariables.values);
|
|
78
|
+
mergedCollectionVariableList.populate(runnableRefRequestCollection.variables.all());
|
|
79
|
+
|
|
80
|
+
runnableRefRequestCollection.variables = mergedCollectionVariableList;
|
|
81
|
+
|
|
82
|
+
// Merge local variables from parent requests & scope + nestedRequest.options.variables
|
|
83
|
+
localVariables.values = [...localVariables.values, ...variableOverrides];
|
|
84
|
+
|
|
85
|
+
// Why clone? Each runner execution needs to track and mutate its vault variables separately and propagate
|
|
86
|
+
// it back up and further down. We don't want to accidentally reset mutations between executions by sharing
|
|
87
|
+
// this scope
|
|
88
|
+
let clonedVaultSecrets;
|
|
89
|
+
|
|
90
|
+
if (vaultSecrets) {
|
|
91
|
+
clonedVaultSecrets = new sdk.VariableScope({
|
|
92
|
+
values: vaultSecrets.values,
|
|
93
|
+
prefix: vaultSecrets.prefix
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
clonedVaultSecrets._ = vaultSecrets._;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
new runner().run(runnableRefRequestCollection,
|
|
100
|
+
{
|
|
101
|
+
...self.state,
|
|
102
|
+
...self.options,
|
|
103
|
+
entrypoint: {
|
|
104
|
+
lookupStrategy: 'idOrName',
|
|
105
|
+
execute: requestToRunId
|
|
106
|
+
},
|
|
107
|
+
iterationCount: 1,
|
|
108
|
+
globals: globals,
|
|
109
|
+
environment: environment,
|
|
110
|
+
localVariables: localVariables,
|
|
111
|
+
vaultSecrets: clonedVaultSecrets,
|
|
112
|
+
abortOnFailure: true,
|
|
113
|
+
host: {
|
|
114
|
+
// Reuse current run's sandbox host across nested executions
|
|
115
|
+
external: true,
|
|
116
|
+
instance: self.host
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
function (err, run) {
|
|
120
|
+
let exceptionForThisRequest = null,
|
|
121
|
+
responseForThisRequest = null,
|
|
122
|
+
variableMutationsFromThisExecution = {};
|
|
123
|
+
|
|
124
|
+
if (err) {
|
|
125
|
+
return self.host
|
|
126
|
+
.dispatch(EXECUTION_RUN_REQUEST_RESPONSE_EVENT_BASE + eventId,
|
|
127
|
+
requestId, err);
|
|
128
|
+
}
|
|
129
|
+
run.start({
|
|
130
|
+
script (_err, _cursor, result) {
|
|
131
|
+
// This is to sync changes to pm.variables, pm.environment & pm.globals
|
|
132
|
+
// that happened inside the nested request's script
|
|
133
|
+
// back to parent request's scripts still currently executing.
|
|
134
|
+
|
|
135
|
+
// collectionVariables don't need to be synced between parent and nested
|
|
136
|
+
|
|
137
|
+
// All other global variables defined by syntax like 'a=1'
|
|
138
|
+
// are anyway synced as the sandbox's common scope is shared across runs
|
|
139
|
+
if (result) {
|
|
140
|
+
SYNCABLE_CONTEXT_VARIABLE_SCOPES.forEach(function (type) {
|
|
141
|
+
if (!result[type]) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
variableMutationsFromThisExecution[type] = [
|
|
146
|
+
...(variableMutationsFromThisExecution[type] || []),
|
|
147
|
+
result[type].mutations
|
|
148
|
+
];
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
if (clonedVaultSecrets && clonedVaultSecrets.mutations) {
|
|
152
|
+
const mutations = new sdk.MutationTracker(clonedVaultSecrets.mutations);
|
|
153
|
+
|
|
154
|
+
mutations.applyOn(vaultSecrets);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
exception (_, err) {
|
|
159
|
+
if (err) {
|
|
160
|
+
exceptionForThisRequest = err;
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
response (err, _, response) {
|
|
164
|
+
if (isExecutionSkipped(executionId)) {
|
|
165
|
+
responseForThisRequest = null;
|
|
166
|
+
exceptionForThisRequest = null;
|
|
167
|
+
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (err) {
|
|
172
|
+
exceptionForThisRequest = err;
|
|
173
|
+
}
|
|
174
|
+
if (response) {
|
|
175
|
+
responseForThisRequest = response;
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
done (err) {
|
|
179
|
+
let error = err || exceptionForThisRequest;
|
|
180
|
+
|
|
181
|
+
if (error) {
|
|
182
|
+
error = serialisedError(error);
|
|
183
|
+
delete error.stack;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return self.host.dispatch(runRequestRespEvent,
|
|
187
|
+
requestId, error || null,
|
|
188
|
+
responseForThisRequest,
|
|
189
|
+
{ variableMutations: variableMutationsFromThisExecution });
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
module.exports = runNestedRequest;
|
package/lib/runner/run.js
CHANGED
|
@@ -157,8 +157,14 @@ _.assign(Run.prototype, {
|
|
|
157
157
|
|
|
158
158
|
// if there is nothing to process, exit
|
|
159
159
|
if (!instruction) {
|
|
160
|
-
|
|
161
|
-
|
|
160
|
+
const isNestedRequest = this.state.nestedRequest !== undefined;
|
|
161
|
+
|
|
162
|
+
// dispose the host before ending the run for the main container request
|
|
163
|
+
// - a nested request uses the parent's sandbox host, so only dispose it once the entire nested request
|
|
164
|
+
// execution chain is complete
|
|
165
|
+
if (!isNestedRequest && this.host) {
|
|
166
|
+
this.host.dispose();
|
|
167
|
+
}
|
|
162
168
|
|
|
163
169
|
callback(null, this.state.cursor.current());
|
|
164
170
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "postman-runtime",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.46.0",
|
|
4
4
|
"description": "Underlying library of executing Postman Collections",
|
|
5
5
|
"author": "Postman Inc.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -54,10 +54,10 @@
|
|
|
54
54
|
"node-forge": "1.3.1",
|
|
55
55
|
"node-oauth1": "1.3.0",
|
|
56
56
|
"performance-now": "2.1.0",
|
|
57
|
-
"postman-collection": "5.1.
|
|
57
|
+
"postman-collection": "5.1.1",
|
|
58
58
|
"postman-request": "2.88.1-postman.43",
|
|
59
|
-
"postman-sandbox": "6.
|
|
60
|
-
"postman-url-encoder": "3.0.
|
|
59
|
+
"postman-sandbox": "6.2.0",
|
|
60
|
+
"postman-url-encoder": "3.0.8",
|
|
61
61
|
"serialised-error": "1.1.3",
|
|
62
62
|
"strip-json-comments": "3.1.1",
|
|
63
63
|
"uuid": "8.3.2"
|