22import traceback
33from typing import Any
44
5- from fastapi import APIRouter , HTTPException , Request , status
5+ from fastapi import HTTPException , Request , status
6+ from fastapi .datastructures import URL
67from returns .maybe import Maybe , Some
78from returns .result import Failure , Success
89from stapi_pydantic import (
2829 GetOrderStatuses ,
2930)
3031from stapi_fastapi .conformance import API as API_CONFORMANCE
31- from stapi_fastapi .constants import TYPE_GEOJSON , TYPE_JSON
32+ from stapi_fastapi .constants import TYPE_GEOJSON
3233from stapi_fastapi .errors import NotFoundError
3334from stapi_fastapi .models .product import Product
3435from stapi_fastapi .responses import GeoJSONResponse
36+ from stapi_fastapi .routers .base import StapiFastapiBaseRouter
3537from stapi_fastapi .routers .product_router import ProductRouter
3638from stapi_fastapi .routers .route_names import (
3739 CONFORMANCE ,
4446 LIST_PRODUCTS ,
4547 ROOT ,
4648)
49+ from stapi_fastapi .routers .utils import json_link
4750
4851logger = logging .getLogger (__name__ )
4952
5053
51- class RootRouter (APIRouter ):
54+ class RootRouter (StapiFastapiBaseRouter ):
5255 def __init__ (
5356 self ,
5457 get_orders : GetOrders ,
@@ -170,50 +173,35 @@ def __init__(
170173
171174 self .conformances = list (_conformances )
172175
173- @staticmethod
174- def url_for (request : Request , name : str , / , ** path_params : Any ) -> str :
175- return str (request .url_for (name , ** path_params ))
176-
177176 def get_root (self , request : Request ) -> RootResponse :
178177 links = [
179- Link (
180- href = self .url_for (request , f"{ self .name } :{ ROOT } " ),
181- rel = "self" ,
182- type = TYPE_JSON ,
178+ json_link (
179+ "self" ,
180+ self .url_for (request , f"{ self .name } :{ ROOT } " ),
183181 ),
184- Link (
185- href = self .url_for (request , self .openapi_endpoint_name ),
186- rel = "service-description" ,
187- type = TYPE_JSON ,
182+ json_link (
183+ "service-description" ,
184+ self .url_for (request , self .openapi_endpoint_name ),
188185 ),
189186 Link (
190- href = self .url_for (request , self .docs_endpoint_name ),
191187 rel = "service-docs" ,
188+ href = self .url_for (request , self .docs_endpoint_name ),
192189 type = "text/html" ,
193190 ),
191+ json_link ("conformance" , href = self .url_for (request , f"{ self .name } :{ CONFORMANCE } " )),
192+ json_link ("products" , self .url_for (request , f"{ self .name } :{ LIST_PRODUCTS } " )),
194193 Link (
195- href = self .url_for (request , f"{ self .name } :{ CONFORMANCE } " ),
196- rel = "conformance" ,
197- type = TYPE_JSON ,
198- ),
199- Link (
200- href = self .url_for (request , f"{ self .name } :{ LIST_PRODUCTS } " ),
201- rel = "products" ,
202- type = TYPE_JSON ,
203- ),
204- Link (
205- href = self .url_for (request , f"{ self .name } :{ LIST_ORDERS } " ),
206194 rel = "orders" ,
195+ href = self .url_for (request , f"{ self .name } :{ LIST_ORDERS } " ),
207196 type = TYPE_GEOJSON ,
208197 ),
209198 ]
210199
211200 if self .supports_async_opportunity_search :
212201 links .append (
213- Link (
214- href = self .url_for (request , f"{ self .name } :{ LIST_OPPORTUNITY_SEARCH_RECORDS } " ),
215- rel = "opportunity-search-records" ,
216- type = TYPE_JSON ,
202+ json_link (
203+ "opportunity-search-records" ,
204+ self .url_for (request , f"{ self .name } :{ LIST_OPPORTUNITY_SEARCH_RECORDS } " ),
217205 ),
218206 )
219207
@@ -238,14 +226,13 @@ def get_products(self, request: Request, next: str | None = None, limit: int = 1
238226 end = start + limit
239227 ids = self .product_ids [start :end ]
240228 links = [
241- Link (
242- href = self .url_for (request , f"{ self .name } :{ LIST_PRODUCTS } " ),
243- rel = "self" ,
244- type = TYPE_JSON ,
229+ json_link (
230+ "self" ,
231+ self .url_for (request , f"{ self .name } :{ LIST_PRODUCTS } " ),
245232 ),
246233 ]
247234 if end > 0 and end < len (self .product_ids ):
248- links .append (self .pagination_link (request , self .product_ids [end ], limit ))
235+ links .append (self .pagination_link (request , f" { self . name } : { LIST_PRODUCTS } " , self .product_ids [end ], limit ))
249236 return ProductsCollection (
250237 products = [self .product_routers [product_id ].get_product (request ) for product_id in ids ],
251238 links = links ,
@@ -261,8 +248,8 @@ async def get_orders( # noqa: C901
261248 for order in orders :
262249 order .links .extend (self .order_links (order , request ))
263250 match maybe_pagination_token :
264- case Some (x ):
265- links .append (self .pagination_link (request , x , limit ))
251+ case Some (next_ ):
252+ links .append (self .pagination_link (request , f" { self . name } : { LIST_ORDERS } " , next_ , limit ))
266253 case Maybe .empty :
267254 pass
268255 match maybe_orders_count :
@@ -325,8 +312,12 @@ async def get_order_statuses(
325312 case Success (Some ((statuses , maybe_pagination_token ))):
326313 links .append (self .order_statuses_link (request , order_id ))
327314 match maybe_pagination_token :
328- case Some (x ):
329- links .append (self .pagination_link (request , x , limit ))
315+ case Some (next_ ):
316+ links .append (
317+ self .pagination_link (
318+ request , f"{ self .name } :{ LIST_ORDER_STATUSES } " , next_ , limit , order_id = order_id
319+ )
320+ )
330321 case Maybe .empty :
331322 pass
332323 case Success (Maybe .empty ):
@@ -353,10 +344,10 @@ def add_product(self, product: Product, *args: Any, **kwargs: Any) -> None:
353344 self .product_routers [product .id ] = product_router
354345 self .product_ids = [* self .product_routers .keys ()]
355346
356- def generate_order_href (self , request : Request , order_id : str ) -> str :
347+ def generate_order_href (self , request : Request , order_id : str ) -> URL :
357348 return self .url_for (request , f"{ self .name } :{ GET_ORDER } " , order_id = order_id )
358349
359- def generate_order_statuses_href (self , request : Request , order_id : str ) -> str :
350+ def generate_order_statuses_href (self , request : Request , order_id : str ) -> URL :
360351 return self .url_for (request , f"{ self .name } :{ LIST_ORDER_STATUSES } " , order_id = order_id )
361352
362353 def order_links (self , order : Order [OrderStatus ], request : Request ) -> list [Link ]:
@@ -366,33 +357,19 @@ def order_links(self, order: Order[OrderStatus], request: Request) -> list[Link]
366357 rel = "self" ,
367358 type = TYPE_GEOJSON ,
368359 ),
369- Link (
370- href = self .generate_order_statuses_href (request , order .id ),
371- rel = "monitor" ,
372- type = TYPE_JSON ,
360+ json_link (
361+ "monitor" ,
362+ self .generate_order_statuses_href (request , order .id ),
373363 ),
374364 ]
375365
376366 def order_statuses_link (self , request : Request , order_id : str ) -> Link :
377- return Link (
378- href = self .url_for (
379- request ,
380- f"{ self .name } :{ LIST_ORDER_STATUSES } " ,
381- order_id = order_id ,
382- ),
383- rel = "self" ,
384- type = TYPE_JSON ,
385- )
386-
387- def pagination_link (self , request : Request , pagination_token : str , limit : int ) -> Link :
388- href = str (request .url .include_query_params (next = pagination_token , limit = limit )).replace (
389- str (request .url_for (f"{ self .name } :{ ROOT } " )), self .url_for (request , f"{ self .name } :{ ROOT } " ), 1
390- )
367+ return json_link ("self" , self .url_for (request , f"{ self .name } :{ LIST_ORDER_STATUSES } " , order_id = order_id ))
391368
392- return Link (
393- href = href ,
394- rel = "next" ,
395- type = TYPE_JSON ,
369+ def pagination_link ( self , request : Request , name : str , pagination_token : str , limit : int , ** kwargs : Any ) -> Link :
370+ return json_link (
371+ "next" ,
372+ self . url_for ( request , name , ** kwargs ). include_query_params ( next = pagination_token , limit = limit ) ,
396373 )
397374
398375 async def get_opportunity_search_records (
@@ -404,8 +381,12 @@ async def get_opportunity_search_records(
404381 for record in records :
405382 record .links .append (self .opportunity_search_record_self_link (record , request ))
406383 match maybe_pagination_token :
407- case Some (x ):
408- links .append (self .pagination_link (request , x , limit ))
384+ case Some (next_ ):
385+ links .append (
386+ self .pagination_link (
387+ request , f"{ self .name } :{ LIST_OPPORTUNITY_SEARCH_RECORDS } " , next_ , limit
388+ )
389+ )
409390 case Maybe .empty :
410391 pass
411392 case Failure (ValueError ()):
@@ -470,7 +451,7 @@ async def get_opportunity_search_record_statuses(
470451 case _:
471452 raise AssertionError ("Expected code to be unreachable" )
472453
473- def generate_opportunity_search_record_href (self , request : Request , search_record_id : str ) -> str :
454+ def generate_opportunity_search_record_href (self , request : Request , search_record_id : str ) -> URL :
474455 return self .url_for (
475456 request ,
476457 f"{ self .name } :{ GET_OPPORTUNITY_SEARCH_RECORD } " ,
@@ -480,11 +461,7 @@ def generate_opportunity_search_record_href(self, request: Request, search_recor
480461 def opportunity_search_record_self_link (
481462 self , opportunity_search_record : OpportunitySearchRecord , request : Request
482463 ) -> Link :
483- return Link (
484- href = self .generate_opportunity_search_record_href (request , opportunity_search_record .id ),
485- rel = "self" ,
486- type = TYPE_JSON ,
487- )
464+ return json_link ("self" , self .generate_opportunity_search_record_href (request , opportunity_search_record .id ))
488465
489466 @property
490467 def _get_order_statuses (self ) -> GetOrderStatuses : # type: ignore
0 commit comments