redmine_airbrake_backend 0.6.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,133 +0,0 @@
1
- require 'json'
2
- require 'base64'
3
- require 'redmine_airbrake_backend/error'
4
-
5
- module RedmineAirbrakeBackend
6
- # Represents a request received by airbrake
7
- class Request
8
- class Error < StandardError; end
9
- class Invalid < Error; end
10
-
11
- attr_reader :api_key, :project, :tracker, :category, :priority, :assignee, :repository, :type
12
- attr_reader :params, :notifier, :errors, :context, :request, :env
13
- attr_reader :environment_name
14
-
15
- def initialize(config, data = {})
16
- # config
17
- @config = self.class.parse_config(config)
18
- raise Invalid.new('Encoded configuration in api-key is missing') if @config.blank?
19
-
20
- # API key
21
- @api_key = @config[:api_key]
22
- raise Invalid.new('No or invalid api-key') if @api_key.blank?
23
-
24
- # Project
25
- @project = Project.where(identifier: @config[:project]).first
26
- raise Invalid.new('No or invalid project') if @project.blank?
27
- raise Invalid.new('Airbrake not enabled for project') if @project.enabled_modules.where(name: :airbrake).empty?
28
-
29
- # Check configuration
30
- raise Invalid.new('Custom field for notice hash is not configured!') if notice_hash_field.blank?
31
-
32
- # Tracker
33
- @tracker = record_for(@project.trackers, :tracker)
34
- raise Invalid.new('No or invalid tracker') if @tracker.blank?
35
- raise Invalid.new('Custom field for notice hash not available on selected tracker') if @tracker.custom_fields.where(id: notice_hash_field.id).first.blank?
36
-
37
- # Category
38
- @category = record_for(@project.issue_categories, :category)
39
-
40
- # Priority
41
- @priority = record_for(IssuePriority, :priority) || IssuePriority.default
42
-
43
- # Assignee
44
- @assignee = record_for(@project.users, :assignee, [:id, :login])
45
-
46
- # Repository
47
- @repository = @project.repositories.where(identifier: (@config[:repository] || '')).first
48
-
49
- # Type
50
- @type = @config[:type]
51
-
52
- # Errors
53
- @errors = (data[:errors] || [])
54
- @errors.each { |e| e.request = self }
55
-
56
- # Environment
57
- @env = data[:env]
58
-
59
- # Request
60
- @request = data[:request]
61
-
62
- # Context
63
- @context = data[:context]
64
-
65
- # Notifier
66
- @notifier = data[:notifier]
67
-
68
- # Params
69
- @params = data[:params]
70
-
71
- # Environment name
72
- @environment_name = (@env[:environment_name].presence || @env[:name].presence) if @env.present?
73
- @environment_name ||= @context[:environment].presence if @context.present?
74
- end
75
-
76
- def notice_hash_field
77
- custom_field(:hash_field)
78
- end
79
-
80
- def occurrences_field
81
- custom_field(:occurrences_field)
82
- end
83
-
84
- def reopen?
85
- reopen_regexp = project_setting(:reopen_regexp)
86
-
87
- return false if environment_name.blank? || reopen_regexp.blank?
88
-
89
- !!(environment_name =~ /#{reopen_regexp}/i)
90
- end
91
-
92
- def reopen_repeat_description?
93
- !!project_setting(:reopen_repeat_description)
94
- end
95
-
96
- private
97
-
98
- def record_for(on, config_key, fields = [:id, :name])
99
- fields.each do |field|
100
- val = on.where(field => @config[config_key]).first
101
- return val if val.present?
102
- end
103
-
104
- project_setting(config_key)
105
- end
106
-
107
- def project_setting(key)
108
- return nil if @project.airbrake_settings.blank?
109
-
110
- @project.airbrake_settings.send(key) if @project.airbrake_settings.respond_to?(key)
111
- end
112
-
113
- def custom_field(key)
114
- @project.issue_custom_fields.where(id: global_setting(key)).first || CustomField.where(id: global_setting(key), is_for_all: true).first
115
- end
116
-
117
- def global_setting(key)
118
- Setting.plugin_redmine_airbrake_backend[key]
119
- end
120
-
121
- def self.parse_config(api_key_data)
122
- config = ::JSON.parse(api_key_data).symbolize_keys rescue nil
123
-
124
- return config.symbolize_keys if config.is_a?(Hash)
125
-
126
- config = ::JSON.parse(Base64.decode64(api_key_data)) rescue nil
127
-
128
- return config.symbolize_keys if config.is_a?(Hash)
129
-
130
- nil
131
- end
132
- end
133
- end
@@ -1,85 +0,0 @@
1
- require 'redmine_airbrake_backend/request'
2
-
3
- module RedmineAirbrakeBackend
4
- class Request
5
- # Represents a JSON request received by airbrake
6
- class JSON < ::RedmineAirbrakeBackend::Request
7
- # Creates a request from a parsed json request
8
- def self.parse(parsed_json_data)
9
- raise Invalid if parsed_json_data.blank?
10
-
11
- context = parse_plain_section(parsed_json_data[:context])
12
- params = parse_plain_section(parsed_json_data[:params])
13
- notifier = parse_plain_section(parsed_json_data[:notifier])
14
- request = parse_plain_section(parsed_json_data[:request])
15
- env = parse_plain_section(parsed_json_data[:environment])
16
- config = parsed_json_data[:key]
17
-
18
- errors = []
19
- errors += parse_errors(parsed_json_data[:errors])
20
- errors << parse_report(parsed_json_data[:type], parsed_json_data[:report])
21
- errors.compact!
22
-
23
- raise Invalid.new('No error or report found') if errors.blank?
24
-
25
- new(config, context: context, params: params, notifier: notifier, errors: errors, request: request, env: env)
26
- end
27
-
28
- private
29
-
30
- def self.parse_plain_section(section_data)
31
- return {} if section_data.blank?
32
-
33
- section_data.symbolize_keys
34
- end
35
-
36
- def self.parse_errors(errors)
37
- errors = []
38
-
39
- (errors || []).each do |error_data|
40
- error = parse_error(error_data)
41
- errors << ::RedmineAirbrakeBackend::Error.new(error) if error.present?
42
- end
43
-
44
- errors
45
- end
46
-
47
- def self.parse_error(error_data)
48
- return nil if error_data.blank?
49
-
50
- error = {}
51
- error[:type] = secure_type(error_data[:type])
52
- error[:message] = error_data[:message]
53
- error[:backtrace] = error_data[:backtrace].map do |backtrace_element|
54
- {
55
- line: backtrace_element[:line].to_i,
56
- file: backtrace_element[:file],
57
- method: (backtrace_element[:method] || backtrace_element[:function])
58
- }
59
- end
60
-
61
- error
62
- end
63
-
64
- def self.parse_report(type, report_data)
65
- return nil if report_data.blank?
66
-
67
- sec_type = secure_type(type)
68
-
69
- require "redmine_airbrake_backend/report/#{sec_type}" rescue nil
70
-
71
- clazz = "RedmineAirbrakeBackend::Report::#{sec_type.camelize}".constantize rescue nil
72
-
73
- return nil if clazz.blank?
74
-
75
- report = clazz.parse(report_data)
76
-
77
- report.present? ? ::RedmineAirbrakeBackend::Error.new(report) : nil
78
- end
79
-
80
- def self.secure_type(type)
81
- type.to_s.gsub(/[^a-zA-Z0-9_-]/, '')
82
- end
83
- end
84
- end
85
- end
@@ -1,122 +0,0 @@
1
- require 'json'
2
- require 'nokogiri'
3
- require 'redmine_airbrake_backend/request'
4
-
5
- module RedmineAirbrakeBackend
6
- class Request
7
- # Represents a XML request received by airbrake
8
- class XML < ::RedmineAirbrakeBackend::Request
9
- # Supported airbrake api versions
10
- SUPPORTED_API_VERSIONS = %w(2.4)
11
-
12
- class UnsupportedVersion < Error; end
13
-
14
- # Creates a notice from an airbrake xml request
15
- def self.parse(xml_data)
16
- doc = Nokogiri::XML(xml_data) rescue nil
17
- raise Invalid if doc.blank?
18
-
19
- notice = doc.at('//notice')
20
- raise Invalid if notice.blank?
21
-
22
- # Version
23
- version = notice.attributes['version']
24
- raise Invalid.new('No version') if version.blank? || version.value.blank?
25
- raise UnsupportedVersion.new(version) unless SUPPORTED_API_VERSIONS.include?(version.value)
26
-
27
- error_data = parse_error(notice)
28
- raise Invalid.new('No error found') if error_data.blank?
29
- error = RedmineAirbrakeBackend::Error.new(error_data)
30
-
31
- notifier = convert_element(notice.at('//notifier'))
32
- request = parse_request(notice)
33
- env = convert_element(notice.at('//server-environment'))
34
- config = notice.at('//api-key').inner_text
35
-
36
- new(config, notifier: notifier, errors: [error], request: request, env: env)
37
- end
38
-
39
- private
40
-
41
- def self.parse_error(notice_doc)
42
- error = convert_element(notice_doc.at('//error'))
43
-
44
- # map class to type
45
- error[:type] ||= error[:class]
46
- error.delete(:class)
47
-
48
- error[:backtrace] = format_backtrace(error[:backtrace])
49
-
50
- error
51
- end
52
-
53
- def self.parse_request(notice_doc)
54
- convert_element(notice_doc.at('//request'))
55
- end
56
-
57
- def self.convert_element(elem)
58
- return nil if elem.nil?
59
-
60
- return elem.children.first.inner_text if elem.children.count == 1 && elem.children.first.is_a?(Nokogiri::XML::Text)
61
- return convert_attributes(elem.attributes) if elem.children.blank?
62
- return convert_var_elements(elem.children) if elem.children.count == elem.children.select { |c| c.name == 'var' }.count
63
-
64
- h = convert_children(elem)
65
- h.delete_if { |k, v| k.strip.blank? }
66
- h.symbolize_keys
67
- end
68
-
69
- def self.convert_children(elem)
70
- h = {}
71
-
72
- elem.children.each do |e|
73
- key = format_hash_key(e.name)
74
-
75
- if h.key?(key)
76
- h[key] = [h[key]] unless h[key].is_a?(Array)
77
- h[key] << convert_element(e)
78
- else
79
- h[key] = convert_element(e)
80
- end
81
- end
82
-
83
- h
84
- end
85
-
86
- def self.convert_var_elements(elements)
87
- vars = {}
88
- elements.each do |elem|
89
- vars[format_hash_key(elem.attributes['key'])] = elem.inner_text
90
- end
91
- vars.delete_if { |k, v| k.strip.blank? }
92
- vars.symbolize_keys
93
- end
94
-
95
- def self.format_hash_key(key)
96
- key.to_s.gsub(/-/, '_')
97
- end
98
-
99
- def self.convert_attributes(attributes)
100
- attrs = {}
101
-
102
- attributes.each do |k, v|
103
- attrs[k] = v.value
104
- end
105
-
106
- attrs.symbolize_keys
107
- end
108
-
109
- def self.ensure_hash_array(data)
110
- return nil if data.blank?
111
-
112
- d = (data.is_a?(Array) ? data : [data]).compact
113
- d.reject! { |e| !e.is_a?(Hash) }
114
- d.blank? ? nil : d
115
- end
116
-
117
- def self.format_backtrace(backtrace)
118
- ensure_hash_array(backtrace).first[:line] rescue nil
119
- end
120
- end
121
- end
122
- end