From 23be4cb745ae1c0c50a06a4268f210f76823877f Mon Sep 17 00:00:00 2001 From: vincentsarago Date: Fri, 31 Jan 2025 11:33:31 +0100 Subject: [PATCH 1/5] add paging links tests --- stac_fastapi/pgstac/models/links.py | 4 +- tests/api/test_links.py | 94 +++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 tests/api/test_links.py diff --git a/stac_fastapi/pgstac/models/links.py b/stac_fastapi/pgstac/models/links.py index c0ec445..4a97e9b 100644 --- a/stac_fastapi/pgstac/models/links.py +++ b/stac_fastapi/pgstac/models/links.py @@ -143,7 +143,7 @@ def link_next(self) -> Optional[Dict[str, Any]]: "rel": Relations.next, "type": MimeTypes.geojson, "method": method, - "href": f"{self.request.url}", + "href": str(self.request.url), "body": {**self.request.postbody, "token": f"next:{self.next}"}, } @@ -167,7 +167,7 @@ def link_prev(self) -> Optional[Dict[str, Any]]: "rel": Relations.previous, "type": MimeTypes.geojson, "method": method, - "href": f"{self.request.url}", + "href": str(self.request.url), "body": {**self.request.postbody, "token": f"prev:{self.prev}"}, } return None diff --git a/tests/api/test_links.py b/tests/api/test_links.py new file mode 100644 index 0000000..6eb1a6e --- /dev/null +++ b/tests/api/test_links.py @@ -0,0 +1,94 @@ +import pytest +from fastapi import APIRouter, FastAPI +from starlette.requests import Request +from starlette.testclient import TestClient + +from stac_fastapi.pgstac.models import links as app_links + + +@pytest.mark.parametrize("root_path", ["", "/api/v1"]) +@pytest.mark.parametrize("prefix", ["", "/stac"]) +def tests_app_links(prefix, root_path): + endpoint_prefix = root_path + prefix + url_prefix = "http://stac.io" + endpoint_prefix + + app = FastAPI(root_path=root_path) + router = APIRouter(prefix=prefix) + app.state.router_prefix = router.prefix + + @router.get("/search") + @router.post("/search") + async def search(request: Request): + links = app_links.PagingLinks(request, next="yo:2", prev="yo:1") + return { + "url": links.url, + "base_url": links.base_url, + "links": await links.get_links(), + } + + @router.get("/collections") + async def collections(request: Request): + pgstac_next = { + "rel": "next", + "body": {"offset": 1}, + "href": "./collections", + "type": "application/json", + "merge": True, + "method": "GET", + } + pgstac_prev = { + "rel": "prev", + "body": {"offset": 0}, + "href": "./collections", + "type": "application/json", + "merge": True, + "method": "GET", + } + links = app_links.CollectionSearchPagingLinks( + request, next=pgstac_next, prev=pgstac_prev + ) + return { + "url": links.url, + "base_url": links.base_url, + "links": await links.get_links(), + } + + app.include_router(router) + + with TestClient( + app, + base_url="http://stac.io", + root_path=root_path, + ) as client: + response = client.get(f"{endpoint_prefix}/search") + assert response.status_code == 200 + assert response.json()["url"] == url_prefix + "/search" + assert response.json()["base_url"].rstrip("/") == url_prefix + links = response.json()["links"] + for link in links: + if link["rel"] in ["previous", "next"]: + assert link["method"] == "GET" + assert link["href"].startswith(url_prefix) + assert {"next", "previous", "root", "self"} == {link["rel"] for link in links} + + response = client.post(f"{endpoint_prefix}/search", json={}) + assert response.status_code == 200 + assert response.json()["url"] == url_prefix + "/search" + assert response.json()["base_url"].rstrip("/") == url_prefix + links = response.json()["links"] + for link in links: + if link["rel"] in ["previous", "next"]: + assert link["method"] == "POST" + assert link["href"].startswith(url_prefix) + assert {"next", "previous", "root", "self"} == {link["rel"] for link in links} + + response = client.get(f"{endpoint_prefix}/collections") + assert response.status_code == 200 + assert response.json()["url"] == url_prefix + "/collections" + assert response.json()["base_url"].rstrip("/") == url_prefix + links = response.json()["links"] + for link in links: + if link["rel"] in ["previous", "next"]: + assert link["method"] == "GET" + assert link["href"].startswith(url_prefix) + assert {"next", "previous", "root", "self"} == {link["rel"] for link in links} From e413875209fb8d1fe5db7d0f0065deb62cf4f8e4 Mon Sep 17 00:00:00 2001 From: vincentsarago Date: Fri, 7 Mar 2025 17:10:32 +0100 Subject: [PATCH 2/5] fix url links with root-path --- stac_fastapi/pgstac/models/links.py | 6 +++--- tests/api/test_links.py | 6 +++--- tests/conftest.py | 6 ------ 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/stac_fastapi/pgstac/models/links.py b/stac_fastapi/pgstac/models/links.py index 8df9932..f93a1c1 100644 --- a/stac_fastapi/pgstac/models/links.py +++ b/stac_fastapi/pgstac/models/links.py @@ -51,7 +51,7 @@ def base_url(self): @property def url(self): """Get the current request url.""" - return str(self.request.url) + return urljoin(str(self.request.base_url), str(self.request.url.path).lstrip("/")) def resolve(self, url): """Resolve url to the current request url.""" @@ -143,7 +143,7 @@ def link_next(self) -> Optional[Dict[str, Any]]: "rel": Relations.next.value, "type": MimeTypes.geojson.value, "method": method, - "href": str(self.request.url), + "href": self.url, "body": {**self.request.postbody, "token": f"next:{self.next}"}, } @@ -167,7 +167,7 @@ def link_prev(self) -> Optional[Dict[str, Any]]: "rel": Relations.previous.value, "type": MimeTypes.geojson.value, "method": method, - "href": str(self.request.url), + "href": self.url, "body": {**self.request.postbody, "token": f"prev:{self.prev}"}, } return None diff --git a/tests/api/test_links.py b/tests/api/test_links.py index 6eb1a6e..cff07c5 100644 --- a/tests/api/test_links.py +++ b/tests/api/test_links.py @@ -60,7 +60,7 @@ async def collections(request: Request): base_url="http://stac.io", root_path=root_path, ) as client: - response = client.get(f"{endpoint_prefix}/search") + response = client.get(f"{prefix}/search") assert response.status_code == 200 assert response.json()["url"] == url_prefix + "/search" assert response.json()["base_url"].rstrip("/") == url_prefix @@ -71,7 +71,7 @@ async def collections(request: Request): assert link["href"].startswith(url_prefix) assert {"next", "previous", "root", "self"} == {link["rel"] for link in links} - response = client.post(f"{endpoint_prefix}/search", json={}) + response = client.post(f"{prefix}/search", json={}) assert response.status_code == 200 assert response.json()["url"] == url_prefix + "/search" assert response.json()["base_url"].rstrip("/") == url_prefix @@ -82,7 +82,7 @@ async def collections(request: Request): assert link["href"].startswith(url_prefix) assert {"next", "previous", "root", "self"} == {link["rel"] for link in links} - response = client.get(f"{endpoint_prefix}/collections") + response = client.get(f"{prefix}/collections") assert response.status_code == 200 assert response.json()["url"] == url_prefix + "/collections" assert response.json()["base_url"].rstrip("/") == url_prefix diff --git a/tests/conftest.py b/tests/conftest.py index ce45653..ec41169 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,3 @@ -import asyncio import json import logging import os @@ -62,11 +61,6 @@ ) -@pytest.fixture(scope="session") -def event_loop(): - return asyncio.get_event_loop() - - @pytest.fixture(scope="session") def database(postgresql_proc): with DatabaseJanitor( From b1521b804025a0dbb5c1fb4567ecda4d8b3e5800 Mon Sep 17 00:00:00 2001 From: vincentsarago Date: Fri, 7 Mar 2025 18:01:14 +0100 Subject: [PATCH 3/5] fix --- stac_fastapi/pgstac/models/links.py | 6 +++++- tests/api/test_links.py | 14 +++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/stac_fastapi/pgstac/models/links.py b/stac_fastapi/pgstac/models/links.py index f93a1c1..6697488 100644 --- a/stac_fastapi/pgstac/models/links.py +++ b/stac_fastapi/pgstac/models/links.py @@ -51,7 +51,11 @@ def base_url(self): @property def url(self): """Get the current request url.""" - return urljoin(str(self.request.base_url), str(self.request.url.path).lstrip("/")) + url = urljoin(str(self.request.base_url), self.request.url.path.lstrip("/")) + if qs := self.request.url.query: + url += f"?{qs}" + + return url def resolve(self, url): """Resolve url to the current request url.""" diff --git a/tests/api/test_links.py b/tests/api/test_links.py index cff07c5..e8e57a9 100644 --- a/tests/api/test_links.py +++ b/tests/api/test_links.py @@ -8,7 +8,7 @@ @pytest.mark.parametrize("root_path", ["", "/api/v1"]) @pytest.mark.parametrize("prefix", ["", "/stac"]) -def tests_app_links(prefix, root_path): +def tests_app_links(prefix, root_path): # noqa: C901 endpoint_prefix = root_path + prefix url_prefix = "http://stac.io" + endpoint_prefix @@ -71,6 +71,18 @@ async def collections(request: Request): assert link["href"].startswith(url_prefix) assert {"next", "previous", "root", "self"} == {link["rel"] for link in links} + response = client.get(f"{prefix}/search", params={"limit": 1}) + assert response.status_code == 200 + assert response.json()["url"] == url_prefix + "/search?limit=1" + assert response.json()["base_url"].rstrip("/") == url_prefix + links = response.json()["links"] + for link in links: + if link["rel"] in ["previous", "next"]: + assert link["method"] == "GET" + assert "limit=1" in link["href"] + assert link["href"].startswith(url_prefix) + assert {"next", "previous", "root", "self"} == {link["rel"] for link in links} + response = client.post(f"{prefix}/search", json={}) assert response.status_code == 200 assert response.json()["url"] == url_prefix + "/search" From 79c3cc650e6238ebbc08362bae7260364577e7af Mon Sep 17 00:00:00 2001 From: vincentsarago Date: Fri, 7 Mar 2025 18:20:31 +0100 Subject: [PATCH 4/5] update requirements --- setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 702c420..1e5837d 100644 --- a/setup.py +++ b/setup.py @@ -10,9 +10,9 @@ "orjson", "pydantic", "stac_pydantic==3.1.*", - "stac-fastapi.api~=5.0", - "stac-fastapi.extensions~=5.0", - "stac-fastapi.types~=5.0", + "stac-fastapi.api>=5.0,<5.1", + "stac-fastapi.extensions>=5.0,<5.1", + "stac-fastapi.types>=5.0,<5.1", "asyncpg", "buildpg", "brotli_asgi", From f08c89dc7116c53ee715f1cb6ff975a75d3b711d Mon Sep 17 00:00:00 2001 From: vincentsarago Date: Mon, 10 Mar 2025 10:39:28 +0100 Subject: [PATCH 5/5] update changelog --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 1f4f156..d3a0db6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- fix links when app is mounted behind proxy or has router-prefix ([#195](https://github.com/stac-utils/stac-fastapi-pgstac/pull/195)) + ## [4.0.2] - 2025-02-18 ### Fixed