Initial commit: Email alerts application
This commit is contained in:
@@ -0,0 +1,492 @@
|
||||
import json
|
||||
from typing import Any, AsyncIterator, Dict, Iterator, Optional, Tuple
|
||||
|
||||
from twilio.base import values
|
||||
from twilio.base.domain import Domain
|
||||
from twilio.base.exceptions import TwilioRestException
|
||||
from twilio.base.page import Page
|
||||
from twilio.http.response import Response
|
||||
|
||||
|
||||
class Version(object):
|
||||
"""
|
||||
Represents an API version.
|
||||
"""
|
||||
|
||||
def __init__(self, domain: Domain, version: str):
|
||||
self.domain = domain
|
||||
self.version = version
|
||||
|
||||
def absolute_url(self, uri: str) -> str:
|
||||
"""
|
||||
Turns a relative uri into an absolute url.
|
||||
"""
|
||||
return self.domain.absolute_url(self.relative_uri(uri))
|
||||
|
||||
def relative_uri(self, uri: str) -> str:
|
||||
"""
|
||||
Turns a relative uri into a versioned relative uri.
|
||||
"""
|
||||
return "{}/{}".format(self.version.strip("/"), uri.strip("/"))
|
||||
|
||||
def request(
|
||||
self,
|
||||
method: str,
|
||||
uri: str,
|
||||
params: Optional[Dict[str, object]] = None,
|
||||
data: Optional[Dict[str, object]] = None,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
auth: Optional[Tuple[str, str]] = None,
|
||||
timeout: Optional[float] = None,
|
||||
allow_redirects: bool = False,
|
||||
) -> Response:
|
||||
"""
|
||||
Make an HTTP request.
|
||||
"""
|
||||
url = self.relative_uri(uri)
|
||||
return self.domain.request(
|
||||
method,
|
||||
url,
|
||||
params=params,
|
||||
data=data,
|
||||
headers=headers,
|
||||
auth=auth,
|
||||
timeout=timeout,
|
||||
allow_redirects=allow_redirects,
|
||||
)
|
||||
|
||||
async def request_async(
|
||||
self,
|
||||
method: str,
|
||||
uri: str,
|
||||
params: Optional[Dict[str, object]] = None,
|
||||
data: Optional[Dict[str, object]] = None,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
auth: Optional[Tuple[str, str]] = None,
|
||||
timeout: Optional[float] = None,
|
||||
allow_redirects: bool = False,
|
||||
) -> Response:
|
||||
"""
|
||||
Make an asynchronous HTTP request
|
||||
"""
|
||||
url = self.relative_uri(uri)
|
||||
return await self.domain.request_async(
|
||||
method,
|
||||
url,
|
||||
params=params,
|
||||
data=data,
|
||||
headers=headers,
|
||||
auth=auth,
|
||||
timeout=timeout,
|
||||
allow_redirects=allow_redirects,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def exception(
|
||||
cls, method: str, uri: str, response: Response, message: str
|
||||
) -> TwilioRestException:
|
||||
"""
|
||||
Wraps an exceptional response in a `TwilioRestException`.
|
||||
"""
|
||||
# noinspection PyBroadException
|
||||
try:
|
||||
error_payload = json.loads(response.text)
|
||||
if "message" in error_payload:
|
||||
message = "{}: {}".format(message, error_payload["message"])
|
||||
details = error_payload.get("details")
|
||||
code = error_payload.get("code", response.status_code)
|
||||
return TwilioRestException(
|
||||
response.status_code, uri, message, code, method, details
|
||||
)
|
||||
except Exception:
|
||||
return TwilioRestException(
|
||||
response.status_code, uri, message, response.status_code, method
|
||||
)
|
||||
|
||||
def _parse_fetch(self, method: str, uri: str, response: Response) -> Any:
|
||||
"""
|
||||
Parses fetch response JSON
|
||||
"""
|
||||
# Note that 3XX response codes are allowed for fetches.
|
||||
if response.status_code < 200 or response.status_code >= 400:
|
||||
raise self.exception(method, uri, response, "Unable to fetch record")
|
||||
|
||||
return json.loads(response.text)
|
||||
|
||||
def fetch(
|
||||
self,
|
||||
method: str,
|
||||
uri: str,
|
||||
params: Optional[Dict[str, object]] = None,
|
||||
data: Optional[Dict[str, object]] = None,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
auth: Optional[Tuple[str, str]] = None,
|
||||
timeout: Optional[float] = None,
|
||||
allow_redirects: bool = False,
|
||||
) -> Any:
|
||||
"""
|
||||
Fetch a resource instance.
|
||||
"""
|
||||
response = self.request(
|
||||
method,
|
||||
uri,
|
||||
params=params,
|
||||
data=data,
|
||||
headers=headers,
|
||||
auth=auth,
|
||||
timeout=timeout,
|
||||
allow_redirects=allow_redirects,
|
||||
)
|
||||
|
||||
return self._parse_fetch(method, uri, response)
|
||||
|
||||
async def fetch_async(
|
||||
self,
|
||||
method: str,
|
||||
uri: str,
|
||||
params: Optional[Dict[str, object]] = None,
|
||||
data: Optional[Dict[str, object]] = None,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
auth: Optional[Tuple[str, str]] = None,
|
||||
timeout: Optional[float] = None,
|
||||
allow_redirects: bool = False,
|
||||
) -> Any:
|
||||
"""
|
||||
Asynchronously fetch a resource instance.
|
||||
"""
|
||||
response = await self.request_async(
|
||||
method,
|
||||
uri,
|
||||
params=params,
|
||||
data=data,
|
||||
headers=headers,
|
||||
auth=auth,
|
||||
timeout=timeout,
|
||||
allow_redirects=allow_redirects,
|
||||
)
|
||||
|
||||
return self._parse_fetch(method, uri, response)
|
||||
|
||||
def _parse_update(self, method: str, uri: str, response: Response) -> Any:
|
||||
"""
|
||||
Parses update response JSON
|
||||
"""
|
||||
if response.status_code < 200 or response.status_code >= 300:
|
||||
raise self.exception(method, uri, response, "Unable to update record")
|
||||
|
||||
return json.loads(response.text)
|
||||
|
||||
def update(
|
||||
self,
|
||||
method: str,
|
||||
uri: str,
|
||||
params: Optional[Dict[str, object]] = None,
|
||||
data: Optional[Dict[str, object]] = None,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
auth: Optional[Tuple[str, str]] = None,
|
||||
timeout: Optional[float] = None,
|
||||
allow_redirects: bool = False,
|
||||
) -> Any:
|
||||
"""
|
||||
Update a resource instance.
|
||||
"""
|
||||
response = self.request(
|
||||
method,
|
||||
uri,
|
||||
params=params,
|
||||
data=data,
|
||||
headers=headers,
|
||||
auth=auth,
|
||||
timeout=timeout,
|
||||
allow_redirects=allow_redirects,
|
||||
)
|
||||
|
||||
return self._parse_update(method, uri, response)
|
||||
|
||||
async def update_async(
|
||||
self,
|
||||
method: str,
|
||||
uri: str,
|
||||
params: Optional[Dict[str, object]] = None,
|
||||
data: Optional[Dict[str, object]] = None,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
auth: Optional[Tuple[str, str]] = None,
|
||||
timeout: Optional[float] = None,
|
||||
allow_redirects: bool = False,
|
||||
) -> Any:
|
||||
"""
|
||||
Asynchronously update a resource instance.
|
||||
"""
|
||||
response = await self.request_async(
|
||||
method,
|
||||
uri,
|
||||
params=params,
|
||||
data=data,
|
||||
headers=headers,
|
||||
auth=auth,
|
||||
timeout=timeout,
|
||||
allow_redirects=allow_redirects,
|
||||
)
|
||||
|
||||
return self._parse_update(method, uri, response)
|
||||
|
||||
def _parse_delete(self, method: str, uri: str, response: Response) -> bool:
|
||||
"""
|
||||
Parses delete response JSON
|
||||
"""
|
||||
if response.status_code < 200 or response.status_code >= 300:
|
||||
raise self.exception(method, uri, response, "Unable to delete record")
|
||||
|
||||
return response.status_code == 204
|
||||
|
||||
def delete(
|
||||
self,
|
||||
method: str,
|
||||
uri: str,
|
||||
params: Optional[Dict[str, object]] = None,
|
||||
data: Optional[Dict[str, object]] = None,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
auth: Optional[Tuple[str, str]] = None,
|
||||
timeout: Optional[float] = None,
|
||||
allow_redirects: bool = False,
|
||||
) -> bool:
|
||||
"""
|
||||
Delete a resource.
|
||||
"""
|
||||
response = self.request(
|
||||
method,
|
||||
uri,
|
||||
params=params,
|
||||
data=data,
|
||||
headers=headers,
|
||||
auth=auth,
|
||||
timeout=timeout,
|
||||
allow_redirects=allow_redirects,
|
||||
)
|
||||
|
||||
return self._parse_delete(method, uri, response)
|
||||
|
||||
async def delete_async(
|
||||
self,
|
||||
method: str,
|
||||
uri: str,
|
||||
params: Optional[Dict[str, object]] = None,
|
||||
data: Optional[Dict[str, object]] = None,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
auth: Optional[Tuple[str, str]] = None,
|
||||
timeout: Optional[float] = None,
|
||||
allow_redirects: bool = False,
|
||||
) -> bool:
|
||||
"""
|
||||
Asynchronously delete a resource.
|
||||
"""
|
||||
response = await self.request_async(
|
||||
method,
|
||||
uri,
|
||||
params=params,
|
||||
data=data,
|
||||
headers=headers,
|
||||
auth=auth,
|
||||
timeout=timeout,
|
||||
allow_redirects=allow_redirects,
|
||||
)
|
||||
|
||||
return self._parse_delete(method, uri, response)
|
||||
|
||||
def read_limits(
|
||||
self, limit: Optional[int] = None, page_size: Optional[int] = None
|
||||
) -> Dict[str, object]:
|
||||
"""
|
||||
Takes a limit on the max number of records to read and a max page_size
|
||||
and calculates the max number of pages to read.
|
||||
|
||||
:param limit: Max number of records to read.
|
||||
:param page_size: Max page size.
|
||||
:return A dictionary of paging limits.
|
||||
"""
|
||||
if limit is not None and page_size is None:
|
||||
page_size = limit
|
||||
|
||||
return {
|
||||
"limit": limit or values.unset,
|
||||
"page_size": page_size or values.unset,
|
||||
}
|
||||
|
||||
def page(
|
||||
self,
|
||||
method: str,
|
||||
uri: str,
|
||||
params: Optional[Dict[str, object]] = None,
|
||||
data: Optional[Dict[str, object]] = None,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
auth: Optional[Tuple[str, str]] = None,
|
||||
timeout: Optional[float] = None,
|
||||
allow_redirects: bool = False,
|
||||
) -> Response:
|
||||
"""
|
||||
Makes an HTTP request.
|
||||
"""
|
||||
return self.request(
|
||||
method,
|
||||
uri,
|
||||
params=params,
|
||||
data=data,
|
||||
headers=headers,
|
||||
auth=auth,
|
||||
timeout=timeout,
|
||||
allow_redirects=allow_redirects,
|
||||
)
|
||||
|
||||
async def page_async(
|
||||
self,
|
||||
method: str,
|
||||
uri: str,
|
||||
params: Optional[Dict[str, object]] = None,
|
||||
data: Optional[Dict[str, object]] = None,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
auth: Optional[Tuple[str, str]] = None,
|
||||
timeout: Optional[float] = None,
|
||||
allow_redirects: bool = False,
|
||||
) -> Response:
|
||||
"""
|
||||
Makes an asynchronous HTTP request.
|
||||
"""
|
||||
return await self.request_async(
|
||||
method,
|
||||
uri,
|
||||
params=params,
|
||||
data=data,
|
||||
headers=headers,
|
||||
auth=auth,
|
||||
timeout=timeout,
|
||||
allow_redirects=allow_redirects,
|
||||
)
|
||||
|
||||
def stream(
|
||||
self,
|
||||
page: Optional[Page],
|
||||
limit: Optional[int] = None,
|
||||
page_limit: Optional[int] = None,
|
||||
) -> Iterator[Any]:
|
||||
"""
|
||||
Generates records one a time from a page, stopping at prescribed limits.
|
||||
|
||||
:param page: The page to stream.
|
||||
:param limit: The max number of records to read.
|
||||
:param page_limit: The max number of pages to read.
|
||||
"""
|
||||
current_record = 1
|
||||
current_page = 1
|
||||
|
||||
while page is not None:
|
||||
for record in page:
|
||||
yield record
|
||||
current_record += 1
|
||||
if limit and limit is not values.unset and limit < current_record:
|
||||
return
|
||||
|
||||
current_page += 1
|
||||
if (
|
||||
page_limit
|
||||
and page_limit is not values.unset
|
||||
and page_limit < current_page
|
||||
):
|
||||
return
|
||||
|
||||
page = page.next_page()
|
||||
|
||||
async def stream_async(
|
||||
self,
|
||||
page: Optional[Page],
|
||||
limit: Optional[int] = None,
|
||||
page_limit: Optional[int] = None,
|
||||
) -> AsyncIterator[Any]:
|
||||
"""
|
||||
Generates records one a time from a page, stopping at prescribed limits.
|
||||
|
||||
:param page: The page to stream.
|
||||
:param limit: The max number of records to read.
|
||||
:param page_limit: The max number of pages to read.
|
||||
"""
|
||||
current_record = 1
|
||||
current_page = 1
|
||||
|
||||
while page is not None:
|
||||
for record in page:
|
||||
yield record
|
||||
current_record += 1
|
||||
if limit and limit is not values.unset and limit < current_record:
|
||||
return
|
||||
|
||||
current_page += 1
|
||||
if (
|
||||
page_limit
|
||||
and page_limit is not values.unset
|
||||
and page_limit < current_page
|
||||
):
|
||||
return
|
||||
|
||||
page = await page.next_page_async()
|
||||
|
||||
def _parse_create(self, method: str, uri: str, response: Response) -> Any:
|
||||
"""
|
||||
Parse create response JSON
|
||||
"""
|
||||
if response.status_code < 200 or response.status_code >= 300:
|
||||
raise self.exception(method, uri, response, "Unable to create record")
|
||||
|
||||
return json.loads(response.text)
|
||||
|
||||
def create(
|
||||
self,
|
||||
method: str,
|
||||
uri: str,
|
||||
params: Optional[Dict[str, object]] = None,
|
||||
data: Optional[Dict[str, object]] = None,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
auth: Optional[Tuple[str, str]] = None,
|
||||
timeout: Optional[float] = None,
|
||||
allow_redirects: bool = False,
|
||||
) -> Any:
|
||||
"""
|
||||
Create a resource instance.
|
||||
"""
|
||||
response = self.request(
|
||||
method,
|
||||
uri,
|
||||
params=params,
|
||||
data=data,
|
||||
headers=headers,
|
||||
auth=auth,
|
||||
timeout=timeout,
|
||||
allow_redirects=allow_redirects,
|
||||
)
|
||||
|
||||
return self._parse_create(method, uri, response)
|
||||
|
||||
async def create_async(
|
||||
self,
|
||||
method: str,
|
||||
uri: str,
|
||||
params: Optional[Dict[str, object]] = None,
|
||||
data: Optional[Dict[str, object]] = None,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
auth: Optional[Tuple[str, str]] = None,
|
||||
timeout: Optional[float] = None,
|
||||
allow_redirects: bool = False,
|
||||
) -> Any:
|
||||
"""
|
||||
Asynchronously create a resource instance.
|
||||
"""
|
||||
response = await self.request_async(
|
||||
method,
|
||||
uri,
|
||||
params=params,
|
||||
data=data,
|
||||
headers=headers,
|
||||
auth=auth,
|
||||
timeout=timeout,
|
||||
allow_redirects=allow_redirects,
|
||||
)
|
||||
|
||||
return self._parse_create(method, uri, response)
|
||||
Reference in New Issue
Block a user