splitclient-rb 8.2.0 → 8.3.0.pre.rc1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cb1d386fbacf462f6fb540e13b07037fef8bfeab4fcbbda8e1197f63cf64e101
4
- data.tar.gz: 758c3e6e1cd5d8a6488d283d5eac29fce2942d0b14eb2757b3825a19bbae86f9
3
+ metadata.gz: 32f8f9ccb8bcbb2b703430a4db530ee1482e21bfbbb3102ed38d2bd4442894a1
4
+ data.tar.gz: 821b33d85d0555a8fd3db9e5c5d8faabd0c544bfc25b1e223f7fdbb24ce502b1
5
5
  SHA512:
6
- metadata.gz: 9ad6c553a9af8afe5f2c91db3160d6043d7fd3e2f7c7c2e935c061dc0e49bfae59c5ddb47853c416d67ee692bd0c21c1e0d6c1b88dc51d713ead0a64f0c3c7c9
7
- data.tar.gz: d4564ffdac1484039e244469b9c845a4def9d817a3a70ab05d1e9b5df78d274a864c2e2c98bb4cc952127b121656ad5522cfa6c6e583fca4422a26953593d157
6
+ metadata.gz: e7d8a70443e65336c2ac3ca09c115f0a7a9cf42f777da8ef4d77f814ea4958b21dc6dcfa1a673ae93952f4611c7a480c4eefdc111bf019de26622f39ae67241f
7
+ data.tar.gz: 15a35c6879e2e85def18e0e3bdf4a2df3d2e2b886497b514fc9f5db4051a881d4d90c79370f0edf9f21bc9a088b743bb86a1cbb2676a57f044a27c2e74d43e81
@@ -17,7 +17,7 @@ module SplitIoClient
17
17
  fetch_splits
18
18
  return
19
19
  end
20
-
20
+
21
21
  splits_thread
22
22
  end
23
23
 
@@ -25,13 +25,8 @@ module SplitIoClient
25
25
  @semaphore.synchronize do
26
26
  data = splits_since(@splits_repository.get_change_number, fetch_options)
27
27
 
28
- data[:splits] && data[:splits].each do |split|
29
- add_split_unless_archived(split)
30
- end
31
-
28
+ SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(@splits_repository, data[:splits], data[:till], @config)
32
29
  @splits_repository.set_segment_names(data[:segment_names])
33
- @splits_repository.set_change_number(data[:till])
34
-
35
30
  @config.logger.debug("segments seen(#{data[:segment_names].length}): #{data[:segment_names].to_a}") if @config.debug_enabled
36
31
 
37
32
  { segment_names: data[:segment_names], success: true }
@@ -64,28 +59,6 @@ module SplitIoClient
64
59
  splits_api.since(since, fetch_options)
65
60
  end
66
61
 
67
- def add_split_unless_archived(split)
68
- if Engine::Models::Split.archived?(split)
69
- @config.logger.debug("Seeing archived feature flag #{split[:name]}") if @config.debug_enabled
70
-
71
- remove_archived_split(split)
72
- else
73
- store_split(split)
74
- end
75
- end
76
-
77
- def remove_archived_split(split)
78
- @config.logger.debug("removing feature flag from store(#{split})") if @config.debug_enabled
79
-
80
- @splits_repository.remove_split(split)
81
- end
82
-
83
- def store_split(split)
84
- @config.logger.debug("storing feature flag (#{split[:name]})") if @config.debug_enabled
85
-
86
- @splits_repository.add_split(split)
87
- end
88
-
89
62
  def splits_api
90
63
  @splits_api ||= SplitIoClient::Api::Splits.new(@api_key, @config, @telemetry_runtime_producer)
