From 1a7084d0c9582f91f395ea2aab6eaf2bb68f8215 Mon Sep 17 00:00:00 2001 From: Eric Fossum Date: Mon, 8 Feb 2021 10:33:39 -0800 Subject: [PATCH 1/8] Incrementing version. Adding build artifacts to gitignore. --- .gitignore | 9 +++++++++ setup.py | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index d1d3644..70396f3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,11 @@ + +# Run artifacts *.pyc + +# Local secrets *.env + +# Python package artifacts. +*.egg-info +build/ +dist/ diff --git a/setup.py b/setup.py index 00d5ec0..b2cdef8 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='flowroute-messaging-python', - version='0.0.0', + version='0.1.0', license='MIT', description='Flowroute\'s Messaging API', author='Flowroute Developers', From 47d84f032d5beeb18bee2e67d6f83f72ddfaa7c9 Mon Sep 17 00:00:00 2001 From: Eric Fossum Date: Mon, 8 Feb 2021 11:22:52 -0800 Subject: [PATCH 2/8] Adding Github CI file. Fixing request version. Adding basic test. --- .github/workflows/python-package.yml | 39 +++++++++++++++++++ .../test/test_apicontroller.py | 21 ++++++++++ pyproject.toml | 3 ++ requirements.txt | 2 +- 4 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/python-package.yml create mode 100644 FlowrouteMessagingLib/test/test_apicontroller.py create mode 100644 pyproject.toml diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml new file mode 100644 index 0000000..58f9017 --- /dev/null +++ b/.github/workflows/python-package.yml @@ -0,0 +1,39 @@ +# This workflow will install Python dependencies, run tests and lint with a variety of Python versions +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +name: Python package + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.7, 3.8, 3.9] + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install flake8 pytest + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with pytest + run: | + pytest diff --git a/FlowrouteMessagingLib/test/test_apicontroller.py b/FlowrouteMessagingLib/test/test_apicontroller.py new file mode 100644 index 0000000..d83bcdc --- /dev/null +++ b/FlowrouteMessagingLib/test/test_apicontroller.py @@ -0,0 +1,21 @@ + +import unittest + +from FlowrouteMessagingLib.Controllers.APIController import APIController +from FlowrouteMessagingLib.Models import Message +from FlowrouteMessagingLib import APIException + + +class TestAPIController(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.controller = APIController('my_user', 'my_pass') + cls.message = Message.Message(to='to_number', from_='from_number', content='Your cool new SMS message here!') + return super().setUpClass() + + def test_create_message_bad_auth(self): + with self.assertRaises(APIException.APIException): + self.controller.create_message(self.message) + +if __name__ == "__main__": + unittest.main() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..270127c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools", "wheel"] +build-backend = "setuptools.build_meta:__legacy__" \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index b0cf2b9..0292e04 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ -requests==1.1.6 +requests==2.25.1 jsonpickle==0.7.1 From 4865a799884b3b8250ac0bf397c96df80cd79f76 Mon Sep 17 00:00:00 2001 From: Eric Fossum Date: Mon, 8 Feb 2021 11:46:39 -0800 Subject: [PATCH 3/8] Attempting CI package install fix. --- .github/workflows/python-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 58f9017..352fc8e 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -27,7 +27,7 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install flake8 pytest - if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + python -m pip install . - name: Lint with flake8 run: | # stop the build if there are Python syntax errors or undefined names From 86d752e246247b0e3b439b0bf20ede58bd3510ba Mon Sep 17 00:00:00 2001 From: Eric Fossum Date: Mon, 8 Feb 2021 11:51:22 -0800 Subject: [PATCH 4/8] Adding Python 2.7 to test matrix. --- .github/workflows/python-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 352fc8e..0d5e030 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.7, 3.8, 3.9] + python-version: [2.7, 3.7, 3.8, 3.9] steps: - uses: actions/checkout@v2 From 6d265a355f3724afbe92e62d778d3dae87247cef Mon Sep 17 00:00:00 2001 From: Eric Fossum Date: Mon, 8 Feb 2021 11:55:09 -0800 Subject: [PATCH 5/8] Add package upload CI. Personalize package meta. --- .github/workflows/package-upload.yml | 26 ++++++++++++++++++++++++++ FlowrouteMessagingLib/APIHelper.py | 4 ++-- setup.py | 8 ++++---- 3 files changed, 32 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/package-upload.yml diff --git a/.github/workflows/package-upload.yml b/.github/workflows/package-upload.yml new file mode 100644 index 0000000..c012281 --- /dev/null +++ b/.github/workflows/package-upload.yml @@ -0,0 +1,26 @@ +name: Upload Python Package + +on: + release: + types: [created] + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.x' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install setuptools wheel twine + - name: Build and publish + env: + TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} + TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + run: | + python setup.py sdist bdist_wheel + twine upload dist/* \ No newline at end of file diff --git a/FlowrouteMessagingLib/APIHelper.py b/FlowrouteMessagingLib/APIHelper.py index dd49686..6e0159b 100644 --- a/FlowrouteMessagingLib/APIHelper.py +++ b/FlowrouteMessagingLib/APIHelper.py @@ -24,7 +24,7 @@ def json_serialize(cls, obj): JSON Serialization of a given object. Args: - obj (object): The object to serialise. + obj (object): The object to serialize. Returns: str: The JSON serialized string of the object. @@ -56,7 +56,7 @@ def json_serialize(cls, obj): @classmethod def json_deserialize(cls, json): """ - JSON Deerialization of a given string. + JSON Deserialization of a given string. Args: json (str): The JSON serialized string to deserialize. diff --git a/setup.py b/setup.py index b2cdef8..a99d00e 100644 --- a/setup.py +++ b/setup.py @@ -2,13 +2,13 @@ setup( - name='flowroute-messaging-python', + name='flowroute-messaging-fossum', version='0.1.0', license='MIT', - description='Flowroute\'s Messaging API', + description='Flowroute\'s Messaging API (Fossum edition).', author='Flowroute Developers', - author_email='developer@flowroute.com', - url='https://github.com/flowroute/flowroute-messaging-python', + author_email='fossum.eric@gmail.com', + url='https://github.com/fossum/flowroute-messaging-python', packages=find_packages('.'), zip_safe=False, classifiers=[ From 21bbf7e29fade9c814e5c5da81ee0a6d4a2d41ee Mon Sep 17 00:00:00 2001 From: Eric Fossum Date: Mon, 8 Feb 2021 12:00:06 -0800 Subject: [PATCH 6/8] Removing 2.7 as code is no longer compatible. --- .github/workflows/python-package.yml | 2 +- setup.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 0d5e030..352fc8e 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [2.7, 3.7, 3.8, 3.9] + python-version: [3.7, 3.8, 3.9] steps: - uses: actions/checkout@v2 diff --git a/setup.py b/setup.py index a99d00e..c94c608 100644 --- a/setup.py +++ b/setup.py @@ -19,6 +19,8 @@ 'Operating System :: POSIX', 'Operating System :: Microsoft :: Windows', 'Programming Language :: Python', + 'Programming Language :: Python :: 3 :: Only', + 'Topic :: Communications :: Telephony' ], keywords=[ 'messaging', 'sms', 'telephony', 'sip', 'api' From c395a3b14310c8264b42575d3e64d950c987fbfc Mon Sep 17 00:00:00 2001 From: Eric Fossum Date: Thu, 11 Feb 2021 12:38:35 -0800 Subject: [PATCH 7/8] Rename most of the package files. --- FlowrouteMessagingLib/Models/__init__.py | 0 FlowrouteMessagingLib/__init__.py | 0 {FlowrouteMessagingLib/Controllers => flowroute}/__init__.py | 0 .../Configuration.py => flowroute/configuration.py | 0 .../Controllers/APIController.py => flowroute/controller.py | 2 +- .../APIException.py => flowroute/exception.py | 0 FlowrouteMessagingLib/APIHelper.py => flowroute/helper.py | 0 .../Models/Message.py => flowroute/message.py | 0 .../test/test_apicontroller.py | 0 setup.py | 4 ++-- 10 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 FlowrouteMessagingLib/Models/__init__.py delete mode 100644 FlowrouteMessagingLib/__init__.py rename {FlowrouteMessagingLib/Controllers => flowroute}/__init__.py (100%) rename FlowrouteMessagingLib/Configuration.py => flowroute/configuration.py (100%) rename FlowrouteMessagingLib/Controllers/APIController.py => flowroute/controller.py (98%) rename FlowrouteMessagingLib/APIException.py => flowroute/exception.py (100%) rename FlowrouteMessagingLib/APIHelper.py => flowroute/helper.py (100%) rename FlowrouteMessagingLib/Models/Message.py => flowroute/message.py (100%) rename {FlowrouteMessagingLib => flowroute}/test/test_apicontroller.py (100%) diff --git a/FlowrouteMessagingLib/Models/__init__.py b/FlowrouteMessagingLib/Models/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/FlowrouteMessagingLib/__init__.py b/FlowrouteMessagingLib/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/FlowrouteMessagingLib/Controllers/__init__.py b/flowroute/__init__.py similarity index 100% rename from FlowrouteMessagingLib/Controllers/__init__.py rename to flowroute/__init__.py diff --git a/FlowrouteMessagingLib/Configuration.py b/flowroute/configuration.py similarity index 100% rename from FlowrouteMessagingLib/Configuration.py rename to flowroute/configuration.py diff --git a/FlowrouteMessagingLib/Controllers/APIController.py b/flowroute/controller.py similarity index 98% rename from FlowrouteMessagingLib/Controllers/APIController.py rename to flowroute/controller.py index 643bbb1..14ba0a1 100644 --- a/FlowrouteMessagingLib/Controllers/APIController.py +++ b/flowroute/controller.py @@ -7,7 +7,7 @@ import requests from FlowrouteMessagingLib.APIHelper import APIHelper -from FlowrouteMessagingLib.Configuration import Configuration +from FlowrouteMessagingLib.configuration import Configuration from FlowrouteMessagingLib.APIException import APIException diff --git a/FlowrouteMessagingLib/APIException.py b/flowroute/exception.py similarity index 100% rename from FlowrouteMessagingLib/APIException.py rename to flowroute/exception.py diff --git a/FlowrouteMessagingLib/APIHelper.py b/flowroute/helper.py similarity index 100% rename from FlowrouteMessagingLib/APIHelper.py rename to flowroute/helper.py diff --git a/FlowrouteMessagingLib/Models/Message.py b/flowroute/message.py similarity index 100% rename from FlowrouteMessagingLib/Models/Message.py rename to flowroute/message.py diff --git a/FlowrouteMessagingLib/test/test_apicontroller.py b/flowroute/test/test_apicontroller.py similarity index 100% rename from FlowrouteMessagingLib/test/test_apicontroller.py rename to flowroute/test/test_apicontroller.py diff --git a/setup.py b/setup.py index c94c608..a4af05c 100644 --- a/setup.py +++ b/setup.py @@ -3,10 +3,10 @@ setup( name='flowroute-messaging-fossum', - version='0.1.0', + version='0.2.0', license='MIT', description='Flowroute\'s Messaging API (Fossum edition).', - author='Flowroute Developers', + author='Eric Fossum', author_email='fossum.eric@gmail.com', url='https://github.com/fossum/flowroute-messaging-python', packages=find_packages('.'), From 191a2c9eed1a0358fba5673fcc75a5a814d2d5d4 Mon Sep 17 00:00:00 2001 From: Eric Fossum Date: Thu, 11 Feb 2021 13:37:15 -0800 Subject: [PATCH 8/8] Updated files to use new package layout. --- .gitignore | 1 + demo_send.py | 13 ++++--- flowroute/__init__.py | 4 +++ flowroute/configuration.py | 7 ++-- flowroute/controller.py | 47 +++++++++++++------------ flowroute/exception.py | 24 ++++++------- flowroute/helper.py | 52 ++++++++++++---------------- flowroute/message.py | 28 +++++++-------- flowroute/test/test_apicontroller.py | 50 ++++++++++++++++++++------ flowroute/test/test_helper.py | 30 ++++++++++++++++ 10 files changed, 158 insertions(+), 98 deletions(-) create mode 100644 flowroute/test/test_helper.py diff --git a/.gitignore b/.gitignore index 70396f3..64e437d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Run artifacts *.pyc +.coverage # Local secrets *.env diff --git a/demo_send.py b/demo_send.py index 5f0c056..12f5307 100644 --- a/demo_send.py +++ b/demo_send.py @@ -16,8 +16,7 @@ import pprint from time import sleep -from FlowrouteMessagingLib.Controllers.APIController import * -from FlowrouteMessagingLib.Models.Message import * +from flowroute import Controller, Message, FlowrouteException # Set up your API credentials # Please replace the variables in Configuration.php with your information. @@ -30,7 +29,7 @@ print("Flowroute, Inc - Demo SMS Python script.\n") # Create the Controller. -controller = APIController(username=username, password=password) +controller = Controller(username=username, password=password) pprint.pprint(controller) # Build your message. @@ -40,7 +39,7 @@ try: response = controller.create_message(message) pprint.pprint(response) -except APIException as e: +except FlowrouteException as e: print("Send Error - " + str(e.response_code) + '\n') pprint.pprint(e.response_body['errors']) exit(1) # can't continue from here @@ -48,12 +47,16 @@ # Get the MDR id from the response. mdr_id = response['data']['id'] +# Wait for message to register. +# Five seconds should be enough. +sleep(5) + # Retrieve the MDR record. try: # Example MDR: 'mdr1-b334f89df8de4f8fa7ce377e06090a2e' mdr_record = controller.get_message_lookup(mdr_id) pprint.pprint(mdr_record) -except APIException as e: +except FlowrouteException as e: print("Get Error - " + str(e.response_code) + '\n') pprint.pprint(e.response_body['errors']) exit(2) diff --git a/flowroute/__init__.py b/flowroute/__init__.py index e69de29..0af9cea 100644 --- a/flowroute/__init__.py +++ b/flowroute/__init__.py @@ -0,0 +1,4 @@ + +from .controller import Controller +from .exception import FlowrouteException +from .message import Message diff --git a/flowroute/configuration.py b/flowroute/configuration.py index e188840..0990324 100644 --- a/flowroute/configuration.py +++ b/flowroute/configuration.py @@ -1,12 +1,11 @@ # -*- coding: utf-8 -*- -""" - FlowrouteMessagingLib.Configuration +"""FlowrouteMessagingLib.Configuration - Copyright Flowroute, Inc. 2016 +Copyright Flowroute, Inc. 2016 """ -class Configuration(object): +class Configuration(): # The base Uri for API calls BASE_URI = "https://api.flowroute.com/v2" diff --git a/flowroute/controller.py b/flowroute/controller.py index 14ba0a1..2626d39 100644 --- a/flowroute/controller.py +++ b/flowroute/controller.py @@ -1,30 +1,29 @@ # -*- coding: utf-8 -*- -""" - FlowrouteMessagingLib.Controllers.APIController +"""flowroute.controller - Copyright Flowroute, Inc. 2016 +Copyright Flowroute, Inc. 2016 """ import requests -from FlowrouteMessagingLib.APIHelper import APIHelper -from FlowrouteMessagingLib.configuration import Configuration -from FlowrouteMessagingLib.APIException import APIException +from flowroute.helper import Helper +from flowroute.configuration import Configuration +from flowroute.exception import FlowrouteException +from flowroute.message import Message -class APIController(object): - """ - A Controller to access Endpoints in the FlowrouteMessagingLib API. +class Controller(): + """Controller to access Endpoints in the Flowroute API. Args: - username (str): Username for authentication - password (str): password for authentication + username (str): Username for authentication. + password (str): Password for authentication. """ def __init__(self, username, password): self.__username = username self.__password = password - def create_message(self, message) -> dict: + def send_message(self, message: Message) -> dict: """Does a POST request to /messages. Send a message. @@ -49,7 +48,7 @@ def create_message(self, message) -> dict: query_builder += "/messages" # Validate and preprocess url - query_url = APIHelper.clean_url(query_builder) + query_url = Helper.clean_url(query_builder) # Prepare headers headers = { @@ -61,19 +60,19 @@ def create_message(self, message) -> dict: response = requests.post( url=query_url, headers=headers, - data=APIHelper.json_serialize(message), + data=Helper.json_serialize(message), auth=(self.__username, self.__password)) - json_content = APIHelper.json_deserialize(response.content) + json_content = Helper.json_deserialize(response.content) # Error handling using HTTP status codes if response.status_code == 401: - raise APIException("UNAUTHORIZED", 401, json_content) + raise FlowrouteException("UNAUTHORIZED", 401, json_content) elif response.status_code == 403: - raise APIException("FORBIDDEN", 403, json_content) + raise FlowrouteException("FORBIDDEN", 403, json_content) elif response.status_code < 200 or response.status_code > 206: # 200 = HTTP OK - raise APIException("HTTP Response Not OK", response.status_code, + raise FlowrouteException("HTTP Response Not OK", response.status_code, json_content) return json_content @@ -104,26 +103,28 @@ def get_message_lookup(self, record_id: str) -> dict: query_builder += "/messages/{record_id}" # Process optional template parameters - query_builder = APIHelper.append_url_with_template_parameters( + query_builder = Helper.append_url_with_template_parameters( query_builder, { "record_id": record_id, }) # Validate and preprocess url - query_url = APIHelper.clean_url(query_builder) + query_url = Helper.clean_url(query_builder) # Prepare headers - headers = {"user-agent": "Flowroute Messaging SDK 1.0", } + headers = { + "user-agent": "Flowroute Messaging SDK 1.0", + } # Prepare and invoke the API call request to fetch the response response = requests.get( url=query_url, auth=(self.__username, self.__password)) - json_content = APIHelper.json_deserialize(response.content) + json_content = Helper.json_deserialize(response.content) # Error handling using HTTP status codes if response.status_code < 200 or response.status_code > 206: # 200 = HTTP OK - raise APIException("HTTP Response Not OK", response.status_code, + raise FlowrouteException("HTTP Response Not OK", response.status_code, json_content) return json_content diff --git a/flowroute/exception.py b/flowroute/exception.py index a83990c..eb5b6e8 100644 --- a/flowroute/exception.py +++ b/flowroute/exception.py @@ -1,26 +1,26 @@ # -*- coding: utf-8 -*- -""" - FlowrouteMessagingLib.APIException +"""FlowrouteMessagingLib.APIException - Copyright Flowroute, Inc. 2016 +Copyright Flowroute, Inc. 2016 """ -class APIException(Exception): - """ - Class that handles HTTP Exceptions when fetching API Endpoints. +class FlowrouteException(Exception): + """Class that handles HTTP Exceptions when fetching API Endpoints. Attributes: - reason (string): The reason (or error message) for the Exception to be - raised. - response_code (int): The HTTP Response Code from the API Request that + reason (str): + The reason (or error message) for the Exception + to be raised. + response_code (int): + The HTTP Response Code from the API Request that caused this exception to be raised. - response_body (string): The body that was retrieved during the API - request. + response_body (str): + The body that was retrieved during the API request. """ - def __init__(self, reason, response_code, response_body): + def __init__(self, reason: str, response_code: int, response_body: str): Exception.__init__(self, reason) self.response_code = response_code self.response_body = response_body diff --git a/flowroute/helper.py b/flowroute/helper.py index 6e0159b..6b8270f 100644 --- a/flowroute/helper.py +++ b/flowroute/helper.py @@ -1,16 +1,15 @@ # -*- coding: utf-8 -*- -""" - FlowrouteMessagingLib.APIHelper +"""Flowroute.Helper - Copyright Flowroute, Inc. 2016 +Copyright Flowroute, Inc. 2016 """ + import jsonpickle import re -class APIHelper: - """ - A Helper Class for various functions associated with API Calls. +class Helper: + """A Helper Class for various functions associated with API Calls. This class contains static methods for operations that need to be performed during API requests. All of the methods inside this class are @@ -19,9 +18,8 @@ class APIHelper: """ @classmethod - def json_serialize(cls, obj): - """ - JSON Serialization of a given object. + def json_serialize(cls, obj) -> str: + """JSON Serialization of a given object. Args: obj (object): The object to serialize. @@ -54,9 +52,8 @@ def json_serialize(cls, obj): return jsonpickle.encode(obj, False) @classmethod - def json_deserialize(cls, json): - """ - JSON Deserialization of a given string. + def json_deserialize(cls, json: str) -> dict: + """JSON deserialization of a given string. Args: json (str): The JSON serialized string to deserialize. @@ -72,7 +69,7 @@ def json_deserialize(cls, json): return jsonpickle.decode(json) @classmethod - def append_url_with_template_parameters(cls, url, parameters): + def append_url_with_template_parameters(cls, url: str, parameters: dict) -> str: """ Replaces template parameters in the given url. @@ -108,7 +105,7 @@ def append_url_with_template_parameters(cls, url, parameters): return url @classmethod - def append_url_with_query_parameters(cls, url, parameters): + def append_url_with_query_parameters(cls, url: str, parameters: dict) -> str: """ Appends the given set of parameters to the given query string. @@ -153,7 +150,7 @@ def append_url_with_query_parameters(cls, url, parameters): return url @classmethod - def clean_url(cls, url): + def clean_url(cls, url: str) -> str: """ Validates and processes the given query Url to clean empty slashes. @@ -178,9 +175,8 @@ def clean_url(cls, url): return protocol + query_url @classmethod - def form_encode(cls, obj, instanceName): - """ - Encodes a model in a form-encoded manner such as person[Name] + def form_encode(cls, obj, instanceName: str) -> dict: + """Encodes a model in a form-encoded manner such as person[Name]. Args: obj (object): The given Object to form encode. @@ -192,7 +188,7 @@ def form_encode(cls, obj, instanceName): """ # Resolve the names first - value = APIHelper.resolve_name(obj) + value = Helper.resolve_name(obj) retval = dict() if value is None: @@ -204,13 +200,13 @@ def form_encode(cls, obj, instanceName): # Loop through each item in the list and add it by number i = 0 for entry in value[item]: - retval.update(APIHelper.form_encode( + retval.update(Helper.form_encode( entry, instanceName + "[" + item + "][" + str( i) + "]")) i += 1 elif isinstance(value[item], dict): # Loop through each item in the dictionary and add it - retval.update(APIHelper.form_encode(value[item], instanceName + + retval.update(Helper.form_encode(value[item], instanceName + "[" + item + "]")) else: # Add the current item @@ -219,9 +215,8 @@ def form_encode(cls, obj, instanceName): return retval @classmethod - def resolve_names(cls, obj, names, retval): - """ - Resolves parameters from their Model names to their API names. + def resolve_names(cls, obj, names: dict, retval: dict) -> dict: + """Resolve parameters from their Model names to their API names. Args: obj (object): The given Object to resolve names for. @@ -243,23 +238,22 @@ def resolve_names(cls, obj, names, retval): # Loop through each item retval[names[name]] = list() for item in value: - retval[names[name]].append(APIHelper.resolve_name(item)) + retval[names[name]].append(Helper.resolve_name(item)) elif isinstance(value, dict): # Loop through each item retval[names[name]] = dict() for key in value: - retval[names[name]][key] = APIHelper.resolve_name(value[ + retval[names[name]][key] = Helper.resolve_name(value[ key]) else: - retval[names[name]] = APIHelper.resolve_name(value) + retval[names[name]] = Helper.resolve_name(value) # Return the result return retval @classmethod def resolve_name(cls, value): - """ - Resolves name for a given object + """Resolves name for a given object. If the object needs to be recursively resolved, this method will perform that recursive call. diff --git a/flowroute/message.py b/flowroute/message.py index 94a677f..69b9cba 100644 --- a/flowroute/message.py +++ b/flowroute/message.py @@ -1,25 +1,24 @@ # -*- coding: utf-8 -*- -""" - FlowrouteMessagingLib.Models.Message +"""flowroute.Message - This file was automatically generated for flowroute - by APIMATIC BETA v2.0 on 02/08/2016 +This file was automatically generated for flowroute +by APIMATIC BETA v2.0 on 02/08/2016 """ -from FlowrouteMessagingLib.APIHelper import APIHelper +from flowroute.helper import Helper -class Message(object): - """ - Implementation of the 'Message' model. + +class Message(): + """Implementation of the 'Message' model. A simple message. Attributes: - to (string): Phone number in E.164 format to send a message to. - mfrom (string): Phone number in E.164 format where the message is sent + to (str): Phone number in E.164 format to send a message to. + mfrom (str): Phone number in E.164 format where the message is sent from. - content (string): The content of the message. + content (str): The content of the message. """ @@ -43,9 +42,8 @@ def __init__(self, **kwargs): if key in replace_names: setattr(self, replace_names[key], kwargs[key]) - def resolve_names(self): - """ - Creates a dictionary representation of this object. + def resolve_names(self) -> dict: + """Create a dictionary representation of this object. This method converts an object to a dictionary that represents the format that the model should be in when passed into an API Request. @@ -65,4 +63,4 @@ def resolve_names(self): retval = dict() - return APIHelper.resolve_names(self, replace_names, retval) + return Helper.resolve_names(self, replace_names, retval) diff --git a/flowroute/test/test_apicontroller.py b/flowroute/test/test_apicontroller.py index d83bcdc..87e7881 100644 --- a/flowroute/test/test_apicontroller.py +++ b/flowroute/test/test_apicontroller.py @@ -1,21 +1,51 @@ +import os import unittest +from unittest import mock -from FlowrouteMessagingLib.Controllers.APIController import APIController -from FlowrouteMessagingLib.Models import Message -from FlowrouteMessagingLib import APIException +from flowroute import Controller +from flowroute import Message +from flowroute import FlowrouteException -class TestAPIController(unittest.TestCase): +class TestController(unittest.TestCase): @classmethod def setUpClass(cls): - cls.controller = APIController('my_user', 'my_pass') - cls.message = Message.Message(to='to_number', from_='from_number', content='Your cool new SMS message here!') + cls.access = os.environ['ACCESS_KEY'] + cls.secret = os.environ['SECRET_KEY'] + cls.to_number = os.environ['TO_E164'] + cls.from_number = os.environ['FROM_E164'] + + cls.controller = Controller(cls.access, cls.secret) + + cls.message_content = 'Flowroute test message...' + cls.message = Message( + to=cls.to_number, from_=cls.from_number, + content=cls.message_content) + return super().setUpClass() - def test_create_message_bad_auth(self): - with self.assertRaises(APIException.APIException): - self.controller.create_message(self.message) + def test_send_message_bad_to_number(self): + bad_number_msg = Message( + to='+5555555555', from_=self.from_number, + content=self.message_content) + with self.assertRaises(FlowrouteException) as flow_exception: + self.controller.send_message(bad_number_msg) + self.assertEqual(flow_exception.exception.response_code, 422) + + def test_send_message_bad_from_number(self): + bad_number_msg = Message( + to=self.to_number, from_="+5555555555", + content=self.message_content) + with self.assertRaises(FlowrouteException) as flow_exception: + self.controller.send_message(bad_number_msg) + self.assertEqual(flow_exception.exception.response_code, 403) + + def test_send_message_bad_auth(self): + self.controller = Controller(self.access, "bad_pass") + with self.assertRaises(FlowrouteException) as flow_exception: + self.controller.send_message(self.message) + self.assertEqual(flow_exception.exception.response_code, 401) -if __name__ == "__main__": +if __name__ == "__main__": # pragma: no cover unittest.main() diff --git a/flowroute/test/test_helper.py b/flowroute/test/test_helper.py new file mode 100644 index 0000000..191dde4 --- /dev/null +++ b/flowroute/test/test_helper.py @@ -0,0 +1,30 @@ + +import os +import unittest +from unittest import mock + +from flowroute.helper import Helper +from flowroute import FlowrouteException + + +class TestJSONSerialize(unittest.TestCase): + def test_no_message(self): + self.assertIsNone(Helper.json_serialize(None)) + + def test_list_message(self): + actual_str = Helper.json_serialize([None]) + expected_str = '[null]' + self.assertEqual(expected_str, actual_str) + + def test_str_message(self): + actual_str = Helper.json_serialize({"key": "value"}) + expected_str = '{"key": "value"}' + self.assertEqual(expected_str, actual_str) + + +class TestJSONDeserialize(unittest.TestCase): + def test_no_message(self): + self.assertIsNone(Helper.json_deserialize(None)) + +if __name__ == "__main__": # pragma: no cover + unittest.main()