redmine_airbrake_backend 0.4.3 → 0.5.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.
@@ -0,0 +1,73 @@
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
+
20
+ (parsed_json_data[:errors] || []).each do |error_data|
21
+ error = parse_error(error_data)
22
+ errors << ::RedmineAirbrakeBackend::Error.new(error) if error.present?
23
+ end
24
+
25
+ report = parse_report(parsed_json_data[:type], parsed_json_data[:report])
26
+ errors << ::RedmineAirbrakeBackend::Error.new(report) if report.present?
27
+
28
+ errors.compact!
29
+
30
+ raise Invalid.new('No error or report found') if errors.blank?
31
+
32
+ new(config, context: context, params: params, notifier: notifier, errors: errors, request: request, env: env)
33
+ end
34
+
35
+ private
36
+
37
+ def self.parse_plain_section(section_data)
38
+ return {} if section_data.blank?
39
+
40
+ section_data.symbolize_keys
41
+ end
42
+
43
+ def self.parse_error(error_data)
44
+ return nil if error_data.blank?
45
+
46
+ error = {}
47
+ error[:type] = error_data[:type]
48
+ error[:message] = error_data[:message]
49
+ error[:backtrace] = error_data[:backtrace].map do |backtrace_element|
50
+ {
51
+ line: backtrace_element[:line].to_i,
52
+ file: backtrace_element[:file],
53
+ method: (backtrace_element[:method] || backtrace_element[:function])
54
+ }
55
+ end
56
+
57
+ error
58
+ end
59
+
60
+ def self.parse_report(type, report_data)
61
+ return nil if report_data.blank?
62
+
63
+ require "redmine_airbrake_backend/report/#{type}"
64
+
65
+ clazz = "RedmineAirbrakeBackend::Report::#{type.to_s.camelize}".constantize rescue nil
66
+
67
+ return nil if clazz.blank?
68
+
69
+ clazz.parse(report_data)
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,121 @@
1
+ require 'json'
2
+ require 'hpricot'
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 = Hpricot::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?
25
+ raise UnsupportedVersion.new(version) unless SUPPORTED_API_VERSIONS.include?(version)
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
+ request = convert_element(notice_doc.at('request'))
55
+
56
+ if request.present? && request[:session].present?
57
+ request[:session][:log] = request[:session][:log].present? ? format_session_log(request[:session][:log]) : nil
58
+ end
59
+
60
+ request
61
+ end
62
+
63
+ def self.convert_element(elem)
64
+ return nil if elem.nil?
65
+ return elem.children.first.inner_text if !elem.children.nil? && elem.children.count == 1 && elem.children.first.is_a?(Hpricot::Text)
66
+ return elem.attributes.to_hash.symbolize_keys if elem.children.nil?
67
+ return convert_var_elements(elem.children) if elem.children.count == elem.children.select { |c| c.name == 'var' }.count
68
+
69
+ h = {}
70
+ elem.children.each do |e|
71
+ key = format_hash_key(e.name)
72
+ if h.key?(key)
73
+ h[key] = [h[key]] unless h[key].is_a?(Array)
74
+ h[key] << convert_element(e)
75
+ else
76
+ h[key] = convert_element(e)
77
+ end
78
+ end
79
+ h.delete_if { |k, v| k.strip.blank? }
80
+ h.symbolize_keys
81
+ end
82
+
83
+ def self.convert_var_elements(elements)
84
+ vars = {}
85
+ elements.each do |elem|
86
+ vars[format_hash_key(elem.attributes['key'])] = elem.inner_text
87
+ end
88
+ vars.delete_if { |k, v| k.strip.blank? }
89
+ vars.symbolize_keys
90
+ end
91
+
92
+ def self.format_hash_key(key)
93
+ key.to_s.gsub(/-/, '_')
94
+ end
95
+
96
+ def self.ensure_hash_array(data)
97
+ return nil if data.blank?
98
+
99
+ d = (data.is_a?(Array) ? data : [data]).compact
100
+ d.reject! { |e| !e.is_a?(Hash) }
101
+ d.blank? ? nil : d
102
+ end
103
+
104
+ def self.format_backtrace(backtrace)
105
+ ensure_hash_array(backtrace).first[:line] rescue nil
106
+ end
107
+
108
+ def self.format_session_log(log)
109
+ log = ::JSON.parse(log) rescue nil
110
+
111
+ log = ensure_hash_array(log)
112
+ return nil if log.blank?
113
+
114
+ log.map! { |l| l.symbolize_keys!; l[:time] = (Time.parse(l[:time]) rescue nil); l }
115
+ log.reject! { |l| l[:time].blank? }
116
+
117
+ log.blank? ? nil : log
118
+ end
119
+ end
120
+ end
121
+ end
@@ -1,3 +1,4 @@
1
1
  module RedmineAirbrakeBackend
2
- VERSION = '0.4.3'
2
+ # Version of this gem
3
+ VERSION = '0.5.0'
3
4
  end
