devcycle-ruby-server-sdk 2.0.3 → 2.1.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: 960c105106387fcb10b360f0befbb77d2dda8c80dce5a51a5eed3c8c49528253
4
- data.tar.gz: 1c9274022cece19d953ceba7adfed10cd6418e4a3097ad3305f1a539ace32e23
3
+ metadata.gz: 66277b1d5bea79db7794168b2f9be005a3c37ef6ea0dc1d9b0cbf876c5fb1c17
4
+ data.tar.gz: 94381d579783bffc333ac56391ea530466c796a44ce700c25dc99267409412ed
5
5
  SHA512:
6
- metadata.gz: 67cb4a92cb5c9247bd10c838cfa0979d89d331c75c5626967af9b4602c55c30a2842f94f2c15471bb459686382be20432e43bf9951f1c27f516cbc5b2d8a504e
7
- data.tar.gz: 802ec0ab8f6b2a7623fe58c94cd007484832c06c5742a38831bebd0991ef2c3b57cc99563ffcee89a545742c0fe1080beea95be01aab88f27983d5d808b359f1
6
+ metadata.gz: 9b958f804a26259dadd8f33d46a0326d9c4ca5a8aad5c0a230ffdbb54d0adc7d82714677add429dc5161c855d31a0e1a19fdc0461db70a0945436285160996b2
7
+ data.tar.gz: 731696f8b1129e8151031d8ede467f646f0c910f747a36dfc9ab54b127db6097eddfcbb35afc2cf1d49da2d53a8c22e20e0d908f9a6371845a7f67e63a84b701
@@ -32,8 +32,8 @@ module DevCycle
32
32
  @api_client.config.enable_edge_db = @dvc_options.enable_edge_db
33
33
  @api_client.config.logger = @logger
34
34
  else
35
- @localbucketing = LocalBucketing.new(@sdkKey, dvc_options, wait_for_init)
36
- @event_queue = EventQueue.new(@sdkKey, dvc_options.event_queue_options, @localbucketing)
35
+ @local_bucketing = LocalBucketing.new(@sdkKey, dvc_options, wait_for_init)
36
+ @event_queue = EventQueue.new(@sdkKey, dvc_options.event_queue_options, @local_bucketing)
37
37
  end
38
38
  end
39
39
 
@@ -42,15 +42,15 @@ module DevCycle
42
42
  @logger.info("Cloud Bucketing does not require closing.")
43
43
  return
44
44
  end
45
- if @localbucketing != nil
46
- if !@localbucketing.initialized
45
+ if @local_bucketing != nil
46
+ if !@local_bucketing.initialized
47
47
  @logger.info("Awaiting client initialization before closing")
48
- while !@localbucketing.initialized
48
+ while !@local_bucketing.initialized
49
49
  sleep(0.5)
50
50
  end
51
51
  end
52
- @localbucketing.close
53
- @localbucketing = nil
52
+ @local_bucketing.close
53
+ @local_bucketing = nil
54
54
  @logger.info("Closed DevCycle Local Bucketing Engine.")
55
55
  end
56
56
 
@@ -65,7 +65,7 @@ module DevCycle
65
65
  end
66
66
 
67
67
  if local_bucketing_initialized?
68
- @localbucketing.set_client_custom_data(customdata)
68
+ @local_bucketing.set_client_custom_data(customdata)
69
69
  else
70
70
  @logger.warn("Local bucketing not initialized. Unable to set client custom data.")
71
71
  end
@@ -93,8 +93,8 @@ module DevCycle
93
93
  return data
94
94
  end
95
95
 
96
- if local_bucketing_initialized? && @localbucketing.has_config
97
- bucketed_config = @localbucketing.generate_bucketed_config(user_data)
96
+ if local_bucketing_initialized? && @local_bucketing.has_config
97
+ bucketed_config = @local_bucketing.generate_bucketed_config(user_data)
98
98
  bucketed_config.features
99
99
  else
100
100
  {}
@@ -176,60 +176,40 @@ module DevCycle
176
176
  return data
177
177
  end
178
178
 
