Repository: CS-SI/eodag
Pull request #819 (2023-09-11 07:10:14)

perf(creodias): use zipper URL to avoid unecessary redirect
datahub URL redirects to zipper url.
Use CREODIAS zipper URL instead of datahub for downloadLink to avoid the redirect.

github-actions[bot]: ## Test Results
       4 files  ±0         4 suites  ±0   6m 43s [:stopwatch:](https://github.com/EnricoMi/publish-unit-test-result-action/blob/v2.9.0/README.md#the-symbols "duration of all tests") + 1m 20s
   413 tests ±0     411 [:heavy_check_mark:](https://github.com/EnricoMi/publish-unit-test-result-action/blob/v2.9.0/README.md#the-symbols "passed tests") ±0    2 [:zzz:](https://github.com/EnricoMi/publish-unit-test-result-action/blob/v2.9.0/README.md#the-symbols "skipped / disabled tests") ±0  0 [:x:](https://github.com/EnricoMi/publish-unit-test-result-action/blob/v2.9.0/README.md#the-symbols "failed tests") ±0 
1 652 runs  ±0  1 584 [:heavy_check_mark:](https://github.com/EnricoMi/publish-unit-test-result-action/blob/v2.9.0/README.md#the-symbols "passed tests") ±0  68 [:zzz:](https://github.com/EnricoMi/publish-unit-test-result-action/blob/v2.9.0/README.md#the-symbols "skipped / disabled tests") ±0  0 [:x:](https://github.com/EnricoMi/publish-unit-test-result-action/blob/v2.9.0/README.md#the-symbols "failed tests") ±0 

Results for commit f97fc8b1. ± Comparison against base commit e100b96b.

[test-results]:data:application/gzip;base64,H4sIAK2+/mQC/03MSw6DIBSF4a0Yxh0A8tBupoELNyFVaRBGTfdetEod/t9JzptgmPxK7p24dWQtIbdwJZkc4rIl7SvUKe8ja/VYC8BO7E/P8KrEG6AJUwXawKcU0yGpLNsnU5IfdX4yOYhGv081nHD53Pt6CXGeQ65BcNQIg2XSewRrlNQjjoIqx5lVFgCdsqK3mny+WGr2yAgBAAA=

github-actions[bot]: <strong>Code Coverage (Ubuntu)</strong>

| File | Coverage |   |
| - | :-: | :-: |
| **All files** | `82%` | :white_check_mark: |

_Minimum allowed coverage is `70%`_

<p align="right">Generated by :monkey: cobertura-action against f97fc8b15eefcba6579f9406d21b6bccfd6b43b7 </p>
github-actions[bot]: <strong>Code Coverage (Windows)</strong>

| File | Coverage |   |
| - | :-: | :-: |
| **All files** | `75%` | :white_check_mark: |

_Minimum allowed coverage is `70%`_

<p align="right">Generated by :monkey: cobertura-action against f97fc8b15eefcba6579f9406d21b6bccfd6b43b7 </p>

Patch:
diff --git a/eodag/api/product/_product.py b/eodag/api/product/_product.py
index f331c604..06958f04 100644
--- a/eodag/api/product/_product.py
+++ b/eodag/api/product/_product.py
@@ -23,7 +23,8 @@ import urllib.parse
 
 import requests
 from requests import RequestException
-from shapely import geometry, geos, wkb, wkt
+from shapely import geometry, wkb, wkt
+from shapely.errors import ShapelyError
 
 from eodag.api.product.drivers import DRIVERS, NoDriver
 from eodag.api.product.metadata_mapping import NOT_AVAILABLE, NOT_MAPPED
@@ -96,7 +97,10 @@ class EOProduct(object):
             and NOT_AVAILABLE not in str(value)
         }
         if "geometry" not in properties or (
-            properties["geometry"] == NOT_AVAILABLE
+            (
+                properties["geometry"] == NOT_AVAILABLE
+                or properties["geometry"] == NOT_MAPPED
+            )
             and "defaultGeometry" not in properties
         ):
             raise MisconfiguredError(
@@ -127,12 +131,12 @@ class EOProduct(object):
         if isinstance(product_geometry, str):
             try:
                 product_geometry = wkt.loads(product_geometry)
-            except geos.WKTReadingError:
+            except (ShapelyError, GEOSException):
                 try:
                     product_geometry = wkb.loads(product_geometry)
                 # Also catching TypeError because product_geometry can be a
                 # string and not a bytes string
-                except (geos.WKBReadingError, TypeError):
+                except (ShapelyError, GEOSException, TypeError):
                     # Giv up!
                     raise
         self.geometry = self.search_intersection = geometry.shape(product_geometry)
@@ -143,7 +147,7 @@ class EOProduct(object):
             )
             try:
                 self.search_intersection = self.geometry.intersection(searched_geom)
-            except GEOSException:
+            except (GEOSException, ShapelyError):
                 logger.warning(
                     "Unable to intersect the requested extent: %s with the product "
                     "geometry: %s",
diff --git a/eodag/resources/providers.yml b/eodag/resources/providers.yml
index a0ea8b5c..42da4c13 100644
--- a/eodag/resources/providers.yml
+++ b/eodag/resources/providers.yml
@@ -1015,7 +1015,7 @@
         - '{$.Footprint.`sub(/.*(SRID=[0-9]+;.*)/, \\1)`#from_ewkt}'
       # The url to download the product "as is" (literal or as a template to be completed either after the search result
       # is obtained from the provider or during the eodag download phase)
-      downloadLink: 'https://datahub.creodias.eu/odata/v1/Products({uid})/$value'
+      downloadLink: 'https://zipper.creodias.eu/odata/v1/Products({uid})/$value'
       # storageStatus: must be one of ONLINE, STAGING, OFFLINE
       storageStatus: '{$.Online#get_group_name((?P<ONLINE>True)|(?P<OFFLINE>False))}'
       collection:
diff --git a/eodag/rest/server.py b/eodag/rest/server.py
index 7364fe8b..d461692b 100755
--- a/eodag/rest/server.py
+++ b/eodag/rest/server.py
@@ -308,6 +308,7 @@ class SearchBody(BaseModel):
     limit: Union[int, None] = 20
     page: Union[int, None] = 1
     query: Union[dict, None] = None
+    ids: Union[List[str], None] = None
 
 
 @router.get("/search", tags=["STAC"])
diff --git a/setup.cfg b/setup.cfg
index 0e3250a6..772eb309 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -53,7 +53,7 @@ install_requires =
     boto3
     fastapi >= 0.93.0
     uvicorn
-    jsonpath-ng
+    jsonpath-ng < 1.6.0
     lxml
     whoosh
     pystac >= 1.0.0b1


Test patch:
diff --git a/tests/units/test_http_server.py b/tests/units/test_http_server.py
index ca986da8..de3db507 100644
--- a/tests/units/test_http_server.py
+++ b/tests/units/test_http_server.py
@@ -252,6 +252,7 @@ class RequestTestCase(unittest.TestCase):
         expected_search_kwargs=None,
         protocol="GET",
         post_data=None,
+        search_call_count=None,
     ):
         if protocol == "GET":
             response = self.app.get(url, follow_redirects=True)
@@ -262,7 +263,22 @@ class RequestTestCase(unittest.TestCase):
                 follow_redirects=True,
             )
 
-        if expected_search_kwargs is not None:
+        if search_call_count is not None:
+            self.assertEqual(mock_search.call_count, search_call_count)
+
+        if (
+            expected_search_kwargs is not None
+            and search_call_count is not None
+            and search_call_count > 1
+        ):
+            self.assertIsInstance(
+                expected_search_kwargs,
+                list,
+                "expected_search_kwargs must be a list if search_call_count > 1",
+            )
+            for single_search_kwargs in expected_search_kwargs:
+                mock_search.assert_any_call(**single_search_kwargs)
+        elif expected_search_kwargs is not None:
             mock_search.assert_called_once_with(**expected_search_kwargs)
 
         self.assertEqual(200, response.status_code, response.text)
@@ -275,12 +291,14 @@ class RequestTestCase(unittest.TestCase):
         expected_search_kwargs=None,
         protocol="GET",
         post_data=None,
+        search_call_count=None,
     ):
         response = self._request_valid_raw(
             url,
             expected_search_kwargs=expected_search_kwargs,
             protocol=protocol,
             post_data=post_data,
+            search_call_count=search_call_count,
         )
 
         # Assert response format is GeoJSON
@@ -553,6 +571,19 @@ class RequestTestCase(unittest.TestCase):
                 "collections": [self.tested_product_type],
                 "ids": ["foo", "bar"],
             },
+            search_call_count=2,
+            expected_search_kwargs=[
+                {
+                    "provider": None,
+                    "id": "foo",
+                    "productType": self.tested_product_type,
+                },
+                {
+                    "provider": None,
+                    "id": "bar",
+                    "productType": self.tested_product_type,
+                },
+            ],
         )
 
     def test_search_response_contains_pagination_info(self):