appnexusapi 0.0.9 → 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7969421f4404cb5d0af083544387927a493d2ccb
4
- data.tar.gz: fdf085dd9a11e7ca9877300f25d271e7e74a4a3d
3
+ metadata.gz: d0d8a696916d00cb33332f0c9f959536f321838b
4
+ data.tar.gz: b6bc7bc92daf371d2f9c6141ee9f174d3f9a35f2
5
5
  SHA512:
6
- metadata.gz: c2f9db3645266be119063ef0bb5c3944ebab0cb036b74c78c99fc9e5abfc4ad105b7a8d0ade59c80bc95ed9c18619305f383ee9034a98ff95373d75e111f4c8e
7
- data.tar.gz: e786a842d258682287651a46d8fcb32f00f41519d7cf53c1ab9dbf4b4f173778409c0cebc22686da3937b12808b4d3d860ef7106a9c63543017e2b2128ec2a72
6
+ metadata.gz: d3c91ef186f3236070d03cbec6780cae75a7b5a8d7631da21fc4187411e3039fe4364aca15e5b6f5f3e6548896b58e95975d3df69339b45dc8f64ab8d3bbf3b2
7
+ data.tar.gz: 310fc055c9f7f0e6ff8c4a2094070bcc03b19f3b4be789078b25877237cf3f24484f745e9b74787092aa4929a8936813f7ed83b8ac99297c2dcaebfae1805e4f
data/.gitignore CHANGED
@@ -16,3 +16,5 @@ spec/reports
16
16
  test/tmp
17
17
  test/version_tmp
18
18
  tmp
19
+ .env
20
+ log
data/README.md CHANGED
@@ -22,11 +22,14 @@ Establish a connection:
22
22
 
23
23
  connection = AppnexusApi::Connection.new(
24
24
  'username' => 'username',
25
- 'password' => 'password'
25
+ 'password' => 'password',
26
+ 'logger' => Logger.new(STDOUT) # defaults to a null logger if no value passed in.
26
27
 
27
28
  # Defaults to connecting to https://api.appnexus.com/ but you can optionally pass a uri to
28
29
  # connect to another endpoint, e.g. the staging site could be
29
- # uri: 'http://api.sand-08.adnxs.net'
30
+ # uri: 'http://api.sand-08.adnxs.net
31
+
32
+
30
33
  )
31
34
 
32
35
  Use a Service:
@@ -40,23 +43,18 @@ Use a Service:
40
43
  creative_service = AppnexusApi::CreativeService.new(connection, member.id)
41
44
 
42
45
  new_creative = {
43
- "media_url" => "http://ad.doubleclick.net/adi/ABC.Advertising.com/DEF.40;sz=300x250;click0=",
46
+ "content" => "<iframe src='helloword.html'></iframe>",
44
47
  "width" => "300",
45
48
  "height" => "250",
46
- "format" => "url-html"
49
+ "template" =>{ "id" => 7 }
47
50
  }
48
51
  creative = creative_service.create(new_creative)
49
52
  creative.update("campaign" => "Testing")
50
53
 
51
- ## Debugging
54
+ ## Testing
52
55
 
53
- The APPNEXUS_API_DEBUG environment variable will trigger full printouts of Faraday's debug output to STDERR.
56
+ There is a rudimentary test suite that centers around creatives/creative_service. To use it, you'll need to copy the `env_example` file to `.env` and replace the values with your correct values for your account. After that, a simple `bundle exec rspec spec` will run the test suite
54
57
 
55
- ```bash
56
- cd /my/app
57
- export APPNEXUS_API_DEBUG=true
58
- bundle exec rails whatever
59
- ```
60
58
 
61
59
  ## Contributing
62
60
 
data/appnexusapi.gemspec CHANGED
@@ -19,6 +19,10 @@ Gem::Specification.new do |gem|
19
19
  gem.add_dependency 'faraday_middleware'
20
20
  gem.add_dependency 'multi_json'
21
21
  gem.add_dependency 'pester'
