devcycle-ruby-server-sdk 2.0.2 → 2.0.3

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: 86e5a04ee7157791c8ed075df281ecc09bad3ae898c7e48d520046953ed5aee7
4
- data.tar.gz: 44271e4418ab7b2219403d9a90bbc702befabb034446bc58a2f32a365038f9cb
3
+ metadata.gz: 960c105106387fcb10b360f0befbb77d2dda8c80dce5a51a5eed3c8c49528253
4
+ data.tar.gz: 1c9274022cece19d953ceba7adfed10cd6418e4a3097ad3305f1a539ace32e23
5
5
  SHA512:
6
- metadata.gz: f5946e97ac8b2d0abda40af307609035945e1881587edc5d1d37c01fc1f75e9e0461527f3c232713ba0c839834b2447e4ab95a3f7a471e314fe28c01c15f3281
7
- data.tar.gz: 147519317a620feef9d975bcb3a5cb844c94e03b7f6fb8631d34e98b57b770a5994c4324dd9c1359e7e744b0b5652f6a5f612fdbc0f68a600816187717b22c43
6
+ metadata.gz: 67cb4a92cb5c9247bd10c838cfa0979d89d331c75c5626967af9b4602c55c30a2842f94f2c15471bb459686382be20432e43bf9951f1c27f516cbc5b2d8a504e
7
+ data.tar.gz: 802ec0ab8f6b2a7623fe58c94cd007484832c06c5742a38831bebd0991ef2c3b57cc99563ffcee89a545742c0fe1080beea95be01aab88f27983d5d808b359f1
@@ -60,10 +60,16 @@ module DevCycle
60
60
  end
61
61
 
62
62
  def set_client_custom_data(customdata)
63
- if @api_client.config.enable_cloud_bucketing
64
- fail ArgumentError("Client Custom Data is only available in Local bucketing mode.")
63
+ if @dvc_options.enable_cloud_bucketing
64
+ raise StandardError.new("Client Custom Data is only available in Local bucketing mode.")
65
+ end
66
+
67
+ if local_bucketing_initialized?
68
+ @localbucketing.set_client_custom_data(customdata)
69
+ else
70
+ @logger.warn("Local bucketing not initialized. Unable to set client custom data.")
65
71
  end
66
- @localbucketing.set_client_custom_data(customdata)
72
+ nil
67
73
  end
68
74
 
69
75
  def validate_model(model)
@@ -87,7 +93,7 @@ module DevCycle
87
93
  return data
88
94
  end
89
95
 
90
- if local_bucketing_initialized?
96
+ if local_bucketing_initialized? && @localbucketing.has_config
91
97
  bucketed_config = @localbucketing.generate_bucketed_config(user_data)
92
98
  bucketed_config.features
93
99
  else
@@ -170,30 +176,59 @@ module DevCycle
170
176
  return data
171
177
  end
172
178
 
173
- if local_bucketing_initialized?
179
+ if local_bucketing_initialized? && @localbucketing.has_config
174
180
  bucketed_config = @localbucketing.generate_bucketed_config(user_data)
175
181
  variable_json = bucketed_config.variables[key]
176
182
  if variable_json == nil
183
+ @logger.warn("No variable found for key #{key}, returning default value")
177
184
  variable_event = Event.new({ type: DevCycle::EventTypes[:agg_variable_defaulted], target: key })
178
185
  @event_queue.queue_aggregate_event(variable_event, bucketed_config)
179
186
 
180
- return Variable.new({ key: key, value: default, isDefaulted: true })
187
+ return Variable.new({
188
+ key: key,
189
+ type: determine_variable_type(default),
190
+ value: default,
191
+ defaultValue: default,
192
+ isDefaulted: true
193
+ })
181
194
  end
195
+ default_type = determine_variable_type(default)
196
+ variable_type = variable_json['type']
197
+ if default_type != variable_type
198
+ @logger.warn("Type mismatch for variable #{key}, returning default value")
199
+ variable_event = Event.new({ type: DevCycle::EventTypes[:agg_variable_defaulted], target: key })
200
+ @event_queue.queue_aggregate_event(variable_event, bucketed_config)
182
201
 
202
+ return Variable.new({
203
+ key: key,
204
+ type: default_type,
205
+ value: default,
206
+ defaultValue: default,
207
+ isDefaulted: true
208
+ })
209
+ end
183
210
  variable_event = Event.new({ type: DevCycle::EventTypes[:agg_variable_evaluated], target: key })
184
211
  @event_queue.queue_aggregate_event(variable_event, bucketed_config)
185
212
 
186
213
  Variable.new({
187
- key: key,
188
- type: variable_json['type'],
189
- value: variable_json['value'],
190
- isDefaulted: false
191
- })
214
+ key: key,
215
+ type: variable_type,
216
+ value: variable_json['value'],
217
+ defaultValue: default,
218
+ isDefaulted: false
219
+ })
192
220
  else
