diff --git a/CHANGES.txt b/CHANGES.txt
index 4a60a24..0cd9583 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,2 +1,4 @@
1.0.0
-- First release. Up to date with spec v0.1.0
+- First release. Up to date with java sdk v0.1.0
+1.1.0
+- Up tp date with spec v0.5.0 and java sdk v0.3.1
diff --git a/README.md b/README.md
index 9d8fa1d..19beb5e 100644
--- a/README.md
+++ b/README.md
@@ -8,11 +8,20 @@ This Provider is designed to allow the use of OpenFeature with Split, the platfo
This SDK is compatible with Java 11 and higher.
## Getting started
+### Add it to your maven build
+```java
+
+ io.split.openfeature
+ split-openfeature-provider
+ 1.1.0
+
+```
+### Configure it
Below is a simple example that describes the instantiation of the Split Provider. Please see the [OpenFeature Documentation](https://docs.openfeature.dev/docs/reference/concepts/evaluation-api) for details on how to use the OpenFeature SDK.
```java
-import dev.openfeature.javasdk.OpenFeatureAPI;
-import io.split.openfeature.SplitProvider
+import dev.openfeature.sdk.OpenFeatureAPI;
+import io.split.openfeature.SplitProvider;
OpenFeatureAPI api = OpenFeatureAPI.getInstance();
api.setProvider(new SplitProvider("YOUR_API_KEY"));
@@ -20,8 +29,8 @@ api.setProvider(new SplitProvider("YOUR_API_KEY"));
If you are more familiar with Split or want access to other initialization options, you can provide a `SplitClient` to the constructor. See the [Split Java SDK Documentation](https://help.split.io/hc/en-us/articles/360020405151-Java-SDK) for more information.
```java
-import dev.openfeature.javasdk.OpenFeatureAPI;
-import io.split.openfeature.SplitProvider
+import dev.openfeature.sdk.OpenFeatureAPI;
+import io.split.openfeature.SplitProvider;
import io.split.client.SplitClient;
import io.split.client.SplitClientConfig;
import io.split.client.SplitFactoryBuilder;
@@ -43,18 +52,18 @@ One important note is that the Split Provider **requires a targeting key** to be
```java
Client client = api.getClient("CLIENT_NAME");
-EvaluationContext context = new EvaluationContext("TARGETING_KEY");
+EvaluationContext context = new MutableContext("TARGETING_KEY");
Boolean boolValue = client.getBooleanValue("boolFlag", false, context);
```
If the same targeting key is used repeatedly, the evaluation context may be set at the client level
```java
-EvaluationContext context = new EvaluationContext("TARGETING_KEY");
+EvaluationContext context = new MutableContext("TARGETING_KEY");
client.setEvaluationContext(context)
```
or at the OpenFeatureAPI level
```java
-EvaluationContext context = new EvaluationContext("TARGETING_KEY");
-OpenFeatureAPI.getInstance().setCtx(context)
+EvaluationContext context = new MutableContext("TARGETING_KEY");
+OpenFeatureAPI.getInstance().setEvaluationContext(context)
````
If the context was set at the client or api level, it is not required to provide it during flag evaluation.
diff --git a/pom.xml b/pom.xml
index 77c5449..66c81c7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
io.split.openfeature
split-openfeature-provider
- 1.0.0
+ 1.1.0
split-openfeature-provider-java
Split OpenFeature Java Provider
@@ -74,9 +74,8 @@
dev.openfeature
- javasdk
-
- 0.1.0
+ sdk
+ 0.3.1
diff --git a/src/main/java/io/split/openfeature/SplitModule.java b/src/main/java/io/split/openfeature/SplitModule.java
index 066caf2..fad7fa8 100644
--- a/src/main/java/io/split/openfeature/SplitModule.java
+++ b/src/main/java/io/split/openfeature/SplitModule.java
@@ -1,6 +1,6 @@
package io.split.openfeature;
-import dev.openfeature.javasdk.exceptions.GeneralError;
+import dev.openfeature.sdk.exceptions.GeneralError;
import io.split.client.SplitClient;
import io.split.client.SplitClientConfig;
import io.split.client.SplitFactory;
diff --git a/src/main/java/io/split/openfeature/SplitProvider.java b/src/main/java/io/split/openfeature/SplitProvider.java
index 1b41cd5..c2d1437 100644
--- a/src/main/java/io/split/openfeature/SplitProvider.java
+++ b/src/main/java/io/split/openfeature/SplitProvider.java
@@ -1,20 +1,20 @@
package io.split.openfeature;
-import dev.openfeature.javasdk.ErrorCode;
-import dev.openfeature.javasdk.EvaluationContext;
-import dev.openfeature.javasdk.FeatureProvider;
-import dev.openfeature.javasdk.Metadata;
-import dev.openfeature.javasdk.ProviderEvaluation;
-import dev.openfeature.javasdk.Reason;
-import dev.openfeature.javasdk.Structure;
-import dev.openfeature.javasdk.Value;
-import dev.openfeature.javasdk.exceptions.GeneralError;
-import dev.openfeature.javasdk.exceptions.OpenFeatureError;
-import dev.openfeature.javasdk.exceptions.ParseError;
+import dev.openfeature.sdk.ErrorCode;
+import dev.openfeature.sdk.EvaluationContext;
+import dev.openfeature.sdk.FeatureProvider;
+import dev.openfeature.sdk.Metadata;
+import dev.openfeature.sdk.MutableStructure;
+import dev.openfeature.sdk.ProviderEvaluation;
+import dev.openfeature.sdk.Reason;
+import dev.openfeature.sdk.Value;
+import dev.openfeature.sdk.exceptions.GeneralError;
+import dev.openfeature.sdk.exceptions.OpenFeatureError;
+import dev.openfeature.sdk.exceptions.ParseError;
+import dev.openfeature.sdk.exceptions.TargetingKeyMissingError;
import io.split.client.SplitClient;
import io.split.openfeature.utils.Serialization;
-
-import java.time.ZonedDateTime;
+import java.time.Instant;
import java.time.format.DateTimeParseException;
import java.util.List;
import java.util.Map;
@@ -48,7 +48,7 @@ public ProviderEvaluation getBooleanEvaluation(String key, Boolean defa
try {
String evaluated = evaluateTreatment(key, evaluationContext);
if (noTreatment(evaluated)) {
- return constructProviderEvaluation(defaultTreatment, evaluated, Reason.DEFAULT, ErrorCode.FLAG_NOT_FOUND.name());
+ return constructProviderEvaluation(defaultTreatment, evaluated, Reason.DEFAULT, ErrorCode.FLAG_NOT_FOUND);
}
// if treatment is "on" or "true" we treat that as true
// if it is "off" or "false" we treat it as false
@@ -59,7 +59,7 @@ public ProviderEvaluation getBooleanEvaluation(String key, Boolean defa
} else if (evaluated.equalsIgnoreCase("false") || evaluated.equals("off")) {
value = false;
} else {
- throw new ParseError(ErrorCode.PARSE_ERROR.name());
+ throw new ParseError();
}
return constructProviderEvaluation(value, evaluated);
} catch (OpenFeatureError e) {
@@ -75,7 +75,7 @@ public ProviderEvaluation getStringEvaluation(String key, String default
try {
String evaluated = evaluateTreatment(key, evaluationContext);
if (noTreatment(evaluated)) {
- return constructProviderEvaluation(defaultTreatment, evaluated, Reason.DEFAULT, ErrorCode.FLAG_NOT_FOUND.name());
+ return constructProviderEvaluation(defaultTreatment, evaluated, Reason.DEFAULT, ErrorCode.FLAG_NOT_FOUND);
}
return constructProviderEvaluation(evaluated, evaluated);
} catch (OpenFeatureError e) {
@@ -90,14 +90,14 @@ public ProviderEvaluation getIntegerEvaluation(String key, Integer defa
try {
String evaluated = evaluateTreatment(key, evaluationContext);
if (noTreatment(evaluated)) {
- return constructProviderEvaluation(defaultTreatment, evaluated, Reason.DEFAULT, ErrorCode.FLAG_NOT_FOUND.name());
+ return constructProviderEvaluation(defaultTreatment, evaluated, Reason.DEFAULT, ErrorCode.FLAG_NOT_FOUND);
}
Integer value = Integer.valueOf(evaluated);
return constructProviderEvaluation(value, evaluated);
} catch (OpenFeatureError e) {
throw e;
} catch (NumberFormatException e) {
- throw new ParseError(ErrorCode.PARSE_ERROR.name());
+ throw new ParseError();
} catch (Exception e) {
throw new GeneralError("Error getting Integer evaluation", e);
}
@@ -108,29 +108,29 @@ public ProviderEvaluation getDoubleEvaluation(String key, Double default
try {
String evaluated = evaluateTreatment(key, evaluationContext);
if (noTreatment(evaluated)) {
- return constructProviderEvaluation(defaultTreatment, evaluated, Reason.DEFAULT, ErrorCode.FLAG_NOT_FOUND.name());
+ return constructProviderEvaluation(defaultTreatment, evaluated, Reason.DEFAULT, ErrorCode.FLAG_NOT_FOUND);
}
Double value = Double.valueOf(evaluated);
return constructProviderEvaluation(value, evaluated);
} catch (OpenFeatureError e) {
throw e;
} catch (NumberFormatException e) {
- throw new ParseError(ErrorCode.PARSE_ERROR.name());
+ throw new ParseError();
} catch (Exception e) {
throw new GeneralError("Error getting Double evaluation", e);
}
}
@Override
- public ProviderEvaluation getObjectEvaluation(String key, Structure defaultTreatment, EvaluationContext evaluationContext) {
+ public ProviderEvaluation getObjectEvaluation(String key, Value defaultTreatment, EvaluationContext evaluationContext) {
try {
String evaluated = evaluateTreatment(key, evaluationContext);
if (noTreatment(evaluated)) {
- return constructProviderEvaluation(defaultTreatment, evaluated, Reason.DEFAULT, ErrorCode.FLAG_NOT_FOUND.name());
+ return constructProviderEvaluation(defaultTreatment, evaluated, Reason.DEFAULT, ErrorCode.FLAG_NOT_FOUND);
}
Map rawMap = Serialization.stringToMap(evaluated);
- Structure structure = mapToStructure(rawMap);
- return constructProviderEvaluation(structure, evaluated);
+ Value value = mapToValue(rawMap);
+ return constructProviderEvaluation(value, evaluated);
} catch (OpenFeatureError e) {
throw e;
} catch (Exception e) {
@@ -139,14 +139,14 @@ public ProviderEvaluation getObjectEvaluation(String key, Structure d
}
public Map transformContext(EvaluationContext context) {
- return getMapFromStructMap(context.asMap());
+ return context.asObjectMap();
}
private String evaluateTreatment(String key, EvaluationContext evaluationContext) {
String id = evaluationContext.getTargetingKey();
if (id == null || id.isEmpty()) {
// targeting key is always required
- throw new GeneralError("TARGETING_KEY_MISSING");
+ throw new TargetingKeyMissingError();
}
Map attributes = transformContext(evaluationContext);
return client.getTreatment(id, key, attributes);
@@ -160,87 +160,47 @@ private ProviderEvaluation constructProviderEvaluation(T value, String va
return constructProviderEvaluation(value, variant, Reason.TARGETING_MATCH, null);
}
- private ProviderEvaluation constructProviderEvaluation(T value, String variant, Reason reason, String errorCode) {
+ private ProviderEvaluation constructProviderEvaluation(T value, String variant, Reason reason, ErrorCode errorCode) {
ProviderEvaluation.ProviderEvaluationBuilder builder = ProviderEvaluation.builder();
return builder
.value(value)
- .reason(reason)
+ .reason(reason.name())
.variant(variant)
.errorCode(errorCode)
.build();
}
- private Map getMapFromStructMap(Map structMap) {
- return structMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> getInnerValue(e.getValue())));
- }
-
- private Structure mapToStructure(Map map) {
- return new Structure(
- map.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> objectToValue(e.getValue()))));
+ /**
+ * Turn map String->Object into a Value.
+ * @param map a Map String->Object, where object is NOT Value or Structure
+ * @return Value representing the map passed in
+ */
+ private Value mapToValue(Map map) {
+ return new Value(
+ new MutableStructure(
+ map.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> objectToValue(e.getValue())))));
}
- private Object getInnerValue(Value value) {
- Object object = value.asBoolean();
- if (object != null) {
- return object;
- }
- object = value.asDouble();
- if (object != null) {
- return object;
- }
- object = value.asInteger();
- if (object != null) {
- return object;
- }
- object = value.asString();
- if (object != null) {
- return object;
- }
- object = value.asZonedDateTime();
- if (object != null) {
- return object;
- }
- object = value.asStructure();
- if (object != null) {
- // must return a map
- return getMapFromStructMap(((Structure) object).asMap());
- }
- object = value.asList();
- if (object != null) {
- // must return a list of inner objects
- List values = (List) object;
- return values.stream().map(this::getInnerValue).collect(Collectors.toList());
- }
- throw new ClassCastException("Could not get inner value from Value object.");
- }
private Value objectToValue(Object object) {
- if (object instanceof Value) {
- return (Value) object;
- } else if (object instanceof String) {
- // try to parse to zoned date time, otherwise use as string
+ if (object instanceof String) {
+ // try to parse as instant, otherwise use as string
try {
- return new Value(ZonedDateTime.parse((String) object));
+ return new Value(Instant.parse((String) object));
} catch (DateTimeParseException e) {
return new Value((String) object);
}
- } else if (object instanceof Boolean) {
- return new Value((Boolean) object);
- } else if (object instanceof Integer) {
- return new Value((Integer) object);
- } else if (object instanceof Double) {
- return new Value((Double) object);
- } else if (object instanceof Structure) {
- return new Value((Structure) object);
} else if (object instanceof List) {
// need to translate each elem in list to a value
return new Value(((List