91
64
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
5
+ module SplitIoClient
6
+ module Cache
7
+ module Filter
8
+ class FlagSetsFilter
9
+ def initialize(flag_sets = [])
10
+ @flag_sets = Set.new(flag_sets)
11
+ @should_filter = @flag_sets.any?
12
+ end
13
+
14
+ def should_filter?
15
+ @should_filter
16
+ end
17
+
18
+ def flag_set_exist?(flag_set)
19
+ return true unless @should_filter
20
+
21
+ if not flag_set.is_a?(String) or flag_set.empty?
22
+ return false
23
+ end
24
+
25
+ @flag_sets.intersection([flag_set]).any?
26
+ end
27
+
28
+ def intersect?(flag_sets)
29
+ return true unless @should_filter
30
+
31
+ if not flag_sets.is_a?(Array) or flag_sets.empty?
32
+ return false
33
+ end
34
+
35
+ @flag_sets.intersection(Set.new(flag_sets)).any?
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,40 @@
1
+ require 'concurrent'
2
+
3
+ module SplitIoClient
4
+ module Cache
5
+ module Repositories
6
+ class MemoryFlagSetsRepository
7
+ def initialize(flag_sets = [])
8
+ @sets_feature_flag_map = {}
9
+ flag_sets.each{ |flag_set| @sets_feature_flag_map[flag_set] = Set[] }
10
+ end
11
+
12
+ def flag_set_exist?(flag_set)
13
+ @sets_feature_flag_map.key?(flag_set)
14
+ end
15
+
16
+ def get_flag_sets(flag_sets)
17
+ to_return = Array.new
18
+ flag_sets.each { |flag_set| to_return.concat(@sets_feature_flag_map[flag_set].to_a)}
19
+ to_return.uniq
20
+ end
21
+
22
+ def add_flag_set(flag_set)
23
+ @sets_feature_flag_map[flag_set] = Set[] if !flag_set_exist?(flag_set)
24
+ end
25
+
26
+ def remove_flag_set(flag_set)
27
+ @sets_feature_flag_map.delete(flag_set) if flag_set_exist?(flag_set)
28
+ end
29
+
30
+ def add_feature_flag_to_flag_set(flag_set, feature_flag)
31
+ @sets_feature_flag_map[flag_set].add(feature_flag) if flag_set_exist?(flag_set)
32
+ end
33
+
34
+ def remove_feature_flag_from_flag_set(flag_set, feature_flag)
35
+ @sets_feature_flag_map[flag_set].delete(feature_flag) if flag_set_exist?(flag_set)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,49 @@
1
+ require 'concurrent'
2
+
3
+ module SplitIoClient
4
+ module Cache
5
+ module Repositories
6
+ class RedisFlagSetsRepository < Repository
7
+
8
+ def initialize(config)
9
+ super(config)
10
+ @adapter = SplitIoClient::Cache::Adapters::RedisAdapter.new(@config.redis_url)
11
+ end
12
+
13
+ def flag_set_exist?(flag_set)
14
+ @adapter.exists?(namespace_key(".flagSet.#{flag_set}"))
15
+ end
16
+
17
+ def get_flag_sets(flag_sets)
18
+ result = @adapter.redis.pipelined do |pipeline|
19
+ flag_sets.each do |flag_set|
20
+ pipeline.smembers(namespace_key(".flagSet.#{flag_set}"))
21
+ end
22
+ end
23
+ to_return = Array.new
24
+ result.each do |flag_set|
25
+ flag_set.each { |feature_flag_name| to_return.push(feature_flag_name.to_s)}
26
+ end
27
+ to_return.uniq
28
+ end
29
+
30
+ def add_flag_set(flag_set)
31
+ # not implemented
32
+ end
33
+
34
+ def remove_flag_set(flag_set)
35
+ # not implemented
36
+ end
37
+
38
+ def add_feature_flag_to_flag_set(flag_set, feature_flag)
39
+ # not implemented
40
+ end
41
+
42
+ def remove_feature_flag_from_flag_set(flag_set, feature_flag)
43
+ # not implemented
44
+ end
45
+
46
+ end
47
+ end
48
+ end
49
+ end
@@ -6,7 +6,7 @@ module SplitIoClient
6
6
  class SplitsRepository < Repository
7
7
  attr_reader :adapter
8
8
 
9
- def initialize(config)
9
+ def initialize(config, flag_sets_repository, flag_set_filter)
10
10
  super(config)
11
11
  @tt_cache = {}
12
12
  @adapter = case @config.cache_adapter.class.to_s
@@ -15,48 +15,18 @@ module SplitIoClient
15
15
  else
16
16
  @config.cache_adapter
17
17
  end
18
+ @flag_sets = flag_sets_repository
19
+ @flag_set_filter = flag_set_filter
18
20
  unless @config.mode.equal?(:consumer)
19
21
  @adapter.set_string(namespace_key('.splits.till'), '-1')
20
22
  @adapter.initialize_map(namespace_key('.segments.registered'))
21
23
  end
22
24
  end
23
25
 
24
- def add_split(split)
25
- return unless split[:name]
26
- existing_split = get_split(split[:name])
27
-
28
- if(!existing_split)
29
- increase_tt_name_count(split[:trafficTypeName])
30
- elsif(existing_split[:trafficTypeName] != split[:trafficTypeName])
31
- increase_tt_name_count(split[:trafficTypeName])
32
- decrease_tt_name_count(existing_split[:trafficTypeName])
33
- end
34
-
35
- @adapter.set_string(namespace_key(".split.#{split[:name]}"), split.to_json)
36
- end
37
-
38
- def remove_split(split)
39
- tt_name = split[:trafficTypeName]
40
-
41
- decrease_tt_name_count(split[:trafficTypeName])
42
-
43
- @adapter.delete(namespace_key(".split.#{split[:name]}"))
44
- end
45
-
46
- def get_splits(names, symbolize_names = true)
47
- splits = {}
48
- split_names = names.map { |name| namespace_key(".split.#{name}") }
49
- splits.merge!(
50
- @adapter
51
- .multiple_strings(split_names)
52
- .map { |name, data| [name.gsub(namespace_key('.split.'), ''), data] }.to_h
53
- )
54
-
55
- splits.map do |name, data|
56
- parsed_data = data ? JSON.parse(data, symbolize_names: true) : nil
57
- split_name = symbolize_names ? name.to_sym : name
58
- [split_name, parsed_data]
59
- end.to_h
26
+ def update(to_add, to_delete, new_change_number)
27
+ to_add.each{ |feature_flag| add_feature_flag(feature_flag) }
28
+ to_delete.each{ |feature_flag| remove_feature_flag(feature_flag) }
29
+ set_change_number(new_change_number)
60
30
  end
