Giter Club home page Giter Club logo

bravado-core's Introduction

https://github.com/Yelp/bravado-core/workflows/build/badge.svg?branch=master PyPi version Supported Python versions

bravado-core

About

bravado-core is a Python library that adds client-side and server-side support for the OpenAPI Specification v2.0.

Features

  • OpenAPI Specification schema validation
  • Marshaling, transformation, and validation of requests and responses
  • Models as Python classes or dicts
  • Custom formats for type conversion

Documentation

Documentation is available at readthedocs.org

Installation

$ pip install bravado-core

Related Projects

Development

Code is documented using Sphinx.
virtualenv is recommended to keep dependencies and libraries isolated.
tox is used for standardized testing.

Setup

# Run tests
tox

# Install git pre-commit hooks
.tox/py310/bin/pre-commit install

Contributing

  1. Fork it ( http://github.com/Yelp/bravado-core/fork )
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Add your modifications
  4. Add short summary of your modifications on CHANGELOG.rst
  5. Commit your changes (git commit -m "Add some feature")
  6. Push to the branch (git push origin my-new-feature)
  7. Create new Pull Request

License

Copyright (c) 2013, Digium, Inc. All rights reserved.
Copyright (c) 2014-2015, Yelp, Inc. All rights reserved.

Bravado is licensed with a BSD 3-Clause License.

bravado-core's People

Contributors

adamhadani avatar analogue avatar andreashug avatar arkq avatar asgillmor avatar asottile avatar bdowling avatar benbariteau avatar bplotnick avatar brycedrennan avatar bwarfield-amplify avatar chriskuehl avatar clarecat avatar cyprieng avatar djeebus avatar dnephin avatar hiromu avatar janekbaraniewski avatar jlumpe avatar jnb avatar jtwang avatar leedm777 avatar macisamuele avatar nickgaya avatar prat0318 avatar sjaensch avatar watterso avatar wting avatar wuub avatar zroadhouse-rmn avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bravado-core's Issues

User-defined formats should be tied to a Spec (not global)

Currently, user-defined formats are defined globally. This is not ideal when a python client process interfaces with more than one Swagger service with a different set of user-defined formats applying to each service. In the worst case, the same format name may be used with differing implementations.

Associate user-defined formats with a bravado_core.spec.Spec instead.

Creation of models with property named "self" fails.

When attempting to invoke an endpoint that creates a model with a "self" property, the following traceback is emitted:

Traceback (most recent call last):
  File "<ipython-input-14-acee5e06b3f1>", line 1, in <module>
    try: c.index.get_index().result()._links.self.href
  File "/home/jkrukoff/Documents/Services/unifi-go/.virtualenv/lib/python3.5/site-packages/bravado/http_future.py", line 77, in result
    self.response_callbacks)
  File "/home/jkrukoff/Documents/Services/unifi-go/.virtualenv/lib/python3.5/site-packages/bravado/http_future.py", line 115, in unmarshal_response
    operation)
  File "/home/jkrukoff/Documents/Services/unifi-go/.virtualenv/lib/python3.5/site-packages/bravado_core/response.py", line 110, in unmarshal_response
    op.swagger_spec, content_spec, content_value)
  File "/home/jkrukoff/Documents/Services/unifi-go/.virtualenv/lib/python3.5/site-packages/bravado_core/unmarshal.py", line 51, in unmarshal_schema_object
    return unmarshal_model(swagger_spec, schema_object_spec, value)
  File "/home/jkrukoff/Documents/Services/unifi-go/.virtualenv/lib/python3.5/site-packages/bravado_core/unmarshal.py", line 173, in unmarshal_model
    model_as_dict = unmarshal_object(swagger_spec, model_spec, model_value)
  File "/home/jkrukoff/Documents/Services/unifi-go/.virtualenv/lib/python3.5/site-packages/bravado_core/unmarshal.py", line 133, in unmarshal_object
    result[k] = unmarshal_schema_object(swagger_spec, prop_spec, v)
  File "/home/jkrukoff/Documents/Services/unifi-go/.virtualenv/lib/python3.5/site-packages/bravado_core/unmarshal.py", line 51, in unmarshal_schema_object
    return unmarshal_model(swagger_spec, schema_object_spec, value)
  File "/home/jkrukoff/Documents/Services/unifi-go/.virtualenv/lib/python3.5/site-packages/bravado_core/unmarshal.py", line 173, in unmarshal_model
    model_as_dict = unmarshal_object(swagger_spec, model_spec, model_value)
  File "/home/jkrukoff/Documents/Services/unifi-go/.virtualenv/lib/python3.5/site-packages/bravado_core/unmarshal.py", line 133, in unmarshal_object
    result[k] = unmarshal_schema_object(swagger_spec, prop_spec, v)
  File "/home/jkrukoff/Documents/Services/unifi-go/.virtualenv/lib/python3.5/site-packages/bravado_core/unmarshal.py", line 54, in unmarshal_schema_object
    return unmarshal_object(swagger_spec, schema_object_spec, value)
  File "/home/jkrukoff/Documents/Services/unifi-go/.virtualenv/lib/python3.5/site-packages/bravado_core/unmarshal.py", line 133, in unmarshal_object
    result[k] = unmarshal_schema_object(swagger_spec, prop_spec, v)
  File "/home/jkrukoff/Documents/Services/unifi-go/.virtualenv/lib/python3.5/site-packages/bravado_core/unmarshal.py", line 51, in unmarshal_schema_object
    return unmarshal_model(swagger_spec, schema_object_spec, value)
  File "/home/jkrukoff/Documents/Services/unifi-go/.virtualenv/lib/python3.5/site-packages/bravado_core/unmarshal.py", line 174, in unmarshal_model
    model_instance = model_type(**model_as_dict)
