Amazon SNSを利用してモバイルプッシュ通知を送信するやり方ー HMS対応（実装）

# ソリューション概要
[Amazon SNSを利用してモバイルプッシュ通知を送信するやり方ー HMS対応（ソリューション）](https://qiita.com/Rei_2020/items/352a0b02d6eb13ab19da)をご参照ください。

# AWS Lambdaの実装

### サブスクリプションからのデータを取得する
プッシュデータはevent.Records[0].Sns.Messageに入っています。

```javascript:index.js
...

exports.handler = function (event, context, callback) {
    ...

    // プッシュデータ
    let pushData = null;
    if (event
        && event.hasOwnProperty("Records")
        && Array.isArray(event.Records) 
        && event.Records.length > 0
        && event.Records[0].hasOwnProperty("Sns")
        && event.Records[0].Sns.hasOwnProperty("Message")
    ) {
        pushData = event.Records[0].Sns.Message;
    }

    ...
}
```

### HMSプッシュサーバーとのやり取り
アクセストークンのAPIを呼び出すメソッドcreateGetAccessTokenPromise()と、プッシュ通知配信のAPIを呼び出すメソッドcreatePushNotificationPromise()を用意します。

```javascript:util.js
const https = require('https');
const querystring = require('querystring');

module.exports = class Util {
    constructor() {}

    createRequestPromise_json(hostname, path, method, headers, data) {
        return new Promise((resolve, reject) => {
            try {
                const stringData = JSON.stringify(data);

                headers["Content-Type"] = "application/json";

                const options = {
                    hostname: hostname,
                    path: path,
                    method: method,
                    rejectUnauthorized: false,
                    headers: headers
                };
        
                const request = https.request(options, (response) => {
                    let data = '';
                    response.on('data', (chunk) => {
                        data += chunk;
                    });
        
                    response.on('end', () => {
                        resolve(data);
                    });
                }).on("error", (error) => {
                    reject(error);
                });
        
                request.write(stringData);
                request.end();
            } catch (exception) {
                reject(exception);
            }
        });
    }

    createRequestPromise_x_www_form_urlencoded(hostname, path, method, headers, data) {
        return new Promise((resolve, reject) => {
            try {
                const postData = querystring.stringify(data);

                headers["Content-Type"] = "application/x-www-form-urlencoded";
                headers["Content-Length"] = postData.length;

                const options = {
                    hostname: hostname,
                    path: path,
                    method: method,
                    headers: headers
                };
        
                const request = https.request(options, (response) => {
                    let data = '';
                    response.on('data', (chunk) => {
                        data += chunk;
                    });
        
                    response.on('end', () => {
                        resolve(data);
                    });
                }).on("error", (error) => {
                    reject(error);
                });
        
                request.write(postData);
                request.end();
            } catch (exception) {
                reject(exception);
            }
        });
    }
}
```

```javascript:pushUtil.js
const Util = require("./util.js");

const CLIENT_ID = "HMSのAppGallery Connectに登録したプロジェクトのクライアントID";
const CLIENT_SECRET = "HMSのAppGallery Connectに登録したプロジェクトのクライアントシークレット";

module.exports = class MenuContent {

    constructor() {
        this.util = new Util();
    }

    createGetAccessTokenPromise() {
        const hostname = "oauth-login.cloud.huawei.com";
        const path = "/oauth2/v3/token";
        const method = "POST";
        const headers = {};
        const data = {
            grant_type: "client_credentials",
            client_id: CLIENT_ID,
            client_secret: CLIENT_SECRET
        };

        return this.util.createRequestPromise_x_www_form_urlencoded(hostname, path, method, headers, data);
    }

    // このサンプルでは通知メッセージ（データメッセージではない）のみに焦点を当てます
    createPushNotificationPromise(accessToken, title, body, token) {
        const hostname = "push-api.cloud.huawei.com";
        const path = "/v2/736430079244623135/messages:send";
        const method = "POST";
        const headers = {
            Authorization: "Bearer " + accessToken
        };

        // リクエストパラメータの詳細は
        // https://developer.huawei.com/consumer/jp/doc/development/HMSCore-References-V5/https-send-api-0000001050986197-V5
        // をご参照ください
        const data = {
            validate_only: false,
            message: {
                notification: {
                    title: title,
                    body: body
                },
                android: {
                    notification: {
                        title: title,
                        body: body,
                        click_action: {
                            "type": 1,
                            "intent": "#Intent;compo=com.rvr/.Activity;S.W=U;end"
                        }
                    }
                },
                // 配信対象のトークンの配列
                token: token
            }
        };

        return this.util.createRequestPromise_json(hostname, path, method, headers, data);
    }

}
```

### HMSプッシュデータを配信する
アクセストークンを取得してから、プッシュデータを配信します。
ここで気を付けなければならないのは、<b>配信先のトークン数の上限</b>です。
現時点では配信先のトークン数の上限は<b><font color="red">1000個</font></b>までなので、1000個以上の場合、分割して送る必要があります。

```javascript:index.js
const Util = require("./util.js");
const PushUtil = require("./pushUtil.js");

exports.handler = function (event, context, callback) {
    const util = new Util();
    const pushUtil = new PushUtil();

    let pushData = event.Records[0].Sns.Message;

    // タイトル＝pushData.titleと仮定する
    const pushTitle = pushData.title;
    // 本文＝pushData.bodyと仮定する
    const pushBody = pushData.body;
    // 送信対象＝pushData.tokenと仮定する
    const pushToken = pushData.token;

    // プッシュサーバーにアクセスするためのプッシュトークンを認証サーバーから取得する
    const getAccessTokenPromise = pushUtil.createGetAccessTokenPromise();
    getAccessTokenPromise.then((result) => {        
        let accessTokenJson = null;
        try {
            accessTokenJson = JSON.parse(result);
        } catch (exception) {
            const response = {
                'statusCode': 500,
                'headers': {
                    'Content-type': 'application/json'
                },
                'body': JSON.stringify(error)
            }
            callback(null, response);
        }
        
        // アクセストークン
        const accessToken = accessTokenJson.access_token;
        
        // プッシュ通知を送る
        const pushNotificationPromise = pushUtil.createPushNotificationPromise(accessToken, pushTitle, pushBody, pushToken);
        pushNotificationPromise.then((result) => {
            const response = {
                'statusCode': 200,
                'headers': {
                    'Content-type': 'application/json'
                },
                'body': JSON.stringify(result)
            }
            callback(null, response);
        }).catch((error) => {
            const response = {
                'statusCode': 500,
                'headers': {
                    'Content-type': 'application/json'
                },
                'body': JSON.stringify(error)
            }
            callback(null, response);
        });
    }
}
```

# Amazon SNSのサブスクリプション作成
Lambdaを作成したら、<b>関数のARN</b>が生成されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/704548/f5a08d6c-33b7-e0e5-1536-5b238e24484c.png)

