grape-swagger 0.7.2 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +33 -0
  3. data/.rubocop.yml +36 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +3 -0
  7. data/CHANGELOG.md +90 -0
  8. data/CONTRIBUTING.md +126 -0
  9. data/Gemfile +1 -21
  10. data/LICENSE.txt +1 -1
  11. data/README.md +397 -0
  12. data/RELEASING.md +80 -0
  13. data/Rakefile +6 -23
  14. data/UPGRADING.md +47 -0
  15. data/grape-swagger.gemspec +26 -84
  16. data/lib/grape-swagger.rb +237 -111
  17. data/lib/grape-swagger/errors.rb +9 -0
  18. data/lib/grape-swagger/markdown.rb +23 -0
  19. data/lib/grape-swagger/markdown/kramdown_adapter.rb +37 -0
  20. data/lib/grape-swagger/markdown/redcarpet_adapter.rb +89 -0
  21. data/lib/grape-swagger/version.rb +3 -0
  22. data/spec/api_description_spec.rb +41 -0
  23. data/spec/api_global_models_spec.rb +76 -0
  24. data/spec/api_models_spec.rb +190 -93
  25. data/spec/default_api_spec.rb +31 -36
  26. data/spec/form_params_spec.rb +56 -53
  27. data/spec/grape-swagger_helper_spec.rb +88 -49
  28. data/spec/grape-swagger_spec.rb +7 -5
  29. data/spec/hide_api_spec.rb +58 -55
  30. data/spec/markdown/kramdown_adapter_spec.rb +38 -0
  31. data/spec/markdown/markdown_spec.rb +27 -0
  32. data/spec/markdown/redcarpet_adapter_spec.rb +81 -0
  33. data/spec/namespaced_api_spec.rb +47 -0
  34. data/spec/non_default_api_spec.rb +372 -222
  35. data/spec/response_model_spec.rb +80 -0
  36. data/spec/simple_mounted_api_spec.rb +113 -118
  37. data/spec/spec_helper.rb +0 -1
  38. data/spec/version_spec.rb +8 -0
  39. data/test/api.rb +62 -0
  40. data/test/config.ru +10 -2
  41. data/test/splines.png +0 -0
  42. metadata +145 -91
  43. data/.rvmrc +0 -48
  44. data/CHANGELOG.markdown +0 -55
  45. data/Gemfile.lock +0 -94
  46. data/README.markdown +0 -168
  47. data/VERSION +0 -1
  48. data/test/nested_api.rb +0 -30
