devcycle-ruby-server-sdk 3.7.0 → 3.8.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: c3acf8324fc7606185240c3ea421048f87c8618a3944951ed71ed729ed4cde7a
4
- data.tar.gz: 763e2632030ffbc90bd365614f158bc960c767e15bcf0d88afd2a2407ff56f24
3
+ metadata.gz: 04ba19c1b3a21f3f12f891099851c154cf1b6e994b1ddf733c9d5140e731f2e3
4
+ data.tar.gz: 833df647e1fdb7337dfefbb8969cf3c9458aa119596a2b79a67d84b7a530b0eb
5
5
  SHA512:
6
- metadata.gz: 7830aef854188cded6ed20a3667d4fe6dbf2bcd4482e605b823c95023b7d9fb3f30a52d01543346adae4ed260290f25b71cd516a3172679e4209d54d342eac6b
7
- data.tar.gz: be2f2cb6349a64b95216dd3868642284d917ea4c8db7d2991f550f9beb9e0ddb3f8dae91dc6aa5c817fbf0f2b5f562da1f95fcccee0bddceea67323b96ec783c
6
+ metadata.gz: 5def331e282c250dc916cb89f297f87e2a23d85c951b452aa007365c76e0738a8a7b224cd55264f4109d3fb260a7bdd93c0527fdd45fe228cc3637e7eeb8949f
7
+ data.tar.gz: 307c208e98faa99b3295be532681bf28313cf2e7422652c173c9b43d21dd08e3b6da728ce8dd255ad668e8b2147ffe502cb51256a8c4d71b4020cb26222155ee
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'
28
- s.add_runtime_dependency 'concurrent-ruby', '~> 1.2.0'
27
+ s.add_runtime_dependency 'wasmtime', '39.0.1'
28
+ s.add_runtime_dependency 'concurrent-ruby', '>= 1.2', '< 1.4'
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
 
@@ -325,14 +325,27 @@ module DevCycle
325
325
  if @api_client.config.debugging
326
326
  @api_client.config.logger.debug "API called: DevCycle::Client#variable\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
327
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
328
341
  return data
329
342
  rescue ApiError => error
330
343
  if error.code != 404
331
344
  @api_client.config.logger.error("Failed to retrieve variable value: #{error.message}")
332
345
  end
333
346
 
334
- return Variable.new(key: key, value: default, isDefaulted: true)
335
- 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
336
349
  end
337
350
 
338
351
  # Get all variables by key for user data
@@ -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)
@@ -9,5 +9,8 @@ module DevCycle
9
9
  module DEFAULT_REASON_DETAILS
10
10
  MISSING_CONFIG = 'Missing Config'
11
11
  USER_NOT_TARGETED = 'User Not Targeted'
12
+ TYPE_MISMATCH = 'Variable Type Mismatch'
13
+ MISSING_VARIABLE = 'Missing Variable'
14
+ ERROR = 'Error'
12
15
  end
13
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
@@ -78,7 +78,8 @@ module DevCycle
78
78
  :'type' => :'String',
79
79
  :'value' => :'Object',
80
80
  :'defaultValue' => :'Object',
81
- :'isDefaulted' => :'Boolean'
81
+ :'isDefaulted' => :'Boolean',
82
+ :'eval' => :'Object'
82
83
  }
83
84
  end
84
85
 
@@ -1,3 +1,3 @@
1
1
  module DevCycle
2
- VERSION = '3.7.0'
2
+ VERSION = '3.8.1'
3
3
  end
@@ -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)
@@ -68,6 +71,8 @@ describe 'DevCycle::Client' do
68
71
  it 'should work' do
69
72
  result = @api_instance.variable(@user, "ruby-example-tests-default", false)
70
73
  expect(result.isDefaulted).to eq true
74
+ expect(result.eval[:reason]).to eq "DEFAULT"
75
+ expect(result.eval[:details]).to eq "Error"
71
76
 
72
77
  result = @api_instance.variable_value(@user, "ruby-example-tests-default", true)
73
78
  expect(result).to eq true
@@ -88,6 +93,51 @@ describe 'DevCycle::Client' do
88
93
 
89
94
  result = @api_instance.variable_value(@user, "test", true)
90
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"
91
141
  end
92
142
  end
93
143
 
@@ -104,4 +154,37 @@ describe 'DevCycle::Client' do
104
154
  end
105
155
  end
106
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
+
107
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.7.0
4
+ version: 3.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - DevCycleHQ
@@ -35,28 +35,34 @@ 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
49
49
  requirements:
50
- - - "~>"
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: '1.2'
53
+ - - "<"
51
54
  - !ruby/object:Gem::Version
52
- version: 1.2.0
55
+ version: '1.4'
53
56
  type: :runtime
54
57
  prerelease: false
55
58
  version_requirements: !ruby/object:Gem::Requirement
56
59
  requirements:
57
- - - "~>"
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '1.2'
63
+ - - "<"
58
64
  - !ruby/object:Gem::Version
59
- version: 1.2.0
65
+ version: '1.4'
60
66
  - !ruby/object:Gem::Dependency
61
67
  name: sorbet-runtime
62
68
  requirement: !ruby/object:Gem::Requirement
@@ -119,14 +125,14 @@ dependencies:
119
125
  requirements:
120
126
  - - "~>"
121
127
  - !ruby/object:Gem::Version
122
- version: 0.4.0
128
+ version: 0.4.1
123
129
  type: :runtime
124
130
  prerelease: false
125
131
  version_requirements: !ruby/object:Gem::Requirement
126
132
  requirements:
127
133
  - - "~>"
128
134
  - !ruby/object:Gem::Version
129
- version: 0.4.0
135
+ version: 0.4.1
130
136
  - !ruby/object:Gem::Dependency
131
137
  name: rspec
132
138
  requirement: !ruby/object:Gem::Requirement
@@ -215,7 +221,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
215
221
  - !ruby/object:Gem::Version
216
222
  version: '0'
217
223
  requirements: []
218
- rubygems_version: 3.6.9
224
+ rubygems_version: 4.0.3
219
225
  specification_version: 4
220
226
  summary: DevCycle Bucketing API Ruby Gem
221
227
  test_files: