From ec1c567cc9fc340bbcf2edad0f563511ef5618a9 Mon Sep 17 00:00:00 2001 From: Jennifer Tran Date: Fri, 12 Jul 2024 16:14:38 -0700 Subject: [PATCH 1/6] feat: add optional web acl configuration for cloudfront --- .example.env | 1 + routes/infrastructure/config.py | 4 ++++ routes/infrastructure/construct.py | 1 + 3 files changed, 6 insertions(+) diff --git a/.example.env b/.example.env index 468bff2a..699d7ffa 100644 --- a/.example.env +++ b/.example.env @@ -33,3 +33,4 @@ CERT_ARN= VEDA_CLOUDFRONT= VEDA_CLOUDFRONT_OAC=[OPTIONAL, CONFIGURES ORIGIN ACCESS CONTROL, DEFAULTS TO TRUE] VEDA_CUSTOM_HOST= +VEDA_SHARED_WEB_ACL_ID=[OPTIONAL] diff --git a/routes/infrastructure/config.py b/routes/infrastructure/config.py index ec59d67e..7f355dc4 100755 --- a/routes/infrastructure/config.py +++ b/routes/infrastructure/config.py @@ -44,6 +44,10 @@ class vedaRouteSettings(BaseSettings): description="Certificate’s ARN", ) + shared_web_acl_id: Optional[str] = Field( + None, description="Shared Web ACL ID for CloudFront Distribution" + ) + class Config: """model config""" diff --git a/routes/infrastructure/construct.py b/routes/infrastructure/construct.py index 2a1bd5a1..524924e7 100755 --- a/routes/infrastructure/construct.py +++ b/routes/infrastructure/construct.py @@ -72,6 +72,7 @@ def __init__( certificate=domain_cert, default_root_object="index.html", enable_logging=True, + web_acl_id=veda_route_settings.shared_web_acl_id, domain_names=[ f"{stage}.{veda_route_settings.domain_hosted_zone_name}" ] From b36fc7cb88f94fdd372e155c4892a764f585804e Mon Sep 17 00:00:00 2001 From: Jennifer Tran Date: Mon, 15 Jul 2024 11:44:46 -0700 Subject: [PATCH 2/6] fix: update to indicate arn is needed for web acl id --- .example.env | 2 +- routes/infrastructure/config.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.example.env b/.example.env index 699d7ffa..f639ead5 100644 --- a/.example.env +++ b/.example.env @@ -33,4 +33,4 @@ CERT_ARN= VEDA_CLOUDFRONT= VEDA_CLOUDFRONT_OAC=[OPTIONAL, CONFIGURES ORIGIN ACCESS CONTROL, DEFAULTS TO TRUE] VEDA_CUSTOM_HOST= -VEDA_SHARED_WEB_ACL_ID=[OPTIONAL] +VEDA_SHARED_WEB_ACL_ID=[OPTIONAL ID ARN for WEB ACL] diff --git a/routes/infrastructure/config.py b/routes/infrastructure/config.py index 7f355dc4..e9d4f435 100755 --- a/routes/infrastructure/config.py +++ b/routes/infrastructure/config.py @@ -45,7 +45,7 @@ class vedaRouteSettings(BaseSettings): ) shared_web_acl_id: Optional[str] = Field( - None, description="Shared Web ACL ID for CloudFront Distribution" + None, description="Shared Web ACL ID ARN for CloudFront Distribution" ) class Config: From f15740f6866a7e0227e42844bd93c2f3b6941d00 Mon Sep 17 00:00:00 2001 From: Stephen Kilbourn Date: Tue, 16 Jul 2024 14:51:36 -0600 Subject: [PATCH 3/6] chore: Improve stac integration tests (#395) --- .github/workflows/tests/test_stac.py | 95 ++++++++++++++++++++++++---- 1 file changed, 84 insertions(+), 11 deletions(-) diff --git a/.github/workflows/tests/test_stac.py b/.github/workflows/tests/test_stac.py index 0bdc80d7..35e2eef6 100644 --- a/.github/workflows/tests/test_stac.py +++ b/.github/workflows/tests/test_stac.py @@ -2,16 +2,89 @@ import httpx +seeded_collection = "noaa-emergency-response" +seeded_id = "20200307aC0853300w361200" stac_endpoint = "http://0.0.0.0:8081" +index_endpoint = "index.html" +docs_endpoint = "docs" +health_endpoint = "_mgmt/ping" +search_endpoint = "search" +collections_endpoint = "collections" + + +def test_stac_health(): + """test stac health endpoint.""" + + assert httpx.get(f"{stac_endpoint}/{health_endpoint}").status_code == 200 + + +def test_stac_viewer(): + """test stac viewer.""" + resp = httpx.get(f"{stac_endpoint}/{index_endpoint}") + assert resp.status_code == 200 + assert resp.headers.get("x-correlation-id") == "local" + assert "Simple STAC API Viewer" in resp.text + + +def test_stac_docs(): + """test stac docs.""" + resp = httpx.get(f"{stac_endpoint}/{docs_endpoint}") + assert resp.status_code == 200 + assert "Swagger UI" in resp.text + + +def test_stac_get_search(): + """test stac get search.""" + default_limit_param = "?limit=10" + resp = httpx.get(f"{stac_endpoint}/{search_endpoint}{default_limit_param}") + assert resp.status_code == 200 + features = resp.json()["features"] + assert len(features) > 0 + collections = [c["collection"] for c in features] + assert seeded_collection in collections + + +def test_stac_post_search(): + """test stac post search.""" + request_body = {"collections": [seeded_collection]} + resp = httpx.post(f"{stac_endpoint}/{search_endpoint}", json=request_body) + assert resp.status_code == 200 + features = resp.json()["features"] + assert len(features) > 0 + collections = [c["collection"] for c in features] + assert seeded_collection in collections + + +def test_stac_get_collections(): + """test stac get collections""" + resp = httpx.get(f"{stac_endpoint}/{collections_endpoint}") + assert resp.status_code == 200 + collections = resp.json()["collections"] + assert len(collections) > 0 + id = [c["id"] for c in collections] + assert seeded_collection in id + + +def test_stac_get_collections_by_id(): + """test stac get collection by id""" + resp = httpx.get(f"{stac_endpoint}/{collections_endpoint}/{seeded_collection}") + assert resp.status_code == 200 + collection = resp.json() + assert collection["id"] == seeded_collection + + +def test_stac_get_collection_items_by_id(): + """test stac get items in collection by collection id""" + resp = httpx.get( + f"{stac_endpoint}/{collections_endpoint}/{seeded_collection}/items" + ) + assert resp.status_code == 200 + collection = resp.json() + assert collection["type"] == "FeatureCollection" def test_stac_api(): """test stac.""" - # Ping - assert httpx.get(f"{stac_endpoint}/_mgmt/ping").status_code == 200 - - # viewer - assert httpx.get(f"{stac_endpoint}/index.html").status_code == 200 # Collections resp = httpx.get(f"{stac_endpoint}/collections") @@ -19,35 +92,35 @@ def test_stac_api(): collections = resp.json()["collections"] assert len(collections) > 0 ids = [c["id"] for c in collections] - assert "noaa-emergency-response" in ids + assert seeded_collection in ids # items - resp = httpx.get(f"{stac_endpoint}/collections/noaa-emergency-response/items") + resp = httpx.get(f"{stac_endpoint}/collections/{seeded_collection}/items") assert resp.status_code == 200 items = resp.json()["features"] assert len(items) == 10 # item resp = httpx.get( - f"{stac_endpoint}/collections/noaa-emergency-response/items/20200307aC0853300w361200" + f"{stac_endpoint}/collections/{seeded_collection}/items/{seeded_id}" ) assert resp.status_code == 200 item = resp.json() - assert item["id"] == "20200307aC0853300w361200" + assert item["id"] == seeded_id def test_stac_to_raster(): """test link to raster api.""" # tilejson resp = httpx.get( - f"{stac_endpoint}/collections/noaa-emergency-response/items/20200307aC0853300w361200/tilejson.json", + f"{stac_endpoint}/collections/{seeded_collection}/items/{seeded_id}/tilejson.json", params={"assets": "cog"}, ) assert resp.status_code == 307 # viewer resp = httpx.get( - f"{stac_endpoint}/collections/noaa-emergency-response/items/20200307aC0853300w361200/viewer", + f"{stac_endpoint}/collections/{seeded_collection}/items/{seeded_id}/viewer", params={"assets": "cog"}, ) assert resp.status_code == 307 From f449325672b94dd5f47e3f992615050640b11dd4 Mon Sep 17 00:00:00 2001 From: Saadiq Mohiuddin <34844565+smohiudd@users.noreply.github.com> Date: Thu, 25 Jul 2024 09:27:32 -0600 Subject: [PATCH 4/6] feat: titiler-pgstac v1 upgrade (#398) upgrade to titiler-pgstac 1.3.0 --- .github/workflows/tests/test_raster.py | 60 +++++++------- raster_api/runtime/setup.py | 8 +- raster_api/runtime/src/app.py | 109 ++++++++++++++++++++----- raster_api/runtime/src/dependencies.py | 21 ----- raster_api/runtime/src/extensions.py | 9 +- 5 files changed, 128 insertions(+), 79 deletions(-) diff --git a/.github/workflows/tests/test_raster.py b/.github/workflows/tests/test_raster.py index beba896b..e21731c0 100644 --- a/.github/workflows/tests/test_raster.py +++ b/.github/workflows/tests/test_raster.py @@ -19,21 +19,23 @@ def test_raster_api(): def test_mosaic_api(): """test mosaic.""" query = {"collections": ["noaa-emergency-response"], "filter-lang": "cql-json"} - resp = httpx.post(f"{raster_endpoint}/mosaic/register", json=query) + resp = httpx.post(f"{raster_endpoint}/searches/register", json=query) assert resp.headers["content-type"] == "application/json" assert resp.status_code == 200 - assert resp.json()["searchid"] + assert resp.json()["id"] assert resp.json()["links"] - searchid = resp.json()["searchid"] + searchid = resp.json()["id"] - resp = httpx.get(f"{raster_endpoint}/mosaic/{searchid}/-85.6358,36.1624/assets") + resp = httpx.get(f"{raster_endpoint}/searches/{searchid}/-85.6358,36.1624/assets") assert resp.status_code == 200 assert len(resp.json()) == 1 assert list(resp.json()[0]) == ["id", "bbox", "assets", "collection"] assert resp.json()[0]["id"] == "20200307aC0853900w361030" - resp = httpx.get(f"{raster_endpoint}/mosaic/{searchid}/tiles/15/8589/12849/assets") + resp = httpx.get( + f"{raster_endpoint}/searches/{searchid}/tiles/15/8589/12849/assets" + ) assert resp.status_code == 200 assert len(resp.json()) == 1 assert list(resp.json()[0]) == ["id", "bbox", "assets", "collection"] @@ -41,7 +43,7 @@ def test_mosaic_api(): z, x, y = 15, 8589, 12849 resp = httpx.get( - f"{raster_endpoint}/mosaic/{searchid}/tiles/{z}/{x}/{y}", + f"{raster_endpoint}/searches/{searchid}/tiles/{z}/{x}/{y}", params={"assets": "cog"}, headers={"Accept-Encoding": "br, gzip"}, timeout=10.0, @@ -105,11 +107,11 @@ def test_mosaic_search(): }, ] for search in searches: - resp = httpx.post(f"{raster_endpoint}/mosaic/register", json=search) + resp = httpx.post(f"{raster_endpoint}/searches/register", json=search) assert resp.status_code == 200 - assert resp.json()["searchid"] + assert resp.json()["id"] - resp = httpx.get(f"{raster_endpoint}/mosaic/list") + resp = httpx.get(f"{raster_endpoint}/searches/list") assert resp.headers["content-type"] == "application/json" assert resp.status_code == 200 assert ( @@ -118,16 +120,18 @@ def test_mosaic_search(): assert resp.json()["context"]["returned"] == 10 # default limit is 10 # Make sure all mosaics returned have - for mosaic in resp.json()["searches"]: - assert mosaic["search"]["metadata"]["type"] == "mosaic" + for search in resp.json()["searches"]: + assert search["search"]["metadata"]["type"] == "mosaic" links = resp.json()["links"] assert len(links) == 2 assert links[0]["rel"] == "self" assert links[1]["rel"] == "next" - assert links[1]["href"] == f"{raster_endpoint}/mosaic/list?limit=10&offset=10" + assert links[1]["href"] == f"{raster_endpoint}/searches/list?limit=10&offset=10" - resp = httpx.get(f"{raster_endpoint}/mosaic/list", params={"limit": 1, "offset": 1}) + resp = httpx.get( + f"{raster_endpoint}/searches/list", params={"limit": 1, "offset": 1} + ) assert resp.status_code == 200 assert resp.json()["context"]["matched"] > 10 assert resp.json()["context"]["limit"] == 1 @@ -136,55 +140,51 @@ def test_mosaic_search(): links = resp.json()["links"] assert len(links) == 3 assert links[0]["rel"] == "self" - assert links[0]["href"] == f"{raster_endpoint}/mosaic/list?limit=1&offset=1" + assert links[0]["href"] == f"{raster_endpoint}/searches/list?limit=1&offset=1" assert links[1]["rel"] == "next" - assert links[1]["href"] == f"{raster_endpoint}/mosaic/list?limit=1&offset=2" + assert links[1]["href"] == f"{raster_endpoint}/searches/list?limit=1&offset=2" assert links[2]["rel"] == "prev" - assert links[2]["href"] == f"{raster_endpoint}/mosaic/list?limit=1&offset=0" + assert links[2]["href"] == f"{raster_endpoint}/searches/list?limit=1&offset=0" # Filter on mosaic metadata - resp = httpx.get(f"{raster_endpoint}/mosaic/list", params={"owner": "vincent"}) + resp = httpx.get(f"{raster_endpoint}/searches/list", params={"owner": "vincent"}) assert resp.status_code == 200 assert resp.json()["context"]["matched"] == 7 assert resp.json()["context"]["limit"] == 10 assert resp.json()["context"]["returned"] == 7 # sortBy - resp = httpx.get(f"{raster_endpoint}/mosaic/list", params={"sortby": "lastused"}) + resp = httpx.get(f"{raster_endpoint}/searches/list", params={"sortby": "lastused"}) assert resp.status_code == 200 - resp = httpx.get(f"{raster_endpoint}/mosaic/list", params={"sortby": "usecount"}) + resp = httpx.get(f"{raster_endpoint}/searches/list", params={"sortby": "usecount"}) assert resp.status_code == 200 - resp = httpx.get(f"{raster_endpoint}/mosaic/list", params={"sortby": "-owner"}) + resp = httpx.get(f"{raster_endpoint}/searches/list", params={"sortby": "-owner"}) assert resp.status_code == 200 assert ( "owner" not in resp.json()["searches"][0]["search"]["metadata"] ) # some mosaic don't have owners - resp = httpx.get(f"{raster_endpoint}/mosaic/list", params={"sortby": "owner"}) + resp = httpx.get(f"{raster_endpoint}/searches/list", params={"sortby": "owner"}) assert resp.status_code == 200 assert "owner" in resp.json()["searches"][0]["search"]["metadata"] def test_item(): """test stac endpoints.""" + collection_id = "noaa-emergency-response" + item_id = "20200307aC0853300w361200" resp = httpx.get( - f"{raster_endpoint}/stac/assets", - params={ - "collection": "noaa-emergency-response", - "item": "20200307aC0853300w361200", - }, + f"{raster_endpoint}/collections/{collection_id}/items/{item_id}/assets" ) assert resp.status_code == 200 assert resp.headers["content-type"] == "application/json" assert resp.json() == ["cog"] resp = httpx.get( - f"{raster_endpoint}/stac/tilejson.json", + f"{raster_endpoint}/collections/{collection_id}/items/{item_id}/WebMercatorQuad/tilejson.json", params={ - "collection": "noaa-emergency-response", - "item": "20200307aC0853300w361200", "assets": "cog", }, ) @@ -192,6 +192,4 @@ def test_item(): assert resp.headers["content-type"] == "application/json" assert resp.json()["tilejson"] assert "assets=cog" in resp.json()["tiles"][0] - assert "item=20200307aC0853300w361200" in resp.json()["tiles"][0] - assert "collection=noaa-emergency-response" in resp.json()["tiles"][0] assert resp.json()["bounds"] == [-85.5501, 36.1749, -85.5249, 36.2001] diff --git a/raster_api/runtime/setup.py b/raster_api/runtime/setup.py index 624e0a75..d2860fc8 100644 --- a/raster_api/runtime/setup.py +++ b/raster_api/runtime/setup.py @@ -8,10 +8,10 @@ inst_reqs = [ "boto3", "rio-tiler==6.5.0", - "titiler.pgstac==0.8.3", - "titiler.core>=0.15.5,<0.16", - "titiler.mosaic>=0.15.5,<0.16", - "titiler.extensions[cogeo]>=0.15.5,<0.16", + "titiler.pgstac==1.3.0", + "titiler.core>=0.18.5,<0.19", + "titiler.mosaic>=0.18.5,<0.19", + "titiler.extensions[cogeo]>=0.18.5,<0.19", "starlette-cramjam>=0.3,<0.4", "aws_xray_sdk>=2.6.0,<3", "aws-lambda-powertools>=1.18.0", diff --git a/raster_api/runtime/src/app.py b/raster_api/runtime/src/app.py index 7a2d03a4..5e0bdc5e 100644 --- a/raster_api/runtime/src/app.py +++ b/raster_api/runtime/src/app.py @@ -6,7 +6,7 @@ from src.algorithms import PostProcessParams from src.alternate_reader import PgSTACReaderAlt from src.config import ApiSettings -from src.dependencies import ColorMapParams, ItemPathParams +from src.dependencies import ColorMapParams from src.extensions import stacViewerExtension from src.monitoring import LoggerRouteHandler, logger, metrics, tracer from src.version import __version__ as veda_raster_version @@ -16,14 +16,25 @@ from starlette.requests import Request from starlette_cramjam.middleware import CompressionMiddleware from titiler.core.errors import DEFAULT_STATUS_CODES, add_exception_handlers -from titiler.core.factory import MultiBaseTilerFactory, TilerFactory, TMSFactory +from titiler.core.factory import ( + ColorMapFactory, + MultiBaseTilerFactory, + TilerFactory, + TMSFactory, +) from titiler.core.middleware import CacheControlMiddleware from titiler.core.resources.enums import OptionalHeader from titiler.core.resources.responses import JSONResponse from titiler.extensions import cogValidateExtension, cogViewerExtension from titiler.mosaic.errors import MOSAIC_STATUS_CODES from titiler.pgstac.db import close_db_connection, connect_to_db -from titiler.pgstac.factory import MosaicTilerFactory +from titiler.pgstac.dependencies import CollectionIdParams, ItemIdParams, SearchIdParams +from titiler.pgstac.extensions import searchInfoExtension +from titiler.pgstac.factory import ( + MosaicTilerFactory, + add_search_list_route, + add_search_register_route, +) from titiler.pgstac.reader import PgSTACReader logging.getLogger("botocore.credentials").disabled = True @@ -64,16 +75,15 @@ async def lifespan(app: FastAPI): add_exception_handlers(app, MOSAIC_STATUS_CODES) ############################################################################### -# /mosaic - PgSTAC Mosaic titiler endpoint +# /searches - STAC Search endpoint ############################################################################### -mosaic = MosaicTilerFactory( - router_prefix="/mosaic", +searches = MosaicTilerFactory( + router_prefix="/searches/{search_id}", + path_dependency=SearchIdParams, optional_headers=optional_headers, environment_dependency=settings.get_gdal_config, process_dependency=PostProcessParams, router=APIRouter(route_class=LoggerRouteHandler), - # add /list (default to False) - add_mosaic_list=settings.enable_mosaic_search, # add /statistics [POST] (default to False) add_statistics=True, # add /map viewer (default to False) @@ -81,19 +91,65 @@ async def lifespan(app: FastAPI): # add /bbox [GET] and /feature [POST] (default to False) add_part=True, colormap_dependency=ColorMapParams, + extensions=[ + searchInfoExtension(), + ], +) +app.include_router( + searches.router, prefix="/searches/{search_id}", tags=["STAC Search"] +) + +# add /register endpoint +add_search_register_route( + app, + prefix="/searches", + # any dependency we want to validate + # when creating the tilejson/map links + tile_dependencies=[ + searches.layer_dependency, + searches.dataset_dependency, + searches.pixel_selection_dependency, + searches.process_dependency, + searches.rescale_dependency, + searches.colormap_dependency, + searches.render_dependency, + searches.pgstac_dependency, + searches.reader_dependency, + searches.backend_dependency, + ], + tags=["STAC Search"], +) +# add /list endpoint +if settings.enable_mosaic_search: + add_search_list_route(app, prefix="/searches", tags=["STAC Search"]) + +############################################################################### +# STAC COLLECTION Endpoints +############################################################################### +collection = MosaicTilerFactory( + path_dependency=CollectionIdParams, + optional_headers=optional_headers, + router_prefix="/collections/{collection_id}", + add_statistics=True, + add_viewer=True, + add_part=True, + extensions=[ + searchInfoExtension(), + ], +) +app.include_router( + collection.router, tags=["STAC Collection"], prefix="/collections/{collection_id}" ) -app.include_router(mosaic.router, prefix="/mosaic", tags=["Mosaic"]) -# TODO -# prefix will be replaced by `/mosaics/{search_id}` in titiler-pgstac 1.0.0 + ############################################################################### -# /stac - Custom STAC titiler endpoint +# /collections/{collection_id}/items/{item_id} - Custom STAC titiler endpoint ############################################################################### stac = MultiBaseTilerFactory( reader=PgSTACReader, - path_dependency=ItemPathParams, + path_dependency=ItemIdParams, optional_headers=optional_headers, - router_prefix="/stac", + router_prefix="/collections/{collection_id}/items/{item_id}", environment_dependency=settings.get_gdal_config, router=APIRouter(route_class=LoggerRouteHandler), extensions=[ @@ -101,16 +157,20 @@ async def lifespan(app: FastAPI): ], colormap_dependency=ColorMapParams, ) -app.include_router(stac.router, tags=["Items"], prefix="/stac") +app.include_router( + stac.router, + tags=["STAC Item"], + prefix="/collections/{collection_id}/items/{item_id}", +) ############################################################################### -# /stac-alt - Custom STAC titiler endpoint for alternate asset locations +# /alt/collections/{collection_id}/items/{item_id} - Custom STAC titiler endpoint for alternate asset locations ############################################################################### stac_alt = MultiBaseTilerFactory( reader=PgSTACReaderAlt, - path_dependency=ItemPathParams, + path_dependency=ItemIdParams, optional_headers=optional_headers, - router_prefix="/stac-alt", + router_prefix="/alt/collections/{collection_id}/items/{item_id}", environment_dependency=settings.get_gdal_config, router=APIRouter(route_class=LoggerRouteHandler), extensions=[ @@ -118,7 +178,12 @@ async def lifespan(app: FastAPI): ], colormap_dependency=ColorMapParams, ) -app.include_router(stac_alt.router, tags=["Items"], prefix="/stac-alt") +app.include_router( + stac_alt.router, + tags=["Alt Href STAC Item"], + prefix="/alt/collections/{collection_id}/items/{item_id}", + include_in_schema=False, +) ############################################################################### # /cog - External Cloud Optimized GeoTIFF endpoints @@ -137,6 +202,12 @@ async def lifespan(app: FastAPI): app.include_router(cog.router, tags=["Cloud Optimized GeoTIFF"], prefix="/cog") +############################################################################### +# Colormaps endpoints +############################################################################### +cmaps = ColorMapFactory() +app.include_router(cmaps.router, tags=["ColorMaps"]) + @app.get("/healthz", description="Health Check", tags=["Health Check"]) def ping(): diff --git a/raster_api/runtime/src/dependencies.py b/raster_api/runtime/src/dependencies.py index c41ef552..cee0a08b 100644 --- a/raster_api/runtime/src/dependencies.py +++ b/raster_api/runtime/src/dependencies.py @@ -1,13 +1,8 @@ """veda.raster.dependencies.""" -import pystac from rio_tiler.colormap import cmap as default_cmap -from typing_extensions import Annotated -from fastapi import Query -from starlette.requests import Request from titiler.core.dependencies import create_colormap_dependency -from titiler.pgstac.dependencies import get_stac_item try: from importlib.resources import files as resources_files # type: ignore @@ -15,22 +10,6 @@ # Try backported to PY<39 `importlib_resources`. from importlib_resources import files as resources_files # type: ignore - -def ItemPathParams( - request: Request, - collection: Annotated[ - str, - Query(description="STAC Collection ID"), - ], - item: Annotated[ - str, - Query(description="STAC Item ID"), - ], -) -> pystac.Item: - """STAC Item dependency.""" - return get_stac_item(request.app.state.dbpool, collection, item) - - VEDA_CMAPS_FILES = { f.stem: str(f) for f in (resources_files(__package__) / "cmap_data").glob("*.npy") # type: ignore } diff --git a/raster_api/runtime/src/extensions.py b/raster_api/runtime/src/extensions.py index 51271d98..971eba51 100644 --- a/raster_api/runtime/src/extensions.py +++ b/raster_api/runtime/src/extensions.py @@ -11,9 +11,10 @@ from titiler.core.factory import BaseTilerFactory, FactoryExtension DEFAULT_TEMPLATES = Jinja2Templates( - directory="", - loader=jinja2.ChoiceLoader([jinja2.PackageLoader(__package__, "templates")]), -) # type:ignore + env=jinja2.Environment( + loader=jinja2.ChoiceLoader([jinja2.PackageLoader(__package__, "templates")]) + ) +) @dataclass @@ -32,9 +33,9 @@ def stac_viewer( ): """STAC Viewer.""" return self.templates.TemplateResponse( + request, name="stac-viewer.html", context={ - "request": request, "endpoint": request.url.path.replace("/viewer", ""), "collection": item.collection_id, "item": item.id, From 18a4d30f2b5fb86defa6cc4f310b3827abe6b591 Mon Sep 17 00:00:00 2001 From: Saadiq Mohiuddin <34844565+smohiudd@users.noreply.github.com> Date: Wed, 31 Jul 2024 17:09:08 -0600 Subject: [PATCH 5/6] fix: render fix for titiler-pgstac v1 upgrade (#408) --- stac_api/runtime/src/links.py | 25 +++++++++++++++++-------- stac_api/runtime/src/render.py | 13 ++++--------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/stac_api/runtime/src/links.py b/stac_api/runtime/src/links.py index 3d0ad823..c763d1e8 100644 --- a/stac_api/runtime/src/links.py +++ b/stac_api/runtime/src/links.py @@ -46,12 +46,16 @@ def inject_item(self, item: Item) -> None: item_id = item.get("id", "") item["links"] = item.get("links", []) if self.tiler_href: - item["links"].append(self._get_item_map_link(item_id)) - item["assets"]["rendered_preview"] = self._get_item_preview_link(item_id) + item["links"].append(self._get_item_map_link(item_id, self.collection_id)) + item["assets"]["rendered_preview"] = self._get_item_preview_link( + item_id, self.collection_id + ) - def _get_item_map_link(self, item_id: str) -> Dict[str, Any]: - qs = self.render_config.get_full_render_qs(self.collection_id, item_id) - href = urljoin(self.tiler_href, f"stac/map?{qs}") + def _get_item_map_link(self, item_id: str, collection_id: str) -> Dict[str, Any]: + qs = self.render_config.get_full_render_qs() + href = urljoin( + self.tiler_href, f"collections/{collection_id}/items/{item_id}/map?{qs}" + ) return { "title": "Map of Item", @@ -60,9 +64,14 @@ def _get_item_map_link(self, item_id: str) -> Dict[str, Any]: "type": "text/html", } - def _get_item_preview_link(self, item_id: str) -> Dict[str, Any]: - qs = self.render_config.get_full_render_qs(self.collection_id, item_id) - href = urljoin(self.tiler_href, f"stac/preview.png?{qs}") + def _get_item_preview_link( + self, item_id: str, collection_id: str + ) -> Dict[str, Any]: + qs = self.render_config.get_full_render_qs() + href = urljoin( + self.tiler_href, + f"collections/{collection_id}/items/{item_id}/preview.png?{qs}", + ) return { "title": "Rendered preview", diff --git a/stac_api/runtime/src/render.py b/stac_api/runtime/src/render.py index d98198bb..23221f01 100644 --- a/stac_api/runtime/src/render.py +++ b/stac_api/runtime/src/render.py @@ -42,21 +42,16 @@ class RenderConfig(BaseModel): mosaic_preview_zoom: Optional[int] = None mosaic_preview_coords: Optional[List[float]] = None - def get_full_render_qs(self, collection: str, item: Optional[str] = None) -> str: + def get_full_render_qs(self) -> str: """ - Return the full render query string, including the - item, collection, render and assets parameters. + Return the full render query string including render and assets parameters. """ - collection_part = f"collection={collection}" if collection else "" - item_part = f"&item={item}" if item else "" - render_part = self.get_render_params() - - return "".join([collection_part, item_part, render_part]) + return self.get_render_params() def get_render_params(self) -> str: """Get the render parameters as a query string.""" params = self.render_params.copy() - return f"&{get_param_str(params)}" + return f"{get_param_str(params)}" class Config: """Pydantic config class for RenderConfig.""" From 450c08291e1e2c84336f01a0479c70fdd93efdc8 Mon Sep 17 00:00:00 2001 From: Ciaran Sweet <9111975+ciaransweet@users.noreply.github.com> Date: Thu, 1 Aug 2024 15:11:51 +0100 Subject: [PATCH 6/6] chore: add gitignore entry for Jetbrains IDEs (#409) --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 560217ac..b75ebfcf 100644 --- a/.gitignore +++ b/.gitignore @@ -143,3 +143,6 @@ cdk-dev.out stac-browser container_logs.log + +# Jetbrains IDEs +.idea