Skip to content

Commit 08c6c42

Browse files
fix: maintain same structure and logic as sync request.
1 parent 86db626 commit 08c6c42

File tree

1 file changed

+79
-36
lines changed

1 file changed

+79
-36
lines changed

jigsawstack/async_request.py

Lines changed: 79 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import json
2-
from io import BytesIO
32
from typing import Any, AsyncGenerator, Dict, Generic, List, TypedDict, Union, cast
4-
3+
from io import BytesIO
54
import aiohttp
65
from typing_extensions import Literal, TypeVar
76

@@ -28,6 +27,7 @@ def __init__(
2827
headers: Dict[str, str] = None,
2928
data: Union[bytes, None] = None,
3029
stream: Union[bool, None] = False,
30+
files: Union[Dict[str, Any], None] = None, # Add files parameter
3131
):
3232
self.path = path
3333
self.params = params
@@ -38,6 +38,7 @@ def __init__(
3838
self.headers = headers or {"Content-Type": "application/json"}
3939
self.disable_request_logging = config.get("disable_request_logging")
4040
self.stream = stream
41+
self.files = files # Store files for multipart requests
4142

4243
def __convert_params(
4344
self, params: Union[Dict[Any, Any], List[Dict[Any, Any]]]
@@ -171,15 +172,23 @@ def __get_headers(self) -> Dict[str, str]:
171172
Dict[str, str]: Configured HTTP Headers
172173
"""
173174
h = {
174-
"Content-Type": "application/json",
175175
"Accept": "application/json",
176176
"x-api-key": f"{self.api_key}",
177177
}
178178

179+
# only add Content-Type if not using multipart (files)
180+
if not self.files and not self.data:
181+
h["Content-Type"] = "application/json"
182+
179183
if self.disable_request_logging:
180184
h["x-jigsaw-no-request-log"] = "true"
181185

182186
_headers = h.copy()
187+
188+
#don't override Content-Type if using multipart
189+
if self.files and "Content-Type" in self.headers:
190+
self.headers.pop("Content-Type")
191+
183192
_headers.update(self.headers)
184193

185194
return _headers
@@ -231,50 +240,84 @@ async def make_request(
231240
self, session: aiohttp.ClientSession, url: str
232241
) -> aiohttp.ClientResponse:
233242
headers = self.__get_headers()
243+
params = self.params
234244
verb = self.verb
235245
data = self.data
246+
files = self.files
236247

237-
# Convert params to string values for URL encoding
238-
converted_params = self.__convert_params(self.params)
248+
_params = None
249+
_json = None
250+
_data = None
251+
_form_data = None
239252

240253
if verb.lower() in ["get", "delete"]:
254+
#convert params for URL encoding if needed
255+
_params = self.__convert_params(params)
256+
elif files:
257+
# for multipart requests - matches request.py behavior
258+
_form_data = aiohttp.FormData()
259+
260+
# add file(s) to form data
261+
for field_name, file_data in files.items():
262+
if isinstance(file_data, bytes):
263+
# just pass the blob without filename
264+
_form_data.add_field(
265+
field_name,
266+
BytesIO(file_data),
267+
content_type="application/octet-stream"
268+
)
269+
elif isinstance(file_data, tuple):
270+
# if tuple format (filename, data, content_type)
271+
filename, content, content_type = file_data
272+
_form_data.add_field(
273+
field_name,
274+
content,
275+
filename=filename,
276+
content_type=content_type
277+
)
278+
279+
# add params as 'body' field in multipart form (JSON stringified)
280+
if params and isinstance(params, dict):
281+
_form_data.add_field(
282+
"body",
283+
json.dumps(params),
284+
content_type="application/json"
285+
)
286+
elif data:
287+
# for binary data without multipart
288+
_data = data
289+
# pass params as query parameters for binary uploads
290+
if params and isinstance(params, dict):
291+
_params = self.__convert_params(params)
292+
else:
293+
# for JSON requests
294+
_json = params
295+
296+
# m,ake the request based on the data type
297+
if _form_data:
298+
return await session.request(
299+
verb,
300+
url,
301+
params=_params,
302+
data=_form_data,
303+
headers=headers,
304+
)
305+
elif _json is not None:
241306
return await session.request(
242307
verb,
243308
url,
244-
params=converted_params,
309+
params=_params,
310+
json=_json,
245311
headers=headers,
246312
)
247313
else:
248-
if data is not None:
249-
form_data = aiohttp.FormData()
250-
form_data.add_field(
251-
"file",
252-
BytesIO(data),
253-
content_type=headers.get("Content-Type", "application/octet-stream"),
254-
filename="file",
255-
)
256-
257-
if self.params and isinstance(self.params, dict):
258-
form_data.add_field(
259-
"body", json.dumps(self.params), content_type="application/json"
260-
)
261-
262-
multipart_headers = headers.copy()
263-
multipart_headers.pop("Content-Type", None)
264-
265-
return await session.request(
266-
verb,
267-
url,
268-
data=form_data,
269-
headers=multipart_headers,
270-
)
271-
else:
272-
return await session.request(
273-
verb,
274-
url,
275-
json=self.params, # Keep JSON body as original
276-
headers=headers,
277-
)
314+
return await session.request(
315+
verb,
316+
url,
317+
params=_params,
318+
data=_data,
319+
headers=headers,
320+
)
278321

279322
def __get_session(self) -> aiohttp.ClientSession:
280323
"""

0 commit comments

Comments
 (0)