airbrake-api 3.3.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml CHANGED
@@ -1,7 +1,13 @@
1
+ language: ruby
2
+ matrix:
3
+ allow_failures:
4
+ - rvm:
5
+ - rbx-18mode
6
+ - rbx-19mode
7
+ - ruby-head
1
8
  rvm:
2
9
  - 1.8.7
3
10
  - 1.9.2
11
+ - 1.9.3
4
12
  - jruby
5
- - rbx
6
13
  - ree
7
- - ruby-head
data/README.md CHANGED
@@ -1,78 +1,102 @@
1
- Airbrake API [![Build Status](https://secure.travis-ci.org/spagalloco/airbrake-api.png)](http://travis-ci.org/spagalloco/airbrake-api)
2
- ======================================================================================================================================
1
+ # Airbrake API [![Build Status](https://secure.travis-ci.org/spagalloco/airbrake-api.png)](http://travis-ci.org/spagalloco/airbrake-api)
3
2
 
4
- A ruby wrapper for the [Airbrake API](http://airbrakeapp.com/pages/api)
3
+ A ruby client for the [Airbrake API](http://help.airbrake.io/kb/api-2/api-overview)
5
4
 
6
- Usage
7
- -----
5
+ ## Changes in 4.0
8
6
 
9
- The first thing you need to set is the account name. This is the same as the web address for your account.
7
+ AirbrakeAPI has been completely rewritten in 4.0. Why the high version number?
8
+ This was the first gem I ever wrote and it's wandered a path that started with
9
+ ActiveResource, followed by HTTParty, and now Faraday. Along the way, AirbrakeAPI
10
+ has lost it's ActiveRecord-like syntax for a more concise and simple API. Instead
11
+ of using classes such as `AirbrakeAPI::Error`, the entire API is contained within
12
+ `AirbrakeAPI::Client`.
10
13
 
11
- AirbrakeAPI.account = 'myaccount'
14
+ The following classes are now deprecated:
12
15
 
13
- Then, you should set the authentication token.
16
+ * `AirbrakeAPI::Error`
17
+ * `AirbrakeAPI::Notice`
18
+ * `AirbrakeAPI::Project`
14
19
 
15
- AirbrakeAPI.auth_token = 'abcdefg'
20
+ While your code will continue to work using the old API, they will ultimately be removed in favor of `AirbrakeAPI::Client`.
16
21
 
17
- If your account uses ssl then turn it on:
22
+ ## Configuration
18
23
 
19
- AirbrakeAPI.secure = true
20
-
21
- Optionally, you can configure through a single method:
24
+ AirbrakeAPI can be configured by passing a hash to the configure method:
22
25
 
23
26
  AirbrakeAPI.configure(:account => 'anapp', :auth_token => 'abcdefg', :secure => true)
24
27
 
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.
28
+ or via a block:
29
+
30
+ AirbrakeAPI.configure do |config|
31
+ config.account = 'anapp'
32
+ config.auth_token = 'abcdefg'
33
+ config.secure = true
34
+ end
26
35
 
27
- Finding Errors
28
- --------------
36
+ ## Finding Errors
29
37
 
30
38
  Errors are paginated, the API responds with 25 at a time, pass an optional params hash for additional pages:
31
39
 
32
- AirbrakeAPI::Error.find(:all)
33
- AirbrakeAPI::Error.find(:all, :page => 2)
40
+ AirbrakeAPI.errors
41
+ AirbrakeAPI.errors(:page => 2)
34
42
 
35
43
  To find an individual error, you can find by ID:
36
44
 
37
- AirbrakeAPI::Error.find(error_id)
45
+ AirbrakeAPI.error(error_id)
46
+
47
+
48
+ ## Finding Notices
38
49
 
39
50
  Find *all* notices of an error:
40
51
 
41
- AirbrakeAPI::Notice.find_all_by_error_id(error_id)
52
+ AirbrakeAPI.notices(error_id)
42
53
 
43
54
  Find an individual notice:
44
55
 
45
- AirbrakeAPI::Notice.find(notice_id, error_id)
56
+ AirbrakeAPI.notice(notice_id, error_id)
46
57
 
47
58
  To resolve an error via the API:
48
59
 
49
- AirbrakeAPI::Error.update(1696170, :group => { :resolved => true})
60
+ AirbrakeAPI.update(1696170, :group => { :resolved => true})
50
61
 
51
62
  Recreate an error:
52
63
 
53
64
  STDOUT.sync = true
54
- AirbrakeAPI::Notice.find_all_by_error_id(error_id) do |batch|
65
+ AirbrakeAPI.notices(error_id) do |batch|
55
66
  batch.each do |notice|
56
67
  result = system "curl --silent '#{notice.request.url}' > /dev/null"
57
68
  print (result ? '.' : 'F')
58
69
  end
59
70
  end
60
71
 
61
- Projects
62
- --------
72
+ ## Projects
63
73
 
64
74
  To retrieve a list of projects:
65
75
 
66
- AirbrakeAPI::Project.find(:all)
76
+ AirbrakeAPI.projects
77
+
78
+ ## Deployments
79
+
80
+ To retrieve a list of deployments:
81
+
82
+ AirbrakeAPI.deployments
83
+
84
+ ## Connecting to more than one account
85
+
86
+ While module-based configuration will work in most cases, if you'd like to simultaneously connect to more than one account at once, you can create instances of `AirbrakeAPI::Client` to do so:
67
87
 
68
- Responses
69
- ---------
88
+ client = AirbrakeAPI::Client.new(:account => 'myaccount', :auth_token => 'abcdefg', :secure => true)
89
+ altclient = AirbrakeAPI::Client.new(:account => 'anotheraccount', :auth_token => '123456789', :secure => false)
90
+
91
+ client.errors
92
+
93
+ altclient.projects
70
94
 
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.
95
+ ## Responses
72
96
 
97
+ If an error is returned from the API, an AirbrakeError exception will be raised. Successful responses will return a Hashie::Mash object based on the data from the response.
73
98
 
74
- Contributors
75
- ------------
99
+ ## Contributors
76
100
 
77
101
  * [Matias Käkelä](https://github.com/massive) - SSL Support
78
102
  * [Jordan Brough](https://github.com/jordan-brough) - Notices
data/airbrake-api.gemspec CHANGED
@@ -14,9 +14,11 @@ Gem::Specification.new do |s|
14
14
  s.email = ['steve.agalloco@gmail.com']
15
15
  s.homepage = 'https://github.com/spagalloco/airbrake-api'
16
16
 
17
- s.add_dependency 'httparty', '~> 0.8.0'
18
17
  s.add_dependency 'hashie', '~> 1.2'
19
18
  s.add_dependency 'parallel', '~> 0.5.0'
19
+ s.add_dependency 'faraday_middleware', '~> 0.8'
20
+ s.add_dependency 'multi_xml', '~> 0.4'
21
+ s.add_dependency 'mash'
20
22
 
21
23
  s.add_development_dependency 'rake', '~> 0.9.2'
22
24
  s.add_development_dependency 'rspec', '~> 2.6.0'
data/lib/airbrake-api.rb CHANGED
@@ -1,29 +1,11 @@
1
- require 'hashie'
2
- require 'httparty'
1
+ require 'airbrake-api/configuration'
3
2
 
4
3
  module AirbrakeAPI
5
- extend self
6
- attr_accessor :account, :auth_token, :secure
4
+ extend Configuration
7
5
 
8
6
  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}.airbrake.io"
18
- end
19
-
20
- def protocol
21
- secure ? "https" : "http"
22
- end
23
-
24
7
  end
25
8
 
26
- require 'airbrake-api/core_extensions'
27
9
  require 'airbrake-api/client'
28
10
  require 'airbrake-api/error'
29
11
  require 'airbrake-api/notice'
@@ -0,0 +1,17 @@
1
+ # placeholder since pre-refactor classes used AirbrakeAPI::Base
2
+ module AirbrakeAPI
3
+ class Base
4
+ class << self
5
+ def setup
6
+ end
7
+
8
+ def fetch(path, options)
9
+ Client.new.get(path, options)
10
+ end
11
+
12
+ def deprecate(msg)
13
+ Kernel.warn("[Deprecation] - #{msg}")
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,41 +1,164 @@
1
+ require 'faraday_middleware'
2
+ require 'airbrake-api/middleware/scrub_response'
3
+ require 'airbrake-api/middleware/raise_server_error'
4
+ require 'airbrake-api/middleware/raise_response_error'
5
+
1
6
  module AirbrakeAPI
2
- class Base
3
- include HTTParty
4
- format :xml
7
+ class Client
5
8
 
6
- private
9
+ PER_PAGE = 30
10
+ PARALLEL_WORKERS = 10
7
11
 
8
- def self.setup
9
- base_uri AirbrakeAPI.account_path
10
- default_params :auth_token => AirbrakeAPI.auth_token
12
+ attr_accessor *AirbrakeAPI::Configuration::VALID_OPTIONS_KEYS
11
13
 
12
- check_configuration
14
+ def initialize(options={})
15
+ attrs = AirbrakeAPI.options.merge(options)
16
+ AirbrakeAPI::Configuration::VALID_OPTIONS_KEYS.each do |key|
17
+ send("#{key}=", attrs[key])
18
+ end
13
19
  end
14
20
 
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)
21
+ # deploys
22
+
23
+ def deploys(project_id, options = {})
24
+ results = request(:get, deploys_path(project_id), options)
25
+ results.projects.respond_to?(:deploy) ? results.projects.try(:deploy) : []
26
+ end
27
+
28
+ def deploys_path(project_id)
29
+ "/projects/#{project_id}/deploys.xml"
30
+ end
31
+
32
+ # projects
33
+ def projects_path
34
+ '/data_api/v1/projects.xml'
35
+ end
36
+
37
+ def projects(options = {})
38
+ results = request(:get, projects_path, options)
39
+ results.projects.project
40
+ end
41
+
42
+ # errors
43
+
44
+ def error_path(error_id)
45
+ "/errors/#{error_id}.xml"
18
46
  end
19
47
 
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')
48
+ def errors_path
49
+ '/errors.xml'
50
+ end
51
+
52
+ def update(error, options = {})
53
+ results = request(:put, error_path(error), :body => options)
54
+ results.group
55
+ end
56
+
57
+ def error(error_id, options = {})
58
+ results = request(:get, error_path(error_id), options)
59
+ results.group || results.groups
60
+ end
61
+
62
+ def errors(options = {})
63
+ results = request(:get, errors_path, options)
64
+ results.group || results.groups
65
+ end
66
+
67
+ # notices
68
+
69
+ def notice_path(notice_id, error_id)
70
+ "/errors/#{error_id}/notices/#{notice_id}.xml"
71
+ end
72
+
73
+ def notices_path(error_id)
74
+ "/errors/#{error_id}/notices.xml"
75
+ end
76
+
77
+ def notice(notice_id, error_id, options = {})
78
+ hash = request(:get, notice_path(notice_id, error_id), options)
79
+ hash.notice
80
+ end
81
+
82
+ def notices(error_id, notice_options = {}, &block)
83
+ options = {}
84
+ notices = []
85
+ page = notice_options[:page]
86
+ page_count = 0
87
+
88
+ # a specific page is requested, only return that page
89
+ # if no page is specified, start on page 1
90
+ if page
91
+ notice_options[:pages] = 1
92
+ else
93
+ page = 1
24
94
  end
25
95
 
26
- Hashie::Mash.new(response)
96
+ while !notice_options[:pages] || (page_count + 1) <= notice_options[:pages]
97
+ options[:page] = page + page_count
98
+ hash = request(:get, notices_path(error_id), options)
99
+
100
+ batch = Parallel.map(hash.notices, :in_threads => PARALLEL_WORKERS) do |notice_stub|
101
+ notice(notice_stub.id, error_id)
102
+ end
103
+ yield batch if block_given?
104
+ batch.each{|n| notices << n }
105
+
106
+ break if batch.size < PER_PAGE
107
+ page_count += 1
108
+ end
109
+ notices
27
110
  end
28
111
 
29
- end
30
- end
112
+ private
113
+
114
+ def account_path
115
+ "#{protocol}://#{@account}.airbrake.io"
116
+ end
117
+
118
+ def protocol
119
+ @secure ? "https" : "http"
120
+ end
121
+
122
+ # Perform an HTTP request
123
+ def request(method, path, params = {}, options = {})
124
+
125
+ raise AirbrakeError.new('API Token cannot be nil') if @auth_token.nil?
126
+ raise AirbrakeError.new('Account cannot be nil') if @account.nil?
127
+
128
+ params.merge!(:auth_token => @auth_token)
129
+
130
+ response = connection(options).run_request(method, nil, nil, nil) do |request|
131
+ case method.to_sym
132
+ when :delete, :get
133
+ request.url(path, params)
134
+ when :post, :put
135
+ request.path = path
136
+ request.body = params unless params.empty?
137
+ end
138
+ end
139
+ response.body
140
+ end
141
+
142
+ def connection(options={})
143
+ default_options = {
144
+ :headers => {
145
+ :accept => 'application/xml',
146
+ :user_agent => user_agent,
147
+ },
148
+ :ssl => {:verify => false},
149
+ :url => account_path,
150
+ }
151
+ @connection ||= Faraday.new(default_options.deep_merge(connection_options)) do |builder|
152
+ builder.use Faraday::Request::UrlEncoded
153
+ builder.use AirbrakeAPI::Middleware::RaiseResponseError
154
+ builder.use FaradayMiddleware::Mashify
155
+ builder.use FaradayMiddleware::ParseXml
156
+ builder.use AirbrakeAPI::Middleware::ScrubResponse
157
+ builder.use AirbrakeAPI::Middleware::RaiseServerError
158
+
159
+ builder.adapter adapter
160
+ end
161
+ end
31
162
 
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
163
  end
41
164
  end
@@ -0,0 +1,55 @@
1
+ require 'airbrake-api/version'
2
+
3
+ module AirbrakeAPI
4
+ module Configuration
5
+ VALID_OPTIONS_KEYS = [
6
+ :account,
7
+ :auth_token,
8
+ :secure,
9
+ :connection_options,
10
+ :adapter,
11
+ :user_agent]
12
+
13
+ attr_accessor *VALID_OPTIONS_KEYS
14
+
15
+ DEFAULT_ADAPTER = :net_http
16
+ DEFAULT_USER_AGENT = "AirbrakeAPI Ruby Gem #{AirbrakeAPI::VERSION}"
17
+ DEFAULT_CONNECTION_OPTIONS = {}
18
+
19
+ def self.extended(base)
20
+ base.reset
21
+ end
22
+
23
+ def configure(options={})
24
+ @account = options[:account] if options.has_key?(:account)
25
+ @auth_token = options[:auth_token] if options.has_key?(:auth_token)
26
+ @secure = options[:secure] if options.has_key?(:secure)
27
+ yield self if block_given?
28
+ self
29
+ end
30
+
31
+ def options
32
+ options = {}
33
+ VALID_OPTIONS_KEYS.each{|k| options[k] = send(k)}
34
+ options
35
+ end
36
+
37
+ def account_path
38
+ "#{protocol}://#{@account}.airbrake.io"
39
+ end
40
+
41
+ def protocol
42
+ @secure ? "https" : "http"
43
+ end
44
+
45
+ def reset
46
+ @account = nil
47
+ @auth_token = nil
48
+ @secure = false
49
+ @adapter = DEFAULT_ADAPTER
50
+ @user_agent = DEFAULT_USER_AGENT
51
+ @connection_options = DEFAULT_CONNECTION_OPTIONS
52
+ end
53
+
54
+ end
55
+ end