summaryrefslogtreecommitdiff
path: root/lib/sentry/client.rb
blob: 4187014d49e2797793431f2cc00ff143ea16af96 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# frozen_string_literal: true

module Sentry
  class Client
    Error = Class.new(StandardError)
    SentryError = Class.new(StandardError)

    attr_accessor :url, :token

    def initialize(api_url, token)
      @url = api_url
      @token = token
    end

    def list_issues(issue_status:, limit:)
      issues = get_issues(issue_status: issue_status, limit: limit)
      map_to_errors(issues)
    end

    def list_projects
      projects = get_projects
      map_to_projects(projects)
    rescue KeyError => e
      raise Client::SentryError, "Sentry API response is missing keys. #{e.message}"
    end

    private

    def request_params
      {
        headers: {
          'Authorization' => "Bearer #{@token}"
        },
        follow_redirects: false
      }
    end

    def http_get(url, params = {})
      resp = Gitlab::HTTP.get(url, **request_params.merge(params))

      handle_response(resp)
    end

    def get_issues(issue_status:, limit:)
      http_get(issues_api_url, query: {
        query: "is:#{issue_status}",
        limit: limit
      })
    end

    def get_projects
      http_get(projects_api_url)
    end

    def handle_response(response)
      unless response.code == 200
        raise Client::Error, "Sentry response error: #{response.code}"
      end

      response.as_json
    end

    def projects_api_url
      projects_url = URI(@url)
      projects_url.path = '/api/0/projects/'

      projects_url
    end

    def issues_api_url
      issues_url = URI(@url + '/issues/')
      issues_url.path.squeeze!('/')

      issues_url
    end

    def map_to_errors(issues)
      issues.map(&method(:map_to_error))
    end

    def map_to_projects(projects)
      projects.map(&method(:map_to_project))
    end

    def issue_url(id)
      issues_url = @url + "/issues/#{id}"
      issues_url = ErrorTracking::ProjectErrorTrackingSetting.extract_sentry_external_url(issues_url)

      uri = URI(issues_url)
      uri.path.squeeze!('/')

      uri.to_s
    end

    def map_to_error(issue)
      id = issue.fetch('id')
      project = issue.fetch('project')

      count = issue.fetch('count', nil)

      frequency = issue.dig('stats', '24h')
      message = issue.dig('metadata', 'value')

      external_url = issue_url(id)

      Gitlab::ErrorTracking::Error.new(
        id: 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,
        culprit: issue.fetch('culprit', nil),
        external_url: external_url,
        short_id: issue.fetch('shortId', nil),
        status: issue.fetch('status', nil),
        frequency: frequency,
        project_id: project.fetch('id'),
        project_name: project.fetch('name', nil),
        project_slug: project.fetch('slug', nil)
      )
    end

    def map_to_project(project)
      organization = project.fetch('organization')

      Gitlab::ErrorTracking::Project.new(
        id: project.fetch('id'),
        name: project.fetch('name'),
        slug: project.fetch('slug'),
        status: project.dig('status'),
        organization_name: organization.fetch('name'),
        organization_id: organization.fetch('id'),
        organization_slug: organization.fetch('slug')
      )
    end
  end
end