splitclient-rb 6.0.1.pre.rc2-java → 6.1.0-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +12 -1
  3. data/.simplecov +1 -0
  4. data/CHANGES.txt +11 -1
  5. data/NEWS +6 -0
  6. data/README.md +2 -2
  7. data/lib/splitclient-rb.rb +1 -0
  8. data/lib/splitclient-rb/clients/split_client.rb +30 -9
  9. data/lib/splitclient-rb/engine/api/client.rb +8 -9
  10. data/lib/splitclient-rb/engine/api/events.rb +4 -2
  11. data/lib/splitclient-rb/engine/api/faraday_middleware/gzip.rb +10 -8
  12. data/lib/splitclient-rb/engine/api/impressions.rb +4 -2
  13. data/lib/splitclient-rb/engine/api/metrics.rb +5 -5
  14. data/lib/splitclient-rb/engine/api/segments.rb +5 -1
  15. data/lib/splitclient-rb/engine/api/splits.rb +4 -4
  16. data/lib/splitclient-rb/engine/matchers/all_keys_matcher.rb +8 -13
  17. data/lib/splitclient-rb/engine/matchers/between_matcher.rb +12 -23
  18. data/lib/splitclient-rb/engine/matchers/combiners.rb +3 -1
  19. data/lib/splitclient-rb/engine/matchers/combining_matcher.rb +16 -24
  20. data/lib/splitclient-rb/engine/matchers/contains_all_matcher.rb +10 -7
  21. data/lib/splitclient-rb/engine/matchers/contains_any_matcher.rb +7 -6
  22. data/lib/splitclient-rb/engine/matchers/contains_matcher.rb +19 -8
  23. data/lib/splitclient-rb/engine/matchers/dependency_matcher.rb +8 -3
  24. data/lib/splitclient-rb/engine/matchers/ends_with_matcher.rb +21 -6
  25. data/lib/splitclient-rb/engine/matchers/equal_to_boolean_matcher.rb +17 -8
  26. data/lib/splitclient-rb/engine/matchers/equal_to_matcher.rb +11 -23
  27. data/lib/splitclient-rb/engine/matchers/equal_to_set_matcher.rb +7 -6
  28. data/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_matcher.rb +13 -25
  29. data/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_matcher.rb +13 -25
  30. data/lib/splitclient-rb/engine/matchers/matcher.rb +30 -0
  31. data/lib/splitclient-rb/engine/matchers/matches_string_matcher.rb +6 -2
  32. data/lib/splitclient-rb/engine/matchers/negation_matcher.rb +8 -22
  33. data/lib/splitclient-rb/engine/matchers/part_of_set_matcher.rb +10 -7
  34. data/lib/splitclient-rb/engine/matchers/set_matcher.rb +7 -1
  35. data/lib/splitclient-rb/engine/matchers/starts_with_matcher.rb +18 -5
  36. data/lib/splitclient-rb/engine/matchers/user_defined_segment_matcher.rb +7 -25
  37. data/lib/splitclient-rb/engine/matchers/whitelist_matcher.rb +33 -35
  38. data/lib/splitclient-rb/managers/split_manager.rb +10 -3
  39. data/lib/splitclient-rb/split_config.rb +34 -9
  40. data/lib/splitclient-rb/split_factory.rb +27 -0
  41. data/lib/splitclient-rb/validators.rb +104 -36
  42. data/lib/splitclient-rb/version.rb +1 -1
  43. metadata +6 -4
@@ -17,7 +17,7 @@ module SplitIoClient
17
17
  #
18
18
  # @returns [object] array of splits
19
19
  def splits
20
- return if @splits_repository.nil?
20
+ return [] if !SplitIoClient.configuration.valid_mode || @splits_repository.nil?
21
21
 
22
22
  @splits_repository.splits.each_with_object([]) do |(name, split), memo|
23
23
  split_view = build_split_view(name, split)
@@ -33,7 +33,7 @@ module SplitIoClient
33
33
  #
34
34
  # @returns [object] array of split names (String)
35
35
  def split_names
