Git Product home page Git Product logo

falcon-oas's Introduction

falcon-oas

PyPI CI Coverage

Prerequisites

  • Validated OpenAPI 3 document
    • falcon-oas does not validate OpenAPI 3 document itself at runtime. It should be validated in advance.

Features

  • Request validation and unmarshaling
  • Access control
  • Association of Path Item Objects and resource classes in Falcon

Example

class PetItem:
    def on_get(self, req, resp, pet_id):
        pet = get_pet_by_id(pet_id)
        resp.media = pet.to_dict()

    def on_delete(self, req, resp, pet_id):
        pet = delete_pet_by_id(pet_id)
        resp.status = falcon.HTTP_NO_CONTENT


with open('/path/to/openapi.json') as f:
    spec_dict = json.load(f)
api = falcon_oas.OAS(spec_dict).create_api()

Here is the part of its OpenAPI 3 document in YAML:

paths:
  /api/v1/pets/{pet_id}:
    x-falcon-oas-implementation: path.to.PetItem
    get:
      responses:
        '200':
          description: A pet.
    delete:
      responses:
        '204':
          description: Successful deletion.
      security:
      - api_key: []
    parameters:
    - name: pet_id
      in: path
      required: true
      schema:
        type: integer
components:
  securitySchemes:
    api_key:
      x-falcon-oas-implementation: path.to.api_key_validator
      type: apiKey
      name: X-API-Key
      in: header

pet_id path parameter is unmarshaled to int without Field Converters since it is defined as integer type.

DELETE /api/v1/pets/{pet_id} requests are protected by the api_key security scheme. The corresponding responder is processed only if it grants the request. Otherwise, 403 Forbidden error occurs automatically.

x-falcon-oas-implementation associates Path Item Object and the REST resource class in Falcon so that falcon-oas automatically calls falcon.API.add_route with its path and the resource instance. Alternatively, the resource instance can be set programmatically using oas.resolve_path_item('/api/v1/pets/{pet_id}', PetItem()), which allows to inject dependencies into the resource instance.

Also x-falcon-oas-implementation associates Security Scheme Object and the access control function so that falcon-oas automatically handles Security Requirement Object in each request. See falcon_oas.extensions for details. Alternatively, the access control function can be set programmatically using oas.resolve_security_scheme('api_key', validate_api_key), which allows to inject dependencies into the access control function.

req.context['oas']

req.context['oas'].user
Authorized user.
req.context['oas'].parameters
Unmarshaled request parameters in dict.
req.context['oas'].request_body
Unmarshaled request body.

Problems

Media Type: application/problem+json only

Unmarshal Error

HTTP status code: 400

  • "type": "https://pypi.org/project/falcon-oas/0.3.0/#unmarshal-error"
  • "title": "Unmarshal Error"
  • "status": 400
  • "parameters": (optional) The array of parameter error objects
  • "request_body": (optional) The array of request body error objects

The parameter error object and the request body error object have the following members from jsonschema.ValidationError:

  • "path": The path to the offending element within the instance
  • "validator": The name of the failed validator
  • "message": A human readable message explaining the error

Example:

{
  "type": "https://pypi.org/project/falcon-oas/0.3.0/#unmarshal-error",
  "title": "Unmarshal Error",
  "status": 400,
  "parameters": [
    {
      "path": ["path", "pet_id"],
      "validator": "type",
      "message": "'me' is not of type 'integer'"
    }
  ],
  "request_body": [
    {
      "path": ["name"],
      "validator": "type",
      "message": "42 is not of type 'string'"
    }
  ]
}

falcon-oas's People

Contributors

dependabot[bot] avatar grktsh avatar sisp avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

sisp cobusc

falcon-oas's Issues

Changelog and frequent releases

Are you open to maintaining a changelog, which I think is helpful especially with 0.y.z library versions to keep track of compatible vs. breaking changes.

Somewhat related to this, are you open to more frequent releases, so changes become available shortly afterwards?

The same applies to python-oas.

I've also contributed to mobx-keystone, which is a relatively young but nicely progressing project. I came to appreciate the changelog and frequent releases there and take it as inspiration elsewhere. What do you think?

Generate routes for all server URLs in OpenAPI spec

Currently, only a single set of routes is generated based on the base path (the URL path of the first array element in the servers property of the OpenAPI spec) and the paths specified in the OpenAPI spec. I think it would be more consistent with the OpenAPI spec if routes for all array elements of the servers property were generated. If the servers property does not exist, then the base path falls back to / (same as now).

