statsig 1.31.1 → 1.32.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: 1afc6737d00e614429706bdcac895ecd61ef3f858fe152bc21c407470ec8168f
4
- data.tar.gz: cbdb4311dd2505d47ada5f78105ec0203e24edd6274161d16b67b196aa68596b
3
+ metadata.gz: dd71ed81ad0f3658f5b6b480adba4a36f560357c67d6d9869b605e2d0e1e76c0
4
+ data.tar.gz: 59b7da08c4991da0e7b33dc08865612347442970932e71b8069bcd4518d50680
5
5
  SHA512:
6
- metadata.gz: 113b3cbc430583fd2144ab7f6dc9188e3e2c87079fb188bc931c954ffe51c18c849373ed53fee2b71c87f6abb76a54f66519475eb5bfd95bf00c29cc21f2e4c0
7
- data.tar.gz: a4f7632a2af82ea4052ed1c94b9b651e7fd7b6ac88010d4d18b77038d374d2833e25443768152f54ab68f6710f48a6c5ada634199d6b4d55f7501074674d3a82
6
+ metadata.gz: 3775510747a856d9ae300ccad20a74de3cbe000bc3bca59c535c64cbab1bcb46a83069aa917299ee6dd7032f44206eecd39f3602c92f18c39a8ebbb57b45d9e9
7
+ data.tar.gz: e0a03db37029a260614a6eb8bf9edf1e2fe60f84d67fb9e027bf5fd72c3c4f969a7ab3145f55d0bd19f796b7d1733416547688e1076c69e066abd819c99b899c
data/lib/api_config.rb ADDED
@@ -0,0 +1,128 @@
1
+ require 'constants'
2
+
3
+ class UnsupportedConfigException < StandardError
4
+ end
5
+
6
+ module Statsig
7
+ class APIConfig
8
+ attr_accessor :name, :type, :is_active, :salt, :default_value, :enabled,
9
+ :rules, :id_type, :entity, :explicit_parameters, :has_shared_params, :target_app_ids
10
+
11
+ def initialize(name:, type:, is_active:, salt:, default_value:, enabled:, rules:, id_type:, entity:,
12
+ explicit_parameters: nil, has_shared_params: nil, target_app_ids: nil)
13
+ @name = name
14
+ @type = type.to_sym unless entity.nil?
15
+ @is_active = is_active
16
+ @salt = salt
17
+ @default_value = JSON.parse(JSON.generate(default_value))
18
+ @enabled = enabled
19
+ @rules = rules
20
+ @id_type = id_type
21
+ @entity = entity.to_sym unless entity.nil?
22
+ @explicit_parameters = explicit_parameters
23
+ @has_shared_params = has_shared_params
24
+ @target_app_ids = target_app_ids
25
+ end
26
+
27
+ def self.from_json(json)
28
+ new(
29
+ name: json[:name],
30
+ type: json[:type],
31
+ is_active: json[:isActive],
32
+ salt: json[:salt],
33
+ default_value: json[:defaultValue] || {},
34
+ enabled: json[:enabled],
35
+ rules: json[:rules]&.map do |rule|
36
+ APIRule.from_json(rule)
37
+ end,
38
+ id_type: json[:idType],
39
+ entity: json[:entity],
40
+ explicit_parameters: json[:explicitParameters],
41
+ has_shared_params: json[:hasSharedParams],
42
+ target_app_ids: json[:targetAppIDs]
43
+ )
44
+ end
45
+ end
46
+ end
47
+
48
+ module Statsig
49
+ class APIRule
50
+
51
+ attr_accessor :name, :pass_percentage, :return_value, :id, :salt,
52
+ :conditions, :id_type, :group_name, :config_delegate, :is_experiment_group
53
+
54
+ def initialize(name:, pass_percentage:, return_value:, id:, salt:, conditions:, id_type:,
55
+ group_name: nil, config_delegate: nil, is_experiment_group: nil)
56
+ @name = name
57
+ @pass_percentage = pass_percentage.to_f
58
+ @return_value = JSON.parse(JSON.generate(return_value))
59
+ @id = id
60
+ @salt = salt
61
+ @conditions = conditions
62
+ @id_type = id_type
63
+ @group_name = group_name
64
+ @config_delegate = config_delegate
65
+ @is_experiment_group = is_experiment_group
66
+ end
67
+
68
+ def self.from_json(json)
69
+ new(
70
+ name: json[:name],
71
+ pass_percentage: json[:passPercentage],
72
+ return_value: json[:returnValue] || {},
73
+ id: json[:id],
74
+ salt: json[:salt],
75
+ conditions: json[:conditions]&.map do |condition|
76
+ APICondition.from_json(condition)
77
+ end,
78
+ id_type: json[:idType],
79
+ group_name: json[:groupName],
80
+ config_delegate: json[:configDelegate],
81
+ is_experiment_group: json[:isExperimentGroup]
82
+ )
83
+ end
84
+ end
85
+ end
86
+
87
+ module Statsig
88
+ class APICondition
89
+
90
+ attr_accessor :type, :target_value, :operator, :field, :additional_values, :id_type
91
+
92
+ def initialize(type:, target_value:, operator:, field:, additional_values:, id_type:)
93
+ @type = type.to_sym unless type.nil?
94
+ @target_value = target_value
95
+ @operator = operator.to_sym unless operator.nil?
96
+ @field = field
97
+ @additional_values = additional_values || {}
98
+ @id_type = id_type
99
+ end
100
+
101
+ def self.from_json(json)
102
+ operator = json[:operator]
103
+ unless operator.nil?
104
+ operator = operator&.downcase&.to_sym
105
+ unless Const::SUPPORTED_OPERATORS.include?(operator)
106
+ raise UnsupportedConfigException
107
+ end
108
+ end
109
+
110
+ type = json[:type]
111
+ unless type.nil?
112
+ type = type&.downcase&.to_sym
113
+ unless Const::SUPPORTED_CONDITION_TYPES.include?(type)
114
+ raise UnsupportedConfigException
115
+ end
116
+ end
117
+
118
+ new(
119
+ type: json[:type],
120
+ target_value: json[:targetValue],
121
+ operator: json[:operator],
122
+ field: json[:field],
123
+ additional_values: json[:additionalValues],
124
+ id_type: json[:idType]
125
+ )
126
+ end
127
+ end
128
+ end
@@ -1,148 +1,138 @@
1
- # typed: true
2
-
3
1
  require_relative 'hash_utils'
4
- require 'sorbet-runtime'
5
2
 
3
+ require 'constants'
6
4
 
7
- module ClientInitializeHelpers
5
+ module Statsig
8
6
  class ResponseFormatter
9
- extend T::Sig
10
-
11
- def initialize(evaluator, user, hash, client_sdk_key)
12
- @evaluator = evaluator
13
- @user = user
14
- @specs = evaluator.spec_store.get_raw_specs
15
- @hash = hash
16
- @client_sdk_key = client_sdk_key
17
- end
18
-
19
- def get_responses(key)
20
- @specs[key]
21
- .map { |name, spec| to_response(name, spec) }
22
- .delete_if { |v| v.nil? }.to_h
23
- end
24
-
25
- private
26
-
27
- sig { params(secondary_exposures: T::Array[T::Hash[String, String]]).returns(T::Array[T::Hash[String, String]]) }
28
- def filter_segments_from_secondary_exposures(secondary_exposures)
29
- secondary_exposures.reject do |exposure|
30
- exposure['gate'].to_s.start_with?('segment:')
7
+ def self.get_responses(entities, evaluator, user, client_sdk_key, hash_algo, include_exposures: true)
8
+ result = {}
9
+ entities.each do |name, spec|
10
+ hashed_name, value = to_response(name, spec, evaluator, user, client_sdk_key, hash_algo, include_exposures)
11
+ if !hashed_name.nil? && !value.nil?
12
+ result[hashed_name] = value
13
+ end
31
14
  end
15
+
16
+ result
32
17
  end
33
18
 
