Skip to content

Commit

Permalink
feat: add tests, utils and refactor resizer class
Browse files Browse the repository at this point in the history
  • Loading branch information
montoyaobeso committed May 9, 2024
1 parent c3b52f9 commit aaa3f6e
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 20 deletions.
2 changes: 2 additions & 0 deletions src/app/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALLOWED_INPUT_FORMATS = ["png", "jpeg"]
ALLOWED_CONTENT_TYPE = ["image/png", "image/jpeg"]
23 changes: 22 additions & 1 deletion src/app/image_resizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@


class ImageResizer:
"""Generate the resized image output."""

def __init__(
self,
image: PngImageFile,
Expand All @@ -20,9 +22,15 @@ def __init__(
self.output_image = None

def get_original_image_size(self):
"""Get the original input image
Returns:
tuple: (width, height).
"""
return self.image_width, self.image_height

def resize(self):
"""Resize image and store it internally"""
self.output_image = self.image.resize(
(
int(self.target_width),
Expand All @@ -31,11 +39,24 @@ def resize(self):
)

def get_output_image(self):
"""Get the resized image.
Returns:
bytes: Resized image.
"""
if self.output_image is None:
self.output_image = self.resize()
self.resize()
return self.output_image

def get_output_buffer(self):
"""Return a stremeable buffer for response purposes.
Returns:
io.Bytes(): Resized image buffer.
"""
print(self.output_image)
if self.output_image is None:
self.resize()
self.image_buffer = io.BytesIO()
self.output_image.convert("RGB").save(
self.image_buffer, self.output_format.upper()
Expand Down
25 changes: 7 additions & 18 deletions src/app/main.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import io
from typing import Annotated, Literal

from fastapi import FastAPI, File, Form, Response, UploadFile, status
from fastapi import FastAPI, File, Form, UploadFile, status
from fastapi.responses import JSONResponse, StreamingResponse
from mangum import Mangum
from PIL import Image

from src.app.image_resizer import ImageResizer
from src.app.utils import validate_content_type

app = FastAPI(
title="Thumbnail Generator",
Expand Down Expand Up @@ -58,27 +59,15 @@ async def get_thumbnail(
File: IOBytes of the resized image, default format is PNG.
"""
# Validate input file type
if file.content_type not in ["image/png", "image/jpeg"]:
return Response(
status_code=status.HTTP_400_BAD_REQUEST,
content=f"Supported formats: [.png, .jpeg]; provided '{file.filename}'.",
)
validate_content_type(file.filename, file.content_type)

# Read image data
image_data = await file.read()

image = ImageResizer(
image=Image.open(io.BytesIO(image_data)),
# Get image resizer
image_buffer = ImageResizer(
image=Image.open(io.BytesIO(await file.read())),
target_width=width,
target_height=height,
output_format=output_format,
)

# Perfrom resizing
image.resize()

# Get output buffer
image_buffer = image.get_output_buffer()
).get_output_buffer()

# Send image as response
return StreamingResponse(image_buffer, media_type=f"image/{output_format.lower()}")
Expand Down
11 changes: 11 additions & 0 deletions src/app/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from src.app.constants import ALLOWED_CONTENT_TYPE, ALLOWED_INPUT_FORMATS
from fastapi.exceptions import HTTPException
from fastapi import status


def validate_content_type(filename: str, content_type: str) -> bool:
if content_type not in ALLOWED_CONTENT_TYPE:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Supported formats: {ALLOWED_INPUT_FORMATS}; provided '{filename}'.",
)
1 change: 0 additions & 1 deletion tests/image_resizer_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ def test_resize_image(self):
target_height=128,
output_format="png",
)
image.resize()

# Act
result = image.get_output_image()
Expand Down
26 changes: 26 additions & 0 deletions tests/validate_content_type_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from unittest import TestCase
from src.app.utils import validate_content_type
from fastapi.exceptions import HTTPException


class TestValidateContentType(TestCase):
def test_validate_content_type_as_valid(self):
# Arrange
content_type = "image/png"

# Act
result = validate_content_type("fake.file", content_type)

# Assert
self.assertIsNone(result)

def test_validate_content_type_as_invalid(self):
# Arrange
content_type = "image/invalid"

# Act
with self.assertRaises(Exception) as context:
validate_content_type("fake.file", content_type)

# Assert
self.assertIsInstance(context.exception, HTTPException)

0 comments on commit aaa3f6e

Please sign in to comment.