launchdarkly-server-sdk 8.4.2 → 8.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c04f38ba70b95c1446fc0aaffd2f3020e208501e9ede2313b559848792c1bf81
4
- data.tar.gz: 6dd37514e2588d78c995702af1d66d6f00cdc3762ce4908690a2a4e13ab5c46d
3
+ metadata.gz: 2a732ff00a5e21bfc907dff44adc5bd3f1651bd80c2483273a1a9730f443448f
4
+ data.tar.gz: 1ca26bf847c2387ed585e07b18c6aa42a78496110a7bf9643751a0e2b04d2908
5
5
  SHA512:
6
- metadata.gz: 40f20870dfa777687da11f7611255ee7f15dde800c7ac9860e5a673643c9d7d19841932162fcabef1d57e6a9ab7710fb3d06ea72567336a5c26ecb0cdd220f74
7
- data.tar.gz: bbf303fc081003f0a5e71f2fd38dc48f707958c5c33f6f450982db6c3fecb7076f287460d597414b52c8e4181d95c55babd599fca7877bebe4ff9d82d38e6dd5
6
+ metadata.gz: 901e6f7562f0861916bf06fd07088561713579633cc5e052ae5cf86c6acdbdd0497b55e273c2f92244a5653d0fa806c9776f92a853bad22232634489c630f85d
7
+ data.tar.gz: a325418b21c256a8b2d846d187e8bc91ba1e323955f460a418732b77f30354b64fc2dbfdadd04c4be4a41217779a4f217d522799ace4ce46a3ebc5019baf060c
@@ -43,6 +43,7 @@ module LaunchDarkly
43
43
  # @option opts [BigSegmentsConfig] :big_segments See {#big_segments}.
44
44
  # @option opts [Hash] :application See {#application}
45
45
  # @option opts [String] :payload_filter_key See {#payload_filter_key}
46
+ # @option opts [Boolean] :omit_anonymous_contexts See {#omit_anonymous_contexts}
46
47
  # @option hooks [Array<Interfaces::Hooks::Hook]
47
48
  #
48
49
  def initialize(opts = {})
@@ -77,6 +78,7 @@ module LaunchDarkly
77
78
  @application = LaunchDarkly::Impl::Util.validate_application_info(opts[:application] || {}, @logger)
78
79
  @payload_filter_key = opts[:payload_filter_key]
79
80
  @hooks = (opts[:hooks] || []).keep_if { |hook| hook.is_a? Interfaces::Hooks::Hook }
81
+ @omit_anonymous_contexts = opts.has_key?(:omit_anonymous_contexts) && opts[:omit_anonymous_contexts]
80
82
  @data_source_update_sink = nil
81
83
  end
82
84
 
@@ -385,6 +387,15 @@ module LaunchDarkly
385
387
  #
386
388
  attr_reader :hooks
387
389
 
390
+ #
391
+ # Sets whether anonymous contexts should be omitted from index and identify events.
392
+ #
393
+ # The default value is false. Anonymous contexts will be included in index and identify events.
394
+ # @return [Boolean]
395
+ #
396
+ attr_reader :omit_anonymous_contexts
397
+
398
+
388
399
  #
389
400
  # The default LaunchDarkly client configuration. This configuration sets
390
401
  # reasonable defaults for most users.
@@ -101,6 +101,26 @@ module LaunchDarkly
101
101
  @error.nil?
102
102
  end
103
103
 
104
+ #
105
+ # For a multi-kind context:
106
+ #
107
+ # A multi-kind context is made up of two or more single-kind contexts. This method will first discard any
108
+ # single-kind contexts which are anonymous. It will then create a new multi-kind context from the remaining
109
+ # single-kind contexts. This may result in an invalid context (e.g. all single-kind contexts are anonymous).
110
+ #
111
+ # For a single-kind context:
112
+ #
113
+ # If the context is not anonymous, this method will return the current context as is and unmodified.
114
+ #
115
+ # If the context is anonymous, this method will return an invalid context.
116
+ #
117
+ def without_anonymous_contexts
118
+ contexts = multi_kind? ? @contexts : [self]
119
+ contexts = contexts.reject { |c| c.anonymous }
120
+
121
+ LDContext.create_multi(contexts)
122
+ end
123
+
104
124
  #
105
125
  # Returns a hash mapping each context's kind to its key.
106
126
  #
@@ -334,6 +354,49 @@ module LaunchDarkly
334
354
  multi_kind? ? individual_context(key.to_s) : get_value(key)
335
355
  end
336
356
 