34
- def to_response(config_name, config_spec)
35
- target_app_id = @evaluator.spec_store.get_app_id_for_sdk_key(@client_sdk_key)
36
- config_target_apps = config_spec['targetAppIDs']
19
+ def self.to_response(config_name, config_spec, evaluator, user, client_sdk_key, hash_algo, include_exposures)
20
+ target_app_id = evaluator.spec_store.get_app_id_for_sdk_key(client_sdk_key)
21
+ config_target_apps = config_spec.target_app_ids
37
22
 
38
23
  unless target_app_id.nil? || (!config_target_apps.nil? && config_target_apps.include?(target_app_id))
39
24
  return nil
40
25
  end
41
26
 
42
- eval_result = @evaluator.eval_spec(@user, config_spec)
43
- if eval_result.nil?
27
+ category = config_spec.type
28
+ entity_type = config_spec.entity
29
+ if entity_type == :segment || entity_type == :holdout
44
30
  return nil
45
31
  end
46
32
 
47
- category = config_spec['type']
48
- entity_type = config_spec['entity']
33
+ eval_result = ConfigResult.new(
34
+ name: config_name,
35
+ disable_evaluation_details: true,
36
+ disable_exposures: !include_exposures
37
+ )
38
+ evaluator.eval_spec(user, config_spec, eval_result)
49
39
 
50
40
  result = {}
51
41
 
52
- case category
53
-
54
- when 'feature_gate'
55
- if entity_type == 'segment' || entity_type == 'holdout'
56
- return nil
57
- end
42
+ result[:id_type] = eval_result.id_type
43
+ unless eval_result.group_name.nil?
44
+ result[:group_name] = eval_result.group_name
45
+ end
58
46
 
59
- result['value'] = eval_result.gate_value
60
- result["group_name"] = eval_result.group_name
61
- result["id_type"] = eval_result.id_type
62
- when 'dynamic_config'
63
- id_type = config_spec['idType']
64
- result['value'] = eval_result.json_value
65
- result["group"] = eval_result.rule_id
66
- result["group_name"] = eval_result.group_name
67
- result["id_type"] = eval_result.id_type
68
- result["is_device_based"] = id_type.is_a?(String) && id_type.downcase == 'stableid'
47
+ case category
48
+ when :feature_gate
49
+ result[:value] = eval_result.gate_value
50
+ when :dynamic_config
51
+ id_type = config_spec.id_type
52
+ result[:value] = eval_result.json_value
53
+ result[:group] = eval_result.rule_id
54
+ result[:is_device_based] = id_type.is_a?(String) && id_type.downcase == Statsig::Const::STABLEID
69
55
  else
70
56
  return nil
71
57
  end
72
58
 
73
- if entity_type == 'experiment'
74
- populate_experiment_fields(config_name, config_spec, eval_result, result)
59
+ if entity_type == :experiment
60
+ populate_experiment_fields(name, config_spec, eval_result, result, evaluator)
75
61
  end
76
62
 
77
- if entity_type == 'layer'
78
- populate_layer_fields(config_spec, eval_result, result)
79
- result.delete('id_type') # not exposed for layer configs in /initialize
63
+ if entity_type == :layer
64
+ populate_layer_fields(config_spec, eval_result, result, evaluator, hash_algo, include_exposures)
65
+ result.delete(:id_type) # not exposed for layer configs in /initialize
80
66
  end
81
67
 
82
- hashed_name = hash_name(config_name)
83
- [hashed_name, result.merge(
84
- {
85
- "name" => hashed_name,
86
- "rule_id" => eval_result.rule_id,
87
- "secondary_exposures" => clean_exposures(eval_result.secondary_exposures)
88
- }).compact]
68
+ hashed_name = hash_name(config_name, hash_algo)
69
+
70
+ result[:name] = hashed_name
71
+ result[:rule_id] = eval_result.rule_id
72
+
73
+ if include_exposures
74
+ result[:secondary_exposures] = clean_exposures(eval_result.secondary_exposures)
75
+ end
76
+
77
+ [hashed_name, result]
89
78
  end
90
79
 
91
- def clean_exposures(exposures)
80
+ def self.clean_exposures(exposures)
92
81
  seen = {}
93
82
  exposures.reject do |exposure|
