mixpanel_client 3.1.4 → 4.0.0
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 +7 -0
- data/.rubocop.yml +9 -0
- data/.rvmrc +2 -2
- data/Rakefile +2 -2
- data/changelog.md +70 -0
- data/lib/mixpanel/client.rb +39 -18
- data/lib/mixpanel/uri.rb +2 -2
- data/lib/mixpanel/utils.rb +14 -2
- data/lib/mixpanel/version.rb +1 -1
- data/{LICENSE → license} +0 -0
- data/manual_test/basic.rb +16 -8
- data/manual_test/parallel.rb +20 -13
- data/mixpanel_client.gemspec +7 -5
- data/{README.md → readme.md} +1 -64
- data/rubocop-todo.yml +10 -0
- data/spec/mixpanel_client/events_externalspec.rb +53 -46
- data/spec/mixpanel_client/mixpanel_client_spec.rb +218 -96
- data/spec/mixpanel_client/properties_externalspec.rb +20 -13
- data/spec/mixpanel_client/uri_spec.rb +38 -16
- metadata +99 -46
@@ -2,34 +2,53 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
|
2
2
|
|
3
3
|
describe Mixpanel::Client do
|
4
4
|
before :all do
|
5
|
-
@client = Mixpanel::Client.new(
|
5
|
+
@client = Mixpanel::Client.new(
|
6
|
+
api_key: 'test_key',
|
7
|
+
api_secret: 'test_secret'
|
8
|
+
)
|
9
|
+
|
6
10
|
@uri = Regexp.escape(Mixpanel::Client::BASE_URI)
|
7
11
|
end
|
8
12
|
|
9
13
|
context 'when initializing a new Mixpanel::Client' do
|
10
14
|
it 'should not raise an exception if a hash is given' do
|
11
|
-
Mixpanel::Client.new(
|
15
|
+
Mixpanel::Client.new(
|
16
|
+
'api_key' => 'test_key',
|
17
|
+
'api_secret' => 'test_secret'
|
18
|
+
).should_not { raise_error }
|
12
19
|
end
|
13
20
|
|
14
21
|
it 'should set a parallel option as false by default' do
|
15
|
-
Mixpanel::Client.new(
|
22
|
+
Mixpanel::Client.new(
|
23
|
+
api_key: 'test_key',
|
24
|
+
api_secret: 'test_secret'
|
25
|
+
).parallel.should eq false
|
16
26
|
end
|
17
27
|
|
18
28
|
it 'should be able to set a parallel option when passed' do
|
19
|
-
Mixpanel::Client.new(
|
29
|
+
Mixpanel::Client.new(
|
30
|
+
api_key: 'test_key',
|
31
|
+
api_secret: 'test_secret',
|
32
|
+
parallel: true
|
33
|
+
).parallel.should eq true
|
20
34
|
end
|
21
35
|
end
|
22
36
|
|
23
37
|
context 'when making an invalid request' do
|
24
|
-
it 'should return an argument error "Wrong number of arguments" if using
|
38
|
+
it 'should return an argument error "Wrong number of arguments" if using
|
39
|
+
the deprecated usage' do
|
25
40
|
# Stub Mixpanel request
|
26
|
-
stub_request(:get, /^#{@uri}.*/)
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
41
|
+
stub_request(:get, /^#{@uri}.*/)
|
42
|
+
.to_return(
|
43
|
+
body: '{"legend_size": 0, "data": {"series": [], "values": {}}}'
|
44
|
+
)
|
45
|
+
|
46
|
+
data = lambda do@client.request(nil, :events,
|
47
|
+
event: '["test-event"]',
|
48
|
+
unit: 'hour',
|
49
|
+
interval: 24
|
50
|
+
)
|
51
|
+
end
|
33
52
|
data.should raise_error(ArgumentError)
|
34
53
|
end
|
35
54
|
end
|
@@ -37,91 +56,134 @@ describe Mixpanel::Client do
|
|
37
56
|
context 'when making a valid request' do
|
38
57
|
it 'should work without an endpoint' do
|
39
58
|
# Stub Mixpanel request
|
40
|
-
stub_request(:get, /^#{@uri}.*/)
|
59
|
+
stub_request(:get, /^#{@uri}.*/)
|
60
|
+
.to_return(
|
61
|
+
body: '{"legend_size": 0, "data": {"series": [], "values": {}}}'
|
62
|
+
)
|
41
63
|
|
42
64
|
# No endpoint
|
43
|
-
data = @client.request('events',
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
data.should
|
65
|
+
data = @client.request('events',
|
66
|
+
event: '["test-event"]',
|
67
|
+
unit: 'hour',
|
68
|
+
interval: 24
|
69
|
+
)
|
70
|
+
data.should eq(
|
71
|
+
'data' => {
|
72
|
+
'series' => [],
|
73
|
+
'values' => {}
|
74
|
+
},
|
75
|
+
'legend_size' => 0
|
76
|
+
)
|
49
77
|
end
|
50
78
|
|
51
79
|
it 'should work when it receives an integer response on import' do
|
52
80
|
@import_uri = Regexp.escape(Mixpanel::Client::IMPORT_URI)
|
53
81
|
# Stub Mixpanel import request to return a realistic response
|
54
|
-
stub_request(:get, /^#{@import_uri}.*/).to_return(:
|
82
|
+
stub_request(:get, /^#{@import_uri}.*/).to_return(body: '1')
|
55
83
|
|
56
|
-
data = @client.request('import',
|
57
|
-
|
58
|
-
|
59
|
-
|
84
|
+
data = @client.request('import',
|
85
|
+
data: 'base64_encoded_data',
|
86
|
+
api_key: 'test_key'
|
87
|
+
)
|
60
88
|
data.should == [1]
|
61
89
|
end
|
62
90
|
|
63
91
|
it 'should work with an endpoint, method, and type' do
|
64
92
|
# Stub Mixpanel request
|
65
|
-
stub_request(:get, /^#{@uri}.*/)
|
93
|
+
stub_request(:get, /^#{@uri}.*/)
|
94
|
+
.to_return(
|
95
|
+
body: '{"events": [], "type": "general"}'
|
96
|
+
)
|
66
97
|
|
67
98
|
# With endpoint
|
68
|
-
data = @client.request(
|
69
|
-
|
70
|
-
|
71
|
-
|
99
|
+
data = @client.request(
|
100
|
+
'events/top',
|
101
|
+
type: 'general'
|
102
|
+
)
|
103
|
+
|
104
|
+
data.should eq(
|
105
|
+
'events' => [],
|
106
|
+
'type' => 'general'
|
107
|
+
)
|
72
108
|
end
|
73
109
|
|
74
110
|
it 'does not modify the provided options' do
|
75
111
|
options = { foo: 'bar' }
|
76
112
|
# Stub Mixpanel request
|
77
|
-
stub_request(:get, /^#{@uri}.*/)
|
78
|
-
|
113
|
+
stub_request(:get, /^#{@uri}.*/)
|
114
|
+
.to_return(
|
115
|
+
body: '{"events": [], "type": "general"}'
|
116
|
+
)
|
117
|
+
|
118
|
+
expect do
|
79
119
|
@client.request('events/top', options)
|
80
|
-
|
120
|
+
end.to_not change { options }
|
81
121
|
end
|
82
122
|
|
83
123
|
context 'with a custom expiry time' do
|
84
124
|
# Stub Mixpanel request
|
85
|
-
before
|
86
|
-
|
125
|
+
before do
|
126
|
+
stub_request(:get, /^#{@uri}.*/)
|
127
|
+
.to_return(
|
128
|
+
body: '{"events": [], "type": "general"}'
|
129
|
+
)
|
130
|
+
end
|
131
|
+
|
132
|
+
let(:expiry) { Time.now + 100_000 }
|
87
133
|
let(:fake_url) { Mixpanel::Client::BASE_URI }
|
88
134
|
|
89
|
-
specify 'Client#request should return a hash with empty events and
|
135
|
+
specify 'Client#request should return a hash with empty events and
|
136
|
+
type' do
|
90
137
|
# With endpoint
|
91
|
-
data = @client.request(
|
92
|
-
|
93
|
-
:
|
94
|
-
|
95
|
-
|
138
|
+
data = @client.request(
|
139
|
+
'events/top',
|
140
|
+
type: 'general',
|
141
|
+
expire: expiry
|
142
|
+
)
|
143
|
+
|
144
|
+
data.should eq(
|
145
|
+
'events' => [],
|
146
|
+
'type' => 'general'
|
147
|
+
)
|
96
148
|
end
|
97
149
|
|
98
|
-
specify 'Mixpanel::URI instance should receive the custom expiry time in
|
150
|
+
specify 'Mixpanel::URI instance should receive the custom expiry time in
|
151
|
+
the options[:expiry] instead of 600s' do
|
99
152
|
Mixpanel::URI.should_receive(:mixpanel).with do |*args|
|
100
|
-
args.pop[:expire].should
|
153
|
+
args.pop[:expire].should eq expiry.to_i
|
101
154
|
true
|
102
155
|
end.and_return(fake_url)
|
103
|
-
@client.request('events/top',
|
104
|
-
|
105
|
-
|
106
|
-
|
156
|
+
@client.request('events/top',
|
157
|
+
type: 'general',
|
158
|
+
expire: expiry
|
159
|
+
)
|
107
160
|
end
|
108
161
|
end
|
109
162
|
|
110
|
-
context
|
163
|
+
context 'with parallel option enabled' do
|
111
164
|
before :all do
|
112
|
-
@parallel_client = Mixpanel::Client.new(
|
165
|
+
@parallel_client = Mixpanel::Client.new(
|
166
|
+
api_key: 'test_key',
|
167
|
+
api_secret: 'test_secret',
|
168
|
+
parallel: true
|
169
|
+
)
|
113
170
|
end
|
114
171
|
|
115
|
-
it
|
172
|
+
it 'should return Typhoeus::Request' do
|
116
173
|
# Stub Mixpanel request
|
117
|
-
stub_request(:get, /^#{@uri}.*/)
|
174
|
+
stub_request(:get, /^#{@uri}.*/)
|
175
|
+
.to_return(
|
176
|
+
body: '{"legend_size": 0, "data": {"series": [], "values": {}}}'
|
177
|
+
)
|
118
178
|
|
119
179
|
# No endpoint
|
120
|
-
data = @parallel_client.request(
|
121
|
-
|
122
|
-
:
|
123
|
-
:
|
124
|
-
|
180
|
+
data = @parallel_client.request(
|
181
|
+
'events',
|
182
|
+
event: '["test-event"]',
|
183
|
+
unit: 'hour',
|
184
|
+
interval: 24
|
185
|
+
)
|
186
|
+
|
125
187
|
data.should be_a Typhoeus::Request
|
126
188
|
end
|
127
189
|
|
@@ -134,45 +196,89 @@ describe Mixpanel::Client do
|
|
134
196
|
describe '#run_parallel_requests' do
|
135
197
|
it 'should run queued requests' do
|
136
198
|
# Stub Mixpanel request
|
137
|
-
stub_request(:any, /^#{@uri}.*/)
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
199
|
+
stub_request(:any, /^#{@uri}.*/)
|
200
|
+
.to_return(
|
201
|
+
body: '{
|
202
|
+
"legend_size": 1,
|
203
|
+
"data": {
|
204
|
+
"series": ["2010-05-29","2010-05-30","2010-05-31"],
|
205
|
+
"values": {
|
206
|
+
"account-page": {"2010-05-30": 1},
|
207
|
+
"splash features": {
|
208
|
+
"2010-05-29": 6,
|
209
|
+
"2010-05-30": 4,
|
210
|
+
"2010-05-31": 5
|
211
|
+
}
|
212
|
+
}
|
213
|
+
}
|
214
|
+
}'
|
215
|
+
)
|
216
|
+
|
217
|
+
stub_request(:any, /^#{@uri}.*secondevent.*/)
|
218
|
+
.to_return(
|
219
|
+
body: '{
|
220
|
+
"legend_size": 2,
|
221
|
+
"data": {
|
222
|
+
"series": ["2010-05-29","2010-05-30","2010-05-31"],
|
223
|
+
"values": {
|
224
|
+
"account-page": {"2010-05-30": 2},
|
225
|
+
"splash features": {
|
226
|
+
"2010-05-29": 8,
|
227
|
+
"2010-05-30": 6,
|
228
|
+
"2010-05-31": 7
|
229
|
+
}
|
230
|
+
}
|
231
|
+
}
|
232
|
+
}'
|
233
|
+
)
|
234
|
+
|
235
|
+
first_request = @parallel_client.request(
|
236
|
+
'events',
|
237
|
+
event: '["firstevent"]',
|
238
|
+
unit: 'day'
|
239
|
+
)
|
240
|
+
|
241
|
+
second_request = @parallel_client.request(
|
242
|
+
'events',
|
243
|
+
event: '["secondevent"]',
|
244
|
+
unit: 'day'
|
245
|
+
)
|
170
246
|
|
171
247
|
@parallel_client.run_parallel_requests
|
172
248
|
|
173
|
-
first_request.response.handled_response.should
|
174
|
-
|
175
|
-
|
249
|
+
first_request.response.handled_response.should eq(
|
250
|
+
'data' => {
|
251
|
+
'series' => %w(2010-05-29 2010-05-30 2010-05-31),
|
252
|
+
'values' => {
|
253
|
+
'splash features' => {
|
254
|
+
'2010-05-29' => 6,
|
255
|
+
'2010-05-30' => 4,
|
256
|
+
'2010-05-31' => 5
|
257
|
+
},
|
258
|
+
'account-page' => {
|
259
|
+
'2010-05-30' => 1
|
260
|
+
}
|
261
|
+
}
|
262
|
+
},
|
263
|
+
'legend_size' => 1
|
264
|
+
)
|
265
|
+
|
266
|
+
second_request.response.handled_response.should eq(
|
267
|
+
'data' => {
|
268
|
+
'series' => %w(2010-05-29 2010-05-30 2010-05-31),
|
269
|
+
'values' => {
|
270
|
+
'splash features' => {
|
271
|
+
'2010-05-29' => 8,
|
272
|
+
'2010-05-30' => 6,
|
273
|
+
'2010-05-31' => 7
|
274
|
+
},
|
275
|
+
'account-page' => {
|
276
|
+
'2010-05-30' => 2
|
277
|
+
}
|
278
|
+
}
|
279
|
+
},
|
280
|
+
'legend_size' => 2
|
281
|
+
)
|
176
282
|
end
|
177
283
|
end
|
178
284
|
end
|
@@ -180,16 +286,32 @@ describe Mixpanel::Client do
|
|
180
286
|
|
181
287
|
describe '#hash_args' do
|
182
288
|
it 'should return a hashed string alpha sorted by key names.' do
|
183
|
-
args = {:
|
184
|
-
args_alpha_sorted = {:
|
185
|
-
|
289
|
+
args = { c: 'see', a: 'ey', d: 'dee', b: 'bee' }
|
290
|
+
args_alpha_sorted = { a: 'ey', b: 'bee', c: 'see', d: 'dee' }
|
291
|
+
|
292
|
+
unsorted_signature = Mixpanel::Client::Utils.generate_signature(
|
293
|
+
args,
|
294
|
+
@client.api_secret
|
295
|
+
)
|
296
|
+
|
297
|
+
sorted_signature = Mixpanel::Client::Utils.generate_signature(
|
298
|
+
args_alpha_sorted,
|
299
|
+
@client.api_secret
|
300
|
+
)
|
301
|
+
|
302
|
+
unsorted_signature.should eq sorted_signature
|
186
303
|
end
|
187
304
|
end
|
188
305
|
|
189
306
|
describe '#to_hash' do
|
190
307
|
it 'should return a ruby hash given json as a string' do
|
191
|
-
Mixpanel::Client::Utils.to_hash(
|
308
|
+
Mixpanel::Client::Utils.to_hash(
|
309
|
+
'{"a" : "ey", "b" : "bee"}',
|
310
|
+
:json
|
311
|
+
).should eq(
|
312
|
+
'a' => 'ey',
|
313
|
+
'b' => 'bee'
|
314
|
+
)
|
192
315
|
end
|
193
316
|
end
|
194
|
-
|
195
317
|
end
|
@@ -4,30 +4,37 @@ WebMock.allow_net_connect!
|
|
4
4
|
|
5
5
|
describe 'External calls to mixpanel' do
|
6
6
|
before :all do
|
7
|
-
config = YAML.load_file(File.
|
7
|
+
config = YAML.load_file(File.join(
|
8
|
+
File.dirname(__FILE__),
|
9
|
+
'..',
|
10
|
+
'..',
|
11
|
+
'config',
|
12
|
+
'mixpanel.yml'
|
13
|
+
))['mixpanel']
|
14
|
+
|
8
15
|
config.should_not be_nil
|
9
16
|
@client = Mixpanel::Client.new(config)
|
10
17
|
end
|
11
18
|
|
12
19
|
context 'when requesting event properties' do
|
13
20
|
it 'should raise an error for bad requests' do
|
14
|
-
data = lambda
|
21
|
+
data = lambda do
|
15
22
|
@client.request('properties', {})
|
16
|
-
|
23
|
+
end
|
17
24
|
data.should raise_error(Mixpanel::HTTPError)
|
18
25
|
end
|
19
26
|
|
20
27
|
it 'should return events' do
|
21
|
-
data = @client.request('events/properties',
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
data = @client.request('events/properties',
|
29
|
+
event: '["test-event"]',
|
30
|
+
name: 'hello',
|
31
|
+
values: '["uno", "dos"]',
|
32
|
+
type: 'general',
|
33
|
+
unit: 'hour',
|
34
|
+
interval: 24,
|
35
|
+
limit: 5,
|
36
|
+
bucket: 'kicked'
|
37
|
+
)
|
31
38
|
data.should_not be_a Exception
|
32
39
|
end
|
33
40
|
end
|
@@ -3,35 +3,57 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
|
3
3
|
|
4
4
|
describe Mixpanel::URI do
|
5
5
|
describe '.mixpanel' do
|
6
|
-
it 'should return a properly formatted mixpanel uri as a string (without an
|
7
|
-
|
8
|
-
|
6
|
+
it 'should return a properly formatted mixpanel uri as a string (without an
|
7
|
+
endpoint)' do
|
8
|
+
resource, params = ['events', { c: 'see', a: 'ey' }]
|
9
|
+
|
10
|
+
Mixpanel::URI.mixpanel(resource, params).should eq(
|
11
|
+
"#{Mixpanel::Client::BASE_URI}/events?a=ey&c=see"
|
12
|
+
)
|
9
13
|
end
|
10
|
-
|
11
|
-
|
12
|
-
|
14
|
+
|
15
|
+
it 'should return a properly formatted mixpanel uri as a string (with an
|
16
|
+
endpoint)' do
|
17
|
+
resource, params = ['events/top', { c: 'see', a: 'ey' }]
|
18
|
+
|
19
|
+
Mixpanel::URI.mixpanel(resource, params).should eq(
|
20
|
+
"#{Mixpanel::Client::BASE_URI}/events/top?a=ey&c=see"
|
21
|
+
)
|
13
22
|
end
|
14
|
-
|
15
|
-
|
16
|
-
|
23
|
+
|
24
|
+
it 'should return a uri with a different endpoint when doing a raw data
|
25
|
+
export' do
|
26
|
+
resource, params = ['export', { c: 'see', a: 'ey' }]
|
27
|
+
|
28
|
+
Mixpanel::URI.mixpanel(resource, params).should eq(
|
29
|
+
"#{Mixpanel::Client::DATA_URI}/export?a=ey&c=see"
|
30
|
+
)
|
17
31
|
end
|
18
|
-
|
19
|
-
|
20
|
-
|
32
|
+
|
33
|
+
it 'should return a uri with a the correct endpoint when doing an
|
34
|
+
import' do
|
35
|
+
resource, params = ['import', { c: 'see', a: 'ey' }]
|
36
|
+
Mixpanel::URI.mixpanel(resource, params).should eq(
|
37
|
+
"#{Mixpanel::Client::IMPORT_URI}/import?a=ey&c=see"
|
38
|
+
)
|
21
39
|
end
|
22
40
|
end
|
23
41
|
|
24
42
|
describe '.encode' do
|
25
43
|
it 'should return a string with url encoded values.' do
|
26
|
-
params = {:
|
27
|
-
|
44
|
+
params = { hey: '!@#$%^&*()\/"Ü', soo: 'hëllö?' }
|
45
|
+
|
46
|
+
Mixpanel::URI.encode(params).should eq(
|
47
|
+
'hey=%21%40%23%24%25%5E%26%2A%28%29%5C%2F%22%C3%9C&soo=h%C3%ABll%C3%B6%3F' # rubocop:disable LineLength
|
48
|
+
)
|
28
49
|
end
|
29
50
|
end
|
30
51
|
|
31
52
|
describe '.get' do
|
32
53
|
it 'should return a string response' do
|
33
|
-
stub_request(:get, 'http://example.com').to_return(:
|
34
|
-
|
54
|
+
stub_request(:get, 'http://example.com').to_return(body: 'something')
|
55
|
+
|
56
|
+
Mixpanel::URI.get('http://example.com').should eq 'something'
|
35
57
|
end
|
36
58
|
end
|
37
59
|
end
|