61
31
 
62
32
  def get_split(name)
@@ -65,8 +35,13 @@ module SplitIoClient
65
35
  JSON.parse(split, symbolize_names: true) if split
66
36
  end
67
37
 
68
- def splits
69
- get_splits(split_names, false)
38
+ def splits(filtered_names=nil)
39
+ symbolize = true
40
+ if filtered_names.nil?
41
+ filtered_names = split_names
42
+ symbolize = false
43
+ end
44
+ get_splits(filtered_names, symbolize)
70
45
  end
71
46
 
72
47
  def traffic_type_exists(tt_name)
@@ -144,8 +119,94 @@ module SplitIoClient
144
119
  split_names.length
145
120
  end
146
121
 
122
+ def get_feature_flags_by_sets(flag_sets)
123
+ sets_to_fetch = Array.new
124
+ flag_sets.each do |flag_set|
125
+ unless @flag_sets.flag_set_exist?(flag_set)
126
+ @config.logger.warn("Flag set #{flag_set} is not part of the configured flag set list, ignoring it.")
127
+ next
128
+ end
129
+ sets_to_fetch.push(flag_set)
130
+ end
131
+ @flag_sets.get_flag_sets(flag_sets)
132
+ end
133
+
134
+ def is_flag_set_exist(flag_set)
135
+ @flag_sets.flag_set_exist?(flag_set)
136
+ end
137
+
138
+ def flag_set_filter
139
+ @flag_set_filter
140
+ end
141
+
147
142
  private
148
143
 
144
+ def add_feature_flag(split)
145
+ return unless split[:name]
146
+ existing_split = get_split(split[:name])
147
+
148
+ if(!existing_split)
149
+ increase_tt_name_count(split[:trafficTypeName])
150
+ elsif(existing_split[:trafficTypeName] != split[:trafficTypeName])
151
+ increase_tt_name_count(split[:trafficTypeName])
152
+ decrease_tt_name_count(existing_split[:trafficTypeName])
153
+ remove_from_flag_sets(existing_split)
154
+ elsif(existing_split[:sets] != split[:sets])
155
+ remove_from_flag_sets(existing_split)
156
+ end
157
+
158
+ if !split[:sets].nil?
159
+ for flag_set in split[:sets]
160
+ if !@flag_sets.flag_set_exist?(flag_set)
161
+ if @flag_set_filter.should_filter?
162
+ next
163
+ end
164
+ @flag_sets.add_flag_set(flag_set)
165
+ end
166
+ @flag_sets.add_feature_flag_to_flag_set(flag_set, split[:name])
167
+ end
168
+ end
169
+
170
+ @adapter.set_string(namespace_key(".split.#{split[:name]}"), split.to_json)
171
+ end
172
+
173
+ def remove_feature_flag(split)
174
+ tt_name = split[:trafficTypeName]
175
+
176
+ decrease_tt_name_count(split[:trafficTypeName])
177
+ remove_from_flag_sets(split)
178
+ @adapter.delete(namespace_key(".split.#{split[:name]}"))
179
+ end
180
+
181
+ def get_splits(names, symbolize_names = true)
182
+ splits = {}
183
+ split_names = names.map { |name| namespace_key(".split.#{name}") }
184
+ splits.merge!(
185
+ @adapter
186
+ .multiple_strings(split_names)
187
+ .map { |name, data| [name.gsub(namespace_key('.split.'), ''), data] }.to_h
188
+ )
189
+
190
+ splits.map do |name, data|
191
+ parsed_data = data ? JSON.parse(data, symbolize_names: true) : nil
192
+ split_name = symbolize_names ? name.to_sym : name
193
+ [split_name, parsed_data]
194
+ end.to_h
195
+ end
196
+
197
+ def remove_from_flag_sets(feature_flag)
198
+ name = feature_flag[:name]
199
+ flag_sets = get_split(name)[:sets] if exists?(name)
200
+ if !flag_sets.nil?
201
+ for flag_set in flag_sets
202
+ @flag_sets.remove_feature_flag_from_flag_set(flag_set, feature_flag[:name])
203
+ if is_flag_set_exist(flag_set) && @flag_sets.get_flag_sets([flag_set]).length == 0 && !@flag_set_filter.should_filter?
204
+ @flag_sets.remove_flag_set(flag_set)
205
+ end
206
+ end
207
+ end
208
+ end
209
+
149
210
  def increase_tt_name_count(tt_name)
150
211
  return unless tt_name
151
212
 
@@ -55,7 +55,7 @@ module SplitIoClient
55
55
  def store_split(split)
56
56
  @config.logger.debug("storing feature flag (#{split[:name]})") if @config.debug_enabled
57
57
 
58
- @splits_repository.add_split(split)
58
+ @splits_repository.update([split], [], -1)
59
59
  end
60
60
 
61
61
  def load_features