TypeError: <lambda>() got multiple values for argument 'self'

It appears that the model constructor depends on passing **kwargs in, which conflicts with the initial self parameter for the init method. This is problematic for me, as I'm building a API that uses JSON+HAL, which mandates that a property named self exist on all resources.

I've an attempt at a PR to resolve the issue, though don't feel like I've a deep enough understanding of the issue to have confidence in my changes.

Cannot Produce File: generate a validation error

Hi,
Thank you for validator, we have some trouble with it on type file, perhaps have you an idea.
When a method return a file,

app_1    |   File "/usr/lib/python3.5/site-packages/bottle_swagger.py", line 43, in __init__
app_1    |     self.swagger = Spec.from_dict(swagger_def)
app_1    |   File "/usr/lib/python3.5/site-packages/bravado_core/spec.py", line 152, in from_dict
app_1    |     spec.build()
app_1    |   File "/usr/lib/python3.5/site-packages/bravado_core/spec.py", line 159, in build
app_1    |     http_handlers=build_http_handlers(self.http_client))
app_1    |   File "/usr/lib/python3.5/site-packages/swagger_spec_validator/validator20.py", line 82, in validate_spec
app_1    |     http_handlers=http_handlers)
app_1    |   File "/usr/lib/python3.5/site-packages/swagger_spec_validator/common.py", line 22, in wrapper
app_1    |     sys.exc_info()[2])
app_1    |   File "/usr/lib/python3.5/site-packages/six.py", line 685, in reraise
app_1    |     raise value.with_traceback(tb)
app_1    |   File "/usr/lib/python3.5/site-packages/swagger_spec_validator/common.py", line 17, in wrapper
app_1    |     return method(*args, **kwargs)
app_1    |   File "/usr/lib/python3.5/site-packages/swagger_spec_validator/validator20.py", line 125, in validate_json
app_1    |     cls=Draft4Validator)
app_1    |   File "/usr/lib/python3.5/site-packages/swagger_spec_validator/ref_validators.py", line 34, in validate
app_1    |     instance_cls(schema, *args, **kwargs).validate(instance)
app_1    |   File "/usr/lib/python3.5/site-packages/jsonschema/validators.py", line 123, in validate
app_1    |     raise error
app_1    | swagger_spec_validator.common.SwaggerValidationError: {'schema': {'type': 'file'}, 'description': 'Returns the pet photo'} is not valid under any of the given schemas
app_1    | 
app_1    | Failed validating 'oneOf' in schema['properties']['paths']['patternProperties']['^/']['properties']['get']['properties']['responses']['patternProperties']['^([0-9]{3})$|^(default)$']:
app_1    |     {'oneOf': [{'$ref': '#/definitions/response'},
app_1    |                {'$ref': '#/definitions/jsonReference'}]}
app_1    | 
app_1    | On instance['paths']['/pets/{petName}/photos/{id}']['get']['responses']['default']:
app_1    |     {'description': 'Returns the pet photo', 'schema': {'type': 'file'}

This example come from :
https://raw.githubusercontent.com/BigstickCarpet/swagger-express-middleware/master/samples/PetStore.yaml

this yaml is correctly validated by http://bigstickcarpet.com/swagger-parser/www/index.html

Thank you,

Alexandre

Validate response body fails on empty response with Python 3

I'm writing a test suite with .tox to validate a spec against an existing API and this error is raised with Python 3.4 and Python3.5. Apparently empty bytearrays aren't considered on EMPTY_BODIES.

    def validate_response_body(op, response_spec, response):
        """Validate an outgoing response's body against the response's Swagger
        specification.
        :type op: :class:`bravado_core.operation.Operation`
        :type response_spec: dict
        :type response: :class:`bravado_core.response.OutgoingResponse`
        :raises: SwaggerMappingError
        """

        deref = op.swagger_spec.deref
        # response that returns nothing in the body
        response_body_spec = deref(response_spec.get('schema'))
        if response_body_spec is None:
            if response.text in EMPTY_BODIES:
                return
            raise SwaggerMappingError(
>               "Response body should be empty: {0}".format(response.text))
E           bravado_core.exception.SwaggerMappingError: Response body should be empty: b''

Refactor exception hierarchy

Currently, bravado-core raises three types of exceptions when errors are encountered:

  • SwaggerMappingError
  • TypeError
  • Exception

When either TypeError or Exception is raised, it is hard for a user of the library to determine if the error is Swagger related or a general Python error. Replace TypeError and Exception with bravado-core specific exceptions that conform to a hierarchy with SwaggerError at the root.

SwaggerError

  • SwaggerMappingError
  • SwaggerTypeError
  • etc

Unmarshaling request with non-required enum fails

unmarshal_request delegates to bravado_core.validate.validate_primitive which fails validation when None is passed in for a non-required parameter.

>           raise error
E           ValidationError: None is not one of ['inky', 'dinky', 'doo']
E
E           Failed validating 'enum' in schema:
E               {'enum': ['inky', 'dinky', 'doo'], 'type': 'string'}
E
E           On instance:
E               None

I've pushed a branch with a test that captures this:

https://github.com/jacobstr/bravado-core/tree/validate-non-required-enum

unspecified properties are allowed in body

in bravado-core's features there is the possibility to validate requests. If I ask to marshal the body of a request which contain an undefined property in swagger, the body is marshalled as if I automatically specified AdditionalProperties = true. Even if I specify additionalProperties = false in swagger, I get no error and the property is marshalled as if it was specified in swagger.

File upload is too aggressive

from mock import Mock
from bravado_core.param import add_file, Param
from bravado_core.operation import Operation
from bravado_core.spec import Spec
empty_swagger_spec = Spec(spec_dict={})
request = {}
op = Mock(spec=Operation, consumes=['multipart/form-data'])
param_spec = {
    'type': 'file',
    'in': 'formData',
    'name': 'photo'
}
param = Param(empty_swagger_spec, op, param_spec)
add_file(param, None, request)
print request

results in {'files': [('photo', ('photo', None))]}

feels like this should be {} as this results in bravado always trying to upload an empty/inexisting file if it is defined in the spec, which doesn't play well with most bravado clients (Fido and RequestsClient).

casting of boolean values doesn't work

See the below code excerpt from bravado_core/param.py for proof.
param_value comes in as a string, using bool() as a casting function would result in the following behavior:

  • bool('non-empty-string') == True
  • bool('') == False (doesn't happen because None is returned if param_value == '')
CAST_TYPE_TO_FUNC = {
    'integer': int,
    'number': float,
    'boolean': bool
}
def cast_request_param(param_type, param_name, param_value):
    ....
    if param_value is None:
        return None

    if param_type in CAST_TYPE_TO_FUNC and param_value == '':
        return None

    try:
        return CAST_TYPE_TO_FUNC.get(param_type, lambda x: x)(param_value)
    except ValueError:
        log.warn("Failed to cast %s value of %s to %s",
                 param_name, param_value, param_type)
        # Ignore type error, let jsonschema validation handle incorrect types
        return param_value

x-scope annotations on server side shouldn't be sent to the client

See internal ticket GSI-252 for the details.

Basically, x-scope annotations that reference file:// type urls are being sent to the client which then tries to use the file:// type urls to resolve $refs. The x-scope annotation should not be part of the swagger.json sent to the client.

The error surfaces on the client side in an exception that closely resembles:

RefResolutionError: <urlopen error [Errno 2] No such file or directory: '/code/api_docs/swagger.json'>

enum with x-nullable

This is my desired spec for a property in an object:

"code": {
               "type": "string",
               "enum": ["M", "U", "P", "C", "H", "F"],
               "x-nullable": true
    }

Using this against an incoming object with property code equal to null produces a validation error in enum_validator(). Tracing the code I see x-nullable is only considered when comparing the instance against the type, but validation doesn't end there and the enum validator fails because null is not part of the enum array. Swagger does not allow null as a value within the enum, so I'm stuck.

Within the enum validator does it not make sense to test None instances against x-nullable?

Also tag models with external references.

Currently only models present in #/definitions get tagged with x-model: <model name> and get used during marshalling/unmarshalling for type conversion to a python class versus a dict. Other models referred using file:// or http:// should also be treated in the same manner.

There can be cases where $ref refers to a complete file, like shown here:

{
  "description": "A simple model",
  "type": "object",
  "properties": {
    "id": {
      "type": "integer",
      "format": "int64"
    },
    "tag": {
      "description": "a complex, shared property.  Note the absolute reference",
      "$ref": "https://my.company.com/definitions/Tag.json"
    }
  }
}

These kind of refs can be ignored, as they do not contain a Tag name, to associate them with.

Update sphinx generated documentation

The current sphinx generated documentation doesn't take into account the splitting of swagger-py into bravado and bravado-core. Update the bravado-core docs to reflect usage of the library in both client-side and server-side scenarios.

Content-Type mismatch crashes bravado-core's unmarshal method

reporting for @nattofriends

Creating a request like:

curl -X POST localhost:8080/my_json_endpoint -H "Content-Type:application/json" -d 'definitely not json'

and calling unmarshal_request throws this error:

pyramid_swagger/tween.py:526: in swaggerize_request
    request_data = unmarshal_request(request, op)
.tox/py27/lib/python2.7/site-packages/bravado_core/request.py:64: in unmarshal_request
    param_value = unmarshal_param(param, request)
.tox/py27/lib/python2.7/site-packages/bravado_core/param.py:171: in unmarshal_param
    raw_value = request.json()
pyramid_swagger/tween.py:249: in json
    return getattr(self.request, 'json_body', {})
.tox/py27/lib/python2.7/site-packages/pyramid/request.py:235: in json_body
    return json.loads(text_(self.body, self.charset))
/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/__init__.py:338: in loads
    return _default_decoder.decode(s)
/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/decoder.py:366: in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())

Not a huge deal since it requires a malformed request, but also seems like a cheap place to be slightly more intentional about behavior when unmarshalling fails. Perhaps a specific exception type gets raised for the calling code to catch and react accordingly?

unintuitive behavior when a param does not have a value

A request such as example.com/path_to_resource?bool_flag= where the type of bool_flag is boolean, the value of bool_flag would be set to False and if the type was integer or number the value would be set to '' (empty string). This behavior seems arbitrary and unintuitive.

I propose their values be set to None, as no value was provided. A caveat however, string types would remain '' (empty string) because there is no other way to specify an empty string as a param's value.

User-defined formatter validate functions have to handle None.

When defining custom formatters with SwaggerFormat, the validate function has to handle None values. So all my validators are littered with code like this:

def my_validator(val):
   if val is not None:
      # real validation here

Shouldn't None be special-cased, left to x-nullable checking, never calling the validator when value==None is True? This is quite tedious.

Add UserFormatterLike interface

This is to be called from pyramid_swagger during init

Roughly interface should look like:

class UserFormatterLike(object):
    """
    Interface for marshalling/unmarshalling user defined formatter objects.
    Subclasses are responsible for providing attrs for __required_attrs__.
    """
    __required_attrs__ = [
        'format',         # string - format name
        'description', # string - describes the marshalling & unmarshalling format
    ]

    def __getattr__(self, name):
        """
        When an attempt to access a required attribute that doesn't exist
        is made, let the caller know that the type is non-compliant in its
        attempt to be `UserFormatterLike`. This is in place of the usual throwing
        of an AttributeError.
        Reminder: __getattr___ is only called when it has already been
                  determined that this object does not have the given attr.
        :raises: NotImplementedError when the subclass has not provided access
                to a required attribute.
        """
        if name in self.__required_attrs__:
            raise NotImplementedError(
                'This UserFormatterLike type {0} forgot to implement an attr '
                'for `{1}`'.format(type(self), name))
        raise AttributeError(
            "'{0}' object has no attribute '{1}'".format(type(self), name))

    def to_python(self, **kwargs):
        """
        :return: unmarshalled python object
        """
        raise NotImplementedError("Implement to_python() in {0}".format(type(self)))

    def to_wire(self, **kwargs):
        """
        :return: marshalled python object
        :rtype: str
        """
        raise NotImplementedError("Implement to_wire() in {0}".format(type(self)))

and a method similar to register_format defined here:

def register_formatter(formatter):
    """Register a user defined formatter with bravado-core

    :param formatter: object having information to marshal/unmarshal particular format
    :type formatter: :class:`UserFormatterLike`
    """
    _formatters[formatter.format] = (formatter.to_wire, formatter.to_python, formatter.description)    

Validation fails for arrays of non-strings when using the "multi" collection format

Examples of failed validation:

When queried with value=2

<html>
 <head>
  <title>400 Bad Request</title>
 </head>
 <body>
  <h1>400 Bad Request</h1>
  The server could not comply with the request since it is either malformed or otherwise incorrect.<br/><br/>
u'2' is not of type 'array'

Failed validating 'type' in schema:
    {'collectionFormat': 'multi',
     'description': 'Value',
     'in': 'query',
     'items': {'format': 'int32', 'type': 'integer'},
     'name': 'value',
     'required': False,
     'type': 'array'}

On instance:
    u'2'


 </body>
</html>

and when queried with value=2&value=1

<html>
 <head>
  <title>400 Bad Request</title>
 </head>
 <body>
  <h1>400 Bad Request</h1>
  The server could not comply with the request since it is either malformed or otherwise incorrect.<br/><br/>
u'2' is not of type 'integer'

Failed validating 'type' in schema['items']:
    {'format': 'int32', 'type': 'integer'}

On instance[0]:
    u'2'

 </body>
</html>

Support model polymorphism

Model polymorphism as defined in the Swagger 2.0 spec is currently not supported by this package. I have implemented it in PR #128 but have not received any feedback. Is this repo still being maintained?

x-scope tripping up validation?

Hey all, ran into an issue when loading up a spec.

First of all, I'm doing

spec = Spec.from_dict(openapi_spec_dict)
spec.build()

Which I hope is the right approach. The spec I'm loading is reproduced below in yaml (with some extraneous parts clipped). I get an error (at the very bottom), which seems to suggest to me that x-scope is causing references to not validate?

Seems far-fetched, I know; I saw all the comments about customizing the validator for exactly this sort of extension. But I'm not sure how to interpret the following error. Any help would be much appreciated!

swagger: '2.0'
host: localhost
basePath: /
schemes:
- http
consumes:
- application/json
produces:
- application/json
info:
  contact:
    name: ''
  description: ''
  title: ''
  version: '0.1'
paths:
  /admin/campaign:
    post:
      description: Get admin-level details for a campaign, identified by campaign
        ID.
      operationId: get_admin_campaign
      parameters:
      - description: JSON request message for get_admin_campaign
        in: body
        name: get_admin_campaign_request_message
        required: true
        schema:
          $ref: '#/definitions/GetAdminCampaignRequestModel'
      produces:
      - application/json
      responses:
        '200':
          schema:
            $ref: '#/definitions/GetAdminCampaignResponseWrapper'
definitions:
  GetAdminCampaignResponseWrapper:
    data:
      schema:
        $ref: '#/definitions/CampaignDetails'
      type: object
    errors:
      items:
        type: string
      type: array
    status:
      type: string
    user:
      schema:
        $ref: null
      type: object
Traceback (most recent call last):
  File "montage/openapi_endpoints.py", line 440, in <module>
    spec = Spec.from_dict(sb.get_openapi_spec_dict())
  File "/home/mahmoud/virtualenvs/montage/local/lib/python2.7/site-packages/bravado_core/spec.py", line 152, in from_dict
    spec.build()
  File "/home/mahmoud/virtualenvs/montage/local/lib/python2.7/site-packages/bravado_core/spec.py", line 159, in build
    http_handlers=build_http_handlers(self.http_client))
  File "/home/mahmoud/virtualenvs/montage/local/lib/python2.7/site-packages/swagger_spec_validator/validator20.py", line 82, in validate_spec
    http_handlers=http_handlers)
  File "/home/mahmoud/virtualenvs/montage/local/lib/python2.7/site-packages/swagger_spec_validator/common.py", line 22, in wrapper
    sys.exc_info()[2])
  File "/home/mahmoud/virtualenvs/montage/local/lib/python2.7/site-packages/swagger_spec_validator/common.py", line 17, in wrapper
    return method(*args, **kwargs)
  File "/home/mahmoud/virtualenvs/montage/local/lib/python2.7/site-packages/swagger_spec_validator/validator20.py", line 125, in validate_json
    cls=Draft4Validator)
  File "/home/mahmoud/virtualenvs/montage/local/lib/python2.7/site-packages/swagger_spec_validator/ref_validators.py", line 34, in validate
    instance_cls(schema, *args, **kwargs).validate(instance)
  File "/home/mahmoud/virtualenvs/montage/local/lib/python2.7/site-packages/jsonschema/validators.py", line 123, in validate
    raise error
