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

Add configurable field-specific validator overrides to set filter operators as optional #1025

Merged
merged 1 commit into from
Dec 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
50 changes: 33 additions & 17 deletions optimade/validator/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,16 @@

"""

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

from optimade.models import InfoResponse, IndexInfoResponse, DataType, StructureFeatures
from optimade.models import (
InfoResponse,
IndexInfoResponse,
DataType,
StructureFeatures,
SupportLevel,
)
from optimade.validator.utils import (
ValidatorLinksResponse,
ValidatorReferenceResponseOne,
Expand Down Expand Up @@ -61,29 +67,19 @@

_UNIQUE_PROPERTIES = ("id", "immutable_id")

inclusive_ops = ("=", "<=", ">=")
substring_operators = ("CONTAINS", "STARTS WITH", "STARTS", "ENDS WITH", "ENDS")

_INCLUSIVE_OPERATORS = {
DataType.STRING: (
"=",
"<=",
">=",
"CONTAINS",
"STARTS WITH",
"STARTS",
"ENDS WITH",
"ENDS",
),
DataType.STRING: inclusive_ops + substring_operators,
DataType.TIMESTAMP: (
# N.B. "=" and "<=" are disabled due to issue with microseconds stored in database vs API response (see Materials-Consortia/optimade-python-tools/#606)
# ">=" is fine as all microsecond trimming will round times down
# "=",
# "<=",
">=",
),
DataType.INTEGER: (
"=",
"<=",
">=",
),
DataType.INTEGER: inclusive_ops,
DataType.FLOAT: (),
DataType.LIST: ("HAS", "HAS ALL", "HAS ANY"),
}
Expand All @@ -99,6 +95,16 @@
}


_FIELD_SPECIFIC_OVERRIDES = {
"chemical_formula_anonymous": {
SupportLevel.OPTIONAL: substring_operators,
},
"chemical_formula_reduced": {
SupportLevel.OPTIONAL: substring_operators,
},
}


class ValidatorConfig(BaseSettings):
"""This class stores validator config parameters in a way that
can be easily modified for testing niche implementations. Many
Expand Down Expand Up @@ -150,6 +156,16 @@ class ValidatorConfig(BaseSettings):
),
)

field_specific_overrides: Dict[str, Dict[SupportLevel, Container[str]]] = Field(
_FIELD_SPECIFIC_OVERRIDES,
description=(
"Some fields do not require all type comparison operators to be supported. "
"This dictionary allows overriding the list of supported operators for a field, using "
"the field name as a key, and the support level of different operators with a subkey. "
"Queries on fields listed in this way will pass the validator provided the server returns a 501 status."
),
)

links_endpoint: str = Field("links", description="The name of the links endpoint")
versions_endpoint: str = Field(
"versions", description="The name of the versions endpoint"
Expand Down
7 changes: 7 additions & 0 deletions optimade/validator/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -778,11 +778,18 @@ def _construct_single_property_filters(

inclusive_operators = CONF.inclusive_operators[prop_type]
exclusive_operators = CONF.exclusive_operators[prop_type]
field_specific_support_overrides = CONF.field_specific_overrides.get(prop, {})

for operator in inclusive_operators | exclusive_operators:
# Need to pre-format list and string test values for the query
_test_value = self._format_test_value(test_value, prop_type, operator)

query_optional = (
query_optional
or operator
in field_specific_support_overrides.get(SupportLevel.OPTIONAL, [])
)

query = f"{prop} {operator} {_test_value}"
request = f"{endp}?filter={query}"
response, message = self._get_endpoint(
Expand Down