179
- if local_bucketing_initialized? && @localbucketing.has_config
180
- bucketed_config = @localbucketing.generate_bucketed_config(user_data)
181
- variable_json = bucketed_config.variables[key]
179
+ value = default
180
+ type = determine_variable_type(default)
181
+ defaulted = true
182
+ if local_bucketing_initialized? && @local_bucketing.has_config
183
+ type_code = variable_type_code_from_type(type)
184
+ variable_json = variable_for_user(user_data, key, type_code)
182
185
  if variable_json == nil
183
186
  @logger.warn("No variable found for key #{key}, returning default value")
184
- variable_event = Event.new({ type: DevCycle::EventTypes[:agg_variable_defaulted], target: key })
185
- @event_queue.queue_aggregate_event(variable_event, bucketed_config)
186
-
187
- return Variable.new({
188
- key: key,
189
- type: determine_variable_type(default),
190
- value: default,
191
- defaultValue: default,
192
- isDefaulted: true
193
- })
194
- end
195
- default_type = determine_variable_type(default)
196
- variable_type = variable_json['type']
197
- if default_type != variable_type
187
+ elsif type != variable_json['type']
198
188
  @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)
201
-
202
- return Variable.new({
203
- key: key,
204
- type: default_type,
205
- value: default,
206
- defaultValue: default,
207
- isDefaulted: true
208
- })
189
+ else
190
+ value = variable_json['value']
191
+ defaulted = false
209
192
  end
210
- variable_event = Event.new({ type: DevCycle::EventTypes[:agg_variable_evaluated], target: key })
211
- @event_queue.queue_aggregate_event(variable_event, bucketed_config)
212
-
213
- Variable.new({
214
- key: key,
215
- type: variable_type,
216
- value: variable_json['value'],
217
- defaultValue: default,
218
- isDefaulted: false
219
- })
220
193
  else
221
194
  @logger.warn("Local bucketing not initialized, returning default value for variable #{key}")
222
195
  variable_event = Event.new({ type: DevCycle::EventTypes[:agg_variable_defaulted], target: key })
196
+ bucketed_config = BucketedUserConfig.new({}, {}, {}, {}, {}, {}, [])
223
197
  @event_queue.queue_aggregate_event(variable_event, bucketed_config)
224
-
225
- Variable.new({
226
- key: key,
227
- type: determine_variable_type(default),
228
- value: default,
229
- defaultValue: default,
230
- isDefaulted: true
231
- })
232
198
  end
199
+
200
+ Variable.new({
201
+ key: key,
202
+ value: value,
203
+ type: type,
204
+ defaultValue: default,
205
+ isDefaulted: defaulted
206
+ })
207
+ end
208
+
209
+ def variable_for_user(user, key, variable_type_code)
210
+ json_str = @local_bucketing.variable_for_user(user, key, variable_type_code)
211
+ return nil if json_str.nil?
212
+ JSON.parse(json_str)
233
213
  end
234
214
 
235
215
  # Get variable by key for user data
@@ -319,8 +299,8 @@ module DevCycle
319
299
  return data
320
300
  end
321
301
 
322
- if local_bucketing_initialized? && @localbucketing.has_config
323
- bucketed_config = @localbucketing.generate_bucketed_config(user_data)
302
+ if local_bucketing_initialized? && @local_bucketing.has_config
303
+ bucketed_config = @local_bucketing.generate_bucketed_config(user_data)
324
304
  bucketed_config.variables
325
305
  else
326
306
  {}
@@ -486,7 +466,7 @@ module DevCycle
486
466
  end
487
467
 
488
468
  def local_bucketing_initialized?
489
- !@localbucketing.nil? && @localbucketing.initialized
469
+ !@local_bucketing.nil? && @local_bucketing.initialized
490
470
  end
491
471
 
492
472
  def determine_variable_type(variable_value)
@@ -502,5 +482,20 @@ module DevCycle
502
482
  raise ArgumentError, "Invalid type for variable: #{variable_value}"
503
483
  end
504
484
  end
