├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── LICENSE ├── README.md ├── hydra_openapi_parser ├── hydra_openapi_parser_v1 │ ├── __init__.py │ └── openapi_parser.py └── hydra_openapi_parser_v2 │ ├── __init__.py │ ├── exceptions.py │ ├── openapi_parser.py │ ├── parsers │ ├── __init__.py │ ├── api_info_parser.py │ ├── components_parser.py │ ├── method_parser.py │ ├── param_parser.py │ ├── path_parser.py │ ├── ref_parser.py │ ├── resp_parser.py │ └── schema_parser.py │ ├── processors │ ├── __init__.py │ ├── api_class_processor.py │ ├── api_info_processor.py │ ├── class_processor.py │ ├── collection_processor.py │ ├── op_processor.py │ ├── prop_processor.py │ └── status_processor.py │ └── utils.py ├── requirements.txt ├── samples ├── v1 │ ├── hydra_doc_sample.py │ └── petstore_openapi.yaml └── v2 │ ├── hydra_doc_sample.py │ ├── petstore-expanded.yaml │ └── uspto.yaml ├── setup.py └── tests ├── __init__.py └── test_parser.py /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. 4 | 5 | We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality. 6 | 7 | Examples of unacceptable behavior by participants include: 8 | 9 | * The use of sexualized language or imagery 10 | * Personal attacks 11 | * Trolling or insulting/derogatory comments 12 | * Public or private harassment 13 | * Publishing other's private information, such as physical or electronic addresses, without explicit permission 14 | * Other unethical or unprofessional conduct. 15 | 16 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team. 17 | 18 | This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. 19 | 20 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. 21 | 22 | This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0, available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/) 23 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ### Contribution Best Practices 2 | 3 | * Read this [how-to about Github workflow here](https://guides.github.com/introduction/flow/) if you are not familiar with. 4 | 5 | * Read all the texts related to [contributing for an OS community](https://github.com/HTTP-APIs/hydrus/tree/master/.github). 6 | 7 | * Read this [how-to about writing a PR](https://github.com/blog/1943-how-to-write-the-perfect-pull-request) and this [other how-to about writing a issue](https://wiredcraft.com/blog/how-we-write-our-github-issues/) 8 | 9 | * **first ask in chat**: if you find a problem, first ask for [help in the chat](https://gitter.im/HTTP-APIs/Lobby), then consider opening a issue. 10 | 11 | * **read history**: before opening a PR be sure that all the tests pass successfully. If any is failing for non-related reasons, annotate the test failure in the PR comment. 12 | 13 | * **PRs on develop**: any change should be PRed first in `develop`, `master` can only receive merge from develop. 14 | 15 | * **testing**: everything should work and be tested for Python 3.5.2 and above. 16 | 17 | * **free PR**: no permission is needed to work on the code. Fork `master`, submit a PR and ask for reviewing. PR is the natural place for code comparison and corrections. If many contributors have something ready in a PR, we can consider opening a branch in which different people working on the same part of the application can collaborate. 18 | 19 | * **pylint**: code in PRs should be accurately compliant with [PEP-8](https://www.python.org/dev/peps/pep-0008/), checking code with `pylint` is fine. 20 | 21 | * **mypy**: every module is and should in future provide type annotations using `mypy`. 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### I'm submitting a 2 | - [ ] bug report. 3 | - [ ] feature request. 4 | 5 | ### Current Behaviour: 6 | 7 | 8 | ### Expected Behaviour: 9 | 10 | 11 | ### Steps to reproduce: 12 | 13 | 14 | ### Do you want to work on this issue? 15 | 16 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Fixes # 5 | 6 | ### Checklist 7 | - [ ] My branch is up-to-date with upstream/develop branch. 8 | - [ ] Everything works and tested for Python 3.5.2 and above. 9 | 10 | ### Description 11 | 12 | 13 | ### Change logs 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 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 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # VScode project settings 94 | .vscode 95 | 96 | # Spyder project settings 97 | .spyderproject 98 | .spyproject 99 | 100 | # Rope project settings 101 | .ropeproject 102 | 103 | # mkdocs documentation 104 | /site 105 | 106 | # mypy 107 | .mypy_cache/ 108 | 109 | #Pipenv generated 110 | Pipfile 111 | Pipfile.lock 112 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Hydra Ecosystem 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hydra-openapi-parser 2 | This library contains the OpenAPI parser implemntaion in Python to be used with `hydrus` and `hydra-python-agent`. 3 | 4 | Currently the library consists of openapi_parser module which helps hydrus parse OpenAPI standard docs. 5 | 6 | To install the library: 7 | 8 | ```bash 9 | pip install git+https://github.com/HTTP-APIs/hydra-openapi-parser.git#egg=hydra_openapi_parser 10 | ``` 11 | 12 | Note :- If using hydrus, the library doesn't need to be installed separately as it is already a part of requirements.txt for hydrus. 13 | Usage 14 | 15 | To import the modules: 16 | 17 | ```python3 18 | from hydra_openapi_parser import openapi_parser 19 | ``` 20 | 21 | Porting out from hydrus the hydraspecs directory 22 | 23 | ## Sample use-cases of openapi_parser module 24 | 25 | Once the OpenAPI YAML document is assigned into the variable `doc` as a Python dictionary, you can do the following: 26 | - Parse the OpenAPI doc into a HydraDoc 27 | ```python3 28 | parsed_dict = openapi_parser.parse(doc) 29 | ``` 30 | - Generate an empty object 31 | ```python3 32 | object = openapi_parser.generate_empty_object() 33 | ``` 34 | - Test if an endpoint is valid 35 | ```python3 36 | path = 'A/B/{id}/C/D' 37 | openapi_parser.valid_endpoint(path) 38 | # False 39 | path = 'A/B/{id}' 40 | openapi_parser.valid_endpoint(path) 41 | # Collection 42 | path = 'A/B/C' 43 | openapi_parser.valid_endpoint(path) 44 | # True 45 | ``` 46 | - Extract class name from the path 47 | ```python3 48 | path = "A/B/C/Pet" 49 | path_list = path.split('/') 50 | openapi_parser.get_class_name(path_list) 51 | # Pet 52 | ``` 53 | - Fetch data from location 54 | ```python3 55 | path = '#/definitions/Order' 56 | path_list = path.split('/') 57 | data = openapi_parser.get_data_at_location(path_list, doc) 58 | # data is of type dict 59 | ``` 60 | - Remove variables from path 61 | ```python3 62 | path = "A/B/C/{id}" 63 | output = openapi_parser.sanitise_path(path) 64 | ``` 65 | - Identify if an object is a collection 66 | ```python3 67 | schema_block = { 68 | 'type': 'array', 69 | 'items': { 70 | '$ref': '#/definitions/Pet' 71 | } 72 | } 73 | method = "/Pet" 74 | openapi_parser.check_collection(schema_block, method) 75 | # True 76 | ``` 77 | -------------------------------------------------------------------------------- /hydra_openapi_parser/hydra_openapi_parser_v1/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTTP-APIs/hydra-openapi-parser/329379434d9bc805883aa5934a85824e5fae1df2/hydra_openapi_parser/hydra_openapi_parser_v1/__init__.py -------------------------------------------------------------------------------- /hydra_openapi_parser/hydra_openapi_parser_v1/openapi_parser.py: -------------------------------------------------------------------------------- 1 | """ 2 | Module to take in Open Api Specification and convert it to HYDRA Api Doc 3 | 4 | """ 5 | import os 6 | import yaml 7 | import json 8 | from typing import Any, Dict, Match, Optional, Tuple, Union, List, Set 9 | from hydra_python_core.doc_writer import ( 10 | HydraDoc, 11 | HydraClass, 12 | HydraClassProp, 13 | HydraClassOp, 14 | ) 15 | import sys 16 | 17 | 18 | def try_catch_replacement(block: Any, get_this: str, default: Any) -> str: 19 | """ 20 | Replacement for the try catch blocks. HELPER FUNCTION 21 | :param block: Data from where information has to be parsed 22 | :param get_this: The key using which we have to fetch values from the block 23 | :param default: default value incase the key does not exist 24 | :return: string containing the value 25 | """ 26 | try: 27 | return block[get_this] 28 | except KeyError: 29 | return default 30 | 31 | 32 | def generateEntrypoint(api_doc: HydraDoc) -> None: 33 | """ 34 | Generates Entrypoint , 35 | Base Collection and Base Resource for the documentation 36 | :param api_doc: contains the Hydra Doc created 37 | """ 38 | api_doc.add_baseCollection() 39 | api_doc.add_baseResource() 40 | api_doc.gen_EntryPoint() 41 | 42 | 43 | def generate_empty_object() -> Dict[str, Any]: 44 | """ 45 | Generate Empty object 46 | :return: empty object 47 | """ 48 | object = { 49 | "class_name": "", 50 | "class_definition": HydraClass, 51 | "prop_definition": list(), 52 | "op_definition": list(), 53 | "collection": False, 54 | "path": "", 55 | "methods": set(), 56 | } 57 | return object 58 | 59 | 60 | def check_collection(schema_obj: Dict[str, Any], method: str) -> bool: 61 | """ 62 | Checks if the method is collection or not , checks if the method is valid 63 | :param class_name: name of class being parsed 64 | :param global_: global state 65 | :param schema_obj: the dict containing the method object 66 | :param method: method ie GET,POST,PUT,DELETE 67 | :return: object that is formed or updated 68 | """ 69 | collection = bool 70 | # get the object type from schema block 71 | try: 72 | type = schema_obj["type"] 73 | # if object type is array it means the method is a collection 74 | if type == "array": 75 | collection = True 76 | else: 77 | # here type should be "object" 78 | collection = False 79 | except KeyError: 80 | collection = False 81 | # checks if the method is something like 'pet/{petid}' 82 | if valid_endpoint(method) == "collection" and collection is False: 83 | collection = True 84 | return collection 85 | 86 | 87 | def check_array_param(paths_: Dict[str, Any]) -> bool: 88 | """ 89 | Check if the path is supported or not 90 | :param paths_: the path object from doc 91 | :return: TRUE if the path is supported 92 | """ 93 | for method in paths_: 94 | for param in paths_[method]["parameters"]: 95 | try: 96 | if param["type"] == "array" and method == "get": 97 | return False 98 | except KeyError: 99 | pass 100 | return True 101 | 102 | 103 | def valid_endpoint(path: str) -> str: 104 | """ 105 | Checks is the path ie endpoint is constructed properly or not 106 | rejects 'A/{id}/B/C' 107 | :param path: endpoint 108 | :return: 109 | """ 110 | # "collection" or true means valid 111 | path_ = path.split("/") 112 | for subPath in path_: 113 | if "{" in subPath: 114 | if subPath != path_[len(path_) - 1]: 115 | return "False" 116 | else: 117 | return "Collection" 118 | return "True" 119 | 120 | 121 | def get_class_name(class_location: List[str]) -> str: 122 | """ 123 | To get class name from the class location reference given 124 | :param class_location: list containing the class location 125 | :return: name of class 126 | """ 127 | return class_location[len(class_location) - 1] 128 | 129 | 130 | def get_data_at_location( 131 | class_location: List[str], doc: Dict[str, Any] 132 | ) -> Dict[str, Any]: 133 | """ 134 | TO get the dict at the class location provided 135 | :param class_location: list containing the class location 136 | :param doc: the open api doc 137 | :return: class defined at class location in the doc 138 | """ 139 | data = doc 140 | index = 0 141 | while index <= len(class_location) - 3: 142 | data = data[class_location[index + 1]][class_location[index + 2]] 143 | index = index + 1 144 | return data 145 | 146 | 147 | def sanitise_path(path: str) -> str: 148 | """ 149 | Removed any variable present in the path 150 | :param path: 151 | :return: 152 | """ 153 | path_ = path.split("/") 154 | new_path = list() 155 | for subPath in path_: 156 | if "{" in subPath: 157 | pass 158 | else: 159 | new_path.append(subPath) 160 | result = "/".join(new_path)[1:] 161 | 162 | return result 163 | 164 | 165 | def get_class_details( 166 | global_: Dict[str, Any], data: Dict[str, Any], class_name: str, path="" 167 | ) -> None: 168 | """ 169 | fetches details of class and adds the class to the dict along with the 170 | classDefinition until this point 171 | :param global_: global state 172 | :param class_name: name of class 173 | :param data: data from the location given in $ref 174 | :param path: Optional , custom enpoint to be assigned 175 | :return: None 176 | """ 177 | doc = global_["doc"] 178 | path = sanitise_path(path) 179 | 180 | class_name = class_name 181 | # we simply check if the class has been defined or not 182 | 183 | if not hasattr(global_[class_name]["class_definition"], "endpoint"): 184 | desc = data 185 | try: 186 | classDefinition = HydraClass( 187 | class_name, class_name, desc["description"], endpoint=True, path=path 188 | ) 189 | except KeyError: 190 | classDefinition = HydraClass( 191 | class_name, class_name, class_name, endpoint=True, path=path 192 | ) 193 | # we need to add object to global before we can attach props 194 | added = generateOrUpdateClass(class_name, False, global_, "") 195 | if added: 196 | global_[class_name]["class_definition"] = classDefinition 197 | 198 | properties = data["properties"] 199 | try: 200 | required = data["required"] 201 | except KeyError: 202 | required = set() 203 | 204 | for prop in properties: 205 | vocabFlag = True 206 | errFlag = False 207 | if prop not in global_["class_names"]: 208 | try: 209 | ref = properties[prop]["$ref"].split("/") 210 | 211 | if ref[0] == "#": 212 | get_class_details( 213 | global_, 214 | get_data_at_location(ref, global_["doc"]), 215 | get_class_name(ref), 216 | get_class_name(ref), 217 | ) 218 | else: 219 | vocabFlag = False 220 | except KeyError: 221 | # throw exception 222 | # ERROR 223 | errFlag = True 224 | pass 225 | except AttributeError: 226 | # ERROR thow 227 | pass 228 | flag = False 229 | if prop in required and len(required) > 0: 230 | flag = True 231 | if vocabFlag: 232 | if errFlag: 233 | global_[class_name]["prop_definition"].append( 234 | HydraClassProp("", prop, required=flag, read=True, write=True) 235 | ) 236 | else: 237 | global_[class_name]["prop_definition"].append( 238 | HydraClassProp( 239 | "vocab:{}".format(prop), 240 | prop, 241 | required=flag, 242 | read=True, 243 | write=True, 244 | ) 245 | ) 246 | else: 247 | global_[class_name]["prop_definition"].append( 248 | HydraClassProp(prop, prop, required=flag, read=True, write=True) 249 | ) 250 | global_[class_name]["path"] = path 251 | global_[class_name]["class_definition"] = classDefinition 252 | global_["class_names"].add(class_name) 253 | 254 | 255 | def generateOrUpdateClass(name, collection, global_, path) -> bool: 256 | """ 257 | Generates or Updates the class if it already exists 258 | :param name: class name 259 | :param collection: if the class is collection or not 260 | :param global_: global state 261 | :param path: path 262 | :return: bool showing if the operation was successful 263 | """ 264 | if valid_endpoint(path): 265 | if name in global_["class_names"] and collection is True: 266 | global_[name]["collection"] = True 267 | return True 268 | elif name in global_["class_names"] and collection is False: 269 | return True 270 | else: 271 | object_ = generate_empty_object() 272 | object_["class_name"] = name 273 | object_["collection"] = collection 274 | global_[name] = object_ 275 | global_["class_names"].add(name) 276 | return True 277 | else: 278 | return False 279 | 280 | 281 | def check_for_ref(global_: Dict[str, Any], path: str, block: Dict[str, Any]) -> str: 282 | """ 283 | Checks for references in responses and parameters key , 284 | and adds classes to state 285 | :param global_: global state 286 | :param path: endpoint 287 | :param block: block containing specific part of doc 288 | :return: class name 289 | """ 290 | 291 | # will check if there is an external ref , go to that location, 292 | # add the class in globals , will also verify 293 | # if we can parse this method or not , if all good will return class name 294 | for obj in block["responses"]: 295 | try: 296 | try: 297 | # can only be internal 298 | class_location = block["responses"][obj]["schema"]["$ref"].split("/") 299 | except KeyError: 300 | class_location = block["responses"][obj]["schema"]["items"][ 301 | "$ref" 302 | ].split("/") 303 | collection = check_collection( 304 | schema_obj=block["responses"][obj]["schema"], method=path 305 | ) 306 | success = generateOrUpdateClass( 307 | get_class_name(class_location), collection, global_, path 308 | ) 309 | if not success: 310 | return "" 311 | 312 | get_class_details( 313 | global_, 314 | get_data_at_location(class_location, global_["doc"]), 315 | get_class_name(class_location), 316 | path=path, 317 | ) 318 | return class_location[2] 319 | except KeyError: 320 | pass 321 | 322 | # when we would be able to take arrays as parameters we will use 323 | # check_if_collection here as well 324 | flag = try_catch_replacement(block, "parameters", "False") 325 | if flag != "False": 326 | for obj in block["parameters"]: 327 | try: 328 | try: 329 | class_location = obj["schema"]["$ref"].split("/") 330 | except KeyError: 331 | class_location = obj["schema"]["items"]["$ref"].split("/") 332 | collection_ = check_collection(obj["schema"], path) 333 | success = generateOrUpdateClass( 334 | get_class_name(class_location), collection_, global_, path 335 | ) 336 | if not success: 337 | return "" 338 | get_class_details( 339 | global_, 340 | get_data_at_location(class_location, global_["doc"]), 341 | get_class_name(class_location), 342 | path=path, 343 | ) 344 | return class_location[2] 345 | except KeyError: 346 | pass 347 | # cannot parse because no external ref 348 | 349 | print("Cannot parse path {} because no ref to local class provided".format(path)) 350 | return "" 351 | 352 | 353 | def allow_parameter(parameter: Dict[str, Any]) -> bool: 354 | """ 355 | Checks the validity of params that are to be processed 356 | according to rules of param passing 357 | :param parameter: the parameter to be parsed 358 | :return: if its valid or not 359 | """ 360 | # can add rules about param processing 361 | # param can be in path too , that is already handled when we declared 362 | # the class as collection from the endpoint 363 | params_location = [ 364 | "body", 365 | "integer", 366 | "string", 367 | "long", 368 | "float", 369 | "boolean", 370 | "dateTime", 371 | "Date", 372 | "array", 373 | ] 374 | try: 375 | if parameter["type"] in params_location: 376 | return True 377 | except KeyError: 378 | if parameter["in"] in params_location: 379 | return True 380 | 381 | return False 382 | 383 | 384 | def type_ref_mapping(type: str) -> str: 385 | """ 386 | Returns semantic ref for OAS data types 387 | :param type: data type 388 | :return: ref 389 | """ 390 | dataType_ref_map = dict() 391 | # todo add support for byte , binary , password ,double data types 392 | dataType_ref_map["integer"] = "https://schema.org/Integer" 393 | dataType_ref_map["string"] = "https://schema.org/Text" 394 | dataType_ref_map["long"] = "http://books.xmlschemata.org/relaxng/ch19-77199.html" 395 | dataType_ref_map["float"] = "https://schema.org/Float" 396 | dataType_ref_map["boolean"] = "https://schema.org/Boolean" 397 | dataType_ref_map["dateTime"] = "https://schema.org/DateTime" 398 | dataType_ref_map["date"] = "https://schema.org/Date" 399 | 400 | return dataType_ref_map[type] 401 | 402 | 403 | def get_parameters( 404 | global_: Dict[str, Any], path: str, method: str, class_name: str 405 | ) -> str: 406 | """ 407 | Parse paramters from method object 408 | :param global_: global state 409 | :param path: endpoint 410 | :param method: method under consideration 411 | :param class_name: name of class 412 | :return: param 413 | """ 414 | param = str 415 | for parameter in global_["doc"]["paths"][path][method]["parameters"]: 416 | # will call schema_parse with class name and schema block 417 | # after checking if type exists 418 | # coz there are instances where no schema key is present 419 | if allow_parameter(parameter): 420 | try: 421 | # check if class has been pared 422 | if parameter["schema"]["$ref"].split("/")[2] in global_["class_names"]: 423 | param = "vocab:{}".format(parameter["schema"]["$ref"].split("/")[2]) 424 | 425 | else: 426 | # if not go to that location and parse and add 427 | get_class_details( 428 | global_, 429 | get_data_at_location(parameter["schema"]["$ref"]), 430 | parameter["schema"]["$ref"].split("/")[2], 431 | path=path, 432 | ) 433 | param = "vocab:{}".format(parameter["schema"]["$ref"].split("/")[2]) 434 | except KeyError: 435 | type = parameter["type"] 436 | if type == "array": 437 | # TODO adaptation to array representation after discussion 438 | items = parameter["items"] 439 | try: 440 | if items["$ref"].split("/")[2] in global_["class_names"]: 441 | param = "vocab" + items["$ref"].split("/")[2] 442 | else: 443 | get_class_details( 444 | global_, 445 | get_data_at_location(items["$ref"]), 446 | items["$ref"].split("/")[2], 447 | path=path, 448 | ) 449 | param = "vocab" + items["$ref"].split("/")[2] 450 | except KeyError: 451 | param = type_ref_mapping(items["type"]) 452 | elif type == "object": 453 | param = "string" 454 | else: 455 | param = type_ref_mapping(type) 456 | 457 | return param 458 | 459 | 460 | def get_ops( 461 | global_: Dict[str, Any], path: str, method: Dict[str, Any], class_name: str 462 | ) -> None: 463 | """ 464 | Get operations from path object and store in global path 465 | :param global_: global state 466 | :param path: endpoint 467 | :param method: method block 468 | :param class_name:class name 469 | """ 470 | if method not in global_[class_name]["methods"]: 471 | op_method = method 472 | 473 | op_expects = None 474 | op_name = try_catch_replacement( 475 | global_["doc"]["paths"][path][method], "summary", class_name 476 | ) 477 | op_status = list() 478 | op_expects = get_parameters(global_, path, method, class_name) 479 | try: 480 | responses = global_["doc"]["paths"][path][method]["responses"] 481 | op_returns = None 482 | for response in responses: 483 | if response != "default": 484 | op_status.append( 485 | { 486 | "statusCode": int(response), 487 | "description": responses[response]["description"], 488 | } 489 | ) 490 | try: 491 | op_returns = "vocab:{}".format( 492 | responses[response]["schema"]["$ref"].split("/")[2] 493 | ) 494 | except KeyError: 495 | pass 496 | if op_returns is None: 497 | try: 498 | op_returns = "vocab:{}".format( 499 | responses[response]["schema"]["items"]["$ref"].split("/")[2] 500 | ) 501 | except KeyError: 502 | op_returns = try_catch_replacement( 503 | responses[response]["schema"], "type", None 504 | ) 505 | except KeyError: 506 | op_returns = None 507 | if len(op_status) == 0: 508 | op_status.append({"statusCode": 200, "description": "Successful Operation"}) 509 | global_[class_name]["methods"].add(method) 510 | global_[class_name]["op_definition"].append( 511 | HydraClassOp(op_name, op_method.upper(), op_expects, op_returns, op_status) 512 | ) 513 | else: 514 | print("Method on path {} already present !".format(path)) 515 | 516 | 517 | def get_paths(global_: Dict[str, Any]) -> None: 518 | """ 519 | Parse paths iteratively 520 | :param global_: Global state 521 | """ 522 | paths = global_["doc"]["paths"] 523 | for path in paths: 524 | for method in paths[path]: 525 | class_name = check_for_ref(global_, path, paths[path][method]) 526 | if class_name != "": 527 | # do further processing 528 | get_ops(global_, path, method, class_name) 529 | 530 | 531 | def parse(doc: Dict[str, Any]) -> Dict[str, Any]: 532 | """ 533 | To parse the "info" block and create Hydra Doc 534 | :param doc: the open api documentation 535 | :return: hydra doc created 536 | """ 537 | definitionSet = set() # type: Set[str] 538 | info = try_catch_replacement(doc, "info", "") 539 | global_ = dict() 540 | global_["class_names"] = definitionSet 541 | global_["doc"] = doc 542 | 543 | if info != "": 544 | desc = try_catch_replacement(info, "description", "not defined") 545 | title = try_catch_replacement(info, "title", "not defined") 546 | else: 547 | desc = "not defined" 548 | title = "not defined" 549 | print("Desc and title not present hence exit") 550 | sys.exit() 551 | baseURL = try_catch_replacement(doc, "host", "localhost") 552 | name = try_catch_replacement(doc, "basePath", "api") 553 | schemes = try_catch_replacement(doc, "schemes", "http") 554 | api_doc = HydraDoc( 555 | name, title, desc, name, "{}://{}".format(schemes[0], baseURL), "vocab" 556 | ) 557 | get_paths(global_) 558 | for name in global_["class_names"]: 559 | for prop in global_[name]["prop_definition"]: 560 | global_[name]["class_definition"].add_supported_prop(prop) 561 | for op in global_[name]["op_definition"]: 562 | global_[name]["class_definition"].add_supported_op(op) 563 | if global_[name]["collection"] is True: 564 | if global_[name]["class_definition"].endpoint is True: 565 | global_[name]["class_definition"].endpoint = False 566 | 567 | api_doc.add_supported_class( 568 | global_[name]["class_definition"], 569 | global_[name]["collection"], 570 | collection_path=global_[name]["path"], 571 | ) 572 | 573 | generateEntrypoint(api_doc) 574 | hydra_doc = api_doc.generate() 575 | 576 | return hydra_doc 577 | 578 | 579 | def dump_documentation(hydra_doc: Dict[str, Any]) -> str: 580 | """ 581 | Helper function to dump generated hydradoc > py file. 582 | :param doc: generated hydra doc 583 | :return: hydra doc created 584 | """ 585 | dump = json.dumps(hydra_doc, indent=4, sort_keys=True) 586 | hydra_doc = '''"""\nGenerated API Documentation for Server API using 587 | server_doc_gen.py."""\n\ndoc = {}'''.format( 588 | dump 589 | ) 590 | hydra_doc = "{}\n".format(hydra_doc) 591 | hydra_doc = hydra_doc.replace("true", '"true"') 592 | hydra_doc = hydra_doc.replace("false", '"false"') 593 | hydra_doc = hydra_doc.replace("null", '"null"') 594 | 595 | return hydra_doc 596 | 597 | 598 | if __name__ == "__main__": 599 | # construct the path for the input OpenAPI doc 600 | current_dir = os.path.dirname(__file__) 601 | input_file_path = os.path.join( 602 | current_dir, "../../samples/v1/petstore_openapi.yaml" 603 | ) 604 | with open(input_file_path) as stream: 605 | try: 606 | doc = yaml.load(stream) 607 | except yaml.YAMLError as exc: 608 | print(exc) 609 | hydra_doc = parse(doc) 610 | 611 | # construct the path for the output Hydra API doc 612 | output_file_path = os.path.join(current_dir, "../../samples/v1/hydra_doc_sample.py") 613 | with open(output_file_path, "w") as f: 614 | f.write(dump_documentation(hydra_doc)) 615 | -------------------------------------------------------------------------------- /hydra_openapi_parser/hydra_openapi_parser_v2/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTTP-APIs/hydra-openapi-parser/329379434d9bc805883aa5934a85824e5fae1df2/hydra_openapi_parser/hydra_openapi_parser_v2/__init__.py -------------------------------------------------------------------------------- /hydra_openapi_parser/hydra_openapi_parser_v2/exceptions.py: -------------------------------------------------------------------------------- 1 | class HydraCollectionException(Exception): 2 | def __init__(self, message) -> None: 3 | super().__init__(message) 4 | 5 | 6 | class ExpectsValueError(Exception): 7 | def __init__(self, message) -> None: 8 | super().__init__(message) 9 | -------------------------------------------------------------------------------- /hydra_openapi_parser/hydra_openapi_parser_v2/openapi_parser.py: -------------------------------------------------------------------------------- 1 | import os 2 | import yaml 3 | from processors.api_info_processor import APIInfoProcessor 4 | from processors.api_class_processor import APIClassProcessor 5 | from utils import gen_entrypoint, gen_doc_file 6 | from hydra_python_core.doc_writer import HydraStatus 7 | 8 | 9 | class OpenAPIDocParser: 10 | def __init__(self, inp_path: str) -> None: 11 | # construct the path for the input OpenAPI doc 12 | self.current_dir = os.path.dirname(__file__) 13 | input_file_path = os.path.join(self.current_dir, inp_path) 14 | with open(input_file_path) as stream: 15 | try: 16 | self.openapi_doc = yaml.safe_load(stream) 17 | except yaml.YAMLError as exc: 18 | print(exc) 19 | 20 | def parse(self, out_path: str) -> str: 21 | # create basic hydra doc with general info (@id, @context, description etc.) 22 | info = APIInfoProcessor(self.openapi_doc) 23 | api_info_doc = info.generate() 24 | api_doc = gen_entrypoint(api_info_doc) 25 | 26 | # create supported classes for hydra doc 27 | api_classes = APIClassProcessor(self.openapi_doc) 28 | supported_classes, supported_collections = api_classes.generate() 29 | for supported_class in supported_classes: 30 | api_doc.add_supported_class(supported_class) 31 | 32 | # create supported collections for hydra doc 33 | for supported_collection in supported_collections: 34 | api_doc.add_supported_collection(supported_collection) 35 | 36 | hydra_doc = api_doc.generate() 37 | 38 | if out_path: 39 | # construct the path for the output Hydra doc 40 | output_file_path = os.path.join(self.current_dir, out_path) 41 | with open(output_file_path, "w") as f: 42 | f.write(gen_doc_file(hydra_doc)) 43 | 44 | return hydra_doc 45 | 46 | 47 | if __name__ == "__main__": 48 | petstore_doc_path = "../../samples/v2/petstore-expanded.yaml" 49 | uspto_doc_path = "../../samples/v2/uspto.yaml" 50 | openapi_doc = OpenAPIDocParser(petstore_doc_path) 51 | hydra_doc = openapi_doc.parse("../../samples/v2/hydra_doc_sample.py") 52 | -------------------------------------------------------------------------------- /hydra_openapi_parser/hydra_openapi_parser_v2/parsers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTTP-APIs/hydra-openapi-parser/329379434d9bc805883aa5934a85824e5fae1df2/hydra_openapi_parser/hydra_openapi_parser_v2/parsers/__init__.py -------------------------------------------------------------------------------- /hydra_openapi_parser/hydra_openapi_parser_v2/parsers/api_info_parser.py: -------------------------------------------------------------------------------- 1 | import re 2 | from urllib.parse import urlparse 3 | 4 | 5 | class InfoParser: 6 | def __init__(self, doc) -> None: 7 | self.doc = doc 8 | 9 | def parse(self): 10 | info = dict() 11 | info_ = dict() 12 | for key, value in self.doc.get("info").items(): 13 | info[key] = value 14 | info["url"] = self.doc.get("servers")[0].get("url") 15 | 16 | # check for variables in the url 17 | if self.doc.get("servers")[0].get("variables"): 18 | server = self.doc.get("servers")[0] 19 | for variable, variable_details in server.get("variables").items(): 20 | info["url"] = info["url"].replace( 21 | rf"{{{variable}}}", variable_details.get("default") 22 | ) 23 | url = urlparse(info["url"]) 24 | 25 | info_ = { 26 | "api": info.get(url.path.split("/")[0], "api"), 27 | "title": info.get("title", ""), 28 | "desc": info.get("description", ""), 29 | "base_url": f"{url.scheme}://{url.netloc}", 30 | "doc_name": info.get("", "vocab"), 31 | } 32 | info_["api"] = "{}/v{}".format( 33 | info_["api"], info.get("version", "1.0.0").split(".")[0] 34 | ) 35 | info_["entrypoint"] = f'{info_["base_url"]}/{info_["api"]}' 36 | info_["id"] = f'{info_["entrypoint"]}/{info_["doc_name"]}' 37 | return info_ 38 | -------------------------------------------------------------------------------- /hydra_openapi_parser/hydra_openapi_parser_v2/parsers/components_parser.py: -------------------------------------------------------------------------------- 1 | from utils import component_class_mapping 2 | 3 | 4 | class ComponentParser: 5 | def __init__(self, component, definition) -> None: 6 | self.component = component 7 | self.definition = definition 8 | 9 | def parse(self): 10 | Parser = component_class_mapping(self.component) 11 | parser = Parser(self.definition) 12 | return parser.parse() 13 | -------------------------------------------------------------------------------- /hydra_openapi_parser/hydra_openapi_parser_v2/parsers/method_parser.py: -------------------------------------------------------------------------------- 1 | from hydra_python_core.doc_writer import ( 2 | HydraClassOp, 3 | HydraClassProp, 4 | HydraError, 5 | HydraStatus, 6 | ) 7 | from exceptions import HydraCollectionException 8 | from processors.api_info_processor import APIInfoProcessor 9 | from parsers.param_parser import ParameterParser 10 | from parsers.resp_parser import ResponseParser 11 | from processors.op_processor import OperationProcessor 12 | from parsers.schema_parser import SchemaParser 13 | 14 | from typing import Any, List, Dict, Union 15 | 16 | 17 | class MethodParser: 18 | def __init__(self, method: str, method_details: Dict[str, Any], id: str) -> None: 19 | self.method = method.upper() 20 | self.method_details = method_details 21 | self.id = id 22 | 23 | def parse(self) -> List[Union[HydraClassOp, List[HydraClassProp]]]: 24 | method_title = str 25 | hydra_props: List[HydraClassProp] = [] 26 | hydra_op: HydraClassOp 27 | possible_status: List[Union[HydraStatus, HydraError]] = [] 28 | expects_resource = "" 29 | returns_resource = "" 30 | for key, value in self.method_details.items(): 31 | if key == "parameters": 32 | for parameter in value: 33 | param_parser = ParameterParser(parameter) 34 | hydra_class_prop = param_parser.parse() 35 | hydra_props.append(hydra_class_prop) 36 | elif key == "responses": 37 | for code, response in value.items(): 38 | response_parser = ResponseParser(code, response) 39 | hydra_status = response_parser.parse() 40 | possible_status.append(hydra_status) 41 | if response_parser.parse_code() != 500: 42 | returns_resource = response_parser.parse_returns() 43 | 44 | elif key == "operationId": 45 | method_title = value 46 | elif key == "requestBody": 47 | request_content = value.get("content") 48 | for _, expects in request_content.items(): 49 | schema_parser = SchemaParser(expects.get("schema")) 50 | hydra_classes, _ = schema_parser.parse() 51 | for title, _ in hydra_classes.items(): 52 | expects_resource = title 53 | 54 | operation_processor = OperationProcessor( 55 | title=method_title, 56 | method=self.method, 57 | id=self.id, 58 | possible_status=possible_status, 59 | expects=expects_resource, 60 | returns=returns_resource, 61 | ) 62 | hydra_op = operation_processor.generate() 63 | 64 | return [hydra_op, hydra_props] 65 | -------------------------------------------------------------------------------- /hydra_openapi_parser/hydra_openapi_parser_v2/parsers/param_parser.py: -------------------------------------------------------------------------------- 1 | from typing import Type 2 | from processors.prop_processor import PropertyProcessor 3 | from utils import type_ref_mapping 4 | from exceptions import ExpectsValueError, HydraCollectionException 5 | 6 | 7 | class ParameterParser: 8 | def __init__(self, parameter) -> None: 9 | self.parameter = parameter 10 | 11 | def parse(self): 12 | is_collection = False 13 | prop = "null" 14 | title = ("null",) 15 | required = (False,) 16 | 17 | for key, value in self.parameter.items(): 18 | if key == "schema": 19 | schema = value 20 | if schema.get("type") == "array": 21 | is_collection = True 22 | else: 23 | try: 24 | prop = type_ref_mapping(schema.get("type")) 25 | except KeyError: 26 | raise ExpectsValueError( 27 | "{} is incorrect parameter for 'supportedProperty'.".format( 28 | schema.get("type") 29 | ) 30 | ) 31 | elif key == "required": 32 | required = value 33 | elif key == "name": 34 | title = value 35 | 36 | if is_collection: 37 | raise HydraCollectionException("Parsed parameter is a collection.") 38 | 39 | property_processor = PropertyProcessor(prop, title, required) 40 | hydra_prop = property_processor.generate() 41 | return hydra_prop 42 | -------------------------------------------------------------------------------- /hydra_openapi_parser/hydra_openapi_parser_v2/parsers/path_parser.py: -------------------------------------------------------------------------------- 1 | from typing import Union, List, Dict 2 | from hydra_python_core.doc_writer import ( 3 | HydraClassOp, 4 | HydraClassProp, 5 | ) 6 | from exceptions import HydraCollectionException 7 | from parsers.method_parser import MethodParser 8 | 9 | 10 | class PathParser: 11 | def __init__(self, path_name, path_method, id) -> None: 12 | self.path_method = path_method 13 | self.id = f'{id}?resource={path_name.split("/")[1].capitalize()}' 14 | self.hydra_class_ops = [] 15 | self.hydra_class_props = [] 16 | self.hydra_collection_ops = {} 17 | 18 | def is_parsed(self): 19 | if self.hydra_class_ops or self.hydra_collection_ops or self.hydra_class_props: 20 | return True 21 | return False 22 | 23 | def get_class_props(self): 24 | if not self.is_parsed(): 25 | self.parse() 26 | return self.hydra_class_props 27 | 28 | def get_class_ops(self) -> List[HydraClassOp]: 29 | if not self.is_parsed(): 30 | self.parse() 31 | return self.hydra_class_ops 32 | 33 | def get_collection_ops(self) -> Dict[str, bool]: 34 | if not self.is_parsed(): 35 | self.parse() 36 | return self.hydra_collection_ops 37 | 38 | def parse(self) -> None: 39 | for method_name, method_details in self.path_method.items(): 40 | try: 41 | method_parser = MethodParser(method_name, method_details, self.id) 42 | hydra_class_op_, hydra_class_props_ = method_parser.parse() 43 | self.hydra_class_ops.append(hydra_class_op_) 44 | self.hydra_class_props.extend(hydra_class_props_) 45 | except HydraCollectionException: 46 | self.hydra_collection_ops[method_name] = True 47 | -------------------------------------------------------------------------------- /hydra_openapi_parser/hydra_openapi_parser_v2/parsers/ref_parser.py: -------------------------------------------------------------------------------- 1 | from utils import parser_class_mapping 2 | from os.path import isdir 3 | 4 | 5 | class RefParser: 6 | def __init__(self, pointer) -> None: 7 | self.pointer = pointer 8 | 9 | def find_root(self): 10 | path_to_ref = self.pointer.split("/") 11 | if path_to_ref[0] == "#": 12 | return path_to_ref[::1] 13 | elif isdir(path_to_ref[0]): 14 | return "directory" 15 | else: 16 | return "url" 17 | 18 | def parse(self): 19 | root = self.find_root() 20 | hydra_class = {} 21 | hydra_collection = {} 22 | if root == "directory": 23 | pass 24 | elif root == "url": 25 | pass 26 | else: 27 | # components within the same file 28 | from processors.api_class_processor import APIClassProcessor 29 | 30 | component_type = root[2] 31 | if component_type == "schemas": 32 | hydra_entity = root[3] 33 | hydra_class = APIClassProcessor.hydra_classes.get(hydra_entity) 34 | hydra_collection = APIClassProcessor.hydra_collections.get(hydra_entity) 35 | 36 | return [hydra_class, hydra_collection] 37 | -------------------------------------------------------------------------------- /hydra_openapi_parser/hydra_openapi_parser_v2/parsers/resp_parser.py: -------------------------------------------------------------------------------- 1 | from processors.status_processor import StatusProcessor 2 | from parsers.schema_parser import SchemaParser 3 | 4 | 5 | class ResponseParser: 6 | def __init__(self, code, response) -> None: 7 | self.code = code 8 | self.response = response 9 | self.returns = "" 10 | 11 | def parse_code(self): 12 | if self.code.isnumeric(): 13 | return int(self.code) 14 | else: 15 | # handles default response 16 | return 500 17 | 18 | def parse_returns(self): 19 | return self.returns 20 | 21 | def parse(self): 22 | response = {"code": self.parse_code(), "desc": "", "title": ""} 23 | for key, value in self.response.items(): 24 | if key == "description": 25 | response["desc"] = value 26 | if key == "content": 27 | for _, expects in value.items(): 28 | schema_parser = SchemaParser(expects.get("schema")) 29 | hydra_classes, _ = schema_parser.parse() 30 | for title, _ in hydra_classes.items(): 31 | self.returns = title 32 | 33 | status_processor = StatusProcessor(response) 34 | hydra_status = status_processor.generate() 35 | 36 | return hydra_status 37 | -------------------------------------------------------------------------------- /hydra_openapi_parser/hydra_openapi_parser_v2/parsers/schema_parser.py: -------------------------------------------------------------------------------- 1 | from hydra_python_core.doc_writer import HydraClassProp, HydraCollection, HydraClass 2 | from exceptions import HydraCollectionException 3 | from processors.api_info_processor import APIInfoProcessor 4 | from processors.class_processor import ClassProcessor 5 | from processors.collection_processor import CollectionProcessor 6 | from processors.prop_processor import PropertyProcessor 7 | from parsers.ref_parser import RefParser 8 | from utils import type_ref_mapping 9 | from typing import Dict, List, Union, Any 10 | 11 | 12 | class SchemaParser: 13 | def __init__(self, schema_details: Dict[str, Any]) -> None: 14 | self.schema_details = schema_details 15 | self.id = APIInfoProcessor.api_info["id"] 16 | 17 | @staticmethod 18 | def create_props(title, property_details, required=False): 19 | if property_details.get("type") == "array": 20 | raise HydraCollectionException("Property contains a hydra collection.") 21 | prop = type_ref_mapping(property_details["type"]) 22 | property_processor = PropertyProcessor(prop, title, required) 23 | hydra_prop = property_processor.generate() 24 | return hydra_prop 25 | 26 | @staticmethod 27 | def parse_props(schema_definition): 28 | props = {} 29 | if schema_definition.get("required"): 30 | for required_prop in schema_definition.get("required"): 31 | property_details = schema_definition.get("properties")[required_prop] 32 | props[required_prop] = SchemaParser.create_props( 33 | required_prop, property_details, True 34 | ) 35 | if schema_definition.get("properties"): 36 | for prop in schema_definition.get("properties"): 37 | if props.get(prop): 38 | continue 39 | property_details = schema_definition.get("properties")[prop] 40 | props[prop] = SchemaParser.create_props(prop, property_details, True) 41 | return list(props.values()) 42 | 43 | @staticmethod 44 | def get_props(schema_definition): 45 | hydra_class_props = [] 46 | for key, value in schema_definition.items(): 47 | if key in ["allOf", "anyOf", "oneOf"]: 48 | for schema_definition_ in value: 49 | hydra_class_props.extend(SchemaParser.get_props(schema_definition_)) 50 | else: 51 | if schema_definition.get("$ref"): 52 | pass 53 | # ref_parser = RefParser(value) 54 | # hydra_class = ref_parser.parse() 55 | elif schema_definition.get("type") == "array": 56 | raise HydraCollectionException("Schema contains hydra collection") 57 | else: 58 | return SchemaParser.parse_props(schema_definition) 59 | return hydra_class_props 60 | 61 | def parse(self) -> List[List[Union[HydraClass, HydraCollection]]]: 62 | hydra_classes = {} 63 | hydra_collections = {} 64 | for name, definition in self.schema_details.items(): 65 | # schmas containing refs 66 | if name == "$ref": 67 | ref_parser = RefParser(definition) 68 | hydra_class, hydra_collection = ref_parser.parse() 69 | if hydra_class: 70 | hydra_classes[hydra_class.__dict__.get("title")] = hydra_class 71 | if hydra_collection: 72 | hydra_collections[ 73 | hydra_collection.__dict__.get("title") 74 | ] = hydra_collection 75 | else: 76 | # for schemas defined in the paramters/responses 77 | if name in ["type", "properties", "required"]: 78 | definition = self.schema_details 79 | try: 80 | hydra_props = SchemaParser.get_props(definition) 81 | class_processor = ClassProcessor( 82 | title=name, id=self.id, hydra_props=hydra_props 83 | ) 84 | hydra_class = class_processor.generate() 85 | hydra_classes[name] = hydra_class 86 | except HydraCollectionException: 87 | collection_processor = CollectionProcessor(title=name, id=self.id) 88 | hydra_collection = collection_processor.generate() 89 | hydra_collections[name] = hydra_collection 90 | return [hydra_classes, hydra_collections] 91 | -------------------------------------------------------------------------------- /hydra_openapi_parser/hydra_openapi_parser_v2/processors/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTTP-APIs/hydra-openapi-parser/329379434d9bc805883aa5934a85824e5fae1df2/hydra_openapi_parser/hydra_openapi_parser_v2/processors/__init__.py -------------------------------------------------------------------------------- /hydra_openapi_parser/hydra_openapi_parser_v2/processors/api_class_processor.py: -------------------------------------------------------------------------------- 1 | from parsers.ref_parser import RefParser 2 | from processors.api_info_processor import APIInfoProcessor 3 | from processors.class_processor import ClassProcessor 4 | from processors.collection_processor import CollectionProcessor 5 | from parsers.components_parser import ComponentParser 6 | from parsers.path_parser import PathParser 7 | from hydra_python_core.doc_writer import HydraClass, HydraCollection 8 | from typing import Dict, List, Any, Union 9 | 10 | 11 | class APIClassProcessor: 12 | hydra_classes: Dict[str, HydraClass] = {} 13 | hydra_collections: Dict[str, HydraCollection] = {} 14 | 15 | def __init__(self, openapi_doc: Dict[str, Any]) -> None: 16 | self.doc = openapi_doc 17 | self.id = APIInfoProcessor.api_info["id"] 18 | 19 | def generate_from_components(self): 20 | for component, definition in self.doc.get("components").items(): 21 | component_parser = ComponentParser(component, definition) 22 | component_classes, component_collections = component_parser.parse() 23 | return [component_classes, component_collections] 24 | 25 | def generate_from_paths(self): 26 | hydra_title = str 27 | hydra_classes = {} 28 | hydra_collections = {} 29 | 30 | for path_name, path_method in self.doc.get("paths").items(): 31 | hydra_title = path_name.split("/")[1].capitalize() 32 | if path_method.get("$ref"): 33 | ref_parser = RefParser(path_method.get("$ref")) 34 | hydra_class, hydra_collection = ref_parser.parse() 35 | APIClassProcessor.hydra_classes[hydra_title] = hydra_class 36 | APIClassProcessor.hydra_collections[hydra_title] = hydra_collection 37 | else: 38 | path_parser = PathParser(path_name, path_method, self.id) 39 | hydra_class_ops = path_parser.get_class_ops() 40 | hydra_class_props = path_parser.get_class_props() 41 | hydra_collection_ops = path_parser.get_collection_ops() 42 | if not hydra_classes.get(hydra_title): 43 | hydra_classes[hydra_title] = [[], []] 44 | if not hydra_collections.get(hydra_title): 45 | hydra_collections[hydra_title] = {} 46 | hydra_classes[hydra_title][0].extend(hydra_class_ops) 47 | hydra_classes[hydra_title][1].extend(hydra_class_props) 48 | hydra_collections[hydra_title].update(hydra_collection_ops) 49 | 50 | return [hydra_classes, hydra_collections] 51 | 52 | def generate(self) -> List[List[Union[HydraClass, HydraCollection]]]: 53 | component_classes, component_collections = self.generate_from_components() 54 | APIClassProcessor.hydra_classes.update(component_classes) 55 | APIClassProcessor.hydra_collections.update(component_collections) 56 | 57 | path_classes, path_collections = self.generate_from_paths() 58 | for hydra_title, [hydra_ops, hydra_props] in path_classes.items(): 59 | class_processor = ClassProcessor( 60 | title=hydra_title, 61 | id=self.id, 62 | hydra_ops=hydra_ops, 63 | hydra_props=hydra_props, 64 | ) 65 | hydra_class = class_processor.generate() 66 | APIClassProcessor.hydra_classes[hydra_title] = hydra_class 67 | 68 | for hydra_title, hydra_ops in path_collections.items(): 69 | collection_processor = CollectionProcessor( 70 | title=hydra_title, id=self.id, hydra_ops=hydra_ops 71 | ) 72 | hydra_collection = collection_processor.generate() 73 | APIClassProcessor.hydra_collections[hydra_title] = hydra_collection 74 | 75 | hydra_classes_ = list(APIClassProcessor.hydra_classes.values()) 76 | hydra_collections_ = list(APIClassProcessor.hydra_collections.values()) 77 | return [hydra_classes_, hydra_collections_] 78 | -------------------------------------------------------------------------------- /hydra_openapi_parser/hydra_openapi_parser_v2/processors/api_info_processor.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Dict 2 | from parsers.api_info_parser import InfoParser 3 | from hydra_python_core.doc_writer import HydraDoc 4 | 5 | 6 | class APIInfoProcessor: 7 | api_info: Dict[str, str] = {} 8 | 9 | def __init__(self, openapi_doc: Dict[str, Any]) -> None: 10 | self.doc = openapi_doc 11 | 12 | def generate(self) -> HydraDoc: 13 | info_parser = InfoParser(self.doc) 14 | APIInfoProcessor.api_info = info_parser.parse() 15 | 16 | api_info_doc = HydraDoc( 17 | API=APIInfoProcessor.api_info["api"], 18 | title=APIInfoProcessor.api_info["title"], 19 | desc=APIInfoProcessor.api_info["desc"], 20 | entrypoint=APIInfoProcessor.api_info["entrypoint"], 21 | base_url=APIInfoProcessor.api_info["base_url"], 22 | doc_name=APIInfoProcessor.api_info["doc_name"], 23 | ) 24 | return api_info_doc 25 | -------------------------------------------------------------------------------- /hydra_openapi_parser/hydra_openapi_parser_v2/processors/class_processor.py: -------------------------------------------------------------------------------- 1 | from hydra_python_core.doc_writer import ( 2 | HydraClass, 3 | HydraClassOp, 4 | HydraClassProp, 5 | HydraLink, 6 | ) 7 | from typing import List, Union, Optional 8 | 9 | 10 | class ClassProcessor: 11 | def __init__( 12 | self, 13 | title: str, 14 | id: Union[str, HydraLink], 15 | desc: str = "", 16 | hydra_ops: List[HydraClassOp] = [], 17 | hydra_props: List[HydraClassProp] = [], 18 | ) -> None: 19 | self.title = title 20 | self.desc = desc if desc else f"Class for {title}" 21 | self.hydra_ops = hydra_ops 22 | self.hydra_props = self.filter_props(hydra_props) 23 | self.id = id 24 | 25 | @staticmethod 26 | def filter_props(objects): 27 | filtered_objs = {} 28 | for object_ in objects: 29 | title = object_.__dict__.get("title") 30 | if not filtered_objs.get(title): 31 | filtered_objs[title] = object_ 32 | elif object_.__dict__ != filtered_objs.get(title).__dict__: 33 | filtered_objs[title] = object_ 34 | return filtered_objs.values() 35 | 36 | def generate(self) -> HydraClass: 37 | hydra_class = HydraClass( 38 | title=self.title, 39 | desc=self.desc, 40 | _id=self.id, 41 | endpoint=True, 42 | ) 43 | 44 | for hydra_op in self.hydra_ops: 45 | hydra_class.add_supported_op(hydra_op) 46 | 47 | for hydra_prop in self.hydra_props: 48 | hydra_class.add_supported_prop(hydra_prop) 49 | 50 | return hydra_class 51 | -------------------------------------------------------------------------------- /hydra_openapi_parser/hydra_openapi_parser_v2/processors/collection_processor.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Optional, Union 2 | from hydra_python_core.doc_writer import HydraCollection, HydraLink 3 | 4 | 5 | class CollectionProcessor: 6 | def __init__( 7 | self, 8 | title: str, 9 | id: Union[str, HydraLink], 10 | desc: str = "", 11 | hydra_ops: Dict[str, bool] = {}, 12 | manages: Optional[Dict[str, str]] = {}, 13 | ) -> None: 14 | self.title = title + "Collection" 15 | self.desc = desc if desc else f"A collection for {title.lower()}" 16 | self.hydra_ops = hydra_ops 17 | self.manages = ( 18 | manages 19 | if manages 20 | else {"object": f"{id}?resource={title}", "property": "rdfs:type"} 21 | ) 22 | 23 | def generate(self) -> HydraCollection: 24 | hydra_collection = HydraCollection( 25 | collection_name=self.title, 26 | collection_description=self.desc, 27 | manages=self.manages, 28 | get=self.hydra_ops.get("get", False), 29 | put=self.hydra_ops.get("put", False), 30 | post=self.hydra_ops.get("post", False), 31 | delete=self.hydra_ops.get("delete", False), 32 | ) 33 | return hydra_collection 34 | -------------------------------------------------------------------------------- /hydra_openapi_parser/hydra_openapi_parser_v2/processors/op_processor.py: -------------------------------------------------------------------------------- 1 | from hydra_python_core.doc_writer import HydraClassOp, HydraStatus, HydraError 2 | from typing import List, Union 3 | 4 | 5 | class OperationProcessor: 6 | def __init__( 7 | self, 8 | title: str, 9 | method: str, 10 | id: str, 11 | expects: str = None, 12 | returns: str = None, 13 | expects_header: List[str] = [], 14 | returns_header: List[str] = [], 15 | possible_status: List[Union[HydraStatus, HydraError]] = [], 16 | ) -> None: 17 | self.method = method.upper() 18 | self.title = title 19 | self.expects = expects 20 | self.returns = returns 21 | self.expects_header = expects_header 22 | self.returns_header = returns_header 23 | self.possible_status = possible_status 24 | if returns: 25 | self.returns = id.replace(f"?resource={title}", f"?resource={returns}") 26 | if expects: 27 | self.expects = id.replace(f"?resource={title}", f"?resource={expects}") 28 | 29 | def generate(self): 30 | hydra_class_op = HydraClassOp( 31 | title=self.title, 32 | method=self.method, 33 | expects=self.expects, 34 | returns=self.returns, 35 | expects_header=self.expects_header, 36 | returns_header=self.returns_header, 37 | possible_status=self.possible_status, 38 | ) 39 | return hydra_class_op 40 | -------------------------------------------------------------------------------- /hydra_openapi_parser/hydra_openapi_parser_v2/processors/prop_processor.py: -------------------------------------------------------------------------------- 1 | from hydra_python_core.doc_writer import HydraClassProp, HydraLink 2 | from typing import Union 3 | 4 | 5 | class PropertyProcessor: 6 | def __init__(self, prop: Union[str, HydraLink], title: str, required: bool) -> None: 7 | self.prop = prop 8 | self.title = title 9 | self.required = required 10 | 11 | def generate(self): 12 | hydra_prop = HydraClassProp( 13 | prop=self.prop, 14 | title=self.title, 15 | read=False, 16 | write=False, 17 | required=self.required, 18 | ) 19 | 20 | return hydra_prop 21 | -------------------------------------------------------------------------------- /hydra_openapi_parser/hydra_openapi_parser_v2/processors/status_processor.py: -------------------------------------------------------------------------------- 1 | from hydra_python_core.doc_writer import HydraStatus 2 | 3 | 4 | class StatusProcessor: 5 | def __init__(self, response) -> None: 6 | self.response = response 7 | 8 | def generate(self): 9 | hydra_status = HydraStatus( 10 | code=self.response["code"], 11 | title=self.response["title"], 12 | desc=self.response["desc"], 13 | ) 14 | 15 | return hydra_status 16 | -------------------------------------------------------------------------------- /hydra_openapi_parser/hydra_openapi_parser_v2/utils.py: -------------------------------------------------------------------------------- 1 | from hydra_python_core.doc_writer import HydraDoc 2 | import json 3 | from typing import Dict, Any, List 4 | 5 | 6 | def parser_class_mapping(category): 7 | from parsers.param_parser import ParameterParser 8 | from parsers.resp_parser import ResponseParser 9 | from parsers.path_parser import PathParser 10 | from parsers.method_parser import MethodParser 11 | from parsers.schema_parser import SchemaParser 12 | 13 | parser_class_mapping = dict() 14 | parser_class_mapping = { 15 | "path": PathParser, 16 | "response": ResponseParser, 17 | "parameter": ParameterParser, 18 | "method": MethodParser, 19 | "schema": SchemaParser, 20 | } 21 | return parser_class_mapping[category] 22 | 23 | 24 | def component_class_mapping(component): 25 | from parsers.param_parser import ParameterParser 26 | from parsers.schema_parser import SchemaParser 27 | from parsers.resp_parser import ResponseParser 28 | 29 | components = dict() 30 | components = { 31 | "schemas": SchemaParser, 32 | "parameters": ParameterParser, 33 | "responses": ResponseParser, 34 | "securitySchemes": "", 35 | "requestBodies": "", 36 | "headers": "", 37 | "examples": "", 38 | "links": "", 39 | "callbacks": "", 40 | } 41 | return components[component] 42 | 43 | 44 | def type_ref_mapping(type: str) -> str: 45 | """ 46 | Returns semantic ref for OAS data types 47 | :param type: data type 48 | :return: ref 49 | """ 50 | dataType_ref_map = dict() 51 | # todo add support for byte , binary , password ,double data types 52 | dataType_ref_map["integer"] = "https://schema.org/Integer" 53 | dataType_ref_map["string"] = "https://schema.org/Text" 54 | dataType_ref_map["long"] = "http://books.xmlschemata.org/relaxng/ch19-77199.html" 55 | dataType_ref_map["float"] = "https://schema.org/Float" 56 | dataType_ref_map["boolean"] = "https://schema.org/Boolean" 57 | dataType_ref_map["dateTime"] = "https://schema.org/DateTime" 58 | dataType_ref_map["date"] = "https://schema.org/Date" 59 | 60 | return dataType_ref_map[type] 61 | 62 | 63 | def gen_entrypoint(api_doc: HydraDoc) -> HydraDoc: 64 | """ 65 | Generates Entrypoint, Base Collection and Base Resource for the documentation 66 | :param api_doc: contains the Hydra Doc created 67 | """ 68 | api_doc.add_baseCollection() 69 | api_doc.add_baseResource() 70 | api_doc.gen_EntryPoint() 71 | return api_doc 72 | 73 | 74 | def gen_doc_file(hydra_doc: Dict[str, Any]) -> str: 75 | """ 76 | Helper function to dump generated hydradoc > py file. 77 | :param doc: generated hydra doc 78 | :return: hydra doc created 79 | """ 80 | dump = json.dumps(hydra_doc, indent=4, sort_keys=True) 81 | hydra_doc = '''"""\nGenerated API Documentation for Server API using 82 | server_doc_gen.py."""\n\ndoc = {}'''.format( 83 | dump 84 | ) 85 | hydra_doc = "{}\n".format(hydra_doc) 86 | hydra_doc = hydra_doc.replace("true", '"true"') 87 | hydra_doc = hydra_doc.replace("false", '"false"') 88 | hydra_doc = hydra_doc.replace("null", '"null"') 89 | 90 | return hydra_doc 91 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | PyYAML==4.2b1 2 | hydra-python-core -------------------------------------------------------------------------------- /samples/v1/hydra_doc_sample.py: -------------------------------------------------------------------------------- 1 | """ 2 | Generated API Documentation for Server API using 3 | server_doc_gen.py.""" 4 | 5 | doc = { 6 | "@context": { 7 | "ApiDocumentation": "hydra:ApiDocumentation", 8 | "description": "hydra:description", 9 | "domain": { 10 | "@id": "rdfs:domain", 11 | "@type": "@id" 12 | }, 13 | "expects": { 14 | "@id": "hydra:expects", 15 | "@type": "@id" 16 | }, 17 | "hydra": "http://www.w3.org/ns/hydra/core#", 18 | "label": "rdfs:label", 19 | "method": "hydra:method", 20 | "possibleStatus": "hydra:possibleStatus", 21 | "property": { 22 | "@id": "hydra:property", 23 | "@type": "@id" 24 | }, 25 | "range": { 26 | "@id": "rdfs:range", 27 | "@type": "@id" 28 | }, 29 | "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", 30 | "rdfs": "http://www.w3.org/2000/01/rdf-schema#", 31 | "readonly": "hydra:readonly", 32 | "required": "hydra:required", 33 | "returns": { 34 | "@id": "hydra:returns", 35 | "@type": "@id" 36 | }, 37 | "statusCode": "hydra:statusCode", 38 | "statusCodes": "hydra:statusCodes", 39 | "subClassOf": { 40 | "@id": "rdfs:subClassOf", 41 | "@type": "@id" 42 | }, 43 | "supportedClass": "hydra:supportedClass", 44 | "supportedOperation": "hydra:supportedOperation", 45 | "supportedProperty": "hydra:supportedProperty", 46 | "title": "hydra:title", 47 | "vocab": "http://petstore.swagger.io/v2/vocab#", 48 | "writeonly": "hydra:writeonly" 49 | }, 50 | "@id": "http://petstore.swagger.io/v2/vocab", 51 | "@type": "ApiDocumentation", 52 | "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", 53 | "possibleStatus": [], 54 | "supportedClass": [ 55 | { 56 | "@id": "vocab:Pet", 57 | "@type": "hydra:Class", 58 | "description": "Pet", 59 | "supportedOperation": [ 60 | { 61 | "@type": "http://schema.org/UpdateAction", 62 | "expects": "vocab:Pet", 63 | "method": "POST", 64 | "possibleStatus": [ 65 | { 66 | "description": "Invalid input", 67 | "statusCode": 405 68 | } 69 | ], 70 | "returns": "null", 71 | "title": "Add a new pet to the store" 72 | }, 73 | { 74 | "@type": "http://schema.org/AddAction", 75 | "expects": "vocab:Pet", 76 | "method": "PUT", 77 | "possibleStatus": [ 78 | { 79 | "description": "Invalid ID supplied", 80 | "statusCode": 400 81 | } 82 | ], 83 | "returns": "null", 84 | "title": "Update an existing pet" 85 | }, 86 | { 87 | "@type": "http://schema.org/FindAction", 88 | "expects": "https://schema.org/Text", 89 | "method": "GET", 90 | "possibleStatus": [ 91 | { 92 | "description": "successful operation", 93 | "statusCode": 200 94 | } 95 | ], 96 | "returns": "vocab:Pet", 97 | "title": "get all pets" 98 | } 99 | ], 100 | "supportedProperty": [ 101 | { 102 | "@type": "SupportedProperty", 103 | "property": "", 104 | "readonly": "true", 105 | "required": "false", 106 | "title": "id", 107 | "writeonly": "true" 108 | }, 109 | { 110 | "@type": "SupportedProperty", 111 | "property": "", 112 | "readonly": "true", 113 | "required": "false", 114 | "title": "category", 115 | "writeonly": "true" 116 | }, 117 | { 118 | "@type": "SupportedProperty", 119 | "property": "", 120 | "readonly": "true", 121 | "required": "true", 122 | "title": "name", 123 | "writeonly": "true" 124 | }, 125 | { 126 | "@type": "SupportedProperty", 127 | "property": "", 128 | "readonly": "true", 129 | "required": "true", 130 | "title": "photoUrls", 131 | "writeonly": "true" 132 | }, 133 | { 134 | "@type": "SupportedProperty", 135 | "property": "", 136 | "readonly": "true", 137 | "required": "false", 138 | "title": "tags", 139 | "writeonly": "true" 140 | }, 141 | { 142 | "@type": "SupportedProperty", 143 | "property": "", 144 | "readonly": "true", 145 | "required": "false", 146 | "title": "status", 147 | "writeonly": "true" 148 | } 149 | ], 150 | "title": "Pet" 151 | }, 152 | { 153 | "@id": "vocab:ApiResponse", 154 | "@type": "hydra:Class", 155 | "description": "ApiResponse", 156 | "supportedOperation": [ 157 | { 158 | "@type": "http://schema.org/UpdateAction", 159 | "expects": "https://schema.org/Text", 160 | "method": "POST", 161 | "possibleStatus": [ 162 | { 163 | "description": "successful operation", 164 | "statusCode": 200 165 | } 166 | ], 167 | "returns": "vocab:ApiResponse", 168 | "title": "uploads an image" 169 | } 170 | ], 171 | "supportedProperty": [ 172 | { 173 | "@type": "SupportedProperty", 174 | "property": "", 175 | "readonly": "true", 176 | "required": "false", 177 | "title": "code", 178 | "writeonly": "true" 179 | }, 180 | { 181 | "@type": "SupportedProperty", 182 | "property": "", 183 | "readonly": "true", 184 | "required": "false", 185 | "title": "type", 186 | "writeonly": "true" 187 | }, 188 | { 189 | "@type": "SupportedProperty", 190 | "property": "", 191 | "readonly": "true", 192 | "required": "false", 193 | "title": "message", 194 | "writeonly": "true" 195 | } 196 | ], 197 | "title": "ApiResponse" 198 | }, 199 | { 200 | "@id": "vocab:User", 201 | "@type": "hydra:Class", 202 | "description": "User", 203 | "supportedOperation": [ 204 | { 205 | "@type": "http://schema.org/UpdateAction", 206 | "expects": "vocab:User", 207 | "method": "POST", 208 | "possibleStatus": [ 209 | { 210 | "description": "Successful Operation", 211 | "statusCode": 200 212 | } 213 | ], 214 | "returns": "null", 215 | "title": "Create user" 216 | }, 217 | { 218 | "@type": "http://schema.org/FindAction", 219 | "expects": "https://schema.org/Text", 220 | "method": "GET", 221 | "possibleStatus": [ 222 | { 223 | "description": "successful operation", 224 | "statusCode": 200 225 | }, 226 | { 227 | "description": "Invalid username supplied", 228 | "statusCode": 400 229 | }, 230 | { 231 | "description": "User not found", 232 | "statusCode": 404 233 | } 234 | ], 235 | "returns": "vocab:User", 236 | "title": "Get user by user name" 237 | }, 238 | { 239 | "@type": "http://schema.org/AddAction", 240 | "expects": "vocab:User", 241 | "method": "PUT", 242 | "possibleStatus": [ 243 | { 244 | "description": "Invalid user supplied", 245 | "statusCode": 400 246 | } 247 | ], 248 | "returns": "null", 249 | "title": "Updated user" 250 | } 251 | ], 252 | "supportedProperty": [ 253 | { 254 | "@type": "SupportedProperty", 255 | "property": "", 256 | "readonly": "true", 257 | "required": "false", 258 | "title": "id", 259 | "writeonly": "true" 260 | }, 261 | { 262 | "@type": "SupportedProperty", 263 | "property": "", 264 | "readonly": "true", 265 | "required": "false", 266 | "title": "username", 267 | "writeonly": "true" 268 | }, 269 | { 270 | "@type": "SupportedProperty", 271 | "property": "", 272 | "readonly": "true", 273 | "required": "false", 274 | "title": "firstName", 275 | "writeonly": "true" 276 | }, 277 | { 278 | "@type": "SupportedProperty", 279 | "property": "", 280 | "readonly": "true", 281 | "required": "false", 282 | "title": "lastName", 283 | "writeonly": "true" 284 | }, 285 | { 286 | "@type": "SupportedProperty", 287 | "property": "", 288 | "readonly": "true", 289 | "required": "false", 290 | "title": "email", 291 | "writeonly": "true" 292 | }, 293 | { 294 | "@type": "SupportedProperty", 295 | "property": "", 296 | "readonly": "true", 297 | "required": "false", 298 | "title": "password", 299 | "writeonly": "true" 300 | }, 301 | { 302 | "@type": "SupportedProperty", 303 | "property": "", 304 | "readonly": "true", 305 | "required": "false", 306 | "title": "phone", 307 | "writeonly": "true" 308 | }, 309 | { 310 | "@type": "SupportedProperty", 311 | "property": "", 312 | "readonly": "true", 313 | "required": "false", 314 | "title": "userStatus", 315 | "writeonly": "true" 316 | } 317 | ], 318 | "title": "User" 319 | }, 320 | { 321 | "@id": "vocab:Order", 322 | "@type": "hydra:Class", 323 | "description": "this is def", 324 | "supportedOperation": [ 325 | { 326 | "@type": "http://schema.org/UpdateAction", 327 | "expects": "vocab:Order", 328 | "method": "POST", 329 | "possibleStatus": [ 330 | { 331 | "description": "successful operation", 332 | "statusCode": 200 333 | }, 334 | { 335 | "description": "Invalid Order", 336 | "statusCode": 400 337 | } 338 | ], 339 | "returns": "vocab:Order", 340 | "title": "Place an order for a pet" 341 | }, 342 | { 343 | "@type": "http://schema.org/FindAction", 344 | "expects": "https://schema.org/Integer", 345 | "method": "GET", 346 | "possibleStatus": [ 347 | { 348 | "description": "successful operation", 349 | "statusCode": 200 350 | }, 351 | { 352 | "description": "Invalid ID supplied", 353 | "statusCode": 400 354 | }, 355 | { 356 | "description": "Order not found", 357 | "statusCode": 404 358 | } 359 | ], 360 | "returns": "vocab:Order", 361 | "title": "Find purchase order by ID" 362 | } 363 | ], 364 | "supportedProperty": [ 365 | { 366 | "@type": "SupportedProperty", 367 | "property": "", 368 | "readonly": "true", 369 | "required": "false", 370 | "title": "id", 371 | "writeonly": "true" 372 | }, 373 | { 374 | "@type": "SupportedProperty", 375 | "property": "", 376 | "readonly": "true", 377 | "required": "false", 378 | "title": "petId", 379 | "writeonly": "true" 380 | }, 381 | { 382 | "@type": "SupportedProperty", 383 | "property": "", 384 | "readonly": "true", 385 | "required": "false", 386 | "title": "quantity", 387 | "writeonly": "true" 388 | }, 389 | { 390 | "@type": "SupportedProperty", 391 | "property": "", 392 | "readonly": "true", 393 | "required": "false", 394 | "title": "shipDate", 395 | "writeonly": "true" 396 | }, 397 | { 398 | "@type": "SupportedProperty", 399 | "property": "", 400 | "readonly": "true", 401 | "required": "false", 402 | "title": "status", 403 | "writeonly": "true" 404 | }, 405 | { 406 | "@type": "SupportedProperty", 407 | "property": "", 408 | "readonly": "true", 409 | "required": "false", 410 | "title": "complete", 411 | "writeonly": "true" 412 | } 413 | ], 414 | "title": "Order" 415 | }, 416 | { 417 | "@id": "http://www.w3.org/ns/hydra/core#Collection", 418 | "@type": "hydra:Class", 419 | "description": "null", 420 | "supportedOperation": [], 421 | "supportedProperty": [ 422 | { 423 | "@type": "SupportedProperty", 424 | "property": "http://www.w3.org/ns/hydra/core#member", 425 | "readonly": "false", 426 | "required": "null", 427 | "title": "members", 428 | "writeonly": "false" 429 | } 430 | ], 431 | "title": "Collection" 432 | }, 433 | { 434 | "@id": "http://www.w3.org/ns/hydra/core#Resource", 435 | "@type": "hydra:Class", 436 | "description": "null", 437 | "supportedOperation": [], 438 | "supportedProperty": [], 439 | "title": "Resource" 440 | }, 441 | { 442 | "@id": "vocab:PetCollection", 443 | "@type": "hydra:Class", 444 | "description": "A collection of pet", 445 | "subClassOf": "http://www.w3.org/ns/hydra/core#Collection", 446 | "supportedOperation": [ 447 | { 448 | "@id": "_:pet_collection_retrieve", 449 | "@type": "http://schema.org/FindAction", 450 | "description": "Retrieves all Pet entities", 451 | "expects": "null", 452 | "method": "GET", 453 | "returns": "vocab:PetCollection", 454 | "statusCodes": [] 455 | }, 456 | { 457 | "@id": "_:pet_create", 458 | "@type": "http://schema.org/AddAction", 459 | "description": "Create new Pet entitity", 460 | "expects": "vocab:Pet", 461 | "method": "PUT", 462 | "returns": "vocab:Pet", 463 | "statusCodes": [ 464 | { 465 | "description": "If the Pet entity was createdsuccessfully.", 466 | "statusCode": 201 467 | } 468 | ] 469 | } 470 | ], 471 | "supportedProperty": [ 472 | { 473 | "@type": "SupportedProperty", 474 | "description": "The pet", 475 | "property": "http://www.w3.org/ns/hydra/core#member", 476 | "readonly": "false", 477 | "required": "false", 478 | "title": "members", 479 | "writeonly": "false" 480 | } 481 | ], 482 | "title": "PetCollection" 483 | }, 484 | { 485 | "@id": "vocab:UserCollection", 486 | "@type": "hydra:Class", 487 | "description": "A collection of user", 488 | "subClassOf": "http://www.w3.org/ns/hydra/core#Collection", 489 | "supportedOperation": [ 490 | { 491 | "@id": "_:user_collection_retrieve", 492 | "@type": "http://schema.org/FindAction", 493 | "description": "Retrieves all User entities", 494 | "expects": "null", 495 | "method": "GET", 496 | "returns": "vocab:UserCollection", 497 | "statusCodes": [] 498 | }, 499 | { 500 | "@id": "_:user_create", 501 | "@type": "http://schema.org/AddAction", 502 | "description": "Create new User entitity", 503 | "expects": "vocab:User", 504 | "method": "PUT", 505 | "returns": "vocab:User", 506 | "statusCodes": [ 507 | { 508 | "description": "If the User entity was createdsuccessfully.", 509 | "statusCode": 201 510 | } 511 | ] 512 | } 513 | ], 514 | "supportedProperty": [ 515 | { 516 | "@type": "SupportedProperty", 517 | "description": "The user", 518 | "property": "http://www.w3.org/ns/hydra/core#member", 519 | "readonly": "false", 520 | "required": "false", 521 | "title": "members", 522 | "writeonly": "false" 523 | } 524 | ], 525 | "title": "UserCollection" 526 | }, 527 | { 528 | "@id": "vocab:EntryPoint", 529 | "@type": "hydra:Class", 530 | "description": "The main entry point or homepage of the API.", 531 | "supportedOperation": [ 532 | { 533 | "@id": "_:entry_point", 534 | "@type": "http://schema.org/FindAction", 535 | "description": "The APIs main entry point.", 536 | "expects": "null", 537 | "method": "GET", 538 | "returns": "null", 539 | "statusCodes": "vocab:EntryPoint" 540 | } 541 | ], 542 | "supportedProperty": [ 543 | { 544 | "hydra:description": "The ApiResponse Class", 545 | "hydra:title": "apiresponse", 546 | "property": { 547 | "@id": "vocab:EntryPoint/pet/uploadImage", 548 | "@type": "hydra:Link", 549 | "description": "ApiResponse", 550 | "domain": "vocab:EntryPoint", 551 | "label": "ApiResponse", 552 | "range": "vocab:ApiResponse", 553 | "supportedOperation": [ 554 | { 555 | "@id": "uploads an image", 556 | "@type": "http://schema.org/UpdateAction", 557 | "description": "null", 558 | "expects": "https://schema.org/Text", 559 | "label": "uploads an image", 560 | "method": "POST", 561 | "returns": "vocab:ApiResponse", 562 | "statusCodes": [ 563 | { 564 | "description": "successful operation", 565 | "statusCode": 200 566 | } 567 | ] 568 | } 569 | ] 570 | }, 571 | "readonly": "true", 572 | "required": "null", 573 | "writeonly": "false" 574 | }, 575 | { 576 | "hydra:description": "The Order Class", 577 | "hydra:title": "order", 578 | "property": { 579 | "@id": "vocab:EntryPoint/store/order", 580 | "@type": "hydra:Link", 581 | "description": "this is def", 582 | "domain": "vocab:EntryPoint", 583 | "label": "Order", 584 | "range": "vocab:Order", 585 | "supportedOperation": [ 586 | { 587 | "@id": "place an order for a pet", 588 | "@type": "http://schema.org/UpdateAction", 589 | "description": "null", 590 | "expects": "vocab:Order", 591 | "label": "Place an order for a pet", 592 | "method": "POST", 593 | "returns": "vocab:Order", 594 | "statusCodes": [ 595 | { 596 | "description": "successful operation", 597 | "statusCode": 200 598 | }, 599 | { 600 | "description": "Invalid Order", 601 | "statusCode": 400 602 | } 603 | ] 604 | }, 605 | { 606 | "@id": "find purchase order by id", 607 | "@type": "http://schema.org/FindAction", 608 | "description": "null", 609 | "expects": "https://schema.org/Integer", 610 | "label": "Find purchase order by ID", 611 | "method": "GET", 612 | "returns": "vocab:Order", 613 | "statusCodes": [ 614 | { 615 | "description": "successful operation", 616 | "statusCode": 200 617 | }, 618 | { 619 | "description": "Invalid ID supplied", 620 | "statusCode": 400 621 | }, 622 | { 623 | "description": "Order not found", 624 | "statusCode": 404 625 | } 626 | ] 627 | } 628 | ] 629 | }, 630 | "readonly": "true", 631 | "required": "null", 632 | "writeonly": "false" 633 | }, 634 | { 635 | "hydra:description": "The PetCollection collection", 636 | "hydra:title": "petcollection", 637 | "property": { 638 | "@id": "vocab:EntryPoint/pet", 639 | "@type": "hydra:Link", 640 | "description": "The PetCollection collection", 641 | "domain": "vocab:EntryPoint", 642 | "label": "PetCollection", 643 | "range": "vocab:PetCollection", 644 | "supportedOperation": [ 645 | { 646 | "@id": "_:pet_collection_retrieve", 647 | "@type": "http://schema.org/FindAction", 648 | "description": "Retrieves all Pet entities", 649 | "expects": "null", 650 | "method": "GET", 651 | "returns": "vocab:PetCollection", 652 | "statusCodes": [] 653 | }, 654 | { 655 | "@id": "_:pet_create", 656 | "@type": "http://schema.org/AddAction", 657 | "description": "Create new Pet entitity", 658 | "expects": "vocab:Pet", 659 | "method": "PUT", 660 | "returns": "vocab:Pet", 661 | "statusCodes": [ 662 | { 663 | "description": "If the Pet entity was createdsuccessfully.", 664 | "statusCode": 201 665 | } 666 | ] 667 | } 668 | ] 669 | }, 670 | "readonly": "true", 671 | "required": "null", 672 | "writeonly": "false" 673 | }, 674 | { 675 | "hydra:description": "The UserCollection collection", 676 | "hydra:title": "usercollection", 677 | "property": { 678 | "@id": "vocab:EntryPoint/user", 679 | "@type": "hydra:Link", 680 | "description": "The UserCollection collection", 681 | "domain": "vocab:EntryPoint", 682 | "label": "UserCollection", 683 | "range": "vocab:UserCollection", 684 | "supportedOperation": [ 685 | { 686 | "@id": "_:user_collection_retrieve", 687 | "@type": "http://schema.org/FindAction", 688 | "description": "Retrieves all User entities", 689 | "expects": "null", 690 | "method": "GET", 691 | "returns": "vocab:UserCollection", 692 | "statusCodes": [] 693 | }, 694 | { 695 | "@id": "_:user_create", 696 | "@type": "http://schema.org/AddAction", 697 | "description": "Create new User entitity", 698 | "expects": "vocab:User", 699 | "method": "PUT", 700 | "returns": "vocab:User", 701 | "statusCodes": [ 702 | { 703 | "description": "If the User entity was createdsuccessfully.", 704 | "statusCode": 201 705 | } 706 | ] 707 | } 708 | ] 709 | }, 710 | "readonly": "true", 711 | "required": "null", 712 | "writeonly": "false" 713 | } 714 | ], 715 | "title": "EntryPoint" 716 | } 717 | ], 718 | "title": "Swagger Petstore" 719 | } 720 | -------------------------------------------------------------------------------- /samples/v1/petstore_openapi.yaml: -------------------------------------------------------------------------------- 1 | swagger: '2.0' 2 | info: 3 | description: 'This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.' 4 | version: 1.0.0 5 | title: Swagger Petstore 6 | termsOfService: 'http://swagger.io/terms/' 7 | contact: 8 | email: apiteam@swagger.io 9 | license: 10 | name: Apache 2.0 11 | url: 'http://www.apache.org/licenses/LICENSE-2.0.html' 12 | host: petstore.swagger.io 13 | basePath: /v2 14 | tags: 15 | - name: pet 16 | description: Everything about your Pets 17 | externalDocs: 18 | description: Find out more 19 | url: 'http://swagger.io' 20 | - name: store 21 | description: Access to Petstore orders 22 | - name: user 23 | description: Operations about user 24 | externalDocs: 25 | description: Find out more about our store 26 | url: 'http://swagger.io' 27 | schemes: 28 | - http 29 | paths: 30 | /pet: 31 | post: 32 | tags: 33 | - pet 34 | summary: Add a new pet to the store 35 | description: '' 36 | operationId: addPet 37 | consumes: 38 | - application/json 39 | - application/xml 40 | produces: 41 | - application/xml 42 | - application/json 43 | parameters: 44 | - in: body 45 | name: body 46 | description: Pet object that needs to be added to the store 47 | required: true 48 | schema: 49 | $ref: '#/definitions/Pet' 50 | responses: 51 | '405': 52 | description: Invalid input 53 | security: 54 | - petstore_auth: 55 | - 'write:pets' 56 | - 'read:pets' 57 | put: 58 | tags: 59 | - pet 60 | summary: Update an existing pet 61 | description: '' 62 | operationId: updatePet 63 | consumes: 64 | - application/json 65 | - application/xml 66 | produces: 67 | - application/xml 68 | - application/json 69 | parameters: 70 | - in: body 71 | name: body 72 | description: Pet object that needs to be added to the store 73 | required: true 74 | schema: 75 | $ref: '#/definitions/Pet' 76 | responses: 77 | '400': 78 | description: Invalid ID supplied 79 | '404': 80 | description: Pet not found 81 | '405': 82 | description: Validation exception 83 | security: 84 | - petstore_auth: 85 | - 'write:pets' 86 | - 'read:pets' 87 | get: 88 | summary: get all pets 89 | description: get all pets 90 | parameters: 91 | - name: status 92 | in: query 93 | description: Status values that need to be considered for filter 94 | required: true 95 | type: array 96 | items: 97 | type: string 98 | enum: 99 | - available 100 | - pending 101 | - sold 102 | default: available 103 | collectionFormat: multi 104 | responses: 105 | '200': 106 | description: successful operation 107 | schema: 108 | type: array 109 | items: 110 | $ref: '#/definitions/Pet' 111 | /pet/findByStatus: 112 | get: 113 | tags: 114 | - pet 115 | summary: Finds Pets by status 116 | description: Multiple status values can be provided with comma separated strings 117 | operationId: findPetsByStatus 118 | produces: 119 | - application/xml 120 | - application/json 121 | parameters: 122 | - name: status 123 | in: query 124 | description: Status values that need to be considered for filter 125 | required: true 126 | type: array 127 | items: 128 | type: string 129 | enum: 130 | - available 131 | - pending 132 | - sold 133 | default: available 134 | collectionFormat: multi 135 | responses: 136 | '200': 137 | description: successful operation 138 | schema: 139 | type: array 140 | items: 141 | $ref: '#/definitions/Pet' 142 | '400': 143 | description: Invalid status value 144 | security: 145 | - petstore_auth: 146 | - 'write:pets' 147 | - 'read:pets' 148 | /pet/findByTags: 149 | get: 150 | tags: 151 | - pet 152 | summary: Finds Pets by tags 153 | description: 'Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.' 154 | operationId: findPetsByTags 155 | produces: 156 | - application/xml 157 | - application/json 158 | parameters: 159 | - name: tags 160 | in: query 161 | description: Tags to filter by 162 | required: true 163 | type: array 164 | items: 165 | type: string 166 | collectionFormat: multi 167 | responses: 168 | '200': 169 | description: successful operation 170 | schema: 171 | type: array 172 | items: 173 | $ref: '#/definitions/Pet' 174 | '400': 175 | description: Invalid tag value 176 | security: 177 | - petstore_auth: 178 | - 'write:pets' 179 | - 'read:pets' 180 | deprecated: true 181 | '/pet/{petId}': 182 | get: 183 | tags: 184 | - pet 185 | summary: Find pet by ID 186 | description: Returns a single pet 187 | operationId: getPetById 188 | produces: 189 | - application/xml 190 | - application/json 191 | parameters: 192 | - name: petId 193 | in: path 194 | description: ID of pet to return 195 | required: true 196 | type: integer 197 | format: int64 198 | responses: 199 | '200': 200 | description: successful operation 201 | schema: 202 | $ref: '#/definitions/Pet' 203 | '400': 204 | description: Invalid ID supplied 205 | '404': 206 | description: Pet not found 207 | security: 208 | - api_key: [] 209 | post: 210 | tags: 211 | - pet 212 | summary: Updates a pet in the store with form data 213 | description: '' 214 | operationId: updatePetWithForm 215 | consumes: 216 | - application/x-www-form-urlencoded 217 | produces: 218 | - application/xml 219 | - application/json 220 | parameters: 221 | - name: petId 222 | in: path 223 | description: ID of pet that needs to be updated 224 | required: true 225 | type: integer 226 | format: int64 227 | - name: name 228 | in: formData 229 | description: Updated name of the pet 230 | required: false 231 | type: string 232 | - name: status 233 | in: formData 234 | description: Updated status of the pet 235 | required: false 236 | type: string 237 | responses: 238 | '405': 239 | description: Invalid input 240 | security: 241 | - petstore_auth: 242 | - 'write:pets' 243 | - 'read:pets' 244 | delete: 245 | tags: 246 | - pet 247 | summary: Deletes a pet 248 | description: '' 249 | operationId: deletePet 250 | produces: 251 | - application/xml 252 | - application/json 253 | parameters: 254 | - name: api_key 255 | in: header 256 | required: false 257 | type: string 258 | - name: petId 259 | in: path 260 | description: Pet id to delete 261 | required: true 262 | type: integer 263 | format: int64 264 | responses: 265 | '400': 266 | description: Invalid ID supplied 267 | '404': 268 | description: Pet not found 269 | security: 270 | - petstore_auth: 271 | - 'write:pets' 272 | - 'read:pets' 273 | '/pet/{petId}/uploadImage': 274 | post: 275 | tags: 276 | - pet 277 | summary: uploads an image 278 | description: '' 279 | operationId: uploadFile 280 | consumes: 281 | - multipart/form-data 282 | produces: 283 | - application/json 284 | parameters: 285 | - name: petId 286 | in: path 287 | description: ID of pet to update 288 | required: true 289 | type: integer 290 | format: int64 291 | - name: additionalMetadata 292 | in: formData 293 | description: Additional data to pass to server 294 | required: false 295 | type: string 296 | - name: file 297 | in: formData 298 | description: file to upload 299 | required: false 300 | type: file 301 | responses: 302 | '200': 303 | description: successful operation 304 | schema: 305 | $ref: '#/definitions/ApiResponse' 306 | security: 307 | - petstore_auth: 308 | - 'write:pets' 309 | - 'read:pets' 310 | /store/inventory: 311 | get: 312 | tags: 313 | - store 314 | summary: Returns pet inventories by status 315 | description: Returns a map of status codes to quantities 316 | operationId: getInventory 317 | produces: 318 | - application/json 319 | parameters: [] 320 | responses: 321 | '200': 322 | description: successful operation 323 | schema: 324 | type: object 325 | additionalProperties: 326 | type: integer 327 | format: int32 328 | security: 329 | - api_key: [] 330 | /store/order: 331 | post: 332 | tags: 333 | - store 334 | summary: Place an order for a pet 335 | description: '' 336 | operationId: placeOrder 337 | produces: 338 | - application/xml 339 | - application/json 340 | parameters: 341 | - in: body 342 | name: body 343 | description: order placed for purchasing the pet 344 | required: true 345 | schema: 346 | $ref: '#/definitions/Order' 347 | responses: 348 | '200': 349 | description: successful operation 350 | schema: 351 | $ref: '#/definitions/Order' 352 | '400': 353 | description: Invalid Order 354 | '/store/order/{orderId}': 355 | get: 356 | tags: 357 | - store 358 | summary: Find purchase order by ID 359 | description: For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions 360 | operationId: getOrderById 361 | produces: 362 | - application/xml 363 | - application/json 364 | parameters: 365 | - name: orderId 366 | in: path 367 | description: ID of pet that needs to be fetched 368 | required: true 369 | type: integer 370 | maximum: 10 371 | minimum: 1 372 | format: int64 373 | responses: 374 | '200': 375 | description: successful operation 376 | schema: 377 | $ref: '#/definitions/Order' 378 | '400': 379 | description: Invalid ID supplied 380 | '404': 381 | description: Order not found 382 | delete: 383 | tags: 384 | - store 385 | summary: Delete purchase order by ID 386 | description: For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors 387 | operationId: deleteOrder 388 | produces: 389 | - application/xml 390 | - application/json 391 | parameters: 392 | - name: orderId 393 | in: path 394 | description: ID of the order that needs to be deleted 395 | required: true 396 | type: integer 397 | minimum: 1 398 | format: int64 399 | responses: 400 | '400': 401 | description: Invalid ID supplied 402 | '404': 403 | description: Order not found 404 | /user: 405 | post: 406 | tags: 407 | - user 408 | summary: Create user 409 | description: This can only be done by the logged in user. 410 | operationId: createUser 411 | produces: 412 | - application/xml 413 | - application/json 414 | parameters: 415 | - in: body 416 | name: body 417 | description: Created user object 418 | required: true 419 | schema: 420 | $ref: '#/definitions/User' 421 | responses: 422 | default: 423 | description: successful operation 424 | /user/createWithArray: 425 | post: 426 | tags: 427 | - user 428 | summary: Creates list of users with given input array 429 | description: '' 430 | operationId: createUsersWithArrayInput 431 | produces: 432 | - application/xml 433 | - application/json 434 | parameters: 435 | - in: body 436 | name: body 437 | description: List of user object 438 | required: true 439 | schema: 440 | type: array 441 | items: 442 | $ref: '#/definitions/User' 443 | responses: 444 | default: 445 | description: successful operation 446 | /user/createWithList: 447 | post: 448 | tags: 449 | - user 450 | summary: Creates list of users with given input array 451 | description: '' 452 | operationId: createUsersWithListInput 453 | produces: 454 | - application/xml 455 | - application/json 456 | parameters: 457 | - in: body 458 | name: body 459 | description: List of user object 460 | required: true 461 | schema: 462 | type: array 463 | items: 464 | $ref: '#/definitions/User' 465 | responses: 466 | default: 467 | description: successful operation 468 | /user/login: 469 | get: 470 | tags: 471 | - user 472 | summary: Logs user into the system 473 | description: '' 474 | operationId: loginUser 475 | produces: 476 | - application/xml 477 | - application/json 478 | parameters: 479 | - name: username 480 | in: query 481 | description: The user name for login 482 | required: true 483 | type: string 484 | - name: password 485 | in: query 486 | description: The password for login in clear text 487 | required: true 488 | type: string 489 | responses: 490 | '200': 491 | description: successful operation 492 | schema: 493 | type: string 494 | headers: 495 | X-Rate-Limit: 496 | type: integer 497 | format: int32 498 | description: calls per hour allowed by the user 499 | X-Expires-After: 500 | type: string 501 | format: date-time 502 | description: date in UTC when token expires 503 | '400': 504 | description: Invalid username/password supplied 505 | /user/logout: 506 | get: 507 | tags: 508 | - user 509 | summary: Logs out current logged in user session 510 | description: '' 511 | operationId: logoutUser 512 | produces: 513 | - application/xml 514 | - application/json 515 | parameters: [] 516 | responses: 517 | default: 518 | description: successful operation 519 | '/user/{username}': 520 | get: 521 | tags: 522 | - user 523 | summary: Get user by user name 524 | description: '' 525 | operationId: getUserByName 526 | produces: 527 | - application/xml 528 | - application/json 529 | parameters: 530 | - name: username 531 | in: path 532 | description: 'The name that needs to be fetched. Use user1 for testing. ' 533 | required: true 534 | type: string 535 | responses: 536 | '200': 537 | description: successful operation 538 | schema: 539 | $ref: '#/definitions/User' 540 | '400': 541 | description: Invalid username supplied 542 | '404': 543 | description: User not found 544 | put: 545 | tags: 546 | - user 547 | summary: Updated user 548 | description: This can only be done by the logged in user. 549 | operationId: updateUser 550 | produces: 551 | - application/xml 552 | - application/json 553 | parameters: 554 | - name: username 555 | in: path 556 | description: name that need to be updated 557 | required: true 558 | type: string 559 | - in: body 560 | name: body 561 | description: Updated user object 562 | required: true 563 | schema: 564 | $ref: '#/definitions/User' 565 | responses: 566 | '400': 567 | description: Invalid user supplied 568 | '404': 569 | description: User not found 570 | delete: 571 | tags: 572 | - user 573 | summary: Delete user 574 | description: This can only be done by the logged in user. 575 | operationId: deleteUser 576 | produces: 577 | - application/xml 578 | - application/json 579 | parameters: 580 | - name: username 581 | in: path 582 | description: The name that needs to be deleted 583 | required: true 584 | type: string 585 | responses: 586 | '400': 587 | description: Invalid username supplied 588 | '404': 589 | description: User not found 590 | securityDefinitions: 591 | petstore_auth: 592 | type: oauth2 593 | authorizationUrl: 'http://petstore.swagger.io/oauth/dialog' 594 | flow: implicit 595 | scopes: 596 | 'write:pets': modify pets in your account 597 | 'read:pets': read your pets 598 | api_key: 599 | type: apiKey 600 | name: api_key 601 | in: header 602 | definitions: 603 | Order: 604 | type: object 605 | description: "this is def" 606 | properties: 607 | id: 608 | type: integer 609 | format: int64 610 | petId: 611 | type: integer 612 | format: int64 613 | quantity: 614 | type: integer 615 | format: int32 616 | shipDate: 617 | type: string 618 | format: date-time 619 | status: 620 | type: string 621 | description: Order Status 622 | enum: 623 | - placed 624 | - approved 625 | - delivered 626 | complete: 627 | type: boolean 628 | default: false 629 | xml: 630 | name: Order 631 | User: 632 | type: object 633 | properties: 634 | id: 635 | type: integer 636 | format: int64 637 | username: 638 | type: string 639 | firstName: 640 | type: string 641 | lastName: 642 | type: string 643 | email: 644 | type: string 645 | password: 646 | type: string 647 | phone: 648 | type: string 649 | userStatus: 650 | type: integer 651 | format: int32 652 | description: User Status 653 | xml: 654 | name: User 655 | Category: 656 | type: object 657 | properties: 658 | id: 659 | type: integer 660 | format: int64 661 | name: 662 | type: string 663 | xml: 664 | name: Category 665 | Tag: 666 | type: object 667 | properties: 668 | id: 669 | type: integer 670 | format: int64 671 | name: 672 | type: string 673 | xml: 674 | name: Tag 675 | Pet: 676 | type: object 677 | required: 678 | - name 679 | - photoUrls 680 | properties: 681 | id: 682 | type: integer 683 | format: int64 684 | category: 685 | $ref: '#/definitions/Category' 686 | name: 687 | type: string 688 | example: doggie 689 | photoUrls: 690 | type: array 691 | xml: 692 | name: photoUrl 693 | wrapped: true 694 | items: 695 | type: string 696 | tags: 697 | type: array 698 | xml: 699 | name: tag 700 | wrapped: true 701 | items: 702 | $ref: '#/definitions/Tag' 703 | status: 704 | type: string 705 | description: pet status in the store 706 | enum: 707 | - available 708 | - pending 709 | - sold 710 | xml: 711 | name: Pet 712 | ApiResponse: 713 | type: object 714 | properties: 715 | code: 716 | type: integer 717 | format: int32 718 | type: 719 | type: string 720 | message: 721 | type: string 722 | externalDocs: 723 | description: Find out more about Swagger 724 | url: 'http://swagger.io' 725 | -------------------------------------------------------------------------------- /samples/v2/hydra_doc_sample.py: -------------------------------------------------------------------------------- 1 | """ 2 | Generated API Documentation for Server API using 3 | server_doc_gen.py.""" 4 | 5 | doc = { 6 | "@context": { 7 | "ApiDocumentation": "hydra:ApiDocumentation", 8 | "description": "hydra:description", 9 | "domain": { 10 | "@id": "rdfs:domain", 11 | "@type": "@id" 12 | }, 13 | "entrypoint": { 14 | "@id": "hydra:entrypoint", 15 | "@type": "@id" 16 | }, 17 | "expects": { 18 | "@id": "hydra:expects", 19 | "@type": "@id" 20 | }, 21 | "expectsHeader": "hydra:expectsHeader", 22 | "hydra": "http://www.w3.org/ns/hydra/core#", 23 | "label": "rdfs:label", 24 | "manages": "hydra:manages", 25 | "method": "hydra:method", 26 | "object": { 27 | "@id": "hydra:object", 28 | "@type": "@id" 29 | }, 30 | "possibleStatus": "hydra:possibleStatus", 31 | "property": { 32 | "@id": "hydra:property", 33 | "@type": "@id" 34 | }, 35 | "range": { 36 | "@id": "rdfs:range", 37 | "@type": "@id" 38 | }, 39 | "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", 40 | "rdfs": "http://www.w3.org/2000/01/rdf-schema#", 41 | "readable": "hydra:readable", 42 | "required": "hydra:required", 43 | "returns": { 44 | "@id": "hydra:returns", 45 | "@type": "@id" 46 | }, 47 | "returnsHeader": "hydra:returnsHeader", 48 | "search": "hydra:search", 49 | "statusCode": "hydra:statusCode", 50 | "subClassOf": { 51 | "@id": "rdfs:subClassOf", 52 | "@type": "@id" 53 | }, 54 | "subject": { 55 | "@id": "hydra:subject", 56 | "@type": "@id" 57 | }, 58 | "supportedClass": "hydra:supportedClass", 59 | "supportedOperation": "hydra:supportedOperation", 60 | "supportedProperty": "hydra:supportedProperty", 61 | "title": "hydra:title", 62 | "writeable": "hydra:writeable", 63 | "xsd": "https://www.w3.org/TR/xmlschema-2/#" 64 | }, 65 | "@id": "http://petstore.swagger.io/api/v1/vocab", 66 | "@type": "ApiDocumentation", 67 | "description": "A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification", 68 | "entrypoint": "http://petstore.swagger.io/api/v1", 69 | "possibleStatus": [], 70 | "supportedClass": [ 71 | { 72 | "@id": "http://petstore.swagger.io/api/v1/vocab", 73 | "@type": "hydra:Class", 74 | "description": "Class for Pet", 75 | "supportedOperation": [], 76 | "supportedProperty": [ 77 | { 78 | "@type": "SupportedProperty", 79 | "property": "https://schema.org/Integer", 80 | "readable": "false", 81 | "required": "true", 82 | "title": "id", 83 | "writeable": "false" 84 | } 85 | ], 86 | "title": "Pet" 87 | }, 88 | { 89 | "@id": "http://petstore.swagger.io/api/v1/vocab", 90 | "@type": "hydra:Class", 91 | "description": "Class for NewPet", 92 | "supportedOperation": [], 93 | "supportedProperty": [ 94 | { 95 | "@type": "SupportedProperty", 96 | "property": "https://schema.org/Text", 97 | "readable": "false", 98 | "required": "true", 99 | "title": "name", 100 | "writeable": "false" 101 | }, 102 | { 103 | "@type": "SupportedProperty", 104 | "property": "https://schema.org/Text", 105 | "readable": "false", 106 | "required": "true", 107 | "title": "tag", 108 | "writeable": "false" 109 | } 110 | ], 111 | "title": "NewPet" 112 | }, 113 | { 114 | "@id": "http://petstore.swagger.io/api/v1/vocab", 115 | "@type": "hydra:Class", 116 | "description": "Class for Error", 117 | "supportedOperation": [], 118 | "supportedProperty": [ 119 | { 120 | "@type": "SupportedProperty", 121 | "property": "https://schema.org/Integer", 122 | "readable": "false", 123 | "required": "true", 124 | "title": "code", 125 | "writeable": "false" 126 | }, 127 | { 128 | "@type": "SupportedProperty", 129 | "property": "https://schema.org/Text", 130 | "readable": "false", 131 | "required": "true", 132 | "title": "message", 133 | "writeable": "false" 134 | } 135 | ], 136 | "title": "Error" 137 | }, 138 | { 139 | "@id": "http://petstore.swagger.io/api/v1/vocab", 140 | "@type": "hydra:Class", 141 | "description": "Class for Pets", 142 | "supportedOperation": [ 143 | { 144 | "@type": "http://schema.org/UpdateAction", 145 | "expects": "http://petstore.swagger.io/api/v1/vocab?resource=Pets", 146 | "expectsHeader": [], 147 | "method": "POST", 148 | "possibleStatus": [ 149 | { 150 | "@context": "https://www.w3.org/ns/hydra/core", 151 | "@type": "Status", 152 | "description": "pet response", 153 | "statusCode": 200, 154 | "title": "" 155 | }, 156 | { 157 | "@context": "https://www.w3.org/ns/hydra/core", 158 | "@type": "Status", 159 | "description": "unexpected error", 160 | "statusCode": 500, 161 | "title": "" 162 | } 163 | ], 164 | "returns": "http://petstore.swagger.io/api/v1/vocab?resource=Pets", 165 | "returnsHeader": [], 166 | "title": "addPet" 167 | }, 168 | { 169 | "@type": "http://schema.org/FindAction", 170 | "expects": "", 171 | "expectsHeader": [], 172 | "method": "GET", 173 | "possibleStatus": [ 174 | { 175 | "@context": "https://www.w3.org/ns/hydra/core", 176 | "@type": "Status", 177 | "description": "pet response", 178 | "statusCode": 200, 179 | "title": "" 180 | }, 181 | { 182 | "@context": "https://www.w3.org/ns/hydra/core", 183 | "@type": "Status", 184 | "description": "unexpected error", 185 | "statusCode": 500, 186 | "title": "" 187 | } 188 | ], 189 | "returns": "http://petstore.swagger.io/api/v1/vocab?resource=Pets", 190 | "returnsHeader": [], 191 | "title": "find pet by id" 192 | }, 193 | { 194 | "@type": "http://schema.org/DeleteAction", 195 | "expects": "", 196 | "expectsHeader": [], 197 | "method": "DELETE", 198 | "possibleStatus": [ 199 | { 200 | "@context": "https://www.w3.org/ns/hydra/core", 201 | "@type": "Status", 202 | "description": "pet deleted", 203 | "statusCode": 204, 204 | "title": "" 205 | }, 206 | { 207 | "@context": "https://www.w3.org/ns/hydra/core", 208 | "@type": "Status", 209 | "description": "unexpected error", 210 | "statusCode": 500, 211 | "title": "" 212 | } 213 | ], 214 | "returns": "", 215 | "returnsHeader": [], 216 | "title": "deletePet" 217 | } 218 | ], 219 | "supportedProperty": [ 220 | { 221 | "@type": "SupportedProperty", 222 | "property": "https://schema.org/Integer", 223 | "readable": "false", 224 | "required": "true", 225 | "title": "id", 226 | "writeable": "false" 227 | } 228 | ], 229 | "title": "Pets" 230 | }, 231 | { 232 | "@id": "http://www.w3.org/ns/hydra/core#Collection", 233 | "@type": "hydra:Class", 234 | "description": "null", 235 | "supportedOperation": [], 236 | "supportedProperty": [ 237 | { 238 | "@type": "SupportedProperty", 239 | "property": "http://www.w3.org/ns/hydra/core#member", 240 | "readable": "false", 241 | "required": "null", 242 | "title": "members", 243 | "writeable": "false" 244 | } 245 | ], 246 | "title": "Collection" 247 | }, 248 | { 249 | "@id": "http://www.w3.org/ns/hydra/core#Resource", 250 | "@type": "hydra:Class", 251 | "description": "null", 252 | "supportedOperation": [], 253 | "supportedProperty": [], 254 | "title": "Resource" 255 | }, 256 | { 257 | "@id": "http://petstore.swagger.io/api/v1/vocab?resource=PetsCollection", 258 | "@type": "Collection", 259 | "description": "A collection for pets", 260 | "manages": { 261 | "object": "http://petstore.swagger.io/api/v1/vocab?resource=Pets", 262 | "property": "rdfs:type" 263 | }, 264 | "subClassOf": "http://www.w3.org/ns/hydra/core#Collection", 265 | "supportedOperation": [ 266 | { 267 | "@id": "_:PetsCollection_retrieve", 268 | "@type": "http://schema.org/FindAction", 269 | "description": "Retrieves all the members of PetsCollection", 270 | "expects": "null", 271 | "expectsHeader": [], 272 | "method": "GET", 273 | "possibleStatus": [], 274 | "returns": "http://petstore.swagger.io/api/v1/vocab?resource=Pets", 275 | "returnsHeader": [] 276 | } 277 | ], 278 | "supportedProperty": [ 279 | { 280 | "@type": "SupportedProperty", 281 | "description": "The members of PetsCollection", 282 | "property": "http://www.w3.org/ns/hydra/core#member", 283 | "readable": "true", 284 | "required": "false", 285 | "title": "members", 286 | "writeable": "true" 287 | } 288 | ], 289 | "title": "PetsCollection" 290 | }, 291 | { 292 | "@id": "http://petstore.swagger.io/api/v1#EntryPoint", 293 | "@type": "hydra:Class", 294 | "description": "The main entry point or homepage of the API.", 295 | "supportedOperation": [ 296 | { 297 | "@id": "_:entry_point", 298 | "@type": "http://petstore.swagger.io/http://petstore.swagger.io/api/v1#EntryPoint", 299 | "description": "The APIs main entry point.", 300 | "expects": "null", 301 | "expectsHeader": [], 302 | "method": "GET", 303 | "possibleStatus": [], 304 | "returns": "null", 305 | "returnsHeader": [] 306 | } 307 | ], 308 | "supportedProperty": [], 309 | "title": "EntryPoint" 310 | } 311 | ], 312 | "title": "Swagger Petstore" 313 | } 314 | -------------------------------------------------------------------------------- /samples/v2/petstore-expanded.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | description: A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification 6 | termsOfService: http://swagger.io/terms/ 7 | contact: 8 | name: Swagger API Team 9 | email: apiteam@swagger.io 10 | url: http://swagger.io 11 | license: 12 | name: Apache 2.0 13 | url: https://www.apache.org/licenses/LICENSE-2.0.html 14 | servers: 15 | - url: http://petstore.swagger.io/api 16 | paths: 17 | /pets: 18 | get: 19 | description: | 20 | Returns all pets from the system that the user has access to 21 | Nam sed condimentum est. Maecenas tempor sagittis sapien, nec rhoncus sem sagittis sit amet. Aenean at gravida augue, ac iaculis sem. Curabitur odio lorem, ornare eget elementum nec, cursus id lectus. Duis mi turpis, pulvinar ac eros ac, tincidunt varius justo. In hac habitasse platea dictumst. Integer at adipiscing ante, a sagittis ligula. Aenean pharetra tempor ante molestie imperdiet. Vivamus id aliquam diam. Cras quis velit non tortor eleifend sagittis. Praesent at enim pharetra urna volutpat venenatis eget eget mauris. In eleifend fermentum facilisis. Praesent enim enim, gravida ac sodales sed, placerat id erat. Suspendisse lacus dolor, consectetur non augue vel, vehicula interdum libero. Morbi euismod sagittis libero sed lacinia. 22 | Sed tempus felis lobortis leo pulvinar rutrum. Nam mattis velit nisl, eu condimentum ligula luctus nec. Phasellus semper velit eget aliquet faucibus. In a mattis elit. Phasellus vel urna viverra, condimentum lorem id, rhoncus nibh. Ut pellentesque posuere elementum. Sed a varius odio. Morbi rhoncus ligula libero, vel eleifend nunc tristique vitae. Fusce et sem dui. Aenean nec scelerisque tortor. Fusce malesuada accumsan magna vel tempus. Quisque mollis felis eu dolor tristique, sit amet auctor felis gravida. Sed libero lorem, molestie sed nisl in, accumsan tempor nisi. Fusce sollicitudin massa ut lacinia mattis. Sed vel eleifend lorem. Pellentesque vitae felis pretium, pulvinar elit eu, euismod sapien. 23 | operationId: findPets 24 | parameters: 25 | - name: tags 26 | in: query 27 | description: tags to filter by 28 | required: false 29 | style: form 30 | schema: 31 | type: array 32 | items: 33 | type: string 34 | - name: limit 35 | in: query 36 | description: maximum number of results to return 37 | required: false 38 | schema: 39 | type: integer 40 | format: int32 41 | responses: 42 | "200": 43 | description: pet response 44 | content: 45 | application/json: 46 | schema: 47 | type: array 48 | items: 49 | $ref: "#/components/schemas/Pet" 50 | default: 51 | description: unexpected error 52 | content: 53 | application/json: 54 | schema: 55 | $ref: "#/components/schemas/Error" 56 | post: 57 | description: Creates a new pet in the store. Duplicates are allowed 58 | operationId: addPet 59 | requestBody: 60 | description: Pet to add to the store 61 | required: true 62 | content: 63 | application/json: 64 | schema: 65 | $ref: "#/components/schemas/NewPet" 66 | responses: 67 | "200": 68 | description: pet response 69 | content: 70 | application/json: 71 | schema: 72 | $ref: "#/components/schemas/Pet" 73 | default: 74 | description: unexpected error 75 | content: 76 | application/json: 77 | schema: 78 | $ref: "#/components/schemas/Error" 79 | /pets/{id}: 80 | get: 81 | description: Returns a user based on a single ID, if the user does not have access to the pet 82 | operationId: find pet by id 83 | parameters: 84 | - name: id 85 | in: path 86 | description: ID of pet to fetch 87 | required: true 88 | schema: 89 | type: integer 90 | format: int64 91 | responses: 92 | "200": 93 | description: pet response 94 | content: 95 | application/json: 96 | schema: 97 | $ref: "#/components/schemas/Pet" 98 | default: 99 | description: unexpected error 100 | content: 101 | application/json: 102 | schema: 103 | $ref: "#/components/schemas/Error" 104 | delete: 105 | description: deletes a single pet based on the ID supplied 106 | operationId: deletePet 107 | parameters: 108 | - name: id 109 | in: path 110 | description: ID of pet to delete 111 | required: true 112 | schema: 113 | type: integer 114 | format: int64 115 | responses: 116 | "204": 117 | description: pet deleted 118 | default: 119 | description: unexpected error 120 | content: 121 | application/json: 122 | schema: 123 | $ref: "#/components/schemas/Error" 124 | components: 125 | schemas: 126 | Pet: 127 | allOf: 128 | - $ref: "#/components/schemas/NewPet" 129 | - type: object 130 | required: 131 | - id 132 | properties: 133 | id: 134 | type: integer 135 | format: int64 136 | 137 | NewPet: 138 | type: object 139 | required: 140 | - name 141 | properties: 142 | name: 143 | type: string 144 | tag: 145 | type: string 146 | 147 | Error: 148 | type: object 149 | required: 150 | - code 151 | - message 152 | properties: 153 | code: 154 | type: integer 155 | format: int32 156 | message: 157 | type: string 158 | -------------------------------------------------------------------------------- /samples/v2/uspto.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.1 2 | servers: 3 | - url: '{scheme}://developer.uspto.gov/ds-api' 4 | variables: 5 | scheme: 6 | description: 'The Data Set API is accessible via https and http' 7 | enum: 8 | - 'https' 9 | - 'http' 10 | default: 'https' 11 | info: 12 | description: >- 13 | The Data Set API (DSAPI) allows the public users to discover and search 14 | USPTO exported data sets. This is a generic API that allows USPTO users to 15 | make any CSV based data files searchable through API. With the help of GET 16 | call, it returns the list of data fields that are searchable. With the help 17 | of POST call, data can be fetched based on the filters on the field names. 18 | Please note that POST call is used to search the actual data. The reason for 19 | the POST call is that it allows users to specify any complex search criteria 20 | without worry about the GET size limitations as well as encoding of the 21 | input parameters. 22 | version: 1.0.0 23 | title: USPTO Data Set API 24 | contact: 25 | name: Open Data Portal 26 | url: 'https://developer.uspto.gov' 27 | email: developer@uspto.gov 28 | tags: 29 | - name: metadata 30 | description: Find out about the data sets 31 | - name: search 32 | description: Search a data set 33 | paths: 34 | /: 35 | get: 36 | tags: 37 | - metadata 38 | operationId: list-data-sets 39 | summary: List available data sets 40 | responses: 41 | '200': 42 | description: Returns a list of data sets 43 | content: 44 | application/json: 45 | schema: 46 | $ref: '#/components/schemas/dataSetList' 47 | example: 48 | { 49 | "total": 2, 50 | "apis": [ 51 | { 52 | "apiKey": "oa_citations", 53 | "apiVersionNumber": "v1", 54 | "apiUrl": "https://developer.uspto.gov/ds-api/oa_citations/v1/fields", 55 | "apiDocumentationUrl": "https://developer.uspto.gov/ds-api-docs/index.html?url=https://developer.uspto.gov/ds-api/swagger/docs/oa_citations.json" 56 | }, 57 | { 58 | "apiKey": "cancer_moonshot", 59 | "apiVersionNumber": "v1", 60 | "apiUrl": "https://developer.uspto.gov/ds-api/cancer_moonshot/v1/fields", 61 | "apiDocumentationUrl": "https://developer.uspto.gov/ds-api-docs/index.html?url=https://developer.uspto.gov/ds-api/swagger/docs/cancer_moonshot.json" 62 | } 63 | ] 64 | } 65 | /{dataset}/{version}/fields: 66 | get: 67 | tags: 68 | - metadata 69 | summary: >- 70 | Provides the general information about the API and the list of fields 71 | that can be used to query the dataset. 72 | description: >- 73 | This GET API returns the list of all the searchable field names that are 74 | in the oa_citations. Please see the 'fields' attribute which returns an 75 | array of field names. Each field or a combination of fields can be 76 | searched using the syntax options shown below. 77 | operationId: list-searchable-fields 78 | parameters: 79 | - name: dataset 80 | in: path 81 | description: 'Name of the dataset.' 82 | required: true 83 | example: "oa_citations" 84 | schema: 85 | type: string 86 | - name: version 87 | in: path 88 | description: Version of the dataset. 89 | required: true 90 | example: "v1" 91 | schema: 92 | type: string 93 | responses: 94 | '200': 95 | description: >- 96 | The dataset API for the given version is found and it is accessible 97 | to consume. 98 | content: 99 | application/json: 100 | schema: 101 | type: string 102 | '404': 103 | description: >- 104 | The combination of dataset name and version is not found in the 105 | system or it is not published yet to be consumed by public. 106 | content: 107 | application/json: 108 | schema: 109 | type: string 110 | /{dataset}/{version}/records: 111 | post: 112 | tags: 113 | - search 114 | summary: >- 115 | Provides search capability for the data set with the given search 116 | criteria. 117 | description: >- 118 | This API is based on Solr/Lucene Search. The data is indexed using 119 | SOLR. This GET API returns the list of all the searchable field names 120 | that are in the Solr Index. Please see the 'fields' attribute which 121 | returns an array of field names. Each field or a combination of fields 122 | can be searched using the Solr/Lucene Syntax. Please refer 123 | https://lucene.apache.org/core/3_6_2/queryparsersyntax.html#Overview for 124 | the query syntax. List of field names that are searchable can be 125 | determined using above GET api. 126 | operationId: perform-search 127 | parameters: 128 | - name: version 129 | in: path 130 | description: Version of the dataset. 131 | required: true 132 | schema: 133 | type: string 134 | default: v1 135 | - name: dataset 136 | in: path 137 | description: 'Name of the dataset. In this case, the default value is oa_citations' 138 | required: true 139 | schema: 140 | type: string 141 | default: oa_citations 142 | responses: 143 | '200': 144 | description: successful operation 145 | content: 146 | application/json: 147 | schema: 148 | type: array 149 | items: 150 | type: object 151 | additionalProperties: 152 | type: object 153 | '404': 154 | description: No matching record found for the given criteria. 155 | requestBody: 156 | content: 157 | application/x-www-form-urlencoded: 158 | schema: 159 | type: object 160 | properties: 161 | criteria: 162 | description: >- 163 | Uses Lucene Query Syntax in the format of 164 | propertyName:value, propertyName:[num1 TO num2] and date 165 | range format: propertyName:[yyyyMMdd TO yyyyMMdd]. In the 166 | response please see the 'docs' element which has the list of 167 | record objects. Each record structure would consist of all 168 | the fields and their corresponding values. 169 | type: string 170 | default: '*:*' 171 | start: 172 | description: Starting record number. Default value is 0. 173 | type: integer 174 | default: 0 175 | rows: 176 | description: >- 177 | Specify number of rows to be returned. If you run the search 178 | with default values, in the response you will see 'numFound' 179 | attribute which will tell the number of records available in 180 | the dataset. 181 | type: integer 182 | default: 100 183 | required: 184 | - criteria 185 | components: 186 | schemas: 187 | dataSetList: 188 | type: object 189 | properties: 190 | total: 191 | type: integer 192 | apis: 193 | type: array 194 | items: 195 | type: object 196 | properties: 197 | apiKey: 198 | type: string 199 | description: To be used as a dataset parameter value 200 | apiVersionNumber: 201 | type: string 202 | description: To be used as a version parameter value 203 | apiUrl: 204 | type: string 205 | format: uriref 206 | description: "The URL describing the dataset's fields" 207 | apiDocumentationUrl: 208 | type: string 209 | format: uriref 210 | description: A URL to the API console for each API 211 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | name='hydra_openapi_parser', 5 | version='0.2.0', 6 | packages=find_packages(), 7 | license='MIT', 8 | description='A Parser from OpenAPI to Hydra specs', 9 | long_description=open('README.md').read(), 10 | long_description_content_type="text/markdown", 11 | url='https://github.com/HTTP-APIs/hydra-openapi-parser', 12 | zip_safe=False 13 | ) 14 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTTP-APIs/hydra-openapi-parser/329379434d9bc805883aa5934a85824e5fae1df2/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_parser.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import os 3 | from os.path import dirname, abspath 4 | 5 | from hydra_python_core.doc_writer import HydraClass 6 | from hydra_openapi_parser import openapi_parser 7 | import yaml 8 | 9 | 10 | def import_doc(): 11 | print("Importing Open Api Documentation ..") 12 | abs_path = abspath("{}/samples/petstore_openapi.yaml".format( 13 | dirname(dirname(__file__)))) 14 | with open(abs_path, 'r') as stream: 15 | try: 16 | return yaml.load(stream) 17 | except yaml.YAMLError as exc: 18 | print(exc) 19 | 20 | 21 | class TestParser(unittest.TestCase): 22 | @classmethod 23 | def setUpClass(self): 24 | doc = import_doc() 25 | self.doc = doc 26 | 27 | @classmethod 28 | def tearDownClass(cls): 29 | pass 30 | 31 | def test_generate_empty_object(self): 32 | """Test if the empty object is being generated correctly """ 33 | object_ = openapi_parser.generate_empty_object() 34 | assert isinstance(object_["prop_definition"], list) 35 | assert isinstance(object_["op_definition"], list) 36 | assert isinstance(object_["class_definition"], type) 37 | assert isinstance(object_["collection"], bool) 38 | 39 | def test_valid_endpoint(self): 40 | """Test if the endpoint is valid and can be parsed """ 41 | path = 'A/B/{id}/C/D' 42 | result = openapi_parser.valid_endpoint(path) 43 | assert result is "False" 44 | assert isinstance(result, str) 45 | path = 'A/B/{id}' 46 | result = openapi_parser.valid_endpoint(path) 47 | assert result is "Collection" 48 | assert isinstance(result, str) 49 | path = 'A/B/id' 50 | result = openapi_parser.valid_endpoint(path) 51 | assert result is "True" 52 | assert isinstance(result, str) 53 | 54 | def test_get_class_name(self): 55 | """Test if the class name is being extracted properly from the path """ 56 | path = "A/B/C/Pet" 57 | path_list = path.split('/') 58 | result = openapi_parser.get_class_name(path_list) 59 | assert result is path_list[3] 60 | assert isinstance(result, str) 61 | 62 | def test_get_data_from_location(self): 63 | """Test if the data from the location given is being fetched correctly""" 64 | path = '#/definitions/Order' 65 | path_list = path.split('/') 66 | result = openapi_parser.get_data_at_location(path_list, self.doc) 67 | response = self.doc["definitions"]["Order"] 68 | assert response is result 69 | 70 | def test_sanitise_path(self): 71 | """Test if the variables can be removed from the path""" 72 | path = "A/B/C/{id}" 73 | result = openapi_parser.sanitise_path(path) 74 | assert isinstance(result, str) 75 | 76 | def test_allow_parameter(self): 77 | """Test if the rules are being followed """ 78 | parameter_block = self.doc["paths"]["/pet"]["post"]["parameters"][0] 79 | result = openapi_parser.allow_parameter(parameter_block) 80 | assert result is True 81 | assert isinstance(result, bool) 82 | parameter_block = self.doc["paths"]["/pet"]["get"]["parameters"][0] 83 | result = openapi_parser.allow_parameter(parameter_block) 84 | assert result is False 85 | assert isinstance(result, bool) 86 | 87 | def test_parse(self): 88 | """Test the hydra documentation """ 89 | result = openapi_parser.parse(self.doc) 90 | assert isinstance(result, dict) 91 | 92 | def test_check_collection(self): 93 | """Test if collections are being identified properly""" 94 | schema_block = { 95 | 'type': 'array', 'items': { 96 | '$ref': '#/definitions/Pet'}} 97 | method = "/Pet" 98 | result = openapi_parser.check_collection(schema_block, method) 99 | assert isinstance(result, bool) 100 | assert result 101 | 102 | def test_check_collection_false(self): 103 | "Test if non collections are identified" 104 | schema = {'$ref': '#/definitions/User'} 105 | method = "/Pet" 106 | result = openapi_parser.check_collection(schema, method) 107 | assert isinstance(result, bool) 108 | assert not result 109 | 110 | 111 | if __name__ == '__main__': 112 | print("Starting tests ..") 113 | unittest.main() 114 | --------------------------------------------------------------------------------