36
- return if @splits_repository.nil?
36
+ return [] if !SplitIoClient.configuration.valid_mode || @splits_repository.nil?
37
37
 
38
38
  @splits_repository.split_names
39
39
  end
@@ -43,7 +43,14 @@ module SplitIoClient
43
43
  #
44
44
  # @returns a split view
45
45
  def split(split_name)
46
- return unless @splits_repository && SplitIoClient::Validators.valid_split_parameters(split_name)
46
+ return unless SplitIoClient.configuration.valid_mode && @splits_repository && SplitIoClient::Validators.valid_split_parameters(split_name)
47
+
48
+ sanitized_split_name= split_name.to_s.strip
49
+
50
+ if split_name.to_s != sanitized_split_name
51
+ SplitIoClient.configuration.logger.warn("split: split_name #{split_name} has extra whitespace, trimming")
52
+ split_name = sanitized_split_name
53
+ end
47
54
 
48
55
  split = @splits_repository.get_split(split_name)
49
56
 
@@ -43,7 +43,7 @@ module SplitIoClient
43
43
  @redis_url = opts[:redis_url] || SplitConfig.default_redis_url
44
44
  @redis_namespace = opts[:redis_namespace] ? "#{opts[:redis_namespace]}.#{SplitConfig.default_redis_namespace}" : SplitConfig.default_redis_namespace
45
45
  @cache_adapter = SplitConfig.init_cache_adapter(
46
- opts[:cache_adapter] || SplitConfig.default_cache_adapter, :map_adapter
46
+ opts[:cache_adapter] || SplitConfig.default_cache_adapter, :map_adapter, nil, @redis_url
47
47
  )
48
48
  @connection_timeout = opts[:connection_timeout] || SplitConfig.default_connection_timeout
49
49
  @read_timeout = opts[:read_timeout] || SplitConfig.default_read_timeout
@@ -54,7 +54,7 @@ module SplitIoClient
54
54
  @impressions_refresh_rate = opts[:impressions_refresh_rate] || SplitConfig.default_impressions_refresh_rate
55
55
  @impressions_queue_size = opts[:impressions_queue_size] || SplitConfig.default_impressions_queue_size
56
56
  @impressions_adapter = SplitConfig.init_cache_adapter(
57
- opts[:cache_adapter] || SplitConfig.default_cache_adapter, :queue_adapter, @impressions_queue_size
57
+ opts[:cache_adapter] || SplitConfig.default_cache_adapter, :queue_adapter, @impressions_queue_size, @redis_url
58
58
  )
59
59
  #Safeguard for users of older SDK versions.
60
60
  @disable_impressions = @impressions_queue_size == -1
@@ -62,13 +62,16 @@ module SplitIoClient
62
62
  @impressions_bulk_size = opts[:impressions_bulk_size] || @impressions_queue_size > 0 ? @impressions_queue_size : 0
63
63
 
64
64
  @metrics_adapter = SplitConfig.init_cache_adapter(
65
- opts[:cache_adapter] || SplitConfig.default_cache_adapter, :map_adapter
65
+ opts[:cache_adapter] || SplitConfig.default_cache_adapter, :map_adapter, nil, @redis_url
66
66
  )
67
67
 
68
68
  @logger = opts[:logger] || SplitConfig.default_logger
69
69
  @debug_enabled = opts[:debug_enabled] || SplitConfig.default_debug
70
70
  @transport_debug_enabled = opts[:transport_debug_enabled] || SplitConfig.default_debug
71
71
  @block_until_ready = opts[:ready] || opts[:block_until_ready] || 0
72
+
73
+ @logger.warn 'no ready parameter has been set - incorrect control treatments could be logged' if block_until_ready == 0
74
+
72
75
  @machine_name = opts[:machine_name] || SplitConfig.machine_hostname
73
76
  @machine_ip = opts[:machine_ip] || SplitConfig.machine_ip
74
77
 
@@ -83,13 +86,16 @@ module SplitIoClient
83
86
  @impression_listener = opts[:impression_listener]
