Skip to content

Commit

Permalink
Move swagger schema model creation and validation to a single callsit…
Browse files Browse the repository at this point in the history
…e to support #73 and in preparation for #81
  • Loading branch information
Daniel Nephin committed Feb 20, 2015
1 parent 201064d commit a72a1f4
Show file tree
Hide file tree
Showing 12 changed files with 76 additions and 82 deletions.
4 changes: 4 additions & 0 deletions pyramid_swagger/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@
"""
import pyramid
from .api import register_api_doc_endpoints
from .ingest import add_swagger_schema


def includeme(config):
add_swagger_schema(config.registry)

config.add_tween(
"pyramid_swagger.tween.validation_tween_factory",
under=pyramid.tweens.EXCVIEW
)

if config.registry.settings.get(
'pyramid_swagger.enable_api_doc_views',
True
Expand Down
12 changes: 3 additions & 9 deletions pyramid_swagger/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,10 @@
"""
import simplejson

from .ingest import compile_swagger_schema
from .tween import load_settings


def register_api_doc_endpoints(config):
"""Create and register pyramid endpoints for /api-docs*."""
settings = load_settings(config.registry)
swagger_schema = compile_swagger_schema(
settings.schema_dir,
settings.validate_swagger_spec,
)
swagger_schema = config.registry.settings['swagger_schema']
with open(swagger_schema.resource_listing) as input_file:
register_resource_listing(config, simplejson.load(input_file))

Expand Down Expand Up @@ -73,7 +66,8 @@ def register_api_declaration(config, resource_name, api_declaration):

