launchdarkly-server-sdk 6.4.0 → 7.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/lib/ldclient-rb/config.rb +102 -56
  3. data/lib/ldclient-rb/context.rb +487 -0
  4. data/lib/ldclient-rb/evaluation_detail.rb +20 -20
  5. data/lib/ldclient-rb/events.rb +77 -132
  6. data/lib/ldclient-rb/flags_state.rb +4 -4
  7. data/lib/ldclient-rb/impl/big_segments.rb +17 -17
  8. data/lib/ldclient-rb/impl/context.rb +96 -0
  9. data/lib/ldclient-rb/impl/context_filter.rb +145 -0
  10. data/lib/ldclient-rb/impl/diagnostic_events.rb +9 -10
  11. data/lib/ldclient-rb/impl/evaluator.rb +379 -131
  12. data/lib/ldclient-rb/impl/evaluator_bucketing.rb +40 -41
  13. data/lib/ldclient-rb/impl/evaluator_helpers.rb +31 -34
  14. data/lib/ldclient-rb/impl/evaluator_operators.rb +26 -55
  15. data/lib/ldclient-rb/impl/event_sender.rb +6 -6
  16. data/lib/ldclient-rb/impl/event_summarizer.rb +12 -7
  17. data/lib/ldclient-rb/impl/event_types.rb +18 -30
  18. data/lib/ldclient-rb/impl/integrations/consul_impl.rb +7 -7
  19. data/lib/ldclient-rb/impl/integrations/dynamodb_impl.rb +29 -29
  20. data/lib/ldclient-rb/impl/integrations/file_data_source.rb +8 -8
  21. data/lib/ldclient-rb/impl/integrations/redis_impl.rb +92 -12
  22. data/lib/ldclient-rb/impl/model/clause.rb +45 -0
  23. data/lib/ldclient-rb/impl/model/feature_flag.rb +232 -0
  24. data/lib/ldclient-rb/impl/model/preprocessed_data.rb +8 -121
  25. data/lib/ldclient-rb/impl/model/segment.rb +132 -0
  26. data/lib/ldclient-rb/impl/model/serialization.rb +52 -12
  27. data/lib/ldclient-rb/impl/repeating_task.rb +1 -1
  28. data/lib/ldclient-rb/impl/store_data_set_sorter.rb +2 -2
  29. data/lib/ldclient-rb/impl/unbounded_pool.rb +1 -1
  30. data/lib/ldclient-rb/impl/util.rb +2 -2
  31. data/lib/ldclient-rb/in_memory_store.rb +2 -2
  32. data/lib/ldclient-rb/integrations/consul.rb +1 -1
  33. data/lib/ldclient-rb/integrations/dynamodb.rb +1 -1
  34. data/lib/ldclient-rb/integrations/file_data.rb +3 -3
  35. data/lib/ldclient-rb/integrations/redis.rb +4 -4
  36. data/lib/ldclient-rb/integrations/test_data/flag_builder.rb +218 -62
  37. data/lib/ldclient-rb/integrations/test_data.rb +16 -12
  38. data/lib/ldclient-rb/integrations/util/store_wrapper.rb +9 -9
  39. data/lib/ldclient-rb/interfaces.rb +14 -14
  40. data/lib/ldclient-rb/ldclient.rb +94 -144
  41. data/lib/ldclient-rb/memoized_value.rb +1 -1
  42. data/lib/ldclient-rb/non_blocking_thread_pool.rb +1 -1
  43. data/lib/ldclient-rb/polling.rb +2 -2
  44. data/lib/ldclient-rb/reference.rb +274 -0
  45. data/lib/ldclient-rb/requestor.rb +5 -5
  46. data/lib/ldclient-rb/stream.rb +7 -8
  47. data/lib/ldclient-rb/util.rb +4 -19
  48. data/lib/ldclient-rb/version.rb +1 -1
  49. data/lib/ldclient-rb.rb +2 -3
  50. metadata +34 -17
  51. data/lib/ldclient-rb/file_data_source.rb +0 -23
  52. data/lib/ldclient-rb/newrelic.rb +0 -17
  53. data/lib/ldclient-rb/redis_store.rb +0 -88
  54. data/lib/ldclient-rb/user_filter.rb +0 -52
