Skip to content

Enable external extensions #1503

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion lib/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
module.exports = {
Runner: require('./runner'),
Requester: require('./requester').Requester,
version: require('./version')
Cursor: require('./runner/cursor'),
version: require('./version'),
utils: require('./runner/util'),
Instruction: require('./runner/instruction'),
backpack: require('./backpack')
};
41 changes: 19 additions & 22 deletions lib/runner/extensions/event.command.js
Original file line number Diff line number Diff line change
Expand Up @@ -582,28 +582,25 @@ module.exports = {
result && payload.context && payload.context.response &&
(result.response = new sdk.Response(payload.context.response));

// persist the pm.variables for the next script
result && result._variables &&
(payload.context._variables = new sdk.VariableScope(result._variables));

// persist the pm.variables for the next request
result && result._variables &&
(this.state._variables = new sdk.VariableScope(result._variables));

// persist the mutated request in payload context,
// @note this will be used for the next prerequest script or
// upcoming commands(request, httprequest).
result && result.request && (payload.context.request = result.request);

// now that this script is done executing, we trigger the event and move to the next script
this.triggers.script(err || null, scriptCursor, result, script, event, item);

// move to next script and pass on the results for accumulation
done(((stopOnScriptError || abortOnError || stopOnFailure) && err) ? err : null, _.assign({
event,
script,
result
}, err && { error: err })); // we use assign here to avoid needless error property
// by persisting in an instruction, we allow extensions to define
// how exactly they want to do it.
this.immediate('persist', {
coords: cursor,
result: result,
payload: payload
}).done(function () {
// now that this script is done executing,
// we trigger the event and move to the next script
this.triggers.script(err || null, scriptCursor, result, script, event, item);

// move to next script and pass on the results for accumulation
done(((stopOnScriptError || abortOnError || stopOnFailure) && err) ? err : null,
_.assign({
event,
script,
result
}, err && { error: err })); // we use assign here to avoid needless error property
}.bind(this));
}.bind(this));
};

Expand Down
22 changes: 22 additions & 0 deletions lib/runner/extensions/persist.command.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
var sdk = require('postman-collection');

module.exports = {
process: {
persist ({ result, payload }, next) {
console.log('persist command');

Check failure on line 6 in lib/runner/extensions/persist.command.js

View workflow job for this annotation

GitHub Actions / Lint

Unexpected console statement
// persist the pm.variables for the next script
result && result._variables &&
(payload.context._variables = new sdk.VariableScope(result._variables));

// persist the pm.variables for the next request
result && result._variables &&
(this.state._variables = new sdk.VariableScope(result._variables));

// persist the mutated request in payload context,
// @note this will be used for the next prerequest script or
// upcoming commands(request, httprequest).
result && result.request && (payload.context.request = result.request);
next();
}
}
};
77 changes: 4 additions & 73 deletions lib/runner/extensions/waterfall.command.js
Original file line number Diff line number Diff line change
@@ -1,65 +1,7 @@
var _ = require('lodash'),
Cursor = require('../cursor'),
VariableScope = require('postman-collection').VariableScope,
{ prepareVaultVariableScope } = require('../util'),

prepareLookupHash,
extractSNR,
getIterationData;

/**
* Returns a hash of IDs and Names of items in an array
*
* @param {Array} items -
* @returns {Object}
*/
prepareLookupHash = function (items) {
var hash = {
ids: {},
names: {},
obj: {}
};

_.forEach(items, function (item, index) {
if (item) {
item.id && (hash.ids[item.id] = index);
item.name && (hash.names[item.name] = index);
}
});

return hash;
};

extractSNR = function (executions, previous) {
var snr = previous || {};

_.isArray(executions) && executions.forEach(function (execution) {
_.has(_.get(execution, 'result.return'), 'nextRequest') && (
(snr.defined = true),
(snr.value = execution.result.return.nextRequest)
);
});

return snr;
};

