devcycle-ruby-server-sdk 3.6.2 → 3.8.0

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: 914b4bf9e916b2796dcd6ce88be3ecf784b074fd30784bf8f28e2156b0bd8c66
4
- data.tar.gz: dc4a335584b6618b4664203460530bc4421232dfdcc6362399694d7278f34bee
3
+ metadata.gz: 5b3044307e6ee4e30c1a53aeac3d62eceaaac8ea86fc380b1a41bbf15294ce98
4
+ data.tar.gz: 3fdb48a7217e4390bfe4f53f2e438324803cd9bc8b32a2f91c31a7e7783a32bf
5
5
  SHA512:
6
- metadata.gz: 6ca7f4b518dfa6bbd6ffa817263df4f625e7583a2ea61ad6b6ead5cd10ce12f2f586e205e457c39dc9d86831aadfd8e1ce7f1346c02362b1427bce7768a9fac0
7
- data.tar.gz: 2d35f58deaa7a1309d8cb1aa6c5e7002e96e957f920d64d49d14a11e03a108051c679fc521acb451c1cd90a5dc32e9caa1754c92f308efecf592cd87da8cb78b
6
+ metadata.gz: af96f97ff9a13800b415f59777446ea075535e24959939e737b845599201c6b1d8ddce1d170b099837ee2d2b91dee7039b8354298f009df0e2e0eeb0d731c071
7
+ data.tar.gz: 021bfd22df2c904f03e4e089387a8914b078e9b70d237f12551cfd036665120cba7df2ba3f09b1df62177b6fd9e1b576f7e75c23415880da4c54c8705445eee8
data/Gemfile CHANGED
@@ -15,4 +15,5 @@ group :development, :test do
15
15
  gem 'rake', '~> 13.0.1'
16
16
  gem 'pry-byebug'
17
17
  gem 'rubocop', '~> 1.57.1'
18
+ gem 'webmock'
18
19
  end
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2021 Taplytics Inc.
3
+ Copyright (c) 2025 Taplytics Inc.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -24,13 +24,13 @@ Gem::Specification.new do |s|
24
24
  s.required_ruby_version = ">= 3.2"
25
25
 
26
26
  s.add_runtime_dependency 'typhoeus', '~> 1.0', '>= 1.0.1'
27
- s.add_runtime_dependency 'wasmtime', '20.0.2'
27
+ s.add_runtime_dependency 'wasmtime', '39.0.1'
28
28
  s.add_runtime_dependency 'concurrent-ruby', '~> 1.2.0'
29
29
  s.add_runtime_dependency 'sorbet-runtime', '>= 0.5.11481'
30
30
  s.add_runtime_dependency 'oj', '~> 3.0'
31
31
  s.add_runtime_dependency 'google-protobuf', '~> 3.22'
32
32
  s.add_runtime_dependency 'ld-eventsource', '~> 2.2.3'
33
- s.add_runtime_dependency 'openfeature-sdk', '~> 0.4.0'
33
+ s.add_runtime_dependency 'openfeature-sdk', '~> 0.4.1'
34
34
 
35
35
  s.add_development_dependency 'rspec', '~> 3.6', '>= 3.6.0'
36
36
 
@@ -195,6 +195,7 @@ module DevCycle
195
195
  value = default
196
196
  type = determine_variable_type(default)
197
197
  defaulted = true
198
+ eval = { reason: DevCycle::EVAL_REASONS::DEFAULT, details: DevCycle::DEFAULT_REASON_DETAILS::USER_NOT_TARGETED }
198
199
  if local_bucketing_initialized? && @local_bucketing.has_config
199
200
  type_code = variable_type_code_from_type(type)
200
201
  variable_pb = variable_for_user_pb(user, key, type_code)
@@ -202,9 +203,11 @@ module DevCycle
202
203
  value = get_variable_value(variable_pb)
203
204
  defaulted = false
204
205
  end
206
+ eval = get_eval_reason(variable_pb)
205
207
  else
