Skip to content

Commit 2b68284

Browse files
committed
finish verify implementation and prepare for release
1 parent 0843a15 commit 2b68284

File tree

22 files changed

+255
-39
lines changed

22 files changed

+255
-39
lines changed

http_client/CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# 1.2.0
22
- Add `last_request` and `last_response` properties
3+
- Add new `Forbidden` error
34

45
# 1.1.1
56
- Add new Patch method

http_client/README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,21 @@ response = client.get(host='api.nexmo.com', request_path='/v1/messages')
4242
response = client.post(host='api.nexmo.com', request_path='/v1/messages', params={'key': 'value'})
4343
```
4444

45+
## Get the Last Request and Last Response from the HTTP Client
46+
47+
The `HttpClient` class exposes two properties, `last_request` and `last_response` that cache the last sent request and response.
48+
49+
```python
50+
# Get last request, has type requests.PreparedRequest
51+
request = client.last_request
52+
53+
# Get last response, has type requests.Response
54+
response = client.last_response
55+
```
56+
4557
### Appending to the User-Agent Header
4658

47-
The HttpClient class also supports appending additional information to the User-Agent header via the append_to_user_agent method:
59+
The `HttpClient` class also supports appending additional information to the User-Agent header via the append_to_user_agent method:
4860

4961
```python
5062
client.append_to_user_agent('additional_info')