221
+ @logger.warn("Local bucketing not initialized, returning default value for variable #{key}")
193
222
  variable_event = Event.new({ type: DevCycle::EventTypes[:agg_variable_defaulted], target: key })
194
223
  @event_queue.queue_aggregate_event(variable_event, bucketed_config)
195
224
 
196
- Variable.new({ key: key, value: default, isDefaulted: true })
225
+ Variable.new({
226
+ key: key,
227
+ type: determine_variable_type(default),
228
+ value: default,
229
+ defaultValue: default,
230
+ isDefaulted: true
231
+ })
197
232
  end
198
233
  end
199
234
 
@@ -284,7 +319,7 @@ module DevCycle
284
319
  return data
285
320
  end
286
321
 
287
- if local_bucketing_initialized?
322
+ if local_bucketing_initialized? && @localbucketing.has_config
288
323
  bucketed_config = @localbucketing.generate_bucketed_config(user_data)
289
324
  bucketed_config.variables
290
325
  else
@@ -453,5 +488,19 @@ module DevCycle
453
488
  def local_bucketing_initialized?
454
489
  !@localbucketing.nil? && @localbucketing.initialized
455
490
  end
491
+
492
+ def determine_variable_type(variable_value)
493
+ if variable_value.is_a?(String)
494
+ 'String'
495
+ elsif variable_value.is_a?(TrueClass) || variable_value.is_a?(FalseClass)
496
+ 'Boolean'
497
+ elsif variable_value.is_a?(Integer) || variable_value.is_a?(Float)
498
+ 'Number'
499
+ elsif variable_value.is_a?(Hash)
500
+ 'JSON'
501
+ else
502
+ raise ArgumentError, "Invalid type for variable: #{variable_value}"
503
+ end
504
+ end
456
505
  end
457
506
  end
@@ -20,19 +20,33 @@ module DevCycle
20
20
  @sdkKey = sdkKey
21
21
  @config_e_tag = ""
22
22
  @logger = local_bucketing.options.logger
23
+ @polling_enabled = true
24
+ @max_config_retries = 2
23
25
 
