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.
- 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
|