はじめまして。
2021年9月に入社したエンジニアのひびきです。

今回は、AWSを使ってBraveGATEのWebhookを受け取りデータを保存するサンプルを作成しましたので、手順を共有します。


Webhookとは?

Webhookとは、ある特定のイベントが発生した際に、指定した宛先へ通知(データを送信)する仕組みのことです。
BraveGATEでは、センサーからのuplinkデータを通知するWebhookを利用することができます。


システム構成(概要)

作成したサンプルは下図のような構成になります。
 

AWS Cloud

AWS(Amazon Web Services)は、Amazonにより提供されているクラウドコンピューティングサービスです。
今回のサンプルはすべてAWSのサービスを利用して構築します。

Amazon API Gateway

API(Apllication Programming Interface)と呼ばれる、特定のアプリケーションへのアクセスを可能とする仕組みを作成できます。
データを受け取り保存するプログラムを利用できる公開APIを作成します。

AWS Lambda

イベントの発生に応じてプログラムを実行する環境を提供するサービスです。
リクエスト(Webhook)を受け取ったら、データを保存するJavaScriptプログラム(Node.js)を作成します。

Amazon S3

Amazon S3(Amazon Simple Storage Service)はストレージサービスです。
Webhookで受け取ったデータの保存先になります。


手順

前提条件

AWSのアカウントは既に準備されているものとします。
本稿ではあくまでサンプルのため、最低限の動作を行うものとなっております。
参考に実装される際は、セキュリティや権限などを考慮し、適切な環境設定をお願いいたします。

Lambda関数を作成する

AWSマネジメントコンソールから「すべてのサービス」→「Lambda」と進みます。
AWS Lambdaの画面から「関数の作成」をクリックして作成画面に進みます。
「一から作成」を選択し、以下の基本情報を入力してから、「関数の作成」をクリックすることでLambda関数が作成できます。

  • 関数名:[ 任意の名前 ]
  • ランタイム:Node.js 14.x
  • アクセス権限:デフォルトのまま  

Lambda関数をテスト実行する

Lambda関数が作成されると「Hello from Lambda!」を表示するコードが記述されているため、「Test」をクリックすることでそのままテスト実行することができます。

index.js
コードをコピー

exports.handler = async (event) => {
    // TODO implement
    const response = {
        statusCode: 200,
        body: JSON.stringify('Hello from Lambda!'),
    };
    return response;
};



「Test」をクリックすると、テストイベントの設定画面が開きます。
ここではまだ必要ではないので、イベント名に「test」(任意のイベント名)を入力して、「作成」をクリックします。

テストイベントが選択された状態で、再度「Test」をクリックします。
テストが実行され、「Excution results」のタブに結果が表示されます。
ここまででLambda関数の土台の作成は完了となります。

API GatewayでAPIを作成する

「関数の概要」のパネルから「+トリガーを追加」をクリックします。

API Gatewayを選択し、新しいAPIを作成するために「APIを作成する」を選択します。
以下で設定し、「追加」をクリックすることで、APIが作成されます。

  • APIタイプ : REST API
  • セキュリティ : オープン

画面下部にAPIの詳細が表示されます。
APIエンドポイントのURLをブラウザで開くと、"Hello from Lambda!"と表示されます。
これでAPIの作成が完了し、リクエストの応じてLambda関数が実行される環境が作成されました。

Amazon S3のバケットを作成する

バケットとはS3におけるデータを保存する場所のことです。
AWSマネジメントコンソールから「すべてのサービス」→「ストレージ」→「S3」をクリックします。
「バケットを作成」をクリックし、開いた画面で「バケット名」を任意の名前で入力後、「バケットを作成」をクリックします。
ここで使用する名前はほかの利用者を含め同じ名前を利用することができませんので、ご注意ください。

作成したLambda関数にS3へのアクセス権限を付与する

AWSマネジメントコンソールから「すべてのサービス」→「セキュリティ、ID、およびコンプライアンス」→「IAM」をクリックします。
IAM(AWS Identity and Access Management)はAWSのサービスやリソースのアクセス管理サービスです。
「アクセス管理」→「ロール」をクリックします。
作成したLambda関数のロールがすでに作成されており、検索バーにLambda関数名を入力すると表示されます。
表示されたLambda関数のロールをクリックします。

「アクセス権限」タブから「Permissions policies」の項目にある「ポリシーをアタッチします」をクリックします。

「AmazonS3FullAccess」にチェックを入れ、「ポリシーをアタッチ」をクリックします。
※検索ボックスに「S3」を入力すると見つけやすくなります。

「Permissions policies」に「AmazonS3FullAccess」が追加されていることを確認してください。

Lambda関数からS3へのアクセスができることを確認する

作成したLambda関数のコードを以下のように変更して、「Deploy」をクリックします。
※S3のバケット名のリストを取得するサンプルコードです。

Index.js
コードをコピー

'use strict';

const AWS = require('aws-sdk');

const s3 = new AWS.S3({apiVersions: '2006-03-01'});

exports.handler = async (event) => {
    
    try{
        const data = await s3.listBuckets().promise();
        console.log(data.Buckets);
    } catch (err){
        console.log(err);
    }
    
    const response = {
        statusCode: 200,
    };
    return response;
};



「Test」をクリックすると、Lambda関数が実行され、Function Logsにバケットリストが表示されます。


