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.
@@ -0,0 +1,311 @@
1
+ var _ = require('lodash'),
2
+ async = require('async'),
3
+ sdk = require('postman-collection'),
4
+
5
+ DEFAULT_TIMEOUT = 5000,
6
+ DEFAULT_RETRY_COUNT = 0;
7
+
8
+ /**
9
+ * Extract all variables that have a `source` property from a variable scope.
10
+ *
11
+ * @param {VariableScope|Object} scope - scope
12
+ * @param {Set} [usedVariables] - optional set of variable keys to filter by
13
+ * @returns {Array} - variables
14
+ */
15
+ function extractSourceVariables (scope, usedVariables) {
16
+ if (!scope) {
17
+ return [];
18
+ }
19
+
20
+ var values = _.get(scope, 'values'),
21
+ sourceVariables = [];
22
+
23
+ if (!values) {
24
+ return [];
25
+ }
26
+
27
+ if (values.members && Array.isArray(values.members)) {
28
+ values.members.forEach(function (variable) {
29
+ if (variable && variable.source && variable.type === 'secret') {
30
+ if (!usedVariables || usedVariables.has(variable.key)) {
31
+ sourceVariables.push(variable);
32
+ }
33
+ }
34
+ });
35
+ }
36
+ else if (Array.isArray(values)) {
37
+ values.forEach(function (variable) {
38
+ if (variable && variable.source && variable.type === 'secret') {
39
+ if (!usedVariables || usedVariables.has(variable.key)) {
40
+ sourceVariables.push(variable);
41
+ }
42
+ }
43
+ });
44
+ }
45
+
46
+ return sourceVariables;
47
+ }
48
+
49
+ /**
50
+ * Find all variable substitutions used in the request item.
51
+ * Scans URL, headers, body, auth, and other request components.
52
+ *
53
+ * @param {Item} item - The request item to scan
54
+ * @returns {Set} - Set of variable keys used in the item
55
+ */
56
+ function findUsedVariables (item) {
57
+ var variableSet = new Set(),
58
+ itemJson,
59
+ substitutions;
60
+
61
+ if (item) {
62
+ itemJson = typeof item.toJSON === 'function' ? item.toJSON() : item;
63
+ substitutions = sdk.Property.findSubstitutions(itemJson);
64
+ substitutions.forEach(function (v) { variableSet.add(v); });
65
+ }
66
+
67
+ return variableSet;
68
+ }
69
+
70
+ /**
71
+ * Build context object for resolver function
72
+ *
73
+ * @param {Object} payload - Request payload
74
+ * @param {String} variableKey - The key of the variable being resolved
75
+ * @returns {Object} - Context object
76
+ */
77
+ function buildResolverContext (payload, variableKey) {
78
+ return {
79
+ item: payload.item,
80
+ variableKey: variableKey
81
+ };
82
+ }
83
+
84
+ /**
85
+ * Create a standardized secret error object
86
+ *
87
+ * @param {String} code - Error code
88
+ * @param {String} message - Human readable error message
89
+ * @param {Object} details - Additional error details
90
+ * @returns {Error} - Error object with additional properties
91
+ */
92
+ function createSecretError (code, message, details) {
93
+ var error = new Error(message);
94
+
95
+ error.code = code;
96
+ error.details = details;
97
+
98
+ return error;
99
+ }
100
+
101
+ /**
102
+ * Execute a single resolver with timeout and retry support
103
+ *
104
+ * @param {Function} resolver - The resolver function
105
+ * @param {Object} secret - Provider-specific secret object (e.g. source.postman)
106
+ * @param {Object} context - Resolver context
107
+ * @param {Object} options - Execution options
108
+ * @param {Number} options.timeout - Timeout in ms
109
+ * @param {Number} options.retryCount - Number of retries
110
+ * @param {Function} callback - Callback(err, resolvedValue)
111
+ */
112
+ function executeResolver (resolver, secret, context, options, callback) {
113
+ var timeout = options.timeout || DEFAULT_TIMEOUT,
114
+ retriesLeft = options.retryCount || DEFAULT_RETRY_COUNT,
115
+ timeoutId,
116
+ completed = false;
117
+
118
+ function attemptResolve () {
119
+ var result;
120
+
121
+ timeoutId = setTimeout(function () {
122
+ if (completed) {
123
+ return;
124
+ }
125
+
126
+ if (retriesLeft > 0) {
127
+ retriesLeft--;
128
+
129
+ return attemptResolve();
130
+ }
131
+
132
+ completed = true;
133
+ callback(new Error('Secret resolution timed out after ' + timeout + 'ms'));
134
+ }, timeout);
135
+
136
+ try {
137
+ result = resolver(secret, context);
138
+
139
+ // promise-based resolver handling
140
+ if (result && typeof result.then === 'function') {
141
+ result
142
+ .then(function (resolvedValue) {
143
+ if (completed) {
144
+ return;
145
+ }
146
+ clearTimeout(timeoutId);
147
+ completed = true;
148
+ callback(null, resolvedValue);
149
+ })
150
+ .catch(function (err) {
151
+ if (completed) {
152
+ return;
153
+ }
154
+ clearTimeout(timeoutId);
155
+
156
+ if (retriesLeft > 0) {
157
+ retriesLeft--;
158
+
159
+ return attemptResolve();
160
+ }
161
+
162
+ completed = true;
163
+ callback(err);
164
+ });
165
+ }
166
+ // synchronous resolver handling
167
+ else if (result !== undefined) {
168
+ if (completed) {
169
+ return;
170
+ }
171
+ clearTimeout(timeoutId);
172
+ completed = true;
173
+
174
+ return callback(null, result);
175
+ }
176
+ // assume callback-based in other cases
177
+ else {
178
+ if (completed) {
179
+ return;
180
+ }
181
+ clearTimeout(timeoutId);
182
+ completed = true;
183
+
184
+ return callback(null, undefined);
185
+ }
186
+ }
187
+ catch (err) {
188
+ if (completed) {
189
+ return;
190
+ }
191
+ clearTimeout(timeoutId);
192
+
193
+ if (retriesLeft > 0) {
194
+ retriesLeft--;
195
+
196
+ return attemptResolve();
197
+ }
198
+
199
+ completed = true;
200
+
201
+ return callback(err);
202
+ }
203
+ }
204
+
205
+ attemptResolve();
206
+ }
207
+
208
+ /**
209
+ * Scans variable scopes for secrets and resolves them using configured resolvers.
210
+ * Resolves secrets in parallel. If any resolution fails, stops and returns the error.
211
+ *
212
+ * @param {Object} payload - Request payload
213
+ * @param {Object} payload.item - The request item
214
+ * @param {Object} payload.environment - Environment scope
215
+ * @param {Object} payload.globals - Globals scope
216
+ * @param {Object} payload.collectionVariables - Collection variables scope
217
+ * @param {Object} secretResolvers - Object mapping provider names to resolver configs
218
+ * @param {Function} callback - callback(err)
219
+ */
220
+ function resolveSecrets (payload, secretResolvers, callback) {
221
+ if (!secretResolvers || typeof secretResolvers !== 'object' || _.isEmpty(secretResolvers)) {
222
+ return callback();
223
+ }
224
+
225
+ var usedVariables = findUsedVariables(payload.item),
226
+ scopesToScan = [
227
+ { name: 'environment', scope: payload.environment },
228
+ { name: 'globals', scope: payload.globals },
229
+ { name: 'collectionVariables', scope: payload.collectionVariables }
230
+ ],
231
+ variablesToResolve = [];
232
+
233
+ scopesToScan.forEach(function (scopeInfo) {
234
+ var sourceVars = extractSourceVariables(scopeInfo.scope, usedVariables);
235
+
236
+ sourceVars.forEach(function (variable) {
237
+ variablesToResolve.push({
238
+ scopeName: scopeInfo.name,
239
+ scope: scopeInfo.scope,
240
+ variable: variable
241
+ });
242
+ });
243
+ });
244
+
245
+ if (variablesToResolve.length === 0) {
246
+ return callback();
247
+ }
248
+
249
+ async.each(variablesToResolve, function (item, next) {
250
+ var variable = item.variable,
251
+ source = variable.source,
252
+ sourceProvider = source && source.provider,
253
+ secret = sourceProvider && (source[sourceProvider] || null),
254
+ resolverConfig = sourceProvider && secretResolvers[sourceProvider],
255
+ context,
256
+ errorDetails;
257
+
258
+ // No resolver for this provider or invalid source - keep placeholder value
259
+ if (!resolverConfig || typeof resolverConfig.resolver !== 'function' || !secret) {
260
+ return next();
261
+ }
262
+
263
+ context = buildResolverContext(payload, variable.key);
264
+ errorDetails = {
265
+ key: variable.key,
266
+ provider: sourceProvider,
267
+ resolverId: resolverConfig.id || sourceProvider,
268
+ resolverName: resolverConfig.name || sourceProvider
269
+ };
270
+
271
+ executeResolver(resolverConfig.resolver,
272
+ secret,
273
+ context,
274
+ {
275
+ timeout: resolverConfig.timeout || DEFAULT_TIMEOUT,
276
+ retryCount: resolverConfig.retryCount || DEFAULT_RETRY_COUNT
277
+ },
278
+ function (err, resolvedValue) {
279
+ if (err) {
280
+ var isTimeout = err.message && err.message.includes('timed out'),
281
+ errorCode = isTimeout ?
282
+ 'SECRET_RESOLUTION_TIMEOUT' : 'SECRET_RESOLUTION_FAILED',
283
+ secretError = createSecretError(errorCode,
284
+ 'Failed to resolve secret: ' + err.message,
285
+ errorDetails);
286
+
287
+ // Blocking error - pass to next() to stop processing and fail the request
288
+ return next(secretError);
289
+ }
290
+
291
+ if (!_.isNil(resolvedValue)) {
292
+ if (typeof variable.set === 'function') {
293
+ variable.set(resolvedValue);
294
+ }
295
+ else {
296
+ variable.value = resolvedValue;
297
+ }
298
+ }
299
+
300
+ return next();
301
+ });
302
+ }, function (err) {
303
+ callback(err);
304
+ });
305
+ }
306
+
307
+ module.exports = {
308
+ resolveSecrets,
309
+ extractSourceVariables,
310
+ findUsedVariables
311
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "postman-runtime",
3
- "version": "7.51.1",
3
+ "version": "7.52.0-beta.2",
4
4
  "description": "Underlying library of executing Postman Collections",
5
5
  "author": "Postman Inc.",
6
6
  "license": "Apache-2.0",
@@ -54,7 +54,7 @@
54
54
  "node-forge": "1.3.3",
55
55
  "node-oauth1": "1.3.0",
56
56
  "performance-now": "2.1.0",
57
- "postman-collection": "5.2.0",
57
+ "postman-collection": "5.3.0-beta.1",
58
58
  "postman-request": "2.88.1-postman.48",
59
59
  "postman-sandbox": "6.4.0",
60
60
  "postman-url-encoder": "3.0.8",