Giter Site home page Giter Site logo

p1c2u / openapi-core Goto Github PK

View Code? Open in Web Editor NEW
274.0 10.0 127.0 3.01 MB

Openapi-core is a Python library that adds client-side and server-side support for the OpenAPI v3.0 and OpenAPI v3.1 specification.

License: BSD 3-Clause "New" or "Revised" License

Python 99.89% Makefile 0.11%
openapi openapi3 client server swagger oas oas3 schema python python-library openapi31

openapi-core's Introduction

openapi-core

image

image

image

image

image

image

About

Openapi-core is a Python library that adds client-side and server-side support for the OpenAPI v3.0 and OpenAPI v3.1 specification.

Key features

  • Validation and unmarshalling of request and response data (including webhooks)
  • Integration with popular libraries (Requests, Werkzeug) and frameworks (Django, Falcon, Flask, Starlette)
  • Customization with media type deserializers and format unmarshallers
  • Security data providers (API keys, Cookie, Basic and Bearer HTTP authentications)

Documentation

Check documentation to see more details about the features. All documentation is in the "docs" directory and online at openapi-core.readthedocs.io

Installation

Recommended way (via pip):

pip install openapi-core

Alternatively you can download the code and install from the repository:

pip install -e git+https://github.com/python-openapi/openapi-core.git#egg=openapi_core

First steps

Firstly create your OpenAPI object.

Now you can use it to validate and unmarshal against requests and/or responses.

Retrieve validated and unmarshalled request data

Request object should implement OpenAPI Request protocol. Check Integrations to find officially supported implementations.

For more details read about Unmarshalling process.

If you just want to validate your request/response data without unmarshalling, read about Validation instead.

  • openapi-spec-validator

    Python library that validates OpenAPI Specs against the OpenAPI 2.0 (aka Swagger), OpenAPI 3.0 and OpenAPI 3.1 specification. The validator aims to check for full compliance with the Specification.

  • openapi-schema-validator

    Python library that validates schema against the OpenAPI Schema Specification v3.0 and OpenAPI Schema Specification v3.1.

  • bottle-openapi-3

    OpenAPI 3.0 Support for the Bottle Web Framework

  • pyramid_openapi3

    Pyramid addon for OpenAPI3 validation of requests and responses.

  • tornado-openapi3

    Tornado OpenAPI 3 request and response validation library.

License

The project is under the terms of BSD 3-Clause License.

openapi-core's People

Contributors

andersk avatar beirdo avatar bjmc avatar correl avatar danielbradburn avatar dependabot[bot] avatar domenkozar avatar gjo avatar jgod avatar jitka avatar jonathanberthias avatar jparise avatar jyggen avatar larsderidder avatar mattiaverga avatar mcapitani avatar mgorny avatar michaelcurrin avatar mik-laj avatar musicinmybrain avatar ngnpope avatar p1c2u avatar pbasista avatar phrfpeixoto avatar rafaelcaricio avatar schunka avatar sisp avatar smarlowucf avatar stephenfin avatar zupo 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

openapi-core's Issues

Bug: list instead of object give fatal AttributeError

Hi!

Using your great lib, I found very annoying bug: validating wrong response (that does not corresponds to the response given in OpenAPI spec) produce fatal AttributeError and script terminates it's work.

Expected behaviour

Library correctly handle wrong responses, give helpful validation error, and continue to work.

Steps to reproduce

  1. Install openapi-core:

     pip3 install openapi-core
    
  2. Create and save following script as listofobjectscheck.py:

     #!/usr/bin/env python3
     
     # -*- coding: utf-8 -*-
     import json
     import sys
     import yaml
     from openapi_core import create_spec
     from openapi_core.validators import RequestValidator, ResponseValidator
     from openapi_core.wrappers import MockRequest, MockResponse
     
     
     def validate(openapi_file):
         with open(openapi_file, 'r') as myfile:
             spec_dict = yaml.safe_load(myfile)
             spec = create_spec(spec_dict)
     
             openapi_request = MockRequest('localhost', 'get', '/someobjects')
             validator = RequestValidator(spec)
             result = validator.validate(openapi_request)
             request_errors = result.errors
     
             data = json.dumps([
                 {
                     'someint': 123,
                     'somestr': '123'
                 },
                 {
                     'someint': 345,
                     'somestr': '345'
                 }
             ])
     
             openapi_response = MockResponse(data)
             validator = ResponseValidator(spec)
             result = validator.validate(openapi_request, openapi_response)
             response_errors = result.errors
     
             print('Request errors: {} Response errors: {}'.format(request_errors, response_errors))
     
     
     if __name__ == "__main__":
         if len(sys.argv) < 2:
             print("Specify path to openapi.yaml file!")
             exit(1)
         else:
             validate(sys.argv[1])
    
  3. Create and save following specification as listofobjectsfail.yaml, that give us fatal error during validation:

     openapi: "3.0.0"
     
     info:
       version: "0.1"
       title: List of objects
       description: Test for list of objects
     
     components:
       schemas:
         SomeObject:
           type: object
           properties:
             someint:
               description: Some integer
               type: integer
               format: int32
               minimum: 0
               example: 667
             somestr:
               description: Some string
               type: string
     
     paths:
       /someobjects:
         get:
           summary: Get the someobjects list
           operationId: getSomeObjects
           responses:
             '200':
               description: List of someobjects
               content:
                 application/json:
                   schema:
                     $ref: '#/components/schemas/SomeObject'
    
  4. Execute script to validate spec:

     python3 listofobjectscheck.py listofobjectsfail.yaml
    

    This give us fatal error:

     $ python3 listofobjectscheck.py listofobjectsfail.yaml
     Traceback (most recent call last):
       File "listofobjectscheck.py", line 45, in <module>
         validate(sys.argv[1])
       File "listofobjectscheck.py", line 34, in validate
         result = validator.validate(openapi_request, openapi_response)
       File "~/.local/lib/python3.5/site-packages/openapi_core/validators.py", line 201, in validate
         data = media_type.unmarshal(raw_data)
       File "~/.local/lib/python3.5/site-packages/openapi_core/media_types.py", line 19, in unmarshal
         return self.schema.unmarshal(value)
       File "~/.local/lib/python3.5/site-packages/openapi_core/schemas.py", line 104, in unmarshal
         casted = self.cast(value)
       File "~/.local/lib/python3.5/site-packages/openapi_core/schemas.py", line 93, in cast
         return cast_callable(value)
       File "~/.local/lib/python3.5/site-packages/openapi_core/schemas.py", line 126, in _unmarshal_object
         value_keys = value.keys()
     AttributeError: 'list' object has no attribute 'keys'
    
  5. Fix wrong openapi file and save it as listofobjectsok.yaml:

     openapi: "3.0.0"
     
     info:
       version: "0.1"
       title: List of objects
       description: Test for list of objects
     
     components:
       schemas:
         SomeObject:
           type: object
           properties:
             someint:
               description: Some integer
               type: integer
               format: int32
               minimum: 0
               example: 667
             somestr:
               description: Some string
               type: string
     
     paths:
       /someobjects:
         get:
           summary: Get the someobjects list
           operationId: getSomeObjects
           responses:
             '200':
               description: List of someobjects
               content:
                 application/json:
                   schema:
                     type: array
                     items:
                       $ref: '#/components/schemas/SomeObject'
    
  6. Sure that now validation works fine:

     $ python3 listofobjectscheck.py listofobjectsok.yaml 
     Request errors: [] Response errors: []
    