このARNを使って、Amazon SNSのサブスクリプションを作ります。

サブスクリプションを作成するときに、<b>プロトコル</b>を<b>AWS Lambda</b>にし、<b>エンドポイント</b>にLambdaの<b>関数のARN</b>を入れればよいです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/704548/62b3a8c6-0fd8-6f12-dbb0-021f40bd29eb.png)

これでHMSに対応したAmazon SNSのモバイルプッシュ通知が完成です！

# サンプル
[https://github.com/Rei2020GitHub/MyPublicProject/tree/master/AWS/hms-push-demo-send-push-notification](https://github.com/Rei2020GitHub/MyPublicProject/tree/master/AWS/hms-push-demo-send-push-notification)

# 参考
[Amazon SNSを利用してモバイルプッシュ通知を送信するやり方ーコンソール利用](https://qiita.com/Rei_2020/items/ab1768c1a0191b73492e)
[Amazon SNSを利用してモバイルプッシュ通知を送信するやり方ー Node.js](https://qiita.com/Rei_2020/items/fd8c679f697b0e0f5ca7)
[Amazon SNSを利用してモバイルプッシュ通知を送信するやり方ー HMS対応（ソリューション）](https://qiita.com/Rei_2020/items/352a0b02d6eb13ab19da)
[Amazon SNSを利用してモバイルプッシュ通知を送信するやり方ー HMS対応（実装）](https://qiita.com/Rei_2020/items/ad4046b59d3e2cc7111c)