├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── Makefile
├── README.md
├── doc
├── bundle-html.png
└── logo.png
├── example
├── example.json
└── example.yaml
├── requirements.txt
├── setup.py
├── src
├── __init__.py
├── commands
│ ├── __init__.py
│ ├── bundle.py
│ ├── list.py
│ ├── resolve.py
│ └── scaffold.py
├── main.py
├── models
│ ├── __init__.py
│ ├── route.py
│ └── route_collection.py
├── resources
│ ├── __init__.py
│ └── template.html
└── utils
│ ├── __init__.py
│ ├── export.py
│ ├── file_control.py
│ ├── repository.py
│ └── resolver.py
└── tests
├── __init__.py
└── unit
├── __init__.py
├── commands
├── __init__.py
├── bundle
│ ├── __init__.py
│ ├── resources
│ │ ├── expected
│ │ │ ├── with-header.yaml
│ │ │ └── without-header.yaml
│ │ └── inputs
│ │ │ ├── file1.json
│ │ │ ├── file2.yaml
│ │ │ └── template-header.yaml
│ └── test_bundle.py
├── resolve
│ ├── __init__.py
│ ├── resources
│ │ ├── expected
│ │ │ ├── duplicated-spec.yaml
│ │ │ └── find-a-spec.yaml
│ │ └── inputs
│ │ │ ├── duplicated-spec-1.yaml
│ │ │ ├── duplicated-spec-2.yaml
│ │ │ └── find-a-spec.yaml
│ └── test_resolve.py
└── scaffold
│ ├── __init__.py
│ ├── resources
│ ├── expected-default-output.json
│ └── expected-multiple-response-output.json
│ └── test_scaffold.py
└── utils
├── __init__.py
├── file_control
├── __init__.py
├── resources
│ ├── file.json
│ ├── file.txt
│ ├── file.yaml
│ └── file.yml
└── test_file_control.py
├── repository
├── __init__.py
├── resources
│ ├── multi-file
│ │ ├── file1.json
│ │ └── file2.yaml
│ ├── sample.yaml
│ ├── with-path-ref.yaml
│ └── with-path-referred.yaml
└── test_repository.py
└── resolver
├── __init__.py
├── resources
├── expected
│ ├── escape_characters.yaml
│ ├── local_components.yaml
│ ├── nested_reference.yaml
│ ├── remote_components.yaml
│ └── remote_file.yaml
└── inputs
│ ├── escape_characters.yaml
│ ├── local_components.yaml
│ ├── nested_reference.yaml
│ ├── remote_components.yaml
│ ├── remote_components_referred.yaml
│ ├── remote_file.yaml
│ └── remote_file_referred.yaml
└── test_resolver_resolve.py
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .DS_Store
3 |
4 | #########################################################
5 | # Byte-compiled / optimized / DLL files
6 | __pycache__/
7 | *.py[cod]
8 | *$py.class
9 |
10 | # C extensions
11 | *.so
12 |
13 | # Distribution / packaging
14 | .Python
15 | build/
16 | develop-eggs/
17 | dist/
18 | downloads/
19 | eggs/
20 | .eggs/
21 | lib/
22 | lib64/
23 | parts/
24 | sdist/
25 | var/
26 | wheels/
27 | *.egg-info/
28 | .installed.cfg
29 | *.egg
30 | MANIFEST
31 |
32 | # PyInstaller
33 | # Usually these files are written by a python script from a template
34 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
35 | *.manifest
36 | *.spec
37 |
38 | # Installer logs
39 | pip-log.txt
40 | pip-delete-this-directory.txt
41 |
42 | # Unit test / coverage reports
43 | htmlcov/
44 | .tox/
45 | .coverage
46 | .coverage.*
47 | .cache
48 | nosetests.xml
49 | coverage.xml
50 | *.cover
51 | .hypothesis/
52 | .pytest_cache/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 |
63 | # Flask stuff:
64 | instance/
65 | .webassets-cache
66 |
67 | # Scrapy stuff:
68 | .scrapy
69 |
70 | # Sphinx documentation
71 | docs/_build/
72 |
73 | # PyBuilder
74 | target/
75 |
76 | # Jupyter Notebook
77 | .ipynb_checkpoints
78 |
79 | # pyenv
80 | .python-version
81 |
82 | # celery beat schedule file
83 | celerybeat-schedule
84 |
85 | # SageMath parsed files
86 | *.sage.py
87 |
88 | # Environments
89 | .env
90 | .venv
91 | env/
92 | venv/
93 | ENV/
94 | env.bak/
95 | venv.bak/
96 |
97 | # Spyder project settings
98 | .spyderproject
99 | .spyproject
100 |
101 | # Rope project settings
102 | .ropeproject
103 |
104 | # mkdocs documentation
105 | /site
106 |
107 | # mypy
108 | .mypy_cache/
109 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 | os:
3 | - linux
4 | python:
5 | - '2.7'
6 | - '3.4'
7 | - '3.5'
8 | - '3.6'
9 | - '3.7'
10 | - '3.8-dev'
11 | install:
12 | - pip install -r requirements.txt
13 | script:
14 | - python -m unittest discover -s tests/unit -p "test_*.py"
15 | - python setup.py install
16 | - openapi-cli-tool list `find ./example`
17 | - openapi-cli-tool bundle -t html `find ./example`
18 | deploy:
19 | provider: pypi
20 | distributions: sdist bdist_wheel
21 | user: hakopako
22 | password:
23 | secure: XHAZIK0o9WRfkZ3SIUrSStB6QOvSZFXccuYXD/GUEJrbtSlgxaaOQZWlpn1hr/e2ZtggEduZMsnHaF63A76XZBL5rA8wukK1sjIZ5+ERX9cPlmswTJdfFayf2QvfHlSYEuW479LVRy5ob2ZL/KvtNO5PQcOoqSGBfbOiukfMGPgn0kgpW27QKRZMnF45sMTwpCfSOpAwKTeRMtbiM7Pj7criqEw4v1NdTk/HljTTJNJUHGhs8xjFsvj5C07aIZTTJwAhnwsKomnYpV948n8zoQNyuyZVp+dmcXV8VIdWBGqRc6bA8zguKcc32CdwOnrMhMmXWIktM0TOf4afm44S47nLwZym5xcjug+w8j6sLhthu7z2U3/HbM18Zm+INFTdMZH4l5jCx9sG6k9TyoWgU04BdM6rrild+7PVL9108WPIjYXWsbsrD8BsF6TGUts0kOiaqoDAvLSv8lQmRIHnYyhoGvY9zpAj3xNdq6g7QlcP0Q7Pq39pXrcSZLKGM00d9RGCCWSl0JHhfIQSkJNAMcVbWTdOdNq/miRK/hdBcB2eHwGhYqzn1CSiYjIe1eBOOtQYkZbHWyvQS5seVRfbuZGbPzCzEjLFadCg4171ED4/qrS0uyGFs/g4e70e1Yu5FrFR9/FN7S+Hc1x68Gqe0xHN3FFNYOUyq/LaPg2cA6c=
24 | skip_existing: true
25 | on:
26 | tags: true
27 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # 0.3.0 (December 13, 2019)
2 |
3 | - Support $ref individual paths (#18)
4 | - Bug fix (#18)
5 |
6 |
7 | # 0.2.1 (October 30, 2019)
8 |
9 | - Fix TypeError for resovler command #14
10 |
11 |
12 | # 0.2.0 (October 2, 2019)
13 |
14 | - Updated performance
15 | - Updated file path arguments to multiple filenames with space separation.
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Aya Shimada
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 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 |
2 | test:
3 | python -m unittest discover -v -s tests/unit -p "test_*.py"
4 |
5 |
6 | build:
7 | python setup.py install
8 |
9 | clean:
10 | find ./ -name "*.pyc" -exec rm {} \;
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 |
4 | [](https://travis-ci.com/hakopako/openapi-cli-tool)
5 |
6 |
7 |
8 |
9 | # openapi-cli-tool
10 | OpenAPI (Swagger 3.x) CLI Tool.
11 |
12 | - Supports multiple file extensions (json|yaml|yml).
13 | - Can list up defined API paths.
14 | - Display an API specification which is resolved (`$ref`).
15 | - Bundle multi-file into one (output to json|yaml|html).
16 | - OAS interactive scaffolding.
17 |
18 | # Requirements
19 |
20 | Python 2.7, 3.4 <=.
21 |
22 | # Installation
23 |
24 | With pip:
25 |
26 | ```bash
27 | $ pip install openapi-cli-tool
28 | ```
29 | Manually:
30 | Clone the repository and execute the Python installation command on your machine.
31 |
32 | ```bash
33 | $ pip -r requirements.txt install
34 | $ python setup.py install
35 | ```
36 |
37 | Then `openapi-cli-tool` command is installed.
38 |
39 | # Usage
40 |
41 | ```
42 | $ openapi-cli-tool --help
43 | Usage: openapi-cli-tool [OPTIONS] COMMAND [ARGS]...
44 |
45 | Options:
46 | --help Show this message and exit.
47 |
48 | Commands:
49 | bundle Bundle multiple files into one.
50 | list List up API paths in a file or directory.
51 | resolve Display `$ref` resolved API specification.
52 | scaffold Interactively create a simple OpenAPI Specification.
53 | ```
54 |
55 | ## Bundle
56 |
57 | Bundle multi-file specifications into one, regardless of file extension (json|yaml|yml).
58 |
59 | ```
60 | $ openapi-cli-tool bundle --help
61 | Usage: openapi-cli-tool bundle [OPTIONS] FILE_PATHS
62 |
63 | Bundle multiple files into one.
64 |
65 | Options:
66 | -f, --file TEXT Load common objects such as info and servers from a
67 | specific file. Default is a file which is the top of list
68 | command result.
69 | -t, --type TEXT Export data type. {json|yaml|html} [default: json]
70 | --help Show this message and exit.
71 | ```
72 |
73 | example:
74 | ```
75 | $ openapi-cli-tool bundle -t html file1.json file2.yaml` > ./specification.html
76 | ```
77 |
78 | In the html file, an unpkg version of [swagger-ui](https://github.com/swagger-api/swagger-ui) is embedded. Rendered screenshot below:
79 |
80 |
81 | 
82 |
83 |
84 | ## List
85 |
86 | List up API paths from a file/directory regardless of the file extension (json|yaml|yml).
87 |
88 | ```bash
89 | $ openapi-cli-tool list `find ./spec`
90 |
91 | Method Path File
92 | -------- --------- ------------------------------------------
93 | PUT /avatar ./tests/resources/spec/sample.yml
94 | GET /follwers ./tests/resources/spec/folder1/sample2.yaml
95 | POST /follwers ./tests/resources/spec/folder1/sample2.yaml
96 | PUT /follwers ./tests/resources/spec/folder1/sample2.yaml
97 | POST /pets ./tests/resources/spec/sample.yml
98 | GET /posts ./tests/resources/spec/folder1/sample.json
99 | POST /posts ./tests/resources/spec/folder1/sample.json
100 | GET /users ./tests/resources/spec/folder1/sample.json
101 | POST /users ./tests/resources/spec/folder1/sample.json
102 | ```
103 |
104 |
105 | ## Resolve
106 |
107 | Display an API specification which is resolved from a multi-file API specification via $ref pointers.
108 |
109 | ```
110 | Usage: openapi-cli-tool resolve [OPTIONS] METHOD PATH FILE_PATHS
111 |
112 | Display `$ref` resolved API specification.
113 |
114 | Options:
115 | -t, --type TEXT Export data type. {json|yaml} [default: json]
116 | --help Show this message and exit.
117 | ```
118 |
119 | example:
120 | ```bash
121 | $ openapi-cli-tool resolve post /cats `find ./tests/resources/spec`
122 | ```
123 |
124 |
125 | ## Scaffold
126 |
127 | Interactively input information of your API.
128 | A simple OpenAPI Specification is generated from your prompt.
129 |
130 | ```bash
131 | $ openapi-cli-tool scaffold
132 |
133 | Please enter title [""]: sample
134 | Please enter version [v1.0]:
135 | Please enter license [Apache 2.0]:
136 | Please enter server url [http://example.com]:
137 | Please enter path [/]: /example
138 | Please enter method for /example [get|post|put|delete|head|option|trace]: get
139 | Please enter description for get /example [""]: sample get endpoint
140 | Please enter response code for get /example [200]:
141 | ```
142 |
--------------------------------------------------------------------------------
/doc/bundle-html.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hakopako/openapi-cli-tool/446f428f0d0c36842bab405e7efef65a9a713b90/doc/bundle-html.png
--------------------------------------------------------------------------------
/doc/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hakopako/openapi-cli-tool/446f428f0d0c36842bab405e7efef65a9a713b90/doc/logo.png
--------------------------------------------------------------------------------
/example/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "info": {
3 | "version": "v1.0",
4 | "license": {
5 | "name": "Apache 2.0"
6 | },
7 | "title": ""
8 | },
9 | "paths": {
10 | "/users": {
11 | "post": {
12 | "description": "",
13 | "responses": {
14 | "200": {
15 | "content": {
16 | "application/json": {
17 | "schema": {}
18 | }
19 | },
20 | "description": "succeed to register a new user"
21 | },
22 | "400": {
23 | "content": {
24 | "application/json": {
25 | "schema": {}
26 | }
27 | },
28 | "description": "failed to register a new user"
29 | }
30 | }
31 | },
32 | "get": {
33 | "description": "",
34 | "responses": {
35 | "200": {
36 | "content": {
37 | "application/json": {
38 | "schema": {}
39 | }
40 | },
41 | "description": "succeed to return all users info"
42 | },
43 | "400": {
44 | "content": {
45 | "application/json": {
46 | "schema": {}
47 | }
48 | },
49 | "description": "failed to return all users infon"
50 | }
51 | }
52 | }
53 | }
54 | },
55 | "servers": [
56 | {
57 | "url": "http://example.com"
58 | }
59 | ],
60 | "openapi": "3.0.0"
61 | }
62 |
--------------------------------------------------------------------------------
/example/example.yaml:
--------------------------------------------------------------------------------
1 | info:
2 | version: v1.0
3 | license:
4 | name: Apache 2.0
5 | title: ''
6 | paths:
7 | /followers:
8 | post:
9 | description: ''
10 | responses:
11 | '200':
12 | content:
13 | application/json:
14 | schema:
15 | $ref: '#/components/schemas/Follower'
16 | description: succeed to register a new follwer
17 | '400':
18 | content:
19 | application/json:
20 | schema: {}
21 | description: failed to register a new follwer
22 | put:
23 | description: ''
24 | responses:
25 | '200':
26 | content:
27 | application/json:
28 | schema:
29 | $ref: '#/components/schemas/Follower'
30 | description: succeed to update a follwer
31 | '400':
32 | content:
33 | application/json:
34 | schema: {}
35 | description: failed to update a follwer
36 |
37 | servers:
38 | - url: 'http://example.com'
39 | openapi: 3.0.0
40 |
41 |
42 | components:
43 | schemas:
44 | Follower:
45 | type: object
46 | properties:
47 | name:
48 | type: string
49 | fav_number:
50 | type: integer
51 | required:
52 | - name
53 | - fav_number
54 | additionalProperties: false
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | click==7.0
2 | pyyaml==5.1
3 | tabulate==0.8.3
4 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup, find_packages
2 |
3 | setup(
4 | name='openapi-cli-tool',
5 | description="openapi cli tool",
6 | long_description=open('README.md').read(),
7 | long_description_content_type='text/markdown',
8 | version='0.3.0',
9 | author="Ayaka Shimada",
10 | author_email='aya.a.shimada@gmail.com',
11 | url='https://github.com/hakopako/openapi-cli-tool',
12 | license='MIT',
13 | install_requires=open('requirements.txt').readlines(),
14 | packages=find_packages(exclude=["tests", "tests.*"]),
15 | package_data={'': ['*.html']},
16 | include_package_data=True,
17 | entry_points={
18 | 'console_scripts': [
19 | 'openapi-cli-tool = src.main:main',
20 | ],
21 | },
22 | zip_safe=False
23 | )
24 |
--------------------------------------------------------------------------------
/src/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hakopako/openapi-cli-tool/446f428f0d0c36842bab405e7efef65a9a713b90/src/__init__.py
--------------------------------------------------------------------------------
/src/commands/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hakopako/openapi-cli-tool/446f428f0d0c36842bab405e7efef65a9a713b90/src/commands/__init__.py
--------------------------------------------------------------------------------
/src/commands/bundle.py:
--------------------------------------------------------------------------------
1 | from src.utils.resolver import resolver
2 | from src.utils.export import export_json, export_yaml, export_html
3 | from src.utils.repository import Repository
4 |
5 |
6 | def run_bundle(spec_paths, filename=None):
7 | repository = Repository(spec_paths)
8 | collection = repository.routes
9 | if collection.len() == 0 and filename is None:
10 | return {}
11 | elif collection.len() > 0 and filename is None:
12 | header_file = collection.get()[0].file
13 | elif filename is not None:
14 | header_file = filename
15 | try:
16 | data = repository.file_control.load_dict_from_file(header_file)
17 | if isinstance(data, dict):
18 | data['paths'] = {}
19 | data.pop('components', None)
20 | else:
21 | raise Exception('Parse Error: ' + header_file)
22 | except Exception as e:
23 | print(e)
24 | return {}
25 |
26 | for route in collection.get():
27 | if route.url not in data['paths']:
28 | data['paths'][route.url] = {}
29 | resolved_spec = resolver(route.file, route.spec, repository.file_control)
30 | data['paths'][route.url].update({route.method.lower(): resolved_spec})
31 |
32 | return data
33 |
34 |
35 | def bundle(spec_paths, type, filename=None):
36 | specs = run_bundle(spec_paths, filename)
37 | if type == 'json':
38 | export_json(specs)
39 | elif type == 'yaml':
40 | export_yaml(specs)
41 | elif type == 'html':
42 | export_html(specs)
43 |
--------------------------------------------------------------------------------
/src/commands/list.py:
--------------------------------------------------------------------------------
1 | from src.utils.repository import Repository
2 | from src.utils.export import export_table
3 |
4 |
5 | def list(paths):
6 | repository = Repository(paths)
7 | export_table(repository.routes.to_list(), ['Method', 'Path', 'File'])
8 |
--------------------------------------------------------------------------------
/src/commands/resolve.py:
--------------------------------------------------------------------------------
1 | from src.utils.resolver import resolver
2 | from src.utils.export import export_json, export_yaml
3 | from src.utils.repository import Repository
4 |
5 |
6 | def run_resolve(method, path, spec_paths):
7 | repository = Repository(spec_paths)
8 | collection = repository.routes
9 | specs = [resolver(route.file, route.spec, repository.file_control) for route in collection.get() if route.method == method.upper() and route.url == path]
10 | return specs
11 |
12 |
13 | def resolve(method, path, spec_paths, type):
14 | specs = run_resolve(method, path, spec_paths)
15 | if len(specs) == 0:
16 | print("Not found")
17 | if type == 'json':
18 | export_json(specs)
19 | elif type == 'yaml':
20 | export_yaml(specs)
21 | if len(specs) > 1:
22 | print("\nWARNING: multiple specifications found for " + method + ' ' + path)
23 |
--------------------------------------------------------------------------------
/src/commands/scaffold.py:
--------------------------------------------------------------------------------
1 | from src.utils.export import export_json
2 |
3 |
4 | data = {
5 | 'openapi': '3.0.0',
6 | 'info': {'title': '', 'version': '', 'license': {'name': ''}},
7 | 'servers': [{'url': ''}],
8 | 'paths': {}
9 | }
10 |
11 |
12 | def data_input(message, default):
13 | try:
14 | return raw_input(message) or default
15 | except NameError:
16 | return input(message) or default
17 |
18 |
19 | def run_scaffold():
20 | data['info']['title'] = data_input('Please enter title [""]: ', '')
21 | data['info']['version'] = data_input('Please enter version [v1.0]: ', 'v1.0')
22 | data['info']['license']['name'] = data_input('Please enter license [Apache 2.0]: ', 'Apache 2.0')
23 | data['servers'][0]['url'] = data_input('Please enter server url [http://example.com]: ', 'http://example.com')
24 |
25 | path_name = data_input('Please enter path [/]: ', '/')
26 | data['paths'][path_name] = dict()
27 |
28 | while len(data['paths'][path_name]) == 0 or \
29 | data_input('Add more request method for %s ? Y/n [n]: ' % path_name, 'n') == 'Y':
30 | path_method = ''
31 | while path_method not in ['get', 'post', 'put', 'delete', 'head', 'option', 'trace']:
32 | path_method = data_input('Please enter method for %s [get|post|put|delete|head|option|trace]: ' % path_name,
33 | '')
34 | path_desc = data_input('Please enter description for %s %s [""]: ' % (path_method, path_name), '')
35 |
36 | data['paths'][path_name][path_method] = {
37 | 'description': path_desc,
38 | 'responses': {}
39 | }
40 |
41 | while len(data['paths'][path_name][path_method]['responses']) == 0 or \
42 | data_input('Add more response for %s %s ? Y/n [n]: ' % (path_method, path_name), 'n') == 'Y':
43 | response_code = data_input('Please enter response code for %s %s [200]: ' % (path_method, path_name), '200')
44 | response_desc = data_input('Please enter response description for %s: ' % response_code, '')
45 | response_content_type = data_input(
46 | 'Please enter response content-type for %s %s [application/json]: ' % (path_method, path_name),
47 | 'application/json')
48 |
49 | data['paths'][path_name][path_method]['responses'][response_code] = {
50 | 'description': response_desc,
51 | 'content': {
52 | response_content_type: {"schema": {}}
53 | }
54 | }
55 | return data
56 |
57 |
58 | def scaffold():
59 | result = run_scaffold()
60 | export_json(result)
61 |
--------------------------------------------------------------------------------
/src/main.py:
--------------------------------------------------------------------------------
1 | import click
2 | from src.commands.scaffold import scaffold
3 | from src.commands.list import list
4 | from src.commands.resolve import resolve
5 | from src.commands.bundle import bundle
6 |
7 |
8 | def validate_resolve_type(ctx, param, value):
9 | if value in ['json', 'yaml']:
10 | return value
11 | else:
12 | raise click.BadParameter('type need to be json or yaml.')
13 |
14 |
15 | def validate_bundle_type(ctx, param, value):
16 | if value in ['json', 'yaml', 'html']:
17 | return value
18 | else:
19 | raise click.BadParameter('type need to be json, yaml or html.')
20 |
21 |
22 | @click.group()
23 | def main():
24 | pass
25 |
26 |
27 | @main.command('list', help='List up API paths in a file or directory.')
28 | @click.argument('file_paths', nargs=-1, type=click.Path(exists=True))
29 | def cmd_list(file_paths):
30 | list(file_paths)
31 |
32 |
33 | @main.command('resolve', help='Display `$ref` resolved API specification.')
34 | @click.argument('method')
35 | @click.argument('path')
36 | @click.argument('file_paths', nargs=-1, type=click.Path(exists=True))
37 | @click.option('-t', '--type', 'type', default='json', show_default=True, callback=validate_resolve_type, help='Export data type. {json|yaml}')
38 | def cmd_resolve(method, path, file_paths, type):
39 | resolve(method, path, file_paths, type)
40 |
41 |
42 | @main.command('bundle', help='Bundle multiple files into one.')
43 | @click.option('-f', '--file', 'file', help='Load common objects such as info and servers from a specific file. Default is a file which is the top of list command result.')
44 | @click.option('-t', '--type', 'type', default='json', show_default=True, callback=validate_bundle_type, help='Export data type. {json|yaml|html}')
45 | @click.argument('file_paths', nargs=-1, type=click.Path(exists=True))
46 | def cmd_bundle(file, type, file_paths):
47 | bundle(file_paths, type, file)
48 |
49 |
50 | @main.command('scaffold', help='Interactively create a simple OpenAPI Specification.')
51 | def cmd_scaffold():
52 | scaffold()
53 |
54 |
55 | if __name__ == '__main__':
56 | main()
57 |
--------------------------------------------------------------------------------
/src/models/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hakopako/openapi-cli-tool/446f428f0d0c36842bab405e7efef65a9a713b90/src/models/__init__.py
--------------------------------------------------------------------------------
/src/models/route.py:
--------------------------------------------------------------------------------
1 |
2 |
3 | class Route:
4 |
5 | def __init__(self, method, url, file, spec={}):
6 | self.method = method
7 | self.url = url
8 | self.file = file
9 | self.spec = spec
10 |
11 | def __repr__(self):
12 | return 'method='+self.method+' url='+self.url+' file='+self.file+' spec='+str(self.spec)
--------------------------------------------------------------------------------
/src/models/route_collection.py:
--------------------------------------------------------------------------------
1 | class RouteCollection:
2 |
3 | def __init__(self):
4 | self.collection = []
5 |
6 |
7 | def to_list(self):
8 | result = []
9 | for route in self.collection:
10 | result.append([route.method, route.url, route.file])
11 | return result
12 |
13 |
14 | def add(self, route):
15 | self.collection.append(route)
16 |
17 |
18 | def get(self):
19 | return self.collection
20 |
21 |
22 | def sort(self):
23 | try:
24 | self.collection.sort(lambda x,y: cmp(x.url, y.url), lambda x,y: cmp(x.method, y.method))
25 | except:
26 | self.collection.sort(key=lambda x: (x.url, x.method))
27 |
28 |
29 | def len(self):
30 | return len(self.collection)
31 |
--------------------------------------------------------------------------------
/src/resources/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hakopako/openapi-cli-tool/446f428f0d0c36842bab405e7efef65a9a713b90/src/resources/__init__.py
--------------------------------------------------------------------------------
/src/resources/template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | ###TITLE###
4 |
5 |
6 |
7 |
8 |
9 |
22 |
23 |
--------------------------------------------------------------------------------
/src/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hakopako/openapi-cli-tool/446f428f0d0c36842bab405e7efef65a9a713b90/src/utils/__init__.py
--------------------------------------------------------------------------------
/src/utils/export.py:
--------------------------------------------------------------------------------
1 | import json
2 | import yaml
3 | from tabulate import tabulate
4 | from src.utils.file_control import load_file
5 | from pkg_resources import resource_filename
6 |
7 |
8 | def export_json(export_data):
9 | result = json.dumps(export_data, indent=2)
10 | print(result)
11 |
12 |
13 | def export_yaml(export_data):
14 | result = yaml.safe_dump(export_data, default_flow_style=False)
15 | print(result)
16 |
17 |
18 | def export_table(export_data, headers):
19 | print(tabulate(export_data, headers=headers))
20 |
21 |
22 | def export_html(export_data):
23 | result = json.dumps(export_data)
24 | template = load_file(resource_filename('src.resources', 'template.html'))
25 | html = template.replace('###TITLE###', export_data['info']['title']).replace('###SPEC###', result)
26 | print(html)
27 |
--------------------------------------------------------------------------------
/src/utils/file_control.py:
--------------------------------------------------------------------------------
1 | import os
2 | import json
3 | import yaml
4 | from copy import deepcopy
5 |
6 |
7 | def load_file(file_path):
8 | try:
9 | r = open(file_path, 'r')
10 | content = r.read()
11 | r.close()
12 | return content
13 | except:
14 | return ''
15 |
16 |
17 | class FileControl:
18 |
19 | def __init__(self):
20 | self.spec_dict = {}
21 |
22 | def load_dict_from_file(self, file_path):
23 | if file_path in self.spec_dict:
24 | return deepcopy(self.spec_dict[file_path])
25 | try:
26 | r = open(file_path, 'r')
27 | content = r.read()
28 | r.close()
29 | _, file_extension = os.path.splitext(file_path)
30 | spec = ""
31 | if not os.path.exists(file_path):
32 | raise Exception('File not found.')
33 | if file_extension == '.json':
34 | spec = json.loads(content)
35 | elif file_extension in ['.yaml', '.yml']:
36 | spec = yaml.safe_load(content)
37 | else:
38 | raise Exception('Unknown extension.')
39 | self.spec_dict[file_path] = spec
40 | return spec
41 | except Exception as e:
42 | raise Exception('File Read Error (' + file_path + '): ' + str(e))
43 |
--------------------------------------------------------------------------------
/src/utils/repository.py:
--------------------------------------------------------------------------------
1 | import os
2 | from src.utils.file_control import FileControl
3 | from src.utils.resolver import resolve_once
4 | from src.models.route import Route
5 | from src.models.route_collection import RouteCollection
6 |
7 |
8 | class Repository:
9 |
10 | def __init__(self, file_path, file_control = FileControl()):
11 | self.file_control = file_control
12 | self.routes = self.generate(file_path)
13 |
14 | def _set_from_file(self, collection, file):
15 | content = self.file_control.load_dict_from_file(file)
16 | if 'paths' not in content or len(content['paths']) == 0:
17 | return
18 | for path, spec in content['paths'].items():
19 | spec = resolve_once(file, spec, self.file_control)
20 | for method in spec:
21 | collection.add(
22 | Route(
23 | method.upper(),
24 | path,
25 | file,
26 | spec[method]
27 | ))
28 |
29 | def generate(self, file_path):
30 | collection = RouteCollection()
31 | files = [p for p in file_path if os.path.isfile(p)]
32 | for file in files:
33 | try:
34 | self._set_from_file(collection, file)
35 | except Exception as e:
36 | raise Exception(e)
37 | collection.sort()
38 | return collection
39 |
--------------------------------------------------------------------------------
/src/utils/resolver.py:
--------------------------------------------------------------------------------
1 | import os
2 | from src.utils.file_control import FileControl
3 |
4 |
5 | def _resolve_escape_character(value):
6 | return value.replace("~0", "~").replace("~1", "/")
7 |
8 |
9 | def _find_reference(link, base_file_path, file_control):
10 | if '#/' in link:
11 | file_path, item_path = link.split('#/')
12 | items = item_path.split('/')
13 | else:
14 | file_path, items = link, []
15 | spec_file = base_file_path if file_path == '' else os.path.join(os.path.dirname(base_file_path), file_path)
16 | try:
17 | spec = file_control.load_dict_from_file(spec_file)
18 | for key in items:
19 | spec = spec[_resolve_escape_character(key)]
20 | return spec, spec_file
21 | except Exception as e:
22 | print('Failed to load reference. file=' + base_file_path + ' $ref=' + link + ' (' + str(e) + ')')
23 | return '', spec_file
24 |
25 |
26 | def resolver(file_path, data, file_control = FileControl()):
27 | if not isinstance(data, dict):
28 | return data
29 |
30 | for key, value in data.items():
31 | if key == '$ref':
32 | new_value, base_file_path = _find_reference(value, file_path, file_control)
33 | data = resolver(base_file_path, new_value, file_control)
34 | else:
35 | new_value = resolver(file_path, value, file_control)
36 | data[key] = new_value
37 | return data
38 |
39 |
40 | def resolve_once(file_path, data, file_control = FileControl()):
41 | if not isinstance(data, dict):
42 | return data
43 |
44 | if '$ref' in data:
45 | new_value, _ = _find_reference(data['$ref'], file_path, file_control)
46 | return new_value
47 | return data
48 |
49 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from os.path import dirname, abspath, join, sep
3 | src = dirname(dirname(abspath(__file__)))
4 | assert src.split(sep)[-1].lower() == 'src'
5 | sys.path.append(src)
6 | print('src folder appended to path: ', src)
7 |
--------------------------------------------------------------------------------
/tests/unit/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hakopako/openapi-cli-tool/446f428f0d0c36842bab405e7efef65a9a713b90/tests/unit/__init__.py
--------------------------------------------------------------------------------
/tests/unit/commands/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hakopako/openapi-cli-tool/446f428f0d0c36842bab405e7efef65a9a713b90/tests/unit/commands/__init__.py
--------------------------------------------------------------------------------
/tests/unit/commands/bundle/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hakopako/openapi-cli-tool/446f428f0d0c36842bab405e7efef65a9a713b90/tests/unit/commands/bundle/__init__.py
--------------------------------------------------------------------------------
/tests/unit/commands/bundle/resources/expected/with-header.yaml:
--------------------------------------------------------------------------------
1 | info:
2 | version: v1.0
3 | license:
4 | name: Apache 2.0
5 | url: ''
6 | title: 'API with Common Header'
7 | paths:
8 | "/followers":
9 | post:
10 | description: ''
11 | responses:
12 | '200':
13 | content:
14 | application/json:
15 | schema:
16 | type: object
17 | properties:
18 | name:
19 | type: string
20 | fav_number:
21 | type: integer
22 | required:
23 | - name
24 | - fav_number
25 | additionalProperties: false
26 | description: succeed to register a new follwer
27 | '400':
28 | content:
29 | application/json:
30 | schema: {}
31 | description: failed to register a new follwer
32 | put:
33 | description: ''
34 | responses:
35 | '200':
36 | content:
37 | application/json:
38 | schema:
39 | type: object
40 | properties:
41 | name:
42 | type: string
43 | fav_number:
44 | type: integer
45 | required:
46 | - name
47 | - fav_number
48 | additionalProperties: false
49 | description: succeed to update a follwer
50 | '400':
51 | content:
52 | application/json:
53 | schema: {}
54 | description: failed to update a follwer
55 | "/users":
56 | post:
57 | description: ''
58 | responses:
59 | '200':
60 | content:
61 | application/json:
62 | schema: {}
63 | description: succeed to register a new user
64 | '400':
65 | content:
66 | application/json:
67 | schema: {}
68 | description: failed to register a new user
69 | get:
70 | description: ''
71 | responses:
72 | '200':
73 | content:
74 | application/json:
75 | schema: {}
76 | description: succeed to return all users info
77 | '400':
78 | content:
79 | application/json:
80 | schema: {}
81 | description: failed to return all users infon
82 |
83 | servers:
84 | - url: http://example.com
85 | - url: http://localhost
86 | openapi: 3.0.0
87 |
--------------------------------------------------------------------------------
/tests/unit/commands/bundle/resources/expected/without-header.yaml:
--------------------------------------------------------------------------------
1 | info:
2 | version: v1.0
3 | license:
4 | name: Apache 2.0
5 | title: ''
6 | paths:
7 | "/followers":
8 | post:
9 | description: ''
10 | responses:
11 | '200':
12 | content:
13 | application/json:
14 | schema:
15 | type: object
16 | properties:
17 | name:
18 | type: string
19 | fav_number:
20 | type: integer
21 | required:
22 | - name
23 | - fav_number
24 | additionalProperties: false
25 | description: succeed to register a new follwer
26 | '400':
27 | content:
28 | application/json:
29 | schema: {}
30 | description: failed to register a new follwer
31 | put:
32 | description: ''
33 | responses:
34 | '200':
35 | content:
36 | application/json:
37 | schema:
38 | type: object
39 | properties:
40 | name:
41 | type: string
42 | fav_number:
43 | type: integer
44 | required:
45 | - name
46 | - fav_number
47 | additionalProperties: false
48 | description: succeed to update a follwer
49 | '400':
50 | content:
51 | application/json:
52 | schema: {}
53 | description: failed to update a follwer
54 | "/users":
55 | post:
56 | description: ''
57 | responses:
58 | '200':
59 | content:
60 | application/json:
61 | schema: {}
62 | description: succeed to register a new user
63 | '400':
64 | content:
65 | application/json:
66 | schema: {}
67 | description: failed to register a new user
68 | get:
69 | description: ''
70 | responses:
71 | '200':
72 | content:
73 | application/json:
74 | schema: {}
75 | description: succeed to return all users info
76 | '400':
77 | content:
78 | application/json:
79 | schema: {}
80 | description: failed to return all users infon
81 |
82 | servers:
83 | - url: http://example.com
84 | openapi: 3.0.0
85 |
--------------------------------------------------------------------------------
/tests/unit/commands/bundle/resources/inputs/file1.json:
--------------------------------------------------------------------------------
1 | {
2 | "info": {
3 | "version": "v1.0",
4 | "license": {
5 | "name": "Apache 2.0"
6 | },
7 | "title": ""
8 | },
9 | "paths": {
10 | "/users": {
11 | "post": {
12 | "description": "",
13 | "responses": {
14 | "200": {
15 | "content": {
16 | "application/json": {
17 | "schema": {}
18 | }
19 | },
20 | "description": "succeed to register a new user"
21 | },
22 | "400": {
23 | "content": {
24 | "application/json": {
25 | "schema": {}
26 | }
27 | },
28 | "description": "failed to register a new user"
29 | }
30 | }
31 | },
32 | "get": {
33 | "description": "",
34 | "responses": {
35 | "200": {
36 | "content": {
37 | "application/json": {
38 | "schema": {}
39 | }
40 | },
41 | "description": "succeed to return all users info"
42 | },
43 | "400": {
44 | "content": {
45 | "application/json": {
46 | "schema": {}
47 | }
48 | },
49 | "description": "failed to return all users infon"
50 | }
51 | }
52 | }
53 | }
54 | },
55 | "servers": [
56 | {
57 | "url": "http://example.com"
58 | }
59 | ],
60 | "openapi": "3.0.0"
61 | }
62 |
--------------------------------------------------------------------------------
/tests/unit/commands/bundle/resources/inputs/file2.yaml:
--------------------------------------------------------------------------------
1 | info:
2 | version: v1.0
3 | license:
4 | name: Apache 2.0
5 | title: ''
6 | paths:
7 | /followers:
8 | post:
9 | description: ''
10 | responses:
11 | '200':
12 | content:
13 | application/json:
14 | schema:
15 | $ref: '#/components/schemas/Follower'
16 | description: succeed to register a new follwer
17 | '400':
18 | content:
19 | application/json:
20 | schema: {}
21 | description: failed to register a new follwer
22 | put:
23 | description: ''
24 | responses:
25 | '200':
26 | content:
27 | application/json:
28 | schema:
29 | $ref: '#/components/schemas/Follower'
30 | description: succeed to update a follwer
31 | '400':
32 | content:
33 | application/json:
34 | schema: {}
35 | description: failed to update a follwer
36 |
37 | servers:
38 | - url: 'http://example.com'
39 | openapi: 3.0.0
40 |
41 |
42 | components:
43 | schemas:
44 | Follower:
45 | type: object
46 | properties:
47 | name:
48 | type: string
49 | fav_number:
50 | type: integer
51 | required:
52 | - name
53 | - fav_number
54 | additionalProperties: false
--------------------------------------------------------------------------------
/tests/unit/commands/bundle/resources/inputs/template-header.yaml:
--------------------------------------------------------------------------------
1 | info:
2 | version: v1.0
3 | license:
4 | name: Apache 2.0
5 | url: ''
6 | title: API with Common Header
7 | paths: {}
8 | servers:
9 | - url: http://example.com
10 | - url: http://localhost
11 | openapi: 3.0.0
12 |
--------------------------------------------------------------------------------
/tests/unit/commands/bundle/test_bundle.py:
--------------------------------------------------------------------------------
1 | import os
2 | import yaml
3 | import unittest
4 | from src.commands.bundle import run_bundle
5 |
6 |
7 | def read_file(file_path):
8 | r = open(file_path, 'r')
9 | content = r.read()
10 | r.close()
11 | return yaml.safe_load(content)
12 |
13 |
14 | class TestBundle(unittest.TestCase):
15 |
16 | def _data_provider(self):
17 | current = os.path.dirname(os.path.abspath(__file__))
18 | return {
19 | 'spec path without header file': {
20 | 'input': {
21 | 'files': [
22 | os.path.join(current, 'resources/inputs/file1.json'),
23 | os.path.join(current, 'resources/inputs/file2.yaml'),
24 | ],
25 | 'header': None
26 | },
27 | 'expected': os.path.join(current, 'resources/expected/without-header.yaml')
28 | },
29 | 'spec path with header file': {
30 | 'input': {
31 | 'files': [
32 | os.path.join(current, 'resources/inputs/file1.json'),
33 | os.path.join(current, 'resources/inputs/file2.yaml'),
34 | ],
35 | 'header': os.path.join(current, 'resources/inputs/template-header.yaml')
36 | },
37 | 'expected': os.path.join(current, 'resources/expected/with-header.yaml')
38 | },
39 | }
40 |
41 | def test_bundle(self):
42 | for key, value in self._data_provider().items():
43 | actual = run_bundle(value['input']['files'], value['input']['header'])
44 | expected = read_file(value['expected'])
45 | self.assertEqual(actual, expected, key)
46 |
--------------------------------------------------------------------------------
/tests/unit/commands/resolve/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hakopako/openapi-cli-tool/446f428f0d0c36842bab405e7efef65a9a713b90/tests/unit/commands/resolve/__init__.py
--------------------------------------------------------------------------------
/tests/unit/commands/resolve/resources/expected/duplicated-spec.yaml:
--------------------------------------------------------------------------------
1 | - summary: Add a new pet
2 | requestBody:
3 | description: Optional description in *Markdown*
4 | required: true
5 | content:
6 | application/json:
7 | schema:
8 | type: object
9 | properties:
10 | name:
11 | type: string
12 | fav_number:
13 | type: integer
14 | required:
15 | - name
16 | - fav_number
17 | additionalProperties: false
18 | text/plain:
19 | schema:
20 | type: string
21 | responses:
22 | '201':
23 | description: Created
24 |
25 | - summary: Upload an avatar
26 | requestBody:
27 | content:
28 | image/*:
29 | schema:
30 | type: string
31 | format: binary
32 | multipart/form-data:
33 | schema:
34 | type: object
35 | properties:
36 | filename:
37 | type: array
38 | items:
39 | type: string
40 | format: binary
41 | responses:
42 | '201':
43 | description: Updated
44 |
45 |
46 |
--------------------------------------------------------------------------------
/tests/unit/commands/resolve/resources/expected/find-a-spec.yaml:
--------------------------------------------------------------------------------
1 | - summary: Add a new pet
2 | requestBody:
3 | description: Optional description in *Markdown*
4 | required: true
5 | content:
6 | application/json:
7 | schema:
8 | type: object
9 | properties:
10 | name:
11 | type: string
12 | fav_number:
13 | type: integer
14 | required:
15 | - name
16 | - fav_number
17 | additionalProperties: false
18 | text/plain:
19 | schema:
20 | type: string
21 | responses:
22 | '201':
23 | description: Created
24 |
25 |
--------------------------------------------------------------------------------
/tests/unit/commands/resolve/resources/inputs/duplicated-spec-1.yaml:
--------------------------------------------------------------------------------
1 | paths:
2 | /pets:
3 | post:
4 | summary: Add a new pet
5 | requestBody:
6 | description: Optional description in *Markdown*
7 | required: true
8 | content:
9 | application/json:
10 | schema:
11 | $ref: '#/components/schemas/Pet'
12 | text/plain:
13 | schema:
14 | type: string
15 | responses:
16 | '201':
17 | description: Created
18 |
19 | components:
20 | schemas:
21 | Pet:
22 | type: object
23 | properties:
24 | name:
25 | type: string
26 | fav_number:
27 | type: integer
28 | required:
29 | - name
30 | - fav_number
31 | additionalProperties: false
32 |
33 |
--------------------------------------------------------------------------------
/tests/unit/commands/resolve/resources/inputs/duplicated-spec-2.yaml:
--------------------------------------------------------------------------------
1 | paths:
2 | /pets:
3 | post:
4 | summary: Upload an avatar
5 | requestBody:
6 | content:
7 | image/*:
8 | schema:
9 | type: string
10 | format: binary
11 | multipart/form-data:
12 | schema:
13 | type: object
14 | properties:
15 | filename:
16 | type: array
17 | items:
18 | type: string
19 | format: binary
20 | responses:
21 | '201':
22 | description: Updated
23 |
--------------------------------------------------------------------------------
/tests/unit/commands/resolve/resources/inputs/find-a-spec.yaml:
--------------------------------------------------------------------------------
1 | paths:
2 | /pets:
3 | post:
4 | summary: Add a new pet
5 | requestBody:
6 | description: Optional description in *Markdown*
7 | required: true
8 | content:
9 | application/json:
10 | schema:
11 | $ref: '#/components/schemas/Pet'
12 | text/plain:
13 | schema:
14 | type: string
15 | responses:
16 | '201':
17 | description: Created
18 |
19 | /avatar:
20 | put:
21 | summary: Upload an avatar
22 | requestBody:
23 | content:
24 | image/*:
25 | schema:
26 | type: string
27 | format: binary
28 | multipart/form-data:
29 | schema:
30 | type: object
31 | properties:
32 | filename:
33 | type: array
34 | items:
35 | type: string
36 | format: binary
37 | responses:
38 | '201':
39 | description: Updated
40 |
41 | components:
42 | schemas:
43 | Pet:
44 | type: object
45 | properties:
46 | name:
47 | type: string
48 | fav_number:
49 | type: integer
50 | required:
51 | - name
52 | - fav_number
53 | additionalProperties: false
54 |
55 |
--------------------------------------------------------------------------------
/tests/unit/commands/resolve/test_resolve.py:
--------------------------------------------------------------------------------
1 | import os
2 | import yaml
3 | import subprocess
4 | import unittest
5 | from src.commands.resolve import run_resolve
6 |
7 |
8 | def read_file(file_path):
9 | r = open(file_path, 'r')
10 | content = r.read()
11 | r.close()
12 | return yaml.safe_load(content)
13 |
14 |
15 | class TestResolve(unittest.TestCase):
16 |
17 | def _data_provider(self):
18 | current = os.path.dirname(os.path.abspath(__file__))
19 | return {
20 | 'spec not found': {
21 | 'input': {
22 | 'method': 'get',
23 | 'path': '/cats',
24 | 'files': ['./not-exist-spec-path']
25 | },
26 | 'expected': []
27 | },
28 | 'find a spec': {
29 | 'input': {
30 | 'method': 'post',
31 | 'path': '/pets',
32 | 'files': [os.path.join(current, 'resources/inputs/find-a-spec.yaml')]
33 | },
34 | 'expected': os.path.join(current, 'resources/expected/find-a-spec.yaml')
35 | },
36 | 'duplicated spec': {
37 | 'input': {
38 | 'method': 'post',
39 | 'path': '/pets',
40 | 'files': [
41 | os.path.join(current, 'resources/inputs/duplicated-spec-1.yaml'),
42 | os.path.join(current, 'resources/inputs/duplicated-spec-2.yaml')
43 | ]
44 | },
45 | 'expected': os.path.join(current, 'resources/expected/duplicated-spec.yaml')
46 | }
47 | }
48 |
49 | def test_resolve(self):
50 | for key, value in self._data_provider().items():
51 | actual = run_resolve(
52 | value['input']['method'],
53 | value['input']['path'],
54 | value['input']['files']
55 | )
56 | if isinstance(value['expected'], str) and os.path.isfile(value['expected']):
57 | expected = read_file(value['expected'])
58 | self.assertEqual(actual, expected, key)
59 | else:
60 | self.assertEqual(actual, value['expected'], key)
61 |
--------------------------------------------------------------------------------
/tests/unit/commands/scaffold/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hakopako/openapi-cli-tool/446f428f0d0c36842bab405e7efef65a9a713b90/tests/unit/commands/scaffold/__init__.py
--------------------------------------------------------------------------------
/tests/unit/commands/scaffold/resources/expected-default-output.json:
--------------------------------------------------------------------------------
1 | {
2 | "info": {
3 | "version": "v1.0",
4 | "license": {
5 | "name": "Apache 2.0"
6 | },
7 | "title": ""
8 | },
9 | "paths": {
10 | "/users": {
11 | "get": {
12 | "description": "",
13 | "responses": {
14 | "200": {
15 | "content": {
16 | "application/json": {
17 | "schema": {}
18 | }
19 | },
20 | "description": "succeed to return all users info"
21 | }
22 | }
23 | }
24 | }
25 | },
26 | "servers": [
27 | {
28 | "url": "http://example.com"
29 | }
30 | ],
31 | "openapi": "3.0.0"
32 | }
33 |
--------------------------------------------------------------------------------
/tests/unit/commands/scaffold/resources/expected-multiple-response-output.json:
--------------------------------------------------------------------------------
1 | {
2 | "info": {
3 | "version": "v1.0",
4 | "license": {
5 | "name": "Apache 2.0"
6 | },
7 | "title": ""
8 | },
9 | "paths": {
10 | "/users": {
11 | "post": {
12 | "description": "",
13 | "responses": {
14 | "200": {
15 | "content": {
16 | "application/json": {
17 | "schema": {}
18 | }
19 | },
20 | "description": "succeed to register a new user"
21 | },
22 | "400": {
23 | "content": {
24 | "application/json": {
25 | "schema": {}
26 | }
27 | },
28 | "description": "failed to register a new user"
29 | }
30 | }
31 | },
32 | "get": {
33 | "description": "",
34 | "responses": {
35 | "200": {
36 | "content": {
37 | "application/json": {
38 | "schema": {}
39 | }
40 | },
41 | "description": "succeed to return all users info"
42 | },
43 | "400": {
44 | "content": {
45 | "application/json": {
46 | "schema": {}
47 | }
48 | },
49 | "description": "failed to return all users infon"
50 | }
51 | }
52 | }
53 | }
54 | },
55 | "servers": [
56 | {
57 | "url": "http://example.com"
58 | }
59 | ],
60 | "openapi": "3.0.0"
61 | }
62 |
--------------------------------------------------------------------------------
/tests/unit/commands/scaffold/test_scaffold.py:
--------------------------------------------------------------------------------
1 | import os
2 | import unittest
3 | import sys
4 | import json
5 |
6 | try:
7 | from StringIO import StringIO
8 | except ImportError:
9 | from io import StringIO
10 | from src.commands.scaffold import run_scaffold
11 |
12 |
13 | def read_file(file_path):
14 | r = open(file_path, 'r')
15 | content = r.read()
16 | r.close()
17 | return json.loads(content)
18 |
19 |
20 | class TestScaffold(unittest.TestCase):
21 |
22 | def _data_provider(self):
23 | current = os.path.dirname(os.path.abspath(__file__))
24 | return {
25 | 'default input': {
26 | 'input':
27 | "\n" + # Please enter title [""]:
28 | "\n" + # Please enter version [v1.0]:
29 | "\n" + # Please enter license [Apache 2.0]:
30 | "\n" + # Please enter server url [http://example.com]:
31 | "/users\n" + # Please enter path [/]:
32 | "get\n" + # Please enter method for /users [get|post|put|delete|head|option|trace]
33 | "\n" + # Please enter description for get /users [""]:
34 | "\n" + # Please enter response code for get /users [200]:
35 | "succeed to return all users info\n" + # Please enter response description for 200:
36 | "application/json\n" + # Please enter response content-type for get /users [application/json]:
37 | "\n" + # Add more response for get /users ? Y/n [n]:
38 | "\n", # Add more request method for /users ? Y/n [n]:
39 | 'expected': os.path.join(current, 'resources/expected-default-output.json')
40 | },
41 | 'multiple response input': {
42 | 'input':
43 | "\n" + # Please enter title [""]:
44 | "\n" + # Please enter version [v1.0]:
45 | "\n" + # Please enter license [Apache 2.0]:
46 | "\n" + # Please enter server url [http://example.com]:
47 | "/users\n" + # Please enter path [/]:
48 | "get\n" + # Please enter method for /users [get|post|put|delete|head|option|trace]
49 | "\n" + # Please enter description for get /users [""]:
50 | "\n" + # Please enter response code for get /users [200]:
51 | "succeed to return all users info\n" + # Please enter response description for 200:
52 | "application/json\n" + # Please enter response content-type for get /users [application/json]:
53 | "Y\n" + # Add more response for get /users ? Y/n [n]:
54 | "400\n" + # Please enter response code for get /users [200]:
55 | "failed to return all users infon\n" + # Please enter response description for 200:
56 | "application/json\n" + # Please enter response content-type for get /users [application/json]:
57 | "\n" + # Add more response for get /users ? Y/n [n]:
58 | "Y\n" + # Add more request method for /users ? Y/n [n]:
59 | "post\n" + # Please enter method for /users [get|post|put|delete|head|option|trace]
60 | "\n" + # Please enter description for post /users [""]:
61 | "\n" + # Please enter response code for post /users [200]:
62 | "succeed to register a new user\n" + # Please enter response description for 200:
63 | "application/json\n" + # Please enter response content-type for post /users [application/json]:
64 | "Y\n" + # Add more response for %s %s ? Y/n [n]:
65 | "400\n" + # Please enter response code for get /users [200]:
66 | "failed to register a new user\n" + # Please enter response description for 200:
67 | "application/json\n" + # Please enter response content-type for post /users [application/json]:
68 | "\n" + # Add more response for post /users ? Y/n [n]:
69 | "\n", # Add more request method for /users ? Y/n [n]:
70 | 'expected': os.path.join(current, 'resources/expected-multiple-response-output.json')
71 | }
72 | }
73 |
74 | def test_scaffold(self):
75 | original_sysin = sys.stdin
76 | oritinal_sysout = sys.stdout
77 | for key, value in self._data_provider().items():
78 | sys.stdin = StringIO(value['input'])
79 | sys.stdout = StringIO()
80 | actual = run_scaffold()
81 | expected = read_file(value['expected'])
82 | self.assertEqual(actual, expected, key)
83 | sys.stdin = original_sysin
84 | sys.stdout = oritinal_sysout
85 |
--------------------------------------------------------------------------------
/tests/unit/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hakopako/openapi-cli-tool/446f428f0d0c36842bab405e7efef65a9a713b90/tests/unit/utils/__init__.py
--------------------------------------------------------------------------------
/tests/unit/utils/file_control/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hakopako/openapi-cli-tool/446f428f0d0c36842bab405e7efef65a9a713b90/tests/unit/utils/file_control/__init__.py
--------------------------------------------------------------------------------
/tests/unit/utils/file_control/resources/file.json:
--------------------------------------------------------------------------------
1 | {"message": "ok"}
--------------------------------------------------------------------------------
/tests/unit/utils/file_control/resources/file.txt:
--------------------------------------------------------------------------------
1 | this file should not be imported.
--------------------------------------------------------------------------------
/tests/unit/utils/file_control/resources/file.yaml:
--------------------------------------------------------------------------------
1 | message: ok
2 |
--------------------------------------------------------------------------------
/tests/unit/utils/file_control/resources/file.yml:
--------------------------------------------------------------------------------
1 | message: ok
--------------------------------------------------------------------------------
/tests/unit/utils/file_control/test_file_control.py:
--------------------------------------------------------------------------------
1 | import os
2 | import unittest
3 | from src.utils.file_control import FileControl
4 |
5 |
6 | class TestFileControl(unittest.TestCase):
7 |
8 | def _success_data_provider(self):
9 | current = os.path.dirname(os.path.abspath(__file__))
10 | return {
11 | 'input yaml extension': {
12 | 'input': os.path.join(current, 'resources/file.yaml'),
13 | 'expected': {'message': 'ok'}
14 | },
15 | 'input yml extension': {
16 | 'input': os.path.join(current, 'resources/file.yml'),
17 | 'expected': {'message': 'ok'}
18 | },
19 | 'input json extension': {
20 | 'input': os.path.join(current, 'resources/file.json'),
21 | 'expected': {'message': 'ok'}
22 | }
23 | }
24 |
25 | def _exception_data_provider(self):
26 | current = os.path.dirname(os.path.abspath(__file__))
27 | return {
28 | 'file not found': {
29 | 'input': './not-exist-spec-path'
30 | },
31 | 'input txt extension': {
32 | 'input': os.path.join(current, 'resources/file.txt')
33 | }
34 | }
35 |
36 | def test_success_file_control(self):
37 | for key, value in self._success_data_provider().items():
38 | file_control = FileControl()
39 | actual = file_control.load_dict_from_file(value['input'])
40 | self.assertEqual(actual, value['expected'], key)
41 |
42 | @unittest.skip("need to be fixed")
43 | def test_exception_file_control(self):
44 | for key, value in self._exception_data_provider().items():
45 | file_control = FileControl()
46 | self.assertRaises(Exception, file_control.load_dict_from_file(value['input']))
47 |
--------------------------------------------------------------------------------
/tests/unit/utils/repository/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hakopako/openapi-cli-tool/446f428f0d0c36842bab405e7efef65a9a713b90/tests/unit/utils/repository/__init__.py
--------------------------------------------------------------------------------
/tests/unit/utils/repository/resources/multi-file/file1.json:
--------------------------------------------------------------------------------
1 | {
2 | "info": {
3 | "version": "v1.0",
4 | "license": {
5 | "name": "Apache 2.0"
6 | },
7 | "title": ""
8 | },
9 | "paths": {
10 | "/users": {
11 | "post": {
12 | "description": "",
13 | "responses": {
14 | "200": {
15 | "content": {
16 | "application/json": {
17 | "schema": {}
18 | }
19 | },
20 | "description": "succeed to register a new user"
21 | },
22 | "400": {
23 | "content": {
24 | "application/json": {
25 | "schema": {}
26 | }
27 | },
28 | "description": "failed to register a new user"
29 | }
30 | }
31 | },
32 | "get": {
33 | "description": "",
34 | "responses": {
35 | "200": {
36 | "content": {
37 | "application/json": {
38 | "schema": {}
39 | }
40 | },
41 | "description": "succeed to return all users info"
42 | },
43 | "400": {
44 | "content": {
45 | "application/json": {
46 | "schema": {}
47 | }
48 | },
49 | "description": "failed to return all users infon"
50 | }
51 | }
52 | }
53 | },
54 | "/posts": {
55 | "post": {
56 | "description": "",
57 | "responses": {
58 | "200": {
59 | "content": {
60 | "application/json": {
61 | "schema": {}
62 | }
63 | },
64 | "description": "succeed to post a new content"
65 | },
66 | "400": {
67 | "content": {
68 | "application/json": {
69 | "schema": {}
70 | }
71 | },
72 | "description": "failed to post a new content"
73 | }
74 | }
75 | },
76 | "get": {
77 | "description": "",
78 | "responses": {
79 | "200": {
80 | "content": {
81 | "application/json": {
82 | "schema": {}
83 | }
84 | },
85 | "description": "succeed to return all post info"
86 | },
87 | "400": {
88 | "content": {
89 | "application/json": {
90 | "schema": {}
91 | }
92 | },
93 | "description": "failed to return all post infon"
94 | }
95 | }
96 | }
97 | }
98 | },
99 | "servers": [
100 | {
101 | "url": "http://example.com"
102 | }
103 | ],
104 | "openapi": "3.0.0"
105 | }
106 |
--------------------------------------------------------------------------------
/tests/unit/utils/repository/resources/multi-file/file2.yaml:
--------------------------------------------------------------------------------
1 | info:
2 | version: v1.0
3 | license:
4 | name: Apache 2.0
5 | title: ''
6 | paths:
7 | /follwers:
8 | post:
9 | description: ''
10 | responses:
11 | '200':
12 | content:
13 | application/json:
14 | schema: {}
15 | description: succeed to register a new follwer
16 | '400':
17 | content:
18 | application/json:
19 | schema: {}
20 | description: failed to register a new follwer
21 | put:
22 | description: ''
23 | responses:
24 | '200':
25 | content:
26 | application/json:
27 | schema: {}
28 | description: succeed to update a follwer
29 | '400':
30 | content:
31 | application/json:
32 | schema: {}
33 | description: failed to update a follwer
34 | get:
35 | description: ''
36 | responses:
37 | '200':
38 | content:
39 | application/json:
40 | schema: {}
41 | description: succeed to return all follwers info
42 | '400':
43 | content:
44 | application/json:
45 | schema: {}
46 | description: failed to return all follwers infon
47 |
48 | servers:
49 | - url: 'http://example.com'
50 | openapi: 3.0.0
51 |
--------------------------------------------------------------------------------
/tests/unit/utils/repository/resources/sample.yaml:
--------------------------------------------------------------------------------
1 | openapi: '3.0.0'
2 |
3 | info:
4 | title: 'requestBody'
5 | description: 'https://swagger.io/docs/specification/describing-request-body/'
6 | version: 1.0.0
7 |
8 | servers:
9 | - url: 'http://localhost/'
10 | description: 'Test server'
11 |
12 | paths:
13 | /pets:
14 | post:
15 | summary: Add a new pet
16 | requestBody:
17 | description: Optional description in *Markdown*
18 | required: true
19 | content:
20 | application/json:
21 | schema:
22 | $ref: '#/components/schemas/Pet'
23 | application/xml:
24 | schema:
25 | $ref: '#/components/schemas/Pet'
26 | application/x-www-form-urlencoded:
27 | schema:
28 | $ref: '#/components/schemas/PetForm'
29 | text/plain:
30 | schema:
31 | type: string
32 | responses:
33 | '201':
34 | description: Created
35 |
36 | /avatar:
37 | put:
38 | summary: Upload an avatar
39 | requestBody:
40 | content:
41 | image/*: # Can be image/png, image/svg, image/gif, etc.
42 | schema:
43 | type: string
44 | format: binary
45 | multipart/form-data:
46 | schema:
47 | type: object
48 | properties:
49 | filename:
50 | type: array
51 | items:
52 | type: string
53 | format: binary
54 | responses:
55 | '201':
56 | description: Updated
57 |
58 | components:
59 | schemas:
60 | Pet:
61 | type: object
62 | properties:
63 | name:
64 | type: string
65 | fav_number:
66 | type: integer
67 | required:
68 | - name
69 | - fav_number
70 | additionalProperties: false
71 | PetForm:
72 | type: object
73 | properties:
74 | name:
75 | type: string
76 | fav_number:
77 | type: string
78 | required:
79 | - name
80 | - fav_number
81 | additionalProperties: false
82 |
--------------------------------------------------------------------------------
/tests/unit/utils/repository/resources/with-path-ref.yaml:
--------------------------------------------------------------------------------
1 | openapi: '3.0.0'
2 |
3 | info:
4 | title: 'requestBody'
5 | description: 'https://swagger.io/docs/specification/describing-request-body/'
6 | version: 1.0.0
7 |
8 | servers:
9 | - url: 'http://localhost/'
10 | description: 'Test server'
11 |
12 | paths:
13 | /with-path-referred:
14 | $ref: './with-path-referred.yaml'
15 | description: 'this field should be ignored.'
16 |
17 |
--------------------------------------------------------------------------------
/tests/unit/utils/repository/resources/with-path-referred.yaml:
--------------------------------------------------------------------------------
1 | post:
2 | summary: Add a new dog
3 | requestBody:
4 | description: Optional description in *Markdown*
5 | required: true
6 | content:
7 | application/json:
8 | schema:
9 | type: string
10 | responses:
11 | '201':
12 | description: Created
13 | get:
14 | summary: get dogs
15 | responses:
16 | '200':
17 | description: Success
--------------------------------------------------------------------------------
/tests/unit/utils/repository/test_repository.py:
--------------------------------------------------------------------------------
1 | import os
2 | import unittest
3 | import subprocess
4 | from src.utils.repository import Repository
5 |
6 | class TestRepository(unittest.TestCase):
7 |
8 | def _data_provider(self):
9 | current = os.path.dirname(os.path.abspath(__file__))
10 | return {
11 | 'spec not found': {
12 | 'input': ['./not-exist-spec-path'],
13 | 'expected': []
14 | },
15 | 'input multi file': {
16 | 'input': [
17 | os.path.join(current, 'resources/multi-file/file1.json'),
18 | os.path.join(current, 'resources/multi-file/file2.yaml')
19 | ],
20 | 'expected': [
21 | ['GET', '/follwers', os.path.join(current, 'resources/multi-file/file2.yaml')],
22 | ['POST', '/follwers', os.path.join(current, 'resources/multi-file/file2.yaml')],
23 | ['PUT', '/follwers', os.path.join(current, 'resources/multi-file/file2.yaml')],
24 | ['GET', '/posts', os.path.join(current, 'resources/multi-file/file1.json')],
25 | ['POST', '/posts', os.path.join(current, 'resources/multi-file/file1.json')],
26 | ['GET', '/users', os.path.join(current, 'resources/multi-file/file1.json')],
27 | ['POST', '/users', os.path.join(current, 'resources/multi-file/file1.json')]
28 | ]
29 | },
30 | 'input a file path': {
31 | 'input': [os.path.join(current, 'resources/sample.yaml')],
32 | 'expected': [
33 | ['PUT', '/avatar', os.path.join(current, 'resources/sample.yaml')],
34 | ['POST', '/pets', os.path.join(current, 'resources/sample.yaml')]
35 | ]
36 | },
37 | 'input path ref': {
38 | 'input': [os.path.join(current, 'resources/with-path-ref.yaml')],
39 | 'expected': [
40 | ['GET', '/with-path-referred', os.path.join(current, 'resources/with-path-ref.yaml')],
41 | ['POST', '/with-path-referred', os.path.join(current, 'resources/with-path-ref.yaml')]
42 | ]
43 | }
44 | }
45 |
46 |
47 | def test_success_repository(self):
48 | for key, value in self._data_provider().items():
49 | repository = Repository(value['input'])
50 | actual = repository.routes
51 |
52 | self.assertEqual(actual.to_list(), value['expected'], key)
53 |
--------------------------------------------------------------------------------
/tests/unit/utils/resolver/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hakopako/openapi-cli-tool/446f428f0d0c36842bab405e7efef65a9a713b90/tests/unit/utils/resolver/__init__.py
--------------------------------------------------------------------------------
/tests/unit/utils/resolver/resources/expected/escape_characters.yaml:
--------------------------------------------------------------------------------
1 | application/json:
2 | schema:
3 | type: object
4 | properties:
5 | name:
6 | type: string
7 | fav_number:
8 | type: integer
9 | required:
10 | - name
11 | - fav_number
12 | additionalProperties: false
13 | text/plain:
14 | schema:
15 | type: string
16 |
17 | components:
18 | schemas:
19 | /blogs/{blog_id}/new~posts:
20 | type: object
21 | properties:
22 | name:
23 | type: string
24 | fav_number:
25 | type: integer
26 | required:
27 | - name
28 | - fav_number
29 | additionalProperties: false
30 |
--------------------------------------------------------------------------------
/tests/unit/utils/resolver/resources/expected/local_components.yaml:
--------------------------------------------------------------------------------
1 |
2 | application/json:
3 | schema:
4 | type: object
5 | properties:
6 | name:
7 | type: string
8 | fav_number:
9 | type: integer
10 | required:
11 | - name
12 | - fav_number
13 | additionalProperties: false
14 | application/x-www-form-urlencoded:
15 | schema:
16 | type: object
17 | properties:
18 | name:
19 | type: string
20 | fav_number:
21 | type: string
22 | required:
23 | - name
24 | - fav_number
25 | additionalProperties: false
26 | text/plain:
27 | schema:
28 | type: string
29 |
30 | components:
31 | schemas:
32 | Pet:
33 | type: object
34 | properties:
35 | name:
36 | type: string
37 | fav_number:
38 | type: integer
39 | required:
40 | - name
41 | - fav_number
42 | additionalProperties: false
43 | PetForm:
44 | type: object
45 | properties:
46 | name:
47 | type: string
48 | fav_number:
49 | type: string
50 | required:
51 | - name
52 | - fav_number
53 | additionalProperties: false
--------------------------------------------------------------------------------
/tests/unit/utils/resolver/resources/expected/nested_reference.yaml:
--------------------------------------------------------------------------------
1 | application/json:
2 | schema:
3 | type: object
4 | properties:
5 | name:
6 | type: string
7 | fav_number:
8 | type: string
9 | required:
10 | - name
11 | - fav_number
12 | additionalProperties: false
13 | application/x-www-form-urlencoded:
14 | schema:
15 | type: object
16 | properties:
17 | name:
18 | type: string
19 | fav_number:
20 | type: string
21 | required:
22 | - name
23 | - fav_number
24 | additionalProperties: false
25 | text/plain:
26 | schema:
27 | type: string
28 |
29 | components:
30 | schemas:
31 | Pet:
32 | type: object
33 | properties:
34 | name:
35 | type: string
36 | fav_number:
37 | type: string
38 | required:
39 | - name
40 | - fav_number
41 | additionalProperties: false
42 | PetForm:
43 | type: object
44 | properties:
45 | name:
46 | type: string
47 | fav_number:
48 | type: string
49 | required:
50 | - name
51 | - fav_number
52 | additionalProperties: false
53 |
--------------------------------------------------------------------------------
/tests/unit/utils/resolver/resources/expected/remote_components.yaml:
--------------------------------------------------------------------------------
1 | application/json:
2 | schema:
3 | type: object
4 | properties:
5 | name:
6 | type: string
7 | fav_number:
8 | type: integer
9 | required:
10 | - name
11 | - fav_number
12 | additionalProperties: false
13 | text/plain:
14 | schema:
15 | type: string
--------------------------------------------------------------------------------
/tests/unit/utils/resolver/resources/expected/remote_file.yaml:
--------------------------------------------------------------------------------
1 | application/json:
2 | schema:
3 | type: object
4 | properties:
5 | name:
6 | type: string
7 | fav_number:
8 | type: integer
9 | required:
10 | - name
11 | - fav_number
12 | additionalProperties: false
13 | text/plain:
14 | schema:
15 | type: string
--------------------------------------------------------------------------------
/tests/unit/utils/resolver/resources/inputs/escape_characters.yaml:
--------------------------------------------------------------------------------
1 | application/json:
2 | schema:
3 | $ref: '#/components/schemas/~1blogs~1{blog_id}~1new~0posts'
4 | text/plain:
5 | schema:
6 | type: string
7 |
8 | components:
9 | schemas:
10 | /blogs/{blog_id}/new~posts:
11 | type: object
12 | properties:
13 | name:
14 | type: string
15 | fav_number:
16 | type: integer
17 | required:
18 | - name
19 | - fav_number
20 | additionalProperties: false
21 |
--------------------------------------------------------------------------------
/tests/unit/utils/resolver/resources/inputs/local_components.yaml:
--------------------------------------------------------------------------------
1 |
2 | application/json:
3 | schema:
4 | $ref: '#/components/schemas/Pet'
5 | application/x-www-form-urlencoded:
6 | schema:
7 | $ref: '#/components/schemas/PetForm'
8 | text/plain:
9 | schema:
10 | type: string
11 |
12 | components:
13 | schemas:
14 | Pet:
15 | type: object
16 | properties:
17 | name:
18 | type: string
19 | fav_number:
20 | type: integer
21 | required:
22 | - name
23 | - fav_number
24 | additionalProperties: false
25 | PetForm:
26 | type: object
27 | properties:
28 | name:
29 | type: string
30 | fav_number:
31 | type: string
32 | required:
33 | - name
34 | - fav_number
35 | additionalProperties: false
36 |
--------------------------------------------------------------------------------
/tests/unit/utils/resolver/resources/inputs/nested_reference.yaml:
--------------------------------------------------------------------------------
1 | application/json:
2 | schema:
3 | $ref: '#/components/schemas/Pet'
4 | application/x-www-form-urlencoded:
5 | schema:
6 | $ref: '#/components/schemas/PetForm'
7 | text/plain:
8 | schema:
9 | type: string
10 |
11 | components:
12 | schemas:
13 | Pet:
14 | $ref : '#/components/schemas/PetForm'
15 | PetForm:
16 | type: object
17 | properties:
18 | name:
19 | type: string
20 | fav_number:
21 | type: string
22 | required:
23 | - name
24 | - fav_number
25 | additionalProperties: false
26 |
--------------------------------------------------------------------------------
/tests/unit/utils/resolver/resources/inputs/remote_components.yaml:
--------------------------------------------------------------------------------
1 | application/json:
2 | schema:
3 | $ref: './remote_components_referred.yaml#/components/schemas/Pet'
4 | text/plain:
5 | schema:
6 | type: string
7 |
--------------------------------------------------------------------------------
/tests/unit/utils/resolver/resources/inputs/remote_components_referred.yaml:
--------------------------------------------------------------------------------
1 | components:
2 | schemas:
3 | Pet:
4 | type: object
5 | properties:
6 | name:
7 | type: string
8 | fav_number:
9 | type: integer
10 | required:
11 | - name
12 | - fav_number
13 | additionalProperties: false
--------------------------------------------------------------------------------
/tests/unit/utils/resolver/resources/inputs/remote_file.yaml:
--------------------------------------------------------------------------------
1 | application/json:
2 | schema:
3 | $ref: './remote_file_referred.yaml'
4 | text/plain:
5 | schema:
6 | type: string
7 |
--------------------------------------------------------------------------------
/tests/unit/utils/resolver/resources/inputs/remote_file_referred.yaml:
--------------------------------------------------------------------------------
1 | type: object
2 | properties:
3 | name:
4 | type: string
5 | fav_number:
6 | type: integer
7 | required:
8 | - name
9 | - fav_number
10 | additionalProperties: false
--------------------------------------------------------------------------------
/tests/unit/utils/resolver/test_resolver_resolve.py:
--------------------------------------------------------------------------------
1 | import os
2 | import yaml
3 | import unittest
4 | from src.utils.resolver import resolver
5 |
6 |
7 | class TestResolverResolve(unittest.TestCase):
8 |
9 | def read_file(self, file_path):
10 | r = open(file_path, 'r')
11 | content = r.read()
12 | r.close()
13 | return yaml.safe_load(content)
14 |
15 | def _success_data_provider(self):
16 | current = os.path.dirname(os.path.abspath(__file__))
17 | return {
18 | 'escape characters': {
19 | 'input': os.path.join(current, 'resources/inputs/escape_characters.yaml'),
20 | 'expected': os.path.join(current, 'resources/expected/escape_characters.yaml')
21 | },
22 | 'nested reference': {
23 | 'input': os.path.join(current, 'resources/inputs/nested_reference.yaml'),
24 | 'expected': os.path.join(current, 'resources/expected/nested_reference.yaml')
25 | },
26 | 'local components': {
27 | 'input': os.path.join(current, 'resources/inputs/local_components.yaml'),
28 | 'expected': os.path.join(current, 'resources/expected/local_components.yaml')
29 | },
30 | 'remote components': {
31 | 'input': os.path.join(current, 'resources/inputs/remote_components.yaml'),
32 | 'expected': os.path.join(current, 'resources/expected/remote_components.yaml')
33 | },
34 | 'remote file': {
35 | 'input': os.path.join(current, 'resources/inputs/remote_file.yaml'),
36 | 'expected': os.path.join(current, 'resources/expected/remote_file.yaml')
37 | },
38 |
39 | # 'url file': {
40 | # 'input': os.path.join(current, 'resources/file.txt'),
41 | # 'expected': ''
42 | # },
43 | # 'url components': {
44 | # 'input': os.path.join(current, 'resources/file.txt'),
45 | # 'expected': ''
46 | # }
47 | }
48 |
49 | def _exception_data_provider(self):
50 | current = os.path.dirname(os.path.abspath(__file__))
51 | return {
52 | 'ref not found': {
53 | 'input': os.path.join(current, 'resources/file.yaml')
54 | }
55 | }
56 |
57 | def test_success_resolve(self):
58 | for key, value in self._success_data_provider().items():
59 | input_spec = self.read_file(value['input'])
60 | actual = resolver(value['input'], input_spec)
61 | expected = self.read_file(value['expected'])
62 | self.assertEqual(actual, expected, key)
63 |
64 |
65 | @unittest.skip("need to be fixed")
66 | def test_exception_resolve(self):
67 | for key, value in self._exception_data_provider().items():
68 | self.assertRaises(Exception, resolver(value['input']))
69 |
--------------------------------------------------------------------------------