gitlab-grape-swagger 1.5.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.
Files changed (166) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.github/dependabot.yml +20 -0
  4. data/.github/workflows/ci.yml +45 -0
  5. data/.gitignore +44 -0
  6. data/.gitlab-ci.yml +19 -0
  7. data/.rspec +3 -0
  8. data/.rubocop.yml +136 -0
  9. data/.rubocop_todo.yml +60 -0
  10. data/.ruby-gemset +1 -0
  11. data/CHANGELOG.md +671 -0
  12. data/CONTRIBUTING.md +126 -0
  13. data/Dangerfile +3 -0
  14. data/Gemfile +45 -0
  15. data/Gemfile.lock +249 -0
  16. data/LICENSE.txt +20 -0
  17. data/README.md +1772 -0
  18. data/RELEASING.md +82 -0
  19. data/Rakefile +20 -0
  20. data/UPGRADING.md +201 -0
  21. data/example/api/endpoints.rb +131 -0
  22. data/example/api/entities.rb +18 -0
  23. data/example/config.ru +42 -0
  24. data/example/example_requests.postman_collection +146 -0
  25. data/example/splines.png +0 -0
  26. data/example/swagger-example.png +0 -0
  27. data/grape-swagger.gemspec +23 -0
  28. data/lib/grape-swagger/doc_methods/build_model_definition.rb +68 -0
  29. data/lib/grape-swagger/doc_methods/data_type.rb +110 -0
  30. data/lib/grape-swagger/doc_methods/extensions.rb +101 -0
  31. data/lib/grape-swagger/doc_methods/file_params.rb +17 -0
  32. data/lib/grape-swagger/doc_methods/format_data.rb +53 -0
  33. data/lib/grape-swagger/doc_methods/headers.rb +20 -0
  34. data/lib/grape-swagger/doc_methods/move_params.rb +209 -0
  35. data/lib/grape-swagger/doc_methods/operation_id.rb +32 -0
  36. data/lib/grape-swagger/doc_methods/optional_object.rb +30 -0
  37. data/lib/grape-swagger/doc_methods/parse_params.rb +190 -0
  38. data/lib/grape-swagger/doc_methods/path_string.rb +52 -0
  39. data/lib/grape-swagger/doc_methods/produces_consumes.rb +15 -0
  40. data/lib/grape-swagger/doc_methods/status_codes.rb +21 -0
  41. data/lib/grape-swagger/doc_methods/tag_name_description.rb +34 -0
  42. data/lib/grape-swagger/doc_methods/version.rb +20 -0
  43. data/lib/grape-swagger/doc_methods.rb +142 -0
  44. data/lib/grape-swagger/endpoint/params_parser.rb +76 -0
  45. data/lib/grape-swagger/endpoint.rb +476 -0
  46. data/lib/grape-swagger/errors.rb +17 -0
  47. data/lib/grape-swagger/instance.rb +7 -0
  48. data/lib/grape-swagger/model_parsers.rb +42 -0
  49. data/lib/grape-swagger/rake/oapi_tasks.rb +135 -0
  50. data/lib/grape-swagger/version.rb +5 -0
  51. data/lib/grape-swagger.rb +174 -0
  52. data/spec/issues/267_nested_namespaces.rb +55 -0
  53. data/spec/issues/403_versions_spec.rb +124 -0
  54. data/spec/issues/427_entity_as_string_spec.rb +39 -0
  55. data/spec/issues/430_entity_definitions_spec.rb +94 -0
  56. data/spec/issues/532_allow_custom_format_spec.rb +42 -0
  57. data/spec/issues/533_specify_status_code_spec.rb +78 -0
  58. data/spec/issues/537_enum_values_spec.rb +50 -0
  59. data/spec/issues/539_array_post_body_spec.rb +65 -0
  60. data/spec/issues/542_array_of_type_in_post_body_spec.rb +46 -0
  61. data/spec/issues/553_align_array_put_post_params_spec.rb +152 -0
  62. data/spec/issues/572_array_post_body_spec.rb +51 -0
  63. data/spec/issues/579_align_put_post_parameters_spec.rb +185 -0
  64. data/spec/issues/582_file_response_spec.rb +55 -0
  65. data/spec/issues/587_range_parameter_delimited_by_dash_spec.rb +26 -0
  66. data/spec/issues/605_root_route_documentation_spec.rb +23 -0
  67. data/spec/issues/650_params_array_spec.rb +65 -0
  68. data/spec/issues/677_consumes_produces_add_swagger_documentation_options_spec.rb +100 -0
  69. data/spec/issues/680_keep_204_error_schemas_spec.rb +55 -0
  70. data/spec/issues/721_set_default_parameter_location_based_on_consumes_spec.rb +62 -0
  71. data/spec/issues/751_deeply_nested_objects_spec.rb +190 -0
  72. data/spec/issues/776_multiple_presents_spec.rb +59 -0
  73. data/spec/issues/784_extensions_on_params_spec.rb +42 -0
  74. data/spec/issues/809_utf8_routes_spec.rb +55 -0
  75. data/spec/issues/832_array_hash_float_decimal_spec.rb +114 -0
  76. data/spec/issues/847_route_param_options_spec.rb +37 -0
  77. data/spec/issues/873_wildcard_segments_path_parameters_spec.rb +28 -0
  78. data/spec/issues/878_optional_path_segments_spec.rb +29 -0
  79. data/spec/issues/881_handle_file_params_spec.rb +38 -0
  80. data/spec/issues/883_query_array_parameter_spec.rb +46 -0
  81. data/spec/issues/884_dont_document_non_schema_examples_spec.rb +49 -0
  82. data/spec/issues/887_prevent_duplicate_operation_ids_spec.rb +35 -0
  83. data/spec/lib/data_type_spec.rb +111 -0
  84. data/spec/lib/endpoint/params_parser_spec.rb +124 -0
  85. data/spec/lib/endpoint_spec.rb +153 -0
  86. data/spec/lib/extensions_spec.rb +185 -0
  87. data/spec/lib/format_data_spec.rb +115 -0
  88. data/spec/lib/model_parsers_spec.rb +104 -0
  89. data/spec/lib/move_params_spec.rb +444 -0
  90. data/spec/lib/oapi_tasks_spec.rb +163 -0
  91. data/spec/lib/operation_id_spec.rb +55 -0
  92. data/spec/lib/optional_object_spec.rb +47 -0
  93. data/spec/lib/parse_params_spec.rb +68 -0
  94. data/spec/lib/path_string_spec.rb +101 -0
  95. data/spec/lib/produces_consumes_spec.rb +116 -0
  96. data/spec/lib/tag_name_description_spec.rb +80 -0
  97. data/spec/lib/version_spec.rb +28 -0
  98. data/spec/spec_helper.rb +39 -0
  99. data/spec/support/empty_model_parser.rb +23 -0
  100. data/spec/support/grape_version.rb +13 -0
  101. data/spec/support/mock_parser.rb +23 -0
  102. data/spec/support/model_parsers/entity_parser.rb +334 -0
  103. data/spec/support/model_parsers/mock_parser.rb +346 -0
  104. data/spec/support/model_parsers/representable_parser.rb +406 -0
  105. data/spec/support/namespace_tags.rb +93 -0
  106. data/spec/support/the_paths_definitions.rb +109 -0
  107. data/spec/swagger_v2/api_documentation_spec.rb +42 -0
  108. data/spec/swagger_v2/api_swagger_v2_additional_properties_spec.rb +83 -0
  109. data/spec/swagger_v2/api_swagger_v2_body_definitions_spec.rb +48 -0
  110. data/spec/swagger_v2/api_swagger_v2_definitions-models_spec.rb +36 -0
  111. data/spec/swagger_v2/api_swagger_v2_detail_spec.rb +79 -0
  112. data/spec/swagger_v2/api_swagger_v2_extensions_spec.rb +145 -0
  113. data/spec/swagger_v2/api_swagger_v2_format-content_type_spec.rb +137 -0
  114. data/spec/swagger_v2/api_swagger_v2_global_configuration_spec.rb +56 -0
  115. data/spec/swagger_v2/api_swagger_v2_hash_and_array_spec.rb +64 -0
  116. data/spec/swagger_v2/api_swagger_v2_headers_spec.rb +58 -0
  117. data/spec/swagger_v2/api_swagger_v2_hide_documentation_path_spec.rb +57 -0
  118. data/spec/swagger_v2/api_swagger_v2_hide_param_spec.rb +109 -0
  119. data/spec/swagger_v2/api_swagger_v2_ignore_defaults_spec.rb +48 -0
  120. data/spec/swagger_v2/api_swagger_v2_mounted_spec.rb +153 -0
  121. data/spec/swagger_v2/api_swagger_v2_param_type_body_nested_spec.rb +355 -0
  122. data/spec/swagger_v2/api_swagger_v2_param_type_body_spec.rb +217 -0
  123. data/spec/swagger_v2/api_swagger_v2_param_type_spec.rb +247 -0
  124. data/spec/swagger_v2/api_swagger_v2_request_params_fix_spec.rb +80 -0
  125. data/spec/swagger_v2/api_swagger_v2_response_spec.rb +147 -0
  126. data/spec/swagger_v2/api_swagger_v2_response_with_examples_spec.rb +135 -0
  127. data/spec/swagger_v2/api_swagger_v2_response_with_headers_spec.rb +216 -0
  128. data/spec/swagger_v2/api_swagger_v2_response_with_models_spec.rb +53 -0
  129. data/spec/swagger_v2/api_swagger_v2_response_with_root_spec.rb +153 -0
  130. data/spec/swagger_v2/api_swagger_v2_spec.rb +245 -0
  131. data/spec/swagger_v2/api_swagger_v2_status_codes_spec.rb +93 -0
  132. data/spec/swagger_v2/api_swagger_v2_type-format_spec.rb +90 -0
  133. data/spec/swagger_v2/boolean_params_spec.rb +38 -0
  134. data/spec/swagger_v2/default_api_spec.rb +175 -0
  135. data/spec/swagger_v2/deprecated_field_spec.rb +25 -0
  136. data/spec/swagger_v2/description_not_initialized_spec.rb +39 -0
  137. data/spec/swagger_v2/endpoint_versioned_path_spec.rb +130 -0
  138. data/spec/swagger_v2/errors_spec.rb +77 -0
  139. data/spec/swagger_v2/float_api_spec.rb +36 -0
  140. data/spec/swagger_v2/form_params_spec.rb +76 -0
  141. data/spec/swagger_v2/grape-swagger_spec.rb +17 -0
  142. data/spec/swagger_v2/guarded_endpoint_spec.rb +162 -0
  143. data/spec/swagger_v2/hide_api_spec.rb +147 -0
  144. data/spec/swagger_v2/host_spec.rb +43 -0
  145. data/spec/swagger_v2/inheritance_and_discriminator_spec.rb +57 -0
  146. data/spec/swagger_v2/mount_override_api_spec.rb +58 -0
  147. data/spec/swagger_v2/mounted_target_class_spec.rb +76 -0
  148. data/spec/swagger_v2/namespace_tags_prefix_spec.rb +122 -0
  149. data/spec/swagger_v2/namespace_tags_spec.rb +78 -0
  150. data/spec/swagger_v2/namespaced_api_spec.rb +121 -0
  151. data/spec/swagger_v2/nicknamed_api_spec.rb +25 -0
  152. data/spec/swagger_v2/operation_id_api_spec.rb +27 -0
  153. data/spec/swagger_v2/param_multi_type_spec.rb +82 -0
  154. data/spec/swagger_v2/param_type_spec.rb +95 -0
  155. data/spec/swagger_v2/param_values_spec.rb +180 -0
  156. data/spec/swagger_v2/params_array_collection_format_spec.rb +105 -0
  157. data/spec/swagger_v2/params_array_spec.rb +225 -0
  158. data/spec/swagger_v2/params_example_spec.rb +38 -0
  159. data/spec/swagger_v2/params_hash_spec.rb +77 -0
  160. data/spec/swagger_v2/params_nested_spec.rb +92 -0
  161. data/spec/swagger_v2/parent_less_namespace_spec.rb +32 -0
  162. data/spec/swagger_v2/reference_entity_spec.rb +129 -0
  163. data/spec/swagger_v2/security_requirement_spec.rb +46 -0
  164. data/spec/swagger_v2/simple_mounted_api_spec.rb +332 -0
  165. data/spec/version_spec.rb +10 -0
  166. metadata +225 -0
