Skip to content
This repository was archived by the owner on Sep 8, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 18 additions & 6 deletions storage3/_async/file_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@
from ..exceptions import StorageApiError
from ..types import (
BaseBucket,
CreateSignedUrlResponse,
CreateSignedURLsOptions,
DownloadOptions,
FileOptions,
ListBucketFilesOptions,
RequestMethod,
SignedUploadURL,
SignedUrlResponse,
UploadData,
UploadResponse,
URLOptions,
Expand Down Expand Up @@ -72,13 +74,14 @@ async def create_signed_upload_url(self, path: str) -> SignedUploadURL:
response = await self._request("POST", f"/object/upload/sign/{_path}")
data = response.json()
full_url: urllib.parse.ParseResult = urllib.parse.urlparse(
str(self._client.base_url) + data["url"]
str(self._client.base_url) + cast(str, data["url"]).lstrip("/")
)
query_params = urllib.parse.parse_qs(full_url.query)
if not query_params.get("token"):
raise StorageException("No token sent by the API")
return {
"signed_url": full_url.geturl(),
"signedUrl": full_url.geturl(),
"token": query_params["token"][0],
"path": path,
}
Expand Down Expand Up @@ -151,7 +154,7 @@ async def upload_to_signed_url(

async def create_signed_url(
self, path: str, expires_in: int, options: URLOptions = {}
) -> dict[str, str]:
) -> SignedUrlResponse:
"""
Parameters
----------
Expand Down Expand Up @@ -187,14 +190,15 @@ async def create_signed_url(
url = urllib.parse.urlparse(data["signedURL"])
url = urllib.parse.quote(url.path) + f"?{url.query}"

data["signedURL"] = (
signedURL = (
f"{self._client.base_url}{cast(str, url).lstrip('/')}{download_query}"
)
data: SignedUrlResponse = {"signedURL": signedURL, "signedUrl": signedURL}
return data

async def create_signed_urls(
self, paths: list[str], expires_in: int, options: CreateSignedURLsOptions = {}
) -> list[dict[str, str]]:
) -> list[CreateSignedUrlResponse]:
"""
Parameters
----------
Expand Down Expand Up @@ -222,16 +226,24 @@ async def create_signed_urls(
json=json,
)
data = response.json()
signed_urls = []
for item in data:

# Prepare URL
url = urllib.parse.urlparse(item["signedURL"])
url = urllib.parse.quote(url.path) + f"?{url.query}"

item["signedURL"] = (
signedURL = (
f"{self._client.base_url}{cast(str, url).lstrip('/')}{download_query}"
)
return data
signed_item: CreateSignedUrlResponse = {
"error": item["error"],
"path": item["path"],
"signedURL": signedURL,
"signedUrl": signedURL,
}
signed_urls.append(signed_item)
return signed_urls

async def get_public_url(self, path: str, options: URLOptions = {}) -> str:
"""
Expand Down
24 changes: 18 additions & 6 deletions storage3/_sync/file_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@
from ..exceptions import StorageApiError
from ..types import (
BaseBucket,
CreateSignedUrlResponse,
CreateSignedURLsOptions,
DownloadOptions,
FileOptions,
ListBucketFilesOptions,
RequestMethod,
SignedUploadURL,
SignedUrlResponse,
UploadData,
UploadResponse,
URLOptions,
Expand Down Expand Up @@ -72,13 +74,14 @@ def create_signed_upload_url(self, path: str) -> SignedUploadURL:
response = self._request("POST", f"/object/upload/sign/{_path}")
data = response.json()
full_url: urllib.parse.ParseResult = urllib.parse.urlparse(
str(self._client.base_url) + data["url"]
str(self._client.base_url) + cast(str, data["url"]).lstrip("/")
)
query_params = urllib.parse.parse_qs(full_url.query)
if not query_params.get("token"):
raise StorageException("No token sent by the API")
return {
"signed_url": full_url.geturl(),
"signedUrl": full_url.geturl(),
"token": query_params["token"][0],
"path": path,
}
Expand Down Expand Up @@ -151,7 +154,7 @@ def upload_to_signed_url(

def create_signed_url(
self, path: str, expires_in: int, options: URLOptions = {}
) -> dict[str, str]:
) -> SignedUrlResponse:
"""
Parameters
----------
Expand Down Expand Up @@ -187,14 +190,15 @@ def create_signed_url(
url = urllib.parse.urlparse(data["signedURL"])
url = urllib.parse.quote(url.path) + f"?{url.query}"

data["signedURL"] = (
signedURL = (
f"{self._client.base_url}{cast(str, url).lstrip('/')}{download_query}"
)
data: SignedUrlResponse = {"signedURL": signedURL, "signedUrl": signedURL}
return data

def create_signed_urls(
self, paths: list[str], expires_in: int, options: CreateSignedURLsOptions = {}
) -> list[dict[str, str]]:
) -> list[CreateSignedUrlResponse]:
"""
Parameters
----------
Expand Down Expand Up @@ -222,16 +226,24 @@ def create_signed_urls(
json=json,
)
data = response.json()
signed_urls = []
for item in data:

# Prepare URL
url = urllib.parse.urlparse(item["signedURL"])
url = urllib.parse.quote(url.path) + f"?{url.query}"

item["signedURL"] = (
signedURL = (
f"{self._client.base_url}{cast(str, url).lstrip('/')}{download_query}"
)
return data
signed_item: CreateSignedUrlResponse = {
"error": item["error"],
"path": item["path"],
"signedURL": signedURL,
"signedUrl": signedURL,
}
signed_urls.append(signed_item)
return signed_urls

def get_public_url(self, path: str, options: URLOptions = {}) -> str:
"""
Expand Down
17 changes: 16 additions & 1 deletion storage3/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from dataclasses import asdict, dataclass
from datetime import datetime
from typing import Any, Dict, Literal, Optional, TypedDict, Union
from typing import Any, Dict, Literal, NotRequired, Optional, TypedDict, Union

import dateutil.parser

Expand Down Expand Up @@ -37,6 +37,7 @@ class _sortByType(TypedDict, total=False):

class SignedUploadURL(TypedDict):
signed_url: str
signedUrl: str
token: str
path: str

Expand Down Expand Up @@ -98,9 +99,23 @@ class UploadData(TypedDict, total=False):
class UploadResponse:
path: str
full_path: str
fullPath: str

def __init__(self, path, Key):
self.path = path
self.full_path = Key
self.fullPath = Key

dict = asdict


class SignedUrlResponse(TypedDict):
signedURL: str
signedUrl: str


class CreateSignedUrlResponse(TypedDict):
error: NotRequired[str]
path: str
signedURL: str
signedUrl: str
2 changes: 1 addition & 1 deletion tests/_async/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ async def test_client_create_signed_upload_url(
data = await storage_file_client.create_signed_upload_url(path)
assert data["path"] == path
assert data["token"]
expected_url = f"{storage_file_client._client.base_url}/object/upload/sign/{storage_file_client.id}/{path.lstrip('/')}"
expected_url = f"{storage_file_client._client.base_url}object/upload/sign/{storage_file_client.id}/{path.lstrip('/')}"
assert data["signed_url"].startswith(expected_url)


Expand Down
2 changes: 1 addition & 1 deletion tests/_sync/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ def test_client_create_signed_upload_url(
data = storage_file_client.create_signed_upload_url(path)
assert data["path"] == path
assert data["token"]
expected_url = f"{storage_file_client._client.base_url}/object/upload/sign/{storage_file_client.id}/{path.lstrip('/')}"
expected_url = f"{storage_file_client._client.base_url}object/upload/sign/{storage_file_client.id}/{path.lstrip('/')}"
assert data["signed_url"].startswith(expected_url)


Expand Down
Loading