Skip to content

Commit 36e700e

Browse files
author
Phil Varner
committed
convert to classes
1 parent ccb2e82 commit 36e700e

6 files changed

Lines changed: 128 additions & 130 deletions

File tree

stapi-fastapi/src/stapi_fastapi/backends/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from .root_backend import (
88
GetOpportunitySearchRecord,
99
GetOpportunitySearchRecords,
10-
GetOrder,
1110
GetOrders,
1211
GetOrderStatuses,
1312
)
@@ -17,7 +16,6 @@
1716
"GetOpportunityCollection",
1817
"GetOpportunitySearchRecord",
1918
"GetOpportunitySearchRecords",
20-
"GetOrder",
2119
"GetOrders",
2220
"GetOrderStatuses",
2321
"SearchOpportunities",

stapi-fastapi/src/stapi_fastapi/backends/root_backend.py

Lines changed: 55 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from abc import abstractmethod
12
from collections.abc import Callable, Coroutine
23
from typing import Any, Generic, Protocol
34

@@ -8,70 +9,74 @@
89

910

1011
class GetOrders(Protocol, Generic[OrderStatusBound]):
11-
"""Callable class wrapping an async method that returns a list of Orders.
12+
"""Interface for getting a list of orders or a single order."""
1213

13-
Args:
14-
next (str | None): A pagination token.
15-
limit (int): The maximum number of orders to return in a page.
16-
request (Request): FastAPI's Request object.
17-
18-
Returns:
19-
A tuple containing a list of orders and a pagination token.
20-
21-
- Should return returns.result.Success[tuple[list[Order], returns.maybe.Some[str]]]
22-
if including a pagination token
23-
- Should return returns.result.Success[tuple[list[Order], returns.maybe.Nothing]]
24-
if not including a pagination token
25-
- Returning returns.result.Failure[Exception] will result in a 500.
26-
"""
27-
28-
async def __call__(
14+
@abstractmethod
15+
async def get_orders(
2916
self,
3017
next: str | None,
3118
limit: int,
3219
request: Request,
33-
) -> ResultE[tuple[list[Order[OrderStatusBound]], Maybe[str], Maybe[int]]]: ...
20+
) -> ResultE[tuple[list[Order[OrderStatusBound]], Maybe[str], Maybe[int]]]:
21+
"""Get a list of Order objects.
3422
23+
Args:
24+
next (str | None): A pagination token.
25+
limit (int): The maximum number of orders to return in a page.
26+
request (Request): FastAPI's Request object.
3527
36-
class GetOrder(Protocol, Generic[OrderStatusBound]):
37-
"""Callable class wrapping an async method that gets details for the order with `order_id`.
28+
Returns:
29+
A tuple containing a list of orders and a pagination token.
3830
39-
Args:
40-
order_id (str): The order ID.
41-
request (Request): FastAPI's Request object.
31+
- Should return returns.result.Success[tuple[list[Order], returns.maybe.Some[str]]]
32+
if including a pagination token
33+
- Should return returns.result.Success[tuple[list[Order], returns.maybe.Nothing]]
34+
if not including a pagination token
35+
- Returning returns.result.Failure[Exception] will result in a 500.
36+
"""
4237

43-
Returns:
44-
- Should return returns.result.Success[returns.maybe.Some[Order]] if order is found.
45-
- Should return returns.result.Success[returns.maybe.Nothing] if the order is not found or if access is denied.
46-
- Returning returns.result.Failure[Exception] will result in a 500.
47-
"""
38+
@abstractmethod
39+
async def get_order(self, order_id: str, request: Request) -> ResultE[Maybe[Order[OrderStatusBound]]]:
40+
"""Get details for the order with `order_id`.
4841
49-
async def __call__(self, order_id: str, request: Request) -> ResultE[Maybe[Order[OrderStatusBound]]]: ...
42+
Args:
43+
order_id (str): The order ID.
44+
request (Request): FastAPI's Request object.
45+
46+
Returns:
47+
- Should return returns.result.Success[returns.maybe.Some[Order]] if order is found.
48+
- Should return returns.result.Success[returns.maybe.Nothing] if the order is not found or if access is
49+
denied.
50+
- Returning returns.result.Failure[Exception] will result in a 500.
51+
"""
5052

