chore: merge executor worktree (worktree-agent-ad0b098c151ab9e3f)
This commit is contained in:
@@ -0,0 +1,103 @@
|
|||||||
|
---
|
||||||
|
phase: 08-stack-upgrade-backend-decomposition
|
||||||
|
plan: "02"
|
||||||
|
subsystem: api
|
||||||
|
tags: [pydantic, schemas, refactor, cross-package, backend]
|
||||||
|
|
||||||
|
# Dependency graph
|
||||||
|
requires:
|
||||||
|
- phase: none
|
||||||
|
provides: "Wave 0 prerequisite — no prior plan dependency"
|
||||||
|
provides:
|
||||||
|
- "backend/api/schemas.py: new cross-package Pydantic schemas module with CloudConnectionOut"
|
||||||
|
- "backend/api/cloud.py: no longer imports from api/admin (coupling eliminated)"
|
||||||
|
affects:
|
||||||
|
- "08-04-admin-split: plan 08-04 will delete original CloudConnectionOut from admin.py and import from api.schemas"
|
||||||
|
|
||||||
|
# Tech tracking
|
||||||
|
tech-stack:
|
||||||
|
added: []
|
||||||
|
patterns:
|
||||||
|
- "api/schemas.py as top-level home for Pydantic models shared across 2+ API packages (D-10)"
|
||||||
|
|
||||||
|
key-files:
|
||||||
|
created:
|
||||||
|
- backend/api/schemas.py
|
||||||
|
modified:
|
||||||
|
- backend/api/cloud.py
|
||||||
|
|
||||||
|
key-decisions:
|
||||||
|
- "CloudConnectionOut stays duplicated in admin.py until plan 08-04 admin split (intentional transient state)"
|
||||||
|
- "credentials_enc excluded from CloudConnectionOut field set — SEC-08 whitelist preserved verbatim"
|
||||||
|
- "coerce_id_to_str field_validator preserved byte-identically to prevent any UUID serialization regression"
|
||||||
|
|
||||||
|
patterns-established:
|
||||||
|
- "backend/api/schemas.py: shared Pydantic models for 2+ packages live here, not in any single package"
|
||||||
|
|
||||||
|
requirements-completed: [CODE-08]
|
||||||
|
|
||||||
|
# Metrics
|
||||||
|
duration: 5min
|
||||||
|
completed: 2026-06-08
|
||||||
|
---
|
||||||
|
|
||||||
|
# Phase 8 Plan 02: Shared Schemas Module Summary
|
||||||
|
|
||||||
|
**New `backend/api/schemas.py` cross-package module with `CloudConnectionOut` extracted from `api/admin.py`; `api/cloud.py` import switched from `api.admin` to `api.schemas`, eliminating cross-package coupling (Pitfall 3)**
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
- **Duration:** ~5 min
|
||||||
|
- **Started:** 2026-06-08
|
||||||
|
- **Completed:** 2026-06-08
|
||||||
|
- **Tasks:** 2 / 2
|
||||||
|
- **Files modified:** 2 (1 created, 1 modified)
|
||||||
|
|
||||||
|
## Accomplishments
|
||||||
|
|
||||||
|
- Created `backend/api/schemas.py` as the canonical home for Pydantic response models shared across 2+ API packages (D-10)
|
||||||
|
- `CloudConnectionOut` copied verbatim from `api/admin.py` including SEC-08 docstring, 7-field whitelist, `from_attributes` config, and `coerce_id_to_str` field_validator
|
||||||
|
- Switched `backend/api/cloud.py` line 35 from `from api.admin import CloudConnectionOut` to `from api.schemas import CloudConnectionOut`
|
||||||
|
- All 51 cloud and admin tests continue to pass after the import switch
|
||||||
|
- Original `CloudConnectionOut` definition in `api/admin.py` left untouched — plan 08-04 owns the deletion during the admin split
|
||||||
|
|
||||||
|
## Task Commits
|
||||||
|
|
||||||
|
Each task was committed atomically:
|
||||||
|
|
||||||
|
1. **Task 1: Create backend/api/schemas.py with CloudConnectionOut** - `10e0900` (feat)
|
||||||
|
2. **Task 2: Switch backend/api/cloud.py import to api.schemas** - `61fa6e2` (refactor)
|
||||||
|
|
||||||
|
**Plan metadata:** (SUMMARY committed separately)
|
||||||
|
|
||||||
|
## Files Created/Modified
|
||||||
|
|
||||||
|
- `backend/api/schemas.py` — New cross-package Pydantic schemas module; contains `CloudConnectionOut` with SEC-08 whitelist, 7 fields, `coerce_id_to_str` validator
|
||||||
|
- `backend/api/cloud.py` — Single-line import change: `from api.admin` → `from api.schemas`
|
||||||
|
|
||||||
|
## Decisions Made
|
||||||
|
|
||||||
|
- Original `CloudConnectionOut` in `api/admin.py` is intentionally left in place until plan 08-04. Both definitions coexist temporarily; only `api/cloud.py` imports from `api/schemas`. This avoids a two-plan cascading dependency and is explicitly documented in T-08-02-03 as accepted transient duplication.
|
||||||
|
- No changes to any other file — plan scope held exactly.
|
||||||
|
|
||||||
|
## Deviations from Plan
|
||||||
|
|
||||||
|
None — plan executed exactly as written.
|
||||||
|
|
||||||
|
## Issues Encountered
|
||||||
|
|
||||||
|
- `tests/test_admin.py` does not exist; the correct file is `tests/test_admin_api.py`. Plan's verify command referenced the wrong filename, but the test run with the correct filename confirmed all 51 tests pass. No code change needed.
|
||||||
|
|
||||||
|
## User Setup Required
|
||||||
|
|
||||||
|
None — no external service configuration required.
|
||||||
|
|
||||||
|
## Next Phase Readiness
|
||||||
|
|
||||||
|
- `backend/api/schemas.py` is ready for plan 08-04 (admin split) to import from it and delete the original `CloudConnectionOut` from `api/admin.py`
|
||||||
|
- No blockers. Verification output: `51 passed, 5 warnings` from `pytest tests/test_cloud.py tests/test_admin_api.py -x -v`
|
||||||
|
- Final check: `grep -rn "from api.admin import CloudConnectionOut" backend/` returns no matches
|
||||||
|
|
||||||
|
---
|
||||||
|
*Phase: 08-stack-upgrade-backend-decomposition*
|
||||||
|
*Completed: 2026-06-08*
|
||||||
@@ -32,7 +32,7 @@ from pydantic import BaseModel
|
|||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from api.admin import CloudConnectionOut
|
from api.schemas import CloudConnectionOut
|
||||||
from config import settings
|
from config import settings
|
||||||
from db.models import CloudConnection, User
|
from db.models import CloudConnection, User
|
||||||
from deps.auth import get_regular_user
|
from deps.auth import get_regular_user
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
"""Cross-package Pydantic response models.
|
||||||
|
|
||||||
|
Models here are used by 2+ API packages and cannot live in a single package
|
||||||
|
without creating circular imports (D-10, RESEARCH.md Pitfall 3).
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel, field_validator
|
||||||
|
|
||||||
|
|
||||||
|
class CloudConnectionOut(BaseModel):
|
||||||
|
"""SEC-08: credentials_enc deliberately excluded from this response model.
|
||||||
|
|
||||||
|
Any admin or user endpoint returning CloudConnection ORM objects MUST use
|
||||||
|
this model to prevent accidental exposure of encrypted credentials.
|
||||||
|
Safe-by-default: whitelist of allowed fields (not blacklist).
|
||||||
|
|
||||||
|
Moved from api/admin.py to eliminate cross-package coupling between
|
||||||
|
api/cloud.py and api/admin.py (D-10, RESEARCH.md Pitfall 3).
|
||||||
|
Used by api/cloud.py and api/admin/ (after plan 08-04 admin split).
|
||||||
|
|
||||||
|
Note: id is declared as str and coerced via validator so UUID ORM values
|
||||||
|
serialize correctly without json_encoders.
|
||||||
|
"""
|
||||||
|
|
||||||
|
id: str
|
||||||
|
provider: str
|
||||||
|
display_name: str
|
||||||
|
status: str
|
||||||
|
connected_at: datetime
|
||||||
|
server_url: Optional[str] = None
|
||||||
|
connection_username: Optional[str] = None
|
||||||
|
model_config = {"from_attributes": True}
|
||||||
|
|
||||||
|
@field_validator("id", mode="before")
|
||||||
|
@classmethod
|
||||||
|
def coerce_id_to_str(cls, v) -> str:
|
||||||
|
"""Coerce UUID objects to str so the model validates from ORM instances."""
|
||||||
|
return str(v)
|
||||||
Reference in New Issue
Block a user