soapy_cake 2.1.0 → 2.1.1

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 (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