Skip to content

Commit

Permalink
Merge pull request #27 from EPICmynamesBG/feature/25_slack_app_package
Browse files Browse the repository at this point in the history
Feature/25 slack app package
  • Loading branch information
EPICmynamesBG committed Dec 26, 2017
2 parents 739d169 + 70c05d4 commit d568a39
Show file tree
Hide file tree
Showing 40 changed files with 1,615 additions and 1,725 deletions.
14 changes: 6 additions & 8 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
language: node_js
dist: trusty
sudo: false
node_js:
- "8.0.0"
services:
- mysql
env:
global:
- PORT=3000
- DATABASE_HOST=localhost
- DATABASE_USER=root
- DATABASE_NAME=subito_test
- ENCRYPTION_KEY=test_key_123
- ADMIN_AUTH_SECRET=helloworld
- TEST_LOGGING_LEVEL=fatal
- TEST_DATABASE_HOST=localhost
- TEST_DATABASE_USER=root
- TEST_DATABASE_NAME=subito_test
- SLACK_VERIFICATION_TOKEN=token
addons:
mysql: "5.7.19"
before_install:
Expand All @@ -22,9 +21,8 @@ install:
- npm install -g swagger
before_script:
- mysql -D subito_test < ddl/ddl.sql
- npm run migrate-up:local
- npm run migrate-up:test
script:
- npm run eslint
- npm run coverage
- npm run test
- npm run coverage
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

A slash command Slack bot for getting the daily soup selection from Indianapolis' [_Subito_](http://www.subitosoups.com/)

<a href="https://slack.com/oauth/authorize?client_id=19000326018.274092194038&scope=commands,chat:write:bot,bot,team:read"><img alt="Add to Slack" height="40" width="139" src="https://platform.slack-edge.com/img/add_to_slack.png" srcset="https://platform.slack-edge.com/img/add_to_slack.png 1x, https://platform.slack-edge.com/img/add_to_slack@2x.png 2x" /></a>

