statsig 1.8.4 → 1.9.0

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: e6b5d1c640eab6b3173784683e1e87c41c97410631aaea5e9e68306cd45df829
4
- data.tar.gz: c78cb2b73b63f496672a2486a8fb7fe212dc4192838b4cf72a5e191f9b4d065f
3
+ metadata.gz: a832901a7d272c5b27408418836ded1042f720bfc0a374489ed0a81c476a1606
4
+ data.tar.gz: c89426f15ab949980299bfc21931466d076e8b23fbe26ddc290e13d1d92cb948
5
5
  SHA512:
6
- metadata.gz: 2b4601bb00d1287c24aceca4e85d9e4c786225d6c42a8abb8bd70ab687d6d0ce508e3e762b302306617d3f17e81d693293509aa1f1550ab3ad670c4bca464737
7
- data.tar.gz: d5518fc18cd937879281663b1592e286fbe45cd2a222e5ef6ab7659443fb5f5db4875ba7f29d7acbe28a5f81b8bec54195123617f58db09fa442b2ec224c1559
6
+ metadata.gz: c92f462bf372241bdd214a9a153b597792970b70631b8631be3df01b189953b6574016eedf437e397e07f62c8ef8f1c3e7ab7834f10080febe9843514cb059e0
7
+ data.tar.gz: 228deaad6a9c224c80321ddc6bd33ab13a4cd6eae86cc83f822f5d8325f25aaa13bfd0903369468b3664d28847b3239df27a446b32b349e5b3d987f77ef3fb74
data/lib/config_result.rb CHANGED
@@ -6,13 +6,15 @@ module Statsig
6
6
  attr_accessor :json_value
7
7
  attr_accessor :rule_id
8
8
  attr_accessor :secondary_exposures
9
+ attr_accessor :config_delegate
9
10
 
10
- def initialize(name, gate_value = false, json_value = {}, rule_id = '', secondary_exposures = [])
11
+ def initialize(name, gate_value = false, json_value = {}, rule_id = '', secondary_exposures = [], config_delegate = '')
11
12
  @name = name
12
13
  @gate_value = gate_value
13
14
  @json_value = json_value
14
15
  @rule_id = rule_id
15
16
  @secondary_exposures = secondary_exposures.is_a?(Array) ? secondary_exposures : []
17
+ @config_delegate = config_delegate
16
18
  end
17
19
  end
18
20
  end
data/lib/evaluator.rb CHANGED
@@ -29,6 +29,11 @@ module Statsig
29
29
  eval_spec(user, @spec_store.get_config(config_name))
30
30
  end
31
31
 
32
+ def get_layer(user, layer_name)
33
+ return nil unless @initialized && @spec_store.has_layer?(layer_name)
34
+ eval_spec(user, @spec_store.get_layer(layer_name))
35
+ end
36
+
32
37
  def shutdown
33
38
  @spec_store.shutdown
34
39
  end
@@ -44,15 +49,16 @@ module Statsig
44
49
  rule = config['rules'][i]
45
50
  result = eval_rule(user, rule)
46
51
  return $fetch_from_server if result == $fetch_from_server
47
- exposures = exposures + result['exposures'] if result['exposures'].is_a? Array
48
- if result['value']
52
+ exposures = exposures + result.secondary_exposures
53
+ if result.gate_value
49
54
  pass = eval_pass_percent(user, rule, config['salt'])
50
55
  return Statsig::ConfigResult.new(
51
56
  config['name'],
52
57
  pass,
53
- pass ? rule['returnValue'] : config['defaultValue'],
54
- rule['id'],
55
- exposures
58
+ pass ? result.json_value : config['defaultValue'],
59
+ result.rule_id,
60
+ exposures,
61
+ result.config_delegate
56
62
  )
57
63
  end
58
64
 
@@ -83,7 +89,16 @@ module Statsig
83
89
  end
84
90
  i += 1
85
91
  end
86
- { 'value' => pass, 'exposures' => exposures }
92
+
93
+ delegate = rule['configDelegate']
94
+ if pass and @spec_store.get_config(delegate)
95
+ delegated_result = self.eval_spec(user, @spec_store.get_config(delegate))
96
+ delegated_result.config_delegate = delegate
97
+ delegated_result.secondary_exposures = exposures + delegated_result.secondary_exposures
98
+ return delegated_result
99
+ end
100
+
101
+ Statsig::ConfigResult.new('', pass, rule['returnValue'], rule['id'], exposures)
87
102
  end
88
103
 
89
104
  def eval_condition(user, condition)
data/lib/layer.rb ADDED
@@ -0,0 +1,15 @@
1
+ class Layer
2
+ attr_accessor :name
3
+ attr_accessor :rule_id
4
+
5
+ def initialize(name, value = {}, rule_id = '')
6
+ @name = name
7
+ @value = value
8
+ @rule_id = rule_id
9
+ end
10
+
11
+ def get(index, default_value)
12
+ return default_value if @value.nil? || !@value.key?(index)
13
+ @value[index]
14
+ end
15
+ end
data/lib/network.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  require 'http'
2
2
  require 'json'
