def generate_swagger() -> str: # Create an APISpec spec = APISpec( title="ARCOR2 Data Models", version=arcor2.api_version(), openapi_version="3.0.2", plugins=[FlaskPlugin(), DataclassesPlugin()], ) # TODO avoid explicit naming of all sub-modules in rpc module for module in (arcor2.data.common, arcor2.data.object_type, rpc.common, rpc.execution, rpc.objects, rpc.robot, rpc.scene, rpc.project, rpc.services, rpc.storage, arcor2.data.events): for name, obj in inspect.getmembers(module): if not inspect.isclass(obj) or not issubclass( obj, JsonSchemaMixin) or obj == JsonSchemaMixin: continue try: spec.components.schema(obj.__name__, schema=obj) except DuplicateComponentNameError: continue return spec.to_yaml()
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 generate_openapi(service_name: str, version: str, rpcs: List[Type[RPC]], events: List[Type[Event]]) -> str: """Generate OpenAPI models from RPCs and events. Be aware: it modifies __name__ attribute! """ # Create an APISpec spec = APISpec( title=f"{service_name} Data Models", version=version, openapi_version="3.0.2", plugins=[FlaskPlugin(), DataclassesPlugin()], ) for obj in events: if obj is Event: continue _rename_childs(obj) for rpc in rpcs: if rpc is RPC: continue for cls in (rpc.Request, rpc.Response): cls.__name__ = rpc.__name__ + cls.__name__ _rename_childs(cls) for obj in events: _add_to_spec(spec, obj) for rpc in rpcs: for cls in (rpc.Request, rpc.Response): _add_to_spec(spec, cls) return spec.to_yaml()
import json from apispec import APISpec from apispec_webframeworks.flask import FlaskPlugin from flask import Flask import pytest from dataclasses_jsonschema.apispec import DataclassesPlugin from dataclasses_jsonschema import JsonSchemaMixin # Create an APISpec spec = APISpec( title="Swagger Petstore", version="1.0.0", openapi_version="3.0.2", plugins=[FlaskPlugin(), DataclassesPlugin()], ) # Optional Flask support app = Flask(__name__) @app.route("/random") def random_pet(): """A cute furry animal endpoint. --- get: description: Get a random pet responses: 200: content:
import json from typing import Optional, List from dataclasses import dataclass from apispec import APISpec from dataclasses_jsonschema.apispec import DataclassesPlugin from dataclasses_jsonschema import JsonSchemaMixin # Create an APISpec spec = APISpec( title="Swagger Petstore", version="1.0.0", openapi_version="3.0.2", plugins=[DataclassesPlugin()], ) @dataclass class Category(JsonSchemaMixin): """Pet category""" name: str id: Optional[int] @dataclass class Pet(JsonSchemaMixin): """A pet""" categories: List[Category] name: str