postman-runtime 7.51.1 → 7.52.0-beta.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/CHANGELOG.yaml +4 -0
- package/dist/index.js +1 -1
- package/lib/runner/extensions/item.command.js +174 -131
- package/lib/runner/extensions/request.command.js +7 -5
- package/lib/runner/index.js +10 -1
- package/lib/runner/resolve-secrets.js +311 -0
- package/package.json +2 -2
|
@@ -2,6 +2,7 @@ var _ = require('lodash'),
|
|
|
2
2
|
uuid = require('uuid'),
|
|
3
3
|
Response = require('postman-collection').Response,
|
|
4
4
|
visualizer = require('../../visualizer'),
|
|
5
|
+
resolveSecrets = require('../resolve-secrets').resolveSecrets,
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* List of request properties which can be mutated via pre-request
|
|
@@ -121,7 +122,11 @@ module.exports = {
|
|
|
121
122
|
stopOnFailure = this.options.stopOnFailure,
|
|
122
123
|
delay = _.get(this.options, 'delay.item'),
|
|
123
124
|
|
|
124
|
-
ctxTemplate
|
|
125
|
+
ctxTemplate,
|
|
126
|
+
self = this,
|
|
127
|
+
secretResolvers = this.state.secretResolvers,
|
|
128
|
+
secretResolutionPayload,
|
|
129
|
+
executeItemFlow;
|
|
125
130
|
|
|
126
131
|
// validate minimum parameters required for the command to work
|
|
127
132
|
if (!(item && coords)) {
|
|
@@ -134,159 +139,197 @@ module.exports = {
|
|
|
134
139
|
// here we code to queue prerequest script, then make a request and then execute test script
|
|
135
140
|
this.triggers.beforeItem(null, coords, item);
|
|
136
141
|
|
|
137
|
-
|
|
142
|
+
// Build payload for secret resolution
|
|
143
|
+
// Only scan environment, globals, and collectionVariables for secrets
|
|
144
|
+
secretResolutionPayload = {
|
|
145
|
+
item,
|
|
146
|
+
environment,
|
|
147
|
+
globals,
|
|
148
|
+
collectionVariables
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Execute the main item flow (delay, prerequest, request, test)
|
|
153
|
+
*/
|
|
154
|
+
executeItemFlow = function () {
|
|
155
|
+
self.queueDelay(function () {
|
|
138
156
|
// create the context object for scripts to run
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
item: item,
|
|
153
|
-
coords: coords,
|
|
154
|
-
context: ctxTemplate,
|
|
155
|
-
// No need to include vaultSecrets here as runtime takes care of tracking internally
|
|
156
|
-
trackContext: ['globals', 'environment', 'collectionVariables'],
|
|
157
|
-
stopOnScriptError: stopOnError,
|
|
158
|
-
stopOnFailure: stopOnFailure
|
|
159
|
-
}).done(function (prereqExecutions, prereqExecutionError, shouldSkipExecution) {
|
|
160
|
-
// if stop on error is marked and script executions had an error,
|
|
161
|
-
// do not proceed with more commands, instead we bail out
|
|
162
|
-
if ((stopOnError || stopOnFailure) && prereqExecutionError) {
|
|
163
|
-
this.triggers.item(null, coords, item); // @todo - should this trigger receive error?
|
|
164
|
-
|
|
165
|
-
return callback && callback.call(this, prereqExecutionError, {
|
|
166
|
-
prerequest: prereqExecutions
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (shouldSkipExecution) {
|
|
171
|
-
this.triggers.item(prereqExecutionError, coords, item, null, { isSkipped: true });
|
|
172
|
-
|
|
173
|
-
return callback && callback.call(this, prereqExecutionError, {
|
|
174
|
-
prerequest: prereqExecutions
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// update allowed request mutation properties with the mutated context
|
|
179
|
-
// @note from this point forward, make sure this mutated
|
|
180
|
-
// request instance is used for upcoming commands.
|
|
181
|
-
ALLOWED_REQUEST_MUTATIONS.forEach(function (property) {
|
|
182
|
-
if (_.has(ctxTemplate, ['request', property])) {
|
|
183
|
-
item.request[property] = ctxTemplate.request[property];
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// update property's parent reference
|
|
187
|
-
if (item.request[property] && typeof item.request[property].setParent === 'function') {
|
|
188
|
-
item.request[property].setParent(item.request);
|
|
189
|
-
}
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
this.queue('request', {
|
|
157
|
+
ctxTemplate = {
|
|
158
|
+
collectionVariables: collectionVariables,
|
|
159
|
+
vaultSecrets: vaultSecrets,
|
|
160
|
+
_variables: _variables,
|
|
161
|
+
globals: globals,
|
|
162
|
+
environment: environment,
|
|
163
|
+
data: data,
|
|
164
|
+
request: item.request
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
// @todo make it less nested by coding Instruction.thenQueue
|
|
168
|
+
this.queue('event', {
|
|
169
|
+
name: 'prerequest',
|
|
193
170
|
item: item,
|
|
194
|
-
vaultSecrets: ctxTemplate.vaultSecrets,
|
|
195
|
-
globals: ctxTemplate.globals,
|
|
196
|
-
environment: ctxTemplate.environment,
|
|
197
|
-
collectionVariables: ctxTemplate.collectionVariables,
|
|
198
|
-
_variables: ctxTemplate._variables,
|
|
199
|
-
data: ctxTemplate.data,
|
|
200
171
|
coords: coords,
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
if ((stopOnError || stopOnFailure) &&
|
|
172
|
+
context: ctxTemplate,
|
|
173
|
+
// No need to include vaultSecrets here as runtime takes care of tracking internally
|
|
174
|
+
trackContext: ['globals', 'environment', 'collectionVariables'],
|
|
175
|
+
stopOnScriptError: stopOnError,
|
|
176
|
+
stopOnFailure: stopOnFailure
|
|
177
|
+
}).done(function (prereqExecutions, prereqExecutionError, shouldSkipExecution) {
|
|
178
|
+
// if stop on error is marked and script executions had an error,
|
|
179
|
+
// do not proceed with more commands, instead we bail out
|
|
180
|
+
if ((stopOnError || stopOnFailure) && prereqExecutionError) {
|
|
210
181
|
this.triggers.item(null, coords, item); // @todo - should this trigger receive error?
|
|
211
182
|
|
|
212
|
-
return callback && callback.call(this,
|
|
213
|
-
|
|
183
|
+
return callback && callback.call(this, prereqExecutionError, {
|
|
184
|
+
prerequest: prereqExecutions
|
|
214
185
|
});
|
|
215
186
|
}
|
|
216
187
|
|
|
217
|
-
|
|
218
|
-
|
|
188
|
+
if (shouldSkipExecution) {
|
|
189
|
+
this.triggers.item(prereqExecutionError, coords, item, null, { isSkipped: true });
|
|
219
190
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
// generates a single string that is easier to stringify and sent over the UVM bridge.
|
|
225
|
-
response && (ctxTemplate.response = getResponseJSON(response));
|
|
191
|
+
return callback && callback.call(this, prereqExecutionError, {
|
|
192
|
+
prerequest: prereqExecutions
|
|
193
|
+
});
|
|
194
|
+
}
|
|
226
195
|
|
|
227
|
-
//
|
|
228
|
-
|
|
196
|
+
// update allowed request mutation properties with the mutated context
|
|
197
|
+
// @note from this point forward, make sure this mutated
|
|
198
|
+
// request instance is used for upcoming commands.
|
|
199
|
+
ALLOWED_REQUEST_MUTATIONS.forEach(function (property) {
|
|
200
|
+
if (_.has(ctxTemplate, ['request', property])) {
|
|
201
|
+
item.request[property] = ctxTemplate.request[property];
|
|
202
|
+
}
|
|
229
203
|
|
|
230
|
-
|
|
231
|
-
|
|
204
|
+
// update property's parent reference
|
|
205
|
+
if (item.request[property] && typeof item.request[property].setParent === 'function') {
|
|
206
|
+
item.request[property].setParent(item.request);
|
|
207
|
+
}
|
|
208
|
+
});
|
|
232
209
|
|
|
233
|
-
this.queue('
|
|
234
|
-
name: 'test',
|
|
210
|
+
this.queue('request', {
|
|
235
211
|
item: item,
|
|
212
|
+
vaultSecrets: ctxTemplate.vaultSecrets,
|
|
213
|
+
globals: ctxTemplate.globals,
|
|
214
|
+
environment: ctxTemplate.environment,
|
|
215
|
+
collectionVariables: ctxTemplate.collectionVariables,
|
|
216
|
+
_variables: ctxTemplate._variables,
|
|
217
|
+
data: ctxTemplate.data,
|
|
236
218
|
coords: coords,
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
var visualizerData = extractVisualizerData(prereqExecutions, testExecutions),
|
|
245
|
-
visualizerResult;
|
|
246
|
-
|
|
247
|
-
if (visualizerData) {
|
|
248
|
-
visualizer.processTemplate(visualizerData.template,
|
|
249
|
-
visualizerData.data,
|
|
250
|
-
visualizerData.options,
|
|
251
|
-
function (err, processedTemplate) {
|
|
252
|
-
visualizerResult = {
|
|
253
|
-
// bubble up the errors while processing template through visualizer result
|
|
254
|
-
error: err,
|
|
219
|
+
source: 'collection'
|
|
220
|
+
}).done(function (result, requestError) {
|
|
221
|
+
!result && (result = {});
|
|
222
|
+
|
|
223
|
+
var request = result.request,
|
|
224
|
+
response = result.response,
|
|
225
|
+
cookies = result.cookies;
|
|
255
226
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
data: visualizerData.data
|
|
259
|
-
};
|
|
227
|
+
if ((stopOnError || stopOnFailure) && requestError) {
|
|
228
|
+
this.triggers.item(null, coords, item); // @todo - should this trigger receive error?
|
|
260
229
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
230
|
+
return callback && callback.call(this, requestError, {
|
|
231
|
+
request
|
|
232
|
+
});
|
|
264
233
|
}
|
|
265
|
-
|
|
234
|
+
|
|
235
|
+
// also the test object requires the updated request object
|
|
236
|
+
// (since auth helpers may modify it)
|
|
237
|
+
request && (ctxTemplate.request = request);
|
|
238
|
+
|
|
239
|
+
// @note convert response instance to plain object.
|
|
240
|
+
// we want to avoid calling Response.toJSON() which triggers
|
|
241
|
+
// toJSON on Response.stream buffer.
|
|
242
|
+
// Because that increases the size of stringified object by 3 times.
|
|
243
|
+
// Also, that increases the total number of tokens (buffer.data) whereas Buffer.toString
|
|
244
|
+
// generates a single string that is easier to stringify and sent over the UVM bridge.
|
|
245
|
+
response && (ctxTemplate.response = getResponseJSON(response));
|
|
246
|
+
|
|
247
|
+
// set cookies for this transaction
|
|
248
|
+
cookies && (ctxTemplate.cookies = cookies);
|
|
249
|
+
|
|
250
|
+
// the context template also has a test object to store assertions
|
|
251
|
+
ctxTemplate.tests = {}; // @todo remove
|
|
252
|
+
|
|
253
|
+
this.queue('event', {
|
|
254
|
+
name: 'test',
|
|
255
|
+
item: item,
|
|
256
|
+
coords: coords,
|
|
257
|
+
context: ctxTemplate,
|
|
258
|
+
// No need to include vaultSecrets here as runtime takes care of tracking internally
|
|
259
|
+
trackContext: ['tests', 'globals', 'environment', 'collectionVariables'],
|
|
260
|
+
stopOnScriptError: stopOnError,
|
|
261
|
+
abortOnFailure: abortOnFailure,
|
|
262
|
+
stopOnFailure: stopOnFailure
|
|
263
|
+
}).done(function (testExecutions, testExecutionError) {
|
|
264
|
+
var visualizerData = extractVisualizerData(prereqExecutions, testExecutions),
|
|
265
|
+
visualizerResult;
|
|
266
|
+
|
|
267
|
+
if (visualizerData) {
|
|
268
|
+
visualizer.processTemplate(visualizerData.template,
|
|
269
|
+
visualizerData.data,
|
|
270
|
+
visualizerData.options,
|
|
271
|
+
function (err, processedTemplate) {
|
|
272
|
+
visualizerResult = {
|
|
273
|
+
// bubble up the errors while processing template through visualizer result
|
|
274
|
+
error: err,
|
|
275
|
+
|
|
276
|
+
// add processed template and data to visualizer result
|
|
277
|
+
processedTemplate: processedTemplate,
|
|
278
|
+
data: visualizerData.data
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
// trigger an event saying that item has been processed
|
|
282
|
+
this.triggers.item(null, coords, item, visualizerResult);
|
|
283
|
+
}.bind(this));
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
266
286
|
// trigger an event saying that item has been processed
|
|
267
287
|
// @todo - should this trigger receive error?
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
288
|
+
this.triggers.item(null, coords, item, null);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// reset mutated request with original request instance
|
|
292
|
+
// @note request mutations are not persisted across iterations
|
|
293
|
+
item.request = originalRequest;
|
|
294
|
+
|
|
295
|
+
callback && callback.call(this, ((stopOnError || stopOnFailure) && testExecutionError) ?
|
|
296
|
+
testExecutionError : null, {
|
|
297
|
+
prerequest: prereqExecutions,
|
|
298
|
+
request: request,
|
|
299
|
+
response: response,
|
|
300
|
+
test: testExecutions
|
|
301
|
+
});
|
|
281
302
|
});
|
|
282
303
|
});
|
|
283
304
|
});
|
|
305
|
+
}.bind(self), {
|
|
306
|
+
time: delay,
|
|
307
|
+
source: 'item',
|
|
308
|
+
cursor: coords
|
|
309
|
+
}, next);
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
// Resolve secrets before pre-request scripts so scripts can access resolved values via related pm APIs
|
|
313
|
+
if (secretResolvers) {
|
|
314
|
+
resolveSecrets(secretResolutionPayload, secretResolvers, function (err) {
|
|
315
|
+
if (err) {
|
|
316
|
+
// Secret resolution failed - this is fatal for the current item
|
|
317
|
+
// (same pattern as pre-request script skip)
|
|
318
|
+
self.triggers.item(err, coords, item, null, { isSecretResolutionFailed: true });
|
|
319
|
+
|
|
320
|
+
callback && callback.call(self, err, {
|
|
321
|
+
request: null
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
return next();
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
executeItemFlow();
|
|
284
328
|
});
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
}, next);
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
executeItemFlow();
|
|
332
|
+
}
|
|
290
333
|
}
|
|
291
334
|
}
|
|
292
335
|
};
|
|
@@ -12,21 +12,23 @@ module.exports = {
|
|
|
12
12
|
|
|
13
13
|
process: {
|
|
14
14
|
request (payload, next) {
|
|
15
|
-
var
|
|
15
|
+
var self = this,
|
|
16
|
+
abortOnError = _.has(payload, 'abortOnError') ? payload.abortOnError : this.options.abortOnError,
|
|
16
17
|
|
|
17
|
-
// helper function to trigger `response` callback
|
|
18
|
+
// helper function to trigger `response` callback and complete the command
|
|
18
19
|
complete = function (err, nextPayload) {
|
|
19
20
|
// nextPayload will be empty for unhandled errors
|
|
20
21
|
// trigger `response` callback
|
|
21
22
|
// nextPayload.response will be empty for error flows
|
|
22
23
|
// the `item` argument is resolved and mutated here
|
|
23
|
-
nextPayload &&
|
|
24
|
+
nextPayload && self.triggers.response(err, nextPayload.coords, nextPayload.response,
|
|
24
25
|
nextPayload.request, nextPayload.item, nextPayload.cookies, nextPayload.history);
|
|
25
26
|
|
|
26
27
|
// the error is passed twice to allow control between aborting the error vs just
|
|
27
28
|
// bubbling it up
|
|
28
29
|
return next(err && abortOnError ? err : null, nextPayload, err);
|
|
29
|
-
}
|
|
30
|
+
},
|
|
31
|
+
|
|
30
32
|
context = createItemContext(payload);
|
|
31
33
|
|
|
32
34
|
// resolve variables in item and auth
|
|
@@ -38,7 +40,7 @@ module.exports = {
|
|
|
38
40
|
// we do not queue `httprequest` instruction here,
|
|
39
41
|
// queueing will unblock the item command to prepare for the next `event` instruction
|
|
40
42
|
// at this moment request is not fulfilled, and we want to block it
|
|
41
|
-
|
|
43
|
+
self.immediate('httprequest', payload)
|
|
42
44
|
.done(function (nextPayload, err) {
|
|
43
45
|
// change signature to error first
|
|
44
46
|
complete(err, nextPayload);
|
package/lib/runner/index.js
CHANGED
|
@@ -97,6 +97,14 @@ _.assign(Runner.prototype, {
|
|
|
97
97
|
* @param {String} [options.entrypoint.lookupStrategy=idOrName] strategy to lookup the entrypoint [idOrName, path]
|
|
98
98
|
* @param {Array<String>} [options.entrypoint.path] path to lookup
|
|
99
99
|
* @param {Object} [options.run] Run-specific options, such as options related to the host
|
|
100
|
+
* @param {Object} [options.secretResolvers] - Object mapping provider names to resolver configs.
|
|
101
|
+
* Keys must match secret.source.provider (e.g. postman, azure, 1password, aws, hashicorp).
|
|
102
|
+
* Each resolver config may have:
|
|
103
|
+
* - {String} id - (Required) Unique identifier for the resolver (same as secret.source.provider)
|
|
104
|
+
* - {String} name - (Required) Human-readable name for the resolver
|
|
105
|
+
* - {Function} resolver - (Required) Function(secret, context) that returns resolved value or Promise
|
|
106
|
+
* - {Number} [timeout=5000] - (Optional) Timeout in milliseconds (default: 5000)
|
|
107
|
+
* - {Number} [retryCount=0] - (Optional) Number of retry attempts on failure (default: 0)
|
|
100
108
|
*
|
|
101
109
|
* @param {Function} callback -
|
|
102
110
|
*/
|
|
@@ -147,7 +155,8 @@ _.assign(Runner.prototype, {
|
|
|
147
155
|
collectionVariables: collection.variables,
|
|
148
156
|
localVariables: options.localVariables,
|
|
149
157
|
certificates: options.certificates,
|
|
150
|
-
proxies: options.proxies
|
|
158
|
+
proxies: options.proxies,
|
|
159
|
+
secretResolvers: options.secretResolvers
|
|
151
160
|
}, runOptions)));
|
|
152
161
|
});
|
|
153
162
|
}
|