3
- require 'dynamic_config'
4
3
 
5
4
  $retry_codes = [408, 500, 502, 503, 504, 522, 524, 599]
6
5
 
data/lib/spec_store.rb CHANGED
@@ -11,6 +11,7 @@ module Statsig
11
11
  @store = {
12
12
  :gates => {},
13
13
  :configs => {},
14
+ :layers => {},
14
15
  :id_lists => {},
15
16
  }
16
17
  e = download_config_specs
@@ -34,6 +35,10 @@ module Statsig
34
35
  @store[:configs].key?(config_name)
35
36
  end
36
37
 
38
+ def has_layer?(layer_name)
39
+ @store[:layers].key?(layer_name)
40
+ end
41
+
37
42
  def get_gate(gate_name)
38
43
  return nil unless has_gate?(gate_name)
39
44
  @store[:gates][gate_name]
@@ -44,6 +49,11 @@ module Statsig
44
49
  @store[:configs][config_name]
45
50
  end
46
51
 
52
+ def get_layer(layer_name)
53
+ return nil unless has_layer?(layer_name)
54
+ @store[:layers][layer_name]
55
+ end
56
+
47
57
  def get_id_list(list_name)
48
58
  @store[:id_lists][list_name]
49
59
  end
@@ -89,15 +99,19 @@ module Statsig
89
99
  @last_sync_time = specs_json['time'] || @last_sync_time
90
100
  return unless specs_json['has_updates'] == true &&
91
101
  !specs_json['feature_gates'].nil? &&
92
- !specs_json['dynamic_configs'].nil?
102
+ !specs_json['dynamic_configs'].nil? &&
103
+ !specs_json['layer_configs'].nil?
93
104
 
94
105
  new_gates = {}
95
106
  new_configs = {}
107
+ new_layers = {}
96
108
 
97
109
  specs_json['feature_gates'].map{|gate| new_gates[gate['name']] = gate }
98
110
  specs_json['dynamic_configs'].map{|config| new_configs[config['name']] = config }
111
+ specs_json['layer_configs'].map{|layer| new_layers[layer['name']] = layer }
99
112
  @store[:gates] = new_gates
100
113
  @store[:configs] = new_configs
114
+ @store[:layers] = new_layers
101
115
 
102
116
  new_id_lists = specs_json['id_lists']
103
117
  if new_id_lists.is_a? Hash
data/lib/statsig.rb CHANGED
@@ -25,6 +25,11 @@ module Statsig
25
25
  @shared_instance&.get_config(user, experiment_name)
26
26
  end
27
27
 
28
+ def self.get_layer(user, layer_name)
29
+ ensure_initialized
30
+ @shared_instance&.get_config(user, layer_name)
31
+ end
32
+
28
33
  def self.log_event(user, event_name, value, metadata)
29
34
  ensure_initialized
30
35
  @shared_instance&.log_event(user, event_name, value, metadata)
@@ -40,7 +45,7 @@ module Statsig
40
45
  def self.get_statsig_metadata
41
46
  {
42
47
  'sdkType' => 'ruby-server',
43
- 'sdkVersion' => '1.8.4',
48
+ 'sdkVersion' => '1.9.0',
44
49
  }
45
50
  end
46
51
 
@@ -26,12 +26,7 @@ class StatsigDriver
26
26
  end
27
27
 
28
28
  def check_gate(user, gate_name)
29
- validate_user(user)
30
- user = normalize_user(user)
31
- if !gate_name.is_a?(String) || gate_name.empty?
32
- raise 'Invalid gate_name provided'
33
- end
34
- check_shutdown
29
+ user = verify_inputs(user, gate_name, "gate_name")
35
30
 
36
31
  res = @evaluator.check_gate(user, gate_name)
37
32
  if res.nil?
@@ -49,33 +44,34 @@ class StatsigDriver
49
44
  end
50
45
 
51
46
  def get_config(user, dynamic_config_name)
52
- validate_user(user)
53
- user = normalize_user(user)
54
- if !dynamic_config_name.is_a?(String) || dynamic_config_name.empty?
55
- raise "Invalid dynamic_config_name provided"
56
- end
57
- check_shutdown
47
+ user = verify_inputs(user, dynamic_config_name, "dynamic_config_name")
48
+ get_config_impl(user, dynamic_config_name)
49
+ end
58
50
 
