adcloud 0.7.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.
Files changed (45) hide show
  1. data/.document +5 -0
  2. data/.gitignore +69 -0
  3. data/.travis.yml +4 -0
  4. data/Gemfile +4 -0
  5. data/Gemfile.lock +50 -0
  6. data/LICENSE.txt +20 -0
  7. data/README.md +221 -0
  8. data/RELEASE_NOTES.md +14 -0
  9. data/Rakefile +8 -0
  10. data/adcloud.gemspec +27 -0
  11. data/lib/adcloud.rb +62 -0
  12. data/lib/adcloud/advertisement.rb +28 -0
  13. data/lib/adcloud/api_error.rb +33 -0
  14. data/lib/adcloud/authentication.rb +24 -0
  15. data/lib/adcloud/campaign.rb +69 -0
  16. data/lib/adcloud/connection.rb +46 -0
  17. data/lib/adcloud/customer.rb +9 -0
  18. data/lib/adcloud/entity.rb +84 -0
  19. data/lib/adcloud/exception_raiser.rb +22 -0
  20. data/lib/adcloud/media_file.rb +10 -0
  21. data/lib/adcloud/product.rb +10 -0
  22. data/lib/adcloud/report.rb +28 -0
  23. data/lib/adcloud/report_entry.rb +48 -0
  24. data/lib/adcloud/response_error_handler.rb +23 -0
  25. data/lib/adcloud/topic.rb +19 -0
  26. data/lib/adcloud/topic_discount.rb +16 -0
  27. data/lib/adcloud/version.rb +3 -0
  28. data/lib/adcloud/webhook.rb +35 -0
  29. data/lib/adcloud/webhook_config.rb +18 -0
  30. data/lib/adcloud/webhook_event.rb +18 -0
  31. data/test/adcloud/advertisement_test.rb +11 -0
  32. data/test/adcloud/authentication_test.rb +58 -0
  33. data/test/adcloud/campaign_test.rb +54 -0
  34. data/test/adcloud/connection_test.rb +78 -0
  35. data/test/adcloud/customer_test.rb +6 -0
  36. data/test/adcloud/entity_test.rb +159 -0
  37. data/test/adcloud/media_file_test.rb +7 -0
  38. data/test/adcloud/product_test.rb +5 -0
  39. data/test/adcloud/report_test.rb +34 -0
  40. data/test/adcloud/topic_test.rb +14 -0
  41. data/test/adcloud/webhook_event_test.rb +19 -0
  42. data/test/adcloud/webhook_test.rb +62 -0
  43. data/test/adcloud_test.rb +47 -0
  44. data/test/test_helper.rb +23 -0
  45. metadata +251 -0
