launchdarkly-server-sdk 5.7.1 → 5.8.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -0
- data/Gemfile.lock +45 -56
- data/launchdarkly-server-sdk.gemspec +10 -4
- data/lib/ldclient-rb/evaluation.rb +17 -8
- data/lib/ldclient-rb/events.rb +3 -2
- data/lib/ldclient-rb/impl/event_sender.rb +2 -3
- data/lib/ldclient-rb/impl/integrations/redis_impl.rb +3 -0
- data/lib/ldclient-rb/integrations/redis.rb +3 -0
- data/lib/ldclient-rb/ldclient.rb +14 -3
- data/lib/ldclient-rb/redis_store.rb +1 -0
- data/lib/ldclient-rb/requestor.rb +0 -8
- data/lib/ldclient-rb/stream.rb +1 -24
- data/lib/ldclient-rb/user_filter.rb +3 -2
- data/lib/ldclient-rb/version.rb +1 -1
- data/spec/evaluation_spec.rb +6 -6
- data/spec/event_sender_spec.rb +8 -8
- data/spec/events_spec.rb +12 -1
- data/spec/ldclient_end_to_end_spec.rb +123 -0
- data/spec/ldclient_spec.rb +40 -0
- data/spec/redis_feature_store_spec.rb +33 -4
- data/spec/requestor_spec.rb +0 -34
- data/spec/spec_helper.rb +0 -3
- data/spec/stream_spec.rb +1 -16
- metadata +40 -32
- data/ext/mkrf_conf.rb +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dcb72b5ad43bf4a2f4250cbe1b12fb3eaf803e66
|
4
|
+
data.tar.gz: bd7ceebb0ff2a746d8b773fbeb7b3e287dcbcb01
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4f3d71bf78a2cc31574470fd57af2eee84b5222dfaddfe90160ecd2bda746e5137df3fed136d12578b01ae84364f2fd04477046964bc8f73e5bced212988c59a
|
7
|
+
data.tar.gz: 165bb1c1b646ca0f759bb081020a16fd78b5df014be47cb9c5386f7e99b2287dfb640b7e865230e7604886dd3ad933ebae3d82b113bce21f549e03e2875c7d03
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,31 @@
|
|
2
2
|
|
3
3
|
All notable changes to the LaunchDarkly Ruby SDK will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org).
|
4
4
|
|
5
|
+
## [5.8.0] - 2020-05-27
|
6
|
+
### Added:
|
7
|
+
- In `LaunchDarkly::Integrations::Redis::new_feature_store`, if you pass in an externally created `pool`, you can now set the new option `pool_shutdown_on_close` to `false` to indicate that the SDK should _not_ shut down this pool if the SDK is shut down. The default behavior, as before, is that it will be shut down. (Thanks, [jacobthemyth](https://github.com/launchdarkly/ruby-server-sdk/pull/158)!)
|
8
|
+
|
9
|
+
## [5.7.4] - 2020-05-04
|
10
|
+
### Fixed:
|
11
|
+
- Setting a user's `custom` property explicitly to `nil`, rather than omitting it entirely or setting it to an empty hash, would cause the SDK to log an error and drop the current batch of analytics events. Now, it will be treated the same as an empty hash. ([#147](https://github.com/launchdarkly/ruby-server-sdk/issues/147))
|
12
|
+
|
13
|
+
## [5.7.3] - 2020-04-27
|
14
|
+
### Changed:
|
15
|
+
- Previously, installing the SDK in an environment that did not have `openssl` would cause a failure at build time. The SDK still requires `openssl` at runtime, but this check has been removed because it caused the `rake` problem mentioned below, and because `openssl` is normally bundled in modern Ruby versions.
|
16
|
+
|
17
|
+
### Fixed:
|
18
|
+
- The `LDClient` constructor will fail immediately with a descriptive `ArgumentError` if you provide a `nil` SDK key in a configuration that requires an SDK key (that is, a configuration that _will_ require communicating with LaunchDarkly services). Previously, it would still fail, but without a clear error message. You are still allowed to omit the SDK key in an offline configuration. ([#154](https://github.com/launchdarkly/ruby-server-sdk/issues/154))
|
19
|
+
- Removed a hidden dependency on `rake` which could cause your build to fail if you had a dependency on this SDK and you did not have `rake` installed. ([#155](https://github.com/launchdarkly/ruby-server-sdk/issues/155))
|
20
|
+
- Previously a clause in a feature flag rule that used a string operator (such as "starts with") or a numeric operator (such as "greater than") could cause evaluation of the flag to completely fail and return a default value if the value on the right-hand side of the expression did not have the right data type-- for instance, "greater than" with a string value. The LaunchDarkly dashboard does not allow creation of such a rule, but it might be possible to do so via the REST API; the correct behavior of the SDK is to simply treat the expression as a non-match.
|
21
|
+
|
22
|
+
## [5.7.2] - 2020-03-27
|
23
|
+
### Fixed:
|
24
|
+
- Fixed a bug in the 5.7.0 and 5.7.1 releases that caused analytics events not to be sent unless diagnostic events were explicitly disabled. This also caused an error to be logged: `undefined method started?`.
|
25
|
+
|
26
|
+
## [5.7.1] - 2020-03-18
|
27
|
+
### Fixed:
|
28
|
+
- The backoff delay logic for reconnecting after a stream failure was broken so that if a failure occurred after a stream had been active for at least 60 seconds, retries would use _no_ delay, potentially causing a flood of requests and a spike in CPU usage. This bug was introduced in version 5.5.0 of the SDK.
|
29
|
+
|
5
30
|
## [5.7.0] - 2020-03-10
|
6
31
|
### Added:
|
7
32
|
- The SDK now periodically sends diagnostic data to LaunchDarkly, describing the version and configuration of the SDK, the architecture and version of the runtime platform, and performance statistics. No credentials, hostnames, or other identifiable values are included. This behavior can be disabled with `Config.diagnostic_opt_out` or configured with `Config.diagnostic_recording_interval`.
|
data/Gemfile.lock
CHANGED
@@ -1,94 +1,83 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
launchdarkly-server-sdk (5.
|
4
|
+
launchdarkly-server-sdk (5.8.1)
|
5
5
|
concurrent-ruby (~> 1.0)
|
6
|
-
json (
|
6
|
+
json (~> 2.3.1)
|
7
7
|
ld-eventsource (= 1.0.3)
|
8
8
|
semantic (~> 1.6)
|
9
9
|
|
10
10
|
GEM
|
11
11
|
remote: https://rubygems.org/
|
12
12
|
specs:
|
13
|
-
aws-eventstream (1.0
|
14
|
-
aws-partitions (1.
|
15
|
-
aws-sdk-core (3.
|
16
|
-
aws-eventstream (~> 1.0)
|
17
|
-
aws-partitions (~> 1.0)
|
18
|
-
aws-sigv4 (~> 1.
|
13
|
+
aws-eventstream (1.1.0)
|
14
|
+
aws-partitions (1.388.0)
|
15
|
+
aws-sdk-core (3.109.1)
|
16
|
+
aws-eventstream (~> 1, >= 1.0.2)
|
17
|
+
aws-partitions (~> 1, >= 1.239.0)
|
18
|
+
aws-sigv4 (~> 1.1)
|
19
19
|
jmespath (~> 1.0)
|
20
|
-
aws-sdk-dynamodb (1.
|
21
|
-
aws-sdk-core (~> 3, >= 3.
|
22
|
-
aws-sigv4 (~> 1.
|
23
|
-
aws-sigv4 (1.
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
diff-lcs (1.
|
29
|
-
diplomat (2.
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
faraday (0.15.4)
|
20
|
+
aws-sdk-dynamodb (1.55.0)
|
21
|
+
aws-sdk-core (~> 3, >= 3.109.0)
|
22
|
+
aws-sigv4 (~> 1.1)
|
23
|
+
aws-sigv4 (1.2.2)
|
24
|
+
aws-eventstream (~> 1, >= 1.0.2)
|
25
|
+
concurrent-ruby (1.1.7)
|
26
|
+
connection_pool (2.2.3)
|
27
|
+
deep_merge (1.2.1)
|
28
|
+
diff-lcs (1.4.4)
|
29
|
+
diplomat (2.4.2)
|
30
|
+
deep_merge (~> 1.0, >= 1.0.1)
|
31
|
+
faraday (>= 0.9, < 1.1.0)
|
32
|
+
faraday (0.17.3)
|
34
33
|
multipart-post (>= 1.2, < 3)
|
35
|
-
ffi (1.
|
36
|
-
ffi (1.9.25-java)
|
34
|
+
ffi (1.12.0)
|
37
35
|
hitimes (1.3.1)
|
38
|
-
hitimes (1.3.1-java)
|
39
36
|
http_tools (0.4.5)
|
40
37
|
jmespath (1.4.0)
|
41
|
-
json (
|
42
|
-
json (1.8.6-java)
|
38
|
+
json (2.3.1)
|
43
39
|
ld-eventsource (1.0.3)
|
44
40
|
concurrent-ruby (~> 1.0)
|
45
41
|
http_tools (~> 0.4.5)
|
46
42
|
socketry (~> 0.5.1)
|
47
|
-
listen (3.1
|
48
|
-
rb-fsevent (~> 0.
|
49
|
-
rb-inotify (~> 0.9, >= 0.9.
|
50
|
-
|
51
|
-
|
52
|
-
rb-
|
53
|
-
|
54
|
-
ffi (>= 0.5.0, < 2)
|
43
|
+
listen (3.2.1)
|
44
|
+
rb-fsevent (~> 0.10, >= 0.10.3)
|
45
|
+
rb-inotify (~> 0.9, >= 0.9.10)
|
46
|
+
multipart-post (2.1.1)
|
47
|
+
rb-fsevent (0.10.4)
|
48
|
+
rb-inotify (0.10.1)
|
49
|
+
ffi (~> 1.0)
|
55
50
|
redis (3.3.5)
|
56
|
-
rspec (3.
|
57
|
-
rspec-core (~> 3.
|
58
|
-
rspec-expectations (~> 3.
|
59
|
-
rspec-mocks (~> 3.
|
60
|
-
rspec-core (3.
|
61
|
-
rspec-support (~> 3.
|
62
|
-
rspec-expectations (3.
|
51
|
+
rspec (3.9.0)
|
52
|
+
rspec-core (~> 3.9.0)
|
53
|
+
rspec-expectations (~> 3.9.0)
|
54
|
+
rspec-mocks (~> 3.9.0)
|
55
|
+
rspec-core (3.9.3)
|
56
|
+
rspec-support (~> 3.9.3)
|
57
|
+
rspec-expectations (3.9.3)
|
63
58
|
diff-lcs (>= 1.2.0, < 2.0)
|
64
|
-
rspec-support (~> 3.
|
65
|
-
rspec-mocks (3.
|
59
|
+
rspec-support (~> 3.9.0)
|
60
|
+
rspec-mocks (3.9.1)
|
66
61
|
diff-lcs (>= 1.2.0, < 2.0)
|
67
|
-
rspec-support (~> 3.
|
68
|
-
rspec-support (3.
|
62
|
+
rspec-support (~> 3.9.0)
|
63
|
+
rspec-support (3.9.4)
|
69
64
|
rspec_junit_formatter (0.3.0)
|
70
65
|
rspec-core (>= 2, < 4, != 2.12.0)
|
71
|
-
ruby_dep (1.5.0)
|
72
66
|
semantic (1.6.1)
|
73
|
-
simplecov (0.15.1)
|
74
|
-
docile (~> 1.1.0)
|
75
|
-
json (>= 1.8, < 3)
|
76
|
-
simplecov-html (~> 0.10.0)
|
77
|
-
simplecov-html (0.10.2)
|
78
67
|
socketry (0.5.1)
|
79
68
|
hitimes (~> 1.2)
|
80
|
-
timecop (0.9.
|
69
|
+
timecop (0.9.2)
|
81
70
|
|
82
71
|
PLATFORMS
|
83
|
-
java
|
84
72
|
ruby
|
85
73
|
|
86
74
|
DEPENDENCIES
|
87
75
|
aws-sdk-dynamodb (~> 1.18)
|
88
|
-
bundler (~> 1.
|
89
|
-
codeclimate-test-reporter (~> 0)
|
76
|
+
bundler (~> 1.17)
|
90
77
|
connection_pool (>= 2.1.2)
|
91
78
|
diplomat (>= 2.0.2)
|
79
|
+
faraday (~> 0.17)
|
80
|
+
ffi (<= 1.12)
|
92
81
|
launchdarkly-server-sdk!
|
93
82
|
listen (~> 3.0)
|
94
83
|
redis (~> 3.3.5)
|
@@ -19,21 +19,27 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
20
20
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
21
21
|
spec.require_paths = ["lib"]
|
22
|
-
spec.extensions = 'ext/mkrf_conf.rb'
|
23
22
|
|
24
23
|
spec.add_development_dependency "aws-sdk-dynamodb", "~> 1.18"
|
25
|
-
spec.add_development_dependency "bundler", "~> 1.
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.17"
|
26
25
|
spec.add_development_dependency "rspec", "~> 3.2"
|
27
|
-
spec.add_development_dependency "codeclimate-test-reporter", "~> 0"
|
28
26
|
spec.add_development_dependency "diplomat", ">= 2.0.2"
|
29
27
|
spec.add_development_dependency "redis", "~> 3.3.5"
|
30
28
|
spec.add_development_dependency "connection_pool", ">= 2.1.2"
|
31
29
|
spec.add_development_dependency "rspec_junit_formatter", "~> 0.3.0"
|
32
30
|
spec.add_development_dependency "timecop", "~> 0.9.1"
|
33
31
|
spec.add_development_dependency "listen", "~> 3.0" # see file_data_source.rb
|
32
|
+
# these are transitive dependencies of listen and consul respectively
|
33
|
+
# we constrain them here to make sure the ruby 2.2, 2.3, and 2.4 CI
|
34
|
+
# cases all pass
|
35
|
+
spec.add_development_dependency "ffi", "<= 1.12" # >1.12 doesnt support ruby 2.2
|
36
|
+
spec.add_development_dependency "faraday", "~> 0.17" # >=0.18 doesnt support ruby 2.2
|
34
37
|
|
35
|
-
spec.add_runtime_dependency "json", [">= 1.8", "< 3"]
|
36
38
|
spec.add_runtime_dependency "semantic", "~> 1.6"
|
37
39
|
spec.add_runtime_dependency "concurrent-ruby", "~> 1.0"
|
38
40
|
spec.add_runtime_dependency "ld-eventsource", "1.0.3"
|
41
|
+
|
42
|
+
# lock json to 2.3.x as ruby libraries often remove
|
43
|
+
# support for older ruby versions in minor releases
|
44
|
+
spec.add_runtime_dependency "json", "~> 2.3.1"
|
39
45
|
end
|
@@ -140,35 +140,44 @@ module LaunchDarkly
|
|
140
140
|
end,
|
141
141
|
endsWith:
|
142
142
|
lambda do |a, b|
|
143
|
-
(a.is_a? String) && (a.end_with? b)
|
143
|
+
(a.is_a? String) && (b.is_a? String) && (a.end_with? b)
|
144
144
|
end,
|
145
145
|
startsWith:
|
146
146
|
lambda do |a, b|
|
147
|
-
(a.is_a? String) && (a.start_with? b)
|
147
|
+
(a.is_a? String) && (b.is_a? String) && (a.start_with? b)
|
148
148
|
end,
|
149
149
|
matches:
|
150
150
|
lambda do |a, b|
|
151
|
-
(b.is_a? String) &&
|
151
|
+
if (b.is_a? String) && (b.is_a? String)
|
152
|
+
begin
|
153
|
+
re = Regexp.new b
|
154
|
+
!re.match(a).nil?
|
155
|
+
rescue
|
156
|
+
false
|
157
|
+
end
|
158
|
+
else
|
159
|
+
false
|
160
|
+
end
|
152
161
|
end,
|
153
162
|
contains:
|
154
163
|
lambda do |a, b|
|
155
|
-
(a.is_a? String) && (a.include? b)
|
164
|
+
(a.is_a? String) && (b.is_a? String) && (a.include? b)
|
156
165
|
end,
|
157
166
|
lessThan:
|
158
167
|
lambda do |a, b|
|
159
|
-
(a.is_a? Numeric) && (a < b)
|
168
|
+
(a.is_a? Numeric) && (b.is_a? Numeric) && (a < b)
|
160
169
|
end,
|
161
170
|
lessThanOrEqual:
|
162
171
|
lambda do |a, b|
|
163
|
-
(a.is_a? Numeric) && (a <= b)
|
172
|
+
(a.is_a? Numeric) && (b.is_a? Numeric) && (a <= b)
|
164
173
|
end,
|
165
174
|
greaterThan:
|
166
175
|
lambda do |a, b|
|
167
|
-
(a.is_a? Numeric) && (a > b)
|
176
|
+
(a.is_a? Numeric) && (b.is_a? Numeric) && (a > b)
|
168
177
|
end,
|
169
178
|
greaterThanOrEqual:
|
170
179
|
lambda do |a, b|
|
171
|
-
(a.is_a? Numeric) && (a >= b)
|
180
|
+
(a.is_a? Numeric) && (b.is_a? Numeric) && (a >= b)
|
172
181
|
end,
|
173
182
|
before:
|
174
183
|
comparator(DATE_OPERAND) { |n| n < 0 },
|
data/lib/ldclient-rb/events.rb
CHANGED
@@ -91,6 +91,7 @@ module LaunchDarkly
|
|
91
91
|
# @private
|
92
92
|
class EventProcessor
|
93
93
|
def initialize(sdk_key, config, client = nil, diagnostic_accumulator = nil, test_properties = nil)
|
94
|
+
raise ArgumentError, "sdk_key must not be nil" if sdk_key.nil? # see LDClient constructor comment on sdk_key
|
94
95
|
@logger = config.logger
|
95
96
|
@inbox = SizedQueue.new(config.capacity < 100 ? 100 : config.capacity)
|
96
97
|
@flush_task = Concurrent::TimerTask.new(execution_interval: config.flush_interval) do
|
@@ -319,7 +320,7 @@ module LaunchDarkly
|
|
319
320
|
success = flush_workers.post do
|
320
321
|
begin
|
321
322
|
events_out = @formatter.make_output_events(payload.events, payload.summary)
|
322
|
-
result = @event_sender.send_event_data(events_out.to_json, false)
|
323
|
+
result = @event_sender.send_event_data(events_out.to_json, "#{events_out.length} events", false)
|
323
324
|
@disabled.value = true if result.must_shutdown
|
324
325
|
if !result.time_from_server.nil?
|
325
326
|
@last_known_past_time.value = (result.time_from_server.to_f * 1000).to_i
|
@@ -348,7 +349,7 @@ module LaunchDarkly
|
|
348
349
|
uri = URI(@config.events_uri + "/diagnostic")
|
349
350
|
diagnostic_event_workers.post do
|
350
351
|
begin
|
351
|
-
@event_sender.send_event_data(event.to_json, true)
|
352
|
+
@event_sender.send_event_data(event.to_json, "diagnostic event", true)
|
352
353
|
rescue => e
|
353
354
|
Util.log_exception(@config.logger, "Unexpected error in event processor", e)
|
354
355
|
end
|
@@ -18,10 +18,9 @@ module LaunchDarkly
|
|
18
18
|
@retry_interval = retry_interval
|
19
19
|
end
|
20
20
|
|
21
|
-
def send_event_data(event_data, is_diagnostic)
|
21
|
+
def send_event_data(event_data, description, is_diagnostic)
|
22
22
|
uri = is_diagnostic ? @diagnostic_uri : @events_uri
|
23
23
|
payload_id = is_diagnostic ? nil : SecureRandom.uuid
|
24
|
-
description = is_diagnostic ? 'diagnostic event' : "#{event_data.length} events"
|
25
24
|
res = nil
|
26
25
|
(0..1).each do |attempt|
|
27
26
|
if attempt > 0
|
@@ -30,7 +29,7 @@ module LaunchDarkly
|
|
30
29
|
end
|
31
30
|
begin
|
32
31
|
@client.start if !@client.started?
|
33
|
-
@logger.debug { "[LDClient] sending #{description}: #{
|
32
|
+
@logger.debug { "[LDClient] sending #{description}: #{event_data}" }
|
34
33
|
req = Net::HTTP::Post.new(uri)
|
35
34
|
req.content_type = "application/json"
|
36
35
|
req.body = event_data
|
@@ -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
|
@@ -118,6 +120,7 @@ module LaunchDarkly
|
|
118
120
|
|
119
121
|
def stop
|
120
122
|
if @stopped.make_true
|
123
|
+
return unless @pool_shutdown_on_close
|
121
124
|
@pool.shutdown { |redis| redis.close }
|
122
125
|
end
|
123
126
|
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
@@ -33,6 +33,16 @@ module LaunchDarkly
|
|
33
33
|
# @return [LDClient] The LaunchDarkly client instance
|
34
34
|
#
|
35
35
|
def initialize(sdk_key, config = Config.default, wait_for_sec = 5)
|
36
|
+
# Note that sdk_key is normally a required parameter, and a nil value would cause the SDK to
|
37
|
+
# fail in most configurations. However, there are some configurations where it would be OK
|
38
|
+
# (offline = true, *or* we are using LDD mode or the file data source and events are disabled
|
39
|
+
# so we're not connecting to any LD services) so rather than try to check for all of those
|
40
|
+
# up front, we will let the constructors for the data source implementations implement this
|
41
|
+
# fail-fast as appropriate, and just check here for the part regarding events.
|
42
|
+
if !config.offline? && config.send_events
|
43
|
+
raise ArgumentError, "sdk_key must not be nil" if sdk_key.nil?
|
44
|
+
end
|
45
|
+
|
36
46
|
@sdk_key = sdk_key
|
37
47
|
|
38
48
|
@event_factory_default = EventFactory.new(false)
|
@@ -56,7 +66,7 @@ module LaunchDarkly
|
|
56
66
|
if @config.offline? || !@config.send_events
|
57
67
|
@event_processor = NullEventProcessor.new
|
58
68
|
else
|
59
|
-
@event_processor = EventProcessor.new(sdk_key, config, diagnostic_accumulator)
|
69
|
+
@event_processor = EventProcessor.new(sdk_key, config, nil, diagnostic_accumulator)
|
60
70
|
end
|
61
71
|
|
62
72
|
if @config.use_ldd?
|
@@ -352,12 +362,13 @@ module LaunchDarkly
|
|
352
362
|
if config.offline?
|
353
363
|
return NullUpdateProcessor.new
|
354
364
|
end
|
355
|
-
|
365
|
+
raise ArgumentError, "sdk_key must not be nil" if sdk_key.nil? # see LDClient constructor comment on sdk_key
|
356
366
|
if config.stream?
|
357
|
-
StreamProcessor.new(sdk_key, config,
|
367
|
+
StreamProcessor.new(sdk_key, config, diagnostic_accumulator)
|
358
368
|
else
|
359
369
|
config.logger.info { "Disabling streaming API" }
|
360
370
|
config.logger.warn { "You should only disable the streaming API if instructed to do so by LaunchDarkly support" }
|
371
|
+
requestor = Requestor.new(sdk_key, config)
|
361
372
|
PollingProcessor.new(config, requestor)
|
362
373
|
end
|
363
374
|
end
|
@@ -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)
|
@@ -26,14 +26,6 @@ module LaunchDarkly
|
|
26
26
|
@cache = @config.cache_store
|
27
27
|
end
|
28
28
|
|
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
29
|
def request_all_data()
|
38
30
|
make_request("/sdk/latest-all")
|
39
31
|
end
|
data/lib/ldclient-rb/stream.rb
CHANGED
@@ -10,10 +10,6 @@ module LaunchDarkly
|
|
10
10
|
# @private
|
11
11
|
DELETE = :delete
|
12
12
|
# @private
|
13
|
-
INDIRECT_PUT = :'indirect/put'
|
14
|
-
# @private
|
15
|
-
INDIRECT_PATCH = :'indirect/patch'
|
16
|
-
# @private
|
17
13
|
READ_TIMEOUT_SECONDS = 300 # 5 minutes; the stream should send a ping every 3 minutes
|
18
14
|
|
19
15
|
# @private
|
@@ -24,11 +20,10 @@ module LaunchDarkly
|
|
24
20
|
|
25
21
|
# @private
|
26
22
|
class StreamProcessor
|
27
|
-
def initialize(sdk_key, config,
|
23
|
+
def initialize(sdk_key, config, diagnostic_accumulator = nil)
|
28
24
|
@sdk_key = sdk_key
|
29
25
|
@config = config
|
30
26
|
@feature_store = config.feature_store
|
31
|
-
@requestor = requestor
|
32
27
|
@initialized = Concurrent::AtomicBoolean.new(false)
|
33
28
|
@started = Concurrent::AtomicBoolean.new(false)
|
34
29
|
@stopped = Concurrent::AtomicBoolean.new(false)
|
@@ -112,24 +107,6 @@ module LaunchDarkly
|
|
112
107
|
break
|
113
108
|
end
|
114
109
|
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
110
|
else
|
134
111
|
@config.logger.warn { "[LDClient] Unknown message received: #{method}" }
|
135
112
|
end
|
@@ -15,8 +15,9 @@ module LaunchDarkly
|
|
15
15
|
user_private_attrs = Set.new((user_props[:privateAttributeNames] || []).map(&:to_sym))
|
16
16
|
|
17
17
|
filtered_user_props, removed = filter_values(user_props, user_private_attrs, ALLOWED_TOP_LEVEL_KEYS, IGNORED_TOP_LEVEL_KEYS)
|
18
|
-
|
19
|
-
|
18
|
+
custom = user_props[:custom]
|
19
|
+
if !custom.nil?
|
20
|
+
filtered_user_props[:custom], removed_custom = filter_values(custom, user_private_attrs)
|
20
21
|
removed.merge(removed_custom)
|
21
22
|
end
|
22
23
|
|
data/lib/ldclient-rb/version.rb
CHANGED
data/spec/evaluation_spec.rb
CHANGED
@@ -495,13 +495,13 @@ describe LaunchDarkly::Evaluation do
|
|
495
495
|
# mixed strings and numbers
|
496
496
|
[ :in, "99", 99, false ],
|
497
497
|
[ :in, 99, "99", false ],
|
498
|
-
|
499
|
-
|
500
|
-
|
498
|
+
[ :contains, "99", 99, false ],
|
499
|
+
[ :startsWith, "99", 99, false ],
|
500
|
+
[ :endsWith, "99", 99, false ],
|
501
501
|
[ :lessThanOrEqual, "99", 99, false ],
|
502
|
-
|
502
|
+
[ :lessThanOrEqual, 99, "99", false ],
|
503
503
|
[ :greaterThanOrEqual, "99", 99, false ],
|
504
|
-
|
504
|
+
[ :greaterThanOrEqual, 99, "99", false ],
|
505
505
|
|
506
506
|
# regex
|
507
507
|
[ :matches, "hello world", "hello.*rld", true ],
|
@@ -509,7 +509,7 @@ describe LaunchDarkly::Evaluation do
|
|
509
509
|
[ :matches, "hello world", "l+", true ],
|
510
510
|
[ :matches, "hello world", "(world|planet)", true ],
|
511
511
|
[ :matches, "hello world", "aloha", false ],
|
512
|
-
|
512
|
+
[ :matches, "hello world", "***not a regex", false ],
|
513
513
|
|
514
514
|
# dates
|
515
515
|
[ :before, dateStr1, dateStr2, true ],
|
data/spec/event_sender_spec.rb
CHANGED
@@ -27,7 +27,7 @@ module LaunchDarkly
|
|
27
27
|
with_sender_and_server do |es, server|
|
28
28
|
server.setup_ok_response("/bulk", "")
|
29
29
|
|
30
|
-
result = es.send_event_data(fake_data, false)
|
30
|
+
result = es.send_event_data(fake_data, "", false)
|
31
31
|
|
32
32
|
expect(result.success).to be true
|
33
33
|
expect(result.must_shutdown).to be false
|
@@ -49,8 +49,8 @@ module LaunchDarkly
|
|
49
49
|
with_sender_and_server do |es, server|
|
50
50
|
server.setup_ok_response("/bulk", "")
|
51
51
|
|
52
|
-
result1 = es.send_event_data(fake_data, false)
|
53
|
-
result2 = es.send_event_data(fake_data, false)
|
52
|
+
result1 = es.send_event_data(fake_data, "", false)
|
53
|
+
result2 = es.send_event_data(fake_data, "", false)
|
54
54
|
expect(result1.success).to be true
|
55
55
|
expect(result2.success).to be true
|
56
56
|
|
@@ -66,7 +66,7 @@ module LaunchDarkly
|
|
66
66
|
with_sender_and_server do |es, server|
|
67
67
|
server.setup_ok_response("/diagnostic", "")
|
68
68
|
|
69
|
-
result = es.send_event_data(fake_data, true)
|
69
|
+
result = es.send_event_data(fake_data, "", true)
|
70
70
|
|
71
71
|
expect(result.success).to be true
|
72
72
|
expect(result.must_shutdown).to be false
|
@@ -94,7 +94,7 @@ module LaunchDarkly
|
|
94
94
|
|
95
95
|
es = make_sender(server)
|
96
96
|
|
97
|
-
result = es.send_event_data(fake_data, false)
|
97
|
+
result = es.send_event_data(fake_data, "", false)
|
98
98
|
|
99
99
|
expect(result.success).to be true
|
100
100
|
|
@@ -116,7 +116,7 @@ module LaunchDarkly
|
|
116
116
|
res.status = req_count == 2 ? 200 : status
|
117
117
|
end
|
118
118
|
|
119
|
-
result = es.send_event_data(fake_data, false)
|
119
|
+
result = es.send_event_data(fake_data, "", false)
|
120
120
|
|
121
121
|
expect(result.success).to be true
|
122
122
|
expect(result.must_shutdown).to be false
|
@@ -141,7 +141,7 @@ module LaunchDarkly
|
|
141
141
|
res.status = req_count == 3 ? 200 : status
|
142
142
|
end
|
143
143
|
|
144
|
-
result = es.send_event_data(fake_data, false)
|
144
|
+
result = es.send_event_data(fake_data, "", false)
|
145
145
|
|
146
146
|
expect(result.success).to be false
|
147
147
|
expect(result.must_shutdown).to be false
|
@@ -164,7 +164,7 @@ module LaunchDarkly
|
|
164
164
|
res.status = status
|
165
165
|
end
|
166
166
|
|
167
|
-
result = es.send_event_data(fake_data, false)
|
167
|
+
result = es.send_event_data(fake_data, "", false)
|
168
168
|
|
169
169
|
expect(result.success).to be false
|
170
170
|
expect(result.must_shutdown).to be true
|
data/spec/events_spec.rb
CHANGED
@@ -408,6 +408,17 @@ describe LaunchDarkly::EventProcessor do
|
|
408
408
|
end
|
409
409
|
end
|
410
410
|
|
411
|
+
it "treats nil value for custom the same as an empty hash" do
|
412
|
+
with_processor_and_sender(default_config) do |ep, sender|
|
413
|
+
user_with_nil_custom = { key: "userkey", custom: nil }
|
414
|
+
e = { kind: "identify", key: "userkey", user: user_with_nil_custom }
|
415
|
+
ep.add_event(e)
|
416
|
+
|
417
|
+
output = flush_and_get_events(ep, sender)
|
418
|
+
expect(output).to contain_exactly(e)
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
411
422
|
it "does a final flush when shutting down" do
|
412
423
|
with_processor_and_sender(default_config) do |ep, sender|
|
413
424
|
e = { kind: "identify", key: user[:key], user: user }
|
@@ -578,7 +589,7 @@ describe LaunchDarkly::EventProcessor do
|
|
578
589
|
@diagnostic_payloads = Queue.new
|
579
590
|
end
|
580
591
|
|
581
|
-
def send_event_data(data, is_diagnostic)
|
592
|
+
def send_event_data(data, description, is_diagnostic)
|
582
593
|
(is_diagnostic ? @diagnostic_payloads : @analytics_payloads).push(JSON.parse(data, symbolize_names: true))
|
583
594
|
@result
|
584
595
|
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require "http_util"
|
2
|
+
require "spec_helper"
|
3
|
+
|
4
|
+
|
5
|
+
SDK_KEY = "sdk-key"
|
6
|
+
|
7
|
+
USER = { key: 'userkey' }
|
8
|
+
|
9
|
+
ALWAYS_TRUE_FLAG = { key: 'flagkey', version: 1, on: false, offVariation: 1, variations: [ false, true ] }
|
10
|
+
DATA_WITH_ALWAYS_TRUE_FLAG = {
|
11
|
+
flags: { ALWAYS_TRUE_FLAG[:key ].to_sym => ALWAYS_TRUE_FLAG },
|
12
|
+
segments: {}
|
13
|
+
}
|
14
|
+
PUT_EVENT_WITH_ALWAYS_TRUE_FLAG = "event: put\ndata:{\"data\":#{DATA_WITH_ALWAYS_TRUE_FLAG.to_json}}\n\n'"
|
15
|
+
|
16
|
+
def with_client(config)
|
17
|
+
client = LaunchDarkly::LDClient.new(SDK_KEY, config)
|
18
|
+
begin
|
19
|
+
yield client
|
20
|
+
ensure
|
21
|
+
client.close
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module LaunchDarkly
|
26
|
+
# Note that we can't do end-to-end tests in streaming mode until we have a test server that can do streaming
|
27
|
+
# responses, which is difficult in WEBrick.
|
28
|
+
|
29
|
+
describe "LDClient end-to-end" do
|
30
|
+
it "starts in polling mode" do
|
31
|
+
with_server do |poll_server|
|
32
|
+
poll_server.setup_ok_response("/sdk/latest-all", DATA_WITH_ALWAYS_TRUE_FLAG.to_json, "application/json")
|
33
|
+
|
34
|
+
config = Config.new(
|
35
|
+
stream: false,
|
36
|
+
base_uri: poll_server.base_uri.to_s,
|
37
|
+
send_events: false,
|
38
|
+
logger: NullLogger.new
|
39
|
+
)
|
40
|
+
with_client(config) do |client|
|
41
|
+
expect(client.initialized?).to be true
|
42
|
+
expect(client.variation(ALWAYS_TRUE_FLAG[:key], USER, false)).to be true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it "fails in polling mode with 401 error" do
|
48
|
+
with_server do |poll_server|
|
49
|
+
poll_server.setup_status_response("/sdk/latest-all", 401)
|
50
|
+
|
51
|
+
config = Config.new(
|
52
|
+
stream: false,
|
53
|
+
base_uri: poll_server.base_uri.to_s,
|
54
|
+
send_events: false,
|
55
|
+
logger: NullLogger.new
|
56
|
+
)
|
57
|
+
with_client(config) do |client|
|
58
|
+
expect(client.initialized?).to be false
|
59
|
+
expect(client.variation(ALWAYS_TRUE_FLAG[:key], USER, false)).to be false
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
it "sends event without diagnostics" do
|
65
|
+
with_server do |poll_server|
|
66
|
+
with_server do |events_server|
|
67
|
+
events_server.setup_ok_response("/bulk", "")
|
68
|
+
poll_server.setup_ok_response("/sdk/latest-all", '{"flags":{},"segments":{}}', "application/json")
|
69
|
+
|
70
|
+
config = Config.new(
|
71
|
+
stream: false,
|
72
|
+
base_uri: poll_server.base_uri.to_s,
|
73
|
+
events_uri: events_server.base_uri.to_s,
|
74
|
+
diagnostic_opt_out: true,
|
75
|
+
logger: NullLogger.new
|
76
|
+
)
|
77
|
+
with_client(config) do |client|
|
78
|
+
client.identify(USER)
|
79
|
+
client.flush
|
80
|
+
|
81
|
+
req, body = events_server.await_request_with_body
|
82
|
+
expect(req.header['authorization']).to eq [ SDK_KEY ]
|
83
|
+
data = JSON.parse(body)
|
84
|
+
expect(data.length).to eq 1
|
85
|
+
expect(data[0]["kind"]).to eq "identify"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
it "sends diagnostic event" do
|
92
|
+
with_server do |poll_server|
|
93
|
+
with_server do |events_server|
|
94
|
+
events_server.setup_ok_response("/bulk", "")
|
95
|
+
events_server.setup_ok_response("/diagnostic", "")
|
96
|
+
poll_server.setup_ok_response("/sdk/latest-all", '{"flags":{},"segments":{}}', "application/json")
|
97
|
+
|
98
|
+
config = Config.new(
|
99
|
+
stream: false,
|
100
|
+
base_uri: poll_server.base_uri.to_s,
|
101
|
+
events_uri: events_server.base_uri.to_s,
|
102
|
+
logger: NullLogger.new
|
103
|
+
)
|
104
|
+
with_client(config) do |client|
|
105
|
+
user = { key: 'userkey' }
|
106
|
+
client.identify(user)
|
107
|
+
client.flush
|
108
|
+
|
109
|
+
req0, body0 = events_server.await_request_with_body
|
110
|
+
req1, body1 = events_server.await_request_with_body
|
111
|
+
req = req0.path == "/diagnostic" ? req0 : req1
|
112
|
+
body = req0.path == "/diagnostic" ? body0 : body1
|
113
|
+
expect(req.header['authorization']).to eq [ SDK_KEY ]
|
114
|
+
data = JSON.parse(body)
|
115
|
+
expect(data["kind"]).to eq "diagnostic-init"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# TODO: TLS tests with self-signed cert
|
122
|
+
end
|
123
|
+
end
|
data/spec/ldclient_spec.rb
CHANGED
@@ -49,6 +49,46 @@ describe LaunchDarkly::LDClient do
|
|
49
49
|
client.instance_variable_get(:@event_processor)
|
50
50
|
end
|
51
51
|
|
52
|
+
describe "constructor requirement of non-nil sdk key" do
|
53
|
+
it "is not enforced when offline" do
|
54
|
+
subject.new(nil, offline_config)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "is not enforced if use_ldd is true and send_events is false" do
|
58
|
+
subject.new(nil, LaunchDarkly::Config.new({ use_ldd: true, send_events: false }))
|
59
|
+
end
|
60
|
+
|
61
|
+
it "is not enforced if using file data and send_events is false" do
|
62
|
+
source = LaunchDarkly::FileDataSource.factory({})
|
63
|
+
subject.new(nil, LaunchDarkly::Config.new({ data_source: source, send_events: false }))
|
64
|
+
end
|
65
|
+
|
66
|
+
it "is enforced in streaming mode even if send_events is false" do
|
67
|
+
expect {
|
68
|
+
subject.new(nil, LaunchDarkly::Config.new({ send_events: false }))
|
69
|
+
}.to raise_error(ArgumentError)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "is enforced in polling mode even if send_events is false" do
|
73
|
+
expect {
|
74
|
+
subject.new(nil, LaunchDarkly::Config.new({ stream: false, send_events: false }))
|
75
|
+
}.to raise_error(ArgumentError)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "is enforced if use_ldd is true and send_events is true" do
|
79
|
+
expect {
|
80
|
+
subject.new(nil, LaunchDarkly::Config.new({ use_ldd: true }))
|
81
|
+
}.to raise_error(ArgumentError)
|
82
|
+
end
|
83
|
+
|
84
|
+
it "is enforced if using file data and send_events is true" do
|
85
|
+
source = LaunchDarkly::FileDataSource.factory({})
|
86
|
+
expect {
|
87
|
+
subject.new(nil, LaunchDarkly::Config.new({ data_source: source }))
|
88
|
+
}.to raise_error(ArgumentError)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
52
92
|
describe '#variation' do
|
53
93
|
feature_with_value = { key: "key", on: false, offVariation: 0, variations: ["value"], version: 100,
|
54
94
|
trackEvents: true, debugEventsUntilDate: 1000 }
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require "connection_pool"
|
1
2
|
require "feature_store_spec_base"
|
2
3
|
require "json"
|
3
4
|
require "redis"
|
@@ -27,11 +28,11 @@ end
|
|
27
28
|
|
28
29
|
describe LaunchDarkly::RedisFeatureStore do
|
29
30
|
subject { LaunchDarkly::RedisFeatureStore }
|
30
|
-
|
31
|
+
|
31
32
|
break if ENV['LD_SKIP_DATABASE_TESTS'] == '1'
|
32
33
|
|
33
34
|
# These tests will all fail if there isn't a Redis instance running on the default port.
|
34
|
-
|
35
|
+
|
35
36
|
context "real Redis with local cache" do
|
36
37
|
include_examples "feature_store", method(:create_redis_store), method(:clear_all_data)
|
37
38
|
end
|
@@ -59,7 +60,7 @@ describe LaunchDarkly::RedisFeatureStore do
|
|
59
60
|
flag = { key: "foo", version: 1 }
|
60
61
|
test_hook = make_concurrent_modifier_test_hook(other_client, flag, 2, 4)
|
61
62
|
store = create_redis_store({ test_hook: test_hook })
|
62
|
-
|
63
|
+
|
63
64
|
begin
|
64
65
|
store.init(LaunchDarkly::FEATURES => { flag[:key] => flag })
|
65
66
|
|
@@ -77,7 +78,7 @@ describe LaunchDarkly::RedisFeatureStore do
|
|
77
78
|
flag = { key: "foo", version: 1 }
|
78
79
|
test_hook = make_concurrent_modifier_test_hook(other_client, flag, 3, 3)
|
79
80
|
store = create_redis_store({ test_hook: test_hook })
|
80
|
-
|
81
|
+
|
81
82
|
begin
|
82
83
|
store.init(LaunchDarkly::FEATURES => { flag[:key] => flag })
|
83
84
|
|
@@ -89,4 +90,32 @@ describe LaunchDarkly::RedisFeatureStore do
|
|
89
90
|
other_client.close
|
90
91
|
end
|
91
92
|
end
|
93
|
+
|
94
|
+
it "shuts down a custom Redis pool by default" do
|
95
|
+
unowned_pool = ConnectionPool.new(size: 1, timeout: 1) { Redis.new({ url: "redis://localhost:6379" }) }
|
96
|
+
store = create_redis_store({ pool: unowned_pool })
|
97
|
+
|
98
|
+
begin
|
99
|
+
store.init(LaunchDarkly::FEATURES => { })
|
100
|
+
store.stop
|
101
|
+
|
102
|
+
expect { unowned_pool.with {} }.to raise_error(ConnectionPool::PoolShuttingDownError)
|
103
|
+
ensure
|
104
|
+
unowned_pool.shutdown { |conn| conn.close }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
it "doesn't shut down a custom Redis pool if pool_shutdown_on_close = false" do
|
109
|
+
unowned_pool = ConnectionPool.new(size: 1, timeout: 1) { Redis.new({ url: "redis://localhost:6379" }) }
|
110
|
+
store = create_redis_store({ pool: unowned_pool, pool_shutdown_on_close: false })
|
111
|
+
|
112
|
+
begin
|
113
|
+
store.init(LaunchDarkly::FEATURES => { })
|
114
|
+
store.stop
|
115
|
+
|
116
|
+
expect { unowned_pool.with {} }.not_to raise_error(ConnectionPool::PoolShuttingDownError)
|
117
|
+
ensure
|
118
|
+
unowned_pool.shutdown { |conn| conn.close }
|
119
|
+
end
|
120
|
+
end
|
92
121
|
end
|
data/spec/requestor_spec.rb
CHANGED
@@ -193,38 +193,4 @@ describe LaunchDarkly::Requestor do
|
|
193
193
|
end
|
194
194
|
end
|
195
195
|
end
|
196
|
-
|
197
|
-
describe "request_flag" do
|
198
|
-
it "uses expected URI and headers" do
|
199
|
-
with_server do |server|
|
200
|
-
with_requestor(server.base_uri.to_s) do |requestor|
|
201
|
-
server.setup_ok_response("/", "{}")
|
202
|
-
requestor.request_flag("key")
|
203
|
-
expect(server.requests.count).to eq 1
|
204
|
-
expect(server.requests[0].unparsed_uri).to eq "/sdk/latest-flags/key"
|
205
|
-
expect(server.requests[0].header).to include({
|
206
|
-
"authorization" => [ $sdk_key ],
|
207
|
-
"user-agent" => [ "RubyClient/" + LaunchDarkly::VERSION ]
|
208
|
-
})
|
209
|
-
end
|
210
|
-
end
|
211
|
-
end
|
212
|
-
end
|
213
|
-
|
214
|
-
describe "request_segment" do
|
215
|
-
it "uses expected URI and headers" do
|
216
|
-
with_server do |server|
|
217
|
-
with_requestor(server.base_uri.to_s) do |requestor|
|
218
|
-
server.setup_ok_response("/", "{}")
|
219
|
-
requestor.request_segment("key")
|
220
|
-
expect(server.requests.count).to eq 1
|
221
|
-
expect(server.requests[0].unparsed_uri).to eq "/sdk/latest-segments/key"
|
222
|
-
expect(server.requests[0].header).to include({
|
223
|
-
"authorization" => [ $sdk_key ],
|
224
|
-
"user-agent" => [ "RubyClient/" + LaunchDarkly::VERSION ]
|
225
|
-
})
|
226
|
-
end
|
227
|
-
end
|
228
|
-
end
|
229
|
-
end
|
230
196
|
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/stream_spec.rb
CHANGED
@@ -4,8 +4,7 @@ require "spec_helper"
|
|
4
4
|
describe LaunchDarkly::StreamProcessor do
|
5
5
|
subject { LaunchDarkly::StreamProcessor }
|
6
6
|
let(:config) { LaunchDarkly::Config.new }
|
7
|
-
let(:
|
8
|
-
let(:processor) { subject.new("sdk_key", config, requestor) }
|
7
|
+
let(:processor) { subject.new("sdk_key", config) }
|
9
8
|
|
10
9
|
describe '#process_message' do
|
11
10
|
let(:put_message) { SSE::StreamEvent.new(:put, '{"data":{"flags":{"asdf": {"key": "asdf"}},"segments":{"segkey": {"key": "segkey"}}}}') }
|
@@ -13,8 +12,6 @@ describe LaunchDarkly::StreamProcessor do
|
|
13
12
|
let(:patch_seg_message) { SSE::StreamEvent.new(:patch, '{"path": "/segments/key", "data": {"key": "asdf", "version": 1}}') }
|
14
13
|
let(:delete_flag_message) { SSE::StreamEvent.new(:delete, '{"path": "/flags/key", "version": 2}') }
|
15
14
|
let(:delete_seg_message) { SSE::StreamEvent.new(:delete, '{"path": "/segments/key", "version": 2}') }
|
16
|
-
let(:indirect_patch_flag_message) { SSE::StreamEvent.new(:'indirect/patch', "/flags/key") }
|
17
|
-
let(:indirect_patch_segment_message) { SSE::StreamEvent.new(:'indirect/patch', "/segments/key") }
|
18
15
|
|
19
16
|
it "will accept PUT methods" do
|
20
17
|
processor.send(:process_message, put_message)
|
@@ -39,18 +36,6 @@ describe LaunchDarkly::StreamProcessor do
|
|
39
36
|
processor.send(:process_message, delete_seg_message)
|
40
37
|
expect(config.feature_store.get(LaunchDarkly::SEGMENTS, "key")).to eq(nil)
|
41
38
|
end
|
42
|
-
it "will accept INDIRECT PATCH method for flags" do
|
43
|
-
flag = { key: 'key', version: 1 }
|
44
|
-
allow(requestor).to receive(:request_flag).with(flag[:key]).and_return(flag)
|
45
|
-
processor.send(:process_message, indirect_patch_flag_message);
|
46
|
-
expect(config.feature_store.get(LaunchDarkly::FEATURES, flag[:key])).to eq(flag)
|
47
|
-
end
|
48
|
-
it "will accept INDIRECT PATCH method for segments" do
|
49
|
-
segment = { key: 'key', version: 1 }
|
50
|
-
allow(requestor).to receive(:request_segment).with(segment[:key]).and_return(segment)
|
51
|
-
processor.send(:process_message, indirect_patch_segment_message);
|
52
|
-
expect(config.feature_store.get(LaunchDarkly::SEGMENTS, segment[:key])).to eq(segment)
|
53
|
-
end
|
54
39
|
it "will log a warning if the method is not recognized" do
|
55
40
|
expect(processor.instance_variable_get(:@config).logger).to receive :warn
|
56
41
|
processor.send(:process_message, SSE::StreamEvent.new(type: :get, data: "", id: nil))
|
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: 5.
|
4
|
+
version: 5.8.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- LaunchDarkly
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-11-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk-dynamodb
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '1.
|
33
|
+
version: '1.17'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '1.
|
40
|
+
version: '1.17'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rspec
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,20 +52,6 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.2'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: codeclimate-test-reporter
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - "~>"
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - "~>"
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
69
55
|
- !ruby/object:Gem::Dependency
|
70
56
|
name: diplomat
|
71
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -151,25 +137,33 @@ dependencies:
|
|
151
137
|
- !ruby/object:Gem::Version
|
152
138
|
version: '3.0'
|
153
139
|
- !ruby/object:Gem::Dependency
|
154
|
-
name:
|
140
|
+
name: ffi
|
155
141
|
requirement: !ruby/object:Gem::Requirement
|
156
142
|
requirements:
|
157
|
-
- - "
|
143
|
+
- - "<="
|
158
144
|
- !ruby/object:Gem::Version
|
159
|
-
version: '1.
|
160
|
-
|
161
|
-
- !ruby/object:Gem::Version
|
162
|
-
version: '3'
|
163
|
-
type: :runtime
|
145
|
+
version: '1.12'
|
146
|
+
type: :development
|
164
147
|
prerelease: false
|
165
148
|
version_requirements: !ruby/object:Gem::Requirement
|
166
149
|
requirements:
|
167
|
-
- - "
|
150
|
+
- - "<="
|
168
151
|
- !ruby/object:Gem::Version
|
169
|
-
version: '1.
|
170
|
-
|
152
|
+
version: '1.12'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: faraday
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - "~>"
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0.17'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - "~>"
|
171
165
|
- !ruby/object:Gem::Version
|
172
|
-
version: '
|
166
|
+
version: '0.17'
|
173
167
|
- !ruby/object:Gem::Dependency
|
174
168
|
name: semantic
|
175
169
|
requirement: !ruby/object:Gem::Requirement
|
@@ -212,12 +206,25 @@ dependencies:
|
|
212
206
|
- - '='
|
213
207
|
- !ruby/object:Gem::Version
|
214
208
|
version: 1.0.3
|
209
|
+
- !ruby/object:Gem::Dependency
|
210
|
+
name: json
|
211
|
+
requirement: !ruby/object:Gem::Requirement
|
212
|
+
requirements:
|
213
|
+
- - "~>"
|
214
|
+
- !ruby/object:Gem::Version
|
215
|
+
version: 2.3.1
|
216
|
+
type: :runtime
|
217
|
+
prerelease: false
|
218
|
+
version_requirements: !ruby/object:Gem::Requirement
|
219
|
+
requirements:
|
220
|
+
- - "~>"
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: 2.3.1
|
215
223
|
description: Official LaunchDarkly SDK for Ruby
|
216
224
|
email:
|
217
225
|
- team@launchdarkly.com
|
218
226
|
executables: []
|
219
|
-
extensions:
|
220
|
-
- ext/mkrf_conf.rb
|
227
|
+
extensions: []
|
221
228
|
extra_rdoc_files: []
|
222
229
|
files:
|
223
230
|
- ".circleci/config.yml"
|
@@ -239,7 +246,6 @@ files:
|
|
239
246
|
- LICENSE.txt
|
240
247
|
- README.md
|
241
248
|
- azure-pipelines.yml
|
242
|
-
- ext/mkrf_conf.rb
|
243
249
|
- launchdarkly-server-sdk.gemspec
|
244
250
|
- lib/launchdarkly-server-sdk.rb
|
245
251
|
- lib/ldclient-rb.rb
|
@@ -302,6 +308,7 @@ files:
|
|
302
308
|
- spec/integrations/store_wrapper_spec.rb
|
303
309
|
- spec/launchdarkly-server-sdk_spec.rb
|
304
310
|
- spec/launchdarkly-server-sdk_spec_autoloadtest.rb
|
311
|
+
- spec/ldclient_end_to_end_spec.rb
|
305
312
|
- spec/ldclient_spec.rb
|
306
313
|
- spec/newrelic_spec.rb
|
307
314
|
- spec/polling_spec.rb
|
@@ -360,6 +367,7 @@ test_files:
|
|
360
367
|
- spec/integrations/store_wrapper_spec.rb
|
361
368
|
- spec/launchdarkly-server-sdk_spec.rb
|
362
369
|
- spec/launchdarkly-server-sdk_spec_autoloadtest.rb
|
370
|
+
- spec/ldclient_end_to_end_spec.rb
|
363
371
|
- spec/ldclient_spec.rb
|
364
372
|
- spec/newrelic_spec.rb
|
365
373
|
- spec/polling_spec.rb
|
data/ext/mkrf_conf.rb
DELETED
@@ -1,11 +0,0 @@
|
|
1
|
-
require "rubygems"
|
2
|
-
|
3
|
-
|
4
|
-
# From http://stackoverflow.com/questions/5830835/how-to-add-openssl-dependency-to-gemspec
|
5
|
-
# the whole reason this file exists: to return an error if openssl
|
6
|
-
# isn't installed.
|
7
|
-
require "openssl"
|
8
|
-
|
9
|
-
f = File.open(File.join(File.dirname(__FILE__), "Rakefile"), "w") # create dummy rakefile to indicate success
|
10
|
-
f.write("task :default\n")
|
11
|
-
f.close
|