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.
@@ -0,0 +1,6 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ *.gem
5
+ pkg/*
6
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --format=nested
3
+ --backtrace
@@ -0,0 +1,7 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.2
4
+ - jruby
5
+ - rbx
6
+ - ree
7
+ - ruby-head
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "http://rubygems.org"
2
+
3
+ platforms :jruby do
4
+ gem 'jruby-openssl', '~> 0.7'
5
+ end
6
+
7
+ gemspec
@@ -0,0 +1,80 @@
1
+ Airbrake API [![Build Status](https://secure.travis-ci.org/spagalloco/airbrake-api.png)](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
@@ -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
@@ -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,5 @@
1
+ class Array
2
+ def extract_options!
3
+ last.is_a?(::Hash) ? pop : {}
4
+ end
5
+ 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
@@ -0,0 +1,3 @@
1
+ module AirbrakeAPI
2
+ VERSION = '3.2.0'
3
+ end
@@ -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