kibonet-python-sdk 0.1.0__tar.gz → 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {kibonet_python_sdk-0.1.0/src/kibonet_python_sdk.egg-info → kibonet_python_sdk-0.1.1}/PKG-INFO +1 -1
- {kibonet_python_sdk-0.1.0 → kibonet_python_sdk-0.1.1}/pyproject.toml +1 -1
- {kibonet_python_sdk-0.1.0 → kibonet_python_sdk-0.1.1/src/kibonet_python_sdk.egg-info}/PKG-INFO +1 -1
- {kibonet_python_sdk-0.1.0 → kibonet_python_sdk-0.1.1}/src/kibonet_sdk/__init__.py +1 -1
- {kibonet_python_sdk-0.1.0 → kibonet_python_sdk-0.1.1}/src/kibonet_sdk/async_client.py +7 -6
- {kibonet_python_sdk-0.1.0 → kibonet_python_sdk-0.1.1}/src/kibonet_sdk/client.py +8 -9
- {kibonet_python_sdk-0.1.0 → kibonet_python_sdk-0.1.1}/src/kibonet_sdk/utils/async_http_client.py +1 -1
- {kibonet_python_sdk-0.1.0 → kibonet_python_sdk-0.1.1}/src/kibonet_sdk/utils/http_client.py +1 -1
- {kibonet_python_sdk-0.1.0 → kibonet_python_sdk-0.1.1}/tests/test_async_client.py +15 -0
- {kibonet_python_sdk-0.1.0 → kibonet_python_sdk-0.1.1}/tests/test_client.py +14 -0
- {kibonet_python_sdk-0.1.0 → kibonet_python_sdk-0.1.1}/LICENSE +0 -0
- {kibonet_python_sdk-0.1.0 → kibonet_python_sdk-0.1.1}/README.md +0 -0
- {kibonet_python_sdk-0.1.0 → kibonet_python_sdk-0.1.1}/setup.cfg +0 -0
- {kibonet_python_sdk-0.1.0 → kibonet_python_sdk-0.1.1}/src/kibonet_python_sdk.egg-info/SOURCES.txt +0 -0
- {kibonet_python_sdk-0.1.0 → kibonet_python_sdk-0.1.1}/src/kibonet_python_sdk.egg-info/dependency_links.txt +0 -0
- {kibonet_python_sdk-0.1.0 → kibonet_python_sdk-0.1.1}/src/kibonet_python_sdk.egg-info/requires.txt +0 -0
- {kibonet_python_sdk-0.1.0 → kibonet_python_sdk-0.1.1}/src/kibonet_python_sdk.egg-info/top_level.txt +0 -0
- {kibonet_python_sdk-0.1.0 → kibonet_python_sdk-0.1.1}/src/kibonet_sdk/exceptions.py +0 -0
- {kibonet_python_sdk-0.1.0 → kibonet_python_sdk-0.1.1}/src/kibonet_sdk/utils/__init__.py +0 -0
- {kibonet_python_sdk-0.1.0 → kibonet_python_sdk-0.1.1}/src/kibonet_sdk/utils/validators.py +0 -0
- {kibonet_python_sdk-0.1.0 → kibonet_python_sdk-0.1.1}/tests/test_validators.py +0 -0
|
@@ -214,21 +214,22 @@ class AsyncKiboNetClient:
|
|
|
214
214
|
)
|
|
215
215
|
if response.status_code == 429:
|
|
216
216
|
raise KiboNetRateLimitError("Rate limit exceeded", code=429)
|
|
217
|
-
if 500 <= response.status_code < 600:
|
|
218
|
-
raise KiboNetServerError(
|
|
219
|
-
f"KiboNet server error (HTTP {response.status_code})",
|
|
220
|
-
code=response.status_code,
|
|
221
|
-
)
|
|
222
217
|
|
|
218
|
+
# Always try to parse JSON first — KiboNet uses 5xx for business errors too
|
|
223
219
|
try:
|
|
224
220
|
data = response.json()
|
|
225
221
|
except Exception:
|
|
222
|
+
if 500 <= response.status_code < 600:
|
|
223
|
+
raise KiboNetServerError(
|
|
224
|
+
f"KiboNet server error (HTTP {response.status_code})",
|
|
225
|
+
code=response.status_code,
|
|
226
|
+
)
|
|
226
227
|
raise KiboNetAPIError(
|
|
227
228
|
f"Unexpected non-JSON response (HTTP {response.status_code}): "
|
|
228
229
|
f"{response.text[:200]}"
|
|
229
230
|
)
|
|
230
231
|
|
|
231
|
-
if
|
|
232
|
+
if not data.get("success", False):
|
|
232
233
|
raise KiboNetAPIError(
|
|
233
234
|
data.get("message", f"API error (HTTP {response.status_code})"),
|
|
234
235
|
code=data.get("code"),
|
|
@@ -252,24 +252,23 @@ class KiboNetClient:
|
|
|
252
252
|
"Authentication failed — check your api_key and api_secret"
|
|
253
253
|
)
|
|
254
254
|
if response.status_code == 429:
|
|
255
|
-
raise KiboNetRateLimitError(
|
|
256
|
-
"Rate limit exceeded", code=429
|
|
257
|
-
)
|
|
258
|
-
if 500 <= response.status_code < 600:
|
|
259
|
-
raise KiboNetServerError(
|
|
260
|
-
f"KiboNet server error (HTTP {response.status_code})",
|
|
261
|
-
code=response.status_code,
|
|
262
|
-
)
|
|
255
|
+
raise KiboNetRateLimitError("Rate limit exceeded", code=429)
|
|
263
256
|
|
|
257
|
+
# Always try to parse JSON first — KiboNet uses 5xx for business errors too
|
|
264
258
|
try:
|
|
265
259
|
data = response.json()
|
|
266
260
|
except Exception:
|
|
261
|
+
if 500 <= response.status_code < 600:
|
|
262
|
+
raise KiboNetServerError(
|
|
263
|
+
f"KiboNet server error (HTTP {response.status_code})",
|
|
264
|
+
code=response.status_code,
|
|
265
|
+
)
|
|
267
266
|
raise KiboNetAPIError(
|
|
268
267
|
f"Unexpected non-JSON response (HTTP {response.status_code}): "
|
|
269
268
|
f"{response.text[:200]}"
|
|
270
269
|
)
|
|
271
270
|
|
|
272
|
-
if
|
|
271
|
+
if not data.get("success", False):
|
|
273
272
|
raise KiboNetAPIError(
|
|
274
273
|
data.get("message", f"API error (HTTP {response.status_code})"),
|
|
275
274
|
code=data.get("code"),
|
|
@@ -130,6 +130,7 @@ class TestAsyncSendSMS:
|
|
|
130
130
|
@pytest.mark.asyncio
|
|
131
131
|
@respx.mock
|
|
132
132
|
async def test_send_sms_server_error(self, async_client):
|
|
133
|
+
# Non-JSON 5xx → KiboNetServerError
|
|
133
134
|
respx.post(f"{BASE_URL}/api/v1/vendor/message/send").mock(
|
|
134
135
|
return_value=httpx.Response(500, text="Internal Server Error")
|
|
135
136
|
)
|
|
@@ -138,6 +139,20 @@ class TestAsyncSendSMS:
|
|
|
138
139
|
sender_id="Sender", message="Hi", contacts="255712345678"
|
|
139
140
|
)
|
|
140
141
|
|
|
142
|
+
@pytest.mark.asyncio
|
|
143
|
+
@respx.mock
|
|
144
|
+
async def test_send_sms_business_error_as_5xx(self, async_client):
|
|
145
|
+
# KiboNet returns 5xx with JSON success=false for business errors
|
|
146
|
+
respx.post(f"{BASE_URL}/api/v1/vendor/message/send").mock(
|
|
147
|
+
return_value=httpx.Response(
|
|
148
|
+
501, json={"code": 501, "success": False, "message": "Sender id does not exists to your account.", "data": None}
|
|
149
|
+
)
|
|
150
|
+
)
|
|
151
|
+
with pytest.raises(KiboNetAPIError, match="Sender id does not exists"):
|
|
152
|
+
await async_client.send_sms(
|
|
153
|
+
sender_id="INVALID", message="Hi", contacts="255712345678"
|
|
154
|
+
)
|
|
155
|
+
|
|
141
156
|
@pytest.mark.asyncio
|
|
142
157
|
@respx.mock
|
|
143
158
|
async def test_send_sms_api_error_success_false(self, async_client):
|
|
@@ -139,6 +139,7 @@ class TestSendSMS:
|
|
|
139
139
|
|
|
140
140
|
@respx.mock
|
|
141
141
|
def test_send_sms_server_error(self, client):
|
|
142
|
+
# Non-JSON 5xx → KiboNetServerError
|
|
142
143
|
respx.post(f"{BASE_URL}/api/v1/vendor/message/send").mock(
|
|
143
144
|
return_value=httpx.Response(500, text="Internal Server Error")
|
|
144
145
|
)
|
|
@@ -147,6 +148,19 @@ class TestSendSMS:
|
|
|
147
148
|
sender_id="Sender", message="Hi", contacts="255712345678"
|
|
148
149
|
)
|
|
149
150
|
|
|
151
|
+
@respx.mock
|
|
152
|
+
def test_send_sms_business_error_as_5xx(self, client):
|
|
153
|
+
# KiboNet returns 5xx with JSON success=false for business errors
|
|
154
|
+
respx.post(f"{BASE_URL}/api/v1/vendor/message/send").mock(
|
|
155
|
+
return_value=httpx.Response(
|
|
156
|
+
501, json={"code": 501, "success": False, "message": "Sender id does not exists to your account.", "data": None}
|
|
157
|
+
)
|
|
158
|
+
)
|
|
159
|
+
with pytest.raises(KiboNetAPIError, match="Sender id does not exists"):
|
|
160
|
+
client.send_sms(
|
|
161
|
+
sender_id="INVALID", message="Hi", contacts="255712345678"
|
|
162
|
+
)
|
|
163
|
+
|
|
150
164
|
@respx.mock
|
|
151
165
|
def test_send_sms_api_error_success_false(self, client):
|
|
152
166
|
respx.post(f"{BASE_URL}/api/v1/vendor/message/send").mock(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kibonet_python_sdk-0.1.0 → kibonet_python_sdk-0.1.1}/src/kibonet_python_sdk.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{kibonet_python_sdk-0.1.0 → kibonet_python_sdk-0.1.1}/src/kibonet_python_sdk.egg-info/requires.txt
RENAMED
|
File without changes
|
{kibonet_python_sdk-0.1.0 → kibonet_python_sdk-0.1.1}/src/kibonet_python_sdk.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|