Example #1
0
 def test_merge_simple_dicts_with_precedence(self):
     a = {'a': 'value', 'ab': 'overwritten'}
     b = {'b': 'other value', 'ab': 'keep'}
     assert utils.merge(a, b) == {
         'a': 'value',
         'b': 'other value',
         'ab': 'keep'
     }
Example #2
0
 def wrapper(func):
     doc = {
         'responses': {
             code: (description, [fields]) if as_list else (
                 description, fields)
         },
         '__mask__': kwargs.get('mask', True),
     }
     func.__apidoc__ = merge(getattr(func, '__apidoc__', {}), doc)
     return marshal_with(fields, ordered=self.ordered, **kwargs)(func)
Example #3
0
 def wrapper(func):
     doc = {
         'responses': {
             code: (description, [fields]) if as_list else (description, fields)
         },
         '__mask__': kwargs.get('mask', True),  # Mask values can't be determined outside app context
     }
     func.__apidoc__ = merge(getattr(func, '__apidoc__', {}), doc)
     kwargs['strict'] = strict
     return marshal_with(fields, **kwargs)(func)
Example #4
0
def add_response_doc(func, fields, as_list, code=200, description=None, **kwargs):
    doc = {
        'responses': {
            code: (description, [fields]) if as_list else (description, fields)
        },
        '__mask__': kwargs.get('mask', True),  # Mask values can't be determined outside app context
    }
    func.__apidoc__ = merge(getattr(func, '__apidoc__', {}), doc)

    return func
 def _handle_api_doc(self, cls, doc):
     if doc is False:
         cls.__apidoc__ = False
         return
     ##unshortcut_params_description(doc)
     ##for key in 'get', 'post', 'put', 'delete', 'options', 'head', 'patch':
     ##    if key in doc:
     ##        if doc[key] is False:
     ##            continue
     ##        unshortcut_params_description(doc[key])
     cls.__apidoc__ = merge(getattr(cls, '__apidoc__', {}), doc)
Example #6
0
 def _handle_api_doc(self, cls, doc):
     if doc is False:
         cls.__apidoc__ = False
         return
     ##unshortcut_params_description(doc)
     ##for key in 'get', 'post', 'put', 'delete', 'options', 'head', 'patch':
     ##    if key in doc:
     ##        if doc[key] is False:
     ##            continue
     ##        unshortcut_params_description(doc[key])
     cls.__apidoc__ = merge(getattr(cls, '__apidoc__', {}), doc)
Example #7
0
 def test_recursions_with_empty(self):
     a = {}
     b = {
         'b': 'other value',
         'ab': 'keep',
         'nested_b': {
             'b': 'nested'
         },
         'nested_a_b': {
             'b': 'b only',
             'ab': 'keep'
         }
     }
     self.assertEqual(utils.merge(a, b), b)
 def _handle_api_doc(self, cls, doc):
     if doc is False:
         cls.__apidoc__ = False
         return
     ##unshortcut_params_description(doc)
     ##handle_deprecations(doc)
     ##for key in 'get', 'post', 'put', 'delete', 'options', 'head', 'patch':
     ##    if key in doc:
     ##        if doc[key] is False:
     ##            continue
     ##        unshortcut_params_description(doc[key])
     ##        handle_deprecations(doc[key])
     ##        if 'expect' in doc[key] and not isinstance(doc[key]['expect'], (list, tuple)):
     ##            doc[key]['expect'] = [doc[key]['expect']]
     cls.__apidoc__ = merge(getattr(cls, '__apidoc__', {}), doc)
 def _handle_api_doc(self, cls, doc):
     if doc is False:
         cls.__apidoc__ = False
         return
     ##unshortcut_params_description(doc)
     ##handle_deprecations(doc)
     ##for key in 'get', 'post', 'put', 'delete', 'options', 'head', 'patch':
     ##    if key in doc:
     ##        if doc[key] is False:
     ##            continue
     ##        unshortcut_params_description(doc[key])
     ##        handle_deprecations(doc[key])
     ##        if 'expect' in doc[key] and not isinstance(doc[key]['expect'], (list, tuple)):
     ##            doc[key]['expect'] = [doc[key]['expect']]
     cls.__apidoc__ = merge(getattr(cls, '__apidoc__', {}), doc)
