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.
@@ -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
- this.queueDelay(function () {
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
- ctxTemplate = {
140
- collectionVariables: collectionVariables,
141
- vaultSecrets: vaultSecrets,
142
- _variables: _variables,
143
- globals: globals,
144
- environment: environment,
145
- data: data,
146
- request: item.request
147
- };
148
-
149
- // @todo make it less nested by coding Instruction.thenQueue
150
- this.queue('event', {
151
- name: 'prerequest',
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
- source: 'collection'
202
- }).done(function (result, requestError) {
203
- !result && (result = {});
204
-
205
- var request = result.request,
206
- response = result.response,
207
- cookies = result.cookies;
208
-
209
- if ((stopOnError || stopOnFailure) && requestError) {
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, requestError, {
213
- request
183
+ return callback && callback.call(this, prereqExecutionError, {
184
+ prerequest: prereqExecutions
214
185
  });
215
186
  }
216
187
 
217
- // also the test object requires the updated request object (since auth helpers may modify it)
218
- request && (ctxTemplate.request = request);
188
+ if (shouldSkipExecution) {
189
+ this.triggers.item(prereqExecutionError, coords, item, null, { isSkipped: true });
219
190
 
220
- // @note convert response instance to plain object.
221
- // we want to avoid calling Response.toJSON() which triggers toJSON on Response.stream buffer.
222
- // Because that increases the size of stringified object by 3 times.
223
- // Also, that increases the total number of tokens (buffer.data) whereas Buffer.toString
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
- // set cookies for this transaction
228
- cookies && (ctxTemplate.cookies = cookies);
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
- // the context template also has a test object to store assertions
231
- ctxTemplate.tests = {}; // @todo remove
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('event', {
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
- context: ctxTemplate,
238
- // No need to include vaultSecrets here as runtime takes care of tracking internally
239
- trackContext: ['tests', 'globals', 'environment', 'collectionVariables'],
240
- stopOnScriptError: stopOnError,
241
- abortOnFailure: abortOnFailure,
242
- stopOnFailure: stopOnFailure
243
- }).done(function (testExecutions, testExecutionError) {
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
- // add processed template and data to visualizer result
257
- processedTemplate: processedTemplate,
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
- // trigger an event saying that item has been processed
262
- this.triggers.item(null, coords, item, visualizerResult);
263
- }.bind(this));
230
+ return callback && callback.call(this, requestError, {
231
+ request
232
+ });
264
233
  }
265
- else {
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
- this.triggers.item(null, coords, item, null);
269
- }
270
-
271
- // reset mutated request with original request instance
272
- // @note request mutations are not persisted across iterations
273
- item.request = originalRequest;
274
-
275
- callback && callback.call(this, ((stopOnError || stopOnFailure) && testExecutionError) ?
276
- testExecutionError : null, {
277
- prerequest: prereqExecutions,
278
- request: request,
279
- response: response,
280
- test: testExecutions
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
- }.bind(this), {
286
- time: delay,
287
- source: 'item',
288
- cursor: coords
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 abortOnError = _.has(payload, 'abortOnError') ? payload.abortOnError : this.options.abortOnError,
15
+ var self = this,
16
+ abortOnError = _.has(payload, 'abortOnError') ? payload.abortOnError : this.options.abortOnError,
16
17
 
17
- // helper function to trigger `response` callback anc complete the command
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 && this.triggers.response(err, nextPayload.coords, nextPayload.response,
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
- }.bind(this),
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
- this.immediate('httprequest', payload)
43
+ self.immediate('httprequest', payload)
42
44
  .done(function (nextPayload, err) {
43
45
  // change signature to error first
44
46
  complete(err, nextPayload);
@@ -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
  }