Skip to content

Commit dae7232

Browse files
committed
adding start verification methods and starting check_code
1 parent b176787 commit dae7232

File tree

15 files changed

+227
-114
lines changed

15 files changed

+227
-114
lines changed

http_client/CHANGES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# 1.2.0
2+
- Add `last_request` and `last_response` properties
3+
14
# 1.1.1
25
- Add new Patch method
36
- New input fields for different ways to pass data in a request

http_client/src/vonage_http_client/http_client.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from typing import Literal, Optional, Union
55

66
from pydantic import BaseModel, Field, ValidationError, validate_call
7-
from requests import Response
7+
from requests import PreparedRequest, Response
88
from requests.adapters import HTTPAdapter
99
from requests.sessions import Session
1010
from typing_extensions import Annotated
@@ -81,6 +81,9 @@ def __init__(
8181
self._user_agent = f'vonage-python-sdk/{sdk_version} python/{python_version()}'
8282
self._headers = {'User-Agent': self._user_agent, 'Accept': 'application/json'}
8383

84+
self._last_request = None
85+
self._last_response = None
86+
8487
@property
8588
def auth(self):
8689
return self._auth
@@ -101,6 +104,24 @@ def rest_host(self):
101104
def user_agent(self):
102105
return self._user_agent
103106

107+
@property
108+
def last_request(self) -> Optional[PreparedRequest]:
109+
"""The last request sent to the server.
110+
111+
Returns:
112+
Optional[PreparedRequest]: The exact bytes of the request sent to the server.
113+
"""
114+
return self._last_response.request
115+
116+
@property
117+
def last_response(self) -> Optional[Response]:
118+
"""The last response received from the server.
119+
120+
Returns:
121+
Optional[Response]: The response object received from the server.
122+
"""
123+
return self._last_response
124+
104125
def post(
105126
self,
106127
host: str,
@@ -119,7 +140,7 @@ def get(
119140
request_path: str = '',
120141
params: dict = None,
121142
auth_type: Literal['jwt', 'basic', 'body', 'signature'] = 'jwt',
122-
sent_data_type: Literal['json', 'form', 'query_params'] = 'json',
143+
sent_data_type: Literal['json', 'form', 'query_params'] = 'query_params',
123144
) -> Union[dict, None]:
124145
return self.make_request(
125146
'GET', host, request_path, params, auth_type, sent_data_type
@@ -199,6 +220,8 @@ def _parse_response(self, response: Response) -> Union[dict, None]:
199220
logger.debug(
200221
f'Response received from {response.url} with status code: {response.status_code}; headers: {response.headers}'
201222
)
223+
self._last_response = response
224+
202225
content_type = response.headers['Content-Type'].split(';', 1)[0]
203226
if 200 <= response.status_code < 300:
204227
if response.status_code == 204:

http_client/tests/test_http_client.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import responses
55
from pytest import raises
6-
from requests import Response
6+
from requests import PreparedRequest, Response
77
from responses import matchers
88
from vonage_http_client.auth import Auth
99
from vonage_http_client.errors import (
@@ -55,7 +55,7 @@ def test_create_http_client_invalid_options_error():
5555

5656

5757
@responses.activate
58-
def test_make_get_request():
58+
def test_make_get_request_and_last_request_and_response():
5959
build_response(
6060
path, 'GET', 'https://example.com/get_json?key=value', 'example_get.json'
6161
)
@@ -64,15 +64,22 @@ def test_make_get_request():
6464
http_client_options={'api_host': 'example.com'},
6565
)
6666
res = client.get(
67-
host='example.com',
68-
request_path='/get_json',
69-
params={'key': 'value'},
70-
sent_data_type='query_params',
67+
host='example.com', request_path='/get_json', params={'key': 'value'}
7168
)
7269

7370
assert res['hello'] == 'world'
7471
assert responses.calls[0].request.headers['User-Agent'] == client._user_agent
7572

73+
assert type(client.last_request) == PreparedRequest
74+
assert client.last_request.method == 'GET'
75+
assert client.last_request.url == 'https://example.com/get_json?key=value'
76+
assert client.last_request.body == None
77+
78+
assert type(client.last_response) == Response
79+
assert client.last_response.status_code == 200
80+
assert client.last_response.json() == res
81+
assert client.last_response.headers == {'Content-Type': 'application/json'}
82+
7683

7784
@responses.activate
7885
def test_make_get_request_no_content():

users/CHANGES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
1+
# 1.0.1
2+
- Internal refactoring
3+
14
# 1.0.0
25
- Initial upload

users/src/vonage_users/users.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ def list_users(
4040
'/v1/users',
4141
params.model_dump(exclude_none=True),
4242
self._auth_type,
43-
'query_params',
4443
)
4544

4645
users_response = ListUsersResponse(**response)

verify/src/vonage_verify/errors.py

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,4 @@
22

33

44
class VerifyError(VonageError):
5-
"""Indicates an error with the Vonage Verify Package."""
6-
7-
8-
# class PartialFailureError(SmsError):
9-
# """Indicates that a request was partially successful."""
10-
11-
# def __init__(self, response: Response):
12-
# self.message = (
13-
# 'Sms.send_message method partially failed. Not all of the message(s) sent successfully.',
14-
# )
15-
# super().__init__(self.message)
16-
# self.response = response
5+
"""Indicates an error when using the Vonage Verify API."""

verify/src/vonage_verify/requests.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,20 @@ class BaseVerifyRequest(BaseModel):
1414

1515
number: PhoneNumber
1616
country: Optional[str] = Field(None, max_length=2)
17-
code_length: Optional[Literal[4, 6]] = 4
17+
code_length: Optional[Literal[4, 6]] = None
1818
pin_expiry: Optional[int] = Field(None, ge=60, le=3600)
1919
next_event_wait: Optional[int] = Field(None, ge=60, le=900)
20-
workflow_id: Optional[Literal[1, 2, 3, 4, 5, 6, 7]] = None
20+
workflow_id: Optional[int] = Field(None, ge=1, le=7)
2121

2222
@model_validator(mode='after')
2323
def check_expiry_and_next_event_timing(self):
2424
if self.pin_expiry is None or self.next_event_wait is None:
2525
return self
2626
if self.pin_expiry % self.next_event_wait != 0:
27-
logger.debug(
27+
logger.warning(
2828
f'The pin_expiry should be a multiple of next_event_wait.'
29-
f'The current values are: pin_expiry={self.pin_expiry}, next_event_wait={self.next_event_wait}.'
30-
f'The value of pin_expiry will be set to next_event_wait.'
29+
f'\nThe current values are: pin_expiry={self.pin_expiry}, next_event_wait={self.next_event_wait}.'
30+
f'\nThe value of pin_expiry will be set to next_event_wait.'
3131
)
3232
self.pin_expiry = self.next_event_wait
3333
return self
@@ -40,7 +40,7 @@ class VerifyRequest(BaseVerifyRequest):
4040
"""
4141

4242
brand: str = Field(..., max_length=18)
43-
sender_id: Optional[str] = Field('VERIFY', max_length=11)
43+
sender_id: Optional[str] = Field(None, max_length=11)
4444
lg: Optional[LanguageCode] = None
4545
pin_code: Optional[str] = Field(None, min_length=4, max_length=10)
4646

verify/src/vonage_verify/responses.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
1+
from typing import Optional
2+
13
from pydantic import BaseModel
24

35

4-
class VerifyResponse(BaseModel):
6+
class StartVerificationResponse(BaseModel):
7+
request_id: str
8+
status: str
9+
10+
11+
class CheckCodeResponse(BaseModel):
512
request_id: str
613
status: str
14+
event_id: str
15+
price: str
16+
currency: str
17+
estimated_price_messages_sent: Optional[str] = None
718

819

920
# class MessageResponse(BaseModel):

verify/src/vonage_verify/verify.py

Lines changed: 88 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,107 +1,115 @@
11
from pydantic import validate_call
22
from vonage_http_client.http_client import HttpClient
33

4+
from .errors import VerifyError
45
from .requests import BaseVerifyRequest, Psd2Request, VerifyRequest
5-
from .responses import VerifyResponse
6+
from .responses import CheckCodeResponse, StartVerificationResponse
67

78

89
class Verify:
9-
"""Calls Vonage's Verify API."""
10+
"""Calls Vonage's Verify API.
11+
12+
This class provides methods to interact with Vonage's Verify API for starting verification
13+
processes.
14+
"""
1015

1116
def __init__(self, http_client: HttpClient) -> None:
1217
self._http_client = http_client
13-
self._sent_post_data_type = 'form'
14-
self._sent_get_data_type = 'query_params'
18+
self._sent_data_type = 'form'
1519
self._auth_type = 'body'
1620

1721
@validate_call
18-
def start_verification(self, verify_request: VerifyRequest) -> VerifyResponse:
19-
"""Start a verification process."""
22+
def start_verification(
23+
self, verify_request: VerifyRequest
24+
) -> StartVerificationResponse:
25+
"""Start a verification process.
26+
27+
Args:
28+
verify_request (VerifyRequest): The verification request object.
29+
30+
Returns:
31+
StartVerificationResponse: The response object containing the verification result.
32+
"""
2033
return self._make_verify_request(verify_request)
2134

2235
@validate_call
23-
def start_psd2_verification(self, verify_request: Psd2Request) -> VerifyResponse:
24-
"""Start a PSD2 verification process."""
36+
def start_psd2_verification(
37+
self, verify_request: Psd2Request
38+
) -> StartVerificationResponse:
39+
"""Start a PSD2 verification process.
40+
41+
Args:
42+
verify_request (Psd2Request): The PSD2 verification request object.
43+
44+
Returns:
45+
StartVerificationResponse: The response object containing the verification result.
46+
"""
2547
return self._make_verify_request(verify_request)
2648

27-
def _make_verify_request(self, verify_request: BaseVerifyRequest) -> VerifyResponse:
49+
@validate_call
50+
def check_code(self, request_id: str, code: str) -> CheckCodeResponse:
51+
"""Check a verification code.
52+
53+
Args:
54+
request_id (str): The request ID.
55+
code (str): The verification code.
56+
57+
Returns:
58+
CheckCodeResponse: The response object containing the verification result.
59+
"""
60+
response = self._http_client.post(
61+
self._http_client.api_host,
62+
'/verify/check/json',
63+
{'request_id': request_id, 'code': code},
64+
self._auth_type,
65+
self._sent_data_type,
66+
)
67+
self._check_for_error(response)
68+
return CheckCodeResponse(**response)
69+
70+
def _make_verify_request(
71+
self, verify_request: BaseVerifyRequest
72+
) -> StartVerificationResponse:
73+
"""Make a verify request.
74+
75+
This method makes a verify request to the Vonage Verify API.
76+
77+
Args:
78+
verify_request (BaseVerifyRequest): The verify request object.
79+
80+
Returns:
81+
VerifyResponse: The response object containing the verification result.
82+
"""
2883
if type(verify_request) == VerifyRequest:
2984
request_path = '/verify/json'
3085
elif type(verify_request) == Psd2Request:
3186
request_path = '/verify/psd2/json'
32-
3387
response = self._http_client.post(
3488
self._http_client.api_host,
3589
request_path,
36-
verify_request.model_dump(by_alias=True),
90+
verify_request.model_dump(by_alias=True, exclude_none=True),
3791
self._auth_type,
38-
self._sent_post_data_type,
92+
self._sent_data_type,
3993
)
40-
return VerifyResponse(**response)
41-
42-
# @validate_call
43-
# def send(self, message: SmsMessage) -> SmsResponse:
44-
# """Send an SMS message."""
45-
# response = self._http_client.post(
46-
# self._http_client.rest_host,
47-
# '/sms/json',
48-
# message.model_dump(by_alias=True),
49-
# self._auth_type,
50-
# self._sent_data_type,
51-
# )
52-
53-
# if int(response['message-count']) > 1:
54-
# self._check_for_partial_failure(response)
55-
# else:
56-
# self._check_for_error(response)
57-
# return SmsResponse(**response)
58-
59-
# def _check_for_partial_failure(self, response_data):
60-
# successful_messages = 0
61-
# total_messages = int(response_data['message-count'])
62-
63-
# for message in response_data['messages']:
64-
# if message['status'] == '0':
65-
# successful_messages += 1
66-
# if successful_messages < total_messages:
67-
# raise PartialFailureError(response_data)
68-
69-
# def _check_for_error(self, response_data):
70-
# message = response_data['messages'][0]
71-
# if int(message['status']) != 0:
72-
# raise SmsError(
73-
# f'Sms.send_message method failed with error code {message["status"]}: {message["error-text"]}'
74-
# )
75-
76-
# @validate_call
77-
# def submit_sms_conversion(
78-
# self, message_id: str, delivered: bool = True, timestamp: datetime = None
79-
# ):
80-
# """
81-
# Note: Not available without having this feature manually enabled on your account.
82-
83-
# Notifies Vonage that an SMS was successfully received.
84-
85-
# This method is used to submit conversion data about SMS messages that were successfully delivered.
86-
# If you are using the Verify API for two-factor authentication (2FA), this information is sent to Vonage automatically,
87-
# so you do not need to use this method for 2FA messages.
88-
89-
# Args:
90-
# message_id (str): The `message-id` returned by the `Sms.send` call.
91-
# delivered (bool, optional): Set to `True` if the user replied to the message you sent. Otherwise, set to `False`.
92-
# timestamp (datetime, optional): A `datetime` object containing the time the SMS arrived.
93-
# """
94-
# params = {
95-
# 'message-id': message_id,
96-
# 'delivered': delivered,
97-
# 'timestamp': (timestamp or datetime.now(timezone.utc)).strftime(
98-
# '%Y-%m-%d %H:%M:%S'
99-
# ),
100-
# }
101-
# self._http_client.post(
102-
# self._http_client.api_host,
103-
# '/conversions/sms',
104-
# params,
105-
# self._auth_type,
106-
# self._sent_data_type,
107-
# )
94+
self._check_for_error(response)
95+
parsed_response = StartVerificationResponse(**response)
96+
97+
return parsed_response
98+
99+
def _check_for_error(self, response: dict) -> None:
100+
"""Check for error in the response.
101+
102+
This method checks if the response contains an error and raises a VerifyError if an error is found.
103+
104+
Args:
105+
response (dict): The response object.
106+
107+
Raises:
108+
VerifyError: If an error is found in the response.
109+
"""
110+
print(self._http_client.last_request.body)
111+
if int(response['status']) != 0:
112+
error_message = f'Error with Vonage status code {response["status"]}: {response["error_text"]}.'
113+
if 'network' in response:
114+
error_message += f' Network ID: {response["network"]}'
115+
raise VerifyError(error_message)

verify/tests/data/check_code.json

Whitespace-only changes.

0 commit comments

Comments
 (0)