appnexusapi 0.0.9 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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