def get_operations(view, operations, autodoc=True): if operations is not None: return operations operations = {} # views can be class based if view.get("attr"): global_meta = load_operations_from_docstring(view["callable"].__doc__) if global_meta: operations.update(global_meta) f_view = getattr(view["callable"], view["attr"]) # or just function callables else: f_view = view.get("callable") methods = view.get("request_methods") view_operations = load_operations_from_docstring(f_view.__doc__) if not view_operations: view_operations = {} if is_string(methods): methods = [methods] if not methods: methods = ALL_METHODS[:] operation = load_yaml_from_docstring(f_view.__doc__) if operation: for method in methods: view_operations[method.lower()] = operation elif autodoc: for method in methods: view_operations.setdefault(method.lower(), {"responses": {}}) operations.update(view_operations) return operations
def path_from_router(spec, view, operations, **kwargs): """Path helper that allows passing a bottle view function.""" operations = utils.load_operations_from_docstring(view.__doc__) app = kwargs.get('app', _default_app) route = _route_for_view(app, view) bottle_path = bottle_path_to_swagger(route.rule) return Path(path=bottle_path, operations=operations)
def path_from_view(spec, view, operations, **kwargs): """Path helper that allows passing a Flask view function.""" rule = _rule_for_view(view) path = flaskpath2swagger(rule.rule) operations = utils.load_operations_from_docstring(view.__doc__) path = Path(path=path, operations=operations) return path
def update_specs(cls, specs): if cls.Schema: specs.definition(cls.meta.name, schema=cls.Schema) operations = utils.load_operations_from_docstring(cls.__doc__) specs.add_path(RE_URL.sub(r'{\1}', cls.meta.url), operations=cls.update_operations_specs( operations, ('GET', 'POST'), )) if cls.meta.url_detail: ops = cls.update_operations_specs( operations, ('GET', 'PUT', 'PATCH', 'DELETE'), parameters=[{ 'name': cls.meta.name, 'in': 'path', 'description': 'Resource Identifier', 'type': 'string', 'required': True, }] ) specs.add_path(RE_URL.sub(r'{\1}', cls.meta.url_detail), operations=ops) for endpoint, (url_, name_, params_) in cls.meta.endpoints.values(): specs.add_path( RE_URL.sub(r'{\1}', "%s%s" % (cls.meta.url.rstrip('/'), url_)), operations=cls.update_operations_specs( operations, params_.get('methods', ('GET',)), method=getattr(cls, name_, None) ))
def update_specs(cls, specs): if cls.Schema: specs.definition(cls.meta.name, schema=cls.Schema) operations = utils.load_operations_from_docstring(cls.__doc__) specs.add_path(RE_URL.sub(r'{\1}', cls.meta.url), operations=cls.update_operations_specs( operations, ('GET', 'POST'), )) if cls.meta.url_detail: ops = cls.update_operations_specs( operations, ('GET', 'PUT', 'PATCH', 'DELETE'), parameters=[{ 'name': cls.meta.name, 'in': 'path', 'description': 'Resource Identifier', 'type': 'string', 'required': True, }]) specs.add_path(RE_URL.sub(r'{\1}', cls.meta.url_detail), operations=ops) for endpoint, (url_, name_, params_) in cls.meta.endpoints.values(): specs.add_path(RE_URL.sub( r'{\1}', "%s%s" % (cls.meta.url.rstrip('/'), url_)), operations=cls.update_operations_specs( operations, params_.get('methods', ('GET', )), method=getattr(cls, name_, None)))
def path_from_view(spec, view, rule, **kwargs): """Path helper that allows passing a Werkzeug view function.""" path = werkzeugpath2swagger(rule.rule) path = urllib.parse.urljoin('/', path.lstrip('/')) operations = utils.load_operations_from_docstring(view.__doc__) path = Path(path=path, operations=operations) return path
def path_from_view(spec, app, view, **kwargs): """Path helper that allows passing a Chalice view function.""" kwarg_ops = kwargs.get('operations') kwarg_ops = set() if not kwarg_ops else set(kwarg_ops) uri, methods = _route_for_view(app, view, path=kwargs.get('path', Path()), operations=kwarg_ops) operations = load_operations_from_docstring(view.__doc__) if not operations: operations = {} # check that the operations in the docstring match those of the actual route decorator path = Path(path=uri, operations={ method: op for method, op in iteritems(operations) if method in methods }) # add methods from route decorator that were not in docstring for op in methods: path.operations.setdefault(op, {}) return path
def path_from_view(spec, view, **kwargs): """Path helper that allows passing a Flask view function.""" rule = _rule_for_view(view) path = flaskpath2swagger(rule.rule) app_root = current_app.config['APPLICATION_ROOT'] or '/' path = urljoin(app_root.rstrip('/') + '/', path.lstrip('/')) operations = utils.load_operations_from_docstring(view.__doc__) path = Path(path=path, operations=operations) return path
def path_helper(self, operations, view, **kwargs): """Path helper that allows passing a Sanic view function.""" operations_from_docs = load_operations_from_docstring(view.__doc__) # Operation is specified in OpenAPI specifications # Refer: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#operation-object if operations_from_docs: operations.update(operations_from_docs) for method in view.view_class.methods: method_name = method.lower() method = getattr(view.view_class, method_name) method_operation = load_yaml_from_docstring(method.__doc__) if method_operation: operations[method_name] = load_yaml_from_docstring( method.__doc__)
def schema_path_helper(spec, view, **kwargs): """Path helper that allows passing a Schema as a response. Responses can be defined in a view's docstring. """ operations = (load_operations_from_docstring(view.__doc__) or kwargs.get('operations')) if not operations: return operations = operations.copy() for operation in operations.values(): for response in operation.get('responses', {}).values(): if 'schema' in response: response['schema'] = resolve_schema_dict( spec, response['schema']) return Path(operations=operations)
def path_from_view(spec, view, **kwargs): """Path helper that allows passing a Flask view function.""" rule = _rule_for_view(view) path = flaskpath2swagger(rule.rule) app_root = current_app.config['APPLICATION_ROOT'] or '/' path = urljoin(app_root.rstrip('/') + '/', path.lstrip('/')) operations = utils.load_operations_from_docstring(view.__doc__) path = Path(path=path, operations=operations) if hasattr(view, 'view_class') and issubclass(view.view_class, MethodView): operations = {} for method in view.methods: method_name = method.lower() method = getattr(view.view_class, method_name) docstring_yaml = utils.load_yaml_from_docstring(method.__doc__) operations[method_name] = docstring_yaml or dict() path.operations.update(operations) return path
def path_helper(self, operations, view, **kwargs): """Path helper that allows passing a Flask view function.""" rule = self._rule_for_view(view) operations.update( utils.load_operations_from_docstring(view.__doc__) or {}) if hasattr(view, 'view_class') and issubclass(view.view_class, MethodView): for method in view.methods: if method in rule.methods: method_name = method.lower() method = getattr(view.view_class, method_name) docstring_yaml = utils.load_yaml_from_docstring( method.__doc__) operations[method_name] = docstring_yaml or dict() path = self.flaskpath2openapi(rule.rule) app_root = current_app.config['APPLICATION_ROOT'] or '/' return urljoin(app_root.rstrip('/') + '/', path.lstrip('/'))
def path_helper(self, operations, resource, **kwargs): """Path helper that allows passing a Falcon resource instance.""" resource_uri_mapping = self._generate_resource_uri_mapping(self._app) if resource not in resource_uri_mapping: raise APISpecError("Could not find endpoint for resource {0}".format(resource)) operations.update(utils.load_operations_from_docstring(resource.__doc__) or {}) path = resource_uri_mapping[resource] for method in falcon.constants.HTTP_METHODS: http_verb = method.lower() method_name = "on_" + http_verb if hasattr(resource, method_name): method = getattr(resource, method_name) docstring_yaml = utils.load_yaml_from_docstring(method.__doc__) operations[http_verb] = docstring_yaml or dict() return path
def schema_path_helper(spec, view=None, **kwargs): """Path helper that allows passing a Schema as a response. Responses can be defined in a view's docstring. :: from pprint import pprint from my_app import Users, UserSchema class UserHandler: def get(self, user_id): '''Get a user endpoint. --- description: Get a user responses: 200: description: A user schema: UserSchema ''' user = Users.get(id=user_id) schema = UserSchema() return schema.dumps(user) urlspec = (r'/users/{user_id}', UserHandler) spec.add_path(urlspec=urlspec) pprint(spec.to_dict()['paths']) # {'/users/{user_id}': {'get': {'description': 'Get a user', # 'responses': {200: {'description': 'A user', # 'schema': {'$ref': '#/definitions/User'}}}}}} :: from pprint import pprint from my_app import Users, UserSchema class UsersHandler: def get(self): '''Get users endpoint. --- description: Get a list of users responses: 200: description: A list of user schema: type: array items: UserSchema ''' users = Users.all() schema = UserSchema(many=True) return schema.dumps(users) urlspec = (r'/users', UsersHandler) spec.add_path(urlspec=urlspec) pprint(spec.to_dict()['paths']) # {'/users': {'get': {'description': 'Get a list of users', # 'responses': {200: {'description': 'A list of users', # 'schema': {'type': 'array', # 'items': {'$ref': '#/definitions/User'}}}}}}} """ operations = ( kwargs.get('operations') or (view and load_operations_from_docstring(view.__doc__)) ) if not operations: return None operations = operations.copy() return Path(operations=operations)
def resource_path_helper(spec, path, resource): """Provides path when given a FlaskRestful resource""" operations = utils.load_operations_from_docstring(resource.__doc__) return Path(path=path, operations=operations)
def docstring_path_helper(spec, path, router, func, **kwargs): operations = load_operations_from_docstring(func.__doc__) cp_config = func._cp_config if operations is not None: for method, data in operations.items(): if cp_config.get('tools.authentication.on', True): data['security'] = [{'Bearer': []}] if 'tools.model_in.cls' in cp_config: model_cls = cp_config['tools.model_in.cls'] spec.definition(model_cls.__name__, **parse_model(spec, model_cls)) data['requestBody']['required'] = True data['requestBody']['content'] = { 'application/json': { 'schema': { '$ref': '#/components/schemas/' + model_cls.__name__ } } } if 'tools.model_params.cls' in cp_config: model_cls = cp_config['tools.model_params.cls'] data['parameters'] = data.get('parameters', []) # In query vs in path for key, obj in model_cls.__dict__.items(): inn = 'query' if '{' + key + '}' in path.path: inn = 'path' if isinstance(obj, FieldDescriptor): data['parameters'].append({ 'name': key, 'in': inn, 'required': model_cls._fields[key].required, 'schema': parse_model_type(spec, model_cls._fields[key]) }) if 'tools.model_out.cls' in cp_config: model_cls = cp_config['tools.model_out.cls'] spec.definition(model_cls.__name__, **parse_model(spec, model_cls)) data['responses'][200]['content'] = { 'application/json': { 'schema': { '$ref': '#/components/schemas/' + model_cls.__name__ } } } if 'tools.model_out_pagination.cls' in cp_config: model_cls = cp_config['tools.model_out_pagination.cls'] spec.definition(model_cls.__name__, **parse_model(spec, model_cls)) data['responses'][200]['content'] = { 'application/json': { 'schema': { 'type': 'array', 'items': { '$ref': '#/components/schemas/' + model_cls.__name__ } } } } if isinstance(router, SandwichProjectRouter): data['parameters'] = data.get('parameters', []) data['parameters'].append({ 'name': 'project_name', 'in': 'path', 'required': True, 'schema': { 'type': 'string' } }) if 'tools.enforce_permission.permission_name' in cp_config: data['x-required-permission'] = cp_config[ 'tools.enforce_permission.permission_name'] return Path(path=path.path, operations=operations)
from apispec import APISpec, utils from apispec.ext.flask import flaskpath2swagger from simple_flask_project.app import app from simple_flask_project.schemas import GLOBAL_SCHEMA_MAP spec = APISpec(title='Users API', version='1.0.0', plugins=('apispec.ext.flask', ), openapi_version='3.0.0', security=[], components={}) for r in app.url_map.iter_rules(): view = app.view_functions.get(r.endpoint) operations = utils.load_operations_from_docstring(view.__doc__) path = flaskpath2swagger(r.rule) if not operations: continue for verb in operations: resp = operations.get(verb).get('responses') for status in resp: val = resp.get(status) content = resp.get(status).get('schema') if content: # Check if mapping is present in global schema map if GLOBAL_SCHEMA_MAP.get(content): val.update({ 'content': {
def add_pyramid_paths(spec, route_name, request=None, request_method=None, operations=None, **kwargs): """ Adds a route and view info to spec :param spec: ApiSpec object :param route_name: Route name to inspect :param request: Request object, if `None` then `get_current_request()` will be used :param request_method: Request method predicate :param operations: Operations dict that will be used instead of introspection :param kwargs: Additional kwargs for predicate matching :return: """ from pyramid.threadlocal import get_current_request if request is None: request = get_current_request() registry = request.registry introspector = registry.introspector route = introspector.get("routes", route_name) views = introspector.related(route) # needs to be rewritten to internal name if request_method: kwargs["request_methods"] = request_method # kwargs.setdefault('route_name', route_name) for view in views: matches = True for kw in kwargs.keys(): # request_methods can be either a list of strings or a string # so lets normalize via sets if kw == "request_methods": if is_string(kwargs[kw]): kwargs[kw] = [kwargs[kw]] methods = view.get(kw) or ALL_METHODS if is_string(methods): methods = [methods] if not set(kwargs[kw] or []).intersection(methods): matches = False else: if not view.get(kw) == kwargs[kw]: matches = False if not matches: continue final_operations = {} # views can be class based if view.get("attr"): global_meta = load_operations_from_docstring( view["callable"].__doc__) if global_meta: final_operations.update(global_meta) f_view = getattr(view["callable"], view["attr"]) # or just function callables else: f_view = view.get("callable") if operations is None: methods = view.get("request_methods") view_operations = load_operations_from_docstring(f_view.__doc__) if not view_operations: view_operations = {} if is_string(methods): methods = [methods] if not methods: methods = ALL_METHODS[:] operation = load_yaml_from_docstring(f_view.__doc__) if operation: for method in methods: view_operations[method.lower()] = operation final_operations.update(view_operations) else: final_operations = operations spec.add_path(route["pattern"], operations=final_operations)
def path_helper(self, operations, view, **kwargs): """Path helper that allows passing a bottle view function.""" operations.update(utils.load_operations_from_docstring(view.__doc__) or {}) app = kwargs.get('app', _default_app) route = self._route_for_view(app, view) return self.bottle_path_to_openapi(route.rule)