22
33import logging
44import traceback
5- from typing import TYPE_CHECKING , Any , TypeVar
5+ from typing import TYPE_CHECKING , Any
66
77from fastapi import (
88 Depends ,
5050from stapi_fastapi .routers .utils import json_link
5151
5252if TYPE_CHECKING :
53- from stapi_fastapi .routers import RootRouter
53+ from stapi_fastapi .routers .root_router import ConformancesSupport , RootProvider
54+
5455
5556logger = logging .getLogger (__name__ )
5657
@@ -68,10 +69,7 @@ def get_prefer(prefer: str | None = Header(None)) -> str | None:
6869 return Prefer (prefer )
6970
7071
71- T = TypeVar ("T" , bound = OrderStatus )
72-
73-
74- def build_conformances (product : Product , root_router : RootRouter [T ]) -> list [str ]:
72+ def build_conformances (product : Product , conformances_support : ConformancesSupport ) -> list [str ]:
7573 # FIXME we can make this check more robust
7674 if not any (conformance .startswith ("https://geojson.org/schema/" ) for conformance in product .conformsTo ):
7775 raise ValueError ("product conformance does not contain at least one geojson conformance" )
@@ -81,7 +79,7 @@ def build_conformances(product: Product, root_router: RootRouter[T]) -> list[str
8179 if product .supports_opportunity_search :
8280 conformances .add (PRODUCT_CONFORMACES .opportunities )
8381
84- if product .supports_async_opportunity_search and root_router .supports_async_opportunity_search :
82+ if product .supports_async_opportunity_search and conformances_support .supports_async_opportunity_search :
8583 conformances .add (PRODUCT_CONFORMACES .opportunities )
8684 conformances .add (PRODUCT_CONFORMACES .opportunities_async )
8785
@@ -93,20 +91,21 @@ class ProductRouter(StapiFastapiBaseRouter):
9391 def __init__ ( # noqa
9492 self ,
9593 product : Product ,
96- root_router : RootRouter [ T ] ,
94+ root_provider : RootProvider ,
9795 * args : Any ,
9896 ** kwargs : Any ,
9997 ) -> None :
10098 super ().__init__ (* args , ** kwargs )
10199
102100 self .product = product
103- self .root_router = root_router
104- self .conformances = build_conformances (product , root_router )
101+ self .root_provider = root_provider
102+ self .conformances_support : ConformancesSupport = root_provider
103+ self .conformances = build_conformances (product , root_provider )
105104
106105 self .add_api_route (
107106 path = "" ,
108107 endpoint = self .get_product ,
109- name = f"{ self .root_router .name } :{ self .product .id } :{ GET_PRODUCT } " ,
108+ name = f"{ self .root_provider .name } :{ self .product .id } :{ GET_PRODUCT } " ,
110109 methods = ["GET" ],
111110 summary = "Retrieve this product" ,
112111 tags = ["Products" ],
@@ -115,7 +114,7 @@ def __init__( # noqa
115114 self .add_api_route (
116115 path = "/conformance" ,
117116 endpoint = self .get_product_conformance ,
118- name = f"{ self .root_router .name } :{ self .product .id } :{ CONFORMANCE } " ,
117+ name = f"{ self .root_provider .name } :{ self .product .id } :{ CONFORMANCE } " ,
119118 methods = ["GET" ],
120119 summary = "Get conformance urls for the product" ,
121120 tags = ["Products" ],
@@ -124,7 +123,7 @@ def __init__( # noqa
124123 self .add_api_route (
125124 path = "/queryables" ,
126125 endpoint = self .get_product_queryables ,
127- name = f"{ self .root_router .name } :{ self .product .id } :{ GET_QUERYABLES } " ,
126+ name = f"{ self .root_provider .name } :{ self .product .id } :{ GET_QUERYABLES } " ,
128127 methods = ["GET" ],
129128 summary = "Get queryables for the product" ,
130129 tags = ["Products" ],
@@ -133,7 +132,7 @@ def __init__( # noqa
133132 self .add_api_route (
134133 path = "/order-parameters" ,
135134 endpoint = self .get_product_order_parameters ,
136- name = f"{ self .root_router .name } :{ self .product .id } :{ GET_ORDER_PARAMETERS } " ,
135+ name = f"{ self .root_provider .name } :{ self .product .id } :{ GET_ORDER_PARAMETERS } " ,
137136 methods = ["GET" ],
138137 summary = "Get order parameters for the product" ,
139138 tags = ["Products" ],
@@ -160,7 +159,7 @@ async def _create_order(
160159 self .add_api_route (
161160 path = "/orders" ,
162161 endpoint = _create_order ,
163- name = f"{ self .root_router .name } :{ self .product .id } :{ CREATE_ORDER } " ,
162+ name = f"{ self .root_provider .name } :{ self .product .id } :{ CREATE_ORDER } " ,
164163 methods = ["POST" ],
165164 response_class = GeoJSONResponse ,
166165 status_code = status .HTTP_201_CREATED ,
@@ -169,12 +168,13 @@ async def _create_order(
169168 )
170169
171170 if product .supports_opportunity_search or (
172- self .product .supports_async_opportunity_search and self .root_router .supports_async_opportunity_search
171+ self .product .supports_async_opportunity_search
172+ and self .conformances_support .supports_async_opportunity_search
173173 ):
174174 self .add_api_route (
175175 path = "/opportunities" ,
176176 endpoint = self .search_opportunities ,
177- name = f"{ self .root_router .name } :{ self .product .id } :{ SEARCH_OPPORTUNITIES } " ,
177+ name = f"{ self .root_provider .name } :{ self .product .id } :{ SEARCH_OPPORTUNITIES } " ,
178178 methods = ["POST" ],
179179 response_class = GeoJSONResponse ,
180180 # unknown why mypy can't see the queryables property on Product, ignoring
@@ -192,11 +192,11 @@ async def _create_order(
192192 tags = ["Products" ],
193193 )
194194
195- if product .supports_async_opportunity_search and root_router .supports_async_opportunity_search :
195+ if product .supports_async_opportunity_search and self . conformances_support .supports_async_opportunity_search :
196196 self .add_api_route (
197197 path = "/opportunities/{opportunity_collection_id}" ,
198198 endpoint = self .get_opportunity_collection ,
199- name = f"{ self .root_router .name } :{ self .product .id } :{ GET_OPPORTUNITY_COLLECTION } " ,
199+ name = f"{ self .root_provider .name } :{ self .product .id } :{ GET_OPPORTUNITY_COLLECTION } " ,
200200 methods = ["GET" ],
201201 response_class = GeoJSONResponse ,
202202 summary = "Get an Opportunity Collection by ID" ,
@@ -205,30 +205,34 @@ async def _create_order(
205205
206206 def get_product (self , request : Request ) -> ProductPydantic :
207207 links = [
208- json_link ("self" , self .url_for (request , f"{ self .root_router .name } :{ self .product .id } :{ GET_PRODUCT } " )),
209- json_link ("conformance" , self .url_for (request , f"{ self .root_router .name } :{ self .product .id } :{ CONFORMANCE } " )),
208+ json_link ("self" , self .url_for (request , f"{ self .root_provider .name } :{ self .product .id } :{ GET_PRODUCT } " )),
210209 json_link (
211- "queryables" , self .url_for (request , f"{ self .root_router .name } :{ self .product .id } :{ GET_QUERYABLES } " )
210+ "conformance" , self .url_for (request , f"{ self .root_provider .name } :{ self .product .id } :{ CONFORMANCE } " )
211+ ),
212+ json_link (
213+ "queryables" ,
214+ self .url_for (request , f"{ self .root_provider .name } :{ self .product .id } :{ GET_QUERYABLES } " ),
212215 ),
213216 json_link (
214217 "order-parameters" ,
215- self .url_for (request , f"{ self .root_router .name } :{ self .product .id } :{ GET_ORDER_PARAMETERS } " ),
218+ self .url_for (request , f"{ self .root_provider .name } :{ self .product .id } :{ GET_ORDER_PARAMETERS } " ),
216219 ),
217220 Link (
218- href = self .url_for (request , f"{ self .root_router .name } :{ self .product .id } :{ CREATE_ORDER } " ),
221+ href = self .url_for (request , f"{ self .root_provider .name } :{ self .product .id } :{ CREATE_ORDER } " ),
219222 rel = "create-order" ,
220223 type = TYPE_JSON ,
221224 method = "POST" ,
222225 ),
223226 ]
224227
225228 if self .product .supports_opportunity_search or (
226- self .product .supports_async_opportunity_search and self .root_router .supports_async_opportunity_search
229+ self .product .supports_async_opportunity_search
230+ and self .conformances_support .supports_async_opportunity_search
227231 ):
228232 links .append (
229233 json_link (
230234 "opportunities" ,
231- self .url_for (request , f"{ self .root_router .name } :{ self .product .id } :{ SEARCH_OPPORTUNITIES } " ),
235+ self .url_for (request , f"{ self .root_provider .name } :{ self .product .id } :{ SEARCH_OPPORTUNITIES } " ),
232236 ),
233237 )
234238
@@ -246,7 +250,8 @@ async def search_opportunities(
246250 """
247251 # sync
248252 if not (
249- self .root_router .supports_async_opportunity_search and self .product .supports_async_opportunity_search
253+ self .product .supports_async_opportunity_search
254+ and self .conformances_support .supports_async_opportunity_search
250255 ) or (prefer is Prefer .wait and self .product .supports_opportunity_search ):
251256 return await self .search_opportunities_sync (
252257 search ,
@@ -301,7 +306,7 @@ async def search_opportunities_sync(
301306 case x :
302307 raise AssertionError (f"Expected code to be unreachable { x } " )
303308
304- if prefer is Prefer .wait and self .root_router .supports_async_opportunity_search :
309+ if prefer is Prefer .wait and self .conformances_support .supports_async_opportunity_search :
305310 response .headers ["Preference-Applied" ] = "wait"
306311
307312 return OpportunityCollection (features = features , links = links )
@@ -314,10 +319,12 @@ async def search_opportunities_async(
314319 ) -> JSONResponse :
315320 match await self .product .search_opportunities_async (self , search , request ):
316321 case Success (search_record ):
317- search_record .links .append (self .root_router .opportunity_search_record_self_link (search_record , request ))
322+ search_record .links .append (
323+ self .root_provider .opportunity_search_record_self_link (search_record , request )
324+ )
318325 headers = {}
319326 headers ["Location" ] = str (
320- self .root_router .generate_opportunity_search_record_href (request , search_record .id )
327+ self .root_provider .generate_opportunity_search_record_href (request , search_record .id )
321328 )
322329 if prefer is not None :
323330 headers ["Preference-Applied" ] = "respond-async"
@@ -368,8 +375,8 @@ async def create_order(self, payload: OrderPayload, request: Request, response:
368375 request ,
369376 ):
370377 case Success (order ):
371- order .links .extend (self .root_router .order_links (order , request ))
372- location = str (self .root_router .generate_order_href (request , order .id ))
378+ order .links .extend (self .root_provider .order_links (order , request ))
379+ location = str (self .root_provider .generate_order_href (request , order .id ))
373380 response .headers ["Location" ] = location
374381 return order # type: ignore
375382 case Failure (e ) if isinstance (e , QueryablesError ):
@@ -388,7 +395,7 @@ async def create_order(self, payload: OrderPayload, request: Request, response:
388395
389396 def order_link (self , request : Request , opp_req : OpportunityPayload ) -> Link :
390397 return Link (
391- href = self .url_for (request , f"{ self .root_router .name } :{ self .product .id } :{ CREATE_ORDER } " ),
398+ href = self .url_for (request , f"{ self .root_provider .name } :{ self .product .id } :{ CREATE_ORDER } " ),
392399 rel = "create-order" ,
393400 type = TYPE_JSON ,
394401 method = "POST" ,
@@ -423,7 +430,7 @@ async def get_opportunity_collection(
423430 "self" ,
424431 self .url_for (
425432 request ,
426- f"{ self .root_router .name } :{ self .product .id } :{ GET_OPPORTUNITY_COLLECTION } " ,
433+ f"{ self .root_provider .name } :{ self .product .id } :{ GET_OPPORTUNITY_COLLECTION } " ,
427434 opportunity_collection_id = opportunity_collection_id ,
428435 ),
429436 ),
0 commit comments