Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: int/float auto-conversion #472

Merged
merged 5 commits into from
Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
import static dev.openfeature.contrib.providers.flagd.resolver.process.model.FeatureFlag.EMPTY_TARGETING_STRING;

/**
* flagd in-process resolver. Resolves feature flags in-process. Flags are retrieved from {@link Storage}, where the
* flagd in-process resolver. Resolves feature flags in-process. Flags are
* retrieved from {@link Storage}, where the
* {@link Storage} maintain flag configurations obtained from known source.
*/
@Slf4j
Expand Down Expand Up @@ -97,31 +98,31 @@ public void shutdown() throws InterruptedException {
* Resolve a boolean flag.
*/
public ProviderEvaluation<Boolean> booleanEvaluation(String key, Boolean defaultValue,
EvaluationContext ctx) {
EvaluationContext ctx) {
return resolve(Boolean.class, key, ctx);
}

/**
* Resolve a string flag.
*/
public ProviderEvaluation<String> stringEvaluation(String key, String defaultValue,
EvaluationContext ctx) {
EvaluationContext ctx) {
return resolve(String.class, key, ctx);
}

/**
* Resolve a double flag.
*/
public ProviderEvaluation<Double> doubleEvaluation(String key, Double defaultValue,
EvaluationContext ctx) {
EvaluationContext ctx) {
return resolve(Double.class, key, ctx);
}

/**
* Resolve an integer flag.
*/
public ProviderEvaluation<Integer> integerEvaluation(String key, Integer defaultValue,
EvaluationContext ctx) {
EvaluationContext ctx) {
return resolve(Integer.class, key, ctx);
}

Expand All @@ -142,7 +143,7 @@ public ProviderEvaluation<Value> objectEvaluation(String key, Value defaultValue
}

private <T> ProviderEvaluation<T> resolve(Class<T> type, String key,
EvaluationContext ctx) {
EvaluationContext ctx) {
final FeatureFlag flag = flagStore.getFlag(key);

// missing flag
Expand All @@ -155,7 +156,6 @@ private <T> ProviderEvaluation<T> resolve(Class<T> type, String key,
throw new FlagNotFoundError("flag: " + key + " is disabled");
}


final Object resolvedVariant;
final String reason;

Expand Down Expand Up @@ -186,7 +186,13 @@ private <T> ProviderEvaluation<T> resolve(Class<T> type, String key,
log.debug(message);
throw new TypeMismatchError(message);
}

if (value instanceof Integer && type == Double.class) {
// if this is an integer and we are trying to resolve a double, convert
value = ((Integer) value).doubleValue();
} else if (value instanceof Double && type == Integer.class) {
// if this is a double and we are trying to resolve an integer, convert
value = ((Double) value).intValue();
}
if (!type.isAssignableFrom(value.getClass()) || !(resolvedVariant instanceof String)) {
String message = "returning default variant for flagKey: %s, type not valid";
log.debug(String.format(message, key));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -460,16 +460,15 @@ public void the_resolved_boolean_zero_value_should_be(String expected) {
// float/double value
@When("a zero-value float flag with key {string} is evaluated with default value {double}")
public void a_zero_value_float_flag_with_key_is_evaluated_with_default_value(String flagKey, Double defaultValue) {
// TODO: There is a bug here with 0 value floats
// this.doubleFlagKey = flagKey;
// this.doubleFlagDefaultValue = defaultValue;
this.doubleFlagKey = flagKey;
this.doubleFlagDefaultValue = defaultValue;
}

@Then("the resolved float zero-value should be {double}")
public void the_resolved_float_zero_value_should_be(Double expected) {
// FlagEvaluationDetails<Double> details =
// client.getDoubleDetails("float-zero-flag", this.doubleFlagDefaultValue);
// assertEquals(expected, details.getValue());
FlagEvaluationDetails<Double> details =
client.getDoubleDetails("float-zero-flag", this.doubleFlagDefaultValue);
assertEquals(expected, details.getValue());
}

// integer value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,17 @@ public void eventHandling() throws Throwable {
final BlockingQueue<StorageState> sender = new LinkedBlockingQueue<>(5);
final BlockingQueue<ProviderState> receiver = new LinkedBlockingQueue<>(5);

InProcessResolver inProcessResolver =
getInProcessResolverWth(new MockStorage(new HashMap<>(), sender), providerState -> {
InProcessResolver inProcessResolver = getInProcessResolverWth(new MockStorage(new HashMap<>(), sender),
providerState -> {
receiver.offer(providerState);
});

// when - init and emit events
Thread initThread = new Thread(() -> {
try {
inProcessResolver.init();
} catch (Exception e) {}
} catch (Exception e) {
}
});
initThread.start();
if (!sender.offer(StorageState.OK, 100, TimeUnit.MILLISECONDS)) {
Expand Down Expand Up @@ -86,8 +87,8 @@ public void simpleBooleanResolving() throws Exception {
});

// when
ProviderEvaluation<Boolean> providerEvaluation =
inProcessResolver.booleanEvaluation("booleanFlag", false, new ImmutableContext());
ProviderEvaluation<Boolean> providerEvaluation = inProcessResolver.booleanEvaluation("booleanFlag", false,
new ImmutableContext());

// then
assertEquals(true, providerEvaluation.getValue());
Expand All @@ -105,15 +106,53 @@ public void simpleDoubleResolving() throws Exception {
});

// when
ProviderEvaluation<Double> providerEvaluation =
inProcessResolver.doubleEvaluation("doubleFlag", 0d, new ImmutableContext());
ProviderEvaluation<Double> providerEvaluation = inProcessResolver.doubleEvaluation("doubleFlag", 0d,
new ImmutableContext());

// then
assertEquals(3.141d, providerEvaluation.getValue());
assertEquals("one", providerEvaluation.getVariant());
assertEquals(Reason.STATIC.toString(), providerEvaluation.getReason());
}

@Test
public void fetchIntegerAsDouble() throws Exception {
// given
final Map<String, FeatureFlag> flagMap = new HashMap<>();
flagMap.put("doubleFlag", DOUBLE_FLAG);

InProcessResolver inProcessResolver = getInProcessResolverWth(new MockStorage(flagMap), providerState -> {
});

// when
ProviderEvaluation<Integer> providerEvaluation = inProcessResolver.integerEvaluation("doubleFlag", 0,
new ImmutableContext());

// then
assertEquals(3, providerEvaluation.getValue());
assertEquals("one", providerEvaluation.getVariant());
assertEquals(Reason.STATIC.toString(), providerEvaluation.getReason());
}

@Test
public void fetchDoubleAsInt() throws Exception {
// given
final Map<String, FeatureFlag> flagMap = new HashMap<>();
flagMap.put("integerFlag", INT_FLAG);

InProcessResolver inProcessResolver = getInProcessResolverWth(new MockStorage(flagMap), providerState -> {
});

// when
ProviderEvaluation<Double> providerEvaluation = inProcessResolver.doubleEvaluation("integerFlag", 0d,
new ImmutableContext());

// then
assertEquals(1d, providerEvaluation.getValue());
assertEquals("one", providerEvaluation.getVariant());
assertEquals(Reason.STATIC.toString(), providerEvaluation.getReason());
}

@Test
public void simpleIntResolving() throws Exception {
// given
Expand All @@ -124,8 +163,8 @@ public void simpleIntResolving() throws Exception {
});

// when
ProviderEvaluation<Integer> providerEvaluation =
inProcessResolver.integerEvaluation("integerFlag", 0, new ImmutableContext());
ProviderEvaluation<Integer> providerEvaluation = inProcessResolver.integerEvaluation("integerFlag", 0,
new ImmutableContext());

// then
assertEquals(1, providerEvaluation.getValue());
Expand All @@ -147,8 +186,8 @@ public void simpleObjectResolving() throws Exception {
typeDefault.put("date", "01.01.1990");

// when
ProviderEvaluation<Value> providerEvaluation =
inProcessResolver.objectEvaluation("objectFlag", Value.objectToValue(typeDefault), new ImmutableContext());
ProviderEvaluation<Value> providerEvaluation = inProcessResolver.objectEvaluation("objectFlag",
Value.objectToValue(typeDefault), new ImmutableContext());

// then
Value value = providerEvaluation.getValue();
Expand Down Expand Up @@ -230,9 +269,8 @@ public void targetingMatchedEvaluationFlag() throws Exception {
});

// when
ProviderEvaluation<String> providerEvaluation =
inProcessResolver.stringEvaluation("stringFlag", "loopAlg",
new MutableContext().add("email", "[email protected]"));
ProviderEvaluation<String> providerEvaluation = inProcessResolver.stringEvaluation("stringFlag", "loopAlg",
new MutableContext().add("email", "[email protected]"));

// then
assertEquals("binetAlg", providerEvaluation.getValue());
Expand All @@ -250,9 +288,8 @@ public void targetingUnmatchedEvaluationFlag() throws Exception {
});

// when
ProviderEvaluation<String> providerEvaluation =
inProcessResolver.stringEvaluation("stringFlag", "loopAlg",
new MutableContext().add("email", "[email protected]"));
ProviderEvaluation<String> providerEvaluation = inProcessResolver.stringEvaluation("stringFlag", "loopAlg",
new MutableContext().add("email", "[email protected]"));

// then
assertEquals("loopAlg", providerEvaluation.getValue());
Expand All @@ -275,13 +312,13 @@ public void targetingErrorEvaluationFlag() throws Exception {
});
}


private InProcessResolver getInProcessResolverWth(final MockStorage storage, Consumer<ProviderState> stateConsumer)
throws NoSuchFieldException, IllegalAccessException {
Field flagStore = InProcessResolver.class.getDeclaredField("flagStore");
flagStore.setAccessible(true);

InProcessResolver resolver = new InProcessResolver(FlagdOptions.builder().deadline(1000).build(), stateConsumer);
InProcessResolver resolver = new InProcessResolver(FlagdOptions.builder().deadline(1000).build(),
stateConsumer);
flagStore.set(resolver, storage);

return resolver;
Expand Down