appnexusapi 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/README.md +71 -39
  4. data/appnexusapi.gemspec +1 -2
  5. data/lib/appnexusapi.rb +21 -10
  6. data/lib/appnexusapi/configuration.rb +12 -0
  7. data/lib/appnexusapi/connection.rb +83 -90
  8. data/lib/appnexusapi/error.rb +3 -0
  9. data/lib/appnexusapi/faraday/raise_http_error.rb +15 -1
  10. data/lib/appnexusapi/read_only_service.rb +8 -0
  11. data/lib/appnexusapi/resource.rb +19 -15
  12. data/lib/appnexusapi/service.rb +35 -48
  13. data/lib/appnexusapi/services/ad_server_service.rb +9 -0
  14. data/lib/appnexusapi/{advertiser_service.rb → services/advertiser_service.rb} +0 -0
  15. data/lib/appnexusapi/{bidder_instance_service.rb → services/bidder_instance_service.rb} +0 -6
  16. data/lib/appnexusapi/{bidder_profile_service.rb → services/bidder_profile_service.rb} +0 -6
  17. data/lib/appnexusapi/{bidder_service.rb → services/bidder_service.rb} +0 -0
  18. data/lib/appnexusapi/services/brand_service.rb +2 -0
  19. data/lib/appnexusapi/services/browser_service.rb +2 -0
  20. data/lib/appnexusapi/{campaign_service.rb → services/campaign_service.rb} +0 -0
  21. data/lib/appnexusapi/services/category_service.rb +5 -0
  22. data/lib/appnexusapi/{content_category_service.rb → services/content_category_service.rb} +0 -1
  23. data/lib/appnexusapi/{creative_service.rb → services/creative_service.rb} +0 -0
  24. data/lib/appnexusapi/{creative_template_service.rb → services/creative_template_service.rb} +1 -6
  25. data/lib/appnexusapi/services/device_model_service.rb +2 -0
  26. data/lib/appnexusapi/{domain_list_service.rb → services/domain_list_service.rb} +0 -0
  27. data/lib/appnexusapi/services/inventory_attribute_service.rb +2 -0
  28. data/lib/appnexusapi/{inventory_source_service.rb → services/inventory_source_service.rb} +0 -0
  29. data/lib/appnexusapi/services/language_service.rb +2 -0
  30. data/lib/appnexusapi/{line_item_service.rb → services/line_item_service.rb} +0 -0
  31. data/lib/appnexusapi/services/log_level_data_download_service.rb +13 -0
  32. data/lib/appnexusapi/services/log_level_data_service.rb +105 -0
  33. data/lib/appnexusapi/services/media_type_service.rb +2 -0
  34. data/lib/appnexusapi/{member_service.rb → services/member_service.rb} +0 -0
  35. data/lib/appnexusapi/{object_limit_service.rb → services/object_limit_service.rb} +0 -0
  36. data/lib/appnexusapi/{operating_system_extended_service.rb → services/operating_system_extended_service.rb} +1 -6
  37. data/lib/appnexusapi/services/operating_system_service.rb +2 -0
  38. data/lib/appnexusapi/{payment_rule_service.rb → services/payment_rule_service.rb} +0 -0
  39. data/lib/appnexusapi/{placement_service.rb → services/placement_service.rb} +0 -0
  40. data/lib/appnexusapi/services/platform_member_service.rb +2 -0
  41. data/lib/appnexusapi/{profile_service.rb → services/profile_service.rb} +0 -0
  42. data/lib/appnexusapi/{publisher_service.rb → services/publisher_service.rb} +0 -0
  43. data/lib/appnexusapi/{segment_service.rb → services/segment_service.rb} +0 -2
  44. data/lib/appnexusapi/{site_service.rb → services/site_service.rb} +0 -0
  45. data/lib/appnexusapi/services/technical_attribute_service.rb +2 -0
  46. data/lib/appnexusapi/{tiny_tag_service.rb → services/tiny_tag_service.rb} +0 -6
  47. data/lib/appnexusapi/{user_service.rb → services/user_service.rb} +0 -0
  48. data/lib/appnexusapi/version.rb +1 -1
  49. data/spec/connection_spec.rb +46 -10
  50. data/spec/fixtures/vcr/content_category_crud.yml +188 -69
  51. data/spec/fixtures/vcr/log_level_data_service_download.yml +213 -0
  52. data/spec/integration/content_category_spec.rb +4 -0
  53. data/spec/{creative_service_spec.rb → integration/creative_service_spec.rb} +0 -0
  54. data/spec/integration/log_level_data_service_spec.rb +13 -0
  55. data/spec/{object_limit_service_spec.rb → integration/object_limit_service_spec.rb} +0 -0
  56. data/spec/integration/placement_spec.rb +0 -1
  57. data/spec/spec_helper.rb +2 -3
  58. metadata +52 -94
  59. data/lib/appnexusapi/ad_server_resource.rb +0 -2
  60. data/lib/appnexusapi/ad_server_service.rb +0 -20
  61. data/lib/appnexusapi/advertiser_resource.rb +0 -2
  62. data/lib/appnexusapi/bidder_instance_resource.rb +0 -2
  63. data/lib/appnexusapi/bidder_profile_resource.rb +0 -2
  64. data/lib/appnexusapi/bidder_resource.rb +0 -2
  65. data/lib/appnexusapi/brand_resource.rb +0 -2
  66. data/lib/appnexusapi/brand_service.rb +0 -8
  67. data/lib/appnexusapi/browser_resource.rb +0 -2
  68. data/lib/appnexusapi/browser_service.rb +0 -8
  69. data/lib/appnexusapi/campaign_resource.rb +0 -2
  70. data/lib/appnexusapi/category_resource.rb +0 -2
  71. data/lib/appnexusapi/category_service.rb +0 -12
  72. data/lib/appnexusapi/content_category_resource.rb +0 -2
  73. data/lib/appnexusapi/creative_resource.rb +0 -2
  74. data/lib/appnexusapi/creative_template_resource.rb +0 -2
  75. data/lib/appnexusapi/device_model_resource.rb +0 -2
  76. data/lib/appnexusapi/device_model_service.rb +0 -6
  77. data/lib/appnexusapi/domain_list_resource.rb +0 -2
  78. data/lib/appnexusapi/inventory_attribute_resource.rb +0 -2
  79. data/lib/appnexusapi/inventory_attribute_service.rb +0 -8
  80. data/lib/appnexusapi/inventory_source_resource.rb +0 -2
  81. data/lib/appnexusapi/language_resource.rb +0 -2
  82. data/lib/appnexusapi/language_service.rb +0 -8
  83. data/lib/appnexusapi/line_item_resource.rb +0 -2
  84. data/lib/appnexusapi/log_level_data_download_service.rb +0 -68
  85. data/lib/appnexusapi/log_level_data_resource.rb +0 -19
  86. data/lib/appnexusapi/log_level_data_service.rb +0 -26
  87. data/lib/appnexusapi/media_type_resource.rb +0 -2
  88. data/lib/appnexusapi/media_type_service.rb +0 -8
  89. data/lib/appnexusapi/member_resource.rb +0 -2
  90. data/lib/appnexusapi/object_limit_resource.rb +0 -2
  91. data/lib/appnexusapi/operating_system_extended_resource.rb +0 -2
  92. data/lib/appnexusapi/operating_system_resource.rb +0 -2
  93. data/lib/appnexusapi/operating_system_service.rb +0 -8
  94. data/lib/appnexusapi/payment_rule_resource.rb +0 -2
  95. data/lib/appnexusapi/placement_resource.rb +0 -2
  96. data/lib/appnexusapi/platform_member_resource.rb +0 -2
  97. data/lib/appnexusapi/platform_member_service.rb +0 -8
  98. data/lib/appnexusapi/profile_resource.rb +0 -2
  99. data/lib/appnexusapi/publisher_resource.rb +0 -2
  100. data/lib/appnexusapi/segment_resource.rb +0 -2
  101. data/lib/appnexusapi/site_resource.rb +0 -2
  102. data/lib/appnexusapi/technical_attribute_resource.rb +0 -2
  103. data/lib/appnexusapi/technical_attribute_service.rb +0 -8
  104. data/lib/appnexusapi/tiny_tag_resource.rb +0 -2
  105. data/lib/appnexusapi/user_resource.rb +0 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0184db6bca5286e5ec3ac206cf0d964ce78484e0
