From df22cd98b58eeb9422bda85d5d94669fdd8b7ebb Mon Sep 17 00:00:00 2001 From: vincentsarago Date: Tue, 4 Feb 2025 13:49:02 +0100 Subject: [PATCH 1/3] update stac-fastapi-pgstac to 4.0 --- infrastructure/app.py | 2 +- runtimes/eoapi/stac/eoapi/stac/app.py | 143 +++++++++++++----- runtimes/eoapi/stac/eoapi/stac/config.py | 3 + .../eoapi/stac/templates/stac-viewer.html | 3 +- runtimes/eoapi/stac/pyproject.toml | 2 +- 5 files changed, 111 insertions(+), 42 deletions(-) diff --git a/infrastructure/app.py b/infrastructure/app.py index a2dcd8d..277af0d 100644 --- a/infrastructure/app.py +++ b/infrastructure/app.py @@ -220,7 +220,7 @@ def __init__( "port" ).to_string(), "EOAPI_STAC_TITILER_ENDPOINT": raster.url.strip("/"), - "EOAPI_STAC_EXTENSIONS": '["filter", "query", "sort", "fields", "pagination", "titiler"]', + "EOAPI_STAC_EXTENSIONS": '["filter", "query", "sort", "fields", "pagination", "titiler", "collection_search", "free_text"]', }, db=pgstac_db.connection_target, db_secret=pgstac_db.pgstac_secret, diff --git a/runtimes/eoapi/stac/eoapi/stac/app.py b/runtimes/eoapi/stac/eoapi/stac/app.py index 65a2b0b..9e8f86d 100644 --- a/runtimes/eoapi/stac/eoapi/stac/app.py +++ b/runtimes/eoapi/stac/eoapi/stac/app.py @@ -9,18 +9,28 @@ from fastapi.responses import ORJSONResponse from stac_fastapi.api.app import StacApi from stac_fastapi.api.models import ( + EmptyRequest, ItemCollectionUri, create_get_request_model, create_post_request_model, create_request_model, ) from stac_fastapi.extensions.core import ( + CollectionSearchExtension, + CollectionSearchFilterExtension, FieldsExtension, - FilterExtension, + FreeTextExtension, + ItemCollectionFilterExtension, + OffsetPaginationExtension, + SearchFilterExtension, SortExtension, TokenPaginationExtension, TransactionExtension, ) +from stac_fastapi.extensions.core.fields import FieldsConformanceClasses +from stac_fastapi.extensions.core.free_text import FreeTextConformanceClasses +from stac_fastapi.extensions.core.query import QueryConformanceClasses +from stac_fastapi.extensions.core.sort import SortConformanceClasses from stac_fastapi.extensions.third_party import BulkTransactionExtension from stac_fastapi.pgstac.config import Settings from stac_fastapi.pgstac.core import CoreCrudClient @@ -53,52 +63,119 @@ auth_settings = OpenIdConnectSettings() settings = Settings(enable_response_models=True) +enabled_extensions = api_settings.extensions or [] + # Logs init_logging(debug=api_settings.debug) logger = logging.getLogger(__name__) # Extensions -extensions_map = { +# application extensions +application_extensions_map = { "transaction": TransactionExtension( client=TransactionsClient(), settings=settings, response_class=ORJSONResponse, ), + "bulk_transactions": BulkTransactionExtension(client=BulkTransactionsClient()), +} +if "titiler" in enabled_extensions and api_settings.titiler_endpoint: + application_extensions_map["titiler"] = TiTilerExtension( + titiler_endpoint=api_settings.titiler_endpoint + ) + +# search extensions +search_extensions_map = { "query": QueryExtension(), "sort": SortExtension(), "fields": FieldsExtension(), + "filter": SearchFilterExtension(client=FiltersClient()), "pagination": TokenPaginationExtension(), - "filter": FilterExtension(client=FiltersClient()), - "bulk_transactions": BulkTransactionExtension(client=BulkTransactionsClient()), - "titiler": ( - TiTilerExtension(titiler_endpoint=api_settings.titiler_endpoint) - if api_settings.titiler_endpoint - else None +} + +# collection_search extensions +cs_extensions_map = { + "query": QueryExtension(conformance_classes=[QueryConformanceClasses.COLLECTIONS]), + "sort": SortExtension(conformance_classes=[SortConformanceClasses.COLLECTIONS]), + "fields": FieldsExtension( + conformance_classes=[FieldsConformanceClasses.COLLECTIONS] + ), + "filter": CollectionSearchFilterExtension(client=FiltersClient()), + "free_text": FreeTextExtension( + conformance_classes=[FreeTextConformanceClasses.COLLECTIONS], ), + "pagination": OffsetPaginationExtension(), +} + +# item_collection extensions +itm_col_extensions_map = { + "query": QueryExtension( + conformance_classes=[QueryConformanceClasses.ITEMS], + ), + "sort": SortExtension( + conformance_classes=[SortConformanceClasses.ITEMS], + ), + "fields": FieldsExtension(conformance_classes=[FieldsConformanceClasses.ITEMS]), + "filter": ItemCollectionFilterExtension(client=FiltersClient()), + "pagination": TokenPaginationExtension(), } -if enabled_extensions := api_settings.extensions: - extensions = [ - extensions_map.get(name) - for name in enabled_extensions - if name in extensions_map +application_extensions = [ + extension + for key, extension in application_extensions_map.items() + if key in enabled_extensions +] + +# Request Models +# /search models +search_extensions = [ + extension + for key, extension in search_extensions_map.items() + if key in enabled_extensions +] +post_request_model = create_post_request_model( + search_extensions, base_model=PgstacSearch +) +get_request_model = create_get_request_model(search_extensions) +application_extensions.extend(search_extensions) + +# /collections/{collectionId}/items model +items_get_request_model = ItemCollectionUri +itm_col_extensions = [ + extension + for key, extension in itm_col_extensions_map.items() + if key in enabled_extensions +] +if itm_col_extensions: + items_get_request_model = create_request_model( + model_name="ItemCollectionUri", + base_model=ItemCollectionUri, + extensions=itm_col_extensions, + request_type="GET", + ) + application_extensions.extend(itm_col_extensions) + +# /collections model +collections_get_request_model = EmptyRequest +if "collection_search" in enabled_extensions: + cs_extensions = [ + extension + for key, extension in cs_extensions_map.items() + if key in enabled_extensions ] -else: - extensions = list(extensions_map.values()) + collection_search_extension = CollectionSearchExtension.from_extensions( + cs_extensions + ) + collections_get_request_model = collection_search_extension.GET + application_extensions.append(collection_search_extension) @asynccontextmanager async def lifespan(app: FastAPI): """FastAPI Lifespan.""" - logger.debug("Connecting to db...") await connect_to_db(app) - logger.debug("Connected to db.") - yield - - logger.debug("Closing db connections...") await close_db_connection(app) - logger.debug("Closed db connection.") # Middlewares @@ -114,19 +191,6 @@ async def lifespan(app: FastAPI): ) ) -# Custom Models -items_get_model = ItemCollectionUri -if any(isinstance(ext, TokenPaginationExtension) for ext in extensions): - items_get_model = create_request_model( - model_name="ItemCollectionUri", - base_model=ItemCollectionUri, - mixins=[TokenPaginationExtension().GET], - request_type="GET", - ) - -search_get_model = create_get_request_model(extensions) -search_post_model = create_post_request_model(extensions, base_model=PgstacSearch) - api = StacApi( app=FastAPI( title=api_settings.name, @@ -142,11 +206,12 @@ async def lifespan(app: FastAPI): title=api_settings.name, description=api_settings.name, settings=settings, - extensions=extensions, - client=CoreCrudClient(post_request_model=search_post_model), - items_get_request_model=items_get_model, - search_get_request_model=search_get_model, - search_post_request_model=search_post_model, + extensions=application_extensions, + client=CoreCrudClient(pgstac_search_model=post_request_model), + items_get_request_model=items_get_request_model, + search_get_request_model=get_request_model, + search_post_request_model=post_request_model, + collections_get_request_model=collections_get_request_model, response_class=ORJSONResponse, middlewares=middlewares, ) diff --git a/runtimes/eoapi/stac/eoapi/stac/config.py b/runtimes/eoapi/stac/eoapi/stac/config.py index 1a0e4a0..f013a0c 100644 --- a/runtimes/eoapi/stac/eoapi/stac/config.py +++ b/runtimes/eoapi/stac/eoapi/stac/config.py @@ -24,7 +24,10 @@ class ApiSettings(BaseSettings): "fields", "pagination", "titiler", + "free_text", "transaction", + # "bulk_transactions", + "collection_search", ] @field_validator("cors_origins") diff --git a/runtimes/eoapi/stac/eoapi/stac/templates/stac-viewer.html b/runtimes/eoapi/stac/eoapi/stac/templates/stac-viewer.html index c48f6c4..b85b203 100644 --- a/runtimes/eoapi/stac/eoapi/stac/templates/stac-viewer.html +++ b/runtimes/eoapi/stac/eoapi/stac/templates/stac-viewer.html @@ -351,6 +351,7 @@ } const query = (body) => { + console.log(body) return fetch('{{ endpoint }}/search', { method: 'POST', body: JSON.stringify(body), @@ -369,7 +370,7 @@ return e; }) - if (data.context.matched === 0) { + if (data.numberMatched === 0) { throw Error("No item found") } diff --git a/runtimes/eoapi/stac/pyproject.toml b/runtimes/eoapi/stac/pyproject.toml index 76d1881..0365ece 100644 --- a/runtimes/eoapi/stac/pyproject.toml +++ b/runtimes/eoapi/stac/pyproject.toml @@ -20,7 +20,7 @@ classifiers = [ ] dynamic = ["version"] dependencies = [ - "stac-fastapi.pgstac>=3.0,<3.1", + "stac-fastapi.pgstac>=4.0,<4.1", "jinja2>=2.11.2,<4.0.0", "starlette-cramjam>=0.4,<0.5", "psycopg_pool", From 67c2aecea09a41275994b0f639e164545c691b59 Mon Sep 17 00:00:00 2001 From: Vincent Sarago Date: Tue, 4 Feb 2025 13:54:15 +0100 Subject: [PATCH 2/3] Update runtimes/eoapi/stac/eoapi/stac/templates/stac-viewer.html --- runtimes/eoapi/stac/eoapi/stac/templates/stac-viewer.html | 1 - 1 file changed, 1 deletion(-) diff --git a/runtimes/eoapi/stac/eoapi/stac/templates/stac-viewer.html b/runtimes/eoapi/stac/eoapi/stac/templates/stac-viewer.html index b85b203..75c42f3 100644 --- a/runtimes/eoapi/stac/eoapi/stac/templates/stac-viewer.html +++ b/runtimes/eoapi/stac/eoapi/stac/templates/stac-viewer.html @@ -351,7 +351,6 @@ } const query = (body) => { - console.log(body) return fetch('{{ endpoint }}/search', { method: 'POST', body: JSON.stringify(body), From f785737785bf7847db36939e5549b2ed3c1cbc24 Mon Sep 17 00:00:00 2001 From: vincentsarago Date: Tue, 4 Feb 2025 17:58:56 +0100 Subject: [PATCH 3/3] update eoapi-cdk and pgstac version --- infrastructure/app.py | 1 + pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/infrastructure/app.py b/infrastructure/app.py index 7822fab..bf21a7a 100644 --- a/infrastructure/app.py +++ b/infrastructure/app.py @@ -121,6 +121,7 @@ def __init__( custom_resource_properties={ "context": True, "mosaic_index": True, + "pgstac_version": "0.9.2", }, ) diff --git a/pyproject.toml b/pyproject.toml index 4a66918..898d79d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ dependencies = [] [dependency-groups] deploy = [ "boto3==1.24.15", - "eoapi-cdk==7.4.2", + "eoapi-cdk==7.5.0", "pydantic-settings[yaml]==2.2.1", "pydantic==2.7", "typing-extensions>=4.12.2",