208
+ eval = { reason: DevCycle::EVAL_REASONS::DEFAULT, details: DevCycle::DEFAULT_REASON_DETAILS::MISSING_CONFIG }
206
209
  @logger.warn("Local bucketing not initialized, returning default value for variable #{key}")
207
- variable_event = Event.new({ type: DevCycle::EventTypes[:agg_variable_defaulted], target: key })
210
+ variable_event = Event.new({ type: DevCycle::EventTypes[:agg_variable_defaulted], target: key, metaData: { evalReason: DevCycle::EVAL_REASONS::DEFAULT }})
208
211
  bucketed_config = BucketedUserConfig.new({}, {}, {}, {}, {}, {}, [])
209
212
  @event_queue.queue_aggregate_event(variable_event, bucketed_config)
210
213
  end
@@ -214,7 +217,8 @@ module DevCycle
214
217
  value: value,
215
218
  type: type,
216
219
  defaultValue: default,
217
- isDefaulted: defaulted
220
+ isDefaulted: defaulted,
221
+ eval: eval
218
222
  })
219
223
  end
220
224
 
@@ -321,14 +325,27 @@ module DevCycle
321
325
  if @api_client.config.debugging
322
326
  @api_client.config.logger.debug "API called: DevCycle::Client#variable\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
323
327
  end
328
+ if default && data.type
329
+ api_type_to_ruby_class = {
330
+ 'Boolean' => [TrueClass, FalseClass],
331
+ 'Number' => [Integer, Float],
332
+ 'String' => [String],
333
+ 'JSON' => [Hash]
334
+ }
335
+ ruby_classes = api_type_to_ruby_class[data.type.to_s]
336
+ unless ruby_classes && ruby_classes.any? { |klass| default.is_a?(klass) }
337
+ eval = { reason: DevCycle::EVAL_REASONS::DEFAULT, details: DevCycle::DEFAULT_REASON_DETAILS::TYPE_MISMATCH }
338
+ return Variable.new(key: key, value: default, isDefaulted: true, eval: eval)
339
+ end
340
+ end
324
341
  return data
325
342
  rescue ApiError => error
326
343
  if error.code != 404
327
344
  @api_client.config.logger.error("Failed to retrieve variable value: #{error.message}")
328
345
  end
329
346
 
330
- return Variable.new(key: key, value: default, isDefaulted: true)
331
- end
347
+ return Variable.new(key: key, value: default, isDefaulted: true, eval: { reason: DevCycle::EVAL_REASONS::DEFAULT, details: DevCycle::DEFAULT_REASON_DETAILS::ERROR })
348
+ end
332
349
  end
333
350
 
334
351
  # Get all variables by key for user data
@@ -574,6 +591,18 @@ module DevCycle
574
591
  end
575
592
  end
576
593
 
594
+ def get_eval_reason(variable_pb)
595
+ if variable_pb.nil?
596
+ { reason: DevCycle::EVAL_REASONS::DEFAULT, details: DevCycle::DEFAULT_REASON_DETAILS::USER_NOT_TARGETED}
597
+ else
598
+ if variable_pb.eval.nil?
599
+ { reason: DevCycle::EVAL_REASONS::DEFAULT, details: DevCycle::DEFAULT_REASON_DETAILS::USER_NOT_TARGETED }
600
+ else
601
+ { reason: variable_pb.eval.reason, details: variable_pb.eval.details, target_id: variable_pb.eval.target_id }
602
+ end
603
+ end
604
+ end
605
+
577
606
  # Adds an eval hook to the client
578
607
  # @param [EvalHook] eval_hook The eval hook to add
579
608
  # @return [void]
@@ -32,7 +32,16 @@ module DevCycle
32
32
  end
33
33
 
34
34
  def fetch_integer_value(flag_key:, default_value:, evaluation_context: nil)
35
- @client.variable(Provider.user_from_openfeature_context(evaluation_context), flag_key, default_value)
35
+ variable = @client.variable(Provider.user_from_openfeature_context(evaluation_context), flag_key, default_value)
36
+
37
+ Variable.new(
38
+ key: variable.key,
39
+ type: variable.type,
40
+ value: variable.value.to_i,
41
+ defaultValue: variable.defaultValue,
42
+ isDefaulted: variable.isDefaulted,
43
+ eval: variable.eval
44
+ )
36
45
  end
