splitclient-rb 5.1.2.pre.rc21 → 5.1.2

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: d36b2f03dcee114ffe3e6bcd1ec6a1c6c4d669f716d15cf4a183c01992ce538e
4
- data.tar.gz: 3ccd0c7f21220c57802f81a9024723b528c8e6762d3ba9075dcf9db1ae92953d
3
+ metadata.gz: aac78f1a43b52720c039260ceb19474cb8fd03fd473cbf0054c590c7e8d45e57
4
+ data.tar.gz: d0cc36d7647956efbb9f0d455d1bf7aacfebca6960aa4fe304bd98e166d9752d
5
5
  SHA512:
6
- metadata.gz: 3c6f4efc722392ec4c3f8cbc7fe671cdd5c2b85dfa00fb81ca36135812da3a55092453f5d2f8608295e524f73221636be155741ac9fbd536d2e99a9a7e160044
7
- data.tar.gz: 6e5e4ceade46af4af60b82ffdfe6c26c2f8e5432092a1cdf42d7827b3c6e40f364518ad52536e4ab988141a43575b10fb7d6cd17b85f3f574c44db4222f5ee56
6
+ metadata.gz: 48898f6530d4dfeea94dd20475bc204c1f4cbfe5c6d560565ad6ea3b33c756fa3c3e0f0cfa80a28919c5e88fc00a0bbc6e22d7a0031ca03b652bf8241d7ca651
7
+ data.tar.gz: 14af6c54d0e1d164a59e2b735518f5bfdf80eb47b33d6fdb78415a056adf8ea2747ee7ee6a16f15c905611c8263ef5e48c964db8f4d3ad68395cecf6894106ae
data/.gitignore CHANGED
@@ -44,5 +44,8 @@ lib/murmurhash/murmurhash.so
44
44
  ext/murmurhash/murmurhash.bundle
45
45
  ext/murmurhash/murmurhash.so
46
46
 
47
- #Ignore dump.rdb
47
+ # Ignore dump.rdb
48
48
  dump.rdb
49
+
50
+ # Ignore Mac OS generated files
51
+ .DS_Store
data/CHANGES.txt CHANGED
@@ -1,3 +1,6 @@
1
+ 5.1.2 (October 26th, 2018)
2
+ - Add input validation for client API methods
3
+
1
4
  5.1.1 (October 4th, 2018)
2
5
  - Change get_treatments so that it sends a single latency metric
3
6
  - Removed unused call to Redis#scan when adding latencies
data/NEWS CHANGED
@@ -1,3 +1,7 @@
1
+ 5.1.2
2
+
3
+ Add input validation for client API methods: get_treatment, get_treatments, track, manager
4
+
1
5
  5.1.1
2
6
 
3
7
  Reduces the number of calls to Redis when calling #client.get_treatments using such cache adapter.
@@ -2,8 +2,7 @@ require 'forwardable'
2
2
 
3
3
  require 'splitclient-rb/version'
4
4
 
5
- require 'splitclient-rb/exceptions/impressions_shutdown_exception'
6
- require 'splitclient-rb/exceptions/sdk_blocker_timeout_expired_exception'
5
+ require 'splitclient-rb/exceptions'
7
6
  require 'splitclient-rb/cache/routers/impression_router'
8
7
  require 'splitclient-rb/cache/adapters/memory_adapters/map_adapter'
9
8
  require 'splitclient-rb/cache/adapters/memory_adapters/queue_adapter'
@@ -80,6 +79,7 @@ require 'splitclient-rb/engine/models/split'
80
79
  require 'splitclient-rb/engine/models/label'
81
80
  require 'splitclient-rb/engine/models/treatment'
82
81
  require 'splitclient-rb/utilitites'
82
+ require 'splitclient-rb/validators'
83
83
 
84
84
  # C extension
85
85
  require 'murmurhash/murmurhash_mri'
@@ -26,7 +26,7 @@ module SplitIoClient
26
26
 
27
27
  def get_splits(names)
28
28
  splits = {}
