Skip to content

Commit 1857c1f

Browse files
committed
Fix InternalEvaluator compatibility and resolve class conflicts
- Remove unused _configuration parameter from get_assignment method - Rename ResolutionDetails to EvaluationResult to avoid class conflict with OpenFeature SDK - Fix variationType output format in flagMetadata (INTEGER→number, BOOLEAN→boolean, etc.) - Add fallback from targeting_key when id attribute is missing in rule evaluation - Update all method signatures and test files to match new get_assignment interface - Fix test data path in test_cases_spec.rb to use local fixtures - Add flagMetadata validation in tests
1 parent 6b42a91 commit 1857c1f

File tree

9 files changed

+142
-94
lines changed

9 files changed

+142
-94
lines changed

lib/datadog/open_feature/binding/evaluator.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module Datadog
22
module OpenFeature
33
module Binding
4-
ResolutionDetails = Struct.new(
4+
EvaluationResult = Struct.new(
55
:value,
66
:reason,
77
:variant,
@@ -17,8 +17,8 @@ def initialize(ufc_json)
1717
@ufc_json = ufc_json
1818
end
1919

20-
def get_assignment(_configuration, _flag_key, _evaluation_context, expected_type, _time, _default_value)
21-
ResolutionDetails.new(
20+
def get_assignment(_flag_key, _evaluation_context, expected_type, _time, _default_value)
21+
EvaluationResult.new(
2222
value: generate(expected_type),
2323
reason: 'hardcoded',
2424
variant: 'hardcoded'

lib/datadog/open_feature/binding/internal_evaluator.rb

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ def initialize(ufc_json)
2424
@parsed_config = parse_and_validate_json(ufc_json)
2525
end
2626

27-
def get_assignment(_configuration, flag_key, _evaluation_context, expected_type, _time, default_value)
27+
def get_assignment(flag_key, _evaluation_context, expected_type, _time, default_value)
2828
# Return default value if JSON parsing failed during initialization
29-
if @parsed_config.is_a?(ResolutionDetails)
30-
return ResolutionDetails.new(
29+
if @parsed_config.is_a?(EvaluationResult)
30+
return EvaluationResult.new(
3131
value: default_value,
3232
error_code: @parsed_config.error_code,
3333
error_message: @parsed_config.error_message
@@ -59,18 +59,18 @@ def get_assignment(_configuration, flag_key, _evaluation_context, expected_type,
5959
selected_allocation, selected_variation, reason = evaluate_flag_allocations(flag, _evaluation_context, _time)
6060

6161
# Return the actual assignment result
62-
ResolutionDetails.new(
62+
EvaluationResult.new(
6363
value: selected_variation.value,
6464
reason: reason,
6565
variant: selected_variation.key,
6666
flag_metadata: {
6767
'allocationKey' => selected_allocation.key,
6868
'doLog' => selected_allocation.do_log,
69-
'variationType' => flag.variation_type
69+
'variationType' => convert_variation_type_for_output(flag.variation_type)
7070
}
7171
)
7272
rescue EvaluationError => e
73-
# Convert evaluation errors to ResolutionDetails with default value - matches Rust error propagation
73+
# Convert evaluation errors to EvaluationResult with default value - matches Rust error propagation
7474
create_evaluation_error_with_default(e.code, e.message, default_value)
7575
end
7676
end
@@ -110,23 +110,23 @@ def parse_and_validate_json(ufc_json)
110110
end
111111

112112
def create_parse_error(error_code, error_message)
113-
ResolutionDetails.new(
113+
EvaluationResult.new(
114114
value: nil,
115115
error_code: error_code,
116116
error_message: error_message
117117
)
118118
end
119119

120120
def create_evaluation_error(error_code, error_message)
121-
ResolutionDetails.new(
121+
EvaluationResult.new(
122122
value: nil,
123123
error_code: error_code,
124124
error_message: error_message
125125
)
126126
end
127127

128128
def create_evaluation_error_with_default(error_code, error_message, default_value)
129-
ResolutionDetails.new(
129+
EvaluationResult.new(
130130
value: default_value,
131131
error_code: error_code,
132132
error_message: error_message
@@ -255,11 +255,23 @@ def get_attribute_from_context(attribute_name, evaluation_context)
255255

256256
# If evaluation_context is a hash, look up the attribute
257257
if evaluation_context.respond_to?(:[])
258-
evaluation_context[attribute_name] || evaluation_context[attribute_name.to_sym]
258+
attribute_value = evaluation_context[attribute_name] || evaluation_context[attribute_name.to_sym]
259+
260+
# Special handling for 'id' attribute: if not present, use targeting_key
261+
if attribute_value.nil? && attribute_name == 'id'
262+
attribute_value = get_targeting_key(evaluation_context)
263+
end
264+
265+
attribute_value
259266
elsif evaluation_context.respond_to?(attribute_name)
260267
evaluation_context.send(attribute_name)
261268
else
262-
nil
269+
# Special handling for 'id' attribute: if not present, use targeting_key
270+
if attribute_name == 'id'
271+
get_targeting_key(evaluation_context)
272+
else
273+
nil
274+
end
263275
end
264276
end
265277

@@ -448,6 +460,19 @@ def determine_assignment_reason(allocation)
448460
AssignmentReason::SPLIT
449461
end
450462
end
463+
464+
def convert_variation_type_for_output(variation_type)
465+
# Convert from SCREAMING_SNAKE_CASE to lowercase format for output
466+
# This matches the expected test format
467+
case variation_type
468+
when VariationType::STRING then 'string'
469+
when VariationType::INTEGER then 'number'
470+
when VariationType::NUMERIC then 'number'
471+
when VariationType::BOOLEAN then 'boolean'
472+
when VariationType::JSON then 'object'
473+
else variation_type # fallback to original value
474+
end
475+
end
451476
end
452477
end
453478
end

lib/datadog/open_feature/evaluation_engine.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def fetch_value(flag_key:, expected_type:, evaluation_context: nil, default_valu
4242
# In the example from the OpenFeature there is zero trust to the result of the evaluation
4343
# do we want to go that way?
4444

45-
@evaluator.get_assignment(:todo_remove_this, flag_key, evaluation_context, expected_type, Time.now.utc.to_i, default_value)
45+
@evaluator.get_assignment(flag_key, evaluation_context, expected_type, Time.now.utc.to_i, default_value)
4646
rescue => e
4747
@telemetry.report(e, description: 'OpenFeature: Failed to fetch value for flag')
4848
ResolutionError.new(code: Ext::PROVIDER_FATAL, message: e.message, reason: Ext::ERROR)

sig/datadog/open_feature/binding/evaluator.rbs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module Datadog
22
module OpenFeature
33
module Binding
4-
class ResolutionDetails < ::Struct[untyped]
4+
class EvaluationResult < ::Struct[untyped]
55
attr_reader value: untyped
66

77
attr_reader reason: ::String
@@ -33,8 +33,9 @@ module Datadog
3333
::String flag_key,
3434
untyped evaluation_context,
3535
::Symbol expected_type,
36-
::Integer time
37-
) -> ResolutionDetails
36+
::Integer time,
37+
untyped default_value
38+
) -> EvaluationResult
3839

3940
private
4041

sig/datadog/open_feature/evaluation_engine.rbs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ module Datadog
2323
flag_key: ::String,
2424
expected_type: ::Symbol,
2525
?evaluation_context: ::OpenFeature::SDK::EvaluationContext
26-
) -> (Binding::ResolutionDetails | ResolutionError)
26+
) -> (Binding::EvaluationResult | ResolutionError)
2727

2828
def reconfigure!: () -> void
2929
end

spec/datadog/open_feature/binding/allocation_matching_spec.rb

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
let(:evaluator) { Datadog::OpenFeature::Binding::InternalEvaluator.new(flag_config.to_json) }
4242

4343
it 'skips expired allocations and uses active ones' do
44-
result = evaluator.get_assignment(nil, "time_test_flag", {}, :string, Time.now, "default")
44+
result = evaluator.get_assignment("time_test_flag", {}, :string, Time.now, "default")
4545

4646
expect(result.error_code).to be_nil
4747
expect(result.value).to eq("treatment_value") # Should use active allocation
@@ -51,7 +51,7 @@
5151
end
5252

5353
it 'returns assignment reason based on allocation properties' do
54-
result = evaluator.get_assignment(nil, "time_test_flag", {}, :string, Time.now, "default")
54+
result = evaluator.get_assignment("time_test_flag", {}, :string, Time.now, "default")
5555

5656
expect(result.reason).to eq("STATIC") # Single split with no shards = static
5757
end
@@ -61,16 +61,16 @@
6161
let(:evaluator) { Datadog::OpenFeature::Binding::InternalEvaluator.new('{"flags": {}}') }
6262

6363
it 'returns user default on flag lookup errors' do
64-
result = evaluator.get_assignment(nil, "missing_flag", {}, :string, Time.now, "user_fallback")
64+
result = evaluator.get_assignment("missing_flag", {}, :string, Time.now, "user_fallback")
6565

6666
expect(result.error_code).to eq("FLAG_UNRECOGNIZED_OR_DISABLED")
6767
expect(result.value).to eq("user_fallback")
6868
end
6969

7070
it 'preserves different default types correctly' do
71-
string_result = evaluator.get_assignment(nil, "missing", {}, :string, Time.now, "string_default")
72-
number_result = evaluator.get_assignment(nil, "missing", {}, :float, Time.now, 3.14)
73-
bool_result = evaluator.get_assignment(nil, "missing", {}, :boolean, Time.now, true)
71+
string_result = evaluator.get_assignment("missing", {}, :string, Time.now, "string_default")
72+
number_result = evaluator.get_assignment("missing", {}, :float, Time.now, 3.14)
73+
bool_result = evaluator.get_assignment("missing", {}, :boolean, Time.now, true)
7474

7575
expect(string_result.value).to eq("string_default")
7676
expect(number_result.value).to eq(3.14)
@@ -101,7 +101,7 @@
101101
}
102102

103103
evaluator = Datadog::OpenFeature::Binding::InternalEvaluator.new(config_with_time_bounds.to_json)
104-
result = evaluator.get_assignment(nil, "timed_flag", {}, :boolean, Time.now, false)
104+
result = evaluator.get_assignment("timed_flag", {}, :boolean, Time.now, false)
105105

106106
expect(result.error_code).to be_nil
107107
expect(result.reason).to eq("TARGETING_MATCH") # Has time bounds

0 commit comments

Comments
 (0)