configcat 3.1.0 → 5.0.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b842d2ff2f62e6a6c3dbe2ddab53c561e4b1db93f3928ce805044127cd2d7819
4
- data.tar.gz: 6d64d084c4a732d26d54879d914f4e9ce1722b9e73fee2aa4161cc5503e152d7
3
+ metadata.gz: 980bb5bf4d92b6a479a10727b25ea488461317569f3b60943642db8fa9960898
4
+ data.tar.gz: 9de2dc2d9fe881372f70e862bb72af396a9ea99b81defabf3c82e1b19423078e
5
5
  SHA512:
6
- metadata.gz: 83a7d19dc642e492bb5443a1fc42f0f35db8f5278f02582848e8677133f4450faa41a1a30b158ccdca58bc5396cad109b914fa4851e4db74dcf03df1f5129d00
7
- data.tar.gz: 2e79ed9965f3c9da2f8afd9fed4636b6d349b0bda09fdfb0d8398b6e4334a458713114d23083fc5f611878ea02801c5e8ea1e73fad54f6e580ef1c098e8f9d7c
6
+ metadata.gz: 7d1e19a4729ef760d686a4e47c2efd2d82464adfa652caf2782b4ebcfefc3edc7f27baddfdcdd9e66264ee0cd13d9806a51b8c171d74cdd21b6088073aff0368
7
+ data.tar.gz: 1640d5a7a23f9af9461ee38bfb6a789cbed6d898c582bb325f18c27b20dec7944c60dbbe9dc749800a0325be844a252225dfb769f89acd157f644bb1d1a93ef9
@@ -1,9 +1,10 @@
1
1
  require 'configcat/interfaces'
2
+ require 'configcat/constants'
2
3
  require 'concurrent'
3
4
 
4
5
  module ConfigCat
5
6
  class AutoPollingCachePolicy < CachePolicy
6
- def initialize(config_fetcher, config_cache, poll_interval_seconds=60, max_init_wait_time_seconds=5, on_configuration_changed_callback=nil)
7
+ def initialize(config_fetcher, config_cache, cache_key, poll_interval_seconds=60, max_init_wait_time_seconds=5, on_configuration_changed_callback=nil)
7
8
  if poll_interval_seconds < 1
8
9
  poll_interval_seconds = 1
9
10
  end
@@ -12,6 +13,7 @@ module ConfigCat
12
13
  end
13
14
  @_config_fetcher = config_fetcher
14
15
  @_config_cache = config_cache
16
+ @_cache_key = cache_key
15
17
  @_poll_interval_seconds = poll_interval_seconds
16
18
  @_max_init_wait_time_seconds = max_init_wait_time_seconds
17
19
  @_on_configuration_changed_callback = on_configuration_changed_callback
@@ -40,7 +42,7 @@ module ConfigCat
40
42
  end
41
43
  begin
42
44
  @_lock.acquire_read_lock()
43
- return @_config_cache.get()
45
+ return @_config_cache.get(@_cache_key)
44
46
  ensure
45
47
  @_lock.release_read_lock()
46
48
  end
@@ -52,7 +54,7 @@ module ConfigCat
52
54
 
53
55
  begin
54
56
  @_lock.acquire_read_lock()
55
- old_configuration = @_config_cache.get()
57
+ old_configuration = @_config_cache.get(@_cache_key)
56
58
  ensure
57
59
  @_lock.release_read_lock()
58
60
  end
@@ -62,7 +64,7 @@ module ConfigCat
62
64
  if configuration != old_configuration
63
65
  begin
64
66
  @_lock.acquire_write_lock()
65
- @_config_cache.set(configuration)
67
+ @_config_cache.set(@_cache_key, configuration)
66
68
  @_initialized = true
67
69
  ensure
68
70
  @_lock.release_write_lock()
@@ -80,6 +82,9 @@ module ConfigCat
80
82
  if !@_initialized && !old_configuration.equal?(nil)
81
83
  @_initialized = true
82
84
  end
85
+ rescue Timeout::Error => e
86
+ ConfigCat.logger.error("Request timed out. Timeout values: [open: %ss, read: %ss]" %
87
+ [@_config_fetcher.get_open_timeout(), @_config_fetcher.get_read_timeout()])
83
88
  rescue Exception => e
84
89
  ConfigCat.logger.error("Double-check your SDK Key at https://app.configcat.com/sdkkey.")
85
90
  ConfigCat.logger.error "threw exception #{e.class}:'#{e}'"
@@ -3,15 +3,15 @@ require 'configcat/interfaces'
3
3
  module ConfigCat
4
4
  class InMemoryConfigCache < ConfigCache
5
5
  def initialize()
6
- @_value = nil
6
+ @_value = {}
7
7
  end
8
8
 
9
- def get()
10
- return @_value
9
+ def get(key)
10
+ return @_value.fetch(key, nil)
11
11
  end
12
12
 
13
- def set(value)
14
- @_value = value
13
+ def set(key, value)
14
+ @_value[key] = value
15
15
  end
16
16
  end
17
17
  end
@@ -5,25 +5,43 @@ require 'configcat/autopollingcachepolicy'
5
5
  require 'configcat/manualpollingcachepolicy'
6
6
  require 'configcat/lazyloadingcachepolicy'
7
7
  require 'configcat/rolloutevaluator'
8
+ require 'configcat/datagovernance'
9
+
8
10
 
9
11
  module ConfigCat
10
12
  KeyValue = Struct.new(:key, :value)
11
13
  class ConfigCatClient
14
+ @@sdk_keys = []
15
+
12
16
  def initialize(sdk_key,
13
- poll_interval_seconds:60,
14
- max_init_wait_time_seconds:5,
15
- on_configuration_changed_callback:nil,
16
- cache_time_to_live_seconds:60,
17
- config_cache_class:nil,
18
- base_url:nil,
19
- proxy_address:nil,
20
- proxy_port:nil,
21
- proxy_user:nil,
22
- proxy_pass:nil)
17
+ poll_interval_seconds: 60,
18
+ max_init_wait_time_seconds: 5,
19
+ on_configuration_changed_callback: nil,
20
+ cache_time_to_live_seconds: 60,
21
+ config_cache_class: nil,
22
+ base_url: nil,
23
+ proxy_address: nil,
24
+ proxy_port: nil,
25
+ proxy_user: nil,
26
+ proxy_pass: nil,
27
+ open_timeout: 10,
28
+ read_timeout: 30,
29
+ flag_overrides: nil,
30
+ data_governance: DataGovernance::GLOBAL)
23
31
  if sdk_key === nil
24
32
  raise ConfigCatClientException, "SDK Key is required."
25
33
  end
34
+
35
+ if @@sdk_keys.include?(sdk_key)
36
+ ConfigCat.logger.warn("A ConfigCat Client is already initialized with sdk_key %s. "\
37
+ "We strongly recommend you to use the ConfigCat Client as "\
38
+ "a Singleton object in your application." % sdk_key)
39
+ else
40
+ @@sdk_keys.push(sdk_key)
41
+ end
42
+
26
43
  @_sdk_key = sdk_key
44
+ @_override_data_source = flag_overrides
27
45
 
28
46
  if config_cache_class
29
47
  @_config_cache = config_cache_class.new()
@@ -31,23 +49,35 @@ module ConfigCat
31
49
  @_config_cache = InMemoryConfigCache.new()