29
- split_names = names.reject(&:empty?).uniq.map { |name| namespace_key(".split.#{name}") }
29
+ split_names = names.map { |name| namespace_key(".split.#{name}") }
30
30
  splits.merge!(
31
31
  @adapter
32
32
  .multiple_strings(split_names)
@@ -1,4 +1,5 @@
1
1
  module SplitIoClient
2
+
2
3
  class SplitClient
3
4
  #
4
5
  # Creates a new split client instance that connects to split.io API.
@@ -17,11 +18,24 @@ module SplitIoClient
17
18
  end
18
19
 
19
20
  def get_treatments(key, split_names, attributes = {})
21
+
22
+ return nil unless SplitIoClient::Validators.valid_get_treatments_parameters(split_names)
23
+
24
+ sanitized_split_names = sanitize_split_names(split_names)
25
+
26
+ if sanitized_split_names.empty?
27
+ SplitIoClient.configuration.logger.warn('get_treatments: split_names is an empty array or has null values')
28
+ return {}
29
+ end
30
+
20
31
  bucketing_key, matching_key = keys_from_key(key)
32
+ bucketing_key = bucketing_key ? bucketing_key.to_s : nil
33
+ matching_key = matching_key ? matching_key.to_s : nil
34
+
21
35
  evaluator = Engine::Parser::Evaluator.new(@segments_repository, @splits_repository, true)
22
36
  start = Time.now
23
37
  treatments_labels_change_numbers =
24
- @splits_repository.get_splits(split_names).each_with_object({}) do |(name, data), memo|
38
+ @splits_repository.get_splits(sanitized_split_names).each_with_object({}) do |(name, data), memo|
25
39
  memo.merge!(name => get_treatment(key, name, attributes, data, false, true, evaluator))
26
40
  end
27
41
  latency = (Time.now - start) * 1000.0
@@ -34,13 +48,13 @@ module SplitIoClient
34
48
  matching_key, bucketing_key, treatments_labels_change_numbers, time
35
49
  )
36
50
 
37
- route_impressions(split_names, matching_key, bucketing_key, time, treatments_labels_change_numbers, attributes)
51
+ route_impressions(sanitized_split_names, matching_key, bucketing_key, time, treatments_labels_change_numbers, attributes)
38
52
  end
39
53
 
40
- split_names = treatments_labels_change_numbers.keys
54
+ split_names_keys = treatments_labels_change_numbers.keys
41
55
  treatments = treatments_labels_change_numbers.values.map { |v| v[:treatment] }
42
56
 
43
- Hash[split_names.zip(treatments)]
57
+ Hash[split_names_keys.zip(treatments)]
44
58
  end
45
59
 
46
60
  #
@@ -59,68 +73,43 @@ module SplitIoClient
59
73
  key, split_name, attributes = {}, split_data = nil, store_impressions = true,
60
74
  multiple = false, evaluator = nil
61
75
  )
62
- bucketing_key, matching_key = keys_from_key(key)
63
- treatment_data = { label: Engine::Models::Label::DEFINITION_NOT_FOUND, treatment: SplitIoClient::Engine::Models::Treatment::CONTROL }
64
- evaluator ||= Engine::Parser::Evaluator.new(@segments_repository, @splits_repository)
76
+ control_treatment = { label: Engine::Models::Label::EXCEPTION, treatment: SplitIoClient::Engine::Models::Treatment::CONTROL }
77
+ parsed_control_treatment = parsed_treatment(multiple, control_treatment)
65
78
 
66
- if matching_key.nil?
67
- SplitIoClient.configuration.logger.warn('matching_key was null for split_name: ' + split_name.to_s)
68
- return parsed_treatment(multiple, treatment_data)
69
- end
79
+ bucketing_key, matching_key = keys_from_key(key)
70
80
 
71
- if split_name.nil?
72
- SplitIoClient.configuration.logger.warn('split_name was null for key: ' + key)
73
- return parsed_treatment(multiple, treatment_data)
74
- end
81
+ return parsed_control_treatment unless SplitIoClient::Validators.valid_get_treatment_parameters(key, split_name, matching_key, bucketing_key)
75
82
 
76
- start = Time.now
83
+ bucketing_key = bucketing_key ? bucketing_key.to_s : nil
84
+ matching_key = matching_key.to_s
85
+ evaluator ||= Engine::Parser::Evaluator.new(@segments_repository, @splits_repository)
77
86
 
78
87
  begin
88
+ start = Time.now
89
+
79
90
  split = multiple ? split_data : @splits_repository.get_split(split_name)
80
91
 
81
92
  if split.nil?
82
- SplitIoClient.configuration.logger.debug("split_name: #{split_name} does not exist. Returning CONTROL")
83
- return parsed_treatment(multiple, treatment_data)
84
- else
85
- treatment_data =
86
- evaluator.call(
87
- { bucketing_key: bucketing_key, matching_key: matching_key }, split, attributes
88
- )
93
+ SplitIoClient.configuration.logger.warn("split_name: #{split_name} does not exist. Returning CONTROL")
94
+ return parsed_control_treatment
89
95
  end
