Skip to content

Commit

Permalink
feat: proto to Confidence conversion
Browse files Browse the repository at this point in the history
  • Loading branch information
fabriziodemaria committed May 20, 2024
1 parent 15c260a commit 204801c
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 6 deletions.
9 changes: 3 additions & 6 deletions src/main/java/com/spotify/confidence/Confidence.java
Original file line number Diff line number Diff line change
Expand Up @@ -140,13 +140,10 @@ public <T> FlagEvaluation<T> getEvaluation(String key, T defaultValue) {
log.debug(errorMessage);
return new FlagEvaluation<>(defaultValue, "", resolvedFlag.getReason().toString());
} else {
// TODO Convert proto to Confidence directly
final ConfidenceValue confidenceValue =
ConfidenceValue.fromProto(
TypeMapper.from(
getValueForPath(
flagPath.getPath(),
TypeMapper.from(resolvedFlag.getValue(), resolvedFlag.getFlagSchema()))));
getValueForPath(
flagPath.getPath(),
TypeMapper.toConfidence(resolvedFlag.getValue(), resolvedFlag.getFlagSchema()));

// regular resolve was successful
return new FlagEvaluation<>(
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/com/spotify/confidence/ConfidenceValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ public static ConfidenceValue of(boolean value) {
return new BooleanValue(value);
}

public static ConfidenceValue.List of(java.util.List<ConfidenceValue> values) {
return new List(values);
}

public static ConfidenceValue.List ofStrings(java.util.List<String> values) {
return new List(values.stream().map(ConfidenceValue::of).collect(Collectors.toList()));
}
Expand Down Expand Up @@ -447,6 +451,10 @@ static Struct fromProto(com.google.protobuf.Struct struct) {
return new Struct(Maps.transformValues(struct.getFieldsMap(), ConfidenceValue::fromProto));
}

static Struct fromMap(Map<String, ConfidenceValue> map) {
return new Struct(map);
}

public Map<String, ConfidenceValue> asMap() {
return values;
}
Expand Down
34 changes: 34 additions & 0 deletions src/main/java/com/spotify/confidence/SdkUtils.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.spotify.confidence;

import com.spotify.confidence.ConfidenceValue.Struct;
import dev.openfeature.sdk.Structure;
import dev.openfeature.sdk.Value;
import dev.openfeature.sdk.exceptions.GeneralError;
Expand Down Expand Up @@ -94,4 +95,37 @@ static Value getValueForPath(List<String> path, Value fullValue) {

return value;
}

static ConfidenceValue getValueForPath(List<String> path, ConfidenceValue fullValue) {
ConfidenceValue value = fullValue;
for (String fieldName : path) {
final Struct structure = value.asStruct();
if (structure == null) {
// value's inner object actually is no structure
log.warn(
"Illegal attempt to derive field '{}' on non-structure value '{}'", fieldName, value);
throw new TypeMismatchError(
String.format(
"Illegal attempt to derive field '%s' on non-structure value '%s'",
fieldName, value));
}

value = structure.get(fieldName);

if (value == null) {
// we know that null indicates absence of a proper value because intended nulls would be an
// instance of type Value
log.warn(
"Illegal attempt to derive non-existing field '{}' on structure value '{}'",
fieldName,
structure);
throw new TypeMismatchError(
String.format(
"Illegal attempt to derive non-existing field '%s' on structure value '%s'",
fieldName, structure));
}
}

return value;
}
}
79 changes: 79 additions & 0 deletions src/main/java/com/spotify/confidence/TypeMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,85 @@ public static Value from(Struct struct, StructFlagSchema schema) {
return new Value(new MutableStructure(map));
}

private static ConfidenceValue toConfidence(com.google.protobuf.Value value, FlagSchema schema) {
if (schema.getSchemaTypeCase() == SchemaTypeCase.SCHEMATYPE_NOT_SET) {
throw new ParseError("schemaType not set in FlagSchema");
}

final String mismatchPrefix = "Mismatch between schema and value:";
switch (value.getKindCase()) {
case NULL_VALUE:
return ConfidenceValue.NULL_VALUE;
case NUMBER_VALUE:
switch (schema.getSchemaTypeCase()) {
case INT_SCHEMA:
final int intVal = (int) value.getNumberValue();
if (intVal != value.getNumberValue()) {
throw new ParseError(
String.format(
"%s value should be an int, but it is a double/long", mismatchPrefix));
}
return ConfidenceValue.of(intVal);
case DOUBLE_SCHEMA:
return ConfidenceValue.of(value.getNumberValue());
default:
throw new ParseError("Number field must have schema type int or double");
}
case STRING_VALUE:
if (schema.getSchemaTypeCase() != SchemaTypeCase.STRING_SCHEMA) {
throw new ParseError(
String.format(
"%s value is a String, but it should be something else", mismatchPrefix));
}
return ConfidenceValue.of(value.getStringValue());
case BOOL_VALUE:
if (schema.getSchemaTypeCase() != SchemaTypeCase.BOOL_SCHEMA) {
throw new ParseError(
String.format("%s value is a bool, but should be something else", mismatchPrefix));
}
return ConfidenceValue.of(value.getBoolValue());
case STRUCT_VALUE:
if (schema.getSchemaTypeCase() != SchemaTypeCase.STRUCT_SCHEMA) {
throw new ParseError(
String.format("%s value is a struct, but should be something else", mismatchPrefix));
}
return toConfidence(value.getStructValue(), schema.getStructSchema());
case LIST_VALUE:
if (schema.getSchemaTypeCase() != SchemaTypeCase.LIST_SCHEMA) {
throw new ParseError(
String.format("%s value is a list, but should be something else", mismatchPrefix));
}
final List<ConfidenceValue> mappedList =
value.getListValue().getValuesList().stream()
.map(val -> toConfidence(val, schema.getListSchema().getElementSchema()))
.collect(Collectors.toList());
return ConfidenceValue.of(mappedList);
case KIND_NOT_SET:
throw new ParseError("kind not set in com.google.protobuf.Value");
default:
throw new ParseError("Unknown value type");
}
}

public static ConfidenceValue toConfidence(Struct struct, StructFlagSchema schema) {
final Map<String, ConfidenceValue> map =
struct.getFieldsMap().entrySet().stream()
.collect(
Collectors.toMap(
Map.Entry::getKey,
entry -> {
if (schema.getSchemaMap().containsKey(entry.getKey())) {
return toConfidence(
entry.getValue(), schema.getSchemaMap().get(entry.getKey()));
} else {
throw new ParseError(
String.format("Lacking schema for field '%s'", entry.getKey()));
}
}));

return ConfidenceValue.Struct.fromMap(map);
}

public static com.google.protobuf.Value from(Value val) {
if (val.isNumber()) {
return Values.of(val.asDouble());
Expand Down

0 comments on commit 204801c

Please sign in to comment.