UNPKG

sandboxjs

Version:
343 lines (287 loc) 10.7 kB
var Bluebird = require('bluebird'); var Decode = require('jwt-decode'); var Path = require('path'); var Superagent = require('superagent'); var Url = require('url'); var defaults = require('lodash.defaults'); var defaultsDeep = require('lodash.defaultsdeep'); var forEach = require('lodash.foreach'); var getFrom = require('lodash.get'); /** * Creates an object representing a Webtask * * @constructor */ function Webtask (sandbox, token, options) { if (!options) options = {}; if (sandbox.securityVersion === 'v1') { try { /** * @property claims - The claims embedded in the Webtask's token */ this.claims = Decode(token); /** * @property token - The token associated with this webtask */ this.token = token; } catch (_) { throw new Error('token must be a valid JWT'); } } if (sandbox.securityVersion === 'v2') { if (typeof options.name !== 'string') { throw new Error('name must be a valid string'); } this.claims = { jtn: options.name, ten: options.container || sandbox.container, } } /** * @property sandbox - The {@see Sandbox} instance used to create this Webtask instance */ this.sandbox = sandbox; /** * @property meta - The metadata associated with this webtask */ this.meta = options.meta || {}; /** * @property secrets - The secrets associated with this webtask if `decrypt=true` */ this.secrets = options.secrets; /** * @property code - The code associated with this webtask if `fetch_code=true` */ this.code = options.code; /** * @property container - The container name in which the webtask will run */ Object.defineProperty(this, 'container', { enumerable: true, get: function () { return options.container || this.sandbox.container; } }); /** * @property url - The public url that can be used to invoke this webtask */ Object.defineProperty(this, 'url', { enumerable: true, get: function () { var url = options.webtask_url; if (!url) { if (this.claims.host) { var surl = Url.parse(this.sandbox.url); url = surl.protocol + '//' + this.claims.host + (surl.port ? (':' + surl.port) : '') + '/' + this.sandbox.container; } else { url = this.sandbox.url + '/api/run/' + this.sandbox.container; } if (this.claims.jtn) url += '/' + this.claims.jtn; else url += '?key=' + this.token; } return url; } }); } /** * Create a stream of logs from the webtask container * * Note that the logs will include messages from our infrastructure. * * @param {Object} options - Streaming options overrides * @param {String} [options.container] - The container for which you would like to stream logs. Defaults to the current profile's container. * @returns {Stream} A stream that will emit 'data' events with container logs */ Webtask.prototype.createLogStream = function (options) { return this.sandbox.createLogStream(options); }; /** * Run the webtask and return the result of execution * * @param {Object} options - Options used to tweak how the webtask will be invoked * @param {Function} [cb] - Optional node-style callback that will be invoked upon completion * @returns {Promise} - A Promise that will be resolved with the response from the server. */ Webtask.prototype.run = function (options, cb) { var methodMap = { 'delete': 'del', }; var urlData = Url.parse(this.url, true); var config = defaultsDeep(options || {}, { method: 'get', path: '', query: {}, }); var method = getFrom(methodMap, config.method, config.method); var query = defaults(urlData.query, config.query); urlData.pathname = Path.join(urlData.pathname, config.path); urlData.query = {}; urlData.search = null; var url = Url.format(urlData); var request = Superagent[method](url) .query(query); if (config.body) request = request.send(config.body); forEach(config.headers, function (value, header) { request = request.set(header, value); }); var promise = Bluebird.resolve(request) .catch(function (err) { if (err.response) return err.response; throw err; }) .then(function (res) { res.statusCode = res.status; return res; }); return cb ? promise.nodeify(cb, {spread: true}) : promise; }; /** * Schedule the webtask to run periodically * * @param {Object} options - Options for creating the webtask * @param {Object} options.schedule - Cron-string-formatted schedule * @param {Object} [options.name] - The name for the cron job * @param {Object} [options.tz] - The timezone for the cron job (IANA timezone) * @param {Function} [cb] - Optional node-style callback that will be invoked upon completion * @returns {Promise} - A Promise that will be resolved with a {@see CronJob} instance. */ Webtask.prototype.createCronJob = function (options, cb) { if (this.token) { var parsedUrl = Url.parse(this.claims.url); var filename = parsedUrl.pathname ? Path.basename(parsedUrl.pathname) : ''; options = defaultsDeep(options, { name: this.claims.jtn || filename, state: 'active', meta: this.meta, }); } else { if (options.name && options.name !== this.claims.jtn) { throw new Error('Server does not support specifying CRON name that is different from webtask name.'); } options = defaultsDeep(options, { name: this.claims.jtn, state: 'active', meta: this.meta, }); } if (!options.name) throw new Error('Cron jobs must have a name.'); var promise = this.sandbox.createCronJob({ name: options.name, token: this.token, schedule: options.schedule, state: options.state, tz: options.tz, meta: options.meta }); return cb ? promise.nodeify(cb, {spread: true}) : promise; }; /** * Inspect an existing webtask to optionally get code and/or secrets * * @param {Object} options - Options for inspecting the webtask. * @param {Boolean} [options.decrypt] - Decrypt the webtask's secrets. * @param {Boolean} [options.fetch_code] - Fetch the code associated with the webtask. * @param {Function} [cb] - Optional callback function for node-style callbacks. * @returns {Promise} A Promise that will be fulfilled with the result of inspecting the token. */ Webtask.prototype.inspect = function (options, cb) { if (this.sandbox.securityVersion === 'v2') { throw new Error('The Webtask.inspect function is not supported in the current configuration. Consider using Sandbox.inspectWebtask instead.') } options.token = this.token; var promise = this.sandbox.inspectToken(options); return cb ? promise.nodeify(cb) : promise; }; /** * Remove the named webtask * * @param {Function} [cb] - Optional callback function for node-style callbacks. * @returns {Promise} A Promise that will be fulfilled with the result of inspecting the token. */ Webtask.prototype.remove = function (cb) { var promise; if (!this.claims.jtn) { var err = new Error('Unnamed webtasks cannot be removed'); err.statusCode = 400; promise = Bluebird.reject(err); return cb ? promise.nodeify(cb) : promise; } else { promise = this.sandbox.removeWebtask({ name: this.claims.jtn, }); } return cb ? promise.nodeify(cb) : promise; }; /** * Revoke the webtask's token * * @param {Function} [cb] - Optional callback function for node-style callbacks. * @returns {Promise} A Promise that will be fulfilled with the result of revoking the token. */ Webtask.prototype.revoke = function (cb) { var promise = this.sandbox.revokeToken(this.token); return cb ? promise.nodeify(cb) : promise; }; /** * Update a webtask * * @param {Object} [options] - Options for updating a webtask (@see: Sandbox.updateWebtask) * @param {Function} [cb] - Optional callback function for node-style callbacks. * @returns {Promise} A Promise that will be fulfilled with the result of revoking the token. */ Webtask.prototype.update = function (options, cb) { if (typeof options === 'function') { cb = options; options = {}; } if (!options) { options = {}; } options.name = this.claims.jtn; var promise = this.sandbox.updateWebtask(options); return cb ? promise.nodeify(cb) : promise; }; Webtask.prototype.toJSON = function () { var data = { container: this.container, token: this.token, url: this.url, }; if (this.claims.jtn) data.name = this.claims.jtn; return data; }; /** * Update the storage associated to the a webtask * * @param {Object} options - Options * @param {String} [options.container] - Set the webtask container. Defaults to the profile's container. * @param {String} options.name - The name of the webtask. * @param {Object} storage - storage * @param {Object} storage.data - The data to be stored * @param {String} storage.etag - Pass in an optional string to be used for optimistic concurrency control to prevent simultaneous updates of the same data. * @param {Function} [cb] - Optional callback function for node-style callbacks. * @return {Promise} A Promise that will be fulfilled with an array of Webtasks */ Webtask.prototype.updateStorage = function (storage, options, cb) { var promise = this.sandbox.updateStorage(storage, options); return cb ? promise.nodeify(cb) : promise; } /** * Read the storage associated to the a webtask * * @param {Object} options - Options * @param {String} [options.container] - Set the webtask container. Defaults to the profile's container. * @param {String} options.name - The name of the webtask. * @param {Function} [cb] - Optional callback function for node-style callbacks. * @return {Promise} A Promise that will be fulfilled with an array of Webtasks */ Webtask.prototype.getStorage = function (options, cb) { var promise = this.sandbox.getStorage(options); return cb ? promise.nodeify(cb) : promise; } module.exports = Webtask;