357
+ #
358
+ # Convert the LDContext to a JSON string.
359
+ #
360
+ # @param args [Array]
361
+ # @return [String]
362
+ #
363
+ def to_json(*args)
364
+ JSON.generate(to_h, *args)
365
+ end
366
+
367
+ #
368
+ # Convert the LDContext to a hash. If the LDContext is invalid, the hash will contain an error key with the error
369
+ # message.
370
+ #
371
+ # @return [Hash]
372
+ #
373
+ def to_h
374
+ return {error: error} unless valid?
375
+ return hash_single_kind unless multi_kind?
376
+
377
+ hash = {kind: 'multi'}
378
+ @contexts.each do |context|
379
+ single_kind_hash = context.to_h
380
+ kind = single_kind_hash.delete(:kind)
381
+ hash[kind] = single_kind_hash
382
+ end
383
+
384
+ hash
385
+ end
386
+
387
+ protected def hash_single_kind
388
+ hash = attributes.nil? ? {} : attributes.clone
389
+
390
+ hash[:kind] = kind
391
+ hash[:key] = key
392
+
393
+ hash[:name] = name unless name.nil?
394
+ hash[:anonymous] = anonymous if anonymous
395
+ hash[:_meta] = {privateAttributes: private_attributes} unless private_attributes.empty?
396
+
397
+ hash
398
+ end
399
+
337
400
  #
338
401
  # Retrieve the value of any top level, addressable attribute.
339
402
  #
@@ -144,6 +144,7 @@ module LaunchDarkly
144
144
  Impl::EventSender.new(sdk_key, config, client || Util.new_http_client(config.events_uri, config))
145
145
 
146
146
  @timestamp_fn = (test_properties || {})[:timestamp_fn] || proc { Impl::Util.current_time_millis }
147
+ @omit_anonymous_contexts = config.omit_anonymous_contexts
147
148
 
148
149
  EventDispatcher.new(@inbox, sdk_key, config, diagnostic_accumulator, event_sender)
149
150
  end
@@ -167,7 +168,8 @@ module LaunchDarkly
167
168
  end
168
169
 
169
170
  def record_identify_event(context)
170
- post_to_inbox(LaunchDarkly::Impl::IdentifyEvent.new(timestamp, context))
171
+ target_context = !@omit_anonymous_contexts ? context : context.without_anonymous_contexts
172
+ post_to_inbox(LaunchDarkly::Impl::IdentifyEvent.new(timestamp, target_context)) if target_context.valid?
171
173
  end
172
174
 
173
175
  def record_custom_event(context, key, data = nil, metric_value = nil)
@@ -319,16 +321,27 @@ module LaunchDarkly
319
321
  will_add_full_event = true
320
322
  end
321
323
 
322
- # For each context we haven't seen before, we add an index event - unless this is already
323
- # an identify event for that context.
324
- if !event.context.nil? && !notice_context(event.context) && !event.is_a?(LaunchDarkly::Impl::IdentifyEvent) && !event.is_a?(LaunchDarkly::Impl::MigrationOpEvent)
325
- outbox.add_event(LaunchDarkly::Impl::IndexEvent.new(event.timestamp, event.context))
324
+ get_indexable_context(event) do |ctx|
325
+ outbox.add_event(LaunchDarkly::Impl::IndexEvent.new(event.timestamp, ctx))
326
326
  end
327
327
 
328
328
  outbox.add_event(event) if will_add_full_event && @sampler.sample(event.sampling_ratio.nil? ? 1 : event.sampling_ratio)
329
329
  outbox.add_event(debug_event) if !debug_event.nil? && @sampler.sample(event.sampling_ratio.nil? ? 1 : event.sampling_ratio)
330
330
  end
331
331
 
332
+ private def get_indexable_context(event, &block)
333
+ return if event.context.nil?
334
+
335
+ context = !@config.omit_anonymous_contexts ? event.context : event.context.without_anonymous_contexts
336
+ return unless context.valid?
337
+
338
+ return if notice_context(context)
339
+ return if event.is_a?(LaunchDarkly::Impl::IdentifyEvent)
340
+ return if event.is_a?(LaunchDarkly::Impl::MigrationOpEvent)
341
+
342
+ yield context unless block.nil?
343
+ end
344
+
332
345
  #
333
346
  # Add to the set of contexts we've noticed, and return true if the context
334
347
  # was already known to us.
@@ -36,18 +36,13 @@ module LaunchDarkly
36
36
  @paths = [ @paths ]
37
37
  end
38
38
  @auto_update = options[:auto_update]
39
- if @auto_update && @@have_listen && !options[:force_polling] # force_polling is used only for tests
40
- # We have seen unreliable behavior in the 'listen' gem in JRuby 9.1 (https://github.com/guard/listen/issues/449).
41
- # Therefore, on that platform we'll fall back to file polling instead.
42
- if defined?(JRUBY_VERSION) && JRUBY_VERSION.start_with?("9.1.")
43
- @use_listen = false
44
- else
45
- @use_listen = true
46
- end
47
- end
39
+ @use_listen = @auto_update && @@have_listen && !options[:force_polling]
48
40
  @poll_interval = options[:poll_interval] || 1
