Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: search preferred provider and wekeo job error handling #790

Merged
merged 23 commits into from Aug 25, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ed86d28
feat: fallback mechanism for search
jlahovnik Jun 28, 2023
f55747c
test: adapt tests to new search behaviour
jlahovnik Jun 29, 2023
eed499c
fix: enable search with id
jlahovnik Jun 30, 2023
fe474fb
test: add test for search with fallback provider
jlahovnik Jun 30, 2023
5df68ea
docs: adapt documentation for search methods
jlahovnik Jun 30, 2023
7a86fd3
feat: use fallback providers for search iter page
jlahovnik Jun 30, 2023
5d04943
test: fix tests failing after changes for search
jlahovnik Jun 30, 2023
46d4992
docs: add docs for fallback providers
jlahovnik Jul 3, 2023
23b1bd3
fix: raise error when creation of data request job fails
jlahovnik Aug 1, 2023
9d57230
Merge branch '213-fallback-mechanism-to-search-for-products-with-othe…
jlahovnik Aug 22, 2023
3eace9d
fix: do not change preferred provider in search and download
jlahovnik Aug 22, 2023
9756f25
test: adapt test after preferred provider change
jlahovnik Aug 22, 2023
2cd595e
fix: raise error when creation of data request job fails
jlahovnik Aug 1, 2023
8b41d40
fix: enable search with id
jlahovnik Jun 30, 2023
30528b3
fix: do not change preferred provider in search and download
jlahovnik Aug 22, 2023
1a56906
test: adapt test after preferred provider change
jlahovnik Aug 22, 2023
46d363f
refactor: removed unused variable
sbrunato Aug 24, 2023
8438edc
Merge branch 'wekeo_fixes' of https://github.com/CS-SI/eodag into wek…
jlahovnik Aug 24, 2023
ba8a373
test: small refactoring + test for search with provider
jlahovnik Aug 24, 2023
a129a4e
refactor: raise RequestErrors
jlahovnik Aug 25, 2023
87f7d1a
test: adapt test
jlahovnik Aug 25, 2023
479cd0b
test: change search result test so that it makes more sense
jlahovnik Aug 25, 2023
1a81749
refactor: use existing test data file
jlahovnik Aug 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
45 changes: 35 additions & 10 deletions eodag/api/core.py
Expand Up @@ -832,6 +832,7 @@ def search(
end=None,
geom=None,
locations=None,
provider=None,
**kwargs,
):
"""Look for products matching criteria on known providers.
Expand Down Expand Up @@ -877,6 +878,9 @@ def search(
:type locations: dict
:param kwargs: Some other criteria that will be used to do the search,
using paramaters compatibles with the provider
:param provider: (optional) the provider to be used, if not set, the configured
default provider will be used
:type provider: str
:type kwargs: Union[int, str, bool, dict]
:returns: A collection of EO products matching the criteria and the total
number of results found
Expand All @@ -888,7 +892,12 @@ def search(
enforced here.
"""
search_kwargs = self._prepare_search(
start=start, end=end, geom=geom, locations=locations, **kwargs
start=start,
end=end,
geom=geom,
locations=locations,
provider=provider,
**kwargs,
)
search_plugins = search_kwargs.pop("search_plugins", [])
if search_kwargs.get("id"):
Expand All @@ -899,7 +908,9 @@ def search(
)
# remove auth from search_kwargs as a loop over providers will be performed
search_kwargs.pop("auth", None)
return self._search_by_id(search_kwargs.pop("id"), **search_kwargs)
return self._search_by_id(
search_kwargs.pop("id"), provider=provider, **search_kwargs
)
search_kwargs.update(
page=page,
items_per_page=items_per_page,
Expand Down Expand Up @@ -1254,12 +1265,16 @@ def _search_by_id(self, uid, provider=None, **kwargs):
of EO products retrieved (0 or 1)
:rtype: tuple(:class:`~eodag.api.search_result.SearchResult`, int)
"""
if not provider:
provider = self.get_preferred_provider()[0]
get_search_plugins_kwargs = dict(
provider=provider, product_type=kwargs.get("productType", None)
)
for plugin in self._plugins_manager.get_search_plugins(
search_plugins = self._plugins_manager.get_search_plugins(
**get_search_plugins_kwargs
):
)

for plugin in search_plugins:
logger.info(
"Searching product with id '%s' on provider: %s", uid, plugin.provider
)
Expand Down Expand Up @@ -1294,7 +1309,7 @@ def _search_by_id(self, uid, provider=None, **kwargs):
return SearchResult([]), 0

def _prepare_search(
self, start=None, end=None, geom=None, locations=None, **kwargs
self, start=None, end=None, geom=None, locations=None, provider=None, **kwargs
):
"""Internal method to prepare the search kwargs and get the search
and auth plugins.
Expand Down Expand Up @@ -1323,6 +1338,9 @@ def _prepare_search(
:type geom: Union[str, dict, shapely.geometry.base.BaseGeometry]
:param locations: (optional) Location filtering by name using locations configuration
:type locations: dict
:param provider: provider to be used, if no provider is given or the product type
is not available for the provider, the preferred provider is used
:type provider: str
:param kwargs: Some other criteria
* id and/or a provider for a search by
* search criteria to guess the product type
Expand Down Expand Up @@ -1400,15 +1418,23 @@ def _prepare_search(
):
search_plugins.append(plugin)

if search_plugins[0].provider != self.get_preferred_provider()[0]:
if not provider:
provider = self.get_preferred_provider()[0]
providers = [plugin.provider for plugin in search_plugins]
if provider not in providers:
logger.warning(
"Product type '%s' is not available with provider '%s'. "
"Searching it on provider '%s' instead.",
product_type,
self.get_preferred_provider()[0],
provider,
search_plugins[0].provider,
)
else:
provider_plugin = list(
filter(lambda p: p.provider == provider, search_plugins)
)[0]
search_plugins.remove(provider_plugin)
search_plugins.insert(0, provider_plugin)
logger.info(
"Searching product type '%s' on provider: %s",
product_type,
Expand Down Expand Up @@ -1440,9 +1466,6 @@ def _prepare_search(
# Remove the ID since this is equal to productType.
search_plugin.config.product_type_config.pop("ID", None)

logger.debug(
"Using plugin class for search: %s", search_plugin.__class__.__name__
)
auth_plugin = self._plugins_manager.get_auth_plugin(search_plugin.provider)
auth_plugins[search_plugin.provider] = auth_plugin

Expand Down Expand Up @@ -1493,6 +1516,7 @@ def _do_search(self, search_plugin, count=True, raise_errors=False, **kwargs):

results = SearchResult([])
total_results = 0

try:
if "auth" in kwargs:
if isinstance(kwargs["auth"], dict):
Expand Down Expand Up @@ -1586,6 +1610,7 @@ def _do_search(self, search_plugin, count=True, raise_errors=False, **kwargs):
pass
else:
eo_product.product_type = guesses[0]

if eo_product.search_intersection is not None:
download_plugin = self._plugins_manager.get_download_plugin(
eo_product
Expand Down
1 change: 1 addition & 0 deletions eodag/plugins/search/data_request_search.py
Expand Up @@ -113,6 +113,7 @@ def _create_data_request(self, product_type, eodag_product_type, **kwargs):
str(e),
request_job.text,
)
raise
sbrunato marked this conversation as resolved.
Show resolved Hide resolved
sbrunato marked this conversation as resolved.
Show resolved Hide resolved
else:
logger.info("search job for product_type %s created", product_type)
return request_job.json()["jobId"]
Expand Down
5 changes: 1 addition & 4 deletions eodag/rest/server.py
Expand Up @@ -409,12 +409,9 @@ def stac_collections_item_download(collection_id, item_id, request: Request):
body = {}
arguments = dict(request.query_params, **body)
provider = arguments.pop("provider", None)
zipped = "True"
if "zip" in arguments:
zipped = arguments["zip"]

return download_stac_item_by_id_stream(
catalogs=[collection_id], item_id=item_id, provider=provider, zip=zipped
catalogs=[collection_id], item_id=item_id, provider=provider
)


Expand Down
3 changes: 0 additions & 3 deletions eodag/rest/stac.py
Expand Up @@ -83,9 +83,6 @@ def __init__(

self.data = {}

if self.provider and self.eodag_api.get_preferred_provider() != self.provider:
self.eodag_api.set_preferred_provider(self.provider)

def update_data(self, data):
"""Updates data using given input STAC dict data

Expand Down
37 changes: 23 additions & 14 deletions eodag/rest/utils.py
Expand Up @@ -393,6 +393,10 @@ def search_products(product_type, arguments, stac_formatted=True):

try:
arg_product_type = arguments.pop("product_type", None)
provider = arguments.pop("provider", None)
if not provider:
provider = eodag_api.get_preferred_provider()[0]

unserialized = arguments.pop("unserialized", None)

page, items_per_page = get_pagination_info(arguments)
Expand All @@ -407,6 +411,7 @@ def search_products(product_type, arguments, stac_formatted=True):
"start": dtstart,
"end": dtend,
"geom": geom,
"provider": provider,
}

if stac_formatted:
Expand Down Expand Up @@ -455,20 +460,24 @@ def search_products(product_type, arguments, stac_formatted=True):
return response


def search_product_by_id(uid, product_type=None):
def search_product_by_id(uid, product_type=None, provider=None):
"""Search a product by its id

:param uid: The uid of the EO product
:type uid: str
:param product_type: (optional) The product type
:type product_type: str
:param provider: (optional) The provider to be used
:type provider: str
:returns: A search result
:rtype: :class:`~eodag.api.search_result.SearchResult`
:raises: :class:`~eodag.utils.exceptions.ValidationError`
:raises: RuntimeError
"""
try:
products, total = eodag_api.search(id=uid, productType=product_type)
products, total = eodag_api.search(
id=uid, productType=product_type, provider=provider
)
return products
except ValidationError:
raise
Expand Down Expand Up @@ -573,7 +582,7 @@ def get_stac_item_by_id(url, item_id, catalogs, root="/", provider=None):
return None


def download_stac_item_by_id_stream(catalogs, item_id, provider=None, zip="True"):
def download_stac_item_by_id_stream(catalogs, item_id, provider=None):
"""Download item

:param catalogs: Catalogs list (only first is used as product_type)
Expand All @@ -587,17 +596,16 @@ def download_stac_item_by_id_stream(catalogs, item_id, provider=None, zip="True"
:returns: a stream of the downloaded data (either as a zip or the individual assets)
:rtype: StreamingResponse
"""
if provider:
eodag_api.set_preferred_provider(provider)

product = search_product_by_id(item_id, product_type=catalogs[0])[0]

product = search_product_by_id(
item_id, product_type=catalogs[0], provider=provider
)[0]
if product.downloader is None:
download_plugin = eodag_api._plugins_manager.get_download_plugin(product)
auth_plugin = eodag_api._plugins_manager.get_auth_plugin(
download_plugin.provider
)
product.register_downloader(download_plugin, auth_plugin)

auth = (
product.downloader_auth.authenticate()
if product.downloader_auth is not None
Expand Down Expand Up @@ -664,7 +672,7 @@ def get_stac_catalogs(url, root="/", catalogs=[], provider=None, fetch_providers
:param fetch_providers: (optional) Whether to fetch providers for new product
types or not
:type fetch_providers: bool
:returns: Catalog dictionnary
:returns: Catalog dictionary
:rtype: dict
"""
return StacCatalog(
Expand Down Expand Up @@ -734,10 +742,10 @@ def search_stac_items(url, arguments, root="/", catalogs=[], provider=None):
ids = [ids]
if ids:
search_results = SearchResult([])
if provider:
eodag_api.set_preferred_provider(provider)
for item_id in ids:
found_products = search_product_by_id(item_id, product_type=collections[0])
found_products = search_product_by_id(
item_id, product_type=collections[0], provider=provider
)
if len(found_products) == 1:
search_results.extend(found_products)
search_results.properties = {
Expand Down Expand Up @@ -769,7 +777,9 @@ def search_stac_items(url, arguments, root="/", catalogs=[], provider=None):
arguments.pop("datetime")

search_products_arguments = dict(
arguments, **result_catalog.search_args, **{"unserialized": "true"}
arguments,
**result_catalog.search_args,
**{"unserialized": "true", "provider": provider},
)

# check if time filtering appears twice
Expand Down Expand Up @@ -825,7 +835,6 @@ def search_stac_items(url, arguments, root="/", catalogs=[], provider=None):
**{"url": result_catalog.url, "root": result_catalog.root},
),
)

search_results = search_products(
product_type=result_catalog.search_args["product_type"],
arguments=search_products_arguments,
Expand Down
2 changes: 1 addition & 1 deletion tests/units/test_core.py
Expand Up @@ -1292,7 +1292,7 @@ def test__prepare_search_with_id(self):
"""_prepare_search must handle a search by id"""
base = {"id": "dummy-id", "provider": "creodias"}
prepared_search = self.dag._prepare_search(**base)
expected = base
expected = {"id": "dummy-id"}
self.assertDictEqual(expected, prepared_search)

def test__prepare_search_preserve_additional_kwargs(self):
Expand Down
13 changes: 13 additions & 0 deletions tests/units/test_http_server.py
Expand Up @@ -317,6 +317,7 @@ def test_request_params(self):
self._request_valid(
f"search?collections={self.tested_product_type}",
expected_search_kwargs=dict(
provider="peps",
productType=self.tested_product_type,
page=1,
items_per_page=DEFAULT_ITEMS_PER_PAGE,
Expand All @@ -326,6 +327,7 @@ def test_request_params(self):
self._request_valid(
f"search?collections={self.tested_product_type}&bbox=0,43,1,44",
expected_search_kwargs=dict(
provider="peps",
productType=self.tested_product_type,
page=1,
items_per_page=DEFAULT_ITEMS_PER_PAGE,
Expand Down Expand Up @@ -360,6 +362,7 @@ def test_filter(self):
result1 = self._request_valid(
f"search?collections={self.tested_product_type}&bbox=89.65,2.65,89.7,2.7",
expected_search_kwargs=dict(
provider="peps",
productType=self.tested_product_type,
page=1,
items_per_page=DEFAULT_ITEMS_PER_PAGE,
Expand All @@ -371,6 +374,7 @@ def test_filter(self):
result2 = self._request_valid(
f"search?collections={self.tested_product_type}&bbox=89.65,2.65,89.7,2.7&filter=latestIntersect",
expected_search_kwargs=dict(
provider="peps",
productType=self.tested_product_type,
page=1,
items_per_page=DEFAULT_ITEMS_PER_PAGE,
Expand All @@ -386,6 +390,7 @@ def test_date_search(self):
self._request_valid(
f"search?collections={self.tested_product_type}&bbox=0,43,1,44&datetime=2018-01-20/2018-01-25",
expected_search_kwargs=dict(
provider="peps",
productType=self.tested_product_type,
page=1,
items_per_page=DEFAULT_ITEMS_PER_PAGE,
Expand All @@ -401,6 +406,7 @@ def test_date_search_from_items(self):
self._request_valid(
f"collections/{self.tested_product_type}/items?bbox=0,43,1,44",
expected_search_kwargs=dict(
provider="peps",
productType=self.tested_product_type,
page=1,
items_per_page=DEFAULT_ITEMS_PER_PAGE,
Expand All @@ -411,6 +417,7 @@ def test_date_search_from_items(self):
self._request_valid(
f"collections/{self.tested_product_type}/items?bbox=0,43,1,44&datetime=2018-01-20/2018-01-25",
expected_search_kwargs=dict(
provider="peps",
productType=self.tested_product_type,
page=1,
items_per_page=DEFAULT_ITEMS_PER_PAGE,
Expand All @@ -426,6 +433,7 @@ def test_date_search_from_catalog_items(self):
results = self._request_valid(
f"catalogs/{self.tested_product_type}/year/2018/month/01/items?bbox=0,43,1,44",
expected_search_kwargs=dict(
provider="peps",
productType=self.tested_product_type,
page=1,
items_per_page=DEFAULT_ITEMS_PER_PAGE,
Expand All @@ -441,6 +449,7 @@ def test_date_search_from_catalog_items(self):
f"catalogs/{self.tested_product_type}/year/2018/month/01/items"
"?bbox=0,43,1,44&datetime=2018-01-20/2018-01-25",
expected_search_kwargs=dict(
provider="peps",
productType=self.tested_product_type,
page=1,
items_per_page=DEFAULT_ITEMS_PER_PAGE,
Expand All @@ -456,6 +465,7 @@ def test_date_search_from_catalog_items(self):
f"catalogs/{self.tested_product_type}/year/2018/month/01/items"
"?bbox=0,43,1,44&datetime=2018-01-20/2019-01-01",
expected_search_kwargs=dict(
provider="peps",
productType=self.tested_product_type,
page=1,
items_per_page=DEFAULT_ITEMS_PER_PAGE,
Expand Down Expand Up @@ -489,6 +499,7 @@ def test_search_item_id_from_catalog(self):
f"catalogs/{self.tested_product_type}/items/foo",
expected_search_kwargs={
"id": "foo",
"provider": None,
"productType": self.tested_product_type,
},
)
Expand All @@ -499,6 +510,7 @@ def test_search_item_id_from_collection(self):
f"collections/{self.tested_product_type}/items/foo",
expected_search_kwargs={
"id": "foo",
"provider": None,
"productType": self.tested_product_type,
},
)
Expand All @@ -521,6 +533,7 @@ def test_cloud_cover_post_search(self):
"query": {"eo:cloud_cover": {"lte": 10}},
},
expected_search_kwargs=dict(
provider="peps",
productType=self.tested_product_type,
page=1,
items_per_page=DEFAULT_ITEMS_PER_PAGE,
Expand Down