Skip to content

Commit

Permalink
prepare 6.3.2 release (#198)
Browse files Browse the repository at this point in the history
* 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 <gpelly@launchdarkly.com>

* 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 <sstokes@launchdarkly.com>

* pr tweaks

* making spec language consistent

Co-authored-by: Sam Stokes <sstokes@launchdarkly.com>

* 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 <91093020+louis-launchdarkly@users.noreply.github.com>

* 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 <dev@launchdarkly.com>
Co-authored-by: Eli Bishop <eli@launchdarkly.com>
Co-authored-by: Harpo roeder <hroeder@launchdarkly.com>
Co-authored-by: hroederld <46500128+hroederld@users.noreply.github.com>
Co-authored-by: Ben Woskow <bwoskow@launchdarkly.com>
Co-authored-by: Ben Woskow <48036130+bwoskow-ld@users.noreply.github.com>
Co-authored-by: Jacob Smith <jacob@jacobsmith.io>
Co-authored-by: Elliot <35050275+Apache-HB@users.noreply.github.com>
Co-authored-by: Kerrie Martinez <kyee@launchdarkly.com>
Co-authored-by: pellyg-ld <gpelly@launchdarkly.com>
Co-authored-by: Sam Stokes <sstokes@launchdarkly.com>
Co-authored-by: LaunchDarklyReleaseBot <launchdarklyreleasebot@launchdarkly.com>
Co-authored-by: Ember Stevens <ember.stevens@launchdarkly.com>
Co-authored-by: ember-stevens <79482775+ember-stevens@users.noreply.github.com>
Co-authored-by: Louis Chan <91093020+louis-launchdarkly@users.noreply.github.com>
Co-authored-by: Matthew M. Keeler <keelerm84@gmail.com>
Co-authored-by: Ben Levy <benjaminlevy007@gmail.com>
Co-authored-by: Ben Levy <ben@foxhound.systems>
Co-authored-by: Matthew M. Keeler <mkeeler@launchdarkly.com>
  • Loading branch information
20 people authored Mar 18, 2022
1 parent edc4efd commit f0a536c
Show file tree
Hide file tree
Showing 25 changed files with 437 additions and 102 deletions.
29 changes: 20 additions & 9 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ workflows:
name: Ruby 3.0
docker-image: cimg/ruby:3.0
- build-test-linux:
name: JRuby 9.2
docker-image: jruby:9.2-jdk
name: JRuby 9.3
docker-image: jruby:9.3-jdk
jruby: true

jobs:
Expand All @@ -41,19 +41,30 @@ jobs:
- when:
condition: <<parameters.jruby>>
steps:
- run: gem install jruby-openssl # required by bundler, no effect on Ruby MRI
- run: gem install jruby-openssl -v 0.11.0 # required by bundler, no effect on Ruby MRI
- run: apt-get update -y && apt-get install -y build-essential
- when:
condition:
not: <<parameters.jruby>>
steps:
- run: sudo apt-get update -y && sudo apt-get install -y build-essential
- run: ruby -v
- run: gem install bundler -v 2.2.10
- run: bundle _2.2.10_ install
- run: mkdir ./rspec
- run: bundle _2.2.10_ exec rspec --format documentation --format RspecJunitFormatter -o ./rspec/rspec.xml spec
- run: gem install bundler -v 2.2.33
- run: bundle _2.2.33_ install
- run: mkdir /tmp/circle-artifacts
- run: bundle _2.2.33_ exec rspec --format documentation --format RspecJunitFormatter -o /tmp/circle-artifacts/rspec.xml spec

- when:
condition:
not: <<parameters.jruby>>
steps:
- run: make build-contract-tests
- run:
command: make start-contract-test-service
background: true
- run: TEST_HARNESS_PARAMS="-junit /tmp/circle-artifacts/contract-tests-junit.xml" make run-contract-tests

- store_test_results:
path: ./rspec
path: /tmp/circle-artifacts
- store_artifacts:
path: ./rspec
path: /tmp/circle-artifacts
19 changes: 19 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
TEMP_TEST_OUTPUT=/tmp/contract-test-service.log

build-contract-tests:
@cd contract-tests && bundle _2.2.33_ install

start-contract-test-service:
@cd contract-tests && bundle _2.2.33_ exec ruby service.rb

start-contract-test-service-bg:
@echo "Test service output will be captured in $(TEMP_TEST_OUTPUT)"
@make start-contract-test-service >$(TEMP_TEST_OUTPUT) 2>&1 &

run-contract-tests:
@curl -s https://raw.githubusercontent.com/launchdarkly/sdk-test-harness/v1.0.0/downloader/run.sh \
| VERSION=v1 PARAMS="-url http://localhost:9000 -debug -stop-service-at-end $(TEST_HARNESS_PARAMS)" sh

contract-tests: build-contract-tests start-contract-test-service-bg run-contract-tests

.PHONY: build-contract-tests start-contract-test-service run-contract-tests contract-tests
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ LaunchDarkly Server-side SDK for Ruby
[![Gem Version](https://badge.fury.io/rb/launchdarkly-server-sdk.svg)](http://badge.fury.io/rb/launchdarkly-server-sdk)

[![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)
[![Security](https://hakiri.io/github/launchdarkly/ruby-server-sdk/master.svg)](https://hakiri.io/github/launchdarkly/ruby-server-sdk/master)
[![RubyDoc](https://img.shields.io/static/v1?label=docs+-+all+versions&message=reference&color=00add8)](https://www.rubydoc.info/gems/launchdarkly-server-sdk)
[![GitHub Pages](https://img.shields.io/static/v1?label=docs+-+latest&message=reference&color=00add8)](https://launchdarkly.github.io/ruby-server-sdk)

Expand Down
10 changes: 10 additions & 0 deletions contract-tests/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
source 'https://rubygems.org'

gem 'launchdarkly-server-sdk', path: '..'

gem 'sinatra', '~> 2.1'
# Sinatra can work with several server frameworks. In JRuby, we have to use glassfish (which
# is only available in JRuby). Otherwise we use thin (which is not available in JRuby).
gem 'glassfish', :platforms => :jruby
gem 'thin', :platforms => :ruby
gem 'json'
7 changes: 7 additions & 0 deletions contract-tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# SDK contract test service

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.

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.

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.
92 changes: 92 additions & 0 deletions contract-tests/client_entity.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
require 'ld-eventsource'
require 'json'
require 'net/http'

class ClientEntity
def initialize(log, config)
@log = log

opts = {}

opts[:logger] = log

if config[:streaming]
streaming = config[:streaming]
opts[:stream_uri] = streaming[:baseUri] if !streaming[:baseUri].nil?
opts[:initial_reconnect_delay] = streaming[:initialRetryDelayMs] / 1_000.0 if !streaming[:initialRetryDelayMs].nil?
end

if config[:events]
events = config[:events]
opts[:events_uri] = events[:baseUri] if events[:baseUri]
opts[:capacity] = events[:capacity] if events[:capacity]
opts[:diagnostic_opt_out] = !events[:enableDiagnostics]
opts[:all_attributes_private] = !!events[:allAttributesPrivate]
opts[:private_attribute_names] = events[:globalPrivateAttributes]
opts[:flush_interval] = (events[:flushIntervalMs] / 1_000) if events.has_key? :flushIntervalMs
opts[:inline_users_in_events] = events[:inlineUsers] || false
else
opts[:send_events] = false
end

startWaitTimeMs = config[:startWaitTimeMs] || 5_000

@client = LaunchDarkly::LDClient.new(
config[:credential],
LaunchDarkly::Config.new(opts),
startWaitTimeMs / 1_000.0)
end

def initialized?
@client.initialized?
end

def evaluate(params)
response = {}

if params[:detail]
detail = @client.variation_detail(params[:flagKey], params[:user], params[:defaultValue])
response[:value] = detail.value
response[:variationIndex] = detail.variation_index
response[:reason] = detail.reason
else
response[:value] = @client.variation(params[:flagKey], params[:user], params[:defaultValue])
end

response
end

def evaluate_all(params)
opts = {}
opts[:client_side_only] = params[:clientSideOnly] || false
opts[:with_reasons] = params[:withReasons] || false
opts[:details_only_for_tracked_flags] = params[:detailsOnlyForTrackedFlags] || false

@client.all_flags_state(params[:user], opts)
end

def track(params)
@client.track(params[:eventKey], params[:user], params[:data], params[:metricValue])
end

def identify(params)
@client.identify(params[:user])
end

def alias(params)
@client.alias(params[:user], params[:previousUser])
end

def flush_events
@client.flush
end

def log
@log
end

def close
@client.close
@log.info("Test ended")
end
end
112 changes: 112 additions & 0 deletions contract-tests/service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
require 'launchdarkly-server-sdk'
require 'json'
require 'logger'
require 'net/http'
require 'sinatra'

require './client_entity.rb'

configure :development do
disable :show_exceptions
end

$log = Logger.new(STDOUT)
$log.formatter = proc {|severity, datetime, progname, msg|
"[GLOBAL] #{datetime.strftime('%Y-%m-%d %H:%M:%S.%3N')} #{severity} #{progname} #{msg}\n"
}

set :port, 9000
set :logging, false

clients = {}
clientCounter = 0

get '/' do
{
capabilities: [
'server-side',
'all-flags-with-reasons',
'all-flags-client-side-only',
'all-flags-details-only-for-tracked-flags',
]
}.to_json
end

delete '/' do
$log.info("Test service has told us to exit")
Thread.new { sleep 1; exit }
return 204
end

post '/' do
opts = JSON.parse(request.body.read, :symbolize_names => true)
tag = "[#{opts[:tag]}]"

clientCounter += 1
clientId = clientCounter.to_s

log = Logger.new(STDOUT)
log.formatter = proc {|severity, datetime, progname, msg|
"#{tag} #{datetime.strftime('%Y-%m-%d %H:%M:%S.%3N')} #{severity} #{progname} #{msg}\n"
}

log.info("Starting client")
log.debug("Parameters: #{opts}")

client = ClientEntity.new(log, opts[:configuration])

if !client.initialized? && opts[:configuration][:initCanFail] == false
client.close()
return [500, nil, "Failed to initialize"]
end

clientResourceUrl = "/clients/#{clientId}"
clients[clientId] = client
return [201, {'Location' => clientResourceUrl}, nil]
end

post '/clients/:id' do |clientId|
client = clients[clientId]
return 404 if client.nil?

params = JSON.parse(request.body.read, :symbolize_names => true)

client.log.info("Processing request for client #{clientId}")
client.log.debug("Parameters: #{params}")

case params[:command]
when "evaluate"
response = client.evaluate(params[:evaluate])
return [200, nil, response.to_json]
when "evaluateAll"
response = {:state => client.evaluate_all(params[:evaluateAll])}
return [200, nil, response.to_json]
when "customEvent"
client.track(params[:customEvent])
return 201
when "identifyEvent"
client.identify(params[:identifyEvent])
return 201
when "aliasEvent"
client.alias(params[:aliasEvent])
return 201
when "flushEvents"
client.flush_events
return 201
end

return [400, nil, {:error => "Unknown command requested"}.to_json]
end

delete '/clients/:id' do |clientId|
client = clients[clientId]
return 404 if client.nil?
clients.delete(clientId)
client.close

return 204
end

error do
env['sinatra.error'].message
end
2 changes: 1 addition & 1 deletion launchdarkly-server-sdk.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
spec.required_ruby_version = ">= 2.5.0"

spec.add_development_dependency "aws-sdk-dynamodb", "~> 1.57"
spec.add_development_dependency "bundler", "2.2.10"
spec.add_development_dependency "bundler", "2.2.33"
spec.add_development_dependency "rspec", "~> 3.10"
spec.add_development_dependency "diplomat", "~> 2.4.2"
spec.add_development_dependency "redis", "~> 4.2"
Expand Down
17 changes: 17 additions & 0 deletions lib/ldclient-rb/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class Config
# @option opts [Integer] :capacity (10000) See {#capacity}.
# @option opts [Float] :flush_interval (30) See {#flush_interval}.
# @option opts [Float] :read_timeout (10) See {#read_timeout}.
# @option opts [Float] :initial_reconnect_delay (1) See {#initial_reconnect_delay}.
# @option opts [Float] :connect_timeout (2) See {#connect_timeout}.
# @option opts [Object] :cache_store See {#cache_store}.
# @option opts [Object] :feature_store See {#feature_store}.
Expand Down Expand Up @@ -54,6 +55,7 @@ def initialize(opts = {})
@flush_interval = opts[:flush_interval] || Config.default_flush_interval
@connect_timeout = opts[:connect_timeout] || Config.default_connect_timeout
@read_timeout = opts[:read_timeout] || Config.default_read_timeout
@initial_reconnect_delay = opts[:initial_reconnect_delay] || Config.default_initial_reconnect_delay
@feature_store = opts[:feature_store] || Config.default_feature_store
@stream = opts.has_key?(:stream) ? opts[:stream] : Config.default_stream
@use_ldd = opts.has_key?(:use_ldd) ? opts[:use_ldd] : Config.default_use_ldd
Expand Down Expand Up @@ -180,6 +182,13 @@ def offline?
#
attr_reader :read_timeout

#
# The initial delay before reconnecting after an error in the SSE client.
# This only applies to the streaming connection.
# @return [Float]
#
attr_reader :initial_reconnect_delay

#
# The connect timeout for network connections in seconds.
# @return [Float]
Expand Down Expand Up @@ -395,6 +404,14 @@ def self.default_read_timeout
10
end

#
# The default value for {#initial_reconnect_delay}.
# @return [Float] 1
#
def self.default_initial_reconnect_delay
1
end

#
# The default value for {#connect_timeout}.
# @return [Float] 10
Expand Down
Loading

0 comments on commit f0a536c

Please sign in to comment.