@@ -0,0 +1,9 @@
1
+ module GrapeSwagger
2
+ module Errors
3
+ class MarkdownDependencyMissingError < StandardError
4
+ def initialize(missing_gem)
5
+ super("Missing required dependency: #{missing_gem}")
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,23 @@
1
+ module GrapeSwagger
2
+ class Markdown
3
+ attr_reader :adapter
4
+
5
+ ###
6
+ # Initializes the markdown class with an adapter.
7
+ # The adapter needs to implement the method markdown which will be called by this interface class.
8
+ # The adapters are responsible of loading the required markdown dependencies and throw errors.
9
+ ###
10
+ def initialize(adapter)
11
+ adapter = adapter.new if adapter.is_a?(Class)
12
+ fail(ArgumentError, "The configured markdown adapter should implement the method #{ :markdown }") unless adapter.respond_to? :markdown
13
+ @adapter = adapter
14
+ end
15
+
16
+ ###
17
+ # Calls markdown to the configured adapter.
18
+ ###
19
+ def as_markdown(text)
20
+ @adapter.markdown(text)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,37 @@
1
+ module GrapeSwagger
2
+ class Markdown
3
+ class KramdownAdapter
4
+ attr_reader :options
5
+
6
+ ###
7
+ # Initializes the kramdown adapter with options.
8
+ # See kramdown documentation what options can be passed.
9
+ # Default it uses Github flavoured markup as input and won't use coderay as converter for syntax highlighting.
10
+ # config: an hash of configuration options to be passed to the kramdown.
11
+ # usage:
12
+ # Add the kramdown gem to your gemfile or run:
13
+ # $ (sudo) gem install kramdown
14
+ #
15
+ # Then pass a new instance of GrapeSwagger::Markdown::KramdownAdapter as markdown option.
16
+ ###
17
+ def initialize(config = {})
18
+ require 'kramdown'
19
+ defaults = {
20
+ input: 'GFM',
21
+ enable_coderay: false
22
+ }
23
+ @options = defaults.merge(config)
24
+ rescue LoadError
25
+ raise GrapeSwagger::Errors::MarkdownDependencyMissingError, 'kramdown'
26
+ end
27
+
28
+ ###
29
+ # marks down the given text to html format.
30
+ # text: The text to be formatted.
31
+ ###
32
+ def markdown(text)
33
+ Kramdown::Document.new(text, @options).to_html
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,89 @@
1
+ module GrapeSwagger
2
+ class Markdown
3
+ class RedcarpetAdapter
4
+ module RenderWithoutSyntaxHighlighter
5
+ require 'cgi'
6
+
7
+ def block_code(code, language)
8
+ language ||= 'text'
9
+ "<div class=\"code_highlight\"><pre><code class=\"highlight #{language}\">#{CGI.escapeHTML(code)}</code></pre></div>"
10
+ end
11
+ end
12
+
13
+ attr_reader :extension_options
14
+
15
+ attr_reader :render_options
16
+
17
+ ###
18
+ # Initializes the redcarpet adapter with markup options.
19
+ # See redcarpet documentation what options can be passed.
20
+ # Default it uses fenced_code_blocks, autolinks and rouge as syntax highlighter.
21
+ # To configure an highlighter add {highlighter: :value} to the extentions hash.
22
+ # Currently supported highlighters:
23
+ # :rouge
24
+ #
25
+ # extensions: an hash of configuration options to be passed to markdown.
26
+ # render_options: an hash of configuration options to be passed to renderer.
27
+ #
28
+ # usage:
29
+ # Add the redcarpet gem to your gemfile or run:
30
+ # $ (sudo) gem install redcarpet
31
+ # when you want to have rouge as syntax highlighter add rouge to the gemfile or run:
32
+ # $ (sudo) gem install rouge
33
+ #
34
+ # GrapeSwagger::Markdown::RedcarpetAdapter.new({highlighter: :none},{no_links: true}) # will use no syntax highlighter and won't render links.
35
+ ###
36
+ def initialize(options = {})
37
+ require 'redcarpet'
38
+ extentions_defaults = {
39
+ fenced_code_blocks: true,
40
+ autolink: true
41
+ }
42
+ render_defaults = { highlighter: :rouge }
43
+ @extension_options = extentions_defaults.merge(options.fetch(:extensions, {}))
44
+ @render_options = render_defaults.merge(options.fetch(:render_options, {}))
45
+ @renderer = new_redcarpet_renderer(@render_options.delete(:highlighter)).new(@render_options)
46
+ @markdown = Redcarpet::Markdown.new(@renderer, @extension_options)
47
+ rescue LoadError
48
+ raise GrapeSwagger::Errors::MarkdownDependencyMissingError, 'redcarpet'
49
+ end
50
+
51
+ ###
52
+ # Marks down the given text to html format.
53
+ ###
54
+ def markdown(text)
55
+ @markdown.render(text)
56
+ end
57
+
58
+ private
59
+
60
+ ###
61
+ # Creates a new redcarpet renderer based on the highlighter given.
62
+ #
63
+ # render_options: options passed to the renderer.
64
+ #
65
+ # usage:
66
+ # new_redcarpet_renderer(:rouge) # uses rouge as highlighter.
67
+ # new_redcarpet_renderer # no highlight plugin
68
+ ###
69
+ def new_redcarpet_renderer(syntax_highlighter = nil)
70
+ case syntax_highlighter
71
+ when :rouge
72
+ begin
73
+ Class.new(Redcarpet::Render::HTML) do
74
+ require 'rouge'
75
+ require 'rouge/plugins/redcarpet'
76
+ include Rouge::Plugins::Redcarpet
77
+ end
78
+ rescue LoadError
79
+ raise GrapeSwagger::Errors::MarkdownDependencyMissingError, 'rouge'
80
+ end
81
+ else
82
+ Class.new(Redcarpet::Render::HTML) do
83
+ include RenderWithoutSyntaxHighlighter
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,3 @@
1
+ module GrapeSwagger
2
+ VERSION = '0.8.0'
3
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'API Description' do
4
+ context 'with no additional options' do
5
+ subject do
6
+ Class.new(Grape::API) do
7
+ add_swagger_documentation
8
+ end
9
+ end
10
+
11
+ it 'describes the API with defaults' do
12
+ routes = subject.endpoints.first.routes
13
+ expect(routes.count).to eq 2
14
+ expect(routes.first.route_description).to eq 'Swagger compatible API description'
15
+ expect(routes.first.route_params).to eq({})
16
+ expect(routes.last.route_description).to eq 'Swagger compatible API description for specific API'
17
+ expect(routes.last.route_params).to eq('name' => { desc: 'Resource name of mounted API', type: 'string', required: true })
18
+ end
19
+ end
20
+
21
+ context 'with additional options' do
22
+ subject do
23
+ Class.new(Grape::API) do
24
+ add_swagger_documentation \
25
+ api_documentation: { desc: 'First', params: { x: 1 }, xx: 11 },
26
+ specific_api_documentation: { desc: 'Second', params: { y: 42 }, yy: 4242 }
27
+ end
28
+ end
29
+
30
+ it 'describes the API with defaults' do
31
+ routes = subject.endpoints.first.routes
32
+ expect(routes.count).to eq 2
33
+ expect(routes.first.route_description).to eq 'First'
34
+ expect(routes.first.route_params).to eq(x: 1)
35
+ expect(routes.first.route_xx).to eq(11)
36
+ expect(routes.last.route_description).to eq 'Second'
37
+ expect(routes.last.route_params).to eq('name' => { desc: 'Resource name of mounted API', type: 'string', required: true }, y: 42)
38
+ expect(routes.last.route_yy).to eq(4242)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Global Models' do
4
+
5
+ before :all do
6
+ module Entities
7
+ module Some
8
+ class Thing < Grape::Entity
9
+ expose :text, documentation: { type: 'string', desc: 'Content of something.' }
10
+ end
11
+
12
+ class CombinedThing < Grape::Entity
13
+ expose :text, documentation: { type: 'string', desc: 'Content of something.' }
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ subject do
20
+ Class.new(Grape::API) do
21
+ desc 'This gets thing.', params: Entities::Some::Thing.documentation
22
+ get '/thing' do
23
+ thing = OpenStruct.new text: 'thing'
24
+ present thing, with: Entities::Some::Thing
25
+ end
26
+
27
+ desc 'This gets combined thing.',
28
+ params: Entities::Some::CombinedThing.documentation,
29
+ entity: Entities::Some::CombinedThing
30
+ get '/combined_thing' do
31
+ thing = OpenStruct.new text: 'thing'
32
+ present thing, with: Entities::Some::CombinedThing
33
+ end
34
+
35
+ add_swagger_documentation models: [Entities::Some::Thing]
36
+ end
37
+ end
38
+
39
+ def app
40
+ subject
41
+ end
42
+
43
+ it 'includes models specified' do
44
+ get '/swagger_doc/thing.json'
45
+ json = JSON.parse(last_response.body)
46
+ expect(json['models']).to eq(
47
+ 'Some::Thing' => {
48
+ 'id' => 'Some::Thing',
49
+ 'properties' => {
50
+ 'text' => { 'type' => 'string', 'description' => 'Content of something.' }
51
+ }
52
+ })
53
+ end
54
+
55
+ it 'uses global models and route endpoint specific entities together' do
56
+ get '/swagger_doc/combined_thing.json'
57
+ json = JSON.parse(last_response.body)
58
+
59
+ expect(json['models']).to include(
60
+ 'Some::Thing' => {
61
+ 'id' => 'Some::Thing',
62
+ 'properties' => {
63
+ 'text' => { 'type' => 'string', 'description' => 'Content of something.' }
64
+ }
65
+ })
66
+
67
+ expect(json['models']).to include(
68
+ 'Some::CombinedThing' => {
69
+ 'id' => 'Some::CombinedThing',
70
+ 'properties' => {
71
+ 'text' => { 'type' => 'string', 'description' => 'Content of something.' }
72
+ }
73
+ })
74
+
75
+ end
76
+ end
@@ -1,132 +1,229 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe "API Models" do
3
+ describe 'API Models' do
4
4
 