32
50
  end
33
51
 
34
- if poll_interval_seconds > 0
35
- @_config_fetcher = CacheControlConfigFetcher.new(sdk_key, "p", base_url, proxy_address, proxy_port, proxy_user, proxy_pass)
36
- @_cache_policy = AutoPollingCachePolicy.new(@_config_fetcher, @_config_cache, poll_interval_seconds, max_init_wait_time_seconds, on_configuration_changed_callback)
52
+ if !@_override_data_source.equal?(nil) && @_override_data_source.get_behaviour() == OverrideBehaviour::LOCAL_ONLY
53
+ @_config_fetcher = nil
54
+ @_cache_policy = nil
55
+ elsif poll_interval_seconds > 0
56
+ @_config_fetcher = CacheControlConfigFetcher.new(sdk_key, "p", base_url: base_url,
57
+ proxy_address: proxy_address, proxy_port: proxy_port, proxy_user: proxy_user, proxy_pass: proxy_pass,
58
+ open_timeout: open_timeout, read_timeout: read_timeout,
59
+ data_governance: data_governance)
60
+ @_cache_policy = AutoPollingCachePolicy.new(@_config_fetcher, @_config_cache, _get_cache_key(), poll_interval_seconds, max_init_wait_time_seconds, on_configuration_changed_callback)
61
+ elsif cache_time_to_live_seconds > 0
62
+ @_config_fetcher = CacheControlConfigFetcher.new(sdk_key, "l", base_url: base_url,
63
+ proxy_address: proxy_address, proxy_port: proxy_port, proxy_user: proxy_user, proxy_pass: proxy_pass,
64
+ open_timeout: open_timeout, read_timeout: read_timeout,
65
+ data_governance: data_governance)
66
+ @_cache_policy = LazyLoadingCachePolicy.new(@_config_fetcher, @_config_cache, _get_cache_key(), cache_time_to_live_seconds)
37
67
  else
38
- if cache_time_to_live_seconds > 0
39
- @_config_fetcher = CacheControlConfigFetcher.new(sdk_key, "l", base_url, proxy_address, proxy_port, proxy_user, proxy_pass)
40
- @_cache_policy = LazyLoadingCachePolicy.new(@_config_fetcher, @_config_cache, cache_time_to_live_seconds)
41
- else
42
- @_config_fetcher = CacheControlConfigFetcher.new(sdk_key, "m", base_url, proxy_address, proxy_port, proxy_user, proxy_pass)
43
- @_cache_policy = ManualPollingCachePolicy.new(@_config_fetcher, @_config_cache)
44
- end
68
+ @_config_fetcher = CacheControlConfigFetcher.new(sdk_key, "m", base_url: base_url,
69
+ proxy_address: proxy_address, proxy_port: proxy_port, proxy_user: proxy_user, proxy_pass: proxy_pass,
70
+ open_timeout: open_timeout, read_timeout: read_timeout,
71
+ data_governance: data_governance)
72
+ @_cache_policy = ManualPollingCachePolicy.new(@_config_fetcher, @_config_cache, _get_cache_key())
45
73
  end
46
74
  end
47
75
 
48
76
  def get_value(key, default_value, user=nil)
49
- config = @_cache_policy.get()
77
+ config = _get_settings()
50
78
  if config === nil
79
+ ConfigCat.logger.warn("Evaluating get_value('%s') failed. Cache is empty. "\
80
+ "Returning default_value in your get_value call: [%s]." % [key, default_value.to_s])
51
81
  return default_value
52
82
  end
53
83
  value, variation_id = RolloutEvaluator.evaluate(key, user, default_value, nil, config)
@@ -55,15 +85,19 @@ module ConfigCat
55
85
  end
56
86
 
57
87
  def get_all_keys()
58
- config = @_cache_policy.get()
88
+ config = _get_settings()
59
89
  if config === nil
60
90
  return []
61
91
  end
62
- return config.keys
92
+ feature_flags = config.fetch(FEATURE_FLAGS, nil)
93
+ if feature_flags === nil
94
+ return []
95
+ end
96
+ return feature_flags.keys
63
97
  end
64
98
 
65
99
  def get_variation_id(key, default_variation_id, user=nil)
66
- config = @_cache_policy.get()
100
+ config = _get_settings()
67
101
  if config === nil
