Flask-Rebar combines flask, marshmallow, and swagger for robust REST services.

Related tags

Flaskpublicacsbp
Overview

Flask-Rebar

Documentation Status CI Status PyPI status Code style Code of Conduct

Flask-Rebar combines flask, marshmallow, and swagger for robust REST services.

Features

  • Request and Response Validation - Flask-Rebar relies on schemas from the popular Marshmallow package to validate incoming requests and marshal outgoing responses.
  • Automatic Swagger Generation - The same schemas used for validation and marshaling are used to automatically generate OpenAPI specifications (a.k.a. Swagger). This also means automatic documentation via Swagger UI.
  • Error Handling - Uncaught exceptions from Flask-Rebar are converted to appropriate HTTP errors.

Example

from flask import Flask
from flask_rebar import errors, Rebar
from marshmallow import fields, Schema

from my_app import database


rebar = Rebar()

# All handler URL rules will be prefixed by '/v1'
registry = rebar.create_handler_registry(prefix='/v1')

class TodoSchema(Schema):
    id = fields.Integer()
    complete = fields.Boolean()
    description = fields.String()

# This schema will validate the incoming request's query string
class GetTodosQueryStringSchema(Schema):
    complete = fields.Boolean()

# This schema will marshal the outgoing response
class GetTodosResponseSchema(Schema):
    data = fields.Nested(TodoSchema, many=True)


@registry.handles(
    rule='/todos',
    method='GET',
    query_string_schema=GetTodosQueryStringSchema(),
    response_body_schema=GetTodosResponseSchema(), # for versions <= 1.7.0, use marshal_schema
)
def get_todos():
    """
    This docstring will be rendered as the operation's description in
    the auto-generated OpenAPI specification.
    """
    # The query string has already been validated by `query_string_schema`
    complete = rebar.validated_args.get('complete')

    ...

    # Errors are converted to appropriate HTTP errors
    raise errors.Forbidden()

    ...

    # The response will be marshaled by `marshal_schema`
    return {'data': []}


def create_app(name):
    app = Flask(name)
    rebar.init_app(app)
    return app


if __name__ == '__main__':
    create_app(__name__).run()

For a more complete example, check out the example app at examples/todo.py. Some example requests to this example app can be found at examples/todo_output.md.

Installation

pip install flask-rebar

Documentation

More extensive documentation can be found here.

Extensions

Flask-Rebar is extensible! Here are some open source extensions:

Contributing

There is still work to be done, and contributions are encouraged! Check out the contribution guide for more information.

