예제 #1
0
파일: spec.py 프로젝트: obmarg/bravado-core
    def descend(fragment, path, visited_refs):

        if is_ref(fragment):
            ref_dict = fragment
            ref = fragment['$ref']
            attach_scope(ref_dict, resolver)

            # Don't recurse down already visited refs. A ref is not unique
            # by its name alone. Its scope (attached above) is part of the
            # equivalence comparison.
            if ref_dict in visited_refs:
                log.debug('Already visited %s' % ref)
                return

            visited_refs.append(ref_dict)
            with resolver.resolving(ref) as target:
                descend(target, path, visited_refs)
                return

        # fragment is guaranteed not to be a ref from this point onwards
        if is_dict_like(fragment):
            for key, value in iteritems(fragment):
                fire_callbacks(fragment, key, path + [key])
                descend(fragment[key], path + [key], visited_refs)

        elif is_list_like(fragment):
            for index in range(len(fragment)):
                fire_callbacks(fragment, index, path + [str(index)])
                descend(fragment[index], path + [str(index)], visited_refs)
예제 #2
0
    def descend(fragment, path, visited_refs):

        if is_ref(fragment):
            ref_dict = fragment
            ref = fragment['$ref']
            attach_scope(ref_dict, resolver)

            # Don't recurse down already visited refs. A ref is not unique
            # by its name alone. Its scope (attached above) is part of the
            # equivalence comparison.
            if ref_dict in visited_refs:
                log.debug('Already visited %s' % ref)
                return

            visited_refs.append(ref_dict)
            with resolver.resolving(ref) as target:
                descend(target, path, visited_refs)
                return

        # fragment is guaranteed not to be a ref from this point onwards
        if is_dict_like(fragment):
            if 'type' not in fragment:
                if 'properties' in fragment:
                    fragment['type'] = 'object'
                elif 'items' in fragment:
                    fragment['type'] = 'array'
            for key, value in iteritems(fragment):
                fire_callbacks(fragment, key, path + [key])
                descend(fragment[key], path + [key], visited_refs)

        elif is_list_like(fragment):
            for index in range(len(fragment)):
                fire_callbacks(fragment, index, path + [str(index)])
                descend(fragment[index], path + [str(index)], visited_refs)
예제 #3
0
def test_create_model_type_properly_extracts_model_name(deref_value, expected_inherits):
    swagger_spec = mock.Mock(
        name='swagger-spec',
        deref=lambda schema: deref_value if is_ref(schema) else schema,
    )
    model_type = create_model_type(
        swagger_spec=swagger_spec,
        model_name='Dog',
        model_spec={
            'type': 'object',
            'title': 'Dog',
            'allOf': [
                {
                    '$ref': '#/definitions/GenericPet'
                },
                {
                    'properties': {
                        'birth_date': {
                            'type': 'string',
                            'format': 'date',
                        },
                    },
                    'required': ['birth_date'],
                },
            ],
        }
    )
    assert model_type._inherits_from == expected_inherits
예제 #4
0
def discriminator_validator(swagger_spec, validator, discriminator_attribute,
                            instance, schema):
    """
    Validates instance against the schema defined by the discriminator attribute.

    [Swagger 2.0 Schema Object](http://swagger.io/specification/#schemaObject) allows discriminator field to be defined.
    discriminator field defines the attribute that will be used to discriminate the object type.

    NOTE: discriminator_validator assumes that discriminator_attribute is not None or empty

    :param swagger_spec: needed for access to deref()
    :type swagger_spec: :class:`bravado_core.spec.Spec`
    :param validator: Validator class used to validate the object
    :type validator: :class: `Swagger20Validator` or
        :class: `jsonschema.validators.Draft4Validator`
    :param discriminator_attribute: name of the discriminator attribute
    :type discriminator_attribute: str
    :param instance: object instance value
    :type instance: dict
    :param schema: swagger spec for the object
    :type schema: dict
    """

    discriminator_value = instance[discriminator_attribute]
    if discriminator_value not in swagger_spec.spec_dict['definitions']:
        raise ValidationError(message='\'{}\' is not a recognized schema'.
                              format(discriminator_value))

    if discriminator_value == schema['x-model']:
        return

    new_schema = deepcopy(
        swagger_spec.deref(
            swagger_spec.spec_dict['definitions'][discriminator_value]))
    if 'allOf' not in new_schema:
        raise ValidationError(
            message='discriminated schema \'{}\' must inherit from \'{}\''.
            format(discriminator_value, schema['x-model']))

    schemas_to_remove = [
        s for s in new_schema['allOf']
        if is_ref(s) and swagger_spec.deref(s) == schema
    ]
    if not schemas_to_remove:
        # Not checking against len(schemas_to_remove) > 1 because it should be prevented by swagger spec validation
        raise ValidationError(
            message='discriminated schema \'{}\' must inherit from \'{}\''.
            format(discriminator_value, schema['x-model']))

    # Remove the current schema from the allOf list in order to avoid unbounded recursion
    # (the current object is already validated against schema)
    new_schema['allOf'].remove(schemas_to_remove[0])

    from bravado_core.validate import validate_object  # Local import due to circular dependency
    validate_object(swagger_spec=swagger_spec,
                    object_spec=new_schema,
                    value=instance)
