vega-framework 0.1.34__py3-none-any.whl → 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vega/cli/commands/add.py +9 -10
- vega/cli/commands/init.py +9 -8
- vega/cli/main.py +4 -4
- vega/cli/scaffolds/__init__.py +6 -2
- vega/cli/scaffolds/vega_web.py +109 -0
- vega/cli/templates/__init__.py +34 -8
- vega/cli/templates/components.py +29 -13
- vega/cli/templates/web/app.py.j2 +5 -5
- vega/cli/templates/web/health_route.py.j2 +2 -2
- vega/cli/templates/web/router.py.j2 +2 -2
- vega/cli/templates/web/routes_init_autodiscovery.py.j2 +2 -2
- vega/cli/templates/web/users_route.py.j2 +2 -2
- vega/discovery/routes.py +13 -13
- vega/web/__init__.py +100 -0
- vega/web/application.py +234 -0
- vega/web/builtin_middlewares.py +288 -0
- vega/web/exceptions.py +151 -0
- vega/web/middleware.py +185 -0
- vega/web/request.py +120 -0
- vega/web/response.py +220 -0
- vega/web/route_middleware.py +266 -0
- vega/web/router.py +350 -0
- vega/web/routing.py +347 -0
- {vega_framework-0.1.34.dist-info → vega_framework-0.2.0.dist-info}/METADATA +10 -9
- {vega_framework-0.1.34.dist-info → vega_framework-0.2.0.dist-info}/RECORD +28 -17
- {vega_framework-0.1.34.dist-info → vega_framework-0.2.0.dist-info}/WHEEL +0 -0
- {vega_framework-0.1.34.dist-info → vega_framework-0.2.0.dist-info}/entry_points.txt +0 -0
- {vega_framework-0.1.34.dist-info → vega_framework-0.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,266 @@
|
|
1
|
+
"""Route-level middleware system for Vega Web Framework"""
|
2
|
+
|
3
|
+
from typing import Callable, List, Optional, Union, Any
|
4
|
+
from enum import Enum
|
5
|
+
from functools import wraps
|
6
|
+
import inspect
|
7
|
+
|
8
|
+
from .request import Request
|
9
|
+
from .response import Response, JSONResponse
|
10
|
+
|
11
|
+
|
12
|
+
class MiddlewarePhase(str, Enum):
|
13
|
+
"""When the middleware should execute"""
|
14
|
+
BEFORE = "before" # Execute before the handler
|
15
|
+
AFTER = "after" # Execute after the handler
|
16
|
+
BOTH = "both" # Execute both before and after
|
17
|
+
|
18
|
+
|
19
|
+
class RouteMiddleware:
|
20
|
+
"""
|
21
|
+
Base class for route-level middleware.
|
22
|
+
|
23
|
+
Route middleware can execute before or after the handler function,
|
24
|
+
allowing for request preprocessing, response postprocessing, or both.
|
25
|
+
|
26
|
+
Attributes:
|
27
|
+
phase: When to execute (BEFORE, AFTER, or BOTH)
|
28
|
+
|
29
|
+
Example:
|
30
|
+
class AuthMiddleware(RouteMiddleware):
|
31
|
+
def __init__(self):
|
32
|
+
super().__init__(phase=MiddlewarePhase.BEFORE)
|
33
|
+
|
34
|
+
async def before(self, request: Request) -> Optional[Response]:
|
35
|
+
token = request.headers.get("Authorization")
|
36
|
+
if not token:
|
37
|
+
return JSONResponse(
|
38
|
+
{"detail": "Missing authorization"},
|
39
|
+
status_code=401
|
40
|
+
)
|
41
|
+
# Continue to handler
|
42
|
+
return None
|
43
|
+
|
44
|
+
@router.get("/protected")
|
45
|
+
@middleware(AuthMiddleware())
|
46
|
+
async def protected_route():
|
47
|
+
return {"message": "Protected data"}
|
48
|
+
"""
|
49
|
+
|
50
|
+
def __init__(self, phase: MiddlewarePhase = MiddlewarePhase.BEFORE):
|
51
|
+
self.phase = phase
|
52
|
+
|
53
|
+
async def before(self, request: Request) -> Optional[Response]:
|
54
|
+
"""
|
55
|
+
Execute before the handler.
|
56
|
+
|
57
|
+
Args:
|
58
|
+
request: The incoming request
|
59
|
+
|
60
|
+
Returns:
|
61
|
+
Optional[Response]: Return a Response to short-circuit,
|
62
|
+
or None to continue to the handler
|
63
|
+
"""
|
64
|
+
return None
|
65
|
+
|
66
|
+
async def after(
|
67
|
+
self,
|
68
|
+
request: Request,
|
69
|
+
response: Response
|
70
|
+
) -> Response:
|
71
|
+
"""
|
72
|
+
Execute after the handler.
|
73
|
+
|
74
|
+
Args:
|
75
|
+
request: The incoming request
|
76
|
+
response: The response from the handler
|
77
|
+
|
78
|
+
Returns:
|
79
|
+
Response: Modified or original response
|
80
|
+
"""
|
81
|
+
return response
|
82
|
+
|
83
|
+
async def process(
|
84
|
+
self,
|
85
|
+
request: Request,
|
86
|
+
handler: Callable,
|
87
|
+
**kwargs
|
88
|
+
) -> Response:
|
89
|
+
"""
|
90
|
+
Process the middleware chain.
|
91
|
+
|
92
|
+
Args:
|
93
|
+
request: The incoming request
|
94
|
+
handler: The route handler function
|
95
|
+
**kwargs: Handler keyword arguments
|
96
|
+
|
97
|
+
Returns:
|
98
|
+
Response object
|
99
|
+
"""
|
100
|
+
# Execute before phase
|
101
|
+
if self.phase in (MiddlewarePhase.BEFORE, MiddlewarePhase.BOTH):
|
102
|
+
before_response = await self.before(request)
|
103
|
+
if before_response is not None:
|
104
|
+
# Short-circuit: return response without calling handler
|
105
|
+
return before_response
|
106
|
+
|
107
|
+
# Call the handler
|
108
|
+
if inspect.iscoroutinefunction(handler):
|
109
|
+
result = await handler(**kwargs)
|
110
|
+
else:
|
111
|
+
result = handler(**kwargs)
|
112
|
+
|
113
|
+
# Convert result to Response if needed
|
114
|
+
if isinstance(result, (Response, JSONResponse)):
|
115
|
+
response = result
|
116
|
+
elif isinstance(result, dict):
|
117
|
+
response = JSONResponse(content=result)
|
118
|
+
elif isinstance(result, (list, tuple)):
|
119
|
+
response = JSONResponse(content=result)
|
120
|
+
elif isinstance(result, str):
|
121
|
+
response = Response(content=result)
|
122
|
+
elif result is None:
|
123
|
+
response = Response(content=b"")
|
124
|
+
else:
|
125
|
+
response = JSONResponse(content=result)
|
126
|
+
|
127
|
+
# Execute after phase
|
128
|
+
if self.phase in (MiddlewarePhase.AFTER, MiddlewarePhase.BOTH):
|
129
|
+
response = await self.after(request, response)
|
130
|
+
|
131
|
+
return response
|
132
|
+
|
133
|
+
|
134
|
+
class MiddlewareChain:
|
135
|
+
"""
|
136
|
+
Manages a chain of middleware for a route.
|
137
|
+
|
138
|
+
Example:
|
139
|
+
chain = MiddlewareChain([AuthMiddleware(), LoggingMiddleware()])
|
140
|
+
response = await chain.execute(request, handler, user_id="123")
|
141
|
+
"""
|
142
|
+
|
143
|
+
def __init__(self, middlewares: List[RouteMiddleware]):
|
144
|
+
self.middlewares = middlewares
|
145
|
+
|
146
|
+
async def execute(
|
147
|
+
self,
|
148
|
+
request: Request,
|
149
|
+
handler: Callable,
|
150
|
+
**kwargs
|
151
|
+
) -> Response:
|
152
|
+
"""
|
153
|
+
Execute the middleware chain.
|
154
|
+
|
155
|
+
Args:
|
156
|
+
request: The incoming request
|
157
|
+
handler: The route handler
|
158
|
+
**kwargs: Handler arguments
|
159
|
+
|
160
|
+
Returns:
|
161
|
+
Response object
|
162
|
+
"""
|
163
|
+
if not self.middlewares:
|
164
|
+
# No middleware, call handler directly
|
165
|
+
if inspect.iscoroutinefunction(handler):
|
166
|
+
result = await handler(**kwargs)
|
167
|
+
else:
|
168
|
+
result = handler(**kwargs)
|
169
|
+
|
170
|
+
# Convert to response
|
171
|
+
if isinstance(result, (Response, JSONResponse)):
|
172
|
+
return result
|
173
|
+
elif isinstance(result, dict):
|
174
|
+
return JSONResponse(content=result)
|
175
|
+
elif isinstance(result, (list, tuple)):
|
176
|
+
return JSONResponse(content=result)
|
177
|
+
elif isinstance(result, str):
|
178
|
+
return Response(content=result)
|
179
|
+
elif result is None:
|
180
|
+
return Response(content=b"")
|
181
|
+
else:
|
182
|
+
return JSONResponse(content=result)
|
183
|
+
|
184
|
+
# Execute BEFORE middleware
|
185
|
+
for mw in self.middlewares:
|
186
|
+
if mw.phase in (MiddlewarePhase.BEFORE, MiddlewarePhase.BOTH):
|
187
|
+
before_response = await mw.before(request)
|
188
|
+
if before_response is not None:
|
189
|
+
# Short-circuit
|
190
|
+
return before_response
|
191
|
+
|
192
|
+
# Call handler - check if it needs request parameter
|
193
|
+
sig = inspect.signature(handler)
|
194
|
+
handler_params = sig.parameters
|
195
|
+
|
196
|
+
# Add request to kwargs if handler expects it
|
197
|
+
if "request" in handler_params or any(
|
198
|
+
p.annotation.__name__ == "Request" if hasattr(p.annotation, "__name__") else False
|
199
|
+
for p in handler_params.values()
|
200
|
+
):
|
201
|
+
kwargs["request"] = request
|
202
|
+
|
203
|
+
if inspect.iscoroutinefunction(handler):
|
204
|
+
result = await handler(**kwargs)
|
205
|
+
else:
|
206
|
+
result = handler(**kwargs)
|
207
|
+
|
208
|
+
# Convert to response
|
209
|
+
if isinstance(result, (Response, JSONResponse)):
|
210
|
+
response = result
|
211
|
+
elif isinstance(result, dict):
|
212
|
+
response = JSONResponse(content=result)
|
213
|
+
elif isinstance(result, (list, tuple)):
|
214
|
+
response = JSONResponse(content=result)
|
215
|
+
elif isinstance(result, str):
|
216
|
+
response = Response(content=result)
|
217
|
+
elif result is None:
|
218
|
+
response = Response(content=b"")
|
219
|
+
else:
|
220
|
+
response = JSONResponse(content=result)
|
221
|
+
|
222
|
+
# Execute AFTER middleware (in reverse order)
|
223
|
+
for mw in reversed(self.middlewares):
|
224
|
+
if mw.phase in (MiddlewarePhase.AFTER, MiddlewarePhase.BOTH):
|
225
|
+
response = await mw.after(request, response)
|
226
|
+
|
227
|
+
return response
|
228
|
+
|
229
|
+
|
230
|
+
def middleware(*middlewares: RouteMiddleware) -> Callable:
|
231
|
+
"""
|
232
|
+
Decorator to attach middleware to a route handler.
|
233
|
+
|
234
|
+
Args:
|
235
|
+
*middlewares: One or more RouteMiddleware instances
|
236
|
+
|
237
|
+
Example:
|
238
|
+
@router.get("/users/{user_id}")
|
239
|
+
@middleware(AuthMiddleware(), LoggingMiddleware())
|
240
|
+
async def get_user(user_id: str):
|
241
|
+
return {"id": user_id}
|
242
|
+
|
243
|
+
# Or single middleware
|
244
|
+
@router.post("/admin/action")
|
245
|
+
@middleware(AdminOnlyMiddleware())
|
246
|
+
async def admin_action():
|
247
|
+
return {"status": "done"}
|
248
|
+
"""
|
249
|
+
middleware_list = list(middlewares)
|
250
|
+
|
251
|
+
def decorator(func: Callable) -> Callable:
|
252
|
+
# Store middleware list on the function
|
253
|
+
if not hasattr(func, '_route_middlewares'):
|
254
|
+
func._route_middlewares = []
|
255
|
+
func._route_middlewares.extend(middleware_list)
|
256
|
+
return func
|
257
|
+
|
258
|
+
return decorator
|
259
|
+
|
260
|
+
|
261
|
+
__all__ = [
|
262
|
+
"RouteMiddleware",
|
263
|
+
"MiddlewarePhase",
|
264
|
+
"MiddlewareChain",
|
265
|
+
"middleware",
|
266
|
+
]
|
vega/web/router.py
ADDED
@@ -0,0 +1,350 @@
|
|
1
|
+
"""Router class for Vega Web Framework"""
|
2
|
+
|
3
|
+
from typing import Any, Callable, List, Optional, Sequence, Type
|
4
|
+
|
5
|
+
from starlette.routing import Mount
|
6
|
+
|
7
|
+
from .routing import Route, route as create_route
|
8
|
+
|
9
|
+
|
10
|
+
class Router:
|
11
|
+
"""
|
12
|
+
HTTP Router for organizing endpoints.
|
13
|
+
|
14
|
+
Similar to FastAPI's APIRouter, this class allows you to group related
|
15
|
+
endpoints together with common configuration like prefix, tags, etc.
|
16
|
+
|
17
|
+
Args:
|
18
|
+
prefix: URL prefix for all routes (e.g., "/api/v1")
|
19
|
+
tags: Default tags for all routes
|
20
|
+
dependencies: Shared dependencies (future feature)
|
21
|
+
responses: Common response models (future feature)
|
22
|
+
|
23
|
+
Example:
|
24
|
+
router = Router(prefix="/users", tags=["users"])
|
25
|
+
|
26
|
+
@router.get("/{user_id}")
|
27
|
+
async def get_user(user_id: str):
|
28
|
+
return {"id": user_id, "name": "John"}
|
29
|
+
|
30
|
+
@router.post("")
|
31
|
+
async def create_user(request: Request):
|
32
|
+
data = await request.json()
|
33
|
+
return {"id": "new_id", **data}
|
34
|
+
"""
|
35
|
+
|
36
|
+
def __init__(
|
37
|
+
self,
|
38
|
+
prefix: str = "",
|
39
|
+
tags: Optional[List[str]] = None,
|
40
|
+
dependencies: Optional[Sequence[Any]] = None,
|
41
|
+
responses: Optional[dict] = None,
|
42
|
+
):
|
43
|
+
self.prefix = prefix
|
44
|
+
self.tags = tags or []
|
45
|
+
self.dependencies = dependencies or []
|
46
|
+
self.responses = responses or {}
|
47
|
+
self.routes: List[Route] = []
|
48
|
+
self.child_routers: List[tuple[Router, str, Optional[List[str]]]] = []
|
49
|
+
|
50
|
+
def add_route(
|
51
|
+
self,
|
52
|
+
path: str,
|
53
|
+
endpoint: Callable,
|
54
|
+
methods: List[str],
|
55
|
+
*,
|
56
|
+
name: Optional[str] = None,
|
57
|
+
include_in_schema: bool = True,
|
58
|
+
tags: Optional[List[str]] = None,
|
59
|
+
summary: Optional[str] = None,
|
60
|
+
description: Optional[str] = None,
|
61
|
+
response_model: Optional[Type] = None,
|
62
|
+
status_code: int = 200,
|
63
|
+
) -> None:
|
64
|
+
"""
|
65
|
+
Add a route to the router.
|
66
|
+
|
67
|
+
Args:
|
68
|
+
path: URL path pattern
|
69
|
+
endpoint: Handler function
|
70
|
+
methods: HTTP methods
|
71
|
+
name: Optional route name
|
72
|
+
include_in_schema: Include in API docs
|
73
|
+
tags: Route tags
|
74
|
+
summary: Short description
|
75
|
+
description: Longer description
|
76
|
+
response_model: Expected response type
|
77
|
+
status_code: Default status code
|
78
|
+
"""
|
79
|
+
# Merge router tags with route-specific tags
|
80
|
+
route_tags = (tags or []) + self.tags
|
81
|
+
|
82
|
+
route_obj = Route(
|
83
|
+
path=path,
|
84
|
+
endpoint=endpoint,
|
85
|
+
methods=methods,
|
86
|
+
name=name,
|
87
|
+
include_in_schema=include_in_schema,
|
88
|
+
tags=route_tags,
|
89
|
+
summary=summary,
|
90
|
+
description=description,
|
91
|
+
response_model=response_model,
|
92
|
+
status_code=status_code,
|
93
|
+
)
|
94
|
+
self.routes.append(route_obj)
|
95
|
+
|
96
|
+
def route(
|
97
|
+
self,
|
98
|
+
path: str,
|
99
|
+
methods: List[str],
|
100
|
+
*,
|
101
|
+
name: Optional[str] = None,
|
102
|
+
include_in_schema: bool = True,
|
103
|
+
tags: Optional[List[str]] = None,
|
104
|
+
summary: Optional[str] = None,
|
105
|
+
description: Optional[str] = None,
|
106
|
+
response_model: Optional[Type] = None,
|
107
|
+
status_code: int = 200,
|
108
|
+
) -> Callable:
|
109
|
+
"""
|
110
|
+
Decorator to add a route.
|
111
|
+
|
112
|
+
Example:
|
113
|
+
@router.route("/items", methods=["GET", "POST"])
|
114
|
+
async def items():
|
115
|
+
return {"items": []}
|
116
|
+
"""
|
117
|
+
|
118
|
+
def decorator(func: Callable) -> Callable:
|
119
|
+
self.add_route(
|
120
|
+
path=path,
|
121
|
+
endpoint=func,
|
122
|
+
methods=methods,
|
123
|
+
name=name,
|
124
|
+
include_in_schema=include_in_schema,
|
125
|
+
tags=tags,
|
126
|
+
summary=summary,
|
127
|
+
description=description,
|
128
|
+
response_model=response_model,
|
129
|
+
status_code=status_code,
|
130
|
+
)
|
131
|
+
return func
|
132
|
+
|
133
|
+
return decorator
|
134
|
+
|
135
|
+
def get(
|
136
|
+
self,
|
137
|
+
path: str,
|
138
|
+
*,
|
139
|
+
name: Optional[str] = None,
|
140
|
+
include_in_schema: bool = True,
|
141
|
+
tags: Optional[List[str]] = None,
|
142
|
+
summary: Optional[str] = None,
|
143
|
+
description: Optional[str] = None,
|
144
|
+
response_model: Optional[Type] = None,
|
145
|
+
status_code: int = 200,
|
146
|
+
) -> Callable:
|
147
|
+
"""
|
148
|
+
Decorator for GET requests.
|
149
|
+
|
150
|
+
Example:
|
151
|
+
@router.get("/items/{item_id}")
|
152
|
+
async def get_item(item_id: str):
|
153
|
+
return {"id": item_id}
|
154
|
+
"""
|
155
|
+
return self.route(
|
156
|
+
path,
|
157
|
+
methods=["GET"],
|
158
|
+
name=name,
|
159
|
+
include_in_schema=include_in_schema,
|
160
|
+
tags=tags,
|
161
|
+
summary=summary,
|
162
|
+
description=description,
|
163
|
+
response_model=response_model,
|
164
|
+
status_code=status_code,
|
165
|
+
)
|
166
|
+
|
167
|
+
def post(
|
168
|
+
self,
|
169
|
+
path: str,
|
170
|
+
*,
|
171
|
+
name: Optional[str] = None,
|
172
|
+
include_in_schema: bool = True,
|
173
|
+
tags: Optional[List[str]] = None,
|
174
|
+
summary: Optional[str] = None,
|
175
|
+
description: Optional[str] = None,
|
176
|
+
response_model: Optional[Type] = None,
|
177
|
+
status_code: int = 201,
|
178
|
+
) -> Callable:
|
179
|
+
"""
|
180
|
+
Decorator for POST requests.
|
181
|
+
|
182
|
+
Example:
|
183
|
+
@router.post("/items")
|
184
|
+
async def create_item(request: Request):
|
185
|
+
data = await request.json()
|
186
|
+
return {"id": "new", **data}
|
187
|
+
"""
|
188
|
+
return self.route(
|
189
|
+
path,
|
190
|
+
methods=["POST"],
|
191
|
+
name=name,
|
192
|
+
include_in_schema=include_in_schema,
|
193
|
+
tags=tags,
|
194
|
+
summary=summary,
|
195
|
+
description=description,
|
196
|
+
response_model=response_model,
|
197
|
+
status_code=status_code,
|
198
|
+
)
|
199
|
+
|
200
|
+
def put(
|
201
|
+
self,
|
202
|
+
path: str,
|
203
|
+
*,
|
204
|
+
name: Optional[str] = None,
|
205
|
+
include_in_schema: bool = True,
|
206
|
+
tags: Optional[List[str]] = None,
|
207
|
+
summary: Optional[str] = None,
|
208
|
+
description: Optional[str] = None,
|
209
|
+
response_model: Optional[Type] = None,
|
210
|
+
status_code: int = 200,
|
211
|
+
) -> Callable:
|
212
|
+
"""Decorator for PUT requests."""
|
213
|
+
return self.route(
|
214
|
+
path,
|
215
|
+
methods=["PUT"],
|
216
|
+
name=name,
|
217
|
+
include_in_schema=include_in_schema,
|
218
|
+
tags=tags,
|
219
|
+
summary=summary,
|
220
|
+
description=description,
|
221
|
+
response_model=response_model,
|
222
|
+
status_code=status_code,
|
223
|
+
)
|
224
|
+
|
225
|
+
def patch(
|
226
|
+
self,
|
227
|
+
path: str,
|
228
|
+
*,
|
229
|
+
name: Optional[str] = None,
|
230
|
+
include_in_schema: bool = True,
|
231
|
+
tags: Optional[List[str]] = None,
|
232
|
+
summary: Optional[str] = None,
|
233
|
+
description: Optional[str] = None,
|
234
|
+
response_model: Optional[Type] = None,
|
235
|
+
status_code: int = 200,
|
236
|
+
) -> Callable:
|
237
|
+
"""Decorator for PATCH requests."""
|
238
|
+
return self.route(
|
239
|
+
path,
|
240
|
+
methods=["PATCH"],
|
241
|
+
name=name,
|
242
|
+
include_in_schema=include_in_schema,
|
243
|
+
tags=tags,
|
244
|
+
summary=summary,
|
245
|
+
description=description,
|
246
|
+
response_model=response_model,
|
247
|
+
status_code=status_code,
|
248
|
+
)
|
249
|
+
|
250
|
+
def delete(
|
251
|
+
self,
|
252
|
+
path: str,
|
253
|
+
*,
|
254
|
+
name: Optional[str] = None,
|
255
|
+
include_in_schema: bool = True,
|
256
|
+
tags: Optional[List[str]] = None,
|
257
|
+
summary: Optional[str] = None,
|
258
|
+
description: Optional[str] = None,
|
259
|
+
response_model: Optional[Type] = None,
|
260
|
+
status_code: int = 204,
|
261
|
+
) -> Callable:
|
262
|
+
"""Decorator for DELETE requests."""
|
263
|
+
return self.route(
|
264
|
+
path,
|
265
|
+
methods=["DELETE"],
|
266
|
+
name=name,
|
267
|
+
include_in_schema=include_in_schema,
|
268
|
+
tags=tags,
|
269
|
+
summary=summary,
|
270
|
+
description=description,
|
271
|
+
response_model=response_model,
|
272
|
+
status_code=status_code,
|
273
|
+
)
|
274
|
+
|
275
|
+
def include_router(
|
276
|
+
self,
|
277
|
+
router: "Router",
|
278
|
+
prefix: str = "",
|
279
|
+
tags: Optional[List[str]] = None,
|
280
|
+
) -> None:
|
281
|
+
"""
|
282
|
+
Include another router's routes in this router.
|
283
|
+
|
284
|
+
Args:
|
285
|
+
router: Router to include
|
286
|
+
prefix: Additional prefix for included routes
|
287
|
+
tags: Additional tags for included routes
|
288
|
+
|
289
|
+
Example:
|
290
|
+
users_router = Router()
|
291
|
+
@users_router.get("/{user_id}")
|
292
|
+
async def get_user(user_id: str):
|
293
|
+
return {"id": user_id}
|
294
|
+
|
295
|
+
main_router = Router()
|
296
|
+
main_router.include_router(users_router, prefix="/users", tags=["users"])
|
297
|
+
"""
|
298
|
+
self.child_routers.append((router, prefix, tags))
|
299
|
+
|
300
|
+
def get_routes(self) -> List[Route]:
|
301
|
+
"""
|
302
|
+
Get all routes including child routers.
|
303
|
+
|
304
|
+
Returns:
|
305
|
+
List of Route objects with prefixes applied
|
306
|
+
"""
|
307
|
+
routes = []
|
308
|
+
|
309
|
+
# Add direct routes with prefix
|
310
|
+
for route in self.routes:
|
311
|
+
# Create a copy with the prefix applied
|
312
|
+
prefixed_route = Route(
|
313
|
+
path=self.prefix + route.path,
|
314
|
+
endpoint=route.endpoint,
|
315
|
+
methods=route.methods,
|
316
|
+
name=route.name,
|
317
|
+
include_in_schema=route.include_in_schema,
|
318
|
+
tags=route.tags,
|
319
|
+
summary=route.summary,
|
320
|
+
description=route.description,
|
321
|
+
response_model=route.response_model,
|
322
|
+
status_code=route.status_code,
|
323
|
+
)
|
324
|
+
routes.append(prefixed_route)
|
325
|
+
|
326
|
+
# Add child router routes
|
327
|
+
for child_router, child_prefix, child_tags in self.child_routers:
|
328
|
+
for route in child_router.get_routes():
|
329
|
+
# Apply additional prefix and tags
|
330
|
+
combined_prefix = self.prefix + child_prefix
|
331
|
+
combined_tags = route.tags + (child_tags or [])
|
332
|
+
|
333
|
+
prefixed_route = Route(
|
334
|
+
path=combined_prefix + route.path,
|
335
|
+
endpoint=route.endpoint,
|
336
|
+
methods=route.methods,
|
337
|
+
name=route.name,
|
338
|
+
include_in_schema=route.include_in_schema,
|
339
|
+
tags=combined_tags,
|
340
|
+
summary=route.summary,
|
341
|
+
description=route.description,
|
342
|
+
response_model=route.response_model,
|
343
|
+
status_code=route.status_code,
|
344
|
+
)
|
345
|
+
routes.append(prefixed_route)
|
346
|
+
|
347
|
+
return routes
|
348
|
+
|
349
|
+
|
350
|
+
__all__ = ["Router"]
|