swagger_spec_validator.common.SwaggerValidationError: {'schema': {'x-scope': [''], '$ref': '#/definitions/GetAdminCampaignResponseWrapper'}} is not valid under any of the given schemas

Failed validating u'oneOf' in schema[u'properties'][u'paths'][u'patternProperties'][u'^/'][u'properties'][u'post'][u'properties'][u'responses'][u'patternProperties'][u'^([0-9]{3})$|^(default)$']:
    {u'oneOf': [{u'$ref': u'#/definitions/response'},
                {u'$ref': u'#/definitions/jsonReference'}]}

On instance[u'paths']['/admin/campaign'][u'post'][u'responses']['200']:
    {'schema': {'$ref': '#/definitions/GetAdminCampaignResponseWrapper',
                'x-scope': ['']}}

Provide config option to toggle the use of models

Introduce a configuration option that toggles the use of models in incoming requests (server-side) and incoming responses (client side). This is primarily for backwards compatibility with swagger-py 0.7.5 behavior.

When use_models is True, references to #/definitions/{models} will be returned as model types. When False, a dict will be used instead.

In bravado_core.spec:

CONFIG_DEFAULTS = {
    "use_models": True
    ...
}

Recursive Model Definition is not correctly handled

A simple recursive model definition like this

{
    "swagger": "2.0",
    "info": {
        "version": "1.0.0",
        "title": ""
    },
    "paths": {
        "/recursion": {
            "get": {
                "responses": {
                    "200": {
                        "description": "Recurse",
                        "schema": {
                            "$ref": "#/definitions/Recursion"
                        }
                    }
                },
                "parameters": []
            }
        }
    },
    "host": "localhost",
    "basePath": "/",
    "definitions": {
        "Recursion": {
            "properties": {
                "title": {
                    "description": "user facing localized display string",
                    "type": "string"
                },
                "parent": {
                    "description": "parent recursion",
                    "$ref": "#/definitions/Recursion"
                }
            },
            "required": [
                "title"
            ]
        }
    }
}

