Skip to content

Commit

Permalink
Add fallback values for enum fields to use on validation (closes #678)
Browse files Browse the repository at this point in the history
Co-authored-by: Casper Welzel Andersen <casper.andersen@epfl.ch>
  • Loading branch information
ml-evs and CasperWA committed Jan 18, 2021
1 parent 3295774 commit 5ea4b75
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 30 deletions.
15 changes: 13 additions & 2 deletions optimade/validator/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
"""

from typing import Dict, Any, Set
from typing import Dict, Any, Set, List
from pydantic import BaseSettings, Field

from optimade.models import InfoResponse, IndexInfoResponse, DataType
from optimade.models import InfoResponse, IndexInfoResponse, DataType, StructureFeatures
from optimade.validator.utils import (
ValidatorLinksResponse,
ValidatorReferenceResponseOne,
Expand Down Expand Up @@ -50,6 +50,12 @@
"links": ValidatorLinksResponse,
}

_ENUM_DUMMY_VALUES = {
"structures": {
"structure_features": [allowed.value for allowed in StructureFeatures]
}
}


_UNIQUE_PROPERTIES = ("id", "immutable_id")

Expand Down Expand Up @@ -159,5 +165,10 @@ class ValidatorConfig(BaseSettings):
description="Field names to treat as top-level",
)

enum_fallback_values: Dict[str, Dict[str, List[str]]] = Field(
_ENUM_DUMMY_VALUES,
description="Provide fallback values for enum fields to use when validating filters.",
)


VALIDATOR_CONFIG = ValidatorConfig()
53 changes: 25 additions & 28 deletions optimade/validator/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -685,11 +685,18 @@ def _construct_single_property_filters(
# Otherwise, None values are not allowed for MUST's, and entire missing fields are not allowed
raise ResponseError(msg)

using_fallback = False
if prop_type == DataType.LIST:
if not test_value or (
isinstance(test_value[0], dict) or isinstance(test_value[0], list)
):
msg = f"Not testing queries on field {prop} of type {prop_type} with nested dictionary/list entries."
if not test_value:
test_value = CONF.enum_fallback_values.get(endp, {}).get(prop)
using_fallback = True

if not test_value:
msg = f"Not testing filters on field {prop} of type {prop_type} as no test value was found to use in filter."
self._log.warning(msg)
return None, msg
if isinstance(test_value[0], dict) or isinstance(test_value[0], list):
msg = f"Not testing filters on field {prop} of type {prop_type} with nested dictionary/list test value."
self._log.warning(msg)
return None, msg

Expand All @@ -713,22 +720,17 @@ def _construct_single_property_filters(
request,
multistage=True,
optional=query_optional,
expected_status_code=(200, 501),
)

if not response:
if query_optional:
return None, ""
return (
None,
"Optional query {query!r} returned {reversed_response.status_code}.",
)
raise ResponseError(
f"Unable to perform {request}: failed with error {message}."
)

if response.status_code == 501:
self._log.warning(
f"Implementation returned {response.content} for {query}"
)
return (
True,
"Implementation safely reported that filter {query} was not implemented.",
f"Unable to perform mandatory query {query!r}: responded with status code {response.status_code}"
)

response = response.json()
Expand Down Expand Up @@ -776,22 +778,17 @@ def _construct_single_property_filters(
reversed_request,
multistage=True,
optional=query_optional,
expected_status_code=(200, 501),
)

if not reversed_response:
if query_optional:
return None, ""
return (
None,
"Optional query {query!r} returned {reversed_response.status_code}.",
)
raise ResponseError(
f"Unable to perform {reversed_request}: failed with error {message}."
)

if reversed_response.status_code == 501:
self._log.warning(
f"Implementation returned {reversed_response.content} for {reversed_query}"
)
return (
True,
"Implementation safely reported that filter {reversed_query} was not implemented.",
f"Unable to perform mandatory query {query!r}: responded with status code {reversed_response.status_code}"
)

reversed_response = reversed_response.json()
Expand Down Expand Up @@ -829,8 +826,8 @@ def _construct_single_property_filters(
f"Objects {excluded} were not necessarily excluded by {query}"
)

# check that at least the archetypal structure was returned
if not excluded:
# check that at least the archetypal structure was returned, unless we are using a fallback value
if not excluded and not using_fallback:
if (
num_data_returned[operator] is not None
and num_data_returned[operator] < 1
Expand Down

0 comments on commit 5ea4b75

Please sign in to comment.