gooddata_marketo 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +131 -0
- data/README.md +207 -0
- data/bin/Gemfile +10 -0
- data/bin/auth.json +17 -0
- data/bin/main.rb +0 -0
- data/bin/process.rbx +541 -0
- data/examples/all_lead_changes.rb +119 -0
- data/examples/all_leads.rb +249 -0
- data/examples/lead_changes_to_ads.rb +63 -0
- data/gooddata_marketo.gemspec +24 -0
- data/gooddata_marketo_gem.zip +0 -0
- data/lib/gooddata_marketo.rb +24 -0
- data/lib/gooddata_marketo/adapters/rest.rb +287 -0
- data/lib/gooddata_marketo/client.rb +373 -0
- data/lib/gooddata_marketo/data/activity_types.rb +104 -0
- data/lib/gooddata_marketo/data/reserved_sql_keywords.rb +205 -0
- data/lib/gooddata_marketo/helpers/s3.rb +141 -0
- data/lib/gooddata_marketo/helpers/stringwizard.rb +32 -0
- data/lib/gooddata_marketo/helpers/table.rb +323 -0
- data/lib/gooddata_marketo/helpers/webdav.rb +118 -0
- data/lib/gooddata_marketo/loads.rb +235 -0
- data/lib/gooddata_marketo/models/campaigns.rb +57 -0
- data/lib/gooddata_marketo/models/channels.rb +30 -0
- data/lib/gooddata_marketo/models/child/activity.rb +104 -0
- data/lib/gooddata_marketo/models/child/criteria.rb +17 -0
- data/lib/gooddata_marketo/models/child/lead.rb +118 -0
- data/lib/gooddata_marketo/models/child/mobj.rb +68 -0
- data/lib/gooddata_marketo/models/etl.rb +75 -0
- data/lib/gooddata_marketo/models/leads.rb +493 -0
- data/lib/gooddata_marketo/models/load.rb +17 -0
- data/lib/gooddata_marketo/models/mobjects.rb +121 -0
- data/lib/gooddata_marketo/models/streams.rb +137 -0
- data/lib/gooddata_marketo/models/tags.rb +35 -0
- data/lib/gooddata_marketo/models/validate.rb +46 -0
- metadata +177 -0
Binary file
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module GoodDataMarketo
|
4
|
+
VERSION = "0.0.1"
|
5
|
+
|
6
|
+
class << self
|
7
|
+
|
8
|
+
attr_accessor :logging
|
9
|
+
|
10
|
+
def client(config = {})
|
11
|
+
GoodDataMarketo.logging = false
|
12
|
+
GoodDataMarketo::Client.new(config)
|
13
|
+
end
|
14
|
+
|
15
|
+
def freeze(*args) # Bloc freeze args.
|
16
|
+
args.each(&:freeze).freeze
|
17
|
+
end
|
18
|
+
|
19
|
+
alias :connect :client
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
require 'gooddata_marketo/client'
|
@@ -0,0 +1,287 @@
|
|
1
|
+
require 'gooddata_marketo/models/child/lead'
|
2
|
+
|
3
|
+
require 'rest_client'
|
4
|
+
require 'json'
|
5
|
+
require 'csv'
|
6
|
+
|
7
|
+
class GoodDataMarketo::RESTAdapter
|
8
|
+
|
9
|
+
attr_accessor :token
|
10
|
+
attr_accessor :token_timer
|
11
|
+
|
12
|
+
def initialize config = {}
|
13
|
+
|
14
|
+
@api_limit = MARKETO_API_LIMIT
|
15
|
+
@lead_list_dump = config[:file] || LEAD_LIST_DUMP_CSV || 'marketo_leads_dump.csv'
|
16
|
+
@marketo_subdomain = config[:subdomain] || MARKETO_SUBDOMAIN
|
17
|
+
@marketo_rest_secret = config[:secret] || config[:user] || MARKETO_REST_SECRET
|
18
|
+
@marketo_rest_id = config[:id] || config[:user] || MARKETO_REST_ID
|
19
|
+
@marketo_domain = "https://#{@marketo_subdomain}.mktorest.com"
|
20
|
+
@webdav = config[:webdav]
|
21
|
+
@lead_list_ids = []
|
22
|
+
@token = self.get_token unless config[:token]
|
23
|
+
@token_uri = "?access_token=#{@token}"
|
24
|
+
|
25
|
+
# Example endpoint = "/rest/v1/lists.json"
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
# If token has existed for more than 6000 seconds, refresh it.
|
30
|
+
def refresh_token?
|
31
|
+
if (@token_timer + 6000) < Time.now
|
32
|
+
self.get_token
|
33
|
+
true
|
34
|
+
else
|
35
|
+
false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def get url
|
40
|
+
@api_limit -= 1
|
41
|
+
begin
|
42
|
+
puts "#{Time.now} => REST:GET (#{url})" if GoodDataMarketo.logging
|
43
|
+
response = RestClient.get url
|
44
|
+
rescue Exception => exc
|
45
|
+
puts exc if GoodDataMarketo.logging
|
46
|
+
puts "#{Time.now} => Possible API Limit reached, last request was ##{@api_limit} of #{MARKETO_API_LIMIT}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def get_lead_lists next_page_token = nil
|
51
|
+
|
52
|
+
endpoint = "/rest/v1/lists.json"
|
53
|
+
url = @marketo_domain + endpoint + @token_uri
|
54
|
+
|
55
|
+
if next_page_token
|
56
|
+
endpoint = "/rest/v1/lists.json"
|
57
|
+
url = @marketo_domain + endpoint + @token_uri + "&nextPageToken="+next_page_token
|
58
|
+
end
|
59
|
+
|
60
|
+
response = self.get url
|
61
|
+
|
62
|
+
json = JSON.parse(response.body, :symbolize_names => true)
|
63
|
+
|
64
|
+
json[:result].each { |m| @lead_list_ids << m[:id] }
|
65
|
+
|
66
|
+
next_page_token = json[:nextPageToken]
|
67
|
+
|
68
|
+
if next_page_token
|
69
|
+
self.get_lead_lists next_page_token
|
70
|
+
else
|
71
|
+
@lead_list_ids
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
def get_multiple_leads config = {}
|
77
|
+
|
78
|
+
self.refresh_token?
|
79
|
+
|
80
|
+
ids = config[:ids] || config[:values] || [28567,30885,32240,37161,40832]
|
81
|
+
domain = @marketo_domain
|
82
|
+
parameters = "&filterType=id"+"&filterValues="+ids.join(',')
|
83
|
+
endpoint= "/rest/v1/leads.json"
|
84
|
+
url = domain + endpoint + @token_uri + parameters
|
85
|
+
|
86
|
+
self.get url
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
def get_all_leads config = {}
|
91
|
+
|
92
|
+
self.refresh_token?
|
93
|
+
|
94
|
+
# Check if we already have a leads list, if so, download it.
|
95
|
+
self.load_lists
|
96
|
+
|
97
|
+
# Check if there was a stream broken while downloading a specific list.
|
98
|
+
self.load_last_page
|
99
|
+
|
100
|
+
# Check if there was a partial ID dump available on WebDAV, if so download it..
|
101
|
+
self.load_id_dump
|
102
|
+
|
103
|
+
@fields = config[:fields] || 'id'
|
104
|
+
|
105
|
+
# For each of the lead list ids, download their respective leads by id.
|
106
|
+
@csv = CSV.open(@lead_list_dump,'a+')
|
107
|
+
|
108
|
+
lists = @lead_list_ids
|
109
|
+
|
110
|
+
lists.each do |list|
|
111
|
+
|
112
|
+
id = @lead_list_ids.shift
|
113
|
+
|
114
|
+
domain = @marketo_domain
|
115
|
+
parameters = "&fields=#{@fields}"
|
116
|
+
endpoint= "/rest/v1/list/#{id}/leads.json"
|
117
|
+
url = domain + endpoint + @token_uri + parameters
|
118
|
+
|
119
|
+
# Conditional recursive function ends if nextPageToken is not available in response. Writes results to CSV.
|
120
|
+
def rest_lead_stream url, list_id
|
121
|
+
|
122
|
+
response = self.get url
|
123
|
+
json = JSON.parse(response.body, :symbolize_names => true)
|
124
|
+
results = json[:result]
|
125
|
+
|
126
|
+
if results
|
127
|
+
results.each { |result| @csv << [result[:id]] }
|
128
|
+
@csv.flush
|
129
|
+
puts "#{Time.now} => REST:leadList:#{list_id}:Results:#{results.length}" if GoodDataMarketo.logging
|
130
|
+
end
|
131
|
+
|
132
|
+
next_page_token = json[:nextPageToken]
|
133
|
+
|
134
|
+
# If there is another page, remember it and then attempt the next load.
|
135
|
+
if next_page_token
|
136
|
+
|
137
|
+
self.remember_next_page :token => token, :list => list_id
|
138
|
+
domain = @marketo_domain
|
139
|
+
parameters = "&fields=#{@fields}"
|
140
|
+
endpoint= "/rest/v1/list/#{list_id}/leads.json"
|
141
|
+
url = domain + endpoint + @token_uri + parameters + "&nextPageToken=" + next_page_token
|
142
|
+
rest_lead_stream url, list_id
|
143
|
+
|
144
|
+
else
|
145
|
+
|
146
|
+
# Update the local and remote lead lists
|
147
|
+
File.open('lead_list_ids.json','w'){ |f| JSON.dump(@lead_list_ids, f) }
|
148
|
+
@webdav.upload('lead_list_ids.json')
|
149
|
+
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
# While on this list, check to see if it failed working on it before and resume where it left off.
|
155
|
+
if @next_page_token_list == list
|
156
|
+
|
157
|
+
puts "#{Time.now} => REST(Resumed):leadList:#{list}:NextPageToken:#{@next_page_token}" if GoodDataMarketo.logging
|
158
|
+
domain = @marketo_domain
|
159
|
+
parameters = "" #"&fields=#{fields}"
|
160
|
+
endpoint= "/rest/v1/list/#{list}/leads.json"
|
161
|
+
url = domain + endpoint + @token_uri + parameters + "&nextPageToken=" + @next_page_token
|
162
|
+
|
163
|
+
rest_lead_stream url, list
|
164
|
+
|
165
|
+
else
|
166
|
+
|
167
|
+
rest_lead_stream url, list
|
168
|
+
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
# Remove lists from WebDav & Local, upload Marketo Ids Dump to webdav.
|
174
|
+
self.clean
|
175
|
+
|
176
|
+
# Update the etl controller that an update as occured.
|
177
|
+
self.resolve_with_etl_controller
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
def get_all_lead_ids config = {}
|
182
|
+
config[:fields] = 'id'
|
183
|
+
self.get_all_leads config
|
184
|
+
end
|
185
|
+
|
186
|
+
def get_all_lead_emails config = {}
|
187
|
+
config[:fields] = 'email'
|
188
|
+
self.get_all_leads config
|
189
|
+
end
|
190
|
+
|
191
|
+
def load_last_page
|
192
|
+
|
193
|
+
if @webdav.include? 'lead_list_next_page_token.json'
|
194
|
+
next_page_token_json = @webdav.download 'lead_list_next_page_token.json'
|
195
|
+
json = JSON.parse(next_page_token_json)
|
196
|
+
elsif File.exists? 'lead_list_next_page_token.json'
|
197
|
+
json = JSON.parse( IO.read('lead_list_next_page_token.json'))
|
198
|
+
else
|
199
|
+
json = {}
|
200
|
+
end
|
201
|
+
|
202
|
+
@next_page_token = json[:token]
|
203
|
+
@next_page_token_list = json[:list]
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
def load_id_dump
|
208
|
+
# Check if the lists leads have already been dumped. Append to it.
|
209
|
+
if @webdav.exists? @lead_list_dump
|
210
|
+
file = @webdav.download @lead_list_dump
|
211
|
+
puts "#{Time.now} => WEBDAV:ID_DUMP:Load" if GoodDataMarketo.logging
|
212
|
+
f = File.open(@lead_list_dump,'w')
|
213
|
+
f.write(file)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def load_lists
|
218
|
+
if @webdav.include? 'lead_list_ids.json'
|
219
|
+
lists = @webdav.download 'lead_list_ids.json'
|
220
|
+
@lead_list_ids = JSON.parse(lists)
|
221
|
+
puts "#{Time.now} => WEBDAV:ListsLoaded:#{@lead_list_ids.length}:lead_list_ids.json" if GoodDataMarketo.logging
|
222
|
+
else
|
223
|
+
self.get_lead_lists
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def remember_next_page config = {}
|
228
|
+
File.open('lead_list_next_page_token.json','w'){ |f| JSON.dump(config, f) }
|
229
|
+
@webdav.upload('lead_list_next_page_token.json')
|
230
|
+
end
|
231
|
+
|
232
|
+
def clean
|
233
|
+
|
234
|
+
lli = 'lead_list_ids.json'
|
235
|
+
llnpt = 'lead_list_next_page_token.json'
|
236
|
+
|
237
|
+
# Remove list ids from webdav and local
|
238
|
+
if @webdav.exists?(lli)
|
239
|
+
@webdav.delete(lli)
|
240
|
+
end
|
241
|
+
|
242
|
+
if @webdav.exists?(llnpt)
|
243
|
+
@webdav.delete(llnpt)
|
244
|
+
end
|
245
|
+
|
246
|
+
File.delete(lli) if File.exists? lli
|
247
|
+
File.delete(llnpt) if File.exists? llnpt
|
248
|
+
|
249
|
+
# Upload marketo_dump to webdav for backup.
|
250
|
+
@webdav.upload(@lead_list_dump)
|
251
|
+
|
252
|
+
end
|
253
|
+
|
254
|
+
def get_token
|
255
|
+
|
256
|
+
|
257
|
+
domain = @marketo_domain
|
258
|
+
endpoint = "/identity/oauth/token?grant_type=client_credentials"
|
259
|
+
url = domain + endpoint + "&client_id=" + @marketo_rest_id + "&client_secret=" + @marketo_rest_secret
|
260
|
+
response = self.get url
|
261
|
+
results = JSON.parse(response.body)
|
262
|
+
access_token = results["access_token"]
|
263
|
+
@token_timer = Time.now
|
264
|
+
@token = access_token
|
265
|
+
access_token
|
266
|
+
|
267
|
+
end
|
268
|
+
|
269
|
+
def usage
|
270
|
+
domain = @marketo_domain
|
271
|
+
endpoint = "/rest/v1/stats/usage.json"
|
272
|
+
url = domain + endpoint + @token_uri
|
273
|
+
|
274
|
+
# Note this is the only rest call which does not use the log so as not to print the keys to log.
|
275
|
+
response = RestClient.get url
|
276
|
+
|
277
|
+
#Parse reponse and return only access token
|
278
|
+
results = JSON.parse(response.body)['result']
|
279
|
+
end
|
280
|
+
|
281
|
+
def resolve_with_etl_controller
|
282
|
+
|
283
|
+
puts 'Complete'
|
284
|
+
# Here it would update the log that a full upload as occured.
|
285
|
+
end
|
286
|
+
|
287
|
+
end
|
@@ -0,0 +1,373 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
begin
|
4
|
+
verbose, $VERBOSE = $VERBOSE, nil
|
5
|
+
|
6
|
+
dependencies = ['aws-sdk -v 1.61.0',
|
7
|
+
'savon -v 2.8.0',
|
8
|
+
'rubyntlm -v 0.3.2',
|
9
|
+
'gooddata -v 0.6.11',
|
10
|
+
'rest-client -v 1.7.2',
|
11
|
+
'pmap -v 1.0.2']
|
12
|
+
|
13
|
+
puts "#{Time.now} => CheckDependencies: #{dependencies.join(', ')}" if GoodDataMarketo.logging
|
14
|
+
dependencies.each do |dep|
|
15
|
+
begin
|
16
|
+
gem dep.split(' ').first
|
17
|
+
rescue LoadError
|
18
|
+
puts "Installing dependency #{dep}..."
|
19
|
+
system("gem install #{dep}")
|
20
|
+
Gem.clear_paths
|
21
|
+
next
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'openssl'
|
26
|
+
require 'savon'
|
27
|
+
require 'json'
|
28
|
+
require 'timeout'
|
29
|
+
require 'date'
|
30
|
+
require 'aws-sdk'
|
31
|
+
require 'pmap'
|
32
|
+
require 'gooddata'
|
33
|
+
require 'gooddata_datawarehouse'
|
34
|
+
|
35
|
+
require 'gooddata_marketo/loads'
|
36
|
+
require 'gooddata_marketo/helpers/stringwizard'
|
37
|
+
require 'gooddata_marketo/helpers/table'
|
38
|
+
require 'gooddata_marketo/helpers/webdav'
|
39
|
+
require 'gooddata_marketo/helpers/s3'
|
40
|
+
|
41
|
+
require 'gooddata_marketo/models/leads'
|
42
|
+
require 'gooddata_marketo/models/validate'
|
43
|
+
require 'gooddata_marketo/models/campaigns'
|
44
|
+
require 'gooddata_marketo/models/channels'
|
45
|
+
require 'gooddata_marketo/models/mobjects'
|
46
|
+
require 'gooddata_marketo/models/streams'
|
47
|
+
require 'gooddata_marketo/models/tags'
|
48
|
+
|
49
|
+
require 'gooddata_marketo/adapters/rest'
|
50
|
+
|
51
|
+
require 'gooddata_marketo/data/activity_types'
|
52
|
+
require 'gooddata_marketo/data/reserved_sql_keywords'
|
53
|
+
|
54
|
+
ensure
|
55
|
+
$VERBOSE = verbose
|
56
|
+
end
|
57
|
+
|
58
|
+
class GoodDataMarketo::Client
|
59
|
+
|
60
|
+
DEFAULT_CONFIG = {
|
61
|
+
wsdl: 'http://app.marketo.com/soap/mktows/2_4?WSDL',
|
62
|
+
read_timeout: 500,
|
63
|
+
open_timeout: 500,
|
64
|
+
namespace_identifier: :ns1,
|
65
|
+
env_namespace: 'SOAP-ENV',
|
66
|
+
namespaces: { 'xmlns:ns1' => 'http://www.marketo.com/mktows/' },
|
67
|
+
pretty_print_xml: true,
|
68
|
+
api_subdomain: '363-IXI-287',
|
69
|
+
api_version: '2_7',
|
70
|
+
log: false,
|
71
|
+
log_level: :debug
|
72
|
+
}
|
73
|
+
|
74
|
+
attr_accessor :load
|
75
|
+
attr_accessor :webdav
|
76
|
+
attr_accessor :api_limit
|
77
|
+
attr_accessor :activity_types
|
78
|
+
|
79
|
+
def initialize(config = {})
|
80
|
+
|
81
|
+
GoodDataMarketo.logging = true if config[:log]
|
82
|
+
|
83
|
+
@api_limit = config[:api_limit] || MARKETO_API_LIMIT
|
84
|
+
|
85
|
+
@loads = nil # Default is no jobs will be active.
|
86
|
+
@load = nil # No job is currently set.
|
87
|
+
@leads = nil
|
88
|
+
|
89
|
+
config = DEFAULT_CONFIG.merge(config)
|
90
|
+
|
91
|
+
@api_version = config.delete(:api_version).freeze
|
92
|
+
@subdomain = config.delete(:api_subdomain).freeze
|
93
|
+
@webdav = config.delete(:webdav)
|
94
|
+
@logger = config.delete(:logger)
|
95
|
+
|
96
|
+
@activity_types = ActivityTypes.new.values
|
97
|
+
|
98
|
+
user_id = config.delete(:user_id)
|
99
|
+
encryption_key = config.delete(:encryption_key)
|
100
|
+
|
101
|
+
@auth = AuthHeader.new(user_id, encryption_key)
|
102
|
+
|
103
|
+
@wsdl = "http://app.marketo.com/soap/mktows/#{@api_version}?WSDL".freeze
|
104
|
+
@endpoint = "https://#{@subdomain}.mktoapi.com/soap/mktows/#{@api_version}".freeze
|
105
|
+
|
106
|
+
#Create SOAP Header
|
107
|
+
@savon = Savon.client(config.merge(endpoint: @endpoint))
|
108
|
+
|
109
|
+
if GoodDataMarketo.logging
|
110
|
+
puts use = self.usage
|
111
|
+
puts "#{Time.now} => Marketo:SOAP/REST:Used of #{MARKETO_API_LIMIT}"
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_rest
|
118
|
+
puts "#{Time.now} => SETUP: Connected to Marketo REST API:#{@subdomain}" if GoodDataMarketo.logging
|
119
|
+
begin
|
120
|
+
self.usage
|
121
|
+
true
|
122
|
+
rescue
|
123
|
+
false
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_soap
|
128
|
+
puts "#{Time.now} => SETUP: Connected to Marketo SOAP API:#{@subdomain}" if GoodDataMarketo.logging
|
129
|
+
begin
|
130
|
+
self.leads.get_by_email('test@test.com')
|
131
|
+
true
|
132
|
+
rescue
|
133
|
+
false
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def configuration
|
138
|
+
DEFAULT_CONFIG
|
139
|
+
end
|
140
|
+
|
141
|
+
def call(web_method, params, config = {}) #:nodoc:
|
142
|
+
@api_limit -= 1
|
143
|
+
@params = params
|
144
|
+
@timeouts = 0
|
145
|
+
@error = nil
|
146
|
+
|
147
|
+
# If the time limit is up, sect the process to wait
|
148
|
+
if @api_limit < 1
|
149
|
+
|
150
|
+
now = DateTime.now.utc
|
151
|
+
cst = now.in_time_zone('Central Time (US & Canada)')
|
152
|
+
delay = (24 - cst.hours) * 2
|
153
|
+
delay = 1 if delay == 0
|
154
|
+
|
155
|
+
sleep (delay * 60 * 60)
|
156
|
+
puts "#{Time.now} => API_LIMIT:Sleeping: #{delay}"
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
begin
|
161
|
+
|
162
|
+
def timed_call web_method, params, config = {}
|
163
|
+
|
164
|
+
puts "#{Time.now} => API Limit: #{@api_limit}" if GoodDataMarketo.logging
|
165
|
+
puts "#{Time.now} => Call: #{web_method}: #{@params.to_s}" if GoodDataMarketo.logging
|
166
|
+
|
167
|
+
Timeout.timeout(config[:timeout] || 4999) do
|
168
|
+
|
169
|
+
response = @savon.call(
|
170
|
+
web_method,
|
171
|
+
message: params,
|
172
|
+
soap_header: { 'ns1:AuthenticationHeader' => @auth.signature }
|
173
|
+
).to_hash
|
174
|
+
|
175
|
+
# Add control flow to the root call because Marketo API changes for structure for just getLeadActivities
|
176
|
+
if response[:success_get_lead_activity]
|
177
|
+
response[:success_get_lead_activity][:lead_activity_list]
|
178
|
+
else
|
179
|
+
response[response.keys.first][:result]
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
timed_call web_method, @params, config
|
187
|
+
|
188
|
+
rescue Timeout::Error => e
|
189
|
+
@timeouts += 1
|
190
|
+
|
191
|
+
forward_days = config[:forward_days] || 30
|
192
|
+
|
193
|
+
def move_forward_30_days original_time, days
|
194
|
+
smart_time = Date.parse(original_time) + days
|
195
|
+
Time.parse(smart_time.to_s).to_s
|
196
|
+
end
|
197
|
+
|
198
|
+
if @params[:start_position]
|
199
|
+
|
200
|
+
last_created_at = @params[:start_position][:last_created_at]
|
201
|
+
latest_created_at = @params[:start_position][:latest_created_at]
|
202
|
+
oldest_created_at = @params[:start_position][:oldest_created_at]
|
203
|
+
last_updated_at = @params[:start_position][:last_updated_at]
|
204
|
+
|
205
|
+
if oldest_created_at
|
206
|
+
new_time = move_forward_30_days(oldest_created_at, forward_days)
|
207
|
+
@params[:start_position][:oldest_created_at] = new_time
|
208
|
+
elsif last_created_at
|
209
|
+
new_time = move_forward_30_days(last_created_at, forward_days)
|
210
|
+
@params[:start_position][:last_created_at] = new_time
|
211
|
+
elsif latest_created_at
|
212
|
+
new_time = move_forward_30_days(latest_created_at, forward_days)
|
213
|
+
@params[:start_position][:latest_created_at] = new_time
|
214
|
+
elsif last_updated_at
|
215
|
+
new_time = move_forward_30_days(last_updated_at, forward_days)
|
216
|
+
@params[:start_position][:last_updated_at] = new_time
|
217
|
+
else
|
218
|
+
exit
|
219
|
+
end
|
220
|
+
|
221
|
+
puts "#{Time.now} => #{e}:Retrying requested date +#{forward_days} days:attempts:#{@timeouts}/12" if GoodDataMarketo.logging
|
222
|
+
retry unless @timeouts > 12
|
223
|
+
|
224
|
+
else
|
225
|
+
exit
|
226
|
+
end
|
227
|
+
|
228
|
+
end
|
229
|
+
|
230
|
+
rescue Exception => e
|
231
|
+
puts @error = e
|
232
|
+
@logger.log(e) if @logger
|
233
|
+
nil
|
234
|
+
end
|
235
|
+
|
236
|
+
def died(json = nil)
|
237
|
+
|
238
|
+
if json
|
239
|
+
file = File.open('marketo_connector_log.json','wb')
|
240
|
+
file.write(json.to_json)
|
241
|
+
exit
|
242
|
+
else
|
243
|
+
json = File.open('marketo_connector_log.json', 'r')
|
244
|
+
json.to_h
|
245
|
+
end
|
246
|
+
|
247
|
+
end
|
248
|
+
|
249
|
+
def stream(web_method, params, config = {})
|
250
|
+
|
251
|
+
# If the stream is part of a load.
|
252
|
+
|
253
|
+
if self.load
|
254
|
+
"#{Time.now} => Load:#{self.load.json[:type]}:#{self.load.json[:method]}" if GoodDataMarketo.logging
|
255
|
+
end
|
256
|
+
|
257
|
+
puts "#{Time.now} => Stream: #{web_method}: #{params.to_s}" if GoodDataMarketo.logging
|
258
|
+
|
259
|
+
safe = config[:safe] || true
|
260
|
+
@timeouts = 0
|
261
|
+
|
262
|
+
def safe_stream web_method, params, config
|
263
|
+
begin
|
264
|
+
#Timeout.timeout(config[:timeout] || 18000) do
|
265
|
+
GoodDataMarketo::Stream.new web_method, params, :client => self
|
266
|
+
#end
|
267
|
+
rescue Timeout::Error => e
|
268
|
+
@timeouts += 1
|
269
|
+
puts e if GoodDataMarketo.logging
|
270
|
+
params[:timeouts] = @timeouts
|
271
|
+
self.load.log('TIMEOUT') if self.load
|
272
|
+
self.died params
|
273
|
+
end
|
274
|
+
|
275
|
+
end
|
276
|
+
|
277
|
+
if safe
|
278
|
+
safe_stream web_method, params, config
|
279
|
+
else
|
280
|
+
GoodDataMarketo::Stream.new web_method, params, :client => self
|
281
|
+
end
|
282
|
+
|
283
|
+
|
284
|
+
end
|
285
|
+
|
286
|
+
class AuthHeader #:nodoc:
|
287
|
+
DIGEST = OpenSSL::Digest.new('sha1')
|
288
|
+
private_constant :DIGEST
|
289
|
+
|
290
|
+
def initialize(user_id, encryption_key)
|
291
|
+
if user_id.nil? || encryption_key.nil?
|
292
|
+
raise ArgumentError, ":user_id and :encryption_key required"
|
293
|
+
end
|
294
|
+
|
295
|
+
@user_id = user_id
|
296
|
+
@encryption_key = encryption_key
|
297
|
+
end
|
298
|
+
|
299
|
+
attr_reader :user_id
|
300
|
+
|
301
|
+
# Compute the HMAC signature and return it.
|
302
|
+
def signature
|
303
|
+
time = Time.now
|
304
|
+
{
|
305
|
+
mktowsUserId: user_id,
|
306
|
+
requestTimestamp: time.to_s,
|
307
|
+
requestSignature: hmac(time),
|
308
|
+
}
|
309
|
+
end
|
310
|
+
|
311
|
+
private
|
312
|
+
def hmac(time)
|
313
|
+
OpenSSL::HMAC.hexdigest(
|
314
|
+
DIGEST,
|
315
|
+
@encryption_key,
|
316
|
+
"#{time}#{user_id}"
|
317
|
+
)
|
318
|
+
end
|
319
|
+
|
320
|
+
end
|
321
|
+
|
322
|
+
private_constant :AuthHeader
|
323
|
+
|
324
|
+
def rest
|
325
|
+
GoodDataMarketo::RESTAdapter.new :webdav => @webdav
|
326
|
+
end
|
327
|
+
|
328
|
+
def get_all_leads config = {}
|
329
|
+
rest = GoodDataMarketo::RESTAdapter.new :webdav => @webdav
|
330
|
+
rest.get_all_leads
|
331
|
+
end
|
332
|
+
|
333
|
+
alias :write_all_lead_ids_to_csv :get_all_leads
|
334
|
+
|
335
|
+
def set_load load
|
336
|
+
@load = load
|
337
|
+
end
|
338
|
+
|
339
|
+
def leads # http://developers.marketo.com/documentation/soap/getleadchanges/
|
340
|
+
GoodDataMarketo::Leads.new :client => self
|
341
|
+
end
|
342
|
+
|
343
|
+
def usage(config = {})
|
344
|
+
rest = GoodDataMarketo::RESTAdapter.new :webdav => @webdav
|
345
|
+
rest.usage
|
346
|
+
end
|
347
|
+
|
348
|
+
def campaigns # http://developers.marketo.com/documentation/soap/campaigns/
|
349
|
+
GoodDataMarketo::Campaigns.new :client => self
|
350
|
+
end
|
351
|
+
|
352
|
+
def mobjects # http://developers.marketo.com/documentation/soap/getmobjects/
|
353
|
+
GoodDataMarketo::MObjects.new :client => self
|
354
|
+
end
|
355
|
+
|
356
|
+
def operations
|
357
|
+
@savon.operations
|
358
|
+
end
|
359
|
+
|
360
|
+
def loads(config = {})
|
361
|
+
self.load = true
|
362
|
+
@loads = GoodDataMarketo::Loads.new config
|
363
|
+
end
|
364
|
+
|
365
|
+
def load=(load)
|
366
|
+
@load = load
|
367
|
+
end
|
368
|
+
|
369
|
+
alias :objects :mobjects
|
370
|
+
|
371
|
+
alias :lead :leads
|
372
|
+
|
373
|
+
end
|