UNPKG

10.7 kBJavaScriptView Raw
1var Bluebird = require('bluebird');
2var Decode = require('jwt-decode');
3var Path = require('path');
4var Superagent = require('superagent');
5var Url = require('url');
6
7var defaults = require('lodash.defaults');
8var defaultsDeep = require('lodash.defaultsdeep');
9var forEach = require('lodash.foreach');
10var getFrom = require('lodash.get');
11
12/**
13 * Creates an object representing a Webtask
14 *
15 * @constructor
16 */
17function Webtask (sandbox, token, options) {
18 if (!options) options = {};
19
20 if (sandbox.securityVersion === 'v1') {
21 try {
22 /**
23 * @property claims - The claims embedded in the Webtask's token
24 */
25 this.claims = Decode(token);
26
27 /**
28 * @property token - The token associated with this webtask
29 */
30 this.token = token;
31 }
32 catch (_) {
33 throw new Error('token must be a valid JWT');
34 }
35 }
36
37 if (sandbox.securityVersion === 'v2') {
38 if (typeof options.name !== 'string') {
39 throw new Error('name must be a valid string');
40 }
41
42 this.claims = {
43 jtn: options.name,
44 ten: options.container || sandbox.container,
45 }
46 }
47
48 /**
49 * @property sandbox - The {@see Sandbox} instance used to create this Webtask instance
50 */
51 this.sandbox = sandbox;
52
53 /**
54 * @property meta - The metadata associated with this webtask
55 */
56 this.meta = options.meta || {};
57
58 /**
59 * @property secrets - The secrets associated with this webtask if `decrypt=true`
60 */
61 this.secrets = options.secrets;
62
63 /**
64 * @property code - The code associated with this webtask if `fetch_code=true`
65 */
66 this.code = options.code;
67
68 /**
69 * @property container - The container name in which the webtask will run
70 */
71 Object.defineProperty(this, 'container', {
72 enumerable: true,
73 get: function () {
74 return options.container || this.sandbox.container;
75 }
76 });
77
78 /**
79 * @property url - The public url that can be used to invoke this webtask
80 */
81 Object.defineProperty(this, 'url', {
82 enumerable: true,
83 get: function () {
84 var url = options.webtask_url;
85 if (!url) {
86 if (this.claims.host) {
87 var surl = Url.parse(this.sandbox.url);
88 url = surl.protocol + '//' + this.claims.host + (surl.port ? (':' + surl.port) : '') + '/' + this.sandbox.container;
89 }
90 else {
91 url = this.sandbox.url + '/api/run/' + this.sandbox.container;
92 }
93
94 if (this.claims.jtn) url += '/' + this.claims.jtn;
95 else url += '?key=' + this.token;
96 }
97
98 return url;
99 }
100 });
101}
102
103/**
104 * Create a stream of logs from the webtask container
105 *
106 * Note that the logs will include messages from our infrastructure.
107 *
108 * @param {Object} options - Streaming options overrides
109 * @param {String} [options.container] - The container for which you would like to stream logs. Defaults to the current profile's container.
110 * @returns {Stream} A stream that will emit 'data' events with container logs
111 */
112Webtask.prototype.createLogStream = function (options) {
113 return this.sandbox.createLogStream(options);
114};
115
116/**
117 * Run the webtask and return the result of execution
118 *
119 * @param {Object} options - Options used to tweak how the webtask will be invoked
120 * @param {Function} [cb] - Optional node-style callback that will be invoked upon completion
121 * @returns {Promise} - A Promise that will be resolved with the response from the server.
122 */
123Webtask.prototype.run = function (options, cb) {
124 var methodMap = {
125 'delete': 'del',
126 };
127 var urlData = Url.parse(this.url, true);
128 var config = defaultsDeep(options || {}, {
129 method: 'get',
130 path: '',
131 query: {},
132 });
133 var method = getFrom(methodMap, config.method, config.method);
134 var query = defaults(urlData.query, config.query);
135
136 urlData.pathname = Path.join(urlData.pathname, config.path);
137 urlData.query = {};
138 urlData.search = null;
139
140 var url = Url.format(urlData);
141 var request = Superagent[method](url)
142 .query(query);
143
144 if (config.body) request = request.send(config.body);
145
146 forEach(config.headers, function (value, header) {
147 request = request.set(header, value);
148 });
149
150 var promise = Bluebird.resolve(request)
151 .catch(function (err) {
152 if (err.response) return err.response;
153
154 throw err;
155 })
156 .then(function (res) {
157 res.statusCode = res.status;
158
159 return res;
160 });
161
162 return cb ? promise.nodeify(cb, {spread: true}) : promise;
163};
164
165/**
166 * Schedule the webtask to run periodically
167 *
168 * @param {Object} options - Options for creating the webtask
169 * @param {Object} options.schedule - Cron-string-formatted schedule
170 * @param {Object} [options.name] - The name for the cron job
171 * @param {Object} [options.tz] - The timezone for the cron job (IANA timezone)
172 * @param {Function} [cb] - Optional node-style callback that will be invoked upon completion
173 * @returns {Promise} - A Promise that will be resolved with a {@see CronJob} instance.
174 */
175Webtask.prototype.createCronJob = function (options, cb) {
176 if (this.token) {
177 var parsedUrl = Url.parse(this.claims.url);
178 var filename = parsedUrl.pathname
179 ? Path.basename(parsedUrl.pathname)
180 : '';
181
182 options = defaultsDeep(options, {
183 name: this.claims.jtn || filename,
184 state: 'active',
185 meta: this.meta,
186 });
187 }
188 else {
189 if (options.name && options.name !== this.claims.jtn) {
190 throw new Error('Server does not support specifying CRON name that is different from webtask name.');
191 }
192 options = defaultsDeep(options, {
193 name: this.claims.jtn,
194 state: 'active',
195 meta: this.meta,
196 });
197 }
198
199 if (!options.name) throw new Error('Cron jobs must have a name.');
200
201 var promise = this.sandbox.createCronJob({
202 name: options.name,
203 token: this.token,
204 schedule: options.schedule,
205 state: options.state,
206 tz: options.tz,
207 meta: options.meta
208 });
209
210 return cb ? promise.nodeify(cb, {spread: true}) : promise;
211};
212
213
214/**
215 * Inspect an existing webtask to optionally get code and/or secrets
216 *
217 * @param {Object} options - Options for inspecting the webtask.
218 * @param {Boolean} [options.decrypt] - Decrypt the webtask's secrets.
219 * @param {Boolean} [options.fetch_code] - Fetch the code associated with the webtask.
220 * @param {Function} [cb] - Optional callback function for node-style callbacks.
221 * @returns {Promise} A Promise that will be fulfilled with the result of inspecting the token.
222 */
223Webtask.prototype.inspect = function (options, cb) {
224 if (this.sandbox.securityVersion === 'v2') {
225 throw new Error('The Webtask.inspect function is not supported in the current configuration. Consider using Sandbox.inspectWebtask instead.')
226 }
227
228 options.token = this.token;
229
230 var promise = this.sandbox.inspectToken(options);
231
232 return cb ? promise.nodeify(cb) : promise;
233};
234
235/**
236 * Remove the named webtask
237 *
238 * @param {Function} [cb] - Optional callback function for node-style callbacks.
239 * @returns {Promise} A Promise that will be fulfilled with the result of inspecting the token.
240 */
241Webtask.prototype.remove = function (cb) {
242 var promise;
243
244 if (!this.claims.jtn) {
245 var err = new Error('Unnamed webtasks cannot be removed');
246 err.statusCode = 400;
247
248 promise = Bluebird.reject(err);
249
250 return cb ? promise.nodeify(cb) : promise;
251 } else {
252 promise = this.sandbox.removeWebtask({
253 name: this.claims.jtn,
254 });
255 }
256
257 return cb ? promise.nodeify(cb) : promise;
258};
259
260/**
261 * Revoke the webtask's token
262 *
263 * @param {Function} [cb] - Optional callback function for node-style callbacks.
264 * @returns {Promise} A Promise that will be fulfilled with the result of revoking the token.
265 */
266Webtask.prototype.revoke = function (cb) {
267 var promise = this.sandbox.revokeToken(this.token);
268
269 return cb ? promise.nodeify(cb) : promise;
270};
271
272/**
273 * Update a webtask
274 *
275 * @param {Object} [options] - Options for updating a webtask (@see: Sandbox.updateWebtask)
276 * @param {Function} [cb] - Optional callback function for node-style callbacks.
277 * @returns {Promise} A Promise that will be fulfilled with the result of revoking the token.
278 */
279Webtask.prototype.update = function (options, cb) {
280 if (typeof options === 'function') {
281 cb = options;
282 options = {};
283 }
284
285 if (!options) {
286 options = {};
287 }
288
289 options.name = this.claims.jtn;
290
291 var promise = this.sandbox.updateWebtask(options);
292
293 return cb ? promise.nodeify(cb) : promise;
294};
295
296
297Webtask.prototype.toJSON = function () {
298 var data = {
299 container: this.container,
300 token: this.token,
301 url: this.url,
302 };
303
304 if (this.claims.jtn) data.name = this.claims.jtn;
305
306 return data;
307};
308
309/**
310 * Update the storage associated to the a webtask
311 *
312 * @param {Object} options - Options
313 * @param {String} [options.container] - Set the webtask container. Defaults to the profile's container.
314 * @param {String} options.name - The name of the webtask.
315 * @param {Object} storage - storage
316 * @param {Object} storage.data - The data to be stored
317 * @param {String} storage.etag - Pass in an optional string to be used for optimistic concurrency control to prevent simultaneous updates of the same data.
318 * @param {Function} [cb] - Optional callback function for node-style callbacks.
319 * @return {Promise} A Promise that will be fulfilled with an array of Webtasks
320 */
321Webtask.prototype.updateStorage = function (storage, options, cb) {
322 var promise = this.sandbox.updateStorage(storage, options);
323
324 return cb ? promise.nodeify(cb) : promise;
325}
326
327/**
328 * Read the storage associated to the a webtask
329 *
330 * @param {Object} options - Options
331 * @param {String} [options.container] - Set the webtask container. Defaults to the profile's container.
332 * @param {String} options.name - The name of the webtask.
333 * @param {Function} [cb] - Optional callback function for node-style callbacks.
334 * @return {Promise} A Promise that will be fulfilled with an array of Webtasks
335 */
336Webtask.prototype.getStorage = function (options, cb) {
337 var promise = this.sandbox.getStorage(options);
338
339 return cb ? promise.nodeify(cb) : promise;
340}
341
342module.exports = Webtask;