@@ -1,5 +1,5 @@
1
1
  # coding: utf-8
2
- $:.push File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.push File.expand_path('../lib', __FILE__)
3
3
 
4
4
  require 'redmine_airbrake_backend/version'
5
5
 
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
8
8
  spec.version = RedmineAirbrakeBackend::VERSION
9
9
  spec.authors = ['Florian Schwab']
10
10
  spec.email = ['me@ydkn.de']
11
- spec.description = %q{Plugin which adds Airbrake support to Redmine}
12
- spec.summary = %q{This plugin provides the necessary API to use Redmine as a Airbrake backend}
11
+ spec.description = %q(Plugin which adds Airbrake support to Redmine)
12
+ spec.summary = %q(This plugin provides the necessary API to use Redmine as a Airbrake backend)
13
13
  spec.homepage = 'https://github.com/ydkn/redmine_airbrake_backend'
14
14
  spec.license = 'MIT'
15
15
 
@@ -1,7 +1,6 @@
1
1
  require File.expand_path('../../test_helper', __FILE__)
2
2
 
3
3
  class NoticeTest < ActiveSupport::TestCase
4
-
5
4
  test 'parse' do
6
5
  api_key = {
7
6
  api_key: 'foobar',
@@ -48,7 +47,6 @@ class NoticeTest < ActiveSupport::TestCase
48
47
  private
49
48
 
50
49
  def prepare_xml(xml_data)
51
- xml_data.split("\n").map{|l| l.strip}.join('')
50
+ xml_data.split("\n").map { |l| l.strip }.join('')
52
51
  end
53
-
54
52
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redmine_airbrake_backend
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Schwab
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-20 00:00:00.000000000 Z
11
+ date: 2014-10-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -89,14 +89,17 @@ extra_rdoc_files: []
89
89
  files:
90
90
  - ".gitignore"
91
91
  - Gemfile
92
+ - Gemfile.lock
92
93
  - MIT-LICENSE
93
94
  - README.md
94
95
  - Rakefile
95
96
  - app/controllers/airbrake_controller.rb
97
+ - app/controllers/airbrake_notice_controller.rb
96
98
  - app/controllers/airbrake_project_settings_controller.rb
99
+ - app/controllers/airbrake_report_controller.rb
97
100
  - app/helpers/airbrake_helper.rb
98
101
  - app/models/airbrake_project_setting.rb
99
- - app/views/airbrake/_issue_description.erb
102
+ - app/views/airbrake/issue_description/default.erb
100
103
  - app/views/projects/settings/_airbrake.html.erb
101
104
  - app/views/settings/_airbrake.html.erb
102
105
  - bin/rails
@@ -107,12 +110,16 @@ files:
107
110
  - db/migrate/20131003151228_add_reopen_repeat_description.rb
108
111
  - lib/redmine_airbrake_backend.rb
109
112
  - lib/redmine_airbrake_backend/engine.rb
110
- - lib/redmine_airbrake_backend/notice.rb
113
+ - lib/redmine_airbrake_backend/error.rb
111
114
  - lib/redmine_airbrake_backend/patches/issue_category.rb
112
115
  - lib/redmine_airbrake_backend/patches/issue_priority.rb
113
116
  - lib/redmine_airbrake_backend/patches/project.rb
114
117
  - lib/redmine_airbrake_backend/patches/projects_helper.rb
115
118
  - lib/redmine_airbrake_backend/patches/tracker.rb
119
+ - lib/redmine_airbrake_backend/report/ios.rb
120
+ - lib/redmine_airbrake_backend/request.rb
121
+ - lib/redmine_airbrake_backend/request/json.rb
122
+ - lib/redmine_airbrake_backend/request/xml.rb
116
123
  - lib/redmine_airbrake_backend/version.rb
117
124
  - lib/tasks/test.rake
118
125
  - redmine_airbrake_backend.gemspec
@@ -1,50 +0,0 @@
1
- h1. <%= @notice.error[:message] %>
2
-
3
- <% if @notice.error.present? && @notice.error[:backtrace].present? %>
4
- h2. Stacktrace:
5
-
6
- p((. <%=raw @notice.error[:backtrace].collect{|e| format_backtrace_element(e)}.join("\n") %>
7
- <% end %>
8
-
9
- <% if @notice.request.present? %>
10
- h2. Request:
11
-
12
- <%=format_list_item('URL', @notice.request[:url]) %>
13
- <%=format_list_item('Component', @notice.request[:component]) %>
14
- <%=format_list_item('Action', @notice.request[:action]) %>
15
-
16
- <% if @notice.request[:params].present? %>
17
- h2. Parameters:
18
-
19
- <%=format_table(@notice.request[:params]) %>
20
- <% end %>
21
-
22
- <% if @notice.request[:cgi_data].present? %>
23
- h2. Headers:
24
-
25
- <%=format_table(@notice.request[:cgi_data]) %>
26
- <% end %>
27
-
28
- <% log = @notice.request[:session].delete(:log) if @notice.request[:session].present? %>
29
- <% if @notice.request[:session].present? %>
30
- h2. Session:
31
-
32
- <%=format_table(@notice.request[:session]) %>
33
- <% end %>
34
-
35
- <% if log.present? %>
36
- h2. Log:
37
-
38
- <pre>
39
- <%=format_log(log) %>
40
- </pre>
41
- <% end %>
42
- <% end %>
43
-
44
- <% if @notice.env.present? %>
45
- h2. Environment
46
-
47
- <%=format_list_item('Name', @notice.env[:environment_name]) %>
48
- <%=format_list_item('Directory', @notice.env[:project_root]) %>
49
- <%=format_list_item('Hostname', @notice.env[:hostname]) %>
50
- <% end %>
@@ -1,109 +0,0 @@
1
- require 'json'
2
- require 'hpricot'
3
- require 'htmlentities'
4
-
5
- module RedmineAirbrakeBackend
6
- class Notice
7
- SUPPORTED_API_VERSIONS = %w(2.4)
8
-
9
- class NoticeInvalid < StandardError; end
10
- class UnsupportedVersion < StandardError; end
11
-
12
- attr_reader :version, :params, :notifier, :error, :request, :env
13
-
14
- def initialize(version, params, notifier, options={})
15
- @version = version
16
- @params = params
17
- @notifier = notifier
18
-
19
- @error = options.delete(:error)
20
- @request = options.delete(:request)
21
- @env = options.delete(:env)
22
- end
23
-
24
- def self.parse(xml_data)
25
- doc = Hpricot::XML(xml_data)
26
-
27
- raise NoticeInvalid if (notice = doc.at('notice')).blank?
28
- raise NoticeInvalid.new('no version') if (version = notice.attributes['version']).blank?
29
- raise UnsupportedVersion.new(version) unless SUPPORTED_API_VERSIONS.include?(version)
30
-
31
- params = JSON.parse(notice.at('api-key').inner_text).symbolize_keys rescue nil
32
- raise NoticeInvalid.new('no or invalid api-key') if params.blank?
33
-
34
- raise NoticeInvalid.new('no notifier') if (notifier = convert_element(notice.at('notifier'))).blank?
35
-
36
- raise NoticeInvalid.new('no error') if (error = convert_element(notice.at('error'))).blank?
37
- raise NoticeInvalid.new('no message') if error[:message].blank?
38
-
39
- error[:backtrace] = format_backtrace(error[:backtrace])
40
-
41
- request = convert_element(notice.at('request'))
42
- if request.present? && request[:session].present?
43
- request[:session][:log] = request[:session][:log].present? ? format_session_log(request[:session][:log]) : nil
44
- end
45
-
46
- env = convert_element(notice.at('server-environment'))
47
-
48
- new(version, params, notifier, error: error, request: request, env: env)
49
- end
50
-
51
- private
52
-
53
- def self.convert_element(elem)
54
- return nil if elem.nil?
55
- return elem.children.first.inner_text if !elem.children.nil? && elem.children.count == 1 && elem.children.first.is_a?(Hpricot::Text)
56
- return elem.attributes.to_hash.symbolize_keys if elem.children.nil?
57
- return convert_var_elements(elem.children) if elem.children.count == elem.children.select{|c| c.name == 'var'}.count
58
-
59
- h = {}
60
- elem.children.each do |e|
61
- key = format_hash_key(e.name)
62
- if h.key?(key)
63
- h[key] = [h[key]] unless h[key].is_a?(Array)
64
- h[key] << convert_element(e)
65
- else
66
- h[key] = convert_element(e)
67
- end
68
- end
69
- h.delete_if{|k,v| k.strip.blank?}
70
- h.symbolize_keys
71
- end
72
-
73
- def self.convert_var_elements(elements)
74
- vars = {}
75
- elements.each do |elem|
76
- vars[format_hash_key(elem.attributes['key'])] = elem.inner_text
77
- end
78
- vars.delete_if{|k,v| k.strip.blank?}
79
- vars.symbolize_keys
80
- end
81
-
82
- def self.format_hash_key(key)
83
- key.to_s.gsub(/-/, '_')
84
- end
85
-
86
- def self.ensure_hash_array(data)
87
- return nil if data.blank?
88
-
89
- d = (data.is_a?(Array) ? data : [data]).compact
90
- d.reject!{|e| !e.is_a?(Hash)}
91
- d.blank? ? nil : d
92
- end
93
-
94
- def self.format_backtrace(backtrace)
95
- ensure_hash_array(backtrace).first[:line] rescue nil
96
- end
97
-
98
- def self.format_session_log(log)
99
- log = JSON.parse(log) rescue nil
100
- return nil unless log = ensure_hash_array(log)
101
-
102
- log.map!{|l| l.symbolize_keys!; l[:time] = (Time.parse(l[:time]) rescue nil); l}
103
- log.reject!{|l| l[:time].blank?}
104
-
105
- log.blank? ? nil : log
106
- end
107
-
108
- end
109
- end