68
102
  ConfigCat.logger.warn("Evaluating get_variation_id('%s') failed. Cache is empty. "\
69
103
  "Returning default_variation_id in your get_variation_id call: [%s]." %
@@ -87,39 +121,91 @@ module ConfigCat
87
121
  end
88
122
 
89
123
  def get_key_and_value(variation_id)
90
- config = @_cache_policy.get()
124
+ config = _get_settings()
91
125
  if config === nil
92
126
  ConfigCat.logger.warn("Evaluating get_variation_id('%s') failed. Cache is empty. Returning nil." % variation_id)
93
127
  return nil
94
128
  end
95
- for key, value in config
96
- if variation_id == value.fetch(RolloutEvaluator::VARIATION_ID, nil)
97
- return KeyValue.new(key, value[RolloutEvaluator::VALUE])
129
+
130
+ feature_flags = config.fetch(FEATURE_FLAGS, nil)
131
+ if feature_flags === nil
132
+ ConfigCat.logger.warn("Evaluating get_key_and_value('%s') failed. Cache is empty. Returning None." % variation_id)
133
+ return nil
134
+ end
135
+
136
+ for key, value in feature_flags
137
+ if variation_id == value.fetch(VARIATION_ID, nil)
138
+ return KeyValue.new(key, value[VALUE])
98
139
  end
99
140
 
100
- rollout_rules = value.fetch(RolloutEvaluator::ROLLOUT_RULES, [])
141
+ rollout_rules = value.fetch(ROLLOUT_RULES, [])
101
142
  for rollout_rule in rollout_rules
102
- if variation_id == rollout_rule.fetch(RolloutEvaluator::VARIATION_ID, nil)
103
- return KeyValue.new(key, rollout_rule[RolloutEvaluator::VALUE])
143
+ if variation_id == rollout_rule.fetch(VARIATION_ID, nil)
144
+ return KeyValue.new(key, rollout_rule[VALUE])
104
145
  end
105
146
  end
106
147
 
107
- rollout_percentage_items = value.fetch(RolloutEvaluator::ROLLOUT_PERCENTAGE_ITEMS, [])
148
+ rollout_percentage_items = value.fetch(ROLLOUT_PERCENTAGE_ITEMS, [])
108
149
  for rollout_percentage_item in rollout_percentage_items
109
- if variation_id == rollout_percentage_item.fetch(RolloutEvaluator::VARIATION_ID, nil)
110
- return KeyValue.new(key, rollout_percentage_item[RolloutEvaluator::VALUE])
150
+ if variation_id == rollout_percentage_item.fetch(VARIATION_ID, nil)
151
+ return KeyValue.new(key, rollout_percentage_item[VALUE])
111
152
  end
112
153
  end
113
154
  end
114
155
  end
115
156
 
157
+ def get_all_values(user: nil)
158
+ keys = get_all_keys()
159
+ all_values = {}
160
+ for key in keys
161
+ value = get_value(key, nil, user)
162
+ if !value.equal?(nil)
163
+ all_values[key] = value
164
+ end
165
+ end
166
+ return all_values
167
+ end
168
+
116
169
  def force_refresh()
117
170
  @_cache_policy.force_refresh()
118
171
  end
119
172
 
120
173
  def stop()
121
- @_cache_policy.stop()
122
- @_config_fetcher.close()
174
+ @_cache_policy.stop() if @_cache_policy
175
+ @_config_fetcher.close() if @_config_fetcher
176
+ @@sdk_keys.delete(@_sdk_key)
177
+ end
178
+
179
+ private
180
+
181
+ def _get_settings()
182
+ if !@_override_data_source.nil?
183
+ behaviour = @_override_data_source.get_behaviour()
184
+ if behaviour == OverrideBehaviour::LOCAL_ONLY
185
+ return @_override_data_source.get_overrides()
186
+ elsif behaviour == OverrideBehaviour::REMOTE_OVER_LOCAL
187
+ remote_settings = @_cache_policy.get()
188
+ local_settings = @_override_data_source.get_overrides()
189
+ result = local_settings.clone()
190
+ if remote_settings.key?(FEATURE_FLAGS) && local_settings.key?(FEATURE_FLAGS)
191
+ result[FEATURE_FLAGS] = result[FEATURE_FLAGS].merge(remote_settings[FEATURE_FLAGS])
192
+ end
193
+ return result
194
+ elsif behaviour == OverrideBehaviour::LOCAL_OVER_REMOTE
195
+ remote_settings = @_cache_policy.get()
196
+ local_settings = @_override_data_source.get_overrides()
197
+ result = remote_settings.clone()
198
+ if remote_settings.key?(FEATURE_FLAGS) && local_settings.key?(FEATURE_FLAGS)
199
+ result[FEATURE_FLAGS] = result[FEATURE_FLAGS].merge(local_settings[FEATURE_FLAGS])
200
+ end
201
+ return result
202
+ end
203
+ end
204
+ return @_cache_policy.get()
205
+ end
206
+
207
+ def _get_cache_key()
208
+ return Digest::SHA1.hexdigest("ruby_" + CONFIG_FILE_NAME + "_" + @_sdk_key)
123
209
  end
124
210
 
125
211
  end
@@ -1,13 +1,22 @@
1
1
  require 'configcat/interfaces'
2
2
  require 'configcat/version'
3
+ require 'configcat/datagovernance'
4
+ require 'configcat/constants'
3
5
  require 'net/http'
4
6
  require 'uri'
5
7
  require 'json'
6
8
 
7
9
  module ConfigCat
8
- BASE_URL = "https://cdn.configcat.com"
10
+ BASE_URL_GLOBAL = "https://cdn-global.configcat.com"
11
+ BASE_URL_EU_ONLY = "https://cdn-eu.configcat.com"
9
12
  BASE_PATH = "configuration-files/"
10
- BASE_EXTENSION = "/config_v4.json"
13
+ BASE_EXTENSION = "/" + CONFIG_FILE_NAME + ".json"
14
+
15
+ class RedirectMode
16
+ NO_REDIRECT = 0
17
+ SHOULD_REDIRECT = 1
18
+ FORCE_REDIRECT = 2
19
+ end
11
20
 
12
21
  class FetchResponse
13
22
  def initialize(response)
@@ -32,34 +41,100 @@ module ConfigCat
32
41
  end
33
42
 
34
43
  class CacheControlConfigFetcher < ConfigFetcher
35
- def initialize(sdk_key, mode, base_url=nil, proxy_address=nil, proxy_port=nil, proxy_user=nil, proxy_pass=nil)
44
+ def initialize(sdk_key, mode, base_url:nil, proxy_address:nil, proxy_port:nil, proxy_user:nil, proxy_pass:nil,
45
+ open_timeout:10, read_timeout:30,
46
+ data_governance:DataGovernance::GLOBAL)
36
47
  @_sdk_key = sdk_key
48
+ @_proxy_address = proxy_address
49
+ @_proxy_port = proxy_port
50
+ @_proxy_user = proxy_user
51
+ @_proxy_pass = proxy_pass
52
+ @_open_timeout = open_timeout
53
+ @_read_timeout = read_timeout
37
54
  @_etag = ""
38
55
  @_headers = {"User-Agent" => ((("ConfigCat-Ruby/") + mode) + ("-")) + VERSION, "X-ConfigCat-UserAgent" => ((("ConfigCat-Ruby/") + mode) + ("-")) + VERSION, "Content-Type" => "application/json"}
39
56
  if !base_url.equal?(nil)
57
+ @_base_url_overridden = true
40
58
  @_base_url = base_url.chomp("/")
41
59
  else
42
- @_base_url = BASE_URL
60
+ @_base_url_overridden = false
61
+ if data_governance == DataGovernance::EU_ONLY
62
+ @_base_url = BASE_URL_EU_ONLY
63
+ else
64
+ @_base_url = BASE_URL_GLOBAL
65
+ end
43
66
  end
44
- uri = URI.parse(@_base_url)
45
- @_http = Net::HTTP.new(uri.host, uri.port, proxy_address, proxy_port, proxy_user, proxy_pass)
46
- @_http.use_ssl = true if uri.scheme == 'https'
47
- @_http.open_timeout = 10 # in seconds
48
- @_http.read_timeout = 30 # in seconds
67
+ end
68
+
69
+ def get_open_timeout()
70
+ return @_open_timeout
71
+ end
72
+
73
+ def get_read_timeout()
74
+ return @_read_timeout
49
75
  end
50
76
 
51
77
  # Returns the FetchResponse object contains configuration json Dictionary
52
- def get_configuration_json()
78
+ def get_configuration_json(retries=0)
53
79
  ConfigCat.logger.debug "Fetching configuration from ConfigCat"
54
80
  uri = URI.parse((((@_base_url + ("/")) + BASE_PATH) + @_sdk_key) + BASE_EXTENSION)
55
81
  headers = @_headers
56
82
  headers["If-None-Match"] = @_etag unless @_etag.empty?
83
+ _create_http()
57
84
  request = Net::HTTP::Get.new(uri.request_uri, headers)
58
85
  response = @_http.request(request)
59
86
  etag = response["ETag"]
60
87
  @_etag = etag unless etag.nil? || etag.empty?
61
88
  ConfigCat.logger.debug "ConfigCat configuration json fetch response code:#{response.code} Cached:#{response['ETag']}"
62
- return FetchResponse.new(response)
89
+ fetch_response = FetchResponse.new(response)
90
+
91
+ # If there wasn't a config change, we return the response.
92
+ if !fetch_response.is_fetched()
93
+ return fetch_response
94
+ end
95
+
96
+ preferences = fetch_response.json().fetch(PREFERENCES, nil)
97
+ if preferences === nil
98
+ return fetch_response
99
+ end
100
+
101
+ base_url = preferences.fetch(BASE_URL, nil)
102
+
103
+ # If the base_url is the same as the last called one, just return the response.
104
+ if base_url.equal?(nil) || @_base_url == base_url
105
+ return fetch_response
106
+ end
107
+
108
+ redirect = preferences.fetch(REDIRECT, nil)
109
+ # If the base_url is overridden, and the redirect parameter is not 2 (force),
110
+ # the SDK should not redirect the calls and it just have to return the response.
111
+ if @_base_url_overridden && redirect != RedirectMode::FORCE_REDIRECT
112
+ return fetch_response
113
+ end
114
+
115
+ # The next call should use the base_url provided in the config json
116
+ @_base_url = base_url
117
+
118
+ # If the redirect property == 0 (redirect not needed), return the response
119
+ if redirect == RedirectMode::NO_REDIRECT
120
+ # Return the response
121
+ return fetch_response
122
+ end
123
+
124
+ # Try to download again with the new url
125
+
126
+ if redirect == RedirectMode::SHOULD_REDIRECT
127
+ ConfigCat.logger.warn("Your data_governance parameter at ConfigCatClient initialization is not in sync with your preferences on the ConfigCat Dashboard: https://app.configcat.com/organization/data-governance. Only Organization Admins can set this preference.")
128
+ end
129
+
130
+ # To prevent loops we check if we retried at least 3 times with the new base_url
131
+ if retries >= 2
132
+ ConfigCat.logger.error("Redirect loop during config.json fetch. Please contact support@configcat.com.")
133
+ return fetch_response
134
+ end
135
+
136
+ # Retry the config download with the new base_url
137
+ return get_configuration_json(retries + 1)
63
138
  end
64
139
 
65
140
  def close()
@@ -67,5 +142,19 @@ module ConfigCat
67
142
  @_http = nil
68
143
  end
69
144
  end
145
+
146
+ private
147
+
148
+ def _create_http()
149
+ uri = URI.parse(@_base_url)
150
+ use_ssl = true if uri.scheme == 'https'
151
+ if @_http.equal?(nil) || @_http.address != uri.host || @_http.port != uri.port || @_http.use_ssl? != use_ssl
152
+ close()
153
+ @_http = Net::HTTP.new(uri.host, uri.port, @_proxy_address, @_proxy_port, @_proxy_user, @_proxy_pass)
154
+ @_http.use_ssl = use_ssl
155
+ @_http.open_timeout = @_open_timeout
156
+ @_http.read_timeout = @_read_timeout
157
+ end
158
+ end
70
159
  end
71
160
  end
@@ -0,0 +1,17 @@
1
+ module ConfigCat
2
+ CONFIG_FILE_NAME = "config_v5"
3
+
4
+ PREFERENCES = "p"
5
+ BASE_URL = "u"
6
+ REDIRECT = "r"
7
+
8
+ FEATURE_FLAGS = "f"
9
+ VALUE = "v"
10
+ COMPARATOR = "t"
11
+ COMPARISON_ATTRIBUTE = "a"
12
+ COMPARISON_VALUE = "c"
13
+ ROLLOUT_PERCENTAGE_ITEMS = "p"
14
+ PERCENTAGE = "p"
15
+ ROLLOUT_RULES = "r"
16
+ VARIATION_ID = "i"
17
+ end
@@ -0,0 +1,10 @@
1
+ module ConfigCat
2
+ class DataGovernance
3
+ # Control the location of the config.json files containing your feature flags
4
+ # and settings within the ConfigCat CDN.
5
+ # Global: Select this if your feature flags are published to all global CDN nodes.
6
+ # EuOnly: Select this if your feature flags are published to CDN nodes only in the EU.
7
+ GLOBAL = 0
8
+ EU_ONLY = 1
9
+ end
10
+ end
@@ -23,13 +23,13 @@ module ConfigCat
23
23
  # Config cache interface
24
24
  #
25
25
 
26
- def get()
26
+ def get(key)
27
27
  #
28
28
  # :returns the config json object from the cache
29
29
  #
30
30
  end
31
31
 
32
- def set(value)
32
+ def set(key, value)
33
33
  #
34
34
  # Sets the config json cache.
35
35
  #
@@ -1,15 +1,18 @@
1
1
  require 'configcat/interfaces'
2
+ require 'configcat/constants'
2
3
  require 'concurrent'
3
4
 
5
+
4
6
  module ConfigCat
5
7
  class LazyLoadingCachePolicy < CachePolicy
6
8
 
7
- def initialize(config_fetcher, config_cache, cache_time_to_live_seconds=60)
9
+ def initialize(config_fetcher, config_cache, cache_key, cache_time_to_live_seconds=60)
8
10
  if cache_time_to_live_seconds < 1
9
11
  cache_time_to_live_seconds = 1
10
12
  end
11
13
  @_config_fetcher = config_fetcher
12
14
  @_config_cache = config_cache
15
+ @_cache_key = cache_key
13
16
  @_cache_time_to_live = cache_time_to_live_seconds
14
17
  @_lock = Concurrent::ReadWriteLock.new()
15
18
  @_last_updated = nil
@@ -20,7 +23,7 @@ module ConfigCat
20
23
  @_lock.acquire_read_lock()
21
24
  utc_now = Time.now.utc
22
25
  if !@_last_updated.equal?(nil) && (@_last_updated + @_cache_time_to_live > utc_now)
23
- config = @_config_cache.get()
26
+ config = @_config_cache.get(@_cache_key)
24
27
  if !config.equal?(nil)
25
28
  return config
26
29
  end
@@ -31,7 +34,7 @@ module ConfigCat
31
34
  force_refresh()
32
35
  begin
33
36
  @_lock.acquire_read_lock()
34
- config = @_config_cache.get()
37
+ config = @_config_cache.get(@_cache_key)
35
38
  return config
36
39
  ensure
37
40
  @_lock.release_read_lock()
@@ -45,7 +48,7 @@ module ConfigCat
45
48
  configuration = configuration_response.json()
46
49
  begin
47
50
  @_lock.acquire_write_lock()
48
- @_config_cache.set(configuration)
51
+ @_config_cache.set(@_cache_key, configuration)
49
52
  @_last_updated = Time.now.utc
50
53
  ensure
51
54
  @_lock.release_write_lock()
@@ -0,0 +1,20 @@
1
+ require 'configcat/overridedatasource'
2
+ require 'configcat/constants'
3
+
4
+
5
+ module ConfigCat
6
+ class LocalDictionaryDataSource < OverrideDataSource
7
+ def initialize(source, override_behaviour)
8
+ super(override_behaviour)
9
+ dictionary = {}
10
+ source.each do |key, value|
11
+ dictionary[key] = {VALUE => value}
12
+ end
13
+ @_settings = {FEATURE_FLAGS => dictionary}
14
+ end
15
+
16
+ def get_overrides()
17
+ return @_settings
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,49 @@
1
+ require 'configcat/overridedatasource'
2
+ require 'configcat/constants'
3
+
4
+
5
+ module ConfigCat
6
+ class LocalFileDataSource < OverrideDataSource
7
+ def initialize(file_path, override_behaviour)
8
+ super(override_behaviour)
9
+ if File.exists?(file_path)
10
+ ConfigCat.logger.error("The file '%s' does not exists." % file_path)
11
+ end
12
+ @_file_path = file_path
13
+ @_settings = nil
14
+ @_cached_file_stamp = 0
15
+ end
16
+
17
+ def get_overrides()
18
+ reload_file_content()
19
+ return @_settings
20
+ end
21
+
22
+ private
23
+
24
+ def reload_file_content()
25
+ begin
26
+ stamp = File.mtime(@_file_path)
27
+ if stamp != @_cached_file_stamp
28
+ @_cached_file_stamp = stamp
29
+ file = File.read(@_file_path)
30
+ data = JSON.parse(file)
31
+ if data.key?("flags")
32
+ dictionary = {}
33
+ source = data["flags"]
34
+ source.each do |key, value|
35
+ dictionary[key] = {VALUE => value}
36
+ end
37
+ @_settings = {FEATURE_FLAGS => dictionary}
38
+ else
39
+ @_settings = data
40
+ end
41
+ end
42
+ rescue JSON::ParserError => e
43
+ ConfigCat.logger.error("Could not decode json from file %s. %s" % [@_file_path, e.to_s])
44
+ rescue Exception => e
45
+ ConfigCat.logger.error("Could not read the content of the file %s. %s" % [@_file_path, e.to_s])
46
+ end
47
+ end
48
+ end
49
+ end
@@ -1,18 +1,20 @@
1
1
  require 'configcat/interfaces'
2
+ require 'configcat/constants'
2
3
  require 'concurrent'
3
4
 
4
5
  module ConfigCat
5
6
  class ManualPollingCachePolicy < CachePolicy
6
- def initialize(config_fetcher, config_cache)
7
+ def initialize(config_fetcher, config_cache, cache_key)
7
8
  @_config_fetcher = config_fetcher
8
9
  @_config_cache = config_cache
10
+ @_cache_key = cache_key
9
11
  @_lock = Concurrent::ReadWriteLock.new()
10
12
  end
11
13
 
12
14
  def get()
13
15
  begin
14
16
  @_lock.acquire_read_lock()
15
- config = @_config_cache.get()
17
+ config = @_config_cache.get(@_cache_key)
16
18
  return config
17
19
  ensure
18
20
  @_lock.release_read_lock()
@@ -26,7 +28,7 @@ module ConfigCat
26
28
  configuration = configuration_response.json()
27
29
  begin
28
30
  @_lock.acquire_write_lock()
29
- @_config_cache.set(configuration)
31
+ @_config_cache.set(@_cache_key, configuration)
30
32
  ensure
31
33
  @_lock.release_write_lock()
32
34
  end
@@ -0,0 +1,32 @@
1
+ module ConfigCat
2
+ class OverrideBehaviour
3
+ # When evaluating values, the SDK will not use feature flags & settings from the ConfigCat CDN, but it will use
4
+ # all feature flags & settings that are loaded from local-override sources.
5
+ LOCAL_ONLY = 0
6
+
7
+ # When evaluating values, the SDK will use all feature flags & settings that are downloaded from the ConfigCat CDN,
8
+ # plus all feature flags & settings that are loaded from local-override sources. If a feature flag or a setting is
9
+ # defined both in the fetched and the local-override source then the local-override version will take precedence.
10
+ LOCAL_OVER_REMOTE = 1
11
+
12
+ # When evaluating values, the SDK will use all feature flags & settings that are downloaded from the ConfigCat CDN,
13
+ # plus all feature flags & settings that are loaded from local-override sources. If a feature flag or a setting is
14
+ # defined both in the fetched and the local-override source then the fetched version will take precedence.
15
+ REMOTE_OVER_LOCAL = 2
16
+ end
17
+
18
+ class OverrideDataSource
19
+ def initialize(override_behaviour)
20
+ @_override_behaviour = override_behaviour
21
+ end
22
+
23
+ def get_behaviour()
24
+ return @_override_behaviour
25
+ end
26
+
27
+ def get_overrides()
28
+ # :returns the override dictionary
29
+ return {}
30
+ end
31
+ end
32
+ end
@@ -1,4 +1,5 @@
1
1
  require 'configcat/user'
2
+ require 'configcat/constants'
2
3
  require 'digest'
3
4
  require 'semantic'
4
5
 
@@ -6,21 +7,18 @@ module ConfigCat
6
7
  class RolloutEvaluator
7
8
  COMPARATOR_TEXTS = ["IS ONE OF", "IS NOT ONE OF", "CONTAINS", "DOES NOT CONTAIN", "IS ONE OF (SemVer)", "IS NOT ONE OF (SemVer)", "< (SemVer)", "<= (SemVer)", "> (SemVer)", ">= (SemVer)", "= (Number)", "<> (Number)", "< (Number)", "<= (Number)", "> (Number)", ">= (Number)"]
8
9
 
9
- VALUE = "v"
10
- COMPARATOR = "t"
11
- COMPARISON_ATTRIBUTE = "a"
12
- COMPARISON_VALUE = "c"
13
- ROLLOUT_PERCENTAGE_ITEMS = "p"
14
- PERCENTAGE = "p"
15
- ROLLOUT_RULES = "r"
16
- VARIATION_ID = "i"
17
-
18
10
  def self.evaluate(key, user, default_value, default_variation_id, config)
19
11
  ConfigCat.logger.info("Evaluating get_value('%s')." % key)
20
12
 
21
- setting_descriptor = config.fetch(key, nil)
13
+ feature_flags = config.fetch(FEATURE_FLAGS, nil)
14
+ if feature_flags === nil
15
+ ConfigCat.logger.error("Evaluating get_value('%s') failed. Value not found for key '%s' Returning default_value: [%s]." % [key, key, default_value.to_s])
16
+ return default_value, default_variation_id
17
+ end
18
+
19
+ setting_descriptor = feature_flags.fetch(key, nil)
22
20
  if setting_descriptor === nil
23
- ConfigCat.logger.error("Evaluating get_value('%s') failed. Value not found for key '%s'. Returning default_value: [%s]. Here are the available keys: %s" % [key, key, default_value.to_s, config.keys.join(", ")])
21
+ ConfigCat.logger.error("Evaluating get_value('%s') failed. Value not found for key '%s'. Returning default_value: [%s]. Here are the available keys: %s" % [key, key, default_value.to_s, feature_flags.keys.join(", ")])
24
22
  return default_value, default_variation_id
25
23
  end
26
24
 
@@ -41,132 +39,142 @@ module ConfigCat
41
39
  return return_value, return_variation_id
42
40
  end
43
41
 
44
- ConfigCat.logger.info("User object:\n%s" % user.to_s)
42
+ log_entries = ["Evaluating get_value('%s')." % key, "User object:\n%s" % user.to_s]
45
43
 
46
- # Evaluate targeting rules
47
- for rollout_rule in rollout_rules
48
- comparison_attribute = rollout_rule.fetch(COMPARISON_ATTRIBUTE)
49
- comparison_value = rollout_rule.fetch(COMPARISON_VALUE, nil)
50
- comparator = rollout_rule.fetch(COMPARATOR, nil)
44
+ begin
45
+ # Evaluate targeting rules
46
+ for rollout_rule in rollout_rules
47
+ comparison_attribute = rollout_rule.fetch(COMPARISON_ATTRIBUTE)
48
+ comparison_value = rollout_rule.fetch(COMPARISON_VALUE, nil)
49
+ comparator = rollout_rule.fetch(COMPARATOR, nil)
51
50
 
52
- user_value = user.get_attribute(comparison_attribute)
53
- if user_value === nil || !user_value
54
- ConfigCat.logger.info(format_no_match_rule(comparison_attribute, user_value, comparator, comparison_value))
55
- next
56
- end
51
+ user_value = user.get_attribute(comparison_attribute)
52
+ if user_value === nil || !user_value
53
+ log_entries.push(format_no_match_rule(comparison_attribute, user_value, comparator, comparison_value))
54
+ next
55
+ end
57
56
 
58
- value = rollout_rule.fetch(VALUE, nil)
59
- variation_id = rollout_rule.fetch(VARIATION_ID, default_variation_id)
57
+ value = rollout_rule.fetch(VALUE, nil)
58
+ variation_id = rollout_rule.fetch(VARIATION_ID, default_variation_id)
60
59
 
61
- # IS ONE OF
62
- if comparator == 0
63
- if comparison_value.to_s.split(",").map { |x| x.strip() }.include?(user_value.to_s)
64
- ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
65
- return value, variation_id
66
- end
67
- # IS NOT ONE OF
68
- elsif comparator == 1
69
- if !comparison_value.to_s.split(",").map { |x| x.strip() }.include?(user_value.to_s)
70
- ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
71
- return value, variation_id
72
- end
73
- # CONTAINS
74
- elsif comparator == 2
75
- if user_value.to_s.include?(comparison_value.to_s)
76
- ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
77
- return value, variation_id
78
- end
79
- # DOES NOT CONTAIN
80
- elsif comparator == 3
81
- if !user_value.to_s.include?(comparison_value.to_s)
82
- ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
83
- return value, variation_id
84
- end
85
- # IS ONE OF, IS NOT ONE OF (Semantic version)
86
- elsif (4 <= comparator) && (comparator <= 5)
87
- begin
88
- match = false
89
- user_value_version = Semantic::Version.new(user_value.to_s.strip())
90
- ((comparison_value.to_s.split(",").map { |x| x.strip() }).reject { |c| c.empty? }).each { |x|
91
- version = Semantic::Version.new(x)
92
- match = (user_value_version == version) || match
93
- }
94
- if match && comparator == 4 || !match && comparator == 5
95
- ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
60
+ # IS ONE OF
61
+ if comparator == 0
62
+ if comparison_value.to_s.split(",").map { |x| x.strip() }.include?(user_value.to_s)
63
+ log_entries.push(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
96
64
  return value, variation_id
97
65
  end
98
- rescue ArgumentError => e
99
- ConfigCat.logger.warn(format_validation_error_rule(comparison_attribute, user_value, comparator, comparison_value, e.to_s))
100
- next
101
- end
102
- # LESS THAN, LESS THAN OR EQUALS TO, GREATER THAN, GREATER THAN OR EQUALS TO (Semantic version)
103
- elsif (6 <= comparator) && (comparator <= 9)
104
- begin
105
- user_value_version = Semantic::Version.new(user_value.to_s.strip())
106
- comparison_value_version = Semantic::Version.new(comparison_value.to_s.strip())
107
- if (comparator == 6 && user_value_version < comparison_value_version) ||
108
- (comparator == 7 && user_value_version <= comparison_value_version) ||
109
- (comparator == 8 && user_value_version > comparison_value_version) ||
110
- (comparator == 9 && user_value_version >= comparison_value_version)
111
- ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
66
+ # IS NOT ONE OF
67
+ elsif comparator == 1
68
+ if !comparison_value.to_s.split(",").map { |x| x.strip() }.include?(user_value.to_s)
69
+ log_entries.push(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
112
70
  return value, variation_id
113
71
  end
114
- rescue ArgumentError => e
115
- ConfigCat.logger.warn(format_validation_error_rule(comparison_attribute, user_value, comparator, comparison_value, e.to_s))
116
- next
117
- end
118
- elsif (10 <= comparator) && (comparator <= 15)
119
- begin
120
- user_value_float = Float(user_value.to_s.gsub(",", "."))
121
- comparison_value_float = Float(comparison_value.to_s.gsub(",", "."))
122
- if (comparator == 10 && user_value_float == comparison_value_float) ||
123
- (comparator == 11 && user_value_float != comparison_value_float) ||
124
- (comparator == 12 && user_value_float < comparison_value_float) ||
125
- (comparator == 13 && user_value_float <= comparison_value_float) ||
126
- (comparator == 14 && user_value_float > comparison_value_float) ||
127
- (comparator == 15 && user_value_float >= comparison_value_float)
128
- ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
72
+ # CONTAINS
73
+ elsif comparator == 2
74
+ if user_value.to_s.include?(comparison_value.to_s)
75
+ log_entries.push(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
76
+ return value, variation_id
77
+ end
78
+ # DOES NOT CONTAIN
79
+ elsif comparator == 3
80
+ if !user_value.to_s.include?(comparison_value.to_s)
81
+ log_entries.push(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
82
+ return value, variation_id
83
+ end
84
+ # IS ONE OF, IS NOT ONE OF (Semantic version)
85
+ elsif (4 <= comparator) && (comparator <= 5)
86
+ begin
87
+ match = false
88
+ user_value_version = Semantic::Version.new(user_value.to_s.strip())
89
+ ((comparison_value.to_s.split(",").map { |x| x.strip() }).reject { |c| c.empty? }).each { |x|
90
+ version = Semantic::Version.new(x)
91
+ match = (user_value_version == version) || match
92
+ }
93
+ if match && comparator == 4 || !match && comparator == 5
94
+ log_entries.push(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
95
+ return value, variation_id
96
+ end
97
+ rescue ArgumentError => e
98
+ message = format_validation_error_rule(comparison_attribute, user_value, comparator, comparison_value, e.to_s)
99
+ ConfigCat.logger.warn(message)
100
+ log_entries.push(message)
101
+ next
102
+ end
103
+ # LESS THAN, LESS THAN OR EQUALS TO, GREATER THAN, GREATER THAN OR EQUALS TO (Semantic version)
104
+ elsif (6 <= comparator) && (comparator <= 9)
105
+ begin
106
+ user_value_version = Semantic::Version.new(user_value.to_s.strip())
107
+ comparison_value_version = Semantic::Version.new(comparison_value.to_s.strip())
108
+ if (comparator == 6 && user_value_version < comparison_value_version) ||
109
+ (comparator == 7 && user_value_version <= comparison_value_version) ||
110
+ (comparator == 8 && user_value_version > comparison_value_version) ||
111
+ (comparator == 9 && user_value_version >= comparison_value_version)
112
+ log_entries.push(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
113
+ return value, variation_id
114
+ end
115
+ rescue ArgumentError => e
116
+ message = format_validation_error_rule(comparison_attribute, user_value, comparator, comparison_value, e.to_s)
117
+ ConfigCat.logger.warn(message)
118
+ log_entries.push(message)
119
+ next
120
+ end
121
+ elsif (10 <= comparator) && (comparator <= 15)
122
+ begin
123
+ user_value_float = Float(user_value.to_s.gsub(",", "."))
124
+ comparison_value_float = Float(comparison_value.to_s.gsub(",", "."))
125
+ if (comparator == 10 && user_value_float == comparison_value_float) ||
126
+ (comparator == 11 && user_value_float != comparison_value_float) ||
127
+ (comparator == 12 && user_value_float < comparison_value_float) ||
128
+ (comparator == 13 && user_value_float <= comparison_value_float) ||
129
+ (comparator == 14 && user_value_float > comparison_value_float) ||
130
+ (comparator == 15 && user_value_float >= comparison_value_float)
131
+ log_entries.push(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
132
+ return value, variation_id
133
+ end
134
+ rescue Exception => e
135
+ message = format_validation_error_rule(comparison_attribute, user_value, comparator, comparison_value, e.to_s)
136
+ ConfigCat.logger.warn(message)
137
+ log_entries.push(message)
138
+ next
139
+ end
140
+ # IS ONE OF (Sensitive)
141
+ elsif comparator == 16
142
+ if comparison_value.to_s.split(",").map { |x| x.strip() }.include?(Digest::SHA1.hexdigest(user_value).to_s)
143
+ log_entries.push(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
144
+ return value, variation_id
145
+ end
146
+ # IS NOT ONE OF (Sensitive)
147
+ elsif comparator == 17
148
+ if !comparison_value.to_s.split(",").map { |x| x.strip() }.include?(Digest::SHA1.hexdigest(user_value).to_s)
149
+ log_entries.push(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
129
150
  return value, variation_id
130
151
  end
131
- rescue Exception => e
132
- ConfigCat.logger.warn(format_validation_error_rule(comparison_attribute, user_value, comparator, comparison_value, e.to_s))
133
- next
134
- end
135
- # IS ONE OF (Sensitive)
136
- elsif comparator == 16
137
- if comparison_value.to_s.split(",").map { |x| x.strip() }.include?(Digest::SHA1.hexdigest(user_value).to_s)
138
- ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
139
- return value, variation_id
140
- end
141
- # IS NOT ONE OF (Sensitive)
142
- elsif comparator == 17
143
- if !comparison_value.to_s.split(",").map { |x| x.strip() }.include?(Digest::SHA1.hexdigest(user_value).to_s)
144
- ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
145
- return value, variation_id
146
152
  end
153
+ log_entries.push(format_no_match_rule(comparison_attribute, user_value, comparator, comparison_value))
147
154
  end
148
- ConfigCat.logger.info(format_no_match_rule(comparison_attribute, user_value, comparator, comparison_value))
149
- end
150
155
 
151
- if rollout_percentage_items.size > 0
152
- user_key = user.get_identifier()
153
- hash_candidate = ("%s%s" % [key, user_key]).encode("utf-8")
154
- hash_val = Digest::SHA1.hexdigest(hash_candidate)[0...7].to_i(base=16) % 100
155
- bucket = 0
156
- for rollout_percentage_item in rollout_percentage_items || []
157
- bucket += rollout_percentage_item.fetch(PERCENTAGE, 0)
158
- if hash_val < bucket
159
- percentage_value = rollout_percentage_item.fetch(VALUE, nil)
160
- variation_id = rollout_percentage_item.fetch(VARIATION_ID, default_variation_id)
161
- ConfigCat.logger.info("Evaluating %% options. Returning %s" % percentage_value)
162
- return percentage_value, variation_id
156
+ if rollout_percentage_items.size > 0
157
+ user_key = user.get_identifier()
158
+ hash_candidate = ("%s%s" % [key, user_key]).encode("utf-8")
159
+ hash_val = Digest::SHA1.hexdigest(hash_candidate)[0...7].to_i(base=16) % 100
160
+ bucket = 0
161
+ for rollout_percentage_item in rollout_percentage_items || []
162
+ bucket += rollout_percentage_item.fetch(PERCENTAGE, 0)
163
+ if hash_val < bucket
164
+ percentage_value = rollout_percentage_item.fetch(VALUE, nil)
165
+ variation_id = rollout_percentage_item.fetch(VARIATION_ID, default_variation_id)
166
+ log_entries.push("Evaluating %% options. Returning %s" % percentage_value)
167
+ return percentage_value, variation_id
168
+ end
163
169
  end
164
170
  end
171
+ return_value = setting_descriptor.fetch(VALUE, default_value)
172
+ return_variation_id = setting_descriptor.fetch(VARIATION_ID, default_variation_id)
173
+ log_entries.push("Returning %s" % return_value)
174
+ return return_value, return_variation_id
175
+ ensure
176
+ ConfigCat.logger.info(log_entries.join("\n"))
165
177
  end
166
- return_value = setting_descriptor.fetch(VALUE, default_value)
167
- return_variation_id = setting_descriptor.fetch(VARIATION_ID, default_variation_id)
168
- ConfigCat.logger.info("Returning %s" % return_value)
169
- return return_value, return_variation_id
170
178
  end
171
179
 
172
180
  private
@@ -32,6 +32,25 @@ module ConfigCat
32
32
  end
33
33
  return nil
34
34
  end
35
+
36
+ def to_s()
37
+ r = %Q({\n "Identifier": "#{@__identifier}")
38
+ if !@__data["Email"].equal?(nil)
39
+ r += %Q(,\n "Email": "#{@__data["Email"]}")
40
+ end
41
+ if !@__data["Country"].equal?(nil)
42
+ r += %Q(,\n "Country": "#{@__data["Country"]}")
43
+ end
44
+ if !@__custom.equal?(nil)
45
+ r += %Q(,\n "Custom": {)
46
+ for customField in @__custom
47
+ r += %Q(\n "#{customField}": "#{@__custom[customField]}",)
48
+ end
49
+ r += "\n }"
50
+ end
51
+ r += "\n}"
52
+ return r
53
+ end
35
54
  end
36
55
 
37
56
  end
@@ -1,3 +1,3 @@
1
1
  module ConfigCat
2
- VERSION = "3.1.0"
2
+ VERSION = "5.0.1"
3
3
  end
data/lib/configcat.rb CHANGED
@@ -1,4 +1,6 @@
1
1
  require 'configcat/interfaces'
2
+ require 'configcat/localdictionarydatasource'
3
+ require 'configcat/localfiledatasource'
2
4
  require 'configcat/configcatclient'
3
5
  require 'configcat/user'
4
6
  require 'logger'
@@ -10,13 +12,17 @@ module ConfigCat
10
12
  attr_accessor :logger
11
13
  end
12
14
 
13
- def ConfigCat.create_client(sdk_key)
15
+ def ConfigCat.create_client(sdk_key, data_governance: DataGovernance::GLOBAL)
14
16
  #
15
17
  # Create an instance of ConfigCatClient and setup Auto Poll mode with default options
16
18
  #
17
19
  # :param sdk_key: ConfigCat SDK Key to access your configuration.
20
+ # :param data_governance:
21
+ # Default: Global. Set this parameter to be in sync with the Data Governance preference on the Dashboard:
22
+ # https://app.configcat.com/organization/data-governance
23
+ # (Only Organization Admins have access)
18
24
  #
19
- return create_client_with_auto_poll(sdk_key)
25
+ return create_client_with_auto_poll(sdk_key, data_governance: data_governance)
20
26
  end
21
27
 
22
28
  def ConfigCat.create_client_with_auto_poll(sdk_key,
@@ -25,10 +31,14 @@ module ConfigCat
25
31
  on_configuration_changed_callback: nil,
26
32
  config_cache_class: nil,
27
33
  base_url: nil,
28
- proxy_address:nil,
29
- proxy_port:nil,
30
- proxy_user:nil,
31
- proxy_pass:nil)
34
+ proxy_address: nil,
35
+ proxy_port: nil,
36
+ proxy_user: nil,
37
+ proxy_pass: nil,
38
+ open_timeout: 10,
39
+ read_timeout: 30,
40
+ flag_overrides: nil,
41
+ data_governance: DataGovernance::GLOBAL)
32
42
  #
33
43
  # Create an instance of ConfigCatClient and setup Auto Poll mode with custom options
34
44
  #
@@ -43,6 +53,13 @@ module ConfigCat
43
53
  # :param proxy_port: Proxy port
44
54
  # :param proxy_user: username for proxy authentication
45
55
  # :param proxy_pass: password for proxy authentication
56
+ # :param open_timeout: The number of seconds to wait for the server to make the initial connection. Default: 10 seconds.
57
+ # :param read_timeout: The number of seconds to wait for the server to respond before giving up. Default: 30 seconds.
58
+ # :param flag_overrides: An OverrideDataSource implementation used to override feature flags & settings.
59
+ # :param data_governance:
60
+ # Default: Global. Set this parameter to be in sync with the Data Governance preference on the Dashboard:
61
+ # https://app.configcat.com/organization/data-governance
62
+ # (Only Organization Admins have access)
46
63
  #
47
64
  if sdk_key === nil
48
65
  raise ConfigCatClientException, "SDK Key is required."
@@ -63,17 +80,25 @@ module ConfigCat
63
80
  proxy_address: proxy_address,
64
81
  proxy_port: proxy_port,
65
82
  proxy_user: proxy_user,
66
- proxy_pass: proxy_pass)
83
+ proxy_pass: proxy_pass,
84
+ open_timeout: open_timeout,
85
+ read_timeout: read_timeout,
86
+ flag_overrides: flag_overrides,
87
+ data_governance: data_governance)
67
88
  end
68
89
 
69
90
  def ConfigCat.create_client_with_lazy_load(sdk_key,
70
91
  cache_time_to_live_seconds: 60,
71
92
  config_cache_class: nil,
72
93
  base_url: nil,
73
- proxy_address:nil,
74
- proxy_port:nil,
75
- proxy_user:nil,
76
- proxy_pass:nil)
94
+ proxy_address: nil,
95
+ proxy_port: nil,
96
+ proxy_user: nil,
97
+ proxy_pass: nil,
98
+ open_timeout: 10,
99
+ read_timeout: 30,
100
+ flag_overrides: nil,
101
+ data_governance: DataGovernance::GLOBAL)
77
102
  #
78
103
  # Create an instance of ConfigCatClient and setup Lazy Load mode with custom options
79
104
  #
@@ -86,6 +111,13 @@ module ConfigCat
86
111
  # :param proxy_port: Proxy port
87
112
  # :param proxy_user: username for proxy authentication
88
113
  # :param proxy_pass: password for proxy authentication
114
+ # :param open_timeout: The number of seconds to wait for the server to make the initial connection. Default: 10 seconds.
115
+ # :param read_timeout: The number of seconds to wait for the server to respond before giving up. Default: 30 seconds.
116
+ # :param flag_overrides: An OverrideDataSource implementation used to override feature flags & settings.
117
+ # :param data_governance:
118
+ # Default: Global. Set this parameter to be in sync with the Data Governance preference on the Dashboard:
119
+ # https://app.configcat.com/organization/data-governance
120
+ # (Only Organization Admins have access)
89
121
  #
90
122
  if sdk_key === nil
91
123
  raise ConfigCatClientException, "SDK Key is required."
@@ -103,16 +135,24 @@ module ConfigCat
103
135
  proxy_address: proxy_address,
104
136
  proxy_port: proxy_port,
105
137
  proxy_user: proxy_user,
106
- proxy_pass: proxy_pass)
138
+ proxy_pass: proxy_pass,
139
+ open_timeout: open_timeout,
140
+ read_timeout: read_timeout,
141
+ flag_overrides: flag_overrides,
142
+ data_governance: data_governance)
107
143
  end
108
144
 
109
145
  def ConfigCat.create_client_with_manual_poll(sdk_key,
110
146
  config_cache_class: nil,
111
147
  base_url: nil,
112
- proxy_address:nil,
113
- proxy_port:nil,
114
- proxy_user:nil,
115
- proxy_pass:nil)
148
+ proxy_address: nil,
149
+ proxy_port: nil,
150
+ proxy_user: nil,
151
+ proxy_pass: nil,
152
+ open_timeout: 10,
153
+ read_timeout: 30,
154
+ flag_overrides: nil,
155
+ data_governance: DataGovernance::GLOBAL)
116
156
  #
117
157
  # Create an instance of ConfigCatClient and setup Manual Poll mode with custom options
118
158
  #
@@ -124,6 +164,13 @@ module ConfigCat
124
164
  # :param proxy_port: Proxy port
125
165
  # :param proxy_user: username for proxy authentication
126
166
  # :param proxy_pass: password for proxy authentication
167
+ # :param open_timeout: The number of seconds to wait for the server to make the initial connection. Default: 10 seconds.
168
+ # :param read_timeout: The number of seconds to wait for the server to respond before giving up. Default: 30 seconds.
169
+ # :param flag_overrides: An OverrideDataSource implementation used to override feature flags & settings.
170
+ # :param data_governance:
171
+ # Default: Global. Set this parameter to be in sync with the Data Governance preference on the Dashboard:
172
+ # https://app.configcat.com/organization/data-governance
173
+ # (Only Organization Admins have access)
127
174
  #
128
175
  if sdk_key === nil
129
176
  raise ConfigCatClientException, "SDK Key is required."
@@ -138,7 +185,11 @@ module ConfigCat
138
185
  proxy_address: proxy_address,
139
186
  proxy_port: proxy_port,
140
187
  proxy_user: proxy_user,
141
- proxy_pass: proxy_pass)
188
+ proxy_pass: proxy_pass,
189
+ open_timeout: open_timeout,
190
+ read_timeout: read_timeout,
191
+ flag_overrides: flag_overrides,
192
+ data_governance: data_governance)
142
193
  end
143
194
 
144
195
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: configcat
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0
4
+ version: 5.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - ConfigCat
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-14 00:00:00.000000000 Z
11
+ date: 2022-05-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -67,19 +67,19 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '12.3'
69
69
  - !ruby/object:Gem::Dependency
70
- name: coveralls
70
+ name: codecov
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '0.8'
75
+ version: '0.5'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '0.8'
82
+ version: '0.5'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: webmock
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -110,9 +110,14 @@ files:
110
110
  - lib/configcat/configcache.rb
111
111
  - lib/configcat/configcatclient.rb
112
112
  - lib/configcat/configfetcher.rb
113
+ - lib/configcat/constants.rb
114
+ - lib/configcat/datagovernance.rb
113
115
  - lib/configcat/interfaces.rb
114
116
  - lib/configcat/lazyloadingcachepolicy.rb
117
+ - lib/configcat/localdictionarydatasource.rb
118
+ - lib/configcat/localfiledatasource.rb
115
119
  - lib/configcat/manualpollingcachepolicy.rb
120
+ - lib/configcat/overridedatasource.rb
116
121
  - lib/configcat/rolloutevaluator.rb
117
122
  - lib/configcat/user.rb
118
123
  - lib/configcat/version.rb
@@ -135,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
135
140
  - !ruby/object:Gem::Version
136
141
  version: '0'
137
142
  requirements: []
138
- rubygems_version: 3.0.8
143
+ rubygems_version: 3.0.3.1
139
144
  signing_key:
140
145
  specification_version: 4
141
146
  summary: ConfigCat SDK for Ruby.