@@ -0,0 +1,19 @@
1
+ module Adcloud
2
+ class Topic < Entity
3
+ attribute :id, Integer
4
+ attribute :start_prio, Integer
5
+ attribute :modified, DateTime
6
+ attribute :created, DateTime
7
+ attribute :discounts, Hash
8
+ attribute :names, Hash
9
+
10
+ def discounts=(data)
11
+ @discounts = data.reduce({}) do |hash, raw_discount|
12
+ discount = TopicDiscount.new(raw_discount)
13
+ hash[discount.country_code] = discount
14
+ hash
15
+ end
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,16 @@
1
+ module Adcloud
2
+ class TopicDiscount
3
+ include Virtus
4
+
5
+ attribute :country_id, Integer
6
+ attribute :country_code, String
7
+ attribute :discount, Float
8
+ attribute :reach, Float
9
+ attribute :min_price_cpc, Float
10
+ attribute :price_cpc, Float
11
+ attribute :max_price_cpc, Float
12
+ attribute :min_price_cpm, Float
13
+ attribute :price_cpm, Float
14
+ attribute :max_price_cpm, Float
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ module Adcloud
2
+ VERSION = '0.7.0'
3
+ end
@@ -0,0 +1,35 @@
1
+ module Adcloud
2
+ class Webhook
3
+ ALL = [:on_topic_price_update, :on_campaign_update]
4
+
5
+ attr_accessor :events
6
+
7
+ # @param events [Array<Hash>, String] Events
8
+ def initialize(events)
9
+ self.events = events.kind_of?(Array) ? events : JSON.parse(events.to_s)
10
+ rescue
11
+ raise ArgumentError.new("Invalid webhook event data!")
12
+ end
13
+
14
+ def events=(events)
15
+ @events = [events].flatten.map { |event| Adcloud::WebhookEvent.new(event) }
16
+ if Adcloud.config.webhooks.filter_tests
17
+ @events.reject! { |event| event.test_data? }
18
+ end
19
+ end
20
+
21
+ def process!
22
+ self.events.each do |event|
23
+ proc = case event.type
24
+ when 'TopicPrice.update'
25
+ :on_topic_price_update
26
+ when 'Booking.update'
27
+ :on_campaign_update
28
+ else
29
+ :on_unknown_webhook
30
+ end
31
+ Adcloud.config.webhooks.send(proc).call(event)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,18 @@
1
+ module Adcloud
2
+ class WebhookConfig < ActiveSupport::Configurable::Configuration
3
+ def initialize
4
+ Adcloud::Webhook::ALL.each do |hook|
5
+ self.send("#{hook}=", WebhookConfig.method(:unused_webhook))
6
+ end
7
+ self.on_unknown_webhook = WebhookConfig.method(:unknown_webhook)
8
+ end
9
+
10
+ def self.unused_webhook(event)
11
+ Adcloud.logger.warn { "Webhook behaviour missing for #{event.inspect}" }
12
+ end
13
+
14
+ def self.unknown_webhook(event)
15
+ Adcloud.logger.warn { "Unknown webhook event #{event.inspect}. Please contact gem maintainer!" }
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ module Adcloud
2
+ class WebhookEvent
3
+ attr_reader :data, :meta
4
+
5
+ def initialize(raw_response)
6
+ @meta = raw_response.delete('_meta') || {}
7
+ @data = raw_response || {}
8
+ end
9
+
10
+ def type
11
+ @meta['event']
12
+ end
13
+
14
+ def test_data?
15
+ @meta.has_key?('is_test_data') && @meta['is_test_data']
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,11 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'test_helper'
3
+
4
+ describe Adcloud::Advertisement do
5
+
6
+ it 'should set the correct api endpoint' do
7
+ Adcloud::Advertisement.api_endpoint.must_equal 'advertisements'
8
+ end
9
+
10
+ # for more tests checkout entity tests
11
+ end
@@ -0,0 +1,58 @@
1
+ require 'test_helper'
2
+
3
+ describe Adcloud::Authentication do
4
+
5
+ describe "initialization" do
6
+
7
+ describe "with a valid hash" do
8
+ before do
9
+ @adcloud_auth = Adcloud::Authentication.new(:client_id => "0987654321", :client_secret => "1234567890")
10
+ end
11
+
12
+ it "should have a client_id 0987654321" do
13
+ @adcloud_auth.client_id.must_equal "0987654321"
14
+ end
15
+
16
+ it "should have a client_secret 1234567890" do
17
+ @adcloud_auth.client_secret.must_equal "1234567890"
18
+ end
19
+
20
+ end
21
+
22
+ describe "with an empty hash" do
23
+
24
+ before do
25
+ @adcloud_auth = Adcloud::Authentication.new({})
26
+ end
27
+
28
+ it "should have a client_id and client_secret being nil" do
29
+ @adcloud_auth.client_secret.must_be_nil
30
+ @adcloud_auth.client_id.must_be_nil
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+
37
+ describe "is successful" do
38
+
39
+ it "should return a token" do
40
+ stub_request(:post, "https://api.adcloud.com:443/v2/oauth/access_token").with(:body => {"client_id"=>"0987654321", "client_secret"=>"1234567890", "grant_type"=>"none"}, :headers => {'Accept'=>'*/*', 'Content-Type'=>'application/x-www-form-urlencoded', 'User-Agent'=>'Ruby'}).to_return(:status => 200, :body => {"_meta" => {"access_token" => "bab0e5c477f211c4612345678907498b6e55600"}, "access_token" => "bab0e5c477f211c4612345678907498b6e55600","scope" => ""}, :headers => {})
41
+ adcloud_auth = Adcloud::Authentication.new(:client_id => "0987654321", :client_secret => "1234567890")
42
+ adcloud_auth.authenticate!
43
+ adcloud_auth.token.must_equal "bab0e5c477f211c4612345678907498b6e55600"
44
+ end
45
+
46
+ end
47
+
48
+ # describe "is unsuccessful" do
49
+
50
+ # it "should raise an authentication error" do
51
+ # stub_request(:post, "https://api.adcloud.com:443/v2/oauth/access_token").with(:body => {"client_id"=>"0987654321", "client_secret"=>"1234567890", "grant_type"=>"none"}, :headers => {'Accept'=>'*/*', 'Content-Type'=>'application/x-www-form-urlencoded', 'User-Agent'=>'Ruby'}).to_return(:status => 401, :body => {"status" => 401, "errors" => [{"message" => "Bad credentials"}]}, :headers => {})
52
+ # adcloud_auth = Adcloud::Authentication.new(:client_id => "0987654321", :client_secret => "1234567890")
53
+ # -> { adcloud_auth.authenticate! }.must_raise(Adcloud::AuthenticationError)
54
+ # end
55
+
56
+ # end
57
+
58
+ end
@@ -0,0 +1,54 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'test_helper'
3
+
4
+ describe Adcloud::Campaign do
5
+ subject { Adcloud::Campaign }
6
+
7
+ let(:campaign) { subject.new }
8
+ let(:connection) { stub() }
9
+
10
+ before do
11
+ subject.stubs(:connection => connection)
12
+ end
13
+
14
+ describe "validate" do
15
+ let(:request_attributes) { campaign.attributes.select { |k, v| ![:_meta, :id].include?(k) } }
16
+
17
+ it 'should validate against the api' do
18
+ connection.expects(:get).with('campaigns/validate', { campaign: request_attributes }).returns({'_meta' => { 'status' => 226, 'details' => {}}})
19
+ campaign.validate
20
+ end
21
+
22
+ it 'should populate the errors hash' do
23
+ response_data = {'_meta' => { 'status' => 226, 'details' => { 'company_id' => ['must be present'] } } }
24
+ connection.stubs(:get).returns(response_data)
25
+ campaign.validate
26
+ campaign.errors['company_id'].must_equal ['must be present']
27
+ end
28
+
29
+ it 'raises an exception when the response is not well formatted' do
30
+ connection.expects(:get).with('campaigns/validate', { campaign: request_attributes })
31
+ -> { campaign.validate }.must_raise(Adcloud::AdcloudSucks::InvalidApiResponse)
32
+ end
33
+ end
34
+
35
+ describe '#valid?' do
36
+ it 'validates the model' do
37
+ campaign.expects(:validate)
38
+ campaign.valid?
39
+ end
40
+
41
+ it 'returns true when object is valid' do
42
+ campaign.stubs(:validate)
43
+ campaign.errors = {}
44
+ campaign.valid?.must_equal true
45
+ end
46
+
47
+ it 'returns false when the object is invalid' do
48
+ campaign.stubs(:validate)
49
+ campaign.errors = { company_id: ['must be present'] }
50
+ campaign.valid?.must_equal false
51
+ end
52
+ end
53
+
54
+ end
@@ -0,0 +1,78 @@
1
+ require 'test_helper'
2
+
3
+ describe Adcloud::Connection do
4
+
5
+ subject { Adcloud::Connection.new }
6
+
7
+ let(:authentication) { stub(:authenticate! => true, :token => "0987654321") }
8
+
9
+ describe "url" do
10
+
11
+ it "should be https://api.adcloud.com:443/" do
12
+ subject.url.must_equal "https://api.adcloud.com:443/v2/"
13
+ end
14
+
15
+ end
16
+
17
+ describe "authentication token" do
18
+
19
+ it "should be 0987654321" do
20
+ subject.expects(:authentication).returns(authentication)
21
+ subject.authentication_token.must_equal "0987654321"
22
+ end
23
+
24
+ end
25
+
26
+ describe "authentication" do
27
+ it "should return an authentication object" do
28
+ Adcloud::Authentication.expects(:new).returns(authentication)
29
+ subject.authentication.must_equal authentication
30
+ end
31
+ end
32
+
33
+ describe "an authenticated connection" do
34
+ let(:url) { "https://api.adcloud.com:443/v2/whatever" }
35
+
36
+ before do
37
+ subject.stubs(:authentication_token).returns("0987654321")
38
+ end
39
+
40
+ describe "post" do
41
+
42
+ it "should fire a post request" do
43
+ stub_request(:post, url).to_return(:status => 200, :body => {"_meta" => {}, "hello" => "world"}, :headers => {})
44
+ subject.post('whatever').must_equal({"_meta" => {}, "hello" => "world" })
45
+ end
46
+
47
+ # it "should raise an InvalidRequest Exception" do
48
+ # stub_request(:post, url).to_return(:status => 500, :body => "{}", :headers => {})
49
+ # -> { subject.post('whatever') }.must_raise(Adcloud::InvalidRequest)
50
+ # end
51
+
52
+ end
53
+
54
+ describe "get" do
55
+
56
+ it "should fire a get request" do
57
+ stub_request(:get, url).to_return(:status => 200, :body => {"_meta" => {}, "hello" => "world"}, :headers => {})
58
+ subject.get('whatever').must_equal({"_meta" => {}, "hello" => "world" })
59
+ end
60
+
61
+ # it "should raise an InvalidFilter Exception" do
62
+ # stub_request(:get, url).to_return(:status => 400, :body => "{}", :headers => {})
63
+ # -> { subject.get('whatever') }.must_raise(Adcloud::InvalidFilter)
64
+ # end
65
+
66
+ # it "should raise a NotFound Exception" do
67
+ # stub_request(:get, url).to_return(:status => 404, :body => "{}", :headers => {})
68
+ # -> { subject.get('whatever') }.must_raise(Adcloud::NotFound)
69
+ # end
70
+
71
+ # it "should raise an InvalidRequest Exception" do
72
+ # stub_request(:get, url).to_return(:status => 500, :body => "{}", :headers => {})
73
+ # -> { subject.get('whatever') }.must_raise(Adcloud::InvalidRequest)
74
+ # end
75
+
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,6 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'test_helper'
3
+
4
+ describe Adcloud::Customer do
5
+ # check the entity test
6
+ end
@@ -0,0 +1,159 @@
1
+ # encoding: utf-8
2
+ require 'test_helper'
3
+
4
+ describe Adcloud::Entity do
5
+
6
+ class Car < Adcloud::Entity
7
+ #self.api_endpoint = 'cars'
8
+ attribute :id, Integer
9
+ attribute :name, String
10
+ end
11
+
12
+ subject { Car }
13
+
14
+ let(:connection) { stub }
15
+
16
+ describe ".connection" do
17
+ it "should return a connection object" do
18
+ Adcloud::Connection.expects(:new).returns(connection)
19
+ subject.connection.must_equal(connection)
20
+ end
21
+ end
22
+
23
+
24
+ describe "#connection" do
25
+ it "should return the class level connection object" do
26
+ subject.expects(:connection).returns(connection)
27
+ subject.new.connection.must_equal(connection)
28
+ end
29
+ end
30
+
31
+ describe '.api_endpoint' do
32
+ it 'its api endpoint is cars' do
33
+ subject.api_endpoint.must_equal 'cars'
34
+ end
35
+ end
36
+
37
+ describe "find" do
38
+ let(:response_data) { { 'id' => 42, 'name' => 'Porsche' } }
39
+
40
+ it "should return a car" do
41
+ subject.connection.expects(:get).with('cars/42').returns(response_data)
42
+ subject.find(42).must_be_instance_of(Car)
43
+ end
44
+ end
45
+
46
+ describe "all" do
47
+ let(:empty_response_data) { {"_meta"=>{"type"=>"Array<>", "page"=>1, "size"=>0, "per_page"=>50, "total_count"=>0, "total_pages"=>0, "sort"=>{}, "uuid"=>"aabe4f5f31691f049ea1942e6a6d1793"}, "items"=>[]} }
48
+ let(:response_data) { { "_meta" => { "type" => "Array<Car>", "page" => 1, "size" => 2, "per_page" => 50, "total_count" => 2, "total_pages" => 1, "sort" => {}, "uuid" => "aabe4f5f31691f049ea1942e6a6d1793" }, "items" => [{ "id" => 52654, "name" => 'Mercedes', "_meta" => { "type" => "Car" } }, { "id" => 52655, "name" => 'Audi', "_meta" => { "type" => "Car" } }]} }
49
+
50
+ it "should return an empty array if response is empty" do
51
+ subject.connection.expects(:get).with('cars', :filter => {}, :page => 1, :per_page => 50).returns(empty_response_data)
52
+ subject.all.must_be_instance_of(Array)
53
+ end
54
+
55
+ it 'should return an array if response contains objects' do
56
+ subject.connection.expects(:get).with('cars', :filter => {}, :page => 1, :per_page => 50).returns(response_data)
57
+ subject.all.must_be_instance_of(Array)
58
+ end
59
+
60
+ it 'should return an array of cars' do
61
+ subject.connection.expects(:get).with('cars', :filter => {}, :page => 1, :per_page => 50).returns(response_data)
62
+ subject.all.each { |item| item.must_be_instance_of(subject) }
63
+ end
64
+
65
+ it 'should return 2 cars' do
66
+ subject.connection.expects(:get).with('cars', :filter => {}, :page => 1, :per_page => 50).returns(response_data)
67
+ subject.all.size.must_equal 2
68
+ end
69
+
70
+ it 'should pass the filter param' do
71
+ subject.connection.expects(:get).with('cars', :filter => { :oliver => "ist chef" }, :page => 1, :per_page => 50).returns(response_data)
72
+ subject.all(:oliver => "ist chef")
73
+ end
74
+ end
75
+
76
+ describe '#create' do
77
+ before { subject.stubs(:connection => connection) }
78
+
79
+ describe 'when submitting a valid object' do
80
+ let(:response) { { '_meta' => { 'status' => 200 }, 'id' => 1 } }
81
+
82
+ it 'sends a request to the api' do
83
+ car = subject.new
84
+ attributes = car.attributes
85
+ attributes.delete(:id)
86
+ attributes.delete(:_meta)
87
+ connection.expects(:post).with('cars', { 'car' => attributes }).returns(response)
88
+ car.create
89
+ end
90
+
91
+ it 'sets the car id' do
92
+ connection.stubs(:post).returns(response)
93
+ car = subject.new
94
+ car.create
95
+ car.id.must_equal 1
96
+ end
97
+
98
+ it 'returns true' do
99
+ connection.stubs(:post).returns(response)
100
+ subject.new.create.must_equal true
101
+ end
102
+ end
103
+
104
+ describe 'when submitting an invalid car' do
105
+ let(:error) { Adcloud::BadRequestError.new(stub(:body => {'_meta' => { 'details' => { :name => ['cannot be empty'] }}})) }
106
+ before do
107
+ subject.stubs(:connection => connection)
108
+ connection.stubs(:post).raises(error)
109
+ end
110
+
111
+ it 'returns false' do
112
+ subject.new.create.must_equal false
113
+ end
114
+
115
+ it 'sets the errors hash' do
116
+ car = subject.new
117
+ car.create
118
+ car.errors.must_equal({ :name => ['cannot be empty'] })
119
+ end
120
+ end
121
+ end
122
+
123
+ describe '.create' do
124
+ it 'creates a new car' do
125
+ car = subject.new
126
+ subject.expects(:new).returns(car)
127
+ car.expects(:create)
128
+ subject.create.must_equal car
129
+ end
130
+ end
131
+
132
+ describe "errors" do
133
+ it "should be empty" do
134
+ subject.new.errors.must_be_empty
135
+ end
136
+ end
137
+
138
+ describe '.api_name' do
139
+ it 'returns the class name in lowercase' do
140
+ Car.api_name.must_equal 'car'
141
+ end
142
+
143
+ it 'removes the namespace' do
144
+ module MyTest
145
+ class Truck < Adcloud::Entity
146
+ end
147
+ end
148
+ MyTest::Truck.api_name.must_equal 'truck'
149
+ end
150
+
151
+ it 'transforms camel case to underscore' do
152
+ class AirPlane < Adcloud::Entity
153
+ end
154
+ AirPlane.api_name.must_equal 'air_plane'
155
+ end
156
+
157
+ end
158
+
159
+ end