59
- res = @evaluator.get_config(user, dynamic_config_name)
51
+ def get_experiment(user, experiment_name)
52
+ user = verify_inputs(user, experiment_name, "experiment_name")
53
+ get_config_impl(user, experiment_name)
54
+ end
55
+
56
+ def get_layer(user, layer_name)
57
+ user = verify_inputs(user, layer_name, "layer_name")
58
+
59
+ res = @evaluator.get_layer(user, layer_name)
60
60
  if res.nil?
61
- res = Statsig::ConfigResult.new(dynamic_config_name)
61
+ res = Statsig::ConfigResult.new(layer_name)
62
62
  end
63
63
 
64
64
  if res == $fetch_from_server
65
- res = get_config_fallback(user, dynamic_config_name)
65
+ if res.config_delegate.empty?
66
+ return Layer.new(layer_name)
67
+ end
68
+ res = get_config_fallback(user, res.config_delegate)
66
69
  # exposure logged by the server
67
70
  else
68
- @logger.log_config_exposure(user, res.name, res.rule_id, res.secondary_exposures)
71
+ @logger.log_layer_exposure(user, res.name, res.rule_id, res.secondary_exposures, res.config_delegate)
69
72
  end
70
73
 
71
- DynamicConfig.new(res.name, res.json_value, res.rule_id)
72
- end
73
-
74
- def get_experiment(user, experiment_name)
75
- if !experiment_name.is_a?(String) || experiment_name.empty?
76
- raise "Invalid experiment_name provided"
77
- end
78
- get_config(user, experiment_name)
74
+ Layer.new(res.name, res.json_value, res.rule_id)
79
75
  end
80
76
 
81
77
  def log_event(user, event_name, value = nil, metadata = nil)
@@ -102,6 +98,32 @@ class StatsigDriver
102
98
 
103
99
  private
104
100
 
101
+ def verify_inputs(user, config_name, variable_name)
102
+ validate_user(user)
103
+ if !config_name.is_a?(String) || config_name.empty?
104
+ raise "Invalid " + variable_name +" provided"
105
+ end
106
+
107
+ check_shutdown
108
+ normalize_user(user)
109
+ end
110
+
111
+ def get_config_impl(user, config_name)
112
+ res = @evaluator.get_config(user, config_name)
113
+ if res.nil?
114
+ res = Statsig::ConfigResult.new(config_name)
115
+ end
116
+
117
+ if res == $fetch_from_server
118
+ res = get_config_fallback(user, config_name)
119
+ # exposure logged by the server
120
+ else
121
+ @logger.log_config_exposure(user, res.name, res.rule_id, res.secondary_exposures)
122
+ end
123
+
124
+ DynamicConfig.new(res.name, res.json_value, res.rule_id)
125
+ end
126
+
105
127
  def validate_user(user)
106
128
  if user.nil? || !user.instance_of?(StatsigUser) || !user.user_id.is_a?(String)
107
129
  raise 'Must provide a valid StatsigUser with a user_id to use the server SDK. See https://docs.statsig.com/messages/serverRequiredUserID/ for more details.'
@@ -2,6 +2,7 @@ require 'statsig_event'
2
2
 
3
3
  $gate_exposure_event = 'statsig::gate_exposure'
4
4
  $config_exposure_event = 'statsig::config_exposure'
5
+ $layer_exposure_event = 'statsig::layer_exposure'
5
6
 
6
7
  module Statsig
7
8
  class StatsigLogger
@@ -43,6 +44,19 @@ module Statsig
43
44
  log_event(event)
44
45
  end
45
46
 
47
+ def log_layer_exposure(user, config_name, rule_id, secondary_exposures, allocated_experiment)
48
+ event = StatsigEvent.new($layer_exposure_event)
49
+ event.user = user
50
+ event.metadata = {
51
+ 'config' => config_name,
52
+ 'ruleID' => rule_id,
53
+ 'allocatedExperiment' => allocated_experiment
54
+ }
55
+ event.statsig_metadata = Statsig.get_statsig_metadata
56
+ event.secondary_exposures = secondary_exposures.is_a?(Array) ? secondary_exposures : []
57
+ log_event(event)
58
+ end
59
+
46
60
  def periodic_flush
47
61
  Thread.new do
48
62
  loop do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: statsig
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.4
4
+ version: 1.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Statsig, Inc
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-11 00:00:00.000000000 Z
11
+ date: 2022-03-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -118,6 +118,7 @@ files:
118
118
  - lib/dynamic_config.rb
119
119
  - lib/evaluation_helpers.rb
120
120
  - lib/evaluator.rb
121
+ - lib/layer.rb
121
122
  - lib/network.rb
122
123
  - lib/spec_store.rb
123
124
  - lib/statsig.rb
@@ -145,7 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
145
146
  - !ruby/object:Gem::Version
146
147
  version: '0'
147
148
  requirements: []
148
- rubygems_version: 3.2.3
149
+ rubygems_version: 3.3.7
149
150
  signing_key:
150
151
  specification_version: 4
151
152
  summary: Statsig server SDK for Ruby