Skip to content

Commit

Permalink
Update to Async/Await
Browse files Browse the repository at this point in the history
  Description:
  Promisify post from request module,
  Convert callback style functions to implement async/await
  Remove done flag from unit test and check function
  Make unit test async/await
  • Loading branch information
kevingrozav committed Sep 12, 2017
1 parent 56b55f0 commit 8c533da
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 130 deletions.
25 changes: 19 additions & 6 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
{
"compact": false,
"presets": ["es2015"],
"auxiliaryCommentBefore": "istanbul ignore next",
"sourceMaps": "inline"
}
{
"presets": [
["env", {
"targets": {
"node": "0.12"
}
}]
],
"compact": false,
"sourceMaps": "inline",
"plugins": [
"transform-async-to-bluebird",
"transform-promise-to-bluebird",
["transform-runtime", {
"polyfill": false,
"regenerator": true
}]
]
}
7 changes: 4 additions & 3 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ globals:
spy: true
stub: true

parserOptions:
sourceType: "module"
parserOptions:
sourceType: "module"
ecmaVersion: 8

rules:
rules:
# ERRORS
space-before-blocks: 2
indent: [2, 2, { "SwitchCase": 1 }]
Expand Down
13 changes: 9 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,21 @@
"dependencies": {
"babel-preset-es2015": "^6.6.0",
"debug": "^2.2.0",
"request": "^2.73.0"
"request": "^2.73.0",
"babel-preset-env": "^1.5.1",
"bluebird": "^3.5.0"
},
"devDependencies": {
"babel-cli": "^6.10.1",
"chai": "^3.5.0",
"eslint": "^3.3.0",
"mocha": "^2.5.3"
"mocha": "^2.5.3",
"babel-cli": "^6.24.1",
"babel-plugin-transform-async-to-bluebird": "^1.1.1",
"babel-plugin-transform-promise-to-bluebird": "^1.1.1",
"babel-plugin-transform-runtime": "^6.23.0"
},
"engines": {
"node": ">=6.9.1",
"npm": ">=3.10.8"
}
}
}
214 changes: 115 additions & 99 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,68 +6,87 @@

import * as request from 'request';
import debug from 'debug';
import Promise from 'bluebird';

// Debug log
const log = debug('watsonwork-helloworld-app');

// Return the list of spaces the app belongs to
const spaces = (tok, cb) => {
request.post('https://api.watsonwork.ibm.com/graphql', {
headers: {
jwt: tok,
'Content-Type': 'application/graphql'
},

// This is a GraphQL query, used to retrieve the list of spaces
// visible to the app (given the app OAuth token)
body: `
query {
spaces (first: 50) {
items {
title
id
// Promisify to enable async/await construct
const post = Promise.promisify(request.post);


// Return a list of spaces the app belongs to
const spaces = async (tok) => {
try {
const res = await post('https://api.watsonwork.ibm.com/graphql', {
headers: {
jwt: tok,
'Content-Type': 'application/graphql'
},
// This is a GraphQL query, used to retrieve the list of spaces
// visible to the app (given the app OAuth token)
body: `
query {
spaces (first: 50) {
items {
title
id
}
}
}
}`
}, (err, res) => {
if(err || res.statusCode !== 200) {
log('Error retrieving spaces %o', err || res.statusCode);
cb(err || new Error(res.statusCode));
return;
}
}`
});

// Throw an error if request unsuccesful
if(res.statusCode !== 200)
throw new Error(res.statusCode);

// Return the list of spaces
const body = JSON.parse(res.body);
log('Space query result %o', body.data.spaces.items);
cb(null, body.data.spaces.items);
});
return body.data.spaces.items;
}
catch(err) {
log('Space query result %o', body.data.spaces.items);
throw err;
}
};


