Skip to content

Commit a77d686

Browse files
[skip ci] commit 4818e03
2 parents 660acc2 + 4818e03 commit a77d686

File tree

2 files changed

+78
-2
lines changed

2 files changed

+78
-2
lines changed

products/feature-flagging/feature-flagging-api/src/main/java/datadog/trace/api/openfeature/DDEvaluator.java

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import datadog.trace.api.featureflag.ufc.v1.Shard;
1515
import datadog.trace.api.featureflag.ufc.v1.ShardRange;
1616
import datadog.trace.api.featureflag.ufc.v1.Split;
17+
import datadog.trace.api.featureflag.ufc.v1.ValueType;
1718
import datadog.trace.api.featureflag.ufc.v1.Variant;
1819
import dev.openfeature.sdk.ErrorCode;
1920
import dev.openfeature.sdk.EvaluationContext;
@@ -343,14 +344,39 @@ private static <T> ProviderEvaluation<T> resolveVariant(
343344
.build();
344345
}
345346

347+
if (!isTypeCompatible(target, flag.variationType)) {
348+
return error(
349+
defaultValue,
350+
ErrorCode.TYPE_MISMATCH,
351+
"Requested type "
352+
+ target.getSimpleName()
353+
+ " does not match flag variationType "
354+
+ flag.variationType.name());
355+
}
356+
357+
final T mappedValue;
358+
try {
359+
mappedValue = mapValue(target, variant.value);
360+
} catch (final NumberFormatException e) {
361+
return error(
362+
defaultValue,
363+
ErrorCode.PARSE_ERROR,
364+
"Variant '"
365+
+ variant.key
366+
+ "' value does not match declared type "
367+
+ flag.variationType.name()
368+
+ ": "
369+
+ e.getMessage());
370+
}
371+
346372
final ImmutableMetadata.ImmutableMetadataBuilder metadataBuilder =
347373
ImmutableMetadata.builder()
348374
.addString("flagKey", flag.key)
349375
.addString("variationType", flag.variationType.name())
350376
.addString("allocationKey", allocation.key);
351377
final ProviderEvaluation<T> result =
352378
ProviderEvaluation.<T>builder()
353-
.value(mapValue(target, variant.value))
379+
.value(mappedValue)
354380
.reason(Reason.TARGETING_MATCH.name())
355381
.variant(variant.key)
356382
.flagMetadata(metadataBuilder.build())
@@ -371,6 +397,26 @@ private static Object resolveAttribute(final String name, final EvaluationContex
371397
return context.convertValue(resolved);
372398
}
373399

400+
private static boolean isTypeCompatible(final Class<?> target, final ValueType variationType) {
401+
if (variationType == null) {
402+
return true; // No type info — allow any
403+
}
404+
switch (variationType) {
405+
case BOOLEAN:
406+
return target == Boolean.class;
407+
case STRING:
408+
return target == String.class;
409+
case INTEGER:
410+
return target == Integer.class;
411+
case NUMERIC:
412+
return target == Double.class;
413+
case JSON:
414+
return target == Value.class;
415+
default:
416+
return true; // Unknown types pass through — mapValue errors caught as GENERAL
417+
}
418+
}
419+
374420
@SuppressWarnings("unchecked")
375421
static <T> T mapValue(final Class<T> target, final Object value) {
376422
if (value == null) {

products/feature-flagging/feature-flagging-api/src/test/java/datadog/trace/api/openfeature/DDEvaluatorTest.java

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,10 +288,37 @@ private static List<TestCase<?>> evaluateTestCases() {
288288
new Result<>("default")
289289
// Result depends on shard calculation - either match or default
290290
.reason(TARGETING_MATCH.name(), DEFAULT.name())),
291+
// Type mismatch: STRING flag evaluated as Integer
291292
new TestCase<>(0)
292293
.flag("string-number-flag")
293294
.targetingKey("user-123")
294-
.result(new Result<>(123).reason(TARGETING_MATCH.name()).variant("string-num")),
295+
.result(new Result<>(0).reason(ERROR.name()).errorCode(ErrorCode.TYPE_MISMATCH)),
296+
// Type mismatch: STRING flag evaluated as Boolean
297+
new TestCase<>(false)
298+
.flag("simple-string")
299+
.targetingKey("user-123")
300+
.result(new Result<>(false).reason(ERROR.name()).errorCode(ErrorCode.TYPE_MISMATCH)),
301+
// Type mismatch: BOOLEAN flag evaluated as String
302+
new TestCase<>("default")
303+
.flag("boolean-flag")
304+
.targetingKey("user-123")
305+
.result(
306+
new Result<>("default").reason(ERROR.name()).errorCode(ErrorCode.TYPE_MISMATCH)),
307+
// Type mismatch: NUMERIC flag evaluated as Integer
308+
new TestCase<>(0)
309+
.flag("double-flag")
310+
.targetingKey("user-123")
311+
.result(new Result<>(0).reason(ERROR.name()).errorCode(ErrorCode.TYPE_MISMATCH)),
312+
// Type mismatch: INTEGER flag evaluated as Double
313+
new TestCase<>(0.0)
314+
.flag("integer-flag")
315+
.targetingKey("user-123")
316+
.result(new Result<>(0.0).reason(ERROR.name()).errorCode(ErrorCode.TYPE_MISMATCH)),
317+
// Variant value type mismatch: INTEGER flag with string variant value
318+
new TestCase<>(0)
319+
.flag("integer-string-variant-flag")
320+
.targetingKey("user-123")
321+
.result(new Result<>(0).reason(ERROR.name()).errorCode(ErrorCode.PARSE_ERROR)),
295322
new TestCase<>("default")
296323
.flag("broken-flag")
297324
.targetingKey("user-123")
@@ -551,6 +578,9 @@ private ServerConfiguration createTestConfiguration() {
551578
flags.put("not-one-of-false-flag", createNotOneOfFalseFlag());
552579
flags.put("null-context-values-flag", createNullContextValuesFlag());
553580
flags.put("country-rule-flag", createCountryRuleFlag());
581+
flags.put(
582+
"integer-string-variant-flag",
583+
createSimpleFlag("integer-string-variant-flag", ValueType.INTEGER, "not-a-number", "bad"));
554584
return new ServerConfiguration(null, null, null, flags);
555585
}
556586

0 commit comments

Comments
 (0)