def build_api_declaration_view(api_declaration_json):
"""Thanks to the magic of closures, this means we gracefully return JSON
without file IO at request time."""
without file IO at request time.
"""
def view_for_api_declaration(request):
# Note that we rewrite basePath to always point at this server's root.
return dict(
Expand Down
32 changes: 27 additions & 5 deletions pyramid_swagger/ingest.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,24 +62,46 @@ def _load_resource_listing(resource_listing):
)


def compile_swagger_schema(schema_dir, should_validate_schemas):
def compile_swagger_schema(schema_dir):
"""Build a SwaggerSchema from various files.
:param schema_dir: the directory schema files live inside
:type schema_dir: string
:param should_validate_schemas: if True, check schemas for correctness
:type should_validate_schemas: boolean
:returns: a SwaggerSchema object
"""
listing_filename = os.path.join(schema_dir, API_DOCS_FILENAME)
listing_json = _load_resource_listing(listing_filename)
mapping = build_schema_mapping(schema_dir, listing_json)
schema_resolvers = ingest_resources(mapping, schema_dir)
return SwaggerSchema(listing_filename, mapping, schema_resolvers)


if should_validate_schemas:
# TODO: more test cases
def add_swagger_schema(registry):
"""Add the swagger_schema to the registry.settings
"""
schema_dir = registry.settings.get(
'pyramid_swagger.schema_directory',
'api_docs/'
)
if registry.settings.get(
'pyramid_swagger.enable_swagger_spec_validation',
True
):
listing_filename = os.path.join(schema_dir, API_DOCS_FILENAME)
# TODO: this will be replaced by ssv shortly
listing_json = _load_resource_listing(listing_filename)
mapping = build_schema_mapping(schema_dir, listing_json)
validate_swagger_schemas(listing_filename, mapping.values())

return SwaggerSchema(listing_filename, mapping, schema_resolvers)
# TODO: docs for this
if registry.settings.get(
'pyramid_swagger.enable_build_swagger_schema_model',
True
):
registry.settings['swagger_schema'] = (
compile_swagger_schema(schema_dir)
)


def ingest_resources(mapping, schema_dir):
Expand Down
3 changes: 0 additions & 3 deletions pyramid_swagger/spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@ def validate_resource_listing(resource_listing_json):

def validate_api_declaration(api_declaration_json):
"""Validate a swagger schema.
:param schema_dir: the directory schema files live inside
:type schema_dir: string
"""
api_spec_path = resource_filename(
'pyramid_swagger',
Expand Down
18 changes: 4 additions & 14 deletions pyramid_swagger/tween.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
from jsonschema.validators import Draft3Validator, Draft4Validator
from pyramid_swagger.exceptions import RequestValidationError
from pyramid_swagger.exceptions import ResponseValidationError
from .ingest import compile_swagger_schema
from .model import PathNotMatchedError


Expand Down Expand Up @@ -85,10 +84,7 @@ def validation_tween_factory(handler, registry):
while delegating to the relevant matching view.
"""
settings = load_settings(registry)
schema = compile_swagger_schema(
settings.schema_dir,
settings.validate_swagger_spec
)
schema = registry.settings['swagger_schema']
route_mapper = registry.queryUtility(IRoutesMapper)
disable_all_validation = not any((
settings.validate_request,
Expand Down Expand Up @@ -138,16 +134,10 @@ def validator_tween(request):


def load_settings(registry):
# TODO: remove these unused settings
return Settings(
# By default, assume cwd contains the swagger schemas.
schema_dir=registry.settings.get(
'pyramid_swagger.schema_directory',
'api_docs/'
),
validate_swagger_spec=registry.settings.get(
'pyramid_swagger.enable_swagger_spec_validation',
True
),
None,
None,
validate_request=registry.settings.get(
'pyramid_swagger.enable_request_validation',
True
Expand Down
14 changes: 8 additions & 6 deletions tests/acceptance/request_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,12 +166,14 @@ def test_200_skip_validation_when_disabled():


def test_path_validation_context():
assert test_app(**{'pyramid_swagger.validation_context_path': validation_ctx_path}) \
.get('/does_not_exist') \
.status_code == 206
app = test_app(
**{'pyramid_swagger.validation_context_path': validation_ctx_path}
)
assert app.get('/does_not_exist').status_code == 206


def test_request_validation_context():
assert test_app(**{'pyramid_swagger.validation_context_path': validation_ctx_path}) \
.get('/get_with_non_string_query_args', params={}) \
.status_code == 206
app = test_app(
**{'pyramid_swagger.validation_context_path': validation_ctx_path})
response = app.get('/get_with_non_string_query_args', params={})
assert response.status_code == 206
4 changes: 3 additions & 1 deletion tests/acceptance/response_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from pyramid.registry import Registry
from pyramid.response import Response
from pyramid_swagger.exceptions import ResponseValidationError
from pyramid_swagger.ingest import compile_swagger_schema
from pyramid_swagger.tween import validation_tween_factory
from webtest import AppError

Expand Down Expand Up @@ -49,7 +50,8 @@ def handler(request):
return response or Response()

settings = dict({
'pyramid_swagger.schema_directory': 'tests/sample_schemas/good_app/',
'swagger_schema': compile_swagger_schema(
'tests/sample_schemas/good_app/'),
'pyramid_swagger.enable_swagger_spec_validation': False},
**overrides
)
Expand Down
33 changes: 0 additions & 33 deletions tests/acceptance/schema_validation_test.py

This file was deleted.

5 changes: 4 additions & 1 deletion tests/api_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from pyramid_swagger.ingest import API_DOCS_FILENAME
from pyramid_swagger.ingest import ApiDeclarationNotFoundError
from pyramid_swagger.ingest import ResourceListingNotFoundError
from pyramid_swagger.ingest import compile_swagger_schema
from tests.acceptance.response_test import get_registry


Expand All @@ -22,7 +23,9 @@ def test_basepath_rewriting():

def build_config(schema_dir):
return mock.Mock(
registry=get_registry({'pyramid_swagger.schema_directory': schema_dir})
registry=get_registry({
'swagger_schema': compile_swagger_schema(schema_dir),
})
)


Expand Down
22 changes: 22 additions & 0 deletions tests/includeme_test.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import jsonschema
import mock
import pytest

import pyramid_swagger

Expand All @@ -7,7 +9,27 @@
def test_disable_api_doc_views(mock_register):
settings = {
'pyramid_swagger.enable_api_doc_views': False,
'pyramid_swagger.enable_swagger_spec_validation': False,
'pyramid_swagger.enable_build_swagger_schema_model': False,
}
mock_config = mock.Mock(registry=mock.Mock(settings=settings))
pyramid_swagger.includeme(mock_config)
assert not mock_register.called


def test_bad_schema_validated_on_include():
settings = {
'pyramid_swagger.schema_directory': 'tests/sample_schemas/bad_app/',
}
mock_config = mock.Mock(registry=mock.Mock(settings=settings))
with pytest.raises(jsonschema.exceptions.ValidationError):
pyramid_swagger.includeme(mock_config)


def test_bad_schema_not_validated_if_spec_validation_is_disabled():
settings = {
'pyramid_swagger.schema_directory': 'tests/sample_schemas/bad_app/',
'pyramid_swagger.enable_swagger_spec_validation': False,
}
mock_config = mock.Mock(registry=mock.Mock(settings=settings))
pyramid_swagger.includeme(mock_config)
5 changes: 1 addition & 4 deletions tests/model_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@

@pytest.fixture
def schema():
schema_dir = 'tests/sample_schemas/good_app/'
enable_swagger_spec_validation = False

return compile_swagger_schema(schema_dir, enable_swagger_spec_validation)
return compile_swagger_schema('tests/sample_schemas/good_app/')


def test_swagger_schema_for_request_different_methods(schema):
Expand Down
6 changes: 0 additions & 6 deletions tests/tween_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
from pyramid_swagger.exceptions import ResponseValidationError
from pyramid_swagger.tween import DEFAULT_EXCLUDED_PATHS
from pyramid_swagger.tween import get_exclude_paths
from pyramid_swagger.tween import load_settings
from pyramid_swagger.tween import prepare_body
from pyramid_swagger.tween import should_exclude_path
from pyramid_swagger.tween import validate_outgoing_response
Expand Down Expand Up @@ -51,11 +50,6 @@ def test_response_charset_missing_raises_5xx():
)


def test_unconfigured_schema_dir_uses_api_docs():
"""If we send a settings dict without schema_dir, fail fast."""
assert load_settings(mock.Mock(settings={}))[0] == 'api_docs/'


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 a72a1f4

Please sign in to comment.