From a8fed976a2431994e8d2c819f604078564c5c118 Mon Sep 17 00:00:00 2001 From: Shubham Padkonde Date: Mon, 21 Sep 2026 20:19:41 +0530 Subject: [PATCH 1/2] Fix Django STAC search query parameter handling --- pygeoapi/api/stac.py | 4 ++-- tests/api/test_stac.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/pygeoapi/api/stac.py b/pygeoapi/api/stac.py index 5b5611d3a8..b9c6139c5c 100644 --- a/pygeoapi/api/stac.py +++ b/pygeoapi/api/stac.py @@ -337,7 +337,7 @@ def search(api: API, request: Union[APIRequest, Any]) -> Tuple[dict, int, str]: if request.data: LOGGER.debug('Intercepting STAC POST request into query args') request_data = json.loads(request.data) - request_params = deepcopy(dict(request.params)) + request_params = deepcopy(dict(request.params.items())) for qp in ['bbox', 'datetime', 'limit', 'offset']: if qp in request_data: @@ -398,7 +398,7 @@ def search(api: API, request: Union[APIRequest, Any]) -> Tuple[dict, int, str]: next_link = False prev_link = False - request_params = deepcopy(dict(request._args)) + request_params = deepcopy(dict(request.params.items())) limit = itemtypes_api.evaluate_limit( request_params.get('limit'), api.config['server'].get('limits', {}), {}) diff --git a/tests/api/test_stac.py b/tests/api/test_stac.py index 1da63919b3..9ac5cb2909 100644 --- a/tests/api/test_stac.py +++ b/tests/api/test_stac.py @@ -28,9 +28,12 @@ # ================================================================= import json +from types import SimpleNamespace +from urllib.parse import parse_qs, urlsplit import pytest +from pygeoapi.api import APIRequest from pygeoapi.api.stac import search, landing_page from pygeoapi.formats import FORMAT_TYPES, F_JSON from pygeoapi.util import yaml_load @@ -95,3 +98,31 @@ def test_search(config, api_, params, matched, returned): for feature in response['features']: assert feature['stac_version'] == '1.0.0' + + +@pytest.mark.parametrize('post_data,limit', [(None, 3), ({'limit': 2}, 2)]) +def test_search_django_query_params(api_, post_data, limit, monkeypatch): + """Django query values stay scalar in POST overrides and paging links.""" + django_http = pytest.importorskip('django.http') + monkeypatch.setattr(django_http.request, 'settings', SimpleNamespace( + DATA_UPLOAD_MAX_NUMBER_FIELDS=1000)) + params = django_http.QueryDict( + 'limit=1&limit=3&offset=2&bbox=-180,-90,180,90', encoding='utf-8') + request = SimpleNamespace( + GET=params, headers={}, path_info='/stac-api/search', + body=json.dumps(post_data).encode() if post_data else b'') + req = APIRequest.from_django(request, api_.locales) + + _, code, response = search(api_, req) + assert code == 200 + response = json.loads(response) + assert response['numberMatched'] == 10 + assert response['numberReturned'] == limit + links = {link['rel']: link['href'] for link in response['links']} + for rel, offset in [('prev', None), ('next', [str(2 + limit)])]: + query = parse_qs(urlsplit(links[rel]).query) + assert query['limit'] == [str(limit)] + assert query['bbox'] == ['-180,-90,180,90'] + assert query.get('offset') == offset + assert params.getlist('limit') == ['1', '3'] + assert params['offset'] == '2' From e9967e50526797facb1c54fb675ff407eab5e4dc Mon Sep 17 00:00:00 2001 From: Shubham Padkonde Date: Tue, 22 Sep 2026 07:06:39 +0530 Subject: [PATCH 2/2] Move Django STAC regression into Django test module --- tests/api/test_stac.py | 31 ---------------------------- tests/other/test_django.py | 41 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 31 deletions(-) diff --git a/tests/api/test_stac.py b/tests/api/test_stac.py index 9ac5cb2909..1da63919b3 100644 --- a/tests/api/test_stac.py +++ b/tests/api/test_stac.py @@ -28,12 +28,9 @@ # ================================================================= import json -from types import SimpleNamespace -from urllib.parse import parse_qs, urlsplit import pytest -from pygeoapi.api import APIRequest from pygeoapi.api.stac import search, landing_page from pygeoapi.formats import FORMAT_TYPES, F_JSON from pygeoapi.util import yaml_load @@ -98,31 +95,3 @@ def test_search(config, api_, params, matched, returned): for feature in response['features']: assert feature['stac_version'] == '1.0.0' - - -@pytest.mark.parametrize('post_data,limit', [(None, 3), ({'limit': 2}, 2)]) -def test_search_django_query_params(api_, post_data, limit, monkeypatch): - """Django query values stay scalar in POST overrides and paging links.""" - django_http = pytest.importorskip('django.http') - monkeypatch.setattr(django_http.request, 'settings', SimpleNamespace( - DATA_UPLOAD_MAX_NUMBER_FIELDS=1000)) - params = django_http.QueryDict( - 'limit=1&limit=3&offset=2&bbox=-180,-90,180,90', encoding='utf-8') - request = SimpleNamespace( - GET=params, headers={}, path_info='/stac-api/search', - body=json.dumps(post_data).encode() if post_data else b'') - req = APIRequest.from_django(request, api_.locales) - - _, code, response = search(api_, req) - assert code == 200 - response = json.loads(response) - assert response['numberMatched'] == 10 - assert response['numberReturned'] == limit - links = {link['rel']: link['href'] for link in response['links']} - for rel, offset in [('prev', None), ('next', [str(2 + limit)])]: - query = parse_qs(urlsplit(links[rel]).query) - assert query['limit'] == [str(limit)] - assert query['bbox'] == ['-180,-90,180,90'] - assert query.get('offset') == offset - assert params.getlist('limit') == ['1', '3'] - assert params['offset'] == '2' diff --git a/tests/other/test_django.py b/tests/other/test_django.py index 85c349e5d8..2f6b7f0fda 100644 --- a/tests/other/test_django.py +++ b/tests/other/test_django.py @@ -31,14 +31,21 @@ from http import HTTPStatus +import json import os import sys from unittest import mock +from types import SimpleNamespace +from urllib.parse import parse_qs, urlsplit import django from django.test import Client import pytest +from pygeoapi.api import API, APIRequest +from pygeoapi.api.stac import search +from pygeoapi.util import yaml_load + from ..util import get_test_file_path @@ -71,3 +78,37 @@ def test_django_edr_without_instance_id(django_): response_json = response.json() assert response_json["type"] == "Coverage" assert response_json["domain"]["domainType"] == "PointSeries" + + +@pytest.fixture +def stac_api(openapi): + with open(get_test_file_path('pygeoapi-test-stac-api-config.yml')) as fh: + return API(yaml_load(fh), openapi) + + +@pytest.mark.parametrize('post_data,limit', [(None, 3), ({'limit': 2}, 2)]) +def test_search_django_query_params(stac_api, post_data, limit, monkeypatch): + """Django query values stay scalar in POST overrides and paging links.""" + django_http = pytest.importorskip('django.http') + monkeypatch.setattr(django_http.request, 'settings', SimpleNamespace( + DATA_UPLOAD_MAX_NUMBER_FIELDS=1000)) + params = django_http.QueryDict( + 'limit=1&limit=3&offset=2&bbox=-180,-90,180,90', encoding='utf-8') + request = SimpleNamespace( + GET=params, headers={}, path_info='/stac-api/search', + body=json.dumps(post_data).encode() if post_data else b'') + req = APIRequest.from_django(request, stac_api.locales) + + _, code, response = search(stac_api, req) + assert code == 200 + response = json.loads(response) + assert response['numberMatched'] == 10 + assert response['numberReturned'] == limit + links = {link['rel']: link['href'] for link in response['links']} + for rel, offset in [('prev', None), ('next', [str(2 + limit)])]: + query = parse_qs(urlsplit(links[rel]).query) + assert query['limit'] == [str(limit)] + assert query['bbox'] == ['-180,-90,180,90'] + assert query.get('offset') == offset + assert params.getlist('limit') == ['1', '3'] + assert params['offset'] == '2'