Skip to content
This repository was archived by the owner on Apr 2, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 2 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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ 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

## Added

- Add constants for route names to be used in link href generation

## [v0.6.0] - 2025-02-11

### Added
Expand Down
20 changes: 20 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Contributing

TODO: Move most of the readme into here.

## Design Principles

### Route Names and Links

The route names used in route defintions should be constants in `stapi_fastapi.routers.route_names`. This
makes it easier to populate these links in numerous places, including in apps that use this library.

The general scheme for route names should follow:

- `create-{x}` - create a resource `x`
- `create-{x}-for-{y}` - create a resource `x` as a sub-resource or associated resource of `y`
- `get-{x}` - retrieve a resource `x`
- `list-{xs}` - retrieve a list of resources of type `x`
- `list-{xs}-for-{y}` - retrieve a list of subresources of type `x` of a resource `y`
- `set-{x}` - update an existing resource `x`
- `set-{x}-for-{y}` - set a sub-resource `x` of a resource `y`, e.g., `set-status-for-order`
34 changes: 21 additions & 13 deletions src/stapi_fastapi/routers/product_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@
from stapi_fastapi.models.product import Product
from stapi_fastapi.models.shared import Link
from stapi_fastapi.responses import GeoJSONResponse
from stapi_fastapi.routers.route_names import (
CREATE_ORDER,
GET_CONSTRAINTS,
GET_OPPORTUNITY_COLLECTION,
GET_ORDER_PARAMETERS,
GET_PRODUCT,
SEARCH_OPPORTUNITIES,
)
from stapi_fastapi.types.json_schema_model import JsonSchemaModel

