Skip to content
This repository has been archived by the owner on Mar 1, 2023. It is now read-only.

Commit

Permalink
Merge e8dd21c into c530054
Browse files Browse the repository at this point in the history
  • Loading branch information
judy2k committed Jun 6, 2019
2 parents c530054 + e8dd21c commit 4295743
Show file tree
Hide file tree
Showing 14 changed files with 592 additions and 51 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,4 @@ ENV*
/site

.requirements.txt
*_quickstart*
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Alternatively, you can clone the repository via the command line:
git clone git@github.com:Nexmo/nexmo-python.git

or by opening it on GitHub desktop.


Usage
-----
Expand Down Expand Up @@ -178,7 +178,7 @@ response = client.send_dtmf(uuid, digits='1234')

Docs: [https://developer.nexmo.com/api/voice#startDTMF](https://developer.nexmo.com/api/voice?utm_source=DEV_REL&utm_medium=github&utm_campaign=python-client-library#startDTMF)

### Get recording
### Get recording

``` python
response = client.get_recording(RECORDING_URL)
Expand Down Expand Up @@ -294,42 +294,42 @@ client.delete_secret(API_KEY, 'my-secret-id')
### Create an application

```python
response = client.create_application(name='Example App', type='voice', answer_url=answer_url, event_url=event_url)
response = client.application_v2.create_application({name='Example App', type='voice'})
```

Docs: [https://developer.nexmo.com/api/application#create-an-application](https://developer.nexmo.com/api/application?utm_source=DEV_REL&utm_medium=github&utm_campaign=python-client-library#create-an-application)
Docs: [https://developer.nexmo.com/api/application.v2#createApplication](https://developer.nexmo.com/api/application.v2#createApplication?utm_source=DEV_REL&utm_medium=github&utm_campaign=python-client-library#create-an-application)

### Retrieve a list of applications

```python
response = client.get_applications()
response = client.application_v2.list_applications()
```

Docs: [https://developer.nexmo.com/api/application#retrieve-your-applications](https://developer.nexmo.com/api/application?utm_source=DEV_REL&utm_medium=github&utm_campaign=python-client-library#retrieve-your-applications)
Docs: [https://developer.nexmo.com/api/application.v2#listApplication](https://developer.nexmo.com/api/application.v2#listApplication?utm_source=DEV_REL&utm_medium=github&utm_campaign=python-client-library#retrieve-your-applications)

### Retrieve a single application

```python
response = client.get_application(uuid)
response = client.application_v2.get_application(uuid)
```

Docs: [https://developer.nexmo.com/api/application#retrieve-an-application](https://developer.nexmo.com/api/application?utm_source=DEV_REL&utm_medium=github&utm_campaign=python-client-library#retrieve-an-application)
Docs: [https://developer.nexmo.com/api/application.v2#getApplication](https://developer.nexmo.com/api/application.v2#getApplication?utm_source=DEV_REL&utm_medium=github&utm_campaign=python-client-library#retrieve-an-application)

### Update an application

```python
response = client.update_application(uuid, answer_method='POST')
response = client.application_v2.update_application(uuid, answer_method='POST')
```

Docs: [https://developer.nexmo.com/api/application#update-an-application](https://developer.nexmo.com/api/application?utm_source=DEV_REL&utm_medium=github&utm_campaign=python-client-library#update-an-application)
Docs: [https://developer.nexmo.com/api/application.v2#updateApplication](https://developer.nexmo.com/api/application.v2#updateApplication?utm_source=DEV_REL&utm_medium=github&utm_campaign=python-client-library#update-an-application)

### Delete an application

```python
response = client.delete_application(uuid)
response = client.application_v2.delete_application(uuid)
```

Docs: [https://developer.nexmo.com/api/application#destroy-an-application](https://developer.nexmo.com/api/application?utm_source=DEV_REL&utm_medium=github&utm_campaign=python-client-library#destroy-an-application)
Docs: [https://developer.nexmo.com/api/application.v2#deleteApplication](https://developer.nexmo.com/api/application.v2#deleteApplication?utm_source=DEV_REL&utm_medium=github&utm_campaign=python-client-library#destroy-an-application)


## Validate webhook signatures
Expand Down
33 changes: 33 additions & 0 deletions docs/_static/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@

# Created by https://www.gitignore.io/api/osx
# Edit at https://www.gitignore.io/?templates=osx

### OSX ###
# General
.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon

# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

# End of https://www.gitignore.io/api/osx
12 changes: 10 additions & 2 deletions docs/reference.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
API Reference
=============

.. automodule:: nexmo
.. autoclass:: nexmo.Client
:members:
:undoc-members:
:undoc-members:

.. attribute:: application_v2

An instance of :class:`nexmo.ApplicationV2` for accessing the Application API.

.. autoclass:: nexmo.ApplicationV2
:members:
:undoc-members:
129 changes: 92 additions & 37 deletions nexmo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,28 +27,44 @@
except ImportError:
JSONDecodeError = ValueError

from .errors import *
from ._internal import ApplicationV2, BasicAuthenticatedServer, _format_date_param

__version__ = "2.3.0"

logger = logging.getLogger("nexmo")


class Error(Exception):
pass


class ClientError(Error):
pass


class ServerError(Error):
pass


class AuthenticationError(ClientError):
pass


class Client:
"""
Create a Client object to start making calls to Nexmo APIs.
Most methods corresponding to Nexmo API calls are on this class itself,
although newer APIs are under namespaces like :attr:`Client.application_v2`.
The credentials you provide when instantiating a Client determine which
methods can be called. Consult the `Nexmo API docs <https://developer.nexmo.com/api/>`_ for details of the
authentication used by the APIs you wish to use, and instantiate your
Client with the appropriate credentials.
:param str key: Your Nexmo API key
:param str secret: Your Nexmo API secret.
:param str signature_secret: Your Nexmo API signature secret.
You may need to have this enabled by Nexmo support. It is only used for SMS authentication.
:param str signature_method:
The encryption method used for signature encryption. This must match the method
configured in the Nexmo Dashboard. We recommend `sha256` or `sha512`.
This should be one of `md5`, `sha1`, `sha256`, or `sha512` if using HMAC digests.
If you want to use a simple MD5 hash, leave this as `None`.
:param str application_id: Your application ID if calling methods which use JWT authentication.
:param str private_key: Your private key if calling methods which use JWT authentication.
This should either be a str containing the key in its PEM form, or a path to a private key file.
:param str app_name: This optional value is added to the user-agent header
provided by this library and can be used by Nexmo to track your app statistics.
:param str app_name: This optional value is added to the user-agent header
provided by this library and can be used by Nexmo to track your app statistics.
"""

def __init__(
self,
key=None,
Expand All @@ -71,7 +87,7 @@ def __init__(
"NEXMO_SIGNATURE_METHOD", None
)

if signature_method in {"md5", "sha1", "sha256", "sha512"}:
if self.signature_method in {"md5", "sha1", "sha256", "sha512"}:
self.signature_method = getattr(hashlib, signature_method)

self.application_id = application_id
Expand Down Expand Up @@ -99,6 +115,14 @@ def __init__(

self.auth_params = {}

api_server = BasicAuthenticatedServer(
"https://api.nexmo.com",
user_agent=user_agent,
api_key=self.api_key,
api_secret=self.api_secret,
)
self.application_v2 = ApplicationV2(api_server)

def auth(self, params=None, **kwargs):
self.auth_params = params or kwargs

Expand Down Expand Up @@ -297,25 +321,50 @@ def request_number_insight(self, params=None, **kwargs):
return self.post(self.host, "/ni/json", params or kwargs)

def get_applications(self, params=None, **kwargs):
warnings.warn(
"nexmo.Client#get_applications is deprecated (use methods from #application_v2 instead)",
DeprecationWarning,
stacklevel=2,
)
return self.get(self.api_host, "/v1/applications", params or kwargs)

def get_application(self, application_id):
warnings.warn(
"nexmo.Client#get_application is deprecated (use methods from #application_v2 instead)",
DeprecationWarning,
stacklevel=2,
)
return self.get(
self.api_host,
"/v1/applications/{application_id}".format(application_id=application_id),
)

def create_application(self, params=None, **kwargs):
warnings.warn(
"nexmo.Client#create_application is deprecated (use methods from #application_v2 instead)",
DeprecationWarning,
stacklevel=2,
)
return self.post(self.api_host, "/v1/applications", params or kwargs)

def update_application(self, application_id, params=None, **kwargs):
warnings.warn(
"nexmo.Client#update_application is deprecated (use methods from #application_v2 instead)",
DeprecationWarning,
stacklevel=2,
)
return self.put(
self.api_host,
"/v1/applications/{application_id}".format(application_id=application_id),
params or kwargs,
)

def delete_application(self, application_id):
warnings.warn(
"nexmo.Client#delete_application is deprecated (use methods from #application_v2 instead)",
DeprecationWarning,
stacklevel=2,
)
return self.delete(
self.api_host,
"/v1/applications/{application_id}".format(application_id=application_id),
Expand Down Expand Up @@ -447,6 +496,12 @@ def get(self, host, request_uri, params=None, header_auth=False):
return self.parse(host, requests.get(uri, params=params, headers=headers))

def post(self, host, request_uri, params, header_auth=False):
"""
Post form-encoded data to `request_uri`.
Auth is either key/secret added to the post data, or basic auth,
if `header_auth` is True.
"""
uri = "https://{host}{request_uri}".format(host=host, request_uri=request_uri)
headers = self.headers
if header_auth:
Expand All @@ -464,6 +519,9 @@ def post(self, host, request_uri, params, header_auth=False):
return self.parse(host, requests.post(uri, data=params, headers=headers))

def _post_json(self, host, request_uri, json):
"""
Post json to `request_uri`, using basic auth.
"""
uri = "https://{host}{request_uri}".format(host=host, request_uri=request_uri)
auth = base64.b64encode(
(
Expand All @@ -480,12 +538,24 @@ def _post_json(self, host, request_uri, json):
)
return self.parse(host, requests.post(uri, headers=headers, json=json))

def put(self, host, request_uri, params):
def put(self, host, request_uri, params, header_auth=False):
uri = "https://{host}{request_uri}".format(host=host, request_uri=request_uri)

params = dict(params, api_key=self.api_key, api_secret=self.api_secret)
logger.debug("PUT to %r with params %r", uri, params)
return self.parse(host, requests.put(uri, json=params, headers=self.headers))
headers = self.headers
if header_auth:
h = base64.b64encode(
(
"{api_key}:{api_secret}".format(
api_key=self.api_key, api_secret=self.api_secret
).encode("utf-8")
)
).decode("ascii")
# Must create a new headers dict here, otherwise we'd be mutating `self.headers`:
headers = dict(headers or {}, Authorization="Basic {hash}".format(hash=h))
else:
params = dict(params, api_key=self.api_key, api_secret=self.api_secret)
logger.debug("PUT to %r with params %r, headers %r", uri, params, headers)
return self.parse(host, requests.put(uri, json=params, headers=headers))

def delete(self, host, request_uri, header_auth=False):
uri = "https://{host}{request_uri}".format(host=host, request_uri=request_uri)
Expand All @@ -500,6 +570,7 @@ def delete(self, host, request_uri, header_auth=False):
).encode("utf-8")
)
).decode("ascii")
# Must create a new headers dict here, otherwise we'd be mutating `self.headers`:
headers = dict(headers or {}, Authorization="Basic {hash}".format(hash=h))
else:
params = {"api_key": self.api_key, "api_secret": self.api_secret}
Expand Down Expand Up @@ -600,19 +671,3 @@ def generate_application_jwt(self, when=None):
payload.setdefault("jti", str(uuid4()))

return jwt.encode(payload, self.private_key, algorithm="RS256")


def _format_date_param(params, key, format="%Y-%m-%d %H:%M:%S"):
"""
Utility function to convert datetime values to strings.
If the value is already a str, or is not in the dict, no change is made.
:param params: A `dict` of params that may contain a `datetime` value.
:param key: The datetime value to be converted to a `str`
:param format: The `strftime` format to be used to format the date. The default value is '%Y-%m-%d %H:%M:%S'
"""
if key in params:
param = params[key]
if hasattr(param, "strftime"):
params[key] = param.strftime(format)

0 comments on commit 4295743

Please sign in to comment.