22
-
22
+ gem.add_dependency 'null_logger'
23
+
23
24
  gem.add_development_dependency 'bundler', '>= 1.2.0'
25
+ gem.add_development_dependency 'rspec'
26
+ gem.add_development_dependency 'dotenv'
27
+ gem.add_development_dependency 'pry'
24
28
  end
data/env_example ADDED
@@ -0,0 +1,5 @@
1
+ APPNEXUS_MEMBER_ID=123
2
+ APPNEXUS_USERNAME=appnexus_user
3
+ APPNEXUS_PASSWORD=HGsgwDdxDVWM
4
+ APPNEXUS_URI=https://sand.api.adnxs.com
5
+
@@ -2,14 +2,15 @@ require 'faraday_middleware'
2
2
  require 'appnexusapi/faraday/raise_http_error'
3
3
 
4
4
  class AppnexusApi::Connection
5
+ RATE_EXCEEDED_DEFAULT_TIMEOUT = 15
6
+ RATE_EXCEEDED_HTTP_CODE = 429
7
+
5
8
  def initialize(config)
6
9
  @config = config
7
10
  @config['uri'] ||= 'https://api.appnexus.com/'
11
+ @logger = @config['logger'] || NullLogger.new
8
12
  @connection = Faraday.new(@config['uri']) do |conn|
9
- if ENV['APPNEXUS_API_DEBUG'].to_s =~ /^(true|1)$/i
10
- conn.response :logger, Logger.new(STDERR), bodies: true
11
- end
12
-
13
+ conn.response :logger, @logger, bodies: true
13
14
  conn.request :json
14
15
  conn.response :json, :content_type => /\bjson$/
15
16
  conn.use AppnexusApi::Faraday::Response::RaiseHttpError
@@ -21,8 +22,13 @@ class AppnexusApi::Connection
21
22
  !@token.nil?
22
23
  end
23
24
 
25
+ def log
26
+ @logger
27
+ end
28
+
24
29
  def login
25
30
  response = @connection.run_request(:post, 'auth', { 'auth' => { 'username' => @config['username'], 'password' => @config['password'] } }, {})
31
+ log.debug(response.body)
26
32
  if response.body['response']['error_code']
27
33
  fail "#{response.body['response']['error_code']}/#{response.body['response']['error_description']}"
28
34
  end
@@ -52,8 +58,20 @@ class AppnexusApi::Connection
52
58
 
53
59
  def run_request(method, route, body, headers)
54
60
  login if !is_authorized?
61
+ response = {}
55
62
  begin
56
- @connection.run_request(method, route, body, { 'Authorization' => @token }.merge(headers))
63
+ loop do
64
+ response = @connection.run_request(
65
+ method,
66
+ route,
67
+ body,
68
+ { 'Authorization' => @token }.merge(headers)
69
+ )
70
+ break unless response.status == RATE_EXCEEDED_HTTP_CODE
71
+ wait_time = response.headers['retry-after'] || RATE_EXCEEDED_DEFAULT_TIMEOUT
72
+ log.info("received rate exceeded. wait time: #{wait_time}s")
73
+ sleep wait_time.to_i
74
+ end
57
75
  rescue AppnexusApi::Unauthorized => e
58
76
  if @retry == true
59
77
  raise AppnexusApi::Unauthorized, e
@@ -62,10 +80,12 @@ class AppnexusApi::Connection
62
80
  logout
63
81
  run_request(method, route, body, headers)
64
82
  end
65
- rescue Faraday::Error::TimeoutError => e
83
+ rescue Faraday::Error::TimeoutError => _e
66
84
  raise AppnexusApi::Timeout, 'Timeout'
67
85
  ensure
68
86
  @retry = false
69
87
  end
88
+ log.debug(response.body)
89
+ response
70
90
  end
71
91
  end
@@ -1,8 +1,11 @@
1
1
  class AppnexusApi::Resource
2
2
 
3
- def initialize(json, service)
3
+ attr_reader :dbg_info
4
+
5
+ def initialize(json, service, dbg_info = nil)
4
6
  @json = json
5
7
  @service = service