37
46
 
38
47
  def fetch_float_value(flag_key:, default_value:, evaluation_context: nil)
@@ -0,0 +1,16 @@
1
+ module DevCycle
2
+ # Evaluation reasons for successful evaluations
3
+ module EVAL_REASONS
4
+ DEFAULT = 'DEFAULT'
5
+ ERROR = 'ERROR'
6
+ end
7
+
8
+ # Default reason details
9
+ module DEFAULT_REASON_DETAILS
10
+ MISSING_CONFIG = 'Missing Config'
11
+ USER_NOT_TARGETED = 'User Not Targeted'
12
+ TYPE_MISMATCH = 'Variable Type Mismatch'
13
+ MISSING_VARIABLE = 'Missing Variable'
14
+ ERROR = 'Error'
15
+ end
16
+ end
@@ -27,14 +27,14 @@ module DevCycle
27
27
  # added to ensure that there is no processes deadlock when compiling wasm before forking
28
28
 
29
29
  @@wasmmodule = Wasmtime::Module.from_file(@@engine, "#{__dir__}/bucketing-lib.release.wasm")
30
- @@wasi_ctx = Wasmtime::WasiCtxBuilder.new
31
- .inherit_stdout
32
- .inherit_stderr
33
- .set_argv(ARGV)
34
- .set_env(ENV)
35
- .build
36
- @@store = Wasmtime::Store.new(@@engine, wasi_ctx: @@wasi_ctx)
37
- @@linker = Wasmtime::Linker.new(@@engine, wasi: true)
30
+ @@wasi_config = Wasmtime::WasiConfig.new
31
+ .inherit_stdout
32
+ .inherit_stderr
33
+ .set_argv(ARGV)
34
+ .set_env(ENV)
35
+ @@store = Wasmtime::Store.new(@@engine, wasi_p1_config: @@wasi_config)
36
+ @@linker = Wasmtime::Linker.new(@@engine)
37
+ Wasmtime::WASI::P1.add_to_linker_sync(@@linker)
38
38
 
39
39
  @@linker.func_new("env", "Date.now", [], [:f64]) do |_caller|
40
40
  DateTime.now.strftime("%Q").to_i
@@ -66,4 +66,12 @@ message SDKVariable_PB {
66
66
  double doubleValue = 5;
67
67
  string stringValue = 6;
68
68
  NullableString evalReason = 7;
69
+ NullableString _feature = 8;
70
+ EvalReason_PB eval = 9;
69
71
  }
72
+
73
+ message EvalReason_PB {
74
+ string reason = 1;
75
+ string details = 2;
76
+ string target_id = 3;
77
+ }
@@ -1,69 +1,34 @@
1
+ # frozen_string_literal: true
1
2
  # Generated by the protocol buffer compiler. DO NOT EDIT!
2
3
  # source: variableForUserParams.proto
3
4
 
4
5
  require 'google/protobuf'
5
6
 
