diff --git a/pctiler/pctiler/endpoints/pg_mosaic.py b/pctiler/pctiler/endpoints/pg_mosaic.py index 3dd77839..d7777988 100644 --- a/pctiler/pctiler/endpoints/pg_mosaic.py +++ b/pctiler/pctiler/endpoints/pg_mosaic.py @@ -11,6 +11,7 @@ from titiler.core.resources.enums import ImageType from titiler.pgstac.dependencies import SearchIdParams, TmsTileParams from titiler.pgstac.factory import MosaicTilerFactory +from titiler.pgstac.extensions import searchInfoExtension from pccommon.config import get_collection_config from pccommon.config.collections import MosaicInfo @@ -45,9 +46,10 @@ def __init__(self, request: Request): colormap_dependency=PCColorMapParams, layer_dependency=AssetsBidxExprParams, reader_dependency=ReaderParams, - router_prefix=get_settings().mosaic_endpoint_prefix + "/{search_id}", + router_prefix=get_settings().mosaic_endpoint_prefix + "/{search_id}", # reverts /searches back to /mosaic backend_dependency=BackendParams, add_statistics=False, + extensions=[searchInfoExtension()] ) diff --git a/pctiler/tests/endpoints/test_pg_mosaic.py b/pctiler/tests/endpoints/test_pg_mosaic.py index 508bb5aa..38997694 100644 --- a/pctiler/tests/endpoints/test_pg_mosaic.py +++ b/pctiler/tests/endpoints/test_pg_mosaic.py @@ -10,7 +10,6 @@ @pytest.fixture async def register_search(client: AsyncClient) -> REGISTER_TYPE: - cql = { "filter-lang": "cql2-json", "filter": { @@ -18,13 +17,38 @@ async def register_search(client: AsyncClient) -> REGISTER_TYPE: "args": [{"op": "=", "args": [{"property": "collection"}, "naip"]}], }, } - expected_content_hash = "8b989f86a149628eabfde894fb965982" response = await client.post("/mosaic/register", json=cql) + expected_content_hash = response.json()["searchid"] resp = response.json() - return (expected_content_hash, resp) +async def test_register_search_with_geom(client: AsyncClient) -> None: + geom = { + "type": "Polygon", + "coordinates": [ + [ + [-79.09062791441062, 43.08554661560049], + [-79.0629876337021, 43.08554661560049], + [-79.0629876337021, 43.067969831431895], + [-79.09062791441062, 43.067969831431895], + [-79.09062791441062, 43.08554661560049], + ] + ], + } + cql = { + "filter-lang": "cql2-json", + "filter": { + "op": "and", + "args": [{"op": "=", "args": [{"property": "collection"}, "naip"]}, + {"op": "s_intersects", "args": [{"property": "geometry"}, geom]}], + }, + } + response = await client.post("/mosaic/register", json=cql) + assert response.status_code == 200 + assert response.json()["searchid"] == '2607eab51afd5d626da8d50d9df3bbf0' + + @pytest.mark.asyncio async def test_mosaic_info(client: AsyncClient) -> None: response = await client.get("/mosaic/info?collection=naip") @@ -43,13 +67,10 @@ async def test_register(client: AsyncClient, register_search: REGISTER_TYPE) -> # and that the search hash remains consistent assert register_resp["searchid"] == expected_content_hash # Test that the links have had the {tileMatrixSetId} template string removed - assert len(register_resp["links"]) == 2 - assert register_resp["links"][0]["href"].endswith( - f"/mosaic/{expected_content_hash}/tilejson.json" - ) - assert register_resp["links"][1]["href"].endswith( - f"/mosaic/{expected_content_hash}/WMTSCapabilities.xml" - ) + assert len(register_resp["links"]) == 3 + assert register_resp["links"][0]["href"].endswith(f"/mosaic/{expected_content_hash}/info") # gets added by searchInfoExtension + assert register_resp["links"][1]["href"].endswith(f"/mosaic/{expected_content_hash}/tilejson.json") + assert register_resp["links"][2]["href"].endswith(f"/mosaic/{expected_content_hash}/WMTSCapabilities.xml") @pytest.mark.asyncio @@ -101,6 +122,40 @@ async def test_mosaic_tile_routes( assert response.status_code == 200 +async def test_info_path_with_searchid(client: AsyncClient, register_search: REGISTER_TYPE) -> None: + # route source code is here https://github.com/developmentseed/titiler/blob/main/src/titiler/mosaic/titiler/mosaic/factory.py#L157 + # the searchId functionality is added by titiler-pgstac + route = "mosaic/{searchId}/info" + expected_content_hash, _ = register_search + formatted_route = route.format(searchId=expected_content_hash) + url = (f"/{formatted_route}?asset_bidx=image%7C1%2C2%2C3&assets=image&collection=naip") + response = await client.get(url) + assert response.status_code == 200 + + +async def test_info_path_with_bad_searchid(client: AsyncClient) -> None: + route = "mosaic/{searchId}/info" + expected_content_hash = "9b989f86a149628eabfde894fb965982" # does not match the one we registered in the fixture + formatted_route = route.format(searchId=expected_content_hash) + url = (f"/{formatted_route}?asset_bidx=image%7C1%2C2%2C3&assets=image&collection=naip") + response = await client.get(url) + assert response.status_code == 404 + + +async def test_bad_searchid(client: AsyncClient) -> None: + route = "mosaic/tiles/{searchId}/{z}/{x}/{y}" + expected_content_hash = "9b989f86a149628eabfde894fb965982" # does not match the one we registered in the fixture + formatted_route = route.format( + searchId=expected_content_hash, + z=16, + x=17218, + y=26838 + ) + url = (f"/{formatted_route}?asset_bidx=image%7C1%2C2%2C3&assets=image&collection=naip") + response = await client.get(url) + assert response.status_code == 404 + + @pytest.mark.asyncio @pytest.mark.parametrize( "route", diff --git a/scripts/test-mosaic b/scripts/test-mosaic new file mode 100755 index 00000000..ca28b9a0 --- /dev/null +++ b/scripts/test-mosaic @@ -0,0 +1,7 @@ +#!/bin/bash + +docker compose \ + -f docker-compose.yml \ + -f docker-compose.dev.yml \ + run --rm \ + tiler-dev python3 -m pytest pctiler/tests/endpoints/test_pg_mosaic.py