launchdarkly-server-sdk 6.1.1 → 6.4.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.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -5
  3. data/lib/ldclient-rb/config.rb +118 -4
  4. data/lib/ldclient-rb/evaluation_detail.rb +104 -14
  5. data/lib/ldclient-rb/events.rb +201 -107
  6. data/lib/ldclient-rb/file_data_source.rb +9 -300
  7. data/lib/ldclient-rb/flags_state.rb +23 -12
  8. data/lib/ldclient-rb/impl/big_segments.rb +117 -0
  9. data/lib/ldclient-rb/impl/diagnostic_events.rb +1 -1
  10. data/lib/ldclient-rb/impl/evaluator.rb +116 -62
  11. data/lib/ldclient-rb/impl/evaluator_bucketing.rb +22 -9
  12. data/lib/ldclient-rb/impl/evaluator_helpers.rb +53 -0
  13. data/lib/ldclient-rb/impl/evaluator_operators.rb +1 -1
  14. data/lib/ldclient-rb/impl/event_summarizer.rb +63 -0
  15. data/lib/ldclient-rb/impl/event_types.rb +90 -0
  16. data/lib/ldclient-rb/impl/integrations/dynamodb_impl.rb +82 -18
  17. data/lib/ldclient-rb/impl/integrations/file_data_source.rb +212 -0
  18. data/lib/ldclient-rb/impl/integrations/redis_impl.rb +84 -31
  19. data/lib/ldclient-rb/impl/integrations/test_data/test_data_source.rb +40 -0
  20. data/lib/ldclient-rb/impl/model/preprocessed_data.rb +177 -0
  21. data/lib/ldclient-rb/impl/model/serialization.rb +7 -37
  22. data/lib/ldclient-rb/impl/repeating_task.rb +47 -0
  23. data/lib/ldclient-rb/impl/util.rb +62 -1
  24. data/lib/ldclient-rb/integrations/consul.rb +8 -1
  25. data/lib/ldclient-rb/integrations/dynamodb.rb +48 -3
  26. data/lib/ldclient-rb/integrations/file_data.rb +108 -0
  27. data/lib/ldclient-rb/integrations/redis.rb +42 -2
  28. data/lib/ldclient-rb/integrations/test_data/flag_builder.rb +438 -0
  29. data/lib/ldclient-rb/integrations/test_data.rb +209 -0
  30. data/lib/ldclient-rb/integrations/util/store_wrapper.rb +5 -0
  31. data/lib/ldclient-rb/integrations.rb +2 -51
  32. data/lib/ldclient-rb/interfaces.rb +152 -2
  33. data/lib/ldclient-rb/ldclient.rb +131 -33
  34. data/lib/ldclient-rb/polling.rb +22 -41
  35. data/lib/ldclient-rb/requestor.rb +3 -3
  36. data/lib/ldclient-rb/stream.rb +4 -3
  37. data/lib/ldclient-rb/util.rb +10 -1
  38. data/lib/ldclient-rb/version.rb +1 -1
  39. data/lib/ldclient-rb.rb +0 -1
  40. metadata +35 -132
  41. data/.circleci/config.yml +0 -40
  42. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -37
  43. data/.github/ISSUE_TEMPLATE/config.yml +0 -5
  44. data/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
  45. data/.github/pull_request_template.md +0 -21
  46. data/.gitignore +0 -16
  47. data/.hound.yml +0 -2
  48. data/.ldrelease/build-docs.sh +0 -18
  49. data/.ldrelease/circleci/linux/execute.sh +0 -18
  50. data/.ldrelease/circleci/mac/execute.sh +0 -18
  51. data/.ldrelease/circleci/template/build.sh +0 -29
  52. data/.ldrelease/circleci/template/publish.sh +0 -23
  53. data/.ldrelease/circleci/template/set-gem-home.sh +0 -7
  54. data/.ldrelease/circleci/template/test.sh +0 -10
  55. data/.ldrelease/circleci/template/update-version.sh +0 -8
  56. data/.ldrelease/circleci/windows/execute.ps1 +0 -19
  57. data/.ldrelease/config.yml +0 -29
  58. data/.rspec +0 -2
  59. data/.rubocop.yml +0 -600
  60. data/.simplecov +0 -4
  61. data/CHANGELOG.md +0 -351
  62. data/CODEOWNERS +0 -1
  63. data/CONTRIBUTING.md +0 -37
  64. data/Gemfile +0 -3
  65. data/azure-pipelines.yml +0 -51
  66. data/docs/Makefile +0 -26
  67. data/docs/index.md +0 -9
  68. data/launchdarkly-server-sdk.gemspec +0 -45
  69. data/lib/ldclient-rb/event_summarizer.rb +0 -55
  70. data/lib/ldclient-rb/impl/event_factory.rb +0 -120
  71. data/spec/config_spec.rb +0 -63
  72. data/spec/diagnostic_events_spec.rb +0 -163
  73. data/spec/evaluation_detail_spec.rb +0 -135
  74. data/spec/event_sender_spec.rb +0 -197
  75. data/spec/event_summarizer_spec.rb +0 -63
  76. data/spec/events_spec.rb +0 -607
  77. data/spec/expiring_cache_spec.rb +0 -76
  78. data/spec/feature_store_spec_base.rb +0 -213
  79. data/spec/file_data_source_spec.rb +0 -283
  80. data/spec/fixtures/feature.json +0 -37
  81. data/spec/fixtures/feature1.json +0 -36
  82. data/spec/fixtures/user.json +0 -9
  83. data/spec/flags_state_spec.rb +0 -81
  84. data/spec/http_util.rb +0 -132
  85. data/spec/impl/evaluator_bucketing_spec.rb +0 -111
  86. data/spec/impl/evaluator_clause_spec.rb +0 -55
  87. data/spec/impl/evaluator_operators_spec.rb +0 -141
  88. data/spec/impl/evaluator_rule_spec.rb +0 -96
  89. data/spec/impl/evaluator_segment_spec.rb +0 -125
  90. data/spec/impl/evaluator_spec.rb +0 -305
  91. data/spec/impl/evaluator_spec_base.rb +0 -75
  92. data/spec/impl/model/serialization_spec.rb +0 -41
  93. data/spec/in_memory_feature_store_spec.rb +0 -12
  94. data/spec/integrations/consul_feature_store_spec.rb +0 -40
  95. data/spec/integrations/dynamodb_feature_store_spec.rb +0 -103
  96. data/spec/integrations/store_wrapper_spec.rb +0 -276
  97. data/spec/launchdarkly-server-sdk_spec.rb +0 -13
  98. data/spec/launchdarkly-server-sdk_spec_autoloadtest.rb +0 -9
  99. data/spec/ldclient_end_to_end_spec.rb +0 -157
  100. data/spec/ldclient_spec.rb +0 -643
  101. data/spec/newrelic_spec.rb +0 -5
  102. data/spec/polling_spec.rb +0 -120
  103. data/spec/redis_feature_store_spec.rb +0 -121
  104. data/spec/requestor_spec.rb +0 -196
  105. data/spec/segment_store_spec_base.rb +0 -95
  106. data/spec/simple_lru_cache_spec.rb +0 -24
  107. data/spec/spec_helper.rb +0 -9
  108. data/spec/store_spec.rb +0 -10
  109. data/spec/stream_spec.rb +0 -45
  110. data/spec/user_filter_spec.rb +0 -91
  111. data/spec/util_spec.rb +0 -17
  112. data/spec/version_spec.rb +0 -7