6
- Google::Protobuf::DescriptorPool.generated_pool.build do
7
- add_file("variableForUserParams.proto", :syntax => :proto3) do
8
- add_message "proto.NullableString" do
9
- optional :value, :string, 1
10
- optional :isNull, :bool, 2
11
- end
12
- add_message "proto.NullableDouble" do
13
- optional :value, :double, 1
14
- optional :isNull, :bool, 2
15
- end
16
- add_message "proto.CustomDataValue" do
17
- optional :type, :enum, 1, "proto.CustomDataType"
18
- optional :boolValue, :bool, 2
19
- optional :doubleValue, :double, 3
20
- optional :stringValue, :string, 4
21
- end
22
- add_message "proto.NullableCustomData" do
23
- map :value, :string, :message, 1, "proto.CustomDataValue"
24
- optional :isNull, :bool, 2
25
- end
26
- add_message "proto.VariableForUserParams_PB" do
27
- optional :sdkKey, :string, 1
28
- optional :variableKey, :string, 2
29
- optional :variableType, :enum, 3, "proto.VariableType_PB"
30
- optional :user, :message, 4, "proto.DVCUser_PB"
31
- optional :shouldTrackEvent, :bool, 5
32
- end
33
- add_message "proto.DVCUser_PB" do
34
- optional :user_id, :string, 1
35
- optional :email, :message, 2, "proto.NullableString"
36
- optional :name, :message, 3, "proto.NullableString"
37
- optional :language, :message, 4, "proto.NullableString"
38
- optional :country, :message, 5, "proto.NullableString"
39
- optional :appBuild, :message, 6, "proto.NullableDouble"
40
- optional :appVersion, :message, 7, "proto.NullableString"
41
- optional :deviceModel, :message, 8, "proto.NullableString"
42
- optional :customData, :message, 9, "proto.NullableCustomData"
43
- optional :privateCustomData, :message, 10, "proto.NullableCustomData"
44
- end
45
- add_message "proto.SDKVariable_PB" do
46
- optional :_id, :string, 1
47
- optional :type, :enum, 2, "proto.VariableType_PB"
48
- optional :key, :string, 3
49
- optional :boolValue, :bool, 4
50
- optional :doubleValue, :double, 5
51
- optional :stringValue, :string, 6
52
- optional :evalReason, :message, 7, "proto.NullableString"
53
- end
54
- add_enum "proto.VariableType_PB" do
55
- value :Boolean, 0
56
- value :Number, 1
57
- value :String, 2
58
- value :JSON, 3
59
- end
60
- add_enum "proto.CustomDataType" do
61
- value :Bool, 0
62
- value :Num, 1
63
- value :Str, 2
64
- value :Null, 3
7
+
8
+ descriptor_data = "\n\x1bvariableForUserParams.proto\x12\x05proto\"/\n\x0eNullableString\x12\r\n\x05value\x18\x01 \x01(\t\x12\x0e\n\x06isNull\x18\x02 \x01(\x08\"/\n\x0eNullableDouble\x12\r\n\x05value\x18\x01 \x01(\x01\x12\x0e\n\x06isNull\x18\x02 \x01(\x08\"s\n\x0f\x43ustomDataValue\x12#\n\x04type\x18\x01 \x01(\x0e\x32\x15.proto.CustomDataType\x12\x11\n\tboolValue\x18\x02 \x01(\x08\x12\x13\n\x0b\x64oubleValue\x18\x03 \x01(\x01\x12\x13\n\x0bstringValue\x18\x04 \x01(\t\"\x9f\x01\n\x12NullableCustomData\x12\x33\n\x05value\x18\x01 \x03(\x0b\x32$.proto.NullableCustomData.ValueEntry\x12\x0e\n\x06isNull\x18\x02 \x01(\x08\x1a\x44\n\nValueEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12%\n\x05value\x18\x02 \x01(\x0b\x32\x16.proto.CustomDataValue:\x02\x38\x01\"\xa8\x01\n\x18VariableForUserParams_PB\x12\x0e\n\x06sdkKey\x18\x01 \x01(\t\x12\x13\n\x0bvariableKey\x18\x02 \x01(\t\x12,\n\x0cvariableType\x18\x03 \x01(\x0e\x32\x16.proto.VariableType_PB\x12\x1f\n\x04user\x18\x04 \x01(\x0b\x32\x11.proto.DVCUser_PB\x12\x18\n\x10shouldTrackEvent\x18\x05 \x01(\x08\"\x9e\x03\n\nDVCUser_PB\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12$\n\x05\x65mail\x18\x02 \x01(\x0b\x32\x15.proto.NullableString\x12#\n\x04name\x18\x03 \x01(\x0b\x32\x15.proto.NullableString\x12\'\n\x08language\x18\x04 \x01(\x0b\x32\x15.proto.NullableString\x12&\n\x07\x63ountry\x18\x05 \x01(\x0b\x32\x15.proto.NullableString\x12\'\n\x08\x61ppBuild\x18\x06 \x01(\x0b\x32\x15.proto.NullableDouble\x12)\n\nappVersion\x18\x07 \x01(\x0b\x32\x15.proto.NullableString\x12*\n\x0b\x64\x65viceModel\x18\x08 \x01(\x0b\x32\x15.proto.NullableString\x12-\n\ncustomData\x18\t \x01(\x0b\x32\x19.proto.NullableCustomData\x12\x34\n\x11privateCustomData\x18\n \x01(\x0b\x32\x19.proto.NullableCustomData\"\x85\x02\n\x0eSDKVariable_PB\x12\x0b\n\x03_id\x18\x01 \x01(\t\x12$\n\x04type\x18\x02 \x01(\x0e\x32\x16.proto.VariableType_PB\x12\x0b\n\x03key\x18\x03 \x01(\t\x12\x11\n\tboolValue\x18\x04 \x01(\x08\x12\x13\n\x0b\x64oubleValue\x18\x05 \x01(\x01\x12\x13\n\x0bstringValue\x18\x06 \x01(\t\x12)\n\nevalReason\x18\x07 \x01(\x0b\x32\x15.proto.NullableString\x12\'\n\x08_feature\x18\x08 \x01(\x0b\x32\x15.proto.NullableString\x12\"\n\x04\x65val\x18\t \x01(\x0b\x32\x14.proto.EvalReason_PB\"C\n\rEvalReason_PB\x12\x0e\n\x06reason\x18\x01 \x01(\t\x12\x0f\n\x07\x64\x65tails\x18\x02 \x01(\t\x12\x11\n\ttarget_id\x18\x03 \x01(\t*@\n\x0fVariableType_PB\x12\x0b\n\x07\x42oolean\x10\x00\x12\n\n\x06Number\x10\x01\x12\n\n\x06String\x10\x02\x12\x08\n\x04JSON\x10\x03*6\n\x0e\x43ustomDataType\x12\x08\n\x04\x42ool\x10\x00\x12\x07\n\x03Num\x10\x01\x12\x07\n\x03Str\x10\x02\x12\x08\n\x04Null\x10\x03\x62\x06proto3"
9
+
10
+ pool = Google::Protobuf::DescriptorPool.generated_pool
11
+
12
+ begin
13
+ pool.add_serialized_file(descriptor_data)
14
+ rescue TypeError => e
15
+ # Compatibility code: will be removed in the next major version.
16
+ require 'google/protobuf/descriptor_pb'
17
+ parsed = Google::Protobuf::FileDescriptorProto.decode(descriptor_data)
18
+ parsed.clear_dependency
19
+ serialized = parsed.class.encode(parsed)
20
+ file = pool.add_serialized_file(serialized)
21
+ warn "Warning: Protobuf detected an import path issue while loading generated file #{__FILE__}"
22
+ imports = [
23
+ ]
24
+ imports.each do |type_name, expected_filename|
25
+ import_file = pool.lookup(type_name).file_descriptor
26
+ if import_file.name != expected_filename
27
+ warn "- #{file.name} imports #{expected_filename}, but that import was loaded as #{import_file.name}"
65
28
  end
