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 +7 -0
- data/README.md +0 -0
- data/lib/sentry-summary.rb +2 -0
- data/lib/sentry-summary/aggregator.rb +58 -0
- data/lib/sentry-summary/sentry.rb +152 -0
- data/spec/sentry-summary/aggregator_spec.rb +24 -0
- data/spec/sentry-summary/sentry_spec.rb +39 -0
- data/spec/spec_helper.rb +1 -0
- metadata +122 -0
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,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
|
data/spec/spec_helper.rb
ADDED
@@ -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: []
|