soapy_cake 2.1.0 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +16 -0
  3. data/Gemfile +2 -1
  4. data/circle.yml +2 -0
  5. data/lib/soapy_cake.rb +3 -0
  6. data/lib/soapy_cake/admin.rb +2 -2
  7. data/lib/soapy_cake/admin_addedit.rb +5 -50
  8. data/lib/soapy_cake/campaigns.rb +104 -0
  9. data/lib/soapy_cake/client.rb +5 -5
  10. data/lib/soapy_cake/helper.rb +14 -9
  11. data/lib/soapy_cake/modification_type.rb +45 -0
  12. data/lib/soapy_cake/version.rb +1 -1
  13. data/spec/fixtures/vcr_cassettes/SoapyCake_AdminAddedit/campaigns/edits_a_campaign.yml +18 -5
  14. data/spec/fixtures/vcr_cassettes/SoapyCake_Campaigns/_create/creates_campaigns.yml +60 -0
  15. data/spec/fixtures/vcr_cassettes/SoapyCake_Campaigns/_create/raises_an_error_if_the_creation_was_unsuccessful.yml +60 -0
  16. data/spec/fixtures/vcr_cassettes/SoapyCake_Campaigns/_get/gets_campaigns.yml +1205 -0
  17. data/spec/fixtures/vcr_cassettes/SoapyCake_Campaigns/_patch/different_pre-existing_values/does_not_change_anything_unintentionally_attribute_set_0_.yml +380 -0
  18. data/spec/fixtures/vcr_cassettes/SoapyCake_Campaigns/_patch/different_pre-existing_values/does_not_change_anything_unintentionally_attribute_set_1_.yml +383 -0
  19. data/spec/fixtures/vcr_cassettes/SoapyCake_Campaigns/_patch/updates_a_campaign.yml +454 -0
  20. data/spec/fixtures/vcr_cassettes/SoapyCake_Campaigns/_update/raises_an_error_if_the_update_was_unsuccessful.yml +82 -0
  21. data/spec/fixtures/vcr_cassettes/SoapyCake_Campaigns/_update/updates_campaigns.yml +82 -0
  22. data/spec/integration/soapy_cake/admin_addedit_spec.rb +0 -31
  23. data/spec/integration/soapy_cake/campaigns_spec.rb +175 -0
  24. data/spec/lib/soapy_cake/admin_spec.rb +0 -1
  25. data/spec/lib/soapy_cake/campaigns_spec.rb +127 -0
  26. data/spec/lib/soapy_cake/modification_type_spec.rb +59 -0
  27. data/spec/spec_helper.rb +3 -3
  28. metadata +27 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6f1b3eafd1d67264317255af0c6a0ec5a7bdf768
4
- data.tar.gz: 060efd87807a74298141dc39000b59f029e06974
3
+ metadata.gz: ffa62a533e0dbfd1cd366b024b007c9a923d4a12
4
+ data.tar.gz: 5b47bba259aa0ccaafcd0eb6ffb10384b3ea4030
5
5
  SHA512:
6
- metadata.gz: 27b28c976de97f17fc8e15e3c2a8052d17855f3edc17fe07b1e116cb713f94fcfb2924fb18bd4ee014cfce5ebedecbfbdb6fe63e8ef5c2fe7330b00595f159de
7
- data.tar.gz: f4242f62639da6318b213391528d4c6e7e43038567a4da2a8502c208348a04e8db0a84a608a7be5d84e10997cac1751ec9d6da1d2d85634d98141c6b7901b202
6
+ metadata.gz: cb9a9ceabe4b5733bec4d5b56a2dc33f47564322d92c96af2beda789b9c376fb6aa4dc2e6b1f6bd05e01b2eff16cdeba1d6c7b9ab1b7373a0a974e2b322105a1
7
+ data.tar.gz: f204021e63684c64c838bb03d45addb6098e0ffb6142cf6fda723c0d97440af1a875b0dcecaf99b5240737240a01afe2be550c262b42a17e56df99b0ffc399b9
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,16 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2016-11-11 15:36:42 +0100 using RuboCop version 0.43.0.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 1
10
+ Metrics/AbcSize:
11
+ Max: 27
12
+
13
+ # Offense count: 1
14
+ # Configuration parameters: CountComments.
15
+ Metrics/MethodLength:
16
+ Max: 20
data/Gemfile CHANGED
@@ -4,6 +4,7 @@ source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
6
  group :development, :test do