66
29
  end
30
+ warn "Each proto file must use a consistent fully-qualified name."
31
+ warn "This will become an error in the next major version."
67
32
  end
68
33
 
69
34
  module Proto
@@ -74,6 +39,7 @@ module Proto
74
39
  VariableForUserParams_PB = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("proto.VariableForUserParams_PB").msgclass
75
40
  DVCUser_PB = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("proto.DVCUser_PB").msgclass
76
41
  SDKVariable_PB = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("proto.SDKVariable_PB").msgclass
42
+ EvalReason_PB = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("proto.EvalReason_PB").msgclass
77
43
  VariableType_PB = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("proto.VariableType_PB").enummodule
78
44
  CustomDataType = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("proto.CustomDataType").enummodule
79
45
  end
@@ -1,5 +1,5 @@
1
1
  #!/bin/bash
2
- BUCKETING_LIB_VERSION="1.35.1"
2
+ BUCKETING_LIB_VERSION="1.41.0"
3
3
  WAT_DOWNLOAD=0
4
4
  rm bucketing-lib.release.wasm
5
- wget "https://unpkg.com/@devcycle/bucketing-assembly-script@$BUCKETING_LIB_VERSION/build/bucketing-lib.release.wasm"
5
+ wget "https://unpkg.com/@devcycle/bucketing-assembly-script@$BUCKETING_LIB_VERSION/build/bucketing-lib.release.wasm"
@@ -29,6 +29,9 @@ module DevCycle
29
29
 
