diff --git a/README.md b/README.md index 69c4853..e5d1ebb 100644 --- a/README.md +++ b/README.md @@ -13,36 +13,16 @@ ![Claude](https://img.shields.io/badge/Claude-contributing-D97757?logo=claude&logoColor=white&labelColor=181818) ![CodeRabbit](https://img.shields.io/badge/CodeRabbit-reviewing-FF570A?logo=coderabbit&logoColor=white&labelColor=181818) -Proof of Concept for a RESTful API built with [Python 3](https://www.python.org/) and [FastAPI](https://fastapi.tiangolo.com/). Manage football player data with SQLite, SQLAlchemy 2.0 (async), Pydantic validation, and in-memory caching. - -## Table of Contents - -- [Features](#features) -- [Tech Stack](#tech-stack) -- [Project Structure](#project-structure) -- [Architecture](#architecture) -- [Architecture Decisions](#architecture-decisions) -- [API Reference](#api-reference) -- [Prerequisites](#prerequisites) -- [Quick Start](#quick-start) -- [Testing](#testing) -- [Containers](#containers) -- [Releases](#releases) -- [Environment Variables](#environment-variables) -- [Command Summary](#command-summary) -- [Contributing](#contributing) -- [Legal](#legal) +Proof of Concept for a RESTful Web Service built with **FastAPI** and **Python 3.13**. This project demonstrates best practices for building a layered, testable, and maintainable API implementing CRUD operations for a Players resource (Argentina 2022 FIFA World Cup squad). ## Features -- ๐Ÿ—๏ธ **Modern async architecture** - Async/await throughout, Pydantic validation, and SQLAlchemy 2.0 patterns -- ๐Ÿ“š **Interactive API exploration** - Auto-generated OpenAPI docs with FastAPI's built-in Swagger UI and `.rest` file for REST Client integration -- โšก **Performance optimizations** - Async SQLAlchemy, in-memory caching with aiocache (10-minute TTL), and efficient database operations -- ๐Ÿงช **High test coverage** - Pytest suite with 80% minimum coverage and automated reporting to Codecov and SonarCloud -- ๐Ÿ“– **Agent-optimized documentation** - Claude Code and GitHub Copilot instructions with coding guidelines, architecture rules, and agent workflows for AI-assisted development -- ๐Ÿณ **Full containerization** - Production-ready Docker setup with Docker Compose orchestration -- ๐Ÿ”„ **Complete CI/CD pipeline** - Automated linting (Black/Flake8), testing, Docker publishing, and GitHub releases -- โ™Ÿ๏ธ **Coach-themed semantic versioning** - Memorable, alphabetical release names honoring legendary football coaches +- ๐Ÿ—๏ธ **Async Architecture** - Async/await throughout with SQLAlchemy 2.0 and dependency injection via FastAPI's `Depends()` +- ๐Ÿ“š **Interactive Documentation** - Auto-generated Swagger UI with VS Code and JetBrains REST Client support +- โšก **Performance Caching** - In-memory caching with aiocache and async SQLite operations +- โœ… **Input Validation** - Pydantic models enforce request/response schemas with automatic error responses +- ๐Ÿณ **Containerized Deployment** - Production-ready Docker setup with pre-seeded database +- ๐Ÿ”„ **Automated Pipeline** - Continuous integration with Black, Flake8, and automated testing ## Tech Stack @@ -58,33 +38,6 @@ Proof of Concept for a RESTful API built with [Python 3](https://www.python.org/ | **Linting / Formatting** | [Flake8](https://flake8.pycqa.org/) + [Black](https://black.readthedocs.io/) | | **Containerization** | [Docker](https://www.docker.com/) & [Docker Compose](https://docs.docker.com/compose/) | -## Project Structure - -```text -/ -โ”œโ”€โ”€ main.py # Entry point: FastAPI setup, router registration -โ”œโ”€โ”€ routes/ # HTTP route definitions, dependency injection + caching -โ”‚ โ”œโ”€โ”€ player_route.py -โ”‚ โ””โ”€โ”€ health_route.py -โ”œโ”€โ”€ services/ # Async business logic -โ”‚ โ””โ”€โ”€ player_service.py -โ”œโ”€โ”€ schemas/ # SQLAlchemy ORM models (database schema) -โ”‚ โ””โ”€โ”€ player_schema.py -โ”œโ”€โ”€ databases/ # Async SQLAlchemy session setup -โ”‚ โ””โ”€โ”€ player_database.py -โ”œโ”€โ”€ models/ # Pydantic models for request/response validation -โ”‚ โ””โ”€โ”€ player_model.py -โ”œโ”€โ”€ tests/ # pytest integration tests -โ”‚ โ”œโ”€โ”€ conftest.py -โ”‚ โ”œโ”€โ”€ player_stub.py -โ”‚ โ””โ”€โ”€ test_main.py -โ”œโ”€โ”€ rest/ # HTTP request files -โ”‚ โ””โ”€โ”€ players.rest # CRUD requests (REST Client / JetBrains HTTP Client) -โ”œโ”€โ”€ storage/ # SQLite database file (pre-seeded) -โ”œโ”€โ”€ scripts/ # Container entrypoint & healthcheck -โ””โ”€โ”€ .github/workflows/ # CI/CD pipelines -``` - ## Architecture Layered architecture with dependency injection via FastAPI's `Depends()` mechanism and Pydantic for request/response validation. @@ -153,54 +106,29 @@ graph RL class tests test ``` -*Simplified, conceptual view โ€” not all components or dependencies are shown.* - -### Arrow Semantics - -Arrows follow the injection direction: `A --> B` means A is injected into B. Solid arrows (`-->`) represent active dependencies โ€” components wired via FastAPI's `Depends()` mechanism and invoked at runtime. Dotted arrows (`-.->`) represent structural dependencies โ€” the consumer references types without invoking runtime behavior. - -### Composition Root Pattern - -`main` is the composition root: it creates the FastAPI application instance, configures the lifespan handler, and registers all route modules via `app.include_router()`. Dependencies are resolved at request time via FastAPI's `Depends()` mechanism. - -### Layered Architecture - -Four layers: Initialization (`main`), HTTP (`routes`), Business (`services`), and Data (`schemas`, `databases`). - -Third-party packages are placed inside the subgraph of the layer that uses them โ€” `FastAPI` and `aiocache` in HTTP, `SQLAlchemy` in Data. The soft dependency from `routes` to `SQLAlchemy` reflects `AsyncSession` appearing only as a type annotation in `Depends()`, with no direct SQLAlchemy calls at the route level. - -`models` is a cross-cutting type concern โ€” Pydantic request/response models consumed across all layers, with no logic of its own. - -### Color Coding - -Blue = core application packages, red = third-party libraries, green = tests. +> *Arrows follow the injection direction (A โ†’ B means A is injected into B). Solid = runtime dependency, dotted = structural. Blue = core domain, red = third-party, green = tests.* -## Architecture Decisions - -Key architectural decisions are documented as ADRs in [`docs/adr/`](docs/adr/README.md). +Significant architectural decisions are documented in [`docs/adr/`](docs/adr/). ## API Reference Interactive API documentation is available via Swagger UI at `http://localhost:9000/docs` when the server is running. -**Quick Reference:** - -- `GET /players/` โ€” List all players -- `GET /players/{player_id}` โ€” Get player by UUID (surrogate key) -- `GET /players/squadnumber/{squad_number}` โ€” Get player by squad number (natural key) -- `POST /players/` โ€” Create a new player -- `PUT /players/squadnumber/{squad_number}` โ€” Update an existing player -- `DELETE /players/squadnumber/{squad_number}` โ€” Remove a player -- `GET /health` โ€” Health check - -### HTTP Requests +| Method | Endpoint | Description | Status | +| ------ | -------- | ----------- | ------ | +| `GET` | `/players/` | List all players | `200 OK` | +| `GET` | `/players/{player_id}` | Get player by ID | `200 OK` | +| `GET` | `/players/squadnumber/{squad_number}` | Get player by squad number | `200 OK` | +| `POST` | `/players/` | Create new player | `201 Created` | +| `PUT` | `/players/squadnumber/{squad_number}` | Update player by squad number | `204 No Content` | +| `DELETE` | `/players/squadnumber/{squad_number}` | Remove player by squad number | `204 No Content` | +| `GET` | `/health` | Health check | `200 OK` | -A ready-to-use HTTP request file is available at [`rest/players.rest`](rest/players.rest). It covers all CRUD operations and can be run directly from your editor without leaving the development environment: +Error codes: `400 Bad Request` (squad number mismatch on `PUT`) ยท `404 Not Found` (player not found) ยท `409 Conflict` (duplicate squad number on `POST`) ยท `422 Unprocessable Entity` (schema validation failed) -- **VS Code** โ€” install the [REST Client](https://marketplace.visualstudio.com/items?itemName=humao.rest-client) extension (`humao.rest-client`), open `rest/players.rest`, and click **Send Request** above any entry. -- **JetBrains IDEs** (IntelliJ IDEA, PyCharm, WebStorm) โ€” the built-in HTTP Client supports `.rest` files natively; no plugin required. +For complete endpoint documentation with request/response schemas, explore the [interactive Swagger UI](http://localhost:9000/docs). -The file targets `http://localhost:9000` by default. +Alternatively, use [`rest/players.rest`](rest/players.rest) with the [REST Client](https://marketplace.visualstudio.com/items?itemName=humao.rest-client) extension for VS Code, or the built-in HTTP Client in JetBrains IDEs (IntelliJ IDEA, PyCharm, WebStorm). ## Prerequisites @@ -212,14 +140,14 @@ Before you begin, ensure you have the following installed: ## Quick Start -### Clone the repository +### Clone ```bash git clone https://github.com/nanotaboada/python-samples-fastapi-restful.git cd python-samples-fastapi-restful ``` -### Install dependencies +### Install Dependencies are defined in `pyproject.toml` using [PEP 735](https://peps.python.org/pep-0735/) dependency groups. @@ -233,8 +161,6 @@ source .venv/bin/activate # On Windows: .venv\Scripts\activate uv pip install --group dev ``` -Or with specific groups: - | Command | Description | | ------- | ----------- | | `uv pip install` | Production dependencies only | @@ -242,64 +168,37 @@ Or with specific groups: | `uv pip install --group lint` | Linting dependencies | | `uv pip install --group dev` | All (test + lint + production) | -### Start the development server +### Run ```bash uv run uvicorn main:app --reload --port 9000 ``` -The server will start on `http://localhost:9000`. - -### Access the application - -- **API:** `http://localhost:9000` -- **Swagger Documentation:** `http://localhost:9000/docs` -- **Health Check:** `http://localhost:9000/health` +### Access -## Testing +Once the application is running, you can access: -Run the test suite with coverage: - -```bash -# Run all tests -uv run pytest - -# Run tests with verbose output -uv run pytest -v - -# Run tests with coverage report -uv run pytest --cov=./ --cov-report=term -``` - -Tests are located in the `tests/` directory and use `httpx` for async integration testing. Coverage reports are generated for routes, services, and databases. - -**Coverage target:** 80% minimum. +- **API Server**: `http://localhost:9000` +- **Swagger UI**: `http://localhost:9000/docs` +- **Health Check**: `http://localhost:9000/health` ## Containers -This project includes full Docker support with Docker Compose for easy deployment. - -### Build the Docker image - -```bash -docker compose build -``` - -### Start the application +### Build and Start ```bash docker compose up ``` -> ๐Ÿ’ก On first run, the container copies a pre-seeded SQLite database into a persistent volume. On subsequent runs, that volume is reused and the data is preserved. +> ๐Ÿ’ก **Note:** On first run, the container copies a pre-seeded SQLite database into a persistent volume. On subsequent runs, that volume is reused and the data is preserved. -### Stop the application +### Stop ```bash docker compose down ``` -### Reset the database +### Reset Database To remove the volume and reinitialize the database from the built-in seed file: @@ -307,61 +206,6 @@ To remove the volume and reinitialize the database from the built-in seed file: docker compose down -v ``` -The containerized application runs on port 9000 and includes health checks that monitor the `/health` endpoint. - -## Releases - -This project uses famous football coaches as release codenames โ™Ÿ๏ธ, following an A-Z naming pattern. - -### Release Naming Convention - -Releases follow the pattern: `v{SEMVER}-{COACH}` (e.g., `v1.0.0-ancelotti`) - -- **Semantic Version**: Standard versioning (MAJOR.MINOR.PATCH) -- **Coach Name**: Alphabetically ordered codename from the [famous coach list](CHANGELOG.md) - -### Create a Release - -To create a new release, follow this workflow: - -#### 1. Create a Release Branch - -```bash -git checkout -b release/v1.0.0-ancelotti -``` - -#### 2. Update CHANGELOG.md - -Document your changes in [CHANGELOG.md](CHANGELOG.md): - -```bash -# Move items from [Unreleased] to new release section -# Example: [1.0.0 - Ancelotti] - 2026-02-15 -git add CHANGELOG.md -git commit -m "docs(changelog): release v1.0.0 Ancelotti" -git push origin release/v1.0.0-ancelotti -``` - -#### 3. Create and Push Tag - -Then create and push the version tag: - -```bash -git tag -a v1.0.0-ancelotti -m "Release 1.0.0 - Ancelotti" -git push origin v1.0.0-ancelotti -``` - -#### 4. Automated CD Workflow - -This triggers the CD workflow which automatically: - -1. Validates the coach name -2. Builds and tests the project with coverage -3. Publishes Docker images to GitHub Container Registry with three tags -4. Creates a GitHub Release with auto-generated changelog from commits - -> ๐Ÿ’ก Always update CHANGELOG.md before creating the tag. See [CHANGELOG.md](CHANGELOG.md) for detailed release instructions. - ### Pull Docker Images Each release publishes multiple tags for flexibility: @@ -377,21 +221,45 @@ docker pull ghcr.io/nanotaboada/python-samples-fastapi-restful:ancelotti docker pull ghcr.io/nanotaboada/python-samples-fastapi-restful:latest ``` -> ๐Ÿ’ก See [CHANGELOG.md](CHANGELOG.md) for the complete coach list (A-Z) and release history. - ## Environment Variables -The application can be configured using the following environment variables (declared in [`compose.yaml`](compose.yaml)): - ```bash # Database storage path (default: ./storage/players-sqlite3.db) -# In Docker: /storage/players-sqlite3.db STORAGE_PATH=./storage/players-sqlite3.db # Python output buffering: set to 1 for real-time logs in Docker PYTHONUNBUFFERED=1 ``` +## Contributing + +Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details on: + +- Code of Conduct +- Development workflow and best practices +- Commit message conventions (Conventional Commits) +- Pull request process and requirements + +**Key guidelines:** + +- Follow [Conventional Commits](https://www.conventionalcommits.org/) for commit messages +- Ensure all tests pass (`uv run pytest`) +- Run `uv run black .` before committing +- Keep changes small and focused +- Review `.github/copilot-instructions.md` for architectural patterns + +**Testing:** + +Run the test suite with pytest: + +```bash +# Run all tests +uv run pytest + +# Run tests with coverage report +uv run pytest --cov=./ --cov-report=term +``` + ## Command Summary | Command | Description | @@ -399,7 +267,6 @@ PYTHONUNBUFFERED=1 | `uv run uvicorn main:app --reload --port 9000` | Start development server | | `uv pip install --group dev` | Install all dependencies | | `uv run pytest` | Run all tests | -| `uv run pytest -v` | Run tests with verbose output | | `uv run pytest --cov=./ --cov-report=term` | Run tests with coverage | | `uv run flake8 .` | Lint code | | `uv run black --check .` | Check formatting | @@ -408,17 +275,9 @@ PYTHONUNBUFFERED=1 | `docker compose up` | Start Docker container | | `docker compose down` | Stop Docker container | | `docker compose down -v` | Stop and remove Docker volume | - -## Contributing - -Contributions are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on the code of conduct and the process for submitting pull requests. - -**Key guidelines:** - -- Follow [Conventional Commits](https://www.conventionalcommits.org/) for commit messages -- Ensure all tests pass (`uv run pytest`) -- Run `uv run black .` before committing -- Keep changes small and focused +| **AI Commands** | | +| `/pre-commit` | Runs linting, tests, and quality checks before committing | +| `/pre-release` | Runs pre-release validation workflow | ## Legal diff --git a/RELEASES.md b/RELEASES.md new file mode 100644 index 0000000..c84dedc --- /dev/null +++ b/RELEASES.md @@ -0,0 +1,74 @@ +# Releases + +Releases follow the pattern `v{SEMVER}-{COACH}` (e.g., `v1.0.0-ancelotti`). Codenames are drawn alphabetically from the [famous coach list](CHANGELOG.md). + +## Workflow + +### 1. Create a Release Branch + +Branch protection prevents direct pushes to `master`, so all release prep goes through a PR: + +```bash +git checkout master && git pull +git checkout -b release/v1.0.0-ancelotti +``` + +### 2. Update CHANGELOG.md + +Move items from `[Unreleased]` to a new release section in [CHANGELOG.md](CHANGELOG.md), then commit and push the branch: + +```bash +# Move items from [Unreleased] to new release section +# Example: [1.0.0 - Ancelotti] - 2026-XX-XX +git add CHANGELOG.md +git commit -m "docs(changelog): prepare release notes for v1.0.0-ancelotti" +git push origin release/v1.0.0-ancelotti +``` + +### 3. Merge the Release PR + +Open a pull request from `release/v1.0.0-ancelotti` into `master` and merge it. The tag must be created **after** the merge so it points to the correct commit on `master`. + +### Pre-release Checklist + +Before creating the tag, verify all of the following: + +- [ ] `CHANGELOG.md` `[Unreleased]` section is moved to a new versioned release entry +- [ ] Release PR is merged into `master` +- [ ] `uv run pytest` passes +- [ ] Coach name is valid and follows alphabetical order (see "Release Naming Convention" in [CHANGELOG.md](CHANGELOG.md)) +- [ ] All CI checks on `master` are green + +### 4. Create and Push Tag + +After the PR is merged, pull `master` and create the annotated tag: + +```bash +git checkout master && git pull +git tag -a v1.0.0-ancelotti -m "Release 1.0.0 - Ancelotti" +git push origin v1.0.0-ancelotti +``` + +### 5. Automated CD Workflow + +Pushing the tag triggers the CD workflow which automatically: + +1. Validates tag format (semver and coach name) +2. Builds and tests the project with coverage +3. Publishes Docker images to GitHub Container Registry with three tags +4. Creates a GitHub Release with auto-generated changelog from commits + +## Docker Pull + +Each release publishes multiple tags for flexibility: + +```bash +# By semantic version (recommended for production) +docker pull ghcr.io/nanotaboada/python-samples-fastapi-restful:1.0.0 + +# By coach name (memorable alternative) +docker pull ghcr.io/nanotaboada/python-samples-fastapi-restful:ancelotti + +# Latest release +docker pull ghcr.io/nanotaboada/python-samples-fastapi-restful:latest +```