## Usage
```text
/subito - what's on the menu today!
Expand Down Expand Up @@ -54,7 +56,8 @@ _ *You can only be subscribed once_

## In Development

- Packaging to be a Slack App!
- Customizable subscription notification time
- Subscribe to multiple criteria

## Docs

Expand All @@ -77,7 +80,7 @@ _ *You can only be subscribed once_

- mysql
- node/npm (recommend using nvm)

#### Setup

- `mysql -e "CREATE DATABASE IF NOT EXISTS subito;" -u root -p`
Expand All @@ -104,8 +107,8 @@ PORT=3000
SSL_PORT=443
SSL_PRIV_KEY=[absolute path to key]
SSL_CERT=[absolute path to cert]
SSL_CA=[absolute path to ca/chain]
ENCRYPTION_KEY=[random key]
ADMIN_AUTH_SECRET=[random string]
TEST_LOGGING_LEVEL=fatal
LOGGING_LEVEL=debug
NODE_ENV=development
Expand All @@ -117,7 +120,7 @@ By design, the `swagger.yaml` file is git-ignored. This is to leverge dynamic va

#### Database Changes

Database changes should be added in the `db-migrations/sql` folder. File names should be something like `[migration id]-[hyphen case description]-[up | down].sql`, with a corresponding up and down file.
Database changes should be added in the `db-migrations/sql` folder. File names should be something like `[migration id]-[hyphen case description]-[up | down].sql`, with a corresponding up and down file.

Migrations can be ran via `npm run migrate-up:[local | test]`. This command will process and run _all_ up migrations.
`npm run migrate-down:[local | test]` will run _only the last_ migration down script, so undoing a migration will happen one at a time.
Expand All @@ -130,7 +133,7 @@ Migrations can be ran via `npm run migrate-up:[local | test]`. This command will
- `mysql -e "CREATE USER 'test'@'localhost' IDENTIFIED BY '[password]';" -u root -p`
- `mysql -e "GRANT ALL PRIVILEGES ON subito_test . * TO 'test'@'localhost';" -u root -p`
- `mysql -D subito_test < ddl/ddl.sql -u root -p`

## License

[GNU General Public License v3.0](http://www.gnu.org/licenses/gpl-3.0.txt)
102 changes: 63 additions & 39 deletions api/controllers/slackController.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,57 +5,81 @@ const errors = require('common-errors');
const logger = require('../helpers/logger');
const utils = require('../helpers/utils');
const slackUtils = require('../helpers/slack').utils;
const SUPPORTED_COMMANDS = require('../../config/constants').SLACK_CONSTS.SUPPORTED_COMMANDS;
const CMD_USAGE = require('../../config/constants').SLACK_CONSTS.CMD_USAGE;
const { CMD_USAGE, SUPPORTED_COMMANDS } = require('../../config/constants').SLACK_CONSTS;

const authService = require('../services/authService');
const oauthService = require('../services/oauthService');
const soupCalendarController = require('./soupCalendarController');
const subscriberController = require('./subscriberController');

function handleSlack(req, res) {
const params = utils.camelCase(req.body);
authService.validateTeamToken(req.db, params.teamId, params.token, (valid) => {
if (!valid) {
logger.warn('Bad auth', params);
utils.processResponse(new errors.HttpStatusError(403, 'Invalid Slack token'), null, res);
return;
}

const action = slackUtils.parseRequestCommand(params);
lodash.set(req, 'fromSlack', true);
switch (action.command) {
case 'subscribe':
lodash.set(req, 'body.slackUserId', action.params.user.id);
lodash.set(req, 'body.slackUsername', action.params.user.username);
lodash.set(req, 'body.slackTeamId', action.params.user.teamId);
lodash.set(req, 'body.searchTerm', action.params.search);
subscriberController.subscribe(req, res);
break;
case 'unsubscribe':
lodash.set(req, 'body.slackUserId', action.params.user.id);
lodash.set(req, 'body.slackUsername', action.params.user.username);
lodash.set(req, 'body.slackTeamId', action.params.user.teamId);
subscriberController.unsubscribe(req, res);
break;
case 'search':
lodash.set(req, 'swagger.params.search.value', action.params.search);
soupCalendarController.search(req, res);
break;
case 'day':
lodash.set(req, 'swagger.params.day.value', action.params.day);
soupCalendarController.getSoupsForDay(req, res);
break;
default: {
logger.warn('Unsupported command', action.command);
let message = "Whoops, I don't recognize that command. Try one of these instead!";
SUPPORTED_COMMANDS.forEach((cmd) => message += `\n>${cmd} ${CMD_USAGE[cmd]}`);
utils.processResponse(null, { text: message }, res);
if (!oauthService.validateTeamToken(params.token)) {
logger.warn('Bad auth', params);
utils.processResponse(new errors.HttpStatusError(403, 'Invalid Slack token'), null, res);
return;
}

const action = slackUtils.parseRequestCommand(params);
lodash.set(req, 'fromSlack', true);
switch (action.command) {
case 'subscribe':
lodash.set(req, 'body.slackUserId', action.params.user.id);
lodash.set(req, 'body.slackUsername', action.params.user.username);
lodash.set(req, 'body.slackTeamId', action.params.user.teamId);
lodash.set(req, 'body.searchTerm', action.params.search);
subscriberController.subscribe(req, res);
break;
case 'unsubscribe':
lodash.set(req, 'body.slackUserId', action.params.user.id);
lodash.set(req, 'body.slackUsername', action.params.user.username);
lodash.set(req, 'body.slackTeamId', action.params.user.teamId);
subscriberController.unsubscribe(req, res);
break;
case 'search':
lodash.set(req, 'swagger.params.search.value', action.params.search);
soupCalendarController.search(req, res);
break;
case 'day':
lodash.set(req, 'swagger.params.day.value', action.params.day);
soupCalendarController.getSoupsForDay(req, res);
break;
default: {
logger.warn('Unsupported command', action.command);
let message = "Whoops, I don't recognize that command. Try one of these instead!";
SUPPORTED_COMMANDS.forEach((cmd) => message += `\n>${cmd} ${CMD_USAGE[cmd]}`);
utils.processResponse(null, { text: message }, res);
}
}
}

function handleOAuth(req, res) {
if (!lodash.has(req, 'query.code')) {
utils.processResponse(new errors.HttpStatusError(400, 'Missing code'), null, res);
return;
}
if (lodash.has(req, 'query.error')) {
// TODO: Maybe this should disable an oauth_integration?
logger.warn('OAuth error', req.query.error);
utils.processResponse(new errors.HttpStatusError(400, req.query.error), null, res);
return;
}
oauthService.processOAuth(req.db, req.query, (err, results) => {
if (err) {
utils.processResponse(err, null, res);
return;
}
if (lodash.get(results, 'team.domain', null) !== null) {
const domain = results.team.domain;
res.redirect(`https://${domain}.slack.com`);
return;
}
utils.processResponse(null, { text: 'Subito-Suboto registered!' }, res);
});
}