START RequestId: 9e7a3748-bdbc-4687-ad58-0888ee047f38 Version: $LATEST
2021-09-17T01:24:20.425Z 9e7a3748-bdbc-4687-ad58-0888ee047f38 INFO [
  { Name: 'myexampletest', CreationDate: 2021-09-17T00:40:43.000Z }
]
END RequestId: 9e7a3748-bdbc-4687-ad58-0888ee047f38
REPORT RequestId: 9e7a3748-bdbc-4687-ad58-0888ee047f38 Duration: 990.86 ms Billed Duration: 991 ms Memory Size: 128 MB Max Memory Used: 90 MB Init Duration: 464.17 ms


受け取ったデータをS3へ保存するプログラムを作成する

データをS3へ保存する処理をLambda関数で実装します。
BraveGATEのWebhookを受け取ることを想定し、以下のようなプログラムを実装します。

  • 受信したデータにデバイスIDの記述があれば、それをフォルダ名とする。記述がない場合は「unknown」というフォルダ名とする。
  • 関数が実行された日時(JST)を取得し、それをファイル名とする。(フォーマット:yyyymmdd_HHMMss_l)
  • データはJSON形式で、保存するファイルの拡張子は(.json)とする。
index.js
コードをコピー

'use strict';

const AWS = require('aws-sdk');
const s3 = new AWS.S3({ apiVersions: '2006-03-01' });

const bucketName = '(/* your bucket name */)';

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);

    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';
}


※「your bucket name」には作成したバケット名を記述してください。

作成したプログラムをテストする

テストイベントに以下を入力し、「変更を保存」をクリックします。


{
  "body": "{\"application\":{\"application_id\":\"APVrieKzXXXXXXXXXXXXXXXXXXXXXXXXXX\",\"name\":\"Test Webhook\"},
  \"router\":{\"router_id\":\"bb000001\",\"imsi\":\"440000000000001\",\"rssi\":-50,\"battery\":75},
  \"device\":{\"device_id\":\"2468800004000004\",\"sensor_id\":\"0004\",\"sensor_name\":\"Temperature sensor\",
  \"rssi\":-30,\"data\":{\"url\":\"https://placehold.jp/3d4070/ffffff/150x150.jpg?text=%E3%83%86%E3%82%B9%E3%83%88\"}},
  \"uplink_id\":\"1111aaaa-4222-bbbb-3333-cccc4444dddd\",\"date\":\"2018-10-23T00:00:00+09:00\"}"
}


※全体が見えるように改行を入れています。「フォーマット」をクリックすることで、改行を取り除けます。
「テスト」をクリックすると、テストが実行され結果が表示されます。

S3に保存されたデータを確認する

上記のテストでS3へデータが保存されたことを確認します。
S3の画面から「バケット」→「(作成したバケット名)」をクリックし、オブジェクトを表示します。

フォルダを選択すると、JSONファイルが実行した日時で登録されていることが確認できます。

「ファイル名」をクリックし、画面右上の「開く」をクリックすると、JSONデータを確認することができます。

APIを利用してS3に保存するまでの疎通テストを実施する

Lambda関数の画面からAPI Gatewayのトリガーをクリックし、表示されたAPI名をクリックします。

「テスト」をクリックします。
メソッドを「POST」に、リクエスト本文に以下を入力し、「テスト」をクリックします。

リクエスト本文
コードをコピー

{
  "application": {
  "application_id": "APVrieKzXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "name": "Test Webhook"
  },
  "router": {
  "router_id": "bb000001",
  "imsi": "440000000000001",
  "rssi": -50,
  "battery": 75
  },
  "device": {
  "device_id": "2468800004000004",
  "sensor_id": "0004",
  "sensor_name": "Temperature sensor",
  "rssi": -30,
  "data": {
  "temperature": 12.34
  }
  },
  "uplink_id": "1111aaaa-4222-bbbb-3333-cccc4444dddd",
  "date": "2018-10-23T00:00:00+09:00"
}



ステータスが200、レスポンス本文にEtagが表示された場合は成功です。
S3でリクエスト本文に入力した内容がJSONファイルで保存されていることを確認できます。

作成したAPIをデプロイする

「アクション」 → 「APIのデプロイ」をクリックします。
デプロイされるステージを「default」に設定して、「デプロイ」をクリックします。

ステージの画面が表示され、「default」配下に「/(api名)」が作成されていれば、デプロイが完了となります。
「URLの呼び出し」に記載されているURLがAPIを利用するためのURLとなります。

以上で、Webhookを受け取りデータを保存するサンプルの作成が完了となります。


課題

AWSを利用してWebhookを受け取りデータを保存するサンプルを作ることができましたが、以下の課題がまだ残っています。

  • BraveGATEのアプリケーション登録APIにてWebhookの通知先を本稿で作成したAPIに設定する。
  • APIのセキュリティが「オープン」になっているため、認証の仕組みを導入する。
  • Webhookで受け取ったデータに画像URLがあった場合は画像をS3に保存する。

これらの課題の解決方法やAWSを使わない方法など、BraveGATE Developer Portalにて公開予定です。
 

最後に

今回、このサンプルを作るにあたって、初めてまともにAWSに触れてみましたが、直感的な操作でそれほど苦労することもありませんでした。
まだ全体のほんの一握りのサービスしか扱っていませんので、いろいろ触ってみるのが楽しみです。

また、こういったBraveGATEを利用する側に立つことで、BraveGATEをよりよくするための課題も見えてくると思いますので
様々な視点からBraveGATEについて勉強していきます。

このような記事を書くのは初めてで、わかりづらい部分も多くあるかと思います。。。
こういった技術も勉強していきます。。。

 

SNS SHARE