grape 1.8.0 → 4.0.1
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 +517 -1
- data/CONTRIBUTING.md +19 -11
- data/README.md +841 -545
- data/UPGRADING.md +1553 -7
- data/grape.gemspec +11 -14
- data/lib/grape/api/instance.rb +77 -160
- data/lib/grape/api.rb +75 -107
- data/lib/grape/content_types.rb +67 -9
- 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 +8 -58
- data/lib/grape/dsl/declared.rb +35 -0
- data/lib/grape/dsl/desc.rb +25 -63
- data/lib/grape/dsl/entity.rb +106 -0
- data/lib/grape/dsl/headers.rb +2 -2
- data/lib/grape/dsl/helpers.rb +83 -64
- data/lib/grape/dsl/inside_route.rb +69 -296
- data/lib/grape/dsl/logger.rb +4 -9
- data/lib/grape/dsl/middleware.rb +22 -40
- data/lib/grape/dsl/parameters.rb +88 -94
- data/lib/grape/dsl/request_response.rb +143 -147
- data/lib/grape/dsl/rescue_options.rb +25 -0
- data/lib/grape/dsl/routing.rb +333 -191
- 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 +25 -0
- data/lib/grape/endpoint.rb +319 -269
- data/lib/grape/{util/env.rb → env.rb} +8 -8
- data/lib/grape/error_formatter/base.rb +57 -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 +9 -25
- data/lib/grape/exceptions/base.rb +22 -58
- data/lib/grape/exceptions/error_response.rb +48 -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_error_formatter.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 +28 -11
- 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 +5 -6
- data/lib/grape/formatter/serializable_hash.rb +7 -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 +44 -42
- data/lib/grape/middleware/auth/base.rb +11 -33
- data/lib/grape/middleware/auth/dsl.rb +12 -34
- data/lib/grape/middleware/auth/strategies.rb +1 -2
- data/lib/grape/middleware/base.rb +56 -33
- data/lib/grape/middleware/error.rb +290 -91
- data/lib/grape/middleware/formatter.rb +164 -106
- data/lib/grape/middleware/precomputed_content_types.rb +51 -0
- data/lib/grape/middleware/stack.rb +38 -40
- data/lib/grape/middleware/versioner/accept_version_header.rb +6 -33
- data/lib/grape/middleware/versioner/base.rb +66 -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 +48 -25
- data/lib/grape/middleware/versioner.rb +7 -14
- data/lib/grape/mountable.rb +22 -0
- data/lib/grape/namespace.rb +21 -14
- 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 +8 -94
- data/lib/grape/precompiled_json.rb +50 -0
- data/lib/grape/railtie.rb +9 -0
- data/lib/grape/request.rb +200 -26
- data/lib/grape/router/base_route.rb +85 -0
- data/lib/grape/router/greedy_route.rb +30 -0
- data/lib/grape/router/mustermann_pattern.rb +44 -0
- data/lib/grape/router/pattern/path.rb +78 -0
- data/lib/grape/router/pattern.rb +77 -35
- data/lib/grape/router/route.rb +79 -60
- data/lib/grape/router.rb +128 -104
- data/lib/grape/serve_stream/file_body.rb +7 -0
- data/lib/grape/serve_stream/sendfile_response.rb +3 -5
- data/lib/grape/serve_stream/stream_response.rb +7 -0
- data/lib/grape/testing.rb +33 -0
- data/lib/grape/util/api_description.rb +67 -0
- data/lib/grape/util/cache.rb +22 -5
- data/lib/grape/util/deep_freeze.rb +34 -0
- data/lib/grape/util/endpoint_configuration.rb +1 -1
- data/lib/grape/util/freeze_on_new.rb +20 -0
- data/lib/grape/util/header.rb +13 -0
- data/lib/grape/util/inheritable_setting.rb +777 -39
- 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 +74 -0
- data/lib/grape/util/path_normalizer.rb +37 -0
- data/lib/grape/util/registry.rb +37 -0
- data/lib/grape/util/shadowed_rescue_handlers.rb +49 -0
- data/lib/grape/util/stackable_values.rb +40 -16
- data/lib/grape/util/translation.rb +42 -0
- data/lib/grape/validations/attributes_iterator.rb +61 -28
- data/lib/grape/validations/coerce_options.rb +21 -0
- data/lib/grape/validations/contract_scope.rb +29 -0
- data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
- data/lib/grape/validations/oneof_collector.rb +35 -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 +223 -306
- data/lib/grape/validations/shared_options.rb +19 -0
- data/lib/grape/validations/single_attribute_iterator.rb +6 -4
- data/lib/grape/validations/types/array_coercer.rb +7 -12
- data/lib/grape/validations/types/custom_type_coercer.rb +47 -85
- data/lib/grape/validations/types/custom_type_collection_coercer.rb +1 -1
- data/lib/grape/validations/types/dry_type_coercer.rb +17 -28
- data/lib/grape/validations/types/json.rb +1 -5
- data/lib/grape/validations/types/multiple_type_coercer.rb +5 -3
- data/lib/grape/validations/types/primitive_coercer.rb +14 -35
- data/lib/grape/validations/types/set_coercer.rb +1 -4
- data/lib/grape/validations/types/variant_collection_coercer.rb +16 -3
- 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 +118 -37
- data/lib/grape/validations/validators/coerce_validator.rb +26 -38
- data/lib/grape/validations/validators/contract_scope_validator.rb +46 -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 +50 -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 +51 -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 +7 -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 +96 -288
- metadata +83 -294
- 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/base_inheritable.rb +0 -43
- data/lib/grape/util/inheritable_values.rb +0 -31
- 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/reverse_stackable_values.rb +0 -20
- 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,15 @@ module Grape
|
|
|
11
11
|
API_VENDOR = 'api.vendor'
|
|
12
12
|
API_FORMAT = 'api.format'
|
|
13
13
|
|
|
14
|
-
|
|
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'
|
|
14
|
+
GRAPE_NORMALIZED_PATH = 'grape.normalized_path'
|
|
22
15
|
GRAPE_ROUTING_ARGS = 'grape.routing_args'
|
|
23
16
|
GRAPE_ALLOWED_METHODS = 'grape.allowed_methods'
|
|
17
|
+
GRAPE_EXCEPTION = 'grape.exception'
|
|
18
|
+
|
|
19
|
+
# Not a Grape-owned key: the de-facto convention for an exception that was
|
|
20
|
+
# handled rather than raised, which is how error trackers find one they
|
|
21
|
+
# never saw propagate. sentry-ruby, for one, collects
|
|
22
|
+
# +env['rack.exception'] || env['sinatra.error']+.
|
|
23
|
+
RACK_EXCEPTION = 'rack.exception'
|
|
24
24
|
end
|
|
25
25
|
end
|
|
@@ -2,36 +2,72 @@
|
|
|
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
|
+
payload = message
|
|
30
|
+
if message.is_a?(Hash) && message.key?(:with)
|
|
31
|
+
payload = message.dup
|
|
32
|
+
with = payload.delete(:with)
|
|
33
|
+
end
|
|
15
34
|
|
|
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 || []
|
|
35
|
+
presenter = with || env[Grape::Env::API_ENDPOINT].entity_class_for_obj(payload)
|
|
20
36
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
37
|
+
unless presenter || env[Grape::Env::GRAPE_ROUTING_ARGS].nil?
|
|
38
|
+
# env['api.endpoint'].route does not work when the error occurs within a middleware
|
|
39
|
+
# the Endpoint does not have a valid env at this moment
|
|
40
|
+
http_codes = env[Grape::Env::GRAPE_ROUTING_ARGS][:route_info].http_codes || []
|
|
41
|
+
found_code = http_codes.find do |http_code|
|
|
42
|
+
(http_code[0].to_i == env[Grape::Env::API_ENDPOINT].status) && http_code[2].respond_to?(:represent)
|
|
43
|
+
end if env[Grape::Env::API_ENDPOINT].request
|
|
24
44
|
|
|
25
|
-
|
|
26
|
-
|
|
45
|
+
presenter = found_code[2] if found_code
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
return payload unless presenter
|
|
27
49
|
|
|
28
|
-
|
|
29
|
-
embeds = { env: env }
|
|
50
|
+
embeds = { env: }
|
|
30
51
|
embeds[:version] = env[Grape::Env::API_VERSION] if env.key?(Grape::Env::API_VERSION)
|
|
31
|
-
|
|
52
|
+
presenter.represent(payload, embeds).serializable_hash
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def wrap_message(message)
|
|
56
|
+
return message if message.is_a?(Hash)
|
|
57
|
+
|
|
58
|
+
{ message: }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def format_structured_message(_structured_message)
|
|
62
|
+
raise NotImplementedError
|
|
32
63
|
end
|
|
33
64
|
|
|
34
|
-
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def inherited(klass)
|
|
68
|
+
super
|
|
69
|
+
ErrorFormatter.register(klass)
|
|
70
|
+
end
|
|
35
71
|
end
|
|
36
72
|
end
|
|
37
73
|
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,18 @@
|
|
|
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
|
-
|
|
9
|
+
# Answers nil when nothing is registered for the format, the way
|
|
10
|
+
# +Parser.parser_for+ does. What to fall back to then is the API's own
|
|
11
|
+
# +default_error_formatter+, which is the caller's state rather than the
|
|
12
|
+
# registry's: +Middleware::Error+ holds it and applies it.
|
|
13
|
+
def formatter_for(format, error_formatters = nil)
|
|
14
|
+
return error_formatters[format] if error_formatters&.key?(format)
|
|
21
15
|
|
|
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
|
|
16
|
+
registry[format]
|
|
33
17
|
end
|
|
34
18
|
end
|
|
35
19
|
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,48 @@
|
|
|
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
|
+
# The backtrace is deliberately left unset: +Exception#backtrace+ builds
|
|
19
|
+
# the whole Array of location strings, and the response only renders one
|
|
20
|
+
# when the API asked for it. The exception travels along, so
|
|
21
|
+
# +Middleware::Error#error_response+ can still materialize it there.
|
|
22
|
+
def self.from_exception(exception)
|
|
23
|
+
new(
|
|
24
|
+
status: exception.status,
|
|
25
|
+
message: exception.message,
|
|
26
|
+
headers: exception.headers,
|
|
27
|
+
original_exception: exception
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Normalize heterogeneous inputs into an ErrorResponse. Preserves the
|
|
32
|
+
# public contract that users can still `throw :error, hash` from their
|
|
33
|
+
# own middleware or `rescue_from` handlers.
|
|
34
|
+
def self.coerce(input)
|
|
35
|
+
case input
|
|
36
|
+
when ErrorResponse
|
|
37
|
+
input
|
|
38
|
+
when Grape::Exceptions::Base
|
|
39
|
+
from_exception(input)
|
|
40
|
+
when Hash
|
|
41
|
+
new(**input.slice(:status, :message, :headers, :backtrace, :original_exception))
|
|
42
|
+
else
|
|
43
|
+
new
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
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
|