Skip to content

Commit

Permalink
Merge pull request #56 from tuffnatty/include_exclude
Browse files Browse the repository at this point in the history
Add include and exclude options (reworked #16)
  • Loading branch information
ikalnytskyi committed Oct 5, 2019
2 parents 58774c4 + 2482aae commit 9306435
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 4 deletions.
30 changes: 30 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,36 @@ The ``openapi`` directive supports the following options:
The format of text in the spec, either ``rst`` or ``markdown``. If
not supplied, ReStructured Text is assumed.

``include``
A line separated list of regular expressions to filter the included openapi
spec by. For example:

.. code:: restructuredtext
.. openapi:: specs/openapi.yml
:include:
/evid.*
:encoding: utf-8
Would render the endpoints at ``/evidence`` and ``/evidence/{pk}``

``exclude``
A line separated list of regular expressions to filter the included openapi
spec by (excluding matches). For example:

.. code:: restructuredtext
.. openapi:: specs/openapi.yml
:exclude:
/evidence/{pk}
:encoding: utf-8
Would render ``/persons`` and ``/evidence`` endpoints, but not
``/evidence/{pk}`` endpoints

``exclude``, ``include`` and ``paths`` can also be used together (``exclude``
taking precedence over ``include`` and ``paths``)


.. _Sphinx: https://www.sphinx-doc.org/en/master/
.. _OpenAPI: https://github.com/OAI/OpenAPI-Specification
Expand Down
2 changes: 2 additions & 0 deletions sphinxcontrib/openapi/directive.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ class OpenApi(Directive):
option_spec = {
'encoding': directives.encoding, # useful for non-ascii cases :)
'paths': lambda s: s.split(), # endpoints to be rendered
'include': lambda s: s.split(), # endpoints to be included (regexp)
'exclude': lambda s: s.split(), # endpoints to be excluded (regexp)
'request': directives.flag, # print the request body structure
'examples': directives.flag, # render examples when passed
'group': directives.flag, # group paths by tag when passed
Expand Down
31 changes: 29 additions & 2 deletions sphinxcontrib/openapi/openapi20.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from __future__ import unicode_literals

import itertools
import re

from sphinxcontrib.openapi import utils

Expand Down Expand Up @@ -182,6 +183,9 @@ def openapihttpdomain(spec, **options):
# spec to have only one (expected) schema, i.e. normalize it.
utils.normalize_spec(spec, **options)

# Paths list to be processed
paths = []

# If 'paths' are passed we've got to ensure they exist within an OpenAPI
# spec; otherwise raise error and ask user to fix that.
if 'paths' in options:
Expand All @@ -191,8 +195,31 @@ def openapihttpdomain(spec, **options):
', '.join(set(options['paths']) - set(spec['paths'])),
)
)

for endpoint in options.get('paths', spec['paths']):
paths = options['paths']

# Check against regular expressions to be included
if 'include' in options:
for i in options['include']:
ir = re.compile(i)
for path in spec['paths']:
if ir.match(path):
paths.append(path)

# If no include nor paths option, then take full path
if 'include' not in options and 'paths' not in options:
paths = spec['paths']

# Remove paths matching regexp
if 'exclude' in options:
_paths = []
for e in options['exclude']:
er = re.compile(e)
for path in paths:
if not er.match(path):
_paths.append(path)
paths = _paths

for endpoint in paths:
for method, properties in spec['paths'][endpoint].items():
generators.append(_httpresource(
endpoint,
Expand Down
31 changes: 29 additions & 2 deletions sphinxcontrib/openapi/openapi30.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from datetime import datetime
import itertools
import json
import re

import six
from sphinx.util import logging
Expand Down Expand Up @@ -353,6 +354,9 @@ def openapihttpdomain(spec, **options):
# spec to have only one (expected) schema, i.e. normalize it.
utils.normalize_spec(spec, **options)

# Paths list to be processed
paths = []

# If 'paths' are passed we've got to ensure they exist within an OpenAPI
# spec; otherwise raise error and ask user to fix that.
if 'paths' in options:
Expand All @@ -362,6 +366,29 @@ def openapihttpdomain(spec, **options):
', '.join(set(options['paths']) - set(spec['paths'])),
)
)
paths = options['paths']

# Check against regular expressions to be included
if 'include' in options:
for i in options['include']:
ir = re.compile(i)
for path in spec['paths']:
if ir.match(path):
paths.append(path)

# If no include nor paths option, then take full path
if 'include' not in options and 'paths' not in options:
paths = spec['paths']

# Remove paths matching regexp
if 'exclude' in options:
_paths = []
for e in options['exclude']:
er = re.compile(e)
for path in paths:
if not er.match(path):
_paths.append(path)
paths = _paths

render_request = False
if 'request' in options:
Expand All @@ -373,7 +400,7 @@ def openapihttpdomain(spec, **options):
if 'group' in options:
groups = collections.defaultdict(list)

for endpoint in options.get('paths', spec['paths']):
for endpoint in paths:
for method, properties in spec['paths'][endpoint].items():
key = properties.get('tags', [''])[0]
groups[key].append(_httpresource(
Expand All @@ -392,7 +419,7 @@ def openapihttpdomain(spec, **options):

generators.extend(groups[key])
else:
for endpoint in options.get('paths', spec['paths']):
for endpoint in paths:
for method, properties in spec['paths'][endpoint].items():
generators.append(_httpresource(
endpoint,
Expand Down
72 changes: 72 additions & 0 deletions tests/test_openapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,78 @@ def test_path_option(self):
ok
''').lstrip()

def test_include_option(self):
spec = collections.defaultdict(collections.OrderedDict)
spec['paths']['/resource_a'] = {
'get': {
'description': 'resource a',
'responses': {
'200': {'description': 'ok'},
}
}
}
spec['paths']['/resource_b'] = {
'post': {
'description': 'resource b',
'responses': {
'404': {'description': 'error'},
}
}
}

text = '\n'.join(openapi20.openapihttpdomain(spec, include=[
'/resource',
]))
assert text == textwrap.dedent('''
.. http:get:: /resource_a
:synopsis: null
resource a
:status 200:
ok
.. http:post:: /resource_b
:synopsis: null
resource b
:status 404:
error
''').lstrip()

def test_exclude_option(self):
spec = collections.defaultdict(collections.OrderedDict)
spec['paths']['/resource_a'] = {
'get': {
'description': 'resource a',
'responses': {
'200': {'description': 'ok'},
}
}
}
spec['paths']['/resource_b'] = {
'post': {
'description': 'resource b',
'responses': {
'404': {'description': 'error'},
}
}
}

text = '\n'.join(openapi20.openapihttpdomain(spec, exclude=[
'/.*_a',
]))
assert text == textwrap.dedent('''
.. http:post:: /resource_b
:synopsis: null
resource b
:status 404:
error
''').lstrip()

def test_root_parameters(self):
spec = {'paths': {}}
spec['paths']['/resources/{name}'] = collections.OrderedDict()
Expand Down

0 comments on commit 9306435

Please sign in to comment.