soapy_cake 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +24 -0
  3. data/.rspec +2 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +56 -0
  8. data/Rakefile +6 -0
  9. data/api_versions.yml +103 -0
  10. data/circle.yml +7 -0
  11. data/lib/soapy_cake.rb +8 -0
  12. data/lib/soapy_cake/client.rb +145 -0
  13. data/lib/soapy_cake/version.rb +3 -0
  14. data/script/update_versions.rb +41 -0
  15. data/soapy_cake.gemspec +30 -0
  16. data/spec/fixtures/vcr_cassettes/client_new_account_statuses.yml +1200 -0
  17. data/spec/fixtures/vcr_cassettes/client_new_advertisers.yml +1202 -0
  18. data/spec/fixtures/vcr_cassettes/client_new_affiliate_tags.yml +1200 -0
  19. data/spec/fixtures/vcr_cassettes/client_new_affiliate_tiers.yml +1201 -0
  20. data/spec/fixtures/vcr_cassettes/client_new_billing_cycles.yml +1200 -0
  21. data/spec/fixtures/vcr_cassettes/client_new_cap_intervals.yml +1201 -0
  22. data/spec/fixtures/vcr_cassettes/client_new_cap_types.yml +1199 -0
  23. data/spec/fixtures/vcr_cassettes/client_new_countries.yml +1515 -0
  24. data/spec/fixtures/vcr_cassettes/client_new_currencies.yml +1199 -0
  25. data/spec/fixtures/vcr_cassettes/client_new_empty_response.yml +1200 -0
  26. data/spec/fixtures/vcr_cassettes/client_new_empty_response_offer_summary.yml +865 -0
  27. data/spec/fixtures/vcr_cassettes/client_new_languages.yml +1199 -0
  28. data/spec/fixtures/vcr_cassettes/client_new_media_types.yml +1215 -0
  29. data/spec/fixtures/vcr_cassettes/client_new_offer_statuses.yml +1202 -0
  30. data/spec/fixtures/vcr_cassettes/client_new_offer_types.yml +1201 -0
  31. data/spec/fixtures/vcr_cassettes/client_new_payment_settings.yml +1203 -0
  32. data/spec/fixtures/vcr_cassettes/client_new_payment_types.yml +1204 -0
  33. data/spec/fixtures/vcr_cassettes/client_new_price_formats.yml +1202 -0
  34. data/spec/fixtures/vcr_cassettes/client_new_roles.yml +1203 -0
  35. data/spec/fixtures/vcr_cassettes/client_new_verticals.yml +1197 -0
  36. data/spec/fixtures/vcr_cassettes/client_new_with_username_and_password.yml +1198 -0
  37. data/spec/fixtures/vcr_cassettes/client_sekken_client_caches_results.yml +1261 -0
  38. data/spec/lib/soapy_cake/client_spec.rb +164 -0
  39. data/spec/spec_helper.rb +36 -0
  40. metadata +219 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d84a9fa4e30d6c6fe16a0b8c4c9ad0f3a7fa5fdb
