embulk-input-marketo 0.2.2 → 0.2.3
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.
- checksums.yaml +4 -4
- data/.travis.yml +3 -0
- data/CHANGELOG.md +5 -0
- data/embulk-input-marketo.gemspec +1 -1
- data/gemfiles/embulk-0.6.26 +4 -0
- data/lib/embulk/input/marketo/activity_log.rb +5 -1
- data/lib/embulk/input/marketo/base.rb +0 -8
- data/lib/embulk/input/marketo/lead.rb +30 -18
- data/lib/embulk/input/marketo/timeslice.rb +59 -2
- data/lib/embulk/input/marketo_api/soap/base.rb +3 -4
- data/lib/embulk/input/marketo_api/soap/lead.rb +20 -20
- data/test/embulk/input/marketo/test_activity_log.rb +20 -0
- data/test/embulk/input/marketo/test_lead.rb +189 -4
- data/test/embulk/input/marketo_api/soap/test_lead.rb +12 -49
- data/test/run-test.rb +3 -0
- metadata +3 -3
- data/lib/embulk/input/marketo_api/soap/timeslice.rb +0 -44
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5485d5527116194e9cf43129e39a686675038d35
|
4
|
+
data.tar.gz: 932cd341055835466e2022c950babd2c0f7966ae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 31b2aa93cbb0e301b1a71fbf15c0b53f0841558aaeafb8e5d38ce884fd6cd9d34c34bc7d44678d57421e47f10739748b9815e523dffd956a6c9c76d7ebe50e41
|
7
|
+
data.tar.gz: b25b1163d52542cf8f284396f36e428fb55a35f1f4418784b93885cc678cc2fd320d53c98ea6648b8ace4dab920e431e447a60747295cf18e238dbcc2a2a2c76
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
## 0.2.3 - 2015-09-14
|
2
|
+
|
3
|
+
* [enhancement] Catch config error [#33](https://github.com/treasure-data/embulk-input-marketo/pull/33)
|
4
|
+
* [enhancement] Concurrent worker [#31](https://github.com/treasure-data/embulk-input-marketo/pull/31)
|
5
|
+
|
1
6
|
## 0.2.2 - 2015-09-08
|
2
7
|
|
3
8
|
* [fixed] Fix handling for activity_date_time [#32](https://github.com/treasure-data/embulk-input-marketo/pull/32)
|
@@ -41,36 +41,48 @@ module Embulk
|
|
41
41
|
columns
|
42
42
|
end
|
43
43
|
|
44
|
+
def init
|
45
|
+
@last_updated_at = task[:last_updated_at]
|
46
|
+
@columns = task[:columns]
|
47
|
+
@ranges = task[:ranges][index]
|
48
|
+
@soap = MarketoApi.soap_client(task, target)
|
49
|
+
end
|
50
|
+
|
44
51
|
def run
|
45
|
-
count = 0
|
46
52
|
from_datetime = task[:from_datetime]
|
47
|
-
to_datetime = task[:to_datetime]
|
53
|
+
to_datetime = task[:to_datetime] || Time.now
|
54
|
+
|
48
55
|
options = {}
|
49
56
|
options[:batch_size] = PREVIEW_COUNT if preview?
|
50
57
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
58
|
+
@ranges.each do |range|
|
59
|
+
soap.each(range, options) do |lead|
|
60
|
+
values = @columns.map do |column|
|
61
|
+
name = column["name"].to_s
|
62
|
+
value = (lead[name] || {})[:value]
|
63
|
+
next unless value
|
56
64
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
65
|
+
case column["type"]
|
66
|
+
when "timestamp"
|
67
|
+
begin
|
68
|
+
Time.parse(value)
|
69
|
+
rescue => e
|
70
|
+
raise ConfigError, "Can't parse as Time '#{value}' (column is #{column["name"]})"
|
71
|
+
end
|
72
|
+
else
|
73
|
+
value
|
74
|
+
end
|
62
75
|
end
|
63
|
-
end
|
64
|
-
|
65
|
-
page_builder.add(values)
|
66
76
|
|
67
|
-
|
68
|
-
|
77
|
+
page_builder.add(values)
|
78
|
+
end
|
69
79
|
end
|
70
80
|
|
71
81
|
page_builder.finish
|
72
82
|
|
73
|
-
commit_report = {
|
83
|
+
commit_report = {
|
84
|
+
from_datetime: to_datetime
|
85
|
+
}
|
74
86
|
return commit_report
|
75
87
|
end
|
76
88
|
end
|
@@ -2,6 +2,8 @@ module Embulk
|
|
2
2
|
module Input
|
3
3
|
module Marketo
|
4
4
|
module Timeslice
|
5
|
+
TIMESLICE_COUNT_PER_TASK = 24
|
6
|
+
|
5
7
|
def self.included(klass)
|
6
8
|
klass.extend ClassMethods
|
7
9
|
end
|
@@ -28,10 +30,20 @@ module Embulk
|
|
28
30
|
from_datetime = config.param(:from_datetime, :string)
|
29
31
|
to_datetime = config.param(:to_datetime, :string, default: Time.now.to_s)
|
30
32
|
|
33
|
+
# check from/to format to parse
|
34
|
+
begin
|
35
|
+
Time.parse(from_datetime)
|
36
|
+
Time.parse(to_datetime)
|
37
|
+
rescue => e
|
38
|
+
# possibly Time.parse fail
|
39
|
+
raise ConfigError, e.message
|
40
|
+
end
|
41
|
+
|
31
42
|
if Time.parse(from_datetime) > Time.parse(to_datetime)
|
32
43
|
raise ConfigError, "config: from_datetime '#{from_datetime}' is later than '#{to_datetime}'."
|
33
44
|
end
|
34
45
|
|
46
|
+
ranges = timeslice(from_datetime, to_datetime, TIMESLICE_COUNT_PER_TASK)
|
35
47
|
task = {
|
36
48
|
endpoint_url: endpoint_url,
|
37
49
|
wsdl_url: config.param(:wsdl, :string, default: "#{endpoint_url}?WSDL"),
|
@@ -39,6 +51,7 @@ module Embulk
|
|
39
51
|
encryption_key: config.param(:encryption_key, :string),
|
40
52
|
from_datetime: from_datetime,
|
41
53
|
to_datetime: to_datetime,
|
54
|
+
ranges: ranges,
|
42
55
|
columns: config.param(:columns, :array)
|
43
56
|
}
|
44
57
|
|
@@ -51,9 +64,53 @@ module Embulk
|
|
51
64
|
columns << Column.new(nil, name, type, column["format"])
|
52
65
|
end
|
53
66
|
|
54
|
-
|
55
|
-
resume(task, columns, 1, &control)
|
67
|
+
resume(task, columns, ranges.size, &control)
|
56
68
|
end
|
69
|
+
|
70
|
+
def timeslice(from, to, count)
|
71
|
+
generate_time_range(from, to).each_slice(count).to_a
|
72
|
+
end
|
73
|
+
|
74
|
+
def generate_time_range(from, to)
|
75
|
+
# e.g. from = 2010-01-01 15:00, to = 2010-01-03 09:30
|
76
|
+
# convert to such array:
|
77
|
+
# [
|
78
|
+
# {from: 2010-01-01 15:00, to: 2010-01-01 16:00},
|
79
|
+
# {from: 2010-01-01 16:00, to: 2010-01-01 17:00},
|
80
|
+
# ...
|
81
|
+
# {from: 2010-01-03 08:00, to: 2010-01-03 09:00},
|
82
|
+
# {from: 2010-01-03 09:00, to: 2010-01-03 09:30},
|
83
|
+
# ]
|
84
|
+
# to fetch data from Marketo API with each day as
|
85
|
+
# desribed on official blog:
|
86
|
+
# http://developers.marketo.com/blog/performance-tuning-api-requests/
|
87
|
+
to ||= Time.now
|
88
|
+
from = Time.parse(from) unless from.is_a?(Time)
|
89
|
+
to = Time.parse(to) unless to.is_a?(Time)
|
90
|
+
|
91
|
+
result = []
|
92
|
+
since = from
|
93
|
+
while since < to
|
94
|
+
next_since = since + 3600
|
95
|
+
if to < next_since
|
96
|
+
next_since = to
|
97
|
+
end
|
98
|
+
result << {
|
99
|
+
"from" => since,
|
100
|
+
"to" => next_since
|
101
|
+
}
|
102
|
+
since = next_since
|
103
|
+
end
|
104
|
+
result
|
105
|
+
end
|
106
|
+
|
107
|
+
def resume(task, columns, count, &control)
|
108
|
+
commit_reports = yield(task, columns, count)
|
109
|
+
|
110
|
+
# all task returns same report as {from_datetime: to_datetime}
|
111
|
+
return commit_reports.first
|
112
|
+
end
|
113
|
+
|
57
114
|
end
|
58
115
|
end
|
59
116
|
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require "savon"
|
2
|
-
require "embulk/input/marketo_api/soap/timeslice"
|
3
2
|
|
4
3
|
module Embulk
|
5
4
|
module Input
|
@@ -80,10 +79,10 @@ module Embulk
|
|
80
79
|
# unretryable error such as Authentication Failed, Invalid Request, etc.
|
81
80
|
raise ConfigError, soap_message
|
82
81
|
end
|
83
|
-
rescue SocketError => e
|
82
|
+
rescue SocketError, Errno::ECONNREFUSED => e
|
84
83
|
# maybe endpoint/wsdl domain was wrong
|
85
|
-
Embulk.logger.debug "
|
86
|
-
raise ConfigError, "
|
84
|
+
Embulk.logger.debug "Connection error: endpoint=#{endpoint} wsdl=#{wsdl}"
|
85
|
+
raise ConfigError, "Connection error: #{e.message} (endpoint is '#{endpoint}')"
|
87
86
|
end
|
88
87
|
end
|
89
88
|
end
|
@@ -5,8 +5,6 @@ module Embulk
|
|
5
5
|
module MarketoApi
|
6
6
|
module Soap
|
7
7
|
class Lead < Base
|
8
|
-
include Timeslice
|
9
|
-
|
10
8
|
# NOTE: batch_size is allowed at 1000, but that takes 2 minutes in 1 request.
|
11
9
|
# We use 250 for the default (about 30 seconds)
|
12
10
|
BATCH_SIZE_DEFAULT = 250
|
@@ -17,28 +15,30 @@ module Embulk
|
|
17
15
|
response.body[:success_describe_m_object][:result][:metadata][:field_list][:field]
|
18
16
|
end
|
19
17
|
|
20
|
-
def each(
|
18
|
+
def each(range, options = {}, &block)
|
21
19
|
# http://developers.marketo.com/documentation/soap/getmultipleleads/
|
22
|
-
|
20
|
+
from = range["from"]
|
21
|
+
to = range["to"]
|
23
22
|
|
24
|
-
|
25
|
-
|
26
|
-
lead_selector: {
|
27
|
-
oldest_updated_at: range[:from].iso8601,
|
28
|
-
latest_updated_at: range[:to].iso8601,
|
29
|
-
},
|
30
|
-
attributes!: {
|
31
|
-
lead_selector: {"xsi:type" => "ns1:LastUpdateAtSelector"}
|
32
|
-
},
|
33
|
-
batch_size: options[:batch_size] || BATCH_SIZE_DEFAULT,
|
34
|
-
}
|
35
|
-
Embulk.logger.info "Fetching from '#{range[:from]}' to '#{range[:to]}'..."
|
23
|
+
from = Time.parse(from) unless from.is_a?(Time)
|
24
|
+
to = Time.parse(to) unless to.is_a?(Time)
|
36
25
|
|
37
|
-
|
26
|
+
request = {
|
27
|
+
lead_selector: {
|
28
|
+
oldest_updated_at: from.iso8601,
|
29
|
+
latest_updated_at: to.iso8601,
|
30
|
+
},
|
31
|
+
attributes!: {
|
32
|
+
lead_selector: {"xsi:type" => "ns1:LastUpdateAtSelector"}
|
33
|
+
},
|
34
|
+
batch_size: options[:batch_size] || BATCH_SIZE_DEFAULT,
|
35
|
+
}
|
36
|
+
Embulk.logger.info "Fetching from '#{from}' to '#{to}'..."
|
38
37
|
|
39
|
-
|
40
|
-
|
41
|
-
|
38
|
+
stream_position = fetch(request, &block)
|
39
|
+
|
40
|
+
while stream_position
|
41
|
+
stream_position = fetch(request.merge(stream_position: stream_position), &block)
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
@@ -141,6 +141,26 @@ module Embulk
|
|
141
141
|
@plugin.run
|
142
142
|
end
|
143
143
|
|
144
|
+
def test_wrong_type
|
145
|
+
any_instance_of(Savon::Client) do |klass|
|
146
|
+
stub(klass).call(:get_lead_changes, message: request) do
|
147
|
+
next_stream_activity_logs_response
|
148
|
+
end
|
149
|
+
end
|
150
|
+
stub(@page_builder).add {}
|
151
|
+
stub(@page_builder).finish {}
|
152
|
+
|
153
|
+
task = task()
|
154
|
+
task[:columns] = [
|
155
|
+
{"name" => "Old Value", "type" => :timestamp}
|
156
|
+
]
|
157
|
+
@plugin = ActivityLog.new(task, nil, nil, @page_builder)
|
158
|
+
|
159
|
+
assert_raise(Embulk::ConfigError) do
|
160
|
+
@plugin.run
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
144
164
|
private
|
145
165
|
|
146
166
|
def request
|
@@ -22,6 +22,9 @@ module Embulk
|
|
22
22
|
|
23
23
|
def setup_plugin
|
24
24
|
@page_builder = Object.new
|
25
|
+
any_instance_of(Embulk::Input::Marketo::Lead) do |klass|
|
26
|
+
stub(klass).index { 0 }
|
27
|
+
end
|
25
28
|
@plugin = Lead.new(task, nil, nil, @page_builder)
|
26
29
|
mute_logger
|
27
30
|
end
|
@@ -47,6 +50,49 @@ module Embulk
|
|
47
50
|
end
|
48
51
|
end
|
49
52
|
|
53
|
+
def test_invalid_datetime_given
|
54
|
+
control = proc {} # dummy
|
55
|
+
|
56
|
+
settings = {
|
57
|
+
endpoint: "https://marketo.example.com",
|
58
|
+
wsdl: "https://marketo.example.com/?wsdl",
|
59
|
+
user_id: "user_id",
|
60
|
+
encryption_key: "TOPSECRET",
|
61
|
+
from_datetime: "invalid time from",
|
62
|
+
to_datetime: "invalid time to",
|
63
|
+
columns: [
|
64
|
+
{"name" => "Name", "type" => "string"},
|
65
|
+
]
|
66
|
+
}
|
67
|
+
config = DataSource[settings.to_a]
|
68
|
+
|
69
|
+
assert_raise(ConfigError) do
|
70
|
+
Lead.transaction(config, &control)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_wrong_type
|
75
|
+
control = proc {} # dummy
|
76
|
+
|
77
|
+
settings = {
|
78
|
+
endpoint: "https://marketo.example.com",
|
79
|
+
wsdl: "https://marketo.example.com/?wsdl",
|
80
|
+
user_id: "user_id",
|
81
|
+
encryption_key: "TOPSECRET",
|
82
|
+
from_datetime: "invalid time from",
|
83
|
+
to_datetime: "invalid time to",
|
84
|
+
columns: [
|
85
|
+
{"name" => "Name", "type" => "timestamp"},
|
86
|
+
]
|
87
|
+
}
|
88
|
+
|
89
|
+
config = DataSource[settings.to_a]
|
90
|
+
|
91
|
+
assert_raise(ConfigError) do
|
92
|
+
Lead.transaction(config, &control)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
50
96
|
class RunTest < self
|
51
97
|
def setup
|
52
98
|
setup_soap
|
@@ -159,13 +205,84 @@ module Embulk
|
|
159
205
|
end
|
160
206
|
end
|
161
207
|
|
208
|
+
class TestTimeslice < self
|
209
|
+
class TestGenerateTimeRange < self
|
210
|
+
def setup
|
211
|
+
super
|
212
|
+
mute_logger
|
213
|
+
end
|
214
|
+
|
215
|
+
data do
|
216
|
+
{
|
217
|
+
"8/1 to 8/2" => ["2015-08-01 00:00:00", "2015-08-02 00:00:00", 24],
|
218
|
+
"over the days" => ["2015-08-01 19:00:00", "2015-08-03 05:00:00", 34],
|
219
|
+
"odd times" => ["2015-08-01 11:11:11", "2015-08-01 22:22:22", 12],
|
220
|
+
}
|
221
|
+
end
|
222
|
+
def test_generate_time_range_by_1hour(data)
|
223
|
+
from, to, count = data
|
224
|
+
range = Lead.generate_time_range(from, to)
|
225
|
+
assert_equal count, range.length
|
226
|
+
end
|
227
|
+
|
228
|
+
def test_if_to_is_nil_use_time_now
|
229
|
+
from = "2000-01-01"
|
230
|
+
now = Time.now
|
231
|
+
stub(Time).now { now }
|
232
|
+
|
233
|
+
range = Lead.generate_time_range(from, nil)
|
234
|
+
assert_equal now, range.last["to"]
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def test_timeslice
|
239
|
+
from = "2015-08-02 20:00:00"
|
240
|
+
to = "2015-08-03 08:08:08"
|
241
|
+
count = 4
|
242
|
+
|
243
|
+
raw_expect = [
|
244
|
+
[
|
245
|
+
{from: "2015-08-02 20:00:00", to: "2015-08-02 21:00:00"},
|
246
|
+
{from: "2015-08-02 21:00:00", to: "2015-08-02 22:00:00"},
|
247
|
+
{from: "2015-08-02 22:00:00", to: "2015-08-02 23:00:00"},
|
248
|
+
{from: "2015-08-02 23:00:00", to: "2015-08-03 00:00:00"},
|
249
|
+
],
|
250
|
+
[
|
251
|
+
{from: "2015-08-03 00:00:00", to: "2015-08-03 01:00:00"},
|
252
|
+
{from: "2015-08-03 01:00:00", to: "2015-08-03 02:00:00"},
|
253
|
+
{from: "2015-08-03 02:00:00", to: "2015-08-03 03:00:00"},
|
254
|
+
{from: "2015-08-03 03:00:00", to: "2015-08-03 04:00:00"},
|
255
|
+
],
|
256
|
+
[
|
257
|
+
{from: "2015-08-03 04:00:00", to: "2015-08-03 05:00:00"},
|
258
|
+
{from: "2015-08-03 05:00:00", to: "2015-08-03 06:00:00"},
|
259
|
+
{from: "2015-08-03 06:00:00", to: "2015-08-03 07:00:00"},
|
260
|
+
{from: "2015-08-03 07:00:00", to: "2015-08-03 08:00:00"},
|
261
|
+
],
|
262
|
+
[
|
263
|
+
{from: "2015-08-03 08:00:00", to: "2015-08-03 08:08:08"},
|
264
|
+
]
|
265
|
+
]
|
266
|
+
|
267
|
+
expect = raw_expect.map do |slice|
|
268
|
+
slice.map do |range|
|
269
|
+
{
|
270
|
+
"from" => Time.parse(range[:from]),
|
271
|
+
"to" => Time.parse(range[:to])
|
272
|
+
}
|
273
|
+
end
|
274
|
+
end
|
275
|
+
assert_equal(expect, Lead.timeslice(from, to, count))
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
162
279
|
private
|
163
280
|
|
164
281
|
def request
|
165
282
|
{
|
166
283
|
lead_selector: {
|
167
|
-
oldest_updated_at: timerange.first[
|
168
|
-
latest_updated_at: timerange.first[
|
284
|
+
oldest_updated_at: timerange.first["from"].iso8601,
|
285
|
+
latest_updated_at: timerange.first["to"].iso8601,
|
169
286
|
},
|
170
287
|
attributes!: {lead_selector: {"xsi:type"=>"ns1:LastUpdateAtSelector"}},
|
171
288
|
batch_size: MarketoApi::Soap::Lead::BATCH_SIZE_DEFAULT,
|
@@ -225,8 +342,7 @@ module Embulk
|
|
225
342
|
end
|
226
343
|
|
227
344
|
def timerange
|
228
|
-
|
229
|
-
soap.send(:generate_time_range, from_datetime, to_datetime)
|
345
|
+
Lead.generate_time_range(from_datetime, to_datetime)
|
230
346
|
end
|
231
347
|
|
232
348
|
def task
|
@@ -237,6 +353,7 @@ module Embulk
|
|
237
353
|
encryption_key: "TOPSECRET",
|
238
354
|
from_datetime: from_datetime,
|
239
355
|
to_datetime: to_datetime,
|
356
|
+
ranges: Lead.timeslice(from_datetime, to_datetime, Lead::TIMESLICE_COUNT_PER_TASK),
|
240
357
|
columns: [
|
241
358
|
{"name" => "Name", "type" => "string"},
|
242
359
|
]
|
@@ -260,6 +377,70 @@ module Embulk
|
|
260
377
|
is_dynamic: true,
|
261
378
|
dynamic_field_ref: "leadAttributeList",
|
262
379
|
updated_at: DateTime.parse("2000-01-01 22:22:22")
|
380
|
+
},
|
381
|
+
{
|
382
|
+
name: "FieldInt",
|
383
|
+
description: nil,
|
384
|
+
display_name: "The Name of Field",
|
385
|
+
source_object: "Lead",
|
386
|
+
data_type: "integer",
|
387
|
+
size: nil,
|
388
|
+
is_readonly: false,
|
389
|
+
is_update_blocked: false,
|
390
|
+
is_name: nil,
|
391
|
+
is_primary_key: false,
|
392
|
+
is_custom: true,
|
393
|
+
is_dynamic: true,
|
394
|
+
dynamic_field_ref: "leadAttributeList",
|
395
|
+
updated_at: DateTime.parse("2000-01-01 22:22:22")
|
396
|
+
},
|
397
|
+
{
|
398
|
+
name: "FieldBoolean",
|
399
|
+
description: nil,
|
400
|
+
display_name: "The Name of Field",
|
401
|
+
source_object: "Lead",
|
402
|
+
data_type: "boolean",
|
403
|
+
size: nil,
|
404
|
+
is_readonly: false,
|
405
|
+
is_update_blocked: false,
|
406
|
+
is_name: nil,
|
407
|
+
is_primary_key: false,
|
408
|
+
is_custom: true,
|
409
|
+
is_dynamic: true,
|
410
|
+
dynamic_field_ref: "leadAttributeList",
|
411
|
+
updated_at: DateTime.parse("2000-01-01 22:22:22")
|
412
|
+
},
|
413
|
+
{
|
414
|
+
name: "FieldFloat",
|
415
|
+
description: nil,
|
416
|
+
display_name: "The Name of Field",
|
417
|
+
source_object: "Lead",
|
418
|
+
data_type: "float",
|
419
|
+
size: nil,
|
420
|
+
is_readonly: false,
|
421
|
+
is_update_blocked: false,
|
422
|
+
is_name: nil,
|
423
|
+
is_primary_key: false,
|
424
|
+
is_custom: true,
|
425
|
+
is_dynamic: true,
|
426
|
+
dynamic_field_ref: "leadAttributeList",
|
427
|
+
updated_at: DateTime.parse("2000-01-01 22:22:22")
|
428
|
+
},
|
429
|
+
{
|
430
|
+
name: "FieldString",
|
431
|
+
description: nil,
|
432
|
+
display_name: "The Name of Field",
|
433
|
+
source_object: "Lead",
|
434
|
+
data_type: "string",
|
435
|
+
size: nil,
|
436
|
+
is_readonly: false,
|
437
|
+
is_update_blocked: false,
|
438
|
+
is_name: nil,
|
439
|
+
is_primary_key: false,
|
440
|
+
is_custom: true,
|
441
|
+
is_dynamic: true,
|
442
|
+
dynamic_field_ref: "leadAttributeList",
|
443
|
+
updated_at: DateTime.parse("2000-01-01 22:22:22")
|
263
444
|
}
|
264
445
|
]
|
265
446
|
end
|
@@ -269,6 +450,10 @@ module Embulk
|
|
269
450
|
{name: "id", type: "long"},
|
270
451
|
{name: "email", type: "string"},
|
271
452
|
{name: "FieldName", type: "timestamp"},
|
453
|
+
{name: "FieldInt", type: "long"},
|
454
|
+
{name: "FieldBoolean", type: "boolean"},
|
455
|
+
{name: "FieldFloat", type: "double"},
|
456
|
+
{name: "FieldString", type: "string"},
|
272
457
|
]
|
273
458
|
end
|
274
459
|
end
|
@@ -19,40 +19,31 @@ module Embulk
|
|
19
19
|
super
|
20
20
|
end
|
21
21
|
|
22
|
-
def test_each_invoke_fetch
|
23
|
-
from_datetime = "2015-07-06"
|
24
|
-
to_datetime = "2015-07-07"
|
25
|
-
timerange = soap.send(:generate_time_range, from_datetime, to_datetime)
|
26
|
-
|
27
|
-
stub(soap).fetch { nil }
|
28
|
-
mock(soap).fetch(anything).times(timerange.length)
|
29
|
-
|
30
|
-
soap.each(from_datetime, to_datetime) { }
|
31
|
-
end
|
32
|
-
|
33
22
|
def test_each_invoke_fetch_with_specified_time
|
34
|
-
|
35
|
-
|
36
|
-
|
23
|
+
timerange = {
|
24
|
+
"from" => Time.parse("2015-07-06 00:00:00"),
|
25
|
+
"to" => Time.parse("2015-07-06 12:00:00"),
|
26
|
+
}
|
37
27
|
|
38
28
|
request = {
|
39
29
|
lead_selector: {
|
40
|
-
oldest_updated_at: timerange
|
41
|
-
latest_updated_at: timerange
|
30
|
+
oldest_updated_at: timerange["from"].iso8601,
|
31
|
+
latest_updated_at: timerange["to"].iso8601,
|
42
32
|
},
|
43
33
|
attributes!: {lead_selector: {"xsi:type"=>"ns1:LastUpdateAtSelector"}},
|
44
34
|
batch_size: Lead::BATCH_SIZE_DEFAULT,
|
45
35
|
}
|
46
36
|
|
47
|
-
stub(soap).fetch { nil }
|
48
37
|
mock(soap).fetch(request)
|
49
38
|
|
50
|
-
soap.each(
|
39
|
+
soap.each(timerange) { }
|
51
40
|
end
|
52
41
|
|
53
42
|
def test_each_fetch_next_page
|
54
|
-
|
55
|
-
|
43
|
+
timerange = {
|
44
|
+
"from" => Time.parse("2015-07-06 23:30:00"),
|
45
|
+
"to" => Time.parse("2015-07-07 00:00:00"),
|
46
|
+
}
|
56
47
|
|
57
48
|
any_instance_of(Savon::Client) do |klass|
|
58
49
|
mock(klass).call(:get_multiple_leads, anything) do
|
@@ -64,35 +55,7 @@ module Embulk
|
|
64
55
|
leads_count = next_stream_leads_response.xpath('//leadRecord').length
|
65
56
|
mock(proc).call(anything).times(leads_count)
|
66
57
|
|
67
|
-
soap.each(
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
class TestGenerateTime < self
|
72
|
-
def setup
|
73
|
-
mute_logger
|
74
|
-
end
|
75
|
-
|
76
|
-
data do
|
77
|
-
{
|
78
|
-
"8/1 to 8/2" => ["2015-08-01 00:00:00", "2015-08-02 00:00:00", 24],
|
79
|
-
"over the days" => ["2015-08-01 19:00:00", "2015-08-03 05:00:00", 34],
|
80
|
-
"odd times" => ["2015-08-01 11:11:11", "2015-08-01 22:22:22", 12],
|
81
|
-
}
|
82
|
-
end
|
83
|
-
def test_generate_time_range_by_1hour(data)
|
84
|
-
from, to, count = data
|
85
|
-
range = soap.send(:generate_time_range, from, to)
|
86
|
-
assert_equal count, range.length
|
87
|
-
end
|
88
|
-
|
89
|
-
def test_if_to_is_nil_use_time_now
|
90
|
-
from = "2000-01-01"
|
91
|
-
now = Time.now
|
92
|
-
stub(Time).now { now }
|
93
|
-
|
94
|
-
range = soap.send(:generate_time_range, from, nil)
|
95
|
-
assert_equal now, range.last[:to]
|
58
|
+
soap.each(timerange, {}, &proc)
|
96
59
|
end
|
97
60
|
end
|
98
61
|
|
data/test/run-test.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: embulk-input-marketo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- uu59
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-09-
|
12
|
+
date: 2015-09-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -170,6 +170,7 @@ files:
|
|
170
170
|
- gemfiles/embulk-0.6.23
|
171
171
|
- gemfiles/embulk-0.6.24
|
172
172
|
- gemfiles/embulk-0.6.25
|
173
|
+
- gemfiles/embulk-0.6.26
|
173
174
|
- gemfiles/embulk-latest
|
174
175
|
- gemfiles/template.erb
|
175
176
|
- lib/embulk/input/marketo/activity_log.rb
|
@@ -180,7 +181,6 @@ files:
|
|
180
181
|
- lib/embulk/input/marketo_api/soap/activity_log.rb
|
181
182
|
- lib/embulk/input/marketo_api/soap/base.rb
|
182
183
|
- lib/embulk/input/marketo_api/soap/lead.rb
|
183
|
-
- lib/embulk/input/marketo_api/soap/timeslice.rb
|
184
184
|
- test/activity_log_fixtures.rb
|
185
185
|
- test/embulk/input/marketo/test_activity_log.rb
|
186
186
|
- test/embulk/input/marketo/test_base.rb
|
@@ -1,44 +0,0 @@
|
|
1
|
-
module Embulk
|
2
|
-
module Input
|
3
|
-
module MarketoApi
|
4
|
-
module Soap
|
5
|
-
module Timeslice
|
6
|
-
private
|
7
|
-
|
8
|
-
def generate_time_range(from, to)
|
9
|
-
# e.g. from = 2010-01-01 15:00, to = 2010-01-03 09:30
|
10
|
-
# convert to such array:
|
11
|
-
# [
|
12
|
-
# {from: 2010-01-01 15:00, to: 2010-01-01 16:00},
|
13
|
-
# {from: 2010-01-01 16:00, to: 2010-01-01 17:00},
|
14
|
-
# ...
|
15
|
-
# {from: 2010-01-03 08:00, to: 2010-01-03 09:00},
|
16
|
-
# {from: 2010-01-03 09:00, to: 2010-01-03 09:30},
|
17
|
-
# ]
|
18
|
-
# to fetch data from Marketo API with each day as
|
19
|
-
# desribed on official blog:
|
20
|
-
# http://developers.marketo.com/blog/performance-tuning-api-requests/
|
21
|
-
to ||= Time.now
|
22
|
-
from = Time.parse(from) unless from.is_a?(Time)
|
23
|
-
to = Time.parse(to) unless to.is_a?(Time)
|
24
|
-
|
25
|
-
result = []
|
26
|
-
since = from
|
27
|
-
while since < to
|
28
|
-
next_since = since + 3600
|
29
|
-
if to < next_since
|
30
|
-
next_since = to
|
31
|
-
end
|
32
|
-
result << {
|
33
|
-
from: since,
|
34
|
-
to: next_since
|
35
|
-
}
|
36
|
-
since = next_since
|
37
|
-
end
|
38
|
-
result
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|