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 +4 -4
- data/Gemfile +1 -0
- data/LICENSE +1 -1
- data/devcycle-ruby-server-sdk.gemspec +2 -2
- data/lib/devcycle-ruby-server-sdk/api/client.rb +33 -4
- data/lib/devcycle-ruby-server-sdk/api/dev_cycle_provider.rb +10 -1
- data/lib/devcycle-ruby-server-sdk/eval_reasons.rb +16 -0
- data/lib/devcycle-ruby-server-sdk/localbucketing/bucketing-lib.release.wasm +0 -0
- data/lib/devcycle-ruby-server-sdk/localbucketing/local_bucketing.rb +8 -8
- data/lib/devcycle-ruby-server-sdk/localbucketing/proto/variableForUserParams.proto +8 -0
- data/lib/devcycle-ruby-server-sdk/localbucketing/proto/variableForUserParams_pb.rb +25 -59
- data/lib/devcycle-ruby-server-sdk/localbucketing/update_wasm.sh +2 -2
- data/lib/devcycle-ruby-server-sdk/models/variable.rb +11 -2
- data/lib/devcycle-ruby-server-sdk/version.rb +1 -1
- data/lib/devcycle-ruby-server-sdk.rb +1 -0
- data/spec/api/devcycle_api_spec.rb +94 -9
- data/spec/spec_helper.rb +4 -0
- metadata +6 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5b3044307e6ee4e30c1a53aeac3d62eceaaac8ea86fc380b1a41bbf15294ce98
|
|
4
|
+
data.tar.gz: 3fdb48a7217e4390bfe4f53f2e438324803cd9bc8b32a2f91c31a7e7783a32bf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: af96f97ff9a13800b415f59777446ea075535e24959939e737b845599201c6b1d8ddce1d170b099837ee2d2b91dee7039b8354298f009df0e2e0eeb0d731c071
|
|
7
|
+
data.tar.gz: 021bfd22df2c904f03e4e089387a8914b078e9b70d237f12551cfd036665120cba7df2ba3f09b1df62177b6fd9e1b576f7e75c23415880da4c54c8705445eee8
|
data/Gemfile
CHANGED
data/LICENSE
CHANGED
|
@@ -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', '
|
|
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.
|
|
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
|
|
Binary file
|
|
@@ -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
|
-
@@
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
@@
|
|
37
|
-
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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.
|
|
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?
|
|
@@ -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
|
-
|
|
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.
|
|
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:
|
|
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:
|
|
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.
|
|
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.
|
|
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
|