30
30
  attr_accessor :defaultValue
31
31
 
32
+ # Variable evaluation details
33
+ attr_accessor :eval
34
+
32
35
  class EnumAttributeValidator
33
36
  attr_reader :datatype
34
37
  attr_reader :allowable_values
@@ -58,7 +61,8 @@ module DevCycle
58
61
  :'type' => :'type',
59
62
  :'value' => :'value',
60
63
  :'defaultValue' => :'defaultValue',
61
- :'isDefaulted' => :'isDefaulted'
64
+ :'isDefaulted' => :'isDefaulted',
65
+ :'eval' => :'eval'
62
66
  }
63
67
  end
64
68
 
@@ -74,7 +78,8 @@ module DevCycle
74
78
  :'type' => :'String',
75
79
  :'value' => :'Object',
76
80
  :'defaultValue' => :'Object',
77
- :'isDefaulted' => :'Boolean'
81
+ :'isDefaulted' => :'Boolean',
82
+ :'eval' => :'Object'
78
83
  }
79
84
  end
80
85
 
@@ -120,6 +125,10 @@ module DevCycle
120
125
  if attributes.key?(:'defaultValue')
121
126
  self.defaultValue = attributes[:'defaultValue']
122
127
  end
128
+
129
+ if attributes.key?(:'eval')
130
+ self.eval = attributes[:'eval']
131
+ end
123
132
  end
124
133
 
125
134
  # Show invalid properties with the reasons. Usually used together with valid?
@@ -1,3 +1,3 @@
1
1
  module DevCycle
2
- VERSION = '3.6.2'
2
+ VERSION = '3.8.0'
3
3
  end
@@ -29,6 +29,7 @@ require 'devcycle-ruby-server-sdk/models/variable'
29
29
  require 'devcycle-ruby-server-sdk/eval_hooks_runner'
30
30
  require 'devcycle-ruby-server-sdk/models/eval_hook'
31
31
  require 'devcycle-ruby-server-sdk/models/eval_hook_context'
32
+ require 'devcycle-ruby-server-sdk/eval_reasons'
32
33
 
33
34
  # APIs
34
35
  require 'devcycle-ruby-server-sdk/api/client'
@@ -12,6 +12,9 @@ OpenAPI Generator version: 5.3.0
12
12
 
13
13
  require 'spec_helper'
14
14
  require 'json'
15
+ require 'webmock'
16
+
17
+ include WebMock::API
15
18
 
16
19
  # Unit tests for DevCycle::Client
17
20
  # Automatically generated by openapi-generator (https://openapi-generator.tech)
@@ -21,16 +24,18 @@ describe 'DevCycle::Client' do
21
24
  sdk_key = ENV["DEVCYCLE_SERVER_SDK_KEY"]
22
25
  if sdk_key.nil?
23
26
  puts("SDK KEY NOT SET - SKIPPING INIT")
24
- return
27
+ @skip_tests = true
28
+ else
29
+ # run before each test
30
+ options = DevCycle::Options.new(enable_cloud_bucketing: true)
31
+ @api_instance = DevCycle::Client.new(sdk_key, options)
32
+
33
+ @user = DevCycle::User.new({
34
+ user_id: 'test-user',
35
+ appVersion: '1.2.3'
36
+ })
37
+ @skip_tests = false
25
38
  end
