【AWS】SNSによる、メール送信・プッシュ通知(nodejs)

# 要件
検証済みのメールアドレスへメール送受信可能です。登録済みのグループ内のメールアドレスまたはモバイルデバイスへ一斉送信・プッシュ通知します。

※今記事のサンプルソースは下記のgithubをご参考ください。
[サンプルソース](https://github.com/ling350181/nodejs-examples/blob/main/src/libs/sns.js)

# 前提
- Node.js環境
- AWSアカウント
- プッシュ通知の場合、Firebaseのアカウントも必要
※AWSとFirebaseの事前準備は[【SNSとFCMの連携】](https://qiita.com/ling350181/items/64073e521f996a8357d0)をご参考ください
- メールアドレス事前登録し済み（topicやSubscription作成済み）

# 専門用語
- DeviceToken
モバイル端末（デバイス）固有の値
- EndPoint
配信対象端末を識別するためのデータです。
DeviceTokenより作成する、端末を識別するためのID
- Topic
配信対象をグルーピングし、配信対象に一斉に通知を配信するための機能です。
- Subscription
Subscriptionとは、作成したTopicと配信対象のEndPointを紐づけるデータです。

# 処理フロー
## プッシュ通知
![プッシュ通知フロー](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/f3f1ae1c-4705-c935-9eab-f67ebfcfcd6b.png)
## メール送信
![メール送信フロー](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/b0f34d90-fb9c-5319-7c98-c1c5e63a0a1d.png)

# nodejsによるメール送信／プッシュ通知
## topic作成
```js
// Load the AWS SDK for Node.js
var AWS = require('aws-sdk');
// Set region
AWS.config.update({region: 'REGION'});

// Create promise and SNS service object
var createTopicPromise = new AWS.SNS({apiVersion: '2010-03-31'}).createTopic({Name: "TOPIC_NAME"}).promise();

// Handle promise's fulfilled/rejected states
createTopicPromise.then(
  function(data) {
    console.log("Topic ARN is " + data.TopicArn);
  }).catch(
    function(err) {
    console.error(err, err.stack);
  });
```
## トピックへのサブスクライブ
メールアドレスの場合、パラメーター下記のようです。ProtocolはEMAILです
~~~
var params = {
  Protocol: 'EMAIL', /* required */
  TopicArn: 'TOPIC_ARN', /* required */
  Endpoint: 'EMAIL_ADDRESS'
};
~~~
モバイルのデバイストークンによる、サブスクライブを作成時は、パラメーターは下記のようです。endPointはデバイストークンで作成したものです。
~~~
var params = {
  Protocol: 'application', /* required */
  TopicArn: 'TOPIC_ARN', /* required */
  Endpoint: 'MOBILE_ENDPOINT_ARN'
};
~~~
トピックへのサブスクライブのセット
```js
// Load the AWS SDK for Node.js
var AWS = require('aws-sdk');
// Set region
AWS.config.update({region: 'REGION'});

// Create subscribe/email parameters
var params = {
  Protocol: 'EMAIL', /* required */
  TopicArn: 'TOPIC_ARN', /* required */
  Endpoint: 'EMAIL_ADDRESS'
};

// Create promise and SNS service object
var subscribePromise = new AWS.SNS({apiVersion: '2010-03-31'}).subscribe(params).promise();

// Handle promise's fulfilled/rejected states
subscribePromise.then(
  function(data) {
    console.log("Subscription ARN is " + data.SubscriptionArn);
  }).catch(
    function(err) {
    console.error(err, err.stack);
  });
```
## Endpoint
メールの場合、Endpointはメールアドレスですが、プッシュ通知の場合はEndpointはデバイストークンによって、作成しないといけません。
### パラメーター
- PlatformApplicationArn
SNSコンソールの[Mobile]の[プッシュ通知]の[プラットフォームアプリケーション](https://ap-northeast-1.console.aws.amazon.com/sns/v3/home?region=ap-northeast-1#/mobile/push-notifications)からプラットフォームアプリケーション作成ボタンをクリックして作成します。
- token
デバイストークン
```js
const params = {
  PlatformApplicationArn: snsPlatformApplicationArn,
  Token: token
}
await sns.createPlatformEndpoint(params).promise()
```

## メール送信／プッシュ通知
```js
    var payload = {
      GCM: {
        fcmV1Message: {
          message: {
            data: {
              type: chatType,
              groupId: groupId
            },
            android: {
              notification: {
                notification_count: badge
              }
            },
            apns: {
              payload: {
                aps: {
                  badge: badge
                }
              }
            },
            notification: {
              body: message,
              title: title
            }
          }
        }
      }
    }
    payload.GCM = JSON.stringify(payload.GCM);
    payload = JSON.stringify(payload);
    const params = {
      TargetArn: endpointArn,
      MessageStructure: 'json',
      Subject: title,
      Message: payload,
    }
    await sns.publish(params).promise()
```
※アプリアイコンのバッジ表示が必要の場合、badgeが必要です。
~~~
badge

省略可、数字

ホーム画面のアプリアイコンのバッジの値。
これを指定しなかった場合、バッジは変更されません。
0 に設定するとバッジは削除されます。
~~~

参考：
[FCMドキュメント](https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?hl=ja)

## 以前のHTTPからHTTP v1 に移行する 
2023年6月21日、Googleは、従来の Firebase Cloud Messaging (FCM) API を使用するアプリケーションがプッシュできなくなると発表しました。Amazon SNSが、Google Firebase の HTTP V1 API を介したモバイル プッシュ通知の配信をサポートするようになりました。

### 移行手順
1. AWS SNSの認証方法をキーからトークンに変更
Firebaseコンソール画面の対象プロジェクトの「サービスアカウント」タブにて「新しい秘密鍵を生成」で秘密鍵ファイを生成し、それをAWS SNSにアップロードします。
[トークンの生成手順](https://firebase.google.com/docs/cloud-messaging/migrate-v1?hl=ja&_gl=1*19pa6mn*_up*MQ..*_ga*MjYxNDU5NjU4LjE3MTE2MTUzNzU.*_ga_CW55HF8NVT*MTcxMTYxNTM3NS4xLjAuMTcxMTYxNTM3NS4wLjAuMA..)
[FCMの設定変更](https://docs.aws.amazon.com/ja_jp/sns/latest/dg/sns-fcm-authentication-methods.html#sns-fcm-authentication-console)
2. 送信構文の修正
HTTP v1 に移行することでプッシュ通知送信のJson構文が変わりました。
[HTTP V1 API](https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?hl=ja)
[AWS FCMのjson構文](https://docs.aws.amazon.com/ja_jp/sns/latest/dg/sns-fcm-v1-payloads.html)

```json
{
  "GCM": "{
     \"fcmV1Message\": { 
        \"validate_only\" : false,
        \"message\" : 
            {
            \"notification\": {
                \"title\": \"string\",
                \"body\": \"string\"
            },
            \"data\": {
                \"dataGen\": \"priority message\",
            },
            \"android\": {
                \"priority\": \"high\",
                \"notification\": {
                    \"body_loc_args\": [
                    \"string\"
                    ],
                    \"title_loc_args\": [
                    \"string\"
                    ],
                    \"sound\": \"string\",
                    \"title_loc_key\": \"string\",
                    \"title\": \"string\",
                    \"body\": \"string\",
                    \"click_action\": \"clicky_clacky\",
                    \"body_loc_key\": \"string\"
                },
                \"data\": {
                    \"dataAndroid\": \"priority message\",
                },
                \"ttl\": \"10023.32s\"
            },
            \"apns\": {
                \"payload\": {
                    \"aps\": {
                        \"alert\": {
                            \"subtitle\": \"string\",
                            \"title-loc-args\": [
                            \"string\"
                            ],
                            \"title-loc-key\": \"string\",
                            \"loc-args\": [
                            \"string\"
                            ],
                            \"loc-key\": \"string\",
                            \"title\": \"string\",
                            \"body\": \"string\"
                        },
                        \"category\": \"Click\",
                        \"content-available\": 0,
                        \"sound\": \"string\",
                        \"badge\": 5
                    }
                }
            },
            \"webpush\": {
                \"notification\": {
                    \"badge\": \"5\",
                    \"title\": \"string\",
                    \"body\": \"string\"
                },
                \"data\": {
                    \"dataWeb\": \"priority message\",
                }
            }
         }
     }
  }"
}    
```

※AWS　SNS送信する際、fcmV1Messageのキーは追加しました。

# その他
メール送信の場合、以上の設定で送信できますが、プッシュ通知の場合、その他の設定が必要です。例えば、FirebaseのFCM設定、iosのプッシュ通知証明書発行などは必要です。ここでは記述しません。

# おまけ
テストする際、エラーログなどはcloudwatchで確認できます。
ロググループ:
sns/region名/アカウント名/app/GCM/プラットフォームアプリケーション名

# 参考
- [ドキュメント](https://docs.aws.amazon.com/ja_jp/sdk-for-javascript/v2/developer-guide/sns-examples.html)
- [AWS Javascript SDK SNSリファレンス](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/SNS.html)
- [ペイロード](https://docs.aws.amazon.com/ja_jp/sns/latest/dg/sns-send-custom-platform-specific-payloads-mobile-devices.html)