Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions stapi-fastapi/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## Unreleased

### Changed

- The GetOrder, GetOrders, and GetOrderStatuses Callable aliases are refactored into two classes (GetOrders and GetOrderStatus) that
must be implemented instead. These are genericized to allow appropriate type annotations such that FastAPI will correctly serialize
subclasses of OrderStatus.
- The use of the RootRouter class in ProductRouter are refactored to use two Protocols (ConformancesSupport and RootProvider), so as
to decouple the actual (generified) RootRouter from the uses in that class.

## [0.8.0] - 2025-12-18

### Added
Expand Down
2 changes: 0 additions & 2 deletions stapi-fastapi/src/stapi_fastapi/backends/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from .root_backend import (
GetOpportunitySearchRecord,
GetOpportunitySearchRecords,
GetOrder,
GetOrders,
GetOrderStatuses,
)
Expand All @@ -17,7 +16,6 @@
"GetOpportunityCollection",
"GetOpportunitySearchRecord",
"GetOpportunitySearchRecords",
"GetOrder",
"GetOrders",
"GetOrderStatuses",
"SearchOpportunities",
Expand Down
144 changes: 74 additions & 70 deletions stapi-fastapi/src/stapi_fastapi/backends/root_backend.py
Original file line number Diff line number Diff line change
@@ -1,79 +1,83 @@
from abc import abstractmethod
from collections.abc import Callable, Coroutine
from typing import Any, TypeVar
from typing import Any, Generic, Protocol

from fastapi import Request
from returns.maybe import Maybe
from returns.result import ResultE
from stapi_pydantic import (
OpportunitySearchRecord,
OpportunitySearchStatus,
Order,
OrderStatus,
)

GetOrders = Callable[
[str | None, int, Request],
Coroutine[Any, Any, ResultE[tuple[list[Order[OrderStatus]], Maybe[str], Maybe[int]]]],
]
"""
Type alias for an async function that returns a list of existing Orders.

Args:
next (str | None): A pagination token.
limit (int): The maximum number of orders to return in a page.
request (Request): FastAPI's Request object.

Returns:
A tuple containing a list of orders and a pagination token.

- Should return returns.result.Success[tuple[list[Order], returns.maybe.Some[str]]]
if including a pagination token
- Should return returns.result.Success[tuple[list[Order], returns.maybe.Nothing]]
if not including a pagination token
- Returning returns.result.Failure[Exception] will result in a 500.
"""

GetOrder = Callable[[str, Request], Coroutine[Any, Any, ResultE[Maybe[Order[OrderStatus]]]]]
"""
Type alias for an async function that gets details for the order with `order_id`.

Args:
order_id (str): The order ID.
request (Request): FastAPI's Request object.
from stapi_pydantic import OpportunitySearchRecord, OpportunitySearchStatus, Order, OrderStatusBound


class GetOrders(Protocol, Generic[OrderStatusBound]):
"""Interface for getting a list of orders or a single order."""

@abstractmethod
async def get_orders(
self,
next: str | None,
limit: int,
request: Request,
) -> ResultE[tuple[list[Order[OrderStatusBound]], Maybe[str], Maybe[int]]]:
"""Get a list of Order objects.

Args:
next (str | None): A pagination token.
limit (int): The maximum number of orders to return in a page.
request (Request): FastAPI's Request object.

Returns:
A tuple containing a list of orders and a pagination token.

- Should return returns.result.Success[tuple[list[Order], returns.maybe.Some[str]]]
if including a pagination token
- Should return returns.result.Success[tuple[list[Order], returns.maybe.Nothing]]
if not including a pagination token
- Returning returns.result.Failure[Exception] will result in a 500.
"""

@abstractmethod
async def get_order(self, order_id: str, request: Request) -> ResultE[Maybe[Order[OrderStatusBound]]]:
"""Get details for the order with `order_id`.

Args:
order_id (str): The order ID.
request (Request): FastAPI's Request object.

Returns:
- Should return returns.result.Success[returns.maybe.Some[Order]] if order is found.
- Should return returns.result.Success[returns.maybe.Nothing] if the order is not found or if access is
denied.
- Returning returns.result.Failure[Exception] will result in a 500.
"""


class GetOrderStatuses(Protocol, Generic[OrderStatusBound]):
"""Callable class wrapping an async method that gets statuses for the order with `order_id`."""

@abstractmethod
async def get_order_statuses(
self, order_id: str, next: str | None, limit: int, request: Request
) -> ResultE[Maybe[tuple[list[OrderStatusBound], Maybe[str]]]]:
"""Method that gets statuses for the order with `order_id`.

Args:
order_id (str): The order ID.
next (str | None): A pagination token.
limit (int): The maximum number of statuses to return in a page.
request (Request): FastAPI's Request object.

Returns:
A tuple containing a list of order statuses and a pagination token.

- Should return returns.result.Success[returns.maybe.Some[tuple[list[OrderStatus], returns.maybe.Some[str]]]
if order is found and including a pagination token.
- Should return returns.result.Success[returns.maybe.Some[tuple[list[OrderStatus], returns.maybe.Nothing]]]
if order is found and not including a pagination token.
- Should return returns.result.Success[returns.maybe.Nothing] if the order is not found or if access is
denied.
- Returning returns.result.Failure[Exception] will result in a 500.
"""

Returns:
- Should return returns.result.Success[returns.maybe.Some[Order]] if order is found.
- Should return returns.result.Success[returns.maybe.Nothing] if the order is not found or if access is denied.
- Returning returns.result.Failure[Exception] will result in a 500.
"""


T = TypeVar("T", bound=OrderStatus)


GetOrderStatuses = Callable[
[str, str | None, int, Request],
Coroutine[Any, Any, ResultE[Maybe[tuple[list[T], Maybe[str]]]]],
]
"""
Type alias for an async function that gets statuses for the order with `order_id`.

Args:
order_id (str): The order ID.
next (str | None): A pagination token.
limit (int): The maximum number of statuses to return in a page.
request (Request): FastAPI's Request object.

Returns:
A tuple containing a list of order statuses and a pagination token.

- Should return returns.result.Success[returns.maybe.Some[tuple[list[OrderStatus], returns.maybe.Some[str]]]
if order is found and including a pagination token.
- Should return returns.result.Success[returns.maybe.Some[tuple[list[OrderStatus], returns.maybe.Nothing]]]
if order is found and not including a pagination token.
- Should return returns.result.Success[returns.maybe.Nothing] if the order is not found or if access is denied.
- Returning returns.result.Failure[Exception] will result in a 500.
"""

GetOpportunitySearchRecords = Callable[
[str | None, int, Request],
Expand Down
Loading