├── .ci ├── business.sh └── dbpedia_music.sh ├── .github └── workflows │ └── build.yaml ├── .gitignore ├── .readthedocs.yml ├── .release-it.json ├── CHANGELOG.md ├── CITATION.cff ├── LICENSE ├── README.md ├── benchmark ├── api_50qps.html ├── api_50qps.png ├── endpoint_50qps.html └── endpoint_50qps.png ├── docs ├── adding_custom_queries.md ├── authentication.md ├── benchmarking.md ├── cache.md ├── config.yaml.sample ├── configuration_file.md ├── examples.md ├── features.md ├── figures │ ├── api_cached.png │ ├── endpoint.png │ ├── firebase.mp4 │ └── oba.svg ├── filtering.md ├── firebase.md ├── index.md ├── mapping.md ├── quickstart.md ├── requirements.txt ├── result.md ├── running_server.md ├── server.md └── test.md ├── examples ├── business │ ├── config.yaml │ ├── ontology.xml │ ├── ontology_hash.xml │ └── ontology_slash.xml ├── dbpedia │ ├── config_full.yaml │ ├── config_music.yaml │ └── ontology.xml ├── example with spaces │ ├── config.yaml │ ├── readme.md │ ├── sd-reduced.ttl │ └── sdm-reduced.ttl ├── games │ ├── config.yaml │ └── vgo.owl ├── jazz │ ├── config.yaml │ └── qm.xml ├── le │ └── config.yaml ├── modelcatalog │ ├── config.yaml │ ├── readme.md │ ├── sd-reduced.ttl │ └── sdm-reduced.ttl ├── modelcatalog_full │ ├── config.yaml │ ├── config_old.yaml │ ├── context.json │ └── custom_queries │ │ ├── custom_configurationsetups.rq │ │ ├── custom_datatransformations.rq │ │ ├── custom_model_index.rq │ │ ├── custom_model_intervetion.rq │ │ ├── custom_model_region.rq │ │ ├── custom_model_standard_variable.rq │ │ ├── custom_modelconfigurations.rq │ │ ├── custom_modelconfigurationsetups.rq │ │ ├── custom_modelconfigurationsetups_variable.rq │ │ └── custom_models_variable.rq ├── opmw │ ├── config.yaml │ └── opmw.owl ├── pplan │ ├── config.yaml │ └── readme.md ├── restrictions │ ├── config.yaml │ ├── example.owl │ └── readme.md ├── saref4agri │ └── config.yaml ├── saref4city │ └── config.yaml ├── software │ └── sd-reduced.ttl ├── testConfig │ ├── config.yaml │ ├── ontology.ttl │ └── sd-reduced.ttl └── wings │ └── config.yaml ├── mkdocs.yml ├── oba.iml ├── pom.xml └── src ├── main ├── java │ └── edu │ │ └── isi │ │ └── oba │ │ ├── Mapper.java │ │ ├── MapperDataProperty.java │ │ ├── MapperObjectProperty.java │ │ ├── MapperOperation.java │ │ ├── MapperSchema.java │ │ ├── Oba.java │ │ ├── ObaManager.java │ │ ├── ObaUtils.java │ │ ├── PathGenerator.java │ │ ├── Query.java │ │ ├── RestrictionVisitor.java │ │ ├── Serializer.java │ │ ├── SerializerPython.java │ │ ├── config │ │ ├── AuthConfig.java │ │ ├── CONFIG_FLAG.java │ │ ├── EndpointConfig.java │ │ ├── FirebaseConfig.java │ │ ├── OntologyConfig.java │ │ ├── Provider.java │ │ ├── RelationConfig.java │ │ └── YamlConfig.java │ │ └── log4j.properties └── resources │ ├── README.md │ ├── logging.properties │ ├── owl2jsonld-0.3.0-SNAPSHOT-standalone.jar │ ├── queries │ ├── README │ ├── get_all.rq │ ├── get_all_search.rq │ ├── get_all_search_user.rq │ ├── get_all_user.rq │ ├── get_one.rq │ └── get_one_user.rq │ ├── servers.zip │ └── servers │ ├── README.txt │ └── python │ ├── .openapi-generator-ignore │ ├── .openapi-generator │ └── template │ │ ├── Dockerfile.mustache │ │ ├── README.mustache │ │ ├── __init__.mustache │ │ ├── __init__model.mustache │ │ ├── __init__test.mustache │ │ ├── __main__.mustache │ │ ├── base_model_.mustache │ │ ├── controller.mustache │ │ ├── controller_test.mustache │ │ ├── dockerignore.mustache │ │ ├── encoder.mustache │ │ ├── git_push.sh.mustache │ │ ├── gitignore.mustache │ │ ├── model.mustache │ │ ├── openapi.mustache │ │ ├── param_type.mustache │ │ ├── requirements.mustache │ │ ├── security_controller_.mustache │ │ ├── setup.mustache │ │ ├── static_files │ │ ├── cached.py │ │ ├── settings │ │ │ ├── __init__.py │ │ │ ├── config.ini │ │ │ └── logging.ini │ │ └── user_controller.py │ │ ├── test-requirements.mustache │ │ ├── tox.mustache │ │ ├── travis.mustache │ │ ├── typing_utils.mustache │ │ └── util.mustache │ └── generate-server.sh └── test ├── config ├── dbpedia.yaml ├── mcat_reduced.yaml ├── missing_file.yaml └── pplan.yaml ├── java └── edu │ └── isi │ └── oba │ ├── MapperTest.java │ ├── ObaUtilsTest.java │ ├── RestrictionsTest.java │ └── config │ ├── ProviderTest.java │ └── YamlConfigTest.java └── resources ├── complex_expr ├── config.yaml └── ontology.owl ├── json_one.json ├── json_three.json ├── json_two.json ├── missing_import ├── config.yaml └── missing_import.ttl ├── modelCat.ttl └── only_classes ├── config.yaml └── o.ttl /.ci/business.sh: -------------------------------------------------------------------------------- 1 | set -xe 2 | 3 | container_name="business" 4 | java -jar target/oba-*-jar-with-dependencies.jar -c examples/business/config.yaml 5 | pushd outputs/BusinessOntology/servers/python 6 | bash generate-server.sh 7 | pushd server 8 | docker build -t openapi_server . 9 | docker run --name ${container_name} -d -p 8081:8080 openapi_server 10 | popd 11 | sleep 10s 12 | curl -X GET "http://0.0.0.0:8081/v1.3.0/bands/Pink_Floyd" -H "accept: application/json" 13 | docker logs ${container_name} 14 | docker rm -f ${container_name} 15 | -------------------------------------------------------------------------------- /.ci/dbpedia_music.sh: -------------------------------------------------------------------------------- 1 | set -xe 2 | 3 | java -jar target/oba-*-jar-with-dependencies.jar -c examples/dbpedia/config_music.yaml 4 | pushd outputs/dbpedia_music/servers/python 5 | bash generate-server.sh 6 | pushd server 7 | docker build -t openapi_server . 8 | docker run --name dbpedia_music -d -p 8080:8080 openapi_server 9 | popd 10 | sleep 10s 11 | curl -X GET "http://0.0.0.0:8080/v1.3.0/bands/Pink_Floyd" -H "accept: application/json" 12 | docker logs dbpedia_music 13 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven 3 | 4 | name: Java CI with Maven 5 | 6 | on: [push, pull_request] 7 | env: 8 | IMAGE_NAME: wings 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-20.04 13 | strategy: 14 | matrix: 15 | java: ["11", "17", "21"] 16 | name: Java ${{ matrix.Java }} 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Setup java 20 | uses: actions/setup-java@v3 21 | with: 22 | distribution: "adopt" 23 | java-version: ${{ matrix.java }} 24 | - name: Build with Maven 25 | run: mvn -B package --file pom.xml 26 | - name: Test with Maven 27 | run: mvn -B test --file pom.xml 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | oba.iml 2 | nbactions.xml 3 | compile.sh 4 | outputs 5 | .idea/ 6 | example.yaml 7 | # Created by https://www.gitignore.io/api/osx,maven,java,linux,intellij 8 | # Edit at https://www.gitignore.io/?templates=osx,maven,java,linux,intellij 9 | 10 | ### Intellij ### 11 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 12 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 13 | # User-specific stuff 14 | .idea/**/workspace.xml 15 | .idea/**/tasks.xml 16 | .idea/**/usage.statistics.xml 17 | .idea/**/dictionaries 18 | .idea/**/shelf 19 | 20 | # Generated files 21 | .idea/**/contentModel.xml 22 | 23 | # Sensitive or high-churn files 24 | .idea/**/dataSources/ 25 | .idea/**/dataSources.ids 26 | .idea/**/dataSources.local.xml 27 | .idea/**/sqlDataSources.xml 28 | .idea/**/dynamic.xml 29 | .idea/**/uiDesigner.xml 30 | .idea/**/dbnavigator.xml 31 | 32 | # Gradle 33 | .idea/**/gradle.xml 34 | .idea/**/libraries 35 | 36 | # Gradle and Maven with auto-import 37 | # When using Gradle or Maven with auto-import, you should exclude module files, 38 | # since they will be recreated, and may cause churn. Uncomment if using 39 | # auto-import. 40 | # .idea/modules.xml 41 | # .idea/*.iml 42 | # .idea/modules 43 | 44 | # Generated files 45 | .vscode/settings.json 46 | 47 | # CMake 48 | cmake-build-*/ 49 | 50 | # Mongo Explorer plugin 51 | .idea/**/mongoSettings.xml 52 | 53 | # File-based project format 54 | *.iws 55 | 56 | # IntelliJ 57 | out/ 58 | 59 | # mpeltonen/sbt-idea plugin 60 | .idea_modules/ 61 | 62 | # JIRA plugin 63 | atlassian-ide-plugin.xml 64 | 65 | # Cursive Clojure plugin 66 | .idea/replstate.xml 67 | 68 | # Crashlytics plugin (for Android Studio and IntelliJ) 69 | com_crashlytics_export_strings.xml 70 | crashlytics.properties 71 | crashlytics-build.properties 72 | fabric.properties 73 | 74 | # Editor-based Rest Client 75 | .idea/httpRequests 76 | 77 | # Android studio 3.1+ serialized cache file 78 | .idea/caches/build_file_checksums.ser 79 | 80 | # JetBrains templates 81 | **___jb_tmp___ 82 | 83 | ### Intellij Patch ### 84 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 85 | 86 | # *.iml 87 | # modules.xml 88 | # .idea/misc.xml 89 | # *.ipr 90 | 91 | # Sonarlint plugin 92 | .idea/sonarlint 93 | 94 | ### Java ### 95 | # Compiled class file 96 | *.class 97 | 98 | # Log file 99 | *.log 100 | 101 | # BlueJ files 102 | *.ctxt 103 | 104 | # Mobile Tools for Java (J2ME) 105 | .mtj.tmp/ 106 | 107 | # Package Files # 108 | *.jar 109 | !src/main/resources/owl2jsonld-0.3.0-SNAPSHOT-standalone.jar 110 | *.war 111 | *.nar 112 | *.ear 113 | *.tar.gz 114 | *.rar 115 | 116 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 117 | hs_err_pid* 118 | 119 | ### Linux ### 120 | *~ 121 | 122 | # temporary files which can be created if a process still has a handle open of a deleted file 123 | .fuse_hidden* 124 | 125 | # KDE directory preferences 126 | .directory 127 | 128 | # Linux trash folder which might appear on any partition or disk 129 | .Trash-* 130 | 131 | # .nfs files are created when an open file is removed but is still being accessed 132 | .nfs* 133 | 134 | ### Maven ### 135 | target/ 136 | pom.xml.tag 137 | pom.xml.releaseBackup 138 | pom.xml.versionsBackup 139 | pom.xml.next 140 | release.properties 141 | dependency-reduced-pom.xml 142 | buildNumber.properties 143 | .mvn/timing.properties 144 | .mvn/wrapper/maven-wrapper.jar 145 | 146 | ### OSX ### 147 | # General 148 | .DS_Store 149 | .AppleDouble 150 | .LSOverride 151 | 152 | # Icon must end with two \r 153 | Icon 154 | 155 | # Thumbnails 156 | ._* 157 | 158 | # Files that might appear in the root of a volume 159 | .DocumentRevisions-V100 160 | .fseventsd 161 | .Spotlight-V100 162 | .TemporaryItems 163 | .Trashes 164 | .VolumeIcon.icns 165 | .com.apple.timemachine.donotpresent 166 | 167 | # Directories potentially created on remote AFP share 168 | .AppleDB 169 | .AppleDesktop 170 | Network Trash Folder 171 | Temporary Items 172 | .apdisk 173 | 174 | # End of https://www.gitignore.io/api/osx,maven,java,linux,intellij 175 | n 176 | *.iml 177 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | # Required 2 | version: 2 3 | 4 | # Build documentation with MkDocs 5 | mkdocs: 6 | configuration: mkdocs.yml 7 | 8 | # Optionally build your docs in additional formats such as PDF and ePub 9 | formats: all 10 | 11 | # Optionally set the version of Python and requirements required to build your docs 12 | python: 13 | version: 3.7 14 | install: 15 | - requirements: docs/requirements.txt 16 | -------------------------------------------------------------------------------- /.release-it.json: -------------------------------------------------------------------------------- 1 | { 2 | "git": { 3 | "changelog": "auto-changelog --stdout --commit-limit false -u --template https://raw.githubusercontent.com/release-it/release-it/master/templates/changelog-compact.hbs", 4 | "requireCleanWorkingDir": false, 5 | "requireUpstream": true, 6 | "addUntrackedFiles": true, 7 | "commit": true, 8 | "commitMessage": "Release ${version}", 9 | "commitArgs": "", 10 | "tag": true, 11 | "tagName": "${version}", 12 | "tagAnnotation": "Release ${version}", 13 | "tagArgs": "", 14 | "push": true, 15 | "pushArgs": "--follow-tags", 16 | "pushRepo": "origin" 17 | }, 18 | "github": { 19 | "release": true 20 | }, 21 | "hooks": { 22 | "after:bump": "mvn --settings pom.xml org.codehaus.mojo:versions-maven-plugin:2.1:set -DnewVersion=${version} 1>/dev/null 2>/dev/null && auto-changelog -v ${version}" 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | title: "OBA: Ontology-Based APIs" 2 | license: Apache-2.0 3 | authors: 4 | - family-names: Osorio 5 | given-names: Maximiliano 6 | orcid: "https://orcid.org/0000-0002-3611-6510" 7 | - family-names: Garijo 8 | given-names: Daniel 9 | orcid: "https://orcid.org/0000-0003-0454-7145" 10 | cff-version: 1.2.0 11 | message: "If you use this software, please cite both the article from preferred-citation and the software itself." 12 | preferred-citation: 13 | authors: 14 | - family-names: Osorio 15 | given-names: Maximiliano 16 | - family-names: Garijo 17 | given-names: Daniel 18 | title: "OBA: An Ontology-Based Framework for Creating REST APIs for Knowledge Graphs" 19 | type: article 20 | year: 2021 21 | doi: 10.1007/978-3-030-62466-8_4 22 | identifiers: 23 | - description: "Collection of archived snapshots for OBA" 24 | type: doi 25 | value: 10.5281/zenodo.6639554 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ontology-Based APIs (OBA) [![Test](https://github.com/KnowledgeCaptureAndDiscovery/OBA/actions/workflows/build.yaml/badge.svg)](https://github.com/KnowledgeCaptureAndDiscovery/OBA/actions/workflows/build.yaml) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.6639554.svg)](https://doi.org/10.5281/zenodo.6639554) 2 | 3 | OBA reads ontologies (OWL) and generates an OpenAPI Specification (OAS). Using this definition, OBA creates a REST API server automatically. 4 | 5 | ![Diagram](docs/figures/oba.svg) 6 | 7 | ## Quickstart 8 | 9 | There are two option to run OBA: 10 | 11 | 1. Download the binary. 12 | 2. Build the binary from the repository. 13 | 14 | ### Pre-requisites 15 | 16 | Due to recent versions of the OpenAPI generator being built with Java 11, you will need Java 11 or higher to run OBA v3.7.0. The current recommended distribution of Java 11+ JDKs (and "JREs") is at [Adoptium's releases page](https://adoptium.net/temurin/releases/?version=11). 17 | 18 | Java versions higher than 11 are also available to use. Java 11 is simply the minimum version. 19 | 20 | ### Downloading binary 21 | 22 | 1. Go the [latest release](https://github.com/KnowledgeCaptureAndDiscovery/OBA/releases/latest) 23 | 2. Download the file with extension .jar 24 | 25 | ### Building binary 26 | 27 | 1. Clone the repository `git clone https://github.com/KnowledgeCaptureAndDiscovery/OBA.git` 28 | 2. Install it using `mvn package` 29 | 3. The binary is available in the `target` directory 30 | 31 | ## Running 32 | 33 | 1. Create the OBA config file (config.yaml) from one of the [sample configuration files in the examples folder](examples/modelcatalog/config.yaml) 34 | 2. Use the configuration to run OBA with the following command: 35 | 36 | ```bash 37 | $ java -jar oba-*-jar-with-dependencies.jar -c config.yaml 38 | ``` 39 | 40 | Congratulations! You have generated an Open Api Specification. 41 | 42 | For instructions on using OBA to create your API server, go to the [documentation](https://oba.readthedocs.io/en/latest/) 43 | 44 | ## Citation 45 | 46 | Please cite our work as follows: 47 | 48 | ``` 49 | @inproceedings{garijo2020OBA, 50 | title = {{OBA}: An Ontology-Based Framework for Creating REST APIs for Knowledge Graphs}, 51 | author = {Garijo, Daniel and Osorio, Maximiliano}, 52 | booktitle={International Semantic Web Conference}, 53 | pages={48--64}, 54 | year={2020}, 55 | doi={https://doi.org/10.1007/978-3-030-62466-8_4}, 56 | organization = {Springer, Cham}, 57 | isbn={978-3-030-62466-8} 58 | } 59 | ``` 60 | -------------------------------------------------------------------------------- /benchmark/api_50qps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KnowledgeCaptureAndDiscovery/OBA/e572ebb13f8faecb1f51bbf34befbe006a631e92/benchmark/api_50qps.png -------------------------------------------------------------------------------- /benchmark/endpoint_50qps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KnowledgeCaptureAndDiscovery/OBA/e572ebb13f8faecb1f51bbf34befbe006a631e92/benchmark/endpoint_50qps.png -------------------------------------------------------------------------------- /docs/adding_custom_queries.md: -------------------------------------------------------------------------------- 1 | 2 | Sometimes, building the REST API from an ontology does not cover all the target queries that need to be supported. In order to address this issue, OBA can create paths in the API using custom SPARQL queries specified by users. 3 | 4 | ## Defining Custom Queries 5 | 6 | First, we must define the SPARQL queries we would like our API to support. For example, let's consider a [sample ontology](https://w3id.org/okn/o/sdm#)which we have already used with OBA. The ontology describes software metadata of complex physical models and, among other classes, it has a Model class and a Variable class. We need to support a special query: get all Models associated with a particular Variable label, which is described in the query below: 7 | 8 | 9 | !!!info 10 | The query must be a **CONSTRUCT**, not SELECT 11 | 12 | ``` 13 | PREFIX sd: 14 | PREFIX sdm: 15 | PREFIX rdfs: 16 | 17 | 18 | CONSTRUCT { 19 | ?model ?predicate ?prop . 20 | ?prop a ?type 21 | } 22 | WHERE { 23 | GRAPH ?_g_iri { 24 | { 25 | SELECT DISTINCT ?model { 26 | ?model sdm:usefulForCalculatingIndex ?sv . 27 | ?sv rdfs:label ?variableLabel 28 | FILTER REGEX(?variableLabel, ?_label, "i") 29 | } 30 | } 31 | ?model ?predicate ?prop 32 | OPTIONAL { 33 | ?prop a ?type 34 | } 35 | } 36 | } 37 | ``` 38 | 39 | The query has two parameters: 40 | 41 | - ?_g_iri (IRI): IRI of the user graph. 42 | - ?_label (string): String of the label belonging to the variable we want to filter by. 43 | 44 | We are using [BASIL's convention](https://github.com/the-open-university/basil/wiki/SPARQL-variable-name-convention-for-WEB-API-parameters-mapping). 45 | 46 | Next, you have to save the query in the **custom** directory. For example, as *custom_models_variable.rq*. 47 | 48 | ## Defining Custom Query Parameters 49 | 50 | We have to extend the OpenAPI specification with the custom query: 51 | 52 | !!! info 53 | You must be familiar with OpenAPI specitification. Please, read [OpenAPI Docs - Describing parameters](https://swagger.io/docs/specification/describing-parameters/) 54 | 55 | 56 | !!! warning 57 | You must add a new parameter with the name *custom_query_name*. 58 | The default value of the parameter must be the filename of the custom query without the extension. 59 | In our example, this name is: **custom_models_variable** 60 | 61 | 62 | ```yaml 63 | parameters: 64 | - description: Username to query 65 | in: query 66 | name: username 67 | required: false 68 | schema: 69 | type: string 70 | - description: variable to search 71 | in: query 72 | name: label 73 | required: true 74 | schema: 75 | type: string 76 | - description: Name of the custom query 77 | in: query 78 | name: custom_query_name 79 | required: false 80 | schema: 81 | default: custom_models_variable 82 | type: string 83 | ``` 84 | 85 | 86 | ## Defining the Custom Query Responses 87 | 88 | Following the OpenAPI specification, we must select the type of response the query is returning. In this case, the response is a list of *Model* in a JSON Format. 89 | 90 | ```yaml 91 | responses: 92 | '200': 93 | content: 94 | application/json: 95 | schema: 96 | items: 97 | $ref: '#/components/schemas/Model' 98 | description: Gets the details of a single instance of Model 99 | summary: Get a Model 100 | ``` 101 | 102 | ## Defining the path name and method 103 | 104 | In this case, the name is going to be `/custom/models/variable`; and we want it to be a GET method: 105 | ```yaml 106 | /custom/models/variable: 107 | get: 108 | ``` 109 | 110 | ## Final result 111 | 112 | The custom_paths must be a List of paths 113 | 114 | ```yaml 115 | custom_paths: 116 | /custom/models/variable: 117 | get: 118 | description: Get models by variable name 119 | parameters: 120 | - description: Name of the custom query 121 | in: query 122 | name: custom_query_name 123 | required: false 124 | schema: 125 | default: custom_models_variable 126 | type: string 127 | - description: Username to query 128 | in: query 129 | name: username 130 | required: false 131 | schema: 132 | type: string 133 | - description: variable to search 134 | in: query 135 | name: label 136 | required: true 137 | schema: 138 | type: string 139 | responses: 140 | 200: 141 | content: 142 | application/json: 143 | schema: 144 | items: 145 | $ref: '#/components/schemas/Model' 146 | description: Gets a list of instance of Model 147 | summary: Get a list of Model 148 | tags: 149 | - Model 150 | ``` 151 | 152 | Finally, you must re run OBA. 153 | -------------------------------------------------------------------------------- /docs/authentication.md: -------------------------------------------------------------------------------- 1 | APIs usually require authentication for POST, PUT and DELETE methods. OBA uses the authentication mechanisms in OpenAPI to address this issue. 2 | 3 | ## Authentication in OpenAPI 4 | 5 | OpenAPI uses the term security scheme for authentication and authorization schemes. OpenAPI 3.0 lets you describe APIs protected using the following security schemes (see more [in the official documentation](https://swagger.io/docs/specification/authentication/)): 6 | 7 | - Basic 8 | - Bearer 9 | - other HTTP schemes as defined by RFC 7235 and HTTP Authentication Scheme Registry 10 | - API keys in headers, query string or cookies 11 | - Cookie authentication 12 | - OAuth 2 13 | - OpenID Connect Discovery 14 | 15 | **OBA supports Bearer**, further described below. 16 | 17 | !!!info 18 | The descriptions in this page have been adapted from the [OpenAPI official documentation](https://swagger.io/docs/specification/authentication/bearer-authentication/) 19 | 20 | ### Bearer Token 21 | 22 | Bearer authentication (also called token authentication) is an HTTP authentication scheme that involves security tokens called bearer tokens. The name Bearer authentication can be understood as give access to the bearer of this token. The bearer token is a cryptic string, usually generated by the server in response to a login request. The client must send this token in the Authorization header when making requests to protected resources: 23 | 24 | ```yaml 25 | Authorization: Bearer 26 | ``` 27 | 28 | #### Describing Bearer Authentication 29 | 30 | !!!info 31 | OBA describes and configures the authentication in your API automatically. 32 | 33 | In OpenAPI 3.0, Bearer authentication is a security scheme with type: http and scheme: bearer. You first need to define the security scheme under components/securitySchemes, then use the security keyword to apply this scheme to the desired scope – global (as in the example below) or specific operations: 34 | 35 | ```yaml 36 | openapi: 3.0.0 37 | ... 38 | # 1) Define the security scheme type (HTTP bearer) 39 | components: 40 | securitySchemes: 41 | bearerAuth: # arbitrary name for the security scheme 42 | type: http 43 | scheme: bearer 44 | bearerFormat: JWT # optional, arbitrary value for documentation purposes 45 | # 2) Apply the security globally to all operations 46 | security: 47 | - bearerAuth: [] # use the same name as above 48 | ``` 49 | 50 | 51 | Optional `bearerFormat` is an arbitrary string that specifies how the bearer token is formatted. Since bearer tokens are usually generated by the server, `bearerFormat` is used mainly for documentation purposes, as a hint to the clients. In the example above, it is "JWT", meaning [JSON Web Token](https://jwt.io/). The square brackets `[]` in `bearerAuth: []` contain a list of security scopes required for API calls. The list is empty because scopes are only used with OAuth 2 and OpenID Connect. 52 | 53 | In the example below, Bearer authentication is applied to the `POST` method: 54 | 55 | ```yaml 56 | paths: 57 | post: 58 | description: Create a new instance of a ConfigurationSetup 59 | parameters: 60 | - description: Username 61 | in: path 62 | name: user 63 | required: false 64 | schema: 65 | type: string 66 | requestBody: 67 | content: 68 | application/json: 69 | schema: 70 | $ref: '#/components/schemas/ConfigurationSetup' 71 | description: A new ConfigurationSetupto be created 72 | responses: 73 | 201: 74 | content: 75 | application/json: 76 | schema: 77 | $ref: '#/components/schemas/ConfigurationSetup' 78 | description: Created 79 | security: 80 | - BearerAuth: [] 81 | summary: Create a ConfigurationSetup 82 | tags: 83 | - ConfigurationSetup 84 | ``` 85 | 86 | ## Add authentication to your API 87 | 88 | OBA supports [Firebase](https://firebase.google.com/docs/) as an authentication provider. We have created a small [tutorial](firebase.md) to help walking you through setting it up. 89 | 90 | !!! warning 91 | User management is out of the scope of OBA. Creating and deleting users will be handled by the authentication provider. 92 | 93 | !!! note 94 | If you would like us to support additional exciting features (like support for new providers), please open an issue in our [GitHub repository](https://github.com/KnowledgeCaptureAndDiscovery/OBA/). 95 | -------------------------------------------------------------------------------- /docs/cache.md: -------------------------------------------------------------------------------- 1 | ## Cached 2 | 3 | When you run the server for the first time, the OpenAPI generator will validate your OpenAPI specification. This process can be take several minutes for long ontologies. Every time the server restart will trigger this process, which may be inconvenient if there are no new changes in the specification. 4 | 5 | In order to speed up this process, the Python server generated by OBA creates a cache file of the specification, which avoid triggering the validation process if there are no new changes. 6 | 7 | 8 | ### Docker 9 | 10 | If you are using Docker, we recommend using volumes as follows: 11 | 12 | ```bash 13 | $ docker run -v $PWD/openapi_server/openapi/:/usr/src/app/openapi_server/openapi/ 14 | ``` 15 | 16 | For example: 17 | 18 | ```bash 19 | $ docker run -v $PWD/openapi_server/openapi/:/usr/src/app/openapi_server/openapi/ dbpedia_music 20 | 2020-05-23 00:23:54,109 - openapi_server.cached - WARNING - Cache file does not exist: [Errno 2] No such file or directory: '/usr/src/app/openapi_server/openapi/openapi.yaml.cache' 21 | * Serving Flask app "__main__" (lazy loading) 22 | * Environment: production 23 | WARNING: This is a development server. Do not use it in a production deployment. 24 | Use a production WSGI server instead. 25 | * Debug mode: off 26 | 2020-05-23 00:23:54,422 - werkzeug - INFO - * Running on http://0.0.0.0:8080/ (Press CTRL+C to quit) 27 | 28 | ``` 29 | 30 | ```bash 31 | $ docker run -v $PWD/openapi_server/openapi/:/usr/src/app/openapi_server/openapi/ dbpedia_music 32 | * Serving Flask app "__main__" (lazy loading) 33 | * Environment: production 34 | WARNING: This is a development server. Do not use it in a production deployment. 35 | Use a production WSGI server instead. 36 | * Debug mode: off 37 | 2020-05-23 00:24:17,308 - werkzeug - INFO - * Running on http://0.0.0.0:8080/ (Press CTRL+C to quit) 38 | ^C% 39 | 40 | ``` 41 | -------------------------------------------------------------------------------- /docs/config.yaml.sample: -------------------------------------------------------------------------------- 1 | ontologies: 2 | - https://mintproject.github.io/Mint-ModelCatalog-Ontology/release/1.2.0/ontology.xml 3 | - https://knowledgecaptureanddiscovery.github.io/SoftwareDescriptionOntology/release/1.4.0/ontology.xml 4 | name: modelcatalog 5 | output_dir: outputs 6 | 7 | openapi: 8 | openapi: 3.0.1 9 | info: 10 | description: This is the API of the Software Description Ontology 11 | at [https://mintproject.github.io/Mint-ModelCatalog-Ontology/release/1.3.0/index-en.html](https://w3id.org/okn/o/sdm) 12 | title: Model Catalog 13 | version: v1.3.0 14 | externalDocs: 15 | description: Model Catalog 16 | url: https://w3id.org/okn/o/sdm 17 | servers: 18 | - url: https://api.models.mint.isi.edu/v1.3.0 19 | - url: https://dev.api.models.mint.isi.edu/v1.3.0 20 | - url: http://localhost:8080/v1.3.0 21 | 22 | endpoint: 23 | url: http://endpoint.mint.isi.edu/modelCatalog-1.2.0 24 | prefix: https://w3id.org/okn/i/mint 25 | graph_base: http://ontosoft.isi.edu:3030/modelCatalog-1.2.0/data/ 26 | -------------------------------------------------------------------------------- /docs/examples.md: -------------------------------------------------------------------------------- 1 | ## API generation examples 2 | 3 | We have tested out OBA with different example ontologies to ensure its fucntionality. Check the [examples](https://github.com/KnowledgeCaptureAndDiscovery/OBA/tree/master/examples) directory on GitHub for sample configurations to test the system. We describe some of them briefly below: 4 | 5 | 1. The [DBPedia music configuration](https://github.com/KnowledgeCaptureAndDiscovery/OBA/blob/master/examples/dbpedia/config_music.yaml) shows how to use OBA to filter an ontology of significant size to include just a few classes and properties of interest (in this case, bands and genres in DBPedia). 6 | 2. The [P-Plan ontology configuration](https://github.com/KnowledgeCaptureAndDiscovery/OBA/tree/master/examples/pplan) shows how to load an ontology using its URI. OBA will do the content negotiation. 7 | 3. The [WINGS configuration](https://github.com/KnowledgeCaptureAndDiscovery/OBA/tree/master/examples/wings) shows how to load an ontology network in OBA. 8 | 4. The [Full Model Catalog configuration](https://github.com/KnowledgeCaptureAndDiscovery/OBA/tree/master/examples/modelcatalog_full) illustrates how to add custom queries in OBA, as well as to include full support for POST, PUT and DELETE methods in the specification. 9 | 10 | ## Server generation examples 11 | 12 | If you are looking to test OBA with an end-to-end example, we recommend generating a server for the [Model Catalog configuration](https://github.com/KnowledgeCaptureAndDiscovery/OBA/tree/master/examples/modelcatalog), which shows how to create an API for a series of classes, properties and data properties with basic restrictions. The deployment and set up of the API don't take more than a few minutes. 13 | 14 | The [DBPedia music configuration](https://github.com/KnowledgeCaptureAndDiscovery/OBA/blob/master/examples/dbpedia/config_music.yaml) also provides a nice end-to-end example, although the response times (without proper optimizations) may be slower than the model catalog example. -------------------------------------------------------------------------------- /docs/features.md: -------------------------------------------------------------------------------- 1 | # OBA: Features 2 | OBA's features are organized on its two main functionalities: the generation of an OpenAPI specification from an ontology and the generation of a server with a given specification: 3 | 4 | - [Generating an OpenAPI Specification from OWL ontologies](#generating-an-openapi-specification-from-owl-ontologies) 5 | - [Generating SPARQL query templates](#generating-sparql-query-templates) 6 | - [Generating JSON-LD contexts](#generating-json-ld-contexts) 7 | - [Class filtering](#class-filtering) 8 | - [Documentation](#documentation) 9 | - [Generating a Python Server from an OAS specification](#generating-a-python-server-from-an-oas-specification) 10 | - [Automatic query handling](#automatic-query-handling) 11 | - [Custom query support](#custom-query-support) 12 | - [Authorization](#authorization) 13 | 14 | 15 | ## Generating an OpenAPI Specification from OWL ontologies 16 | 17 | OBA converts one or multiple OWL ontologies to OpenAPI schemas and creates paths (GET, POST, DELETE, PUT) for each schema corresponding to an ontology class. OBA will document automatically the specification from the definitions provided in the ontology (using rdfs:comment, skos:definition or prov:definition annotations). Each ontology can be loaded from your local computer or from its URI. 18 | 19 | **Paths:** 20 | For each class in the provided ontology (unless filtered) OBA generates the following paths: 21 | 22 | - Get all the resources of a type `GET /persons` 23 | - Search by a free text `GET /persons?label=pattern` 24 | - Get one resource `GET /persons/{id}` 25 | - Post a new resource `POST /persons` 26 | - Put a existing resource `PUT /persons/{id}` 27 | - Delete a existing resource `DELETE /persons/{id}` 28 | 29 | 30 | ### Generating SPARQL query templates 31 | 32 | For each of these paths, OBA generates the SPARQL queries that are necessary to retrieve them from a target SPARQL endpoint. 33 | 34 | ### Generating JSON-LD contexts 35 | 36 | OBA converts the format of the responses from JSON/LD to plain JSON, which is a widely format used by Web developers. In order to achieve this, OBA requires two files with the context of the ontologies used. The context is used to map simple terms to IRIs. OBA generates these context file automatically. 37 | 38 | !!! note 39 | Since OBA 3.3.0, OBA uses two files `context.json` and `context_class.json`. The first file contains all the mappings (classes and properties) and the second file contains the classes mapping. OBAsparql uses the `context.json` for `POST` and `PUT` method and uses the `context_class.json` for `GET` method. 40 | 41 | !!! info 42 | OBA reuses and extends [owl2jsonld](https://github.com/stain/owl2jsonld), developed by [Stain Soiland-Reyes](https://github.com/stain). 43 | 44 | ### Class filtering 45 | 46 | OBA allows selecting a subset of the classes to expose, as large ontologies may lead to big specifications. 47 | 48 | ### Documentation 49 | 50 | OBA will document automatically your API using the annotations found in your ontologies. 51 | 52 | ## Generating a Python Server from an OAS specification 53 | 54 | OBA generates a Python Server using [OpenAPITools/openapi-generator](https://github.com/OpenAPITools/openapi-generator) and [Connexion](https://github.com/zalando/connexion), integrating the SPARQL queries with the server. 55 | 56 | ### Automatic query handling 57 | 58 | For each of the paths in the OpenAPI specification, OBA will automatically convert any requests to deliver a JSON file that follows the structure specified in the ontology. 59 | 60 | ### Custom query support 61 | 62 | OBA will allow you to add custom paths in the API using your own SPARQL queries. 63 | 64 | ### Authorization 65 | 66 | OBA supports authorization using Firebase as backend. When using POST, PUT and DELETE methods with a Knowledge Graph, OBA requires log-in per user. OBA separates the contributions and editions of each user in a different named graph, the user id as the id named graph URI. 67 | -------------------------------------------------------------------------------- /docs/figures/api_cached.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KnowledgeCaptureAndDiscovery/OBA/e572ebb13f8faecb1f51bbf34befbe006a631e92/docs/figures/api_cached.png -------------------------------------------------------------------------------- /docs/figures/endpoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KnowledgeCaptureAndDiscovery/OBA/e572ebb13f8faecb1f51bbf34befbe006a631e92/docs/figures/endpoint.png -------------------------------------------------------------------------------- /docs/figures/firebase.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KnowledgeCaptureAndDiscovery/OBA/e572ebb13f8faecb1f51bbf34befbe006a631e92/docs/figures/firebase.mp4 -------------------------------------------------------------------------------- /docs/firebase.md: -------------------------------------------------------------------------------- 1 | ## Authentication using Firebase 2 | 3 | 1. Go to [https://console.firebase.google.com/u/0/](https://console.firebase.google.com/u/0/) 4 | 2. Create a new project 5 | 3. Enable the authentication and copy the Web API key 6 | 10 | 4. Edit the OBA configuration file as follows: 11 | ```yaml 12 | auth: 13 | provider: firebase 14 | firebase: 15 | key: YOUR_KEY 16 | ``` 17 | Where `YOUR_KEY` corresponds to the Web API key you obtained above. 18 | 19 | Finally, re-run OBA. 20 | 21 | ### Testing the authentication 22 | 23 | Now you can see that OBA added a new path `/user/login`: 24 | 25 | 26 | ``` 27 | /user/login: 28 | get: 29 | description: Login the user 30 | operationId: user_login_get 31 | parameters: 32 | - description: The user name for login 33 | in: query 34 | name: username 35 | required: true 36 | schema: 37 | type: string 38 | - description: The password for login in clear text 39 | in: query 40 | name: password 41 | required: true 42 | schema: 43 | type: string 44 | responses: 45 | 200: 46 | content: 47 | application/json: 48 | schema: 49 | type: string 50 | description: successful operation 51 | headers: 52 | X-Rate-Limit: 53 | description: calls per hour allowed by the user 54 | schema: 55 | format: int32 56 | type: integer 57 | X-Expires-After: 58 | description: date in UTC when token expires 59 | schema: 60 | format: date-time 61 | type: string 62 | 400: 63 | content: 64 | application/json: 65 | schema: 66 | type: string 67 | description: unsuccessful operation 68 | x-openapi-router-controller: openapi_server.controllers.user_controller 69 | ``` 70 | 71 | 72 | You can test the authentication with the following command: 73 | 74 | ``` 75 | $ curl -s -X GET 'https://$SERVER/$VERSION/user/login?username=$USERNAME&password=$PASSWORD' -H 'accept: application/json' 76 | { 77 | "access_token": "$ACCESS_TOKEN", 78 | "expires_in": 60000, 79 | "refresh_token": "$REFREST_TOKEN", 80 | "scope": "create", 81 | "token_type": "bearer" 82 | } 83 | ``` 84 | 85 | To use the token, add the token in the header 86 | 87 | ``` 88 | $ curl -v -X POST "$SERVER/$CLASS" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d "$payload" 89 | ``` -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Ontology-Based APIs (OBA) [![Build Status](https://travis-ci.org/KnowledgeCaptureAndDiscovery/OBA.svg?branch=master)](https://travis-ci.org/KnowledgeCaptureAndDiscovery/OBA) 2 | 3 | **Authors**: Maximiliano Osorio and Daniel Garijo. 4 | 5 | 6 | The Ontology-Based API (OBA) framework takes as input an ontology or ontology network (specified in OWL) and generates an OpenAPI Specification (OAS). Using this definition, OBA creates a REST API server automatically that can validate the requests from users; deliver JSON objects following the structure described in the ontology; accept custom queries needed by users; and support clients for easing the interaction with the API. Figure 1 shows a snapshot of the different capabilities of OBA: 7 | 8 | ![Diagram](figures/oba.svg) 9 | **Figure 1**: Overview of the capabilities of OBA 10 | 11 | !!! info 12 | If you experience any issues when using OBA, or if you would like us to support additional exciting features, please open an issue on our [GitHub repository](https://github.com/KnowledgeCaptureAndDiscovery/OBA/issues). 13 | 14 | To see how to install and use OBA, just follow this [link](quickstart.md). 15 | 16 | ## Why OBA? 17 | We developed OBA to help bridge the gap between ontology engineers -who create and maintain knowledge graphs using RDF, OWL and SPARQL- and Web developers who need to consume data in knowledge graphs but are not familiar with Semantic Web standards. OBA helps ontology engineers sketch and customize APIs created directly from their ontologies, making the process easy to maintain. 18 | 19 | ## Used Technologies and Standards 20 | 21 | ### OpenAPI 22 | 23 | The OpenAPI Specification (OAS) defines a standard, language-agnostic interface for RESTful APIs which allows **both humans and computers to discover and understand** the capabilities of the service without having to inspect the source code or network traffic. When properly defined, a consumer can understand and interact with the remote service with a minimal amount of implementation logic. 24 | More information about Open API can be found at the [OpenAPI Specification official page (Swagger)](https://swagger.io/specification/) 25 | 26 | ### OWL 27 | 28 | The [W3C Web Ontology Language (OWL)](https://www.w3.org/TR/owl-semantics/) is a Semantic Web language designed to represent rich and complex knowledge about things, groups of things, and relations between things. OWL is a computational logic-based language such that knowledge expressed in **OWL can be exploited by computer programs**, e.g., to verify the consistency of that knowledge or to make implicit knowledge explicit. OWL documents, known as ontologies, can be published in the World Wide Web. 29 | 30 | ### JSON 31 | The JavaScript Object Notation (JSON) is a syntax for storing and exchanging data commonly used by Web developers. 32 | 33 | ### JSON-LD 34 | JSON-LD is "a JSON-based format to serialize Linked Data. The syntax is designed to easily integrate into deployed systems that already use JSON, and provides a smooth upgrade path from JSON to JSON-LD". [[Source: Json-LD1.1 specification](https://www.w3.org/TR/json-ld11/)] 35 | 36 | OBA uses JSON-LD to transform JSON-LD results to JSON. 37 | 38 | ### SPARQL 39 | [W3C standard](https://www.w3.org/TR/sparql11-query/) describing a query language for RDF. OBA uses SPARQL queries to retrieve the contents of a given knowledge graph according the contents of an ontology. 40 | -------------------------------------------------------------------------------- /docs/mapping.md: -------------------------------------------------------------------------------- 1 | OBA supports the mapping defined in [https://owl-to-oas.readthedocs.io/en/latest/mapping/](https://owl-to-oas.readthedocs.io/en/latest/mapping/), with a few small modifications: 2 | 3 | - The mapping suggests using the `allOf` property from OAS to capture subclass relationships. However, this was not supported by any existing generators until very recently (it is still on test), and therefore OBA will iterate through all superclasses to add the appropriate properties for a given schema. 4 | - Path naming conventions: The mapping suggest using the labels of the terms of the ontology for creating the paths in the target API. However, at the moment OBA will use the local namespace of the classes. 5 | 6 | Additional materials and examples of the mapping are available in [this GitHub repository](https://github.com/oeg-upm/OWL-To-OAS-Specification) 7 | -------------------------------------------------------------------------------- /docs/quickstart.md: -------------------------------------------------------------------------------- 1 | 2 | ## Requirements 3 | 4 | Java 1.8 or higher (SDK 1.8 or JRE 8). 5 | 6 | ## Installation 7 | 8 | There are two options to run OBA: 9 | 10 | 1. Download the binary 11 | 2. Build the binary from the repository 12 | 13 | 14 | ### 1 Downloading binary 15 | 16 | 1. Go the [latest release page](https://github.com/KnowledgeCaptureAndDiscovery/OBA/releases/latest) 17 | 2. Download the file with extension .jar 18 | 19 | ### 2 Building binary 20 | 21 | 1. Clone the repository `git clone https://github.com/KnowledgeCaptureAndDiscovery/OBA.git` 22 | 2. Install it executing `mvn package` 23 | 3. The binary will be available in the `target` directory 24 | 25 | ## Running OBA 26 | 27 | !!! info 28 | Documentation on the configuration file can be accessed [in the configuration page](configuration_file.md) 29 | 30 | 31 | 1. Create the OBA config file (config.yaml) from the [sample configuration we provide](config.yaml.sample) 32 | 2. Use the configuration to run the OBA JAR: 33 | 34 | ```bash 35 | $ java -jar oba-*-jar-with-dependencies.jar -c config.yaml 36 | ``` 37 | 38 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | mkdocs-material -------------------------------------------------------------------------------- /docs/result.md: -------------------------------------------------------------------------------- 1 | 2 | OBA generates two directories: 3 | 4 | 1. servers: Contains the OpenAPI specification and server implementation 5 | 2. queries: Contains all the generated queries. 6 | 7 | ## Servers directory 8 | 9 | The servers directory contains: 10 | 11 | - [File] OpenAPI specification `openapi.yaml` 12 | - [Directory] Server implementation (default: python) 13 | 14 | ## Queries directory 15 | 16 | The queries directory contains the query files that are automatically generated by OBA. There are three types of directories inside the queries directory: 17 | 18 | 1. Default queries (_default_): contains the default queries generated by OBA. 19 | 2. Custom queries (_custom_): In some cases, users need to implement custom queries that are not the default instances of a class of the ontology (e.g., get all instances of a class that comply with certain conditions). In these cases it is more effective to allow users implementing their own SPARQL queries. The custom directory contains these special queries. See more details in [the custom queries documentation](https://oba.readthedocs.io/en/latest/adding_custom_queries/). 20 | 3. Classes: OBA creates a query directory per class. It allows users to modify the default queries for each class in case it's necessary. 21 | 22 | !!!info 23 | The server will use the query from the default directory unless the query exists in the class directory. 24 | 25 | 26 | ### Default queries 27 | 28 | OBA generates the following queries for each class. For example, if we had the class **Person**: 29 | 30 | - get_all: Get all the resources of a type. `GET /persons`. 31 | - get_all_user: Get all the resources of a type. `GET /persons?username=USER` in the graph **USER**. 32 | - get_one: Get the resource of a given type with a given ID `GET /persons/ID`. 33 | - get_one_user: Get the resource of a given type with the `GET /persons/ID?username=USER` in the graph **USER**. 34 | 35 | The queries for POST, PUT and DELETE cannot be modified. 36 | -------------------------------------------------------------------------------- /docs/running_server.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Running with Docker 4 | 5 | To run the server on a Docker container, execute the following command from the root directory: 6 | 7 | ```bash 8 | # building the image 9 | $ docker build -t . 10 | 11 | # starting up a container 12 | $ docker run -p 8080:8080 -v $PWD/openapi_server/openapi/:/usr/src/app/openapi_server/openapi/ 13 | ``` 14 | 15 | !!!info 16 | To improve the speed at which the queries are returned, you can configure OBA to use a [cache](cache.md) (recommended) 17 | 18 | and open the following URL in your browser: 19 | 20 | 21 | ``` 22 | http://localhost:8080//ui/ 23 | ``` 24 | 25 | !!! warning 26 | The (e.g., v1.3.0) is defined in your configuration yaml file (the field `version`). For more information, see the README file generated in your server folder when running OBA. 27 | 28 | 29 | Your OpenAPI definition is accessible here: 30 | 31 | ``` 32 | http://localhost:8080//openapi.json 33 | ``` 34 | 35 | To launch the integration tests, install and execute [tox](https://pypi.org/project/tox/): 36 | 37 | ``` 38 | sudo pip install tox 39 | tox 40 | ``` 41 | 42 | ### Enabling CORS 43 | 44 | !!! info 45 | We recommend to enable CORS in the WebServer and not in the application. [https://enable-cors.org/server.html](https://enable-cors.org/server.html) 46 | 47 | 48 | You can enable CORS in the Python server as follows: 49 | 50 | ```python 51 | import connexion 52 | from flask_cors import CORS 53 | 54 | app = connexion.FlaskApp(__name__) 55 | app.add_api('swagger.yaml') 56 | 57 | # add CORS support 58 | CORS(app.app) 59 | 60 | app.run(port=8080) 61 | ``` 62 | 63 | You can see an example in the following [GitHub repository](https://github.com/sirspock/dbpedia_api/blob/master/server/openapi_server/__main__.py) 64 | 65 | -------------------------------------------------------------------------------- /docs/server.md: -------------------------------------------------------------------------------- 1 | # Generating the server 2 | 3 | OBA uses [OpenAPI Generator](https://github.com/OpenAPITools/openapi-generator#overview) to generate a server instance. 4 | 5 | ## OpenAPI Generator 6 | 7 | Given an OpenAPI Specification, the OpenAPI Generator generates automatically API client libraries (SDK generation), server stubs, documentation and configuration files. The [OpenAPI Generator website](https://github.com/OpenAPITools/openapi-generator#32---workflow-integration-maven-gradle-github-cicd) 8 | maintains a list of the languages/frameworks that are supported. 9 | 10 | ### RDF Integration 11 | 12 | The OpenAPI Generator provides an API backend based on the OpenAPI Specification. However, it does not provide the logic of the server, like the connection to the target knowledge graph. 13 | 14 | OBA modifies the server generated by the OpenAPI Generator using [templates](https://github.com/OpenAPITools/openapi-generator/blob/master/docs/templating.md). As a result, the server can: 15 | 16 | 1. Connect and query resources from a target SPARQL endpoint. 17 | - Convert RDF triples under a given namespace(s) to JSON (using JSON/LD). 18 | 2. Connect and insert resources into the SPARQL endpoint. 19 | - Convert JSON into RDF triples (using JSON/LD and frames). 20 | 21 | 22 | 23 | ## Generating the Server 24 | 25 | Currently, OBA supports **Python/Flask** for the server implementation. In order to generate the server, you should follow the instructions below: 26 | 27 | ### Execute Server Generation Scripts 28 | 29 | !!! warning 30 | You must have [Docker](https://docs.docker.com/get-started/) installed. 31 | 32 | Access to the directory with the server implementation: 33 | 34 | ```bash 35 | $ cd python 36 | ``` 37 | 38 | 39 | Run the OpenAPI generator script to generate the server. If you are in Unix, you may have to change the permission of the file with `chmod +x generate-server.sh`: 40 | 41 | ```bash 42 | $ bash generate-server.sh 43 | ... 44 | ... 45 | SUCCESS 46 | ``` 47 | 48 | #### Structure of the server 49 | 50 | The generated server directory should contain the following files and directories: 51 | 52 | ```bash 53 | $ ls server/ 54 | Dockerfile: A Dockerfile used to build the server Docker image 55 | README.md: A README.md with the instructions to run the server 56 | contexts: Directory with the JSON/LD contexts generated from the ontology 57 | openapi_server: The server implemenation 58 | queries: Directory with the SPARQL queries 59 | requirements.txt: Python requirements of the server 60 | test-requirements.txt: Python requirements for testing the server 61 | ``` 62 | 63 | 64 | -------------------------------------------------------------------------------- /docs/test.md: -------------------------------------------------------------------------------- 1 | # Running API tests 2 | 3 | The resultant server code contains the tests to evaluate the status of your API against a knowledge graph. 4 | 5 | ## Installation 6 | 7 | To use the tests, you must install `tox` (we recommend to use a [virtual environment](https://docs.python.org/3/library/venv.html)): 8 | 9 | ```bash 10 | $ pip install tox 11 | ``` 12 | 13 | The tests are in the `servers/python/server/openapi_server/test/` folder. 14 | 15 | ## Before you run the tests 16 | OBA creates tests for all the paths in your API, including specific instances. However, since the instances to test are unknown beforehand, OBA uses a placeholder `id_example` which has to be modified with the instance id you want to test. For example, for the `dbpedia_music` example, the files `servers/python/server/openapi_server/test/test_band_controller.py` and `python/server/openapi_server/test/test_genre_controller.py` will need to be modified with a band name and a music genre of your choice. You can choose an id by running your API, e.g., for bands: 17 | 18 | ``` 19 | curl -X GET "http://localhost:8080/v1.3.0/bands?page=1&per_page=10" -H "accept: application/json" 20 | ``` 21 | 22 | And then selecting one to change in the test file. For example, in test_band_controller.py we can ask for the Black_Sabbath, changing 23 | ``` 24 | response = self.client.open( 25 | '/v1.3.0/bands/{id}'.format(id='id_example'), 26 | ``` 27 | into 28 | ``` 29 | response = self.client.open( 30 | '/v1.3.0/bands/{id}'.format(id='Black_Sabbath'), 31 | ``` 32 | 33 | You will need to provide a sample id to test for all the paths of the API that test individual instances, or the tests will fail. 34 | 35 | ## Running the tests 36 | 37 | Just access the server folder `servers/python/server/openapi_server/` and run: 38 | 39 | ```bash 40 | $ tox 41 | ``` 42 | 43 | You can modify the test requirements in `src/test-requirements.txt`. 44 | 45 | ## Editing 46 | 47 | The following [documentation](https://nose.readthedocs.io/en/latest/testing.html) indicates how to edit the tests. 48 | 49 | ### Configure 50 | 51 | 52 | There are two useful options to test your API against a knowledge graph: 53 | 54 | - validate_responses can be useful to detect invalid properties or types on your knowledge graph. 55 | - strict_validation can be helpful to see an invalid request. 56 | 57 | ``` 58 | :param validate_responses: True enables validation. 59 | Validation errors generate HTTP 500 responses. 60 | :type validate_responses: bool 61 | :param strict_validation: 62 | True enables validation on invalid request parameters 63 | :type strict_validation: bool 64 | ``` 65 | 66 | 67 | 68 | You can edit these option in `server/openapi_server/test/__init__.py` 69 | 70 | ```python 71 | def create_app(self): 72 | Specification.from_file = CachedSpecification.from_file 73 | app = connexion.App(__name__, specification_dir='../openapi/') 74 | app.app.json_encoder = JSONEncoder 75 | app.add_api('openapi.yaml', 76 | pythonic_params=False, 77 | validate_responses=True) 78 | return app.app 79 | ``` 80 | 81 | ## Examples 82 | 83 | The following OpenAPI Operation (`/bands`) will generate a test with the parameters and request formats required. 84 | 85 | ```yaml 86 | /bands: 87 | get: 88 | description: Gets a list of all instances of Band (more information in http://dbpedia.org/ontology/Band) 89 | operationId: bands_get 90 | parameters: 91 | - description: Filter by label 92 | explode: true 93 | in: query 94 | name: label 95 | required: false 96 | schema: 97 | type: string 98 | style: form 99 | - description: Page number 100 | explode: true 101 | in: query 102 | name: page 103 | required: false 104 | schema: 105 | default: 1 106 | format: int32 107 | type: integer 108 | style: form 109 | - description: Items per page 110 | explode: true 111 | in: query 112 | name: per_page 113 | required: false 114 | schema: 115 | default: 100 116 | format: int32 117 | maximum: 200 118 | minimum: 1 119 | type: integer 120 | style: form 121 | responses: 122 | 200: 123 | content: 124 | application/json: 125 | schema: 126 | items: 127 | $ref: '#/components/schemas/Band' 128 | type: array 129 | description: Successful response - returns an array with the instances of 130 | Band. 131 | summary: List all instances of Band 132 | tags: 133 | - Band 134 | x-openapi-router-controller: openapi_server.controllers.band_controller 135 | ``` 136 | 137 | 138 | ```python 139 | def test_bands_get(self): 140 | """Test case for bands_get 141 | 142 | List all instances of Band 143 | """ 144 | query_string = [('label', 'label_example'), 145 | ('page', 1), 146 | ('per_page', 100)] 147 | headers = { 148 | 'Accept': 'application/json', 149 | } 150 | response = self.client.open( 151 | '/v1.3.0/bands', 152 | method='GET', 153 | headers=headers, 154 | query_string=query_string) 155 | self.assert200(response, 156 | 'Response body is : ' + response.data.decode('utf-8')) 157 | ``` -------------------------------------------------------------------------------- /examples/business/config.yaml: -------------------------------------------------------------------------------- 1 | #Name of the project 2 | name: BusinessOntology 3 | 4 | ## OpenAPI Section 5 | ### Name, version and URL of the OpenAPI 6 | ### For more information about the section. Go to the official documentation 7 | openapi: 8 | openapi: 3.0.1 9 | info: 10 | description: This is the API of the Business Ontology 11 | title: Business Ontology test 12 | version: v1 13 | externalDocs: 14 | description: busont 15 | url: https://www.semanticarts.com/gist/previous-versions/ 16 | servers: 17 | - url: http://localhost:8080/v1 18 | description: localhost server 19 | 20 | ## Ontologies 21 | ### List of ontologies 22 | ontologies: 23 | - examples/business/ontology.xml 24 | 25 | ## SPARQL information 26 | endpoint: 27 | url: http://localhost:7201/sparql 28 | prefix: https://businessontology.com/resource 29 | 30 | ## Filter the paths by methods 31 | enable_get_paths: true 32 | enable_post_paths: false 33 | enable_delete_paths: false 34 | enable_put_paths: false 35 | 36 | 37 | follow_references: true 38 | -------------------------------------------------------------------------------- /examples/business/ontology.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /examples/business/ontology_hash.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | has location def 30 | has location 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | Artifact def 71 | Artifact 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | The building 86 | Building 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | Buisiness user def 95 | Buisiness user 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | Customer definition 110 | Customer 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | Location 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | Transaction document definition 127 | Transaction document 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /examples/business/ontology_slash.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | has location def 30 | has location 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | Artifact def 71 | Artifact 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | The building 86 | Building 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | Buisiness user def 95 | Buisiness user 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | Customer definition 110 | Customer 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | Location 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | Transaction document definition 127 | Transaction document 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /examples/dbpedia/config_full.yaml: -------------------------------------------------------------------------------- 1 | #Name of the project 2 | name: dbpedia 3 | 4 | ## OpenAPI Section 5 | ### Name, version and URL of the OpenAPI 6 | ### For more information about the section. Go to the official documentation 7 | openapi: 8 | openapi: 3.0.1 9 | info: 10 | description: This is the API of the DBpedia Ontology 11 | title: DBpedia 12 | version: v1.3.0 13 | externalDocs: 14 | description: DBpedia 15 | url: https://w3id.org/okn/o/sdm 16 | servers: 17 | - url: https:///dbpedia.dbpedia.oba.isi.edu/v1.3.0 18 | - url: http://localhost:8080/v1.3.0 19 | 20 | ## Ontologies 21 | ### List of ontologies 22 | ontologies: 23 | - examples/dbpedia/ontology.xml 24 | 25 | ## SPARQL information 26 | endpoint: 27 | url: http://dbpedia.org/sparql 28 | prefix: http://dbpedia.org/resource 29 | 30 | ## Filter the paths by methods 31 | enable_get_paths: true 32 | enable_post_paths: false 33 | enable_delete_paths: false 34 | enable_put_paths: false 35 | 36 | ## Select the classes to add in the API 37 | follow_references: false 38 | -------------------------------------------------------------------------------- /examples/dbpedia/config_music.yaml: -------------------------------------------------------------------------------- 1 | #Name of the project 2 | name: dbpedia_music 3 | 4 | ## OpenAPI Section 5 | ### Name, version and URL of the OpenAPI 6 | ### For more information about the section. Go to the official documentation 7 | openapi: 8 | openapi: 3.0.1 9 | info: 10 | description: This is the API of the DBpedia Ontology 11 | title: DBpedia 12 | version: v1.3.0 13 | externalDocs: 14 | description: DBpedia 15 | url: http://dbpedia.org/ 16 | servers: 17 | - url: https://dbpedia.dbpedia.oba.isi.edu/v1.3.0 18 | - url: http://localhost:8080/v1.3.0 19 | 20 | ## Ontologies 21 | ### List of ontologies 22 | ontologies: 23 | - examples/dbpedia/ontology.xml 24 | 25 | ## SPARQL information 26 | endpoint: 27 | url: http://dbpedia.org/sparql 28 | prefix: http://dbpedia.org/resource 29 | 30 | ## Filter the paths by methods 31 | enable_get_paths: true 32 | enable_post_paths: false 33 | enable_delete_paths: false 34 | enable_put_paths: false 35 | 36 | ## Select the classes to add in the API 37 | classes: 38 | - http://dbpedia.org/ontology/Genre 39 | - http://dbpedia.org/ontology/Band 40 | follow_references: false 41 | -------------------------------------------------------------------------------- /examples/example with spaces/config.yaml: -------------------------------------------------------------------------------- 1 | ontologies: 2 | - examples/example with spaces/sdm-reduced.ttl 3 | - examples/example with spaces/sd-reduced.ttl 4 | name: modelcatalog 5 | output_dir: outputs 6 | 7 | openapi: 8 | openapi: 3.0.1 9 | info: 10 | description: This is an API example for the Software Description Ontology for models [https://w3id.org/okn/o/sdm](https://w3id.org/okn/o/sdm). The endpoint uses information of models from hydrology, climate, agriculture and economic domains. 11 | title: Model Catalog 12 | version: v1.5.0 13 | externalDocs: 14 | description: Model Catalog 15 | url: https://w3id.org/okn/o/sdm 16 | servers: 17 | - url: https://api.models.mint.isi.edu/v1.5.0 18 | - url: https://dev.api.models.mint.isi.edu/v1.5.0 19 | - url: http://localhost:8080/v1.5.0 20 | 21 | firebase: 22 | key: "test" 23 | 24 | endpoint: 25 | url: http://endpoint.mint.isi.edu/modelCatalog-1.4.0 26 | prefix: https://w3id.org/okn/i/masd 27 | graph_base: http://endpoint.mint.isi.edu/modelCatalog-1.4.0/data/ 28 | 29 | enable_get_paths: true 30 | enable_post_paths: false 31 | enable_delete_paths: false 32 | enable_put_paths: false 33 | 34 | auth: 35 | provider: firebase 36 | follow_references: true 37 | 38 | -------------------------------------------------------------------------------- /examples/example with spaces/readme.md: -------------------------------------------------------------------------------- 1 | ## Model catalog: reduced example 2 | 3 | This example illustrates loading ontologies from folders with spaces -------------------------------------------------------------------------------- /examples/games/config.yaml: -------------------------------------------------------------------------------- 1 | ontologies: 2 | - examples\games\vgo.owl 3 | name: Videogame 4 | output_dir: outputs 5 | 6 | openapi: 7 | openapi: 3.0.1 8 | info: 9 | description: This is the API of the videogame ontology 10 | title: Videogame 11 | version: v0.0.1 12 | externalDocs: 13 | description: Videogame ontology 14 | url: http://purl.org/net/VideoGameOntology# 15 | servers: 16 | - url: http://localhost:8080/v0.0.1 17 | 18 | 19 | endpoint: 20 | url: http://localhost:3030/ 21 | prefix: http://example.org/game 22 | graph_base: http://localhost:3030/game/data/ 23 | 24 | -------------------------------------------------------------------------------- /examples/jazz/config.yaml: -------------------------------------------------------------------------------- 1 | ontologies: 2 | - examples/jazz/qm.xml 3 | name: rqm-test 4 | output_dir: outputs 5 | 6 | openapi: 7 | openapi: 3.0.1 8 | info: 9 | description: This is an API example for the Software Description Ontology for models [https://w3id.org/okn/o/sdm](https://w3id.org/okn/o/sdm). 10 | title: Jazz Catalog 11 | version: v7.0.2 12 | externalDocs: 13 | description: Jazz Catalog 14 | url: https://jazz.net/wiki/bin/view/Deployment/CLMProductAPILanding 15 | servers: 16 | - url: https://elmwb.com:9443/qm 17 | 18 | endpoint: 19 | url: https://elmwb.com:9443/qm 20 | 21 | enable_get_paths: true 22 | enable_post_paths: false 23 | enable_delete_paths: false 24 | enable_put_paths: false 25 | 26 | follow_references: true 27 | 28 | -------------------------------------------------------------------------------- /examples/le/config.yaml: -------------------------------------------------------------------------------- 1 | ontologies: 2 | - http://linked.earth/ontology# 3 | # Example with an ontology that aggregates several ontologies (big) 4 | name: LinkedEarth 5 | output_dir: outputs 6 | 7 | openapi: 8 | openapi: 3.0.1 9 | info: 10 | description: This is the API of the Linked Earth Ontology 11 | title: LinkedEarth 12 | version: v0.0.1 13 | externalDocs: 14 | description: Linked Earth Ontology 15 | url: http://linked.earth/ontology# 16 | servers: 17 | - url: http://localhost:8080/v0.0.1 18 | 19 | 20 | endpoint: 21 | url: http://localhost:3030/ 22 | prefix: http://linked.earth/data 23 | graph_base: http://localhost:3030/wings/data/ 24 | 25 | -------------------------------------------------------------------------------- /examples/modelcatalog/config.yaml: -------------------------------------------------------------------------------- 1 | ontologies: 2 | - examples/modelcatalog/sdm-reduced.ttl 3 | - examples/modelcatalog/sd-reduced.ttl 4 | name: modelcatalog 5 | output_dir: outputs 6 | 7 | openapi: 8 | openapi: 3.0.1 9 | info: 10 | description: This is an API example for the Software Description Ontology for models [https://w3id.org/okn/o/sdm](https://w3id.org/okn/o/sdm). The endpoint uses information of models from hydrology, climate, agriculture and economic domains. 11 | title: Model Catalog 12 | version: v1.5.0 13 | externalDocs: 14 | description: Model Catalog 15 | url: https://w3id.org/okn/o/sdm 16 | servers: 17 | - url: https://api.models.mint.isi.edu/v1.5.0 18 | - url: https://dev.api.models.mint.isi.edu/v1.5.0 19 | - url: http://localhost:8080/v1.5.0 20 | 21 | firebase: 22 | key: "test" 23 | 24 | endpoint: 25 | url: http://endpoint.mint.isi.edu/modelCatalog-1.4.0 26 | prefix: https://w3id.org/okn/i/masd 27 | graph_base: http://endpoint.mint.isi.edu/modelCatalog-1.4.0/data/ 28 | 29 | enable_get_paths: true 30 | enable_post_paths: false 31 | enable_delete_paths: false 32 | enable_put_paths: false 33 | 34 | auth: 35 | provider: firebase 36 | follow_references: true 37 | 38 | -------------------------------------------------------------------------------- /examples/modelcatalog/readme.md: -------------------------------------------------------------------------------- 1 | ## Model catalog: reduced example 2 | 3 | This example illustrates how to load ontologies from local files. 4 | 5 | Note that this example was tested on Unix (hence the file path) and the working directory is assumed to be the above the "examples" folder -------------------------------------------------------------------------------- /examples/modelcatalog_full/custom_queries/custom_configurationsetups.rq: -------------------------------------------------------------------------------- 1 | PREFIX sd: 2 | PREFIX rdfs: 3 | CONSTRUCT { 4 | ?_resource_iri ?predicate ?prop . 5 | ?prop ?p ?o . 6 | ?input_resource ?input_resource_p ?input_resource_o . 7 | ?output_resource ?output_resource_p ?output_resource_o . 8 | ?parameter ?parameter_p ?parameter_o 9 | } 10 | WHERE { 11 | GRAPH ?_g_iri { 12 | { 13 | ?_resource_iri ?predicate ?prop . 14 | OPTIONAL { 15 | ?prop ?p ?o 16 | } 17 | } 18 | UNION { 19 | ?_resource_iri sd:hasInput ?input . 20 | ?input sd:hasFixedResource ?input_resource . 21 | ?input_resource ?input_resource_p ?input_resource_o 22 | } 23 | UNION { 24 | ?_resource_iri sd:hasOutput ?output . 25 | ?output sd:hasFixedResource ?output_resource . 26 | ?output_resource ?output_resource_p ?output_resource_o 27 | } 28 | UNION { 29 | ?_resource_iri sd:hasParameter ?parameter . 30 | ?parameter ?parameter_p ?parameter_o 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /examples/modelcatalog_full/custom_queries/custom_datatransformations.rq: -------------------------------------------------------------------------------- 1 | PREFIX sd: 2 | PREFIX sdm: 3 | PREFIX rdfs: 4 | CONSTRUCT { 5 | ?dt ?p ?o . 6 | } 7 | WHERE { 8 | GRAPH ?_g_iri { 9 | ?_resource_iri rdfs:label ?label . 10 | ?setup sd:hasInput ?_resource_iri . 11 | ?mc sd:hasSetup ?setup . 12 | ?mc sd:hasInput ?input . 13 | ?input rdfs:label ?label . 14 | ?input sd:hasDataTransformation ?dt . 15 | ?dt ?p ?o 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/modelcatalog_full/custom_queries/custom_model_index.rq: -------------------------------------------------------------------------------- 1 | #+ summary: Given a rdf type, returns all the resources related to the type 2 | PREFIX sd: 3 | PREFIX sdm: 4 | PREFIX rdfs: 5 | 6 | 7 | 8 | CONSTRUCT { 9 | ?model ?predicate ?prop . 10 | ?prop a ?type 11 | } 12 | WHERE { 13 | GRAPH ?_g_iri { 14 | { 15 | SELECT DISTINCT ?model where { 16 | ?model a ?type. 17 | ?model sd:usefulForCalculatingIndex ?index . 18 | ?index rdfs:label ?indexLabel 19 | FILTER REGEX(?indexLabel, ?_label, "i") 20 | VALUES ?type {sdm:Model sdm:ModelConfiguration sdm:ModelConfigurationSetup} 21 | } 22 | } 23 | ?model ?predicate ?prop 24 | OPTIONAL { 25 | ?prop a ?type 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/modelcatalog_full/custom_queries/custom_model_intervetion.rq: -------------------------------------------------------------------------------- 1 | #+ summary: Given a rdf type, returns all the resources related to the type 2 | PREFIX sd: 3 | PREFIX sdm: 4 | PREFIX rdfs: 5 | 6 | 7 | CONSTRUCT { 8 | ?model ?predicate ?prop . 9 | ?prop a ?type 10 | } 11 | WHERE { 12 | GRAPH ?_g_iri { 13 | { 14 | SELECT DISTINCT ?model where { 15 | ?model sd:hasVersion ?model_version . 16 | ?model_version sd:hasConfiguration ?model_c . 17 | ?model_c sd:hasSetup ?setup . 18 | ?setup sd:hasParameter ?param . 19 | ?param sdm:relevantForIntervention ?interv . 20 | ?interv a sdm:Intervention . 21 | ?interv rdfs:label ?intervLabel . 22 | FILTER REGEX(?intervLabel, ?_label, "i") 23 | } 24 | } 25 | ?model ?predicate ?prop 26 | OPTIONAL { 27 | ?prop a ?type 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /examples/modelcatalog_full/custom_queries/custom_model_region.rq: -------------------------------------------------------------------------------- 1 | #+ summary: Given a rdf type, returns all the resources related to the type 2 | #+ - graph: http://ontosoft.isi.edu:3030/modelCatalog-1.2.0/data/mint@isi.edu 3 | 4 | PREFIX sd: 5 | PREFIX sdm: 6 | PREFIX rdfs: 7 | 8 | 9 | CONSTRUCT { 10 | ?model ?predicate ?prop . 11 | ?prop a ?type 12 | } 13 | WHERE { 14 | GRAPH ?_g_iri { 15 | { 16 | SELECT DISTINCT ?model { 17 | ?model sd:hasVersion ?model_version . 18 | ?model_version sd:hasConfiguration ?model_c . 19 | ?model_c sd:hasSetup ?setup . 20 | { ?model_c sdm:hasRegion ?region } 21 | union { 22 | ?setup sdm:hasRegion ?region . 23 | } 24 | ?region rdfs:label ?regionLabel . 25 | FILTER REGEX(?regionLabel, ?_label, "i") 26 | } 27 | } 28 | ?model ?predicate ?prop 29 | OPTIONAL { 30 | ?prop a ?type 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /examples/modelcatalog_full/custom_queries/custom_model_standard_variable.rq: -------------------------------------------------------------------------------- 1 | #+ summary: Given a rdf type, returns all the resources related to the type 2 | PREFIX sd: 3 | PREFIX sdm: 4 | PREFIX rdfs: 5 | 6 | 7 | CONSTRUCT { 8 | ?model ?predicate ?prop . 9 | ?prop a ?type 10 | } 11 | WHERE { 12 | GRAPH ?_g_iri { 13 | { 14 | SELECT DISTINCT ?model where { 15 | ?model a sdm:Model . 16 | ?model sd:hasVersion ?modelV . 17 | ?modelV sd:hasConfiguration ?config . 18 | { 19 | ?config ?prop ?d . 20 | ?d sd:hasPresentation ?w. 21 | ?w a sd:VariablePresentation . 22 | } UNION { 23 | ?config ?prop ?d. 24 | ?d a sd:DatasetSpecification. 25 | ?d sd:hasPresentation/sd:hasStandardVariable ?w. 26 | ?w a . 27 | } 28 | ?w ?property ?desc. 29 | FILTER REGEX(str(?desc), ?_label, "i") 30 | VALUES ?property { sd:description rdfs:label } 31 | } 32 | } 33 | ?model ?predicate ?prop 34 | OPTIONAL { 35 | ?prop a ?type 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/modelcatalog_full/custom_queries/custom_modelconfigurations.rq: -------------------------------------------------------------------------------- 1 | PREFIX sd: 2 | PREFIX sdm: 3 | PREFIX rdfs: 4 | 5 | 6 | CONSTRUCT { 7 | ?_resource_iri ?predicate ?prop . 8 | ?prop ?p ?o . 9 | ?input ?input_p ?input_o . 10 | ?output ?output_p ?output_o . 11 | ?parameter ?parameter_p ?parameter_o 12 | } 13 | WHERE { 14 | GRAPH ?_g_iri { 15 | { 16 | ?_resource_iri ?predicate ?prop . 17 | OPTIONAL { 18 | ?prop ?p ?o 19 | } 20 | } 21 | UNION { 22 | ?_resource_iri sd:hasInput ?input . 23 | ?input ?input_p ?input_o 24 | } 25 | UNION { 26 | ?_resource_iri sd:hasOutput ?output . 27 | ?output ?output_p ?output_o 28 | } 29 | UNION { 30 | ?_resource_iri sd:hasParameter ?parameter . 31 | ?parameter ?parameter_p ?parameter_o 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /examples/modelcatalog_full/custom_queries/custom_modelconfigurationsetups.rq: -------------------------------------------------------------------------------- 1 | PREFIX sd: 2 | PREFIX rdfs: 3 | PREFIX sdm: 4 | 5 | CONSTRUCT { 6 | ?_resource_iri ?predicate ?prop . 7 | ?prop ?p ?o . 8 | ?input_resource ?input_resource_p ?input_resource_o . 9 | ?output_resource ?output_resource_p ?output_resource_o . 10 | ?parameter ?parameter_p ?parameter_o . 11 | ?region ?region_p ?region_o . 12 | ?region_o ?sub_region_p ?sub_region_o . 13 | ?grid ?grid_p ?grid_o . 14 | ?process ?process_p ?process_o . 15 | ?variable_presentation ?variable_presentation_o ?variable_presentation_p . 16 | ?variable_presentation_output ?variable_presentation_o_output ?variable_presentation_p_output . 17 | ?variable_presentation_p ?variable_o ?variable_p . 18 | ?variable_presentation_p_output ?variable_o_output ?variable_p_output . 19 | ?unit ?unit_p ?unit_o 20 | } 21 | WHERE { 22 | GRAPH ?_g_iri { 23 | { 24 | ?_resource_iri ?predicate ?prop . 25 | OPTIONAL { 26 | ?prop ?p ?o . 27 | FILTER (?p != ) . 28 | FILTER (?p != ) 29 | } 30 | } 31 | UNION { 32 | ?_resource_iri sd:hasInput ?input 33 | OPTIONAL { 34 | ?input sd:hasFixedResource ?input_resource . 35 | ?input_resource ?input_resource_p ?input_resource_o 36 | } 37 | OPTIONAL { 38 | ?input sd:hasPresentation ?variable_presentation . 39 | ?variable_presentation ?variable_presentation_o ?variable_presentation_p . 40 | ?variable_presentation_p ?variable_o ?variable_p 41 | } 42 | 43 | } 44 | UNION { 45 | ?_resource_iri sdm:hasOutputTimeInterval ?output_time_interval_resource . 46 | ?output_time_interval_resource sdm:intervalUnit ?unit . 47 | OPTIONAL { 48 | ?unit ?unit_p ?unit_o 49 | } 50 | } 51 | UNION { 52 | ?_resource_iri sd:hasOutput ?output_resource . 53 | ?output_resource ?output_resource_p ?output_resource_o 54 | OPTIONAL { 55 | ?output_resource sd:hasPresentation ?variable_presentation_output . 56 | ?variable_presentation_output ?variable_presentation_o_output ?variable_presentation_p_output . 57 | ?variable_presentation_p_output ?variable_o_output ?variable_p_output 58 | 59 | } 60 | } 61 | UNION { 62 | ?_resource_iri sd:hasParameter ?parameter . 63 | ?parameter ?parameter_p ?parameter_o 64 | } 65 | UNION { 66 | ?_resource_iri sdm:hasRegion ?region . 67 | ?region ?region_p ?region_o . 68 | OPTIONAL { 69 | ?region_o ?sub_region_p ?sub_region_o 70 | FILTER (?sub_region_p != ) . 71 | 72 | } 73 | FILTER (?region_p != ) 74 | } 75 | UNION { 76 | ?_resource_iri sdm:hasGrid ?grid . 77 | ?grid ?grid_p ?grid_o . 78 | } 79 | UNION { 80 | ?_resource_iri sdm:hasProcess ?process . 81 | ?process ?process_p ?process_o . 82 | FILTER (?process_p != ) 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /examples/modelcatalog_full/custom_queries/custom_modelconfigurationsetups_variable.rq: -------------------------------------------------------------------------------- 1 | #+ summary: Given a rdf type, returns all the resources related to the type 2 | 3 | PREFIX sd: 4 | PREFIX sdm: 5 | PREFIX rdfs: 6 | 7 | 8 | CONSTRUCT { 9 | ?setup ?predicate ?prop . 10 | ?prop a ?type . 11 | ?region ?region_p ?region_o . 12 | ?region_o ?sub_region_p ?sub_region_o . 13 | } 14 | WHERE { 15 | GRAPH ?_g_iri { 16 | { 17 | SELECT DISTINCT ?setup { 18 | ?setup a sdm:ModelConfigurationSetup . 19 | { 20 | ?setup sd:hasOutput/sd:hasPresentation/sd:hasStandardVariable ?sv. 21 | } UNION { 22 | ?model sd:hasVersion/sd:hasConfiguration/sd:hasSetup ?setup . 23 | ?model sd:usefulForCalculatingIndex/sd:hasStandardVariable ?sv . 24 | } 25 | ?sv rdfs:label ?variableLabel . 26 | FILTER REGEX(?variableLabel, ?_label, "i") 27 | } 28 | } 29 | { 30 | ?setup ?predicate ?prop 31 | OPTIONAL { 32 | ?prop a ?type 33 | } 34 | } 35 | UNION { 36 | ?setup sdm:hasRegion ?region . 37 | ?region ?region_p ?region_o . 38 | FILTER (?region_p != ) . 39 | OPTIONAL { 40 | ?region_o ?sub_region_p ?sub_region_o . 41 | FILTER (?sub_region_p != ) 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /examples/modelcatalog_full/custom_queries/custom_models_variable.rq: -------------------------------------------------------------------------------- 1 | #+ summary: Given a rdf type, returns all the resources related to the type 2 | #+ - graph: http://ontosoft.isi.edu:3030/modelCatalog-1.2.0/data/mint@isi.edu 3 | 4 | PREFIX sd: 5 | PREFIX sdm: 6 | PREFIX rdfs: 7 | 8 | 9 | CONSTRUCT { 10 | ?model ?predicate ?prop . 11 | ?prop a ?type 12 | } 13 | WHERE { 14 | GRAPH ?_g_iri { 15 | { 16 | SELECT DISTINCT ?model { 17 | ?model sd:usefulForCalculatingIndex ?sv . 18 | ?sv rdfs:label ?variableLabel 19 | FILTER REGEX(?variableLabel, ?_label, "i") 20 | } 21 | } 22 | ?model ?predicate ?prop 23 | OPTIONAL { 24 | ?prop a ?type 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/opmw/config.yaml: -------------------------------------------------------------------------------- 1 | ontologies: 2 | - examples\opmw\opmw.owl 3 | #- http://www.opmw.org/ontology/ 4 | #Example with a slash ontology 5 | name: opmw 6 | output_dir: outputs 7 | 8 | openapi: 9 | openapi: 3.0.1 10 | info: 11 | description: Example of the opmw ontology 12 | title: opmw 13 | version: v1.0.0 14 | externalDocs: 15 | description: open provenance ontology for workflows 16 | url: http://datos.opmw.es/def/ 17 | 18 | firebase: 19 | key: 20 | 21 | #endpoint not used for this example. 22 | endpoint: 23 | url: http://endpoint.mint.isi.edu/example 24 | prefix: https://w3id.org/okn/i/example 25 | graph_base: http://endpoint.mint.isi.edu/example 26 | 27 | enable_get_paths: true 28 | enable_post_paths: false 29 | enable_delete_paths: false 30 | enable_put_paths: false 31 | 32 | follow_references: true 33 | 34 | -------------------------------------------------------------------------------- /examples/pplan/config.yaml: -------------------------------------------------------------------------------- 1 | ontologies: 2 | - http://purl.org/net/p-plan 3 | name: p-plan 4 | output_dir: outputs 5 | 6 | openapi: 7 | openapi: 3.0.1 8 | info: 9 | description: Example of the P-Plan ontology 10 | title: P-Plan 11 | version: v1.0.0 12 | externalDocs: 13 | description: P-Plan 14 | url: http://purl.org/net/p-plan 15 | 16 | firebase: 17 | key: 18 | 19 | #endpoint not used for this example. 20 | endpoint: 21 | url: http://endpoint.mint.isi.edu/example 22 | prefix: https://w3id.org/okn/i/example 23 | graph_base: http://endpoint.mint.isi.edu/example 24 | 25 | enable_get_paths: true 26 | enable_post_paths: false 27 | enable_delete_paths: false 28 | enable_put_paths: false 29 | 30 | follow_references: true 31 | 32 | -------------------------------------------------------------------------------- /examples/pplan/readme.md: -------------------------------------------------------------------------------- 1 | ## Model catalog: reduced example 2 | 3 | This example illustrates how to load ontologies from local files. 4 | 5 | Note that this example was tested on Windows (hence the file path) and the working directory is assumed to be the above the "examples" folder -------------------------------------------------------------------------------- /examples/restrictions/config.yaml: -------------------------------------------------------------------------------- 1 | ontologies: 2 | - examples/restrictions/example.owl 3 | name: Restrictions example 4 | output_dir: outputs 5 | 6 | openapi: 7 | openapi: 3.0.1 8 | info: 9 | description: This is the API for the restrictions example ontology 10 | title: Restrictions example ontology 11 | version: v1.5.0 12 | externalDocs: 13 | description: Restrictions example ontology 14 | url: https://w3id.org/example 15 | servers: 16 | - url: http://localhost:8080/v1.5.0 17 | 18 | firebase: 19 | key: "test" 20 | 21 | endpoint: 22 | url: http://localhost:8080/example-1.5.0 23 | prefix: https://w3id.org/example 24 | graph_base: http://localhost:8080/example-1.5.0/data/ 25 | 26 | enable_get_paths: true 27 | enable_post_paths: true 28 | enable_delete_paths: true 29 | enable_put_paths: true 30 | 31 | auth: 32 | provider: firebase 33 | follow_references: true 34 | -------------------------------------------------------------------------------- /examples/restrictions/readme.md: -------------------------------------------------------------------------------- 1 | ## Restrictions example ontology 2 | 3 | This restrictions example ontology illustrates several ontology constructs to show how to represent them in the OAS. 4 | 5 | Note that this example was tested on macOS (hence the file path) and the working directory is assumed to be the above the "examples" folder 6 | -------------------------------------------------------------------------------- /examples/saref4agri/config.yaml: -------------------------------------------------------------------------------- 1 | ontologies: 2 | - https://w3id.org/def/saref4agri 3 | #test using w3id 4 | name: saref4agri 5 | output_dir: outputs 6 | 7 | openapi: 8 | openapi: 3.0.1 9 | info: 10 | description: Example with the Saref4agri ontology (generating a specifciation draft) 11 | title: Saref4agri 12 | version: v1.0.0 13 | externalDocs: 14 | description: Saref4agri ontology 15 | url: https://w3id.org/def/saref4city 16 | 17 | firebase: 18 | key: 19 | 20 | endpoint: 21 | url: http://endpoint.mint.isi.edu/example 22 | prefix: https://w3id.org/okn/i/example 23 | graph_base: http://endpoint.mint.isi.edu/example 24 | 25 | 26 | enable_get_paths: true 27 | enable_post_paths: false 28 | enable_delete_paths: false 29 | enable_put_paths: false 30 | 31 | follow_references: true 32 | 33 | -------------------------------------------------------------------------------- /examples/saref4city/config.yaml: -------------------------------------------------------------------------------- 1 | ontologies: 2 | - https://raw.githubusercontent.com/mariapoveda/saref-ext/master/SAREF4CITY/ontology/saref4city.ttl 3 | #Test using directly a raw file URL 4 | name: saref4city 5 | output_dir: outputs 6 | 7 | openapi: 8 | openapi: 3.0.1 9 | info: 10 | description: Example with the Saref4city ontology 11 | title: Saref4city 12 | version: v1.0.0 13 | externalDocs: 14 | description: Saref4city 15 | url: https://w3id.org/def/saref4city 16 | 17 | firebase: 18 | key: 19 | 20 | endpoint: 21 | url: http://endpoint.mint.isi.edu/example 22 | prefix: https://w3id.org/okn/i/example 23 | graph_base: http://endpoint.mint.isi.edu/example 24 | 25 | enable_get_paths: true 26 | enable_post_paths: false 27 | enable_delete_paths: false 28 | enable_put_paths: false 29 | 30 | follow_references: true 31 | -------------------------------------------------------------------------------- /examples/testConfig/config.yaml: -------------------------------------------------------------------------------- 1 | ontologies: 2 | - examples/testConfig/ontology.ttl 3 | name: test 4 | output_dir: outputs 5 | 6 | openapi: 7 | openapi: 3.0.1 8 | info: 9 | description: This is a test. 10 | title: Test 11 | version: v1.5.0 12 | externalDocs: 13 | description: Test 14 | url: https://w3id.org/okn/o/sd 15 | servers: 16 | - url: http://localhost:8080/v1.5.0 17 | 18 | firebase: 19 | key: "test" 20 | 21 | endpoint: 22 | url: http://endpoint.mint.isi.edu/modelCatalog-1.5.0 23 | prefix: https://w3id.org/okn/i/example 24 | graph_base: http://endpoint.mint.isi.edu/modelCatalog-1.5.0/data/ 25 | 26 | enable_get_paths: true 27 | enable_post_paths: false 28 | enable_delete_paths: false 29 | enable_put_paths: false 30 | 31 | auth: 32 | provider: firebase 33 | 34 | follow_references: true 35 | 36 | -------------------------------------------------------------------------------- /examples/wings/config.yaml: -------------------------------------------------------------------------------- 1 | ontologies: 2 | - http://www.wings-workflows.org/ontology/workflow.owl 3 | - http://www.wings-workflows.org/ontology/data.owl 4 | - http://www.wings-workflows.org/ontology/component.owl 5 | name: wings 6 | output_dir: outputs 7 | 8 | openapi: 9 | openapi: 3.0.1 10 | info: 11 | description: This is the API of the Wings Ontology 12 | title: Wings Workflow Catalog 13 | version: v0.0.1 14 | externalDocs: 15 | description: Wings Workflow Catalog 16 | url: https://w3id.org/okn/o/sdm 17 | servers: 18 | - url: http://localhost:8080/v0.0.1 19 | 20 | 21 | endpoint: 22 | url: http://localhost:3030/wings 23 | prefix: http://www.wings-workflows.org/ontology 24 | graph_base: http://localhost:3030/wings/data/ 25 | 26 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: OBA documentation 2 | nav: 3 | - Home: index.md 4 | - OBA features: features.md 5 | - 'Generate an OpenAPI specification': 6 | - 'Quickstart...': 'quickstart.md' 7 | - 'OBA Output': 'result.md' 8 | - 'Class filtering': 'filtering.md' 9 | - 'OWL to OAS mapping': 'mapping.md' 10 | - 'Generate the server': 11 | - 'Quickstart...': 'server.md' 12 | - 'Running the server': 'running_server.md' 13 | - 'Testing your API': 'test.md' 14 | - 'Adding custom queries': 'adding_custom_queries.md' 15 | - 'Authentication': 'authentication.md' 16 | - Configuration file: 'configuration_file.md' 17 | - Examples: examples.md 18 | - Performance tips: benchmarking.md 19 | theme: 20 | name: material 21 | 22 | markdown_extensions: 23 | - admonition 24 | - codehilite: 25 | guess_lang: false 26 | - toc: 27 | permalink: true 28 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | jar 7 | edu.isi.oba 8 | oba 9 | 3.9.1 10 | core 11 | https://github.com/KnowledgeCaptureAndDiscovery/OBA 12 | 13 | 14 | 15 | UTF-8 16 | 11 17 | 11 18 | 19 | 3.5.3 20 | 21 | 22 | 23 | 24 | 25 | org.json 26 | json 27 | 20240303 28 | 29 | 30 | 31 | 32 | org.junit.jupiter 33 | junit-jupiter-api 34 | 5.10.2 35 | test 36 | 37 | 38 | 39 | 40 | io.swagger 41 | swagger-compat-spec-parser 42 | 1.0.71 43 | 44 | 45 | 46 | io.swagger.core.v3 47 | swagger-models 48 | 2.2.21 49 | 50 | 51 | 52 | io.swagger.parser.v3 53 | swagger-parser 54 | 2.1.22 55 | 56 | 57 | 58 | io.swagger.parser.v3 59 | swagger-parser-core 60 | 2.1.22 61 | 62 | 63 | 64 | 65 | org.openapitools 66 | openapi-generator 67 | 7.5.0 68 | 69 | 70 | 71 | 72 | net.sourceforge.owlapi 73 | owlapi-api 74 | 5.5.0 75 | 76 | 77 | 78 | net.sourceforge.owlapi 79 | owlapi-apibinding 80 | 5.5.0 81 | 82 | 83 | 84 | net.sourceforge.owlapi 85 | owlapi-impl 86 | 5.5.0 87 | 88 | 89 | 90 | 91 | org.yaml 92 | snakeyaml 93 | 2.2 94 | 95 | 96 | 97 | 98 | commons-cli 99 | commons-cli 100 | 1.7.0 101 | 102 | 103 | 104 | 105 | 106 | com.fasterxml.jackson.core 107 | jackson-core 108 | 2.17.1 109 | 110 | 111 | 112 | 113 | org.jibx 114 | jibx-tools 115 | 1.4.2 116 | 117 | 118 | 119 | 120 | 121 | 122 | ${project.basedir}/src/main/resources 123 | 124 | 125 | 126 | 127 | 128 | org.apache.maven.plugins 129 | maven-eclipse-plugin 130 | 2.10 131 | 132 | true 133 | false 134 | 135 | 136 | 137 | 138 | 139 | org.apache.maven.plugins 140 | maven-compiler-plugin 141 | 3.13.0 142 | 143 | ${jdk.version} 144 | ${jdk.version} 145 | 146 | 147 | 148 | 149 | 150 | org.apache.maven.plugins 151 | maven-surefire-plugin 152 | 3.2.5 153 | 154 | 155 | 156 | 157 | maven-assembly-plugin 158 | 159 | 160 | package 161 | 162 | single 163 | 164 | 165 | 166 | 167 | 168 | 169 | edu.isi.oba.Oba 170 | 171 | 172 | 173 | jar-with-dependencies 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | -------------------------------------------------------------------------------- /src/main/java/edu/isi/oba/Oba.java: -------------------------------------------------------------------------------- 1 | package edu.isi.oba; 2 | 3 | import edu.isi.oba.config.*; 4 | 5 | import io.swagger.v3.oas.models.OpenAPI; 6 | import io.swagger.v3.oas.models.PathItem; 7 | 8 | import java.io.*; 9 | import java.nio.file.Path; 10 | import java.nio.file.Paths; 11 | import java.util.*; 12 | import java.util.logging.ConsoleHandler; 13 | import java.util.logging.Level; 14 | import java.util.logging.LogManager; 15 | import java.util.logging.Logger; 16 | 17 | import org.json.JSONObject; 18 | 19 | class Oba { 20 | public static final String SERVERS_ZIP = "/servers.zip"; 21 | public static final String SERVERS_DIRECTORY = "servers"; 22 | static Logger logger = null; 23 | public enum LANGUAGE { 24 | PYTHON_FLASK 25 | } 26 | 27 | public static void main(String[] args) throws Exception { 28 | /* 29 | TODO: we are supporting one language. Issue #42 30 | */ 31 | 32 | InputStream stream = Oba.class.getClassLoader().getResourceAsStream("logging.properties"); 33 | try { 34 | LogManager.getLogManager().readConfiguration(stream); 35 | logger = Logger.getLogger(Oba.class.getName()); 36 | logger.setUseParentHandlers(false);//remove double logging 37 | 38 | } catch (IOException e) { 39 | e.printStackTrace(); 40 | } 41 | 42 | LANGUAGE selected_language = LANGUAGE.PYTHON_FLASK; 43 | logger.setLevel(Level.FINE); 44 | logger.addHandler(new ConsoleHandler()); 45 | 46 | //parse command line 47 | String config_yaml = ObaUtils.get_config_yaml(args); 48 | //read the config yaml from command line 49 | YamlConfig config_data = new YamlConfig(); 50 | 51 | try { 52 | config_data = ObaUtils.get_yaml_data(config_yaml); 53 | } catch (Exception e){ 54 | logger.severe("Error parsing the configuration file. Please make sure it is valid \n " + e); 55 | System.exit(1); 56 | } 57 | 58 | String destination_dir = config_data.getOutput_dir() + File.separator + config_data.getName(); 59 | FirebaseConfig firebase_data = config_data.getFirebase(); 60 | AuthConfig authConfig = config_data.getAuth(); 61 | if (authConfig != null) { 62 | 63 | Provider provider = authConfig.getProvider_obj(); 64 | if (provider.equals(Provider.FIREBASE) && firebase_data.getKey() == null) { 65 | logger.severe("You must set up the firebase key"); 66 | System.exit(1); 67 | } 68 | } else { 69 | config_data.setAuth(new AuthConfig()); 70 | } 71 | 72 | try { 73 | Mapper mapper = new Mapper(config_data); 74 | mapper.createSchemas(destination_dir); 75 | 76 | LinkedHashMap custom_paths = config_data.getCustom_paths(); 77 | OpenAPI openapi_base = config_data.getOpenapi(); 78 | String custom_queries_dir = config_data.getCustom_queries_directory(); 79 | 80 | //copy base project 81 | ObaUtils.unZipIt(Oba.SERVERS_ZIP, destination_dir); 82 | //get schema and paths 83 | generate_openapi_spec(openapi_base, mapper, destination_dir, custom_paths); 84 | generate_openapi_template(mapper, destination_dir, config_data, selected_language); 85 | generate_context(config_data, destination_dir); 86 | copy_custom_queries(custom_queries_dir, destination_dir); 87 | logger.info("OBA finished successfully. Output can be found at: " + destination_dir); 88 | } catch (Exception e) { 89 | logger.severe("Error while creating the API specification: " + e.getLocalizedMessage()); 90 | e.printStackTrace(); 91 | System.exit(1); 92 | } 93 | } 94 | 95 | private static void generate_context(YamlConfig config_data, String destination_dir) { 96 | List ontologies = config_data.getOntologies(); 97 | JSONObject context_json_object = null; 98 | JSONObject context_json_object_class = null; 99 | try { 100 | context_json_object = ObaUtils.generate_context_file(ontologies.toArray(new String[0]), false); 101 | context_json_object_class = ObaUtils.generate_context_file(ontologies.toArray(new String[0]), true); 102 | } catch (Exception e) { 103 | e.printStackTrace(); 104 | } 105 | String file_path = destination_dir + File.separator + "servers" + File.separator + "context.json"; 106 | String file_path_class = destination_dir + File.separator + "servers" + File.separator + "context_class.json"; 107 | try { 108 | ObaUtils.write_file(file_path, context_json_object.toString(4)); 109 | ObaUtils.write_file(file_path_class, context_json_object_class.toString(4)); 110 | } catch(Exception e) { 111 | logger.severe("Could not generate the context files: "+e.getMessage()); 112 | } 113 | } 114 | 115 | private static void copy_custom_queries(String source, String destination) { 116 | if (source != null) { 117 | try { 118 | ObaUtils.copyFolder(new File(source), new File(destination + File.separator + "queries" + File.separator + "custom")); 119 | } catch (IOException e) { 120 | logger.severe("The directory custom queries doesn't exists "); 121 | } 122 | } 123 | } 124 | 125 | private static void generate_openapi_template(Mapper mapper, 126 | String destination_directory, 127 | YamlConfig config, 128 | LANGUAGE language) throws Exception { 129 | switch (language) { 130 | case PYTHON_FLASK: 131 | new SerializerPython(mapper, destination_directory, config); 132 | break; 133 | default: 134 | logger.severe("The language selected is not supported"); 135 | } 136 | 137 | } 138 | 139 | private static void generate_openapi_spec(OpenAPI openapi_base, 140 | Mapper mapper, 141 | String dir, 142 | LinkedHashMap custom_paths 143 | ) throws Exception { 144 | String destinationProjectDirectory = dir + File.separator + Oba.SERVERS_DIRECTORY; 145 | Path destinationProject = Paths.get(destinationProjectDirectory); 146 | new Serializer(mapper, destinationProject, openapi_base, custom_paths); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/main/java/edu/isi/oba/ObaManager.java: -------------------------------------------------------------------------------- 1 | package edu.isi.oba; 2 | 3 | public class ObaManager { 4 | public void filter() { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/edu/isi/oba/PathGenerator.java: -------------------------------------------------------------------------------- 1 | package edu.isi.oba; 2 | 3 | import edu.isi.oba.config.CONFIG_FLAG; 4 | 5 | import io.swagger.models.Method; 6 | import io.swagger.v3.oas.models.Operation; 7 | import io.swagger.v3.oas.models.PathItem; 8 | import io.swagger.v3.oas.models.headers.Header; 9 | import io.swagger.v3.oas.models.media.*; 10 | import io.swagger.v3.oas.models.parameters.RequestBody; 11 | import io.swagger.v3.oas.models.responses.ApiResponse; 12 | import io.swagger.v3.oas.models.responses.ApiResponses; 13 | 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | 17 | class PathGenerator { 18 | private final Map configFlags = new HashMap<>(); 19 | private Boolean auth; 20 | 21 | public PathGenerator(Map configFlags, Boolean auth) { 22 | this.auth = auth; 23 | this.configFlags.putAll(configFlags); 24 | } 25 | 26 | public PathItem generate_singular(String schemaName, String schemaURI) { 27 | PathItem path_item = new PathItem(); 28 | if (this.configFlags.get(CONFIG_FLAG.PATH_GET)) { 29 | path_item.get(new MapperOperation(schemaName, schemaURI, Method.GET, Cardinality.SINGULAR, auth).getOperation()); 30 | } 31 | 32 | if (this.configFlags.get(CONFIG_FLAG.PATH_DELETE)) { 33 | path_item.delete(new MapperOperation(schemaName, schemaURI, Method.DELETE, Cardinality.SINGULAR, auth).getOperation()); 34 | } 35 | 36 | if (this.configFlags.get(CONFIG_FLAG.PATH_POST)) { 37 | path_item.put(new MapperOperation(schemaName, schemaURI, Method.POST, Cardinality.SINGULAR, auth).getOperation()); 38 | } 39 | 40 | if (this.configFlags.get(CONFIG_FLAG.PATH_PUT)) { 41 | path_item.put(new MapperOperation(schemaName, schemaURI, Method.PUT, Cardinality.SINGULAR, auth).getOperation()); 42 | } 43 | 44 | return path_item; 45 | } 46 | 47 | 48 | public PathItem generate_plural(String schemaName, String schemaURI) { 49 | PathItem path_item = new PathItem(); 50 | if (this.configFlags.get(CONFIG_FLAG.PATH_GET)) { 51 | path_item.get(new MapperOperation(schemaName, schemaURI, Method.GET, Cardinality.PLURAL, auth).getOperation()); 52 | } 53 | 54 | if (this.configFlags.get(CONFIG_FLAG.PATH_POST)) { 55 | path_item.post(new MapperOperation(schemaName,schemaURI, Method.POST, Cardinality.PLURAL, auth).getOperation()); 56 | } 57 | 58 | return path_item; 59 | } 60 | 61 | 62 | public static PathItem user_login(String schema_name) { 63 | ApiResponses apiResponses = new ApiResponses(); 64 | 65 | final RequestBody requestBody = new RequestBody(); 66 | 67 | String ref_text = "#/components/schemas/" + schema_name; 68 | Schema schema = new Schema().$ref(ref_text); 69 | 70 | MediaType mediaType = new MediaType().schema(schema); 71 | Content content = new Content().addMediaType("application/json", mediaType); 72 | requestBody.setContent(content); 73 | String requestDescription = "User credentials"; 74 | requestBody.setDescription(requestDescription); 75 | 76 | 77 | Map headers = new HashMap<>(); 78 | headers.put("X-Rate-Limit", new Header().description("calls per hour allowed by the user"). 79 | schema(new IntegerSchema().format("int32"))); 80 | headers.put("X-Expires-After", new Header().description("date in UTC when token expires"). 81 | schema(new StringSchema().format("date-time"))); 82 | apiResponses.addApiResponse("200", new ApiResponse() 83 | .description("successful operation") 84 | .headers(headers) 85 | .content(new Content().addMediaType("application/json", new MediaType().schema(new StringSchema())))); 86 | 87 | apiResponses.addApiResponse("400", new ApiResponse() 88 | .description("unsuccessful operation") 89 | .content(new Content().addMediaType("application/json", new MediaType().schema(new StringSchema())))); 90 | 91 | Map extensions = new HashMap(); 92 | extensions.put("x-openapi-router-controller", "openapi_server.controllers.user_controller"); 93 | Operation operation = new Operation() 94 | .description("Login the user") 95 | .extensions(extensions) 96 | .requestBody(requestBody) 97 | .responses(apiResponses); 98 | return new PathItem().post(operation); 99 | } 100 | } -------------------------------------------------------------------------------- /src/main/java/edu/isi/oba/Query.java: -------------------------------------------------------------------------------- 1 | package edu.isi.oba; 2 | 3 | import java.io.*; 4 | 5 | class Query { 6 | 7 | private String query_directory; 8 | private static final String get_all_query_file = "/queries/get_all.rq"; 9 | private static final String get_all_graph_query_file = "/queries/get_all_user.rq"; 10 | 11 | private static final String get_one_query_file = "/queries/get_one.rq"; 12 | private static final String get_one_graph_query_file = "/queries/get_one_user.rq"; 13 | 14 | private static final String get_all_search_query_file = "/queries/get_all_search.rq"; 15 | private static final String get_all_search_graph_query_file = "/queries/get_all_search_user.rq"; 16 | 17 | 18 | public Query(String query_directory) { 19 | this.query_directory = query_directory + File.separator + "queries"; 20 | } 21 | 22 | public void get_all(String schema_name) throws Exception { 23 | String dir_path = query_directory + File.separator + schema_name + File.separator; 24 | write_query(get_all_query_file, schema_name); 25 | write_query(get_all_graph_query_file, schema_name); 26 | write_query(get_one_query_file, schema_name); 27 | write_query(get_one_graph_query_file, schema_name); 28 | write_query(get_all_search_query_file, schema_name); 29 | write_query(get_all_search_graph_query_file, schema_name); 30 | } 31 | 32 | public void write_readme(String schema_name) { 33 | String dir_path = query_directory + File.separator + schema_name + File.separator; 34 | String file_path = dir_path + File.separator + "README"; 35 | File directory = new File(dir_path); 36 | if (! directory.exists()){ 37 | directory.mkdirs(); 38 | } 39 | 40 | 41 | BufferedWriter writer = null; 42 | try { 43 | writer = new BufferedWriter(new FileWriter(file_path)); 44 | writer.write("To modify the query from this class, edit this file"); 45 | writer.close(); 46 | } catch (IOException e) { 47 | e.printStackTrace(); 48 | } 49 | } 50 | 51 | private void write_query(String query_file_name, String schema_name) throws Exception { 52 | String dir_path = query_directory + File.separator + schema_name + File.separator; 53 | File directory = new File(dir_path); 54 | if (! directory.exists()){ 55 | directory.mkdirs(); 56 | } 57 | File resource = new File(ObaUtils.class.getResource(query_file_name).getFile()); 58 | InputStream query_file = ObaUtils.class.getResourceAsStream(query_file_name); 59 | File file_path = new File(dir_path + File.separator + resource.getName()); 60 | ObaUtils.copy(query_file, file_path); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/edu/isi/oba/Serializer.java: -------------------------------------------------------------------------------- 1 | package edu.isi.oba; 2 | 3 | import java.io.BufferedWriter; 4 | import java.io.File; 5 | import java.io.FileWriter; 6 | import java.util.*; 7 | 8 | import io.swagger.parser.OpenAPIParser; 9 | import io.swagger.v3.oas.models.*; 10 | import io.swagger.v3.oas.models.security.SecurityScheme; 11 | import io.swagger.v3.parser.core.models.ParseOptions; 12 | import io.swagger.v3.parser.core.models.SwaggerParseResult; 13 | 14 | import org.openapitools.codegen.serializer.SerializerUtils; 15 | 16 | class Serializer { 17 | //TODO: validate the yaml 18 | String openapi_path; 19 | public Serializer(Mapper mapper, java.nio.file.Path dir, OpenAPI openAPI, LinkedHashMap custom_paths) throws Exception { 20 | Map extensions = new HashMap(); 21 | final String openapi_file = "openapi.yaml"; 22 | 23 | //Generate security schema 24 | Map securitySchemes = new HashMap(); 25 | SecurityScheme securityScheme = getSecurityScheme(extensions); 26 | securitySchemes.put("BearerAuth", securityScheme); 27 | 28 | Components components = new Components(); 29 | Paths paths = new Paths(); 30 | mapper.paths.forEach((k, v) -> paths.addPathItem(k, v)); 31 | mapper.schemas.forEach((k, v) -> components.addSchemas(k, v)); 32 | components.securitySchemes(securitySchemes); 33 | 34 | //add custom paths 35 | Map custom_extensions = new HashMap(); 36 | custom_extensions.put("x-oba-custom", true); 37 | 38 | if (custom_paths != null) 39 | custom_paths.forEach((k, v) -> { 40 | System.out.println("inserting custom query " + k); 41 | v.setExtensions(custom_extensions); 42 | paths.addPathItem(k, v); 43 | }); 44 | 45 | openAPI.setPaths(paths); 46 | openAPI.components(components); 47 | 48 | //write the filename 49 | String content = SerializerUtils.toYamlString(openAPI); 50 | this.openapi_path = dir + File.separator + openapi_file; 51 | File file = new File(openapi_path); 52 | BufferedWriter writer = new BufferedWriter(new FileWriter(file.getAbsoluteFile())); 53 | writer.write(content); 54 | writer.close(); 55 | this.validate(); 56 | } 57 | 58 | private void validate() throws Exception { 59 | ParseOptions options = new ParseOptions(); 60 | options.setResolve(true); 61 | SwaggerParseResult result = new OpenAPIParser().readLocation(openapi_path, null, options); 62 | List messageList = result.getMessages(); 63 | Set errors = new HashSet<>(messageList); 64 | Set warnings = new HashSet<>(); 65 | if (!errors.isEmpty()) { 66 | throw new Exception("Error when validating the API specification. " + errors.iterator().next()); 67 | } 68 | } 69 | 70 | private SecurityScheme getSecurityScheme(Map extensions) { 71 | extensions.put("x-bearerInfoFunc", "openapi_server.controllers.user_controller.decode_token"); 72 | SecurityScheme securityScheme = new SecurityScheme(); 73 | securityScheme.setType(SecurityScheme.Type.HTTP); 74 | securityScheme.bearerFormat("JWT"); 75 | securityScheme.setScheme("bearer"); 76 | securityScheme.setExtensions(extensions); 77 | return securityScheme; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/edu/isi/oba/SerializerPython.java: -------------------------------------------------------------------------------- 1 | package edu.isi.oba; 2 | 3 | 4 | import edu.isi.oba.config.*; 5 | 6 | import org.semanticweb.owlapi.model.IRI; 7 | 8 | import java.io.File; 9 | import java.io.FileWriter; 10 | import java.io.IOException; 11 | import java.util.*; 12 | 13 | import static edu.isi.oba.Oba.SERVERS_DIRECTORY; 14 | import static edu.isi.oba.Oba.logger; 15 | 16 | class SerializerPython { 17 | static final String name = "python"; 18 | static final String STATIC_DIRECTORY = SERVERS_DIRECTORY + File.separator + name + File.separator + ".openapi-generator/template/static_files/"; 19 | static final String UTILS_DIR = STATIC_DIRECTORY + File.separator + "utils"; 20 | static final String VARIABLES_FILE = UTILS_DIR + File.separator + "vars.py"; 21 | static final String CONFIG_FILE = STATIC_DIRECTORY + File.separator + "settings" + File.separator + "config.ini"; 22 | 23 | private String utils_dir; 24 | private String variables_file; 25 | private String config_file; 26 | 27 | String base; 28 | public SerializerPython(Mapper mapper, 29 | String base, 30 | YamlConfig config) throws Exception { 31 | 32 | this.base = base; 33 | this.utils_dir = base + File.separator + UTILS_DIR; 34 | this.variables_file = base + File.separator + VARIABLES_FILE; 35 | this.config_file = base + File.separator + CONFIG_FILE; 36 | 37 | //Create directory utils 38 | File utils_dir_path = new File(utils_dir); 39 | utils_dir_path.mkdir(); 40 | 41 | //Create variable file 42 | FileWriter var_file_writer = new FileWriter(variables_file); 43 | Iterator it = mapper.schemaNames.entrySet().iterator(); 44 | add_variable_python(var_file_writer, it); 45 | var_file_writer.close(); 46 | 47 | 48 | //Create the config.ini 49 | try { 50 | EndpointConfig endpoint_config = config.getEndpoint(); 51 | create_settings_file(endpoint_config.getUrl(), endpoint_config.getPrefix(), endpoint_config.getGraph_base(), config); 52 | }catch(Exception e){ 53 | throw new Exception("Cannot create the endpoint configuration.\n" + 54 | "Please check that an endpoint is included in your config.yaml file\n" + 55 | "If you don't have an endpoint, you can use a placeholder"); 56 | } 57 | 58 | } 59 | 60 | private void create_settings_file(String endpoint, String prefix, String graph_base, YamlConfig config){ 61 | String content = "[defaults]\n" + 62 | "endpoint = " + endpoint + "\n" + 63 | "queries_dir = queries/\n" + 64 | "context_dir = contexts/\n" + 65 | "prefix = " + prefix + "\n" + 66 | "graph_base = " + graph_base + "\n"; 67 | 68 | if (config.getAuth().getEnable() && config.getAuth().getProvider_obj() == Provider.FIREBASE) 69 | content.concat("firebase_key = " + config.getFirebase().getKey() + "\n"); 70 | 71 | ObaUtils.write_file(this.config_file, content); 72 | } 73 | private void add_variable_python(FileWriter myWriter, Iterator it) { 74 | IRI clsIRI; 75 | String name; 76 | while (it.hasNext()) { 77 | Map.Entry pair = (Map.Entry) it.next(); 78 | clsIRI = (IRI) pair.getKey(); 79 | name = (String) pair.getValue(); 80 | try { 81 | String variable_name = name.toUpperCase().replace("-", "").replace("_", ""); 82 | //TODO: Catch class name empty 83 | String catch_temp = name.replace("<", "").replace(">", ""); 84 | if (!catch_temp.equals(clsIRI.toString())) { 85 | myWriter.write(variable_name + "_TYPE_URI = \"" + clsIRI + "\"\n"); 86 | myWriter.write(variable_name + "_TYPE_NAME = \"" + name + "\"\n"); 87 | } 88 | else { 89 | logger.warning("Ignoring class " + clsIRI); 90 | } 91 | } catch (IOException e) { 92 | System.out.println("An error occurred."); 93 | e.printStackTrace(); 94 | } 95 | } 96 | } 97 | } -------------------------------------------------------------------------------- /src/main/java/edu/isi/oba/config/AuthConfig.java: -------------------------------------------------------------------------------- 1 | package edu.isi.oba.config; 2 | 3 | public class AuthConfig { 4 | public Boolean enable = false; 5 | private String provider; 6 | public Provider provider_obj; 7 | 8 | public Provider getProvider_obj() { 9 | return provider_obj; 10 | } 11 | 12 | public void setProvider_obj(Provider provider_obj) { 13 | this.provider_obj = provider_obj; 14 | } 15 | 16 | 17 | public Boolean getEnable() { 18 | return enable; 19 | } 20 | 21 | public void setEnable(Boolean enable) { 22 | this.enable = enable; 23 | } 24 | 25 | public String getProvider() { 26 | return provider; 27 | } 28 | 29 | public void setProvider(String provider) { 30 | this.provider = provider; 31 | this.provider_obj = Provider.get(provider); 32 | this.enable = true; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/edu/isi/oba/config/CONFIG_FLAG.java: -------------------------------------------------------------------------------- 1 | package edu.isi.oba.config; 2 | 3 | public enum CONFIG_FLAG { 4 | ALWAYS_GENERATE_ARRAYS, 5 | DEFAULT_DESCRIPTIONS, 6 | DEFAULT_PROPERTIES, 7 | FOLLOW_REFERENCES, 8 | PATH_DELETE, 9 | PATH_GET, 10 | PATH_PATCH, 11 | PATH_POST, 12 | PATH_PUT, 13 | REQUIRED_PROPERTIES_FROM_CARDINALITY, 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/edu/isi/oba/config/EndpointConfig.java: -------------------------------------------------------------------------------- 1 | package edu.isi.oba.config; 2 | 3 | import edu.isi.oba.ObaUtils; 4 | 5 | public class EndpointConfig { 6 | private String url; 7 | private String prefix; 8 | private String graph_base; 9 | 10 | public String getUrl() { 11 | return url; 12 | } 13 | 14 | public void setUrl(String url) { 15 | this.url = url; 16 | } 17 | 18 | public String getPrefix() { 19 | return prefix; 20 | } 21 | 22 | public void setPrefix(String prefix) { 23 | this.prefix = ObaUtils.check_trailing_slash(prefix); 24 | } 25 | 26 | public String getGraph_base() { 27 | return graph_base; 28 | } 29 | 30 | public void setGraph_base(String graph_base) { 31 | this.graph_base = ObaUtils.check_trailing_slash(graph_base); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/edu/isi/oba/config/FirebaseConfig.java: -------------------------------------------------------------------------------- 1 | package edu.isi.oba.config; 2 | 3 | public class FirebaseConfig { 4 | public String key = ""; 5 | 6 | public String getKey() { 7 | return key; 8 | } 9 | 10 | public void setKey(String key) { 11 | this.key = key; 12 | } 13 | } 14 | 15 | -------------------------------------------------------------------------------- /src/main/java/edu/isi/oba/config/OntologyConfig.java: -------------------------------------------------------------------------------- 1 | package edu.isi.oba.config; 2 | 3 | 4 | public class OntologyConfig { 5 | private String xmlUrl; 6 | private String prefix; 7 | private String prefixUri; 8 | 9 | public String getXmlUrl() { 10 | return xmlUrl; 11 | } 12 | 13 | public void setXmlUrl(String xmlUrl) { 14 | this.xmlUrl = xmlUrl; 15 | } 16 | 17 | public String getPrefix() { 18 | return prefix; 19 | } 20 | 21 | public void setPrefix(String prefix) { 22 | this.prefix = prefix; 23 | } 24 | 25 | public String getPrefixUri() { 26 | return prefixUri; 27 | } 28 | 29 | public void setPrefixUri(String prefixUri) { 30 | this.prefixUri = prefixUri; 31 | } 32 | } 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/main/java/edu/isi/oba/config/Provider.java: -------------------------------------------------------------------------------- 1 | package edu.isi.oba.config; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | 7 | public enum Provider 8 | { 9 | FIREBASE("firebase"); 10 | 11 | private String name; 12 | 13 | Provider(String envUrl) { 14 | this.name = envUrl; 15 | } 16 | 17 | public String getName() { 18 | return name; 19 | } 20 | 21 | //****** Reverse Lookup Implementation************// 22 | 23 | //Lookup table 24 | private static final Map lookup = new HashMap<>(); 25 | 26 | //Populate the lookup table on loading time 27 | static 28 | { 29 | for(Provider env : Provider.values()) 30 | { 31 | lookup.put(env.getName(), env); 32 | } 33 | } 34 | 35 | //This method can be used for reverse lookup purpose 36 | public static Provider get(String url) 37 | { 38 | return lookup.get(url); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/edu/isi/oba/config/RelationConfig.java: -------------------------------------------------------------------------------- 1 | package edu.isi.oba.config; 2 | 3 | public class RelationConfig { 4 | public String getSubject() { 5 | return subject; 6 | } 7 | 8 | public void setSubject(String subject) { 9 | this.subject = subject; 10 | } 11 | 12 | public String getPath() { 13 | return path; 14 | } 15 | 16 | public void setPath(String path) { 17 | this.path = path; 18 | } 19 | 20 | public String getPredicate() { 21 | return predicate; 22 | } 23 | 24 | public void setPredicate(String predicate) { 25 | this.predicate = predicate; 26 | } 27 | 28 | private String subject; 29 | private String path; 30 | private String predicate; 31 | 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/edu/isi/oba/config/YamlConfig.java: -------------------------------------------------------------------------------- 1 | package edu.isi.oba.config; 2 | 3 | import io.swagger.v3.oas.models.OpenAPI; 4 | import io.swagger.v3.oas.models.PathItem; 5 | 6 | import java.util.HashMap; 7 | import java.util.LinkedHashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | public class YamlConfig { 12 | private final Map configFlags = new HashMap<>(){{ 13 | put(CONFIG_FLAG.ALWAYS_GENERATE_ARRAYS, true); 14 | put(CONFIG_FLAG.DEFAULT_DESCRIPTIONS, true); 15 | put(CONFIG_FLAG.DEFAULT_PROPERTIES, true); 16 | put(CONFIG_FLAG.FOLLOW_REFERENCES, true); 17 | put(CONFIG_FLAG.PATH_DELETE, false); 18 | put(CONFIG_FLAG.PATH_GET, true); 19 | put(CONFIG_FLAG.PATH_PATCH, false); 20 | put(CONFIG_FLAG.PATH_POST, false); 21 | put(CONFIG_FLAG.PATH_PUT, false); 22 | put(CONFIG_FLAG.REQUIRED_PROPERTIES_FROM_CARDINALITY, false); 23 | }}; 24 | 25 | String DEFAULT_OUTPUT_DIRECTORY = "outputs"; 26 | String DEFAULT_PROJECT_NAME = "default_project"; 27 | public OpenAPI openapi; 28 | public String output_dir = DEFAULT_OUTPUT_DIRECTORY; 29 | public String name = DEFAULT_PROJECT_NAME; 30 | public List paths; 31 | public List ontologies; 32 | private EndpointConfig endpoint; 33 | public AuthConfig auth; 34 | public FirebaseConfig firebase; 35 | public Map> relations; 36 | private LinkedHashMap custom_paths = null; 37 | public List classes; 38 | public String custom_queries_directory; 39 | 40 | public Boolean getEnable_get_paths() { 41 | return this.configFlags.get(CONFIG_FLAG.PATH_GET); 42 | } 43 | 44 | public void setEnable_get_paths(Boolean enable_get_paths) { 45 | this.configFlags.put(CONFIG_FLAG.PATH_GET, enable_get_paths); 46 | } 47 | 48 | public Boolean getEnable_patch_paths() { 49 | return this.configFlags.get(CONFIG_FLAG.PATH_PATCH); 50 | } 51 | 52 | public void setEnable_patch_paths(Boolean enable_patch_paths) { 53 | this.configFlags.put(CONFIG_FLAG.PATH_PATCH, enable_patch_paths); 54 | } 55 | 56 | public Boolean getEnable_post_paths() { 57 | return this.configFlags.get(CONFIG_FLAG.PATH_POST); 58 | } 59 | 60 | public void setEnable_post_paths(Boolean enable_post_paths) { 61 | this.configFlags.put(CONFIG_FLAG.PATH_POST, enable_post_paths); 62 | } 63 | 64 | public Boolean getEnable_put_paths() { 65 | return this.configFlags.get(CONFIG_FLAG.PATH_PUT); 66 | } 67 | 68 | public void setEnable_put_paths(Boolean enable_put_paths) { 69 | this.configFlags.put(CONFIG_FLAG.PATH_PUT, enable_put_paths); 70 | } 71 | 72 | public Boolean getEnable_delete_paths() { 73 | return this.configFlags.get(CONFIG_FLAG.PATH_DELETE); 74 | } 75 | 76 | public void setEnable_delete_paths(Boolean enable_delete_paths) { 77 | this.configFlags.put(CONFIG_FLAG.PATH_DELETE, enable_delete_paths); 78 | } 79 | 80 | public String getCustom_queries_directory() { 81 | return custom_queries_directory; 82 | } 83 | 84 | public void setCustom_queries_directory(String custom_queries_directory) { 85 | this.custom_queries_directory = custom_queries_directory; 86 | } 87 | 88 | public String getOutput_dir() { 89 | return output_dir; 90 | } 91 | 92 | public void setOutput_dir(String output_dir) { 93 | this.output_dir = output_dir; 94 | } 95 | 96 | public String getName() { 97 | return name; 98 | } 99 | 100 | public void setName(String name) { 101 | this.name = name; 102 | } 103 | 104 | public List getPaths() { 105 | return paths; 106 | } 107 | 108 | public void setPaths(List paths) { 109 | this.paths = paths; 110 | } 111 | 112 | public List getOntologies() { 113 | return ontologies; 114 | } 115 | 116 | public void setOntologies(List ontologies) { 117 | this.ontologies = ontologies; 118 | } 119 | 120 | public EndpointConfig getEndpoint() { 121 | return endpoint; 122 | } 123 | 124 | public void setEndpoint(EndpointConfig endpoint) { 125 | this.endpoint = endpoint; 126 | } 127 | 128 | public FirebaseConfig getFirebase() { 129 | return firebase; 130 | } 131 | 132 | public void setFirebase(FirebaseConfig firebase) { 133 | this.firebase = firebase; 134 | } 135 | 136 | public Map> getRelations() { 137 | return relations; 138 | } 139 | 140 | public void setRelations(Map> relations) { 141 | this.relations = relations; 142 | } 143 | 144 | public LinkedHashMap getCustom_paths() { 145 | return custom_paths; 146 | } 147 | 148 | public void setCustom_paths(LinkedHashMap custom_paths) { 149 | this.custom_paths = custom_paths; 150 | } 151 | 152 | public OpenAPI getOpenapi() { 153 | return openapi; 154 | } 155 | 156 | public void setOpenapi(OpenAPI openapi) { 157 | this.openapi = openapi; 158 | } 159 | 160 | public List getClasses() { 161 | return this.classes; 162 | } 163 | 164 | public void setClasses(List classes) { 165 | this.classes = classes; 166 | } 167 | 168 | public Boolean getAlways_generate_arrays() { 169 | return this.configFlags.get(CONFIG_FLAG.ALWAYS_GENERATE_ARRAYS); 170 | } 171 | 172 | public void setAlways_generate_arrays(Boolean always_generate_arrays) { 173 | this.configFlags.put(CONFIG_FLAG.ALWAYS_GENERATE_ARRAYS, always_generate_arrays); 174 | } 175 | 176 | public Boolean getFollow_references() { 177 | return this.configFlags.get(CONFIG_FLAG.FOLLOW_REFERENCES); 178 | } 179 | 180 | public void setFollow_references(Boolean follow_references) { 181 | this.configFlags.put(CONFIG_FLAG.FOLLOW_REFERENCES, follow_references); 182 | } 183 | 184 | public Boolean getDefault_descriptions() { 185 | return this.configFlags.get(CONFIG_FLAG.DEFAULT_DESCRIPTIONS); 186 | } 187 | 188 | public void setDefault_descriptions(Boolean default_descriptions) { 189 | this.configFlags.put(CONFIG_FLAG.DEFAULT_DESCRIPTIONS, default_descriptions); 190 | } 191 | 192 | public Boolean getDefault_properties() { 193 | return this.configFlags.get(CONFIG_FLAG.DEFAULT_PROPERTIES); 194 | } 195 | 196 | public void setDefault_properties(Boolean default_properties) { 197 | this.configFlags.put(CONFIG_FLAG.DEFAULT_PROPERTIES, default_properties); 198 | } 199 | 200 | public Boolean getRequired_properties_from_cardinality() { 201 | return this.configFlags.get(CONFIG_FLAG.REQUIRED_PROPERTIES_FROM_CARDINALITY); 202 | } 203 | 204 | public void setRequired_properties_from_cardinality(Boolean required_properties_from_cardinality) { 205 | this.configFlags.put(CONFIG_FLAG.REQUIRED_PROPERTIES_FROM_CARDINALITY, required_properties_from_cardinality); 206 | } 207 | 208 | public AuthConfig getAuth() { 209 | return auth; 210 | } 211 | 212 | public void setAuth(AuthConfig auth) { 213 | this.auth = auth; 214 | } 215 | 216 | /** 217 | * Get the value of a particular configuration flag. 218 | * 219 | * @param {flag} the configuration flag name 220 | * @return The flag's value (true/false/null). 221 | */ 222 | public Boolean getConfigFlagValue(CONFIG_FLAG flag) { 223 | return this.configFlags.get(flag); 224 | } 225 | 226 | /** 227 | * Get map of all config flags and their values. 228 | * 229 | * @return Map of CONFIG_FLAGs and their Boolean value (true/false/null). 230 | */ 231 | public Map getConfigFlags() { 232 | return this.configFlags; 233 | } 234 | } -------------------------------------------------------------------------------- /src/main/java/edu/isi/oba/log4j.properties: -------------------------------------------------------------------------------- 1 | # Set root logger level to DEBUG and its only appender to A1. 2 | log4j.rootLogger=DEBUG, A1 3 | 4 | # A1 is set to be a ConsoleAppender. 5 | log4j.appender.A1=org.apache.log4j.ConsoleAppender 6 | 7 | # A1 uses PatternLayout. 8 | log4j.appender.A1.layout=org.apache.log4j.PatternLayout 9 | log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n -------------------------------------------------------------------------------- /src/main/resources/README.md: -------------------------------------------------------------------------------- 1 | # Servers directory 2 | 3 | Note servers.zip file is used to generate the python flask server, so any changes to files in the folder need to be replicated into a new servers.zip file. eg. 4 | 5 | ``` 6 | rm servers.zip 7 | zip -r servers.zip servers 8 | ``` 9 | 10 | Also note the moustache template files in the hidden folder: OBA/src/main/resources/servers/python/.openapi-generator were copied from https://github.com/OpenAPITools/openapi-generator/tree/master/modules/openapi-generator/src/main/resources/python-flask on 17 Jul 2020, and have not followed changes to that project so that a full refactor would be required to get back in sync. -------------------------------------------------------------------------------- /src/main/resources/logging.properties: -------------------------------------------------------------------------------- 1 | handlers= java.util.logging.ConsoleHandler 2 | .level= INFO 3 | java.util.logging.ConsoleHandler.level = FINE 4 | java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter 5 | java.util.logging.SimpleFormatter.format=[%1$tF %1$tT] [%4$-7s] %5$s %n -------------------------------------------------------------------------------- /src/main/resources/owl2jsonld-0.3.0-SNAPSHOT-standalone.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KnowledgeCaptureAndDiscovery/OBA/e572ebb13f8faecb1f51bbf34befbe006a631e92/src/main/resources/owl2jsonld-0.3.0-SNAPSHOT-standalone.jar -------------------------------------------------------------------------------- /src/main/resources/queries/README: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KnowledgeCaptureAndDiscovery/OBA/e572ebb13f8faecb1f51bbf34befbe006a631e92/src/main/resources/queries/README -------------------------------------------------------------------------------- /src/main/resources/queries/get_all.rq: -------------------------------------------------------------------------------- 1 | #+ summary: Given a rdf type, returns all the resources related to the type 2 | CONSTRUCT { 3 | ?item ?predicate ?prop . 4 | ?prop a ?type 5 | } 6 | WHERE { 7 | { 8 | SELECT DISTINCT ?item where { 9 | ?item a ?_type_iri . 10 | } 11 | LIMIT 100 12 | OFFSET 0 13 | } 14 | ?item ?predicate ?prop 15 | OPTIONAL { 16 | ?prop a ?type 17 | } 18 | } -------------------------------------------------------------------------------- /src/main/resources/queries/get_all_search.rq: -------------------------------------------------------------------------------- 1 | #+ summary: Given a rdf type, returns all the resources related to the type 2 | CONSTRUCT { 3 | ?item ?predicate ?prop . 4 | ?prop a ?type 5 | } 6 | WHERE { 7 | { 8 | SELECT DISTINCT ?item where { 9 | ?item a ?_type_iri . 10 | } 11 | LIMIT 100 12 | OFFSET 0 13 | } 14 | ?item ?predicate ?prop 15 | OPTIONAL { 16 | ?prop a ?type 17 | } 18 | } -------------------------------------------------------------------------------- /src/main/resources/queries/get_all_search_user.rq: -------------------------------------------------------------------------------- 1 | #+ summary: Given a rdf type, returns all the resources related to the type 2 | CONSTRUCT { 3 | ?item ?predicate ?prop . 4 | ?prop a ?type 5 | } 6 | WHERE { 7 | GRAPH ?_g_iri { 8 | { 9 | SELECT DISTINCT ?item where { 10 | ?item a ?_type_iri . 11 | } 12 | LIMIT 100 13 | OFFSET 0 14 | } 15 | ?item ?predicate ?prop 16 | OPTIONAL { 17 | ?prop a ?type 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/main/resources/queries/get_all_user.rq: -------------------------------------------------------------------------------- 1 | #+ summary: Given a rdf type, returns all the resources related to the type 2 | CONSTRUCT { 3 | ?item ?predicate ?prop . 4 | ?prop a ?type 5 | } 6 | WHERE { 7 | GRAPH ?_g_iri { 8 | { 9 | SELECT DISTINCT ?item where { 10 | ?item a ?_type_iri . 11 | } 12 | LIMIT 100 13 | OFFSET 0 14 | } 15 | ?item ?predicate ?prop 16 | OPTIONAL { 17 | ?prop a ?type 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/main/resources/queries/get_one.rq: -------------------------------------------------------------------------------- 1 | #+ summary: Return the query to a resource by the resource_iri 2 | PREFIX rdfs: 3 | CONSTRUCT { 4 | ?_resource_iri ?predicate ?prop . 5 | ?prop a ?type . 6 | ?prop rdfs:label ?label 7 | } 8 | WHERE { 9 | ?_resource_iri ?predicate ?prop 10 | OPTIONAL { 11 | ?prop a ?type 12 | OPTIONAL { 13 | ?prop rdfs:label ?label 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/main/resources/queries/get_one_user.rq: -------------------------------------------------------------------------------- 1 | #+ summary: Return the query to a resource by the resource_iri 2 | PREFIX rdfs: 3 | CONSTRUCT { 4 | ?_resource_iri ?predicate ?prop . 5 | ?prop a ?type . 6 | ?prop rdfs:label ?label 7 | } 8 | WHERE { 9 | GRAPH ?_g_iri { 10 | ?_resource_iri ?predicate ?prop 11 | OPTIONAL { 12 | ?prop a ?type 13 | OPTIONAL { 14 | ?prop rdfs:label ?label 15 | } 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/main/resources/servers.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KnowledgeCaptureAndDiscovery/OBA/e572ebb13f8faecb1f51bbf34befbe006a631e92/src/main/resources/servers.zip -------------------------------------------------------------------------------- /src/main/resources/servers/README.txt: -------------------------------------------------------------------------------- 1 | # Servers 2 | 3 | This directory contains the server implementations. 4 | -------------------------------------------------------------------------------- /src/main/resources/servers/python/.openapi-generator-ignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KnowledgeCaptureAndDiscovery/OBA/e572ebb13f8faecb1f51bbf34befbe006a631e92/src/main/resources/servers/python/.openapi-generator-ignore -------------------------------------------------------------------------------- /src/main/resources/servers/python/.openapi-generator/template/Dockerfile.mustache: -------------------------------------------------------------------------------- 1 | {{#supportPython2}} 2 | FROM python:2-alpine 3 | {{/supportPython2}} 4 | {{^supportPython2}} 5 | FROM python:3-alpine 6 | {{/supportPython2}} 7 | 8 | RUN apk add build-base libffi-dev openssl-dev git libxml2-dev libxslt-dev curl 9 | RUN mkdir -p /usr/src/app 10 | WORKDIR /usr/src/app 11 | 12 | COPY requirements.txt /usr/src/app/ 13 | 14 | {{#supportPython2}} 15 | RUN pip install --no-cache-dir -r requirements.txt 16 | {{/supportPython2}} 17 | {{^supportPython2}} 18 | RUN pip3 install --no-cache-dir -r requirements.txt 19 | {{/supportPython2}} 20 | 21 | COPY . /usr/src/app 22 | 23 | EXPOSE {{serverPort}} 24 | 25 | {{#supportPython2}} 26 | ENTRYPOINT ["python"] 27 | {{/supportPython2}} 28 | {{^supportPython2}} 29 | ENTRYPOINT ["python3"] 30 | {{/supportPython2}} 31 | 32 | CMD ["-m", "{{packageName}}"] 33 | -------------------------------------------------------------------------------- /src/main/resources/servers/python/.openapi-generator/template/README.mustache: -------------------------------------------------------------------------------- 1 | # OpenAPI generated server 2 | 3 | ## Overview 4 | This server was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using the 5 | [OpenAPI-Spec](https://openapis.org) from a remote server, you can easily generate a server stub. This 6 | is an example of building a OpenAPI-enabled Flask server. 7 | 8 | This example uses the [Connexion](https://github.com/zalando/connexion) library on top of Flask. 9 | 10 | ## Requirements 11 | {{#supportPython2}} 12 | Python 2.7+ 13 | {{/supportPython2}} 14 | {{^supportPython2}} 15 | Python 3.5.2+ 16 | {{/supportPython2}} 17 | 18 | ## Usage 19 | To run the server, please execute the following from the root directory: 20 | 21 | ``` 22 | {{#supportPython2}} 23 | pip install -r requirements.txt 24 | python -m {{packageName}} 25 | {{/supportPython2}} 26 | {{^supportPython2}} 27 | pip3 install -r requirements.txt 28 | python3 -m {{packageName}} 29 | {{/supportPython2}} 30 | ``` 31 | 32 | and open your browser to here: 33 | 34 | ``` 35 | http://localhost:{{serverPort}}{{contextPath}}/ui/ 36 | ``` 37 | 38 | Your OpenAPI definition lives here: 39 | 40 | ``` 41 | http://localhost:{{serverPort}}{{contextPath}}/openapi.json 42 | ``` 43 | 44 | To launch the integration tests, use tox: 45 | ``` 46 | sudo pip install tox 47 | tox 48 | ``` 49 | 50 | ## Running with Docker 51 | 52 | To run the server on a Docker container, please execute the following from the root directory: 53 | 54 | ```bash 55 | # building the image 56 | docker build -t {{packageName}} . 57 | 58 | # starting up a container 59 | docker run -p {{serverPort}}:{{serverPort}} {{packageName}} 60 | ``` -------------------------------------------------------------------------------- /src/main/resources/servers/python/.openapi-generator/template/__init__.mustache: -------------------------------------------------------------------------------- 1 | from obasparql import QueryManager 2 | from {{packageName}}.settings import * 3 | import logging 4 | 5 | try: 6 | logging.config.fileConfig(logging_file) 7 | except: 8 | logging.error("Logging config file does not exist {}".format(logging_file)) 9 | exit(0) 10 | 11 | logger = logging.getLogger(__name__) 12 | 13 | query_manager = QueryManager(queries_dir=QUERY_DIRECTORY, 14 | context_dir=CONTEXT_DIRECTORY, 15 | endpoint=ENDPOINT, 16 | endpoint_username=ENDPOINT_USERNAME, 17 | endpoint_password=ENDPOINT_PASSWORD, 18 | named_graph_base=ENDPOINT_GRAPH_BASE, 19 | uri_prefix=ENDPOINT_RESOURCE_PREFIX) 20 | 21 | -------------------------------------------------------------------------------- /src/main/resources/servers/python/.openapi-generator/template/__init__model.mustache: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # flake8: noqa 4 | from __future__ import absolute_import 5 | # import models into model package 6 | {{#models}}{{#model}}from {{modelPackage}}.{{classFilename}} import {{classname}}{{/model}} 7 | {{/models}} -------------------------------------------------------------------------------- /src/main/resources/servers/python/.openapi-generator/template/__init__test.mustache: -------------------------------------------------------------------------------- 1 | import logging 2 | from pathlib import Path 3 | import connexion 4 | from flask_testing import TestCase 5 | from {{packageName}}.cached import CachedSpecification 6 | from {{packageName}}.encoder import JSONEncoder 7 | from {{packageName}}.settings import * 8 | from connexion.spec import Specification 9 | 10 | import logging.config 11 | from obasparql import QueryManager 12 | 13 | 14 | query_manager = QueryManager(queries_dir=QUERY_DIRECTORY, 15 | context_dir=CONTEXT_DIRECTORY, 16 | queries_types=QUERIES_TYPES, 17 | endpoint=ENDPOINT, 18 | endpoint_username=ENDPOINT_USERNAME, 19 | endpoint_password=ENDPOINT_PASSWORD, 20 | graph_base=ENDPOINT_GRAPH_BASE, 21 | prefix=ENDPOINT_RESOURCE_PREFIX) 22 | 23 | 24 | 25 | class BaseTestCase(TestCase): 26 | logging_file = Path(__file__).parent.parent / "settings" / "logging.ini" 27 | try: 28 | logging.config.fileConfig(logging_file) 29 | except: 30 | logging.error("Logging config file does not exist {}".format(logging_file)) 31 | exit(0) 32 | logger = logging.getLogger(__name__) 33 | 34 | def create_app(self): 35 | Specification.from_file = CachedSpecification.from_file 36 | app = connexion.App(__name__, specification_dir='../openapi/') 37 | app.app.json_encoder = JSONEncoder 38 | app.add_api('openapi.yaml', 39 | pythonic_params=False, 40 | validate_responses=True) 41 | return app.app 42 | -------------------------------------------------------------------------------- /src/main/resources/servers/python/.openapi-generator/template/__main__.mustache: -------------------------------------------------------------------------------- 1 | {{#supportPython2}} 2 | #!/usr/bin/env python 3 | {{/supportPython2}} 4 | {{^supportPython2}} 5 | #!/usr/bin/env python3 6 | {{/supportPython2}} 7 | 8 | import connexion 9 | from connexion.spec import Specification 10 | from {{packageName}}.cached import CachedSpecification 11 | from {{packageName}} import encoder 12 | 13 | 14 | def main(): 15 | app = connexion.App(__name__, specification_dir='./openapi/') 16 | app.app.json_encoder = encoder.JSONEncoder 17 | Specification.from_file = CachedSpecification.from_file 18 | app.add_api('openapi.yaml', 19 | arguments={'title': '{{appName}}'}, 20 | pythonic_params=False) 21 | app.run(port={{serverPort}}) 22 | 23 | 24 | if __name__ == '__main__': 25 | main() 26 | -------------------------------------------------------------------------------- /src/main/resources/servers/python/.openapi-generator/template/base_model_.mustache: -------------------------------------------------------------------------------- 1 | import pprint 2 | 3 | import six 4 | {{^supportPython2}} 5 | import typing 6 | {{/supportPython2}} 7 | 8 | from {{packageName}} import util 9 | {{^supportPython2}} 10 | 11 | T = typing.TypeVar('T') 12 | {{/supportPython2}} 13 | 14 | 15 | class Model(object): 16 | # openapiTypes: The key is attribute name and the 17 | # value is attribute type. 18 | openapi_types = {} 19 | 20 | # attributeMap: The key is attribute name and the 21 | # value is json key in definition. 22 | attribute_map = {} 23 | 24 | @classmethod 25 | def from_dict(cls{{^supportPython2}}: typing.Type[T]{{/supportPython2}}, dikt){{^supportPython2}} -> T{{/supportPython2}}: 26 | """Returns the dict as a model""" 27 | return util.deserialize_model(dikt, cls) 28 | 29 | def to_dict(self): 30 | """Returns the model properties as a dict 31 | 32 | :rtype: dict 33 | """ 34 | result = {} 35 | 36 | for attr, _ in six.iteritems(self.openapi_types): 37 | value = getattr(self, attr) 38 | if isinstance(value, list): 39 | result[attr] = list(map( 40 | lambda x: x.to_dict() if hasattr(x, "to_dict") else x, 41 | value 42 | )) 43 | elif hasattr(value, "to_dict"): 44 | result[attr] = value.to_dict() 45 | elif isinstance(value, dict): 46 | result[attr] = dict(map( 47 | lambda item: (item[0], item[1].to_dict()) 48 | if hasattr(item[1], "to_dict") else item, 49 | value.items() 50 | )) 51 | else: 52 | result[attr] = value 53 | 54 | return result 55 | 56 | def to_str(self): 57 | """Returns the string representation of the model 58 | 59 | :rtype: str 60 | """ 61 | return pprint.pformat(self.to_dict()) 62 | 63 | def __repr__(self): 64 | """For `print` and `pprint`""" 65 | return self.to_str() 66 | 67 | def __eq__(self, other): 68 | """Returns true if both objects are equal""" 69 | return self.__dict__ == other.__dict__ 70 | 71 | def __ne__(self, other): 72 | """Returns true if both objects are not equal""" 73 | return not self == other 74 | -------------------------------------------------------------------------------- /src/main/resources/servers/python/.openapi-generator/template/controller.mustache: -------------------------------------------------------------------------------- 1 | import connexion 2 | import six 3 | from openapi_server import query_manager 4 | from openapi_server.utils.vars import {{#lambda.uppercase}}{{baseName}}_type_name{{/lambda.uppercase}}, {{#lambda.uppercase}}{{baseName}}_type_uri{{/lambda.uppercase}} 5 | 6 | {{#imports}}{{import}} # noqa: E501 7 | {{/imports}} 8 | from {{packageName}} import util 9 | {{#operations}} 10 | {{#operation}} 11 | 12 | def {{operationId}}({{#allParams}}{{paramName}}{{^required}}=None{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): # noqa: E501 13 | """{{#summary}}{{.}}{{/summary}}{{^summary}}{{operationId}}{{/summary}} 14 | 15 | {{#notes}}{{.}}{{/notes}} # noqa: E501 16 | 17 | {{#allParams}} 18 | :param {{paramName}}: {{description}} 19 | {{^isContainer}} 20 | {{#isPrimitiveType}} 21 | :type {{paramName}}: {{>param_type}} 22 | {{/isPrimitiveType}} 23 | {{#isUuid}} 24 | :type {{paramName}}: {{>param_type}} 25 | {{/isUuid}} 26 | {{^isPrimitiveType}} 27 | {{#isFile}} 28 | :type {{paramName}}: werkzeug.datastructures.FileStorage 29 | {{/isFile}} 30 | {{^isFile}} 31 | {{^isUuid}} 32 | :type {{paramName}}: dict | bytes 33 | {{/isUuid}} 34 | {{/isFile}} 35 | {{/isPrimitiveType}} 36 | {{/isContainer}} 37 | {{#isListContainer}} 38 | {{#items}} 39 | {{#isPrimitiveType}} 40 | :type {{paramName}}: List[{{>param_type}}] 41 | {{/isPrimitiveType}} 42 | {{^isPrimitiveType}} 43 | :type {{paramName}}: list | bytes 44 | {{/isPrimitiveType}} 45 | {{/items}} 46 | {{/isListContainer}} 47 | {{#isMapContainer}} 48 | {{#items}} 49 | {{#isPrimitiveType}} 50 | :type {{paramName}}: Dict[str, {{>param_type}}] 51 | {{/isPrimitiveType}} 52 | {{^isPrimitiveType}} 53 | :type {{paramName}}: dict | bytes 54 | {{/isPrimitiveType}} 55 | {{/items}} 56 | {{/isMapContainer}} 57 | {{/allParams}} 58 | 59 | :rtype: {{#returnType}}{{.}}{{/returnType}}{{^returnType}}None{{/returnType}} 60 | """ 61 | 62 | {{#allParams}} 63 | {{^isContainer}} 64 | {{#isDate}} 65 | {{paramName}} = util.deserialize_date({{paramName}}) 66 | {{/isDate}} 67 | {{#isDateTime}} 68 | {{paramName}} = util.deserialize_datetime({{paramName}}) 69 | {{/isDateTime}} 70 | {{^isPrimitiveType}} 71 | {{^isFile}} 72 | {{^isUuid}} 73 | if connexion.request.is_json: 74 | {{paramName}} = {{baseType}}.from_dict(connexion.request.get_json()) # noqa: E501 75 | {{/isUuid}} 76 | {{/isFile}} 77 | {{/isPrimitiveType}} 78 | {{/isContainer}} 79 | {{#isListContainer}} 80 | {{#items}} 81 | {{#isDate}} 82 | if connexion.request.is_json: 83 | {{paramName}} = [util.deserialize_date(s) for s in connexion.request.get_json()] # noqa: E501 84 | {{/isDate}} 85 | {{#isDateTime}} 86 | if connexion.request.is_json: 87 | {{paramName}} = [util.deserialize_datetime(s) for s in connexion.request.get_json()] # noqa: E501 88 | {{/isDateTime}} 89 | {{#complexType}} 90 | if connexion.request.is_json: 91 | {{paramName}} = [{{complexType}}.from_dict(d) for d in connexion.request.get_json()] # noqa: E501 92 | {{/complexType}} 93 | {{/items}} 94 | {{/isListContainer}} 95 | {{#isMapContainer}} 96 | {{#items}} 97 | {{#isDate}} 98 | if connexion.request.is_json: 99 | {{paramName}} = {k: util.deserialize_date(v) for k, v in six.iteritems(connexion.request.get_json())} # noqa: E501 100 | {{/isDate}} 101 | {{#isDateTime}} 102 | if connexion.request.is_json: 103 | {{paramName}} = {k: util.deserialize_datetime(v) for k, v in six.iteritems(connexion.request.get_json())} # noqa: E501 104 | {{/isDateTime}} 105 | {{#complexType}} 106 | if connexion.request.is_json: 107 | {{paramName}} = {k: {{baseType}}.from_dict(v) for k, v in six.iteritems(connexion.request.get_json())} # noqa: E501 108 | {{/complexType}} 109 | {{/items}} 110 | {{/isMapContainer}} 111 | {{/allParams}} 112 | 113 | return query_manager.{{#lambda.lowercase}}{{httpMethod}}_resource{{/lambda.lowercase}}({{#pathParams}}{{paramName}}={{paramName}},{{/pathParams}}{{#queryParams}} 114 | {{paramName}}={{paramName}},{{/queryParams}}{{#bodyParams}} 115 | body={{paramName}},{{/bodyParams}} 116 | rdf_type_uri={{#lambda.uppercase}}{{baseName}}_type_uri{{/lambda.uppercase}}, 117 | rdf_type_name={{#lambda.uppercase}}{{baseName}}_type_name{{/lambda.uppercase}}, 118 | kls={{baseName}}) 119 | {{/operation}} 120 | {{/operations}} 121 | -------------------------------------------------------------------------------- /src/main/resources/servers/python/.openapi-generator/template/controller_test.mustache: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | from __future__ import absolute_import 4 | import unittest 5 | 6 | from flask import json 7 | from six import BytesIO 8 | 9 | {{#imports}}{{import}} # noqa: E501 10 | {{/imports}} 11 | from {{packageName}}.test import BaseTestCase 12 | 13 | 14 | class {{#operations}}Test{{classname}}(BaseTestCase): 15 | """{{classname}} integration test stubs""" 16 | 17 | {{#operation}} 18 | {{#vendorExtensions.x-skip-test}} 19 | @unittest.skip("{{reason}}") 20 | {{/vendorExtensions.x-skip-test}} 21 | def test_{{operationId}}(self): 22 | """Test case for {{{operationId}}} 23 | 24 | {{{summary}}} 25 | """ 26 | {{#bodyParam}} 27 | {{paramName}} = {{{example}}} 28 | {{/bodyParam}} 29 | {{#queryParams}} 30 | {{#-first}}query_string = [{{/-first}}{{^-first}} {{/-first}}('{{^vendorExtensions.x-python-connexion-openapi-name}}{{paramName}}{{/vendorExtensions.x-python-connexion-openapi-name}}{{#vendorExtensions.x-python-connexion-openapi-name}}{{vendorExtensions.x-python-connexion-openapi-name}}{{/vendorExtensions.x-python-connexion-openapi-name}}', {{{example}}}){{#hasMore}},{{/hasMore}}{{#-last}}]{{/-last}} 31 | {{/queryParams}} 32 | headers = { {{#vendorExtensions.x-prefered-produce}} 33 | 'Accept': '{{mediaType}}',{{/vendorExtensions.x-prefered-produce}}{{#vendorExtensions.x-prefered-consume}} 34 | 'Content-Type': '{{mediaType}}',{{/vendorExtensions.x-prefered-consume}}{{#headerParams}} 35 | '{{paramName}}': {{{example}}},{{/headerParams}}{{#authMethods}} 36 | {{#isOAuth}}'Authorization': 'Bearer special-key',{{/isOAuth}}{{#isApiKey}}'{{name}}': 'special-key',{{/isApiKey}}{{#isBasicBasic}}'Authorization': 'BasicZm9vOmJhcg==',{{/isBasicBasic}}{{#isBasicBearer}}'Authorization': 'Bearer special-key',{{/isBasicBearer}}{{/authMethods}} 37 | } 38 | {{#formParams}} 39 | {{#-first}}data = dict({{/-first}}{{^-first}} {{/-first}}{{paramName}}={{{example}}}{{#hasMore}},{{/hasMore}}{{#-last}}){{/-last}} 40 | {{/formParams}} 41 | response = self.client.open( 42 | '{{#contextPath}}{{{.}}}{{/contextPath}}{{{path}}}'{{#pathParams}}{{#-first}}.format({{/-first}}{{paramName}}={{{example}}}{{#hasMore}}, {{/hasMore}}{{^hasMore}}){{/hasMore}}{{/pathParams}}, 43 | method='{{httpMethod}}', 44 | headers=headers{{#bodyParam}}, 45 | data=json.dumps({{paramName}}){{^consumes}}, 46 | content_type='application/json'{{/consumes}}{{/bodyParam}}{{#formParams}}{{#-first}}, 47 | data=data{{/-first}}{{/formParams}}{{#consumes}}{{#-first}}, 48 | content_type='{{{mediaType}}}'{{/-first}}{{/consumes}}{{#queryParams}}{{#-first}}, 49 | query_string=query_string{{/-first}}{{/queryParams}}) 50 | self.assert200(response, 51 | 'Response body is : ' + response.data.decode('utf-8')) 52 | 53 | {{/operation}} 54 | {{/operations}} 55 | 56 | if __name__ == '__main__': 57 | unittest.main() 58 | -------------------------------------------------------------------------------- /src/main/resources/servers/python/.openapi-generator/template/dockerignore.mustache: -------------------------------------------------------------------------------- 1 | .travis.yaml 2 | .openapi-generator-ignore 3 | README.md 4 | tox.ini 5 | git_push.sh 6 | test-requirements.txt 7 | setup.py 8 | 9 | # Byte-compiled / optimized / DLL files 10 | __pycache__/ 11 | *.py[cod] 12 | *$py.class 13 | 14 | # C extensions 15 | *.so 16 | 17 | # Distribution / packaging 18 | .Python 19 | env/ 20 | build/ 21 | develop-eggs/ 22 | dist/ 23 | downloads/ 24 | eggs/ 25 | .eggs/ 26 | lib/ 27 | lib64/ 28 | parts/ 29 | sdist/ 30 | var/ 31 | *.egg-info/ 32 | .installed.cfg 33 | *.egg 34 | 35 | # PyInstaller 36 | # Usually these files are written by a python script from a template 37 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 38 | *.manifest 39 | *.spec 40 | 41 | # Installer logs 42 | pip-log.txt 43 | pip-delete-this-directory.txt 44 | 45 | # Unit test / coverage reports 46 | htmlcov/ 47 | .tox/ 48 | .coverage 49 | .coverage.* 50 | .cache 51 | nosetests.xml 52 | coverage.xml 53 | *,cover 54 | .hypothesis/ 55 | venv/ 56 | .python-version 57 | 58 | # Translations 59 | *.mo 60 | *.pot 61 | 62 | # Django stuff: 63 | *.log 64 | 65 | # Sphinx documentation 66 | docs/_build/ 67 | 68 | # PyBuilder 69 | target/ 70 | 71 | #Ipython Notebook 72 | .ipynb_checkpoints 73 | -------------------------------------------------------------------------------- /src/main/resources/servers/python/.openapi-generator/template/encoder.mustache: -------------------------------------------------------------------------------- 1 | from connexion.apps.flask_app import FlaskJSONEncoder 2 | import six 3 | 4 | from {{modelPackage}}.base_model_ import Model 5 | 6 | 7 | class JSONEncoder(FlaskJSONEncoder): 8 | include_nulls = False 9 | 10 | def default(self, o): 11 | if isinstance(o, Model): 12 | dikt = {} 13 | for attr, _ in six.iteritems(o.openapi_types): 14 | value = getattr(o, attr) 15 | if value is None and not self.include_nulls: 16 | continue 17 | attr = o.attribute_map[attr] 18 | dikt[attr] = value 19 | return dikt 20 | return FlaskJSONEncoder.default(self, o) 21 | -------------------------------------------------------------------------------- /src/main/resources/servers/python/.openapi-generator/template/git_push.sh.mustache: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ 3 | # 4 | # Usage example: /bin/sh ./git_push.sh wing328 openapi-pestore-perl "minor update" 5 | 6 | git_user_id=$1 7 | git_repo_id=$2 8 | release_note=$3 9 | 10 | if [ "$git_user_id" = "" ]; then 11 | git_user_id="{{{gitUserId}}}" 12 | echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" 13 | fi 14 | 15 | if [ "$git_repo_id" = "" ]; then 16 | git_repo_id="{{{gitRepoId}}}" 17 | echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" 18 | fi 19 | 20 | if [ "$release_note" = "" ]; then 21 | release_note="{{{releaseNote}}}" 22 | echo "[INFO] No command line input provided. Set \$release_note to $release_note" 23 | fi 24 | 25 | # Initialize the local directory as a Git repository 26 | git init 27 | 28 | # Adds the files in the local repository and stages them for commit. 29 | git add . 30 | 31 | # Commits the tracked changes and prepares them to be pushed to a remote repository. 32 | git commit -m "$release_note" 33 | 34 | # Sets the new remote 35 | git_remote=`git remote` 36 | if [ "$git_remote" = "" ]; then # git remote not defined 37 | 38 | if [ "$GIT_TOKEN" = "" ]; then 39 | echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." 40 | git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git 41 | else 42 | git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git 43 | fi 44 | 45 | fi 46 | 47 | git pull origin master 48 | 49 | # Pushes (Forces) the changes in the local repository up to the remote repository 50 | echo "Git pushing to https://github.com/${git_user_id}/${git_repo_id}.git" 51 | git push origin master 2>&1 | grep -v 'To https' 52 | 53 | -------------------------------------------------------------------------------- /src/main/resources/servers/python/.openapi-generator/template/gitignore.mustache: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | venv/ 48 | .python-version 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | 57 | # Sphinx documentation 58 | docs/_build/ 59 | 60 | # PyBuilder 61 | target/ 62 | 63 | #Ipython Notebook 64 | .ipynb_checkpoints 65 | -------------------------------------------------------------------------------- /src/main/resources/servers/python/.openapi-generator/template/model.mustache: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | from __future__ import absolute_import 4 | from datetime import date, datetime # noqa: F401 5 | 6 | from typing import List, Dict # noqa: F401 7 | 8 | from {{modelPackage}}.base_model_ import Model 9 | from {{packageName}} import util 10 | 11 | 12 | {{#models}} 13 | {{#model}} 14 | class {{classname}}(Model): 15 | """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 16 | 17 | Do not edit the class manually. 18 | """{{#allowableValues}} 19 | 20 | """ 21 | allowed enum values 22 | """ 23 | {{#enumVars}} 24 | {{name}} = {{{value}}}{{^-last}} 25 | {{/-last}} 26 | {{/enumVars}}{{/allowableValues}} 27 | 28 | def __init__(self{{#vars}}, {{name}}={{#defaultValue}}{{{defaultValue}}}{{/defaultValue}}{{^defaultValue}}None{{/defaultValue}}{{/vars}}): # noqa: E501 29 | """{{classname}} - a model defined in OpenAPI 30 | 31 | {{#vars}} 32 | :param {{name}}: The {{name}} of this {{classname}}. # noqa: E501 33 | :type {{name}}: {{dataType}} 34 | {{/vars}} 35 | """ 36 | {{#models}} 37 | {{#model}} 38 | {{#pyImports}} 39 | {{import}} 40 | {{/pyImports}} 41 | {{/model}} 42 | {{/models}} 43 | 44 | {{#imports}} 45 | {{{import}}} # noqa: E501 46 | {{/imports}} 47 | 48 | self.openapi_types = { 49 | {{#vars}} 50 | '{{name}}': {{{dataType}}}{{^-last}},{{/-last}} 51 | {{/vars}} 52 | } 53 | 54 | self.attribute_map = { 55 | {{#vars}} 56 | '{{name}}': '{{baseName}}'{{^-last}},{{/-last}} 57 | {{/vars}} 58 | } 59 | {{#vars}}{{#-first}} 60 | {{/-first}} 61 | self._{{name}} = {{name}} 62 | {{/vars}} 63 | 64 | @classmethod 65 | def from_dict(cls, dikt){{^supportPython2}} -> '{{classname}}'{{/supportPython2}}: 66 | """Returns the dict as a model 67 | 68 | :param dikt: A dict. 69 | :type: dict 70 | :return: The {{name}} of this {{classname}}. # noqa: E501 71 | :rtype: {{classname}} 72 | """ 73 | return util.deserialize_model(dikt, cls){{#vars}}{{#-first}} 74 | 75 | {{/-first}} 76 | @property 77 | def {{name}}(self): 78 | """Gets the {{name}} of this {{classname}}. 79 | 80 | {{#description}} 81 | {{{description}}} # noqa: E501 82 | {{/description}} 83 | 84 | :return: The {{name}} of this {{classname}}. 85 | :rtype: {{dataType}} 86 | """ 87 | return self._{{name}} 88 | 89 | @{{name}}.setter 90 | def {{name}}(self, {{name}}): 91 | """Sets the {{name}} of this {{classname}}. 92 | 93 | {{#description}} 94 | {{{description}}} # noqa: E501 95 | {{/description}} 96 | 97 | :param {{name}}: The {{name}} of this {{classname}}. 98 | :type {{name}}: {{dataType}} 99 | """ 100 | {{#isEnum}} 101 | allowed_values = [{{#allowableValues}}{{#values}}"{{{this}}}"{{^-last}}, {{/-last}}{{/values}}{{/allowableValues}}] # noqa: E501 102 | {{#isContainer}} 103 | {{#isListContainer}} 104 | if not set({{{name}}}).issubset(set(allowed_values)): 105 | raise ValueError( 106 | "Invalid values for `{{{name}}}` [{0}], must be a subset of [{1}]" # noqa: E501 107 | .format(", ".join(map(str, set({{{name}}}) - set(allowed_values))), # noqa: E501 108 | ", ".join(map(str, allowed_values))) 109 | ) 110 | {{/isListContainer}} 111 | {{#isMapContainer}} 112 | if not set({{{name}}}.keys()).issubset(set(allowed_values)): 113 | raise ValueError( 114 | "Invalid keys in `{{{name}}}` [{0}], must be a subset of [{1}]" # noqa: E501 115 | .format(", ".join(map(str, set({{{name}}}.keys()) - set(allowed_values))), # noqa: E501 116 | ", ".join(map(str, allowed_values))) 117 | ) 118 | {{/isMapContainer}} 119 | {{/isContainer}} 120 | {{^isContainer}} 121 | if {{{name}}} not in allowed_values: 122 | raise ValueError( 123 | "Invalid value for `{{{name}}}` ({0}), must be one of {1}" 124 | .format({{{name}}}, allowed_values) 125 | ) 126 | {{/isContainer}} 127 | {{/isEnum}} 128 | {{^isEnum}} 129 | {{#required}} 130 | if {{name}} is None: 131 | raise ValueError("Invalid value for `{{name}}`, must not be `None`") # noqa: E501 132 | {{/required}} 133 | {{#hasValidation}} 134 | {{#maxLength}} 135 | if {{name}} is not None and len({{name}}) > {{maxLength}}: 136 | raise ValueError("Invalid value for `{{name}}`, length must be less than or equal to `{{maxLength}}`") # noqa: E501 137 | {{/maxLength}} 138 | {{#minLength}} 139 | if {{name}} is not None and len({{name}}) < {{minLength}}: 140 | raise ValueError("Invalid value for `{{name}}`, length must be greater than or equal to `{{minLength}}`") # noqa: E501 141 | {{/minLength}} 142 | {{#maximum}} 143 | if {{name}} is not None and {{name}} >{{#exclusiveMaximum}}={{/exclusiveMaximum}} {{maximum}}: # noqa: E501 144 | raise ValueError("Invalid value for `{{name}}`, must be a value less than {{^exclusiveMaximum}}or equal to {{/exclusiveMaximum}}`{{maximum}}`") # noqa: E501 145 | {{/maximum}} 146 | {{#minimum}} 147 | if {{name}} is not None and {{name}} <{{#exclusiveMinimum}}={{/exclusiveMinimum}} {{minimum}}: # noqa: E501 148 | raise ValueError("Invalid value for `{{name}}`, must be a value greater than {{^exclusiveMinimum}}or equal to {{/exclusiveMinimum}}`{{minimum}}`") # noqa: E501 149 | {{/minimum}} 150 | {{#pattern}} 151 | if {{name}} is not None and not re.search(r'{{{vendorExtensions.x-regex}}}', {{name}}{{#vendorExtensions.x-modifiers}}{{#-first}}, flags={{/-first}}re.{{.}}{{^-last}} | {{/-last}}{{/vendorExtensions.x-modifiers}}): # noqa: E501 152 | raise ValueError("Invalid value for `{{name}}`, must be a follow pattern or equal to `{{{pattern}}}`") # noqa: E501 153 | {{/pattern}} 154 | {{#maxItems}} 155 | if {{name}} is not None and len({{name}}) > {{maxItems}}: 156 | raise ValueError("Invalid value for `{{name}}`, number of items must be less than or equal to `{{maxItems}}`") # noqa: E501 157 | {{/maxItems}} 158 | {{#minItems}} 159 | if {{name}} is not None and len({{name}}) < {{minItems}}: 160 | raise ValueError("Invalid value for `{{name}}`, number of items must be greater than or equal to `{{minItems}}`") # noqa: E501 161 | {{/minItems}} 162 | {{/hasValidation}} 163 | {{/isEnum}} 164 | 165 | self._{{name}} = {{name}}{{^-last}} 166 | 167 | {{/-last}} 168 | {{/vars}} 169 | 170 | {{/model}} 171 | {{/models}} 172 | -------------------------------------------------------------------------------- /src/main/resources/servers/python/.openapi-generator/template/openapi.mustache: -------------------------------------------------------------------------------- 1 | {{{openapi-yaml}}} -------------------------------------------------------------------------------- /src/main/resources/servers/python/.openapi-generator/template/param_type.mustache: -------------------------------------------------------------------------------- 1 | {{#isString}}str{{/isString}}{{#isInteger}}int{{/isInteger}}{{#isLong}}int{{/isLong}}{{#isFloat}}float{{/isFloat}}{{#isDouble}}float{{/isDouble}}{{#isByteArray}}str{{/isByteArray}}{{#isBinary}}str{{/isBinary}}{{#isBoolean}}bool{{/isBoolean}}{{#isDate}}str{{/isDate}}{{#isDateTime}}str{{/isDateTime}} -------------------------------------------------------------------------------- /src/main/resources/servers/python/.openapi-generator/template/requirements.mustache: -------------------------------------------------------------------------------- 1 | pythonql3==0.9.61 2 | connexion >= 2.6.0 3 | obasparql >= 3.4.2 4 | werkzeug>=2.0 5 | swagger-ui-bundle >= 0.0.2 6 | python_dateutil >= 2.6.0 7 | setuptools >= 21.0.0 8 | validators >= 0.14.2 9 | python-jose >= 3.0.1 10 | markupsafe==2.0.1 11 | -------------------------------------------------------------------------------- /src/main/resources/servers/python/.openapi-generator/template/security_controller_.mustache: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | {{#authMethods}} 4 | {{#isOAuth}} 5 | 6 | def info_from_{{name}}(token): 7 | """ 8 | Validate and decode token. 9 | Returned value will be passed in 'token_info' parameter of your operation function, if there is one. 10 | 'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one. 11 | 'scope' or 'scopes' will be passed to scope validation function. 12 | 13 | :param token Token provided by Authorization header 14 | :type token: str 15 | :return: Decoded token information or None if token is invalid 16 | :rtype: dict | None 17 | """ 18 | return {'scopes': ['read:pets', 'write:pets'], 'uid': 'user_id'} 19 | 20 | 21 | def validate_scope_{{name}}(required_scopes, token_scopes): 22 | """ 23 | Validate required scopes are included in token scope 24 | 25 | :param required_scopes Required scope to access called API 26 | :type required_scopes: List[str] 27 | :param token_scopes Scope present in token 28 | :type token_scopes: List[str] 29 | :return: True if access to called API is allowed 30 | :rtype: bool 31 | """ 32 | return set(required_scopes).issubset(set(token_scopes)) 33 | 34 | {{/isOAuth}} 35 | {{#isApiKey}} 36 | 37 | def info_from_{{name}}(api_key, required_scopes): 38 | """ 39 | Check and retrieve authentication information from api_key. 40 | Returned value will be passed in 'token_info' parameter of your operation function, if there is one. 41 | 'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one. 42 | 43 | :param api_key API key provided by Authorization header 44 | :type api_key: str 45 | :param required_scopes Always None. Used for other authentication method 46 | :type required_scopes: None 47 | :return: Information attached to provided api_key or None if api_key is invalid or does not allow access to called API 48 | :rtype: dict | None 49 | """ 50 | return {'uid': 'user_id'} 51 | 52 | {{/isApiKey}} 53 | {{#isBasicBasic}} 54 | 55 | def info_from_{{name}}(username, password, required_scopes): 56 | """ 57 | Check and retrieve authentication information from basic auth. 58 | Returned value will be passed in 'token_info' parameter of your operation function, if there is one. 59 | 'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one. 60 | 61 | :param username login provided by Authorization header 62 | :type username: str 63 | :param password password provided by Authorization header 64 | :type password: str 65 | :param required_scopes Always None. Used for other authentication method 66 | :type required_scopes: None 67 | :return: Information attached to user or None if credentials are invalid or does not allow access to called API 68 | :rtype: dict | None 69 | """ 70 | return {'uid': 'user_id'} 71 | 72 | {{/isBasicBasic}} 73 | {{#isBasicBearer}} 74 | 75 | def info_from_{{name}}(token): 76 | """ 77 | Check and retrieve authentication information from custom bearer token. 78 | Returned value will be passed in 'token_info' parameter of your operation function, if there is one. 79 | 'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one. 80 | 81 | :param token Token provided by Authorization header 82 | :type token: str 83 | :return: Decoded token information or None if token is invalid 84 | :rtype: dict | None 85 | """ 86 | return {'uid': 'user_id'} 87 | 88 | {{/isBasicBearer}} 89 | {{/authMethods}} 90 | 91 | -------------------------------------------------------------------------------- /src/main/resources/servers/python/.openapi-generator/template/setup.mustache: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import sys 4 | from setuptools import setup, find_packages 5 | 6 | NAME = "{{packageName}}" 7 | VERSION = "{{packageVersion}}" 8 | {{#apiInfo}}{{#apis}}{{^hasMore}} 9 | # To install the library, run the following 10 | # 11 | # python setup.py install 12 | # 13 | # prerequisite: setuptools 14 | # http://pypi.python.org/pypi/setuptools 15 | 16 | REQUIRES = [ 17 | "connexion==2.6.0", 18 | "swagger-ui-bundle==0.0.2", 19 | "python_dateutil==2.6.0"{{#supportPython2}}, 20 | "typing==3.5.2.2"{{/supportPython2}} 21 | ] 22 | 23 | setup( 24 | name=NAME, 25 | version=VERSION, 26 | description="{{appName}}", 27 | author_email="{{infoEmail}}", 28 | url="{{packageUrl}}", 29 | keywords=["OpenAPI", "{{appName}}"], 30 | install_requires=REQUIRES, 31 | packages=find_packages(), 32 | package_data={'': ['openapi/openapi.yaml']}, 33 | include_package_data=True, 34 | entry_points={ 35 | 'console_scripts': ['{{packageName}}={{packageName}}.__main__:main']}, 36 | long_description="""\ 37 | {{appDescription}} 38 | """ 39 | ) 40 | {{/hasMore}}{{/apis}}{{/apiInfo}} 41 | -------------------------------------------------------------------------------- /src/main/resources/servers/python/.openapi-generator/template/static_files/cached.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | from logging import getLogger 3 | import pathlib 4 | import pickle 5 | 6 | from connexion.spec import Specification 7 | 8 | logger = getLogger(__name__) 9 | 10 | 11 | class CachedSpecification(Specification): 12 | """Cache the built API specification. 13 | 14 | Building and loading our OpenAPI specification is very slow, by caching 15 | the result we can drastically reduce the reload time of the application. 16 | The cache is invalidated when the yaml file changes. 17 | """ 18 | 19 | @classmethod 20 | def from_file(cls, spec, arguments=None): 21 | md5_hash = cls.md5(spec) 22 | cache_file = str(spec) + '.cache' 23 | try: 24 | with open(cache_file, 'rb') as f: 25 | cache = pickle.load(f) 26 | if cache['md5_hash'] == md5_hash: 27 | logger.info('Loaded spec from cache') 28 | return cache['spec'] 29 | except OSError as e: 30 | logger.warning('Cache file does not exist: %s', e) 31 | pass 32 | 33 | rv = cls._real_from_file(spec, arguments=arguments) 34 | try: 35 | with open(cache_file, 'wb') as f: 36 | cache = { 37 | 'md5_hash': md5_hash, 38 | 'spec': rv 39 | } 40 | pickle.dump(cache, f) 41 | logger.info('Stored spec in cache') 42 | except OSError as e: 43 | logger.warning('Could not store spec in cache: %s', e) 44 | 45 | return rv 46 | 47 | @classmethod 48 | def _real_from_file(cls, spec, arguments=None): 49 | """ 50 | Takes in a path to a YAML file, and returns a Specification 51 | """ 52 | specification_path = pathlib.Path(spec) 53 | spec = cls._load_spec_from_file(arguments, specification_path) 54 | return cls.from_dict(spec) 55 | 56 | @classmethod 57 | def md5(cls, file) -> str: 58 | hash_md5 = hashlib.md5() 59 | with open(file, 'rb') as f: 60 | for chunk in iter(lambda: f.read(4096), b""): 61 | hash_md5.update(chunk) 62 | return hash_md5.hexdigest() -------------------------------------------------------------------------------- /src/main/resources/servers/python/.openapi-generator/template/static_files/settings/__init__.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from configparser import ConfigParser 3 | import os 4 | from pathlib import Path 5 | 6 | path = Path(__file__).parent.parent.parent 7 | 8 | # Setting headers to use access_token for the GitHub API 9 | config_fallbacks = { 10 | 'github_access_token': '', 11 | 'endpoint': '', 12 | 'user': '', 13 | 'password': '', 14 | 'server_name': '', 15 | 'prefix': '', 16 | 'graph_base': '', 17 | 'firebase_key': '', 18 | 'local_sparql_dir': '', 19 | 'query_dir': '', 20 | 'context_dir': '', 21 | } 22 | config = ConfigParser(config_fallbacks) 23 | config.add_section('auth') 24 | config.add_section('defaults') 25 | config.add_section('local') 26 | config.read('config.ini') 27 | config_filename = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'config.ini') 28 | config.read(config_filename) 29 | 30 | # Default endpoint, if none specified elsewhere 31 | ENDPOINT = config.get('defaults', 'endpoint') 32 | ENDPOINT_USERNAME = config.get('defaults', 'user') 33 | ENDPOINT_PASSWORD = config.get('defaults', 'password') 34 | ENDPOINT_RESOURCE_PREFIX = config.get('defaults', 'prefix') 35 | ENDPOINT_GRAPH_BASE = config.get('defaults', 'graph_base') 36 | FIREBASE_KEY = config.get('defaults', 'firebase_key') 37 | 38 | QUERY_DIRECTORY = path/config.get('defaults', 'queries_dir') 39 | CONTEXT_DIRECTORY = path/config.get('defaults', 'context_dir') 40 | 41 | mime_types = { 42 | 'csv': 'text/csv; q=1.0, */*; q=0.1', 43 | 'json': 'application/json; q=1.0, application/sparql-results+json; q=0.8, */*; q=0.1', 44 | 'html': 'text/html; q=1.0, */*; q=0.1', 45 | 'ttl': 'text/turtle' 46 | } 47 | 48 | UPDATE_ENDPOINT = f'{ENDPOINT}/update' 49 | QUERY_ENDPOINT = f'{ENDPOINT}/query' 50 | 51 | QUERIES_TYPES = ["get_all", "get_all_related", "get_all_related_user", "get_all_user", "get_one", "get_one_user"] 52 | 53 | logging_file = Path(__file__).parent / "logging.ini" 54 | -------------------------------------------------------------------------------- /src/main/resources/servers/python/.openapi-generator/template/static_files/settings/config.ini: -------------------------------------------------------------------------------- 1 | [defaults] 2 | endpoint = 3 | queries_dir = queries/ 4 | context_dir = contexts/ 5 | prefix = 6 | graph_base = 7 | -------------------------------------------------------------------------------- /src/main/resources/servers/python/.openapi-generator/template/static_files/settings/logging.ini: -------------------------------------------------------------------------------- 1 | [loggers] 2 | keys=root,grlc,oba,test 3 | 4 | [handlers] 5 | keys=consoleHandler 6 | 7 | [formatters] 8 | keys=simpleFormatter 9 | 10 | [logger_root] 11 | level=WARNING 12 | handlers=consoleHandler 13 | 14 | [logger_grlc] 15 | level=WARNING 16 | handlers=consoleHandler 17 | qualname=grlc 18 | propagate=0 19 | 20 | [logger_test] 21 | level=INFO 22 | handlers=consoleHandler 23 | qualname=test 24 | propagate=0 25 | 26 | [logger_oba] 27 | level=INFO 28 | handlers=consoleHandler 29 | qualname=oba 30 | propagate=0 31 | 32 | [handler_consoleHandler] 33 | class=StreamHandler 34 | level=DEBUG 35 | formatter=simpleFormatter 36 | args=(sys.stdout,) 37 | 38 | [formatter_simpleFormatter] 39 | format=%(asctime)s - %(name)s - %(levelname)s - %(message)s 40 | datefmt= -------------------------------------------------------------------------------- /src/main/resources/servers/python/.openapi-generator/template/static_files/user_controller.py: -------------------------------------------------------------------------------- 1 | import six 2 | from jose import JWTError, jwt 3 | import time 4 | import json 5 | import requests 6 | from werkzeug.exceptions import Unauthorized 7 | import connexion 8 | from openapi_server.settings import FIREBASE_KEY 9 | from openapi_server.models.user import User 10 | 11 | JWT_ISSUER = 'com.zalando.connexion' 12 | JWT_SECRET = 'change_this' 13 | JWT_LIFETIME_SECONDS = 60000000 14 | JWT_ALGORITHM = 'HS256' 15 | 16 | 17 | def decode_token(token): 18 | try: 19 | return jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM]) 20 | except JWTError as e: 21 | six.raise_from(Unauthorized, e) 22 | 23 | 24 | def _current_timestamp() -> int: 25 | return int(time.time()) 26 | 27 | def auth_with_password(email, password): 28 | headers = { 29 | 'Sec-Fetch-Mode': 'cors', 30 | 'Content-Type': 'application/json', 31 | } 32 | 33 | params = ( 34 | ('key', FIREBASE_KEY), 35 | ) 36 | 37 | data = json.dumps({"email": email,"password": password,"returnSecureToken": True}) 38 | 39 | response = requests.post('https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword', 40 | headers=headers, params=params, data=data) 41 | 42 | return response.ok 43 | 44 | def user_login_post(): # noqa: E501 45 | """Logs user into the system 46 | 47 | # noqa: E501 48 | 49 | :param username: The user name for login 50 | :type username: str 51 | :param password: The password for login in clear text 52 | :type password: str 53 | 54 | :rtype: str 55 | """ 56 | if connexion.request.is_json: 57 | user = User.from_dict(connexion.request.get_json()) # noqa: E501 58 | if not auth_with_password(user.username, user.password): 59 | return "Invalid User or Password", 401, {} 60 | 61 | 62 | timestamp = _current_timestamp() 63 | payload = { 64 | "iss": JWT_ISSUER, 65 | "iat": int(timestamp), 66 | "exp": int(timestamp + JWT_LIFETIME_SECONDS), 67 | "sub": str(user.username), 68 | } 69 | 70 | access_token = jwt.encode(payload, JWT_SECRET, algorithm=JWT_ALGORITHM) 71 | return { 72 | "access_token": access_token, 73 | "token_type": "bearer", 74 | "expires_in": JWT_LIFETIME_SECONDS, 75 | "refresh_token": "IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk", 76 | "scope": "create" 77 | }, 200, {'X-Expires-After': JWT_LIFETIME_SECONDS, 'X-Rate-Limit': 1000} 78 | -------------------------------------------------------------------------------- /src/main/resources/servers/python/.openapi-generator/template/test-requirements.mustache: -------------------------------------------------------------------------------- 1 | flask_testing==0.6.1 2 | coverage>=4.0.3 3 | nose>=1.3.7 4 | pluggy>=0.3.1 5 | py>=1.4.31 6 | randomize>=0.13 7 | deepdiff>=4.3.1 8 | ordered-set==3.1.1 -------------------------------------------------------------------------------- /src/main/resources/servers/python/.openapi-generator/template/tox.mustache: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = {{#supportPython2}}py27, {{/supportPython2}}py3 3 | 4 | [testenv] 5 | deps=-r{toxinidir}/requirements.txt 6 | -r{toxinidir}/test-requirements.txt 7 | 8 | commands= 9 | nosetests \ 10 | [] -------------------------------------------------------------------------------- /src/main/resources/servers/python/.openapi-generator/template/travis.mustache: -------------------------------------------------------------------------------- 1 | # ref: https://docs.travis-ci.com/user/languages/python 2 | language: python 3 | python: 4 | {{#supportPython2}} 5 | - "2.7" 6 | {{/supportPython2}} 7 | - "3.2" 8 | - "3.3" 9 | - "3.4" 10 | - "3.5" 11 | #- "3.5-dev" # 3.5 development branch 12 | #- "nightly" # points to the latest development branch e.g. 3.6-dev 13 | # command to install dependencies 14 | install: "pip install -r requirements.txt" 15 | # command to run tests 16 | script: nosetests 17 | -------------------------------------------------------------------------------- /src/main/resources/servers/python/.openapi-generator/template/typing_utils.mustache: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import sys 4 | 5 | if sys.version_info < (3, 7): 6 | import typing 7 | 8 | def is_generic(klass): 9 | """ Determine whether klass is a generic class """ 10 | return type(klass) == typing.GenericMeta 11 | 12 | def is_dict(klass): 13 | """ Determine whether klass is a Dict """ 14 | return klass.__extra__ == dict 15 | 16 | def is_list(klass): 17 | """ Determine whether klass is a List """ 18 | return klass.__extra__ == list 19 | 20 | else: 21 | 22 | def is_generic(klass): 23 | """ Determine whether klass is a generic class """ 24 | return hasattr(klass, '__origin__') 25 | 26 | def is_dict(klass): 27 | """ Determine whether klass is a Dict """ 28 | return klass.__origin__ == dict 29 | 30 | def is_list(klass): 31 | """ Determine whether klass is a List """ 32 | return klass.__origin__ == list 33 | -------------------------------------------------------------------------------- /src/main/resources/servers/python/.openapi-generator/template/util.mustache: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | import six 4 | import typing 5 | from {{packageName}} import typing_utils 6 | 7 | 8 | def _deserialize(data, klass): 9 | """Deserializes dict, list, str into an object. 10 | 11 | :param data: dict, list or str. 12 | :param klass: class literal, or string of class name. 13 | 14 | :return: object. 15 | """ 16 | if data is None: 17 | return None 18 | 19 | if klass in six.integer_types or klass in (float, str, bool, bytearray): 20 | return _deserialize_primitive(data, klass) 21 | elif klass == object: 22 | return _deserialize_object(data) 23 | elif klass == datetime.date: 24 | return deserialize_date(data) 25 | elif klass == datetime.datetime: 26 | return deserialize_datetime(data) 27 | elif typing_utils.is_generic(klass): 28 | if typing_utils.is_list(klass): 29 | return _deserialize_list(data, klass.__args__[0]) 30 | if typing_utils.is_dict(klass): 31 | return _deserialize_dict(data, klass.__args__[1]) 32 | else: 33 | return deserialize_model(data, klass) 34 | 35 | 36 | def _deserialize_primitive(data, klass): 37 | """Deserializes to primitive type. 38 | 39 | :param data: data to deserialize. 40 | :param klass: class literal. 41 | 42 | :return: int, long, float, str, bool. 43 | :rtype: int | long | float | str | bool 44 | """ 45 | try: 46 | value = klass(data) 47 | except UnicodeEncodeError: 48 | value = six.u(data) 49 | except TypeError: 50 | value = data 51 | return value 52 | 53 | 54 | def _deserialize_object(value): 55 | """Return an original value. 56 | 57 | :return: object. 58 | """ 59 | return value 60 | 61 | 62 | def deserialize_date(string): 63 | """Deserializes string to date. 64 | 65 | :param string: str. 66 | :type string: str 67 | :return: date. 68 | :rtype: date 69 | """ 70 | try: 71 | from dateutil.parser import parse 72 | return parse(string).date() 73 | except ImportError: 74 | return string 75 | 76 | 77 | def deserialize_datetime(string): 78 | """Deserializes string to datetime. 79 | 80 | The string should be in iso8601 datetime format. 81 | 82 | :param string: str. 83 | :type string: str 84 | :return: datetime. 85 | :rtype: datetime 86 | """ 87 | try: 88 | from dateutil.parser import parse 89 | return parse(string) 90 | except ImportError: 91 | return string 92 | 93 | 94 | def deserialize_model(data, klass): 95 | """Deserializes list or dict to model. 96 | 97 | :param data: dict, list. 98 | :type data: dict | list 99 | :param klass: class literal. 100 | :return: model object. 101 | """ 102 | instance = klass() 103 | 104 | if not instance.openapi_types: 105 | return data 106 | 107 | for attr, attr_type in six.iteritems(instance.openapi_types): 108 | if data is not None \ 109 | and instance.attribute_map[attr] in data \ 110 | and isinstance(data, (list, dict)): 111 | value = data[instance.attribute_map[attr]] 112 | #fix: force list if the type is a list 113 | if attr_type == typing.List[type(value)] and type(value) != typing.List: 114 | value = [value] 115 | setattr(instance, attr, _deserialize(value, attr_type)) 116 | 117 | return instance 118 | 119 | 120 | def _deserialize_list(data, boxed_type): 121 | """Deserializes a list and its elements. 122 | 123 | :param data: list to deserialize. 124 | :type data: list 125 | :param boxed_type: class literal. 126 | 127 | :return: deserialized list. 128 | :rtype: list 129 | """ 130 | return [_deserialize(sub_data, boxed_type) 131 | for sub_data in data] 132 | 133 | 134 | def _deserialize_dict(data, boxed_type): 135 | """Deserializes a dict and its elements. 136 | 137 | :param data: dict to deserialize. 138 | :type data: dict 139 | :param boxed_type: class literal. 140 | 141 | :return: deserialized dict. 142 | :rtype: dict 143 | """ 144 | return {k: _deserialize(v, boxed_type) 145 | for k, v in six.iteritems(data)} 146 | -------------------------------------------------------------------------------- /src/main/resources/servers/python/generate-server.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | GREEN='\033[0;32m' 5 | RED='\033[0;31m' 6 | WHITE='\033[0;37m' 7 | RESET='\033[0m' 8 | 9 | if [ -x "$(command -v docker)" ]; then 10 | echo "Docker is installed" 11 | else 12 | echo "Docker is not installed" 13 | exit 1 14 | fi 15 | 16 | 17 | dir=${PWD} 18 | cp ../openapi.yaml ${PWD} 19 | 20 | SERVER_DIR=server 21 | 22 | 23 | docker run --rm -v ${PWD}:/local \ 24 | -u "$(id -u):$(id -u)" \ 25 | openapitools/openapi-generator-cli:v6.0.0 \ 26 | generate \ 27 | -i /local/openapi.yaml\ 28 | -g python-flask \ 29 | -o /local/$SERVER_DIR/ \ 30 | --template-dir /local/.openapi-generator/template \ 31 | --ignore-file-override /local/.openapi-generator-ignore 32 | 33 | cp -r ${PWD}/.openapi-generator/template/static_files/utils/ ${PWD}/$SERVER_DIR/openapi_server/utils/ 34 | cp -r ${PWD}/.openapi-generator/template/static_files/settings/ ${PWD}/$SERVER_DIR/openapi_server/settings/ 35 | cp -r ${PWD}/.openapi-generator/template/static_files/user_controller.py ${PWD}/$SERVER_DIR/openapi_server/controllers/ 36 | cp -r ${PWD}/.openapi-generator/template/static_files/cached.py ${PWD}/$SERVER_DIR/openapi_server/ 37 | mkdir -p ${PWD}/$SERVER_DIR/contexts/ 38 | echo "Copying query files" 39 | cp -r ../../queries ${PWD}/$SERVER_DIR/queries 40 | cp -r ../context.json ${PWD}/$SERVER_DIR/contexts/ 41 | cp -r ../context_class.json ${PWD}/$SERVER_DIR/contexts/ 42 | if [ "$?" == "0" ]; then 43 | echo -e "${GREEN}SUCCESS${RESET}" 44 | fi 45 | -------------------------------------------------------------------------------- /src/test/config/dbpedia.yaml: -------------------------------------------------------------------------------- 1 | #Name of the project 2 | name: dbpedia_music 3 | 4 | ## OpenAPI Section 5 | ### Name, version and URL of the OpenAPI 6 | ### For more information about the section. Go to the official documentation 7 | openapi: 8 | openapi: 3.0.1 9 | info: 10 | description: This is the API of the DBpedia Ontology 11 | title: DBpedia 12 | version: v1.3.0 13 | externalDocs: 14 | description: DBpedia 15 | url: https://w3id.org/okn/o/sdm 16 | servers: 17 | - url: https:///dbpedia.dbpedia.oba.isi.edu/v1.3.0 18 | - url: http://localhost:8080/v1.3.0 19 | 20 | ## Ontologies 21 | ### List of ontologies 22 | ontologies: 23 | - https://gist.githubusercontent.com/mosoriob/cec147b24bd241295584dfcc21c21b93/raw/b6fa41ddf93212d967f35da20278f54d2ae2d40d/gistfile1.txt 24 | 25 | ## SPARQL information 26 | endpoint: 27 | url: http://endpoint.mint.isi.edu/modelCatalog-1.2.0 28 | prefix: https://w3id.org/okn/i/mint 29 | graph_base: http://ontosoft.isi.edu:3030/modelCatalog-1.2.0/data/ 30 | 31 | ## Filter the paths by methods 32 | enable_get_paths: true 33 | enable_post_paths: false 34 | enable_delete_paths: false 35 | enable_put_paths: false 36 | 37 | ## Select the classes to add in the API 38 | classes: 39 | - http://dbpedia.org/ontology/Genre 40 | - http://dbpedia.org/ontology/Band 41 | follow_references: false 42 | -------------------------------------------------------------------------------- /src/test/config/mcat_reduced.yaml: -------------------------------------------------------------------------------- 1 | ontologies: 2 | - src/test/resources/modelCat.ttl 3 | name: modelcatalog-reduced 4 | output_dir: outputs 5 | 6 | openapi: 7 | openapi: 3.0.1 8 | info: 9 | description: This is the API of the Software Description Ontology at [https://w3id.org/okn/o/sdm](https://w3id.org/okn/o/sdm) 10 | title: Model Catalog 11 | version: v1.5.0 12 | externalDocs: 13 | description: Model Catalog 14 | url: https://w3id.org/okn/o/sdm 15 | servers: 16 | - url: https://api.models.mint.isi.edu/v1.5.0 17 | - url: https://dev.api.models.mint.isi.edu/v1.5.0 18 | - url: http://localhost:8080/v1.5.0 19 | 20 | 21 | endpoint: 22 | url: http://endpoint.mint.isi.edu/modelCatalog-1.4.0 23 | prefix: https://w3id.org/okn/i/masd 24 | graph_base: http://endpoint.mint.isi.edu/modelCatalog-1.4.0/data/ 25 | 26 | enable_get_paths: true 27 | enable_post_paths: false 28 | enable_delete_paths: false 29 | enable_put_paths: false 30 | 31 | follow_references: true 32 | 33 | -------------------------------------------------------------------------------- /src/test/config/missing_file.yaml: -------------------------------------------------------------------------------- 1 | ontologies: 2 | - src/test/resources/non_existent_file.ttl 3 | name: modelcatalog-reduced 4 | output_dir: outputs 5 | 6 | openapi: 7 | openapi: 3.0.1 8 | info: 9 | description: This is the API of the Software Description Ontology at [https://w3id.org/okn/o/sdm](https://w3id.org/okn/o/sdm) 10 | title: Model Catalog 11 | version: v1.5.0 12 | externalDocs: 13 | description: Model Catalog 14 | url: https://w3id.org/okn/o/sdm 15 | servers: 16 | - url: https://api.models.mint.isi.edu/v1.5.0 17 | - url: https://dev.api.models.mint.isi.edu/v1.5.0 18 | - url: http://localhost:8080/v1.5.0 19 | 20 | 21 | endpoint: 22 | url: http://endpoint.mint.isi.edu/modelCatalog-1.4.0 23 | prefix: https://w3id.org/okn/i/masd 24 | graph_base: http://endpoint.mint.isi.edu/modelCatalog-1.4.0/data/ 25 | 26 | enable_get_paths: true 27 | enable_post_paths: false 28 | enable_delete_paths: false 29 | enable_put_paths: false 30 | 31 | follow_references: true 32 | 33 | -------------------------------------------------------------------------------- /src/test/config/pplan.yaml: -------------------------------------------------------------------------------- 1 | ontologies: 2 | - http://purl.org/net/p-plan 3 | name: p-plan 4 | output_dir: outputs 5 | 6 | openapi: 7 | openapi: 3.0.1 8 | info: 9 | description: Example of the P-Plan ontology 10 | title: P-Plan 11 | version: v1.0.0 12 | externalDocs: 13 | description: P-Plan 14 | url: http://purl.org/net/p-plan 15 | 16 | #not used for this example. 17 | endpoint: 18 | url: http://endpoint.mint.isi.edu/example 19 | prefix: https://w3id.org/okn/i/example 20 | graph_base: http://endpoint.mint.isi.edu/example 21 | 22 | enable_get_paths: true 23 | enable_post_paths: false 24 | enable_delete_paths: false 25 | enable_put_paths: false 26 | 27 | follow_references: true 28 | 29 | -------------------------------------------------------------------------------- /src/test/java/edu/isi/oba/MapperTest.java: -------------------------------------------------------------------------------- 1 | package edu.isi.oba; 2 | 3 | import static edu.isi.oba.ObaUtils.get_yaml_data; 4 | import edu.isi.oba.config.AuthConfig; 5 | import edu.isi.oba.config.CONFIG_FLAG; 6 | import edu.isi.oba.config.YamlConfig; 7 | 8 | import java.io.File; 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.util.ArrayList; 12 | import java.util.Collections; 13 | import java.util.List; 14 | import java.util.Map; 15 | import java.util.logging.ConsoleHandler; 16 | import java.util.logging.Level; 17 | import java.util.logging.LogManager; 18 | import java.util.logging.Logger; 19 | 20 | import io.swagger.v3.oas.models.media.Schema; 21 | 22 | import org.junit.jupiter.api.Assertions; 23 | import org.junit.jupiter.api.Test; 24 | 25 | import org.semanticweb.owlapi.model.OWLClass; 26 | 27 | public class MapperTest { 28 | @Test 29 | public void testFilter() throws Exception{ 30 | String config_test_file_path = "src/test/config/dbpedia.yaml"; 31 | YamlConfig config_data = get_yaml_data(config_test_file_path); 32 | Mapper mapper = new Mapper(config_data); 33 | List config = config_data.getClasses(); 34 | List classes = mapper.filter_classes(); 35 | List filter_classes = new ArrayList(); 36 | for (OWLClass _class : classes){ 37 | filter_classes.add(_class.getIRI().getIRIString()); 38 | } 39 | Collections.sort(filter_classes); 40 | Collections.sort(config); 41 | Assertions.assertEquals(config, filter_classes); 42 | 43 | } 44 | 45 | /** 46 | * This test attempts to load a local ontology. 47 | * @throws java.lang.Exception 48 | */ 49 | @Test 50 | public void testLocalFile() throws Exception{ 51 | String local_ontology = "src/test/config/mcat_reduced.yaml"; 52 | YamlConfig config_data = get_yaml_data(local_ontology); 53 | Mapper mapper = new Mapper(config_data); 54 | Assertions.assertEquals(false, mapper.ontologies.isEmpty()); 55 | } 56 | 57 | /** 58 | * This test attempts to load a config in a folder with spaces. 59 | * @throws java.lang.Exception 60 | */ 61 | @Test 62 | public void testSpacesInPath() throws Exception{ 63 | String local_ontology = "examples/example with spaces/config.yaml"; 64 | YamlConfig config_data = get_yaml_data(local_ontology); 65 | Mapper mapper = new Mapper(config_data); 66 | Assertions.assertEquals(false, mapper.ontologies.isEmpty()); 67 | } 68 | 69 | /** 70 | * This test attempts to run OBA with an online ontology through a URI. 71 | * The ontology is hosted in GitHub, but there is a small risk of the test 72 | * not passing due to the unavailability of the ontology. 73 | * @throws java.lang.Exception 74 | */ 75 | @Test 76 | public void testRemoteOntology() throws Exception{ 77 | String example_remote = "src/test/config/pplan.yaml"; 78 | YamlConfig config_data = get_yaml_data(example_remote); 79 | Mapper mapper = new Mapper(config_data); 80 | Assertions.assertEquals(false, mapper.ontologies.isEmpty()); 81 | 82 | } 83 | 84 | /** 85 | * Test an ontology (very simple, two classes) with a missing import 86 | */ 87 | @Test 88 | public void testMissingImportOntology() throws Exception{ 89 | String example_remote = "src/test/resources/missing_import/config.yaml"; 90 | YamlConfig config_data = get_yaml_data(example_remote); 91 | Mapper mapper = new Mapper(config_data); 92 | Assertions.assertEquals(false, mapper.ontologies.isEmpty()); 93 | } 94 | 95 | /** 96 | * Test an ontology (very simple, two classes) with a missing import 97 | */ 98 | @Test 99 | public void testComplexOntology() throws Exception{ 100 | InputStream stream = Oba.class.getClassLoader().getResourceAsStream("logging.properties"); 101 | try { 102 | LogManager.getLogManager().readConfiguration(stream); 103 | edu.isi.oba.Oba.logger = Logger.getLogger(Oba.class.getName()); 104 | 105 | } catch (IOException e) { 106 | e.printStackTrace(); 107 | } 108 | edu.isi.oba.Oba.logger.setLevel(Level.FINE); 109 | edu.isi.oba.Oba.logger.addHandler(new ConsoleHandler()); 110 | String example_remote = "src/test/resources/complex_expr/config.yaml"; 111 | YamlConfig config_data = get_yaml_data(example_remote); 112 | String destination_dir = config_data.getOutput_dir() + File.separator + config_data.getName(); 113 | config_data.setAuth(new AuthConfig()); 114 | Mapper mapper = new Mapper(config_data); 115 | OWLClass cls = mapper.manager.getOWLDataFactory().getOWLClass("https://businessontology.com/ontology/Person"); 116 | String desc = ObaUtils.getDescription(cls, mapper.ontologies.get(0), true); 117 | MapperSchema mapperSchema = new MapperSchema(mapper.ontologies, cls, desc, mapper.schemaNames, mapper.ontologies.get(0), Map.ofEntries(Map.entry(CONFIG_FLAG.DEFAULT_DESCRIPTIONS, true), Map.entry(CONFIG_FLAG.DEFAULT_PROPERTIES, true), Map.entry(CONFIG_FLAG.FOLLOW_REFERENCES, true))); 118 | Schema schema = mapperSchema.getSchema(); 119 | // The person schema must not be null. 120 | Assertions.assertNotNull(schema); 121 | Assertions.assertEquals(schema.getName(),"Person"); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/test/java/edu/isi/oba/ObaUtilsTest.java: -------------------------------------------------------------------------------- 1 | package edu.isi.oba; 2 | 3 | import static edu.isi.oba.ObaUtils.get_yaml_data; 4 | import edu.isi.oba.config.YamlConfig; 5 | 6 | import java.io.File; 7 | import java.io.IOException; 8 | 9 | import org.json.JSONObject; 10 | 11 | import org.junit.jupiter.api.Assertions; 12 | import org.junit.jupiter.api.Test; 13 | 14 | import org.semanticweb.owlapi.model.OWLClass; 15 | import org.semanticweb.owlapi.model.OWLOntologyCreationException; 16 | 17 | public class ObaUtilsTest { 18 | 19 | @Test 20 | public void read_json_file() throws IOException { 21 | JSONObject actual = ObaUtils.read_json_file("json_one.json"); 22 | Assertions.assertNotNull(actual.get("@context")); 23 | } 24 | 25 | @Test 26 | public void mergeJSONObjects() throws IOException { 27 | JSONObject one = ObaUtils.read_json_file("json_one.json"); 28 | JSONObject two = ObaUtils.read_json_file("json_two.json"); 29 | JSONObject merge = ObaUtils.mergeJSONObjects(one, two); 30 | Assertions.assertNotNull(merge.get("@context")); 31 | Assertions.assertNotNull(merge.get("@context")); 32 | } 33 | 34 | @Test 35 | public void concat_json_common_key() throws IOException { 36 | JSONObject one = ObaUtils.read_json_file("json_one.json"); 37 | JSONObject two = ObaUtils.read_json_file("json_two.json"); 38 | JSONObject three = ObaUtils.read_json_file("json_three.json"); 39 | JSONObject[] jsons = new JSONObject[]{ one, two, three}; 40 | JSONObject merge = ObaUtils.concat_json_common_key(jsons, "@context"); 41 | JSONObject o = (JSONObject) merge.get("@context"); 42 | Assertions.assertNotNull(o.get("Entity")); 43 | Assertions.assertNotNull(o.get("Model")); 44 | Assertions.assertNotNull(o.get("Setup")); 45 | } 46 | 47 | @Test 48 | public void getDescription () throws OWLOntologyCreationException{ 49 | String example_remote = "src/test/config/pplan.yaml"; 50 | YamlConfig config_data = get_yaml_data(example_remote); 51 | try { 52 | Mapper mapper = new Mapper(config_data); 53 | OWLClass planClass = mapper.manager.getOWLDataFactory().getOWLClass("http://purl.org/net/p-plan#Plan"); 54 | String desc = ObaUtils.getDescription(planClass, mapper.ontologies.get(0), true); 55 | Assertions.assertNotEquals(desc, ""); 56 | }catch(Exception e){ 57 | Assertions.fail("Failed to get description.", e); 58 | } 59 | } 60 | 61 | /** 62 | * This test will try to load a file that does not exits. The exception is captured and reported. 63 | * This test will pass IF you see an error on the output terminal 64 | * @throws OWLOntologyCreationException 65 | */ 66 | @Test 67 | public void missing_file () throws OWLOntologyCreationException{ 68 | String missing_file = "src/test/config/missing_file.yaml"; 69 | YamlConfig config_data = get_yaml_data(missing_file); 70 | try { 71 | Mapper mapper = new Mapper(config_data); 72 | Assertions.fail("Missing file: If no exception is launched, fail test"); 73 | }catch(Exception e){ 74 | //pass test if there is an exception 75 | } 76 | } 77 | 78 | @Test 79 | public void run() { 80 | String ontology1 = "https://mintproject.github.io/Mint-ModelCatalog-Ontology/release/1.8.0/ontology.owl"; 81 | String ontology2 = "https://knowledgecaptureanddiscovery.github.io/SoftwareDescriptionOntology/release/1.9.0/ontology.owl"; 82 | File ont1 = new File("ontology1"); 83 | File ont2 = new File("ontology2"); 84 | ObaUtils.downloadOntology(ontology1, ont1.getPath()); 85 | ObaUtils.downloadOntology(ontology2, ont2.getPath()); 86 | String[] ontologies = new String[]{"ontology1", "ontology2"}; 87 | JSONObject context = null; 88 | try { 89 | context = ObaUtils.generate_context_file(ontologies, false); 90 | } catch (Exception e) { 91 | e.printStackTrace(); 92 | } 93 | 94 | JSONObject o = (JSONObject) context.get("@context"); 95 | Assertions.assertEquals(o.get("id"), "@id"); 96 | Assertions.assertEquals(o.get("type"), "@type"); 97 | Assertions.assertNotNull(o.get("Entity")); 98 | Assertions.assertNotNull(o.get("Model")); 99 | try{ 100 | java.nio.file.Files.delete(ont1.toPath()); 101 | java.nio.file.Files.delete(ont2.toPath()); 102 | }catch(Exception e){ 103 | } 104 | } 105 | 106 | } -------------------------------------------------------------------------------- /src/test/java/edu/isi/oba/config/ProviderTest.java: -------------------------------------------------------------------------------- 1 | package edu.isi.oba.config; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | public class ProviderTest { 7 | @Test 8 | public void getSelectedClasses() { 9 | Provider provider = Provider.get("firebase"); 10 | Provider provider_test = Provider.FIREBASE; 11 | Assertions.assertEquals(provider_test, provider); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/test/java/edu/isi/oba/config/YamlConfigTest.java: -------------------------------------------------------------------------------- 1 | package edu.isi.oba.config; 2 | 3 | import static edu.isi.oba.ObaUtils.get_yaml_data; 4 | 5 | import java.util.Arrays; 6 | import java.util.List; 7 | 8 | import org.junit.jupiter.api.Assertions; 9 | import org.junit.jupiter.api.Test; 10 | 11 | public class YamlConfigTest { 12 | @Test 13 | public void getSelectedClasses(){ 14 | String config_test_file_path = "examples/dbpedia/config_music.yaml"; 15 | YamlConfig config_data = get_yaml_data(config_test_file_path); 16 | List expected = Arrays.asList("http://dbpedia.org/ontology/Genre", "http://dbpedia.org/ontology/Band"); 17 | List config = config_data.getClasses(); 18 | Assertions.assertEquals(expected, config); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/resources/complex_expr/config.yaml: -------------------------------------------------------------------------------- 1 | #Name of the project 2 | name: BusinessOntology-v0.6 3 | 4 | ## OpenAPI Section 5 | ### Name, version and URL of the OpenAPI 6 | ### For more information about the section. Go to the official documentation 7 | openapi: 8 | openapi: 3.0.1 9 | info: 10 | description: This is the API of the Business Ontology 11 | title: Business Ontology test 12 | version: v1 13 | externalDocs: 14 | description: BusinessOntology 15 | url: https://www.semanticarts.com/gist/previous-versions/ 16 | servers: 17 | - url: http://localhost:8080/v1 18 | description: localhost server 19 | 20 | ## Ontologies 21 | ### List of ontologies 22 | ontologies: 23 | - src/test/resources/complex_expr/ontology.owl 24 | 25 | ## SPARQL information 26 | endpoint: 27 | url: http://localhost:7201/sparql 28 | prefix: https://businessontology.com/resource 29 | 30 | ## Filter the paths by methods 31 | enable_get_paths: true 32 | enable_post_paths: false 33 | enable_delete_paths: false 34 | enable_put_paths: false 35 | 36 | ## Select the classes to add in the API 37 | classes: 38 | - https://businessontology.com/ontology/Address 39 | - https://businessontology.com/ontology/AddressType 40 | - https://businessontology.com/ontology/BaseUnit 41 | - https://businessontology.com/ontology/Category 42 | - https://businessontology.com/ontology/Component 43 | - https://businessontology.com/ontology/Contract 44 | - https://businessontology.com/ontology/Duration 45 | - https://businessontology.com/ontology/DurationUnit 46 | - https://businessontology.com/ontology/Identifier 47 | - https://businessontology.com/ontology/IntellectualProperty 48 | - https://businessontology.com/ontology/LivingThing 49 | - https://businessontology.com/ontology/Magnitude 50 | - https://businessontology.com/ontology/Mass 51 | - https://businessontology.com/ontology/MassUnit 52 | - https://businessontology.com/ontology/Organization 53 | - https://businessontology.com/ontology/Person 54 | - https://businessontology.com/ontology/PhysicalSubstance 55 | - https://businessontology.com/ontology/System 56 | - https://businessontology.com/ontology/UnitOfMeasure 57 | - https://businessontology.com/ontology/Volume 58 | follow_references: true 59 | -------------------------------------------------------------------------------- /src/test/resources/json_one.json: -------------------------------------------------------------------------------- 1 | { 2 | "@context": { 3 | "Entity": { 4 | "@id": "http://www.w3.org/ns/prov#Entity" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /src/test/resources/json_three.json: -------------------------------------------------------------------------------- 1 | { 2 | "@context": { 3 | "Model": { 4 | "@id": "http://www.w3.org/ns/prov#Model" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /src/test/resources/json_two.json: -------------------------------------------------------------------------------- 1 | { 2 | "@context": { 3 | "Setup": { 4 | "@id": "http://www.w3.org/ns/prov#Setup" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /src/test/resources/missing_import/config.yaml: -------------------------------------------------------------------------------- 1 | ontologies: 2 | - src/test/resources/missing_import/missing_import.ttl 3 | name: missing-import-example 4 | output_dir: outputs 5 | 6 | openapi: 7 | openapi: 3.0.1 8 | info: 9 | description: This is the API for the restrictions example ontology 10 | version: v1.5.0 11 | title: Missing import example ontology 12 | externalDocs: 13 | description: Missing import example ontology 14 | url: https://w3id.org/example 15 | servers: 16 | - url: http://localhost:8080/v1.5.0 17 | 18 | firebase: 19 | key: "test" 20 | 21 | endpoint: 22 | url: http://localhost:8080/example-1.5.0 23 | prefix: https://w3id.org/example 24 | graph_base: http://localhost:8080/example-1.5.0/data/ 25 | 26 | enable_get_paths: true 27 | enable_post_paths: true 28 | enable_delete_paths: true 29 | enable_put_paths: true 30 | 31 | auth: 32 | provider: firebase 33 | follow_references: true 34 | -------------------------------------------------------------------------------- /src/test/resources/missing_import/missing_import.ttl: -------------------------------------------------------------------------------- 1 | @prefix : . 2 | @prefix owl: . 3 | @prefix rdf: . 4 | @prefix xml: . 5 | @prefix xsd: . 6 | @prefix rdfs: . 7 | @base . 8 | 9 | rdf:type owl:Ontology ; 10 | owl:imports ; 11 | rdfs:comment "Sample ontology missing an import"@en . 12 | 13 | ################################################################# 14 | # Classes 15 | ################################################################# 16 | 17 | ### https://example.org/missing-import#Banana 18 | :Banana rdf:type owl:Class ; 19 | rdfs:label "Banana"@en . 20 | 21 | ### https://example.org/missing-import#Orange 22 | :Orange rdf:type owl:Class ; 23 | rdfs:label "Orange"@en . 24 | -------------------------------------------------------------------------------- /src/test/resources/only_classes/config.yaml: -------------------------------------------------------------------------------- 1 | ontologies: 2 | - src/test/resources/only_classes/o.ttl 3 | name: only-classes 4 | output_dir: outputs 5 | 6 | openapi: 7 | openapi: 3.0.1 8 | info: 9 | description: This is the API for the restrictions example ontology 10 | version: v1.5.0 11 | title: Only 2 classes example ontology 12 | externalDocs: 13 | description: Only 2 classes 14 | url: https://w3id.org/example 15 | servers: 16 | - url: http://localhost:8080/v1.5.0 17 | 18 | firebase: 19 | key: "test" 20 | 21 | endpoint: 22 | url: http://localhost:8080/example-1.5.0 23 | prefix: https://w3id.org/example 24 | graph_base: http://localhost:8080/example-1.5.0/data/ 25 | 26 | enable_get_paths: true 27 | enable_post_paths: false 28 | enable_delete_paths: false 29 | enable_put_paths: false 30 | 31 | auth: 32 | provider: firebase 33 | follow_references: true 34 | -------------------------------------------------------------------------------- /src/test/resources/only_classes/o.ttl: -------------------------------------------------------------------------------- 1 | @prefix : . 2 | @prefix owl: . 3 | @prefix rdf: . 4 | @prefix xml: . 5 | @prefix xsd: . 6 | @prefix rdfs: . 7 | @base . 8 | 9 | rdf:type owl:Ontology ; 10 | rdfs:comment "Sample ontology with 2 classes and no properties"@en . 11 | 12 | ################################################################# 13 | # Classes 14 | ################################################################# 15 | 16 | ### https://example.org/missing-import#Banana 17 | :Banana rdf:type owl:Class ; 18 | rdfs:label "Banana"@en . 19 | 20 | ### https://example.org/missing-import#Orange 21 | :Orange rdf:type owl:Class ; 22 | rdfs:label "Orange"@en . 23 | --------------------------------------------------------------------------------