Simon Priet e69a613a37 feat: Created a mini nodeJS server with NewMan for testing without PostMan GUI.
This will mimic a run in a CD/CI environment or docker container.
2021-09-08 14:01:19 +02:00

212 lines
9.6 KiB
JavaScript

var _ = require('lodash'),
async = require('async'),
uuid = require('uuid'),
// These are functions which a request passes through _before_ being sent. They take care of stuff such as
// variable resolution, loading of files, etc.
prehelpers = require('../request-helpers-presend'),
// Similarly, these run after the request, and have the power to dictate whether a request should be re-queued
posthelpers = require('../request-helpers-postsend'),
ReplayController = require('../replay-controller'),
RequesterPool = require('../../requester').RequesterPool,
RESPONSE_START_EVENT_BASE = 'response.start.',
RESPONSE_END_EVENT_BASE = 'response.end.';
module.exports = {
init: function (done) {
// Request timeouts are applied by the requester, so add them to requester options (if any).
// create a requester pool
this.requester = new RequesterPool(this.options, done);
},
// the http trigger is actually directly triggered by the requester
// todo - figure out whether we should trigger it from here rather than the requester.
triggers: ['beforeRequest', 'request', 'responseStart', 'io'],
process: {
/**
* @param {Object} payload
* @param {Item} payload.item
* @param {Object} payload.data
* @param {Object} payload.context
* @param {VariableScope} payload.globals
* @param {VariableScope} payload.environment
* @param {Cursor} payload.coords
* @param {Boolean} payload.abortOnError
* @param {String} payload.source
* @param {Function} next
*
* @todo validate payload
*/
httprequest: function (payload, next) {
var abortOnError = _.has(payload, 'abortOnError') ? payload.abortOnError : this.options.abortOnError,
self = this,
context;
context = payload.context;
// generates a unique id for each http request
// a collection request can have multiple http requests
_.set(context, 'coords.httpRequestId', payload.httpRequestId || uuid());
// Run the helper functions
async.applyEachSeries(prehelpers, context, self, function (err) {
var xhr,
aborted,
item = context.item,
beforeRequest,
afterRequest,
safeNext;
// finish up current command
safeNext = function (error, finalPayload) {
// the error is passed twice to allow control between aborting the error vs just
// bubbling it up
return next((error && abortOnError) ? error : null, finalPayload, error);
};
// Helper function which calls the beforeRequest trigger ()
beforeRequest = function (err) {
self.triggers.beforeRequest(err, context.coords, item.request, payload.item, {
httpRequestId: context.coords && context.coords.httpRequestId,
abort: function () {
!aborted && xhr && xhr.abort();
aborted = true;
}
});
};
// Helper function to call the afterRequest trigger.
afterRequest = function (err, response, request, cookies, history) {
self.triggers.request(err, context.coords, response, request, payload.item, cookies, history);
};
// Ensure that this is called.
beforeRequest(null);
if (err) {
// Since we encountered an error before even attempting to send the request, we bubble it up
// here.
afterRequest(err, undefined, item.request);
return safeNext(
err,
{request: item.request, coords: context.coords, item: context.originalItem}
);
}
if (aborted) {
return next(new Error('runtime: request aborted'));
}
self.requester.create({
type: 'http',
source: payload.source,
cursor: context.coords
}, function (err, requester) {
if (err) { return next(err); } // this should never happen
var requestId = uuid(),
replayOptions;
// eslint-disable-next-line max-len
requester.on(RESPONSE_START_EVENT_BASE + requestId, function (err, response, request, cookies, history) {
// we could have also added the response to the set of responses in the cloned item,
// but then, we would have to iterate over all of them, which seems unnecessary
context.response = response;
// run the post request helpers, which need to use the response, assigned above
async.applyEachSeries(posthelpers, context, self, function (error, options) {
if (error) {
return;
}
// find the first helper that requested a replay
replayOptions = _.find(options, {replay: true});
// bail out if we know that request will be replayed.
if (replayOptions) {
return;
}
// bail out if its a pm.sendRequest
// @todo find a better way of identifying scripts
// @note don't use source='script'. Script requests
// can trigger `*.auth` source requests as well.
if (context.coords && context.coords.scriptId) {
return;
}
// trigger responseStart only for collection request.
// if there are replays, this will be triggered for the last request in the replay chain.
self.triggers.responseStart(err, context.coords, response, request, payload.item, cookies,
history);
});
});
requester.on(RESPONSE_END_EVENT_BASE + requestId, self.triggers.io.bind(self.triggers));
// eslint-disable-next-line max-len
xhr = requester.request(requestId, item.request, context.protocolProfileBehavior, function (err, res, req, cookies, history) {
err = err || null;
var nextPayload = {
response: res,
request: req,
item: context.originalItem,
cookies: cookies,
coords: context.coords,
history: history
},
replayController;
// trigger the request event.
// @note - we give the _original_ item in this trigger, so someone can do reference
// checking. Not sure if we should do that or not, but that's how it is.
// Don't break it.
afterRequest(err, res, req, cookies, history);
// Dispose off the requester, we don't need it anymore.
requester.dispose();
// do not process replays if there was an error
if (err) {
return safeNext(err, nextPayload);
}
// request replay logic
if (replayOptions) {
// prepare for replay
replayController = new ReplayController(context.replayState, self);
// replay controller invokes callback no. 1 when replaying the request
// invokes callback no. 2 when replay count has exceeded maximum limit
// @note: errors in replayed requests are passed to callback no. 1
return replayController.requestReplay(context,
context.item,
{source: replayOptions.helper},
// new payload with response from replay is sent to `next`
function (err, payloadFromReplay) { safeNext(err, payloadFromReplay); },
// replay was stopped, move on with older payload
function (err) {
// warn users that maximum retries have exceeded
// but don't bubble up the error with the request
self.triggers.console(context.coords, 'warn', (err.message || err));
safeNext(null, nextPayload);
}
);
}
// finish up for any other request
return safeNext(err, nextPayload);
});
});
});
}
}
};