7
- gem 'rubocop-ci', github: 'ad2games/rubocop-ci'
7
+ gem 'rubocop-ci', git: 'https://github.com/ad2games/rubocop-ci'
8
+ gem 'simplecov'
8
9
  gem 'codeclimate-test-reporter', require: false
9
10
  end
data/circle.yml CHANGED
@@ -4,3 +4,5 @@ dependencies:
4
4
  test:
5
5
  pre:
6
6
  - bundle exec rake rubocop
7
+ post:
8
+ - bundle exec codeclimate-test-reporter
data/lib/soapy_cake.rb CHANGED
@@ -23,6 +23,9 @@ require 'soapy_cake/admin_addedit'
23
23
  require 'soapy_cake/admin_track'
24
24
  require 'soapy_cake/affiliate'
25
25
 
26
+ require 'soapy_cake/modification_type'
27
+ require 'soapy_cake/campaigns'
28
+
26
29
  module SoapyCake
27
30
  API_CONFIG = YAML.load(File.read(File.expand_path('../../api.yml', __FILE__)))
28
31
  NET_TIMEOUT = 600
@@ -37,7 +37,7 @@ module SoapyCake
37
37
  end
38
38
 
39
39
  def creatives(opts = {})
40
- opts = translate_values(opts, %i(creative_type_id creative_status_id))
40
+ opts = translate_values(opts)
41
41
 
42
42
  run Request.new(:admin, :export, :creatives, opts)
43
43
  end
@@ -80,7 +80,7 @@ module SoapyCake
80
80
 
81
81
  def caps(opts)
82
82
  require_params(opts, %i(start_date end_date))
83
- opts = translate_values(opts, %i(cap_type_id))
83
+ opts = translate_values(opts)
84
84
 
85
85
  run Request.new(:admin, :reports, :caps, opts)
86
86
  end
@@ -70,29 +70,6 @@ module SoapyCake
70
70
  offer_contract_is_default use_fallback_targeting
71
71
  ).freeze
72
72
 
73
- REQUIRED_NEW_CAMPAIGN_PARAMS = %i(
74
- affiliate_id offer_id account_status_id payout
75
- redirect_404 clear_session_on_conversion paid_upsells
76
- ).freeze
77
-
78
- CAMPAIGN_UPDATE_DEFAULT_OPTIONS = {
79
- account_status_id: :no_change,
80
- auto_disposition_delay_hours: 0,
81
- clear_session_on_conversion: 'no_change',
82
- currency_id: 0,
83
- expiration_date_modification_type: 'do_not_change',
84
- media_type_id: 0,
85
- paid: 'no_change',
86
- paid_redirects: 'no_change',
87
- paid_upsells: 'no_change',
88
- postback_delay_ms: -1,
89
- redirect_404: 'no_change',
90
- redirect_offer_contract_id: 0,
91
- review: 'no_change',
92
- use_offer_contract_payout: 'no_change',
93
- payout_update_option: 'do_not_change'
94
- }.freeze
95
-
96
73
  def add_offer(opts)
97
74
  require_params(opts, REQUIRED_NEW_OFFER_PARAMS)
98
75
 
@@ -164,7 +141,7 @@ module SoapyCake
164
141
  def update_caps(opts)
165
142
  require_params(opts, %i(cap_type_id cap_interval_id cap_amount send_alert_only))
166
143
 
167
- opts = translate_values(opts, %i(cap_type_id cap_interval_id))
144
+ opts = translate_values(opts)
168
145
 
