【AWS】API GatewayとLambdaを使ってBacklog Webhookを扱ってみた
はじめに
こんにちは植木和樹@上越妙高オフィスです。本日はAWS API GatewayとLambdaを用いてBacklogと各種サービスを連携させる仕組みを作ってみました。その際に苦労した点などのを書き記しておこうと思います。
背景
クラスメソッドでは案件の進捗管理にBacklogを用いています。主に開発や構築案件で用いているわけですが、インターフェースがわかりやすいため、そのまま保守運用フェーズでも使いたいというお客様が多くいらっしゃいます。
しかしBacklogは元々個人単位でチケットを管理するのが目的のツールのため、チームでサポート業務でするには少々使いがってが悪い部分があります。
- たくさんの複数のプロジェクトにサポートメンバー全員の登録が必要
- 問い合わせ時に誰にチケットをアサインしたらいいのかわかりづらい
- アサインされた人が当日非番だったりすると、チケットの確認・対応が遅れる可能性がある
そのためサポート業務ではITILのSPOCに基づき、チーム共用ユーザーを用意して窓口を一本化しています。お客様には運用時の作業依頼の際に、この「オペユーザー」を担当者としてチケットを起票してもらうわけです。
しかし共用ユーザーを使うと、今度はそのチケットがを誰が担当しているのかBacklog上で管理するのが難しくなってしまいます。そこでクラスメソッドではOTRSというツールを使って、Backlogとは別に社内チケットで担当者を別途管理しています。Backlogではチケットの作成や更新が行われるとメールでお知らせする仕組みがありますので、そのメールをOTRSに取り込んでチケット化して管理しているわけです。一言でいえば二重管理です。
長年この二重管理が不便でしたので、今回API GatewayとLambdaを用いてチケットステータスを同期させる仕組みを作ってみました。
システム構成
Backlog Webhook
BacklogのWebhookの仕組みは下記ページに解説があります。
Webhookはプロジェクト単位で設定します。設定にはスペースの管理者権限が必要です。
Lambda
LambdaではBacklog WebhookからPOSTされてきたJSONデータを元に処理を行います。node.js ならJSONは手軽に扱えますので、あとはデータに応じた条件分岐と各種サービスAPIをたたく処理を実装します。詳細は後述。
API Gateway
API GatewayがまだTokyoリージョンでサービス開始していないため、ここだけVirginia(us-east-1)を用いています。リージョンが異なってもちゃんとAPI GatewayからLambdaを呼び出せるようです。
API GatewayからLambdaを呼び出すための設定は下記が参考になるかと思います。
Chatwork
Chatworkにもメッセージを送ってみました。今回は単純にチケットの内容を整形して書きんでいるだけで凝った使い方はしていません。
OTRS(SES)
OTRSとの連携には悩みました。どうやらWeb APIを持っているようなのですが、拡張性と汎用性が高いらしく具体的な呼び出し方法を解説したページがないのです。
そこでWeb APIでの連携はひとまず見送り、メールにOTRS固有のヘッダーを埋め込むことでステータス変更をさせることにしました。SESのsendRawEmailを使うとカスタムのメールヘッダーを入れることができます。
S3
今回はデバッグ目的でWebhookで渡されたJSONをS3へ保存しています。テキストファイルなのでサイズはほとんど気にしなくていいですが、BucketにLifeCycleを設定して自動削除するようにしておくと安心です。
コードと解説
コードは長いので gist にいてあります。きれいなコードではないですが、参考になれば幸いです。
node.js で非同期処理の扱いがポイント
呼び出されたLambdaのコードが、Chatworkへのリクエストを投げてレスポンスが返るまでに終了してしまうという症状に悩まされていました。最初のうちはうまくいっていたのに、コードを追加したところ動かなくなるという不思議。
これは node.js プログラム初心者がはまりやすいポイントらしいです。Chatworkのrequest処理やAWS API呼び出しは非同期のため、メインの処理は呼び出しの完了を待たずに終わってしまう場合があります。そのためちゃんと待ってから処理を終了する必要があります。
node.js での同期処理にはasyncを使うのが定石のようですので、今回は以下のようにして同期をとりました。Chatwork/SES/S3への各処理は順序を気にしないためparallelで並行処理しています。
exports.handler = function (event, context) { var body; console.log('event: ' + JSON.stringify(event, null, 4)); body = event; console.log('json parsed'); async.parallel([ function(callback) { post2Chatwork(body, callback) }, function(callback) { post2OTRS(body, callback) }, function(callback) { put2Bucket(body, callback) }, ], function(err, results) { console.log('Done.'); context.done(err); }); };
各関数にcallbackを渡して、処理が完了したら終わったことを通知させるわけですね。
AWS API呼び出しはサンプルを拝借する
node.js に慣れないうちは、どこから書きはじめれば良いのか分からないものです。AWSのドキュメントにはサンプルコードが掲載されていますので、遠慮なく拝借してきましょう。
これを元に書きかえることで徐々にコツがつかめてくるはずです。
デバッグ用ログは多めにしておく
Lambdaではconsole.log()で出力されたログメッセージをCloudWatch Logsへ出力させることができます。上記の通りnode.jsは非同期処理のため、コードの記述順に実行が行われないことが多々あります。慣れないうちはコードがどこでおかしくなっているのか調べるのが難しいため、ログは多めに出しておくのがオススメです。
また request モジュールで外部APIを呼び出す際にも、デバッグを有効にしているとリクエストとレスポンスの内容が見えて便利です。
var request = require('request'); require('request').debug = true
まとめ
Backlog WebhookとAPI Gateway + Lambdaで各種サービスを連携させるネタは7月末頃からいろいろ調べていたのですが、なぜかBacklog WebhookがAPI Gatewayを叩けない(ログも残らない)という問題があり、ヌーラボさんに報告だけしてずっと放置していました。
先日ヌーラボの中の方から、9/14のリリースで対応したので試してみてね!というメールをいただき試してみたところ、サクっと動きました。このブログの場を借りてお礼とご報告させていただきます。ありがとうございました!! (本当は9/15中に公開する予定だったのですが、node.jsの非同期処理に半日くらいハマってまして遅れてしまいました)
node.js プログラムのコツがわかれば、Webhook + API Gateway + Lambdaで本当に簡単に処理が実装できます。お金もほとんどかからないので、ぜひお試しください。 次のJAWS-UG上越妙高はLambdaハンズオンもいいなぁ。