26
- # run before each test
27
- options = DevCycle::Options.new(enable_cloud_bucketing: true)
28
- @api_instance = DevCycle::Client.new(sdk_key, options)
29
-
30
- @user = DevCycle::User.new({
31
- user_id: 'test-user',
32
- appVersion: '1.2.3'
33
- })
34
39
  end
35
40
 
36
41
  after do
@@ -66,6 +71,8 @@ describe 'DevCycle::Client' do
66
71
  it 'should work' do
67
72
  result = @api_instance.variable(@user, "ruby-example-tests-default", false)
68
73
  expect(result.isDefaulted).to eq true
74
+ expect(result.eval[:reason]).to eq "DEFAULT"
75
+ expect(result.eval[:details]).to eq "Error"
69
76
 
70
77
  result = @api_instance.variable_value(@user, "ruby-example-tests-default", true)
71
78
  expect(result).to eq true
@@ -86,6 +93,51 @@ describe 'DevCycle::Client' do
86
93
 
87
94
  result = @api_instance.variable_value(@user, "test", true)
88
95
  expect(result).to eq true
96
+
97
+ result = @api_instance.variable(@user, "test-number-variable", 0)
98
+ expect(result.isDefaulted).to eq false
99
+ expect(result.value).to eq 123
100
+
101
+ result = @api_instance.variable(@user, "test-number-variable", 0)
102
+ expect(result.isDefaulted).to eq false
103
+ expect(result.value).to eq 123
104
+
105
+ result = @api_instance.variable(@user, "test-float-variable", 0.0)
106
+ expect(result.isDefaulted).to eq false
107
+ expect(result.value).to eq 4.56
108
+
109
+ result = @api_instance.variable(@user, "test-string-variable", "")
110
+ expect(result.isDefaulted).to eq false
111
+ expect(result.value).to eq "on"
112
+
113
+ result = @api_instance.variable(@user, "test-json-variable", {})
114
+ expect(result.isDefaulted).to eq false
115
+ expect(result.value).to eq({:message => "a"})
116
+
117
+ result = @api_instance.variable(@user, "test", "false")
118
+ expect(result.isDefaulted).to eq true
119
+ expect(result.eval[:reason]).to eq "DEFAULT"
120
+ expect(result.eval[:details]).to eq "Variable Type Mismatch"
121
+
122
+ result = @api_instance.variable(@user, "test-number-variable", "123")
123
+ expect(result.isDefaulted).to eq true
124
+ expect(result.eval[:reason]).to eq "DEFAULT"
125
+ expect(result.eval[:details]).to eq "Variable Type Mismatch"
126
+
127
+ result = @api_instance.variable(@user, "test-number-variable", true)
128
+ expect(result.isDefaulted).to eq true
129
+ expect(result.eval[:reason]).to eq "DEFAULT"
130
+ expect(result.eval[:details]).to eq "Variable Type Mismatch"
131
+
132
+ result = @api_instance.variable(@user, "test-string-variable", true)
133
+ expect(result.isDefaulted).to eq true
134
+ expect(result.eval[:reason]).to eq "DEFAULT"
135
+ expect(result.eval[:details]).to eq "Variable Type Mismatch"
136
+
137
+ result = @api_instance.variable(@user, "test-json-variable", "on")
138
+ expect(result.isDefaulted).to eq true
139
+ expect(result.eval[:reason]).to eq "DEFAULT"
140
+ expect(result.eval[:details]).to eq "Variable Type Mismatch"
89
141
  end
90
142
  end
91
143
 
@@ -102,4 +154,37 @@ describe 'DevCycle::Client' do
102
154
  end
103
155
  end
104
156
 
