Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Impl contacts API #60

Merged
merged 6 commits into from
Dec 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/batch_email_send.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@
]

emails = resend.Batch.send(params)
print(emails)
print(emails)
41 changes: 41 additions & 0 deletions examples/contacts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import os

import resend

if not os.environ["RESEND_API_KEY"]:
raise EnvironmentError("RESEND_API_KEY is missing")


audience_id = "78b8d3bc-a55a-45a3-aee6-6ec0a5e13d7e"

contact = resend.Contacts.create(
{
"audience_id": audience_id,
"email": "[email protected]",
"first_name": "Steve",
"last_name": "Wozniak",
"unsubscribed": True,
}
)
print("created contact !")
print(contact)

update_params = {
"audience_id": audience_id,
"id": contact["id"],
"last_name": "Updated",
"unsubscribed": False,
}

updated = resend.Contacts.update(update_params)
print("updated contact !")
print(updated)

cont = resend.Contacts.get(audience_id=audience_id, id=contact["id"])
print(cont)

contacts = resend.Contacts.list(audience_id=audience_id)
print(contacts)

rmed = resend.Contacts.remove(audience_id=audience_id, id=contact["id"])
print(rmed)
2 changes: 2 additions & 0 deletions resend/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from .api_keys import ApiKeys
from .audiences import Audiences
from .batch import Batch
from .contacts import Contacts
from .domains import Domains
from .emails import Emails
from .request import Request
Expand All @@ -25,4 +26,5 @@
"Domains",
"Batch",
"Audiences",
"Contacts"
]
37 changes: 37 additions & 0 deletions resend/contacts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from typing import Dict

from resend import request


class Contacts:
"""Contacts API Wrapper"""

@classmethod
# https://resend.com/docs/api-reference/contacts/create-contact
def create(cls, params={}) -> Dict:
path = f"/audiences/{params['audience_id']}/contacts"
return request.Request(path=path, params=params, verb="post").perform()

@classmethod
# https://resend.com/docs/api-reference/contacts/update-contact
def update(cls, params={}) -> Dict:
path = f"/audiences/{params['audience_id']}/contacts/{params['id']}"
return request.Request(path=path, params=params, verb="patch").perform()

@classmethod
# https://resend.com/docs/api-reference/audiences/list-audiences
def list(cls, audience_id) -> Dict:
path = f"/audiences/#{audience_id}/contacts"
return request.Request(path=path, params={}, verb="get").perform()

@classmethod
# https://resend.com/docs/api-reference/audiences/get-audience
def get(cls, audience_id, id) -> Dict:
path = f"/audiences/{audience_id}/contacts/{id}"
return request.Request(path=path, params={}, verb="get").perform()

