|
@@ -1,6 +1,8 @@
|
|
import asyncio
|
|
import asyncio
|
|
import re
|
|
import re
|
|
from http import HTTPStatus
|
|
from http import HTTPStatus
|
|
|
|
+from io import BytesIO
|
|
|
|
+from typing import Any
|
|
from typing import Awaitable
|
|
from typing import Awaitable
|
|
from typing import Callable
|
|
from typing import Callable
|
|
from typing import Dict
|
|
from typing import Dict
|
|
@@ -13,13 +15,15 @@ import aiohttp
|
|
from aiohttp import ClientResponse
|
|
from aiohttp import ClientResponse
|
|
from aiohttp import ClientSession
|
|
from aiohttp import ClientSession
|
|
from pydantic import AnyHttpUrl
|
|
from pydantic import AnyHttpUrl
|
|
|
|
+from pydantic import field_validator
|
|
|
|
|
|
from clean_python import Json
|
|
from clean_python import Json
|
|
|
|
+from clean_python import ValueObject
|
|
|
|
|
|
from .exceptions import ApiException
|
|
from .exceptions import ApiException
|
|
from .response import Response
|
|
from .response import Response
|
|
|
|
|
|
-__all__ = ["ApiProvider"]
|
|
|
|
|
|
+__all__ = ["ApiProvider", "FileFormPost"]
|
|
|
|
|
|
|
|
|
|
RETRY_STATUSES = frozenset({413, 429, 503}) # like in urllib3
|
|
RETRY_STATUSES = frozenset({413, 429, 503}) # like in urllib3
|
|
@@ -57,6 +61,21 @@ def add_query_params(url: str, params: Optional[Json]) -> str:
|
|
return url + "?" + urlencode(params, doseq=True)
|
|
return url + "?" + urlencode(params, doseq=True)
|
|
|
|
|
|
|
|
|
|
|
|
+class FileFormPost(ValueObject):
|
|
|
|
+ file_name: str
|
|
|
|
+ file: Any # typing of BinaryIO / BytesIO is hard!
|
|
|
|
+ field_name: str = "file"
|
|
|
|
+ content_type: str = "application/octet-stream"
|
|
|
|
+
|
|
|
|
+ @field_validator("file")
|
|
|
|
+ @classmethod
|
|
|
|
+ def validate_file(cls, v):
|
|
|
|
+ if isinstance(v, bytes):
|
|
|
|
+ return BytesIO(v)
|
|
|
|
+ assert hasattr(v, "read") # poor-mans BinaryIO validation
|
|
|
|
+ return v
|
|
|
|
+
|
|
|
|
+
|
|
class ApiProvider:
|
|
class ApiProvider:
|
|
"""Basic JSON API provider with retry policy and bearer tokens.
|
|
"""Basic JSON API provider with retry policy and bearer tokens.
|
|
|
|
|
|
@@ -94,8 +113,11 @@ class ApiProvider:
|
|
params: Optional[Json],
|
|
params: Optional[Json],
|
|
json: Optional[Json],
|
|
json: Optional[Json],
|
|
fields: Optional[Json],
|
|
fields: Optional[Json],
|
|
|
|
+ file: Optional[FileFormPost],
|
|
timeout: float,
|
|
timeout: float,
|
|
) -> ClientResponse:
|
|
) -> ClientResponse:
|
|
|
|
+ if file is not None:
|
|
|
|
+ raise NotImplementedError("ApiProvider doesn't yet support file uploads")
|
|
request_kwargs = {
|
|
request_kwargs = {
|
|
"method": method,
|
|
"method": method,
|
|
"url": add_query_params(
|
|
"url": add_query_params(
|
|
@@ -130,10 +152,11 @@ class ApiProvider:
|
|
params: Optional[Json] = None,
|
|
params: Optional[Json] = None,
|
|
json: Optional[Json] = None,
|
|
json: Optional[Json] = None,
|
|
fields: Optional[Json] = None,
|
|
fields: Optional[Json] = None,
|
|
|
|
+ file: Optional[FileFormPost] = None,
|
|
timeout: float = 5.0,
|
|
timeout: float = 5.0,
|
|
) -> Optional[Json]:
|
|
) -> Optional[Json]:
|
|
response = await self._request_with_retry(
|
|
response = await self._request_with_retry(
|
|
- method, path, params, json, fields, timeout
|
|
|
|
|
|
+ method, path, params, json, fields, file, timeout
|
|
)
|
|
)
|
|
status = HTTPStatus(response.status)
|
|
status = HTTPStatus(response.status)
|
|
content_type = response.headers.get("Content-Type")
|
|
content_type = response.headers.get("Content-Type")
|
|
@@ -156,10 +179,11 @@ class ApiProvider:
|
|
params: Optional[Json] = None,
|
|
params: Optional[Json] = None,
|
|
json: Optional[Json] = None,
|
|
json: Optional[Json] = None,
|
|
fields: Optional[Json] = None,
|
|
fields: Optional[Json] = None,
|
|
|
|
+ file: Optional[FileFormPost] = None,
|
|
timeout: float = 5.0,
|
|
timeout: float = 5.0,
|
|
) -> Response:
|
|
) -> Response:
|
|
response = await self._request_with_retry(
|
|
response = await self._request_with_retry(
|
|
- method, path, params, json, fields, timeout
|
|
|
|
|
|
+ method, path, params, json, fields, file, timeout
|
|
)
|
|
)
|
|
return Response(
|
|
return Response(
|
|
status=response.status,
|
|
status=response.status,
|