94
- key = "#{exposure["gate"]}|#{exposure["gateValue"]}|#{exposure["ruleID"]}}"
83
+ key = "#{exposure[:gate]}|#{exposure[:gateValue]}|#{exposure[:ruleID]}}"
95
84
  should_reject = seen[key]
96
85
  seen[key] = true
97
86
  should_reject == true
98
87
  end
99
88
  end
100
89
 
101
- def populate_experiment_fields(config_name, config_spec, eval_result, result)
102
- result["is_user_in_experiment"] = eval_result.is_experiment_group
103
- result["is_experiment_active"] = config_spec['isActive'] == true
90
+ def self.populate_experiment_fields(config_name, config_spec, eval_result, result, evaluator)
91
+ result[:is_user_in_experiment] = eval_result.is_experiment_group
92
+ result[:is_experiment_active] = config_spec.is_active == true
104
93
 
105
- if config_spec['hasSharedParams'] != true
94
+ if config_spec.has_shared_params != true
106
95
  return
107
96
  end
108
97
 
109
- result["is_in_layer"] = true
110
- result["explicit_parameters"] = config_spec["explicitParameters"] || []
98
+ result[:is_in_layer] = true
99
+ result[:explicit_parameters] = config_spec.explicit_parameters || []
111
100
 
112
- layer_name = @specs[:experiment_to_layer][config_name]
113
- if layer_name.nil? || @specs[:layers][layer_name].nil?
101
+ layer_name = evaluator.spec_store.experiment_to_layer[config_name]
102
+ if layer_name.nil? || evaluator.spec_store.layers[layer_name].nil?
114
103
  return
115
104
  end
116
105
 
117
- layer = @specs[:layers][layer_name]
118
- result["value"] = layer["defaultValue"].merge(result["value"])
106
+ layer = evaluator.spec_store.layers[layer_name]
107
+ result[:value] = layer[:defaultValue].merge(result[:value])
119
108
  end
120
109
 
121
- def populate_layer_fields(config_spec, eval_result, result)
110
+ def self.populate_layer_fields(config_spec, eval_result, result, evaluator, hash_algo, include_exposures)
122
111
  delegate = eval_result.config_delegate
123
- result["explicit_parameters"] = config_spec["explicitParameters"] || []
112
+ result[:explicit_parameters] = config_spec.explicit_parameters || []
124
113
 
125
114
  if delegate.nil? == false && delegate.empty? == false
126
- delegate_spec = @specs[:configs][delegate]
127
- delegate_result = @evaluator.eval_spec(@user, delegate_spec)
115
+ delegate_spec = evaluator.spec_store.configs[delegate]
128
116
 
129
- result["allocated_experiment_name"] = hash_name(delegate)
130
- result["is_user_in_experiment"] = delegate_result.is_experiment_group
131
- result["is_experiment_active"] = delegate_spec['isActive'] == true
132
- result["explicit_parameters"] = delegate_spec["explicitParameters"] || []
117
+ result[:allocated_experiment_name] = hash_name(delegate, hash_algo)
118
+ result[:is_user_in_experiment] = eval_result.is_experiment_group
119
+ result[:is_experiment_active] = delegate_spec.is_active == true
120
+ result[:explicit_parameters] = delegate_spec.explicit_parameters || []
133
121
  end
134
122
 
135
- result["undelegated_secondary_exposures"] = clean_exposures(eval_result.undelegated_sec_exps || [])
123
+ if include_exposures
124
+ result[:undelegated_secondary_exposures] = clean_exposures(eval_result.undelegated_sec_exps || [])
125
+ end
136
126
  end
137
127
 
138
- def hash_name(name)
139
- case @hash
140
- when 'none'
128
+ def self.hash_name(name, hash_algo)
129
+ case hash_algo
130
+ when Statsig::Const::NONE
141
131
  return name
142
- when 'sha256'
143
- return Statsig::HashUtils.sha256(name)
144
- when 'djb2'
132
+ when Statsig::Const::DJB2
145
133
  return Statsig::HashUtils.djb2(name)
134
+ else
135
+ return Statsig::HashUtils.sha256(name)
146
136
  end
147
137
  end
148
138
  end
data/lib/config_result.rb CHANGED
@@ -1,10 +1,5 @@
1
- # typed: true
2
-
3
- require 'sorbet-runtime'
4
-
5
1
  module Statsig
