Skip to content

Commit 61e502f

Browse files
committed
Added old spec support
1 parent 14cee73 commit 61e502f

9 files changed

Lines changed: 163 additions & 75 deletions

File tree

lib/splitclient-rb/cache/fetchers/split_fetcher.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ def call
2525
def fetch_splits(fetch_options = { cache_control_headers: false, till: nil })
2626
@semaphore.synchronize do
2727
data = splits_since(@splits_repository.get_change_number, @rule_based_segments_repository.get_change_number, fetch_options)
28-
SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(@splits_repository, data[:ff][:d], data[:ff][:t], @config)
29-
SplitIoClient::Helpers::RepositoryHelper.update_rule_based_segment_repository(@rule_based_segments_repository, data[:rbs][:d], data[:rbs][:t], @config)
28+
SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(@splits_repository, data[:ff][:d], data[:ff][:t], @config, @splits_api.clear_storage)
29+
SplitIoClient::Helpers::RepositoryHelper.update_rule_based_segment_repository(@rule_based_segments_repository, data[:rbs][:d], data[:rbs][:t], @config, @splits_api.clear_storage)
3030
@splits_repository.set_segment_names(data[:segment_names])
3131
@rule_based_segments_repository.set_segment_names(data[:segment_names])
3232
@config.logger.debug("segments seen(#{data[:segment_names].length}): #{data[:segment_names].to_a}") if @config.debug_enabled

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ def post_api(url, api_key, data, headers = {}, params = {})
5050
raise e, 'Split SDK failed to connect to backend to post information', e.backtrace
5151
end
5252

53+
def sdk_url_overriden?
54+
@config.sdk_url_overriden?
55+
end
5356
private
5457

5558
def api_client

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

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,68 @@ module Api
55
# Retrieves split definitions from the Split Backend
66
class Splits < Client
77

8+
PROXY_CHECK_INTERVAL_SECONDS = 24 * 60 * 60
9+
SPEC_1_1 = "1.1"
10+
811
def initialize(api_key, config, telemetry_runtime_producer)
912
super(config)
1013
@api_key = api_key
1114
@telemetry_runtime_producer = telemetry_runtime_producer
1215
@flag_sets_filter = @config.flag_sets_filter
16+
@spec_version = SplitIoClient::Spec::FeatureFlags::SPEC_VERSION
17+
@last_proxy_check_timestamp = 0
18+
@clear_storage = false
1319
end
1420

1521
def since(since, since_rbs, fetch_options = { cache_control_headers: false, till: nil, sets: nil})
1622
start = Time.now
23+
24+
if check_last_proxy_check_timestamp
25+
puts "switching to new spec"
26+
@spec_version = SplitIoClient::Spec::FeatureFlags::SPEC_VERSION
27+
@config.logger.debug("Switching to new Feature flag spec #{@spec_version} and fetching.")
28+
since = -1
29+
since_rbs = -1
30+
fetch_options = { cache_control_headers: false, till: nil, sets: nil}
31+
end
32+
33+
if @spec_version == Splits::SPEC_1_1
34+
params = { s: @spec_version, since: since }
35+
else
36+
params = { s: @spec_version, since: since, rbSince: since_rbs }
37+
end
1738

18-
params = { s: SplitIoClient::Spec::FeatureFlags::SPEC_VERSION, since: since, rbSince: since_rbs }
1939
params[:sets] = @flag_sets_filter.join(",") unless @flag_sets_filter.empty?
2040
params[:till] = fetch_options[:till] unless fetch_options[:till].nil?
2141
@config.logger.debug("Fetching from splitChanges with #{params}: ")
2242
response = get_api("#{@config.base_uri}/splitChanges", @api_key, params, fetch_options[:cache_control_headers])
43+
2344
if response.status == 414
2445
@config.logger.error("Error fetching feature flags; the amount of flag sets provided are too big, causing uri length error.")
2546
raise ApiException.new response.body, 414
2647
end
48+
49+
if response.status == 400 and sdk_url_overriden? and @spec_version == SplitIoClient::Spec::FeatureFlags::SPEC_VERSION
50+
@config.logger.warn("Detected proxy response error, changing spec version from #{@spec_version} to #{Splits::SPEC_1_1} and re-fetching.")
51+
@spec_version = Splits::SPEC_1_1
52+
@last_proxy_check_timestamp = Time.now
53+
return since(since, 0, fetch_options = {cache_control_headers: fetch_options[:cache_control_headers], till: fetch_options[:till],
54+
sets: fetch_options[:sets]})
55+
end
56+
2757
if response.success?
28-
result = objects_with_segment_names(response.body)
58+
result = JSON.parse(response.body, symbolize_names: true)
59+
if @spec_version == Splits::SPEC_1_1
60+
result = convert_to_newSPEC(result)
61+
end
62+
63+
result = objects_with_segment_names(result)
2964

