brainstem 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +147 -0
- data/Gemfile.lock +68 -39
- data/lib/brainstem/api_docs.rb +9 -4
- data/lib/brainstem/api_docs/atlas.rb +3 -3
- data/lib/brainstem/api_docs/controller.rb +12 -4
- data/lib/brainstem/api_docs/controller_collection.rb +11 -2
- data/lib/brainstem/api_docs/endpoint.rb +17 -7
- data/lib/brainstem/api_docs/endpoint_collection.rb +9 -1
- data/lib/brainstem/api_docs/formatters/open_api_specification/helper.rb +19 -16
- data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/endpoint/param_definitions_formatter.rb +52 -80
- data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/endpoint/response_definitions_formatter.rb +64 -84
- data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/endpoint_formatter.rb +1 -1
- data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/field_definitions/endpoint_param_formatter.rb +39 -0
- data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/field_definitions/presenter_field_formatter.rb +147 -0
- data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/field_definitions/response_field_formatter.rb +146 -0
- data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/presenter_formatter.rb +53 -55
- data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/tags_formatter.rb +1 -1
- data/lib/brainstem/api_docs/presenter.rb +16 -8
- data/lib/brainstem/api_docs/presenter_collection.rb +8 -5
- data/lib/brainstem/api_docs/sinks/open_api_specification_sink.rb +3 -1
- data/lib/brainstem/cli/generate_api_docs_command.rb +4 -0
- data/lib/brainstem/concerns/controller_dsl.rb +90 -20
- data/lib/brainstem/concerns/presenter_dsl.rb +16 -8
- data/lib/brainstem/dsl/association.rb +12 -0
- data/lib/brainstem/dsl/fields_block.rb +1 -1
- data/lib/brainstem/version.rb +1 -1
- data/spec/brainstem/api_docs/controller_spec.rb +127 -5
- data/spec/brainstem/api_docs/endpoint_spec.rb +489 -57
- data/spec/brainstem/api_docs/formatters/open_api_specification/helper_spec.rb +15 -4
- data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/endpoint/param_definitions_formatter_spec.rb +112 -66
- data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/endpoint/response_definitions_formatter_spec.rb +404 -32
- data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/field_definitions/endpoint_param_formatter_spec.rb +335 -0
- data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/field_definitions/presenter_field_formatter_spec.rb +237 -0
- data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/field_definitions/response_field_formatter_spec.rb +413 -0
- data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/presenter_formatter_spec.rb +116 -4
- data/spec/brainstem/api_docs/presenter_spec.rb +406 -24
- data/spec/brainstem/cli/generate_api_docs_command_spec.rb +8 -0
- data/spec/brainstem/concerns/controller_dsl_spec.rb +606 -45
- data/spec/brainstem/concerns/presenter_dsl_spec.rb +34 -2
- data/spec/brainstem/dsl/association_spec.rb +54 -3
- metadata +11 -2
@@ -0,0 +1,335 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'brainstem/api_docs/formatters/open_api_specification/version_2/field_definitions/endpoint_param_formatter'
|
3
|
+
|
4
|
+
module Brainstem
|
5
|
+
module ApiDocs
|
6
|
+
module Formatters
|
7
|
+
module OpenApiSpecification
|
8
|
+
module Version2
|
9
|
+
module FieldDefinitions
|
10
|
+
describe EndpointParamFormatter do
|
11
|
+
describe '#format' do
|
12
|
+
let(:endpoint) { OpenStruct.new(controller_name: 'Test', action: 'create') }
|
13
|
+
let(:field_name) { 'sprocket' }
|
14
|
+
let(:configuration_tree) { field_configuration_tree.with_indifferent_access }
|
15
|
+
|
16
|
+
subject { described_class.new(endpoint, field_name, configuration_tree).format }
|
17
|
+
|
18
|
+
context 'when formatting nested field' do
|
19
|
+
context 'when formatting a nested array field with objects' do
|
20
|
+
let(:field_configuration_tree) do
|
21
|
+
{
|
22
|
+
_config: {
|
23
|
+
type: 'array',
|
24
|
+
nested_levels: 2,
|
25
|
+
item_type: 'hash',
|
26
|
+
},
|
27
|
+
widget_name: {
|
28
|
+
_config: {
|
29
|
+
required: true,
|
30
|
+
type: 'string',
|
31
|
+
info: 'the name of the widget',
|
32
|
+
nodoc: false
|
33
|
+
},
|
34
|
+
},
|
35
|
+
support_email: {
|
36
|
+
_config: {
|
37
|
+
required: true,
|
38
|
+
type: 'string',
|
39
|
+
info: 'contact support',
|
40
|
+
nodoc: false
|
41
|
+
},
|
42
|
+
},
|
43
|
+
version: {
|
44
|
+
_config: {
|
45
|
+
type: 'string',
|
46
|
+
info: 'the version of the widget',
|
47
|
+
nodoc: false
|
48
|
+
},
|
49
|
+
},
|
50
|
+
widget_permissions: {
|
51
|
+
_config: {
|
52
|
+
type: 'array',
|
53
|
+
item_type: 'hash',
|
54
|
+
info: 'the permissions of the widget',
|
55
|
+
nodoc: false
|
56
|
+
},
|
57
|
+
can_edit: {
|
58
|
+
_config: {
|
59
|
+
required: true,
|
60
|
+
type: 'array',
|
61
|
+
item_type: 'boolean',
|
62
|
+
nodoc: false
|
63
|
+
},
|
64
|
+
},
|
65
|
+
can_delete: {
|
66
|
+
_config: {
|
67
|
+
type: 'boolean',
|
68
|
+
nodoc: false
|
69
|
+
},
|
70
|
+
},
|
71
|
+
can_rename: {
|
72
|
+
_config: {
|
73
|
+
type: 'boolean',
|
74
|
+
info: 'can rename the widget',
|
75
|
+
nodoc: false
|
76
|
+
},
|
77
|
+
},
|
78
|
+
},
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'formats a complicated tree with arrays and hashes as children' do
|
83
|
+
expect(subject).to eq(
|
84
|
+
'type' => 'array',
|
85
|
+
'items' => {
|
86
|
+
'type' => 'array',
|
87
|
+
'items' => {
|
88
|
+
'type' => 'object',
|
89
|
+
'required' => ['widget_name', 'support_email'],
|
90
|
+
'properties' => {
|
91
|
+
'widget_name' => {
|
92
|
+
'type' => 'string',
|
93
|
+
'description' => 'The name of the widget.',
|
94
|
+
},
|
95
|
+
'support_email' => {
|
96
|
+
'type' => 'string',
|
97
|
+
'description' => 'Contact support.',
|
98
|
+
},
|
99
|
+
'version' => {
|
100
|
+
'type' => 'string',
|
101
|
+
'description' => 'The version of the widget.',
|
102
|
+
},
|
103
|
+
'widget_permissions' => {
|
104
|
+
'type' => 'array',
|
105
|
+
'description' => 'The permissions of the widget.',
|
106
|
+
'items' => {
|
107
|
+
'type' => 'object',
|
108
|
+
'required' => ['can_edit'],
|
109
|
+
'properties' => {
|
110
|
+
'can_edit' => {
|
111
|
+
'type' => 'array',
|
112
|
+
'items' => {
|
113
|
+
'type' => 'boolean',
|
114
|
+
}
|
115
|
+
},
|
116
|
+
'can_delete' => {
|
117
|
+
'type' => 'boolean',
|
118
|
+
},
|
119
|
+
'can_rename' => {
|
120
|
+
'type' => 'boolean',
|
121
|
+
'description' => 'Can rename the widget.',
|
122
|
+
},
|
123
|
+
},
|
124
|
+
},
|
125
|
+
},
|
126
|
+
},
|
127
|
+
}
|
128
|
+
}
|
129
|
+
)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context 'when formatting a hash field' do
|
134
|
+
let(:field_configuration_tree) do
|
135
|
+
{
|
136
|
+
_config: {
|
137
|
+
type: 'hash',
|
138
|
+
info: 'Details about the widget',
|
139
|
+
},
|
140
|
+
widget_name: {
|
141
|
+
_config: {
|
142
|
+
required: true,
|
143
|
+
type: 'string',
|
144
|
+
info: 'the name of the widget',
|
145
|
+
nodoc: false
|
146
|
+
},
|
147
|
+
},
|
148
|
+
widget_permissions: {
|
149
|
+
_config: {
|
150
|
+
type: 'array',
|
151
|
+
item_type: 'string',
|
152
|
+
info: 'the permissions of the widget',
|
153
|
+
nodoc: false
|
154
|
+
},
|
155
|
+
},
|
156
|
+
}
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'returns the formatted field schema' do
|
160
|
+
expect(subject).to eq(
|
161
|
+
'type' => 'object',
|
162
|
+
'description' => 'Details about the widget.',
|
163
|
+
'required' => ['widget_name'],
|
164
|
+
'properties' => {
|
165
|
+
'widget_name' => {
|
166
|
+
'type' => 'string',
|
167
|
+
'description' => 'The name of the widget.',
|
168
|
+
},
|
169
|
+
'widget_permissions' => {
|
170
|
+
'type' => 'array',
|
171
|
+
'description' => 'The permissions of the widget.',
|
172
|
+
'items' => {
|
173
|
+
'type' => 'string',
|
174
|
+
}
|
175
|
+
}
|
176
|
+
}
|
177
|
+
)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
context 'when formatting params with dynamic keys' do
|
183
|
+
context 'when formatting a hash param' do
|
184
|
+
let(:field_configuration_tree) do
|
185
|
+
{
|
186
|
+
_config: {
|
187
|
+
type: 'hash',
|
188
|
+
info: 'Dynamic keys hash.',
|
189
|
+
},
|
190
|
+
_dynamic_key: {
|
191
|
+
_config: {
|
192
|
+
nodoc: false,
|
193
|
+
type: 'hash',
|
194
|
+
dynamic_key: true,
|
195
|
+
info: 'a dynamic description.'
|
196
|
+
},
|
197
|
+
blah: {
|
198
|
+
_config: {
|
199
|
+
nodoc: false,
|
200
|
+
type: 'string',
|
201
|
+
},
|
202
|
+
},
|
203
|
+
},
|
204
|
+
}
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'returns the formatted field schema' do
|
208
|
+
expect(subject).to eq({
|
209
|
+
'type' => 'object',
|
210
|
+
'description' => 'Dynamic keys hash.',
|
211
|
+
'additionalProperties' => {
|
212
|
+
'type' => 'object',
|
213
|
+
'description' => 'A dynamic description.',
|
214
|
+
'properties' => {
|
215
|
+
'blah' => {
|
216
|
+
'type' => 'string',
|
217
|
+
},
|
218
|
+
},
|
219
|
+
},
|
220
|
+
})
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
context 'when formatting a nested hash field' do
|
225
|
+
let(:field_configuration_tree) do
|
226
|
+
{
|
227
|
+
_config: {
|
228
|
+
type: 'hash',
|
229
|
+
info: 'Dynamic keys hash.',
|
230
|
+
},
|
231
|
+
non_dynamic_key: {
|
232
|
+
_config: {
|
233
|
+
nodoc: false,
|
234
|
+
type: 'hash',
|
235
|
+
info: 'A non-dynamic description.'
|
236
|
+
},
|
237
|
+
non_dynamic_property: {
|
238
|
+
_config: {
|
239
|
+
nodoc: false,
|
240
|
+
type: 'string',
|
241
|
+
},
|
242
|
+
},
|
243
|
+
},
|
244
|
+
_dynamic_key: {
|
245
|
+
_config: {
|
246
|
+
nodoc: false,
|
247
|
+
type: 'hash',
|
248
|
+
dynamic_key: true,
|
249
|
+
required: true,
|
250
|
+
info: 'A dynamic description.'
|
251
|
+
},
|
252
|
+
dynamic_property1: {
|
253
|
+
_config: {
|
254
|
+
nodoc: false,
|
255
|
+
type: 'string',
|
256
|
+
required: true,
|
257
|
+
},
|
258
|
+
},
|
259
|
+
_dynamic_key: {
|
260
|
+
_config: {
|
261
|
+
nodoc: false,
|
262
|
+
type: 'hash',
|
263
|
+
dynamic_key: true,
|
264
|
+
info: 'A 2nd dynamic description.'
|
265
|
+
},
|
266
|
+
_dynamic_key: {
|
267
|
+
_config: {
|
268
|
+
nodoc: false,
|
269
|
+
type: 'string',
|
270
|
+
dynamic_key: true,
|
271
|
+
info: 'A dynamic string.',
|
272
|
+
required: true,
|
273
|
+
},
|
274
|
+
},
|
275
|
+
dynamic_property2: {
|
276
|
+
_config: {
|
277
|
+
nodoc: false,
|
278
|
+
type: 'string',
|
279
|
+
},
|
280
|
+
},
|
281
|
+
},
|
282
|
+
},
|
283
|
+
}
|
284
|
+
end
|
285
|
+
|
286
|
+
it 'returns the formatted field schema' do
|
287
|
+
expect(subject).to eq({
|
288
|
+
'type' => 'object',
|
289
|
+
'description' => 'Dynamic keys hash.',
|
290
|
+
'properties' => {
|
291
|
+
'non_dynamic_key' => {
|
292
|
+
'type' => 'object',
|
293
|
+
'description' => 'A non-dynamic description.',
|
294
|
+
'properties' => {
|
295
|
+
'non_dynamic_property' => {
|
296
|
+
'type' => 'string',
|
297
|
+
}
|
298
|
+
}
|
299
|
+
},
|
300
|
+
},
|
301
|
+
'additionalProperties' => {
|
302
|
+
'type' => 'object',
|
303
|
+
'description' => 'A dynamic description.',
|
304
|
+
'required' => ['dynamic_property1'],
|
305
|
+
'properties' => {
|
306
|
+
'dynamic_property1' => {
|
307
|
+
'type' => 'string',
|
308
|
+
},
|
309
|
+
},
|
310
|
+
'additionalProperties' => {
|
311
|
+
'type' => 'object',
|
312
|
+
'description' => 'A 2nd dynamic description.',
|
313
|
+
'properties' => {
|
314
|
+
'dynamic_property2' => {
|
315
|
+
'type' => 'string',
|
316
|
+
},
|
317
|
+
},
|
318
|
+
'additionalProperties' => {
|
319
|
+
'type' => 'string',
|
320
|
+
'description' => 'A dynamic string.',
|
321
|
+
},
|
322
|
+
},
|
323
|
+
},
|
324
|
+
})
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
@@ -0,0 +1,237 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'brainstem/api_docs/presenter'
|
3
|
+
require 'brainstem/api_docs/formatters/open_api_specification/version_2/field_definitions/presenter_field_formatter'
|
4
|
+
|
5
|
+
module Brainstem
|
6
|
+
module ApiDocs
|
7
|
+
module Formatters
|
8
|
+
module OpenApiSpecification
|
9
|
+
module Version2
|
10
|
+
module FieldDefinitions
|
11
|
+
describe PresenterFieldFormatter do
|
12
|
+
describe '#format' do
|
13
|
+
let(:presenter_class) do
|
14
|
+
Class.new(Brainstem::Presenter) do
|
15
|
+
presents Workspace
|
16
|
+
end
|
17
|
+
end
|
18
|
+
let(:presenter) { Presenter.new(Object.new, const: presenter_class, target_class: 'Workspace') }
|
19
|
+
let(:conditionals) { {} }
|
20
|
+
let(:field) { presenter.valid_fields.values.first }
|
21
|
+
|
22
|
+
|
23
|
+
subject { described_class.new(presenter, field).format }
|
24
|
+
|
25
|
+
before do
|
26
|
+
stub(presenter).conditionals { conditionals }
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'when formatting non-nested field' do
|
30
|
+
before do
|
31
|
+
presenter_class.fields do
|
32
|
+
field :sprocket_name, :string, via: :name, info: 'The name of the sprocket'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'returns the formatted field schema' do
|
37
|
+
expect(subject).to eq(
|
38
|
+
'type' => 'string',
|
39
|
+
'description' => 'The name of the sprocket.',
|
40
|
+
)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'when formatting nested field' do
|
45
|
+
context 'when formatting an array field' do
|
46
|
+
before do
|
47
|
+
presenter_class.fields do
|
48
|
+
field :sprocket_names, :array, info: 'All the names for the sprocket'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'returns the formatted field schema' do
|
53
|
+
expect(subject).to eq(
|
54
|
+
'type' => 'array',
|
55
|
+
'description' => 'All the names for the sprocket.',
|
56
|
+
'items' => {
|
57
|
+
'type' => 'string',
|
58
|
+
}
|
59
|
+
)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'when formatting a nested array field with non nested data type' do
|
64
|
+
before do
|
65
|
+
presenter_class.fields do
|
66
|
+
field :sprocket_usages, :array,
|
67
|
+
item_type: :decimal,
|
68
|
+
nested_levels: 3,
|
69
|
+
info: 'All the names for the sprocket'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'returns the formatted field schema' do
|
74
|
+
expect(subject).to eq(
|
75
|
+
'type' => 'array',
|
76
|
+
'description' => 'All the names for the sprocket.',
|
77
|
+
'items' => {
|
78
|
+
'type' => 'array',
|
79
|
+
'items' => {
|
80
|
+
'type' => 'array',
|
81
|
+
'items' => {
|
82
|
+
'type' => 'number',
|
83
|
+
'format' => 'float'
|
84
|
+
}
|
85
|
+
}
|
86
|
+
}
|
87
|
+
)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context 'when formatting a nested array field with objects' do
|
92
|
+
before do
|
93
|
+
presenter_class.fields do
|
94
|
+
fields :sprockets, :array, nested_levels: 2, info: 'I am a sprocket' do
|
95
|
+
field :widget_name, :string,
|
96
|
+
info: 'the name of the widget'
|
97
|
+
|
98
|
+
fields :widget_permissions, :array, info: 'the permissions of the widget' do
|
99
|
+
field :can_edit, :array,
|
100
|
+
nested_levels: 3,
|
101
|
+
item_type: 'boolean',
|
102
|
+
info: 'the ethos of the widget'
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'formats a complicated tree with arrays and hashes as children' do
|
109
|
+
expect(subject).to eq(
|
110
|
+
'type' => 'array',
|
111
|
+
'description' => 'I am a sprocket.',
|
112
|
+
'items' => {
|
113
|
+
'type' => 'array',
|
114
|
+
'items' => {
|
115
|
+
'type' => 'object',
|
116
|
+
'properties' => {
|
117
|
+
'widget_name' => {
|
118
|
+
'type' => 'string',
|
119
|
+
'description' => 'The name of the widget.',
|
120
|
+
},
|
121
|
+
'widget_permissions' => {
|
122
|
+
'type' => 'array',
|
123
|
+
'description' => 'The permissions of the widget.',
|
124
|
+
'items' => {
|
125
|
+
'type' => 'object',
|
126
|
+
'properties' => {
|
127
|
+
'can_edit' => {
|
128
|
+
'type' => 'array',
|
129
|
+
'description' => 'The ethos of the widget.',
|
130
|
+
'items' => {
|
131
|
+
'type' => 'array',
|
132
|
+
'items' => {
|
133
|
+
'type' => 'array',
|
134
|
+
'items' => {
|
135
|
+
'type' => 'boolean'
|
136
|
+
}
|
137
|
+
}
|
138
|
+
}
|
139
|
+
}
|
140
|
+
}
|
141
|
+
}
|
142
|
+
}
|
143
|
+
}
|
144
|
+
}
|
145
|
+
}
|
146
|
+
)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
context 'when formatting a hash field' do
|
151
|
+
before do
|
152
|
+
presenter_class.fields do
|
153
|
+
fields :sprockets, :hash, info: 'Details about the widget' do
|
154
|
+
field :widget_name, :string,
|
155
|
+
info: 'the name of the widget'
|
156
|
+
|
157
|
+
fields :widget_permissions, :array, info: 'the permissions of the widget' do
|
158
|
+
field :can_edit, :boolean,
|
159
|
+
info: 'can edit the widget'
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'returns the formatted field schema' do
|
166
|
+
expect(subject).to eq(
|
167
|
+
'type' => 'object',
|
168
|
+
'description' => 'Details about the widget.',
|
169
|
+
'properties' => {
|
170
|
+
'widget_name' => {
|
171
|
+
'type' => 'string',
|
172
|
+
'description' => 'The name of the widget.',
|
173
|
+
},
|
174
|
+
'widget_permissions' => {
|
175
|
+
'type' => 'array',
|
176
|
+
'description' => 'The permissions of the widget.',
|
177
|
+
'items' => {
|
178
|
+
'type' => 'object',
|
179
|
+
'properties' => {
|
180
|
+
'can_edit' => {
|
181
|
+
'type' => 'boolean',
|
182
|
+
'description' => 'Can edit the widget.'
|
183
|
+
}
|
184
|
+
}
|
185
|
+
}
|
186
|
+
}
|
187
|
+
}
|
188
|
+
)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
context 'when formatting a multi nested hash field' do
|
193
|
+
before do
|
194
|
+
presenter_class.fields do
|
195
|
+
fields :sprockets do
|
196
|
+
field :widget_name, :string,
|
197
|
+
info: 'the name of the widget'
|
198
|
+
|
199
|
+
fields :widget_permissions, :hash, info: 'the permissions of the widget' do
|
200
|
+
field :can_edit, :boolean,
|
201
|
+
info: 'can edit the widget'
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'returns the formatted field schema' do
|
208
|
+
expect(subject).to eq(
|
209
|
+
'type' => 'object',
|
210
|
+
'properties' => {
|
211
|
+
'widget_name' => {
|
212
|
+
'type' => 'string',
|
213
|
+
'description' => 'The name of the widget.',
|
214
|
+
},
|
215
|
+
'widget_permissions' => {
|
216
|
+
'type' => 'object',
|
217
|
+
'description' => 'The permissions of the widget.',
|
218
|
+
'properties' => {
|
219
|
+
'can_edit' => {
|
220
|
+
'type' => 'boolean',
|
221
|
+
'description' => 'Can edit the widget.',
|
222
|
+
}
|
223
|
+
}
|
224
|
+
}
|
225
|
+
}
|
226
|
+
)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|