launchdarkly-server-sdk 6.3.0 → 8.0.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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -4
  3. data/lib/ldclient-rb/config.rb +112 -62
  4. data/lib/ldclient-rb/context.rb +444 -0
  5. data/lib/ldclient-rb/evaluation_detail.rb +26 -22
  6. data/lib/ldclient-rb/events.rb +256 -146
  7. data/lib/ldclient-rb/flags_state.rb +26 -15
  8. data/lib/ldclient-rb/impl/big_segments.rb +18 -18
  9. data/lib/ldclient-rb/impl/broadcaster.rb +78 -0
  10. data/lib/ldclient-rb/impl/context.rb +96 -0
  11. data/lib/ldclient-rb/impl/context_filter.rb +145 -0
  12. data/lib/ldclient-rb/impl/data_source.rb +188 -0
  13. data/lib/ldclient-rb/impl/data_store.rb +59 -0
  14. data/lib/ldclient-rb/impl/dependency_tracker.rb +102 -0
  15. data/lib/ldclient-rb/impl/diagnostic_events.rb +9 -10
  16. data/lib/ldclient-rb/impl/evaluator.rb +386 -142
  17. data/lib/ldclient-rb/impl/evaluator_bucketing.rb +40 -41
  18. data/lib/ldclient-rb/impl/evaluator_helpers.rb +50 -0
  19. data/lib/ldclient-rb/impl/evaluator_operators.rb +26 -55
  20. data/lib/ldclient-rb/impl/event_sender.rb +7 -6
  21. data/lib/ldclient-rb/impl/event_summarizer.rb +68 -0
  22. data/lib/ldclient-rb/impl/event_types.rb +136 -0
  23. data/lib/ldclient-rb/impl/flag_tracker.rb +58 -0
  24. data/lib/ldclient-rb/impl/integrations/consul_impl.rb +19 -7
  25. data/lib/ldclient-rb/impl/integrations/dynamodb_impl.rb +38 -30
  26. data/lib/ldclient-rb/impl/integrations/file_data_source.rb +24 -11
  27. data/lib/ldclient-rb/impl/integrations/redis_impl.rb +109 -12
  28. data/lib/ldclient-rb/impl/migrations/migrator.rb +287 -0
  29. data/lib/ldclient-rb/impl/migrations/tracker.rb +136 -0
  30. data/lib/ldclient-rb/impl/model/clause.rb +45 -0
  31. data/lib/ldclient-rb/impl/model/feature_flag.rb +255 -0
  32. data/lib/ldclient-rb/impl/model/preprocessed_data.rb +64 -0
  33. data/lib/ldclient-rb/impl/model/segment.rb +132 -0
  34. data/lib/ldclient-rb/impl/model/serialization.rb +54 -44
  35. data/lib/ldclient-rb/impl/repeating_task.rb +3 -4
  36. data/lib/ldclient-rb/impl/sampler.rb +25 -0
  37. data/lib/ldclient-rb/impl/store_client_wrapper.rb +102 -8
  38. data/lib/ldclient-rb/impl/store_data_set_sorter.rb +2 -2
  39. data/lib/ldclient-rb/impl/unbounded_pool.rb +1 -1
  40. data/lib/ldclient-rb/impl/util.rb +59 -1
  41. data/lib/ldclient-rb/in_memory_store.rb +9 -2
  42. data/lib/ldclient-rb/integrations/consul.rb +2 -2
  43. data/lib/ldclient-rb/integrations/dynamodb.rb +2 -2
  44. data/lib/ldclient-rb/integrations/file_data.rb +4 -4
  45. data/lib/ldclient-rb/integrations/redis.rb +5 -5
  46. data/lib/ldclient-rb/integrations/test_data/flag_builder.rb +287 -62
  47. data/lib/ldclient-rb/integrations/test_data.rb +18 -14
  48. data/lib/ldclient-rb/integrations/util/store_wrapper.rb +20 -9
  49. data/lib/ldclient-rb/interfaces.rb +600 -14
  50. data/lib/ldclient-rb/ldclient.rb +314 -134
  51. data/lib/ldclient-rb/memoized_value.rb +1 -1
  52. data/lib/ldclient-rb/migrations.rb +230 -0
  53. data/lib/ldclient-rb/non_blocking_thread_pool.rb +1 -1
  54. data/lib/ldclient-rb/polling.rb +52 -6
  55. data/lib/ldclient-rb/reference.rb +274 -0
  56. data/lib/ldclient-rb/requestor.rb +9 -11
  57. data/lib/ldclient-rb/stream.rb +96 -34
  58. data/lib/ldclient-rb/util.rb +97 -14
  59. data/lib/ldclient-rb/version.rb +1 -1
  60. data/lib/ldclient-rb.rb +3 -4
  61. metadata +65 -23
  62. data/lib/ldclient-rb/event_summarizer.rb +0 -55
  63. data/lib/ldclient-rb/file_data_source.rb +0 -23
  64. data/lib/ldclient-rb/impl/event_factory.rb +0 -126
  65. data/lib/ldclient-rb/newrelic.rb +0 -17
  66. data/lib/ldclient-rb/redis_store.rb +0 -88
  67. data/lib/ldclient-rb/user_filter.rb +0 -52