84
87
  @impression_listener_refresh_rate = opts[:impression_listener_refresh_rate] || SplitConfig.default_impression_listener_refresh_rate
85
88
 
89
+ @max_key_size = SplitConfig.max_key_size
90
+
86
91
  @threads = {}
87
92
 
88
93
  @events_push_rate = opts[:events_push_rate] || SplitConfig.default_events_push_rate
89
94
  @events_queue_size = opts[:events_queue_size] || SplitConfig.default_events_queue_size
90
95
  @events_adapter = SplitConfig.init_cache_adapter(
91
- opts[:cache_adapter] || SplitConfig.default_cache_adapter, :queue_adapter, @events_queue_size
96
+ opts[:cache_adapter] || SplitConfig.default_cache_adapter, :queue_adapter, @events_queue_size, @redis_url
92
97
  )
98
+ @valid_mode = true
93
99
 
94
100
  startup_log
95
101
  end
@@ -184,6 +190,8 @@ module SplitIoClient
184
190
  attr_accessor :cache_ttl
185
191
  attr_accessor :max_cache_size
186
192
 
193
+ attr_accessor :max_key_size
194
+
187
195
  attr_accessor :language
188
196
  attr_accessor :version
189
197
 
@@ -208,6 +216,8 @@ module SplitIoClient
208
216
 
209
217
  attr_accessor :threads
210
218
 
219
+ attr_accessor :valid_mode
220
+
211
221
  #
212
222
  # The schedule time for events flush after the first one
213
223
  #
@@ -240,7 +250,7 @@ module SplitIoClient
240
250
  'https://events.split.io/api/'
241
251
  end
242
252
 
243
- def self.init_cache_adapter(adapter, data_structure, queue_size = nil)
253
+ def self.init_cache_adapter(adapter, data_structure, queue_size = nil, redis_url = nil)
244
254
  case adapter
245
255
  when :memory
246
256
  SplitIoClient::Cache::Adapters::MemoryAdapter.new(map_memory_adapter(data_structure, queue_size))
@@ -251,7 +261,7 @@ module SplitIoClient
251
261
  fail StandardError, 'To use Redis as a cache adapter you must include it in your Gemfile'
252
262
  end
253
263
 
254
- SplitIoClient::Cache::Adapters::RedisAdapter.new(@redis_url)
264
+ SplitIoClient::Cache::Adapters::RedisAdapter.new(redis_url)
255
265
  end
256
266
  end
257
267
 
@@ -330,9 +340,17 @@ module SplitIoClient
330
340
  #
331
341
  # @return [object]
332
342
  def self.default_logger
333
- (defined?(Rails) && Rails.logger) ? Rails.logger : Logger.new($stdout)
343
+ if defined?(Rails) && Rails.logger
344
+ Rails.logger
345
+ elsif ENV['SPLITCLIENT_ENV'] == 'test'
346
+ Logger.new('/dev/null')
347
+ else
348
+ Logger.new($stdout)
349
+ end
334
350
  end
335
351
 
352
+
353
+
336
354
  #
337
355
  # The default debug value
338
356
  #
@@ -368,18 +386,25 @@ module SplitIoClient
368
386
  #
369
387
  # The default cache time to live
370
388
  #
371
- # @return [boolean]
389
+ # @return [int]
372
390
  def self.cache_ttl
373
391
  5
374
392
  end
375
393
 
376
394
  # The default max cache size
377
395
  #
378
- # @return [boolean]
396
+ # @return [int]
379
397
  def self.max_cache_size
380
398
  500
381
399
  end
382
400
 
401
+ # The default max key size
402
+ #
403
+ # @return [int]
404
+ def self.max_key_size
405
+ 250
406
+ end
407
+
383
408
  #
384
409
  # custom logger of exceptions
385
410
  #
@@ -1,5 +1,6 @@
1
1
  module SplitIoClient
2
2
  class SplitFactory
3
+ ROOT_PROCESS_ID = Process.pid
3
4
  include SplitIoClient::Cache::Repositories
