diff --git a/mkdocs.yml b/mkdocs.yml index 60374dc3b..4811370ac 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -68,7 +68,6 @@ nav: - requests: api/stac_fastapi/types/requests.md - rfc3339: api/stac_fastapi/types/rfc3339.md - search: api/stac_fastapi/types/search.md - - stac: api/stac_fastapi/types/stac.md - version: api/stac_fastapi/types/version.md - Development - Contributing: "contributing.md" - Release Notes: "release-notes.md" diff --git a/stac_fastapi/api/tests/test_api.py b/stac_fastapi/api/tests/test_api.py index 703c9d79c..9896cd4e8 100644 --- a/stac_fastapi/api/tests/test_api.py +++ b/stac_fastapi/api/tests/test_api.py @@ -1,4 +1,8 @@ +import json + +import pytest from fastapi import Depends, HTTPException, security, status +from stac_pydantic import Collection, Item from starlette.testclient import TestClient from stac_fastapi.api.app import StacApi @@ -41,7 +45,7 @@ def _assert_dependency_applied(api, routes): method=route["method"].lower(), url=path, auth=("bob", "dobbs"), - content='{"dummy": "payload"}', + content=route.get("payload"), headers={"content-type": "application/json"}, ) assert ( @@ -58,27 +62,53 @@ def test_openapi_content_type(self): == "application/vnd.oai.openapi+json;version=3.0" ) - def test_build_api_with_route_dependencies(self): + def test_build_api_with_route_dependencies( + self, collection: Collection, item: Item + ): routes = [ - {"path": "/collections", "method": "POST"}, - {"path": "/collections", "method": "PUT"}, + {"path": "/collections", "method": "POST", "payload": collection}, + {"path": "/collections", "method": "PUT", "payload": collection}, {"path": "/collections/{collectionId}", "method": "DELETE"}, - {"path": "/collections/{collectionId}/items", "method": "POST"}, - {"path": "/collections/{collectionId}/items/{itemId}", "method": "PUT"}, - {"path": "/collections/{collectionId}/items/{itemId}", "method": "DELETE"}, + { + "path": "/collections/{collectionId}/items", + "method": "POST", + "payload": item, + }, + { + "path": "/collections/{collectionId}/items/{itemId}", + "method": "PUT", + "payload": item, + }, + { + "path": "/collections/{collectionId}/items/{itemId}", + "method": "DELETE", + }, ] dependencies = [Depends(must_be_bob)] api = self._build_api(route_dependencies=[(routes, dependencies)]) self._assert_dependency_applied(api, routes) - def test_add_route_dependencies_after_building_api(self): + def test_add_route_dependencies_after_building_api( + self, collection: Collection, item: Item + ): routes = [ - {"path": "/collections", "method": "POST"}, - {"path": "/collections", "method": "PUT"}, + {"path": "/collections", "method": "POST", "payload": collection}, + {"path": "/collections", "method": "PUT", "payload": collection}, {"path": "/collections/{collectionId}", "method": "DELETE"}, - {"path": "/collections/{collectionId}/items", "method": "POST"}, - {"path": "/collections/{collectionId}/items/{itemId}", "method": "PUT"}, - {"path": "/collections/{collectionId}/items/{itemId}", "method": "DELETE"}, + { + "path": "/collections/{collectionId}/items", + "method": "POST", + "payload": item, + }, + { + "path": "/collections/{collectionId}/items/{itemId}", + "method": "PUT", + "payload": item, + }, + { + "path": "/collections/{collectionId}/items/{itemId}", + "method": "DELETE", + }, ] api = self._build_api() api.add_route_dependencies(scopes=routes, dependencies=[Depends(must_be_bob)]) @@ -132,3 +162,39 @@ def must_be_bob( detail="You're not Bob", headers={"WWW-Authenticate": "Basic"}, ) + + +@pytest.fixture +def collection(): + return json.dumps( + { + "id": "test_collection", + "title": "Test collection", + "description": "This is a test collection", + "license": "open", + "keywords": [], + "extent": { + "spatial": {"bbox": [[-105, 40, -105, 40]]}, + "temporal": {"interval": [["2023-02-12T12:00:00Z", None]]}, + }, + "links": [], + } + ) + + +@pytest.fixture +def item(): + return json.dumps( + { + "type": "Feature", + "stac_version": "1.0.0", + "stac_extensions": [], + "id": "test_item", + "geometry": {"type": "Point", "coordinates": [-105, 40]}, + "bbox": [-105, 40, -105, 40], + "properties": {"datetime": "2023-02-12T12:00:00Z"}, + "links": [], + "assets": {}, + "collection": "test_collection", + } + ) diff --git a/stac_fastapi/extensions/tests/test_transaction.py b/stac_fastapi/extensions/tests/test_transaction.py index fc5acc2cf..b2f59ae47 100644 --- a/stac_fastapi/extensions/tests/test_transaction.py +++ b/stac_fastapi/extensions/tests/test_transaction.py @@ -2,13 +2,14 @@ from typing import Iterator, Union import pytest +from stac_pydantic.item import Item +from stac_pydantic.item_collection import ItemCollection from starlette.testclient import TestClient from stac_fastapi.api.app import StacApi from stac_fastapi.extensions.core import TransactionExtension from stac_fastapi.types.config import ApiSettings from stac_fastapi.types.core import BaseCoreClient, BaseTransactionsClient -from stac_fastapi.types.stac import Item, ItemCollection class DummyCoreClient(BaseCoreClient): @@ -35,7 +36,7 @@ class DummyTransactionsClient(BaseTransactionsClient): """Defines a pattern for implementing the STAC transaction extension.""" def create_item(self, item: Union[Item, ItemCollection], *args, **kwargs): - return {"created": True, "type": item["type"]} + return {"created": True, "type": item.type} def update_item(self, *args, **kwargs): raise NotImplementedError @@ -114,7 +115,7 @@ def item() -> Item: "id": "test_item", "geometry": {"type": "Point", "coordinates": [-105, 40]}, "bbox": [-105, 40, -105, 40], - "properties": {}, + "properties": {"datetime": "2023-02-12T12:00:00Z"}, "links": [], "assets": {}, "collection": "test_collection", diff --git a/stac_fastapi/types/stac_fastapi/types/search.py b/stac_fastapi/types/stac_fastapi/types/search.py index e16259e4d..f049ccd86 100644 --- a/stac_fastapi/types/stac_fastapi/types/search.py +++ b/stac_fastapi/types/stac_fastapi/types/search.py @@ -19,18 +19,27 @@ Polygon, _GeometryBase, ) -from pydantic import BaseModel, Field, PositiveInt, field_validator, model_validator +from pydantic import BaseModel, PositiveInt, field_validator, model_validator +from pydantic.functional_validators import AfterValidator from stac_pydantic.shared import BBox from stac_pydantic.utils import AutoValueEnum from typing_extensions import Annotated, Self from stac_fastapi.types.rfc3339 import rfc3339_str_to_datetime, str_to_interval + +def check_limit(v: int) -> int: + """Validate input value.""" + if v > 10000: + return 10000 + else: + return v + + +Limit = Annotated[PositiveInt, AfterValidator(check_limit)] # Be careful: https://github.com/samuelcolvin/pydantic/issues/1423#issuecomment-642797287 NumType = Union[float, int] -Limit = Annotated[PositiveInt, Field(strict=True, le=10000)] - class Operator(str, AutoValueEnum): """Defines the set of operators supported by the API."""