4
- data.tar.gz: dec7fe42720c2c0b89dcaa55bbb142bfce2721b5
3
+ metadata.gz: 06455610db5b16548020d670d175ef13fd7451f5
4
+ data.tar.gz: 07544e12f14bb6e8fe351a53798fcbf5e2eaef94
5
5
  SHA512:
6
- metadata.gz: fb39def781471b2b91f8803116d0c71d734dca49ddaca2fec95e6d104b1f7e21c61aa9c0d55fa11300a69d6d95e7e961a7479dc34ef319a8791e9032483f5aa4
7
- data.tar.gz: 6932d3ac84d2f075c5cb71c4d5a97a0a618ed2af94b7f41091f2776dff59c82099527a8ac6b2c319d9577c61f6725b1f7a322e115f24fbd1e3b76686dfe49535
6
+ metadata.gz: f96356e6bb6566aff2903b3964ccb01dbb0f2ad89aaa9c2372708fa9049569c3c37c95b8c15a28370af856dfe0352409775eba90c04aaa9b0eaef03a055bb476
7
+ data.tar.gz: 640ca6320f0c33ab849038014a8f7971cbaf09a02d729252c33b819609e50384f9fe5c1566c347991181dfa19a9e374c402c160f9554a32f9e79cbc3e2f4bb44
@@ -0,0 +1,9 @@
1
+ ## 1.1.0
2
+ - Add `Resource#save` method to allow saving resources that have been modified with `update`
3
+ - Replace `Pester` gem with `Retriable`
4
+ - Remove `NullLogger` dependency; log to STDOUT by default
5
+ - Delete unnecessary `_resource.rb` files
6
+
7
+ ## 1.0.0
8
+
9
+ - Initial 1.0 release; lots of new services and resources.
data/README.md CHANGED
@@ -18,58 +18,83 @@ Or install it yourself as:
18
18
 