4
+ data.tar.gz: 271ad7e092f7c9ee8319b7fa73720bfcbc4c1dbc
5
+ SHA512:
6
+ metadata.gz: b77069e1844a7c8ba88d5d84982b5a8b95cc81bb1301744e7ef509780a18f376d296fa1ec21021d591804976f8b1656d0b024bd01588def199bc96427457a375
7
+ data.tar.gz: 2b362c00e8ed251b12025ad67ee73a757e1f7739c376bd6d348cadbabb711ab177836cded26f47d5d906dd23e6ad18c94344d3395276c744c23ba1cf405f4567
data/.gitignore ADDED
@@ -0,0 +1,24 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .env
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
19
+ *.bundle
20
+ *.so
21
+ *.o
22
+ *.a
23
+ mkmf.log
24
+ *~
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.0.0-p451
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in soapy_cake.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 ad2games GmbH
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # SoapyCake
2
+
3
+ Simple client library for [cake](http://getcake.com).
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'soapy_cake'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install soapy_cake
18
+
19
+ ## Usage Examples
20
+
21
+ First we assume that you set the `CAKE_DOMAIN` and `CAKE_API_KEY`
22
+ environment variables.
23
+
24
+ Export all advertisers:
25
+
26
+ ```ruby
27
+ SoapyCake::Client.export.advertisers(opts)
28
+ ```
29
+
30
+ Get report for specific date range:
31
+
32
+ ```ruby
33
+ SoapyCake::Client.reports.affiliate_summary(
34
+ start_date: Date.beginning_of_month,
35
+ end_date: Date.today
36
+ )
37
+ ```
38
+
39
+ If you are interested in how we map methods to api endpoints take a look
40
+ at [api_versions.yml](/ad2games/soapy_cake/blob/master/api_versions.yml).
41
+
42
+ ## Contributing
43
+
44
+ 1. Fork it (https://github.com/ad2games/soapy_cake/fork)
45
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
46
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
47
+ 4. Push to the branch (`git push origin my-new-feature`)
48
+ 5. Create a new Pull Request
49
+
50
+ ## Actual Soapy Cakes
51
+
52
+ If you are looking for actual soap cakes...
53
+
54
+ ![Soapy Cake](http://soapycakes.co/wp-content/uploads/2012/05/blogpics17-310x205.jpg)
55
+
56
+ you should head to [soapycakes.co](http://soapycakes.co).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
data/api_versions.yml ADDED
@@ -0,0 +1,103 @@
1
+ ---
2
+ :admin:
3
+ :add:
4
+ :advertiser_credit: 1
5
+ :creative: 1
6
+ :manual_credit: 1
7
+ :addedit:
8
+ :advertiser: 1
9
+ :advertiser_credit_limit: 1
10
+ :affiliate: 2
11
+ :apply_suppression_list_to_offer: 1
12
+ :campaign: 2
13
+ :caps: 1
14
+ :contact: 1
15
+ :creative: 1
16
+ :creative_files: 1
17
+ :exchange_rates: 1
18
+ :geo_targets: 1
19
+ :offer: 3
20
+ :offer_contract: 1
21
+ :suppresion_list: 1
22
+ :vertical: 1
23
+ :auth:
24
+ :login: 2
25
+ :doc:
26
+ :posting_doc: 1
27
+ :edit:
28
+ :buyer: 1
29
+ :export:
30
+ :advertisers: 5
31
+ :affiliate_referrals: 1
32
+ :affiliates: 5
33
+ :buyer_contracts: 1
34
+ :buyers: 1
35
+ :campaigns: 6
36
+ :creatives: 3
37
+ :offers: 5
38
+ :rules_targets: 3
39
+ :schedules: 1
40
+ :get:
41
+ :account_statuses: 1
42
+ :advertisers: 1
43
+ :affiliate_tags: 1
44
+ :affiliate_tiers: 1
45
+ :billing_cycles: 1
46
+ :cap_intervals: 1
47
+ :cap_types: 1
48
+ :countries: 1
49
+ :currencies: 1
50
+ :departments: 1
51
+ :exchange_rates: 1
52
+ :get_api_key: 1
53
+ :inactive_reasons: 1
54
+ :languages: 1
55
+ :offer_statuses: 1
56
+ :offer_types: 1
57
+ :payment_settings: 1
58
+ :payment_types: 1
59
+ :price_formats: 1
60
+ :roles: 1
61
+ :tracking_domains: 1
62
+ :verticals: 2
63
+ :reports:
64
+ :advertiser_summary: 2
65
+ :affiliate_summary: 2
66
+ :campaign_summary: 2
67
+ :creative_summary: 2
68
+ :caps: 3
69
+ :clicks: 7
70
+ :conversion_changes: 10
71
+ :conversions: 11
72
+ :daily_summary_export: 1
73
+ :leads_by_affiliate_export: 1
74
+ :leads_by_buyer: 4
75
+ :login_export: 1
76
+ :offer_summary: 2
77
+ :traffic_export: 1
78
+ :signup:
79
+ :advertiser: 1
80
+ :affiliate: 4
81
+ :get_media_types: 1
82
+ :get_price_formats: 1
83
+ :get_traffic_types: 1
84
+ :get_vertical_categories: 1
85
+ :track:
86
+ :accepted_dispositions: 1
87
+ :conversion_dispositions: 2
88
+ :decrypt_affiliate_link: 1
89
+ :mass_conversion_adjustment: 2
90
+ :mass_conversion_insert: 2
91
+ :rejected_dispositions: 1
92
+ :update_conversion: 2
93
+ :update_conversion_disposition: 2
94
+ :update_conversion_price: 2
95
+ :update_conversion_revenue: 1
96
+ :update_lead_price: 2
97
+ :update_sale_revenue: 1
98
+ :advertisers:
99
+ :reports:
100
+ :bills: 1
101
+ :affiliates:
102
+ :reports:
103
+ :bills: 3
data/circle.yml ADDED
@@ -0,0 +1,7 @@
1
+ deployment:
2
+ production:
3
+ branch: master
4
+ commands:
5
+ - gem install geminabox
6
+ - gem build soapy_cake.gemspec
7
+ - gem inabox soapy_cake-*.gem --host $GEMINABOX_URL
data/lib/soapy_cake.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'active_support/core_ext/string'
2
+ require 'active_support/core_ext/date'
3
+ require 'soapy_cake/version'
4
+ require 'soapy_cake/client'
5
+
6
+ module SoapyCake
7
+ API_VERSIONS = YAML.load(File.read(File.expand_path('../../api_versions.yml', __FILE__)))
8
+ end
@@ -0,0 +1,145 @@
1
+ require 'sekken'
2
+ require 'active_support/core_ext/time/zones'
3
+
4
+ module SoapyCake
5
+ class Client
6
+ attr_reader :service, :api_key, :domain, :role
7
+
8
+ def initialize(service, opts = {})
9
+ @service = service.to_sym
10
+ @version = opts[:version]
11
+ @role = opts[:role] || :admin
12
+
13
+ @domain = opts.fetch(:domain) do
14
+ if ENV['CAKE_DOMAIN'].present?
15
+ ENV['CAKE_DOMAIN']
16
+ else
17
+ raise 'We need a domain'
18
+ end
19
+ end
20
+
21
+ @api_key = opts.fetch(:api_key) do
22
+ if opts[:username] && opts[:password]
23
+ get_api_key(opts[:username], opts[:password])
24
+ elsif ENV['CAKE_API_KEY']
25
+ ENV['CAKE_API_KEY']
26
+ else
27
+ raise 'We need an API key here!'
28
+ end
29
+ end
30
+ end
31
+
32
+ def self.method_missing(method, opts = {})
33
+ new(method, opts)
34
+ end
35
+
36
+ def sekken_client(method)
37
+ self.class.sekken_client(wsdl_url(version(method)))
38
+ end
39
+
40
+ def self.sekken_client(url)
41
+ @sekken_clients ||= {}
42
+ @sekken_clients[url] ||= Sekken.new(url)
43
+ end
44
+
45
+ def method_missing(method, opts = {})
46
+ if supported?(method)
47
+ method = method.to_s
48
+ operation = sekken_client(method).operation(service, "#{service}Soap12", method.camelize)
49
+ operation.body = build_body(method, opts)
50
+ process_response(method, operation.call.body)
51
+ else
52
+ super
53
+ end
54
+ end
55
+
56
+ def known_params_for(method)
57
+ method = method.to_s
58
+ operation = sekken_client(method).operation(service, "#{service}Soap12", method.camelize)
59
+ operation.example_body
60
+ end
61
+
62
+ private
63
+
64
+ def build_body(method, opts)
65
+ {
66
+ method.camelize.to_sym => { api_key: api_key }.merge(
67
+ opts.each_with_object({}) do |(key, value), memo|
68
+ memo[key] = format_param(value)
69
+ end
70
+ )
71
+ }
72
+ end
73
+
74
+ def format_param(value)
75
+ case value
76
+ when Time
77
+ value.utc.strftime('%Y-%m-%dT%H:%M:%S')
78
+ when Date
79
+ value.strftime('%Y-%m-%dT00:00:00')
80
+ else
81
+ value
82
+ end
83
+ end
84
+
85
+ def process_response(method, response)
86
+ Time.use_zone('UTC') do
87
+ raise response[:fault][:reason][:text] if response[:fault]
88
+ node_name = {
89
+ 'affiliate_summary' => 'affiliates',
90
+ 'affiliate_tags' => 'tags',
91
+ 'offer_summary' => 'offers',
92
+ }.fetch(method, method)
93
+ result = response[:"#{method}_response"][:"#{method}_result"]
94
+ raise result[:message] if result[:success] == false
95
+ return result unless result_has_collection?(result)
96
+ extract_collection(node_name, result).
97
+ map { |hash| remove_prefix(node_name, hash) }
98
+ end
99
+ end
100
+
101
+ def result_has_collection?(result)
102
+ !result.key?(:message)
103
+ end
104
+
105
+ def extract_collection(node_name, response)
106
+ node_name = node_name.to_sym
107
+ if response.key?(node_name)
108
+ return [] if response[node_name].nil?
109
+ response = response[node_name]
110
+ end
111
+ [response[response.keys.first]].flatten
112
+ end
113
+
114
+ def remove_prefix(prefix, object)
115
+ object.each_with_object({}) do |(k, v), m|
116
+ prefix_ = "#{prefix.singularize}_"
117
+ if k.to_s.start_with?(prefix_)
118
+ m[k[(prefix_.size)..-1].to_sym] = v
119
+ else
120
+ m[k] = v
121
+ end
122
+ end
123
+ end
124
+
125
+ def get_api_key(username, password)
126
+ operation = sekken_client(:get_api_key).operation('get', 'getSoap12', 'GetAPIKey')
127
+ operation.body = { GetAPIKey: { username: username, password: password } }
128
+ response = operation.call.body
129
+ response[:get_api_key_response][:get_api_key_result]
130
+ end
131
+
132
+ def wsdl_url(version)
133
+ role_path = (role && role != :admin) ? "/#{role}" : nil
134
+ "https://#{domain}#{role_path}/api/#{version}/#{service}.asmx?WSDL"
135
+ end
136
+
137
+ def version(method)
138
+ API_VERSIONS[role][service][method.to_sym]
139
+ end
140
+
141
+ def supported?(method)
142
+ API_VERSIONS[role][service].keys.include?(method)
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,3 @@
1
+ module SoapyCake
2
+ VERSION = '0.2.1'
3
+ end
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ SOURCE = 'https://support.getcake.com/hc/en-us/articles/200704900-Admin-API-Version-Tracker'
4
+
5
+ require 'net/https'
6
+ require 'bundler/setup'
7
+ require 'nokogiri'
8
+ require 'active_support/core_ext/string'
9
+ require 'yaml'
10
+
11
+ uri = URI.parse(SOURCE)
12
+
13
+ http = Net::HTTP.new(uri.host, uri.port)
14
+ http.use_ssl = true
15
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
16
+ request = Net::HTTP::Get.new(uri.request_uri)
17
+ response = http.request(request)
18
+
19
+ html = Nokogiri::HTML.parse(response.body)
20
+ versions = {}
21
+
22
+ html.css('div.article-body table').each do |table|
23
+ section_head = table.previous_element
24
+ # Find the section title in the previous elements
25
+ until section_head.text[/\w+/][/^[A-Z]+$/]
26
+ section_head = section_head.previous_element
27
+ end
28
+ section = section_head.text[/\w+/].downcase.to_sym
29
+ versions[section] = {}
30
+ table.css('tr')[1..-1].each do |row|
31
+ version, method = row.css('td').map(&:text)
32
+ method = method[/\w+/]
33
+ next if method.blank?
34
+ method = method.underscore.to_sym
35
+ versions[section][method] = version.to_i
36
+ end
37
+ end
38
+
39
+ yaml = YAML.load_file('api_versions.yml')
40
+ yaml[:admin] = versions
41
+ File.open('api_versions.yml', 'w') { |f| f << YAML.dump(yaml) }