/**
* Returns the data for the given iteration
*
* @function getIterationData
* @param {Array} data - The data array containing all iterations' data
* @param {Number} iteration - The iteration to get data for
* @return {Any} - The data for the iteration
*/
getIterationData = function (data, iteration) {
// if iteration has a corresponding data element use that
if (iteration < data.length) {
return data[iteration];
}

// otherwise use the last data element
return data[data.length - 1];
};
{ prepareVaultVariableScope, prepareVariablesScope, prepareLookupHash,
extractSNR, getIterationData } = require('../util');

/**
* Adds options
Expand All @@ -71,19 +13,8 @@ module.exports = {
init: function (done) {
var state = this.state;

// ensure that the environment, globals and collectionVariables are in VariableScope instance format
state.environment = VariableScope.isVariableScope(state.environment) ? state.environment :
new VariableScope(state.environment);
state.globals = VariableScope.isVariableScope(state.globals) ? state.globals :
new VariableScope(state.globals);
state.vaultSecrets = VariableScope.isVariableScope(state.vaultSecrets) ? state.vaultSecrets :
new VariableScope(state.vaultSecrets);
state.collectionVariables = VariableScope.isVariableScope(state.collectionVariables) ?
state.collectionVariables : new VariableScope(state.collectionVariables);
state._variables = VariableScope.isVariableScope(state.localVariables) ?
state.localVariables : new VariableScope(state.localVariables);

// prepare the vault variable scope
// prepare the vault variable scope and other variables
prepareVariablesScope(state);
prepareVaultVariableScope(state.vaultSecrets);

// ensure that the items and iteration data set is in place
Expand Down
9 changes: 9 additions & 0 deletions lib/runner/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ var _ = require('lodash'),
script: Infinity
};


function applyRunExtensions (extensions) {
if (extensions && !_.isEmpty(extensions)) {
Run.applyExtensions(extensions);
}
}

/**
* @typedef {runCallback}
* @property {Function} [done]
Expand All @@ -25,6 +32,7 @@ var _ = require('lodash'),
*/
Runner = function PostmanCollectionRunner (options) { // eslint-disable-line func-name-matching
this.options = _.assign({}, options);
applyRunExtensions(this.options.extensions);
};