90
- rescue StandardError => error
91
- SplitIoClient.configuration.log_found_exception(__method__.to_s, error)
92
96
 
93
- store_impression(
94
- split_name, matching_key, bucketing_key,
95
- {
96
- treatment: SplitIoClient::Engine::Models::Treatment::CONTROL,
97
- label: SplitIoClient::Engine::Models::Label::EXCEPTION
98
- },
99
- store_impressions, attributes
97
+ treatment_data =
98
+ evaluator.call(
99
+ { bucketing_key: bucketing_key, matching_key: matching_key }, split, attributes
100
100
  )
101
101
 
102
- return parsed_treatment(multiple, treatment_data)
103
- end
104
-
105
- begin
106
102
  latency = (Time.now - start) * 1000.0
107
- split && store_impression(split_name, matching_key, bucketing_key, treatment_data, store_impressions, attributes)
103
+ store_impression(split_name, matching_key, bucketing_key, treatment_data, store_impressions, attributes)
108
104
 
109
105
  # Measure
110
106
  @adapter.metrics.time('sdk.get_treatment', latency) unless multiple
111
107
  rescue StandardError => error
112
108
  SplitIoClient.configuration.log_found_exception(__method__.to_s, error)
113
109
 
114
- store_impression(
115
- split_name, matching_key, bucketing_key,
116
- {
117
- treatment: SplitIoClient::Engine::Models::Treatment::CONTROL,
118
- label: SplitIoClient::Engine::Models::Label::EXCEPTION
119
- },
120
- store_impressions, attributes
121
- )
110
+ store_impression(split_name, matching_key, bucketing_key, control_treatment, store_impressions, attributes)
122
111
 
123
- return parsed_treatment(multiple, treatment_data)
112
+ return parsed_control_treatment
124
113
  end
125
114
 
126
115
  parsed_treatment(multiple, treatment_data)
@@ -188,16 +177,23 @@ module SplitIoClient
188
177
  @impression_router ||= SplitIoClient::ImpressionRouter.new
189
178
  end
190
179
 
191
- def track(key, traffic_type, event_type, value = nil)
192
- @events_repository.add(key, traffic_type, event_type, (Time.now.to_f * 1000).to_i, value)
180
+ def track(key, traffic_type_name, event_type, value = nil)
181
+ return false unless SplitIoClient::Validators.valid_track_parameters(key, traffic_type_name, event_type, value)
182
+ begin
183
+ @events_repository.add(key.to_s, traffic_type_name, event_type.to_s, (Time.now.to_f * 1000).to_i, value)
184
+ true
185
+ rescue StandardError => error
186
+ SplitIoClient.configuration.log_found_exception(__method__.to_s, error)
187
+ false
188
+ end
193
189
  end
194
190
 
195
191
  def keys_from_key(key)
196
192
  case key.class.to_s
197
193
  when 'Hash'
198
- key.values_at(:bucketing_key, :matching_key).map { |k| k.nil? ? nil : k.to_s }
194
+ key.values_at(:bucketing_key, :matching_key).map { |k| k.nil? ? nil : k }
199
195
  else
200
- [nil, key].map { |k| k.nil? ? nil : k.to_s }
196
+ [nil, key].map { |k| k.nil? ? nil : k }
201
197
  end
202
198
  end
203
199
 
@@ -212,5 +208,16 @@ module SplitIoClient
212
208
  treatment_data[:treatment]
213
209
  end
214
210
  end
211
+
212
+ def sanitize_split_names(split_names)
213
+ split_names.compact.uniq.select do |split_name|
214
+ if split_name.is_a?(String) && !split_name.empty?
215
+ true
216
+ else
217
+ SplitIoClient.configuration.logger.warn('get_treatments: split_name has to be a non empty string')
218
+ false
219
+ end
220
+ end
221
+ end
215
222
  end
216
223
  end
@@ -4,5 +4,4 @@ class SplitIoClient::Engine::Models::Label
4
4
  EXCEPTION = 'exception'.freeze
5
5
  KILLED = 'killed'.freeze
6
6
  NOT_IN_SPLIT = 'not in split'.freeze
7
- DEFINITION_NOT_FOUND = 'definition not found'.freeze
8
7
  end
@@ -0,0 +1,7 @@
1
+ module SplitIoClient
2
+ class SplitIoError < StandardError; end
3
+
4
+ class ImpressionShutdownException < SplitIoError; end
5
+
6
+ class SDKBlockerTimeoutExpiredException < SplitIoError; end
7
+ end
@@ -43,7 +43,7 @@ module SplitIoClient
43
43
  #
44
44
  # @returns a split view
45
45
  def split(split_name)
46
- return unless @splits_repository
46
+ return unless @splits_repository && SplitIoClient::Validators.valid_split_parameters(split_name)
47
47
 
48
48
  split = @splits_repository.get_split(split_name)
49
49
 
@@ -63,7 +63,6 @@ module SplitIoClient
63
63
  treatments = []
64
64
  end
65
65
 
66
-
67
66
  {
68
67
  name: name,
69
68
  traffic_type_name: split[:trafficTypeName],
@@ -0,0 +1,185 @@
1
+ module SplitIoClient
2
+ module Validators
3
+ extend self
4
+
5
+ def valid_get_treatment_parameters(key, split_name, matching_key, bucketing_key)
6
+ valid_key?(key) && valid_split_name?(split_name) && valid_matching_key?(matching_key) && valid_bucketing_key?(bucketing_key)
7
+ end
8
+
9
+ def valid_get_treatments_parameters(split_names)
10
+ valid_split_names?(split_names)
11
+ end
12
+
13
+ def valid_track_parameters(key, traffic_type_name, event_type, value)
14
+ valid_track_key?(key) && valid_traffic_type_name?(traffic_type_name) && valid_event_type?(event_type) && valid_value?(value)
15
+ end
16
+
17
+ def valid_split_parameters(split_name)
18
+ valid_split_name?(split_name, :split)
19
+ end
20
+
21
+ private
22
+
23
+ def string?(value)
24
+ value.is_a?(String) || value.is_a?(Symbol)
25
+ end
26
+
27
+ def number_or_string?(value)
28
+ value.is_a?(Numeric) || string?(value)
29
+ end
30
+
31
+ def log_nil(key, method)
32
+ SplitIoClient.configuration.logger.error("#{method}: #{key} cannot be nil")
33
+ end
34
+
35
+ def log_string(key, method)
36
+ SplitIoClient.configuration.logger.error("#{method}: #{key} must be a String or a Symbol")
37
+ end
38
+
39
+ def log_number_or_string(key, method)
40
+ SplitIoClient.configuration.logger.error("#{method}: #{key} must be a String")
41
+ end
42
+
43
+ def log_convert_numeric(key, method)
44
+ SplitIoClient.configuration.logger.warn("#{method}: #{key} is not of type String, converting to String")
45
+ end
46
+
47
+ def valid_split_name?(split_name, method=:get_treatment)
48
+ if split_name.nil?
49
+ log_nil(:split_name, method)
50
+ return false
51
+ end
52
+
53
+ unless string?(split_name)
54
+ log_string(:split_name, method)
55
+ return false
56
+ end
57
+
58
+ return true
59
+ end
60
+
61
+ def valid_key?(key)
62
+ if key.nil?
63
+ log_nil(:key, :get_treatment)
64
+ return false
65
+ end
66
+
67
+ return true
68
+ end
69
+
70
+ def valid_matching_key?(matching_key)
71
+ if matching_key.nil?
72
+ log_nil(:matching_key, :get_treatment)
73
+ return false
74
+ end
75
+
76
+ unless number_or_string?(matching_key)
77
+ log_number_or_string(:matching_key, :get_treatment)
78
+ return false
79
+ end
80
+
81
+ if matching_key.is_a? Numeric
82
+ log_convert_numeric(:matching_key, :get_treatment)
83
+ end
84
+
85
+ return true
86
+ end
87
+
88
+ def valid_bucketing_key?(bucketing_key)
89
+ if bucketing_key.nil?
90
+ SplitIoClient.configuration.logger.warn('get_treatment: key object should have bucketing_key set')
91
+ return true
92
+ end
93
+
94
+ unless number_or_string?(bucketing_key)
95
+ log_number_or_string(:bucketing_key, :get_treatment)
96
+ return false
97
+ end
98
+
99
+ if bucketing_key.is_a? Numeric
100
+ log_convert_numeric(:bucketing_key, :get_treatment)
101
+ end
102
+
103
+ return true
104
+ end
105
+
106
+ def valid_split_names?(split_names)
107
+ if split_names.nil?
108
+ log_nil(:split_names, :get_treatments)
109
+ return false
110
+ end
111
+
112
+ unless split_names.is_a? Array
113
+ SplitIoClient.configuration.logger.warn('get_treatments: split_names must be an Array')
114
+ return false
115
+ end
116
+
117
+ return true
118
+ end
119
+
120
+ def valid_track_key?(key)
121
+ if key.nil?
122
+ log_nil(:key, :track)
123
+ return false
124
+ end
125
+
126
+ unless number_or_string?(key)
127
+ log_number_or_string(:key, :track)
128
+ return false
129
+ end
130
+
131
+ if key.is_a? Numeric
132
+ log_convert_numeric(:key, :track)
133
+ end
134
+
135
+ return true
136
+ end
137
+
138
+ def valid_event_type?(event_type)
139
+ if event_type.nil?
140
+ log_nil(:event_type, :track)
141
+ return false
142
+ end
143
+
144
+ unless string?(event_type)
145
+ log_string(:event_type, :track)
146
+ return false
147
+ end
148
+
149
+ if (event_type.to_s =~ /[a-zA-Z0-9][-_\.a-zA-Z0-9]{0,62}/).nil?
150
+ SplitIoClient.configuration.logger.error('track: event_type must adhere to [a-zA-Z0-9][-_\.a-zA-Z0-9]{0,62}')
151
+ return false
152
+ end
153
+
154
+ return true
155
+ end
156
+
157
+ def valid_traffic_type_name?(traffic_type_name)
158
+ if traffic_type_name.nil?
159
+ log_nil(:traffic_type_name, :track)
160
+ return false
161
+ end
162
+
163
+ unless string?(traffic_type_name)
164
+ log_string(:traffic_type_name, :track)
165
+ return false
166
+ end
167
+
168
+ if traffic_type_name.empty?
169
+ SplitIoClient.configuration.logger.error('track: traffic_type_name must not be an empty String')
170
+ return false
171
+ end
172
+
173
+ return true
174
+ end
175
+
176
+ def valid_value?(value)
177
+ unless value.is_a?(Numeric) || value.nil?
178
+ SplitIoClient.configuration.logger.error('track: value must be a number')
179
+ return false
180
+ end
181
+
182
+ return true
183
+ end
184
+ end
185
+ end
@@ -1,3 +1,3 @@
1
1
  module SplitIoClient
2
- VERSION = '5.1.2.pre.rc21'
2
+ VERSION = '5.1.2'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: splitclient-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.1.2.pre.rc21
4
+ version: 5.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Split Software
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-10-18 00:00:00.000000000 Z
11
+ date: 2018-10-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: allocation_stats
@@ -325,8 +325,7 @@ files:
325
325
  - lib/splitclient-rb/engine/parser/evaluator.rb
326
326
  - lib/splitclient-rb/engine/parser/partition.rb
327
327
  - lib/splitclient-rb/engine/parser/split_adapter.rb
328
- - lib/splitclient-rb/exceptions/impressions_shutdown_exception.rb
329
- - lib/splitclient-rb/exceptions/sdk_blocker_timeout_expired_exception.rb
328
+ - lib/splitclient-rb/exceptions.rb
330
329
  - lib/splitclient-rb/localhost_split_factory.rb
331
330
  - lib/splitclient-rb/localhost_utils.rb
332
331
  - lib/splitclient-rb/managers/localhost_split_manager.rb
@@ -336,6 +335,7 @@ files:
336
335
  - lib/splitclient-rb/split_factory_builder.rb
337
336
  - lib/splitclient-rb/split_logger.rb
338
337
  - lib/splitclient-rb/utilitites.rb
338
+ - lib/splitclient-rb/validators.rb
339
339
  - lib/splitclient-rb/version.rb
340
340
  - splitclient-rb.gemspec
341
341
  - splitio.yml.example
@@ -357,9 +357,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
357
357
  version: '0'
358
358
  required_rubygems_version: !ruby/object:Gem::Requirement
359
359
  requirements:
360
- - - ">"
360
+ - - ">="
361
361
  - !ruby/object:Gem::Version
362
- version: 1.3.1
362
+ version: '0'
363
363
  requirements: []
364
364
  rubyforge_project:
365
365
  rubygems_version: 2.7.6
@@ -1,4 +0,0 @@
1
- module SplitIoClient
2
- class ImpressionShutdownException < StandardError
3
- end
4
- end
@@ -1,4 +0,0 @@
1
- module SplitIoClient
2
- class SDKBlockerTimeoutExpiredException < StandardError
3
- end
4
- end