Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,7 @@ output/*.html
output/*/index.html

# Sphinx
docs/_build
docs/_build

# IDE
.idea/
6 changes: 3 additions & 3 deletions flex/datastructures.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def __init__(self, value=None):
if value:
self.add_validator(value)

def add_validator(self, validator):
def add_validator(self, validator, **kwargs):
if is_non_string_iterable(validator)\
and not isinstance(validator, collections.Mapping):
for value in validator:
Expand Down Expand Up @@ -55,8 +55,8 @@ def __init__(self, validators=None):
for key, validator in validators.items():
self.add_validator(key, validator)

def add_validator(self, key, validator):
self[key].add_validator(validator)
def add_validator(self, key, validator, **kwargs):
self[key].add_validator(validator, **kwargs)

def add_property_validator(self, key, validator):
self.add_validator(
Expand Down
10 changes: 9 additions & 1 deletion flex/loading/common/reference.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import os

from six.moves import urllib_parse as urlparse

import jsonpointer
Expand Down Expand Up @@ -37,11 +39,17 @@
@skip_if_not_of_type(STRING)
def validate_reference_pointer(reference, context, **kwargs):
parts = urlparse.urlparse(reference)
if any((parts.scheme, parts.netloc, parts.path, parts.params, parts.query)):
if any((parts.scheme, parts.netloc, parts.params, parts.query)):
raise ValidationError(
MESSAGES['reference']['unsupported'].format(reference),
)

if parts.path:
from flex.core import load_source
if parts.path.startswith('/'):
context = load_source(parts.path)
elif 'base_path' in kwargs:
context = load_source(os.path.join(kwargs['base_path'], parts.path))
try:
jsonpointer.resolve_pointer(context, parts.fragment)
except jsonpointer.JsonPointerException:
Expand Down
10 changes: 9 additions & 1 deletion flex/loading/definitions/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import os

from six.moves import urllib_parse as urlparse

import jsonpointer
Expand Down Expand Up @@ -47,12 +49,18 @@ def validate_deferred_references(schema, context, **kwargs):
with ErrorDict() as errors:
for reference in deferred_references:
parts = urlparse.urlparse(reference)
if any((parts.scheme, parts.netloc, parts.path, parts.params, parts.query)):
if any((parts.scheme, parts.netloc, parts.params, parts.query)):
errors.add_error(
reference,
MESSAGES['reference']['unsupported'].format(reference),
)
continue
if parts.path:
from flex.core import load_source
if parts.path.startswith('/'):
schema = load_source(parts.path)
elif 'base_path' in kwargs:
schema = load_source(os.path.join(kwargs['base_path'], parts.path))
try:
jsonpointer.resolve_pointer(schema, parts.fragment)
except jsonpointer.JsonPointerException:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import os

from six.moves import urllib_parse as urlparse

import jsonpointer
Expand All @@ -24,6 +26,12 @@
def validate_reference(reference, context, **kwargs):
try:
parts = urlparse.urlparse(reference)
if parts.path:
from flex.core import load_source
if parts.path.startswith('/'):
context = load_source(parts.path)
elif 'base_path' in kwargs:
context = load_source(os.path.join(kwargs['base_path'], parts.path))
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This same block of code is repeated quite a few times. Can you abstract it into a reusable function.

jsonpointer.resolve_pointer(context, parts.fragment)
except jsonpointer.JsonPointerException:
raise ValidationError(MESSAGES['reference']['undefined'].format(reference))
Expand Down
4 changes: 2 additions & 2 deletions flex/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ def merge_parameter_lists(*parameter_definitions):
return merged_parameters.values()


def dereference_parameter_list(parameters, context):
def dereference_parameter_list(parameters, context, **kwargs):
return tuple((
dereference_reference(p['$ref'], context)
dereference_reference(p['$ref'], context, **kwargs)
if '$ref' in p
else p
for p in parameters
Expand Down
6 changes: 3 additions & 3 deletions flex/paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def path_to_pattern(api_path, parameters):


def path_to_regex(api_path, path_parameters, operation_parameters=None,
context=None):
context=None, **kwargs):
if context is None:
context = {}
if operation_parameters is None:
Expand All @@ -107,8 +107,8 @@ def path_to_regex(api_path, path_parameters, operation_parameters=None,
api_path=api_path,
parameters=merge_parameter_lists(
context.get('parameters', {}).values(),
dereference_parameter_list(path_parameters, context),
dereference_parameter_list(operation_parameters, context),
dereference_parameter_list(path_parameters, context, **kwargs),
dereference_parameter_list(operation_parameters, context, **kwargs),
),
)
return re.compile(pattern)
Expand Down
12 changes: 10 additions & 2 deletions flex/utils.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
import math
import collections
import numbers
Expand Down Expand Up @@ -192,10 +193,17 @@ def prettify_errors(errors):
return '\n'.join(format_errors(errors))


def dereference_reference(reference, context):
def dereference_reference(reference, context, **kwargs):
parts = urlparse.urlparse(reference)
if any((parts.scheme, parts.netloc, parts.path, parts.params, parts.query)):
if any((parts.scheme, parts.netloc, parts.params, parts.query)):
raise ValueError(
MESSAGES['reference']['unsupported'].format(reference),
)

if parts.path:
from flex.core import load_source
if parts.path.startswith('/'):
context = load_source(parts.path)
elif 'base_path' in kwargs:
context = load_source(os.path.join(kwargs['base_path'], parts.path))
return jsonpointer.resolve_pointer(context, parts.fragment)
10 changes: 5 additions & 5 deletions flex/validation/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ def validate_allof_anyof(value, sub_schemas, context, method, **kwargs):
messages = []
success = []
for schema in sub_schemas:
schema_validators = construct_schema_validators(schema, context)
schema_validators = construct_schema_validators(schema, context, **kwargs)
try:
schema_validators.validate_object(value, context=context)
except ValidationError as err:
Expand All @@ -331,7 +331,7 @@ def generate_anyof_validator(anyOf, context, **kwargs):


def validate_object(obj, field_validators=None, non_field_validators=None,
schema=None, context=None):
schema=None, context=None, base_path=None):
"""
Takes a mapping and applies a mapping of validator functions to it
collecting and reraising any validation errors that occur.
Expand All @@ -348,7 +348,7 @@ def validate_object(obj, field_validators=None, non_field_validators=None,
from flex.validation.schema import (
construct_schema_validators,
)
schema_validators = construct_schema_validators(schema, context)
schema_validators = construct_schema_validators(schema, context, base_path=base_path)

if '$ref' in schema_validators and hasattr(schema_validators['$ref'], 'validators'):
ref_ = field_validators.pop('$ref')
Expand All @@ -358,8 +358,8 @@ def validate_object(obj, field_validators=None, non_field_validators=None,

schema_validators.update(field_validators)

schema_validators.validate_object(obj, context=context)
non_field_validators.validate_object(obj, context=context)
schema_validators.validate_object(obj, context=context, base_path=base_path)
non_field_validators.validate_object(obj, context=context, base_path=base_path)

return obj

Expand Down
6 changes: 5 additions & 1 deletion flex/validation/operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,9 @@ def generate_request_body_validator(body_parameters, context, **kwargs):
elif not body_parameters:
return noop
body_validators = construct_parameter_validators(
body_parameters[0], context=context,
body_parameters[0],
context=context,
**kwargs
)
return generate_object_validator(field_validators=body_validators)

Expand All @@ -119,10 +121,12 @@ def generate_parameters_validator(api_path, path_definition, parameters,
path_level_parameters = dereference_parameter_list(
path_definition.get('parameters', []),
context,
**kwargs
)
operation_level_parameters = dereference_parameter_list(
parameters,
context,
**kwargs
)

all_parameters = merge_parameter_lists(
Expand Down
20 changes: 12 additions & 8 deletions flex/validation/parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def get_path_parameter_values(target_path, api_path, path_parameters, context):
return type_cast_parameters(raw_values, path_parameters, context=context)


def validate_path_parameters(target_path, api_path, path_parameters, context):
def validate_path_parameters(target_path, api_path, path_parameters, context, **kwargs):
"""
Helper function for validating a request path
"""
Expand All @@ -79,21 +79,21 @@ def validate_path_parameters(target_path, api_path, path_parameters, context):
parameter_values = get_path_parameter_values(
target_path, api_path, path_parameters, context,
)
validate_parameters(parameter_values, path_parameters, context=context)
validate_parameters(parameter_values, path_parameters, context=context, **kwargs)


def validate_query_parameters(raw_query_data, query_parameters, context):
def validate_query_parameters(raw_query_data, query_parameters, context, **kwargs):
query_data = {}
for key, value in raw_query_data.items():
if is_non_string_iterable(value) and len(value) == 1:
query_data[key] = value[0]
else:
query_data[key] = value
query_data = type_cast_parameters(query_data, query_parameters, context)
validate_parameters(query_data, query_parameters, context)
validate_parameters(query_data, query_parameters, context, **kwargs)


def validate_parameters(parameter_values, parameters, context):
def validate_parameters(parameter_values, parameters, context, **kwargs):
validators = construct_multi_parameter_validators(parameters, context=context)

with ErrorCollection() as errors:
Expand All @@ -104,15 +104,15 @@ def validate_parameters(parameter_values, parameters, context):
errors[key].add_error(err.detail)


def construct_parameter_validators(parameter, context):
def construct_parameter_validators(parameter, context, **kwargs):
"""
Constructs a dictionary of validator functions for the provided parameter
definition.
"""
validators = ValidationDict()
if '$ref' in parameter:
validators.add_validator(
'$ref', ParameterReferenceValidator(parameter['$ref'], context),
'$ref', ParameterReferenceValidator(parameter['$ref'], context, **kwargs),
)
for key in parameter:
if key in validator_mapping:
Expand All @@ -121,7 +121,11 @@ def construct_parameter_validators(parameter, context):
validator_mapping[key](context=context, **parameter),
)
if 'schema' in parameter:
schema_validators = construct_schema_validators(parameter['schema'], context=context)
schema_validators = construct_schema_validators(
parameter['schema'],
context=context,
**kwargs
)
for key, value in schema_validators.items():
validators.setdefault(key, value)
return validators
Expand Down
20 changes: 17 additions & 3 deletions flex/validation/reference.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from six.moves import urllib_parse as urlparse
import os

from six.moves import urllib_parse as urlparse
import jsonpointer

from flex.validation.common import (
Expand All @@ -17,14 +18,26 @@ class LazyReferenceValidator(object):
"""
validators_constructor = None

def __init__(self, reference, context):
def __init__(self, reference, context, **kwargs):
if self.validators_constructor is None:
raise NotImplementedError(
"Subclasses of LazyReferenceValidator must specify a "
"`validators_constructor` function"
)

self.reference_fragment = urlparse.urlparse(reference).fragment
self._kwargs = kwargs

parsed_ref = urlparse.urlparse(reference)
self.reference_path = parsed_ref.path
self.reference_fragment = parsed_ref.fragment

if self.reference_path:
from flex.core import load_source
if self.reference_path.startswith('/'):
context = load_source(self.reference_path)
elif 'base_path' in kwargs:
context = load_source(os.path.join(kwargs['base_path'], self.reference_path))

# TODO: something better than this which potentiall raises a
# JsonPointerException
jsonpointer.resolve_pointer(context, self.reference_fragment)
Expand All @@ -47,6 +60,7 @@ def validators(self):
return self.validators_constructor(
self.schema,
self.context,
**self._kwargs
)

def items(self):
Expand Down
2 changes: 2 additions & 0 deletions flex/validation/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,12 @@ def generate_path_validator(api_path, path_definition, parameters,
path_level_parameters = dereference_parameter_list(
path_definition.get('parameters', []),
context,
**kwargs
)
operation_level_parameters = dereference_parameter_list(
parameters,
context,
**kwargs
)

all_parameters = merge_parameter_lists(
Expand Down
Loading