_.assign(Runner.prototype, {
Expand Down Expand Up @@ -82,6 +90,7 @@ _.assign(Runner.prototype, {
var self = this,
runOptions = this.prepareRunConfig(options);

applyRunExtensions(this.options.extensions);
callback = backpack.normalise(callback);
!_.isObject(options) && (options = {});

Expand Down
36 changes: 31 additions & 5 deletions lib/runner/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ _.assign(Run.prototype, {
* @param {Function|Object} callback -
*/
start (callback) {
this.commands = Run.commands;
// @todo add `when` parameter to backpack.normalise
callback = backpack.normalise(callback, Object.keys(Run.triggers));

Expand All @@ -111,7 +112,7 @@ _.assign(Run.prototype, {
}

// invoke all the initialiser functions one after another and if it has any error then abort with callback.
async.series(_.map(Run.initialisers, function (initializer) {
async.series(_.map(Array.from(Run.initialisers.values()), function (initializer) {
return initializer.bind(this);
}.bind(this)), function (err) {
if (err) { return callback(err); }
Expand Down Expand Up @@ -195,7 +196,7 @@ _.assign(Run, {
*
* @type {Array}
*/
initialisers: []
initialisers: new Map()
});

// commands are loaded by flattening the modules in the `./commands` directory
Expand All @@ -206,8 +207,9 @@ Run.commands = _.transform({
'request.command': require('./extensions/request.command'),
'waterfall.command': require('./extensions/waterfall.command'),
'item.command': require('./extensions/item.command'),
'delay.command': require('./extensions/delay.command')
}, function (all, extension) {
'delay.command': require('./extensions/delay.command'),
'persist.command': require('./extensions/persist.command')
}, function (all, extension, extensionName) {
// extract the prototype from the command interface
if (_.has(extension, 'prototype')) {
_.forOwn(extension.prototype, function (value, prop) {
Expand Down Expand Up @@ -238,7 +240,31 @@ Run.commands = _.transform({
}

// add the initialisation functions
_.has(extension, 'init') && _.isFunction(extension.init) && Run.initialisers.push(extension.init);
_.has(extension, 'init') && _.isFunction(extension.init) && Run.initialisers.set(extensionName, extension.init);
});

Run.applyExtensions = function (extensions) {
_.forEach(extensions, (extension, extensionName) => {
if (_.has(extension, 'prototype')) {
_.forOwn(extension.prototype, (value, prop) => {
Run.prototype[prop] = value;
});
}
if (_.has(extension, 'triggers') && _.isArray(extension.triggers)) {
_.forEach(extension.triggers, function (name) {
name && (Run.triggers[name] = true);
});
}
if (_.has(extension, 'process')) {
_.forOwn(extension.process, function (command, name) {
if (!_.isFunction(command)) { return; }
Run.commands[name] = command;
});
}
if (_.has(extension, 'init') && _.isFunction(extension.init)) {
Run.initialisers.set(extensionName, extension.init);
}
});
};

module.exports = Run;
81 changes: 81 additions & 0 deletions lib/runner/util.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
var { Url, UrlMatchPatternList, VariableList } = require('postman-collection'),
VariableScope = require('postman-collection').VariableScope,
sdk = require('postman-collection'),
_ = require('lodash'),

Expand Down Expand Up @@ -238,6 +239,86 @@ module.exports = {
scope.__vaultVariableScope = true;
},

/**
* ensure that the environment, globals and collectionVariables are in VariableScope instance format
* @param {*} state application state object.
*/
prepareVariablesScope (state) {
state.environment = VariableScope.isVariableScope(state.environment) ? state.environment :
new VariableScope(state.environment);
state.globals = VariableScope.isVariableScope(state.globals) ? state.globals :
new VariableScope(state.globals);
state.vaultSecrets = VariableScope.isVariableScope(state.vaultSecrets) ? state.vaultSecrets :
new VariableScope(state.vaultSecrets);
state.collectionVariables = VariableScope.isVariableScope(state.collectionVariables) ?
state.collectionVariables : new VariableScope(state.collectionVariables);
state._variables = VariableScope.isVariableScope(state.localVariables) ?
state.localVariables : new VariableScope(state.localVariables);
},

/**
* Returns a hash of IDs and Names of items in an array
*
* @param {Array} items -
* @returns {Object}
*/
prepareLookupHash (items) {
var hash = {
ids: {},
names: {},
obj: {}
};

_.forEach(items, function (item, index) {
if (item) {
item.id && (hash.ids[item.id] = index);
item.name && (hash.names[item.name] = index);
}
});

return hash;
},


/**
* Extract set next request from the execution.
*
* @function getIterationData
* @param {Array} executions - The prerequests or the tests of an item's execution.
* @param {Object} previous - If extracting the tests request then prerequest's snr.
* @return {Any} - The Set Next Request
*/
extractSNR (executions, previous) {
var snr = previous || {};

_.isArray(executions) && executions.forEach(function (execution) {
_.has(_.get(execution, 'result.return'), 'nextRequest') && (
(snr.defined = true),
(snr.value = execution.result.return.nextRequest)
);
});

return snr;
},

/**
* Returns the data for the given iteration
*
* @function getIterationData
* @param {Array} data - The data array containing all iterations' data
* @param {Number} iteration - The iteration to get data for
* @return {Any} - The data for the iteration
*/
getIterationData (data, iteration) {
// if iteration has a corresponding data element use that
if (iteration < data.length) {
return data[iteration];
}

// otherwise use the last data element
return data[data.length - 1];
},

/**
* Resolve variables in item and auth in context.
*
Expand Down
Loading