UNPKG

postman-sandbox

Version:

Sandbox for Postman Scripts to run in Node.js or browser

205 lines (174 loc) 5.98 kB
const _ = require('lodash'), PostmanSandbox = require('./postman-sandbox'); /** * @typedef {Object} SDK * * @property {Class} request * @property {Class} response */ /** * Returns the SDK classes for `request` and `response` * * @typedef {Function} initializeExecution * * @param {String} - Execution [event's]{@link http://www.postmanlabs.com/postman-collection/Event} `listen` property * @param {Object} - Current execution context * @return {SDK} - Request and response instances based on context. */ /** * ChaiJS plugin function to be passed as an argument to `chai.use` * * @see {@link https://www.chaijs.com/guide/plugins/} for details * * @typedef {Function} chaiJSPlugin */ /** * Template to govern the behavior of individual * sandbox instances in the fleet. It should be a * valid node.js code which on execution should * return two function using `module.exports`: * - {@link initializeExecution} * - {@link chaiJSPlugin} * * @typedef {String} template */ /** * Registry of templates. * Each template should be mapped to unique name * which then could be used as identifier to retrieve * the sandbox instance initialized by that template. * * @typedef {Object.<String, template>} templateRegistry */ /** * Options to configure PostmanSandboxFleet initialization * * @typedef initOptions * * @property {Boolean} [disableLegacyAPIs=true] - Disable legacy pm interface APIs * @property {Array.<String>} [disabledAPIs] - List of pm APIs to disable */ /** * Options to configure UniversalVM connection * for each individual PostmanSandbox instance * * @typedef connectOptions * * @property {String} [bootCode] Code to be executed inside a UVM on boot * @property {Number} [timeout] - * @property {Boolean} [debug] - Enable console logs inside UVM */ /** * Class representing a fleet of PostmanSandboxes, * allowing orchestration of different variants * of sandboxes governed by templates. */ class PostmanSandboxFleet { /** * Create a fleet of sandboxes * * @param {templateRegistry} registry - * @param {initOptions} initOptions - * @param {connectOptions} connectOptions - */ constructor (registry, initOptions, connectOptions) { this.fleet = new Map(); if (!_.isObject(registry)) { throw new TypeError('sandbox-fleet: template registry must be an object'); } this.templateRegistry = registry; this.initOptions = initOptions; this.connectOptions = connectOptions; } /** * Check if a template is registered with a given name * * @param {string} templateName - * @returns {boolean} */ isRegistered (templateName) { return _.has(this.templateRegistry, templateName); } /** * Register a new template * * @param {string} templateName - * @param {template} template - */ register (templateName, template) { if (typeof templateName !== 'string') { throw new TypeError('sandbox-fleet: template name must be a string'); } if (typeof template !== 'string') { throw new TypeError('sandbox-fleet: template must be a string'); } if (this.isRegistered(templateName)) { throw new Error(`sandbox-fleet: template already registered for name ${templateName}`); } this.templateRegistry[templateName] = template; } /** * Returns sandbox instance for the given template name * * @param {String} templateName - * @param {Function} callback - * @returns {PostmanSandbox} */ getContext (templateName, callback) { if (typeof callback !== 'function') { return callback(new TypeError('sandbox-fleet: callback must be a function')); } if (typeof templateName !== 'string') { return callback(new TypeError('sandbox-fleet: template name must be a string')); } if (this.fleet.has(templateName)) { return callback(null, this.fleet.get(templateName)); } if (!this.isRegistered(templateName)) { return callback(new Error(`sandbox-fleet: template not found for name ${templateName}`)); } const template = this.templateRegistry[templateName]; if (typeof template !== 'string') { return callback(new Error(`sandbox-fleet: invalid template for name ${templateName}`)); } new PostmanSandbox().initialize({ disableLegacyAPIs: true, ...this.initOptions, template: template }, this.connectOptions, (err, context) => { if (err) { return callback(err); } // Trapping call to `context.dispose` to do the required cleanup in the fleet const proxiedContext = new Proxy(context, { get: (target, prop, receiver) => { if (prop === 'dispose') { return (...args) => { for (const context of this.fleet.values()) { if (proxiedContext === context) { this.fleet.delete(templateName); return target[prop](...args); } } }; } return Reflect.get(target, prop, receiver); } }); this.fleet.set(templateName, proxiedContext); callback(null, proxiedContext); }); } /** * Dispose off all initialized sandbox instances from the fleet * * @returns {void} */ disposeAll () { this.fleet.forEach((context, templateName) => { context.dispose(); this.fleet.delete(templateName); }); } } module.exports = PostmanSandboxFleet;