launchdarkly-server-sdk 8.11.2-java → 8.12.0-java

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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/lib/ldclient-rb/config.rb +69 -9
  3. data/lib/ldclient-rb/context.rb +1 -1
  4. data/lib/ldclient-rb/data_system.rb +227 -0
  5. data/lib/ldclient-rb/events.rb +34 -19
  6. data/lib/ldclient-rb/flags_state.rb +1 -1
  7. data/lib/ldclient-rb/impl/big_segments.rb +4 -4
  8. data/lib/ldclient-rb/impl/cache_store.rb +44 -0
  9. data/lib/ldclient-rb/impl/data_source/polling.rb +108 -0
  10. data/lib/ldclient-rb/impl/data_source/requestor.rb +113 -0
  11. data/lib/ldclient-rb/impl/data_source/status_provider.rb +83 -0
  12. data/lib/ldclient-rb/impl/data_source/stream.rb +198 -0
  13. data/lib/ldclient-rb/impl/data_source.rb +3 -3
  14. data/lib/ldclient-rb/impl/data_store/data_kind.rb +108 -0
  15. data/lib/ldclient-rb/impl/data_store/feature_store_client_wrapper.rb +187 -0
  16. data/lib/ldclient-rb/impl/data_store/in_memory_feature_store.rb +130 -0
  17. data/lib/ldclient-rb/impl/data_store/status_provider.rb +76 -0
  18. data/lib/ldclient-rb/impl/data_store/store.rb +371 -0
  19. data/lib/ldclient-rb/impl/data_store.rb +11 -97
  20. data/lib/ldclient-rb/impl/data_system/data_source_builder_common.rb +77 -0
  21. data/lib/ldclient-rb/impl/data_system/fdv1.rb +20 -7
  22. data/lib/ldclient-rb/impl/data_system/fdv2.rb +472 -0
  23. data/lib/ldclient-rb/impl/data_system/http_config_options.rb +32 -0
  24. data/lib/ldclient-rb/impl/data_system/polling.rb +628 -0
  25. data/lib/ldclient-rb/impl/data_system/protocolv2.rb +264 -0
  26. data/lib/ldclient-rb/impl/data_system/streaming.rb +401 -0
  27. data/lib/ldclient-rb/impl/dependency_tracker.rb +21 -9
  28. data/lib/ldclient-rb/impl/evaluator.rb +3 -2
  29. data/lib/ldclient-rb/impl/event_sender.rb +14 -6
  30. data/lib/ldclient-rb/impl/expiring_cache.rb +79 -0
  31. data/lib/ldclient-rb/impl/integrations/file_data_source.rb +8 -8
  32. data/lib/ldclient-rb/impl/integrations/file_data_source_v2.rb +460 -0
  33. data/lib/ldclient-rb/impl/integrations/test_data/test_data_source_v2.rb +290 -0
  34. data/lib/ldclient-rb/impl/memoized_value.rb +34 -0
  35. data/lib/ldclient-rb/impl/migrations/migrator.rb +2 -1
  36. data/lib/ldclient-rb/impl/migrations/tracker.rb +2 -1
  37. data/lib/ldclient-rb/impl/model/serialization.rb +6 -6
  38. data/lib/ldclient-rb/impl/non_blocking_thread_pool.rb +48 -0
  39. data/lib/ldclient-rb/impl/repeating_task.rb +2 -2
  40. data/lib/ldclient-rb/impl/simple_lru_cache.rb +27 -0
  41. data/lib/ldclient-rb/impl/store_data_set_sorter.rb +1 -1
  42. data/lib/ldclient-rb/impl/util.rb +71 -0
  43. data/lib/ldclient-rb/impl.rb +1 -2
  44. data/lib/ldclient-rb/in_memory_store.rb +1 -18
  45. data/lib/ldclient-rb/integrations/file_data.rb +67 -0
  46. data/lib/ldclient-rb/integrations/test_data/flag_builder.rb +9 -9
  47. data/lib/ldclient-rb/integrations/test_data.rb +11 -11
  48. data/lib/ldclient-rb/integrations/test_data_v2/flag_builder_v2.rb +582 -0
  49. data/lib/ldclient-rb/integrations/test_data_v2.rb +254 -0
  50. data/lib/ldclient-rb/integrations/util/store_wrapper.rb +3 -2
  51. data/lib/ldclient-rb/interfaces/data_system.rb +704 -0
  52. data/lib/ldclient-rb/interfaces/feature_store.rb +5 -2
  53. data/lib/ldclient-rb/ldclient.rb +66 -132
  54. data/lib/ldclient-rb/util.rb +11 -70
  55. data/lib/ldclient-rb/version.rb +1 -1
  56. data/lib/ldclient-rb.rb +9 -17
  57. metadata +41 -19
  58. data/lib/ldclient-rb/cache_store.rb +0 -45
  59. data/lib/ldclient-rb/expiring_cache.rb +0 -77
  60. data/lib/ldclient-rb/memoized_value.rb +0 -32
  61. data/lib/ldclient-rb/non_blocking_thread_pool.rb +0 -46
  62. data/lib/ldclient-rb/polling.rb +0 -102
  63. data/lib/ldclient-rb/requestor.rb +0 -102
  64. data/lib/ldclient-rb/simple_lru_cache.rb +0 -25
  65. data/lib/ldclient-rb/stream.rb +0 -197