Leads to this exception

  File "/usr/local/lib/python2.7/site-packages/bravado_core/spec.py", line 203, in descend
    if is_dict_like(fragment):
  File "/usr/local/lib/python2.7/site-packages/bravado_core/schema.py", line 49, in is_dict_like
    if type(spec) == dict:
RuntimeError: maximum recursion depth exceeded while calling a Python object

As nothing in the swagger spec advise against recursive model definitions and the canonical swagger-codegen handles it, this should also work in bravado.

Validation of user defined formats only works for primitive schemas

Given:

def validate_palindrome(x):
    if x != ''.join(reversed(x)):
        raise SwaggerValidationException('%s is not a palindrome' % x)

palindrome_format = SwaggerFormat(
    format='palindrome',
    to_wire=lambda x: x,
    to_pyton=lambda x: x,
    validate=validate_palindrome)

This works for schema:

{
    "name": "foo",
    "type": "string",
    "format": "palindrome"
}

But doesn't work for nested primitives:

{
    "type": "object",
    "properties": {
        "foo": {
            "type": "string", 
            "format": "palindrome"
        }
    }
}

This is because validation is outsourced wholesale to Swagger20Validator which is not aware of the registered SwaggerFormats. Few options here:

  1. Figure out how to hook the SwaggerFormat validation functions into Swagger20Validator which in turn uses jsonschema (best)
  2. Make another pass over the schema post-Swagger20Validator and find/validate every format found (ick, a hack!)
  3. Post-pone the validation for user defined formats until unmarshalling and do it then (double ick, a nastier hack!)

