diff --git a/src/main/java/dev/openfeature/javasdk/Structure.java b/src/main/java/dev/openfeature/javasdk/Structure.java index 05a66552..062750fb 100644 --- a/src/main/java/dev/openfeature/javasdk/Structure.java +++ b/src/main/java/dev/openfeature/javasdk/Structure.java @@ -41,6 +41,11 @@ public class Structure { } // adders + public Structure add(String key, Value value) { + attributes.put(key, value); + return this; + } + public Structure add(String key, Boolean value) { attributes.put(key, new Value(value)); return this; diff --git a/src/main/java/dev/openfeature/javasdk/Value.java b/src/main/java/dev/openfeature/javasdk/Value.java index 786ee295..426d4a1f 100644 --- a/src/main/java/dev/openfeature/javasdk/Value.java +++ b/src/main/java/dev/openfeature/javasdk/Value.java @@ -7,9 +7,9 @@ import lombok.EqualsAndHashCode; import lombok.ToString; /** - * Values server as a return type for provider objects. Providers may deal in protobufs or JSON in the backend and - * have no reasonable way to convert that into a type that users care about (e.g. an instance of `T`). This - * intermediate representation should provide a good medium of exchange. + * Values serve as a return type for provider objects. + * Providers may deal in JSON, protobuf, XML or some other data-interchange format. + * This intermediate representation provides a good medium of exchange. */ @ToString @EqualsAndHashCode @@ -18,6 +18,10 @@ public class Value { private final Object innerObject; + public Value() { + this.innerObject = null; + } + public Value(Value value) { this.innerObject = value.innerObject; } @@ -31,7 +35,7 @@ public class Value { } public Value(Integer value) { - this.innerObject = value; + this.innerObject = value.doubleValue(); } public Value(Double value) { @@ -50,6 +54,15 @@ public class Value { this.innerObject = value; } + /** + * Check if this Value represents null. + * + * @return boolean + */ + public boolean isNull() { + return this.innerObject == null; + } + /** * Check if this Value represents a Boolean. * @@ -69,20 +82,11 @@ public class Value { } /** - * Check if this Value represents an Integer. + * Check if this Value represents a numeric value. * * @return boolean */ - public boolean isInteger() { - return this.innerObject instanceof Integer; - } - - /** - * Check if this Value represents a Double. - * - * @return boolean - */ - public boolean isDouble() { + public boolean isNumber() { return this.innerObject instanceof Double; } @@ -140,24 +144,25 @@ public class Value { } /** - * Retrieve the underlying Integer value, or null. + * Retrieve the underlying numeric value as an Integer, or null. + * If the value is not an integer, it will be rounded using Math.round(). * * @return Integer */ public Integer asInteger() { - if (this.isInteger()) { - return (Integer)this.innerObject; + if (this.isNumber()) { + return (int)Math.round((Double)this.innerObject); } return null; } /** - * Retrieve the underlying Double value, or null. + * Retrieve the underlying numeric value as a Double, or null. * * @return Double */ public Double asDouble() { - if (this.isDouble()) { + if (this.isNumber()) { return (Double)this.innerObject; } return null; diff --git a/src/test/java/dev/openfeature/javasdk/StructureTest.java b/src/test/java/dev/openfeature/javasdk/StructureTest.java new file mode 100644 index 00000000..0232bc79 --- /dev/null +++ b/src/test/java/dev/openfeature/javasdk/StructureTest.java @@ -0,0 +1,71 @@ +package dev.openfeature.javasdk; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +public class StructureTest { + @Test public void noArgShouldContainEmptyAttributes() { + Structure structure = new Structure(); + assertEquals(0, structure.asMap().keySet().size()); + } + + @Test public void mapArgShouldContainNewMap() { + String KEY = "key"; + Map map = new HashMap() { + { + put(KEY, new Value(KEY)); + } + }; + Structure structure = new Structure(map); + assertEquals(KEY, structure.asMap().get(KEY).asString()); + assertNotSame(structure.asMap(), map); // should be a copy + } + + @Test public void addAndGetAddAndReturnValues() { + String BOOL_KEY = "bool"; + String STRING_KEY = "string"; + String INT_KEY = "int"; + String DOUBLE_KEY = "double"; + String DATE_KEY = "date"; + String STRUCT_KEY = "struct"; + String LIST_KEY = "list"; + String VALUE_KEY = "value"; + + boolean BOOL_VAL = true; + String STRING_VAL = "val"; + int INT_VAL = 13; + double DOUBLE_VAL = .5; + ZonedDateTime DATE_VAL = ZonedDateTime.now(); + Structure STRUCT_VAL = new Structure(); + List LIST_VAL = new ArrayList(); + Value VALUE_VAL = new Value(); + + Structure structure = new Structure(); + structure.add(BOOL_KEY, BOOL_VAL); + structure.add(STRING_KEY, STRING_VAL); + structure.add(INT_KEY, INT_VAL); + structure.add(DOUBLE_KEY, DOUBLE_VAL); + structure.add(DATE_KEY, DATE_VAL); + structure.add(STRUCT_KEY, STRUCT_VAL); + structure.add(LIST_KEY, LIST_VAL); + structure.add(VALUE_KEY, VALUE_VAL); + + assertEquals(BOOL_VAL, structure.getValue(BOOL_KEY).asBoolean()); + assertEquals(STRING_VAL, structure.getValue(STRING_KEY).asString()); + assertEquals(INT_VAL, structure.getValue(INT_KEY).asInteger()); + assertEquals(DOUBLE_VAL, structure.getValue(DOUBLE_KEY).asDouble()); + assertEquals(DATE_VAL, structure.getValue(DATE_KEY).asZonedDateTime()); + assertEquals(STRUCT_VAL, structure.getValue(STRUCT_KEY).asStructure()); + assertEquals(LIST_VAL, structure.getValue(LIST_KEY).asList()); + assertTrue(structure.getValue(VALUE_KEY).isNull()); + } +} diff --git a/src/test/java/dev/openfeature/javasdk/ValueTest.java b/src/test/java/dev/openfeature/javasdk/ValueTest.java new file mode 100644 index 00000000..c86feb93 --- /dev/null +++ b/src/test/java/dev/openfeature/javasdk/ValueTest.java @@ -0,0 +1,70 @@ +package dev.openfeature.javasdk; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.Test; + +public class ValueTest { + @Test public void noArgShouldContainNull() { + Value value = new Value(); + assertTrue(value.isNull()); + } + + @Test public void boolArgShouldContainBool() { + boolean innerValue = true; + Value value = new Value(innerValue); + assertTrue(value.isBoolean()); + assertEquals(innerValue, value.asBoolean()); + } + + @Test public void numericArgShouldReturnDoubleOrInt() { + double innerDoubleValue = .75; + Value doubleValue = new Value(innerDoubleValue); + assertTrue(doubleValue.isNumber()); + assertEquals(1, doubleValue.asInteger()); // should be rounded + assertEquals(.75, doubleValue.asDouble()); + + int innerIntValue = 100; + Value intValue = new Value(innerIntValue); + assertTrue(intValue.isNumber()); + assertEquals(innerIntValue, intValue.asInteger()); + assertEquals(innerIntValue, intValue.asDouble()); + } + + @Test public void stringArgShouldContainString() { + String innerValue = "hi!"; + Value value = new Value(innerValue); + assertTrue(value.isString()); + assertEquals(innerValue, value.asString()); + } + + @Test public void dateShouldContainDate() { + ZonedDateTime innerValue = ZonedDateTime.now(); + Value value = new Value(innerValue); + assertTrue(value.isZonedDateTime()); + assertEquals(innerValue, value.asZonedDateTime()); + } + + @Test public void structureShouldContainStructure() { + String INNER_KEY = "key"; + String INNER_VALUE = "val"; + Structure innerValue = new Structure().add(INNER_KEY, INNER_VALUE); + Value value = new Value(innerValue); + assertTrue(value.isStructure()); + assertEquals(INNER_VALUE, value.asStructure().getValue(INNER_KEY).asString()); + } + + @Test public void listArgShouldContainList() { + String ITEM_VALUE = "val"; + List innerValue = new ArrayList(); + innerValue.add(new Value(ITEM_VALUE)); + Value value = new Value(innerValue); + assertTrue(value.isList()); + assertEquals(ITEM_VALUE, value.asList().get(0).asString()); + } +}