def __init__(self, schema=DEFAULT_LTM_SCHEMA): """Choose schema and initialize extended Draft4Validator. Raises: F5CcclSchemaError: Failed to read or validate the CCCL API schema file. """ try: self.schema = read_yaml_or_json(schema) except json.JSONDecodeError as error: LOGGER.error("%s", error) raise cccl_exc.F5CcclSchemaError( 'CCCL API schema could not be decoded.') except IOError as error: LOGGER.error("%s", error) raise cccl_exc.F5CcclSchemaError( 'CCCL API schema could not be read.') try: Draft4Validator.check_schema(self.schema) self.validate_properties = Draft4Validator.VALIDATORS["properties"] validator_with_defaults = validators.extend( Draft4Validator, {"properties": self.__set_defaults}) self.validator = validator_with_defaults(self.schema) except jsonschema.SchemaError as error: LOGGER.error("%s", error) raise cccl_exc.F5CcclSchemaError("Invalid API schema")
def unique_by_key(validator_class): ''' Factory function for validator with 'uniqueKey' check for objects array. ''' validate_items = validator_class.VALIDATORS["items"] def unique_keys(validator, items, instance, schema): ''' Checks for duplicate objects in array. ''' for err in validate_items(validator, items, instance, schema): yield err if 'uniqueKey' not in items: return uniq_key = items['uniqueKey'] keys_set = set() if validator.is_type(uniq_key, 'array'): get = lambda d: tuple(d.get(k, None) for k in uniq_key) else: get = lambda d: d.get(uniq_key, None) for i in instance: val = get(i) if val in keys_set: yield ValidationError( 'Duplicate value `{}` for key `{}`.' .format(val, uniq_key) ) keys_set.add(val) return validators.extend(validator_class, {'items': unique_keys})
def extends(validator_class, partial): """Extend `Draft4Validator` with some validators.""" def custom(validator, _custom, instance, schema): """Support user customized validators.""" message = _custom(instance) if message is not None: yield ValidationError(message) def required(validator, _required, instance, schema): """Disable required validating if in partial mode. In normal (non-partial) mode, validate and yield errors with additional `path` and `schema_path` for each required field. """ if partial: return if not validator.is_type(instance, "object"): return for property in _required: if property not in instance: yield ValidationError( '%r is a required property' % property, path=(property,), schema_path=(property,) ) return validators.extend( validator_class, {'custom': custom, 'required': required}, )
def extend_with_default(validator_class): ''' Factory function for validator with 'default' values substitution. ''' validate_props = validator_class.VALIDATORS["properties"] validate_required = validator_class.VALIDATORS["required"] validate_type = validator_class.VALIDATORS["type"] def set_default(validator, props, instance, schema): ''' Substitutes default value in a validating object instance. ''' required = schema.get("required", []) valid = True for err in validate_required(validator, required, instance, schema): valid = False yield err for err in validate_props(validator, props, instance, schema): valid = False yield err for prop, subschema in props.iteritems(): if "default" in subschema: typ = subschema["type"] default = subschema["default"] for err in validate_type(validator, typ, default, None): valid = False yield err if valid: instance.setdefault(prop, default) return validators.extend(validator_class, {"properties": set_default})
def test_validator_map(json_validation_spec_dir, spec): def validate_type(validator, types, instance, schema): types = _utils.ensure_list(types) errors = Draft4RequestValidator.VALIDATORS['type'](validator, types, instance, schema) for e in errors: yield e if 'string' in types and 'minLength' not in schema: errors = Draft4RequestValidator.VALIDATORS['minLength'](validator, 1, instance, schema) for e in errors: yield e MinLengthRequestValidator = extend(Draft4RequestValidator, {'type': validate_type}) class MyRequestBodyValidator(RequestBodyValidator): def __init__(self, *args, **kwargs): super(MyRequestBodyValidator, self).__init__(*args, validator=MinLengthRequestValidator, **kwargs) validator_map = {'body': MyRequestBodyValidator} app = App(__name__, specification_dir=json_validation_spec_dir) app.add_api(spec, validate_responses=True, validator_map=validator_map) app_client = app.app.test_client() res = app_client.post('/v1.0/minlength', data=json.dumps({'foo': 'bar'}), content_type='application/json') # type: flask.Response assert res.status_code == 200 res = app_client.post('/v1.0/minlength', data=json.dumps({'foo': ''}), content_type='application/json') # type: flask.Response assert res.status_code == 400
def extend_with_default(validator_class): """Add support for the 'default' attribute for properties and patternProperties. jsonschema does not handle this out of the box -- it only validates. This allows us to set default values for configs where certain fields are `None` b/c they're deleted or commented out. """ validate_properties = validator_class.VALIDATORS["properties"] validate_pattern_properties = validator_class.VALIDATORS["patternProperties"] def set_defaults(validator, properties, instance, schema): for property, subschema in properties.iteritems(): if "default" in subschema: instance.setdefault(property, subschema["default"]) for err in validate_properties(validator, properties, instance, schema): yield err def set_pp_defaults(validator, properties, instance, schema): for property, subschema in properties.iteritems(): if "default" in subschema: if isinstance(instance, dict): for key, val in instance.iteritems(): if re.match(property, key) and val is None: instance[key] = subschema["default"] for err in validate_pattern_properties(validator, properties, instance, schema): yield err return validators.extend(validator_class, { "properties" : set_defaults, "patternProperties" : set_pp_defaults })
def test_object_extensions_can_handle_custom_validators(self): schema = { "type": "object", "required": ["x"], "properties": {"x": {"type": "integer"}}, } type_checker = Draft4Validator.TYPE_CHECKER.redefine( u"object", is_object_or_named_tuple, ) CustomValidator = extend( Draft4Validator, type_checker=type_checker, validators={"required": required, "properties": properties}, ) validator = CustomValidator(schema) Point = namedtuple("Point", ["x", "y"]) # Can now process required and properties validator.validate(Point(x=4, y=5)) with self.assertRaises(ValidationError): validator.validate(Point(x="not an integer", y=5))
def test_extend(self): validators = dict(self.Validator.VALIDATORS) new = mock.Mock() Extended = extend(self.Validator, validators={"a new one": new}) validators.update([("a new one", new)]) self.assertEqual(Extended.VALIDATORS, validators) self.assertEqual(Extended.META_SCHEMA, self.Validator.META_SCHEMA) self.assertEqual(Extended.DEFAULT_TYPES, self.Validator.DEFAULT_TYPES)
def extend_with_default(validator_class): validate_properties = validator_class.VALIDATORS["properties"] def set_defaults(validator, properties, instance, schema): for error in validate_properties( validator, properties, instance, schema, ): yield error for property, subschema in six.iteritems(properties): if "default" in subschema: instance.setdefault(property, subschema["default"]) def oneOf_discriminator(validator, oneOf, instance, schema): subschemas = enumerate(oneOf) all_errors = [] if 'discriminator' in schema: discriminator = schema['discriminator'] if discriminator in instance: subschema = first_or_default(lambda s: s['$ref'] == instance[discriminator], oneOf) if subschema: for err in validator.descend(instance, subschema): yield err return for index, subschema in subschemas: errs = list(validator.descend(instance, subschema, schema_path=index)) if not errs: first_valid = subschema break all_errors.extend(errs) else: yield ValidationError( "%r is not valid under any of the given schemas" % (instance,), context=all_errors, ) more_valid = [s for i, s in subschemas if validator.is_valid(instance, s)] if more_valid: more_valid.append(first_valid) reprs = ", ".join(repr(schema) for schema in more_valid) yield ValidationError( "%r is valid under each of %s" % (instance, reprs) ) return validators.extend( validator_class, { "properties": set_defaults, "oneOf": oneOf_discriminator }, )
def extend_with_default(validator_class): validate_properties = validator_class.VALIDATORS['properties'] def set_defaults(validator, properties, instance, schema): for property, sub_schema in properties.iteritems(): if "default" in sub_schema: instance.setdefault(property, sub_schema['default']) for error in validate_properties(validator, properties, instance, schema): yield error return validators.extend(validator_class, {"properties": set_defaults})
def fill_defaults(validator_class): validate_props = validator_class.VALIDATORS['properties'] def set_defaults(validator, props, instance, schema): for prop, subschema in props.items(): if isinstance(instance, dict) and 'default' in subschema: instance.setdefault(prop, subschema['default']) for error in validate_props(validator, props, instance, schema): yield error return validators.extend(validator_class, {'properties': set_defaults})
def get_validator_type(swagger_spec): """Create a custom jsonschema validator for Swagger 2.0 specs. :rtype: Its complicated. See jsonschema.validators.create() """ return validators.extend( Draft4Validator, { 'required': functools.partial(required_validator, swagger_spec), 'enum': enum_validator, 'type': functools.partial(type_validator, swagger_spec), })
def extend_with_default(validator_class): validate_properties = validator_class.VALIDATORS["properties"] def set_defaults(validator, properties, instance, schema): for error in validate_properties(validator, properties, instance, schema): yield error for property, subschema in six.iteritems(properties): if "default" in subschema: instance.setdefault(property, subschema["default"]) return validators.extend(validator_class, {"properties": set_defaults})
def test_object_can_be_extended(self): schema = {'type': 'object'} Point = namedtuple('Point', ['x', 'y']) type_checker = Draft4Validator.TYPE_CHECKER.redefine( u"object", is_object_or_named_tuple ) CustomValidator = extend(Draft4Validator, type_checker=type_checker) v = CustomValidator(schema) v.validate(Point(x=4, y=5))
def test_object_can_be_extended(self): schema = {"type": "object"} Point = namedtuple("Point", ["x", "y"]) type_checker = Draft4Validator.TYPE_CHECKER.redefine( u"object", is_object_or_named_tuple, ) CustomValidator = extend(Draft4Validator, type_checker=type_checker) validator = CustomValidator(schema) validator.validate(Point(x=4, y=5))
def test_object_extensions_require_custom_validators(self): schema = {"type": "object", "required": ["x"]} type_checker = Draft4Validator.TYPE_CHECKER.redefine( u"object", is_object_or_named_tuple, ) CustomValidator = extend(Draft4Validator, type_checker=type_checker) validator = CustomValidator(schema) Point = namedtuple("Point", ["x", "y"]) # Cannot handle required with self.assertRaises(ValidationError): validator.validate(Point(x=4, y=5))
def test_default_types_update_type_checker(self): Validator = validators.create( meta_schema=self.meta_schema, validators=self.validators, default_types={u"array": list} ) self.assertEqual(set(Validator.DEFAULT_TYPES), {u"array"}) Extended = validators.extend( Validator, type_checker=Validator.TYPE_CHECKER.remove(u"array") ) self.assertEqual(set(Extended.DEFAULT_TYPES), {})
def test_simple_type_can_be_extended(self): schema = {'type': 'integer'} type_checker = Draft4Validator.TYPE_CHECKER.redefine( "integer", is_int_or_string_int ) CustomValidator = extend(Draft4Validator, type_checker=type_checker) v = CustomValidator(schema) v.validate(4) v.validate('4') with self.assertRaises(ValidationError): v.validate(4.4)
def extend_with_default(validator_class): """ This code was adapted from the jsonschema FAQ: http://python-jsonschema.readthedocs.org/en/latest/faq/ """ validate_properties = validator_class.VALIDATORS["properties"] def set_defaults_and_validate(validator, properties, instance, schema): for property, subschema in properties.iteritems(): if "default" in subschema: instance.setdefault(property, subschema["default"]) for error in validate_properties(validator, properties, instance, schema): yield error return validators.extend(validator_class, {"properties" : set_defaults_and_validate})
def test_extend(self): original_validators = dict(self.Validator.VALIDATORS) new = mock.Mock() Extended = validators.extend( self.Validator, validators={u"a new one": new}, ) original_validators.update([(u"a new one", new)]) self.assertEqual(Extended.VALIDATORS, original_validators) self.assertNotIn(u"a new one", self.Validator.VALIDATORS) self.assertEqual(Extended.META_SCHEMA, self.Validator.META_SCHEMA) self.assertEqual(Extended.DEFAULT_TYPES, self.Validator.DEFAULT_TYPES) self.assertEqual(Extended.TYPE_CHECKER, self.Validator.TYPE_CHECKER)
def extend_with_default(validator_class: Any) -> Any: """Append defaults from schema to instance need to be validated. :param validator_class: Apply the change for given validator class. """ validate_properties = validator_class.VALIDATORS["properties"] def set_defaults(validator: Any, properties: dict, instance: dict, schema: dict) -> Iterator[ValidationError]: for prop, subschema in properties.items(): if "default" in subschema: instance.setdefault(prop, subschema["default"]) for error in validate_properties(validator, properties, instance, schema): yield error # pragma: no cover return extend(validator_class, {"properties": set_defaults})
def extend_with_default(validator_cls): """ Applies default field from jsonschema Copied, with modificatios, from python-jsonschema FAQ. See: https://python-jsonschema.readthedocs.io/en/latest/faq/#why-doesn-t-my-schema-that-has-a-default-property-actually-set-the-default-on-my-instance """ validate_props = validator_cls.VALIDATORS['properties'] def set_defaults(validator, properties, instance, schema): for prop, subschema in six.iteritems(properties): if 'default' in subschema: instance.setdefault(prop, subschema['default']) for error in validate_props(validator, properties, instance, schema): yield error return validators.extend(validator_cls, {'properties': set_defaults})
def _extend_with_default(self, validator_class): validate_properties = validator_class.VALIDATORS["properties"] def set_defaults(validator, properties, instance, schema): for property, subschema in properties.iteritems(): if "default" in subschema: instance.setdefault(property, subschema["default"]) for error in validate_properties( validator, properties, instance, schema): yield error extended_validators = validators.extend( validator_class, {"properties" : set_defaults}, ) VALIDATORS = extended_validators.VALIDATORS for _validator in self.skip_validators: if VALIDATORS.get(_validator): del(VALIDATORS[_validator]) return extended_validators
def extend_with_default(validator_class): validate_properties = validator_class.VALIDATORS["properties"] def set_defaults(validator, properties, instance, schema): if schema.get('title',None)=='Yadage Stage': if 'dependencies' in instance and type(instance['dependencies'])==list: log.debug('dependencies provided as list, assume jsonpath_ready predicate') instance['dependencies'] = { "dependency_type" : "jsonpath_ready", "expressions": instance["dependencies"] } if "Scheduler" in schema.get('title','') and instance['scheduler_type'] in ['singlestep-stage','multistep-stage']: if(type(instance['parameters'])==dict): asarray = [] for k,v in instance['parameters'].items(): if type(v) == dict and any(k in v for k in ['steps','stages','step']): v['expression_type'] = 'stage-output-selector' asarray.append({'key':k,'value':v}) instance['parameters'] = asarray errors_found = False for error in validate_properties( validator, properties, instance, schema, ): errors_found = True yield error if errors_found: return for prop, subschema in properties.items(): if "default" in subschema and type(instance) == dict: # Note not clear why this is the case, appeared in py3.X # intermittently on Travis instance.setdefault(prop, subschema["default"]) validator = validators.extend( validator_class, {"properties" : set_defaults}, ) def validate(self, *args, **kwargs): for error in self.iter_errors(*args, **kwargs): pass validator.validate = validate return validator
def extend_with_default(validator_class): """Append defaults from schema to instance need to be validated. :param validator_class: Apply the change for given validator class. """ validate_properties = validator_class.VALIDATORS['properties'] def set_defaults(validator, properties, instance, schema): for prop, subschema in properties.items(): if 'default' in subschema: instance.setdefault(prop, subschema['default']) for error in validate_properties( validator, properties, instance, schema, ): yield error # pragma: no cover return extend(validator_class, {'properties': set_defaults})
def get_body_validator(models): """Returns a validator for the request body, based on a :class:`jsonschema.validators.Draft4Validator`, with extra validations added for swaggers extensions to jsonschema. :param models: a mapping of reference to models :returns: a :class:`jsonschema.validators.Validator` which can validate the request body. """ return validators.extend( Draft4Validator, { 'paramType': ignore, 'name': ignore, 'type': build_swagger_type_validator(models), 'required': required_validator, } )
def extend_with_default(self, validator_class): ''' Method to add default fields to our schema validation ( From the docs ) ''' validate_properties = validator_class.VALIDATORS["properties"] def set_defaults(validator, properties, instance, schema): for error in validate_properties( validator, properties, instance, schema, ): yield error for property, subschema in properties.iteritems(): if "default" in subschema: instance.setdefault(property, subschema["default"]) return validators.extend( validator_class, {"properties": set_defaults}, )
def create_dereffing_validator(instance_resolver): """Create a customized Draft4Validator that follows $refs in the schema being validated (the Swagger spec for a service). This is not to be confused with $refs that are in the schema that describes the Swagger 2.0 specification. :param instance_resolver: resolver for the swagger service's spec :type instance_resolver: :class:`jsonschema.RefResolver` :rtype: Its complicated. See jsonschema.validators.create() """ visited_refs = {} validators_to_bound = { '$ref', 'additionalProperties', 'allOf', 'anyOf', 'dependencies', 'maxProperties', 'minProperties', 'not', 'oneOf', 'patternProperties', 'properties', 'required', 'type', } bound_validators = { k: functools.partial( validator_wrapper, instance_resolver=instance_resolver, visited_refs=visited_refs, default_validator_callable=v, ) if k in validators_to_bound else v for k, v in iteritems(Draft4Validator.VALIDATORS) } return validators.extend(Draft4Validator, bound_validators)
def create_dereffing_validator(instance_resolver): """Create a customized Draft4Validator that follows $refs in the schema being validated (the Swagger spec for a service). This is not to be confused with $refs that are in the schema that describes the Swagger 2.0 specification. :param instance_resolver: resolver for the swagger service's spec :type instance_resolver: :class:`jsonschema.RefResolver` :rtype: Its complicated. See jsonschema.validators.create() """ visited_refs = {} custom_validators = { '$ref': _validators.ref, 'properties': _validators.properties_draft4, 'additionalProperties': _validators.additionalProperties, 'patternProperties': _validators.patternProperties, 'type': _validators.type_draft4, 'dependencies': _validators.dependencies, 'required': _validators.required_draft4, 'minProperties': _validators.minProperties_draft4, 'maxProperties': _validators.maxProperties_draft4, 'allOf': _validators.allOf_draft4, 'oneOf': _validators.oneOf_draft4, 'anyOf': _validators.anyOf_draft4, 'not': _validators.not_draft4, } bound_validators = {} for k, v in iteritems(custom_validators): bound_validators[k] = functools.partial( validator_wrapper, instance_resolver=instance_resolver, visited_refs=visited_refs, default_validator_callable=v) return validators.extend(Draft4Validator, bound_validators)
def _extend_config_with_default(validator_class): validate_properties = validator_class.VALIDATORS["properties"] def set_defaults(validator, properties, instance, schema): # Do it only for root. if instance != {}: return for property, subschema in properties.iteritems(): main_dict = {} for property2, subschema2 in subschema["properties"].iteritems(): sub_dict = {} for property3, subschema3 in subschema2["properties"].iteritems(): if "default" in subschema3: sub_dict[property3] = subschema3["default"] main_dict[property2] = sub_dict instance.setdefault(property, main_dict) for error in validate_properties(validator, properties, instance, schema): yield error return validators.extend( validator_class, {"properties": set_defaults}, )
"""Check is instance of type.""" if is_env_variable(instance): return True return super().is_type(instance, type) def ownAdditionalProperties(validator, aP, instance, schema): """Additioinal properties validator.""" for _ in additionalProperties(validator, aP, instance, schema): raise ExtraPropertiesError( list(find_additional_properties(instance, schema))) return iter(()) OwnDraft4Validator = extend( validator=Draft4Validator, validators={"additionalProperties": ownAdditionalProperties}, ) EnvVarsFriendlyDraft4Validator = extend( validator=Draft4Validator, type_checker=CustomTypeChecker(Draft4Validator.TYPE_CHECKER._type_checkers # pylint: disable=protected-access ), ) class ConfigValidator: """Configuration validator implementation.""" def __init__(self, schema_filename: str, env_vars_friendly: bool = False): """ Initialize the parser for configuration files.
def set_defaults(validator, properties, instance, schema): for property_, subschema in six.iteritems(properties): if "default" in subschema: instance.setdefault(property_, subschema["default"]) for error in validate_properties(validator, properties, instance, schema): yield error return extend(validator_class, {"properties": set_defaults}) Draft4RequestValidator = extend_with_set_default( extend( Draft4Validator, { 'type': validate_type, 'enum': validate_enum, 'required': validate_required, 'readOnly': validate_readOnly })) Draft4ResponseValidator = extend_with_set_default( extend( Draft4Validator, { 'type': validate_type, 'enum': validate_enum, 'required': validate_required, 'writeOnly': validate_writeOnly, 'x-writeOnly': validate_writeOnly }))
try: import pandas except ImportError: pass else: def data_frame(description): return primitive("data_frame", description) def is_data_frame(_checker, value): return isinstance(value, pandas.DataFrame) _TYPE_CHECKER = _TYPE_CHECKER.redefine("data_frame", is_data_frame) CustomValidator = extend(jsonschema.Draft7Validator, type_checker=_TYPE_CHECKER) def validate(schema, value, cls=CustomValidator, **kwargs): cls(schema, format_checker=jsonschema.draft7_format_checker).validate( value, **kwargs ) schema = {"type": "data_frame"} value = pandas.DataFrame({"x": [1]}) def test(): try:
if 'x-writeOnly' in validator.VALIDATORS and subschema.get( 'x-writeOnly') is True: continue yield ValidationError("%r is a required property" % prop) def validate_readOnly(validator, ro, instance, schema): yield ValidationError("Property is read-only") def validate_writeOnly(validator, wo, instance, schema): yield ValidationError("Property is write-only") Draft4RequestValidator = extend( Draft4Validator, { 'type': validate_type, 'enum': validate_enum, 'required': validate_required, 'readOnly': validate_readOnly }) Draft4ResponseValidator = extend( Draft4Validator, { 'type': validate_type, 'enum': validate_enum, 'required': validate_required, 'writeOnly': validate_writeOnly, 'x-writeOnly': validate_writeOnly })
for prop, subschema in properties.items(): if "default" in subschema: instance.setdefault(prop, subschema["default"]) return Draft6Validator.VALIDATORS["properties"](validator, properties, instance, schema) validator_overrides = { "anyOf": None, "properties": set_defaults, "required": None, "oneOf": None, "not": None, } DefaultValidator = validators.extend(Draft6Validator, validator_overrides) RequiredValidator = Draft6Validator class ActionModule(ActionBase): def run(self, tmp=None, task_vars=None): action_vars = self._task.args if 'instance' not in action_vars: raise AnsibleError("instance parameter missing") if 'schema' not in action_vars: raise AnsibleError("schema parameter missing") instance_copy = copy.deepcopy(action_vars['instance']) try:
from functools import partial from connexion.decorators import response as r from connexion.decorators.validation import ResponseBodyValidator from jsonschema.validators import Draft4Validator, extend from jsonschema._validators import type_draft4 def type_custom(validator, types, instance, schema, nullable_attr="x-nullable"): if schema.get(nullable_attr, False): if not isinstance(types, (list, tuple)): types = [types] types.append("null") yield from type_draft4(validator, types, instance, schema) CustomValidator = extend(Draft4Validator, { "type": type_custom, }) # monkey patch r.ResponseBodyValidator = partial(ResponseBodyValidator, validator=CustomValidator)
# -*- coding: utf-8 -*- # # This file is part of Invenio. # Copyright (C) 2015-2018 CERN. # # Invenio is free software; you can redistribute it and/or modify it # under the terms of the MIT License; see LICENSE file for more details. """Record validators.""" from jsonschema.validators import Draft4Validator, extend PartialDraft4Validator = extend(Draft4Validator, {'required': None}) """Partial JSON Schema (draft 4) validator. Special validator that contains the same validation rules of Draft4Validator, except for required fields. """
type_checker=oas_types.oas30_type_checker, # NOTE: version causes conflict with global jsonschema validator # See https://github.com/p1c2u/openapi-schema-validator/pull/12 # version="oas30", id_of=lambda schema: schema.get(u"id", ""), ) BaseOAS31Validator = extend( Draft202012Validator, { # adjusted to OAS u"allOf": oas_validators.allOf, u"oneOf": oas_validators.oneOf, u"anyOf": oas_validators.anyOf, u"description": oas_validators.not_implemented, u"format": oas_validators.format, # fixed OAS fields u"discriminator": oas_validators.not_implemented, u"xml": oas_validators.not_implemented, u"externalDocs": oas_validators.not_implemented, u"example": oas_validators.not_implemented, }, type_checker=oas31_type_checker, ) @attrs class OAS30Validator(BaseOAS30Validator): read: bool = attrib(default=None) write: bool = attrib(default=None)
# Copyright 2013 Hewlett-Packard Development Company, L.P. # # Author: Kiall Mac Innes <*****@*****.**> # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from jsonschema import validators from designate.schema import _validators Draft3Validator = validators.extend(validators.Draft3Validator, validators={ "type": _validators.type_draft3, "oneOf": _validators.oneOf_draft3, }) Draft4Validator = validators.extend(validators.Draft4Validator, validators={ "type": _validators.type_draft4, })
def _create_validator(schema, enforce_defaults: bool): cls = validator_for(schema) cls.check_schema(schema) if enforce_defaults: cls = extend(cls, {"default": _default_validator}) return cls(schema)
if subject is not None: metadata["subject"] = subject file_information = { "filename": os.path.basename(filepath), "mimetype": mimetype, "metadata": metadata, } return file_information # json schema validation # use the type check from Draft4Validator, because Draft7 considers 1.0 as integer CustomValidator = validators.extend(Draft7Validator, type_checker=Draft4Validator.TYPE_CHECKER) def validate_against_schema(instance, schema, raise_exception=True): # load schema object schema = load_schema(schema, False) # get the absolute path and setup a resolver schema_abs_path = "file:///{0}/".format( os.path.abspath(schemas_folder()).replace("\\", "/")) resolver = RefResolver(schema_abs_path, schema) validator = CustomValidator(schema=schema, resolver=resolver) # validate against schema and catch eventual exception try:
def extend_with_default(validator_class=Draft7Validator): """ Extends a validator class to add defaults before validation. From: https://github.com/Julian/jsonschema/blob/master/docs/faq.rst """ validate_props = validator_class.VALIDATORS["properties"] validate_array = validator_class.VALIDATORS["items"] def set_defaults(validator, properties, instance, schema): """ Set defaults within a "properties" context """ if not validator.is_type(instance, "object"): return for prop, subschema in properties.items(): if instance.get(prop) is None: if "default" in subschema: default_val = subschema["default"] elif subschema.get("type") == "array": default_val = [] elif subschema.get("type") == "object": default_val = {} else: continue instance[prop] = default_val for error in validate_props(validator, properties, instance, schema): yield error def set_array_defaults(validator, properties, instance, schema): """ Set defaults within an "array" context """ if not validator.is_type(instance, "array"): return if not instance: default_val = None if "default" in properties: default_val = properties["default"] elif properties.get("type") == "array": default_val = [] elif properties.get("type") == "object": default_val = {} if default_val is not None: instance.append(default_val) for error in validate_array(validator, properties, instance, schema): yield error def set_oneOf_defaults(validator, properties, instance, schema): """ Set defaults within a "oneOf" context. This ensures that only defaults from the matching subschema are set on the instance. TODO: If we ever use other optional subschema contexts (ex: allOf, anyOf) then we should implement custom validator functions for those as well. """ good_properties = [] all_errors = [] good_instance = None for i, subschema in enumerate(properties): new_instance = deepcopy(instance) # first time to fill in defaults since validating 'required', 'minProperties', # etc. can't be done until the instance has been properly filled with defaults. list(validator.descend(new_instance, subschema, schema_path=i)) errs = list( validator.descend(new_instance, subschema, schema_path=i)) if errs: all_errors.extend(errs) else: good_properties.append(subschema) good_instance = new_instance if len(good_properties) == 0: msg = f"{instance} is not valid under any of the given schemas" yield ValidationError(msg, context=all_errors) elif len(good_properties) > 1: msg = f'{instance} is valid under each of {", ".join(repr(p) for p in good_properties)}' yield ValidationError(msg) else: instance.clear() instance.update(good_instance) custom_validators = { "properties": set_defaults, "items": set_array_defaults, "oneOf": set_oneOf_defaults, } return validators.extend(validator_class, custom_validators)
def emit_defaults(schema, include_yaml_comments=False, yaml_indent=2, base_cls=None, *args, **kwargs): """ Emit all default values for the given schema. Similar to calling ``validate({}, schema, inject_defaults=True)``, except: 1. Ignore schema validation errors and 'required' property errors 2. If no default is given for a property, inject ``"{{NO_DEFAULT}}"``, even if the property isn't supposed to be a string. 3. If ``include_yaml_comments`` is True, insert ``CommentedMap`` objects instead of ordinary dicts, and insert a comment above each key, with the contents of the property ``"description"`` in the schema. Args: schema: The schema data to pull defaults from include_yaml_comments: Whether or not to return ``ruamel.yaml`` objects so that comments will be written when the data is dumped to YAML. yaml_indent: To ensure correctly indented comments, you must specify the indent step you plan to use when this data is eventually dumped as yaml. Returns: A copy of instance, with default values injected, and comments if specified. """ instance = {} if include_yaml_comments: instance = CommentedMap(instance) instance.key_indent = 0 # monkey-patch! if "description" in schema: instance.yaml_set_start_comment('\n' + schema["description"] + '\n\n') else: instance = dict(instance) if base_cls is None: base_cls = validators.validator_for(schema) base_cls.check_schema(schema) def is_object(checker, instance): return (base_cls.TYPE_CHECKER.is_type(instance, "object") or isinstance(instance, (ordereddict, CommentedMap))) def is_array(checker, instance): return (base_cls.TYPE_CHECKER.is_type(instance, "array") or isinstance(instance, CommentedSeq)) # By default, jsonschema expects JSON objects to be of type 'dict'. # We also want to permit ruamel.yaml.comments.CommentedSeq and CommentedMap type_checker = base_cls.TYPE_CHECKER.redefine_many({ "object": is_object, "array": is_array }) cls = validators.extend(base_cls, type_checker=type_checker) # Add default-injection behavior to the validator cls = extend_with_default_without_validation(cls, include_yaml_comments, yaml_indent) extended_validator = cls(schema, *args, **kwargs) # Inject defaults. extended_validator.validate(instance) return instance
import json from jsonschema import Draft7Validator, TypeChecker, FormatChecker from jsonschema.validators import extend from application.utils.datetime_utils import str_to_datetime, is_interval, is_datetime from application.collections_db import Couriers, Orders from flask import Response # Create validator extended with custom types 'interval' and 'datetime' type_checker = Draft7Validator.TYPE_CHECKER \ .redefine("interval", is_interval) \ .redefine("datetime", is_datetime) ValidatorWithDatetime = extend(Draft7Validator, type_checker=type_checker) # Response for not application/json headers bad_header = Response(json.dumps({'error': 'Content-Type must be application/json'}), 400, headers={'Content-Type': 'application/json'}) def validate_couriers(couriers_data: dict) -> (dict, int): with open('application/schemas/CouriersPostRequest.json') as f: couriers_post_schema = ValidatorWithDatetime(json.load(f)) return _validate_post_items(couriers_data, couriers_post_schema, 'courier') def validate_orders(orders_data: dict) -> (dict, int): with open('application/schemas/OrdersPostRequest.json') as f: orders_post_schema = ValidatorWithDatetime(json.load(f)) return _validate_post_items(orders_data, orders_post_schema, 'order') def validate_update_courier(new_data: dict, courier_id, couriers_db: Couriers) -> (dict, int):
""" This module contains the `OpenAPISchemaValidator`. The `OpenAPISchemaValidator` extends the `jsonschema.Draft4Validator` with the functionalities as described in :swagger:`schemaObject`. """ from jsonschema import Draft4Validator from jsonschema.validators import extend #: This extended validator implements the extra formats specified by the #: Open API specification. OpenAPISchemaValidator = extend(Draft4Validator, {})
def get_s4s_validator(fetcher): type_checker = TYPE_CHECKER.redefine('reference', make_reference_type(fetcher)) return extend(Draft6Validator, validators=VALIDATORS, type_checker=type_checker)
# -*- coding: utf-8 -*- # # This file is part of CERN Analysis Preservation Framework. # Copyright (C) 2016 CERN. # # CERN Analysis Preservation Framework is free software; you can redistribute # it and/or modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # CERN Analysis Preservation Framework is distributed in the hope that it will # be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with CERN Analysis Preservation Framework; if not, write to the # Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, # MA 02111-1307, USA. # # In applying this license, CERN does not # waive the privileges and immunities granted to it by virtue of its status # as an Intergovernmental Organization or submit itself to any jurisdiction. """Record validators.""" from jsonschema import Draft4Validator from jsonschema.validators import extend record_validators = dict(Draft4Validator.VALIDATORS) RecordValidator = extend(Draft4Validator, validators=record_validators)
try: from jsonschema import Draft7Validator from jsonschema.validators import extend except ImportError: raise ImportError( 'Cannot find "jsonschema" package. Either install it manually ' 'with pip, or install confj with validation option: ' 'pip install confj[validation]') from .confdata import ConfigData # pylint: disable=W0613 def is_config(checker, instance): return (Draft7Validator.TYPE_CHECKER.is_type(instance, "object") or isinstance(instance, ConfigData)) TYPE_CHECKER = Draft7Validator.TYPE_CHECKER.redefine("object", is_config) ConfigValidator = extend(Draft7Validator, type_checker=TYPE_CHECKER) CONFIG_VALIDATOR = ConfigValidator(schema={"type": "object"})
def test_extending_a_legacy_validator_does_not_rewarn(self): Validator = validators.create(meta_schema={}, default_types={}) self.assertTrue(self.flushWarnings()) validators.extend(Validator) self.assertFalse(self.flushWarnings())
""" return validators.extend( Draft4Validator, { 'paramType': ignore, 'name': ignore, 'type': build_swagger_type_validator(models), 'required': required_validator, } ) Swagger12ParamValidator = validators.extend( Draft3Validator, { 'paramType': ignore, 'name': ignore, 'type': type_validator, } ) class ValidatorMap( namedtuple('_VMap', 'query path form headers body response') ): """ A data object with validators for each part of the request and response objects. Each field is a :class:`SchemaValidator`. """ __slots__ = () @classmethod
def extend_with_no_checks_for_required(validator_class): def no_checks(_validator, _properties, _instance, _schema): return return extend(validator_class, {"required": no_checks})
@fixture def json_schema_merge(request): schema_path = os.path.join(schema_dir, 'merge_format.schema.json') with io.open(schema_path, encoding="utf8") as f: schema_json = json.load(f) return schema_json def is_sequence(checker, instance): return isinstance(instance, Sequence) type_checker = Validator.TYPE_CHECKER.redefine("array", is_sequence) CustomValidator = extend(Validator, type_checker=type_checker) @fixture def merge_validator(request, json_schema_merge): return CustomValidator( json_schema_merge, resolver=RefResolver( 'file://localhost/' + schema_dir.replace('\\', '/') + '/', json_schema_merge), ) @fixture(scope='session') def ioloop_patch(): from nbdime.webapp.nbdimeserver import asyncio_patch
from ..exceptions import ValidationError def _nullable(base_validator): def _validator(validator, v, instance, schema): if instance is None and schema.get('nullable'): return for error in base_validator(validator, v, instance, schema): yield error return _validator _Validator = validators.extend( Draft4Validator, { 'type': _nullable(Draft4Validator.VALIDATORS['type']), 'enum': _nullable(Draft4Validator.VALIDATORS['enum']), }, ) class SchemaValidator(object): def __init__(self, schema, format_checker=None): self._validator = _Validator(schema, format_checker=format_checker) def validate(self, instance, schema): errors = list(self._validator.iter_errors(instance, schema)) if errors: raise ValidationError(errors)
from openapi_spec_validator import validate_spec from jsonschema import Draft4Validator, Draft7Validator, RefResolver from jsonschema.validators import extend from jsonschema.exceptions import ValidationError import pytest from unittest import TestCase from yaml import safe_load from utils.invalids import INVALID_SYSTEM_PROFILES from utils.valids import VALID_SYSTEM_PROFILES CustomDraft4Validator = extend( Draft4Validator, {"x-propertyNames": Draft7Validator.VALIDATORS.get("propertyNames")} ) class SystemProfileTests(TestCase): def setUp(self): super().setUp() with open('schemas/system_profile/v1.yaml') as spec_yaml: self.specification = safe_load(spec_yaml) self.resolver = RefResolver.from_schema(self.specification) def test_system_profile_invalids(self): for system_profile in INVALID_SYSTEM_PROFILES: with self.subTest(system_profile=system_profile): with pytest.raises(ValidationError): CustomDraft4Validator(self.specification["$defs"]["SystemProfile"], resolver=self.resolver).validate(system_profile) def test_system_profile_valids(self): for system_profile in VALID_SYSTEM_PROFILES:
# # You should have received a copy of the GNU General Public License # along with CERN Analysis Preservation Framework; if not, write to the # Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, # MA 02111-1307, USA. # # In applying this license, CERN does not # waive the privileges and immunities granted to it by virtue of its status # as an Intergovernmental Organization or submit itself to any jurisdiction. """Deposit validators.""" import os from jsonschema import Draft4Validator from jsonschema.validators import extend from cap.modules.experiments.validators import (validate_cadi_id, validate_cms_trigger, validate_das_path) deposit_validators = dict(Draft4Validator.VALIDATORS) if not os.environ.get("CAP_CMS_VALIDATION_DISABLE"): deposit_validators['x-validate-cms-trigger'] = validate_cms_trigger deposit_validators['x-validate-das-path'] = validate_das_path # deposit_validators['x-validate-cadi-id'] = validate_cadi_id DepositValidator = extend(Draft4Validator, validators=deposit_validators) NoRequiredValidator = extend(DepositValidator, {'required': None})
def extend_with_default(validator_class): """ Helper function for validate(..., inject_defaults=True) This code was adapted from the jsonschema FAQ: http://python-jsonschema.readthedocs.org/en/latest/faq/ Unlike extend_with_default_without_validation(), below, this function does not bother to convert the defaults to commented YAML types before injecting them. (The results of this function are not meant for pretty-printing.) """ validate_properties = validator_class.VALIDATORS["properties"] validate_items = validator_class.VALIDATORS["items"] def _set_property_defaults(properties, instance): for property_name, subschema in properties.items(): if "default" in subschema: default = copy.deepcopy(subschema["default"]) if isinstance(default, dict): default = _Dict(default) default.from_default = True instance.setdefault(property_name, default) def set_defaults_and_validate(validator, properties, instance, schema): _set_property_defaults(properties, instance) for error in validate_properties(validator, properties, instance, schema): yield error def fill_in_default_array_items(validator, items_schema, instance, schema): if "default" in items_schema and isinstance(items_schema["default"], Mapping): new_items = [] for item in instance: if not isinstance(item, Mapping): new_items.append(item) else: default = copy.deepcopy(items_schema["default"]) default = _Dict(default) if item == {}: # FIXME: Instead of a simple bool, it would be better to specify # WHICH properties in this dict were copied from the default value. default.from_default = True default.update(item) new_items.append(default) instance.clear() instance.extend(new_items) # Descend into array list for error in validate_items(validator, items_schema, instance, schema): yield error def check_required(validator, required, instance, schema): # We only check 'required' properties that don't have specified defaults for prop in required: if prop in instance: continue if prop not in schema['properties'] or 'default' not in schema[ 'properties'][prop]: yield ValidationError( "%r is a required property and has no default value in your schema" % prop) return validators.extend( validator_class, { "properties": set_defaults_and_validate, "items": fill_in_default_array_items, "required": check_required })
else: local_base_uri = local_base_uri[5:] try: local_filepath = os.path.abspath(os.path.join(local_base_uri, '../'+ref)) local_schema = load_schema(local_filepath) schema_id = local_schema.get('$id', '') if schema_id: validator.resolver.store[schema_id] = local_schema except FileNotFoundError: pass return Draft7Validator.VALIDATORS['$ref'](validator, ref, instance, schema) STIXValidator = extend(Draft7Validator, {'$ref': ref_store}) def load_validator(schema_path, schema): """Create a JSON schema validator for the given schema. Args: schema_path: The filename of the JSON schema. schema: A Python object representation of the same schema. Returns: An instance of Draft7Validator. """ global SCHEMA_STORE
max_deviation): yield error validators = { 'required': _required, 'x-required': _required, 'file': _file, 'x-file': _file, 'fkey': _fkey, 'x-fkey': _fkey, 'unique': _unique, 'x-unique': _unique, 'unique-together': _unique, 'x-unique-together': _unique, 'x-check-geolocation': _check_geolocation, } _CustomDraft4Validator = extend(Draft4Validator, validators, str('draft4')) class CustomDraft4Validator(_CustomDraft4Validator): def __init__(self, *args, **kwargs): self.datastore = kwargs.pop('datastore') self.upload_path = kwargs.pop('upload_path') self.index = kwargs.pop('index', None) self.doc_type = kwargs.pop('doc_type', None) super(CustomDraft4Validator, self).__init__(*args, **kwargs)
def get_json(path): with open(path) as f: return json.load(f) def recommended_draft4(validator, required, instance, schema): if not validator.is_type(instance, "object"): return for prop in required: if prop not in instance: yield ValidationError("%r is a recommended property" % prop) Draft4ValidatorExtended = extend( validator=Draft4Validator, validators={u"recommended": recommended_draft4}, version="draft4e" ) def is_integer(text): try: int(text) return True except ValueError: return False def validate_item(item): file_name = SCHEMA_PATH + item['type'].rsplit('/', 1)[1] schema = get_json(file_name + '.json')
def extend_with_default_without_validation(validator_class, include_yaml_comments=False, yaml_indent=2): validate_properties = validator_class.VALIDATORS["properties"] validate_items = validator_class.VALIDATORS["items"] def set_default_object_properties(validator, properties, instance, schema): for property, subschema in properties.items(): if instance == "{{NO_DEFAULT}}": continue if "default" in subschema: default = copy.deepcopy(subschema["default"]) if isinstance(default, list): try: # Lists of numbers should use 'flow style' # and so should lists-of-lists of numbers # (e.g. bounding boxes like [[0,0,0],[1,2,3]]) if (subschema["items"]["type"] in ("integer", "number") or (subschema["items"]["type"] == "array" and subschema["items"]["items"]["type"] in ("integer", "number"))): default = flow_style(default) except KeyError: pass if include_yaml_comments and isinstance(default, dict): default = CommentedMap(default) # To keep track of the current indentation level, # we just monkey-patch this member onto the dict. default.key_indent = instance.key_indent + yaml_indent default.from_default = True if include_yaml_comments and isinstance(default, list): if not isinstance(default, CommentedSeq): default = CommentedSeq(copy.copy(default)) # To keep track of the current indentation level, # we just monkey-patch this member onto the dict. default.key_indent = instance.key_indent + yaml_indent default.from_default = True if property not in instance: instance[property] = default else: if property not in instance: instance[property] = "{{NO_DEFAULT}}" if include_yaml_comments and "description" in subschema: comment = '\n' + subschema["description"] if comment[-1] == '\n': comment = comment[:-1] instance.yaml_set_comment_before_after_key( property, comment, instance.key_indent) for _error in validate_properties(validator, properties, instance, schema): # Ignore validation errors pass def fill_in_default_array_items(validator, items, instance, schema): if include_yaml_comments and items["type"] == "object": new_items = [] for item in instance: new_item = CommentedMap(item) new_item.key_indent = instance.key_indent + yaml_indent new_items.append(new_item) instance.clear() instance.extend(new_items) # Descend into array list for _error in validate_items(validator, items, instance, schema): # Ignore validation errors pass def ignore_required(validator, required, instance, schema): return return validators.extend( validator_class, { "properties": set_default_object_properties, "items": fill_in_default_array_items, "required": ignore_required })