@classmethod
# https://resend.com/docs/api-reference/audiences/delete-audience
def remove(cls, audience_id, id) -> Dict:
path = f"/audiences/{id}/contacts/{id}"
return request.Request(path=path, params={}, verb="delete").perform()
30 changes: 29 additions & 1 deletion resend/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def __init__(
suggested_action = """Check the error message
to see the list of missing fields."""

if message != "":
if message == "":
message = default_message

ResendError.__init__(
Expand All @@ -132,11 +132,38 @@ def __init__(
)


class ApplicationError(ResendError):
"""see https://resend.com/docs/errors"""

def __init__(
self,
message,
error_type,
code,
):
default_message = """
Something went wrong."""

suggested_action = """Contact Resend support."""

if message == "":
message = default_message

ResendError.__init__(
self,
code=code or 500,
message=message,
suggested_action=suggested_action,
error_type=error_type,
)


ERRORS: Dict[str, Dict[str, ResendError]] = {
"400": {"validation_error": ValidationError},
"422": {"missing_required_fields": MissingRequiredFieldsError},
"401": {"missing_api_key": MissingApiKeyError},
"403": {"invalid_api_key": InvalidApiKeyError},
"500": {"application_error": ApplicationError},
}


Expand All @@ -149,4 +176,5 @@ def raise_for_code_and_type(code, error_type, message: str) -> ResendError:

# Raise error from errors list
error: ResendError = error.get(error_type)

raise error(code=code, message=message, error_type=error_type)
1 change: 0 additions & 1 deletion resend/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ def perform(self):
message=error.get("message"),
error_type=error.get("name"),
)

return resp.json()

def __get_headers(self) -> Dict:
Expand Down
172 changes: 172 additions & 0 deletions tests/contacts_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import unittest
from unittest.mock import MagicMock, patch

import resend

# flake8: noqa


class TestResendContacts(unittest.TestCase):
def test_contacts_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": "contact", "id": "479e3145-dd38-476b-932c-529ceb705947"}

m.json = mock_json
mock.return_value = m

params = {
"audience_id": "48c269ed-9873-4d60-bdd9-cd7e6fc0b9b8",
"email": "[email protected]",
"first_name": "Steve",
"last_name": "Wozniak",
"unsubscribed": True,
}
contact = resend.Contacts.create(params)
assert contact["id"] == "479e3145-dd38-476b-932c-529ceb705947"
assert contact["object"] == "contact"

patcher.stop()

def test_contacts_update(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": "contact",
"id": "479e3145-dd38-476b-932c-529ceb705947",
}

m.json = mock_json
mock.return_value = m

params = {
"audience_id": "48c269ed-9873-4d60-bdd9-cd7e6fc0b9b8",
"id": "479e3145-dd38-476b-932c-529ceb705947",
"first_name": "Updated",
"unsubscribed": True,
}
contact = resend.Contacts.update(params)
assert contact["id"] == "479e3145-dd38-476b-932c-529ceb705947"
assert contact["object"] == "contact"

patcher.stop()

def test_contacts_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": "contact",
"id": "e169aa45-1ecf-4183-9955-b1499d5701d3",
"email": "[email protected]",
"first_name": "Steve",
"last_name": "Wozniak",
"created_at": "2023-10-06T23:47:56.678Z",
"unsubscribed": False,
}

m.json = mock_json
mock.return_value = m

contact = resend.Contacts.get(
audience_id="48c269ed-9873-4d60-bdd9-cd7e6fc0b9b8",
id="e169aa45-1ecf-4183-9955-b1499d5701d3",
)
assert contact["object"] == "contact"
assert contact["id"] == "e169aa45-1ecf-4183-9955-b1499d5701d3"
assert contact["email"] == "[email protected]"
assert contact["first_name"] == "Steve"
assert contact["last_name"] == "Wozniak"
assert contact["created_at"] == "2023-10-06T23:47:56.678Z"
assert contact["unsubscribed"] is False

patcher.stop()

def test_contacts_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": "contact",
"id": "520784e2-887d-4c25-b53c-4ad46ad38100",
"deleted": True,
}

m.json = mock_json
mock.return_value = m

rmed = resend.Contacts.remove(
audience_id="48c269ed-9873-4d60-bdd9-cd7e6fc0b9b8",
id="78261eea-8f8b-4381-83c6-79fa7120f1cf",
)
assert rmed["object"] == "contact"
assert rmed["id"] == "520784e2-887d-4c25-b53c-4ad46ad38100"
assert rmed["deleted"] is True

patcher.stop()

def test_contacts_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": "e169aa45-1ecf-4183-9955-b1499d5701d3",
"email": "[email protected]",
"first_name": "Steve",
"last_name": "Wozniak",
"created_at": "2023-10-06T23:47:56.678Z",
"unsubscribed": False,
}
],
}

m.json = mock_json
mock.return_value = m

contacts = resend.Contacts.list(
audience_id="48c269ed-9873-4d60-bdd9-cd7e6fc0b9b8"
)
assert contacts["object"] == "list"
assert contacts["data"][0]["id"] == "e169aa45-1ecf-4183-9955-b1499d5701d3"
assert contacts["data"][0]["email"] == "[email protected]"
assert contacts["data"][0]["first_name"] == "Steve"
assert contacts["data"][0]["last_name"] == "Wozniak"
assert contacts["data"][0]["created_at"] == "2023-10-06T23:47:56.678Z"
assert contacts["data"][0]["unsubscribed"] is False

patcher.stop()
Loading