Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'lib/error_tracking/sentry_client/issue.rb')
-rw-r--r--lib/error_tracking/sentry_client/issue.rb184
1 files changed, 184 insertions, 0 deletions
diff --git a/lib/error_tracking/sentry_client/issue.rb b/lib/error_tracking/sentry_client/issue.rb
new file mode 100644
index 00000000000..513fb3daabe
--- /dev/null
+++ b/lib/error_tracking/sentry_client/issue.rb
@@ -0,0 +1,184 @@
+# frozen_string_literal: true
+
+module ErrorTracking
+ class SentryClient
+ module Issue
+ BadRequestError = Class.new(StandardError)
+ ResponseInvalidSizeError = Class.new(StandardError)
+
+ SENTRY_API_SORT_VALUE_MAP = {
+ # <accepted_by_client> => <accepted_by_sentry_api>
+ 'frequency' => 'freq',
+ 'first_seen' => 'new',
+ 'last_seen' => nil
+ }.freeze
+
+ def list_issues(**keyword_args)
+ response = get_issues(**keyword_args)
+
+ issues = response[:issues]
+ pagination = response[:pagination]
+
+ validate_size(issues)
+
+ handle_mapping_exceptions do
+ {
+ issues: map_to_errors(issues),
+ pagination: pagination
+ }
+ end
+ end
+
+ def issue_details(issue_id:)
+ issue = get_issue(issue_id: issue_id)
+
+ map_to_detailed_error(issue)
+ end
+
+ def update_issue(issue_id:, params:)
+ http_put(api_urls.issue_url(issue_id), params)[:body]
+ end
+
+ private
+
+ def get_issues(**keyword_args)
+ response = http_get(
+ api_urls.issues_url,
+ query: list_issue_sentry_query(**keyword_args)
+ )
+
+ {
+ issues: response[:body],
+ pagination: SentryClient::PaginationParser.parse(response[:headers])
+ }
+ end
+
+ def list_issue_sentry_query(issue_status:, limit:, sort: nil, search_term: '', cursor: nil)
+ unless SENTRY_API_SORT_VALUE_MAP.key?(sort)
+ raise BadRequestError, 'Invalid value for sort param'
+ end
+
+ {
+ query: "is:#{issue_status} #{search_term}".strip,
+ limit: limit,
+ sort: SENTRY_API_SORT_VALUE_MAP[sort],
+ cursor: cursor
+ }.compact
+ end
+
+ def validate_size(issues)
+ return if Gitlab::Utils::DeepSize.new(issues).valid?
+
+ raise ResponseInvalidSizeError, "Sentry API response is too big. Limit is #{Gitlab::Utils::DeepSize.human_default_max_size}."
+ end
+
+ def get_issue(issue_id:)
+ http_get(api_urls.issue_url(issue_id))[:body]
+ end
+
+ def parse_gitlab_issue(issue)
+ parse_issue_annotations(issue) || parse_plugin_issue(issue)
+ end
+
+ def parse_issue_annotations(issue)
+ issue
+ .fetch('annotations', [])
+ .reject(&:blank?)
+ .map { |annotation| Nokogiri.make(annotation) }
+ .find { |html| html['href']&.starts_with?(Gitlab.config.gitlab.url) }
+ .try(:[], 'href')
+ end
+
+ def parse_plugin_issue(issue)
+ plugin_issues = issue.fetch('pluginIssues', nil)
+ return unless plugin_issues
+
+ gitlab_plugin = plugin_issues.detect { |item| item['id'] == 'gitlab' }
+ return unless gitlab_plugin
+
+ gitlab_plugin.dig('issue', 'url')
+ end
+
+ def issue_url(id)
+ parse_sentry_url("#{url}/issues/#{id}")
+ end
+
+ def project_url
+ parse_sentry_url(url)
+ end
+
+ def parse_sentry_url(api_url)
+ url = ErrorTracking::ProjectErrorTrackingSetting.extract_sentry_external_url(api_url)
+
+ uri = URI(url)
+ uri.path.squeeze!('/')
+ # Remove trailing slash
+ uri = uri.to_s.delete_suffix('/')
+
+ uri
+ end
+
+ def map_to_errors(issues)
+ issues.map(&method(:map_to_error))
+ end
+
+ def map_to_error(issue)
+ Gitlab::ErrorTracking::Error.new(
+ id: issue.fetch('id'),
+ first_seen: issue.fetch('firstSeen', nil),
+ last_seen: issue.fetch('lastSeen', nil),
+ title: issue.fetch('title', nil),
+ type: issue.fetch('type', nil),
+ user_count: issue.fetch('userCount', nil),
+ count: issue.fetch('count', nil),
+ message: issue.dig('metadata', 'value'),
+ culprit: issue.fetch('culprit', nil),
+ external_url: issue_url(issue.fetch('id')),
+ short_id: issue.fetch('shortId', nil),
+ status: issue.fetch('status', nil),
+ frequency: issue.dig('stats', '24h'),
+ project_id: issue.dig('project', 'id'),
+ project_name: issue.dig('project', 'name'),
+ project_slug: issue.dig('project', 'slug')
+ )
+ end
+
+ def map_to_detailed_error(issue)
+ Gitlab::ErrorTracking::DetailedError.new({
+ id: issue.fetch('id'),
+ first_seen: issue.fetch('firstSeen', nil),
+ last_seen: issue.fetch('lastSeen', nil),
+ tags: extract_tags(issue),
+ title: issue.fetch('title', nil),
+ type: issue.fetch('type', nil),
+ user_count: issue.fetch('userCount', nil),
+ count: issue.fetch('count', nil),
+ message: issue.dig('metadata', 'value'),
+ culprit: issue.fetch('culprit', nil),
+ external_url: issue_url(issue.fetch('id')),
+ external_base_url: project_url,
+ short_id: issue.fetch('shortId', nil),
+ status: issue.fetch('status', nil),
+ frequency: issue.dig('stats', '24h'),
+ gitlab_issue: parse_gitlab_issue(issue),
+ project_id: issue.dig('project', 'id'),
+ project_name: issue.dig('project', 'name'),
+ project_slug: issue.dig('project', 'slug'),
+ first_release_last_commit: issue.dig('firstRelease', 'lastCommit'),
+ first_release_short_version: issue.dig('firstRelease', 'shortVersion'),
+ first_release_version: issue.dig('firstRelease', 'version'),
+ last_release_last_commit: issue.dig('lastRelease', 'lastCommit'),
+ last_release_short_version: issue.dig('lastRelease', 'shortVersion'),
+ last_release_version: issue.dig('lastRelease', 'version')
+ })
+ end
+
+ def extract_tags(issue)
+ {
+ level: issue.fetch('level', nil),
+ logger: issue.fetch('logger', nil)
+ }
+ end
+ end
+ end
+end