ImagesAPI is a RESTful API that allows users to upload and manage their images. The API offers three built-in account tiers: Basic, Premium, and Enterprise, each with varying features and benefits. In addition, admins can create arbitrary tiers with customizable thumbnail sizes and the presence of links to the originally uploaded file and the ability to generate expiring links.
- I decided to resize images on go, because resizing images is not very computationally intensive. I also used PilImage.NEAREST - the quickest PIL resizing algorithm (drawback - returns worse quality images) - i'm assuming, that thumbnail will just be a preview for original image.
- in production I'd use some form of caching (either using caching service e.g Cloudflare, or use own caching backend), to cache most-accessed images (depends on client target)
- in production I'd store images at cloud object storage e.g Amazon S3 or self-hosted storage
- README was supposed to be short, but that was a perfect opportunity to practice creating docs
- my next task would be to replace the default Django TokenAuthentication with Django REST Knox, because according to DRF documentation, Django REST Knox offers a more secure implementation that allows generating multiple tokens per user, as well as token expiration.
https://imagesapi.onrender.com/api/
- email: test@test.com
- password: N52f#He!hf2&QG9DTe17ziQYg
SECRET_KEY: Django secret keyDEBUG: Debug mode flagDB_NAME: Database nameDB_USER: Database usernameDB_PASSWORD: Database passwordDB_HOST: Database hostDB_PORT: Database portALLOWED_HOSTS: List of allowed hosts
docker-compose build
docker-compose run imagesapi python generate_fixtures.py
docker-compose up
docker-compose run imagesapi python -m coverage run --source='.' manage.py test && python -m coverage html
current code coverage: 100%, however tests need to be expanded into more cases, and some repetition of code should be considered whether they should be moved into functions or not.
| Endpoint | Method | Description | Payload | Params |
|---|---|---|---|---|
| /api/images/ | GET | List available images | expiration_time:int |
|
| /api/images/<pk>/generate_image_variants/ | GET | Get links to images with different sizes. | ||
| /api/images/ | POST | Create a new image. | {"name": file name, "file": image file} |
|
| /api/images/ | PUT | Update an existing image. | {"id": id, "name": file name, "file": image file} |
|
| /api/images/ | DELETE | Delete an existing image. | {"id": id} |
|
| /api/images/<pk>/ | GET | Show an image file | height:int, expires_at:int, signature:str |
This endpoint allows you to list available images, and get urls to these images in available for user formats.
- Method:
GET - Payload: None
- Params: None
| Status code | content-type | response |
|---|---|---|
| 200 | application/json | {"id":id, "name":filename, "url": url for image generation, "created_at": time} |
| 400 | application/json | {"code":"400","error":"Bad Request"} |
| 401 | application/json | {"code":"400","detail": "Authentication credentials were not provided."} |
This endpoint allows you to retrieve links to image thumbnails, and original image / expiring binary image based on AccountTier.
- Method:
GET - Payload: None
- Params: expiration_time[optional, int]
| Status code | content-type | response |
|---|---|---|
| 200 | application/json | {"thumbnail_urls": Object with keys representing different image sizes eg.(size_200:url}) and values representing the corresponding thumbnail URL.,"original_image": String representing the URL of the original image.,"expiring_binary_url": String representing the URL of the expiring binary image.} |
| 400 | application/json | {"code":"400","error":"Bad Request"} |
| 401 | application/json | {"code":"400","detail": "Authentication credentials were not provided."} |
| 404 | application/json | {"code":"404","detail":"Not found."} |
This endpoint allows you to upload image.
- Method:
POST - Payload: {"name":
filename, "file":image file} - Params: None
| Status code | content-type | response |
|---|---|---|
| 201 | application/json | {"id":id, "name":filename, "url": url for image generation, "created_at": time} |
| 400 | application/json | {"code":"400","error":"Bad Request"} |
| 401 | application/json | {"code":"401","detail": "Authentication credentials were not provided."} |
This endpoint allows you to update an image.
- Method:
PUT - Payload: optional one of: {"name":
filename, "file":image file} - Params: None
| Status code | content-type | response |
|---|---|---|
| 201 | application/json | {"id":id, "name":filename, "url": url for image generation, "created_at": time} |
| 400 | application/json | {"code":"400","error":"Bad Request"} |
| 401 | application/json | {"code":"401","detail": "Authentication credentials were not provided."} |
| 404 | application/json | {"code":"404","detail":"Not found."} |
This endpoint allows you to remove uploaded image.
- Method:
DELETE - Payload: None
- Params: None
| Status code | content-type | response |
|---|---|---|
| 204 | application/json | {"code": "204", "detail": "No Content"} |
| 400 | application/json | {"code":"400","error":"Bad Request"} |
| 401 | application/json | {"code":"401","detail": "Authentication credentials were not provided."} |
| 404 | application/json | {"code":"404","detail":"Not found."} |
This endpoint allows you to show formatted uploaded image.
- Method:
GET - Payload: None
- Params: signature, height, expires_at
| Status code | content-type | response |
|---|---|---|
| 200 | image/<img-format> | image file |
| 400 | application/json | {"error":"Bad Request"} |
| 401 | application/json | {"detail": "Authentication credentials were not provided."} |
| 410 | application/json | {"error":"Image expired."} |