Comments
  • DELETE requests should return specified Content-Type

    DELETE requests should return specified Content-Type

    DELETE requests are returning Content-Type: text/html, this is breaking some clients that expect application/json, even though there is no actual content, simply the type in the header broke a lookup. Looks like this was introduced here, as text/html is the Flask response default mimetype.

    Rolling that back here and adding a test.

    opened by joeb1415 20
  • Prepare for Marshmallow 3

    Prepare for Marshmallow 3

    https://github.com/plangrid/flask-rebar/blob/master/setup.py#L19 specifies marshmallow>=2.13. Right now this is picking up Marshmallow 2, but as soon as Marshmallow 3 final comes out, this will start picking up v3, which has several backwards-incompatible changes:

    https://marshmallow.readthedocs.io/en/3.0/changelog.html

    Are any updates necessary before then?

    v2.0 triaged swagger-generation validation 
    opened by twosigmajab 20
  • Create a more complete example

    Create a more complete example

    I just discovered flask-rebar and I think it could very well become the new standard for REST with flask. The documentation is very well made, but a more complete "real-life" example/template would help a lot to get started. I am creating one for my project that i could share, but I don't know all the best practices for the framework. Keep up the good work, cheers!

    wip 
    opened by Sytten 19
  • Fix bug that prevents returning `Flask.Response`s.

    Fix bug that prevents returning `Flask.Response`s.

    https://flask-rebar.readthedocs.io/en/latest/quickstart/basics.html#marshaling currently documents the following: "This means the if response_body_schema is None, the return value must be a return value that Flask supports, e.g. a string or a Flask.Response object.

    However, Flask-Rebar currently fails to interoperate properly with a Flask.Response object, as demonstrated by the included test. Without this bugfix to rebar.py, the included test fails with KeyError: 200. This is because the code currently passes rv to _unpack_view_func_return_value, which always returns a 200 status when rv is not a tuple, not realizing that a Flask.Response instance can get passed through as rv which can have a non-200 .status_code (and also custom .headers as well).

    opened by twosigmajab 13
  • Adding pep8 compliance test case?

    Adding pep8 compliance test case?

    Since this came up in the discussion, would it be a good idea to just jump into the deep and make add a test to ensure pep8 compliance going forward? Do people have a strong opinion about pep8 settings or should we just go with the vanilla version for now?

    Thoughts?

    opened by kazamatzuri 12
  • BP-763: Add support for multiple authenticators

    BP-763: Add support for multiple authenticators

    Corresponds to Issue #121

    • Extended deprecated_parameters decorator to allow a coercion function to convert between old and new styles of parameters.
    • Adds support for handlers to have multiple Authenticators.
    • Adds support for registries to have multiple Authenticators as their default authentication.
    • Should mark authenticator parameters as deprecated using the deprecation utils

    JIRA Tickets | --------------| BP-763|

    enhancement 
    opened by airstandley 11
  • Customize error returned in Flask-rebar

    Customize error returned in Flask-rebar

    Today we discussed creating our own class of errors because we wanted to have uniforms errors with a machine parsable type. Though we could add it just fine with the additional_data field, it is a bit painful to enforce it on every error thrown. The problem is that we can't currently modify the default errors that Flask-Rebar raise in the case of an invalid schema for example. I don't have a clear answer as to how we should handle this, but I figured I could ask if someone had an idea as to how we could do this.

    v2.0 triaged 
    opened by Sytten 11
  • Integrate marshmallow-objects

    Integrate marshmallow-objects

    One annoying thing about marshmallow is the fast that you get a dictionary instead of an object with attributes (I am bit jealous of pydantic). We started using marshmallow-objects in our project. Since we already define some custom schemas, it would be very valuable to either use this lib or make something similar. Especially since we are going toward a v2 with some breaking changes anyway.

    enhancement v2.0 triaged v2-breaking-change 
    opened by Sytten 10
  • sort required array

    sort required array

    I believe obj.fields.items(): does not consistently return objects in the same order. When committing the generated swagger.json file, it causes some redundant diffs in the required fields array

    e.g: https://github.com/plangrid/informant/pull/419

    opened by BrandonWeng 10
  • registry.add_supplemental_schemas

    registry.add_supplemental_schemas

    Add registry.add_supplemental_schemas function to export supplemental schemas to swagger, in addition to those used by the API handlers.

    In my app, I have a handler:

    @registry.handles(
        marshal_schema={201: FooSchema}
    )
    def create_foo():
        pass
    

    With:

    class FooSchema(Schema):
        template = fields.String()
        vars = fields.Dict()
    

    Elsewhere, I validate the vars against the template according to various template schemas:

    class Template_1_Schema(Schema):
        var_1 = fields.String()
        var_2 = fields.String()
    
    class Template_2_Schema(Schema):
        var_3 = fields.Boolean()
        var_4 = fields.Integer()
    

    I would like to export these template schemas in the swagger definition, so others can know what to expect to send to my handler in the vars dictionary param.

    This PR introduces a registry function add_supplemental_schemas to support this. The function can accept a single schema, or for convenience, a directory containing all the supplemental schemas.

    cc: @barakalon

    opened by joeb1415 10
  • Allow disabling OrderedDicts in generated swagger

    Allow disabling OrderedDicts in generated swagger

    If you try to yaml.dump a rebar-generated swagger object, you end up with yaml that looks like this:

    !!python/object/apply:collections.OrderedDict
    - - - consumes
        - [application/json]
      - - definitions
        - !!python/object/apply:collections.OrderedDict
          - - - Error
              - !!python/object/apply:collections.OrderedDict
                - - - properties
                    - !!python/object/apply:collections.OrderedDict
                      - - - errors
                          - !!python/object/apply:collections.OrderedDict
                            - - [type, object]
                        - - message
                          - !!python/object/apply:collections.OrderedDict
                            - - [type, string]
                  - - required
                    - [message]
                  - [title, Error]
                  - [type, object]
    ...
    

    It looks like this is due to the current behavior of unconditionally converting dicts to OrderedDicts.

    This PR allows passing order_dicts=False to swagger_generator.generate(...) to suppress the conversion.

    This may also be desired by users who want to avoid the extra work if they're using a Python implementation where dicts preserve insertion order already (e.g. PyPy or CPython >= 3.6).

    opened by twosigmajab 10
  • Flask signals not triggered on unhandled exceptions

    Flask signals not triggered on unhandled exceptions

    Flask has a signal got_request_exception intended to signify that an unhandled exception occurred in a handler. This signal is triggered by the default error handler, ref: https://github.com/pallets/flask/blob/0d8c8ba71bc6362e6ea9af08146dc97e1a0a8abc/src/flask/app.py#L1675. This means that to Flask, adding error handlers is equivalent to catching an exception; their documentation attempts to explains this, but it can be easy to miss the implication that the exception signal is not sent if a registered error handler matches the exception.

    Existing instrumentation/observability tooling uses this signal to detect when an unexpected error has occurred in a service.

    Rebar currently registers a generic Exception error handler, ref: https://github.com/plangrid/flask-rebar/blob/63922a17379a5b5a99c7422b7f7b881f8c08eb13/flask_rebar/rebar.py#L818-L827.

    This causes tools/plugins that rely on the got_request_exception signal to fail to work with a Rebar service.

    Rebar should either

    1. Switch the generic error handler to register for InternalServerError; meaning that the rebar handler would only run after the default flask handler had run and sent the got_request_exception signal.
    2. Send the got_request_exception signal as part of it's generic exception handling.
    bug error-handling 
    opened by airstandley 0
  • marshmallow-to-swagger RecursionError if schema has self references

    marshmallow-to-swagger RecursionError if schema has self references

    I am getting a RecursionError trying to generate swagger with this schema:

    class ZipStructureRowSchema(Schema):
        name = fields.String(required=True)
        sub_rows = fields.List(
            fields.Nested("ZipStructureRowSchema"), load_from="subRows", dump_to="subRows"
        )
    
    
    class ZipPreviewResponseSchema(Schema):
        preview = fields.List(fields.Nested(ZipStructureRowSchema))
    

    As far as I can tell, this is valid according to Marshmallow. Thank you!

    opened by AutodeskAbe 1
  • AttributeError: 'AuthenticatorConverterRegistry' object has no attribute 'get_security_schemes_legacy'

    AttributeError: 'AuthenticatorConverterRegistry' object has no attribute 'get_security_schemes_legacy'

    I have a custom authenticator that works, but I am trying to get the swagger docs to work as well but I keep getting the above exception.

    Here is my custom authenticator:

    class CustomAuthenticator(authenticators.Authenticator):
        def authenticate(self):
            if current_user and current_user.is_authenticated:
                pass
            elif current_app.config["AUTH_OFF"]:
                try:
                    login_user(default_user)
                except AttributeError:
                    raise ValueError("Trying to log into user that doesn't exist!")
    

    This part I took directly from the flask_rebar docs:

    class SwaggerAuthConverter(AuthenticatorConverter):
        AUTHENTICATOR_TYPE = CustomAuthenticator
    
        def get_security_schemes(self, obj, context):
            return {
                obj.name: {sw.type_: sw.api_key, sw.in_: sw.header, sw.name: obj.header}
            }
    
        def get_security_requirements(self, obj, context):
            return [{obj.name: []}]
    
    
    custom_auth_registry = AuthenticatorConverterRegistry()
    custom_auth_registry.register_type(SwaggerAuthConverter())
    

    Finally, this is where I set up the app:

    rebar = Rebar()
    swagger_generator = SwaggerV2Generator(authenticator_converter_registry=custom_auth_registry)
    registry = rebar.create_handler_registry(swagger_generator=swagger_generator)
    registry.set_default_authenticator(CustomAuthenticator)
    

    Here is the full backtrace:

    127.0.0.1 - - [04/Nov/2021 09:33:13] "GET /swagger HTTP/1.0" 500 -
    Traceback (most recent call last):
      File "/home/canopy/canopy/venv/lib/python3.6/site-packages/flask/app.py", line 2464, in __call__
        return self.wsgi_app(environ, start_response)
      File "/home/canopy/canopy/venv/lib/python3.6/site-packages/flask/app.py", line 2450, in wsgi_app
        response = self.handle_exception(e)
      File "/home/canopy/canopy/venv/lib/python3.6/site-packages/flask/app.py", line 1867, in handle_exception
        reraise(exc_type, exc_value, tb)
      File "/home/canopy/canopy/venv/lib/python3.6/site-packages/flask/_compat.py", line 39, in reraise
        raise value
      File "/home/canopy/canopy/venv/lib/python3.6/site-packages/flask/app.py", line 2447, in wsgi_app
        response = self.full_dispatch_request()
      File "/home/canopy/canopy/venv/lib/python3.6/site-packages/flask/app.py", line 1952, in full_dispatch_request
        rv = self.handle_user_exception(e)
      File "/home/canopy/canopy/venv/lib/python3.6/site-packages/flask/app.py", line 1822, in handle_user_exception
        return handler(e)
      File "/home/canopy/canopy/venv/lib/python3.6/site-packages/flask_rebar/rebar.py", line 831, in handle_generic_error
        raise error
      File "/home/canopy/canopy/venv/lib/python3.6/site-packages/flask/app.py", line 1950, in full_dispatch_request
        rv = self.dispatch_request()
      File "/home/canopy/canopy/venv/lib/python3.6/site-packages/flask/app.py", line 1936, in dispatch_request
        return self.view_functions[rule.endpoint](**req.view_args)
      File "/home/canopy/canopy/venv/lib/python3.6/site-packages/flask_rebar/rebar.py", line 612, in get_swagger
        registry=self, host=request.host_url.rstrip("/")
      File "/home/canopy/canopy/venv/lib/python3.6/site-packages/flask_rebar/swagger_generation/swagger_generator_v2.py", line 100, in generate_swagger
        return self.generate(registry=registry, host=host)
      File "/home/canopy/canopy/venv/lib/python3.6/site-packages/flask_rebar/swagger_generation/swagger_generator_v2.py", line 131, in generate
        self.authenticator_converter.get_security_schemes(authenticator)
      File "/home/canopy/canopy/venv/lib/python3.6/site-packages/flask_rebar/swagger_generation/authenticator_to_swagger.py", line 168, in get_security_schemes
        return self.get_security_schemes_legacy(registry=authenticator)
    AttributeError: 'AuthenticatorConverterRegistry' object has no attribute 'get_security_schemes_legacy'
    
    bug 
    opened by mattcarp12 5
  • Feature: Add SchemaOpts to opt-in to validation

    Feature: Add SchemaOpts to opt-in to validation

    Currently (technically, when 2.0.1 is merged and released) it is possible to opt in to response validation at the schema level by inclusion of `RequireOnDumpMixin. While this works, a more natural fit would be to allow this to be directly specified in the schema definition itself by including a "custom class Meta option" as described here: https://marshmallow.readthedocs.io/en/stable/extending.html

    opened by RookieRick 0
  • Rebar can generate invalid OpenAPI specs: non-unique operationId's

    Rebar can generate invalid OpenAPI specs: non-unique operationId's

    https://swagger.io/docs/specification/paths-and-operations/#operationid

    operationId is an optional unique string used to identify an operation. If provided, these IDs must be unique among all operations described in your API.

    This is important because things downstream of the spec (such as generated clients) may use the operationId (e.g. to code-generate methods) in such a way that depends on unique operationIds.

    However, it looks like Rebar's OpenAPI spec generation uses the function name to generate operationIds, which need not be unique:

    # my_rebar_registry.py 
    from flask_rebar import Rebar
    
    rebar = Rebar()
    registry = rebar.create_handler_registry(prefix="/api")
    
    # handlers1.py 
    from my_rebar_registry import registry
    
    @registry.handles(
        rule="/foo",
    )
    def foo():
        return "foo"
    
    # handlers2.py 
    from my_rebar_registry import registry
    
    @registry.handles(
        rule="/bar",
    )
    def foo():
        return "bar"
    
    # my_api_spec.py
    import json
    
    from my_rebar_registry import registry
    import handlers1
    import handlers2
    
    if __name__ == "__main__":
        print(json.dumps(registry.swagger_generator.generate_swagger(registry), indent=2))
    
    > python -m my_api_spec | grep operationId  # thither be duplicates
            "operationId": "foo",
            "operationId": "foo",
    
    (Click for full spec output)
    {
      "consumes": [
        "application/json"
      ],
      "definitions": {
        "Error": {
          "properties": {
            "errors": {
              "type": "object"
            },
            "message": {
              "type": "string"
            }
          },
          "required": [
            "message"
          ],
          "title": "Error",
          "type": "object"
        }
      },
      "host": "localhost",
      "info": {
        "description": "",
        "title": "My API",
        "version": "1.0.0"
      },
      "paths": {
        "/api/bar": {
          "get": {
            "operationId": "foo",
            "responses": {
              "default": {
                "description": "Error",
                "schema": {
                  "$ref": "#/definitions/Error"
                }
              }
            }
          }
        },
        "/api/foo": {
          "get": {
            "operationId": "foo",
            "responses": {
              "default": {
                "description": "Error",
                "schema": {
                  "$ref": "#/definitions/Error"
                }
              }
            }
          }
        }
      },
      "produces": [
        "application/json"
      ],
      "schemes": [],
      "securityDefinitions": {},
      "swagger": "2.0"
    }
    

    This may not be a terribly big deal in practice, since the moment you try to use such a registry with a Flask app, Flask's own duplication checking (of endpoint names) prevents you from doing so:

    # app.py 
    from flask import Flask
    
    from my_rebar_registry import rebar
    import handlers1
    import handlers2
    
    app = Flask("app")
    rebar.init_app(app)  # kaboom
    
    > python -m app
    Traceback (most recent call last):
      File "/usr/local/Cellar/[email protected]/3.9.6/Frameworks/Python.framework/Versions/3.9/lib/python3.9/runpy.py", line 197, in _run_module_as_main
        return _run_code(code, main_globals, None,
      File "/usr/local/Cellar/[email protected]/3.9.6/Frameworks/Python.framework/Versions/3.9/lib/python3.9/runpy.py", line 87, in _run_code
        exec(code, run_globals)
      File "/Users/jab/tmp/rebarv2/app.py", line 9, in <module>
        rebar.init_app(app)  # kaboom
      File "/Users/jab/tmp/rebarv2/.venv/lib/python3.9/site-packages/flask_rebar/rebar.py", line 784, in init_app
        registry.register(app=app)
      File "/Users/jab/tmp/rebarv2/.venv/lib/python3.9/site-packages/flask_rebar/rebar.py", line 554, in register
        self._register_routes(app=app)
      File "/Users/jab/tmp/rebarv2/.venv/lib/python3.9/site-packages/flask_rebar/rebar.py", line 576, in _register_routes
        app.add_url_rule(
      File "/Users/jab/tmp/rebarv2/.venv/lib/python3.9/site-packages/flask/app.py", line 98, in wrapper_func
        return f(self, *args, **kwargs)
      File "/Users/jab/tmp/rebarv2/.venv/lib/python3.9/site-packages/flask/app.py", line 1282, in add_url_rule
        raise AssertionError(
    AssertionError: View function mapping is overwriting an existing endpoint function: api.foo
    

    However, it still seems preferable to make Rebar's OpenAPI spec generation smart enough to not generate invalid specs in this way. I think Rebar should instead raise an error telling the user to provide a unique function name to avoid generating duplicate operationIds, or perhaps better yet, the @registry.handles() decorators (and so forth) could accept an operation_id param that allows the user to explicitly set the associated operationId that gets generated into the spec, making the operationId no longer tied to the Python function name 1-to-1. (A non-option, IMO, would be for Rebar to silently append some number to deduplicate what would otherwise be a duplicate operationId, which I've seen some Swagger tooling do, and it causes all kinds of madness.)

    opened by jab 0
Releases(v2.2.1)
Owner
PlanGrid
PlanGrid
Telegram bot + Flask API ( Make Introduction pages )

Introduction-Page-Maker Setup the api Upload the flask api on your host Setup requirements Make pages file on your host and upload the css and js and

Plugin 9 Feb 11, 2022
A Python chat app built with Flask that runs in the browser.

A Python chat app built with Flask that runs in the browser. Designed for local area networks that are not connected to the Internet.

Leonard Kleber 1 Dec 23, 2021
Geometry Dash Song Bypass with Python Flask Server

Geometry Dash Song Bypass with Python Flask Server

pixelsuft‮ 1 Nov 16, 2021
Small flask based opds catalog designed to serve a directory via OPDS

teenyopds Small flask based opds catalog designed to serve a directory via OPDS, it has currently only been verified to work with KyBook 3 on iOS but

Adam Furbee 4 Jul 14, 2022
RestApi_flask_sql.alchemy - Product REST API With Flask & SQL Alchemy

REST API With Flask & SQL Alchemy Products API using Python Flask, SQL Alchemy and Marshmallow Quick Start Using Pipenv # Activate venv $ pipenv shell

amirwahla 1 Jan 01, 2022
Another redis monitor by using flask, angular, socket.io

RedisPAPA we use redis info to monitor the redis usage. PAPA means a father who is monitoring the redis. accoding to the redis doc, it is be recommand

no13bus 393 Dec 30, 2022
Paid roles for discord using Stripe, Python, Flask & Docker

Welcome to Paycord Paid roles for discord using Stripe, Python, Flask & Docker. Setup Production On stripe dashboard, go Developers ➡️ Webhooks ➡️ Add

Ward 12 Dec 28, 2022
Flaskr: Intro to Flask, Test-Driven Development (TDD), and JavaScript

Flaskr - Intro to Flask, Test-Driven Development, and JavaScript Share on Twitter As many of you know, Flaskr -- a mini-blog-like-app -- is the app th

Michael Herman 2.2k Jan 04, 2023
A Flask app template with integrated SQLAlchemy, authentication, and Bootstrap frontend

Flask-Bootstrap Flask-Bootstrap is an Flask app template for users to clone and customize as desired, as opposed to a Flask extension that you can ins

Eric S. Bullington 204 Dec 26, 2022
Flask Boilerplate - Paper Kit Design | AppSeed

Flask Paper Kit Open-Source Web App coded in Flask Framework - Provided by AppSeed Web App Generator. App Features: SQLite database SQLAlchemy ORM Ses

App Generator 86 Nov 29, 2021
A Flask wrapper of Starknet state. Similar in purpose to Ganache.

Introduction A Flask wrapper of Starknet state. Similar in purpose to Ganache. Aims to mimic Starknet's Alpha testnet, but with simplified functionali

Shard Labs 159 Jan 04, 2023
Small and simple gravatar usage in Flask.

Flask Gravatar About This is small and simple integration gravatar into flask. Installation Flask-Gravatar is on PyPI so all you need is: pip install

Alexander Zelenyak 78 Sep 15, 2022
REST API with mongoDB and Flask.

Flask REST API with mongoDB py 3.10 First, to install all dependencies: python -m pip install -r requirements.txt Second, into the ./src/ folder, cop

Luis Quiñones Requelme 3 Mar 05, 2022
Criando um Bot com PYAUTOGUI e utilizando o Flask para Interface para Usuário

Criando um Bot com PYAUTOGUI e utilizando o Flask para Interface para Usuário O pyautogui foi escolhido pela possibilidade de fazer a identificação do

Rodrigo Vital 2 Oct 20, 2021
An extension to add support of Plugin in Flask.

An extension to add support of Plugin in Flask.

Doge Gui 31 May 19, 2022
Find and notify users in your Active Directory with weak passwords

Crack-O-Matic Find and notify users in your Active Directory with weak passwords. Features: Linux-based Flask-based web app Hashcat or John cracker Au

Adrian Vollmer 92 Dec 31, 2022
This is a simple web application using Python Flask and MySQL database.

Simple Web Application This is a simple web application using Python Flask and MySQL database. This is used in the demonstration of development of Ans

Alaaddin Tarhan 1 Nov 16, 2021
A boilerplate Flask API for a Fullstack Project :rocket:

Flask Boilerplate to quickly get started with production grade flask application with some additional packages and configuration prebuilt.

Yasser Tahiri 32 Dec 24, 2022
Flask starter template for better structuring.

Flask Starter app Flask starter template for better structuring. use the starter plate step 1 : cloning this repo through git clone the repo git clone

Tirtharaj Sinha 1 Jul 26, 2022
Glauth management ui created with python/flask

glauth-ui Glauth-UI is a small flask web app i created to manage the minimal glauth ldap server. I created this as i wanted to use glauth for authenti

Nils Thiele 67 Nov 29, 2022