4
5
  include SplitIoClient::Cache::Stores
5
6
 
@@ -25,7 +26,21 @@ module SplitIoClient
25
26
  @client = SplitClient.new(@api_key, @adapter, @splits_repository, @segments_repository, @impressions_repository, @metrics_repository, @events_repository)
26
27
  @manager = SplitManager.new(@api_key, @adapter, @splits_repository)
27
28
 
29
+ validate_api_key
30
+
28
31
  @sdk_blocker.block if SplitIoClient.configuration.block_until_ready > 0
32
+
33
+
34
+ at_exit do
35
+ unless ENV['SPLITCLIENT_ENV'] == 'test'
36
+ if (Process.pid == ROOT_PROCESS_ID)
37
+ SplitIoClient.configuration.logger.info('Split SDK shutdown started...')
38
+ @client.destroy
39
+ stop!
40
+ SplitIoClient.configuration.logger.info('Split SDK shutdown complete')
41
+ end
42
+ end
43
+ end
29
44
  end
30
45
 
31
46
  def start!
@@ -65,5 +80,17 @@ module SplitIoClient
65
80
  end
66
81
 
67
82
  alias resume! start!
83
+
84
+ private
85
+
86
+ def validate_api_key
87
+ if(@api_key.nil?)
88
+ SplitIoClient.configuration.logger.error('Factory Instantiation: you passed a nil api_key, api_key must be a non-empty String')
89
+ SplitIoClient.configuration.valid_mode = false
90
+ elsif (@api_key.empty?)
91
+ SplitIoClient.configuration.logger.error('Factory Instantiation: you passed and empty api_key, api_key must be a non-empty String')
92
+ SplitIoClient.configuration.valid_mode = false
93
+ end
94
+ end
68
95
  end
69
96
  end
@@ -4,11 +4,12 @@ module SplitIoClient
4
4
  module Validators
5
5
  extend self
6
6
 
7
- def valid_get_treatment_parameters(key, split_name, matching_key, bucketing_key)
7
+ def valid_get_treatment_parameters(key, split_name, matching_key, bucketing_key, attributes)
8
8
  valid_key?(key) &&
9
9
  valid_split_name?(split_name) &&
10
10
  valid_matching_key?(matching_key) &&
11
- valid_bucketing_key?(key, bucketing_key)
11
+ valid_bucketing_key?(key, bucketing_key) &&
12
+ valid_attributes?(attributes)
12
13
  end
13
14
 
14
15
  def valid_get_treatments_parameters(split_names)
@@ -26,30 +27,45 @@ module SplitIoClient
26
27
  valid_split_name?(split_name, :split)
27
28
  end
28
29
 
30
+ def valid_matcher_arguments(args)
31
+ return false if !args.key?(:attributes) && !args.key?(:value)
32
+ return false if args.key?(:value) && args[:value].nil?
33
+ return false if args.key?(:attributes) && args[:attributes].nil?
34
+ true
35
+ end
36
+
29
37
  private
30
38
 
31
39
  def string?(value)
32
40
  value.is_a?(String) || value.is_a?(Symbol)
33
41
  end
34
42
 
43
+ def empty_string?(value)
44
+ value.is_a?(String) && value.empty?
45
+ end
46
+
35
47
  def number_or_string?(value)
36
- value.is_a?(Numeric) || string?(value)
48
+ (value.is_a?(Numeric) && !value.to_f.nan?) || string?(value)
37
49
  end
38
50
 
39
51
  def log_nil(key, method)
40
- SplitIoClient.configuration.logger.error("#{method}: #{key} cannot be nil")
52
+ SplitIoClient.configuration.logger.error("#{method}: you passed a nil #{key}, #{key} must be a non-empty String or a Symbol")
41
53
  end
42
54
 
43
- def log_string(key, method)
44
- SplitIoClient.configuration.logger.error("#{method}: #{key} must be a String or a Symbol")
55
+ def log_empty_string(key, method)
56
+ SplitIoClient.configuration.logger.error("#{method}: you passed an empty #{key}, #{key} must be a non-empty String or a Symbol")
45
57
  end