8
+ @dbg_info = dbg_info
6
9
  end
7
10
 
8
11
  def update(attributes={})
@@ -44,14 +44,8 @@ class AppnexusApi::Service
44
44
  response = @connection.get(uri_suffix, params).body['response']
45
45
  if return_response
46
46
  response
47
- elsif response.has_key?(plural_name) || response.has_key?(plural_uri_name)
48
- key = response.has_key?(plural_name) ? plural_name : plural_uri_name
49
- response[key].map do |json|
50
- resource_class.new(json, self)
51
- end
52
- elsif response.has_key?(name) || response.has_key?(uri_name)
53
- key = response.has_key?(name) ? name : uri_name
54
- [resource_class.new(response[key], self)]
47
+ else
48
+ parse_response(response)
55
49
  end
56
50
  end
57
51
 
@@ -62,7 +56,6 @@ class AppnexusApi::Service
62
56
  while last_responses.size > 0
63
57
  responses += last_responses
64
58
  last_responses = get(params.merge('start_element' => responses.size))
65
- sleep(1) # The gem has no error handling at all for rate limit errors; sleeping for a second prevents errors
66
59
  end
67
60
 
68
61
  responses
@@ -76,7 +69,7 @@ class AppnexusApi::Service
76
69
  response.delete('dbg')
77
70
  raise AppnexusApi::BadRequest.new(response.inspect)
78
71
  end
79
- get("id" => response["id"]).first
72
+ parse_response(response).first
80
73
  end
81
74
 
82
75
  def update(id, attributes={})
@@ -87,7 +80,7 @@ class AppnexusApi::Service
87
80
  response.delete('dbg')
88
81
  raise AppnexusApi::BadRequest.new(response.inspect)
89
82
  end
90
- get("id" => response["id"]).first
83
+ parse_response(response).first
91
84
  end
92
85
 
93
86
  def delete(id)
@@ -95,4 +88,19 @@ class AppnexusApi::Service
95
88
  @connection.delete([uri_suffix, id].join('/')).body['response']
96
89
  end
97
90
 
91
+ def parse_response(response)
92
+ case key = resource_name(response)
93
+ when plural_name, plural_uri_name
94
+ response[key].map do |json|
95
+ resource_class.new(json, self, response['dbg'])
96
+ end
97
+ when name, uri_name
98
+ [resource_class.new(response[key], self, response['dbg'])]
99
+ end
100
+ end
101
+
102
+ def resource_name(response)
103
+ [plural_name, plural_uri_name, name, uri_name].detect { |n| response.key?(n) }
104
+ end
105
+
98
106
  end
@@ -1,3 +1,3 @@
1
1
  module AppnexusApi
