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.
- checksums.yaml +4 -4
- data/Gemfile.lock +91 -0
- data/Rakefile +3 -8
- data/app/controllers/airbrake_controller.rb +107 -127
- data/app/controllers/airbrake_notice_controller.rb +25 -0
- data/app/controllers/airbrake_project_settings_controller.rb +5 -4
- data/app/controllers/airbrake_report_controller.rb +15 -0
- data/app/helpers/airbrake_helper.rb +40 -15
- data/app/models/airbrake_project_setting.rb +1 -0
- data/app/views/airbrake/issue_description/default.erb +52 -0
- data/config/routes.rb +5 -1
- data/db/migrate/20130708102357_create_airbrake_project_settings.rb +0 -2
- data/db/migrate/20131003151228_add_reopen_repeat_description.rb +0 -2
- data/lib/redmine_airbrake_backend.rb +0 -2
- data/lib/redmine_airbrake_backend/engine.rb +10 -10
- data/lib/redmine_airbrake_backend/error.rb +78 -0
- data/lib/redmine_airbrake_backend/patches/projects_helper.rb +8 -7
- data/lib/redmine_airbrake_backend/report/ios.rb +71 -0
- data/lib/redmine_airbrake_backend/request.rb +132 -0
- data/lib/redmine_airbrake_backend/request/json.rb +73 -0
- data/lib/redmine_airbrake_backend/request/xml.rb +121 -0
- data/lib/redmine_airbrake_backend/version.rb +2 -1
- data/redmine_airbrake_backend.gemspec +3 -3
- data/test/unit/notice_test.rb +1 -3
- metadata +11 -4
- data/app/views/airbrake/_issue_description.erb +0 -50
- data/lib/redmine_airbrake_backend/notice.rb +0 -109
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fc1393573d1439588f21f8995af1bc85b4d8661e
|
4
|
+
data.tar.gz: eafd09a81fccf63b1b7af678fb59acf66d86401d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f1e451b7341129c911d65f16fe679980d5d5efd4a3c9d2b70b771ca4e13b3ff0f69b6a873ed8e69bd34d28758e5cb75ec501e292b6ae0405dfcec3b4d9e6da74
|
7
|
+
data.tar.gz: 97012b7e3cc150080116ea79121de9cc84602ab82ea125c345f3530ab69f0a9ba869298defabe076c0b31acd048347359c07bb25c6043c52eb0d1676dbd34917
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
redmine_airbrake_backend (0.5.0)
|
5
|
+
hpricot
|
6
|
+
htmlentities
|
7
|
+
rails
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
actionmailer (4.1.6)
|
13
|
+
actionpack (= 4.1.6)
|
14
|
+
actionview (= 4.1.6)
|
15
|
+
mail (~> 2.5, >= 2.5.4)
|
16
|
+
actionpack (4.1.6)
|
17
|
+
actionview (= 4.1.6)
|
18
|
+
activesupport (= 4.1.6)
|
19
|
+
rack (~> 1.5.2)
|
20
|
+
rack-test (~> 0.6.2)
|
21
|
+
actionview (4.1.6)
|
22
|
+
activesupport (= 4.1.6)
|
23
|
+
builder (~> 3.1)
|
24
|
+
erubis (~> 2.7.0)
|
25
|
+
activemodel (4.1.6)
|
26
|
+
activesupport (= 4.1.6)
|
27
|
+
builder (~> 3.1)
|
28
|
+
activerecord (4.1.6)
|
29
|
+
activemodel (= 4.1.6)
|
30
|
+
activesupport (= 4.1.6)
|
31
|
+
arel (~> 5.0.0)
|
32
|
+
activesupport (4.1.6)
|
33
|
+
i18n (~> 0.6, >= 0.6.9)
|
34
|
+
json (~> 1.7, >= 1.7.7)
|
35
|
+
minitest (~> 5.1)
|
36
|
+
thread_safe (~> 0.1)
|
37
|
+
tzinfo (~> 1.1)
|
38
|
+
arel (5.0.1.20140414130214)
|
39
|
+
builder (3.2.2)
|
40
|
+
erubis (2.7.0)
|
41
|
+
hike (1.2.3)
|
42
|
+
hpricot (0.8.6)
|
43
|
+
htmlentities (4.3.2)
|
44
|
+
i18n (0.6.11)
|
45
|
+
json (1.8.1)
|
46
|
+
mail (2.6.1)
|
47
|
+
mime-types (>= 1.16, < 3)
|
48
|
+
mime-types (2.4.2)
|
49
|
+
minitest (5.4.2)
|
50
|
+
multi_json (1.10.1)
|
51
|
+
rack (1.5.2)
|
52
|
+
rack-test (0.6.2)
|
53
|
+
rack (>= 1.0)
|
54
|
+
rails (4.1.6)
|
55
|
+
actionmailer (= 4.1.6)
|
56
|
+
actionpack (= 4.1.6)
|
57
|
+
actionview (= 4.1.6)
|
58
|
+
activemodel (= 4.1.6)
|
59
|
+
activerecord (= 4.1.6)
|
60
|
+
activesupport (= 4.1.6)
|
61
|
+
bundler (>= 1.3.0, < 2.0)
|
62
|
+
railties (= 4.1.6)
|
63
|
+
sprockets-rails (~> 2.0)
|
64
|
+
railties (4.1.6)
|
65
|
+
actionpack (= 4.1.6)
|
66
|
+
activesupport (= 4.1.6)
|
67
|
+
rake (>= 0.8.7)
|
68
|
+
thor (>= 0.18.1, < 2.0)
|
69
|
+
rake (10.3.2)
|
70
|
+
sprockets (2.12.2)
|
71
|
+
hike (~> 1.2)
|
72
|
+
multi_json (~> 1.0)
|
73
|
+
rack (~> 1.0)
|
74
|
+
tilt (~> 1.1, != 1.3.0)
|
75
|
+
sprockets-rails (2.2.0)
|
76
|
+
actionpack (>= 3.0)
|
77
|
+
activesupport (>= 3.0)
|
78
|
+
sprockets (>= 2.8, < 4.0)
|
79
|
+
thor (0.19.1)
|
80
|
+
thread_safe (0.3.4)
|
81
|
+
tilt (1.4.1)
|
82
|
+
tzinfo (1.2.2)
|
83
|
+
thread_safe (~> 0.1)
|
84
|
+
|
85
|
+
PLATFORMS
|
86
|
+
ruby
|
87
|
+
|
88
|
+
DEPENDENCIES
|
89
|
+
bundler (~> 1.3)
|
90
|
+
rake
|
91
|
+
redmine_airbrake_backend!
|
data/Rakefile
CHANGED
@@ -4,7 +4,10 @@ rescue LoadError
|
|
4
4
|
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
5
|
end
|
6
6
|
|
7
|
+
Bundler::GemHelper.install_tasks
|
8
|
+
|
7
9
|
require 'rdoc/task'
|
10
|
+
require 'rake/testtask'
|
8
11
|
|
9
12
|
RDoc::Task.new(:rdoc) do |rdoc|
|
10
13
|
rdoc.rdoc_dir = 'rdoc'
|
@@ -14,14 +17,6 @@ RDoc::Task.new(:rdoc) do |rdoc|
|
|
14
17
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
18
|
end
|
16
19
|
|
17
|
-
#APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
18
|
-
#load 'rails/tasks/engine.rake'
|
19
|
-
|
20
|
-
|
21
|
-
Bundler::GemHelper.install_tasks
|
22
|
-
|
23
|
-
require 'rake/testtask'
|
24
|
-
|
25
20
|
Rake::TestTask.new(:test) do |t|
|
26
21
|
t.libs << 'lib'
|
27
22
|
t.libs << 'test'
|
@@ -1,171 +1,151 @@
|
|
1
|
-
require '
|
1
|
+
require 'tempfile'
|
2
|
+
require 'redmine_airbrake_backend/request/xml'
|
3
|
+
require 'redmine_airbrake_backend/request/json'
|
2
4
|
|
5
|
+
# Controller with airbrake related stuff
|
3
6
|
class AirbrakeController < ::ApplicationController
|
4
|
-
|
5
|
-
prepend_before_filter :parse_notice_and_api_auth
|
6
|
-
before_filter :load_records
|
7
|
-
|
8
|
-
accept_api_auth :notice
|
9
|
-
|
10
|
-
def notice
|
11
|
-
return unless authorize(:issues, :create)
|
12
|
-
|
13
|
-
# Issue by project, tracker and hash
|
14
|
-
issue_ids = CustomValue.where(customized_type: Issue.name, custom_field_id: notice_hash_field.id, value: notice_hash).select([:customized_id]).collect{|cv| cv.customized_id}
|
15
|
-
@issue = Issue.where(id: issue_ids, project_id: @project.id, tracker_id: @tracker.id).first
|
16
|
-
@issue = Issue.new(
|
17
|
-
subject: subject,
|
18
|
-
project: @project,
|
19
|
-
tracker: @tracker,
|
20
|
-
author: User.current,
|
21
|
-
category: @category,
|
22
|
-
priority: @priority,
|
23
|
-
description: render_description,
|
24
|
-
assigned_to: @assignee
|
25
|
-
) unless @issue
|
7
|
+
skip_before_filter :verify_authenticity_token
|
26
8
|
|
27
|
-
|
9
|
+
before_filter :authorize_airbrake
|
10
|
+
before_filter :handle_request
|
28
11
|
|
29
|
-
|
30
|
-
custom_field_values[notice_hash_field.id] = notice_hash if @issue.new_record?
|
12
|
+
after_filter :cleanup_tempfiles
|
31
13
|
|
32
|
-
|
33
|
-
if occurrences_field.present?
|
34
|
-
occurrences_value = @issue.custom_value_for(occurrences_field.id)
|
35
|
-
custom_field_values[occurrences_field.id] = ((occurrences_value ? occurrences_value.value.to_i : 0) + 1).to_s
|
36
|
-
end
|
14
|
+
rescue_from RedmineAirbrakeBackend::Request::Error, with: :render_bad_request
|
37
15
|
|
38
|
-
|
16
|
+
private
|
39
17
|
|
40
|
-
|
41
|
-
|
42
|
-
desc = "*Issue reopened after occurring again in _#{@notice.env[:environment_name]}_ environment.*"
|
43
|
-
desc << "\n\n#{render_description}" if project_setting(:reopen_repeat_description)
|
18
|
+
def authorize_airbrake
|
19
|
+
@project = @request.project
|
44
20
|
|
45
|
-
|
46
|
-
|
47
|
-
end
|
21
|
+
authorize(:issues, :create)
|
22
|
+
end
|
48
23
|
|
49
|
-
|
50
|
-
|
51
|
-
notice: {
|
52
|
-
id: notice_hash,
|
53
|
-
url: issue_url(@issue)
|
54
|
-
}
|
55
|
-
}
|
56
|
-
else
|
57
|
-
render nothing: true, status: :internal_server_error
|
58
|
-
end
|
24
|
+
def render_bad_request(error)
|
25
|
+
render text: error.message, status: :bad_request
|
59
26
|
end
|
60
27
|
|
61
|
-
|
28
|
+
def parse_xml_request
|
29
|
+
@request = RedmineAirbrakeBackend::Request::XML.parse(request.body)
|
62
30
|
|
63
|
-
|
64
|
-
@notice = RedmineAirbrakeBackend::Notice.parse(request.body)
|
65
|
-
params[:key] = @notice.params[:api_key]
|
66
|
-
rescue RedmineAirbrakeBackend::Notice::NoticeInvalid, RedmineAirbrakeBackend::Notice::UnsupportedVersion
|
67
|
-
render nothing: true, status: :bad_request
|
31
|
+
params[:key] = @request.api_key
|
68
32
|
end
|
69
33
|
|
70
|
-
def
|
71
|
-
|
72
|
-
unless @project = Project.where(identifier: @notice.params[:project]).first
|
73
|
-
render text: 'Project not found!', status: :bad_request
|
74
|
-
return
|
75
|
-
end
|
34
|
+
def parse_json_request
|
35
|
+
@request = RedmineAirbrakeBackend::Request::JSON.parse(params)
|
76
36
|
|
77
|
-
|
78
|
-
|
79
|
-
render text: 'Custom field for notice hash is not configured!', status: :internal_server_error
|
80
|
-
return
|
81
|
-
end
|
37
|
+
params[:key] = @request.api_key
|
38
|
+
end
|
82
39
|
|
83
|
-
|
84
|
-
|
85
|
-
render text: 'Tracker not found!', status: :bad_request
|
86
|
-
return
|
87
|
-
end
|
40
|
+
def handle_request
|
41
|
+
@tempfiles = []
|
88
42
|
|
89
|
-
|
90
|
-
@category = record_for(@project.issue_categories, :category)
|
43
|
+
@results = []
|
91
44
|
|
92
|
-
|
93
|
-
|
45
|
+
@request.errors.each do |error|
|
46
|
+
issue = find_or_initialize_issue(error)
|
94
47
|
|
95
|
-
|
96
|
-
@assignee = record_for(@project.users, :assignee, [:id, :login])
|
97
|
-
end
|
48
|
+
set_custom_field_values(issue, @request, error.airbrake_hash)
|
98
49
|
|
99
|
-
|
100
|
-
fields.each do |field|
|
101
|
-
val = on.where(field => @notice.params[param_key]).first
|
102
|
-
return val if val.present?
|
103
|
-
end
|
50
|
+
reopen_issue(issue, error) if @request.reopen? && issue.status.is_closed?
|
104
51
|
|
105
|
-
|
52
|
+
if issue.save
|
53
|
+
@results << {
|
54
|
+
issue: issue,
|
55
|
+
hash: error.airbrake_hash
|
56
|
+
}
|
57
|
+
end
|
58
|
+
end
|
106
59
|
end
|
107
60
|
|
108
|
-
|
109
|
-
|
110
|
-
|
61
|
+
# Load or initialize issue by project, tracker and airbrake hash
|
62
|
+
def find_or_initialize_issue(error)
|
63
|
+
issue_ids = CustomValue.where(customized_type: Issue.name, custom_field_id: @request.notice_hash_field.id, value: error.airbrake_hash).pluck(:customized_id)
|
64
|
+
|
65
|
+
issue = Issue.where(id: issue_ids, project_id: @request.project.id, tracker_id: @request.tracker.id).first
|
66
|
+
|
67
|
+
return issue if issue.present?
|
68
|
+
|
69
|
+
issue = Issue.new(
|
70
|
+
subject: error.subject,
|
71
|
+
project: @request.project,
|
72
|
+
tracker: @request.tracker,
|
73
|
+
author: User.current,
|
74
|
+
category: @request.category,
|
75
|
+
priority: @request.priority,
|
76
|
+
description: render_description(error),
|
77
|
+
assigned_to: @request.assignee
|
78
|
+
)
|
79
|
+
|
80
|
+
add_error_attachments_to_issue(issue, error)
|
81
|
+
|
82
|
+
issue
|
111
83
|
end
|
112
84
|
|
113
|
-
def
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
85
|
+
def set_custom_field_values(issue, request, airbrake_hash)
|
86
|
+
custom_field_values = {}
|
87
|
+
|
88
|
+
# Error hash
|
89
|
+
custom_field_values[request.notice_hash_field.id] = airbrake_hash if issue.new_record?
|
90
|
+
|
91
|
+
# Update occurrences
|
92
|
+
if request.occurrences_field.present?
|
93
|
+
occurrences_value = issue.custom_value_for(request.occurrences_field.id)
|
94
|
+
custom_field_values[request.occurrences_field.id] = ((occurrences_value ? occurrences_value.value.to_i : 0) + 1).to_s
|
119
95
|
end
|
120
|
-
|
96
|
+
|
97
|
+
issue.custom_field_values = custom_field_values
|
121
98
|
end
|
122
99
|
|
123
|
-
def
|
124
|
-
|
125
|
-
h << @notice.error[:class]
|
126
|
-
h << @notice.error[:message]
|
127
|
-
h += normalized_backtrace
|
100
|
+
def add_error_attachments_to_issue(issue, error)
|
101
|
+
return if error.attachments.blank?
|
128
102
|
|
129
|
-
|
130
|
-
|
103
|
+
error.attachments.each do |attachment_data|
|
104
|
+
filename = attachment_data[:filename].presence || Redmine::Utils.random_hex(16)
|
131
105
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
106
|
+
file = Tempfile.new(filename)
|
107
|
+
@tempfiles << file
|
108
|
+
|
109
|
+
file.write(attachment_data[:data])
|
110
|
+
file.rewind
|
111
|
+
|
112
|
+
attachment = Attachment.new(file: file)
|
113
|
+
attachment.author = User.current
|
114
|
+
attachment.filename = filename
|
115
|
+
|
116
|
+
issue.attachments << attachment
|
139
117
|
end
|
140
118
|
end
|
141
119
|
|
142
|
-
def
|
143
|
-
|
144
|
-
end
|
120
|
+
def reopen_issue(issue, error)
|
121
|
+
return if @request.environment_name.blank?
|
145
122
|
|
146
|
-
|
147
|
-
|
148
|
-
end
|
123
|
+
desc = "*Issue reopened after occurring again in _#{@request.environment_name}_ environment.*"
|
124
|
+
desc << "\n\n#{render_description(error)}" if @request.reopen_repeat_description?
|
149
125
|
|
150
|
-
|
151
|
-
@project.issue_custom_fields.where(id: setting(key)).first || CustomField.where(id: setting(key), is_for_all: true).first
|
152
|
-
end
|
126
|
+
issue.status = IssueStatus.where(is_default: true).order(:position).first
|
153
127
|
|
154
|
-
|
155
|
-
return false if @notice.env.blank? || @notice.env[:environment_name].blank? || project_setting(:reopen_regexp).blank?
|
156
|
-
!!(@notice.env[:environment_name] =~ /#{project_setting(:reopen_regexp)}/i)
|
157
|
-
end
|
128
|
+
issue.init_journal(User.current, desc)
|
158
129
|
|
159
|
-
|
160
|
-
Setting.plugin_redmine_airbrake_backend[key]
|
130
|
+
add_error_attachments_to_issue(issue, error)
|
161
131
|
end
|
162
132
|
|
163
|
-
def render_description
|
164
|
-
|
165
|
-
|
133
|
+
def render_description(error)
|
134
|
+
locals = { request: @request, error: error }
|
135
|
+
|
136
|
+
if template_exists?("airbrake/issue_description/#{@request.type}")
|
137
|
+
render_to_string("airbrake/issue_description/#{@request.type}", layout: false, locals: locals)
|
166
138
|
else
|
167
|
-
render_to_string(
|
139
|
+
render_to_string('airbrake/issue_description/default', layout: false, locals: locals)
|
168
140
|
end
|
169
141
|
end
|
170
142
|
|
143
|
+
def cleanup_tempfiles
|
144
|
+
return if @tempfiles.blank?
|
145
|
+
|
146
|
+
@tempfiles.each do |tempfile|
|
147
|
+
tempfile.close rescue nil
|
148
|
+
tempfile.unlink rescue nil
|
149
|
+
end
|
150
|
+
end
|
171
151
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# Controller for airbrake notices
|
2
|
+
class AirbrakeNoticeController < ::AirbrakeController
|
3
|
+
prepend_before_filter :parse_xml_request, only: [:notice_xml]
|
4
|
+
prepend_before_filter :parse_json_request, only: [:notice_json]
|
5
|
+
|
6
|
+
accept_api_auth :notice_xml, :notice_json
|
7
|
+
|
8
|
+
# Handle airbrake XML notices
|
9
|
+
def notice_xml
|
10
|
+
render xml: {
|
11
|
+
notice: {
|
12
|
+
id: (@results.first[:hash] rescue nil)
|
13
|
+
}
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
# Handle airbrake JSON notices
|
18
|
+
def notice_json
|
19
|
+
render json: {
|
20
|
+
notice: {
|
21
|
+
id: (@results.first[:hash] rescue nil)
|
22
|
+
}
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# Controller for project-specific airbrake settings
|
1
2
|
class AirbrakeProjectSettingsController < ::ApplicationController
|
2
3
|
before_filter :find_project
|
3
4
|
before_filter :find_airbrake_setting
|
@@ -7,10 +8,11 @@ class AirbrakeProjectSettingsController < ::ApplicationController
|
|
7
8
|
def update
|
8
9
|
@airbrake_project_setting.safe_attributes = params[:airbrake_project_setting]
|
9
10
|
|
10
|
-
@airbrake_project_setting.save
|
11
|
+
if @airbrake_project_setting.save
|
12
|
+
flash[:notice] = l(:notice_successful_update)
|
13
|
+
end
|
11
14
|
|
12
|
-
|
13
|
-
redirect_to settings_project_path(@project, :tab => 'airbrake')
|
15
|
+
redirect_to settings_project_path(@project, tab: 'airbrake')
|
14
16
|
end
|
15
17
|
|
16
18
|
private
|
@@ -22,5 +24,4 @@ class AirbrakeProjectSettingsController < ::ApplicationController
|
|
22
24
|
def find_airbrake_setting
|
23
25
|
@airbrake_project_setting = @project.airbrake_settings || AirbrakeProjectSetting.new(project: @project)
|
24
26
|
end
|
25
|
-
|
26
27
|
end
|