airbrake-api 3.2.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.
- data/.gitignore +6 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/Gemfile +7 -0
- data/README.md +80 -0
- data/Rakefile +19 -0
- data/airbrake-api.gemspec +36 -0
- data/lib/airbrake-api.rb +30 -0
- data/lib/airbrake-api/client.rb +41 -0
- data/lib/airbrake-api/core_extensions.rb +5 -0
- data/lib/airbrake-api/error.rb +59 -0
- data/lib/airbrake-api/notice.rb +66 -0
- data/lib/airbrake-api/project.rb +19 -0
- data/lib/airbrake-api/version.rb +3 -0
- data/lib/airbrake_api.rb +1 -0
- data/spec/airbrake_api/error_spec.rb +53 -0
- data/spec/airbrake_api/notice_spec.rb +42 -0
- data/spec/airbrake_api/project_spec.rb +21 -0
- data/spec/airbrake_api_spec.rb +59 -0
- data/spec/fixtures/broken_notice.xml +288 -0
- data/spec/fixtures/errors.xml +545 -0
- data/spec/fixtures/individual_error.xml +207 -0
- data/spec/fixtures/individual_notice.xml +198 -0
- data/spec/fixtures/notices.xml +245 -0
- data/spec/fixtures/paginated_errors.xml +41 -0
- data/spec/fixtures/paginated_notices.xml +101 -0
- data/spec/fixtures/projects.xml +25 -0
- data/spec/fixtures/update_error.xml +207 -0
- data/spec/spec_helper.rb +50 -0
- metadata +220 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
Airbrake API [](http://travis-ci.org/spagalloco/airbrake-api)
|
2
|
+
======================================================================================================================================
|
3
|
+
|
4
|
+
A ruby wrapper for the [Airbrake API](http://airbrakeapp.com/pages/api)
|
5
|
+
|
6
|
+
Usage
|
7
|
+
-----
|
8
|
+
|
9
|
+
The first thing you need to set is the account name. This is the same as the web address for your account.
|
10
|
+
|
11
|
+
AirbrakeAPI.account = 'myaccount'
|
12
|
+
|
13
|
+
Then, you should set the authentication token.
|
14
|
+
|
15
|
+
AirbrakeAPI.auth_token = 'abcdefg'
|
16
|
+
|
17
|
+
If your account uses ssl then turn it on:
|
18
|
+
|
19
|
+
AirbrakeAPI.secure = true
|
20
|
+
|
21
|
+
Optionally, you can configure through a single method:
|
22
|
+
|
23
|
+
AirbrakeAPI.configure(:account => 'anapp', :auth_token => 'abcdefg', :secure => true)
|
24
|
+
|
25
|
+
Once you've configured authentication, you can make calls against the API. If no token or authentication is given, an AirbrakeError exception will be raised.
|
26
|
+
|
27
|
+
Finding Errors
|
28
|
+
--------------
|
29
|
+
|
30
|
+
Errors are paginated, the API responds with 25 at a time, pass an optional params hash for additional pages:
|
31
|
+
|
32
|
+
AirbrakeAPI::Error.find(:all)
|
33
|
+
AirbrakeAPI::Error.find(:all, :page => 2)
|
34
|
+
|
35
|
+
To find an individual error, you can find by ID:
|
36
|
+
|
37
|
+
AirbrakeAPI::Error.find(error_id)
|
38
|
+
|
39
|
+
Find *all* notices of an error:
|
40
|
+
|
41
|
+
AirbrakeAPI::Notice.find_all_by_error_id(error_id)
|
42
|
+
|
43
|
+
Find an individual notice:
|
44
|
+
|
45
|
+
AirbrakeAPI::Notice.find(notice_id, error_id)
|
46
|
+
|
47
|
+
To resolve an error via the API:
|
48
|
+
|
49
|
+
AirbrakeAPI::Error.update(1696170, :group => { :resolved => true})
|
50
|
+
|
51
|
+
Recreate an error:
|
52
|
+
|
53
|
+
STDOUT.sync = true
|
54
|
+
AirbrakeAPI::Notice.find_all_by_error_id(error_id) do |batch|
|
55
|
+
batch.each do |notice|
|
56
|
+
result = system "curl --silent '#{notice.request.url}' > /dev/null"
|
57
|
+
print (result ? '.' : 'F')
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
Projects
|
62
|
+
--------
|
63
|
+
|
64
|
+
To retrieve a list of projects:
|
65
|
+
|
66
|
+
AirbrakeAPI::Project.find(:all)
|
67
|
+
|
68
|
+
Responses
|
69
|
+
---------
|
70
|
+
|
71
|
+
If an error is returned from the API, an AirbrakeError will be raised. Successful responses will return a Hashie::Mash object based on the data from the response.
|
72
|
+
|
73
|
+
|
74
|
+
Contributors
|
75
|
+
------------
|
76
|
+
|
77
|
+
* [Matias Käkelä](https://github.com/massive) - SSL Support
|
78
|
+
* [Jordan Brough](https://github.com/jordan-brough) - Notices
|
79
|
+
* [Michael Grosser](https://github.com/grosser) - Numerous performance improvements and bug fixes
|
80
|
+
* [Brad Greenlee](https://github.com/bgreenlee) - Switch from Hoptoad to Airbrake
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
RSpec::Core::RakeTask.new(:spec)
|
6
|
+
|
7
|
+
task :default => :spec
|
8
|
+
|
9
|
+
namespace :doc do
|
10
|
+
require 'yard'
|
11
|
+
YARD::Rake::YardocTask.new do |task|
|
12
|
+
task.files = ['lib/**/*.rb']
|
13
|
+
task.options = [
|
14
|
+
'--protected',
|
15
|
+
'--output-dir', 'doc/yard',
|
16
|
+
'--markup', 'markdown',
|
17
|
+
]
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "airbrake-api/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'airbrake-api'
|
7
|
+
s.version = AirbrakeAPI::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
|
10
|
+
s.summary = "A ruby wrapper for the Airbrake API"
|
11
|
+
s.description = "A ruby wrapper for the Airbrake API"
|
12
|
+
|
13
|
+
s.authors = ['Steve Agalloco']
|
14
|
+
s.email = ['steve.agalloco@gmail.com']
|
15
|
+
s.homepage = 'https://github.com/spagalloco/airbrake-api'
|
16
|
+
|
17
|
+
s.add_dependency 'httparty', '~> 0.8.0'
|
18
|
+
s.add_dependency 'hashie', '~> 1.1.0'
|
19
|
+
s.add_dependency 'parallel', '~> 0.5.0'
|
20
|
+
|
21
|
+
s.add_development_dependency 'rake', '~> 0.9.2'
|
22
|
+
s.add_development_dependency 'rspec', '~> 2.6.0'
|
23
|
+
s.add_development_dependency 'yard', '~> 0.7.2'
|
24
|
+
s.add_development_dependency 'maruku', '~> 0.6'
|
25
|
+
s.add_development_dependency 'simplecov', '~> 0.4.2'
|
26
|
+
s.add_development_dependency 'fakeweb', '~> 1.3.0'
|
27
|
+
s.add_development_dependency 'nokogiri', '~> 1.4'
|
28
|
+
s.add_development_dependency 'airbrake', '~> 3.0'
|
29
|
+
s.add_development_dependency 'i18n', '~> 0.6.0'
|
30
|
+
|
31
|
+
# ensure the gem is built out of versioned files
|
32
|
+
s.files = `git ls-files`.split("\n")
|
33
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
34
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
35
|
+
s.require_paths = ["lib"]
|
36
|
+
end
|
data/lib/airbrake-api.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
require 'httparty'
|
3
|
+
|
4
|
+
module AirbrakeAPI
|
5
|
+
extend self
|
6
|
+
attr_accessor :account, :auth_token, :secure
|
7
|
+
|
8
|
+
class AirbrakeError < StandardError; end
|
9
|
+
|
10
|
+
def configure(options={})
|
11
|
+
@account = options[:account] if options.has_key?(:account)
|
12
|
+
@auth_token = options[:auth_token] if options.has_key?(:auth_token)
|
13
|
+
@secure = options[:secure] if options.has_key?(:secure)
|
14
|
+
end
|
15
|
+
|
16
|
+
def account_path
|
17
|
+
"#{protocol}://#{@account}.airbrakeapp.com"
|
18
|
+
end
|
19
|
+
|
20
|
+
def protocol
|
21
|
+
secure ? "https" : "http"
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
require 'airbrake-api/core_extensions'
|
27
|
+
require 'airbrake-api/client'
|
28
|
+
require 'airbrake-api/error'
|
29
|
+
require 'airbrake-api/notice'
|
30
|
+
require 'airbrake-api/project'
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module AirbrakeAPI
|
2
|
+
class Base
|
3
|
+
include HTTParty
|
4
|
+
format :xml
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def self.setup
|
9
|
+
base_uri AirbrakeAPI.account_path
|
10
|
+
default_params :auth_token => AirbrakeAPI.auth_token
|
11
|
+
|
12
|
+
check_configuration
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.check_configuration
|
16
|
+
raise AirbrakeError.new('API Token cannot be nil') if default_options.nil? || default_options[:default_params].nil? || !default_options[:default_params].has_key?(:auth_token)
|
17
|
+
raise AirbrakeError.new('Account cannot be nil') unless default_options.has_key?(:base_uri)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.fetch(path, options)
|
21
|
+
response = get(path, { :query => options })
|
22
|
+
if response.code == 403
|
23
|
+
raise AirbrakeError.new('SSL should be enabled - use AirbrakeAPI.secure = true in configuration')
|
24
|
+
end
|
25
|
+
|
26
|
+
Hashie::Mash.new(response)
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# airbrake sometimes returns broken xml with invalid xml tag names
|
33
|
+
# so we remove them
|
34
|
+
require 'httparty/parser'
|
35
|
+
class HTTParty::Parser
|
36
|
+
def xml
|
37
|
+
body.gsub!(/<__utmz>.*?<\/__utmz>/m,'')
|
38
|
+
body.gsub!(/<[0-9]+.*?>.*?<\/[0-9]+.*?>/m,'')
|
39
|
+
MultiXml.parse(body)
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module AirbrakeAPI
|
2
|
+
class Error < AirbrakeAPI::Base
|
3
|
+
|
4
|
+
def self.find(*args)
|
5
|
+
setup
|
6
|
+
|
7
|
+
results = case args.first
|
8
|
+
when Fixnum
|
9
|
+
find_individual(args)
|
10
|
+
when :all
|
11
|
+
find_all(args)
|
12
|
+
else
|
13
|
+
raise AirbrakeError.new('Invalid argument')
|
14
|
+
end
|
15
|
+
|
16
|
+
raise AirbrakeError.new('No results found.') if results.nil?
|
17
|
+
raise AirbrakeError.new(results.errors.error) if results.errors
|
18
|
+
|
19
|
+
results.group || results.groups
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.update(error, options)
|
23
|
+
setup
|
24
|
+
|
25
|
+
response = put(error_path(error), { :query => options })
|
26
|
+
if response.code == 403
|
27
|
+
raise AirbrakeError.new('SSL should be enabled - use Airbrake.secure = true in configuration')
|
28
|
+
end
|
29
|
+
results = Hashie::Mash.new(response)
|
30
|
+
|
31
|
+
raise AirbrakeError.new(results.errors.error) if results.errors
|
32
|
+
results.group
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def self.find_all(args)
|
38
|
+
options = args.extract_options!
|
39
|
+
|
40
|
+
fetch(collection_path, options)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.find_individual(args)
|
44
|
+
id = args.shift
|
45
|
+
options = args.extract_options!
|
46
|
+
|
47
|
+
fetch(error_path(id), options)
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.collection_path
|
51
|
+
'/errors.xml'
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.error_path(error_id)
|
55
|
+
"/errors/#{error_id}.xml"
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'parallel'
|
2
|
+
|
3
|
+
module AirbrakeAPI
|
4
|
+
class Notice < AirbrakeAPI::Base
|
5
|
+
PER_PAGE = 30
|
6
|
+
PARALLEL_WORKERS = 10
|
7
|
+
|
8
|
+
def self.find(id, error_id, options={})
|
9
|
+
setup
|
10
|
+
|
11
|
+
hash = fetch(find_path(id, error_id), options)
|
12
|
+
|
13
|
+
if hash.errors
|
14
|
+
raise AirbrakeError.new(results.errors.error)
|
15
|
+
end
|
16
|
+
|
17
|
+
hash.notice
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.find_all_by_error_id(error_id, notice_options = {})
|
21
|
+
setup
|
22
|
+
|
23
|
+
options = {}
|
24
|
+
notices = []
|
25
|
+
page = 1
|
26
|
+
while !notice_options[:pages] || page <= notice_options[:pages]
|
27
|
+
options[:page] = page
|
28
|
+
hash = fetch(all_path(error_id), options)
|
29
|
+
if hash.errors
|
30
|
+
raise AirbrakeError.new(results.errors.error)
|
31
|
+
end
|
32
|
+
|
33
|
+
batch = Parallel.map(hash.notices, :in_threads => PARALLEL_WORKERS) do |notice_stub|
|
34
|
+
find(notice_stub.id, error_id)
|
35
|
+
end
|
36
|
+
yield batch if block_given?
|
37
|
+
batch.each{|n| notices << n }
|
38
|
+
|
39
|
+
break if batch.size < PER_PAGE
|
40
|
+
page += 1
|
41
|
+
end
|
42
|
+
notices
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.find_by_error_id(error_id, options={ 'page' => 1})
|
46
|
+
setup
|
47
|
+
|
48
|
+
hash = fetch(all_path(error_id), options)
|
49
|
+
if hash.errors
|
50
|
+
raise AirbrakeError.new(results.errors.error)
|
51
|
+
end
|
52
|
+
|
53
|
+
hash.notices
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def self.find_path(id, error_id)
|
59
|
+
"/errors/#{error_id}/notices/#{id}.xml"
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.all_path(error_id)
|
63
|
+
"/errors/#{error_id}/notices.xml"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module AirbrakeAPI
|
2
|
+
class Project < AirbrakeAPI::Base
|
3
|
+
|
4
|
+
def self.find(*args)
|
5
|
+
setup
|
6
|
+
options = args.extract_options!
|
7
|
+
|
8
|
+
results = fetch(collection_path, options)
|
9
|
+
|
10
|
+
raise AirbrakeError.new(results.errors.error) if results.errors
|
11
|
+
results.projects.project
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.collection_path
|
15
|
+
'/data_api/v1/projects.xml'
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
data/lib/airbrake_api.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'airbrake-api'
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AirbrakeAPI::Error do
|
4
|
+
before(:all) do
|
5
|
+
AirbrakeAPI.account = 'myapp'
|
6
|
+
AirbrakeAPI.auth_token = 'abcdefg123456'
|
7
|
+
AirbrakeAPI.secure = false
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should have correct collection path" do
|
11
|
+
AirbrakeAPI::Error.collection_path.should == "/errors.xml"
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should generate correct error path given an id" do
|
15
|
+
AirbrakeAPI::Error.error_path(1234).should == "/errors/1234.xml"
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '.find' do
|
19
|
+
it "should find a page of the 30 most recent errors" do
|
20
|
+
errors = AirbrakeAPI::Error.find(:all)
|
21
|
+
ordered = errors.sort_by(&:most_recent_notice_at).reverse
|
22
|
+
ordered.should == errors
|
23
|
+
errors.size.should == 30
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should paginate errors" do
|
27
|
+
errors = AirbrakeAPI::Error.find(:all, :page => 2)
|
28
|
+
ordered = errors.sort_by(&:most_recent_notice_at).reverse
|
29
|
+
ordered.should == errors
|
30
|
+
errors.size.should == 2
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should find an individual error" do
|
34
|
+
error = AirbrakeAPI::Error.find(1696170)
|
35
|
+
error.action.should == 'index'
|
36
|
+
error.id.should == 1696170
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should raise an error when not passed an id" do
|
40
|
+
lambda do
|
41
|
+
AirbrakeAPI::Error.find
|
42
|
+
end.should raise_error(AirbrakeAPI::AirbrakeError)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '.update' do
|
47
|
+
it 'should update the status of an error' do
|
48
|
+
error = AirbrakeAPI::Error.update(1696170, :group => { :resolved => true})
|
49
|
+
error.resolved.should be_true
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|