diff --git a/providers/openfeature-flagd-provider/docker/flags.json b/providers/openfeature-flagd-provider/docker/flags.json index 5c46ccc..e5fd506 100644 --- a/providers/openfeature-flagd-provider/docker/flags.json +++ b/providers/openfeature-flagd-provider/docker/flags.json @@ -36,6 +36,57 @@ "real-object": { "real": "value" } }, "defaultVariant": "real-object" + }, + "boolean-flag-targeting": { + "state": "ENABLED", + "variants": { + "on": true, + "off": false + }, + "defaultVariant": "off", + "targeting": { + "if": [ + { + "==": [ + { + "var": "be_true" + }, + true + ] + }, + "on" + ] + } + }, + "color-palette-experiment": { + "state": "ENABLED", + "defaultVariant": "grey", + "variants": { + "red": "#b91c1c", + "blue": "#0284c7", + "green": "#16a34a", + "grey": "#4b5563" + }, + "targeting": { + "fractional": [ + [ + "red", + 25 + ], + [ + "blue", + 25 + ], + [ + "green", + 25 + ], + [ + "grey", + 25 + ] + ] + } } } } diff --git a/providers/openfeature-flagd-provider/lib/openfeature/flagd/provider/client.rb b/providers/openfeature-flagd-provider/lib/openfeature/flagd/provider/client.rb index 90a67fc..31cfa46 100644 --- a/providers/openfeature-flagd-provider/lib/openfeature/flagd/provider/client.rb +++ b/providers/openfeature-flagd-provider/lib/openfeature/flagd/provider/client.rb @@ -1,9 +1,11 @@ # frozen_string_literal: true require "grpc" +require 'google/protobuf/well_known_types' require_relative "schema/v1/schema_services_pb" require_relative "configuration" + module OpenFeature module FlagD module Provider @@ -15,20 +17,20 @@ module Provider # # * metadata - Returns the associated provider metadata with the name # - # * resolve_boolean_value(flag_key:, default_value:, context: nil) - # manner; client.resolve_boolean(flag_key: 'boolean-flag', default_value: false) + # * fetch_boolean_value(flag_key:, default_value:, evaluation_context: nil) + # manner; client.fetch_boolean(flag_key: 'boolean-flag', default_value: false) # - # * resolve_integer_value(flag_key:, default_value:, context: nil) - # manner; client.resolve_integer_value(flag_key: 'integer-flag', default_value: 2) + # * fetch_integer_value(flag_key:, default_value:, evaluation_context: nil) + # manner; client.fetch_integer_value(flag_key: 'integer-flag', default_value: 2) # - # * resolve_float_value(flag_key:, default_value:, context: nil) - # manner; client.resolve_float_value(flag_key: 'float-flag', default_value: 2.0) + # * fetch_float_value(flag_key:, default_value:, evaluation_context: nil) + # manner; client.fetch_float_value(flag_key: 'float-flag', default_value: 2.0) # - # * resolve_string_value(flag_key:, default_value:, context: nil) - # manner; client.resolve_string_value(flag_key: 'string-flag', default_value: 'some-default-value') + # * fetch_string_value(flag_key:, default_value:, evaluation_context: nil) + # manner; client.fetch_string_value(flag_key: 'string-flag', default_value: 'some-default-value') # - # * resolve_object_value(flag_key:, default_value:, context: nil) - # manner; client.resolve_object_value(flag_key: 'flag', default_value: { default_value: 'value'}) + # * fetch_object_value(flag_key:, default_value:, evaluation_context: nil) + # manner; client.fetch_object_value(flag_key: 'flag', default_value: { default_value: 'value'}) class Client PROVIDER_NAME = "flagd Provider" @@ -40,7 +42,7 @@ def initialize(configuration: nil) end def fetch_boolean_value(flag_key:, default_value:, evaluation_context: nil) - request = Grpc::ResolveBooleanRequest.new(flag_key: flag_key) + request = Grpc::ResolveBooleanRequest.new(flag_key: flag_key, context: prepare_evaluation_context(evaluation_context)) process_request { @grpc_client.resolve_boolean(request) } end @@ -54,22 +56,22 @@ def fetch_number_value(flag_key:, default_value:, evaluation_context: nil) end def fetch_integer_value(flag_key:, default_value:, evaluation_context: nil) - request = Grpc::ResolveIntRequest.new(flag_key: flag_key) + request = Grpc::ResolveIntRequest.new(flag_key: flag_key, context: prepare_evaluation_context(evaluation_context)) process_request { @grpc_client.resolve_int(request) } end def fetch_float_value(flag_key:, default_value:, evaluation_context: nil) - request = Grpc::ResolveFloatRequest.new(flag_key: flag_key) - process_request { @grpc_client.resolve_float(request) } + request = Grpc::ResolveFloatRequest.new(flag_key: flag_key, context: prepare_evaluation_context(evaluation_context)) + process_request { @grpc_client.resolve_float(request) } end def fetch_string_value(flag_key:, default_value:, evaluation_context: nil) - request = Grpc::ResolveStringRequest.new(flag_key: flag_key) + request = Grpc::ResolveStringRequest.new(flag_key: flag_key, context: prepare_evaluation_context(evaluation_context)) process_request { @grpc_client.resolve_string(request) } end def fetch_object_value(flag_key:, default_value:, evaluation_context: nil) - request = Grpc::ResolveObjectRequest.new(flag_key: flag_key) + request = Grpc::ResolveObjectRequest.new(flag_key: flag_key, context: prepare_evaluation_context(evaluation_context)) process_request { @grpc_client.resolve_object(request) } end @@ -93,6 +95,14 @@ def process_request(&block) error_response("GENERAL", e.message) end + def prepare_evaluation_context(evaluation_context) + return nil if !evaluation_context + + fields = evaluation_context.fields + fields["targetingKey"] = fields.delete(:targeting_key) + Google::Protobuf::Struct.from_hash(fields) + end + def error_response(error_code, error_message) ResolutionDetails.new(error_code, error_message, "ERROR", nil, nil) end diff --git a/providers/openfeature-flagd-provider/spec/openfeature/flagd/provider_spec.rb b/providers/openfeature-flagd-provider/spec/openfeature/flagd/provider_spec.rb index d7b1f93..d40ed3f 100644 --- a/providers/openfeature-flagd-provider/spec/openfeature/flagd/provider_spec.rb +++ b/providers/openfeature-flagd-provider/spec/openfeature/flagd/provider_spec.rb @@ -110,6 +110,33 @@ end end + context "get value with evaluated context" do + it do + expect( + client.fetch_boolean_value( + flag_key: 'boolean-flag-targeting', + default_value: false, + evaluation_context: OpenFeature::SDK::EvaluationContext.new(be_true: true) + ) + ).to be_truthy + end + + it do + fetch_value_with_targeting_key = ->(targeting_key) do + client.fetch_boolean_value( + flag_key: 'color-palette-experiment', + default_value: "#b91c1c", + evaluation_context: OpenFeature::SDK::EvaluationContext.new(targeting_key: targeting_key) + ) + end + + initial_value = fetch_value_with_targeting_key.("123") + (0..2).to_a.each do # try with 1000 + expect(fetch_value_with_targeting_key.("123")).to eq(initial_value) + end + end + end + context "get details" do it do expect(client.fetch_boolean_details(flag_key: 'boolean-flag', default_value: false).resolution_details.to_h).to include(