ldclient-rb 3.0.3 → 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 +4 -4
- data/.circleci/config.yml +90 -0
- data/CHANGELOG.md +10 -0
- data/README.md +0 -1
- data/ldclient-rb.gemspec +8 -2
- data/lib/ldclient-rb.rb +5 -1
- data/lib/ldclient-rb/config.rb +41 -1
- data/lib/ldclient-rb/evaluation.rb +33 -17
- data/lib/ldclient-rb/event_summarizer.rb +52 -0
- data/lib/ldclient-rb/events.rb +383 -51
- data/lib/ldclient-rb/expiring_cache.rb +76 -0
- data/lib/ldclient-rb/ldclient.rb +44 -23
- data/lib/ldclient-rb/non_blocking_thread_pool.rb +46 -0
- data/lib/ldclient-rb/redis_store.rb +13 -17
- data/lib/ldclient-rb/simple_lru_cache.rb +24 -0
- data/lib/ldclient-rb/{event_serializer.rb → user_filter.rb} +17 -23
- data/lib/ldclient-rb/version.rb +1 -1
- data/spec/evaluation_spec.rb +44 -9
- data/spec/event_summarizer_spec.rb +63 -0
- data/spec/events_spec.rb +506 -0
- data/spec/expiring_cache_spec.rb +76 -0
- data/spec/fixtures/feature.json +1 -0
- data/spec/ldclient_spec.rb +94 -17
- data/spec/simple_lru_cache_spec.rb +24 -0
- data/spec/{event_serializer_spec.rb → user_filter_spec.rb} +23 -44
- metadata +49 -23
- data/circle.yml +0 -35
@@ -0,0 +1,63 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe LaunchDarkly::EventSummarizer do
|
4
|
+
subject { LaunchDarkly::EventSummarizer }
|
5
|
+
|
6
|
+
let(:user) { { key: "key" } }
|
7
|
+
|
8
|
+
it "does not add identify event to summary" do
|
9
|
+
es = subject.new
|
10
|
+
snapshot = es.snapshot
|
11
|
+
es.summarize_event({ kind: "identify", user: user })
|
12
|
+
|
13
|
+
expect(es.snapshot).to eq snapshot
|
14
|
+
end
|
15
|
+
|
16
|
+
it "does not add custom event to summary" do
|
17
|
+
es = subject.new
|
18
|
+
snapshot = es.snapshot
|
19
|
+
es.summarize_event({ kind: "custom", key: "whatever", user: user })
|
20
|
+
|
21
|
+
expect(es.snapshot).to eq snapshot
|
22
|
+
end
|
23
|
+
|
24
|
+
it "tracks start and end dates" do
|
25
|
+
es = subject.new
|
26
|
+
flag = { key: "key" }
|
27
|
+
event1 = { kind: "feature", creationDate: 2000, user: user }
|
28
|
+
event2 = { kind: "feature", creationDate: 1000, user: user }
|
29
|
+
event3 = { kind: "feature", creationDate: 1500, user: user }
|
30
|
+
es.summarize_event(event1)
|
31
|
+
es.summarize_event(event2)
|
32
|
+
es.summarize_event(event3)
|
33
|
+
data = es.snapshot
|
34
|
+
|
35
|
+
expect(data.start_date).to be 1000
|
36
|
+
expect(data.end_date).to be 2000
|
37
|
+
end
|
38
|
+
|
39
|
+
it "counts events" do
|
40
|
+
es = subject.new
|
41
|
+
flag1 = { key: "key1", version: 11 }
|
42
|
+
flag2 = { key: "key2", version: 22 }
|
43
|
+
event1 = { kind: "feature", key: "key1", version: 11, user: user, variation: 1, value: "value1", default: "default1" }
|
44
|
+
event2 = { kind: "feature", key: "key1", version: 11, user: user, variation: 2, value: "value2", default: "default1" }
|
45
|
+
event3 = { kind: "feature", key: "key2", version: 22, user: user, variation: 1, value: "value99", default: "default2" }
|
46
|
+
event4 = { kind: "feature", key: "key1", version: 11, user: user, variation: 1, value: "value1", default: "default1" }
|
47
|
+
event5 = { kind: "feature", key: "badkey", user: user, variation: nil, value: "default3", default: "default3" }
|
48
|
+
[event1, event2, event3, event4, event5].each { |e| es.summarize_event(e) }
|
49
|
+
data = es.snapshot
|
50
|
+
|
51
|
+
expectedCounters = {
|
52
|
+
{ key: "key1", version: 11, variation: 1 } =>
|
53
|
+
{ count: 2, value: "value1", default: "default1" },
|
54
|
+
{ key: "key1", version: 11, variation: 2 } =>
|
55
|
+
{ count: 1, value: "value2", default: "default1" },
|
56
|
+
{ key: "key2", version: 22, variation: 1 } =>
|
57
|
+
{ count: 1, value: "value99", default: "default2" },
|
58
|
+
{ key: "badkey", version: nil, variation: nil } =>
|
59
|
+
{ count: 1, value: "default3", default: "default3" }
|
60
|
+
}
|
61
|
+
expect(data.counters).to eq expectedCounters
|
62
|
+
end
|
63
|
+
end
|
data/spec/events_spec.rb
ADDED
@@ -0,0 +1,506 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "faraday"
|
3
|
+
require "time"
|
4
|
+
|
5
|
+
describe LaunchDarkly::EventProcessor do
|
6
|
+
subject { LaunchDarkly::EventProcessor }
|
7
|
+
|
8
|
+
let(:default_config) { LaunchDarkly::Config.new }
|
9
|
+
let(:hc) { FakeHttpClient.new }
|
10
|
+
let(:user) { { key: "userkey", name: "Red" } }
|
11
|
+
let(:filtered_user) { { key: "userkey", privateAttrs: [ "name" ] } }
|
12
|
+
|
13
|
+
after(:each) do
|
14
|
+
if !@ep.nil?
|
15
|
+
@ep.stop
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it "queues identify event" do
|
20
|
+
@ep = subject.new("sdk_key", default_config, hc)
|
21
|
+
e = { kind: "identify", key: user[:key], user: user }
|
22
|
+
@ep.add_event(e)
|
23
|
+
|
24
|
+
output = flush_and_get_events
|
25
|
+
expect(output).to contain_exactly(e)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "filters user in identify event" do
|
29
|
+
config = LaunchDarkly::Config.new(all_attributes_private: true)
|
30
|
+
@ep = subject.new("sdk_key", config, hc)
|
31
|
+
e = { kind: "identify", key: user[:key], user: user }
|
32
|
+
@ep.add_event(e)
|
33
|
+
|
34
|
+
output = flush_and_get_events
|
35
|
+
expect(output).to contain_exactly({
|
36
|
+
kind: "identify",
|
37
|
+
key: user[:key],
|
38
|
+
creationDate: e[:creationDate],
|
39
|
+
user: filtered_user
|
40
|
+
})
|
41
|
+
end
|
42
|
+
|
43
|
+
it "queues individual feature event with index event" do
|
44
|
+
@ep = subject.new("sdk_key", default_config, hc)
|
45
|
+
flag = { key: "flagkey", version: 11 }
|
46
|
+
fe = {
|
47
|
+
kind: "feature", key: "flagkey", version: 11, user: user,
|
48
|
+
variation: 1, value: "value", trackEvents: true
|
49
|
+
}
|
50
|
+
@ep.add_event(fe)
|
51
|
+
|
52
|
+
output = flush_and_get_events
|
53
|
+
expect(output).to contain_exactly(
|
54
|
+
eq(index_event(fe, user)),
|
55
|
+
eq(feature_event(fe, flag, false, nil)),
|
56
|
+
include(:kind => "summary")
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "filters user in index event" do
|
61
|
+
config = LaunchDarkly::Config.new(all_attributes_private: true)
|
62
|
+
@ep = subject.new("sdk_key", config, hc)
|
63
|
+
flag = { key: "flagkey", version: 11 }
|
64
|
+
fe = {
|
65
|
+
kind: "feature", key: "flagkey", version: 11, user: user,
|
66
|
+
variation: 1, value: "value", trackEvents: true
|
67
|
+
}
|
68
|
+
@ep.add_event(fe)
|
69
|
+
|
70
|
+
output = flush_and_get_events
|
71
|
+
expect(output).to contain_exactly(
|
72
|
+
eq(index_event(fe, filtered_user)),
|
73
|
+
eq(feature_event(fe, flag, false, nil)),
|
74
|
+
include(:kind => "summary")
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "can include inline user in feature event" do
|
79
|
+
config = LaunchDarkly::Config.new(inline_users_in_events: true)
|
80
|
+
@ep = subject.new("sdk_key", config, hc)
|
81
|
+
flag = { key: "flagkey", version: 11 }
|
82
|
+
fe = {
|
83
|
+
kind: "feature", key: "flagkey", version: 11, user: user,
|
84
|
+
variation: 1, value: "value", trackEvents: true
|
85
|
+
}
|
86
|
+
@ep.add_event(fe)
|
87
|
+
|
88
|
+
output = flush_and_get_events
|
89
|
+
expect(output).to contain_exactly(
|
90
|
+
eq(feature_event(fe, flag, false, user)),
|
91
|
+
include(:kind => "summary")
|
92
|
+
)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "filters user in feature event" do
|
96
|
+
config = LaunchDarkly::Config.new(all_attributes_private: true, inline_users_in_events: true)
|
97
|
+
@ep = subject.new("sdk_key", config, hc)
|
98
|
+
flag = { key: "flagkey", version: 11 }
|
99
|
+
fe = {
|
100
|
+
kind: "feature", key: "flagkey", version: 11, user: user,
|
101
|
+
variation: 1, value: "value", trackEvents: true
|
102
|
+
}
|
103
|
+
@ep.add_event(fe)
|
104
|
+
|
105
|
+
output = flush_and_get_events
|
106
|
+
expect(output).to contain_exactly(
|
107
|
+
eq(feature_event(fe, flag, false, filtered_user)),
|
108
|
+
include(:kind => "summary")
|
109
|
+
)
|
110
|
+
end
|
111
|
+
|
112
|
+
it "still generates index event if inline_users is true but feature event was not tracked" do
|
113
|
+
config = LaunchDarkly::Config.new(inline_users_in_events: true)
|
114
|
+
@ep = subject.new("sdk_key", config, hc)
|
115
|
+
flag = { key: "flagkey", version: 11 }
|
116
|
+
fe = {
|
117
|
+
kind: "feature", key: "flagkey", version: 11, user: user,
|
118
|
+
variation: 1, value: "value", trackEvents: false
|
119
|
+
}
|
120
|
+
@ep.add_event(fe)
|
121
|
+
|
122
|
+
output = flush_and_get_events
|
123
|
+
expect(output).to contain_exactly(
|
124
|
+
eq(index_event(fe, user)),
|
125
|
+
include(:kind => "summary")
|
126
|
+
)
|
127
|
+
end
|
128
|
+
|
129
|
+
it "sets event kind to debug if flag is temporarily in debug mode" do
|
130
|
+
@ep = subject.new("sdk_key", default_config, hc)
|
131
|
+
flag = { key: "flagkey", version: 11 }
|
132
|
+
future_time = (Time.now.to_f * 1000).to_i + 1000000
|
133
|
+
fe = {
|
134
|
+
kind: "feature", key: "flagkey", version: 11, user: user,
|
135
|
+
variation: 1, value: "value", trackEvents: false, debugEventsUntilDate: future_time
|
136
|
+
}
|
137
|
+
@ep.add_event(fe)
|
138
|
+
|
139
|
+
output = flush_and_get_events
|
140
|
+
expect(output).to contain_exactly(
|
141
|
+
eq(index_event(fe, user)),
|
142
|
+
eq(feature_event(fe, flag, true, user)),
|
143
|
+
include(:kind => "summary")
|
144
|
+
)
|
145
|
+
end
|
146
|
+
|
147
|
+
it "can be both debugging and tracking an event" do
|
148
|
+
@ep = subject.new("sdk_key", default_config, hc)
|
149
|
+
flag = { key: "flagkey", version: 11 }
|
150
|
+
future_time = (Time.now.to_f * 1000).to_i + 1000000
|
151
|
+
fe = {
|
152
|
+
kind: "feature", key: "flagkey", version: 11, user: user,
|
153
|
+
variation: 1, value: "value", trackEvents: true, debugEventsUntilDate: future_time
|
154
|
+
}
|
155
|
+
@ep.add_event(fe)
|
156
|
+
|
157
|
+
output = flush_and_get_events
|
158
|
+
expect(output).to contain_exactly(
|
159
|
+
eq(index_event(fe, user)),
|
160
|
+
eq(feature_event(fe, flag, false, nil)),
|
161
|
+
eq(feature_event(fe, flag, true, user)),
|
162
|
+
include(:kind => "summary")
|
163
|
+
)
|
164
|
+
end
|
165
|
+
|
166
|
+
it "ends debug mode based on client time if client time is later than server time" do
|
167
|
+
@ep = subject.new("sdk_key", default_config, hc)
|
168
|
+
|
169
|
+
# Pick a server time that is somewhat behind the client time
|
170
|
+
server_time = (Time.now.to_f * 1000).to_i - 20000
|
171
|
+
|
172
|
+
# Send and flush an event we don't care about, just to set the last server time
|
173
|
+
hc.set_server_time(server_time)
|
174
|
+
@ep.add_event({ kind: "identify", user: { key: "otherUser" }})
|
175
|
+
flush_and_get_events
|
176
|
+
|
177
|
+
# Now send an event with debug mode on, with a "debug until" time that is further in
|
178
|
+
# the future than the server time, but in the past compared to the client.
|
179
|
+
flag = { key: "flagkey", version: 11 }
|
180
|
+
debug_until = server_time + 1000
|
181
|
+
fe = {
|
182
|
+
kind: "feature", key: "flagkey", version: 11, user: user,
|
183
|
+
variation: 1, value: "value", trackEvents: false, debugEventsUntilDate: debug_until
|
184
|
+
}
|
185
|
+
@ep.add_event(fe)
|
186
|
+
|
187
|
+
# Should get a summary event only, not a full feature event
|
188
|
+
output = flush_and_get_events
|
189
|
+
expect(output).to contain_exactly(
|
190
|
+
eq(index_event(fe, user)),
|
191
|
+
include(:kind => "summary")
|
192
|
+
)
|
193
|
+
end
|
194
|
+
|
195
|
+
it "ends debug mode based on server time if server time is later than client time" do
|
196
|
+
@ep = subject.new("sdk_key", default_config, hc)
|
197
|
+
|
198
|
+
# Pick a server time that is somewhat ahead of the client time
|
199
|
+
server_time = (Time.now.to_f * 1000).to_i + 20000
|
200
|
+
|
201
|
+
# Send and flush an event we don't care about, just to set the last server time
|
202
|
+
hc.set_server_time(server_time)
|
203
|
+
@ep.add_event({ kind: "identify", user: { key: "otherUser" }})
|
204
|
+
flush_and_get_events
|
205
|
+
|
206
|
+
# Now send an event with debug mode on, with a "debug until" time that is further in
|
207
|
+
# the future than the server time, but in the past compared to the client.
|
208
|
+
flag = { key: "flagkey", version: 11 }
|
209
|
+
debug_until = server_time - 1000
|
210
|
+
fe = {
|
211
|
+
kind: "feature", key: "flagkey", version: 11, user: user,
|
212
|
+
variation: 1, value: "value", trackEvents: false, debugEventsUntilDate: debug_until
|
213
|
+
}
|
214
|
+
@ep.add_event(fe)
|
215
|
+
|
216
|
+
# Should get a summary event only, not a full feature event
|
217
|
+
output = flush_and_get_events
|
218
|
+
expect(output).to contain_exactly(
|
219
|
+
eq(index_event(fe, user)),
|
220
|
+
include(:kind => "summary")
|
221
|
+
)
|
222
|
+
end
|
223
|
+
|
224
|
+
it "generates only one index event for multiple events with same user" do
|
225
|
+
@ep = subject.new("sdk_key", default_config, hc)
|
226
|
+
flag1 = { key: "flagkey1", version: 11 }
|
227
|
+
flag2 = { key: "flagkey2", version: 22 }
|
228
|
+
future_time = (Time.now.to_f * 1000).to_i + 1000000
|
229
|
+
fe1 = {
|
230
|
+
kind: "feature", key: "flagkey1", version: 11, user: user,
|
231
|
+
variation: 1, value: "value", trackEvents: true
|
232
|
+
}
|
233
|
+
fe2 = {
|
234
|
+
kind: "feature", key: "flagkey2", version: 22, user: user,
|
235
|
+
variation: 1, value: "value", trackEvents: true
|
236
|
+
}
|
237
|
+
@ep.add_event(fe1)
|
238
|
+
@ep.add_event(fe2)
|
239
|
+
|
240
|
+
output = flush_and_get_events
|
241
|
+
expect(output).to contain_exactly(
|
242
|
+
eq(index_event(fe1, user)),
|
243
|
+
eq(feature_event(fe1, flag1, false, nil)),
|
244
|
+
eq(feature_event(fe2, flag2, false, nil)),
|
245
|
+
include(:kind => "summary")
|
246
|
+
)
|
247
|
+
end
|
248
|
+
|
249
|
+
it "summarizes non-tracked events" do
|
250
|
+
@ep = subject.new("sdk_key", default_config, hc)
|
251
|
+
flag1 = { key: "flagkey1", version: 11 }
|
252
|
+
flag2 = { key: "flagkey2", version: 22 }
|
253
|
+
future_time = (Time.now.to_f * 1000).to_i + 1000000
|
254
|
+
fe1 = {
|
255
|
+
kind: "feature", key: "flagkey1", version: 11, user: user,
|
256
|
+
variation: 1, value: "value1", default: "default1"
|
257
|
+
}
|
258
|
+
fe2 = {
|
259
|
+
kind: "feature", key: "flagkey2", version: 22, user: user,
|
260
|
+
variation: 2, value: "value2", default: "default2"
|
261
|
+
}
|
262
|
+
@ep.add_event(fe1)
|
263
|
+
@ep.add_event(fe2)
|
264
|
+
|
265
|
+
output = flush_and_get_events
|
266
|
+
expect(output).to contain_exactly(
|
267
|
+
eq(index_event(fe1, user)),
|
268
|
+
eq({
|
269
|
+
kind: "summary",
|
270
|
+
startDate: fe1[:creationDate],
|
271
|
+
endDate: fe2[:creationDate],
|
272
|
+
features: {
|
273
|
+
flagkey1: {
|
274
|
+
default: "default1",
|
275
|
+
counters: [
|
276
|
+
{ version: 11, variation: 1, value: "value1", count: 1 }
|
277
|
+
]
|
278
|
+
},
|
279
|
+
flagkey2: {
|
280
|
+
default: "default2",
|
281
|
+
counters: [
|
282
|
+
{ version: 22, variation: 2, value: "value2", count: 1 }
|
283
|
+
]
|
284
|
+
}
|
285
|
+
}
|
286
|
+
})
|
287
|
+
)
|
288
|
+
end
|
289
|
+
|
290
|
+
it "queues custom event with user" do
|
291
|
+
@ep = subject.new("sdk_key", default_config, hc)
|
292
|
+
e = { kind: "custom", key: "eventkey", user: user, data: { thing: "stuff" } }
|
293
|
+
@ep.add_event(e)
|
294
|
+
|
295
|
+
output = flush_and_get_events
|
296
|
+
expect(output).to contain_exactly(
|
297
|
+
eq(index_event(e, user)),
|
298
|
+
eq(custom_event(e, nil))
|
299
|
+
)
|
300
|
+
end
|
301
|
+
|
302
|
+
it "can include inline user in custom event" do
|
303
|
+
config = LaunchDarkly::Config.new(inline_users_in_events: true)
|
304
|
+
@ep = subject.new("sdk_key", config, hc)
|
305
|
+
e = { kind: "custom", key: "eventkey", user: user, data: { thing: "stuff" } }
|
306
|
+
@ep.add_event(e)
|
307
|
+
|
308
|
+
output = flush_and_get_events
|
309
|
+
expect(output).to contain_exactly(
|
310
|
+
eq(custom_event(e, user))
|
311
|
+
)
|
312
|
+
end
|
313
|
+
|
314
|
+
it "filters user in custom event" do
|
315
|
+
config = LaunchDarkly::Config.new(all_attributes_private: true, inline_users_in_events: true)
|
316
|
+
@ep = subject.new("sdk_key", config, hc)
|
317
|
+
e = { kind: "custom", key: "eventkey", user: user, data: { thing: "stuff" } }
|
318
|
+
@ep.add_event(e)
|
319
|
+
|
320
|
+
output = flush_and_get_events
|
321
|
+
expect(output).to contain_exactly(
|
322
|
+
eq(custom_event(e, filtered_user))
|
323
|
+
)
|
324
|
+
end
|
325
|
+
|
326
|
+
it "does a final flush when shutting down" do
|
327
|
+
@ep = subject.new("sdk_key", default_config, hc)
|
328
|
+
e = { kind: "identify", key: user[:key], user: user }
|
329
|
+
@ep.add_event(e)
|
330
|
+
|
331
|
+
@ep.stop
|
332
|
+
|
333
|
+
output = get_events_from_last_request
|
334
|
+
expect(output).to contain_exactly(e)
|
335
|
+
end
|
336
|
+
|
337
|
+
it "sends nothing if there are no events" do
|
338
|
+
@ep = subject.new("sdk_key", default_config, hc)
|
339
|
+
@ep.flush
|
340
|
+
expect(hc.get_request).to be nil
|
341
|
+
end
|
342
|
+
|
343
|
+
it "sends SDK key" do
|
344
|
+
@ep = subject.new("sdk_key", default_config, hc)
|
345
|
+
e = { kind: "identify", user: user }
|
346
|
+
@ep.add_event(e)
|
347
|
+
|
348
|
+
@ep.flush
|
349
|
+
@ep.wait_until_inactive
|
350
|
+
|
351
|
+
expect(hc.get_request.headers["Authorization"]).to eq "sdk_key"
|
352
|
+
end
|
353
|
+
|
354
|
+
it "stops posting events after getting a 401 error" do
|
355
|
+
@ep = subject.new("sdk_key", default_config, hc)
|
356
|
+
e = { kind: "identify", user: user }
|
357
|
+
@ep.add_event(e)
|
358
|
+
|
359
|
+
hc.set_response_status(401)
|
360
|
+
@ep.flush
|
361
|
+
@ep.wait_until_inactive
|
362
|
+
expect(hc.get_request).not_to be_nil
|
363
|
+
hc.reset
|
364
|
+
|
365
|
+
@ep.add_event(e)
|
366
|
+
@ep.flush
|
367
|
+
@ep.wait_until_inactive
|
368
|
+
expect(hc.get_request).to be_nil
|
369
|
+
end
|
370
|
+
|
371
|
+
it "retries flush once after 5xx error" do
|
372
|
+
@ep = subject.new("sdk_key", default_config, hc)
|
373
|
+
e = { kind: "identify", user: user }
|
374
|
+
@ep.add_event(e)
|
375
|
+
|
376
|
+
hc.set_response_status(503)
|
377
|
+
@ep.flush
|
378
|
+
@ep.wait_until_inactive
|
379
|
+
|
380
|
+
expect(hc.get_request).not_to be_nil
|
381
|
+
expect(hc.get_request).not_to be_nil
|
382
|
+
expect(hc.get_request).to be_nil # no 3rd request
|
383
|
+
end
|
384
|
+
|
385
|
+
it "retries flush once after connection error" do
|
386
|
+
@ep = subject.new("sdk_key", default_config, hc)
|
387
|
+
e = { kind: "identify", user: user }
|
388
|
+
@ep.add_event(e)
|
389
|
+
|
390
|
+
hc.set_exception(Faraday::Error::ConnectionFailed.new("fail"))
|
391
|
+
@ep.flush
|
392
|
+
@ep.wait_until_inactive
|
393
|
+
|
394
|
+
expect(hc.get_request).not_to be_nil
|
395
|
+
expect(hc.get_request).not_to be_nil
|
396
|
+
expect(hc.get_request).to be_nil # no 3rd request
|
397
|
+
end
|
398
|
+
|
399
|
+
def index_event(e, user)
|
400
|
+
{
|
401
|
+
kind: "index",
|
402
|
+
creationDate: e[:creationDate],
|
403
|
+
user: user
|
404
|
+
}
|
405
|
+
end
|
406
|
+
|
407
|
+
def feature_event(e, flag, debug, inline_user)
|
408
|
+
out = {
|
409
|
+
kind: debug ? "debug" : "feature",
|
410
|
+
creationDate: e[:creationDate],
|
411
|
+
key: flag[:key],
|
412
|
+
variation: e[:variation],
|
413
|
+
version: flag[:version],
|
414
|
+
value: e[:value]
|
415
|
+
}
|
416
|
+
if inline_user.nil?
|
417
|
+
out[:userKey] = e[:user][:key]
|
418
|
+
else
|
419
|
+
out[:user] = inline_user
|
420
|
+
end
|
421
|
+
out
|
422
|
+
end
|
423
|
+
|
424
|
+
def custom_event(e, inline_user)
|
425
|
+
out = {
|
426
|
+
kind: "custom",
|
427
|
+
creationDate: e[:creationDate],
|
428
|
+
key: e[:key]
|
429
|
+
}
|
430
|
+
out[:data] = e[:data] if e.has_key?(:data)
|
431
|
+
if inline_user.nil?
|
432
|
+
out[:userKey] = e[:user][:key]
|
433
|
+
else
|
434
|
+
out[:user] = inline_user
|
435
|
+
end
|
436
|
+
out
|
437
|
+
end
|
438
|
+
|
439
|
+
def flush_and_get_events
|
440
|
+
@ep.flush
|
441
|
+
@ep.wait_until_inactive
|
442
|
+
get_events_from_last_request
|
443
|
+
end
|
444
|
+
|
445
|
+
def get_events_from_last_request
|
446
|
+
req = hc.get_request
|
447
|
+
JSON.parse(req.body, symbolize_names: true)
|
448
|
+
end
|
449
|
+
|
450
|
+
class FakeHttpClient
|
451
|
+
def initialize
|
452
|
+
reset
|
453
|
+
end
|
454
|
+
|
455
|
+
def set_response_status(status)
|
456
|
+
@status = status
|
457
|
+
end
|
458
|
+
|
459
|
+
def set_server_time(time_millis)
|
460
|
+
@server_time = Time.at(time_millis.to_f / 1000)
|
461
|
+
end
|
462
|
+
|
463
|
+
def set_exception(e)
|
464
|
+
@exception = e
|
465
|
+
end
|
466
|
+
|
467
|
+
def reset
|
468
|
+
@requests = []
|
469
|
+
@status = 200
|
470
|
+
end
|
471
|
+
|
472
|
+
def post(uri)
|
473
|
+
req = Faraday::Request.create("POST")
|
474
|
+
req.headers = {}
|
475
|
+
req.options = Faraday::RequestOptions.new
|
476
|
+
yield req
|
477
|
+
@requests.push(req)
|
478
|
+
if @exception
|
479
|
+
raise @exception
|
480
|
+
else
|
481
|
+
resp = Faraday::Response.new
|
482
|
+
headers = {}
|
483
|
+
if @server_time
|
484
|
+
headers["Date"] = @server_time.httpdate
|
485
|
+
end
|
486
|
+
resp.finish({
|
487
|
+
status: @status ? @status : 200,
|
488
|
+
response_headers: headers
|
489
|
+
})
|
490
|
+
resp
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
def get_request
|
495
|
+
@requests.shift
|
496
|
+
end
|
497
|
+
end
|
498
|
+
|
499
|
+
class FakeResponse
|
500
|
+
def initialize(status)
|
501
|
+
@status = status
|
502
|
+
end
|
503
|
+
|
504
|
+
attr_reader :status
|
505
|
+
end
|
506
|
+
end
|