Serverless FrameworkでChatwork通知用APIを作成する方法

こんにちは。串上です。
前回ReactとAWS API Gatewayの連携方法を書いた者です。

前回の記事を読んでいただいた方は思ったかもしれませんが、API Gatewayって手動で設定するの面倒ですよね。

そこで今回はそのあたりの設定をコマンド一発で自動化してくれる素敵なフレームワークであるServerless Framework(v1.1)について書きたいと思います。 https://serverless.com/

Serverless Frameworkを利用するにあたって、 AWS CLIAWSコマンドラインからごにょごにょするツール)をインストールする必要があるので、まずはそちらから。

pipをインストール

curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py"
sudo python get-pip.py

え、AWS CLIちゃうん?って思った方、そうあせらないでください。
AWS CLIをインストールするためにはPythonのパッケージ管理ツールpipが必要なんです。
とりあえず入れて下さい。

こんな感じになればインストール完了です。

Collecting pip
  Downloading pip-9.0.1-py2.py3-none-any.whl (1.3MB)
    100% |████████████████████████████████| 1.3MB 816kB/s
Collecting wheel
  Downloading wheel-0.29.0-py2.py3-none-any.whl (66kB)
    100% |████████████████████████████████| 71kB 5.9MB/s

次に行きます。

AWS CLIをインストール

sudo pip install awscli

で、OKと思ったらsixがどうちゃら言われてエラーが出たら、以下の魔法版で実行して下さい。

sudo pip install awscli --upgrade --ignore-installed six

するとこんな感じにインストールが完了します。

Collecting awscli
  Downloading awscli-1.11.15-py2.py3-none-any.whl (1.0MB)
    100% |████████████████████████████████| 1.0MB 804kB/s
Collecting six
  Downloading six-1.10.0-py2.py3-none-any.whl
...
Successfully installed awscli-1.11.15 botocore-1.4.72 colorama-0.3.7 docutils-0.12 futures-3.0.5 jmespath-0.9.0 pyasn1-0.1.9 python-dateutil-2.6.0 rsa-3.4.2 s3transfer-0.1.9 six-1.10.0

AWS CLIのインストールは完了しましたが、このままではまだ「だれのAWSに接続するん?」って話なんで、そのあたりも設定していきます。

AWS CLIの設定

aws configure

ってコマンドを打つと、以下のように質問してくるので答えます。

AWS Access Key ID [None]: xxxxxxxxxx
AWS Secret Access Key [None]: yyyyyyyyyy
Default region name [None]: ap-northeast-1
Default output format [None]: json

region nameとoutput formatは好きに変えて下さい。
自分はjson好きの日本在住なので上記の設定にしています。

xxxxxxxxxxとyyyyyyyyyyはAWSのユーザーを作成したときの一回だけしかダウンロードまたは表示ができないアレです。
一回だけだなんて厳しいですよね。
しかもユーザー作成したときなんて何にもわかってないときやのに、ダウンロードってなんやねんって感じですよね。
でもダウンロードはしといてください。
そして安全な場所に保管しておいて下さい。
確かファイル名はcredentialsだったような。

これが完了すると、~/.aws/credentialsっていうファイルが出来ます。 中身はこんな感じ。

[default]
aws_access_key_id = xxxxxxxxxx
aws_secret_access_key = yyyyyyyyyy

これでAWS CLIが使えるようになったので、ついにServerless Frameworkをインストールします。

※Serverless Frameworkをインストールするにはnpmが必要です。
npmって何?ってひとはこちらを参考にしてください。

Serverless Frameworkをインストール

npm install -g serverless

こんな感じになればインストール完了です。

└─┬ serverless@1.1.0 
  ├── agent-base@2.0.1 
  ├── ansi-regex@2.0.0 
  ├── ansi-styles@2.2.1 
  ├─┬ archiver@1.1.0 
  │ └── async@2.1.1 
...

これでserverlessコマンドが使えるようになったんですが、いちいちserverlessとか長いしタイポしそうなんで、
slsって短縮コマンドも用意されています。

早速プロジェクトを作成しましょう!

