-
Notifications
You must be signed in to change notification settings - Fork 7
Fix issue #29. Add support of Emails Sandbox (Testing) API: Projects #31
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
Merged
Merged
Changes from 2 commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
16ac0ab
Fix issue #29. Add support of Emails Sandbox (Testing) API: Projects
994cacf
Fix typo for DELETE http method
3723d35
Fix issue #29. Implement new version of HttpClient and use it for Pro…
26d8c57
Fixx issue #29. Start using pydantic models instead of dataclasses
efa0737
Fix issue #29: Add tests for error responses, add user_input validati…
7bb3725
Fix issue #29: Change the way how to set access token
1f3ff3a
Fix issue #29: Split Mailtrap client into current and new
6d4dce9
Fix issue #29: Remove unused import in project models
92c788a
Fix issue #29: Fixed names, changed location of methods
57a5452
Fix issue #29: Start using pydantic.dataclasses instead of simple da…
20826ef
Fix issue #29: make attributes as private in api classes
2e5d0b2
Fix issue #29: Update README.md file
0a1a767
Fix issue #29: Update README.md description
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
from abc import ABC | ||
from enum import Enum | ||
from typing import Any, Dict, List, NoReturn, Union, cast | ||
from requests import Response, Session | ||
|
||
from mailtrap.exceptions import APIError, AuthorizationError | ||
|
||
RESPONSE_TYPE = Dict[str, Any] | ||
LIST_RESPONSE_TYPE = List[Dict[str, Any]] | ||
|
||
|
||
class HttpMethod(Enum): | ||
GET = "GET" | ||
POST = "POST" | ||
PUT = "PUT" | ||
PATCH = "PATCH" | ||
DELETE = "DELETE" | ||
|
||
|
||
def _extract_errors(data: Dict[str, Any]) -> List[str]: | ||
if "errors" in data: | ||
errors = data["errors"] | ||
|
||
if isinstance(errors, list): | ||
return [str(err) for err in errors] | ||
|
||
if isinstance(errors, dict): | ||
flat_errors = [] | ||
for key, value in errors.items(): | ||
if isinstance(value, list): | ||
flat_errors.extend([f"{key}: {v}" for v in value]) | ||
else: | ||
flat_errors.append(f"{key}: {value}") | ||
return flat_errors | ||
|
||
return [str(errors)] | ||
|
||
elif "error" in data: | ||
return [str(data["error"])] | ||
|
||
return ["Unknown error"] | ||
|
||
|
||
class BaseHttpApiClient(ABC): | ||
def __init__(self, session: Session): | ||
self.session = session | ||
|
||
def _request(self, method: HttpMethod, url: str, **kwargs: Any) -> Union[RESPONSE_TYPE, LIST_RESPONSE_TYPE]: | ||
response = self.session.request(method.value, url, **kwargs) | ||
if response.ok: | ||
data = cast(Union[RESPONSE_TYPE, LIST_RESPONSE_TYPE], response.json()) | ||
return data | ||
|
||
self._handle_failed_response(response) | ||
|
||
@staticmethod | ||
def _handle_failed_response(response: Response) -> NoReturn: | ||
status_code = response.status_code | ||
|
||
try: | ||
data = response.json() | ||
except ValueError: | ||
raise APIError(status_code, errors=["Unknown Error"]) | ||
|
||
errors = _extract_errors(data) | ||
|
||
if status_code == 401: | ||
raise AuthorizationError(errors=errors) | ||
|
||
raise APIError(status_code, errors=errors) | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
from typing import List, cast | ||
|
||
from mailtrap.api.base import LIST_RESPONSE_TYPE, RESPONSE_TYPE, BaseHttpApiClient, HttpMethod | ||
from mailtrap.constants import MAILTRAP_HOST | ||
from mailtrap.models.common import DeletedObject | ||
from mailtrap.models.projects import Project | ||
|
||
|
||
class ProjectsApiClient(BaseHttpApiClient): | ||
|
||
def _build_url(self, account_id: str, *parts: str) -> str: | ||
base_url = f"https://{MAILTRAP_HOST}/api/accounts/{account_id}/projects" | ||
return "/".join([base_url, *parts]) | ||
|
||
def get_list(self, account_id: str) -> List[Project]: | ||
response: LIST_RESPONSE_TYPE = cast(LIST_RESPONSE_TYPE, self._request( | ||
HttpMethod.GET, | ||
self._build_url(account_id) | ||
)) | ||
return [Project.from_dict(proj) for proj in response] | ||
|
||
def get_by_id(self, account_id: str, project_id: str) -> Project: | ||
response: RESPONSE_TYPE = cast(RESPONSE_TYPE, self._request( | ||
HttpMethod.GET, | ||
self._build_url(account_id, project_id) | ||
)) | ||
return Project.from_dict(response) | ||
|
||
def create(self, account_id: str, name: str) -> Project: | ||
response: RESPONSE_TYPE = cast(RESPONSE_TYPE, self._request( | ||
HttpMethod.POST, | ||
self._build_url(account_id), | ||
json={"project": {"name": name}}, | ||
)) | ||
return Project.from_dict(response) | ||
|
||
def update(self, account_id: str, project_id: str, name: str) -> Project: | ||
response: RESPONSE_TYPE = cast(RESPONSE_TYPE, self._request( | ||
HttpMethod.PATCH, | ||
self._build_url(account_id, project_id), | ||
json={"project": {"name": name}}, | ||
)) | ||
return Project.from_dict(response) | ||
|
||
def delete(self, account_id: str, project_id: str) -> DeletedObject: | ||
response: RESPONSE_TYPE = cast(RESPONSE_TYPE, self._request( | ||
HttpMethod.DELETE, | ||
self._build_url(account_id, project_id), | ||
)) | ||
return DeletedObject(response["id"]) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
MAILTRAP_HOST = "mailtrap.io" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
class DeletedObject: | ||
def __init__(self, id: str): | ||
self.id = id |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
from typing import Any, List, Dict | ||
|
||
|
||
class ShareLinks: | ||
def __init__(self, admin: str, viewer: str): | ||
self.admin = admin | ||
self.viewer = viewer | ||
|
||
|
||
class Permissions: | ||
def __init__( | ||
self, | ||
can_read: bool, | ||
can_update: bool, | ||
can_destroy: bool, | ||
can_leave: bool | ||
): | ||
self.can_read = can_read | ||
self.can_update = can_update | ||
self.can_destroy = can_destroy | ||
self.can_leave = can_leave | ||
|
||
|
||
class Project: | ||
def __init__( | ||
self, | ||
id: str, | ||
name: str, | ||
share_links: ShareLinks, | ||
inboxes: List[Dict[str, Any]], | ||
permissions: Permissions | ||
): | ||
self.id = id | ||
self.name = name | ||
self.share_links = share_links | ||
self.inboxes = inboxes | ||
self.permissions = permissions | ||
|
||
@classmethod | ||
def from_dict(cls, data: Dict[str, Any]) -> "Project": | ||
share_links = ShareLinks(**data["share_links"]) | ||
permissions = Permissions(**data["permissions"]) | ||
inboxes = data.get("inboxes", []) | ||
return cls( | ||
id=data["id"], | ||
name=data["name"], | ||
share_links=share_links, | ||
inboxes=inboxes, | ||
permissions=permissions, | ||
) | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
Empty file.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.