Skip to content

Commit

Permalink
feat(sandbox): implement a wrapper on dispatch to check if should ski…
Browse files Browse the repository at this point in the history
…p execution
  • Loading branch information
vedkribhu committed Oct 10, 2023
1 parent 5e890c2 commit f0a93dc
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 22 deletions.
5 changes: 3 additions & 2 deletions lib/sandbox/console.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function replacer (key, value) {
return value;
}

function PostmanConsole (emitter, cursor, originalConsole) {
function PostmanConsole (emitter, cursor, originalConsole, execution) {
const dispatch = function (level) { // create a dispatch function that emits events
const args = arrayProtoSlice.call(arguments, 1);

Expand All @@ -54,7 +54,8 @@ function PostmanConsole (emitter, cursor, originalConsole) {
originalConsole[level].apply(originalConsole, args);
}

emitter.dispatch(CONSOLE_EVENT, cursor, level, teleportJS.stringify(args, replacer));

emitter.dispatch(execution, CONSOLE_EVENT, cursor, level, teleportJS.stringify(args, replacer));
};

// setup variants of the logger based on log levels
Expand Down
5 changes: 3 additions & 2 deletions lib/sandbox/cookie-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ const _ = require('lodash'),
arrayProtoSlice = Array.prototype.slice;

class PostmanCookieStore extends Store {
constructor (id, emitter, timers) {
constructor (id, emitter, timers, execution) {
super();

this.id = id; // execution identifier
this.emitter = emitter;
this.timers = timers;
this.execution = execution;
}
}

Expand Down Expand Up @@ -77,7 +78,7 @@ STORE_METHODS.forEach(function (method) {
// Refer: https://github.com/postmanlabs/postman-app-support/issues/11064
setTimeout(() => {
// finally, dispatch event over the bridge
this.emitter.dispatch(eventName, eventId, EVENT_STORE_ACTION, method, args);
this.emitter.dispatch(this.execution, eventName, eventId, EVENT_STORE_ACTION, method, args);
});
};
});
Expand Down
66 changes: 49 additions & 17 deletions lib/sandbox/execute.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,28 @@ module.exports = function (bridge, glob) {
// @note we use a common scope for all executions. this causes issues when scripts are run inside the sandbox
// in parallel, but we still use this way for the legacy "persistent" behaviour needed in environment
const scope = Scope.create({
eval: true,
ignore: ['require'],
block: ['bridge']
});
eval: true,
ignore: ['require'],
block: ['bridge']
}),
originalBridgeDispatch = bridge.dispatch;

/**
* Why Do we need this wrapper?
* Because when user executes pm.execution.skipRequest(), we need to stop the execution of the current request.
* But, we don't stop the execution of the script. We just stop sending events to the bridge.
*
* @param {Execution} execution
* @param {any} args
* @returns
*/
bridge.dispatch = function (execution, ...args) {
if (execution && execution.shouldSkipExecution) {
return;
}

return originalBridgeDispatch.call(bridge, ...args);
};

// For caching required information provided during
// initialization which will be used during execution
Expand All @@ -49,7 +67,7 @@ module.exports = function (bridge, glob) {
if (!template) {
chai.use(require('chai-postman')(sdk, _, Ajv));

return bridge.dispatch('initialize');
return bridge.dispatch(null, 'initialize');
}

const _module = { exports: {} },
Expand All @@ -66,7 +84,7 @@ module.exports = function (bridge, glob) {

scope.exec(template, (err) => {
if (err) {
return bridge.dispatch('initialize', err);
return bridge.dispatch(null, 'initialize', err);
}

const { chaiPlugin, initializeExecution: setupExecution } = (_module && _module.exports) || {};
Expand All @@ -79,7 +97,7 @@ module.exports = function (bridge, glob) {
initializeExecution = setupExecution;
}

bridge.dispatch('initialize');
bridge.dispatch(null, 'initialize');
});
});

Expand All @@ -97,7 +115,8 @@ module.exports = function (bridge, glob) {
*/
bridge.on('execute', function (id, event, context, options) {
if (!(id && _.isString(id))) {
return bridge.dispatch('error', new Error('sandbox: execution identifier parameter(s) missing'));
return bridge.dispatch(null, 'error',
new Error('sandbox: execution identifier parameter(s) missing'));
}

!options && (options = {});
Expand Down Expand Up @@ -136,8 +155,8 @@ module.exports = function (bridge, glob) {
// For compatibility, dispatch the single assertion as an array.
!Array.isArray(assertions) && (assertions = [assertions]);

bridge.dispatch(assertionEventName, options.cursor, assertions);
bridge.dispatch(EXECUTION_ASSERTION_EVENT, options.cursor, assertions);
bridge.dispatch(execution, assertionEventName, options.cursor, assertions);
bridge.dispatch(execution, EXECUTION_ASSERTION_EVENT, options.cursor, assertions);
};

let waiting,
Expand All @@ -148,8 +167,8 @@ module.exports = function (bridge, glob) {
// create the controlled timers
timers = new PostmanTimers(null, function (err) {
if (err) { // propagate the error out of sandbox
bridge.dispatch(errorEventName, options.cursor, err);
bridge.dispatch(EXECUTION_ERROR_EVENT, options.cursor, err);
bridge.dispatch(execution, errorEventName, options.cursor, err);
bridge.dispatch(execution, EXECUTION_ERROR_EVENT, options.cursor, err);
}
}, function () {
execution.return.async = true;
Expand All @@ -169,16 +188,22 @@ module.exports = function (bridge, glob) {
bridge.off(cookiesEventName);

if (err) { // fire extra execution error event
bridge.dispatch(errorEventName, options.cursor, err);
bridge.dispatch(EXECUTION_ERROR_EVENT, options.cursor, err);
bridge.dispatch(execution, errorEventName, options.cursor, err);
bridge.dispatch(execution, EXECUTION_ERROR_EVENT, options.cursor, err);
}

// @note delete response from the execution object to avoid dispatching
// the large response payload back due to performance reasons.
execution.response && (delete execution.response);

// fire the execution completion event
(dnd !== true) && bridge.dispatch(executionEventName, err || null, execution);

// note: We are sending shouldSkipExecution: false to dispatchEvent function
// because this event should be fired even if shouldSkipExecution is true as this event is
// used to complete the execution in the sandbox. All other events are fired only if
// shouldSkipExecution is false.
(dnd !== true) && bridge.dispatch({ shouldSkipExecution: false },
executionEventName, err || null, execution);
});

// if a timeout is set, we must ensure that all pending timers are cleared and an execution timeout event is
Expand Down Expand Up @@ -213,8 +238,15 @@ module.exports = function (bridge, glob) {
new PostmanAPI(execution, function (request, callback) {
var eventId = timers.setEvent(callback);

bridge.dispatch(executionRequestEventName, options.cursor, id, eventId, request);
}, dispatchAssertions, new PostmanCookieStore(id, bridge, timers), {
bridge.dispatch(execution, executionRequestEventName, options.cursor, id, eventId, request);
}, /* onStopExecution = */ function () {
// Dispatch event to display system message on console informing user that the request
// execution was skipped from script
dispatchSystemMessage(postmanConsole, 'skip_request', _.get(execution, 'legacy._itemName'));
execution.shouldSkipExecution = true;
timers.terminate(null);
},
dispatchAssertions, new PostmanCookieStore(id, bridge, timers, execution), {
disabledAPIs: initializationOptions.disabledAPIs
})
),
Expand Down
2 changes: 1 addition & 1 deletion lib/sandbox/ping.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module.exports = {
listener (pong) {
return function (payload) {
this.dispatch(pong, payload);
this.dispatch(null, pong, payload);
};
}
};
Expand Down

0 comments on commit f0a93dc

Please sign in to comment.