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: Update in-process resolver to support flag metadata #1102 #1122

Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
d127cb5
feat: Update in-process resolver to support flag metadata #1102
chrfwow Jan 2, 2025
0bedec3
fixup! feat: Update in-process resolver to support flag metadata #1102
chrfwow Jan 2, 2025
319ee49
Merge branch 'main' into feat/Update-in-process-resolver-to-support-f…
chrfwow Jan 2, 2025
c08e7bc
feat: Update in-process resolver to support flag metadata #1102
chrfwow Jan 2, 2025
4c6393a
Merge remote-tracking branch 'origin/feat/Update-in-process-resolver-…
chrfwow Jan 7, 2025
da9e92b
fixup! feat: Update in-process resolver to support flag metadata #1102
chrfwow Jan 9, 2025
18219c0
fixup! feat: Update in-process resolver to support flag metadata #1102
chrfwow Jan 9, 2025
af0006c
fixup! feat: Update in-process resolver to support flag metadata #1102
chrfwow Jan 9, 2025
f8e69aa
fixup! feat: Update in-process resolver to support flag metadata #1102
chrfwow Jan 9, 2025
72d1fbc
Merge branch 'main' into feat/Update-in-process-resolver-to-support-f…
chrfwow Jan 9, 2025
b495c6b
Merge branch 'main' into feat/Update-in-process-resolver-to-support-f…
toddbaert Jan 9, 2025
65a508c
fixup! feat: Update in-process resolver to support flag metadata #1102
chrfwow Jan 10, 2025
9b49abc
Merge remote-tracking branch 'origin/feat/Update-in-process-resolver-…
chrfwow Jan 10, 2025
a8a2cd6
fixup! feat: Update in-process resolver to support flag metadata #1102
chrfwow Jan 10, 2025
53f570f
Merge branch 'main' into feat/Update-in-process-resolver-to-support-f…
toddbaert Jan 13, 2025
1579eff
fixup! feat: Update in-process resolver to support flag metadata #1102
chrfwow Jan 14, 2025
0551684
fixup! feat: Update in-process resolver to support flag metadata #1102
chrfwow Jan 14, 2025
242b262
fixup: merge conflicts
toddbaert Jan 14, 2025
b0868e0
fixup: more conflicts
toddbaert Jan 14, 2025
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 @@ -24,6 +24,7 @@
import dev.openfeature.sdk.Value;
import dev.openfeature.sdk.exceptions.ParseError;
import dev.openfeature.sdk.exceptions.TypeMismatchError;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Supplier;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -39,8 +40,9 @@ public class InProcessResolver implements Resolver {
private final Consumer<ConnectionEvent> onConnectionEvent;
private final Operator operator;
private final long deadline;
private final ImmutableMetadata metadata;
private final ImmutableMetadata fallBackMetadata;
private final Supplier<Boolean> connectedSupplier;
private final String scope;

/**
* Resolves flag values using
Expand All @@ -62,11 +64,14 @@ public InProcessResolver(
this.onConnectionEvent = onConnectionEvent;
this.operator = new Operator();
this.connectedSupplier = connectedSupplier;
this.metadata = options.getSelector() == null
? null
: ImmutableMetadata.builder()
.addString("scope", options.getSelector())
.build();
if (options.getSelector() == null) {
this.scope = null;
this.fallBackMetadata = null;
} else {
this.scope = options.getSelector();
this.fallBackMetadata =
ImmutableMetadata.builder().addString("scope", this.scope).build();
}
}

/**
Expand Down Expand Up @@ -178,6 +183,7 @@ private <T> ProviderEvaluation<T> resolve(Class<T> type, String key, EvaluationC
return ProviderEvaluation.<T>builder()
.errorMessage("flag: " + key + " not found")
.errorCode(ErrorCode.FLAG_NOT_FOUND)
.flagMetadata(fallBackMetadata)
toddbaert marked this conversation as resolved.
Show resolved Hide resolved
.build();
}

Expand All @@ -186,6 +192,7 @@ private <T> ProviderEvaluation<T> resolve(Class<T> type, String key, EvaluationC
return ProviderEvaluation.<T>builder()
.errorMessage("flag: " + key + " is disabled")
.errorCode(ErrorCode.FLAG_NOT_FOUND)
.flagMetadata(getFlagMetadata(flag))
.build();
}

Expand Down Expand Up @@ -232,13 +239,51 @@ private <T> ProviderEvaluation<T> resolve(Class<T> type, String key, EvaluationC
throw new TypeMismatchError(message);
}

final ProviderEvaluation.ProviderEvaluationBuilder<T> evaluationBuilder = ProviderEvaluation.<T>builder()
return ProviderEvaluation.<T>builder()
.value((T) value)
.variant(resolvedVariant)
.reason(reason);
.reason(reason)
.flagMetadata(getFlagMetadata(flag))
.build();
}

private ImmutableMetadata getFlagMetadata(FeatureFlag flag) {
if (flag == null) {
return fallBackMetadata;
}

ImmutableMetadata.ImmutableMetadataBuilder metadataBuilder = ImmutableMetadata.builder();
toddbaert marked this conversation as resolved.
Show resolved Hide resolved
if (scope != null) {
metadataBuilder.addString("scope", scope);
}

for (Map.Entry<String, Object> entry : flag.getMetadata().entrySet()) {
Object value = entry.getValue();
if (value instanceof Number) {
if (value instanceof Long) {
metadataBuilder.addLong(entry.getKey(), (Long) value);
continue;
} else if (value instanceof Double) {
metadataBuilder.addDouble(entry.getKey(), (Double) value);
continue;
} else if (value instanceof Integer) {
metadataBuilder.addInteger(entry.getKey(), (Integer) value);
continue;
} else if (value instanceof Float) {
metadataBuilder.addFloat(entry.getKey(), (Float) value);
continue;
}
} else if (value instanceof Boolean) {
metadataBuilder.addBoolean(entry.getKey(), (Boolean) value);
continue;
} else if (value instanceof String) {
metadataBuilder.addString(entry.getKey(), (String) value);
continue;
}
throw new IllegalArgumentException("The type of the Metadata entry with key " + entry.getKey()
+ " and value " + entry.getValue() + " is not supported");
}

return this.metadata == null
? evaluationBuilder.build()
: evaluationBuilder.flagMetadata(this.metadata).build();
return metadataBuilder.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.HashMap;
import java.util.Map;
import lombok.EqualsAndHashCode;
import lombok.Getter;
Expand All @@ -23,18 +24,46 @@ public class FeatureFlag {
private final String defaultVariant;
private final Map<String, Object> variants;
private final String targeting;
private final Map<String, Object> metadata;

/** Construct a flagd feature flag. */
@JsonCreator
public FeatureFlag(
@JsonProperty("state") String state,
@JsonProperty("defaultVariant") String defaultVariant,
@JsonProperty("variants") Map<String, Object> variants,
@JsonProperty("targeting") @JsonDeserialize(using = StringSerializer.class) String targeting) {
@JsonProperty("targeting") @JsonDeserialize(using = StringSerializer.class) String targeting,
@JsonProperty("metadata") Map<String, Object> metadata) {
this.state = state;
this.defaultVariant = defaultVariant;
this.variants = variants;
this.targeting = targeting;
if (metadata == null) {
this.metadata = new HashMap<>();
} else {
this.metadata = metadata;
}
}

/** Construct a flagd feature flag. */
public FeatureFlag(String state, String defaultVariant, Map<String, Object> variants, String targeting) {
this.state = state;
this.defaultVariant = defaultVariant;
this.variants = variants;
this.targeting = targeting;
this.metadata = new HashMap<>();
}

/**
* Add global metadata to this FeatureFlag. Keys that already exist in the metadata of this flag are not
* overwritten.
*
* @param metadata The metadata to add to this flag
*/
public void addMetadata(Map<String, Object> metadata) {
for (Map.Entry<String, Object> entry : metadata.entrySet()) {
this.metadata.putIfAbsent(entry.getKey(), entry.getValue());
}
}

/** Get targeting rule of the flag. */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package dev.openfeature.contrib.providers.flagd.resolver.process.model;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.networknt.schema.JsonSchema;
import com.networknt.schema.JsonSchemaFactory;
Expand All @@ -24,6 +26,7 @@
justification = "Feature flag comes as a Json configuration, hence they must be exposed")
public class FlagParser {
private static final String FLAG_KEY = "flags";
private static final String METADATA_KEY = "metadata";
private static final String EVALUATOR_KEY = "$evaluators";
private static final String REPLACER_FORMAT = "\"\\$ref\":(\\s)*\"%s\"";
private static final ObjectMapper MAPPER = new ObjectMapper();
Expand Down Expand Up @@ -73,6 +76,8 @@ public static Map<String, FeatureFlag> parseString(final String configuration, b
try (JsonParser parser = MAPPER.createParser(transposedConfiguration)) {
final TreeNode treeNode = parser.readValueAsTree();
final TreeNode flagNode = treeNode.get(FLAG_KEY);
final TreeNode metadataNode = treeNode.get(METADATA_KEY);
final Map<String, Object> metadata = parseMetadata(metadataNode);

if (flagNode == null) {
throw new IllegalArgumentException("No flag configurations found in the payload");
Expand All @@ -81,13 +86,24 @@ public static Map<String, FeatureFlag> parseString(final String configuration, b
final Iterator<String> it = flagNode.fieldNames();
while (it.hasNext()) {
final String key = it.next();
flagMap.put(key, MAPPER.treeToValue(flagNode.get(key), FeatureFlag.class));
FeatureFlag flag = MAPPER.treeToValue(flagNode.get(key), FeatureFlag.class);
flag.addMetadata(metadata);
flagMap.put(key, flag);
}
}

return flagMap;
}

private static Map<String, Object> parseMetadata(TreeNode metadataNode) throws JsonProcessingException {
if (metadataNode == null) {
return new HashMap<>();
}

TypeReference<Map<String, Object>> typeRef = new TypeReference<Map<String, Object>>() {};
return MAPPER.treeToValue(metadataNode, typeRef);
}

private static String transposeEvaluators(final String configuration) throws IOException {
try (JsonParser parser = MAPPER.createParser(configuration)) {
final Map<String, Pattern> patternMap = new HashMap<>();
Expand Down
Loading
Loading