diff --git a/pom.xml b/pom.xml index 96ac6dd..3e2d365 100644 --- a/pom.xml +++ b/pom.xml @@ -15,13 +15,14 @@ OpenFeature bundle for Dropwizard - - 8 + + 11 UTF-8 UTF-8 5.0.0 1.18.2 0.11.17 + 1.0.1 0.8.14 3.14.1 3.2.8 @@ -31,6 +32,7 @@ 3.5.4 3.5.4 5.20.0 + 1.21.3 0.9.0 @@ -75,8 +77,15 @@ mockito-bom ${mockito.version} pom - test + import + + org.testcontainers + testcontainers-bom + ${testcontainers.version} + pom + import + @@ -112,6 +121,12 @@ flagd ${openfeature-contrib-providers-flagd.version} + + dev.openfeature.contrib.providers + go-feature-flag + ${openfeature-contrib-providers-go-feature-flag.version} + + io.dropwizard dropwizard-testing @@ -127,6 +142,21 @@ mockito-junit-jupiter test + + org.junit.jupiter + junit-jupiter + test + + + org.testcontainers + junit-jupiter + test + + + org.testcontainers + testcontainers + test + @@ -165,8 +195,7 @@ maven-compiler-plugin ${maven-compiler-plugin.version} - ${java.version} - ${java.version} + ${maven.compiler.release} diff --git a/src/main/java/io/github/sideshowcoder/dropwizard_openfeature/GoFeatureFlagConfiguration.java b/src/main/java/io/github/sideshowcoder/dropwizard_openfeature/GoFeatureFlagConfiguration.java new file mode 100644 index 0000000..864b8c2 --- /dev/null +++ b/src/main/java/io/github/sideshowcoder/dropwizard_openfeature/GoFeatureFlagConfiguration.java @@ -0,0 +1,26 @@ +package io.github.sideshowcoder.dropwizard_openfeature; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import dev.openfeature.contrib.providers.gofeatureflag.GoFeatureFlagProviderOptions; +import dev.openfeature.contrib.providers.gofeatureflag.GoFeatureFlagProviderOptions.GoFeatureFlagProviderOptionsBuilder; +import dev.openfeature.contrib.providers.gofeatureflag.exception.InvalidOptions; +import jakarta.validation.constraints.NotNull; + +public class GoFeatureFlagConfiguration { + + @JsonProperty + @NotNull + private String endpoint; + + public GoFeatureFlagProviderOptions getGoFeatureFlagProviderOptions() throws InvalidOptions { + GoFeatureFlagProviderOptionsBuilder builder = GoFeatureFlagProviderOptions.builder(); + + builder.endpoint(endpoint); + + GoFeatureFlagProviderOptions options = builder.build(); + options.validate(); + return options; + } + +} diff --git a/src/main/java/io/github/sideshowcoder/dropwizard_openfeature/OpenFeatureBundle.java b/src/main/java/io/github/sideshowcoder/dropwizard_openfeature/OpenFeatureBundle.java index 6177a93..9aa31d9 100644 --- a/src/main/java/io/github/sideshowcoder/dropwizard_openfeature/OpenFeatureBundle.java +++ b/src/main/java/io/github/sideshowcoder/dropwizard_openfeature/OpenFeatureBundle.java @@ -1,6 +1,9 @@ package io.github.sideshowcoder.dropwizard_openfeature; 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.sdk.Client; import dev.openfeature.sdk.FeatureProvider; import dev.openfeature.sdk.OpenFeatureAPI; @@ -44,6 +47,13 @@ private synchronized void initializeFeatureProvider(OpenFeatureConfiguration con case FLAGD: featureProvider = new FlagdProvider(config.getFlagd().getFlagdOptions()); break; + case GOFEATUREFLAG: + try { + featureProvider = new GoFeatureFlagProvider(config.getGoFeatureFlag().getGoFeatureFlagProviderOptions()); + } catch(InvalidOptions e) { + new RuntimeException(e); + } + break; } } } diff --git a/src/main/java/io/github/sideshowcoder/dropwizard_openfeature/OpenFeatureConfiguration.java b/src/main/java/io/github/sideshowcoder/dropwizard_openfeature/OpenFeatureConfiguration.java index 1ec8b69..6a4d48d 100644 --- a/src/main/java/io/github/sideshowcoder/dropwizard_openfeature/OpenFeatureConfiguration.java +++ b/src/main/java/io/github/sideshowcoder/dropwizard_openfeature/OpenFeatureConfiguration.java @@ -11,6 +11,10 @@ public class OpenFeatureConfiguration { @JsonProperty private FlagdConfiguration flagd = new FlagdConfiguration(); + @Valid + @JsonProperty + private GoFeatureFlagConfiguration gofeatureflag = new GoFeatureFlagConfiguration(); + @Valid @JsonProperty private OpenFeatureHealthCheckConfiguration healthcheck = new OpenFeatureHealthCheckConfiguration(); @@ -28,6 +32,14 @@ public void setFlagd(FlagdConfiguration flagd) { this.flagd = flagd; } + public GoFeatureFlagConfiguration getGoFeatureFlag() { + return gofeatureflag; + } + + public void setGoFeatureFlag(GoFeatureFlagConfiguration gofeatureflag) { + this.gofeatureflag = gofeatureflag; + } + public OpenFeatureHealthCheckConfiguration getHealthcheck() { return healthcheck; } diff --git a/src/main/java/io/github/sideshowcoder/dropwizard_openfeature/ProviderType.java b/src/main/java/io/github/sideshowcoder/dropwizard_openfeature/ProviderType.java index 3e46a69..7f846df 100644 --- a/src/main/java/io/github/sideshowcoder/dropwizard_openfeature/ProviderType.java +++ b/src/main/java/io/github/sideshowcoder/dropwizard_openfeature/ProviderType.java @@ -2,5 +2,6 @@ public enum ProviderType { FLAGD, + GOFEATUREFLAG, INMEMORY; } diff --git a/src/test/java/io/github/sideshowcoder/dropwizard_openfeature/OpenFeatureBundleGoFeatureFlagProviderTest.java b/src/test/java/io/github/sideshowcoder/dropwizard_openfeature/OpenFeatureBundleGoFeatureFlagProviderTest.java new file mode 100644 index 0000000..8a5d281 --- /dev/null +++ b/src/test/java/io/github/sideshowcoder/dropwizard_openfeature/OpenFeatureBundleGoFeatureFlagProviderTest.java @@ -0,0 +1,65 @@ +package io.github.sideshowcoder.dropwizard_openfeature; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import com.codahale.metrics.health.HealthCheck; + +import dev.openfeature.sdk.Client; +import dev.openfeature.sdk.EvaluationContext; +import dev.openfeature.sdk.ImmutableContext; +import dev.openfeature.sdk.OpenFeatureAPI; +import io.dropwizard.testing.ResourceHelpers; +import io.dropwizard.testing.junit5.DropwizardAppExtension; +import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; +import io.github.sideshowcoder.dropwizard_openfeature.helpers.App; +import io.github.sideshowcoder.dropwizard_openfeature.helpers.Config; + +@Testcontainers +@ExtendWith(DropwizardExtensionsSupport.class) +public class OpenFeatureBundleGoFeatureFlagProviderTest { + + @Container + private static GenericContainer goFeatureFlagRelay = createGoFeatureFlagContainer(); + + private static GenericContainer createGoFeatureFlagContainer() { + GenericContainer container = new GenericContainer<>(DockerImageName.parse("gofeatureflag/go-feature-flag:latest")) + .withCopyFileToContainer(MountableFile.forClasspathResource("goff-proxy.yaml"), "/goff/goff-proxy.yaml") + .withCopyFileToContainer(MountableFile.forClasspathResource("go-feature-flags-flags.yaml"), "/goff/flags.yaml"); + + container.setPortBindings(List.of("1031:1031")); + container.waitingFor(Wait.forHttp("/health")); + + return container; + } + + private static final DropwizardAppExtension APP = new DropwizardAppExtension<>( + App.class, + ResourceHelpers.resourceFilePath("go-feature-flag-provider-config.yml") + ); + + @Test + public void initializesHealthCheck() throws Exception { + HealthCheck.Result healthcheckResult = APP.getEnvironment().healthChecks().runHealthCheck("openfeature-health-check"); + assertTrue(healthcheckResult.isHealthy()); + } + + @Test + public void providesFeatureFlagsViaInMemoryProvider() throws Exception { + Client client = OpenFeatureAPI.getInstance().getClient("go-feature-flag-client"); + // GoFeatureFlags requires a target key for all queries! + EvaluationContext ctx = new ImmutableContext("target"); + assertEquals("red", client.getStringValue("staticstringflag", "not-expected-value", ctx)); + } +} diff --git a/src/test/resources/basic-flagd-config.yml b/src/test/resources/basic-flagd-config.yml index 04995ee..e4403d4 100644 --- a/src/test/resources/basic-flagd-config.yml +++ b/src/test/resources/basic-flagd-config.yml @@ -1,4 +1,4 @@ host: flagd port: 8082 resolver: rpc -cacheType: disabled \ No newline at end of file +cacheType: disabled diff --git a/src/test/resources/go-feature-flag-provider-config.yml b/src/test/resources/go-feature-flag-provider-config.yml new file mode 100644 index 0000000..b51c2ce --- /dev/null +++ b/src/test/resources/go-feature-flag-provider-config.yml @@ -0,0 +1,4 @@ +openfeature: + provider: gofeatureflag + gofeatureflag: + endpoint: "http://localhost:1031/" diff --git a/src/test/resources/go-feature-flags-flags.yaml b/src/test/resources/go-feature-flags-flags.yaml new file mode 100644 index 0000000..538b374 --- /dev/null +++ b/src/test/resources/go-feature-flags-flags.yaml @@ -0,0 +1,7 @@ +staticstringflag: + variations: + red: "red" + green: "green" + blue: "blue" + defaultRule: + variation: red diff --git a/src/test/resources/goff-proxy.yaml b/src/test/resources/goff-proxy.yaml new file mode 100644 index 0000000..4a373a3 --- /dev/null +++ b/src/test/resources/goff-proxy.yaml @@ -0,0 +1,3 @@ +retrievers: + - kind: file + path: /goff/flags.yaml