diff --git a/.codacy.yml b/.codacy.yml index 2c9ccd2..16ef4c1 100644 --- a/.codacy.yml +++ b/.codacy.yml @@ -2,7 +2,7 @@ exclude_paths: - "assets/**/*" - - "database/**/*" + - "databases/**/*" - "models/**/*" - "postman_collections/**/*" - "schemas/**/*" diff --git a/Dockerfile b/Dockerfile index 20975d3..736bc88 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,7 +38,7 @@ COPY README.md ./ COPY assets ./assets # Copy pre-built wheels from builder -COPY --from=builder /app/wheelhouse /app/wheelhouse +COPY --from=builder /app/wheelhouse /app/wheelhouse # Install dependencies COPY requirements.txt . @@ -53,9 +53,12 @@ COPY routes ./routes COPY schemas ./schemas COPY services ./services -# Copy entrypoint script and image-bundled, pre-seeded SQLite database +# https://rules.sonarsource.com/docker/RSPEC-6504/ + +# Copy entrypoint and healthcheck scripts COPY --chmod=755 scripts/entrypoint.sh ./entrypoint.sh COPY --chmod=755 scripts/healthcheck.sh ./healthcheck.sh +# Copy pre-seeded SQLite database as init bundle COPY --chmod=755 storage ./docker-compose # Add non-root user and make volume mount point writable diff --git a/codecov.yml b/codecov.yml index e58090c..87c6853 100644 --- a/codecov.yml +++ b/codecov.yml @@ -40,7 +40,7 @@ comment: # https://docs.codecov.com/docs/ignoring-paths ignore: - "^assets/.*" - - "^database/.*" + - "^databases/.*" - "^models/.*" - "^postman_collections/.*" - "^schemas/.*" diff --git a/databases/__init__.py b/databases/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/database/player_database.py b/databases/player_database.py similarity index 71% rename from database/player_database.py rename to databases/player_database.py index 404871a..a18607d 100644 --- a/database/player_database.py +++ b/databases/player_database.py @@ -1,7 +1,13 @@ -# ------------------------------------------------------------------------------ -# Database -# ------------------------------------------------------------------------------ +""" +Database setup and session management for async SQLAlchemy with SQLite. +- Configures the async database engine using `aiosqlite` driver. +- Creates an async sessionmaker for ORM operations. +- Defines the declarative base class for model definitions. +- Provides an async generator dependency to yield database sessions. + +The `STORAGE_PATH` environment variable controls the SQLite file location. +""" import logging import os from typing import AsyncGenerator diff --git a/main.py b/main.py index 85fbc3a..14e912d 100644 --- a/main.py +++ b/main.py @@ -1,7 +1,12 @@ -# ------------------------------------------------------------------------------ -# Main -# ------------------------------------------------------------------------------ +""" +Main application module for the FastAPI RESTful API. +- Sets up the FastAPI app with metadata (title, description, version). +- Defines the lifespan event handler for app startup/shutdown logging. +- Includes API routers for player and health endpoints. + +This serves as the entry point for running the API server. +""" from contextlib import asynccontextmanager import logging from typing import AsyncIterator @@ -14,7 +19,7 @@ @asynccontextmanager async def lifespan(_: FastAPI) -> AsyncIterator[None]: - """" + """ Lifespan event handler for FastAPI. """ logger.info("Lifespan event handler execution complete.") diff --git a/models/player_model.py b/models/player_model.py index 4d80d49..7b3f419 100644 --- a/models/player_model.py +++ b/models/player_model.py @@ -1,7 +1,11 @@ -# ------------------------------------------------------------------------------ -# Model -# ------------------------------------------------------------------------------ +""" +Pydantic models defining the data schema for football players. +- `MainModel`: Base model with common config for camelCase aliasing. +- `PlayerModel`: Represents a football player with personal and team details. + +These models are used for data validation and serialization in the API. +""" from typing import Optional from pydantic import BaseModel, ConfigDict from pydantic.alias_generators import to_camel diff --git a/routes/health_route.py b/routes/health_route.py index c206abf..838f454 100644 --- a/routes/health_route.py +++ b/routes/health_route.py @@ -1,10 +1,11 @@ -# ------------------------------------------------------------------------------ -# Route -# ------------------------------------------------------------------------------ +""" +Health check API route. +Defines a simple endpoint to verify that the service is up and running. +Returns a JSON response with a "status" key set to "ok". +""" from fastapi import APIRouter - api_router = APIRouter() @api_router.get("/health", tags=["Health"]) diff --git a/routes/player_route.py b/routes/player_route.py index e570bba..39e55bf 100644 --- a/routes/player_route.py +++ b/routes/player_route.py @@ -1,13 +1,27 @@ -# ------------------------------------------------------------------------------ -# Route -# ------------------------------------------------------------------------------ - +""" +API routes for managing Player resources. + +Provides CRUD endpoints to create, read, update, and delete Player entities. + +Features: +- Caching with in-memory cache to optimize retrieval performance. +- Async database session dependency injection. +- Standard HTTP status codes and error handling. + +Endpoints: +- POST /players/ : Create a new Player. +- GET /players/ : Retrieve all Players. +- GET /players/{player_id} : Retrieve Player by ID. +- GET /players/squadnumber/{squad_number} : Retrieve Player by Squad Number. +- PUT /players/{player_id} : Update an existing Player. +- DELETE /players/{player_id} : Delete an existing Player. +""" from typing import List from fastapi import APIRouter, Body, Depends, HTTPException, status, Path, Response from sqlalchemy.ext.asyncio import AsyncSession from aiocache import SimpleMemoryCache -from database.player_database import generate_async_session +from databases.player_database import generate_async_session from models.player_model import PlayerModel from services import player_service diff --git a/schemas/player_schema.py b/schemas/player_schema.py index 857997c..f769809 100644 --- a/schemas/player_schema.py +++ b/schemas/player_schema.py @@ -1,9 +1,12 @@ -# ------------------------------------------------------------------------------ -# Schema -# ------------------------------------------------------------------------------ +""" +SQLAlchemy ORM model for the Player database table. +Defines the schema and columns corresponding to football player attributes. + +Used for async database CRUD operations in the application. +""" from sqlalchemy import Column, String, Integer, Boolean -from database.player_database import Base +from databases.player_database import Base class Player(Base): diff --git a/services/player_service.py b/services/player_service.py index d9029fa..5464e03 100644 --- a/services/player_service.py +++ b/services/player_service.py @@ -1,7 +1,16 @@ -# ------------------------------------------------------------------------------ -# Service -# ------------------------------------------------------------------------------ - +""" +Async CRUD operations for Player entities using SQLAlchemy ORM. + +Functions: +- create_async : Add a new Player to the database. +- retrieve_all_async : Fetch all Player records. +- retrieve_by_id_async : Fetch a Player by its ID. +- retrieve_by_squad_number_async : Fetch a Player by its Squad Number. +- update_async : Fully update an existing Player. +- delete_async : Remove a Player from the database. + +Handles SQLAlchemy exceptions with transaction rollback and logs errors. +""" from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.exc import SQLAlchemyError diff --git a/tests/conftest.py b/tests/conftest.py index 652a53f..655690b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -9,5 +9,14 @@ @pytest.fixture(scope="function") def client(): + """ + Creates a test client for the FastAPI app. + + This fixture provides a fresh instance of TestClient for each test function, + ensuring test isolation and a clean request context. + + Yields: + TestClient: A client instance for sending HTTP requests to the FastAPI app. + """ with TestClient(app) as test_client: yield test_client diff --git a/tests/test_main.py b/tests/test_main.py index bf9a600..9528352 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1,7 +1,20 @@ -# ------------------------------------------------------------------------------ -# Test -# ------------------------------------------------------------------------------ - +""" +Test suite for the /players/ API endpoints. + +Covers: +- GET /health/ +- GET /players/ +- GET /players/{player_id} +- GET /players/squadnumber/{squad_number} +- POST /players/ +- PUT /players/{player_id} +- DELETE /players/{player_id} + +Validates: +- Status codes, response bodies, headers (e.g., X-Cache) +- Handling of existing, nonexistent, and malformed requests +- Conflict and edge case behaviors +""" import json from tests.player_stub import existing_player, nonexistent_player, unknown_player