Exemplo n.º 1
0
    def build(self):
        self._validate_spec()

        model_discovery(self)

        if self.config['internally_dereference_refs']:
            # Avoid to evaluate is_ref every time, no references are possible at this time
            self.deref = lambda ref_dict: ref_dict
            self._internal_spec_dict = self.deref_flattened_spec

        for user_defined_format in self.config['formats']:
            self.register_format(user_defined_format)

        self.resources = build_resources(self)

        self.api_url = build_api_serving_url(self.spec_dict, self.origin_url)
Exemplo n.º 2
0
def test_model_discovery_flow_with_ref_dereference(mock__run_post_processing,
                                                   minimal_swagger_dict):
    spec = Spec(
        spec_dict=minimal_swagger_dict,
        config={
            'internally_dereference_refs': True,
        },
        origin_url='',
    )
    model_discovery(swagger_spec=spec)

    # _run_post_processing is called 3 times
    # 1. post processing on initial specs
    # 2. post processing on on bravado_core.spec_flattening.flattened_spec
    # 3. post processing to rebuild definitions to remove possible references in the model specs
    assert mock__run_post_processing.call_count == 3
Exemplo n.º 3
0
def flattened_spec(swagger_spec, marshal_uri_function=_marshal_uri):
    """
    Flatten Swagger Specs description into an unique and JSON serializable document.
    The flattening injects in place the referenced [path item objects](https://swagger.io/specification/#pathItemObject)
    while it injects in '#/parameters' the [parameter objects](https://swagger.io/specification/#parameterObject),
    in '#/definitions' the [schema objects](https://swagger.io/specification/#schemaObject) and in
    '#/responses' the [response objects](https://swagger.io/specification/#responseObject).

    Note: the object names in '#/definitions', '#/parameters' and '#/responses' are evaluated by
    ``marshal_uri_function``, the default method takes care of creating unique names for all the used references.
    Since name clashing are still possible take care that a warning could be filed.
    If it happen please report to us the specific warning text and the specs that generated it.
    We can work to improve it and in the mean time you can "plug" a custom marshalling function.

    Note: https://swagger.io/specification/ has been update to track the latest version of the Swagger/OpenAPI specs.
    Please refer to https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#responseObject for the
    most recent Swagger 2.0 specifications.

    WARNING: In the future releases all the parameters except swagger_spec and marshal_uri_function will be removed.
    Please make sure to use only those two parameters.
    Until the deprecation is not effective you can still pass all the parameters but it's strongly discouraged.

    :param swagger_spec: bravado-core Spec object
    :type swagger_spec: bravado_core.spec.Spec
    :param marshal_uri_function: function used to marshal uris in string suitable to be keys in Swagger Specs.
    :type marshal_uri_function: Callable with the same signature of ``_marshal_uri``

    :return: Flattened representation of the Swagger Specs
    :rtype: dict
    """
    spec_url = swagger_spec.origin_url

    if spec_url is None:
        warnings.warn(
            message=
            'It is recommended to set origin_url to your spec before flattering it. '
            'Doing so internal paths will be hidden, reducing the amount of exposed information.',
            category=Warning,
        )

    known_mappings = {
        object_type.get_root_holder(): {}
        for object_type in ObjectType if object_type.get_root_holder()
    }

    # Define marshal_uri method to be used by descend
    marshal_uri = functools.partial(
        marshal_uri_function,
        origin_uri=urlparse(spec_url) if spec_url else None,
    )

    # Avoid object attribute extraction during descend
    spec_resolver = swagger_spec.resolver
    resolve = swagger_spec.resolver.resolve

    default_type_to_object = swagger_spec.config[
        'default_type_to_object'] if swagger_spec else True

    def descend(value):
        if is_ref(value):
            # Update spec_resolver scope to be able to dereference relative specs from a not root file
            with in_scope(spec_resolver, value):
                uri, deref_value = resolve(value['$ref'])
                object_type = determine_object_type(
                    object_dict=deref_value,
                    default_type_to_object=default_type_to_object,
                )

                known_mapping_key = object_type.get_root_holder()
                if known_mapping_key is None:
                    return descend(value=deref_value)
                else:
                    uri = urlparse(uri)
                    if uri not in known_mappings.get(known_mapping_key, {}):
                        # The placeholder is present to interrupt the recursion
                        # during the recursive traverse of the data model (``descend``)
                        known_mappings[known_mapping_key][uri] = None

                        known_mappings[known_mapping_key][uri] = descend(
                            value=deref_value)

                    return {
                        '$ref':
                        '#/{}/{}'.format(known_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

    # Create internal copy of spec_dict to avoid external dict pollution
    resolved_spec = descend(value=copy.deepcopy(swagger_spec.spec_dict))

    # If definitions were identified by the swagger_spec object
    if swagger_spec.definitions is not None:
        # local imports due to circular dependency
        from bravado_core.spec import Spec
        from bravado_core.model import model_discovery

        # Run model-discovery in order to tag the models available in known_mappings['definitions']
        # This is a required step that removes duplications of models due to the presence of models
        # in swagger.json#/definitions and the equivalent models generated by flattening
        model_discovery(
            Spec(spec_dict={
                'definitions': {
                    marshal_uri(uri): value
                    for uri, value in iteritems(known_mappings['definitions'])
                },
            }, ))

        flatten_models = {
            # schema objects might not have a "type" set so they won't be tagged as models
            definition.get(MODEL_MARKER)
            for definition in itervalues(known_mappings['definitions'])
        }

        for model_name, model_type in iteritems(swagger_spec.definitions):
            if model_name in flatten_models:
                continue
            model_url = urlparse(model_type._json_reference)
            known_mappings['definitions'][model_url] = descend(
                value=model_type._model_spec)

    for mapping_key, mappings in iteritems(known_mappings):
        _warn_if_uri_clash_on_same_marshaled_representation(
            uri_schema_mappings=mappings,
            marshal_uri=marshal_uri,
        )
        if len(mappings) > 0:
            resolved_spec.update({
                mapping_key: {
                    marshal_uri(uri): value
                    for uri, value in iteritems(mappings)
                }
            })

    return resolved_spec