The components of the Unified Biomedical Knowledge Graph (UBKG) include:
This repository contains the source for the API.
The ubkg-api is a Flask web application with Blueprint extensions that provides a REST API for parameterized queries against an instance of a UBKG neo4j instance.
The ubkg-api contains code that is common to all UBKG contexts, including code to handle connecting to a UBKG instance.
Other API implementations, such as the hs-ontology-api, extend the ubkg-api. Endpoints common to all UBKG contexts are deployed in the ubkg-api; other API implementations can both "pass through" calls to ubkg-api endpoints or execute endpoints specific to the implementation.
An API implementation extends the ubkg-api by calling a version of the ubkg-api that has been published as a PyPI.
The specification for the UBKG API can be found here.
To enhance or fix the ubkg-api, you will need to establish an application development environment on the development machine. Components of the application development environment include:
To connect the local branch of ubkg-api to a neo4j instance that hosts a UBKG context:
If you are working with a local Docker distribution based on from ubkg-neo4j, then
Some deployments based on the UBKG API may require that calls to endpoints include an API key in the header. Because this involves integration with authorization architecture (e.g., API gateways and authorization specific to a network resource), the configuration of API keys is beyond the scope of the UBKG API.
If you are using a local instance of the UBKG, the instance should be running. In particular, if you installed a local Docker instance of UBKG, be sure that Docker Desktop is running. If the neo4j instance is not available, calls to API endpoints will result in a 500 error.
For URLs that execute endpoints in your local instance, use the values indicated in the main.py script, in the section prefaced with the comment For local development/testing
:
For example, if main.py indicates
app.run(host='0.0.0.0', port="5002")
then your test endpoint URLs should start with http://127.0.0.1:5002/
.
For example, if you test using PostMan, you can set a global variable corresponding to the first part of your test URLs:
To test changes to ubkg-api, you will need to start a local instance of the API. The following assumes that you have created a local branch of ubkg-api.
Move to the root of your local branch.
Create a Python virtual environment. The following command creates a virtual environment named venv.
python -m venv venv
Activate the virtual environment.
source venv/bin/activate
Move to the /src directory and install dependencies, inclduing the ubkg-api package.
pip install -r requirements.txt
Run app.py to start the local instance of the API.
python app.py
chmod 777 app.py
Once you have connected your instance of hs-ontology-api to instances of both neo4j and ubkg-api, run the following tests:
http://127.0.0.1:5002/
. You should see a window with the status message Hello! This is UBKG-API service :)
. The status message verifies that your local instance of hs-ontology-api is connected to an instance of ubkg-api.http://127.0.0.1:5002/concepts/C0678222/codes
. You should see a response from the ubkg-api, depending on the endpoint.Various methods of testing endpoint URLs are possible, including:
Each endpoint in ubkg-api involves:
The model script is a class that defines the response for the endpoint.
Create the script in the models path.
__init__
: For every key that is returned,
self.openapi_types
dictionary.self.attribute_map
dictionary.For example, for a string value with key _approvedsymbol,
self.openapi_types = {
'approved_symbol': str
}
self.attribute_map = {
'approved_symbol': 'approved_symbol',
}
self._approved_symbol = approved_symbol
serialize
and from_dict
methods that refer to the returned key/value pairs. Override the return type of the from_dict
to point to the model class.The following code is from the GeneDetail model class in genedetail.
def serialize(self):
return {
"approved_symbol": self._approved_symbol
}
@classmethod
def from_dict(cls, dikt) -> 'GeneDetail':
"""Returns the dict as a model
:param dikt: A dict.
:type: dict
:return: The GeneDetail of this GeneDetail
:rtype: GeneDetail
"""
return util.deserialize_model(dikt, cls)
For each key in the response, define getter and setter functions.
@property
def approved_symbol(self):
return self.approved_symbol
@approved_symbol.setter
def approved_symbol(self, approved_symbol):
self._approved_symbol = approved_symbol
### Add functional script code to neo4j_logic.py
The _neo4j_logic.py_ script contains endpoint-related functions. The usual use case is a parameterized Cypher query.
#### Naming convention
1. For functions called directly from controllers, name the function with format *model*_*method*_logic. For example, the function that satisfies the POST method for the *genedetail* controller would be called **genedetail_post_logic**.
2. Subfunctions called by main functions should be prefixed with an underscore.
#### Loading large Cypher queries
If the Cypher query used by an endpoint function is complex, store an annotated copy of the query in the _cypher_ directory.
#### Examples
The methods for returning to GET requests and POST requests are slightly different. You should be able to find examples of either type of function.
#### Loading Cypher query strings
Large or complex Cypher query strings can be stored in files in the _cypher_ directory and loaded using the **loadquerystring** function in the **common_neo4j_logic.py** script.
Following is the excerpt from **common_neo4j_logic.py** that loads the large Cypher query string used for the _genes_ endpoint.
# Load Cypher query from file.
query: str = loadquerystring('codes_code_id_codes.cypher')
#### Nested objects
If your response body is to contain nested objects, you will need to create models for each type of sub-object.
The containing model script will need to import the sub-object models.
For an example, review **genedetail.py** in hs-ontology-api.
### Build a controller script
#### File path
Create a Python package in the __routes__ path.
#### Define Blueprint
Define a Blueprint object and route for your endpoint. Follow examples in the existing controllers.
### Register your Blueprint
In *app.py*,
1. Import your Blueprint.
2. Register your Blueprint with Flask.
The following snippet registers the Blueprint:
from common_routes.codes.codes_controller import codes_blueprint self.app.register_blueprint(codes_blueprint)
# Updating SmartAPI documentation
To add the specification for a new endpoint to the SmartAPI documentation for hs-ontology-api, update the file **hs-ontology-api-spec.yaml**.
hs-ontology-api-spec.yaml conforms to [Swagger OpenAPI](https://swagger.io/specification/) format.
You will need to specify:
1. Paths that correspond to your endpoint routes.
2. Schemas that correspond to the responses from endpoints.
# Building and publishing to PyPI
To build and publish the ubkg-api as a PyPI package,
python -m build python3 -m twine upload dist/*
## Requirements
Python 3.9 or newer