485
+
486
+ def variable_type_code_from_type(type)
487
+ case type
488
+ when 'String'
489
+ @local_bucketing.variable_type_codes[:string]
490
+ when 'Boolean'
491
+ @local_bucketing.variable_type_codes[:boolean]
492
+ when 'Number'
493
+ @local_bucketing.variable_type_codes[:number]
494
+ when 'JSON'
495
+ @local_bucketing.variable_type_codes[:json]
496
+ else
497
+ raise ArgumentError.new("Invalid type for variable: #{type}")
498
+ end
499
+ end
505
500
  end
506
501
  end
@@ -13,6 +13,7 @@ module DevCycle
13
13
  extend T::Sig
14
14
 
15
15
  attr_reader :options
16
+ attr_reader :variable_type_codes
16
17
  attr_accessor :initialized
17
18
  attr_accessor :has_config
18
19
 
@@ -48,6 +49,8 @@ module DevCycle
48
49
  result
49
50
  }
50
51
 
52
+ @@stack_tracer_raise = lambda { |message| raise message }
53
+ # each method reassigns stack_tracer so the call stack is properly displayed
51
54
  @@stack_tracer = lambda {}
52
55
 
53
56
  @@linker.func_new("env", "abort", [:i32, :i32, :i32, :i32], []) do |_caller, messagePtr, filenamePtr, lineNum, colNum|
@@ -91,6 +94,13 @@ module DevCycle
91
94
  @sdkkey = sdkkey
92
95
  @options = options
93
96
  @logger = options.logger
97
+ @wasm_mutex = Mutex.new
98
+ @variable_type_codes = {
99
+ boolean: @@instance.export("VariableType.Boolean").to_global.get.to_i,
100
+ string: @@instance.export("VariableType.String").to_global.get.to_i,
101
+ number: @@instance.export("VariableType.Number").to_global.get.to_i,
102
+ json: @@instance.export("VariableType.JSON").to_global.get.to_i
103
+ }
94
104
  set_sdk_key_internal(sdkkey)
95
105
  platform_data = PlatformData.new('server', VERSION, RUBY_VERSION, nil, 'Ruby', Socket.gethostname)
96
106
  set_platform_data(platform_data)
@@ -104,123 +114,158 @@ module DevCycle
104
114
 
105
115
  sig { params(user: UserData).returns(BucketedUserConfig) }
106
116
  def generate_bucketed_config(user)
107
- user_addr = malloc_asc_string(user.to_json)
108
- @@stack_tracer = lambda { |message| raise message }
109
- config_addr = @@instance.invoke("generateBucketedConfigForUser", @sdkKeyAddr, user_addr)
110
- bucketed_config_json = read_asc_string(config_addr)
111
- bucketed_config_hash = Oj.load(bucketed_config_json)
112
-
113
- BucketedUserConfig.new(bucketed_config_hash['project'],
114
- bucketed_config_hash['environment'],
115
- bucketed_config_hash['features'],
116
- bucketed_config_hash['featureVariationMap'],
117
- bucketed_config_hash['variableVariationMap'],
118
- bucketed_config_hash['variables'],
119
- bucketed_config_hash['knownVariableKeys'])
117
+ @wasm_mutex.synchronize do
118
+ user_addr = malloc_asc_string(user.to_json)
119
+ @@stack_tracer = @@stack_tracer_raise
120
+ config_addr = @@instance.invoke("generateBucketedConfigForUser", @sdkKeyAddr, user_addr)
121
+ bucketed_config_json = read_asc_string(config_addr)
122
+ bucketed_config_hash = Oj.load(bucketed_config_json)
123
+
124
+ BucketedUserConfig.new(bucketed_config_hash['project'],
125
+ bucketed_config_hash['environment'],
126
+ bucketed_config_hash['features'],
127
+ bucketed_config_hash['featureVariationMap'],
128
+ bucketed_config_hash['variableVariationMap'],
129
+ bucketed_config_hash['variables'],
130
+ bucketed_config_hash['knownVariableKeys'])
131
+ end
132
+ end
133
+
134
+ sig { params(user: UserData, key: String, variable_type: Integer).returns(T.nilable(String)) }
135
+ def variable_for_user(user, key, variable_type)
136
+ @wasm_mutex.synchronize do
137
+ user_addr = malloc_asc_string(user.to_json)
138
+ key_addr = malloc_asc_string(key)
139
+ @@stack_tracer = @@stack_tracer_raise
140
+ var_addr = @@instance.invoke("variableForUser", @sdkKeyAddr, user_addr, key_addr, variable_type, 1)
141
+ read_asc_string(var_addr)
142
+ end
120
143
  end
