launchdarkly-server-sdk 5.7.4 → 6.1.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 +28 -0
- data/CONTRIBUTING.md +1 -1
- data/Gemfile.lock +92 -76
- data/README.md +4 -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 +3 -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_factory.rb +22 -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 +33 -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/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 +10 -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 +60 -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
data/lib/ldclient-rb/polling.rb
CHANGED
@@ -37,10 +37,7 @@ module LaunchDarkly
|
|
37
37
|
def poll
|
38
38
|
all_data = @requestor.request_all_data
|
39
39
|
if all_data
|
40
|
-
@config.feature_store.init(
|
41
|
-
FEATURES => all_data[:flags],
|
42
|
-
SEGMENTS => all_data[:segments]
|
43
|
-
})
|
40
|
+
@config.feature_store.init(all_data)
|
44
41
|
if @initialized.make_true
|
45
42
|
@config.logger.info { "[LDClient] Polling connection initialized" }
|
46
43
|
@ready.set
|
@@ -35,6 +35,7 @@ module LaunchDarkly
|
|
35
35
|
# @option opts [Integer] :expiration expiration time for the in-memory cache, in seconds; 0 for no local caching
|
36
36
|
# @option opts [Integer] :capacity maximum number of feature flags (or related objects) to cache locally
|
37
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.
|
38
39
|
#
|
39
40
|
def initialize(opts = {})
|
40
41
|
core = LaunchDarkly::Impl::Integrations::Redis::RedisFeatureStoreCore.new(opts)
|
@@ -1,6 +1,9 @@
|
|
1
|
+
require "ldclient-rb/impl/model/serialization"
|
2
|
+
|
1
3
|
require "concurrent/atomics"
|
2
4
|
require "json"
|
3
5
|
require "uri"
|
6
|
+
require "http"
|
4
7
|
|
5
8
|
module LaunchDarkly
|
6
9
|
# @private
|
@@ -22,45 +25,44 @@ module LaunchDarkly
|
|
22
25
|
def initialize(sdk_key, config)
|
23
26
|
@sdk_key = sdk_key
|
24
27
|
@config = config
|
25
|
-
@
|
28
|
+
@http_client = LaunchDarkly::Util.new_http_client(config.base_uri, config)
|
26
29
|
@cache = @config.cache_store
|
27
30
|
end
|
28
31
|
|
29
|
-
def request_flag(key)
|
30
|
-
make_request("/sdk/latest-flags/" + key)
|
31
|
-
end
|
32
|
-
|
33
|
-
def request_segment(key)
|
34
|
-
make_request("/sdk/latest-segments/" + key)
|
35
|
-
end
|
36
|
-
|
37
32
|
def request_all_data()
|
38
|
-
make_request("/sdk/latest-all")
|
33
|
+
all_data = JSON.parse(make_request("/sdk/latest-all"), symbolize_names: true)
|
34
|
+
Impl::Model.make_all_store_data(all_data)
|
39
35
|
end
|
40
36
|
|
41
37
|
def stop
|
42
38
|
begin
|
43
|
-
@
|
39
|
+
@http_client.close
|
44
40
|
rescue
|
45
41
|
end
|
46
42
|
end
|
47
43
|
|
48
44
|
private
|
49
45
|
|
46
|
+
def request_single_item(kind, path)
|
47
|
+
Impl::Model.deserialize(kind, make_request(path))
|
48
|
+
end
|
49
|
+
|
50
50
|
def make_request(path)
|
51
|
-
@client.start if !@client.started?
|
52
51
|
uri = URI(@config.base_uri + path)
|
53
|
-
|
54
|
-
Impl::Util.default_http_headers(@sdk_key, @config).each { |k, v|
|
55
|
-
|
52
|
+
headers = {}
|
53
|
+
Impl::Util.default_http_headers(@sdk_key, @config).each { |k, v| headers[k] = v }
|
54
|
+
headers["Connection"] = "keep-alive"
|
56
55
|
cached = @cache.read(uri)
|
57
56
|
if !cached.nil?
|
58
|
-
|
57
|
+
headers["If-None-Match"] = cached.etag
|
59
58
|
end
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
59
|
+
response = @http_client.request("GET", uri, {
|
60
|
+
headers: headers
|
61
|
+
})
|
62
|
+
status = response.status.code
|
63
|
+
@config.logger.debug { "[LDClient] Got response from uri: #{uri}\n\tstatus code: #{status}\n\theaders: #{response.headers}\n\tbody: #{res.to_s}" }
|
64
|
+
# must fully read body for persistent connections
|
65
|
+
body = response.to_s
|
64
66
|
if status == 304 && !cached.nil?
|
65
67
|
body = cached.body
|
66
68
|
else
|
@@ -68,11 +70,11 @@ module LaunchDarkly
|
|
68
70
|
if status < 200 || status >= 300
|
69
71
|
raise UnexpectedResponseError.new(status)
|
70
72
|
end
|
71
|
-
body = fix_encoding(
|
72
|
-
etag =
|
73
|
+
body = fix_encoding(body, response.headers["content-type"])
|
74
|
+
etag = response.headers["etag"]
|
73
75
|
@cache.write(uri, CacheEntry.new(etag, body)) if !etag.nil?
|
74
76
|
end
|
75
|
-
|
77
|
+
body
|
76
78
|
end
|
77
79
|
|
78
80
|
def fix_encoding(body, content_type)
|
data/lib/ldclient-rb/stream.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require "ldclient-rb/impl/model/serialization"
|
2
|
+
|
1
3
|
require "concurrent/atomics"
|
2
4
|
require "json"
|
3
5
|
require "ld-eventsource"
|
@@ -10,10 +12,6 @@ module LaunchDarkly
|
|
10
12
|
# @private
|
11
13
|
DELETE = :delete
|
12
14
|
# @private
|
13
|
-
INDIRECT_PUT = :'indirect/put'
|
14
|
-
# @private
|
15
|
-
INDIRECT_PATCH = :'indirect/patch'
|
16
|
-
# @private
|
17
15
|
READ_TIMEOUT_SECONDS = 300 # 5 minutes; the stream should send a ping every 3 minutes
|
18
16
|
|
19
17
|
# @private
|
@@ -24,11 +22,10 @@ module LaunchDarkly
|
|
24
22
|
|
25
23
|
# @private
|
26
24
|
class StreamProcessor
|
27
|
-
def initialize(sdk_key, config,
|
25
|
+
def initialize(sdk_key, config, diagnostic_accumulator = nil)
|
28
26
|
@sdk_key = sdk_key
|
29
27
|
@config = config
|
30
28
|
@feature_store = config.feature_store
|
31
|
-
@requestor = requestor
|
32
29
|
@initialized = Concurrent::AtomicBoolean.new(false)
|
33
30
|
@started = Concurrent::AtomicBoolean.new(false)
|
34
31
|
@stopped = Concurrent::AtomicBoolean.new(false)
|
@@ -49,7 +46,8 @@ module LaunchDarkly
|
|
49
46
|
opts = {
|
50
47
|
headers: headers,
|
51
48
|
read_timeout: READ_TIMEOUT_SECONDS,
|
52
|
-
logger: @config.logger
|
49
|
+
logger: @config.logger,
|
50
|
+
socket_factory: @config.socket_factory
|
53
51
|
}
|
54
52
|
log_connection_started
|
55
53
|
@es = SSE::Client.new(@config.stream_uri + "/all", **opts) do |conn|
|
@@ -87,10 +85,8 @@ module LaunchDarkly
|
|
87
85
|
@config.logger.debug { "[LDClient] Stream received #{method} message: #{message.data}" }
|
88
86
|
if method == PUT
|
89
87
|
message = JSON.parse(message.data, symbolize_names: true)
|
90
|
-
|
91
|
-
|
92
|
-
SEGMENTS => message[:data][:segments]
|
93
|
-
})
|
88
|
+
all_data = Impl::Model.make_all_store_data(message[:data])
|
89
|
+
@feature_store.init(all_data)
|
94
90
|
@initialized.make_true
|
95
91
|
@config.logger.info { "[LDClient] Stream initialized" }
|
96
92
|
@ready.set
|
@@ -99,7 +95,9 @@ module LaunchDarkly
|
|
99
95
|
for kind in [FEATURES, SEGMENTS]
|
100
96
|
key = key_for_path(kind, data[:path])
|
101
97
|
if key
|
102
|
-
|
98
|
+
data = data[:data]
|
99
|
+
Impl::Model.postprocess_item_after_deserializing!(kind, data)
|
100
|
+
@feature_store.upsert(kind, data)
|
103
101
|
break
|
104
102
|
end
|
105
103
|
end
|
@@ -112,24 +110,6 @@ module LaunchDarkly
|
|
112
110
|
break
|
113
111
|
end
|
114
112
|
end
|
115
|
-
elsif method == INDIRECT_PUT
|
116
|
-
all_data = @requestor.request_all_data
|
117
|
-
@feature_store.init({
|
118
|
-
FEATURES => all_data[:flags],
|
119
|
-
SEGMENTS => all_data[:segments]
|
120
|
-
})
|
121
|
-
@initialized.make_true
|
122
|
-
@config.logger.info { "[LDClient] Stream initialized (via indirect message)" }
|
123
|
-
elsif method == INDIRECT_PATCH
|
124
|
-
key = key_for_path(FEATURES, message.data)
|
125
|
-
if key
|
126
|
-
@feature_store.upsert(FEATURES, @requestor.request_flag(key))
|
127
|
-
else
|
128
|
-
key = key_for_path(SEGMENTS, message.data)
|
129
|
-
if key
|
130
|
-
@feature_store.upsert(SEGMENTS, @requestor.request_segment(key))
|
131
|
-
end
|
132
|
-
end
|
133
113
|
else
|
134
114
|
@config.logger.warn { "[LDClient] Unknown message received: #{method}" }
|
135
115
|
end
|
data/lib/ldclient-rb/util.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require "net/http"
|
2
1
|
require "uri"
|
2
|
+
require "http"
|
3
3
|
|
4
4
|
module LaunchDarkly
|
5
5
|
# @private
|
@@ -18,14 +18,18 @@ module LaunchDarkly
|
|
18
18
|
end
|
19
19
|
ret
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
def self.new_http_client(uri_s, config)
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
23
|
+
http_client_options = {}
|
24
|
+
if config.socket_factory
|
25
|
+
http_client_options["socket_class"] = config.socket_factory
|
26
|
+
end
|
27
|
+
return HTTP::Client.new(http_client_options)
|
28
|
+
.timeout({
|
29
|
+
read: config.read_timeout,
|
30
|
+
connect: config.connect_timeout
|
31
|
+
})
|
32
|
+
.persistent(uri_s)
|
29
33
|
end
|
30
34
|
|
31
35
|
def self.log_exception(logger, message, exc)
|
data/lib/ldclient-rb/version.rb
CHANGED
@@ -0,0 +1,135 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module LaunchDarkly
|
4
|
+
describe "EvaluationDetail" do
|
5
|
+
subject { EvaluationDetail }
|
6
|
+
|
7
|
+
it "sets properties" do
|
8
|
+
expect(EvaluationDetail.new("x", 0, EvaluationReason::off).value).to eq "x"
|
9
|
+
expect(EvaluationDetail.new("x", 0, EvaluationReason::off).variation_index).to eq 0
|
10
|
+
expect(EvaluationDetail.new("x", 0, EvaluationReason::off).reason).to eq EvaluationReason::off
|
11
|
+
end
|
12
|
+
|
13
|
+
it "checks parameter types" do
|
14
|
+
expect { EvaluationDetail.new(nil, nil, EvaluationReason::off) }.not_to raise_error
|
15
|
+
expect { EvaluationDetail.new(nil, 0, EvaluationReason::off) }.not_to raise_error
|
16
|
+
expect { EvaluationDetail.new(nil, "x", EvaluationReason::off) }.to raise_error(ArgumentError)
|
17
|
+
expect { EvaluationDetail.new(nil, 0, { kind: "OFF" }) }.to raise_error(ArgumentError)
|
18
|
+
expect { EvaluationDetail.new(nil, 0, nil) }.to raise_error(ArgumentError)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "equality test" do
|
22
|
+
expect(EvaluationDetail.new("x", 0, EvaluationReason::off)).to eq EvaluationDetail.new("x", 0, EvaluationReason::off)
|
23
|
+
expect(EvaluationDetail.new("x", 0, EvaluationReason::off)).not_to eq EvaluationDetail.new("y", 0, EvaluationReason::off)
|
24
|
+
expect(EvaluationDetail.new("x", 0, EvaluationReason::off)).not_to eq EvaluationDetail.new("x", 1, EvaluationReason::off)
|
25
|
+
expect(EvaluationDetail.new("x", 0, EvaluationReason::off)).not_to eq EvaluationDetail.new("x", 0, EvaluationReason::fallthrough)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "EvaluationReason" do
|
30
|
+
subject { EvaluationReason }
|
31
|
+
|
32
|
+
values = [
|
33
|
+
[ EvaluationReason::off, EvaluationReason::OFF, { "kind" => "OFF" }, "OFF", nil ],
|
34
|
+
[ EvaluationReason::fallthrough, EvaluationReason::FALLTHROUGH,
|
35
|
+
{ "kind" => "FALLTHROUGH" }, "FALLTHROUGH", nil ],
|
36
|
+
[ EvaluationReason::target_match, EvaluationReason::TARGET_MATCH,
|
37
|
+
{ "kind" => "TARGET_MATCH" }, "TARGET_MATCH", nil ],
|
38
|
+
[ EvaluationReason::rule_match(1, "x"), EvaluationReason::RULE_MATCH,
|
39
|
+
{ "kind" => "RULE_MATCH", "ruleIndex" => 1, "ruleId" => "x" }, "RULE_MATCH(1,x)",
|
40
|
+
[ EvaluationReason::rule_match(2, "x"), EvaluationReason::rule_match(1, "y") ] ],
|
41
|
+
[ EvaluationReason::prerequisite_failed("x"), EvaluationReason::PREREQUISITE_FAILED,
|
42
|
+
{ "kind" => "PREREQUISITE_FAILED", "prerequisiteKey" => "x" }, "PREREQUISITE_FAILED(x)" ],
|
43
|
+
[ EvaluationReason::error(EvaluationReason::ERROR_FLAG_NOT_FOUND), EvaluationReason::ERROR,
|
44
|
+
{ "kind" => "ERROR", "errorKind" => "FLAG_NOT_FOUND" }, "ERROR(FLAG_NOT_FOUND)" ]
|
45
|
+
]
|
46
|
+
values.each_index do |i|
|
47
|
+
params = values[i]
|
48
|
+
reason = params[0]
|
49
|
+
kind = params[1]
|
50
|
+
json_rep = params[2]
|
51
|
+
brief_str = params[3]
|
52
|
+
unequal_values = params[4]
|
53
|
+
|
54
|
+
describe "reason #{reason.kind}" do
|
55
|
+
it "has correct kind" do
|
56
|
+
expect(reason.kind).to eq kind
|
57
|
+
end
|
58
|
+
|
59
|
+
it "equality to self" do
|
60
|
+
expect(reason).to eq reason
|
61
|
+
end
|
62
|
+
|
63
|
+
it "inequality to others" do
|
64
|
+
values.each_index do |j|
|
65
|
+
if i != j
|
66
|
+
expect(reason).not_to eq values[j][0]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
if !unequal_values.nil?
|
70
|
+
unequal_values.each do |v|
|
71
|
+
expect(reason).not_to eq v
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
it "JSON representation" do
|
77
|
+
expect(JSON.parse(reason.as_json.to_json)).to eq json_rep
|
78
|
+
expect(JSON.parse(reason.to_json)).to eq json_rep
|
79
|
+
end
|
80
|
+
|
81
|
+
it "brief representation" do
|
82
|
+
expect(reason.inspect).to eq brief_str
|
83
|
+
expect(reason.to_s).to eq brief_str
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
it "reuses singleton reasons" do
|
89
|
+
expect(EvaluationReason::off).to be EvaluationReason::off
|
90
|
+
expect(EvaluationReason::fallthrough).to be EvaluationReason::fallthrough
|
91
|
+
expect(EvaluationReason::target_match).to be EvaluationReason::target_match
|
92
|
+
expect(EvaluationReason::rule_match(1, 'x')).not_to be EvaluationReason::rule_match(1, 'x')
|
93
|
+
expect(EvaluationReason::prerequisite_failed('x')).not_to be EvaluationReason::prerequisite_failed('x')
|
94
|
+
errors = [ EvaluationReason::ERROR_CLIENT_NOT_READY, EvaluationReason::ERROR_FLAG_NOT_FOUND,
|
95
|
+
EvaluationReason::ERROR_MALFORMED_FLAG, EvaluationReason::ERROR_USER_NOT_SPECIFIED, EvaluationReason::ERROR_EXCEPTION ]
|
96
|
+
errors.each do |e|
|
97
|
+
expect(EvaluationReason::error(e)).to be EvaluationReason::error(e)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
it "supports [] with JSON property names" do
|
102
|
+
expect(EvaluationReason::off[:kind]).to eq "OFF"
|
103
|
+
expect(EvaluationReason::off[:ruleIndex]).to be nil
|
104
|
+
expect(EvaluationReason::off[:ruleId]).to be nil
|
105
|
+
expect(EvaluationReason::off[:prerequisiteKey]).to be nil
|
106
|
+
expect(EvaluationReason::off[:errorKind]).to be nil
|
107
|
+
expect(EvaluationReason::rule_match(1, "x")[:ruleIndex]).to eq 1
|
108
|
+
expect(EvaluationReason::rule_match(1, "x")[:ruleId]).to eq "x"
|
109
|
+
expect(EvaluationReason::prerequisite_failed("x")[:prerequisiteKey]).to eq "x"
|
110
|
+
expect(EvaluationReason::error(EvaluationReason::ERROR_FLAG_NOT_FOUND)[:errorKind]).to eq "FLAG_NOT_FOUND"
|
111
|
+
end
|
112
|
+
|
113
|
+
it "freezes string properties" do
|
114
|
+
rm = EvaluationReason::rule_match(1, "x")
|
115
|
+
expect { rm.rule_id.upcase! }.to raise_error(RuntimeError)
|
116
|
+
pf = EvaluationReason::prerequisite_failed("x")
|
117
|
+
expect { pf.prerequisite_key.upcase! }.to raise_error(RuntimeError)
|
118
|
+
end
|
119
|
+
|
120
|
+
it "checks parameter types" do
|
121
|
+
expect { EvaluationReason::rule_match(nil, "x") }.to raise_error(ArgumentError)
|
122
|
+
expect { EvaluationReason::rule_match(true, "x") }.to raise_error(ArgumentError)
|
123
|
+
expect { EvaluationReason::rule_match(1, nil) }.not_to raise_error # we allow nil rule_id for backward compatibility
|
124
|
+
expect { EvaluationReason::rule_match(1, 9) }.to raise_error(ArgumentError)
|
125
|
+
expect { EvaluationReason::prerequisite_failed(nil) }.to raise_error(ArgumentError)
|
126
|
+
expect { EvaluationReason::prerequisite_failed(9) }.to raise_error(ArgumentError)
|
127
|
+
expect { EvaluationReason::error(nil) }.to raise_error(ArgumentError)
|
128
|
+
expect { EvaluationReason::error(9) }.to raise_error(ArgumentError)
|
129
|
+
end
|
130
|
+
|
131
|
+
it "does not allow direct access to constructor" do
|
132
|
+
expect { EvaluationReason.new(:off, nil, nil, nil, nil) }.to raise_error(NoMethodError)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
data/spec/event_sender_spec.rb
CHANGED
@@ -39,12 +39,29 @@ module LaunchDarkly
|
|
39
39
|
"authorization" => [ sdk_key ],
|
40
40
|
"content-type" => [ "application/json" ],
|
41
41
|
"user-agent" => [ "RubyClient/" + LaunchDarkly::VERSION ],
|
42
|
-
"x-launchdarkly-event-schema" => [ "3" ]
|
42
|
+
"x-launchdarkly-event-schema" => [ "3" ],
|
43
|
+
"connection" => [ "Keep-Alive" ]
|
43
44
|
})
|
44
45
|
expect(req.header['x-launchdarkly-payload-id']).not_to eq []
|
45
46
|
end
|
46
47
|
end
|
47
|
-
|
48
|
+
|
49
|
+
it "can use a socket factory" do
|
50
|
+
with_server do |server|
|
51
|
+
server.setup_ok_response("/bulk", "")
|
52
|
+
|
53
|
+
config = Config.new(events_uri: "http://events.com/bulk", socket_factory: SocketFactoryFromHash.new({"events.com" => server.port}), logger: $null_log)
|
54
|
+
es = subject.new(sdk_key, config, nil, 0.1)
|
55
|
+
|
56
|
+
result = es.send_event_data(fake_data, "", false)
|
57
|
+
|
58
|
+
expect(result.success).to be true
|
59
|
+
req = server.await_request
|
60
|
+
expect(req.body).to eq fake_data
|
61
|
+
expect(req.host).to eq "events.com"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
48
65
|
it "generates a new payload ID for each payload" do
|
49
66
|
with_sender_and_server do |es, server|
|
50
67
|
server.setup_ok_response("/bulk", "")
|
@@ -78,6 +95,7 @@ module LaunchDarkly
|
|
78
95
|
"authorization" => [ sdk_key ],
|
79
96
|
"content-type" => [ "application/json" ],
|
80
97
|
"user-agent" => [ "RubyClient/" + LaunchDarkly::VERSION ],
|
98
|
+
"connection" => [ "Keep-Alive" ]
|
81
99
|
})
|
82
100
|
expect(req.header['x-launchdarkly-event-schema']).to eq []
|
83
101
|
expect(req.header['x-launchdarkly-payload-id']).to eq []
|
data/spec/events_spec.rb
CHANGED
@@ -408,6 +408,16 @@ describe LaunchDarkly::EventProcessor do
|
|
408
408
|
end
|
409
409
|
end
|
410
410
|
|
411
|
+
it "queues alias event" do
|
412
|
+
with_processor_and_sender(default_config) do |ep, sender|
|
413
|
+
e = { kind: "alias", key: "a", contextKind: "user", previousKey: "b", previousContextKind: "user" }
|
414
|
+
ep.add_event(e)
|
415
|
+
|
416
|
+
output = flush_and_get_events(ep, sender)
|
417
|
+
expect(output).to contain_exactly(e)
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
411
421
|
it "treats nil value for custom the same as an empty hash" do
|
412
422
|
with_processor_and_sender(default_config) do |ep, sender|
|
413
423
|
user_with_nil_custom = { key: "userkey", custom: nil }
|
data/spec/http_util.rb
CHANGED
@@ -3,7 +3,7 @@ require "webrick/httpproxy"
|
|
3
3
|
require "webrick/https"
|
4
4
|
|
5
5
|
class StubHTTPServer
|
6
|
-
attr_reader :requests
|
6
|
+
attr_reader :requests, :port
|
7
7
|
|
8
8
|
@@next_port = 50000
|
9
9
|
|
@@ -120,3 +120,13 @@ def with_server(server = nil)
|
|
120
120
|
server.stop
|
121
121
|
end
|
122
122
|
end
|
123
|
+
|
124
|
+
class SocketFactoryFromHash
|
125
|
+
def initialize(ports = {})
|
126
|
+
@ports = ports
|
127
|
+
end
|
128
|
+
|
129
|
+
def open(uri, timeout)
|
130
|
+
TCPSocket.new 'localhost', @ports[uri]
|
131
|
+
end
|
132
|
+
end
|