Skip to content

Commit

Permalink
Add response_validation_exclude_routes support (#253)
Browse files Browse the repository at this point in the history
* RESTODART-130 Add response_validation_exclude_routes support

* Remove print statement

* Remove version bump
  • Loading branch information
raychan authored May 8, 2024
1 parent 0c14f74 commit 84ec523
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 5 deletions.
5 changes: 2 additions & 3 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
Changelog
=========

2.8.0 (2024--5-03)
2.8.0 (2024-5-03)
++++++++++++++++++++++++++
* Ensure http 401 in case of missing security (see #239)
* Fix pyramid swagger renderer if missing schema (see #242)
* Reduce usage of deprecated methods (see #241)
* Minor fixes (see #243, #244 and #246)
* Update tox to py38 and py310 and skip swagger 1.2 tests (see #249)

2.7.0 (2019--5-07)
2.7.0 (2019-5-07)
++++++++++++++++++++++++++
* Remove not needed deprecation warnings (see #238)
* Make ``pyramid_swagger`` compatible with ``jsonschema>3`` (see #327)
Expand Down
12 changes: 11 additions & 1 deletion pyramid_swagger/tween.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class Settings(namedtuple(
'exclude_paths',
'exclude_routes',
'prefer_20_routes',
'response_validation_exclude_routes'
]
)):

Expand All @@ -79,6 +80,8 @@ class Settings(namedtuple(
:param prefer_20_routes: list of route names that should be handled
via v2.0 spec when `2.0` is in `swagger_versions`. All others will be
handled via v1.2 spec. [i.e. Make v2.0 an opt-in feature]
:param response_validation_exclude_routes: list of route names that should be excluded from
response validation.
"""


Expand Down Expand Up @@ -195,7 +198,7 @@ def swagger_data(_):

response = handler(request)

if settings.validate_response:
if settings.validate_response and not should_exclude_response_validation(settings, route_info):
with validation_context(request, response=response):
swagger_handler.handle_response(response, op_or_validators_map)

Expand Down Expand Up @@ -389,6 +392,9 @@ def load_settings(registry):
) or [])),
prefer_20_routes=set(aslist(registry.settings.get(
'pyramid_swagger.prefer_20_routes') or [])),
response_validation_exclude_routes=set(aslist(registry.settings.get(
'pyramid_swagger.response_validation_exclude_routes',
) or [])),
)


Expand Down Expand Up @@ -462,6 +468,10 @@ def should_exclude_request(settings, request, route_info):
)


def should_exclude_response_validation(settings, route_info):
return should_exclude_route(settings.response_validation_exclude_routes, route_info)


def should_exclude_path(exclude_path_regexes, path):
# Skip validation for the specified endpoints
return any(r.match(path) for r in exclude_path_regexes)
Expand Down
2 changes: 2 additions & 0 deletions tests/acceptance/app/config.ini
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ pyramid_swagger.exclude_routes = /undefined/first
pyramid_swagger.prefer_20_routes = /sample
pyramid_swagger.schema_directory = tests/sample_schemas/good_app
pyramid_swagger.swagger_versions = 1.2 2.0
pyramid_swagger.response_validation_exclude_routes = /exclude_response/first
/exclude_response/second
1 change: 1 addition & 0 deletions tests/acceptance/config_ini_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def test_load_ini_settings(ini_app):
assert settings.validate_path is True
assert settings.exclude_routes == {'/undefined/first', '/undefined/second'}
assert settings.prefer_20_routes == {'/sample'}
assert settings.response_validation_exclude_routes == {'/exclude_response/first', '/exclude_response/second'}


def test_get_swagger_versions(ini_app):
Expand Down
23 changes: 22 additions & 1 deletion tests/acceptance/response20_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import simplejson
from mock import Mock
from mock import patch
from pyramid.interfaces import IRoute
from pyramid.interfaces import IRoutesMapper
from pyramid.response import Response
from webtest.app import AppError
Expand Down Expand Up @@ -58,7 +59,9 @@ def handler(request):
# so that usages in the tween meet expectations. Holler if you know a
# better way to do this!
op = spec.get_op_for_request(request.method, path_pattern)
mock_route_info = {'match': request.matchdict, 'route': None}
mock_route = Mock(spec=IRoute)
mock_route.name = path_pattern
mock_route_info = {'match': request.matchdict, 'route': mock_route}
mock_route_mapper = Mock(spec=IRoutesMapper, return_value=mock_route_info)
with patch('pyramid_swagger.tween.get_op_for_request', return_value=op):
with patch('pyramid.registry.Registry.queryUtility',
Expand Down Expand Up @@ -183,6 +186,24 @@ def test_500_for_bad_validated_array_response():
str(excinfo.value)


def test_response_not_validated_if_route_in_response_validation_exclude_routes():
request = EnhancedDummyRequest(
method='GET',
path='/sample_array_response',
)
response = Response(
body='{}',
headers={'Content-Type': 'application/json; charset=UTF-8'},
)

_validate_against_tween(
request,
response=response,
path_pattern='/sample_array_response',
# the route name is configured to be the same as the path_pattern in `_validate_against_tween`
**{'pyramid_swagger.response_validation_exclude_routes': {'/sample_array_response'}})


def test_200_for_good_validated_array_response():
request = EnhancedDummyRequest(
method='GET',
Expand Down
16 changes: 16 additions & 0 deletions tests/tween_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from pyramid_swagger.tween import PyramidSwaggerResponse
from pyramid_swagger.tween import Settings
from pyramid_swagger.tween import should_exclude_path
from pyramid_swagger.tween import should_exclude_response_validation
from pyramid_swagger.tween import should_exclude_route
from pyramid_swagger.tween import SWAGGER_12
from pyramid_swagger.tween import SWAGGER_20
Expand Down Expand Up @@ -108,6 +109,21 @@ def test_should_exclude_route_no_route():
assert not should_exclude_route(set(['foo', 'two']), {'route': None})


def test_should_exclude_response_validation(settings, mock_route_info):
settings.response_validation_exclude_routes = ['route-one', 'two']
assert should_exclude_response_validation(settings, mock_route_info)


def test_should_exclude_response_validation_no_matched_route(settings, mock_route_info):
settings.response_validation_exclude_routes = ['foo', 'two']
assert not should_exclude_response_validation(settings, mock_route_info)


def test_should_exclude_response_validation_no_route():
settings.response_validation_exclude_routes = ['foo', 'two']
assert not should_exclude_response_validation(settings, {'route': None})


def test_validation_skips_path_properly():
excluded_paths = [re.compile(r) for r in DEFAULT_EXCLUDED_PATHS]
assert should_exclude_path(excluded_paths, '/static')
Expand Down

0 comments on commit 84ec523

Please sign in to comment.