10
10
11
11
from .. import fields
12
12
from ..json .schemas import schema_to_json
13
- from ..schema import EventSchema , build_action_schema
13
+ from ..schema import EventSchema , ActionSchema , Schema , build_action_schema
14
14
from ..utilities import get_docstring , get_summary , merge
15
+ from .utilities import ensure_schema , get_marshamallow_plugin
15
16
from ..views import ActionView , EventView , PropertyView , View
16
17
17
18
@@ -54,6 +55,10 @@ class MarshmallowPlugin(_MarshmallowPlugin):
54
55
class FlaskLabThingsPlugin (BasePlugin ):
55
56
"""APIspec plugin for Flask LabThings"""
56
57
58
+ def init_spec (self , spec ):
59
+ self .spec = spec
60
+ return super ().init_spec (spec )
61
+
57
62
@classmethod
58
63
def spec_for_interaction (cls , interaction ):
59
64
d = {}
@@ -88,11 +93,13 @@ def spec_for_interaction(cls, interaction):
88
93
}
89
94
},
90
95
}
96
+ if hasattr (prop , "responses" ):
97
+ d [method ]["responses" ].update (prop .responses )
91
98
return d
92
99
93
100
@classmethod
94
101
def spec_for_property (cls , prop ):
95
- class_json_schema = schema_to_json (prop .schema ) if prop . schema else None
102
+ class_schema = ensure_schema (prop .schema ) or {}
96
103
97
104
d = cls .spec_for_interaction (prop )
98
105
@@ -104,21 +111,13 @@ def spec_for_property(cls, prop):
104
111
{
105
112
"requestBody" : {
106
113
"content" : {
107
- prop .content_type : (
108
- {"schema" : class_json_schema }
109
- if class_json_schema
110
- else {}
111
- )
114
+ prop .content_type : { "schema" : class_schema }
112
115
}
113
116
},
114
117
"responses" : {
115
118
200 : {
116
119
"content" : {
117
- prop .content_type : (
118
- {"schema" : class_json_schema }
119
- if class_json_schema
120
- else {}
121
- )
120
+ prop .content_type : { "schema" : class_schema }
122
121
},
123
122
"description" : "Write property" ,
124
123
}
@@ -134,11 +133,7 @@ def spec_for_property(cls, prop):
134
133
"responses" : {
135
134
200 : {
136
135
"content" : {
137
- prop .content_type : (
138
- {"schema" : class_json_schema }
139
- if class_json_schema
140
- else {}
141
- )
136
+ prop .content_type : { "schema" : class_schema }
142
137
},
143
138
"description" : "Read property" ,
144
139
}
@@ -152,17 +147,30 @@ def spec_for_property(cls, prop):
152
147
153
148
return d
154
149
155
- @classmethod
156
- def spec_for_action (cls , action ):
157
- class_args = schema_to_json (action .args )
158
- action_json_schema = schema_to_json (
159
- build_action_schema (action .schema , action .args )()
160
- )
161
- queue_json_schema = schema_to_json (
162
- build_action_schema (action .schema , action .args )(many = True )
163
- )
150
+ def spec_for_action (self , action ):
151
+ action_input = ensure_schema (action .args )
152
+ action_output = ensure_schema (action .schema )
153
+ # We combine input/output parameters with ActionSchema using an
154
+ # allOf directive, so we don't end up duplicating the schema
155
+ # for every action.
156
+ if action_output or action_input :
157
+ # It would be neater to combine the schemas in OpenAPI with allOf
158
+ # but this seems to break everything and I don't know why!!
159
+ plugin = get_marshamallow_plugin (self .spec )
160
+ action_io_schema = build_action_schema (action_output , action_input , base_class = Schema )
161
+ action_schema = {
162
+ "allOf" : [
163
+ plugin .resolver .resolve_schema_dict (ActionSchema ),
164
+ plugin .resolver .resolve_schema_dict (action_io_schema ),
165
+ ]
166
+ }
167
+ # The line below builds an ActionSchema subclass. This works and
168
+ # is valid, but results in ActionSchema being duplicated many times...
169
+ #action_schema = build_action_schema(action_output, action_input)
170
+ else :
171
+ action_schema = ActionSchema
164
172
165
- d = cls .spec_for_interaction (action )
173
+ d = self .spec_for_interaction (action )
166
174
167
175
# Add in Action spec
168
176
d = merge (
@@ -172,7 +180,7 @@ def spec_for_action(cls, action):
172
180
"requestBody" : {
173
181
"content" : {
174
182
action .content_type : (
175
- {"schema" : class_args } if class_args else {}
183
+ {"schema" : action_input } if action_input else {}
176
184
)
177
185
}
178
186
},
@@ -181,24 +189,17 @@ def spec_for_action(cls, action):
181
189
# 200 responses with cls.responses = {200: {...}}
182
190
200 : {
183
191
"description" : "Action completed immediately" ,
184
- # Allow customising 200 (immediate response) content type
192
+ # Allow customising 200 (immediate response) content type?
193
+ # TODO: I'm not convinced it's still possible to customise this.
185
194
"content" : {
186
- action .response_content_type : (
187
- {"schema" : action_json_schema }
188
- if action_json_schema
189
- else {}
190
- )
195
+ "application/json" : { "schema" : action_schema }
191
196
},
192
197
},
193
198
201 : {
194
199
"description" : "Action started" ,
195
200
# Our POST 201 MUST be application/json
196
201
"content" : {
197
- "application/json" : (
198
- {"schema" : action_json_schema }
199
- if action_json_schema
200
- else {}
201
- )
202
+ "application/json" : { "schema" : action_schema }
202
203
},
203
204
},
204
205
},
@@ -210,9 +211,12 @@ def spec_for_action(cls, action):
210
211
"description" : "Action queue" ,
211
212
"content" : {
212
213
"application/json" : (
213
- {"schema" : queue_json_schema }
214
- if queue_json_schema
215
- else {}
214
+ {
215
+ "schema" : {
216
+ "type" : "array" ,
217
+ "items" : action_schema ,
218
+ }
219
+ }
216
220
)
217
221
},
218
222
}
0 commit comments