Serverless Frameworkでプロジェクトを作成

mkdir serverless-chatwork
cd serverless-chatwork
sls create --template=aws-nodejs

するとこんな感じにカッコイイロゴが表示されれば、プロジェクトの作成は完了です。

Serverless: Generating boilerplate…
 _______                             __
|   _   .-----.----.--.--.-----.----|  .-----.-----.-----.
|   |___|  -__|   _|  |  |  -__|   _|  |  -__|__ --|__ --|
|____   |_____|__|  \___/|_____|__| |__|_____|_____|_____|
|   |   |             The Serverless Application Framework
|       |                           serverless.com, v1.1.0
 -------'

Serverless: Successfully generated boilerplate for template: "aws-nodejs"
Serverless: NOTE: Please update the "service" property in serverless.yml with your service name

nodejs?僕パイソニスタなんだけど。って人はpython版で作成してください。 templateのaws-nodejsをaws-pythonに変更すればOKです。

ではプロジェクトの設定をしていきましょう。

Serverless Frameworkプロジェクトの設定

serverless.ymlというファイルがあるので以下のように編集します。

service: serverless-chatwork # プロジェクト名です。好きな値をどうぞ。

provider:
  name: aws
  runtime: nodejs4.3
#  stage: dev # デプロイする環境。デフォルトではdevになってる。開発環境なんていらんよ。って人はproduction等に変更しましょう。
  profile: default # ~/.aws/credentialsの中の[]の中の文字です。defaultの場合はこの行削除してもいいです。
  region: ap-northeast-1 # AWSのリージョンです。東京に変更したい場合はこんな感じ。

functions:
  push: # 作成するLambdaの名前
    handler: handler.push # handler.js内の関数名
    events:
      - http:
          path: v1/push # API Gatewayのエンドポイント v1とかバージョンを作っとくと大規模な改修の際に便利かも?
          method: post # get, post, put, delete等
          cors: true # これ設定するとCORSが有効になって外部からも叩けるよ

plugins:
  - serverless-run-function-plugin # あとで説明するけど、これ入れとくとローカルでテストできます。

上の設定で出て来るserverless-run-function-pluginですが、デプロイする前にローカルでAPIのテスト出来るようになるので入れておきましょう。

serverless-run-function-pluginのインストール

npm install -g serverless-run-function-plugin

早速ローカルでテストを実行する前に、handler.jsも修正しましょう。 handler.jsはLambdaの中身になります。

handler.jsとevent.jsonの設定

今回は以下のように修正して下さい。 簡単に補足するとevent.jsonはコマンドから実行するときにhandler.jsのeventに任意の値を設定するためのものです。

handler.js

'use strict'; // 厳しいモードでいきましょう。

const request = require('request');

const CHATWORK_TOKEN = 'チャットワークAPIを利用するためのトークン';

module.exports.push = (event, context, callback) => {
  var response = {
    statusCode: 200,
    // CORSを有効にする場合は設定必要。*は全てなので許可する範囲を設定したい人は修正してください。
    headers: {
      "Access-Control-Allow-Origin" : "*"
    },
  };

  // postでjson文字列を送信した場合、受け取ったデータは全てevent.bodyに入っています
  const post = JSON.parse(event.body);

  // room_idは必須にする
  if(typeof post.room_id == 'undefined'){
    response.statusCode = 400;
    response.body = JSON.stringify({ message: 'room_idを指定して下さい。' });
    return callback(null, response);
  }

  // textは必須にする
  if(typeof post.text == 'undefined'){
    response.statusCode = 400;
    response.body = JSON.stringify({ message: 'textを指定して下さい。' });
    return callback(null, response);
  }

  // チャットワークにチャット送信
  pushToChatwork(post.room_id, post.text);

  response.body = JSON.stringify({ message: '送信完了' });
  callback(null, response);

};

// チャットワークにチャット送信
const pushToChatwork = (room_id, text) => {
  var options = {
    url: 'https://api.chatwork.com/v1/rooms/' + room_id + '/messages',
    headers: {
      'X-ChatWorkToken': CHATWORK_TOKEN
    },
    form: { body: text },
    json: true
  };
  request.post(options);
}