@@ -0,0 +1,145 @@
1
+ module LaunchDarkly
2
+ module Impl
3
+ class ContextFilter
4
+ #
5
+ # @param all_attributes_private [Boolean]
6
+ # @param private_attributes [Array<String>]
7
+ #
8
+ def initialize(all_attributes_private, private_attributes)
9
+ @all_attributes_private = all_attributes_private
10
+
11
+ @private_attributes = []
12
+ private_attributes.each do |attribute|
13
+ reference = LaunchDarkly::Reference.create(attribute)
14
+ @private_attributes << reference if reference.error.nil?
15
+ end
16
+ end
17
+
18
+ #
19
+ # Return a hash representation of the provided context with attribute
20
+ # redaction applied.
21
+ #
22
+ # @param context [LaunchDarkly::LDContext]
23
+ # @return [Hash]
24
+ #
25
+ def filter(context)
26
+ return filter_single_context(context, true) unless context.multi_kind?
27
+
28
+ filtered = {kind: 'multi'}
29
+ (0...context.individual_context_count).each do |i|
30
+ c = context.individual_context(i)
31
+ next if c.nil?
32
+
33
+ filtered[c.kind] = filter_single_context(c, false)
34
+ end
35
+
36
+ filtered
37
+ end
38
+
39
+ #
40
+ # Apply redaction rules for a single context.
41
+ #
42
+ # @param context [LaunchDarkly::LDContext]
43
+ # @param include_kind [Boolean]
44
+ # @return [Hash]
45
+ #
46
+ private def filter_single_context(context, include_kind)
47
+ filtered = {key: context.key}
48
+
49
+ filtered[:kind] = context.kind if include_kind
50
+ filtered[:anonymous] = true if context.get_value(:anonymous)
51
+
52
+ redacted = []
53
+ private_attributes = @private_attributes.concat(context.private_attributes)
54
+
55
+ name = context.get_value(:name)
56
+ if !name.nil? && !check_whole_attribute_private(:name, private_attributes, redacted)
57
+ filtered[:name] = name
58
+ end
59
+
60
+ context.get_custom_attribute_names.each do |attribute|
61
+ unless check_whole_attribute_private(attribute, private_attributes, redacted)
62
+ value = context.get_value(attribute)
63
+ filtered[attribute] = redact_json_value(nil, attribute, value, private_attributes, redacted)
64
+ end
65
+ end
66
+
67
+ filtered[:_meta] = {redactedAttributes: redacted} unless redacted.empty?
68
+
69
+ filtered
70
+ end
71
+
72
+ #
73
+ # Check if an entire attribute should be redacted.
74
+ #
75
+ # @param attribute [Symbol]
76
+ # @param private_attributes [Array<Reference>]
77
+ # @param redacted [Array<Symbol>]
78
+ # @return [Boolean]
79
+ #
80
+ private def check_whole_attribute_private(attribute, private_attributes, redacted)
81
+ if @all_attributes_private
82
+ redacted << attribute
83
+ return true
84
+ end
85
+
86
+ private_attributes.each do |private_attribute|
87
+ if private_attribute.component(0) == attribute && private_attribute.depth == 1
88
+ redacted << attribute
89
+ return true
90
+ end
91
+ end
92
+
93
+ false
94
+ end
95
+
96
+ #
97
+ # Apply redaction rules to the provided value.
98
+ #
99
+ # @param parent_path [Array<String>, nil]
100
+ # @param name [String]
101
+ # @param value [any]
102
+ # @param private_attributes [Array<Reference>]
103
+ # @param redacted [Array<Symbol>]
104
+ # @return [any]
105
+ #
106
+ private def redact_json_value(parent_path, name, value, private_attributes, redacted)
107
+ return value unless value.is_a?(Hash)
108
+
109
+ ret = {}
110
+ current_path = parent_path.clone || []
111
+ current_path << name
112
+
113
+ value.each do |k, v|
114
+ was_redacted = false
115
+ private_attributes.each do |private_attribute|
116
+ next unless private_attribute.depth == (current_path.count + 1)
117
+
118
+ component = private_attribute.component(current_path.count)
119
+ next unless component == k
120
+
121
+ match = true
122
+ (0...current_path.count).each do |i|
123
+ unless private_attribute.component(i) == current_path[i]
124
+ match = false
125
+ break
126
+ end
127
+ end
128
+
129
+ if match
130
+ redacted << private_attribute.raw_path.to_sym
131
+ was_redacted = true
132
+ break
133
+ end
134
+ end
135
+
136
+ unless was_redacted
137
+ ret[k] = redact_json_value(current_path, k, v, private_attributes, redacted)
138
+ end
139
+ end
140
+
141
+ ret
142
+ end
143
+ end
144
+ end
145
+ end
@@ -9,7 +9,7 @@ module LaunchDarkly
9
9
  def self.create_diagnostic_id(sdk_key)