5
5
  before :all do
6
6
  module Entities
7
7
  class Something < Grape::Entity
8
- expose :text, :documentation => { :type => "string", :desc => "Content of something." }
8
+ expose :text, documentation: { type: 'string', desc: 'Content of something.' }
9
+ end
10
+ end
11
+
12
+ module Entities
13
+ class EnumValues < Grape::Entity
14
+ expose :gender, documentation: { type: 'string', desc: 'Content of something.', values: %w(Male Female) }
15
+ expose :number, documentation: { type: 'integer', desc: 'Content of something.', values: proc { [1, 2] } }
9
16
  end
10
17
  end
11
18
 
12
19
  module Entities
13
20
  module Some
14
21
  class Thing < Grape::Entity
15
- expose :text, :documentation => { :type => "string", :desc => "Content of something." }
22
+ expose :text, documentation: { type: 'string', desc: 'Content of something.' }
23
+ end
24
+ end
25
+ end
26
+
27
+ module Entities
28
+ class ComposedOf < Grape::Entity
29
+ expose :part_text, documentation: { type: 'string', desc: 'Content of composedof.' }
30
+ end
31
+
32
+ class ComposedOfElse < Grape::Entity
33
+ def self.entity_name
34
+ 'composed'
16
35
  end
36
+ expose :part_text, documentation: { type: 'string', desc: 'Content of composedof else.' }
37
+ end
38
+
39
+ class SomeThingElse < Grape::Entity
40
+ expose :else_text, documentation: { type: 'string', desc: 'Content of something else.' }
41
+ expose :parts, using: Entities::ComposedOf, documentation: { type: 'ComposedOf',
42
+ is_array: true,
43
+ required: true }
44
+
45
+ expose :part, using: Entities::ComposedOfElse, documentation: { type: 'composes' }
46
+ end
47
+ end
48
+
49
+ module Entities
50
+ class AliasedThing < Grape::Entity
51
+ expose :something, as: :post, using: Entities::Something, documentation: { type: 'Something', desc: 'Reference to something.' }
17
52
  end
