From 7b5686018edc21c14d07a571a7058247562eda61 Mon Sep 17 00:00:00 2001 From: Derich Pacheco Date: Sun, 17 Dec 2023 12:25:54 -0300 Subject: [PATCH] feat: impl Audiences API --- examples/audiences.py | 23 ++++ examples/with_b64_attachments.py | 7 +- examples/with_html_file_as_b64_attachment.py | 7 +- resend/__init__.py | 14 ++- resend/api.py | 5 +- resend/audiences.py | 31 +++++ resend/batch.py | 2 +- tests/audiences_test.py | 121 +++++++++++++++++++ tests/batch_emails_test.py | 30 ++--- 9 files changed, 212 insertions(+), 28 deletions(-) create mode 100644 examples/audiences.py create mode 100644 resend/audiences.py create mode 100644 tests/audiences_test.py diff --git a/examples/audiences.py b/examples/audiences.py new file mode 100644 index 0000000..57b0a0c --- /dev/null +++ b/examples/audiences.py @@ -0,0 +1,23 @@ +import os + +import resend + +if not os.environ["RESEND_API_KEY"]: + raise EnvironmentError("RESEND_API_KEY is missing") + + +audience = resend.Audiences.create( + { + "name": "New Audience from Python SDK", + } +) +print(audience) + +aud = resend.Audiences.get(audience["id"]) +print(aud) + +audiences = resend.Audiences.list() +print(audiences) + +rmed = resend.Audiences.remove(audience["id"]) +print(rmed) diff --git a/examples/with_b64_attachments.py b/examples/with_b64_attachments.py index ae5ee6e..fd827cb 100644 --- a/examples/with_b64_attachments.py +++ b/examples/with_b64_attachments.py @@ -1,6 +1,7 @@ +import base64 import os + import resend -import base64 if not os.environ["RESEND_API_KEY"]: raise EnvironmentError("RESEND_API_KEY is missing") @@ -10,7 +11,7 @@ ).read() b64 = base64.b64encode(f) -b64_str = b64.decode('utf-8') +b64_str = b64.decode("utf-8") params = { "from": "onboarding@resend.dev", @@ -21,4 +22,4 @@ } email = resend.Emails.send(params) -print(email) \ No newline at end of file +print(email) diff --git a/examples/with_html_file_as_b64_attachment.py b/examples/with_html_file_as_b64_attachment.py index 09eac5b..882aaf2 100644 --- a/examples/with_html_file_as_b64_attachment.py +++ b/examples/with_html_file_as_b64_attachment.py @@ -1,6 +1,7 @@ +import base64 import os + import resend -import base64 if not os.environ["RESEND_API_KEY"]: raise EnvironmentError("RESEND_API_KEY is missing") @@ -10,7 +11,7 @@ ).read() b64 = base64.b64encode(f) -b64_str = b64.decode('utf-8') +b64_str = b64.decode("utf-8") params = { "from": "onboarding@resend.dev", @@ -21,4 +22,4 @@ } email = resend.Emails.send(params) -print(email) \ No newline at end of file +print(email) diff --git a/resend/__init__.py b/resend/__init__.py index 5d518e1..eeaa0b3 100644 --- a/resend/__init__.py +++ b/resend/__init__.py @@ -2,9 +2,10 @@ from .api import Resend from .api_keys import ApiKeys +from .audiences import Audiences +from .batch import Batch from .domains import Domains from .emails import Emails -from .batch import Batch from .request import Request from .version import get_version @@ -15,4 +16,13 @@ # API resources from .emails import Emails # noqa -__all__ = ["get_version", "Resend", "Request", "Emails", "ApiKeys", "Domains", "Batch"] +__all__ = [ + "get_version", + "Resend", + "Request", + "Emails", + "ApiKeys", + "Domains", + "Batch", + "Audiences", +] diff --git a/resend/api.py b/resend/api.py index 9e73463..c512dac 100644 --- a/resend/api.py +++ b/resend/api.py @@ -1,5 +1,5 @@ -from warnings import warn from typing import Dict, List +from warnings import warn import resend @@ -25,7 +25,8 @@ def send_email( ): warn( "[DEPRECATION]: method `send_email` is deprecated. Use resend.Emails.send() instead", - DeprecationWarning) + DeprecationWarning, + ) return resend.Emails.send( { "from": sender, diff --git a/resend/audiences.py b/resend/audiences.py new file mode 100644 index 0000000..96296d4 --- /dev/null +++ b/resend/audiences.py @@ -0,0 +1,31 @@ +from typing import Dict + +from resend import request + + +class Audiences: + """Audiences API Wrapper""" + + @classmethod + # https://resend.com/docs/api-reference/audiences/create-audience + def create(cls, params={}) -> Dict: + path = "/audiences" + return request.Request(path=path, params=params, verb="post").perform() + + @classmethod + # https://resend.com/docs/api-reference/audiences/list-audiences + def list(cls) -> Dict: + path = "/audiences/" + return request.Request(path=path, params={}, verb="get").perform() + + @classmethod + # https://resend.com/docs/api-reference/audiences/get-audience + def get(cls, id) -> Dict: + path = f"/audiences/{id}" + return request.Request(path=path, params={}, verb="get").perform() + + @classmethod + # https://resend.com/docs/api-reference/audiences/delete-audience + def remove(cls, id="") -> Dict: + path = f"/audiences/{id}" + return request.Request(path=path, params={}, verb="delete").perform() diff --git a/resend/batch.py b/resend/batch.py index 39f6b64..b71eecf 100644 --- a/resend/batch.py +++ b/resend/batch.py @@ -1,4 +1,4 @@ -from typing import List, Dict +from typing import Dict, List from resend import request diff --git a/tests/audiences_test.py b/tests/audiences_test.py new file mode 100644 index 0000000..10c8b43 --- /dev/null +++ b/tests/audiences_test.py @@ -0,0 +1,121 @@ +import unittest +from unittest.mock import MagicMock, patch + +import resend + +# flake8: noqa + + +class TestResendAudiences(unittest.TestCase): + def test_audiences_create(self): + resend.api_key = "re_123" + + patcher = patch("resend.Request.make_request") + mock = patcher.start() + mock.status_code = 200 + m = MagicMock() + m.status_code = 200 + + def mock_json(): + return { + "object": "audience", + "id": "78261eea-8f8b-4381-83c6-79fa7120f1cf", + "name": "Registered Users", + } + + m.json = mock_json + mock.return_value = m + + params = { + "name": "Python SDK Audience", + } + audience = resend.Audiences.create(params) + assert audience["id"] == "78261eea-8f8b-4381-83c6-79fa7120f1cf" + assert audience["name"] == "Registered Users" + assert audience["object"] == "audience" + + patcher.stop() + + def test_audiences_get(self): + resend.api_key = "re_123" + + patcher = patch("resend.Request.make_request") + mock = patcher.start() + mock.status_code = 200 + m = MagicMock() + m.status_code = 200 + + def mock_json(): + return { + "object": "audience", + "id": "78261eea-8f8b-4381-83c6-79fa7120f1cf", + "name": "Registered Users", + "created_at": "2023-10-06T22:59:55.977Z", + } + + m.json = mock_json + mock.return_value = m + + audience = resend.Audiences.list() + assert audience["object"] == "audience" + assert audience["id"] == "78261eea-8f8b-4381-83c6-79fa7120f1cf" + assert audience["name"] == "Registered Users" + + patcher.stop() + + def test_audiences_remove(self): + resend.api_key = "re_123" + + patcher = patch("resend.Request.make_request") + mock = patcher.start() + mock.status_code = 200 + m = MagicMock() + m.status_code = 200 + + def mock_json(): + return { + "object": "audience", + "id": "78261eea-8f8b-4381-83c6-79fa7120f1cf", + "deleted": True, + } + + m.json = mock_json + mock.return_value = m + + rmed = resend.Audiences.remove("78261eea-8f8b-4381-83c6-79fa7120f1cf") + assert rmed["object"] == "audience" + assert rmed["id"] == "78261eea-8f8b-4381-83c6-79fa7120f1cf" + assert rmed["deleted"] is True + + patcher.stop() + + def test_audiences_list(self): + resend.api_key = "re_123" + + patcher = patch("resend.Request.make_request") + mock = patcher.start() + mock.status_code = 200 + m = MagicMock() + m.status_code = 200 + + def mock_json(): + return { + "object": "list", + "data": [ + { + "id": "78261eea-8f8b-4381-83c6-79fa7120f1cf", + "name": "Registered Users", + "created_at": "2023-10-06T22:59:55.977Z", + } + ], + } + + m.json = mock_json + mock.return_value = m + + audience = resend.Audiences.list() + assert audience["object"] == "list" + assert audience["data"][0]["id"] == "78261eea-8f8b-4381-83c6-79fa7120f1cf" + assert audience["data"][0]["name"] == "Registered Users" + + patcher.stop() diff --git a/tests/batch_emails_test.py b/tests/batch_emails_test.py index efa89d4..76ede51 100644 --- a/tests/batch_emails_test.py +++ b/tests/batch_emails_test.py @@ -18,14 +18,10 @@ def test_batch_email_send(self): def mock_json(): return { - "data": [ - { - "id": "ae2014de-c168-4c61-8267-70d2662a1ce1" - }, - { - "id": "faccb7a5-8a28-4e9a-ac64-8da1cc3bc1cb" - } - ] + "data": [ + {"id": "ae2014de-c168-4c61-8267-70d2662a1ce1"}, + {"id": "faccb7a5-8a28-4e9a-ac64-8da1cc3bc1cb"}, + ] } m.json = mock_json @@ -33,20 +29,20 @@ def mock_json(): params = [ { - "from": "from@resend.dev", - "to": ["to@resend.dev"], - "subject": "hey", - "html": "hello, world!", + "from": "from@resend.dev", + "to": ["to@resend.dev"], + "subject": "hey", + "html": "hello, world!", }, { - "from": "from@resend.dev", - "to": ["to@resend.dev"], - "subject": "hello", - "html": "hello, world!", + "from": "from@resend.dev", + "to": ["to@resend.dev"], + "subject": "hello", + "html": "hello, world!", }, ] emails = resend.Batch.send(params) assert len(emails["data"]) == 2 assert emails["data"][0]["id"] == "ae2014de-c168-4c61-8267-70d2662a1ce1" assert emails["data"][1]["id"] == "faccb7a5-8a28-4e9a-ac64-8da1cc3bc1cb" - patcher.stop() \ No newline at end of file + patcher.stop()