module.exports = {
handleSlack: handleSlack
handleSlack: handleSlack,
handleOAuth: handleOAuth
};
66 changes: 0 additions & 66 deletions api/controllers/teamIntegrationController.js

This file was deleted.

13 changes: 7 additions & 6 deletions api/helpers/cronHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,28 +40,29 @@ const _processSubscriber = (db, subscriber, soups, callback) => {
(err, searchResults) => {
if (err) cb(err);
else if (searchResults.length > 0) cb(null, soups);
else cb(new Error(`no soups for "${subscriber.search_term}" found today`));
else cb({ clean: true, error: new Error(`no soups for "${subscriber.search_term}" found today`)});
});
},
message: (searchResults, cb) => {
if (searchResults) {
const message = _buildCustomText(subscriber.search_term, searchResults.soups);
slack.messageUser(subscriber.slack_username, message, subscriber.slack_webhook_url, (err, res) => {
slack.messageUserAsBot(subscriber.slack_user_id, message, subscriber.slack_slash_token, (err, res) => {
if (err) callback(err);
else if (res.status === 'fail') callback(res);
else if (!res.ok) callback(res);
else callback(null, res);
});
} else {
cb(null, null);
}
}
}, (err) => {
if (err) logger.error('_processSubscriber', subscriber, err);
if (err && err.clean) logger.debug('_processSubscriber', subscriber, err);
else if (err) logger.error('_processSubscriber', subscriber, err);
callback();
});
} else {
slack.messageUser(subscriber.slack_username, soups.text, subscriber.slack_webhook_url, (err, res) => {
if (err || res.status === 'fail') logger.error('_processSubscriber', subscriber, err, res);
slack.messageUserAsBot(subscriber.slack_user_id, soups.text, subscriber.slack_slash_token, (err, res) => {
if (err || !res.ok) logger.error('_processSubscriber', subscriber, err, res);
callback();
});
}
Expand Down
9 changes: 7 additions & 2 deletions api/helpers/queryHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,15 @@ function _queryBuilder(table, queryType, valuesParam, whereParams = {}) {
};
}

/* eslint-disable complexity */
function _resultsHandler(err, results, callback, context = 'No context provided') {
if (err) {
logger.error(err, context);
callback(err);
return;
}
if (!results || results.length === 0) {
logger.info('No results', context);
logger.debug('No results', context);
callback(null, null);
return;
}
Expand Down Expand Up @@ -130,10 +131,14 @@ function _resultsHandler(err, results, callback, context = 'No context provided'
}
callback(null, lodash.toPlainObject(results));
}
/* eslint-enable complexity */

function _query(db, build, callback) {
let paramArr = [];
if (build.values.length > 0 && build.queryType !== QUERY_TYPE.UPDATE) {
if (build.values.length === 0 && build.queryType === QUERY_TYPE.INSERT) {
module.exports.private.resultsHandler(null, [], callback, build);
return;
} else if (build.values.length > 0 && build.queryType !== QUERY_TYPE.UPDATE) {
paramArr = [build.values];
} else if (build.queryType === QUERY_TYPE.UPDATE) {
paramArr = build.values;
Expand Down

0 comments on commit d568a39

Please sign in to comment.