def openapi_spec() -> apispec.APISpec: """Return OpenAPI (fka Swagger) spec for API.""" spec = apispec.APISpec( title="httpstan HTTP-based REST API", version=httpstan.__version__, openapi_version="2.0", # plugin order, MarshmallowPlugin resolves schema references created by DocPlugin plugins=[DocPlugin(), apispec.ext.marshmallow.MarshmallowPlugin()], ) spec.path(path="/v1/health", view=views.handle_health) spec.path(path="/v1/models", view=views.handle_create_model) spec.path(path="/v1/models", view=views.handle_list_models) spec.path(path="/v1/models/{model_id}", view=views.handle_delete_model) spec.path(path="/v1/models/{model_id}/params", view=views.handle_show_params) spec.path(path="/v1/models/{model_id}/log_prob", view=views.handle_log_prob) spec.path(path="/v1/models/{model_id}/log_prob_grad", view=views.handle_log_prob_grad) spec.path(path="/v1/models/{model_id}/write_array", view=views.handle_write_array) spec.path(path="/v1/models/{model_id}/transform_inits", view=views.handle_transform_inits) spec.path(path="/v1/models/{model_id}/fits", view=views.handle_create_fit) spec.path(path="/v1/models/{model_id}/fits/{fit_id}", view=views.handle_get_fit) spec.path(path="/v1/models/{model_id}/fits/{fit_id}", view=views.handle_delete_fit) spec.path(path="/v1/operations/{operation_id}", view=views.handle_get_operation) apispec.utils.validate_spec(spec) return spec
def create_app(): app = flask.Flask(__name__) app.config.from_object(get_config()) api_title = 'starter_pack' version = '1.0.0' spec = apispec.APISpec( title=api_title, version=version, plugins=[ 'apispec.ext.flask', ], ) swagger_url = '/docs' def doc_spec(): return json.dumps(spec.to_dict()) blueprint = starter_pack.routes.create_blueprint_with_routes() blueprint.add_url_rule('/spec.json', view_func=doc_spec) app.register_blueprint( create_swagger_ui(swagger_url, api_title), url_prefix=swagger_url ) app.register_blueprint(blueprint) print('Environment: {}'.format(app.config['ENVIRONMENT'])) return app
def generate_schema(api, config=None): if not config: cwd = os.getcwd() try: with open(os.path.join(cwd, '.feistyrc.yaml')) as f: config = yaml.load(f) except (IOError, OSError): raise ValueError('Could not find .feistyrc.yaml file') schema = FeistyConfigSchema() result = schema.load(config) if result.errors: raise ValueError('Invalid Feisty config: {}'.format(result.errors)) config = result.data spec = apispec.APISpec( config['title'], str(config['version']), openapi_version='2.0', plugins=(apispec.ext.marshmallow.MarshmallowPlugin(), )) _recurse_nodes(api._router._roots, spec) return spec
def test_multi_operation_multi_reponses(self, openapi_version): """Should loop all operations and all responses.""" spec = apispec.APISpec('title', 'version', openapi_version) plugin = ResponseReferencesPlugin() plugin.init_spec(spec) operations = { 'get': { 'responses': { http.HTTPStatus.OK.value: http.HTTPStatus.OK.name, http.HTTPStatus.NO_CONTENT.value: http.HTTPStatus.NO_CONTENT.name, } }, 'post': { 'responses': { http.HTTPStatus.OK.value: http.HTTPStatus.OK.name, # Ignored repeat http.HTTPStatus.CREATED.value: http.HTTPStatus.CREATED.name, } }, } plugin.operation_helper(operations=operations) components = spec.to_dict() if openapi_version == '3.0.2': components = components['components'] assert len(components['responses']) == 3 # 200, 201, 204
def make_apispec(handlers, output='data/basic-apispec.json'): spec = apispec.APISpec( title='BasicRESTSpec', version='1.5.6', info=dict(description='OpenAPI specification for the Resort server'), plugins=['apispec.ext.tornado']) for urlspec in handlers: spec.add_path(urlspec=urlspec) output_fullpath = os.path.abspath(output) with open(output_fullpath, 'w') as fp: json.dump(spec.to_dict(), fp)
def openapi_spec() -> str: """Return OpenAPI (fka Swagger) spec for API.""" spec = apispec.APISpec( title="httpstan API", version=httpstan.__version__, plugins=["apispec.ext.marshmallow"] ) spec.add_path(path="/v1/models", view=views.handle_models) spec.add_path(path="/v1/models/{model_id}/actions", view=views.handle_models_actions) spec.add_path(path="/v1/models/{model_id}/params", view=views.handle_models_params) spec.definition("Model", schema=views.ModelSchema) spec.definition("ModelsAction", schema=views.ModelsActionSchema) spec.definition("Param", schema=views.ParamSchema) return json.dumps(spec.to_dict())
def _init_spec(self, *, flask_plugin=None, marshmallow_plugin=None, extra_plugins=None, openapi_version=None, **options): # Plugins self.flask_plugin = flask_plugin or FlaskPlugin() self.ma_plugin = marshmallow_plugin or MarshmallowPlugin() plugins = [self.flask_plugin, self.ma_plugin] plugins.extend(extra_plugins or ()) # APISpec options openapi_version = self._app.config.get('OPENAPI_VERSION', openapi_version) if openapi_version is None: raise OpenAPIVersionNotSpecified( 'The OpenAPI version must be specified, either as ' '"OPENAPI_VERSION" app parameter or as ' '"openapi_version" spec kwarg.') openapi_major_version = int(openapi_version.split('.')[0]) if openapi_major_version < 3: base_path = self._app.config.get('APPLICATION_ROOT') options.setdefault('basePath', base_path) options.setdefault('produces', [ DEFAULT_RESPONSE_CONTENT_TYPE, ]) options.setdefault('consumes', [ DEFAULT_REQUEST_BODY_CONTENT_TYPE, ]) options.update(self._app.config.get('API_SPEC_OPTIONS', {})) # Instantiate spec self.spec = apispec.APISpec( self._app.name, self._app.config.get('API_VERSION', '1'), openapi_version=openapi_version, plugins=plugins, **options, ) # Register custom fields in spec for args in self._fields: self._register_field(*args) # Register custom converters in spec for args in self._converters: self._register_converter(*args) # Register Upload field properties function self.ma_plugin.converter.add_attribute_function(uploadfield2properties) # Register OpenAPI command group self._app.cli.add_command(openapi_cli)
def openapi_spec() -> str: """Return OpenAPI (fka Swagger) spec for API.""" spec = apispec.APISpec( title='httpstan API', version=httpstan.__version__, plugins=['apispec.ext.marshmallow'], ) spec.add_path(path='/v1/programs', view=views.handle_programs) spec.add_path(path='/v1/programs/{program_id}/actions', view=views.handle_programs_actions) spec.definition('Program', schema=views.ProgramSchema) spec.definition('ProgramAction', schema=views.ProgramActionSchema) return json.dumps(spec.to_dict())
def _init_spec(self, *, flask_plugin=None, marshmallow_plugin=None, extra_plugins=None, openapi_version=None, **options): # Plugins self.flask_plugin = flask_plugin or FlaskPlugin() self.ma_plugin = marshmallow_plugin or MarshmallowPlugin() plugins = [self.flask_plugin, self.ma_plugin] plugins.extend(extra_plugins or ()) # APISpec options openapi_version = self._app.config.get('OPENAPI_VERSION', openapi_version) if openapi_version is None: raise OpenAPIVersionNotSpecified( 'The OpenAPI version must be specified, either as ' '"OPENAPI_VERSION" app parameter or as ' '"openapi_version" spec kwarg.') openapi_major_version = int(openapi_version.split('.')[0]) if openapi_major_version < 3: base_path = self._app.config.get('APPLICATION_ROOT') options.setdefault('basePath', base_path) options.setdefault('produces', [ 'application/json', ]) options.setdefault('consumes', [ 'application/json', ]) options.update(self._app.config.get('API_SPEC_OPTIONS', {})) # Instantiate spec self.spec = apispec.APISpec( self._app.name, self._app.config.get('API_VERSION', '1'), openapi_version=openapi_version, plugins=plugins, **options, ) # Register custom fields in spec for args in self._fields: self._register_field(*args) # Register schemas in spec for name, schema_cls, kwargs in self._schemas: self.spec.components.schema(name, schema=schema_cls, **kwargs) # Register custom converters in spec for args in self._converters: self._register_converter(*args)
def __init__(self, title: str, version: str, description: str, openapi_version="3.0.0"): assert apispec is not None, "`apispec` must be installed to use SchemaGenerator." from apispec.ext.marshmallow import MarshmallowPlugin marshmallow_plugin = MarshmallowPlugin() self.spec = apispec.APISpec( title=title, version=version, openapi_version=openapi_version, info={"description": description}, plugins=[marshmallow_plugin], ) self.converter = marshmallow_plugin.converter self.resolver = marshmallow_plugin.resolver # Builtin definitions self.schemas = SchemaRegistry(self.spec, self.resolver)
def setup_apispec(app): """Setup Swagger UI. Args: app ([flask.Flask]): the application """ app.config.update({ "APISPEC_SPEC": apispec.APISpec( title="cnapps API", version="", info=dict(description="REST API of Cnapps."), plugins=("apispec.ext.marshmallow", ), ), # 'APISPEC_SWAGGER_UI_URL': "/%s/swagger-ui" % core.PATH, # 'APISPEC_SWAGGER_URL': "/%s/swagger" % core.PATH, }) docs = flask_apispec.FlaskApiSpec(app) # PB: on a aussi l'API en defaults avec : # docs.register_existing_resources() v1_api_path = "/api/%s" % v1_core.PATH v1alpha_api_path = "/api/%s" % v1alpha_core.PATH for name, rule in app.view_functions.items(): try: blueprint_name, _ = name.split(".") if isinstance(rule, types.FunctionType): paths = docs.view_converter.convert(rule, None, blueprint_name) LOGGER.debug("Endpoint: %s", paths[0]["path"]) if paths[0]["path"].startswith(v1_api_path) or paths[0][ "path"].startswith(v1alpha_api_path): LOGGER.info("Add to Swagger: %s", paths[0]["path"]) docs.register(rule, blueprint=blueprint_name) elif paths[0]["path"].startswith("/api/apis"): docs.register(rule, blueprint=blueprint_name) except ValueError: pass except TypeError: pass
def create_spec() -> apispec.APISpec: """ Create an `APISpec` that documents the MHS API :return: the `APISpec` that documents the MHS API """ spec = apispec.APISpec(title='MHS API docs', version='0.1', openapi_version='3.0.2', plugins=[ apispec.ext.marshmallow.MarshmallowPlugin(), apispec_webframeworks.tornado.TornadoPlugin() ]) spec.components.schema("RequestBody", schema=request_body_schema.RequestBodySchema) # The paths here should match the paths in main.py spec.path(urlspec=(r'/', handler.SynchronousHandler)) spec.path(urlspec=(r'/healthcheck', healthcheck_handler.HealthcheckHandler)) return spec
def test_api_registers_error_responses(self, openapi_version, http_status_code, http_status_name): """Responses should be added to spec.""" spec = apispec.APISpec('title', 'version', openapi_version) plugin = ResponseReferencesPlugin() plugin.init_spec(spec) operations = { 'get': { 'responses': { http_status_code: http_status_name, } } } plugin.operation_helper(operations=operations) components = spec.to_dict() if openapi_version == '3.0.2': components = components['components'] assert len(components['responses']) == 1 assert http_status_name in components['responses']
def test_repeated_response(self, openapi_version): """Repeated response, different endpoint.""" spec = apispec.APISpec('title', 'version', openapi_version) plugin = ResponseReferencesPlugin() plugin.init_spec(spec) operations = { 'get': { 'responses': { http.HTTPStatus.OK.value: http.HTTPStatus.OK.name, } } } # operation_helper is called on each path, so this simulates # 2 endpoints with the same response plugin.operation_helper(operations=copy.deepcopy(operations)) plugin.operation_helper(operations=copy.deepcopy(operations)) components = spec.to_dict() if openapi_version == '3.0.2': components = components['components'] assert len(components['responses']) == 1
def add_routes_from(module, spec): for _, f in vars(module).items(): if callable(f) and hasattr(f, 'func_doc') and getattr(f, 'func_doc'): try: spec.add_path(view=f) except apispec.exceptions.APISpecError: print( "Failed to add documented callable {} to swagger.".format( f)) spec = apispec.APISpec( title='trackit.io', version=BUILD_VERSION, plugins=[ 'apispec.ext.flask', 'apispec.ext.marshmallow', ], ) ctx = app.test_request_context() ctx.push() from app.views.aws import account_management, forecast, usage, aws_key_schema from app.views.aws.cost import cost, stats add_routes_from(account_management, spec) add_routes_from(cost, spec) add_routes_from(stats, spec) add_routes_from(forecast, spec) add_routes_from(usage, spec)
import apispec from apispec.ext.marshmallow import MarshmallowPlugin from apispec_webframeworks.flask import DocumentedBlueprint, FlaskPlugin from flask import jsonify from flask.views import MethodView spec = apispec.APISpec( title="Swagger Petstore", version="1.0.0", openapi_version="3.0.2", plugins=[FlaskPlugin(), MarshmallowPlugin()], ) bp = DocumentedBlueprint('documented_bp', __name__, spec) @bp.route('/') def index(): """ Gist detail view. --- x-extension: metadata get: responses: 200: schema: $ref: '#/definitions/Gist' """ return 'index'
import apispec from apispec.exceptions import DuplicateComponentNameError from apispec.ext.marshmallow import MarshmallowPlugin from contextlib import suppress from .schemas import * from .resources import * from inspect import isclass from marshmallow.fields import String from drowsy.schema import ModelResourceSchema from drowsy.fields import NestedRelated, APIUrl import inflection ma_plugin = MarshmallowPlugin() spec = apispec.APISpec(title="Swagger Docs", version="0.1.1", openapi_version="3.0.2", plugins=[ma_plugin]) def nestedrelated2properties(self, field, **kwargs): """Handle NestedRelated Drowsy field. Only needed due to potential use of `many=True`, whereas Nested fields are often embedded in List fields. This is pretty hacky, and basically just moves around properties that `nested2properties` generated. :param MarshmallowPlugin self: This method will end up bound to a MarshmallowPlugin instance. :param field: The marshmallow field being converted. :return: A dict of properties to be included in our spec.
def _init_spec(self, *, flask_plugin=None, marshmallow_plugin=None, response_plugin=None, extra_plugins=None, title=None, version=None, openapi_version=None, **options): # Plugins self.flask_plugin = flask_plugin or FlaskPlugin() self.ma_plugin = marshmallow_plugin or MarshmallowPlugin() self.resp_plugin = (response_plugin or ResponseReferencesPlugin(self.ERROR_SCHEMA)) plugins = [self.flask_plugin, self.ma_plugin, self.resp_plugin] plugins.extend(extra_plugins or ()) # APISpec options title = self._app.config.get('API_TITLE', title) if title is None: raise MissingAPIParameterError( 'API title must be specified either as "API_TITLE" ' 'app parameter or as "title" spec kwarg.') version = self._app.config.get('API_VERSION', version) if version is None: raise MissingAPIParameterError( 'API version must be specified either as "API_VERSION" ' 'app parameter or as "version" spec kwarg.') openapi_version = self._app.config.get('OPENAPI_VERSION', openapi_version) if openapi_version is None: raise MissingAPIParameterError( 'OpenAPI version must be specified either as "OPENAPI_VERSION ' 'app parameter or as "openapi_version" spec kwarg.') openapi_major_version = int(openapi_version.split('.')[0]) if openapi_major_version < 3: options.setdefault('produces', [ DEFAULT_RESPONSE_CONTENT_TYPE, ]) options.setdefault('consumes', [ DEFAULT_REQUEST_BODY_CONTENT_TYPE, ]) options.update(self._app.config.get('API_SPEC_OPTIONS', {})) # Instantiate spec self.spec = apispec.APISpec( title, version, openapi_version, plugins, **options, ) # Register custom fields in spec for args in self._fields: self._register_field(*args) # Register custom converters in spec for args in self._converters: self._register_converter(*args) # Register Upload field properties function self.ma_plugin.converter.add_attribute_function(uploadfield2properties) # Register OpenAPI command group self._app.cli.add_command(openapi_cli)
os.getcwd(), 'config', os.getenv('APPLICATION_ENV', 'development').lower() + '.config')) # Create an API doc store for Swagger version_file_path = 'version.json' if isfile(version_file_path): version_details = json.loads(open(version_file_path).read()) version = 'Build: {0}, Commit: {1}'.format(version_details['build_no'], version_details['commit']) else: version = 'No {} file found.'.format(version_file_path) app.api_doc = apispec.APISpec( title='Range Planning Tool', version=version, description='Sainsburys range planner project.', plugins=[ 'apispec.ext.flask', 'apispec.ext.marshmallow', ], ) from api import api as api_blueprint app.register_blueprint(api_blueprint, url_prefix='/api') for view_name in app.view_functions.keys(): if not view_name.startswith('_') and view_name not in ['static']: view_fn = app.view_functions[view_name] app.api_doc.add_path(view=view_fn)
import apispec import apispec.ext.marshmallow import apispec_webframeworks.flask from ..api import _examples as ex from ..config import Config spec = apispec.APISpec( title=Config.APPLICATION_NAME, version="1.0.0", openapi_version="3.0.2", plugins=[ apispec_webframeworks.flask.FlaskPlugin(), apispec.ext.marshmallow.MarshmallowPlugin(), ], servers=[{ "url": "http://localhost:5000/" }], ) example2 = { "code": 400, "description": { "extra": ["Unknown field."] }, "name": "Bad Request", } def _error(error, example, schema=None): content = {"example": example}