Skip to content

Commit 01145a9

Browse files
committed
Sms structuring and tests
1 parent 52e94c4 commit 01145a9

File tree

11 files changed

+161
-223
lines changed

11 files changed

+161
-223
lines changed

http_client/src/vonage_http_client/http_client.py

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -104,18 +104,20 @@ def post(
104104
host: str,
105105
request_path: str = '',
106106
params: dict = None,
107-
auth_type: str = 'jwt',
107+
auth_type: Literal['jwt', 'basic', 'signature'] = 'jwt',
108+
body_type: Literal['json', 'data'] = 'json',
108109
) -> Union[dict, None]:
109-
return self.make_request('POST', host, request_path, params, auth_type)
110+
return self.make_request('POST', host, request_path, params, auth_type, body_type)
110111

111112
def get(
112113
self,
113114
host: str,
114115
request_path: str = '',
115116
params: dict = None,
116-
auth_type: str = 'jwt',
117+
auth_type: Literal['jwt', 'basic', 'signature'] = 'jwt',
118+
body_type: Literal['json', 'data'] = 'json',
117119
) -> Union[dict, None]:
118-
return self.make_request('GET', host, request_path, params, auth_type)
120+
return self.make_request('GET', host, request_path, params, auth_type, body_type)
119121

120122
@validate_call
121123
def make_request(
@@ -125,6 +127,7 @@ def make_request(
125127
request_path: str = '',
126128
params: Optional[dict] = None,
127129
auth_type: Literal['jwt', 'basic', 'signature'] = 'jwt',
130+
body_type: Literal['json', 'data'] = 'json',
128131
):
129132
url = f'https://{host}{request_path}'
130133
logger.debug(
@@ -137,22 +140,20 @@ def make_request(
137140
elif auth_type == 'signature':
138141
params['api_key'] = self._auth.api_key
139142
params['sig'] = self._auth.sign_params(params)
140-
with self._session.request(
141-
request_type,
142-
url,
143-
data=params,
144-
headers=self._headers,
145-
timeout=self._timeout,
146-
) as response:
147-
return self._parse_response(response)
148-
149-
with self._session.request(
150-
request_type,
151-
url,
152-
json=params,
153-
headers=self._headers,
154-
timeout=self._timeout,
155-
) as response:
143+
144+
request_params = {
145+
'method': request_type,
146+
'url': url,
147+
'headers': self._headers,
148+
'timeout': self._timeout,
149+
}
150+
151+
if body_type == 'json':
152+
request_params['json'] = params
153+
else:
154+
request_params['data'] = params
155+
156+
with self._session.request(**request_params) as response:
156157
return self._parse_response(response)
157158

158159
def append_to_user_agent(self, string: str):

http_client/tests/test_auth.py

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -163,33 +163,21 @@ def test_sign_params_with_dynamic_timestamp(mock_time):
163163
assert signed_params == 'bc7e95bb4e341090b3a202a2885903a5'
164164

165165

166-
def test_check_signature_with_valid_signature():
166+
def test_check_signature_valid_signature():
167167
auth = Auth(api_key=api_key, signature_secret=signature_secret)
168168
params = {
169-
'param1': 'value1',
170-
'param2': 'value2',
171-
'sig': 'valid_signature',
169+
'param': 'value',
172170
'timestamp': 1234567890,
171+
'sig': '655a4d0b7f064dff438defc52b012cf5',
173172
}
174-
175173
assert auth.check_signature(params) == True
176174

177175

178-
# def test_check_signature_with_invalid_signature():
179-
# auth = Auth(signature_secret='signature_secret')
180-
# params = {'param1': 'value1', 'param2': 'value2', 'sig': 'invalid_signature'}
181-
# expected_signature = hmac.new(
182-
# b'signature_secret', b'param1value1param2value2', hashlib.sha256
183-
# ).hexdigest()
184-
185-
# assert auth.check_signature(params) == False
186-
187-
188-
# def test_check_signature_with_empty_signature():
189-
# auth = Auth(signature_secret='signature_secret')
190-
# params = {'param1': 'value1', 'param2': 'value2', 'sig': ''}
191-
# expected_signature = hmac.new(
192-
# b'signature_secret', b'param1value1param2value2', hashlib.sha256
193-
# ).hexdigest()
194-
195-
# assert auth.check_signature(params) == False
176+
def test_check_signature_invalid_signature():
177+
auth = Auth(api_key=api_key, signature_secret=signature_secret)
178+
params = {
179+
'param': 'value',
180+
'timestamp': 1234567890,
181+
'sig': 'invalid_signature',
182+
}
183+
assert auth.check_signature(params) == False

http_client/tests/test_http_client.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ def test_make_post_request_with_signature():
131131
request_path='/post_signed_params',
132132
params=params,
133133
auth_type='signature',
134+
body_type='data',
134135
)
135136
assert res['hello'] == 'world!'
136137

sms/src/vonage_sms/models.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from typing import Literal, Optional
2+
3+
from pydantic import (
4+
BaseModel,
5+
Field,
6+
ValidationInfo,
7+
field_validator,
8+
model_validator,
9+
)
10+
11+
12+
class SmsMessage(BaseModel):
13+
"""Message object containing the data and options for an SMS message."""
14+
15+
to: str
16+
from_: str = Field(..., serialization_alias='from')
17+
text: str
18+
sig: Optional[str] = Field(None, min_length=16, max_length=60)
19+
client_ref: Optional[str] = Field(
20+
None, serialization_alias='client-ref', max_length=100
21+
)
22+
type: Optional[Literal['text', 'binary', 'unicode']] = None
23+
ttl: Optional[int] = Field(None, ge=20000, le=604800000)
24+
status_report_req: Optional[bool] = Field(
25+
None, serialization_alias='status-report-req'
26+
)
27+
callback: Optional[str] = Field(None, max_length=100)
28+
message_class: Optional[int] = Field(
29+
None, serialization_alias='message-class', ge=0, le=3
30+
)
31+
body: Optional[str] = None
32+
udh: Optional[str] = None
33+
protocol_id: Optional[int] = Field(
34+
None, serialization_alias='protocol-id', ge=0, le=255
35+
)
36+
account_ref: Optional[str] = Field(None, serialization_alias='account-ref')
37+
entity_id: Optional[str] = Field(None, serialization_alias='entity-id')
38+
content_id: Optional[str] = Field(None, serialization_alias='content-id')
39+
40+
@field_validator('body', 'udh')
41+
@classmethod
42+
def validate_body(cls, value, info: ValidationInfo):
43+
data = info.data
44+
if 'type' not in data or not data['type'] == 'binary':
45+
raise ValueError(
46+
'This parameter can only be set when the "type" parameter is set to "binary".'
47+
)
48+
return value
49+
50+
@model_validator(mode='after')
51+
def validate_type(self) -> 'SmsMessage':
52+
if self.type == 'binary' and self.body is None and self.udh is None:
53+
raise ValueError('This parameter is required for binary messages.')
54+
return self

sms/src/vonage_sms/responses.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from dataclasses import dataclass
2+
from typing import List, Optional
3+
4+
5+
@dataclass
6+
class MessageResponse:
7+
to: str
8+
message_id: str
9+
status: str
10+
remaining_balance: str
11+
message_price: str
12+
network: str
13+
client_ref: Optional[str] = None
14+
account_ref: Optional[str] = None
15+
16+
17+
@dataclass
18+
class SmsResponse:
19+
message_count: str
20+
messages: List[MessageResponse]

sms/src/vonage_sms/sms.py

Lines changed: 5 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,19 @@
11
from copy import deepcopy
2-
from dataclasses import dataclass
3-
from typing import List, Literal, Optional
42

5-
from pydantic import BaseModel, Field, field_validator, validate_call
3+
from pydantic import validate_call
64
from vonage_http_client.http_client import HttpClient
75

86
from .errors import PartialFailureError, SmsError
9-
10-
11-
class SmsMessage(BaseModel):
12-
to: str
13-
from_: str = Field(..., alias="from")
14-
text: str
15-
sig: Optional[str] = Field(None, min_length=16, max_length=60)
16-
client_ref: Optional[str] = Field(None, alias="client-ref", max_length=100)
17-
type: Optional[Literal['text', 'binary', 'unicode']] = None
18-
ttl: Optional[int] = Field(None, ge=20000, le=604800000)
19-
status_report_req: Optional[bool] = Field(None, alias='status-report-req')
20-
callback: Optional[str] = Field(None, max_length=100)
21-
message_class: Optional[int] = Field(None, alias='message-class', ge=0, le=3)
22-
body: Optional[str] = None
23-
udh: Optional[str] = None
24-
protocol_id: Optional[int] = Field(None, alias='protocol-id', ge=0, le=255)
25-
account_ref: Optional[str] = Field(None, alias='account-ref')
26-
entity_id: Optional[str] = Field(None, alias='entity-id')
27-
content_id: Optional[str] = Field(None, alias='content-id')
28-
29-
@field_validator('body', 'udh')
30-
@classmethod
31-
def validate_body(cls, value, values):
32-
if 'type' not in values or not values['type'] == 'binary':
33-
raise ValueError(
34-
'This parameter can only be set when the "type" parameter is set to "binary".'
35-
)
36-
if values['type'] == 'binary' and not value:
37-
raise ValueError('This parameter is required for binary messages.')
38-
39-
40-
@dataclass
41-
class MessageResponse:
42-
to: str
43-
message_id: str
44-
status: str
45-
remaining_balance: str
46-
message_price: str
47-
network: str
48-
client_ref: Optional[str] = None
49-
account_ref: Optional[str] = None
50-
51-
52-
@dataclass
53-
class SmsResponse:
54-
message_count: str
55-
messages: List[MessageResponse]
7+
from .models import SmsMessage
8+
from .responses import MessageResponse, SmsResponse
569

5710

5811
class Sms:
5912
"""Calls Vonage's SMS API."""
6013

6114
def __init__(self, http_client: HttpClient) -> None:
6215
self._http_client = deepcopy(http_client)
16+
self._body_type = 'data'
6317
if self._http_client._auth._signature_secret:
6418
self._auth_type = 'signature'
6519
else:
@@ -73,6 +27,7 @@ def send(self, message: SmsMessage) -> SmsResponse:
7327
'/sms/json',
7428
message.model_dump(by_alias=True),
7529
self._auth_type,
30+
self._body_type,
7631
)
7732

7833
if int(response['message-count']) > 1:

sms/tests/data/default.json

Lines changed: 0 additions & 19 deletions
This file was deleted.

sms/tests/data/fraud_score.json

Lines changed: 0 additions & 15 deletions
This file was deleted.

sms/tests/data/send_sms.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

sms/tests/data/sim_swap.json

Lines changed: 0 additions & 11 deletions
This file was deleted.

0 commit comments

Comments
 (0)