18
53
  end
54
+ end
19
55
 
20
- class ModelsApi < Grape::API
56
+ def app
57
+ Class.new(Grape::API) do
21
58
  format :json
22
- desc 'This gets something.', {
23
- entity: Entities::Something
24
- }
59
+ desc 'This gets something.', entity: Entities::Something
60
+
25
61
  get '/something' do
26
62
  something = OpenStruct.new text: 'something'
27
63
  present something, with: Entities::Something
28
64
  end
29
65
 
30
- desc 'This gets thing.', {
31
- entity: Entities::Some::Thing
32
- }
33
- get "/thing" do
66
+ desc 'This gets thing.', entity: Entities::Some::Thing
67
+ get '/thing' do
34
68
  thing = OpenStruct.new text: 'thing'
35
69
  present thing, with: Entities::Some::Thing
36
70
  end
71
+
72
+ desc 'This gets somthing else.', entity: Entities::SomeThingElse
73
+ get '/somethingelse' do
74
+ part = OpenStruct.new part_text: 'part thing'
75
+ thing = OpenStruct.new else_text: 'else thing', parts: [part], part: part
76
+
77
+ present thing, with: Entities::SomeThingElse
78
+ end
79
+
80
+ desc 'This tests the enum values in params and documentation.', entity: Entities::EnumValues, params: Entities::EnumValues.documentation
81
+ get '/enum_description_in_entity' do
82
+
83
+ enum_value = OpenStruct.new gender: 'Male', number: 1
84
+
85
+ present enum_value, with: Entities::EnumValues
86
+ end
87
+
88
+ desc 'This gets an aliased thing.', entity: Entities::AliasedThing
89
+ get '/aliasedthing' do
90
+ something = OpenStruct.new(something: OpenStruct.new(text: 'something'))
91
+ present something, with: Entities::AliasedThing
92
+ end
93
+
37
94
  add_swagger_documentation
