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,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GrapeSwagger
|
|
4
|
+
module DocMethods
|
|
5
|
+
class BuildModelDefinition
|
|
6
|
+
class << self
|
|
7
|
+
def build(_model, properties, required, other_def_properties = {})
|
|
8
|
+
definition = { type: 'object', properties: properties }.merge(other_def_properties)
|
|
9
|
+
|
|
10
|
+
definition[:required] = required if required.is_a?(Array) && required.any?
|
|
11
|
+
|
|
12
|
+
definition
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def parse_params_from_model(parsed_response, model, model_name)
|
|
16
|
+
if parsed_response.is_a?(Hash) && parsed_response.keys.first == :allOf
|
|
17
|
+
refs_or_models = parsed_response[:allOf]
|
|
18
|
+
parsed = parse_refs_and_models(refs_or_models, model)
|
|
19
|
+
|
|
20
|
+
{
|
|
21
|
+
allOf: parsed
|
|
22
|
+
}
|
|
23
|
+
else
|
|
24
|
+
properties, required = parsed_response
|
|
25
|
+
unless properties&.any?
|
|
26
|
+
raise GrapeSwagger::Errors::SwaggerSpec,
|
|
27
|
+
"Empty model #{model_name}, swagger 2.0 doesn't support empty definitions."
|
|
28
|
+
end
|
|
29
|
+
properties, other_def_properties = parse_properties(properties)
|
|
30
|
+
|
|
31
|
+
build(
|
|
32
|
+
model, properties, required, other_def_properties
|
|
33
|
+
)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def parse_properties(properties)
|
|
38
|
+
other_properties = {}
|
|
39
|
+
|
|
40
|
+
discriminator_key, discriminator_value =
|
|
41
|
+
properties.find do |_key, value|
|
|
42
|
+
value[:documentation].try(:[], :is_discriminator)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
if discriminator_key
|
|
46
|
+
discriminator_value.delete(:documentation)
|
|
47
|
+
properties[discriminator_key] = discriminator_value
|
|
48
|
+
|
|
49
|
+
other_properties[:discriminator] = discriminator_key
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
[properties, other_properties]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def parse_refs_and_models(refs_or_models, model)
|
|
56
|
+
refs_or_models.map do |ref_or_models|
|
|
57
|
+
if ref_or_models.is_a?(Hash) && ref_or_models.keys.first == '$ref'
|
|
58
|
+
ref_or_models
|
|
59
|
+
else
|
|
60
|
+
properties, required = ref_or_models
|
|
61
|
+
GrapeSwagger::DocMethods::BuildModelDefinition.build(model, properties, required)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GrapeSwagger
|
|
4
|
+
module DocMethods
|
|
5
|
+
class DataType
|
|
6
|
+
class << self
|
|
7
|
+
def call(value)
|
|
8
|
+
raw_data_type = value.is_a?(Hash) ? value[:type] : value
|
|
9
|
+
raw_data_type ||= 'String'
|
|
10
|
+
raw_data_type = parse_multi_type(raw_data_type)
|
|
11
|
+
|
|
12
|
+
case raw_data_type.to_s
|
|
13
|
+
when 'Boolean', 'Date', 'Integer', 'String', 'Float', 'JSON', 'Array'
|
|
14
|
+
raw_data_type.to_s.downcase
|
|
15
|
+
when 'Hash'
|
|
16
|
+
'object'
|
|
17
|
+
when 'Rack::Multipart::UploadedFile', 'File'
|
|
18
|
+
'file'
|
|
19
|
+
when 'Grape::API::Boolean'
|
|
20
|
+
'boolean'
|
|
21
|
+
when 'BigDecimal'
|
|
22
|
+
'double'
|
|
23
|
+
when 'DateTime', 'Time'
|
|
24
|
+
'dateTime'
|
|
25
|
+
when 'Numeric'
|
|
26
|
+
'long'
|
|
27
|
+
when 'Symbol'
|
|
28
|
+
'string'
|
|
29
|
+
else
|
|
30
|
+
parse_entity_name(raw_data_type)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def parse_multi_type(raw_data_type)
|
|
35
|
+
case raw_data_type
|
|
36
|
+
when /\A\[.*\]\z/
|
|
37
|
+
type_as_string = raw_data_type.gsub(/[\[\s+\]]/, '').split(',').first
|
|
38
|
+
begin
|
|
39
|
+
Object.const_get(type_as_string)
|
|
40
|
+
rescue NameError
|
|
41
|
+
type_as_string
|
|
42
|
+
end
|
|
43
|
+
when Array
|
|
44
|
+
raw_data_type.first
|
|
45
|
+
else
|
|
46
|
+
raw_data_type
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def parse_entity_name(model)
|
|
51
|
+
if model.respond_to?(:entity_name)
|
|
52
|
+
model.entity_name
|
|
53
|
+
elsif model.to_s.end_with?('::Entity', '::Entities')
|
|
54
|
+
model.to_s.split('::')[0..-2].join('_')
|
|
55
|
+
elsif model.to_s.start_with?('Entity::', 'Entities::', 'Representable::')
|
|
56
|
+
model.to_s.split('::')[1..-1].join('_')
|
|
57
|
+
else
|
|
58
|
+
model.to_s.split('::').join('_')
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def request_primitive?(type)
|
|
63
|
+
request_primitives.include?(type.to_s.downcase)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def primitive?(type)
|
|
67
|
+
primitives.include?(type.to_s.downcase)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def request_primitives
|
|
71
|
+
primitives + %w[object string boolean file json array]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def primitives
|
|
75
|
+
PRIMITIVE_MAPPINGS.keys.map(&:downcase)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def query_array_primitive?(type)
|
|
79
|
+
query_array_primitives.include?(type.to_s.downcase)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def query_array_primitives
|
|
83
|
+
primitives << 'string'
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def mapping(value)
|
|
87
|
+
PRIMITIVE_MAPPINGS[value] || 'string'
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def collections
|
|
91
|
+
%w[csv ssv tsv pipes multi brackets]
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
PRIMITIVE_MAPPINGS = {
|
|
96
|
+
'integer' => %w[integer int32],
|
|
97
|
+
'long' => %w[integer int64],
|
|
98
|
+
'float' => %w[number float],
|
|
99
|
+
'double' => %w[number double],
|
|
100
|
+
'byte' => %w[string byte],
|
|
101
|
+
'date' => %w[string date],
|
|
102
|
+
'dateTime' => %w[string date-time],
|
|
103
|
+
'binary' => %w[string binary],
|
|
104
|
+
'password' => %w[string password],
|
|
105
|
+
'email' => %w[string email],
|
|
106
|
+
'uuid' => %w[string uuid]
|
|
107
|
+
}.freeze
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GrapeSwagger
|
|
4
|
+
module DocMethods
|
|
5
|
+
class Extensions
|
|
6
|
+
class << self
|
|
7
|
+
def add(path, definitions, route)
|
|
8
|
+
@route = route
|
|
9
|
+
|
|
10
|
+
description = route.settings[:description]
|
|
11
|
+
add_extension_to(path[method], extension(description)) if description && extended?(description, :x)
|
|
12
|
+
|
|
13
|
+
settings = route.settings
|
|
14
|
+
add_extensions_to_operation(settings, path, route) if settings && extended?(settings, :x_operation)
|
|
15
|
+
add_extensions_to_path(settings, path) if settings && extended?(settings, :x_path)
|
|
16
|
+
add_extensions_to_definition(settings, path, definitions) if settings && extended?(settings, :x_def)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def add_extensions_to_root(settings, object)
|
|
20
|
+
add_extension_to(object, extension(settings)) if extended?(settings, :x)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def add_extensions_to_info(settings, info)
|
|
24
|
+
add_extension_to(info, extension(settings)) if extended?(settings, :x)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def add_extensions_to_operation(settings, path, route)
|
|
28
|
+
add_extension_to(path[route.request_method.downcase.to_sym], extension(settings, :x_operation))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def add_extensions_to_path(settings, path)
|
|
32
|
+
add_extension_to(path, extension(settings, :x_path))
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def add_extensions_to_definition(settings, path, definitions)
|
|
36
|
+
def_extension = extension(settings, :x_def)
|
|
37
|
+
|
|
38
|
+
if def_extension[:x_def].is_a?(Array)
|
|
39
|
+
def_extension[:x_def].each { |extension| setup_definition(extension, path, definitions) }
|
|
40
|
+
else
|
|
41
|
+
setup_definition(def_extension[:x_def], path, definitions)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def setup_definition(def_extension, path, definitions)
|
|
46
|
+
return unless def_extension.key?(:for)
|
|
47
|
+
|
|
48
|
+
status = def_extension[:for]
|
|
49
|
+
|
|
50
|
+
definition = find_definition(status, path)
|
|
51
|
+
add_extension_to(definitions[definition], x_def: def_extension)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def find_definition(status, path)
|
|
55
|
+
response = path[method][:responses][status]
|
|
56
|
+
return if response.nil?
|
|
57
|
+
|
|
58
|
+
return response[:schema]['$ref'].split('/').last if response[:schema].key?('$ref')
|
|
59
|
+
return response[:schema]['items']['$ref'].split('/').last if response[:schema].key?('items')
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def add_extension_to(part, extensions)
|
|
63
|
+
return if part.nil?
|
|
64
|
+
|
|
65
|
+
concatenate(extensions).each do |key, value|
|
|
66
|
+
part[key] = value unless key.start_with?('x-for')
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def concatenate(extensions)
|
|
71
|
+
result = {}
|
|
72
|
+
|
|
73
|
+
extensions.each_value do |extension|
|
|
74
|
+
extension.each do |key, value|
|
|
75
|
+
result["x-#{key}"] = value
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
result
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def extended?(part, identifier = :x)
|
|
83
|
+
!extension(part, identifier).empty?
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def extension(part, identifier = :x)
|
|
87
|
+
part.select { |x| x == identifier }
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def method(*args)
|
|
91
|
+
# We're shadowing Object.method(:symbol) here so we provide
|
|
92
|
+
# a compatibility layer for code that introspects the methods
|
|
93
|
+
# of this class
|
|
94
|
+
return super if args.size.positive?
|
|
95
|
+
|
|
96
|
+
@route.request_method.downcase.to_sym
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GrapeSwagger
|
|
4
|
+
module DocMethods
|
|
5
|
+
class FileParams
|
|
6
|
+
class << self
|
|
7
|
+
def includes_file_param?(params)
|
|
8
|
+
return params.any? { |x| x[:type] == 'file' }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def to_formdata(params)
|
|
12
|
+
params.each { |x| x[:in] = 'formData' if x[:in] == 'body' }
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GrapeSwagger
|
|
4
|
+
module DocMethods
|
|
5
|
+
class FormatData
|
|
6
|
+
class << self
|
|
7
|
+
def to_format(parameters)
|
|
8
|
+
parameters.reject { |parameter| parameter[:in] == 'body' }.each do |b|
|
|
9
|
+
related_parameters = parameters.select do |p|
|
|
10
|
+
p[:name] != b[:name] && p[:name].to_s.start_with?("#{b[:name].to_s.gsub(/\[\]\z/, '')}[")
|
|
11
|
+
end
|
|
12
|
+
parameters.reject! { |p| p[:name] == b[:name] } if move_down(b, related_parameters)
|
|
13
|
+
end
|
|
14
|
+
parameters
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def move_down(parameter, related_parameters)
|
|
18
|
+
case parameter[:type]
|
|
19
|
+
when 'array'
|
|
20
|
+
add_array(parameter, related_parameters)
|
|
21
|
+
unless related_parameters.blank?
|
|
22
|
+
add_braces(parameter, related_parameters) if parameter[:name].match?(/\A.*\[\]\z/)
|
|
23
|
+
return true
|
|
24
|
+
end
|
|
25
|
+
when 'object'
|
|
26
|
+
return true
|
|
27
|
+
end
|
|
28
|
+
false
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def add_braces(parameter, related_parameters)
|
|
32
|
+
param_name = parameter[:name].gsub(/\A(.*)\[\]\z/, '\1')
|
|
33
|
+
related_parameters.each { |p| p[:name] = p[:name].gsub(param_name, "#{param_name}[]") }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def add_array(parameter, related_parameters)
|
|
37
|
+
related_parameters.each do |p|
|
|
38
|
+
next if p.key?(:items)
|
|
39
|
+
|
|
40
|
+
p_type = p[:type] == 'array' ? 'string' : p[:type]
|
|
41
|
+
p[:items] = { type: p_type, format: p[:format], enum: p[:enum], is_array: p[:is_array] }
|
|
42
|
+
p[:items].delete_if { |_k, v| v.nil? }
|
|
43
|
+
p[:type] = 'array'
|
|
44
|
+
p[:is_array] = parameter[:is_array]
|
|
45
|
+
p.delete(:format)
|
|
46
|
+
p.delete(:enum)
|
|
47
|
+
p.delete_if { |_k, v| v.nil? }
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GrapeSwagger
|
|
4
|
+
module DocMethods
|
|
5
|
+
class Headers
|
|
6
|
+
class << self
|
|
7
|
+
def parse(route)
|
|
8
|
+
route.headers.to_a.map do |route_header|
|
|
9
|
+
route_header.tap do |header|
|
|
10
|
+
hash = header[1]
|
|
11
|
+
description = hash.delete('description')
|
|
12
|
+
hash[:documentation] = { desc: description, in: 'header' }
|
|
13
|
+
hash[:type] = hash['type'].titleize if hash['type']
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_support/core_ext/hash/deep_merge'
|
|
4
|
+
|
|
5
|
+
module GrapeSwagger
|
|
6
|
+
module DocMethods
|
|
7
|
+
class MoveParams
|
|
8
|
+
class << self
|
|
9
|
+
attr_accessor :definitions
|
|
10
|
+
|
|
11
|
+
def can_be_moved?(http_verb, params)
|
|
12
|
+
move_methods.include?(http_verb) && includes_body_param?(params)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def to_definition(path, params, route, definitions)
|
|
16
|
+
@definitions = definitions
|
|
17
|
+
unify!(params)
|
|
18
|
+
|
|
19
|
+
params_to_move = movable_params(params)
|
|
20
|
+
|
|
21
|
+
return (params + correct_array_param(params_to_move)) if should_correct_array?(params_to_move)
|
|
22
|
+
|
|
23
|
+
params << parent_definition_of_params(params_to_move, path, route)
|
|
24
|
+
|
|
25
|
+
params
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def should_correct_array?(param)
|
|
31
|
+
param.length == 1 && param.first[:in] == 'body' && param.first[:type] == 'array'
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def correct_array_param(param)
|
|
35
|
+
param.first[:schema] = { type: param.first.delete(:type), items: param.first.delete(:items) }
|
|
36
|
+
|
|
37
|
+
param
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def parent_definition_of_params(params, path, route)
|
|
41
|
+
definition_name = OperationId.build(route, path)
|
|
42
|
+
build_definition(definition_name, params)
|
|
43
|
+
definition = @definitions[definition_name]
|
|
44
|
+
|
|
45
|
+
move_params_to_new(definition, params)
|
|
46
|
+
|
|
47
|
+
definition[:description] = route.description if route.try(:description)
|
|
48
|
+
|
|
49
|
+
build_body_parameter(definition_name, route.options)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def move_params_to_new(definition, params)
|
|
53
|
+
params, nested_params = params.partition { |x| !x[:name].to_s.include?('[') }
|
|
54
|
+
params.each do |param|
|
|
55
|
+
property = param[:name]
|
|
56
|
+
param_properties, param_required = build_properties([param])
|
|
57
|
+
add_properties_to_definition(definition, param_properties, param_required)
|
|
58
|
+
related_nested_params, nested_params = nested_params.partition { |x| x[:name].start_with?("#{property}[") }
|
|
59
|
+
prepare_nested_names(property, related_nested_params)
|
|
60
|
+
|
|
61
|
+
next if related_nested_params.blank?
|
|
62
|
+
|
|
63
|
+
nested_definition = if should_expose_as_array?([param])
|
|
64
|
+
move_params_to_new(array_type, related_nested_params)
|
|
65
|
+
else
|
|
66
|
+
move_params_to_new(object_type, related_nested_params)
|
|
67
|
+
end
|
|
68
|
+
if definition.key?(:items)
|
|
69
|
+
definition[:items][:properties][property.to_sym].deep_merge!(nested_definition)
|
|
70
|
+
else
|
|
71
|
+
definition[:properties][property.to_sym].deep_merge!(nested_definition)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
definition
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def build_properties(params)
|
|
78
|
+
properties = {}
|
|
79
|
+
required = []
|
|
80
|
+
|
|
81
|
+
params.each do |param|
|
|
82
|
+
name = param[:name].to_sym
|
|
83
|
+
|
|
84
|
+
properties[name] = if should_expose_as_array?([param])
|
|
85
|
+
document_as_array(param)
|
|
86
|
+
else
|
|
87
|
+
document_as_property(param)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
required << name if deletable?(param) && param[:required]
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
[properties, required]
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def document_as_array(param)
|
|
97
|
+
{}.tap do |property|
|
|
98
|
+
property[:type] = 'array'
|
|
99
|
+
property[:description] = param.delete(:description) unless param[:description].nil?
|
|
100
|
+
property[:items] = document_as_property(param)[:items]
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def document_as_property(param)
|
|
105
|
+
property_keys.each_with_object({}) do |x, memo|
|
|
106
|
+
next unless param.key?(x)
|
|
107
|
+
|
|
108
|
+
value = param[x]
|
|
109
|
+
if x == :type && @definitions[value].present?
|
|
110
|
+
memo['$ref'] = "#/definitions/#{value}"
|
|
111
|
+
else
|
|
112
|
+
memo[x] = value
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def movable_params(params)
|
|
118
|
+
to_delete = params.each_with_object([]) { |x, memo| memo << x if deletable?(x) }
|
|
119
|
+
delete_from(params, to_delete)
|
|
120
|
+
|
|
121
|
+
to_delete
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def delete_from(params, to_delete)
|
|
125
|
+
to_delete.each { |x| params.delete(x) }
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def add_properties_to_definition(definition, properties, required)
|
|
129
|
+
if definition.key?(:items)
|
|
130
|
+
definition[:items][:properties].deep_merge!(properties)
|
|
131
|
+
add_to_required(definition[:items], required)
|
|
132
|
+
else
|
|
133
|
+
definition[:properties].deep_merge!(properties)
|
|
134
|
+
add_to_required(definition, required)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def add_to_required(definition, value)
|
|
139
|
+
return if value.blank?
|
|
140
|
+
|
|
141
|
+
definition[:required] ||= []
|
|
142
|
+
definition[:required].push(*value)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def build_body_parameter(name, options)
|
|
146
|
+
{}.tap do |x|
|
|
147
|
+
x[:name] = options[:body_name] || name
|
|
148
|
+
x[:in] = 'body'
|
|
149
|
+
x[:required] = true
|
|
150
|
+
x[:schema] = { '$ref' => "#/definitions/#{name}" }
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def build_definition(name, params)
|
|
155
|
+
@definitions[name] = should_expose_as_array?(params) ? array_type : object_type
|
|
156
|
+
|
|
157
|
+
name
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def array_type
|
|
161
|
+
{ type: 'array', items: { type: 'object', properties: {} } }
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def object_type
|
|
165
|
+
{ type: 'object', properties: {} }
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def prepare_nested_names(property, params)
|
|
169
|
+
params.each { |x| x[:name] = x[:name].sub(property, '').sub('[', '').sub(']', '') }
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def unify!(params)
|
|
173
|
+
params.each { |x| x[:in] = x.delete(:param_type) if x[:param_type] }
|
|
174
|
+
params.each { |x| x[:in] = 'body' if x[:in] == 'formData' } if includes_body_param?(params)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def parse_model(ref)
|
|
178
|
+
parts = ref.split('/')
|
|
179
|
+
parts.last.include?('{') ? parts[0..-2].join('/') : parts[0..-1].join('/')
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def property_keys
|
|
183
|
+
%i[type format description minimum maximum items enum default additional_properties additionalProperties
|
|
184
|
+
example]
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def deletable?(param)
|
|
188
|
+
param[:in] == 'body'
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def move_methods
|
|
192
|
+
[:post, :put, :patch, 'POST', 'PUT', 'PATCH']
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def includes_body_param?(params)
|
|
196
|
+
params.any? { |x| x[:in] == 'body' || x[:param_type] == 'body' }
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def should_expose_as_array?(params)
|
|
200
|
+
should_exposed_as(params) == 'array'
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def should_exposed_as(params)
|
|
204
|
+
params.any? { |x| x[:type] && x[:type] != 'array' } ? 'object' : 'array'
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GrapeSwagger
|
|
4
|
+
module DocMethods
|
|
5
|
+
class OperationId
|
|
6
|
+
class << self
|
|
7
|
+
def build(route, path = nil)
|
|
8
|
+
if route.options[:nickname]
|
|
9
|
+
route.options[:nickname]
|
|
10
|
+
else
|
|
11
|
+
verb = route.request_method.to_s.downcase
|
|
12
|
+
operation = manipulate(path) unless path.nil?
|
|
13
|
+
"#{verb}#{operation}"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def manipulate(path)
|
|
18
|
+
operation = path.split('/').map(&:capitalize).join
|
|
19
|
+
operation.gsub!(/-(\w)/, &:upcase).delete!('-') if operation[/-(\w)/]
|
|
20
|
+
operation.gsub!(/_(\w)/, &:upcase).delete!('_') if operation.include?('_')
|
|
21
|
+
operation.gsub!(/\.(\w)/, &:upcase).delete!('.') if operation[/\.(\w)/]
|
|
22
|
+
if path.include?('{')
|
|
23
|
+
operation.gsub!(/\{(\w)/, &:upcase)
|
|
24
|
+
operation.delete!('{').delete!('}')
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
operation
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GrapeSwagger
|
|
4
|
+
module DocMethods
|
|
5
|
+
class OptionalObject
|
|
6
|
+
class << self
|
|
7
|
+
def build(key, options, request = nil)
|
|
8
|
+
if options[key]
|
|
9
|
+
return evaluate(key, options, request) if options[key].is_a?(Proc)
|
|
10
|
+
|
|
11
|
+
options[key]
|
|
12
|
+
else
|
|
13
|
+
request.send(default_values[key])
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def evaluate(key, options, request)
|
|
18
|
+
options[key].arity.zero? ? options[key].call : options[key].call(request)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def default_values
|
|
22
|
+
{
|
|
23
|
+
host: 'host_with_port',
|
|
24
|
+
base_path: 'script_name'
|
|
25
|
+
}
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|