Skip to content

Commit e4f7f17

Browse files
authored
Merge pull request #535 from splitio/development
Release 8.4.0
2 parents cdbba26 + 496265d commit e4f7f17

File tree

65 files changed

+1846
-262
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+1846
-262
lines changed

CHANGES.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
CHANGES
22

3+
8.4.0 (May 3, 2024)
4+
- Fixed issue preventing Impressopns and Events posting if client.destroy is called before the post threads started
5+
- Added support for targeting rules based on semantic versions (https://semver.org/).
6+
37
8.3.1 (Mar 22, 2024)
48
- Fixed ruby process hanging due to failed thread.join command, when calling destroy and a http request still active.
59
- Fixed streaming notification parser. Issue ref: https://github.com/splitio/ruby-client/issues/511

lib/splitclient-rb.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,12 @@
9090
require 'splitclient-rb/engine/matchers/equal_to_boolean_matcher'
9191
require 'splitclient-rb/engine/matchers/equal_to_matcher'
9292
require 'splitclient-rb/engine/matchers/matches_string_matcher'
93+
require 'splitclient-rb/engine/matchers/semver'
94+
require 'splitclient-rb/engine/matchers/equal_to_semver_matcher'
95+
require 'splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher'
96+
require 'splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher'
97+
require 'splitclient-rb/engine/matchers/between_semver_matcher'
98+
require 'splitclient-rb/engine/matchers/in_list_semver_matcher'
9399
require 'splitclient-rb/engine/evaluator/splitter'
94100
require 'splitclient-rb/engine/impressions/noop_unique_keys_tracker'
95101
require 'splitclient-rb/engine/impressions/unique_keys_tracker'
@@ -105,6 +111,8 @@
105111
require 'splitclient-rb/engine/synchronizer'
106112
require 'splitclient-rb/utilitites'
107113

114+
require 'splitclient-rb/spec.rb'
115+
108116
# SSE
109117
require 'splitclient-rb/sse/event_source/client'
110118
require 'splitclient-rb/sse/event_source/event_parser'

lib/splitclient-rb/cache/repositories/events/memory_repository.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ def clear
2929
@adapter.clear
3030
end
3131

32+
def empty?
33+
@adapter.empty?
34+
end
35+
3236
def batch
3337
return [] if @config.events_queue_size.zero?
3438

lib/splitclient-rb/cache/repositories/events_repository.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ def post_events
2525
@config.log_found_exception(__method__.to_s, e)
2626
end
2727

28+
def empty?
29+
@repository.empty?
30+
end
31+
2832
protected
2933

3034
def metadata

lib/splitclient-rb/cache/repositories/splits_repository.rb

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,32 @@ module Cache
55
module Repositories
66
class SplitsRepository < Repository
77
attr_reader :adapter
8+
DEFAULT_CONDITIONS_TEMPLATE = [{
9+
conditionType: "ROLLOUT",
10+
matcherGroup: {
11+
combiner: "AND",
12+
matchers: [
13+
{
14+
keySelector: nil,
15+
matcherType: "ALL_KEYS",
16+
negate: false,
17+
userDefinedSegmentMatcherData: nil,
18+
whitelistMatcherData: nil,
19+
unaryNumericMatcherData: nil,
20+
betweenMatcherData: nil,
21+
dependencyMatcherData: nil,
22+
booleanMatcherData: nil,
23+
stringMatcherData: nil
24+
}]
25+
},
26+
partitions: [
27+
{
28+
treatment: "control",
29+
size: 100
30+
}
31+
],
32+
label: "targeting rule type unsupported by sdk"
33+
}]
834

935
def initialize(config, flag_sets_repository, flag_set_filter)
1036
super(config)
@@ -155,6 +181,10 @@ def add_feature_flag(split)
155181
remove_from_flag_sets(existing_split)
156182
end
157183

184+
if check_undefined_matcher(split)
185+
@config.logger.warn("Feature Flag #{split[:name]} has undefined matcher, setting conditions to default template.")
186+
split[:conditions] = SplitsRepository::DEFAULT_CONDITIONS_TEMPLATE
187+
end
158188
if !split[:sets].nil?
159189
for flag_set in split[:sets]
160190
if !@flag_sets.flag_set_exist?(flag_set)
@@ -170,6 +200,18 @@ def add_feature_flag(split)
170200
@adapter.set_string(namespace_key(".split.#{split[:name]}"), split.to_json)
171201
end
172202

203+
def check_undefined_matcher(split)
204+
for condition in split[:conditions]
205+
for matcher in condition[:matcherGroup][:matchers]
206+
if !SplitIoClient::Condition.instance_methods(false).map(&:to_s).include?("matcher_#{matcher[:matcherType].downcase}")
207+
@config.logger.error("Detected undefined matcher #{matcher[:matcherType].downcase} in feature flag #{split[:name]}")
208+
return true
209+
end
210+
end
211+
end
212+
return false
213+
end
214+
173215
def remove_feature_flag(split)
174216
decrease_tt_name_count(split[:trafficTypeName])
175217
remove_from_flag_sets(split)

lib/splitclient-rb/clients/split_client.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,16 @@ def get_treatments_with_config_by_flag_sets(key, flag_sets, attributes = {})
9696

9797
def destroy
9898
@config.logger.info('Split client shutdown started...') if @config.debug_enabled
99-
99+
if !@config.cache_adapter.is_a?(SplitIoClient::Cache::Adapters::RedisAdapter) && @config.impressions_mode != :none &&
100+
(!@impressions_repository.empty? || !@events_repository.empty?)
101+
@config.logger.debug("Impressions and/or Events cache is not empty")
102+
# Adding small delay to ensure sender threads are fully running
103+
sleep(0.1)
104+
if !@config.threads.key?(:impressions_sender) || !@config.threads.key?(:events_sender)
105+
@config.logger.debug("Periodic data recording thread has not started yet, waiting for service startup.")
106+
@config.threads[:start_sdk].join(5) if @config.threads.key?(:start_sdk)
107+
end
108+
end
100109
@config.threads.select { |name, thread| name.to_s.end_with? 'sender' }.values.each do |thread|
101110
thread.raise(SplitIoClient::SDKShutdownException)
102111
thread.join

lib/splitclient-rb/engine/api/client.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ def initialize(config)
1010
end
1111

1212
def get_api(url, api_key, params = {}, cache_control_headers = false)
13+
api_client.options.params_encoder.sort_params = false
1314
api_client.get(url, params) do |req|
1415
req.headers = common_headers(api_key).merge('Accept-Encoding' => 'gzip')
1516
req.headers = req.headers.merge('Cache-Control' => 'no-cache') if cache_control_headers
@@ -29,7 +30,7 @@ def post_api(url, api_key, data, headers = {}, params = {})
2930
req.headers = common_headers(api_key)
3031
.merge('Content-Type' => 'application/json')
3132
.merge(headers)
32-
33+
3334
machine_ip = @config.machine_ip
3435
machine_name = @config.machine_name
3536

@@ -55,6 +56,7 @@ def api_client
5556
@api_client ||= Faraday.new do |builder|
5657
builder.use SplitIoClient::FaradayMiddleware::Gzip
5758
builder.adapter :net_http_persistent
59+
builder.options.params_encoder = Faraday::FlatParamsEncoder
5860
end
5961
end
6062

lib/splitclient-rb/engine/api/splits.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,20 @@ module SplitIoClient
44
module Api
55
# Retrieves split definitions from the Split Backend
66
class Splits < Client
7+
78
def initialize(api_key, config, telemetry_runtime_producer)
89
super(config)
910
@api_key = api_key
1011
@telemetry_runtime_producer = telemetry_runtime_producer
1112
@flag_sets_filter = @config.flag_sets_filter
1213
end
1314

14-
def since(since, fetch_options = { cache_control_headers: false, till: nil, sets: nil })
15+
def since(since, fetch_options = { cache_control_headers: false, till: nil, sets: nil})
1516
start = Time.now
1617

17-
params = { since: since }
18-
params[:till] = fetch_options[:till] unless fetch_options[:till].nil?
18+
params = { s: SplitIoClient::Spec::FeatureFlags::SPEC_VERSION, since: since }
1919
params[:sets] = @flag_sets_filter.join(",") unless @flag_sets_filter.empty?
20+
params[:till] = fetch_options[:till] unless fetch_options[:till].nil?
2021
@config.logger.debug("Fetching from splitChanges with #{params}: ")
2122
response = get_api("#{@config.base_uri}/splitChanges", @api_key, params, fetch_options[:cache_control_headers])
2223
if response.status == 414

lib/splitclient-rb/engine/auth_api_client.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def initialize(config, telemetry_runtime_producer)
1414

1515
def authenticate(api_key)
1616
start = Time.now
17-
response = @api_client.get_api(@config.auth_service_url, api_key)
17+
response = @api_client.get_api("#{@config.auth_service_url}?s=#{SplitIoClient::Spec::FeatureFlags::SPEC_VERSION}", api_key)
1818

1919
return process_success(response, start) if response.success?
2020

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# frozen_string_literal: true
2+
3+
module SplitIoClient
4+
class BetweenSemverMatcher < Matcher
5+
MATCHER_TYPE = 'BETWEEN_SEMVER'
6+
7+
attr_reader :attribute
8+
9+
def initialize(attribute, start_value, end_value, logger, validator)
10+
super(logger)
11+
@validator = validator
12+
@attribute = attribute
13+
@semver_start = SplitIoClient::Semver.build(start_value, logger)
14+
@semver_end = SplitIoClient::Semver.build(end_value, logger)
15+
@logger = logger
16+
end
17+
18+
def match?(args)
19+
return false unless verify_semver_arg?(args, 'BetweenSemverMatcher')
20+
21+
value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger)
22+
if value_to_match.nil? || @semver_start.nil? || @semver_end.nil?
23+
@logger.error('betweenStringMatcherData is required for BETWEEN_SEMVER matcher type')
24+
return false
25+
26+
end
27+
matches = ([0, -1].include?(@semver_start.compare(value_to_match)) &&
28+
[0, 1].include?(@semver_end.compare(value_to_match)))
29+
@logger.debug("[BetweenMatcher] #{value_to_match} matches -> #{matches}")
30+
matches
31+
end
32+
end
33+
end

0 commit comments

Comments
 (0)