Skip to content

Commit 1fa47fc

Browse files
chrfwowtoddbaert
authored andcommitted
feat: Update in-process resolver to support flag metadata #1102
Signed-off-by: christian.lutnik <[email protected]>
1 parent 75469ba commit 1fa47fc

File tree

9 files changed

+560
-366
lines changed

9 files changed

+560
-366
lines changed

Diff for: providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/InProcessResolver.java

+70-17
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
import static dev.openfeature.contrib.providers.flagd.resolver.process.model.FeatureFlag.EMPTY_TARGETING_STRING;
44

5+
import java.util.Map;
6+
import java.util.function.Consumer;
7+
import java.util.function.Supplier;
8+
59
import dev.openfeature.contrib.providers.flagd.FlagdOptions;
610
import dev.openfeature.contrib.providers.flagd.resolver.Resolver;
711
import dev.openfeature.contrib.providers.flagd.resolver.common.ConnectionEvent;
@@ -37,8 +41,9 @@ public class InProcessResolver implements Resolver {
3741
private final Consumer<ConnectionEvent> onConnectionEvent;
3842
private final Operator operator;
3943
private final long deadline;
40-
private final ImmutableMetadata metadata;
44+
private final ImmutableMetadata fallBackMetadata;
4145
private final Supplier<Boolean> connectedSupplier;
46+
private final String scope;
4247

4348
/**
4449
* Resolves flag values using https://buf.build/open-feature/flagd/docs/main:flagd.sync.v1. Flags
@@ -48,20 +53,22 @@ public class InProcessResolver implements Resolver {
4853
* @param connectedSupplier lambda providing current connection status from caller
4954
* @param onConnectionEvent lambda which handles changes in the connection/stream
5055
*/
51-
public InProcessResolver(
52-
FlagdOptions options,
53-
final Supplier<Boolean> connectedSupplier,
54-
Consumer<ConnectionEvent> onConnectionEvent) {
56+
public InProcessResolver(FlagdOptions options, final Supplier<Boolean> connectedSupplier,
57+
Consumer<ConnectionEvent> onConnectionEvent) {
5558
this.flagStore = new FlagStore(getConnector(options));
5659
this.deadline = options.getDeadline();
5760
this.onConnectionEvent = onConnectionEvent;
5861
this.operator = new Operator();
5962
this.connectedSupplier = connectedSupplier;
60-
this.metadata = options.getSelector() == null
61-
? null
62-
: ImmutableMetadata.builder()
63-
.addString("scope", options.getSelector())
64-
.build();
63+
if (options.getSelector() == null) {
64+
this.scope = null;
65+
this.fallBackMetadata = null;
66+
} else {
67+
this.scope = options.getSelector();
68+
this.fallBackMetadata = ImmutableMetadata.builder()
69+
.addString("scope", this.scope)
70+
.build();
71+
}
6572
}
6673

6774
/** Initialize in-process resolver. */
@@ -109,8 +116,14 @@ public void shutdown() throws InterruptedException {
109116
onConnectionEvent.accept(new ConnectionEvent(false));
110117
}
111118

112-
/** Resolve a boolean flag. */
113-
public ProviderEvaluation<Boolean> booleanEvaluation(String key, Boolean defaultValue, EvaluationContext ctx) {
119+
/**
120+
* Resolve a boolean flag.
121+
*/
122+
public ProviderEvaluation<Boolean> booleanEvaluation(
123+
String key,
124+
Boolean defaultValue,
125+
EvaluationContext ctx
126+
) {
114127
return resolve(Boolean.class, key, ctx);
115128
}
116129

@@ -161,6 +174,7 @@ private <T> ProviderEvaluation<T> resolve(Class<T> type, String key, EvaluationC
161174
return ProviderEvaluation.<T>builder()
162175
.errorMessage("flag: " + key + " not found")
163176
.errorCode(ErrorCode.FLAG_NOT_FOUND)
177+
.flagMetadata(fallBackMetadata)
164178
.build();
165179
}
166180

@@ -169,6 +183,7 @@ private <T> ProviderEvaluation<T> resolve(Class<T> type, String key, EvaluationC
169183
return ProviderEvaluation.<T>builder()
170184
.errorMessage("flag: " + key + " is disabled")
171185
.errorCode(ErrorCode.FLAG_NOT_FOUND)
186+
.flagMetadata(getFlagMetadata(flag))
172187
.build();
173188
}
174189

@@ -215,13 +230,51 @@ private <T> ProviderEvaluation<T> resolve(Class<T> type, String key, EvaluationC
215230
throw new TypeMismatchError(message);
216231
}
217232

218-
final ProviderEvaluation.ProviderEvaluationBuilder<T> evaluationBuilder = ProviderEvaluation.<T>builder()
233+
return ProviderEvaluation.<T>builder()
219234
.value((T) value)
220235
.variant(resolvedVariant)
221-
.reason(reason);
236+
.reason(reason)
237+
.flagMetadata(getFlagMetadata(flag))
238+
.build();
239+
}
240+
241+
private ImmutableMetadata getFlagMetadata(FeatureFlag flag) {
242+
if (flag == null) {
243+
return fallBackMetadata;
244+
}
245+
246+
ImmutableMetadata.ImmutableMetadataBuilder metadataBuilder = ImmutableMetadata.builder();
247+
if (scope != null) {
248+
metadataBuilder.addString("scope", scope);
249+
}
250+
251+
for (Map.Entry<String, Object> entry : flag.getMetadata().entrySet()) {
252+
Object value = entry.getValue();
253+
if (value instanceof Number) {
254+
if (value instanceof Long) {
255+
metadataBuilder.addLong(entry.getKey(), (Long) value);
256+
continue;
257+
} else if (value instanceof Double) {
258+
metadataBuilder.addDouble(entry.getKey(), (Double) value);
259+
continue;
260+
} else if (value instanceof Integer) {
261+
metadataBuilder.addInteger(entry.getKey(), (Integer) value);
262+
continue;
263+
} else if (value instanceof Float) {
264+
metadataBuilder.addFloat(entry.getKey(), (Float) value);
265+
continue;
266+
}
267+
} else if (value instanceof Boolean) {
268+
metadataBuilder.addBoolean(entry.getKey(), (Boolean) value);
269+
continue;
270+
} else if (value instanceof String) {
271+
metadataBuilder.addString(entry.getKey(), (String) value);
272+
continue;
273+
}
274+
throw new IllegalArgumentException("The type of the Metadata entry with key " + entry.getKey()
275+
+ " and value " + entry.getValue() + " is not supported");
276+
}
222277

223-
return this.metadata == null
224-
? evaluationBuilder.build()
225-
: evaluationBuilder.flagMetadata(this.metadata).build();
278+
return metadataBuilder.build();
226279
}
227280
}

Diff for: providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/model/FeatureFlag.java

+8-5
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,21 @@ public class FeatureFlag {
2323
private final String defaultVariant;
2424
private final Map<String, Object> variants;
2525
private final String targeting;
26+
private final Map<String, Object> metadata;
2627

2728
/** Construct a flagd feature flag. */
2829
@JsonCreator
29-
public FeatureFlag(
30-
@JsonProperty("state") String state,
31-
@JsonProperty("defaultVariant") String defaultVariant,
32-
@JsonProperty("variants") Map<String, Object> variants,
33-
@JsonProperty("targeting") @JsonDeserialize(using = StringSerializer.class) String targeting) {
30+
public FeatureFlag(@JsonProperty("state") String state,
31+
@JsonProperty("defaultVariant") String defaultVariant,
32+
@JsonProperty("variants") Map<String, Object> variants,
33+
@JsonProperty("targeting") @JsonDeserialize(using = StringSerializer.class) String targeting,
34+
@JsonProperty("metadata") Map<String, Object> metadata
35+
) {
3436
this.state = state;
3537
this.defaultVariant = defaultVariant;
3638
this.variants = variants;
3739
this.targeting = targeting;
40+
this.metadata = metadata;
3841
}
3942

4043
/** Get targeting rule of the flag. */

0 commit comments

Comments
 (0)