この記事にはこんなことが書かれています。
- webhookとは
- webhookを受ける口をつくってみよう(AWS活用)
- API Gateway
- Lambda
‐ Amazon SNS
■モチベーション
客先システム導入時に担当者から「ある処理の完了通知をwebhookで投げることができる」
と連絡をうけた。webhookとは何かから勉強することになったが
AWSでその受け皿をつくることができたのでその軌跡を書く
<webhook概要>
あるアプリケーションから別のアプリに対して、リアルタイムな情報提供を実現するための仕組み。
例えばインターネットであるホームページを見るときには、自身のPCは「この情報くれよ」とあるサーバーに対して問合せをして、サーバー側から「お前は誰?」とか「誰か確認できたからこの情報あげるね」とかをやり取りしている。
それに対してWebhookは、何かイベント発生をしたら、決められた情報を通知するだけの仕組みで、送信側・受信側ともに効率のいい方法です。
■webhookをもう少し詳しく
以下資料がわかりやすかったです↓
WebhookのWeb APIとの違い 〜イベントと通信に着目してみた〜
データ連携(Webhook)
まとめると、webhookは“広義のWeb API”(=以下、HTTP通信を主体としたインターフェース)
・http(s) GET
・http(s) POST
の一種であり、特にWebhookは、イベント駆動で情報を通知(=POST)するものということがわかりました。
つまり、情報をGETで得たりせず、イベント発生時に、相手システムにPOSTして情報を伝達するものという理解です。
ボディと呼ばれる、HTTPの中身に情報をいれて送受信しますが
JSONと呼ばれる形式で書くのが一般的なようです。
これはWebhookに依らずよく使われる形式で
非常に見やすく簡単な構造で以下のような感じです
{
"id":"{id}",
"value":"{value}",
"unit":"{unit}"
}
“id”部分をキーと呼び、“{ほにゃらら}”をバリューといいます
キーは何か概念を表すもので、バリューはその値です。非常にシンプルですね。
例えば、ある温度センサーから定期的に温度の値の値知する仕組みを作って、JSONでやり取りしましょうとなったらこんな感じでしょうか。
{
"id":"{1}",
"area":"{nagoya}",
"temperature":"{28.5}"
}
■webhookを実装する
AWSでwebhookを受信し自分にメール通知する仕組みを作ってみる。
まず結論から、こんな構成で作成しました。
Webhookの受信部はAPI Gatewayで作成し、エンドポイントを提供します。
Lambdaは、関数部で、API Gatewayが受け取ったデータを読み取り、ログやEmail作成のためにデータを加工します。
S3は、Lambdaで作成したデータを保存します。
Amazon SNSは、Lambdaで作成したデータを本文として、Emailに通知します。
以下サイトを参考に作成しました。非常に助かります。
Webhook参考1
Webhook参考2
本当はもっといろんなサイトを見ましたが、一番参考になったのがこの2つでした
■Lambda関数のソースサンプル
pythonの方が慣れていますが、上記参考サイトがjavascript形式でしたので見よう見まねで書いてみました。
初めてなので、色々とおかしいところあるかもしれません
'use strict';
const AWS = require('aws-sdk');
const s3 = new AWS.S3({ apiVersions: '2006-03-01' });
const bucketName = 'ここに名前を入れる';
//SNS通知部分--ここから--
var sns = new AWS.SNS({apiVersion: '2010-03-31', region: 'ap-northeast-1'});
function timer(ms, name){
console.log('name: ${name} start!')
return new Promise((resolve, resject) => {
setTimeout(() => resolve(name), ms)
})
}
//SNS通知部分--ここまで--
exports.handler = async (event, context) => {
// ファイル名のために、現在時刻を取得する
// LambdaのタイムゾーンはUTCのためJSTに変換する
const now = new Date(Date.now() + ((new Date().getTimezoneOffset() + (9 * 60 * 60 * 1000))));
// フォーマット通りに変換する
const fileName = now.toISOString().replace(/-|:|Z/g, '').replace(/T|\./g, '_');
const eventBody = JSON.parse(event.body);
// フォルダ名にデバイスIDを設定する
let dirName = getDeviceId(eventBody);
//SNS通知部分--ここから--
console.log('publish start')
await timer(1000, publish(event, context));
function publish(event, context) {
sns.publish({
Message: JSON.stringify(eventBody, null, 2),
Subject: '名前をいれる ' ,
TargetArn: 'arn:を入力する'
}, function(err, data) {
if (err) {
console.log(err.stack);
return "failed publish".err.stack;
}
console.log('publish sent');
console.log(data);
});
}
//SNS通知部分 --ここまで--
try {
// S3へデータを保存する。保存するファイルは「S3bucket/dirName/fileName.json」の形式となる。
const data = await s3.putObject(
createParamsToPutObject(bucketName, (dirName + '/' + fileName + '.json'), JSON.stringify(eventBody, null, 2)))
.promise();
// 成功したらログにSUCCESSを表示する
console.log('SUCCESS', data);
return createResponse(data.statusCode, data);
} catch (err) {
// 失敗したらログにERRORを表示する
console.log('ERROR', err.stack);
return createResponse(err.statusCode, err.message);
}
};
// S3への保存のためのパラメータ(JSON)を作る
const createParamsToPutObject = (bucket, key, body) => {
return {
Bucket: bucket,
Key: key,
Body: body,
ContentType: 'application/json'
};
};
// レスポンスを作る
const createResponse = (code, body, headers) => {
if (!headers) {
headers = {};
}
const response = {
statusCode: code,
body: JSON.stringify(body),
headers: headers
};
return response;
}
// リクエスト本文にデバイスIDが含まれていればデバイスを、そうでなければ'unknown'を返す
const getDeviceId = (jsonObject) => {
if ('device' in jsonObject) {
const deviceId = jsonObject.device.device_id;
if (deviceId) {
return deviceId;
}
}
return 'unknown';
}
■テスト方法
上記で紹介したURLを参考にコンソールのテストツールでもテストできますが
実際に他のサイトからwebhookを受けてみたいと思いました。以下を試しました。
WebブラウザでAPIを投げることができる
インストールしたりするのが大変だと思ったので調べてみると、Google Chromeの拡張機能で便利なものがありました。非常に使いやすく、1分くらいで使えちゃいます。
API Gatewayで作ったエンドポイントを入力して、適当にBodyを書いて、POSTするだけです。
操作画面は、以下の写真のような感じです。
※Bodyの内容が間違ってたり、エンドポイントを誤っているとサーバーエラーが出てしまいます
また、Webhookは色々なところで標準対応しているので、手短なところとしてGithubのWebhook通知との連携を試してみました。以下サイトを参考にしました
GithubでWebhook通知を行う
リポジトリにソースをアップしたら通知がくることを確認しました。
皆さんの参考になれば幸いです。即席で色々と書きましたが、詳しくは別の機会に。