// Return an OAuth token for the app
const token = (appId, secret, cb) => {
request.post('https://api.watsonwork.ibm.com/oauth/token', {
auth: {
user: appId,
pass: secret
},
json: true,
form: {
grant_type: 'client_credentials'
}
}, (err, res) => {
if(err || res.statusCode !== 200) {
log('Error getting OAuth token %o', err || res.statusCode);
cb(err || new Error(res.statusCode));
return;
}
cb(null, res.body.access_token);
});
const token = async (appId, secret) => {
try {
// Authentication request with client_credentials
const res = await post('https://api.watsonwork.ibm.com/oauth/token', {
auth: {
user: appId,
pass: secret
},
json: true,
form: {
grant_type: 'client_credentials'
}
});

// Throw an error if request unsuccesful
if(res.statusCode !== 200)
throw new Error(res.statusCode);

// Return the OAuth token
return res.body.access_token;
}
catch(err) {
log('Error getting OAuth token %o', err || res.statusCode);
throw err;
}

};


// Send an app message to the conversation in a space
const send = (spaceId, text, tok, cb) => {
request.post(
'https://api.watsonwork.ibm.com/v1/spaces/' + spaceId + '/messages', {
const send = async (spaceId, text, tok) => {

try {
const res = await post('https://api.watsonwork.ibm.com/v1/spaces/' + spaceId + '/messages', {
headers: {
Authorization: 'Bearer ' + tok
},
Expand All @@ -90,64 +109,61 @@ const send = (spaceId, text, tok, cb) => {
}
}]
}
}, (err, res) => {
if(err || res.statusCode !== 201) {
log('Error sending message %o', err || res.statusCode);
cb(err || new Error(res.statusCode));
return;
}
log('Send result %d, %o', res.statusCode, res.body);
cb(null, res.body);
});

// Throw an error if request unsuccesful
if(res.statusCode !== 201)
throw new Error(res.statusCode);

// Return the body of the message response
log('Send result %d, %o', res.statusCode, res.body);
return res.body;
}
catch(err) {
log('Error sending message %o', err);
throw err;
}
};


// Main app entry point
// Send a message to the conversation in the space matching the given name
export const main = (name, text, appId, secret, cb) => {
// Get an OAuth token for the app
token(appId, secret, (err, tok) => {
if(err) {
cb(err);
return;
}

export const main = async (name, text, appId, secret) => {
try {
// Get an OAuth token for the app
var tok = await token(appId, secret);
// List the spaces the app belongs to
spaces(tok, (err, slist) => {
if(err) {
cb(err);
return;
}

// Find a space matching the given name
const space = slist.filter((s) => s.title === name)[0];
if(!space) {
cb(new Error('Space not found'));
return;
}

// Send the message
log('Sending \'%s\' to space %s', text, space.title);
send(space.id,
text || 'Hello World! Welcome to **Watson Work**!',
tok, (err, res) => {
if(err) {
cb(err);
return;
}
log('Sent message to space %s', space.title);
cb(null);
});
});
});
var spaceList = await spaces(tok);
// Find a space matching the given name
const space = spaceList.filter((s) => s.title === name)[0];
if (!space)
throw new Error('Space not found');

log('Sending \'%s\' to space %s', text, space.title);

// Send the message
await send(space.id,
text || 'Hello World! Welcome to **Watson Work**!',
tok);
log('Sent message to space %s', space.title);

} catch(err) {
log('Error starting the application %o', error);
throw err;
}
};

if(require.main === module)
// Run the app
main(process.argv[2], process.argv[3],
// Expect the app id and secret to be configured in env variables
process.env.HELLOWORLD_APP_ID, process.env.HELLOWORLD_APP_SECRET,
(err) => {
if(err)
console.log('Error sending message:', err);
});

// Run program as an IIFE (top level await not supported in node)
(async () => {
if(require.main === module)
// Run the app
main(process.argv[2], process.argv[3],
// Expect the app id and secret to be configured in env variables
process.env.HELLOWORLD_APP_ID, process.env.HELLOWORLD_APP_SECRET,
(err) => {
if(err)
console.log('Error sending message:', err);
});
})();

28 changes: 10 additions & 18 deletions src/test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,7 @@ require.cache[require.resolve('request')].exports = {
const helloworld = require('../app');

describe('watsonwork-helloworld', () => {
it('sends a hello world message to a space', (done) => {

// Check async callbacks
let checks = 0;
const check = () => {
if(++checks === 4)
done();
};
it('sends a hello world message to a space', async () => {

postspy = (uri, opt, cb) => {
// Expect a call to get an OAuth token for the app
Expand All @@ -44,7 +37,7 @@ describe('watsonwork-helloworld', () => {
access_token: 'testaccesstoken'
}
}));
check();
// check();
return;
}

Expand Down Expand Up @@ -78,7 +71,6 @@ describe('watsonwork-helloworld', () => {
}
})
}));
check();
return;
}

Expand Down Expand Up @@ -111,17 +103,17 @@ describe('watsonwork-helloworld', () => {
body: {
}
}));
check();
}
};

// Run the HelloWorld app
helloworld.main('Test Space', 'Hey', 'testappid', 'testsecret',
(err, res) => {
// Expect a successful run
expect(err).to.equal(null);
check();
});
try {
// Run the HelloWorld app
await helloworld.main('Test Space', 'Hey', 'testappid', 'testsecret');
}
catch(err) {
throw err;
}

});
});

0 comments on commit 8c533da

Please sign in to comment.