Unmarshal fails with multi-type additionalProperties

It's quite unusual to have something like this, but given an object:

  stored-object:
    type: object
    properties:
      data:
        type: object
        properties:
          id:
            type: string
        additionalProperties:
          type: [integer, string, boolean]
      permissions:
        $ref: '#/definitions/permissions'

I've got this error on pyramid_swagger:

{
    "code": "400 Bad Request",
    "message": "The server could not comply with the request since it is either malformed or otherwise incorrect.\n\n\nDon't know how to unmarshal value aaa with a type of [u'integer', u'string', u'boolean']\n\n",
    "title": "Bad Request"
}

UnicodeEncodeError on accented characters

Hi, I observed this UnicodeEncodeError issue that's raised when we encounter accented characters (like è é) in the following situations:

  1. Descriptions that are going to become docstrings.
  2. Response bodies.
  3. Request parameters and bodies (potentially; I'm yet to try this one.)

That's with 1.0.0-rc2, downloaded 3 days ago, on 2015-06-08.

Here's an example of situation 1:

api = SwaggerClient.from_url("http://bit.ly/1L2tMtX")

>>> api.op.get_op()
Traceback (most recent call last):
  ...
    api.op.get_op()
  File "[…]/bravado_core/resource.py", line 94, in __getattr__
    return operation_docstring_wrapper(op)
  File "[…]/bravado_core/docstring.py", line 53, in operation_docstring_wrapper
    wrapper.__doc__ = create_operation_docstring(operation)
  File "[…]/bravado_core/docstring.py", line 94, in create_operation_docstring
    s += "{0}\n\n".format(desc)
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe8' in position 24: ordinal not in range(128)

Here's the minimal swagger.json used to reproduce situation 1:

{
  "swagger": "2.0",
  "info": { "title": "Minimal case", "version": "1" },
  "paths": {
    "/op": {
      "get": {
        "description": "Minimal case with caractères accentués",
        "responses": {
          "200": {
            "description": ""
          }
        }
      }
    }
  }
}

I can come back with more details and/or help if you want.

Thanks for sharing this library!

Error during unmarshal_object for x-nullable=true property

I believe unmarshal_object code path is missing support for x-nullable. I get the following traceback when unmarshaling a property which a specification like:

definitions:
  SomeType:
    properties:
      extra_data:
        type: object
        x-nullable: true

Here's the stack trace:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/zroadhouse/.virtualenvs/dartclient-bravado/lib/python2.7/site-packages/bravado/http_future.py", line 77, in result
    self.response_callbacks)
  File "/Users/zroadhouse/.virtualenvs/dartclient-bravado/lib/python2.7/site-packages/bravado/http_future.py", line 115, in unmarshal_response
    operation)
  File "/Users/zroadhouse/.virtualenvs/dartclient-bravado/lib/python2.7/site-packages/bravado_core/response.py", line 110, in unmarshal_response
    op.swagger_spec, content_spec, content_value)
  File "/Users/zroadhouse/.virtualenvs/dartclient-bravado/lib/python2.7/site-packages/bravado_core/unmarshal.py", line 52, in unmarshal_schema_object
    return unmarshal_object(swagger_spec, schema_object_spec, value)
  File "/Users/zroadhouse/.virtualenvs/dartclient-bravado/lib/python2.7/site-packages/bravado_core/unmarshal.py", line 124, in unmarshal_object
    result[k] = unmarshal_schema_object(swagger_spec, prop_spec, v)
  File "/Users/zroadhouse/.virtualenvs/dartclient-bravado/lib/python2.7/site-packages/bravado_core/unmarshal.py", line 42, in unmarshal_schema_object
    return unmarshal_array(swagger_spec, schema_object_spec, value)
  File "/Users/zroadhouse/.virtualenvs/dartclient-bravado/lib/python2.7/site-packages/bravado_core/unmarshal.py", line 100, in unmarshal_array
    for item in array_value
  File "/Users/zroadhouse/.virtualenvs/dartclient-bravado/lib/python2.7/site-packages/bravado_core/unmarshal.py", line 52, in unmarshal_schema_object
    return unmarshal_object(swagger_spec, schema_object_spec, value)
  File "/Users/zroadhouse/.virtualenvs/dartclient-bravado/lib/python2.7/site-packages/bravado_core/unmarshal.py", line 124, in unmarshal_object
    result[k] = unmarshal_schema_object(swagger_spec, prop_spec, v)
  File "/Users/zroadhouse/.virtualenvs/dartclient-bravado/lib/python2.7/site-packages/bravado_core/unmarshal.py", line 52, in unmarshal_schema_object
    return unmarshal_object(swagger_spec, schema_object_spec, value)
  File "/Users/zroadhouse/.virtualenvs/dartclient-bravado/lib/python2.7/site-packages/bravado_core/unmarshal.py", line 124, in unmarshal_object
    result[k] = unmarshal_schema_object(swagger_spec, prop_spec, v)
  File "/Users/zroadhouse/.virtualenvs/dartclient-bravado/lib/python2.7/site-packages/bravado_core/unmarshal.py", line 52, in unmarshal_schema_object
    return unmarshal_object(swagger_spec, schema_object_spec, value)
  File "/Users/zroadhouse/.virtualenvs/dartclient-bravado/lib/python2.7/site-packages/bravado_core/unmarshal.py", line 117, in unmarshal_object
    type(object_value), object_value))
