Welcome to the Movie Theater API project! This educational assignment is designed to help you develop and refine your skills in creating robust web applications using FastAPI, SQLAlchemy, and Docker. Here's what the project offers:
- Movie Theater API Project
- Features
- Root Directory
- Commands
- Docker Configuration
- Source Directory (src)
- Additional Features
- Tip: Using Dependency Injection in FastAPI
- Using
get_dbfor Database Dependency Injection - Extended Dependency Injection in FastAPI
- Handling Authentication via Authorization Headers
- Summary
- Primary Services (docker-compose.yml)
- Testing Services (docker-compose-tests.yml)
- Volumes
- Networks
- Summary (again)
- How to Run the Project
- 1. Clone the Repository
- 2. Create and Activate a Virtual Environment
- 3. Install Dependencies with Poetry
- 4. Create a
.envFile - 5. Run the Project with Docker Compose
- 6. Access the Services
- 7. Verify Setup
- 8. Running the Development Server without Docker
- 9. Running End-to-End (E2E) Tests
- 10. Running Tests Locally
- Run project with PyCharm
- Models and Entities Overview
- Task Description: Extending the Cinema Application
- Tasks: Implementing Email Notifications for User Registration and Password Reset
- Task: Implement User Profile Creation and Validation Schema
- PostgreSQL for development: The application uses PostgreSQL as the main database for the development environment, configured via Docker Compose.
- SQLite for testing: A lightweight SQLite database is utilized for testing, ensuring fast and isolated test execution.
- The database can be automatically populated with movie data from a provided dataset. This includes associated entities such as genres, actors, languages, and countries, ensuring a rich and interconnected data structure.
- The project is fully Dockerized, allowing seamless setup and execution of the application and its dependencies.
- Docker Compose simplifies the orchestration of services like the FastAPI application, PostgreSQL database, and other required components.
- MailHog for email testing: The project includes MailHog, which captures outgoing emails and provides a web interface for viewing test emails. This is useful for testing user registration, password reset, and account activation flows.
- MinIO for object storage: The project integrates MinIO, an S3-compatible storage solution used for handling file uploads, such as user avatars.
- Separate Docker Compose files:
docker-compose.yml: The main configuration for development, including FastAPI, PostgreSQL, MailHog, and MinIO.docker-compose-test.yml: A separate configuration optimized for end-to-end (E2E) testing, using SQLite as the test database.
A well-organized and modular project structure is provided, including:
- Database models and schemas for movies and related entities.
- Routing logic for managing API endpoints.
- Utility scripts for tasks like data seeding and database migrations.
- Testing framework integrated with
pytest, including unit and E2E tests for API functionality.
This setup ensures a scalable, maintainable, and testable API that adheres to best practices in web development.
The Movie Theater API project maintains a structured and modular organization, making development, testing, and deployment more efficient. Below is a detailed breakdown of the project's structure:
.
├── Dockerfile
├── README.md
├── README_NEW.md
├── alembic.ini
├── commands
│ ├── run_migration.sh
│ ├── run_web_server_dev.sh
│ ├── setup_mailhog_auth.sh
│ └── setup_minio.sh
├── docker
│ ├── mailhog
│ │ └── Dockerfile
│ ├── minio_mc
│ │ └── Dockerfile
│ └── tests
│ └── Dockerfile
├── docker-compose.yml
├── docker-compose-tests.yml
├── init.sql
├── poetry.lock
├── pyproject.toml
├── pytest.ini
└── src
├── __pycache__
├── config
│ ├── __init__.py
│ ├── dependencies.py
│ └── settings.py
├── database
│ ├── __init__.py
│ ├── migrations
│ │ ├── README
│ │ ├── env.py
│ │ ├── script.py.mako
│ │ └── versions
│ │ ├── 2da0dc469be8_temp_migration.py
│ │ ├── 32b1054a69e3_initial_migration.py
│ │ └── 41cdafa531cf_temp_migration.py
│ ├── models
│ │ ├── __init__.py
│ │ ├── accounts.py
│ │ ├── base.py
│ │ └── movies.py
│ ├── populate.py
│ ├── seed_data
│ │ ├── imdb_movies.csv
│ │ └── test_data.csv
│ ├── session_postgresql.py
│ ├── session_sqlite.py
│ ├── source
│ │ └── theater.db
│ └── validators
│ ├── __init__.py
│ └── accounts.py
├── exceptions
│ ├── __init__.py
│ ├── email.py
│ ├── security.py
│ └── storage.py
├── main.py
├── notifications
│ ├── __init__.py
│ ├── emails.py
│ ├── interfaces.py
│ └── templates
│ ├── activation_complete.html
│ ├── activation_request.html
│ ├── password_reset_complete.html
│ └── password_reset_request.html
├── routes
│ ├── __init__.py
│ ├── accounts.py
│ ├── movies.py
│ └── profiles.py
├── schemas
│ ├── __init__.py
│ ├── accounts.py
│ ├── examples
│ │ ├── __init__.py
│ │ └── movies.py
│ ├── movies.py
│ └── profiles.py
├── security
│ ├── __init__.py
│ ├── http.py
│ ├── interfaces.py
│ ├── passwords.py
│ ├── token_manager.py
│ └── utils.py
├── storages
│ ├── __init__.py
│ ├── interfaces.py
│ └── s3.py
├── tests
│ ├── __init__.py
│ ├── conftest.py
│ ├── doubles
│ │ ├── __init__.py
│ │ ├── fakes
│ │ │ ├── __init__.py
│ │ │ └── storage.py
│ │ └── stubs
│ │ ├── __init__.py
│ │ └── emails.py
│ ├── test_e2e
│ │ ├── __init__.py
│ │ ├── test_email_notification.py
│ │ └── test_storage.py
│ ├── test_integration
│ │ ├── __init__.py
│ │ ├── test_accounts.py
│ │ ├── test_movies.py
│ │ └── test_profiles.py
└── validation
├── __init__.py
└── profile.py
README.MD: Main project documentation.README_NEW.MD: Updated version of the project documentation.docker-compose.yml: Configures the main application services using PostgreSQL, FastAPI, and MinIO.docker-compose-tests.yml: Configures services specifically for end-to-end (e2e) testing, including SQLite, MailHog, and MinIO.poetry.lock&pyproject.toml: Poetry-based dependency management.pytest.ini: Configuration for running tests.
run_migration.sh: Script to execute database migrations using Alembic.run_web_server_dev.sh: Script to start the FastAPI development server.setup_mailhog_auth.sh: Initializes authentication for the MailHog testing environment.setup_minio.sh: Configures MinIO client (mc) to work with the application.
The docker directory contains Dockerfiles for different services:
docker/mailhog/Dockerfile: Builds a custom MailHog container for email testing.docker/minio_mc/Dockerfile: Builds a MinIO client (mc) container for interacting with MinIO storage.docker/tests/Dockerfile: Builds a container for executing end-to-end (e2e) tests.
dependencies.py: Defines FastAPI dependencies for dependency injection.settings.py: Manages project settings, including database configurations and external service settings.
models/: Defines SQLAlchemy ORM models for Users, Movies, and related entities.migrations/: Contains Alembic migration scripts.populate.py: Seeds the database with initial movie data.session_postgresql.py: PostgreSQL session for development.session_sqlite.py: SQLite session for testing.
email.py: Custom exceptions related to email notifications.security.py: Handles security-related exceptions.storage.py: Manages MinIO/S3-related exceptions.
emails.py: Implements the EmailSender interface for sending email notifications.interfaces.py: Defines an interface for email-related operations.templates/: Stores HTML email templates:activation_request.htmlactivation_complete.htmlpassword_reset_request.htmlpassword_reset_complete.html
accounts.py: API endpoints for user authentication and account management.movies.py: API endpoints for movies and related information.profiles.py: API endpoints for user profile management.
accounts.py: Defines schemas for user-related requests and responses.movies.py: Defines schemas for movie-related API responses.profiles.py: Defines schemas for user profile-related API operations.
http.py: Handles HTTP security-related logic.interfaces.py: Defines interfaces for authentication and authorization services.passwords.py: Implements password hashing and verification.token_manager.py: Manages JWT authentication tokens.utils.py: Security utility functions.
s3.py: Implements MinIO/S3 storage client for handling file uploads.interfaces.py: Defines an interface for interacting with S3-compatible storage.
conftest.py: Defines shared test fixtures.doubles/: Contains test doubles (fakes, stubs, and mocks).fakes/storage.py: Fake MinIO storage implementation for unit testing.stubs/emails.py: Stub implementation for email notifications.
test_e2e/: End-to-End (E2E) tests for API endpoints.test_email_notification.py: Tests email notifications using MailHog.test_storage.py: Tests S3 file upload functionality with MinIO.
test_integration/: Integration tests for individual components.test_accounts.py: Tests user authentication and account creation.test_movies.py: Tests movie API operations.test_profiles.py: Tests user profile creation and management.
profile.py: Implements input validation for user profile creation.
-
MinIO Integration:
- MinIO is used as an S3-compatible object storage for storing profile avatars.
- Configured via Docker and managed using the MinIO client (
mc).
-
MailHog Integration:
- MailHog is used as a mock SMTP server for testing email notifications.
- A separate Docker container ensures email delivery in the testing environment.
-
E2E Testing Setup:
- The
docker-compose-tests.ymlfile orchestrates services for end-to-end tests. - Uses SQLite for an isolated testing database.
- The
FastAPI provides an efficient way to manage dependencies through the Depends function, enabling structured and reusable dependency injection for database sessions, authentication, email notifications, and storage clients.
The get_db function is a generator that provides a SQLAlchemy session for interacting with the database. This ensures proper session management where the session is created before route execution and automatically closed afterward.
from fastapi import Depends, APIRouter
from sqlalchemy.ext.asyncio import AsyncSession
from database import get_db # Import the get_db generator
router = APIRouter()
@router.get("/example")
async def example_route(db: AsyncSession = Depends(get_db)):
"""Use the db session here to interact with the database"""
pass- Simplifies injecting dependencies into route handlers.
- Ensures session lifecycle management (creation and cleanup).
- Promotes better testability by separating database logic from the route handler.
- Can be used across multiple routes for consistency.
Apart from database injection, the project also includes dependencies for settings management, JWT authentication, email notifications, and S3 storage.
import os
from fastapi import Depends
from config.settings import TestingSettings, Settings, BaseAppSettings
def get_settings() -> BaseAppSettings:
environment = os.getenv("ENVIRONMENT", "developing")
if environment == "testing":
return TestingSettings()
return Settings()- Determines whether the app is running in development or testing mode.
- Provides access to relevant configuration settings.
from security.interfaces import JWTAuthManagerInterface
from security.token_manager import JWTAuthManager
def get_jwt_auth_manager(settings: BaseAppSettings = Depends(get_settings)) -> JWTAuthManagerInterface:
return JWTAuthManager(
secret_key_access=settings.SECRET_KEY_ACCESS,
secret_key_refresh=settings.SECRET_KEY_REFRESH,
algorithm=settings.JWT_SIGNING_ALGORITHM
)- Provides JWT authentication management.
- Uses
Depends(get_settings)to dynamically configure JWT parameters.
from notifications import EmailSenderInterface, EmailSender
def get_accounts_email_notificator(settings: BaseAppSettings = Depends(get_settings)) -> EmailSenderInterface:
return EmailSender(
hostname=settings.EMAIL_HOST,
port=settings.EMAIL_PORT,
email=settings.EMAIL_HOST_USER,
password=settings.EMAIL_HOST_PASSWORD,
use_tls=settings.EMAIL_USE_TLS,
template_dir=settings.PATH_TO_EMAIL_TEMPLATES_DIR,
activation_email_template_name=settings.ACTIVATION_EMAIL_TEMPLATE_NAME,
activation_complete_email_template_name=settings.ACTIVATION_COMPLETE_EMAIL_TEMPLATE_NAME,
password_email_template_name=settings.PASSWORD_RESET_TEMPLATE_NAME,
password_complete_email_template_name=settings.PASSWORD_RESET_COMPLETE_TEMPLATE_NAME
)- Sends account activation, password reset, and other notifications.
- Loads email configurations from environment variables.
from storages import S3StorageInterface, S3StorageClient
def get_s3_storage_client(settings: BaseAppSettings = Depends(get_settings)) -> S3StorageInterface:
return S3StorageClient(
endpoint_url=settings.S3_STORAGE_ENDPOINT,
access_key=settings.S3_STORAGE_ACCESS_KEY,
secret_key=settings.S3_STORAGE_SECRET_KEY,
bucket_name=settings.S3_BUCKET_NAME
)- Provides access to MinIO S3 storage for file management.
- Uses settings injected via
Depends(get_settings).
When implementing authentication, it is critical to properly validate the Authorization header to ensure security.
from fastapi import Request, HTTPException, status
def get_token(request: Request) -> str:
"""
Extracts the Bearer token from the Authorization header.
:param request: FastAPI Request object.
:return: Extracted token string.
:raises HTTPException: If Authorization header is missing or invalid.
"""
authorization: str = request.headers.get("Authorization")
if not authorization:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Authorization header is missing"
)
scheme, _, token = authorization.partition(" ")
if scheme.lower() != "bearer" or not token:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid Authorization header format. Expected 'Bearer <token>'"
)
return token- Ensures a valid
Authorizationheader is present. - Validates the
Bearerscheme and ensures a token is provided. - Raises a
401 Unauthorizederror if the format is incorrect.
- Database Connection (
get_db): Injects a session into routes. - Settings (
get_settings): Dynamically loads configurations. - JWT Authentication (
get_jwt_auth_manager): Manages authentication. - Email Notifications (
get_accounts_email_notificator): Sends account-related emails. - S3 Storage (
get_s3_storage_client): Handles file storage via MinIO. - Authentication (
get_token): Extracts and validates JWT tokens from headers.
Using FastAPI’s dependency injection system, these components ensure cleaner, testable, and modular application architecture.
The project is configured with multiple services in both docker-compose.yml and docker-compose-tests.yml, allowing seamless development, testing, and deployment of the Movie Theater API. Below is a breakdown of the services and their roles.
- Image:
postgres:latest - Purpose: Acts as the primary PostgreSQL database for the application.
- Configuration:
- Loads an initial schema from
init.sql. - Uses a persistent Docker volume
postgres_theater_datato store data. - Exposes PostgreSQL on port 5432.
- Loads an initial schema from
- Health Check: Uses
pg_isreadyto ensure the database is ready before dependent services start. - Network: Attached to
theater_network.
- Image:
dpage/pgadmin4 - Purpose: Provides a web interface for managing and monitoring the PostgreSQL database.
- Configuration:
- Exposes pgAdmin on port 3333.
- Uses a persistent volume
pgadmin_theater_datato store settings.
- Dependency: Waits until
dbis healthy before starting. - Network: Attached to
theater_network.
- Build Context: Uses the local directory (
.) as the build source. - Purpose: Runs the FastAPI application, serving API endpoints.
- Configuration:
- Runs the development server via
/commands/run_web_server_dev.sh. - Uses
PYTHONPATHto reference thesrcdirectory. - Enables live file watching (
WATCHFILES_FORCE_POLLING=true). - Exposes the API on port 8000.
- Runs the development server via
- Dependencies:
- Waits for
dbandminioservices to be healthy before starting.
- Waits for
- Network: Attached to
theater_network.
- Build Context: Shares the same build context as
web. - Purpose: Runs Alembic migrations to keep the database schema up-to-date.
- Configuration:
- Uses the
run_migration.shscript to apply migrations. - Mounts the
srcdirectory to access migration scripts.
- Uses the
- Dependency: Starts only after
dbis healthy. - Network: Attached to
theater_network.
- Build Context: Uses a custom MailHog image (Dockerfile located in
docker/mailhog). - Purpose: Provides a local SMTP server for email testing.
- Configuration:
- Runs
setup_mailhog_auth.shto configure authentication. - Exposes MailHog UI on port 8025 and SMTP on port 1025.
- Runs
- Network: Attached to
theater_network.
- Image:
minio/minio:latest - Purpose: Provides an S3-compatible storage for managing file uploads.
- Configuration:
- Runs the MinIO server with console access on port 9001.
- Uses a persistent volume
minio_datato store objects.
- Health Check: Checks MinIO’s
/minio/health/liveendpoint to ensure it's ready. - Network: Attached to
theater_network.
- Build Context: Uses a custom MinIO Client image (Dockerfile in
docker/minio_mc). - Purpose: Configures MinIO storage, creates required buckets, and sets permissions.
- Configuration:
- Runs
setup_minio.shto initialize storage.
- Runs
- Dependency: Waits until
miniois healthy before executing commands. - Network: Attached to
theater_network.
To support end-to-end (E2E) testing, the project includes a separate testing configuration in docker-compose-tests.yml.
- Build Context: Uses a dedicated test Dockerfile (
docker/tests/Dockerfile). - Purpose: Runs Pytest E2E tests.
- Configuration:
- Executes
pytestwith specific flags:pytest -m e2e --maxfail=5 --disable-warnings -v --tb=short
- Uses a dedicated testing environment (
ENVIRONMENT=testing).
- Executes
- Dependencies: Waits for
mailhogandminiobefore running tests. - Network: Uses a separate network (
theater_network_test).
- Image:
mailhog/mailhog - Purpose: Provides an SMTP server for email testing.
- Configuration:
- Exposes MailHog UI on port 8025 and SMTP on port 1025.
- Network: Attached to
theater_network_test.
- Image:
minio/minio:latest - Purpose: Provides S3-compatible storage for E2E tests.
- Configuration:
- Uses
minio-theater-testas the test container name. - Stores test files in a separate volume (
minio_data_test). - Health checks ensure MinIO is ready before tests start.
- Uses
- Network: Attached to
theater_network_test.
- Build Context: Uses the same MinIO Client image as production.
- Purpose: Configures MinIO storage for testing.
- Configuration:
- Runs
setup_minio.shto initialize test storage.
- Runs
- Dependency: Waits for
miniobefore executing. - Network: Attached to
theater_network_test.
| Volume Name | Purpose |
|---|---|
postgres_theater_data |
Stores PostgreSQL database data. |
pgadmin_theater_data |
Stores pgAdmin configuration. |
minio_data |
Stores MinIO objects (production). |
minio_data_test |
Stores MinIO objects (testing). |
| Network Name | Description |
|---|---|
theater_network |
Connects production services. |
theater_network_test |
Connects test services. |
- The production services in
docker-compose.ymlinclude:- PostgreSQL (
db), pgAdmin (pgadmin), FastAPI backend (web), Alembic migrations (migrator), MailHog (mailhog), MinIO storage (minio), and MinIO client (minio_mc).
- PostgreSQL (
- The E2E testing services in
docker-compose-tests.ymlinclude:- A dedicated test backend running pytest, a separate MailHog instance, and MinIO storage for test uploads.
- Separate networks (
theater_networkandtheater_network_test) ensure production and test environments remain isolated.
This modular structure ensures a robust and scalable development workflow, supporting local development, integration testing, and production deployment.
Follow these steps to set up and run the Movie Theater API project on your local machine.
Start by cloning the project repository from GitHub:
git clone <repository-url>
cd <repository-folder>It is recommended to use a virtual environment to isolate project dependencies:
# Create a virtual environment
python -m venv venv
# Activate the virtual environment
# On Windows
venv\Scripts\activate
# On macOS/Linux
source venv/bin/activateThis project uses Poetry for dependency management. Install dependencies as follows:
# Install Poetry if not already installed
pip install poetry
# Install project dependencies
poetry installCreate a .env file in the project root directory with the following variables. Customize the values as needed:
# PostgreSQL
POSTGRES_DB=movies_db
POSTGRES_DB_PORT=5432
POSTGRES_USER=admin
POSTGRES_PASSWORD=some_password
POSTGRES_HOST=postgres_theater
# pgAdmin
PGADMIN_DEFAULT_EMAIL=admin@gmail.com
PGADMIN_DEFAULT_PASSWORD=admin
# JWT keys
SECRET_KEY_ACCESS=838qKq7dGp34hWij3c8txA5ZD2qm9ybt
SECRET_KEY_REFRESH=cFzRk8kllHMW71wQKLXBqDzl24fkhisw
JWT_SIGNING_ALGORITHM=HS256
# MailHog
MAILHOG_USER=admin
MAILHOG_PASSWORD=some_password
# Email settings for MailHog
EMAIL_HOST=mailhog_theater
EMAIL_PORT=1025
EMAIL_HOST_USER=testuser@mate.com
EMAIL_HOST_PASSWORD=test_password
EMAIL_USE_TLS=False
# MinIO (S3-Compatible Storage)
MINIO_ROOT_USER=minioadmin
MINIO_ROOT_PASSWORD=some_password
MINIO_HOST=minio-theater
MINIO_PORT=9000
MINIO_STORAGE=theater-storageThe project is Dockerized for easy setup. To start all the required services (PostgreSQL, pgAdmin, FastAPI app, MailHog, MinIO, and Alembic migrator), run:
docker-compose up --buildNotes:
-
The first run may take some time as the database will be populated with initial data.
-
Logs for services can be viewed using:
docker-compose logs -f
| Service | URL |
|---|---|
| API | http://localhost:8000 |
| pgAdmin | http://localhost:3333 (Use .env credentials) |
| MailHog UI | http://localhost:8025 (SMTP testing) |
| MinIO Console | http://localhost:9001 (S3-compatible storage) |
After all services are running, you can test the API by accessing the OpenAPI documentation:
http://localhost:8000/docs
If you prefer running the application without Docker, follow these steps:
-
Ensure PostgreSQL is running on your system with credentials matching
.env. -
Apply migrations manually:
poetry run alembic upgrade head
-
Start the development server:
poetry run uvicorn src.main:app --host 0.0.0.0 --port 8000 --reload
The project includes E2E tests that validate the system using a separate testing environment. These tests run inside dedicated containers using docker-compose-tests.yml.
-
Start the test environment:
docker-compose -f docker-compose-tests.yml up --build
-
Run E2E tests manually (inside a container):
docker exec -it backend_theater_test pytest -m e2e --maxfail=5 --disable-warnings -v --tb=short -
Stop the test environment:
docker-compose -f docker-compose-tests.yml down -v
For unit and integration tests, use:
pytest -m "unit or integration" --disable-warnings -v| Task | Command |
|---|---|
| Run all services (Dockerized) | docker-compose up --build |
| Run development server manually | poetry run uvicorn src.main:app --reload |
| Apply database migrations | poetry run alembic upgrade head |
| Run E2E tests | docker-compose -f docker-compose-tests.yml up --build |
| Run unit/integration tests | pytest -m "unit or integration" -v |
- Mark the
srcdirectory as root
- Close all open code tabs
- Restart
PyCharm - Create a new Run Configuration for the
FastAPIproject
- Add the required environment variables to the Run Configuration.
- Start the local Docker Compose services:
docker-compose -f docker-compose-local.yml up --build -d
- Wait for the initial database seeding to finish (this may take a moment the first time).

- Run the project via
PyCharmand use breakpoints for debugging - If you need to apply database migrations manually, run:
docker-compose -f docker-compose-local.yml up --build migrator
This structured setup allows easy development, testing, and deployment.
The project defines the following entities and relationships using SQLAlchemy. Each entity represents a table in the database and maps to a specific domain concept in the Movie Theater API.
These models handle user authentication, authorization, and related functionality.
Represents user groups in the application (e.g., USER, MODERATOR, ADMIN).
-
Table Name:
user_groups -
Fields:
id(Primary Key): Unique identifier for each user group.name: Enum value representing the group (UserGroupEnum).
-
Relationships:
users: One-to-many relationship withUserModel.
-
Constraints:
- Unique constraint on
name.
- Unique constraint on
Represents application users.
-
Table Name:
users -
Fields:
id(Primary Key): Unique identifier for each user.email: Email address of the user (unique).hashed_password: Securely stored password hash.is_active: Boolean indicating whether the user account is active.created_at: Timestamp when the user was created.updated_at: Timestamp when the user was last updated.group_id: Foreign key linking to theuser_groupstable.
-
Relationships:
group: Links toUserGroupModel.activation_token: One-to-one relationship withActivationTokenModel.password_reset_token: One-to-one relationship withPasswordResetTokenModel.refresh_tokens: One-to-many relationship withRefreshTokenModel.profile: One-to-one relationship withUserProfileModel.
Represents additional information about a user (optional).
-
Table Name:
user_profiles -
Fields:
id(Primary Key): Unique identifier for each profile.first_name: User's first name.last_name: User's last name.avatar: Path to the user's avatar image.gender: Enum value representing the user's gender (GenderEnum).date_of_birth: User's date of birth.info: Additional information about the user.user_id: Foreign key linking to theuserstable.
-
Relationships:
user: Links toUserModel.
-
Constraints:
- Unique constraint on
user_id.
- Unique constraint on
Abstract base class for all token-based models.
- Fields:
id(Primary Key): Unique identifier for each token.token: Securely generated token value.expires_at: Expiration timestamp for the token.user_id: Foreign key linking to theuserstable.
Represents tokens used for user account activation.
-
Table Name:
activation_tokens -
Fields:
- Inherits all fields from
TokenBaseModel.
- Inherits all fields from
-
Relationships:
user: Links toUserModel.
-
Constraints:
- Unique constraint on
user_id.
- Unique constraint on
Represents tokens used for password reset.
-
Table Name:
password_reset_tokens -
Fields:
- Inherits all fields from
TokenBaseModel.
- Inherits all fields from
-
Relationships:
user: Links toUserModel.
-
Constraints:
- Unique constraint on
user_id.
- Unique constraint on
Represents refresh tokens for user authentication.
-
Table Name:
refresh_tokens -
Fields:
- Inherits all fields from
TokenBaseModel. token: Securely generated token value with an extended length.
- Inherits all fields from
-
Relationships:
user: Links toUserModel.
-
Methods:
create: Factory method to simplify the creation of new refresh tokens.
These models handle movies theater functionality.
Represents a movie in the database.
-
Table Name:
movies -
Fields:
id(Primary Key): Unique identifier for each movie.name: Name of the movie.date: Release date of the movie.score: Movie rating score (e.g., IMDb score).overview: A short description or synopsis of the movie.status: Production status of the movie (e.g., Released, In Production).budget: The budget of the movie (stored as a decimal value).revenue: The revenue generated by the movie.country_id: Foreign key linking to thecountriestable.
-
Relationships:
country: Links to theCountryModel.genres: Many-to-many relationship withGenreModel.actors: Many-to-many relationship withActorModel.languages: Many-to-many relationship withLanguageModel.
-
Constraints:
- Unique constraint on
nameanddateto prevent duplicate entries.
- Unique constraint on
Represents a genre (e.g., Action, Comedy).
-
Table Name:
genres -
Fields:
id(Primary Key): Unique identifier for each genre.name: Name of the genre (e.g., Action, Drama).
-
Relationships:
movies: Many-to-many relationship withMovieModel.
Represents an actor in the database.
-
Table Name:
actors -
Fields:
id(Primary Key): Unique identifier for each actor.name: Name of the actor.
-
Relationships:
movies: Many-to-many relationship withMovieModel.
Represents a country associated with a movie (e.g., production country).
-
Table Name:
countries -
Fields:
id(Primary Key): Unique identifier for each country.code: ISO 3166-1 alpha-3 country code (e.g., USA, FRA).name: Full name of the country.
-
Relationships:
movies: One-to-many relationship withMovieModel.
Represents a language spoken in a movie.
-
Table Name:
languages -
Fields:
id(Primary Key): Unique identifier for each language.name: Name of the language (e.g., English, French).
-
Relationships:
movies: Many-to-many relationship withMovieModel.
Used to establish many-to-many relationships between entities.
-
MoviesGenresModel:- Links
moviesandgenres. - Fields:
movie_id,genre_id.
- Links
-
ActorsMoviesModel:- Links
moviesandactors. - Fields:
movie_id,actor_id.
- Links
-
MoviesLanguagesModel:- Links
moviesandlanguages. - Fields:
movie_id,language_id.
- Links
In this assignment, you are tasked with continuing the development of the cinema application.
The schemas/movies.py and routes/movies.py files have already been provided as examples of implementation and do not require any changes.
Your objective is as follows:
-
Enhancing User Account Functionality
- The
routes/accounts.pyandschemas/accounts.pyfiles already contain the necessary logic for user registration, authentication, and account management. - However, some controllers need to be extended to include email notifications.
- Specifically, email notifications should be sent for:
- Account activation.
- Password reset requests.
- Password reset completions.
- The
-
Implementing User Profile Management
- The
routes/profiles.pyandschemas/profiles.pyfiles require full implementation. - You need to develop the logic for creating a user profile, including:
- Handling user input (first name, last name, gender, date of birth, bio, and avatar image).
- Validating the profile data.
- Uploading the avatar image to MinIO storage.
- Storing the profile details in the database.
- The
Your implementation should follow the structure and design patterns demonstrated in the existing movie-related files while ensuring proper validation, security, and database consistency.
To enhance the user account management functionality, you need to integrate email notifications for key user actions. This will involve modifying the following controllers:
- User Registration (
register_user) - Account Activation (
activate_account) - Password Reset Request (
request_password_reset_token) - Password Reset Completion (
reset_password)
-
The project already includes an email sending utility:
EmailSender. -
You will use the existing dependency injection function:
def get_accounts_email_notificator(settings: BaseAppSettings = Depends(get_settings)) -> EmailSenderInterface: return EmailSender(...)
This function provides an instance of
EmailSenderInterface, which is responsible for sending different types of account-related emails.
- In each of the four mentioned controllers, you will add email notifications using the
EmailSenderinstance. - Background Tasks (
BackgroundTasks) should be used to send emails asynchronously without blocking the main request.
-
You need to generate a valid URL for the email action (e.g., login page, password reset confirmation).
-
Example of sending a password reset completion email:
login_link = "http://127.0.0.1/accounts/login/" background_tasks.add_task( email_sender.send_password_reset_complete_email, str(data.email), login_link )
- The
background_tasks.add_task(...)method schedules the email to be sent in the background. - The function
send_password_reset_complete_emailis executed asynchronously. - The recipient’s email and the required link are passed as arguments.
- The
- Modify the controllers (
register_user,activate_account,request_password_reset_token,reset_password) to include the email notification logic. - Ensure that valid URLs are generated for actions like activation and password reset.
- Use background tasks to handle email dispatching efficiently.
This implementation will enhance the user experience by providing automated email notifications for critical account-related operations.
The next step in extending the cinema application is implementing the functionality for creating user profiles.
This includes defining the necessary validation schema (schemas/profiles.py) and implementing the route (routes/profiles.py) that handles profile creation.
The profile schema defines the structure of the user profile request and response. It must include validation rules to ensure the correctness of user input.
- Validation must be performed using functions from the
validationpackage:from validation import ( validate_name, validate_image, validate_gender, validate_birth_date )
- The schema must allow users to submit their:
- First Name (
first_name) and Last Name (last_name) → Must contain only English letters. - Gender (
gender) → Must be a valid gender option. - Date of Birth (
date_of_birth) → Must be a valid date and the user must be at least 18 years old. - Info (
info) → Cannot be empty or consist only of spaces. - Avatar (
avatar) → Must be a valid image file (JPG, JPEG, PNG) and not exceed 1MB.
- First Name (
{
"first_name": "John",
"last_name": "Doe",
"gender": "man",
"date_of_birth": "1990-01-01",
"info": "This is a test profile.",
"avatar": "<binary-image-data>"
}The POST /users/{user_id}/profile/ endpoint is responsible for handling user profile creation.
- HTTP Method:
POST - Path:
/users/{user_id}/profile/ - Response:
ProfileResponseSchema - Authorization: Requires a valid
Bearertoken in theAuthorizationheader.
The request must include a valid authentication token (Bearer <token>).
-
If the token is missing, return:
{ "detail": "Authorization header is missing" }- Error Code:
401 Unauthorized
- Error Code:
-
If the token has an invalid format, return:
{ "detail": "Invalid Authorization header format. Expected 'Bearer <token>'" }- Error Code:
401 Unauthorized
- Error Code:
-
If the token is expired, return:
{ "detail": "Token has expired." }- Error Code:
401 Unauthorized
- Error Code:
- A user can only create their own profile unless they have elevated permissions (e.g., admin).
- If a regular user tries to create a profile for another user, return:
{ "detail": "You don't have permission to edit this profile." }- Error Code:
403 Forbidden
- Error Code:
- The system checks whether the specified
user_idexists and whether the user is active. - If the user does not exist or is not active, return:
{ "detail": "User not found or not active." }- Error Code:
401 Unauthorized
- Error Code:
- If the user already has a profile, return:
{ "detail": "User already has a profile." }- Error Code:
400 Bad Request
- Error Code:
- The avatar image is uploaded using:
s3_client: S3StorageInterface = Depends(get_s3_storage_client)
- If the upload fails, return:
{ "detail": "Failed to upload avatar. Please try again later." }- Error Code:
500 Internal Server Error
- Error Code:
- The profile is stored in the database after passing all validations.
- The final avatar URL is generated and included in the response.
{
"id": 1,
"user_id": 1,
"first_name": "john",
"last_name": "doe",
"gender": "man",
"date_of_birth": "1990-01-01",
"info": "This is a test profile.",
"avatar": "http://minio-theater/avatars/1_avatar.jpg"
}- Status Code:
201 Created
| Error Type | Message | HTTP Status |
|---|---|---|
| Missing Token | "Authorization header is missing" |
401 Unauthorized |
| Invalid Token Format | "Invalid Authorization header format. Expected 'Bearer <token>'" |
401 Unauthorized |
| Expired Token | "Token has expired." |
401 Unauthorized |
| Unauthorized Profile Creation | "You don't have permission to edit this profile." |
403 Forbidden |
| User Not Found or Not Active | "User not found or not active." |
401 Unauthorized |
| Profile Already Exists | "User already has a profile." |
400 Bad Request |
| Avatar Upload Failed | "Failed to upload avatar. Please try again later." |
500 Internal Server Error |
- Implement
schemas/profiles.py:- Define validation rules for profile creation using
pydanticand custom validation functions.
- Define validation rules for profile creation using
- Implement
routes/profiles.py:- Implement token validation, authorization, database storage, and avatar upload logic.
This implementation will complete the user profile management functionality within the Movie Theater API.
If you’re unsure about the expected behavior or need clarification, refer to the provided test suite. Running the tests will:
- Show the expected logic and flow for each endpoint.
- Help you identify edge cases and handle errors correctly.
- Ensure your implementation aligns with the project's requirements.
You can run the unit and integration tests directly using pytest:
pytest src/tests/test_integration/This will execute only the integration tests, ensuring that your API logic functions correctly without spinning up Docker containers.
To run the full E2E test suite, you need to start the test environment using Docker:
docker-compose -f docker-compose-tests.yml up --buildThis will spin up all necessary services (database, MinIO, MailHog, FastAPI instance) and execute the end-to-end tests inside the test container.
- The test logs will highlight any failures and discrepancies between your implementation and expected behavior.
- Use the error messages from the test output to fix issues and adjust your code accordingly.
By following these steps, you can ensure your implementation is correct and fully aligned with the Movie Theater API project requirements.