redmine_airbrake_backend 1.2.1 → 1.2.2

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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.gitlab-ci.yml +42 -0
  3. data/.rubocop.yml +13 -0
  4. data/CHANGELOG.md +8 -0
  5. data/Gemfile +3 -1
  6. data/README.md +0 -4
  7. data/Rakefile +2 -1
  8. data/app/controllers/airbrake_controller.rb +32 -137
  9. data/app/controllers/airbrake_notice_controller.rb +8 -6
  10. data/app/controllers/airbrake_project_settings_controller.rb +6 -4
  11. data/app/controllers/airbrake_report_controller.rb +2 -0
  12. data/app/controllers/concerns/airbrake_attachments.rb +56 -0
  13. data/app/controllers/concerns/airbrake_issue_handling.rb +109 -0
  14. data/app/controllers/concerns/airbrake_rendering.rb +16 -0
  15. data/app/helpers/airbrake_helper.rb +35 -23
  16. data/app/models/airbrake_project_setting.rb +2 -0
  17. data/bin/rails +3 -3
  18. data/config/routes.rb +2 -0
  19. data/db/migrate/20130708102357_create_airbrake_project_settings.rb +3 -0
  20. data/db/migrate/20131003151228_add_reopen_repeat_description.rb +3 -0
  21. data/lib/redmine_airbrake_backend.rb +3 -0
  22. data/lib/redmine_airbrake_backend/backtrace_element.rb +6 -4
  23. data/lib/redmine_airbrake_backend/engine.rb +27 -25
  24. data/lib/redmine_airbrake_backend/error.rb +2 -0
  25. data/lib/redmine_airbrake_backend/ios_report.rb +33 -28
  26. data/lib/redmine_airbrake_backend/notice.rb +43 -27
  27. data/lib/redmine_airbrake_backend/patches/issue_category.rb +10 -5
  28. data/lib/redmine_airbrake_backend/patches/issue_priority.rb +10 -5
  29. data/lib/redmine_airbrake_backend/patches/project.rb +10 -5
  30. data/lib/redmine_airbrake_backend/patches/projects_helper.rb +16 -11
  31. data/lib/redmine_airbrake_backend/patches/tracker.rb +10 -5
  32. data/lib/redmine_airbrake_backend/version.rb +3 -1
  33. data/lib/tasks/test.rake +11 -9
  34. data/redmine_airbrake_backend.gemspec +10 -6
  35. data/test/unit/notice_test.rb +1 -1
  36. metadata +24 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bd9ed3cb0664c977824beee01de24dabd2883bba7bbaa0df8dbe1bfdbfd6ca47
4
- data.tar.gz: 4d52088e65e445ab788d459ef88d88395716fd544e85ea97a15082198f586c11
3
+ metadata.gz: 78d324b14146a054ed7f62573df24493843ebccc846a1a8936348fff51228098
4
+ data.tar.gz: 6225ac96f31412e899d0ab2f06e869f22761d4af19583dda20cdfefc1e00d416
5
5
  SHA512:
