grape 1.8.0 → 3.3.5
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 +391 -1
- data/CONTRIBUTING.md +3 -11
- data/README.md +648 -552
- data/UPGRADING.md +803 -7
- data/grape.gemspec +11 -14
- data/lib/grape/api/instance.rb +68 -158
- data/lib/grape/api.rb +71 -107
- data/lib/grape/content_types.rb +23 -11
- data/lib/grape/cookies.rb +28 -25
- data/lib/grape/declared_params_handler.rb +116 -0
- data/lib/grape/dry_types.rb +48 -6
- data/lib/grape/dsl/callbacks.rb +14 -58
- data/lib/grape/dsl/declared.rb +35 -0
- data/lib/grape/dsl/desc.rb +15 -63
- data/lib/grape/dsl/entity.rb +88 -0
- data/lib/grape/dsl/headers.rb +2 -2
- data/lib/grape/dsl/helpers.rb +83 -64
- data/lib/grape/dsl/inside_route.rb +56 -295
- data/lib/grape/dsl/logger.rb +4 -9
- data/lib/grape/dsl/middleware.rb +22 -40
- data/lib/grape/dsl/parameters.rb +50 -82
- data/lib/grape/dsl/request_response.rb +144 -146
- data/lib/grape/dsl/rescue_options.rb +24 -0
- data/lib/grape/dsl/routing.rb +261 -186
- data/lib/grape/dsl/settings.rb +29 -135
- data/lib/grape/dsl/validations.rb +39 -32
- data/lib/grape/dsl/version_options.rb +23 -0
- data/lib/grape/endpoint/options.rb +19 -0
- data/lib/grape/endpoint.rb +277 -250
- data/lib/grape/{util/env.rb → env.rb} +1 -8
- data/lib/grape/error_formatter/base.rb +56 -21
- data/lib/grape/error_formatter/json.rb +13 -15
- data/lib/grape/error_formatter/serializable_hash.rb +7 -0
- data/lib/grape/error_formatter/txt.rb +12 -18
- data/lib/grape/error_formatter/xml.rb +3 -13
- data/lib/grape/error_formatter.rb +5 -25
- data/lib/grape/exceptions/base.rb +22 -58
- data/lib/grape/exceptions/error_response.rb +45 -0
- data/lib/grape/exceptions/incompatible_option_values.rb +1 -1
- data/lib/grape/exceptions/internal_server_error.rb +16 -0
- data/lib/grape/exceptions/invalid_accept_header.rb +1 -1
- data/lib/grape/exceptions/invalid_formatter.rb +1 -1
- data/lib/grape/exceptions/invalid_message_body.rb +1 -1
- data/lib/grape/exceptions/invalid_version_header.rb +1 -1
- data/lib/grape/exceptions/invalid_versioner_option.rb +1 -1
- data/lib/grape/exceptions/method_not_allowed.rb +1 -1
- data/lib/grape/exceptions/missing_group_type.rb +0 -2
- data/lib/grape/exceptions/missing_mime_type.rb +1 -1
- data/lib/grape/exceptions/request_error.rb +11 -0
- data/lib/grape/exceptions/unknown_auth_strategy.rb +11 -0
- data/lib/grape/exceptions/unknown_parameter.rb +1 -1
- data/lib/grape/exceptions/unknown_params_builder.rb +11 -0
- data/lib/grape/exceptions/unknown_validator.rb +1 -1
- data/lib/grape/exceptions/unsupported_group_type.rb +0 -2
- data/lib/grape/exceptions/validation.rb +25 -9
- data/lib/grape/exceptions/validation_array_errors.rb +5 -0
- data/lib/grape/exceptions/validation_errors.rb +22 -26
- data/lib/grape/formatter/base.rb +16 -0
- data/lib/grape/formatter/json.rb +4 -6
- data/lib/grape/formatter/serializable_hash.rb +6 -10
- data/lib/grape/formatter/txt.rb +3 -5
- data/lib/grape/formatter/xml.rb +4 -6
- data/lib/grape/formatter.rb +7 -25
- data/lib/grape/json.rb +46 -0
- data/lib/grape/locale/en.yml +43 -42
- data/lib/grape/middleware/auth/base.rb +11 -33
- data/lib/grape/middleware/auth/dsl.rb +37 -31
- data/lib/grape/middleware/auth/strategies.rb +1 -2
- data/lib/grape/middleware/base.rb +56 -33
- data/lib/grape/middleware/deprecated_options_hash_access.rb +19 -0
- data/lib/grape/middleware/error.rb +183 -91
- data/lib/grape/middleware/formatter.rb +94 -105
- data/lib/grape/middleware/precomputed_content_types.rb +46 -0
- data/lib/grape/middleware/stack.rb +27 -38
- data/lib/grape/middleware/versioner/accept_version_header.rb +6 -33
- data/lib/grape/middleware/versioner/base.rb +72 -0
- data/lib/grape/middleware/versioner/header.rb +44 -129
- data/lib/grape/middleware/versioner/param.rb +4 -25
- data/lib/grape/middleware/versioner/path.rb +13 -32
- data/lib/grape/middleware/versioner.rb +7 -14
- data/lib/grape/mountable.rb +22 -0
- data/lib/grape/namespace.rb +18 -12
- data/lib/grape/params_builder/base.rb +20 -0
- data/lib/grape/params_builder/hash.rb +11 -0
- data/lib/grape/params_builder/hash_with_indifferent_access.rb +11 -0
- data/lib/grape/params_builder/hashie_mash.rb +11 -0
- data/lib/grape/params_builder.rb +15 -0
- data/lib/grape/parser/base.rb +16 -0
- data/lib/grape/parser/json.rb +6 -8
- data/lib/grape/parser/xml.rb +6 -8
- data/lib/grape/parser.rb +5 -23
- data/lib/grape/path.rb +38 -63
- data/lib/grape/railtie.rb +9 -0
- data/lib/grape/request.rb +168 -26
- data/lib/grape/router/base_route.rb +48 -0
- data/lib/grape/router/greedy_route.rb +26 -0
- data/lib/grape/router/mustermann_pattern.rb +44 -0
- data/lib/grape/router/pattern.rb +30 -34
- data/lib/grape/router/route.rb +28 -61
- data/lib/grape/router.rb +71 -86
- data/lib/grape/serve_stream/file_body.rb +1 -0
- data/lib/grape/serve_stream/sendfile_response.rb +3 -5
- data/lib/grape/serve_stream/stream_response.rb +1 -0
- data/lib/grape/testing.rb +33 -0
- data/lib/grape/util/api_description.rb +58 -0
- data/lib/grape/util/base_inheritable.rb +14 -14
- data/lib/grape/util/cache.rb +1 -3
- data/lib/grape/util/deep_freeze.rb +35 -0
- data/lib/grape/util/endpoint_configuration.rb +1 -1
- data/lib/grape/util/header.rb +13 -0
- data/lib/grape/util/inheritable_setting.rb +51 -27
- data/lib/grape/util/inheritable_values.rb +7 -5
- data/lib/grape/util/lazy/base.rb +16 -0
- data/lib/grape/util/lazy/block.rb +22 -0
- data/lib/grape/util/lazy/value.rb +31 -0
- data/lib/grape/util/lazy/value_array.rb +21 -0
- data/lib/grape/util/lazy/value_enumerable.rb +31 -0
- data/lib/grape/util/lazy/value_hash.rb +21 -0
- data/lib/grape/util/media_type.rb +67 -0
- data/lib/grape/util/path_normalizer.rb +34 -0
- data/lib/grape/util/registry.rb +27 -0
- data/lib/grape/util/reverse_stackable_values.rb +1 -6
- data/lib/grape/util/stackable_values.rb +12 -14
- data/lib/grape/util/translation.rb +42 -0
- data/lib/grape/validations/attributes_iterator.rb +43 -27
- data/lib/grape/validations/coerce_options.rb +21 -0
- data/lib/grape/validations/contract_scope.rb +28 -0
- data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
- data/lib/grape/validations/oneof_collector.rb +39 -0
- data/lib/grape/validations/param_scope_tracker.rb +62 -0
- data/lib/grape/validations/params_documentation.rb +52 -0
- data/lib/grape/validations/params_scope.rb +180 -303
- data/lib/grape/validations/shared_options.rb +19 -0
- data/lib/grape/validations/single_attribute_iterator.rb +2 -2
- data/lib/grape/validations/types/array_coercer.rb +4 -7
- data/lib/grape/validations/types/custom_type_coercer.rb +41 -85
- data/lib/grape/validations/types/custom_type_collection_coercer.rb +1 -1
- data/lib/grape/validations/types/dry_type_coercer.rb +15 -28
- data/lib/grape/validations/types/json.rb +0 -2
- data/lib/grape/validations/types/primitive_coercer.rb +11 -35
- data/lib/grape/validations/types/set_coercer.rb +1 -4
- data/lib/grape/validations/types/variant_collection_coercer.rb +11 -0
- data/lib/grape/validations/types.rb +29 -54
- data/lib/grape/validations/validations_spec.rb +164 -0
- data/lib/grape/validations/validators/all_or_none_of_validator.rb +6 -3
- data/lib/grape/validations/validators/allow_blank_validator.rb +10 -5
- data/lib/grape/validations/validators/at_least_one_of_validator.rb +5 -2
- data/lib/grape/validations/validators/base.rb +120 -36
- data/lib/grape/validations/validators/coerce_validator.rb +18 -36
- data/lib/grape/validations/validators/contract_scope_validator.rb +45 -0
- data/lib/grape/validations/validators/default_validator.rb +13 -16
- data/lib/grape/validations/validators/exactly_one_of_validator.rb +10 -3
- data/lib/grape/validations/validators/except_values_validator.rb +15 -5
- data/lib/grape/validations/validators/length_validator.rb +48 -0
- data/lib/grape/validations/validators/multiple_params_base.rb +12 -9
- data/lib/grape/validations/validators/{mutual_exclusion_validator.rb → mutually_exclusive_validator.rb} +4 -2
- data/lib/grape/validations/validators/oneof_validator.rb +49 -0
- data/lib/grape/validations/validators/presence_validator.rb +4 -2
- data/lib/grape/validations/validators/regexp_validator.rb +11 -3
- data/lib/grape/validations/validators/same_as_validator.rb +6 -15
- data/lib/grape/validations/validators/values_validator.rb +36 -65
- data/lib/grape/validations.rb +8 -21
- data/lib/grape/version.rb +1 -2
- data/lib/grape/xml.rb +17 -0
- data/lib/grape.rb +75 -288
- metadata +79 -291
- data/lib/grape/api/helpers.rb +0 -9
- data/lib/grape/dsl/api.rb +0 -19
- data/lib/grape/dsl/configuration.rb +0 -15
- data/lib/grape/eager_load.rb +0 -20
- data/lib/grape/exceptions/empty_message_body.rb +0 -11
- data/lib/grape/exceptions/missing_option.rb +0 -11
- data/lib/grape/exceptions/too_many_multipart_files.rb +0 -11
- data/lib/grape/exceptions/unknown_options.rb +0 -11
- data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +0 -27
- data/lib/grape/extensions/hash.rb +0 -22
- data/lib/grape/extensions/hashie/mash.rb +0 -26
- data/lib/grape/http/headers.rb +0 -61
- data/lib/grape/middleware/globals.rb +0 -16
- data/lib/grape/middleware/helpers.rb +0 -12
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +0 -24
- data/lib/grape/router/attribute_translator.rb +0 -63
- data/lib/grape/types/invalid_value.rb +0 -8
- data/lib/grape/util/json.rb +0 -12
- 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/lib/grape/util/registrable.rb +0 -15
- data/lib/grape/util/strict_hash_configuration.rb +0 -108
- data/lib/grape/util/xml.rb +0 -10
- data/lib/grape/validations/attributes_doc.rb +0 -58
- data/lib/grape/validations/types/build_coercer.rb +0 -94
- data/lib/grape/validations/validator_factory.rb +0 -15
- 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
|
@@ -11,15 +11,8 @@ module Grape
|
|
|
11
11
|
API_VENDOR = 'api.vendor'
|
|
12
12
|
API_FORMAT = 'api.format'
|
|
13
13
|
|
|
14
|
-
RACK_INPUT = 'rack.input'
|
|
15
|
-
RACK_REQUEST_QUERY_HASH = 'rack.request.query_hash'
|
|
16
|
-
RACK_REQUEST_FORM_HASH = 'rack.request.form_hash'
|
|
17
|
-
RACK_REQUEST_FORM_INPUT = 'rack.request.form_input'
|
|
18
|
-
|
|
19
|
-
GRAPE_REQUEST = 'grape.request'
|
|
20
|
-
GRAPE_REQUEST_HEADERS = 'grape.request.headers'
|
|
21
|
-
GRAPE_REQUEST_PARAMS = 'grape.request.params'
|
|
22
14
|
GRAPE_ROUTING_ARGS = 'grape.routing_args'
|
|
23
15
|
GRAPE_ALLOWED_METHODS = 'grape.allowed_methods'
|
|
16
|
+
GRAPE_EXCEPTION = 'grape.exception'
|
|
24
17
|
end
|
|
25
18
|
end
|
|
@@ -2,36 +2,71 @@
|
|
|
2
2
|
|
|
3
3
|
module Grape
|
|
4
4
|
module ErrorFormatter
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
class Base
|
|
6
|
+
class << self
|
|
7
|
+
# Custom error formatters override +call+. The +error+ is a frozen
|
|
8
|
+
# {Grape::Exceptions::ErrorResponse} carrying +status+/+message+/
|
|
9
|
+
# +headers+/+backtrace+/+original_exception+. +env+ is the Rack env
|
|
10
|
+
# (needed by entity-presenter resolution). +include_backtrace+ and
|
|
11
|
+
# +include_original_exception+ are the request-time toggles set by
|
|
12
|
+
# +rescue_from+; the base implementation embeds the corresponding
|
|
13
|
+
# fields in the response body when they are true.
|
|
14
|
+
def call(error:, env: nil, include_backtrace: false, include_original_exception: false)
|
|
15
|
+
wrapped_message = wrap_message(present(error.message, env))
|
|
16
|
+
if wrapped_message.is_a?(Hash)
|
|
17
|
+
wrapped_message[:backtrace] = error.backtrace if include_backtrace && error.backtrace.present?
|
|
18
|
+
wrapped_message[:original_exception] = error.original_exception.inspect if include_original_exception && error.original_exception
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
format_structured_message(wrapped_message)
|
|
12
22
|
end
|
|
13
23
|
|
|
14
|
-
|
|
24
|
+
def present(message, env)
|
|
25
|
+
# error! accepts a message hash with an optional :with key specifying the entity presenter.
|
|
26
|
+
# Extract it here so the presenter can be resolved and the key is not serialized in the response.
|
|
27
|
+
# See spec/integration/grape_entity/entity_spec.rb for examples.
|
|
28
|
+
with = nil
|
|
29
|
+
if message.is_a?(Hash) && message.key?(:with)
|
|
30
|
+
message = message.dup
|
|
31
|
+
with = message.delete(:with)
|
|
32
|
+
end
|
|
15
33
|
|
|
16
|
-
|
|
17
|
-
# env['api.endpoint'].route does not work when the error occurs within a middleware
|
|
18
|
-
# the Endpoint does not have a valid env at this moment
|
|
19
|
-
http_codes = env[Grape::Env::GRAPE_ROUTING_ARGS][:route_info].http_codes || []
|
|
34
|
+
presenter = with || env[Grape::Env::API_ENDPOINT].entity_class_for_obj(message)
|
|
20
35
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
36
|
+
unless presenter || env[Grape::Env::GRAPE_ROUTING_ARGS].nil?
|
|
37
|
+
# env['api.endpoint'].route does not work when the error occurs within a middleware
|
|
38
|
+
# the Endpoint does not have a valid env at this moment
|
|
39
|
+
http_codes = env[Grape::Env::GRAPE_ROUTING_ARGS][:route_info].http_codes || []
|
|
40
|
+
found_code = http_codes.find do |http_code|
|
|
41
|
+
(http_code[0].to_i == env[Grape::Env::API_ENDPOINT].status) && http_code[2].respond_to?(:represent)
|
|
42
|
+
end if env[Grape::Env::API_ENDPOINT].request
|
|
24
43
|
|
|
25
|
-
|
|
26
|
-
|
|
44
|
+
presenter = found_code[2] if found_code
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
return message unless presenter
|
|
27
48
|
|
|
28
|
-
|
|
29
|
-
embeds = { env: env }
|
|
49
|
+
embeds = { env: }
|
|
30
50
|
embeds[:version] = env[Grape::Env::API_VERSION] if env.key?(Grape::Env::API_VERSION)
|
|
31
|
-
|
|
51
|
+
presenter.represent(message, embeds).serializable_hash
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def wrap_message(message)
|
|
55
|
+
return message if message.is_a?(Hash)
|
|
56
|
+
|
|
57
|
+
{ message: }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def format_structured_message(_structured_message)
|
|
61
|
+
raise NotImplementedError
|
|
32
62
|
end
|
|
33
63
|
|
|
34
|
-
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
def inherited(klass)
|
|
67
|
+
super
|
|
68
|
+
ErrorFormatter.register(klass)
|
|
69
|
+
end
|
|
35
70
|
end
|
|
36
71
|
end
|
|
37
72
|
end
|
|
@@ -2,27 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
module Grape
|
|
4
4
|
module ErrorFormatter
|
|
5
|
-
|
|
6
|
-
extend Base
|
|
7
|
-
|
|
5
|
+
class Json < Base
|
|
8
6
|
class << self
|
|
9
|
-
def
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
rescue_options = options[:rescue_options] || {}
|
|
13
|
-
result = result.merge(backtrace: backtrace) if rescue_options[:backtrace] && backtrace && !backtrace.empty?
|
|
14
|
-
result = result.merge(original_exception: original_exception.inspect) if rescue_options[:original_exception] && original_exception
|
|
15
|
-
::Grape::Json.dump(result)
|
|
7
|
+
def format_structured_message(structured_message)
|
|
8
|
+
::Grape::Json.dump(structured_message)
|
|
16
9
|
end
|
|
17
10
|
|
|
18
11
|
private
|
|
19
12
|
|
|
20
13
|
def wrap_message(message)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
14
|
+
# Use +is_a?+ rather than +case/when+ here. +case/when Hash+ matches via
|
|
15
|
+
# +Module#===+, a C-level real-class check that ignores delegation, so a
|
|
16
|
+
# +SimpleDelegator+ wrapping a Hash (e.g. the +OutputBuilder+ returned by
|
|
17
|
+
# +Grape::Entity#serializable_hash+ when an error is presented via an entity)
|
|
18
|
+
# would fall through and be wrapped in a spurious +{ error: ... }+ envelope.
|
|
19
|
+
# +is_a?+ is forwarded by the delegator to the wrapped Hash, so it matches.
|
|
20
|
+
return message if message.is_a?(Hash)
|
|
21
|
+
return message.as_json if message.is_a?(Exceptions::ValidationErrors)
|
|
22
|
+
|
|
23
|
+
{ error: ensure_utf8(message) }
|
|
26
24
|
end
|
|
27
25
|
|
|
28
26
|
def ensure_utf8(message)
|
|
@@ -2,25 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
module Grape
|
|
4
4
|
module ErrorFormatter
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
result += backtrace.join("\r\n ")
|
|
17
|
-
end
|
|
18
|
-
if rescue_options[:original_exception] && original_exception
|
|
19
|
-
result += "\r\n original exception:"
|
|
20
|
-
result += "\r\n #{original_exception.inspect}"
|
|
21
|
-
end
|
|
22
|
-
result
|
|
5
|
+
class Txt < Base
|
|
6
|
+
def self.format_structured_message(structured_message)
|
|
7
|
+
message = structured_message[:message] || Grape::Json.dump(structured_message)
|
|
8
|
+
final_message = Array.wrap(message)
|
|
9
|
+
if structured_message.key?(:backtrace)
|
|
10
|
+
final_message << 'backtrace:'
|
|
11
|
+
final_message.concat(structured_message[:backtrace])
|
|
12
|
+
end
|
|
13
|
+
if structured_message.key?(:original_exception)
|
|
14
|
+
final_message << 'original exception:'
|
|
15
|
+
final_message << structured_message[:original_exception]
|
|
23
16
|
end
|
|
17
|
+
final_message.join("\r\n ")
|
|
24
18
|
end
|
|
25
19
|
end
|
|
26
20
|
end
|
|
@@ -2,19 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
module Grape
|
|
4
4
|
module ErrorFormatter
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class << self
|
|
9
|
-
def call(message, backtrace, options = {}, env = nil, original_exception = nil)
|
|
10
|
-
message = present(message, env)
|
|
11
|
-
|
|
12
|
-
result = message.is_a?(Hash) ? message : { message: message }
|
|
13
|
-
rescue_options = options[:rescue_options] || {}
|
|
14
|
-
result = result.merge(backtrace: backtrace) if rescue_options[:backtrace] && backtrace && !backtrace.empty?
|
|
15
|
-
result = result.merge(original_exception: original_exception.inspect) if rescue_options[:original_exception] && original_exception
|
|
16
|
-
result.respond_to?(:to_xml) ? result.to_xml(root: :error) : result.to_s
|
|
17
|
-
end
|
|
5
|
+
class Xml < Base
|
|
6
|
+
def self.format_structured_message(structured_message)
|
|
7
|
+
structured_message.respond_to?(:to_xml) ? structured_message.to_xml(root: :error) : structured_message.to_s
|
|
18
8
|
end
|
|
19
9
|
end
|
|
20
10
|
end
|
|
@@ -2,34 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
module Grape
|
|
4
4
|
module ErrorFormatter
|
|
5
|
-
extend Util::
|
|
5
|
+
extend Grape::Util::Registry
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
def builtin_formatters
|
|
9
|
-
@builtin_formatters ||= {
|
|
10
|
-
serializable_hash: Grape::ErrorFormatter::Json,
|
|
11
|
-
json: Grape::ErrorFormatter::Json,
|
|
12
|
-
jsonapi: Grape::ErrorFormatter::Json,
|
|
13
|
-
txt: Grape::ErrorFormatter::Txt,
|
|
14
|
-
xml: Grape::ErrorFormatter::Xml
|
|
15
|
-
}
|
|
16
|
-
end
|
|
7
|
+
module_function
|
|
17
8
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
end
|
|
9
|
+
def formatter_for(format, error_formatters = nil, default_error_formatter = nil)
|
|
10
|
+
return error_formatters[format] if error_formatters&.key?(format)
|
|
21
11
|
|
|
22
|
-
|
|
23
|
-
spec = formatters(**options)[api_format]
|
|
24
|
-
case spec
|
|
25
|
-
when nil
|
|
26
|
-
options[:default_error_formatter] || Grape::ErrorFormatter::Txt
|
|
27
|
-
when Symbol
|
|
28
|
-
method(spec)
|
|
29
|
-
else
|
|
30
|
-
spec
|
|
31
|
-
end
|
|
32
|
-
end
|
|
12
|
+
registry[format] || default_error_formatter || Grape::ErrorFormatter::Txt
|
|
33
13
|
end
|
|
34
14
|
end
|
|
35
15
|
end
|
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
module Grape
|
|
4
4
|
module Exceptions
|
|
5
5
|
class Base < StandardError
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
include Grape::Util::Translation
|
|
7
|
+
|
|
8
|
+
MESSAGE_STEPS = %w[problem summary resolution].to_h { |s| [s.to_sym, s.capitalize] }.freeze
|
|
9
9
|
|
|
10
10
|
attr_reader :status, :headers
|
|
11
11
|
|
|
12
|
-
def initialize(status: nil, message: nil, headers: nil
|
|
12
|
+
def initialize(status: nil, message: nil, headers: nil)
|
|
13
13
|
super(message)
|
|
14
14
|
|
|
15
15
|
@status = status
|
|
@@ -17,70 +17,34 @@ module Grape
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def [](index)
|
|
20
|
-
|
|
20
|
+
__send__ index
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
# TODO: translate attribute first
|
|
26
|
-
# if BASE_ATTRIBUTES_KEY.key respond to a string message, then short_message is returned
|
|
27
|
-
# if BASE_ATTRIBUTES_KEY.key respond to a Hash, means it may have problem , summary and resolution
|
|
28
|
-
def compose_message(key, **attributes)
|
|
29
|
-
short_message = translate_message(key, **attributes)
|
|
30
|
-
if short_message.is_a? Hash
|
|
31
|
-
@problem = problem(key, **attributes)
|
|
32
|
-
@summary = summary(key, **attributes)
|
|
33
|
-
@resolution = resolution(key, **attributes)
|
|
34
|
-
[['Problem', @problem], ['Summary', @summary], ['Resolution', @resolution]].each_with_object(+'') do |detail_array, message|
|
|
35
|
-
message << "\n#{detail_array[0]}:\n #{detail_array[1]}" unless detail_array[1].blank?
|
|
36
|
-
message
|
|
37
|
-
end
|
|
38
|
-
else
|
|
39
|
-
short_message
|
|
40
|
-
end
|
|
41
|
-
end
|
|
23
|
+
private
|
|
42
24
|
|
|
43
|
-
def
|
|
44
|
-
translate_message(
|
|
45
|
-
|
|
25
|
+
def compose_message(key, **)
|
|
26
|
+
short_message = translate_message(key, **)
|
|
27
|
+
return short_message unless short_message.is_a?(Hash)
|
|
46
28
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
end
|
|
29
|
+
MESSAGE_STEPS.filter_map do |step, label|
|
|
30
|
+
next unless short_message.key?(step)
|
|
50
31
|
|
|
51
|
-
|
|
52
|
-
|
|
32
|
+
detail = translate_message(:"#{key}.#{step}", **)
|
|
33
|
+
"\n#{label}:\n #{detail}" if detail.present?
|
|
34
|
+
end.join
|
|
53
35
|
end
|
|
54
36
|
|
|
55
|
-
def
|
|
56
|
-
|
|
57
|
-
translate("#{BASE_ATTRIBUTES_KEY}.#{key}", default: key, **options)
|
|
58
|
-
end.join(', ')
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def translate_message(key, **options)
|
|
62
|
-
case key
|
|
37
|
+
def translate_message(translation_key, **)
|
|
38
|
+
case translation_key
|
|
63
39
|
when Symbol
|
|
64
|
-
translate(
|
|
40
|
+
translate(translation_key, **)
|
|
41
|
+
when Hash
|
|
42
|
+
translation_key => { key:, **opts }
|
|
43
|
+
translate(key, **opts)
|
|
65
44
|
when Proc
|
|
66
|
-
|
|
67
|
-
else
|
|
68
|
-
key
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
def translate(key, **options)
|
|
73
|
-
options = options.dup
|
|
74
|
-
options[:default] &&= options[:default].to_s
|
|
75
|
-
message = ::I18n.translate(key, **options)
|
|
76
|
-
message.presence || fallback_message(key, **options)
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
def fallback_message(key, **options)
|
|
80
|
-
if ::I18n.enforce_available_locales && ::I18n.available_locales.exclude?(FALLBACK_LOCALE)
|
|
81
|
-
key
|
|
45
|
+
translation_key.call
|
|
82
46
|
else
|
|
83
|
-
|
|
47
|
+
translation_key
|
|
84
48
|
end
|
|
85
49
|
end
|
|
86
50
|
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Exceptions
|
|
5
|
+
# Value object representing the payload thrown via `throw :error, ...`
|
|
6
|
+
# and consumed by `Middleware::Error#error_response`. Replaces the
|
|
7
|
+
# implicit-schema Hash that previously circulated between throw sites
|
|
8
|
+
# and the error middleware.
|
|
9
|
+
ErrorResponse = Data.define(:status, :message, :headers, :backtrace, :original_exception) do
|
|
10
|
+
def initialize(status: nil, message: nil, headers: nil, backtrace: nil, original_exception: nil)
|
|
11
|
+
super
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def to_s
|
|
15
|
+
"#<#{self.class.name} status=#{status.inspect} message=#{message.inspect} headers=#{headers.inspect}>"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.from_exception(exception)
|
|
19
|
+
new(
|
|
20
|
+
status: exception.status,
|
|
21
|
+
message: exception.message,
|
|
22
|
+
headers: exception.headers,
|
|
23
|
+
backtrace: exception.backtrace,
|
|
24
|
+
original_exception: exception
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Normalize heterogeneous inputs into an ErrorResponse. Preserves the
|
|
29
|
+
# public contract that users can still `throw :error, hash` from their
|
|
30
|
+
# own middleware or `rescue_from` handlers.
|
|
31
|
+
def self.coerce(input)
|
|
32
|
+
case input
|
|
33
|
+
when ErrorResponse
|
|
34
|
+
input
|
|
35
|
+
when Grape::Exceptions::Base
|
|
36
|
+
from_exception(input)
|
|
37
|
+
when Hash
|
|
38
|
+
new(**input.slice(:status, :message, :headers, :backtrace, :original_exception))
|
|
39
|
+
else
|
|
40
|
+
new
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -4,7 +4,7 @@ module Grape
|
|
|
4
4
|
module Exceptions
|
|
5
5
|
class IncompatibleOptionValues < Base
|
|
6
6
|
def initialize(option1, value1, option2, value2)
|
|
7
|
-
super(message: compose_message(:incompatible_option_values, option1
|
|
7
|
+
super(message: compose_message(:incompatible_option_values, option1:, value1:, option2:, value2:))
|
|
8
8
|
end
|
|
9
9
|
end
|
|
10
10
|
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Exceptions
|
|
5
|
+
# Raised internally when a +rescue_from+ handler itself raises an
|
|
6
|
+
# unrecognised exception. The framework substitutes the original
|
|
7
|
+
# exception with this safe stand-in for rendering, while preserving
|
|
8
|
+
# the original on +env[Grape::Env::GRAPE_EXCEPTION]+ for upstream
|
|
9
|
+
# observability (loggers, error trackers, etc.).
|
|
10
|
+
class InternalServerError < Base
|
|
11
|
+
def initialize
|
|
12
|
+
super(status: 500, message: compose_message(:internal_server_error))
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -4,7 +4,7 @@ module Grape
|
|
|
4
4
|
module Exceptions
|
|
5
5
|
class InvalidAcceptHeader < Base
|
|
6
6
|
def initialize(message, headers)
|
|
7
|
-
super(message: compose_message(:invalid_accept_header, message:
|
|
7
|
+
super(message: compose_message(:invalid_accept_header, message:), status: 406, headers:)
|
|
8
8
|
end
|
|
9
9
|
end
|
|
10
10
|
end
|
|
@@ -4,7 +4,7 @@ module Grape
|
|
|
4
4
|
module Exceptions
|
|
5
5
|
class InvalidFormatter < Base
|
|
6
6
|
def initialize(klass, to_format)
|
|
7
|
-
super(message: compose_message(:invalid_formatter, klass
|
|
7
|
+
super(message: compose_message(:invalid_formatter, klass:, to_format:))
|
|
8
8
|
end
|
|
9
9
|
end
|
|
10
10
|
end
|
|
@@ -4,7 +4,7 @@ module Grape
|
|
|
4
4
|
module Exceptions
|
|
5
5
|
class InvalidMessageBody < Base
|
|
6
6
|
def initialize(body_format)
|
|
7
|
-
super(message: compose_message(:invalid_message_body, body_format:
|
|
7
|
+
super(message: compose_message(:invalid_message_body, body_format:), status: 400)
|
|
8
8
|
end
|
|
9
9
|
end
|
|
10
10
|
end
|
|
@@ -4,7 +4,7 @@ module Grape
|
|
|
4
4
|
module Exceptions
|
|
5
5
|
class InvalidVersionHeader < Base
|
|
6
6
|
def initialize(message, headers)
|
|
7
|
-
super(message: compose_message(:invalid_version_header, message:
|
|
7
|
+
super(message: compose_message(:invalid_version_header, message:), status: 406, headers:)
|
|
8
8
|
end
|
|
9
9
|
end
|
|
10
10
|
end
|
|
@@ -4,7 +4,7 @@ module Grape
|
|
|
4
4
|
module Exceptions
|
|
5
5
|
class InvalidVersionerOption < Base
|
|
6
6
|
def initialize(strategy)
|
|
7
|
-
super(message: compose_message(:invalid_versioner_option, strategy:
|
|
7
|
+
super(message: compose_message(:invalid_versioner_option, strategy:))
|
|
8
8
|
end
|
|
9
9
|
end
|
|
10
10
|
end
|
|
@@ -4,7 +4,7 @@ module Grape
|
|
|
4
4
|
module Exceptions
|
|
5
5
|
class UnknownValidator < Base
|
|
6
6
|
def initialize(validator_type)
|
|
7
|
-
super(message: compose_message(:unknown_validator, validator_type:
|
|
7
|
+
super(message: compose_message(:unknown_validator, validator_type:))
|
|
8
8
|
end
|
|
9
9
|
end
|
|
10
10
|
end
|
|
@@ -1,19 +1,27 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'grape/exceptions/base'
|
|
4
|
-
|
|
5
3
|
module Grape
|
|
6
4
|
module Exceptions
|
|
7
|
-
class Validation <
|
|
8
|
-
|
|
5
|
+
class Validation < Base
|
|
6
|
+
EMPTY_BACKTRACE = [].freeze
|
|
7
|
+
|
|
8
|
+
attr_reader :params, :message_key
|
|
9
9
|
|
|
10
|
-
def initialize(params:, message: nil,
|
|
11
|
-
@params = params
|
|
10
|
+
def initialize(params:, message: nil, status: nil, headers: nil)
|
|
11
|
+
@params = Array(params)
|
|
12
12
|
if message
|
|
13
|
-
@message_key =
|
|
14
|
-
|
|
13
|
+
@message_key = case message
|
|
14
|
+
when Symbol then message
|
|
15
|
+
when Hash then message[:key]
|
|
16
|
+
end
|
|
17
|
+
message = translate_message(message)
|
|
15
18
|
end
|
|
16
|
-
|
|
19
|
+
|
|
20
|
+
super(status:, message:, headers:)
|
|
21
|
+
# Pre-seed the backtrace so Ruby's raise skips capture. Validation errors are
|
|
22
|
+
# a hot path (raised per bad attribute) and end up as 400 Bad Request responses;
|
|
23
|
+
# backtraces here point into Grape internals and have no diagnostic value.
|
|
24
|
+
set_backtrace(EMPTY_BACKTRACE)
|
|
17
25
|
end
|
|
18
26
|
|
|
19
27
|
# Remove all the unnecessary stuff from Grape::Exceptions::Base like status
|
|
@@ -21,6 +29,14 @@ module Grape
|
|
|
21
29
|
def as_json(*_args)
|
|
22
30
|
to_s
|
|
23
31
|
end
|
|
32
|
+
|
|
33
|
+
# Returns +self+ so callers (e.g. +ValidationErrors#initialize+) can treat
|
|
34
|
+
# a single +Validation+ and a +ValidationArrayErrors+ wrapper uniformly via
|
|
35
|
+
# +flat_map(&:errors)+ — Array returns flatten in, non-Array returns
|
|
36
|
+
# (i.e. this +self+) append as one element.
|
|
37
|
+
def errors
|
|
38
|
+
self
|
|
39
|
+
end
|
|
24
40
|
end
|
|
25
41
|
end
|
|
26
42
|
end
|
|
@@ -3,10 +3,15 @@
|
|
|
3
3
|
module Grape
|
|
4
4
|
module Exceptions
|
|
5
5
|
class ValidationArrayErrors < Base
|
|
6
|
+
EMPTY_BACKTRACE = [].freeze
|
|
7
|
+
|
|
6
8
|
attr_reader :errors
|
|
7
9
|
|
|
8
10
|
def initialize(errors)
|
|
11
|
+
super()
|
|
9
12
|
@errors = errors
|
|
13
|
+
# Skip backtrace capture — see Grape::Exceptions::Validation for rationale.
|
|
14
|
+
set_backtrace(EMPTY_BACKTRACE)
|
|
10
15
|
end
|
|
11
16
|
end
|
|
12
17
|
end
|