6
2
  class ConfigResult
7
- extend T::Sig
8
3
 
9
4
  attr_accessor :name
10
5
  attr_accessor :gate_value
@@ -19,25 +14,30 @@ module Statsig
19
14
  attr_accessor :group_name
20
15
  attr_accessor :id_type
21
16
  attr_accessor :target_app_ids
17
+ attr_accessor :disable_evaluation_details
18
+ attr_accessor :disable_exposures
22
19
 
23
20
  def initialize(
24
- name,
25
- gate_value = false,
26
- json_value = {},
27
- rule_id = '',
28
- secondary_exposures = [],
29
- config_delegate = nil,
30
- explicit_parameters = [],
21
+ name:,
22
+ gate_value: false,
23
+ json_value: nil,
24
+ rule_id: nil,
25
+ secondary_exposures: [],
26
+ config_delegate: nil,
27
+ explicit_parameters: nil,
31
28
  is_experiment_group: false,
32
29
  evaluation_details: nil,
33
30
  group_name: nil,
34
- id_type: '',
35
- target_app_ids: nil)
31
+ id_type: nil,
32
+ target_app_ids: nil,
33
+ disable_evaluation_details: false,
34
+ disable_exposures: false
35
+ )
36
36
  @name = name
37
37
  @gate_value = gate_value
38
38
  @json_value = json_value
39
39
  @rule_id = rule_id
40
- @secondary_exposures = secondary_exposures.is_a?(Array) ? secondary_exposures : []
40
+ @secondary_exposures = secondary_exposures
41
41
  @undelegated_sec_exps = @secondary_exposures
42
42
  @config_delegate = config_delegate
43
43
  @explicit_parameters = explicit_parameters
@@ -46,9 +46,10 @@ module Statsig
46
46
  @group_name = group_name
47
47
  @id_type = id_type
48
48
  @target_app_ids = target_app_ids
49
+ @disable_evaluation_details = disable_evaluation_details
50
+ @disable_exposures = disable_exposures
49
51
  end
50
52
 
51
- sig { params(config_name: String, user_persisted_values: UserPersistedValues).returns(T.nilable(ConfigResult)) }
52
53
  def self.from_user_persisted_values(config_name, user_persisted_values)
53
54
  sticky_values = user_persisted_values[config_name]
54
55
  return nil if sticky_values.nil?
@@ -56,22 +57,6 @@ module Statsig
56
57
  from_hash(config_name, sticky_values)
57
58
  end
58
59
 
59
- sig { params(config_name: String, hash: Hash).returns(ConfigResult) }
60
- def self.from_hash(config_name, hash)
61
- new(
62
- config_name,
63
- hash['gate_value'],
64
- hash['json_value'],
65
- hash['rule_id'],
66
- hash['secondary_exposures'],
67
- evaluation_details: EvaluationDetails.persisted(hash['config_sync_time'], hash['init_time']),
68
- group_name: hash['group_name'],
69
- id_type: hash['id_type'],
70
- target_app_ids: hash['target_app_ids']
71
- )
72
- end
73
-
74
- sig { returns(Hash) }
75
60
  def to_hash
