Skip to content

Commit f802356

Browse files
committed
fix: Add missing core files and fix CI/CD issues
- Add app/__init__.py and all missing __init__.py files - Create app/main.py with FastAPI application setup - Add auth/dependencies.py with API key authentication - Create utils/logging.py with proper logging configuration - Add lambda_function.py as AWS Lambda entry point - Fix test_operator.py to use Bearer authentication - Add test_main.py for health check endpoints - Update requirements.txt with python-json-logger dependency - All tests now passing (4/4) ✅
1 parent 41479ea commit f802356

File tree

14 files changed

+211
-4
lines changed

14 files changed

+211
-4
lines changed

app/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# NeuroBank FastAPI Toolkit

app/auth/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Authentication utilities

app/auth/dependencies.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from fastapi import HTTPException, Depends
2+
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
3+
import os
4+
from typing import Optional
5+
6+
# Configuración del esquema de seguridad
7+
security = HTTPBearer()
8+
9+
def get_api_key() -> str:
10+
"""Obtiene la API key desde las variables de entorno"""
11+
api_key = os.getenv("API_KEY")
12+
if not api_key:
13+
raise HTTPException(
14+
status_code=500,
15+
detail="API_KEY not configured"
16+
)
17+
return api_key
18+
19+
async def verify_api_key(credentials: HTTPAuthorizationCredentials = Depends(security)) -> str:
20+
"""
21+
Verifica que la API key proporcionada sea válida
22+
23+
Args:
24+
credentials: Credenciales HTTP Bearer
25+
26+
Returns:
27+
str: API key válida
28+
29+
Raises:
30+
HTTPException: Si la API key no es válida
31+
"""
32+
expected_api_key = get_api_key()
33+
34+
if credentials.credentials != expected_api_key:
35+
raise HTTPException(
36+
status_code=401,
37+
detail="Invalid API key",
38+
headers={"WWW-Authenticate": "Bearer"}
39+
)
40+
41+
return credentials.credentials

app/config.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import os
2+
from functools import lru_cache
3+
from pydantic import BaseSettings
4+
5+
class Settings(BaseSettings):
6+
"""Configuración de la aplicación"""
7+
8+
# API Configuration
9+
api_key: str = os.getenv("API_KEY", "default-test-key")
10+
app_name: str = "NeuroBank FastAPI Toolkit"
11+
app_version: str = "1.0.0"
12+
13+
# Server Configuration
14+
host: str = "0.0.0.0"
15+
port: int = 8000
16+
17+
# AWS Configuration
18+
aws_region: str = os.getenv("AWS_REGION", "eu-west-1")
19+
20+
# Logging Configuration
21+
log_level: str = os.getenv("LOG_LEVEL", "INFO")
22+
23+
class Config:
24+
env_file = ".env"
25+
26+
@lru_cache()
27+
def get_settings() -> Settings:
28+
"""Obtiene la configuración de la aplicación (cached)"""
29+
return Settings()

app/main.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
from fastapi import FastAPI
2+
from fastapi.middleware.cors import CORSMiddleware
3+
from fastapi.responses import JSONResponse
4+
import logging
5+
from .routers import operator
6+
from .utils.logging import setup_logging
7+
8+
# Configuración constantes
9+
APP_NAME = "NeuroBank FastAPI Toolkit"
10+
APP_VERSION = "1.0.0"
11+
12+
# Configurar logging
13+
setup_logging()
14+
logger = logging.getLogger(__name__)
15+
16+
# Crear la aplicación FastAPI
17+
app = FastAPI(
18+
title=APP_NAME,
19+
description="API toolkit for banking operations",
20+
version=APP_VERSION,
21+
docs_url="/docs",
22+
redoc_url="/redoc"
23+
)
24+
25+
# Configurar CORS
26+
app.add_middleware(
27+
CORSMiddleware,
28+
allow_origins=["*"], # En producción, especificar dominios exactos
29+
allow_credentials=True,
30+
allow_methods=["*"],
31+
allow_headers=["*"],
32+
)
33+
34+
# Incluir routers
35+
app.include_router(operator.router, prefix="/operator", tags=["operator"])
36+
37+
# Health check endpoint
38+
@app.get("/health")
39+
async def health_check():
40+
"""Endpoint de health check"""
41+
return JSONResponse(
42+
status_code=200,
43+
content={
44+
"status": "healthy",
45+
"service": APP_NAME,
46+
"version": APP_VERSION
47+
}
48+
)
49+
50+
# Root endpoint
51+
@app.get("/")
52+
async def root():
53+
"""Endpoint raíz con información básica"""
54+
return {
55+
"message": APP_NAME,
56+
"version": APP_VERSION,
57+
"docs": "/docs",
58+
"health": "/health"
59+
}
60+
61+
if __name__ == "__main__":
62+
import uvicorn
63+
uvicorn.run(app, host="0.0.0.0", port=8000)

app/routers/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# API routers

app/services/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Business services

app/tests/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Test modules

app/tests/test_main.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import pytest
2+
from httpx import AsyncClient
3+
from app.main import app
4+
5+
@pytest.mark.asyncio
6+
async def test_health_check():
7+
"""Test del endpoint de health check"""
8+
async with AsyncClient(app=app, base_url="http://test") as ac:
9+
response = await ac.get("/health")
10+
11+
assert response.status_code == 200
12+
data = response.json()
13+
assert data["status"] == "healthy"
14+
assert "service" in data
15+
assert "version" in data
16+
17+
@pytest.mark.asyncio
18+
async def test_root_endpoint():
19+
"""Test del endpoint raíz"""
20+
async with AsyncClient(app=app, base_url="http://test") as ac:
21+
response = await ac.get("/")
22+
23+
assert response.status_code == 200
24+
data = response.json()
25+
assert "message" in data
26+
assert "version" in data
27+
assert data["docs"] == "/docs"
28+
assert data["health"] == "/health"

app/tests/test_operator.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
import pytest
2+
import os
23
from httpx import AsyncClient
34
from app.main import app
45

5-
API_KEY = "secret" # Debe coincidir con tu .env
6+
# Configurar API key para tests
7+
API_KEY = "test-api-key"
8+
os.environ["API_KEY"] = API_KEY
69

710
@pytest.mark.asyncio
811
async def test_order_status():
912
async with AsyncClient(app=app, base_url="http://test") as ac:
1013
resp = await ac.get(
1114
"/operator/order_status/123",
12-
headers={"X-API-Key": API_KEY}
15+
headers={"Authorization": f"Bearer {API_KEY}"}
1316
)
1417
assert resp.status_code == 200
1518
data = resp.json()
@@ -21,7 +24,7 @@ async def test_generate_invoice():
2124
resp = await ac.post(
2225
"/operator/generate_invoice",
2326
json={"order_id": "123"},
24-
headers={"X-API-Key": API_KEY}
27+
headers={"Authorization": f"Bearer {API_KEY}"}
2528
)
2629
assert resp.status_code == 200
2730
data = resp.json()

0 commit comments

Comments
 (0)