Skip to content

Commit

Permalink
Add option to exclude OPTIONS requests
Browse files Browse the repository at this point in the history
These changes add a new optional parameter called `document_options`
to the FlaskApiSpec constructor that will exclude OPTIONS requests
from the swagger specification if set to `False`.

Flask automatically generates OPTIONS requests for each route. There are
cases, such as when using CORS, where one would want these OPTIONS
requests to be generated but do not want to have them in their swagger
docs.

My line of reasoning for wanting to exclude these is: My API users
will never explicitly make an OPTIONS request. The browser will
automatically send a pre-flight OPTIONS request when making a
cross-origin request, and I want to have OPTIONS endpoints in order
to support that, but I don't want to have this functionality adding
a bunch of endpoints to my swagger docs which will largely be ignored
by users.
  • Loading branch information
Brcrwilliams committed Oct 21, 2018
1 parent 535fc9d commit 03d0d78
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 11 deletions.
8 changes: 6 additions & 2 deletions flask_apispec/apidoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@

class Converter(object):

def __init__(self, app, spec):
def __init__(self, app, spec, document_options):
self.app = app
self.spec = spec
self.document_options = document_options
try:
self.marshmallow_plugin = next(
plugin for plugin in self.spec.plugins
Expand All @@ -39,13 +40,16 @@ def convert(self, target, endpoint=None, blueprint=None, **kwargs):
def get_path(self, rule, target, **kwargs):
operations = self.get_operations(rule, target)
parent = self.get_parent(target, **kwargs)
excluded_methods = {'head'}
if not self.document_options:
excluded_methods.add('options')
return {
'view': target,
'path': rule_to_path(rule),
'operations': {
method.lower(): self.get_operation(rule, view, parent=parent)
for method, view in six.iteritems(operations)
if method.lower() in (set(VALID_METHODS) - {'head'})
if method.lower() in (set(VALID_METHODS) - excluded_methods)
},
}

Expand Down
11 changes: 8 additions & 3 deletions flask_apispec/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,17 @@ def get_pet(pet_id):
:param Flask app: App associated with API documentation
:param APISpec spec: apispec specification associated with API documentation
:param bool document_options: Whether or not to include
OPTIONS requests in the specification
"""

def __init__(self, app=None):
def __init__(self, app=None, document_options=True):
self._deferred = []
self.app = app
self.view_converter = None
self.resource_converter = None
self.spec = None
self.document_options = document_options

if app:
self.init_app(app)
Expand All @@ -53,8 +56,10 @@ def init_app(self, app):
make_apispec(self.app.config.get('APISPEC_TITLE', 'flask-apispec'),
self.app.config.get('APISPEC_VERSION', 'v1'))
self.add_swagger_routes()
self.resource_converter = ResourceConverter(self.app, spec=self.spec)
self.view_converter = ViewConverter(app=self.app, spec=self.spec)
self.resource_converter = ResourceConverter(self.app,
self.spec,
self.document_options)
self.view_converter = ViewConverter(self.app, self.spec, self.document_options)

for deferred in self._deferred:
deferred()
Expand Down
12 changes: 6 additions & 6 deletions tests/test_openapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ def test_error_if_spec_does_not_have_marshmallow_plugin(app):
plugins=[], # oh no! no MarshmallowPlugin
)
with pytest.raises(RuntimeError):
ViewConverter(app=app, spec=bad_spec)
ViewConverter(app=app, spec=bad_spec, document_options=True)
with pytest.raises(RuntimeError):
ResourceConverter(app=app, spec=bad_spec)
ResourceConverter(app=app, spec=bad_spec, document_options=True)


class TestFunctionView:
Expand All @@ -54,7 +54,7 @@ def get_band(band_id):

@pytest.fixture
def path(self, app, spec, function_view):
converter = ViewConverter(app=app, spec=spec)
converter = ViewConverter(app=app, spec=spec, document_options=True)
paths = converter.convert(function_view)
for path in paths:
spec.add_path(**path)
Expand Down Expand Up @@ -98,7 +98,7 @@ def get_band(**kwargs):

@pytest.fixture
def path(self, app, spec, function_view):
converter = ViewConverter(app=app, spec=spec)
converter = ViewConverter(app=app, spec=spec, document_options=True)
paths = converter.convert(function_view)
for path in paths:
spec.add_path(**path)
Expand Down Expand Up @@ -142,7 +142,7 @@ def delete_band(band_id):

@pytest.fixture
def path(self, app, spec, function_view):
converter = ViewConverter(app=app, spec=spec)
converter = ViewConverter(app=app, spec=spec, document_options=True)
paths = converter.convert(function_view)
for path in paths:
spec.add_path(**path)
Expand All @@ -169,7 +169,7 @@ def get(self, **kwargs):

@pytest.fixture
def path(self, app, spec, resource_view):
converter = ResourceConverter(app=app, spec=spec)
converter = ResourceConverter(app=app, spec=spec, document_options=True)
paths = converter.convert(resource_view, endpoint='band')
for path in paths:
spec.add_path(**path)
Expand Down

0 comments on commit 03d0d78

Please sign in to comment.