Skip to content

Commit 63f7226

Browse files
authored
fix(storage): add query parameters option to download (#1327)
1 parent 8757f23 commit 63f7226

File tree

2 files changed

+50
-5
lines changed

2 files changed

+50
-5
lines changed

src/storage/src/storage3/_async/file_api.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from dataclasses import dataclass, field
77
from io import BufferedReader, FileIO
88
from pathlib import Path
9-
from typing import Any, List, Literal, Optional, Union, cast
9+
from typing import Any, Dict, List, Literal, Optional, Union, cast
1010

1111
from httpx import AsyncClient, Headers, HTTPStatusError, Response
1212
from yarl import URL
@@ -439,7 +439,10 @@ async def list(
439439
return response.json()
440440

441441
async def download(
442-
self, path: str, options: Optional[DownloadOptions] = None
442+
self,
443+
path: str,
444+
options: Optional[DownloadOptions] = None,
445+
query_params: Optional[Dict[str, str]] = None,
443446
) -> bytes:
444447
"""
445448
Downloads a file.
@@ -449,20 +452,23 @@ async def download(
449452
path
450453
The file path to be downloaded, including the path and file name. For example `folder/image.png`.
451454
"""
452-
url_options = options or {}
455+
url_options = options or DownloadOptions()
453456
render_path = (
454457
["render", "image", "authenticated"]
455458
if url_options.get("transform")
456459
else ["object"]
457460
)
458461

459-
transform_options = url_options.get("transform") or {}
462+
transform_options = url_options.get("transform") or TransformOptions()
460463

461464
path_parts = relative_path_to_parts(path)
462465
response = await self._request(
463466
"GET",
464467
[*render_path, self.id, *path_parts],
465-
query_params=transform_to_dict(transform_options),
468+
query_params={
469+
**transform_to_dict(transform_options),
470+
**(query_params or {}),
471+
},
466472
)
467473
return response.content
468474

src/storage/tests/_async/test_client.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,45 @@ async def test_client_upload(
283283
assert image_info.get("metadata", {}).get("mimetype") == file.mime_type
284284

285285

286+
async def test_client_upload_with_query(
287+
storage_file_client: AsyncBucketProxy, file: FileForTesting
288+
) -> None:
289+
"""Ensure we can upload files to a bucket, even with query parameters"""
290+
await storage_file_client.upload(
291+
file.bucket_path, file.local_path, {"content-type": file.mime_type}
292+
)
293+
294+
image = await storage_file_client.download(
295+
file.bucket_path, query_params={"my-param": "test"}
296+
)
297+
files = await storage_file_client.list(file.bucket_folder)
298+
image_info = next((f for f in files if f.get("name") == file.name), None)
299+
300+
assert image == file.file_content
301+
assert image_info is not None
302+
assert image_info.get("metadata", {}).get("mimetype") == file.mime_type
303+
304+
305+
async def test_client_download_with_query_doesnt_lose_params(
306+
storage_file_client: AsyncBucketProxy, file: FileForTesting
307+
) -> None:
308+
"""Ensure query params aren't lost"""
309+
from yarl import URL
310+
311+
params = {"my-param": "test"}
312+
mock_response = Mock()
313+
with patch.object(HttpxClient, "request") as mock_request:
314+
mock_request.return_value = mock_response
315+
await storage_file_client.download(file.bucket_path, query_params=params)
316+
expected_url = storage_file_client._base_url.joinpath(
317+
"object", storage_file_client.id, *URL(file.bucket_path).parts
318+
).with_query(params)
319+
actual_url = mock_request.call_args[0][1]
320+
321+
assert URL(actual_url).query == params
322+
assert str(expected_url) == actual_url
323+
324+
286325
async def test_client_update(
287326
storage_file_client: AsyncBucketProxy,
288327
two_files: list[FileForTesting],

0 commit comments

Comments
 (0)