-
Notifications
You must be signed in to change notification settings - Fork 77
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #22 from stephenfin/add-openapi-version-3-support
OpenAPI 3.0.0 support
- Loading branch information
Showing
12 changed files
with
996 additions
and
235 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
""" | ||
sphinxcontrib.openapi | ||
--------------------- | ||
The OpenAPI spec renderer for Sphinx. It's a new way to document your | ||
RESTful API. Based on ``sphinxcontrib-httpdomain``. | ||
:copyright: (c) 2016, Ihor Kalnytskyi. | ||
:license: BSD, see LICENSE for details. | ||
""" | ||
|
||
from __future__ import unicode_literals | ||
|
||
from pkg_resources import get_distribution, DistributionNotFound | ||
|
||
from sphinxcontrib.openapi import directive | ||
|
||
try: | ||
__version__ = get_distribution(__name__).version | ||
except DistributionNotFound: | ||
# package is not installed | ||
__version__ = None | ||
|
||
|
||
def setup(app): | ||
app.setup_extension('sphinxcontrib.httpdomain') | ||
app.add_directive('openapi', directive.OpenApi) | ||
|
||
return {'version': __version__, 'parallel_read_safe': True} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
""" | ||
sphinxcontrib.openapi.directive | ||
------------------------------- | ||
The main directive for the extension. | ||
:copyright: (c) 2016, Ihor Kalnytskyi. | ||
:license: BSD, see LICENSE for details. | ||
""" | ||
|
||
from __future__ import unicode_literals | ||
|
||
import collections | ||
import io | ||
|
||
from docutils import nodes | ||
from docutils.parsers.rst import Directive, directives | ||
from docutils.statemachine import ViewList | ||
import yaml | ||
|
||
from sphinx.util.nodes import nested_parse_with_titles | ||
|
||
from sphinxcontrib.openapi import openapi20 | ||
from sphinxcontrib.openapi import openapi30 | ||
|
||
|
||
# Dictionaries do not guarantee to preserve the keys order so when we load | ||
# JSON or YAML - we may loose the order. In most cases it's not important | ||
# because we're interested in data. However, in case of OpenAPI spec it'd | ||
# be really nice to preserve them since, for example, endpoints may be | ||
# grouped logically and that improved readability. | ||
class _YamlOrderedLoader(yaml.SafeLoader): | ||
pass | ||
|
||
|
||
_YamlOrderedLoader.add_constructor( | ||
yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, | ||
lambda loader, node: collections.OrderedDict(loader.construct_pairs(node)) | ||
) | ||
|
||
|
||
class OpenApi(Directive): | ||
|
||
required_arguments = 1 # path to openapi spec | ||
final_argument_whitespace = True # path may contain whitespaces | ||
option_spec = { | ||
'encoding': directives.encoding, # useful for non-ascii cases :) | ||
'paths': lambda s: s.split(), # endpoints to be rendered | ||
} | ||
|
||
def run(self): | ||
env = self.state.document.settings.env | ||
relpath, abspath = env.relfn2path(directives.path(self.arguments[0])) | ||
|
||
# Add OpenAPI spec as a dependency to the current document. That means | ||
# the document will be rebuilt if the spec is changed. | ||
env.note_dependency(relpath) | ||
|
||
# Read the spec using encoding passed to the directive or fallback to | ||
# the one specified in Sphinx's config. | ||
encoding = self.options.get('encoding', env.config.source_encoding) | ||
with io.open(abspath, 'rt', encoding=encoding) as stream: | ||
spec = yaml.load(stream, _YamlOrderedLoader) | ||
|
||
# URI parameter is crucial for resolving relative references. So | ||
# we need to set this option properly as it's used later down the | ||
# stack. | ||
self.options.setdefault('uri', 'file://%s' % abspath) | ||
|
||
# We support both OpenAPI 2.0 (f.k.a. Swagger) and OpenAPI 3.0.0, so | ||
# determine which version we are parsing here. | ||
spec_version = spec.get('openapi', spec.get('swagger', '2.0')) | ||
if spec_version.startswith('2.'): | ||
openapihttpdomain = openapi20.openapihttpdomain | ||
elif spec_version.startswith('3.'): | ||
openapihttpdomain = openapi30.openapihttpdomain | ||
else: | ||
raise ValueError('Unsupported OpenAPI version (%s)' % spec_version) | ||
|
||
# reStructuredText DOM manipulation is pretty tricky task. It requires | ||
# passing dozen arguments which is not easy without well-documented | ||
# internals. So the idea here is to represent OpenAPI spec as | ||
# reStructuredText in-memory text and parse it in order to produce a | ||
# real DOM. | ||
viewlist = ViewList() | ||
for line in openapihttpdomain(spec, **self.options): | ||
viewlist.append(line, '<openapi>') | ||
|
||
# Parse reStructuredText contained in `viewlist` and return produced | ||
# DOM nodes. | ||
node = nodes.section() | ||
node.document = self.state.document | ||
nested_parse_with_titles(self.state, viewlist, node) | ||
return node.children |
Oops, something went wrong.