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

Commit 30cae8d

Browse files
author
Phil Varner
committed
Merge branch 'main' into 87_order_parameters
2 parents 21e6312 + c78f069 commit 30cae8d

File tree

7 files changed

+107
-22
lines changed

7 files changed

+107
-22
lines changed

CHANGELOG.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,18 @@ 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]
8+
## [v0.1.0] - TBD
9+
10+
Initial release
911

1012
### Added
1113

1214
- Conformance endpoint `/conformance` and root body `conformsTo` attribute.
15+
- Field `product_id` to Opportunity and Order Properties
16+
- Endpoint /product/{productId}/order-parameters.
17+
- Links in Product entity to order-parameters and constraints endpoints for that product.
18+
- Add links `opportunities` and `create-order` to Product
19+
- Add link `create-order` to OpportunityCollection
1320

1421
### Changed
1522

@@ -31,9 +38,6 @@ none
3138

3239
none
3340

34-
## [v0.1.0] - 2024-10-23
35-
36-
Initial release
3741

3842
[unreleased]: https://github.com/stapi-spec/stapi-fastapi/compare/v0.1.0...main
3943
[v0.1.0]: https://github.com/stapi-spec/stapi-fastapi/tree/v0.1.0

bin/server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ class MyOpportunityProperties(OpportunityProperties):
9696

9797

9898
class MyOrderParameters(OrderParameters):
99-
s3_path: str
99+
delivery_mechanism: str | None = None
100100

101101

102102
order_db = MockOrderDB()

src/stapi_fastapi/models/opportunity.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
# Copied and modified from https://github.com/stac-utils/stac-pydantic/blob/main/stac_pydantic/item.py#L11
1313
class OpportunityProperties(BaseModel):
14+
product_id: str
1415
datetime: DatetimeInterval
1516
model_config = ConfigDict(extra="allow")
1617

src/stapi_fastapi/routers/product_router.py

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ def __init__(
6969
name=f"{self.root_router.name}:{self.product.id}:get-order-parameters",
7070
methods=["GET"],
7171
summary="Get order parameters for the product",
72+
tags=["Products"],
7273
)
7374

7475
# This wraps `self.create_order` to explicitly parameterize `OrderRequest`
@@ -118,6 +119,42 @@ def get_product(self, request: Request) -> Product:
118119
rel="self",
119120
type=TYPE_JSON,
120121
),
122+
Link(
123+
href=str(
124+
request.url_for(
125+
f"{self.root_router.name}:{self.product.id}:get-constraints",
126+
),
127+
),
128+
rel="constraints",
129+
type=TYPE_JSON,
130+
),
131+
Link(
132+
href=str(
133+
request.url_for(
134+
f"{self.root_router.name}:{self.product.id}:get-order-parameters",
135+
),
136+
),
137+
rel="order-parameters",
138+
type=TYPE_JSON,
139+
),
140+
Link(
141+
href=str(
142+
request.url_for(
143+
f"{self.root_router.name}:{self.product.id}:search-opportunities",
144+
),
145+
),
146+
rel="opportunities",
147+
type=TYPE_JSON,
148+
),
149+
Link(
150+
href=str(
151+
request.url_for(
152+
f"{self.root_router.name}:{self.product.id}:create-order",
153+
),
154+
),
155+
rel="create-order",
156+
type=TYPE_JSON,
157+
),
121158
],
122159
)
123160

