ウオッ?! Django REST FrameworkとAWS SNSでアルティメットにクールなSMS送信を実現！！！🐰

## 概要
Django REST FrameworkとAWS SNSを利用してSMSテキストを送信する実装を説明します！

## 使用技術
```pyproject.toml
python = "^3.11.2"
Django = "^4.2.7"
djangorestframework = "^3.14.0"
boto3 = "^1.28.84"
injector = "^0.21.0"
pydantic = "^1.10.7"
gunicorn = "^21.2.0"
```

## 説明する前の前提
コードの細部の説明はあまりしません
また、インポートもとのコードが不要な場合は明記していません
ご了承ください🙇

## AWS SNSとは
以下、公式サイトに説明は任せる

https://aws.amazon.com/jp/sns/


## まずはコードの確認


```sms_view.py
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.viewsets import ViewSet

from common.environment_variable import settings
from common.sms import SnsWrapper
from app.injectors import injector
from app.serializers import SmsSerializer


class SmsViewSet(ViewSet):
    """SMSを送信するAPI"""

    serializer_class = SmsSerializer

    @action(methods=["post"], detail=False)
    def sms(self, request):
        """SMS送信API

        Parameters:
            phone_number（str）: 電話番号

        Returns:
            detail (str): メッセージ
        """

        serializer = self.serializer_class(data=request.data)
        serializer.is_valid(raise_exception=True)

        sms = injector.get(SnsWrapper)
        sms.publish_text_message(
            settings.INTERNATIONAL_TEL_IDENTIFICATION_NUMBER
            + serializer.validated_data.get("phone_number"),
        )
        return Response(
            {"detail": "SMS送信に成功しました"},
            status=status.HTTP_200_OK,
        )

```
SMS送信のAPIのビュー部分です



```injector.py
import boto3
from injector import Binder, Injector, Module

from common.environment_variable import aws_settings
from common.sms import SnsResource, SnsWrapper


class SnsWrapperModule(Module):
    def configure(self, binder):
        binder.bind(SnsWrapper)


class LocalModule(Module):
    """Local環境用のモジュール"""

    def configure(self, binder: Binder) -> None:
        sns_resource = SnsResource(
            boto3.resource(
                "sns",
                region_name=aws_settings.REGION_NAME,
                # localstackに接続（http://localhost:4566）
                endpoint_url=aws_settings.ENDPOINT_URL,
            )
        )
        binder.bind(SnsResource, to=sns_resource)


class DevModule(Module):
    """Dev環境用のモジュール"""

    def configure(self, binder: Binder) -> None:
        sns_resource = SnsResource(
            boto3.resource("sns", region_name=aws_settings.REGION_NAME)
        )
        binder.bind(SnsResource, to=sns_resource)


injector = Injector([SnsWrapperModule()])

```



ローカル環境とDEV環境で含めるラッパーモジュールを切り替えられるようにしています
ローカルではlocalstackを使用して、SNSの擬似的なレスポンスを返してくれるようにしています

https://docs.localstack.cloud/user-guide/integrations/sdks/python/





```sms.py
from injector import inject

from common.environment_variable import settings


class SnsResource:
    """SNSのResource用のクラス"""

    def __init__(self, sns_resource):
        self.sns_resource = sns_resource


class SnsWrapper:
    """Amazon SNSのトピックとサブスクリプションの機能をカプセル化する"""

    @inject
    def __init__(self, sns_resource: SnsResource):
        """
        :param sns_resource: Boto3 Amazon SNSリソース
        """
        self.sns_resource = sns_resource.sns_resource

    def publish_text_message(self, phone_number):
        """
        サブスクリプションを必要とせずに、直接電話番号にテキストメッセージを送信するメソッド

        :Parameters:
            phone_number（str）: 電話番号
        """
        message = "SMSテキストメッセージだぽん"
        self.sns_resource.meta.client.publish(
            PhoneNumber=phone_number,
            Message=message,
            MessageAttributes={
                "AWS.SNS.SMS.SenderID": {
                    "DataType": "String",
                    # 差出人名を指定できます
                    "StringValue": settings.SMS_SENDER_NAME,
                }
            },
        )
```


以下記事を参考に送信メソッドを実装します

https://docs.aws.amazon.com/ja_jp/code-library/latest/ug/python_3_sns_code_examples.html




## 環境変数の管理方法について
環境変数の管理方法については以下を参照ください

https://qiita.com/sotaheavymetal21/items/ec76bb4f3032ec1bdf90

## boto3について
https://aws.amazon.com/jp/sdk-for-python/

## 最後に
流れさえ、抑えればスムーズに開発が進められると思います🐰