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,190 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GrapeSwagger
4
+ module DocMethods
5
+ class ParseParams
6
+ class << self
7
+ def call(param, settings, path, route, definitions, consumes)
8
+ method = route.request_method
9
+ additional_documentation = settings.fetch(:documentation, {})
10
+ settings.merge!(additional_documentation)
11
+ data_type = DataType.call(settings)
12
+
13
+ value_type = settings.merge(data_type: data_type, path: path, param_name: param, method: method)
14
+
15
+ # required properties
16
+ @parsed_param = {
17
+ in: param_type(value_type, consumes),
18
+ name: settings[:full_name] || param
19
+ }
20
+
21
+ # optional properties
22
+ document_description(settings)
23
+ document_type_and_format(settings, data_type)
24
+ document_array_param(value_type, definitions, consumes) if value_type[:is_array]
25
+ document_default_value(settings) unless value_type[:is_array]
26
+ document_range_values(settings) unless value_type[:is_array]
27
+ document_required(settings)
28
+ document_additional_properties(definitions, settings) unless value_type[:is_array]
29
+ document_add_extensions(settings)
30
+ document_example(settings) if @parsed_param[:in] == 'body'
31
+
32
+ @parsed_param
33
+ end
34
+
35
+ private
36
+
37
+ def document_description(settings)
38
+ description = settings[:desc] || settings[:description]
39
+ @parsed_param[:description] = description if description
40
+ end
41
+
42
+ def document_required(settings)
43
+ @parsed_param[:required] = settings[:required] || false
44
+ @parsed_param[:required] = true if @parsed_param[:in] == 'path'
45
+ end
46
+
47
+ def document_range_values(settings)
48
+ values = settings[:values] || nil
49
+ enum_or_range_values = parse_enum_or_range_values(values)
50
+ @parsed_param.merge!(enum_or_range_values) if enum_or_range_values
51
+ end
52
+
53
+ def document_default_value(settings)
54
+ @parsed_param[:default] = settings[:default] if settings.key?(:default)
55
+ end
56
+
57
+ def document_type_and_format(settings, data_type)
58
+ if DataType.primitive?(data_type)
59
+ data = DataType.mapping(data_type)
60
+ @parsed_param[:type], @parsed_param[:format] = data
61
+ else
62
+ @parsed_param[:type] = data_type
63
+ end
64
+ @parsed_param[:format] = settings[:format] if settings[:format].present?
65
+ end
66
+
67
+ def document_add_extensions(settings)
68
+ GrapeSwagger::DocMethods::Extensions.add_extensions_to_root(settings, @parsed_param)
69
+ end
70
+
71
+ def document_array_param(value_type, definitions, consumes)
72
+ if value_type[:documentation].present?
73
+ param_type = value_type[:documentation][:param_type]
74
+ doc_type = value_type[:documentation][:type]
75
+ type = DataType.mapping(doc_type) if doc_type && !DataType.request_primitive?(doc_type)
76
+ collection_format = value_type[:documentation][:collectionFormat]
77
+ end
78
+
79
+ array_items = parse_array_item(
80
+ definitions,
81
+ type,
82
+ value_type
83
+ )
84
+
85
+ @parsed_param[:in] = array_param_type(value_type, consumes)
86
+ @parsed_param[:items] = array_items
87
+ @parsed_param[:type] = 'array'
88
+ @parsed_param[:collectionFormat] = collection_format if DataType.collections.include?(collection_format)
89
+ end
90
+
91
+ def array_param_type(value_type, consumes)
92
+ param_type = value_type[:param_type] || value_type[:in]
93
+ if param_type
94
+ param_type
95
+ elsif %w[POST PUT PATCH].include?(value_type[:method])
96
+ consumes.include?('application/x-www-form-urlencoded') || consumes.include?('multipart/form-data') ? 'formData' : 'body'
97
+ elsif DataType.query_array_primitive?(value_type[:data_type])
98
+ 'query'
99
+ else
100
+ 'formData'
101
+ end
102
+ end
103
+
104
+ def parse_array_item(definitions, type, value_type)
105
+ array_items = {}
106
+ if definitions[value_type[:data_type]]
107
+ array_items['$ref'] = "#/definitions/#{@parsed_param[:type]}"
108
+ else
109
+ array_items[:type] = type || @parsed_param[:type] == 'array' ? 'string' : @parsed_param[:type]
110
+ end
111
+ array_items[:format] = @parsed_param.delete(:format) if @parsed_param[:format]
112
+
113
+ values = value_type[:values] || nil
114
+ enum_or_range_values = parse_enum_or_range_values(values)
115
+ array_items.merge!(enum_or_range_values) if enum_or_range_values
116
+
117
+ array_items[:default] = value_type[:default] if value_type[:default].present?
118
+
119
+ set_additional_properties, additional_properties = parse_additional_properties(definitions, value_type)
120
+ array_items[:additionalProperties] = additional_properties if set_additional_properties
121
+
122
+ array_items
123
+ end
124
+
125
+ def document_additional_properties(definitions, settings)
126
+ set_additional_properties, additional_properties = parse_additional_properties(definitions, settings)
127
+ @parsed_param[:additionalProperties] = additional_properties if set_additional_properties
128
+ end
129
+
130
+ def parse_additional_properties(definitions, settings)
131
+ return false unless settings.key?(:additionalProperties) || settings.key?(:additional_properties)
132
+
133
+ value =
134
+ if settings.key?(:additionalProperties)
135
+ GrapeSwagger::Errors::SwaggerSpecDeprecated.tell!(:additionalProperties)
136
+ settings[:additionalProperties]
137
+ else
138
+ settings[:additional_properties]
139
+ end
140
+
141
+ parsed_value =
142
+ if definitions[value.to_s]
143
+ { '$ref': "#/definitions/#{value}" }
144
+ elsif value.is_a?(Class)
145
+ { type: DataType.call(value) }
146
+ else
147
+ value
148
+ end
149
+
150
+ [true, parsed_value]
151
+ end
152
+
153
+ def document_example(settings)
154
+ example = settings[:example]
155
+ @parsed_param[:example] = example if example
156
+ end
157
+
158
+ def param_type(value_type, consumes)
159
+ param_type = value_type[:param_type] || value_type[:in]
160
+ if value_type[:path].include?("{#{value_type[:param_name]}}")
161
+ 'path'
162
+ elsif param_type
163
+ param_type
164
+ elsif %w[POST PUT PATCH].include?(value_type[:method])
165
+ consumes.include?('application/x-www-form-urlencoded') || consumes.include?('multipart/form-data') ? 'formData' : 'body'
166
+ else
167
+ 'query'
168
+ end
169
+ end
170
+
171
+ def parse_enum_or_range_values(values)
172
+ case values
173
+ when Proc
174
+ parse_enum_or_range_values(values.call) if values.parameters.empty?
175
+ when Range
176
+ parse_range_values(values) if values.first.is_a?(Integer)
177
+ when Array
178
+ { enum: values }
179
+ else
180
+ { enum: [values] } if values
181
+ end
182
+ end
183
+
184
+ def parse_range_values(values)
185
+ { minimum: values.first, maximum: values.last }
186
+ end
187
+ end
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GrapeSwagger
4
+ module DocMethods
5
+ class PathString
6
+ class << self
7
+ def build(route, path, options = {})
8
+ # always removing format
9
+ path.sub!(/\(\.\w+?\)$/, '')
10
+ path.sub!('(.:format)', '')
11
+
12
+ # ... format path params
13
+ path.gsub!(/:(\w+)/, '{\1}')
14
+ path.gsub!(/\*(\w+)/, '{\1}')
15
+
16
+ # set item from path, this could be used for the definitions object
17
+ path_name = path.gsub(%r{/{(.+?)}}, '').split('/').last
18
+ item = path_name.present? ? path_name.singularize.underscore.camelize : 'Item'
19
+
20
+ if route.version && options[:add_version]
21
+ version = GrapeSwagger::DocMethods::Version.get(route)
22
+ version = version.first while version.is_a?(Array)
23
+ path.sub!('{version}', version.to_s)
24
+ else
25
+ path.sub!('/{version}', '')
26
+ end
27
+
28
+ path = "#{OptionalObject.build(:base_path, options)}#{path}" if options[:add_base_path]
29
+
30
+ [item, path.start_with?('/') ? path : "/#{path}"]
31
+ end
32
+
33
+ def generate_optional_segments(path)
34
+ # always removing format
35
+ path.sub!(/\(\.\w+?\)$/, '')
36
+ path.sub!('(.:format)', '')
37
+
38
+ paths = []
39
+ if path.match(/\(.+\)/)
40
+ # recurse with included optional segment
41
+ paths.concat(generate_optional_segments(path.sub(/\([^\)]+\)/, '')))
42
+ # recurse with excluded optional segment
43
+ paths.concat(generate_optional_segments(path.sub(/\(/, '').sub(/\)/, '')))
44
+ else
45
+ paths << path
46
+ end
47
+ paths
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GrapeSwagger
4
+ module DocMethods
5
+ class ProducesConsumes
6
+ class << self
7
+ def call(*args)
8
+ return ['application/json'] unless args.flatten.present?
9
+
10
+ args.flatten.map { |x| Grape::ContentTypes::CONTENT_TYPES[x] || x }.uniq
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GrapeSwagger
4
+ module DocMethods
5
+ class StatusCodes
6
+ class << self
7
+ def get
8
+ {
9
+ get: { code: 200, message: 'get {item}(s)' },
10
+ post: { code: 201, message: 'created {item}' },
11
+ put: { code: 200, message: 'updated {item}' },
12
+ patch: { code: 200, message: 'patched {item}' },
13
+ delete: { code: 200, message: 'deleted {item}' },
14
+ head: { code: 200, message: 'head {item}' },
15
+ options: { code: 200, message: 'option {item}' }
16
+ }
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GrapeSwagger
4
+ module DocMethods
5
+ class TagNameDescription
6
+ class << self
7
+ def build(paths)
8
+ paths.values.each_with_object([]) do |path, memo|
9
+ tags = path.values.first[:tags]
10
+ next if tags.nil?
11
+
12
+ case tags
13
+ when String
14
+ memo << build_memo(tags)
15
+ when Array
16
+ path.values.first[:tags].each do |tag|
17
+ memo << build_memo(tag)
18
+ end
19
+ end
20
+ end.uniq
21
+ end
22
+
23
+ private
24
+
25
+ def build_memo(tag)
26
+ {
27
+ name: tag,
28
+ description: "Operations about #{tag.pluralize}"
29
+ }
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GrapeSwagger
4
+ module DocMethods
5
+ class Version
6
+ class << self
7
+ def get(route)
8
+ version = route.version
9
+ # for grape version 0.16.2, the version can be a string like '[:v1, :v2]'
10
+ # for grape version bigger than 0.16.2, the version can be a array like [:v1, :v2]
11
+ if version.is_a?(String) && version.start_with?('[') && version.end_with?(']')
12
+ instance_eval(version)
13
+ else
14
+ version
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'grape-swagger/doc_methods/status_codes'
4
+ require 'grape-swagger/doc_methods/produces_consumes'
5
+ require 'grape-swagger/doc_methods/data_type'
6
+ require 'grape-swagger/doc_methods/extensions'
7
+ require 'grape-swagger/doc_methods/format_data'
8
+ require 'grape-swagger/doc_methods/operation_id'
9
+ require 'grape-swagger/doc_methods/optional_object'
10
+ require 'grape-swagger/doc_methods/path_string'
11
+ require 'grape-swagger/doc_methods/tag_name_description'
12
+ require 'grape-swagger/doc_methods/parse_params'
13
+ require 'grape-swagger/doc_methods/move_params'
14
+ require 'grape-swagger/doc_methods/headers'
15
+ require 'grape-swagger/doc_methods/build_model_definition'
16
+ require 'grape-swagger/doc_methods/version'
17
+ require 'grape-swagger/doc_methods/file_params'
18
+
19
+ module GrapeSwagger
20
+ module DocMethods
21
+ DEFAULTS =
22
+ {
23
+ info: {},
24
+ models: [],
25
+ doc_version: '0.0.1',
26
+ target_class: nil,
27
+ mount_path: '/swagger_doc',
28
+ host: nil,
29
+ base_path: nil,
30
+ add_base_path: false,
31
+ add_version: true,
32
+ add_root: false,
33
+ hide_documentation_path: true,
34
+ format: :json,
35
+ authorizations: nil,
36
+ security_definitions: nil,
37
+ security: nil,
38
+ api_documentation: { desc: 'Swagger compatible API description' },
39
+ specific_api_documentation: { desc: 'Swagger compatible API description for specific API' },
40
+ endpoint_auth_wrapper: nil,
41
+ swagger_endpoint_guard: nil,
42
+ token_owner: nil
43
+ }.freeze
44
+
45
+ FORMATTER_METHOD = %i[format default_format default_error_formatter].freeze
46
+
47
+ def self.output_path_definitions(combi_routes, endpoint, target_class, options)
48
+ output = endpoint.swagger_object(
49
+ target_class,
50
+ endpoint.request,
51
+ options
52
+ )
53
+
54
+ paths, definitions = endpoint.path_and_definition_objects(combi_routes, options)
55
+ tags = tags_from(paths, options)
56
+
57
+ output[:tags] = tags unless tags.empty? || paths.blank?
58
+ output[:paths] = paths unless paths.blank?
59
+ output[:definitions] = definitions unless definitions.blank?
60
+
61
+ output
62
+ end
63
+
64
+ def self.tags_from(paths, options)
65
+ tags = GrapeSwagger::DocMethods::TagNameDescription.build(paths)
66
+
67
+ if options[:tags]
68
+ names = options[:tags].map { |t| t[:name] }
69
+ tags.reject! { |t| names.include?(t[:name]) }
70
+ tags += options[:tags]
71
+ end
72
+
73
+ tags
74
+ end
75
+
76
+ def hide_documentation_path
77
+ @@hide_documentation_path
78
+ end
79
+
80
+ def mount_path
81
+ @@mount_path
82
+ end
83
+
84
+ def setup(options)
85
+ options = DEFAULTS.merge(options)
86
+
87
+ # options could be set on #add_swagger_documentation call,
88
+ # for available options see #defaults
89
+ target_class = options[:target_class]
90
+ guard = options[:swagger_endpoint_guard]
91
+ api_doc = options[:api_documentation].dup
92
+ specific_api_doc = options[:specific_api_documentation].dup
93
+
94
+ class_variables_from(options)
95
+
96
+ setup_formatter(options[:format])
97
+
98
+ desc api_doc.delete(:desc), api_doc
99
+
100
+ instance_eval(guard) unless guard.nil?
101
+
102
+ get mount_path do
103
+ header['Access-Control-Allow-Origin'] = '*'
104
+ header['Access-Control-Request-Method'] = '*'
105
+
106
+ GrapeSwagger::DocMethods
107
+ .output_path_definitions(target_class.combined_namespace_routes, self, target_class, options)
108
+ end
109
+
110
+ desc specific_api_doc.delete(:desc), { params: specific_api_doc.delete(:params) || {}, **specific_api_doc }
111
+
112
+ params do
113
+ requires :name, type: String, desc: 'Resource name of mounted API'
114
+ optional :locale, type: Symbol, desc: 'Locale of API documentation'
115
+ end
116
+
117
+ instance_eval(guard) unless guard.nil?
118
+
119
+ get "#{mount_path}/:name" do
120
+ I18n.locale = params[:locale] || I18n.default_locale
121
+
122
+ combined_routes = target_class.combined_namespace_routes[params[:name]]
123
+ error!({ error: 'named resource not exist' }, 400) if combined_routes.nil?
124
+
125
+ GrapeSwagger::DocMethods
126
+ .output_path_definitions({ params[:name] => combined_routes }, self, target_class, options)
127
+ end
128
+ end
129
+
130
+ def class_variables_from(options)
131
+ @@mount_path = options[:mount_path]
132
+ @@class_name = options[:class_name] || options[:mount_path].delete('/')
133
+ @@hide_documentation_path = options[:hide_documentation_path]
134
+ end
135
+
136
+ def setup_formatter(formatter)
137
+ return unless formatter
138
+
139
+ FORMATTER_METHOD.each { |method| send(method, formatter) }
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GrapeSwagger
4
+ module Endpoint
5
+ class ParamsParser
6
+ attr_reader :params, :settings, :endpoint
7
+
8
+ def self.parse_request_params(params, settings, endpoint)
9
+ new(params, settings, endpoint).parse_request_params
10
+ end
11
+
12
+ def initialize(params, settings, endpoint)
13
+ @params = params
14
+ @settings = settings
15
+ @endpoint = endpoint
16
+ end
17
+
18
+ def parse_request_params
19
+ public_params.each_with_object({}) do |(name, options), memo|
20
+ name = name.to_s
21
+ param_type = options[:type]
22
+ param_type = param_type.to_s unless param_type.nil?
23
+
24
+ if param_type_is_array?(param_type)
25
+ options[:is_array] = true
26
+ name += '[]' if array_use_braces?
27
+ end
28
+
29
+ memo[name] = options
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def array_use_braces?
36
+ @array_use_braces ||= settings[:array_use_braces] && !includes_body_param?
37
+ end
38
+
39
+ def param_type_is_array?(param_type)
40
+ return false unless param_type
41
+ return true if param_type == 'Array'
42
+
43
+ param_types = param_type.match(/\[(.*)\]$/)
44
+ return false unless param_types
45
+
46
+ param_types = param_types[0].split(',') if param_types
47
+ param_types.size == 1
48
+ end
49
+
50
+ def public_params
51
+ params.select { |param| public_parameter?(param) }
52
+ end
53
+
54
+ def public_parameter?(param)
55
+ param_options = param.last
56
+ return true unless param_options.key?(:documentation) && !param_options[:required]
57
+
58
+ param_hidden = param_options[:documentation].fetch(:hidden, false)
59
+ if param_hidden.is_a?(Proc)
60
+ param_hidden = if settings[:token_owner]
61
+ param_hidden.call(endpoint.send(settings[:token_owner].to_sym))
62
+ else
63
+ param_hidden.call
64
+ end
65
+ end
66
+ !param_hidden
67
+ end
68
+
69
+ def includes_body_param?
70
+ params.any? do |_, options|
71
+ options.dig(:documentation, :param_type) == 'body' || options.dig(:documentation, :in) == 'body'
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end