76
61
  {
77
62
  json_value: @json_value,
data/lib/constants.rb ADDED
@@ -0,0 +1,60 @@
1
+ module Statsig
2
+ module Const
3
+ EMPTY_STR = ''.freeze
4
+
5
+ SUPPORTED_CONDITION_TYPES = Set.new(%i[
6
+ public fail_gate pass_gate ip_based ua_based user_field
7
+ environment_field current_time user_bucket unit_id
8
+ ]).freeze
9
+
10
+ SUPPORTED_OPERATORS = Set.new(%i[
11
+ gt gte lt lte version_gt version_gte version_lt version_lte
12
+ version_eq version_neq any none any_case_sensitive none_case_sensitive
13
+ str_starts_with_any str_ends_with_any str_contains_any str_contains_none
14
+ str_matches eq neq before after on in_segment_list not_in_segment_list
15
+ ]).freeze
16
+
17
+ APP_VERSION = 'app_version'.freeze
18
+ APPVERSION = 'appversion'.freeze
19
+ BROWSER_NAME = 'browser_name'.freeze
20
+ BROWSER_VERSION = 'browser_version'.freeze
21
+ BROWSERNAME = 'browsername'.freeze
22
+ BROWSERVERSION = 'browserversion'.freeze
23
+ CML_SHA_256 = 'sha256'.freeze
24
+ CML_USER_ID = 'userID'.freeze
25
+ COUNTRY = 'country'.freeze
26
+ DEFAULT = 'default'.freeze
27
+ DISABLED = 'disabled'.freeze
28
+ DJB2 = 'djb2'.freeze
29
+ EMAIL = 'email'.freeze
30
+ FALSE = 'false'.freeze
31
+ IP = 'ip'.freeze
32
+ LAYER = :layer
33
+ LOCALE = 'locale'.freeze
34
+ NONE = 'none'.freeze
35
+ OS_NAME = 'os_name'.freeze
36
+ OS_VERSION = 'os_version'.freeze
37
+ OSNAME = 'osname'.freeze
38
+ OSVERSION = 'osversion'.freeze
39
+ OVERRIDE = 'override'.freeze
40
+ Q_RIGHT_CHEVRON = 'Q>'.freeze
41
+ STABLEID = 'stableid'.freeze
42
+ STATSIG_RUBY_SDK = 'statsig-ruby-sdk'.freeze
43
+ TRUE = 'true'.freeze
44
+ USER_AGENT = 'user_agent'.freeze
45
+ USER_ID = 'user_id'.freeze
46
+ USERAGENT = 'useragent'.freeze
47
+ USERID = 'userid'.freeze
48
+
49
+ # Persisted Evaluations
50
+ GATE_VALUE = 'gate_value'.freeze
51
+ JSON_VALUE = 'json_value'.freeze
52
+ RULE_ID = 'rule_id'.freeze
53
+ SECONDARY_EXPOSURES = 'secondary_exposures'.freeze
54
+ GROUP_NAME = 'group_name'.freeze
55
+ ID_TYPE = 'id_type'.freeze
56
+ TARGET_APP_IDS = 'target_app_ids'.freeze
57
+ CONFIG_SYNC_TIME = 'config_sync_time'.freeze
58
+ INIT_TIME = 'init_time'.freeze
59
+ end
60
+ end
data/lib/diagnostics.rb CHANGED
@@ -1,15 +1,7 @@
1
- # typed: true
2
-
3
- require 'sorbet-runtime'
4
-
5
1
  module Statsig
6
2
  class Diagnostics
7
- extend T::Sig
8
-
9
- sig { returns(T::Hash[String, T::Array[T::Hash[Symbol, T.untyped]]]) }
10
3
  attr_reader :markers
11
4
 
12
- sig { returns(T::Hash[String, Numeric]) }
13
5
  attr_accessor :sample_rates
14
6
 
15
7
  def initialize()
@@ -17,16 +9,6 @@ module Statsig
17
9
  @sample_rates = {}
18
10
  end
19
11
 
20
- sig do
21
- params(
22
- key: String,
23
- action: String,
24
- step: T.any(String, NilClass),
25
- tags: T::Hash[Symbol, T.untyped],
26
- context: String
27
- ).void
28
- end
29
-
30
12
  def mark(key, action, step, tags, context)
31
13
  marker = {
32
14
  key: key,
@@ -47,14 +29,6 @@ module Statsig
47
29
  @markers[context].push(marker)
48
30
  end
49
31
 
50
- sig do
51
- params(
52
- context: String,
53
- key: String,
54
- step: T.any(String, NilClass),
55
- tags: T::Hash[Symbol, T.untyped]
56
- ).returns(Tracker)
57
- end
58
32
  def track(context, key, step = nil, tags = {})
59
33
  tracker = Tracker.new(self, context, key, step, tags)
60
34
  tracker.start(**tags)
@@ -87,17 +61,6 @@ module Statsig
87
61
  API_CALL_KEYS = %w[check_gate get_config get_experiment get_layer].freeze
88
62
 
89
63
  class Tracker
90
- extend T::Sig
91
-
92
- sig do
93
- params(
94
- diagnostics: Diagnostics,
95
- context: String,
96
- key: String,
97
- step: T.any(String, NilClass),
98
- tags: T::Hash[Symbol, T.untyped]
99
- ).void
100
- end
101
64
  def initialize(diagnostics, context, key, step, tags = {})
102
65
  @diagnostics = diagnostics
103
66
  @context = context
@@ -115,4 +78,4 @@ module Statsig
115
78
  end
116
79
  end
117
80
  end
118
- end
81
+ end
@@ -1,7 +1,3 @@
1
- # typed: false
2
-
3
- require 'sorbet-runtime'
4
-
5
1
  ##
6
2
  # Contains the current experiment/dynamic config values from Statsig
7
3
  #
@@ -9,46 +5,28 @@ require 'sorbet-runtime'
9
5
  #
10
6
  # Experiments Documentation: https://docs.statsig.com/experiments-plus
11
7
  class DynamicConfig
12
- extend T::Sig
13
8
 
14
- sig { returns(String) }
15
9
  attr_accessor :name
16
10
 
17
- sig { returns(T::Hash[String, T.untyped]) }
18
11
  attr_accessor :value
19
12
 
20
- sig { returns(String) }
21
13
  attr_accessor :rule_id
22
14
 
23
- sig { returns(T.nilable(String)) }
24
15
  attr_accessor :group_name
25
16
 
26
- sig { returns(String) }
27
17
  attr_accessor :id_type
28
18
 
29
- sig { returns(T.nilable(Statsig::EvaluationDetails)) }
30
19
  attr_accessor :evaluation_details
31
20
 
32
- sig do
33
- params(
34
- name: String,
35
- value: T::Hash[String, T.untyped],
36
- rule_id: String,
37
- group_name: T.nilable(String),
38
- id_type: String,
39
- evaluation_details: T.nilable(Statsig::EvaluationDetails)
40
- ).void
41
- end
42
21
  def initialize(name, value = {}, rule_id = '', group_name = nil, id_type = '', evaluation_details = nil)
43
22
  @name = name
44
- @value = value
23
+ @value = value || {}
45
24
  @rule_id = rule_id
46
25
  @group_name = group_name
47
26
  @id_type = id_type
48
27
  @evaluation_details = evaluation_details
49
28
  end
50
29
 
51
- sig { params(index: String, default_value: T.untyped).returns(T.untyped) }
52
30
  ##
53
31
  # Get the value for the given key (index), falling back to the default_value if it cannot be found.
54
32
  #
@@ -59,7 +37,6 @@ class DynamicConfig
59
37
  @value[index]
60
38
  end
61
39
 
62
- sig { params(index: String, default_value: T.untyped).returns(T.untyped) }
63
40
  ##
64
41
  # Get the value for the given key (index), falling back to the default_value if it cannot be found
65
42
  # or is found to have a different type from the default_value.
@@ -1,15 +1,10 @@
1
- # typed: true
2
-
3
1
  require 'statsig_errors'
4
- require 'sorbet-runtime'
5
2
 
6
3
  $endpoint = 'https://statsigapi.net/v1/sdk_exception'
7
4
 
8
5
  module Statsig
9
6
  class ErrorBoundary
10
- extend T::Sig
11
7
 
12
- sig { params(sdk_key: String).void }
13
8
  def initialize(sdk_key)
14
9
  @sdk_key = sdk_key
15
10
  @seen = Set.new
@@ -1,4 +1,3 @@
1
- # typed: true
2
1
  module Statsig
3
2
 
4
3
  module EvaluationReason
@@ -25,6 +24,10 @@ module Statsig
25
24
  @server_time = (Time.now.to_i * 1000).to_s
26
25
  end
27
26
 
27
+ def self.unsupported(config_sync_time, init_time)
28
+ EvaluationDetails.new(config_sync_time, init_time, EvaluationReason::UNSUPPORTED)
29
+ end
30
+
28
31
  def self.unrecognized(config_sync_time, init_time)
29
32
  EvaluationDetails.new(config_sync_time, init_time, EvaluationReason::UNRECOGNIZED)
30
33
  end