Skip to content

Commit 2b2cd0e

Browse files
committed
Properly handle API response format
1 parent e0b5563 commit 2b2cd0e

File tree

4 files changed

+34
-19
lines changed

4 files changed

+34
-19
lines changed

src/contiguity/_response.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,37 @@
11
from http import HTTPStatus
2+
from typing import Generic, TypeVar
23

3-
from msgspec import Struct
4+
import msgspec
45

6+
T = TypeVar("T", bound=msgspec.Struct)
57

6-
class ResponseMetadata(Struct):
8+
9+
class ResponseMetadata(msgspec.Struct):
710
id: str
811
timestamp: int
912
api_version: str
1013
object: str
1114

1215

13-
class BaseResponse(Struct):
16+
class RawResponse(ResponseMetadata, Generic[T]):
17+
data: T
18+
19+
20+
class BaseResponse(msgspec.Struct):
1421
metadata: ResponseMetadata
1522

1623

1724
class ErrorResponse(BaseResponse):
1825
error: str
1926
status: HTTPStatus
27+
28+
29+
def decode_response(content: bytes, /, *, type: type[T]) -> T:
30+
raw = msgspec.json.decode(content, type=RawResponse[type])
31+
metadata = ResponseMetadata(
32+
id=raw.id,
33+
timestamp=raw.timestamp,
34+
api_version=raw.api_version,
35+
object=raw.object,
36+
)
37+
return msgspec.convert({**msgspec.to_builtins(raw.data), "metadata": metadata}, type=type)

src/contiguity/email.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@
33
from http import HTTPStatus
44
from typing import overload
55

6-
import msgspec
7-
86
from ._product import BaseProduct
9-
from ._response import BaseResponse, ErrorResponse
7+
from ._response import BaseResponse, ErrorResponse, decode_response
108

119

1210
class EmailResponse(BaseResponse):
@@ -83,11 +81,11 @@ def email( # noqa: PLR0913
8381
response = self._client.post("/send/email", json=email_payload)
8482

8583
if response.status_code != HTTPStatus.OK:
86-
data = msgspec.json.decode(response.content, type=ErrorResponse)
84+
data = decode_response(response.content, type=ErrorResponse)
8785
msg = f"failed to send email. {response.status_code} {data.error}"
8886
raise ValueError(msg)
8987

90-
data = msgspec.json.decode(response.content, type=EmailResponse)
88+
data = decode_response(response.content, type=EmailResponse)
9189
if self.debug:
9290
print(f"successfully sent email to {to}")
9391

src/contiguity/otp.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@
33
from enum import Enum
44
from http import HTTPStatus
55

6-
import msgspec
76
import phonenumbers
87

98
from ._product import BaseProduct
10-
from ._response import BaseResponse, ErrorResponse
9+
from ._response import BaseResponse, ErrorResponse, decode_response
1110

1211

1312
class OTPLanguage(str, Enum):
@@ -82,11 +81,11 @@ def send(
8281
)
8382

8483
if response.status_code != HTTPStatus.OK:
85-
data = msgspec.json.decode(response.content, type=ErrorResponse)
84+
data = decode_response(response.content, type=ErrorResponse)
8685
msg = f"failed to send OTP. {response.status_code} {data.error}"
8786
raise ValueError(msg)
8887

89-
data = msgspec.json.decode(response.content, type=OTPSendResponse)
88+
data = decode_response(response.content, type=OTPSendResponse)
9089
if self.debug:
9190
print(f"successfully sent OTP {data.otp_id} to {to}")
9291

@@ -101,11 +100,11 @@ def resend(self, otp_id: str, /) -> OTPResendResponse:
101100
)
102101

103102
if response.status_code != HTTPStatus.OK:
104-
data = msgspec.json.decode(response.content, type=ErrorResponse)
103+
data = decode_response(response.content, type=ErrorResponse)
105104
msg = f"failed to resend OTP. {response.status_code} {data.error}"
106105
raise ValueError(msg)
107106

108-
data = msgspec.json.decode(response.content, type=OTPResendResponse)
107+
data = decode_response(response.content, type=OTPResendResponse)
109108
if self.debug:
110109
print(f"successfully resent OTP {otp_id} with status: {data.resent}")
111110

@@ -121,11 +120,11 @@ def verify(self, otp: int | str, /, *, otp_id: str) -> OTPVerifyResponse:
121120
)
122121

123122
if response.status_code != HTTPStatus.OK:
124-
data = msgspec.json.decode(response.content, type=ErrorResponse)
123+
data = decode_response(response.content, type=ErrorResponse)
125124
msg = f"failed to verify OTP. {response.status_code} {data.error}"
126125
raise ValueError(msg)
127126

128-
data = msgspec.json.decode(response.content, type=OTPVerifyResponse)
127+
data = decode_response(response.content, type=OTPVerifyResponse)
129128
if self.debug:
130129
print(f"successfully verified OTP ({otp}) with status: {data.verified}")
131130

src/contiguity/text.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
from http import HTTPStatus
22

3-
import msgspec
43
import phonenumbers
54

65
from ._product import BaseProduct
7-
from ._response import BaseResponse, ErrorResponse
6+
from ._response import BaseResponse, ErrorResponse, decode_response
87

98

109
class TextResponse(BaseResponse):
@@ -41,10 +40,11 @@ def send(self, *, to: str, message: str) -> TextResponse:
4140
)
4241

4342
if response.status_code != HTTPStatus.OK:
43+
data = decode_response(response.content, type=ErrorResponse)
4444
msg = f"failed to send message. {response.status_code} {data.error}"
4545
raise ValueError(msg)
4646

47-
data = msgspec.json.decode(response.content, type=TextResponse)
47+
data = decode_response(response.content, type=TextResponse)
4848
if self.debug:
4949
print(f"successfully sent text to {to}")
5050

0 commit comments

Comments
 (0)