6
- metadata.gz: 2a17ebe81cdb4aac103031e7ee8920f33a0a23ed9075a1deb0f48b288ebb0c3fd24b91580b83c62c36d805c096a240d467ac6d488b6a75e38ebc4e9db7bdf398
7
- data.tar.gz: dec97c57aab26ddd559c0680eae89ed05b810866755866b0ae8e326a6438a314423c5d31adb32e96f91d4ecd116b20473cfcd5450184819a3eac061c8b1604b0
6
+ metadata.gz: 366e346434ea2a82047ff0d1a7c6314ff2782e5e4056cdf3f2563e2ebfe0f958265d727a1e87dd47eddb001c61b3d832dd3fa20ee4e6f8eb9973e2aba3a59801
7
+ data.tar.gz: abb59d8c978611fa973c20a3de001904a1bfade5c30d910739ad2285cccc7a024a0e0951e57f4fb1da429d9fae0d2b61ebcb16c6fab24f94a97cd40fd763e18a
data/.gitlab-ci.yml ADDED
@@ -0,0 +1,42 @@
1
+ stages:
2
+ - build
3
+ - codequality
4
+ - security
5
+
6
+ build:
7
+ stage: build
8
+ image: ruby:2.5
9
+ script:
10
+ - gem install bundler --no-ri --no-rdoc
11
+ - bundle update
12
+ artifacts:
13
+ paths:
14
+ - Gemfile.lock
15
+
16
+ rubocop:
17
+ stage: codequality
18
+ image: ruby:2.5
19
+ script:
20
+ - gem install rubocop --no-ri --no-rdoc
21
+ - rubocop
22
+
23
+ dependency_scanning:
24
+ stage: security
25
+ dependencies:
26
+ - build
27
+ image: docker:stable
28
+ variables:
29
+ DOCKER_DRIVER: overlay2
30
+ allow_failure: true
31
+ services:
32
+ - docker:stable-dind
33
+ script:
34
+ - export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
35
+ - docker run
36
+ --env DEP_SCAN_DISABLE_REMOTE_CHECKS="${DEP_SCAN_DISABLE_REMOTE_CHECKS:-false}"
37
+ --volume "$PWD:/code"
38
+ --volume /var/run/docker.sock:/var/run/docker.sock
39
+ "registry.gitlab.com/gitlab-org/security-products/dependency-scanning:$SP_VERSION" /code
40
+ artifacts:
41
+ paths:
42
+ - gl-dependency-scanning-report.json
data/.rubocop.yml ADDED
@@ -0,0 +1,13 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.4
3
+ Exclude:
4
+ - 'test/**/*.rb'
5
+ - '**/*.rake'
6
+
7
+ Layout/AlignHash:
8
+ EnforcedHashRocketStyle: table
9
+ EnforcedColonStyle: table
10
+ Metrics/LineLength:
11
+ Max: 120
12
+ Metrics/MethodLength:
13
+ Max: 15
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.2.2 (2018-11-03)
4
+ ### Fixes
5
+ - Fix finding repository if no filename is for backtrace element
6
+ ### Changes
7
+ - Internal restructuring for better code style
8
+ - Requires Ruby >= 2.4.0
9
+ - Requires Redmine >= 3.4.0
10
+
3
11
  ## 1.2.1 (2018-01-29)
4
12
  ### Features
5
13
  - All responses are formatted as JSON
