Skip to content

Commit

Permalink
Merge pull request #44 from analogue/fix_swagger12_regression
Browse files Browse the repository at this point in the history
Fix regression with Swagger 1.2 specs - #43
  • Loading branch information
analogue committed Nov 18, 2015
2 parents e497494 + 06d025e commit 773d6bd
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 54 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
Changelog
=========

2.0.2 (2015-11-18)
------------------
- Fix regression with Swagger 1.2 schemas - #43

2.0.1 (2015-11-17)
------------------
- Fix rich validations that rely on a working deref with scope annotations
Expand Down
44 changes: 0 additions & 44 deletions swagger_spec_validator/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,9 @@
import simplejson as json
except ImportError:
import json
from jsonschema import RefResolver
from jsonschema.validators import Draft4Validator
from pkg_resources import resource_filename
import six
from six.moves.urllib import request

from swagger_spec_validator import ref_validators

TIMEOUT_SEC = 1


Expand All @@ -28,45 +23,6 @@ def wrapper(*args, **kwargs):
return wrapper


@wrap_exception
def validate_json(spec_dict, schema_path, spec_url='', http_handlers=None):
"""Validate a json document against a json schema.
:param spec_dict: json document in the form of a list or dict.
:param schema_path: package relative path of the json schema file.
:param spec_url: base uri to use when creating a
RefResolver for the passed in spec_dict.
:param http_handlers: used to download any remote $refs in spec_dict with
a custom http client. Defaults to None in which case the default
http client built into jsonschema's RefResolver is used. This
is a mapping from uri scheme to a callable that takes a
uri.
:return: RefResolver for spec_dict with cached remote $refs used during
validation.
:rtype: :class:`jsonschema.RefResolver`
"""
schema_path = resource_filename('swagger_spec_validator', schema_path)
with open(schema_path) as schema_file:
schema = json.loads(schema_file.read())

schema_resolver = RefResolver('file://{0}'.format(schema_path), schema)

spec_resolver = RefResolver(spec_url, spec_dict,
handlers=http_handlers or {})

ref_validators.validate(
spec_dict,
schema,
resolver=schema_resolver,
instance_cls=ref_validators.create_dereffing_validator(spec_resolver),
cls=Draft4Validator)

# Since remote $refs were downloaded, pass the resolver back to the caller
# so that its cached $refs can be re-used.
return spec_resolver


def load_json(url):
with contextlib.closing(request.urlopen(url, timeout=TIMEOUT_SEC)) as fh:
return json.loads(fh.read().decode('utf-8'))
Expand Down
24 changes: 22 additions & 2 deletions swagger_spec_validator/validator12.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,23 @@
The validator uses the published jsonschema files for basic structural
validation, augmented with custom validation code where necessary.
https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md
https://github.com/swagger-api/swagger-spec/blob/master/versions/1.2.md
"""
try:
import simplejson as json
except ImportError:
import json
import logging
import os

import jsonschema
from jsonschema import RefResolver
from pkg_resources import resource_filename
import six
from six.moves.urllib.parse import urlparse

from swagger_spec_validator.common import SwaggerValidationError
from swagger_spec_validator.common import load_json
from swagger_spec_validator.common import validate_json
from swagger_spec_validator.common import wrap_exception

log = logging.getLogger(__name__)
Expand Down Expand Up @@ -237,3 +243,17 @@ def validate_resource_listing(resource_listing):
:raises: :py:class:`jsonschema.exceptions.ValidationError`
"""
validate_json(resource_listing, 'schemas/v1.2/resourceListing.json')


@wrap_exception
def validate_json(json_document, schema_path):
"""Validate a json document against a json schema.
:param json_document: json document in the form of a list or dict.
:param schema_path: package relative path of the json schema file.
"""
schema_path = resource_filename('swagger_spec_validator', schema_path)
with open(schema_path) as schema_file:
schema = json.loads(schema_file.read())
resolver = RefResolver('file://{0}'.format(schema_path), schema)
jsonschema.validate(json_document, schema, resolver=resolver)
51 changes: 49 additions & 2 deletions swagger_spec_validator/validator20.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import functools
try:
import simplejson as json
except ImportError:
import json
import logging
import string

from jsonschema import RefResolver
from jsonschema.validators import Draft4Validator
from pkg_resources import resource_filename
from six import iteritems


