launchdarkly-server-sdk 5.7.3 → 6.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.
- checksums.yaml +5 -5
- data/.circleci/config.yml +28 -122
- data/.gitignore +1 -1
- data/.ldrelease/build-docs.sh +18 -0
- data/.ldrelease/circleci/linux/execute.sh +18 -0
- data/.ldrelease/circleci/mac/execute.sh +18 -0
- data/.ldrelease/circleci/template/build.sh +29 -0
- data/.ldrelease/circleci/template/publish.sh +23 -0
- data/.ldrelease/circleci/template/set-gem-home.sh +7 -0
- data/.ldrelease/circleci/template/test.sh +10 -0
- data/.ldrelease/circleci/template/update-version.sh +8 -0
- data/.ldrelease/circleci/windows/execute.ps1 +19 -0
- data/.ldrelease/config.yml +14 -2
- data/CHANGELOG.md +36 -0
- data/CONTRIBUTING.md +1 -1
- data/Gemfile.lock +92 -76
- data/README.md +5 -3
- data/azure-pipelines.yml +1 -1
- data/docs/Makefile +26 -0
- data/docs/index.md +9 -0
- data/launchdarkly-server-sdk.gemspec +20 -13
- data/lib/ldclient-rb.rb +0 -1
- data/lib/ldclient-rb/config.rb +15 -3
- data/lib/ldclient-rb/evaluation_detail.rb +293 -0
- data/lib/ldclient-rb/events.rb +1 -4
- data/lib/ldclient-rb/file_data_source.rb +1 -1
- data/lib/ldclient-rb/impl/evaluator.rb +225 -0
- data/lib/ldclient-rb/impl/evaluator_bucketing.rb +74 -0
- data/lib/ldclient-rb/impl/evaluator_operators.rb +160 -0
- data/lib/ldclient-rb/impl/event_sender.rb +56 -40
- data/lib/ldclient-rb/impl/integrations/consul_impl.rb +5 -5
- data/lib/ldclient-rb/impl/integrations/dynamodb_impl.rb +5 -5
- data/lib/ldclient-rb/impl/integrations/redis_impl.rb +8 -7
- data/lib/ldclient-rb/impl/model/serialization.rb +62 -0
- data/lib/ldclient-rb/impl/unbounded_pool.rb +34 -0
- data/lib/ldclient-rb/integrations/redis.rb +3 -0
- data/lib/ldclient-rb/ldclient.rb +16 -11
- data/lib/ldclient-rb/polling.rb +1 -4
- data/lib/ldclient-rb/redis_store.rb +1 -0
- data/lib/ldclient-rb/requestor.rb +25 -23
- data/lib/ldclient-rb/stream.rb +10 -30
- data/lib/ldclient-rb/user_filter.rb +3 -2
- data/lib/ldclient-rb/util.rb +12 -8
- data/lib/ldclient-rb/version.rb +1 -1
- data/spec/evaluation_detail_spec.rb +135 -0
- data/spec/event_sender_spec.rb +20 -2
- data/spec/events_spec.rb +11 -0
- data/spec/http_util.rb +11 -1
- data/spec/impl/evaluator_bucketing_spec.rb +111 -0
- data/spec/impl/evaluator_clause_spec.rb +55 -0
- data/spec/impl/evaluator_operators_spec.rb +141 -0
- data/spec/impl/evaluator_rule_spec.rb +96 -0
- data/spec/impl/evaluator_segment_spec.rb +125 -0
- data/spec/impl/evaluator_spec.rb +305 -0
- data/spec/impl/evaluator_spec_base.rb +75 -0
- data/spec/impl/model/serialization_spec.rb +41 -0
- data/spec/launchdarkly-server-sdk_spec.rb +1 -1
- data/spec/ldclient_end_to_end_spec.rb +34 -0
- data/spec/ldclient_spec.rb +10 -8
- data/spec/polling_spec.rb +2 -2
- data/spec/redis_feature_store_spec.rb +32 -3
- data/spec/requestor_spec.rb +11 -45
- data/spec/spec_helper.rb +0 -3
- data/spec/stream_spec.rb +1 -16
- metadata +110 -60
- data/.yardopts +0 -9
- data/lib/ldclient-rb/evaluation.rb +0 -462
- data/scripts/gendocs.sh +0 -11
- data/scripts/release.sh +0 -27
- data/spec/evaluation_spec.rb +0 -789
@@ -1,4 +1,7 @@
|
|
1
|
+
require "ldclient-rb/impl/unbounded_pool"
|
2
|
+
|
1
3
|
require "securerandom"
|
4
|
+
require "http"
|
2
5
|
|
3
6
|
module LaunchDarkly
|
4
7
|
module Impl
|
@@ -9,62 +12,75 @@ module LaunchDarkly
|
|
9
12
|
DEFAULT_RETRY_INTERVAL = 1
|
10
13
|
|
11
14
|
def initialize(sdk_key, config, http_client = nil, retry_interval = DEFAULT_RETRY_INTERVAL)
|
12
|
-
@client = http_client ? http_client : LaunchDarkly::Util.new_http_client(config.events_uri, config)
|
13
15
|
@sdk_key = sdk_key
|
14
16
|
@config = config
|
15
17
|
@events_uri = config.events_uri + "/bulk"
|
16
18
|
@diagnostic_uri = config.events_uri + "/diagnostic"
|
17
19
|
@logger = config.logger
|
18
20
|
@retry_interval = retry_interval
|
21
|
+
@http_client_pool = UnboundedPool.new(
|
22
|
+
lambda { LaunchDarkly::Util.new_http_client(@config.events_uri, @config) },
|
23
|
+
lambda { |client| client.close })
|
24
|
+
end
|
25
|
+
|
26
|
+
def stop
|
27
|
+
@http_client_pool.dispose_all()
|
19
28
|
end
|
20
29
|
|
21
30
|
def send_event_data(event_data, description, is_diagnostic)
|
22
31
|
uri = is_diagnostic ? @diagnostic_uri : @events_uri
|
23
32
|
payload_id = is_diagnostic ? nil : SecureRandom.uuid
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
@client.start if !@client.started?
|
32
|
-
@logger.debug { "[LDClient] sending #{description}: #{event_data}" }
|
33
|
-
req = Net::HTTP::Post.new(uri)
|
34
|
-
req.content_type = "application/json"
|
35
|
-
req.body = event_data
|
36
|
-
Impl::Util.default_http_headers(@sdk_key, @config).each { |k, v| req[k] = v }
|
37
|
-
if !is_diagnostic
|
38
|
-
req["X-LaunchDarkly-Event-Schema"] = CURRENT_SCHEMA_VERSION.to_s
|
39
|
-
req["X-LaunchDarkly-Payload-ID"] = payload_id
|
33
|
+
begin
|
34
|
+
http_client = @http_client_pool.acquire()
|
35
|
+
response = nil
|
36
|
+
(0..1).each do |attempt|
|
37
|
+
if attempt > 0
|
38
|
+
@logger.warn { "[LDClient] Will retry posting events after #{@retry_interval} second" }
|
39
|
+
sleep(@retry_interval)
|
40
40
|
end
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
res_time = nil
|
50
|
-
if !res["date"].nil?
|
51
|
-
begin
|
52
|
-
res_time = Time.httpdate(res["date"])
|
53
|
-
rescue ArgumentError
|
41
|
+
begin
|
42
|
+
@logger.debug { "[LDClient] sending #{description}: #{event_data}" }
|
43
|
+
headers = {}
|
44
|
+
headers["content-type"] = "application/json"
|
45
|
+
Impl::Util.default_http_headers(@sdk_key, @config).each { |k, v| headers[k] = v }
|
46
|
+
if !is_diagnostic
|
47
|
+
headers["X-LaunchDarkly-Event-Schema"] = CURRENT_SCHEMA_VERSION.to_s
|
48
|
+
headers["X-LaunchDarkly-Payload-ID"] = payload_id
|
54
49
|
end
|
50
|
+
response = http_client.request("POST", uri, {
|
51
|
+
headers: headers,
|
52
|
+
body: event_data
|
53
|
+
})
|
54
|
+
rescue StandardError => exn
|
55
|
+
@logger.warn { "[LDClient] Error sending events: #{exn.inspect}." }
|
56
|
+
next
|
57
|
+
end
|
58
|
+
status = response.status.code
|
59
|
+
# must fully read body for persistent connections
|
60
|
+
body = response.to_s
|
61
|
+
if status >= 200 && status < 300
|
62
|
+
res_time = nil
|
63
|
+
if !response.headers["date"].nil?
|
64
|
+
begin
|
65
|
+
res_time = Time.httpdate(response.headers["date"])
|
66
|
+
rescue ArgumentError
|
67
|
+
end
|
68
|
+
end
|
69
|
+
return EventSenderResult.new(true, false, res_time)
|
70
|
+
end
|
71
|
+
must_shutdown = !LaunchDarkly::Util.http_error_recoverable?(status)
|
72
|
+
can_retry = !must_shutdown && attempt == 0
|
73
|
+
message = LaunchDarkly::Util.http_error_message(status, "event delivery", can_retry ? "will retry" : "some events were dropped")
|
74
|
+
@logger.error { "[LDClient] #{message}" }
|
75
|
+
if must_shutdown
|
76
|
+
return EventSenderResult.new(false, true, nil)
|
55
77
|
end
|
56
|
-
return EventSenderResult.new(true, false, res_time)
|
57
|
-
end
|
58
|
-
must_shutdown = !LaunchDarkly::Util.http_error_recoverable?(status)
|
59
|
-
can_retry = !must_shutdown && attempt == 0
|
60
|
-
message = LaunchDarkly::Util.http_error_message(status, "event delivery", can_retry ? "will retry" : "some events were dropped")
|
61
|
-
@logger.error { "[LDClient] #{message}" }
|
62
|
-
if must_shutdown
|
63
|
-
return EventSenderResult.new(false, true, nil)
|
64
78
|
end
|
79
|
+
# used up our retries
|
80
|
+
return EventSenderResult.new(false, false, nil)
|
81
|
+
ensure
|
82
|
+
@http_client_pool.release(http_client)
|
65
83
|
end
|
66
|
-
# used up our retries
|
67
|
-
return EventSenderResult.new(false, false, nil)
|
68
84
|
end
|
69
85
|
end
|
70
86
|
end
|
@@ -39,7 +39,7 @@ module LaunchDarkly
|
|
39
39
|
# Insert or update every provided item
|
40
40
|
all_data.each do |kind, items|
|
41
41
|
items.values.each do |item|
|
42
|
-
value = item
|
42
|
+
value = Model.serialize(kind, item)
|
43
43
|
key = item_key(kind, item[:key])
|
44
44
|
ops.push({ 'KV' => { 'Verb' => 'set', 'Key' => key, 'Value' => value } })
|
45
45
|
unused_old_keys.delete(key)
|
@@ -62,7 +62,7 @@ module LaunchDarkly
|
|
62
62
|
|
63
63
|
def get_internal(kind, key)
|
64
64
|
value = Diplomat::Kv.get(item_key(kind, key), {}, :return) # :return means "don't throw an error if not found"
|
65
|
-
(value.nil? || value == "") ? nil :
|
65
|
+
(value.nil? || value == "") ? nil : Model.deserialize(kind, value)
|
66
66
|
end
|
67
67
|
|
68
68
|
def get_all_internal(kind)
|
@@ -71,7 +71,7 @@ module LaunchDarkly
|
|
71
71
|
(results == "" ? [] : results).each do |result|
|
72
72
|
value = result[:value]
|
73
73
|
if !value.nil?
|
74
|
-
item =
|
74
|
+
item = Model.deserialize(kind, value)
|
75
75
|
items_out[item[:key].to_sym] = item
|
76
76
|
end
|
77
77
|
end
|
@@ -80,7 +80,7 @@ module LaunchDarkly
|
|
80
80
|
|
81
81
|
def upsert_internal(kind, new_item)
|
82
82
|
key = item_key(kind, new_item[:key])
|
83
|
-
json = new_item
|
83
|
+
json = Model.serialize(kind, new_item)
|
84
84
|
|
85
85
|
# We will potentially keep retrying indefinitely until someone's write succeeds
|
86
86
|
while true
|
@@ -88,7 +88,7 @@ module LaunchDarkly
|
|
88
88
|
if old_value.nil? || old_value == ""
|
89
89
|
mod_index = 0
|
90
90
|
else
|
91
|
-
old_item =
|
91
|
+
old_item = Model.deserialize(kind, old_value[0]["Value"])
|
92
92
|
# Check whether the item is stale. If so, don't do the update (and return the existing item to
|
93
93
|
# FeatureStoreWrapper so it can be cached)
|
94
94
|
if old_item[:version] >= new_item[:version]
|
@@ -77,7 +77,7 @@ module LaunchDarkly
|
|
77
77
|
|
78
78
|
def get_internal(kind, key)
|
79
79
|
resp = get_item_by_keys(namespace_for_kind(kind), key)
|
80
|
-
unmarshal_item(resp.item)
|
80
|
+
unmarshal_item(kind, resp.item)
|
81
81
|
end
|
82
82
|
|
83
83
|
def get_all_internal(kind)
|
@@ -86,7 +86,7 @@ module LaunchDarkly
|
|
86
86
|
while true
|
87
87
|
resp = @client.query(req)
|
88
88
|
resp.items.each do |item|
|
89
|
-
item_out = unmarshal_item(item)
|
89
|
+
item_out = unmarshal_item(kind, item)
|
90
90
|
items_out[item_out[:key].to_sym] = item_out
|
91
91
|
end
|
92
92
|
break if resp.last_evaluated_key.nil? || resp.last_evaluated_key.length == 0
|
@@ -196,15 +196,15 @@ module LaunchDarkly
|
|
196
196
|
def marshal_item(kind, item)
|
197
197
|
make_keys_hash(namespace_for_kind(kind), item[:key]).merge({
|
198
198
|
VERSION_ATTRIBUTE => item[:version],
|
199
|
-
ITEM_JSON_ATTRIBUTE => item
|
199
|
+
ITEM_JSON_ATTRIBUTE => Model.serialize(kind, item)
|
200
200
|
})
|
201
201
|
end
|
202
202
|
|
203
|
-
def unmarshal_item(item)
|
203
|
+
def unmarshal_item(kind, item)
|
204
204
|
return nil if item.nil? || item.length == 0
|
205
205
|
json_attr = item[ITEM_JSON_ATTRIBUTE]
|
206
206
|
raise RuntimeError.new("DynamoDB map did not contain expected item string") if json_attr.nil?
|
207
|
-
|
207
|
+
Model.deserialize(kind, json_attr)
|
208
208
|
end
|
209
209
|
end
|
210
210
|
|
@@ -33,6 +33,8 @@ module LaunchDarkly
|
|
33
33
|
@pool = opts[:pool] || ConnectionPool.new(size: max_connections) do
|
34
34
|
::Redis.new(@redis_opts)
|
35
35
|
end
|
36
|
+
# shutdown pool on close unless the client passed a custom pool and specified not to shutdown
|
37
|
+
@pool_shutdown_on_close = (!opts[:pool] || opts.fetch(:pool_shutdown_on_close, true))
|
36
38
|
@prefix = opts[:prefix] || LaunchDarkly::Integrations::Redis::default_prefix
|
37
39
|
@logger = opts[:logger] || Config.default_logger
|
38
40
|
@test_hook = opts[:test_hook] # used for unit tests, deliberately undocumented
|
@@ -53,7 +55,7 @@ module LaunchDarkly
|
|
53
55
|
multi.del(items_key(kind))
|
54
56
|
count = count + items.count
|
55
57
|
items.each do |key, item|
|
56
|
-
multi.hset(items_key(kind), key, item
|
58
|
+
multi.hset(items_key(kind), key, Model.serialize(kind,item))
|
57
59
|
end
|
58
60
|
end
|
59
61
|
multi.set(inited_key, inited_key)
|
@@ -73,8 +75,7 @@ module LaunchDarkly
|
|
73
75
|
with_connection do |redis|
|
74
76
|
hashfs = redis.hgetall(items_key(kind))
|
75
77
|
hashfs.each do |k, json_item|
|
76
|
-
|
77
|
-
fs[k.to_sym] = f
|
78
|
+
fs[k.to_sym] = Model.deserialize(kind, json_item)
|
78
79
|
end
|
79
80
|
end
|
80
81
|
fs
|
@@ -93,7 +94,7 @@ module LaunchDarkly
|
|
93
94
|
before_update_transaction(base_key, key)
|
94
95
|
if old_item.nil? || old_item[:version] < new_item[:version]
|
95
96
|
result = redis.multi do |multi|
|
96
|
-
multi.hset(base_key, key, new_item
|
97
|
+
multi.hset(base_key, key, Model.serialize(kind, new_item))
|
97
98
|
end
|
98
99
|
if result.nil?
|
99
100
|
@logger.debug { "RedisFeatureStore: concurrent modification detected, retrying" }
|
@@ -113,11 +114,12 @@ module LaunchDarkly
|
|
113
114
|
end
|
114
115
|
|
115
116
|
def initialized_internal?
|
116
|
-
with_connection { |redis| redis.exists(inited_key) }
|
117
|
+
with_connection { |redis| redis.exists?(inited_key) }
|
117
118
|
end
|
118
119
|
|
119
120
|
def stop
|
120
121
|
if @stopped.make_true
|
122
|
+
return unless @pool_shutdown_on_close
|
121
123
|
@pool.shutdown { |redis| redis.close }
|
122
124
|
end
|
123
125
|
end
|
@@ -145,8 +147,7 @@ module LaunchDarkly
|
|
145
147
|
end
|
146
148
|
|
147
149
|
def get_redis(redis, kind, key)
|
148
|
-
|
149
|
-
json_item.nil? ? nil : JSON.parse(json_item, symbolize_names: true)
|
150
|
+
Model.deserialize(kind, redis.hget(items_key(kind), key))
|
150
151
|
end
|
151
152
|
end
|
152
153
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
|
2
|
+
module LaunchDarkly
|
3
|
+
module Impl
|
4
|
+
module Model
|
5
|
+
# Abstraction of deserializing a feature flag or segment that was read from a data store or
|
6
|
+
# received from LaunchDarkly.
|
7
|
+
def self.deserialize(kind, json)
|
8
|
+
return nil if json.nil?
|
9
|
+
item = JSON.parse(json, symbolize_names: true)
|
10
|
+
postprocess_item_after_deserializing!(kind, item)
|
11
|
+
item
|
12
|
+
end
|
13
|
+
|
14
|
+
# Abstraction of serializing a feature flag or segment that will be written to a data store.
|
15
|
+
# Currently we just call to_json.
|
16
|
+
def self.serialize(kind, item)
|
17
|
+
item.to_json
|
18
|
+
end
|
19
|
+
|
20
|
+
# Translates a { flags: ..., segments: ... } object received from LaunchDarkly to the data store format.
|
21
|
+
def self.make_all_store_data(received_data)
|
22
|
+
flags = received_data[:flags]
|
23
|
+
postprocess_items_after_deserializing!(FEATURES, flags)
|
24
|
+
segments = received_data[:segments]
|
25
|
+
postprocess_items_after_deserializing!(SEGMENTS, segments)
|
26
|
+
{ FEATURES => flags, SEGMENTS => segments }
|
27
|
+
end
|
28
|
+
|
29
|
+
# Called after we have deserialized a model item from JSON (because we received it from LaunchDarkly,
|
30
|
+
# or read it from a persistent data store). This allows us to precompute some derived attributes that
|
31
|
+
# will never change during the lifetime of that item.
|
32
|
+
def self.postprocess_item_after_deserializing!(kind, item)
|
33
|
+
return if !item
|
34
|
+
# Currently we are special-casing this for FEATURES; eventually it will be handled by delegating
|
35
|
+
# to the "kind" object or the item class.
|
36
|
+
if kind.eql? FEATURES
|
37
|
+
# For feature flags, we precompute all possible parameterized EvaluationReason instances.
|
38
|
+
prereqs = item[:prerequisites]
|
39
|
+
if !prereqs.nil?
|
40
|
+
prereqs.each do |prereq|
|
41
|
+
prereq[:_reason] = EvaluationReason::prerequisite_failed(prereq[:key])
|
42
|
+
end
|
43
|
+
end
|
44
|
+
rules = item[:rules]
|
45
|
+
if !rules.nil?
|
46
|
+
rules.each_index do |i|
|
47
|
+
rule = rules[i]
|
48
|
+
rule[:_reason] = EvaluationReason::rule_match(i, rule[:id])
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.postprocess_items_after_deserializing!(kind, items_map)
|
55
|
+
return items_map if !items_map
|
56
|
+
items_map.each do |key, item|
|
57
|
+
postprocess_item_after_deserializing!(kind, item)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module LaunchDarkly
|
2
|
+
module Impl
|
3
|
+
# A simple thread safe generic unbounded resource pool abstraction
|
4
|
+
class UnboundedPool
|
5
|
+
def initialize(instance_creator, instance_destructor)
|
6
|
+
@pool = Array.new
|
7
|
+
@lock = Mutex.new
|
8
|
+
@instance_creator = instance_creator
|
9
|
+
@instance_destructor = instance_destructor
|
10
|
+
end
|
11
|
+
|
12
|
+
def acquire
|
13
|
+
@lock.synchronize {
|
14
|
+
if @pool.length == 0
|
15
|
+
@instance_creator.call()
|
16
|
+
else
|
17
|
+
@pool.pop()
|
18
|
+
end
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def release(instance)
|
23
|
+
@lock.synchronize { @pool.push(instance) }
|
24
|
+
end
|
25
|
+
|
26
|
+
def dispose_all
|
27
|
+
@lock.synchronize {
|
28
|
+
@pool.map { |instance| @instance_destructor.call(instance) } if !@instance_destructor.nil?
|
29
|
+
@pool.clear()
|
30
|
+
}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -45,6 +45,9 @@ module LaunchDarkly
|
|
45
45
|
# @option opts [Integer] :expiration (15) expiration time for the in-memory cache, in seconds; 0 for no local caching
|
46
46
|
# @option opts [Integer] :capacity (1000) maximum number of items in the cache
|
47
47
|
# @option opts [Object] :pool custom connection pool, if desired
|
48
|
+
# @option opts [Boolean] :pool_shutdown_on_close whether calling `close` should shutdown the custom connection pool;
|
49
|
+
# this is true by default, and should be set to false only if you are managing the pool yourself and want its
|
50
|
+
# lifecycle to be independent of the SDK client
|
48
51
|
# @return [LaunchDarkly::Interfaces::FeatureStore] a feature store object
|
49
52
|
#
|
50
53
|
def self.new_feature_store(opts)
|
data/lib/ldclient-rb/ldclient.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "ldclient-rb/impl/diagnostic_events"
|
2
|
+
require "ldclient-rb/impl/evaluator"
|
2
3
|
require "ldclient-rb/impl/event_factory"
|
3
4
|
require "ldclient-rb/impl/store_client_wrapper"
|
4
5
|
require "concurrent/atomics"
|
@@ -14,7 +15,6 @@ module LaunchDarkly
|
|
14
15
|
# should create a single client instance for the lifetime of the application.
|
15
16
|
#
|
16
17
|
class LDClient
|
17
|
-
include Evaluation
|
18
18
|
include Impl
|
19
19
|
#
|
20
20
|
# Creates a new client instance that connects to LaunchDarkly. A custom
|
@@ -57,6 +57,10 @@ module LaunchDarkly
|
|
57
57
|
updated_config.instance_variable_set(:@feature_store, @store)
|
58
58
|
@config = updated_config
|
59
59
|
|
60
|
+
get_flag = lambda { |key| @store.get(FEATURES, key) }
|
61
|
+
get_segment = lambda { |key| @store.get(SEGMENTS, key) }
|
62
|
+
@evaluator = LaunchDarkly::Impl::Evaluator.new(get_flag, get_segment, @config.logger)
|
63
|
+
|
60
64
|
if !@config.offline? && @config.send_events && !@config.diagnostic_opt_out?
|
61
65
|
diagnostic_accumulator = Impl::DiagnosticAccumulator.new(Impl::DiagnosticAccumulator.create_diagnostic_id(sdk_key))
|
62
66
|
else
|
@@ -333,12 +337,13 @@ module LaunchDarkly
|
|
333
337
|
next
|
334
338
|
end
|
335
339
|
begin
|
336
|
-
result = evaluate(f, user, @
|
340
|
+
result = @evaluator.evaluate(f, user, @event_factory_default)
|
337
341
|
state.add_flag(f, result.detail.value, result.detail.variation_index, with_reasons ? result.detail.reason : nil,
|
338
342
|
details_only_if_tracked)
|
339
343
|
rescue => exn
|
340
344
|
Util.log_exception(@config.logger, "Error evaluating flag \"#{k}\" in all_flags_state", exn)
|
341
|
-
state.add_flag(f, nil, nil, with_reasons ?
|
345
|
+
state.add_flag(f, nil, nil, with_reasons ? EvaluationReason::error(EvaluationReason::ERROR_EXCEPTION) : nil,
|
346
|
+
details_only_if_tracked)
|
342
347
|
end
|
343
348
|
end
|
344
349
|
|
@@ -363,12 +368,12 @@ module LaunchDarkly
|
|
363
368
|
return NullUpdateProcessor.new
|
364
369
|
end
|
365
370
|
raise ArgumentError, "sdk_key must not be nil" if sdk_key.nil? # see LDClient constructor comment on sdk_key
|
366
|
-
requestor = Requestor.new(sdk_key, config)
|
367
371
|
if config.stream?
|
368
|
-
StreamProcessor.new(sdk_key, config,
|
372
|
+
StreamProcessor.new(sdk_key, config, diagnostic_accumulator)
|
369
373
|
else
|
370
374
|
config.logger.info { "Disabling streaming API" }
|
371
375
|
config.logger.warn { "You should only disable the streaming API if instructed to do so by LaunchDarkly support" }
|
376
|
+
requestor = Requestor.new(sdk_key, config)
|
372
377
|
PollingProcessor.new(config, requestor)
|
373
378
|
end
|
374
379
|
end
|
@@ -376,7 +381,7 @@ module LaunchDarkly
|
|
376
381
|
# @return [EvaluationDetail]
|
377
382
|
def evaluate_internal(key, user, default, event_factory)
|
378
383
|
if @config.offline?
|
379
|
-
return error_result(
|
384
|
+
return Evaluator.error_result(EvaluationReason::ERROR_CLIENT_NOT_READY, default)
|
380
385
|
end
|
381
386
|
|
382
387
|
if !initialized?
|
@@ -384,7 +389,7 @@ module LaunchDarkly
|
|
384
389
|
@config.logger.warn { "[LDClient] Client has not finished initializing; using last known values from feature store" }
|
385
390
|
else
|
386
391
|
@config.logger.error { "[LDClient] Client has not finished initializing; feature store unavailable, returning default value" }
|
387
|
-
detail = error_result(
|
392
|
+
detail = Evaluator.error_result(EvaluationReason::ERROR_CLIENT_NOT_READY, default)
|
388
393
|
@event_processor.add_event(event_factory.new_unknown_flag_event(key, user, default, detail.reason))
|
389
394
|
return detail
|
390
395
|
end
|
@@ -394,20 +399,20 @@ module LaunchDarkly
|
|
394
399
|
|
395
400
|
if feature.nil?
|
396
401
|
@config.logger.info { "[LDClient] Unknown feature flag \"#{key}\". Returning default value" }
|
397
|
-
detail = error_result(
|
402
|
+
detail = Evaluator.error_result(EvaluationReason::ERROR_FLAG_NOT_FOUND, default)
|
398
403
|
@event_processor.add_event(event_factory.new_unknown_flag_event(key, user, default, detail.reason))
|
399
404
|
return detail
|
400
405
|
end
|
401
406
|
|
402
407
|
unless user
|
403
408
|
@config.logger.error { "[LDClient] Must specify user" }
|
404
|
-
detail = error_result(
|
409
|
+
detail = Evaluator.error_result(EvaluationReason::ERROR_USER_NOT_SPECIFIED, default)
|
405
410
|
@event_processor.add_event(event_factory.new_default_event(feature, user, default, detail.reason))
|
406
411
|
return detail
|
407
412
|
end
|
408
413
|
|
409
414
|
begin
|
410
|
-
res = evaluate(feature, user,
|
415
|
+
res = @evaluator.evaluate(feature, user, event_factory)
|
411
416
|
if !res.events.nil?
|
412
417
|
res.events.each do |event|
|
413
418
|
@event_processor.add_event(event)
|
@@ -421,7 +426,7 @@ module LaunchDarkly
|
|
421
426
|
return detail
|
422
427
|
rescue => exn
|
423
428
|
Util.log_exception(@config.logger, "Error evaluating feature flag \"#{key}\"", exn)
|
424
|
-
detail = error_result(
|
429
|
+
detail = Evaluator.error_result(EvaluationReason::ERROR_EXCEPTION, default)
|
425
430
|
@event_processor.add_event(event_factory.new_default_event(feature, user, default, detail.reason))
|
426
431
|
return detail
|
427
432
|
end
|