Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
78 changes: 53 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
[![Maven Central](https://maven-badges.sml.io/sonatype-central/io.github.sideshowcoder/dropwizard-openfeature/badge.svg?version=2.0.0)](https://maven-badges.sml.io/sonatype-central/io.github.sideshowcoder/dropwizard-openfeature) ![Maven Test & Build](https://github.com/sideshowcoder/dropwizard-openfeature/actions/workflows/maven-build.yml/badge.svg)
[![Maven Central](https://maven-badges.sml.io/sonatype-central/io.github.sideshowcoder/dropwizard-openfeature/badge.svg?version=2.1.0)](https://maven-badges.sml.io/sonatype-central/io.github.sideshowcoder/dropwizard-openfeature) ![Maven Test & Build](https://github.com/sideshowcoder/dropwizard-openfeature/actions/workflows/maven-build.yml/badge.svg)

# Dropwizard Openfeature

This plugin integrates [openfeature][1] with dropwizard and allows you to use openfeature feature
flags, provided by supported openfeature providers via a managed `OpenFeatureAPI` instance.
This plugin integrates [openfeature][1] with dropwizard and allows you to use
openfeature feature flags, provided by supported openfeature providers via a
managed `OpenFeatureAPI` instance.

Currently only [flagd][2] and the SDKs [InMemoryProvider][3] providers are supported
It implements support for
- [InMemoryProvider][3] useful for testing
- [flagd][2]
- [GO Feature Flag][6]
- [OpenFeature Remote Evaluation Protocol (OFREP)][8] to support any providers implementing it

Currently only [flagd][2] and the SDKs [InMemoryProvider][3] providers are
supported

## Installing the bundle

Expand All @@ -17,7 +25,7 @@ Currently only [flagd][2] and the SDKs [InMemoryProvider][3] providers are suppo
<dependency>
<groupId>io.github.sideshowcoder</groupId>
<artifactId>dropwizard-openfeature</artifactId>
<version>2.0.0</version>
<version>2.1.0</version>
</dependency>
```

Expand All @@ -35,38 +43,49 @@ After installing the plugin locally you can include it in your `pom.xml`
<dependency>
<groupId>io.github.sideshowcoder</groupId>
<artifactId>dropwizard-openfeature</artifactId>
<version>2.0.1-SNAPSHOT</version>
<version>2.1.1-SNAPSHOT</version>
</dependency>
```



## Included in the bundle

### Supported providers

The bundle currently supports both the SDK included `InMemoryProvider` as well as `flagd`, the provider can be selected
via the configuration. For details on the configuration options see `FlagdConfiguration` as well the
[flagd documentation][5].
Currently the following providers, implemented in the
[open-feature/java-sdk-contrib][11] are supported, and their respective most
used configuration options are mapped to yaml configuration. Addtional
configuration can be set by accessing the underlying provider as needed.

- [InMemoryProvider][3]
- [flagd][9]
- [GO Feature Flag][10]
- [OpenFeature Remote Evaluation Protocol (OFREP)][7] to support any providers
implementing it

For the configuration see the respective configuration classes.

### OpenFeatureAPI management

The initialized `OpenFeatureAPI` is managed via the dropwizard lifecycle and will be shutdown gracefully upon
application shutdown, see `OpenFeatureAPIManager`.
The initialized `OpenFeatureAPI` is managed via the dropwizard lifecycle and
will be shutdown gracefully upon application shutdown, see
`OpenFeatureAPIManager`.

### Healthcheck

By default the bundle registers a healthcheck on the state of the provider configured, this healthcheck can be further
configured via the `OpenFeatureHealthCheckConfiguration`.
By default the bundle registers a healthcheck on the state of the provider
configured, this healthcheck can be further configured via the
`OpenFeatureHealthCheckConfiguration`.

## Activating the bundle

Your Dropwizard application configuration class must implement `OpenFeatureBundleConfiguration`

### Configuring dropwizard-openfeature in the dropwizard config file

For a full overview see `OpenFeatureConfiguration`, `OpenFeatureHealthCheckConfiguration`, and `FlagdConfiguration` a
minimal configuration for flagd runnining locally on the port 8013 would look as follows.
For a full overview see `OpenFeatureConfiguration`,
`OpenFeatureHealthCheckConfiguration`, and the respective provider specific
configuration. A minimal configuration for flagd runnining locally on the port
8013 would look as follows.

```yaml
openfeature:
Expand All @@ -76,8 +95,8 @@ openfeature:
port: 8013
```

For the bundle to have access to the configuration, your application configuration needs to implement
`OpenFeatureBundleConfiguration`.
For the bundle to have access to the configuration, your application
configuration needs to implement `OpenFeatureBundleConfiguration`.

```java
public class Config extends Configuration implements OpenFeatureBundleConfiguration {
Expand All @@ -96,7 +115,8 @@ public class Config extends Configuration implements OpenFeatureBundleConfigurat

### Initialization

In your application's `initialize` method, call `bootstrap.addBundle(new OpenFeatureBundle())`:
In your application's `initialize` method, call `bootstrap.addBundle(new
OpenFeatureBundle())`:

```java
public class App extends Application<Config> {
Expand All @@ -115,9 +135,10 @@ public class App extends Application<Config> {

### Using the client

OpenFeature configures a global `OpenFeatureAPI` which grants access to a client, which can be injected as needed, it is
common practise to provide a domain as an identifier, this is however not required, unless multiple clients are to be
created.
OpenFeature configures a global `OpenFeatureAPI` which grants access to a
client, which can be injected as needed, it is common practise to provide a
domain as an identifier, this is however not required, unless multiple clients
are to be created.

```java
public class App extends Application<Config> {
Expand All @@ -144,8 +165,9 @@ public class App extends Application<Config> {

### Accessing the underlying feature provider

The bundle exposes access to the underlying feature provider. Useful for runtime configuration and introspection of the
provider. For example when using the `InMemoryProvider` flags can be updated at runtime for example for testing.
The bundle exposes access to the underlying feature provider. Useful for runtime
configuration and introspection of the provider. For example when using the
`InMemoryProvider` flags can be updated at runtime for example for testing.

```java
public class App extends Application<Config> {
Expand Down Expand Up @@ -177,3 +199,9 @@ public class App extends Application<Config> {
[3]: https://github.com/open-feature/java-sdk/blob/main/src/main/java/dev/openfeature/sdk/providers/memory/InMemoryProvider.java
[4]: https://central.sonatype.com/artifact/io.github.sideshowcoder/dropwizard-openfeature
[5]: https://flagd.dev/providers/java/
[6]: https://gofeatureflag.org
[7]: https://github.com/open-feature/java-sdk-contrib/tree/main/providers/ofrep
[8]: https://openfeature.dev/docs/reference/other-technologies/ofrep/
[9]: https://github.com/open-feature/java-sdk-contrib/tree/main/providers/flagd
[10]: https://github.com/open-feature/java-sdk-contrib/tree/main/providers/go-feature-flag
[11]: https://github.com/open-feature/java-sdk-contrib
18 changes: 18 additions & 0 deletions compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
services:
go-feature-flag:
image: gofeatureflag/go-feature-flag:latest
ports:
- 1031:1031 # relay proxy port
volumes:
# relay proxy config
- ./src/test/resources/goff-proxy.yaml:/goff/goff-proxy.yaml
# flags to be served
- ./src/test/resources/go-feature-flags-flags.yaml:/goff/flags.yaml
flagd:
image: ghcr.io/open-feature/flagd:latest
command: start --uri file:/etc/flagd/flagd-test-flags.json
ports:
- 8013:8013 # standard port
- 8016:8016 # ofrep port
volumes:
- ./src/test/resources:/etc/flagd
36 changes: 36 additions & 0 deletions flag-provider-requests.restclient
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
### flagd ###
POST http://localhost:8013/flagd.evaluation.v1.Service/ResolveString
Content-Type: application/json

{
"flagKey": "static-string-flag",
"context": {}
}

### ofrep ###

# flagd bulk
POST http://localhost:8016/ofrep/v1/evaluate/flags

# flagd flag
POST http://localhost:8016/ofrep/v1/evaluate/flags/static-string-flag

# go-feature-flag
POST http://localhost:1031/ofrep/v1/evaluate/flags/staticstringflag
Content-Type: application/json

{
"context": {
"targetingKey": "key"
}
}

# go-feature-flag bulk
POST http://localhost:1031/ofrep/v1/evaluate/flags
Content-Type: application/json

{
"context": {
"targetingKey": "key"
}
}
9 changes: 8 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>io.github.sideshowcoder</groupId>
<artifactId>dropwizard-openfeature</artifactId>
<version>2.0.1-SNAPSHOT</version>
<version>2.1.0</version>
<packaging>jar</packaging>

<name>dropwizard-openfeature</name>
Expand All @@ -23,6 +23,7 @@
<openfeature-sdk.version>1.20.1</openfeature-sdk.version>
<openfeature-contrib-providers-flagd.version>0.11.20</openfeature-contrib-providers-flagd.version>
<openfeature-contrib-providers-go-feature-flag.version>1.1.1</openfeature-contrib-providers-go-feature-flag.version>
<openfeature-contrib-providers-ofrep.version>0.0.1</openfeature-contrib-providers-ofrep.version>
<jacoco-maven-plugin.version>0.8.14</jacoco-maven-plugin.version>
<maven-compiler-plugin.version>3.15.0</maven-compiler-plugin.version>
<maven-gpg-plugin.version>3.2.8</maven-gpg-plugin.version>
Expand Down Expand Up @@ -116,6 +117,7 @@
<artifactId>sdk</artifactId>
<version>${openfeature-sdk.version}</version>
</dependency>

<dependency>
<groupId>dev.openfeature.contrib.providers</groupId>
<artifactId>flagd</artifactId>
Expand All @@ -126,6 +128,11 @@
<artifactId>go-feature-flag</artifactId>
<version>${openfeature-contrib-providers-go-feature-flag.version}</version>
</dependency>
<dependency>
<groupId>dev.openfeature.contrib.providers</groupId>
<artifactId>ofrep</artifactId>
<version>${openfeature-contrib-providers-ofrep.version}</version>
</dependency>

<dependency>
<groupId>io.dropwizard</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@
import dev.openfeature.contrib.providers.gofeatureflag.exception.InvalidOptions;
import jakarta.validation.constraints.NotNull;

/**
* Maps the {@link GoFeatureFlagConfiguration} confiuration in yaml to the actual {@link GoFeatureFlagProviderOptions }.
*
* Supported options
*
* Name | Type | example | required | description
* endpoit | String | http://localhost:8016 | yes | endpoint to reach the GoFeatureFlag relay proxy
*/
public class GoFeatureFlagConfiguration {

@JsonProperty
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package io.github.sideshowcoder.dropwizard_openfeature;

import java.time.Duration;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import com.fasterxml.jackson.annotation.JsonProperty;

import dev.openfeature.contrib.providers.ofrep.OfrepProviderOptions;
import jakarta.validation.constraints.NotNull;

/**
* Maps the ofrep confiuration in yaml to the actual {@link OfrepProviderOptions}.
*
* Supported options
*
* Name | Type | example | required | description
* baseurl | String | http://localhost:8016 | yes | baseurl for the ofrep protocol to use
* timeout | long | 500 | no | request timeout in millis
* threadcount | int | 5 | no | thread pool size for the http client
*/
public class OfrepConfiguration {

@JsonProperty
@NotNull
private String baseurl;

@JsonProperty
private long timeout;

@JsonProperty
private int threadcount;

public OfrepProviderOptions getOfrepProviderOptions() {
OfrepProviderOptions.Builder builder = OfrepProviderOptions.builder().baseUrl(baseurl);

if (timeout != 0) {
Duration requestTimeout = Duration.ofMillis(timeout);
builder.requestTimeout(requestTimeout);
}

if (threadcount != 0) {
Executor executor = Executors.newFixedThreadPool(threadcount);
builder.executor(executor);
}

return builder.build();
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import dev.openfeature.contrib.providers.flagd.FlagdProvider;
import dev.openfeature.contrib.providers.gofeatureflag.GoFeatureFlagProvider;
import dev.openfeature.contrib.providers.gofeatureflag.GoFeatureFlagProviderOptions;
import dev.openfeature.contrib.providers.gofeatureflag.exception.InvalidOptions;
import dev.openfeature.contrib.providers.ofrep.OfrepProvider;
import dev.openfeature.sdk.Client;
import dev.openfeature.sdk.FeatureProvider;
import dev.openfeature.sdk.OpenFeatureAPI;
Expand Down Expand Up @@ -54,6 +54,9 @@ private synchronized void initializeFeatureProvider(OpenFeatureConfiguration con
new RuntimeException(e);
}
break;
case OFREP:
featureProvider = OfrepProvider.constructProvider(config.getOfrep().getOfrepProviderOptions());
break;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ public class OpenFeatureConfiguration {
@JsonProperty
private GoFeatureFlagConfiguration gofeatureflag = new GoFeatureFlagConfiguration();

@Valid
@JsonProperty
private OfrepConfiguration ofrep = new OfrepConfiguration();

@Valid
@JsonProperty
private OpenFeatureHealthCheckConfiguration healthcheck = new OpenFeatureHealthCheckConfiguration();
Expand All @@ -40,6 +44,14 @@ public void setGoFeatureFlag(GoFeatureFlagConfiguration gofeatureflag) {
this.gofeatureflag = gofeatureflag;
}

public OfrepConfiguration getOfrep() {
return ofrep;
}

public void setOfrep(OfrepConfiguration ofrep) {
this.ofrep = ofrep;
}

public OpenFeatureHealthCheckConfiguration getHealthcheck() {
return healthcheck;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
public enum ProviderType {
FLAGD,
GOFEATUREFLAG,
OFREP,
INMEMORY;
}
Loading