Example #10
0
 def test_recursions(self):
     a = {
         'a': 'value',
         'ab': 'overwritten',
         'nested_a': {
             'a': 'nested'
         },
         'nested_a_b': {
             'a': 'a only',
             'ab': 'overwritten'
         }
     }
     b = {
         'b': 'other value',
         'ab': 'keep',
         'nested_b': {
             'b': 'nested'
         },
         'nested_a_b': {
             'b': 'b only',
             'ab': 'keep'
         }
     }
     self.assertEqual(utils.merge(a, b), {
         'a': 'value',
         'b': 'other value',
         'ab': 'keep',
         'nested_a': {
             'a': 'nested'
         },
         'nested_b': {
             'b': 'nested'
         },
         'nested_a_b': {
             'a': 'a only',
             'b': 'b only',
             'ab': 'keep'
         }
     })
 def _handle_api_doc(self, cls, doc):
     if doc is False:
         cls.__apidoc__ = False
         return
     cls.__apidoc__ = merge(getattr(cls, '__apidoc__', {}), doc)
def doc_endpoint(
        fn_name,
        fn_args,
        schema=None,
        schema_view=None,
        description=None,
        search_parsers=None,
        search_params_doc=None,
        parser=None,
        params=None,
        permissions=None,
        is_collection=None,
        **kwargs):
    """
    Generate a Swagger 2.0 compliant documentation dict that describes an API endpoint operation and its parameters.
    :param fn_name: The name of the `APIResource`'s method that implements this operation.
    The naming of this method is used to derive the container type of the return value.
    :param fn_args: A list of the parameter names for the `APIResource`'s method.
    :param schema: The `APISchema` class representing the Swagger response model.
    :param schema_view: The name of the schema view for this `schema`, optional.
    It's combined with the `schema`'s name to form the name of the Swagger model.
    :param description: A description of this API operation that will be shown as "Implementation Notes", optional.
    :param search_parsers: One or more search related parsers describing the operation's input parameters, optional.
    :param search_params_doc: A documentation dict describing the components of a search query string, optional.
    :param parser: The parser describing the operation's input parameters, optional.
    :param params: An explicit list of Swagger documentation input params to include alongside the others.
    :param permissions: One or more `Permission`s describing the `Need`s of this operation, optional.
    See `doc_response_permissions`.
    :param is_collection: The Swagger response is represented as a collection of models if True or as a single model
    if False. The default is `None` which means the collection type will be derived from `fn_name`.
    :param kwargs: A dict of additional information to merge into the final documentation dict.
    :return: The final documentation dict
    """
    params_list = []
    parser_list = []

    schema_model = None

    if schema:
        # Resolve the schema model name, taking the schema view into account.
        schema_model = _resolve_schema_name(schema, schema_view)

        # Determine if the response is a single model or a collection of them
        # If a collection, the name of the schema model will put into a list so that Swagger knows its an array.
        if is_collection is not None:
            schema_model = [schema_model] if is_collection else schema_model
        elif '_many' in fn_name or '_all' in fn_name:
            schema_model = [schema_model]

    # Accumulate the common params for this resource endpoint
    if 'id' in fn_args:
        params_list.append(doc_param_id())

    if search.SearchConfig.FIELD_SELECTORS_PARAM in fn_args:
        params_list.append(doc_param_selectors())

    if search_params_doc:
        params_list.append(doc_param_search_query(search_params_doc=search_params_doc))

    if params:
        params_list.append(params)

    # Accumulate the common parsers for this resource endpoint
    if search_parsers:
        parser_list.extend(search_parsers)

    if parser:
        parser_list.append(parser)

    # Resolve all parser params and combine them with the others to create a single params dict
    params = _resolve_params(params_list=params_list, parser_list=parser_list)

    # Build the final documentation dict
    doc = dict(model=schema_model) if schema_model else dict()
    # doc['id'] = fn_name  # Use the function's name as the operation's identifier (this value is also the "action")

    if params:
        doc['params'] = params

    if description:
        doc['description'] = description

    doc.update(kwargs)

    if permissions:
        doc['permissions'] = doc_permissions(*permissions)

        # Recursively merge the response permissions into the documentation dict
        doc = merge(doc, doc_response_permissions(*permissions))

    return doc
 def merge_apidoc(from_field, to_field):
     if hasattr(from_field, '__apidoc__'):
         to_field.__apidoc__ = merge(getattr(to_field, '__apidoc__', {}), from_field.__apidoc__)
