embulk-input-mixpanel 0.2.0 → 0.2.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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/CHANGELOG.md +5 -0
- data/README.md +3 -1
- data/embulk-input-mixpanel.gemspec +1 -1
- data/lib/embulk/input/mixpanel.rb +48 -27
- data/lib/embulk/input/mixpanel_api/client.rb +5 -0
- data/test/embulk/input/mixpanel_api/test_client.rb +1 -0
- data/test/embulk/input/test_mixpanel.rb +153 -18
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9a688fb2eb175f13ee5c82b8ea2da6c74ed69839
|
4
|
+
data.tar.gz: af0fdae8cc6fba0911ab269d7d8f354d51803f25
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 25381a19a9743a0b945d5d453931b1f93ab87009a79185b72eba32577a496550f1050b38ff8baa81c164d628a1c67714e785c27bf6833a04e69431bcad2e6ace
|
7
|
+
data.tar.gz: d69d65730c8c3a3d1ea1c85182e274ea8bde716662c132ee628e1c0be7d25e2b97c0a2c15dc0a7269b714677cf1635f04fcdd8a8237ec7353714d4f1ede05094
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
## 0.2.1 - 2015-08-26
|
2
|
+
|
3
|
+
* [fixed] Fix guess with recently from date [#18](https://github.com/treasure-data/embulk-input-mixpanel/pull/18)
|
4
|
+
* [fixed] Fix error handling when invalid date set given [#17](https://github.com/treasure-data/embulk-input-mixpanel/pull/17)
|
5
|
+
|
1
6
|
## 0.2.0 - 2015-08-17
|
2
7
|
|
3
8
|
* [enhanement] Raise config error for unretryable [#15](https://github.com/treasure-data/embulk-input-mixpanel/pull/15) [[Reported by @muga](https://github.com/treasure-data/embulk-input-mixpanel/issues/11). Thanks!!]
|
data/README.md
CHANGED
@@ -35,7 +35,9 @@ To get it, you should log in mixpanel website, and click gear icon at the lower
|
|
35
35
|
- **api_secret**: project API Secret (string, required)
|
36
36
|
- **timezone**: project timezone(string, required)
|
37
37
|
- **from_date**: From date to export (string, optional, default: today - 2)
|
38
|
-
-
|
38
|
+
- NOTE: Mixpanel API supports to export data from at least 2 days before to at most the previous day.
|
39
|
+
- **days**: Count of days range for exporting (integer, optional, default: from_date - (today - 1))
|
40
|
+
- NOTE: Mixpanel doesn't support to from_date > today - 2
|
39
41
|
- **event**: The event or events to filter data (array, optional, default: nil)
|
40
42
|
- **where**: Expression to filter data (c.f. https://mixpanel.com/docs/api-documentation/data-export-api#segmentation-expressions) (string, optional, default: nil)
|
41
43
|
- **bucket**:The data backet to filter data (string, optional, default: nil)
|
@@ -18,8 +18,40 @@ module Embulk
|
|
18
18
|
|
19
19
|
task[:params] = export_params(config)
|
20
20
|
|
21
|
-
|
22
|
-
|
21
|
+
begin
|
22
|
+
from_date_str = config.param(:from_date, :string, default: (Date.today - 2).to_s)
|
23
|
+
from_date = Date.parse(from_date_str)
|
24
|
+
rescue ArgumentError # invalid date
|
25
|
+
raise ConfigError, "from_date '#{from_date_str}' is invalid date"
|
26
|
+
end
|
27
|
+
|
28
|
+
if from_date > Date.today - 1
|
29
|
+
Embulk.logger.warn "Mixpanel allow 2 days before to from_date, so no data is input."
|
30
|
+
target_dates = []
|
31
|
+
else
|
32
|
+
days = config.param(:days, :integer, default: nil)
|
33
|
+
|
34
|
+
if days.nil?
|
35
|
+
# When no 'days' is specified in config file, so dates is
|
36
|
+
# generated by from_date and yeasterday.
|
37
|
+
dates = from_date..(Date.today - 1)
|
38
|
+
elsif days < 1
|
39
|
+
raise ConfigError, "days '#{days}' is invalid. Please specify bigger number than 0."
|
40
|
+
else
|
41
|
+
# When 'days' is specified in config file and it is satisfied,
|
42
|
+
# so it is used for dates.
|
43
|
+
dates = from_date..(from_date + days)
|
44
|
+
end
|
45
|
+
|
46
|
+
target_dates = dates.find_all {|date| date < Date.today}
|
47
|
+
|
48
|
+
overtimes = dates.to_a - target_dates
|
49
|
+
unless overtimes.empty?
|
50
|
+
Embulk.logger.warn "These dates are too early access, ignored them: #{overtimes.map(&:to_s).join(', ')}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
task[:dates] = target_dates.map {|date| date.to_s}
|
23
55
|
|
24
56
|
task[:api_key] = config.param(:api_key, :string)
|
25
57
|
task[:api_secret] = config.param(:api_secret, :string)
|
@@ -60,14 +92,22 @@ module Embulk
|
|
60
92
|
def self.guess(config)
|
61
93
|
client = MixpanelApi::Client.new(config.param(:api_key, :string), config.param(:api_secret, :string))
|
62
94
|
|
63
|
-
|
64
|
-
|
65
|
-
|
95
|
+
from_date_str = config.param(:from_date, :string, default: (Date.today - 1 - SLICE_DAYS_COUNT).to_s)
|
96
|
+
|
97
|
+
from_date = Date.parse(from_date_str)
|
98
|
+
|
99
|
+
if from_date > Date.today - 1
|
100
|
+
raise ConfigError, "Please specify date later than yesterday (inclusive) as 'from_date'"
|
101
|
+
end
|
102
|
+
|
103
|
+
# NOTE: to_date is yeasterday if from_date..Date.Today doesn't have
|
104
|
+
# more SLICE_DAYS_COUNT days.
|
105
|
+
to_date = [from_date + SLICE_DAYS_COUNT, Date.today - 1].min
|
66
106
|
|
67
107
|
params = export_params(config)
|
68
108
|
params = params.merge(
|
69
|
-
from_date: from_date,
|
70
|
-
to_date: to_date,
|
109
|
+
from_date: from_date.to_s,
|
110
|
+
to_date: to_date.to_s,
|
71
111
|
)
|
72
112
|
|
73
113
|
records = client.export(params)
|
@@ -85,25 +125,6 @@ module Embulk
|
|
85
125
|
return {"columns" => columns}
|
86
126
|
end
|
87
127
|
|
88
|
-
def self.generate_dates(config)
|
89
|
-
default_from_date = (Date.today - 2).to_s
|
90
|
-
|
91
|
-
begin
|
92
|
-
from_date = Date.parse(config.param(:from_date, :string, default: default_from_date))
|
93
|
-
rescue ArgumentError # invalid date
|
94
|
-
raise ConfigError, "from_date '#{from_date}' is invalid date"
|
95
|
-
end
|
96
|
-
|
97
|
-
default_days = ((Date.today - 1) - from_date).to_i
|
98
|
-
days = config.param(:days, :integer, default: default_days)
|
99
|
-
|
100
|
-
if days < 1
|
101
|
-
raise ConfigError, "days '#{days}' is invalid. Please spcify bigger number than 0."
|
102
|
-
end
|
103
|
-
|
104
|
-
from_date..(from_date + days)
|
105
|
-
end
|
106
|
-
|
107
128
|
def init
|
108
129
|
@api_key = task[:api_key]
|
109
130
|
@api_secret = task[:api_secret]
|
@@ -147,7 +168,7 @@ module Embulk
|
|
147
168
|
|
148
169
|
page_builder.finish
|
149
170
|
|
150
|
-
commit_report = {to_date: @dates.last}
|
171
|
+
commit_report = {to_date: @dates.last || (Date.today - 1)}
|
151
172
|
return commit_report
|
152
173
|
end
|
153
174
|
|
@@ -19,8 +19,13 @@ module Embulk
|
|
19
19
|
# https://mixpanel.com/docs/api-documentation/exporting-raw-data-you-inserted-into-mixpanel
|
20
20
|
params[:expire] ||= Time.now.to_i + TIMEOUT_SECONDS
|
21
21
|
params[:sig] = signature(params)
|
22
|
+
|
23
|
+
Embulk.logger.debug "Export param: #{params.to_s}"
|
24
|
+
|
22
25
|
response = httpclient.get(ENDPOINT_EXPORT, params)
|
23
26
|
|
27
|
+
Embulk.logger.debug "response code: #{response.code}"
|
28
|
+
|
24
29
|
if (400..499).include?(response.code)
|
25
30
|
raise ConfigError, response.body
|
26
31
|
elsif response.code >= 500
|
@@ -36,6 +36,7 @@ module Embulk
|
|
36
36
|
from_date = duration[:from_date]
|
37
37
|
to_date = duration[:to_date]
|
38
38
|
|
39
|
+
params = params.merge(from_date: from_date, to_date: to_date)
|
39
40
|
stub(klass).export(params) { records }
|
40
41
|
end
|
41
42
|
end
|
@@ -45,35 +46,126 @@ module Embulk
|
|
45
46
|
stub(Embulk).logger { ::Logger.new(IO::NULL) }
|
46
47
|
end
|
47
48
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
}
|
49
|
+
class GuessTest < self
|
50
|
+
def test_from_date_old_date
|
51
|
+
config = {
|
52
|
+
type: "mixpanel",
|
53
|
+
api_key: API_KEY,
|
54
|
+
api_secret: API_SECRET,
|
55
|
+
from_date: FROM_DATE,
|
56
|
+
}
|
57
57
|
|
58
|
-
|
59
|
-
|
58
|
+
from_date = config[:from_date]
|
59
|
+
to_date = Date.parse(from_date) + Mixpanel::SLICE_DAYS_COUNT
|
60
|
+
stub_export(from_date, to_date)
|
61
|
+
|
62
|
+
actual = Mixpanel.guess(embulk_config(config))
|
63
|
+
assert_equal(expected, actual)
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_from_date_today
|
67
|
+
config = {
|
68
|
+
type: "mixpanel",
|
69
|
+
api_key: API_KEY,
|
70
|
+
api_secret: API_SECRET,
|
71
|
+
from_date: Date.today.to_s,
|
72
|
+
}
|
73
|
+
|
74
|
+
assert_raise(ConfigError) do
|
75
|
+
Mixpanel.guess(embulk_config(config))
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_from_date_yesterday
|
80
|
+
config = {
|
81
|
+
type: "mixpanel",
|
82
|
+
api_key: API_KEY,
|
83
|
+
api_secret: API_SECRET,
|
84
|
+
from_date: (Date.today - 1).to_s,
|
85
|
+
}
|
86
|
+
|
87
|
+
from_date = config[:from_date]
|
88
|
+
to_date = from_date
|
89
|
+
stub_export(from_date, to_date)
|
90
|
+
|
91
|
+
actual = Mixpanel.guess(embulk_config(config))
|
92
|
+
assert_equal(expected, actual)
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_no_from_date
|
96
|
+
config = {
|
97
|
+
type: "mixpanel",
|
98
|
+
api_key: API_KEY,
|
99
|
+
api_secret: API_SECRET,
|
100
|
+
}
|
101
|
+
|
102
|
+
from_date = Date.today - 1 - Mixpanel::SLICE_DAYS_COUNT
|
103
|
+
to_date = Date.today - 1
|
104
|
+
stub_export(from_date, to_date)
|
105
|
+
|
106
|
+
actual = Mixpanel.guess(embulk_config(config))
|
107
|
+
assert_equal(expected, actual)
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
def stub_export(from_date, to_date)
|
113
|
+
params = {
|
114
|
+
api_key: API_KEY,
|
115
|
+
event: nil,
|
116
|
+
where: nil,
|
117
|
+
bucket: nil,
|
118
|
+
from_date: from_date.to_s,
|
119
|
+
to_date: to_date.to_s,
|
120
|
+
}
|
121
|
+
|
122
|
+
any_instance_of(MixpanelApi::Client) do |klass|
|
123
|
+
stub(klass).export(params) { records }
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def embulk_config(config)
|
128
|
+
DataSource[*config.to_a.flatten(1)]
|
129
|
+
end
|
130
|
+
|
131
|
+
def expected
|
132
|
+
{
|
133
|
+
"columns" => [
|
134
|
+
{name: "event", type: :string},
|
135
|
+
{name: "foo", type: :string},
|
136
|
+
{name: "time", type: :long},
|
137
|
+
{name: "int", type: :long},
|
138
|
+
]
|
139
|
+
}
|
140
|
+
end
|
60
141
|
end
|
61
142
|
|
62
|
-
class
|
143
|
+
class TransactionDateTest < self
|
63
144
|
def test_valid_from_date
|
64
145
|
from_date = "2015-08-14"
|
65
|
-
|
146
|
+
mock(Mixpanel).resume(anything, anything, 1)
|
66
147
|
|
67
|
-
|
68
|
-
actual = Mixpanel.generate_dates(transaction_config(from_date))
|
69
|
-
assert_equal(expected, actual)
|
148
|
+
Mixpanel.transaction(transaction_config(from_date))
|
70
149
|
end
|
71
150
|
|
72
151
|
def test_invalid_from_date
|
73
152
|
from_date = "2015-08-41"
|
74
153
|
|
75
154
|
assert_raise(Embulk::ConfigError) do
|
76
|
-
Mixpanel.
|
155
|
+
Mixpanel.transaction(transaction_config(from_date))
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def test_future
|
160
|
+
from_date = (Date.today + 10).to_s
|
161
|
+
mock(Mixpanel).resume(anything, anything, 1)
|
162
|
+
|
163
|
+
Mixpanel.transaction(transaction_config(from_date))
|
164
|
+
end
|
165
|
+
|
166
|
+
def test_negative_days
|
167
|
+
assert_raise(Embulk::ConfigError) do
|
168
|
+
Mixpanel.transaction(transaction_config((Date.today - 1).to_s).merge(days: -1))
|
77
169
|
end
|
78
170
|
end
|
79
171
|
|
@@ -91,6 +183,42 @@ module Embulk
|
|
91
183
|
|
92
184
|
class TransactionTest < self
|
93
185
|
class FromDateTest < self
|
186
|
+
def setup
|
187
|
+
end
|
188
|
+
|
189
|
+
def test_ignore_early_days
|
190
|
+
stub(Embulk).logger { Logger.new(File::NULL) }
|
191
|
+
|
192
|
+
mock(Mixpanel).resume(task.merge(dates: target_dates), columns, 1, &control)
|
193
|
+
Mixpanel.transaction(transaction_config, &control)
|
194
|
+
end
|
195
|
+
|
196
|
+
def test_warn
|
197
|
+
stub(Mixpanel).resume(task.merge(dates: target_dates), columns, 1, &control)
|
198
|
+
mock(Embulk.logger).warn(anything)
|
199
|
+
|
200
|
+
Mixpanel.transaction(transaction_config, &control)
|
201
|
+
end
|
202
|
+
|
203
|
+
private
|
204
|
+
|
205
|
+
def dates
|
206
|
+
(Date.today - 10)..(Date.today + 10)
|
207
|
+
end
|
208
|
+
|
209
|
+
def target_dates
|
210
|
+
dates.find_all{|d| d < Date.today}.map {|date| date.to_s}
|
211
|
+
end
|
212
|
+
|
213
|
+
def transaction_config
|
214
|
+
_config = config.merge(
|
215
|
+
from_date: dates.first.to_s,
|
216
|
+
days: dates.to_a.size,
|
217
|
+
timezone: TIMEZONE,
|
218
|
+
columns: schema
|
219
|
+
)
|
220
|
+
DataSource[*_config.to_a.flatten(1)]
|
221
|
+
end
|
94
222
|
end
|
95
223
|
|
96
224
|
class TimezoneTest < self
|
@@ -223,6 +351,13 @@ module Embulk
|
|
223
351
|
end
|
224
352
|
|
225
353
|
class RunTest < self
|
354
|
+
def setup_client
|
355
|
+
|
356
|
+
any_instance_of(MixpanelApi::Client) do |klass|
|
357
|
+
stub(klass).export(anything) { records }
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
226
361
|
def setup
|
227
362
|
super
|
228
363
|
|
@@ -286,7 +421,7 @@ module Embulk
|
|
286
421
|
api_secret: API_SECRET,
|
287
422
|
timezone: TIMEZONE,
|
288
423
|
schema: schema,
|
289
|
-
dates: DATES.to_a,
|
424
|
+
dates: DATES.to_a.map(&:to_s),
|
290
425
|
params: Mixpanel.export_params(embulk_config),
|
291
426
|
}
|
292
427
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: embulk-input-mixpanel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- yoshihara
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-08-
|
12
|
+
date: 2015-08-26 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|