File size: 3,148 Bytes
4a2ab42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87172ae
 
 
4a2ab42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87172ae
 
 
4a2ab42
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# API Models for standardized responses and requests
from fastapi import Request
from pydantic import BaseModel


# Standardized API Feature Models
class PaginationParams(BaseModel):
    """Standard pagination parameters"""

    page: int = 1
    page_size: int = 20

    class Config:
        validate_assignment = True

    @property
    def offset(self) -> int:
        return (self.page - 1) * self.page_size

    @property
    def limit(self) -> int:
        return self.page_size


class PaginationResponse(BaseModel):
    """Standard pagination response metadata"""

    page: int
    page_size: int
    total_items: int
    total_pages: int
    has_next: bool
    has_prev: bool

    @classmethod
    def create(
        cls, page: int, page_size: int, total_items: int
    ) -> "PaginationResponse":
        total_pages = (total_items + page_size - 1) // page_size
        return cls(
            page=page,
            page_size=page_size,
            total_items=total_items,
            total_pages=total_pages,
            has_next=page < total_pages,
            has_prev=page > 1,
        )


class FilterParams(BaseModel):
    """Standard filtering parameters"""

    q: str | None = None  # General search query
    sort_by: str | None = None
    sort_order: str = "asc"  # "asc" or "desc"
    status: str | None = None
    date_from: str | None = None
    date_to: str | None = None


class BulkOperationRequest(BaseModel):
    """Standard bulk operation request"""

    ids: list[str]
    operation: str  # "delete", "update", "archive", etc.
    data: dict | None = None


class BulkOperationResponse(BaseModel):
    """Standard bulk operation response"""

    operation: str
    total_requested: int
    successful: int
    failed: int
    errors: list[dict] | None = None


# Standardized Error Response Models
class ErrorDetail(BaseModel):
    """Standardized error detail structure"""

    field: str | None = None
    message: str
    code: str | None = None


class ErrorResponse(BaseModel):
    """Standardized error response structure"""

    error: dict = {
        "type": "api_error",
        "status_code": 500,
        "detail": "An error occurred",
        "request_id": None,
        "timestamp": None,
        "path": None,
        "method": None,
        "details": [],
    }


def create_error_response(
    status_code: int,
    detail: str,
    error_type: str = "api_error",
    request: Request = None,
    details: list[ErrorDetail] | None = None,
) -> dict:
    """Create standardized error response"""
    from datetime import datetime

    error_response = {
        "error": {
            "type": error_type,
            "status_code": status_code,
            "detail": detail,
            "request_id": (
                getattr(request.state, "request_id", None) if request else None
            ),
            "timestamp": datetime.now().isoformat(),
            "path": str(request.url.path) if request else None,
            "method": request.method if request else None,
            "details": [detail.dict() for detail in details] if details else [],
        }
    }
    return error_response