46
58
 
47
- def log_number_or_string(key, method)
48
- SplitIoClient.configuration.logger.error("#{method}: #{key} must be a String")
59
+ def log_invalid_type(key, method)
60
+ SplitIoClient.configuration.logger.error("#{method}: you passed an invalid #{key} type, #{key} must be a non-empty String or a Symbol")
49
61
  end
50
62
 
51
- def log_convert_numeric(key, method)
52
- SplitIoClient.configuration.logger.warn("#{method}: #{key} is not of type String, converting to String")
63
+ def log_convert_numeric(key, method, value)
64
+ SplitIoClient.configuration.logger.warn("#{method}: #{key} \"#{value}\" is not of type String, converting")
65
+ end
66
+
67
+ def log_key_too_long(key, method)
68
+ SplitIoClient.configuration.logger.error("#{method}: #{key} is too long - must be #{SplitIoClient.configuration.max_key_size} characters or less")
53
69
  end
54
70
 
55
71
  def valid_split_name?(split_name, method = :get_treatment)
@@ -59,7 +75,12 @@ module SplitIoClient
59
75
  end
60
76
 
61
77
  unless string?(split_name)
62
- log_string(:split_name, method)
78
+ log_invalid_type(:split_name, method)
79
+ return false
80
+ end
81
+
82
+ if empty_string?(split_name)
83
+ log_empty_string(:split_name, method)
63
84
  return false
64
85
  end
65
86
 
@@ -82,41 +103,65 @@ module SplitIoClient
82
103
  end
83
104
 
84
105
  unless number_or_string?(matching_key)
85
- log_number_or_string(:matching_key, :get_treatment)
106
+ log_invalid_type(:matching_key, :get_treatment)
86
107
  return false
87
108
  end
88
109
 
89
- log_convert_numeric(:matching_key, :get_treatment) if matching_key.is_a? Numeric
110
+ if empty_string?(matching_key)
111
+ log_empty_string(:matching_key, :get_treatment)
112
+ return false
113
+ end
114
+
115
+ log_convert_numeric(:matching_key, :get_treatment, matching_key) if matching_key.is_a? Numeric
116
+
117
+ if matching_key.size > SplitIoClient.configuration.max_key_size
118
+ log_key_too_long(:matching_key, :get_treatment)
119
+ return false
120
+ end
90
121
 
91
122
  true
92
123
  end
93
124
 
94
125
  def valid_bucketing_key?(key, bucketing_key)
95
- if bucketing_key.nil?
96
- if key.is_a? Hash
97
- SplitIoClient.configuration.logger.warn('get_treatment: key object should have bucketing_key set')
126
+ if key.is_a? Hash
127
+ if bucketing_key.nil?
128
+ log_nil(:bucketing_key, :get_treatment)
129
+ return false
98
130
  end
99
- return true
100
- end
101
131
 
102
- unless number_or_string?(bucketing_key)
103
- log_number_or_string(:bucketing_key, :get_treatment)
104
- return false
105
- end
132
+ unless number_or_string?(bucketing_key)
133
+ log_invalid_type(:bucketing_key, :get_treatment)
134
+ return false
135
+ end
106
136
 
107
- log_convert_numeric(:bucketing_key, :get_treatment) if bucketing_key.is_a? Numeric
137
+ if empty_string?(bucketing_key)
138
+ log_empty_string(:bucketing_key, :get_treatment)
139
+ return false
140
+ end
141
+
142
+ log_convert_numeric(:bucketing_key, :get_treatment, bucketing_key) if bucketing_key.is_a? Numeric
143
+
144
+ if bucketing_key.size > SplitIoClient.configuration.max_key_size
145
+ log_key_too_long(:bucketing_key, :get_treatment)
146
+ return false
147
+ end
148
+ end
108
149
 
109
150
  true
110
151
  end
111
152
 
112
153
  def valid_split_names?(split_names)
