From e13df8a103c8cbc68746f0439a781e9da40ef8ff Mon Sep 17 00:00:00 2001 From: Sanjog Thapa Date: Thu, 1 May 2025 14:29:37 -0500 Subject: [PATCH 1/3] feat: query params to filter collections based on the collectionId substring --- tipg/dependencies.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tipg/dependencies.py b/tipg/dependencies.py index a82feeb..969bfa8 100644 --- a/tipg/dependencies.py +++ b/tipg/dependencies.py @@ -450,6 +450,7 @@ def CollectionsParams( description="Starts the response at an offset.", ), ] = None, + collectionId_substring: Annotated[Optional[str], Query(description="Filter based on collectionId substring.")] = None ) -> CollectionList: """Return Collections Catalog.""" limit = limit or 0 @@ -487,6 +488,27 @@ def CollectionsParams( and t_intersects(datetime_filter, collection.dt_bounds) ] + # collectionId filter + if collectionId_substring is not None: + # --- Compile and Validate Regex --- + compiled_regex = None + try: + # Attempt to compile the regex pattern provided by the user + compiled_regex = re.compile(collectionId_substring) + except re.error as e: + # If compilation fails, raise an HTTP error (e.g., 400 Bad Request) + raise HTTPException( + status_code=400, # 400 for client syntax error + detail=f"Invalid regex pattern provided for 'collectionId_substring': {e}" + ) + # --- End of Regex Validation --- + collections_list = [ + collection + for collection in collections_list + # Use search() to find the pattern anywhere in the collection ID + if compiled_regex.search(collection.id) + ] + matched = len(collections_list) if limit: From 60880a2b0556efe134ee53af0b386299118eb6db Mon Sep 17 00:00:00 2001 From: Sanjog Thapa Date: Thu, 1 May 2025 14:38:47 -0500 Subject: [PATCH 2/3] chore: refactor collectionId_substring_query via depedency injection --- tipg/dependencies.py | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/tipg/dependencies.py b/tipg/dependencies.py index 969bfa8..1926606 100644 --- a/tipg/dependencies.py +++ b/tipg/dependencies.py @@ -233,6 +233,21 @@ def datetime_query( return None +def collectionId_substring_query( + collectionId_substring: Annotated[Optional[str], Query(description="Filter based on collectionId substring.")] = None +) -> Optional[str]: + """collectionId substring dependency.""" + compiled_substring = None + if collectionId_substring: + try: + # Attempt to compile the substring pattern provided by the user + compiled_substring = re.compile(collectionId_substring) + except re.error as e: + raise HTTPException( + status_code=422, + detail=f"Invalid substring '{collectionId_substring}' provided for 'collectionId_substring': {e}" + ) + return compiled_substring def properties_query( properties: Annotated[ @@ -450,7 +465,7 @@ def CollectionsParams( description="Starts the response at an offset.", ), ] = None, - collectionId_substring: Annotated[Optional[str], Query(description="Filter based on collectionId substring.")] = None + collectionId_substring: Annotated[Optional[str], Depends(collectionId_substring_query)] = None ) -> CollectionList: """Return Collections Catalog.""" limit = limit or 0 @@ -488,25 +503,13 @@ def CollectionsParams( and t_intersects(datetime_filter, collection.dt_bounds) ] - # collectionId filter + # collectionId substring filter if collectionId_substring is not None: - # --- Compile and Validate Regex --- - compiled_regex = None - try: - # Attempt to compile the regex pattern provided by the user - compiled_regex = re.compile(collectionId_substring) - except re.error as e: - # If compilation fails, raise an HTTP error (e.g., 400 Bad Request) - raise HTTPException( - status_code=400, # 400 for client syntax error - detail=f"Invalid regex pattern provided for 'collectionId_substring': {e}" - ) - # --- End of Regex Validation --- collections_list = [ collection for collection in collections_list - # Use search() to find the pattern anywhere in the collection ID - if compiled_regex.search(collection.id) + # Use search() to find the substring anywhere in the collection ID + if collectionId_substring.search(collection.id) ] matched = len(collections_list) From a599cb31a86f002403eff35e54801f9fec8c35c0 Mon Sep 17 00:00:00 2001 From: Sanjog Thapa Date: Thu, 1 May 2025 15:42:35 -0500 Subject: [PATCH 3/3] chore: add test for the substring based collectionId filter --- tests/routes/test_collections.py | 17 +++++++++++++++++ tipg/dependencies.py | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/tests/routes/test_collections.py b/tests/routes/test_collections.py index 4ce3cf3..65fdd06 100644 --- a/tests/routes/test_collections.py +++ b/tests/routes/test_collections.py @@ -372,3 +372,20 @@ def test_collections_temporal_extent_datetime_column(app): assert len(intervals) == 4 assert intervals[0][0] == "2004-10-19T10:23:54+00:00" assert intervals[0][1] == "2007-10-24T00:00:00+00:00" + +def test_collections_collectionId_substring_filter(app): + """Test /collections endpoint.""" + response = app.get("/collections", params={"collectionId_substring": "_mgrs"}) + assert response.status_code == 200 + assert response.headers["content-type"] == "application/json" + body = response.json() + + ids = [x["id"] for x in body["collections"]] + + assert "public.sentinel_mgrs" in ids + assert "pg_temp.landsat_centroids" not in ids + assert "pg_temp.hexagons" not in ids + assert "pg_temp.squares" not in ids + assert "public.st_squaregrid" not in ids + assert "public.st_hexagongrid" not in ids + assert "public.st_subdivide" not in ids diff --git a/tipg/dependencies.py b/tipg/dependencies.py index 1926606..58b543e 100644 --- a/tipg/dependencies.py +++ b/tipg/dependencies.py @@ -234,7 +234,7 @@ def datetime_query( return None def collectionId_substring_query( - collectionId_substring: Annotated[Optional[str], Query(description="Filter based on collectionId substring.")] = None + collectionId_substring: Annotated[Optional[str], Query(description="Filter based on collectionId substring regex.")] = None ) -> Optional[str]: """collectionId substring dependency.""" compiled_substring = None