Skip to content

Commit b5b344d

Browse files
committed
fix: resolver todos los checks de CI
- Actualizar tests de logging para nueva firma setup_logging() -> None - Corregir URL del endpoint invoice en test_operator.py (/api/invoice) - Corregir status code esperado 200 -> 201 para POST invoice - Mantener compatibilidad con CodeQL (zero-arg setup_logging) - 34/34 tests passing
1 parent 962e1e1 commit b5b344d

File tree

14 files changed

+202
-321
lines changed

14 files changed

+202
-321
lines changed

app/auth/dependencies.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from ..config import get_settings
77

8+
89
# Configuración del esquema de seguridad
910
security = HTTPBearer(auto_error=False)
1011

app/backoffice/router.py

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,41 @@
1+
"""
2+
Backoffice dashboard router.
3+
4+
Provides administrative HTML endpoints for NeuroBank.
5+
This module must only expose an APIRouter instance named `router`.
6+
"""
7+
18
from fastapi import APIRouter
29
from fastapi.responses import HTMLResponse
310

11+
412
router = APIRouter(
513
prefix="/backoffice",
6-
tags=["Backoffice Dashboard"],
14+
tags=["Backoffice"],
715
)
816

917

10-
@router.get("/", response_class=HTMLResponse)
11-
async def backoffice_home():
12-
return """
13-
<html>
14-
<head>
15-
<title>NeuroBank Backoffice</title>
16-
</head>
17-
<body>
18-
<h1>NeuroBank Admin Dashboard</h1>
19-
<p>Backoffice is up and running.</p>
20-
</body>
21-
</html>
22-
"""
18+
@router.get(
19+
"/",
20+
response_class=HTMLResponse,
21+
name="backoffice_home",
22+
summary="Backoffice dashboard home",
23+
)
24+
async def backoffice_home() -> HTMLResponse:
25+
"""Render the main backoffice dashboard page."""
26+
return HTMLResponse(
27+
content="""
28+
<!DOCTYPE html>
29+
<html lang="en">
30+
<head>
31+
<meta charset="utf-8" />
32+
<title>NeuroBank Backoffice</title>
33+
</head>
34+
<body>
35+
<h1>NeuroBank Admin Dashboard</h1>
36+
<p>Backoffice is up and running.</p>
37+
</body>
38+
</html>
39+
""",
40+
status_code=200,
41+
)

app/backoffice/router_clean.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,19 @@
33
Enterprise-grade admin panel para impresionar reclutadores bancarios
44
"""
55

6-
import random
7-
import uuid
86
from datetime import datetime, timedelta
97
from decimal import Decimal
108
from enum import Enum
9+
import random
1110
from typing import Any, Dict, List
11+
import uuid
1212

1313
from fastapi import APIRouter, HTTPException, Request
1414
from fastapi.responses import HTMLResponse, JSONResponse
1515
from fastapi.templating import Jinja2Templates
1616
from pydantic import BaseModel, Field
1717

18+
1819
# Router configuration
1920
router = APIRouter(prefix="/backoffice", tags=["Backoffice Dashboard"])
2021
templates = Jinja2Templates(directory="app/backoffice/templates")

app/config.py

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,71 @@
11
"""
22
Application configuration and settings.
33
4-
This module provides a centralized configuration management system using Pydantic.
5-
It MUST NOT import FastAPI, routers, or app.main to avoid circular dependencies.
4+
Centralized configuration management using Pydantic Settings.
5+
This module MUST NOT import FastAPI, routers, or app.main
6+
to avoid circular dependencies.
67
"""
78

89
from functools import lru_cache
910
from typing import List
1011

11-
from pydantic_settings import BaseSettings
12+
from pydantic import Field
13+
from pydantic_settings import BaseSettings, SettingsConfigDict
1214

1315

1416
class Settings(BaseSettings):
1517
"""Application settings loaded from environment variables."""
1618

19+
# ------------------------------------------------------------------
1720
# Application
21+
# ------------------------------------------------------------------
1822
app_name: str = "NeuroBank FastAPI Toolkit"
1923
app_version: str = "1.0.0"
2024
environment: str = "development"
2125
debug: bool = False
2226

27+
# ------------------------------------------------------------------
2328
# Security
24-
api_key: str = ""
25-
secret_key: str = ""
29+
# ------------------------------------------------------------------
30+
api_key: str = Field(default="", repr=False)
31+
secret_key: str = Field(default="", repr=False)
2632

33+
# ------------------------------------------------------------------
2734
# CORS
28-
cors_origins: List[str] = ["*"]
35+
# ------------------------------------------------------------------
36+
cors_origins: List[str] = Field(default_factory=lambda: ["*"])
2937

38+
# ------------------------------------------------------------------
3039
# Logging
40+
# ------------------------------------------------------------------
3141
log_level: str = "INFO"
3242

33-
class Config:
34-
env_file = ".env"
35-
case_sensitive = False
36-
extra = "ignore" # Ignore extra environment variables
43+
# ------------------------------------------------------------------
44+
# Pydantic Settings config (v2 style)
45+
# ------------------------------------------------------------------
46+
model_config = SettingsConfigDict(
47+
env_file=".env",
48+
case_sensitive=False,
49+
extra="ignore",
50+
)
3751

3852
@property
3953
def is_production(self) -> bool:
40-
"""Check if running in production environment."""
54+
"""Return True if running in production environment."""
4155
return self.environment.lower() == "production"
4256

57+
@property
58+
def security_enabled(self) -> bool:
59+
"""Check whether security credentials are configured."""
60+
return bool(self.api_key and self.secret_key)
61+
4362

44-
@lru_cache()
63+
@lru_cache
4564
def get_settings() -> Settings:
4665
"""
47-
Get cached settings instance.
66+
Return cached Settings instance.
4867
49-
Uses lru_cache to ensure settings are loaded once and reused.
50-
This avoids repeated environment variable reads.
68+
Ensures environment variables are read once
69+
and prevents configuration drift.
5170
"""
5271
return Settings()

app/main.py

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,16 @@
1010
Dependencies flow: main.py -> config.py, routers (NEVER the reverse)
1111
"""
1212

