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

Commit a151d25

Browse files
author
Phil Varner
committed
RootBackend protocol uses library types Result and Maybe instead of exceptions.
1 parent 3011357 commit a151d25

File tree

7 files changed

+76
-24
lines changed

7 files changed

+76
-24
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ none
1313

1414
### Changed
1515

16-
none
16+
- RootBackend protocol uses `returns` library types Result and Maybe instead of exceptions.
1717

1818
### Deprecated
1919

bin/server.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
from uuid import uuid4
33

44
from fastapi import FastAPI, Request
5+
from returns.maybe import Maybe, Nothing
6+
from returns.result import Failure, Result, Success
57

68
from stapi_fastapi.backends.product_backend import ProductBackend
79
from stapi_fastapi.backends.root_backend import RootBackend
8-
from stapi_fastapi.exceptions import NotFoundException
910
from stapi_fastapi.models.conformance import CORE
1011
from stapi_fastapi.models.opportunity import (
1112
Opportunity,
@@ -43,14 +44,16 @@ async def get_orders(self, request: Request) -> OrderCollection:
4344
"""
4445
return OrderCollection(features=list(self._orders.values()))
4546

46-
async def get_order(self, order_id: str, request: Request) -> Order:
47+
async def get_order(
48+
self, order_id: str, request: Request
49+
) -> Result[Order, Maybe[Exception]]:
4750
"""
4851
Show details for order with `order_id`.
4952
"""
50-
try:
51-
return self._orders[order_id]
52-
except KeyError:
53-
raise NotFoundException()
53+
if order := self._orders.get(order_id):
54+
return Success(order)
55+
56+
return Failure(Nothing)
5457

5558

5659
class MockProductBackend(ProductBackend):

poetry.lock

Lines changed: 22 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ fastapi = "^0.115.0"
1414
pydantic = "^2.9.2"
1515
geojson-pydantic = "^1.1.1"
1616
pygeofilter = "^0.2.4"
17+
returns = "^0.23.0"
1718

1819
[tool.poetry.group.dev]
1920
optional = true

src/stapi_fastapi/backends/root_backend.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from typing import Protocol
22

33
from fastapi import Request
4+
from returns.maybe import Maybe
5+
from returns.result import Result
46

57
from stapi_fastapi.models.order import Order, OrderCollection
68

@@ -12,11 +14,18 @@ async def get_orders(self, request: Request) -> OrderCollection:
1214
"""
1315
...
1416

15-
async def get_order(self, order_id: str, request: Request) -> Order:
17+
async def get_order(
18+
self, order_id: str, request: Request
19+
) -> Result[Order, Maybe[Exception]]:
1620
"""
1721
Get details for order with `order_id`.
1822
19-
Backends must raise `stapi_fastapi.exceptions.NotFoundException`
20-
if not found or access denied.
23+
Should return returns.results.Success[Order] if order is found.
24+
25+
Should return returns.results.Failure[returns.maybe.Nothing] if the order is
26+
not found or if access is denied. If there is an Exception associated with attempting to find the order,
27+
then resturns.results.Failure[returns.maybe.Some[Exception]] should be returned.
28+
29+
Typically, a Failure[Nothing] will result in a 404 and Failure[Some[Exception]] will resulting in a 500.
2130
"""
2231
...

src/stapi_fastapi/routers/root_router.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1+
import logging
12
from typing import Self
23

3-
from fastapi import APIRouter, Request
4+
from fastapi import APIRouter, HTTPException, Request, status
45
from fastapi.datastructures import URL
6+
from returns.maybe import Some
7+
from returns.result import Failure, Success
58

69
from stapi_fastapi.backends.root_backend import RootBackend
710
from stapi_fastapi.constants import TYPE_GEOJSON, TYPE_JSON
11+
from stapi_fastapi.exceptions import NotFoundException
812
from stapi_fastapi.models.conformance import CORE, Conformance
913
from stapi_fastapi.models.order import Order, OrderCollection
1014
from stapi_fastapi.models.product import Product, ProductsCollection
@@ -152,9 +156,22 @@ async def get_order(self: Self, order_id: str, request: Request) -> Order:
152156
"""
153157
Get details for order with `order_id`.
154158
"""
155-
order = await self.backend.get_order(order_id, request)
156-
order.links.append(Link(href=str(request.url), rel="self", type=TYPE_GEOJSON))
157-
return order
159+
match await self.backend.get_order(order_id, request):
160+
case Success(order):
161+
order.links.append(
162+
Link(href=str(request.url), rel="self", type=TYPE_GEOJSON)
163+
)
164+
return order
165+
case Failure(Some(e)):
166+
logging.exception(
167+
f"An error occurred while retrieving order '{order_id}'", e
168+
)
169+
raise HTTPException(
170+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
171+
detail="Error finding Order",
172+
)
173+
case _:
174+
raise NotFoundException("Order not found")
158175

159176
def add_product(self: Self, product: Product) -> None:
160177
# Give the include a prefix from the product router

tests/backends.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
from uuid import uuid4
33

44
from fastapi import Request
5+
from returns.maybe import Maybe, Nothing
6+
from returns.result import Failure, Result, Success
57

68
from stapi_fastapi.backends.product_backend import ProductBackend
79
from stapi_fastapi.backends.root_backend import RootBackend
8-
from stapi_fastapi.exceptions import ConstraintsException, NotFoundException
10+
from stapi_fastapi.exceptions import ConstraintsException
911
from stapi_fastapi.models.opportunity import Opportunity, OpportunityRequest
1012
from stapi_fastapi.models.order import (
1113
Order,
@@ -31,14 +33,16 @@ async def get_orders(self, request: Request) -> OrderCollection:
3133
"""
3234
return OrderCollection(features=list(self._orders.values()))
3335

34-
async def get_order(self, order_id: str, request: Request) -> Order:
36+
async def get_order(
37+
self, order_id: str, request: Request
38+
) -> Result[Order, Maybe[Exception]]:
3539
"""
3640
Show details for order with `order_id`.
3741
"""
38-
try:
39-
return self._orders[order_id]
40-
except KeyError:
41-
raise NotFoundException()
42+
if order := self._orders.get(order_id):
43+
return Success(order)
44+
45+
return Failure(Nothing)
4246

4347

4448
class MockProductBackend(ProductBackend):

0 commit comments

Comments
 (0)