event.json

{
  "body": "{\"room_id\": \"チャットワークのルームID\", \"text\": \"投稿したい文字列\"}"
}

また、今回requestパッケージを使用したので、npmの下準備をしてインストールしましょう。

npm init

Press ^C at any time to quit.
name: (serverless-chatwork) 
version: (1.0.0) 
description: Chatwork API with Serverless Framework
entry point: (handler.js) 
test command: 
git repository: 
keywords: 
author: 
license: (ISC) 
About to write to /Users/shunkushigami/tmp/serverless-chatwork/package.json:

{
  "name": "serverless-chatwork",
  "version": "1.0.0",
  "description": "Chatwork API with Serverless Framework",
  "main": "handler.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

requestのインストール

npm install request --save

これでnode_modulesというディレクトリが出来て、デプロイ時に一緒にアップしてくれます。

ローカルでテストを実行

sls run -f push

-fの後に実行する関数名を指定すると実行してくれます。

こんな感じに表示されて、

Serverless: -----------------
Serverless: Success! - This Response Was Returned:
Serverless: {
    "statusCode": 200,
    "headers": {
        "Access-Control-Allow-Origin": "*"
    },
    "body": "{\"message\":\"送信完了\"}"
}

指定のルームにチャットが届けば成功です!

今のままではローカルでしか実行できないので、いよいよデプロイします。

Serverless FrameworkでAWSにデプロイする

sls deploy

たったのこれだけです。
Serverless Frameworkやばいですね。 これだけでCloudFormationを作成してくれて、それらをS3にアップしてくれて、Lambda作ってくれて、API Gateway作ってくれます。

こんな感じでエンドポイントが表示されればデプロイ完了です。

Serverless: Creating Stack…
Serverless: Checking Stack create progress…
..
Serverless: Stack create finished…
Serverless: Deprecation Notice: Starting with the next update, we will drop support for Lambda to implicitly create LogGroups. Please remove your log groups and set "provider.cfLogs: true", for CloudFormation to explicitly create them for you.
Serverless: Packaging service…
Serverless: Uploading CloudFormation file to S3…
Serverless: Uploading service .zip file to S3…
Serverless: Updating Stack…
Serverless: Checking Stack update progress…
.................
Serverless: Stack update finished…

Service Information
service: serverless-chatwork
stage: dev
region: ap-northeast-1
api keys:
  None
endpoints:
  POST - https://xxxxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/v1/push
functions:
  serverless-chatwork-dev-push: arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxxxxxx:function:serverless-chatwork-dev-push

では、AWSにアップされたようなのでコマンドから実際にエンドポイントが効いているか試してみましょう。

Serverless FrameworkでAPIをコールする

sls invoke -f push -p event.json

このコマンドで実際にリモートのAPIをevent.jsonの内容でコールしてくれます。
-pで使用するevent用jsonを指定します。

以下のように表示されて、実際にチャットが届けば完了です!

{
    "statusCode": 200,
    "headers": {
        "Access-Control-Allow-Origin": "*"
    },
    "body": "{\"message\":\"送信完了\"}"
}

作ったはいいけど、他の人に試してもらうためには以下のように教えてあげましょう。

jQueryから試す

jQueryマイスターには以下で試してもらいましょう。

var data = {
    room_id: "チャットワークのルームID",
    text: "投稿したい文字列"
}
$.ajax({
    url: "生成したエンドポイント",
    type: "POST",
    dataType: "JSON",
    data: JSON.stringify(data)
});

Curlから試す

コマンドライン大好きな人には以下で試してもらいましょう。

curl 生成したエンドポイント -X POST -d "{\"room_id\": "チャットワークのルームID", \"text\": \"投稿したい文字列\"}"

まとめ

以上、長々と書きましたが、Serverless Frameworkを実践的に使用する方法を解説しました。
Serverless Frameworkとかサーバーレス自体に興味ある人はぜひ試してみて下さい。

次回以降はDynamoDBとの連携方法や、Lambdaの定期実行等も書いていきたいと思いますので、乞うご期待!

お疲れ様でした。