Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .codacy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

exclude_paths:
- "assets/**/*"
- "database/**/*"
- "databases/**/*"
- "models/**/*"
- "postman_collections/**/*"
- "schemas/**/*"
Expand Down
7 changes: 5 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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 .
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ comment:
# https://docs.codecov.com/docs/ignoring-paths
ignore:
- "^assets/.*"
- "^database/.*"
- "^databases/.*"
- "^models/.*"
- "^postman_collections/.*"
- "^schemas/.*"
Expand Down
Empty file added databases/__init__.py
Empty file.
12 changes: 9 additions & 3 deletions database/player_database.py → databases/player_database.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down
13 changes: 9 additions & 4 deletions main.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -14,7 +19,7 @@

@asynccontextmanager
async def lifespan(_: FastAPI) -> AsyncIterator[None]:
""""
"""
Lifespan event handler for FastAPI.
"""
logger.info("Lifespan event handler execution complete.")
Expand Down
10 changes: 7 additions & 3 deletions models/player_model.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down
9 changes: 5 additions & 4 deletions routes/health_route.py
Original file line number Diff line number Diff line change
@@ -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"])
Expand Down
24 changes: 19 additions & 5 deletions routes/player_route.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down
11 changes: 7 additions & 4 deletions schemas/player_schema.py
Original file line number Diff line number Diff line change
@@ -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):
Expand Down
17 changes: 13 additions & 4 deletions services/player_service.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down
9 changes: 9 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
21 changes: 17 additions & 4 deletions tests/test_main.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down