169
146
  run Request.new(:admin, :addedit, :caps, opts)
170
147
  end
@@ -172,7 +149,7 @@ module SoapyCake
172
149
  def remove_caps(opts)
173
150
  require_params(opts, %i(cap_type_id))
174
151
 
175
- opts = translate_values(opts, %i(cap_type_id))
152
+ opts = translate_values(opts)
176
153
 
177
154
  opts = opts.merge(cap_interval_id: 0, cap_amount: -1, send_alert_only: false)
178
155
  run Request.new(:admin, :addedit, :caps, opts)
@@ -196,25 +173,13 @@ module SoapyCake
196
173
  run Request.new(:admin, :addedit, :advertiser, opts.merge(advertiser_id: 0))
197
174
  end
198
175
 
199
- def add_campaign(opts)
200
- require_params(opts, REQUIRED_NEW_CAMPAIGN_PARAMS)
201
- addedit_campaign(opts.merge(campaign_id: 0, expiration_date: future_expiration_date))
202
- end
203
-
204
- def edit_campaign(opts)
205
- require_params(opts, %i(campaign_id))
206
- validate_id(opts, :campaign_id)
207
-
208
- addedit_campaign(CAMPAIGN_UPDATE_DEFAULT_OPTIONS.merge(opts))
209
- end
210
-
211
176
  private
212
177
 
213
178
  def addedit_offer_tier(add_edit_option, opts)
214
179
  require_params(opts, %i(offer_id tier_id price_format_id offer_contract_id status_id))
215
180
 
216
181
  opts = opts.merge(redirect_offer_contract_id: -1, add_edit_option: add_edit_option)
217
- opts = translate_values(opts, %i(status_id price_format_id))
182
+ opts = translate_values(opts)
218
183
 
219
184
  run Request.new(:admin, :addedit, :offer_tiers, opts)
220
185
  end
@@ -252,26 +217,16 @@ module SoapyCake
252
217
 
253
218
  opts = translate_booleans(opts)
254
219
  opts = apply_tag_opts(opts)
255
- opts = translate_values(opts, %i(
256
- currency_id offer_status_id offer_type_id price_format_id
257
- conversion_cap_behavior conversion_behavior_on_redirect
258
- ))
220
+ opts = translate_values(opts)
259
221
 
260
222
  run(Request.new(:admin, :addedit, :offer, default_offer_options.merge(opts)))[:success_info]
261
223
  end
262
224
 
263
225
  def addedit_offer_contract(opts)
264
226
  require_params(opts, REQUIRED_OFFER_CONTRACT_PARAMS)
265
- opts = translate_values(opts, %i(price_format_id))
227
+ opts = translate_values(opts)
266
228
 
267
229
  run Request.new(:admin, :addedit, :offer_contract, opts)
268
230
  end
269
-
270
- def addedit_campaign(opts)
271
- opts = translate_booleans(opts)
272
- opts = translate_values(opts, %i(account_status_id))
273
- opts = opts.reverse_merge(display_link_type_id: 1)
274
- run Request.new(:admin, :addedit, :campaign, opts)
275
- end
276
231
  end
277
232
  end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+ module SoapyCake