@@ -0,0 +1,254 @@
1
+ require 'ldclient-rb/impl/integrations/test_data/test_data_source_v2'
2
+ require 'ldclient-rb/impl/model/feature_flag'
3
+ require 'ldclient-rb/integrations/test_data_v2/flag_builder_v2'
4
+ require 'concurrent/atomics'
5
+
6
+ module LaunchDarkly
7
+ module Integrations
8
+ #
9
+ # A mechanism for providing dynamically updatable feature flag state in a
10
+ # simplified form to an SDK client in test scenarios using the FDv2 protocol.
11
+ #
12
+ # This class is not stable, and not subject to any backwards compatibility guarantees or semantic versioning.
13
+ # It is in early access. If you want access to this feature please join the EAP. https://launchdarkly.com/docs/sdk/features/data-saving-mode
14
+ #
15
+ # Unlike {LaunchDarkly::Integrations::FileData}, this mechanism does not use any external resources. It
16
+ # provides only the data that the application has put into it using the {#update} method.
17
+ #
18
+ # @example
19
+ # require 'ldclient-rb/integrations/test_data_v2'
20
+ #
21
+ # td = LaunchDarkly::Integrations::TestDataV2.data_source
22
+ # td.update(td.flag('flag-key-1').variation_for_all(true))
23
+ #
24
+ # # Configure the data system with TestDataV2 as both initializer and synchronizer
25
+ # # Note: This example assumes FDv2 data system configuration is available
26
+ # # data_config = LaunchDarkly::Impl::DataSystem::Config.custom
27
+ # # data_config.initializers([td.method(:build_initializer)])
28
+ # # data_config.synchronizers(td.method(:build_synchronizer))
29
+ #
30
+ # # config = LaunchDarkly::Config.new(
31
+ # # sdk_key,
32
+ # # data_system_config: data_config.build
33
+ # # )
34
+ #
35
+ # # flags can be updated at any time:
36
+ # td.update(td.flag('flag-key-1')
37
+ # .variation_for_user('some-user-key', true)
38
+ # .fallthrough_variation(false))
39
+ #
40
+ # The above example uses a simple boolean flag, but more complex configurations are possible using
41
+ # the methods of the {FlagBuilderV2} that is returned by {#flag}. {FlagBuilderV2}
42
+ # supports many of the ways a flag can be configured on the LaunchDarkly dashboard, but does not
43
+ # currently support 1. rule operators other than "in" and "not in", or 2. percentage rollouts.
44
+ #
45
+ # If the same `TestDataV2` instance is used to configure multiple `LDClient` instances,
46
+ # any changes made to the data will propagate to all of the `LDClient` instances.
47
+ #
48
+ class TestDataV2
49
+ # Creates a new instance of the test data source.
50
+ #
51
+ # @return [TestDataV2] a new configurable test data source
52
+ def self.data_source
53
+ self.new
54
+ end
55
+
56
+ # @api private
57
+ def initialize
58
+ @flag_builders = Hash.new
59
+ @current_flags = Hash.new
60
+ @current_segments = Hash.new
61
+ @lock = Concurrent::ReadWriteLock.new
62
+ @instances = Array.new
63
+ @version = 0
64
+ end
65
+
66
+ #
67
+ # Creates or copies a {FlagBuilderV2} for building a test flag configuration.
68
+ #
69
+ # If this flag key has already been defined in this `TestDataV2` instance, then the builder
70
+ # starts with the same configuration that was last provided for this flag.
71
+ #
72
+ # Otherwise, it starts with a new default configuration in which the flag has `true` and
73
+ # `false` variations, is `true` for all contexts when targeting is turned on and
74
+ # `false` otherwise, and currently has targeting turned on. You can change any of those
75
+ # properties, and provide more complex behavior, using the {FlagBuilderV2} methods.
76
+ #
77
+ # Once you have set the desired configuration, pass the builder to {#update}.
78
+ #
79
+ # @param key [String] the flag key
80
+ # @return [FlagBuilderV2] a flag configuration builder
81
+ #
82
+ def flag(key)
83
+ existing_builder = @lock.with_read_lock do
84
+ if @flag_builders.key?(key) && !@flag_builders[key].nil?
85
+ @flag_builders[key]
86
+ else
87
+ nil
88
+ end
89
+ end
90
+
91
+ if existing_builder.nil?
92
+ LaunchDarkly::Integrations::TestDataV2::FlagBuilderV2.new(key).boolean_flag
93
+ else
94
+ existing_builder.clone
95
+ end
96
+ end
97
+
98
+ #
99
+ # Updates the test data with the specified flag configuration.
100
+ #
101
+ # This has the same effect as if a flag were added or modified on the LaunchDarkly dashboard.
102
+ # It immediately propagates the flag change to any `LDClient` instance(s) that you have
103
+ # already configured to use this `TestDataV2`. If no `LDClient` has been started yet,
104
+ # it simply adds this flag to the test data which will be provided to any `LDClient` that
105
+ # you subsequently configure.
106
+ #
107
+ # Any subsequent changes to this {FlagBuilderV2} instance do not affect the test data,
108
+ # unless you call {#update} again.
109
+ #
110
+ # @param flag_builder [FlagBuilderV2] a flag configuration builder
111
+ # @return [TestDataV2] the TestDataV2 instance
112
+ #
113
+ def update(flag_builder)
114
+ instances_copy = []
115
+ new_flag = nil
116
+ @lock.with_write_lock do
117
+ flag_key = flag_builder._key.to_sym
118
+ old_flag = @current_flags[flag_key]
119
+ old_version = old_flag ? old_flag[:version] : 0
120
+
121
+ new_flag = flag_builder.build(old_version + 1)
122
+
123
+ @current_flags[flag_key] = new_flag
124
+ @flag_builders[flag_builder._key] = flag_builder.clone
125
+
126
+ # Create a copy of instances while holding the lock to avoid race conditions
127
+ instances_copy = @instances.dup
128
+ end
129
+
130
+ instances_copy.each do |instance|
131
+ instance.upsert_flag(new_flag)
132
+ end
133
+
134
+ self
135
+ end
136
+
137
+ # @api private
138
+ def make_init_data
139
+ @lock.with_read_lock do
140
+ {
141
+ flags: @current_flags.dup,
142
+ segments: @current_segments.dup,
143
+ }
144
+ end
145
+ end
146
+
147
+ # @api private
148
+ def get_version
149
+ @lock.with_write_lock do
150
+ version = @version
151
+ @version += 1
152
+ version
153
+ end
154
+ end
155
+
156
+ # @api private
157
+ # @param instance [LaunchDarkly::Impl::Integrations::TestData::TestDataSourceV2] the TestDataSourceV2 instance to remove
158
+ def closed_instance(instance)
159
+ @lock.with_write_lock do
160
+ @instances.delete(instance) if @instances.include?(instance)
161
+ end
162
+ end
163
+
164
+ # @api private
165
+ # @param instance [LaunchDarkly::Impl::Integrations::TestData::TestDataSourceV2] the TestDataSourceV2 instance to add
166
+ def add_instance(instance)
167
+ @lock.with_write_lock do
168
+ @instances.push(instance)
169
+ end
170
+ end
171
+
172
+ #
173
+ # Copies a full segment data model object into the test data.
174
+ #
175
+ # It immediately propagates the change to any `LDClient` instance(s) that you have already
176
+ # configured to use this `TestDataV2`. If no `LDClient` has been started yet, it simply adds
177
+ # this segment to the test data which will be provided to any LDClient that you subsequently
178
+ # configure.
179
+ #
180
+ # This method is currently the only way to inject segment data, since there is no builder
181
+ # API for segments. It is mainly intended for the SDK's own tests of segment functionality,
182
+ # since application tests that need to produce a desired evaluation state could do so more easily
183
+ # by just setting flag values.
184
+ #
185
+ # @param segment [Hash, LaunchDarkly::Impl::Model::Segment] the segment configuration as a hash or
186
+ # a Segment model object.
187
+ # @return [TestDataV2] the TestDataV2 instance
188
+ #
189
+ def use_preconfigured_segment(segment)
190
+ instances_copy = []
191
+ segment_key = nil
192
+ updated_segment = nil
193
+
194
+ @lock.with_write_lock do
195
+ # Convert to hash and normalize keys to symbols
196
+ segment_hash = if segment.is_a?(Hash)
197
+ segment.transform_keys(&:to_sym)
198
+ else
199
+ segment.as_json
200
+ end
201
+ segment_key = segment_hash[:key].to_sym
202
+
203
+ old_segment = @current_segments[segment_key]
204
+ old_version = old_segment ? old_segment[:version] : 0
205
+
206
+ updated_segment = segment_hash.dup
207
+ updated_segment[:version] = old_version + 1
208
+
209
+ @current_segments[segment_key] = updated_segment
210
+
211
+ # Create a copy of instances while holding the lock to avoid race conditions
212
+ instances_copy = @instances.dup
213
+ end
214
+
215
+ instances_copy.each do |instance|
216
+ instance.upsert_segment(updated_segment)
217
+ end
218
+
219
+ self
220
+ end
221
+
222
+ #
223
+ # Returns a builder for creating a data source that can be used with the FDv2 data system
224
+ # as either an initializer or synchronizer.
225
+ #
226
+ # @return [TestDataV2Builder] a builder for a test data source
227
+ #
228
+ def test_data_ds_builder
229
+ TestDataV2Builder.new(self)
230
+ end
231
+ end
232
+
233
+ #
234
+ # Builder for TestDataV2 data sources (works for both initializers and synchronizers).
235
+ #
236
+ class TestDataV2Builder
237
+ def initialize(test_data)
238
+ @test_data = test_data
239
+ end
240
+
241
+ #
242
+ # Builds the data source.
243
+ #
244
+ # @param sdk_key [String] the SDK key (unused, for interface compatibility)
245
+ # @param config [LaunchDarkly::Config] the SDK configuration (unused, for interface compatibility)
246
+ # @return [LaunchDarkly::Impl::Integrations::TestData::TestDataSourceV2] a test data source
247
+ #
248
+ def build(sdk_key, config)
249
+ LaunchDarkly::Impl::Integrations::TestData::TestDataSourceV2.new(@test_data)
250
+ end
251
+ end
252
+ end
253
+ end
254
+
@@ -1,6 +1,7 @@
1
1
  require "concurrent/atomics"
2
2
 
3
- require "ldclient-rb/expiring_cache"
3
+ require "ldclient-rb/impl/expiring_cache"
4
+ require "ldclient-rb/interfaces"
4
5
 
5
6
  module LaunchDarkly
6
7
  module Integrations
@@ -37,7 +38,7 @@ module LaunchDarkly
37
38
  expiration_seconds = opts[:expiration] || 15
38
39
  if expiration_seconds > 0
39
40
  capacity = opts[:capacity] || 1000
40
- @cache = ExpiringCache.new(capacity, expiration_seconds)
41
+ @cache = Impl::ExpiringCache.new(capacity, expiration_seconds)
41
42
  else
42
43
  @cache = nil
43
44
  end