Skip to content

Commit 45fd13b

Browse files
authored
extract route names to constants (#146)
* extract route names to constants
1 parent ff2a923 commit 45fd13b

File tree

5 files changed

+99
-31
lines changed

5 files changed

+99
-31
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
66
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
77

8+
## unreleased
9+
10+
## Added
11+
12+
- Add constants for route names to be used in link href generation
13+
814
## [v0.6.0] - 2025-02-11
915

1016
### Added

CONTRIBUTING.md

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Contributing
2+
3+
TODO: Move most of the readme into here.
4+
5+
## Design Principles
6+
7+
### Route Names and Links
8+
9+
The route names used in route defintions should be constants in `stapi_fastapi.routers.route_names`. This
10+
makes it easier to populate these links in numerous places, including in apps that use this library.
11+
12+
The general scheme for route names should follow:
13+
14+
- `create-{x}` - create a resource `x`
15+
- `create-{x}-for-{y}` - create a resource `x` as a sub-resource or associated resource of `y`
16+
- `get-{x}` - retrieve a resource `x`
17+
- `list-{xs}` - retrieve a list of resources of type `x`
18+
- `list-{xs}-for-{y}` - retrieve a list of subresources of type `x` of a resource `y`
19+
- `set-{x}` - update an existing resource `x`
20+
- `set-{x}-for-{y}` - set a sub-resource `x` of a resource `y`, e.g., `set-status-for-order`

src/stapi_fastapi/routers/product_router.py

+21-13
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@
3030
from stapi_fastapi.models.product import Product
3131
from stapi_fastapi.models.shared import Link
3232
from stapi_fastapi.responses import GeoJSONResponse
33+
from stapi_fastapi.routers.route_names import (
34+
CREATE_ORDER,
35+
GET_CONSTRAINTS,
36+
GET_OPPORTUNITY_COLLECTION,
37+
GET_ORDER_PARAMETERS,
38+
GET_PRODUCT,
39+
SEARCH_OPPORTUNITIES,
40+
)
3341
from stapi_fastapi.types.json_schema_model import JsonSchemaModel
3442

3543
if TYPE_CHECKING:
@@ -76,7 +84,7 @@ def __init__(
7684
self.add_api_route(
7785
path="",
7886
endpoint=self.get_product,
79-
name=f"{self.root_router.name}:{self.product.id}:get-product",
87+
name=f"{self.root_router.name}:{self.product.id}:{GET_PRODUCT}",
8088
methods=["GET"],
8189
summary="Retrieve this product",
8290
tags=["Products"],
@@ -85,7 +93,7 @@ def __init__(
8593
self.add_api_route(
8694
path="/constraints",
8795
endpoint=self.get_product_constraints,
88-
name=f"{self.root_router.name}:{self.product.id}:get-constraints",
96+
name=f"{self.root_router.name}:{self.product.id}:{GET_CONSTRAINTS}",
8997
methods=["GET"],
9098
summary="Get constraints for the product",
9199
tags=["Products"],
@@ -94,7 +102,7 @@ def __init__(
94102
self.add_api_route(
95103
path="/order-parameters",
96104
endpoint=self.get_product_order_parameters,
97-
name=f"{self.root_router.name}:{self.product.id}:get-order-parameters",
105+
name=f"{self.root_router.name}:{self.product.id}:{GET_ORDER_PARAMETERS}",
98106
methods=["GET"],
99107
summary="Get order parameters for the product",
100108
tags=["Products"],
@@ -121,7 +129,7 @@ async def _create_order(
121129
self.add_api_route(
122130
path="/orders",
123131
endpoint=_create_order,
124-
name=f"{self.root_router.name}:{self.product.id}:create-order",
132+
name=f"{self.root_router.name}:{self.product.id}:{CREATE_ORDER}",
125133
methods=["POST"],
126134
response_class=GeoJSONResponse,
127135
status_code=status.HTTP_201_CREATED,
@@ -136,7 +144,7 @@ async def _create_order(
136144
self.add_api_route(
137145
path="/opportunities",
138146
endpoint=self.search_opportunities,
139-
name=f"{self.root_router.name}:{self.product.id}:search-opportunities",
147+
name=f"{self.root_router.name}:{self.product.id}:{SEARCH_OPPORTUNITIES}",
140148
methods=["POST"],
141149
response_class=GeoJSONResponse,
142150
# unknown why mypy can't see the constraints property on Product, ignoring
@@ -158,7 +166,7 @@ async def _create_order(
158166
self.add_api_route(
159167
path="/opportunities/{opportunity_collection_id}",
160168
endpoint=self.get_opportunity_collection,
161-
name=f"{self.root_router.name}:{self.product.id}:get-opportunity-collection",
169+
name=f"{self.root_router.name}:{self.product.id}:{GET_OPPORTUNITY_COLLECTION}",
162170
methods=["GET"],
163171
response_class=GeoJSONResponse,
164172
summary="Get an Opportunity Collection by ID",
@@ -170,7 +178,7 @@ def get_product(self, request: Request) -> Product:
170178
Link(
171179
href=str(
172180
request.url_for(
173-
f"{self.root_router.name}:{self.product.id}:get-product",
181+
f"{self.root_router.name}:{self.product.id}:{GET_PRODUCT}",
174182
),
175183
),
176184
rel="self",
@@ -179,7 +187,7 @@ def get_product(self, request: Request) -> Product:
179187
Link(
180188
href=str(
181189
request.url_for(
182-
f"{self.root_router.name}:{self.product.id}:get-constraints",
190+
f"{self.root_router.name}:{self.product.id}:{GET_CONSTRAINTS}",
183191
),
184192
),
185193
rel="constraints",
@@ -188,7 +196,7 @@ def get_product(self, request: Request) -> Product:
188196
Link(
189197
href=str(
190198
request.url_for(
191-
f"{self.root_router.name}:{self.product.id}:get-order-parameters",
199+
f"{self.root_router.name}:{self.product.id}:{GET_ORDER_PARAMETERS}",
192200
),
193201
),
194202
rel="order-parameters",
@@ -197,7 +205,7 @@ def get_product(self, request: Request) -> Product:
197205
Link(
198206
href=str(
199207
request.url_for(
200-
f"{self.root_router.name}:{self.product.id}:create-order",
208+
f"{self.root_router.name}:{self.product.id}:{CREATE_ORDER}",
201209
),
202210
),
203211
rel="create-order",
@@ -213,7 +221,7 @@ def get_product(self, request: Request) -> Product:
213221
Link(
214222
href=str(
215223
request.url_for(
216-
f"{self.root_router.name}:{self.product.id}:search-opportunities",
224+
f"{self.root_router.name}:{self.product.id}:{SEARCH_OPPORTUNITIES}",
217225
),
218226
),
219227
rel="opportunities",
@@ -381,7 +389,7 @@ def order_link(self, request: Request, opp_req: OpportunityPayload):
381389
return Link(
382390
href=str(
383391
request.url_for(
384-
f"{self.root_router.name}:{self.product.id}:create-order",
392+
f"{self.root_router.name}:{self.product.id}:{CREATE_ORDER}",
385393
),
386394
),
387395
rel="create-order",
@@ -419,7 +427,7 @@ async def get_opportunity_collection(
419427
Link(
420428
href=str(
421429
request.url_for(
422-
f"{self.root_router.name}:{self.product.id}:get-opportunity-collection",
430+
f"{self.root_router.name}:{self.product.id}:{GET_OPPORTUNITY_COLLECTION}",
423431
opportunity_collection_id=opportunity_collection_id,
424432
),
425433
),

src/stapi_fastapi/routers/root_router.py

+30-18
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@
3434
from stapi_fastapi.models.shared import Link
3535
from stapi_fastapi.responses import GeoJSONResponse
3636
from stapi_fastapi.routers.product_router import ProductRouter
37+
from stapi_fastapi.routers.route_names import (
38+
CONFORMANCE,
39+
GET_OPPORTUNITY_SEARCH_RECORD,
40+
GET_ORDER,
41+
LIST_OPPORTUNITY_SEARCH_RECORDS,
42+
LIST_ORDER_STATUSES,
43+
LIST_ORDERS,
44+
LIST_PRODUCTS,
45+
ROOT,
46+
)
3747

3848
logger = logging.getLogger(__name__)
3949

@@ -84,31 +94,31 @@ def __init__(
8494
"/",
8595
self.get_root,
8696
methods=["GET"],
87-
name=f"{self.name}:root",
97+
name=f"{self.name}:{ROOT}",
8898
tags=["Root"],
8999
)
90100

91101
self.add_api_route(
92102
"/conformance",
93103
self.get_conformance,
94104
methods=["GET"],
95-
name=f"{self.name}:conformance",
105+
name=f"{self.name}:{CONFORMANCE}",
96106
tags=["Conformance"],
97107
)
98108

99109
self.add_api_route(
100110
"/products",
101111
self.get_products,
102112
methods=["GET"],
103-
name=f"{self.name}:list-products",
113+
name=f"{self.name}:{LIST_PRODUCTS}",
104114
tags=["Products"],
105115
)
106116

107117
self.add_api_route(
108118
"/orders",
109119
self.get_orders,
110120
methods=["GET"],
111-
name=f"{self.name}:list-orders",
121+
name=f"{self.name}:{LIST_ORDERS}",
112122
response_class=GeoJSONResponse,
113123
tags=["Orders"],
114124
)
@@ -117,7 +127,7 @@ def __init__(
117127
"/orders/{order_id}",
118128
self.get_order,
119129
methods=["GET"],
120-
name=f"{self.name}:get-order",
130+
name=f"{self.name}:{GET_ORDER}",
121131
response_class=GeoJSONResponse,
122132
tags=["Orders"],
123133
)
@@ -126,7 +136,7 @@ def __init__(
126136
"/orders/{order_id}/statuses",
127137
self.get_order_statuses,
128138
methods=["GET"],
129-
name=f"{self.name}:list-order-statuses",
139+
name=f"{self.name}:{LIST_ORDER_STATUSES}",
130140
tags=["Orders"],
131141
)
132142

@@ -135,7 +145,7 @@ def __init__(
135145
"/searches/opportunities",
136146
self.get_opportunity_search_records,
137147
methods=["GET"],
138-
name=f"{self.name}:list-opportunity-search-records",
148+
name=f"{self.name}:{LIST_OPPORTUNITY_SEARCH_RECORDS}",
139149
summary="List all Opportunity Search Records",
140150
tags=["Opportunities"],
141151
)
@@ -144,15 +154,15 @@ def __init__(
144154
"/searches/opportunities/{search_record_id}",
145155
self.get_opportunity_search_record,
146156
methods=["GET"],
147-
name=f"{self.name}:get-opportunity-search-record",
157+
name=f"{self.name}:{GET_OPPORTUNITY_SEARCH_RECORD}",
148158
summary="Get an Opportunity Search Record by ID",
149159
tags=["Opportunities"],
150160
)
151161

152162
def get_root(self, request: Request) -> RootResponse:
153163
links = [
154164
Link(
155-
href=str(request.url_for(f"{self.name}:root")),
165+
href=str(request.url_for(f"{self.name}:{ROOT}")),
156166
rel="self",
157167
type=TYPE_JSON,
158168
),
@@ -167,17 +177,17 @@ def get_root(self, request: Request) -> RootResponse:
167177
type="text/html",
168178
),
169179
Link(
170-
href=str(request.url_for(f"{self.name}:conformance")),
180+
href=str(request.url_for(f"{self.name}:{CONFORMANCE}")),
171181
rel="conformance",
172182
type=TYPE_JSON,
173183
),
174184
Link(
175-
href=str(request.url_for(f"{self.name}:list-products")),
185+
href=str(request.url_for(f"{self.name}:{LIST_PRODUCTS}")),
176186
rel="products",
177187
type=TYPE_JSON,
178188
),
179189
Link(
180-
href=str(request.url_for(f"{self.name}:list-orders")),
190+
href=str(request.url_for(f"{self.name}:{LIST_ORDERS}")),
181191
rel="orders",
182192
type=TYPE_GEOJSON,
183193
),
@@ -187,7 +197,9 @@ def get_root(self, request: Request) -> RootResponse:
187197
links.append(
188198
Link(
189199
href=str(
190-
request.url_for(f"{self.name}:list-opportunity-search-records")
200+
request.url_for(
201+
f"{self.name}:{LIST_OPPORTUNITY_SEARCH_RECORDS}"
202+
)
191203
),
192204
rel="opportunity-search-records",
193205
type=TYPE_JSON,
@@ -220,7 +232,7 @@ def get_products(
220232
ids = self.product_ids[start:end]
221233
links = [
222234
Link(
223-
href=str(request.url_for(f"{self.name}:list-products")),
235+
href=str(request.url_for(f"{self.name}:{LIST_PRODUCTS}")),
224236
rel="self",
225237
type=TYPE_JSON,
226238
),
@@ -327,10 +339,10 @@ def add_product(self, product: Product, *args, **kwargs) -> None:
327339
self.product_ids = [*self.product_routers.keys()]
328340

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

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

335347
def order_links(self, order: Order, request: Request) -> list[Link]:
336348
return [
@@ -350,7 +362,7 @@ def order_statuses_link(self, request: Request, order_id: str):
350362
return Link(
351363
href=str(
352364
request.url_for(
353-
f"{self.name}:list-order-statuses",
365+
f"{self.name}:{LIST_ORDER_STATUSES}",
354366
order_id=order_id,
355367
)
356368
),
@@ -428,7 +440,7 @@ def generate_opportunity_search_record_href(
428440
self, request: Request, search_record_id: str
429441
) -> URL:
430442
return request.url_for(
431-
f"{self.name}:get-opportunity-search-record",
443+
f"{self.name}:{GET_OPPORTUNITY_SEARCH_RECORD}",
432444
search_record_id=search_record_id,
433445
)
434446

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Root
2+
ROOT = "root"
3+
CONFORMANCE = "conformance"
4+
5+
# Product
6+
LIST_PRODUCTS = "list-products"
7+
LIST_PRODUCTS = "list-products"
8+
GET_PRODUCT = "get-product"
9+
GET_CONSTRAINTS = "get-constraints"
10+
GET_ORDER_PARAMETERS = "get-order-parameters"
11+
12+
# Opportunity
13+
LIST_OPPORTUNITY_SEARCH_RECORDS = "list-opportunity-search-records"
14+
GET_OPPORTUNITY_SEARCH_RECORD = "get-opportunity-search-record"
15+
SEARCH_OPPORTUNITIES = "search-opportunities"
16+
GET_OPPORTUNITY_COLLECTION = "get-opportunity-collection"
17+
18+
# Order
19+
LIST_ORDERS = "list-orders"
20+
GET_ORDER = "get-order"
21+
LIST_ORDER_STATUSES = "list-order-statuses"
22+
CREATE_ORDER = "create-order"

0 commit comments

Comments
 (0)