grape 1.8.0 → 2.1.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 +4 -4
- data/CHANGELOG.md +65 -1
- data/README.md +377 -334
- data/UPGRADING.md +231 -6
- data/grape.gemspec +6 -10
- data/lib/grape/api/instance.rb +13 -10
- data/lib/grape/api.rb +17 -8
- data/lib/grape/content_types.rb +0 -2
- data/lib/grape/cookies.rb +2 -1
- data/lib/grape/dry_types.rb +0 -2
- data/lib/grape/dsl/desc.rb +23 -21
- data/lib/grape/dsl/headers.rb +1 -1
- data/lib/grape/dsl/inside_route.rb +47 -22
- data/lib/grape/dsl/parameters.rb +4 -3
- data/lib/grape/dsl/routing.rb +20 -4
- data/lib/grape/dsl/validations.rb +13 -0
- data/lib/grape/endpoint.rb +15 -10
- data/lib/grape/{util/env.rb → env.rb} +0 -5
- data/lib/grape/error_formatter/txt.rb +11 -10
- data/lib/grape/exceptions/base.rb +3 -3
- data/lib/grape/exceptions/missing_group_type.rb +1 -1
- data/lib/grape/exceptions/unsupported_group_type.rb +1 -1
- data/lib/grape/exceptions/validation.rb +0 -2
- data/lib/grape/exceptions/validation_array_errors.rb +1 -0
- data/lib/grape/exceptions/validation_errors.rb +1 -3
- data/lib/grape/extensions/hash.rb +5 -1
- data/lib/grape/http/headers.rb +18 -24
- data/lib/grape/{util/json.rb → json.rb} +1 -3
- data/lib/grape/locale/en.yml +3 -0
- data/lib/grape/middleware/auth/base.rb +0 -2
- data/lib/grape/middleware/auth/dsl.rb +0 -2
- data/lib/grape/middleware/auth/strategies.rb +1 -2
- data/lib/grape/middleware/base.rb +0 -2
- data/lib/grape/middleware/error.rb +55 -50
- data/lib/grape/middleware/formatter.rb +21 -18
- data/lib/grape/middleware/globals.rb +1 -3
- data/lib/grape/middleware/stack.rb +2 -3
- data/lib/grape/middleware/versioner/accept_version_header.rb +0 -2
- data/lib/grape/middleware/versioner/header.rb +17 -163
- data/lib/grape/middleware/versioner/param.rb +2 -4
- data/lib/grape/middleware/versioner/path.rb +1 -3
- data/lib/grape/namespace.rb +3 -4
- data/lib/grape/path.rb +24 -29
- data/lib/grape/railtie.rb +9 -0
- data/lib/grape/request.rb +3 -5
- data/lib/grape/router/base_route.rb +39 -0
- data/lib/grape/router/greedy_route.rb +20 -0
- data/lib/grape/router/pattern.rb +39 -30
- data/lib/grape/router/route.rb +22 -59
- data/lib/grape/router.rb +30 -36
- data/lib/grape/util/accept/header.rb +19 -0
- data/lib/grape/util/accept_header_handler.rb +105 -0
- data/lib/grape/util/base_inheritable.rb +4 -4
- data/lib/grape/util/cache.rb +0 -3
- data/lib/grape/util/endpoint_configuration.rb +1 -1
- data/lib/grape/util/header.rb +13 -0
- data/lib/grape/util/inheritable_values.rb +0 -2
- data/lib/grape/util/lazy/block.rb +29 -0
- data/lib/grape/util/lazy/object.rb +45 -0
- data/lib/grape/util/lazy/value.rb +38 -0
- data/lib/grape/util/lazy/value_array.rb +21 -0
- data/lib/grape/util/lazy/value_enumerable.rb +34 -0
- data/lib/grape/util/lazy/value_hash.rb +21 -0
- data/lib/grape/util/media_type.rb +70 -0
- data/lib/grape/util/reverse_stackable_values.rb +1 -6
- data/lib/grape/util/stackable_values.rb +1 -6
- data/lib/grape/util/strict_hash_configuration.rb +3 -3
- data/lib/grape/validations/attributes_doc.rb +38 -36
- data/lib/grape/validations/contract_scope.rb +71 -0
- data/lib/grape/validations/params_scope.rb +10 -9
- data/lib/grape/validations/types/array_coercer.rb +0 -2
- data/lib/grape/validations/types/build_coercer.rb +69 -71
- data/lib/grape/validations/types/dry_type_coercer.rb +1 -11
- data/lib/grape/validations/types/json.rb +0 -2
- data/lib/grape/validations/types/primitive_coercer.rb +0 -2
- data/lib/grape/validations/types/set_coercer.rb +0 -3
- data/lib/grape/validations/types.rb +0 -3
- data/lib/grape/validations/validators/base.rb +2 -1
- data/lib/grape/validations/validators/default_validator.rb +5 -1
- data/lib/grape/validations/validators/length_validator.rb +42 -0
- data/lib/grape/validations/validators/values_validator.rb +8 -3
- data/lib/grape/validations.rb +3 -7
- data/lib/grape/version.rb +1 -1
- data/lib/grape/{util/xml.rb → xml.rb} +1 -1
- data/lib/grape.rb +38 -269
- metadata +33 -274
- data/lib/grape/eager_load.rb +0 -20
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +0 -24
- data/lib/grape/router/attribute_translator.rb +0 -63
- data/lib/grape/util/lazy_block.rb +0 -27
- data/lib/grape/util/lazy_object.rb +0 -43
- data/lib/grape/util/lazy_value.rb +0 -91
- data/spec/grape/api/custom_validations_spec.rb +0 -213
- data/spec/grape/api/deeply_included_options_spec.rb +0 -56
- data/spec/grape/api/defines_boolean_in_params_spec.rb +0 -38
- data/spec/grape/api/documentation_spec.rb +0 -59
- data/spec/grape/api/inherited_helpers_spec.rb +0 -114
- data/spec/grape/api/instance_spec.rb +0 -103
- data/spec/grape/api/invalid_format_spec.rb +0 -45
- data/spec/grape/api/namespace_parameters_in_route_spec.rb +0 -38
- data/spec/grape/api/nested_helpers_spec.rb +0 -50
- data/spec/grape/api/optional_parameters_in_route_spec.rb +0 -43
- data/spec/grape/api/parameters_modification_spec.rb +0 -41
- data/spec/grape/api/patch_method_helpers_spec.rb +0 -79
- data/spec/grape/api/recognize_path_spec.rb +0 -21
- data/spec/grape/api/required_parameters_in_route_spec.rb +0 -37
- data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +0 -26
- data/spec/grape/api/routes_with_requirements_spec.rb +0 -59
- data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +0 -41
- data/spec/grape/api/shared_helpers_spec.rb +0 -36
- data/spec/grape/api_remount_spec.rb +0 -509
- data/spec/grape/api_spec.rb +0 -4356
- data/spec/grape/dsl/callbacks_spec.rb +0 -45
- data/spec/grape/dsl/desc_spec.rb +0 -98
- data/spec/grape/dsl/headers_spec.rb +0 -62
- data/spec/grape/dsl/helpers_spec.rb +0 -100
- data/spec/grape/dsl/inside_route_spec.rb +0 -531
- data/spec/grape/dsl/logger_spec.rb +0 -24
- data/spec/grape/dsl/middleware_spec.rb +0 -60
- data/spec/grape/dsl/parameters_spec.rb +0 -180
- data/spec/grape/dsl/request_response_spec.rb +0 -225
- data/spec/grape/dsl/routing_spec.rb +0 -275
- data/spec/grape/dsl/settings_spec.rb +0 -261
- data/spec/grape/dsl/validations_spec.rb +0 -55
- data/spec/grape/endpoint/declared_spec.rb +0 -846
- data/spec/grape/endpoint_spec.rb +0 -1085
- data/spec/grape/entity_spec.rb +0 -336
- data/spec/grape/exceptions/base_spec.rb +0 -81
- data/spec/grape/exceptions/body_parse_errors_spec.rb +0 -185
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +0 -358
- data/spec/grape/exceptions/invalid_formatter_spec.rb +0 -15
- data/spec/grape/exceptions/invalid_response_spec.rb +0 -11
- data/spec/grape/exceptions/invalid_versioner_option_spec.rb +0 -15
- data/spec/grape/exceptions/missing_group_type_spec.rb +0 -17
- data/spec/grape/exceptions/missing_mime_type_spec.rb +0 -17
- data/spec/grape/exceptions/missing_option_spec.rb +0 -15
- data/spec/grape/exceptions/unknown_options_spec.rb +0 -15
- data/spec/grape/exceptions/unknown_validator_spec.rb +0 -15
- data/spec/grape/exceptions/unsupported_group_type_spec.rb +0 -19
- data/spec/grape/exceptions/validation_errors_spec.rb +0 -92
- data/spec/grape/exceptions/validation_spec.rb +0 -19
- data/spec/grape/extensions/param_builders/hash_spec.rb +0 -83
- data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +0 -105
- data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +0 -79
- data/spec/grape/grape_spec.rb +0 -9
- data/spec/grape/integration/global_namespace_function_spec.rb +0 -29
- data/spec/grape/integration/rack_sendfile_spec.rb +0 -48
- data/spec/grape/integration/rack_spec.rb +0 -51
- data/spec/grape/loading_spec.rb +0 -44
- data/spec/grape/middleware/auth/base_spec.rb +0 -31
- data/spec/grape/middleware/auth/dsl_spec.rb +0 -60
- data/spec/grape/middleware/auth/strategies_spec.rb +0 -120
- data/spec/grape/middleware/base_spec.rb +0 -221
- data/spec/grape/middleware/error_spec.rb +0 -85
- data/spec/grape/middleware/exception_spec.rb +0 -294
- data/spec/grape/middleware/formatter_spec.rb +0 -461
- data/spec/grape/middleware/globals_spec.rb +0 -30
- data/spec/grape/middleware/stack_spec.rb +0 -155
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +0 -122
- data/spec/grape/middleware/versioner/header_spec.rb +0 -345
- data/spec/grape/middleware/versioner/param_spec.rb +0 -171
- data/spec/grape/middleware/versioner/path_spec.rb +0 -62
- data/spec/grape/middleware/versioner_spec.rb +0 -21
- data/spec/grape/named_api_spec.rb +0 -19
- data/spec/grape/parser_spec.rb +0 -86
- data/spec/grape/path_spec.rb +0 -252
- data/spec/grape/presenters/presenter_spec.rb +0 -71
- data/spec/grape/request_spec.rb +0 -126
- data/spec/grape/util/inheritable_setting_spec.rb +0 -242
- data/spec/grape/util/inheritable_values_spec.rb +0 -79
- data/spec/grape/util/reverse_stackable_values_spec.rb +0 -134
- data/spec/grape/util/stackable_values_spec.rb +0 -128
- data/spec/grape/util/strict_hash_configuration_spec.rb +0 -38
- data/spec/grape/validations/attributes_doc_spec.rb +0 -153
- data/spec/grape/validations/instance_behaivour_spec.rb +0 -43
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +0 -38
- data/spec/grape/validations/params_scope_spec.rb +0 -1420
- data/spec/grape/validations/single_attribute_iterator_spec.rb +0 -56
- data/spec/grape/validations/types/array_coercer_spec.rb +0 -33
- data/spec/grape/validations/types/primitive_coercer_spec.rb +0 -150
- data/spec/grape/validations/types/set_coercer_spec.rb +0 -32
- data/spec/grape/validations/types_spec.rb +0 -111
- data/spec/grape/validations/validators/all_or_none_spec.rb +0 -162
- data/spec/grape/validations/validators/allow_blank_spec.rb +0 -575
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +0 -205
- data/spec/grape/validations/validators/base_spec.rb +0 -38
- data/spec/grape/validations/validators/coerce_spec.rb +0 -1261
- data/spec/grape/validations/validators/default_spec.rb +0 -463
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +0 -233
- data/spec/grape/validations/validators/except_values_spec.rb +0 -192
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +0 -214
- data/spec/grape/validations/validators/presence_spec.rb +0 -315
- data/spec/grape/validations/validators/regexp_spec.rb +0 -161
- data/spec/grape/validations/validators/same_as_spec.rb +0 -57
- data/spec/grape/validations/validators/values_spec.rb +0 -733
- data/spec/grape/validations/validators/zh-CN.yml +0 -10
- data/spec/grape/validations_spec.rb +0 -2030
- data/spec/integration/eager_load/eager_load_spec.rb +0 -15
- data/spec/integration/multi_json/json_spec.rb +0 -7
- data/spec/integration/multi_xml/xml_spec.rb +0 -7
- data/spec/shared/deprecated_class_examples.rb +0 -16
- data/spec/shared/versioning_examples.rb +0 -215
- data/spec/spec_helper.rb +0 -52
- data/spec/support/basic_auth_encode_helpers.rb +0 -11
- data/spec/support/chunks.rb +0 -14
- data/spec/support/content_type_helpers.rb +0 -15
- data/spec/support/endpoint_faker.rb +0 -25
- data/spec/support/file_streamer.rb +0 -13
- data/spec/support/integer_helpers.rb +0 -13
- data/spec/support/versioned_helpers.rb +0 -55
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'grape/middleware/base'
|
|
4
|
-
|
|
5
3
|
module Grape
|
|
6
4
|
module Middleware
|
|
7
5
|
class Formatter < Base
|
|
8
6
|
CHUNKED = 'chunked'
|
|
7
|
+
FORMAT = 'format'
|
|
9
8
|
|
|
10
9
|
def default_options
|
|
11
10
|
{
|
|
@@ -26,7 +25,7 @@ module Grape
|
|
|
26
25
|
status, headers, bodies = *@app_response
|
|
27
26
|
|
|
28
27
|
if Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status)
|
|
29
|
-
|
|
28
|
+
[status, headers, []]
|
|
30
29
|
else
|
|
31
30
|
build_formatted_response(status, headers, bodies)
|
|
32
31
|
end
|
|
@@ -54,7 +53,7 @@ module Grape
|
|
|
54
53
|
end
|
|
55
54
|
|
|
56
55
|
def fetch_formatter(headers, options)
|
|
57
|
-
api_format = mime_types[headers[
|
|
56
|
+
api_format = mime_types[headers[Rack::CONTENT_TYPE]] || env[Grape::Env::API_FORMAT]
|
|
58
57
|
Grape::Formatter.formatter_for(api_format, **options)
|
|
59
58
|
end
|
|
60
59
|
|
|
@@ -63,10 +62,10 @@ module Grape
|
|
|
63
62
|
# @param headers [Hash]
|
|
64
63
|
# @return [Hash]
|
|
65
64
|
def ensure_content_type(headers)
|
|
66
|
-
if headers[
|
|
65
|
+
if headers[Rack::CONTENT_TYPE]
|
|
67
66
|
headers
|
|
68
67
|
else
|
|
69
|
-
headers.merge(
|
|
68
|
+
headers.merge(Rack::CONTENT_TYPE => content_type_for(env[Grape::Env::API_FORMAT]))
|
|
70
69
|
end
|
|
71
70
|
end
|
|
72
71
|
|
|
@@ -82,14 +81,14 @@ module Grape
|
|
|
82
81
|
!request.parseable_data? &&
|
|
83
82
|
(request.content_length.to_i.positive? || request.env[Grape::Http::Headers::HTTP_TRANSFER_ENCODING] == CHUNKED)
|
|
84
83
|
|
|
85
|
-
return unless (input = env[
|
|
84
|
+
return unless (input = env[Rack::RACK_INPUT])
|
|
86
85
|
|
|
87
|
-
input
|
|
86
|
+
rewind_input input
|
|
88
87
|
body = env[Grape::Env::API_REQUEST_INPUT] = input.read
|
|
89
88
|
begin
|
|
90
89
|
read_rack_input(body) if body && !body.empty?
|
|
91
90
|
ensure
|
|
92
|
-
input
|
|
91
|
+
rewind_input input
|
|
93
92
|
end
|
|
94
93
|
end
|
|
95
94
|
|
|
@@ -103,12 +102,12 @@ module Grape
|
|
|
103
102
|
begin
|
|
104
103
|
body = (env[Grape::Env::API_REQUEST_BODY] = parser.call(body, env))
|
|
105
104
|
if body.is_a?(Hash)
|
|
106
|
-
env[
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
env[
|
|
105
|
+
env[Rack::RACK_REQUEST_FORM_HASH] = if env.key?(Rack::RACK_REQUEST_FORM_HASH)
|
|
106
|
+
env[Rack::RACK_REQUEST_FORM_HASH].merge(body)
|
|
107
|
+
else
|
|
108
|
+
body
|
|
109
|
+
end
|
|
110
|
+
env[Rack::RACK_REQUEST_FORM_INPUT] = env[Rack::RACK_INPUT]
|
|
112
111
|
end
|
|
113
112
|
rescue Grape::Exceptions::Base => e
|
|
114
113
|
raise e
|
|
@@ -141,7 +140,7 @@ module Grape
|
|
|
141
140
|
end
|
|
142
141
|
|
|
143
142
|
def format_from_params
|
|
144
|
-
fmt = Rack::Utils.parse_nested_query(env[
|
|
143
|
+
fmt = Rack::Utils.parse_nested_query(env[Rack::QUERY_STRING])[FORMAT]
|
|
145
144
|
# avoid symbol memory leak on an unknown format
|
|
146
145
|
return fmt.to_sym if content_type_for(fmt)
|
|
147
146
|
|
|
@@ -164,16 +163,20 @@ module Grape
|
|
|
164
163
|
\w+/[\w+.-]+) # eg application/vnd.example.myformat+xml
|
|
165
164
|
(?:
|
|
166
165
|
(?:;[^,]*?)? # optionally multiple formats in a row
|
|
167
|
-
;\s*q=([\
|
|
166
|
+
;\s*q=([\w.]+) # optional "quality" preference (eg q=0.5)
|
|
168
167
|
)?
|
|
169
168
|
}x
|
|
170
169
|
|
|
171
170
|
vendor_prefix_pattern = /vnd\.[^+]+\+/
|
|
172
171
|
|
|
173
172
|
accept.scan(accept_into_mime_and_quality)
|
|
174
|
-
.sort_by { |_, quality_preference| -quality_preference.to_f }
|
|
173
|
+
.sort_by { |_, quality_preference| -(quality_preference ? quality_preference.to_f : 1.0) }
|
|
175
174
|
.flat_map { |mime, _| [mime, mime.sub(vendor_prefix_pattern, '')] }
|
|
176
175
|
end
|
|
176
|
+
|
|
177
|
+
def rewind_input(input)
|
|
178
|
+
input.rewind if input.respond_to?(:rewind)
|
|
179
|
+
end
|
|
177
180
|
end
|
|
178
181
|
end
|
|
179
182
|
end
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'grape/middleware/base'
|
|
4
|
-
|
|
5
3
|
module Grape
|
|
6
4
|
module Middleware
|
|
7
5
|
class Globals < Base
|
|
@@ -9,7 +7,7 @@ module Grape
|
|
|
9
7
|
request = Grape::Request.new(@env, build_params_with: @options[:build_params_with])
|
|
10
8
|
@env[Grape::Env::GRAPE_REQUEST] = request
|
|
11
9
|
@env[Grape::Env::GRAPE_REQUEST_HEADERS] = request.headers
|
|
12
|
-
@env[Grape::Env::GRAPE_REQUEST_PARAMS] = request.params if @env[
|
|
10
|
+
@env[Grape::Env::GRAPE_REQUEST_PARAMS] = request.params if @env[Rack::RACK_INPUT]
|
|
13
11
|
end
|
|
14
12
|
end
|
|
15
13
|
end
|
|
@@ -76,11 +76,10 @@ module Grape
|
|
|
76
76
|
end
|
|
77
77
|
ruby2_keywords :insert_after if respond_to?(:ruby2_keywords, true)
|
|
78
78
|
|
|
79
|
-
def use(
|
|
80
|
-
middleware = self.class::Middleware.new(
|
|
79
|
+
def use(...)
|
|
80
|
+
middleware = self.class::Middleware.new(...)
|
|
81
81
|
middlewares.push(middleware)
|
|
82
82
|
end
|
|
83
|
-
ruby2_keywords :use if respond_to?(:ruby2_keywords, true)
|
|
84
83
|
|
|
85
84
|
def merge_with(middleware_specs)
|
|
86
85
|
middleware_specs.each do |operation, *args|
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'grape/middleware/base'
|
|
4
|
-
require 'grape/middleware/versioner/parse_media_type_patch'
|
|
5
|
-
|
|
6
3
|
module Grape
|
|
7
4
|
module Middleware
|
|
8
5
|
module Versioner
|
|
@@ -25,169 +22,26 @@ module Grape
|
|
|
25
22
|
# X-Cascade header to alert Grape::Router to attempt the next matched
|
|
26
23
|
# route.
|
|
27
24
|
class Header < Base
|
|
28
|
-
VENDOR_VERSION_HEADER_REGEX =
|
|
29
|
-
/\Avnd\.([a-z0-9.\-_!#{Regexp.last_match(0)}\^]+?)(?:-([a-z0-9*.]+))?(?:\+([a-z0-9*\-.]+))?\z/.freeze
|
|
30
|
-
|
|
31
|
-
HAS_VENDOR_REGEX = /\Avnd\.[a-z0-9.\-_!#{Regexp.last_match(0)}\^]+/.freeze
|
|
32
|
-
HAS_VERSION_REGEX = /\Avnd\.([a-z0-9.\-_!#{Regexp.last_match(0)}\^]+?)(?:-([a-z0-9*.]+))+/.freeze
|
|
33
|
-
|
|
34
25
|
def before
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def strict_accept_header_presence_check
|
|
54
|
-
return unless header.qvalues.empty?
|
|
55
|
-
|
|
56
|
-
fail_with_invalid_accept_header!('Accept header must be set.')
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def strict_version_vendor_accept_header_presence_check
|
|
60
|
-
return if versions.blank? || an_accept_header_with_version_and_vendor_is_present?
|
|
61
|
-
|
|
62
|
-
fail_with_invalid_accept_header!('API vendor or version not found.')
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def an_accept_header_with_version_and_vendor_is_present?
|
|
66
|
-
header.qvalues.keys.any? do |h|
|
|
67
|
-
VENDOR_VERSION_HEADER_REGEX.match?(h.sub('application/', ''))
|
|
26
|
+
handler = Grape::Util::AcceptHeaderHandler.new(
|
|
27
|
+
accept_header: env[Grape::Http::Headers::HTTP_ACCEPT],
|
|
28
|
+
versions: options[:versions],
|
|
29
|
+
**options.fetch(:version_options) { {} }
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
handler.match_best_quality_media_type!(
|
|
33
|
+
content_types: content_types,
|
|
34
|
+
allowed_methods: env[Grape::Env::GRAPE_ALLOWED_METHODS]
|
|
35
|
+
) do |media_type|
|
|
36
|
+
env.update(
|
|
37
|
+
Grape::Env::API_TYPE => media_type.type,
|
|
38
|
+
Grape::Env::API_SUBTYPE => media_type.subtype,
|
|
39
|
+
Grape::Env::API_VENDOR => media_type.vendor,
|
|
40
|
+
Grape::Env::API_VERSION => media_type.version,
|
|
41
|
+
Grape::Env::API_FORMAT => media_type.format
|
|
42
|
+
)
|
|
68
43
|
end
|
|
69
44
|
end
|
|
70
|
-
|
|
71
|
-
def header
|
|
72
|
-
@header ||= rack_accept_header
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
def media_type
|
|
76
|
-
@media_type ||= header.best_of(available_media_types)
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
def media_type_header_handler
|
|
80
|
-
type, subtype = Rack::Accept::Header.parse_media_type(media_type)
|
|
81
|
-
env[Grape::Env::API_TYPE] = type
|
|
82
|
-
env[Grape::Env::API_SUBTYPE] = subtype
|
|
83
|
-
|
|
84
|
-
return unless VENDOR_VERSION_HEADER_REGEX =~ subtype
|
|
85
|
-
|
|
86
|
-
env[Grape::Env::API_VENDOR] = Regexp.last_match[1]
|
|
87
|
-
env[Grape::Env::API_VERSION] = Regexp.last_match[2]
|
|
88
|
-
# weird that Grape::Middleware::Formatter also does this
|
|
89
|
-
env[Grape::Env::API_FORMAT] = Regexp.last_match[3]
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def fail_with_invalid_accept_header!(message)
|
|
93
|
-
raise Grape::Exceptions::InvalidAcceptHeader
|
|
94
|
-
.new(message, error_headers)
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
def fail_with_invalid_version_header!(message)
|
|
98
|
-
raise Grape::Exceptions::InvalidVersionHeader
|
|
99
|
-
.new(message, error_headers)
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def available_media_types
|
|
103
|
-
[].tap do |available_media_types|
|
|
104
|
-
content_types.each_key do |extension|
|
|
105
|
-
versions.reverse_each do |version|
|
|
106
|
-
available_media_types << "application/vnd.#{vendor}-#{version}+#{extension}"
|
|
107
|
-
available_media_types << "application/vnd.#{vendor}-#{version}"
|
|
108
|
-
end
|
|
109
|
-
available_media_types << "application/vnd.#{vendor}+#{extension}"
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
available_media_types << "application/vnd.#{vendor}"
|
|
113
|
-
available_media_types.concat(content_types.values.flatten)
|
|
114
|
-
end
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
def headers_contain_wrong_vendor?
|
|
118
|
-
header.values.all? do |header_value|
|
|
119
|
-
vendor?(header_value) && request_vendor(header_value) != vendor
|
|
120
|
-
end
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
def headers_contain_wrong_version?
|
|
124
|
-
header.values.all? do |header_value|
|
|
125
|
-
version?(header_value) && versions.exclude?(request_version(header_value))
|
|
126
|
-
end
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
def rack_accept_header
|
|
130
|
-
Rack::Accept::MediaType.new env[Grape::Http::Headers::HTTP_ACCEPT]
|
|
131
|
-
rescue RuntimeError => e
|
|
132
|
-
fail_with_invalid_accept_header!(e.message)
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
def versions
|
|
136
|
-
options[:versions] || []
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
def vendor
|
|
140
|
-
version_options && version_options[:vendor]
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
def strict?
|
|
144
|
-
version_options && version_options[:strict]
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
def version_options
|
|
148
|
-
options[:version_options]
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
# By default those errors contain an `X-Cascade` header set to `pass`,
|
|
152
|
-
# which allows nesting and stacking of routes
|
|
153
|
-
# (see Grape::Router for more
|
|
154
|
-
# information). To prevent # this behavior, and not add the `X-Cascade`
|
|
155
|
-
# header, one can set the `:cascade` option to `false`.
|
|
156
|
-
def cascade?
|
|
157
|
-
if version_options&.key?(:cascade)
|
|
158
|
-
version_options[:cascade]
|
|
159
|
-
else
|
|
160
|
-
true
|
|
161
|
-
end
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
def error_headers
|
|
165
|
-
cascade? ? { Grape::Http::Headers::X_CASCADE => 'pass' } : {}
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
# @param [String] media_type a content type
|
|
169
|
-
# @return [Boolean] whether the content type sets a vendor
|
|
170
|
-
def vendor?(media_type)
|
|
171
|
-
_, subtype = Rack::Accept::Header.parse_media_type(media_type)
|
|
172
|
-
subtype.present? && subtype[HAS_VENDOR_REGEX]
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
def request_vendor(media_type)
|
|
176
|
-
_, subtype = Rack::Accept::Header.parse_media_type(media_type)
|
|
177
|
-
subtype.match(VENDOR_VERSION_HEADER_REGEX)[1]
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
def request_version(media_type)
|
|
181
|
-
_, subtype = Rack::Accept::Header.parse_media_type(media_type)
|
|
182
|
-
subtype.match(VENDOR_VERSION_HEADER_REGEX)[2]
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
# @param [String] media_type a content type
|
|
186
|
-
# @return [Boolean] whether the content type sets an API version
|
|
187
|
-
def version?(media_type)
|
|
188
|
-
_, subtype = Rack::Accept::Header.parse_media_type(media_type)
|
|
189
|
-
subtype.present? && subtype[HAS_VERSION_REGEX]
|
|
190
|
-
end
|
|
191
45
|
end
|
|
192
46
|
end
|
|
193
47
|
end
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'grape/middleware/base'
|
|
4
|
-
|
|
5
3
|
module Grape
|
|
6
4
|
module Middleware
|
|
7
5
|
module Versioner
|
|
@@ -30,12 +28,12 @@ module Grape
|
|
|
30
28
|
end
|
|
31
29
|
|
|
32
30
|
def before
|
|
33
|
-
potential_version = Rack::Utils.parse_nested_query(env[
|
|
31
|
+
potential_version = Rack::Utils.parse_nested_query(env[Rack::QUERY_STRING])[paramkey]
|
|
34
32
|
return if potential_version.nil?
|
|
35
33
|
|
|
36
34
|
throw :error, status: 404, message: '404 API Version Not Found', headers: { Grape::Http::Headers::X_CASCADE => 'pass' } if options[:versions] && !options[:versions].find { |v| v.to_s == potential_version }
|
|
37
35
|
env[Grape::Env::API_VERSION] = potential_version
|
|
38
|
-
env[
|
|
36
|
+
env[Rack::RACK_REQUEST_QUERY_HASH].delete(paramkey) if env.key? Rack::RACK_REQUEST_QUERY_HASH
|
|
39
37
|
end
|
|
40
38
|
|
|
41
39
|
private
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'grape/middleware/base'
|
|
4
|
-
|
|
5
3
|
module Grape
|
|
6
4
|
module Middleware
|
|
7
5
|
module Versioner
|
|
@@ -26,7 +24,7 @@ module Grape
|
|
|
26
24
|
end
|
|
27
25
|
|
|
28
26
|
def before
|
|
29
|
-
path = env[
|
|
27
|
+
path = env[Rack::PATH_INFO].dup
|
|
30
28
|
path.sub!(mount_path, '') if mounted_path?(path)
|
|
31
29
|
|
|
32
30
|
if prefix && path.index(prefix) == 0 # rubocop:disable all
|
data/lib/grape/namespace.rb
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'grape/util/cache'
|
|
4
|
-
|
|
5
3
|
module Grape
|
|
6
4
|
# A container for endpoints or other namespaces, which allows for both
|
|
7
5
|
# logical grouping of endpoints as well as sharing common configuration.
|
|
@@ -33,13 +31,14 @@ module Grape
|
|
|
33
31
|
# Join the namespaces from a list of settings to create a path prefix.
|
|
34
32
|
# @param settings [Array] list of Grape::Util::InheritableSettings.
|
|
35
33
|
def self.joined_space_path(settings)
|
|
36
|
-
|
|
34
|
+
JoinedSpaceCache[joined_space(settings)]
|
|
37
35
|
end
|
|
38
36
|
|
|
39
37
|
class JoinedSpaceCache < Grape::Util::Cache
|
|
40
38
|
def initialize
|
|
39
|
+
super
|
|
41
40
|
@cache = Hash.new do |h, joined_space|
|
|
42
|
-
h[joined_space] =
|
|
41
|
+
h[joined_space] = Grape::Router.normalize_path(joined_space.join('/'))
|
|
43
42
|
end
|
|
44
43
|
end
|
|
45
44
|
end
|
data/lib/grape/path.rb
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'grape/util/cache'
|
|
4
|
-
|
|
5
3
|
module Grape
|
|
6
4
|
# Represents a path to an endpoint.
|
|
7
5
|
class Path
|
|
8
|
-
def self.prepare(raw_path, namespace, settings)
|
|
9
|
-
Path.new(raw_path, namespace, settings)
|
|
10
|
-
end
|
|
11
|
-
|
|
12
6
|
attr_reader :raw_path, :namespace, :settings
|
|
13
7
|
|
|
14
8
|
def initialize(raw_path, namespace, settings)
|
|
@@ -22,31 +16,27 @@ module Grape
|
|
|
22
16
|
end
|
|
23
17
|
|
|
24
18
|
def root_prefix
|
|
25
|
-
|
|
19
|
+
settings[:root_prefix]
|
|
26
20
|
end
|
|
27
21
|
|
|
28
22
|
def uses_specific_format?
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
false
|
|
33
|
-
end
|
|
23
|
+
return false unless settings.key?(:format) && settings.key?(:content_types)
|
|
24
|
+
|
|
25
|
+
settings[:format] && Array(settings[:content_types]).size == 1
|
|
34
26
|
end
|
|
35
27
|
|
|
36
28
|
def uses_path_versioning?
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
false
|
|
41
|
-
end
|
|
29
|
+
return false unless settings.key?(:version) && settings[:version_options]&.key?(:using)
|
|
30
|
+
|
|
31
|
+
settings[:version] && settings[:version_options][:using] == :path
|
|
42
32
|
end
|
|
43
33
|
|
|
44
34
|
def namespace?
|
|
45
|
-
namespace&.match?(/^\S/) && namespace
|
|
35
|
+
namespace&.match?(/^\S/) && not_slash?(namespace)
|
|
46
36
|
end
|
|
47
37
|
|
|
48
38
|
def path?
|
|
49
|
-
raw_path&.match?(/^\S/) && raw_path
|
|
39
|
+
raw_path&.match?(/^\S/) && not_slash?(raw_path)
|
|
50
40
|
end
|
|
51
41
|
|
|
52
42
|
def suffix
|
|
@@ -60,7 +50,7 @@ module Grape
|
|
|
60
50
|
end
|
|
61
51
|
|
|
62
52
|
def path
|
|
63
|
-
|
|
53
|
+
PartsCache[parts]
|
|
64
54
|
end
|
|
65
55
|
|
|
66
56
|
def path_with_suffix
|
|
@@ -75,24 +65,29 @@ module Grape
|
|
|
75
65
|
|
|
76
66
|
class PartsCache < Grape::Util::Cache
|
|
77
67
|
def initialize
|
|
68
|
+
super
|
|
78
69
|
@cache = Hash.new do |h, parts|
|
|
79
|
-
h[parts] =
|
|
70
|
+
h[parts] = Grape::Router.normalize_path(parts.join('/'))
|
|
80
71
|
end
|
|
81
72
|
end
|
|
82
73
|
end
|
|
83
74
|
|
|
84
75
|
def parts
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
76
|
+
[].tap do |parts|
|
|
77
|
+
add_part(parts, mount_path)
|
|
78
|
+
add_part(parts, root_prefix)
|
|
79
|
+
parts << ':version' if uses_path_versioning?
|
|
80
|
+
add_part(parts, namespace)
|
|
81
|
+
add_part(parts, raw_path)
|
|
82
|
+
end
|
|
90
83
|
end
|
|
91
84
|
|
|
92
|
-
def
|
|
93
|
-
|
|
85
|
+
def add_part(parts, value)
|
|
86
|
+
parts << value if value && not_slash?(value)
|
|
87
|
+
end
|
|
94
88
|
|
|
95
|
-
|
|
89
|
+
def not_slash?(value)
|
|
90
|
+
value != '/'
|
|
96
91
|
end
|
|
97
92
|
end
|
|
98
93
|
end
|
data/lib/grape/request.rb
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'grape/util/lazy_object'
|
|
4
|
-
|
|
5
3
|
module Grape
|
|
6
4
|
class Request < Rack::Request
|
|
7
5
|
HTTP_PREFIX = 'HTTP_'
|
|
@@ -36,8 +34,8 @@ module Grape
|
|
|
36
34
|
end
|
|
37
35
|
|
|
38
36
|
def build_headers
|
|
39
|
-
Grape::Util::
|
|
40
|
-
env.each_pair.with_object(
|
|
37
|
+
Grape::Util::Lazy::Object.new do
|
|
38
|
+
env.each_pair.with_object(Grape::Util::Header.new) do |(k, v), headers|
|
|
41
39
|
next unless k.to_s.start_with? HTTP_PREFIX
|
|
42
40
|
|
|
43
41
|
transformed_header = Grape::Http::Headers::HTTP_HEADERS[k] || transform_header(k)
|
|
@@ -47,7 +45,7 @@ module Grape
|
|
|
47
45
|
end
|
|
48
46
|
|
|
49
47
|
def transform_header(header)
|
|
50
|
-
-header[5..].
|
|
48
|
+
-header[5..].tr('_', '-').downcase
|
|
51
49
|
end
|
|
52
50
|
end
|
|
53
51
|
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
class Router
|
|
5
|
+
class BaseRoute
|
|
6
|
+
delegate_missing_to :@options
|
|
7
|
+
|
|
8
|
+
attr_reader :index, :pattern, :options
|
|
9
|
+
|
|
10
|
+
def initialize(**options)
|
|
11
|
+
@options = ActiveSupport::OrderedOptions.new.update(options)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
alias attributes options
|
|
15
|
+
|
|
16
|
+
def regexp_capture_index
|
|
17
|
+
CaptureIndexCache[index]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def pattern_regexp
|
|
21
|
+
pattern.to_regexp
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def to_regexp(index)
|
|
25
|
+
@index = index
|
|
26
|
+
Regexp.new("(?<#{regexp_capture_index}>#{pattern_regexp})")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class CaptureIndexCache < Grape::Util::Cache
|
|
30
|
+
def initialize
|
|
31
|
+
super
|
|
32
|
+
@cache = Hash.new do |h, index|
|
|
33
|
+
h[index] = "_#{index}"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Act like a Grape::Router::Route but for greedy_match
|
|
4
|
+
# see @neutral_map
|
|
5
|
+
|
|
6
|
+
module Grape
|
|
7
|
+
class Router
|
|
8
|
+
class GreedyRoute < BaseRoute
|
|
9
|
+
def initialize(pattern:, **options)
|
|
10
|
+
@pattern = pattern
|
|
11
|
+
super(**options)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Grape::Router:Route defines params as a function
|
|
15
|
+
def params(_input = nil)
|
|
16
|
+
options[:params] || {}
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|