def test_schema_name_resolver_returns_none_v3(self, pet_schema): def resolver(schema): return None spec = APISpec( title="Test resolver returns None", version="0.1", openapi_version="3.0.0", plugins=(MarshmallowPlugin(schema_name_resolver=resolver),), ) spec.path( path="/pet", operations={ "get": { "responses": { 200: {"content": {"application/json": {"schema": pet_schema}}} } } }, ) get = get_paths(spec)["/pet"]["get"] assert ( "properties" in get["responses"]["200"]["content"]["application/json"]["schema"] )
def test_resolve_schema_dict_auto_reference_in_list(self, schema): def resolver(schema): return schema.__name__ spec = APISpec( title='Test auto-reference', version='0.1', openapi_version='2.0', plugins=( MarshmallowPlugin(schema_name_resolver=resolver,), ), ) assert {} == get_definitions(spec) spec.components.schema('analysis', schema=schema) spec.path( '/test', operations={ 'get': { 'responses': { '200': { 'schema': { '$ref': '#/definitions/analysis', }, }, }, }, }, ) definitions = get_definitions(spec) assert 3 == len(definitions) assert 'analysis' in definitions assert 'SampleSchema' in definitions assert 'RunSchema' in definitions
def create_app(config_filename=None): if config_filename is None: conf = default_config.copy() else: conf = read_conf(config_filename) app = falcon.API() rethink_factory = RethinkDBFactory(**conf["rethinkdb"]) user_resource = Users(rethink_factory) card_resource = Cards(rethink_factory) app.add_route("/users", user_resource) app.add_route("/cards", card_resource) # Create an APISpec spec = APISpec( title='Swagger Prelaunch Spec', version='1.0.0', openapi_version='2.0', plugins=[ FalconPlugin(app), MarshmallowPlugin(), ], ) # Register entities and paths spec.components.schema('User', schema=User.schema()) spec.components.schema('Card', schema=Card.schema()) spec.path(resource=user_resource) spec.path(resource=card_resource) return app, spec
def generate_api_docs(filename, api_version, openapi_version): """ Generates swagger (openapi) yaml from routes' docstrings (using apispec library). """ from apispec import APISpec from apispec_webframeworks.flask import FlaskPlugin apidoc = APISpec( title="Grafolean API", version=api_version, openapi_version=openapi_version, plugins=[FlaskPlugin()], info={ "description": "> IMPORTANT: API is under development and is **not final yet** - breaking changes might occur.\n\n" \ "Grafolean is designed API-first. In other words, every functionality of the system is accessible through the API " \ "described below. This allows integration with external systems so that (given the permissions) they too can " \ "enter values, automatically modify entities, set up dashboards... Everything that can be done through frontend " \ "can also be achieved through API.\n\n" \ "This documentation is also available as Swagger/OpenAPI definition: [OAS2](./swagger2.yml) / [OAS3](./swagger3.yml)." } ) for apidoc_schemas_func in [users_apidoc_schemas, accounts_apidoc_schemas, admin_apidoc_schemas]: for schema_name, schema in apidoc_schemas_func(): apidoc.components.schema(schema_name, schema) with app.test_request_context(): for rule in app.url_map.iter_rules(): view = app.view_functions.get(rule.endpoint) apidoc.path(view=view) with open(filename, 'w') as openapi_yaml_file: openapi_yaml_file.write(apidoc.to_yaml())
def test_resolve_schema_dict_auto_reference(self, schema): def resolver(schema): return schema.__name__ spec = APISpec( title="Test auto-reference", version="0.1", openapi_version="2.0", plugins=(SerpycoPlugin( schema_name_resolver=schema_name_resolver), ), ) assert {} == get_definitions(spec) spec.components.schema("analysis", schema=schema) spec.path( "/test", operations={ "get": { "responses": { "200": { "schema": { "$ref": "#/definitions/analysis" } } } } }, ) definitions = get_definitions(spec) assert 3 == len(definitions) assert "analysis" in definitions assert "SampleSchema" in definitions assert "RunSchema_exclude_sample" in definitions
def test_resolve_schema_dict_auto_reference_in_list(self, schema): def resolver(schema): return schema.__name__ spec = APISpec( title="Test auto-reference", version="0.1", openapi_version="2.0", plugins=(MarshmallowPlugin(schema_name_resolver=resolver),), ) with pytest.raises(KeyError): get_schemas(spec) spec.components.schema("analysis", schema=schema) spec.path( "/test", operations={ "get": { "responses": { "200": {"schema": build_ref(spec, "schema", "analysis")} } } }, ) definitions = get_schemas(spec) assert 3 == len(definitions) assert "analysis" in definitions assert "SampleSchema" in definitions assert "RunSchema" in definitions
def generate_swagger_file(handlers, file_location): """Automatically generates Swagger spec file based on RequestHandler docstrings and saves it to the specified file_location. """ # Starting to generate Swagger spec file. All the relevant # information can be found from here https://apispec.readthedocs.io/ spec = APISpec( title="Car Brand API", version="1.0.0", openapi_version="3.0.2", info=dict(description="Documentation for the Car Brand API"), plugins=[TornadoPlugin(), MarshmallowPlugin()], servers=[ { "url": "http://localhost:8888/", "description": "Local environment", }, ], ) # Looping through all the handlers and trying to register them. # Handlers without docstring will raise errors. That's why we # are catching them silently. for handler in handlers: try: spec.path(urlspec=handler) except APISpecError: pass # Write the Swagger file into specified location. with open(file_location, "w", encoding="utf-8") as file: json.dump(spec.to_dict(), file, ensure_ascii=False, indent=4)
def test_schema_uses_ref_if_available_name_resolver_returns_none_v3(self): def resolver(schema): return None spec = APISpec( title="Test auto-reference", version="0.1", openapi_version="3.0.0", plugins=(MarshmallowPlugin(schema_name_resolver=resolver),), ) spec.components.schema("Pet", schema=PetSchema) spec.path( path="/pet", operations={ "get": { "responses": { 200: {"content": {"application/json": {"schema": PetSchema}}} } } }, ) get = get_paths(spec)["/pet"]["get"] assert get["responses"]["200"]["content"]["application/json"][ "schema" ] == build_ref(spec, "schema", "Pet")
def get_json(app: Chalice, route_functions: dict, route_prefix: str, version="1.0.0"): spec = APISpec( title=app.app_name, version=version, openapi_version="3.0.2", plugins=[generate_plugin(app)(), MarshmallowPlugin()], servers=[{ "url": f"/{route_prefix}", "description": f"Host on /" }], ) fn_list = {[fn_name.view_name for fn_name in r.values()][0] for path, r in app.routes.items()} for fn_name in [ fn_name for fn_name in fn_list if fn_name in route_functions ]: try: spec.path(view=route_functions[fn_name]) except (TypeError, KeyError, ValueError) as e: logging.warning(f"Error parsing docstring: {e}") return json.dumps(spec.to_dict())
def _apispec(self): info = {} if self.description is not None: info["description"] = self.description if self.terms_of_service is not None: info["termsOfService"] = self.terms_of_service if self.contact is not None: info["contact"] = self.contact if self.license is not None: info["license"] = self.license spec = APISpec( title=self.title, version=self.version, openapi_version=self.openapi_version, plugins=[MarshmallowPlugin()], info=info, ) for route in self.routes: if self.routes[route].description: operations = yaml_utils.load_operations_from_docstring( self.routes[route].description) spec.path(path=route, operations=operations) for name, schema in self.schemas.items(): spec.components.schema(name, schema=schema) return spec
def document_endpoint_oauth2_authentication( spec: APISpec, *, endpoint: str, method: str, required_scopes: List[str], unauthorized_status_code: int = 401, forbidden_status_code: int = 403): spec.path( endpoint, operations={ method.lower(): { "responses": { str(unauthorized_status_code): { "description": "No permission -- see authorization schemes", "schema": { "type": "string" }, }, str(forbidden_status_code): { "description": "Request forbidden -- authorization will not help", "schema": { "type": "string" }, }, }, "security": [{ "oauth2": required_scopes }], } }, )
def get_spec(): """ Produce the OpenAPIv3 spec document. :rtype: dict """ spec = APISpec( title="rest-demo-api", version=__version__, openapi_version="3.0.2", plugins=[FlaskPlugin(), MarshmallowPlugin()], ) app = get_app() # Useful for debugging # print(app.view_functions) spec.path("/authors", view=app.view_functions["authors"], app=app) spec.path("/authors/{author_id}", view=app.view_functions["author"], app=app) spec.path("/authors/{author_id}/quotes", view=app.view_functions["authorquotes"], app=app) spec.path("/quotes", view=app.view_functions["quotes"], app=app) spec.path("/quotes/{quote_id}", view=app.view_functions["quote"], app=app) return spec.to_dict()
def test_schema_uses_ref_if_available_name_resolver_returns_none_v3(self): def resolver(schema): return None spec = APISpec( title='Test auto-reference', version='0.1', openapi_version='3.0.0', plugins=( MarshmallowPlugin(schema_name_resolver=resolver,), ), ) spec.components.schema('Pet', schema=PetSchema) spec.path( path='/pet', operations={ 'get': { 'responses': { 200: { 'content': { 'application/json': { 'schema': PetSchema, }, }, }, }, }, }, ) get = get_paths(spec)['/pet']['get'] assert get['responses'][200]['content']['application/json']['schema']['$ref'] == ref_path( spec, ) + 'Pet'
def add_paths(spec: APISpec): spec.path( path='/category/{id}', parameters=[param('id', 'ID of a Category')], operations=dict(get=method(responses=[ response(OK, 'Information about a category', CategorySchema), ])), )
def run_app( app: Flask, name: str, version: str, api_version: str, port: int, dataclasses: Optional[List[Type[JsonSchemaMixin]]] = None, print_spec: bool = False, ) -> None: spec = APISpec( title=f"{name} ({version})", version=api_version, openapi_version="3.0.2", plugins=[FlaskPlugin(), DataclassesPlugin()], ) if dataclasses is not None: for dc in dataclasses: spec.components.schema(dc.__name__, schema=dc) with app.test_request_context(): for rule in app.url_map.iter_rules(): if rule.endpoint != "static": spec.path(view=app.view_functions[rule.endpoint]) if print_spec: print(spec.to_yaml()) return @app.route("/swagger/api/swagger.json", methods=["GET"]) def get_swagger() -> str: return jsonify(spec.to_dict()) @app.errorhandler(Arcor2Exception) def handle_bad_request_general(e: Arcor2Exception) -> Tuple[str, int]: return json.dumps(str(e)), 400 @app.errorhandler(FlaskException) def handle_bad_request_intentional(e: FlaskException) -> Tuple[str, int]: return json.dumps(str(e)), e.error_code SWAGGER_URL = "/swagger" swaggerui_blueprint = get_swaggerui_blueprint( SWAGGER_URL, "./api/swagger.json" # Swagger UI static files will be mapped to '{SWAGGER_URL}/dist/' ) # Register blueprint at URL app.register_blueprint(swaggerui_blueprint, url_prefix=SWAGGER_URL) if not env.get_bool("ARCOR2_REST_API_DEBUG", False): # turn off logging each endpoint call by default log = logging.getLogger("werkzeug") log.setLevel(logging.ERROR) app.run(host="0.0.0.0", port=port)
def oas_schema(self, pkg): spec = APISpec( title=pkg.meta["name"].capitalize(), version=pkg.meta["version"], openapi_version=self.config["oas_version"], plugins=[MarshmallowPlugin()], ) for ctrl in pkg.controllers: if not isinstance(ctrl, BaseHttpController): continue routes = {} for func, handler in ctrl.handlers: if not handler.status: warnings.warn(f"No @returns for {func}, cannot generate OAS3 schema for this handler") break abspath = handler.path_full method = handler.method.lower() if abspath not in routes: routes[abspath] = {} if method not in routes[abspath]: routes[abspath][method] = dict( responses={}, parameters=[] ) route = routes[abspath][method] responses = route["responses"] parameters = route["parameters"] for location, schema_cls in handler.schemas: if location == "response": if not schema_cls: content = {} else: content = {"application/json": {"schema": schema_cls}} responses[handler.status] = dict( description=None, content=content ) elif location in ["path", "query", "header"]: if not schema_cls: continue parameters.append({ "in": location, "schema": schema_cls }) spec.path(handler.path_full, operations=routes[abspath]) return spec.to_dict()
def test_openapi_tools_validate_v2(): ma_plugin = MarshmallowPlugin() spec = APISpec( title="Pets", version="0.1", plugins=(ma_plugin,), openapi_version="2.0" ) openapi = ma_plugin.converter spec.components.schema("Category", schema=CategorySchema) spec.components.schema("Pet", {"discriminator": "name"}, schema=PetSchema) spec.path( view=None, path="/category/{category_id}", operations={ "get": { "parameters": [ {"name": "q", "in": "query", "type": "string"}, { "name": "category_id", "in": "path", "required": True, "type": "string", }, openapi.field2parameter( field=fields.List( fields.Str(), validate=validate.OneOf(["freddie", "roger"]), location="querystring", ), default_in=None, name="body", ), ] + openapi.schema2parameters(PageSchema, default_in="query"), "responses": {200: {"schema": PetSchema, "description": "A pet"}}, }, "post": { "parameters": ( [ { "name": "category_id", "in": "path", "required": True, "type": "string", } ] + openapi.schema2parameters(CategorySchema, default_in="body") ), "responses": {201: {"schema": PetSchema, "description": "A pet"}}, }, }, ) try: utils.validate_spec(spec) except exceptions.OpenAPIError as error: pytest.fail(str(error))
def add_paths(spec: APISpec): spec.path( path='/category/{id}', parameters=[param('id', 'Identifier of a category')], operations=dict( get=method(tags=['categories'], responses=[ response_json(OK, 'Information about a category', CategorySchema), ])), )
def test_plugin_operation_helper_is_used(self, openapi_version): spec = APISpec( title="Swagger Petstore", version="1.0.0", openapi_version=openapi_version, plugins=(self.test_plugin_factory(), ), ) spec.path("/path_2", operations={"post": {"responses": {"200": {}}}}) paths = get_paths(spec) assert len(paths) == 1 assert paths["/path_2"] == {"post": {"responses": {"201": {}}}}
def test_plugin_operation_helper_is_used(self, openapi_version): spec = APISpec( title='Swagger Petstore', version='1.0.0', openapi_version=openapi_version, plugins=(self.test_plugin_factory(), ), ) spec.path('/path_2', operations={'post': {'responses': {'200': {}}}}) paths = get_paths(spec) assert len(paths) == 1 assert paths['/path_2'] == {'post': {'responses': {'201': {}}}}
def convert(self, yaml=False, references=True, ignorespec=None, **options): spec = APISpec(title=self.info.get('title'), version=self.info.get('version'), openapi_version=self.openapi, plugins=[MultiOperationBuilderPlugin(references)], **options) for requestitem in self.postman_collection.get_requestitems(): spec.path(path=requestitem.get_request().path, operations=Operation(requestitem, ignorespec=ignorespec).get()) return spec.to_yaml() if yaml else spec.to_dict()
def document_response(spec: APISpec, *, endpoint: str, method: str, status_code: int, response: dict): spec.path( endpoint, operations={ method.lower(): { "responses": { str(status_code): response } } }, )
def test_path_summary(): app = Starlette() plugin = StarlettePlugin(app) spec = APISpec(title="Test API", version="0.0.1", openapi_version="2.0", plugins=[plugin]) @app.route("/test") def test_endpoint(request): """This is the expected summary""" pass # pragma: no cover @app.route("/test_with_doc") def test_endpoint_with_doc(request): """ This is the expected summary 2 --- responses: 200: description: "ok" """ pass # pragma: no cover for endpoint in plugin.endpoints(): spec.path(endpoint.path, endpoint=endpoint) assert spec.to_dict() == { "info": { "title": "Test API", "version": "0.0.1" }, "paths": { "/test": { "get": { "operationId": "get_test_endpoint", "summary": "This is the expected summary", } }, "/test_with_doc": { "get": { "summary": "This is the expected summary 2", "operationId": "get_test_endpoint_with_doc", "responses": { "200": { "description": "ok" } }, } }, }, "swagger": "2.0", }
def test_path_called_twice_with_same_operations_parameters( self, openapi_version): """Test calling path twice with same operations or parameters operations and parameters being mutated by clean_operations and plugin helpers should not make path fail on second call """ class TestPlugin(BasePlugin): def path_helper(self, path, operations, parameters, **kwargs): """Mutate operations and parameters""" operations.update( {"post": { "responses": { "201": "201ResponseRef" } }}) parameters.append("ParamRef_3") return path spec = APISpec( title="Swagger Petstore", version="1.0.0", openapi_version=openapi_version, plugins=[TestPlugin()], ) path = "/pet/{petId}" parameters = ["ParamRef_1"] operation = { "parameters": ["ParamRef_2"], "responses": { "200": "200ResponseRef" }, } spec.path(path=path, operations={"get": operation}, parameters=parameters) spec.path(path=path, operations={"put": operation}, parameters=parameters) operations = (get_paths(spec))[path] assert (operations["get"] == operations["put"] == { "parameters": [build_ref(spec, "parameter", "ParamRef_2")], "responses": { "200": build_ref(spec, "response", "200ResponseRef") }, }) assert operations["parameters"] == [ build_ref(spec, "parameter", "ParamRef_1"), build_ref(spec, "parameter", "ParamRef_3"), ]
def build_openapi_spec(handlers: List[Tuple[str, Callable]]) -> APISpec: spec = APISpec( title='VGS Satellite management API', version='1.0.0', openapi_version='3.0.2', info={'description': 'The management API for VGS Satellite app'}, plugins=[TornadoPlugin(), MarshmallowPlugin()], ) for urlspec in handlers: spec.path(urlspec=urlspec) return spec
def get_spec(self, references=True, ignorespec=None, **options) -> APISpec: spec = APISpec(title=self.info.get("title"), version=self.info.get("version"), openapi_version=self.openapi, plugins=[MultiOperationBuilderPlugin(references)], **options) for requestitem in self.postman_collection.get_requestitems(): spec.path( path=requestitem.get_request().path, operations=Operation(requestitem, ignorespec=ignorespec).get(), ) return spec
def create_spec(registry, zone=None, merge=None): title = registry.settings.get("openapi.title", "Untitled") version = registry.settings.get("openapi.version", "0.0.0") name_resolver = DottedNameResolver() MarshmallowPlugin = name_resolver.maybe_resolve( registry.settings.get("openapi.plugin", "apispec.ext.marshmallow.MarshmallowPlugin")) marshmallow_plugin = MarshmallowPlugin( schema_name_resolver=schema_name_resolver) spec = APISpec( title=title, version=version, openapi_version="3.0.2", plugins=[marshmallow_plugin], ) for path, operations in list_paths(registry.introspector): final_ops = dict() for method, view in operations.items(): if zone is not None and zone != view.get("api_zone"): continue summary, descr, user_op = split_docstring(view["callable"].__doc__) op = { "responses": dict(), "parameters": [], } if summary: op["summary"] = summary if descr: op["description"] = descr set_url_params(spec, op, view) if "validate" in view: if method == "get": set_query_params(spec, op, view) else: set_request_body(spec, op, view) if "marshal" in view: set_response_body(spec, op, view) set_tag(spec, op, view) final_op = utils.deepupdate(op, user_op) final_op = utils.deepupdate(final_op, view.get("api_spec", dict())) # We are required to have some response, so make one up. if not final_op["responses"]: final_op["responses"]["200"] = { "description": "", } final_ops[method] = final_op spec.path(path, operations=final_ops) json = spec.to_dict() return _perform_merges(json, merge, registry.settings.get("openapi.merge"))
def test_plugin_path_helper_is_used(self, openapi_version, return_none): spec = APISpec( title="Swagger Petstore", version="1.0.0", openapi_version=openapi_version, plugins=(self.test_plugin_factory(return_none),), ) spec.path("/path_1") paths = get_paths(spec) assert len(paths) == 1 if return_none: assert paths["/path_1"] == {} else: assert paths["/path_1_modified"] == {"get": {"responses": {"200": {}}}}
def test_plugin_path_helper_is_used(self, openapi_version, return_none): spec = APISpec( title='Swagger Petstore', version='1.0.0', openapi_version=openapi_version, plugins=(self.test_plugin_factory(return_none), ), ) spec.path('/path_1') paths = get_paths(spec) assert len(paths) == 1 if return_none: assert paths['/path_1'] == {} else: assert paths['/path_1_modified'] == {'get': {'responses': {'200': {}}}}
def test_schema_uses_ref_if_available_name_resolver_returns_none_v2(self): def resolver(schema): return None spec = APISpec( title="Test auto-reference", version="0.1", openapi_version="2.0", plugins=(MarshmallowPlugin(schema_name_resolver=resolver),), ) spec.components.schema("Pet", schema=PetSchema) spec.path( path="/pet", operations={"get": {"responses": {200: {"schema": PetSchema}}}} ) get = get_paths(spec)["/pet"]["get"] assert get["responses"][200]["schema"]["$ref"] == ref_path(spec) + "Pet"