embulk-input-mixpanel 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5fca90578e059bd058fa52d1d8283afbd85a58a1
4
- data.tar.gz: 09305de32323e4284f33a005330d522f2afc98e7
3
+ metadata.gz: 9a688fb2eb175f13ee5c82b8ea2da6c74ed69839
4
+ data.tar.gz: af0fdae8cc6fba0911ab269d7d8f354d51803f25
5
5
  SHA512:
6
- metadata.gz: 8a200ccc100c2baa7552e933846648253bace0b944a7cefa8bfcc87198ab0e5dfe2a8a754a3195ac2aeca096114a92defc74c3d7e35176a2d8475bcac830cf83
7
- data.tar.gz: 91c0591f7bf83a14dc5bdb7f56f2915c97ba8e3d3b81cac8f37ac411541c0ae5302bf2712c6f4e6347a542955c5278a085cab67925a8da6559ce0195dc12edaf
6
+ metadata.gz: 25381a19a9743a0b945d5d453931b1f93ab87009a79185b72eba32577a496550f1050b38ff8baa81c164d628a1c67714e785c27bf6833a04e69431bcad2e6ace
7
+ data.tar.gz: d69d65730c8c3a3d1ea1c85182e274ea8bde716662c132ee628e1c0be7d25e2b97c0a2c15dc0a7269b714677cf1635f04fcdd8a8237ec7353714d4f1ede05094
data/.travis.yml CHANGED
@@ -23,3 +23,4 @@ gemfile:
23
23
  matrix:
24
24
  allow_failures:
25
25
  - gemfile: gemfiles/embulk-0.6.22
26
+ - gemfile: gemfiles/embulk-latest # embulk 0.7.1
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
- - **days**: Count of days range for exporting (integer, optional, default: (today - 1) - from_date)
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)
@@ -1,7 +1,7 @@
1
1
 
2
2
  Gem::Specification.new do |spec|
3
3
  spec.name = "embulk-input-mixpanel"
4
- spec.version = "0.2.0"
4
+ spec.version = "0.2.1"
5
5
  spec.authors = ["yoshihara", "uu59"]
6
6
  spec.summary = "Mixpanel input plugin for Embulk"
7
7
  spec.description = "Loads records from Mixpanel."
@@ -18,8 +18,40 @@ module Embulk
18
18
 
19
19
  task[:params] = export_params(config)
20
20
 
21
- dates = generate_dates(config)
22
- task[:dates] = dates.map {|date| date.to_s}
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
- from_date = config.param(:from_date, :string)
64
- # NOTE: It should have 7 days beteen from_date and to_date
65
- to_date = (Date.parse(from_date) + SLICE_DAYS_COUNT - 1).to_s
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
@@ -9,6 +9,7 @@ module Embulk
9
9
 
10
10
  def setup
11
11
  @client = Client.new(API_KEY, API_SECRET)
12
+ stub(Embulk).logger { ::Logger.new(IO::NULL) }
12
13
  end
13
14
 
14
15
  # NOTE: Client#signature is private method but this value
@@ -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
- def test_guess
49
- expected = {
50
- "columns" => [
51
- {name: "event", type: :string},
52
- {name: "foo", type: :string},
53
- {name: "time", type: :long},
54
- {name: "int", type: :long},
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
- actual = Mixpanel.guess(embulk_config)
59
- assert_equal(expected, actual)
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 GenerateDatesTest < self
143
+ class TransactionDateTest < self
63
144
  def test_valid_from_date
64
145
  from_date = "2015-08-14"
65
- parsed_date = Date.parse(from_date)
146
+ mock(Mixpanel).resume(anything, anything, 1)
66
147
 
67
- expected = (parsed_date..(parsed_date + DAYS))
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.generate_dates(transaction_config(from_date))
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.0
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-17 00:00:00.000000000 Z
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