soapy_cake 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +24 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +56 -0
- data/Rakefile +6 -0
- data/api_versions.yml +103 -0
- data/circle.yml +7 -0
- data/lib/soapy_cake.rb +8 -0
- data/lib/soapy_cake/client.rb +145 -0
- data/lib/soapy_cake/version.rb +3 -0
- data/script/update_versions.rb +41 -0
- data/soapy_cake.gemspec +30 -0
- data/spec/fixtures/vcr_cassettes/client_new_account_statuses.yml +1200 -0
- data/spec/fixtures/vcr_cassettes/client_new_advertisers.yml +1202 -0
- data/spec/fixtures/vcr_cassettes/client_new_affiliate_tags.yml +1200 -0
- data/spec/fixtures/vcr_cassettes/client_new_affiliate_tiers.yml +1201 -0
- data/spec/fixtures/vcr_cassettes/client_new_billing_cycles.yml +1200 -0
- data/spec/fixtures/vcr_cassettes/client_new_cap_intervals.yml +1201 -0
- data/spec/fixtures/vcr_cassettes/client_new_cap_types.yml +1199 -0
- data/spec/fixtures/vcr_cassettes/client_new_countries.yml +1515 -0
- data/spec/fixtures/vcr_cassettes/client_new_currencies.yml +1199 -0
- data/spec/fixtures/vcr_cassettes/client_new_empty_response.yml +1200 -0
- data/spec/fixtures/vcr_cassettes/client_new_empty_response_offer_summary.yml +865 -0
- data/spec/fixtures/vcr_cassettes/client_new_languages.yml +1199 -0
- data/spec/fixtures/vcr_cassettes/client_new_media_types.yml +1215 -0
- data/spec/fixtures/vcr_cassettes/client_new_offer_statuses.yml +1202 -0
- data/spec/fixtures/vcr_cassettes/client_new_offer_types.yml +1201 -0
- data/spec/fixtures/vcr_cassettes/client_new_payment_settings.yml +1203 -0
- data/spec/fixtures/vcr_cassettes/client_new_payment_types.yml +1204 -0
- data/spec/fixtures/vcr_cassettes/client_new_price_formats.yml +1202 -0
- data/spec/fixtures/vcr_cassettes/client_new_roles.yml +1203 -0
- data/spec/fixtures/vcr_cassettes/client_new_verticals.yml +1197 -0
- data/spec/fixtures/vcr_cassettes/client_new_with_username_and_password.yml +1198 -0
- data/spec/fixtures/vcr_cassettes/client_sekken_client_caches_results.yml +1261 -0
- data/spec/lib/soapy_cake/client_spec.rb +164 -0
- data/spec/spec_helper.rb +36 -0
- 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
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.0.0-p451
|
data/Gemfile
ADDED
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
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
data/lib/soapy_cake.rb
ADDED
@@ -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,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) }
|