3
+ class Campaigns
4
+ include Helper
5
+
6
+ # TODO: Figure out what `static_suppression` is for and whether it needs to
7
+ # be in the list.
8
+ ALL_PARAMS = %i(
9
+ account_status_id affiliate_id auto_disposition_delay_hours campaign_id
10
+ clear_session_on_conversion currency_id display_link_type_id
11
+ expiration_date expiration_date_modification_type media_type_id
12
+ offer_contract_id offer_id paid paid_redirects paid_upsells payout
13
+ payout_update_option pixel_html postback_delay_ms postback_url
14
+ redirect_404 redirect_domain redirect_offer_contract_id review test_link
15
+ third_party_name unique_key_hash use_offer_contract_payout
16
+ ).freeze
17
+
18
+ NO_CHANGE_VALUES = {
19
+ account_status_id: 0,
20
+ expiration_date_modification_type: ModificationType::DO_NOT_CHANGE,
21
+ currency_id: 0,
22
+ use_offer_contract_payout: 'no_change',
23
+ payout_update_option: ModificationType::DO_NOT_CHANGE,
24
+ paid: 'no_change',
25
+ paid_redirects: 'no_change',
26
+ paid_upsells: 'no_change',
27
+ review: 'no_change',
28
+ auto_disposition_delay_hours: 0,
29
+ redirect_offer_contract_id: 0,
30
+ redirect_404: 'no_change',
31
+ clear_session_on_conversion: 'no_change',
32
+ postback_delay_ms: -1
33
+ }.freeze
34
+
35
+ delegate :read_only?, to: :client
36
+
37
+ def get(opts = {})
38
+ client.run Request.new(:admin, :export, :campaigns, opts)
39
+ end
40
+
41
+ def create(opts = {})
42
+ response = addedit_campaign(opts.merge(campaign_id: 0))
43
+ response.fetch(:success_info).fetch(:campaign_id)
44
+ end
45
+
46
+ def update(campaign_id, opts = {})
47
+ opts = opts.merge(campaign_id: campaign_id)
48
+ opts = opts.merge(payout.options(opts))
49
+ opts = opts.merge(expiration_date.options(opts))
50
+ opts = NO_CHANGE_VALUES.merge(opts)
51
+ require_params(opts, ALL_PARAMS)
52
+ addedit_campaign(opts)
53
+ nil
54
+ end
55
+
56
+ def patch(campaign_id, opts = {})
57
+ campaign = get(campaign_id: campaign_id).first
58
+ opts = NO_CHANGE_VALUES
59
+ .merge(
60
+ affiliate_id: campaign.fetch(:affiliate).fetch(:affiliate_id),
61
+ # Only present in production:
62
+ display_link_type_id: campaign.dig(:link_display_type, :link_display_type_id) || 1,
63
+ media_type_id: campaign.fetch(:media_type).fetch(:media_type_id),
64
+ offer_contract_id: campaign.fetch(:offer_contract).fetch(:offer_contract_id),
65
+ offer_id: campaign.fetch(:offer).fetch(:offer_id),
66
+ payout: campaign.fetch(:payout).fetch(:amount),
67
+ payout_update_option: 'do_not_change',
68
+ pixel_html: campaign.dig(:pixel_info, :pixel_html) || '',
69
+ postback_url: campaign.dig(:pixel_info, :postback_url) || '',
70
+ redirect_domain: campaign.fetch(:redirect_domain, ''),
71
+ test_link: campaign[:test_link] || '',
72
+ unique_key_hash: campaign.dig(:pixel_info, :hash_type, :hash_type_id) || 'none',
73
+ third_party_name: campaign.fetch(:third_party_name, '')
74
+ )
75
+ .merge(opts)
76
+ update(campaign_id, opts)
77
+ nil
78
+ end
79
+
80
+ private
81
+
82
+ def payout
83
+ ModificationType.new(:payout, :payout_update_option, 0)
84
+ end
85
+
86
+ def expiration_date
87
+ ModificationType.new(
88
+ :expiration_date,
89
+ :expiration_date_modification_type,
90
+ Time.utc(1970, 1, 1)
91
+ )
92
+ end
93
+
94
+ def addedit_campaign(opts)
95
+ opts = translate_booleans(opts)
96
+ opts = translate_values(opts)
97
+ client.run Request.new(:admin, :addedit, :campaign, opts)
98
+ end
99
+
100
+ def client
101
+ @client ||= Client.new
102
+ end
103
+ end
104
+ end
@@ -22,10 +22,6 @@ module SoapyCake
22
22
  !write_enabled
23
23
  end
24
24
 
25
- protected
26
-
27
- attr_reader :domain, :api_key, :time_converter, :opts, :logger, :retry_count, :write_enabled
28
-
29
25
  def run(request)
