This commit is contained in:
2026-04-29 02:58:56 +03:30
parent f0f2ac34b7
commit 27784ee8b9
15 changed files with 1277 additions and 36 deletions
+13 -2
View File
@@ -1,8 +1,10 @@
from dataclasses import dataclass, field
import json
import logging
import requests
from django.conf import settings
from django.core.serializers.json import DjangoJSONEncoder
from .exceptions import ExternalAPIRequestError
from .exceptions import MockDirectoryNotFound, MockFileNotFound
@@ -72,7 +74,8 @@ class ExternalAPIAdapter:
url = f"{base_url}/{str(path).lstrip('/')}"
files = None
request_payload = payload
request_payload = self._make_json_safe(payload)
request_query = self._make_json_safe(query)
request_headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
@@ -95,7 +98,7 @@ class ExternalAPIAdapter:
request_kwargs = {
"method": method,
"url": url,
"params": query,
"params": request_query,
"headers": request_headers,
"timeout": getattr(settings, "EXTERNAL_API_TIMEOUT", 30),
}
@@ -150,6 +153,14 @@ class ExternalAPIAdapter:
if method not in supported_methods:
raise ValueError(f"Unsupported HTTP method '{method}'. Supported methods: {sorted(supported_methods)}")
@staticmethod
def _make_json_safe(value):
if value is None:
return None
# Match Django/DRF JSON rendering so UUID/date-like values can be forwarded safely.
return json.loads(json.dumps(value, cls=DjangoJSONEncoder))
_default_adapter = ExternalAPIAdapter()
+60
View File
@@ -0,0 +1,60 @@
import uuid
from unittest.mock import patch
from django.test import SimpleTestCase, override_settings
from .adapter import ExternalAPIAdapter
class ExternalAPIAdapterTests(SimpleTestCase):
@override_settings(EXTERNAL_API_TIMEOUT=30)
@patch("external_api_adapter.adapter.requests.request")
def test_request_serializes_uuid_payload_for_json_requests(self, mock_request):
mock_request.return_value.status_code = 200
mock_request.return_value.json.return_value = {"ok": True}
farm_uuid = uuid.uuid4()
adapter = ExternalAPIAdapter(
service_registry=type(
"Registry",
(),
{"get": lambda self, name: {"base_url": "https://example.com", "api_key": "token"}},
)()
)
adapter.request(
"ai",
"/api/farm-alerts/tracker/",
method="POST",
payload={"farm_uuid": farm_uuid},
)
mock_request.assert_called_once()
request_kwargs = mock_request.call_args.kwargs
self.assertEqual(request_kwargs["json"], {"farm_uuid": str(farm_uuid)})
@override_settings(EXTERNAL_API_TIMEOUT=30)
@patch("external_api_adapter.adapter.requests.request")
def test_request_serializes_uuid_payload_for_multipart_requests(self, mock_request):
mock_request.return_value.status_code = 200
mock_request.return_value.json.return_value = {"ok": True}
farm_uuid = uuid.uuid4()
adapter = ExternalAPIAdapter(
service_registry=type(
"Registry",
(),
{"get": lambda self, name: {"base_url": "https://example.com", "api_key": "token"}},
)()
)
adapter.request(
"ai",
"/api/upload/",
method="POST",
payload={"farm_uuid": farm_uuid, "__files__": {"image": ("leaf.jpg", b"data", "image/jpeg")}},
)
mock_request.assert_called_once()
request_kwargs = mock_request.call_args.kwargs
self.assertEqual(request_kwargs["data"], {"farm_uuid": str(farm_uuid)})