@@ -0,0 +1,406 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'representable/json'
4
+
5
+ RSpec.shared_context 'representable swagger example' do
6
+ before :all do
7
+ module Entities
8
+ class Something < Representable::Decorator
9
+ include Representable::JSON
10
+
11
+ class << self
12
+ # Representable doesn't have documentation method, mock this
13
+ def documentation
14
+ {
15
+ id: { type: Integer, desc: 'Identity of Something' },
16
+ text: { type: String, desc: 'Content of something.' },
17
+ links: { type: 'link', is_array: true },
18
+ others: { type: 'text', is_array: false }
19
+ }
20
+ end
21
+ end
22
+
23
+ property :id, documentation: { type: Integer, desc: 'Identity of Something' }
24
+ property :text, documentation: { type: String, desc: 'Content of something.' }
25
+ property :links, documentation: { type: 'link', is_array: true }
26
+ property :others, documentation: { type: 'text', is_array: false }
27
+ end
28
+
29
+ class EnumValues < Representable::Decorator
30
+ include Representable::JSON
31
+
32
+ property :gender, documentation: { type: 'string', desc: 'Content of something.', values: %w[Male Female] }
33
+ property :number, documentation: { type: 'integer', desc: 'Content of something.', values: [1, 2] }
34
+ end
35
+
36
+ class AliasedThing < Representable::Decorator
37
+ include Representable::JSON
38
+
39
+ property :something, as: :post, decorator: Entities::Something, documentation: { type: 'Something', desc: 'Reference to something.' }
40
+ end
41
+
42
+ class FourthLevel < Representable::Decorator
43
+ include Representable::JSON
44
+
45
+ property :text, documentation: { type: 'string' }
46
+ end
47
+
48
+ class ThirdLevel < Representable::Decorator
49
+ include Representable::JSON
50
+
51
+ property :parts, decorator: Entities::FourthLevel, documentation: { type: 'FourthLevel' }
52
+ end
53
+
54
+ class SecondLevel < Representable::Decorator
55
+ include Representable::JSON
56
+
57
+ property :parts, decorator: Entities::ThirdLevel, documentation: { type: 'ThirdLevel' }
58
+ end
59
+
60
+ class FirstLevel < Representable::Decorator
61
+ include Representable::JSON
62
+
63
+ property :parts, decorator: Entities::SecondLevel, documentation: { type: 'SecondLevel' }
64
+ end
65
+
66
+ class QueryInputElement < Representable::Decorator
67
+ include Representable::JSON
68
+
69
+ property :key, documentation: {
70
+ type: String, desc: 'Name of parameter', required: true
71
+ }
72
+ property :value, documentation: {
73
+ type: String, desc: 'Value of parameter', required: true
74
+ }
75
+ end
76
+
77
+ class QueryInput < Representable::Decorator
78
+ include Representable::JSON
79
+
80
+ property :elements, decorator: Entities::QueryInputElement, documentation: {
81
+ type: 'QueryInputElement',
82
+ desc: 'Set of configuration',
83
+ param_type: 'body',
84
+ is_array: true,
85
+ required: true
86
+ }
87
+ end
88
+
89
+ class ApiError < Representable::Decorator
90
+ include Representable::JSON
91
+
92
+ property :code, documentation: { type: Integer, desc: 'status code' }
93
+ property :message, documentation: { type: String, desc: 'error message' }
94
+ end
95
+
96
+ module NestedModule
97
+ class ApiResponse < Representable::Decorator
98
+ include Representable::JSON
99
+
100
+ property :status, documentation: { type: String }
101
+ property :error, documentation: { type: ::Entities::ApiError }
102
+ end
103
+ end
104
+
105
+ class SecondApiError < Representable::Decorator
106
+ include Representable::JSON
107
+
108
+ property :code, documentation: { type: Integer }
109
+ property :severity, documentation: { type: String }
110
+ property :message, documentation: { type: String }
111
+ end
112
+
113
+ class ResponseItem < Representable::Decorator
114
+ include Representable::JSON
115
+
116
+ class << self
117
+ def documentation
118
+ {
119
+ id: { type: Integer },
120
+ name: { type: String }
121
+ }
122
+ end
123
+ end
124
+
125
+ property :id, documentation: { type: Integer }
126
+ property :name, documentation: { type: String }
127
+ end
128
+
129
+ class OtherItem < Representable::Decorator
130
+ include Representable::JSON
131
+
132
+ property :key, documentation: { type: Integer }
133
+ property :symbol, documentation: { type: String }
134
+ end
135
+
136
+ class UseResponse < Representable::Decorator
137
+ include Representable::JSON
138
+
139
+ class << self
140
+ def documentation
141
+ {
142
+ :description => { type: String },
143
+ '$responses' => { is_array: true }
144
+ }
145
+ end
146
+ end
147
+
148
+ property :description, documentation: { type: String }
149
+ property :items, as: '$responses', decorator: Entities::ResponseItem, documentation: { is_array: true }
150
+ end
151
+
152
+ class UseItemResponseAsType < Representable::Decorator
153
+ include Representable::JSON
154
+
155
+ property :description, documentation: { type: String }
156
+ property :responses, documentation: { type: Entities::ResponseItem, is_array: false }
157
+ end
158
+
159
+ class UseAddress < Representable::Decorator
160
+ include Representable::JSON
161
+
162
+ property :street, documentation: { type: String, desc: 'street' }
163
+ property :postcode, documentation: { type: String, desc: 'postcode' }
164
+ property :city, documentation: { type: String, desc: 'city' }
165
+ property :country, documentation: { type: String, desc: 'country' }
166
+ end
167
+
168
+ class UseNestedWithAddress < Representable::Decorator
169
+ include Representable::JSON
170
+
171
+ property :name, documentation: { type: String }
172
+ property :address, decorator: Entities::UseAddress
173
+ end
174
+
175
+ class TypedDefinition < Representable::Decorator
176
+ include Representable::JSON
177
+
178
+ property :prop_integer, documentation: { type: Integer, desc: 'prop_integer description' }
179
+ property :prop_long, documentation: { type: Numeric, desc: 'prop_long description' }
180
+ property :prop_float, documentation: { type: Float, desc: 'prop_float description' }
181
+ property :prop_double, documentation: { type: BigDecimal, desc: 'prop_double description' }
182
+ property :prop_string, documentation: { type: String, desc: 'prop_string description' }
183
+ property :prop_symbol, documentation: { type: Symbol, desc: 'prop_symbol description' }
184
+ property :prop_date, documentation: { type: Date, desc: 'prop_date description' }
185
+ property :prop_date_time, documentation: { type: DateTime, desc: 'prop_date_time description' }
186
+ property :prop_time, documentation: { type: Time, desc: 'prop_time description' }
187
+ property :prop_password, documentation: { type: 'password', desc: 'prop_password description' }
188
+ property :prop_email, documentation: { type: 'email', desc: 'prop_email description' }
189
+ property :prop_boolean, documentation: { type: Grape::API::Boolean, desc: 'prop_boolean description' }
190
+ property :prop_file, documentation: { type: File, desc: 'prop_file description' }
191
+ property :prop_json, documentation: { type: JSON, desc: 'prop_json description' }
192
+ end
193
+
194
+ class RecursiveModel < Representable::Decorator
195
+ include Representable::JSON
196
+
197
+ property :name, documentation: { type: String, desc: 'The name.' }
198
+ property :children, decorator: self, documentation: { type: 'RecursiveModel', is_array: true, desc: 'The child nodes.' }
199
+ end
200
+
201
+ class DocumentedHashAndArrayModel < Representable::Decorator
202
+ include Representable::JSON
203
+
204
+ property :raw_hash, documentation: { type: Hash, desc: 'Example Hash.' }
205
+ property :raw_array, documentation: { type: Array, desc: 'Example Array' }
206
+ end
207
+ end
208
+ end
209
+
210
+ let(:swagger_definitions_models) do
211
+ {
212
+ 'ApiError' => { 'type' => 'object', 'properties' => { 'code' => { 'description' => 'status code', 'type' => 'integer', 'format' => 'int32' }, 'message' => { 'description' => 'error message', 'type' => 'string' } } },
213
+ 'ResponseItem' => { 'type' => 'object', 'properties' => { 'id' => { 'description' => '', 'type' => 'integer', 'format' => 'int32' }, 'name' => { 'description' => '', 'type' => 'string' } } },
214
+ 'UseResponse' => { 'type' => 'object', 'properties' => { 'description' => { 'description' => '', 'type' => 'string' }, '$responses' => { 'type' => 'array', 'items' => { '$ref' => '#/definitions/ResponseItem' }, 'description' => '' } } },
215
+ 'RecursiveModel' => { 'type' => 'object', 'properties' => { 'name' => { 'type' => 'string', 'description' => 'The name.' }, 'children' => { 'type' => 'array', 'items' => { '$ref' => '#/definitions/RecursiveModel' }, 'description' => 'The child nodes.' } } }
216
+ }
217
+ end
218
+
219
+ let(:swagger_nested_type) do
220
+ {
221
+ 'ApiError' => { 'type' => 'object', 'properties' => { 'code' => { 'description' => 'status code', 'type' => 'integer', 'format' => 'int32' }, 'message' => { 'description' => 'error message', 'type' => 'string' } }, 'description' => 'ApiError model' },
222
+ 'UseItemResponseAsType' => { 'type' => 'object', 'properties' => { 'description' => { 'description' => '', 'type' => 'string' }, 'responses' => { 'description' => '', 'type' => 'ResponseItem' } }, 'description' => 'UseItemResponseAsType model' }
223
+ }
224
+ end
225
+
226
+ let(:swagger_entity_as_response_object) do
227
+ {
228
+ 'ApiError' => { 'type' => 'object', 'properties' => { 'code' => { 'description' => 'status code', 'type' => 'integer', 'format' => 'int32' }, 'message' => { 'description' => 'error message', 'type' => 'string' } }, 'description' => 'ApiError model' },
229
+ 'ResponseItem' => { 'type' => 'object', 'properties' => { 'id' => { 'description' => '', 'type' => 'integer', 'format' => 'int32' }, 'name' => { 'description' => '', 'type' => 'string' } } },
230
+ 'UseResponse' => { 'type' => 'object', 'properties' => { 'description' => { 'description' => '', 'type' => 'string' }, '$responses' => { 'type' => 'array', 'items' => { '$ref' => '#/definitions/ResponseItem' }, 'description' => '' } }, 'description' => 'UseResponse model' }
231
+ }
232
+ end
233
+
234
+ let(:swagger_params_as_response_object) do
235
+ {
236
+ 'ApiError' => { 'type' => 'object', 'properties' => { 'code' => { 'description' => 'status code', 'type' => 'integer', 'format' => 'int32' }, 'message' => { 'description' => 'error message', 'type' => 'string' } }, 'description' => 'ApiError model' }
237
+ }
238
+ end
239
+
240
+ let(:swagger_typed_defintion) do
241
+ {
242
+ 'prop_boolean' => { 'description' => 'prop_boolean description', 'type' => 'boolean' },
243
+ 'prop_date' => { 'description' => 'prop_date description', 'type' => 'string', 'format' => 'date' },
244
+ 'prop_date_time' => { 'description' => 'prop_date_time description', 'type' => 'string', 'format' => 'date-time' },
245
+ 'prop_double' => { 'description' => 'prop_double description', 'type' => 'number', 'format' => 'double' },
246
+ 'prop_email' => { 'description' => 'prop_email description', 'type' => 'string', 'format' => 'email' },
247
+ 'prop_file' => { 'description' => 'prop_file description', 'type' => 'file' },
248
+ 'prop_float' => { 'description' => 'prop_float description', 'type' => 'number', 'format' => 'float' },
249
+ 'prop_integer' => { 'description' => 'prop_integer description', 'type' => 'integer', 'format' => 'int32' },
250
+ 'prop_json' => { 'description' => 'prop_json description', 'type' => 'JSON' },
251
+ 'prop_long' => { 'description' => 'prop_long description', 'type' => 'integer', 'format' => 'int64' },
252
+ 'prop_password' => { 'description' => 'prop_password description', 'type' => 'string', 'format' => 'password' },
253
+ 'prop_string' => { 'description' => 'prop_string description', 'type' => 'string' },
254
+ 'prop_symbol' => { 'description' => 'prop_symbol description', 'type' => 'string' },
255
+ 'prop_time' => { 'description' => 'prop_time description', 'type' => 'string', 'format' => 'date-time' }
256
+ }
257
+ end
258
+
259
+ let(:swagger_json) do
260
+ {
261
+ 'info' => {
262
+ 'title' => 'The API title to be displayed on the API homepage.',
263
+ 'description' => 'A description of the API.',
264
+ 'termsOfService' => 'www.The-URL-of-the-terms-and-service.com',
265
+ 'contact' => { 'name' => 'Contact name', 'email' => 'Contact@email.com', 'url' => 'Contact URL' },
266
+ 'license' => { 'name' => 'The name of the license.', 'url' => 'www.The-URL-of-the-license.org' },
267
+ 'version' => '0.0.1'
268
+ },
269
+ 'swagger' => '2.0',
270
+ 'produces' => ['application/json'],
271
+ 'host' => 'example.org',
272
+ 'basePath' => '/api',
273
+ 'tags' => [
274
+ { 'name' => 'other_thing', 'description' => 'Operations about other_things' },
275
+ { 'name' => 'thing', 'description' => 'Operations about things' },
276
+ { 'name' => 'thing2', 'description' => 'Operations about thing2s' },
277
+ { 'name' => 'dummy', 'description' => 'Operations about dummies' }
278
+ ],
279
+ 'paths' => {
280
+ '/v3/other_thing/{elements}' => {
281
+ 'get' => {
282
+ 'description' => 'nested route inside namespace',
283
+ 'produces' => ['application/json'],
284
+ 'parameters' => [{ 'in' => 'body', 'name' => 'elements', 'description' => 'Set of configuration', 'type' => 'array', 'items' => { 'type' => 'string' }, 'required' => true }],
285
+ 'responses' => { '200' => { 'description' => 'nested route inside namespace', 'schema' => { '$ref' => '#/definitions/QueryInput' } } },
286
+ 'tags' => ['other_thing'],
287
+ 'operationId' => 'getV3OtherThingElements',
288
+ 'x-amazon-apigateway-auth' => { 'type' => 'none' },
289
+ 'x-amazon-apigateway-integration' => { 'type' => 'aws', 'uri' => 'foo_bar_uri', 'httpMethod' => 'get' }
290
+ }
291
+ },
292
+ '/thing' => {
293
+ 'get' => {
294
+ 'description' => 'This gets Things.',
295
+ 'produces' => ['application/json'],
296
+ 'parameters' => [
297
+ { 'in' => 'query', 'name' => 'id', 'description' => 'Identity of Something', 'type' => 'integer', 'format' => 'int32', 'required' => false },
298
+ { 'in' => 'query', 'name' => 'text', 'description' => 'Content of something.', 'type' => 'string', 'required' => false },
299
+ { 'in' => 'formData', 'name' => 'links', 'type' => 'array', 'items' => { 'type' => 'link' }, 'required' => false },
300
+ { 'in' => 'query', 'name' => 'others', 'type' => 'text', 'required' => false }
301
+ ],
302
+ 'responses' => { '200' => { 'description' => 'This gets Things.' }, '401' => { 'description' => 'Unauthorized', 'schema' => { '$ref' => '#/definitions/ApiError' } } },
303
+ 'tags' => ['thing'],
304
+ 'operationId' => 'getThing'
305
+ },
306
+ 'post' => {
307
+ 'description' => 'This creates Thing.',
308
+ 'produces' => ['application/json'],
309
+ 'consumes' => ['application/x-www-form-urlencoded'],
310
+ 'parameters' => [
311
+ { 'in' => 'formData', 'name' => 'text', 'description' => 'Content of something.', 'type' => 'string', 'required' => true },
312
+ { 'in' => 'formData', 'name' => 'links', 'type' => 'array', 'items' => { 'type' => 'string' }, 'required' => true }
313
+ ],
314
+ 'responses' => { '201' => { 'description' => 'This creates Thing.', 'schema' => { '$ref' => '#/definitions/Something' } }, '422' => { 'description' => 'Unprocessible Entity' } },
315
+ 'tags' => ['thing'],
316
+ 'operationId' => 'postThing'
317
+ }
318
+ },
319
+ '/thing/{id}' => {
320
+ 'get' => {
321
+ 'description' => 'This gets Thing.',
322
+ 'produces' => ['application/json'],
323
+ 'parameters' => [{ 'in' => 'path', 'name' => 'id', 'type' => 'integer', 'format' => 'int32', 'required' => true }],
324
+ 'responses' => { '200' => { 'description' => 'getting a single thing' }, '401' => { 'description' => 'Unauthorized' } },
325
+ 'tags' => ['thing'],
326
+ 'operationId' => 'getThingId'
327
+ },
328
+ 'put' => {
329
+ 'description' => 'This updates Thing.',
330
+ 'produces' => ['application/json'],
331
+ 'consumes' => ['application/x-www-form-urlencoded'],
332
+ 'parameters' => [
333
+ { 'in' => 'path', 'name' => 'id', 'type' => 'integer', 'format' => 'int32', 'required' => true },
334
+ { 'in' => 'formData', 'name' => 'text', 'description' => 'Content of something.', 'type' => 'string', 'required' => false },
335
+ { 'in' => 'formData', 'name' => 'links', 'type' => 'array', 'items' => { 'type' => 'string' }, 'required' => false }
336
+ ],
337
+ 'responses' => { '200' => { 'description' => 'This updates Thing.', 'schema' => { '$ref' => '#/definitions/Something' } } },
338
+ 'tags' => ['thing'],
339
+ 'operationId' => 'putThingId'
340
+ },
341
+ 'delete' => {
342
+ 'description' => 'This deletes Thing.',
343
+ 'produces' => ['application/json'],
344
+ 'parameters' => [{ 'in' => 'path', 'name' => 'id', 'type' => 'integer', 'format' => 'int32', 'required' => true }],
345
+ 'responses' => { '200' => { 'description' => 'This deletes Thing.', 'schema' => { '$ref' => '#/definitions/Something' } } },
346
+ 'tags' => ['thing'],
347
+ 'operationId' => 'deleteThingId'
348
+ }
349
+ },
350
+ '/thing2' => {
351
+ 'get' => {
352
+ 'description' => 'This gets Things.',
353
+ 'produces' => ['application/json'],
354
+ 'responses' => { '200' => { 'description' => 'get Horses', 'schema' => { '$ref' => '#/definitions/Something' } }, '401' => { 'description' => 'HorsesOutError', 'schema' => { '$ref' => '#/definitions/ApiError' } } },
355
+ 'tags' => ['thing2'],
356
+ 'operationId' => 'getThing2'
357
+ }
358
+ },
359
+ '/dummy/{id}' => {
360
+ 'delete' => {
361
+ 'description' => 'dummy route.',
362
+ 'produces' => ['application/json'],
363
+ 'parameters' => [{ 'in' => 'path', 'name' => 'id', 'type' => 'integer', 'format' => 'int32', 'required' => true }],
364
+ 'responses' => { '204' => { 'description' => 'dummy route.' }, '401' => { 'description' => 'Unauthorized' } },
365
+ 'tags' => ['dummy'],
366
+ 'operationId' => 'deleteDummyId'
367
+ }
368
+ }
369
+ },
370
+ 'definitions' => {
371
+ 'QueryInput' => {
372
+ 'type' => 'object',
373
+ 'required' => ['elements'],
374
+ 'properties' => { 'elements' => { 'type' => 'array', 'items' => { '$ref' => '#/definitions/QueryInputElement' }, 'description' => 'Set of configuration' } },
375
+ 'description' => 'QueryInput model'
376
+ },
377
+ 'QueryInputElement' => {
378
+ 'type' => 'object',
379
+ 'required' => %w[key value],
380
+ 'properties' => { 'key' => { 'type' => 'string', 'description' => 'Name of parameter' }, 'value' => { 'type' => 'string', 'description' => 'Value of parameter' } }
381
+ },
382
+ 'ApiError' => {
383
+ 'type' => 'object',
384
+ 'properties' => { 'code' => { 'type' => 'integer', 'format' => 'int32', 'description' => 'status code' }, 'message' => { 'type' => 'string', 'description' => 'error message' } },
385
+ 'description' => 'ApiError model'
386
+ },
387
+ 'Something' => {
388
+ 'type' => 'object',
389
+ 'properties' => {
390
+ 'id' => { 'type' => 'integer', 'format' => 'int32', 'description' => 'Identity of Something' },
391
+ 'text' => { 'type' => 'string', 'description' => 'Content of something.' },
392
+ 'links' => { 'type' => 'array', 'items' => { 'description' => '', 'type' => 'link' } },
393
+ 'others' => { 'description' => '', 'type' => 'text' }
394
+ },
395
+ 'description' => 'Something model'
396
+ }
397
+ }
398
+ }
399
+ end
400
+
401
+ let(:http_verbs) { %w[get post put delete] }
402
+ end
403
+
404
+ def mounted_paths
405
+ %w[/thing /other_thing /dummy]
406
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_context 'namespace example' do
4
+ before :all do
5
+ module TheApi
6
+ # rubocop:disable Lint/EmptyClass
7
+ class CustomType; end
8
+ # rubocop:enable Lint/EmptyClass
9
+
10
+ class NamespaceApi < Grape::API
11
+ namespace :hudson do
12
+ desc 'Document root'
13
+ get '/' do
14
+ { message: 'hi' }
15
+ end
16
+ end
17
+
18
+ namespace :colorado do
19
+ desc 'This gets something.',
20
+ notes: '_test_'
21
+
22
+ get '/simple' do
23
+ { bla: 'something' }
24
+ end
25
+ end
26
+
27
+ namespace :colorado do
28
+ desc 'This gets something for URL using - separator.',
29
+ notes: '_test_'
30
+
31
+ get '/simple-test' do
32
+ { bla: 'something' }
33
+ end
34
+ end
35
+
36
+ namespace :thames do
37
+ desc 'this gets something else',
38
+ headers: {
39
+ 'XAuthToken' => { description: 'A required header.', required: true },
40
+ 'XOtherHeader' => { description: 'An optional header.', required: false }
41
+ },
42
+ http_codes: [
43
+ { code: 403, message: 'invalid pony' },
44
+ { code: 405, message: 'no ponies left!' }
45
+ ]
46
+
47
+ get '/simple_with_headers' do
48
+ { bla: 'something_else' }
49
+ end
50
+ end
51
+
52
+ namespace :niles do
53
+ desc 'this takes an array of parameters',
54
+ params: {
55
+ 'items[]' => { description: 'array of items', is_array: true }
56
+ }
57
+
58
+ post '/items' do
59
+ {}
60
+ end
61
+ end
62
+
63
+ namespace :niles do
64
+ desc 'this uses a custom parameter',
65
+ params: {
66
+ 'custom' => { type: CustomType, description: 'array of items', is_array: true }
67
+ }
68
+
69
+ get '/custom' do
70
+ {}
71
+ end
72
+ end
73
+ end
74
+
75
+ class ParentLessNamespaceApi < Grape::API
76
+ route_param :animal do
77
+ route_param :breed do
78
+ resource :queues do
79
+ route_param :queue_id do
80
+ resource :reservations do
81
+ desc 'Lists all reservations specific type of animal of specific breed in specific queue'
82
+ get do
83
+ { bla: 'Bla Black' }
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_context 'the api paths/defs' do
4
+ let(:paths) do
5
+ {
6
+ '/in_body' => {
7
+ post: {
8
+ produces: ['application/json'],
9
+ consumes: ['application/json'],
10
+ parameters: [
11
+ { in: 'body', name: 'in_body_1', description: 'in_body_1', type: 'integer', format: 'int32', required: true, example: 23 },
12
+ { in: 'body', name: 'in_body_2', description: 'in_body_2', type: 'string', required: false },
13
+ { in: 'body', name: 'in_body_3', description: 'in_body_3', type: 'string', required: false }
14
+ ],
15
+ responses: { 201 => { description: 'post in body /wo entity', schema: { '$ref' => '#/definitions/InBody' } } },
16
+ tags: ['in_body'],
17
+ operationId: 'postInBody'
18
+ },
19
+ get: {
20
+ produces: ['application/json'],
21
+ responses: { 200 => { description: 'get in path /wo entity', schema: { '$ref' => '#/definitions/InBody' } } },
22
+ tags: ['in_body'],
23
+ operationId: 'getInBody'
24
+ }
25
+ },
26
+ '/in_body/{key}' => {
27
+ put: {
28
+ produces: ['application/json'],
29
+ consumes: ['application/json'],
30
+ parameters: [
31
+ { in: 'path', name: 'key', description: nil, type: 'integer', format: 'int32', required: true },
32
+ { in: 'body', name: 'in_body_1', description: 'in_body_1', type: 'integer', format: 'int32', required: true },
33
+ { in: 'body', name: 'in_body_2', description: 'in_body_2', type: 'string', required: false },
34
+ { in: 'body', name: 'in_body_3', description: 'in_body_3', type: 'string', required: false, example: 'my example string' }
35
+ ],
36
+ responses: { 200 => { description: 'put in body /wo entity', schema: { '$ref' => '#/definitions/InBody' } } },
37
+ tags: ['in_body'],
38
+ operationId: 'putInBodyKey'
39
+ },
40
+ get: {
41
+ produces: ['application/json'],
42
+ parameters: [
43
+ { in: 'path', name: 'key', description: nil, type: 'integer', format: 'int32', required: true }
44
+ ],
45
+ responses: { 200 => { description: 'get in path /wo entity', schema: { '$ref' => '#/definitions/InBody' } } },
46
+ tags: ['in_body'],
47
+ operationId: 'getInBodyKey'
48
+ }
49
+ }
50
+ }
51
+ end
52
+
53
+ let(:found_path) do
54
+ {
55
+ post: {
56
+ produces: ['application/json'],
57
+ consumes: ['application/json'],
58
+ parameters: [
59
+ { in: 'body', name: 'in_body_1', description: 'in_body_1', type: 'integer', format: 'int32', required: true },
60
+ { in: 'body', name: 'in_body_2', description: 'in_body_2', type: 'string', required: false },
61
+ { in: 'body', name: 'in_body_3', description: 'in_body_3', type: 'string', required: false }
62
+ ],
63
+ responses: { 201 => { description: 'post in body /wo entity', schema: { '$ref' => '#/definitions/InBody' } } },
64
+ tags: ['in_body'],
65
+ operationId: 'postInBody'
66
+ }
67
+ }
68
+ end
69
+
70
+ let(:definitions) do
71
+ {
72
+ 'InBody' => {
73
+ type: 'object',
74
+ properties: {
75
+ in_body_1: { type: 'integer', format: 'int32' },
76
+ in_body_2: { type: 'string' },
77
+ in_body_3: { type: 'string' },
78
+ key: { type: 'integer', format: 'int32' }
79
+ }
80
+ }
81
+ }
82
+ end
83
+
84
+ let(:expected_post_defs) do
85
+ {
86
+ type: 'object',
87
+ properties: {
88
+ in_body_1: { type: 'integer', format: 'int32', description: 'in_body_1', example: 23 },
89
+ in_body_2: { type: 'string', description: 'in_body_2' },
90
+ in_body_3: { type: 'string', description: 'in_body_3' }
91
+ },
92
+ required: [:in_body_1]
93
+ }
94
+ end
95
+
96
+ let(:expected_put_defs) do
97
+ {
98
+ type: 'object',
99
+ properties: {
100
+ in_body_1: { type: 'integer', format: 'int32', description: 'in_body_1' },
101
+ in_body_2: { type: 'string', description: 'in_body_2' },
102
+ in_body_3: { type: 'string', description: 'in_body_3', example: 'my example string' }
103
+ },
104
+ required: [:in_body_1]
105
+ }
106
+ end
107
+
108
+ let(:expected_path) { [] }
109
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe 'API with additional options' do
6
+ let(:api) do
7
+ Class.new(Grape::API) do
8
+ add_swagger_documentation \
9
+ api_documentation: { desc: 'Swagger compatible API description' },
10
+ specific_api_documentation: { desc: 'Swagger compatible API description for specific API' }
11
+ end
12
+ end
13
+
14
+ subject do
15
+ api.routes.map do |route|
16
+ route.settings[:description]
17
+ end
18
+ end
19
+
20
+ it 'documents api' do
21
+ expect(subject).to eq(
22
+ [
23
+ { description: 'Swagger compatible API description' },
24
+ {
25
+ description: 'Swagger compatible API description for specific API',
26
+ params: {
27
+ 'locale' => {
28
+ desc: 'Locale of API documentation',
29
+ required: false,
30
+ type: 'Symbol'
31
+ },
32
+ 'name' => {
33
+ desc: 'Resource name of mounted API',
34
+ required: true,
35
+ type: 'String'
36
+ }
37
+ }
38
+ }
39
+ ]
40
+ )
41
+ end
42
+ end