diff --git a/Gemfile b/Gemfile index b356cad..5bb001b 100644 --- a/Gemfile +++ b/Gemfile @@ -15,4 +15,5 @@ group :development, :test do gem 'rake', '~> 13.0.1' gem 'pry-byebug' gem 'rubocop', '~> 1.57.1' + gem 'webmock' end diff --git a/lib/devcycle-ruby-server-sdk/api/client.rb b/lib/devcycle-ruby-server-sdk/api/client.rb index 5261730..73bf626 100644 --- a/lib/devcycle-ruby-server-sdk/api/client.rb +++ b/lib/devcycle-ruby-server-sdk/api/client.rb @@ -325,14 +325,27 @@ def variable_with_http_info(key, user, default, opts = {}) if @api_client.config.debugging @api_client.config.logger.debug "API called: DevCycle::Client#variable\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" end + if default && data.type + api_type_to_ruby_class = { + 'Boolean' => [TrueClass, FalseClass], + 'Number' => [Integer, Float], + 'String' => [String], + 'JSON' => [Hash] + } + ruby_classes = api_type_to_ruby_class[data.type.to_s] + unless ruby_classes && ruby_classes.any? { |klass| default.is_a?(klass) } + eval = { reason: DevCycle::EVAL_REASONS::DEFAULT, details: DevCycle::DEFAULT_REASON_DETAILS::TYPE_MISMATCH } + return Variable.new(key: key, value: default, isDefaulted: true, eval: eval) + end + end return data rescue ApiError => error - if error.code != 404 + if error.code == 404 @api_client.config.logger.error("Failed to retrieve variable value: #{error.message}") end - return Variable.new(key: key, value: default, isDefaulted: true) - end + return Variable.new(key: key, value: default, isDefaulted: true, eval: { reason: DevCycle::EVAL_REASONS::DEFAULT, details: DevCycle::DEFAULT_REASON_DETAILS::ERROR }) + end end # Get all variables by key for user data diff --git a/lib/devcycle-ruby-server-sdk/eval_reasons.rb b/lib/devcycle-ruby-server-sdk/eval_reasons.rb index 01f58de..030e891 100644 --- a/lib/devcycle-ruby-server-sdk/eval_reasons.rb +++ b/lib/devcycle-ruby-server-sdk/eval_reasons.rb @@ -9,5 +9,8 @@ module EVAL_REASONS module DEFAULT_REASON_DETAILS MISSING_CONFIG = 'Missing Config' USER_NOT_TARGETED = 'User Not Targeted' + TYPE_MISMATCH = 'Variable Type Mismatch' + MISSING_VARIABLE = 'Missing Variable' + ERROR = 'Error' end end diff --git a/lib/devcycle-ruby-server-sdk/models/variable.rb b/lib/devcycle-ruby-server-sdk/models/variable.rb index 675c3c8..359780b 100644 --- a/lib/devcycle-ruby-server-sdk/models/variable.rb +++ b/lib/devcycle-ruby-server-sdk/models/variable.rb @@ -78,7 +78,8 @@ def self.openapi_types :'type' => :'String', :'value' => :'Object', :'defaultValue' => :'Object', - :'isDefaulted' => :'Boolean' + :'isDefaulted' => :'Boolean', + :'eval' => :'Object' } end diff --git a/spec/api/devcycle_api_spec.rb b/spec/api/devcycle_api_spec.rb index 437c210..f18f139 100644 --- a/spec/api/devcycle_api_spec.rb +++ b/spec/api/devcycle_api_spec.rb @@ -12,6 +12,9 @@ require 'spec_helper' require 'json' +require 'webmock' + +include WebMock::API # Unit tests for DevCycle::Client # Automatically generated by openapi-generator (https://openapi-generator.tech) @@ -68,6 +71,8 @@ it 'should work' do result = @api_instance.variable(@user, "ruby-example-tests-default", false) expect(result.isDefaulted).to eq true + expect(result.eval[:reason]).to eq "DEFAULT" + expect(result.eval[:details]).to eq "Error" result = @api_instance.variable_value(@user, "ruby-example-tests-default", true) expect(result).to eq true @@ -88,6 +93,51 @@ result = @api_instance.variable_value(@user, "test", true) expect(result).to eq true + + result = @api_instance.variable(@user, "test-number-variable", 0) + expect(result.isDefaulted).to eq false + expect(result.value).to eq 123 + + result = @api_instance.variable(@user, "test-number-variable", 0) + expect(result.isDefaulted).to eq false + expect(result.value).to eq 123 + + result = @api_instance.variable(@user, "test-float-variable", 0.0) + expect(result.isDefaulted).to eq false + expect(result.value).to eq 4.56 + + result = @api_instance.variable(@user, "test-string-variable", "") + expect(result.isDefaulted).to eq false + expect(result.value).to eq "on" + + result = @api_instance.variable(@user, "test-json-variable", {}) + expect(result.isDefaulted).to eq false + expect(result.value).to eq({:message => "a"}) + + result = @api_instance.variable(@user, "test", "false") + expect(result.isDefaulted).to eq true + expect(result.eval[:reason]).to eq "DEFAULT" + expect(result.eval[:details]).to eq "Variable Type Mismatch" + + result = @api_instance.variable(@user, "test-number-variable", "123") + expect(result.isDefaulted).to eq true + expect(result.eval[:reason]).to eq "DEFAULT" + expect(result.eval[:details]).to eq "Variable Type Mismatch" + + result = @api_instance.variable(@user, "test-number-variable", true) + expect(result.isDefaulted).to eq true + expect(result.eval[:reason]).to eq "DEFAULT" + expect(result.eval[:details]).to eq "Variable Type Mismatch" + + result = @api_instance.variable(@user, "test-string-variable", true) + expect(result.isDefaulted).to eq true + expect(result.eval[:reason]).to eq "DEFAULT" + expect(result.eval[:details]).to eq "Variable Type Mismatch" + + result = @api_instance.variable(@user, "test-json-variable", "on") + expect(result.isDefaulted).to eq true + expect(result.eval[:reason]).to eq "DEFAULT" + expect(result.eval[:details]).to eq "Variable Type Mismatch" end end @@ -104,4 +154,37 @@ end end + describe 'get_variable_by_key test' do + before do + WebMock.disable_net_connect!(allow_localhost: true) + end + + after do + WebMock.allow_net_connect! + end + + it 'should work with mocked response' do + stub_request(:post, "https://bucketing-api.devcycle.com/v1/variables/mocked_variable"). + to_return(status: 200, body: "{\"isDefaulted\": false, \"value\": true, \"eval\": {\"reason\": \"SPLIT\", \"details\": \"Random Distribution | All Users\", \"target_id\": \"621642332ea68943c8833c4d\"}}", headers: {}) + + result = @api_instance.variable(@user, "mocked_variable", false) + expect(result.isDefaulted).to eq false + expect(result.value).to eq true + # Use Hash syntax since eval is deserialized as a Hash + expect(result.eval[:reason]).to eq "SPLIT" + expect(result.eval[:details]).to eq "Random Distribution | All Users" + expect(result.eval[:target_id]).to eq "621642332ea68943c8833c4d" + end + + it 'should return error details' do + stub_request(:post, "https://bucketing-api.devcycle.com/v1/variables/test"). + to_return(status: 500, body: "{\"isDefaulted\": true, \"value\": false, \"eval\": {\"reason\": \"DEFAULT\", \"details\": \"Error\"}}", headers: {}) + + result = @api_instance.variable(@user, "test", false) + expect(result.isDefaulted).to eq true + expect(result.eval[:reason]).to eq "DEFAULT" + expect(result.eval[:details]).to eq "Error" + end + end + end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index c57463b..60942ed 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -12,6 +12,10 @@ # load the gem require 'devcycle-ruby-server-sdk' +require 'webmock/rspec' + +# Configure WebMock to allow real HTTP requests by default, but enable it for specific tests +WebMock.allow_net_connect! # The following was generated by the `rspec --init` command. Conventionally, all # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.