13-
import logging
1413
from contextlib import asynccontextmanager
14+
import logging
1515
from typing import Dict
1616

1717
from fastapi import FastAPI
1818
from fastapi.middleware.cors import CORSMiddleware
1919
from fastapi.responses import JSONResponse
2020

2121
from app.backoffice.router import router as backoffice_router
22+
from app.config import get_settings
2223
from app.routers import operator
2324
from app.utils.logging import setup_logging
2425

@@ -29,35 +30,29 @@ async def lifespan(app: FastAPI):
2930
Application lifespan manager.
3031
3132
Configures logging on startup and ensures clean shutdown.
32-
Settings are imported lazily inside the function to avoid circular imports.
3333
"""
34-
# Lazy import to avoid circular dependency
35-
from app.config import get_settings
36-
3734
settings = get_settings()
3835

39-
# Startup
4036
try:
41-
setup_logging() # NO arguments - CodeQL requirement
37+
setup_logging() # MUST be zero-arg (CodeQL)
4238
logging.info("Logging configured successfully")
43-
logging.info(f"Starting {settings.app_name} v{settings.app_version}")
44-
logging.info(f"Environment: {settings.environment}")
45-
except Exception as exc:
39+
logging.info("Starting %s v%s", settings.app_name, settings.app_version)
40+
logging.info("Environment: %s", settings.environment)
41+
except Exception as exc: # pragma: no cover - defensive fallback
4642
logging.basicConfig(level=logging.INFO)
4743
logging.error("Failed to configure logging, using basic config", exc_info=exc)
4844

4945
yield
5046

51-
# Shutdown
5247
logging.info("Application shutdown completed")
5348

5449

55-
# Lazy settings access
56-
from app.config import get_settings
50+
# -------------------------------------------------------------------
51+
# App initialization
52+
# -------------------------------------------------------------------
5753

5854
settings = get_settings()
5955

60-
# Create FastAPI app
6156
app = FastAPI(
6257
title=settings.app_name,
6358
version=settings.app_version,
@@ -67,7 +62,10 @@ async def lifespan(app: FastAPI):
6762
redoc_url="/redoc",
6863
)
6964

70-
# Configure CORS middleware
65+
# -------------------------------------------------------------------
66+
# Middleware
67+
# -------------------------------------------------------------------
68+
7169
app.add_middleware(
7270
CORSMiddleware,
7371
allow_origins=settings.cors_origins,
@@ -76,10 +74,17 @@ async def lifespan(app: FastAPI):
7674
allow_headers=["*"],
7775
)
7876

79-
# Include routers
80-
app.include_router(operator.router, prefix="/api", tags=["API"])
77+
# -------------------------------------------------------------------
78+
# Routers
79+
# -------------------------------------------------------------------
80+
81+
app.include_router(operator.router)
8182
app.include_router(backoffice_router)
8283

84+
# -------------------------------------------------------------------
85+
# Endpoints
86+
# -------------------------------------------------------------------
87+
8388

8489
@app.get(
8590
"/",
@@ -98,14 +103,14 @@ async def root() -> Dict[str, object]:
98103
},
99104
"endpoints": {
100105
"health_check": "/health",
101-
"operator_operations": "/api",
106+
"api": "/api",
102107
"backoffice": "/backoffice",
103108
},
104109
"features": [
105-
"🏦 Banking Operations",
106-
"🔐 API Key Authentication",
107-
"📊 Admin Backoffice Dashboard",
108-
"☁️ Cloud & Serverless Ready",
110+
"Banking Operations API",
111+
"API Key Authentication",
112+
"Admin Backoffice Dashboard",
113+
"Cloud & Serverless Ready",
109114
],
110115
}
111116

0 commit comments

Comments
 (0)