feat: service scaffold, k8s test infra, health

FastAPI + SQLAlchemy async + Alembic scaffold, src-layout (mirrors the
sowai-fiscal lib's own convention), pyproject wired to sowai-fiscal@v0.1.0
via git+https (uv.lock pins the commit). Makefile mirrors auto/Makefile's
k8s-test workflow: syncs into /app/fiscal-svc in the SAME auto-tests pod,
against a dedicated fiscal_svc_test database on the shared Postgres
sidecar, serialized by the SAME lock file the auto uses on purpose so the
two repos' test runs never race in the pod. Dockerfile installs git (the
git+https dependency needs it at uv sync time) and splits the dependency
layer from the project's own editable install for build caching.

GET /v1/health -> {"status": "ok"}, verified green via `make k8s-test`.
This commit is contained in:
jonatanritter
2026-07-22 16:12:11 -03:00
commit 923848af33
23 changed files with 2223 additions and 0 deletions
View File
View File
+34
View File
@@ -0,0 +1,34 @@
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
"""Ported from `auto/backend/app/core/config.py` (Task 1). Same shape:
`pydantic-settings` reading `.env` + env vars, `extra="ignore"` so a
product-specific env var set alongside this service's own (e.g. in a
shared k8s namespace) never trips validation.
Adaptation per the porte table (2026-07-17-fiscal-svc-f2-servico.md):
this service has NO JWT auth of its own (`require_product` in Task 2
reads a bcrypt-hashed API key straight from the `products` table, not a
JWT keypair) -- so the `jwt_private_key_path`/`jwt_public_key_path`/
`access_token_expire_minutes`/`refresh_token_expire_days` fields the
auto's `Settings` carries have no equivalent here and are deliberately
NOT ported. `FISCAL_CERT_ENCRYPTION_KEY` (Task 4's Fernet key for
certificate ciphertext) is likewise read straight from `os.environ` at
the point of use, never through this class -- same "secrets don't live
in Settings" convention as the auto's own
`FISCAL_CERT_ENCRYPTION_KEY`/`INTEGRACOES_ENCRYPTION_KEY` (see the auto's
`app.core.config.Settings` module comment)."""
model_config = SettingsConfigDict(env_file=".env", extra="ignore")
app_name: str = "sowai-fiscal-svc"
database_url: str = (
"postgresql+asyncpg://postgres:postgres@localhost:5432/fiscal_svc_dev"
)
test_database_url: str = (
"postgresql+asyncpg://postgres:postgres@localhost:5432/fiscal_svc_test"
)
settings = Settings()
+19
View File
@@ -0,0 +1,19 @@
from collections.abc import AsyncGenerator
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
from sqlalchemy.orm import DeclarativeBase
from fiscal_svc.core.config import settings
class Base(DeclarativeBase):
pass
engine = create_async_engine(settings.database_url, echo=False)
async_session_maker = async_sessionmaker(engine, expire_on_commit=False)
async def get_session() -> AsyncGenerator[AsyncSession, None]:
async with async_session_maker() as session:
yield session
+10
View File
@@ -0,0 +1,10 @@
from fastapi import FastAPI
from fiscal_svc.core.config import settings
app = FastAPI(title=settings.app_name)
@app.get("/v1/health")
async def health_check() -> dict[str, str]:
return {"status": "ok"}