-
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.
refactor(helpers): refactor helpers module
- Refactors helper module into separate files in "helpers" folder - Existing "helpers" file left as facilitator to split helper module - Report "generate_raw_exceptions" function split into helper file
- Loading branch information
Showing
5 changed files
with
209 additions
and
195 deletions.
There are no files selected for viewing
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 |
---|---|---|
@@ -1,156 +1,3 @@ | ||
require 'uri' | ||
require 'set' | ||
require 'json' | ||
|
||
|
||
module Bugsnag | ||
module Helpers | ||
MAX_STRING_LENGTH = 3072 | ||
MAX_PAYLOAD_LENGTH = 256000 | ||
MAX_ARRAY_LENGTH = 40 | ||
RAW_DATA_TYPES = [Numeric, TrueClass, FalseClass] | ||
|
||
## | ||
# Trim the size of value if the serialized JSON value is longer than is | ||
# accepted by Bugsnag | ||
def self.trim_if_needed(value) | ||
value = "" if value.nil? | ||
sanitized_value = Bugsnag::Cleaner.clean_object_encoding(value) | ||
return sanitized_value unless payload_too_long?(sanitized_value) | ||
reduced_value = trim_strings_in_value(sanitized_value) | ||
return reduced_value unless payload_too_long?(reduced_value) | ||
reduced_value = truncate_arrays_in_value(reduced_value) | ||
return reduced_value unless payload_too_long?(reduced_value) | ||
remove_metadata_from_events(reduced_value) | ||
end | ||
|
||
## | ||
# Merges r_hash into l_hash recursively, favouring the values in r_hash. | ||
# | ||
# Returns a new array consisting of the merged values | ||
def self.deep_merge(l_hash, r_hash) | ||
l_hash.merge(r_hash) do |key, l_val, r_val| | ||
if l_val.is_a?(Hash) && r_val.is_a?(Hash) | ||
deep_merge(l_val, r_val) | ||
elsif l_val.is_a?(Array) && r_val.is_a?(Array) | ||
l_val.concat(r_val) | ||
else | ||
r_val | ||
end | ||
end | ||
end | ||
|
||
## | ||
# Merges r_hash into l_hash recursively, favouring the values in r_hash. | ||
# | ||
# Overwrites the values in the existing l_hash | ||
def self.deep_merge!(l_hash, r_hash) | ||
l_hash.merge!(r_hash) do |key, l_val, r_val| | ||
if l_val.is_a?(Hash) && r_val.is_a?(Hash) | ||
deep_merge(l_val, r_val) | ||
elsif l_val.is_a?(Array) && r_val.is_a?(Array) | ||
l_val.concat(r_val) | ||
else | ||
r_val | ||
end | ||
end | ||
end | ||
|
||
private | ||
|
||
TRUNCATION_INFO = '[TRUNCATED]' | ||
|
||
## | ||
# Check if a value is a raw type which should not be trimmed, truncated | ||
# or converted to a string | ||
def self.is_json_raw_type?(value) | ||
RAW_DATA_TYPES.detect {|klass| value.is_a?(klass)} != nil | ||
end | ||
|
||
# Shorten array until it fits within the payload size limit when serialized | ||
def self.truncate_array(array) | ||
return [] unless array.respond_to?(:slice) | ||
array.slice(0, MAX_ARRAY_LENGTH).map do |item| | ||
truncate_arrays_in_value(item) | ||
end | ||
end | ||
|
||
# Trim all strings to be less than the maximum allowed string length | ||
def self.trim_strings_in_value(value) | ||
return value if is_json_raw_type?(value) | ||
case value | ||
when Hash | ||
trim_strings_in_hash(value) | ||
when Array, Set | ||
trim_strings_in_array(value) | ||
else | ||
trim_as_string(value) | ||
end | ||
end | ||
|
||
# Validate that the serialized JSON string value is below maximum payload | ||
# length | ||
def self.payload_too_long?(value) | ||
if value.is_a?(String) | ||
value.length >= MAX_PAYLOAD_LENGTH | ||
else | ||
::JSON.dump(value).length >= MAX_PAYLOAD_LENGTH | ||
end | ||
end | ||
|
||
def self.trim_strings_in_hash(hash) | ||
return {} unless hash.is_a?(Hash) | ||
hash.each_with_object({}) do |(key, value), reduced_hash| | ||
if reduced_value = trim_strings_in_value(value) | ||
reduced_hash[key] = reduced_value | ||
end | ||
end | ||
end | ||
|
||
# If possible, convert the provided object to a string and trim to the | ||
# maximum allowed string length | ||
def self.trim_as_string(text) | ||
return "" unless text.respond_to? :to_s | ||
text = text.to_s | ||
if text.length > MAX_STRING_LENGTH | ||
length = MAX_STRING_LENGTH - TRUNCATION_INFO.length | ||
text = text.slice(0, length) + TRUNCATION_INFO | ||
end | ||
text | ||
end | ||
|
||
def self.trim_strings_in_array(collection) | ||
return [] unless collection.respond_to?(:map) | ||
collection.map {|value| trim_strings_in_value(value)} | ||
end | ||
|
||
def self.truncate_arrays_in_value(value) | ||
case value | ||
when Hash | ||
truncate_arrays_in_hash(value) | ||
when Array, Set | ||
truncate_array(value) | ||
else | ||
value | ||
end | ||
end | ||
|
||
# Remove `metaData` from array of `events` within object | ||
def self.remove_metadata_from_events(object) | ||
return {} unless object.is_a?(Hash) and object[:events].respond_to?(:map) | ||
object[:events].map do |event| | ||
event.delete(:metaData) if object.is_a?(Hash) | ||
end | ||
object | ||
end | ||
|
||
def self.truncate_arrays_in_hash(hash) | ||
return {} unless hash.is_a?(Hash) | ||
hash.each_with_object({}) do |(key, value), reduced_hash| | ||
if reduced_value = truncate_arrays_in_value(value) | ||
reduced_hash[key] = reduced_value | ||
end | ||
end | ||
end | ||
end | ||
end | ||
require "bugsnag/helpers/exceptions" | ||
require "bugsnag/helpers/merge" | ||
require "bugsnag/helpers/trim" |
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,43 @@ | ||
module Bugsnag | ||
module Helpers | ||
MAX_EXCEPTIONS_TO_UNWRAP = 5 | ||
|
||
class << self | ||
## | ||
# Generates a list of exceptions | ||
def generate_raw_exceptions(exception) | ||
exceptions = [] | ||
|
||
ex = exception | ||
while !ex.nil? && !exceptions.include?(ex) && exceptions.length < MAX_EXCEPTIONS_TO_UNWRAP | ||
|
||
unless ex.is_a? Exception | ||
if ex.respond_to?(:to_exception) | ||
ex = ex.to_exception | ||
elsif ex.respond_to?(:exception) | ||
ex = ex.exception | ||
end | ||
end | ||
|
||
unless ex.is_a?(Exception) || (defined?(Java::JavaLang::Throwable) && ex.is_a?(Java::JavaLang::Throwable)) | ||
Bugsnag.configuration.warn("Converting non-Exception to RuntimeError: #{ex.inspect}") | ||
ex = RuntimeError.new(ex.to_s) | ||
ex.set_backtrace caller | ||
end | ||
|
||
exceptions << ex | ||
|
||
ex = if ex.respond_to?(:cause) && ex.cause | ||
ex.cause | ||
elsif ex.respond_to?(:continued_exception) && ex.continued_exception | ||
ex.continued_exception | ||
elsif ex.respond_to?(:original_exception) && ex.original_exception | ||
ex.original_exception | ||
end | ||
end | ||
|
||
exceptions | ||
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
module Bugsnag | ||
module Helpers | ||
class << self | ||
## | ||
# Merges r_hash into l_hash recursively, favouring the values in r_hash. | ||
# | ||
# Returns a new array consisting of the merged values | ||
def deep_merge(l_hash, r_hash) | ||
l_hash.merge(r_hash) do |_key, l_val, r_val| | ||
if l_val.is_a?(Hash) && r_val.is_a?(Hash) | ||
deep_merge(l_val, r_val) | ||
elsif l_val.is_a?(Array) && r_val.is_a?(Array) | ||
l_val.concat(r_val) | ||
else | ||
r_val | ||
end | ||
end | ||
end | ||
|
||
## | ||
# Merges r_hash into l_hash recursively, favouring the values in r_hash. | ||
# | ||
# Overwrites the values in the existing l_hash | ||
def deep_merge!(l_hash, r_hash) | ||
l_hash.merge!(r_hash) do |_key, l_val, r_val| | ||
if l_val.is_a?(Hash) && r_val.is_a?(Hash) | ||
deep_merge(l_val, r_val) | ||
elsif l_val.is_a?(Array) && r_val.is_a?(Array) | ||
l_val.concat(r_val) | ||
else | ||
r_val | ||
end | ||
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
require 'uri' | ||
require 'set' | ||
require 'json' | ||
|
||
module Bugsnag | ||
module Helpers | ||
MAX_STRING_LENGTH = 3072 | ||
MAX_PAYLOAD_LENGTH = 256000 | ||
MAX_ARRAY_LENGTH = 40 | ||
RAW_DATA_TYPES = [Numeric, TrueClass, FalseClass] | ||
|
||
class << self | ||
## | ||
# Trim the size of value if the serialized JSON value is longer than is | ||
# accepted by Bugsnag | ||
def trim_if_needed(value) | ||
value = "" if value.nil? | ||
sanitized_value = Bugsnag::Cleaner.clean_object_encoding(value) | ||
return sanitized_value unless payload_too_long?(sanitized_value) | ||
reduced_value = trim_strings_in_value(sanitized_value) | ||
return reduced_value unless payload_too_long?(reduced_value) | ||
reduced_value = truncate_arrays_in_value(reduced_value) | ||
return reduced_value unless payload_too_long?(reduced_value) | ||
remove_metadata_from_events(reduced_value) | ||
end | ||
|
||
private | ||
|
||
TRUNCATION_INFO = '[TRUNCATED]' | ||
|
||
## | ||
# Check if a value is a raw type which should not be trimmed, truncated | ||
# or converted to a string | ||
def json_raw_type?(value) | ||
RAW_DATA_TYPES.detect { |klass| value.is_a?(klass) } != nil | ||
end | ||
|
||
# Shorten array until it fits within the payload size limit when serialized | ||
def truncate_array(array) | ||
return [] unless array.respond_to?(:slice) | ||
array.slice(0, MAX_ARRAY_LENGTH).map do |item| | ||
truncate_arrays_in_value(item) | ||
end | ||
end | ||
|
||
# Trim all strings to be less than the maximum allowed string length | ||
def trim_strings_in_value(value) | ||
return value if json_raw_type?(value) | ||
case value | ||
when Hash | ||
trim_strings_in_hash(value) | ||
when Array, Set | ||
trim_strings_in_array(value) | ||
else | ||
trim_as_string(value) | ||
end | ||
end | ||
|
||
# Validate that the serialized JSON string value is below maximum payload | ||
# length | ||
def payload_too_long?(value) | ||
if value.is_a?(String) | ||
value.length >= MAX_PAYLOAD_LENGTH | ||
else | ||
::JSON.dump(value).length >= MAX_PAYLOAD_LENGTH | ||
end | ||
end | ||
|
||
def trim_strings_in_hash(hash) | ||
return {} unless hash.is_a?(Hash) | ||
hash.each_with_object({}) do |(key, value), reduced_hash| | ||
if (reduced_value = trim_strings_in_value(value)) | ||
reduced_hash[key] = reduced_value | ||
end | ||
end | ||
end | ||
|
||
# If possible, convert the provided object to a string and trim to the | ||
# maximum allowed string length | ||
def trim_as_string(text) | ||
return "" unless text.respond_to? :to_s | ||
text = text.to_s | ||
if text.length > MAX_STRING_LENGTH | ||
length = MAX_STRING_LENGTH - TRUNCATION_INFO.length | ||
text = text.slice(0, length) + TRUNCATION_INFO | ||
end | ||
text | ||
end | ||
|
||
def trim_strings_in_array(collection) | ||
return [] unless collection.respond_to?(:map) | ||
collection.map { |value| trim_strings_in_value(value) } | ||
end | ||
|
||
def truncate_arrays_in_value(value) | ||
case value | ||
when Hash | ||
truncate_arrays_in_hash(value) | ||
when Array, Set | ||
truncate_array(value) | ||
else | ||
value | ||
end | ||
end | ||
|
||
# Remove `metaData` from array of `events` within object | ||
def remove_metadata_from_events(object) | ||
return {} unless object.is_a?(Hash) && object[:events].respond_to?(:map) | ||
object[:events].map do |event| | ||
event.delete(:metaData) if object.is_a?(Hash) | ||
end | ||
object | ||
end | ||
|
||
def truncate_arrays_in_hash(hash) | ||
return {} unless hash.is_a?(Hash) | ||
hash.each_with_object({}) do |(key, value), reduced_hash| | ||
if (reduced_value = truncate_arrays_in_value(value)) | ||
reduced_hash[key] = reduced_value | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |
Oops, something went wrong.