def ref_validator( validator, # type: Draft4Validator ref, # type: typing.Any instance, # type: typing.Any schema, # type: JSONDict ): # type: (...) -> typing.Generator[ValidationError, None, None] """When validating a request or response that contain $refs, jsonschema's RefResolver only contains scope (RefResolver._scopes_stack) for the request_spec or response_spec that it is fed. This scope doesn't contain the full scope built when ingesting the spec from its root (#/ in swagger.json). So, we need to modify the behavior of ref validation to use the `x-scope` annotations that were created during spec ingestion (see model_discovery in bravado_core/model.py). :param validator: Validator class used to validate the object :type validator: :class: `Swagger20Validator` or :class: `jsonschema.validators.Draft4Validator` :param ref: the target of the ref. eg. #/foo/bar/Baz :type ref: string :param instance: the object being validated :param schema: swagger spec that contains the ref. eg {'$ref': '#/foo/bar/Baz'} :type schema: dict """ # This is a copy of jsonschema._validators.ref(..) with the # in_scope(..) context manager applied before any refs are resolved. resolve = getattr(validator.resolver, "resolve") if resolve is None: with in_scope(validator.resolver, schema): with validator.resolver.resolving(ref) as resolved: for error in validator.descend(instance, resolved): yield error else: with in_scope(validator.resolver, schema): scope, resolved = validator.resolver.resolve(ref) validator.resolver.push_scope(scope) try: for error in validator.descend(instance, resolved): yield error finally: validator.resolver.pop_scope()
def ref_validator(validator, ref, instance, schema): """When validating a request or response that contain $refs, jsonschema's RefResolver only contains scope (RefResolver._scopes_stack) for the request_spec or response_spec that it is fed. This scope doesn't contain the full scope built when ingesting the spec from its root (#/ in swagger.json). So, we need to modify the behavior of ref validation to use the `x-scope` annotations that were created during spec ingestion (see post_process_spec in spec.py). :param validator: Validator class used to validate the object :type validator: :class: `Swagger20Validator` or :class: `jsonschema.validators.Draft4Validator` :param ref: the target of the ref. eg. #/foo/bar/Baz :type ref: string :param instance: the object being validated :param schema: swagger spec that contains the ref. eg {'$ref': '#/foo/bar/Baz'} :type schema: dict """ # This is a copy of jsonscehama._validators.ref(..) with the # in_scope(..) context manager applied before any refs are resolved. resolve = getattr(validator.resolver, "resolve") if resolve is None: with in_scope(validator.resolver, schema): with validator.resolver.resolving(ref) as resolved: for error in validator.descend(instance, resolved): yield error else: with in_scope(validator.resolver, schema): scope, resolved = validator.resolver.resolve(ref) validator.resolver.push_scope(scope) try: for error in validator.descend(instance, resolved): yield error finally: validator.resolver.pop_scope()
def _force_deref(self, ref_dict): """Dereference ref_dict (if it is indeed a ref) and return what the ref points to. :param ref_dict: {'$ref': '#/blah/blah'} :return: dereferenced value of ref_dict :rtype: scalar, list, dict """ if ref_dict is None or not is_ref(ref_dict): return ref_dict # Restore attached resolution scope before resolving since the # resolver doesn't have a traversal history (accumulated scope_stack) # when asked to resolve. with in_scope(self.resolver, ref_dict): _, target = self.resolver.resolve(ref_dict['$ref']) return target
def descend(self, value): if is_ref(value): # Update spec_resolver scope to be able to dereference relative specs from a not root file with in_scope(self.spec_resolver, value): uri, deref_value = self.resolve(value['$ref']) object_type = determine_object_type( object_dict=deref_value, default_type_to_object=self.default_type_to_object, ) known_mapping_key = object_type.get_root_holder() if known_mapping_key is None: return self.descend(value=deref_value) else: uri = urlparse(uri) if uri not in self.known_mappings.get( known_mapping_key, {}): # The placeholder is present to interrupt the recursion # during the recursive traverse of the data model (``descend``) self.known_mappings[known_mapping_key][uri] = None self.known_mappings[known_mapping_key][ uri] = self.descend(value=deref_value) return { '$ref': '#/{}/{}'.format(known_mapping_key, self.marshal_uri(uri)) } elif is_dict_like(value): return { key: self.descend(value=subval) for key, subval in iteritems(value) } elif is_list_like(value): return [ self.descend(value=subval) for index, subval in enumerate(value) ] else: return value
def deref(ref_dict, resolver): """Dereference ref_dict (if it is indeed a ref) and return what the ref points to. :param ref_dict: Something like {'$ref': '#/blah/blah'} :type ref_dict: dict :param resolver: Ref resolver used to do the de-referencing :type resolver: :class:`jsonschema.RefResolver` :return: de-referenced value of ref_dict :rtype: scalar, list, dict """ if ref_dict is None or not is_ref(ref_dict): return ref_dict ref = ref_dict['$ref'] with in_scope(resolver, ref_dict): with resolver.resolving(ref) as target: log.debug('Resolving %s', ref) return target
def descend(value): if is_ref(value): uri, deref_value = resolve(value['$ref']) # Update spec_resolver scope to be able to dereference relative specs from a not root file with in_scope(spec_resolver, {'x-scope': [uri]}): object_type = _determine_object_type(object_dict=deref_value) if object_type is _TYPE_PATH_ITEM: return descend(value=deref_value) else: mapping_key = _TYPE_PROPERTY_HOLDER_MAPPING.get( object_type, 'definitions') uri = urlparse(uri) if uri not in known_mappings.get(mapping_key, {}): # The placeholder is present to interrupt the recursion # during the recursive traverse of the data model (``descend``) known_mappings[mapping_key][uri] = None known_mappings[mapping_key][uri] = descend( value=deref_value) return { '$ref': '#/{}/{}'.format(mapping_key, marshal_uri(uri)) } elif is_dict_like(value): return { key: descend(value=subval) for key, subval in iteritems(value) } elif is_list_like(value): return [ descend(value=subval) for index, subval in enumerate(value) ] else: return value
def descend(value): if is_ref(value): uri, deref_value = resolve(value['$ref']) # Update spec_resolver scope to be able to dereference relative specs from a not root file with in_scope(spec_resolver, {'x-scope': [uri]}): object_type = _determine_object_type(object_dict=deref_value) if object_type is _TYPE_PATH_ITEM: return descend(value=deref_value) else: mapping_key = _TYPE_PROPERTY_HOLDER_MAPPING.get(object_type, 'definitions') uri = urlparse(uri) if uri not in known_mappings.get(mapping_key, {}): # The placeholder is present to interrupt the recursion # during the recursive traverse of the data model (``descend``) known_mappings[mapping_key][uri] = None known_mappings[mapping_key][uri] = descend(value=deref_value) return {'$ref': '#/{}/{}'.format(mapping_key, marshal_uri(uri))} elif is_dict_like(value): return { key: descend(value=subval) for key, subval in iteritems(value) } elif is_list_like(value): return [ descend(value=subval) for index, subval in enumerate(value) ] else: return value