121
144
 
122
145
  sig { returns(T::Array[EventsPayload]) }
123
146
  def flush_event_queue
124
- @@stack_tracer = lambda { |message| raise message }
125
- payload_addr = @@instance.invoke("flushEventQueue", @sdkKeyAddr)
126
- raw_json = read_asc_string(payload_addr)
127
- raw_payloads = Oj.load(raw_json)
128
-
129
- if raw_payloads == nil
130
- return []
147
+ @wasm_mutex.synchronize do
148
+ @@stack_tracer = @@stack_tracer_raise
149
+ payload_addr = @@instance.invoke("flushEventQueue", @sdkKeyAddr)
150
+ raw_json = read_asc_string(payload_addr)
151
+ raw_payloads = Oj.load(raw_json)
152
+
153
+ if raw_payloads == nil
154
+ return []
155
+ end
156
+ raw_payloads.map { |raw_payload| EventsPayload.new(raw_payload["records"], raw_payload["payloadId"], raw_payload["eventCount"]) }
131
157
  end
132
- raw_payloads.map { |raw_payload| EventsPayload.new(raw_payload["records"], raw_payload["payloadId"], raw_payload["eventCount"]) }
133
158
  end
134
159
 
135
160
  sig { returns(Integer) }
136
161
  def check_event_queue_size
137
- @@stack_tracer = lambda { |message| raise message }
138
- @@instance.invoke("eventQueueSize", @sdkKeyAddr)
162
+ @wasm_mutex.synchronize do
163
+ @@stack_tracer = @@stack_tracer_raise
164
+ @@instance.invoke("eventQueueSize", @sdkKeyAddr)
165
+ end
139
166
  end
140
167
 
141
168
  sig { params(payload_id: String).returns(NilClass) }
142
169
  def on_payload_success(payload_id)
143
- payload_addr = malloc_asc_string(payload_id)
144
- @@stack_tracer = lambda { |message| raise message }
145
- @@instance.invoke("onPayloadSuccess", @sdkKeyAddr, payload_addr)
170
+ @wasm_mutex.synchronize do
171
+ payload_addr = malloc_asc_string(payload_id)
172
+ @@stack_tracer = @@stack_tracer_raise
173
+ @@instance.invoke("onPayloadSuccess", @sdkKeyAddr, payload_addr)
174
+ end
175
+ end
176
+
177
+ sig { params(payload_id: String, retryable: Object).returns(NilClass) }
178
+ def on_payload_failure(payload_id, retryable)
179
+ @wasm_mutex.synchronize do
180
+ payload_addr = malloc_asc_string(payload_id)
181
+ @@stack_tracer = @@stack_tracer_raise
182
+ @@instance.invoke("onPayloadFailure", @sdkKeyAddr, payload_addr, retryable ? 1 : 0)
183
+ end
146
184
  end
147
185
 
148
186
  sig { params(user: UserData, event: Event).returns(NilClass) }
149
187
  def queue_event(user, event)
150
- begin
151
- user_addr = malloc_asc_string(Oj.dump(user))
152
- asc_pin(user_addr)
153
- event_addr = malloc_asc_string(Oj.dump(event))
154
- @@stack_tracer = lambda { |message| raise message }
155
- @@instance.invoke("queueEvent", @sdkKeyAddr, user_addr, event_addr)
156
- ensure
157
- asc_unpin(user_addr)
188
+ @wasm_mutex.synchronize do
189
+ begin
190
+ user_addr = malloc_asc_string(Oj.dump(user))
191
+ asc_pin(user_addr)
192
+ event_addr = malloc_asc_string(Oj.dump(event))
193
+ @@stack_tracer = @@stack_tracer_raise
194
+ @@instance.invoke("queueEvent", @sdkKeyAddr, user_addr, event_addr)
195
+ ensure
196
+ asc_unpin(user_addr)
197
+ end
158
198
  end