113
- if split_names.nil?
114
- log_nil(:split_names, :get_treatments)
154
+ unless !split_names.nil? && split_names.is_a?(Array)
155
+ SplitIoClient.configuration.logger.error('get_treatments: split_names must be a non-empty Array')
115
156
  return false
116
157
  end
117
158
 
118
- unless split_names.is_a? Array
119
- SplitIoClient.configuration.logger.warn('get_treatments: split_names must be an Array')
159
+ true
160
+ end
161
+
162
+ def valid_attributes?(attributes)
163
+ unless attributes.nil? || attributes.is_a?(Hash)
164
+ SplitIoClient.configuration.logger.error('get_treatment: attributes must be of type Hash')
120
165
  return false
121
166
  end
122
167
 
@@ -130,11 +175,21 @@ module SplitIoClient
130
175
  end
131
176
 
132
177
  unless number_or_string?(key)
133
- log_number_or_string(:key, :track)
178
+ log_invalid_type(:key, :track)
134
179
  return false
135
180
  end
136
181
 
137
- log_convert_numeric(:key, :track) if key.is_a? Numeric
182
+ if empty_string?(key)
183
+ log_empty_string(:key, :track)
184
+ return false
185
+ end
186
+
187
+ log_convert_numeric(:key, :track, key) if key.is_a? Numeric
188
+
189
+ if key.size > SplitIoClient.configuration.max_key_size
190
+ log_key_too_long(:key, :track)
191
+ return false
192
+ end
138
193
 
139
194
  true
140
195
  end
@@ -146,12 +201,20 @@ module SplitIoClient
146
201
  end
147
202
 
148
203
  unless string?(event_type)
149
- log_string(:event_type, :track)
204
+ log_invalid_type(:event_type, :track)
150
205
  return false
151
206
  end
152
207
 
153
- if (event_type.to_s =~ /[a-zA-Z0-9][-_\.a-zA-Z0-9]{0,62}/).nil?
154
- SplitIoClient.configuration.logger.error('track: event_type must adhere to [a-zA-Z0-9][-_\.a-zA-Z0-9]{0,62}')
208
+ if event_type.empty?
209
+ log_empty_string(:event_type, :track)
210
+ return false
211
+ end
212
+
213
+ if (event_type.to_s =~ /^[a-zA-Z0-9][-_.:a-zA-Z0-9]{0,79}$/).nil?
214
+ SplitIoClient.configuration.logger.error("track: you passed '#{event_type}', " \
215
+ 'event_type must adhere to the regular expression ^[a-zA-Z0-9][-_.:a-zA-Z0-9]{0,79}$. ' \
216
+ 'This means an event name must be alphanumeric, cannot be more than 80 characters long, ' \
217
+ 'and can only include a dash, underscore, period, or colon as separators of alphanumeric characters')
155
218
  return false
156
219
  end
157
220
 
@@ -165,21 +228,26 @@ module SplitIoClient
165
228
  end
166
229
 
167
230
  unless string?(traffic_type_name)
168
- log_string(:traffic_type_name, :track)
231
+ log_invalid_type(:traffic_type_name, :track)
169
232
  return false
170
233
  end
171
234
 
172
235
  if traffic_type_name.empty?
173
- SplitIoClient.configuration.logger.error('track: traffic_type_name must not be an empty String')
236
+ log_empty_string(:traffic_type_name, :track)
174
237
  return false
175
238
  end
176
239
 
240
+ unless traffic_type_name == traffic_type_name.downcase
241
+ SplitIoClient.configuration.logger.warn('track: traffic_type_name should be all lowercase - ' \
242
+ 'converting string to lowercase')
243
+ end
244
+
177
245
  true
178
246
  end
179
247
 
180
248
  def valid_value?(value)
181
- unless value.is_a?(Numeric) || value.nil?
182
- SplitIoClient.configuration.logger.error('track: value must be a number')
249
+ unless (value.is_a?(Numeric) && !value.to_f.nan?) || value.nil?
250
+ SplitIoClient.configuration.logger.error('track: value must be Numeric')
183
251
  return false
184
252
  end
185
253