Files
shopdb-flask/shopdb/utils/responses.py
cproudlock cd353b6432
Some checks failed
CI / backend (push) Has been cancelled
CI / naming (push) Has been cancelled
CI / frontend (push) Has been cancelled
Review safe-polish: docs accuracy, dead imports, no-emoji, geenforce robustness
From the full multi-agent review (0 high, 7 medium, 17 low findings). Applies
the mechanical, low-risk items; design/policy findings left for a decision.

Docs accuracy: CLAUDE.md contract 0.10.0 -> 0.11.0 and both stale Alembic head
citations -> 7d24_customfield_searchable / 31 migrations; Dockerfile bundled-
plugin comment fixed (drop nonexistent "equipment", add machines +
measuringtools, count eleven).

Style/naming (LOCKED rules): remove a CSS-escaped pushpin emoji before location
search results (no-emoji policy); rename ManifestEditor shareRoot -> shareroot
(variable mirrors the API field verbatim).

Dead code: remove confirmed-unused imports across ~20 modules (require_role/
require_permission scaffold residue, stray db/Vendor/Model/current_user/Optional/
error_response); drop unused build_scope import + a stale GEENFORCE_API_KEY
docstring clause in geenforce. Migration files left untouched.

Correctness: geenforce ingest robustness - record_enforcement_report now 400s
on a non-dict counts / non-list results instead of 500; _apply_app_link ignores
a non-numeric appid per its docstring instead of 500. Regression tests added.

Backend query.get sweep finished: auth.py refresh -> db.session.get (last one).

910 backend tests pass; pyflakes clean; naming green; frontend build green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 08:02:43 -04:00

176 lines
3.8 KiB
Python

"""Standardized API response helpers."""
from flask import jsonify, make_response
from typing import Any, Dict, List
from datetime import datetime, timezone
import uuid
class ErrorCodes:
"""Standard error codes."""
VALIDATION_ERROR = 'VALIDATION_ERROR'
NOT_FOUND = 'NOT_FOUND'
UNAUTHORIZED = 'UNAUTHORIZED'
FORBIDDEN = 'FORBIDDEN'
CONFLICT = 'CONFLICT'
INTERNAL_ERROR = 'INTERNAL_ERROR'
BAD_REQUEST = 'BAD_REQUEST'
PLUGIN_ERROR = 'PLUGIN_ERROR'
def api_response(
data: Any = None,
message: str = None,
status: str = 'success',
meta: Dict = None,
http_code: int = 200
):
"""
Create standardized API response.
Response format:
{
"status": "success" | "error",
"data": {...} | [...],
"message": "Optional message",
"meta": {
"timestamp": "2025-01-12T...",
"request_id": "uuid"
}
}
"""
response = {
'status': status,
'meta': {
'timestamp': datetime.now(timezone.utc).replace(tzinfo=None).isoformat() + 'Z',
'requestid': str(uuid.uuid4())[:8],
**(meta or {})
}
}
if data is not None:
response['data'] = data
if message:
response['message'] = message
return make_response(jsonify(response), http_code)
def success_response(
data: Any = None,
message: str = None,
meta: Dict = None,
http_code: int = 200
):
"""Success response helper."""
return api_response(
data=data,
message=message,
meta=meta,
status='success',
http_code=http_code
)
def error_response(
code: str,
message: str,
details: Dict = None,
http_code: int = 400
):
"""Error response helper.
Response format:
{
"status": "error",
"data": {
"error": {
"code": "VALIDATION_ERROR",
"message": "Human-readable message",
"details": {...}
}
},
"meta": {
"timestamp": "...",
"requestid": "..."
}
}
"""
error_data = {
'code': code,
'message': message
}
if details:
error_data['details'] = details
return api_response(
data={'error': error_data},
status='error',
http_code=http_code
)
def api_error(
message: str,
code: str = ErrorCodes.BAD_REQUEST,
details: Dict = None,
http_code: int = 400
):
"""
Simplified error response helper.
Args:
message: Human-readable error message
code: Error code (default BAD_REQUEST)
details: Optional error details
http_code: HTTP status code (default 400)
"""
return error_response(code=code, message=message, details=details, http_code=http_code)
def paginated_response(
items: List,
page: int,
per_page: int,
total: int,
schema=None
):
"""Paginated list response.
Response format:
{
"status": "success",
"data": [...],
"meta": {
"pagination": {
"page": 1,
"perpage": 20,
"total": 150,
"totalpages": 8,
"hasnext": true,
"hasprev": false
}
}
}
"""
total_pages = (total + per_page - 1) // per_page if per_page > 0 else 0
if schema:
items = schema.dump(items, many=True)
return api_response(
data=items,
meta={
'pagination': {
'page': page,
'perpage': per_page,
'total': total,
'totalpages': total_pages,
'hasnext': page < total_pages,
'hasprev': page > 1
}
}
)