From 52b47c724e3407513cab1c1ec384d7ffe24d4a25 Mon Sep 17 00:00:00 2001 From: Dimitri Zuave Date: Mon, 2 Jun 2025 17:45:50 -0300 Subject: [PATCH 1/2] Fix: issue #385 - Resctructured to follow bigger apps layout --- Dockerfile | 16 ++--- {databases => app}/__init__.py | 0 {models => app/databases}/__init__.py | 0 .../databases/player.py | 0 main.py => app/main.py | 6 +- {routes => app/models}/__init__.py | 0 .../player_model.py => app/models/player.py | 0 {schemas => app/routes}/__init__.py | 0 .../health_route.py => app/routes/health.py | 4 +- .../player_route.py => app/routes/player.py | 60 +++++++++--------- {services => app/schemas}/__init__.py | 0 .../player_schema.py => app/schemas/player.py | 2 +- app/services/__init__.py | 0 .../services/player.py | 4 +- storage/players-sqlite3.db | Bin 16384 -> 16384 bytes tests/conftest.py | 2 +- 16 files changed, 48 insertions(+), 46 deletions(-) rename {databases => app}/__init__.py (100%) rename {models => app/databases}/__init__.py (100%) rename databases/player_database.py => app/databases/player.py (100%) rename main.py => app/main.py (87%) rename {routes => app/models}/__init__.py (100%) rename models/player_model.py => app/models/player.py (100%) rename {schemas => app/routes}/__init__.py (100%) rename routes/health_route.py => app/routes/health.py (84%) rename routes/player_route.py => app/routes/player.py (82%) rename {services => app/schemas}/__init__.py (100%) rename schemas/player_schema.py => app/schemas/player.py (97%) create mode 100644 app/services/__init__.py rename services/player_service.py => app/services/player.py (98%) diff --git a/Dockerfile b/Dockerfile index d6e6c52..4e23b1c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -45,13 +45,13 @@ COPY requirements.txt . RUN pip install --no-cache-dir --no-index --find-links /app/wheelhouse -r requirements.txt && \ rm -rf /app/wheelhouse -# Copy application source code -COPY main.py ./ -COPY databases/ ./databases/ -COPY models/ ./models/ -COPY routes/ ./routes/ -COPY schemas/ ./schemas/ -COPY services/ ./services/ +# Copy application source code +COPY app/main.py ./ +COPY app/databases/ ./databases/ +COPY app/models/ ./models/ +COPY app/routes/ ./routes/ +COPY app/schemas/ ./schemas/ +COPY app/services/ ./services/ # https://rules.sonarsource.com/docker/RSPEC-6504/ @@ -78,4 +78,4 @@ HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \ CMD ["./healthcheck.sh"] ENTRYPOINT ["./entrypoint.sh"] -CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "9000"] +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "9000"] diff --git a/databases/__init__.py b/app/__init__.py similarity index 100% rename from databases/__init__.py rename to app/__init__.py diff --git a/models/__init__.py b/app/databases/__init__.py similarity index 100% rename from models/__init__.py rename to app/databases/__init__.py diff --git a/databases/player_database.py b/app/databases/player.py similarity index 100% rename from databases/player_database.py rename to app/databases/player.py diff --git a/main.py b/app/main.py similarity index 87% rename from main.py rename to app/main.py index a1167f0..ad104aa 100644 --- a/main.py +++ b/app/main.py @@ -12,7 +12,7 @@ import logging from typing import AsyncIterator from fastapi import FastAPI -from routes import player_route, health_route +from app.routes import player, health # https://github.com/encode/uvicorn/issues/562 UVICORN_LOGGER = "uvicorn.error" @@ -35,5 +35,5 @@ async def lifespan(_: FastAPI) -> AsyncIterator[None]: version="1.0.0", ) -app.include_router(player_route.api_router) -app.include_router(health_route.api_router) +app.include_router(player.router) +app.include_router(health.router) diff --git a/routes/__init__.py b/app/models/__init__.py similarity index 100% rename from routes/__init__.py rename to app/models/__init__.py diff --git a/models/player_model.py b/app/models/player.py similarity index 100% rename from models/player_model.py rename to app/models/player.py diff --git a/schemas/__init__.py b/app/routes/__init__.py similarity index 100% rename from schemas/__init__.py rename to app/routes/__init__.py diff --git a/routes/health_route.py b/app/routes/health.py similarity index 84% rename from routes/health_route.py rename to app/routes/health.py index 0db6333..6f06fbc 100644 --- a/routes/health_route.py +++ b/app/routes/health.py @@ -7,10 +7,10 @@ from fastapi import APIRouter -api_router = APIRouter() +router = APIRouter() -@api_router.get("/health", tags=["Health"]) +@router.get("/health", tags=["Health"]) async def health_check(): """ Simple health check endpoint. diff --git a/routes/player_route.py b/app/routes/player.py similarity index 82% rename from routes/player_route.py rename to app/routes/player.py index ddeecc8..68947b2 100644 --- a/routes/player_route.py +++ b/app/routes/player.py @@ -22,20 +22,22 @@ from sqlalchemy.ext.asyncio import AsyncSession from aiocache import SimpleMemoryCache -from databases.player_database import generate_async_session -from models.player_model import PlayerModel -from services import player_service +from app.databases.player import generate_async_session +from app.models.player import PlayerModel +from app.services import player -api_router = APIRouter() +router = APIRouter() simple_memory_cache = SimpleMemoryCache() CACHE_KEY = "players" CACHE_TTL = 600 # 10 minutes +PLAYER_TITLE = "The ID of the Player" + # POST ------------------------------------------------------------------------- -@api_router.post( +@router.post( "/players/", status_code=status.HTTP_201_CREATED, summary="Creates a new Player", @@ -56,17 +58,17 @@ async def post_async( Raises: HTTPException: HTTP 409 Conflict error if the Player already exists. """ - player = await player_service.retrieve_by_id_async(async_session, player_model.id) - if player: + player_res = await player.retrieve_by_id_async(async_session, player_model.id) + if player_res: raise HTTPException(status_code=status.HTTP_409_CONFLICT) - await player_service.create_async(async_session, player_model) + await player.create_async(async_session, player_model) await simple_memory_cache.clear(CACHE_KEY) # GET -------------------------------------------------------------------------- -@api_router.get( +@router.get( "/players/", response_model=List[PlayerModel], status_code=status.HTTP_200_OK, @@ -88,13 +90,13 @@ async def get_all_async( players = await simple_memory_cache.get(CACHE_KEY) response.headers["X-Cache"] = "HIT" if not players: - players = await player_service.retrieve_all_async(async_session) + players = await player.retrieve_all_async(async_session) await simple_memory_cache.set(CACHE_KEY, players, ttl=CACHE_TTL) response.headers["X-Cache"] = "MISS" return players -@api_router.get( +@router.get( "/players/{player_id}", response_model=PlayerModel, status_code=status.HTTP_200_OK, @@ -102,7 +104,7 @@ async def get_all_async( tags=["Players"], ) async def get_by_id_async( - player_id: int = Path(..., title="The ID of the Player"), + player_id: int = Path(..., title=PLAYER_TITLE), async_session: AsyncSession = Depends(generate_async_session), ): """ @@ -119,13 +121,13 @@ async def get_by_id_async( HTTPException: Not found error if the Player with the specified ID does not exist. """ - player = await player_service.retrieve_by_id_async(async_session, player_id) - if not player: + player_res = await player.retrieve_by_id_async(async_session, player_id) + if not player_res: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) - return player + return player_res -@api_router.get( +@router.get( "/players/squadnumber/{squad_number}", response_model=PlayerModel, status_code=status.HTTP_200_OK, @@ -150,25 +152,25 @@ async def get_by_squad_number_async( HTTPException: HTTP 404 Not Found error if the Player with the specified Squad Number does not exist. """ - player = await player_service.retrieve_by_squad_number_async( + player_res = await player.retrieve_by_squad_number_async( async_session, squad_number ) - if not player: + if not player_res: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) - return player + return player_res # PUT -------------------------------------------------------------------------- -@api_router.put( +@router.put( "/players/{player_id}", status_code=status.HTTP_204_NO_CONTENT, summary="Updates an existing Player", tags=["Players"], ) async def put_async( - player_id: int = Path(..., title="The ID of the Player"), + player_id: int = Path(..., title=PLAYER_TITLE), player_model: PlayerModel = Body(...), async_session: AsyncSession = Depends(generate_async_session), ): @@ -185,24 +187,24 @@ async def put_async( HTTPException: HTTP 404 Not Found error if the Player with the specified ID does not exist. """ - player = await player_service.retrieve_by_id_async(async_session, player_id) - if not player: + player_res = await player.retrieve_by_id_async(async_session, player_id) + if not player_res: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) - await player_service.update_async(async_session, player_model) + await player.update_async(async_session, player_model) await simple_memory_cache.clear(CACHE_KEY) # DELETE ----------------------------------------------------------------------- -@api_router.delete( +@router.delete( "/players/{player_id}", status_code=status.HTTP_204_NO_CONTENT, summary="Deletes an existing Player", tags=["Players"], ) async def delete_async( - player_id: int = Path(..., title="The ID of the Player"), + player_id: int = Path(..., title=PLAYER_TITLE), async_session: AsyncSession = Depends(generate_async_session), ): """ @@ -216,8 +218,8 @@ async def delete_async( HTTPException: HTTP 404 Not Found error if the Player with the specified ID does not exist. """ - player = await player_service.retrieve_by_id_async(async_session, player_id) - if not player: + player_res = await player.retrieve_by_id_async(async_session, player_id) + if not player_res: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) - await player_service.delete_async(async_session, player_id) + await player.delete_async(async_session, player_id) await simple_memory_cache.clear(CACHE_KEY) diff --git a/services/__init__.py b/app/schemas/__init__.py similarity index 100% rename from services/__init__.py rename to app/schemas/__init__.py diff --git a/schemas/player_schema.py b/app/schemas/player.py similarity index 97% rename from schemas/player_schema.py rename to app/schemas/player.py index a3524a3..62888a8 100644 --- a/schemas/player_schema.py +++ b/app/schemas/player.py @@ -7,7 +7,7 @@ """ from sqlalchemy import Column, String, Integer, Boolean -from databases.player_database import Base +from app.databases.player import Base class Player(Base): diff --git a/app/services/__init__.py b/app/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/services/player_service.py b/app/services/player.py similarity index 98% rename from services/player_service.py rename to app/services/player.py index 5c9e894..d1f1f19 100644 --- a/services/player_service.py +++ b/app/services/player.py @@ -15,8 +15,8 @@ from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.exc import SQLAlchemyError -from models.player_model import PlayerModel -from schemas.player_schema import Player +from app.models.player import PlayerModel +from app.schemas.player import Player # Create ----------------------------------------------------------------------- diff --git a/storage/players-sqlite3.db b/storage/players-sqlite3.db index 07bb6204a409da4bc4484a291fc8c3a3a23dcaae..03c49b9973407c7638a604fa9d75b22cb9fd107a 100644 GIT binary patch delta 175 zcmZo@U~Fh$oFL68H&Mo!RgOWgtb1e1Tz*denGD?A8yWZ)Y~~X1;*O2x;bD-Ll(uCw z6qJ_c@JUU~ODW2CNzBVk%?U^>N=->EHng-f(KRsBHL?sbFt7q6Jp%)SC{~x$wA8%f z%(7Gk-^`RWpoSC|U&mmDp#0p#;MAhbR0YS)8>L?`i!v}UuuIFz$1(CS$nwhDGAasc XYI1H~C(o_G#>&jd$jUQ$zr7;>_82VD delta 57 zcmV-90LK4-fB}Gj0gxL37m*x81s4D=Zo08#oev2B000XMu>cR5vkDMO3z3jrv#~2r P1hOExACXW7lfOSf&tefq diff --git a/tests/conftest.py b/tests/conftest.py index 655690b..c7607ec 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,7 @@ import warnings import pytest from fastapi.testclient import TestClient -from main import app +from app.main import app # Suppress the DeprecationWarning from httpx warnings.filterwarnings("ignore", category=DeprecationWarning) From fad6cc72c16977d9299c9be4f53bd42498061407 Mon Sep 17 00:00:00 2001 From: Dimitri Zuave Date: Thu, 5 Jun 2025 12:06:16 -0300 Subject: [PATCH 2/2] Changed app folder to src to avoid folder name conflict --- Dockerfile | 14 +++++++------- {app => src}/__init__.py | 0 {app => src}/databases/__init__.py | 0 {app => src}/databases/player.py | 0 {app => src}/main.py | 2 +- {app => src}/models/__init__.py | 0 {app => src}/models/player.py | 0 {app => src}/routes/__init__.py | 0 {app => src}/routes/health.py | 0 {app => src}/routes/player.py | 6 +++--- {app => src}/schemas/__init__.py | 0 {app => src}/schemas/player.py | 2 +- {app => src}/services/__init__.py | 0 {app => src}/services/player.py | 4 ++-- tests/conftest.py | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) rename {app => src}/__init__.py (100%) rename {app => src}/databases/__init__.py (100%) rename {app => src}/databases/player.py (100%) rename {app => src}/main.py (96%) rename {app => src}/models/__init__.py (100%) rename {app => src}/models/player.py (100%) rename {app => src}/routes/__init__.py (100%) rename {app => src}/routes/health.py (100%) rename {app => src}/routes/player.py (98%) rename {app => src}/schemas/__init__.py (100%) rename {app => src}/schemas/player.py (97%) rename {app => src}/services/__init__.py (100%) rename {app => src}/services/player.py (98%) diff --git a/Dockerfile b/Dockerfile index 4e23b1c..92e3a97 100644 --- a/Dockerfile +++ b/Dockerfile @@ -46,12 +46,12 @@ RUN pip install --no-cache-dir --no-index --find-links /app/wheelhouse -r requir rm -rf /app/wheelhouse # Copy application source code -COPY app/main.py ./ -COPY app/databases/ ./databases/ -COPY app/models/ ./models/ -COPY app/routes/ ./routes/ -COPY app/schemas/ ./schemas/ -COPY app/services/ ./services/ +COPY src/main.py ./src/ +COPY src/databases/ ./src/databases/ +COPY src/models/ ./src/models/ +COPY src/routes/ ./src/routes/ +COPY src/schemas/ ./src/schemas/ +COPY src/services/ ./src/services/ # https://rules.sonarsource.com/docker/RSPEC-6504/ @@ -78,4 +78,4 @@ HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \ CMD ["./healthcheck.sh"] ENTRYPOINT ["./entrypoint.sh"] -CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "9000"] +CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "9000"] diff --git a/app/__init__.py b/src/__init__.py similarity index 100% rename from app/__init__.py rename to src/__init__.py diff --git a/app/databases/__init__.py b/src/databases/__init__.py similarity index 100% rename from app/databases/__init__.py rename to src/databases/__init__.py diff --git a/app/databases/player.py b/src/databases/player.py similarity index 100% rename from app/databases/player.py rename to src/databases/player.py diff --git a/app/main.py b/src/main.py similarity index 96% rename from app/main.py rename to src/main.py index ad104aa..a0d64bb 100644 --- a/app/main.py +++ b/src/main.py @@ -12,7 +12,7 @@ import logging from typing import AsyncIterator from fastapi import FastAPI -from app.routes import player, health +from src.routes import player, health # https://github.com/encode/uvicorn/issues/562 UVICORN_LOGGER = "uvicorn.error" diff --git a/app/models/__init__.py b/src/models/__init__.py similarity index 100% rename from app/models/__init__.py rename to src/models/__init__.py diff --git a/app/models/player.py b/src/models/player.py similarity index 100% rename from app/models/player.py rename to src/models/player.py diff --git a/app/routes/__init__.py b/src/routes/__init__.py similarity index 100% rename from app/routes/__init__.py rename to src/routes/__init__.py diff --git a/app/routes/health.py b/src/routes/health.py similarity index 100% rename from app/routes/health.py rename to src/routes/health.py diff --git a/app/routes/player.py b/src/routes/player.py similarity index 98% rename from app/routes/player.py rename to src/routes/player.py index 68947b2..92632ee 100644 --- a/app/routes/player.py +++ b/src/routes/player.py @@ -22,9 +22,9 @@ from sqlalchemy.ext.asyncio import AsyncSession from aiocache import SimpleMemoryCache -from app.databases.player import generate_async_session -from app.models.player import PlayerModel -from app.services import player +from src.databases.player import generate_async_session +from src.models.player import PlayerModel +from src.services import player router = APIRouter() simple_memory_cache = SimpleMemoryCache() diff --git a/app/schemas/__init__.py b/src/schemas/__init__.py similarity index 100% rename from app/schemas/__init__.py rename to src/schemas/__init__.py diff --git a/app/schemas/player.py b/src/schemas/player.py similarity index 97% rename from app/schemas/player.py rename to src/schemas/player.py index 62888a8..12e7295 100644 --- a/app/schemas/player.py +++ b/src/schemas/player.py @@ -7,7 +7,7 @@ """ from sqlalchemy import Column, String, Integer, Boolean -from app.databases.player import Base +from src.databases.player import Base class Player(Base): diff --git a/app/services/__init__.py b/src/services/__init__.py similarity index 100% rename from app/services/__init__.py rename to src/services/__init__.py diff --git a/app/services/player.py b/src/services/player.py similarity index 98% rename from app/services/player.py rename to src/services/player.py index d1f1f19..f93b09a 100644 --- a/app/services/player.py +++ b/src/services/player.py @@ -15,8 +15,8 @@ from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.exc import SQLAlchemyError -from app.models.player import PlayerModel -from app.schemas.player import Player +from src.models.player import PlayerModel +from src.schemas.player import Player # Create ----------------------------------------------------------------------- diff --git a/tests/conftest.py b/tests/conftest.py index c7607ec..574ca5d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,7 @@ import warnings import pytest from fastapi.testclient import TestClient -from app.main import app +from src.main import app # Suppress the DeprecationWarning from httpx warnings.filterwarnings("ignore", category=DeprecationWarning)