5153

5254
class GetOrderStatuses(Protocol, Generic[OrderStatusBound]):
53-
"""Callable class wrapping an async method that gets statuses for the order with `order_id`.
54-
55-
Args:
56-
order_id (str): The order ID.
57-
next (str | None): A pagination token.
58-
limit (int): The maximum number of statuses to return in a page.
59-
request (Request): FastAPI's Request object.
60-
61-
Returns:
62-
A tuple containing a list of order statuses and a pagination token.
63-
64-
- Should return returns.result.Success[returns.maybe.Some[tuple[list[OrderStatus], returns.maybe.Some[str]]]
65-
if order is found and including a pagination token.
66-
- Should return returns.result.Success[returns.maybe.Some[tuple[list[OrderStatus], returns.maybe.Nothing]]]
67-
if order is found and not including a pagination token.
68-
- Should return returns.result.Success[returns.maybe.Nothing] if the order is not found or if access is denied.
69-
- Returning returns.result.Failure[Exception] will result in a 500.
70-
"""
71-
72-
async def __call__(
55+
"""Callable class wrapping an async method that gets statuses for the order with `order_id`."""
56+
57+
@abstractmethod
58+
async def get_order_statuses(
7359
self, order_id: str, _next: str | None, limit: int, request: Request
74-
) -> ResultE[Maybe[tuple[list[OrderStatusBound], Maybe[str]]]]: ...
60+
) -> ResultE[Maybe[tuple[list[OrderStatusBound], Maybe[str]]]]:
61+
"""Method that gets statuses for the order with `order_id`.
62+
63+
Args:
64+
order_id (str): The order ID.
65+
next (str | None): A pagination token.
66+
limit (int): The maximum number of statuses to return in a page.
67+
request (Request): FastAPI's Request object.
68+
69+
Returns:
70+
A tuple containing a list of order statuses and a pagination token.
71+
72+
- Should return returns.result.Success[returns.maybe.Some[tuple[list[OrderStatus], returns.maybe.Some[str]]]
73+
if order is found and including a pagination token.
74+
- Should return returns.result.Success[returns.maybe.Some[tuple[list[OrderStatus], returns.maybe.Nothing]]]
75+
if order is found and not including a pagination token.
76+
- Should return returns.result.Success[returns.maybe.Nothing] if the order is not found or if access is
77+
denied.
78+
- Returning returns.result.Failure[Exception] will result in a 500.
79+
"""
7580

7681

