Blueprint Plugins¶
Flask Smore Better extends the functionality of Flask-Smorest by allowing plugins to be used to generate OpenAPI documentation. The following sections describe how to create and use plugins.
Designing a Plugin¶
In this example, we will be creating a plugin to implement route security.
Warning
The example below is not secure and is only meant to show how to create a plugin. DO NOT USE THIS IN PRODUCTION! YOU HAVE BEEN WARNED!
To get started, create a class that extends PluginABC and set the __decorator_name__ class attribute:
import flask # We will need this later
from flask_smore_better.blueprint.plugin import PluginABC
class SecurityExample(PluginABC):
"""Example plugin"""
__decorator_name__ = 'api_security'
# ... continued below ...
The __decorator_name__ attribute is used to identify the decorator method name that the plugin will use to decorate the view functions. More on this later.
Next we will implement the api_init method. This method is used to add API documentation to the OpenAPI schema on a global level. In this example, we will add a security scheme to the OpenAPI schema:
def api_init(self, api):
api.spec.components.security_scheme('example_security', {
'type': 'apiKey',
'in': 'header',
'name': 'X-API-Key'
})
# ... continued below ...
The api parameter is an instance of flask_smore_better.api.Api and is used to interact with the OpenAPI schema.
Next we must implement the __process_wrapper_args__ method. This method is used to determine the route specific schema that will be added to the OpenAPI schema for the route decorated by this plugin. It will receive the arguments passed to the decorator and must return a dictionary.
def __process_wrapper_args__(self, *args, **kwargs):
return {
'security': [{'example_security': []}]
}
# ... continued below ...
Next, we must implement the prepare_apidocs method. This method is used to add the route specific schema to the OpenAPI schema:
def prepare_apidocs(self, current_docs, new_docs, **kwargs):
if "security" in new_docs:
return {"security": new_docs["security"]}
return {}
# ... continued below ...
This method will receive the current docs for the route, the docs to be added to the route (from the __process_wrapper_args__ method), the flask_smore_better.api.Api instance, and the APISpec instance; it may also recieve additional kwargs. It must return a dictionary that will be used to update the route’s current docs.
Note
This method will be called for every plugin when the docs are created, hence the need to check if the route specific schema is present in the new docs.
Lastly, we will implement the __on_request__ method. This method is used to perform any actions before the route is executed. In this example, we will check if the X-API-Key header is present in the request and matches our pretend API key:
def __on_request__(self, *args, **kwargs):
# Reminder: This is not secure! Do not use this in production!
if flask.request.headers.get('X-API-Key') != 'example_key':
flask.abort(401)
return {}
This method will receive the arguments and keyword arguments passed to the decorator. It is called when the decorated route is executed and its return value is passed to the decorated route method as keyword arguments.
Using Plugins¶
First, we need to create a new instance of the plugin and add it to the plugins class attribute of the Blueprint class:
from flask_smore_better.blueprint.blueprint import Blueprint
from flask.views import MethodView
from .security import SecurityExample # We will pretend this
# is the plugin we created above
Blueprint.plugins.append(SecurityExample())
# ... continued below ...
Now we can use the plugin by decorating a route with the decorator name we set in the plugin:
index = Blueprint('index', __name__)
@index.route('/')
class Index(MethodView):
@index.api_security()
def get(self, **kwargs):
return {'message': 'Hello, World!'}
Warning
The decorator must be called using the @decorator_name() pattern. Any arguments passed here will be passed to the __process_wrapper_args__ and __on_request__ methods of the plugin. This is so that the plugin can be configured on a per-route basis (if desired).
See also
For information on how plugins interact with blueprint collectors, see Blueprint Collectors.