appnexusapi 0.1.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +33 -4
- data/UPGRADING.md +10 -0
- data/appnexusapi.gemspec +2 -0
- data/env_example +1 -3
- data/lib/appnexusapi/advertiser_resource.rb +2 -0
- data/lib/appnexusapi/advertiser_service.rb +2 -0
- data/lib/appnexusapi/campaign_resource.rb +2 -0
- data/lib/appnexusapi/campaign_service.rb +2 -0
- data/lib/appnexusapi/connection.rb +19 -5
- data/lib/appnexusapi/content_category_service.rb +3 -1
- data/lib/appnexusapi/creative_service.rb +0 -10
- data/lib/appnexusapi/payment_rule_resource.rb +2 -0
- data/lib/appnexusapi/payment_rule_service.rb +2 -0
- data/lib/appnexusapi/placement_resource.rb +2 -0
- data/lib/appnexusapi/placement_service.rb +2 -0
- data/lib/appnexusapi/profile_resource.rb +2 -0
- data/lib/appnexusapi/profile_service.rb +2 -0
- data/lib/appnexusapi/publisher_resource.rb +2 -0
- data/lib/appnexusapi/publisher_service.rb +2 -0
- data/lib/appnexusapi/resource.rb +5 -4
- data/lib/appnexusapi/service.rb +16 -8
- data/lib/appnexusapi/site_resource.rb +2 -0
- data/lib/appnexusapi/site_service.rb +2 -0
- data/lib/appnexusapi/version.rb +1 -1
- data/spec/connection_spec.rb +13 -3
- data/spec/creative_service_spec.rb +33 -22
- data/spec/fixtures/vcr/advertiser_get.yml +108 -0
- data/spec/fixtures/vcr/campaign_life_cycle.yml +524 -0
- data/spec/fixtures/vcr/content_category_crud.yml +398 -0
- data/spec/fixtures/vcr/creative_service_create.yml +216 -0
- data/spec/fixtures/vcr/creative_service_delete.yml +354 -0
- data/spec/fixtures/vcr/creative_service_get.yml +214 -0
- data/spec/fixtures/vcr/creative_service_update.yml +266 -0
- data/spec/fixtures/vcr/domain_list_limits.yml +105 -0
- data/spec/fixtures/vcr/line_item_life_cycle.yml +521 -0
- data/spec/fixtures/vcr/member_get.yml +150 -0
- data/spec/fixtures/vcr/object_limit_info.yml +105 -0
- data/spec/fixtures/vcr/payment_rule_lifecycle.yml +346 -0
- data/spec/fixtures/vcr/placement_service_default_placement.yml +166 -0
- data/spec/fixtures/vcr/profile_life_cycle.yml +349 -0
- data/spec/fixtures/vcr/profile_limits.yml +105 -0
- data/spec/fixtures/vcr/publisher_crud.yml +282 -0
- data/spec/fixtures/vcr/site_lifecycle.yml +284 -0
- data/spec/fixtures/vcr/user_login.yml +107 -0
- data/spec/fixtures/vcr/user_update.yml +288 -0
- data/spec/integration/advertiser_spec.rb +11 -0
- data/spec/integration/campaign_spec.rb +38 -0
- data/spec/integration/content_category_spec.rb +22 -0
- data/spec/integration/line_item_spec.rb +27 -0
- data/spec/integration/member_spec.rb +9 -0
- data/spec/integration/payment_rule_spec.rb +33 -0
- data/spec/integration/placement_spec.rb +29 -0
- data/spec/integration/profile_spec.rb +29 -0
- data/spec/integration/publisher_spec.rb +20 -0
- data/spec/integration/site_spec.rb +32 -0
- data/spec/integration/user_spec.rb +42 -0
- data/spec/object_limit_service_spec.rb +14 -11
- data/spec/spec_helper.rb +52 -25
- data/spec/support/shared_contexts.rb +28 -0
- metadata +109 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0184db6bca5286e5ec3ac206cf0d964ce78484e0
|
4
|
+
data.tar.gz: dec7fe42720c2c0b89dcaa55bbb142bfce2721b5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fb39def781471b2b91f8803116d0c71d734dca49ddaca2fec95e6d104b1f7e21c61aa9c0d55fa11300a69d6d95e7e961a7479dc34ef319a8791e9032483f5aa4
|
7
|
+
data.tar.gz: 6932d3ac84d2f075c5cb71c4d5a97a0a618ed2af94b7f41091f2776dff59c82099527a8ac6b2c319d9577c61f6725b1f7a322e115f24fbd1e3b76686dfe49535
|
data/README.md
CHANGED
@@ -27,9 +27,9 @@ Establish a connection:
|
|
27
27
|
|
28
28
|
# Defaults to connecting to https://api.appnexus.com/ but you can optionally pass a uri to
|
29
29
|
# connect to another endpoint, e.g. the staging site could be
|
30
|
-
# uri
|
30
|
+
# "uri" => 'http://api-test.appnexus.com',
|
31
|
+
|
31
32
|
|
32
|
-
|
33
33
|
)
|
34
34
|
|
35
35
|
Use a Service:
|
@@ -40,7 +40,27 @@ Use a Service:
|
|
40
40
|
# and returns an AppnexusApi::Resource object which is a wrapper around the JSON
|
41
41
|
member = member_service.get.first
|
42
42
|
|
43
|
-
|
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
|
46
|
+
|
47
|
+
# create a new object
|
48
|
+
url_params = { advertiser_id: 12345 }
|
49
|
+
body_params = { name: "some line item", code: "line item code"}
|
50
|
+
|
51
|
+
line_item = line_item_service.create(url_params, body_params)
|
52
|
+
line_item.state
|
53
|
+
|
54
|
+
|
55
|
+
# update an object
|
56
|
+
update_params = { state: "inactive" }
|
57
|
+
json_result = line_item.update(url_params, update_params)
|
58
|
+
|
59
|
+
# delete an object
|
60
|
+
line_item.delete(url_params)
|
61
|
+
|
62
|
+
# this raises an AppnexusApi::UnprocessableEntity, not a 404 as it should
|
63
|
+
line_item_service.get(line_item.id)
|
44
64
|
|
45
65
|
new_creative = {
|
46
66
|
"content" => "<iframe src='helloword.html'></iframe>",
|
@@ -53,13 +73,22 @@ Use a Service:
|
|
53
73
|
|
54
74
|
## Testing
|
55
75
|
|
56
|
-
|
76
|
+
### Running Existing Specs
|
77
|
+
```
|
78
|
+
bundle exec rspec
|
79
|
+
```
|
80
|
+
|
81
|
+
### Writing New Specs or Updating Old Ones
|
82
|
+
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
|
+
|
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.
|
57
85
|
|
58
86
|
|
59
87
|
## Contributing
|
60
88
|
|
61
89
|
1. Fork it
|
62
90
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
91
|
+
3. Make changes (with tests -- at least integration tests, please)
|
63
92
|
3. Commit your changes (`git commit -am 'Added some feature'`)
|
64
93
|
4. Push to the branch (`git push origin my-new-feature`)
|
65
94
|
5. Create new Pull Request
|
data/UPGRADING.md
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
Upgrading from 0.1.x or earlier
|
2
|
+
|
3
|
+
- create/update/delete methods now take an additional argument to pass, e.g.
|
4
|
+
"?publisher_id=5" where appropriate and required. This goes before the
|
5
|
+
"body" hash.
|
6
|
+
|
7
|
+
|
8
|
+
the implementation should probably read advertiser_id/publisher_id from
|
9
|
+
the object for update/delete but in the cases that both are present, I'm
|
10
|
+
unsure which one (or both?) should be sent
|
data/appnexusapi.gemspec
CHANGED
data/env_example
CHANGED
@@ -6,7 +6,7 @@ class AppnexusApi::Connection
|
|
6
6
|
RATE_EXCEEDED_DEFAULT_TIMEOUT = 15
|
7
7
|
# Inexplicably, sandbox uses the correct code of 429, while production uses 405? so
|
8
8
|
# we just rely on the error message
|
9
|
-
RATE_EXCEEDED_ERROR =
|
9
|
+
RATE_EXCEEDED_ERROR = 'RATE_EXCEEDED'.freeze
|
10
10
|
|
11
11
|
def initialize(config)
|
12
12
|
@config = config
|
@@ -44,7 +44,11 @@ class AppnexusApi::Connection
|
|
44
44
|
|
45
45
|
def get(route, params={}, headers={})
|
46
46
|
params = params.delete_if {|key, value| value.nil? }
|
47
|
-
run_request(:get,
|
47
|
+
run_request(:get, build_url(route, params), nil, headers)
|
48
|
+
end
|
49
|
+
|
50
|
+
def build_url(route, params)
|
51
|
+
@connection.build_url(route, params)
|
48
52
|
end
|
49
53
|
|
50
54
|
def post(route, body=nil, headers={})
|
@@ -60,16 +64,17 @@ class AppnexusApi::Connection
|
|
60
64
|
end
|
61
65
|
|
62
66
|
def run_request(method, route, body, headers)
|
63
|
-
login
|
67
|
+
login unless is_authorized?
|
64
68
|
response = {}
|
65
69
|
begin
|
66
70
|
loop do
|
67
|
-
response =
|
71
|
+
response = run_request_only(
|
68
72
|
method,
|
69
73
|
route,
|
70
74
|
body,
|
71
75
|
{ 'Authorization' => @token }.merge(headers)
|
72
76
|
)
|
77
|
+
break if response.body.empty? # Log level data download service returns a body of ""
|
73
78
|
break unless response.body.fetch('response', {})['error_code'] == RATE_EXCEEDED_ERROR
|
74
79
|
wait_time = response.headers['retry-after'] || RATE_EXCEEDED_DEFAULT_TIMEOUT
|
75
80
|
log.info("received rate exceeded. wait time: #{wait_time}s")
|
@@ -81,7 +86,7 @@ class AppnexusApi::Connection
|
|
81
86
|
else
|
82
87
|
@retry = true
|
83
88
|
logout
|
84
|
-
run_request(method, route, body, headers)
|
89
|
+
response = run_request(method, route, body, headers)
|
85
90
|
end
|
86
91
|
rescue Faraday::Error::TimeoutError => _e
|
87
92
|
raise AppnexusApi::Timeout, 'Timeout'
|
@@ -91,4 +96,13 @@ class AppnexusApi::Connection
|
|
91
96
|
log.debug(response.body)
|
92
97
|
response
|
93
98
|
end
|
99
|
+
|
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
|
+
)
|
107
|
+
end
|
94
108
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
class AppnexusApi::ContentCategoryService < AppnexusApi::Service
|
2
2
|
|
3
3
|
def initialize(connection)
|
4
|
-
@read_only = true
|
5
4
|
super(connection)
|
6
5
|
end
|
7
6
|
|
@@ -9,4 +8,7 @@ class AppnexusApi::ContentCategoryService < AppnexusApi::Service
|
|
9
8
|
"content_categories"
|
10
9
|
end
|
11
10
|
|
11
|
+
def plural_uri_name
|
12
|
+
"content-categories"
|
13
|
+
end
|
12
14
|
end
|
data/lib/appnexusapi/resource.rb
CHANGED
@@ -8,13 +8,14 @@ class AppnexusApi::Resource
|
|
8
8
|
@dbg_info = dbg_info
|
9
9
|
end
|
10
10
|
|
11
|
-
def update(
|
12
|
-
resource = @service.update(id,
|
11
|
+
def update(route_params={}, body_params={})
|
12
|
+
resource = @service.update(id, route_params, body_params)
|
13
13
|
@json = resource.raw_json
|
14
|
+
self
|
14
15
|
end
|
15
16
|
|
16
|
-
def delete
|
17
|
-
@service.delete(id)
|
17
|
+
def delete(route_params={})
|
18
|
+
@service.delete(id, route_params)
|
18
19
|
end
|
19
20
|
|
20
21
|
def raw_json
|
data/lib/appnexusapi/service.rb
CHANGED
@@ -61,10 +61,11 @@ class AppnexusApi::Service
|
|
61
61
|
responses
|
62
62
|
end
|
63
63
|
|
64
|
-
def create(
|
64
|
+
def create(route_params={}, body={})
|
65
65
|
raise(AppnexusApi::NotImplemented, "Service is read-only.") if @read_only
|
66
|
-
|
67
|
-
|
66
|
+
body = { uri_name => body }
|
67
|
+
route = @connection.build_url(uri_suffix, route_params)
|
68
|
+
response = @connection.post(route, body).body['response']
|
68
69
|
if response['error_id']
|
69
70
|
response.delete('dbg')
|
70
71
|
raise AppnexusApi::BadRequest.new(response.inspect)
|
@@ -72,10 +73,11 @@ class AppnexusApi::Service
|
|
72
73
|
parse_response(response).first
|
73
74
|
end
|
74
75
|
|
75
|
-
def update(id,
|
76
|
+
def update(id, route_params={}, body={})
|
76
77
|
raise(AppnexusApi::NotImplemented, "Service is read-only.") if @read_only
|
77
|
-
|
78
|
-
|
78
|
+
body = { uri_name => body }
|
79
|
+
route = @connection.build_url(uri_suffix, route_params.merge("id" => id))
|
80
|
+
response = @connection.put(route, body).body['response']
|
79
81
|
if response['error_id']
|
80
82
|
response.delete('dbg')
|
81
83
|
raise AppnexusApi::BadRequest.new(response.inspect)
|
@@ -83,9 +85,15 @@ class AppnexusApi::Service
|
|
83
85
|
parse_response(response).first
|
84
86
|
end
|
85
87
|
|
86
|
-
def delete(id)
|
88
|
+
def delete(id, route_params)
|
87
89
|
raise(AppnexusApi::NotImplemented, "Service is read-only.") if @read_only
|
88
|
-
@connection.
|
90
|
+
route = @connection.build_url(uri_suffix, route_params.merge("id" => id))
|
91
|
+
response = @connection.delete(route).body['response']
|
92
|
+
if response['error_id']
|
93
|
+
response.delete('dbg')
|
94
|
+
raise AppnexusApi::BadRequest.new(response.inspect)
|
95
|
+
end
|
96
|
+
response
|
89
97
|
end
|
90
98
|
|
91
99
|
def parse_response(response)
|
data/lib/appnexusapi/version.rb
CHANGED
data/spec/connection_spec.rb
CHANGED
@@ -1,11 +1,21 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe AppnexusApi::Connection do
|
4
|
+
subject { AppnexusApi::Connection.new({}) }
|
4
5
|
|
5
6
|
it 'allows no logger to be specified' do
|
6
|
-
expect
|
7
|
-
AppnexusApi::CreativeService.new(connection_with_null_logger, ENV['APPNEXUS_MEMBER_ID'])
|
8
|
-
end.to_not raise_error
|
7
|
+
expect { AppnexusApi::CreativeService.new(connection_with_null_logger) }.to_not raise_error
|
9
8
|
end
|
10
9
|
|
10
|
+
it 'returns data from expiration' do
|
11
|
+
#stub to raise error the first time and then return []
|
12
|
+
counter = 0
|
13
|
+
allow(subject).to receive(:login)
|
14
|
+
allow(subject).to receive(:run_request_only) do |arg|
|
15
|
+
counter += 1
|
16
|
+
raise AppnexusApi::Unauthorized.new if counter == 1
|
17
|
+
Faraday::Response.new(body: { not_an_error: 1 })
|
18
|
+
end
|
19
|
+
expect(subject.run_request(:get, 'http://localhost', nil, {})).not_to eq({})
|
20
|
+
end
|
11
21
|
end
|
@@ -1,12 +1,12 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe AppnexusApi::CreativeService do
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
include_context 'with an advertiser'
|
5
|
+
|
6
|
+
let(:creative_service) { described_class.new(connection) }
|
7
7
|
let(:new_creative) do
|
8
8
|
{
|
9
|
-
'
|
9
|
+
'name' => 'rspec test creative',
|
10
10
|
'content' => '<iframe src="helloword.html"></iframe>',
|
11
11
|
'width' => '300',
|
12
12
|
'height' => '250',
|
@@ -26,46 +26,57 @@ describe AppnexusApi::CreativeService do
|
|
26
26
|
threads = []
|
27
27
|
10.times do
|
28
28
|
threads << Thread.new do
|
29
|
-
creative = creative_service.create(new_creative)
|
29
|
+
creative = creative_service.create(advertiser_url_params, new_creative)
|
30
30
|
puts creative.dbg_info
|
31
31
|
end
|
32
32
|
end
|
33
33
|
threads.map(&:join)
|
34
34
|
end
|
35
35
|
end.to_not raise_error
|
36
|
+
|
37
|
+
advertiser.delete
|
36
38
|
end
|
37
39
|
|
38
40
|
it 'supports a get operation' do
|
39
|
-
|
40
|
-
creative_service.get('start_element' => 0, 'num_elements' => 1)
|
41
|
-
|
41
|
+
VCR.use_cassette('creative_service_get') do
|
42
|
+
expect { creative_service.get('start_element' => 0, 'num_elements' => 1) }.to_not raise_error
|
43
|
+
advertiser.delete
|
44
|
+
end
|
42
45
|
end
|
43
46
|
|
44
47
|
context 'creating a new creative' do
|
45
48
|
it 'supports creating a new creative' do
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
+
VCR.use_cassette('creative_service_create') do
|
50
|
+
creative = creative_service.create(advertiser_url_params, new_creative)
|
51
|
+
expect(creative.width).to eq(300)
|
52
|
+
expect(creative.height).to eq(250)
|
53
|
+
advertiser.delete
|
54
|
+
end
|
49
55
|
end
|
50
56
|
end
|
51
57
|
|
52
58
|
context 'an existing creative' do
|
53
|
-
let(:existing_creative) { creative_service.create(new_creative) }
|
59
|
+
let(:existing_creative) { creative_service.create(advertiser_url_params, new_creative) }
|
54
60
|
|
55
61
|
it 'supports changing attributes with the update action' do
|
56
|
-
|
57
|
-
|
58
|
-
|
62
|
+
VCR.use_cassette('creative_service_update') do
|
63
|
+
expect(existing_creative.code).to be_nil
|
64
|
+
existing_creative.update(advertiser_url_params, code: "abc")
|
65
|
+
expect(existing_creative.code).to eq "abc"
|
66
|
+
advertiser.delete
|
67
|
+
end
|
59
68
|
end
|
60
69
|
|
61
70
|
it 'supports removing the creative' do
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
71
|
+
VCR.use_cassette('creative_service_delete') do
|
72
|
+
id = existing_creative.id
|
73
|
+
creative = creative_service.get('id' => id).first
|
74
|
+
expect(creative.id).to eq id
|
75
|
+
existing_creative.delete(advertiser_url_params)
|
76
|
+
creative = creative_service.get('id' => id)
|
77
|
+
expect(creative).to be_nil
|
78
|
+
advertiser.delete
|
79
|
+
end
|
68
80
|
end
|
69
81
|
end
|
70
82
|
end
|
71
|
-
|