launchdarkly-server-sdk 8.4.2 → 8.6.0

Sign up to get free protection for your applications and to get access to all the features.
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