1+ from typing import Self , Optional
12from fastapi import APIRouter , HTTPException , Request , status
23from fastapi .encoders import jsonable_encoder
34from fastapi .responses import JSONResponse
1415from stapi_fastapi .models .root import RootResponse
1516from stapi_fastapi .models .shared import HTTPException as HTTPExceptionModel
1617from stapi_fastapi .models .shared import Link
18+ from stapi_fastapi .products_router import ProductRouter
1719
1820"""
1921/products/{component router} # router for each product added to main router
2022/orders # list all orders
2123"""
22- class MainRouter :
23- NAME_PREFIX = "main"
24- backend : StapiBackend
25- openapi_endpoint_name : str
26- docs_endpoint_name : str
27- router : APIRouter
24+ class MainRouter (APIRouter ):
2825
2926 def __init__ (
30- self ,
27+ self : Self ,
3128 backend : StapiBackend ,
32- openapi_endpoint_name = "openapi" ,
33- docs_endpoint_name = "swagger_ui_html" ,
29+ name : str = "main" ,
30+ openapi_endpoint_name : str = "openapi" ,
31+ docs_endpoint_name : str = "swagger_ui_html" ,
3432 * args ,
3533 ** kwargs ,
3634 ):
35+ super ().__init__ (* args , ** kwargs )
3736 self .backend = backend
37+ self .name = name
3838 self .openapi_endpoint_name = openapi_endpoint_name
3939 self .docs_endpoint_name = docs_endpoint_name
4040
41- self .router = APIRouter (* args , ** kwargs )
42- self .router .add_api_route (
41+ self .product_routers : dict [str , ProductRouter ] = {}
42+
43+ self .add_api_route (
4344 "/" ,
4445 self .root ,
4546 methods = ["GET" ],
46- name = f"{ self .NAME_PREFIX } :root" ,
47+ name = f"{ self .name } :root" ,
4748 tags = ["Root" ],
4849 )
4950
50- self .router . add_api_route (
51+ self .add_api_route (
5152 "/products" ,
5253 self .products ,
5354 methods = ["GET" ],
54- name = f"{ self .NAME_PREFIX } :list-products" ,
55- tags = ["Product" ],
56- )
57-
58- self .router .add_api_route (
59- "/products/{product_id}" ,
60- self .product ,
61- methods = ["GET" ],
62- name = f"{ self .NAME_PREFIX } :get-product" ,
55+ name = f"{ self .name } :list-products" ,
6356 tags = ["Product" ],
64- responses = {status .HTTP_404_NOT_FOUND : {"model" : HTTPExceptionModel }},
6557 )
6658
67- self .router .add_api_route (
68- "/opportunities" ,
69- self .search_opportunities ,
70- methods = ["POST" ],
71- name = f"{ self .NAME_PREFIX } :search-opportunities" ,
72- tags = ["Opportunities" ],
73- )
74-
75- self .router .add_api_route (
76- "/orders" ,
77- self .create_order ,
78- methods = ["POST" ],
79- name = f"{ self .NAME_PREFIX } :create-order" ,
80- tags = ["Orders" ],
81- response_model = Order ,
82- )
83-
84- self .router .add_api_route (
59+ self .add_api_route (
8560 "/orders/{order_id}" ,
8661 self .get_order ,
8762 methods = ["GET" ],
88- name = f"{ self .NAME_PREFIX } :get-order" ,
63+ name = f"{ self .name } :get-order" ,
8964 tags = ["Orders" ],
9065 )
9166
9267 def root (self , request : Request ) -> RootResponse :
9368 return RootResponse (
9469 links = [
9570 Link (
96- href = str (request .url_for (f"{ self .NAME_PREFIX } :root" )),
71+ href = str (request .url_for (f"{ self .name } :root" )),
9772 rel = "self" ,
9873 type = TYPE_JSON ,
9974 ),
10075 Link (
101- href = str (request .url_for (f"{ self .NAME_PREFIX } :list-products" )),
76+ href = str (request .url_for (f"{ self .name } :list-products" )),
10277 rel = "products" ,
10378 type = TYPE_JSON ,
10479 ),
@@ -122,7 +97,7 @@ def products(self, request: Request) -> ProductsCollection:
12297 Link (
12398 href = str (
12499 request .url_for (
125- f"{ self .NAME_PREFIX } :get-product" , product_id = product .id
100+ f"{ self .name } :get-product" , product_id = product .id
126101 )
127102 ),
128103 rel = "self" ,
@@ -133,70 +108,13 @@ def products(self, request: Request) -> ProductsCollection:
133108 products = products ,
134109 links = [
135110 Link (
136- href = str (request .url_for (f"{ self .NAME_PREFIX } :list-products" )),
111+ href = str (request .url_for (f"{ self .name } :list-products" )),
137112 rel = "self" ,
138113 type = TYPE_JSON ,
139114 )
140115 ],
141116 )
142117
143- def product (self , product_id : str , request : Request ) -> Product :
144- try :
145- product = self .backend .product (product_id , request )
146- except NotFoundException as exc :
147- raise StapiException (
148- status .HTTP_404_NOT_FOUND , "product not found"
149- ) from exc
150- product .links .append (
151- Link (
152- href = str (
153- request .url_for (
154- f"{ self .NAME_PREFIX } :get-product" , product_id = product .id
155- )
156- ),
157- rel = "self" ,
158- type = TYPE_JSON ,
159- )
160- )
161- return product
162-
163- async def search_opportunities (
164- self , search : OpportunityRequest , request : Request
165- ) -> OpportunityCollection :
166- """
167- Explore the opportunities available for a particular set of constraints
168- """
169- try :
170- opportunities = await self .backend .search_opportunities (search , request )
171- except ConstraintsException as exc :
172- raise HTTPException (status .HTTP_422_UNPROCESSABLE_ENTITY , detail = exc .detail )
173- return JSONResponse (
174- jsonable_encoder (OpportunityCollection (features = opportunities )),
175- media_type = TYPE_GEOJSON ,
176- )
177-
178- async def create_order (
179- self , search : OpportunityRequest , request : Request
180- ) -> JSONResponse :
181- """
182- Create a new order.
183- """
184- try :
185- order = await self .backend .create_order (search , request )
186- except ConstraintsException as exc :
187- raise HTTPException (status .HTTP_422_UNPROCESSABLE_ENTITY , detail = exc .detail )
188-
189- location = str (
190- request .url_for (f"{ self .NAME_PREFIX } :get-order" , order_id = order .id )
191- )
192- order .links .append (Link (href = location , rel = "self" , type = TYPE_GEOJSON ))
193- return JSONResponse (
194- jsonable_encoder (order , exclude_unset = True ),
195- status .HTTP_201_CREATED ,
196- {"Location" : location },
197- TYPE_GEOJSON ,
198- )
199-
200118 async def get_order (self , order_id : str , request : Request ) -> Order :
201119 """
202120 Get details for order with `order_id`.
@@ -213,3 +131,8 @@ async def get_order(self, order_id: str, request: Request) -> Order:
213131 status .HTTP_200_OK ,
214132 media_type = TYPE_GEOJSON ,
215133 )
134+
135+ def add_product_router (self , product_router : ProductRouter ):
136+ # Give the include a prefix from the product router
137+ self .include_router (product_router , prefix = product_router .product .id )
138+ self .product_routers [product_router .product .id ] = product_router
0 commit comments