data/Gemfile CHANGED
@@ -1,4 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in redmine_airbrake_backend.gemspec
4
- gemspec path: File.dirname(__FILE__)
6
+ gemspec path: __dir__
data/README.md CHANGED
@@ -1,7 +1,3 @@
1
- [![Gem Version](https://img.shields.io/gem/v/redmine_airbrake_backend.svg)](https://rubygems.org/gems/redmine_airbrake_backend)
2
- [![Dependencies](https://img.shields.io/gemnasium/ydkn/redmine_airbrake_backend.svg)](https://gemnasium.com/ydkn/redmine_airbrake_backend)
3
- [![Code Climate](https://img.shields.io/codeclimate/github/ydkn/redmine_airbrake_backend.svg)](https://codeclimate.com/github/ydkn/redmine_airbrake_backend)
4
-
5
1
  # Redmine Airbrake Backend
6
2
 
7
3
  This plugin provides the necessary API to use Redmine as an Airbrake backend.
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require 'bundler/setup'
3
5
  rescue LoadError
@@ -24,5 +26,4 @@ Rake::TestTask.new(:test) do |t|
24
26
  t.verbose = false
25
27
  end
26
28
 
27
-
28
29
  task default: :test
@@ -1,13 +1,18 @@
1
- require 'tempfile'
1
+ # frozen_string_literal: true
2
+
2
3
  require 'redmine_airbrake_backend/notice'
3
4
 
4
5
  # Controller with airbrake related stuff
5
6
  class AirbrakeController < ::ApplicationController
7
+ include AirbrakeIssueHandling
8
+ include AirbrakeRendering
9
+
6
10
  skip_before_action :verify_authenticity_token
7
- skip_before_action :session_expiration, :user_setup, :check_if_login_required, :set_localization, :check_password_change
11
+ skip_before_action :session_expiration, :user_setup, :check_if_login_required, :set_localization,
12
+ :check_password_change
8
13
 
9
14
  prepend_before_action :find_project
10
- prepend_before_action :parse_key
15
+ prepend_before_action :check_key, :parse_key
11
16
 
12
17
  before_action :authorize
13
18
  before_action :find_tracker
@@ -16,8 +21,6 @@ class AirbrakeController < ::ApplicationController
16
21
  before_action :find_assignee
17
22
  before_action :find_repository
18
23
 
19
- after_action :cleanup_tempfiles
20
-
21
24
  private
22
25
 
23
26
  def find_project
@@ -25,16 +28,18 @@ class AirbrakeController < ::ApplicationController
25
28
  end
26
29
 
27
30
  def parse_key
31
+ raw_key = if (request.headers['HTTP_AUTHORIZATION'] || '') =~ /^Bearer\s+(.*)\s*$/i # New auth method
32
+ Regexp.last_match[1]
33
+ elsif params[:key].present? # Old auth method
34
+ params[:key]
35
+ end
36
+
37
+ @key = JSON.parse(raw_key).with_indifferent_access
38
+ rescue StandardError
28
39
  @key = nil
40
+ end
29
41
 
30
- if (request.headers['HTTP_AUTHORIZATION'] || '') =~ /^Bearer\s+(.*)\s*$/i
31
- # New auth method
32
- @key = JSON.parse(Regexp.last_match[1]).with_indifferent_access rescue nil
33
- elsif params[:key].present?
34
- # Old auth method
35
- @key = JSON.parse(params[:key]).with_indifferent_access rescue nil
36
- end
37
-
42
+ def check_key
38
43
  render_error('No or invalid API key format', :unauthorized) if @key.blank? || @key[:key].blank?
39
44
  end
40
45
 
@@ -58,7 +63,9 @@ class AirbrakeController < ::ApplicationController
58
63
  render_error('No or invalid tracker', :failed_dependency) if @tracker.blank?
59
64
 
60
65
  # Check notice ID field
61
- render_error('Custom field for notice hash not available on selected tracker', :failed_dependency) if @tracker.custom_fields.find_by(id: notice_hash_field.id).blank?
66
+ return unless @tracker.custom_fields.find_by(id: notice_hash_field.id).blank?
67
+
68
+ render_error('Custom field for notice hash not available on selected tracker', :failed_dependency)
62
69
  end
63
70
 
64
71
  def find_category
@@ -70,7 +77,7 @@ class AirbrakeController < ::ApplicationController
70
77
  end
71
78
 
72
79
  def find_assignee
73
- @assignee = record_for(@project.users, :assignee, [:id, :login])
80
+ @assignee = record_for(@project.users, :assignee, %i[id login])
74
81
  end
75
82
 
76
83
  def find_repository
@@ -85,8 +92,14 @@ class AirbrakeController < ::ApplicationController
85
92
 
86
93
  def render_airbrake_response
87
94
  if @issue.present?
95
+ airbrake_id_value = CustomValue.find_by(
96
+ customized_type: Issue.name,
97
+ customized_id: @issue.id,
98
+ custom_field_id: notice_hash_field.id
99
+ )
100
+
88
101
  render json: {
89
- id: (CustomValue.find_by(customized_type: Issue.name, customized_id: @issue.id, custom_field_id: notice_hash_field.id).value rescue nil),
102
+ id: airbrake_id_value&.value,
90
103
  url: issue_url(@issue)
91
104
  }, status: :created
92
105
  else
@@ -94,7 +107,7 @@ class AirbrakeController < ::ApplicationController
94
107
  end
95
108
  end
96
109
 
97
- def record_for(on, key, fields = [:id, :name])
110
+ def record_for(on, key, fields = %i[id name])
98
111
  fields.each do |field|
99
112
  val = on.find_by(field => @key[key])
100
113
  return val if val.present?
@@ -114,7 +127,8 @@ class AirbrakeController < ::ApplicationController
114
127
  end
115
128
 
116
129
  def custom_field(key)
117
- @project.issue_custom_fields.find_by(id: global_setting(key)) || CustomField.find_by(id: global_setting(key), is_for_all: true)
130
+ @project.issue_custom_fields.find_by(id: global_setting(key)) ||
131
+ CustomField.find_by(id: global_setting(key), is_for_all: true)
118
132
  end
119
133
 
120
134
  def notice_hash_field
@@ -124,123 +138,4 @@ class AirbrakeController < ::ApplicationController
124
138
  def occurrences_field
125
139
  custom_field(:occurrences_field)
126
140
  end
127
-
128
- def create_issue!
129
- return if @notice.blank?
130
- return if @notice.errors.blank?
131
-
132
- @issue = find_or_initialize_issue(@notice)
133
-
134
- set_issue_custom_field_values(@issue, @notice)
135
-
136
- reopen_issue(@issue, @notice) if @issue.persisted? && @issue.status.is_closed? && reopen_issue?(@notice)
137
-
138
- @issue = nil unless @issue.save
139
- end
140
-
141
- # Load or initialize issue by project, tracker and airbrake hash
142
- def find_or_initialize_issue(notice)
143
- issue_ids = CustomValue.where(customized_type: Issue.name, custom_field_id: notice_hash_field.id, value: notice.id).pluck(:customized_id)
144
-
145
- issue = Issue.find_by(id: issue_ids, project_id: @project.id, tracker_id: @tracker.id)
146
-
147
- return issue if issue.present?
148
-
149
- initialize_issue(notice)
150
- end
151
-
152
- def initialize_issue(notice)
153
- issue = Issue.new(
154
- subject: notice.subject,
155
- project: @project,
156
- tracker: @tracker,
157
- author: User.current,
158
- category: @category,
159
- priority: @priority,
160
- description: render_description(notice),
161
- assigned_to: @assignee
162
- )
163
-
164
- add_attachments_to_issue(issue, notice)
165
-
166
- issue
167
- end
168
-
169
- def set_issue_custom_field_values(issue, notice)
170
- custom_field_values = {}
171
-
172
- # Error ID
173
- custom_field_values[notice_hash_field.id] = notice.id if issue.new_record?
174
-
175
- # Update occurrences
176
- if occurrences_field.present?
177
- occurrences_value = issue.custom_value_for(occurrences_field.id)
178
- custom_field_values[occurrences_field.id] = ((occurrences_value ? occurrences_value.value.to_i : 0) + 1).to_s
179
- end
180
-
181
- issue.custom_field_values = custom_field_values
182
- end
183
-
184
- def add_attachments_to_issue(issue, notice)
185
- return if notice.attachments.blank?
186
-
187
- @tempfiles ||= []
188
-
189
- notice.attachments.each do |data|
190
- filename = data[:filename].presence || Redmine::Utils.random_hex(16)
191
-
192
- file = Tempfile.new(filename)
193
- @tempfiles << file
194
-
195
- file.write(data[:data])
196
- file.rewind
197
-
198
- issue.attachments << Attachment.new(file: file, author: User.current, filename: filename)
199
- end
200
- end
201
-
202
- def reopen_issue?(notice)
203
- reopen_regexp = project_setting(:reopen_regexp)
204
-
205
- return false if reopen_regexp.blank?
206
- return false if notice.environment_name.blank?
207
-
208
- !!(notice.environment_name =~ /#{reopen_regexp}/i)
209
- end
210
-
211
- def issue_reopen_repeat_description?
212
- !!project_setting(:reopen_repeat_description)
213
- end
214
-
215
- def reopen_issue(issue, notice)
216
- return if notice.environment_name.blank?
217
-
218
- desc = "*Issue reopened after occurring again in _#{notice.environment_name}_ environment.*"
219
- desc << "\n\n#{render_description(notice)}" if issue_reopen_repeat_description?
220
-
221
- issue.status = issue.tracker.default_status
222
-
223
- issue.init_journal(User.current, desc)
224
-
225
- add_attachments_to_issue(issue, notice)
226
- end
227
-
228
- def render_description(notice)
229
- locals = { notice: notice }
230
-
231
- if template_exists?("airbrake/issue_description/#{notice.type}")
232
- render_to_string("airbrake/issue_description/#{notice.type}", layout: false, locals: locals)
233
- else
234
- render_to_string('airbrake/issue_description/default', layout: false, locals: locals)
235
- end
236
- end
237
-
238
- def cleanup_tempfiles
239
- return if @tempfiles.blank?
240
-
241
- @tempfiles.each do |tempfile|
242
- tempfile.close rescue nil
243
- tempfile.unlink rescue nil
244
- end
245
- end
246
141
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Controller for airbrake notices
2
4
  class AirbrakeNoticeController < ::AirbrakeController
3
5
  accept_api_auth :notices
@@ -15,11 +17,11 @@ class AirbrakeNoticeController < ::AirbrakeController
15
17
 
16
18
  def parse_notice
17
19
  @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
- )
20
+ errors: params[:errors].map { |e| RedmineAirbrakeBackend::Error.new(e) },
21
+ params: params[:params],
22
+ session: params[:session],
23
+ context: params[:context],
24
+ environment: params[:environment]
25
+ )
24
26
  end
25
27
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Controller for project-specific airbrake settings
2
4
  class AirbrakeProjectSettingsController < ::ApplicationController
3
5
  before_filter :find_project
@@ -6,9 +8,7 @@ class AirbrakeProjectSettingsController < ::ApplicationController
6
8
  menu_item :settings
7
9
 
8
10
  def update
9
- if @airbrake_project_setting.update(airbrake_project_setting_params)
10
- flash[:notice] = l(:notice_successful_update)
11
- end
11
+ flash[:notice] = l(:notice_successful_update) if @airbrake_project_setting.update(airbrake_project_setting_params)
12
12
 
13
13
  redirect_to settings_project_path(@project, tab: 'airbrake')
14
14
  end
@@ -25,6 +25,8 @@ class AirbrakeProjectSettingsController < ::ApplicationController
25
25
  end
26
26
 
27
27
  def airbrake_project_setting_params
28
- params.require(:airbrake_project_setting).permit(:tracker_id, :category_id, :priority_id, :reopen_regexp, :reopen_repeat_description)
28
+ params
29
+ .require(:airbrake_project_setting)
30
+ .permit(:tracker_id, :category_id, :priority_id, :reopen_regexp, :reopen_repeat_description)
29
31
  end
30
32
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'redmine_airbrake_backend/ios_report'
2
4
 
3
5
  # Controller for airbrake reports
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tempfile'
4
+
5
+ # Handling for creating / updating issues
6
+ module AirbrakeAttachments
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ after_action :cleanup_tempfiles
11
+ end
12
+
13
+ private
14
+
15
+ def add_attachment_to_issue(issue, data)
16
+ filename = data[:filename].presence || Redmine::Utils.random_hex(16)
17
+
18
+ file = Tempfile.new(filename)
19
+
20
+ @tempfiles << file
21
+
22
+ file.write(data[:data])
23
+ file.rewind
24
+
25
+ issue.attachments << Attachment.new(file: file, author: User.current, filename: filename)
26
+ end
27
+
28
+ def add_attachments_to_issue(issue, notice)
29
+ return if notice.attachments.blank?
30
+
31
+ @tempfiles ||= []
32
+
33
+ notice.attachments.each do |data|
34
+ add_attachment_to_issue(issue, data)
35
+ end
36
+ end
37
+
38
+ def close_tempfile(tempfile)
39
+ tempfile.close
40
+ rescue StandardError
41
+ nil
42
+ end
43
+
44
+ def unlink_tempfile(tempfile)
45
+ tempfile.unlink
46
+ rescue StandardError
47
+ nil
48
+ end
49
+
50
+ def cleanup_tempfiles
51
+ Array(@tempfiles).compact.each do |tempfile|
52
+ close_tempfile(tempfile)
53
+ unlink_tempfile(tempfile)
54
+ end
55
+ end
56
+ end