10
10
  {
11
11
  diagnosticId: SecureRandom.uuid,
12
- sdkKeySuffix: sdk_key[-6..-1] || sdk_key
12
+ sdkKeySuffix: sdk_key[-6..-1] || sdk_key,
13
13
  }
14
14
  end
15
15
 
@@ -25,16 +25,16 @@ module LaunchDarkly
25
25
  end
26
26
 
27
27
  def create_init_event(config)
28
- return {
28
+ {
29
29
  kind: 'diagnostic-init',
30
30
  creationDate: Util.current_time_millis,
31
31
  id: @id,
32
32
  configuration: DiagnosticAccumulator.make_config_data(config),
33
33
  sdk: DiagnosticAccumulator.make_sdk_data(config),
34
- platform: DiagnosticAccumulator.make_platform_data
34
+ platform: DiagnosticAccumulator.make_platform_data,
35
35
  }
36
36
  end
37
-
37
+
38
38
  def record_stream_init(timestamp, failed, duration_millis)
39
39
  @lock.synchronize do
40
40
  @stream_inits.push({ timestamp: timestamp, failed: failed, durationMillis: duration_millis })
@@ -57,7 +57,7 @@ module LaunchDarkly
57
57
  droppedEvents: dropped_events,
58
58
  deduplicatedUsers: deduplicated_users,
59
59
  eventsInLastBatch: events_in_last_batch,
60
- streamInits: previous_stream_inits
60
+ streamInits: previous_stream_inits,
61
61
  }
62
62
  @data_since_date = current_time
63
63
  event
@@ -73,12 +73,11 @@ module LaunchDarkly
73
73
  diagnosticRecordingIntervalMillis: self.seconds_to_millis(config.diagnostic_recording_interval),
74
74
  eventsCapacity: config.capacity,
75
75
  eventsFlushIntervalMillis: self.seconds_to_millis(config.flush_interval),
76
- inlineUsersInEvents: config.inline_users_in_events,
77
76
  pollingIntervalMillis: self.seconds_to_millis(config.poll_interval),
78
77
  socketTimeoutMillis: self.seconds_to_millis(config.read_timeout),
79
78
  streamingDisabled: !config.stream?,
80
- userKeysCapacity: config.user_keys_capacity,
81
- userKeysFlushIntervalMillis: self.seconds_to_millis(config.user_keys_flush_interval),
79
+ userKeysCapacity: config.context_keys_capacity,
80
+ userKeysFlushIntervalMillis: self.seconds_to_millis(config.context_keys_flush_interval),
82
81
  usingProxy: ENV.has_key?('http_proxy') || ENV.has_key?('https_proxy') || ENV.has_key?('HTTP_PROXY') || ENV.has_key?('HTTPS_PROXY'),
83
82
  usingRelayDaemon: config.use_ldd?,
84
83
  }
@@ -88,7 +87,7 @@ module LaunchDarkly
88
87
  def self.make_sdk_data(config)
89
88
  ret = {
90
89
  name: 'ruby-server-sdk',
91
- version: LaunchDarkly::VERSION
90
+ version: LaunchDarkly::VERSION,
92
91
  }
93
92
  if config.wrapper_name
94
93
  ret[:wrapperName] = config.wrapper_name
@@ -105,7 +104,7 @@ module LaunchDarkly
105
104
  osName: self.normalize_os_name(conf['host_os']),
106
105
  osVersion: 'unknown', # there seems to be no portable way to detect this in Ruby
107
106
  rubyVersion: conf['ruby_version'],
108
- rubyImplementation: Object.constants.include?(:RUBY_ENGINE) ? RUBY_ENGINE : 'unknown'
107
+ rubyImplementation: Object.constants.include?(:RUBY_ENGINE) ? RUBY_ENGINE : 'unknown',
109
108
  }
110
109
  end
111
110