65+
if @spec_version == SplitIoClient::Spec::FeatureFlags::SPEC_VERSION
66+
@clear_storage = @last_proxy_check_timestamp != 0
67+
@last_proxy_check_timestamp = 0
68+
end
69+
3070
unless result[:ff][:d].empty?
3171
@config.split_logger.log_if_debug("#{result[:ff][:d].length} feature flags retrieved. since=#{since}")
3272
end
@@ -52,10 +92,13 @@ def since(since, since_rbs, fetch_options = { cache_control_headers: false, till
5292
end
5393
end
5494

95+
def clear_storage
96+
@clear_storage
97+
end
98+
5599
private
56100

57-
def objects_with_segment_names(objects_json)
58-
parsed_objects = JSON.parse(objects_json, symbolize_names: true)
101+
def objects_with_segment_names(parsed_objects)
59102
parsed_objects[:segment_names] = Set.new
60103
parsed_objects[:segment_names] =
61104
parsed_objects[:ff][:d].each_with_object(Set.new) do |split, splits|
@@ -76,6 +119,14 @@ def objects_with_segment_names(objects_json)
76119

77120
parsed_objects
78121
end
122+
123+
def check_last_proxy_check_timestamp
124+
@spec_version == Splits::SPEC_1_1 and ((Time.now - @last_proxy_check_timestamp) >= Splits::PROXY_CHECK_INTERVAL_SECONDS)
125+
end
126+
127+
def convert_to_newSPEC(body)
128+
{:ff => {:d => body[:splits], :s => body[:since], :t => body[:till]}, :rbs => {:d => [], :s => -1, :t => -1}}
129+
end
79130
end
80131
end
81132
end

lib/splitclient-rb/helpers/repository_helper.rb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
module SplitIoClient
44
module Helpers
55
class RepositoryHelper
6-
def self.update_feature_flag_repository(feature_flag_repository, feature_flags, change_number, config)
6+
def self.update_feature_flag_repository(feature_flag_repository, feature_flags, change_number, config, clear_storage)
77
to_add = []
88
to_delete = []
99
feature_flags.each do |feature_flag|
@@ -23,10 +23,11 @@ def self.update_feature_flag_repository(feature_flag_repository, feature_flags,
2323
config.logger.debug("storing feature flag (#{feature_flag[:name]})") if config.debug_enabled
2424
to_add.push(feature_flag)
2525
end
26+
feature_flag_repository.clear if clear_storage
2627
feature_flag_repository.update(to_add, to_delete, change_number)
2728
end
2829

29-
def self.update_rule_based_segment_repository(rule_based_segment_repository, rule_based_segments, change_number, config)
30+
def self.update_rule_based_segment_repository(rule_based_segment_repository, rule_based_segments, change_number, config, clear_storage)
3031
to_add = []
3132
to_delete = []
3233
rule_based_segments.each do |rule_based_segment|
@@ -39,6 +40,8 @@ def self.update_rule_based_segment_repository(rule_based_segment_repository, rul
3940
config.logger.debug("storing rule based segment (#{rule_based_segment[:name]})") if config.debug_enabled
4041
to_add.push(rule_based_segment)
4142
end
43+
rule_based_segment_repository.clear if clear_storage
44+
4245
rule_based_segment_repository.update(to_add, to_delete, change_number)
4346
end
4447
end

lib/splitclient-rb/split_config.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,10 @@ def consumer?
645645
@mode.equal?(:consumer)
646646
end
647647

648+
def sdk_url_overriden?
649+
return @base_uri != SplitConfig.default_base_uri
650+
end
651+
648652
#
649653
# gets the hostname where the sdk gem is running
650654
#

lib/splitclient-rb/sse/workers/splits_worker.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ def update_feature_flag(notification)
7171
new_split = return_object_from_json(notification)
7272
SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(@feature_flags_repository,
7373
[new_split],
74-
notification.data['changeNumber'], @config)
74+
notification.data['changeNumber'], @config, false)
7575
fetch_segments_if_not_exists(Helpers::Util.segment_names_by_object(new_split, "IN_SEGMENT"), @feature_flags_repository)
7676
if fetch_rule_based_segments_if_not_exists(Helpers::Util.segment_names_by_object(new_split, "IN_RULE_BASED_SEGMENT"), notification.data['changeNumber'])
7777
return true
@@ -93,7 +93,7 @@ def update_rule_based_segment(notification)
9393
new_rb_segment = return_object_from_json(notification)
9494
SplitIoClient::Helpers::RepositoryHelper.update_rule_based_segment_repository(@rule_based_segment_repository,
9595
[new_rb_segment],
96-
notification.data['changeNumber'], @config)
96+
notification.data['changeNumber'], @config, false)
9797
fetch_segments_if_not_exists(Helpers::Util.segment_names_by_object(new_rb_segment, "IN_SEGMENT"), @rule_based_segment_repository)
9898

9999
# TODO: enable when telemetry spec is added

spec/engine/api/splits_spec.rb

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
stub_request(:get, 'https://sdk.split.io/api/splitChanges?s=1.3&since=-1&rbSince=-1')
2222
.to_return(status: 200, body: splits)
2323

24-
parsed_splits = splits_api.send(:objects_with_segment_names, splits)
24+
parsed_splits = splits_api.send(:objects_with_segment_names, JSON.parse(splits, symbolize_names: true))
2525

2626
expect(parsed_splits[:segment_names]).to eq(Set.new(%w[demo employees]))
2727
end
@@ -183,4 +183,53 @@
183183
)
184184
end
185185
end
186+
187+
context 'old spec tests' do
188+
let(:old_spec_splits) { File.read(File.expand_path(File.join(File.dirname(__FILE__), '../../test_data/rule_based_segments/split_old_spec.json'))) }
189+
let(:config) do
190+
SplitIoClient::SplitConfig.new(
191+
logger: Logger.new(log),
192+
debug_enabled: true,
193+
transport_debug_enabled: true,
194+
base_uri: "https://proxy-server/api"
195+
)
196+
end
197+
let(:log) { StringIO.new }
198+
let(:telemetry_runtime_producer) { SplitIoClient::Telemetry::RuntimeProducer.new(config) }
199+
let(:splits_api) { described_class.new('', config, telemetry_runtime_producer) }
200+
201+
it 'switch to old spec url whith proper conditions' do
202+
stub_request(:get, 'https://proxy-server/api/splitChanges?s=1.3&since=-1&rbSince=-1')
203+
.to_return(status: 400, body: '')
204+
stub_request(:get, 'https://proxy-server/api/splitChanges?s=1.1&since=-1')
205+
.to_return(status: 200, body: old_spec_splits)
206+
207+
parsed_splits = splits_api.since(-1, -1)
208+
209+
expect(parsed_splits[:ff][:d].length()).to eq(7)
210+
expect(parsed_splits[:ff][:t]).to eq(1457726098069)
211+
expect(parsed_splits[:ff][:s]).to eq(-1)
212+
expect(parsed_splits[:rbs]).to eq({:d => [], :s => -1, :t => -1})
213+
expect(splits_api.clear_storage).to eq(false)
214+
end
215+
216+
it 'check new spec after last proxy timestamp expires' do
217+
stub_request(:get, 'https://proxy-server/api/splitChanges?s=1.3&since=-1&rbSince=-1')
218+
.to_return({status: 400, body: ''}, {status: 200, body: splits})
219+
stub_request(:get, 'https://proxy-server/api/splitChanges?s=1.1&since=-1')
220+
.to_return(status: 200, body: old_spec_splits)
221+
222+
parsed_splits = splits_api.since(-1, -1)
223+
expect(parsed_splits[:ff][:d].length()).to eq(7)
224+
expect(splits_api.instance_variable_get(:@spec_version)).to eq(SplitIoClient::Api::Splits::SPEC_1_1)
225+
226+
SplitIoClient::Api::Splits::PROXY_CHECK_INTERVAL_SECONDS = 1
227+
sleep 1
228+
parsed_splits = splits_api.since(-1, -1)
229+
expect(splits_api.clear_storage).to eq(true)
230+
expect(parsed_splits[:ff][:d].length()).to eq(2)
231+
expect(parsed_splits[:rbs][:d].length()).to eq(1)
232+
expect(splits_api.instance_variable_get(:@spec_version)).to eq(SplitIoClient::Spec::FeatureFlags::SPEC_VERSION)
233+
end
234+
end
186235
end
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
describe SplitIoClient do
6+
context 'old spec tests' do
7+
let(:old_spec_splits) { File.read(File.expand_path(File.join(File.dirname(__FILE__), '../test_data/rule_based_segments/split_old_spec.json'))) }
8+
9+
it 'check new spec after last proxy timestamp expires' do
10+
splits_rbs = File.read(File.join(SplitIoClient.root, 'spec/test_data/rule_based_segments/rule_base_segments.json'))
11+
12+
stub_request(:get, 'https://proxy-server/api/splitChanges?s=1.3&since=-1&rbSince=-1')
13+
.to_return({status: 400, body: ''}, {status: 200, body: splits_rbs})
14+
stub_request(:get, "https://sdk.split.io/api/splitChanges?rbSince=1506703262916&s=1.3&since=1506703262916")
15+
.to_return(status: 200, body: '')
16+
stub_request(:get, 'https://proxy-server/api/splitChanges?s=1.1&since=-1')
17+
.to_return(status: 200, body: old_spec_splits)
18+
stub_request(:get, "https://proxy-server/api/splitChanges?s=1.1&since=1457726098069")
19+
.to_return(status: 200, body: '')
20+
stub_request(:post, "https://telemetry.split.io/api/v1/metrics/config")
21+
.to_return(status: 200, body: '')
22+
23+
factory_old_spec =
24+
SplitIoClient::SplitFactory.new('test_api_key',
25+
{impressions_mode: :none,
26+
features_refresh_rate: 2,
27+
base_uri: "https://proxy-server/api",
28+
streaming_enabled: false})
29+
30+
SplitIoClient::Api::Splits::PROXY_CHECK_INTERVAL_SECONDS = 1
31+
client_old_spec = factory_old_spec.client
32+
client_old_spec.block_until_ready
33+
expect(client_old_spec.get_treatment('whitelisted_user', 'whitelist_feature')).to eq('on')
34+
35+
sleep 1
36+
split_fetcher = factory_old_spec.instance_variable_get(:@split_fetcher)
37+
split_fetcher.fetch_splits
38+
sleep 1
39+
expect(client_old_spec.get_treatment('bilal@split.io', 'rbs_feature_flag', {:email => 'bilal@split.io'})).to eq('on')
40+
end
41+
end
42+
end

spec/test_data/rule_based_segments/split_old_spec.json

Lines changed: 0 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -141,70 +141,6 @@
141141
],
142142
"sets": ["set3"]
143143
},
144-
{
145-
"orgId": null,
146-
"environment": null,
147-
"trafficTypeId": null,
148-
"trafficTypeName": null,
149-
"name": "sample_feature",
150-
"seed": 1548363147,
151-
"status": "ACTIVE",
152-
"killed": false,
153-
"changeNumber": 123,
154-
"defaultTreatment": "off",
155-
"configurations": {
156-
"on": "{\"size\":15,\"test\":20}"
157-
},
158-
"conditions": [
159-
{
160-
"matcherGroup": {
161-
"combiner": "AND",
162-
"matchers": [
163-
{
164-
"matcherType": "IN_SEGMENT",
165-
"negate": false,
166-
"userDefinedSegmentMatcherData": {
167-
"segmentName": "employees"
168-
},
169-
"whitelistMatcherData": null
170-
}
171-
]
172-
},
173-
"partitions": [
174-
{
175-
"treatment": "on",
176-
"size": 100
177-
}
178-
]
179-
},
180-
{
181-
"matcherGroup": {
182-
"combiner": "AND",
183-
"matchers": [
184-
{
185-
"matcherType": "IN_SEGMENT",
186-
"negate": false,
187-
"userDefinedSegmentMatcherData": {
188-
"segmentName": "human_beigns"
189-
},
190-
"whitelistMatcherData": null
191-
}
192-
]
193-
},
194-
"partitions": [
195-
{
196-
"treatment": "on",
197-
"size": 30
198-
},
199-
{
200-
"treatment": "off",
201-
"size": 70
202-
}
203-
]
204-
}
205-
],
206-
"sets": ["set1"]
207-
},
208144
{
209145
"orgId": null,
210146
"environment": null,

0 commit comments

Comments
 (0)