Skip to content
Merged
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
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ dev = [
"pre-commit-hooks>=5.0.0",
"fastapi[standard]>=0.115.12",
"pygithub>=2.6.1",
"respx>=0.22.0",
]
docs = [
"mkdocs-material>=9.6.11",
Expand Down
4 changes: 0 additions & 4 deletions pystapi-client/src/pystapi_client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -432,10 +432,6 @@ def get_order(self, order_id: str) -> Order: # type: ignore[type-arg]

order_endpoint = self._get_orders_href(order_id)
order_json = self.stapi_io.read_json(order_endpoint)

if order_json is None:
raise ValueError(f"Order {order_id} not found")

return Order.model_validate(order_json)

def _get_orders_href(self, order_id: str | None = None) -> str:
Expand Down
2 changes: 1 addition & 1 deletion pystapi-client/src/pystapi_client/stapi_api_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ def request(
resp = self.session.send(modified)
except Exception as err:
logger.debug(err)
raise APIError.from_response(resp)
raise APIError(f"Error sending request: {err}")

# NOTE what about other successful status codes?
if resp.status_code != 200:
Expand Down
56 changes: 56 additions & 0 deletions pystapi-client/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import json
from collections.abc import Generator
from copy import deepcopy
from pathlib import Path
from typing import Any

import httpx
import pytest
import respx
from httpx import Response

WORKING_DIR = Path(__file__).parent


def load_fixture(name: str) -> dict[str, Any]:
with open(WORKING_DIR / "fixtures" / f"{name}.json") as f:
return json.load(f) # type: ignore[no-any-return]


@pytest.fixture
def mocked_api() -> Generator[respx.MockRouter, None, None]:
landing_page_data = load_fixture("landing_page")
products = load_fixture("products")

with respx.mock(base_url="https://stapi.example.com", assert_all_called=False) as respx_mock:
landing_page = respx_mock.get("/")
landing_page.return_value = Response(200, json=landing_page_data)

conformance_route = respx_mock.get("/conformance")
conformance_route.return_value = Response(200, json={"conformsTo": landing_page_data["conformsTo"]})

# products_route.return_value = Response(200, json=products)

def mock_products_response(request: httpx.Request) -> httpx.Response:
products_limited = deepcopy(products)
limit = request.url.params.get("limit")
page = int(request.url.params.get("page", 1))
if limit is not None:
start_index = (page - 1) * int(limit)
end_index = start_index + int(limit)
products_limited["products"] = products_limited["products"][start_index:end_index]
has_next_page = end_index < len(products_limited["products"]) + 1
if has_next_page:
products_limited["links"].append(
{
"href": "https://stapi.example.com/products?limit=1&page=2",
"method": "GET",
"rel": "next",
}
)
return Response(200, json=products_limited)

respx_mock.get("/products").mock(side_effect=mock_products_response)
respx_mock.get("/products", params={"limit": 1}).mock(side_effect=mock_products_response)

yield respx_mock
45 changes: 45 additions & 0 deletions pystapi-client/tests/fixtures/landing_page.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@

{
"id": "example-stapi",
"title": "A simple STAPI Example",
"description": "This API demonstrated the landing page for a SpatioTemporal Asset Tasking API",
"conformsTo" : [
"https://stapi.example.com/v0.1.0/core",
"https://geojson.org/schema/Point.json",
"https://geojson.org/schema/Polygon.json"
],
"links": [
{
"rel": "conformance",
"type": "application/json",
"href": "https://stapi.example.com/conformance",
"title": "Conformance classes implemented by this API"
},
{
"rel": "orders",
"type": "application/json",
"href": "https://stapi.example.com/orders",
"title": "List of existing orders"
},
{
"rel": "products",
"type": "application/json",
"href": "https://stapi.example.com/products",
"title": "List of available products"
},
{
"rel": "service-desc",
"type": "application/vnd.oai.openapi+json;version=3.0",
"href": "https://stapi.example.com/api",
"title": "The machine-readable API definition"

},
{
"rel": "service-doc",
"type": "text/html",
"href": "https://stapi.example.com/api.html",
"title": "The human-readable API documentation"

}
]
}
168 changes: 168 additions & 0 deletions pystapi-client/tests/fixtures/products.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
{
"products": [
{
"type": "Collection",
"id": "multispectral",
"title": "Multispectral",
"description": "Full color EO image",
"keywords": [
"EO",
"color"
],
"license": "license",
"providers": [
{
"name": "planet",
"description": "planet description",
"roles": [
"producer"
],
"url": "https://planet.com"
}
],
"links": [
{
"href": "https://stapi.example.com/",
"rel": "latest-version",
"type": "media type",
"title": "title"
}
],
"queryables": {
"gsd": {
"minimum": 0.5,
"maximum": 10.0
},
"target_elevation": {
"minimum": 30.0,
"maximum": 90.0
},
"target_azimuth": {
"minimum": -360.0,
"maximum": 360.0
},
"view:sun_elevation": {
"minimum": 10.0,
"maximum": 90.0
},
"view:sun_azimuth": {
"minimum": -360.0,
"maximum": 360.0
},
"view:off_nadir": {
"minimum": 0.0,
"maximum": 30.0
},
"cloud_coverage_prediction_max": {
"type": "number",
"minimum": 0,
"maximum": 100,
"multipleOf": 0.01
}
},
"parameters": {
"eo:cloud_cover": {
"type": "number",
"minimum": 0,
"maximum": 100,
"multipleOf": 0.01
}
},
"properties": {
"eo:bands": [
{
"name": "band1",
"common_name": "blue",
"center_wavelength": 0.47,
"full_width_half_max": 0.07,
"solar_illumination": 1959.66
},
{
"name": "band2",
"common_name": "green",
"center_wavelength": 0.56,
"full_width_half_max": 0.08,
"solar_illumination": 1823.24
},
{
"name": "band3",
"common_name": "red",
"center_wavelength": 0.645,
"full_width_half_max": 0.09,
"solar_illumination": 1512.06
},
{
"name": "band4",
"common_name": "nir",
"center_wavelength": 0.8,
"full_width_half_max": 0.152,
"solar_illumination": 1041.63
}
]
}
},
{
"type": "Collection",
"id": "spotlight",
"title": "Spotlight",
"description": "SAR Spotlight frame",
"keywords": [
"SAR",
"spotlight"
],
"license": "license",
"providers": [
{
"name": "planet",
"description": "planet description",
"roles": [
"producer"
],
"url": "https://planet.com"
}
],
"links": [
{
"href": "https://stapi.example.com/",
"rel": "latest-version",
"type": "media type",
"title": "title"
}
],
"queryables": {
"sar:resolution_range": {
"minimum": 0.5,
"maximum": 5.0
},
"sar:resolution_azimuth": {
"minimum": 0.5,
"maximum": 5.0
},
"grazing": {
"minimum": 20.0,
"maximum": 40.0
},
"target_azimuth": {
"minimum": -360.0,
"maximum": 360.0
},
"squint": {
"minimum": -5.0,
"maximum": 5.0
}
},
"parameters": {
"sar:polarizarions": [
"HH",
"VV",
"HV"
]
},
"properties": {
"sar:product_type": "SSC",
"sar:frequency_band": "X"
}
}
],
"links": []
}
25 changes: 25 additions & 0 deletions pystapi-client/tests/test_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import respx
from pystapi_client.client import Client
from stapi_pydantic import Link


def test_get_products(mocked_api: respx.MockRouter) -> None:
client = Client.open(url="https://stapi.example.com")

products = list(client.get_products())
assert len(products) == 2


def test_get_products_paginated(mocked_api: respx.MockRouter) -> None:
client = Client.open(url="https://stapi.example.com")

products = list(client.get_products(limit=1))
assert len(products) == 2


def test_pagination(mocked_api: respx.MockRouter) -> None:
client = Client.open(url="https://stapi.example.com")

products_link = Link(href="https://stapi.example.com/products", method="GET", body={"limit": 1}, rel="")
for products_collection in client.stapi_io.get_pages(products_link, "products"):
assert len(products_collection["products"]) == 1
Loading