19
19
  ## Usage
20
20
 
21
- Establish a connection:
21
+ #### Configure defaults
22
+ ```ruby
23
+ AppnexusApi.configure do |config|
24
+ config.logger = Logger.new('/path/to/log.log')
25
+ end
26
+ ```
27
+
28
+ #### Establish a connection:
29
+
30
+ ```ruby
31
+ connection = AppnexusApi::Connection.new(
32
+ 'username' => 'username',
33
+ 'password' => 'password',
34
+ 'logger' => Logger.new(STDOUT) # defaults to STDOUT
35
+
36
+ # Defaults to connecting to https://api.appnexus.com/ but you can optionally pass a uri to
37
+ # connect to another endpoint, e.g. the staging site could be
38
+ # "uri" => 'http://api-test.appnexus.com',
39
+ )
40
+ ```
22
41
 
23
- connection = AppnexusApi::Connection.new(
24
- 'username' => 'username',
25
- 'password' => 'password',
26
- 'logger' => Logger.new(STDOUT) # defaults to a null logger if no value passed in.
42
+ #### Use a Service:
27
43
 
28
- # Defaults to connecting to https://api.appnexus.com/ but you can optionally pass a uri to
29
- # connect to another endpoint, e.g. the staging site could be
30
- # "uri" => 'http://api-test.appnexus.com',
44
+ ```ruby
45
+ member_service = AppnexusApi::MemberService.new(connection)
46
+ # get always returns an array of results
47
+ # and defaults "num_elements" to 100 and "start_element" to 0
48
+ # and returns an AppnexusApi::Resource object which is a wrapper around the JSON
49
+ member = member_service.get.first
31
50
 
51
+ line_item_service = AppnexusApi::LineItemService.new(connection)
52
+ line_item = line_item_service.get.first
53
+ line_item = line_item_service.get({advertiser_id: 12345}).first
32
54
 
33
- )
55
+ # create a new object
56
+ url_params = { advertiser_id: 12345 }
57
+ body_params = { name: "some line item", code: "line item code"}
34
58
 
35
- Use a Service:
59
+ line_item = line_item_service.create(url_params, body_params)
60
+ line_item.state
36
61
 
37
- member_service = AppnexusApi::MemberService.new(connection)
38
- # get always returns an array of results
39
- # and defaults "num_elements" to 100 and "start_element" to 0
40
- # and returns an AppnexusApi::Resource object which is a wrapper around the JSON
41
- member = member_service.get.first
42
62
 
43
- line_item_service = AppnexusApi::LineItemService.new(connection)
44
- line_item = line_item_service.get.first
45
- line_item = line_item_service.get({advertiser_id: 12345}).first
63
+ # update an object
64
+ update_params = { state: "inactive" }
65
+ json_result = line_item.update(url_params, update_params)
46
66
 
47
- # create a new object
48
- url_params = { advertiser_id: 12345 }
49
- body_params = { name: "some line item", code: "line item code"}
67
+ # delete an object
68
+ line_item.delete(url_params)
50
69
 
51
- line_item = line_item_service.create(url_params, body_params)
52
- line_item.state
70
+ # save an object that you modified locally
71
+ line_item.raw_json[:state] = 'active'
72
+ line_item.save
53
73
 
74
+ # this raises an AppnexusApi::UnprocessableEntity, not a 404 as it should
75
+ line_item_service.get(line_item.id)
54
76
 
55
- # update an object
56
- update_params = { state: "inactive" }
57
- json_result = line_item.update(url_params, update_params)
77
+ new_creative = {
78
+ "content" => "<iframe src='helloword.html'></iframe>",
79
+ "width" => "300",
80
+ "height" => "250",
81
+ "template" =>{ "id" => 7 }
82
+ }
83
+ creative = creative_service.create(new_creative)
84
+ creative.update("campaign" => "Testing")
85
+ ```
58
86
 
59
- # delete an object
60
- line_item.delete(url_params)
87
+ #### Downloading Log Level Data:
61
88
 
62
- # this raises an AppnexusApi::UnprocessableEntity, not a 404 as it should
63
- line_item_service.get(line_item.id)
89
+ ```ruby
90
+ download_service = AppnexusApi::LogLevelDataService.new(
91
+ connection,
92
+ downloaded_files_path: '/example/local/path',
93
+ siphon_name: 'standard_feed'
94
+ )
64
95
 
65
- new_creative = {
66
- "content" => "<iframe src='helloword.html'></iframe>",
67
- "width" => "300",
68
- "height" => "250",
69
- "template" =>{ "id" => 7 }
70
- }
71
- creative = creative_service.create(new_creative)
72
- creative.update("campaign" => "Testing")
96
+ downloaded_files = data_service.download_new_files_since('2016-02-01'.to_time)
97
+ ```
73
98
 
74
99
  ## Testing
75
100
 
@@ -81,8 +106,15 @@ bundle exec rspec
81
106
  ### Writing New Specs or Updating Old Ones
82
107
  This library uses [VCR](https://github.com/vcr/vcr) and Webmock to record API call HTTP data and play it back when you run the specs. To write new specs or update old ones, however, you will need to actually provide a valid login/password in a `.env` file (see [`env_example`](env_example) for an example) before launching the specs.
83
108
 
84
- To update a spec, simply remove the relevant file from [spec/fixtures/vcr](spec/fixtures/vcr) (and setup the username/password) before launching `rspec`; the changes will be recorded automatically by VCR.
109
+ To update a spec, simply remove the relevant file from [spec/fixtures/vcr](spec/fixtures/vcr) (and setup the username/password as above) before launching `rspec`; the changes will be recorded automatically by VCR.
110
+
111
+ ## Debugging
85
112
 
113
+ To trigger full Faraday request/respone logging, set the `APPNEXUS_API_DEBUG` environment variable before launching
114
+ your application
115
+ ```
116
+ export APPNEXUS_API_DEBUG=true
117
+ ```
86
118
 
87
119
  ## Contributing
88
120
 
@@ -18,8 +18,7 @@ Gem::Specification.new do |gem|
18
18
  gem.add_dependency 'faraday', '~> 0.9'
19
19
  gem.add_dependency 'faraday_middleware'
20
20
  gem.add_dependency 'multi_json'
21
- gem.add_dependency 'pester'
22
- gem.add_dependency 'null_logger'
21
+ gem.add_dependency 'retriable', '>= 2.0'
23
22
 
24
23
  gem.add_development_dependency 'bundler', '>= 1.2.0'
25
24
  gem.add_development_dependency 'rspec'
@@ -1,15 +1,26 @@
1
- require "faraday"
2
- require "appnexusapi/version"
3
- require "appnexusapi/error"
1
+ require 'faraday'
2
+ require 'faraday_middleware'
3
+ require 'logger'
4
+ require 'retriable'
4
5
 
5
- module AppnexusApi
6
- autoload :Connection, "appnexusapi/connection"
6
+ require 'appnexusapi/configuration'
7
+ require 'appnexusapi/version'
8
+ require 'appnexusapi/error'
9
+ require 'appnexusapi/resource'
10
+ require 'appnexusapi/service'
11
+ require 'appnexusapi/read_only_service'
12
+ require 'appnexusapi/connection'
13
+
14
+ Dir.glob("#{File.join(File.dirname(__FILE__), 'appnexusapi', 'services')}/*.rb").each do |service_file|
15
+ require service_file
16
+ end
7
17
 
8
- dir = File.dirname(__FILE__) + "/appnexusapi"
9
- files = Dir.glob(File.expand_path("{*resource,*service}.rb", dir)).map{|f| File.basename(f, '.rb')}
10
- files.each do |file|
11
- sym = file.capitalize.gsub(/(_(.))/) { |c| $2.upcase }.to_sym
12
- autoload sym, "appnexusapi/#{file}"
18
+ module AppnexusApi
19
+ def self.config
20
+ @configuration ||= Configuration.new
13
21
  end
14
22
 
23
+ def self.configure
24
+ yield(config)
25
+ end
15
26
  end
@@ -0,0 +1,12 @@
1
+ module AppnexusApi
2
+ class Configuration
3
+ attr_accessor :api_debug, :logger
4
+
5
+ def initialize
6
+ @api_debug = ENV['APPNEXUS_API_DEBUG'].to_s =~ /^(true|t|yes|y|1)$/i
7
+ @logger = Logger.new(STDOUT).tap do |logger|
8
+ @api_debug ? logger.level = Logger::DEBUG : logger.level = Logger::INFO
9
+ end
10
+ end
11
+ end
12
+ end
@@ -1,108 +1,101 @@
1
- require 'faraday_middleware'
2
1
  require 'appnexusapi/faraday/raise_http_error'
3
- require 'null_logger'
4
-
5
- class AppnexusApi::Connection
6
- RATE_EXCEEDED_DEFAULT_TIMEOUT = 15
7
- # Inexplicably, sandbox uses the correct code of 429, while production uses 405? so
8
- # we just rely on the error message
9
- RATE_EXCEEDED_ERROR = 'RATE_EXCEEDED'.freeze
10
-
11
- def initialize(config)
12
- @config = config
13
- @config['uri'] ||= 'https://api.appnexus.com/'
14
- @logger = @config['logger'] || NullLogger.instance
15
- @connection = Faraday.new(@config['uri']) do |conn|
16
- conn.response :logger, @logger, bodies: true
17
- conn.request :json
18
- conn.response :json, :content_type => /\bjson$/
19
- conn.use AppnexusApi::Faraday::Response::RaiseHttpError
20
- conn.adapter Faraday.default_adapter
2
+
3
+ module AppnexusApi
4
+ class Connection
5
+ RATE_EXCEEDED_DEFAULT_TIMEOUT = 15
6
+
7
+ RATE_LIMIT_WAITER = Proc.new do |exception, try, elapsed_time, next_interval|
8
+ if (match = /Retry after (\d+)s/.match(exception.message))
9
+ seconds = match[1].to_i
10
+ else
11
+ seconds = RATE_EXCEEDED_DEFAULT_TIMEOUT
12
+ end
13
+ seconds += RATE_EXCEEDED_DEFAULT_TIMEOUT # Just to be sure we waited long enough
14
+ AppnexusApi.config.logger.warn("WARN: Rate limit exceeded; retrying after #{seconds}s...")
15
+ sleep(seconds)
21
16
  end
22
- end
23
17
 
24
- def is_authorized?
25
- !@token.nil?
26
- end
18
+ attr_reader :connection, :logger
27
19
 
28
- def log
29
- @logger
30
- end
20
+ def initialize(config)
21
+ @config = config
22
+ @config['uri'] ||= 'https://api.appnexus.com/'
23
+ @logger = @config['logger'] || AppnexusApi.config.logger
24
+ @connection = ::Faraday.new(@config['uri']) do |conn|
25
+ conn.response :logger, @logger, bodies: true if AppnexusApi.config.api_debug
26
+ conn.request :json
27
+ conn.response :json, :content_type => /\bjson$/
28
+ conn.use AppnexusApi::Faraday::Response::RaiseHttpError
29
+ conn.adapter ::Faraday.default_adapter
30
+ end
31
+ end
32
+
33
+ def login
34
+ response = @connection.run_request(
35
+ :post,
36
+ 'auth',
37
+ { 'auth' => { 'username' => @config['username'], 'password' => @config['password'] } },
38
+ {}
39
+ )
40
+
41
+ if response.body['response']['error_code']
42
+ fail "#{response.body['response']['error_code']}/#{response.body['response']['error_description']}"
43
+ end
31
44
 
32
- def login
33
- response = @connection.run_request(:post, 'auth', { 'auth' => { 'username' => @config['username'], 'password' => @config['password'] } }, {})
34
- log.debug(response.body)
35
- if response.body['response']['error_code']
36
- fail "#{response.body['response']['error_code']}/#{response.body['response']['error_description']}"
45
+ @token = response.body['response']['token']
37
46
  end
38
- @token = response.body['response']['token']
39
- end
40
47
 
41
- def logout
42
- @token = nil
43
- end
48
+ def get(route, params={}, headers={})
49
+ params = params.delete_if { |_, v| v.nil? }
50
+ run_request(:get, build_url(route, params), nil, headers)
51
+ end
44
52
 
45
- def get(route, params={}, headers={})
46
- params = params.delete_if {|key, value| value.nil? }
47
- run_request(:get, build_url(route, params), nil, headers)
48
- end
53
+ def build_url(route, params)
54
+ @connection.build_url(route, params)
55
+ end
49
56
 
50
- def build_url(route, params)
51
- @connection.build_url(route, params)
52
- end
57
+ def post(route, body=nil, headers={})
58
+ run_request(:post, route, body, headers)
59
+ end
53
60
 
54
- def post(route, body=nil, headers={})
55
- run_request(:post, route, body, headers)
56
- end
61
+ def put(route, body=nil, headers={})
62
+ run_request(:put, route, body, headers)
63
+ end
57
64
 
58
- def put(route, body=nil, headers={})
59
- run_request(:put, route, body, headers)
60
- end
65
+ def delete(route, body=nil, headers={})
66
+ run_request(:delete, route, body, headers)
67
+ end
61
68
 
62
- def delete(route, body=nil, headers={})
63
- run_request(:delete, route, body, headers)
64
- end
69
+ private
65
70
 
66
- def run_request(method, route, body, headers)
67
- login unless is_authorized?
68
- response = {}
69
- begin
70
- loop do
71
- response = run_request_only(
72
- method,
73
- route,
74
- body,
75
- { 'Authorization' => @token }.merge(headers)
76
- )
77
- break if response.body.empty? # Log level data download service returns a body of ""
78
- break unless response.body.fetch('response', {})['error_code'] == RATE_EXCEEDED_ERROR
79
- wait_time = response.headers['retry-after'] || RATE_EXCEEDED_DEFAULT_TIMEOUT
80
- log.info("received rate exceeded. wait time: #{wait_time}s")
81
- sleep wait_time.to_i
82
- end
83
- rescue AppnexusApi::Unauthorized => e
84
- if @retry == true
85
- raise AppnexusApi::Unauthorized, e
86
- else
87
- @retry = true
88
- logout
89
- response = run_request(method, route, body, headers)
71
+ def run_request(method, route, body, headers)
72
+ login unless is_authorized?
73
+ response = {}
74
+
75
+ Retriable.retriable(on: Unauthorized, on_retry: Proc.new { logout }) do
76
+ Retriable.retriable(on: RateLimitExceeded, on_retry: RATE_LIMIT_WAITER) do
77
+ begin
78
+ response = @connection.run_request(
79
+ method,
80
+ route,
81
+ body,
82
+ { 'Authorization' => @token }.merge(headers)
83
+ )
84
+ rescue ::Faraday::Error::TimeoutError => e
85
+ raise Timeout, e.message, e.message, e.backtrace
86
+ end
87
+ end
90
88
  end
91
- rescue Faraday::Error::TimeoutError => _e
92
- raise AppnexusApi::Timeout, 'Timeout'
93
- ensure
94
- @retry = false
89
+
90
+ response
91
+ end
92
+
93
+ def is_authorized?
94
+ !@token.nil?
95
95
  end
96
- log.debug(response.body)
97
- response
98
- end
99
96
 
100
- def run_request_only(method, route, body, headers)
101
- @connection.run_request(
102
- method,
103
- route,
104
- body,
105
- { 'Authorization' => @token }.merge(headers)
106
- )
97
+ def logout
98
+ @token = nil
99
+ end
107
100
  end
108
101
  end
@@ -1,5 +1,7 @@
1
1
  module AppnexusApi
2
2
  class Error < StandardError; end
3
+
4
+ class BadChecksumException < Error; end
3
5
  class BadRequest < Error; end
4
6
  class Unauthorized < Error; end
5
7
  class Forbidden < Error; end
@@ -12,4 +14,5 @@ module AppnexusApi
12
14
  class ServiceUnavailable < Error; end
13
15
  class InvalidJson < Error; end
14
16
  class Timeout < Error; end
17
+ class RateLimitExceeded < Error; end
15
18
  end
@@ -2,6 +2,10 @@ module AppnexusApi
2
2
  module Faraday
3
3
  module Response
4
4
  class RaiseHttpError < ::Faraday::Response::Middleware
5
+ # Inexplicably, sandbox uses the correct code of 429, while production uses 405? so
6
+ # we just rely on the error message
7
+ RATE_EXCEEDED_ERROR = 'RATE_EXCEEDED'.freeze
8
+
5
9
  def on_complete(response)
6
10
  case response[:status].to_i
7
11
  when 400
@@ -25,11 +29,21 @@ module AppnexusApi
25
29
  when 503
26
30
  raise AppnexusApi::ServiceUnavailable, error_message(response)
27
31
  end
32
+
33
+ return if response.body.empty?
34
+ body = JSON.parse(response.body).fetch('response', {})
35
+
36
+ if body['error_code'] == RATE_EXCEEDED_ERROR
37
+ raise AppnexusApi::RateLimitExceeded, "Retry after #{response.response_headers['retry-after']}s"
38
+ elsif body['error_id'] && !body['error_id'].empty?
39
+ # TODO: this should raise an error, but a lot of the specs currently have error responses
40
+ # raise AppnexusApi::Error, "#{body['error_id']}: #{body['error']} - #{body['error_description']}"
41
+ end
28
42
  end
29
43
 
30
44
  def error_message(response)
31
45
  msg = "#{response[:method].to_s.upcase} #{response[:url].to_s}: #{response[:status]}"
32
- if errors = response[:body] && response[:body]["errors"]
46
+ if (errors = response[:body]) && response[:body]['errors']
33
47
  msg << "\n"
34
48
  msg << errors.join("\n")
35
49
  end