Validation always pass

Because of those lines: https://github.com/p1c2u/openapi-core/blob/master/openapi_core/schema/schemas/models.py#L224-L225

Validation always pass. It will fail in the proper object validation, but the exception it caught and continue is called, failing silently. Then it validates on boolean type (https://github.com/p1c2u/openapi-core/blob/master/openapi_core/schema/schemas/util.py#L8-L12) because any dict with keys is a valid bool true. So the value returned is true which is not valid by my schema.

Body from `RequestValidationResult` not a dict anymore

Hi guys,

I upgraded openapi-core on my project to 0.6.0, and now when I get an instance of RequestValidationResult and try to access the request body, it's a Model instance now, and I can't get it easily transformed to a plain dictionary to be used elsewhere - for example with dacite.from_dict, which I'm also using together with openapi-core.

I could get the plain dict body from the Flask request instead, but it would be nice if I could keep using the result.body instead, as this leads to a better design - it makes it clear that the body was validated.

Any suggestions on how I should proceed? Can the library receive an implementation change so that it's easier to get the dict representation of the model? Or should I go on and use Flask's request object instead?

Thanks,
Diogo

Minimum and maximum properties are not validated

Version: 0.5.0

Expected result: setting a number below 'minimum' or above 'maximum' properties on an should add an error in the attribute RequestValidationResult.errors when RequestValidator.validate is called.

Actual result: Specifying the parameters 'minimum' or 'maximum' makes no difference when validating, returning a None when RequestValidationResult.errors is checked.

"Any Type" not supported

"Any Type" is part of the specification ( https://swagger.io/docs/specification/data-models/data-types/#any ), however this library does not support it.

The problem seems that, when creating the Schema for a schema spec that has {} as its value (meaning "anything can be used as input here"), it is created with an object type - see here: https://github.com/p1c2u/openapi-core/blob/master/openapi_core/schemas.py#L179 - it tries to infer "object" by default.

After checking the schema types ( https://github.com/p1c2u/openapi-core/blob/master/openapi_core/enums.py#L27 ) I can see that "any" is not there.

Please let me know if you need help with implementing that.

Thanks,
Diogo

oneOf in array items not working

If you have multiple types (oneOf) of items in an array only the first is validated against:

schema

conditions:
  type: array
  items:
    oneOf:
      - type: object
        required:
          - image
        properties:
          image:
            type: string
        additionalProperties: false
      - type: object
        required:
          - package
        properties:
          package:
            type: array
            items:
              type: string
            minItems: 2

request

{'conditions': [{'image': 'data'}, {'package': ['info', 'data']}]}

Yields: openapi_core.schema.schemas.exceptions.UndefinedSchemaProperty: Undefined properties in schema: {'package'}

oneOf type forced to object

If you have a list oneOf types validator defaults them to object and fails:

schema

utctime:
  oneOf:
    - type: string
      enum: [always, now]
    - type: string
      format: date-time

request

{'utctime': 'now'}

Yields: openapi_core.schema.media_types.exceptions.InvalidMediaTypeValue: Value of now not an object.

Now should be of type string not object.

Request for documentation

Please describe the request object for the request_parameters_factory.create. Can't find it in source code.

Is it possible to validate a response data?

Uncustomizable type casting

Currently the Schema class does automatic type casting, for string it doesn't do even that.
Which actually could be not the desired behavior: some might need to force strict type match for example, just like me. I solved the problem currently by monkey-patching DEFAULT_CAST_CALLABLE_GETTER.
Does it sound reasonable to make this customizable?

"links" parameter of response

Similarily to #118 , I needed the "links" parameter of the "Response" object [spec ref].
I saw that it is in the model, but it is not populated.
I have already an implementation (although I have some issues with complex "requestBody" values), if you'd like to merge it upstream, I'll open a pull request for review.

allOf element's required keyword is ignored, if its element doesn't have properites keyword

I'd like to define PetNew schema for POST /pets and PetUpdate schema for PATCH /pets/{pet_id} as following to reuse name schema:

components:
  schemas:
    PetNew:
      allOf:
      - $ref: '#/components/schemas/PetUpdate'
      - type: object
        required:
        - name
    PetUpdate:
      type: object
      properties:
        name:
          type: string

But openapi-core treats PetNew's name property as optional.

def test_all_of_required_keyword_only_element(self):
    schema = Schema('object', all_of=[
        Schema('object', properties={'name': Schema('string')}),
        Schema('object', required=['name']),
    ])
    assert schema.get_all_required_properties_names() == {'name'}

swagger-editor shows no schema errors for this definitions and openapi-generator generates required name property.

does not handle content type wildcards in requestBody

e.g.: the spec allows mimeType wildcards like image/* (or even /) in requestBody, but a request sent with e.g. image/png does not match any of the above and raises InvalidContentType exception in the validator.

Server urls with variables aren't supported

If one has openapiv3 with server urls:

  - url: https://app-pr-{prId}.herokuapp.com/api/v1	   
     description: Staging	    
     variables:	
        prId:	
           default: '1'

Then using https://app-pr-56.herokuapp.com/api/v1 will result into
InvalidServer('Invalid request server https://app-pr-56.herokuapp.com/api/v1/user/login',).

Workaround for now is just to skip domain with url: /api/v1.

Request Validation stops after first failure

The validator is working fine, but if there is more than one error in the data, the validate routine only raises the first error and stops validating the rest of the data.

This is quite frustrating, since you actually want to know all the errors in your dataset, and not just the first one.

Is there any way to prevent that (without overwriting a bunch of classes and methods of coruse)?

KeyError: <SchemaFormat.BYTE: 'byte'>

We encounter the following error because we use a string property with format 'byte' (for base64 strings, according to the OpenAPI specification (https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#data-types).

The properties part of the OpenAPI schema looks like this:

"properties": {
	"data": {
		"type": "string",
		"format": "byte"
	}
  File "/venv/lib/python3.6/site-packages/openapi_core/validation/request/validators.py", line 37, in validate
    body, body_errors = self._get_body(request, operation)
  File "/venv/lib/python3.6/site-packages/openapi_core/validation/request/validators.py", line 82, in _get_body
    body = media_type.unmarshal(raw_body, self.custom_formatters)
  File "/venv/lib/python3.6/site-packages/openapi_core/schema/media_types/models.py", line 45, in unmarshal
    unmarshalled = self.schema.unmarshal(deserialized, custom_formatters=custom_formatters)
  File "/venv/lib/python3.6/site-packages/openapi_core/schema/schemas/models.py", line 183, in unmarshal
    casted = self.cast(value, custom_formatters=custom_formatters)
  File "/venv/lib/python3.6/site-packages/openapi_core/schema/schemas/models.py", line 173, in cast
    return cast_callable(value)
  File "/venv/lib/python3.6/site-packages/openapi_core/schema/schemas/models.py", line 263, in _unmarshal_object
    value, custom_formatters=custom_formatters)
  File "/venv/lib/python3.6/site-packages/openapi_core/schema/schemas/models.py", line 302, in _unmarshal_properties
    prop_value, custom_formatters=custom_formatters)
  File "/venv/lib/python3.6/site-packages/openapi_core/schema/schemas/models.py", line 183, in unmarshal
    casted = self.cast(value, custom_formatters=custom_formatters)
  File "/venv/lib/python3.6/site-packages/openapi_core/schema/schemas/models.py", line 173, in cast
    return cast_callable(value)
  File "/venv/lib/python3.6/site-packages/openapi_core/schema/schemas/models.py", line 263, in _unmarshal_object
    value, custom_formatters=custom_formatters)
  File "/venv/lib/python3.6/site-packages/openapi_core/schema/schemas/models.py", line 302, in _unmarshal_properties
    prop_value, custom_formatters=custom_formatters)
  File "/venv/lib/python3.6/site-packages/openapi_core/schema/schemas/models.py", line 183, in unmarshal
    casted = self.cast(value, custom_formatters=custom_formatters)
  File "/venv/lib/python3.6/site-packages/openapi_core/schema/schemas/models.py", line 173, in cast
    return cast_callable(value)
  File "/venv/lib/python3.6/site-packages/openapi_core/schema/schemas/models.py", line 206, in _unmarshal_string
    formatstring = self.STRING_FORMAT_CALLABLE_GETTER[schema_format]
KeyError: <SchemaFormat.BYTE: 'byte'>

validate_data Shortcut missing from __init__.py

I think that the validate_data shortcut is supposed to be exported but it's not.

Expected:
all = ['create_spec', 'validate_parameters', 'validate_body', 'validate_data']

Actual:
all = ['create_spec', 'validate_parameters', 'validate_body']

SpecFactory doesn't keep scope state resulting in unresolvable relative references

I have a set of files that form a spec that fully validates with openapi-spec-validator. It can find all references, but openapi-core cannot and after much debugging I figured out why.
The various generators that are started in the SpecFactory, specifically the PathsGenerator and everything it drives, only keep reference to the top-level spec_url.
When you trace through openapi-spec-validator, you see that with every file change the derefencer.resolver_manager.resolver._scopes_stack increases and the current location is appended to it and used to resolve any references.
But these generators do not do anything with the scope, so cannot resolve the relative path references and the base_uri always remains the same.

Discriminator for oneOf schemas ignored

If a discriminator is set for a list of oneOf schemas only the matching schema should be validated against and therefore useful errors can be provided:

schema

requestBody:
  content:
    application/json:
      schema:
        oneOf:
          - $ref: '#/components/schemas/cat_type'
          - $ref: '#/components/schemas/dog_type'
        discriminator:
          propertyName: type
          mapping:
            cat: '#/components/schemas/cat_type'
            dog: '#/components/schemas/dog_type'

request

{'type': 'dog' ...}

If the request is invalid the validator raises: "Exactly one valid schema should be valid, None found."

But if there's a discriminator we know which schema to validate against and could provide a useful error message. Such as what property is missing or invalid.

Path templating not supported

Hi,

Unfortunately path templates are not supported in this library; For example, if a PUT request is made for a Flask view which has a pattern of, say, /users/<user_id> and I have a /users/{user_id} path defined in my OpenAPI 3 schema, then it doesn't match the path because of the difference between "lesser-than/greater-than" bracers from Flask and the curly bracers from OpenAPI 3.

Please let me know if you need help with implementing this fix, in which case I can send a pull request.

Thanks,
Diogo

Custom validators for format of strings

As per https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#data-types

However, to support documentation needs, the format property is an open string-valued property, and can have any value. Formats such as "email", "uuid", and so on, MAY be used even though undefined by this specification. Types that are not accompanied by a format property follow the type definition in the JSON Schema. Tools that do not recognize a specific format MAY default back to the type alone, as if the format is not specified.

It would be great if openapi-core allows custom validation of format: uuid, etc.

referencing a json-schema from openapi3

Hello,

I am currently in the process of creating a python based REST api. I wrote an openapi 3.0 interface that reference an external json-schema file using $ref: 'test.json' (openapiref)[https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#schemaObject] as I thought this is supposed to be possible.

Now I wanted to use openapi-core to validate requests and responses but I hit a problem which is that the validation of my openapi interface file does not go through because I get an error of type:

{'description': 'some external json', 'content': {'application/json': {'schema': {'$ref': './test.json', 'x-scope': ['file:///c:/Users/anderegg/Documents/dev/OSPARC/jsonschematest/testapi.yaml']}}}}
is not valid under any of the given schemas

Failed validating 'oneOf' in schema['properties']['paths']['patternProperties']['^/']['properties']['get']['properties']['responses']['properties']['default']:
    {'oneOf': [{'$ref': '#/definitions/response'},
               {'$ref': '#/definitions/reference'}]}

On instance['paths']['/']['get']['responses']['default']:
    {'content': {'application/json': {'schema': {'$ref': './test.json',
                                                 'x-scope': ['file:///c:/Users/anderegg/Documents/dev/OSPARC/jsonschematest/testapi.yaml']}}},
     'description': 'some external json'}

my example openapi interface looks like this:

# test.yaml
openapi: "3.0.0"
info:
  description: This is a test api
  version: 1.0.0
  title: Test API

paths:
  /:
    get:
      tags: 
        - users
      summary: Returns a nice greeting
      description: Returns a nice greeting
      operationId: root_get
      responses:
        default:
          description: some external json
          content:
            application/json:
              schema:
                $ref: './test.json'

and my example json-schema:

// test.json
{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "Test",
    "type": ["string", "null"],
    "format": "date-time"
}

Now I understand the problem comes from factories.py:SpecFactory:create where openapi_spec_validator is failing in validating my file because the ref does not follow #/something.

Am I wrong in thinking I can direclty reference a JSON schema?
If not, is it possible to disable the spec validation from client side? Or should I change something in the way I reference it?

Thanks for any input on this...

"oneOf" keyword not supported

Unfortunately this library doesn't support "oneOf", which is fundamental for using flexible/union types. The standard describes this part of the specification, but this library doesn't implement it.

Please let me know if you need help with implementing this.

Casting incorrect types

Is there a way to cast incorrect types for request bodies, or at least validate the type and throw an error.

At the moment if I submit an int instead of a string, it passes validation and causes problems elsewhere.

Error out in human friendly way if mimetype is missing on response

Currently one gets:

../../../../.local/share/virtualenvs/woocart-api-zie1bQwL/lib/python3.6/site-packages/openapi_core/validation/response/validators.py:37: in validate
    data, data_errors = self._get_data(response, operation_response)
../../../../.local/share/virtualenvs/woocart-api-zie1bQwL/lib/python3.6/site-packages/openapi_core/validation/response/validators.py:53: in _get_data
    media_type = operation_response[response.mimetype]
../../../../.local/share/virtualenvs/woocart-api-zie1bQwL/lib/python3.6/site-packages/openapi_core/schema/responses/models.py:21: in __getitem__
    return self.content[mimetype]
../../../../.local/share/virtualenvs/woocart-api-zie1bQwL/lib/python3.6/site-packages/openapi_core/schema/content/models.py:18: in __getitem__
    if fnmatch.fnmatch(mimetype, key):  
../../../../.local/share/virtualenvs/woocart-api-zie1bQwL/lib/python3.6/fnmatch.py:34: in fnmatch
    name = os.path.normcase(name)                                  
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
                                          
s = None                             
       
    def normcase(s):                                                                                                                                     
        """Normalize case of pathname.  Has no effect under Posix"""
>       s = os.fspath(s)
E       TypeError: expected str, bytes or os.PathLike object, not NoneType

Wheel package of 0.5.0 on pypi.org contains extra files

This wheel package of version 0.5.0 hosted on pypi.org contains extra files when compared to a wheel package generated locally from git tag 0.5.0 using python setup.py bdist_wheel command in a clean virtual environment. The full diff is here.

The extra files, for instance openapi_core/validators.py, were present in the repository before PR #25 was merged. They are present in the wheel package of 0.5.0, but not in the source package of 0.5.0.

This probably indicates an issue with generating the wheel packages, where the files from previously generated wheel packages are somehow included in the new wheel package as well. I think it should be investigated and resolved.

additionalProperties support?

Hi,

Not sure if I'm doing something wrong or if additionalProperties is missing proper support.

Here is my test case:

from openapi_core import create_spec
from openapi_core.wrappers import MockRequest, MockResponse
from openapi_core.validators import ResponseValidator


schema = {
    "components": {
        "schemas": {
            "Errors": {
                "additionalProperties": {
                    "type": "string"
                },
                "type": "object"
            }
        }
    },
    "info": {
        "title": "xxx",
        "version": "1.0.0"
    },
    "openapi": "3.0.0",
    "paths": {
        "/simulate": {
            "get": {
                "responses": {
                    "422": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Errors"
                                }
                            }
                        },
                        "description": "Error in data"
                    }
                }
            }
        }
    }
}


spec = create_spec(schema)

validator = ResponseValidator(spec)
request = MockRequest('http://localhost', 'get', '/simulate')
response = MockResponse('{"error_key": "message"}', 422)
result = validator.validate(request, response)
result.raise_for_errors()

Which raises the error openapi_core.exceptions.UndefinedSchemaProperty: Undefined properties in schema: {'error_key'}.

I'm expecting instead that any key is allowed in the Error schema, given I'm using additionalProperties (see http://json-schema.org/latest/json-schema-validation.html#rfc.section.6.5.6 and this discussion).

Thanks for your inputs on this! :)

Yaml: status codes are converted as integers

When writing status codes in yaml as:

reponses:
   200:
     description: OK

They are internally converted to integers and json_schema trips over them here. In addition this isn't caught and so the default TypeError: expected string or bytes-like object tells you nothing.

The same schema converted to Json by ApiMatic converts status codes to strings and so the work around is to quote the response codes in your yaml file.

can't get to validate GeoJSON features using `openapi-core`, `oneOf` and `enum`

Hello,
I'm trying to integrate openapi-core with the test suite of my Flask API, so that all requests that I test are verified against the specification. Before I used the flex.core lib and Swagger 2.0 specification, but now I must pass on to OpenApi 3.0 (not supported by flex), so I chose openapi-core (my current version: 0.7.1). I use the FlaskOpenAPIRequest wrapper class on the flask.request object, within the correct context etc.

My problem is that I can't validate any HTTP requests that contain GeoJSON geometries in their bodies. The library "thinks" that more than one schema satisfies the specification whereas it is not the case. Thus, the validation always fails. The error I get is:

InvalidMediaTypeValue: Mimetype invalid: Invalid schema property areas: Invalid schema property geometry: Exactly one schema type SchemaType.OBJECT should be valid, more than one found

I think this might be due to a bug with oneOf choosing from schemas that contain enum, but maybe I'm doing something wrong?

Below details on requests that I make.

So, my request has a JSON body:

      tags:
        - [...]
      parameters:
        - [...]
      responses:
        [...]
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NewLayer
        required: true

NewLayer are basically lists of geojson geometries with some additional properties, like in an example test body that I send:

{
        "name": "Places in Bordeaux",
        "areas": [
            {
                "geometry": {
                    "type": "Polygon",
                    "coordinates": [
                        [
                            [
                                -0.5781233310699463,
                                44.84706857978168
                            ],
                            [
                                -0.576556921005249,
                                44.84687080641056
                            ],
                            [
                                -0.5747973918914795,
                                44.84725874584433
                            ],
                            [
                                -0.5743038654327393,
                                44.8486203165129
                            ],
                            [
                                -0.5761599540710449,
                                44.84973084752832
                            ],
                            [
                                -0.579303503036499,
                                44.849137552800435
                            ],
                            [
                                -0.5795502662658691,
                                44.84646765089727
                            ],
                            [
                                -0.5781233310699463,
                                44.84706857978168
                            ]
                        ]
                    ]
                },
                "area_id": "bdx_place_1",
                "name": "test-area"
            }
        ]
    }

To resume briefly the problem, each geometry element in my OpenApi specification has a choice of one of 5 schemas.

    Geometry:
      type: object
      oneOf:
        - $ref: '#/components/schemas/Point'
        - $ref: '#/components/schemas/LineString'
        - $ref: '#/components/schemas/MultiLineString'
        - $ref: '#/components/schemas/Polygon'
        - $ref: '#/components/schemas/MultiPolygon'

The elements that "conflict" are MultiLineString and Polygon (if I remove any of these, all works!).
Yet, they are different because they have a type field that is an enum with only one allowed value! So both can never be valid both at a time.
On the other hand, their coordinates fields can be both valid a time. That's what makes me think that enum fields are not handled correctly by the .

Below the definitions:

    MultiLineString:
      type: object
      required:
        - type
        - coordinates
      properties:
        type:
          description: Geometry type
          type: string
          enum:
            - MultiLineString
        coordinates:
          type: array
          items:
            type: array
            items:
              $ref: '#/components/schemas/Point2D'
            minItems: 2

    Polygon:
      type: object
      required:
        - type
        - coordinates
      properties:
        type:
          description: Geometry type
          type: string
          enum:
            - Polygon
        coordinates:
          type: array
          items:
            type: array
            items:
              $ref: '#/components/schemas/Point2D'
            minItems: 3

    Point2D:
      type: array
      maxItems: 2
      minItems: 2
      items:
        type: number

I will be very glad for any help! :-)
Thank you.

Bug: when API endpoint return list, InvalidMediaTypeValue exception was generated

When some API endpoint return list (array) of something, openapi-core generates InvalidMediaTypeValue exception:

/peers/all
Request errors: [] Response errors: [InvalidMediaTypeValue('Failed to cast value of [ to SchemaType.OBJECT',)]
 [FAIL] 1 errors found 
Response body: [
]

This exception generated no matter list is empty or not.

Response validation does not work in case response is an array of oneOf objects

Hi. I am trying to validate a response which is an array of objects and this case does not work properly.

spec:

paths:
  /one_of:
    get:
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Cont'

components:
  schemas:
    Cont:
      type: array
      items:
        oneOf:
        - $ref: '#/components/schemas/Obj1'
        - $ref: '#/components/schemas/Obj2'

    Obj1:
      type: object
      properties:
        a:
          type: string

    Obj2:
      type: object
      properties:
        b:
          type: string

If response is [{'a': '1'}, {'c': '2'}] - validator does not raise SchemaValidationError.
Expecting that {'c': '2'} is rejected.

openapi_core-0.7.1
openapi_spec_validator-0.2.4

Recursive models cause RecursionError

An API definition with recursive models causes a RecursionError when calling create_spec. The definition file does validate though (checked using openapi_spec_validator).

Here is a small working example:

openapi: "3.0.0"

info:
  title: "Test API"
  version: "1.0.0"

paths:
  /help:
    get:
      summary: "Gets general information about the service"
      responses:

        default:
          description: "Unexpected error"
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

components:
  schemas:
    Error:
      type: object
      properties:
        message:
          type: string
        suberror:
          $ref: '#/components/schemas/Error'

yields the following

    self.openapi_spec = create_spec(yaml.load(open(openapi_path)))
  File "/opt/conda/lib/python3.6/site-packages/openapi_core/shortcuts.py", line 16, in create_spec
    return spec_factory.create(spec_dict, spec_url=spec_url)
  File "/opt/conda/lib/python3.6/site-packages/openapi_core/specs.py", line 88, in create
    components = self.components_factory.create(components_spec)
  File "/opt/conda/lib/python3.6/site-packages/openapi_core/components.py", line 37, in create
    schemas=list(schemas), responses=responses, parameters=parameters,
  File "/opt/conda/lib/python3.6/site-packages/openapi_core/schemas.py", line 245, in generate
    schema, _ = self.schemas_registry.get_or_create(schema_spec)
  File "/opt/conda/lib/python3.6/site-packages/openapi_core/schemas.py", line 232, in get_or_create
    return self.create(schema_deref), True
  File "/opt/conda/lib/python3.6/site-packages/openapi_core/schemas.py", line 207, in create
    deprecated=deprecated, all_of=all_of,
  File "/opt/conda/lib/python3.6/site-packages/openapi_core/schemas.py", line 43, in __init__
    self.properties = properties and dict(properties) or {}

...


  File "/opt/conda/lib/python3.6/site-packages/openapi_core/schemas.py", line 207, in create
    deprecated=deprecated, all_of=all_of,
  File "/opt/conda/lib/python3.6/site-packages/openapi_core/schemas.py", line 43, in __init__
    self.properties = properties and dict(properties) or {}
  File "/opt/conda/lib/python3.6/site-packages/openapi_core/schemas.py", line 164, in generate
    schema = self._create_schema(schema_spec)
  File "/opt/conda/lib/python3.6/site-packages/openapi_core/schemas.py", line 168, in _create_schema
    return SchemaFactory(self.dereferencer).create(schema_spec)
  File "/opt/conda/lib/python3.6/site-packages/openapi_core/schemas.py", line 207, in create
    deprecated=deprecated, all_of=all_of,
  File "/opt/conda/lib/python3.6/site-packages/openapi_core/schemas.py", line 41, in __init__
    self.type = schema_type and SchemaType(schema_type)
RecursionError: maximum recursion depth exceeded while calling a Python object

Model with recursive items cause RecursionError

The SchemaRegistry doesn't handle recursive references, for example the following schema crashes with a RecursionError: maximum recursion depth exceeded while calling a Python object while trying to parse...

    Tree:
      properties:
        children:
          type: array
          items: 
            $ref: '#/components/schemas/Tree'

It looks like support was added to handle recursive properties, but recursive array items is currently not supported.

readOnly properties not supported

According to the specs, readonly properties should be validated only against response, but in requestBody they shouldn't be present.
It seems like readOnly flag is ignored at the moment.

Can not install (python2.7)

IOError: [Errno 2] No such file or directory: '/tmp/pip-build-kCtpTM/openapi-core/requirements_2.7.txt'

Would you please add this file?  ↑

Thank you!

Full error sentence:

/usr/local/lib/python2.7/dist-packages/pip/commands/install.py:194: UserWarning: Disabling all use of wheels due to the use of --build-options / --global-options / --install-options.
cmdoptions.check_install_build_global(options)
Collecting openapi-core==0.7.0
Using cached https://files.pythonhosted.org/packages/37/1e/780b34c10f3152a34cc3d96dc65664781db260b0b76c240a18753eae08f0/openapi-core-0.7.0.tar.gz
Complete output from command python setup.py egg_info:
Traceback (most recent call last):
File "", line 1, in
File "/tmp/pip-build-kCtpTM/openapi-core/setup.py", line 84, in
install_requires=install_requires(),
File "/tmp/pip-build-kCtpTM/openapi-core/setup.py", line 30, in install_requires
return read_requirements('requirements{}.txt'.format(py27))
File "/tmp/pip-build-kCtpTM/openapi-core/setup.py", line 12, in read_requirements
contents = read_file(filename).strip('\n')
File "/tmp/pip-build-kCtpTM/openapi-core/setup.py", line 19, in read_file
with open(path) as f:
IOError: [Errno 2] No such file or directory: '/tmp/pip-build-kCtpTM/openapi-core/requirements_2.7.txt'

----------------------------------------

Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-kCtpTM/openapi-core/

Invalid URL (jsonschema RefResolutionError)

We probably should install an extra handler for jsonschema, one that prepends file:// and resolves the current directory to all relative file name references. The relevant code that errors out:

    # jsonschema.validators
    def resolve_from_url(self, url):
        url, fragment = urldefrag(url)
        try:
            document = self.store[url]
        except KeyError:
            try:
                document = self.resolve_remote(url)
            except Exception as exc:
                raise RefResolutionError(exc)

        return self.resolve_fragment(document, fragment)

The code in question:

openapi: 3.0.1
servers:
  - url: 'https://example.com/api/v2.0/'
info:
  title: Example API
  description: Our awesome example API
  version: 1.1.0
tags:
  - name: Service
paths:
  $ref: './paths.yaml'

I have also tried file://./paths.yaml, which then results in it looking for /paths.yaml, which is an error of the file resolver jsonschema is using. Either way, we currently cannot resolve any relative file references, which are allowed and correctly working in other tools, including openapi-spec-validator. The latter is a bit odd, since it should be resolving references and validating them.

pip version is corrupted

pip install openapi-core==0.5.0
installed package contains different structure than this repo on github:

schema
validation
wrappers
__init__.py
components.py
enums.py
exceptions.py
infos.py
media_types.py
models.py
operations.py
parameters.py
paths.py
request_bodies.py
responses.py
schemas.py
servers.py
shortcuts.py
specs.py
validators.py
wrappers.py

Which obviously isn't functioning correctly: for example it has two different modules with same exception classes, which leads to undefined behavior.

When installed from github, the package seems correct.
(Python 3.6, MacOS)

AnyOf Keyword Not supported

Is the 'anyOf' keyword supported? It doesn't appear to work.
See here for the full spec example.

Following is an excerpt of the relevant part. Notice that the healthSigns and animalType objects do not appear in the final parse result.

PetCreatePartTwo:
      type: object
      x-model: PetCreatePartTwo
      properties:
        position:
          $ref: "#/components/schemas/Position"
        healthy:
          type: boolean
        healthSigns:
          type: array
          items:
            anyOf:
              - $ref: '#/components/schemas/FreshBreath'
              - $ref: '#/components/schemas/ShinyCoat'
              - $ref: '#/components/schemas/EyesBright'

        animalType:
          oneOf:
            - $ref: '#/components/schemas/MammalDetails'
            - $ref: '#/components/schemas/AvianDetails'

"format" should accept any value

Hi,

According to the spec, "format" should accept any value:

However, to support documentation needs, the format property is an open string-valued property, and can have any value. Formats such as "email", "uuid", and so on, MAY be used even though undefined by this specification. Types that are not accompanied by a format property follow the type definition in the JSON Schema. Tools that do not recognize a specific format MAY default back to the type alone, as if the format is not specified.

However, this will raise: ValueError: 'custom' is not a valid SchemaFormat

from openapi_core import create_spec


schema = {
    "components": {
        "schemas": {
            "Foo": {
                "type": "object",
                "properties": {
                    "propname": {
                        "type": "string",
                        "format": "custom"
                    }
                }
            }
        }
    },
    "info": {
        "title": "xxx",
        "version": "1.0.0"
    },
    "openapi": "3.0.0",
    "paths": {
        "/simulate": {
            "get": {
                "responses": {
                    "200": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Foo"
                                }
                            }
                        },
                        "description": "OK"
                    }
                }
            }
        }
    }
}


spec = create_spec(schema)

Am I missing something?

Thanks! :)

Inheritance Not handled Correctly

Consider the following example schema fragment.

{
  "components": {
    "schemas": {
      "ErrorModel": {
        "type": "object",
        "required": [
          "message",
          "code"
        ],
        "properties": {
          "message": {
            "type": "string"
          },
          "code": {
            "type": "integer",
            "minimum": 100,
            "maximum": 600
          }
        }
      },
      "ExtendedErrorModel": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ErrorModel"
          },
          {
            "type": "object",
            "required": [
              "rootCause"
            ],
            "properties": {
              "rootCause": {
                "type": "string"
              }
            }
          }
        ]
      }
    }
  }
}

Parsing the ExtendedErrorModel will fail. This is because the SchemaFactory will check for a type even though this property is not required when using inheritance.

    def create(self, schema_spec):
        schema_deref = self.dereferencer.dereference(schema_spec)

        schema_type = schema_deref['type']
       .....

Validation pass when object, list or integer returned instead of string for object string property (must fail)

Hi!

We faced with following bug. Some object's property declared as string, If this object's property returned by API is object itself, or integer, or list, no errors generated.

If API returns wrong property type for example int property, InvalidMediaTypeValue thrown (as expected).

Tested on version 0.5.0, 0.7.1, master - same results.

Expected behavior

InvalidMediaType should be thrown

Steps to reproduce

  1. Install openapi-core:

     pip3 install openapi-core
    

2.. Create and save following fatless specification as test.yml:

openapi: "3.0.1"

info:
  version: "0.1"
  title: Object instead of string
  description: Test for if returns objects instead of string

components:
  schemas:
	SomeObject:
	  type: object
	  properties:
		someprop:
		  description: Some property
		  type: string
		someint:
		  type: integer

paths:
  /getsomeobject:
	get:
	  summary: Get the SomeObject
	  operationId: getSomeObject
	  responses:
		'200':
		  description: This is SomeObject
		  content:
			application/json:
			  schema:
				$ref: '#/components/schemas/SomeObject'
  1. Create and save following script as test.py:

     #!/usr/bin/env python3
     
     # -*- coding: utf-8 -*-
     import json
     import sys
     import yaml
     from openapi_core import create_spec
     from openapi_core.shortcuts import RequestValidator, ResponseValidator
     from openapi_core.wrappers.mock import MockRequest, MockResponse
     
     
     def validate(openapi_file):
         with open(openapi_file, 'r') as myfile:
             spec_dict = yaml.safe_load(myfile)
             spec = create_spec(spec_dict)
     
             openapi_request = MockRequest('localhost', 'get', '/getsomeobject')
             validator = RequestValidator(spec)
             result = validator.validate(openapi_request)
             request_errors = result.errors
     
             # PASS (must PASS)
             data = json.dumps({
                 'someprop': 'content'
             })
     
             # PASS (must FAIL)
             data = json.dumps({
                 'someprop': {
                     'nested_object_property': 'content',
                     'nested_object_another property': 13,
                 }
             })
     
             # PASS (must FAIL)
             data = json.dumps({
                 'someprop': ['dfdfdf', 'dfdfdfsssss']
             })
     
             # PASS (must FAIL)
             data = json.dumps({
                 'someprop': 123
             })
     
             # PASS (must FAIL)
             data = json.dumps({
                 'someprop': 123
             })
     
             # FAIL (must FAIL)
             data = json.dumps({
                 'someint': 'content'
             })
     
             # FAIL (must FAIL)
             data = json.dumps({
                 'someprop': 'dsdsd',
                 'someint': 123,
                 'not_in_scheme_prop': 123
             })
     
             openapi_response = MockResponse(data)
             validator = ResponseValidator(spec)
             result = validator.validate(openapi_request, openapi_response)
             response_errors = result.errors
     
             print('Request errors: {} Response errors: {}'.format(request_errors, response_errors))
     
     
     if __name__ == "__main__":
         if len(sys.argv) < 2:
             print("Specify path to openapi.yaml file!")
             exit(1)
         else:
             validate(sys.argv[1])
    
  2. Execute script to validate spec:

     python3 test.py test.yml
    
  3. Try to comment out test payloads to see actual results.

Mess with paths

If you have url pattern in flask, you'll never get your paths validated.

import yaml
from flask import Flask, request
from openapi_core import create_spec
from openapi_core.validation.request.validators import RequestValidator
from openapi_core.wrappers.flask import FlaskOpenAPIRequest

spec = """openapi: "3.0.0"
info:
    version: 1.0.0
    title: test
servers:
- url: http://127.0.0.1:8000
components:
    parameters:
        some_param:
            name: some
            in: path
            required: true
            schema:
                type: string
paths:
    /{some}/path/:
        parameters:
        - $ref: '#/components/parameters/some_param'
        post:
            summary: anything
"""
TEST_SPEC = create_spec(spec)

FLASK_APP = Flask(__name__)

@FLASK_APP.route('/<some>/path', methods=['POST'])
def duh(some: str):
    validator = RequestValidator(TEST_SPEC)
    result = validator.validate(FlaskOpenAPIRequest(request))
    print(result.errors)

Will always get an error

[InvalidOperation('Unknown operation path /<some>/path with method post')]

Because flask url pattern syntax and openapi pattern syntax do not match.
An error hides here https://github.com/p1c2u/openapi-core/blob/master/openapi_core/validation/request/validators.py#L24

  1. path_pattern is not transformed to openapi format
  2. request.path_pattern is not used

Request validation does not work properly in case request schema is an object "derived" from other object using allOf

Hi. I am trying to validate a request which is an object "derived" from other object using allOf

components:
  schemas:
    Base:
      type: object
      properties:
        ctime:
          type: integer

    ExtendedResource:
      allOf:
        - $ref: '#/components/schemas/Base'
        - type: object
          properties:
            name:
              type: string
            options:
              type: object
              properties:
                option_1:
                  type: string
                option_2:
                  type: string
            capabilities:
              type: object
              properties:
                capability_1:
                  type: string
                capability_2:
                  type: string

Currently validator does not raise any exceptions in case of the following request body:

{'name': 'ext_res', 
 'options': {'option_1': 'a', 'option_3': 'b'}, 
 'capabilities': {'capability_1': 'c', 'capability_3': '3'}}

Expecting that options.option_3 or capabilities.capability_3 are rejected.

Many Thanks!

Multiple missing mandatory fields result in only one error

Given a openapi spec with multiple mandatory fields and a request missing several, the result of RequestValidator.validate only shows the first field.

To reproduce have a look at this repo, it contains a simple flask app, openapi spec and a curl command to test it.

The gist of it here:

Openapi spec (extract)

"requestBody": {
   "required": true,
   "content": {
      "application/json": {
         "schema": {
            "type": "object",
            "properties": {
               "name": {
                  "type": "string"
               },
               "brand": {
                  "type": "string"
               },
               "ps": {
                  "type": "integer"
               }
            },
            "required": [
               "name",
               "brand"
            ]
         },

Two mandatory fields. When sending only the optional field like this:

curl -X POST \
  http://127.0.0.1:8080/cars \
  -H 'Content-Type: application/json' \
  -d '{"ps": 123}'

I would expect result.errors to contain two entries, but there is only one:

[MissingSchemaProperty('Missing schema property name')]

There is no notion of brand missing as well.

Path-level parameters

Currently, as far as I can see, there is no support for parameters specified at the path level [spec].

I am working on a project where this is used, so I have (lightly) extended the library for my use case by adding support in PathsGenerator and the Path model as follows.
Assuming I did not miss this feature somehow and you'd like to have it, I can send a pull request for review.

paths/generators.py

class PathsGenerator(object):

    def __init__(self, dereferencer, schemas_registry):
        self.dereferencer = dereferencer
        self.schemas_registry = schemas_registry

    def generate(self, paths):
        paths_deref = self.dereferencer.dereference(paths)
        for path_name, path in iteritems(paths_deref):
            operations = self.operations_generator.generate(path_name, path)
+           parameters = self.parameters_generator.generate_from_list(
+               path.get('parameters', {})
+           )
+           yield path_name, Path(path_name, list(operations), parameters)

    @property
    @lru_cache()
    def operations_generator(self):
        return OperationsGenerator(self.dereferencer, self.schemas_registry)

+    @property
+    @lru_cache()
+    def parameters_generator(self):
+        return ParametersGenerator(self.dereferencer, self.schemas_registry)

paths/models.py

class Path(object):
    """Represents an OpenAPI Path."""

+   def __init__(self, name, operations, parameters=None):
        self.name = name
        self.operations = dict(operations)
+       self.parameters = dict(parameters) if parameters else {}

    def __getitem__(self, http_method):
        return self.operations[http_method]

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.