redmine_airbrake_backend 1.2.1 → 1.2.2

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