49
41
  @initialized = Concurrent::AtomicBoolean.new(false)
50
42
  @ready = Concurrent::Event.new
43
+
44
+ @version_lock = Mutex.new
45
+ @last_version = 1
51
46
  end
52
47
 
53
48
  def initialized?
@@ -101,14 +96,22 @@ module LaunchDarkly
101
96
  end
102
97
 
103
98
  def load_file(path, all_data)
99
+ version = 1
100
+ @version_lock.synchronize {
101
+ version = @last_version
102
+ @last_version += 1
103
+ }
104
+
104
105
  parsed = parse_content(IO.read(path))
105
106
  (parsed[:flags] || {}).each do |key, flag|
107
+ flag[:version] = version
106
108
  add_item(all_data, FEATURES, flag)
107
109
  end
108
110
  (parsed[:flagValues] || {}).each do |key, value|
109
- add_item(all_data, FEATURES, make_flag_with_value(key.to_s, value))
111
+ add_item(all_data, FEATURES, make_flag_with_value(key.to_s, value, version))
110
112
  end
111
113
  (parsed[:segments] || {}).each do |key, segment|
114
+ segment[:version] = version
112
115
  add_item(all_data, SEGMENTS, segment)
113
116
  end
114
117
  end
@@ -142,10 +145,11 @@ module LaunchDarkly
142
145
  items[key] = Model.deserialize(kind, item)
143
146
  end
144
147
 
145
- def make_flag_with_value(key, value)
148
+ def make_flag_with_value(key, value, version)
146
149
  {
147
150
  key: key,
148
151
  on: true,
152
+ version: version,
149
153
  fallthrough: { variation: 0 },
150
154
  variations: [ value ],
151
155
  }
@@ -93,7 +93,7 @@ module LaunchDarkly
93
93
  # Note that the default implementation of this feature is based on polling the filesystem,
94
94
  # which may not perform well. If you install the 'listen' gem (not included by default, to
95
95
  # avoid adding unwanted dependencies to the SDK), its native file watching mechanism will be
96
- # used instead. However, 'listen' will not be used in JRuby 9.1 due to a known instability.
96
+ # used instead.
97
97
  # @option options [Float] :poll_interval The minimum interval, in seconds, between checks for
98
98
  # file modifications - used only if auto_update is true, and if the native file-watching
99
99
  # mechanism from 'listen' is not being used. The default value is 1 second.
@@ -124,13 +124,18 @@ module LaunchDarkly
124
124
  end
125
125
 
126
126
  ready = @data_source.start
127
- if wait_for_sec > 0
128
- ok = ready.wait(wait_for_sec)
129
- if !ok
130
- @config.logger.error { "[LDClient] Timeout encountered waiting for LaunchDarkly client initialization" }
131
- elsif !@data_source.initialized?
132
- @config.logger.error { "[LDClient] LaunchDarkly client initialization failed" }
133
- end
127
+
128
+ return unless wait_for_sec > 0
129
+
130
+ if wait_for_sec > 60
131
+ @config.logger.warn { "[LDClient] Client was configured to block for up to #{wait_for_sec} seconds when initializing. We recommend blocking no longer than 60." }
132
+ end
133
+
134
+ ok = ready.wait(wait_for_sec)
135
+ if !ok
136
+ @config.logger.error { "[LDClient] Timeout encountered waiting for LaunchDarkly client initialization" }
137
+ elsif !@data_source.initialized?
138
+ @config.logger.error { "[LDClient] LaunchDarkly client initialization failed" }
134
139
  end
135
140
  end
136
141
 
@@ -238,6 +238,16 @@ module LaunchDarkly
238
238
  ([error] + components).hash
239
239
  end
240
240
 
241
+ #
242
+ # Convert the Reference to a JSON string.
243
+ #
244
+ # @param args [Array]
245
+ # @return [String]
246
+ #
247
+ def to_json(*args)
248
+ JSON.generate(@raw_path, *args)
249
+ end
250
+
241
251
  #
242
252
  # Performs unescaping of attribute reference path components:
243
253
  #
@@ -1,3 +1,3 @@
1
1
  module LaunchDarkly
2
- VERSION = "8.4.2" # x-release-please-version
2
+ VERSION = "8.6.0" # x-release-please-version
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: launchdarkly-server-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.4.2
4
+ version: 8.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - LaunchDarkly
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-05-03 00:00:00.000000000 Z
11
+ date: 2024-06-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-dynamodb
@@ -376,7 +376,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
376
376
  - !ruby/object:Gem::Version
377
377
  version: '0'
378
378
  requirements: []
379
- rubygems_version: 3.5.9
379
+ rubygems_version: 3.5.11
380
380
  signing_key:
381
381
  specification_version: 4
382
382
  summary: LaunchDarkly SDK for Ruby