redmine_airbrake_backend 0.4.3 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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