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

feat: add flag metadata #459

Merged
merged 6 commits into from
Jun 7, 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
11 changes: 8 additions & 3 deletions src/main/java/dev/openfeature/sdk/FlagEvaluationDetails.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,27 @@

/**
* Contains information about how the evaluation happened, including any resolved values.
*
* @param <T> the type of the flag being evaluated.
*/
@Data @Builder
@Data
@Builder
public class FlagEvaluationDetails<T> implements BaseEvaluation<T> {

private String flagKey;
private T value;
@Nullable private String variant;
@Nullable private String reason;
private ErrorCode errorCode;
@Nullable private String errorMessage;
@Builder.Default private FlagMetadata flagMetadata = FlagMetadata.builder().build();

/**
* Generate detail payload from the provider response.
*
* @param providerEval provider response
* @param flagKey key for the flag being evaluated
* @param <T> type of flag being returned
* @param flagKey key for the flag being evaluated
* @param <T> type of flag being returned
* @return detail payload
*/
public static <T> FlagEvaluationDetails<T> from(ProviderEvaluation<T> providerEval, String flagKey) {
Expand All @@ -33,6 +37,7 @@ public static <T> FlagEvaluationDetails<T> from(ProviderEvaluation<T> providerEv
.variant(providerEval.getVariant())
.reason(providerEval.getReason())
.errorCode(providerEval.getErrorCode())
.flagMetadata(providerEval.getFlagMetadata())
.build();
}
}
188 changes: 188 additions & 0 deletions src/main/java/dev/openfeature/sdk/FlagMetadata.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
package dev.openfeature.sdk;

import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.Map;

/**
* Immutable Flag Metadata representation. Implementation is backed by a {@link Map} and immutability is provided
* through builder and accessors.
*/
@Slf4j
public class FlagMetadata {
toddbaert marked this conversation as resolved.
Show resolved Hide resolved
private final Map<String, Object> metadata;

private FlagMetadata(Map<String, Object> metadata) {
this.metadata = metadata;
}

/**
* Retrieve a {@link String} value for the given key. A {@code null} value is returned if the key does not exist
* or if the value is of a different type.
*
* @param key flag metadata key to retrieve
*/
public String getString(final String key) {
return getValue(key, String.class);
}

/**
* Retrieve a {@link Integer} value for the given key. A {@code null} value is returned if the key does not exist
* or if the value is of a different type.
*
* @param key flag metadata key to retrieve
*/
public Integer getInteger(final String key) {
return getValue(key, Integer.class);
}

/**
* Retrieve a {@link Long} value for the given key. A {@code null} value is returned if the key does not exist
* or if the value is of a different type.
*
* @param key flag metadata key to retrieve
*/
public Long getLong(final String key) {
return getValue(key, Long.class);
}

/**
* Retrieve a {@link Float} value for the given key. A {@code null} value is returned if the key does not exist
* or if the value is of a different type.
*
* @param key flag metadata key to retrieve
*/
public Float getFloat(final String key) {
return getValue(key, Float.class);
}

/**
* Retrieve a {@link Double} value for the given key. A {@code null} value is returned if the key does not exist
* or if the value is of a different type.
*
* @param key flag metadata key to retrieve
*/
public Double getDouble(final String key) {
return getValue(key, Double.class);
}

/**
* Retrieve a {@link Boolean} value for the given key. A {@code null} value is returned if the key does not exist
* or if the value is of a different type.
*
* @param key flag metadata key to retrieve
*/
public Boolean getBoolean(final String key) {
return getValue(key, Boolean.class);
}

private <T> T getValue(final String key, final Class<T> type) {
final Object o = metadata.get(key);

if (o == null) {
log.debug("Metadata key " + key + "does not exist");
return null;
}

try {
return type.cast(o);
} catch (ClassCastException e) {
log.debug("Error retrieving value for key " + key, e);
return null;
}
}


/**
* Obtain a builder for {@link FlagMetadata}.
*/
public static FlagMetadataBuilder builder() {
return new FlagMetadataBuilder();
}

/**
* Immutable builder for {@link FlagMetadata}.
*/
public static class FlagMetadataBuilder {
private final Map<String, Object> metadata;

private FlagMetadataBuilder() {
metadata = new HashMap<>();
}

/**
* Add String value to the metadata.
*
* @param key flag metadata key to add
* @param value flag metadata value to add
*/
public FlagMetadataBuilder addString(final String key, final String value) {
metadata.put(key, value);
return this;
}

/**
* Add Integer value to the metadata.
*
* @param key flag metadata key to add
* @param value flag metadata value to add
*/
public FlagMetadataBuilder addInteger(final String key, final Integer value) {
metadata.put(key, value);
return this;
}

/**
* Add Long value to the metadata.
*
* @param key flag metadata key to add
* @param value flag metadata value to add
*/
public FlagMetadataBuilder addLong(final String key, final Long value) {
metadata.put(key, value);
return this;
}

/**
* Add Float value to the metadata.
*
* @param key flag metadata key to add
* @param value flag metadata value to add
*/
public FlagMetadataBuilder addFloat(final String key, final Float value) {
metadata.put(key, value);
return this;
}

/**
* Add Double value to the metadata.
*
* @param key flag metadata key to add
* @param value flag metadata value to add
*/
public FlagMetadataBuilder addDouble(final String key, final Double value) {
metadata.put(key, value);
return this;
}

/**
* Add Boolean value to the metadata.
*
* @param key flag metadata key to add
* @param value flag metadata value to add
*/
public FlagMetadataBuilder addBoolean(final String key, final Boolean value) {
metadata.put(key, value);
return this;
}

/**
* Retrieve {@link FlagMetadata} with provided key,value pairs.
*/
public FlagMetadata build() {
return new FlagMetadata(this.metadata);
}

}
}
2 changes: 2 additions & 0 deletions src/main/java/dev/openfeature/sdk/ProviderEvaluation.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ public class ProviderEvaluation<T> implements BaseEvaluation<T> {
@Nullable private String reason;
ErrorCode errorCode;
@Nullable private String errorMessage;
@Builder.Default
private FlagMetadata flagMetadata = FlagMetadata.builder().build();
}
17 changes: 13 additions & 4 deletions src/test/java/dev/openfeature/sdk/DoSomethingProvider.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package dev.openfeature.sdk;

public class DoSomethingProvider implements FeatureProvider {
class DoSomethingProvider implements FeatureProvider {

static final String name = "Something";
// Flag evaluation metadata
static final FlagMetadata flagMetadata = FlagMetadata.builder().build();

public static final String name = "Something";
private EvaluationContext savedContext;

public EvaluationContext getMergedContext() {
EvaluationContext getMergedContext() {
return savedContext;
}

Expand All @@ -18,13 +21,16 @@ public Metadata getMetadata() {
public ProviderEvaluation<Boolean> getBooleanEvaluation(String key, Boolean defaultValue, EvaluationContext ctx) {
savedContext = ctx;
return ProviderEvaluation.<Boolean>builder()
.value(!defaultValue).build();
.value(!defaultValue)
.flagMetadata(flagMetadata)
.build();
}

@Override
public ProviderEvaluation<String> getStringEvaluation(String key, String defaultValue, EvaluationContext ctx) {
return ProviderEvaluation.<String>builder()
.value(new StringBuilder(defaultValue).reverse().toString())
.flagMetadata(flagMetadata)
.build();
}

Expand All @@ -33,6 +39,7 @@ public ProviderEvaluation<Integer> getIntegerEvaluation(String key, Integer defa
savedContext = ctx;
return ProviderEvaluation.<Integer>builder()
.value(defaultValue * 100)
.flagMetadata(flagMetadata)
.build();
}

Expand All @@ -41,6 +48,7 @@ public ProviderEvaluation<Double> getDoubleEvaluation(String key, Double default
savedContext = ctx;
return ProviderEvaluation.<Double>builder()
.value(defaultValue * 100)
.flagMetadata(flagMetadata)
.build();
}

Expand All @@ -49,6 +57,7 @@ public ProviderEvaluation<Value> getObjectEvaluation(String key, Value defaultVa
savedContext = invocationContext;
return ProviderEvaluation.<Value>builder()
.value(null)
.flagMetadata(flagMetadata)
.build();
}
}
16 changes: 11 additions & 5 deletions src/test/java/dev/openfeature/sdk/FlagEvaluationSpecTest.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package dev.openfeature.sdk;

import static org.assertj.core.api.Assertions.*;
import static dev.openfeature.sdk.DoSomethingProvider.flagMetadata;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
Expand All @@ -15,18 +17,18 @@
import java.util.List;
import java.util.Map;

import dev.openfeature.sdk.exceptions.FlagNotFoundError;
import dev.openfeature.sdk.testutils.FeatureProviderTestUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import dev.openfeature.sdk.fixtures.HookFixtures;
import org.mockito.ArgumentMatchers;
import org.mockito.Mockito;
import org.simplify4u.slf4jmock.LoggerMock;
import org.slf4j.Logger;

import dev.openfeature.sdk.exceptions.FlagNotFoundError;
import dev.openfeature.sdk.fixtures.HookFixtures;
import dev.openfeature.sdk.testutils.FeatureProviderTestUtils;

class FlagEvaluationSpecTest implements HookFixtures {

private Logger logger;
Expand Down Expand Up @@ -150,6 +152,7 @@ void getApiInstance() {
.flagKey(key)
.value(false)
.variant(null)
.flagMetadata(flagMetadata)
.build();
assertEquals(bd, c.getBooleanDetails(key, true));
assertEquals(bd, c.getBooleanDetails(key, true, new ImmutableContext()));
Expand All @@ -159,6 +162,7 @@ void getApiInstance() {
.flagKey(key)
.value("tset")
.variant(null)
.flagMetadata(flagMetadata)
.build();
assertEquals(sd, c.getStringDetails(key, "test"));
assertEquals(sd, c.getStringDetails(key, "test", new ImmutableContext()));
Expand All @@ -167,6 +171,7 @@ void getApiInstance() {
FlagEvaluationDetails<Integer> id = FlagEvaluationDetails.<Integer>builder()
.flagKey(key)
.value(400)
.flagMetadata(flagMetadata)
.build();
assertEquals(id, c.getIntegerDetails(key, 4));
assertEquals(id, c.getIntegerDetails(key, 4, new ImmutableContext()));
Expand All @@ -175,6 +180,7 @@ void getApiInstance() {
FlagEvaluationDetails<Double> dd = FlagEvaluationDetails.<Double>builder()
.flagKey(key)
.value(40.0)
.flagMetadata(flagMetadata)
.build();
assertEquals(dd, c.getDoubleDetails(key, .4));
assertEquals(dd, c.getDoubleDetails(key, .4, new ImmutableContext()));
Expand Down
Loading