Skip to content
This repository was archived by the owner on Apr 2, 2025. It is now read-only.

Commit 37bda4b

Browse files
committed
fix: revert back to a clunky, but functional, opportunity search endpoint
1 parent 7a00095 commit 37bda4b

File tree

2 files changed

+29
-36
lines changed

2 files changed

+29
-36
lines changed

src/stapi_fastapi/models/opportunity.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class OpportunitySearchStatus(BaseModel):
5959
class OpportunitySearchRecord(BaseModel):
6060
id: str
6161
product_id: str
62-
request: OpportunityRequest
62+
opportunity_request: OpportunityRequest
6363
status: OpportunitySearchStatus
6464
links: list[Link] = Field(default_factory=list)
6565

src/stapi_fastapi/routers/product_router.py

Lines changed: 28 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
Response,
1515
status,
1616
)
17+
from fastapi.responses import JSONResponse
1718
from geojson_pydantic.geometries import Geometry
1819
from returns.maybe import Maybe, Some
1920
from returns.result import Failure, Success
@@ -48,7 +49,7 @@ def get_preference(prefer: str | None = Header(None)) -> str | None:
4849
detail=f"Invalid Prefer header value: {prefer}",
4950
)
5051

51-
return prefer
52+
return Prefer(prefer)
5253

5354

5455
class ProductRouter(APIRouter):
@@ -129,7 +130,10 @@ async def _create_order(
129130
tags=["Products"],
130131
)
131132

132-
if product.supports_opportunity_search:
133+
if (
134+
product.supports_opportunity_search
135+
or root_router.supports_async_opportunity_search
136+
):
133137
self.add_api_route(
134138
path="/opportunities",
135139
endpoint=self.search_opportunities,
@@ -141,21 +145,17 @@ async def _create_order(
141145
Geometry,
142146
self.product.opportunity_properties, # type: ignore
143147
],
148+
responses={
149+
201: {
150+
"model": OpportunitySearchRecord,
151+
"content": {TYPE_JSON: {}},
152+
}
153+
},
144154
summary="Search Opportunities for the product",
145155
tags=["Products"],
146156
)
147157

148158
if root_router.supports_async_opportunity_search:
149-
self.add_api_route(
150-
path="/opportunities",
151-
endpoint=self.search_opportunities_async,
152-
name=f"{self.root_router.name}:{self.product.id}:search-opportunities",
153-
methods=["POST"],
154-
status_code=status.HTTP_201_CREATED,
155-
summary="Search Opportunities for the product",
156-
tags=["Products"],
157-
)
158-
159159
self.add_api_route(
160160
path="/opportunities/{opportunity_collection_id}",
161161
endpoint=self.get_opportunity_collection,
@@ -216,18 +216,19 @@ def get_product(self: Self, request: Request) -> Product:
216216
],
217217
)
218218

219-
async def search_opportunities(
219+
async def search_opportunities( # noqa: C901
220220
self: Self,
221221
search: OpportunityRequest,
222222
request: Request,
223-
response: GeoJSONResponse,
224-
prefer: str | None = Depends(get_preference),
223+
response: Response,
225224
next: Annotated[str | None, Body()] = None,
226225
limit: Annotated[int, Body()] = 10,
227-
) -> OpportunityCollection:
226+
prefer: str | None = Depends(get_preference),
227+
) -> OpportunityCollection | Response:
228228
"""
229229
Explore the opportunities available for a particular set of constraints
230230
"""
231+
# synchronous opportunities search
231232
if (
232233
not self.root_router.supports_async_opportunity_search
233234
or prefer is Prefer.wait
@@ -272,20 +273,7 @@ async def search_opportunities(
272273

273274
return OpportunityCollection(features=features, links=links)
274275

275-
raise AssertionError("Expected code to be unreachable")
276-
277-
async def search_opportunities_async(
278-
self: Self,
279-
search: OpportunityRequest,
280-
request: Request,
281-
response: Response,
282-
prefer: str | None = Depends(get_preference),
283-
) -> OpportunitySearchRecord:
284-
"""
285-
Initiate an asynchronous search for opportunities.
286-
287-
TODO: Do I need a location header somewhere?
288-
"""
276+
# asynchronous opportunities search
289277
if (
290278
prefer is None
291279
or prefer is Prefer.respond_async
@@ -296,14 +284,19 @@ async def search_opportunities_async(
296284
self.root_router.add_opportunity_search_record_self_link(
297285
search_record, request
298286
)
299-
response.headers["Location"] = str(
287+
headers = {}
288+
headers["Location"] = str(
300289
self.root_router.generate_opportunity_search_record_href(
301290
request, search_record.id
302291
)
303292
)
304293
if prefer is not None:
305-
response.headers["Preference-Applied"] = "respond-async"
306-
return search_record
294+
headers["Preference-Applied"] = "respond-async"
295+
return JSONResponse(
296+
status_code=201,
297+
content=search_record.model_dump(mode="json"),
298+
headers=headers,
299+
)
307300
case Failure(e) if isinstance(e, ConstraintsException):
308301
raise e
309302
case Failure(e):
@@ -315,8 +308,8 @@ async def search_opportunities_async(
315308
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
316309
detail="Error initiating an asynchronous opportunity search",
317310
)
318-
case x:
319-
raise AssertionError(f"Expected code to be unreachable: {x}")
311+
case y:
312+
raise AssertionError(f"Expected code to be unreachable: {y}")
320313

321314
raise AssertionError("Expected code to be unreachable")
322315

0 commit comments

Comments
 (0)