if TYPE_CHECKING:
Expand Down Expand Up @@ -76,7 +84,7 @@ def __init__(
self.add_api_route(
path="",
endpoint=self.get_product,
name=f"{self.root_router.name}:{self.product.id}:get-product",
name=f"{self.root_router.name}:{self.product.id}:{GET_PRODUCT}",
methods=["GET"],
summary="Retrieve this product",
tags=["Products"],
Expand All @@ -85,7 +93,7 @@ def __init__(
self.add_api_route(
path="/constraints",
endpoint=self.get_product_constraints,
name=f"{self.root_router.name}:{self.product.id}:get-constraints",
name=f"{self.root_router.name}:{self.product.id}:{GET_CONSTRAINTS}",
methods=["GET"],
summary="Get constraints for the product",
tags=["Products"],
Expand All @@ -94,7 +102,7 @@ def __init__(
self.add_api_route(
path="/order-parameters",
endpoint=self.get_product_order_parameters,
name=f"{self.root_router.name}:{self.product.id}:get-order-parameters",
name=f"{self.root_router.name}:{self.product.id}:{GET_ORDER_PARAMETERS}",
methods=["GET"],
summary="Get order parameters for the product",
tags=["Products"],
Expand All @@ -121,7 +129,7 @@ async def _create_order(
self.add_api_route(
path="/orders",
endpoint=_create_order,
name=f"{self.root_router.name}:{self.product.id}:create-order",
name=f"{self.root_router.name}:{self.product.id}:{CREATE_ORDER}",
methods=["POST"],
response_class=GeoJSONResponse,
status_code=status.HTTP_201_CREATED,
Expand All @@ -136,7 +144,7 @@ async def _create_order(
self.add_api_route(
path="/opportunities",
endpoint=self.search_opportunities,
name=f"{self.root_router.name}:{self.product.id}:search-opportunities",
name=f"{self.root_router.name}:{self.product.id}:{SEARCH_OPPORTUNITIES}",
methods=["POST"],
response_class=GeoJSONResponse,
# unknown why mypy can't see the constraints property on Product, ignoring
Expand All @@ -158,7 +166,7 @@ async def _create_order(
self.add_api_route(
path="/opportunities/{opportunity_collection_id}",
endpoint=self.get_opportunity_collection,
name=f"{self.root_router.name}:{self.product.id}:get-opportunity-collection",
name=f"{self.root_router.name}:{self.product.id}:{GET_OPPORTUNITY_COLLECTION}",
methods=["GET"],
response_class=GeoJSONResponse,
summary="Get an Opportunity Collection by ID",
Expand All @@ -170,7 +178,7 @@ def get_product(self, request: Request) -> Product:
Link(
href=str(
request.url_for(
f"{self.root_router.name}:{self.product.id}:get-product",
f"{self.root_router.name}:{self.product.id}:{GET_PRODUCT}",
),
),
rel="self",
Expand All @@ -179,7 +187,7 @@ def get_product(self, request: Request) -> Product:
Link(
href=str(
request.url_for(
f"{self.root_router.name}:{self.product.id}:get-constraints",
f"{self.root_router.name}:{self.product.id}:{GET_CONSTRAINTS}",
),
),
rel="constraints",
Expand All @@ -188,7 +196,7 @@ def get_product(self, request: Request) -> Product:
Link(
href=str(
request.url_for(
f"{self.root_router.name}:{self.product.id}:get-order-parameters",
f"{self.root_router.name}:{self.product.id}:{GET_ORDER_PARAMETERS}",
),
),
rel="order-parameters",
Expand All @@ -197,7 +205,7 @@ def get_product(self, request: Request) -> Product:
Link(
href=str(
request.url_for(
f"{self.root_router.name}:{self.product.id}:create-order",
f"{self.root_router.name}:{self.product.id}:{CREATE_ORDER}",
),
),
rel="create-order",
Expand All @@ -213,7 +221,7 @@ def get_product(self, request: Request) -> Product:
Link(
href=str(
request.url_for(
f"{self.root_router.name}:{self.product.id}:search-opportunities",
f"{self.root_router.name}:{self.product.id}:{SEARCH_OPPORTUNITIES}",
),
),
rel="opportunities",
Expand Down Expand Up @@ -381,7 +389,7 @@ def order_link(self, request: Request, opp_req: OpportunityPayload):
return Link(
href=str(
request.url_for(
f"{self.root_router.name}:{self.product.id}:create-order",
f"{self.root_router.name}:{self.product.id}:{CREATE_ORDER}",
),
),
rel="create-order",
Expand Down Expand Up @@ -419,7 +427,7 @@ async def get_opportunity_collection(
Link(
href=str(
request.url_for(
f"{self.root_router.name}:{self.product.id}:get-opportunity-collection",
f"{self.root_router.name}:{self.product.id}:{GET_OPPORTUNITY_COLLECTION}",
opportunity_collection_id=opportunity_collection_id,
),
),
Expand Down
48 changes: 30 additions & 18 deletions src/stapi_fastapi/routers/root_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@
from stapi_fastapi.models.shared import Link
from stapi_fastapi.responses import GeoJSONResponse
from stapi_fastapi.routers.product_router import ProductRouter
from stapi_fastapi.routers.route_names import (
CONFORMANCE,
GET_OPPORTUNITY_SEARCH_RECORD,
GET_ORDER,
LIST_OPPORTUNITY_SEARCH_RECORDS,
LIST_ORDER_STATUSES,
LIST_ORDERS,
LIST_PRODUCTS,
ROOT,
)

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -84,31 +94,31 @@ def __init__(
"/",
self.get_root,
methods=["GET"],
name=f"{self.name}:root",
name=f"{self.name}:{ROOT}",
tags=["Root"],
)

self.add_api_route(
"/conformance",
self.get_conformance,
methods=["GET"],
name=f"{self.name}:conformance",
name=f"{self.name}:{CONFORMANCE}",
tags=["Conformance"],
)

self.add_api_route(
"/products",
self.get_products,
methods=["GET"],
name=f"{self.name}:list-products",
name=f"{self.name}:{LIST_PRODUCTS}",
tags=["Products"],
)

self.add_api_route(
"/orders",
self.get_orders,
methods=["GET"],
name=f"{self.name}:list-orders",
name=f"{self.name}:{LIST_ORDERS}",
response_class=GeoJSONResponse,
tags=["Orders"],
)
Expand All @@ -117,7 +127,7 @@ def __init__(
"/orders/{order_id}",
self.get_order,
methods=["GET"],
name=f"{self.name}:get-order",
name=f"{self.name}:{GET_ORDER}",
response_class=GeoJSONResponse,
tags=["Orders"],
)
Expand All @@ -126,7 +136,7 @@ def __init__(
"/orders/{order_id}/statuses",
self.get_order_statuses,
methods=["GET"],
name=f"{self.name}:list-order-statuses",
name=f"{self.name}:{LIST_ORDER_STATUSES}",
tags=["Orders"],
)

Expand All @@ -135,7 +145,7 @@ def __init__(
"/searches/opportunities",
self.get_opportunity_search_records,
methods=["GET"],
name=f"{self.name}:list-opportunity-search-records",
name=f"{self.name}:{LIST_OPPORTUNITY_SEARCH_RECORDS}",
summary="List all Opportunity Search Records",
tags=["Opportunities"],
)
Expand All @@ -144,15 +154,15 @@ def __init__(
"/searches/opportunities/{search_record_id}",
self.get_opportunity_search_record,
methods=["GET"],
name=f"{self.name}:get-opportunity-search-record",
name=f"{self.name}:{GET_OPPORTUNITY_SEARCH_RECORD}",
summary="Get an Opportunity Search Record by ID",
tags=["Opportunities"],
)

def get_root(self, request: Request) -> RootResponse:
links = [
Link(
href=str(request.url_for(f"{self.name}:root")),
href=str(request.url_for(f"{self.name}:{ROOT}")),
rel="self",
type=TYPE_JSON,
),
Expand All @@ -167,17 +177,17 @@ def get_root(self, request: Request) -> RootResponse:
type="text/html",
),
Link(
href=str(request.url_for(f"{self.name}:conformance")),
href=str(request.url_for(f"{self.name}:{CONFORMANCE}")),
rel="conformance",
type=TYPE_JSON,
),
Link(
href=str(request.url_for(f"{self.name}:list-products")),
href=str(request.url_for(f"{self.name}:{LIST_PRODUCTS}")),
rel="products",
type=TYPE_JSON,
),
Link(
href=str(request.url_for(f"{self.name}:list-orders")),
href=str(request.url_for(f"{self.name}:{LIST_ORDERS}")),
rel="orders",
type=TYPE_GEOJSON,
),
Expand All @@ -187,7 +197,9 @@ def get_root(self, request: Request) -> RootResponse:
links.append(
Link(
href=str(
request.url_for(f"{self.name}:list-opportunity-search-records")
request.url_for(
f"{self.name}:{LIST_OPPORTUNITY_SEARCH_RECORDS}"
)
),
rel="opportunity-search-records",
type=TYPE_JSON,
Expand Down Expand Up @@ -220,7 +232,7 @@ def get_products(
ids = self.product_ids[start:end]
links = [
Link(
href=str(request.url_for(f"{self.name}:list-products")),
href=str(request.url_for(f"{self.name}:{LIST_PRODUCTS}")),
rel="self",
type=TYPE_JSON,
),
Expand Down Expand Up @@ -327,10 +339,10 @@ def add_product(self, product: Product, *args, **kwargs) -> None:
self.product_ids = [*self.product_routers.keys()]

def generate_order_href(self, request: Request, order_id: str) -> URL:
return request.url_for(f"{self.name}:get-order", order_id=order_id)
return request.url_for(f"{self.name}:{GET_ORDER}", order_id=order_id)

def generate_order_statuses_href(self, request: Request, order_id: str) -> URL:
return request.url_for(f"{self.name}:list-order-statuses", order_id=order_id)
return request.url_for(f"{self.name}:{LIST_ORDER_STATUSES}", order_id=order_id)

def order_links(self, order: Order, request: Request) -> list[Link]:
return [
Expand All @@ -350,7 +362,7 @@ def order_statuses_link(self, request: Request, order_id: str):
return Link(
href=str(
request.url_for(
f"{self.name}:list-order-statuses",
f"{self.name}:{LIST_ORDER_STATUSES}",
order_id=order_id,
)
),
Expand Down Expand Up @@ -428,7 +440,7 @@ def generate_opportunity_search_record_href(
self, request: Request, search_record_id: str
) -> URL:
return request.url_for(
f"{self.name}:get-opportunity-search-record",
f"{self.name}:{GET_OPPORTUNITY_SEARCH_RECORD}",
search_record_id=search_record_id,
)

Expand Down
23 changes: 23 additions & 0 deletions src/stapi_fastapi/routers/route_names.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
ROOT = "root"
CONFORMANCE = "conformance"
LIST_PRODUCTS = "list-products"
LIST_ORDERS = "list-orders"
GET_ORDER = "get-order"
LIST_ORDER_STATUSES = "list-order-statuses"
LIST_OPPORTUNITY_SEARCH_RECORDS = "list-opportunity-search-records"
GET_OPPORTUNITY_SEARCH_RECORD = "get-opportunity-search-record"


LIST_ORDERS = "list-orders"

# Product
LIST_PRODUCTS = "list-products"
GET_PRODUCT = "get-product"


GET_CONSTRAINTS = "get-constraints"
GET_ORDER_PARAMETERS = "get-order-parameters"
CREATE_ORDER = "create-order"
SEARCH_OPPORTUNITIES = "search-opportunities"
GET_OPPORTUNITY_COLLECTION = "get-opportunity-collection"
GET_ORDER_PARAMETERS = "get-order-parameters"
Loading