Skip to content

Commit fbe19cd

Browse files
committed
flag metadata
Signed-off-by: Kavindu Dodanduwa <[email protected]>
1 parent 7f91942 commit fbe19cd

File tree

6 files changed

+261
-28
lines changed

6 files changed

+261
-28
lines changed

src/main/java/dev/openfeature/sdk/FlagEvaluationDetails.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,25 @@
77

88
/**
99
* Contains information about how the evaluation happened, including any resolved values.
10+
*
1011
* @param <T> the type of the flag being evaluated.
1112
*/
12-
@Data @Builder
13-
public class FlagEvaluationDetails<T> implements BaseEvaluation<T> {
13+
@Data @Builder public class FlagEvaluationDetails<T> implements BaseEvaluation<T> {
14+
1415
private String flagKey;
1516
private T value;
1617
@Nullable private String variant;
1718
@Nullable private String reason;
1819
private ErrorCode errorCode;
1920
@Nullable private String errorMessage;
21+
@Builder.Default private FlagMetadata flagMetadata = FlagMetadata.builder().build();
2022

2123
/**
2224
* Generate detail payload from the provider response.
2325
*
2426
* @param providerEval provider response
25-
* @param flagKey key for the flag being evaluated
26-
* @param <T> type of flag being returned
27+
* @param flagKey key for the flag being evaluated
28+
* @param <T> type of flag being returned
2729
* @return detail payload
2830
*/
2931
public static <T> FlagEvaluationDetails<T> from(ProviderEvaluation<T> providerEval, String flagKey) {
@@ -33,6 +35,7 @@ public static <T> FlagEvaluationDetails<T> from(ProviderEvaluation<T> providerEv
3335
.variant(providerEval.getVariant())
3436
.reason(providerEval.getReason())
3537
.errorCode(providerEval.getErrorCode())
38+
.flagMetadata(providerEval.getFlagMetadata())
3639
.build();
3740
}
3841
}
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
package dev.openfeature.sdk;
2+
3+
import dev.openfeature.sdk.exceptions.GeneralError;
4+
import dev.openfeature.sdk.exceptions.ParseError;
5+
6+
import java.util.HashMap;
7+
import java.util.Map;
8+
9+
/**
10+
* Immutable Flag Metadata representation. Implementation is backed by a {@link Map} and immutability is provided
11+
* through builder and accessors.
12+
*/
13+
public class FlagMetadata {
14+
private final Map<String, Object> metadata;
15+
16+
private FlagMetadata(Map<String, Object> metadata) {
17+
this.metadata = metadata;
18+
}
19+
20+
/**
21+
* Retrieve a {@link String} value for the given key. If a value is not found, {@link GeneralError} will be thrown.
22+
* If value exist but of another type, {@link ParseError} will be thrown.
23+
*
24+
* @param key flag metadata key to retrieve
25+
*/
26+
public String getString(final String key) {
27+
return getValue(key, String.class);
28+
}
29+
30+
/**
31+
* Retrieve an {@link Integer} value for the given key.
32+
* If a value is not found, {@link GeneralError} will be thrown.
33+
* If value exist but of another type, {@link ParseError} will be thrown.
34+
*
35+
* @param key flag metadata key to retrieve
36+
*/
37+
public Integer getInteger(final String key) {
38+
return getValue(key, Integer.class);
39+
}
40+
41+
/**
42+
* Retrieve an {@link Float} value for the given key. If a value is not found, {@link GeneralError} will be thrown.
43+
* If value exist but of another type, {@link ParseError} will be thrown.
44+
*
45+
* @param key flag metadata key to retrieve
46+
*/
47+
public Float getFloat(final String key) {
48+
return getValue(key, Float.class);
49+
}
50+
51+
/**
52+
* Retrieve an {@link Double} value for the given key.
53+
* If a value is not found, {@link GeneralError} will be thrown.
54+
* If value exist but of another type, {@link ParseError} will be thrown.
55+
*
56+
* @param key flag metadata key to retrieve
57+
*/
58+
public Double getDouble(final String key) {
59+
return getValue(key, Double.class);
60+
}
61+
62+
/**
63+
* Retrieve an {@link Boolean} value for the given key.
64+
* If a value is not found, {@link GeneralError} will be thrown.
65+
* If value exist but of another type, {@link ParseError} will be thrown.
66+
*
67+
* @param key flag metadata key to retrieve
68+
*/
69+
public Boolean getBoolean(final String key) {
70+
return getValue(key, Boolean.class);
71+
}
72+
73+
private <T> T getValue(final String key, final Class<T> type) {
74+
final Object o = metadata.get(key);
75+
76+
if (o == null) {
77+
throw new GeneralError("key " + key + " does not exist in metadata");
78+
}
79+
80+
try {
81+
return type.cast(o);
82+
} catch (ClassCastException e) {
83+
throw new ParseError(
84+
"wrong type for key " + key
85+
+ ". Expected" + type.getSimpleName() + "but got " + o.getClass().getSimpleName(), e);
86+
}
87+
}
88+
89+
90+
/**
91+
* Obtain a builder for {@link FlagMetadata}.
92+
*/
93+
public static FlagMetadataBuilder builder() {
94+
return new FlagMetadataBuilder();
95+
}
96+
97+
/**
98+
* Immutable builder for {@link FlagMetadata}.
99+
*/
100+
public static class FlagMetadataBuilder {
101+
private final Map<String, Object> metadata;
102+
103+
private FlagMetadataBuilder() {
104+
metadata = new HashMap<>();
105+
}
106+
107+
/**
108+
* Add String value to the metadata.
109+
*
110+
* @param key flag metadata key to add
111+
* @param value flag metadata value to add
112+
*/
113+
public FlagMetadataBuilder addString(final String key, final String value) {
114+
metadata.put(key, value);
115+
return this;
116+
}
117+
118+
/**
119+
* Add Integer value to the metadata.
120+
*
121+
* @param key flag metadata key to add
122+
* @param value flag metadata value to add
123+
*/
124+
public FlagMetadataBuilder addInteger(final String key, final Integer value) {
125+
metadata.put(key, value);
126+
return this;
127+
}
128+
129+
/**
130+
* Add Float value to the metadata.
131+
*
132+
* @param key flag metadata key to add
133+
* @param value flag metadata value to add
134+
*/
135+
public FlagMetadataBuilder addFloat(final String key, final Float value) {
136+
metadata.put(key, value);
137+
return this;
138+
}
139+
140+
/**
141+
* Add Double value to the metadata.
142+
*
143+
* @param key flag metadata key to add
144+
* @param value flag metadata value to add
145+
*/
146+
public FlagMetadataBuilder addDouble(final String key, final Double value) {
147+
metadata.put(key, value);
148+
return this;
149+
}
150+
151+
/**
152+
* Add Boolean value to the metadata.
153+
*
154+
* @param key flag metadata key to add
155+
* @param value flag metadata value to add
156+
*/
157+
public FlagMetadataBuilder addBoolean(final String key, final Boolean value) {
158+
metadata.put(key, value);
159+
return this;
160+
}
161+
162+
/**
163+
* Retrieve {@link FlagMetadata} with provided key,value pairs.
164+
*/
165+
public FlagMetadata build() {
166+
return new FlagMetadata(this.metadata);
167+
}
168+
169+
}
170+
}

src/main/java/dev/openfeature/sdk/ProviderEvaluation.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,6 @@ public class ProviderEvaluation<T> implements BaseEvaluation<T> {
1313
@Nullable private String reason;
1414
ErrorCode errorCode;
1515
@Nullable private String errorMessage;
16+
@Builder.Default
17+
private FlagMetadata flagMetadata = FlagMetadata.builder().build();
1618
}

src/test/java/dev/openfeature/sdk/DoSomethingProvider.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package dev.openfeature.sdk;
22

3-
public class DoSomethingProvider implements FeatureProvider {
3+
class DoSomethingProvider implements FeatureProvider {
4+
5+
static final String name = "Something";
6+
// Flag evaluation metadata
7+
static final FlagMetadata flagMetadata = FlagMetadata.builder().build();
48

5-
public static final String name = "Something";
69
private EvaluationContext savedContext;
710

8-
public EvaluationContext getMergedContext() {
11+
EvaluationContext getMergedContext() {
912
return savedContext;
1013
}
1114

@@ -18,13 +21,16 @@ public Metadata getMetadata() {
1821
public ProviderEvaluation<Boolean> getBooleanEvaluation(String key, Boolean defaultValue, EvaluationContext ctx) {
1922
savedContext = ctx;
2023
return ProviderEvaluation.<Boolean>builder()
21-
.value(!defaultValue).build();
24+
.value(!defaultValue)
25+
.flagMetadata(flagMetadata)
26+
.build();
2227
}
2328

2429
@Override
2530
public ProviderEvaluation<String> getStringEvaluation(String key, String defaultValue, EvaluationContext ctx) {
2631
return ProviderEvaluation.<String>builder()
2732
.value(new StringBuilder(defaultValue).reverse().toString())
33+
.flagMetadata(flagMetadata)
2834
.build();
2935
}
3036

@@ -33,6 +39,7 @@ public ProviderEvaluation<Integer> getIntegerEvaluation(String key, Integer defa
3339
savedContext = ctx;
3440
return ProviderEvaluation.<Integer>builder()
3541
.value(defaultValue * 100)
42+
.flagMetadata(flagMetadata)
3643
.build();
3744
}
3845

@@ -41,6 +48,7 @@ public ProviderEvaluation<Double> getDoubleEvaluation(String key, Double default
4148
savedContext = ctx;
4249
return ProviderEvaluation.<Double>builder()
4350
.value(defaultValue * 100)
51+
.flagMetadata(flagMetadata)
4452
.build();
4553
}
4654

@@ -49,6 +57,7 @@ public ProviderEvaluation<Value> getObjectEvaluation(String key, Value defaultVa
4957
savedContext = invocationContext;
5058
return ProviderEvaluation.<Value>builder()
5159
.value(null)
60+
.flagMetadata(flagMetadata)
5261
.build();
5362
}
5463
}

src/test/java/dev/openfeature/sdk/FlagEvaluationSpecTest.java

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,20 @@
11
package dev.openfeature.sdk;
22

3-
import static org.assertj.core.api.Assertions.assertThat;
4-
import static org.assertj.core.api.InstanceOfAssertFactories.optional;
3+
import dev.openfeature.sdk.exceptions.FlagNotFoundError;
4+
import dev.openfeature.sdk.fixtures.HookFixtures;
5+
import org.junit.jupiter.api.AfterEach;
6+
import org.junit.jupiter.api.BeforeEach;
7+
import org.junit.jupiter.api.Test;
8+
import org.mockito.ArgumentMatchers;
9+
import org.mockito.Mockito;
10+
import org.simplify4u.slf4jmock.LoggerMock;
11+
import org.slf4j.Logger;
12+
13+
import java.util.HashMap;
14+
import java.util.List;
15+
import java.util.Map;
16+
17+
import static dev.openfeature.sdk.DoSomethingProvider.flagMetadata;
518
import static org.junit.jupiter.api.Assertions.assertEquals;
619
import static org.junit.jupiter.api.Assertions.assertFalse;
720
import static org.junit.jupiter.api.Assertions.assertNull;
@@ -12,24 +25,6 @@
1225
import static org.mockito.Mockito.times;
1326
import static org.mockito.Mockito.verify;
1427

15-
import java.io.Serializable;
16-
import java.util.ArrayList;
17-
import java.util.HashMap;
18-
import java.util.List;
19-
import java.util.Map;
20-
21-
22-
import dev.openfeature.sdk.exceptions.FlagNotFoundError;
23-
import org.junit.jupiter.api.AfterEach;
24-
import org.junit.jupiter.api.BeforeEach;
25-
import org.junit.jupiter.api.Test;
26-
27-
import dev.openfeature.sdk.fixtures.HookFixtures;
28-
import org.mockito.ArgumentMatchers;
29-
import org.mockito.Mockito;
30-
import org.simplify4u.slf4jmock.LoggerMock;
31-
import org.slf4j.Logger;
32-
3328
class FlagEvaluationSpecTest implements HookFixtures {
3429

3530
private Logger logger;
@@ -154,6 +149,7 @@ private Client _client() {
154149
.flagKey(key)
155150
.value(false)
156151
.variant(null)
152+
.flagMetadata(flagMetadata)
157153
.build();
158154
assertEquals(bd, c.getBooleanDetails(key, true));
159155
assertEquals(bd, c.getBooleanDetails(key, true, new ImmutableContext()));
@@ -163,6 +159,7 @@ private Client _client() {
163159
.flagKey(key)
164160
.value("tset")
165161
.variant(null)
162+
.flagMetadata(flagMetadata)
166163
.build();
167164
assertEquals(sd, c.getStringDetails(key, "test"));
168165
assertEquals(sd, c.getStringDetails(key, "test", new ImmutableContext()));
@@ -171,6 +168,7 @@ private Client _client() {
171168
FlagEvaluationDetails<Integer> id = FlagEvaluationDetails.<Integer>builder()
172169
.flagKey(key)
173170
.value(400)
171+
.flagMetadata(flagMetadata)
174172
.build();
175173
assertEquals(id, c.getIntegerDetails(key, 4));
176174
assertEquals(id, c.getIntegerDetails(key, 4, new ImmutableContext()));
@@ -179,6 +177,7 @@ private Client _client() {
179177
FlagEvaluationDetails<Double> dd = FlagEvaluationDetails.<Double>builder()
180178
.flagKey(key)
181179
.value(40.0)
180+
.flagMetadata(flagMetadata)
182181
.build();
183182
assertEquals(dd, c.getDoubleDetails(key, .4));
184183
assertEquals(dd, c.getDoubleDetails(key, .4, new ImmutableContext()));

0 commit comments

Comments
 (0)