Taking this a bit further, I think also the scheme and hostname of the OpenAPI server object should be considered by the router when resolving routes, but I think Falcon currently doesn't support host-based routing.

What do you think @grktsh?

Support nullable array items

I believe OpenAPI supports nullable also for array items. falcon-oas currently does not honor nullable inside array items:

type: array
items:
  nullable: true
  ...

To support this, another custom validator for items needs to be added like this one:

_type_draft4_validator = Draft4Validator.VALIDATORS['type']
def _type_validator(validator, types, instance, schema):
if instance is None and schema.get('nullable'):
return
for error in _type_draft4_validator(validator, types, instance, schema):
yield error

I'd be happy to create a PR if you agree that array items can be nullable according to OpenAPI 3.

Missing request body passes validation

Hi,

First of all, thanks for this very promising OpenAPI middleware for Falcon!

I've noticed that a missing/empty request body, i.e. no JSON object at all, passes request body validation although the OpenAPI spec has a schema with required fields. When setting an invalid request body, e.g. an empty JSON object {}, validation fails and returns "Unmarshal Error" (400) as expected.

Allow specifying access control handlers via OAS factory and middleware

Currently, it is only possible to specify an access control handler via the x-falcon-oas-implementation property in the OpenAPI specification. Then, the access control handler is dynamically imported when the OAS middleware is instantiated.

security_schemes = _get_security_schemes(spec, base_module=base_module)
self._access_control = AccessControl(security_schemes)

This pattern makes an access control handler not configurable, e.g. providing credentials for a database with user credentials is only possible using global variables and/or environment variables, which is a bad design pattern. Instead, it should be possible to provide an access control handler to the OAS middleware class, e.g.:

mw = Middleware(..., security_handlers={'api_key': <handler function>, ...})

The new security_handlers argument could be optional and its value (dict) could be merged with the parsed handlers from the OpenAPI specification to continue support for the x-falcon-oas-implementation property and make the change backwards compatible. Perhaps the schemes provided in the argument could take precedence over the ones specified in the OpenAPI specification or an exception is raised when the same security scheme is specified in both places.

Since the Middleware class is typically not manually instantiated, the OAS class constructor should provide this argument as well and pass it through to the Middleware instantiation:

api = OAS(..., security_handlers={'api_key': <handler function>, ...}).create_api()

There is a similar problem with using x-falcon-oas-implementation for specifying request handlers, but it is solved by omitting x-falcon-oas-implementation in the endpoint specifications of the OpenAPI specification file and instead creating the router manually, which allows to instantiate resource classes manually and inject dependencies as needed.

I know that using a custom property lke x-falcon-oas-implementation or operationId for linking an endpoint to a request handler is quite popular (e.g. Connexion uses it too), but it facilitates bad design like using global/environment variables for configuration instead of dependency injection.

Factor out OpenAPI validation core for reusability

falcon-oas is for the most part a general OpenAPI validation library with a Falcon integration. Would you be open to factoring out the general OpenAPI validation core into an independent library? This way, other web frameworks like Flask can leverage this common core library, too. In my opinion, the community should agree on a common OpenAPI core library and add thin integrations with the different web frameworks leveraging the common core library instead of writing an extension specific to a particular web framework that includes the core logic all over again.

Some context:

I've been trying out several OpenAPI libraries including falcon-oas over the past year or so and noticed that efforts are scattered across the different libraries (with varying quality) although they should all be sharing a common core. I think you've done a terrific job in creating a solid validation core library leveraging jsonschema which is a proven JSON schema library. Not all OpenAPI validation libraries use jsonschema, e.g. openapi-core. Some do but don't support OpenAPI 3, e.g. bravado-core, or tightly couple the validation core with a particular web framework, e.g. connexion. falguard is quite similar to falcon-oas but builds upon bravado-core which doesn't seem to get OpenAPI 3 support anytime soon. Also, most OpenAPI libraries only report the first validation error whereas falcon-oas reports all, which I personally prefer. And then there are libraries that generate OpenAPI specs from inline annotations like flask-restplus, quart-openapi, flasgger etc., all of which implement the OpenAPI core logic all over again. This repeated core logic implementation also leads to slow adoption of OpenAPI 3 because each library needs to make the necessary changes.

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.