24
- @config_poller = Concurrent::TimerTask.new(
25
- {
26
+ @config_poller = Concurrent::TimerTask.new({
26
27
  execution_interval: @local_bucketing.options.config_polling_interval_ms.fdiv(1000)
27
28
  }) do |task|
28
- fetch_config(false, task)
29
+ fetch_config
29
30
  end
30
31
 
31
- t = Thread.new { fetch_config(false, nil) }
32
+ t = Thread.new { initialize_config }
32
33
  t.join if wait_for_init
33
34
  end
34
35
 
35
- def fetch_config(retrying, task)
36
+ def initialize_config
37
+ begin
38
+ fetch_config
39
+ @config_poller.execute if @polling_enabled
40
+ rescue => e
41
+ @logger.error("DVC Error Initializing Config: #{e.message}")
42
+ ensure
43
+ @local_bucketing.initialized = true
44
+ end
45
+ end
46
+
47
+ def fetch_config
48
+ return unless @polling_enabled
49
+
36
50
  req = Typhoeus::Request.new(
37
51
  get_config_url,
38
52
  headers: {
@@ -43,25 +57,26 @@ module DevCycle
43
57
  req.options[:headers]['If-None-Match'] = @config_e_tag
44
58
  end
45
59
 
46
- resp = req.run
47
-
48
- case resp.code
49
- when 304
50
- return nil
51
- when 200
52
- return set_config(resp.body, resp.headers['Etag'])
53
- when 403
54
- raise("Failed to download DevCycle config; Invalid SDK Key.")
55
- when 500...599
56
- if !retrying
57
- return fetch_config(true, task)
60
+ @max_config_retries.times do
61
+ resp = req.run
62
+ case resp.code
63
+ when 304
64
+ @logger.debug("Config not modified, using cache, etag: #{this.configEtag}")
65
+ return
66
+ when 200
67
+ set_config(resp.body, resp.headers['Etag'])
68
+ return
69
+ when 403
70
+ stop_polling
71
+ @logger.error("Failed to download DevCycle config; Invalid SDK Key.")
72
+ return
73
+ when 500...599
74
+ @logger.error("Failed to download DevCycle config. Status: #{resp.code}")
75
+ else
76
+ stop_polling
77
+ @logger.error("Unexpected response code - DevCycle Response: #{Oj.dump(resp)}")
78
+ return
58
79
  end
59
- @logger.warn("Failed to download DevCycle config. Status: #{resp.code}")
60
- else
61
- if task != nil
62
- task.shutdown
63
- end
64
- raise("Unexpected response code - DevCycle Response: #{Oj.dump(resp)}")
65
80
  end
66
81
 
67
82
  nil
@@ -74,13 +89,7 @@ module DevCycle
74
89
 
75
90
  @local_bucketing.store_config(config)
76
91
  @config_e_tag = etag
77
-
78
- if @first_load
79
- @logger.info("Config Set. Client Initialized.")
80
- @first_load = false
81
- @local_bucketing.initialized = true
82
- @config_poller.execute
83
- end
92
+ @local_bucketing.has_config = true
84
93
  end
85
94
 
86
95
  def get_config_url
@@ -88,8 +97,13 @@ module DevCycle
88
97
  "#{configBasePath}/config/#{@config_version}/server/#{@sdkKey}.json"
89
98
  end
90
99
 
100
+ def stop_polling
101
+ @polling_enabled = false
102
+ @config_poller.shutdown if @config_poller.running?
103
+ end
104
+
91
105
  def close
92
- @config_poller.shutdown
106
+ @config_poller.shutdown if @config_poller.running?
93
107
  nil
94
108
  end
95
109
  end
@@ -14,6 +14,7 @@ module DevCycle
14
14
 
15
15
  attr_reader :options
16
16
  attr_accessor :initialized
17
+ attr_accessor :has_config
17
18
 
18
19
  @@rand = Random.new(seed = Random.new_seed)
19
20
  @@engine = Wasmtime::Engine.new
@@ -86,18 +87,19 @@ module DevCycle
86
87
  ).void }
87
88
  def initialize(sdkkey, options, wait_for_init)
88
89
  @initialized = false
90
+ @has_config = false
89
91
  @sdkkey = sdkkey
90
92
  @options = options
91
93
  @logger = options.logger
92
94
  set_sdk_key_internal(sdkkey)
93
95
  platform_data = PlatformData.new('server', VERSION, RUBY_VERSION, nil, 'Ruby', Socket.gethostname)
94
96
  set_platform_data(platform_data)
95
- @configmanager = ConfigManager.new(@sdkkey, self, wait_for_init)
97
+ @config_manager = ConfigManager.new(@sdkkey, self, wait_for_init)
96
98
  end
97
99
 
98
100
  def close
99
- @configmanager.close
100
- @configmanager = nil
101
+ @config_manager.close
102
+ @config_manager = nil
101
103
  end
102
104
 
103
105
  sig { params(user: UserData).returns(BucketedUserConfig) }
@@ -236,7 +238,7 @@ module DevCycle
236
238
  wasm_object_id = 1
237
239
  @@stack_tracer = lambda { |message| raise message }
238
240
  wasm_new = @@instance.export("__new").to_func
239
- utf8_bytes = string.encode("iso-8859-1").force_encoding("utf-8").bytes
241
+ utf8_bytes = string.bytes
240
242
  byte_len = utf8_bytes.length
241
243
 
242
244
  start_addr = wasm_new.call(byte_len * 2, wasm_object_id)
@@ -27,6 +27,8 @@ module DevCycle
27
27
  # Set to true if the Variable could not be fetched
28
28
  attr_accessor :isDefaulted
29
29
 
30
+ attr_accessor :defaultValue
31
+
30
32
  class EnumAttributeValidator
31
33
  attr_reader :datatype
32
34
  attr_reader :allowable_values
@@ -55,6 +57,7 @@ module DevCycle
55
57
  :'key' => :'key',
56
58
  :'type' => :'type',
57
59
  :'value' => :'value',
60
+ :'defaultValue' => :'defaultValue',
58
61
  :'isDefaulted' => :'isDefaulted'
59
62
  }
60
63
  end
@@ -70,6 +73,7 @@ module DevCycle
70
73
  :'key' => :'String',
71
74
  :'type' => :'String',
72
75
  :'value' => :'Object',
76
+ :'defaultValue' => :'Object',
73
77
  :'isDefaulted' => :'Boolean'
74
78
  }
75
79
  end
@@ -112,6 +116,10 @@ module DevCycle
112
116
  else
113
117
  self.isDefaulted = false
114
118
  end
119
+
120
+ if attributes.key?(:'defaultValue')
121
+ self.defaultValue = attributes[:'defaultValue']
122
+ end
115
123
  end
116
124
 
117
125
  # Show invalid properties with the reasons. Usually used together with valid?
@@ -296,7 +304,5 @@ module DevCycle
296
304
  value
297
305
  end
298
306
  end
299
-
300
307
  end
301
-
302
308
  end
@@ -11,5 +11,5 @@ OpenAPI Generator version: 5.3.0
11
11
  =end
12
12
 
13
13
  module DevCycle
14
- VERSION = '2.0.2'
14
+ VERSION = '2.0.3'
15
15
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devcycle-ruby-server-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.2
4
+ version: 2.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - DevCycleHQ
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-03-07 00:00:00.000000000 Z
11
+ date: 2023-03-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: typhoeus