redmine_airbrake_backend 1.0.3 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/app/controllers/airbrake_controller.rb +39 -34
- data/app/controllers/airbrake_notice_controller.rb +11 -13
- data/app/controllers/airbrake_report_controller.rb +5 -11
- data/app/helpers/airbrake_helper.rb +21 -10
- data/app/views/airbrake/issue_description/default.erb +15 -14
- data/lib/redmine_airbrake_backend/backtrace_element.rb +1 -1
- data/lib/redmine_airbrake_backend/error.rb +5 -27
- data/lib/redmine_airbrake_backend/ios_report.rb +25 -17
- data/lib/redmine_airbrake_backend/notice.rb +64 -0
- data/lib/redmine_airbrake_backend/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5926fca2b10cfdf572494c9891adc633b583bbdb
|
4
|
+
data.tar.gz: 0dc91282b47937a48254ff1e3da0fbd4b555c0be
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 58cfc22082b4c0098a5c756d915401fe334a3346c55541038b21fcd500978f5646ab7627234b5eb882f00561df2bbc28569de2b3c2976d1708c31857a59036ad
|
7
|
+
data.tar.gz: 71de8ff6c61f7894afbafb4a763cef7402651beaac638f0756839a140fdc877c0ba7f6cc0466b5f39883b39460472ddd1531d968a37599b2aaaf772bd5c589ff
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'tempfile'
|
2
|
-
require 'redmine_airbrake_backend/
|
2
|
+
require 'redmine_airbrake_backend/notice'
|
3
3
|
|
4
4
|
|
5
5
|
# Controller with airbrake related stuff
|
@@ -11,7 +11,6 @@ class AirbrakeController < ::ApplicationController
|
|
11
11
|
prepend_before_action :load_records
|
12
12
|
prepend_before_action :parse_key
|
13
13
|
prepend_before_action :find_project
|
14
|
-
before_action :set_environment
|
15
14
|
before_action :authorize
|
16
15
|
|
17
16
|
after_action :cleanup_tempfiles
|
@@ -25,14 +24,11 @@ class AirbrakeController < ::ApplicationController
|
|
25
24
|
end
|
26
25
|
|
27
26
|
def parse_key
|
28
|
-
@key = JSON.parse(params[:key]).symbolize_keys
|
27
|
+
@key = JSON.parse(params[:key]).symbolize_keys rescue nil
|
29
28
|
|
30
29
|
# API key
|
31
30
|
invalid_request!('No or invalid API key') if @key.blank? || @key[:key].blank?
|
32
31
|
params[:key] = @key[:key]
|
33
|
-
|
34
|
-
# Type
|
35
|
-
@type = @key[:type] || (params[:context][:language].split('/', 2).first.downcase rescue nil)
|
36
32
|
end
|
37
33
|
|
38
34
|
def load_records
|
@@ -56,10 +52,6 @@ class AirbrakeController < ::ApplicationController
|
|
56
52
|
@repository = @project.repositories.find_by(identifier: (@key[:repository] || ''))
|
57
53
|
end
|
58
54
|
|
59
|
-
def set_environment
|
60
|
-
@environment ||= params[:context][:environment].presence rescue nil
|
61
|
-
end
|
62
|
-
|
63
55
|
def invalid_request!(message)
|
64
56
|
raise InvalidRequest.new(message)
|
65
57
|
end
|
@@ -112,39 +104,52 @@ class AirbrakeController < ::ApplicationController
|
|
112
104
|
custom_field(:occurrences_field)
|
113
105
|
end
|
114
106
|
|
107
|
+
def create_issue!
|
108
|
+
return if @notice.blank?
|
109
|
+
return if @notice.errors.blank?
|
110
|
+
|
111
|
+
@issue = find_or_initialize_issue(@notice)
|
112
|
+
|
113
|
+
set_issue_custom_field_values(@issue, @notice)
|
114
|
+
|
115
|
+
reopen_issue(@issue, @notice) if @issue.persisted? && @issue.status.is_closed? && reopen_issue?(@notice)
|
116
|
+
|
117
|
+
@issue = nil unless @issue.save
|
118
|
+
end
|
119
|
+
|
115
120
|
# Load or initialize issue by project, tracker and airbrake hash
|
116
|
-
def find_or_initialize_issue(
|
117
|
-
issue_ids = CustomValue.where(customized_type: Issue.name, custom_field_id: notice_hash_field.id, value:
|
121
|
+
def find_or_initialize_issue(notice)
|
122
|
+
issue_ids = CustomValue.where(customized_type: Issue.name, custom_field_id: notice_hash_field.id, value: notice.id).pluck(:customized_id)
|
118
123
|
|
119
124
|
issue = Issue.find_by(id: issue_ids, project_id: @project.id, tracker_id: @tracker.id)
|
120
125
|
|
121
126
|
return issue if issue.present?
|
122
127
|
|
123
|
-
initialize_issue(
|
128
|
+
initialize_issue(notice)
|
124
129
|
end
|
125
130
|
|
126
|
-
def initialize_issue(
|
131
|
+
def initialize_issue(notice)
|
127
132
|
issue = Issue.new(
|
128
|
-
subject:
|
133
|
+
subject: notice.subject,
|
129
134
|
project: @project,
|
130
135
|
tracker: @tracker,
|
131
136
|
author: User.current,
|
132
137
|
category: @category,
|
133
138
|
priority: @priority,
|
134
|
-
description: render_description(
|
139
|
+
description: render_description(notice),
|
135
140
|
assigned_to: @assignee
|
136
141
|
)
|
137
142
|
|
138
|
-
add_attachments_to_issue(issue,
|
143
|
+
add_attachments_to_issue(issue, notice)
|
139
144
|
|
140
145
|
issue
|
141
146
|
end
|
142
147
|
|
143
|
-
def set_issue_custom_field_values(issue,
|
148
|
+
def set_issue_custom_field_values(issue, notice)
|
144
149
|
custom_field_values = {}
|
145
150
|
|
146
151
|
# Error ID
|
147
|
-
custom_field_values[notice_hash_field.id] =
|
152
|
+
custom_field_values[notice_hash_field.id] = notice.id if issue.new_record?
|
148
153
|
|
149
154
|
# Update occurrences
|
150
155
|
if occurrences_field.present?
|
@@ -155,12 +160,12 @@ class AirbrakeController < ::ApplicationController
|
|
155
160
|
issue.custom_field_values = custom_field_values
|
156
161
|
end
|
157
162
|
|
158
|
-
def add_attachments_to_issue(issue,
|
159
|
-
return if
|
163
|
+
def add_attachments_to_issue(issue, notice)
|
164
|
+
return if notice.attachments.blank?
|
160
165
|
|
161
166
|
@tempfiles ||= []
|
162
167
|
|
163
|
-
|
168
|
+
notice.attachments.each do |data|
|
164
169
|
filename = data[:filename].presence || Redmine::Utils.random_hex(16)
|
165
170
|
|
166
171
|
file = Tempfile.new(filename)
|
@@ -173,37 +178,37 @@ class AirbrakeController < ::ApplicationController
|
|
173
178
|
end
|
174
179
|
end
|
175
180
|
|
176
|
-
def reopen_issue?
|
181
|
+
def reopen_issue?(notice)
|
177
182
|
reopen_regexp = project_setting(:reopen_regexp)
|
178
183
|
|
179
184
|
return false if reopen_regexp.blank?
|
180
|
-
return false if
|
185
|
+
return false if notice.environment_name.blank?
|
181
186
|
|
182
|
-
!!(
|
187
|
+
!!(notice.environment_name =~ /#{reopen_regexp}/i)
|
183
188
|
end
|
184
189
|
|
185
190
|
def issue_reopen_repeat_description?
|
186
191
|
!!project_setting(:reopen_repeat_description)
|
187
192
|
end
|
188
193
|
|
189
|
-
def reopen_issue(issue,
|
190
|
-
return if
|
194
|
+
def reopen_issue(issue, notice)
|
195
|
+
return if notice.environment_name.blank?
|
191
196
|
|
192
|
-
desc = "*Issue reopened after occurring again in _#{
|
193
|
-
desc << "\n\n#{render_description(
|
197
|
+
desc = "*Issue reopened after occurring again in _#{notice.environment_name}_ environment.*"
|
198
|
+
desc << "\n\n#{render_description(notice)}" if issue_reopen_repeat_description?
|
194
199
|
|
195
200
|
issue.status = issue.tracker.default_status
|
196
201
|
|
197
202
|
issue.init_journal(User.current, desc)
|
198
203
|
|
199
|
-
add_attachments_to_issue(issue,
|
204
|
+
add_attachments_to_issue(issue, notice)
|
200
205
|
end
|
201
206
|
|
202
|
-
def render_description(
|
203
|
-
locals = {
|
207
|
+
def render_description(notice)
|
208
|
+
locals = { notice: notice }
|
204
209
|
|
205
|
-
if template_exists?("airbrake/issue_description/#{
|
206
|
-
render_to_string("airbrake/issue_description/#{
|
210
|
+
if template_exists?("airbrake/issue_description/#{notice.type}")
|
211
|
+
render_to_string("airbrake/issue_description/#{notice.type}", layout: false, locals: locals)
|
207
212
|
else
|
208
213
|
render_to_string('airbrake/issue_description/default', layout: false, locals: locals)
|
209
214
|
end
|
@@ -2,26 +2,24 @@
|
|
2
2
|
class AirbrakeNoticeController < ::AirbrakeController
|
3
3
|
accept_api_auth :notices
|
4
4
|
|
5
|
+
before_action :parse_notice
|
6
|
+
|
5
7
|
# Handle airbrake notices
|
6
8
|
def notices
|
7
|
-
|
9
|
+
create_issue!
|
8
10
|
|
9
11
|
render_airbrake_response
|
10
12
|
end
|
11
13
|
|
12
14
|
private
|
13
15
|
|
14
|
-
def
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
reopen_issue(issue, error) if issue.persisted? && issue.status.is_closed? && reopen_issue?
|
23
|
-
|
24
|
-
@issue ||= issue if issue.save
|
25
|
-
end
|
16
|
+
def parse_notice
|
17
|
+
@notice = RedmineAirbrakeBackend::Notice.new(
|
18
|
+
errors: params[:errors].map { |e| RedmineAirbrakeBackend::Error.new(e) },
|
19
|
+
params: params[:params],
|
20
|
+
session: params[:session],
|
21
|
+
context: params[:context],
|
22
|
+
environment: params[:environment]
|
23
|
+
)
|
26
24
|
end
|
27
25
|
end
|
@@ -5,24 +5,18 @@ require 'redmine_airbrake_backend/ios_report'
|
|
5
5
|
class AirbrakeReportController < ::AirbrakeController
|
6
6
|
accept_api_auth :ios_reports
|
7
7
|
|
8
|
+
before_action :parse_report
|
9
|
+
|
8
10
|
# Handle airbrake iOS reports
|
9
11
|
def ios_reports
|
10
|
-
create_issue
|
12
|
+
create_issue!
|
11
13
|
|
12
14
|
render_airbrake_response
|
13
15
|
end
|
14
16
|
|
15
17
|
private
|
16
18
|
|
17
|
-
def
|
18
|
-
|
19
|
-
|
20
|
-
@issue = find_or_initialize_issue(error)
|
21
|
-
|
22
|
-
set_issue_custom_field_values(@issue, error)
|
23
|
-
|
24
|
-
reopen_issue(@issue, error) if @issue.persisted? && @issue.status.is_closed? && reopen_issue?
|
25
|
-
|
26
|
-
@issue = nil unless @issue.save
|
19
|
+
def parse_report
|
20
|
+
@notice = RedmineAirbrakeBackend::IosReport.new(params)
|
27
21
|
end
|
28
22
|
end
|
@@ -1,7 +1,17 @@
|
|
1
1
|
module AirbrakeHelper
|
2
|
+
# Error title
|
3
|
+
def airbrake_error_title(error)
|
4
|
+
if error.type.blank?
|
5
|
+
error.message
|
6
|
+
else
|
7
|
+
"#{error.type}: #{error.message}"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
2
11
|
# Wiki markup for a table
|
3
|
-
def
|
12
|
+
def airbrake_format_table(data)
|
4
13
|
lines = []
|
14
|
+
|
5
15
|
data.each do |key, value|
|
6
16
|
next if value.blank?
|
7
17
|
|
@@ -11,19 +21,20 @@ module AirbrakeHelper
|
|
11
21
|
lines << "|@#{key}@|@#{value.map { |k, v| "#{k}: #{v}"}.join(', ')}@|"
|
12
22
|
end
|
13
23
|
end
|
24
|
+
|
14
25
|
lines.join("\n")
|
15
26
|
end
|
16
27
|
|
17
28
|
# Wiki markup for a list item
|
18
|
-
def
|
29
|
+
def airbrake_format_list_item(name, value)
|
19
30
|
return '' if value.blank?
|
20
31
|
|
21
32
|
"* *#{name}:* #{value}"
|
22
33
|
end
|
23
34
|
|
24
35
|
# Wiki markup for backtrace element with link to repository if possible
|
25
|
-
def
|
26
|
-
repository =
|
36
|
+
def airbrake_format_backtrace_element(element)
|
37
|
+
repository = airbrake_repository_for_backtrace_element(element)
|
27
38
|
|
28
39
|
if repository.blank?
|
29
40
|
if element.line.blank?
|
@@ -32,7 +43,7 @@ module AirbrakeHelper
|
|
32
43
|
markup = "@#{element.file}:#{element.line}@"
|
33
44
|
end
|
34
45
|
else
|
35
|
-
filename =
|
46
|
+
filename = airbrake_filename_for_backtrace_element(element)
|
36
47
|
|
37
48
|
if repository.identifier.blank?
|
38
49
|
markup = "source:\"#{filename}#L#{element.line}\""
|
@@ -46,15 +57,15 @@ module AirbrakeHelper
|
|
46
57
|
|
47
58
|
private
|
48
59
|
|
49
|
-
def
|
60
|
+
def airbrake_repository_for_backtrace_element(element)
|
50
61
|
return nil unless element.file.start_with?('[PROJECT_ROOT]')
|
51
62
|
|
52
|
-
filename =
|
63
|
+
filename = airbrake_filename_for_backtrace_element(element)
|
53
64
|
|
54
|
-
|
65
|
+
airbrake_repositories_for_backtrace.find { |r| r.entry(filename) }
|
55
66
|
end
|
56
67
|
|
57
|
-
def
|
68
|
+
def airbrake_repositories_for_backtrace
|
58
69
|
return @_bactrace_repositories unless @_bactrace_repositories.nil?
|
59
70
|
|
60
71
|
if @repository.present?
|
@@ -66,7 +77,7 @@ module AirbrakeHelper
|
|
66
77
|
@_bactrace_repositories
|
67
78
|
end
|
68
79
|
|
69
|
-
def
|
80
|
+
def airbrake_filename_for_backtrace_element(element)
|
70
81
|
return nil if element.file.blank?
|
71
82
|
|
72
83
|
element.file[14..-1]
|
@@ -1,44 +1,45 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
<% notice.errors.each do |error| %>
|
2
|
+
h1. <%= airbrake_error_title(error) %>
|
3
3
|
|
4
4
|
<% if error.backtrace.present? %>
|
5
5
|
h2. Stacktrace:
|
6
6
|
|
7
|
-
p((. <%= raw error.backtrace.map { |e|
|
7
|
+
p((. <%= raw error.backtrace.map { |e| airbrake_format_backtrace_element(e) }.join("\n") %>
|
8
8
|
<% end %>
|
9
9
|
|
10
|
+
<% end %>
|
10
11
|
|
11
|
-
<% if
|
12
|
+
<% if notice.application.present? %>
|
12
13
|
h2. Application:
|
13
14
|
|
14
|
-
<%=
|
15
|
-
<%=
|
15
|
+
<%= airbrake_format_list_item('Name', notice.application[:name]) %>
|
16
|
+
<%= airbrake_format_list_item('Version', notice.application[:version]) %>
|
16
17
|
<% end %>
|
17
18
|
|
18
19
|
|
19
|
-
<% if params
|
20
|
+
<% if notice.params.present? %>
|
20
21
|
h2. Parameters:
|
21
22
|
|
22
|
-
<%=
|
23
|
+
<%= airbrake_format_table(notice.params) %>
|
23
24
|
<% end %>
|
24
25
|
|
25
26
|
|
26
|
-
<% if
|
27
|
+
<% if notice.session.present? %>
|
27
28
|
h2. Session
|
28
29
|
|
29
|
-
<%=
|
30
|
+
<%= airbrake_format_table(notice.session) %>
|
30
31
|
<% end %>
|
31
32
|
|
32
33
|
|
33
|
-
<% if
|
34
|
+
<% if notice.context.present? %>
|
34
35
|
h2. Context
|
35
36
|
|
36
|
-
<%=
|
37
|
+
<%= airbrake_format_table(notice.context) %>
|
37
38
|
<% end %>
|
38
39
|
|
39
40
|
|
40
|
-
<% if
|
41
|
+
<% if notice.environment.present? %>
|
41
42
|
h2. Environment
|
42
43
|
|
43
|
-
<%=
|
44
|
+
<%= airbrake_format_table(notice.environment) %>
|
44
45
|
<% end %>
|
@@ -5,30 +5,20 @@ require 'redmine_airbrake_backend/backtrace_element'
|
|
5
5
|
module RedmineAirbrakeBackend
|
6
6
|
# Error received by airbrake
|
7
7
|
class Error
|
8
|
-
attr_reader :type, :message, :backtrace
|
9
|
-
attr_reader :id, :subject, :application, :attachments
|
8
|
+
attr_reader :id, :type, :message, :backtrace
|
10
9
|
|
11
|
-
def initialize(
|
10
|
+
def initialize(options)
|
12
11
|
# Type
|
13
|
-
@type =
|
12
|
+
@type = options[:type]
|
14
13
|
|
15
14
|
# Message
|
16
|
-
@message =
|
15
|
+
@message = options[:message]
|
17
16
|
|
18
17
|
# Backtrace
|
19
|
-
@backtrace =
|
18
|
+
@backtrace = options[:backtrace].map { |b| BacktraceElement.new(b) }
|
20
19
|
|
21
20
|
# Error ID
|
22
21
|
@id = generate_id
|
23
|
-
|
24
|
-
# Subject
|
25
|
-
@subject = generate_subject
|
26
|
-
|
27
|
-
# Attachments
|
28
|
-
@attachments = (data[:attachments].presence || []).compact
|
29
|
-
|
30
|
-
# Application
|
31
|
-
@application = data[:application].presence
|
32
22
|
end
|
33
23
|
|
34
24
|
private
|
@@ -41,17 +31,5 @@ module RedmineAirbrakeBackend
|
|
41
31
|
|
42
32
|
Digest::MD5.hexdigest(h.compact.join("\n"))
|
43
33
|
end
|
44
|
-
|
45
|
-
def generate_subject
|
46
|
-
s = ''
|
47
|
-
|
48
|
-
if @type.blank? || @message.starts_with?("#{@type}:")
|
49
|
-
s = "[#{@id[0..7]}] #{@message}"
|
50
|
-
else
|
51
|
-
s = "[#{@id[0..7]}] #{@type}: #{@message}"
|
52
|
-
end
|
53
|
-
|
54
|
-
s[0..254].strip
|
55
|
-
end
|
56
34
|
end
|
57
35
|
end
|
@@ -1,21 +1,26 @@
|
|
1
|
-
require 'redmine_airbrake_backend/
|
1
|
+
require 'redmine_airbrake_backend/notice'
|
2
2
|
|
3
3
|
|
4
4
|
module RedmineAirbrakeBackend
|
5
5
|
# iOS Report received by airbrake
|
6
|
-
class IosReport <
|
7
|
-
def initialize(
|
8
|
-
|
6
|
+
class IosReport < Notice
|
7
|
+
def initialize(options)
|
8
|
+
error, application, attachments = self.class.parse(options[:report])
|
9
|
+
|
10
|
+
super({
|
11
|
+
errors: [Error.new(error)],
|
12
|
+
context: options[:context],
|
13
|
+
application: application,
|
14
|
+
attachments: attachments
|
15
|
+
})
|
9
16
|
end
|
10
17
|
|
11
18
|
private
|
12
19
|
|
13
20
|
def self.parse(data)
|
14
|
-
error
|
15
|
-
|
16
|
-
|
17
|
-
attachments: [],
|
18
|
-
}
|
21
|
+
error = { backtrace: [] }
|
22
|
+
application = {}
|
23
|
+
attachments = []
|
19
24
|
|
20
25
|
header_finished = false
|
21
26
|
next_line_is_message = false
|
@@ -25,8 +30,8 @@ module RedmineAirbrakeBackend
|
|
25
30
|
data.split("\n").each do |line|
|
26
31
|
header_finished = true if line =~ /^(Application Specific Information|Last Exception Backtrace|Thread \d+( Crashed)?):$/
|
27
32
|
|
28
|
-
unless
|
29
|
-
ii = parse_header_line(line, error)
|
33
|
+
unless header_finished
|
34
|
+
ii = parse_header_line(line, error, application)
|
30
35
|
indicent_identifier ||= ii if ii
|
31
36
|
end
|
32
37
|
|
@@ -38,7 +43,10 @@ module RedmineAirbrakeBackend
|
|
38
43
|
|
39
44
|
crashed_thread = false if line =~ /^Thread \d+:$/
|
40
45
|
|
41
|
-
|
46
|
+
if crashed_thread
|
47
|
+
backtrace = parse_backtrace_element(line)
|
48
|
+
error[:backtrace] << backtrace if backtrace
|
49
|
+
end
|
42
50
|
|
43
51
|
crashed_thread = true if error[:backtrace].compact.blank? && line =~ /^(Last Exception Backtrace|Thread \d+ Crashed):$/
|
44
52
|
|
@@ -47,15 +55,15 @@ module RedmineAirbrakeBackend
|
|
47
55
|
|
48
56
|
return nil if error.blank?
|
49
57
|
|
50
|
-
|
58
|
+
attachments << {
|
51
59
|
filename: "#{indicent_identifier}.crash",
|
52
60
|
data: data
|
53
61
|
} if indicent_identifier.present?
|
54
62
|
|
55
|
-
error
|
63
|
+
[error, application, attachments]
|
56
64
|
end
|
57
65
|
|
58
|
-
def self.parse_header_line(line, error)
|
66
|
+
def self.parse_header_line(line, error, application)
|
59
67
|
key, value = line.split(':', 2).map { |s| s.strip }
|
60
68
|
|
61
69
|
return nil if key.blank? || value.blank?
|
@@ -68,9 +76,9 @@ module RedmineAirbrakeBackend
|
|
68
76
|
when 'Incident Identifier'
|
69
77
|
return value
|
70
78
|
when 'Identifier'
|
71
|
-
|
79
|
+
application[:name] = value
|
72
80
|
when 'Version'
|
73
|
-
|
81
|
+
application[:version] = value
|
74
82
|
end
|
75
83
|
|
76
84
|
nil
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'redmine_airbrake_backend/error'
|
2
|
+
|
3
|
+
|
4
|
+
module RedmineAirbrakeBackend
|
5
|
+
# Notice received by airbrake
|
6
|
+
class Notice
|
7
|
+
attr_reader :id, :subject, :type, :environment_name
|
8
|
+
attr_reader :errors, :params, :session, :context, :environment, :application, :attachments
|
9
|
+
|
10
|
+
def initialize(options)
|
11
|
+
# Errors
|
12
|
+
@errors = options[:errors].compact
|
13
|
+
|
14
|
+
# Params
|
15
|
+
@params = options[:params]
|
16
|
+
|
17
|
+
# Session
|
18
|
+
@session = options[:session]
|
19
|
+
|
20
|
+
# Context
|
21
|
+
@context = options[:context].reject { |k, v| ['notifier'].include?(k) }
|
22
|
+
|
23
|
+
# Environment
|
24
|
+
@environment = options[:environment]
|
25
|
+
|
26
|
+
# Application
|
27
|
+
@application = options[:application]
|
28
|
+
|
29
|
+
# Attachments
|
30
|
+
@attachments = (options[:attachments].presence || []).compact
|
31
|
+
|
32
|
+
# Environment name
|
33
|
+
@environment_name = options[:context][:environment].presence rescue nil
|
34
|
+
|
35
|
+
# Type
|
36
|
+
@type = options[:type] || (options[:context][:language].split('/', 2).first.downcase rescue nil)
|
37
|
+
|
38
|
+
# Error ID
|
39
|
+
@id = generate_id
|
40
|
+
|
41
|
+
# Subject
|
42
|
+
@subject = generate_subject
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def generate_id
|
48
|
+
Digest::MD5.hexdigest(@errors.map(&:id).join("\n"))
|
49
|
+
end
|
50
|
+
|
51
|
+
def generate_subject
|
52
|
+
error = @errors.first
|
53
|
+
s = ''
|
54
|
+
|
55
|
+
if error.type.blank? || error.message.starts_with?("#{error.type}:")
|
56
|
+
s = "[#{@id[0..7]}] #{error.message}"
|
57
|
+
else
|
58
|
+
s = "[#{@id[0..7]}] #{error.type}: #{error.message}"
|
59
|
+
end
|
60
|
+
|
61
|
+
s[0..254].strip
|
62
|
+
end
|
63
|
+
end
|
64
|
+
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: 1.0
|
4
|
+
version: 1.1.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: 2016-01-
|
11
|
+
date: 2016-01-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -85,6 +85,7 @@ files:
|
|
85
85
|
- lib/redmine_airbrake_backend/engine.rb
|
86
86
|
- lib/redmine_airbrake_backend/error.rb
|
87
87
|
- lib/redmine_airbrake_backend/ios_report.rb
|
88
|
+
- lib/redmine_airbrake_backend/notice.rb
|
88
89
|
- lib/redmine_airbrake_backend/patches/issue_category.rb
|
89
90
|
- lib/redmine_airbrake_backend/patches/issue_priority.rb
|
90
91
|
- lib/redmine_airbrake_backend/patches/project.rb
|