diff --git a/.gitignore b/.gitignore index cea554d..780906a 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,7 @@ docker/ubkg-api/BUILD BUILD **/__pycache__ + +/tests/*/*.out +/src/cells_index/*.csv +/src/cells_index/*.tsv \ No newline at end of file diff --git a/README.md b/README.md index a64d227..ecc3a5a 100644 --- a/README.md +++ b/README.md @@ -55,13 +55,17 @@ 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 instance of the ubkg-api. This is possible by doing the following: -1. Check out a branch of ubkg-api. -2. Configure the local branch of ubkg-api, similarly to the local instance of hs-ontology-api. -3. Start the local instance of ubkg-api. -4. In the virtual environment for hs-ontology-api, install the local instance of ubkg-api using pip with the **-e** flag. This will override the pointer to the ubkg-api package. - -``pip install -e path/to/local/ubkg/repo`` +need to work with a local or branch instance of the ubkg-api. This is possible by doing the following: +1. If your working ubkg-api instance has been committed to a branch, you can point to the branch instance in requirements.txt with a command such as ``git+https://github.com/x-atlas-consortia/ubkg-api.git@<YOUR BRANCH>`` +2. Check out a branch of ubkg-api. +2. Configure the app.cfg file of the local branch of ubkg-api to connect to the appropriate UBKG instance. +3. In the virtual environment for hs-ontology-api, install an editable local instance of ubkg-api. Two ways to do this: + a. ``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. +4. Because ubkg-api has a PyPI TOML file, any of the aforementioned commands will compile a local package and override the pointer to the ubkg-api package. ## Connecting to the local instance of hs-ontology-api 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`: diff --git a/VERSION b/VERSION index 079d7f6..359a5b9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.4.11 +2.0.0 \ No newline at end of file diff --git a/hs-ontology-api-spec.yaml b/hs-ontology-api-spec.yaml index 91cb4f6..1bea432 100644 --- a/hs-ontology-api-spec.yaml +++ b/hs-ontology-api-spec.yaml @@ -2,7 +2,7 @@ openapi: 3.0.3 info: title: HubMAP/SenNet Ontology API (hs-ontology-api) description: The HuBMAP/SenNet Ontology API contains endpoints for querying a [UBKG](https://ubkg.docs.xconsortia.org/) instance with content from the [HuBMAP/SenNet context](https://ubkg.docs.xconsortia.org/contexts/#hubmapsennet-context). The hs-ontology-api imports the [ubkg-api](https://smart-api.info/ui/96e5b5c0b0efeef5b93ea98ac2794837), which encapsulates both basic connectivity to a UBKG instance and generic endpoint code. - version: 1.4.11 + version: 2.0.0 contact: name: GitHub repository url: https://github.com/x-atlas-consortia/hs-ontology-api @@ -1894,4 +1894,4 @@ components: schema: type: string description: name of schema - example: imc3d \ No newline at end of file + example: imc3d diff --git a/src/hs_ontology_api/cypher/celltypedetail.cypher b/src/hs_ontology_api/cypher/celltypedetail.cypher index 765528e..b9ed0a8 100644 --- a/src/hs_ontology_api/cypher/celltypedetail.cypher +++ b/src/hs_ontology_api/cypher/celltypedetail.cypher @@ -12,8 +12,9 @@ CALL // The calling function in neo4j_logic.py will replace $ids. WITH [$ids] AS ids -OPTIONAL MATCH (pCL:Concept)-[:CODE]->(cCL:Code) WHERE cCL.SAB='CL' AND CASE WHEN ids[0]<>'' THEN ANY(id in ids WHERE cCL.CODE=id) ELSE 1=1 END RETURN DISTINCT pCL.CUI AS CLCUI -} +// APRIL 2024 Bug fix to use CodeID instead of CODE for cases of leading zeroes in strings. +OPTIONAL MATCH (pCL:Concept)-[:CODE]->(cCL:Code) +WHERE CASE WHEN ids[0]<>'' THEN ANY(id in ids WHERE cCL.CodeID='CL:'+id) ELSE 1=1 END RETURN DISTINCT pCL.CUI AS CLCUI} CALL { @@ -54,13 +55,16 @@ ORDER BY CLID UNION //CL-HGNC mappings via HRA +// APRIL 2024 - HRA changed "has_marker_component" to "characterized_by" //HGNC ID WITH CLCUI -OPTIONAL MATCH (cCL:Code)<-[:CODE]-(pCL:Concept)-[:has_marker_component]->(pGene:Concept)-[:CODE]->(cGene:Code)-[r]->(tGene:Term) +OPTIONAL MATCH (cCL:Code)<-[:CODE]-(pCL:Concept)-[:characterized_by]->(pGene:Concept)-[:CODE]->(cGene:Code)-[r]->(tGene:Term) WHERE pCL.CUI=CLCUI AND cGene.SAB='HGNC' AND r.CUI=pGene.CUI AND cCL.SAB='CL' AND type(r) IN ['ACR','PT'] -RETURN distinct cCL.CodeID as CLID, 'cell_types_genes' as ret_key, cGene.CodeID + '|' + apoc.text.join(COLLECT(tGene.name),'|') AS ret_value -ORDER BY CLID, cGene.CodeID + '|' + apoc.text.join(COLLECT(tGene.name),'|') +WITH COLLECT(tGene.name) AS tgene_names, cGene.CodeID AS cgene_codeid, cCL.CodeID AS ccl_codeid +WITH distinct ccl_codeid AS CLID, 'cell_types_genes' AS ret_key, cgene_codeid+'|'+apoc.text.join(tgene_names,'|') AS ret_value +RETURN CLID, ret_key, ret_value +ORDER BY CLID, ret_value UNION @@ -110,4 +114,4 @@ map['cell_types_definition'] AS cell_types_definition, map['cell_types_genes'] AS cell_types_genes, map['cell_types_organ'] AS cell_types_organs -order by CLID \ No newline at end of file +order by CLID diff --git a/src/hs_ontology_api/cypher/genedetail.cypher b/src/hs_ontology_api/cypher/genedetail.cypher index e25e6f3..d45fc4b 100644 --- a/src/hs_ontology_api/cypher/genedetail.cypher +++ b/src/hs_ontology_api/cypher/genedetail.cypher @@ -74,8 +74,9 @@ ORDER BY hgnc_id,ret_key UNION //Cell types - CL Codes +// APRIL 2024 - HRA changed "has_marker_component" to "characterized_by" WITH GeneCUI -OPTIONAL MATCH (cGene:Code)<-[:CODE]-(pGene:Concept)-[:inverse_has_marker_component]->(pCL:Concept)-[:CODE]->(cCL:Code)-[rCL]->(tCL:Term) WHERE pGene.CUI=GeneCUI AND cGene.SAB='HGNC' AND cCL.SAB='CL' AND rCL.CUI=pCL.CUI RETURN toInteger(cGene.CODE) AS hgnc_id, 'cell_types_code' AS ret_key, cCL.CodeID AS ret_value +OPTIONAL MATCH (cGene:Code)<-[:CODE]-(pGene:Concept)-[:inverse_characterized_by]->(pCL:Concept)-[:CODE]->(cCL:Code)-[rCL]->(tCL:Term) WHERE pGene.CUI=GeneCUI AND cGene.SAB='HGNC' AND cCL.SAB='CL' AND rCL.CUI=pCL.CUI RETURN toInteger(cGene.CODE) AS hgnc_id, 'cell_types_code' AS ret_key, cCL.CodeID AS ret_value ORDER BY hgnc_id,ret_key,ret_value UNION @@ -87,27 +88,34 @@ UNION // The preferred term will be the term of type PT; if there is no PT, then any of the others of type PT_SAB will do. // First, order the preferred terms by whether they are the PT or a PT_SAB. +// APRIL 2024 - HRA changed the label from "has_marker_component" to "characterized_by" WITH GeneCUI CALL{ WITH GeneCUI -OPTIONAL MATCH (cGene:Code)<-[:CODE]-(pGene:Concept)-[:inverse_has_marker_component]->(pCL:Concept)-[:CODE]->(cCL:Code)-[rCL]->(tCL:Term) WHERE pGene.CUI=GeneCUI AND cGene.SAB='HGNC' AND cCL.SAB='CL' AND rCL.CUI=pCL.CUI AND type(rCL) STARTS WITH 'PT' RETURN toInteger(cGene.CODE) AS hgnc_id, cCL.CodeID AS CLID, MIN(CASE WHEN type(rCL)='PT' THEN 0 ELSE 1 END) AS mintype order by hgnc_id,CLID,mintype +OPTIONAL MATCH (cGene:Code)<-[:CODE]-(pGene:Concept)-[:inverse_characterized_by]->(pCL:Concept)-[:CODE]->(cCL:Code)-[rCL]->(tCL:Term) WHERE pGene.CUI=GeneCUI AND cGene.SAB='HGNC' AND cCL.SAB='CL' AND rCL.CUI=pCL.CUI AND type(rCL) STARTS WITH 'PT' RETURN toInteger(cGene.CODE) AS hgnc_id, cCL.CodeID AS CLID, MIN(CASE WHEN type(rCL)='PT' THEN 0 ELSE 1 END) AS mintype order by hgnc_id,CLID,mintype } // Next, filter to either the PT or one of the PT_SABs. +// MARCH 2024 - WITH used in return to upgrade to v5 Cypher. WITH hgnc_id, CLID, mintype OPTIONAL MATCH (cCL:Code)-[rCL]->(tCL:Term) where cCL.CodeID = CLID AND type(rCL) STARTS WITH 'PT' AND CASE WHEN type(rCL)='PT' THEN 0 ELSE 1 END=mintype -return hgnc_id, 'cell_types_name' AS ret_key, CLID +'|'+ CASE WHEN tCL.name IS NULL THEN '' ELSE tCL.name END AS ret_value +WITH hgnc_id, 'cell_types_name' AS ret_key, CLID +'|'+ CASE WHEN tCL.name IS NULL THEN '' ELSE tCL.name END AS ret_value +RETURN hgnc_id, ret_key, ret_value UNION // Cell types - CL code|definition // Definitions link to Concepts and multiple CL codes can match to the same concept; however, each CL code has a "preferred" CUI, identified by the CUI property of the relationship of any of the code's linked terms. +// MARCH 2024 - final WITH added to work with v5 Cypher +// APRIL 2024 - HRA changed "has_marker_component" to "characterized_by" WITH GeneCUI -OPTIONAL MATCH (cGene:Code)<-[:CODE]-(pGene:Concept)-[:inverse_has_marker_component]->(pCL:Concept)-[:CODE]->(cCL:Code)-[rCL]->(tCL:Term),(pCL:Concept)-[:DEF]->(dCL:Definition) WHERE rCL.CUI=pCL.CUI AND pGene.CUI=GeneCUI AND cGene.SAB='HGNC' AND cCL.SAB='CL' AND dCL.SAB='CL' RETURN DISTINCT toInteger(cGene.CODE) AS hgnc_id,'cell_types_definition' as ret_key, cCL.CodeID + '|'+ dCL.DEF as ret_value -ORDER BY hgnc_id,cCL.CodeID + '|'+ dCL.DEF +OPTIONAL MATCH (cGene:Code)<-[:CODE]-(pGene:Concept)-[:inverse_characterized_by]->(pCL:Concept)-[:CODE]->(cCL:Code)-[rCL]->(tCL:Term),(pCL:Concept)-[:DEF]->(dCL:Definition) WHERE rCL.CUI=pCL.CUI AND pGene.CUI=GeneCUI AND cGene.SAB='HGNC' AND cCL.SAB='CL' AND dCL.SAB='CL' +WITH toInteger(cGene.CODE) AS hgnc_id,'cell_types_definition' as ret_key, cCL.CodeID + '|'+ dCL.DEF as ret_value +RETURN DISTINCT hgnc_id, ret_key, ret_value +ORDER BY hgnc_id, ret_value UNION @@ -118,32 +126,36 @@ UNION // 3. Assigns UBERON codes as cross-references to AZ organ codes. // // To get organ information, map gene to cell type to organ location. +// APRIL 2024 - HRA changed "has_marker_component" to "characterized_by" WITH GeneCUI //First, get Azimuth Codes that are cross-referenced to CL codes. For the case of a CL code being cross-referenced to multiple AZ codes, only one AZ code gets the "preferred" cross-reference to the CL code; however, all AZ codes have a cross-reference to the CL code, so do not check on rAZ.CUI=pCL.CUI. CALL {WITH GeneCUI -OPTIONAL MATCH (cGene:Code)<-[:CODE]-(pGene:Concept)-[:inverse_has_marker_component]->(pCL:Concept)-[:CODE]->(cCL:Code)-[rCL]->(tCL:Term), (pCL:Concept)-[:CODE]->(cAZ:Code)-[rAZ]->(tAZ:Term) WHERE rCL.CUI=pCL.CUI AND pGene.CUI=GeneCUI AND cGene.SAB='HGNC' AND cCL.SAB='CL' AND cAZ.SAB='AZ' RETURN DISTINCT toInteger(cGene.CODE) AS hgnc_id,cCL.CodeID as CLID,cAZ.CodeID AS AZID} +OPTIONAL MATCH (cGene:Code)<-[:CODE]-(pGene:Concept)-[:inverse_characterized_by]->(pCL:Concept)-[:CODE]->(cCL:Code)-[rCL]->(tCL:Term), (pCL:Concept)-[:CODE]->(cAZ:Code)-[rAZ]->(tAZ:Term) WHERE rCL.CUI=pCL.CUI AND pGene.CUI=GeneCUI AND cGene.SAB='HGNC' AND cCL.SAB='CL' AND cAZ.SAB='AZ' RETURN DISTINCT toInteger(cGene.CODE) AS hgnc_id,cCL.CodeID as CLID,cAZ.CodeID AS AZID} //Use the AZ codes to map to concepts that have located_in relationships with AZ organ codes. The AZ organ codes are cross-referenced to UBERON codes. Limit the located_in relationships to those from AZ. CALL {WITH AZID OPTIONAL MATCH (cAZ:Code)<-[:CODE]-(pAZ:Concept)-[rAZUB:located_in]->(pUB:Concept)-[:CODE]->(cUB:Code)-[rUB:PT]->(tUB:Term) WHERE rAZUB.SAB='AZ' AND rUB.CUI=pUB.CUI AND cAZ.CodeID=AZID AND cUB.SAB='UBERON' RETURN cUB.CodeID+'*'+ tUB.name + '' as UBERONID } -WITH hgnc_id, CLID,UBERONID -RETURN DISTINCT hgnc_id,'cell_types_organ' as ret_key, CLID+ '|' + apoc.text.join(COLLECT(DISTINCT UBERONID),",") AS ret_value -ORDER BY hgnc_id, CLID+ '|' + apoc.text.join(COLLECT(DISTINCT UBERONID),",") +WITH hgnc_id, 'cell_types_organ' as ret_key, CLID,UBERONID, CLID+ '|' + apoc.text.join(COLLECT(DISTINCT UBERONID),",") AS ret_value +RETURN DISTINCT hgnc_id, ret_key, ret_value +ORDER BY hgnc_id, ret_value // Indicate the source of cell type information. +// APRIL 2024 - HRA changed "has_marker_component" to "characterized_by" UNION WITH GeneCUI -OPTIONAL MATCH (cGene:Code)<-[:CODE]-(pGene:Concept)-[:inverse_has_marker_component]->(pCL:Concept)-[:CODE]->(cCL:Code)-[rCL]->(tCL:Term) WHERE rCL.CUI=pCL.CUI AND pGene.CUI=GeneCUI AND cGene.SAB='HGNC' AND cCL.SAB='CL' RETURN DISTINCT toInteger(cGene.CODE) AS hgnc_id,'cell_types_source' as ret_key, cCL.CodeID + '|Human Reference Atlas' as ret_value +OPTIONAL MATCH (cGene:Code)<-[:CODE]-(pGene:Concept)-[:inverse_characterized_by]->(pCL:Concept)-[:CODE]->(cCL:Code)-[rCL]->(tCL:Term) WHERE rCL.CUI=pCL.CUI AND pGene.CUI=GeneCUI AND cGene.SAB='HGNC' AND cCL.SAB='CL' RETURN DISTINCT toInteger(cGene.CODE) AS hgnc_id,'cell_types_source' as ret_key, cCL.CodeID + '|Human Reference Atlas' as ret_value ORDER BY hgnc_id,cCL.CodeID + '|Human Reference Atlas' } +// APRIL 2024 bug fix check for null gene before calling fromlists + WITH hgnc_id, ret_key, COLLECT(ret_value) AS values -WITH hgnc_id,apoc.map.fromLists(COLLECT(ret_key),COLLECT(values)) AS map WHERE hgnc_id IS NOT NULL +WITH hgnc_id,apoc.map.fromLists(COLLECT(ret_key),COLLECT(values)) AS map RETURN hgnc_id, map['approved_symbol'] AS approved_symbol, map['approved_name'] AS approved_name, @@ -159,4 +171,4 @@ map['cell_types_definition'] AS cell_types_code_definition, map['cell_types_organ'] AS cell_types_codes_organ, map['cell_types_source'] AS cell_types_codes_source -order by hgnc_id \ No newline at end of file +order by hgnc_id diff --git a/src/main.py b/src/main.py index 91256cc..6670d07 100755 --- a/src/main.py +++ b/src/main.py @@ -38,7 +38,8 @@ def make_flask_config(): return temp_flask_app.config -app = UbkgAPI(make_flask_config()).app +app = UbkgAPI(make_flask_config(), Path(__file__).absolute().parent.parent).app + app.register_blueprint(assaytype_blueprint) app.register_blueprint(assayname_blueprint) app.register_blueprint(datasets_blueprint) @@ -68,22 +69,6 @@ def make_flask_config(): app.cells_client = OntologyCellsClient(cellsurl) -# Defining the /status endpoint in the ubkg_api package will cause 500 error -# Because the VERSION and BUILD files are not built into the package -@app.route('/status', methods=['GET']) -def api_status(): - status_data = { - # Use strip() to remove leading and trailing spaces, newlines, and tabs - 'version': (Path(__file__).absolute().parent.parent / 'VERSION').read_text().strip(), - 'build': (Path(__file__).absolute().parent.parent / 'BUILD').read_text().strip(), - 'neo4j_connection': False - } - is_connected = current_app.neo4jConnectionHelper.check_connection() - if is_connected: - status_data['neo4j_connection'] = True - - return jsonify(status_data) - #################################################################################################### ## For local development/testing #################################################################################################### diff --git a/src/requirements.txt b/src/requirements.txt index 2a669e4..d013d6e 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -1,6 +1,6 @@ -ubkg-api==1.4.0 -Flask == 2.1.3 -neo4j == 4.4 +ubkg-api==2.1.1 +Flask==2.1.3 +neo4j==5.15.0 # for analysis of tabular data pandas==1.5.0 @@ -12,4 +12,9 @@ numpy==1.23.5 Werkzeug==2.3.7 # Cells API client -hubmap-api-py-client==0.0.9 \ No newline at end of file +hubmap-api-py-client==0.0.9 + +# Test and analysis scripts +argparse==1.4.0 +datatest==0.11.1 +deepdiff==6.7.1 diff --git a/src/uwsgi.ini b/src/uwsgi.ini index 1a3ca51..15854af 100644 --- a/src/uwsgi.ini +++ b/src/uwsgi.ini @@ -9,9 +9,12 @@ module = wsgi:application # Send logs to stdout instead of file so docker picks it up and writes to AWS CloudWatch log-master=true -# Master with 2 worker process (based on CPU number) +# Master with 4 worker process (based on CPU number) master = true -processes = 2 +processes = 4 + +# Enable multithreading +enable-threads = true # Use http socket for integration with nginx running on the same machine socket = localhost:5000 diff --git a/test/compare_responses.py b/test/compare_responses.py new file mode 100755 index 0000000..7fb3b31 --- /dev/null +++ b/test/compare_responses.py @@ -0,0 +1,195 @@ +#!/usr/bin/env python3 + +import argparse +import requests +import json +from datatest import validate, ValidationError +# https://zepworks.com/deepdiff/6.7.1/ +from deepdiff import DeepDiff +import pprint + + +UBKG_URL_PROD = 'https://ontology.api.hubmapconsortium.org' +UBKG_URL_DEV = 'https://ontology-api.dev.hubmapconsortium.org' +UBKG_URL_TEST = 'https://ontology-api.test.hubmapconsortium.org' + +URL_LOCAL = 'http://127.0.0.1:5002' + +DIFFERENCES_STATUS_CODE = 0 +DIFFERENCES_RESPONSE = 0 +ENDPOINTS_PROCESSED = 0 +STATUS_CODE_500_HOST_A = 0 +STATUS_CODE_500_HOST_B = 0 + + +class RawTextArgumentDefaultsHelpFormatter( + argparse.ArgumentDefaultsHelpFormatter, + argparse.RawTextHelpFormatter +): + pass + + +# https://docs.python.org/3/howto/argparse.html +parser = argparse.ArgumentParser( + description=""" + Check for differences between endpoints from two UBKG micro-services. + + For example executing: + % src/compare_responses.py https://ontology.api.hubmapconsortium.org http://127.0.0.1:5002 -d + Will compare the results from microservice on ontology.api.hubmapconsortium.org to the + one running on localhost using deepdiff to do the comparison of the output. + + Without using one of '-t', '-d', or '-V' only endpoint status differences will be flagged. + + For documentation on DeepDiff see: https://zepworks.com/deepdiff/6.7.1/diff.html + """, + formatter_class=RawTextArgumentDefaultsHelpFormatter) +parser.add_argument('host_a', type=str, default=UBKG_URL_PROD, + help='host to be compared too (expected)') +parser.add_argument('host_b', type=str, default=URL_LOCAL, + help='host to check for differences') +parser.add_argument("-t", "--textual", action="store_true", + help='show textual differences') +parser.add_argument("-d", "--deepdiff", action="store_true", + help='show differences using DeepDiff') +parser.add_argument("-V", "--validate", action="store_true", + help='show differences using Validate') +parser.add_argument("-v", "--verbose", action="store_true", + help='increase output verbosity') + +args = parser.parse_args() + + +def endpoint_get_diff(host_a: str, host_b: str, path) -> None: + resp_a = requests.get(f"{host_a}{path}") + resp_b = requests.get(f"{host_b}{path}") + resp_diff(host_a, host_b, path, resp_a, resp_b) + + +def endpoint_post_diff(host_a: str, host_b: str, path: str, data: dict) -> None: + resp_a = requests.post(f"{host_a}{path}", json=data) + resp_b = requests.post(f"{host_b}{path}", json=data) + resp_diff(host_a, host_b, path, resp_a, resp_b) + + +def resp_diff(host_a: str, host_b: str, path: str, resp_a, resp_b) -> None: + global DIFFERENCES_STATUS_CODE, DIFFERENCES_RESPONSE, ENDPOINTS_PROCESSED + global STATUS_CODE_500_HOST_A, STATUS_CODE_500_HOST_B + print(f"Checking path: {path} ", end='') + ENDPOINTS_PROCESSED += 1 + if resp_a.status_code == 500: + STATUS_CODE_500_HOST_A += 1 + if resp_b.status_code == 500: + STATUS_CODE_500_HOST_B += 1 + if resp_a.status_code != resp_b.status_code: + print(f"\nSTATUS CODES DIFFER: {host_a}:{resp_a.status_code}; {host_b}:{resp_b.status_code}") + DIFFERENCES_STATUS_CODE += 1 + return + if resp_a.text != resp_b.text: + DIFFERENCES_RESPONSE += 1 + if args.textual is True: + print(f"\nDIFFERENCES (Textual): {host_a}:{resp_a.text}; {host_b}:{resp_b.text}") + resp_a_dict: dict = json.loads(resp_a.text) + resp_b_dict: dict = json.loads(resp_b.text) + if args.deepdiff is True: + # https://zepworks.com/deepdiff/6.7.1/diff.html + deepdiff = DeepDiff(resp_b_dict, resp_a_dict, + ignore_order=True, ignore_string_case=True, + report_repetition=True, verbose_level=2 + ) + print('DIFFERENCE (DeepDiff):') + pp = pprint.PrettyPrinter(indent=4) + pp.pprint(deepdiff) + if args.validate is True: + try: + validate(resp_b_dict, resp_a_dict) + except ValidationError as ve: + # https://datatest.readthedocs.io/en/stable/reference/datatest-core.html#datatest.ValidationError + print('DIFFERENCES (Validate):') + print(f"Expected (host_a): {ve.differences[0].expected}") + print(f"Found (host_b): {ve.differences[0].invalid}") + print() + + return + print('OK!') + + +def diff(host_a: str, host_b: str) -> None: + endpoint_post_diff(host_a, host_b, '/assayname', {"name": "bulk-RNA"}) + endpoint_get_diff(host_a, host_b, '/assaytype?application_context=HUBMAP') + endpoint_get_diff(host_a, host_b, '/assaytype/bulk-RNA?application_context=HUBMAP') + endpoint_get_diff(host_a, host_b, '/datasets?application_context=HUBMAP') + endpoint_get_diff(host_a, host_b, '/organs?application_context=HUBMAP') + endpoint_get_diff(host_a, host_b, '/organs?application_context=SENNET') + endpoint_get_diff(host_a, host_b, '/organs/by-code?application_context=HUBMAP') + endpoint_get_diff(host_a, host_b, '/relationships/gene/MMRN1') + endpoint_get_diff(host_a, host_b, '/valueset?child_sabs=OBI&parent_sab=HUBMAP&parent_code=C001000') + endpoint_get_diff(host_a, host_b, '/genes-info?page=1&genes_per_page=3') + endpoint_get_diff(host_a, host_b, '/genes-info?page=last&genes_per_page=3') + endpoint_get_diff(host_a, host_b, '/genes-info?genes_per_page=3&starts_with=B') + endpoint_get_diff(host_a, host_b, '/genes/MMRN1') + endpoint_get_diff(host_a, host_b, '/proteins-info?page=1&proteins_per_page=3') + endpoint_get_diff(host_a, host_b, '/proteins-info?page=last&proteins_per_page=3') + endpoint_get_diff(host_a, host_b, '/proteins-info?proteins_per_page=3&starts_with=B') + endpoint_get_diff(host_a, host_b, '/proteins/MMRN1_HUMAN') + endpoint_get_diff(host_a, host_b, '/celltypes-info?page=1&proteins_per_page=3') + endpoint_get_diff(host_a, host_b, '/celltypes-info?page=last&proteins_per_page=3') + endpoint_get_diff(host_a, host_b, '/celltypes-info?proteins_per_page=3&starts_with=B') + endpoint_get_diff(host_a, host_b, '/celltypes/0002138') + endpoint_get_diff(host_a, host_b, '/valueset?parent_sab=SENNET&parent_code=C020076&child_sabs=SENNET') + endpoint_get_diff(host_a, host_b, '/valueset?parent_sab=SENNET&parent_code=C050020&child_sabs=SENNET') + endpoint_get_diff(host_a, host_b, '/valueset?parent_sab=SENNET&parent_code=C000012&child_sabs=SENNET') + endpoint_get_diff(host_a, host_b, '/field-descriptions') + endpoint_get_diff(host_a, host_b, '/field-descriptions/acquisition_instrument_model') + endpoint_get_diff(host_a, host_b, '/field-descriptions/acquisition_instrument_model?test=X') + endpoint_get_diff(host_a, host_b, '/field-descriptions/acquisition_instrument_model?source=X') + endpoint_get_diff(host_a, host_b, '/field-descriptions/acquisition_instrument_model?source=HMFIELD') + endpoint_get_diff(host_a, host_b, '/field-types') + endpoint_get_diff(host_a, host_b, '/field-types/acquisition_instrument_model') + endpoint_get_diff(host_a, host_b, '/field-types/acquisition_instrument_model?test=x') + endpoint_get_diff(host_a, host_b, '/field-types/acquisition_instrument_model?mapping_source=X') + endpoint_get_diff(host_a, host_b, '/field-types/acquisition_instrument_model?mapping_source=HMFIELD') + endpoint_get_diff(host_a, host_b, '/field-types/acquisition_instrument_model?type_source=X') + endpoint_get_diff(host_a, host_b, '/field-types/acquisition_instrument_model?type_source=HMFIELD') + endpoint_get_diff(host_a, host_b, '/field-types/acquisition_instrument_model?type=X') + endpoint_get_diff(host_a, host_b, '/field-types/acquisition_instrument_model?type=string') + endpoint_get_diff(host_a, host_b, '/field-types-info') + endpoint_get_diff(host_a, host_b, '/field-types-info?test=x') + endpoint_get_diff(host_a, host_b, '/field-types-info?type_source=x') + endpoint_get_diff(host_a, host_b, '/field-types-info?type_source=HMFIELD') + endpoint_get_diff(host_a, host_b, '/field-assays') + endpoint_get_diff(host_a, host_b, '/field-assays/acquisition_instrument_model') + endpoint_get_diff(host_a, host_b, '/field-assays?test=X') + endpoint_get_diff(host_a, host_b, '/field-assays?assay_identifier=X') + endpoint_get_diff(host_a, host_b, '/field-assays?assay_identifier=snRNAseq') + endpoint_get_diff(host_a, host_b, '/field-assays/acquisition_instrument_model?assay_identifier=snRNAseq') + endpoint_get_diff(host_a, host_b, '/field-assays?data_type=X') + endpoint_get_diff(host_a, host_b, '/field-assays?data_type=seqFISH') + endpoint_get_diff(host_a, host_b, '/field-assays/acquisition_instrument_model?data_type=seqFISH') + endpoint_get_diff(host_a, host_b, '/field-assays?dataset_type=x') + endpoint_get_diff(host_a, host_b, '/field-assays?dataset_type=RNAseq') + endpoint_get_diff(host_a, host_b, '/field-assays/acquisition_instrument_model?dataset_type=RNAseq') + endpoint_get_diff(host_a, host_b, '/field-entities') + endpoint_get_diff(host_a, host_b, '/field-entities?test=X') + endpoint_get_diff(host_a, host_b, '/field-entities/acquisition_instrument_model') + endpoint_get_diff(host_a, host_b, '/field-entities/acquisition_instrument_model?source=x') + endpoint_get_diff(host_a, host_b, '/field-entities/acquisition_instrument_model?source=HMFIELD') + endpoint_get_diff(host_a, host_b, '/field-entities/acquisition_instrument_model?entity=x') + endpoint_get_diff(host_a, host_b, '/field-entities/acquisition_instrument_model?entity=dataset') + endpoint_get_diff(host_a, host_b, '/field-schemas') + endpoint_get_diff(host_a, host_b, '/field-schemas?test=x') + endpoint_get_diff(host_a, host_b, '/field-schemas/acquisition_instrument_model') + endpoint_get_diff(host_a, host_b, '/field-schemas?source=x') + endpoint_get_diff(host_a, host_b, '/field-schemas?source=HMFIELD') + endpoint_get_diff(host_a, host_b, '/field-schemas?schema=x') + endpoint_get_diff(host_a, host_b, '/field-schemas?schema=imc3d') + + +diff(args.host_a, args.host_b) +print(f"\nEndpoints processed: {ENDPOINTS_PROCESSED}; " + f"Response Differences: {DIFFERENCES_RESPONSE}" + f"\nStatus Code Differences: {DIFFERENCES_STATUS_CODE}; " + f"Status Codes 500 {args.host_a}: {STATUS_CODE_500_HOST_A}; " + f"Status Codes 500 {args.host_b}: {STATUS_CODE_500_HOST_B} " + ) +print("\nDone!") diff --git a/test/test_api.sh b/test/test_api.sh new file mode 100755 index 0000000..42f18c8 --- /dev/null +++ b/test/test_api.sh @@ -0,0 +1,705 @@ +#!/bin/bash +########## +# Test script for UBKG API +########## + + +set -e +set -u +########### +# Help function +########## +Help() +{ + # Display Help + echo "" + echo "****************************************" + echo "HELP: UBKG API test script" + echo + echo "Syntax: ./test_api.sh [-option]..." + echo "option" + echo "-v test environment: l (local), d (DEV), or p (PROD)" +} + +##### +# Get options +while getopts ":hv:" option; do + case $option in + h) # display Help + Help + exit;; + v) # environment + env=$OPTARG;; + \?) # Invalid option + echo "Error: Invalid option" + exit;; + esac +done + +# Environment URLs. +UBKG_URL_PROD=https://ontology.api.hubmapconsortium.org +UBKG_URL_DEV=https://ontology-api.dev.hubmapconsortium.org +UBKG_URL_LOCAL=http://127.0.0.1:5002 + +# Map to selected API environment. +case "$env" in + l) # local + UBKG_URL="${UBKG_URL:-$UBKG_URL_LOCAL}";; + d) # DEV + UBKG_URL="${UBKG_URL:-$UBKG_URL_DEV}";; + p) # PROD + UBKG_URL="${UBKG_URL:-$UBKG_URL_PROD}";; + \?) # default to local machine + UBKG_URL="${UBKG_URL:-$UBKG_URL_LOCAL}";; + +esac + +echo "Using UBKG at: ${UBKG_URL}" | tee test.out +echo "For these tests, only first 60 characters of output from HTTP 200 returns displayed." | tee -a test.out +echo "To review response bodies in detail, call endpoints individually." | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +# $ ./test_api.sh +# Using UBKG at: https://ontology-api.dev.hubmapconsortium.org +# $ (export UBKG_URL=http://127.0.0.1:5002; ./test_api.sh) +# Using UBKG at: http://127.0.0.1:5002 + +#-------------------------------------------- +echo "TESTS FOR: assayname POST" | tee -a test.out +echo "SIGNATURE: /assayname" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "/assayname_POST with bulk-RNA => should return 200" | tee -a test.out +curl --request POST \ + --url "${UBKG_URL}/assayname" \ + --header "Content-Type: application/json" \ + --data '{"name": "bulk-RNA"}' | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "TESTS FOR: assaytypes GET" | tee -a test.out +echo "SIGNATURE: /assaytypes?application_context=<context>" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out +echo "/assaytype?application_context=HUBMAP GET => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/assaytype?application_context=HUBMAP" \ + --header "Accept: application/json"| cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + + +echo "TESTS FOR: assaytypes GET" | tee -a test.out +echo "SIGNATURE: /assaytypes/<data_type name>?application_context=<context>" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out +echo "/assaytypes/bulk-RNA?application_context=HUBMAP => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/assaytype/bulk-RNA?application_context=HUBMAP" \ + --header "Accept: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "TESTS FOR: datasets GET" | tee -a test.out +echo "SIGNATURE: /datasets?application_context=<context>&data_type=<data_type>&description=<description>&alt_name=<alt_name>&primary=<true or false>&contains_pii=<true or false>&vis_only=<true or false>&vitessce_hint=<hint>&dataset_provider=<provider>" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out +echo "/datasets?application_context=HUBMAP => should return 200" | tee -a test.out + +curl --request GET \ + --url "${UBKG_URL}/datasets?application_context=HUBMAP" \ + --header "Accept: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + + +echo "TESTS FOR: organs GET" | tee -a test.out +echo "SIGNATURE: /organs?application_context=<context>" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "1. /organs => missing application context; should return custom 400" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/organs" \ + --header "Accept: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "2. /organs?application_context=HUBMAP => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/organs?application_context=HUBMAP" \ + --header "Accept: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "3. /organs?application_context=SENNET => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/organs?application_context=SENNET" \ + --header "Accept: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "TESTS FOR: organs/by-code GET" | tee -a test.out +echo "SIGNATURE: /organs/by-code?application_context=<context>" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "/organs/by-code?application_context=HUBMAP => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/organs/by-code?application_context=HUBMAP" \ + --header "Accept: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "TESTS FOR: relationships/gene GET" | tee -a test.out +echo "SIGNATURE: /relationships/gene/<HGNC symbol>" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "relationships/gene/MMRN1 => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/relationships/gene/MMRN1" \ + --header "Content-Type: application/json"| cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "TESTS FOR: valueset GET" | tee -a test.out +echo "SIGNATURE: /valueset?child_sabs=<list of sabs.&parent_sab=<sab>&parent_code=<code>" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "/valueset?child_sabs=OBI&parent_sab=HUBMAP&parent_code=C001000 => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/valueset?child_sabs=OBI&parent_sab=HUBMAP&parent_code=C001000" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "TESTS FOR: genes-info GET" | tee -a test.out +echo "SIGNATURE: /gene_list?page=<page>&genes_per_page=<number>&starts_with=<characters>" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +# Test for genes-info endpoint +echo "1. /genes-info?page=1&genes_per_page=3 => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/genes-info?page=1&genes_per_page=3" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "2. /genes-info?page=last&genes_per_page=3 => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/genes-info?page=last&genes_per_page=3" \ + --header "Content-Type: application/json"| cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "3. /genes-info?genes_per_page=3&starts_with=B => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/genes-info?genes_per_page=3&starts_with=B" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +# Test for genes endpoint. +echo "TESTS FOR: genes GET" | tee -a test.out +echo "SIGNATURE: /genes/<HGNC symbol>" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "/genes/MMRN1 => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/genes/MMRN1" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +# Test for proteins-info endpoint. +echo "TESTS FOR: proteins-info GET" | tee -a test.out +echo "SIGNATURE: /proteins_info?page=<page>&proteins_per_page=<number>&starts_with=<characters>" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "1. /proteins-info?page=1&proteins_per_page=3 => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/proteins-info?page=1&proteins_per_page=3" \ + --header "Content-Type: application/json"| cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "2. /proteins-info?page=last&proteins_per_page=3 => should return 200" | tee -a test.out +echo "SHOULD RETURN 200" +curl --request GET \ + --url "${UBKG_URL}/proteins-info?page=last&proteins_per_page=3" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "3. /proteins-info?proteins_per_page=3&starts_with=B => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/proteins-info?proteins_per_page=3&starts_with=B" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "TESTS FOR: proteins GET" | tee -a test.out +echo "SIGNATURE: /genes/<UniProtKB symbol>" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +# Test for proteins endpoint. +echo "/proteins/MMRN1_HUMAN => should return 200" | tee -a test.out +echo "SHOULD RETURN 200" +curl --request GET \ + --url "${UBKG_URL}/proteins/MMRN1_HUMAN" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "TESTS FOR: celltypes-info GET" | tee -a test.out +echo "SIGNATURE: /celltypesinfo?page=<page>&cell_types_per_page=<number>&starts_with=<characters>" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "1. /celltypes-info?page=1&proteins_per_page=3 => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/celltypes-info?page=1&proteins_per_page=3" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "2. /celltypes-info?page=last&proteins_per_page=3 => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/celltypes-info?page=last&proteins_per_page=3" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "2. /celltypes-info?proteins_per_page=3&starts_with=B => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/celltypes-info?proteins_per_page=3&starts_with=B" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +# Test for celltypes endpoint. +echo "TESTS FOR: celltypes GET" | tee -a test.out +echo "SIGNATURE: /celltypesinfo/<CL code>" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "/celltypes/0002138 => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/celltypes/0002138" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "TEST FOR: SENNET source types" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/valueset?parent_sab=SENNET&parent_code=C020076&child_sabs=SENNET" \ + --header "Content-Type: application/json" | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "TEST FOR: SENNET sample categories" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/valueset?parent_sab=SENNET&parent_code=C050020&child_sabs=SENNET" \ + --header "Content-Type: application/json" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "TEST FOR: SENNET entities" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/valueset?parent_sab=SENNET&parent_code=C000012&child_sabs=SENNET" \ + --header "Content-Type: application/json" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "TESTS FOR: field-descriptions GET" | tee -a test.out +echo "SIGNATURE: /field-descriptions/<field name>?source=<source?>" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "1./ field-descriptions => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-descriptions" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "2. /field-descriptions/acquisition_instrument_model => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-descriptions/acquisition_instrument_model" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "3. /field-descriptions/acquisition_instrument_model?test=X => invalid parameter; should return custom 400" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-descriptions/acquisition_instrument_model?test=X" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "4. /field-descriptions/acquisition_instrument_model?source=X => invalid source; should return custom 400" | tee -a test.out +echo "SHOULD RETURN 400; invalid parameter value" +curl --request GET \ + --url "${UBKG_URL}/field-descriptions/acquisition_instrument_model?source=X" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "5. field-descriptions/acquisition_instrument_model?source=HMFIELD = > should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-descriptions/acquisition_instrument_model?source=HMFIELD" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "TESTS FOR: field-types GET" | tee -a test.out +echo "SIGNATURE: /field-types/<field name>?mapping_source=<source>&type_source=<source>&type=<type>" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "1. /field-types => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-types" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "2. /field-types/acquisition_instrument_model => should return 200" | tee -a test.out +echo "SHOULD RETURN 200" +curl --request GET \ + --url "${UBKG_URL}/field-types/acquisition_instrument_model" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "3. field-types/acquisition_instrument_model?test=x => invalid parameter: should return 400" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-types/acquisition_instrument_model?test=x" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "4. field-types/acquisition_instrument_model?mapping_source=X => invalid mapping source; should return 400" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-types/acquisition_instrument_model?mapping_source=X" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "5. field-types/acquisition_instrument_model?mapping_source=HMFIELD => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-types/acquisition_instrument_model?mapping_source=HMFIELD" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "6. field-types/acquisition_instrument_model?type_source=X => invalid type_source; should return 400" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-types/acquisition_instrument_model?type_source=X" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "7. field-types/acquisition_instrument_model?type_source=HMFIELD => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-types/acquisition_instrument_model?type_source=HMFIELD" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "8. field-types/acquisition_instrument_model?type=X => invalid type; should return 404" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-types/acquisition_instrument_model?type=X" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "9. field-types/acquisition_instrument_model?type=string => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-types/acquisition_instrument_model?type=string" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "TESTS FOR: field-types-info GET" | tee -a test.out +echo "SIGNATURE: /field-types-info?type_source=<source>" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "1. /field-types-info => SHOULD RETURN 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-types-info" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "2. /field-types-info?test=x => invalid parameter; should return 400" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-types-info?test=x" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "3. /field-types-info?type_source=x => invalid type_source; should return 400" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-types-info?type_source=x" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "4. field-types-info?type_source=HMFIELD => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-types-info?type_source=HMFIELD" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "TESTS FOR: field-assays GET" | tee -a test.out +echo "SIGNATURE: /field-assays/<assay_identifier>?data_type=<data_type>&dataset_type=<dataset_type" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "1. /field-assays => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-assays" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "2. /field-assays/acquisition_instrument_model => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-assays/acquisition_instrument_model" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "3. field-assays/acquisition_instrument_model?test=X => invalid parameter; should return 400" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-assays/acquisition_instrument_model?test=X" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "4. /field-assays?assay_identifier=X => no results; should return 404" | tee -a test.out +echo "SHOULD RETURN 404; no results" +curl --request GET \ + --url "${UBKG_URL}/field-assays?assay_identifier=X" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "5. /field-assays?assay_identifier=snRNAseq => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-assays?assay_identifier=snRNAseq" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "5. /field-assays?data_type=X => no results; should return 404" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-assays?data_type=X" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "6. /field-assays?data_type=seqFISH => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-assays?data_type=seqFISH" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "7. field-assays?dataset_type=X => no results; should return 404" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-assays?dataset_type=x" \ + --header "Content-Type: application/json" +echo + +echo "8. /field-assays?dataset_type=RNAseq => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-assays?dataset_type=RNAseq" \ + --header "Content-Type: application/json"| cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "TESTS FOR: field-entities GET" | tee -a test.out +echo "SIGNATURE: /field-entities/<field_name>?source=<source>&entity=<entity>&application_context=<context>" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "1. /field-entities => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-entities" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "2. /field-entities?test=X => invalid parameter; should return 400" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-entities?test=X" \ + --header "Content-Type: application/json" +echo + +echo "3. /field-entities/acquisition_instrument_model => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-entities/acquisition_instrument_model" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "4. /field-entities/acquisition_instrument_model?source=x => invalid source; should return 400" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-entities/acquisition_instrument_model?source=x" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "5. /field-entities/acquisition_instrument_model?source=HMFIELD => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-entities/acquisition_instrument_model?source=HMFIELD" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "6. /field-entities/acquisition_instrument_model?entity=x => no results; should return 404" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-entities/acquisition_instrument_model?entity=x" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "7. /field-entities/acquisition_instrument_model?entity=dataset => should return 200" +echo "SHOULD RETURN 200" +curl --request GET \ + --url "${UBKG_URL}/field-entities/acquisition_instrument_model?entity=dataset" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "TESTS FOR: field-schemas GET" | tee -a test.out +echo "SIGNATURE: /field-entities/<field_name>?source=<source>&schema=<schema>" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "1. /field-schemas => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-schemas" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "2. /field-schemas?test=x => invalid parameter; should return 400" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-schemas?test=x" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + + +echo "3. /field-schemas/acquisition_instrument_model => should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-schemas/acquisition_instrument_model" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "4. /field-schemas/acquisition_instrument_model?source=x => invalid parameter value; should return 400" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-schemas?source=x" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "5. /field-schemas/acquisition_instrument_model?source=HMFIELD => should return 200" | tee -a test.out +echo "SHOULD RETURN 200" +curl --request GET \ + --url "${UBKG_URL}/field-schemas?source=HMFIELD" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "6. /field-schemas/acquisition_instrument_model?schema=x => no results; should return 404" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/field-schemas?schema=x" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out + +echo "7. /field-schemas/acquisition_instrument_model?schema=imc3d => should return 200" | tee -a test.out +echo "SHOULD RETURN 200" +curl --request GET \ + --url "${UBKG_URL}/field-schemas?schema=imc3d" \ + --header "Content-Type: application/json" | cut -c1-60 | tee -a test.out +echo +echo | tee -a test.out +echo | tee -a test.out +echo