예제 #5
0
def test_ensure_model_spec_contains_reference_if_fully_dereference_is_not_enabled(polymorphic_dict):
    spec = Spec.from_dict(
        spec_dict=polymorphic_dict,
        config={'internally_dereference_refs': False},
        origin_url='',
    )

    # Ensure that Dog model has reference to GenericPet
    assert any(
        is_ref(all_of_item) and all_of_item['$ref'] == '#/definitions/GenericPet'
        for all_of_item in spec.definitions['Dog']._model_spec['allOf']
    )
예제 #6
0
def test_ensure_model_spec_does_not_contain_references_if_fully_dereference_is_enabled(
        polymorphic_dict):
    spec = Spec.from_dict(
        spec_dict=polymorphic_dict,
        config={'internally_dereference_refs': True},
        origin_url='',
    )

    # Ensure that no reference are present
    assert all(not is_ref(all_of_item)
               for all_of_item in spec.definitions['Dog']._model_spec['allOf'])

    # Ensure that GenericPet is part of the allOf items of Dog
    assert any(all_of_item == spec.definitions['GenericPet']._model_spec
               for all_of_item in spec.definitions['Dog']._model_spec['allOf'])
예제 #7
0
    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
예제 #8
0
    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
예제 #9
0
    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
예제 #10
0
        def _rename_references_descend(value):
            if is_ref(value):
                return {
                    '$ref': reference_renaming_mapping.get(value['$ref'], value['$ref'])
                }
            elif is_dict_like(value):
                return {
                    key: _rename_references_descend(value=subval)
                    for key, subval in iteritems(value)
                }

            elif is_list_like(value):
                return [
                    _rename_references_descend(value=subval)
                    for index, subval in enumerate(value)
                ]

            else:
                return value
예제 #11
0
        def wrapper(fragment, *args, **kwargs):
            is_reference = is_ref(fragment)
            if is_reference:
                ref = fragment['$ref']
                attach_scope(fragment, resolver)
                with resolver.resolving(ref) as target:
                    if id(target) in cache:
                        log.debug('Already visited %s', ref)
                        return

                    func(target, *args, **kwargs)
                    return

            # fragment is guaranteed not to be a ref from this point onwards
            fragment_id = id(fragment)

            if fragment_id in cache:
                log.debug('Already visited id %d', fragment_id)
                return

            cache.add(id(fragment))
            func(fragment, *args, **kwargs)
예제 #12
0
    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
예제 #13
0
        def wrapper(fragment, *args, **kwargs):
            is_reference = is_ref(fragment)
            if is_reference:
                ref = fragment['$ref']
                attach_scope(fragment, resolver)
                with resolver.resolving(ref) as target:
                    if id(target) in cache:
                        log.debug('Already visited %s', ref)
                        return

                    func(target, *args, **kwargs)
                    return

            # fragment is guaranteed not to be a ref from this point onwards
            fragment_id = id(fragment)

            if fragment_id in cache:
                log.debug('Already visited id %d', fragment_id)
                return

            cache.add(id(fragment))
            func(fragment, *args, **kwargs)
예제 #14
0
        def wrapper(fragment, json_reference=None):
            if json_reference is None:
                json_reference = '{}#'.format(spec_resolver.resolution_scope)

            is_reference = is_ref(fragment)
            if is_reference:
                ref = fragment['$ref']
                attach_scope(fragment, spec_resolver)
                with spec_resolver.resolving(ref) as target:
                    if id(target) in cache:
                        log.debug('Already visited %s', ref)
                        return

                    json_reference = spec_resolver.resolution_scope
                    if '#' not in json_reference:
                        # If $ref points to a file make sure that the fragment sign is present
                        json_reference = '{}#'.format(json_reference)

                    func(
                        fragment=target,
                        json_reference=json_reference,
                    )
                    return

            # fragment is guaranteed not to be a ref from this point onwards
            fragment_id = id(fragment)

            if fragment_id in cache:
                log.debug('Already visited id %d', fragment_id)
                return

            cache.add(id(fragment))
            func(
                fragment=fragment,
                json_reference=json_reference,
            )
예제 #15
0
    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
예제 #16
0
    def descend(fragment, path=None, visited_refs=None):
        """
        :param fragment: node in spec_dict
        :param path: list of strings that form the current path to fragment
        :param visited_refs: list of visted ref_dict
        """
        path = path or []
        visited_refs = visited_refs or []

        if is_ref(fragment):
            ref_dict = fragment
            ref = fragment['$ref']
            attach_scope(ref_dict, resolver)

            # Don't recurse down already visited refs. A ref is not unique
            # by its name alone. Its scope (attached above) is part of the
            # equivalence comparison.
            if ref_dict in visited_refs:
                log.debug('Already visited %s' % ref)
                return

            visited_refs.append(ref_dict)
            with resolver.resolving(ref) as target:
                descend(target, path, visited_refs)
                return

        # fragment is guaranteed not to be a ref from this point onwards
        if is_dict_like(fragment):
            for key, value in iteritems(fragment):
                fire_callbacks(fragment, key, path + [key])
                descend(fragment[key], path + [key], visited_refs)

        elif is_list_like(fragment):
            for index in range(len(fragment)):
                fire_callbacks(fragment, index, path + [str(index)])
                descend(fragment[index], path + [str(index)], visited_refs)