Commit 6d7921e8 authored by jtc42's avatar jtc42
Browse files

First draft of auto-Swagger

parent 99d0d005
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -168,4 +168,8 @@ def cleanup():
atexit.register(cleanup)

if __name__ == "__main__":
    app.run(host="0.0.0.0", port="5000", threaded=True, debug=True, use_reloader=False)
#    app.run(host="0.0.0.0", port="5000", threaded=True, debug=True, use_reloader=False)
    #from pprint import pprint
    #pprint(labthing.spec.to_dict())
    with open('spec.yaml', 'w') as f:
        f.write(labthing.spec.to_yaml())
 No newline at end of file
+2 −2
Original line number Diff line number Diff line
from openflexure_microscope.api.utilities import get_bool, JsonResponse
from openflexure_microscope.common.flask_labthings.resource import Resource
from openflexure_microscope.common.flask_labthings.find import find_device
from openflexure_microscope.common.flask_labthings.decorators import use_args, marshal_with
from openflexure_microscope.common.flask_labthings.decorators import use_args, marshal_with, doc
from openflexure_microscope.common.flask_labthings import fields
from openflexure_microscope.utilities import filter_dict

@@ -10,7 +10,7 @@ from openflexure_microscope.api.v2.views.captures import capture_schema
import logging
from flask import jsonify, request, abort, url_for, redirect, send_file


@doc(tags=["actions"])
class CaptureAPI(Resource):
    """
    Create a new image capture. 
+23 −9
Original line number Diff line number Diff line
@@ -32,6 +32,10 @@ class marshal_with(object):
        self.schema = schema

    def __call__(self, f):
        # Pass params to call function attribute for external access
        f.__apispec__ = f.__dict__.get('__apispec__', {})
        f.__apispec__["_schema"] = self.schema
        # Wrapper function
        @wraps(f)
        def wrapper(*args, **kwargs):
            resp = f(*args, **kwargs)
@@ -46,27 +50,37 @@ class marshal_with(object):


class use_args(object):
    def __init__(self, argmap, **kwargs):
    def __init__(self, schema, **kwargs):
        """
        Equivalent to webargs.flask_parser.use_args
        """
        self.argmap = argmap
        self.wrapper = flaskparser.use_args(argmap, **kwargs)
        self.schema = schema
        self.wrapper = flaskparser.use_args(schema, **kwargs)

    def __call__(self, f):
        # Pass params to call function attribute for external access
        f.__apispec__ = f.__dict__.get('__apispec__', {})
        f.__apispec__["_params"] = self.schema
        # Wrapper function
        update_wrapper(self.wrapper, f)
        return self.wrapper(f)


class use_kwargs(object):
    def __init__(self, argmap, **kwargs):
class use_kwargs(use_args):
    def __init__(self, schema, **kwargs):
        """
        Equivalent to webargs.flask_parser.use_kwargs
        """
        kwargs["as_kwargs"] = True
        self.argmap = argmap
        self.wrapper = flaskparser.use_args(argmap, **kwargs)
        use_args.__init__(self, schema, **kwargs)


class doc(object):
    def __init__(self, **kwargs):
        self.kwargs = kwargs

    def __call__(self, f):
        update_wrapper(self.wrapper, f)
        return self.wrapper(f)
        # Pass params to call function attribute for external access
        f.__apispec__ = f.__dict__.get('__apispec__', {})
        f.__apispec__.update(self.kwargs)
        return f
 No newline at end of file
+56 −7
Original line number Diff line number Diff line
from flask import url_for, jsonify
from apispec import APISpec
from apispec.ext.marshmallow import MarshmallowPlugin

from .plugins import BasePlugin
from .views.plugins import PluginListResource
from .views.tasks import TaskList, TaskResource

from .spec import view2path

from openflexure_microscope.common.labthings_core.utilities import get_docstring
from .exceptions import JSONExceptionHandler

from . import EXTENSION_NAME

import logging

class LabThing(object):
    def __init__(
@@ -17,6 +22,7 @@ class LabThing(object):
        prefix: str = "",
        title: str = "",
        description: str = "",
        version: str = "0.0.0",
        handle_errors: bool = True,
    ):
        self.app = app
@@ -32,17 +38,53 @@ class LabThing(object):
        self.endpoints = set()

        self.url_prefix = prefix
        self.description = description
        self.title = title
        self._description = description
        self._title = title
        self._version = version

        if handle_errors:
            self.error_handler = JSONExceptionHandler()
        else:
            self.error_handler = None

        self.spec = APISpec(
            title=self.title,
            version=self.version,
            openapi_version="3.0.2",
            plugins=[MarshmallowPlugin()],
        )

        if app is not None:
            self.init_app(app)

    @property
    def description(self, ):
        return self._description
    
    @description.setter
    def description(self, description: str):
        self._description = description
        self.spec.description = description
    
    @property
    def title(self, ):
        return self._title
    
    @title.setter
    def title(self, title: str):
        self._title = title
        self.spec.title = title
    
    @property
    def version(self, ):
        return str(self._version)
    
    @version.setter
    def version(self, version: str):
        self._version = version
        self.spec.version = version
    

    ### Flask stuff

    def init_app(self, app):
@@ -56,14 +98,14 @@ class LabThing(object):
        if self.error_handler:
            self.error_handler.init_app(self.app)

        # Create base routes
        self._create_base_routes()

        # Add resources, if registered before tying to a Flask app
        if len(self.resources) > 0:
            for resource, urls, endpoint, kwargs in self.resources:
                self._register_view(app, resource, *urls, endpoint=endpoint, **kwargs)

        # Create base routes
        self._create_base_routes()

    def teardown(self, exception):
        pass

@@ -158,9 +200,12 @@ class LabThing(object):
            api.add_resource(FooSpecial, '/special/foo', endpoint="foo")
        """
        endpoint = endpoint or resource.__name__.lower()

        logging.debug(f"{endpoint}: {type(resource)}")

        if self.app is not None:
            self._register_view(self.app, resource, *urls, endpoint=endpoint, **kwargs)
        else:
        
        self.resources.append((resource, urls, endpoint, kwargs))

    def resource(self, *urls, **kwargs):
@@ -208,6 +253,10 @@ class LabThing(object):
            rule = self._complete_url(url, "")
            # Add the url to the application or blueprint
            app.add_url_rule(rule, view_func=resource_func, **kwargs)
            # Add the resource to our API spec
            self.spec.path(
                **view2path(rule, resource, self.spec)
            )

    ### Utilities

+13 −0
Original line number Diff line number Diff line
@@ -5,6 +5,19 @@ class Resource(MethodView):
    """Currently identical to MethodView
    """
    endpoint = None
    methods = ["get", "post", "put", "delete"]

    def __init__(self, *args, **kwargs):
        MethodView.__init__(self, *args, **kwargs)

    def doc(self):
        docs = {"operations": {}}
        if hasattr(self, "__apispec__"):
            docs.update(self.__apispec__)


        for meth in Resource.methods:
            if hasattr(self, meth) and hasattr(getattr(self, meth), "__apispec__"):
                docs["operations"][meth] = {}
                docs["operations"][meth] = getattr(self, meth).__apispec__
        return docs
Loading