feat: sync sdk contact book support (#373)
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
"""Python client for the UseSend API."""
|
||||
|
||||
from .usesend import UseSend, UseSendHTTPError
|
||||
from .contacts import Contacts # type: ignore
|
||||
from .contact_books import ContactBooks # type: ignore
|
||||
from .domains import Domains # type: ignore
|
||||
from .campaigns import Campaigns # type: ignore
|
||||
from .webhooks import (
|
||||
@@ -17,6 +19,8 @@ __all__ = [
|
||||
"UseSend",
|
||||
"UseSendHTTPError",
|
||||
"types",
|
||||
"Contacts",
|
||||
"ContactBooks",
|
||||
"Domains",
|
||||
"Campaigns",
|
||||
"Webhooks",
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
"""Contact book resource client using TypedDict shapes (no Pydantic)."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional, Tuple, List
|
||||
|
||||
from .types import (
|
||||
APIError,
|
||||
ContactBook,
|
||||
ContactBookCreate,
|
||||
ContactBookCreateResponse,
|
||||
ContactBookDeleteResponse,
|
||||
ContactBookUpdate,
|
||||
ContactBookUpdateResponse,
|
||||
)
|
||||
|
||||
|
||||
class ContactBooks:
|
||||
"""Client for `/contactBooks` endpoints."""
|
||||
|
||||
def __init__(self, usesend: "UseSend") -> None:
|
||||
self.usesend = usesend
|
||||
|
||||
def list(self) -> Tuple[Optional[List[ContactBook]], Optional[APIError]]:
|
||||
data, err = self.usesend.get("/contactBooks")
|
||||
return (data, err) # type: ignore[return-value]
|
||||
|
||||
def create(
|
||||
self, payload: ContactBookCreate
|
||||
) -> Tuple[Optional[ContactBookCreateResponse], Optional[APIError]]:
|
||||
data, err = self.usesend.post("/contactBooks", payload)
|
||||
return (data, err) # type: ignore[return-value]
|
||||
|
||||
def get(self, contact_book_id: str) -> Tuple[Optional[ContactBook], Optional[APIError]]:
|
||||
data, err = self.usesend.get(f"/contactBooks/{contact_book_id}")
|
||||
return (data, err) # type: ignore[return-value]
|
||||
|
||||
def update(
|
||||
self, contact_book_id: str, payload: ContactBookUpdate
|
||||
) -> Tuple[Optional[ContactBookUpdateResponse], Optional[APIError]]:
|
||||
data, err = self.usesend.patch(f"/contactBooks/{contact_book_id}", payload)
|
||||
return (data, err) # type: ignore[return-value]
|
||||
|
||||
def delete(
|
||||
self, contact_book_id: str
|
||||
) -> Tuple[Optional[ContactBookDeleteResponse], Optional[APIError]]:
|
||||
data, err = self.usesend.delete(f"/contactBooks/{contact_book_id}")
|
||||
return (data, err) # type: ignore[return-value]
|
||||
|
||||
|
||||
from .usesend import UseSend # noqa: E402 pylint: disable=wrong-import-position
|
||||
@@ -2,11 +2,17 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, Optional, Tuple
|
||||
from urllib.parse import urlencode
|
||||
|
||||
from .types import (
|
||||
APIError,
|
||||
ContactDeleteResponse,
|
||||
Contact,
|
||||
ContactBulkCreate,
|
||||
ContactBulkCreateResponse,
|
||||
ContactBulkDelete,
|
||||
ContactBulkDeleteResponse,
|
||||
ContactList,
|
||||
ContactUpdate,
|
||||
ContactUpdateResponse,
|
||||
ContactUpsert,
|
||||
@@ -31,6 +37,32 @@ class Contacts:
|
||||
)
|
||||
return (data, err) # type: ignore[return-value]
|
||||
|
||||
def list(
|
||||
self,
|
||||
book_id: str,
|
||||
*,
|
||||
emails: Optional[str] = None,
|
||||
page: Optional[int] = None,
|
||||
limit: Optional[int] = None,
|
||||
ids: Optional[str] = None,
|
||||
) -> Tuple[Optional[ContactList], Optional[APIError]]:
|
||||
query: Dict[str, Any] = {}
|
||||
if emails is not None:
|
||||
query["emails"] = emails
|
||||
if page is not None:
|
||||
query["page"] = page
|
||||
if limit is not None:
|
||||
query["limit"] = limit
|
||||
if ids is not None:
|
||||
query["ids"] = ids
|
||||
|
||||
path = f"/contactBooks/{book_id}/contacts"
|
||||
if query:
|
||||
path = f"{path}?{urlencode(query)}"
|
||||
|
||||
data, err = self.usesend.get(path)
|
||||
return (data, err) # type: ignore[return-value]
|
||||
|
||||
def get(
|
||||
self, book_id: str, contact_id: str
|
||||
) -> Tuple[Optional[Contact], Optional[APIError]]:
|
||||
@@ -57,6 +89,24 @@ class Contacts:
|
||||
)
|
||||
return (data, err) # type: ignore[return-value]
|
||||
|
||||
def bulk_create(
|
||||
self, book_id: str, payload: ContactBulkCreate
|
||||
) -> Tuple[Optional[ContactBulkCreateResponse], Optional[APIError]]:
|
||||
data, err = self.usesend.post(
|
||||
f"/contactBooks/{book_id}/contacts/bulk",
|
||||
payload,
|
||||
)
|
||||
return (data, err) # type: ignore[return-value]
|
||||
|
||||
def bulk_delete(
|
||||
self, book_id: str, payload: ContactBulkDelete
|
||||
) -> Tuple[Optional[ContactBulkDeleteResponse], Optional[APIError]]:
|
||||
data, err = self.usesend.delete(
|
||||
f"/contactBooks/{book_id}/contacts/bulk",
|
||||
payload,
|
||||
)
|
||||
return (data, err) # type: ignore[return-value]
|
||||
|
||||
def delete(
|
||||
self, *, book_id: str, contact_id: str
|
||||
) -> Tuple[Optional[ContactDeleteResponse], Optional[APIError]]:
|
||||
|
||||
@@ -271,6 +271,65 @@ class EmailCancelResponse(TypedDict, total=False):
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class ContactBookCounts(TypedDict, total=False):
|
||||
contacts: int
|
||||
|
||||
|
||||
class ContactBook(TypedDict, total=False):
|
||||
id: str
|
||||
name: str
|
||||
teamId: float
|
||||
properties: Dict[str, str]
|
||||
variables: List[str]
|
||||
emoji: str
|
||||
doubleOptInEnabled: Optional[bool]
|
||||
doubleOptInFrom: Optional[str]
|
||||
doubleOptInSubject: Optional[str]
|
||||
doubleOptInContent: Optional[str]
|
||||
createdAt: str
|
||||
updatedAt: str
|
||||
_count: ContactBookCounts
|
||||
|
||||
|
||||
ContactBookList = List[ContactBook]
|
||||
|
||||
|
||||
class ContactBookCreate(TypedDict, total=False):
|
||||
name: str
|
||||
emoji: Optional[str]
|
||||
properties: Optional[Dict[str, str]]
|
||||
doubleOptInEnabled: Optional[bool]
|
||||
doubleOptInFrom: Optional[str]
|
||||
doubleOptInSubject: Optional[str]
|
||||
doubleOptInContent: Optional[str]
|
||||
variables: Optional[List[str]]
|
||||
|
||||
|
||||
class ContactBookCreateResponse(ContactBook, total=False):
|
||||
pass
|
||||
|
||||
|
||||
class ContactBookUpdate(TypedDict, total=False):
|
||||
name: Optional[str]
|
||||
emoji: Optional[str]
|
||||
properties: Optional[Dict[str, str]]
|
||||
doubleOptInEnabled: Optional[bool]
|
||||
doubleOptInFrom: Optional[str]
|
||||
doubleOptInSubject: Optional[str]
|
||||
doubleOptInContent: Optional[str]
|
||||
variables: Optional[List[str]]
|
||||
|
||||
|
||||
class ContactBookUpdateResponse(ContactBook, total=False):
|
||||
pass
|
||||
|
||||
|
||||
class ContactBookDeleteResponse(TypedDict):
|
||||
id: str
|
||||
success: bool
|
||||
message: str
|
||||
|
||||
|
||||
class ContactCreate(TypedDict, total=False):
|
||||
email: str
|
||||
firstName: Optional[str]
|
||||
@@ -298,6 +357,23 @@ class ContactListItem(TypedDict, total=False):
|
||||
ContactList = List[ContactListItem]
|
||||
|
||||
|
||||
ContactBulkCreate = List[ContactCreate]
|
||||
|
||||
|
||||
class ContactBulkCreateResponse(TypedDict):
|
||||
message: str
|
||||
count: float
|
||||
|
||||
|
||||
class ContactBulkDelete(TypedDict):
|
||||
contactIds: List[str]
|
||||
|
||||
|
||||
class ContactBulkDeleteResponse(TypedDict):
|
||||
success: bool
|
||||
count: float
|
||||
|
||||
|
||||
class ContactUpdate(TypedDict, total=False):
|
||||
firstName: Optional[str]
|
||||
lastName: Optional[str]
|
||||
|
||||
@@ -71,6 +71,8 @@ class UseSend:
|
||||
# Lazily initialise resource clients.
|
||||
self.emails = Emails(self)
|
||||
self.contacts = Contacts(self)
|
||||
self.contact_books = ContactBooks(self)
|
||||
self.contactBooks = self.contact_books
|
||||
self.domains = Domains(self)
|
||||
self.campaigns = Campaigns(self)
|
||||
|
||||
@@ -186,6 +188,7 @@ class UseSend:
|
||||
# Import here to avoid circular dependency during type checking
|
||||
from .emails import Emails # noqa: E402 pylint: disable=wrong-import-position
|
||||
from .contacts import Contacts # noqa: E402 pylint: disable=wrong-import-position
|
||||
from .contact_books import ContactBooks # noqa: E402 pylint: disable=wrong-import-position
|
||||
from .domains import Domains # type: ignore # noqa: E402
|
||||
from .campaigns import Campaigns # type: ignore # noqa: E402
|
||||
from .webhooks import Webhooks # noqa: E402 pylint: disable=wrong-import-position
|
||||
|
||||
Reference in New Issue
Block a user