159
199
  end
160
200
 
161
201
  sig { params(event: Event, bucketeduser: T.nilable(BucketedUserConfig)).returns(NilClass) }
162
202
  def queue_aggregate_event(event, bucketeduser)
163
- begin
164
- variable_variation_map =
165
- if !bucketeduser.nil?
166
- bucketeduser.variable_variation_map
167
- else
168
- {}
169
- end
170
- varmap_addr = malloc_asc_string(Oj.dump(variable_variation_map))
171
- asc_pin(varmap_addr)
172
- event_addr = malloc_asc_string(Oj.dump(event))
173
- @@stack_tracer = lambda { |message| raise message }
174
- @@instance.invoke("queueAggregateEvent", @sdkKeyAddr, event_addr, varmap_addr)
175
- ensure
176
- asc_unpin(varmap_addr)
203
+ @wasm_mutex.synchronize do
204
+ begin
205
+ variable_variation_map =
206
+ if !bucketeduser.nil?
207
+ bucketeduser.variable_variation_map
208
+ else
209
+ {}
210
+ end
211
+ varmap_addr = malloc_asc_string(Oj.dump(variable_variation_map))
212
+ asc_pin(varmap_addr)
213
+ event_addr = malloc_asc_string(Oj.dump(event))
214
+ @@stack_tracer = @@stack_tracer_raise
215
+ @@instance.invoke("queueAggregateEvent", @sdkKeyAddr, event_addr, varmap_addr)
216
+ ensure
217
+ asc_unpin(varmap_addr)
218
+ end
177
219
  end
178
220
  end
179
221
 
180
- sig { params(payload_id: String, retryable: Object).returns(NilClass) }
181
- def on_payload_failure(payload_id, retryable)
182
- payload_addr = malloc_asc_string(payload_id)
183
- @@stack_tracer = lambda { |message| raise message }
184
- @@instance.invoke("onPayloadFailure", @sdkKeyAddr, payload_addr, retryable ? 1 : 0)
185
- end
186
-
187
222
  sig { params(config: String).returns(NilClass) }
188
223
  def store_config(config)
189
- config_addr = malloc_asc_string(config)
190
- @@stack_tracer = lambda { |message| raise message }
191
- @@instance.invoke("setConfigData", @sdkKeyAddr, config_addr)
224
+ @wasm_mutex.synchronize do
225
+ config_addr = malloc_asc_string(config)
226
+ @@stack_tracer = @@stack_tracer_raise
227
+ @@instance.invoke("setConfigData", @sdkKeyAddr, config_addr)
228
+ end
192
229
  end
193
230
 
194
231
  sig { params(options: EventQueueOptions).returns(NilClass) }
195
232
  def init_event_queue(options)
196
- options_json = Oj.dump(options)
197
- options_addr = malloc_asc_string(options_json)
198
- @@stack_tracer = lambda { |message| raise message }
199
- @@instance.invoke("initEventQueue", @sdkKeyAddr, options_addr)
233
+ @wasm_mutex.synchronize do
234
+ options_json = Oj.dump(options)
235
+ options_addr = malloc_asc_string(options_json)
236
+ @@stack_tracer = @@stack_tracer_raise
237
+ @@instance.invoke("initEventQueue", @sdkKeyAddr, options_addr)
238
+ end
200
239
  end
201
240
 
202
241
  sig { params(customdata: Hash).returns(NilClass) }
203
242
  def set_client_custom_data(customdata)
204
- customdata_json = Oj.dump(customdata)
205
- customdata_addr = malloc_asc_string(customdata_json)
206
- @@stack_tracer = lambda { |message| raise message }
207
- @@instance.invoke("setClientCustomData", customdata_addr)
243
+ @wasm_mutex.synchronize do
244
+ customdata_json = Oj.dump(customdata)
245
+ customdata_addr = malloc_asc_string(customdata_json)
246
+ @@stack_tracer = @@stack_tracer_raise
247
+ @@instance.invoke("setClientCustomData", @sdkKeyAddr, customdata_addr)
248
+ end
208
249
  end
