embulk-input-mixpanel 0.5.15 → 0.6.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.
@@ -0,0 +1,745 @@
1
+ require "prepare_embulk"
2
+ require "override_assert_raise"
3
+ require "embulk/input/mixpanel"
4
+ require "embulk/input/service/base_service"
5
+ require "embulk/input/service/jql_service"
6
+ require "active_support/core_ext/time"
7
+ require "json"
8
+
9
+ module Embulk
10
+ module Input
11
+ class JQLServiceTest < Test::Unit::TestCase
12
+ include OverrideAssertRaise
13
+
14
+ API_SECRET = "api_secret".freeze
15
+ FROM_DATE = "2015-02-22".freeze
16
+ TO_DATE = "2015-03-02".freeze
17
+ DAYS = 8
18
+ SLICE_RANGE = 10
19
+ DATES = Date.parse(FROM_DATE)..(Date.parse(FROM_DATE) + DAYS - 1)
20
+ TIMEZONE = "Asia/Tokyo".freeze
21
+ SMALL_NUM_OF_RECORDS = 10
22
+ DURATIONS = [
23
+ {from_date: FROM_DATE, to_date: "2015-02-28"}, # It has 7 days between 2015-02-22 and 2015-02-28
24
+ {from_date: "2015-03-01", to_date: TO_DATE},
25
+ ]
26
+ JQL_SCRIPT = 'function main() { return Events({ from_date: "2015-01-01", to_date:"2015-01-02"}'
27
+ JQL_SCRIPT_WITH_PARAMS = 'function main() { return Events({ from_date: params.from_date, to_date: to_date}'
28
+
29
+ def setup
30
+ setup_client
31
+ setup_logger
32
+ stub(Embulk::Input::MixpanelApi::Client).mixpanel_available? {true}
33
+ end
34
+
35
+ def setup_client
36
+ params = {
37
+ params: nil,
38
+ script: nil,
39
+ }
40
+
41
+ any_instance_of(MixpanelApi::Client) do |klass|
42
+ DURATIONS.each do |duration|
43
+ from_date = duration[:from_date]
44
+ to_date = duration[:to_date]
45
+
46
+ params = params.merge(from_date: from_date, to_date: to_date)
47
+ stub(klass).send_jql_script_small_dataset(anything) {records}
48
+ end
49
+ end
50
+ end
51
+
52
+ def satisfy_task_ignore_start_time(expected_task)
53
+ satisfy {|input_task|
54
+ assert_equal(expected_task, input_task)
55
+ true
56
+ }
57
+ end
58
+
59
+ def setup_logger
60
+ stub(Embulk).logger {::Logger.new(IO::NULL)}
61
+ end
62
+
63
+ class GuessTest < self
64
+ def setup
65
+ # Do nothing from parent
66
+ mute_warn
67
+ stub(Embulk::Input::MixpanelApi::Client).mixpanel_available? {true}
68
+ end
69
+
70
+ def test_from_date_old_date
71
+ config = {
72
+ type: "mixpanel",
73
+ jql_mode: true,
74
+ incremental: false,
75
+ jql_script: JQL_SCRIPT,
76
+ api_secret: API_SECRET,
77
+ from_date: FROM_DATE,
78
+ timezone: TIMEZONE,
79
+ }
80
+
81
+ stub_export_all
82
+ mock(Embulk.logger).info(/^Guessing.*#{Regexp.escape FROM_DATE}\.\./)
83
+
84
+ actual = Mixpanel.guess(embulk_config(config))
85
+ assert_equal(expected, actual)
86
+ end
87
+
88
+ def test_from_date_future
89
+ config = {
90
+ type: "mixpanel",
91
+ api_secret: API_SECRET,
92
+ jql_mode: true,
93
+ incremental: false,
94
+ jql_script: JQL_SCRIPT,
95
+ timezone: TIMEZONE,
96
+ from_date: (today + 1).to_s
97
+ }
98
+
99
+ stub_export_all
100
+ mock(Embulk.logger).info(/Guessing.*#{Regexp.escape Embulk::Input::Service::JqlService.new(config).default_guess_start_date(TIMEZONE).to_s}/)
101
+
102
+ Mixpanel.guess(embulk_config(config))
103
+ end
104
+
105
+ def test_from_date_yesterday
106
+ from_date = (today - 1).to_s
107
+ config = {
108
+ type: "mixpanel",
109
+ api_secret: API_SECRET,
110
+ from_date: from_date,
111
+ timezone: TIMEZONE,
112
+ incremental: false,
113
+ jql_mode: true,
114
+ jql_script: JQL_SCRIPT,
115
+ }
116
+
117
+ stub_export_all
118
+ mock(Embulk.logger).info(/Guessing.*#{Regexp.escape from_date}/)
119
+
120
+ Mixpanel.guess(embulk_config(config))
121
+ end
122
+
123
+ def test_no_from_date
124
+ config = {
125
+ type: "mixpanel",
126
+ api_secret: API_SECRET,
127
+ timezone: TIMEZONE,
128
+ jql_mode: true,
129
+ incremental: false,
130
+ jql_script: JQL_SCRIPT,
131
+ }
132
+
133
+ stub_export_all
134
+ mock(Embulk.logger).info(/Guessing.*#{Regexp.escape Embulk::Input::Service::JqlService.new(config).default_guess_start_date(TIMEZONE).to_s}/)
135
+
136
+ Mixpanel.guess(embulk_config(config))
137
+ end
138
+
139
+ def test_json_type
140
+ sample_records = records.map do |r|
141
+ r.merge("properties"=>{"time"=>1, "array"=>[1, 2], "hash"=>{foo: "FOO"}})
142
+ end
143
+
144
+ config = {
145
+ type: "mixpanel",
146
+ api_secret: API_SECRET,
147
+ timezone: TIMEZONE,
148
+ jql_mode: true,
149
+ incremental: false,
150
+ jql_script: JQL_SCRIPT,
151
+ }
152
+
153
+ actual = Embulk::Input::Service::JqlService.new(config).guess_from_records(sample_records)
154
+
155
+ assert actual.include?(name: "properties", type: :json)
156
+ end
157
+
158
+ def test_mixpanel_is_down
159
+ stub(Embulk::Input::MixpanelApi::Client).mixpanel_available? {false}
160
+ config = {
161
+ type: "mixpanel",
162
+ api_secret: API_SECRET,
163
+ timezone: TIMEZONE,
164
+ jql_mode: true,
165
+ incremental: false,
166
+ jql_script: JQL_SCRIPT,
167
+ }
168
+
169
+ assert_raise(Embulk::DataError) do
170
+ Mixpanel.guess(embulk_config(config))
171
+ end
172
+ end
173
+
174
+ private
175
+
176
+ def stub_export_all
177
+ any_instance_of(MixpanelApi::Client) do |klass|
178
+ stub(klass).send_brief_checked_jql_script(anything) {}
179
+ stub(klass).send_jql_script_small_dataset(anything) {records}
180
+ end
181
+ end
182
+
183
+ def mute_warn
184
+ stub(Embulk.logger).warn(anything) {}
185
+ end
186
+
187
+ def embulk_config(config)
188
+ DataSource[*config.to_a.flatten(1)]
189
+ end
190
+
191
+ def expected
192
+ {"columns"=>
193
+ [{:name=>:name, :type=>:string},
194
+ {:name=>:distinct_id, :type=>:string},
195
+ {:name=>:labels, :type=>:json},
196
+ {:name=>:time, :type=>:long},
197
+ {:name=>:sampling_factor, :type=>:long},
198
+ {:name=>:dataset, :type=>:string},
199
+ {:name=>:properties, :type=>:json}]
200
+ }
201
+ end
202
+ end
203
+
204
+ class TransactionDateTest < self
205
+ def test_valid_from_date
206
+ from_date = "2015-08-14"
207
+ mock(Mixpanel).resume(anything, anything, 1)
208
+
209
+ Mixpanel.transaction(transaction_config(from_date))
210
+ end
211
+
212
+ def test_invalid_from_date
213
+ from_date = "2015-08-41"
214
+
215
+ assert_raise(Embulk::ConfigError) do
216
+ Mixpanel.transaction(transaction_config(from_date))
217
+ end
218
+ end
219
+
220
+ def test_future
221
+ from_date = (today + 10).to_s
222
+ mock(Mixpanel).resume(anything, anything, 1)
223
+
224
+ Mixpanel.transaction(transaction_config(from_date))
225
+ end
226
+
227
+ def test_negative_days
228
+ assert_raise(Embulk::ConfigError) do
229
+ Mixpanel.transaction(transaction_config((today - 1).to_s).merge(fetch_days: -1))
230
+ end
231
+ end
232
+
233
+ def test_default_configuration
234
+ stub(Mixpanel).resume {|task|
235
+ assert_true(task[:jql_mode])
236
+ assert_true(task[:incremental])
237
+ }
238
+ Mixpanel.transaction(transaction_config(today))
239
+ end
240
+
241
+ private
242
+
243
+ def transaction_config(from_date)
244
+ _config = config.merge(
245
+ from_date: from_date,
246
+ timezone: TIMEZONE,
247
+ columns: schema,
248
+ jql_mode: true,
249
+ jql_script: JQL_SCRIPT,
250
+ )
251
+ DataSource[*_config.to_a.flatten(1)]
252
+ end
253
+ end
254
+
255
+ class TransactionTest < self
256
+ class FromDateTest < self
257
+ def setup
258
+ any_instance_of(MixpanelApi::Client) do |klass|
259
+ stub(klass).send_brief_checked_jql_script(anything) {}
260
+ end
261
+ end
262
+
263
+ def test_info
264
+ stub(Mixpanel).resume(satisfy_task_ignore_start_time(task.merge(dates: target_dates)), columns, 1, &control)
265
+
266
+ info_message_regexp = /#{Regexp.escape(target_dates.first)}.+#{Regexp.escape(target_dates.last)}/
267
+ mock(Embulk.logger).info(info_message_regexp)
268
+ stub(Embulk.logger).warn
269
+
270
+ Mixpanel.transaction(transaction_config, &control)
271
+ end
272
+
273
+ def test_warn
274
+ stub(Mixpanel).resume(satisfy_task_ignore_start_time(task.merge(dates: target_dates)), columns, 1, &control)
275
+ stub(Embulk.logger).info
276
+
277
+ ignore_dates = dates.map {|date| date.to_s}.to_a - target_dates
278
+ warn_message_regexp = /#{Regexp.escape(ignore_dates.first)}.+#{Regexp.escape(ignore_dates.last)}/
279
+ mock(Embulk.logger).warn(warn_message_regexp)
280
+
281
+ Mixpanel.transaction(transaction_config, &control)
282
+ end
283
+
284
+ private
285
+
286
+ def dates
287
+ (today - 10)..(today + 10)
288
+ end
289
+
290
+ def target_dates
291
+ dates.find_all {|d| d <= today}.map {|date| date.to_s}
292
+ end
293
+
294
+ def transaction_config
295
+ _config = config.merge(
296
+ from_date: dates.first.to_s,
297
+ fetch_days: dates.to_a.size,
298
+ timezone: TIMEZONE,
299
+ columns: schema,
300
+ jql_mode: true,
301
+ jql_script: JQL_SCRIPT,
302
+ )
303
+ DataSource[*_config.to_a.flatten(1)]
304
+ end
305
+ end
306
+
307
+ class TimezoneTest < self
308
+ def test_valid_timezone
309
+ timezone = TIMEZONE
310
+ mock(Mixpanel).resume(satisfy_task_ignore_start_time(transaction_task(timezone)), columns, 1, &control)
311
+ Mixpanel.transaction(transaction_config(timezone), &control)
312
+ end
313
+
314
+ def test_invalid_timezone
315
+ timezone = "#{TIMEZONE}ooooo"
316
+
317
+ assert_raise(Embulk::ConfigError) do
318
+ Mixpanel.transaction(transaction_config(timezone), &control)
319
+ end
320
+ end
321
+
322
+ private
323
+
324
+ def transaction_task(timezone)
325
+ task.merge(
326
+ dates: DATES.map {|date| date.to_s},
327
+ api_secret: API_SECRET,
328
+ incremental: true,
329
+ timezone: timezone,
330
+ schema: schema
331
+ )
332
+ end
333
+
334
+ def transaction_config(timezone)
335
+ _config = config.merge(
336
+ timezone: timezone,
337
+ columns: schema,
338
+ )
339
+ DataSource[*_config.to_a.flatten(1)]
340
+ end
341
+ end
342
+
343
+ class DaysTest < self
344
+ def test_valid_days
345
+ days = 5
346
+
347
+ mock(Mixpanel).resume(satisfy_task_ignore_start_time(transaction_task(days)), columns, 1, &control)
348
+ Mixpanel.transaction(transaction_config(days), &control)
349
+ end
350
+
351
+ def test_next_to_date
352
+ next_config_diff = Mixpanel.resume(transaction_task(1).merge(incremental: true), columns, 1) do
353
+ [{to_date: today.to_s, latest_fetched_time: 1502707247000}]
354
+ end
355
+ assert_equal((today).to_s, next_config_diff[:from_date])
356
+ end
357
+
358
+ def test_invalid_days
359
+ days = 0
360
+
361
+ assert_raise(Embulk::ConfigError) do
362
+ Mixpanel.transaction(transaction_config(days), &control)
363
+ end
364
+ end
365
+
366
+ def test_valid_days_with_backfill
367
+ days = 5
368
+
369
+ stub(Mixpanel).resume() do |task|
370
+ assert_equal(["2015-02-17", "2015-02-18", "2015-02-19", "2015-02-20", "2015-02-21", "2015-02-22", "2015-02-23", "2015-02-24", "2015-02-25", "2015-02-26"], task[:dates])
371
+ end
372
+ config=transaction_config(days).merge("back_fill_days" => 5, "incremental_column" => "test_column", "latest_fetched_time" => 1501599491000)
373
+ Mixpanel.transaction(config, &control)
374
+ end
375
+
376
+ def test_valid_days_with_backfill_first_run
377
+ days = 5
378
+ stub(Mixpanel).resume() do |task|
379
+ assert_equal(transaction_task(days)[:dates], task[:dates])
380
+ end
381
+ config=transaction_config(days).merge("back_fill_days" => 5, "incremental_column" => "test_column")
382
+ Mixpanel.transaction(config, &control)
383
+ end
384
+
385
+ private
386
+
387
+ def transaction_task(days)
388
+ from_date = Date.parse(FROM_DATE)
389
+ task.merge(
390
+ dates: (from_date..(from_date + days - 1)).map {|date| date.to_s},
391
+ api_secret: API_SECRET,
392
+ timezone: TIMEZONE,
393
+ schema: schema,
394
+ jql_mode: true,
395
+ jql_script: JQL_SCRIPT,
396
+ )
397
+ end
398
+
399
+ def transaction_config(days)
400
+ _config = config.merge(
401
+ fetch_days: days,
402
+ columns: schema,
403
+ timezone: TIMEZONE,
404
+ jql_mode: true,
405
+ jql_script: JQL_SCRIPT,
406
+ )
407
+ DataSource[*_config.to_a.flatten(1)]
408
+ end
409
+ end
410
+
411
+ class ValidateTest < self
412
+ def setup_client
413
+ any_instance_of(MixpanelApi::Client) do |klass|
414
+ stub(klass).send_jql_script(anything) {[1]}
415
+ stub(klass).send_jql_script_small_dataset(anything) {[1]}
416
+ end
417
+ end
418
+
419
+ def setup
420
+ super
421
+ @page_builder = Object.new
422
+ @plugin = Mixpanel.new(task, nil, nil, @page_builder)
423
+ end
424
+
425
+ def test_unsupport_data_format
426
+ assert_raise(Embulk::ConfigError) do
427
+ Mixpanel.guess(embulk_config)
428
+ end
429
+ end
430
+ end
431
+
432
+ def control
433
+ proc {} # dummy
434
+ end
435
+
436
+ def transaction_task
437
+ task.merge(
438
+ dates: DATES.map {|date| date.to_s},
439
+ api_secret: API_SECRET,
440
+ timezone: TIMEZONE,
441
+ schema: schema,
442
+ jql_mode: true,
443
+ jql_script: JQL_SCRIPT,
444
+ )
445
+ end
446
+
447
+ def columns
448
+ schema.map do |col|
449
+ Column.new(nil, col["name"], col["type"].to_sym)
450
+ end
451
+ end
452
+ end
453
+
454
+ def test_export_params
455
+ config_params = [
456
+ :type, "mixpanel",
457
+ :api_secret, API_SECRET,
458
+ :from_date, FROM_DATE,
459
+ :to_date, TO_DATE,
460
+ :jql_mode, true,
461
+ :jql_script, JQL_SCRIPT,
462
+ ]
463
+
464
+ config = DataSource[*config_params]
465
+
466
+ expected = {
467
+ params: {:from_date=>:from_date, :to_date=>:today},
468
+ script: "function main() { return Events({ from_date: \"2015-01-01\", to_date:\"2015-01-02\"}",
469
+ }
470
+ actual = Embulk::Input::Service::JqlService.new(config).parameters(JQL_SCRIPT, :from_date, :today)
471
+
472
+ assert_equal(expected, actual)
473
+ end
474
+
475
+ sub_test_case "retry" do
476
+ def setup
477
+ @page_builder = Object.new
478
+ @plugin = Mixpanel.new(task, nil, nil, @page_builder)
479
+ @plugin.init
480
+ @httpclient = HTTPClient.new
481
+ stub(HTTPClient).new {@httpclient}
482
+ stub(@page_builder).add {}
483
+ stub(@page_builder).finish {}
484
+ stub(Embulk.logger).warn {}
485
+ stub(Embulk.logger).info {}
486
+ stub(Embulk::Input::MixpanelApi::Client).mixpanel_available? {true}
487
+ end
488
+
489
+ test "200 and don't support format" do
490
+ stub_response(200)
491
+ mock(Embulk.logger).warn(/Retrying/).never
492
+ assert_raise(Embulk::DataError) do
493
+ @plugin.run
494
+ end
495
+ end
496
+
497
+ test "400" do
498
+ stub_response(400)
499
+ mock(Embulk.logger).warn(/Retrying/).never
500
+ assert_raise(Embulk::ConfigError) do
501
+ @plugin.run
502
+ end
503
+ end
504
+
505
+ test "401" do
506
+ stub_response(401)
507
+ mock(Embulk.logger).warn(/Retrying/).never
508
+ assert_raise(Embulk::ConfigError) do
509
+ @plugin.run
510
+ end
511
+ end
512
+
513
+ test "500" do
514
+ stub_response(500)
515
+ mock(Embulk.logger).warn(/Retrying/).times(task[:retry_limit])
516
+ assert_raise(PerfectRetry::TooManyRetry) do
517
+ @plugin.run
518
+ end
519
+ end
520
+
521
+ test "timeout" do
522
+ stub(@httpclient).post {raise HTTPClient::TimeoutError, "timeout"}
523
+ mock(Embulk.logger).warn(/Retrying/).times(task[:retry_limit])
524
+
525
+ assert_raise(PerfectRetry::TooManyRetry) do
526
+ @plugin.run
527
+ end
528
+ end
529
+
530
+ test "Mixpanel is down" do
531
+ stub(Embulk::Input::MixpanelApi::Client).mixpanel_available? {false}
532
+
533
+ assert_raise(Embulk::ConfigError) do
534
+ @plugin.run
535
+ end
536
+ end
537
+
538
+ def stub_response(code)
539
+ stub(@httpclient.test_loopback_http_response).shift {"HTTP/1.1 #{code} \r\n\r\n"}
540
+ end
541
+
542
+ def task
543
+ {
544
+ api_secret: API_SECRET,
545
+ jql_endpoint: "https://mixpanel.com/api/2.0/jql/",
546
+ timezone: TIMEZONE,
547
+ incremental: true,
548
+ schema: schema,
549
+ dates: DATES.to_a.map(&:to_s),
550
+ retry_initial_wait_sec: 2,
551
+ retry_limit: 3,
552
+ jql_mode: true,
553
+ jql_script: JQL_SCRIPT,
554
+ slice_range: SLICE_RANGE,
555
+ }
556
+ end
557
+ end
558
+
559
+ class RunTest < self
560
+ def setup_client
561
+ any_instance_of(MixpanelApi::Client) do |klass|
562
+ stub(klass).send_jql_script(anything) {data}
563
+ end
564
+ end
565
+
566
+ def setup
567
+ super
568
+ @page_builder = Object.new
569
+ @plugin = Mixpanel.new(DataSource[task.to_a], nil, nil, @page_builder)
570
+ end
571
+
572
+ def test_run
573
+ any_instance_of(Embulk::Input::Service::JqlService) do |klass|
574
+ stub(klass).preview? {false}
575
+ end
576
+ mock(@page_builder).add(anything).times(data.length)
577
+ mock(@page_builder).finish
578
+ task_report = @plugin.run
579
+ assert_equal("2015-03-01", task_report[:to_date])
580
+ end
581
+
582
+ def test_run_with_incremental_column
583
+ any_instance_of(Embulk::Input::Service::JqlService) do |klass|
584
+ stub(klass).preview? {false}
585
+ end
586
+ mock(@page_builder).add(anything).times(data.length)
587
+ mock(@page_builder).finish
588
+ plugin = Mixpanel.new(DataSource[task.to_a].merge({"incremental_column"=>"time", "latest_fetched_time"=>1452027551999}), nil, nil, @page_builder)
589
+ task_report = plugin.run
590
+ assert_equal("2015-03-01", task_report[:to_date])
591
+ assert_equal("1452027552000", task_report[:latest_fetched_time])
592
+ end
593
+
594
+ def test_run_with_incremental_column_skip
595
+ any_instance_of(Embulk::Input::Service::JqlService) do |klass|
596
+ stub(klass).preview? {false}
597
+ end
598
+
599
+ mock(@page_builder).add(anything).times(0)
600
+ mock(@page_builder).finish
601
+ plugin = Mixpanel.new(DataSource[task.to_a].merge({"incremental_column"=>"time", "latest_fetched_time"=>1452027552001}), nil, nil, @page_builder)
602
+ task_report = plugin.run
603
+ assert_equal("2015-03-01", task_report[:to_date])
604
+ assert_equal("1452027552001", task_report[:latest_fetched_time])
605
+ end
606
+
607
+ class SliceRangeRunTest < self
608
+ def test_default_slice_range
609
+ plugin = Mixpanel.new(task.merge(slice_range: 4), nil, nil, @page_builder)
610
+ any_instance_of(Embulk::Input::Service::JqlService) do |klass|
611
+ stub(klass).preview? {false}
612
+ end
613
+
614
+ mock(@page_builder).add(anything).times(data.length * 2)
615
+ mock(@page_builder).finish
616
+ plugin.run
617
+ end
618
+ end
619
+
620
+ def test_preview
621
+ any_instance_of(MixpanelApi::Client) do |klass|
622
+ stub(klass).request_jql(anything) {Struct.new(:code, :body).new(200, records.to_json)}
623
+ end
624
+ any_instance_of(Embulk::Input::Service::JqlService) do |klass|
625
+ stub(klass).preview? {true}
626
+ end
627
+ mock(@page_builder).add(anything).times(SMALL_NUM_OF_RECORDS)
628
+ mock(@page_builder).finish
629
+ @plugin.run
630
+ end
631
+ end
632
+
633
+ private
634
+
635
+ def schema
636
+ [
637
+ {"name"=>"name", "type"=>"string"},
638
+ {"name"=>"distinct_id", "type"=>"string"},
639
+ {"name"=>"labels", "type"=>"json"},
640
+ {"name"=>"time", "type"=>"long"},
641
+ {"name"=>"sampling_factor", "type"=>"long"},
642
+ {"name"=>"dataset", "type"=>"string"},
643
+ {"name"=>"properties", "type"=>"json"}
644
+ ]
645
+ end
646
+
647
+ def task
648
+ {
649
+ api_secret: API_SECRET,
650
+ jql_endpoint: "https://mixpanel.com/api/2.0/jql/",
651
+ timezone: TIMEZONE,
652
+ incremental: true,
653
+ schema: schema,
654
+ dates: DATES.to_a.map(&:to_s),
655
+ retry_initial_wait_sec: 2,
656
+ retry_limit: 3,
657
+ jql_mode: true,
658
+ jql_script: JQL_SCRIPT,
659
+ slice_range: SLICE_RANGE,
660
+ incremental_column: nil,
661
+ latest_fetched_time: 0,
662
+ }
663
+ end
664
+
665
+ def records
666
+ [
667
+ {
668
+ "name": "pageview",
669
+ "distinct_id": "02a99746-0f52-4acd-9a53-7deb763803ca",
670
+ "labels": [],
671
+ "time": 1452027552000,
672
+ "sampling_factor": 1,
673
+ "dataset": "$mixpanel",
674
+ "properties": {
675
+ "$email": "Alexander.Davidson@hotmailx.com",
676
+ "$import": true,
677
+ "country": "UK",
678
+ "load_time_ms": 4
679
+ }
680
+ },
681
+ ] * 30
682
+ end
683
+
684
+ def data
685
+ [
686
+ {
687
+ "name"=> "pageview",
688
+ "distinct_id"=> "02a99746-0f52-4acd-9a53-7deb763803ca",
689
+ "labels"=> [],
690
+ "time"=> 1452027552000,
691
+ "sampling_factor"=> 1,
692
+ "dataset"=> "$mixpanel",
693
+ "properties"=> {
694
+ "$email"=> "Alexander.Davidson@hotmailx.com",
695
+ "$import"=> true,
696
+ "country"=> "UK",
697
+ "load_time_ms"=> 4
698
+ }
699
+ },
700
+ ] * 30
701
+ end
702
+
703
+ def record
704
+ {
705
+ "name": "pageview",
706
+ "distinct_id": "02a99746-0f52-4acd-9a53-7deb763803ca",
707
+ "labels": [],
708
+ "time": 1452027552000,
709
+ "sampling_factor": 1,
710
+ "dataset": "$mixpanel",
711
+ "properties": {
712
+ "$email": "Alexander.Davidson@hotmailx.com",
713
+ "$import": true,
714
+ "country": "UK",
715
+ "load_time_ms": 4
716
+ }
717
+ }
718
+
719
+ end
720
+
721
+ def config
722
+ {
723
+ type: "mixpanel",
724
+ api_secret: API_SECRET,
725
+ from_date: FROM_DATE,
726
+ timezone: TIMEZONE,
727
+ fetch_days: DAYS,
728
+ retry_initial_wait_sec: 2,
729
+ retry_limit: 3,
730
+ jql_mode: true,
731
+ jql_script: JQL_SCRIPT,
732
+ slice_range: SLICE_RANGE,
733
+ }
734
+ end
735
+
736
+ def embulk_config
737
+ DataSource[*config.to_a.flatten(1)]
738
+ end
739
+
740
+ def today
741
+ ActiveSupport::TimeZone[TIMEZONE].today
742
+ end
743
+ end
744
+ end
745
+ end