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: raise errors when metadata discovery is not allowed #1534

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion eodag/api/product/metadata_mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
string_to_jsonpath,
update_nested_dict,
)
from eodag.utils.exceptions import ValidationError

if TYPE_CHECKING:
from shapely.geometry.base import BaseGeometry
Expand Down Expand Up @@ -1283,11 +1284,45 @@ def format_query_params(
config.metadata_mapping,
**config.products.get(product_type, {}).get("metadata_mapping", {}),
)
# Initialize the configuration parameter which indicates if an error must be raised while there is a search
# parameter which is not queryable. It is useful when the provider does not return any result if the search
# parameter is not a queryable without raising an error by itself.
# This parameter is set to False by default and the configuration at product type level has priority over the
# one at provider level
raise_mtd_discovery_error = (
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add some comments for this part of the code!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

getattr(config, "discover_metadata", {})
.get("search_param", {})
.get("raise_mtd_discovery_error", False)
if isinstance(
getattr(config, "discover_metadata", {}).get("search_param", {}), dict
)
else False
)
raise_mtd_discovery_error = (
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic is not correct when raise_mtd_discovery_error is is True for provider and False for product type.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

config.products.get(product_type, {})
.get("discover_metadata", {})
.get("search_param", {})
.get("raise_mtd_discovery_error")
if isinstance(
config.products.get(product_type, {})
.get("discover_metadata", {})
.get("search_param", {}),
dict,
)
and config.products.get(product_type, {})
.get("discover_metadata", {})
.get("search_param", {})
.get("raise_mtd_discovery_error")
is not None
else raise_mtd_discovery_error
)

query_params: dict[str, Any] = {}
# Get all the search parameters that are recognised as queryables by the
# provider (they appear in the queryables dictionary)
queryables = _get_queryables(query_dict, config, product_type_metadata_mapping)
queryables = _get_queryables(
query_dict, config, product_type_metadata_mapping, raise_mtd_discovery_error
)

for eodag_search_key, provider_search_key in queryables.items():
user_input = query_dict[eodag_search_key]
Expand Down Expand Up @@ -1419,13 +1454,20 @@ def _get_queryables(
search_params: dict[str, Any],
config: PluginConfig,
metadata_mapping: dict[str, Any],
raise_mtd_discovery_error: bool,
) -> dict[str, Any]:
"""Retrieve the metadata mappings that are query-able"""
logger.debug("Retrieving queryable metadata from metadata_mapping")
queryables: dict[str, Any] = {}
for eodag_search_key, user_input in search_params.items():
if user_input is not None:
md_mapping = metadata_mapping.get(eodag_search_key, (None, NOT_MAPPED))
# raise an error when a query param not allowed by the provider is found
if not isinstance(md_mapping, list) and raise_mtd_discovery_error:
raise ValidationError(
f"Search parameters which are not queryable are disallowed: {eodag_search_key}",
{eodag_search_key},
)
_, md_value = md_mapping
# query param from defined metadata_mapping
if md_mapping is not None and isinstance(md_mapping, list):
Expand Down
5 changes: 3 additions & 2 deletions eodag/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,9 @@ class DiscoverMetadata(TypedDict):
auto_discovery: bool
#: Metadata regex pattern used for discovery in search result properties
metadata_pattern: str
#: Configuration/template that will be used to query for a discovered parameter
search_param: str
#: Configuration/template that will be used to query for a discovered parameter, or indication if
#: using a search parameter which is not queryable will fail the search request or not
search_param: str | dict[str, Any]
#: Path to the metadata in search result
metadata_path: str

