Skip to content

Commit

Permalink
Merge pull request #7 from declandewet/develop
Browse files Browse the repository at this point in the history
Better Errors for Web API
  • Loading branch information
zspecza committed Sep 11, 2015
2 parents 37b9959 + 33a8456 commit 379f1cc
Show file tree
Hide file tree
Showing 4 changed files with 757 additions and 168 deletions.
173 changes: 75 additions & 98 deletions lib/base-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import qs from 'querystring';
import fetch,
{Response} from 'node-fetch';
import WebSocket from 'ws';
import WEB_METHODS from './web-methods';
import singleLine from './utils/singleline';

const VALID_TOKEN = /^([a-z]*)\-([0-9]*)\-([0-9a-zA-Z]*)/;
Expand Down Expand Up @@ -108,33 +109,27 @@ export default class BaseAPI extends EventEmitter {
* @return {Promise} - a promise for the parsed reply from rtm.start
*/
async connect(opts, WebSocket=WebSocket) {
let connection;
try {
// call the rtm.start method
connection = await this.method('rtm.start', opts);
// call the rtm.start method
let connection = await this.method('rtm.start', opts);

this.ws = new WebSocket(connection.url);
this.connection = connection;
this.ws = new WebSocket(connection.url);
this.connection = connection;

// bind websocket events to base class
// and emit events of the same name
this.events.forEach(event => {
this.ws.on(event, data => {
// if the event is a message event, parse the
// JSON payload before emitting from base class
if (event === 'message' && data != null) data = JSON.parse(data);
this.emit(event, data);
});
// bind websocket events to base class
// and emit events of the same name
this.events.forEach(event => {
this.ws.on(event, data => {
// if the event is a message event, parse the
// JSON payload before emitting from base class
if (event === 'message' && data != null) data = JSON.parse(data);
this.emit(event, data);
});
});

// only resolve once the connection has been opened
return new Promise(resolve => {
this.on('open', () => resolve(this.connection));
});

} catch (error) {
throw new Error(error);
}
// only resolve once the connection has been opened
return new Promise(resolve => {
this.on('open', () => resolve(this.connection));
});
}

/**
Expand All @@ -145,37 +140,34 @@ export default class BaseAPI extends EventEmitter {
* @return {Promise} - a promise for the parsed JSON response
*/
async method(method, opts) {
let response;
try {
// merge given opts with defaults
opts = { ...this.defaults, ...opts };
cleanOptProps(opts, 'webhook', 'slackbot');
if (method === 'api.test') {
cleanOptProps(opts, 'token');
}
validateOptions(opts, 'error');
for (let option of Object.keys(opts)) {
// if the option is an array, stringify it,
// as slack uses a weird query string syntax
// that mixes JSON strings
if (Array.isArray(opts[option])) {
opts[option] = JSON.stringify(opts[option]);
}
// merge given opts with defaults
opts = { ...this.defaults, ...opts };
cleanOptProps(opts, 'webhook', 'slackbot');
if (method === 'api.test') {
cleanOptProps(opts, 'token');
}
validateOptions(opts, 'error');
for (let option of Object.keys(opts)) {
// if the option is an array, stringify it,
// as slack uses a weird query string syntax
// that mixes JSON strings
if (Array.isArray(opts[option])) {
opts[option] = JSON.stringify(opts[option]);
}
response = await this.api(
`${this.baseURL + method}?${qs.stringify(opts)}`
}
let response = await this.api(
`${this.baseURL + method}?${qs.stringify(opts)}`
);
if (!response.ok) {
let errors = WEB_METHODS.find(web => web.name === method).errors;
throw new Error(
singleLine`
[${method} error] the response returned with error "${response.error}"
- ${errors[response.error] || 'No description.'}
`
);
if (!response.ok) {
throw new Error(
singleLine`
[error] the response returned with error "${response.error}"
`
);
}
return response;
} catch (error) {
throw new Error(error);
}
return response;
}

/**
Expand All @@ -185,28 +177,23 @@ export default class BaseAPI extends EventEmitter {
* @return {Promise} the parsed JSON response
*/
async submit(opts) {
let response;
try {
opts = { ...this.defaults, ...opts };
validateOptions(opts, 'error');
let webhook = opts.webhook;
// delete the webhook key as Slack doesn't know WTF that is
cleanOptProps(opts, 'webhook', 'slackbot', 'token');
response = await this.api(webhook, {
method: 'post',
body: JSON.stringify(opts)
}, 'text');
if (response !== 'ok') {
throw new Error(
singleLine`
[error] the response returned with error "${response}"
`
);
}
return response;
} catch (error) {
throw new Error(error);
opts = { ...this.defaults, ...opts };
validateOptions(opts, 'error');
let webhook = opts.webhook;
// delete the webhook key as Slack doesn't know WTF that is
cleanOptProps(opts, 'webhook', 'slackbot', 'token');
let response = await this.api(webhook, {
method: 'post',
body: JSON.stringify(opts)
}, 'text');
if (response !== 'ok') {
throw new Error(
singleLine`
[error] the response returned with error "${response}"
`
);
}
return response;
}

/**
Expand All @@ -216,26 +203,21 @@ export default class BaseAPI extends EventEmitter {
* @return {Promise} the parsed JSON response
*/
async slackbot(opts) {
let response;
try {
opts.channel = opts.channel || '#general';
let channel = qs.stringify({ channel: opts.channel });
let url = `${this.defaults.slackbot}&${channel}`;
response = await this.api(url, {
method: 'post',
body: opts.text
}, 'text');
if (response !== 'ok') {
throw new Error(
singleLine`
[error] the response returned with error "${response}"
`
);
}
return response;
} catch (error) {
throw new Error(error);
opts.channel = opts.channel || '#general';
let channel = qs.stringify({ channel: opts.channel });
let url = `${this.defaults.slackbot}&${channel}`;
let response = await this.api(url, {
method: 'post',
body: opts.text
}, 'text');
if (response !== 'ok') {
throw new Error(
singleLine`
[error] the response returned with error "${response}"
`
);
}
return response;
}

/**
Expand All @@ -246,13 +228,8 @@ export default class BaseAPI extends EventEmitter {
* @return {Promise} - the resolved & parsed JSON response
*/
async api(endpoint, opts, type='json') {
let response;
try {
response = await fetch(endpoint, opts);
return await response[type]();
} catch (error) {
throw new Error(error);
}
let response = await fetch(endpoint, opts);
return await response[type]();
}

}
Expand Down
5 changes: 3 additions & 2 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ export default class Slack {
}

for (let method of WEB_METHODS) {
let [namespace, submethod] = method.split('.');
let name = method.name;
let [namespace, submethod] = name.split('.');
if (this[namespace] == null) this[namespace] = {};
this[namespace][submethod] = opts => {
return Bacon.fromPromise(
this.slack.method(method, opts)
this.slack.method(name, opts)
);
}
}
Expand Down
Loading

0 comments on commit 379f1cc

Please sign in to comment.