Skip to content
Open
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
2 changes: 2 additions & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ def get_config(self) -> Config:
cfg.AWS_RESULT_STORAGE_BUCKET_NAME = "test-bucket-rs"
cfg.AWS_RESULT_STORAGE_S3_ENDPOINT_URL = "https://localhost:4566"
cfg.AWS_RESULT_STORAGE_ROOT_PATH = "/test-rs"
cfg.AWS_RESULT_STORAGE_TAGGING = True
cfg.STORES_CRYPTO_KEY_FOR_EACH_IMAGE = True

# Loader Config
Expand Down Expand Up @@ -98,6 +99,7 @@ def get_compatibility_config(self) -> Config:
# Result Storage Config
cfg.TC_AWS_RESULT_STORAGE_BUCKET = "test-bucket-compat-rs"
cfg.TC_AWS_RESULT_STORAGE_ROOT_PATH = "/test-compat-rs"
cfg.AWS_RESULT_STORAGE_TAGGING = True
cfg.STORES_CRYPTO_KEY_FOR_EACH_IMAGE = True

# Loader Config
Expand Down
29 changes: 23 additions & 6 deletions tests/test_result_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

from tests import BaseS3TestCase
from thumbor_aws.result_storage import Storage as ResultStorage

from thumbor_aws.utils import normalize_path

@pytest.mark.usefixtures("test_images")
class ResultStorageTestCase(BaseS3TestCase):
Expand All @@ -45,7 +45,7 @@ async def test_can_put_file_in_s3_with_webp(self):
"""
await self.ensure_bucket()
filepath = f"test/can_put_file_{uuid4()}"
self.context.request = Mock(url=filepath)
self.context.request = Mock(url=filepath, image_url=filepath)
storage = ResultStorage(self.context)
expected = self.test_images["default"]

Expand All @@ -71,7 +71,7 @@ async def test_can_put_file_in_s3_without_webp(self):
filepath = f"test/can_put_file_{uuid4()}"

context_without_webp = self.get_context()
context_without_webp.request = Mock(url=filepath)
context_without_webp.request = Mock(url=filepath, image_url=filepath)
context_without_webp.request.accepts_webp = False
storage = ResultStorage(context_without_webp)
expected = self.test_images["default"]
Expand All @@ -96,7 +96,7 @@ async def test_can_get_result_in_s3(self):
"""
await self.ensure_bucket()
filepath = f"/test/can_put_file_{uuid4()}"
self.context.request = Mock(url=filepath)
self.context.request = Mock(url=filepath, image_url=filepath)
storage = ResultStorage(self.context)
expected = self.test_images["default"]
await storage.put(expected)
Expand All @@ -117,7 +117,7 @@ async def test_can_get_result_in_s3_for_invalid_file(self):
"""
await self.ensure_bucket()
filepath = f"/test/can_put_file_{uuid4()}"
self.context.request = Mock(url=filepath)
self.context.request = Mock(url=filepath, image_url=filepath)
storage = ResultStorage(self.context)

data = await storage.get()
Expand All @@ -132,7 +132,7 @@ async def test_can_check_deprecated_last_updated_method(self):
"""
await self.ensure_bucket()
filepath = f"/test/can_put_file_{uuid4()}"
self.context.request = Mock(url=filepath)
self.context.request = Mock(url=filepath, image_url=filepath)
storage = ResultStorage(self.context)
expected = self.test_images["default"]
await storage.put(expected)
Expand All @@ -141,6 +141,23 @@ async def test_can_check_deprecated_last_updated_method(self):

expect(last_updated).not_to_be_null()

@gen_test
async def test_has_set_object_tags(self):
"""
Verifies that an image has the
AWS_RESULT_STORAGE_ORIGINAL tag
"""
await self.ensure_bucket()
filepath = f"/test/can_put_file_{uuid4()}"
self.context.request = Mock(url=filepath, image_url=filepath)
storage = ResultStorage(self.context)
expected = self.test_images["default"]
await storage.put(expected)

async with storage.get_client() as client:
fileabspath = normalize_path(storage.context, storage.prefix, filepath)
response = await client.get_object_tagging(Bucket=storage.bucket_name, Key=fileabspath)
assert any(item["Key"] == "AWS_RESULT_STORAGE_ORIGINAL" and item["Value"] == filepath for item in response["TagSet"])

@pytest.mark.usefixtures("test_images")
class ResultStorageCompatibilityTestCase(ResultStorageTestCase):
Expand Down
11 changes: 11 additions & 0 deletions thumbor_aws/result_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,13 @@
"AWS Result Storage",
)

Config.define(
"AWS_RESULT_STORAGE_TAGGING",
False,
"If Result Storage Objects should be tagged with Original Filename",
"AWS Result Storage",
)


class Storage(BaseStorage, S3Client):
def __init__(self, context):
Expand Down Expand Up @@ -138,11 +145,15 @@ async def put(self, image_bytes: bytes) -> str:
file_abspath = normalize_path(self.context, self.prefix, self.context.request.url)
logger.debug("[RESULT_STORAGE] putting at %s", file_abspath)
content_type = BaseEngine.get_mimetype(image_bytes)
tags = None
if self.context.config.AWS_RESULT_STORAGE_TAGGING:
tags = {"AWS_RESULT_STORAGE_ORIGINAL": self.context.request.image_url}
response = await self.upload(
file_abspath,
image_bytes,
content_type,
self.context.config.AWS_DEFAULT_LOCATION,
tags=tags,
)
logger.info(
"[RESULT_STORAGE] Image uploaded successfully to %s", file_abspath
Expand Down
3 changes: 3 additions & 0 deletions thumbor_aws/s3_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ async def upload(
data: bytes,
content_type,
default_location,
tags: dict[str, str] | None = None,
) -> str:
"""Uploads a File to S3"""

Expand All @@ -127,6 +128,8 @@ async def upload(
}
if self.file_acl is not None:
settings["ACL"] = self.file_acl
if tags is not None:
settings["Tagging"] = "&".join(f"{key}={value}" for key, value in tags.items())

response = await client.put_object(**settings)
except Exception as error:
Expand Down