157
+ describe 'get_variable_by_key test' do
158
+ before do
159
+ WebMock.disable_net_connect!(allow_localhost: true)
160
+ end
161
+
162
+ after do
163
+ WebMock.allow_net_connect!
164
+ end
165
+
166
+ it 'should work with mocked response' do
167
+ stub_request(:post, "https://bucketing-api.devcycle.com/v1/variables/mocked_variable").
168
+ to_return(status: 200, body: "{\"isDefaulted\": false, \"value\": true, \"eval\": {\"reason\": \"SPLIT\", \"details\": \"Random Distribution | All Users\", \"target_id\": \"621642332ea68943c8833c4d\"}}", headers: {})
169
+
170
+ result = @api_instance.variable(@user, "mocked_variable", false)
171
+ expect(result.isDefaulted).to eq false
172
+ expect(result.value).to eq true
173
+ # Use Hash syntax since eval is deserialized as a Hash
174
+ expect(result.eval[:reason]).to eq "SPLIT"
175
+ expect(result.eval[:details]).to eq "Random Distribution | All Users"
176
+ expect(result.eval[:target_id]).to eq "621642332ea68943c8833c4d"
177
+ end
178
+
179
+ it 'should return error details' do
180
+ stub_request(:post, "https://bucketing-api.devcycle.com/v1/variables/test").
181
+ to_return(status: 500, body: "{\"isDefaulted\": true, \"value\": false, \"eval\": {\"reason\": \"DEFAULT\", \"details\": \"Error\"}}", headers: {})
182
+
183
+ result = @api_instance.variable(@user, "test", false)
184
+ expect(result.isDefaulted).to eq true
185
+ expect(result.eval[:reason]).to eq "DEFAULT"
186
+ expect(result.eval[:details]).to eq "Error"
187
+ end
188
+ end
189
+
105
190
  end
data/spec/spec_helper.rb CHANGED
@@ -12,6 +12,10 @@ OpenAPI Generator version: 5.3.0
12
12
 
13
13
  # load the gem
14
14
  require 'devcycle-ruby-server-sdk'
15
+ require 'webmock/rspec'
16
+
17
+ # Configure WebMock to allow real HTTP requests by default, but enable it for specific tests
18
+ WebMock.allow_net_connect!
15
19
 
16
20
  # The following was generated by the `rspec --init` command. Conventionally, all
17
21
  # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devcycle-ruby-server-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.6.2
4
+ version: 3.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - DevCycleHQ
@@ -35,14 +35,14 @@ dependencies:
35
35
  requirements:
36
36
  - - '='
37
37
  - !ruby/object:Gem::Version
38
- version: 20.0.2
38
+ version: 39.0.1
39
39
  type: :runtime
40
40
  prerelease: false
41
41
  version_requirements: !ruby/object:Gem::Requirement
42
42
  requirements:
43
43
  - - '='
44
44
  - !ruby/object:Gem::Version
45
- version: 20.0.2
45
+ version: 39.0.1
46
46
  - !ruby/object:Gem::Dependency
47
47
  name: concurrent-ruby
48
48
  requirement: !ruby/object:Gem::Requirement
@@ -119,14 +119,14 @@ dependencies:
119
119
  requirements:
120
120
  - - "~>"
121
121
  - !ruby/object:Gem::Version
122
- version: 0.4.0
122
+ version: 0.4.1
123
123
  type: :runtime
124
124
  prerelease: false
125
125
  version_requirements: !ruby/object:Gem::Requirement
126
126
  requirements:
127
127
  - - "~>"
128
128
  - !ruby/object:Gem::Version
129
- version: 0.4.0
129
+ version: 0.4.1
130
130
  - !ruby/object:Gem::Dependency
131
131
  name: rspec
132
132
  requirement: !ruby/object:Gem::Requirement
@@ -166,6 +166,7 @@ files:
166
166
  - lib/devcycle-ruby-server-sdk/api_error.rb
167
167
  - lib/devcycle-ruby-server-sdk/configuration.rb
168
168
  - lib/devcycle-ruby-server-sdk/eval_hooks_runner.rb
169
+ - lib/devcycle-ruby-server-sdk/eval_reasons.rb
169
170
  - lib/devcycle-ruby-server-sdk/localbucketing/bucketed_user_config.rb
170
171
  - lib/devcycle-ruby-server-sdk/localbucketing/bucketing-lib.release.wasm
171
172
  - lib/devcycle-ruby-server-sdk/localbucketing/config_manager.rb