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 +4 -4
- data/.gitignore +2 -0
- data/README.md +9 -11
- data/appnexusapi.gemspec +5 -1
- data/env_example +5 -0
- data/lib/appnexusapi/connection.rb +26 -6
- data/lib/appnexusapi/resource.rb +4 -1
- data/lib/appnexusapi/service.rb +19 -11
- data/lib/appnexusapi/version.rb +1 -1
- data/spec/creative_service_spec.rb +71 -0
- data/spec/spec_helper.rb +28 -0
- metadata +65 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d0d8a696916d00cb33332f0c9f959536f321838b
|
4
|
+
data.tar.gz: b6bc7bc92daf371d2f9c6141ee9f174d3f9a35f2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d3c91ef186f3236070d03cbec6780cae75a7b5a8d7631da21fc4187411e3039fe4364aca15e5b6f5f3e6548896b58e95975d3df69339b45dc8f64ab8d3bbf3b2
|
7
|
+
data.tar.gz: 310fc055c9f7f0e6ff8c4a2094070bcc03b19f3b4be789078b25877237cf3f24484f745e9b74787092aa4929a8936813f7ed83b8ac99297c2dcaebfae1805e4f
|
data/.gitignore
CHANGED
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
|
-
"
|
46
|
+
"content" => "<iframe src='helloword.html'></iframe>",
|
44
47
|
"width" => "300",
|
45
48
|
"height" => "250",
|
46
|
-
"
|
49
|
+
"template" =>{ "id" => 7 }
|
47
50
|
}
|
48
51
|
creative = creative_service.create(new_creative)
|
49
52
|
creative.update("campaign" => "Testing")
|
50
53
|
|
51
|
-
##
|
54
|
+
## Testing
|
52
55
|
|
53
|
-
|
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
@@ -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
|
-
|
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
|
-
|
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 =>
|
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
|
data/lib/appnexusapi/resource.rb
CHANGED
data/lib/appnexusapi/service.rb
CHANGED
@@ -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
|
-
|
48
|
-
|
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
|
-
|
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
|
-
|
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
|
data/lib/appnexusapi/version.rb
CHANGED
@@ -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
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -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
|
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-
|
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.
|
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
|
-
|
238
|
+
test_files:
|
239
|
+
- spec/creative_service_spec.rb
|
240
|
+
- spec/spec_helper.rb
|