30
26
  check_write_enabled!(request)
31
27
  request.api_key = api_key
@@ -37,6 +33,10 @@ module SoapyCake
37
33
  end
38
34
  end
39
35
 
36
+ protected
37
+
38
+ attr_reader :domain, :api_key, :time_converter, :opts, :logger, :retry_count, :write_enabled
39
+
40
40
  private
41
41
 
42
42
  def fetch_opt(key, fallback = nil)
@@ -49,7 +49,7 @@ module SoapyCake
49
49
  end
50
50
 
51
51
  def with_retries(&block)
52
- opts = { tries: retry_count + 1, on: [RateLimitError, SocketError], sleep: -> (n) { 3**n } }
52
+ opts = { tries: retry_count + 1, on: [RateLimitError, SocketError], sleep: ->(n) { 3**n } }
53
53
  Retryable.retryable(opts, &block)
54
54
  end
55
55
 
@@ -5,9 +5,9 @@ module SoapyCake
5
5
  return nil if obj == {}
6
6
 
7
7
  case obj
8
- when Hash
8
+ when Hash, Saxerator::Builder::HashElement
9
9
  obj.map { |hk, hv| [hk, walk_tree(hv, hk, &block)] }.to_h
10
- when Array
10
+ when Array, Saxerator::Builder::ArrayElement
11
11
  obj.map { |av| walk_tree(av, &block) }
12
12
  else
13
13
  yield(obj, key)
@@ -20,7 +20,7 @@ module SoapyCake
20
20
 
21
21
  def require_params(opts, params)
22
22
  params.each do |param|
23
- raise Error, "Parameter '#{param}' missing!" unless opts.key?(param)
23
+ raise Error, "Parameter '#{param}' missing!" if opts[param].nil?
24
24
  end
25
25
  end
26
26
 
@@ -34,17 +34,22 @@ module SoapyCake
34
34
  end
35
35
  end
36
36
 
37
- def translate_values(opts, params)
37
+ def translate_values(opts)
38
38
  opts.map do |k, v|
39
- [
40
- k,
41
- params.include?(k) ? const_lookup(k, v) : v
42
- ]
39
+ id_key = :"#{k}_id"
40
+
41
+ if Const::CONSTS.key?(id_key)
42
+ [id_key, const_lookup(id_key, v)]
43
+ elsif Const::CONSTS.key?(k) && !v.is_a?(Integer)
44
+ [k, const_lookup(k, v)]
45
+ else
46
+ [k, v]
47
+ end
43
48
  end.to_h
44
49
  end
45
50
 
46
51
  def const_lookup(type, key)
47
- Const::CONSTS[type].fetch(key) do
52
+ Const::CONSTS.fetch(type).fetch(key) do
48
53
  raise ArgumentError, "#{key} is not a valid value for #{type}"
49
54
  end
50
55
  end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+ module SoapyCake
3
+ class ModificationType
4
+ CHANGE = 'change'
5
+ REMOVE = 'remove'
6
+ DO_NOT_CHANGE = 'do_not_change'
7
+
8
+ def initialize(key, modification_type_key, default)
9
+ @key = key
10
+ @modification_type_key = modification_type_key
11
+ @default = default
12
+ end
13
+
14
+ def options(input_opts)
15
+ validate_input(input_opts)
16
+
17
+ input_opts.merge(
18
+ key => value(input_opts),
19
+ modification_type_key => modification_type(input_opts)
20
+ )
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :key, :modification_type_key, :default
26
+
27
+ def value(input_opts)
28
+ input_opts.fetch(key, default)
29
+ end
30
+
31
+ def modification_type(input_opts)
32
+ input_opts.fetch(modification_type_key) do
33
+ input_opts[key] ? CHANGE : REMOVE
34
+ end
35
+ end
36
+
37
+ def validate_input(input_opts)
38
+ return unless input_opts[key].nil? && input_opts[modification_type_key] == CHANGE
39
+ raise InvalidInput,
40
+ "`#{modification_type_key}` was '#{CHANGE}', but no `#{key}` was provided to change it to"
41
+ end
42
+
43
+ InvalidInput = Class.new(StandardError)
44
+ end
45
+ end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module SoapyCake
3
- VERSION = '2.1.0'
3
+ VERSION = '2.1.1'
4
4
  end
