diff --git a/environments/nodejs/.gitignore b/environments/nodejs/.gitignore new file mode 100644 index 00000000..c2658d7d --- /dev/null +++ b/environments/nodejs/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/environments/nodejs/Dockerfile b/environments/nodejs/Dockerfile index 8d8b54a9..a20d035c 100644 --- a/environments/nodejs/Dockerfile +++ b/environments/nodejs/Dockerfile @@ -1,6 +1,6 @@ # A docker image for the func container. -FROM node:4-onbuild +FROM node:7-onbuild ADD server.js /usr/src/app/server.js diff --git a/environments/nodejs/package.json b/environments/nodejs/package.json index 3b7312ff..f6c9f1c0 100644 --- a/environments/nodejs/package.json +++ b/environments/nodejs/package.json @@ -1,27 +1,26 @@ { - "name": "fission-nodejs-runtime", - "version": "0.0.0", - "author": "Soam Vasani", - "contributors": [ - { - "name": "Soam Vasani", - "email": "soamvasani@platform9.com" - } - ], - "description": "NodeJS run container for the fission framework", - "engines": { - "node": ">=4.2.2" - }, - "dependencies": { - "express": "", - "minimist": "", - "body-parser": "", - "morgan": "", - - "co": "~4.6.0", - "request": "", - "request-promise": "^1.0.2", - "mz": "~2.1.0", - "underscore": ">=1.8.3" + "name": "fission-nodejs-runtime", + "version": "0.1.0", + "author": "Soam Vasani", + "contributors": [ + { + "name": "Soam Vasani", + "email": "soamvasani@platform9.com" } + ], + "description": "NodeJS run container for the fission framework", + "engines": { + "node": ">=7.6.0" + }, + "dependencies": { + "body-parser": "", + "co": "~4.6.0", + "express": "", + "minimist": "", + "morgan": "", + "mz": "~2.1.0", + "request": "^2.81.0", + "request-promise-native": "^1.0.3", + "underscore": ">=1.8.3" + } } diff --git a/environments/nodejs/server.js b/environments/nodejs/server.js index 591793d2..3966be41 100644 --- a/environments/nodejs/server.js +++ b/environments/nodejs/server.js @@ -75,11 +75,13 @@ app.all('/', function (req, res) { res.status(500).send("Generic container: no requests supported"); return; } + const context = { request: req, response: res // TODO: context should also have: URL template params, query string }; + function callback(status, body, headers) { if (!status) return; @@ -90,18 +92,32 @@ app.all('/', function (req, res) { } res.status(status).send(body); } - try { - // - // Customizing the request context - // - // If you want to modify the context to add anything to it, - // you can do that here by adding properties to the context. - // - userFunction(context, callback); - } catch(e) { - console.log(`Function error: ${e}`); - callback(500, "Internal server error") + + // + // Customizing the request context + // + // If you want to modify the context to add anything to it, + // you can do that here by adding properties to the context. + // + + let functionProm; + if (userFunction.length === 1) { // One argument (context) + // Make sure their function returns a promise + Promise.resolve(userFunction(context)).then(function({ status, body, headers }) { + callback(status, body, headers); + }).catch(function(err) { + console.log(`Function error: ${e}`); + callback(500, "Internal server error"); + }); + } else { // 2 arguments (context, callback) + try { + userFunction(context, callback); + } catch (err) { + console.log(`Function error: ${e}`); + callback(500, "Internal server error"); + } } + }); app.listen(argv.port); diff --git a/environments/nodejs/test/test.js b/environments/nodejs/test/test.js index 6b6ae4fd..4d0791d4 100644 --- a/environments/nodejs/test/test.js +++ b/environments/nodejs/test/test.js @@ -1,6 +1,9 @@ -module.exports = function (context, callback) { +module.exports = async function (context) { console.log("headers=", JSON.stringify(context.request.headers)); console.log("body=", JSON.stringify(context.request.body)); - callback(200, "Hello, world !\n"); + return { + status: 200, + body: "Hello, world !\n" + }; } diff --git a/examples/nodejs/hello-callback.js b/examples/nodejs/hello-callback.js new file mode 100644 index 00000000..b679a442 --- /dev/null +++ b/examples/nodejs/hello-callback.js @@ -0,0 +1,4 @@ + +module.exports = function(context, callback) { + callback(200, "Hello, world!\n"); +} diff --git a/examples/nodejs/hello.js b/examples/nodejs/hello.js index b679a442..4bcbf4f8 100644 --- a/examples/nodejs/hello.js +++ b/examples/nodejs/hello.js @@ -1,4 +1,7 @@ -module.exports = function(context, callback) { - callback(200, "Hello, world!\n"); +module.exports = async function(context) { + return { + status: 200, + body: "Hello, world!\n" + }; } diff --git a/examples/nodejs/kubeEventsSlack.js b/examples/nodejs/kubeEventsSlack.js index b0bde05d..f797a3b3 100644 --- a/examples/nodejs/kubeEventsSlack.js +++ b/examples/nodejs/kubeEventsSlack.js @@ -23,7 +23,7 @@ function upcaseFirst(s) { return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase(); } -function sendSlackMessage(msg, cb) { +async function sendSlackMessage(msg) { let postData = `{"text": "${msg}"}`; let options = { hostname: "hooks.slack.com", @@ -33,17 +33,20 @@ function sendSlackMessage(msg, cb) { "Content-Type": "application/json" } }; - let req = https.request(options, function(res) { - console.log(`slack request status = ${res.statusCode}`); - cb(); + + return new Promise(function(resolve, reject) { + let req = https.request(options, function(res) { + console.log(`slack request status = ${res.statusCode}`); + return resolve(); + }); + req.write(postData); + req.end(); }); - req.write(postData); - req.end(); } -module.exports = function(context, callback) { +module.exports = async function(context) { console.log(context.request.headers); - + let obj = context.request.body; let version = obj.metadata.resourceVersion; let eventType = context.request.get('X-Kubernetes-Event-Type'); @@ -54,10 +57,11 @@ module.exports = function(context, callback) { if (eventType == 'DELETED' || eventType == 'ADDED') { console.log("sending event to slack") - sendSlackMessage(msg, function() { - callback(200, ""); - }); - } else { - callback(200, ""); + await sendSlackMessage(msg); + } + + return { + status: 200, + body: "" } } diff --git a/examples/nodejs/stock.js b/examples/nodejs/stock.js index 27d96e5c..2150b68e 100644 --- a/examples/nodejs/stock.js +++ b/examples/nodejs/stock.js @@ -1,31 +1,37 @@ 'use strict'; -var http = require('http'); +const rp = require('request-promise-native'); -module.exports = function (context, callback) { - let body = context.request.body; - console.log(`body text: ${body['text']}`); +module.exports = async function (context) { + const body = context.request.body; + const symbol = body.symbol - var symbol = body['text'].split(' ')[1]; + console.log(`Got symbol: ${symbol}`); - http.get({ - host: 'finance.google.com', - path: `/finance/info?q=NYSE:${symbol}` - }, function(response) { - var resp = ''; - response.on('data', function(d) { - resp += d; - }); - response.on('end', function() { - - try { - var parsed = JSON.parse(resp.slice(3)); - var lastTrade = parsed[0]['l_cur'] - callback(200, `{ "text": "${symbol} last traded at ${lastTrade}" }`); - } catch (e) { - callback(200, `{ "text": "Error (invalid NYSE symbol?)" }`); + if (!symbol) { + return { + status: 400, + body: { + text: 'You must provide a stock symbol.' } - }); - }); + }; + } + try { + const response = await rp(`http://finance.google.com/finance/info?q=NYSE:${symbol}`); + const parsed = JSON.parse(response.slice(3)); + const lastTrade = parsed[0]['l_cur']; + return { + status: 200, + body: { + text: `${symbol} last traded at ${lastTrade}` + } + }; + } catch (e) { + console.error(e); + return { + status: 500, + body: e + }; + } }