bravado_core.exception.SwaggerMappingError: Expected dict like type for <type 'NoneType'>:None

Discussed here:

I'm working on a PR containing a fix.

Fix handling of security requirements to match spec

For both the root swagger object and an operation object the security property is defined to be an array of security requirement objects. The official spec says the following in the description for this property:

The list of values describes alternative security schemes that can be used (that is, there is a logical OR between the security requirements).

The security requirement object is described as:

Lists the required security schemes to execute this operation. The object can have multiple security schemes declared in it which are all required (that is, there is a logical AND between the schemes).

The current implementation effectively just uses a logical AND between all security schemes in all security requirements by marking all parameters derived from these as required. This prevents alternate sets of security schemes from being used. It also introduces a possible bug where parameters would be duplicated if the same security scheme appears in two or more different security requirement objects.

I can submit a pull request which removes the required property from security parameters and removes possible duplicates from the Operation.security_objects property. However this is not a complete fix as no checks are performed to make sure a valid set of security parameters are given in a request. I'm not sure that fix belongs in this package, probably it should be done in bravado.client.construct_params instead.

get_op_for_request seems not very helpful?

get_op_for_request claims that it makes server implementations very easy by letting you supply a path pattern and method to look up an op.

The thing is, a server implementation is going to have a concrete path more likely than a pattern, and the server would have to implement a look up of the operation anyway to get to the point of identifying the path pattern, at which point this work has already been done.