38
95
  end
39
96
  end
40
97
 
41
- def app; ModelsApi; end
42
-
43
- it "should document specified models" do
44
- get '/swagger_doc'
45
- JSON.parse(last_response.body).should == {
46
- "apiVersion" => "0.1",
47
- "swaggerVersion" => "1.2",
48
- "basePath" => "http://example.org",
49
- "info" => {},
50
- "produces" => ["application/json"],
51
- "operations" => [],
52
- "apis" => [
53
- { "path" => "/swagger_doc/something.{format}" },
54
- { "path" => "/swagger_doc/thing.{format}" },
55
- { "path" => "/swagger_doc/swagger_doc.{format}" }
98
+ context 'swagger_doc' do
99
+ subject do
100
+ get '/swagger_doc'
101
+ JSON.parse(last_response.body)
102
+ end
103
+
104
+ it 'returns a swagger-compatible doc' do
105
+ expect(subject).to include(
106
+ 'apiVersion' => '0.1',
107
+ 'swaggerVersion' => '1.2',
108
+ 'info' => {},
109
+ 'produces' => ['application/json']
110
+ )
111
+ end
112
+
113
+ it 'documents apis' do
114
+ expect(subject['apis']).to eq [
115
+ { 'path' => '/something.{format}', 'description' => 'Operations about somethings' },
116
+ { 'path' => '/thing.{format}', 'description' => 'Operations about things' },
117
+ { 'path' => '/somethingelse.{format}', 'description' => 'Operations about somethingelses' },
118
+ { 'path' => '/enum_description_in_entity.{format}', 'description' => 'Operations about enum_description_in_entities' },
119
+ { 'path' => '/aliasedthing.{format}', 'description' => 'Operations about aliasedthings' },
120
+ { 'path' => '/swagger_doc.{format}', 'description' => 'Operations about swagger_docs' }
56
121
  ]
57
- }
122
+ end
58
123
  end
59
124
 
60
- it "should include type when specified" do
125
+ it 'returns type' do
61
126
  get '/swagger_doc/something.json'
62
- JSON.parse(last_response.body).should == {
63
- "apiVersion" => "0.1",
64
- "swaggerVersion" => "1.2",
65
- "basePath" => "http://example.org",
66
- "resourcePath" => "",
67
- "apis" => [{
68
- "path" => "/something.{format}",
69
- "operations" => [{
70
- "produces" => [
71
- "application/json"
72
- ],
73
- "notes" => "",
74
- "type" => "Something",
75
- "summary" => "This gets something.",
76
- "nickname" => "GET-something---format-",
77
- "httpMethod" => "GET",
78
- "parameters" => []
79
- }]
80
- }],
81
- "models" => {
82
- "Something" => {
83
- "id" => "Something",
84
- "name" => "Something",
85
- "properties" => {
86
- "text" => {
87
- "type" => "string",
88
- "description" => "Content of something."
89
- }
90
- }
91
- }
92
- }
93
- }
127
+ result = JSON.parse(last_response.body)
128
+ expect(result['apis'].first['operations'].first['type']).to eq 'Something'
94
129
  end
95
130
 
96
- it "should include nested type when specified" do
131
+ it 'includes nested type' do
97
132
  get '/swagger_doc/thing.json'
98
- JSON.parse(last_response.body).should == {
99
- "apiVersion" => "0.1",
100
- "swaggerVersion" => "1.2",
101
- "basePath" => "http://example.org",
102
- "resourcePath" => "",
103
- "apis" => [{
104
- "path" => "/thing.{format}",
105
- "operations" => [{
106
- "produces" => [
107
- "application/json"
108
- ],
109
- "notes" => "",
110
- "type" => "Some::Thing",
111
- "summary" => "This gets thing.",
112
- "nickname" => "GET-thing---format-",
113
- "httpMethod" => "GET",
114
- "parameters" => []
115
- }]
116
- }],
117
- "models" => {
118
- "Some::Thing" => {
119
- "id" => "Some::Thing",
120
- "name" => "Some::Thing",
121
- "properties" => {
122
- "text" => {
123
- "type" => "string",
124
- "description" => "Content of something."
125
- }
126
- }
127
- }
128
- }
129
- }
133
+ result = JSON.parse(last_response.body)
134
+ expect(result['apis'].first['operations'].first['type']).to eq 'Some::Thing'
135
+ end
136
+
137
+ it 'includes entities which are only used as composition' do
138
+ get '/swagger_doc/somethingelse.json'
139
+ result = JSON.parse(last_response.body)
140
+ expect(result['apis']).to eq([{
141
+ 'path' => '/somethingelse.{format}',
142
+ 'operations' => [{
143
+ 'notes' => '',
144
+ 'type' => 'SomeThingElse',
145
+ 'summary' => 'This gets somthing else.',
146
+ 'nickname' => 'GET-somethingelse---format-',
147
+ 'method' => 'GET',
148
+ 'parameters' => []
149
+ }]
150
+ }])
151
+
152
+ expect(result['models']['SomeThingElse']).to include('id' => 'SomeThingElse',
153
+ 'properties' => {
154
+ 'else_text' => {
155
+ 'type' => 'string',
156
+ 'description' => 'Content of something else.'
157
+ },
158
+ 'parts' => {
159
+ 'type' => 'array',
160
+ 'items' => { '$ref' => 'ComposedOf' }
161
+ },
162
+ 'part' => { '$ref' => 'composes' }
163
+ },
164
+ 'required' => ['parts']
165
+
166
+ )
167
+
168
+ expect(result['models']['ComposedOf']).to include(
169
+ 'id' => 'ComposedOf',
170
+ 'properties' => {
171
+ 'part_text' => {
172
+ 'type' => 'string',
173
+ 'description' => 'Content of composedof.'
174
+ }
175
+ }
176
+ )
177
+
178
+ expect(result['models']['composed']).to include(
179
+ 'id' => 'composed',
180
+ 'properties' => {
181
+ 'part_text' => {
182
+ 'type' => 'string',
183
+ 'description' => 'Content of composedof else.'
184
+ }
185
+
186
+ }
187
+ )
130
188
  end
131
189
 
190
+ it 'includes enum values in params and documentation.' do
191
+ get '/swagger_doc/enum_description_in_entity.json'
192
+ result = JSON.parse(last_response.body)
193
+ expect(result['models']['EnumValues']).to eq(
194
+ 'id' => 'EnumValues',
195
+ 'properties' => {
196
+ 'gender' => { 'type' => 'string', 'description' => 'Content of something.', 'enum' => %w(Male Female) },
197
+ 'number' => { 'type' => 'integer', 'description' => 'Content of something.', 'enum' => [1, 2] }
198
+ }
199
+ )
200
+
201
+ expect(result['apis'][0]['operations'][0]).to include(
202
+ 'parameters' =>
203
+ [
204
+ { 'paramType' => 'query', 'name' => 'gender', 'description' => 'Content of something.', 'type' => 'string', 'required' => false, 'allowMultiple' => false, 'enum' => %w(Male Female) },
205
+ { 'paramType' => 'query', 'name' => 'number', 'description' => 'Content of something.', 'type' => 'integer', 'required' => false, 'allowMultiple' => false, 'format' => 'int32', 'enum' => [1, 2] }
206
+ ],
207
+ 'type' => 'EnumValues'
208
+ )
209
+
210
+ end
211
+
212
+ it 'includes referenced models in those with aliased references.' do
213
+ get '/swagger_doc/aliasedthing.json'
214
+ result = JSON.parse(last_response.body)
215
+ expect(result['models']['AliasedThing']).to eq(
216
+ 'id' => 'AliasedThing',
217
+ 'properties' => {
218
+ 'post' => { '$ref' => 'Something', 'description' => 'Reference to something.' }
219
+ }
220
+ )
221
+
222
+ expect(result['models']['Something']).to eq(
223
+ 'id' => 'Something',
224
+ 'properties' => {
225
+ 'text' => { 'type' => 'string', 'description' => 'Content of something.' }
226
+ }
227
+ )
228
+ end
132
229
  end