adcloud 0.7.0

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