Like ubkg-api, the hs-ontology-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 hs-ontology-api is specific to the HuBMAP/SenNet context of UBKG: it manages endpoints that assume that the UBKG instance includes content ingested from SABs related to HuBMAP and SenNet. The UBKG-api contains code that is common to all UBKG contexts.
By default, hs-ontology-api imports the ubkg-api that has been compiled as a PyPi package.
The SmartAPI documentation for the hs-ontology-api can be found here.
To enhance or fix the hs-ontology-api, you will need to establish an application development environment on the development machine. Components of the application development environment include:
To connect your branch of hs-ontology-api to a neo4j instance that hosts a HuBMAP/SenNet UBKG context:
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.
If you are modifying code only in hs-ontology-api, you will only need to use the PyPy package version of ubkg-api. The package is included in the requirements.txt file of this repo.
If you need to modify both the hs-ontology-api and ubkg-api in concert, you will need to work with a local or branch instance of the ubkg-api. This is possible by doing the following:
git+https://github.com/x-atlas-consortia/ubkg-api.git@<YOUR BRANCH>
pip install -e path/to/local/ubkg-api/repo
b. If using PyCharm, in the Python Packages tab,
1) Click Add Package.
2) Navigate to the root of the ubkg-api repo.
3) Indicate that the package is editable.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/
To test changes to hs-ontology-api, you will need to start a local instance of your local API.
The following assumes that you have created a local branch of hs-ontology-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 main.py to start the local instance of the API.
python main.py
chmod 777 main.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/datasets?application_context=HUBMAP
. You should see a response from either the hs-ontology-api or the ubkg-api, depending on the endpoint and your development configuration.Various methods of testing endpoint URLs are possible, including:
Each endpoint in hs-ontology-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 **util_query.py** script.
Following is the excerpt from **neo4j_logic.py** that loads the large Cypher query string used for the _genes_ endpoint.
from hs_ontology_api.cypher.util_query import loadquerystring
queryfile = 'genedetail.cypher' query = loadquerystring(queryfile)
#### 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**.
### 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 *main.py*,
1. Import your Blueprint.
2. Register your Blueprint with Flask.
The following snippet registers
from hs_ontology_api.routes.genedetail.genedetail_controller import genedetail_blueprint app.register_blueprint(genedetail_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.