2
- VERSION = "0.0.9"
2
+ VERSION = '0.1.0'.freeze
3
3
  end
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ describe AppnexusApi::CreativeService do
4
+ let(:creative_service) do
5
+ AppnexusApi::CreativeService.new(connection, ENV['APPNEXUS_MEMBER_ID'])
6
+ end
7
+ let(:new_creative) do
8
+ {
9
+ 'campaign' => 'default campaign',
10
+ 'content' => '<iframe src="helloword.html"></iframe>',
11
+ 'width' => '300',
12
+ 'height' => '250',
13
+ 'template' => { 'id' => 7 }
14
+ }
15
+ end
16
+
17
+ it 'respects the throttle limit', slow: true do
18
+ # this spec will attempt to make 100 writes as fast as possible with 10
19
+ # threads running in a loop of 10. Typically you hit the limit right
20
+ # around 25 seconds in. If your connection is slow, this
21
+ # test may never hit the throttle limit
22
+ expect do
23
+ # this runs so we only authenticate once instead of 10 times.
24
+ _p = creative_service.get('num_elements' => 1, 'start_element' => 0)
25
+ 10.times do
26
+ threads = []
27
+ 10.times do
28
+ threads << Thread.new do
29
+ creative = creative_service.create(new_creative)
30
+ puts creative.dbg_info
31
+ end
32
+ end
33
+ threads.map(&:join)
34
+ end
35
+ end.to_not raise_error
36
+ end
37
+
38
+ it 'supports a get operation' do
39
+ expect do
40
+ creative_service.get('start_element' => 0, 'num_elements' => 1)
41
+ end.to_not raise_error
42
+ end
43
+
44
+ context 'creating a new creative' do
45
+ it 'supports creating a new creative' do
46
+ creative = creative_service.create(new_creative)
47
+ expect(creative.width).to eq 300
48
+ expect(creative.height).to eq 250
49
+ end
50
+ end
51
+
52
+ context 'an existing creative' do
53
+ let(:existing_creative) { creative_service.create(new_creative) }
54
+
55
+ it 'supports changing attributes with the update action' do
56
+ expect(existing_creative.campaign).to eq 'default campaign'
57
+ existing_creative.update('campaign' => 'My Best Campaign Yet')
58
+ expect(existing_creative.campaign).to eq 'My Best Campaign Yet'
59
+ end
60
+
61
+ it 'supports removing the creative' do
62
+ id = existing_creative.id
63
+ creative = creative_service.get('id' => id).first
64
+ expect(creative.id).to eq id
65
+ existing_creative.delete
66
+ creative = creative_service.get('id' => id)
67
+ expect(creative).to be_nil
68
+ end
69
+ end
70
+ end
71
+
@@ -0,0 +1,28 @@
1
+ require 'dotenv'
2
+ Dotenv.load
3
+
4
+ Bundler.require
5
+ require 'pry'
6
+ require 'logger'
7
+ require_relative '../lib/appnexusapi'
8
+
9
+ RSpec.configure do |c|
10
+ c.filter_run_excluding :slow => true
11
+ end
12
+
13
+ def test_logger
14
+ return @logger unless @logger.nil?
15
+ FileUtils.mkdir_p('./log')
16
+ @logger = Logger.new('./log/test.log')
17
+ @logger.level = Logger::INFO
18
+ @logger
19
+ end
20
+
21
+ def connection
22
+ AppnexusApi::Connection.new(
23
+ 'username' => ENV['APPNEXUS_USERNAME'],
24
+ 'password' => ENV['APPNEXUS_PASSWORD'],
25
+ 'uri' => ENV['APPNEXUS_URI'],
26
+ 'logger' => test_logger
27
+ )
28
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appnexusapi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brandon Aaron
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-07-06 00:00:00.000000000 Z
11
+ date: 2016-08-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: null_logger
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'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: bundler
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +94,48 @@ dependencies:
80
94
  - - ">="
81
95
  - !ruby/object:Gem::Version
82
96
  version: 1.2.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: dotenv
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: pry
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
83
139
  description: ''
84
140
  email:
85
141
  - brandon.aaron@gmail.com
@@ -93,6 +149,7 @@ files:
93
149
  - README.md
94
150
  - Rakefile
95
151
  - appnexusapi.gemspec
152
+ - env_example
96
153
  - lib/appnexusapi.rb
97
154
  - lib/appnexusapi/ad_server_resource.rb
98
155
  - lib/appnexusapi/ad_server_service.rb
@@ -153,6 +210,8 @@ files:
153
210
  - lib/appnexusapi/user_resource.rb
154
211
  - lib/appnexusapi/user_service.rb
155
212
  - lib/appnexusapi/version.rb
213
+ - spec/creative_service_spec.rb
214
+ - spec/spec_helper.rb
156
215
  homepage: http://simpli.fi
157
216
  licenses: []
158
217
  metadata: {}
@@ -172,9 +231,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
172
231
  version: '0'
173
232
  requirements: []
174
233
  rubyforge_project:
175
- rubygems_version: 2.2.3
234
+ rubygems_version: 2.2.5
176
235
  signing_key:
177
236
  specification_version: 4
178
237
  summary: Unofficial Ruby API Wrapper for Appnexus
179
- test_files: []
180
- has_rdoc:
238
+ test_files:
239
+ - spec/creative_service_spec.rb
240
+ - spec/spec_helper.rb