Am I missing something?

Unmarshaling responses more flexibly

Hi, do you plan to expose:

  1. response headers
  2. response body as non-json (is this TODO still planned for?)

As we look to use Bravado, both those features would be quite important for us.

That said, it looks like supporting response headers would require wrapping the operation results to provide users with accessors not only for the response body, but also for the response headers. Do you have plans regarding this?

Raise helpful message when `type` is not found for a definition

Current message for the error looks like this:
Unknown type None for value {u'WED': {u'time_start': 9, u'time_end': 22}}
for a definition:

    WeekHours: {
      properties: {
        WED: {
          $ref: #/definitions/DayHours,
          description: Hours for Wednesday
        }
      }

It is better to instead show the spec definition which is faulty instead of the request value.

build_resource failure for root path with no tags

The issue is related to what is reported in bravado issue #239.

An small example of the issue is

Python 2.7.9 (default, Aug 23 2016, 10:24:57)
[GCC 4.2.1 Compatible Apple LLVM 7.3.0 (clang-703.0.31)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from bravado_core.spec import Spec
>>> import yaml
>>>
>>> spec_text = """
info:
...   version: 0.0.0
...   title: Simple API
... paths:
...   /:
...     get:
...       responses:
...         '200':
...           description: OK
... swagger: '2.0'
... """
>>> spec_dict = yaml.load(spec_text)
>>> print spec_dict
{'info': {'version': '0.0.0', 'title': 'Simple API'}, 'paths': {'/': {'get': {'responses': {'200': {'description': 'OK'}}}}}, 'swagger': '2.0'}
>>> swagger_spec = Spec.from_dict(spec_dict)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/maci/pg/bravado/.tox/py27/lib/python2.7/site-packages/bravado_core/spec.py", line 152, in from_dict
    spec.build()
  File "/Users/maci/pg/bravado/.tox/py27/lib/python2.7/site-packages/bravado_core/spec.py", line 175, in build
    self.resources = build_resources(self)
  File "/Users/maci/pg/bravado/.tox/py27/lib/python2.7/site-packages/bravado_core/resource.py", line 67, in build_resources
    tags.append(convert_path_to_resource(path_name))
  File "/Users/maci/pg/bravado/.tox/py27/lib/python2.7/site-packages/bravado_core/resource.py", line 31, in convert_path_to_resource
    raise SwaggerMappingError(err_msg.format(path_name))
bravado_core.exception.SwaggerMappingError: Could not extract resource name from path /

The issue seems caused by the assumption that the path will match the following regexp /.+ (resource.py)

'type' is forced by bravado-core to be present in definitions

If definitions object does not contain a type field, validate method throws up like so:

5   2015-08-28 00:47:14,022 y_app.yelp.web.pyramid_app ERROR   : 'type'
Traceback (most recent call last):
  File "/nail/live/yelp/virtualenv_run/lib/python2.7/site-packages/pyramid/tweens.py", line 21, in excview_tween
    response = handler(request)
  File "/nail/live/yelp/virtualenv_run/lib/python2.7/site-packages/pyramid_swagger/tween.py", line 181, in validator_tween
    swagger_handler.handle_response(response, op_or_validators_map)
  File "/nail/live/yelp/virtualenv_run/lib/python2.7/site-packages/pyramid_swagger/tween.py", line 412, in _validate
    return f(*args, **kwargs)
  File "/nail/live/yelp/virtualenv_run/lib/python2.7/site-packages/pyramid_swagger/tween.py", line 535, in swaggerize_response
    response_spec, op, PyramidSwaggerResponse(response))
  File "/nail/live/yelp/virtualenv_run/lib/python2.7/site-packages/bravado_core/response.py", line 156, in validate_response
    validate_response_body(op, response_spec, response)
  File "/nail/live/yelp/virtualenv_run/lib/python2.7/site-packages/bravado_core/response.py", line 186, in validate_response_body
    validate_schema_object(response_body_spec, response_value)
  File "/nail/live/yelp/virtualenv_run/lib/python2.7/site-packages/bravado_core/validate.py", line 21, in validate_schema_object
    obj_type = spec['type']
KeyError: 'type'

From the spec it does not look like 'type' key presence should be mandated. Instead it should be defaulted to 'object' is properties field is non-empty imo. Also, swagger-converter does not add type: object to any of the definitions after conversions.

jsonref dereference happening two times, leading to failures.

Following swagger.json fails:

{
  "swagger": "2.0",
  "info": {
    "version": "1.0.0",
    "title": "Title was not specified"
  },
  "tags": [
    {
      "name": "hours",
      "description": "Retrieve business hours."
    }
  ],
  "paths": {
    "/hours/{biz_id}": {
      "get": {
        "produces": ["application/json"],
        "responses": {
          "200": {
            "$ref": "biz_id_param.json"
          },
          "405": {
            "description": "Invalid input"
          }
        },
        "description": "",
        "tags": [
          "hours"
        ],
        "summary": "Get happy hour times by biz_id",
        "operationId": "getHours",
        "parameters": [
          {
              "$ref": "file:///home/prat0318/param.json"
          }
        ]
      }
    }
  },
  "host": "localhost:1200",
  "basePath": "/",
  "schemes": [
    "http"
  ],
  "definitions": { }
}

and param.json being:

{
            "in": "path",
            "description": "Business ID",
            "required": true,
              "name": "biz_id",
            "type": "string"
}

(To run the example, MENTION full file:// address of param.json in above swagger.json)

The issue being dict gets dereferenced twice, once during from_dict init and later when it calls build, and then validate_spec.

Note: The error does not occur in all cases, but in some specific cases.

Fix ref resolution during response validation

Ref resolution during response validation sometimes fails because of where the validation starts. For refs to resolve 100% of the time, the scope maintained by jsonschema's RefResolver has to be rooted at / in swagger.json, not at the response_spec fragment that is currently passed to the validate_response(..) function.

Luckily, the full scope is available as x-scope annotations in the spec so the ref validator just needs to be customized to use the x-scope annotation if it is available.

This is quite difficult to explain, so an example is probably useful to compare working and broken scopes for the RefResolver for an internal use case:

Broken RefResolver scope:

['file:///nail/home/spatel/pg/services/biz_app/api_docs/swagger.json']

Working RefResolver scope:

[
'file:///nail/home/spatel/pg/services/biz_app/api_docs/swagger.json',
'file:///nail/home/spatel/pg/services/biz_app/api_docs/tags/account.json#/paths/account.photo.v1',
'file:///nail/home/spatel/pg/services/biz_app/api_docs/tags/account.json#/definitions/AccountPhotoResponse'
]

Missing support for multiple types

http://json-schema.org/latest/json-schema-validation.html#anchor79

The value of this keyword MUST be either a string or an array. If it is an array, elements of the array MUST be strings and MUST be unique.

...

An instance matches successfully if its primitive type is one of the types defined by keyword.

I take this to mean that I can use

properties:
    foo:
        type:
            - 'string'
            - 'null'

But Bravado bombs:

SwaggerMappingError: Don't know how to unmarshal value None with a type of ['string', 'null']

Unfortunately, I can't remove the null type here, because I do get strings and nulls in that property from my backend, and validation fails for nulls; non-required properties are expected to not exist in the dictionary and do not accept a null value.

Badly formatted string inputs cause ValueError instead of SwaggerMappingError

Given a string value that has a date-time format, an incorrectly formatted input (say, "string") will cause dateutil.parser.parse to raise ValueError: Unknown string format when unmarshalling the request. This isn't checked anywhere, so it bubbles up as an unexpected error, rather than being reported as invalid input.

It seems like the place to check this is in unmarshal_primitive: the call to formatter.to_python could be wrapped in a try block, with ValueError trapped and re-raised as a SwaggerMappingError (allowing callers to recognize this as a malformed request)—is that the right fix?

Inline schema failed to validate

I'm not sure if this is expected or not, I tried to create a response with an inline schema, something like this:

    {                                                                                                                                                                              
        "swagger": "2.0",                                                                                                                                                                      
        "info": {                                                                                                                                                                              
            "version": "1.0.0",                                                                                                                                                                
            "title": "Simple"                                                                                                                                                                  
        },                                                                                                                                                                                     
        "paths": {                                                                                                                                                                             
            "/pet": {                                                                                                                                                                          
                "get": {                                                                                                                                                                       
                    "operationId": "getPet",                                                                                                                                                   
                    "responses": {                                                                                                                                                             
                        "200": {                                                                                                                                                               
                            "description": "Get a pet",                                                                                                                                        
                            "schema": {                                                                                                                                                        
                                "properties": {                                                                                                                                                
                                    "id": {                                                                                                                                                    
                                        "type": "integer",                                                                                                                                     
                                    }                                                                                                                                                          
                                }                                                                                                                                                              
                            }                                                                                                                                                                  
                        }                                                                                                                                                                      
                    }                                                                                                                                                                          
                }                                                                                                                                                                              
            }                                                                                                                                                                                  
        }       

But I got

SwaggerMappingError: ("No proper type could be found from {'properties': {'id': {'type': 'integer'}}}", None)

From what I can tell, this should be valid under https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#schema-object

Moving it to a $ref worked

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.