209
250
 
210
251
  private
211
252
 
212
253
  sig { params(platformdata: PlatformData).returns(NilClass) }
213
254
  def set_platform_data(platformdata)
214
- platformdata_json = Oj.dump(platformdata)
215
- platformdata_addr = malloc_asc_string(platformdata_json)
216
- @@stack_tracer = lambda { |message| raise message }
217
- @@instance.invoke("setPlatformData", platformdata_addr)
255
+ @wasm_mutex.synchronize do
256
+ platformdata_json = Oj.dump(platformdata)
257
+ platformdata_addr = malloc_asc_string(platformdata_json)
258
+ @@stack_tracer = @@stack_tracer_raise
259
+ @@instance.invoke("setPlatformData", platformdata_addr)
260
+ end
218
261
  end
219
262
 
220
263
  def set_sdk_key_internal(sdkKey)
221
- addr = malloc_asc_string(sdkKey)
222
- @sdkKeyAddr = addr
223
- asc_pin(addr)
264
+ @wasm_mutex.synchronize do
265
+ addr = malloc_asc_string(sdkKey)
266
+ @sdkKeyAddr = addr
267
+ asc_pin(addr)
268
+ end
224
269
  end
225
270
 
226
271
  def asc_pin(addr)
@@ -236,7 +281,7 @@ module DevCycle
236
281
  sig { params(string: String).returns(Integer) }
237
282
  def malloc_asc_string(string)
238
283
  wasm_object_id = 1
239
- @@stack_tracer = lambda { |message| raise message }
284
+ @@stack_tracer = @@stack_tracer_raise
240
285
  wasm_new = @@instance.export("__new").to_func
241
286
  utf8_bytes = string.bytes
242
287
  byte_len = utf8_bytes.length
@@ -244,8 +289,8 @@ module DevCycle
244
289
  start_addr = wasm_new.call(byte_len * 2, wasm_object_id)
245
290
  i = 0
246
291
  while i < byte_len
247
- @@stack_tracer = lambda { |message| raise message }
248
- @@memory.write(start_addr + (i * 2), [utf8_bytes[i]].pack('U'))
292
+ @@stack_tracer = @@stack_tracer_raise
293
+ @@memory.write(start_addr + (i * 2), [utf8_bytes[i]].pack('c'))
249
294
  i += 1
250
295
  end
251
296
  start_addr
@@ -253,23 +298,23 @@ module DevCycle
253
298
 
254
299
  # @param [Integer] address start address of string.
255
300
  # @return [String] resulting string
256
- sig { params(address: Integer).returns(String) }
301
+ sig { params(address: Integer).returns(T.nilable(String)) }
257
302
  def read_asc_string(address)
258
- @@stack_tracer = lambda { |message| raise message }
303
+ if address == 0
304
+ @logger.debug("null address passed to read_asc_string")
305
+ return nil
306
+ end
307
+
308
+ @@stack_tracer = @@stack_tracer_raise
259
309
  raw_bytes = @@memory.read(address - 4, 4).bytes.reverse
260
310
  len = 0
261
311
  raw_bytes.each { |j|
262
312
  len = (len << 8) + (j & 0xFF)
263
313
  }
264
- result = ""
265
- i = 0
266
- while i < len
267
- @@stack_tracer = lambda { |message| raise message }
268
- result += @@memory.read(address + i, 1)
269
- i += 2
270
- end
271
- result
314
+
315
+ @@stack_tracer = @@stack_tracer_raise
316
+ result = @@memory.read(address, len).bytes
317
+ result.select.with_index { |_, i| i.even? }.pack('c*')
272
318
  end
273
319
  end
274
320
  end
275
-
@@ -11,5 +11,5 @@ OpenAPI Generator version: 5.3.0
11
11
  =end
12
12
 
13
13
  module DevCycle
14
- VERSION = '2.0.3'
14
+ VERSION = '2.1.0'
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.3
4
+ version: 2.1.0
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-08 00:00:00.000000000 Z
11
+ date: 2023-03-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: typhoeus