This repository implements the required backend workflow from Python Developer Assignment.pdf using FastAPI, SQLAlchemy, SQLite, JWT-style bearer tokens, email-based 2FA, and Redis-backed task caching with a documented in-memory fallback for local development.
- FastAPI for the HTTP API
- SQLAlchemy ORM with SQLite for local development
- Custom HS256 token signing for JWT-compatible bearer tokens
- PBKDF2 password/code hashing using the Python standard library
- Redis for
/tasks/view-my-taskscaching - Pytest for workflow validation
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtTo use Redis caching locally, start Redis and set REDIS_URL if you are not using the default redis://localhost:6379/0.
Optional cache-related environment variables:
CACHE_BACKEND=auto|redis|memory(autotries Redis first and falls back to memory)REDIS_URL=redis://localhost:6379/0REDIS_KEY_PREFIX=fastapi_assignment:REDIS_TIMEOUT_SECONDS=0.25
uvicorn app.main:app --reloadThe app uses sqlite:///./app.db by default. You can override it with DATABASE_URL.
Preferred repeatable local setup:
python scripts/seed_demo.py --resetThis creates:
admin@example.com/Password123!/adminjamesbond@example.com/Password123!/staff
There is also a local seed endpoint:
POST /seed/users?reset=truePOST /seed/usersPOST /auth/loginGET /dev/email-logs/latestPOST /auth/verify-2faPOST /tasksPOST /tasks/assignGET /tasks/view-my-tasks
- Seed users with
python scripts/seed_demo.py --resetorPOST /seed/users?reset=true. - Login as admin with
POST /auth/login. - Read the latest admin 2FA code from
GET /dev/email-logs/latest?email=admin@example.com. - Exchange the code at
POST /auth/verify-2fato receive a bearer token. - Create exactly five tasks using
POST /tasks. - Assign exactly three tasks to James Bond using
POST /tasks/assign. - Repeat the login + 2FA flow for
jamesbond@example.com. - Attempting
POST /tasksas James Bond returns403. - Call
GET /tasks/view-my-tasksand confirm exactly three tasks are returned. - Call the same endpoint again and confirm
cache.hitchanges fromfalsetotrue.
{
"user": {
"email": "jamesbond@example.com",
"role": "staff"
},
"tasks": [
{
"id": "8cb6557a-4cab-4745-8dbc-d581a2d4ac93",
"title": "Task 1",
"description": "Description 1",
"status": "todo",
"priority": "high",
"assigned_to": "jamesbond@example.com"
},
{
"id": "06bb56e2-e8ca-46cb-b509-c5f8af81f83e",
"title": "Task 2",
"description": "Description 2",
"status": "todo",
"priority": "medium",
"assigned_to": "jamesbond@example.com"
},
{
"id": "d53750a1-2cad-4d29-9f7d-c63828ee4c62",
"title": "Task 3",
"description": "Description 3",
"status": "todo",
"priority": "low",
"assigned_to": "jamesbond@example.com"
}
],
"summary": {
"total_assigned_tasks": 3
},
"cache": {
"hit": false
}
}pytestThe tests cover:
- User seeding
- Login starting 2FA without returning a token immediately
- Incorrect, expired, and reused 2FA code rejection
- Admin-only task creation
- Admin task assignment to James Bond
- Staff forbidden from creating tasks
- James Bond viewing assigned tasks
- Cache miss on first read and hit on second read
- Cache invalidation after reassignment
- SQLite is used instead of PostgreSQL for a simpler local setup.
CACHE_BACKEND=autofalls back to process-local in-memory caching when Redis is unavailable, which means cache state is not shared across multiple API workers./dev/email-logs/latestis intentionally development-only and exposes the latest one-time code for validation.- Schema creation is automatic at application startup instead of being driven by migrations.