diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..b1439fa --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,38 @@ +name: Run Tests +on: + push: + branches: [ '*' ] + pull_request: + branches: [ master ] + schedule: + # Run automatically at 8AM PST Monday-Friday + - cron: '0 15 * * 1-5' + workflow_dispatch: + +jobs: + tests: + name: Run Tests + runs-on: ubuntu-latest + timeout-minutes: 20 + strategy: + matrix: + python-version: [ '3.6', '3.7', '3.8', '3.9' ] + steps: + - name: Install Poetry + uses: snok/install-poetry@v1 + - name: Checkout mailgun_sdk-sdk + uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Install Dependencies + run: | + pip install virtualenv --upgrade + make install + make test-install + + - name: Run the tests + run: make test diff --git a/.gitignore b/.gitignore index 84229f4..b1e1787 100644 --- a/.gitignore +++ b/.gitignore @@ -24,7 +24,7 @@ wheels/ *.egg-info/ .installed.cfg *.egg - +.idea # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 071fa3d..0000000 --- a/.travis.yml +++ /dev/null @@ -1,6 +0,0 @@ -language: python -python: - - "2.7" - - "3.6" -install: pip install tox-travis -script: tox diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2043dab --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +.PHONY: install test-install + + +install: + poetry install --no-dev + +test-install: install + poetry install + +analysis: + poetry run flake8 --ignore=E123,E126,E128,E501,W391,W291,W293,F401 tests + poetry run flake8 --ignore=E402,F401,W391,W291,W293 mailgun --max-line-length=300 + +test: analysis + poetry run pytest diff --git a/mailgun/__init__.py b/mailgun_sdk/__init__.py similarity index 100% rename from mailgun/__init__.py rename to mailgun_sdk/__init__.py diff --git a/mailgun/api.py b/mailgun_sdk/api.py similarity index 93% rename from mailgun/api.py rename to mailgun_sdk/api.py index 6dcdf4c..bd41af6 100644 --- a/mailgun/api.py +++ b/mailgun_sdk/api.py @@ -14,8 +14,9 @@ class MailgunApi(object): # pylint: disable=too-few-public-methods """ def __init__(self, api_key=None): - self.api_key = api_key self.session = requests.Session() + self.set_api_key(api_key) + # APIs self.ips = IPs(self) self.mailing_list = MailingList(self) @@ -24,7 +25,7 @@ def __init__(self, api_key=None): def set_api_key(self, api_key): """ - Set the API key on the client. This is called by `mailgun.initialize()`. + Set the API key on the client. This is called by `mailgun_sdk.initialize()`. :param api_key: your Mailgun API key """ diff --git a/mailgun/base.py b/mailgun_sdk/base.py similarity index 86% rename from mailgun/base.py rename to mailgun_sdk/base.py index d12b382..9c09170 100644 --- a/mailgun/base.py +++ b/mailgun_sdk/base.py @@ -15,9 +15,10 @@ class ApiResource(object): # pylint: disable=too-few-public-methods api_endpoint = None api_url = "https://api.mailgun.net/v3" - def __init__(self, api): + def __init__(self, api, base_url="https://api.mailgun.net/v3"): assert self.api_endpoint, "Missing `api_endpoint` attribute definition." self.api = api + self.api_url = base_url self.base_url = self._get_base_url() def _get_base_url(self): @@ -35,12 +36,17 @@ def request(self, method, endpoint="", **params): :param **params: every additional keyword argument is forwarded to `requests` """ - url = self.base_url + url = self._get_base_url() if endpoint: url = "{}/{}".format(url, endpoint) response = self.api.session.request(method, url, **params) - response.raise_for_status() + + try: + response.raise_for_status() + except HTTPError as e: + + raise HTTPError("{}\n{}".format(e.strerror, e.response.text), e.response) return response.json() @@ -52,9 +58,9 @@ class ApiDomainResource(ApiResource): # pylint: disable=too-few-public-methods DOMAIN_NAMESPACE = False - def __init__(self, api, domain): + def __init__(self, api, domain, base_url=ApiResource.api_url): self.domain = domain - super(ApiDomainResource, self).__init__(api) + super(ApiDomainResource, self).__init__(api, base_url) def _get_base_url(self): """ diff --git a/mailgun/domain/__init__.py b/mailgun_sdk/domain/__init__.py similarity index 100% rename from mailgun/domain/__init__.py rename to mailgun_sdk/domain/__init__.py diff --git a/mailgun/domain/ips.py b/mailgun_sdk/domain/ips.py similarity index 100% rename from mailgun/domain/ips.py rename to mailgun_sdk/domain/ips.py diff --git a/mailgun/domain/logs.py b/mailgun_sdk/domain/logs.py similarity index 100% rename from mailgun/domain/logs.py rename to mailgun_sdk/domain/logs.py diff --git a/mailgun_sdk/domain/messages.py b/mailgun_sdk/domain/messages.py new file mode 100644 index 0000000..1d5f713 --- /dev/null +++ b/mailgun_sdk/domain/messages.py @@ -0,0 +1,48 @@ +""" +Messages API. +url: https://documentation.mailgun.com/en/latest/user_manual.html#sending-via-api +""" +import json + +from mailgun_sdk.base import ApiDomainResource + + +class Messages(ApiDomainResource): + """ + Mailing list resource. + """ + + api_endpoint = "messages" + require_tls = False + """ + data example + data={ + "from": "Excited User ", + "to": ["bar@example.com"], + "subject": "Hello", + "template": "template.test", + "h:X-Mailgun-Variables": json.dumps( + {"title": "API documentation", "body": "Sending messages with templates"} + ) + }, + """ + + def send_via_template(self, from_name: str, from_email: str, to: str, subject: str, template: str, variables: dict): + payload = { + "from": "{} <{}>".format(from_name, from_email), + "to": [to], + "subject": subject, + "template": template, + "h:X-Mailgun-Variables": json.dumps(variables) + } + print(self.base_url) + + print(self.api_endpoint) + + if self.require_tls: + payload['o:require-tls'] = 'True' + + return self.request( + "POST", + data=payload, + ) diff --git a/mailgun/ips.py b/mailgun_sdk/ips.py similarity index 100% rename from mailgun/ips.py rename to mailgun_sdk/ips.py diff --git a/mailgun/mailing_list.py b/mailgun_sdk/mailing_list.py similarity index 100% rename from mailgun/mailing_list.py rename to mailgun_sdk/mailing_list.py diff --git a/setup.cfg b/setup.cfg index b88034e..a597672 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,6 @@ [metadata] description-file = README.md + +[tool.pytest] +env = + MAILGUN_BASE_URL="https://api.eu.mailgun.net/v3" \ No newline at end of file diff --git a/setup.py b/setup.py index 64486e1..10f9cea 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ setup( - name='mailgun-python-sdk', + name='mailgun_sdk', version=VERSION, description='Mailgun Python SDK (unofficial)', author='Maxime Vdb', @@ -15,7 +15,7 @@ packages=find_packages(), install_requires=['requests', 'six'], license="MIT", - keywords="mailgun api sdk", + keywords="mailgun_sdk api sdk", url='https://github.com/m-vdb/mailgun-python-sdk', download_url='https://github.com/m-vdb/mailgun-python-sdk/archive/v{}.tar.gz'.format(VERSION), project_urls={ diff --git a/tests/domain/test_init.py b/tests/domain/test_init.py index 93eed16..1ad89bc 100644 --- a/tests/domain/test_init.py +++ b/tests/domain/test_init.py @@ -1,8 +1,8 @@ import unittest -from mailgun.api import MailgunApi -from mailgun.domain import Domain -from mailgun.domain.logs import Logs +from mailgun_sdk.api import MailgunApi +from mailgun_sdk.domain import Domain +from mailgun_sdk.domain.logs import Logs api = MailgunApi() diff --git a/tests/domain/test_ips.py b/tests/domain/test_ips.py index d24a570..5db9268 100644 --- a/tests/domain/test_ips.py +++ b/tests/domain/test_ips.py @@ -2,8 +2,8 @@ from mock import patch -from mailgun.api import MailgunApi -from mailgun.domain.ips import IPs +from mailgun_sdk.api import MailgunApi +from mailgun_sdk.domain.ips import IPs api = MailgunApi() diff --git a/tests/domain/test_logs.py b/tests/domain/test_logs.py index 26eb816..511afcf 100644 --- a/tests/domain/test_logs.py +++ b/tests/domain/test_logs.py @@ -2,8 +2,8 @@ from mock import patch -from mailgun.api import MailgunApi -from mailgun.domain.logs import Logs +from mailgun_sdk.api import MailgunApi +from mailgun_sdk.domain.logs import Logs api = MailgunApi() diff --git a/tests/domain/test_messages.py b/tests/domain/test_messages.py new file mode 100644 index 0000000..6db4d4e --- /dev/null +++ b/tests/domain/test_messages.py @@ -0,0 +1,68 @@ +import json +import unittest +import os + +from mock import patch, call + +from mailgun_sdk.api import MailgunApi +from mailgun_sdk.domain.messages import Messages + + +class MessagesTestCase(unittest.TestCase): + def setUp(self): + super(MessagesTestCase, self).setUp() + api = MailgunApi(api_key="blah") + self.messages = Messages(api, api.domain('blah.net'), os.getenv('MAILGUN_BASE_URL')) + + @patch.object(Messages, "request") + def test_send_via_template(self, request): + sending_variables = {"title": "API documentation", "body": "Sending messages with templates"} + + self.messages.send_via_template( + from_name="Al brenss", + from_email="albrens@domain.com", + to="ml@domain.com", + subject="Hello", + template="template.test", + variables=sending_variables + ) + + request.assert_called_with( + "POST", + data={ + "from": "Al brenss ", + "to": ["ml@domain.com"], + "subject": "Hello", + "template": "template.test", + "h:X-Mailgun-Variables": json.dumps( + sending_variables + ) + }, + ) + + @patch.object(Messages, "request") + def test_send_with_tls(self, request): + sending_variables = {"title": "API documentation", "body": "Sending messages with templates"} + self.messages.require_tls = True + self.messages.send_via_template( + from_name="Al brenss", + from_email="albrens@domain.com", + to="ml@domain.com", + subject="Hello", + template="template.test", + variables=sending_variables + ) + + request.assert_called_with( + "POST", + data={ + "from": "Al brenss ", + "to": ["ml@domain.com"], + "subject": "Hello", + "template": "template.test", + "h:X-Mailgun-Variables": json.dumps( + sending_variables + ), + "o:require-tls": "True" + }, + ) diff --git a/tests/test_api.py b/tests/test_api.py index e2f5af0..5a8ca17 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -3,9 +3,9 @@ import requests -from mailgun.api import MailgunApi -from mailgun.domain import Domain -from mailgun.mailing_list import MailingList +from mailgun_sdk.api import MailgunApi +from mailgun_sdk.domain import Domain +from mailgun_sdk.mailing_list import MailingList class MailgunApiTestCase(unittest.TestCase): diff --git a/tests/test_base.py b/tests/test_base.py index 7edca45..0684918 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -5,8 +5,8 @@ from requests import Response from requests.exceptions import HTTPError -from mailgun.api import MailgunApi -from mailgun.base import ApiResource, ApiDomainResource, silence_error +from mailgun_sdk.api import MailgunApi +from mailgun_sdk.base import ApiResource, ApiDomainResource, silence_error class FakeResource(ApiResource): diff --git a/tests/test_init.py b/tests/test_init.py index 88a619a..e5dea8c 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -1,13 +1,13 @@ import unittest -import mailgun -from mailgun.api import MailgunApi +import mailgun_sdk +from mailgun_sdk.api import MailgunApi class MailgunInitTestCase(unittest.TestCase): def test_api(self): - self.assertIsInstance(mailgun.api, MailgunApi) + self.assertIsInstance(mailgun_sdk.api, MailgunApi) def test_initialize(self): - mailgun.initialize("api-key-xxx") - self.assertEqual(mailgun.api.api_key, "api-key-xxx") + mailgun_sdk.initialize("api-key-xxx") + self.assertEqual(mailgun_sdk.api.api_key, "api-key-xxx") diff --git a/tests/test_ips.py b/tests/test_ips.py index 6007da6..4b931bb 100644 --- a/tests/test_ips.py +++ b/tests/test_ips.py @@ -3,8 +3,8 @@ from mock import patch -from mailgun.api import MailgunApi -from mailgun.ips import IPs +from mailgun_sdk.api import MailgunApi +from mailgun_sdk.ips import IPs class IPsTestCase(unittest.TestCase): diff --git a/tests/test_mailing_list.py b/tests/test_mailing_list.py index 0002c43..7327395 100644 --- a/tests/test_mailing_list.py +++ b/tests/test_mailing_list.py @@ -3,8 +3,8 @@ from mock import patch, call -from mailgun.api import MailgunApi -from mailgun.mailing_list import MailingList +from mailgun_sdk.api import MailgunApi +from mailgun_sdk.mailing_list import MailingList class MailingListTestCase(unittest.TestCase): @@ -125,8 +125,7 @@ def test_update_multiple_list_members_several_batches(self, request): "ml@domain.com/members.json", data={ "members": json.dumps( - ["member@gmail.com"] - * self.mailing_list.MEMBERS_UPLOAD_LIMIT + ["member@gmail.com"] * self.mailing_list.MEMBERS_UPLOAD_LIMIT ), "upsert": "yes", }, diff --git a/tox.ini b/tox.ini index 67ffbc5..42d0fd4 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,6 @@ [tox] -envlist = py27,py36 +envlist = py3{6,7,8,9}, pypy + [testenv] passenv = TRAVIS TRAVIS_*