-
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 10 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
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,24 @@ | ||
from mailtrap import MailtrapClient | ||
from mailtrap.models.projects import Project | ||
|
||
API_TOKEN = "YOU_API_TOKEN" | ||
ACCOUNT_ID = "YOU_ACCOUNT_ID" | ||
|
||
client = MailtrapClient(token=API_TOKEN, account_id=ACCOUNT_ID) | ||
projects_api = client.testing_api.projects | ||
|
||
|
||
def list_projects() -> list[Project]: | ||
return projects_api.get_list() | ||
|
||
|
||
def create_project(project_name: str) -> Project: | ||
return projects_api.create(project_name=project_name) | ||
|
||
|
||
def update_project(project_id: str, new_name: str) -> Project: | ||
return projects_api.update(project_id, new_name) | ||
|
||
|
||
def delete_project(project_id: str): | ||
return projects_api.delete(project_id) | ||
Empty file.
Empty file.
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,39 @@ | ||
from mailtrap.http import HttpClient | ||
from mailtrap.models.common import DeletedObject | ||
from mailtrap.models.projects import Project | ||
|
||
|
||
class ProjectsApi: | ||
def __init__(self, client: HttpClient, account_id: str) -> None: | ||
self.account_id = account_id | ||
Ihor-Bilous marked this conversation as resolved.
Show resolved
Hide resolved
|
||
self.client = client | ||
Ihor-Bilous marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
def get_list(self) -> list[Project]: | ||
andrii-porokhnavets marked this conversation as resolved.
Show resolved
Hide resolved
|
||
response = self.client.get(f"/api/accounts/{self.account_id}/projects") | ||
return [Project(**project) for project in response] | ||
|
||
def get_by_id(self, project_id: int) -> Project: | ||
response = self.client.get( | ||
f"/api/accounts/{self.account_id}/projects/{project_id}" | ||
) | ||
return Project(**response) | ||
|
||
def create(self, project_name: str) -> Project: | ||
response = self.client.post( | ||
f"/api/accounts/{self.account_id}/projects", | ||
json={"project": {"name": project_name}}, | ||
) | ||
return Project(**response) | ||
|
||
def update(self, project_id: int, project_name: str) -> Project: | ||
response = self.client.patch( | ||
f"/api/accounts/{self.account_id}/projects/{project_id}", | ||
json={"project": {"name": project_name}}, | ||
) | ||
return Project(**response) | ||
|
||
def delete(self, project_id: int) -> DeletedObject: | ||
response = self.client.delete( | ||
f"/api/accounts/{self.account_id}/projects/{project_id}", | ||
) | ||
return DeletedObject(**response) |
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,17 @@ | ||
from typing import Optional | ||
|
||
from mailtrap.api.resources.projects import ProjectsApi | ||
from mailtrap.http import HttpClient | ||
|
||
|
||
class TestingApi: | ||
def __init__( | ||
self, client: HttpClient, account_id: str, inbox_id: Optional[str] = None | ||
) -> None: | ||
self.account_id = account_id | ||
self.inbox_id = inbox_id | ||
Ihor-Bilous marked this conversation as resolved.
Show resolved
Hide resolved
|
||
self.client = client | ||
Ihor-Bilous marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
@property | ||
def projects(self) -> ProjectsApi: | ||
return ProjectsApi(account_id=self.account_id, client=self.client) |
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,3 @@ | ||
GENERAL_HOST = "mailtrap.io" | ||
Ihor-Bilous marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
DEFAULT_REQUEST_TIMEOUT = 30 # in seconds |
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,92 @@ | ||
from typing import Any | ||
from typing import NoReturn | ||
from typing import Optional | ||
|
||
from requests import Response | ||
from requests import Session | ||
|
||
from mailtrap.config import DEFAULT_REQUEST_TIMEOUT | ||
from mailtrap.exceptions import APIError | ||
from mailtrap.exceptions import AuthorizationError | ||
|
||
|
||
class HttpClient: | ||
def __init__( | ||
self, | ||
host: str, | ||
headers: Optional[dict[str, str]] = None, | ||
timeout: int = DEFAULT_REQUEST_TIMEOUT, | ||
): | ||
self._host = host | ||
self._session = Session() | ||
self._session.headers.update(headers or {}) | ||
self._timeout = timeout | ||
|
||
def get(self, path: str, params: Optional[dict[str, Any]] = None) -> Any: | ||
response = self._session.get( | ||
self._url(path), params=params, timeout=self._timeout | ||
) | ||
return self._process_response(response) | ||
|
||
def post(self, path: str, json: Optional[dict[str, Any]] = None) -> Any: | ||
response = self._session.post(self._url(path), json=json, timeout=self._timeout) | ||
return self._process_response(response) | ||
|
||
def put(self, path: str, json: Optional[dict[str, Any]] = None) -> Any: | ||
response = self._session.put(self._url(path), json=json, timeout=self._timeout) | ||
return self._process_response(response) | ||
|
||
def patch(self, path: str, json: Optional[dict[str, Any]] = None) -> Any: | ||
response = self._session.patch(self._url(path), json=json, timeout=self._timeout) | ||
return self._process_response(response) | ||
|
||
def delete(self, path: str) -> Any: | ||
response = self._session.delete(self._url(path), timeout=self._timeout) | ||
return self._process_response(response) | ||
Ihor-Bilous marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
def _url(self, path: str) -> str: | ||
return f"https://{self._host}/{path.lstrip('/')}" | ||
|
||
def _process_response(self, response: Response) -> Any: | ||
if not response.ok: | ||
self._handle_failed_response(response) | ||
return response.json() | ||
|
||
def _handle_failed_response(self, response: Response) -> NoReturn: | ||
status_code = response.status_code | ||
try: | ||
data = response.json() | ||
except ValueError as exc: | ||
raise APIError(status_code, errors=["Unknown Error"]) from exc | ||
|
||
errors = self._extract_errors(data) | ||
|
||
if status_code == 401: | ||
raise AuthorizationError(errors=errors) | ||
|
||
raise APIError(status_code, errors=errors) | ||
|
||
@staticmethod | ||
def _extract_errors(data: dict[str, Any]) -> list[str]: | ||
def flatten_errors(errors: Any) -> list[str]: | ||
if isinstance(errors, list): | ||
return [str(error) for error 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)] | ||
|
||
if "errors" in data: | ||
return flatten_errors(data["errors"]) | ||
|
||
if "error" in data: | ||
return flatten_errors(data["error"]) | ||
|
||
return ["Unknown error"] |
Empty file.
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,6 @@ | ||
from pydantic.dataclasses import dataclass | ||
|
||
|
||
@dataclass | ||
class DeletedObject: | ||
id: int |
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,34 @@ | ||
from typing import Optional | ||
|
||
from pydantic.dataclasses import dataclass | ||
|
||
from mailtrap.models.permissions import Permissions | ||
|
||
|
||
@dataclass | ||
class Inbox: | ||
id: int | ||
name: str | ||
username: str | ||
max_size: int | ||
status: str | ||
email_username: str | ||
email_username_enabled: bool | ||
sent_messages_count: int | ||
forwarded_messages_count: int | ||
used: bool | ||
forward_from_email_address: str | ||
project_id: int | ||
domain: str | ||
pop3_domain: str | ||
email_domain: str | ||
emails_count: int | ||
emails_unread_count: int | ||
smtp_ports: list[int] | ||
pop3_ports: list[int] | ||
max_message_size: int | ||
permissions: Permissions | ||
password: Optional[str] = ( | ||
None # Password is only available if you have admin permissions for the inbox. | ||
) | ||
last_message_sent_at: Optional[str] = None |
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,9 @@ | ||
from pydantic.dataclasses import dataclass | ||
|
||
|
||
@dataclass | ||
class Permissions: | ||
can_read: bool | ||
can_update: bool | ||
can_destroy: bool | ||
can_leave: bool |
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,21 @@ | ||
from typing import Optional | ||
|
||
from pydantic.dataclasses import dataclass | ||
|
||
from mailtrap.models.inboxes import Inbox | ||
from mailtrap.models.permissions import Permissions | ||
|
||
|
||
@dataclass | ||
class ShareLinks: | ||
admin: str | ||
viewer: str | ||
|
||
|
||
@dataclass | ||
class Project: | ||
id: int | ||
name: str | ||
inboxes: list[Inbox] | ||
permissions: Permissions | ||
share_links: Optional[ShareLinks] = None |
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 |
---|---|---|
@@ -1,2 +1,4 @@ | ||
-r requirements.txt | ||
|
||
pytest>=7.0.1 | ||
responses>=0.17.0 |
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 |
---|---|---|
@@ -1 +1,2 @@ | ||
requests>=2.26.0 | ||
pydantic>=2.11.7 |
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,11 @@ | ||
UNAUTHORIZED_STATUS_CODE = 401 | ||
UNAUTHORIZED_ERROR_MESSAGE = "Incorrect API token" | ||
UNAUTHORIZED_RESPONSE = {"error": UNAUTHORIZED_ERROR_MESSAGE} | ||
|
||
FORBIDDEN_STATUS_CODE = 403 | ||
FORBIDDEN_ERROR_MESSAGE = "Access forbidden" | ||
FORBIDDEN_RESPONSE = {"errors": FORBIDDEN_ERROR_MESSAGE} | ||
|
||
NOT_FOUND_STATUS_CODE = 404 | ||
NOT_FOUND_ERROR_MESSAGE = "Not Found" | ||
NOT_FOUND_RESPONSE = {"error": NOT_FOUND_ERROR_MESSAGE} |
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.