@@ -134,7 +171,20 @@ async def search_opportunities(
134171
except ConstraintsException as exc:
135172
raise HTTPException(status.HTTP_422_UNPROCESSABLE_ENTITY, detail=exc.detail)
136173

137-
return OpportunityCollection(features=opportunities)
174+
return OpportunityCollection(
175+
features=opportunities,
176+
links=[
177+
Link(
178+
href=str(
179+
request.url_for(
180+
f"{self.root_router.name}:{self.product.id}:create-order",
181+
),
182+
),
183+
rel="create-order",
184+
type=TYPE_JSON,
185+
),
186+
],
187+
)
138188

139189
def get_product_constraints(self: Self) -> JsonSchemaModel:
140190
"""
@@ -144,7 +194,7 @@ def get_product_constraints(self: Self) -> JsonSchemaModel:
144194

145195
def get_product_order_parameters(self: Self) -> JsonSchemaModel:
146196
"""
147-
Return supported order parameters of a specific product
197+
Return supported constraints of a specific product
148198
"""
149199
return self.product.order_parameters
150200

tests/conftest.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,22 @@
1313
from stapi_fastapi.models.opportunity import (
1414
Opportunity,
1515
)
16-
from stapi_fastapi.models.product import Product, Provider, ProviderRole
16+
from stapi_fastapi.models.product import (
17+
OrderParameters,
18+
Product,
19+
Provider,
20+
ProviderRole,
21+
)
1722
from stapi_fastapi.routers.root_router import RootRouter
1823

1924
from .backends import MockOrderDB, MockProductBackend, MockRootBackend
2025
from .shared import SpotlightOpportunityProperties, SpotlightOrderParameters, find_link
2126

2227

28+
class TestSpotlightOrderParameters(OrderParameters):
29+
delivery_mechanism: str | None = None
30+
31+
2332
@pytest.fixture(scope="session")
2433
def base_url() -> Iterator[str]:
2534
yield "http://stapiserver"
@@ -132,6 +141,7 @@ def mock_test_spotlight_opportunities() -> list[Opportunity]:
132141
coordinates=Position2D(longitude=0.0, latitude=0.0),
133142
),
134143
properties=SpotlightOpportunityProperties(
144+
product_id="xyz123",
135145
datetime=(start, end),
136146
off_nadir=20,
137147
),

tests/opportunity_test.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ def test_search_opportunities_response(
1616
mock_test_spotlight_opportunities: List[Opportunity],
1717
product_backend: MockProductBackend,
1818
stapi_client: TestClient,
19+
assert_link,
1920
) -> None:
2021
product_backend._opportunities = mock_test_spotlight_opportunities
2122

@@ -50,9 +51,11 @@ def test_search_opportunities_response(
5051

5152
# Validate response status and structure
5253
assert response.status_code == 200, f"Failed for product: {product_id}"
53-
_json = response.json()
54+
body = response.json()
5455

5556
try:
56-
OpportunityCollection(**_json)
57+
_ = OpportunityCollection(**body)
5758
except Exception as _:
5859
pytest.fail("response is not an opportunity collection")
60+
61+
assert_link(f"POST {url}", body, "create-order", f"/products/{product_id}/order")

tests/product_test.py

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
from fastapi import status
33
from fastapi.testclient import TestClient
44

5-
from .shared import find_link
6-
75

86
def test_products_response(stapi_client: TestClient):
97
res = stapi_client.get("/products")
@@ -21,17 +19,22 @@ def test_products_response(stapi_client: TestClient):
2119
def test_product_response_self_link(
2220
product_id: str,
2321
stapi_client: TestClient,
24-
url_for,
22+
assert_link,
2523
):
2624
res = stapi_client.get(f"/products/{product_id}")
2725
assert res.status_code == status.HTTP_200_OK
2826
assert res.headers["Content-Type"] == "application/json"
2927

30-
data = res.json()
31-
link = find_link(data["links"], "self")
32-
assert link, "GET /products Link[rel=self] should exist"
33-
assert link["type"] == "application/json"
34-
assert link["href"] == url_for(f"/products/{product_id}")
28+
body = res.json()
29+
30+
url = "GET /products"
31+
assert_link(url, body, "self", f"/products/{product_id}")
32+
assert_link(url, body, "constraints", f"/products/{product_id}/constraints")
33+
assert_link(
34+
url, body, "order-parameters", f"/products/{product_id}/order-parameters"
35+
)
36+
assert_link(url, body, "opportunities", f"/products/{product_id}/opportunities")
37+
assert_link(url, body, "create-order", f"/products/{product_id}/order")
3538

3639

3740
@pytest.mark.parametrize("product_id", ["test-spotlight"])
@@ -43,7 +46,21 @@ def test_product_constraints_response(
4346
assert res.status_code == status.HTTP_200_OK
4447
assert res.headers["Content-Type"] == "application/json"
4548

46-
data = res.json()
47-
assert "properties" in data
48-
assert "datetime" in data["properties"]
49-
assert "off_nadir" in data["properties"]
49+
json_schema = res.json()
50+
assert "properties" in json_schema
51+
assert "datetime" in json_schema["properties"]
52+
assert "off_nadir" in json_schema["properties"]
53+
54+
55+
@pytest.mark.parametrize("product_id", ["test-spotlight"])
56+
def test_product_order_parameters_response(
57+
product_id: str,
58+
stapi_client: TestClient,
59+
):
60+
res = stapi_client.get(f"/products/{product_id}/order-parameters")
61+
assert res.status_code == status.HTTP_200_OK
62+
assert res.headers["Content-Type"] == "application/json"
63+
64+
json_schema = res.json()
65+
assert "properties" in json_schema
66+
assert "delivery_mechanism" in json_schema["properties"]

0 commit comments

Comments
 (0)