From 3b1e9f72581da8b484601c4f750cecef25688146 Mon Sep 17 00:00:00 2001 From: Matt McFarland Date: Fri, 14 Jun 2024 13:58:12 -0400 Subject: [PATCH] Set root-path from uvicorn (#221) * Set root-path from uvicorn Sets the root path in uvicorn to match that set via FastAPI. This is required with updates to starlette, which were included previously. Fixes https://github.com/microsoft/PlanetaryComputer/issues/360 * New black formatting --- docker-compose.yml | 2 +- pccommon/pccommon/cli.py | 4 ++-- pcfuncs/funclib/raster.py | 12 ++++-------- pcfuncs/funclib/tiles.py | 3 +-- pcstac/Dockerfile | 6 +++++- pcstac/Dockerfile.dev | 3 +-- pcstac/pcstac/main.py | 1 + pcstac/requirements-server.txt | 10 ++++------ pcstac/setup.py | 2 +- pcstac/tests/conftest.py | 4 ++-- pcstac/tests/resources/test_item.py | 4 +++- requirements-dev.txt | 6 +++--- scripts/generate-requirements | 2 +- 13 files changed, 29 insertions(+), 30 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 31cef50f..98f3b1c4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,7 +16,7 @@ services: - azurite - redis command: > - bash -c "pypgstac pgready && uvicorn pcstac.main:app --host 0.0.0.0 --port 8081 --reload --proxy-headers" + bash -c "pypgstac pgready && uvicorn pcstac.main:app --host 0.0.0.0 --port 8081 --reload --proxy-headers --root-path '/stac'" tiler: image: pc-apis-tiler diff --git a/pccommon/pccommon/cli.py b/pccommon/pccommon/cli.py index 10991c13..8ee834c4 100644 --- a/pccommon/pccommon/cli.py +++ b/pccommon/pccommon/cli.py @@ -71,7 +71,7 @@ def dump(sas: str, account: str, table: str, type: str, **kwargs: Any) -> int: assert col_config result[id] = col_config.dict() else: - for (_, collection_id, col_config) in col_config_table.get_all(): + for _, collection_id, col_config in col_config_table.get_all(): assert collection_id assert col_config result[collection_id] = col_config.dict() @@ -87,7 +87,7 @@ def dump(sas: str, account: str, table: str, type: str, **kwargs: Any) -> int: assert con_config result[f"{con_account}/{id}"] = con_config.dict() else: - for (storage_account, container, con_config) in con_config_table.get_all(): + for storage_account, container, con_config in con_config_table.get_all(): assert con_config result[f"{storage_account}/{container}"] = con_config.dict() else: diff --git a/pcfuncs/funclib/raster.py b/pcfuncs/funclib/raster.py index bfdc9607..fa2ea5b2 100644 --- a/pcfuncs/funclib/raster.py +++ b/pcfuncs/funclib/raster.py @@ -107,20 +107,16 @@ def __init__(self, extent: RasterExtent) -> None: self.extent = extent @abstractmethod - def to_bytes(self, format: str = ExportFormats.PNG) -> io.BytesIO: - ... + def to_bytes(self, format: str = ExportFormats.PNG) -> io.BytesIO: ... @abstractmethod - def crop(self: T, bbox: Bbox) -> T: - ... + def crop(self: T, bbox: Bbox) -> T: ... @abstractmethod - def resample(self: T, cols: int, rows: int) -> T: - ... + def resample(self: T, cols: int, rows: int) -> T: ... @abstractmethod - def mask(self: T, geom: Dict[str, Any]) -> T: - ... + def mask(self: T, geom: Dict[str, Any]) -> T: ... class PILRaster(Raster): diff --git a/pcfuncs/funclib/tiles.py b/pcfuncs/funclib/tiles.py index e695b239..914f2324 100644 --- a/pcfuncs/funclib/tiles.py +++ b/pcfuncs/funclib/tiles.py @@ -81,8 +81,7 @@ def get_tile_url(self, z: int, x: int, y: int) -> str: return url @abstractmethod - async def get_mosaic(self, tiles: List[Tile]) -> T: - ... + async def get_mosaic(self, tiles: List[Tile]) -> T: ... @staticmethod def get_covering_tiles( diff --git a/pcstac/Dockerfile b/pcstac/Dockerfile index aa800380..f2d183cb 100644 --- a/pcstac/Dockerfile +++ b/pcstac/Dockerfile @@ -22,4 +22,8 @@ RUN --mount=type=cache,target=/root/.cache \ ENV APP_HOST=0.0.0.0 ENV APP_PORT=81 -CMD uvicorn pcstac.main:app --host ${APP_HOST} --port ${APP_PORT} --log-level info +# This value should match that which is used as the root_path in FastAPI, which +# is typically set via the APP_ROOT_PATH environment variable. +ENV APP_ROOT_PATH="" + +CMD uvicorn pcstac.main:app --host ${APP_HOST} --port ${APP_PORT} --root-path ${APP_ROOT_PATH} --log-level info diff --git a/pcstac/Dockerfile.dev b/pcstac/Dockerfile.dev index 380d98eb..52cd5b99 100644 --- a/pcstac/Dockerfile.dev +++ b/pcstac/Dockerfile.dev @@ -1,9 +1,8 @@ FROM pc-apis-stac - RUN --mount=type=cache,target=/root/.cache \ --mount=type=bind,source=requirements-dev.txt,target=requirements-dev.txt \ pip install -r requirements-dev.txt RUN --mount=type=cache,target=/root/.cache \ - pip install -e ./pccommon[dev] -e ./pcstac + pip install --no-deps -e ./pccommon[dev] -e ./pcstac diff --git a/pcstac/pcstac/main.py b/pcstac/pcstac/main.py index 55ba7950..ad6fc55d 100644 --- a/pcstac/pcstac/main.py +++ b/pcstac/pcstac/main.py @@ -1,4 +1,5 @@ """FastAPI application using PGStac.""" + import logging import os from typing import Any, Dict diff --git a/pcstac/requirements-server.txt b/pcstac/requirements-server.txt index e4113bfa..3be02356 100644 --- a/pcstac/requirements-server.txt +++ b/pcstac/requirements-server.txt @@ -7,9 +7,7 @@ anyio==3.7.1 # via # starlette - # watchgod -asgiref==3.8.1 - # via uvicorn + # watchfiles async-timeout==4.0.3 # via asyncpg asyncpg==0.29.0 @@ -147,22 +145,22 @@ termcolor==2.4.0 # via fire typing-extensions==4.10.0 # via - # asgiref # fastapi # psycopg # psycopg-pool # pydantic # pygeoif # starlette + # uvicorn tzlocal==5.2 # via dateparser -uvicorn[standard]==0.17.6 +uvicorn[standard]==0.30.1 # via pcstac (pcstac/setup.py) uvloop==0.19.0 # via uvicorn version-parser==1.0.1 # via pypgstac -watchgod==0.8.2 +watchfiles==0.22.0 # via uvicorn websockets==12.0 # via uvicorn diff --git a/pcstac/setup.py b/pcstac/setup.py index dcadd19c..11cd128b 100644 --- a/pcstac/setup.py +++ b/pcstac/setup.py @@ -17,7 +17,7 @@ extra_reqs = { "server": [ - "uvicorn[standard]>=0.17.0,<0.18.0", + "uvicorn[standard]==0.30.1", ], } diff --git a/pcstac/tests/conftest.py b/pcstac/tests/conftest.py index 8826e961..b76f64a1 100644 --- a/pcstac/tests/conftest.py +++ b/pcstac/tests/conftest.py @@ -75,7 +75,7 @@ def api_client(pqe_pg): post_request_model=search_post_request_model, ), extensions=EXTENSIONS, - app=FastAPI(default_response_class=ORJSONResponse), + app=FastAPI(root_path="/stac", default_response_class=ORJSONResponse), search_get_request_model=search_get_request_model, search_post_request_model=search_post_request_model, ) @@ -102,7 +102,7 @@ async def app(api_client) -> AsyncGenerator[FastAPI, None]: @pytest.fixture(scope="session") async def app_client(app) -> AsyncGenerator[AsyncClient, None]: async with AsyncClient( - app=app, base_url="http://test", headers={"X-Forwarded-For": "127.0.0.1"} + app=app, base_url="http://test/stac", headers={"X-Forwarded-For": "127.0.0.1"} ) as c: yield c diff --git a/pcstac/tests/resources/test_item.py b/pcstac/tests/resources/test_item.py index 7b5022a1..d0e84df5 100644 --- a/pcstac/tests/resources/test_item.py +++ b/pcstac/tests/resources/test_item.py @@ -390,17 +390,19 @@ async def test_pagination_item_collection(app_client): idx = 0 item_ids = [] nextThing = None + while True: idx += 1 page_data = page.json() item_ids.append(page_data["features"][0]["id"]) - print(idx, item_ids) nextlink = [ link["href"] for link in page_data["links"] if link["rel"] == "next" ] if len(nextlink) < 1: break nextThing = nextlink.pop() + assert nextThing.startswith("http://test/stac/collections") + page = await app_client.get(nextThing) if idx >= 20: assert False diff --git a/requirements-dev.txt b/requirements-dev.txt index b1ea644f..fd0c6386 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,9 +1,8 @@ -black==22.3.0 +black==24.4.2 flake8==3.8.4 isort==5.9.2 mypy==1.8.0 openapi-spec-validator==0.3.0 -cachetools<=4.2. pytest==7.* pytest-asyncio==0.18.* httpx>=0.22.0 @@ -11,5 +10,6 @@ json-schema<4.18.0 # https://github.com/stac-utils/pystac/issues/1186 pip-tools # Mypy types -types-python-dateutil types-cachetools +types-python-dateutil +types-redis diff --git a/scripts/generate-requirements b/scripts/generate-requirements index dd5aa9cd..26293130 100755 --- a/scripts/generate-requirements +++ b/scripts/generate-requirements @@ -31,4 +31,4 @@ docker-compose \ -f docker-compose.dev.yml \ run --rm \ tiler-dev \ - pip-compile ./pccommon/setup.py --extra server -o pccommon/requirements.txt $pip_compile_options \ No newline at end of file + pip-compile ./pccommon/setup.py --extra server -o pccommon/requirements.txt $pip_compile_options