Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
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
2 changes: 2 additions & 0 deletions .github/component_owners.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ components:
- thomaspoignant
providers/jsonlogic-eval-provider:
- justinabrahms
providers/unleash:
- liran2000

ignored-authors:
- renovate-bot
3 changes: 2 additions & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
"providers/go-feature-flag": "0.2.11",
"providers/flagsmith": "0.0.8",
"providers/env-var": "0.0.4",
"providers/jsonlogic-eval-provider": "1.0.0"
"providers/jsonlogic-eval-provider": "1.0.0",
"providers/unleash": "0.0.1"
}
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<module>providers/go-feature-flag</module>
<module>providers/jsonlogic-eval-provider</module>
<module>providers/env-var</module>
<module>providers/unleash</module>
</modules>

<scm>
Expand Down
Empty file.
54 changes: 54 additions & 0 deletions providers/unleash/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Unofficial Unleash OpenFeature Provider for Java

Unleash OpenFeature Provider can provide usage for Unleash via OpenFeature Java SDK.

## Installation

<!-- x-release-please-start-version -->

```xml

<dependency>
<groupId>dev.openfeature.contrib.providers</groupId>
<artifactId>unleash</artifactId>
<version>0.0.1</version>
</dependency>
```

<!-- x-release-please-end-version -->

## Concepts
* Boolean evaluation gets feature enabled status.
* String evaluation gets feature variant value.

## Usage
Unleash OpenFeature Provider is using Unleash Java SDK.

### Usage Example

```
FeatureProvider featureProvider = new UnleashProvider(unleashOptions);
OpenFeatureAPI.getInstance().setProviderAndWait(unleashProvider);
boolean featureEnabled = client.getBooleanValue(FLAG_NAME, false);

UnleashContext unleashContext = UnleashContext.builder().userId("1").build();
EvaluationContext evaluationContext = UnleashProvider.transform(unleashContext);
featureEnabled = client.getBooleanValue(FLAG_NAME, false, evaluationContext);

String variantValue = client.getStringValue(FLAG_NAME, "");
```

See [UnleashProviderTest.java](./src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java) for more information.

## Caveats / Limitations

* json/csv payloads are evaluated via object evaluation as what returned from Unleash - string, wrapped with Value.
* integer/double evaluation currently not supported.

## Unleash Provider Tests Strategies

Unit test based on Unleash instance with Unleash features schema file, with WireMock for API mocking.
See [UnleashProviderTest.java](./src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java) for more information.

## References
* [Unleash](https://getunleash.io)
5 changes: 5 additions & 0 deletions providers/unleash/lombok.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# This file is needed to avoid errors throw by findbugs when working with lombok.
lombok.addSuppressWarnings = true
lombok.addLombokGeneratedAnnotation = true
config.stopBubbling = true
lombok.extern.findbugs.addSuppressFBWarnings = true
47 changes: 47 additions & 0 deletions providers/unleash/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.openfeature.contrib</groupId>
<artifactId>parent</artifactId>
<version>0.1.0</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<groupId>dev.openfeature.contrib.providers</groupId>
<artifactId>unleash</artifactId>
<version>0.0.1</version> <!--x-release-please-version -->

<name>unleash</name>
<description>unleash provider for Java</description>
<url>https://www.getunleash.io/</url>

<dependencies>
<dependency>
<groupId>io.getunleash</groupId>
<artifactId>unleash-client-java</artifactId>
<version>8.3.1</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.9</version>
</dependency>

<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8</artifactId>
<version>2.35.1</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.20.0</version>
<scope>test</scope>
</dependency>

</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package dev.openfeature.contrib.providers.unleash;

import dev.openfeature.sdk.EvaluationContext;
import dev.openfeature.sdk.ImmutableContext;
import dev.openfeature.sdk.Value;
import io.getunleash.UnleashContext;

import java.time.ZonedDateTime;
import java.util.HashMap;
import java.util.Map;

/**
* Transformer from Unleash context to OpenFeature context and vice versa.
*/
public class ContextTransformer {

public static final String CONTEXT_APP_NAME = "appName";
public static final String CONTEXT_USER_ID = "userId";
public static final String CONTEXT_ENVIRONMENT = "environment";
public static final String CONTEXT_REMOTE_ADDRESS = "remoteAddress";
public static final String CONTEXT_SESSION_ID = "sessionId";
public static final String CONTEXT_CURRENT_TIME = "currentTime";

protected static UnleashContext transform(EvaluationContext ctx) {
UnleashContext.Builder unleashContextBuilder = new UnleashContext.Builder();
ctx.asObjectMap().forEach((k, v) -> {
switch (k) {
case CONTEXT_APP_NAME:
unleashContextBuilder.appName(String.valueOf(v));
break;
case CONTEXT_USER_ID:
unleashContextBuilder.userId(String.valueOf(v));
break;
case CONTEXT_ENVIRONMENT:
unleashContextBuilder.environment(String.valueOf(v));
break;
case CONTEXT_REMOTE_ADDRESS:
unleashContextBuilder.remoteAddress(String.valueOf(v));
break;
case CONTEXT_SESSION_ID:
unleashContextBuilder.sessionId(String.valueOf(v));
break;
case CONTEXT_CURRENT_TIME:
unleashContextBuilder.currentTime(ZonedDateTime.parse(String.valueOf(v)));
break;
default:
unleashContextBuilder.addProperty(k, String.valueOf(v));
break;
}
});
return unleashContextBuilder.build();
}

/**
* Transform UnleashContext to EvaluationContext.
* @param unleashContext the UnleashContext
* @return transformed EvaluationContext
*/
public static EvaluationContext transform(UnleashContext unleashContext) {
Map<String, Value> attributes = new HashMap<>();
unleashContext.getAppName().ifPresent(o -> attributes.put(CONTEXT_APP_NAME, Value.objectToValue(o)));
unleashContext.getUserId().ifPresent(o -> attributes.put(CONTEXT_USER_ID, Value.objectToValue(o)));
unleashContext.getEnvironment().ifPresent(o -> attributes.put(CONTEXT_ENVIRONMENT, Value.objectToValue(o)));
unleashContext.getSessionId().ifPresent(o -> attributes.put(CONTEXT_SESSION_ID, Value.objectToValue(o)));
unleashContext.getRemoteAddress().ifPresent(o -> attributes.put(
CONTEXT_REMOTE_ADDRESS, Value.objectToValue(o)));
unleashContext.getCurrentTime().ifPresent(
o -> attributes.put(CONTEXT_CURRENT_TIME, Value.objectToValue(o.toString())));

unleashContext.getProperties().forEach((k, v) -> {
attributes.put(k, Value.objectToValue(v));
});
return new ImmutableContext(attributes);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package dev.openfeature.contrib.providers.unleash;

import io.getunleash.util.UnleashConfig;
import lombok.Builder;
import lombok.Getter;


/**
* Options for initializing Unleash provider.
*/
@Getter
@Builder
public class UnleashOptions {
private UnleashConfig.Builder unleashConfigBuilder;
}
Loading