from swagger_spec_validator import ref_validators
from swagger_spec_validator.common import load_json
from swagger_spec_validator.common import SwaggerValidationError
from swagger_spec_validator.common import validate_json
from swagger_spec_validator.common import wrap_exception
from swagger_spec_validator.ref_validators import in_scope

Expand All @@ -22,7 +30,7 @@ def deref(ref_dict, resolver):
:param resolver: Ref resolver used to do the de-referencing
:type resolver: :class:`jsonschema.RefResolver`
:return: dereferenced value of ref_dict
:return: de-referenced value of ref_dict
:rtype: scalar, list, dict
"""
if ref_dict is None or not is_ref(ref_dict):
Expand Down Expand Up @@ -82,6 +90,45 @@ def validate_spec(spec_dict, spec_url='', http_handlers=None):
return swagger_resolver


@wrap_exception
def validate_json(spec_dict, schema_path, spec_url='', http_handlers=None):
"""Validate a json document against a json schema.
:param spec_dict: json document in the form of a list or dict.
:param schema_path: package relative path of the json schema file.
:param spec_url: base uri to use when creating a
RefResolver for the passed in spec_dict.
:param http_handlers: used to download any remote $refs in spec_dict with
a custom http client. Defaults to None in which case the default
http client built into jsonschema's RefResolver is used. This
is a mapping from uri scheme to a callable that takes a
uri.
:return: RefResolver for spec_dict with cached remote $refs used during
validation.
:rtype: :class:`jsonschema.RefResolver`
"""
schema_path = resource_filename('swagger_spec_validator', schema_path)
with open(schema_path) as schema_file:
schema = json.loads(schema_file.read())

schema_resolver = RefResolver('file://{0}'.format(schema_path), schema)

spec_resolver = RefResolver(spec_url, spec_dict,
handlers=http_handlers or {})

ref_validators.validate(
spec_dict,
schema,
resolver=schema_resolver,
instance_cls=ref_validators.create_dereffing_validator(spec_resolver),
cls=Draft4Validator)

# Since remote $refs were downloaded, pass the resolver back to the caller
# so that its cached $refs can be re-used.
return spec_resolver


def validate_apis(apis, deref):
"""Validates semantic errors in #/paths.
Expand Down
18 changes: 16 additions & 2 deletions tests/data/v1.2/foo/foo.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
{
"operations": [
{
"type": "string",
"type": "array",
"items": {
"$ref": "FooResponse"
},
"nickname": "foo",
"method": "GET",
"parameters": [
Expand All @@ -19,5 +22,16 @@
],
"path": "/foo"
}
]
],
"models": {
"FooResponse": {
"id": "FooResponse",
"properties": {
"message": {
"type": "string",
"description": "A message"
}
}
}
}
}
25 changes: 25 additions & 0 deletions tests/validator12/validate_json_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import json
import os

import pytest

from swagger_spec_validator.common import SwaggerValidationError
from swagger_spec_validator.validator12 import validate_json


def test_success():
my_dir = os.path.abspath(os.path.dirname(__file__))

with open(os.path.join(my_dir, '../data/v1.2/foo/swagger_api.json')) as f:
resource_listing = json.load(f)
validate_json(resource_listing, 'schemas/v1.2/resourceListing.json')

with open(os.path.join(my_dir, '../data/v1.2/foo/foo.json')) as f:
api_declaration = json.load(f)
validate_json(api_declaration, 'schemas/v1.2/apiDeclaration.json')


def test_failure():
with pytest.raises(SwaggerValidationError) as excinfo:
validate_json({}, 'schemas/v1.2/apiDeclaration.json')
assert "'swaggerVersion' is a required property" in str(excinfo.value)
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@
import os
import pytest

from swagger_spec_validator.common import SwaggerValidationError, validate_json
from swagger_spec_validator.common import SwaggerValidationError
from swagger_spec_validator.validator20 import validate_json


def test_success():
my_dir = os.path.abspath(os.path.dirname(__file__))
with open(os.path.join(my_dir, '../data/v2.0/petstore.json')) as petstore_file:
petstore_spec = json.load(petstore_file)
with open(os.path.join(my_dir, '../data/v2.0/petstore.json')) as f:
petstore_spec = json.load(f)
validate_json(petstore_spec, 'schemas/v2.0/schema.json')


def test_failure():
with pytest.raises(SwaggerValidationError):
with pytest.raises(SwaggerValidationError) as excinfo:
validate_json({}, 'schemas/v2.0/schema.json')
assert "'swagger' is a required property" in str(excinfo.value)

0 comments on commit 773d6bd

Please sign in to comment.