def create_restplus_field_from_marshmallow_field(field, _nested_field_name=None):
    """Create a Restplus field from a Marshmallow field.
    The Restplus field contains the API documentation and metadata necessary to generate the Swagger documentation.
    :param field: A Marshmallow :class:`Field` instance.
    :param _nested_field_name: A derived name for the nested field -- for internal use only.
    :return: A Restplus field derived from its base `Raw` class.
    """
    def field_kwargs(f, *attrs):
        kwargs = {}
        for attr in attrs:
            if isinstance(attr, tuple):
                a, k = attr
            else:
                a = k = attr
            try:
                value = getattr(f, a)
            except AttributeError:
                value = f.metadata.get(a)
            kwargs[k] = value
        return kwargs

    def merge_apidoc(from_field, to_field):
        if hasattr(from_field, '__apidoc__'):
            to_field.__apidoc__ = merge(getattr(to_field, '__apidoc__', {}), from_field.__apidoc__)

    if not hasattr(field, '__apidoc__'):
        field.__apidoc__ = {}

    restplus_field = None
    restplus_cls = None
    restplus_attrs = ['attribute', 'default', 'description', 'required']  # default attrs

    if isinstance(field, ma.Arbitrary):
        restplus_cls = restplus_fields.Arbitrary
        restplus_attrs += ['min', 'max']
    elif isinstance(field, (ma.ExternalId, ma.Html)):
        restplus_cls = restplus_fields.String
    elif isinstance(field, (ma.Select, ma.Enum)):
        restplus_cls = restplus_fields.String
        restplus_attrs += [('choices', 'enum')]
    elif isinstance(field, ma.Fixed):
        restplus_cls = restplus_fields.Fixed
        restplus_attrs += ('min', 'max', 'decimals')
    elif isinstance(field, ma.Float):
        restplus_cls = restplus_fields.Float
        restplus_attrs += ['min', 'max']
    elif isinstance(field, ma.FormattedString):
        restplus_cls = restplus_fields.FormattedString
        restplus_attrs += ['min', 'max', 'src_str']
    elif isinstance(field, ma.Integer):
        restplus_cls = restplus_fields.Integer
        restplus_attrs += ['min', 'max']
    elif isinstance(field, ma.List):
        container_field = field.container
        merge_apidoc(field, container_field)
        restplus_field = restplus_fields.List(
            create_restplus_field_from_marshmallow_field(container_field),
            **field_kwargs(field, *restplus_attrs))
    elif isinstance(field, ma.Nested):
        nested = field.nested
        # Marshmallow supports self-referential nested fields by using the `self` identifier.
        if nested == 'self':
            # In this case, the nested field's schema class will be the same as its parent class
            nested = field.parent.__class__
            _nested_field_name = _nested_field_name or nested.resolve_schema_name()

        # Support Swagger array type documentation
        field.__apidoc__ = merge(field.__apidoc__, {'as_list': field.many})

        nested_proxy = APIModelProxy(_nested_field_name or field.schema_name)
        restplus_cls = restplus_fields.Nested
        restplus_attrs = ('attribute', 'default', 'allow_null')  # set attrs explicitly
        restplus_field = restplus_fields.Nested(nested=nested_proxy, **field_kwargs(field, *restplus_attrs))
    elif isinstance(field, ma.Raw):
        restplus_cls = restplus_fields.Raw
    elif isinstance(field, ma.Url):
        restplus_cls = restplus_fields.Url
        restplus_attrs += ('endpoint', 'absolute', 'scheme')
    elif isinstance(field, (ma.Function, ma.Method)):
        if field.return_field is not None:
            return_field = field.return_field
            merge_apidoc(field, return_field)
            _nested_field_name = field.return_field.schema.resolve_schema_name(field.schema_view) \
                if isinstance(field.return_field, ma.Nested) else None
            return create_restplus_field_from_marshmallow_field(return_field, _nested_field_name)
        restplus_cls = restplus_fields.Raw
    else:
        try:
            field_name = field.__class__.__name__
            restplus_cls = getattr(restplus_fields, field_name)
        except AttributeError:
            # logger.warn(
            #     "Unable to convert Marshmallow field to Restplus field. Unsupported type: {}".format(field_name))
            restplus_cls = restplus_fields.String

    if restplus_field is None:
        restplus_field = restplus_cls(**field_kwargs(field, *restplus_attrs)) if restplus_cls else None

    # Denote deprecated fields
    if 'deprecated' in field.metadata:
        field.__apidoc__['deprecated'] = field.metadata['deprecated']

    # Include additional documentation metadata based on field type
    if isinstance(field, ma.Enum):
        field.__apidoc__['enum_cls'] = field.enum_cls
    elif isinstance(field, ma.Html):
        field.__apidoc__['has_html'] = True

    # Copy Swagger documentation over to restplus field
    if hasattr(field, '__apidoc__'):
        restplus_field.__apidoc__ = field.__apidoc__

    return restplus_field
Example #15
0
 def test_merge_simple_dicts_without_precedence(self):
     a = {'a': 'value'}
     b = {'b': 'other value'}
     self.assertEqual(utils.merge(a, b), {'a': 'value', 'b': 'other value'})
Example #16
0
 def test_merge_simple_dicts_with_precedence(self):
     a = {'a': 'value', 'ab': 'overwritten'}
     b = {'b': 'other value', 'ab': 'keep'}
     self.assertEqual(utils.merge(a, b), {'a': 'value', 'b': 'other value', 'ab': 'keep'})