Skip to content

Commit f0a536c

Browse files
LaunchDarklyReleaseBotLaunchDarklyCIeli-darklyhroederldbwoskow-ld
authored
prepare 6.3.2 release (#198)
* fix doc comments * add YARD config so our docs show up correctly everywhere * don't need markup-provider option * rm obsolete proxy param * remove net-http-persistent * fix concurrent-ruby usage that breaks on Windows * add pipeline and clean up with with rm_rf instead of rm * fix highlight blocks * Hr/azure3 (#103) * Add Consul and Redis services to Windows. * Enable Consul and Redis testing * add dynamo (#104) * add experimentation event overrides for rules and fallthrough * warn & don't send event if identify or track has no valid user * include user in prereq flag events * rm unnecessary logic * more factory methods * update readme to refer to docs * add Ruby 2.6.2 to CI * fix missing require for net/http * stringify built-in user attributes in events, and secondary key for evals * make const names consistent * support metric value with track() * update method description * applying markdown templates and updating repository url references * Cleaning up markdown files * allow skipping database tests * Updating the package name (#115) * update package name * missed one * revert module entry point name change * bump ld-eventsource version for stream logging fix * use YAML.safe_load * add unit test and temporarily revert fix to demonstrate failure * restore fix * add comment about not using FileDataSource in production * drop events if inbox is full * update doc comment for track with metric_value * don't let user fall outside of last bucket in rollout * refactor evaluation logic and move it out of the main namespace * comments * fix type coercion behavior * make type coercion behavior consistent with earlier versions for now * whitespace * break up Evaluator tests further * make EvaluationReason an immutable class * FrozenError doesn't exist in older Ruby, use more general RuntimeError * precompute evaluation reasons when we receive a flag * rm unused * rename FeatureStore to DataStore * remove references to UpdateProcessor (now DataSource) * add event payload ID header * (6.0) drop support for old Ruby versions * add Ruby version constraint to gemspec * remove Rake dependency * update ld-eventsource to 1.0.2 which doesn't have Rake dependency * implement diagnostic events in Ruby (#130) * update ruby-eventsource to 1.0.3 for backoff bug * fix incorrect initialization of EventProcessor * remove install-time openssl check that breaks if you don't have rake * treat comparison with wrong data type as a non-match, not an exception (#134) * fail fast for nil SDK key when appropriate * tolerate nil value for user.custom (#137) * Only shutdown the Redis pool if it is owned by the SDK (#158) * Only shutdown a Redis pool created by SDK * Make pool shutdown behavior an option * improve doc comment * remove support for indirect/patch and indirect/put (#138) * update to json 2.3.1 (#139) * update json dep to 2.3.x to fix CVE * add publication of API docs on GitHub Pages (#143) * try fixing release metadata * update the default base url (#144) * revert renames of feature_store & update_processor * [ch92483] Use http gem and add socket factory support (#142) * update dependencies and add CI for ruby 3 (#141) * reference eventsource 2.0 in gemspec * add 5.x releasable branch for releaser * use Ruby 2.6.6 in releases * Removed the guides link * [ch99757] add alias method (#147) * don't send event for nil user evaluation * remove lockfile (#148) * rm redundant nil check * Experiment Allocation Changes (#150) * WIP - from sam's pairing session * starting sdk changes * adding tests and making sure everything works * adding more tests * removing the singleton for fallthrough * Revert "removing the singleton for fallthrough" This reverts commit dff7adbb809ecc63118d0fbff9742a88a039c679. * taking a different approach to keep things immutable * adding tests for untracked * remove unnecessary comment * making sure to return two values in all code paths Co-authored-by: pellyg-ld <[email protected]> * Use camelCase for JSON property names (#151) The in_experiment attribute was added to reasons as part of #150 but it doesn't appear to be received in events. I think that's because it's sending it in JSON as "in_experiment" rather than "inExperiment" as we expect to parse it. * fixing ruby logic causing ih failures (#152) * fixing ruby logic * adding missing spec * Apply suggestions from code review Co-authored-by: Sam Stokes <[email protected]> * pr tweaks * making spec language consistent Co-authored-by: Sam Stokes <[email protected]> * add log warning for missing user key (#153) * add log warnings for nil/empty user key * rm warning for empty string key * fix test * diagnostic events should respect HTTPS_PROXY (#154) * minor test simplification (#155) * allow higher minor versions of json and http gems * allow v5.x of http gem (#157) * use Bundler 2.2.10 + modernize CI config (#158) * enable verbose rspec output * fix socket factory tests * restore log suppression * Replacing deprecated circleci image usage (#159) * use Releaser v2 config (#161) * Updates docs URLs * Update lib/ldclient-rb/ldclient.rb Co-authored-by: Louis Chan <[email protected]> * remove reliance on git in gemspec (#163) * use ruby-eventsource 2.1.1 for fix of sc-123850 and sc-125504 (#164) * use ruby-eventsource 2.1.1 for fix of sc-123850 and sc-125504 * comment phrasing * Start work on flag builder. * Add user targeting and rule builder * Add datasource implementation * Convert the current_flags hash to use symbols instead of strings as keys * Fix typo on FlagRuleBuilder copy constructor * minor refactoring of impl; Added use of new Clause struct instead of Hash in FlagRuleBuilder; Moved TestData.factory out of Impl namespace and renamed Impl to TestDataImpl * Add the doc comments * (big segments 1) add public config/interface/reason types (#167) * Cleanup docstrings to be YARD docs * Added Util.is_bool helper function to clean up the check for whether an object is a boolean; Removed the DeepCopyHash/DeepCopyArray objects in favor of deep_copy_hash and deep_copy_array functions * Move public classes out of Impl namespace. Most of it is in public namespace except for the data source now. * Move require of concurrent/atomics to the correct module * (big segments 2) implement Big Segments evaluation & status APIs (#168) * improve CONTRIBUTING.md with notes on code organization * add note about doc comments * Cleanup YARD warnings and cleanup docs * Address PR feedback: Move is_bool back to Impl namespace to avoid confusion; Remove unnecessary nil check on variations in build function; fixup comments * (big segments 3) implement Redis & DynamoDB big segment stores (#169) * add missing import * fix stale calculation * fix big segments user hash algorithm to use SHA256 * improve & refactor client/evaluation tests * more cleanup/DRY * add use_preconfigured_flag and use_preconfigured_segment to TestData (#173) * always cache big segment query result even if it's nil * comments * add test for cache expiration * use TestData in our own tests (#174) * use TestData in our own tests * fix test * replace LaunchDarkly::FileDataSource with LaunchDarkly::Integrations::FileData * update ruby-eventsource version for recent SSE fixes * Bump bundler version (#184) * Add ability to to set initial reconnect delay (#183) * Treat secondary as a built-in attribute (#180) * all_flags_state is invalid if store isn't initialized (#182) * identify should not emit events if user key is "" (#181) * Account for traffic allocation on all flags (#185) * Add contract tests (#178) * Fix string interpolation in log message (#187) * Default opts to empty hash when creating persistent feature store (#186) * Remove Hakiri badge from README (#188) Hakiri was sunset on January 31st, 2022 at which time our badge stopped working. * detect http/https proxy env vars when creating HTTP clients * rever accidental change Co-authored-by: LaunchDarklyCI <[email protected]> Co-authored-by: Eli Bishop <[email protected]> Co-authored-by: Harpo roeder <[email protected]> Co-authored-by: hroederld <[email protected]> Co-authored-by: Ben Woskow <[email protected]> Co-authored-by: Ben Woskow <[email protected]> Co-authored-by: Jacob Smith <[email protected]> Co-authored-by: Elliot <[email protected]> Co-authored-by: Kerrie Martinez <[email protected]> Co-authored-by: pellyg-ld <[email protected]> Co-authored-by: Sam Stokes <[email protected]> Co-authored-by: LaunchDarklyReleaseBot <[email protected]> Co-authored-by: Ember Stevens <[email protected]> Co-authored-by: ember-stevens <[email protected]> Co-authored-by: Louis Chan <[email protected]> Co-authored-by: Matthew M. Keeler <[email protected]> Co-authored-by: Ben Levy <[email protected]> Co-authored-by: Ben Levy <[email protected]> Co-authored-by: Matthew M. Keeler <[email protected]>
1 parent edc4efd commit f0a536c

25 files changed

+437
-102
lines changed

.circleci/config.yml

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ workflows:
1717
name: Ruby 3.0
1818
docker-image: cimg/ruby:3.0
1919
- build-test-linux:
20-
name: JRuby 9.2
21-
docker-image: jruby:9.2-jdk
20+
name: JRuby 9.3
21+
docker-image: jruby:9.3-jdk
2222
jruby: true
2323

2424
jobs:
@@ -41,19 +41,30 @@ jobs:
4141
- when:
4242
condition: <<parameters.jruby>>
4343
steps:
44-
- run: gem install jruby-openssl # required by bundler, no effect on Ruby MRI
44+
- run: gem install jruby-openssl -v 0.11.0 # required by bundler, no effect on Ruby MRI
4545
- run: apt-get update -y && apt-get install -y build-essential
4646
- when:
4747
condition:
4848
not: <<parameters.jruby>>
4949
steps:
5050
- run: sudo apt-get update -y && sudo apt-get install -y build-essential
5151
- run: ruby -v
52-
- run: gem install bundler -v 2.2.10
53-
- run: bundle _2.2.10_ install
54-
- run: mkdir ./rspec
55-
- run: bundle _2.2.10_ exec rspec --format documentation --format RspecJunitFormatter -o ./rspec/rspec.xml spec
52+
- run: gem install bundler -v 2.2.33
53+
- run: bundle _2.2.33_ install
54+
- run: mkdir /tmp/circle-artifacts
55+
- run: bundle _2.2.33_ exec rspec --format documentation --format RspecJunitFormatter -o /tmp/circle-artifacts/rspec.xml spec
56+
57+
- when:
58+
condition:
59+
not: <<parameters.jruby>>
60+
steps:
61+
- run: make build-contract-tests
62+
- run:
63+
command: make start-contract-test-service
64+
background: true
65+
- run: TEST_HARNESS_PARAMS="-junit /tmp/circle-artifacts/contract-tests-junit.xml" make run-contract-tests
66+
5667
- store_test_results:
57-
path: ./rspec
68+
path: /tmp/circle-artifacts
5869
- store_artifacts:
59-
path: ./rspec
70+
path: /tmp/circle-artifacts

Makefile

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
TEMP_TEST_OUTPUT=/tmp/contract-test-service.log
2+
3+
build-contract-tests:
4+
@cd contract-tests && bundle _2.2.33_ install
5+
6+
start-contract-test-service:
7+
@cd contract-tests && bundle _2.2.33_ exec ruby service.rb
8+
9+
start-contract-test-service-bg:
10+
@echo "Test service output will be captured in $(TEMP_TEST_OUTPUT)"
11+
@make start-contract-test-service >$(TEMP_TEST_OUTPUT) 2>&1 &
12+
13+
run-contract-tests:
14+
@curl -s https://raw.githubusercontent.com/launchdarkly/sdk-test-harness/v1.0.0/downloader/run.sh \
15+
| VERSION=v1 PARAMS="-url http://localhost:9000 -debug -stop-service-at-end $(TEST_HARNESS_PARAMS)" sh
16+
17+
contract-tests: build-contract-tests start-contract-test-service-bg run-contract-tests
18+
19+
.PHONY: build-contract-tests start-contract-test-service run-contract-tests contract-tests

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ LaunchDarkly Server-side SDK for Ruby
44
[![Gem Version](https://badge.fury.io/rb/launchdarkly-server-sdk.svg)](http://badge.fury.io/rb/launchdarkly-server-sdk)
55

66
[![Circle CI](https://circleci.com/gh/launchdarkly/ruby-server-sdk/tree/master.svg?style=svg)](https://circleci.com/gh/launchdarkly/ruby-server-sdk/tree/master)
7-
[![Security](https://hakiri.io/github/launchdarkly/ruby-server-sdk/master.svg)](https://hakiri.io/github/launchdarkly/ruby-server-sdk/master)
87
[![RubyDoc](https://img.shields.io/static/v1?label=docs+-+all+versions&message=reference&color=00add8)](https://www.rubydoc.info/gems/launchdarkly-server-sdk)
98
[![GitHub Pages](https://img.shields.io/static/v1?label=docs+-+latest&message=reference&color=00add8)](https://launchdarkly.github.io/ruby-server-sdk)
109

contract-tests/Gemfile

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
source 'https://rubygems.org'
2+
3+
gem 'launchdarkly-server-sdk', path: '..'
4+
5+
gem 'sinatra', '~> 2.1'
6+
# Sinatra can work with several server frameworks. In JRuby, we have to use glassfish (which
7+
# is only available in JRuby). Otherwise we use thin (which is not available in JRuby).
8+
gem 'glassfish', :platforms => :jruby
9+
gem 'thin', :platforms => :ruby
10+
gem 'json'

contract-tests/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# SDK contract test service
2+
3+
This directory contains an implementation of the cross-platform SDK testing protocol defined by https://github.com/launchdarkly/sdk-test-harness. See that project's `README` for details of this protocol, and the kinds of SDK capabilities that are relevant to the contract tests. This code should not need to be updated unless the SDK has added or removed such capabilities.
4+
5+
To run these tests locally, run `make contract-tests` from the SDK project root directory. This downloads the correct version of the test harness tool automatically.
6+
7+
Or, to test against an in-progress local version of the test harness, run `make start-contract-test-service` from the SDK project root directory; then, in the root directory of the `sdk-test-harness` project, build the test harness and run it from the command line.

contract-tests/client_entity.rb

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
require 'ld-eventsource'
2+
require 'json'
3+
require 'net/http'
4+
5+
class ClientEntity
6+
def initialize(log, config)
7+
@log = log
8+
9+
opts = {}
10+
11+
opts[:logger] = log
12+
13+
if config[:streaming]
14+
streaming = config[:streaming]
15+
opts[:stream_uri] = streaming[:baseUri] if !streaming[:baseUri].nil?
16+
opts[:initial_reconnect_delay] = streaming[:initialRetryDelayMs] / 1_000.0 if !streaming[:initialRetryDelayMs].nil?
17+
end
18+
19+
if config[:events]
20+
events = config[:events]
21+
opts[:events_uri] = events[:baseUri] if events[:baseUri]
22+
opts[:capacity] = events[:capacity] if events[:capacity]
23+
opts[:diagnostic_opt_out] = !events[:enableDiagnostics]
24+
opts[:all_attributes_private] = !!events[:allAttributesPrivate]
25+
opts[:private_attribute_names] = events[:globalPrivateAttributes]
26+
opts[:flush_interval] = (events[:flushIntervalMs] / 1_000) if events.has_key? :flushIntervalMs
27+
opts[:inline_users_in_events] = events[:inlineUsers] || false
28+
else
29+
opts[:send_events] = false
30+
end
31+
32+
startWaitTimeMs = config[:startWaitTimeMs] || 5_000
33+
34+
@client = LaunchDarkly::LDClient.new(
35+
config[:credential],
36+
LaunchDarkly::Config.new(opts),
37+
startWaitTimeMs / 1_000.0)
38+
end
39+
40+
def initialized?
41+
@client.initialized?
42+
end
43+
44+
def evaluate(params)
45+
response = {}
46+
47+
if params[:detail]
48+
detail = @client.variation_detail(params[:flagKey], params[:user], params[:defaultValue])
49+
response[:value] = detail.value
50+
response[:variationIndex] = detail.variation_index
51+
response[:reason] = detail.reason
52+
else
53+
response[:value] = @client.variation(params[:flagKey], params[:user], params[:defaultValue])
54+
end
55+
56+
response
57+
end
58+
59+
def evaluate_all(params)
60+
opts = {}
61+
opts[:client_side_only] = params[:clientSideOnly] || false
62+
opts[:with_reasons] = params[:withReasons] || false
63+
opts[:details_only_for_tracked_flags] = params[:detailsOnlyForTrackedFlags] || false
64+
65+
@client.all_flags_state(params[:user], opts)
66+
end
67+
68+
def track(params)
69+
@client.track(params[:eventKey], params[:user], params[:data], params[:metricValue])
70+
end
71+
72+
def identify(params)
73+
@client.identify(params[:user])
74+
end
75+
76+
def alias(params)
77+
@client.alias(params[:user], params[:previousUser])
78+
end
79+
80+
def flush_events
81+
@client.flush
82+
end
83+
84+
def log
85+
@log
86+
end
87+
88+
def close
89+
@client.close
90+
@log.info("Test ended")
91+
end
92+
end

contract-tests/service.rb

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
require 'launchdarkly-server-sdk'
2+
require 'json'
3+
require 'logger'
4+
require 'net/http'
5+
require 'sinatra'
6+
7+
require './client_entity.rb'
8+
9+
configure :development do
10+
disable :show_exceptions
11+
end
12+
13+
$log = Logger.new(STDOUT)
14+
$log.formatter = proc {|severity, datetime, progname, msg|
15+
"[GLOBAL] #{datetime.strftime('%Y-%m-%d %H:%M:%S.%3N')} #{severity} #{progname} #{msg}\n"
16+
}
17+
18+
set :port, 9000
19+
set :logging, false
20+
21+
clients = {}
22+
clientCounter = 0
23+
24+
get '/' do
25+
{
26+
capabilities: [
27+
'server-side',
28+
'all-flags-with-reasons',
29+
'all-flags-client-side-only',
30+
'all-flags-details-only-for-tracked-flags',
31+
]
32+
}.to_json
33+
end
34+
35+
delete '/' do
36+
$log.info("Test service has told us to exit")
37+
Thread.new { sleep 1; exit }
38+
return 204
39+
end
40+
41+
post '/' do
42+
opts = JSON.parse(request.body.read, :symbolize_names => true)
43+
tag = "[#{opts[:tag]}]"
44+
45+
clientCounter += 1
46+
clientId = clientCounter.to_s
47+
48+
log = Logger.new(STDOUT)
49+
log.formatter = proc {|severity, datetime, progname, msg|
50+
"#{tag} #{datetime.strftime('%Y-%m-%d %H:%M:%S.%3N')} #{severity} #{progname} #{msg}\n"
51+
}
52+
53+
log.info("Starting client")
54+
log.debug("Parameters: #{opts}")
55+
56+
client = ClientEntity.new(log, opts[:configuration])
57+
58+
if !client.initialized? && opts[:configuration][:initCanFail] == false
59+
client.close()
60+
return [500, nil, "Failed to initialize"]
61+
end
62+
63+
clientResourceUrl = "/clients/#{clientId}"
64+
clients[clientId] = client
65+
return [201, {'Location' => clientResourceUrl}, nil]
66+
end
67+
68+
post '/clients/:id' do |clientId|
69+
client = clients[clientId]
70+
return 404 if client.nil?
71+
72+
params = JSON.parse(request.body.read, :symbolize_names => true)
73+
74+
client.log.info("Processing request for client #{clientId}")
75+
client.log.debug("Parameters: #{params}")
76+
77+
case params[:command]
78+
when "evaluate"
79+
response = client.evaluate(params[:evaluate])
80+
return [200, nil, response.to_json]
81+
when "evaluateAll"
82+
response = {:state => client.evaluate_all(params[:evaluateAll])}
83+
return [200, nil, response.to_json]
84+
when "customEvent"
85+
client.track(params[:customEvent])
86+
return 201
87+
when "identifyEvent"
88+
client.identify(params[:identifyEvent])
89+
return 201
90+
when "aliasEvent"
91+
client.alias(params[:aliasEvent])
92+
return 201
93+
when "flushEvents"
94+
client.flush_events
95+
return 201
96+
end
97+
98+
return [400, nil, {:error => "Unknown command requested"}.to_json]
99+
end
100+
101+
delete '/clients/:id' do |clientId|
102+
client = clients[clientId]
103+
return 404 if client.nil?
104+
clients.delete(clientId)
105+
client.close
106+
107+
return 204
108+
end
109+
110+
error do
111+
env['sinatra.error'].message
112+
end

launchdarkly-server-sdk.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
2222
spec.required_ruby_version = ">= 2.5.0"
2323

2424
spec.add_development_dependency "aws-sdk-dynamodb", "~> 1.57"
25-
spec.add_development_dependency "bundler", "2.2.10"
25+
spec.add_development_dependency "bundler", "2.2.33"
2626
spec.add_development_dependency "rspec", "~> 3.10"
2727
spec.add_development_dependency "diplomat", "~> 2.4.2"
2828
spec.add_development_dependency "redis", "~> 4.2"

lib/ldclient-rb/config.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class Config
2121
# @option opts [Integer] :capacity (10000) See {#capacity}.
2222
# @option opts [Float] :flush_interval (30) See {#flush_interval}.
2323
# @option opts [Float] :read_timeout (10) See {#read_timeout}.
24+
# @option opts [Float] :initial_reconnect_delay (1) See {#initial_reconnect_delay}.
2425
# @option opts [Float] :connect_timeout (2) See {#connect_timeout}.
2526
# @option opts [Object] :cache_store See {#cache_store}.
2627
# @option opts [Object] :feature_store See {#feature_store}.
@@ -54,6 +55,7 @@ def initialize(opts = {})
5455
@flush_interval = opts[:flush_interval] || Config.default_flush_interval
5556
@connect_timeout = opts[:connect_timeout] || Config.default_connect_timeout
5657
@read_timeout = opts[:read_timeout] || Config.default_read_timeout
58+
@initial_reconnect_delay = opts[:initial_reconnect_delay] || Config.default_initial_reconnect_delay
5759
@feature_store = opts[:feature_store] || Config.default_feature_store
5860
@stream = opts.has_key?(:stream) ? opts[:stream] : Config.default_stream
5961
@use_ldd = opts.has_key?(:use_ldd) ? opts[:use_ldd] : Config.default_use_ldd
@@ -180,6 +182,13 @@ def offline?
180182
#
181183
attr_reader :read_timeout
182184

185+
#
186+
# The initial delay before reconnecting after an error in the SSE client.
187+
# This only applies to the streaming connection.
188+
# @return [Float]
189+
#
190+
attr_reader :initial_reconnect_delay
191+
183192
#
184193
# The connect timeout for network connections in seconds.
185194
# @return [Float]
@@ -395,6 +404,14 @@ def self.default_read_timeout
395404
10
396405
end
397406

407+
#
408+
# The default value for {#initial_reconnect_delay}.
409+
# @return [Float] 1
410+
#
411+
def self.default_initial_reconnect_delay
412+
1
413+
end
414+
398415
#
399416
# The default value for {#connect_timeout}.
400417
# @return [Float] 10

0 commit comments

Comments
 (0)