@@ -17,6 +17,7 @@ http_interactions:
17
17
  <cake:auto_disposition_delay_hours>0</cake:auto_disposition_delay_hours>
18
18
  <cake:clear_session_on_conversion>no_change</cake:clear_session_on_conversion>
19
19
  <cake:currency_id>0</cake:currency_id>
20
+ <cake:expiration_date>1970-01-01T01:00:00</cake:expiration_date>
20
21
  <cake:expiration_date_modification_type>do_not_change</cake:expiration_date_modification_type>
21
22
  <cake:media_type_id>1</cake:media_type_id>
22
23
  <cake:paid>no_change</cake:paid>
@@ -28,16 +29,28 @@ http_interactions:
28
29
  <cake:review>no_change</cake:review>
29
30
  <cake:use_offer_contract_payout>no_change</cake:use_offer_contract_payout>
30
31
  <cake:payout_update_option>do_not_change</cake:payout_update_option>
31
- <cake:campaign_id>123</cake:campaign_id>
32
+ <cake:campaign_id>23727</cake:campaign_id>
32
33
  <cake:affiliate_id>1</cake:affiliate_id>
33
34
  <cake:offer_id>8910</cake:offer_id>
34
35
  <cake:payout>1.23</cake:payout>
36
+ <cake:offer_contract_id>767</cake:offer_contract_id>
37
+ <cake:pixel_html>&lt;&gt;</cake:pixel_html>
38
+ <cake:postback_url>http://examle.com/postback</cake:postback_url>
39
+ <cake:redirect_domain>trk_ad2games.cakemarketing.net</cake:redirect_domain>
40
+ <cake:test_link>http://example.com/test</cake:test_link>
41
+ <cake:unique_key_hash>none</cake:unique_key_hash>
35
42
  </cake:Campaign>
36
43
  </env:Body>
37
44
  </env:Envelope>
38
45
  headers:
39
46
  Content-Type:
40
47
  - application/soap+xml;charset=UTF-8
48
+ Accept-Encoding:
49
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
50
+ Accept:
51
+ - "*/*"
52
+ User-Agent:
53
+ - Ruby
41
54
  response:
42
55
  status:
43
56
  code: 200
@@ -54,15 +67,15 @@ http_interactions:
54
67
  X-Powered-By:
55
68
  - ASP.NET
56
69
  Date:
57
- - Wed, 04 May 2016 09:44:11 GMT
70
+ - Thu, 03 Nov 2016 16:08:18 GMT
58
71
  Content-Length:
59
- - '626'
72
+ - '631'
60
73
  body:
61
74
  encoding: UTF-8
62
75
  string: <?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
63
76
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><CampaignResponse
64
77
  xmlns="http://cakemarketing.com/api/3/"><CampaignResult><success>true</success><message>Campaign
65
- Updated</message><success_info><campaign_id>123</campaign_id><affiliate_id>-1</affiliate_id><offer_id>5078</offer_id><offer_contract_id>123</offer_contract_id><media_type_id>1</media_type_id><original>true</original></success_info></CampaignResult></CampaignResponse></soap:Body></soap:Envelope>
78
+ Updated</message><success_info><campaign_id>23727</campaign_id><affiliate_id>13978</affiliate_id><offer_id>7704</offer_id><offer_contract_id>767</offer_contract_id><media_type_id>1</media_type_id><original>true</original></success_info></CampaignResult></CampaignResponse></soap:Body></soap:Envelope>
66
79
  http_version:
67
80
  recorded_at: Tue, 17 Feb 2015 12:00:00 GMT
68
- recorded_with: VCR 3.0.1
81
+ recorded_with: VCR 3.0.3