-
-
Notifications
You must be signed in to change notification settings - Fork 176
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: support problem+json for error messages (#583)
- Loading branch information
Showing
17 changed files
with
514 additions
and
77 deletions.
There are no files selected for viewing
36 changes: 36 additions & 0 deletions
36
lib/pact_broker/api/decorators/custom_error_problem_json_decorator.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# Formats a message string into application/problem+json format. | ||
|
||
module PactBroker | ||
module Api | ||
module Decorators | ||
class CustomErrorProblemJSONDecorator | ||
|
||
# @option title [String] | ||
# @option type [String] | ||
# @option detail [String] | ||
# @option status [Integer] HTTP status code | ||
def initialize(title:, type:, detail:, status: ) | ||
@title = title | ||
@type = type | ||
@detail = detail | ||
@status = status | ||
end | ||
|
||
# @return [Hash] | ||
def to_hash(decorator_options = {}) | ||
{ | ||
"title" => @title, | ||
"type" => "#{decorator_options.dig(:user_options, :base_url)}/problem/#{@type}", | ||
"detail" => @detail, | ||
"status" => @status | ||
} | ||
end | ||
|
||
# @return [String] JSON | ||
def to_json(decorator_options = {}) | ||
to_hash(decorator_options).to_json | ||
end | ||
end | ||
end | ||
end | ||
end |
34 changes: 34 additions & 0 deletions
34
lib/pact_broker/api/decorators/runtime_error_problem_json_decorator.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# Formats a message string into application/problem+json format. | ||
|
||
module PactBroker | ||
module Api | ||
module Decorators | ||
class RuntimeErrorProblemJSONDecorator | ||
|
||
# @param message [String] | ||
def initialize(message) | ||
@message = message | ||
end | ||
|
||
# @return [Hash] | ||
def to_hash(decorator_options = {}) | ||
{ | ||
"title" => "Server error", | ||
"type" => "#{decorator_options.dig(:user_options, :base_url)}/problems/server_error", | ||
"detail" => message, | ||
"status" => 500 | ||
} | ||
end | ||
|
||
# @return [String] JSON | ||
def to_json(decorator_options = {}) | ||
to_hash(decorator_options).to_json | ||
end | ||
|
||
private | ||
|
||
attr_reader :message | ||
end | ||
end | ||
end | ||
end |
57 changes: 57 additions & 0 deletions
57
lib/pact_broker/api/decorators/validation_errors_problem_json_decorator.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
# Formats a nested Hash of errors as it comes out of the Dry Validation library | ||
# into application/problem+json format. | ||
|
||
module PactBroker | ||
module Api | ||
module Decorators | ||
class ValidationErrorsProblemJSONDecorator | ||
|
||
# @param errors [Hash] | ||
def initialize(errors) | ||
@errors = errors | ||
end | ||
|
||
# @return [Hash] | ||
def to_hash(decorator_options = {}) | ||
error_list = [] | ||
walk_errors(errors, error_list, "", decorator_options.dig(:user_options, :base_url)) | ||
{ | ||
"title" => "Validation errors", | ||
"type" => "#{decorator_options.dig(:user_options, :base_url)}/problems/validation-error", | ||
"status" => 400, | ||
"errors" => error_list | ||
} | ||
end | ||
|
||
# @return [String] JSON | ||
def to_json(decorator_options = {}) | ||
to_hash(decorator_options).to_json | ||
end | ||
|
||
private | ||
|
||
attr_reader :errors | ||
|
||
def walk_errors(object, list, path, base_url) | ||
if object.is_a?(Hash) | ||
object.each { | key, value | walk_errors(value, list, "#{path}/#{key}", base_url) } | ||
elsif object.is_a?(Array) | ||
object.each { | value | walk_errors(value, list, path, base_url) } | ||
elsif object.is_a?(String) | ||
append_error(list, object, path, base_url) | ||
end | ||
end | ||
|
||
def append_error(list, message, path, base_url) | ||
list << { | ||
"type" => "#{base_url}/problems/invalid-body-property-value", | ||
"title" => "Validation error", | ||
"detail" => message, | ||
"instance" => path, | ||
"status" => 400 | ||
} | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
require "pact_broker/api/decorators/validation_errors_problem_json_decorator" | ||
require "pact_broker/api/decorators/custom_error_problem_json_decorator" | ||
|
||
module PactBroker | ||
module Api | ||
module Resources | ||
module ErrorHandlingMethods | ||
|
||
# @override | ||
def handle_exception(error) | ||
error_reference = PactBroker::Errors.generate_error_reference | ||
application_context.error_logger.call(error, error_reference, request.env) | ||
if PactBroker::Errors.reportable_error?(error) | ||
PactBroker::Errors.report(error, error_reference, request.env) | ||
end | ||
headers, body = application_context.error_response_generator.call(error, error_reference, request.env) | ||
headers.each { | key, value | response.headers[key] = value } | ||
response.body = body | ||
end | ||
|
||
def set_json_error_message detail, title: "Server error", type: "server_error", status: 500 | ||
response.headers["Content-Type"] = error_response_content_type | ||
response.body = error_response_body(detail, title, type, status) | ||
end | ||
|
||
def set_json_validation_error_messages errors | ||
response.headers["Content-Type"] = error_response_content_type | ||
if problem_json_error_content_type? | ||
response.body = PactBroker::Api::Decorators::ValidationErrorsProblemJSONDecorator.new(errors).to_json(decorator_options) | ||
else | ||
response.body = { errors: errors }.to_json | ||
end | ||
end | ||
|
||
def error_response_content_type | ||
if problem_json_error_content_type? | ||
"application/problem+json;charset=utf-8" | ||
else | ||
"application/hal+json;charset=utf-8" | ||
end | ||
end | ||
|
||
def error_response_body(detail, title, type, status) | ||
if problem_json_error_content_type? | ||
PactBroker::Api::Decorators::CustomErrorProblemJSONDecorator.new(detail: detail, title: title, type: type, status: status).to_json(decorator_options) | ||
else | ||
{ error: detail }.to_json | ||
end | ||
end | ||
|
||
def problem_json_error_content_type? | ||
request.headers["Accept"]&.include?("application/problem+json") | ||
end | ||
end | ||
end | ||
end | ||
end |
41 changes: 0 additions & 41 deletions
41
lib/pact_broker/api/resources/error_response_body_generator.rb
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
require "pact_broker/configuration" | ||
require "pact_broker/api/decorators/runtime_error_problem_json_decorator" | ||
|
||
module PactBroker | ||
module Api | ||
module Resources | ||
class ErrorResponseGenerator | ||
include PactBroker::Logging | ||
|
||
# @param error [StandardError] | ||
# @param error_reference [String] an error reference to display to the user | ||
# @param env [Hash] the rack env | ||
# @return [Hash, String] the response headers to set, the response body to set | ||
def self.call error, error_reference, env = {} | ||
body = response_body_hash(error, error_reference, env, display_message(error, obfuscated_error_message(error_reference))) | ||
return headers(env), body.to_json | ||
end | ||
|
||
def self.display_message(error, obfuscated_message) | ||
if PactBroker.configuration.show_backtrace_in_error_response? | ||
error.message || obfuscated_message | ||
else | ||
PactBroker::Errors.reportable_error?(error) ? obfuscated_message : error.message | ||
end | ||
end | ||
|
||
private_class_method def self.response_body_hash(error, error_reference, env, message) | ||
if problem_json?(env) | ||
problem_json_response_body(message, env) | ||
else | ||
hal_json_response_body(error, error_reference, message) | ||
end | ||
end | ||
|
||
private_class_method def self.hal_json_response_body(error, error_reference, message) | ||
response_body = { | ||
error: { | ||
message: message, | ||
reference: error_reference | ||
} | ||
} | ||
if PactBroker.configuration.show_backtrace_in_error_response? | ||
response_body[:error][:backtrace] = error.backtrace | ||
end | ||
response_body | ||
end | ||
|
||
private_class_method def self.problem_json_response_body(message, env) | ||
PactBroker::Api::Decorators::RuntimeErrorProblemJSONDecorator.new(message).to_hash(user_options: { base_url: env["pactbroker.base_url" ] }) | ||
end | ||
|
||
private_class_method def self.obfuscated_error_message(error_reference) | ||
"An error has occurred. The details have been logged with the reference #{error_reference}" | ||
end | ||
|
||
private_class_method def self.headers(env) | ||
if problem_json?(env) | ||
{ "Content-Type" => "application/problem+json;charset=utf-8" } | ||
else | ||
{ "Content-Type" => "application/hal+json;charset=utf-8" } | ||
end | ||
end | ||
|
||
private_class_method def self.problem_json?(env) | ||
env["HTTP_ACCEPT"]&.include?("application/problem+json") | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.