UPDATE
This commit is contained in:
@@ -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()
|
||||
|
||||
|
||||
@@ -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)})
|
||||
Reference in New Issue
Block a user