gitlab-grape-swagger 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.github/dependabot.yml +20 -0
- data/.github/workflows/ci.yml +45 -0
- data/.gitignore +44 -0
- data/.gitlab-ci.yml +19 -0
- data/.rspec +3 -0
- data/.rubocop.yml +136 -0
- data/.rubocop_todo.yml +60 -0
- data/.ruby-gemset +1 -0
- data/CHANGELOG.md +671 -0
- data/CONTRIBUTING.md +126 -0
- data/Dangerfile +3 -0
- data/Gemfile +45 -0
- data/Gemfile.lock +249 -0
- data/LICENSE.txt +20 -0
- data/README.md +1772 -0
- data/RELEASING.md +82 -0
- data/Rakefile +20 -0
- data/UPGRADING.md +201 -0
- data/example/api/endpoints.rb +131 -0
- data/example/api/entities.rb +18 -0
- data/example/config.ru +42 -0
- data/example/example_requests.postman_collection +146 -0
- data/example/splines.png +0 -0
- data/example/swagger-example.png +0 -0
- data/grape-swagger.gemspec +23 -0
- data/lib/grape-swagger/doc_methods/build_model_definition.rb +68 -0
- data/lib/grape-swagger/doc_methods/data_type.rb +110 -0
- data/lib/grape-swagger/doc_methods/extensions.rb +101 -0
- data/lib/grape-swagger/doc_methods/file_params.rb +17 -0
- data/lib/grape-swagger/doc_methods/format_data.rb +53 -0
- data/lib/grape-swagger/doc_methods/headers.rb +20 -0
- data/lib/grape-swagger/doc_methods/move_params.rb +209 -0
- data/lib/grape-swagger/doc_methods/operation_id.rb +32 -0
- data/lib/grape-swagger/doc_methods/optional_object.rb +30 -0
- data/lib/grape-swagger/doc_methods/parse_params.rb +190 -0
- data/lib/grape-swagger/doc_methods/path_string.rb +52 -0
- data/lib/grape-swagger/doc_methods/produces_consumes.rb +15 -0
- data/lib/grape-swagger/doc_methods/status_codes.rb +21 -0
- data/lib/grape-swagger/doc_methods/tag_name_description.rb +34 -0
- data/lib/grape-swagger/doc_methods/version.rb +20 -0
- data/lib/grape-swagger/doc_methods.rb +142 -0
- data/lib/grape-swagger/endpoint/params_parser.rb +76 -0
- data/lib/grape-swagger/endpoint.rb +476 -0
- data/lib/grape-swagger/errors.rb +17 -0
- data/lib/grape-swagger/instance.rb +7 -0
- data/lib/grape-swagger/model_parsers.rb +42 -0
- data/lib/grape-swagger/rake/oapi_tasks.rb +135 -0
- data/lib/grape-swagger/version.rb +5 -0
- data/lib/grape-swagger.rb +174 -0
- data/spec/issues/267_nested_namespaces.rb +55 -0
- data/spec/issues/403_versions_spec.rb +124 -0
- data/spec/issues/427_entity_as_string_spec.rb +39 -0
- data/spec/issues/430_entity_definitions_spec.rb +94 -0
- data/spec/issues/532_allow_custom_format_spec.rb +42 -0
- data/spec/issues/533_specify_status_code_spec.rb +78 -0
- data/spec/issues/537_enum_values_spec.rb +50 -0
- data/spec/issues/539_array_post_body_spec.rb +65 -0
- data/spec/issues/542_array_of_type_in_post_body_spec.rb +46 -0
- data/spec/issues/553_align_array_put_post_params_spec.rb +152 -0
- data/spec/issues/572_array_post_body_spec.rb +51 -0
- data/spec/issues/579_align_put_post_parameters_spec.rb +185 -0
- data/spec/issues/582_file_response_spec.rb +55 -0
- data/spec/issues/587_range_parameter_delimited_by_dash_spec.rb +26 -0
- data/spec/issues/605_root_route_documentation_spec.rb +23 -0
- data/spec/issues/650_params_array_spec.rb +65 -0
- data/spec/issues/677_consumes_produces_add_swagger_documentation_options_spec.rb +100 -0
- data/spec/issues/680_keep_204_error_schemas_spec.rb +55 -0
- data/spec/issues/721_set_default_parameter_location_based_on_consumes_spec.rb +62 -0
- data/spec/issues/751_deeply_nested_objects_spec.rb +190 -0
- data/spec/issues/776_multiple_presents_spec.rb +59 -0
- data/spec/issues/784_extensions_on_params_spec.rb +42 -0
- data/spec/issues/809_utf8_routes_spec.rb +55 -0
- data/spec/issues/832_array_hash_float_decimal_spec.rb +114 -0
- data/spec/issues/847_route_param_options_spec.rb +37 -0
- data/spec/issues/873_wildcard_segments_path_parameters_spec.rb +28 -0
- data/spec/issues/878_optional_path_segments_spec.rb +29 -0
- data/spec/issues/881_handle_file_params_spec.rb +38 -0
- data/spec/issues/883_query_array_parameter_spec.rb +46 -0
- data/spec/issues/884_dont_document_non_schema_examples_spec.rb +49 -0
- data/spec/issues/887_prevent_duplicate_operation_ids_spec.rb +35 -0
- data/spec/lib/data_type_spec.rb +111 -0
- data/spec/lib/endpoint/params_parser_spec.rb +124 -0
- data/spec/lib/endpoint_spec.rb +153 -0
- data/spec/lib/extensions_spec.rb +185 -0
- data/spec/lib/format_data_spec.rb +115 -0
- data/spec/lib/model_parsers_spec.rb +104 -0
- data/spec/lib/move_params_spec.rb +444 -0
- data/spec/lib/oapi_tasks_spec.rb +163 -0
- data/spec/lib/operation_id_spec.rb +55 -0
- data/spec/lib/optional_object_spec.rb +47 -0
- data/spec/lib/parse_params_spec.rb +68 -0
- data/spec/lib/path_string_spec.rb +101 -0
- data/spec/lib/produces_consumes_spec.rb +116 -0
- data/spec/lib/tag_name_description_spec.rb +80 -0
- data/spec/lib/version_spec.rb +28 -0
- data/spec/spec_helper.rb +39 -0
- data/spec/support/empty_model_parser.rb +23 -0
- data/spec/support/grape_version.rb +13 -0
- data/spec/support/mock_parser.rb +23 -0
- data/spec/support/model_parsers/entity_parser.rb +334 -0
- data/spec/support/model_parsers/mock_parser.rb +346 -0
- data/spec/support/model_parsers/representable_parser.rb +406 -0
- data/spec/support/namespace_tags.rb +93 -0
- data/spec/support/the_paths_definitions.rb +109 -0
- data/spec/swagger_v2/api_documentation_spec.rb +42 -0
- data/spec/swagger_v2/api_swagger_v2_additional_properties_spec.rb +83 -0
- data/spec/swagger_v2/api_swagger_v2_body_definitions_spec.rb +48 -0
- data/spec/swagger_v2/api_swagger_v2_definitions-models_spec.rb +36 -0
- data/spec/swagger_v2/api_swagger_v2_detail_spec.rb +79 -0
- data/spec/swagger_v2/api_swagger_v2_extensions_spec.rb +145 -0
- data/spec/swagger_v2/api_swagger_v2_format-content_type_spec.rb +137 -0
- data/spec/swagger_v2/api_swagger_v2_global_configuration_spec.rb +56 -0
- data/spec/swagger_v2/api_swagger_v2_hash_and_array_spec.rb +64 -0
- data/spec/swagger_v2/api_swagger_v2_headers_spec.rb +58 -0
- data/spec/swagger_v2/api_swagger_v2_hide_documentation_path_spec.rb +57 -0
- data/spec/swagger_v2/api_swagger_v2_hide_param_spec.rb +109 -0
- data/spec/swagger_v2/api_swagger_v2_ignore_defaults_spec.rb +48 -0
- data/spec/swagger_v2/api_swagger_v2_mounted_spec.rb +153 -0
- data/spec/swagger_v2/api_swagger_v2_param_type_body_nested_spec.rb +355 -0
- data/spec/swagger_v2/api_swagger_v2_param_type_body_spec.rb +217 -0
- data/spec/swagger_v2/api_swagger_v2_param_type_spec.rb +247 -0
- data/spec/swagger_v2/api_swagger_v2_request_params_fix_spec.rb +80 -0
- data/spec/swagger_v2/api_swagger_v2_response_spec.rb +147 -0
- data/spec/swagger_v2/api_swagger_v2_response_with_examples_spec.rb +135 -0
- data/spec/swagger_v2/api_swagger_v2_response_with_headers_spec.rb +216 -0
- data/spec/swagger_v2/api_swagger_v2_response_with_models_spec.rb +53 -0
- data/spec/swagger_v2/api_swagger_v2_response_with_root_spec.rb +153 -0
- data/spec/swagger_v2/api_swagger_v2_spec.rb +245 -0
- data/spec/swagger_v2/api_swagger_v2_status_codes_spec.rb +93 -0
- data/spec/swagger_v2/api_swagger_v2_type-format_spec.rb +90 -0
- data/spec/swagger_v2/boolean_params_spec.rb +38 -0
- data/spec/swagger_v2/default_api_spec.rb +175 -0
- data/spec/swagger_v2/deprecated_field_spec.rb +25 -0
- data/spec/swagger_v2/description_not_initialized_spec.rb +39 -0
- data/spec/swagger_v2/endpoint_versioned_path_spec.rb +130 -0
- data/spec/swagger_v2/errors_spec.rb +77 -0
- data/spec/swagger_v2/float_api_spec.rb +36 -0
- data/spec/swagger_v2/form_params_spec.rb +76 -0
- data/spec/swagger_v2/grape-swagger_spec.rb +17 -0
- data/spec/swagger_v2/guarded_endpoint_spec.rb +162 -0
- data/spec/swagger_v2/hide_api_spec.rb +147 -0
- data/spec/swagger_v2/host_spec.rb +43 -0
- data/spec/swagger_v2/inheritance_and_discriminator_spec.rb +57 -0
- data/spec/swagger_v2/mount_override_api_spec.rb +58 -0
- data/spec/swagger_v2/mounted_target_class_spec.rb +76 -0
- data/spec/swagger_v2/namespace_tags_prefix_spec.rb +122 -0
- data/spec/swagger_v2/namespace_tags_spec.rb +78 -0
- data/spec/swagger_v2/namespaced_api_spec.rb +121 -0
- data/spec/swagger_v2/nicknamed_api_spec.rb +25 -0
- data/spec/swagger_v2/operation_id_api_spec.rb +27 -0
- data/spec/swagger_v2/param_multi_type_spec.rb +82 -0
- data/spec/swagger_v2/param_type_spec.rb +95 -0
- data/spec/swagger_v2/param_values_spec.rb +180 -0
- data/spec/swagger_v2/params_array_collection_format_spec.rb +105 -0
- data/spec/swagger_v2/params_array_spec.rb +225 -0
- data/spec/swagger_v2/params_example_spec.rb +38 -0
- data/spec/swagger_v2/params_hash_spec.rb +77 -0
- data/spec/swagger_v2/params_nested_spec.rb +92 -0
- data/spec/swagger_v2/parent_less_namespace_spec.rb +32 -0
- data/spec/swagger_v2/reference_entity_spec.rb +129 -0
- data/spec/swagger_v2/security_requirement_spec.rb +46 -0
- data/spec/swagger_v2/simple_mounted_api_spec.rb +332 -0
- data/spec/version_spec.rb +10 -0
- 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
|