Amazon SNSを用いてAndroid(GMSとHMS)とiOSに対応したプッシュ通知の実装方法１０―サーバー(Amazon SNS：プッシュ通知を配信：HMS Part 2)

# Amazon SNSを用いてAndroid(GMSとHMS)とiOSに対応したプッシュ通知の実装方法―サーバー(Amazon SNS：プッシュ通知を配信：HMS Part 2)

### Amazon SNSのトピックに送られたプッシュ配信内容をプッシュサーバーに転送するLambdaのサンプル

```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 = null;
    
    try {
        // プッシュデータを取得
        const dataString = util.getData(event);

        // ログ出力
        console.info("data = " + dataString);

        // プッシュデータのJSONオブジェクトを生成
        pushData = JSON.parse(dataString);
    } catch (exception) {
        util.returnErrorResponse(callback, 400, exception);
    }
    
    // プッシュデータの有効性を検証
    if (!pushData) {
        // プッシュデータのJSONオブジェクトが作れない
        util.returnErrorResponse(callback, 400, "No data");
    } else if (!pushData.hasOwnProperty("title")) {
        // タイトルがない
        util.returnErrorResponse(callback, 400, "No title");
    } else if (!pushData.hasOwnProperty("body")) {
        // 本文がない
        util.returnErrorResponse(callback, 400, "No body");
    } else if (!pushData.hasOwnProperty("token") || !Array.isArray(pushData.token) || pushData.token.length < 1) {
        // 送信先がない
        util.returnErrorResponse(callback, 400, "No push target");
    } else {
        // プッシュサーバーにアクセスするためのプッシュトークンを認証サーバーから取得する
        const getAccessTokenPromise = pushUtil.createGetAccessTokenPromise();
        getAccessTokenPromise.then((result) => {
            // ログ出力
            console.info("https://oauth-login.cloud.huawei.com/oauth2/v3/token = " + result);
            
            let accessTokenJson = null;
            try {
                accessTokenJson = JSON.parse(result);
            } catch (exception) {
                util.returnErrorResponse(callback, 500, exception);
            }
            
            // レスポンスを検証する
            if (!accessTokenJson) {
                // レスポンスがない
                util.returnErrorResponse(callback, 500, "No access token response");
            } else if (!accessTokenJson.hasOwnProperty("access_token")) {
                // アクセストークンがない
                util.returnErrorResponse(callback, 500, "No access token");
            } else if (!accessTokenJson.hasOwnProperty("token_type")) {
                // アクセストークンの種類が不明
                util.returnErrorResponse(callback, 500, "Unknown access token type");
            } else if (accessTokenJson.token_type != "Bearer") {
                // アクセストークンの種類が違う
                util.returnErrorResponse(callback, 500, "Incorrect access token type");
            } else {
                // アクセストークン
                const accessToken = accessTokenJson.access_token;
                // タイトル
                const pushTitle = pushData.title;
                // 本文
                const pushBody = pushData.body;
                // 送信対象
                const pushToken = pushData.token;
                
                // プッシュ通知を送る
                const pushNotificationPromise = pushUtil.createPushNotificationPromise(accessToken, pushTitle, pushBody, pushToken);
                pushNotificationPromise.then((result) => {
                    // ログ出力
                    console.info("https://push-api.cloud.huawei.com/v2/736430079244623135/messages:send = " + result);
                    
                    let pushNotificationJson = null;
                    try {
                        pushNotificationJson = JSON.parse(result);
                    } catch (exception) {
                        util.returnErrorResponse(callback, 500, exception);
                    }
                    
                    // レスポンスを検証する
                    if (!pushNotificationJson) {
                        // レスポンスがない
                        util.returnErrorResponse(callback, 500, "No push notification response");
                    } else if (!pushNotificationJson.hasOwnProperty("code")) {
                        // 送信結果がない
                        util.returnErrorResponse(callback, 500, "No push notification result");
                    } else if (pushNotificationJson.result != "80000000" && pushNotificationJson.result != "80100000") {
                        // アクセストークンの種類が不明
                        util.returnErrorResponse(callback, 500, "Push failed");
                    } else {
                        util.returnResponse(callback, 200, result);
                    }
                }).catch((error) => {
                    util.returnErrorResponse(callback, 500, error);
                });
            }
        }).catch((error) => {
            util.returnErrorResponse(callback, 500, error);
        });
    }
}
```

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

const CLIENT_ID = "AppGallery ConnectのMy AppsのApp informationのApp ID";
const CLIENT_SECRET = "AppGallery ConnectのMy AppsのApp informationのApp secret";

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

}
```

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

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

    getData(event) {
        if (event
            && event.hasOwnProperty("Records")
            && Array.isArray(event.Records) 
            && event.Records.length > 0
            && event.Records[0].hasOwnProperty("Sns")
            && event.Records[0].Sns.hasOwnProperty("Message")
        ) {
            return event.Records[0].Sns.Message;
        }
        
        return null;
    }

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

    returnResponse(callback, statusCode, body) {
        if (callback && callback instanceof Function) {
            const response = {
                'statusCode': statusCode,
                'headers': {
                    'Content-type': 'application/json'
                },
                'body': JSON.stringify(body)
            }
            callback(null, response);    
        }
    }

    returnErrorResponse(callback, statusCode, error) {
        const body = {
            error: error
        };

        this.returnResponse(callback, statusCode, body);
    }
}
```

# GitHub
https://github.com/Rei2020GitHub/MyPublicProject/tree/master/PushDemo(H%2BG)/AWS/Lambda/hms-push-demo-send-push-notification

# 参考
<b>Amazon SNSを用いてAndroid(GMSとHMS)とiOSに対応したプッシュ通知の実装方法</b>
[１―概要](https://qiita.com/Rei_2020/items/69b9d9344e6d23dea097)
[２―クライアント(Android-GMS)](https://qiita.com/Rei_2020/items/2f4424cb3a5bbf86abc3)
[３―クライアント(Android-HMS)](https://qiita.com/Rei_2020/items/9d3764fc150e0e7a8014)
[４―クライアント(iOS-APNS)](https://qiita.com/Rei_2020/items/d839753f1514338809f9)
[５―サーバー(Amazon SNS：プッシュトークンの保存)](https://qiita.com/Rei_2020/items/c2ecd5bcd3866fe7d4e4)
[６―サーバー(Amazon SNS：プッシュトークンの管理：HMS)](https://qiita.com/Rei_2020/items/5e1906e3a4d1b737a803)
[７―サーバー(Amazon SNS：プッシュトークンの保存：HMS)](https://qiita.com/Rei_2020/items/6ad6f4ef13d5155cffa7)
[８―サーバー(Amazon SNS：プッシュ通知を配信)](https://qiita.com/Rei_2020/items/6ff3c1b8d3f7f44d27f0)
[９―サーバー(Amazon SNS：プッシュ通知を配信：HMS)](https://qiita.com/Rei_2020/items/40df3519b99c80fdea3d)
[１０―サーバー(Amazon SNS：プッシュ通知を配信：複数のデバイスに同時配信)](https://qiita.com/Rei_2020/items/ee32f3caeff8257097e6)