Moved all API stuff to flask_labthings
This commit is contained in:
parent
6bf178750b
commit
851d70842c
12 changed files with 96 additions and 109 deletions
|
|
@ -1,26 +0,0 @@
|
|||
from .spec import update_spec
|
||||
from .utilities import rupdate
|
||||
|
||||
|
||||
class doc(object):
|
||||
def __init__(self, **kwargs):
|
||||
self.kwargs = kwargs
|
||||
|
||||
def __call__(self, f):
|
||||
# Pass params to call function attribute for external access
|
||||
update_spec(f, self.kwargs)
|
||||
return f
|
||||
|
||||
|
||||
class doc_response(object):
|
||||
def __init__(self, code, description, **kwargs):
|
||||
self.code = code
|
||||
self.description = description
|
||||
self.kwargs = kwargs
|
||||
|
||||
def __call__(self, f):
|
||||
# Pass params to call function attribute for external access
|
||||
f.__apispec__ = f.__dict__.get("__apispec__", {})
|
||||
d = {"responses": {self.code: {"description": self.description, **self.kwargs}}}
|
||||
rupdate(f.__apispec__, d)
|
||||
return f
|
||||
|
|
@ -1 +0,0 @@
|
|||
from marshmallow.fields import *
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
class BaseResource:
|
||||
"""
|
||||
A LabThing Resource class should, regardless of the underlying framework, make use of
|
||||
functions get(), put(), post(), and delete() corresponding to HTTP methods.
|
||||
|
||||
These functions will allow for automated documentation generation
|
||||
"""
|
||||
|
||||
methods = ["get", "post", "put", "delete"]
|
||||
|
||||
def doc(self):
|
||||
docs = {"operations": {}}
|
||||
if hasattr(self, "__apispec__"):
|
||||
docs.update(self.__apispec__)
|
||||
|
||||
for meth in BaseResource.methods:
|
||||
if hasattr(self, meth) and hasattr(getattr(self, meth), "__apispec__"):
|
||||
docs["operations"][meth] = {}
|
||||
docs["operations"][meth] = getattr(self, meth).__apispec__
|
||||
return docs
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import flask
|
||||
import marshmallow
|
||||
|
||||
MARSHMALLOW_VERSION_INFO = tuple(
|
||||
[int(part) for part in marshmallow.__version__.split(".") if part.isdigit()]
|
||||
)
|
||||
|
||||
sentinel = object()
|
||||
|
||||
|
||||
class Schema(marshmallow.Schema):
|
||||
"""Base serializer with which to define custom serializers.
|
||||
See `marshmallow.Schema` for more details about the `Schema` API.
|
||||
"""
|
||||
|
||||
def jsonify(self, obj, many=sentinel, *args, **kwargs):
|
||||
"""Return a JSON response containing the serialized data.
|
||||
:param obj: Object to serialize.
|
||||
:param bool many: Whether `obj` should be serialized as an instance
|
||||
or as a collection. If unset, defaults to the value of the
|
||||
`many` attribute on this Schema.
|
||||
:param kwargs: Additional keyword arguments passed to `flask.jsonify`.
|
||||
.. versionchanged:: 0.6.0
|
||||
Takes the same arguments as `marshmallow.Schema.dump`. Additional
|
||||
keyword arguments are passed to `flask.jsonify`.
|
||||
.. versionchanged:: 0.6.3
|
||||
The `many` argument for this method defaults to the value of
|
||||
the `many` attribute on the Schema. Previously, the `many`
|
||||
argument of this method defaulted to False, regardless of the
|
||||
value of `Schema.many`.
|
||||
"""
|
||||
if many is sentinel:
|
||||
many = self.many
|
||||
if MARSHMALLOW_VERSION_INFO[0] >= 3:
|
||||
data = self.dump(obj, many=many)
|
||||
else:
|
||||
data = self.dump(obj, many=many).data
|
||||
return flask.jsonify(data, *args, **kwargs)
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
from .resource import BaseResource
|
||||
from .utilities import rupdate
|
||||
from apispec import APISpec
|
||||
from apispec.ext.marshmallow import MarshmallowPlugin
|
||||
|
||||
from openflexure_microscope.common.labthings_core.utilities import (
|
||||
get_docstring,
|
||||
get_summary,
|
||||
)
|
||||
|
||||
from .fields import Field
|
||||
from marshmallow import Schema as BaseSchema
|
||||
|
||||
from collections import Mapping
|
||||
|
||||
|
||||
def update_spec(obj, spec):
|
||||
obj.__apispec__ = obj.__dict__.get("__apispec__", {})
|
||||
rupdate(obj.__apispec__, spec)
|
||||
return obj.__apispec__
|
||||
|
||||
|
||||
def view2path(rule: str, view: BaseResource, spec: APISpec):
|
||||
params = {
|
||||
"path": rule, # TODO: Validate this slightly (leading / etc)
|
||||
"operations": view2operations(view, spec),
|
||||
"description": get_docstring(view),
|
||||
"summary": get_summary(view),
|
||||
}
|
||||
|
||||
if hasattr(view, "__apispec__"):
|
||||
# Recursively update params
|
||||
rupdate(params, view.__apispec__)
|
||||
|
||||
return params
|
||||
|
||||
|
||||
def view2operations(view: BaseResource, spec: APISpec, populate_default: bool = True):
|
||||
ops = {}
|
||||
for method in BaseResource.methods:
|
||||
if hasattr(view, method):
|
||||
# Populate with default responses
|
||||
if populate_default:
|
||||
ops[method] = {
|
||||
"responses": {
|
||||
200: {
|
||||
"description": get_summary(getattr(view, method))
|
||||
or "Success"
|
||||
},
|
||||
404: {"description": "Resource not found"},
|
||||
}
|
||||
}
|
||||
else:
|
||||
ops[method] = {}
|
||||
|
||||
rupdate(
|
||||
ops[method],
|
||||
{
|
||||
"description": get_docstring(getattr(view, method)),
|
||||
"summary": get_summary(getattr(view, method)),
|
||||
},
|
||||
)
|
||||
|
||||
if hasattr(getattr(view, method), "__apispec__"):
|
||||
rupdate(
|
||||
ops[method], doc2operation(getattr(view, method).__apispec__, spec)
|
||||
)
|
||||
|
||||
return ops
|
||||
|
||||
|
||||
def doc2operation(apispec: dict, spec: APISpec):
|
||||
op = {}
|
||||
if "_params" in apispec:
|
||||
rupdate(
|
||||
op,
|
||||
{
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": convert_schema(apispec.get("_params"), spec)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
if "_schema" in apispec:
|
||||
rupdate(
|
||||
op,
|
||||
{
|
||||
"responses": {
|
||||
200: {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": convert_schema(apispec.get("_schema"), spec)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
for key, val in apispec.items():
|
||||
if not key in ["_params", "_schema"]:
|
||||
op[key] = val
|
||||
|
||||
return op
|
||||
|
||||
|
||||
def convert_schema(schema, spec: APISpec):
|
||||
if isinstance(schema, BaseSchema):
|
||||
return schema
|
||||
elif isinstance(schema, Mapping):
|
||||
return map2properties(schema, spec)
|
||||
else:
|
||||
raise TypeError(
|
||||
"Unsupported schema type. Ensure schema is a Schema class, or dictionary of Field objects"
|
||||
)
|
||||
|
||||
|
||||
def map2properties(schema, spec: APISpec):
|
||||
marshmallow_plugin = next(
|
||||
plugin for plugin in spec.plugins if isinstance(plugin, MarshmallowPlugin)
|
||||
)
|
||||
converter = marshmallow_plugin.converter
|
||||
|
||||
d = {}
|
||||
for k, v in schema.items():
|
||||
if isinstance(v, Field):
|
||||
d[k] = converter.field2property(v)
|
||||
elif isinstance(v, Mapping):
|
||||
d[k] = map2properties(v, spec)
|
||||
else:
|
||||
d[k] = v
|
||||
|
||||
return {"properties": d}
|
||||
Loading…
Add table
Add a link
Reference in a new issue