From d07e4e40e51ce215f86b3b601e28fe254c50c345 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Tue, 16 Aug 2022 16:30:12 -0400 Subject: [PATCH] feat: add flagd provider implementation --- checkstyle-suppressions.xml | 8 + checkstyle.xml | 399 +++++++++--------- eclipse-java-google-style.xml.bak | 337 +++++++++++++++ .../contrib/hooks/otel/OpenTelemetryHook.java | 15 +- pom.xml | 27 +- providers/flagd/pom.xml | 55 +++ .../providers/flagd/FlagdProvider.java | 186 +++++++- .../contrib/providers/flagd/Service.java | 9 + providers/flagd/src/main/proto/schema.proto | 97 +++++ .../providers/flagd/FlagdProviderTest.java | 129 +++++- .../org.mockito.plugins.MockMaker | 1 + spotbugs-exclusions.xml | 21 +- 12 files changed, 1041 insertions(+), 243 deletions(-) create mode 100644 checkstyle-suppressions.xml create mode 100644 eclipse-java-google-style.xml.bak create mode 100644 providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/Service.java create mode 100644 providers/flagd/src/main/proto/schema.proto create mode 100644 providers/flagd/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker diff --git a/checkstyle-suppressions.xml b/checkstyle-suppressions.xml new file mode 100644 index 000000000..3d374f555 --- /dev/null +++ b/checkstyle-suppressions.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/checkstyle.xml b/checkstyle.xml index 9e524577a..f03d82985 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -1,7 +1,5 @@ - + - - + + - + - + + + - + + - - + - + - - - + + + - + - - - + + + - - - + + + - + + - + - - + + - + - + OBJBLOCK, STATIC_INIT, RECORD_DEF, COMPACT_CTOR_DEF" /> - - + + - - - + + + + + + + + + + + - - - - - - + + + + + + - - - - - - - - - - + LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS, MINUS_ASSIGN, MOD, MOD_ASSIGN, + NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION, RCURLY, SL, SLIST, SL_ASSIGN, SR, + SR_ASSIGN, STAR, STAR_ASSIGN, LITERAL_ASSERT, TYPE_EXTENSION_AND" /> + + + + + + + + + + - - + + - - - + + + - - - + + + - - - - + + + + - - - - + + + + - - - + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + - - + + + + + + + + + + - - + + - - + + - + - - - - + + + + - - - - - - + + + + + + - - - - - - - + + + + + + + + - + - - + + - + METHOD_DEF, QUESTION, RESOURCE_SPECIFICATION, SUPER_CTOR_CALL, LAMBDA, + RECORD_DEF" /> - - + + - - - + + - - - + + + - - - + + + - + - + + - - + + - - - - - + + + + + - - - - + + + + + + + + + - - - - - + + + - + - + - - + + + + + + + + + + + + + + + - - + \ No newline at end of file diff --git a/eclipse-java-google-style.xml.bak b/eclipse-java-google-style.xml.bak new file mode 100644 index 000000000..2380bc2c8 --- /dev/null +++ b/eclipse-java-google-style.xml.bak @@ -0,0 +1,337 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/OpenTelemetryHook.java b/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/OpenTelemetryHook.java index 24100d424..8cd8cf4ec 100644 --- a/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/OpenTelemetryHook.java +++ b/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/OpenTelemetryHook.java @@ -4,20 +4,21 @@ import dev.openfeature.javasdk.NoOpProvider; import dev.openfeature.javasdk.OpenFeatureAPI; -/** +/** * A placeholder. */ public class OpenTelemetryHook { - - /** + + /** * Create a new OpenTelemetryHook instance. */ - public OpenTelemetryHook() { + private OpenTelemetryHook() { } - /** - * A test method... - * @return {boolean} + /** + * A test. + * + * @return boolean */ public static boolean test() { OpenFeatureAPI.getInstance().setProvider(new NoOpProvider()); diff --git a/pom.xml b/pom.xml index 0d5e545b2..b9c172d98 100644 --- a/pom.xml +++ b/pom.xml @@ -33,6 +33,14 @@ 0.0.3 + + + org.projectlombok + lombok + 1.18.24 + provided + + org.mockito @@ -121,13 +129,13 @@ com.puppycrawl.tools checkstyle - 8.31 + 10.3.2 validate - validate + verify check @@ -138,7 +146,9 @@ org.apache.maven.plugins maven-pmd-plugin - 3.13.0 + + ${basedir}/target/generated-sources/ + run-pmd @@ -223,21 +233,24 @@ 3.4.0 true + dev.openfeature.flagd.grpc - vet-javadoc - validate + attach-javadocs + compile jar - attach-javadocs + vet-javadoc + verify jar + @@ -250,7 +263,7 @@ sign-artifacts - verify + install sign diff --git a/providers/flagd/pom.xml b/providers/flagd/pom.xml index 136d80795..0698c3ecd 100644 --- a/providers/flagd/pom.xml +++ b/providers/flagd/pom.xml @@ -31,6 +31,61 @@ + + + io.grpc + grpc-netty-shaded + 1.48.1 + runtime + + + io.grpc + grpc-protobuf + 1.48.1 + + + io.grpc + grpc-stub + 1.48.1 + + + + org.apache.tomcat + annotations-api + 6.0.53 + provided + + + + + kr.motd.maven + os-maven-plugin + 1.6.2 + + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + 0.6.1 + + com.google.protobuf:protoc:3.21.1:exe:${os.detected.classifier} + grpc-java + io.grpc:protoc-gen-grpc-java:1.48.1:exe:${os.detected.classifier} + + + + + compile + compile-custom + + + + + + + \ No newline at end of file diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdProvider.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdProvider.java index 8c81a3cc2..53e307a86 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdProvider.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdProvider.java @@ -1,29 +1,185 @@ package dev.openfeature.contrib.providers.flagd; -import dev.openfeature.javasdk.Client; -import dev.openfeature.javasdk.NoOpProvider; -import dev.openfeature.javasdk.OpenFeatureAPI; +import java.util.HashMap; +import java.util.Map; -/** - * A placeholder. +import org.apache.commons.lang3.EnumUtils; +import org.apache.commons.lang3.NotImplementedException; + +import com.google.protobuf.Struct; +import com.google.protobuf.Value; + +import dev.openfeature.javasdk.EvaluationContext; +import dev.openfeature.javasdk.FeatureProvider; +import dev.openfeature.javasdk.FlagEvaluationOptions; +import dev.openfeature.javasdk.Metadata; +import dev.openfeature.javasdk.ProviderEvaluation; +import dev.openfeature.javasdk.Reason; +import io.grpc.ManagedChannelBuilder; +import lombok.extern.slf4j.Slf4j; +import dev.openfeature.flagd.grpc.Schema.ResolveBooleanRequest; +import dev.openfeature.flagd.grpc.Schema.ResolveBooleanResponse; +import dev.openfeature.flagd.grpc.Schema.ResolveFloatRequest; +import dev.openfeature.flagd.grpc.Schema.ResolveFloatResponse; +import dev.openfeature.flagd.grpc.Schema.ResolveIntRequest; +import dev.openfeature.flagd.grpc.Schema.ResolveIntResponse; +import dev.openfeature.flagd.grpc.Schema.ResolveStringRequest; +import dev.openfeature.flagd.grpc.Schema.ResolveStringResponse; +import dev.openfeature.flagd.grpc.ServiceGrpc; +import dev.openfeature.flagd.grpc.ServiceGrpc.ServiceBlockingStub; + +/** + * OpenFeature provider for flagd. */ -public class FlagdProvider { +@Slf4j +public class FlagdProvider implements FeatureProvider { + + private ServiceBlockingStub serviceStub; + static final String PROVIDER_NAME = "flagD Provider"; - /** + /** + * Create a new FlagdProvider instance. + * + * @param protocol transport protocol, "http" or "https" + * @param host flagd host, defaults to localhost + * @param port flagd port, defaults to 8013 + */ + public FlagdProvider(String protocol, String host, int port) { + + this("https".equalsIgnoreCase(protocol) + ? ServiceGrpc.newBlockingStub(ManagedChannelBuilder.forAddress(host, port) + .useTransportSecurity() + .build()) : + ServiceGrpc.newBlockingStub(ManagedChannelBuilder.forAddress(host, port) + .usePlaintext() + .build())); + } + + /** * Create a new FlagdProvider instance. */ public FlagdProvider() { + this("http", "localhost", 8013); } - /** - * A test method... - * - * @return {boolean} + /** + * Create a new FlagdProvider instance. + * + * @param serviceStub service stub instance to use */ - public static boolean test() { - OpenFeatureAPI.getInstance().setProvider(new NoOpProvider()); - Client client = OpenFeatureAPI.getInstance().getClient(); - return client.getBooleanValue("test", true); + public FlagdProvider(ServiceBlockingStub serviceStub) { + this.serviceStub = serviceStub; + } + + @Override + public Metadata getMetadata() { + return new Metadata() { + @Override + public String getName() { + return PROVIDER_NAME; + } + }; + } + + @Override + public ProviderEvaluation getBooleanEvaluation(String key, Boolean defaultValue, + EvaluationContext ctx, FlagEvaluationOptions options) { + + ResolveBooleanRequest request = ResolveBooleanRequest.newBuilder() + .setFlagKey(key) + .setContext(this.convertContext(ctx)) + .build(); + ResolveBooleanResponse r = this.serviceStub.resolveBoolean(request); + return ProviderEvaluation.builder() + .value(r.getValue()) + .variant(r.getVariant()) + .reason(this.mapReason(r.getReason())) + .build(); + } + + @Override + public ProviderEvaluation getStringEvaluation(String key, String defaultValue, + EvaluationContext ctx, FlagEvaluationOptions options) { + ResolveStringRequest request = ResolveStringRequest.newBuilder() + .setFlagKey(key) + .setContext(this.convertContext(ctx)).build(); + ResolveStringResponse r = this.serviceStub.resolveString(request); + return ProviderEvaluation.builder().value(r.getValue()) + .variant(r.getVariant()) + .reason(this.mapReason(r.getReason())) + .build(); + } + + @Override + public ProviderEvaluation getDoubleEvaluation(String key, Double defaultValue, + EvaluationContext ctx, FlagEvaluationOptions options) { + ResolveFloatRequest request = ResolveFloatRequest.newBuilder() + .setFlagKey(key) + .setContext(this.convertContext(ctx)) + .build(); + ResolveFloatResponse r = this.serviceStub.resolveFloat(request); + return ProviderEvaluation.builder() + .value(r.getValue()) + .variant(r.getVariant()) + .reason(this.mapReason(r.getReason())) + .build(); + } + + @Override + public ProviderEvaluation getIntegerEvaluation(String key, Integer defaultValue, + EvaluationContext ctx, FlagEvaluationOptions options) { + ResolveIntRequest request = ResolveIntRequest.newBuilder() + .setFlagKey(key) + .setContext(this.convertContext(ctx)) + .build(); + ResolveIntResponse r = this.serviceStub.resolveInt(request); + return ProviderEvaluation.builder() + .value((int) r.getValue()) + .variant(r.getVariant()) + .reason(this.mapReason(r.getReason())) + .build(); + } + + @Override + public ProviderEvaluation getObjectEvaluation(String key, T defaultValue, + EvaluationContext ctx, FlagEvaluationOptions options) { + throw new NotImplementedException(); + } + + // Map FlagD reasons to Java SDK reasons. + private Reason mapReason(String flagDreason) { + if (!EnumUtils.isValidEnum(Reason.class, flagDreason)) { + // until we have "STATIC" in the spec and SDK, we map STATIC to DEFAULT + if ("STATIC".equals(flagDreason)) { + return Reason.DEFAULT; + } else { + return Reason.UNKNOWN; + } + } else { + return Reason.valueOf(flagDreason); + } + } + + private Struct convertContext(EvaluationContext ctx) { + // TODO: structure attributes? + Map booleanAttributes = ctx.getBooleanAttributes(); + Map stringAttributes = ctx.getStringAttributes(); + Map intAttributes = ctx.getIntegerAttributes(); + Map values = new HashMap<>(); + booleanAttributes.keySet().stream().forEach((String key) -> values.put(key, Value + .newBuilder() + .setBoolValue(booleanAttributes.get(key)) + .build())); + stringAttributes.keySet().stream().forEach((String key) -> values.put(key, + Value.newBuilder().setStringValue(stringAttributes + .get(key)) + .build())); + intAttributes.keySet().stream().forEach((String key) -> values.put(key, Value + .newBuilder().setNumberValue(intAttributes.get(key)) + .build())); + return Struct.newBuilder() + .putAllFields(values) + .build(); } } diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/Service.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/Service.java new file mode 100644 index 000000000..6efe5357d --- /dev/null +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/Service.java @@ -0,0 +1,9 @@ +package dev.openfeature.contrib.providers.flagd; + +/** + * The commication style to yus. + */ +enum Service { + HTTP, + GRPC +} diff --git a/providers/flagd/src/main/proto/schema.proto b/providers/flagd/src/main/proto/schema.proto new file mode 100644 index 000000000..fe460aed3 --- /dev/null +++ b/providers/flagd/src/main/proto/schema.proto @@ -0,0 +1,97 @@ +syntax = "proto3"; + +package schema.v1; + +option go_package = "schema/service/v1"; +option java_package = "dev.openfeature.flagd.grpc"; + +import "google/protobuf/struct.proto"; +import "google/api/annotations.proto"; + +message ErrorResponse { + string error_code = 1; + string reason = 2; +} + +message ResolveBooleanRequest { + string flag_key = 1; + google.protobuf.Struct context = 2; +} +message ResolveBooleanResponse { + bool value = 1; + string reason = 2; + string variant = 3; +} + +message ResolveStringRequest { + string flag_key = 1; + google.protobuf.Struct context = 2; +} +message ResolveStringResponse { + string value = 1; + string reason = 2; + string variant = 3; +} + +message ResolveFloatRequest { + string flag_key = 1; + google.protobuf.Struct context = 2; +} +message ResolveFloatResponse { + double value = 1; + string reason = 2; + string variant = 3; +} + +message ResolveIntRequest { + string flag_key = 1; + google.protobuf.Struct context = 2; +} +message ResolveIntResponse { + int64 value = 1; + string reason = 2; + string variant = 3; +} + +message ResolveObjectRequest { + string flag_key = 1; + google.protobuf.Struct context = 2; +} +message ResolveObjectResponse { + google.protobuf.Struct value = 1; + string reason = 2; + string variant = 3; +} + +service Service { + rpc ResolveBoolean(ResolveBooleanRequest) returns (ResolveBooleanResponse) { + option (google.api.http) = { + post: "/flags/{flag_key}/resolve/boolean" + body: "context" + }; + } + rpc ResolveString(ResolveStringRequest) returns (ResolveStringResponse) { + option (google.api.http) = { + post: "/flags/{flag_key}/resolve/string" + body: "context" + }; + } + rpc ResolveFloat(ResolveFloatRequest) returns (ResolveFloatResponse) { + option (google.api.http) = { + post: "/flags/{flag_key}/resolve/float" + body: "context" + }; + } + rpc ResolveInt(ResolveIntRequest) returns (ResolveIntResponse) { + option (google.api.http) = { + post: "/flags/{flag_key}/resolve/int" + body: "context" + }; + } + rpc ResolveObject(ResolveObjectRequest) returns (ResolveObjectResponse) { + option (google.api.http) = { + post: "/flags/{flag_key}/resolve/object" + body: "context" + }; + } +} \ No newline at end of file diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdProviderTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdProviderTest.java index f7f45fa17..114a40986 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdProviderTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdProviderTest.java @@ -1,15 +1,132 @@ package dev.openfeature.contrib.providers.flagd; -import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; -import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import dev.openfeature.flagd.grpc.Schema.ResolveBooleanResponse; +import dev.openfeature.flagd.grpc.Schema.ResolveFloatResponse; +import dev.openfeature.flagd.grpc.Schema.ResolveIntResponse; +import dev.openfeature.flagd.grpc.Schema.ResolveStringResponse; +import dev.openfeature.flagd.grpc.ServiceGrpc.ServiceBlockingStub; +import dev.openfeature.javasdk.EvaluationContext; +import dev.openfeature.javasdk.FlagEvaluationDetails; +import dev.openfeature.javasdk.OpenFeatureAPI; +import dev.openfeature.javasdk.Reason; + class FlagdProviderTest { + static final String FLAG_KEY = "some-key"; + static final String BOOL_VARIANT = "on"; + static final String DOUBLE_VARIANT = "half"; + static final String INT_VARIANT = "one-hundred"; + static final String STRING_VARIANT = "greeting"; + static final Reason DEFAULT = Reason.DEFAULT; + static final Integer INT_VALUE = 100; + static final Double DOUBLE_VALUE = .5d; + static final String STRING_VALUE = "hi!"; + + static OpenFeatureAPI api; + + @BeforeAll + public static void init() { + api = OpenFeatureAPI.getInstance(); + } + @Test - @DisplayName("a simple test") - void test() { - assertThat(FlagdProvider.test()).isEqualTo(true); + void resolvers_call_grpc_service_and_return_details() { + ResolveBooleanResponse booleanResponse = ResolveBooleanResponse.newBuilder() + .setValue(true) + .setVariant(BOOL_VARIANT) + .setReason(DEFAULT.toString()) + .build(); + + ResolveStringResponse stringResponse = ResolveStringResponse.newBuilder() + .setValue(STRING_VALUE) + .setVariant(STRING_VARIANT) + .setReason(DEFAULT.toString()) + .build(); + + ResolveIntResponse intResponse = ResolveIntResponse.newBuilder() + .setValue(INT_VALUE) + .setVariant(INT_VARIANT) + .setReason(DEFAULT.toString()) + .build(); + + ResolveFloatResponse floatResponse = ResolveFloatResponse.newBuilder() + .setValue(DOUBLE_VALUE) + .setVariant(DOUBLE_VARIANT) + .setReason(DEFAULT.toString()) + .build(); + + ServiceBlockingStub serviceBlockingStubMock = mock(ServiceBlockingStub.class); + when(serviceBlockingStubMock.resolveBoolean(argThat(x -> FLAG_KEY.equals(x.getFlagKey())))).thenReturn(booleanResponse); + when(serviceBlockingStubMock.resolveFloat(argThat(x -> FLAG_KEY.equals(x.getFlagKey())))).thenReturn(floatResponse); + when(serviceBlockingStubMock.resolveInt(argThat(x -> FLAG_KEY.equals(x.getFlagKey())))).thenReturn(intResponse); + when(serviceBlockingStubMock.resolveString(argThat(x -> FLAG_KEY.equals(x.getFlagKey())))).thenReturn(stringResponse); + + OpenFeatureAPI.getInstance().setProvider(new FlagdProvider(serviceBlockingStubMock)); + + FlagEvaluationDetails booleanDetails = api.getClient().getBooleanDetails(FLAG_KEY, false); + assertTrue(booleanDetails.getValue()); + assertEquals(BOOL_VARIANT, booleanDetails.getVariant()); + assertEquals(DEFAULT, booleanDetails.getReason()); + + FlagEvaluationDetails stringDetails = api.getClient().getStringDetails(FLAG_KEY, "wrong"); + assertEquals(STRING_VALUE, stringDetails.getValue()); + assertEquals(STRING_VARIANT, stringDetails.getVariant()); + assertEquals(DEFAULT, stringDetails.getReason()); + + FlagEvaluationDetails intDetails = api.getClient().getIntegerDetails(FLAG_KEY, 0); + assertEquals(INT_VALUE, intDetails.getValue()); + assertEquals(INT_VARIANT, intDetails.getVariant()); + assertEquals(DEFAULT, intDetails.getReason()); + + FlagEvaluationDetails floatDetails = api.getClient().getDoubleDetails(FLAG_KEY, 0.1); + assertEquals(DOUBLE_VALUE, floatDetails.getValue()); + assertEquals(DOUBLE_VARIANT, floatDetails.getVariant()); + assertEquals(DEFAULT, floatDetails.getReason()); + } + + @Test + void context_is_passed_to_grpc_service() { + + final String BOOLEAN_ATTR_KEY = "bool-attr"; + final String NUM_ATTR_KEY = "int-attr"; + final String STRING_ATTR_KEY = "string-attr"; + + final Boolean BOOLEAN_ATTR_VALUE = true; + final Integer NUM_ATTR_VALUE = 1; + final String STRING_ATTR_VALUE = "str"; + + ResolveBooleanResponse booleanResponse = ResolveBooleanResponse.newBuilder() + .setValue(true) + .setVariant(BOOL_VARIANT) + .setReason(DEFAULT.toString()) + .build(); + + ServiceBlockingStub serviceBlockingStubMock = mock(ServiceBlockingStub.class); + when(serviceBlockingStubMock.resolveBoolean(argThat(x -> + STRING_ATTR_VALUE.equals(x.getContext().getFieldsMap().get(STRING_ATTR_KEY).getStringValue()) && + NUM_ATTR_VALUE == x.getContext().getFieldsMap().get(NUM_ATTR_KEY).getNumberValue() && + x.getContext().getFieldsMap().get(BOOLEAN_ATTR_KEY).getBoolValue() + ))).thenReturn(booleanResponse); + + OpenFeatureAPI.getInstance().setProvider(new FlagdProvider(serviceBlockingStubMock)); + + EvaluationContext context = new EvaluationContext(); + context.addBooleanAttribute(BOOLEAN_ATTR_KEY, BOOLEAN_ATTR_VALUE); + context.addIntegerAttribute(NUM_ATTR_KEY, NUM_ATTR_VALUE); + context.addStringAttribute(STRING_ATTR_KEY, STRING_ATTR_VALUE); + + FlagEvaluationDetails booleanDetails = api.getClient().getBooleanDetails(FLAG_KEY, false, context); + assertTrue(booleanDetails.getValue()); + assertEquals(BOOL_VARIANT, booleanDetails.getVariant()); + assertEquals(DEFAULT, booleanDetails.getReason()); } -} +} \ No newline at end of file diff --git a/providers/flagd/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/providers/flagd/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 000000000..ca6ee9cea --- /dev/null +++ b/providers/flagd/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1 @@ +mock-maker-inline \ No newline at end of file diff --git a/spotbugs-exclusions.xml b/spotbugs-exclusions.xml index 59e92ca80..0aad72fd8 100644 --- a/spotbugs-exclusions.xml +++ b/spotbugs-exclusions.xml @@ -1,24 +1,11 @@ - + - - - - - - - - - - - - + - + +