[picture-service] - Need To Implement An Endpoint At /api/v1/pictures That Accepts Picture File Uploads
Overview
In this article, we will explore the implementation of a picture file upload endpoint using the FastAPI framework. The endpoint will be responsible for accepting picture file uploads, storing them in a designated configured storage backend, and returning a response with the uploaded file's metadata.
Details and Assumptions
- The microservice utilizes the FastAPI framework.
- The target endpoint is POST /api/v1/pictures.
- The endpoint expects file uploads using multipart/form-data. The file part name is assumed to be standard (e.g., 'file').
- The endpoint internally uses the abstract FileStorageService base class provided by the shared library (cba-core-lib). The actual implementation (e.g., MinioService, AwsService) used at runtime is determined by configuration.
- The specific storage backend provider (e.g., 'minio', 'aws') is configured via an environment variable (e.g., FILE_STORAGE_PROVIDER).
- The shared library (cba-core-lib) must be installed with the appropriate optional extras (e.g., [storage]) corresponding to the chosen provider in this microservice's environment.
- The microservice's configuration provides the necessary provider-specific configuration object (e.g., MinioConfig, AwsConfig) required by the selected FileStorageService implementation.
- The endpoint receives a FastAPI UploadFile object. It is responsible for reading its content and creating a FileUploadData object (as defined in the shared library) to pass to the FileStorageService.upload_file method.
- The specific storage bucket name, currently hardcoded as "test", will be changed to the user's name later.
- Basic validation occurs within the endpoint (e.g., checking if a file was provided). More advanced validation (file type, size limits) may be added separately.
- A successful upload should result in an HTTP 201 Created status code.
- The response body for a successful upload is defined by a Pydantic response model and contains information returned by the selected FileStorageService implementation (typically object_name, etag, size).
- Errors during the process (missing file, underlying storage service failure reported via FileStorageError) result in appropriate HTTP error codes (e.g., 400, 500, 503).
Acceptance Criteria
The following scenarios outline the expected behavior of the picture file upload endpoint:
Scenario 1: Successful Upload
- Given the FastAPI application is running
- And the "/api/v1/pictures" POST endpoint is available
- And the application environment is configured with FILE_STORAGE_PROVIDER set to "
" - And the application has valid configuration for the "
" service (e.g., credentials, endpoint, bucket name) - And the underlying "
" storage system (e.g., MinIO cluster, AWS S3) is operational - When a client sends a POST request to "/api/v1/pictures" with a valid picture file in the 'file' field
- Then the API should instantiate or retrieve the correct "
" implementation of "FileStorageService" - And the API should read the file and create a valid "FileUploadData" object
- And the API should call the "FileStorageService.upload_file" method with the "FileUploadData" and the configured bucket name for "
" - And the API should return an HTTP status code of "201 Created"
- And the API response body should be a JSON object containing the 'object_name', 'etag', and 'size' returned by the service
- And the picture file should exist in the configured "
" bucket under the returned 'object_name'
Scenario 2: Missing File
- Given the FastAPI application is running
- And the "/api/v1/pictures" POST endpoint is available
- When a client sends a POST request to "/api/v1/pictures" without including a file in the multipart/form-data payload
- Then the API should return an HTTP status code of "400 Bad Request"
- And the API response body should indicate that a file is required
Scenario 3: Storage Service Error
- Given the FastAPI application is running
- And the "/api/v1/pictures" POST endpoint is available
- And the application environment is configured with a specific FILE_STORAGE_PROVIDER (e.g., "minio")
- And the underlying configured FileStorageService implementation encounters an error during the upload process (e.g., storage unavailable, config issue caught by service) raising a "FileStorageError"
- When a client sends a POST request to "/api/v1/pictures" with a valid picture file
- Then the API should attempt the upload via the configured "FileStorageService"
- And the API should return an appropriate HTTP error status code (e.g., "500 Internal Server Error" or "503 Service Unavailable") based on the "FileStorageError"
- And the API response body should indicate a storage service error occurred
- And the file should not be reliably stored in the backend storage
Scenario 4: Empty Filename
- Given the FastAPI application is running
- And the "/api/v1/pictures" POST endpoint is available
- And the configured "FileStorageService" implementation requires a non-empty filename in "FileUploadData" (e.g., for extension detection)
- When a client sends a POST request to "/api/v1/pictures" with a file that has an empty filename
- Then the API should attempt to process the file and call the "FileStorageService"
- And the API should return an HTTP status code of "400 Bad Request" (reflecting the ValueError from the service)
- And the API response body should indicate an issue with the provided filename
Implementation
To implement the picture file upload endpoint, we will use the FastAPI framework and the abstract FileStorageService base class provided by the shared library (cba-core-lib). We will also use the Pydantic library to define the response model for a successful upload.
from fastapi import FastAPI, UploadFile, File
from pydantic import BaseModel
from cba_core_lib import FileStorageService, FileUploadData
app = FastAPI()
class FileUploadResponse(BaseModel):
object_name: str
etag: str
size: int
@app.post("/api/v1/pictures")
async def upload_picture(file: UploadFile = File(...)):
# Read the file content
file_content = await file.read()
# Create a FileUploadData object
file_upload_data = FileUploadData(file.filename, file_content)
# Get the configured FileStorageService implementation
file_storage_service = FileStorageService.get_instance()
# Call the upload_file method
try:
# Upload the file
result = file_storage_service.upload_file(file_upload_data, "test")
# Return the response
return FileUploadResponse(**result)
except FileStorageError as e:
# Return an error response
return {"error": str(e)}
except Exception as e:
# Return an internal server error response
return {"error": "Internal Server Error"}
Configuration
To configure the picture file upload endpoint, we need to set the FILE_STORAGE_PROVIDER environment variable to the desired storage backend provider (e.g., "minio" or "aws"). We also need to install the shared library (cba-core-lib) with the appropriate optional extras (e.g., [storage]) corresponding to the chosen provider.
export FILE_STORAGE_PROVIDER=minio
pip install cba-core-lib[storage]
Testing
To test the picture file upload endpoint, we can use a tool like curl to send a POST request with a valid picture file. We can also use a testing framework like Pytest to write unit tests for the endpoint.
curl -X POST \
http://localhost:8000/api/v1/pictures \
-H 'Content-Type: multipart/form-data' \
-F 'file=@/path/to/picture.jpg'
Conclusion
Frequently Asked Questions
In this article, we will answer some frequently asked questions about implementing a picture file upload endpoint with FastAPI.
Q: What is the purpose of the picture file upload endpoint?
A: The picture file upload endpoint is designed to accept picture file uploads from client applications and store them in a designated configured storage backend.
Q: What is the expected behavior of the endpoint?
A: The endpoint is expected to:
- Accept picture file uploads using multipart/form-data
- Store the uploaded files in the configured storage backend
- Return a response with the uploaded file's metadata (e.g., object_name, etag, size)
- Handle errors during the upload process (e.g., missing file, storage service failure)
Q: What is the role of the FileStorageService in the endpoint?
A: The FileStorageService is an abstract base class that provides a common interface for different storage backend providers (e.g., MinIO, AWS S3). The endpoint uses the FileStorageService to interact with the storage backend and perform file uploads.
Q: How is the FileStorageService instance obtained?
A: The FileStorageService instance is obtained using the get_instance()
method, which returns an instance of the configured storage backend provider.
Q: What is the purpose of the FileUploadData object?
A: The FileUploadData object is used to represent the uploaded file and its metadata. It contains the file's name, content, and other relevant information.
Q: How is the file uploaded to the storage backend?
A: The file is uploaded to the storage backend using the upload_file()
method of the FileStorageService instance. This method takes the FileUploadData object and the configured bucket name as arguments.
Q: What is the expected response from the endpoint?
A: The expected response from the endpoint is a JSON object containing the uploaded file's metadata (e.g., object_name, etag, size).
Q: How does the endpoint handle errors during the upload process?
A: The endpoint handles errors during the upload process by catching exceptions raised by the FileStorageService instance. It returns an error response with a suitable HTTP status code (e.g., 400, 500, 503).
Q: Can the endpoint be configured to use different storage backend providers?
A: Yes, the endpoint can be configured to use different storage backend providers by setting the FILE_STORAGE_PROVIDER environment variable to the desired provider (e.g., "minio" or "aws").
Q: How can the endpoint be tested?
A: The endpoint can be tested using a tool like curl to send a POST request with a valid picture file. It can also be tested using a testing framework like Pytest to write unit tests for the endpoint.
Q: What is the benefit of using the FileStorageService abstract base class?
A: The FileStorageService abstract base class provides a common interface for different storage backend providers, making it easier to switch between providers or add new ones in the future.
Q: Can the endpoint be used with other file types besides pictures?
A: Yes, the endpoint can be used with other file types besides pictures. However, the FileStorageService instance may need to be configured to handle file types.
Q: How can the endpoint be secured?
A: The endpoint can be secured by implementing authentication and authorization mechanisms, such as OAuth or JWT, to ensure that only authorized clients can upload files.
Q: Can the endpoint be used with a load balancer or proxy server?
A: Yes, the endpoint can be used with a load balancer or proxy server. However, the endpoint may need to be configured to handle requests from the load balancer or proxy server.
Q: How can the endpoint be monitored and logged?
A: The endpoint can be monitored and logged using tools like Prometheus, Grafana, or ELK Stack. The endpoint can also be configured to log errors and other relevant information.