Skip to content

Commit aa35bb9

Browse files
committed
Enable single-field input and output schemas
1 parent 8ac445d commit aa35bb9

File tree

1 file changed

+59
-16
lines changed

1 file changed

+59
-16
lines changed

labthings/server/decorators.py

Lines changed: 59 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
from webargs import flaskparser
22
from functools import wraps, update_wrapper
3-
from flask import make_response
3+
from flask import make_response, jsonify, abort, request
44
from http import HTTPStatus
5+
from marshmallow.exceptions import ValidationError
56

67
from ..core.utilities import rupdate
78

89
from .spec import update_spec
9-
from .schema import TaskSchema
10+
from .schema import TaskSchema, Schema
11+
from .fields import Field
12+
13+
import logging
1014

1115

1216
def unpack(value):
@@ -38,6 +42,11 @@ def __init__(self, schema, code=200):
3842
self.schema = schema
3943
self.code = code
4044

45+
if isinstance(self.schema, Schema):
46+
self.converter = self.schema.jsonify
47+
elif isinstance(self.schema, Field):
48+
self.converter = lambda x: jsonify(self.schema._serialize(x, None, None))
49+
4150
def __call__(self, f):
4251
# Pass params to call function attribute for external access
4352
update_spec(f, {"_schema": {self.code: self.schema}})
@@ -47,9 +56,9 @@ def wrapper(*args, **kwargs):
4756
resp = f(*args, **kwargs)
4857
if isinstance(resp, tuple):
4958
data, code, headers = unpack(resp)
50-
return make_response(self.schema.jsonify(data), code, headers)
59+
return make_response(self.converter(data), code, headers)
5160
else:
52-
return make_response(self.schema.jsonify(resp))
61+
return make_response(self.converter(resp))
5362

5463
return wrapper
5564

@@ -91,11 +100,54 @@ def ThingProperty(viewcls):
91100
thing_property = ThingProperty
92101

93102

103+
class use_body(object):
104+
"""
105+
Gets the request body as a single value and adds it as a positional argument
106+
"""
107+
108+
def __init__(self, schema, **kwargs):
109+
self.schema = schema
110+
111+
def __call__(self, f):
112+
# Pass params to call function attribute for external access
113+
update_spec(f, {"_params": self.schema})
114+
115+
# Wrapper function
116+
@wraps(f)
117+
def wrapper(*args, **kwargs):
118+
# Get data from request
119+
data = request.data or None
120+
121+
# If no data is there
122+
if not data:
123+
# If data is required
124+
if self.schema.required == True:
125+
# Abort
126+
return abort(400)
127+
# Otherwise, look for the schema fields 'missing' property
128+
if self.schema.missing:
129+
data = self.schema.missing
130+
131+
# Serialize data if it exists
132+
if data:
133+
try:
134+
data = self.schema._deserialize(data, None, None)
135+
except ValidationError as e:
136+
logging.error(e)
137+
return abort(400)
138+
139+
# Inject argument and return wrapped function
140+
return f(*args, data, **kwargs)
141+
142+
return wrapper
143+
144+
94145
class use_args(object):
146+
"""
147+
Equivalent to webargs.flask_parser.use_args
148+
"""
149+
95150
def __init__(self, schema, **kwargs):
96-
"""
97-
Equivalent to webargs.flask_parser.use_args
98-
"""
99151
self.schema = schema
100152
self.wrapper = flaskparser.use_args(schema, **kwargs)
101153

@@ -107,15 +159,6 @@ def __call__(self, f):
107159
return self.wrapper(f)
108160

109161

110-
class use_kwargs(use_args):
111-
def __init__(self, schema, **kwargs):
112-
"""
113-
Equivalent to webargs.flask_parser.use_kwargs
114-
"""
115-
kwargs["as_kwargs"] = True
116-
use_args.__init__(self, schema, **kwargs)
117-
118-
119162
class Doc(object):
120163
def __init__(self, **kwargs):
121164
self.kwargs = kwargs

0 commit comments

Comments
 (0)