Expand Down
5 changes: 3 additions & 2 deletions eodag/plugins/search/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,8 @@ def build_sort_by(
if eodag_sort_order[:3] != "ASC" and eodag_sort_order[:3] != "DES":
raise ValidationError(
"Sorting order is invalid: it must be set to 'ASC' (ASCENDING) or "
f"'DESC' (DESCENDING), got '{eodag_sort_order}' with '{eodag_sort_param}' instead"
f"'DESC' (DESCENDING), got '{eodag_sort_order}' with '{eodag_sort_param}' instead",
{eodag_sort_param},
)
eodag_sort_order = eodag_sort_order[:3]

Expand All @@ -295,7 +296,7 @@ def build_sort_by(
raise ValidationError(
f"'{eodag_sort_param}' parameter is called several times to sort results with different "
"sorting orders. Please set it to only one ('ASC' (ASCENDING) or 'DESC' (DESCENDING))",
set([eodag_sort_param]),
{eodag_sort_param},
)
provider_sort_by_tuples_used.append(provider_sort_by_tuple)

Expand Down
14 changes: 10 additions & 4 deletions eodag/plugins/search/build_search_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ def discover_queryables(
formated_kwargs[key] = kwargs[key]
else:
raise ValidationError(
f"{key} is not a queryable parameter for {self.provider}"
f"'{key}' is not a queryable parameter for {self.provider}", {key}
)

# we use non empty kwargs as default to integrate user inputs
Expand Down Expand Up @@ -658,7 +658,9 @@ def discover_queryables(
and keyword.replace("ecmwf:", "")
not in set(list(available_values.keys()) + [f["name"] for f in form])
):
raise ValidationError(f"{keyword} is not a queryable parameter")
raise ValidationError(
f"'{keyword}' is not a queryable parameter", {keyword}
)

# generate queryables
if form:
Expand Down Expand Up @@ -745,7 +747,8 @@ def available_values_from_constraints(
# we only compare list of strings.
if isinstance(values, dict):
raise ValidationError(
f"Parameter value as object is not supported: {keyword}={values}"
f"Parameter value as object is not supported: {keyword}={values}",
{keyword},
)
filter_v = values if isinstance(values, (list, tuple)) else [values]

Expand Down Expand Up @@ -810,7 +813,10 @@ def available_values_from_constraints(
raise ValidationError(
f"{keyword}={values} is not available"
f"{all_keywords_str}."
f" Allowed values are {', '.join(allowed_values)}."
f" Allowed values are {', '.join(allowed_values)}.",
set(
[keyword] + [k for k in parsed_keywords if k in input_keywords]
),
)

parsed_keywords.append(keyword)
Expand Down
15 changes: 14 additions & 1 deletion eodag/plugins/search/data_request_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
)
from eodag.plugins.search import PreparedSearch
from eodag.plugins.search.base import Search
from eodag.types.queryables import Queryables
from eodag.utils import (
DEFAULT_ITEMS_PER_PAGE,
DEFAULT_MISSION_START_DATE,
Expand Down Expand Up @@ -359,7 +360,19 @@ def _create_data_request(
ssl_verify = getattr(self.config.ssl_verify, "ssl_verify", True)
try:
url = self.config.data_request_url
request_body = format_query_params(eodag_product_type, self.config, kwargs)
try:
request_body = format_query_params(
eodag_product_type, self.config, kwargs
)
except ValidationError as context:
not_queryable_search_param = Queryables.get_queryable_from_alias(
str(context.message).split(":")[-1].strip()
)
raise ValidationError(
f"Search parameters which are not queryable are disallowed for {product_type} with "
f"{self.provider}: please remove '{not_queryable_search_param}' from your search parameters",
{not_queryable_search_param},
)
logger.debug(
f"Sending search job request to {url} with {str(request_body)}"
)
Expand Down
27 changes: 24 additions & 3 deletions eodag/plugins/search/qssearch.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
from eodag.plugins.search import PreparedSearch
from eodag.plugins.search.base import Search
from eodag.types import json_field_definition_to_python, model_fields_to_annotated
from eodag.types.queryables import Queryables
from eodag.types.search_args import SortByList
from eodag.utils import (
DEFAULT_MISSION_START_DATE,
Expand Down Expand Up @@ -533,7 +534,7 @@ def discover_product_types_per_page(

prep.info_message = "Fetching product types: {}".format(prep.url)
prep.exception_message = (
"Skipping error while fetching product types for " "{} {} instance:"
"Skipping error while fetching product types for {} {} instance:"
).format(self.provider, self.__class__.__name__)

# Query using appropriate method
Expand Down Expand Up @@ -813,7 +814,17 @@ def build_query_string(
) -> tuple[dict[str, Any], str]:
"""Build The query string using the search parameters"""
logger.debug("Building the query string that will be used for search")
query_params = format_query_params(product_type, self.config, kwargs)
try:
query_params = format_query_params(product_type, self.config, kwargs)
except ValidationError as context:
not_queryable_search_param = Queryables.get_queryable_from_alias(
str(context.message).split(":")[-1].strip()
)
raise ValidationError(
f"Search parameters which are not queryable are disallowed for {product_type} with "
f"{self.provider}: please remove '{not_queryable_search_param}' from your search parameters",
{not_queryable_search_param},
)

# Build the final query string, in one go without quoting it
# (some providers do not operate well with urlencoded and quoted query strings)
Expand Down Expand Up @@ -1858,7 +1869,17 @@ def build_query_string(
kwargs.setdefault("startTimeFromAscendingNode", "..")
kwargs.setdefault("completionTimeFromAscendingNode", "..")

query_params = format_query_params(product_type, self.config, kwargs)
try:
query_params = format_query_params(product_type, self.config, kwargs)
except ValidationError as context:
not_queryable_search_param = Queryables.get_queryable_from_alias(
str(context.message).split(":")[-1].strip()
)
raise ValidationError(
f"Search parameters which are not queryable are disallowed for {product_type} with "
f"{self.provider}: please remove '{not_queryable_search_param}' from your search parameters",
{not_queryable_search_param},
)

# Build the final query string, in one go without quoting it
# (some providers do not operate well with urlencoded and quoted query strings)
Expand Down
5 changes: 5 additions & 0 deletions eodag/resources/providers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3733,11 +3733,16 @@
productType: EO:CLMS:DAT:CORINE
providerProductType: Corine Land Cover 2018
format: GeoTiff100mt
discover_metadata:
search_param:
raise_mtd_discovery_error: true
metadata_mapping:
id: '$.id'
providerProductType:
- '{{"product_type": "{providerProductType}"}}'
- '$.null'
startTimeFromAscendingNode: '$.properties.startdate'
completionTimeFromAscendingNode: '$.properties.enddate'
defaultGeometry: 'POLYGON((180 -90, 180 90, -180 90, -180 -90, 180 -90))'
orderLink: 'https://gateway.prod.wekeo2.eu/hda-broker/api/v1/dataaccess/download?{{"location": "{downloadLink}","product_id":"{id}", "dataset_id": "EO:CLMS:DAT:CORINE"}}'
CLMS_GLO_FCOVER_333M:
Expand Down
13 changes: 9 additions & 4 deletions eodag/rest/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
import re
from typing import Union

from fastapi import FastAPI, Request
Expand Down Expand Up @@ -89,10 +90,14 @@ def errors(self) -> list[dict[str, Union[str, int]]]:

if type(exception) is ValidationError:
for error_param in exception.parameters:
stac_param = EODAGSearch.to_stac(error_param)
exception.message = exception.message.replace(
error_param, stac_param
)
error_param_pattern = rf"\b{error_param}\b"
if re.search(error_param_pattern, exception.message):
stac_param = EODAGSearch.to_stac(error_param)
exception.message = re.sub(
error_param_pattern,
stac_param,
exception.message,
)
error_dict["message"] = exception.message

error_list.append(error_dict)
Expand Down
Loading