📋 Software Requirements Specification (SRS)
Product Catalog Service - Monolithic Application
Version: 1.0.0 Date: 2025-12-24 Based on: Building High-Performance APIs with FastAPI
1. Introduction
1.1 Purpose
This document describes the software requirements for the Product Catalog Service - a monolithic RESTful API application built with FastAPI, serving the purpose of learning and practicing according to the training syllabus.
1.2 Scope
The system provides:
- User authentication with JWT
- Product management (CRUD)
- Role-based access control
- Automatic API documentation
- Unit testing
1.3 Definitions
| Term | Definition |
|---|---|
| JWT | JSON Web Token - Authentication Token |
| CRUD | Create, Read, Update, Delete |
| RBAC | Role-Based Access Control |
| DI | Dependency Injection |
2. Overall Description
2.1 System Architecture
┌─────────────────────────────────────────────────────────────┐
│ Product Catalog Service │
│ (Port 8000) │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────────┐ │
│ │ API Layer (FastAPI Routes) │ │
│ │ • POST /register, POST /login │ │
│ │ • GET/POST/PUT/DELETE /products │ │
│ └─────────────────────┬───────────────────────────────┘ │
│ │ │
│ ┌─────────────────────▼───────────────────────────────┐ │
│ │ Service Layer (Business Logic) │ │
│ │ • AuthService, ProductService │ │
│ └─────────────────────┬───────────────────────────────┘ │
│ │ │
│ ┌─────────────────────▼───────────────────────────────┐ │
│ │ Repository Layer (Data Access) │ │
│ │ • UserRepository, ProductRepository │ │
│ └─────────────────────┬───────────────────────────────┘ │
│ │ │
│ ┌─────────────────────▼───────────────────────────────┐ │
│ │ Database (PostgreSQL/SQLite) │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
2.2 Technology Stack
| Layer | Technology |
|---|---|
| Framework | FastAPI 0.104+ |
| ORM | SQLAlchemy 2.0 |
| Migrations | Alembic |
| Validation | Pydantic 2.0 |
| Auth | python-jose, passlib, bcrypt |
| Server | Uvicorn |
| Database | PostgreSQL / SQLite |
3. Functional Requirements by Training Unit
3.1 Unit 1: Foundation & Inputs (Assignment 01)
REQ-1.1: Hello World Endpoint
| ID | REQ-1.1 |
|---|---|
| Description | API must have a root endpoint returning basic information |
| Priority | High |
| Status | ✅ Implemented |
Acceptance Criteria:
-
GET /returns JSON with message, version, docs links - Response status code: 200
- Content-Type: application/json
API Specification:
GET /
Response 200:
{
"message": "Welcome to Product Catalog Service API",
"version": "1.0.0",
"docs": "/docs",
"redoc": "/redoc"
}
REQ-1.2: Health Check Endpoint
| ID | REQ-1.2 |
|---|---|
| Description | API must have a health check endpoint |
| Priority | High |
| Status | ✅ Implemented |
Acceptance Criteria:
-
GET /healthreturns healthy status - Response includes service name and version
API Specification:
GET /health
Response 200:
{
"status": "healthy",
"service": "Product Catalog Service",
"version": "1.0.0"
}
REQ-1.3: OpenAPI Documentation
| ID | REQ-1.3 |
|---|---|
| Description | API automatically generates OpenAPI documentation |
| Priority | High |
| Status | ✅ Implemented |
Acceptance Criteria:
- Swagger UI accessible at
/docs - ReDoc accessible at
/redoc - OpenAPI JSON at
/openapi.json - All endpoints documented with descriptions
REQ-1.4: Path & Query Parameters
| ID | REQ-1.4 |
|---|---|
| Description | API supports path and query parameters with data conversion |
| Priority | High |
| Status | ✅ Implemented |
Acceptance Criteria:
-
GET /products/{product_id}- Path parameter (int) -
GET /products?skip=0&limit=100- Query parameters - Automatic type conversion and validation
3.2 Unit 2: Data Modeling - Pydantic (Assignment 02)
REQ-2.1: User Schemas
| ID | REQ-2.1 |
|---|---|
| Description | Define Pydantic schemas for User |
| Priority | High |
| Status | ✅ Implemented |
Schema Definitions:
# UserCreate - Input schema
class UserCreate(BaseModel):
username: str # min_length=3, max_length=50
password: str # min_length=6
# UserResponse - Output schema
class UserResponse(BaseModel):
id: int
username: str
is_active: bool
created_at: datetime
model_config = {"from_attributes": True}
# Token - JWT response
class Token(BaseModel):
access_token: str
token_type: str # "bearer"
Acceptance Criteria:
- UserCreate validates username (3-50 chars)
- UserCreate validates password (min 6 chars)
- UserResponse excludes password field
- Validation errors return 422 with details
REQ-2.2: Product Schemas
| ID | REQ-2.2 |
|---|---|
| Description | Define Pydantic schemas for Product |
| Priority | High |
| Status | ✅ Implemented |
Schema Definitions:
# ProductCreate - Input for creation
class ProductCreate(BaseModel):
name: str # min_length=1, max_length=100
description: str # optional, max_length=500
price: Decimal # gt=0, max_digits=10, decimal_places=2
quantity: int # ge=0
# ProductUpdate - Input for update (all optional)
class ProductUpdate(BaseModel):
name: Optional[str]
description: Optional[str]
price: Optional[Decimal]
quantity: Optional[int]
# ProductResponse - Output schema
class ProductResponse(BaseModel):
id: int
name: str
description: Optional[str]
price: Decimal
quantity: int
created_at: datetime
updated_at: datetime
model_config = {"from_attributes": True}
Acceptance Criteria:
- Price validation: positive number, max 2 decimal places
- Quantity validation: non-negative integer
- Name required, description optional
- response_model filters output fields
REQ-2.3: Nested Models Support
| ID | REQ-2.3 |
|---|---|
| Description | Support nested Pydantic models |
| Priority | Medium |
| Status | ⚠️ Partial |
Acceptance Criteria:
- Support nested structures in request/response
- Proper serialization/deserialization
3.3 Unit 3: Database Setup (Assignment 03)
REQ-3.1: SQLAlchemy Models
| ID | REQ-3.1 |
|---|---|
| Description | Define SQLAlchemy 2.0 models |
| Priority | High |
| Status | ✅ Implemented |
User Model:
class User(Base):
__tablename__ = "users"
id: int # Primary key, auto-increment
username: str # Unique, indexed, max 50 chars
hashed_password: str # Max 255 chars
is_active: bool # Default True
created_at: datetime # Server default now()
updated_at: datetime # Auto-update on change
Product Model:
class Product(Base):
__tablename__ = "products"
id: int # Primary key, auto-increment
name: str # Max 100 chars, indexed
description: str # Optional, max 500 chars
price: Numeric # Precision 10, scale 2
quantity: int # Default 0
created_at: datetime # Server default now()
updated_at: datetime # Auto-update on change
Acceptance Criteria:
- Models use SQLAlchemy 2.0 style (Mapped annotations)
- Proper indexes on frequently queried columns
- Timestamps auto-managed
REQ-3.2: Database Connection
| ID | REQ-3.2 |
|---|---|
| Description | Setup database connection with SQLAlchemy |
| Priority | High |
| Status | ✅ Implemented |
Acceptance Criteria:
- Support PostgreSQL and SQLite
- Connection pooling configured
-
get_dbdependency for session management - Proper session cleanup (try/finally)
Configuration:
# Environment variable
DATABASE_URL=postgresql://user:pass@localhost:5432/dbname
# or
DATABASE_URL=sqlite:///./product_catalog.db
REQ-3.3: Alembic Migrations
| ID | REQ-3.3 |
|---|---|
| Description | Database migrations with Alembic |
| Priority | High |
| Status | ✅ Implemented |
Acceptance Criteria:
-
alembic initconfigured - Auto-generate migrations from models
-
alembic upgrade headcreates all tables -
alembic downgrade -1rollback works - Migration history tracked
Commands:
# Generate migration
alembic revision --autogenerate -m "description"
# Apply migrations
alembic upgrade head
# Rollback
alembic downgrade -1
3.4 Unit 4: CRUD & Dependency Injection (Assignment 04)
REQ-4.1: Dependency Injection Setup
| ID | REQ-4.1 |
|---|---|
| Description | Implement DI pattern with FastAPI Depends() |
| Priority | High |
| Status | ✅ Implemented |
Dependencies:
# Database session dependency
def get_db() -> Generator[Session, None, None]:
db = SessionLocal()
try:
yield db
finally:
db.close()
# Usage in routes
@router.get("/products")
def get_products(db: Session = Depends(get_db)):
pass
Acceptance Criteria:
-
get_dbdependency properly yields session - Session closed after request
- Dependencies injectable in routes
REQ-4.2: Repository Pattern
| ID | REQ-4.2 |
|---|---|
| Description | Implement Repository pattern for data access |
| Priority | High |
| Status | ✅ Implemented |
Base Repository:
class BaseRepository(Generic[T]):
def __init__(self, db: Session, model: Type[T]):
self.db = db
self.model = model
def create(self, obj_data: dict) -> T
def get_by_id(self, id: int) -> Optional[T]
def get_all(self, skip: int, limit: int) -> List[T]
def update(self, id: int, obj_data: dict) -> Optional[T]
def delete(self, id: int) -> bool
Acceptance Criteria:
- Generic base repository with CRUD methods
- UserRepository extends base with custom methods
- ProductRepository extends base with custom methods
- Separation of data access from business logic
REQ-4.3: Product CRUD Operations
| ID | REQ-4.3 |
|---|---|
| Description | Full CRUD for Products |
| Priority | High |
| Status | ✅ Implemented |
API Endpoints:
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /products | List all products | No |
| GET | /products/{id} | Get product by ID | No |
| POST | /products | Create product | Yes |
| PUT | /products/{id} | Update product | Yes |
| DELETE | /products/{id} | Delete product | Yes |
Acceptance Criteria:
- GET /products - Pagination with skip/limit
- GET /products/{id} - 404 if not found
- POST /products - 201 Created, return product
- PUT /products/{id} - Partial update support
- DELETE /products/{id} - 204 No Content
3.5 Unit 5: Authentication (Assignment 05)
REQ-5.1: User Registration
| ID | REQ-5.1 |
|---|---|
| Description | User registration endpoint |
| Priority | High |
| Status | ✅ Implemented |
API Specification:
POST /register
Content-Type: application/json
Request:
{
"username": "john_doe",
"password": "SecurePass123"
}
Response 201:
{
"id": 1,
"username": "john_doe",
"is_active": true,
"created_at": "2025-10-18T10:00:00"
}
Response 400:
{
"detail": "Username already exists"
}
Acceptance Criteria:
- Password hashed with bcrypt before storage
- Duplicate username returns 400
- Password not returned in response
- User created as active by default
REQ-5.2: Password Hashing
| ID | REQ-5.2 |
|---|---|
| Description | Secure password hashing |
| Priority | High |
| Status | ✅ Implemented |
Implementation:
import bcrypt
def get_password_hash(password: str) -> str:
"""Hash password using bcrypt"""
return bcrypt.hashpw(
password.encode('utf-8'),
bcrypt.gensalt()
).decode('utf-8')
def verify_password(plain: str, hashed: str) -> bool:
"""Verify password against hash"""
return bcrypt.checkpw(
plain.encode('utf-8'),
hashed.encode('utf-8')
)
Acceptance Criteria:
- Use bcrypt for hashing
- Salt automatically generated
- Verification function works correctly
REQ-5.3: User Login & JWT Token
| ID | REQ-5.3 |
|---|---|
| Description | Login endpoint returning JWT |
| Priority | High |
| Status | ✅ Implemented |
API Specification:
POST /login
Content-Type: application/x-www-form-urlencoded
Request:
username=john_doe&password=SecurePass123
Response 200:
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "bearer"
}
Response 401:
{
"detail": "Incorrect username or password"
}
Acceptance Criteria:
- OAuth2 Password Flow (form data)
- JWT token contains username/user_id
- Token expires after configured time
- Invalid credentials return 401
REQ-5.4: JWT Token Generation
| ID | REQ-5.4 |
|---|---|
| Description | JWT token creation and validation |
| Priority | High |
| Status | ✅ Implemented |
Implementation:
from jose import jwt, JWTError
from datetime import datetime, timedelta
def create_access_token(data: dict) -> str:
to_encode = data.copy()
expire = datetime.utcnow() + timedelta(
minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES
)
to_encode.update({"exp": expire})
return jwt.encode(
to_encode,
settings.SECRET_KEY,
algorithm=settings.ALGORITHM
)
def decode_access_token(token: str) -> Optional[str]:
try:
payload = jwt.decode(
token,
settings.SECRET_KEY,
algorithms=[settings.ALGORITHM]
)
return payload.get("sub")
except JWTError:
return None
Configuration:
SECRET_KEY=your-super-secret-key-min-32-characters
ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_MINUTES=30
Acceptance Criteria:
- Token contains "sub" claim with username
- Token contains "exp" claim for expiration
- Expired tokens rejected
- Invalid tokens rejected
3.6 Unit 6: Authorization (Assignment 06)
REQ-6.1: Get Current User Dependency
| ID | REQ-6.1 |
|---|---|
| Description | Dependency to extract current user from JWT |
| Priority | High |
| Status | ✅ Implemented |
Implementation:
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login")
def get_current_user(
token: str = Depends(oauth2_scheme),
db: Session = Depends(get_db)
) -> User:
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
username = decode_access_token(token)
if username is None:
raise credentials_exception
user = UserRepository(db).get_by_username(username)
if user is None:
raise credentials_exception
return user
Acceptance Criteria:
- Extracts token from Authorization header
- Decodes and validates JWT
- Returns User object if valid
- Raises 401 if invalid/expired
REQ-6.2: Protected Endpoints
| ID | REQ-6.2 |
|---|---|
| Description | Endpoints require authentication |
| Priority | High |
| Status | ✅ Implemented |
Protected Routes:
@router.post("/products")
def create_product(
product: ProductCreate,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user)
):
# Only authenticated users can create
pass
Acceptance Criteria:
- POST /products requires JWT
- PUT /products/{id} requires JWT
- DELETE /products/{id} requires JWT
- GET endpoints remain public
REQ-6.3: Role-Based Access Control (RBAC)
| ID | REQ-6.3 |
|---|---|
| Description | Implement roles for users |
| Priority | Medium |
| Status | ❌ Not Implemented |
Required Changes:
- Add role to User model:
class User(Base):
# ... existing fields
role: str # "user", "admin"
- Role checking dependency:
def require_role(required_role: str):
def role_checker(
current_user: User = Depends(get_current_user)
) -> User:
if current_user.role != required_role:
raise HTTPException(
status_code=403,
detail="Insufficient permissions"
)
return current_user
return role_checker
# Usage
@router.delete("/products/{id}")
def delete_product(
id: int,
admin: User = Depends(require_role("admin"))
):
pass
Acceptance Criteria:
- User model has role field
- Default role is "user"
- Admin endpoints check role
- 403 Forbidden for insufficient permissions
3.7 Unit 7: Testing & Middleware (Assignment 07)
REQ-7.1: Unit Tests with Pytest
| ID | REQ-7.1 |
|---|---|
| Description | Unit tests for API endpoints |
| Priority | High |
| Status | ❌ Not Implemented |
Test Structure:
mono-src/
└── tests/
├── __init__.py
├── conftest.py # Fixtures
├── test_auth.py # Auth tests
├── test_products.py # Product tests
└── test_health.py # Health check tests
Required Tests:
# tests/conftest.py
import pytest
from fastapi.testclient import TestClient
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from app.main import app
from app.database import Base, get_db
SQLALCHEMY_TEST_URL = "sqlite:///./test.db"
engine = create_engine(SQLALCHEMY_TEST_URL)
TestingSessionLocal = sessionmaker(bind=engine)
@pytest.fixture
def client():
Base.metadata.create_all(bind=engine)
def override_get_db():
db = TestingSessionLocal()
try:
yield db
finally:
db.close()
app.dependency_overrides[get_db] = override_get_db
with TestClient(app) as c:
yield c
Base.metadata.drop_all(bind=engine)
# tests/test_auth.py
def test_register_user(client):
response = client.post("/register", json={
"username": "testuser",
"password": "Test@123"
})
assert response.status_code == 201
assert response.json()["username"] == "testuser"
def test_register_duplicate_user(client):
client.post("/register", json={
"username": "testuser",
"password": "Test@123"
})
response = client.post("/register", json={
"username": "testuser",
"password": "Test@123"
})
assert response.status_code == 400
def test_login_success(client):
client.post("/register", json={
"username": "testuser",
"password": "Test@123"
})
response = client.post("/login", data={
"username": "testuser",
"password": "Test@123"
})
assert response.status_code == 200
assert "access_token" in response.json()
def test_login_wrong_password(client):
client.post("/register", json={
"username": "testuser",
"password": "Test@123"
})
response = client.post("/login", data={
"username": "testuser",
"password": "WrongPass"
})
assert response.status_code == 401
# tests/test_products.py
def test_get_products_empty(client):
response = client.get("/products")
assert response.status_code == 200
assert response.json() == []
def test_create_product_unauthorized(client):
response = client.post("/products", json={
"name": "Test Product",
"price": 99.99,
"quantity": 10
})
assert response.status_code == 401
def test_create_product_authorized(client):
# Register and login
client.post("/register", json={
"username": "testuser",
"password": "Test@123"
})
login_resp = client.post("/login", data={
"username": "testuser",
"password": "Test@123"
})
token = login_resp.json()["access_token"]
# Create product
response = client.post(
"/products",
json={
"name": "Test Product",
"price": 99.99,
"quantity": 10
},
headers={"Authorization": f"Bearer {token}"}
)
assert response.status_code == 201
assert response.json()["name"] == "Test Product"
Acceptance Criteria:
- Test coverage > 80%
- Tests use separate test database
- Auth tests cover success/failure cases
- CRUD tests cover all operations
- Tests run with
pytest tests/ -v
REQ-7.2: CORS Middleware
| ID | REQ-7.2 |
|---|---|
| Description | CORS configuration for cross-origin requests |
| Priority | High |
| Status | ✅ Implemented |
Implementation:
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=settings.ALLOWED_ORIGINS, # ["*"] or specific
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
Configuration:
ALLOWED_ORIGINS=["http://localhost:3000","http://localhost:5173"]
Acceptance Criteria:
- CORS headers in responses
- Configurable origins via environment
- Preflight requests handled
REQ-7.3: Error Handling Middleware
| ID | REQ-7.3 |
|---|---|
| Description | Custom exception handlers |
| Priority | Medium |
| Status | ⚠️ Partial |
Required Implementation:
from fastapi import Request
from fastapi.responses import JSONResponse
class AppException(Exception):
def __init__(self, status_code: int, detail: str):
self.status_code = status_code
self.detail = detail
@app.exception_handler(AppException)
async def app_exception_handler(
request: Request,
exc: AppException
):
return JSONResponse(
status_code=exc.status_code,
content={
"error": True,
"message": exc.detail,
"path": str(request.url)
}
)
@app.exception_handler(Exception)
async def global_exception_handler(
request: Request,
exc: Exception
):
return JSONResponse(
status_code=500,
content={
"error": True,
"message": "Internal server error",
"path": str(request.url)
}
)
Acceptance Criteria:
- Custom exception class
- Consistent error response format
- Global exception handler for 500 errors
- Detailed errors in development mode
3.8 Unit 8: GraphQL APIs (Optional)
REQ-8.1: Strawberry GraphQL Integration
| ID | REQ-8.1 |
|---|---|
| Description | GraphQL endpoint with Strawberry |
| Priority | Low |
| Status | ❌ Not Implemented |
Required Implementation:
# app/graphql/types.py
import strawberry
from datetime import datetime
@strawberry.type
class ProductType:
id: int
name: str
description: str | None
price: float
quantity: int
created_at: datetime
# app/graphql/schema.py
@strawberry.type
class Query:
@strawberry.field
def products(self) -> list[ProductType]:
# Resolve products
pass
@strawberry.field
def product(self, id: int) -> ProductType | None:
# Resolve single product
pass
@strawberry.type
class Mutation:
@strawberry.mutation
def create_product(
self,
name: str,
price: float,
quantity: int
) -> ProductType:
pass
schema = strawberry.Schema(query=Query, mutation=Mutation)
# app/main.py
from strawberry.fastapi import GraphQLRouter
from app.graphql.schema import schema
graphql_router = GraphQLRouter(schema)
app.include_router(graphql_router, prefix="/graphql")
Acceptance Criteria:
- GraphiQL accessible at
/graphql - Query products
- Query single product
- Mutation for create/update/delete
- Authentication in resolvers
3.9 Unit 9: Deployment (Final Project)
REQ-9.1: Environment Configuration
| ID | REQ-9.1 |
|---|---|
| Description | Configuration with pydantic-settings |
| Priority | High |
| Status | ✅ Implemented |
Implementation:
from pydantic_settings import BaseSettings
from typing import List
class Settings(BaseSettings):
# Application
APP_NAME: str = "Product Catalog Service"
APP_VERSION: str = "1.0.0"
DEBUG: bool = False
# Database
DATABASE_URL: str
# Security
SECRET_KEY: str
ALGORITHM: str = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
# CORS
ALLOWED_ORIGINS: List[str] = ["*"]
class Config:
env_file = ".env"
case_sensitive = True
settings = Settings()
Acceptance Criteria:
- All secrets from environment
- No hardcoded credentials
- .env.example provided
- Different configs for dev/prod
REQ-9.2: Production Deployment
| ID | REQ-9.2 |
|---|---|
| Description | Production-ready deployment |
| Priority | Medium |
| Status | ⚠️ Partial |
Required Files:
# Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- '8000:8000'
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/catalog
- SECRET_KEY=${SECRET_KEY}
depends_on:
- db
db:
image: postgres:15
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
- POSTGRES_DB=catalog
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
Acceptance Criteria:
- Dockerfile provided
- docker-compose.yml for local dev
- Health check endpoint
- Proper logging configuration
4. Non-Functional Requirements
4.1 Performance
| ID | Requirement |
|---|---|
| NFR-1 | Response time < 200ms for simple queries |
| NFR-2 | Support 100 concurrent users |
| NFR-3 | Database connection pooling enabled |
4.2 Security
| ID | Requirement |
|---|---|
| NFR-4 | Passwords hashed with bcrypt |
| NFR-5 | JWT tokens expire after 30 minutes |
| NFR-6 | CORS properly configured |
| NFR-7 | SQL injection prevention via ORM |
| NFR-8 | No sensitive data in logs |
4.3 Maintainability
| ID | Requirement |
|---|---|
| NFR-9 | Code follows PEP 8 |
| NFR-10 | Type hints for all functions |
| NFR-11 | Docstrings for public methods |
| NFR-12 | Test coverage > 80% |
5. Implementation Checklist
5.1 Current Status Summary
| Unit | Assignment | Status | Priority |
|---|---|---|---|
| 1 | Foundation & Inputs | ✅ Complete | - |
| 2 | Pydantic Models | ✅ Complete | - |
| 3 | Database Setup | ✅ Complete | - |
| 4 | CRUD & DI | ✅ Complete | - |
| 5 | Authentication | ✅ Complete | - |
| 6 | Authorization | ⚠️ RBAC missing | High |
| 7 | Testing | ❌ Not done | Critical |
| 8 | GraphQL | ❌ Not done | Low |
| 9 | Deployment | ⚠️ Partial | Medium |
5.2 Required Actions
Critical (Must Have)
- Add Unit Tests
mkdir -p mono-src/tests
# Create test files as specified in REQ-7.1
- Add Role to User Model
# app/models/user.py
role = Column(String(20), default="user", nullable=False)
- Create Migration for Role
alembic revision --autogenerate -m "Add role to users"
alembic upgrade head
Medium Priority
- Add Custom Exception Handlers
- Create Dockerfile
- Create docker-compose.yml
Low Priority
- Implement GraphQL (Optional)
- Add API Rate Limiting
6. API Reference Summary
Authentication
| Endpoint | Method | Description | Auth |
|---|---|---|---|
/register | POST | Register new user | No |
/login | POST | Login, get JWT | No |
Products
| Endpoint | Method | Description | Auth |
|---|---|---|---|
/products | GET | List products | No |
/products/{id} | GET | Get product | No |
/products | POST | Create product | Yes |
/products/{id} | PUT | Update product | Yes |
/products/{id} | DELETE | Delete product | Yes |
System
| Endpoint | Method | Description |
|---|---|---|
/ | GET | API info |
/health | GET | Health check |
/docs | GET | Swagger UI |
/redoc | GET | ReDoc |
7. Appendix
A. Environment Variables
# Application
APP_NAME=Product Catalog Service
APP_VERSION=1.0.0
DEBUG=True
# Database
DATABASE_URL=postgresql://user:password@localhost:5432/product_catalog
# Security
SECRET_KEY=your-super-secret-key-minimum-32-characters-long
ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_MINUTES=30
# CORS
ALLOWED_ORIGINS=["http://localhost:3000","http://localhost:5173"]
B. Quick Commands
# Setup
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
# Database
alembic upgrade head
# Run
uvicorn app.main:app --reload
# Test
pytest tests/ -v --cov=app
Document Version: 1.0.0 Created: 2025-12-24 Author: CongDX Status: Draft