postman-runtime 7.46.1 → 7.47.1

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.
@@ -8,15 +8,21 @@ var _ = require('lodash'),
8
8
  * @param {Number} [position=0] -
9
9
  * @param {Number} [iteration=0] -
10
10
  * @param {String} [ref] -
11
+ * @param {Number} [partitionIndex=0] -
12
+ * @param {Number} [partitionCycles=0] -
11
13
  * @constructor
12
14
  */
13
- Cursor = function RunCursor (length, cycles, position, iteration, ref) { // eslint-disable-line func-name-matching
15
+ Cursor = function RunCursor (length, cycles, position, iteration, // eslint-disable-line func-name-matching
16
+ ref, partitionIndex, partitionCycles) {
14
17
  this.length = Cursor.validate(length, 0);
15
18
  this.position = Cursor.validate(position, 0, this.length);
16
19
 
17
20
  this.cycles = Cursor.validate(cycles, 1, 1);
18
21
  this.iteration = Cursor.validate(iteration, 0, this.cycles);
19
22
 
23
+ this.partitionIndex = Cursor.validate(partitionIndex, 0, this.cycles);
24
+ this.partitionCycles = Cursor.validate(partitionCycles, 0, this.cycles);
25
+
20
26
  this.ref = ref || uuid.v4();
21
27
  };
22
28
 
@@ -202,7 +208,9 @@ _.assign(Cursor.prototype, {
202
208
  var base = {
203
209
  ref: this.ref,
204
210
  length: this.length,
205
- cycles: this.cycles
211
+ cycles: this.cycles,
212
+ partitionIndex: this.partitionIndex,
213
+ partitionCycles: this.partitionCycles
206
214
  },
207
215
  position,
208
216
  iteration;
@@ -265,6 +273,8 @@ _.assign(Cursor.prototype, {
265
273
  iteration: this.iteration,
266
274
  length: this.length,
267
275
  cycles: this.cycles,
276
+ partitionIndex: this.partitionIndex,
277
+ partitionCycles: this.partitionCycles,
268
278
  empty: this.empty(),
269
279
  eof: this.eof(),
270
280
  bof: this.bof(),
@@ -349,7 +359,8 @@ _.assign(Cursor, {
349
359
  if (!_.isObject(obj)) { return new Cursor(bounds && bounds.length, bounds && bounds.cycles); }
350
360
 
351
361
  // load Cursor values from object
352
- return new Cursor((bounds || obj).length, (bounds || obj).cycles, obj.position, obj.iteration, obj.ref);
362
+ return new Cursor((bounds || obj).length, (bounds || obj).cycles, obj.position,
363
+ obj.iteration, obj.ref, obj.partitionIndex, obj.partitionCycles);
353
364
  },
354
365
 
355
366
  /**
@@ -94,11 +94,20 @@ module.exports = {
94
94
  abort (userback, payload, next) {
95
95
  // clear instruction pool and as such there will be nothing next to execute
96
96
  this.pool.clear();
97
- this.triggers.abort(null, this.state.cursor.current());
98
97
 
99
- // execute the userback sent as part of the command and do so in a try block to ensure it does not hamper
100
- // the process tick
101
- backpack.ensure(userback, this) && userback();
98
+ // clear all partition pools, if exist
99
+ this.partitionManager.dispose();
100
+
101
+ if (!this.aborted) {
102
+ this.aborted = true;
103
+
104
+ // always trigger abort event here to ensure it's called even if host has been disposed
105
+ this.triggers.abort(null, this.state.cursor.current());
106
+
107
+ // execute the userback sent as part of the command and
108
+ // do so in a try block to ensure it does not hamper the process tick
109
+ backpack.ensure(userback, this) && userback();
110
+ }
102
111
 
103
112
  next(null);
104
113
  }
@@ -621,6 +621,11 @@ module.exports = {
621
621
  result && result._variables &&
622
622
  (this.state._variables = new sdk.VariableScope(result._variables));
623
623
 
624
+ if (this.areIterationsParallelized) {
625
+ // persist the pm.variables for the next request in the current partition
626
+ this.partitionManager.updatePartitionVariables(payload.coords.partitionIndex, result);
627
+ }
628
+
624
629
  // persist the mutated request in payload context,
625
630
  // @note this will be used for the next prerequest script or
626
631
  // upcoming commands(request, httprequest).
@@ -0,0 +1,178 @@
1
+ var _ = require('lodash'),
2
+ { prepareVaultVariableScope, prepareVariablesScope,
3
+ getIterationData, processExecutionResult
4
+ } = require('../util');
5
+
6
+ /**
7
+ * Adds options
8
+ * disableSNR:Boolean
9
+ *
10
+ * @type {Object}
11
+ */
12
+ module.exports = {
13
+ init: function (done) {
14
+ // bail out if iterations are not parallelized
15
+ if (!this.areIterationsParallelized) {
16
+ return done();
17
+ }
18
+
19
+ var state = this.state;
20
+
21
+ // ensure that the environment, globals and collectionVariables are in VariableScope instance format
22
+ prepareVariablesScope(state);
23
+ // prepare the vault variable scope
24
+ prepareVaultVariableScope(state.vaultSecrets);
25
+
26
+ // create the partition manager and partition the iterations
27
+ this.partitionManager.createPartitions();
28
+ const { partitions } = this.partitionManager;
29
+
30
+ // queue a parallel command for each of our partitions
31
+ partitions.forEach((partition) => {
32
+ this.queue('parallel', {
33
+ coords: partition.cursor.current(),
34
+ static: true,
35
+ start: true
36
+ });
37
+ });
38
+
39
+ done();
40
+ },
41
+
42
+ triggers: ['beforeIteration', 'iteration'],
43
+
44
+ prototype: {
45
+ /**
46
+ * Starts a parallel iteration
47
+ *
48
+ * @param {Number} index - The index of the partition to run
49
+ * @param {Object} localVariables - Local variables for the iteration
50
+ * @param {Function} callback - The callback to call when the iteration is complete
51
+ */
52
+ startParallelIteration (index, localVariables, callback) {
53
+ this.partitionManager.runSinglePartition(index, localVariables, callback);
54
+ },
55
+
56
+ /**
57
+ * Stops a parallel iteration
58
+ *
59
+ * @param {Number} index - The index of the partition to stop
60
+ * @param {Function} callback - The callback to call when the iteration is complete
61
+ */
62
+ stopParallelIteration (index, callback) {
63
+ this.partitionManager.stopSinglePartition(index, callback);
64
+ }
65
+ },
66
+
67
+ process: {
68
+ /**
69
+ * This processor queues partitions in parallel.
70
+ *
71
+ * @param {Object} payload -
72
+ * @param {Object} payload.coords -
73
+ * @param {Boolean} [payload.static=false] -
74
+ * @param {Function} next -
75
+ */
76
+ parallel (payload, next) {
77
+ var partitionIndex = payload.coords.partitionIndex,
78
+ partition = this.partitionManager.partitions[partitionIndex],
79
+ coords = payload.static ? payload.coords : partition.cursor.whatnext(payload.coords),
80
+ item = this.state.items[coords.position],
81
+ delay;
82
+
83
+
84
+ if (coords.empty) {
85
+ return next();
86
+ }
87
+
88
+ if (payload.stopRunNow) {
89
+ this.triggers.iteration(null, coords);
90
+
91
+ return next();
92
+ }
93
+
94
+ // if it is a beginning of a run, we need to raise events for iteration start
95
+ if (payload.start) {
96
+ this.triggers.beforeIteration(null, coords);
97
+ }
98
+
99
+ // since we will never reach coords.eof for some partitions because each cursor
100
+ // contains cycles for the entire run, we are breaking off early here.
101
+ // this has been done to keep the contract of a cursor intact.
102
+ // cycles is defined as "number of iterations in the run"
103
+ if (coords.iteration === partition.startIndex + coords.partitionCycles) {
104
+ this.triggers.iteration(null, payload.coords);
105
+
106
+ return next();
107
+ }
108
+
109
+ if (coords.cr) {
110
+ delay = _.get(this.options, 'delay.iteration', 0);
111
+
112
+ this.triggers.iteration(null, payload.coords);
113
+ this.triggers.beforeIteration(null, coords);
114
+ }
115
+
116
+
117
+ if (coords.eof) {
118
+ this.triggers.iteration(null, coords);
119
+
120
+ return next();
121
+ }
122
+
123
+ this.queueDelay(function () {
124
+ this.queue('item', {
125
+ item: item,
126
+ coords: coords,
127
+ data: getIterationData(this.state.data, coords.iteration + partition.startIndex),
128
+ environment: partition.variables.environment,
129
+ globals: partition.variables.globals,
130
+ vaultSecrets: this.state.vaultSecrets,
131
+ collectionVariables: partition.variables.collectionVariables,
132
+ _variables: partition.variables._variables
133
+ }, function (executionError, executions) {
134
+ // Use shared utility function to process execution results and handle SNR logic
135
+ var result = processExecutionResult({
136
+ coords: coords,
137
+ executions: executions,
138
+ executionError: executionError,
139
+ runnerOptions: this.options,
140
+ snrHash: this.snrHash,
141
+ items: this.state.items
142
+ }),
143
+ nextCoords,
144
+ seekingToStart,
145
+ stopRunNow;
146
+
147
+ // Update the snrHash if it was created/updated by the utility function
148
+ this.snrHash = result.snrHash;
149
+
150
+ nextCoords = result.nextCoords;
151
+ seekingToStart = result.seekingToStart;
152
+ stopRunNow = result.stopRunNow;
153
+
154
+
155
+ partition.cursor.seek(nextCoords.position, nextCoords.iteration, function (err, chngd, coords) {
156
+ // this condition should never arise, so better throw error when this happens
157
+ if (err) {
158
+ throw err;
159
+ }
160
+
161
+ this.queue('parallel', {
162
+ coords: {
163
+ ...coords,
164
+ partitionIndex
165
+ },
166
+ static: seekingToStart,
167
+ stopRunNow: stopRunNow
168
+ });
169
+ }, this);
170
+ });
171
+ }.bind(this), {
172
+ time: delay,
173
+ source: 'iteration',
174
+ cursor: coords
175
+ }, next);
176
+ }
177
+ }
178
+ };
@@ -1,65 +1,11 @@
1
1
  var _ = require('lodash'),
2
2
  Cursor = require('../cursor'),
3
- VariableScope = require('postman-collection').VariableScope,
4
- { prepareVaultVariableScope } = require('../util'),
5
-
6
- prepareLookupHash,
7
- extractSNR,
8
- getIterationData;
9
-
10
- /**
11
- * Returns a hash of IDs and Names of items in an array
12
- *
13
- * @param {Array} items -
14
- * @returns {Object}
15
- */
16
- prepareLookupHash = function (items) {
17
- var hash = {
18
- ids: {},
19
- names: {},
20
- obj: {}
21
- };
22
-
23
- _.forEach(items, function (item, index) {
24
- if (item) {
25
- item.id && (hash.ids[item.id] = index);
26
- item.name && (hash.names[item.name] = index);
27
- }
28
- });
29
-
30
- return hash;
31
- };
32
-
33
- extractSNR = function (executions, previous) {
34
- var snr = previous || {};
35
-
36
- _.isArray(executions) && executions.forEach(function (execution) {
37
- _.has(_.get(execution, 'result.return'), 'nextRequest') && (
38
- (snr.defined = true),
39
- (snr.value = execution.result.return.nextRequest)
40
- );
41
- });
42
-
43
- return snr;
44
- };
45
-
46
- /**
47
- * Returns the data for the given iteration
48
- *
49
- * @function getIterationData
50
- * @param {Array} data - The data array containing all iterations' data
51
- * @param {Number} iteration - The iteration to get data for
52
- * @return {Any} - The data for the iteration
53
- */
54
- getIterationData = function (data, iteration) {
55
- // if iteration has a corresponding data element use that
56
- if (iteration < data.length) {
57
- return data[iteration];
58
- }
59
-
60
- // otherwise use the last data element
61
- return data[data.length - 1];
62
- };
3
+ {
4
+ getIterationData,
5
+ prepareVariablesScope,
6
+ processExecutionResult,
7
+ prepareVaultVariableScope
8
+ } = require('../util');
63
9
 
64
10
  /**
65
11
  * Adds options
@@ -71,19 +17,8 @@ module.exports = {
71
17
  init: function (done) {
72
18
  var state = this.state;
73
19
 
74
- // ensure that the environment, globals and collectionVariables are in VariableScope instance format
75
- state.environment = VariableScope.isVariableScope(state.environment) ? state.environment :
76
- new VariableScope(state.environment);
77
- state.globals = VariableScope.isVariableScope(state.globals) ? state.globals :
78
- new VariableScope(state.globals);
79
- state.vaultSecrets = VariableScope.isVariableScope(state.vaultSecrets) ? state.vaultSecrets :
80
- new VariableScope(state.vaultSecrets);
81
- state.collectionVariables = VariableScope.isVariableScope(state.collectionVariables) ?
82
- state.collectionVariables : new VariableScope(state.collectionVariables);
83
- state._variables = VariableScope.isVariableScope(state.localVariables) ?
84
- state.localVariables : new VariableScope(state.localVariables);
85
-
86
- // prepare the vault variable scope
20
+ // prepare the vault variable scope and other variables
21
+ prepareVariablesScope(state);
87
22
  prepareVaultVariableScope(state.vaultSecrets);
88
23
 
89
24
  // ensure that the items and iteration data set is in place
@@ -99,12 +34,14 @@ module.exports = {
99
34
  });
100
35
  this.waterfall = state.cursor; // copy the location object to instance for quick access
101
36
 
102
- // queue the iteration command on start
103
- this.queue('waterfall', {
104
- coords: this.waterfall.current(),
105
- static: true,
106
- start: true
107
- });
37
+ // queue the waterfall command if iterations are not parallelized
38
+ if (!this.areIterationsParallelized) {
39
+ this.queue('waterfall', {
40
+ coords: this.waterfall.current(),
41
+ static: true,
42
+ start: true
43
+ });
44
+ }
108
45
 
109
46
  // clear the variable that is supposed to store item name and id lookup hash for easy setNextRequest
110
47
  this.snrHash = null; // we populate it in the first SNR call
@@ -175,57 +112,25 @@ module.exports = {
175
112
  collectionVariables: this.state.collectionVariables,
176
113
  _variables: this.state._variables
177
114
  }, function (executionError, executions) {
178
- var snr = {},
115
+ // Use shared utility function to process execution results and handle SNR logic
116
+ var result = processExecutionResult({
117
+ coords: coords,
118
+ executions: executions,
119
+ executionError: executionError,
120
+ runnerOptions: this.options,
121
+ snrHash: this.snrHash,
122
+ items: this.state.items
123
+ }),
179
124
  nextCoords,
180
125
  seekingToStart,
181
- stopRunNow,
182
-
183
- stopOnFailure = this.options.stopOnFailure;
184
-
185
- if (!executionError) {
186
- // extract set next request
187
- snr = extractSNR(executions.prerequest);
188
- snr = extractSNR(executions.test, snr);
189
- }
190
-
191
- if (!this.options.disableSNR && snr.defined) {
192
- // prepare the snr lookup hash if it is not already provided
193
- // @todo - figure out a way to reset this post run complete
194
- !this.snrHash && (this.snrHash = prepareLookupHash(this.state.items));
195
-
196
- // if it is null, we do not proceed further and move on
197
- // see if a request is found in the hash and then reset the coords position to the lookup
198
- // value.
199
- (snr.value !== null) && (snr.position = // eslint-disable-next-line no-nested-ternary
200
- this.snrHash[_.has(this.snrHash.ids, snr.value) ? 'ids' :
201
- (_.has(this.snrHash.names, snr.value) ? 'names' : 'obj')][snr.value]);
202
-
203
- snr.valid = _.isNumber(snr.position);
204
- }
205
-
206
- nextCoords = _.clone(coords);
207
-
208
- if (snr.valid) {
209
- // if the position was detected, we set the position to the one previous to the desired location
210
- // this ensures that the next call to .whatnext() will return the desired position.
211
- nextCoords.position = snr.position - 1;
212
- }
213
- else {
214
- // if snr was requested, but not valid, we stop this iteration.
215
- // stopping an iteration is equivalent to seeking the last position of the current
216
- // iteration, so that the next call to .whatnext() will automatically move to the next
217
- // iteration.
218
- (snr.defined || executionError) && (nextCoords.position = nextCoords.length - 1);
219
-
220
- // If we need to stop on a run, we set the stop flag to true.
221
- (stopOnFailure && executionError) && (stopRunNow = true);
222
- }
223
-
224
- // @todo - do this in unhacky way
225
- if (nextCoords.position === -1) {
226
- nextCoords.position = 0;
227
- seekingToStart = true;
228
- }
126
+ stopRunNow;
127
+
128
+ // Update the snrHash if it was created/updated by the utility function
129
+ this.snrHash = result.snrHash;
130
+
131
+ nextCoords = result.nextCoords;
132
+ seekingToStart = result.seekingToStart;
133
+ stopRunNow = result.stopRunNow;
229
134
 
230
135
  this.waterfall.seek(nextCoords.position, nextCoords.iteration, function (err, chngd, coords) {
231
136
  // this condition should never arise, so better throw error when this happens