@@ -1,63 +0,0 @@
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 DELETED
@@ -1,607 +0,0 @@
1
- require "http_util"
2
- require "spec_helper"
3
- require "time"
4
-
5
- describe LaunchDarkly::EventProcessor do
6
- subject { LaunchDarkly::EventProcessor }
7
-
8
- let(:default_config_opts) { { diagnostic_opt_out: true, logger: $null_log } }
9
- let(:default_config) { LaunchDarkly::Config.new(default_config_opts) }
10
- let(:user) { { key: "userkey", name: "Red" } }
11
- let(:filtered_user) { { key: "userkey", privateAttrs: [ "name" ] } }
12
- let(:numeric_user) { { key: 1, secondary: 2, ip: 3, country: 4, email: 5, firstName: 6, lastName: 7,
13
- avatar: 8, name: 9, anonymous: false, custom: { age: 99 } } }
14
- let(:stringified_numeric_user) { { key: '1', secondary: '2', ip: '3', country: '4', email: '5', firstName: '6',
15
- lastName: '7', avatar: '8', name: '9', anonymous: false, custom: { age: 99 } } }
16
-
17
- def with_processor_and_sender(config)
18
- sender = FakeEventSender.new
19
- ep = subject.new("sdk_key", config, nil, nil, { event_sender: sender })
20
- begin
21
- yield ep, sender
22
- ensure
23
- ep.stop
24
- end
25
- end
26
-
27
- it "queues identify event" do
28
- with_processor_and_sender(default_config) do |ep, sender|
29
- e = { kind: "identify", key: user[:key], user: user }
30
- ep.add_event(e)
31
-
32
- output = flush_and_get_events(ep, sender)
33
- expect(output).to contain_exactly(e)
34
- end
35
- end
36
-
37
- it "filters user in identify event" do
38
- config = LaunchDarkly::Config.new(default_config_opts.merge(all_attributes_private: true))
39
- with_processor_and_sender(config) do |ep, sender|
40
- e = { kind: "identify", key: user[:key], user: user }
41
- ep.add_event(e)
42
-
43
- output = flush_and_get_events(ep, sender)
44
- expect(output).to contain_exactly({
45
- kind: "identify",
46
- key: user[:key],
47
- creationDate: e[:creationDate],
48
- user: filtered_user
49
- })
50
- end
51
- end
52
-
53
- it "stringifies built-in user attributes in identify event" do
54
- with_processor_and_sender(default_config) do |ep, sender|
55
- flag = { key: "flagkey", version: 11 }
56
- e = { kind: "identify", key: numeric_user[:key], user: numeric_user }
57
- ep.add_event(e)
58
-
59
- output = flush_and_get_events(ep, sender)
60
- expect(output).to contain_exactly(
61
- kind: "identify",
62
- key: numeric_user[:key].to_s,
63
- creationDate: e[:creationDate],
64
- user: stringified_numeric_user
65
- )
66
- end
67
- end
68
-
69
- it "queues individual feature event with index event" do
70
- with_processor_and_sender(default_config) do |ep, sender|
71
- flag = { key: "flagkey", version: 11 }
72
- fe = {
73
- kind: "feature", key: "flagkey", version: 11, user: user,
74
- variation: 1, value: "value", trackEvents: true
75
- }
76
- ep.add_event(fe)
77
-
78
- output = flush_and_get_events(ep, sender)
79
- expect(output).to contain_exactly(
80
- eq(index_event(fe, user)),
81
- eq(feature_event(fe, flag, false, nil)),
82
- include(:kind => "summary")
83
- )
84
- end
85
- end
86
-
87
- it "filters user in index event" do
88
- config = LaunchDarkly::Config.new(default_config_opts.merge(all_attributes_private: true))
89
- with_processor_and_sender(config) do |ep, sender|
90
- flag = { key: "flagkey", version: 11 }
91
- fe = {
92
- kind: "feature", key: "flagkey", version: 11, user: user,
93
- variation: 1, value: "value", trackEvents: true
94
- }
95
- ep.add_event(fe)
96
-
97
- output = flush_and_get_events(ep, sender)
98
- expect(output).to contain_exactly(
99
- eq(index_event(fe, filtered_user)),
100
- eq(feature_event(fe, flag, false, nil)),
101
- include(:kind => "summary")
102
- )
103
- end
104
- end
105
-
106
- it "stringifies built-in user attributes in index event" do
107
- with_processor_and_sender(default_config) do |ep, sender|
108
- flag = { key: "flagkey", version: 11 }
109
- fe = {
110
- kind: "feature", key: "flagkey", version: 11, user: numeric_user,
111
- variation: 1, value: "value", trackEvents: true
112
- }
113
- ep.add_event(fe)
114
-
115
- output = flush_and_get_events(ep, sender)
116
- expect(output).to contain_exactly(
117
- eq(index_event(fe, stringified_numeric_user)),
118
- eq(feature_event(fe, flag, false, nil)),
119
- include(:kind => "summary")
120
- )
121
- end
122
- end
123
-
124
- it "can include inline user in feature event" do
125
- config = LaunchDarkly::Config.new(default_config_opts.merge(inline_users_in_events: true))
126
- with_processor_and_sender(config) do |ep, sender|
127
- flag = { key: "flagkey", version: 11 }
128
- fe = {
129
- kind: "feature", key: "flagkey", version: 11, user: user,
130
- variation: 1, value: "value", trackEvents: true
131
- }
132
- ep.add_event(fe)
133
-
134
- output = flush_and_get_events(ep, sender)
135
- expect(output).to contain_exactly(
136
- eq(feature_event(fe, flag, false, user)),
137
- include(:kind => "summary")
138
- )
139
- end
140
- end
141
-
142
- it "stringifies built-in user attributes in feature event" do
143
- config = LaunchDarkly::Config.new(default_config_opts.merge(inline_users_in_events: true))
144
- with_processor_and_sender(config) do |ep, sender|
145
- flag = { key: "flagkey", version: 11 }
146
- fe = {
147
- kind: "feature", key: "flagkey", version: 11, user: numeric_user,
148
- variation: 1, value: "value", trackEvents: true
149
- }
150
- ep.add_event(fe)
151
-
152
- output = flush_and_get_events(ep, sender)
153
- expect(output).to contain_exactly(
154
- eq(feature_event(fe, flag, false, stringified_numeric_user)),
155
- include(:kind => "summary")
156
- )
157
- end
158
- end
159
-
160
- it "filters user in feature event" do
161
- config = LaunchDarkly::Config.new(default_config_opts.merge(all_attributes_private: true, inline_users_in_events: true))
162
- with_processor_and_sender(config) do |ep, sender|
163
- flag = { key: "flagkey", version: 11 }
164
- fe = {
165
- kind: "feature", key: "flagkey", version: 11, user: user,
166
- variation: 1, value: "value", trackEvents: true
167
- }
168
- ep.add_event(fe)
169
-
170
- output = flush_and_get_events(ep, sender)
171
- expect(output).to contain_exactly(
172
- eq(feature_event(fe, flag, false, filtered_user)),
173
- include(:kind => "summary")
174
- )
175
- end
176
- end
177
-
178
- it "still generates index event if inline_users is true but feature event was not tracked" do
179
- config = LaunchDarkly::Config.new(default_config_opts.merge(inline_users_in_events: true))
180
- with_processor_and_sender(config) do |ep, sender|
181
- flag = { key: "flagkey", version: 11 }
182
- fe = {
183
- kind: "feature", key: "flagkey", version: 11, user: user,
184
- variation: 1, value: "value", trackEvents: false
185
- }
186
- ep.add_event(fe)
187
-
188
- output = flush_and_get_events(ep, sender)
189
- expect(output).to contain_exactly(
190
- eq(index_event(fe, user)),
191
- include(:kind => "summary")
192
- )
193
- end
194
- end
195
-
196
- it "sets event kind to debug if flag is temporarily in debug mode" do
197
- with_processor_and_sender(default_config) do |ep, sender|
198
- flag = { key: "flagkey", version: 11 }
199
- future_time = (Time.now.to_f * 1000).to_i + 1000000
200
- fe = {
201
- kind: "feature", key: "flagkey", version: 11, user: user,
202
- variation: 1, value: "value", trackEvents: false, debugEventsUntilDate: future_time
203
- }
204
- ep.add_event(fe)
205
-
206
- output = flush_and_get_events(ep, sender)
207
- expect(output).to contain_exactly(
208
- eq(index_event(fe, user)),
209
- eq(feature_event(fe, flag, true, user)),
210
- include(:kind => "summary")
211
- )
212
- end
213
- end
214
-
215
- it "can be both debugging and tracking an event" do
216
- with_processor_and_sender(default_config) do |ep, sender|
217
- flag = { key: "flagkey", version: 11 }
218
- future_time = (Time.now.to_f * 1000).to_i + 1000000
219
- fe = {
220
- kind: "feature", key: "flagkey", version: 11, user: user,
221
- variation: 1, value: "value", trackEvents: true, debugEventsUntilDate: future_time
222
- }
223
- ep.add_event(fe)
224
-
225
- output = flush_and_get_events(ep, sender)
226
- expect(output).to contain_exactly(
227
- eq(index_event(fe, user)),
228
- eq(feature_event(fe, flag, false, nil)),
229
- eq(feature_event(fe, flag, true, user)),
230
- include(:kind => "summary")
231
- )
232
- end
233
- end
234
-
235
- it "ends debug mode based on client time if client time is later than server time" do
236
- with_processor_and_sender(default_config) do |ep, sender|
237
- # Pick a server time that is somewhat behind the client time
238
- server_time = Time.now - 20
239
-
240
- # Send and flush an event we don't care about, just to set the last server time
241
- sender.result = LaunchDarkly::Impl::EventSenderResult.new(true, false, server_time)
242
- ep.add_event({ kind: "identify", user: user })
243
- flush_and_get_events(ep, sender)
244
-
245
- # Now send an event with debug mode on, with a "debug until" time that is further in
246
- # the future than the server time, but in the past compared to the client.
247
- flag = { key: "flagkey", version: 11 }
248
- debug_until = (server_time.to_f * 1000).to_i + 1000
249
- fe = {
250
- kind: "feature", key: "flagkey", version: 11, user: user,
251
- variation: 1, value: "value", trackEvents: false, debugEventsUntilDate: debug_until
252
- }
253
- ep.add_event(fe)
254
-
255
- # Should get a summary event only, not a full feature event
256
- output = flush_and_get_events(ep, sender)
257
- expect(output).to contain_exactly(
258
- include(:kind => "summary")
259
- )
260
- end
261
- end
262
-
263
- it "ends debug mode based on server time if server time is later than client time" do
264
- with_processor_and_sender(default_config) do |ep, sender|
265
- # Pick a server time that is somewhat ahead of the client time
266
- server_time = Time.now + 20
267
-
268
- # Send and flush an event we don't care about, just to set the last server time
269
- sender.result = LaunchDarkly::Impl::EventSenderResult.new(true, false, server_time)
270
- ep.add_event({ kind: "identify", user: user })
271
- flush_and_get_events(ep, sender)
272
-
273
- # Now send an event with debug mode on, with a "debug until" time that is further in
274
- # the future than the server time, but in the past compared to the client.
275
- flag = { key: "flagkey", version: 11 }
276
- debug_until = (server_time.to_f * 1000).to_i - 1000
277
- fe = {
278
- kind: "feature", key: "flagkey", version: 11, user: user,
279
- variation: 1, value: "value", trackEvents: false, debugEventsUntilDate: debug_until
280
- }
281
- ep.add_event(fe)
282
-
283
- # Should get a summary event only, not a full feature event
284
- output = flush_and_get_events(ep, sender)
285
- expect(output).to contain_exactly(
286
- include(:kind => "summary")
287
- )
288
- end
289
- end
290
-
291
- it "generates only one index event for multiple events with same user" do
292
- with_processor_and_sender(default_config) do |ep, sender|
293
- flag1 = { key: "flagkey1", version: 11 }
294
- flag2 = { key: "flagkey2", version: 22 }
295
- future_time = (Time.now.to_f * 1000).to_i + 1000000
296
- fe1 = {
297
- kind: "feature", key: "flagkey1", version: 11, user: user,
298
- variation: 1, value: "value", trackEvents: true
299
- }
300
- fe2 = {
301
- kind: "feature", key: "flagkey2", version: 22, user: user,
302
- variation: 1, value: "value", trackEvents: true
303
- }
304
- ep.add_event(fe1)
305
- ep.add_event(fe2)
306
-
307
- output = flush_and_get_events(ep, sender)
308
- expect(output).to contain_exactly(
309
- eq(index_event(fe1, user)),
310
- eq(feature_event(fe1, flag1, false, nil)),
311
- eq(feature_event(fe2, flag2, false, nil)),
312
- include(:kind => "summary")
313
- )
314
- end
315
- end
316
-
317
- it "summarizes non-tracked events" do
318
- with_processor_and_sender(default_config) do |ep, sender|
319
- flag1 = { key: "flagkey1", version: 11 }
320
- flag2 = { key: "flagkey2", version: 22 }
321
- future_time = (Time.now.to_f * 1000).to_i + 1000000
322
- fe1 = {
323
- kind: "feature", key: "flagkey1", version: 11, user: user,
324
- variation: 1, value: "value1", default: "default1"
325
- }
326
- fe2 = {
327
- kind: "feature", key: "flagkey2", version: 22, user: user,
328
- variation: 2, value: "value2", default: "default2"
329
- }
330
- ep.add_event(fe1)
331
- ep.add_event(fe2)
332
-
333
- output = flush_and_get_events(ep, sender)
334
- expect(output).to contain_exactly(
335
- eq(index_event(fe1, user)),
336
- eq({
337
- kind: "summary",
338
- startDate: fe1[:creationDate],
339
- endDate: fe2[:creationDate],
340
- features: {
341
- flagkey1: {
342
- default: "default1",
343
- counters: [
344
- { version: 11, variation: 1, value: "value1", count: 1 }
345
- ]
346
- },
347
- flagkey2: {
348
- default: "default2",
349
- counters: [
350
- { version: 22, variation: 2, value: "value2", count: 1 }
351
- ]
352
- }
353
- }
354
- })
355
- )
356
- end
357
- end
358
-
359
- it "queues custom event with user" do
360
- with_processor_and_sender(default_config) do |ep, sender|
361
- e = { kind: "custom", key: "eventkey", user: user, data: { thing: "stuff" }, metricValue: 1.5 }
362
- ep.add_event(e)
363
-
364
- output = flush_and_get_events(ep, sender)
365
- expect(output).to contain_exactly(
366
- eq(index_event(e, user)),
367
- eq(custom_event(e, nil))
368
- )
369
- end
370
- end
371
-
372
- it "can include inline user in custom event" do
373
- config = LaunchDarkly::Config.new(default_config_opts.merge(inline_users_in_events: true))
374
- with_processor_and_sender(config) do |ep, sender|
375
- e = { kind: "custom", key: "eventkey", user: user, data: { thing: "stuff" } }
376
- ep.add_event(e)
377
-
378
- output = flush_and_get_events(ep, sender)
379
- expect(output).to contain_exactly(
380
- eq(custom_event(e, user))
381
- )
382
- end
383
- end
384
-
385
- it "filters user in custom event" do
386
- config = LaunchDarkly::Config.new(default_config_opts.merge(all_attributes_private: true, inline_users_in_events: true))
387
- with_processor_and_sender(config) do |ep, sender|
388
- e = { kind: "custom", key: "eventkey", user: user, data: { thing: "stuff" } }
389
- ep.add_event(e)
390
-
391
- output = flush_and_get_events(ep, sender)
392
- expect(output).to contain_exactly(
393
- eq(custom_event(e, filtered_user))
394
- )
395
- end
396
- end
397
-
398
- it "stringifies built-in user attributes in custom event" do
399
- config = LaunchDarkly::Config.new(default_config_opts.merge(inline_users_in_events: true))
400
- with_processor_and_sender(config) do |ep, sender|
401
- e = { kind: "custom", key: "eventkey", user: numeric_user }
402
- ep.add_event(e)
403
-
404
- output = flush_and_get_events(ep, sender)
405
- expect(output).to contain_exactly(
406
- eq(custom_event(e, stringified_numeric_user))
407
- )
408
- end
409
- end
410
-
411
- it "queues alias event" do
412
- with_processor_and_sender(default_config) do |ep, sender|
413
- e = { kind: "alias", key: "a", contextKind: "user", previousKey: "b", previousContextKind: "user" }
414
- ep.add_event(e)
415
-
416
- output = flush_and_get_events(ep, sender)
417
- expect(output).to contain_exactly(e)
418
- end
419
- end
420
-
421
- it "treats nil value for custom the same as an empty hash" do
422
- with_processor_and_sender(default_config) do |ep, sender|
423
- user_with_nil_custom = { key: "userkey", custom: nil }
424
- e = { kind: "identify", key: "userkey", user: user_with_nil_custom }
425
- ep.add_event(e)
426
-
427
- output = flush_and_get_events(ep, sender)
428
- expect(output).to contain_exactly(e)
429
- end
430
- end
431
-
432
- it "does a final flush when shutting down" do
433
- with_processor_and_sender(default_config) do |ep, sender|
434
- e = { kind: "identify", key: user[:key], user: user }
435
- ep.add_event(e)
436
-
437
- ep.stop
438
-
439
- output = sender.analytics_payloads.pop
440
- expect(output).to contain_exactly(e)
441
- end
442
- end
443
-
444
- it "sends nothing if there are no events" do
445
- with_processor_and_sender(default_config) do |ep, sender|
446
- ep.flush
447
- ep.wait_until_inactive
448
- expect(sender.analytics_payloads.empty?).to be true
449
- end
450
- end
451
-
452
- it "stops posting events after unrecoverable error" do
453
- with_processor_and_sender(default_config) do |ep, sender|
454
- sender.result = LaunchDarkly::Impl::EventSenderResult.new(false, true, nil)
455
- e = { kind: "identify", key: user[:key], user: user }
456
- ep.add_event(e)
457
- flush_and_get_events(ep, sender)
458
-
459
- e = { kind: "identify", key: user[:key], user: user }
460
- ep.add_event(e)
461
- ep.flush
462
- ep.wait_until_inactive
463
- expect(sender.analytics_payloads.empty?).to be true
464
- end
465
- end
466
-
467
- describe "diagnostic events" do
468
- let(:default_id) { LaunchDarkly::Impl::DiagnosticAccumulator.create_diagnostic_id('sdk_key') }
469
- let(:diagnostic_config) { LaunchDarkly::Config.new(diagnostic_opt_out: false, logger: $null_log) }
470
-
471
- def with_diagnostic_processor_and_sender(config)
472
- sender = FakeEventSender.new
473
- acc = LaunchDarkly::Impl::DiagnosticAccumulator.new(default_id)
474
- ep = subject.new("sdk_key", config, nil, acc,
475
- { diagnostic_recording_interval: 0.2, event_sender: sender })
476
- begin
477
- yield ep, sender
478
- ensure
479
- ep.stop
480
- end
481
- end
482
-
483
- it "sends init event" do
484
- with_diagnostic_processor_and_sender(diagnostic_config) do |ep, sender|
485
- event = sender.diagnostic_payloads.pop
486
- expect(event).to include({
487
- kind: 'diagnostic-init',
488
- id: default_id
489
- })
490
- end
491
- end
492
-
493
- it "sends periodic event" do
494
- with_diagnostic_processor_and_sender(diagnostic_config) do |ep, sender|
495
- init_event = sender.diagnostic_payloads.pop
496
- periodic_event = sender.diagnostic_payloads.pop
497
- expect(periodic_event).to include({
498
- kind: 'diagnostic',
499
- id: default_id,
500
- droppedEvents: 0,
501
- deduplicatedUsers: 0,
502
- eventsInLastBatch: 0,
503
- streamInits: []
504
- })
505
- end
506
- end
507
-
508
- it "counts events in queue from last flush and dropped events" do
509
- config = LaunchDarkly::Config.new(diagnostic_opt_out: false, capacity: 2, logger: $null_log)
510
- with_diagnostic_processor_and_sender(config) do |ep, sender|
511
- init_event = sender.diagnostic_payloads.pop
512
-
513
- ep.add_event({ kind: 'identify', user: user })
514
- ep.add_event({ kind: 'identify', user: user })
515
- ep.add_event({ kind: 'identify', user: user })
516
- flush_and_get_events(ep, sender)
517
-
518
- periodic_event = sender.diagnostic_payloads.pop
519
- expect(periodic_event).to include({
520
- kind: 'diagnostic',
521
- droppedEvents: 1,
522
- eventsInLastBatch: 2
523
- })
524
- end
525
- end
526
-
527
- it "counts deduplicated users" do
528
- with_diagnostic_processor_and_sender(diagnostic_config) do |ep, sender|
529
- init_event = sender.diagnostic_payloads.pop
530
-
531
- ep.add_event({ kind: 'custom', key: 'event1', user: user })
532
- ep.add_event({ kind: 'custom', key: 'event2', user: user })
533
- events = flush_and_get_events(ep, sender)
534
-
535
- periodic_event = sender.diagnostic_payloads.pop
536
- expect(periodic_event).to include({
537
- kind: 'diagnostic',
538
- deduplicatedUsers: 1
539
- })
540
- end
541
- end
542
- end
543
-
544
- def index_event(e, user)
545
- {
546
- kind: "index",
547
- creationDate: e[:creationDate],
548
- user: user
549
- }
550
- end
551
-
552
- def feature_event(e, flag, debug, inline_user)
553
- out = {
554
- kind: debug ? "debug" : "feature",
555
- creationDate: e[:creationDate],
556
- key: flag[:key],
557
- variation: e[:variation],
558
- version: flag[:version],
559
- value: e[:value]
560
- }
561
- if inline_user.nil?
562
- out[:userKey] = e[:user][:key]
563
- else
564
- out[:user] = inline_user
565
- end
566
- out
567
- end
568
-
569
- def custom_event(e, inline_user)
570
- out = {
571
- kind: "custom",
572
- creationDate: e[:creationDate],
573
- key: e[:key]
574
- }
575
- out[:data] = e[:data] if e.has_key?(:data)
576
- if inline_user.nil?
577
- out[:userKey] = e[:user][:key]
578
- else
579
- out[:user] = inline_user
580
- end
581
- out[:metricValue] = e[:metricValue] if e.has_key?(:metricValue)
582
- out
583
- end
584
-
585
- def flush_and_get_events(ep, sender)
586
- ep.flush
587
- ep.wait_until_inactive
588
- sender.analytics_payloads.pop
589
- end
590
-
591
- class FakeEventSender
592
- attr_accessor :result
593
- attr_reader :analytics_payloads
594
- attr_reader :diagnostic_payloads
595
-
596
- def initialize
597
- @result = LaunchDarkly::Impl::EventSenderResult.new(true, false, nil)
598
- @analytics_payloads = Queue.new
599
- @diagnostic_payloads = Queue.new
600
- end
601
-
602
- def send_event_data(data, description, is_diagnostic)
603
- (is_diagnostic ? @diagnostic_payloads : @analytics_payloads).push(JSON.parse(data, symbolize_names: true))
604
- @result
605
- end
606
- end
607
- end