sentry-summary 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 541e367b83259e8a6bcbb7945fdb3a3272cb1fe8
4
+ data.tar.gz: 0748d500ec67a8c04197a85395aeaeb523054e1d
5
+ SHA512:
6
+ metadata.gz: 16b84601861e486ce9b3e9ab0fb0ef4573f7415ad727e3ee2f8ab3a356a09bc665075358c2db266fe5d9f3c5a280da76715a42bbb06726c822ec20526ab10360
7
+ data.tar.gz: 85709d2737991736995ebbd35acb5e1a4b0415d2116304b8ec32480e0cd581902fad968a12a32d3a30f39703f4a14d084854d741982a60213c3ea4f6d29dcdcd
data/README.md ADDED
File without changes
@@ -0,0 +1,2 @@
1
+ current_dir = File.dirname(__FILE__)
2
+ Dir["#{current_dir}/**/*.rb"].each { |file| require_relative file }
@@ -0,0 +1,58 @@
1
+ require "similar_text"
2
+
3
+ module SentrySummary
4
+ class Aggregator
5
+ def initialize(client)
6
+ @client = client
7
+ end
8
+
9
+ def events_by_route(since = "24 hours ago")
10
+ issues = client.issues("api", since)
11
+
12
+ issues.each_with_object({}) do |issue, hash|
13
+ events = client.events(issue.id)
14
+
15
+ events.each do |event|
16
+ hash[event.route] ||= {}
17
+
18
+ closest_title = closest_title(hash[event.route].keys, event.metadata[:value])
19
+ hash[event.route][closest_title] = (hash[event.route][closest_title] || 0) + 1
20
+ end
21
+
22
+ hash
23
+ end
24
+ end
25
+
26
+ def events_by_type(since = "24 hours ago")
27
+ issues = client.issues("api", since)
28
+
29
+ issues.each_with_object({}) do |issue, hash|
30
+ events = client.events(issue.id)
31
+
32
+ events.each do |event|
33
+ type = event.metadata[:value]
34
+ type = closest_title(hash.keys, type)
35
+ hash[type] ||= {}
36
+
37
+ hash[type][event.route] = (hash[type][event.route] || 0) + 1
38
+ end
39
+
40
+ hash
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def closest_title(titles, candidate)
47
+ closest_title = titles.max_by { |title| title.similar(candidate) }
48
+
49
+ return candidate unless closest_title && closest_title.similar(candidate) > 80
50
+
51
+ closest_title
52
+ end
53
+
54
+ def client
55
+ @client
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,152 @@
1
+ require "nestful"
2
+ require "chronic"
3
+
4
+ module SentrySummary
5
+ class Sentry
6
+ attr_writer :token, :organization
7
+
8
+ def initialize(&block)
9
+ block.call(self)
10
+ end
11
+
12
+ def issues(project, since = nil)
13
+ paginate("projects/#{@organization}/#{project}/issues/", since) do |issue|
14
+ Issue.build(issue)
15
+ end
16
+ end
17
+
18
+ def latest_samples(project)
19
+ issues(project).map do |issue|
20
+ dto = request(:get, "issues/#{issue.id}/events/latest/").merge(issue_id: issue.id)
21
+ Event.build(dto)
22
+ end
23
+ end
24
+
25
+ def events(issue, since = nil)
26
+ paginate("/issues/#{issue}/events/", since) do |event|
27
+ Event.build(event.merge(issue_id: issue))
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def request(method, path, parameters = {})
34
+ response = Nestful::Request.new("#{base_url}/#{path}",
35
+ method: method,
36
+ auth_type: :bearer,
37
+ password: @token,
38
+ params: parameters
39
+ ).execute
40
+
41
+ links = response.headers["Link"].split(",").map do |link|
42
+ Link.build(link)
43
+ end
44
+
45
+ next_link = links.find { |link| link.rel == :next && link.results? }
46
+
47
+ Response.new(JSON.parse(response.body, symbolize_names: true), next_link)
48
+ end
49
+
50
+ def base_url
51
+ "https://app.getsentry.com/api/0"
52
+ end
53
+
54
+ def paginate(path, since, &block)
55
+ since ||= "24 hours ago"
56
+ since = Chronic.parse(since)
57
+
58
+ items = []
59
+ cursor = nil
60
+
61
+ begin
62
+ response = request(:get, path, cursor: cursor)
63
+ new_items = response.body.map(&block)
64
+
65
+ items_in_time_range = new_items.select { |item| item.date >= since }
66
+ items.concat(items_in_time_range)
67
+
68
+ cursor = response.cursor
69
+ end while response.next? && new_items.count == items_in_time_range.count
70
+
71
+ items
72
+ end
73
+ end
74
+
75
+ class Response
76
+ attr_reader :body
77
+
78
+ def initialize(body, next_link)
79
+ @body = body
80
+ @next_link = next_link
81
+ end
82
+
83
+ def cursor
84
+ @next_link && @next_link.cursor
85
+ end
86
+
87
+ def next?
88
+ !cursor.nil?
89
+ end
90
+ end
91
+
92
+ class Link
93
+ attr_reader :rel, :cursor
94
+
95
+ def initialize(rel, cursor, results)
96
+ @rel = rel.to_sym
97
+ @cursor = cursor
98
+ @results = results
99
+ end
100
+
101
+ def results?
102
+ @results == "true"
103
+ end
104
+
105
+ def self.build(link)
106
+ match = link.strip.match(/^<[^>]+>\s*((?:;\s*(?:[^;]+))*)$/)
107
+
108
+ parameters = match[1]
109
+
110
+ parameters = parameters.scan(/;\s*([^;]+)/).map(&:first)
111
+
112
+ parameters = parameters.map do |parameter|
113
+ parameter.scan(/^([^=]+)="([^"]+)"$/).first
114
+ end.to_h
115
+
116
+ Link.new(parameters["rel"], parameters["cursor"], parameters["results"])
117
+ end
118
+ end
119
+
120
+ class Issue
121
+ attr_reader :id, :title, :date, :metadata, :count
122
+
123
+ def initialize(id, title, date, metadata, count)
124
+ @id = id
125
+ @title = title
126
+ @date = DateTime.parse(date).to_time
127
+ @metadata = metadata
128
+ @count = count
129
+ end
130
+
131
+ def self.build(dto)
132
+ Issue.new(dto[:id], dto[:title], dto[:lastSeen], dto[:metadata], dto[:count].to_i)
133
+ end
134
+ end
135
+
136
+ class Event
137
+ attr_reader :id, :issue_id, :date, :route, :metadata
138
+
139
+ def initialize(id, issue_id, date, route, metadata)
140
+ @id = id
141
+ @issue_id = issue_id
142
+ @date = DateTime.parse(date).to_time
143
+ @route = route
144
+ @metadata = metadata
145
+ end
146
+
147
+ def self.build(dto)
148
+ Event.new(dto[:id], dto[:issue_id], dto[:dateCreated], dto[:context][:Route], dto[:metadata])
149
+ end
150
+ end
151
+
152
+ end
@@ -0,0 +1,24 @@
1
+ require "spec_helper"
2
+ require "pp"
3
+ require "similar_text"
4
+
5
+ describe SentrySummary::Aggregator do
6
+ let(:client) do
7
+ SentrySummary::Sentry.new do |sentry|
8
+ sentry.token = "d2c9a2993de64aba98051152e4442ed97106eb9a8594449786a0b88b9a9e64e4"
9
+ sentry.organization = "chicisimo"
10
+ end
11
+ end
12
+
13
+ it "aggregates the events by route" do
14
+ aggregator = SentrySummary::Aggregator.new(client)
15
+
16
+ pp aggregator.events_by_route
17
+ end
18
+
19
+ it "aggregates the events by route" do
20
+ aggregator = SentrySummary::Aggregator.new(client)
21
+
22
+ pp aggregator.events_by_type
23
+ end
24
+ end
@@ -0,0 +1,39 @@
1
+ require "spec_helper"
2
+ require "pp"
3
+ require "similar_text"
4
+
5
+ describe SentrySummary::Sentry do
6
+ let(:client) do
7
+ SentrySummary::Sentry.new do |sentry|
8
+ sentry.token = "d2c9a2993de64aba98051152e4442ed97106eb9a8594449786a0b88b9a9e64e4"
9
+ sentry.organization = "chicisimo"
10
+ end
11
+ end
12
+
13
+ xit "returns the issues for the given project" do
14
+ issues = client.issues("api")
15
+
16
+ expect(issues).to be_an(Array)
17
+ expect(issues.first).to be_an(SentrySummary::Issue)
18
+ end
19
+
20
+ it "returns the issues last seen since the given relative time" do
21
+ issues = client.issues("api", "10 min ago")
22
+ end
23
+
24
+ xit "returns the latest sample for each issue" do
25
+ samples = client.latest_samples("api")
26
+
27
+ expect(samples).to be_an(Array)
28
+ expect(samples.first).to be_an(SentrySummary::Event)
29
+ expect(samples.group_by(&:issue_id).count).to eq(samples.count)
30
+ end
31
+
32
+ it "returns the latest events for the given issue" do
33
+ issue = client.issues("api").max_by { |issue| issue.count }
34
+ events = client.events(issue.id)
35
+
36
+ expect(events).to be_an(Array)
37
+ expect(events.first).to be_an(SentrySummary::Event)
38
+ end
39
+ end
@@ -0,0 +1 @@
1
+ require "sentry-summary"
metadata ADDED
@@ -0,0 +1,122 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sentry-summary
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Juan Guerrero
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-09-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: nestful
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: json
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: similar_text
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: chronic
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Shows a summary of the sentry issues, aggregating by request
84
+ email:
85
+ - juan@chicisimo.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files:
89
+ - README.md
90
+ files:
91
+ - README.md
92
+ - lib/sentry-summary.rb
93
+ - lib/sentry-summary/aggregator.rb
94
+ - lib/sentry-summary/sentry.rb
95
+ - spec/sentry-summary/aggregator_spec.rb
96
+ - spec/sentry-summary/sentry_spec.rb
97
+ - spec/spec_helper.rb
98
+ homepage: http://github.com/juanxo/sentry-summary
99
+ licenses:
100
+ - MIT
101
+ metadata: {}
102
+ post_install_message:
103
+ rdoc_options: []
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: 2.1.0
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ requirements: []
117
+ rubyforge_project:
118
+ rubygems_version: 2.5.1
119
+ signing_key:
120
+ specification_version: 4
121
+ summary: Shows a summary of the sentry issues
122
+ test_files: []