Posted at

lambda入門(Node)③ - API Gatewayを使ってslackからのリクエストをlambdaで受けられるようにする

More than 1 year has passed since last update.

第3回になりました。

過去のはこちら。

今回は、ようやくやりたいことに近づいて来まして

slackからのリクエストをlambdaで受けられるようにしたいと思います。

何か調べていくと、どうもAPI Gatewayを使うと良い感じぽい。

まずはAPI Gatewayについて予習を。


API Gateway


どんな役割をしてくれるのか

APIのエンドポイントとして待ち構える玄関として使える

現在はhoge/*のようなパスを/hoge/{proxy+}として設定できる

プロキシリソースなるものらしい

post, putの振り分けなどが簡単になった


課金体系

受信した API 呼び出しと、送出したデータ量に対して発生


serverless frameworkでの設定


serverless.yml

functions:

bookStore:
handler: books/store.store
events:
- http:
path: books
method: get
cors: true

これだけ。

デプロイ実行すると

$ serverless deploy -v --stage dev

Serverless: Packaging service...


CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Deployment - ApiGatewayDeployment1490662361327
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Deployment - ApiGatewayDeployment1490662361327
CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::Deployment - ApiGatewayDeployment1490662361327


Service Information
service: testProject
stage: dev
region: ap-northeast-1
api keys:
None
endpoints:
GET - https://xxxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/books
functions:
bookStore: testProject-dev-bookStore

エンドポイントが作られた。。!

レスポンスを送れるか試してみる

$ curl https://xxxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/books

{"message":"ok",...........}

うん、okそう、すごい。ymlに書くだけで何でも設定してくれちゃう。


Slackから応答させるようにする

ここで今回の本題。

作成したエンドポイントに対してスラックがリクエストを送って

slack上にメッセージが返ってくるようにする。

全体像

やることとしては、リクエストパラメータを解析してデータを保存。

保存した結果を返す


book.js


'use strict';

const slackAuthorizer = require('../authorizer/slackAuthorizer');
const parser = require('../service/queryParser');

const bookSave = require('../useCase/book/save.js');

module.exports.book = (event, context, callback) => {
const queryParser = new parser(event.body);
const authorizer = new slackAuthorizer(queryParser.parseToken());

/**
* 認証
*/

if (!authorizer.authorize()) {
context.done('Unauthorized');
}

/**
* @Todo ここで lambda function の振り分けを行いたい
*/

bookSave(event, (error, result) => {});

const response = {
statusCode: 200,
body: JSON.stringify({
message: 'ok',
}),
};

callback(null, response);
};


同期で処理を行いたいので asyncを使って処理をブロックごとに実行する


save.js


'use strict';

const AWS = require('aws-sdk');
const dynamoDB = new AWS.DynamoDB.DocumentClient();
const bookTable = process.env.bookTable;

const uuidV1 = require('uuid/v1');

const webhookUrl = process.env.slack_webhook_url;
const request = require('request');

const dateTime = require('node-datetime');
const dt = dateTime.create();
const insertDate = dt.format('Y-m-d H:M:S');

const async = require('async');

const parser = require('../../service/queryParser');

module.exports = (event, callback) => {

const queryParser = new parser(event.body);

const key = uuidV1();

async.series([
function(callback) {

/**
* データを保存
*/

dynamoDB.put({
'TableName': bookTable,
'Item': {
'id': key,
'title': queryParser.parseText(),
'insert_date': insertDate,
},
}, function(err, data) {
callback(null, "saved");
});
},
function(callback) {

/**
* 保存したものを取り出して
* 結果を返す
*/

dynamoDB.get({
TableName: bookTable,
Key: {
id: key,
},
}, function(err, data) {
if (!err) {
const response = {
text: `\`${data.Item.title}\` is Saved !!`,
};
  
/**
* webhook でチャンネルにメッセージを返す
*/

request.post(webhookUrl, {
form: {
payload: JSON.stringify(response),
},
}, (err, response, body) => {
callback(null, 'getData');
});
}
});
},
], function(err, results) {
if (err) {
throw err;
}
});
};


slash commandから送られてきたパラメータをパースするために

query-stringモジュールを使い、ラップした


queryParser.js

'use strict';

const queryStringParser = require('query-string');

module.exports = class queryParser {

constructor (queryString) {
this.queryString = queryString;
}

parseToken () {
return queryStringParser.parse(this.queryString).token;
}

parseText () {
return queryStringParser.parse(this.queryString).text;
}
};


slashコマンドの設定は省略します。

最初の api gatewayで設定されたエンドポイントのURLを

設定してあげればいいので

実際に動かすとこんな感じです。

ソースは汚いかもしれませんが、saverlessフレームワークで

エンドポイント作成やdb、外部サービスの連携が簡単に出来ました

もっとキレイに書けるようにjsの筋力をつけていかねば。。笑