http_client/pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
[project]
22
name = "vonage-http-client"
3-
version = "1.1.1"
3+
version = "1.2.0"
44
description = "An HTTP client for making requests to Vonage APIs."
55
readme = "README.md"
66
authors = [{ name = "Vonage", email = "devrel@vonage.com" }]
77
requires-python = ">=3.8"
88
dependencies = [
9-
"vonage-utils>=1.0.0",
9+
"vonage-utils>=1.0.1",
1010
"vonage-jwt>=1.1.0",
1111
"requests>=2.27.0",
1212
"pydantic>=2.6.1",

http_client/src/vonage_http_client/errors.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,24 @@ def __init__(self, response: Response, content_type: str):
6767
super().__init__(response, content_type)
6868

6969

70+
class ForbiddenError(HttpRequestError):
71+
"""Exception indicating a forbidden request in a Vonage SDK request.
72+
73+
This error is raised when the HTTP response status code is 403 (Forbidden).
74+
75+
Args:
76+
response (requests.Response): The HTTP response object.
77+
content_type (str): The response content type.
78+
79+
Attributes (inherited from HttpRequestError parent exception):
80+
response (requests.Response): The HTTP response object.
81+
message (str): The returned error message.
82+
"""
83+
84+
def __init__(self, response: Response, content_type: str):
85+
super().__init__(response, content_type)
86+
87+
7088
class NotFoundError(HttpRequestError):
7189
"""Exception indicating a resource was not found in a Vonage SDK request.
7290

http_client/src/vonage_http_client/http_client.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from vonage_http_client.auth import Auth
1212
from vonage_http_client.errors import (
1313
AuthenticationError,
14+
ForbiddenError,
1415
HttpRequestError,
1516
InvalidHttpClientOptionsError,
1617
NotFoundError,
@@ -109,7 +110,8 @@ def last_request(self) -> Optional[PreparedRequest]:
109110
"""The last request sent to the server.
110111
111112
Returns:
112-
Optional[PreparedRequest]: The exact bytes of the request sent to the server.
113+
Optional[PreparedRequest]: The exact bytes of the request sent to the server,
114+
or None if no request has been sent.
113115
"""
114116
return self._last_response.request
115117

@@ -118,7 +120,8 @@ def last_response(self) -> Optional[Response]:
118120
"""The last response received from the server.
119121
120122
Returns:
121-
Optional[Response]: The response object received from the server.
123+
Optional[Response]: The response object received from the server,
124+
or None if no response has been received.
122125
"""
123126
return self._last_response
124127

@@ -221,7 +224,6 @@ def _parse_response(self, response: Response) -> Union[dict, None]:
221224
f'Response received from {response.url} with status code: {response.status_code}; headers: {response.headers}'
222225
)
223226
self._last_response = response
224-
print(response.content)
225227
content_type = response.headers['Content-Type'].split(';', 1)[0]
226228
if 200 <= response.status_code < 300:
227229
if response.status_code == 204:
@@ -234,8 +236,10 @@ def _parse_response(self, response: Response) -> Union[dict, None]:
234236
logger.warning(
235237
f'Http Response Error! Status code: {response.status_code}; content: {repr(response.text)}; from url: {response.url}'
236238
)
237-
if response.status_code == 401 or response.status_code == 403:
239+
if response.status_code == 401:
238240
raise AuthenticationError(response, content_type)
241+
if response.status_code == 403:
242+
raise ForbiddenError(response, content_type)
239243
elif response.status_code == 404:
240244
raise NotFoundError(response, content_type)
241245
elif response.status_code == 429:

http_client/tests/data/403.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "https://developer.vonage.com/api-errors#forbidden",
3+
"title": "Forbidden",
4+
"detail": "Your account does not have permission to perform this action.",
5+
"instance": "bf0ca0bf927b3b52e3cb03217e1a1ddf"
6+
}

http_client/tests/test_http_client.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from vonage_http_client.auth import Auth
99
from vonage_http_client.errors import (
1010
AuthenticationError,
11+
ForbiddenError,
1112
HttpRequestError,
1213
InvalidHttpClientOptionsError,
1314
RateLimitedError,
@@ -196,6 +197,21 @@ def test_authentication_error_no_content():
196197
assert type(err.response) == Response
197198

198199

200+
@responses.activate
201+
def test_forbidden_error():
202+
build_response(path, 'GET', 'https://example.com/get_json', '403.json', 403)
203+
204+
client = HttpClient(Auth())
205+
try:
206+
client.get(host='example.com', request_path='/get_json', auth_type='basic')
207+
except ForbiddenError as err:
208+
assert err.response.json()['title'] == 'Forbidden'
209+
assert (
210+
err.response.json()['detail']
211+
== 'Your account does not have permission to perform this action.'
212+
)
213+
214+
199215
@responses.activate
200216
def test_not_found_error():
201217
build_response(path, 'GET', 'https://example.com/get_json', '404.json', 404)

users/pyproject.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
[project]
22
name = 'vonage-users'
3-
version = '1.0.0'
4-
description = 'Vonage SMS package'
3+
version = '1.0.1'
4+
description = 'Vonage Users package'
55
readme = "README.md"
66
authors = [{ name = "Vonage", email = "devrel@vonage.com" }]
77
requires-python = ">=3.8"
88
dependencies = [
9-
"vonage-http-client>=1.1.1",
10-
"vonage-utils>=1.0.0",
9+
"vonage-http-client>=1.2.0",
10+
"vonage-utils>=1.0.1",
1111
"pydantic>=2.6.1",
1212
]
1313
classifiers = [

verify/README.md

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,63 @@
11
# Vonage Verify Package
22

3-
This package contains the code to use Vonage's Verify API in Python. There is a more current package to user Vonage's Verify v2 API which is recommended to use for most use cases. The v2 API lets you send messages via multiple channels, including Email, SMS, MMS, WhatsApp, Messenger and others. You can also make Silent Authentication requests with Verify v2 to give an end user a more seamless experience.
4-
5-
This package includes methods for sending 2-factor authentication (2FA) messages and returns...
6-
7-
8-
asdf
9-
asdf
3+
This package contains the code to use Vonage's Verify API in Python. This package includes methods for working with 2-factor authentication (2FA) messages sent via SMS or TTS.
104

5+
Note: There is a more current package available: [Vonage's Verify v2 API](https://developer.vonage.com/en/verify/overview) which is recommended for most use cases. The v2 API lets you send messages via multiple channels, including Email, SMS, MMS, WhatsApp, Messenger and others. You can also make Silent Authentication requests with Verify v2 to give an end user a more seamless experience.
116

127
## Usage
138

149
It is recommended to use this as part of the main `vonage` package. The examples below assume you've created an instance of the `vonage.Vonage` class called `vonage_client`.
1510

1611
### Make a Verify Request
1712

18-
<!-- Create an `SmsMessage` object, then pass into the `Sms.send` method.
13+
```python
14+
from vonage_verify import VerifyRequest
15+
params = {'number': '1234567890', 'brand': 'Acme Inc.'}
16+
request = VerifyRequest(**params)
17+
response = vonage_client.verify.start_verification(request)
18+
```
19+
20+
### Make a PSD2 (Payment Services Directive v2) Request
21+
22+
```python
23+
from vonage_verify import Psd2Request
24+
params = {'number': '1234567890', 'payee': 'Acme Inc.', 'amount': 99.99}
25+
request = VerifyRequest(**params)
26+
response = vonage_client.verify.start_verification(request)
27+
```
28+
29+
### Check a Verification Code
30+
31+
```python
32+
vonage_client.verify.check_code(request_id='my_request_id', code='1234')
33+
```
34+
35+
### Search Verification Requests
36+
37+
```python
38+
# Search for single request
39+
response = vonage_client.verify.search('my_request_id')
40+
41+
# Search for multiple requests
42+
response = vonage_client.verify.search(['my_request_id_1', 'my_request_id_2'])
43+
```
44+
45+
### Cancel a Verification
1946

2047
```python
21-
from vonage_sms import SmsMessage, SmsResponse
48+
response = vonage_client.verify.cancel_verification('my_request_id')
49+
```
2250

23-
message = SmsMessage(to='1234567890', from_='Acme Inc.', text='Hello, World!')
24-
response: SmsResponse = vonage_client.sms.send(message)
51+
### Trigger the Next Workflow Event
2552

26-
print(response.model_dump(exclude_unset=True))
27-
``` -->
53+
```python
54+
response = vonage_client.verify.trigger_next_event('my_request_id')
55+
```
56+
57+
### Request a Network Unblock
58+
59+
Note: Network Unblock is switched off by default. Contact Sales to enable the Network Unblock API for your account.
60+
61+
```python
62+
response = vonage_client.verify.request_network_unblock('23410')
63+
```

verify/pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ readme = "README.md"
66
authors = [{ name = "Vonage", email = "devrel@vonage.com" }]
77
requires-python = ">=3.8"
88
dependencies = [
9-
"vonage-http-client>=1.1.1",
10-
"vonage-utils>=1.0.0",
9+
"vonage-http-client>=1.2.0",
10+
"vonage-utils>=1.0.1",
1111
"pydantic>=2.6.1",
1212
]
1313
classifiers = [

0 commit comments

Comments
 (0)