grape 1.6.2 → 1.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +54 -1
- data/CONTRIBUTING.md +30 -0
- data/README.md +146 -23
- data/UPGRADING.md +15 -0
- data/grape.gemspec +2 -2
- data/lib/grape/api/instance.rb +1 -1
- data/lib/grape/dry_types.rb +12 -0
- data/lib/grape/dsl/api.rb +0 -2
- data/lib/grape/dsl/callbacks.rb +0 -2
- data/lib/grape/dsl/configuration.rb +0 -2
- data/lib/grape/dsl/desc.rb +2 -16
- data/lib/grape/dsl/helpers.rb +0 -2
- data/lib/grape/dsl/inside_route.rb +34 -30
- data/lib/grape/dsl/middleware.rb +0 -2
- data/lib/grape/dsl/parameters.rb +10 -7
- data/lib/grape/dsl/request_response.rb +1 -3
- data/lib/grape/dsl/routing.rb +4 -2
- data/lib/grape/dsl/settings.rb +0 -2
- data/lib/grape/dsl/validations.rb +0 -15
- data/lib/grape/endpoint.rb +2 -2
- data/lib/grape/error_formatter/json.rb +7 -1
- data/lib/grape/exceptions/base.rb +3 -2
- data/lib/grape/exceptions/missing_group_type.rb +8 -1
- data/lib/grape/exceptions/too_many_multipart_files.rb +11 -0
- data/lib/grape/exceptions/unsupported_group_type.rb +8 -1
- data/lib/grape/exceptions/validation.rb +0 -4
- data/lib/grape/locale/en.yml +9 -8
- data/lib/grape/middleware/auth/dsl.rb +0 -1
- data/lib/grape/middleware/error.rb +2 -2
- data/lib/grape/middleware/stack.rb +1 -1
- data/lib/grape/request.rb +3 -1
- data/lib/grape/router/attribute_translator.rb +1 -1
- data/lib/grape/types/invalid_value.rb +8 -0
- data/lib/grape/util/cache.rb +1 -1
- data/lib/grape/util/json.rb +2 -0
- data/lib/grape/validations/attributes_doc.rb +58 -0
- data/lib/grape/validations/params_scope.rb +67 -41
- data/lib/grape/validations/types/array_coercer.rb +0 -2
- data/lib/grape/validations/types/dry_type_coercer.rb +3 -7
- data/lib/grape/validations/types/invalid_value.rb +0 -7
- data/lib/grape/validations/types/primitive_coercer.rb +14 -6
- data/lib/grape/validations/types/set_coercer.rb +0 -2
- data/lib/grape/validations/types.rb +98 -30
- data/lib/grape/validations/validators/{all_or_none.rb → all_or_none_of_validator.rb} +0 -2
- data/lib/grape/validations/validators/{at_least_one_of.rb → at_least_one_of_validator.rb} +0 -2
- data/lib/grape/validations/validators/base.rb +7 -0
- data/lib/grape/validations/validators/{exactly_one_of.rb → exactly_one_of_validator.rb} +0 -2
- data/lib/grape/validations/validators/{mutual_exclusion.rb → mutual_exclusion_validator.rb} +0 -2
- data/lib/grape/validations.rb +16 -12
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +74 -28
- data/spec/grape/api/custom_validations_spec.rb +41 -2
- data/spec/grape/api/deeply_included_options_spec.rb +0 -2
- data/spec/grape/api/defines_boolean_in_params_spec.rb +0 -2
- data/spec/grape/api/documentation_spec.rb +59 -0
- data/spec/grape/api/inherited_helpers_spec.rb +0 -2
- data/spec/grape/api/instance_spec.rb +0 -1
- data/spec/grape/api/invalid_format_spec.rb +0 -2
- data/spec/grape/api/namespace_parameters_in_route_spec.rb +0 -2
- data/spec/grape/api/nested_helpers_spec.rb +0 -2
- data/spec/grape/api/optional_parameters_in_route_spec.rb +0 -2
- data/spec/grape/api/parameters_modification_spec.rb +0 -2
- data/spec/grape/api/patch_method_helpers_spec.rb +0 -2
- data/spec/grape/api/recognize_path_spec.rb +0 -2
- data/spec/grape/api/required_parameters_in_route_spec.rb +0 -2
- data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +0 -2
- data/spec/grape/api/routes_with_requirements_spec.rb +0 -2
- data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +0 -2
- data/spec/grape/api/shared_helpers_spec.rb +0 -2
- data/spec/grape/api_remount_spec.rb +0 -1
- data/spec/grape/api_spec.rb +23 -25
- data/spec/grape/config_spec.rb +0 -2
- data/spec/grape/dsl/callbacks_spec.rb +0 -2
- data/spec/grape/dsl/desc_spec.rb +2 -2
- data/spec/grape/dsl/headers_spec.rb +2 -4
- data/spec/grape/dsl/helpers_spec.rb +0 -2
- data/spec/grape/dsl/inside_route_spec.rb +10 -12
- data/spec/grape/dsl/logger_spec.rb +0 -2
- data/spec/grape/dsl/middleware_spec.rb +0 -2
- data/spec/grape/dsl/parameters_spec.rb +0 -2
- data/spec/grape/dsl/request_response_spec.rb +6 -8
- data/spec/grape/dsl/routing_spec.rb +1 -3
- data/spec/grape/dsl/settings_spec.rb +0 -2
- data/spec/grape/dsl/validations_spec.rb +0 -17
- data/spec/grape/endpoint/declared_spec.rb +2 -4
- data/spec/grape/endpoint_spec.rb +29 -9
- data/spec/grape/entity_spec.rb +0 -1
- data/spec/grape/exceptions/base_spec.rb +16 -2
- data/spec/grape/exceptions/body_parse_errors_spec.rb +0 -2
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +3 -2
- data/spec/grape/exceptions/invalid_formatter_spec.rb +0 -2
- data/spec/grape/exceptions/invalid_response_spec.rb +0 -2
- data/spec/grape/exceptions/invalid_versioner_option_spec.rb +1 -3
- data/spec/grape/exceptions/missing_group_type_spec.rb +21 -0
- data/spec/grape/exceptions/missing_mime_type_spec.rb +0 -2
- data/spec/grape/exceptions/missing_option_spec.rb +1 -3
- data/spec/grape/exceptions/unknown_options_spec.rb +0 -2
- data/spec/grape/exceptions/unknown_validator_spec.rb +0 -2
- data/spec/grape/exceptions/unsupported_group_type_spec.rb +23 -0
- data/spec/grape/exceptions/validation_errors_spec.rb +0 -1
- data/spec/grape/exceptions/validation_spec.rb +1 -3
- data/spec/grape/extensions/param_builders/hash_spec.rb +0 -2
- data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +0 -2
- data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +0 -2
- data/spec/grape/integration/global_namespace_function_spec.rb +0 -2
- data/spec/grape/integration/rack_sendfile_spec.rb +0 -2
- data/spec/grape/integration/rack_spec.rb +6 -7
- data/spec/grape/loading_spec.rb +0 -2
- data/spec/grape/middleware/auth/base_spec.rb +0 -1
- data/spec/grape/middleware/auth/dsl_spec.rb +0 -2
- data/spec/grape/middleware/auth/strategies_spec.rb +0 -2
- data/spec/grape/middleware/base_spec.rb +7 -7
- data/spec/grape/middleware/error_spec.rb +6 -1
- data/spec/grape/middleware/exception_spec.rb +0 -2
- data/spec/grape/middleware/formatter_spec.rb +6 -8
- data/spec/grape/middleware/globals_spec.rb +0 -2
- data/spec/grape/middleware/stack_spec.rb +0 -2
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +0 -2
- data/spec/grape/middleware/versioner/header_spec.rb +18 -4
- data/spec/grape/middleware/versioner/param_spec.rb +0 -2
- data/spec/grape/middleware/versioner/path_spec.rb +0 -2
- data/spec/grape/middleware/versioner_spec.rb +0 -2
- data/spec/grape/named_api_spec.rb +0 -2
- data/spec/grape/parser_spec.rb +0 -2
- data/spec/grape/path_spec.rb +0 -2
- data/spec/grape/presenters/presenter_spec.rb +0 -2
- data/spec/grape/request_spec.rb +0 -2
- data/spec/grape/util/inheritable_setting_spec.rb +0 -1
- data/spec/grape/util/inheritable_values_spec.rb +0 -1
- data/spec/grape/util/reverse_stackable_values_spec.rb +0 -1
- data/spec/grape/util/stackable_values_spec.rb +0 -1
- data/spec/grape/util/strict_hash_configuration_spec.rb +0 -1
- data/spec/grape/validations/attributes_doc_spec.rb +153 -0
- data/spec/grape/validations/instance_behaivour_spec.rb +0 -2
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +0 -2
- data/spec/grape/validations/params_scope_spec.rb +315 -86
- data/spec/grape/validations/single_attribute_iterator_spec.rb +0 -2
- data/spec/grape/validations/types/array_coercer_spec.rb +0 -2
- data/spec/grape/validations/types/primitive_coercer_spec.rb +20 -5
- data/spec/grape/validations/types/set_coercer_spec.rb +0 -2
- data/spec/grape/validations/types_spec.rb +28 -2
- data/spec/grape/validations/validators/all_or_none_spec.rb +0 -2
- data/spec/grape/validations/validators/allow_blank_spec.rb +0 -2
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +0 -2
- data/spec/grape/validations/validators/coerce_spec.rb +0 -2
- data/spec/grape/validations/validators/default_spec.rb +0 -2
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +0 -2
- data/spec/grape/validations/validators/except_values_spec.rb +0 -2
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +0 -2
- data/spec/grape/validations/validators/presence_spec.rb +0 -2
- data/spec/grape/validations/validators/regexp_spec.rb +0 -2
- data/spec/grape/validations/validators/same_as_spec.rb +0 -2
- data/spec/grape/validations/validators/values_spec.rb +19 -2
- data/spec/grape/validations_spec.rb +78 -27
- data/spec/integration/multi_json/json_spec.rb +0 -2
- data/spec/integration/multi_xml/xml_spec.rb +0 -2
- data/spec/spec_helper.rb +9 -4
- metadata +134 -122
- data/spec/grape/dsl/configuration_spec.rb +0 -16
- data/spec/grape/validations/attributes_iterator_spec.rb +0 -6
- data/spec/support/eager_load.rb +0 -19
- /data/lib/grape/validations/validators/{allow_blank.rb → allow_blank_validator.rb} +0 -0
- /data/lib/grape/validations/validators/{as.rb → as_validator.rb} +0 -0
- /data/lib/grape/validations/validators/{coerce.rb → coerce_validator.rb} +0 -0
- /data/lib/grape/validations/validators/{default.rb → default_validator.rb} +0 -0
- /data/lib/grape/validations/validators/{except_values.rb → except_values_validator.rb} +0 -0
- /data/lib/grape/validations/validators/{presence.rb → presence_validator.rb} +0 -0
- /data/lib/grape/validations/validators/{regexp.rb → regexp_validator.rb} +0 -0
- /data/lib/grape/validations/validators/{same_as.rb → same_as_validator.rb} +0 -0
- /data/lib/grape/validations/validators/{values.rb → values_validator.rb} +0 -0
data/lib/grape/dsl/parameters.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'active_support/concern'
|
4
|
-
|
5
3
|
module Grape
|
6
4
|
module DSL
|
7
5
|
# Defines DSL methods, meant to be applied to a ParamsScope, which define
|
@@ -64,7 +62,12 @@ module Grape
|
|
64
62
|
params_block = named_params.fetch(name) do
|
65
63
|
raise "Params :#{name} not found!"
|
66
64
|
end
|
67
|
-
|
65
|
+
|
66
|
+
if options.empty?
|
67
|
+
instance_exec(options, ¶ms_block)
|
68
|
+
else
|
69
|
+
instance_exec(**options, ¶ms_block)
|
70
|
+
end
|
68
71
|
end
|
69
72
|
end
|
70
73
|
alias use_scope use
|
@@ -150,8 +153,8 @@ module Grape
|
|
150
153
|
|
151
154
|
# check type for optional parameter group
|
152
155
|
if attrs && block
|
153
|
-
raise Grape::Exceptions::
|
154
|
-
raise Grape::Exceptions::
|
156
|
+
raise Grape::Exceptions::MissingGroupType if type.nil?
|
157
|
+
raise Grape::Exceptions::UnsupportedGroupType unless Grape::Validations::Types.group?(type)
|
155
158
|
end
|
156
159
|
|
157
160
|
if opts[:using]
|
@@ -219,8 +222,8 @@ module Grape
|
|
219
222
|
else
|
220
223
|
# @declared_params also includes hashes of options and such, but those
|
221
224
|
# won't be flattened out.
|
222
|
-
@declared_params.flatten.any? do |
|
223
|
-
first_hash_key_or_param(
|
225
|
+
@declared_params.flatten.any? do |declared_param_attr|
|
226
|
+
first_hash_key_or_param(declared_param_attr.key) == param
|
224
227
|
end
|
225
228
|
end
|
226
229
|
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'active_support/concern'
|
4
|
-
|
5
3
|
module Grape
|
6
4
|
module DSL
|
7
5
|
module RequestResponse
|
@@ -127,7 +125,7 @@ module Grape
|
|
127
125
|
:base_only_rescue_handlers
|
128
126
|
end
|
129
127
|
|
130
|
-
namespace_reverse_stackable
|
128
|
+
namespace_reverse_stackable(handler_type, args.to_h { |arg| [arg, handler] })
|
131
129
|
end
|
132
130
|
|
133
131
|
namespace_stackable(:rescue_options, options)
|
data/lib/grape/dsl/routing.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'active_support/concern'
|
4
|
-
|
5
3
|
module Grape
|
6
4
|
module DSL
|
7
5
|
module Routing
|
@@ -79,6 +77,10 @@ module Grape
|
|
79
77
|
namespace_inheritable(:do_not_route_options, true)
|
80
78
|
end
|
81
79
|
|
80
|
+
def do_not_document!
|
81
|
+
namespace_inheritable(:do_not_document, true)
|
82
|
+
end
|
83
|
+
|
82
84
|
def mount(mounts, *opts)
|
83
85
|
mounts = { mounts => '/' } unless mounts.respond_to?(:each_pair)
|
84
86
|
mounts.each_pair do |app, path|
|
data/lib/grape/dsl/settings.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'active_support/concern'
|
4
|
-
|
5
3
|
module Grape
|
6
4
|
module DSL
|
7
5
|
module Validations
|
@@ -32,7 +30,6 @@ module Grape
|
|
32
30
|
unset_namespace_stackable :declared_params
|
33
31
|
unset_namespace_stackable :validations
|
34
32
|
unset_namespace_stackable :params
|
35
|
-
unset_description_field :params
|
36
33
|
end
|
37
34
|
|
38
35
|
# Opens a root-level ParamsScope, defining parameter coercions and
|
@@ -41,18 +38,6 @@ module Grape
|
|
41
38
|
def params(&block)
|
42
39
|
Grape::Validations::ParamsScope.new(api: self, type: Hash, &block)
|
43
40
|
end
|
44
|
-
|
45
|
-
def document_attribute(names, opts)
|
46
|
-
setting = description_field(:params)
|
47
|
-
setting ||= description_field(:params, {})
|
48
|
-
Array(names).each do |name|
|
49
|
-
full_name = name[:full_name].to_s
|
50
|
-
setting[full_name] ||= {}
|
51
|
-
setting[full_name].merge!(opts)
|
52
|
-
|
53
|
-
namespace_stackable(:params, full_name => opts)
|
54
|
-
end
|
55
|
-
end
|
56
41
|
end
|
57
42
|
end
|
58
43
|
end
|
data/lib/grape/endpoint.rb
CHANGED
@@ -299,7 +299,7 @@ module Grape
|
|
299
299
|
|
300
300
|
if namespace_inheritable(:version)
|
301
301
|
stack.use Grape::Middleware::Versioner.using(namespace_inheritable(:version_options)[:using]),
|
302
|
-
versions: namespace_inheritable(:version)
|
302
|
+
versions: namespace_inheritable(:version)&.flatten,
|
303
303
|
version_options: namespace_inheritable(:version_options),
|
304
304
|
prefix: namespace_inheritable(:root_prefix),
|
305
305
|
mount_path: namespace_stackable(:mount_path).first
|
@@ -325,7 +325,7 @@ module Grape
|
|
325
325
|
private :build_stack, :build_helpers
|
326
326
|
|
327
327
|
def execute
|
328
|
-
@block
|
328
|
+
@block&.call(self)
|
329
329
|
end
|
330
330
|
|
331
331
|
def helpers
|
@@ -21,9 +21,15 @@ module Grape
|
|
21
21
|
if message.is_a?(Exceptions::ValidationErrors) || message.is_a?(Hash)
|
22
22
|
message
|
23
23
|
else
|
24
|
-
{ error: message }
|
24
|
+
{ error: ensure_utf8(message) }
|
25
25
|
end
|
26
26
|
end
|
27
|
+
|
28
|
+
def ensure_utf8(message)
|
29
|
+
return message unless message.respond_to? :encode
|
30
|
+
|
31
|
+
message.encode('UTF-8', invalid: :replace, undef: :replace)
|
32
|
+
end
|
27
33
|
end
|
28
34
|
end
|
29
35
|
end
|
@@ -7,11 +7,12 @@ module Grape
|
|
7
7
|
BASE_ATTRIBUTES_KEY = 'grape.errors.attributes'
|
8
8
|
FALLBACK_LOCALE = :en
|
9
9
|
|
10
|
-
attr_reader :status, :
|
10
|
+
attr_reader :status, :headers
|
11
11
|
|
12
12
|
def initialize(status: nil, message: nil, headers: nil, **_options)
|
13
|
+
super(message)
|
14
|
+
|
13
15
|
@status = status
|
14
|
-
@message = message
|
15
16
|
@headers = headers
|
16
17
|
end
|
17
18
|
|
@@ -2,10 +2,17 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module Exceptions
|
5
|
-
class
|
5
|
+
class MissingGroupType < Base
|
6
6
|
def initialize
|
7
7
|
super(message: compose_message(:missing_group_type))
|
8
8
|
end
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
12
|
+
|
13
|
+
Grape::Exceptions::MissingGroupTypeError = Class.new(Grape::Exceptions::MissingGroupType) do
|
14
|
+
def initialize(*)
|
15
|
+
super
|
16
|
+
warn '[DEPRECATION] `Grape::Exceptions::MissingGroupTypeError` is deprecated. Use `Grape::Exceptions::MissingGroupType` instead.'
|
17
|
+
end
|
18
|
+
end
|
@@ -2,10 +2,17 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module Exceptions
|
5
|
-
class
|
5
|
+
class UnsupportedGroupType < Base
|
6
6
|
def initialize
|
7
7
|
super(message: compose_message(:unsupported_group_type))
|
8
8
|
end
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
12
|
+
|
13
|
+
Grape::Exceptions::UnsupportedGroupTypeError = Class.new(Grape::Exceptions::UnsupportedGroupType) do
|
14
|
+
def initialize(*)
|
15
|
+
super
|
16
|
+
warn '[DEPRECATION] `Grape::Exceptions::UnsupportedGroupTypeError` is deprecated. Use `Grape::Exceptions::UnsupportedGroupType` instead.'
|
17
|
+
end
|
18
|
+
end
|
data/lib/grape/locale/en.yml
CHANGED
@@ -11,8 +11,8 @@ en:
|
|
11
11
|
except_values: 'has a value not allowed'
|
12
12
|
same_as: 'is not the same as %{parameter}'
|
13
13
|
missing_vendor_option:
|
14
|
-
problem: 'missing :vendor option
|
15
|
-
summary: 'when version using header, you must specify :vendor option
|
14
|
+
problem: 'missing :vendor option'
|
15
|
+
summary: 'when version using header, you must specify :vendor option'
|
16
16
|
resolution: "eg: version 'v1', using: :header, vendor: 'twitter'"
|
17
17
|
missing_mime_type:
|
18
18
|
problem: 'missing mime type for %{new_format}'
|
@@ -21,12 +21,12 @@ en:
|
|
21
21
|
or add your own with content_type :%{new_format}, 'application/%{new_format}'
|
22
22
|
"
|
23
23
|
invalid_with_option_for_represent:
|
24
|
-
problem: '
|
24
|
+
problem: 'you must specify an entity class in the :with option'
|
25
25
|
resolution: 'eg: represent User, :with => Entity::User'
|
26
|
-
missing_option: '
|
26
|
+
missing_option: 'you must specify :%{option} options'
|
27
27
|
invalid_formatter: 'cannot convert %{klass} to %{to_format}'
|
28
28
|
invalid_versioner_option:
|
29
|
-
problem: '
|
29
|
+
problem: 'unknown :using for versioner: %{strategy}'
|
30
30
|
resolution: 'available strategy for :using is :path, :header, :accept_version_header, :param'
|
31
31
|
unknown_validator: 'unknown validator: %{validator_type}'
|
32
32
|
unknown_options: 'unknown options: %{options}'
|
@@ -44,11 +44,12 @@ en:
|
|
44
44
|
"when specifying %{body_format} as content-type, you must pass valid
|
45
45
|
%{body_format} in the request's 'body'
|
46
46
|
"
|
47
|
-
empty_message_body: '
|
47
|
+
empty_message_body: 'empty message body supplied with %{body_format} content-type'
|
48
|
+
too_many_multipart_files: "the number of uploaded files exceeded the system's configured limit (%{limit})"
|
48
49
|
invalid_accept_header:
|
49
|
-
problem: '
|
50
|
+
problem: 'invalid accept header'
|
50
51
|
resolution: '%{message}'
|
51
52
|
invalid_version_header:
|
52
|
-
problem: '
|
53
|
+
problem: 'invalid version header'
|
53
54
|
resolution: '%{message}'
|
54
55
|
invalid_response: 'Invalid response'
|
@@ -72,7 +72,7 @@ module Grape
|
|
72
72
|
|
73
73
|
def rack_response(message, status = options[:default_status], headers = { Grape::Http::Headers::CONTENT_TYPE => content_type })
|
74
74
|
message = ERB::Util.html_escape(message) if headers[Grape::Http::Headers::CONTENT_TYPE] == TEXT_HTML
|
75
|
-
Rack::Response.new([message], status, headers)
|
75
|
+
Rack::Response.new([message], Rack::Utils.status_code(status), headers)
|
76
76
|
end
|
77
77
|
|
78
78
|
def format_message(message, backtrace, original_exception = nil)
|
@@ -121,7 +121,7 @@ module Grape
|
|
121
121
|
|
122
122
|
def run_rescue_handler(handler, error)
|
123
123
|
if handler.instance_of?(Symbol)
|
124
|
-
raise NoMethodError, "undefined method
|
124
|
+
raise NoMethodError, "undefined method '#{handler}'" unless respond_to?(handler)
|
125
125
|
|
126
126
|
handler = public_method(handler)
|
127
127
|
end
|
@@ -95,7 +95,7 @@ module Grape
|
|
95
95
|
|
96
96
|
# @return [Rack::Builder] the builder object with our middlewares applied
|
97
97
|
def build(builder = Rack::Builder.new)
|
98
|
-
others.shift(others.size).each(
|
98
|
+
others.shift(others.size).each { |m| merge_with(m) }
|
99
99
|
middlewares.each do |m|
|
100
100
|
m.use_in(builder)
|
101
101
|
end
|
data/lib/grape/request.rb
CHANGED
@@ -17,6 +17,8 @@ module Grape
|
|
17
17
|
@params ||= build_params
|
18
18
|
rescue EOFError
|
19
19
|
raise Grape::Exceptions::EmptyMessageBody.new(content_type)
|
20
|
+
rescue Rack::Multipart::MultipartPartLimitError
|
21
|
+
raise Grape::Exceptions::TooManyMultipartFiles.new(Rack::Utils.multipart_part_limit)
|
20
22
|
end
|
21
23
|
|
22
24
|
def headers
|
@@ -45,7 +47,7 @@ module Grape
|
|
45
47
|
end
|
46
48
|
|
47
49
|
def transform_header(header)
|
48
|
-
-header[5
|
50
|
+
-header[5..].split('_').each(&:capitalize!).join('-')
|
49
51
|
end
|
50
52
|
end
|
51
53
|
end
|
data/lib/grape/util/cache.rb
CHANGED
data/lib/grape/util/json.rb
CHANGED
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Validations
|
5
|
+
class ParamsScope
|
6
|
+
# Documents parameters of an endpoint. If documentation isn't needed (for instance, it is an
|
7
|
+
# internal API), the class only cleans up attributes to avoid junk in RAM.
|
8
|
+
class AttributesDoc
|
9
|
+
attr_accessor :type, :values
|
10
|
+
|
11
|
+
# @param api [Grape::API::Instance]
|
12
|
+
# @param scope [Validations::ParamsScope]
|
13
|
+
def initialize(api, scope)
|
14
|
+
@api = api
|
15
|
+
@scope = scope
|
16
|
+
@type = type
|
17
|
+
end
|
18
|
+
|
19
|
+
def extract_details(validations)
|
20
|
+
details[:required] = validations.key?(:presence)
|
21
|
+
|
22
|
+
desc = validations.delete(:desc) || validations.delete(:description)
|
23
|
+
|
24
|
+
details[:desc] = desc if desc
|
25
|
+
|
26
|
+
documentation = validations.delete(:documentation)
|
27
|
+
|
28
|
+
details[:documentation] = documentation if documentation
|
29
|
+
|
30
|
+
details[:default] = validations[:default] if validations.key?(:default)
|
31
|
+
end
|
32
|
+
|
33
|
+
def document(attrs)
|
34
|
+
return if @api.namespace_inheritable(:do_not_document)
|
35
|
+
|
36
|
+
details[:type] = type.to_s if type
|
37
|
+
details[:values] = values if values
|
38
|
+
|
39
|
+
documented_attrs = attrs.each_with_object({}) do |name, memo|
|
40
|
+
memo[@scope.full_name(name)] = details
|
41
|
+
end
|
42
|
+
|
43
|
+
@api.namespace_stackable(:params, documented_attrs)
|
44
|
+
end
|
45
|
+
|
46
|
+
def required
|
47
|
+
details[:required]
|
48
|
+
end
|
49
|
+
|
50
|
+
protected
|
51
|
+
|
52
|
+
def details
|
53
|
+
@details ||= {}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'attributes_doc'
|
4
|
+
|
3
5
|
module Grape
|
4
6
|
module Validations
|
5
7
|
class ParamsScope
|
@@ -8,6 +10,35 @@ module Grape
|
|
8
10
|
|
9
11
|
include Grape::DSL::Parameters
|
10
12
|
|
13
|
+
class Attr
|
14
|
+
attr_accessor :key, :scope
|
15
|
+
|
16
|
+
# Open up a new ParamsScope::Attr
|
17
|
+
# @param key [Hash, Symbol] key of attr
|
18
|
+
# @param scope [Grape::Validations::ParamsScope] scope of attr
|
19
|
+
def initialize(key, scope)
|
20
|
+
@key = key
|
21
|
+
@scope = scope
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return Array[Symbol, Hash[Symbol => Array]] declared_params with symbol instead of Attr
|
25
|
+
def self.attrs_keys(declared_params)
|
26
|
+
declared_params.map do |declared_param_attr|
|
27
|
+
attr_key(declared_param_attr)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.attr_key(declared_param_attr)
|
32
|
+
return attr_key(declared_param_attr.key) if declared_param_attr.is_a?(self)
|
33
|
+
|
34
|
+
if declared_param_attr.is_a?(Hash)
|
35
|
+
declared_param_attr.transform_values { |value| attrs_keys(value) }
|
36
|
+
else
|
37
|
+
declared_param_attr
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
11
42
|
# Open up a new ParamsScope, allowing parameter definitions per
|
12
43
|
# Grape::DSL::Params.
|
13
44
|
# @param opts [Hash] options for this scope
|
@@ -31,7 +62,7 @@ module Grape
|
|
31
62
|
@api = opts[:api]
|
32
63
|
@optional = opts[:optional] || false
|
33
64
|
@type = opts[:type]
|
34
|
-
@group = opts[:group]
|
65
|
+
@group = opts[:group]
|
35
66
|
@dependent_on = opts[:dependent_on]
|
36
67
|
@declared_params = []
|
37
68
|
@index = nil
|
@@ -64,6 +95,18 @@ module Grape
|
|
64
95
|
|
65
96
|
return params.any? { |param| meets_dependency?(param, request_params) } if params.is_a?(Array)
|
66
97
|
|
98
|
+
meets_hash_dependency?(params)
|
99
|
+
end
|
100
|
+
|
101
|
+
def attr_meets_dependency?(params)
|
102
|
+
return true unless @dependent_on
|
103
|
+
|
104
|
+
return false if @parent.present? && !@parent.attr_meets_dependency?(params)
|
105
|
+
|
106
|
+
meets_hash_dependency?(params)
|
107
|
+
end
|
108
|
+
|
109
|
+
def meets_hash_dependency?(params)
|
67
110
|
# params might be anything what looks like a hash, so it must implement a `key?` method
|
68
111
|
return false unless params.respond_to?(:key?)
|
69
112
|
|
@@ -128,13 +171,14 @@ module Grape
|
|
128
171
|
# Adds a parameter declaration to our list of validations.
|
129
172
|
# @param attrs [Array] (see Grape::DSL::Parameters#requires)
|
130
173
|
def push_declared_params(attrs, **opts)
|
174
|
+
opts = opts.merge(declared_params_scope: self) unless opts.key?(:declared_params_scope)
|
131
175
|
if lateral?
|
132
176
|
@parent.push_declared_params(attrs, **opts)
|
133
177
|
else
|
134
178
|
push_renamed_param(full_path + [attrs.first], opts[:as]) \
|
135
179
|
if opts && opts[:as]
|
136
180
|
|
137
|
-
@declared_params.concat
|
181
|
+
@declared_params.concat(attrs.map { |attr| ::Grape::Validations::ParamsScope::Attr.new(attr, opts[:declared_params_scope]) })
|
138
182
|
end
|
139
183
|
end
|
140
184
|
|
@@ -206,8 +250,8 @@ module Grape
|
|
206
250
|
# if required params are grouped and no type or unsupported type is provided, raise an error
|
207
251
|
type = attrs[1] ? attrs[1][:type] : nil
|
208
252
|
if attrs.first && !optional
|
209
|
-
raise Grape::Exceptions::
|
210
|
-
raise Grape::Exceptions::
|
253
|
+
raise Grape::Exceptions::MissingGroupType if type.nil?
|
254
|
+
raise Grape::Exceptions::UnsupportedGroupType unless Grape::Validations::Types.group?(type)
|
211
255
|
end
|
212
256
|
|
213
257
|
self.class.new(
|
@@ -269,17 +313,14 @@ module Grape
|
|
269
313
|
end
|
270
314
|
|
271
315
|
def validates(attrs, validations)
|
272
|
-
|
316
|
+
doc = AttributesDoc.new @api, self
|
317
|
+
doc.extract_details validations
|
273
318
|
|
274
319
|
coerce_type = infer_coercion(validations)
|
275
320
|
|
276
|
-
|
277
|
-
|
278
|
-
desc = validations.delete(:desc) || validations.delete(:description)
|
279
|
-
doc_attrs[:desc] = desc if desc
|
321
|
+
doc.type = coerce_type
|
280
322
|
|
281
323
|
default = validations[:default]
|
282
|
-
doc_attrs[:default] = default if validations.key?(:default)
|
283
324
|
|
284
325
|
if (values_hash = validations[:values]).is_a? Hash
|
285
326
|
values = values_hash[:value]
|
@@ -288,7 +329,8 @@ module Grape
|
|
288
329
|
else
|
289
330
|
values = validations[:values]
|
290
331
|
end
|
291
|
-
|
332
|
+
|
333
|
+
doc.values = values
|
292
334
|
|
293
335
|
except_values = options_key?(:except_values, :value, validations) ? validations[:except_values][:value] : validations[:except_values]
|
294
336
|
|
@@ -304,28 +346,22 @@ module Grape
|
|
304
346
|
# type should be compatible with values array, if both exist
|
305
347
|
validate_value_coercion(coerce_type, values, except_values, excepts)
|
306
348
|
|
307
|
-
|
308
|
-
|
309
|
-
document_attribute(attrs, doc_attrs)
|
349
|
+
doc.document attrs
|
310
350
|
|
311
351
|
opts = derive_validator_options(validations)
|
312
352
|
|
313
|
-
order_specific_validations = Set[:as]
|
314
|
-
|
315
353
|
# Validate for presence before any other validators
|
316
|
-
validates_presence(validations, attrs,
|
317
|
-
order_specific_validations << validation_type
|
318
|
-
end
|
354
|
+
validates_presence(validations, attrs, doc, opts)
|
319
355
|
|
320
356
|
# Before we run the rest of the validators, let's handle
|
321
357
|
# whatever coercion so that we are working with correctly
|
322
358
|
# type casted values
|
323
|
-
coerce_type validations, attrs,
|
359
|
+
coerce_type validations, attrs, doc, opts
|
324
360
|
|
325
361
|
validations.each do |type, options|
|
326
|
-
next if
|
362
|
+
next if type == :as
|
327
363
|
|
328
|
-
validate(type, options, attrs,
|
364
|
+
validate(type, options, attrs, doc, opts)
|
329
365
|
end
|
330
366
|
end
|
331
367
|
|
@@ -389,7 +425,7 @@ module Grape
|
|
389
425
|
# composited from more than one +requires+/+optional+
|
390
426
|
# parameter, and needs to be run before most other
|
391
427
|
# validations.
|
392
|
-
def coerce_type(validations, attrs,
|
428
|
+
def coerce_type(validations, attrs, doc, opts)
|
393
429
|
check_coerce_with(validations)
|
394
430
|
|
395
431
|
return unless validations.key?(:coerce)
|
@@ -399,7 +435,7 @@ module Grape
|
|
399
435
|
method: validations[:coerce_with],
|
400
436
|
message: validations[:coerce_message]
|
401
437
|
}
|
402
|
-
validate('coerce', coerce_options, attrs,
|
438
|
+
validate('coerce', coerce_options, attrs, doc, opts)
|
403
439
|
validations.delete(:coerce_with)
|
404
440
|
validations.delete(:coerce)
|
405
441
|
validations.delete(:coerce_message)
|
@@ -430,18 +466,14 @@ module Grape
|
|
430
466
|
unless Array(default).none? { |def_val| excepts.include?(def_val) }
|
431
467
|
end
|
432
468
|
|
433
|
-
def validate(type, options, attrs,
|
434
|
-
validator_class = Validations.validators[type.to_s]
|
435
|
-
|
436
|
-
raise Grape::Exceptions::UnknownValidator.new(type) unless validator_class
|
437
|
-
|
469
|
+
def validate(type, options, attrs, doc, opts)
|
438
470
|
validator_options = {
|
439
471
|
attributes: attrs,
|
440
472
|
options: options,
|
441
|
-
required:
|
473
|
+
required: doc.required,
|
442
474
|
params_scope: self,
|
443
475
|
opts: opts,
|
444
|
-
validator_class:
|
476
|
+
validator_class: Validations.require_validator(type)
|
445
477
|
}
|
446
478
|
@api.namespace_stackable(:validations, validator_options)
|
447
479
|
end
|
@@ -453,7 +485,7 @@ module Grape
|
|
453
485
|
values_list.each do |values|
|
454
486
|
next if !values || values.is_a?(Proc)
|
455
487
|
|
456
|
-
value_types = values.is_a?(Range) ? [values.begin, values.end] : values
|
488
|
+
value_types = values.is_a?(Range) ? [values.begin, values.end].compact : values
|
457
489
|
value_types = value_types.map { |type| Grape::API::Boolean.build(type) } if coerce_type == Grape::API::Boolean
|
458
490
|
raise Grape::Exceptions::IncompatibleOptionValues.new(:type, coerce_type, :values, values) unless value_types.all?(coerce_type)
|
459
491
|
end
|
@@ -485,17 +517,11 @@ module Grape
|
|
485
517
|
}
|
486
518
|
end
|
487
519
|
|
488
|
-
def validates_presence(validations, attrs,
|
520
|
+
def validates_presence(validations, attrs, doc, opts)
|
489
521
|
return unless validations.key?(:presence) && validations[:presence]
|
490
522
|
|
491
|
-
validate(:presence, validations
|
492
|
-
|
493
|
-
yield :message if validations.key?(:message)
|
494
|
-
end
|
495
|
-
|
496
|
-
def document_attribute(attrs, doc_attrs)
|
497
|
-
full_attrs = attrs.collect { |name| { name: name, full_name: full_name(name) } }
|
498
|
-
@api.document_attribute(full_attrs, doc_attrs)
|
523
|
+
validate(:presence, validations.delete(:presence), attrs, doc, opts)
|
524
|
+
validations.delete(:message) if validations.key?(:message)
|
499
525
|
end
|
500
526
|
end
|
501
527
|
end
|
@@ -14,8 +14,6 @@ module Grape
|
|
14
14
|
# behavior of Virtus which was used earlier, a `Grape::Validations::Types::PrimitiveCoercer`
|
15
15
|
# maintains Virtus behavior in coercing.
|
16
16
|
class ArrayCoercer < DryTypeCoercer
|
17
|
-
register_collection Array
|
18
|
-
|
19
17
|
def initialize(type, strict = false)
|
20
18
|
super
|
21
19
|
|