diff options
Diffstat (limited to 'lib/sentry/client.rb')
-rw-r--r-- | lib/sentry/client.rb | 122 |
1 files changed, 107 insertions, 15 deletions
diff --git a/lib/sentry/client.rb b/lib/sentry/client.rb index 07cca1c8d1e..6191d69c870 100644 --- a/lib/sentry/client.rb +++ b/lib/sentry/client.rb @@ -4,6 +4,7 @@ module Sentry class Client Error = Class.new(StandardError) MissingKeysError = Class.new(StandardError) + ResponseInvalidSizeError = Class.new(StandardError) attr_accessor :url, :token @@ -12,9 +13,23 @@ module Sentry @token = token end + def issue_details(issue_id:) + issue = get_issue(issue_id: issue_id) + + map_to_detailed_error(issue) + end + + def issue_latest_event(issue_id:) + latest_event = get_issue_latest_event(issue_id: issue_id) + + map_to_event(latest_event) + end + def list_issues(issue_status:, limit:) issues = get_issues(issue_status: issue_status, limit: limit) + validate_size(issues) + handle_mapping_exceptions do map_to_errors(issues) end @@ -30,6 +45,12 @@ module Sentry private + def validate_size(issues) + return if Gitlab::Utils::DeepSize.new(issues).valid? + + raise Client::ResponseInvalidSizeError, "Sentry API response is too big. Limit is #{Gitlab::Utils::DeepSize.human_default_max_size}." + end + def handle_mapping_exceptions(&block) yield rescue KeyError => e @@ -61,6 +82,14 @@ module Sentry }) end + def get_issue(issue_id:) + http_get(issue_api_url(issue_id)) + end + + def get_issue_latest_event(issue_id:) + http_get(issue_latest_event_api_url(issue_id)) + end + def get_projects http_get(projects_api_url) end @@ -88,7 +117,7 @@ module Sentry raise_error "Sentry response status code: #{response.code}" end - response + response.parsed_response end def raise_error(message) @@ -102,6 +131,20 @@ module Sentry projects_url end + def issue_api_url(issue_id) + issue_url = URI(@url) + issue_url.path = "/api/0/issues/#{issue_id}/" + + issue_url + end + + def issue_latest_event_api_url(issue_id) + latest_event_url = URI(@url) + latest_event_url.path = "/api/0/issues/#{issue_id}/events/latest/" + + latest_event_url + end + def issues_api_url issues_url = URI(@url + '/issues/') issues_url.path.squeeze!('/') @@ -119,38 +162,87 @@ module Sentry def issue_url(id) issues_url = @url + "/issues/#{id}" - issues_url = ErrorTracking::ProjectErrorTrackingSetting.extract_sentry_external_url(issues_url) - uri = URI(issues_url) + parse_sentry_url(issues_url) + 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 spaces + uri = uri.to_s.gsub(/\/\z/, '') - uri.to_s + uri end - def map_to_error(issue) - id = issue.fetch('id') + def map_to_event(event) + stack_trace = parse_stack_trace(event) - count = issue.fetch('count', nil) + Gitlab::ErrorTracking::ErrorEvent.new( + issue_id: event.dig('groupID'), + date_received: event.dig('dateReceived'), + stack_trace_entries: stack_trace + ) + end - frequency = issue.dig('stats', '24h') - message = issue.dig('metadata', 'value') + def parse_stack_trace(event) + exception_entry = event.dig('entries')&.detect { |h| h['type'] == 'exception' } + return unless exception_entry - external_url = issue_url(id) + exception_values = exception_entry.dig('data', 'values') + stack_trace_entry = exception_values&.detect { |h| h['stacktrace'].present? } + return unless stack_trace_entry + + stack_trace_entry.dig('stacktrace', 'frames') + 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), + 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'), + 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'), + last_release_last_commit: issue.dig('lastRelease', 'lastCommit'), + first_release_short_version: issue.dig('firstRelease', 'shortVersion'), + last_release_short_version: issue.dig('lastRelease', 'shortVersion') + ) + end + def map_to_error(issue) Gitlab::ErrorTracking::Error.new( - id: id, + 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: count, - message: message, + count: issue.fetch('count', nil), + message: issue.dig('metadata', 'value'), culprit: issue.fetch('culprit', nil), - external_url: external_url, + external_url: issue_url(issue.fetch('id')), short_id: issue.fetch('shortId', nil), status: issue.fetch('status', nil), - frequency: frequency, + frequency: issue.dig('stats', '24h'), project_id: issue.dig('project', 'id'), project_name: issue.dig('project', 'name'), project_slug: issue.dig('project', 'slug') |