sentry-summary 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []