Skip to content

Commit

Permalink
Merge pull request #93 from nens/joost-filename-in-s3-download-url
Browse files Browse the repository at this point in the history
Add filename to generate s3 download url
  • Loading branch information
joostsijm authored Oct 1, 2024
2 parents 9626ab8 + 6d93f37 commit fba464e
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 10 deletions.
8 changes: 7 additions & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
# Changelog of clean-python

## 0.17.1 (unreleased)
## 0.17.2 (unreleased)
----------------------

- Nothing changed yet.


## 0.17.1 (2024-10-01)
----------------------

- Add filename to generate s3 download url


## 0.17.0 (2024-09-23)
----------------------

Expand Down
2 changes: 1 addition & 1 deletion clean_python/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
from .base import * # NOQA

# fmt: off
__version__ = '0.17.1.dev0'
__version__ = '0.17.2.dev0'
# fmt: on
15 changes: 7 additions & 8 deletions clean_python/s3/sync_s3_gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,18 +137,17 @@ def remove_multiple(self, ids: list[Id]) -> None:
)

def _create_presigned_url(
self,
id: Id,
client_method: str,
self, id: Id, client_method: str, filename: str | None = None
) -> AnyHttpUrl:
params = {"Bucket": self.provider.bucket, "Key": self._id_to_key(id)}
if filename:
params["ResponseContentDisposition"] = f"attachment; filename={filename}"
return self.provider.client.generate_presigned_url(
client_method,
Params={"Bucket": self.provider.bucket, "Key": self._id_to_key(id)},
ExpiresIn=DEFAULT_EXPIRY,
client_method, Params=params, ExpiresIn=DEFAULT_EXPIRY
)

def create_download_url(self, id: Id) -> AnyHttpUrl:
return self._create_presigned_url(id, "get_object")
def create_download_url(self, id: Id, filename: str | None = None) -> AnyHttpUrl:
return self._create_presigned_url(id, "get_object", filename)

def create_upload_url(self, id: Id) -> AnyHttpUrl:
return self._create_presigned_url(id, "put_object")
Expand Down
27 changes: 27 additions & 0 deletions integration_tests/test_sync_s3_gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,33 @@ def test_remove_multiple_empty_list(s3_gateway: SyncS3Gateway, s3_bucket):
s3_gateway.remove_multiple([])


def test_create_download_url(s3_gateway: SyncS3Gateway, object_in_s3):
actual = s3_gateway.create_download_url(object_in_s3)

assert object_in_s3 in actual
assert "response-content-disposition=attachment%3B%20filename%3D" not in actual
assert "X-Amz-Expires=3600" in actual
assert "X-Amz-SignedHeaders=host" in actual


def test_create_download_url_with_filename(s3_gateway: SyncS3Gateway, object_in_s3):
actual = s3_gateway.create_download_url(object_in_s3, "file.txt")

assert "file.txt" in actual
assert "response-content-disposition=attachment%3B%20filename%3Dfile.txt" in actual
assert object_in_s3 in actual
assert "X-Amz-Expires=3600" in actual
assert "X-Amz-SignedHeaders=host" in actual


def test_create_upload_url(s3_gateway: SyncS3Gateway, object_in_s3):
actual = s3_gateway.create_upload_url(object_in_s3)

assert object_in_s3 in actual
assert "X-Amz-Expires=3600" in actual
assert "X-Amz-SignedHeaders=host" in actual


def test_remove_filtered_all(s3_gateway: SyncS3Gateway, multiple_objects):
s3_gateway.remove_filtered([])

Expand Down
62 changes: 62 additions & 0 deletions tests/s3/test_sync_s3_gateway.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from unittest.mock import Mock

import pytest

from clean_python.s3 import SyncS3BucketProvider
from clean_python.s3 import SyncS3Gateway
from clean_python.s3.sync_s3_gateway import DEFAULT_EXPIRY


@pytest.fixture
def provider() -> SyncS3BucketProvider:
return Mock(SyncS3BucketProvider, bucket="S3Bucket")


@pytest.fixture
def gateway(provider: Mock) -> SyncS3Gateway:
return SyncS3Gateway(provider)


def test_create_download_url(gateway: SyncS3Gateway, provider: Mock):
provider.client.generate_presigned_url.return_value = (
"https://s3.amazonaws.com/object_in_s3"
)

actual = gateway.create_download_url("object-in-s3")

assert actual == provider.client.generate_presigned_url.return_value
provider.client.generate_presigned_url.assert_called_once_with(
"get_object",
Params={"Bucket": "S3Bucket", "Key": "object-in-s3"},
ExpiresIn=DEFAULT_EXPIRY,
)


def test_create_download_url_with_filename(gateway: SyncS3Gateway, provider: Mock):
provider.client.generate_presigned_url.return_value = "https://s3.com/S3Object"

actual = gateway.create_download_url("S3Object", "file.txt")

assert actual == provider.client.generate_presigned_url.return_value
provider.client.generate_presigned_url.assert_called_once_with(
"get_object",
Params={
"Bucket": "S3Bucket",
"Key": "S3Object",
"ResponseContentDisposition": "attachment; filename=file.txt",
},
ExpiresIn=DEFAULT_EXPIRY,
)


def test_create_upload_url(gateway: SyncS3Gateway, provider: Mock):
provider.client.generate_presigned_url.return_value = "https://s3.com/S3Object"

actual = gateway.create_upload_url("S3Object")

assert actual == provider.client.generate_presigned_url.return_value
provider.client.generate_presigned_url.assert_called_once_with(
"put_object",
Params={"Bucket": "S3Bucket", "Key": "S3Object"},
ExpiresIn=DEFAULT_EXPIRY,
)

0 comments on commit fba464e

Please sign in to comment.