@@ -1,126 +0,0 @@
1
-
2
- module LaunchDarkly
3
- module Impl
4
- # Event constructors are centralized here to avoid mistakes and repetitive logic.
5
- # The LDClient owns two instances of EventFactory: one that always embeds evaluation reasons
6
- # in the events (for when variation_detail is called) and one that doesn't.
7
- #
8
- # Note that these methods do not set the "creationDate" property, because in the Ruby client,
9
- # that is done by EventProcessor.add_event().
10
- class EventFactory
11
- def initialize(with_reasons)
12
- @with_reasons = with_reasons
13
- end
14
-
15
- def new_eval_event(flag, user, detail, default_value, prereq_of_flag = nil)
16
- add_experiment_data = is_experiment(flag, detail.reason)
17
- e = {
18
- kind: 'feature',
19
- key: flag[:key],
20
- user: user,
21
- variation: detail.variation_index,
22
- value: detail.value,
23
- default: default_value,
24
- version: flag[:version]
25
- }
26
- # the following properties are handled separately so we don't waste bandwidth on unused keys
27
- e[:trackEvents] = true if add_experiment_data || flag[:trackEvents]
28
- e[:debugEventsUntilDate] = flag[:debugEventsUntilDate] if flag[:debugEventsUntilDate]
29
- e[:prereqOf] = prereq_of_flag[:key] if !prereq_of_flag.nil?
30
- e[:reason] = detail.reason if add_experiment_data || @with_reasons
31
- e[:contextKind] = context_to_context_kind(user) if !user.nil? && user[:anonymous]
32
- e
33
- end
34
-
35
- def new_default_event(flag, user, default_value, reason)
36
- e = {
37
- kind: 'feature',
38
- key: flag[:key],
39
- user: user,
40
- value: default_value,
41
- default: default_value,
42
- version: flag[:version]
43
- }
44
- e[:trackEvents] = true if flag[:trackEvents]
45
- e[:debugEventsUntilDate] = flag[:debugEventsUntilDate] if flag[:debugEventsUntilDate]
46
- e[:reason] = reason if @with_reasons
47
- e[:contextKind] = context_to_context_kind(user) if !user.nil? && user[:anonymous]
48
- e
49
- end
50
-
51
- def new_unknown_flag_event(key, user, default_value, reason)
52
- e = {
53
- kind: 'feature',
54
- key: key,
55
- user: user,
56
- value: default_value,
57
- default: default_value
58
- }
59
- e[:reason] = reason if @with_reasons
60
- e[:contextKind] = context_to_context_kind(user) if !user.nil? && user[:anonymous]
61
- e
62
- end
63
-
64
- def new_identify_event(user)
65
- {
66
- kind: 'identify',
67
- key: user[:key],
68
- user: user
69
- }
70
- end
71
-
72
- def new_alias_event(current_context, previous_context)
73
- {
74
- kind: 'alias',
75
- key: current_context[:key],
76
- contextKind: context_to_context_kind(current_context),
77
- previousKey: previous_context[:key],
78
- previousContextKind: context_to_context_kind(previous_context)
79
- }
80
- end
81
-
82
- def new_custom_event(event_name, user, data, metric_value)
83
- e = {
84
- kind: 'custom',
85
- key: event_name,
86
- user: user
87
- }
88
- e[:data] = data if !data.nil?
89
- e[:metricValue] = metric_value if !metric_value.nil?
90
- e[:contextKind] = context_to_context_kind(user) if !user.nil? && user[:anonymous]
91
- e
92
- end
93
-
94
- private
95
-
96
- def context_to_context_kind(user)
97
- if !user.nil? && user[:anonymous]
98
- return "anonymousUser"
99
- else
100
- return "user"
101
- end
102
- end
103
-
104
- def is_experiment(flag, reason)
105
- return false if !reason
106
-
107
- if reason.in_experiment
108
- return true
109
- end
110
-
111
- case reason[:kind]
112
- when 'RULE_MATCH'
113
- index = reason[:ruleIndex]
114
- if !index.nil?
115
- rules = flag[:rules] || []
116
- return index >= 0 && index < rules.length && rules[index][:trackEvents]
117
- end
118
- when 'FALLTHROUGH'
119
- return !!flag[:trackEventsFallthrough]
120
- end
121
- false
122
- end
123
-
124
- end
125
- end
126
- end
@@ -1,17 +0,0 @@
1
- module LaunchDarkly
2
- # @private
3
- class LDNewRelic
4
- begin
5
- require "newrelic_rpm"
6
- NR_ENABLED = defined?(::NewRelic::Agent.add_custom_parameters)
7
- rescue ScriptError, StandardError
8
- NR_ENABLED = false
9
- end
10
-
11
- def self.annotate_transaction(key, value)
12
- if NR_ENABLED
13
- ::NewRelic::Agent.add_custom_parameters(key.to_s => value.to_s)
14
- end
15
- end
16
- end
17
- end
@@ -1,88 +0,0 @@
1
- require "ldclient-rb/interfaces"
2
- require "ldclient-rb/impl/integrations/redis_impl"
3
-
4
- module LaunchDarkly
5
- #
6
- # An implementation of the LaunchDarkly client's feature store that uses a Redis
7
- # instance. This object holds feature flags and related data received from the
8
- # streaming API. Feature data can also be further cached in memory to reduce overhead
9
- # of calls to Redis.
10
- #
11
- # To use this class, you must first have the `redis` and `connection-pool` gems
12
- # installed. Then, create an instance and store it in the `feature_store` property
13
- # of your client configuration.
14
- #
15
- # @deprecated Use the factory method in {LaunchDarkly::Integrations::Redis} instead. This specific
16
- # implementation class may be changed or removed in the future.
17
- #
18
- class RedisFeatureStore
19
- include LaunchDarkly::Interfaces::FeatureStore
20
-
21
- # Note that this class is now just a facade around CachingStoreWrapper, which is in turn delegating
22
- # to RedisFeatureStoreCore where the actual database logic is. This class was retained for historical
23
- # reasons, so that existing code can still call RedisFeatureStore.new. In the future, we will migrate
24
- # away from exposing these concrete classes and use factory methods instead.
25
-
26
- #
27
- # Constructor for a RedisFeatureStore instance.
28
- #
29
- # @param opts [Hash] the configuration options
30
- # @option opts [String] :redis_url URL of the Redis instance (shortcut for omitting redis_opts)
31
- # @option opts [Hash] :redis_opts options to pass to the Redis constructor (if you want to specify more than just redis_url)
32
- # @option opts [String] :prefix namespace prefix to add to all hash keys used by LaunchDarkly
33
- # @option opts [Logger] :logger a `Logger` instance; defaults to `Config.default_logger`
34
- # @option opts [Integer] :max_connections size of the Redis connection pool
35
- # @option opts [Integer] :expiration expiration time for the in-memory cache, in seconds; 0 for no local caching
36
- # @option opts [Integer] :capacity maximum number of feature flags (or related objects) to cache locally
37
- # @option opts [Object] :pool custom connection pool, if desired
38
- # @option opts [Boolean] :pool_shutdown_on_close whether calling `close` should shutdown the custom connection pool.
39
- #
40
- def initialize(opts = {})
41
- core = LaunchDarkly::Impl::Integrations::Redis::RedisFeatureStoreCore.new(opts)
42
- @wrapper = LaunchDarkly::Integrations::Util::CachingStoreWrapper.new(core, opts)
43
- end
44
-
45
- #
46
- # Default value for the `redis_url` constructor parameter; points to an instance of Redis
47
- # running at `localhost` with its default port.
48
- #
49
- def self.default_redis_url
50
- LaunchDarkly::Integrations::Redis::default_redis_url
51
- end
52
-
53
- #
54
- # Default value for the `prefix` constructor parameter.
55
- #
56
- def self.default_prefix
57
- LaunchDarkly::Integrations::Redis::default_prefix
58
- end
59
-
60
- def get(kind, key)
61
- @wrapper.get(kind, key)
62
- end
63
-
64
- def all(kind)
65
- @wrapper.all(kind)
66
- end
67
-
68
- def delete(kind, key, version)
69
- @wrapper.delete(kind, key, version)
70
- end
71
-
72
- def init(all_data)
73
- @wrapper.init(all_data)
74
- end
75
-
76
- def upsert(kind, item)
77
- @wrapper.upsert(kind, item)
78
- end
79
-
80
- def initialized?
81
- @wrapper.initialized?
82
- end
83
-
84
- def stop
85
- @wrapper.stop
86
- end
87
- end
88
- end
@@ -1,52 +0,0 @@
1
- require "json"
2
- require "set"
3
-
4
- module LaunchDarkly
5
- # @private
6
- class UserFilter
7
- def initialize(config)
8
- @all_attributes_private = config.all_attributes_private
9
- @private_attribute_names = Set.new(config.private_attribute_names.map(&:to_sym))
10
- end
11
-
12
- def transform_user_props(user_props)
13
- return nil if user_props.nil?
14
-
15
- user_private_attrs = Set.new((user_props[:privateAttributeNames] || []).map(&:to_sym))
16
-
17
- filtered_user_props, removed = filter_values(user_props, user_private_attrs, ALLOWED_TOP_LEVEL_KEYS, IGNORED_TOP_LEVEL_KEYS)
18
- custom = user_props[:custom]
19
- if !custom.nil?
20
- filtered_user_props[:custom], removed_custom = filter_values(custom, user_private_attrs)
21
- removed.merge(removed_custom)
22
- end
23
-
24
- unless removed.empty?
25
- # note, :privateAttributeNames is what the developer sets; :privateAttrs is what we send to the server
26
- filtered_user_props[:privateAttrs] = removed.to_a.sort.map { |s| s.to_s }
27
- end
28
- return filtered_user_props
29
- end
30
-
31
- private
32
-
33
- ALLOWED_TOP_LEVEL_KEYS = Set.new([:key, :secondary, :ip, :country, :email,
34
- :firstName, :lastName, :avatar, :name, :anonymous, :custom])
35
- IGNORED_TOP_LEVEL_KEYS = Set.new([:custom, :key, :anonymous])
36
-
37
- def filter_values(props, user_private_attrs, allowed_keys = [], keys_to_leave_as_is = [])
38
- is_valid_key = lambda { |key| allowed_keys.empty? || allowed_keys.include?(key) }
39
- removed_keys = Set.new(props.keys.select { |key|
40
- # Note that if is_valid_key returns false, we don't explicitly *remove* the key (which would place
41
- # it in the privateAttrs list) - we just silently drop it when we calculate filtered_hash.
42
- is_valid_key.call(key) && !keys_to_leave_as_is.include?(key) && private_attr?(key, user_private_attrs)
43
- })
44
- filtered_hash = props.select { |key, value| !removed_keys.include?(key) && is_valid_key.call(key) }
45
- [filtered_hash, removed_keys]
46
- end
47
-
48
- def private_attr?(name, user_private_attrs)
49
- @all_attributes_private || @private_attribute_names.include?(name) || user_private_attrs.include?(name)
50
- end
51
- end
52
- end