diff --git a/README.md b/README.md
index c36402a..66a363d 100644
--- a/README.md
+++ b/README.md
@@ -99,7 +99,7 @@ index names:
{
"status": "running",
"deployment_environment": "CI",
- "version": "1.1.0",
+ "version": "1.2.0",
"babel_version": "2026jul22",
"babel_version_url": "https://github.com/NCATSTranslator/Babel/blob/main/releases/2026jul22/README.md",
"backend": "elasticsearch",
@@ -186,3 +186,18 @@ success and **1 second** on failure, with concurrent requests sharing one check
the next status request to use a new health check. A failed check returns HTTP 503;
an expired healthy result is never served after a failed check. Health checks do not
trigger Babel discovery. All status responses send `Cache-Control: no-store`.
+
+## OpenAPI document
+
+API version 1.2.0 corrects conflation and semantic-type responses and rejects invalid boolean
+flags. It retains the multi-Babel selectors, response headers and status health checks added in 1.1.0.
+
+The API is described by `src/nodenorm/webapp/openapi.json`, which is written by hand and served
+verbatim at `/webapp/openapi.json` (with `/openapi.json` redirecting there); the Swagger UI at `/`
+renders it. `/status` reports its `info.version` as the API version, so bump that when the API
+changes. `tests/test_openapi.py` checks that the document validates and that its paths, methods,
+query parameters, POST body fields and the `SetIDResponse` schema match the handlers, so a handler
+change that isn't reflected in the JSON fails the test suite. The parameter and body checks find
+what each HTTP method reads by inspecting literal argument names in Tornado `get_argument(s)` and
+`get_query_argument(s)` calls, and literal body fields read with `.get("…")`. They follow handler
+helper methods and resolve local parameter references. Dynamic names still need explicit coverage.
diff --git a/docker/configuration/config.json b/docker/configuration/config.json
index f5323e5..b20d4b8 100644
--- a/docker/configuration/config.json
+++ b/docker/configuration/config.json
@@ -28,6 +28,6 @@
"OPENTELEMETRY_SERVICE_NAME": "NodeNorm",
"OPENTELEMETRY_JAEGER_HOST": "http://jaeger-otel-collector.sri",
"OPENTELEMETRY_JAEGER_PORT": 4318,
- "OPENTELEMETRY_EXCLUDED_URLS": ["^/$", "^/status$", "^/version$", "^/webapp", "^/favicon\\.ico$"]
+ "OPENTELEMETRY_EXCLUDED_URLS": ["^/$", "^/status$", "^/version$", "^/webapp", "^/openapi\\.json$", "^/favicon\\.ico$"]
}
}
diff --git a/pyproject.toml b/pyproject.toml
index 5b38ada..b562d24 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -54,6 +54,8 @@ tests = [
"pytest >= 8.1.1",
"pytest-asyncio >= 0.23.8",
"pytest-randomly",
+ "openapi-spec-validator",
+ "jsonschema",
]
telemetry = [
diff --git a/src/nodenorm/config/config.default.json b/src/nodenorm/config/config.default.json
index acf91e9..72f662b 100644
--- a/src/nodenorm/config/config.default.json
+++ b/src/nodenorm/config/config.default.json
@@ -29,6 +29,6 @@
"OPENTELEMETRY_SERVICE_NAME": "NodeNorm",
"OPENTELEMETRY_JAEGER_HOST": "http://localhost",
"OPENTELEMETRY_JAEGER_PORT": 4318,
- "OPENTELEMETRY_EXCLUDED_URLS": ["^/$", "^/status$", "^/version$", "^/webapp", "^/favicon\\.ico$"]
+ "OPENTELEMETRY_EXCLUDED_URLS": ["^/$", "^/status$", "^/version$", "^/webapp", "^/openapi\\.json$", "^/favicon\\.ico$"]
}
}
diff --git a/src/nodenorm/handlers/__init__.py b/src/nodenorm/handlers/__init__.py
index 77965b6..22a5849 100644
--- a/src/nodenorm/handlers/__init__.py
+++ b/src/nodenorm/handlers/__init__.py
@@ -15,12 +15,14 @@ def build_handlers() -> dict[str, tuple[str, Callable]]:
from nodenorm.handlers.set_identifiers import SetIdentifierHandler
from nodenorm.handlers.version import VersionHandler
+ # Patterns are regexes matched against the path alone (query strings never reach them), so
+ # don't end one with "?": that makes its last character optional. Escape any literal dot.
handler_collection = [
- (r"/get_allowed_conflations?", ValidConflationsHandler),
- (r"/get_normalized_nodes?", NormalizedNodesHandler),
- (r"/get_semantic_types?", SemanticTypeHandler),
- (r"/get_setid?", SetIdentifierHandler),
- (r"/status?", NodeNormHealthHandler),
+ (r"/get_allowed_conflations", ValidConflationsHandler),
+ (r"/get_normalized_nodes", NormalizedNodesHandler),
+ (r"/get_semantic_types", SemanticTypeHandler),
+ (r"/get_setid", SetIdentifierHandler),
+ (r"/status", NodeNormHealthHandler),
(r"/version", VersionHandler),
]
# build static file frontend
@@ -42,8 +44,12 @@ def build_handlers() -> dict[str, tuple[str, Callable]]:
handler_collection.append(index_handler)
# This redirect ensures we default so the favicon icon can be found in the webapp directory
- favicon_handler = (r"/favicon.ico", tornado.web.RedirectHandler, {"url": "/webapp/swaggerui/favicon-32x32.png"})
+ favicon_handler = (r"/favicon\.ico", tornado.web.RedirectHandler, {"url": "/webapp/swaggerui/favicon-32x32.png"})
handler_collection.append(favicon_handler)
+ # NodeNorm Redis (FastAPI) serves its OpenAPI document at the root; keep that address working
+ openapi_handler = (r"/openapi\.json", tornado.web.RedirectHandler, {"url": "/webapp/openapi.json"})
+ handler_collection.append(openapi_handler)
+
handlers = {handler[0]: handler for handler in handler_collection}
return handlers
diff --git a/src/nodenorm/handlers/base.py b/src/nodenorm/handlers/base.py
index 6aab91b..c070773 100644
--- a/src/nodenorm/handlers/base.py
+++ b/src/nodenorm/handlers/base.py
@@ -1,5 +1,7 @@
"""Shared handler behavior for NodeNormalization API endpoints."""
+import json
+
from biothings.web.handlers import BaseHandler
from tornado.web import HTTPError
@@ -41,3 +43,13 @@ def select_babel_release(self, version: str = None) -> BabelRelease:
def options(self, *args, **kwargs):
self.finish()
+
+ def load_json_body(self):
+ """Parse the request body as JSON, answering 400 rather than 500 if it can't be.
+
+ json.loads() raises UnicodeDecodeError, not JSONDecodeError, for bytes that aren't valid UTF-8.
+ """
+ try:
+ return json.loads(self.request.body)
+ except (json.JSONDecodeError, UnicodeDecodeError) as error:
+ raise HTTPError(detail=f"The body is not valid JSON: {error}", status_code=400)
diff --git a/src/nodenorm/handlers/conflations.py b/src/nodenorm/handlers/conflations.py
index 62cb57e..cebc8ba 100644
--- a/src/nodenorm/handlers/conflations.py
+++ b/src/nodenorm/handlers/conflations.py
@@ -1,17 +1,14 @@
-import logging
-
from nodenorm.handlers.base import NodeNormalizationBaseHandler
-logger = logging.getLogger(__name__)
+ALLOWED_CONFLATIONS = ["GeneProtein", "DrugChemical"]
class ValidConflationsHandler(NodeNormalizationBaseHandler):
name = "allowed-conflations"
async def get(self):
- conflations = ["GeneProtein", "DrugChemical"]
- self.finish(conflations)
+ # Wrapped in an object, as NodeNorm Redis does. Tornado also refuses to write a bare list.
+ self.finish({"conflations": ALLOWED_CONFLATIONS})
async def head(self):
- conflations = ["GeneProtein", "DrugChemical"]
- self.finish(conflations)
+ await self.get()
diff --git a/src/nodenorm/handlers/curie_prefix.py b/src/nodenorm/handlers/curie_prefix.py
deleted file mode 100644
index e4a4dc6..0000000
--- a/src/nodenorm/handlers/curie_prefix.py
+++ /dev/null
@@ -1,63 +0,0 @@
-from biothings.web.handlers import BaseHandler
-from tornado.web import HTTPError
-
-from nodenorm.biolink import toolkit
-
-
-class SemanticTypeHandler(BaseHandler):
- """
- Mirror implementation to the renci implementation found at
- https://nodenormalization-sri.renci.org/docs
-
- We intend to mirror the /get_curie_prefixes endpoint
- """
-
- name = "curie_prefixes"
-
- async def get(self) -> dict:
- type_aggregation = {"unique_types": {"terms": {"field": "type", "size": 100}}}
- source_fields = ["type"]
- try:
- search_indices = self.biothings.elasticsearch.indices
- type_aggregation_result = await self.biothings.elasticsearch.async_client.search(
- aggregations=type_aggregation, index=search_indices, size=0, source_includes=source_fields
- )
- except Exception as gen_exc:
- network_error = HTTPError(
- detail="Unable to access the elasticsearch index for type information", status_code=500
- )
- raise network_error from gen_exc
-
- semantic_types = set()
- for bucket in type_aggregation_result.body["aggregations"]["unique_types"]["buckets"]:
- biolink_type = bucket["key"]
- semantic_types.add(biolink_type)
- for ancestor in toolkit.get_ancestors(biolink_type):
- semantic_types.add(toolkit.get_element(ancestor)["class_uri"].lower())
-
- semantic_type_response = {"semantic_types": {"types": list(semantic_types)}}
- self.finish(semantic_type_response)
-
- async def post(self) -> dict:
- type_aggregation = {"unique_types": {"terms": {"field": "type", "size": 100}}}
- source_fields = ["type"]
- try:
- search_indices = self.biothings.elasticsearch.indices
- type_aggregation_result = await self.biothings.elasticsearch.async_client.search(
- aggregations=type_aggregation, index=search_indices, size=0, source_includes=source_fields
- )
- except Exception as gen_exc:
- network_error = HTTPError(
- detail="Unable to access the elasticsearch index for type information", status_code=500
- )
- raise network_error from gen_exc
-
- semantic_types = set()
- for bucket in type_aggregation_result.body["aggregations"]["unique_types"]["buckets"]:
- biolink_type = bucket["key"]
- semantic_types.add(biolink_type)
- for ancestor in toolkit.get_ancestors(biolink_type):
- semantic_types.add(toolkit.get_element(ancestor)["class_uri"].lower())
-
- semantic_type_response = {"semantic_types": {"types": list(semantic_types)}}
- self.finish(semantic_type_response)
diff --git a/src/nodenorm/handlers/normalized_nodes.py b/src/nodenorm/handlers/normalized_nodes.py
index e7f1452..87d95bd 100644
--- a/src/nodenorm/handlers/normalized_nodes.py
+++ b/src/nodenorm/handlers/normalized_nodes.py
@@ -1,5 +1,4 @@
import dataclasses
-import json
import logging
import time
from typing import Optional, Union
@@ -25,6 +24,25 @@ class NormalizedNode:
taxa: list[str]
+# The strings FastAPI (Pydantic) accepted as booleans in NodeNorm Redis, which this API mirrors.
+TRUE_STRINGS = {"true", "1", "yes", "on", "t", "y"}
+FALSE_STRINGS = {"false", "0", "no", "off", "f", "n"}
+
+
+def parse_boolean(name: str, argument: Union[str, bool]) -> bool:
+ """Read a boolean flag, rejecting values that aren't clearly true or false with HTTP 400."""
+ if isinstance(argument, bool):
+ return argument
+ if isinstance(argument, str):
+ if argument.lower() in TRUE_STRINGS:
+ return True
+ if argument.lower() in FALSE_STRINGS:
+ return False
+ raise HTTPError(
+ detail=f"{name} must be true or false (or 1/0, yes/no, on/off, t/f, y/n), not {argument!r}", status_code=400
+ )
+
+
class NormalizedNodesHandler(NodeNormalizationBaseHandler):
"""
Mirror implementation to the renci implementation found at
@@ -42,17 +60,12 @@ async def get(self):
detail="Missing curie argument, there must be at least one curie to normalize", status_code=400
)
- def parse_boolean(argument: Union[str, bool]) -> bool:
- if isinstance(argument, bool):
- return argument
- if isinstance(argument, str):
- return not argument.lower() == "false"
- return False
-
- conflate = parse_boolean(self.get_argument("conflate", True))
- drug_chemical_conflate = parse_boolean(self.get_argument("drug_chemical_conflate", False))
- description = parse_boolean(self.get_argument("description", False))
- individual_types = parse_boolean(self.get_argument("individual_types", False))
+ conflate = parse_boolean("conflate", self.get_argument("conflate", True))
+ drug_chemical_conflate = parse_boolean(
+ "drug_chemical_conflate", self.get_argument("drug_chemical_conflate", False)
+ )
+ description = parse_boolean("description", self.get_argument("description", False))
+ individual_types = parse_boolean("individual_types", self.get_argument("individual_types", False))
release = self.select_babel_release(self.get_query_argument("babel_version", None))
normalized_nodes = await get_normalized_nodes(
@@ -78,7 +91,7 @@ async def post(self):
Example body
{
- "curie": [
+ "curies": [
"MESH:D014867",
"NCIT:C34373"
]
@@ -134,17 +147,21 @@ async def post(self):
}
}
"""
- post_body: dict = json.loads(self.request.body)
+ post_body: dict = self.load_json_body()
+ if not isinstance(post_body, dict):
+ raise HTTPError(
+ detail='The body must be a JSON object, such as {"curies": ["MESH:D014867"]}', status_code=400
+ )
normalization_curies = post_body.get("curies", [])
if len(normalization_curies) == 0:
raise HTTPError(
detail="Missing curie argument, there must be at least one curie to normalize", status_code=400
)
- conflate = post_body.get("conflate", True)
- drug_chemical_conflate = post_body.get("drug_chemical_conflate", False)
- description = post_body.get("description", False)
- individual_types = post_body.get("individual_types", False)
+ conflate = parse_boolean("conflate", post_body.get("conflate", True))
+ drug_chemical_conflate = parse_boolean("drug_chemical_conflate", post_body.get("drug_chemical_conflate", False))
+ description = parse_boolean("description", post_body.get("description", False))
+ individual_types = parse_boolean("individual_types", post_body.get("individual_types", False))
babel_version = post_body.get("babel_version")
if "babel_version" in post_body and not isinstance(babel_version, str):
supported = ", ".join(self.biothings.elasticsearch.babel.available_versions)
diff --git a/src/nodenorm/handlers/semantic_types.py b/src/nodenorm/handlers/semantic_types.py
index 262401f..79df56c 100644
--- a/src/nodenorm/handlers/semantic_types.py
+++ b/src/nodenorm/handlers/semantic_types.py
@@ -33,7 +33,7 @@ async def get(self) -> dict:
biolink_type = bucket["key"]
semantic_types.add(biolink_type)
for ancestor in toolkit.get_ancestors(biolink_type):
- semantic_types.add(toolkit.get_element(ancestor)["class_uri"].lower())
+ semantic_types.add(toolkit.get_element(ancestor)["class_uri"])
semantic_type_response = {"semantic_types": {"types": list(semantic_types)}}
self.finish(semantic_type_response)
diff --git a/src/nodenorm/handlers/set_identifiers.py b/src/nodenorm/handlers/set_identifiers.py
index 7e33da8..52aacf2 100644
--- a/src/nodenorm/handlers/set_identifiers.py
+++ b/src/nodenorm/handlers/set_identifiers.py
@@ -1,7 +1,6 @@
# set_id.py
# Code related to generating IDs for sets (as in https://github.com/TranslatorSRI/NodeNormalization/issues/256).
import dataclasses
-import json
import logging
import uuid
from typing import Optional
@@ -57,7 +56,12 @@ async def get(self):
self.finish(set_identifiers)
async def post(self):
- post_body: list[dict] = json.loads(self.request.body)
+ post_body: list[dict] = self.load_json_body()
+ if not isinstance(post_body, list) or not all(isinstance(group, dict) for group in post_body):
+ raise HTTPError(
+ detail='The body must be a JSON array of sets, such as [{"curies": ["MESH:D014867"]}]',
+ status_code=400,
+ )
if len(post_body) == 0:
raise HTTPError(
detail="Missing JSON body, there must be at least one curie to generate a set identifier",
@@ -95,7 +99,7 @@ async def generate_setid(
:param curies: A list of curies to generate a set ID for.
:param conflations: A list of conflations to apply. Must be one or both of 'GeneProtein' and 'DrugChemical'.
:param search_indices: Request-selected Elasticsearch targets; omitted for the deployment default.
- :return: A SetIDResponse with the Set ID.
+ :return: A SetIDResponse with the Set ID, as a dict: Tornado can only write dicts, not dataclasses.
"""
# Step 0. Prepare the SetIDResponse by filling it with the arguments.
@@ -109,7 +113,7 @@ async def generate_setid(
"Conflations provided to "
+ f"generate_setid() are {conflations}, but only 'GeneProtein' and 'DrugChemical' are allowed."
)
- return response
+ return dataclasses.asdict(response)
# We use get_normalized_nodes() to normalize all the CURIEs for us.
normalization_results = await get_normalized_nodes(
@@ -156,7 +160,7 @@ async def generate_setid(
# Do we have any normalized CURIEs? If not, return now.
if not sorted_normalized_curies:
- return response
+ return dataclasses.asdict(response)
normalized_string = "||".join(sorted_normalized_curies)
response.normalized_string = normalized_string
diff --git a/src/nodenorm/webapp/openapi.json b/src/nodenorm/webapp/openapi.json
index 205499e..37f4c6d 100644
--- a/src/nodenorm/webapp/openapi.json
+++ b/src/nodenorm/webapp/openapi.json
@@ -2,29 +2,59 @@
"openapi": "3.1.0",
"info": {
"title": "Node Normalization",
- "version": "1.1.0",
+ "version": "1.2.0",
"x-translator": {
"component": "Utility",
"team": [
- "Standards Reference Implementation Team"
+ "DOGSURF"
],
- "infores": "infores:sri-node-normalizer"
- },
- "x-trapi": {
- "version": "1.5.0",
- "operations": [
- "annotate_nodes"
- ]
+ "infores": "infores:nodenorm"
},
"contact": {
- "email": "bizon@renci.org",
- "name": "Chris Bizon",
- "x-id": "https://github.com/cbizon",
+ "email": "scliu@scripps.edu",
+ "name": "Will Liu",
+ "x-id": "https://github.com/shuchenliu",
"x-role": "responsible developer"
},
"termsOfService": "https://toss.apps.renci.org/tos?service_long=Node+Normalization&provider_long=the+Translator+Consortium",
- "description": "Node Normalization takes a CURIE, and returns:
- The preferred CURIE for this entity
- All other known equivalent identifiers for the entity
- Semantic types for the entity as defined by the BioLink Model
The data served by Node Normalization is created by Babel, which attempts to find identifier equivalences, and makes sure that CURIE prefixes are BioLink Model Compliant. To determine whether Node Normalization is likely to be useful, check /get_semantic_types, which lists the BioLink semantic types for which normalization has been attempted, and /get_curie_prefixes, which lists the number of times each prefix is used for a semantic type."
+ "description": "Node Normalization takes a CURIE, and returns: - The preferred CURIE for this entity
- All other known equivalent identifiers for the entity
- Semantic types for the entity as defined by the Biolink Model
This is the Elasticsearch-backed implementation of the Node Normalization API, maintained by the BioThings group at Scripps Research (source code). The data it serves is created by Babel, which finds identifier equivalences and makes sure that CURIE prefixes are Biolink Model compliant. To determine whether Node Normalization is likely to be useful for the CURIEs you are interested in, please check /get_semantic_types, which lists the Biolink semantic types for which normalization has been attempted. The semantics of each endpoint are described in the Node Normalization API documentation. To learn more about how Babel creates cliques of equivalent identifiers, read its documentation."
},
+ "servers": [
+ {
+ "description": "Production",
+ "url": "https://nodenorm-es.transltr.io/",
+ "x-maturity": "production",
+ "x-location": "ITRB"
+ },
+ {
+ "description": "Continuous integration",
+ "url": "https://nodenorm-es.ci.transltr.io/",
+ "x-maturity": "staging",
+ "x-location": "ITRB"
+ },
+ {
+ "description": "Test",
+ "url": "https://nodenorm-es.test.transltr.io/",
+ "x-maturity": "testing",
+ "x-location": "ITRB"
+ },
+ {
+ "description": "Localhost",
+ "url": "http://localhost:8000/",
+ "x-maturity": "development"
+ }
+ ],
+ "tags": [
+ {
+ "name": "translator"
+ },
+ {
+ "name": "health"
+ },
+ {
+ "name": "metadata"
+ }
+ ],
"paths": {
"/status": {
"get": {
@@ -35,11 +65,6 @@
"responses": {
"200": {
"description": "Successful Response",
- "headers": {
- "Cache-Control": {
- "schema": {"type": "string", "const": "no-store"}
- }
- },
"content": {
"application/json": {
"schema": {
@@ -70,7 +95,7 @@
"version": {
"type": "string",
"description": "Node Normalization API version.",
- "example": "1.1.0"
+ "example": "1.2.0"
},
"babel_version": {
"type": "string",
@@ -163,6 +188,11 @@
}
}
}
+ },
+ "headers": {
+ "Cache-Control": {
+ "schema": {"type": "string", "const": "no-store"}
+ }
}
},
"503": {
@@ -176,80 +206,77 @@
}
}
},
+ "/version": {
+ "get": {
+ "summary": "The git commit this instance was built from",
+ "tags": ["health"],
+ "description": "Returns the git commit hash of the deployed code. For the API version, see `version` in /status.",
+ "operationId": "version",
+ "responses": {
+ "200": {
+ "description": "The deployed commit",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "version": {"type": "string"}
+ },
+ "required": ["version"]
+ },
+ "example": {"version": "fa7d74dc9537373fb36a7bef0d29cdaf4e83b897"}
+ }
+ }
+ }
+ }
+ }
+ },
"/get_normalized_nodes": {
"get": {
"summary": "Get the equivalent identifiers and semantic types for the curie(s) entered.",
"tags": ["translator"],
- "description": "Returns the equivalent identifiers and semantic types for the curie(s)",
- "operationId": "get_normalized_node_handler_get_normalized_nodes_get",
+ "description": "Returns the equivalent identifiers and semantic types for the curie(s). Each input CURIE maps to a normalized node, or to `null` if it is unknown. The boolean flags accept `true`/`false`, `1`/`0`, `yes`/`no`, `on`/`off`, `t`/`f` and `y`/`n`, in any case; any other value is a 400.",
+ "operationId": "get_normalized_nodes_get",
"parameters": [
{
+ "name": "curie",
+ "in": "query",
"description": "List of curies to normalize",
- "required": false,
+ "required": true,
"schema": {
- "items": {
- "type": "string"
- },
"type": "array",
- "minItems": 1,
- "title": "Curie",
- "description": "List of curies to normalize",
- "default": []
+ "items": {"type": "string"},
+ "minItems": 1
},
- "example": [
- "MESH:D014867",
- "NCIT:C34373"
- ],
- "name": "curie",
- "in": "query"
+ "example": ["MESH:D014867", "NCIT:C34373"]
},
{
+ "name": "conflate",
+ "in": "query",
"description": "Whether to apply gene/protein conflation",
"required": false,
- "schema": {
- "type": "boolean",
- "title": "Conflate",
- "description": "Whether to apply gene/protein conflation",
- "default": true
- },
- "name": "conflate",
- "in": "query"
+ "schema": {"type": "boolean", "default": true}
},
{
+ "name": "drug_chemical_conflate",
+ "in": "query",
"description": "Whether to apply drug/chemical conflation",
"required": false,
- "schema": {
- "type": "boolean",
- "title": "Drug Chemical Conflate",
- "description": "Whether to apply drug/chemical conflation",
- "default": false
- },
- "name": "drug_chemical_conflate",
- "in": "query"
+ "schema": {"type": "boolean", "default": false}
},
{
+ "name": "description",
+ "in": "query",
"description": "Whether to return curie descriptions when possible",
"required": false,
- "schema": {
- "type": "boolean",
- "title": "Description",
- "description": "Whether to return curie descriptions when possible",
- "default": false
- },
- "name": "description",
- "in": "query"
+ "schema": {"type": "boolean", "default": false}
},
{
+ "name": "individual_types",
+ "in": "query",
"description": "Whether to return individual types for equivalent identifiers",
"required": false,
- "schema": {
- "type": "boolean",
- "title": "Individual Types",
- "description": "Whether to return individual types for equivalent identifiers",
- "default": false
- },
- "name": "individual_types",
- "in": "query"
+ "schema": {"type": "boolean", "default": false}
},
{
"$ref": "#/components/parameters/BabelVersion"
@@ -257,10 +284,11 @@
],
"responses": {
"200": {
- "description": "Successful Response",
+ "description": "A normalized node (or `null`) for every input CURIE",
"content": {
"application/json": {
- "schema": {}
+ "schema": {"$ref": "#/components/schemas/NormalizedNodes"},
+ "examples": {"default": {"$ref": "#/components/examples/NormalizedNodes"}}
}
},
"headers": {
@@ -269,18 +297,8 @@
}
}
},
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- }
- },
"400": {
- "$ref": "#/components/responses/UnsupportedBabelVersion"
+ "description": "No `curie` parameter was supplied, or a flag is not a boolean. An unknown, removed, or malformed babel_version returns 400 with the available release names."
},
"500": {
"$ref": "#/components/responses/BabelReleaseUnavailable"
@@ -293,24 +311,23 @@
"post": {
"summary": "Get the equivalent identifiers and semantic types for the curie(s) entered.",
"tags": ["translator"],
- "description": "Returns the equivalent identifiers and semantic types for the curie(s). Use the `conflate` flag to choose whether to apply conflation. Select a Babel release with the optional babel_version string in the JSON body. Omitting it uses the configured default release.",
- "operationId": "get_normalized_node_handler_post_get_normalized_nodes_post",
+ "description": "Returns the equivalent identifiers and semantic types for the curie(s). Use the `conflate` and `drug_chemical_conflate` flags to choose which conflations to apply. Select a Babel release with the optional babel_version string in the JSON body. Omitting it uses the configured default release.",
+ "operationId": "get_normalized_nodes_post",
"requestBody": {
+ "required": true,
"content": {
"application/json": {
- "schema": {
- "$ref": "#/components/schemas/CurieList"
- }
+ "schema": {"$ref": "#/components/schemas/CurieList"}
}
- },
- "required": true
+ }
},
"responses": {
"200": {
- "description": "Successful Response",
+ "description": "A normalized node (or `null`) for every input CURIE",
"content": {
"application/json": {
- "schema": {}
+ "schema": {"$ref": "#/components/schemas/NormalizedNodes"},
+ "examples": {"default": {"$ref": "#/components/examples/NormalizedNodes"}}
}
},
"headers": {
@@ -319,18 +336,8 @@
}
}
},
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- }
- },
"400": {
- "$ref": "#/components/responses/UnsupportedBabelVersion"
+ "description": "The body is not a JSON object, has no `curies`, or a flag is not a boolean. An unknown, removed, or malformed babel_version returns 400 with the available release names."
},
"500": {
"$ref": "#/components/responses/BabelReleaseUnavailable"
@@ -345,48 +352,32 @@
"get": {
"summary": "Normalize and deduplicate a set of identifiers and return a single hash that represents this set.",
"tags": ["translator"],
- "operationId": "get_setid_get_setid_get",
+ "description": "Returns the set ID for a given set of CURIEs. A set ID is an identifier that can be used to identify this set of CURIEs going forward. It is currently impossible to recreate a set of CURIEs from a set ID, but the same set of CURIEs given to the same version of Node Normalization will always return the same set ID.",
+ "operationId": "get_setid_get",
"parameters": [
{
+ "name": "curie",
+ "in": "query",
"description": "Set of curies to normalize",
- "required": false,
+ "required": true,
"schema": {
- "items": {
- "type": "string"
- },
"type": "array",
- "minItems": 1,
- "title": "Curie",
- "description": "Set of curies to normalize",
- "default": []
+ "items": {"type": "string"},
+ "minItems": 1
},
- "example": [
- "MESH:D014867",
- "NCIT:C34373",
- "UNII:63M8RYN44N",
- "RUBBISH:1234"
- ],
- "name": "curie",
- "in": "query"
+ "example": ["MESH:D014867", "NCIT:C34373", "UNII:63M8RYN44N", "RUBBISH:1234"]
},
{
- "description": "Set of conflations to apply",
+ "name": "conflation",
+ "in": "query",
+ "description": "Set of conflations to apply; see /get_allowed_conflations",
"required": false,
"schema": {
- "items": {
- "type": "string"
- },
"type": "array",
- "title": "Conflation",
- "description": "Set of conflations to apply",
+ "items": {"type": "string"},
"default": []
},
- "example": [
- "GeneProtein",
- "DrugChemical"
- ],
- "name": "conflation",
- "in": "query"
+ "example": ["GeneProtein", "DrugChemical"]
},
{
"$ref": "#/components/parameters/BabelVersion"
@@ -394,12 +385,10 @@
],
"responses": {
"200": {
- "description": "Successful Response",
+ "description": "The set ID for the input CURIEs",
"content": {
"application/json": {
- "schema": {
- "$ref": "#/components/schemas/SetIDResponse"
- }
+ "schema": {"$ref": "#/components/schemas/SetIDResponse"}
}
},
"headers": {
@@ -408,18 +397,8 @@
}
}
},
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- }
- },
"400": {
- "$ref": "#/components/responses/UnsupportedBabelVersion"
+ "description": "No `curie` parameter was supplied. An unknown, removed, or malformed babel_version returns 400 with the available release names."
},
"500": {
"$ref": "#/components/responses/BabelReleaseUnavailable"
@@ -430,39 +409,24 @@
}
},
"post": {
- "summary": "Normalize and deduplicate a set of identifiers and return a single hash that represents this set.",
+ "summary": "Normalize and deduplicate several sets of identifiers and return a hash for each set.",
"tags": ["translator"],
- "operationId": "get_setid_get_setid_post",
+ "description": "Returns one set ID per input set, keyed by the set's position in the request as a string (\"0\", \"1\", ...). Select one Babel release for the entire batch with the optional babel_version query parameter.",
+ "operationId": "get_setid_post",
"requestBody": {
+ "required": true,
"content": {
"application/json": {
"schema": {
- "items": {
- "$ref": "#/components/schemas/SetIDQuery"
- },
"type": "array",
- "title": "Sets",
- "description": "Set of identifiers to normalize",
- "default": []
+ "items": {"$ref": "#/components/schemas/SetIDQuery"},
+ "minItems": 1
},
"example": [
+ {"curies": ["MESH:D014867", "NCIT:C34373"]},
{
- "curies": [
- "MESH:D014867",
- "NCIT:C34373"
- ]
- },
- {
- "curies": [
- "NCIT:C34373",
- "MESH:D014867",
- "UNII:63M8RYN44N",
- "RUBBISH:1234"
- ],
- "conflations": [
- "GeneProtein",
- "DrugChemical"
- ]
+ "curies": ["NCIT:C34373", "MESH:D014867", "UNII:63M8RYN44N", "RUBBISH:1234"],
+ "conflations": ["GeneProtein", "DrugChemical"]
}
]
}
@@ -470,15 +434,12 @@
},
"responses": {
"200": {
- "description": "Successful Response",
+ "description": "One set ID per input set, keyed by its index",
"content": {
"application/json": {
"schema": {
- "items": {
- "$ref": "#/components/schemas/SetIDResponse"
- },
- "type": "array",
- "title": "Response Get Setid Get Setid Post"
+ "type": "object",
+ "additionalProperties": {"$ref": "#/components/schemas/SetIDResponse"}
}
}
},
@@ -488,18 +449,8 @@
}
}
},
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- }
- },
"400": {
- "$ref": "#/components/responses/UnsupportedBabelVersion"
+ "description": "The body is not a non-empty JSON array of objects. An unknown, removed, or malformed babel_version returns 400 with the available release names."
},
"500": {
"$ref": "#/components/responses/BabelReleaseUnavailable"
@@ -512,23 +463,32 @@
{
"$ref": "#/components/parameters/BabelVersion"
}
- ],
- "description": "Select one Babel release for the entire batch using the optional babel_version query parameter. The JSON body remains an array of SetID queries."
+ ]
}
},
"/get_semantic_types": {
"get": {
- "summary": "Return a list of BioLink semantic types for which normalization has been attempted.",
+ "summary": "Return a list of Biolink semantic types for which normalization has been attempted.",
"tags": ["metadata"],
- "description": "Returns a distinct set of the semantic types discovered in the compendium data.",
- "operationId": "get_semantic_types_handler_get_semantic_types_get",
+ "description": "Returns the distinct Biolink classes found in the index, together with all of their ancestors.",
+ "operationId": "get_semantic_types",
"responses": {
"200": {
- "description": "Successful Response",
+ "description": "The semantic types in this instance",
"content": {
"application/json": {
- "schema": {
- "$ref": "#/components/schemas/SemanticTypes"
+ "schema": {"$ref": "#/components/schemas/SemanticTypes"},
+ "example": {
+ "semantic_types": {
+ "types": [
+ "biolink:NamedThing",
+ "biolink:ChemicalEntity",
+ "biolink:SmallMolecule",
+ "biolink:Disease",
+ "biolink:Gene",
+ "biolink:Protein"
+ ]
+ }
}
}
},
@@ -538,14 +498,14 @@
}
}
},
- "400": {
- "$ref": "#/components/responses/UnsupportedBabelVersion"
- },
"500": {
"$ref": "#/components/responses/BabelReleaseUnavailable"
},
"503": {
"$ref": "#/components/responses/BabelReleaseUnavailable"
+ },
+ "400": {
+ "description": "Invalid request. An unknown, removed, or malformed babel_version returns 400 with the available release names."
}
},
"parameters": [
@@ -557,2848 +517,289 @@
},
"/get_allowed_conflations": {
"get": {
+ "summary": "Return a list of named conflations.",
"tags": ["metadata"],
- "description": "Returns a list of allowed conflation options",
+ "description": "Returns the conflations this instance can apply. Conflation combines cliques on the fly: GeneProtein merges protein-coding genes with the proteins they encode. The gene(s) always appear first in the combined clique. DrugChemical merges chemicals based on their active ingredient. The active ingredient appears before any formulations in the combined clique.
You can read more about conflation in the Babel documentation.",
+ "operationId": "get_allowed_conflations",
"responses": {
"200": {
+ "description": "The conflations this instance supports",
"content": {
"application/json": {
- "schema": {
- "example": {
- "conflations": [
- "GeneProtein",
- "DrugChemical"
- ],
- "type": "object",
- "description": "Results",
- "summary": "Return a list of named conflations.",
- "tags": [
- "Interfaces"
- ]
- }
- }
+ "schema": {"$ref": "#/components/schemas/ConflationList"},
+ "example": {"conflations": ["GeneProtein", "DrugChemical"]}
}
}
}
}
+ },
+ "head": {
+ "summary": "Check that the list of named conflations is available.",
+ "tags": ["metadata"],
+ "description": "The headers of `GET /get_allowed_conflations`, without the body.",
+ "operationId": "get_allowed_conflations_head",
+ "responses": {
+ "200": {"description": "The conflation list is available"}
+ }
}
}
},
"components": {
"schemas": {
- "AboveOrBelowEnum": {
- "type": "string",
- "enum": [
- "above",
- "below"
- ],
- "title": "AboveOrBelowEnum",
- "description": "'above' or 'below'."
- },
- "Analysis": {
+ "CurieList": {
+ "title": "CurieList",
+ "description": "Curie list input model",
+ "type": "object",
+ "required": ["curies"],
"properties": {
- "resource_id": {
- "type": "string",
- "title": "resource infores"
+ "curies": {
+ "title": "List of CURIEs to normalize",
+ "type": "array",
+ "items": {"type": "string"},
+ "minItems": 1
},
- "edge_bindings": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableMapping_str__HashableSet_EdgeBinding__"
- }
- ],
- "title": "list of edge bindings"
+ "conflate": {
+ "title": "Whether to apply gene/protein conflation",
+ "type": "boolean",
+ "default": true
},
- "score": {
- "type": "number",
- "format": "float",
- "title": "Score"
+ "drug_chemical_conflate": {
+ "title": "Whether to apply drug/chemical conflation",
+ "type": "boolean",
+ "default": false
},
- "support_graphs": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableSet_str_"
- }
- ],
- "title": "Support Graphs",
- "nullable": true
+ "description": {
+ "title": "Whether to return CURIE descriptions when possible",
+ "type": "boolean",
+ "default": false
},
- "scoring_method": {
- "type": "string",
- "title": "Scoring Method",
- "nullable": true
+ "individual_types": {
+ "title": "Whether to return individual types for equivalent identifiers",
+ "type": "boolean",
+ "default": false
},
- "attributes": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableSet_Attribute_"
- }
- ],
- "title": "Attributes",
- "nullable": true
+ "babel_version": {
+ "type": "string",
+ "title": "Babel Version",
+ "description": "Babel release name discovered from Elasticsearch metadata. Omit to use the default release; unknown, removed, or malformed values return HTTP 400 with the available release names. Ambiguous releases return HTTP 503; /status reports discovery errors.",
+ "example": "2025sep1"
}
},
- "type": "object",
- "required": [
- "resource_id",
- "edge_bindings"
- ],
- "title": "analysis",
- "description": "Analysis."
+ "example": {
+ "curies": ["MESH:D014867", "NCIT:C34373"],
+ "conflate": true,
+ "drug_chemical_conflate": false,
+ "description": false,
+ "individual_types": false
+ }
},
- "AnnotateEdgesParameters": {
- "properties": {
- "attributes": {
- "$ref": "#/components/schemas/HashableSequence_str_"
- }
- },
+ "Identifier": {
+ "title": "Identifier",
+ "description": "A CURIE with its label, and optionally its description and Biolink type",
"type": "object",
- "title": "AnnotateEdgesParameters",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "AnnotateNodesParameters": {
+ "required": ["identifier"],
"properties": {
- "attributes": {
- "$ref": "#/components/schemas/HashableSequence_str_"
- }
- },
- "type": "object",
- "title": "AnnotateNodesParameters",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "AscOrDescEnum": {
- "type": "string",
- "enum": [
- "ascending",
- "descending"
- ],
- "title": "AscOrDescEnum",
- "description": "'ascending' or 'descending'."
+ "identifier": {"type": "string"},
+ "label": {"type": "string"},
+ "description": {"type": "string", "description": "Only when `description` was requested"},
+ "type": {"type": ["string", "null"], "description": "Only when `individual_types` was requested"}
+ }
},
- "AsyncQuery": {
- "properties": {
- "callback": {
- "type": "string",
- "pattern": "^https?://",
- "format": "uri",
- "title": "Callback"
- },
- "message": {
- "allOf": [
- {
- "$ref": "#/components/schemas/Message"
- }
- ],
- "title": "message"
- },
- "log_level": {
- "allOf": [
- {
- "$ref": "#/components/schemas/LogLevel"
- }
- ],
- "title": "log_level",
- "nullable": true
- },
- "workflow": {
- "$ref": "#/components/schemas/Workflow"
- }
- },
+ "NormalizedNode": {
+ "title": "NormalizedNode",
+ "description": "The clique an input CURIE belongs to",
"type": "object",
- "required": [
- "callback",
- "message"
- ],
- "title": "query",
- "description": "AsyncQuery.",
- "x-body-name": "request_body"
- },
- "Attribute": {
+ "required": ["id", "equivalent_identifiers", "type", "taxa"],
"properties": {
- "attribute_type_id": {
- "type": "string",
- "title": "type"
- },
- "value": {
- "title": "value"
- },
- "value_type_id": {
- "type": "string",
- "title": "value_type_id",
- "nullable": true
+ "id": {
+ "description": "The preferred identifier for this clique",
+ "$ref": "#/components/schemas/Identifier"
},
- "original_attribute_name": {
- "type": "string",
- "title": "Original Attribute Name",
- "nullable": true
+ "equivalent_identifiers": {
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/Identifier"}
},
- "value_url": {
- "type": "string",
- "title": "Value Url",
- "nullable": true
+ "type": {
+ "description": "Biolink classes for this clique, most specific first",
+ "type": "array",
+ "items": {"type": "string"}
},
- "attribute_source": {
- "type": "string",
- "title": "Attribute Source",
- "nullable": true
+ "information_content": {
+ "type": "number",
+ "description": "Omitted when Babel has no information content for this clique"
},
- "description": {
- "type": "string",
- "title": "Description",
- "nullable": true
+ "descriptions": {
+ "type": "array",
+ "items": {"type": "string"},
+ "description": "Only when `description` was requested"
},
- "attributes": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableSequence_SubAttribute_"
- }
- ],
- "title": "Attributes",
- "nullable": true
+ "taxa": {
+ "type": "array",
+ "items": {"type": "string"},
+ "description": "NCBITaxon CURIEs this clique is associated with"
}
- },
- "additionalProperties": false,
+ }
+ },
+ "NormalizedNodes": {
+ "title": "NormalizedNodes",
+ "description": "One normalized node per input CURIE, keyed by the input CURIE; `null` when the CURIE is unknown",
"type": "object",
- "required": [
- "attribute_type_id",
- "value"
- ],
- "title": "Attribute",
- "description": "Node/edge attribute."
+ "additionalProperties": {
+ "oneOf": [
+ {"$ref": "#/components/schemas/NormalizedNode"},
+ {"type": "null"}
+ ]
+ }
},
- "AttributeConstraint": {
+ "SetIDQuery": {
+ "title": "SetIDQuery",
+ "type": "object",
+ "required": ["curies"],
"properties": {
- "name": {
- "type": "string",
- "title": "name",
- "nullable": false
- },
- "id": {
- "type": "string",
- "title": "id",
- "nullable": false
+ "curies": {
+ "title": "Set of CURIEs",
+ "type": "array",
+ "items": {"type": "string"},
+ "minItems": 1
},
- "not": {
- "type": "boolean",
- "title": "not",
- "default": false
- },
- "operator": {
- "allOf": [
- {
- "$ref": "#/components/schemas/Operator"
- }
- ],
- "title": "operator"
- },
- "value": {
- "title": "value"
- },
- "unit_id": {
- "title": "unit_id"
- },
- "unit_name": {
- "title": "unit_name"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "name",
- "id",
- "operator",
- "value"
- ],
- "title": "AttributeConstraint",
- "description": "QNode or QEdge attribute constraint."
- },
- "AuxiliaryGraph": {
- "properties": {
- "edges": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableSet_str_"
- }
- ],
- "title": "edges in auxiliary graph"
- },
- "attributes": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableSet_Attribute_"
- }
- ],
- "title": "Attributes",
- "nullable": true
- }
- },
- "type": "object",
- "required": [
- "edges"
- ],
- "title": "auxiliary graph",
- "description": "Auxiliary Graph"
- },
- "AuxiliaryGraphs": {
- "$ref": "#/components/schemas/HashableMapping_str__AuxiliaryGraph_",
- "title": "auxiliary graphs",
- "description": "Auxiliary Graphs"
- },
- "ConflationList": {
- "properties": {
- "conflations": {
- "items": {},
- "type": "array",
- "title": "Conflations"
- }
- },
- "type": "object",
- "required": [
- "conflations"
- ],
- "title": "ConflationList"
- },
- "CurieList": {
- "properties": {
- "curies": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "minItems": 1,
- "title": "List of CURIEs to normalize"
- },
- "conflate": {
- "type": "boolean",
- "title": "Whether to apply gene/protein conflation",
- "default": true
- },
- "description": {
- "type": "boolean",
- "title": "Whether to return CURIE descriptions when possible",
- "default": false
- },
- "drug_chemical_conflate": {
- "type": "boolean",
- "title": "Whether to apply drug/chemical conflation",
- "default": false
- },
- "individual_types": {
- "type": "boolean",
- "title": "Whether to return individual types for equivalent identifiers",
- "default": false
- },
- "babel_version": {
- "type": "string",
- "title": "Babel Version",
- "description": "Babel release name discovered from Elasticsearch metadata. Omit to use the default release; unknown, removed, or malformed values return HTTP 400 with the available release names. Ambiguous releases return HTTP 503; /status reports discovery errors.",
- "example": "2025sep1"
- }
- },
- "type": "object",
- "required": [
- "curies"
- ],
- "title": "CurieList",
- "description": "Curie list input model",
- "example": {
- "curies": [
- "MESH:D014867",
- "NCIT:C34373"
- ],
- "conflate": true,
- "description": false,
- "drug_chemical_conflate": false
- }
- },
- "CuriePivot": {
- "properties": {
- "curie_prefix": {
- "additionalProperties": {
- "type": "string"
- },
- "type": "object",
- "title": "Curie Prefix"
- }
- },
- "type": "object",
- "required": [
- "curie_prefix"
- ],
- "title": "CuriePivot"
- },
- "Edge": {
- "properties": {
- "subject": {
- "type": "string",
- "title": "subject node id"
- },
- "object": {
- "type": "string",
- "title": "object node id"
- },
- "predicate": {
- "type": "string",
- "pattern": "^biolink:[a-z][a-z_]*$",
- "title": "edge predicate"
- },
- "sources": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableSet_RetrievalSource_"
- }
- ],
- "title": "list of source retrievals"
- },
- "qualifiers": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableSet_Qualifier_"
- }
- ],
- "title": "Qualifiers",
- "nullable": true
- },
- "attributes": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableSet_Attribute_"
- }
- ],
- "title": "Attributes",
- "nullable": true
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "subject",
- "object",
- "predicate",
- "sources"
- ],
- "title": "knowledge-graph edge",
- "description": "Knowledge graph edge."
- },
- "EdgeBinding": {
- "properties": {
- "id": {
- "type": "string",
- "title": "knowledge graph id"
- },
- "attributes": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableSet_Attribute_"
- }
- ],
- "title": "Attributes",
- "nullable": true
- }
- },
- "type": "object",
- "required": [
- "id"
- ],
- "title": "edge binding",
- "description": "Edge binding.",
- "example": {
- "id": "string"
- }
- },
- "EnrichResultsParameters": {
- "properties": {
- "pvalue_threshold": {
- "type": "number",
- "maximum": 1.0,
- "minimum": 0.0,
- "title": "Pvalue Threshold",
- "default": 0.000001
- },
- "qnode_keys": {
- "$ref": "#/components/schemas/HashableSequence_str_"
- },
- "predicates_to_exclude": {
- "$ref": "#/components/schemas/HashableSequence_BiolinkPredicate_"
- }
- },
- "type": "object",
- "title": "EnrichResultsParameters",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "FillAllowParameters": {
- "properties": {
- "allowlist": {
- "$ref": "#/components/schemas/HashableSequence_str_"
- },
- "qedge_keys": {
- "$ref": "#/components/schemas/HashableSequence_str_"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "title": "FillAllowParameters",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "FillDenyParameters": {
- "properties": {
- "denylist": {
- "$ref": "#/components/schemas/HashableSequence_str_"
- },
- "qedge_keys": {
- "$ref": "#/components/schemas/HashableSequence_str_"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "title": "FillDenyParameters",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "FillParameters": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/FillAllowParameters"
- },
- {
- "$ref": "#/components/schemas/FillDenyParameters"
- }
- ],
- "title": "FillParameters",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "FilterKgraphContinuousKedgeAttributeParameters": {
- "properties": {
- "edge_attribute": {
- "type": "string",
- "title": "Edge Attribute"
- },
- "threshold": {
- "type": "number",
- "title": "Threshold"
- },
- "remove_above_or_below": {
- "$ref": "#/components/schemas/AboveOrBelowEnum"
- },
- "qedge_keys": {
- "$ref": "#/components/schemas/HashableSequence_str_"
- },
- "qnode_keys": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableSequence_str_"
- }
- ],
- "title": "Qnode Keys",
- "default": []
- }
- },
- "type": "object",
- "required": [
- "edge_attribute",
- "threshold",
- "remove_above_or_below"
- ],
- "title": "FilterKgraphContinuousKedgeAttributeParameters",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "FilterKgraphDiscreteKedgeAttributeParameters": {
- "properties": {
- "edge_attribute": {
- "type": "string",
- "title": "Edge Attribute"
- },
- "remove_value": {
- "title": "Remove Value"
- },
- "qedge_keys": {
- "$ref": "#/components/schemas/HashableSequence_str_"
- },
- "qnode_keys": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableSequence_str_"
- }
- ],
- "title": "Qnode Keys",
- "default": []
- }
- },
- "type": "object",
- "required": [
- "edge_attribute"
- ],
- "title": "FilterKgraphDiscreteKedgeAttributeParameters",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "FilterKgraphDiscreteKnodeAttributeParameters": {
- "properties": {
- "node_attribute": {
- "type": "string",
- "title": "Node Attribute"
- },
- "remove_value": {
- "title": "Remove Value"
- },
- "qnode_keys": {
- "$ref": "#/components/schemas/HashableSequence_str_"
- }
- },
- "type": "object",
- "required": [
- "node_attribute"
- ],
- "title": "FilterKgraphDiscreteKnodeAttributeParameters",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "FilterKgraphPercentileParameters": {
- "properties": {
- "edge_attribute": {
- "type": "string",
- "title": "Edge Attribute"
- },
- "threshold": {
- "type": "number",
- "maximum": 100.0,
- "minimum": 0.0,
- "title": "Threshold",
- "default": 95
- },
- "remove_above_or_below": {
- "allOf": [
- {
- "$ref": "#/components/schemas/AboveOrBelowEnum"
- }
- ],
- "default": "below"
- },
- "qedge_keys": {
- "$ref": "#/components/schemas/HashableSequence_str_"
- },
- "qnode_keys": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableSequence_str_"
- }
- ],
- "title": "Qnode Keys",
- "default": []
- }
- },
- "type": "object",
- "required": [
- "edge_attribute"
- ],
- "title": "FilterKgraphPercentileParameters",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "FilterKgraphStdDevParameters": {
- "properties": {
- "edge_attribute": {
- "type": "string",
- "title": "Edge Attribute"
- },
- "plus_or_minus_std_dev": {
- "allOf": [
- {
- "$ref": "#/components/schemas/PlusOrMinusEnum"
- }
- ],
- "default": "plus"
- },
- "num_sigma": {
- "type": "number",
- "minimum": 0.0,
- "title": "Num Sigma",
- "default": 1
- },
- "remove_above_or_below": {
- "allOf": [
- {
- "$ref": "#/components/schemas/AboveOrBelowEnum"
- }
- ],
- "default": "below"
- },
- "qedge_keys": {
- "$ref": "#/components/schemas/HashableSequence_str_"
- },
- "qnode_keys": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableSequence_str_"
- }
- ],
- "title": "Qnode Keys",
- "default": []
- }
- },
- "type": "object",
- "required": [
- "edge_attribute"
- ],
- "title": "FilterKgraphStdDevParameters",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "FilterKgraphTopNParameters": {
- "properties": {
- "edge_attribute": {
- "type": "string",
- "title": "Edge Attribute"
- },
- "max_edges": {
- "type": "integer",
- "maximum": 0.0,
- "title": "Max Edges",
- "default": 50
- },
- "remove_top_or_bottom": {
- "allOf": [
- {
- "$ref": "#/components/schemas/TopOrBottomEnum"
- }
- ],
- "default": "top"
- },
- "qedge_keys": {
- "$ref": "#/components/schemas/HashableSequence_str_"
- },
- "qnode_keys": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableSequence_str_"
- }
- ],
- "title": "Qnode Keys",
- "default": []
- }
- },
- "type": "object",
- "required": [
- "edge_attribute"
- ],
- "title": "FilterKgraphTopNParameters",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "FilterResultsTopNParameters": {
- "properties": {
- "max_results": {
- "type": "integer",
- "minimum": 0.0,
- "title": "Max Results"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "max_results"
- ],
- "title": "FilterResultsTopNParameters",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "HTTPValidationError": {
- "properties": {
- "detail": {
- "items": {
- "$ref": "#/components/schemas/ValidationError"
- },
- "type": "array",
- "title": "Detail"
- }
- },
- "type": "object",
- "title": "HTTPValidationError"
- },
- "HashableMapping_CURIE__Node_": {
- "additionalProperties": {
- "$ref": "#/components/schemas/Node"
- },
- "type": "object",
- "title": "HashableMapping[CURIE, Node]",
- "description": "Custom class that implements MutableMapping and is hashable",
- "default": {}
- },
- "HashableMapping_EdgeIdentifier__Edge_": {
- "additionalProperties": {
- "$ref": "#/components/schemas/Edge"
- },
- "type": "object",
- "title": "HashableMapping[EdgeIdentifier, Edge]",
- "description": "Custom class that implements MutableMapping and is hashable",
- "default": {}
- },
- "HashableMapping_str__AuxiliaryGraph_": {
- "additionalProperties": {
- "$ref": "#/components/schemas/AuxiliaryGraph"
- },
- "type": "object",
- "title": "HashableMapping[str, AuxiliaryGraph]",
- "description": "Custom class that implements MutableMapping and is hashable",
- "default": {}
- },
- "HashableMapping_str__HashableSet_EdgeBinding__": {
- "additionalProperties": {
- "$ref": "#/components/schemas/HashableSet_EdgeBinding_"
- },
- "type": "object",
- "title": "HashableMapping[str, HashableSet[EdgeBinding]]",
- "description": "Custom class that implements MutableMapping and is hashable",
- "default": {}
- },
- "HashableMapping_str__HashableSet_NodeBinding__": {
- "additionalProperties": {
- "$ref": "#/components/schemas/HashableSet_NodeBinding_"
- },
- "type": "object",
- "title": "HashableMapping[str, HashableSet[NodeBinding]]",
- "description": "Custom class that implements MutableMapping and is hashable",
- "default": {}
- },
- "HashableMapping_str__QEdge_": {
- "additionalProperties": {
- "$ref": "#/components/schemas/QEdge"
- },
- "type": "object",
- "title": "HashableMapping[str, QEdge]",
- "description": "Custom class that implements MutableMapping and is hashable",
- "default": {}
- },
- "HashableMapping_str__QNode_": {
- "additionalProperties": {
- "$ref": "#/components/schemas/QNode"
- },
- "type": "object",
- "title": "HashableMapping[str, QNode]",
- "description": "Custom class that implements MutableMapping and is hashable",
- "default": {}
- },
- "HashableSequence_AttributeConstraint_": {
- "items": {
- "$ref": "#/components/schemas/AttributeConstraint"
- },
- "type": "array",
- "title": "HashableSequence[AttributeConstraint]",
- "description": "Custom class that implements MutableSequence and is hashable",
- "default": []
- },
- "HashableSequence_BiolinkEntity_": {
- "items": {
- "type": "string",
- "pattern": "^biolink:[A-Z][a-zA-Z]*$"
- },
- "type": "array",
- "title": "HashableSequence[BiolinkEntity]",
- "description": "Custom class that implements MutableSequence and is hashable",
- "default": []
- },
- "HashableSequence_BiolinkPredicate_": {
- "items": {
- "type": "string",
- "pattern": "^biolink:[a-z][a-z_]*$"
- },
- "type": "array",
- "title": "HashableSequence[BiolinkPredicate]",
- "description": "Custom class that implements MutableSequence and is hashable",
- "default": []
- },
- "HashableSequence_CURIE_": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "HashableSequence[CURIE]",
- "description": "Custom class that implements MutableSequence and is hashable",
- "default": []
- },
- "HashableSequence_Operation_": {
- "items": {
- "$ref": "#/components/schemas/Operation"
- },
- "type": "array",
- "title": "HashableSequence[Operation]",
- "description": "Custom class that implements MutableSequence and is hashable",
- "default": []
- },
- "HashableSequence_QualifierConstraint_": {
- "items": {
- "$ref": "#/components/schemas/QualifierConstraint"
- },
- "type": "array",
- "title": "HashableSequence[QualifierConstraint]",
- "description": "Custom class that implements MutableSequence and is hashable",
- "default": []
- },
- "HashableSequence_Qualifier_": {
- "items": {
- "$ref": "#/components/schemas/Qualifier"
- },
- "type": "array",
- "title": "HashableSequence[Qualifier]",
- "description": "Custom class that implements MutableSequence and is hashable",
- "default": []
- },
- "HashableSequence_RecursiveAttribute_": {
- "items": {
- "$ref": "#/components/schemas/RecursiveAttribute"
- },
- "type": "array",
- "title": "HashableSequence[RecursiveAttribute]",
- "description": "Custom class that implements MutableSequence and is hashable",
- "default": []
- },
- "HashableSequence_Result_": {
- "items": {
- "$ref": "#/components/schemas/Result"
- },
- "type": "array",
- "title": "HashableSequence[Result]",
- "description": "Custom class that implements MutableSequence and is hashable",
- "default": []
- },
- "HashableSequence_SubAttribute_": {
- "items": {
- "$ref": "#/components/schemas/SubAttribute"
- },
- "type": "array",
- "title": "HashableSequence[SubAttribute]",
- "description": "Custom class that implements MutableSequence and is hashable",
- "default": []
- },
- "HashableSequence_str_": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "HashableSequence[str]",
- "description": "Custom class that implements MutableSequence and is hashable",
- "default": []
- },
- "HashableSet_Analysis_": {
- "items": {
- "$ref": "#/components/schemas/Analysis"
- },
- "type": "array",
- "uniqueItems": true,
- "title": "HashableSet[Analysis]",
- "description": "Custom class that implements MutableSet and is hashable",
- "default": []
- },
- "HashableSet_Attribute_": {
- "items": {
- "$ref": "#/components/schemas/Attribute"
- },
- "type": "array",
- "uniqueItems": true,
- "title": "HashableSet[Attribute]",
- "description": "Custom class that implements MutableSet and is hashable",
- "default": []
- },
- "HashableSet_BiolinkEntity_": {
- "items": {
- "type": "string",
- "pattern": "^biolink:[A-Z][a-zA-Z]*$"
- },
- "type": "array",
- "uniqueItems": true,
- "title": "HashableSet[BiolinkEntity]",
- "description": "Custom class that implements MutableSet and is hashable",
- "default": []
- },
- "HashableSet_CURIE_": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "uniqueItems": true,
- "title": "HashableSet[CURIE]",
- "description": "Custom class that implements MutableSet and is hashable",
- "default": []
- },
- "HashableSet_EdgeBinding_": {
- "items": {
- "$ref": "#/components/schemas/EdgeBinding"
- },
- "type": "array",
- "uniqueItems": true,
- "title": "HashableSet[EdgeBinding]",
- "description": "Custom class that implements MutableSet and is hashable",
- "default": []
- },
- "HashableSet_NodeBinding_": {
- "items": {
- "$ref": "#/components/schemas/NodeBinding"
- },
- "type": "array",
- "uniqueItems": true,
- "title": "HashableSet[NodeBinding]",
- "description": "Custom class that implements MutableSet and is hashable",
- "default": []
- },
- "HashableSet_Qualifier_": {
- "items": {
- "$ref": "#/components/schemas/Qualifier"
- },
- "type": "array",
- "uniqueItems": true,
- "title": "HashableSet[Qualifier]",
- "description": "Custom class that implements MutableSet and is hashable",
- "default": []
- },
- "HashableSet_RetrievalSource_": {
- "items": {
- "$ref": "#/components/schemas/RetrievalSource"
- },
- "type": "array",
- "uniqueItems": true,
- "title": "HashableSet[RetrievalSource]",
- "description": "Custom class that implements MutableSet and is hashable",
- "default": []
- },
- "HashableSet_str_": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "uniqueItems": true,
- "title": "HashableSet[str]",
- "description": "Custom class that implements MutableSet and is hashable",
- "default": []
- },
- "KnowledgeGraph": {
- "properties": {
- "nodes": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableMapping_CURIE__Node_"
- }
- ],
- "title": "nodes"
- },
- "edges": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableMapping_EdgeIdentifier__Edge_"
- }
- ],
- "title": "edges"
- }
- },
- "type": "object",
- "required": [
- "nodes",
- "edges"
- ],
- "title": "knowledge graph",
- "description": "Knowledge graph."
- },
- "KnowledgeType": {
- "type": "string",
- "enum": [
- "lookup",
- "inferred"
- ],
- "title": "KnowledgeType",
- "description": "Knowledge Type."
- },
- "LogLevel": {
- "$ref": "#/components/schemas/LogLevelEnum",
- "title": "LogLevel",
- "description": "Log level."
- },
- "LogLevelEnum": {
- "type": "string",
- "enum": [
- "ERROR",
- "WARNING",
- "INFO",
- "DEBUG"
- ],
- "title": "LogLevelEnum",
- "description": "Log level."
- },
- "Message": {
- "properties": {
- "query_graph": {
- "allOf": [
- {
- "$ref": "#/components/schemas/QueryGraph"
- }
- ],
- "title": "query graph",
- "nullable": true
- },
- "knowledge_graph": {
- "allOf": [
- {
- "$ref": "#/components/schemas/KnowledgeGraph"
- }
- ],
- "title": "knowledge graph",
- "nullable": true
- },
- "results": {
- "allOf": [
- {
- "$ref": "#/components/schemas/Results"
- }
- ],
- "title": "list of results",
- "nullable": true
- },
- "auxiliary_graphs": {
- "allOf": [
- {
- "$ref": "#/components/schemas/AuxiliaryGraphs"
- }
- ],
- "title": "dict of auxiliary graphs",
- "nullable": true
- }
- },
- "additionalProperties": false,
- "type": "object",
- "title": "message",
- "description": "Message."
- },
- "Node": {
- "properties": {
- "categories": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableSet_BiolinkEntity_"
- }
- ],
- "title": "categories",
- "nullable": true
- },
- "name": {
- "type": "string",
- "title": "Name",
- "nullable": true
- },
- "attributes": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableSet_Attribute_"
- }
- ],
- "title": "Attributes",
- "nullable": true
- }
- },
- "additionalProperties": false,
- "type": "object",
- "title": "knowledge-graph node",
- "description": "Knowledge graph node.",
- "example": {
- "category": "string"
- }
- },
- "NodeBinding": {
- "properties": {
- "id": {
- "type": "string",
- "title": "knowledge graph id"
- },
- "query_id": {
- "type": "string",
- "title": "query graph id"
- },
- "attributes": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableSet_Attribute_"
- }
- ],
- "title": "Attributes",
- "nullable": true
- }
- },
- "type": "object",
- "required": [
- "id"
- ],
- "title": "node binding",
- "description": "Node binding.",
- "example": {
- "id": "x:string"
- }
- },
- "Operation": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/OperationAnnotate"
- },
- {
- "$ref": "#/components/schemas/OperationAnnotateEdges"
- },
- {
- "$ref": "#/components/schemas/OperationAnnotateNodes"
- },
- {
- "$ref": "#/components/schemas/OperationBind"
- },
- {
- "$ref": "#/components/schemas/OperationCompleteResults"
- },
- {
- "$ref": "#/components/schemas/OperationEnrichResults"
- },
- {
- "$ref": "#/components/schemas/OperationFill"
- },
- {
- "$ref": "#/components/schemas/OperationFilterKgraph"
- },
- {
- "$ref": "#/components/schemas/OperationFilterKgraphContinuousKedgeAttribute"
- },
- {
- "$ref": "#/components/schemas/OperationFilterKgraphDiscreteKedgeAttribute"
- },
- {
- "$ref": "#/components/schemas/OperationFilterKgraphDiscreteKnodeAttribute"
- },
- {
- "$ref": "#/components/schemas/OperationFilterKgraphOrphans"
- },
- {
- "$ref": "#/components/schemas/OperationFilterKgraphPercentile"
- },
- {
- "$ref": "#/components/schemas/OperationFilterKgraphStdDev"
- },
- {
- "$ref": "#/components/schemas/OperationFilterKgraphTopN"
- },
- {
- "$ref": "#/components/schemas/OperationFilterResults"
- },
- {
- "$ref": "#/components/schemas/OperationFilterResultsTopN"
- },
- {
- "$ref": "#/components/schemas/OperationLookup"
- },
- {
- "$ref": "#/components/schemas/OperationOverlay"
- },
- {
- "$ref": "#/components/schemas/OperationOverlayComputeJaccard"
- },
- {
- "$ref": "#/components/schemas/OperationOverlayComputeNgd"
- },
- {
- "$ref": "#/components/schemas/OperationOverlayConnectKnodes"
- },
- {
- "$ref": "#/components/schemas/OperationOverlayFisherExactTest"
- },
- {
- "$ref": "#/components/schemas/OperationRestate"
- },
- {
- "$ref": "#/components/schemas/OperationScore"
- },
- {
- "$ref": "#/components/schemas/OperationSortResults"
- },
- {
- "$ref": "#/components/schemas/OperationSortResultsEdgeAttribute"
- },
- {
- "$ref": "#/components/schemas/OperationSortResultsNodeAttribute"
- },
- {
- "$ref": "#/components/schemas/OperationSortResultsScore"
- }
- ],
- "title": "Operation",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OperationAnnotate": {
- "properties": {
- "runner_parameters": {
- "$ref": "#/components/schemas/RunnerParameters"
- },
- "id": {
- "$ref": "#/components/schemas/annotate"
- },
- "parameters": {
- "title": "Parameters"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "id"
- ],
- "title": "OperationAnnotate",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OperationAnnotateEdges": {
- "properties": {
- "runner_parameters": {
- "$ref": "#/components/schemas/RunnerParameters"
- },
- "id": {
- "$ref": "#/components/schemas/annotate_edges"
- },
- "parameters": {
- "$ref": "#/components/schemas/AnnotateEdgesParameters"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "id"
- ],
- "title": "OperationAnnotateEdges",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OperationAnnotateNodes": {
- "properties": {
- "runner_parameters": {
- "$ref": "#/components/schemas/RunnerParameters"
- },
- "id": {
- "$ref": "#/components/schemas/annotate_nodes"
- },
- "parameters": {
- "$ref": "#/components/schemas/AnnotateNodesParameters"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "id"
- ],
- "title": "OperationAnnotateNodes",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OperationBind": {
- "properties": {
- "runner_parameters": {
- "$ref": "#/components/schemas/RunnerParameters"
- },
- "id": {
- "$ref": "#/components/schemas/bind"
- },
- "parameters": {
- "title": "Parameters"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "id"
- ],
- "title": "OperationBind",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OperationCompleteResults": {
- "properties": {
- "runner_parameters": {
- "$ref": "#/components/schemas/RunnerParameters"
- },
- "id": {
- "$ref": "#/components/schemas/complete_results"
- },
- "parameters": {
- "title": "Parameters"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "id"
- ],
- "title": "OperationCompleteResults",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OperationEnrichResults": {
- "properties": {
- "runner_parameters": {
- "$ref": "#/components/schemas/RunnerParameters"
- },
- "id": {
- "$ref": "#/components/schemas/enrich_results"
- },
- "parameters": {
- "$ref": "#/components/schemas/EnrichResultsParameters"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "id"
- ],
- "title": "OperationEnrichResults",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OperationFill": {
- "properties": {
- "runner_parameters": {
- "$ref": "#/components/schemas/RunnerParameters"
- },
- "id": {
- "$ref": "#/components/schemas/fill"
- },
- "parameters": {
- "$ref": "#/components/schemas/FillParameters"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "id"
- ],
- "title": "OperationFill",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OperationFilterKgraph": {
- "properties": {
- "runner_parameters": {
- "$ref": "#/components/schemas/RunnerParameters"
- },
- "id": {
- "$ref": "#/components/schemas/filter_kgraph"
- },
- "parameters": {
- "title": "Parameters"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "id"
- ],
- "title": "OperationFilterKgraph",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OperationFilterKgraphContinuousKedgeAttribute": {
- "properties": {
- "runner_parameters": {
- "$ref": "#/components/schemas/RunnerParameters"
- },
- "id": {
- "$ref": "#/components/schemas/filter_kgraph_continuous_kedge_attribute"
- },
- "parameters": {
- "$ref": "#/components/schemas/FilterKgraphContinuousKedgeAttributeParameters"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "id",
- "parameters"
- ],
- "title": "OperationFilterKgraphContinuousKedgeAttribute",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OperationFilterKgraphDiscreteKedgeAttribute": {
- "properties": {
- "runner_parameters": {
- "$ref": "#/components/schemas/RunnerParameters"
- },
- "id": {
- "$ref": "#/components/schemas/filter_kgraph_discrete_kedge_attribute"
- },
- "parameters": {
- "$ref": "#/components/schemas/FilterKgraphDiscreteKedgeAttributeParameters"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "id",
- "parameters"
- ],
- "title": "OperationFilterKgraphDiscreteKedgeAttribute",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OperationFilterKgraphDiscreteKnodeAttribute": {
- "properties": {
- "runner_parameters": {
- "$ref": "#/components/schemas/RunnerParameters"
- },
- "id": {
- "$ref": "#/components/schemas/filter_kgraph_discrete_knode_attribute"
- },
- "parameters": {
- "$ref": "#/components/schemas/FilterKgraphDiscreteKnodeAttributeParameters"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "id",
- "parameters"
- ],
- "title": "OperationFilterKgraphDiscreteKnodeAttribute",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OperationFilterKgraphOrphans": {
- "properties": {
- "runner_parameters": {
- "$ref": "#/components/schemas/RunnerParameters"
- },
- "id": {
- "$ref": "#/components/schemas/filter_kgraph_orphans"
- },
- "parameters": {
- "title": "Parameters"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "id"
- ],
- "title": "OperationFilterKgraphOrphans",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OperationFilterKgraphPercentile": {
- "properties": {
- "runner_parameters": {
- "$ref": "#/components/schemas/RunnerParameters"
- },
- "id": {
- "$ref": "#/components/schemas/filter_kgraph_percentile"
- },
- "parameters": {
- "$ref": "#/components/schemas/FilterKgraphPercentileParameters"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "id",
- "parameters"
- ],
- "title": "OperationFilterKgraphPercentile",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OperationFilterKgraphStdDev": {
- "properties": {
- "runner_parameters": {
- "$ref": "#/components/schemas/RunnerParameters"
- },
- "id": {
- "$ref": "#/components/schemas/filter_kgraph_std_dev"
- },
- "parameters": {
- "$ref": "#/components/schemas/FilterKgraphStdDevParameters"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "id",
- "parameters"
- ],
- "title": "OperationFilterKgraphStdDev",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OperationFilterKgraphTopN": {
- "properties": {
- "runner_parameters": {
- "$ref": "#/components/schemas/RunnerParameters"
- },
- "id": {
- "$ref": "#/components/schemas/filter_kgraph_top_n"
- },
- "parameters": {
- "$ref": "#/components/schemas/FilterKgraphTopNParameters"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "id",
- "parameters"
- ],
- "title": "OperationFilterKgraphTopN",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OperationFilterResults": {
- "properties": {
- "runner_parameters": {
- "$ref": "#/components/schemas/RunnerParameters"
- },
- "id": {
- "$ref": "#/components/schemas/filter_results"
- },
- "parameters": {
- "title": "Parameters"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "id"
- ],
- "title": "OperationFilterResults",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OperationFilterResultsTopN": {
- "properties": {
- "runner_parameters": {
- "$ref": "#/components/schemas/RunnerParameters"
- },
- "id": {
- "$ref": "#/components/schemas/filter_results_top_n"
- },
- "parameters": {
- "$ref": "#/components/schemas/FilterResultsTopNParameters"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "id"
- ],
- "title": "OperationFilterResultsTopN",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OperationLookup": {
- "properties": {
- "runner_parameters": {
- "$ref": "#/components/schemas/RunnerParameters"
- },
- "id": {
- "$ref": "#/components/schemas/lookup"
- },
- "parameters": {
- "title": "Parameters"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "id"
- ],
- "title": "OperationLookup",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OperationOverlay": {
- "properties": {
- "runner_parameters": {
- "$ref": "#/components/schemas/RunnerParameters"
- },
- "id": {
- "$ref": "#/components/schemas/overlay"
- },
- "parameters": {
- "title": "Parameters"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "id"
- ],
- "title": "OperationOverlay",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OperationOverlayComputeJaccard": {
- "properties": {
- "runner_parameters": {
- "$ref": "#/components/schemas/RunnerParameters"
- },
- "id": {
- "$ref": "#/components/schemas/overlay_compute_jaccard"
- },
- "parameters": {
- "$ref": "#/components/schemas/OverlayComputeJaccardParameters"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "id",
- "parameters"
- ],
- "title": "OperationOverlayComputeJaccard",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OperationOverlayComputeNgd": {
- "properties": {
- "runner_parameters": {
- "$ref": "#/components/schemas/RunnerParameters"
- },
- "id": {
- "$ref": "#/components/schemas/overlay_compute_ngd"
- },
- "parameters": {
- "$ref": "#/components/schemas/OverlayComputeNgdParameters"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "id",
- "parameters"
- ],
- "title": "OperationOverlayComputeNgd",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OperationOverlayConnectKnodes": {
- "properties": {
- "runner_parameters": {
- "$ref": "#/components/schemas/RunnerParameters"
- },
- "id": {
- "$ref": "#/components/schemas/overlay_connect_knodes"
- },
- "parameters": {
- "title": "Parameters"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "id"
- ],
- "title": "OperationOverlayConnectKnodes",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OperationOverlayFisherExactTest": {
- "properties": {
- "runner_parameters": {
- "$ref": "#/components/schemas/RunnerParameters"
- },
- "id": {
- "$ref": "#/components/schemas/overlay_fisher_exact_test"
- },
- "parameters": {
- "$ref": "#/components/schemas/OverlayFisherExactTestParameters"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "id",
- "parameters"
- ],
- "title": "OperationOverlayFisherExactTest",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OperationRestate": {
- "properties": {
- "runner_parameters": {
- "$ref": "#/components/schemas/RunnerParameters"
- },
- "id": {
- "$ref": "#/components/schemas/restate"
- },
- "parameters": {
- "title": "Parameters"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "id"
- ],
- "title": "OperationRestate",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OperationScore": {
- "properties": {
- "runner_parameters": {
- "$ref": "#/components/schemas/RunnerParameters"
- },
- "id": {
- "$ref": "#/components/schemas/score"
- },
- "parameters": {
- "title": "Parameters"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "id"
- ],
- "title": "OperationScore",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OperationSortResults": {
- "properties": {
- "runner_parameters": {
- "$ref": "#/components/schemas/RunnerParameters"
- },
- "id": {
- "$ref": "#/components/schemas/sort_results"
- },
- "parameters": {
- "title": "Parameters"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "id"
- ],
- "title": "OperationSortResults",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OperationSortResultsEdgeAttribute": {
- "properties": {
- "runner_parameters": {
- "$ref": "#/components/schemas/RunnerParameters"
- },
- "id": {
- "$ref": "#/components/schemas/sort_results_edge_attribute"
- },
- "parameters": {
- "$ref": "#/components/schemas/SortResultsEdgeAttributeParameters"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "id",
- "parameters"
- ],
- "title": "OperationSortResultsEdgeAttribute",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OperationSortResultsNodeAttribute": {
- "properties": {
- "runner_parameters": {
- "$ref": "#/components/schemas/RunnerParameters"
- },
- "id": {
- "$ref": "#/components/schemas/sort_results_node_attribute"
- },
- "parameters": {
- "$ref": "#/components/schemas/SortResultsNodeAttributeParameters"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "id",
- "parameters"
- ],
- "title": "OperationSortResultsNodeAttribute",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OperationSortResultsScore": {
- "properties": {
- "runner_parameters": {
- "$ref": "#/components/schemas/RunnerParameters"
- },
- "id": {
- "$ref": "#/components/schemas/sort_results_score"
- },
- "parameters": {
- "$ref": "#/components/schemas/SortResultsScoreParameters"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "id",
- "parameters"
- ],
- "title": "OperationSortResultsScore",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "Operator": {
- "type": "string",
- "enum": [
- "==",
- "===",
- ">",
- "<",
- "matches"
- ],
- "title": "Operator",
- "description": "Operator."
- },
- "OverlayComputeJaccardParameters": {
- "properties": {
- "intermediate_node_key": {
- "type": "string",
- "title": "Intermediate Node Key"
- },
- "end_node_keys": {
- "$ref": "#/components/schemas/HashableSequence_str_"
- },
- "virtual_relation_label": {
- "type": "string",
- "title": "Virtual Relation Label"
- }
- },
- "type": "object",
- "required": [
- "intermediate_node_key",
- "end_node_keys",
- "virtual_relation_label"
- ],
- "title": "OverlayComputeJaccardParameters",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OverlayComputeNgdParameters": {
- "properties": {
- "qnode_keys": {
- "$ref": "#/components/schemas/HashableSequence_str_"
- },
- "virtual_relation_label": {
- "type": "string",
- "title": "Virtual Relation Label"
- }
- },
- "type": "object",
- "required": [
- "qnode_keys",
- "virtual_relation_label"
- ],
- "title": "OverlayComputeNgdParameters",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "OverlayFisherExactTestParameters": {
- "properties": {
- "subject_qnode_key": {
- "type": "string",
- "title": "Subject Qnode Key"
- },
- "object_qnode_key": {
- "type": "string",
- "title": "Object Qnode Key"
- },
- "virtual_relation_label": {
- "type": "string",
- "title": "Virtual Relation Label"
- },
- "rel_edge_key": {
- "type": "string",
- "title": "Rel Edge Key"
- }
- },
- "type": "object",
- "required": [
- "subject_qnode_key",
- "object_qnode_key",
- "virtual_relation_label"
- ],
- "title": "OverlayFisherExactTestParameters",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "PlusOrMinusEnum": {
- "type": "string",
- "enum": [
- "plus",
- "minus"
- ],
- "title": "PlusOrMinusEnum",
- "description": "'plus' or 'minus'."
- },
- "QEdge": {
- "properties": {
- "subject": {
- "type": "string",
- "title": "subject node id"
- },
- "object": {
- "type": "string",
- "title": "object node id"
- },
- "knowledge_type": {
- "allOf": [
- {
- "$ref": "#/components/schemas/KnowledgeType"
- }
- ],
- "title": "knowledge type"
- },
- "predicates": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableSequence_BiolinkPredicate_"
- }
- ],
- "title": "predicates",
- "nullable": true
- },
- "attribute_constraints": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableSequence_AttributeConstraint_"
- }
- ],
- "title": "attribute constraints",
- "default": {
- "__root__": []
- }
- },
- "qualifier_constraints": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableSequence_QualifierConstraint_"
- }
- ],
- "title": "qualifier constraint",
- "default": {
- "__root__": []
- }
- }
- },
- "type": "object",
- "required": [
- "subject",
- "object"
- ],
- "title": "query-graph edge",
- "description": "Query edge."
- },
- "QNode": {
- "properties": {
- "ids": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableSequence_CURIE_"
- }
- ],
- "title": "ids",
- "nullable": true
- },
- "categories": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableSequence_BiolinkEntity_"
- }
- ],
- "title": "categories",
- "nullable": true
- },
- "is_set": {
- "type": "boolean",
- "title": "Is Set",
- "default": false
- },
- "constraints": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableSequence_AttributeConstraint_"
- }
- ],
- "title": "attribute constraints",
- "default": {
- "__root__": []
- }
- }
- },
- "type": "object",
- "title": "query-graph node",
- "description": "Query node."
- },
- "Qualifier": {
- "properties": {
- "qualifier_type_id": {
- "type": "string",
- "pattern": "^biolink:[a-z][a-z_]*$",
- "title": "type"
- },
- "qualifier_value": {
- "type": "string",
- "title": "value"
- }
- },
- "type": "object",
- "required": [
- "qualifier_type_id",
- "qualifier_value"
- ],
- "title": "Qualifier",
- "description": "Edge qualifier."
- },
- "QualifierConstraint": {
- "properties": {
- "qualifier_set": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableSequence_Qualifier_"
- }
- ],
- "title": "qualifier set",
- "default": {
- "__root__": []
- }
- }
- },
- "type": "object",
- "title": "QualifierConstraint",
- "description": "QEdge Qualifier constraint."
- },
- "Query": {
- "properties": {
- "message": {
- "allOf": [
- {
- "$ref": "#/components/schemas/Message"
- }
- ],
- "title": "message"
- },
- "log_level": {
- "allOf": [
- {
- "$ref": "#/components/schemas/LogLevel"
- }
- ],
- "title": "log_level",
- "nullable": true
- },
- "workflow": {
- "$ref": "#/components/schemas/Workflow"
- }
- },
- "type": "object",
- "required": [
- "message"
- ],
- "title": "query",
- "description": "Request.",
- "x-body-name": "request_body"
- },
- "QueryGraph": {
- "properties": {
- "nodes": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableMapping_str__QNode_"
- }
- ],
- "title": "dict of nodes"
- },
- "edges": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableMapping_str__QEdge_"
- }
- ],
- "title": "dict of edges"
- }
- },
- "type": "object",
- "required": [
- "nodes",
- "edges"
- ],
- "title": "simple query graph",
- "description": "Query graph."
- },
- "RecursiveAttribute": {
- "properties": {
- "attribute_type_id": {
- "type": "string",
- "title": "type"
- },
- "value": {
- "title": "value"
- },
- "value_type_id": {
- "type": "string",
- "title": "value_type_id",
- "nullable": true
- },
- "original_attribute_name": {
- "type": "string",
- "title": "Original Attribute Name",
- "nullable": true
- },
- "value_url": {
- "type": "string",
- "title": "Value Url",
- "nullable": true
- },
- "attribute_source": {
- "type": "string",
- "title": "Attribute Source",
- "nullable": true
- },
- "description": {
- "type": "string",
- "title": "Description",
- "nullable": true
- },
- "attributes": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableSequence_RecursiveAttribute_"
- }
- ],
- "title": "Attributes",
- "nullable": true
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "attribute_type_id",
- "value"
- ],
- "title": "RecursiveAttribute",
- "description": "Attribute subattribute."
- },
- "ResourceRoleEnum": {
- "type": "string",
- "enum": [
- "aggregator_knowledge_source",
- "primary_knowledge_source",
- "supporting_data_source"
- ],
- "title": "ResourceRoleEnum",
- "description": "Types of resources"
- },
- "Result": {
- "properties": {
- "node_bindings": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableMapping_str__HashableSet_NodeBinding__"
- }
- ],
- "title": "list of node bindings"
- },
- "analyses": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableSet_Analysis_"
- }
- ],
- "title": "list of anlysis blocks"
- }
- },
- "type": "object",
- "required": [
- "node_bindings",
- "analyses"
- ],
- "title": "result",
- "description": "Result."
- },
- "Results": {
- "$ref": "#/components/schemas/HashableSequence_Result_",
- "title": "results",
- "description": "Results."
- },
- "RetrievalSource": {
- "properties": {
- "resource_id": {
- "type": "string",
- "title": "infores for source"
- },
- "resource_role": {
- "allOf": [
- {
- "$ref": "#/components/schemas/ResourceRoleEnum"
- }
- ],
- "title": "source type"
- },
- "upstream_resource_ids": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableSet_CURIE_"
- }
- ],
- "title": "Upstream Resource Ids",
- "nullable": true
- },
- "source_record_urls": {
- "allOf": [
- {
- "$ref": "#/components/schemas/HashableSet_str_"
- }
- ],
- "title": "Source Record Urls",
- "nullable": true
+ "conflations": {
+ "title": "Conflations to apply; see /get_allowed_conflations",
+ "type": "array",
+ "items": {"type": "string"},
+ "default": []
}
- },
- "type": "object",
- "required": [
- "resource_id",
- "resource_role"
- ],
- "title": "RetrievalSource",
- "description": "A component of source retrieval provenance"
+ }
},
- "RunnerAllowList": {
- "properties": {
- "allowlist": {
- "$ref": "#/components/schemas/HashableSequence_str_"
- },
- "timeout": {
- "type": "number",
- "title": "Timeout"
- }
- },
- "additionalProperties": false,
+ "SetIDResponse": {
+ "title": "SetIDResponse",
"type": "object",
- "title": "RunnerAllowList",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "RunnerDenyList": {
+ "required": ["curies", "conflations", "error", "normalized_curies", "normalized_string", "setid"],
"properties": {
- "denylist": {
- "$ref": "#/components/schemas/HashableSequence_str_"
- },
- "timeout": {
- "type": "number",
- "title": "Timeout"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "title": "RunnerDenyList",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "RunnerParameters": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/RunnerAllowList"
- },
- {
- "$ref": "#/components/schemas/RunnerDenyList"
+ "curies": {"type": "array", "items": {"type": "string"}},
+ "conflations": {"type": "array", "items": {"type": "string"}},
+ "error": {"type": ["string", "null"], "description": "Set when the conflations are not all allowed"},
+ "normalized_curies": {
+ "type": ["array", "null"],
+ "items": {"type": "string"},
+ "description": "The preferred identifiers, deduplicated and sorted; unknown CURIEs pass through unchanged"
},
- {
- "$ref": "#/components/schemas/RunnerTimeout"
- }
- ],
- "title": "RunnerParameters",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "RunnerTimeout": {
- "properties": {
- "timeout": {
- "type": "number",
- "title": "Timeout"
- }
+ "normalized_string": {"type": ["string", "null"], "description": "`normalized_curies` joined with `||`"},
+ "setid": {"type": ["string", "null"], "description": "UUID v5 of `normalized_string`, prefixed `uuid:`"}
},
- "additionalProperties": false,
- "type": "object",
- "title": "RunnerTimeout",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
+ "example": {
+ "curies": ["MESH:D014867", "NCIT:C34373", "UNII:63M8RYN44N", "RUBBISH:1234"],
+ "conflations": ["GeneProtein", "DrugChemical"],
+ "error": null,
+ "normalized_curies": ["CHEBI:15377", "MONDO:0004976", "RUBBISH:1234"],
+ "normalized_string": "CHEBI:15377||MONDO:0004976||RUBBISH:1234",
+ "setid": "uuid:771d3c09-9a8c-5c46-8b85-97f481a90d40"
+ }
},
"SemanticTypes": {
+ "title": "SemanticTypes",
+ "type": "object",
+ "required": ["semantic_types"],
"properties": {
"semantic_types": {
- "additionalProperties": {
- "items": {},
- "type": "array"
- },
"type": "object",
- "title": "Semantic Types"
- }
- },
- "type": "object",
- "required": [
- "semantic_types"
- ],
- "title": "SemanticTypes",
- "example": {
- "semantic_types": {
- "types": [
- "biolink:CellularComponent",
- "biolink:NamedThing",
- "etc."
- ]
+ "required": ["types"],
+ "properties": {
+ "types": {
+ "type": "array",
+ "items": {"type": "string"}
+ }
+ }
}
}
},
- "SemanticTypesInput": {
- "properties": {
- "semantic_types": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "list of semantic types"
- }
- },
+ "ConflationList": {
+ "title": "ConflationList",
"type": "object",
- "required": [
- "semantic_types"
- ],
- "title": "SemanticTypesInput",
- "description": "Semantic type input model",
- "example": {
- "semantic_types": [
- "biolink:ChemicalEntity",
- "biolink:AnatomicalEntity"
- ]
- }
- },
- "SetIDQuery": {
+ "required": ["conflations"],
"properties": {
- "curies": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Curies",
- "description": "Set of curies to normalize",
- "example": [
- "MESH:D014867",
- "NCIT:C34373"
- ]
- },
"conflations": {
- "items": {
- "type": "string"
- },
"type": "array",
- "title": "Conflations",
- "description": "Set of conflations to apply",
- "default": [],
- "example": [
- "GeneProtein",
- "DrugChemical"
- ]
+ "items": {"type": "string"}
}
- },
- "type": "object",
- "required": [
- "curies"
- ],
- "title": "SetIDQuery",
- "description": "Query for a single SetID. Includes a set of CURIEs as well as a set of conflations to apply. "
- },
- "SetIDResponse": {
- "properties": {
- "curies": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Curies"
- },
- "conflations": {
- "items": {
- "type": "string"
+ }
+ }
+ },
+ "examples": {
+ "NormalizedNodes": {
+ "summary": "Water, ALS and an unknown CURIE, with descriptions and individual types requested",
+ "value": {
+ "MESH:D014867": {
+ "id": {
+ "identifier": "CHEBI:15377",
+ "label": "Water",
+ "description": "An oxygen hydride consisting of an oxygen atom that is covalently bonded to two hydrogen atoms"
},
- "type": "array",
- "title": "Conflations"
- },
- "error": {
- "type": "string",
- "title": "Error"
+ "descriptions": [
+ "An oxygen hydride consisting of an oxygen atom that is covalently bonded to two hydrogen atoms"
+ ],
+ "equivalent_identifiers": [
+ {
+ "identifier": "CHEBI:15377",
+ "label": "water",
+ "description": "An oxygen hydride consisting of an oxygen atom that is covalently bonded to two hydrogen atoms",
+ "type": "biolink:SmallMolecule"
+ },
+ {
+ "identifier": "CHEBI:44701",
+ "label": "",
+ "type": "biolink:SmallMolecule"
+ }
+ ],
+ "type": [
+ "biolink:SmallMolecule",
+ "biolink:MolecularEntity",
+ "biolink:OntologyClass",
+ "biolink:ChemicalEntity",
+ "biolink:PhysicalEssence",
+ "biolink:ChemicalOrDrugOrTreatment",
+ "biolink:ChemicalEntityOrGeneOrGeneProduct",
+ "biolink:ChemicalEntityOrProteinOrPolypeptide",
+ "biolink:NamedThing",
+ "biolink:PhysicalEssenceOrOccurrent"
+ ],
+ "information_content": 47.5,
+ "taxa": []
},
- "normalized_curies": {
- "items": {
- "type": "string"
+ "NCIT:C34373": {
+ "id": {
+ "identifier": "MONDO:0004976",
+ "label": "amyotrophic lateral sclerosis",
+ "description": "Amyotrophic lateral sclerosis (ALS) is a neurodegenerative disease characterized by progressive muscular paralysis reflecting degeneration of motor neurons in the primary motor cortex, corticospinal tracts, brainstem and spinal cord."
},
- "type": "array",
- "title": "Normalized Curies"
- },
- "normalized_string": {
- "type": "string",
- "title": "Normalized String"
- },
- "setid": {
- "type": "string",
- "title": "Setid"
- }
- },
- "type": "object",
- "required": [
- "curies",
- "conflations"
- ],
- "title": "SetIDResponse"
- },
- "SortResultsEdgeAttributeParameters": {
- "properties": {
- "edge_attribute": {
- "type": "string",
- "title": "Edge Attribute"
- },
- "ascending_or_descending": {
- "$ref": "#/components/schemas/AscOrDescEnum"
- },
- "qedge_keys": {
- "$ref": "#/components/schemas/HashableSequence_str_"
- }
- },
- "type": "object",
- "required": [
- "edge_attribute",
- "ascending_or_descending"
- ],
- "title": "SortResultsEdgeAttributeParameters",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "SortResultsNodeAttributeParameters": {
- "properties": {
- "node_attribute": {
- "type": "string",
- "title": "Node Attribute"
- },
- "ascending_or_descending": {
- "$ref": "#/components/schemas/AscOrDescEnum"
- },
- "qnode_keys": {
- "$ref": "#/components/schemas/HashableSequence_str_"
- }
- },
- "type": "object",
- "required": [
- "node_attribute",
- "ascending_or_descending"
- ],
- "title": "SortResultsNodeAttributeParameters",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "SortResultsScoreParameters": {
- "properties": {
- "ascending_or_descending": {
- "$ref": "#/components/schemas/AscOrDescEnum"
- }
- },
- "type": "object",
- "required": [
- "ascending_or_descending"
- ],
- "title": "SortResultsScoreParameters",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "SubAttribute": {
- "properties": {
- "attribute_type_id": {
- "type": "string",
- "title": "type"
- },
- "value": {
- "title": "value"
- },
- "value_type_id": {
- "type": "string",
- "title": "value_type_id",
- "nullable": true
- },
- "original_attribute_name": {
- "type": "string",
- "title": "Original Attribute Name",
- "nullable": true
- },
- "value_url": {
- "type": "string",
- "title": "Value Url",
- "nullable": true
- },
- "attribute_source": {
- "type": "string",
- "title": "Attribute Source",
- "nullable": true
- },
- "description": {
- "type": "string",
- "title": "Description",
- "nullable": true
- },
- "attributes": {
- "allOf": [
+ "descriptions": [
+ "Amyotrophic lateral sclerosis (ALS) is a neurodegenerative disease characterized by progressive muscular paralysis reflecting degeneration of motor neurons in the primary motor cortex, corticospinal tracts, brainstem and spinal cord."
+ ],
+ "equivalent_identifiers": [
+ {
+ "identifier": "MONDO:0004976",
+ "label": "amyotrophic lateral sclerosis",
+ "description": "Amyotrophic lateral sclerosis (ALS) is a neurodegenerative disease characterized by progressive muscular paralysis reflecting degeneration of motor neurons in the primary motor cortex, corticospinal tracts, brainstem and spinal cord.",
+ "type": "biolink:Disease"
+ },
{
- "$ref": "#/components/schemas/HashableSequence_RecursiveAttribute_"
+ "identifier": "DOID:332",
+ "label": "amyotrophic lateral sclerosis",
+ "type": "biolink:Disease"
}
],
- "title": "Attributes",
- "nullable": true
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "attribute_type_id",
- "value"
- ],
- "title": "SubAttribute",
- "description": "Attribute subattribute."
- },
- "TopOrBottomEnum": {
- "type": "string",
- "enum": [
- "top",
- "bottom"
- ],
- "title": "TopOrBottomEnum",
- "description": "'top' or 'bottom'."
- },
- "ValidationError": {
- "properties": {
- "loc": {
- "items": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "integer"
- }
- ]
- },
- "type": "array",
- "title": "Location"
- },
- "msg": {
- "type": "string",
- "title": "Message"
+ "type": [
+ "biolink:Disease",
+ "biolink:DiseaseOrPhenotypicFeature",
+ "biolink:OntologyClass",
+ "biolink:BiologicalEntity",
+ "biolink:ThingWithTaxon",
+ "biolink:NamedThing"
+ ],
+ "information_content": 74.9,
+ "taxa": []
},
- "type": {
- "type": "string",
- "title": "Error Type"
- }
- },
- "type": "object",
- "required": [
- "loc",
- "msg",
- "type"
- ],
- "title": "ValidationError"
- },
- "Workflow": {
- "$ref": "#/components/schemas/HashableSequence_Operation_",
- "title": "Workflow",
- "description": "Custom base model for all classes\n\nThis provides hash and equality methods."
- },
- "annotate": {
- "type": "string",
- "enum": [
- "annotate"
- ],
- "title": "annotate",
- "description": "An enumeration."
- },
- "annotate_edges": {
- "type": "string",
- "enum": [
- "annotate_edges"
- ],
- "title": "annotate_edges",
- "description": "An enumeration."
- },
- "annotate_nodes": {
- "type": "string",
- "enum": [
- "annotate_nodes"
- ],
- "title": "annotate_nodes",
- "description": "An enumeration."
- },
- "bind": {
- "type": "string",
- "enum": [
- "bind"
- ],
- "title": "bind",
- "description": "An enumeration."
- },
- "complete_results": {
- "type": "string",
- "enum": [
- "complete_results"
- ],
- "title": "complete_results",
- "description": "An enumeration."
- },
- "enrich_results": {
- "type": "string",
- "enum": [
- "enrich_results"
- ],
- "title": "enrich_results",
- "description": "An enumeration."
- },
- "fill": {
- "type": "string",
- "enum": [
- "fill"
- ],
- "title": "fill",
- "description": "An enumeration."
- },
- "filter_kgraph": {
- "type": "string",
- "enum": [
- "filter_kgraph"
- ],
- "title": "filter_kgraph",
- "description": "An enumeration."
- },
- "filter_kgraph_continuous_kedge_attribute": {
- "type": "string",
- "enum": [
- "filter_kgraph_continuous_kedge_attribute"
- ],
- "title": "filter_kgraph_continuous_kedge_attribute",
- "description": "An enumeration."
- },
- "filter_kgraph_discrete_kedge_attribute": {
- "type": "string",
- "enum": [
- "filter_kgraph_discrete_kedge_attribute"
- ],
- "title": "filter_kgraph_discrete_kedge_attribute",
- "description": "An enumeration."
- },
- "filter_kgraph_discrete_knode_attribute": {
- "type": "string",
- "enum": [
- "filter_kgraph_discrete_knode_attribute"
- ],
- "title": "filter_kgraph_discrete_knode_attribute",
- "description": "An enumeration."
- },
- "filter_kgraph_orphans": {
- "type": "string",
- "enum": [
- "filter_kgraph_orphans"
- ],
- "title": "filter_kgraph_orphans",
- "description": "An enumeration."
- },
- "filter_kgraph_percentile": {
- "type": "string",
- "enum": [
- "filter_kgraph_percentile"
- ],
- "title": "filter_kgraph_percentile",
- "description": "An enumeration."
- },
- "filter_kgraph_std_dev": {
- "type": "string",
- "enum": [
- "filter_kgraph_std_dev"
- ],
- "title": "filter_kgraph_std_dev",
- "description": "An enumeration."
- },
- "filter_kgraph_top_n": {
- "type": "string",
- "enum": [
- "filter_kgraph_top_n"
- ],
- "title": "filter_kgraph_top_n",
- "description": "An enumeration."
- },
- "filter_results": {
- "type": "string",
- "enum": [
- "filter_results"
- ],
- "title": "filter_results",
- "description": "An enumeration."
- },
- "filter_results_top_n": {
- "type": "string",
- "enum": [
- "filter_results_top_n"
- ],
- "title": "filter_results_top_n",
- "description": "An enumeration."
- },
- "lookup": {
- "type": "string",
- "enum": [
- "lookup"
- ],
- "title": "lookup",
- "description": "An enumeration."
- },
- "overlay": {
- "type": "string",
- "enum": [
- "overlay"
- ],
- "title": "overlay",
- "description": "An enumeration."
- },
- "overlay_compute_jaccard": {
- "type": "string",
- "enum": [
- "overlay_compute_jaccard"
- ],
- "title": "overlay_compute_jaccard",
- "description": "An enumeration."
- },
- "overlay_compute_ngd": {
- "type": "string",
- "enum": [
- "overlay_compute_ngd"
- ],
- "title": "overlay_compute_ngd",
- "description": "An enumeration."
- },
- "overlay_connect_knodes": {
- "type": "string",
- "enum": [
- "overlay_connect_knodes"
- ],
- "title": "overlay_connect_knodes",
- "description": "An enumeration."
- },
- "overlay_fisher_exact_test": {
- "type": "string",
- "enum": [
- "overlay_fisher_exact_test"
- ],
- "title": "overlay_fisher_exact_test",
- "description": "An enumeration."
- },
- "restate": {
- "type": "string",
- "enum": [
- "restate"
- ],
- "title": "restate",
- "description": "An enumeration."
- },
- "score": {
- "type": "string",
- "enum": [
- "score"
- ],
- "title": "score",
- "description": "An enumeration."
- },
- "sort_results": {
- "type": "string",
- "enum": [
- "sort_results"
- ],
- "title": "sort_results",
- "description": "An enumeration."
- },
- "sort_results_edge_attribute": {
- "type": "string",
- "enum": [
- "sort_results_edge_attribute"
- ],
- "title": "sort_results_edge_attribute",
- "description": "An enumeration."
- },
- "sort_results_node_attribute": {
- "type": "string",
- "enum": [
- "sort_results_node_attribute"
- ],
- "title": "sort_results_node_attribute",
- "description": "An enumeration."
- },
- "sort_results_score": {
- "type": "string",
- "enum": [
- "sort_results_score"
- ],
- "title": "sort_results_score",
- "description": "An enumeration."
+ "RUBBISH:1234": null
+ }
}
},
"parameters": {
@@ -3430,30 +831,5 @@
"description": "Processing failed or the selected Babel release is unavailable. HTTP 503 indicates an unavailable release; see babel.errors in /status. Requests never fall back to a different release."
}
}
- },
- "tags": [
- {
- "name": "translator"
- },
- {
- "name": "health"
- },
- {
- "name": "metadata"
- }
- ],
- "servers": [
- {
- "description": "Default server",
- "url": "https://nodenorm-es.ci.transltr.io/",
- "x-maturity": "development",
- "x-location": "RENCI"
- },
- {
- "description": "Localhost",
- "url": "http://localhost:8000/",
- "x-maturity": "development",
- "x-location": "RENCI"
- }
- ]
+ }
}
diff --git a/tests/test_application.py b/tests/test_application.py
index a4f757f..8e967c5 100644
--- a/tests/test_application.py
+++ b/tests/test_application.py
@@ -48,7 +48,7 @@ def test_default_configuration_starts_autoreload():
assert app.settings["debug"] is True
assert app.settings["autoreload"] is True
start.assert_called_once_with()
- assert "/status?" in app.biothings.handlers
+ assert "/status" in app.biothings.handlers
@pytest.mark.parametrize("environment", [None, ""])
diff --git a/tests/test_babel_endpoints.py b/tests/test_babel_endpoints.py
index 9830ac9..a53ee6e 100644
--- a/tests/test_babel_endpoints.py
+++ b/tests/test_babel_endpoints.py
@@ -223,8 +223,8 @@ def test_semantic_types_select_release_and_default(self):
)
default = self.assert_selected_release(self.fetch("/get_semantic_types"), DEFAULT_VERSION)
- assert set(old["semantic_types"]["types"]) == {"biolink:Disease", "biolink:disease"}
- assert set(default["semantic_types"]["types"]) == {"biolink:Gene", "biolink:gene"}
+ assert set(old["semantic_types"]["types"]) == {"biolink:Disease"}
+ assert set(default["semantic_types"]["types"]) == {"biolink:Gene"}
assert [call[1]["index"] for call in self.client.calls] == [[OLD_INDEX], [DEFAULT_INDEX]]
def test_semantic_types_reject_unsupported_release_before_lookup(self):
@@ -366,7 +366,7 @@ def mapping(version, build_date):
default = self.assert_selected_release(self.fetch("/get_setid?curie=INPUT:1"), DEFAULT_VERSION)
assert selected["normalized_curies"] == ["OLD:1"]
- assert set(semantic_types["semantic_types"]["types"]) == {"biolink:Disease", "biolink:disease"}
+ assert set(semantic_types["semantic_types"]["types"]) == {"biolink:Disease"}
assert default["normalized_curies"] == ["NEW:1"]
assert [(operation, kwargs["index"]) for operation, kwargs in self.client.calls] == [
("msearch", [OLD_INDEX]),
diff --git a/tests/test_openapi.py b/tests/test_openapi.py
new file mode 100644
index 0000000..131a2fa
--- /dev/null
+++ b/tests/test_openapi.py
@@ -0,0 +1,448 @@
+"""
+Keep src/nodenorm/webapp/openapi.json in step with the handlers.
+
+The OpenAPI document is written by hand and served as a static file (Tornado has nothing like
+FastAPI's schema generation), so nothing stops it drifting from the code except these tests.
+If one fails after a change to a handler, edit the JSON; if it fails after a change to the
+JSON, the document now promises something the code doesn't do.
+"""
+
+import ast
+import dataclasses
+import importlib.resources
+import inspect
+import json
+import re
+import textwrap
+from types import SimpleNamespace
+from unittest import mock
+
+import jsonschema
+import pytest
+import tornado.web
+from openapi_spec_validator import validate
+from tornado.testing import AsyncHTTPTestCase
+
+import nodenorm
+from nodenorm.babel import BabelReleases
+from nodenorm.handlers import build_handlers
+from nodenorm.handlers.base import NodeNormalizationBaseHandler
+from nodenorm.handlers.conflations import ValidConflationsHandler
+from nodenorm.handlers.normalized_nodes import NormalizedNodesHandler, parse_boolean
+from nodenorm.handlers.semantic_types import SemanticTypeHandler
+from nodenorm.handlers.set_identifiers import SetIdentifierHandler, SetIDResponse
+from nodenorm.handlers.status import NodeNormHealthHandler
+
+SPEC = json.loads((importlib.resources.files(nodenorm) / "webapp" / "openapi.json").read_text(encoding="utf-8"))
+RELEASES = BabelReleases("2026jul22", {"2026jul22": "nodenorm", "2025sep1": "nodenorm-old"})
+
+# Only the handlers that answer the API itself: not the static files, index or redirects.
+API_HANDLERS = {
+ pattern: handler
+ for pattern, handler, *_ in build_handlers().values()
+ if isinstance(handler, type) and issubclass(handler, NodeNormalizationBaseHandler)
+}
+
+
+def test_document_is_valid_openapi():
+ validate(SPEC)
+
+
+def test_every_route_is_documented_and_every_documented_path_is_routed():
+ assert set(SPEC["paths"]) == set(API_HANDLERS)
+
+
+# Every method Tornado routes except OPTIONS, which the base handler answers for CORS preflight.
+HTTP_METHODS = {method.lower() for method in tornado.web.RequestHandler.SUPPORTED_METHODS} - {"options"}
+
+
+def test_documented_methods_match_the_handlers():
+ for path, handler in API_HANDLERS.items():
+ documented = set(SPEC["paths"][path]) & HTTP_METHODS
+ implemented = {method for method in HTTP_METHODS if method in vars(handler)}
+ assert documented == implemented, path
+
+
+def resolve_reference(value: dict) -> dict:
+ """Resolve local JSON pointers, including shared query parameters and headers."""
+ while "$ref" in value:
+ reference = value["$ref"]
+ assert reference.startswith("#/"), reference
+ value = SPEC
+ for part in reference[2:].split("/"):
+ value = value[part.replace("~1", "/").replace("~0", "~")]
+ return value
+
+
+def method_calls(handler, method):
+ """Inspect a method and its handler helpers without mixing GET and POST fields."""
+ visited = set()
+
+ def visit(name):
+ if name in visited:
+ return
+ visited.add(name)
+ function = getattr(handler, name, None)
+ if function is None or function.__module__ != handler.__module__:
+ return
+ tree = ast.parse(textwrap.dedent(inspect.getsource(function)))
+ for node in ast.walk(tree):
+ if not isinstance(node, ast.Call) or not isinstance(node.func, ast.Attribute):
+ continue
+ yield node
+ if isinstance(node.func.value, ast.Name) and node.func.value.id == "self":
+ yield from visit(node.func.attr)
+
+ return visit(method)
+
+
+def literal_argument(call):
+ if call.args and isinstance(call.args[0], ast.Constant) and isinstance(call.args[0].value, str):
+ return call.args[0].value
+ return None
+
+
+def query_parameters(path_item, method):
+ parameters = path_item.get("parameters", []) + path_item[method].get("parameters", [])
+ return {p["name"] for parameter in parameters if (p := resolve_reference(parameter))["in"] == "query"}
+
+
+def test_documented_query_parameters_match_the_handlers():
+ readers = {"get_argument", "get_arguments", "get_query_argument", "get_query_arguments"}
+ for path, handler in API_HANDLERS.items():
+ for method in HTTP_METHODS & vars(handler).keys():
+ documented = query_parameters(SPEC["paths"][path], method)
+ implemented = {
+ name
+ for call in method_calls(handler, method)
+ if call.func.attr in readers and (name := literal_argument(call)) is not None
+ }
+ assert documented == implemented, (path, method)
+
+
+def test_query_parameter_check_resolves_shared_parameters_for_post():
+ # POST /get_setid keeps its release selector in the query, outside the array body.
+ assert query_parameters(SPEC["paths"]["/get_setid"], "post") == {"babel_version"}
+ assert query_parameters(SPEC["paths"]["/get_semantic_types"], "get") == {"babel_version"}
+
+
+def test_query_parameter_check_follows_helpers_and_keeps_methods_separate():
+ class Handler:
+ def get(self):
+ self.read_release()
+
+ def read_release(self):
+ return self.get_query_argument("babel_version")
+
+ def post(self):
+ return self.get_query_arguments("batch")
+
+ assert [
+ literal_argument(call) for call in method_calls(Handler, "get") if call.func.attr == "get_query_argument"
+ ] == ["babel_version"]
+ assert [literal_argument(call) for call in method_calls(Handler, "post")] == ["batch"]
+
+
+def resolve(schema: dict) -> dict:
+ """Follow a local $ref, and an array to its items, down to the object schema of one body item."""
+ schema = resolve_reference(schema)
+ if schema.get("type") == "array":
+ return resolve(schema["items"])
+ return schema
+
+
+def test_documented_request_bodies_match_the_handlers():
+ # Body fields are read with .get("field", default), in post() or a helper it calls.
+ posting = {path: handler for path, handler in API_HANDLERS.items() if "post" in vars(handler)}
+ assert posting, "no handler defines post()"
+ for path, handler in posting.items():
+ body_schema = SPEC["paths"][path]["post"]["requestBody"]["content"]["application/json"]["schema"]
+ documented = set(resolve(body_schema)["properties"])
+ implemented = {
+ name
+ for call in method_calls(handler, "post")
+ if call.func.attr == "get" and (name := literal_argument(call)) is not None
+ }
+ assert documented == implemented, path
+
+
+def test_setid_response_schema_matches_the_dataclass():
+ documented = set(SPEC["components"]["schemas"]["SetIDResponse"]["properties"])
+ assert documented == {field.name for field in dataclasses.fields(SetIDResponse)}
+
+
+class TestOpenAPIRedirect(AsyncHTTPTestCase):
+ """/openapi.json is where NodeNorm Redis, SmartAPI and Babel Validation look for the document."""
+
+ def get_app(self) -> tornado.web.Application:
+ return tornado.web.Application(list(build_handlers().values()))
+
+ def test_openapi_json_redirects_to_the_document(self):
+ response = self.fetch("/openapi.json", follow_redirects=False)
+
+ assert response.code == 301
+ assert response.headers["Location"] == "/webapp/openapi.json"
+
+ def test_served_document_is_valid_openapi(self):
+ # test_document_is_valid_openapi() reads the file; this checks what the static route serves.
+ response = self.fetch("/openapi.json")
+
+ assert response.code == 200
+ assert response.headers["Content-Type"].startswith("application/json")
+ validate(json.loads(response.body))
+
+ def test_only_the_exact_path_redirects(self):
+ assert self.fetch("/openapiXjson", follow_redirects=False).code == 404
+
+ def test_api_paths_are_matched_exactly(self):
+ # Regression: patterns like r"/get_setid?" made the last letter optional, so /get_seti was routed.
+ for path in API_HANDLERS:
+ assert self.fetch(path[:-1]).code == 404, path
+
+
+class TestAllowedConflations(AsyncHTTPTestCase):
+ """Regression: this endpoint returned HTTP 500 because it finished a bare list."""
+
+ def get_app(self) -> tornado.web.Application:
+ return tornado.web.Application([(r"/get_allowed_conflations", ValidConflationsHandler)])
+
+ def test_response_matches_the_documented_schema(self):
+ response = self.fetch("/get_allowed_conflations")
+
+ assert response.code == 200
+ body = json.loads(response.body)
+ jsonschema.validate(body, SPEC["components"]["schemas"]["ConflationList"])
+ assert {"GeneProtein", "DrugChemical"} <= set(body["conflations"])
+
+ def test_head_is_answered(self):
+ assert self.fetch("/get_allowed_conflations", method="HEAD").code == 200
+
+
+class TestSetIDErrors(AsyncHTTPTestCase):
+ """Answers that /get_setid gives without reaching Elasticsearch."""
+
+ def get_app(self) -> tornado.web.Application:
+ app = tornado.web.Application([(r"/get_setid", SetIdentifierHandler)])
+ app.biothings = SimpleNamespace(elasticsearch=SimpleNamespace(babel=RELEASES))
+ return app
+
+ def test_get_reports_an_unknown_conflation(self):
+ # Regression: generate_setid() returned the dataclass itself here, which Tornado can't write (HTTP 500).
+ response = self.fetch("/get_setid?curie=MESH:D014867&conflation=Bogus")
+
+ assert response.code == 200
+ body = json.loads(response.body)
+ jsonschema.validate(body, SPEC["components"]["schemas"]["SetIDResponse"])
+ assert "Bogus" in body["error"]
+ assert body["setid"] is None
+
+ def test_post_reports_an_unknown_conflation(self):
+ body = json.dumps([{"curies": ["MESH:D014867"], "conflations": ["Bogus"]}])
+ response = self.fetch("/get_setid", method="POST", body=body)
+
+ assert response.code == 200
+ result = json.loads(response.body)
+ jsonschema.validate(result["0"], SPEC["components"]["schemas"]["SetIDResponse"])
+ assert "Bogus" in result["0"]["error"]
+
+ def test_post_answers_a_set_with_no_curies(self):
+ # The other early return in generate_setid(); it too used to return the dataclass (HTTP 500).
+ empty_lookup = mock.AsyncMock(return_value={})
+ with mock.patch("nodenorm.handlers.set_identifiers.get_normalized_nodes", empty_lookup):
+ response = self.fetch("/get_setid", method="POST", body=json.dumps([{"curies": []}]))
+
+ assert response.code == 200
+ result = json.loads(response.body)
+ jsonschema.validate(result["0"], SPEC["components"]["schemas"]["SetIDResponse"])
+ assert result["0"]["normalized_curies"] == [] and result["0"]["setid"] is None
+
+ def test_post_rejects_a_body_that_is_not_an_array_of_objects(self):
+ # A single object was the documented body before; it used to fail with a 500.
+ for body in ('{"curies": ["MESH:D014867"]}', '["MESH:D014867"]', "[]", "not json", b'[{"curies": ["\xff"]}]'):
+ assert self.fetch("/get_setid", method="POST", body=body).code == 400, body
+
+
+class FakeElasticsearch:
+ """Enough of AsyncElasticsearch to exercise the documented response shapes offline."""
+
+ def __init__(self):
+ self.calls = []
+ self.indices = SimpleNamespace(get_mapping=self.get_mapping)
+
+ async def search(self, **kwargs):
+ self.calls.append(kwargs)
+ return SimpleNamespace(body={"aggregations": {"unique_types": {"buckets": [{"key": "biolink:Disease"}]}}})
+
+ async def msearch(self, **kwargs):
+ self.calls.append(kwargs)
+ responses = []
+ for query in kwargs["searches"][1::2]:
+ curie = query["query"]["bool"]["filter"][0]["terms"]["identifiers.i"][0]
+ source = {
+ "identifiers": [{"i": curie, "l": "Example", "d": ["Example description"]}],
+ "type": "biolink:Disease",
+ "preferred_name": "Example",
+ "taxa": ["NCBITaxon:9606"],
+ "ic": 1.0,
+ }
+ responses.append({"hits": {"total": {"value": 1}, "hits": [{"_id": curie, "_source": source}]}})
+ return SimpleNamespace(body={"responses": responses})
+
+ async def get_mapping(self, **kwargs):
+ return {
+ index: {"mappings": {"_meta": {"src": {"nodenorm": {"version": RELEASES.default_version}}}}}
+ for index in kwargs["index"]
+ }
+
+
+class TestMultiBabelResponses(AsyncHTTPTestCase):
+ def get_app(self):
+ self.client = FakeElasticsearch()
+ app = tornado.web.Application(list(build_handlers().values()))
+ app.biothings = SimpleNamespace(
+ config=SimpleNamespace(deployment_environment="CI"),
+ elasticsearch=SimpleNamespace(
+ async_client=self.client,
+ indices=["nodenorm"],
+ babel=RELEASES,
+ babel_checked_at="2026-11-02T12:00:00+00:00",
+ babel_refresh_error=None,
+ ),
+ )
+ return app
+
+ def validate_response(self, response, path, method):
+ assert response.code == 200, response.body
+ documented = SPEC["paths"][path][method]["responses"]["200"]
+ if path != "/status":
+ assert "X-Babel-Version" in documented.get("headers", {})
+ schema = documented["content"]["application/json"]["schema"]
+ jsonschema.validate(json.loads(response.body), {**schema, "components": SPEC["components"]})
+ for name, header in documented.get("headers", {}).items():
+ jsonschema.validate(response.headers[name], resolve_reference(header)["schema"])
+
+ def test_selected_release_responses_match_the_document(self):
+ for version in RELEASES.available_versions:
+ for path, method, body in [
+ ("/get_normalized_nodes", "get", None),
+ ("/get_normalized_nodes", "post", {"curies": ["MONDO:0004976"], "babel_version": version}),
+ ("/get_setid", "get", None),
+ ("/get_setid", "post", [{"curies": ["MONDO:0004976"]}]),
+ ("/get_semantic_types", "get", None),
+ ]:
+ if path == "/get_normalized_nodes" and method == "post":
+ url = path
+ body.update(description=True, individual_types=True)
+ else:
+ url = f"{path}?babel_version={version}"
+ if method == "get" and path != "/get_semantic_types":
+ url += "&curie=MONDO:0004976"
+ options = {"method": method.upper()}
+ if body is not None:
+ options["body"] = json.dumps(body)
+ options["headers"] = {"Content-Type": "application/json"}
+ response = self.fetch(url, **options)
+ self.validate_response(response, path, method)
+ assert response.headers["X-Babel-Version"] == version
+ assert self.client.calls[-1]["index"] == RELEASES.resolve(version).search_indices
+
+ def test_status_matches_current_metadata_schema(self):
+ response = self.fetch("/status")
+ self.validate_response(response, "/status", "get")
+ body = json.loads(response.body)
+ assert body["deployment_environment"] == "CI"
+ assert body["babel_version"] == body["babel"]["default_version"]
+ assert body["babel_version_url"] == body["babel"]["available_versions"][body["babel_version"]]
+
+
+class TestSemanticTypes(AsyncHTTPTestCase):
+ """Regression: the handler lower-cased every class ("biolink:namedthing")."""
+
+ def get_app(self) -> tornado.web.Application:
+ app = tornado.web.Application([(r"/get_semantic_types", SemanticTypeHandler)])
+ app.biothings = SimpleNamespace(
+ elasticsearch=SimpleNamespace(indices="nodenorm", async_client=FakeElasticsearch(), babel=RELEASES)
+ )
+ return app
+
+ def test_types_are_biolink_class_curies(self):
+ response = self.fetch("/get_semantic_types")
+
+ assert response.code == 200
+ body = json.loads(response.body)
+ jsonschema.validate(body, SPEC["components"]["schemas"]["SemanticTypes"])
+ types = body["semantic_types"]["types"]
+ assert "biolink:Disease" in types and "biolink:NamedThing" in types
+ assert all(re.fullmatch(r"biolink:[A-Z][A-Za-z]*", t) for t in types), types
+
+
+class UnreachableElasticsearch:
+ """An AsyncElasticsearch whose cluster is down."""
+
+ class indices:
+ @staticmethod
+ async def get_mapping(**kwargs):
+ raise ConnectionError("cluster unreachable")
+
+
+class TestStatusWithoutElasticsearch(AsyncHTTPTestCase):
+ """An unreachable cluster preserves main's documented HTTP 503 health response."""
+
+ def get_app(self) -> tornado.web.Application:
+ app = tornado.web.Application([(r"/status", NodeNormHealthHandler)])
+ app.biothings = SimpleNamespace(
+ elasticsearch=SimpleNamespace(
+ indices="nodenorm",
+ async_client=UnreachableElasticsearch(),
+ babel=RELEASES,
+ babel_checked_at="2026-11-02T12:00:00+00:00",
+ babel_refresh_error=None,
+ )
+ )
+ return app
+
+ def test_status_is_unavailable(self):
+ response = self.fetch("/status")
+
+ assert response.code == 503
+ assert "503" in SPEC["paths"]["/status"]["get"]["responses"]
+ assert response.headers["Cache-Control"] == "no-store"
+
+
+@pytest.mark.parametrize("value", [True, "true", "TRUE", "1", "yes", "on", "t", "Y"])
+def test_true_flags(value):
+ assert parse_boolean("conflate", value) is True
+
+
+@pytest.mark.parametrize("value", [False, "false", "False", "0", "no", "off", "f", "N"])
+def test_false_flags(value):
+ assert parse_boolean("conflate", value) is False
+
+
+@pytest.mark.parametrize("value", ["", "maybe", "2", None, 0])
+def test_other_flags_are_rejected(value):
+ # Regression: anything but "false" used to mean true, so conflate=0 turned conflation on.
+ with pytest.raises(tornado.web.HTTPError) as raised:
+ parse_boolean("conflate", value)
+ assert raised.value.status_code == 400
+
+
+class TestNormalizedNodesFlags(AsyncHTTPTestCase):
+ """A flag that isn't a boolean is rejected before Elasticsearch is queried."""
+
+ def get_app(self) -> tornado.web.Application:
+ app = tornado.web.Application([(r"/get_normalized_nodes", NormalizedNodesHandler)])
+ app.biothings = SimpleNamespace()
+ return app
+
+ def test_get_rejects_a_flag_that_is_not_a_boolean(self):
+ assert self.fetch("/get_normalized_nodes?curie=MESH:D014867&conflate=maybe").code == 400
+
+ def test_post_rejects_a_flag_that_is_not_a_boolean(self):
+ body = json.dumps({"curies": ["MESH:D014867"], "conflate": "maybe"})
+ assert self.fetch("/get_normalized_nodes", method="POST", body=body).code == 400
+
+ def test_post_rejects_a_body_that_is_not_a_json_object(self):
+ # Each of these used to fail with a 500.
+ for body in ('["MESH:D014867"]', "not json", b'{"curies": ["\xff"]}'):
+ assert self.fetch("/get_normalized_nodes", method="POST", body=body).code == 400, body
diff --git a/tests/test_set_identifiers.py b/tests/test_set_identifiers.py
index ea96679..2d82983 100644
--- a/tests/test_set_identifiers.py
+++ b/tests/test_set_identifiers.py
@@ -82,10 +82,12 @@ def test_post_endpoint(self):
url = self.get_url(normalized_nodes_endpoint)
body = json.dumps(
- {
- "curies": ["MESH:D014867", "NCIT:C34373", "UNII:63M8RYN44N", "RUBBISH:1234"],
- "conflations": ["GeneProtein", "DrugChemical"],
- }
+ [
+ {
+ "curies": ["MESH:D014867", "NCIT:C34373", "UNII:63M8RYN44N", "RUBBISH:1234"],
+ "conflations": ["GeneProtein", "DrugChemical"],
+ }
+ ]
)
headers = {"Content-Type": "application/json"}
@@ -93,12 +95,15 @@ def test_post_endpoint(self):
response = yield http_client.fetch(url, self.stop, method="POST", headers=headers, body=body, request_timeout=0)
body = json.loads(response.body.decode("utf-8"))
+ # One SetIDResponse per input set, keyed by the set's index in the request.
expected_body = {
- "curies": ["MESH:D014867", "NCIT:C34373", "UNII:63M8RYN44N", "RUBBISH:1234"],
- "conflations": ["GeneProtein", "DrugChemical"],
- "error": None,
- "normalized_curies": ["CHEBI:15377", "MONDO:0004976", "RUBBISH:1234"],
- "normalized_string": "CHEBI:15377||MONDO:0004976||RUBBISH:1234",
- "setid": "uuid:771d3c09-9a8c-5c46-8b85-97f481a90d40",
+ "0": {
+ "curies": ["MESH:D014867", "NCIT:C34373", "UNII:63M8RYN44N", "RUBBISH:1234"],
+ "conflations": ["GeneProtein", "DrugChemical"],
+ "error": None,
+ "normalized_curies": ["CHEBI:15377", "MONDO:0004976", "RUBBISH:1234"],
+ "normalized_string": "CHEBI:15377||MONDO:0004976||RUBBISH:1234",
+ "setid": "uuid:771d3c09-9a8c-5c46-8b85-97f481a90d40",
+ }
}
assert json.loads(response.body.decode("utf-8")) == expected_body