7782
GetOpportunitySearchRecords = Callable[

stapi-fastapi/src/stapi_fastapi/routers/root_router.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
GetOpportunitySearchRecord,
2727
GetOpportunitySearchRecords,
2828
GetOpportunitySearchRecordStatuses,
29-
GetOrder,
3029
GetOrders,
3130
GetOrderStatuses,
3231
)
@@ -83,7 +82,6 @@ class RootRouter(StapiFastapiBaseRouter, RootProvider, Generic[OrderStatusBound]
8382
def __init__(
8483
self,
8584
get_orders: GetOrders[OrderStatusBound],
86-
get_order: GetOrder[OrderStatusBound],
8785
get_order_statuses: GetOrderStatuses[OrderStatusBound] | None = None,
8886
get_opportunity_search_records: GetOpportunitySearchRecords | None = None,
8987
get_opportunity_search_record: GetOpportunitySearchRecord | None = None,
@@ -100,7 +98,6 @@ def __init__(
10098
_conformances = set(conformances)
10199

102100
self._get_orders = get_orders
103-
self._get_order = get_order
104101
self.__get_order_statuses = get_order_statuses
105102
self.__get_opportunity_search_records = get_opportunity_search_records
106103
self.__get_opportunity_search_record = get_opportunity_search_record
@@ -275,7 +272,7 @@ async def get_orders( # noqa: C901
275272
) -> OrderCollection[OrderStatusBound]:
276273
links: list[Link] = []
277274
orders_count: int | None = None
278-
match await self._get_orders(next, limit, request):
275+
match await self._get_orders.get_orders(next, limit, request):
279276
case Success((orders, maybe_pagination_token, maybe_orders_count)):
280277
for order in orders:
281278
order.links.extend(self.order_links(order, request))
@@ -313,7 +310,7 @@ async def get_order(self, order_id: str, request: Request) -> Order[OrderStatus]
313310
"""
314311
Get details for order with `order_id`.
315312
"""
316-
match await self._get_order(order_id, request):
313+
match await self._get_orders.get_order(order_id, request):
317314
case Success(Some(order)):
318315
order.links.extend(self.order_links(order, request))
319316
return order # type: ignore
@@ -340,7 +337,7 @@ async def get_order_statuses(
340337
limit: int = 10,
341338
) -> OrderStatuses[OrderStatusBound]:
342339
links: list[Link] = []
343-
match await self._get_order_statuses(order_id, next, limit, request):
340+
match await self._get_order_statuses.get_order_statuses(order_id, next, limit, request):
344341
case Success(Some((statuses, maybe_pagination_token))):
345342
links.append(self.order_statuses_link(request, order_id))
346343
match maybe_pagination_token:

stapi-fastapi/tests/application.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@
77
from stapi_fastapi.routers.root_router import RootRouter
88

99
from tests.backends import (
10+
MockGetOrders,
11+
MockGetOrderStatuses,
1012
mock_get_opportunity_search_record,
1113
mock_get_opportunity_search_records,
12-
mock_get_order,
13-
mock_get_order_statuses,
14-
mock_get_orders,
1514
)
1615
from tests.shared import (
1716
InMemoryOpportunityDB,
@@ -30,9 +29,8 @@ async def lifespan(app: FastAPI) -> AsyncIterator[dict[str, Any]]:
3029

3130

3231
root_router = RootRouter(
33-
get_orders=mock_get_orders,
34-
get_order=mock_get_order,
35-
get_order_statuses=mock_get_order_statuses,
32+
get_orders=MockGetOrders(),
33+
get_order_statuses=MockGetOrderStatuses(),
3634
get_opportunity_search_records=mock_get_opportunity_search_records,
3735
get_opportunity_search_record=mock_get_opportunity_search_record,
3836
conformances=[API.core],

stapi-fastapi/tests/backends.py

Lines changed: 60 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from fastapi import Request
55
from returns.maybe import Maybe, Nothing, Some
66
from returns.result import Failure, ResultE, Success
7+
from stapi_fastapi.backends.root_backend import GetOrders, GetOrderStatuses
78
from stapi_fastapi.routers.product_router import ProductRouter
89
from stapi_pydantic import (
910
Opportunity,
@@ -21,63 +22,65 @@
2122
)
2223

2324

24-
async def mock_get_orders(
25-
next: str | None,
26-
limit: int,
27-
request: Request,
28-
) -> ResultE[tuple[list[Order], Maybe[str], Maybe[int]]]:
29-
"""
30-
Return orders from backend. Handle pagination/limit if applicable
31-
"""
32-
count = 314
33-
try:
34-
start = 0
35-
limit = min(limit, 100)
36-
order_ids = [*request.state._orders_db._orders.keys()]
37-
38-
if next:
39-
start = order_ids.index(next)
40-
end = start + limit
41-
ids = order_ids[start:end]
42-
orders = [request.state._orders_db.get_order(order_id) for order_id in ids]
43-
44-
if end > 0 and end < len(order_ids):
45-
return Success((orders, Some(request.state._orders_db._orders[order_ids[end]].id), Some(count)))
46-
return Success((orders, Nothing, Some(count)))
47-
except Exception as e:
48-
return Failure(e)
49-
50-
51-
async def mock_get_order(order_id: str, request: Request) -> ResultE[Maybe[Order]]:
52-
"""
53-
Show details for order with `order_id`.
54-
"""
55-
try:
56-
return Success(Maybe.from_optional(request.state._orders_db.get_order(order_id)))
57-
except Exception as e:
58-
return Failure(e)
59-
60-
61-
async def mock_get_order_statuses(
62-
order_id: str, next: str | None, limit: int, request: Request
63-
) -> ResultE[Maybe[tuple[list[OrderStatus], Maybe[str]]]]:
64-
try:
65-
start = 0
66-
limit = min(limit, 100)
67-
statuses = request.state._orders_db.get_order_statuses(order_id)
68-
if statuses is None:
69-
return Success(Nothing)
70-
71-
if next:
72-
start = int(next)
73-
end = start + limit
74-
stati = statuses[start:end]
75-
76-
if end > 0 and end < len(statuses):
77-
return Success(Some((stati, Some(str(end)))))
78-
return Success(Some((stati, Nothing)))
79-
except Exception as e:
80-
return Failure(e)
25+
class MockGetOrders(GetOrders):
26+
async def get_orders(
27+
self,
28+
next: str | None,
29+
limit: int,
30+
request: Request,
31+
) -> ResultE[tuple[list[Order], Maybe[str], Maybe[int]]]:
32+
"""
33+
Return orders from backend. Handle pagination/limit if applicable
34+
"""
35+
count = 314
36+
try:
37+
start = 0
38+
limit = min(limit, 100)
39+
order_ids = [*request.state._orders_db._orders.keys()]
40+
41+
if next:
42+
start = order_ids.index(next)
43+
end = start + limit
44+
ids = order_ids[start:end]
45+
orders = [request.state._orders_db.get_order(order_id) for order_id in ids]
46+
47+
if end > 0 and end < len(order_ids):
48+
return Success((orders, Some(request.state._orders_db._orders[order_ids[end]].id), Some(count)))
49+
return Success((orders, Nothing, Some(count)))
50+
except Exception as e:
51+
return Failure(e)
52+
53+
async def get_order(self, order_id: str, request: Request) -> ResultE[Maybe[Order]]:
54+
"""
55+
Show details for order with `order_id`.
56+
"""
57+
try:
58+
return Success(Maybe.from_optional(request.state._orders_db.get_order(order_id)))
59+
except Exception as e:
60+
return Failure(e)
61+
62+
63+
class MockGetOrderStatuses(GetOrderStatuses):
64+
async def get_order_statuses(
65+
self, order_id: str, next: str | None, limit: int, request: Request
66+
) -> ResultE[Maybe[tuple[list[OrderStatus], Maybe[str]]]]:
67+
try:
68+
start = 0
69+
limit = min(limit, 100)
70+
statuses = request.state._orders_db.get_order_statuses(order_id)
71+
if statuses is None:
72+
return Success(Nothing)
73+
74+
if next:
75+
start = int(next)
76+
end = start + limit
77+
stati = statuses[start:end]
78+
79+
if end > 0 and end < len(statuses):
80+
return Success(Some((stati, Some(str(end)))))
81+
return Success(Some((stati, Nothing)))
82+
except Exception as e:
83+
return Failure(e)
8184

8285

8386
async def mock_create_order(product_router: ProductRouter, payload: OrderPayload, request: Request) -> ResultE[Order]:

stapi-fastapi/tests/conftest.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,11 @@
1717
)
1818

1919
from .backends import (
20+
MockGetOrders,
21+
MockGetOrderStatuses,
2022
mock_get_opportunity_search_record,
2123
mock_get_opportunity_search_record_statuses,
2224
mock_get_opportunity_search_records,
23-
mock_get_order,
24-
mock_get_order_statuses,
25-
mock_get_orders,
2625
)
2726
from .shared import (
2827
InMemoryOpportunityDB,
@@ -72,9 +71,8 @@ async def lifespan(app: FastAPI) -> AsyncIterator[dict[str, Any]]:
7271
pass
7372

7473
root_router = RootRouter(
75-
get_orders=mock_get_orders,
76-
get_order=mock_get_order,
77-
get_order_statuses=mock_get_order_statuses,
74+
get_orders=MockGetOrders(),
75+
get_order_statuses=MockGetOrderStatuses(),
7876
conformances=[API.core],
7977
)
8078

@@ -107,9 +105,8 @@ async def lifespan(app: FastAPI) -> AsyncIterator[dict[str, Any]]:
107105
pass
108106

109107
root_router = RootRouter(
110-
get_orders=mock_get_orders,
111-
get_order=mock_get_order,
112-
get_order_statuses=mock_get_order_statuses,
108+
get_orders=MockGetOrders(),
109+
get_order_statuses=MockGetOrderStatuses(),
113110
get_opportunity_search_records=mock_get_opportunity_search_records,
114111
get_opportunity_search_record=mock_get_opportunity_search_record,
115112
get_opportunity_search_record_statuses=mock_get_opportunity_search_record_statuses,

0 commit comments

Comments
 (0)