Skip to content

Commit

Permalink
Merge branch 'master' into ml-evs/add_codeowners
Browse files Browse the repository at this point in the history
  • Loading branch information
ml-evs committed Jan 7, 2021
2 parents 6320c3d + 790ab7d commit 2fa6575
Showing 1 changed file with 32 additions and 39 deletions.
71 changes: 32 additions & 39 deletions optimade/validator/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,17 +261,25 @@ def validate_implementation(self):
self._log.debug("Testing base info endpoint of %s", info_endp)

# Get and validate base info to find endpoints
# If this is not possible, then exit at this stage
base_info = self._test_info_or_links_endpoint(info_endp)
if not base_info:
raise RuntimeError(
"Unable to deserialize base info (see previous errors), so cannot continue."
self._log.critical(
f"Unable to deserialize response from introspective {info_endp!r} endpoint. "
"This is required for all further validation, so the validator will now exit."
)
self.print_summary()
# Set valid to False to ensure error code 1 is raised at CLI
self.valid = False
return

self.provider_prefix = (
base_info.dict()["meta"].get("provider", {}).get("prefix")
)
# Grab the provider prefix from base info and use it when looking for provider fields
self.provider_prefix = None
meta = base_info.get("meta", {})
if meta.get("provider") is not None:
self.provider_prefix = meta["provider"].get("prefix")

# Set the response class for all `/info/entry` endpoints
# Set the response class for all `/info/entry` endpoints based on `/info` response
self.available_json_endpoints, _ = self._get_available_endpoints(base_info)
for endp in self.available_json_endpoints:
self._response_classes[f"{info_endp}/{endp}"] = EntryInfoResponse
Expand All @@ -282,12 +290,14 @@ def validate_implementation(self):
self._test_bad_version_returns_553()

# Test that entry info endpoints deserialize correctly
# If they do not, the corresponding entry in _entry_info_by_type
# is set to False, which must be checked for further validation
for endp in self.available_json_endpoints:
entry_info_endpoint = f"{info_endp}/{endp}"
self._log.debug("Testing expected info endpoint %s", entry_info_endpoint)
entry_info = self._test_info_or_links_endpoint(entry_info_endpoint)
if entry_info:
self._entry_info_by_type[endp] = entry_info.dict()
self._entry_info_by_type[endp] = self._test_info_or_links_endpoint(
entry_info_endpoint
)

# Use the _entry_info_by_type to construct filters on the relevant endpoints
if not self.minimal:
Expand Down Expand Up @@ -334,17 +344,18 @@ def _recurse_through_endpoint(self, endp: str) -> Tuple[bool, str]:
"""
entry_info = self._entry_info_by_type.get(endp)

if not entry_info:
raise ResponseError(
f"Unable to generate filters for endpoint {endp}: 'info/{endp}' response was malformed."
)

_impl_properties = self._check_entry_info(entry_info, endp)
prop_list = list(_impl_properties.keys())

self._check_response_fields(endp, prop_list)
chosen_entry, _ = self._get_archetypal_entry(endp, prop_list)

if not entry_info:
raise ResponseError(
f"Unable to generate filters for endpoint {endp}: entry info not found."
)

if not chosen_entry:
return (
None,
Expand Down Expand Up @@ -388,7 +399,7 @@ def _check_entry_info(self, entry_info: Dict[str, Any], endp: str) -> List[str]:
The list of property names supported by this implementation.
"""
properties = entry_info["data"]["properties"]
properties = entry_info.get("data", {}).get("properties", [])
self._test_must_properties(
properties, endp, request="; checking entry info for required properties"
)
Expand Down Expand Up @@ -636,9 +647,9 @@ def _construct_single_property_filters(
"""
if prop == "id":
test_value = chosen_entry["id"]
test_value = chosen_entry.get("id")
else:
test_value = chosen_entry["attributes"].get(prop, "_missing")
test_value = chosen_entry.get("attributes", {}).get(prop, "_missing")

if test_value in ("_missing", None):
support = CONF.entry_schemas[endp].get(prop, {}).get("support")
Expand Down Expand Up @@ -778,7 +789,7 @@ def _construct_single_property_filters(

return True, f"{prop} passed filter tests"

def _test_info_or_links_endpoint(self, request_str: str) -> Union[bool, Any]:
def _test_info_or_links_endpoint(self, request_str: str) -> Union[bool, dict]:
"""Requests an info or links endpoint and attempts to deserialize
the response.
Expand All @@ -797,9 +808,8 @@ def _test_info_or_links_endpoint(self, request_str: str) -> Union[bool, Any]:
self._response_classes[request_str],
request=request_str,
)
if not deserialized:
return response
return deserialized
if deserialized:
return deserialized.dict()

return False

Expand Down Expand Up @@ -1146,25 +1156,8 @@ def _get_available_endpoints(
"""
for _ in [0]:
available_json_entry_endpoints = []
try:
available_json_entry_endpoints = (
base_info.data.attributes.entry_types_by_format.get("json")
)
break
except Exception:
self._log.warning(
"Info endpoint failed serialization, trying to manually extract entry_types_by_format."
)

if not base_info.json():
raise ResponseError(
"Unable to get entry types from base info endpoint. "
"This may most likely be attributed to a wrong request to the info endpoint."
)

try:
available_json_entry_endpoints = base_info.json()["data"]["attributes"][
available_json_entry_endpoints = base_info["data"]["attributes"][
"entry_types_by_format"
]["json"]
break
Expand Down

0 comments on commit 2fa6575

Please sign in to comment.