feat: add proper Pydantic response_model to all stats endpoints
- Create comprehensive stats schemas in models/schema/stats.py: - ImportStatsResponse, UserStatsResponse, ProductStatsResponse - PlatformStatsResponse, AdminDashboardResponse - VendorDashboardStatsResponse with nested models - VendorAnalyticsResponse, CodeQualityDashboardStatsResponse - Move DashboardStatsResponse from code_quality.py to schema file - Fix get_vendor_statistics() to return pending_vendors field - Fix get_vendor_stats() to return flat structure matching schema - Add response_model to all stats endpoints: - GET /admin/dashboard -> AdminDashboardResponse - GET /admin/dashboard/stats/platform -> PlatformStatsResponse - GET /admin/marketplace-import-jobs/stats -> ImportStatsResponse - GET /vendor/dashboard/stats -> VendorDashboardStatsResponse - GET /vendor/analytics -> VendorAnalyticsResponse - Enhance API-001 architecture rule with detailed guidance - Add SVC-007 rule for service/schema compatibility 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -41,16 +41,51 @@ api_endpoint_rules:
|
||||
description: |
|
||||
All API endpoints must use Pydantic models (BaseModel) for request bodies
|
||||
and response models. Never use raw dicts or SQLAlchemy models directly.
|
||||
|
||||
WHY THIS MATTERS:
|
||||
- Type safety: Pydantic validates response structure at runtime
|
||||
- Documentation: OpenAPI/Swagger auto-generates accurate docs
|
||||
- Contract stability: Schema changes are explicit and reviewable
|
||||
- IDE support: Consumers get autocomplete and type hints
|
||||
- Prevents bugs: Field name mismatches caught immediately
|
||||
|
||||
COMMON VIOLATION: Service returns dict, frontend expects different field names.
|
||||
Example: Service returns {"total_imports": 5} but frontend expects {"total": 5}.
|
||||
With response_model, this mismatch is caught immediately.
|
||||
|
||||
SCHEMA LOCATION: All response schemas must be defined in models/schema/*.py,
|
||||
never inline in endpoint files. This ensures schemas are reusable and discoverable.
|
||||
pattern:
|
||||
file_pattern: "app/api/v1/**/*.py"
|
||||
anti_patterns:
|
||||
- "return dict"
|
||||
- "-> dict"
|
||||
- "return db_object"
|
||||
- "return {" # Returning inline dict literal
|
||||
example_good: |
|
||||
@router.post("/vendors", response_model=VendorResponse)
|
||||
async def create_vendor(vendor: VendorCreate):
|
||||
return vendor_service.create_vendor(db, vendor)
|
||||
# In models/schema/stats.py
|
||||
class ImportStatsResponse(BaseModel):
|
||||
total: int
|
||||
pending: int
|
||||
completed: int
|
||||
failed: int
|
||||
|
||||
# In app/api/v1/admin/marketplace.py
|
||||
@router.get("/stats", response_model=ImportStatsResponse)
|
||||
def get_import_statistics(db: Session = Depends(get_db)):
|
||||
return stats_service.get_import_statistics(db)
|
||||
example_bad: |
|
||||
# ❌ WRONG: No response_model, returns raw dict
|
||||
@router.get("/stats")
|
||||
def get_import_statistics(db: Session = Depends(get_db)):
|
||||
return stats_service.get_import_statistics(db) # Returns dict
|
||||
|
||||
# ❌ WRONG: Schema defined inline in endpoint file
|
||||
class MyResponse(BaseModel): # Should be in models/schema/
|
||||
...
|
||||
|
||||
@router.get("/data", response_model=MyResponse)
|
||||
def get_data(): ...
|
||||
|
||||
- id: "API-002"
|
||||
name: "Endpoint must NOT contain business logic"
|
||||
@@ -238,6 +273,42 @@ service_layer_rules:
|
||||
exceptions:
|
||||
- "log_service.py" # Audit logs may need immediate commits
|
||||
|
||||
- id: "SVC-007"
|
||||
name: "Service return types must match API response schemas"
|
||||
severity: "error"
|
||||
description: |
|
||||
When a service method's return value will be used as an API response,
|
||||
the returned dict keys MUST match the corresponding Pydantic schema fields.
|
||||
|
||||
This prevents the common bug where:
|
||||
- Service returns {"total_imports": 5, "completed_imports": 3}
|
||||
- Schema expects {"total": 5, "completed": 3}
|
||||
- Frontend receives wrong/empty values
|
||||
|
||||
RECOMMENDED PATTERNS:
|
||||
|
||||
1. Return Pydantic model directly from service:
|
||||
def get_stats(self, db: Session) -> StatsResponse:
|
||||
return StatsResponse(total=count, completed=done)
|
||||
|
||||
2. Return dict with schema-matching keys:
|
||||
def get_stats(self, db: Session) -> dict:
|
||||
return {"total": count, "completed": done} # Matches StatsResponse
|
||||
|
||||
3. Document the expected schema in service docstring:
|
||||
def get_stats(self, db: Session) -> dict:
|
||||
\"\"\"
|
||||
Returns dict compatible with StatsResponse schema.
|
||||
Keys: total, pending, completed, failed
|
||||
\"\"\"
|
||||
|
||||
TESTING: Write tests that validate service output against schema:
|
||||
result = service.get_stats(db)
|
||||
StatsResponse(**result) # Raises if keys don't match
|
||||
pattern:
|
||||
file_pattern: "app/services/**/*.py"
|
||||
check: "schema_compatibility"
|
||||
|
||||
# ============================================================================
|
||||
# MODEL RULES (models/database/*.py, models/schema/*.py)
|
||||
# ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user