From 3f109baccf85ea7f6bdd3fbcab1804d280d4665f Mon Sep 17 00:00:00 2001 From: Simon Schrottner Date: Wed, 1 Jan 2025 19:18:01 +0100 Subject: [PATCH 1/4] build: change checkstyle to google code format, plus adding spotless We are regularly fighting with codestyles in pullrequests, which is annoying to the contributors, as well as the maintainers. There are tools out there, like spotless, which can automate those processes. We could define an own codestyle, but in fact there is already one widely used codestyle from google (google java format). There are plugins to help us within intellij and vscode to follow this code style. Hence the overall experience can improve by switching to this defitinion. Signed-off-by: Simon Schrottner --- checkstyle.xml | 308 ++++++++++++++++++++++++++++++++----------------- pom.xml | 99 +++++++++++----- 2 files changed, 278 insertions(+), 129 deletions(-) diff --git a/checkstyle.xml b/checkstyle.xml index 2a7a8d05c..765a8b455 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -1,7 +1,7 @@ + "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN" + "https://checkstyle.org/dtds/configuration_1_3.dtd"> - + + - + - + - + + + + - - + + + + + - - + + - - - - - - - - - - - - - - - - - - + value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/> + value="Consider using special escape sequence instead of octal value or Unicode escaped value."/> + - - + + value="LITERAL_DO, LITERAL_ELSE, LITERAL_FOR, LITERAL_IF, LITERAL_WHILE"/> - + + + value="ANNOTATION_DEF, CLASS_DEF, CTOR_DEF, ENUM_CONSTANT_DEF, ENUM_DEF, + INTERFACE_DEF, LITERAL_CATCH, + LITERAL_DO, LITERAL_ELSE, LITERAL_FOR, LITERAL_IF, + LITERAL_WHILE, METHOD_DEF, + OBJBLOCK, STATIC_INIT, RECORD_DEF, COMPACT_CTOR_DEF"/> + + + value=" LITERAL_DEFAULT"/> + + + + + + value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT, + INSTANCE_INIT, ANNOTATION_DEF, ENUM_DEF, INTERFACE_DEF, RECORD_DEF, + COMPACT_CTOR_DEF"/> + + + + + + + + @@ -119,18 +133,35 @@ + + + value="ASSIGN, BAND, BAND_ASSIGN, BOR, BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR, + BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN, DO_WHILE, EQUAL, GE, GT, LAND, + LCURLY, LE, LITERAL_DO, LITERAL_ELSE, + LITERAL_FOR, LITERAL_IF, + 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"/> + value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks + may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/> + value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/> + + + + + + + + @@ -141,8 +172,9 @@ + value="PACKAGE_DEF, IMPORT, STATIC_IMPORT, CLASS_DEF, INTERFACE_DEF, ENUM_DEF, + STATIC_INIT, INSTANCE_INIT, METHOD_DEF, CTOR_DEF, VARIABLE_DEF, RECORD_DEF, + COMPACT_CTOR_DEF"/> @@ -156,13 +188,13 @@ - + - + @@ -175,22 +207,23 @@ + value="Package name ''{0}'' must match pattern ''{1}''."/> - + + value="Type name ''{0}'' must match pattern ''{1}''."/> + value="Member name ''{0}'' must match pattern ''{1}''."/> + value="Parameter name ''{0}'' must match pattern ''{1}''."/> @@ -200,85 +233,117 @@ + value="Catch parameter name ''{0}'' must match pattern ''{1}''."/> + value="Local variable name ''{0}'' must match pattern ''{1}''."/> + + + + + value="Class type name ''{0}'' must match pattern ''{1}''."/> + + + + + + + + + value="Method type name ''{0}'' must match pattern ''{1}''."/> + value="Interface type name ''{0}'' must match pattern ''{1}''."/> + value="GenericWhitespace ''{0}'' is followed by whitespace."/> + value="GenericWhitespace ''{0}'' is preceded with whitespace."/> + value="GenericWhitespace ''{0}'' should followed by whitespace."/> + value="GenericWhitespace ''{0}'' is not preceded with whitespace."/> - - - + + + - + + + + + + - - - + + + value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, ANNOTATION_DEF, ANNOTATION_FIELD_DEF, + PARAMETER_DEF, VARIABLE_DEF, METHOD_DEF, PATTERN_VARIABLE_DEF, RECORD_DEF, + RECORD_COMPONENT_DEF"/> + + + + + + + + + value="COMMA, SEMI, POST_INC, POST_DEC, DOT, + LABELED_STAT, METHOD_REF"/> + value="ANNOTATION, ANNOTATION_FIELD_DEF, CTOR_DEF, DOT, ENUM_CONSTANT_DEF, + EXPR, LITERAL_DO, LITERAL_FOR, LITERAL_IF, LITERAL_NEW, + LITERAL_WHILE, METHOD_CALL, + METHOD_DEF, QUESTION, RESOURCE_SPECIFICATION, SUPER_CTOR_CALL"/> + value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR, + LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF, + TYPE_EXTENSION_AND "/> - + value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, + RECORD_DEF, COMPACT_CTOR_DEF"/> @@ -290,46 +355,83 @@ + value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/> + + + - + + value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/> - + - - + + - + + + + + - - + + value="Method name ''{0}'' must match pattern ''{1}''."/> - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index 1129f01b3..f8ef6eac4 100644 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 dev.openfeature.contrib @@ -8,17 +9,17 @@ pom java-sdk-contrib - Contrib parent - https://openfeature.dev - - - - toddbaert - Todd Baert - OpenFeature - https://openfeature.dev/ - - + Contrib parent + https://openfeature.dev + + + + toddbaert + Todd Baert + OpenFeature + https://openfeature.dev/ + + Apache License 2.0 @@ -244,7 +245,7 @@ com.puppycrawl.tools checkstyle - 8.45.1 + 9.3 @@ -315,11 +316,11 @@ maven-jar-plugin 3.4.2 - - - ${module-name} - - + + + ${module-name} + + @@ -366,8 +367,11 @@ **/GoFeatureFlagProviderOptions.java - dev.openfeature.flagd.grpc,dev.openfeature.contrib.providers.gofeatureflag.exception,dev.openfeature.contrib.providers.gofeatureflag.bean - all,-missing + + dev.openfeature.flagd.grpc,dev.openfeature.contrib.providers.gofeatureflag.exception,dev.openfeature.contrib.providers.gofeatureflag.bean + + all,-missing + @@ -400,11 +404,11 @@ - package - - - makeBom - + package + + + makeBom + @@ -440,7 +444,48 @@ + + com.diffplug.spotless + spotless-maven-plugin + 2.30.0 + + + + + + + + + .gitattributes + .gitignore + + + + + + true + 4 + + + + + + + + + true + 4 + + + + + + + + + + @@ -489,7 +534,9 @@ ${testExclusions} - @{surefireArgLine} --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED + @{surefireArgLine} --add-opens java.base/java.util=ALL-UNNAMED --add-opens + java.base/java.lang=ALL-UNNAMED + From b820457a5aef180482f6c5afa112786720fd1ea0 Mon Sep 17 00:00:00 2001 From: Simon Schrottner Date: Wed, 1 Jan 2025 20:13:05 +0100 Subject: [PATCH 2/4] fixup: apply changes Signed-off-by: Simon Schrottner --- .editorconfig | 72 ++ checkstyle.xml | 46 +- .../contrib/hooks/otel/MetricHookOptions.java | 10 +- .../contrib/hooks/otel/MetricsHook.java | 39 +- .../contrib/hooks/otel/OTelCommons.java | 4 +- .../contrib/hooks/otel/TracesHook.java | 14 +- .../contrib/hooks/otel/TracesHookOptions.java | 3 +- .../contrib/hooks/otel/MetricsHookTest.java | 63 +- .../contrib/hooks/otel/TracesHookTest.java | 64 +- .../configcat/ConfigCatProvider.java | 55 +- .../configcat/ConfigCatProviderConfig.java | 8 +- .../configcat/ContextTransformer.java | 6 +- .../configcat/ConfigCatProviderTest.java | 165 +++- .../providers/envvar/EnvVarProvider.java | 15 +- .../providers/envvar/EnvironmentGateway.java | 6 +- .../envvar/EnvironmentKeyTransformer.java | 6 +- .../contrib/providers/envvar/OS.java | 4 +- .../providers/envvar/EnvVarProviderTest.java | 119 +-- .../envvar/EnvironmentKeyTransformerTest.java | 93 +- .../contrib/providers/flagd/Config.java | 39 +- .../contrib/providers/flagd/FlagdOptions.java | 137 ++- .../providers/flagd/FlagdProvider.java | 45 +- .../providers/flagd/SyncMetadataHook.java | 13 +- .../providers/flagd/resolver/Resolver.java | 4 +- .../flagd/resolver/common/ChannelBuilder.java | 34 +- .../resolver/common/ConnectionEvent.java | 23 +- .../flagd/resolver/common/Convert.java | 81 +- .../resolver/common/FlagdGrpcInterceptor.java | 14 +- .../common/GenericConfigException.java | 5 +- .../resolver/common/SslConfigException.java | 4 +- .../resolver/common/SupportedScheme.java | 5 +- .../providers/flagd/resolver/common/Util.java | 19 +- .../common/backoff/BackoffService.java | 24 +- .../common/backoff/BackoffStrategies.java | 11 +- .../common/backoff/BackoffStrategy.java | 16 +- .../common/backoff/CombinedBackoff.java | 16 +- .../common/backoff/ConstantTimeBackoff.java | 9 +- .../backoff/ExponentialTimeBackoff.java | 14 +- .../GrpcStreamConnectorBackoffService.java | 20 +- .../backoff/NumberOfRetriesBackoff.java | 8 +- .../common/nameresolvers/EnvoyResolver.java | 20 +- .../nameresolvers/EnvoyResolverProvider.java | 14 +- .../flagd/resolver/grpc/Constants.java | 4 +- .../flagd/resolver/grpc/Convert.java | 6 +- .../resolver/grpc/EventStreamObserver.java | 32 +- .../flagd/resolver/grpc/GrpcConnector.java | 41 +- .../flagd/resolver/grpc/GrpcResolver.java | 85 +- .../flagd/resolver/grpc/cache/Cache.java | 17 +- .../flagd/resolver/grpc/cache/CacheType.java | 4 +- .../grpc/strategy/ResolveFactory.java | 7 +- .../grpc/strategy/ResolveStrategy.java | 9 +- .../grpc/strategy/SimpleResolving.java | 9 +- .../grpc/strategy/TracedResolving.java | 14 +- .../resolver/process/InProcessResolver.java | 76 +- .../resolver/process/model/FeatureFlag.java | 27 +- .../resolver/process/model/FlagParser.java | 40 +- .../process/model/StringSerializer.java | 8 +- .../resolver/process/storage/FlagStore.java | 47 +- .../resolver/process/storage/Storage.java | 5 +- .../process/storage/StorageState.java | 16 +- .../process/storage/StorageStateChange.java | 17 +- .../process/storage/connector/Connector.java | 4 +- .../storage/connector/QueuePayload.java | 4 +- .../storage/connector/QueuePayloadType.java | 4 +- .../storage/connector/file/FileConnector.java | 29 +- .../connector/grpc/GrpcStreamConnector.java | 68 +- .../connector/grpc/GrpcStreamHandler.java | 6 +- .../process/targeting/Fractional.java | 14 +- .../resolver/process/targeting/Operator.java | 23 +- .../resolver/process/targeting/SemVer.java | 6 +- .../process/targeting/StringComp.java | 4 +- .../targeting/TargetingRuleException.java | 8 +- .../providers/flagd/FlagdOptionsTest.java | 33 +- .../providers/flagd/FlagdProviderTest.java | 442 +++++----- .../providers/flagd/SyncMetadataHookTest.java | 24 +- .../providers/flagd/e2e/ContainerConfig.java | 45 +- .../flagd/e2e/RunConfigCucumberTest.java | 14 +- .../e2e/RunFlagdInProcessCucumberTest.java | 18 +- .../RunFlagdInProcessEnvoyCucumberTest.java | 18 +- ...unFlagdInProcessReconnectCucumberTest.java | 19 +- .../e2e/RunFlagdInProcessSSLCucumberTest.java | 24 +- .../flagd/e2e/RunFlagdRpcCucumberTest.java | 18 +- .../e2e/RunFlagdRpcReconnectCucumberTest.java | 20 +- .../flagd/e2e/RunFlagdRpcSSLCucumberTest.java | 19 +- .../e2e/process/core/FlagdInProcessSetup.java | 21 +- .../envoy/FlagdInProcessEnvoySetup.java | 6 +- .../process/FlagdInProcessSetup.java | 31 +- .../e2e/reconnect/steps/StepDefinitions.java | 45 +- .../flagd/e2e/rpc/FlagdRpcSetup.java | 1 - .../e2e/ssl/process/FlagdInProcessSetup.java | 14 +- .../flagd/e2e/ssl/rpc/FlagdRpcSetup.java | 14 +- .../flagd/e2e/steps/StepDefinitions.java | 178 ++-- .../flagd/e2e/steps/config/ConfigSteps.java | 28 +- .../config/EnvironmentVariableUtils.java | 38 +- .../common/backoff/BackoffServiceTest.java | 9 +- .../common/backoff/CombinedBackoffTest.java | 43 +- .../backoff/ConstantTimeBackoffTest.java | 7 +- .../backoff/ExponentialTimeBackoffTest.java | 12 +- .../backoff/NumberOfRetriesBackoffTest.java | 6 +- .../EnvoyResolverProviderTest.java | 18 +- .../nameresolvers/EnvoyResolverTest.java | 2 +- .../grpc/EventStreamObserverTest.java | 20 +- .../resolver/grpc/GrpcConnectorTest.java | 276 +++--- .../flagd/resolver/grpc/cache/CacheTest.java | 15 +- .../grpc/strategy/ResolveFactoryTest.java | 13 +- .../grpc/strategy/TracedResolvingTest.java | 7 +- .../process/InProcessResolverTest.java | 807 +++++++++--------- .../flagd/resolver/process/MockFlags.java | 19 +- .../flagd/resolver/process/MockStorage.java | 7 +- .../flagd/resolver/process/TestUtils.java | 1 - .../process/model/FlagParserTest.java | 14 +- .../process/storage/FlagStoreTest.java | 79 +- .../process/storage/MockConnector.java | 6 +- .../connector/file/FileConnectorTest.java | 26 +- .../grpc/GrpcStreamConnectorTest.java | 101 +-- .../process/targeting/FractionalTest.java | 22 +- .../process/targeting/OperatorTest.java | 205 +++-- .../process/targeting/SemVerTest.java | 25 +- .../process/targeting/StringCompTest.java | 19 +- .../FlagsmithClientConfigurer.java | 49 +- .../FlagsmithProvider.java | 87 +- .../FlagsmithProviderOptions.java | 142 +-- .../FlagsmithProviderException.java | 5 +- .../InvalidCacheOptionsException.java | 1 - .../exceptions/InvalidOptionsException.java | 4 +- .../FlagsmithProviderTest.java | 259 +++--- .../providers/flipt/ContextTransformer.java | 5 +- .../providers/flipt/FliptProvider.java | 50 +- .../providers/flipt/FliptProviderConfig.java | 4 +- .../providers/flipt/FliptProviderTest.java | 88 +- .../gofeatureflag/GoFeatureFlagProvider.java | 97 +-- .../GoFeatureFlagProviderOptions.java | 62 +- .../gofeatureflag/bean/BeanUtils.java | 4 +- .../bean/ConfigurationChange.java | 4 +- .../bean/GoFeatureFlagResponse.java | 9 +- .../gofeatureflag/bean/GoFeatureFlagUser.java | 15 +- .../controller/CacheController.java | 13 +- .../controller/GoFeatureFlagController.java | 101 ++- .../ConfigurationChangeEndpointNotFound.java | 7 +- ...ConfigurationChangeEndpointUnknownErr.java | 7 +- .../exception/GoFeatureFlagException.java | 7 +- .../exception/InvalidEndpoint.java | 7 +- .../exception/InvalidOptions.java | 7 +- .../exception/InvalidTypeInCache.java | 4 +- .../gofeatureflag/hook/DataCollectorHook.java | 29 +- .../hook/DataCollectorHookOptions.java | 34 +- .../gofeatureflag/hook/events/Event.java | 4 +- .../gofeatureflag/hook/events/Events.java | 7 +- .../hook/events/EventsPublisher.java | 10 +- .../gofeatureflag/util/ConcurrentUtils.java | 19 +- .../gofeatureflag/util/MetadataUtil.java | 11 +- .../GoFeatureFlagProviderTest.java | 315 ++++--- .../providers/jsonlogic/FileBasedFetcher.java | 19 +- .../jsonlogic/JsonlogicProvider.java | 6 +- .../providers/jsonlogic/RuleFetcher.java | 13 +- .../jsonlogic/FileBasedFetcherTest.java | 10 +- .../jsonlogic/JsonlogicProviderTest.java | 22 +- .../multiprovider/FirstMatchStrategy.java | 35 +- .../FirstSuccessfulStrategy.java | 22 +- .../multiprovider/MultiProvider.java | 42 +- .../providers/multiprovider/Strategy.java | 13 +- .../multiprovider/MultiProviderTest.java | 131 +-- .../providers/statsig/ContextTransformer.java | 6 +- .../providers/statsig/StatsigProvider.java | 84 +- .../statsig/StatsigProviderConfig.java | 5 +- .../statsig/StatsigProviderTest.java | 304 ++++--- .../providers/unleash/ContextTransformer.java | 6 +- .../providers/unleash/UnleashProvider.java | 72 +- .../unleash/UnleashProviderConfig.java | 5 +- .../unleash/UnleashSubscriberWrapper.java | 24 +- .../unleash/UnleashProviderTest.java | 168 ++-- .../contrib/tools/junitopenfeature/Flag.java | 5 +- .../contrib/tools/junitopenfeature/Flags.java | 5 +- .../tools/junitopenfeature/OpenFeature.java | 7 +- .../OpenFeatureDefaultDomain.java | 5 +- .../OpenFeatureExtension.java | 63 +- .../tools/junitopenfeature/OpenFeatures.java | 5 +- .../tools/junitopenfeature/TestProvider.java | 28 +- .../junitopenfeature/BooleanFlagTest.java | 38 +- .../junitopenfeature/DoubleFlagTest.java | 41 +- .../junitopenfeature/IntegerFlagTest.java | 41 +- .../OpenFeatureExtensionTest.java | 16 +- .../junitopenfeature/StringFlagTest.java | 53 +- 183 files changed, 3681 insertions(+), 3910 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..5bffb8ae0 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,72 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +tab_width = 4 +trim_trailing_whitespace = true + +ij_continuation_indent_size = 8 + +[*.md] +max_line_length = off +trim_trailing_whitespace = false + +# Following the rules of the Google Java Style Guide. +# See https://google.github.io/styleguide/javaguide.html +[*.java] +max_line_length = 120 + +ij_java_do_not_wrap_after_single_annotation_in_parameter = true +ij_java_insert_inner_class_imports = false +ij_java_class_count_to_use_import_on_demand = 999 +ij_java_names_count_to_use_import_on_demand = 999 +ij_java_packages_to_use_import_on_demand = unset +ij_java_imports_layout = $*,|,* +ij_java_doc_align_param_comments = true +ij_java_doc_align_exception_comments = true +ij_java_doc_add_p_tag_on_empty_lines = false +ij_java_doc_do_not_wrap_if_one_line = true +ij_java_doc_keep_empty_parameter_tag = false +ij_java_doc_keep_empty_throws_tag = false +ij_java_doc_keep_empty_return_tag = false +ij_java_doc_preserve_line_breaks = true +ij_java_doc_indent_on_continuation = true +ij_java_keep_control_statement_in_one_line = false +ij_java_keep_blank_lines_in_code = 1 +ij_java_align_multiline_parameters = false +ij_java_align_multiline_resources = false +ij_java_align_multiline_for = true +ij_java_space_before_array_initializer_left_brace = true +ij_java_call_parameters_wrap = normal +ij_java_method_parameters_wrap = normal +ij_java_extends_list_wrap = normal +ij_java_throws_keyword_wrap = normal +ij_java_method_call_chain_wrap = normal +ij_java_binary_operation_wrap = normal +ij_java_binary_operation_sign_on_next_line = true +ij_java_ternary_operation_wrap = normal +ij_java_ternary_operation_signs_on_next_line = true +ij_java_keep_simple_methods_in_one_line = true +ij_java_keep_simple_lambdas_in_one_line = true +ij_java_keep_simple_classes_in_one_line = true +ij_java_for_statement_wrap = normal +ij_java_array_initializer_wrap = normal +ij_java_wrap_comments = true +ij_java_if_brace_force = always +ij_java_do_while_brace_force = always +ij_java_while_brace_force = always +ij_java_for_brace_force = always +ij_java_space_after_closing_angle_bracket_in_type_argument = false + +[{*.json,*.json5}] +indent_size = 2 +tab_width = 2 +ij_smart_tabs = false + +[*.yaml] +indent_size = 2 +tab_width = 2 diff --git a/checkstyle.xml b/checkstyle.xml index 765a8b455..3b01278d3 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -20,7 +20,7 @@ - + @@ -28,10 +28,7 @@ - - - - + @@ -54,12 +51,28 @@ - - + + + + + + + + + + + + + + + + + + @@ -282,12 +295,12 @@ value="GenericWhitespace ''{0}'' is not preceded with whitespace."/> - - - + + + - + @@ -297,8 +310,9 @@ | //SLIST[not(parent::CASE_GROUP)]/SLIST/RCURLY"/> - - + + + - @@ -374,7 +387,8 @@ - + + before(HookContext ctx, Map hints) { activeFlagEvaluationsCounter.add(+1, Attributes.of(flagKeyAttributeKey, ctx.getFlagKey())); - evaluationRequestCounter.add(+1, Attributes.of(flagKeyAttributeKey, ctx.getFlagKey(), providerNameAttributeKey, - ctx.getProviderMetadata().getName())); + evaluationRequestCounter.add( + +1, + Attributes.of( + flagKeyAttributeKey, + ctx.getFlagKey(), + providerNameAttributeKey, + ctx.getProviderMetadata().getName())); return Optional.empty(); } @@ -96,7 +99,8 @@ public void after(HookContext ctx, FlagEvaluationDetails details, Map hints) { final AttributesBuilder attributesBuilder = Attributes.builder(); attributesBuilder.put(flagKeyAttributeKey, ctx.getFlagKey()); - attributesBuilder.put(providerNameAttributeKey, ctx.getProviderMetadata().getName()); + attributesBuilder.put( + providerNameAttributeKey, ctx.getProviderMetadata().getName()); attributesBuilder.putAll(extraDimensions); if (details.getReason() != null) { @@ -122,7 +126,8 @@ public void after(HookContext ctx, FlagEvaluationDetails details, Map hints) { public void error(HookContext ctx, Exception error, Map hints) { final AttributesBuilder attributesBuilder = Attributes.builder(); attributesBuilder.put(flagKeyAttributeKey, ctx.getFlagKey()); - attributesBuilder.put(providerNameAttributeKey, ctx.getProviderMetadata().getName()); + attributesBuilder.put( + providerNameAttributeKey, ctx.getProviderMetadata().getName()); attributesBuilder.putAll(extraDimensions); evaluationErrorCounter.add(+1, attributesBuilder.build()); @@ -136,8 +141,8 @@ public void finallyAfter(HookContext ctx, Map hints) { /** * A helper to derive attributes from {@link DimensionDescription}. */ - private static Attributes attributesFromFlagMetadata(final ImmutableMetadata flagMetadata, - final List dimensionDescriptions) { + private static Attributes attributesFromFlagMetadata( + final ImmutableMetadata flagMetadata, final List dimensionDescriptions) { final AttributesBuilder builder = Attributes.builder(); for (DimensionDescription dimension : dimensionDescriptions) { diff --git a/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/OTelCommons.java b/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/OTelCommons.java index b56d1239a..b36c79ec5 100644 --- a/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/OTelCommons.java +++ b/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/OTelCommons.java @@ -1,11 +1,11 @@ package dev.openfeature.contrib.hooks.otel; -import io.opentelemetry.api.common.AttributeKey; - import static io.opentelemetry.semconv.incubating.FeatureFlagIncubatingAttributes.FEATURE_FLAG_KEY; import static io.opentelemetry.semconv.incubating.FeatureFlagIncubatingAttributes.FEATURE_FLAG_PROVIDER_NAME; import static io.opentelemetry.semconv.incubating.FeatureFlagIncubatingAttributes.FEATURE_FLAG_VARIANT; +import io.opentelemetry.api.common.AttributeKey; + class OTelCommons { // Define semantic conventions // Refer - https://opentelemetry.io/docs/specs/otel/logs/semantic_conventions/feature-flags/ diff --git a/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/TracesHook.java b/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/TracesHook.java index 8a5926ebd..b260d393b 100644 --- a/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/TracesHook.java +++ b/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/TracesHook.java @@ -1,5 +1,10 @@ package dev.openfeature.contrib.hooks.otel; +import static dev.openfeature.contrib.hooks.otel.OTelCommons.EVENT_NAME; +import static dev.openfeature.contrib.hooks.otel.OTelCommons.flagKeyAttributeKey; +import static dev.openfeature.contrib.hooks.otel.OTelCommons.providerNameAttributeKey; +import static dev.openfeature.contrib.hooks.otel.OTelCommons.variantAttributeKey; + import dev.openfeature.sdk.FlagEvaluationDetails; import dev.openfeature.sdk.Hook; import dev.openfeature.sdk.HookContext; @@ -8,15 +13,9 @@ import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.StatusCode; - import java.util.Map; import java.util.function.Function; -import static dev.openfeature.contrib.hooks.otel.OTelCommons.EVENT_NAME; -import static dev.openfeature.contrib.hooks.otel.OTelCommons.flagKeyAttributeKey; -import static dev.openfeature.contrib.hooks.otel.OTelCommons.providerNameAttributeKey; -import static dev.openfeature.contrib.hooks.otel.OTelCommons.variantAttributeKey; - /** * The OpenTelemetry hook provides a way to automatically add a feature flag evaluation to a span as a span event. * Refer to OpenTelemetry @@ -63,7 +62,8 @@ public void after(HookContext ctx, FlagEvaluationDetails details, Map hints) { final AttributesBuilder attributesBuilder = Attributes.builder(); attributesBuilder.put(flagKeyAttributeKey, ctx.getFlagKey()); - attributesBuilder.put(providerNameAttributeKey, ctx.getProviderMetadata().getName()); + attributesBuilder.put( + providerNameAttributeKey, ctx.getProviderMetadata().getName()); attributesBuilder.put(variantAttributeKey, variant); attributesBuilder.putAll(extraAttributes); diff --git a/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/TracesHookOptions.java b/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/TracesHookOptions.java index 7d0f1e565..6ab390315 100644 --- a/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/TracesHookOptions.java +++ b/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/TracesHookOptions.java @@ -2,11 +2,10 @@ import dev.openfeature.sdk.ImmutableMetadata; import io.opentelemetry.api.common.Attributes; +import java.util.function.Function; import lombok.Builder; import lombok.Getter; -import java.util.function.Function; - /** * OpenTelemetry {@link TracesHook} options. */ diff --git a/hooks/open-telemetry/src/test/java/dev/openfeature/contrib/hooks/otel/MetricsHookTest.java b/hooks/open-telemetry/src/test/java/dev/openfeature/contrib/hooks/otel/MetricsHookTest.java index db3fe3d5e..942f82c01 100644 --- a/hooks/open-telemetry/src/test/java/dev/openfeature/contrib/hooks/otel/MetricsHookTest.java +++ b/hooks/open-telemetry/src/test/java/dev/openfeature/contrib/hooks/otel/MetricsHookTest.java @@ -1,5 +1,11 @@ package dev.openfeature.contrib.hooks.otel; +import static dev.openfeature.contrib.hooks.otel.OTelCommons.REASON_KEY; +import static dev.openfeature.contrib.hooks.otel.OTelCommons.flagKeyAttributeKey; +import static dev.openfeature.contrib.hooks.otel.OTelCommons.providerNameAttributeKey; +import static dev.openfeature.contrib.hooks.otel.OTelCommons.variantAttributeKey; +import static org.assertj.core.api.Assertions.assertThat; + import dev.openfeature.sdk.FlagEvaluationDetails; import dev.openfeature.sdk.FlagValueType; import dev.openfeature.sdk.HookContext; @@ -10,19 +16,12 @@ import io.opentelemetry.sdk.metrics.data.LongPointData; import io.opentelemetry.sdk.metrics.data.MetricData; import io.opentelemetry.sdk.testing.junit5.OpenTelemetryExtension; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Optional; - -import static dev.openfeature.contrib.hooks.otel.OTelCommons.REASON_KEY; -import static dev.openfeature.contrib.hooks.otel.OTelCommons.flagKeyAttributeKey; -import static dev.openfeature.contrib.hooks.otel.OTelCommons.providerNameAttributeKey; -import static dev.openfeature.contrib.hooks.otel.OTelCommons.variantAttributeKey; -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; class MetricsHookTest { private final HookContext commonHookContext = HookContext.builder() @@ -40,7 +39,6 @@ public void setup() { telemetryExtension = OpenTelemetryExtension.create(); } - @Test public void before_stage_validation() { // given @@ -81,7 +79,8 @@ public void after_stage_validation() { assertThat(successMetric.getName()).isEqualTo("feature_flag.evaluation_success_total"); // Validate dimensions(attributes) setup by extracting the first point - Optional data = successMetric.getLongSumData().getPoints().stream().findFirst(); + Optional data = + successMetric.getLongSumData().getPoints().stream().findFirst(); assertThat(data.isPresent()).isTrue(); final LongPointData longPointData = data.get(); @@ -94,9 +93,9 @@ public void after_stage_validation() { } @Test - public void after_stage_validation_of_custom_dimensions(){ + public void after_stage_validation_of_custom_dimensions() { // given - final List dimensionList = new ArrayList<>(); + final List dimensionList = new ArrayList<>(); dimensionList.add(new DimensionDescription("boolean", Boolean.class)); dimensionList.add(new DimensionDescription("integer", Integer.class)); dimensionList.add(new DimensionDescription("long", Long.class)); @@ -104,7 +103,8 @@ public void after_stage_validation_of_custom_dimensions(){ dimensionList.add(new DimensionDescription("double", Double.class)); dimensionList.add(new DimensionDescription("string", String.class)); - final MetricsHook metricHook = new MetricsHook(telemetryExtension.getOpenTelemetry(), + final MetricsHook metricHook = new MetricsHook( + telemetryExtension.getOpenTelemetry(), MetricHookOptions.builder().setDimensions(dimensionList).build()); final ImmutableMetadata metadata = ImmutableMetadata.builder() @@ -132,7 +132,8 @@ public void after_stage_validation_of_custom_dimensions(){ assertThat(metrics).hasSize(1); final MetricData metricData = metrics.get(0); - final Optional pointData = metricData.getLongSumData().getPoints().stream().findFirst(); + final Optional pointData = + metricData.getLongSumData().getPoints().stream().findFirst(); assertThat(pointData).isPresent(); final LongPointData longPointData = pointData.get(); @@ -149,13 +150,12 @@ public void after_stage_validation_of_custom_dimensions(){ @Test public void error_stage_validation() { // given - final MetricsHook metricHook = - new MetricsHook(telemetryExtension.getOpenTelemetry(), - MetricHookOptions.builder() - .extraAttributes(Attributes.builder() - .put("scope", "app-a") - .build()) - .build()); + final MetricsHook metricHook = new MetricsHook( + telemetryExtension.getOpenTelemetry(), + MetricHookOptions.builder() + .extraAttributes( + Attributes.builder().put("scope", "app-a").build()) + .build()); final Exception exception = new Exception("some_exception"); @@ -170,7 +170,8 @@ public void error_stage_validation() { assertThat(successMetric.getName()).isEqualTo("feature_flag.evaluation_error_total"); // Validate dimensions(attributes) setup by extracting the first point - Optional data = successMetric.getLongSumData().getPoints().stream().findFirst(); + Optional data = + successMetric.getLongSumData().getPoints().stream().findFirst(); assertThat(data.isPresent()).isTrue(); final LongPointData longPointData = data.get(); @@ -181,7 +182,6 @@ public void error_stage_validation() { assertThat(attributes.get(AttributeKey.stringKey("scope"))).isEqualTo("app-a"); } - @Test public void finally_stage_validation() { // given @@ -198,7 +198,8 @@ public void finally_stage_validation() { assertThat(successMetric.getName()).isEqualTo("feature_flag.evaluation_active_count"); // Validate dimensions(attributes) setup by extracting the first point - Optional data = successMetric.getLongSumData().getPoints().stream().findFirst(); + Optional data = + successMetric.getLongSumData().getPoints().stream().findFirst(); assertThat(data.isPresent()).isTrue(); final LongPointData longPointData = data.get(); @@ -208,7 +209,7 @@ public void finally_stage_validation() { } @Test - public void hook_option_validation(){ + public void hook_option_validation() { // given MetricHookOptions hookOptions = MetricHookOptions.builder() .attributeSetter(metadata -> Attributes.builder() @@ -219,10 +220,7 @@ public void hook_option_validation(){ .put("double", metadata.getDouble("double")) .put("string", metadata.getString("string")) .build()) - .extraAttributes( - Attributes.builder() - .put("scope", "value") - .build()) + .extraAttributes(Attributes.builder().put("scope", "value").build()) .build(); final MetricsHook metricHook = new MetricsHook(telemetryExtension.getOpenTelemetry(), hookOptions); @@ -252,7 +250,8 @@ public void hook_option_validation(){ assertThat(metrics).hasSize(1); final MetricData metricData = metrics.get(0); - final Optional pointData = metricData.getLongSumData().getPoints().stream().findFirst(); + final Optional pointData = + metricData.getLongSumData().getPoints().stream().findFirst(); assertThat(pointData).isPresent(); final LongPointData longPointData = pointData.get(); @@ -266,4 +265,4 @@ public void hook_option_validation(){ assertThat(attributes.get(AttributeKey.booleanKey("boolean"))).isEqualTo(true); assertThat(attributes.get(AttributeKey.stringKey("scope"))).isEqualTo("value"); } -} \ No newline at end of file +} diff --git a/hooks/open-telemetry/src/test/java/dev/openfeature/contrib/hooks/otel/TracesHookTest.java b/hooks/open-telemetry/src/test/java/dev/openfeature/contrib/hooks/otel/TracesHookTest.java index b8ca237d5..f9731a30f 100644 --- a/hooks/open-telemetry/src/test/java/dev/openfeature/contrib/hooks/otel/TracesHookTest.java +++ b/hooks/open-telemetry/src/test/java/dev/openfeature/contrib/hooks/otel/TracesHookTest.java @@ -1,5 +1,11 @@ package dev.openfeature.contrib.hooks.otel; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; + import dev.openfeature.sdk.FlagEvaluationDetails; import dev.openfeature.sdk.FlagValueType; import dev.openfeature.sdk.HookContext; @@ -18,12 +24,6 @@ import org.mockito.MockedStatic; import org.mockito.junit.jupiter.MockitoExtension; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mockStatic; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; - @ExtendWith(MockitoExtension.class) class TracesHookTest { private static MockedStatic mockedSpan; @@ -65,9 +65,13 @@ void should_add_event_in_span_during_after_method_execution() { TracesHook tracesHook = new TracesHook(); tracesHook.after(hookContext, details, null); - Attributes expectedAttr = Attributes.of(flagKeyAttributeKey, "test_key", - providerNameAttributeKey, "test provider", - variantAttributeKey, "test_variant"); + Attributes expectedAttr = Attributes.of( + flagKeyAttributeKey, + "test_key", + providerNameAttributeKey, + "test provider", + variantAttributeKey, + "test_variant"); verify(span).addEvent("feature_flag", expectedAttr); } @@ -75,18 +79,20 @@ void should_add_event_in_span_during_after_method_execution() { @Test @DisplayName("attribute should fallback to value field when variant is null") void attribute_should_fallback_to_value_field_when_variant_is_null() { - FlagEvaluationDetails details = FlagEvaluationDetails.builder() - .value("variant_value") - .build(); + FlagEvaluationDetails details = + FlagEvaluationDetails.builder().value("variant_value").build(); mockedSpan.when(Span::current).thenReturn(span); - TracesHook tracesHook = new TracesHook(); tracesHook.after(hookContext, details, null); - Attributes expectedAttr = Attributes.of(flagKeyAttributeKey, "test_key", - providerNameAttributeKey, "test provider", - variantAttributeKey, "variant_value"); + Attributes expectedAttr = Attributes.of( + flagKeyAttributeKey, + "test_key", + providerNameAttributeKey, + "test provider", + variantAttributeKey, + "variant_value"); verify(span).addEvent("feature_flag", expectedAttr); } @@ -119,12 +125,9 @@ void should_record_exception_and_status_in_span_during_error_method_execution() RuntimeException runtimeException = new RuntimeException("could not resolve the flag"); mockedSpan.when(Span::current).thenReturn(span); - final TracesHook tracesHook = new TracesHook( - TracesHookOptions.builder() - .extraAttributes(Attributes.builder() - .put("scope", "app-a") - .build()) - .build()); + final TracesHook tracesHook = new TracesHook(TracesHookOptions.builder() + .extraAttributes(Attributes.builder().put("scope", "app-a").build()) + .build()); tracesHook.error(hookContext, runtimeException, null); @@ -144,15 +147,12 @@ void should_record_exception_but_not_status_in_span_with_options() { RuntimeException runtimeException = new RuntimeException("could not resolve the flag"); mockedSpan.when(Span::current).thenReturn(span); - TracesHook tracesHook = - new TracesHook(TracesHookOptions - .builder() - .setSpanErrorStatus(true) - .build()); + TracesHook tracesHook = new TracesHook( + TracesHookOptions.builder().setSpanErrorStatus(true).build()); tracesHook.error(hookContext, runtimeException, null); - Attributes expectedAttr = Attributes.of(flagKeyAttributeKey, "test_key", - providerNameAttributeKey, "test provider"); + Attributes expectedAttr = + Attributes.of(flagKeyAttributeKey, "test_key", providerNameAttributeKey, "test provider"); verify(span).recordException(runtimeException, expectedAttr); verify(span, times(1)).setStatus(any()); @@ -195,8 +195,7 @@ void should_execute_callback_which_populate_span_attributes() { .put("float", metadata.getFloat("float")) .put("double", metadata.getDouble("double")) .put("string", metadata.getString("string")) - .build() - ) + .build()) .extraAttributes(Attributes.builder().put("scope", "value").build()) .build(); @@ -218,5 +217,4 @@ void should_execute_callback_which_populate_span_attributes() { // verify span built with given attributes verify(span).addEvent("feature_flag", attributesBuilder.build()); } - -} \ No newline at end of file +} diff --git a/providers/configcat/src/main/java/dev/openfeature/contrib/providers/configcat/ConfigCatProvider.java b/providers/configcat/src/main/java/dev/openfeature/contrib/providers/configcat/ConfigCatProvider.java index b34fec746..a437412b4 100644 --- a/providers/configcat/src/main/java/dev/openfeature/contrib/providers/configcat/ConfigCatProvider.java +++ b/providers/configcat/src/main/java/dev/openfeature/contrib/providers/configcat/ConfigCatProvider.java @@ -1,12 +1,8 @@ package dev.openfeature.contrib.providers.configcat; -import java.util.ArrayList; -import java.util.concurrent.atomic.AtomicBoolean; - import com.configcat.ConfigCatClient; import com.configcat.EvaluationDetails; import com.configcat.User; - import dev.openfeature.sdk.EvaluationContext; import dev.openfeature.sdk.EventProvider; import dev.openfeature.sdk.Metadata; @@ -14,13 +10,13 @@ import dev.openfeature.sdk.ProviderEventDetails; import dev.openfeature.sdk.Value; import dev.openfeature.sdk.exceptions.GeneralError; +import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicBoolean; import lombok.Getter; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -/** - * Provider implementation for ConfigCat. - */ +/** Provider implementation for ConfigCat. */ @Slf4j public class ConfigCatProvider extends EventProvider { @@ -39,6 +35,7 @@ public class ConfigCatProvider extends EventProvider { /** * Constructor. + * * @param configCatProviderConfig configCatProvider Config */ public ConfigCatProvider(ConfigCatProviderConfig configCatProviderConfig) { @@ -47,6 +44,7 @@ public ConfigCatProvider(ConfigCatProviderConfig configCatProviderConfig) { /** * Initialize the provider. + * * @param evaluationContext evaluation context * @throws Exception on error */ @@ -57,30 +55,28 @@ public void initialize(EvaluationContext evaluationContext) throws Exception { throw new GeneralError("already initialized"); } super.initialize(evaluationContext); - configCatClient = ConfigCatClient.get(configCatProviderConfig.getSdkKey(), - configCatProviderConfig.getOptions()); + configCatClient = + ConfigCatClient.get(configCatProviderConfig.getSdkKey(), configCatProviderConfig.getOptions()); configCatProviderConfig.postInit(); log.info("finished initializing provider"); configCatClient.getHooks().addOnClientReady(() -> { - ProviderEventDetails providerEventDetails = ProviderEventDetails.builder() - .message("provider ready") - .build(); + ProviderEventDetails providerEventDetails = + ProviderEventDetails.builder().message("provider ready").build(); emitProviderReady(providerEventDetails); }); configCatClient.getHooks().addOnConfigChanged(map -> { ProviderEventDetails providerEventDetails = ProviderEventDetails.builder() - .flagsChanged(new ArrayList<>(map.keySet())) - .message("config changed") - .build(); + .flagsChanged(new ArrayList<>(map.keySet())) + .message("config changed") + .build(); emitProviderConfigurationChanged(providerEventDetails); }); configCatClient.getHooks().addOnError(errorMessage -> { - ProviderEventDetails providerEventDetails = ProviderEventDetails.builder() - .message(errorMessage) - .build(); + ProviderEventDetails providerEventDetails = + ProviderEventDetails.builder().message(errorMessage).build(); emitProviderError(providerEventDetails); }); } @@ -116,26 +112,25 @@ public ProviderEvaluation getObjectEvaluation(String key, Value defaultVa return getEvaluation(Value.class, key, defaultValue, ctx); } - private ProviderEvaluation getEvaluation(Class classOfT, String key, T defaultValue, - EvaluationContext ctx) { + private ProviderEvaluation getEvaluation( + Class classOfT, String key, T defaultValue, EvaluationContext ctx) { User user = ctx == null ? null : ContextTransformer.transform(ctx); EvaluationDetails evaluationDetails; T evaluatedValue; if (classOfT.isAssignableFrom(Value.class)) { - String defaultValueStr = defaultValue == null ? null : ((Value)defaultValue).asString(); - evaluationDetails = (EvaluationDetails) configCatClient - .getValueDetails(String.class, key, user, defaultValueStr); - evaluatedValue = evaluationDetails.getValue() == null ? null : - (T) Value.objectToValue(evaluationDetails.getValue()); + String defaultValueStr = defaultValue == null ? null : ((Value) defaultValue).asString(); + evaluationDetails = + (EvaluationDetails) configCatClient.getValueDetails(String.class, key, user, defaultValueStr); + evaluatedValue = + evaluationDetails.getValue() == null ? null : (T) Value.objectToValue(evaluationDetails.getValue()); } else { - evaluationDetails = configCatClient - .getValueDetails(classOfT, key, user, defaultValue); + evaluationDetails = configCatClient.getValueDetails(classOfT, key, user, defaultValue); evaluatedValue = evaluationDetails.getValue(); } return ProviderEvaluation.builder() - .value(evaluatedValue) - .variant(evaluationDetails.getVariationId()) - .build(); + .value(evaluatedValue) + .variant(evaluationDetails.getVariationId()) + .build(); } @SneakyThrows diff --git a/providers/configcat/src/main/java/dev/openfeature/contrib/providers/configcat/ConfigCatProviderConfig.java b/providers/configcat/src/main/java/dev/openfeature/contrib/providers/configcat/ConfigCatProviderConfig.java index bd4b9e1ef..fc89ab3c3 100644 --- a/providers/configcat/src/main/java/dev/openfeature/contrib/providers/configcat/ConfigCatProviderConfig.java +++ b/providers/configcat/src/main/java/dev/openfeature/contrib/providers/configcat/ConfigCatProviderConfig.java @@ -1,15 +1,11 @@ package dev.openfeature.contrib.providers.configcat; import com.configcat.ConfigCatClient; +import java.util.function.Consumer; import lombok.Builder; import lombok.Getter; -import java.util.function.Consumer; - - -/** - * Options for initializing ConfigCat provider. - */ +/** Options for initializing ConfigCat provider. */ @Getter @Builder public class ConfigCatProviderConfig { diff --git a/providers/configcat/src/main/java/dev/openfeature/contrib/providers/configcat/ContextTransformer.java b/providers/configcat/src/main/java/dev/openfeature/contrib/providers/configcat/ContextTransformer.java index 75ab0db41..d6be9a99b 100644 --- a/providers/configcat/src/main/java/dev/openfeature/contrib/providers/configcat/ContextTransformer.java +++ b/providers/configcat/src/main/java/dev/openfeature/contrib/providers/configcat/ContextTransformer.java @@ -2,13 +2,10 @@ import com.configcat.User; import dev.openfeature.sdk.EvaluationContext; - import java.util.HashMap; import java.util.Map; -/** - * Transformer from OpenFeature context to ConfigCat User. - */ +/** Transformer from OpenFeature context to ConfigCat User. */ public class ContextTransformer { public static final String CONTEXT_EMAIL = "Email"; @@ -33,5 +30,4 @@ protected static User transform(EvaluationContext ctx) { userBuilder.custom(customMap); return userBuilder.build(ctx.getTargetingKey()); } - } diff --git a/providers/configcat/src/test/java/dev/openfeature/contrib/providers/configcat/ConfigCatProviderTest.java b/providers/configcat/src/test/java/dev/openfeature/contrib/providers/configcat/ConfigCatProviderTest.java index 685089109..798f980bc 100644 --- a/providers/configcat/src/test/java/dev/openfeature/contrib/providers/configcat/ConfigCatProviderTest.java +++ b/providers/configcat/src/test/java/dev/openfeature/contrib/providers/configcat/ConfigCatProviderTest.java @@ -3,28 +3,25 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; -import java.util.HashMap; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - import com.configcat.OverrideBehaviour; import com.configcat.OverrideDataSourceBuilder; import com.configcat.User; - import dev.openfeature.sdk.Client; import dev.openfeature.sdk.ImmutableContext; import dev.openfeature.sdk.MutableContext; import dev.openfeature.sdk.OpenFeatureAPI; import dev.openfeature.sdk.ProviderEventDetails; import dev.openfeature.sdk.Value; +import java.util.HashMap; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; /** - * ConfigCatProvider test, based on local config file evaluation. - * Configuration file test by ConfigCat tests. + * ConfigCatProvider test, based on local config file evaluation. Configuration file test by + * ConfigCat tests. */ @Slf4j class ConfigCatProviderTest { @@ -45,11 +42,11 @@ class ConfigCatProviderTest { @BeforeAll static void setUp() { String sdkKey = "configcat-sdk-1/TEST_KEY-0123456789012/1234567890123456789012"; - ConfigCatProviderConfig configCatProviderConfig = ConfigCatProviderConfig.builder().sdkKey(sdkKey) - .options(options -> - options.flagOverrides( - OverrideDataSourceBuilder.classPathResource("features.json"), - OverrideBehaviour.LOCAL_ONLY)).build(); + ConfigCatProviderConfig configCatProviderConfig = ConfigCatProviderConfig.builder() + .sdkKey(sdkKey) + .options(options -> options.flagOverrides( + OverrideDataSourceBuilder.classPathResource("features.json"), OverrideBehaviour.LOCAL_ONLY)) + .build(); configCatProvider = new ConfigCatProvider(configCatProviderConfig); OpenFeatureAPI.getInstance().setProviderAndWait(configCatProvider); client = OpenFeatureAPI.getInstance().getClient(); @@ -62,37 +59,60 @@ static void shutdown() { @Test void getBooleanEvaluation() { - assertEquals(true, configCatProvider.getBooleanEvaluation(FLAG_NAME, false, new ImmutableContext()).getValue()); + assertEquals( + true, + configCatProvider + .getBooleanEvaluation(FLAG_NAME, false, new ImmutableContext()) + .getValue()); assertEquals(true, client.getBooleanValue(FLAG_NAME, false)); - assertEquals(false, configCatProvider.getBooleanEvaluation("non-existing", false, new ImmutableContext()).getValue()); + assertEquals( + false, + configCatProvider + .getBooleanEvaluation("non-existing", false, new ImmutableContext()) + .getValue()); assertEquals(false, client.getBooleanValue("non-existing", false)); } @Test void getStringEvaluation() { - assertEquals(VARIANT_FLAG_VALUE, configCatProvider.getStringEvaluation(VARIANT_FLAG_NAME, "", - new ImmutableContext()).getValue()); + assertEquals( + VARIANT_FLAG_VALUE, + configCatProvider + .getStringEvaluation(VARIANT_FLAG_NAME, "", new ImmutableContext()) + .getValue()); assertEquals(VARIANT_FLAG_VALUE, client.getStringValue(VARIANT_FLAG_NAME, "")); - assertEquals("fallback_str", configCatProvider.getStringEvaluation("non-existing", - "fallback_str", new ImmutableContext()).getValue()); + assertEquals( + "fallback_str", + configCatProvider + .getStringEvaluation("non-existing", "fallback_str", new ImmutableContext()) + .getValue()); assertEquals("fallback_str", client.getStringValue("non-existing", "fallback_str")); } @Test void getObjectEvaluation() { - assertEquals(VARIANT_FLAG_VALUE, configCatProvider.getStringEvaluation(VARIANT_FLAG_NAME, "", - new ImmutableContext()).getValue()); + assertEquals( + VARIANT_FLAG_VALUE, + configCatProvider + .getStringEvaluation(VARIANT_FLAG_NAME, "", new ImmutableContext()) + .getValue()); assertEquals(new Value(VARIANT_FLAG_VALUE), client.getObjectValue(VARIANT_FLAG_NAME, new Value(""))); - assertEquals(new Value("fallback_str"), configCatProvider.getObjectEvaluation("non-existing", - new Value("fallback_str"), new ImmutableContext()).getValue()); + assertEquals( + new Value("fallback_str"), + configCatProvider + .getObjectEvaluation("non-existing", new Value("fallback_str"), new ImmutableContext()) + .getValue()); assertEquals(new Value("fallback_str"), client.getObjectValue("non-existing", new Value("fallback_str"))); } @Test void getIntegerEvaluation() { MutableContext evaluationContext = new MutableContext(); - assertEquals(INT_FLAG_VALUE, configCatProvider.getIntegerEvaluation(INT_FLAG_NAME, 1, - evaluationContext).getValue()); + assertEquals( + INT_FLAG_VALUE, + configCatProvider + .getIntegerEvaluation(INT_FLAG_NAME, 1, evaluationContext) + .getValue()); assertEquals(INT_FLAG_VALUE, client.getIntegerValue(INT_FLAG_NAME, 1)); assertEquals(1, client.getIntegerValue("non-existing", 1)); @@ -103,8 +123,11 @@ void getIntegerEvaluation() { @Test void getDoubleEvaluation() { MutableContext evaluationContext = new MutableContext(); - assertEquals(DOUBLE_FLAG_VALUE, configCatProvider.getDoubleEvaluation(DOUBLE_FLAG_NAME, 1.1, - evaluationContext).getValue()); + assertEquals( + DOUBLE_FLAG_VALUE, + configCatProvider + .getDoubleEvaluation(DOUBLE_FLAG_NAME, 1.1, evaluationContext) + .getValue()); assertEquals(DOUBLE_FLAG_VALUE, client.getDoubleValue(DOUBLE_FLAG_NAME, 1.1)); assertEquals(1.1, client.getDoubleValue("non-existing", 1.1)); @@ -116,10 +139,18 @@ void getDoubleEvaluation() { void getBooleanEvaluationByUser() { MutableContext evaluationContext = new MutableContext(); evaluationContext.setTargetingKey("csp@matching.com"); - assertEquals(true, configCatProvider.getBooleanEvaluation(USERS_FLAG_NAME, false, evaluationContext).getValue()); + assertEquals( + true, + configCatProvider + .getBooleanEvaluation(USERS_FLAG_NAME, false, evaluationContext) + .getValue()); assertEquals(true, client.getBooleanValue(USERS_FLAG_NAME, false, evaluationContext)); evaluationContext.setTargetingKey("csp@notmatching.com"); - assertEquals(false, configCatProvider.getBooleanEvaluation(USERS_FLAG_NAME, false, evaluationContext).getValue()); + assertEquals( + false, + configCatProvider + .getBooleanEvaluation(USERS_FLAG_NAME, false, evaluationContext) + .getValue()); assertEquals(false, client.getBooleanValue(USERS_FLAG_NAME, false, evaluationContext)); } @@ -128,10 +159,18 @@ void getBooleanEvaluationByEmail() { MutableContext evaluationContext = new MutableContext(); evaluationContext.setTargetingKey("csp@matching.com"); evaluationContext.add("Email", "a@matching.com"); - assertEquals(true, configCatProvider.getBooleanEvaluation(EMAIL_FLAG_NAME, false, evaluationContext).getValue()); + assertEquals( + true, + configCatProvider + .getBooleanEvaluation(EMAIL_FLAG_NAME, false, evaluationContext) + .getValue()); assertEquals(true, client.getBooleanValue(EMAIL_FLAG_NAME, false, evaluationContext)); evaluationContext.add("Email", "a@matchingnot.com"); - assertEquals(false, configCatProvider.getBooleanEvaluation(EMAIL_FLAG_NAME, false, evaluationContext).getValue()); + assertEquals( + false, + configCatProvider + .getBooleanEvaluation(EMAIL_FLAG_NAME, false, evaluationContext) + .getValue()); assertEquals(false, client.getBooleanValue(EMAIL_FLAG_NAME, false, evaluationContext)); } @@ -140,10 +179,18 @@ void getBooleanEvaluationByCountry() { MutableContext evaluationContext = new MutableContext(); evaluationContext.setTargetingKey("csp@matching.com"); evaluationContext.add("Country", "country1"); - assertEquals(true, configCatProvider.getBooleanEvaluation(COUNTRY_FLAG_NAME, false, evaluationContext).getValue()); + assertEquals( + true, + configCatProvider + .getBooleanEvaluation(COUNTRY_FLAG_NAME, false, evaluationContext) + .getValue()); assertEquals(true, client.getBooleanValue(COUNTRY_FLAG_NAME, false, evaluationContext)); evaluationContext.add("Country", "country2"); - assertEquals(false, configCatProvider.getBooleanEvaluation(COUNTRY_FLAG_NAME, false, evaluationContext).getValue()); + assertEquals( + false, + configCatProvider + .getBooleanEvaluation(COUNTRY_FLAG_NAME, false, evaluationContext) + .getValue()); assertEquals(false, client.getBooleanValue(COUNTRY_FLAG_NAME, false, evaluationContext)); } @@ -151,10 +198,18 @@ void getBooleanEvaluationByCountry() { void getIntEvaluationByUser() { MutableContext evaluationContext = new MutableContext(); evaluationContext.setTargetingKey("csp@matching.com"); - assertEquals(123, configCatProvider.getIntegerEvaluation(USERS_FLAG_NAME + "Int", 111, evaluationContext).getValue()); + assertEquals( + 123, + configCatProvider + .getIntegerEvaluation(USERS_FLAG_NAME + "Int", 111, evaluationContext) + .getValue()); assertEquals(123, client.getIntegerValue(USERS_FLAG_NAME + "Int", 111, evaluationContext)); evaluationContext.setTargetingKey("csp@notmatching.com"); - assertEquals(111, configCatProvider.getIntegerEvaluation(USERS_FLAG_NAME + "Int", 222, evaluationContext).getValue()); + assertEquals( + 111, + configCatProvider + .getIntegerEvaluation(USERS_FLAG_NAME + "Int", 222, evaluationContext) + .getValue()); assertEquals(111, client.getIntegerValue(USERS_FLAG_NAME + "Int", 222, evaluationContext)); } @@ -162,10 +217,18 @@ void getIntEvaluationByUser() { void getDoubleEvaluationByUser() { MutableContext evaluationContext = new MutableContext(); evaluationContext.setTargetingKey("csp@matching.com"); - assertEquals(1.23, configCatProvider.getDoubleEvaluation(USERS_FLAG_NAME + "Double", 1.11, evaluationContext).getValue()); + assertEquals( + 1.23, + configCatProvider + .getDoubleEvaluation(USERS_FLAG_NAME + "Double", 1.11, evaluationContext) + .getValue()); assertEquals(1.23, client.getDoubleValue(USERS_FLAG_NAME + "Double", 1.11, evaluationContext)); evaluationContext.setTargetingKey("csp@matchingnot.com"); - assertEquals(0.1, configCatProvider.getDoubleEvaluation(USERS_FLAG_NAME + "Double", 1.11, evaluationContext).getValue()); + assertEquals( + 0.1, + configCatProvider + .getDoubleEvaluation(USERS_FLAG_NAME + "Double", 1.11, evaluationContext) + .getValue()); assertEquals(0.1, client.getDoubleValue(USERS_FLAG_NAME + "Double", 1.11, evaluationContext)); } @@ -173,10 +236,18 @@ void getDoubleEvaluationByUser() { void getStringEvaluationByUser() { MutableContext evaluationContext = new MutableContext(); evaluationContext.setTargetingKey("csp@matching.com"); - assertEquals("expected", configCatProvider.getStringEvaluation(USERS_FLAG_NAME + "Str", "111", evaluationContext).getValue()); + assertEquals( + "expected", + configCatProvider + .getStringEvaluation(USERS_FLAG_NAME + "Str", "111", evaluationContext) + .getValue()); assertEquals("expected", client.getStringValue(USERS_FLAG_NAME + "Str", "111", evaluationContext)); evaluationContext.setTargetingKey("csp@notmatching.com"); - assertEquals("fallback", configCatProvider.getStringEvaluation(USERS_FLAG_NAME + "Str", "111", evaluationContext).getValue()); + assertEquals( + "fallback", + configCatProvider + .getStringEvaluation(USERS_FLAG_NAME + "Str", "111", evaluationContext) + .getValue()); assertEquals("fallback", client.getStringValue(USERS_FLAG_NAME + "Str", "111", evaluationContext)); } @@ -184,7 +255,8 @@ void getStringEvaluationByUser() { void eventsTest() { configCatProvider.emitProviderReady(ProviderEventDetails.builder().build()); configCatProvider.emitProviderError(ProviderEventDetails.builder().build()); - configCatProvider.emitProviderConfigurationChanged(ProviderEventDetails.builder().build()); + configCatProvider.emitProviderConfigurationChanged( + ProviderEventDetails.builder().build()); assertDoesNotThrow(() -> log.debug("provider state: {}", configCatProvider.getState())); } @@ -204,14 +276,17 @@ void contextTransformTest() { evaluationContext.add("Email", email); evaluationContext.add(customPropertyKey, customPropertyValue); - HashMap customMap = new HashMap<>(); + HashMap customMap = new HashMap<>(); customMap.put(customPropertyKey, customPropertyValue); customMap.put(targetingKeyKey, userId); - User expectedUser = User.newBuilder().email(email).country(country).custom(customMap).build(userId); + User expectedUser = User.newBuilder() + .email(email) + .country(country) + .custom(customMap) + .build(userId); User transformedUser = ContextTransformer.transform(evaluationContext); // equals not implemented for User, using toString assertEquals(expectedUser.toString(), transformedUser.toString()); } - -} \ No newline at end of file +} diff --git a/providers/env-var/src/main/java/dev/openfeature/contrib/providers/envvar/EnvVarProvider.java b/providers/env-var/src/main/java/dev/openfeature/contrib/providers/envvar/EnvVarProvider.java index d421242f9..968b960d9 100644 --- a/providers/env-var/src/main/java/dev/openfeature/contrib/providers/envvar/EnvVarProvider.java +++ b/providers/env-var/src/main/java/dev/openfeature/contrib/providers/envvar/EnvVarProvider.java @@ -9,12 +9,9 @@ import dev.openfeature.sdk.exceptions.FlagNotFoundError; import dev.openfeature.sdk.exceptions.ParseError; import dev.openfeature.sdk.exceptions.ValueNotConvertableError; - import java.util.function.Function; -/** - * EnvVarProvider is the Java provider implementation for the environment variables. - */ +/** EnvVarProvider is the Java provider implementation for the environment variables. */ public final class EnvVarProvider implements FeatureProvider { private static final String NAME = "Environment Variables Provider"; @@ -68,10 +65,7 @@ public ProviderEvaluation getObjectEvaluation(String key, Value defaultVa throw new ValueNotConvertableError("EnvVarProvider supports only primitives"); } - private ProviderEvaluation evaluateEnvironmentVariable( - String key, - Function parse - ) { + private ProviderEvaluation evaluateEnvironmentVariable(String key, Function parse) { final String value = environmentGateway.getEnvironmentVariable(keyTransformer.transformKey(key)); if (value == null) { @@ -84,10 +78,7 @@ private ProviderEvaluation evaluateEnvironmentVariable( .reason(Reason.STATIC.toString()) .build(); } catch (Exception e) { - throw new ParseError( - e.getMessage() != null ? e.getMessage() : "Unknown parsing error", - e - ); + throw new ParseError(e.getMessage() != null ? e.getMessage() : "Unknown parsing error", e); } } } diff --git a/providers/env-var/src/main/java/dev/openfeature/contrib/providers/envvar/EnvironmentGateway.java b/providers/env-var/src/main/java/dev/openfeature/contrib/providers/envvar/EnvironmentGateway.java index 30064a266..16005a722 100644 --- a/providers/env-var/src/main/java/dev/openfeature/contrib/providers/envvar/EnvironmentGateway.java +++ b/providers/env-var/src/main/java/dev/openfeature/contrib/providers/envvar/EnvironmentGateway.java @@ -2,9 +2,9 @@ /** * This is an abstraction to fetch environment variables. It can be used to support - * environment-specific access or provide additional functionality, like prefixes, - * casing and even sources like spring configurations which come from different sources. - * Also, a test double could implement this interface, making the tests independent of the actual environment. + * environment-specific access or provide additional functionality, like prefixes, casing and even + * sources like spring configurations which come from different sources. Also, a test double could + * implement this interface, making the tests independent of the actual environment. */ public interface EnvironmentGateway { String getEnvironmentVariable(String key); diff --git a/providers/env-var/src/main/java/dev/openfeature/contrib/providers/envvar/EnvironmentKeyTransformer.java b/providers/env-var/src/main/java/dev/openfeature/contrib/providers/envvar/EnvironmentKeyTransformer.java index 10f0b73b7..9a576307d 100644 --- a/providers/env-var/src/main/java/dev/openfeature/contrib/providers/envvar/EnvironmentKeyTransformer.java +++ b/providers/env-var/src/main/java/dev/openfeature/contrib/providers/envvar/EnvironmentKeyTransformer.java @@ -2,7 +2,6 @@ import java.util.function.Function; import java.util.function.UnaryOperator; - import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.StringUtils; import org.apache.commons.text.CaseUtils; @@ -67,7 +66,7 @@ public class EnvironmentKeyTransformer { private static final UnaryOperator REPLACE_DOT_WITH_UNDERSCORE = s -> StringUtils.replaceChars(s, ".", "_"); private static final UnaryOperator REPLACE_UNDERSCORE_WITH_DOT = s -> StringUtils.replaceChars(s, "_", "."); private static final UnaryOperator REPLACE_HYPHEN_WITH_UNDERSCORE = - s -> StringUtils.replaceChars(s, "-", "_"); + s -> StringUtils.replaceChars(s, "-", "_"); private final Function transformation; @@ -100,8 +99,7 @@ public static EnvironmentKeyTransformer replaceDotWithUnderscoreTransformer() { } public static EnvironmentKeyTransformer hyphenCaseToScreamingSnake() { - return new EnvironmentKeyTransformer(REPLACE_HYPHEN_WITH_UNDERSCORE) - .andThen(toUpperCaseTransformer()); + return new EnvironmentKeyTransformer(REPLACE_HYPHEN_WITH_UNDERSCORE).andThen(toUpperCaseTransformer()); } public static EnvironmentKeyTransformer doNothing() { diff --git a/providers/env-var/src/main/java/dev/openfeature/contrib/providers/envvar/OS.java b/providers/env-var/src/main/java/dev/openfeature/contrib/providers/envvar/OS.java index eee15192e..eac9bffe0 100644 --- a/providers/env-var/src/main/java/dev/openfeature/contrib/providers/envvar/OS.java +++ b/providers/env-var/src/main/java/dev/openfeature/contrib/providers/envvar/OS.java @@ -1,8 +1,8 @@ package dev.openfeature.contrib.providers.envvar; /** - * This internal class abstracts away Java's {@link System} class for test purposes. - * It is not intended to be used directly. + * This internal class abstracts away Java's {@link System} class for test purposes. It is not + * intended to be used directly. */ class OS implements EnvironmentGateway { public String getEnvironmentVariable(String key) { diff --git a/providers/env-var/src/test/java/dev/openfeature/contrib/providers/envvar/EnvVarProviderTest.java b/providers/env-var/src/test/java/dev/openfeature/contrib/providers/envvar/EnvVarProviderTest.java index 15dfa0adf..9bbaab6f0 100644 --- a/providers/env-var/src/test/java/dev/openfeature/contrib/providers/envvar/EnvVarProviderTest.java +++ b/providers/env-var/src/test/java/dev/openfeature/contrib/providers/envvar/EnvVarProviderTest.java @@ -1,27 +1,24 @@ package dev.openfeature.contrib.providers.envvar; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + import dev.openfeature.sdk.*; import dev.openfeature.sdk.exceptions.FlagNotFoundError; import dev.openfeature.sdk.exceptions.ParseError; import dev.openfeature.sdk.exceptions.ValueNotConvertableError; -import org.junit.jupiter.api.*; - import java.util.*; import java.util.function.Consumer; import java.util.function.Function; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; +import org.junit.jupiter.api.*; class EnvVarProviderTest { @Test void shouldThrowOnGetObjectEvaluation() { - assertThrows( - ValueNotConvertableError.class, - () -> new EnvVarProvider().getObjectEvaluation("any-key", new Value(), new ImmutableContext()) - ); + assertThrows(ValueNotConvertableError.class, () -> new EnvVarProvider() + .getObjectEvaluation("any-key", new Value(), new ImmutableContext())); } @TestFactory @@ -32,44 +29,37 @@ Iterable shouldEvaluateValuesCorrectly() { "bool_true", "true", provider -> provider.getBooleanEvaluation("bool_true", null, null), - evaluation -> evaluationChecks(evaluation, REASON_STATIC, true) - ), + evaluation -> evaluationChecks(evaluation, REASON_STATIC, true)), evaluationTest( "bool_false", "bool_false", "FaLsE", provider -> provider.getBooleanEvaluation("bool_false", null, null), - evaluation -> evaluationChecks(evaluation, REASON_STATIC, false) - ), + evaluation -> evaluationChecks(evaluation, REASON_STATIC, false)), evaluationTest( "bool_false", "bool_false", "not-a-bool", provider -> provider.getBooleanEvaluation("bool_false", null, null), - evaluation -> evaluationChecks(evaluation, REASON_STATIC, false) - ), + evaluation -> evaluationChecks(evaluation, REASON_STATIC, false)), evaluationTest( "string", "string", "value", provider -> provider.getStringEvaluation("string", null, null), - evaluation -> evaluationChecks(evaluation, REASON_STATIC, "value") - ), + evaluation -> evaluationChecks(evaluation, REASON_STATIC, "value")), evaluationTest( "int", "INT", "42", provider -> provider.getIntegerEvaluation("INT", null, null), - evaluation -> evaluationChecks(evaluation, REASON_STATIC, 42) - ), + evaluation -> evaluationChecks(evaluation, REASON_STATIC, 42)), evaluationTest( "double", "double", "42.0", provider -> provider.getDoubleEvaluation("double", null, null), - evaluation -> evaluationChecks(evaluation, REASON_STATIC, 42.0) - ) - ); + evaluation -> evaluationChecks(evaluation, REASON_STATIC, 42.0))); } @TestFactory @@ -80,37 +70,31 @@ Iterable shouldThrowFlagNotFoundOnMissingEnv() { "other", "other", provider -> provider.getBooleanEvaluation("bool_default", true, null), - FlagNotFoundError.class - ), + FlagNotFoundError.class), throwingEvaluationTest( "string_default", "other", "other", provider -> provider.getStringEvaluation("string_default", "value", null), - FlagNotFoundError.class - ), + FlagNotFoundError.class), throwingEvaluationTest( "int_default", "other", "other", provider -> provider.getIntegerEvaluation("int_default", 42, null), - FlagNotFoundError.class - ), + FlagNotFoundError.class), throwingEvaluationTest( "double_default", "other", "other", provider -> provider.getDoubleEvaluation("double_default", 42.0, null), - FlagNotFoundError.class - ), + FlagNotFoundError.class), throwingEvaluationTest( "null_default", "other", "other", provider -> provider.getStringEvaluation("null_default", null, null), - FlagNotFoundError.class - ) - ); + FlagNotFoundError.class)); } @TestFactory @@ -121,16 +105,13 @@ Iterable shouldThrowOnUnparseableValues() { "int_incorrect", "fourty-two", provider -> provider.getIntegerEvaluation("int_incorrect", null, null), - ParseError.class - ), + ParseError.class), throwingEvaluationTest( "double_incorrect", "double_incorrect", "fourty-two", provider -> provider.getDoubleEvaluation("double_incorrect", null, null), - ParseError.class - ) - ); + ParseError.class)); } @Test @@ -143,7 +124,8 @@ void shouldTransformKeyIfConfigured() { EnvironmentGateway gateway = s -> s; EnvVarProvider provider = new EnvVarProvider(gateway, transformer); - String environmentVariableValue = provider.getStringEvaluation(key, "failed", null).getValue(); + String environmentVariableValue = + provider.getStringEvaluation(key, "failed", null).getValue(); assertThat(environmentVariableValue).isEqualTo(expected); } @@ -165,21 +147,17 @@ private DynamicTest evaluationTest( String variableName, String value, Function> callback, - Consumer> checks - ) { - return DynamicTest.dynamicTest( - testName, - () -> { - // Given - final FeatureProvider provider = provider(variableName, value); - - // When - final ProviderEvaluation evaluation = callback.apply(provider); - - // Then - checks.accept(evaluation); - } - ); + Consumer> checks) { + return DynamicTest.dynamicTest(testName, () -> { + // Given + final FeatureProvider provider = provider(variableName, value); + + // When + final ProviderEvaluation evaluation = callback.apply(provider); + + // Then + checks.accept(evaluation); + }); } private DynamicTest throwingEvaluationTest( @@ -187,27 +165,20 @@ private DynamicTest throwingEvaluationTest( String variableName, String value, Function> callback, - Class throwing - ) { - return DynamicTest.dynamicTest( - testName, - () -> { - // Given - final FeatureProvider provider = provider(variableName, value); - - // Then - assertThrows(throwing, () -> { - // When - callback.apply(provider); - }); - } - ); + Class throwing) { + return DynamicTest.dynamicTest(testName, () -> { + // Given + final FeatureProvider provider = provider(variableName, value); + + // Then + assertThrows(throwing, () -> { + // When + callback.apply(provider); + }); + }); } - private FeatureProvider provider( - String variableName, - String value - ) { + private FeatureProvider provider(String variableName, String value) { return new EnvVarProvider(name -> { if (name.equals(variableName)) { return value; diff --git a/providers/env-var/src/test/java/dev/openfeature/contrib/providers/envvar/EnvironmentKeyTransformerTest.java b/providers/env-var/src/test/java/dev/openfeature/contrib/providers/envvar/EnvironmentKeyTransformerTest.java index b3fe71071..cbd2c0201 100644 --- a/providers/env-var/src/test/java/dev/openfeature/contrib/providers/envvar/EnvironmentKeyTransformerTest.java +++ b/providers/env-var/src/test/java/dev/openfeature/contrib/providers/envvar/EnvironmentKeyTransformerTest.java @@ -1,5 +1,7 @@ package dev.openfeature.contrib.providers.envvar; +import static org.assertj.core.api.Assertions.assertThat; + import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.extension.ExtendWith; @@ -7,76 +9,44 @@ import org.junit.jupiter.params.provider.CsvSource; import org.mockito.junit.jupiter.MockitoExtension; -import static org.assertj.core.api.Assertions.assertThat; - @ExtendWith(MockitoExtension.class) class EnvironmentKeyTransformerTest { @ParameterizedTest - @CsvSource({ - ", ", - "a, a", - "A, a", - "ABC_DEF_GHI, abc_def_ghi", - "ABC.DEF.GHI, abc.def.ghi", - "aBc, abc" - }) + @CsvSource({", ", "a, a", "A, a", "ABC_DEF_GHI, abc_def_ghi", "ABC.DEF.GHI, abc.def.ghi", "aBc, abc"}) @DisplayName("should transform keys to lower case prior delegating call to actual gateway") - void shouldTransformKeysToLowerCasePriorDelegatingCallToActualGateway(String originalKey, String expectedTransformedKey) { - String actual = EnvironmentKeyTransformer - .toLowerCaseTransformer() - .transformKey(originalKey); + void shouldTransformKeysToLowerCasePriorDelegatingCallToActualGateway( + String originalKey, String expectedTransformedKey) { + String actual = EnvironmentKeyTransformer.toLowerCaseTransformer().transformKey(originalKey); assertThat(actual).isEqualTo(expectedTransformedKey); } @ParameterizedTest - @CsvSource({ - ", ", - "a, a", - "A, A", - "ABC_DEF_GHI, ABC_DEF_GHI", - "ABC.DEF.GHI, ABC.DEF.GHI", - "aBc, aBc" - }) + @CsvSource({", ", "a, a", "A, A", "ABC_DEF_GHI, ABC_DEF_GHI", "ABC.DEF.GHI, ABC.DEF.GHI", "aBc, aBc"}) @DisplayName("should not transform key when using doNothing transformer") void shouldNotTransformKeyWhenUsingDoNothingTransformer(String originalKey, String expectedTransformedKey) { - String actual = EnvironmentKeyTransformer - .doNothing() - .transformKey(originalKey); + String actual = EnvironmentKeyTransformer.doNothing().transformKey(originalKey); assertThat(actual).isEqualTo(expectedTransformedKey); } @ParameterizedTest - @CsvSource({ - ", ", - "a, a", - "A, a", - "ABC_DEF_GHI, abcDefGhi", - "ABC.DEF.GHI, abc.def.ghi", - "aBc, abc" - }) + @CsvSource({", ", "a, a", "A, a", "ABC_DEF_GHI, abcDefGhi", "ABC.DEF.GHI, abc.def.ghi", "aBc, abc"}) @DisplayName("should transform keys to camel case prior delegating call to actual gateway") - void shouldTransformKeysToCamelCasePriorDelegatingCallToActualGateway(String originalKey, String expectedTransformedKey) { - EnvironmentKeyTransformer transformingGateway = EnvironmentKeyTransformer - .toCamelCaseTransformer(); + void shouldTransformKeysToCamelCasePriorDelegatingCallToActualGateway( + String originalKey, String expectedTransformedKey) { + EnvironmentKeyTransformer transformingGateway = EnvironmentKeyTransformer.toCamelCaseTransformer(); String actual = transformingGateway.transformKey(originalKey); assertThat(actual).isEqualTo(expectedTransformedKey); } @ParameterizedTest - @CsvSource({ - ", ", - "a, A", - "A, A", - "ABC_DEF_GHI, ABC_DEF_GHI", - "ABC.DEF.GHI, ABC.DEF.GHI", - "aBc_abc, ABC_ABC" - }) + @CsvSource({", ", "a, A", "A, A", "ABC_DEF_GHI, ABC_DEF_GHI", "ABC.DEF.GHI, ABC.DEF.GHI", "aBc_abc, ABC_ABC"}) @DisplayName("should transform keys according to given transformation prior delegating call to actual gateway") - void shouldTransformKeysAccordingToGivenPriorDelegatingCallToActualGateway(String originalKey, String expectedTransformedKey) { + void shouldTransformKeysAccordingToGivenPriorDelegatingCallToActualGateway( + String originalKey, String expectedTransformedKey) { EnvironmentKeyTransformer transformingGateway = new EnvironmentKeyTransformer(StringUtils::toRootUpperCase); String actual = transformingGateway.transformKey(originalKey); @@ -85,17 +55,11 @@ void shouldTransformKeysAccordingToGivenPriorDelegatingCallToActualGateway(Strin } @ParameterizedTest - @CsvSource({ - ", ", - "ABC_DEF_GHI, abc.def.ghi", - "ABC.DEF.GHI, abc.def.ghi", - "aBc, abc" - }) + @CsvSource({", ", "ABC_DEF_GHI, abc.def.ghi", "ABC.DEF.GHI, abc.def.ghi", "aBc, abc"}) @DisplayName("should compose transformers") void shouldComposeTransformers(String originalKey, String expectedTransformedKey) { - EnvironmentKeyTransformer transformingGateway = EnvironmentKeyTransformer - .toLowerCaseTransformer() - .andThen(EnvironmentKeyTransformer.replaceUnderscoreWithDotTransformer()); + EnvironmentKeyTransformer transformingGateway = EnvironmentKeyTransformer.toLowerCaseTransformer() + .andThen(EnvironmentKeyTransformer.replaceUnderscoreWithDotTransformer()); String actual = transformingGateway.transformKey(originalKey); @@ -103,16 +67,10 @@ void shouldComposeTransformers(String originalKey, String expectedTransformedKey } @ParameterizedTest - @CsvSource({ - ", ", - "abc-def-ghi, ABC_DEF_GHI", - "abc.def.ghi, ABC.DEF.GHI", - "abc, ABC" - }) + @CsvSource({", ", "abc-def-ghi, ABC_DEF_GHI", "abc.def.ghi, ABC.DEF.GHI", "abc, ABC"}) @DisplayName("should support screaming snake to hyphen case keys") void shouldSupportScreamingSnakeToHyphenCaseKeys(String originalKey, String expectedTransformedKey) { - EnvironmentKeyTransformer transformingGateway = EnvironmentKeyTransformer - .hyphenCaseToScreamingSnake(); + EnvironmentKeyTransformer transformingGateway = EnvironmentKeyTransformer.hyphenCaseToScreamingSnake(); String actual = transformingGateway.transformKey(originalKey); @@ -120,20 +78,13 @@ void shouldSupportScreamingSnakeToHyphenCaseKeys(String originalKey, String expe } @ParameterizedTest - @CsvSource({ - ", ", - "abc-def-ghi, abc-def-ghi", - "abc.def.ghi, abc_def_ghi", - "abc, abc" - }) + @CsvSource({", ", "abc-def-ghi, abc-def-ghi", "abc.def.ghi, abc_def_ghi", "abc, abc"}) @DisplayName("should support replacing dot with underscore") void shouldSupportReplacingDotWithUnderscore(String originalKey, String expectedTransformedKey) { - EnvironmentKeyTransformer transformingGateway = EnvironmentKeyTransformer - .replaceDotWithUnderscoreTransformer(); + EnvironmentKeyTransformer transformingGateway = EnvironmentKeyTransformer.replaceDotWithUnderscoreTransformer(); String actual = transformingGateway.transformKey(originalKey); assertThat(actual).isEqualTo(expectedTransformedKey); } - } diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/Config.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/Config.java index 2df30ec6d..4c3fba4c1 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/Config.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/Config.java @@ -1,13 +1,10 @@ package dev.openfeature.contrib.providers.flagd; import dev.openfeature.contrib.providers.flagd.resolver.grpc.cache.CacheType; -import lombok.extern.slf4j.Slf4j; - import java.util.function.Function; +import lombok.extern.slf4j.Slf4j; -/** - * Helper class to hold configuration default values. - */ +/** Helper class to hold configuration default values. */ @Slf4j public final class Config { static final Resolver DEFAULT_RESOLVER_TYPE = Resolver.RPC; @@ -95,22 +92,20 @@ static Resolver fromValueProvider(Function provider) { } } - /** - * intermediate interface to unify deprecated Evaluator and new Resolver. - **/ + /** intermediate interface to unify deprecated Evaluator and new Resolver. */ public interface EvaluatorType { String asString(); } /** - * flagd evaluator type. - * Deprecated : Please use {@code Config.Resolver}, which is a drop-in replacement of this. + * flagd evaluator type. Deprecated : Please use {@code Config.Resolver}, which is a drop-in + * replacement of this. */ @Deprecated public enum Evaluator implements EvaluatorType { /** - * This is the default resolver type, which connects to flagd instance with flag evaluation gRPC contract. - * Evaluations are performed remotely. + * This is the default resolver type, which connects to flagd instance with flag evaluation gRPC + * contract. Evaluations are performed remotely. */ RPC { public String asString() { @@ -118,26 +113,21 @@ public String asString() { } }, /** - * This is the in-process resolving type, where flags are fetched with flag sync gRPC contract and stored - * locally for in-process evaluation. - * Evaluations are preformed in-process. + * This is the in-process resolving type, where flags are fetched with flag sync gRPC contract + * and stored locally for in-process evaluation. Evaluations are preformed in-process. */ IN_PROCESS { public String asString() { return RESOLVER_IN_PROCESS; } } - } - - /** - * flagd Resolver type. - */ + /** flagd Resolver type. */ public enum Resolver implements EvaluatorType { /** - * This is the default resolver type, which connects to flagd instance with flag evaluation gRPC contract. - * Evaluations are performed remotely. + * This is the default resolver type, which connects to flagd instance with flag evaluation gRPC + * contract. Evaluations are performed remotely. */ RPC { public String asString() { @@ -145,9 +135,8 @@ public String asString() { } }, /** - * This is the in-process resolving type, where flags are fetched with flag sync gRPC contract and stored - * locally for in-process evaluation. - * Evaluations are preformed in-process. + * This is the in-process resolving type, where flags are fetched with flag sync gRPC contract + * and stored locally for in-process evaluation. Evaluations are preformed in-process. */ IN_PROCESS { public String asString() { diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdOptions.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdOptions.java index 8741dd905..4ba459e4d 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdOptions.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdOptions.java @@ -3,160 +3,124 @@ import static dev.openfeature.contrib.providers.flagd.Config.fallBackToEnvOrDefault; import static dev.openfeature.contrib.providers.flagd.Config.fromValueProvider; -import java.util.function.Function; - import dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.Connector; import dev.openfeature.sdk.EvaluationContext; import dev.openfeature.sdk.ImmutableContext; import dev.openfeature.sdk.Structure; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; +import java.util.function.Function; import lombok.Builder; import lombok.Getter; -/** - * FlagdOptions is a builder to build flagd provider options. - */ +/** FlagdOptions is a builder to build flagd provider options. */ @Builder @Getter @SuppressWarnings("PMD.TooManyStaticImports") public class FlagdOptions { - /** - * flagd resolving type. - */ + /** flagd resolving type. */ private Config.EvaluatorType resolverType; - /** - * flagd connection host. - */ + /** flagd connection host. */ @Builder.Default private String host = fallBackToEnvOrDefault(Config.HOST_ENV_VAR_NAME, Config.DEFAULT_HOST); - /** - * flagd connection port. - */ + /** flagd connection port. */ private int port; - /** - * Use TLS connectivity. - */ + /** Use TLS connectivity. */ @Builder.Default private boolean tls = Boolean.parseBoolean(fallBackToEnvOrDefault(Config.TLS_ENV_VAR_NAME, Config.DEFAULT_TLS)); - /** - * TLS certificate overriding if TLS connectivity is used. - */ + /** TLS certificate overriding if TLS connectivity is used. */ @Builder.Default private String certPath = fallBackToEnvOrDefault(Config.SERVER_CERT_PATH_ENV_VAR_NAME, null); - /** - * Unix socket path to flagd. - */ + /** Unix socket path to flagd. */ @Builder.Default private String socketPath = fallBackToEnvOrDefault(Config.SOCKET_PATH_ENV_VAR_NAME, null); - /** - * Cache type to use. Supports - lru, disabled. - */ + /** Cache type to use. Supports - lru, disabled. */ @Builder.Default private String cacheType = fallBackToEnvOrDefault(Config.CACHE_ENV_VAR_NAME, Config.DEFAULT_CACHE); - /** - * Max cache size. - */ + /** Max cache size. */ @Builder.Default - private int maxCacheSize = fallBackToEnvOrDefault(Config.MAX_CACHE_SIZE_ENV_VAR_NAME, - Config.DEFAULT_MAX_CACHE_SIZE); + private int maxCacheSize = + fallBackToEnvOrDefault(Config.MAX_CACHE_SIZE_ENV_VAR_NAME, Config.DEFAULT_MAX_CACHE_SIZE); - /** - * Max event stream connection retries. - */ + /** Max event stream connection retries. */ @Builder.Default - private int maxEventStreamRetries = fallBackToEnvOrDefault(Config.MAX_EVENT_STREAM_RETRIES_ENV_VAR_NAME, - Config.DEFAULT_MAX_EVENT_STREAM_RETRIES); + private int maxEventStreamRetries = fallBackToEnvOrDefault( + Config.MAX_EVENT_STREAM_RETRIES_ENV_VAR_NAME, Config.DEFAULT_MAX_EVENT_STREAM_RETRIES); - /** - * Backoff interval in milliseconds. - */ + /** Backoff interval in milliseconds. */ @Builder.Default - private int retryBackoffMs = fallBackToEnvOrDefault(Config.BASE_EVENT_STREAM_RETRY_BACKOFF_MS_ENV_VAR_NAME, - Config.BASE_EVENT_STREAM_RETRY_BACKOFF_MS); + private int retryBackoffMs = fallBackToEnvOrDefault( + Config.BASE_EVENT_STREAM_RETRY_BACKOFF_MS_ENV_VAR_NAME, Config.BASE_EVENT_STREAM_RETRY_BACKOFF_MS); /** - * Connection deadline in milliseconds. - * For RPC resolving, this is the deadline to connect to flagd for flag - * evaluation. - * For in-process resolving, this is the deadline for sync stream termination. + * Connection deadline in milliseconds. For RPC resolving, this is the deadline to connect to + * flagd for flag evaluation. For in-process resolving, this is the deadline for sync stream + * termination. */ @Builder.Default private int deadline = fallBackToEnvOrDefault(Config.DEADLINE_MS_ENV_VAR_NAME, Config.DEFAULT_DEADLINE); /** - * Streaming connection deadline in milliseconds. - * Set to 0 to disable the deadline. - * Defaults to 600000 (10 minutes); recommended to prevent infrastructure from killing idle connections. + * Streaming connection deadline in milliseconds. Set to 0 to disable the deadline. Defaults to + * 600000 (10 minutes); recommended to prevent infrastructure from killing idle connections. */ @Builder.Default - private int streamDeadlineMs = fallBackToEnvOrDefault(Config.STREAM_DEADLINE_MS_ENV_VAR_NAME, - Config.DEFAULT_STREAM_DEADLINE_MS); + private int streamDeadlineMs = + fallBackToEnvOrDefault(Config.STREAM_DEADLINE_MS_ENV_VAR_NAME, Config.DEFAULT_STREAM_DEADLINE_MS); - /** - * Selector to be used with flag sync gRPC contract. - **/ + /** Selector to be used with flag sync gRPC contract. */ @Builder.Default private String selector = fallBackToEnvOrDefault(Config.SOURCE_SELECTOR_ENV_VAR_NAME, null); - /** - * gRPC client KeepAlive in milliseconds. Disabled with 0. - * Defaults to 0 (disabled). - * - **/ + /** gRPC client KeepAlive in milliseconds. Disabled with 0. Defaults to 0 (disabled). */ @Builder.Default - private long keepAlive = fallBackToEnvOrDefault(Config.KEEP_ALIVE_MS_ENV_VAR_NAME, + private long keepAlive = fallBackToEnvOrDefault( + Config.KEEP_ALIVE_MS_ENV_VAR_NAME, fallBackToEnvOrDefault(Config.KEEP_ALIVE_MS_ENV_VAR_NAME_OLD, Config.DEFAULT_KEEP_ALIVE)); /** - * File source of flags to be used by offline mode. - * Setting this enables the offline mode of the in-process provider. + * File source of flags to be used by offline mode. Setting this enables the offline mode of the + * in-process provider. */ @Builder.Default private String offlineFlagSourcePath = fallBackToEnvOrDefault(Config.OFFLINE_SOURCE_PATH, null); - /** * gRPC custom target string. * - *

Setting this will allow user to use custom gRPC name resolver at present - * we are supporting all core resolver along with a custom resolver for envoy proxy - * resolution. For more visit (https://grpc.io/docs/guides/custom-name-resolution/) + *

Setting this will allow user to use custom gRPC name resolver at present we are supporting + * all core resolver along with a custom resolver for envoy proxy resolution. For more visit + * (https://grpc.io/docs/guides/custom-name-resolution/) */ @Builder.Default private String targetUri = fallBackToEnvOrDefault(Config.TARGET_URI_ENV_VAR_NAME, null); - /** - * Function providing an EvaluationContext to mix into every evaluations. - * The sync-metadata response + * Function providing an EvaluationContext to mix into every evaluations. The sync-metadata + * response * (https://buf.build/open-feature/flagd/docs/main:flagd.sync.v1#flagd.sync.v1.GetMetadataResponse), - * represented as a {@link dev.openfeature.sdk.Structure}, is passed as an - * argument. - * This function runs every time the provider (re)connects, and its result is cached and used in every evaluation. - * By default, the entire sync response (converted to a Structure) is used. + * represented as a {@link dev.openfeature.sdk.Structure}, is passed as an argument. This function + * runs every time the provider (re)connects, and its result is cached and used in every + * evaluation. By default, the entire sync response (converted to a Structure) is used. */ @Builder.Default - private Function contextEnricher = (syncMetadata) -> new ImmutableContext( - syncMetadata.asMap()); + private Function contextEnricher = + (syncMetadata) -> new ImmutableContext(syncMetadata.asMap()); - /** - * Inject a Custom Connector for fetching flags. - */ + /** Inject a Custom Connector for fetching flags. */ private Connector customConnector; /** - * Inject OpenTelemetry for the library runtime. Providing sdk will initiate - * distributed tracing for flagd grpc - * connectivity. + * Inject OpenTelemetry for the library runtime. Providing sdk will initiate distributed tracing + * for flagd grpc connectivity. */ private OpenTelemetry openTelemetry; @@ -175,14 +139,11 @@ public FlagdOptions build() { }; } - /** - * Overload default lombok builder. - */ + /** Overload default lombok builder. */ public static class FlagdOptionsBuilder { /** - * Enable OpenTelemetry instance extraction from GlobalOpenTelemetry. Note that, - * this is only useful if global - * configurations are registered. + * Enable OpenTelemetry instance extraction from GlobalOpenTelemetry. Note that, this is only + * useful if global configurations are registered. */ public FlagdOptionsBuilder withGlobalTelemetry(final boolean b) { if (b) { @@ -197,8 +158,8 @@ void prebuild() { } if (port == 0) { - port = Integer - .parseInt(fallBackToEnvOrDefault(Config.PORT_ENV_VAR_NAME, determineDefaultPortForResolver())); + port = Integer.parseInt( + fallBackToEnvOrDefault(Config.PORT_ENV_VAR_NAME, determineDefaultPortForResolver())); } } 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 7b451ec91..5f3d8a361 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,10 +1,5 @@ package dev.openfeature.contrib.providers.flagd; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.function.Function; - import dev.openfeature.contrib.providers.flagd.resolver.Resolver; import dev.openfeature.contrib.providers.flagd.resolver.common.ConnectionEvent; import dev.openfeature.contrib.providers.flagd.resolver.grpc.GrpcResolver; @@ -20,13 +15,15 @@ import dev.openfeature.sdk.ProviderEventDetails; import dev.openfeature.sdk.Structure; import dev.openfeature.sdk.Value; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; import lombok.extern.slf4j.Slf4j; -/** - * OpenFeature provider for flagd. - */ +/** OpenFeature provider for flagd. */ @Slf4j -@SuppressWarnings({ "PMD.TooManyStaticImports", "checkstyle:NoFinalizer" }) +@SuppressWarnings({"PMD.TooManyStaticImports", "checkstyle:NoFinalizer"}) public class FlagdProvider extends EventProvider { private Function contextEnricher; private static final String FLAGD_PROVIDER = "flagd"; @@ -41,9 +38,7 @@ protected final void finalize() { // DO NOT REMOVE, spotbugs: CT_CONSTRUCTOR_THROW } - /** - * Create a new FlagdProvider instance with default options. - */ + /** Create a new FlagdProvider instance with default options. */ public FlagdProvider() { this(FlagdOptions.builder().build()); } @@ -56,11 +51,11 @@ public FlagdProvider() { public FlagdProvider(final FlagdOptions options) { switch (options.getResolverType().asString()) { case Config.RESOLVER_IN_PROCESS: - this.flagResolver = new InProcessResolver(options, this::isConnected, - this::onConnectionEvent); + this.flagResolver = new InProcessResolver(options, this::isConnected, this::onConnectionEvent); break; case Config.RESOLVER_RPC: - this.flagResolver = new GrpcResolver(options, + this.flagResolver = new GrpcResolver( + options, new Cache(options.getCacheType(), options.getMaxCacheSize()), this::isConnected, this::onConnectionEvent); @@ -134,12 +129,10 @@ public ProviderEvaluation getObjectEvaluation(String key, Value defaultVa } /** - * An unmodifiable view of a Structure representing the latest result of the - * SyncMetadata. - * Set on initial connection and updated with every reconnection. - * see: + * An unmodifiable view of a Structure representing the latest result of the SyncMetadata. Set on + * initial connection and updated with every reconnection. see: * https://buf.build/open-feature/flagd/docs/main:flagd.sync.v1#flagd.sync.v1.FlagSyncService.GetMetadata - * + * * @return Object map representing sync metadata */ protected Structure getSyncMetadata() { @@ -148,6 +141,7 @@ protected Structure getSyncMetadata() { /** * The updated context mixed into all evaluations based on the sync-metadata. + * * @return context */ EvaluationContext getEnrichedContext() { @@ -169,21 +163,26 @@ private void onConnectionEvent(ConnectionEvent connectionEvent) { log.debug("Configuration changed"); ProviderEventDetails details = ProviderEventDetails.builder() .flagsChanged(connectionEvent.getFlagsChanged()) - .message("configuration changed").build(); + .message("configuration changed") + .build(); this.emitProviderConfigurationChanged(details); return; } // there was an error if (initialized && previous && !current) { log.debug("There has been an error"); - ProviderEventDetails details = ProviderEventDetails.builder().message("there has been an error").build(); + ProviderEventDetails details = ProviderEventDetails.builder() + .message("there has been an error") + .build(); this.emitProviderError(details); return; } // we recovered from an error if (initialized && !previous && current) { log.debug("Recovered from error"); - ProviderEventDetails details = ProviderEventDetails.builder().message("recovered from error").build(); + ProviderEventDetails details = ProviderEventDetails.builder() + .message("recovered from error") + .build(); this.emitProviderReady(details); this.emitProviderConfigurationChanged(details); } diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/SyncMetadataHook.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/SyncMetadataHook.java index a7ee75d0b..99d93e855 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/SyncMetadataHook.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/SyncMetadataHook.java @@ -1,12 +1,11 @@ package dev.openfeature.contrib.providers.flagd; -import java.util.Map; -import java.util.Optional; -import java.util.function.Supplier; - import dev.openfeature.sdk.EvaluationContext; import dev.openfeature.sdk.Hook; import dev.openfeature.sdk.HookContext; +import java.util.Map; +import java.util.Optional; +import java.util.function.Supplier; class SyncMetadataHook implements Hook { @@ -16,11 +15,9 @@ class SyncMetadataHook implements Hook { this.contextSupplier = contextSupplier; } - /** - * Return the context adapted from the sync-metadata provided by the supplier. - */ + /** Return the context adapted from the sync-metadata provided by the supplier. */ @Override public Optional before(HookContext ctx, Map hints) { return Optional.ofNullable(contextSupplier.get()); } -} \ No newline at end of file +} diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/Resolver.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/Resolver.java index 45d82b66b..1f9106501 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/Resolver.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/Resolver.java @@ -4,9 +4,7 @@ import dev.openfeature.sdk.ProviderEvaluation; import dev.openfeature.sdk.Value; -/** - * Abstraction that resolves flag values in from some source. - */ +/** Abstraction that resolves flag values in from some source. */ public interface Resolver { void init() throws Exception; diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/ChannelBuilder.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/ChannelBuilder.java index 587f59cc7..51cb3e43f 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/ChannelBuilder.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/ChannelBuilder.java @@ -12,20 +12,16 @@ import io.netty.channel.epoll.EpollEventLoopGroup; import io.netty.channel.unix.DomainSocketAddress; import io.netty.handler.ssl.SslContextBuilder; - -import javax.net.ssl.SSLException; import java.io.File; import java.net.URI; import java.net.URISyntaxException; import java.util.concurrent.TimeUnit; +import javax.net.ssl.SSLException; -/** - * gRPC channel builder helper. - */ +/** gRPC channel builder helper. */ public class ChannelBuilder { - private ChannelBuilder() { - } + private ChannelBuilder() {} /** * This method is a helper to build a {@link ManagedChannel} from provided {@link FlagdOptions}. @@ -33,7 +29,8 @@ private ChannelBuilder() { @SuppressFBWarnings(value = "PATH_TRAVERSAL_IN", justification = "certificate path is a user input") public static ManagedChannel nettyChannel(final FlagdOptions options) { - // keepAliveTime: Long.MAX_VALUE disables keepAlive; very small values are increased automatically + // keepAliveTime: Long.MAX_VALUE disables keepAlive; very small values are increased + // automatically long keepAliveMs = options.getKeepAlive() == 0 ? Long.MAX_VALUE : options.getKeepAlive(); // we have a socket path specified, build a channel with a unix socket @@ -43,8 +40,7 @@ public static ManagedChannel nettyChannel(final FlagdOptions options) { throw new IllegalStateException("unix socket cannot be used", Epoll.unavailabilityCause()); } - return NettyChannelBuilder - .forAddress(new DomainSocketAddress(options.getSocketPath())) + return NettyChannelBuilder.forAddress(new DomainSocketAddress(options.getSocketPath())) .keepAliveTime(keepAliveMs, TimeUnit.MILLISECONDS) .eventLoopGroup(new EpollEventLoopGroup()) .channelType(EpollDomainSocketChannel.class) @@ -62,12 +58,10 @@ public static ManagedChannel nettyChannel(final FlagdOptions options) { // default to current `dns` resolution i.e. :, if valid / supported // target string use the user provided target uri. final String defaultTarget = String.format("%s:%s", options.getHost(), options.getPort()); - final String targetUri = isValidTargetUri(options.getTargetUri()) ? options.getTargetUri() : - defaultTarget; + final String targetUri = isValidTargetUri(options.getTargetUri()) ? options.getTargetUri() : defaultTarget; - final NettyChannelBuilder builder = NettyChannelBuilder - .forTarget(targetUri) - .keepAliveTime(keepAliveMs, TimeUnit.MILLISECONDS); + final NettyChannelBuilder builder = + NettyChannelBuilder.forTarget(targetUri).keepAliveTime(keepAliveMs, TimeUnit.MILLISECONDS); if (options.isTls()) { SslContextBuilder sslContext = GrpcSslContexts.forClient(); @@ -95,21 +89,22 @@ public static ManagedChannel nettyChannel(final FlagdOptions options) { sslConfigException.initCause(ssle); throw sslConfigException; } catch (IllegalArgumentException argumentException) { - GenericConfigException genericConfigException = new GenericConfigException( - "Error with gRPC target string configuration"); + GenericConfigException genericConfigException = + new GenericConfigException("Error with gRPC target string configuration"); genericConfigException.initCause(argumentException); throw genericConfigException; } } - private static boolean isValidTargetUri(String targetUri) { + private static boolean isValidTargetUri(String targetUri) { if (targetUri == null) { return false; } try { final String scheme = new URI(targetUri).getScheme(); - if (scheme.equals(SupportedScheme.ENVOY.getScheme()) || scheme.equals(SupportedScheme.DNS.getScheme()) + if (scheme.equals(SupportedScheme.ENVOY.getScheme()) + || scheme.equals(SupportedScheme.DNS.getScheme()) || scheme.equals(SupportedScheme.XDS.getScheme()) || scheme.equals(SupportedScheme.UDS.getScheme())) { return true; @@ -136,6 +131,5 @@ private static boolean isEnvoyTarget(String targetUri) { } return false; - } } diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/ConnectionEvent.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/ConnectionEvent.java index d48b9e49e..994ccdc9c 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/ConnectionEvent.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/ConnectionEvent.java @@ -1,28 +1,27 @@ package dev.openfeature.contrib.providers.flagd.resolver.common; -import java.util.Collections; -import java.util.List; - import dev.openfeature.sdk.ImmutableStructure; import dev.openfeature.sdk.Structure; +import java.util.Collections; +import java.util.List; import lombok.AllArgsConstructor; import lombok.Getter; /** - * Event payload for a - * {@link dev.openfeature.contrib.providers.flagd.resolver.Resolver} connection + * Event payload for a {@link dev.openfeature.contrib.providers.flagd.resolver.Resolver} connection * state change event. */ @AllArgsConstructor public class ConnectionEvent { @Getter private final boolean connected; + private final List flagsChanged; private final Structure syncMetadata; /** * Construct a new ConnectionEvent. - * + * * @param connected status of the connection */ public ConnectionEvent(boolean connected) { @@ -31,8 +30,8 @@ public ConnectionEvent(boolean connected) { /** * Construct a new ConnectionEvent. - * - * @param connected status of the connection + * + * @param connected status of the connection * @param flagsChanged list of flags changed */ public ConnectionEvent(boolean connected, List flagsChanged) { @@ -41,8 +40,8 @@ public ConnectionEvent(boolean connected, List flagsChanged) { /** * Construct a new ConnectionEvent. - * - * @param connected status of the connection + * + * @param connected status of the connection * @param syncMetadata sync.getMetadata */ public ConnectionEvent(boolean connected, Structure syncMetadata) { @@ -51,7 +50,7 @@ public ConnectionEvent(boolean connected, Structure syncMetadata) { /** * Get changed flags. - * + * * @return an unmodifiable view of the changed flags */ public List getFlagsChanged() { @@ -60,7 +59,7 @@ public List getFlagsChanged() { /** * Get changed sync metadata represented as SDK structure type. - * + * * @return an unmodifiable view of the sync metadata */ public Structure getSyncMetadata() { diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/Convert.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/Convert.java index e33f59736..5d57b6b03 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/Convert.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/Convert.java @@ -1,30 +1,26 @@ package dev.openfeature.contrib.providers.flagd.resolver.common; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - import com.google.protobuf.Descriptors; import com.google.protobuf.ListValue; import com.google.protobuf.Message; import com.google.protobuf.NullValue; import com.google.protobuf.Struct; - import dev.openfeature.sdk.EvaluationContext; import dev.openfeature.sdk.ImmutableContext; import dev.openfeature.sdk.MutableStructure; import dev.openfeature.sdk.Structure; import dev.openfeature.sdk.Value; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; -/** - * gRPC type conversion utils. - */ +/** gRPC type conversion utils. */ public class Convert { /** * Converts a protobuf struct to EvaluationContext. - * + * * @param struct profobuf struct to convert * @return a context */ @@ -34,16 +30,12 @@ public static EvaluationContext convertProtobufStructToContext(final Struct stru return new ImmutableContext(values); } - /** - * Recursively convert protobuf structure to openfeature value. - */ + /** Recursively convert protobuf structure to openfeature value. */ public static Value convertObjectResponse(Struct protobuf) { return convertProtobufMap(protobuf.getFieldsMap()); } - /** - * Recursively convert the Evaluation context to a protobuf structure. - */ + /** Recursively convert the Evaluation context to a protobuf structure. */ public static Struct convertContext(EvaluationContext ctx) { Map ctxMap = ctx.asMap(); // asMap() does not provide explicitly set targeting key (ex:- new @@ -54,9 +46,7 @@ public static Struct convertContext(EvaluationContext ctx) { return convertMap(ctxMap).getStructValue(); } - /** - * Convert any openfeature value to a protobuf value. - */ + /** Convert any openfeature value to a protobuf value. */ public static com.google.protobuf.Value convertAny(Value value) { if (value.isList()) { return convertList(value.asList()); @@ -67,9 +57,7 @@ public static com.google.protobuf.Value convertAny(Value value) { } } - /** - * Convert any protobuf value to {@link Value}. - */ + /** Convert any protobuf value to {@link Value}. */ public static Value convertAny(com.google.protobuf.Value protobuf) { if (protobuf.hasListValue()) { return convertList(protobuf.getListValue()); @@ -80,9 +68,7 @@ public static Value convertAny(com.google.protobuf.Value protobuf) { } } - /** - * Convert OpenFeature map to protobuf {@link com.google.protobuf.Value}. - */ + /** Convert OpenFeature map to protobuf {@link com.google.protobuf.Value}. */ public static com.google.protobuf.Value convertMap(Map map) { Map values = new HashMap<>(); @@ -90,23 +76,16 @@ public static com.google.protobuf.Value convertMap(Map map) { Value value = map.get(key); values.put(key, convertAny(value)); }); - Struct struct = Struct.newBuilder() - .putAllFields(values).build(); + Struct struct = Struct.newBuilder().putAllFields(values).build(); return com.google.protobuf.Value.newBuilder().setStructValue(struct).build(); } - /** - * Convert protobuf map with {@link com.google.protobuf.Value} to OpenFeature - * map. - */ + /** Convert protobuf map with {@link com.google.protobuf.Value} to OpenFeature map. */ public static Value convertProtobufMap(Map map) { return new Value(convertProtobufMapToStructure(map)); } - /** - * Convert protobuf map with {@link com.google.protobuf.Value} to OpenFeature - * map. - */ + /** Convert protobuf map with {@link com.google.protobuf.Value} to OpenFeature map. */ public static Structure convertProtobufMapToStructure(Map map) { Map values = new HashMap<>(); @@ -117,28 +96,21 @@ public static Structure convertProtobufMapToStructure(Map values) { ListValue list = ListValue.newBuilder() - .addAllValues(values.stream() - .map(v -> convertAny(v)).collect(Collectors.toList())) + .addAllValues(values.stream().map(v -> convertAny(v)).collect(Collectors.toList())) .build(); return com.google.protobuf.Value.newBuilder().setListValue(list).build(); } - /** - * Convert protobuf list to OpenFeature {@link com.google.protobuf.Value}. - */ + /** Convert protobuf list to OpenFeature {@link com.google.protobuf.Value}. */ public static Value convertList(ListValue protobuf) { - return new Value(protobuf.getValuesList().stream().map(p -> convertAny(p)).collect(Collectors.toList())); + return new Value( + protobuf.getValuesList().stream().map(p -> convertAny(p)).collect(Collectors.toList())); } - /** - * Convert OpenFeature {@link Value} to protobuf - * {@link com.google.protobuf.Value}. - */ + /** Convert OpenFeature {@link Value} to protobuf {@link com.google.protobuf.Value}. */ public static com.google.protobuf.Value convertPrimitive(Value value) { com.google.protobuf.Value.Builder builder = com.google.protobuf.Value.newBuilder(); @@ -154,10 +126,7 @@ public static com.google.protobuf.Value convertPrimitive(Value value) { return builder.build(); } - /** - * Convert protobuf {@link com.google.protobuf.Value} to OpenFeature - * {@link Value}. - */ + /** Convert protobuf {@link com.google.protobuf.Value} to OpenFeature {@link Value}. */ public static Value convertPrimitive(com.google.protobuf.Value protobuf) { final Value value; if (protobuf.hasBoolValue()) { @@ -175,10 +144,10 @@ public static Value convertPrimitive(com.google.protobuf.Value protobuf) { /** * Get the specified protobuf field from the message. - * + * * @param type * @param message protobuf message - * @param name field name + * @param name field name * @return field value */ public static T getField(Message message, String name) { @@ -187,9 +156,9 @@ public static T getField(Message message, String name) { /** * Get the specified protobuf field descriptor from the message. - * + * * @param message protobuf message - * @param name field name + * @param name field name * @return field descriptor */ public static Descriptors.FieldDescriptor getFieldDescriptor(Message message, String name) { diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/FlagdGrpcInterceptor.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/FlagdGrpcInterceptor.java index 8926e1a68..656859660 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/FlagdGrpcInterceptor.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/FlagdGrpcInterceptor.java @@ -10,13 +10,12 @@ import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.context.Context; import io.opentelemetry.context.propagation.TextMapSetter; - import javax.annotation.Nonnull; import javax.annotation.Nullable; /** - * FlagdGrpcInterceptor is an interceptor for grpc communication from java-sdk to flagd. - * credits + * FlagdGrpcInterceptor is an interceptor for grpc communication from java-sdk to flagd. credits */ public final class FlagdGrpcInterceptor implements ClientInterceptor { private static final TextMapSetter SETTER = new Setter(); @@ -32,8 +31,9 @@ public FlagdGrpcInterceptor(@Nonnull final OpenTelemetry openTelemetry) { this.openTelemetry = openTelemetry; } - @Override public ClientCall interceptCall(MethodDescriptor methodDescriptor, - CallOptions callOptions, Channel channel) { + @Override + public ClientCall interceptCall( + MethodDescriptor methodDescriptor, CallOptions callOptions, Channel channel) { final ClientCall call = channel.newCall(methodDescriptor, callOptions); @@ -46,9 +46,7 @@ public void start(Listener responseListener, Metadata headers) { }; } - /** - * Setter implements TextMapSetter with carrier check. - */ + /** Setter implements TextMapSetter with carrier check. */ static class Setter implements TextMapSetter { @Override public void set(@Nullable Metadata carrier, String key, String value) { diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/GenericConfigException.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/GenericConfigException.java index 8bce152b2..0015305ad 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/GenericConfigException.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/GenericConfigException.java @@ -1,9 +1,6 @@ package dev.openfeature.contrib.providers.flagd.resolver.common; -/** - * Custom exception for invalid gRPC configurations. - */ - +/** Custom exception for invalid gRPC configurations. */ public class GenericConfigException extends RuntimeException { public GenericConfigException(String message) { super(message); diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/SslConfigException.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/SslConfigException.java index ea0b2beda..be2461bc3 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/SslConfigException.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/SslConfigException.java @@ -1,8 +1,6 @@ package dev.openfeature.contrib.providers.flagd.resolver.common; -/** - * Custom exception for invalid SSL configurations. - */ +/** Custom exception for invalid SSL configurations. */ public class SslConfigException extends RuntimeException { public SslConfigException(String message) { super(message); diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/SupportedScheme.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/SupportedScheme.java index 74bc266c6..69cbfe481 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/SupportedScheme.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/SupportedScheme.java @@ -4,7 +4,10 @@ @Getter enum SupportedScheme { - ENVOY("envoy"), DNS("dns"), XDS("xds"), UDS("uds"); + ENVOY("envoy"), + DNS("dns"), + XDS("xds"), + UDS("uds"); private final String scheme; diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/Util.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/Util.java index 3f9d8981f..d634f0745 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/Util.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/Util.java @@ -1,21 +1,17 @@ package dev.openfeature.contrib.providers.flagd.resolver.common; -import java.util.function.Supplier; - import dev.openfeature.sdk.exceptions.GeneralError; +import java.util.function.Supplier; -/** - * Utils for flagd resolvers. - */ +/** Utils for flagd resolvers. */ public class Util { - private Util() { - } + private Util() {} /** * A helper to block the caller for given conditions. - * - * @param deadline number of milliseconds to block + * + * @param deadline number of milliseconds to block * @param connectedSupplier func to check for status true * @throws InterruptedException if interrupted */ @@ -25,9 +21,8 @@ public static void busyWaitAndCheck(final Long deadline, final Supplier do { if (deadline <= System.currentTimeMillis() - start) { - throw new GeneralError( - String.format("Deadline exceeded. Condition did not complete within the %d deadline", - deadline)); + throw new GeneralError(String.format( + "Deadline exceeded. Condition did not complete within the %d deadline", deadline)); } Thread.sleep(50L); diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/BackoffService.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/BackoffService.java index ccb62c37f..1bf183c69 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/BackoffService.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/BackoffService.java @@ -1,12 +1,9 @@ package dev.openfeature.contrib.providers.flagd.resolver.common.backoff; -import lombok.Getter; - import java.util.concurrent.ThreadLocalRandom; +import lombok.Getter; -/** - * A service that provides backoff functionality. - */ +/** A service that provides backoff functionality. */ public class BackoffService { public static final int DEFAULT_MAX_JITTER = 0x1 << 8; // 256; Random likes boundaries that are a power of 2 @@ -17,8 +14,8 @@ public class BackoffService { private final int maxJitter; /** - * Creates a new BackoffService with the given strategy and default maximum jitter. - * The default maximum jitter is 256. + * Creates a new BackoffService with the given strategy and default maximum jitter. The default + * maximum jitter is 256. * * @param strategy The backoff strategy to use */ @@ -38,8 +35,8 @@ public BackoffService(BackoffStrategy strategy, int maxJitter) { } /** - * Returns the current backoff time in milliseconds. - * This backoff time will be used in waitUntilNextAttempt. + * Returns the current backoff time in milliseconds. This backoff time will be used in + * waitUntilNextAttempt. * * @return the current backoff time in milliseconds */ @@ -60,15 +57,14 @@ public long getRandomJitter() { return ThreadLocalRandom.current().nextInt(maxJitter); } - /** - * Resets the backoff strategy to its initial state. - */ + /** Resets the backoff strategy to its initial state. */ public void reset() { strategy.reset(); } /** * Returns whether the backoff strategy has more attempts left. + * * @return true if the backoff strategy has more attempts left, false otherwise */ public boolean shouldRetry() { @@ -76,8 +72,8 @@ public boolean shouldRetry() { } /** - * Bolocks the current thread until the next attempt should be made. - * The time to wait is determined by the backoff strategy and a random jitter. + * Bolocks the current thread until the next attempt should be made. The time to wait is + * determined by the backoff strategy and a random jitter. * * @throws InterruptedException if the thread is interrupted while waiting */ diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/BackoffStrategies.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/BackoffStrategies.java index 9ed30cbc0..3827a7617 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/BackoffStrategies.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/BackoffStrategies.java @@ -1,11 +1,8 @@ package dev.openfeature.contrib.providers.flagd.resolver.common.backoff; -/** - * A factory class for creating common backoff strategies. - */ +/** A factory class for creating common backoff strategies. */ public class BackoffStrategies { - private BackoffStrategies() { - } + private BackoffStrategies() {} public static BackoffStrategy exponentialTimeBackoff(long initialBackoffMillis) { return new ExponentialTimeBackoff(initialBackoffMillis); @@ -23,8 +20,8 @@ public static BackoffStrategy noBackoff() { return new ConstantTimeBackoff(0L); } - public static BackoffStrategy maxRetriesWithExponentialTimeBackoffStrategy(int maxRetries, - long initialBackoffMillis) { + public static BackoffStrategy maxRetriesWithExponentialTimeBackoffStrategy( + int maxRetries, long initialBackoffMillis) { return new NumberOfRetriesBackoff(maxRetries, exponentialTimeBackoff(initialBackoffMillis)); } } diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/BackoffStrategy.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/BackoffStrategy.java index cab831938..31224de4b 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/BackoffStrategy.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/BackoffStrategy.java @@ -1,13 +1,11 @@ package dev.openfeature.contrib.providers.flagd.resolver.common.backoff; -/** - * A strategy interface for determining how long to backoff before retrying a failed operation. - */ +/** A strategy interface for determining how long to backoff before retrying a failed operation. */ public interface BackoffStrategy { /** - * The current backoff time in milliseconds. - * This value should be used to determine how long to wait before retrying. + * The current backoff time in milliseconds. This value should be used to determine how long to + * wait before retrying. * * @return the current backoff time in milliseconds */ @@ -20,13 +18,9 @@ public interface BackoffStrategy { */ boolean isExhausted(); - /** - * Move to the next backoff time. - */ + /** Move to the next backoff time. */ void nextBackoff(); - /** - * Reset the backoff strategy to its initial state. - */ + /** Reset the backoff strategy to its initial state. */ void reset(); } diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/CombinedBackoff.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/CombinedBackoff.java index 7d77bcb72..89aee161a 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/CombinedBackoff.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/CombinedBackoff.java @@ -3,9 +3,9 @@ import lombok.Getter; /** - * A backoff strategy that combines multiple backoff strategies. - * The strategy starts with the first provided strategy and will switch to the next backoff strategy in the list when - * the current one is exhausted. + * A backoff strategy that combines multiple backoff strategies. The strategy starts with the first + * provided strategy and will switch to the next backoff strategy in the list when the current one + * is exhausted. */ public class CombinedBackoff implements BackoffStrategy { private final BackoffStrategy[] backoffStrategies; @@ -15,13 +15,11 @@ public class CombinedBackoff implements BackoffStrategy { private BackoffStrategy currentStrategy; /** - * Creates a new combined backoff strategy. - * The strategy starts with the first provided strategy and will switch to the next backoff strategy in the list - * when the current one is exhausted. + * Creates a new combined backoff strategy. The strategy starts with the first provided strategy + * and will switch to the next backoff strategy in the list when the current one is exhausted. * * @param backoffStrategies the list of backoff strategies to combine */ - public CombinedBackoff(BackoffStrategy[] backoffStrategies) { this.backoffStrategies = backoffStrategies.clone(); this.currentStrategyIndex = 0; @@ -46,9 +44,7 @@ public void nextBackoff() { currentStrategy.nextBackoff(); } - /** - * Switches to the next backoff strategy if the current one is exhausted. - */ + /** Switches to the next backoff strategy if the current one is exhausted. */ private void updateCurrentStrategy() { // Move to the next non-exhausted strategy if the current one is exhausted while (!isLastStrategy() && currentStrategy.isExhausted()) { diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/ConstantTimeBackoff.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/ConstantTimeBackoff.java index 662aefe3a..f84ec50f5 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/ConstantTimeBackoff.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/ConstantTimeBackoff.java @@ -1,8 +1,7 @@ package dev.openfeature.contrib.providers.flagd.resolver.common.backoff; /** - * A backoff strategy that always returns the same backoff time. - * This backoff is never exhausted. + * A backoff strategy that always returns the same backoff time. This backoff is never exhausted. */ public class ConstantTimeBackoff implements BackoffStrategy { final long millis; @@ -22,10 +21,8 @@ public boolean isExhausted() { } @Override - public void nextBackoff() { - } + public void nextBackoff() {} @Override - public void reset() { - } + public void reset() {} } diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/ExponentialTimeBackoff.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/ExponentialTimeBackoff.java index 70e327aa8..8dffbc778 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/ExponentialTimeBackoff.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/ExponentialTimeBackoff.java @@ -1,8 +1,8 @@ package dev.openfeature.contrib.providers.flagd.resolver.common.backoff; /** - * A backoff strategy that exponentially increases the backoff time. - * This backoff is never exhausted. + * A backoff strategy that exponentially increases the backoff time. This backoff is never + * exhausted. */ public class ExponentialTimeBackoff implements BackoffStrategy { public static final long DEFAULT_MAX_BACK_OFF = 120 * 1000; @@ -12,8 +12,8 @@ public class ExponentialTimeBackoff implements BackoffStrategy { private long currentBackoff; /** - * A backoff strategy that exponentially increases the backoff time. - * This backoff will double the backoff time until the DEFAULT_MAX_BACK_OFF is reached. + * A backoff strategy that exponentially increases the backoff time. This backoff will double the + * backoff time until the DEFAULT_MAX_BACK_OFF is reached. * * @param initialBackoffMillis the initial backoff time in milliseconds */ @@ -22,9 +22,9 @@ public ExponentialTimeBackoff(long initialBackoffMillis) { } /** - * A backoff strategy that exponentially increases the backoff time. - * This backoff will double the backoff time until the maximum backoff time is reached. - * It is never exhausted but will stale at the maximum backoff time. + * A backoff strategy that exponentially increases the backoff time. This backoff will double the + * backoff time until the maximum backoff time is reached. It is never exhausted but will stale at + * the maximum backoff time. * * @param initialBackoffMillis the initial backoff time in milliseconds * @param maxBackoffMillis the maximum backoff time in milliseconds diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/GrpcStreamConnectorBackoffService.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/GrpcStreamConnectorBackoffService.java index 1b814e772..01152a972 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/GrpcStreamConnectorBackoffService.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/GrpcStreamConnectorBackoffService.java @@ -1,24 +1,23 @@ package dev.openfeature.contrib.providers.flagd.resolver.common.backoff; -/** - * Backoff service that supports "silent" backoff. - */ +/** Backoff service that supports "silent" backoff. */ public class GrpcStreamConnectorBackoffService extends BackoffService { private final BackoffStrategy silentRecoverBackoff; /** - * Create a new backoff service that will not backoff (0ms) on first attempt. - * Subsequent attempts will backoff exponentially. + * Create a new backoff service that will not backoff (0ms) on first attempt. Subsequent attempts + * will backoff exponentially. * - * @param initialBackoffMillis initial backoff time in milliseconds used for exponential error backoff + * @param initialBackoffMillis initial backoff time in milliseconds used for exponential error + * backoff */ public GrpcStreamConnectorBackoffService(long initialBackoffMillis) { this(BackoffStrategies.exponentialTimeBackoff(initialBackoffMillis)); } /** - * Create a new backoff service that will not backoff (0ms) on first attempt. - * Subsequent attempts will backoff using the provided backoff strategy. + * Create a new backoff service that will not backoff (0ms) on first attempt. Subsequent attempts + * will backoff using the provided backoff strategy. * * @param errorBackoff backoff strategy to use after the first attempt */ @@ -27,10 +26,7 @@ public GrpcStreamConnectorBackoffService(BackoffStrategy errorBackoff) { } private GrpcStreamConnectorBackoffService(BackoffStrategy silentRecoverBackoff, BackoffStrategy errorBackoff) { - super(new CombinedBackoff(new BackoffStrategy[]{ - silentRecoverBackoff, - errorBackoff - })); + super(new CombinedBackoff(new BackoffStrategy[] {silentRecoverBackoff, errorBackoff})); this.silentRecoverBackoff = silentRecoverBackoff; } diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/NumberOfRetriesBackoff.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/NumberOfRetriesBackoff.java index c4b54c906..16a956e01 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/NumberOfRetriesBackoff.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/NumberOfRetriesBackoff.java @@ -3,8 +3,8 @@ import lombok.Getter; /** - * This strategy will backoff for a fixed number of retries before being exhausted. - * The backoff time is determined by the provided {@link BackoffStrategy}. + * This strategy will backoff for a fixed number of retries before being exhausted. The backoff time + * is determined by the provided {@link BackoffStrategy}. */ public class NumberOfRetriesBackoff implements BackoffStrategy { private final int numRetries; @@ -14,8 +14,8 @@ public class NumberOfRetriesBackoff implements BackoffStrategy { private int retryCount; /** - * Creates a new backoff strategy that will backoff for a fixed number of retries before being exhausted. - * The backoff time is determined by the provided {@link BackoffStrategy}. + * Creates a new backoff strategy that will backoff for a fixed number of retries before being + * exhausted. The backoff time is determined by the provided {@link BackoffStrategy}. * * @param numRetries the number of retries before the backoff is exhausted * @param backoffStrategy the backoff strategy to use for determining the backoff time diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/nameresolvers/EnvoyResolver.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/nameresolvers/EnvoyResolver.java index e1b62ebfd..ee28a005a 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/nameresolvers/EnvoyResolver.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/nameresolvers/EnvoyResolver.java @@ -1,17 +1,17 @@ package dev.openfeature.contrib.providers.flagd.resolver.common.nameresolvers; +import io.grpc.Attributes; import io.grpc.EquivalentAddressGroup; import io.grpc.NameResolver; -import java.net.InetSocketAddress; -import io.grpc.Attributes; import io.grpc.Status; +import java.net.InetSocketAddress; import java.net.URI; import java.util.Collections; import java.util.List; /** - * Envoy NameResolver, will always override the authority with the specified authority and - * use the socketAddress to connect. + * Envoy NameResolver, will always override the authority with the specified authority and use the + * socketAddress to connect. * *

Custom URI Scheme: * @@ -35,8 +35,7 @@ public String getServiceAuthority() { } @Override - public void shutdown() { - } + public void shutdown() {} @Override public void start(Listener2 listener) { @@ -55,15 +54,16 @@ private void resolve() { Attributes addressGroupAttributes = Attributes.newBuilder() .set(EquivalentAddressGroup.ATTR_AUTHORITY_OVERRIDE, this.authority) .build(); - List equivalentAddressGroup = Collections.singletonList( - new EquivalentAddressGroup(address, addressGroupAttributes) - ); + List equivalentAddressGroup = + Collections.singletonList(new EquivalentAddressGroup(address, addressGroupAttributes)); ResolutionResult resolutionResult = ResolutionResult.newBuilder() .setAddresses(equivalentAddressGroup) .build(); this.listener.onResult(resolutionResult); } catch (Exception e) { - this.listener.onError(Status.UNAVAILABLE.withDescription("Unable to resolve host ").withCause(e)); + this.listener.onError(Status.UNAVAILABLE + .withDescription("Unable to resolve host ") + .withCause(e)); } } } diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/nameresolvers/EnvoyResolverProvider.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/nameresolvers/EnvoyResolverProvider.java index ab8000219..42f94b749 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/nameresolvers/EnvoyResolverProvider.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/nameresolvers/EnvoyResolverProvider.java @@ -5,8 +5,7 @@ import java.net.URI; /** - * A custom NameResolver provider to resolve gRPC target uri for envoy in the - * format of. + * A custom NameResolver provider to resolve gRPC target uri for envoy in the format of. * *

envoy://[proxy-agent-host]:[proxy-agent-port]/[service-name] */ @@ -35,8 +34,12 @@ public NameResolver newNameResolver(URI targetUri, NameResolver.Args args) { if (!isValidPath(targetUri.getPath()) || targetUri.getHost() == null || targetUri.getPort() == -1) { throw new IllegalArgumentException("Incorrectly formatted target uri; " - + "expected: '" + ENVOY_SCHEME + ":[//]:/';" - + "but was '" + targetUri + "'"); + + "expected: '" + + ENVOY_SCHEME + + ":[//]:/';" + + "but was '" + + targetUri + + "'"); } return new EnvoyResolver(targetUri); @@ -48,7 +51,8 @@ public String getDefaultScheme() { } private static boolean isValidPath(String path) { - return !path.isEmpty() && !path.substring(1).isEmpty() + return !path.isEmpty() + && !path.substring(1).isEmpty() && !path.substring(1).contains("/"); } } diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/Constants.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/Constants.java index d6ba68efd..7e5fa05d0 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/Constants.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/Constants.java @@ -1,8 +1,6 @@ package dev.openfeature.contrib.providers.flagd.resolver.grpc; -/** - * Constants for evaluation proto. - */ +/** Constants for evaluation proto. */ public class Constants { public static final String CONFIGURATION_CHANGE = "configuration_change"; public static final String PROVIDER_READY = "provider_ready"; diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/Convert.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/Convert.java index 29b68f68e..0a782ae47 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/Convert.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/Convert.java @@ -1,9 +1,7 @@ package dev.openfeature.contrib.providers.flagd.resolver.grpc; -/** - * A converter lambda. - */ +/** A converter lambda. */ @FunctionalInterface public interface Convert { OutT convert(InT value); -} \ No newline at end of file +} diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/EventStreamObserver.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/EventStreamObserver.java index 6b4efe58e..7d60a704a 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/EventStreamObserver.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/EventStreamObserver.java @@ -1,23 +1,19 @@ package dev.openfeature.contrib.providers.flagd.resolver.grpc; +import com.google.protobuf.Value; +import dev.openfeature.contrib.providers.flagd.resolver.grpc.cache.Cache; +import dev.openfeature.flagd.grpc.evaluation.Evaluation.EventStreamResponse; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import io.grpc.stub.StreamObserver; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.function.BiConsumer; import java.util.function.Supplier; - -import com.google.protobuf.Value; - -import dev.openfeature.contrib.providers.flagd.resolver.grpc.cache.Cache; -import dev.openfeature.flagd.grpc.evaluation.Evaluation.EventStreamResponse; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import io.grpc.stub.StreamObserver; import lombok.extern.slf4j.Slf4j; -/** - * EventStreamObserver handles events emitted by flagd. - */ +/** EventStreamObserver handles events emitted by flagd. */ @Slf4j @SuppressFBWarnings(justification = "cache needs to be read and write by multiple objects") class EventStreamObserver implements StreamObserver { @@ -29,13 +25,17 @@ class EventStreamObserver implements StreamObserver { /** * Create a gRPC stream that get notified about flag changes. * - * @param sync synchronization object from caller - * @param cache cache to update - * @param onConnectionEvent lambda to call to handle the response - * @param shouldRetrySilently Boolean supplier indicating if the GRPC connector will try to recover silently + * @param sync synchronization object from caller + * @param cache cache to update + * @param onConnectionEvent lambda to call to handle the response + * @param shouldRetrySilently Boolean supplier indicating if the GRPC connector will try to + * recover silently */ - EventStreamObserver(Object sync, Cache cache, BiConsumer> onConnectionEvent, - Supplier shouldRetrySilently) { + EventStreamObserver( + Object sync, + Cache cache, + BiConsumer> onConnectionEvent, + Supplier shouldRetrySilently) { this.sync = sync; this.cache = cache; this.onConnectionEvent = onConnectionEvent; diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/GrpcConnector.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/GrpcConnector.java index 5cf10a94a..8fabb5d8b 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/GrpcConnector.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/GrpcConnector.java @@ -1,5 +1,7 @@ package dev.openfeature.contrib.providers.flagd.resolver.grpc; +import static dev.openfeature.contrib.providers.flagd.resolver.common.backoff.BackoffStrategies.maxRetriesWithExponentialTimeBackoffStrategy; + import dev.openfeature.contrib.providers.flagd.FlagdOptions; import dev.openfeature.contrib.providers.flagd.resolver.common.ChannelBuilder; import dev.openfeature.contrib.providers.flagd.resolver.common.ConnectionEvent; @@ -12,19 +14,14 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.grpc.ManagedChannel; import io.grpc.stub.StreamObserver; -import lombok.extern.slf4j.Slf4j; - import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Supplier; +import lombok.extern.slf4j.Slf4j; -import static dev.openfeature.contrib.providers.flagd.resolver.common.backoff.BackoffStrategies.maxRetriesWithExponentialTimeBackoffStrategy; - -/** - * Class that abstracts the gRPC communication with flagd. - */ +/** Class that abstracts the gRPC communication with flagd. */ @Slf4j @SuppressFBWarnings(justification = "cache needs to be read and write by multiple objects") public class GrpcConnector { @@ -48,13 +45,16 @@ public class GrpcConnector { /** * GrpcConnector creates an abstraction over gRPC communication. * - * @param options flagd options - * @param cache cache to use + * @param options flagd options + * @param cache cache to use * @param connectedSupplier lambda providing current connection status from caller * @param onConnectionEvent lambda which handles changes in the connection/stream */ - public GrpcConnector(final FlagdOptions options, final Cache cache, final Supplier connectedSupplier, - Consumer onConnectionEvent) { + public GrpcConnector( + final FlagdOptions options, + final Cache cache, + final Supplier connectedSupplier, + Consumer onConnectionEvent) { this.channel = ChannelBuilder.nettyChannel(options); this.serviceStub = ServiceGrpc.newStub(channel); this.serviceBlockingStub = ServiceGrpc.newBlockingStub(channel); @@ -64,14 +64,10 @@ public GrpcConnector(final FlagdOptions options, final Cache cache, final Suppli this.onConnectionEvent = onConnectionEvent; this.connectedSupplier = connectedSupplier; this.backoff = new GrpcStreamConnectorBackoffService(maxRetriesWithExponentialTimeBackoffStrategy( - options.getMaxEventStreamRetries(), - options.getRetryBackoffMs()) - ); + options.getMaxEventStreamRetries(), options.getRetryBackoffMs())); } - /** - * Initialize the gRPC stream. - */ + /** Initialize the gRPC stream. */ public void initialize() throws Exception { eventObserverThread = new Thread(this::observeEventStream); eventObserverThread.setDaemon(true); @@ -84,8 +80,7 @@ public void initialize() throws Exception { /** * Shuts down all gRPC resources. * - * @throws Exception is something goes wrong while terminating the - * communication. + * @throws Exception is something goes wrong while terminating the communication. */ public void shutdown() throws Exception { // first shutdown the event listener @@ -119,13 +114,13 @@ public ServiceGrpc.ServiceBlockingStub getResolver() { } /** - * Event stream observer logic. This contains blocking mechanisms, hence must be - * run in a dedicated thread. + * Event stream observer logic. This contains blocking mechanisms, hence must be run in a + * dedicated thread. */ private void observeEventStream() { while (backoff.shouldRetry()) { - final StreamObserver responseObserver = new EventStreamObserver(sync, this.cache, - this::onConnectionEvent, backoff::shouldRetrySilently); + final StreamObserver responseObserver = + new EventStreamObserver(sync, this.cache, this::onConnectionEvent, backoff::shouldRetrySilently); ServiceGrpc.ServiceStub localServiceStub = this.serviceStub; diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/GrpcResolver.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/GrpcResolver.java index 9fcede67e..9d8c3a9f2 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/GrpcResolver.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/GrpcResolver.java @@ -5,14 +5,8 @@ import static dev.openfeature.contrib.providers.flagd.resolver.common.Convert.getField; import static dev.openfeature.contrib.providers.flagd.resolver.common.Convert.getFieldDescriptor; -import java.util.Map; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - import com.google.protobuf.Message; import com.google.protobuf.Struct; - import dev.openfeature.contrib.providers.flagd.Config; import dev.openfeature.contrib.providers.flagd.FlagdOptions; import dev.openfeature.contrib.providers.flagd.resolver.Resolver; @@ -37,6 +31,10 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.grpc.Status.Code; import io.grpc.StatusRuntimeException; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; /** * Resolves flag values using https://buf.build/open-feature/flagd/docs/main:flagd.evaluation.v1. @@ -54,13 +52,16 @@ public final class GrpcResolver implements Resolver { /** * Resolves flag values using https://buf.build/open-feature/flagd/docs/main:flagd.evaluation.v1. * Flags are evaluated remotely. - * + * * @param options flagd options * @param cache cache to use * @param connectedSupplier lambda providing current connection status from caller * @param onConnectionEvent lambda which handles changes in the connection/stream */ - public GrpcResolver(final FlagdOptions options, final Cache cache, final Supplier connectedSupplier, + public GrpcResolver( + final FlagdOptions options, + final Cache cache, + final Supplier connectedSupplier, final Consumer onConnectionEvent) { this.cache = cache; this.connectedSupplier = connectedSupplier; @@ -68,80 +69,68 @@ public GrpcResolver(final FlagdOptions options, final Cache cache, final Supplie this.connector = new GrpcConnector(options, cache, connectedSupplier, onConnectionEvent); } - /** - * Initialize Grpc resolver. - */ + /** Initialize Grpc resolver. */ public void init() throws Exception { this.connector.initialize(); } - /** - * Shutdown Grpc resolver. - */ + /** Shutdown Grpc resolver. */ public void shutdown() throws Exception { this.connector.shutdown(); } - /** - * Boolean evaluation from grpc resolver. - */ - public ProviderEvaluation booleanEvaluation(String key, Boolean defaultValue, - EvaluationContext ctx) { + /** Boolean evaluation from grpc resolver. */ + public ProviderEvaluation booleanEvaluation(String key, Boolean defaultValue, EvaluationContext ctx) { ResolveBooleanRequest request = ResolveBooleanRequest.newBuilder().buildPartial(); return resolve(key, ctx, request, this.connector.getResolver()::resolveBoolean, null); } - /** - * String evaluation from grpc resolver. - */ - public ProviderEvaluation stringEvaluation(String key, String defaultValue, - EvaluationContext ctx) { + /** String evaluation from grpc resolver. */ + public ProviderEvaluation stringEvaluation(String key, String defaultValue, EvaluationContext ctx) { ResolveStringRequest request = ResolveStringRequest.newBuilder().buildPartial(); return resolve(key, ctx, request, this.connector.getResolver()::resolveString, null); } - /** - * Double evaluation from grpc resolver. - */ - public ProviderEvaluation doubleEvaluation(String key, Double defaultValue, - EvaluationContext ctx) { + /** Double evaluation from grpc resolver. */ + public ProviderEvaluation doubleEvaluation(String key, Double defaultValue, EvaluationContext ctx) { ResolveFloatRequest request = ResolveFloatRequest.newBuilder().buildPartial(); return resolve(key, ctx, request, this.connector.getResolver()::resolveFloat, null); } - /** - * Integer evaluation from grpc resolver. - */ - public ProviderEvaluation integerEvaluation(String key, Integer defaultValue, - EvaluationContext ctx) { + /** Integer evaluation from grpc resolver. */ + public ProviderEvaluation integerEvaluation(String key, Integer defaultValue, EvaluationContext ctx) { ResolveIntRequest request = ResolveIntRequest.newBuilder().buildPartial(); - return resolve(key, ctx, request, this.connector.getResolver()::resolveInt, - (Object value) -> ((Long) value).intValue()); + return resolve(key, ctx, request, this.connector.getResolver()::resolveInt, (Object value) -> ((Long) value) + .intValue()); } - /** - * Object evaluation from grpc resolver. - */ - public ProviderEvaluation objectEvaluation(String key, Value defaultValue, - EvaluationContext ctx) { + /** Object evaluation from grpc resolver. */ + public ProviderEvaluation objectEvaluation(String key, Value defaultValue, EvaluationContext ctx) { ResolveObjectRequest request = ResolveObjectRequest.newBuilder().buildPartial(); - return resolve(key, ctx, request, this.connector.getResolver()::resolveObject, + return resolve( + key, + ctx, + request, + this.connector.getResolver()::resolveObject, (Object value) -> convertObjectResponse((Struct) value)); } /** - * A generic resolve method that takes a resolverRef and an optional converter - * lambda to transform the result. + * A generic resolve method that takes a resolverRef and an optional converter lambda to transform + * the result. */ private ProviderEvaluation resolve( - String key, EvaluationContext ctx, ReqT request, Function resolverRef, + String key, + EvaluationContext ctx, + ReqT request, + Function resolverRef, Convert converter) { // return from cache if available and item is present @@ -169,7 +158,8 @@ private ProviderEvaluation entry : struct.getFieldsMap().entrySet()) { + for (Map.Entry entry : + struct.getFieldsMap().entrySet()) { if (entry.getValue().hasStringValue()) { builder.addString(entry.getKey(), entry.getValue().getStringValue()); } else if (entry.getValue().hasBoolValue()) { diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/cache/Cache.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/cache/Cache.java index 77e345c62..2824c3ef3 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/cache/Cache.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/cache/Cache.java @@ -1,19 +1,16 @@ package dev.openfeature.contrib.providers.flagd.resolver.grpc.cache; +import static dev.openfeature.contrib.providers.flagd.resolver.grpc.cache.CacheType.DISABLED; +import static dev.openfeature.contrib.providers.flagd.resolver.grpc.cache.CacheType.LRU; + import dev.openfeature.sdk.ProviderEvaluation; +import java.util.Collections; +import java.util.Map; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.map.LRUMap; -import java.util.Collections; -import java.util.Map; - -import static dev.openfeature.contrib.providers.flagd.resolver.grpc.cache.CacheType.DISABLED; -import static dev.openfeature.contrib.providers.flagd.resolver.grpc.cache.CacheType.LRU; - -/** - * Exposes caching mechanism for flag evaluations. - */ +/** Exposes caching mechanism for flag evaluations. */ @Slf4j public class Cache { private Map> store; @@ -24,7 +21,7 @@ public class Cache { /** * Initialize the cache. * - * @param forType type of the cache. + * @param forType type of the cache. * @param maxCacheSize max amount of element to keep. */ public Cache(final String forType, int maxCacheSize) { diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/cache/CacheType.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/cache/CacheType.java index 7c73afb95..9be02de31 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/cache/CacheType.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/cache/CacheType.java @@ -2,9 +2,7 @@ import lombok.Getter; -/** - * Defines which type of cache to use. - */ +/** Defines which type of cache to use. */ @Getter public enum CacheType { DISABLED("disabled"), diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/strategy/ResolveFactory.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/strategy/ResolveFactory.java index 3b7978fdf..493156bdd 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/strategy/ResolveFactory.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/strategy/ResolveFactory.java @@ -2,13 +2,10 @@ import dev.openfeature.contrib.providers.flagd.FlagdOptions; -/** - * Factory to create a ResolveStrategy. - */ +/** Factory to create a ResolveStrategy. */ public final class ResolveFactory { - private ResolveFactory() { - } + private ResolveFactory() {} /** * Factory method to initialize the resolving strategy. diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/strategy/ResolveStrategy.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/strategy/ResolveStrategy.java index 2ae4cc602..716fd8ba7 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/strategy/ResolveStrategy.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/strategy/ResolveStrategy.java @@ -1,13 +1,10 @@ package dev.openfeature.contrib.providers.flagd.resolver.grpc.strategy; import com.google.protobuf.Message; - import java.util.function.Function; -/** - * Request to Response resolving strategy. - * */ +/** Request to Response resolving strategy. */ public interface ResolveStrategy { - ResT resolve(final Function resolverRef, final Message req, - final String key); + ResT resolve( + final Function resolverRef, final Message req, final String key); } diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/strategy/SimpleResolving.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/strategy/SimpleResolving.java index 6ca3706d4..8b7f113d9 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/strategy/SimpleResolving.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/strategy/SimpleResolving.java @@ -1,17 +1,14 @@ package dev.openfeature.contrib.providers.flagd.resolver.grpc.strategy; import com.google.protobuf.Message; - import java.util.function.Function; -/** - * {@link SimpleResolving} is a simple request to response resolver. - */ +/** {@link SimpleResolving} is a simple request to response resolver. */ public class SimpleResolving implements ResolveStrategy { @Override - public ResT resolve(final Function resolverRef, - final Message req, final String key) { + public ResT resolve( + final Function resolverRef, final Message req, final String key) { return resolverRef.apply((ReqT) req); } } diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/strategy/TracedResolving.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/strategy/TracedResolving.java index b2addfd8b..36925f979 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/strategy/TracedResolving.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/strategy/TracedResolving.java @@ -6,13 +6,10 @@ import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.context.Scope; - -import javax.annotation.Nonnull; import java.util.function.Function; +import javax.annotation.Nonnull; -/** - * {@link TracedResolving} a request to response resolver with tracing for telemetry. - */ +/** {@link TracedResolving} a request to response resolver with tracing for telemetry. */ @SuppressWarnings("PMD.UnusedLocalVariable") public class TracedResolving implements ResolveStrategy { @@ -23,10 +20,11 @@ public TracedResolving(@Nonnull OpenTelemetry telemetry) { } @Override - public ResT resolve(final Function resolverRef, - final Message req, final String key) { + public ResT resolve( + final Function resolverRef, final Message req, final String key) { - final Span span = tracer.spanBuilder("resolve").setSpanKind(SpanKind.CLIENT).startSpan(); + final Span span = + tracer.spanBuilder("resolve").setSpanKind(SpanKind.CLIENT).startSpan(); span.setAttribute("feature_flag.key", key); span.setAttribute("feature_flag.provider_name", "flagd"); diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/InProcessResolver.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/InProcessResolver.java index 39c77f01b..663d4bb0d 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/InProcessResolver.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/InProcessResolver.java @@ -2,9 +2,6 @@ import static dev.openfeature.contrib.providers.flagd.resolver.process.model.FeatureFlag.EMPTY_TARGETING_STRING; -import java.util.function.Consumer; -import java.util.function.Supplier; - import dev.openfeature.contrib.providers.flagd.FlagdOptions; import dev.openfeature.contrib.providers.flagd.resolver.Resolver; import dev.openfeature.contrib.providers.flagd.resolver.common.ConnectionEvent; @@ -26,12 +23,13 @@ import dev.openfeature.sdk.Value; import dev.openfeature.sdk.exceptions.ParseError; import dev.openfeature.sdk.exceptions.TypeMismatchError; +import java.util.function.Consumer; +import java.util.function.Supplier; import lombok.extern.slf4j.Slf4j; /** - * Resolves flag values using - * https://buf.build/open-feature/flagd/docs/main:flagd.sync.v1. - * Flags are evaluated locally. + * Resolves flag values using https://buf.build/open-feature/flagd/docs/main:flagd.sync.v1. Flags + * are evaluated locally. */ @Slf4j public class InProcessResolver implements Resolver { @@ -43,49 +41,50 @@ public class InProcessResolver implements Resolver { private final Supplier connectedSupplier; /** - * Resolves flag values using - * https://buf.build/open-feature/flagd/docs/main:flagd.sync.v1. - * Flags are evaluated locally. - * - * @param options flagd options - * @param connectedSupplier lambda providing current connection status from - * caller - * @param onConnectionEvent lambda which handles changes in the - * connection/stream + * Resolves flag values using https://buf.build/open-feature/flagd/docs/main:flagd.sync.v1. Flags + * are evaluated locally. + * + * @param options flagd options + * @param connectedSupplier lambda providing current connection status from caller + * @param onConnectionEvent lambda which handles changes in the connection/stream */ - public InProcessResolver(FlagdOptions options, final Supplier connectedSupplier, + public InProcessResolver( + FlagdOptions options, + final Supplier connectedSupplier, Consumer onConnectionEvent) { this.flagStore = new FlagStore(getConnector(options)); this.deadline = options.getDeadline(); this.onConnectionEvent = onConnectionEvent; this.operator = new Operator(); this.connectedSupplier = connectedSupplier; - this.metadata = options.getSelector() == null ? null + this.metadata = options.getSelector() == null + ? null : ImmutableMetadata.builder() .addString("scope", options.getSelector()) .build(); } - /** - * Initialize in-process resolver. - */ + /** Initialize in-process resolver. */ public void init() throws Exception { flagStore.init(); final Thread stateWatcher = new Thread(() -> { try { while (true) { - final StorageStateChange storageStateChange = flagStore.getStateQueue().take(); + final StorageStateChange storageStateChange = + flagStore.getStateQueue().take(); switch (storageStateChange.getStorageState()) { case OK: - onConnectionEvent.accept(new ConnectionEvent(true, storageStateChange.getChangedFlagsKeys(), + onConnectionEvent.accept(new ConnectionEvent( + true, + storageStateChange.getChangedFlagsKeys(), storageStateChange.getSyncMetadata())); break; case ERROR: onConnectionEvent.accept(new ConnectionEvent(false)); break; default: - log.info(String.format("Storage emitted unhandled status: %s", - storageStateChange.getStorageState())); + log.info(String.format( + "Storage emitted unhandled status: %s", storageStateChange.getStorageState())); } } } catch (InterruptedException e) { @@ -110,38 +109,27 @@ public void shutdown() throws InterruptedException { onConnectionEvent.accept(new ConnectionEvent(false)); } - /** - * Resolve a boolean flag. - */ - public ProviderEvaluation booleanEvaluation(String key, Boolean defaultValue, - EvaluationContext ctx) { + /** Resolve a boolean flag. */ + public ProviderEvaluation booleanEvaluation(String key, Boolean defaultValue, EvaluationContext ctx) { return resolve(Boolean.class, key, ctx); } - /** - * Resolve a string flag. - */ + /** Resolve a string flag. */ public ProviderEvaluation stringEvaluation(String key, String defaultValue, EvaluationContext ctx) { return resolve(String.class, key, ctx); } - /** - * Resolve a double flag. - */ + /** Resolve a double flag. */ public ProviderEvaluation doubleEvaluation(String key, Double defaultValue, EvaluationContext ctx) { return resolve(Double.class, key, ctx); } - /** - * Resolve an integer flag. - */ + /** Resolve an integer flag. */ public ProviderEvaluation integerEvaluation(String key, Integer defaultValue, EvaluationContext ctx) { return resolve(Integer.class, key, ctx); } - /** - * Resolve an object flag. - */ + /** Resolve an object flag. */ public ProviderEvaluation objectEvaluation(String key, Value defaultValue, EvaluationContext ctx) { final ProviderEvaluation evaluation = resolve(Object.class, key, ctx); @@ -159,7 +147,8 @@ static Connector getConnector(final FlagdOptions options) { if (options.getCustomConnector() != null) { return options.getCustomConnector(); } - return options.getOfflineFlagSourcePath() != null && !options.getOfflineFlagSourcePath().isEmpty() + return options.getOfflineFlagSourcePath() != null + && !options.getOfflineFlagSourcePath().isEmpty() ? new FileConnector(options.getOfflineFlagSourcePath()) : new GrpcStreamConnector(options); } @@ -231,7 +220,8 @@ private ProviderEvaluation resolve(Class type, String key, EvaluationC .variant(resolvedVariant) .reason(reason); - return this.metadata == null ? evaluationBuilder.build() + return this.metadata == null + ? evaluationBuilder.build() : evaluationBuilder.flagMetadata(this.metadata).build(); } } diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/model/FeatureFlag.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/model/FeatureFlag.java index 4e687c369..9a3475aa8 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/model/FeatureFlag.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/model/FeatureFlag.java @@ -5,16 +5,14 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import java.util.Map; import lombok.EqualsAndHashCode; import lombok.Getter; -import java.util.Map; - -/** - * flagd feature flag model. - */ +/** flagd feature flag model. */ @Getter -@SuppressFBWarnings(value = {"EI_EXPOSE_REP"}, +@SuppressFBWarnings( + value = {"EI_EXPOSE_REP"}, justification = "Feature flag comes as a Json configuration, hence they must be parsed and exposed") @JsonIgnoreProperties(ignoreUnknown = true) @EqualsAndHashCode @@ -26,23 +24,20 @@ public class FeatureFlag { private final Map variants; private final String targeting; - /** - * Construct a flagd feature flag. - */ + /** Construct a flagd feature flag. */ @JsonCreator - public FeatureFlag(@JsonProperty("state") String state, - @JsonProperty("defaultVariant") String defaultVariant, - @JsonProperty("variants") Map variants, - @JsonProperty("targeting") @JsonDeserialize(using = StringSerializer.class) String targeting) { + public FeatureFlag( + @JsonProperty("state") String state, + @JsonProperty("defaultVariant") String defaultVariant, + @JsonProperty("variants") Map variants, + @JsonProperty("targeting") @JsonDeserialize(using = StringSerializer.class) String targeting) { this.state = state; this.defaultVariant = defaultVariant; this.variants = variants; this.targeting = targeting; } - /** - * Get targeting rule of the flag. - */ + /** Get targeting rule of the flag. */ public String getTargeting() { return this.targeting == null ? EMPTY_TARGETING_STRING : this.targeting; } diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/model/FlagParser.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/model/FlagParser.java index c1b9a205c..1dbe7f710 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/model/FlagParser.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/model/FlagParser.java @@ -1,13 +1,5 @@ package dev.openfeature.contrib.providers.flagd.resolver.process.model; -import java.io.IOException; -import java.net.URI; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; -import java.util.regex.Pattern; - import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.TreeNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -15,15 +7,20 @@ import com.networknt.schema.JsonSchemaFactory; import com.networknt.schema.SpecVersion; import com.networknt.schema.ValidationMessage; - import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import java.io.IOException; +import java.net.URI; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; import lombok.extern.slf4j.Slf4j; -/** - * flagd feature flag configuration parser. - */ +/** flagd feature flag configuration parser. */ @Slf4j -@SuppressFBWarnings(value = {"EI_EXPOSE_REP"}, +@SuppressFBWarnings( + value = {"EI_EXPOSE_REP"}, justification = "Feature flag comes as a Json configuration, hence they must be exposed") public class FlagParser { private static final String FLAG_KEY = "flags"; @@ -32,8 +29,7 @@ public class FlagParser { private static final ObjectMapper MAPPER = new ObjectMapper(); private static JsonSchema SCHEMA_VALIDATOR; - private FlagParser() { - } + private FlagParser() {} static { try { @@ -43,10 +39,9 @@ private FlagParser() { mappings.put("https://flagd.dev/schema/v0/targeting.json", "classpath:flagd/schemas/targeting.json"); mappings.put("https://flagd.dev/schema/v0/flags.json", "classpath:flagd/schemas/flags.json"); - SCHEMA_VALIDATOR = JsonSchemaFactory - .getInstance(SpecVersion.VersionFlag.V7, - builder -> builder - .schemaMappers(schemaMappers -> schemaMappers.mappings(mappings))) + SCHEMA_VALIDATOR = JsonSchemaFactory.getInstance( + SpecVersion.VersionFlag.V7, + builder -> builder.schemaMappers(schemaMappers -> schemaMappers.mappings(mappings))) .getSchema(new URI("https://flagd.dev/schema/v0/flags.json")); } catch (Throwable e) { // log only, do not throw @@ -54,9 +49,7 @@ private FlagParser() { } } - /** - * Parse {@link String} for feature flags. - */ + /** Parse {@link String} for feature flags. */ public static Map parseString(final String configuration, boolean throwIfInvalid) throws IOException { if (SCHEMA_VALIDATOR != null) { @@ -121,7 +114,8 @@ private static String transposeEvaluators(final String configuration) throws IOE patternMap.computeIfAbsent(replacePattern, s -> Pattern.compile(replacePattern)); // finally replace all references - replacedConfigurations = reg_replace.matcher(replacedConfigurations).replaceAll(replacer); + replacedConfigurations = + reg_replace.matcher(replacedConfigurations).replaceAll(replacer); } return replacedConfigurations; diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/model/StringSerializer.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/model/StringSerializer.java index 8b8743476..ac18eae79 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/model/StringSerializer.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/model/StringSerializer.java @@ -4,12 +4,9 @@ import com.fasterxml.jackson.core.TreeNode; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; - import java.io.IOException; -/** - * Custom serializer to preserve Json node as a {@link String}. - * */ +/** Custom serializer to preserve Json node as a {@link String}. */ class StringSerializer extends StdDeserializer { public StringSerializer() { @@ -17,8 +14,7 @@ public StringSerializer() { } @Override - public String deserialize(JsonParser p, DeserializationContext ctxt) - throws IOException { + public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { TreeNode node = p.readValueAsTree(); return node.toString(); } diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/FlagStore.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/FlagStore.java index e4add48e9..7d419a353 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/FlagStore.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/FlagStore.java @@ -2,6 +2,14 @@ import static dev.openfeature.contrib.providers.flagd.resolver.common.Convert.convertProtobufMapToStructure; +import dev.openfeature.contrib.providers.flagd.resolver.process.model.FeatureFlag; +import dev.openfeature.contrib.providers.flagd.resolver.process.model.FlagParser; +import dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.Connector; +import dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.QueuePayload; +import dev.openfeature.flagd.grpc.sync.Sync.GetMetadataResponse; +import dev.openfeature.sdk.ImmutableStructure; +import dev.openfeature.sdk.Structure; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -12,23 +20,13 @@ import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import java.util.stream.Collectors; - -import dev.openfeature.contrib.providers.flagd.resolver.process.model.FeatureFlag; -import dev.openfeature.contrib.providers.flagd.resolver.process.model.FlagParser; -import dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.Connector; -import dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.QueuePayload; -import dev.openfeature.flagd.grpc.sync.Sync.GetMetadataResponse; -import dev.openfeature.sdk.ImmutableStructure; -import dev.openfeature.sdk.Structure; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import lombok.extern.slf4j.Slf4j; -/** - * Feature flag storage. - */ +/** Feature flag storage. */ @Slf4j -@SuppressFBWarnings(value = { - "EI_EXPOSE_REP" }, justification = "Feature flag comes as a Json configuration, hence they must be exposed") +@SuppressFBWarnings( + value = {"EI_EXPOSE_REP"}, + justification = "Feature flag comes as a Json configuration, hence they must be exposed") public class FlagStore implements Storage { private final ReentrantReadWriteLock sync = new ReentrantReadWriteLock(); private final ReadLock readLock = sync.readLock(); @@ -50,9 +48,7 @@ public FlagStore(final Connector connector, final boolean throwIfInvalid) { this.throwIfInvalid = throwIfInvalid; } - /** - * Initialize storage layer. - */ + /** Initialize storage layer. */ public void init() throws Exception { connector.init(); Thread streamer = new Thread(() -> { @@ -80,9 +76,7 @@ public void shutdown() throws InterruptedException { connector.shutdown(); } - /** - * Retrieve flag for the given key. - */ + /** Retrieve flag for the given key. */ public FeatureFlag getFlag(final String key) { readLock.lock(); try { @@ -92,9 +86,7 @@ public FeatureFlag getFlag(final String key) { } } - /** - * Retrieve blocking queue to check storage status. - */ + /** Retrieve blocking queue to check storage status. */ public BlockingQueue getStateQueue() { return stateBlockingQueue; } @@ -108,8 +100,8 @@ private void streamerListener(final Connector connector) throws InterruptedExcep case DATA: try { List changedFlagsKeys; - Map flagMap = FlagParser.parseString(payload.getFlagData(), - throwIfInvalid); + Map flagMap = + FlagParser.parseString(payload.getFlagData(), throwIfInvalid); Structure metadata = parseSyncMetadata(payload.getMetadataResponse()); writeLock.lock(); try { @@ -119,8 +111,8 @@ private void streamerListener(final Connector connector) throws InterruptedExcep } finally { writeLock.unlock(); } - if (!stateBlockingQueue - .offer(new StorageStateChange(StorageState.OK, changedFlagsKeys, metadata))) { + if (!stateBlockingQueue.offer( + new StorageStateChange(StorageState.OK, changedFlagsKeys, metadata))) { log.warn("Failed to convey OK satus, queue is full"); } } catch (Throwable e) { @@ -175,5 +167,4 @@ private List getChangedFlagsKeys(Map newFlags) { changedFlags.putAll(updatedFeatureFlags); return changedFlags.keySet().stream().collect(Collectors.toList()); } - } diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/Storage.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/Storage.java index 10772154f..c18ce82b2 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/Storage.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/Storage.java @@ -1,12 +1,9 @@ package dev.openfeature.contrib.providers.flagd.resolver.process.storage; import dev.openfeature.contrib.providers.flagd.resolver.process.model.FeatureFlag; - import java.util.concurrent.BlockingQueue; -/** - * Storage abstraction for resolver. - */ +/** Storage abstraction for resolver. */ public interface Storage { void init() throws Exception; diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/StorageState.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/StorageState.java index d614a52c4..c47670b7d 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/StorageState.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/StorageState.java @@ -1,19 +1,11 @@ package dev.openfeature.contrib.providers.flagd.resolver.process.storage; -/** - * Satus of the storage. - */ +/** Satus of the storage. */ public enum StorageState { - /** - * Storage is upto date and working as expected. - */ + /** Storage is upto date and working as expected. */ OK, - /** - * Storage has gone stale(most recent sync failed). May get to OK status with next sync. - */ + /** Storage has gone stale(most recent sync failed). May get to OK status with next sync. */ STALE, - /** - * Storage is in an unrecoverable error stage. - */ + /** Storage is in an unrecoverable error stage. */ ERROR, } diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/StorageStateChange.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/StorageStateChange.java index a4db81553..9875cbe9c 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/StorageStateChange.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/StorageStateChange.java @@ -1,17 +1,14 @@ package dev.openfeature.contrib.providers.flagd.resolver.process.storage; -import java.util.Collections; -import java.util.List; - import dev.openfeature.sdk.ImmutableStructure; import dev.openfeature.sdk.Structure; +import java.util.Collections; +import java.util.List; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.ToString; -/** - * Represents a change in the stored flags. - */ +/** Represents a change in the stored flags. */ @Getter @ToString @EqualsAndHashCode @@ -22,12 +19,12 @@ public class StorageStateChange { /** * Construct a new StorageStateChange. + * * @param storageState state of the storage * @param changedFlagsKeys flags changed - * @param syncMetadata possibly updated metadata + * @param syncMetadata possibly updated metadata */ - public StorageStateChange(StorageState storageState, List changedFlagsKeys, - Structure syncMetadata) { + public StorageStateChange(StorageState storageState, List changedFlagsKeys, Structure syncMetadata) { this.storageState = storageState; this.changedFlagsKeys = Collections.unmodifiableList(changedFlagsKeys); this.syncMetadata = new ImmutableStructure(syncMetadata.asMap()); @@ -35,6 +32,7 @@ public StorageStateChange(StorageState storageState, List changedFlagsKe /** * Construct a new StorageStateChange. + * * @param storageState state of the storage * @param changedFlagsKeys flags changed */ @@ -46,6 +44,7 @@ public StorageStateChange(StorageState storageState, List changedFlagsKe /** * Construct a new StorageStateChange. + * * @param storageState state of the storage */ public StorageStateChange(StorageState storageState) { diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/Connector.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/Connector.java index 1a00737b5..d8a75a035 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/Connector.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/Connector.java @@ -3,8 +3,8 @@ import java.util.concurrent.BlockingQueue; /** - * Contract of the in-process storage connector. Connectors are responsible to stream flag configurations in - * {@link QueuePayload} format. + * Contract of the in-process storage connector. Connectors are responsible to stream flag + * configurations in {@link QueuePayload} format. */ public interface Connector { void init() throws Exception; diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/QueuePayload.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/QueuePayload.java index e9983a42d..3190f2d05 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/QueuePayload.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/QueuePayload.java @@ -4,9 +4,7 @@ import lombok.AllArgsConstructor; import lombok.Getter; -/** - * Payload emitted by a {@link Connector}. - */ +/** Payload emitted by a {@link Connector}. */ @AllArgsConstructor @Getter public class QueuePayload { diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/QueuePayloadType.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/QueuePayloadType.java index 4839dab51..abde235ab 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/QueuePayloadType.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/QueuePayloadType.java @@ -1,8 +1,6 @@ package dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector; -/** - * Payload type emitted by {@link Connector}. - */ +/** Payload type emitted by {@link Connector}. */ public enum QueuePayloadType { DATA, ERROR diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/file/FileConnector.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/file/FileConnector.java index a60c58be2..31129ea1b 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/file/FileConnector.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/file/FileConnector.java @@ -1,5 +1,9 @@ package dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.file; +import dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.Connector; +import dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.QueuePayload; +import dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.QueuePayloadType; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -7,19 +11,15 @@ import java.nio.file.Paths; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; - -import dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.Connector; -import dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.QueuePayload; -import dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.QueuePayloadType; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import lombok.extern.slf4j.Slf4j; /** - * File connector reads flag configurations from a given file, polls for changes and expose the content through - * {@code Connector} contract. - * The implementation is kept minimal and suites testing, local development needs. + * File connector reads flag configurations from a given file, polls for changes and expose the + * content through {@code Connector} contract. The implementation is kept minimal and suites + * testing, local development needs. */ -@SuppressFBWarnings(value = {"EI_EXPOSE_REP", "PATH_TRAVERSAL_IN"}, +@SuppressFBWarnings( + value = {"EI_EXPOSE_REP", "PATH_TRAVERSAL_IN"}, justification = "File connector read feature flag from a file source.") @Slf4j public class FileConnector implements Connector { @@ -36,7 +36,8 @@ public FileConnector(final String flagSourcePath) { } /** - * Initialize file connector. Reads file content, poll for changes and offer content through the queue. + * Initialize file connector. Reads file content, poll for changes and offer content through the + * queue. */ public void init() throws IOException { Thread watcherT = new Thread(() -> { @@ -83,16 +84,12 @@ public void init() throws IOException { log.info(String.format("Using feature flag configurations from file %s", flagSourcePath)); } - /** - * Expose the queue to fulfil the {@code Connector} contract. - */ + /** Expose the queue to fulfil the {@code Connector} contract. */ public BlockingQueue getStream() { return queue; } - /** - * Shutdown file connector. - */ + /** Shutdown file connector. */ public void shutdown() throws InterruptedException { shutdown = true; } diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/grpc/GrpcStreamConnector.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/grpc/GrpcStreamConnector.java index 39e39397d..d0e2083fd 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/grpc/GrpcStreamConnector.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/grpc/GrpcStreamConnector.java @@ -1,10 +1,5 @@ package dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.grpc; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - import dev.openfeature.contrib.providers.flagd.FlagdOptions; import dev.openfeature.contrib.providers.flagd.resolver.common.ChannelBuilder; import dev.openfeature.contrib.providers.flagd.resolver.common.backoff.GrpcStreamConnectorBackoffService; @@ -22,16 +17,20 @@ import io.grpc.Context; import io.grpc.Context.CancellableContext; import io.grpc.ManagedChannel; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import lombok.extern.slf4j.Slf4j; import org.slf4j.event.Level; /** - * Implements the {@link Connector} contract and emit flags obtained from flagd - * sync gRPC contract. + * Implements the {@link Connector} contract and emit flags obtained from flagd sync gRPC contract. */ @Slf4j -@SuppressFBWarnings(value = { "PREDICTABLE_RANDOM", - "EI_EXPOSE_REP" }, justification = "Random is used to generate a variation & flag configurations require exposing") +@SuppressFBWarnings( + value = {"PREDICTABLE_RANDOM", "EI_EXPOSE_REP"}, + justification = "Random is used to generate a variation & flag configurations require exposing") public class GrpcStreamConnector implements Connector { private static final int QUEUE_SIZE = 5; @@ -60,14 +59,19 @@ public GrpcStreamConnector(final FlagdOptions options) { retryBackoffMillis = options.getRetryBackoffMs(); } - /** - * Initialize gRPC stream connector. - */ + /** Initialize gRPC stream connector. */ public void init() { Thread listener = new Thread(() -> { try { - observeEventStream(blockingQueue, shutdown, serviceStub, serviceBlockingStub, selector, deadline, - streamDeadlineMs, retryBackoffMillis); + observeEventStream( + blockingQueue, + shutdown, + serviceStub, + serviceBlockingStub, + selector, + deadline, + streamDeadlineMs, + retryBackoffMillis); } catch (InterruptedException e) { log.warn("gRPC event stream interrupted, flag configurations are stale", e); Thread.currentThread().interrupt(); @@ -78,9 +82,7 @@ public void init() { listener.start(); } - /** - * Get blocking queue to obtain payloads exposed by this connector. - */ + /** Get blocking queue to obtain payloads exposed by this connector. */ public BlockingQueue getStream() { return blockingQueue; } @@ -109,17 +111,16 @@ public void shutdown() throws InterruptedException { } } - /** - * Contains blocking calls, to be used concurrently. - */ - static void observeEventStream(final BlockingQueue writeTo, - final AtomicBoolean shutdown, - final FlagSyncServiceStub serviceStub, - final FlagSyncServiceBlockingStub serviceBlockingStub, - final String selector, - final int deadline, - final int streamDeadlineMs, - int retryBackoffMillis) + /** Contains blocking calls, to be used concurrently. */ + static void observeEventStream( + final BlockingQueue writeTo, + final AtomicBoolean shutdown, + final FlagSyncServiceStub serviceStub, + final FlagSyncServiceBlockingStub serviceBlockingStub, + final String selector, + final int deadline, + final int streamDeadlineMs, + int retryBackoffMillis) throws InterruptedException { final BlockingQueue streamReceiver = new LinkedBlockingQueue<>(QUEUE_SIZE); @@ -150,7 +151,8 @@ static void observeEventStream(final BlockingQueue writeTo, localServiceStub.syncFlags(syncRequest.build(), new GrpcStreamHandler(streamReceiver)); try { - metadataResponse = serviceBlockingStub.withDeadlineAfter(deadline, TimeUnit.MILLISECONDS) + metadataResponse = serviceBlockingStub + .withDeadlineAfter(deadline, TimeUnit.MILLISECONDS) .getMetadata(metadataRequest.build()); } catch (Exception e) { // the chances this call fails but the syncRequest does not are slim @@ -179,8 +181,8 @@ static void observeEventStream(final BlockingQueue writeTo, logExceptions(Level.INFO, streamException, metadataException, retryDelay); } else { logExceptions(Level.ERROR, streamException, metadataException, retryDelay); - if (!writeTo.offer(new QueuePayload(QueuePayloadType.ERROR, - "Error from stream or metadata", metadataResponse))) { + if (!writeTo.offer(new QueuePayload( + QueuePayloadType.ERROR, "Error from stream or metadata", metadataResponse))) { log.error("Failed to convey ERROR status, queue is full"); } } @@ -213,8 +215,8 @@ static void observeEventStream(final BlockingQueue writeTo, log.info("Shutdown invoked, exiting event stream listener"); } - private static void logExceptions(Level logLevel, Throwable streamException, Exception metadataException, - long retryDelay) { + private static void logExceptions( + Level logLevel, Throwable streamException, Exception metadataException, long retryDelay) { if (streamException != null) { log.atLevel(logLevel) .setCause(streamException) diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/grpc/GrpcStreamHandler.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/grpc/GrpcStreamHandler.java index 743058ad7..4435bdc89 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/grpc/GrpcStreamHandler.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/grpc/GrpcStreamHandler.java @@ -1,11 +1,9 @@ package dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.grpc; +import dev.openfeature.flagd.grpc.sync.Sync.SyncFlagsResponse; import io.grpc.stub.StreamObserver; -import lombok.extern.slf4j.Slf4j; - import java.util.concurrent.BlockingQueue; - -import dev.openfeature.flagd.grpc.sync.Sync.SyncFlagsResponse; +import lombok.extern.slf4j.Slf4j; @Slf4j class GrpcStreamHandler implements StreamObserver { diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/Fractional.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/Fractional.java index 1530dbe96..b3f6d7c01 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/Fractional.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/Fractional.java @@ -3,14 +3,13 @@ import io.github.jamsesso.jsonlogic.JsonLogicException; import io.github.jamsesso.jsonlogic.evaluator.JsonLogicEvaluationException; import io.github.jamsesso.jsonlogic.evaluator.expressions.PreEvaluatedArgumentsExpression; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.codec.digest.MurmurHash3; - import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.digest.MurmurHash3; @Slf4j class Fractional implements PreEvaluatedArgumentsExpression { @@ -68,9 +67,8 @@ public Object evaluate(List arguments, Object data) throws JsonLogicEvaluationEx } private static String distributeValue( - final String hashKey, - final List propertyList, - int totalWeight) throws JsonLogicEvaluationException { + final String hashKey, final List propertyList, int totalWeight) + throws JsonLogicEvaluationException { byte[] bytes = hashKey.getBytes(StandardCharsets.UTF_8); int mmrHash = MurmurHash3.hash32x86(bytes, 0, bytes.length, 0); float bucket = Math.abs(mmrHash) * 1.0f / Integer.MAX_VALUE * 100; @@ -89,7 +87,7 @@ private static String distributeValue( } @Getter - @SuppressWarnings({ "checkstyle:NoFinalizer" }) + @SuppressWarnings({"checkstyle:NoFinalizer"}) private static class FractionProperty { private final String variant; private final int weight; diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/Operator.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/Operator.java index ba56f142b..3df419993 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/Operator.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/Operator.java @@ -3,16 +3,14 @@ import dev.openfeature.sdk.EvaluationContext; import io.github.jamsesso.jsonlogic.JsonLogic; import io.github.jamsesso.jsonlogic.JsonLogicException; -import lombok.Getter; - import java.time.Instant; import java.util.HashMap; import java.util.Map; +import lombok.Getter; /** - * Targeting operator wraps JsonLogic handlers and expose a simple API for - * external layers. - * This helps to isolate external dependencies to this package. + * Targeting operator wraps JsonLogic handlers and expose a simple API for external layers. This + * helps to isolate external dependencies to this package. */ public class Operator { @@ -23,9 +21,7 @@ public class Operator { private final JsonLogic jsonLogicHandler; - /** - * Construct a targeting operator. - */ + /** Construct a targeting operator. */ public Operator() { jsonLogicHandler = new JsonLogic(); jsonLogicHandler.addOperation(new Fractional()); @@ -34,9 +30,7 @@ public Operator() { jsonLogicHandler.addOperation(new StringComp(StringComp.Type.ENDS_WITH)); } - /** - * Apply this operator on the provided rule. - */ + /** Apply this operator on the provided rule. */ public Object apply(final String flagKey, final String targetingRule, final EvaluationContext ctx) throws TargetingRuleException { final Map flagdProperties = new HashMap<>(); @@ -47,7 +41,8 @@ public Object apply(final String flagKey, final String targetingRule, final Eval final Map targetingCtxData = ctx.asObjectMap(); - // asObjectMap() does not provide explicitly set targeting key (ex:- new ImmutableContext("TargetingKey") ). + // asObjectMap() does not provide explicitly set targeting key (ex:- new + // ImmutableContext("TargetingKey") ). // Hence, we add this explicitly here for targeting rule processing. targetingCtxData.put(TARGET_KEY, ctx.getTargetingKey()); targetingCtxData.put(FLAGD_PROPS_KEY, flagdProperties); @@ -60,8 +55,8 @@ public Object apply(final String flagKey, final String targetingRule, final Eval } /** - * A utility class to extract well-known properties such as flag key, targeting key and timestamp from json logic - * evaluation context data for further processing at evaluators. + * A utility class to extract well-known properties such as flag key, targeting key and timestamp + * from json logic evaluation context data for further processing at evaluators. */ @Getter static class FlagProperties { diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/SemVer.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/SemVer.java index 4ae83b99f..223d62f9a 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/SemVer.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/SemVer.java @@ -2,12 +2,11 @@ import io.github.jamsesso.jsonlogic.evaluator.JsonLogicEvaluationException; import io.github.jamsesso.jsonlogic.evaluator.expressions.PreEvaluatedArgumentsExpression; -import lombok.extern.slf4j.Slf4j; -import org.semver4j.Semver; - import java.util.HashSet; import java.util.List; import java.util.Set; +import lombok.extern.slf4j.Slf4j; +import org.semver4j.Semver; @Slf4j class SemVer implements PreEvaluatedArgumentsExpression { @@ -106,5 +105,4 @@ private static boolean compare(final String operator, final Semver arg1, final S String.format("Unsupported operator received. Operator: %s", operator)); } } - } diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/StringComp.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/StringComp.java index 7e91dafec..ce3490ead 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/StringComp.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/StringComp.java @@ -2,9 +2,8 @@ import io.github.jamsesso.jsonlogic.evaluator.JsonLogicEvaluationException; import io.github.jamsesso.jsonlogic.evaluator.expressions.PreEvaluatedArgumentsExpression; -import lombok.extern.slf4j.Slf4j; - import java.util.List; +import lombok.extern.slf4j.Slf4j; @Slf4j class StringComp implements PreEvaluatedArgumentsExpression { @@ -53,7 +52,6 @@ public Object evaluate(List arguments, Object data) throws JsonLogicEvaluationEx } } - enum Type { STARTS_WITH("starts_with"), ENDS_WITH("ends_with"); diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/TargetingRuleException.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/TargetingRuleException.java index 55bfccc9d..48522d7ad 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/TargetingRuleException.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/TargetingRuleException.java @@ -1,13 +1,9 @@ package dev.openfeature.contrib.providers.flagd.resolver.process.targeting; -/** - * Exception used by targeting rule package. - **/ +/** Exception used by targeting rule package. */ public class TargetingRuleException extends Exception { - /** - * Construct exception. - **/ + /** Construct exception. */ public TargetingRuleException(final String message, final Throwable t) { super(message, t); } diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdOptionsTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdOptionsTest.java index 71adc687f..c8e8aba1c 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdOptionsTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdOptionsTest.java @@ -1,19 +1,18 @@ package dev.openfeature.contrib.providers.flagd; +import static dev.openfeature.contrib.providers.flagd.Config.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + import dev.openfeature.contrib.providers.flagd.resolver.process.storage.MockConnector; import dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.Connector; import io.opentelemetry.api.OpenTelemetry; +import java.util.function.Function; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junitpioneer.jupiter.SetEnvironmentVariable; import org.mockito.Mockito; -import java.util.function.Function; - -import static dev.openfeature.contrib.providers.flagd.Config.*; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.*; - class FlagdOptionsTest { @Test @@ -74,7 +73,6 @@ void TestBuilderOptions() { assertEquals(1000, flagdOptions.getKeepAlive()); } - @Test void testValueProviderForEdgeCase_valid() { Function valueProvider = s -> "in-process"; @@ -136,13 +134,10 @@ void usesSetOldAndNewName() { } } - - @Test void testInProcessProvider_noPortConfigured_defaultsToCorrectPort() { - FlagdOptions flagdOptions = FlagdOptions.builder() - .resolverType(Resolver.IN_PROCESS) - .build(); + FlagdOptions flagdOptions = + FlagdOptions.builder().resolverType(Resolver.IN_PROCESS).build(); assertThat(flagdOptions.getResolverType()).isEqualTo(Resolver.IN_PROCESS); assertThat(flagdOptions.getPort()).isEqualTo(Integer.parseInt(DEFAULT_IN_PROCESS_PORT)); @@ -151,9 +146,7 @@ void testInProcessProvider_noPortConfigured_defaultsToCorrectPort() { @Test @SetEnvironmentVariable(key = RESOLVER_ENV_VAR, value = RESOLVER_IN_PROCESS) void testInProcessProviderFromEnv_portConfigured_usesConfiguredPort() { - FlagdOptions flagdOptions = FlagdOptions.builder() - .port(1000) - .build(); + FlagdOptions flagdOptions = FlagdOptions.builder().port(1000).build(); assertThat(flagdOptions.getResolverType()).isEqualTo(Resolver.IN_PROCESS); assertThat(flagdOptions.getPort()).isEqualTo(1000); @@ -170,9 +163,8 @@ void testRpcProviderFromEnv_noPortConfigured_defaultsToCorrectPort() { @Test void testRpcProvider_noPortConfigured_defaultsToCorrectPort() { - FlagdOptions flagdOptions = FlagdOptions.builder() - .resolverType(Resolver.RPC) - .build(); + FlagdOptions flagdOptions = + FlagdOptions.builder().resolverType(Resolver.RPC).build(); assertThat(flagdOptions.getResolverType()).isEqualTo(Resolver.RPC); assertThat(flagdOptions.getPort()).isEqualTo(Integer.parseInt(DEFAULT_RPC_PORT)); @@ -181,13 +173,10 @@ void testRpcProvider_noPortConfigured_defaultsToCorrectPort() { @Test @SetEnvironmentVariable(key = RESOLVER_ENV_VAR, value = RESOLVER_RPC) void testRpcProviderFromEnv_portConfigured_usesConfiguredPort() { - FlagdOptions flagdOptions = FlagdOptions.builder() - .port(1534) - .build(); + FlagdOptions flagdOptions = FlagdOptions.builder().port(1534).build(); assertThat(flagdOptions.getResolverType()).isEqualTo(Resolver.RPC); assertThat(flagdOptions.getPort()).isEqualTo(1534); - } @Test 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 2a5850172..0186ff38d 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 @@ -17,29 +17,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.concurrent.atomic.AtomicReference; -import java.util.Collections; -import java.util.Optional; - - -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.mockito.MockedConstruction; -import org.mockito.MockedStatic; - import com.google.protobuf.Struct; - import dev.openfeature.contrib.providers.flagd.resolver.Resolver; import dev.openfeature.contrib.providers.flagd.resolver.common.ConnectionEvent; import dev.openfeature.contrib.providers.flagd.resolver.grpc.GrpcConnector; @@ -74,6 +52,23 @@ import io.cucumber.java.AfterAll; import io.grpc.Channel; import io.grpc.Deadline; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.mockito.MockedConstruction; +import org.mockito.MockedStatic; class FlagdProviderTest { private static final String FLAG_KEY = "some-key"; @@ -93,8 +88,10 @@ class FlagdProviderTest { private static final String INNER_STRUCT_KEY = "inner_key"; private static final String INNER_STRUCT_VALUE = "inner_value"; private static final com.google.protobuf.Struct PROTOBUF_STRUCTURE_VALUE = Struct.newBuilder() - .putFields(INNER_STRUCT_KEY, - com.google.protobuf.Value.newBuilder().setStringValue(INNER_STRUCT_VALUE) + .putFields( + INNER_STRUCT_KEY, + com.google.protobuf.Value.newBuilder() + .setStringValue(INNER_STRUCT_VALUE) .build()) .build(); private static final String STRING_VALUE = "hi!"; @@ -147,20 +144,15 @@ void resolvers_call_grpc_service_and_return_details() { when(serviceBlockingStubMock.withDeadlineAfter(anyLong(), any(TimeUnit.class))) .thenReturn(serviceBlockingStubMock); - when(serviceBlockingStubMock - .resolveBoolean(argThat(x -> FLAG_KEY_BOOLEAN.equals(x.getFlagKey())))) + when(serviceBlockingStubMock.resolveBoolean(argThat(x -> FLAG_KEY_BOOLEAN.equals(x.getFlagKey())))) .thenReturn(booleanResponse); - when(serviceBlockingStubMock - .resolveFloat(argThat(x -> FLAG_KEY_DOUBLE.equals(x.getFlagKey())))) + when(serviceBlockingStubMock.resolveFloat(argThat(x -> FLAG_KEY_DOUBLE.equals(x.getFlagKey())))) .thenReturn(floatResponse); - when(serviceBlockingStubMock - .resolveInt(argThat(x -> FLAG_KEY_INTEGER.equals(x.getFlagKey())))) + when(serviceBlockingStubMock.resolveInt(argThat(x -> FLAG_KEY_INTEGER.equals(x.getFlagKey())))) .thenReturn(intResponse); - when(serviceBlockingStubMock - .resolveString(argThat(x -> FLAG_KEY_STRING.equals(x.getFlagKey())))) + when(serviceBlockingStubMock.resolveString(argThat(x -> FLAG_KEY_STRING.equals(x.getFlagKey())))) .thenReturn(stringResponse); - when(serviceBlockingStubMock - .resolveObject(argThat(x -> FLAG_KEY_OBJECT.equals(x.getFlagKey())))) + when(serviceBlockingStubMock.resolveObject(argThat(x -> FLAG_KEY_OBJECT.equals(x.getFlagKey())))) .thenReturn(objectResponse); GrpcConnector grpc = mock(GrpcConnector.class); @@ -168,14 +160,12 @@ void resolvers_call_grpc_service_and_return_details() { OpenFeatureAPI.getInstance().setProviderAndWait(createProvider(grpc)); - FlagEvaluationDetails booleanDetails = api.getClient().getBooleanDetails(FLAG_KEY_BOOLEAN, - false); + FlagEvaluationDetails booleanDetails = api.getClient().getBooleanDetails(FLAG_KEY_BOOLEAN, false); assertTrue(booleanDetails.getValue()); assertEquals(BOOL_VARIANT, booleanDetails.getVariant()); assertEquals(DEFAULT.toString(), booleanDetails.getReason()); - FlagEvaluationDetails stringDetails = api.getClient().getStringDetails(FLAG_KEY_STRING, - "wrong"); + FlagEvaluationDetails stringDetails = api.getClient().getStringDetails(FLAG_KEY_STRING, "wrong"); assertEquals(STRING_VALUE, stringDetails.getValue()); assertEquals(STRING_VARIANT, stringDetails.getVariant()); assertEquals(DEFAULT.toString(), stringDetails.getReason()); @@ -190,10 +180,15 @@ void resolvers_call_grpc_service_and_return_details() { assertEquals(DOUBLE_VARIANT, floatDetails.getVariant()); assertEquals(DEFAULT.toString(), floatDetails.getReason()); - FlagEvaluationDetails objectDetails = api.getClient().getObjectDetails(FLAG_KEY_OBJECT, - new Value()); - assertEquals(INNER_STRUCT_VALUE, objectDetails.getValue().asStructure() - .asMap().get(INNER_STRUCT_KEY).asString()); + FlagEvaluationDetails objectDetails = api.getClient().getObjectDetails(FLAG_KEY_OBJECT, new Value()); + assertEquals( + INNER_STRUCT_VALUE, + objectDetails + .getValue() + .asStructure() + .asMap() + .get(INNER_STRUCT_KEY) + .asString()); assertEquals(OBJECT_VARIANT, objectDetails.getVariant()); assertEquals(DEFAULT.toString(), objectDetails.getReason()); } @@ -228,20 +223,15 @@ void zero_value() { ServiceBlockingStub serviceBlockingStubMock = mock(ServiceBlockingStub.class); when(serviceBlockingStubMock.withDeadlineAfter(anyLong(), any(TimeUnit.class))) .thenReturn(serviceBlockingStubMock); - when(serviceBlockingStubMock - .resolveBoolean(argThat(x -> FLAG_KEY_BOOLEAN.equals(x.getFlagKey())))) + when(serviceBlockingStubMock.resolveBoolean(argThat(x -> FLAG_KEY_BOOLEAN.equals(x.getFlagKey())))) .thenReturn(booleanResponse); - when(serviceBlockingStubMock - .resolveFloat(argThat(x -> FLAG_KEY_DOUBLE.equals(x.getFlagKey())))) + when(serviceBlockingStubMock.resolveFloat(argThat(x -> FLAG_KEY_DOUBLE.equals(x.getFlagKey())))) .thenReturn(floatResponse); - when(serviceBlockingStubMock - .resolveInt(argThat(x -> FLAG_KEY_INTEGER.equals(x.getFlagKey())))) + when(serviceBlockingStubMock.resolveInt(argThat(x -> FLAG_KEY_INTEGER.equals(x.getFlagKey())))) .thenReturn(intResponse); - when(serviceBlockingStubMock - .resolveString(argThat(x -> FLAG_KEY_STRING.equals(x.getFlagKey())))) + when(serviceBlockingStubMock.resolveString(argThat(x -> FLAG_KEY_STRING.equals(x.getFlagKey())))) .thenReturn(stringResponse); - when(serviceBlockingStubMock - .resolveObject(argThat(x -> FLAG_KEY_OBJECT.equals(x.getFlagKey())))) + when(serviceBlockingStubMock.resolveObject(argThat(x -> FLAG_KEY_OBJECT.equals(x.getFlagKey())))) .thenReturn(objectResponse); GrpcConnector grpc = mock(GrpcConnector.class); @@ -249,14 +239,12 @@ void zero_value() { OpenFeatureAPI.getInstance().setProviderAndWait(createProvider(grpc)); - FlagEvaluationDetails booleanDetails = api.getClient().getBooleanDetails(FLAG_KEY_BOOLEAN, - false); + FlagEvaluationDetails booleanDetails = api.getClient().getBooleanDetails(FLAG_KEY_BOOLEAN, false); assertEquals(false, booleanDetails.getValue()); assertEquals(BOOL_VARIANT, booleanDetails.getVariant()); assertEquals(DEFAULT.toString(), booleanDetails.getReason()); - FlagEvaluationDetails stringDetails = api.getClient().getStringDetails(FLAG_KEY_STRING, - "wrong"); + FlagEvaluationDetails stringDetails = api.getClient().getStringDetails(FLAG_KEY_STRING, "wrong"); assertEquals("", stringDetails.getValue()); assertEquals(STRING_VARIANT, stringDetails.getVariant()); assertEquals(DEFAULT.toString(), stringDetails.getReason()); @@ -271,8 +259,7 @@ void zero_value() { assertEquals(DOUBLE_VARIANT, floatDetails.getVariant()); assertEquals(DEFAULT.toString(), floatDetails.getReason()); - FlagEvaluationDetails objectDetails = api.getClient().getObjectDetails(FLAG_KEY_OBJECT, - new Value()); + FlagEvaluationDetails objectDetails = api.getClient().getObjectDetails(FLAG_KEY_OBJECT, new Value()); assertEquals(new MutableStructure(), objectDetails.getValue().asObject()); assertEquals(OBJECT_VARIANT, objectDetails.getVariant()); assertEquals(DEFAULT.toString(), objectDetails.getReason()); @@ -283,17 +270,21 @@ void test_metadata_from_grpc_response() { // given final Map metadataInput = new HashMap<>(); - com.google.protobuf.Value scope = com.google.protobuf.Value.newBuilder().setStringValue("flagd-scope") + com.google.protobuf.Value scope = com.google.protobuf.Value.newBuilder() + .setStringValue("flagd-scope") .build(); metadataInput.put("scope", scope); - com.google.protobuf.Value bool = com.google.protobuf.Value.newBuilder().setBoolValue(true).build(); + com.google.protobuf.Value bool = + com.google.protobuf.Value.newBuilder().setBoolValue(true).build(); metadataInput.put("boolean", bool); - com.google.protobuf.Value number = com.google.protobuf.Value.newBuilder().setNumberValue(1).build(); + com.google.protobuf.Value number = + com.google.protobuf.Value.newBuilder().setNumberValue(1).build(); metadataInput.put("number", number); - final Struct metadataStruct = Struct.newBuilder().putAllFields(metadataInput).build(); + final Struct metadataStruct = + Struct.newBuilder().putAllFields(metadataInput).build(); ResolveBooleanResponse booleanResponse = ResolveBooleanResponse.newBuilder() .setValue(true) @@ -304,10 +295,9 @@ void test_metadata_from_grpc_response() { ServiceBlockingStub serviceBlockingStubMock = mock(ServiceBlockingStub.class); - when(serviceBlockingStubMock.withDeadlineAfter(anyLong(), any(TimeUnit.class))).thenReturn( - serviceBlockingStubMock); - when(serviceBlockingStubMock - .resolveBoolean(argThat(x -> FLAG_KEY_BOOLEAN.equals(x.getFlagKey())))) + when(serviceBlockingStubMock.withDeadlineAfter(anyLong(), any(TimeUnit.class))) + .thenReturn(serviceBlockingStubMock); + when(serviceBlockingStubMock.resolveBoolean(argThat(x -> FLAG_KEY_BOOLEAN.equals(x.getFlagKey())))) .thenReturn(booleanResponse); GrpcConnector grpc = mock(GrpcConnector.class); @@ -315,8 +305,7 @@ void test_metadata_from_grpc_response() { OpenFeatureAPI.getInstance().setProviderAndWait(createProvider(grpc)); // when - FlagEvaluationDetails booleanDetails = api.getClient().getBooleanDetails(FLAG_KEY_BOOLEAN, - false); + FlagEvaluationDetails booleanDetails = api.getClient().getBooleanDetails(FLAG_KEY_BOOLEAN, false); // then final ImmutableMetadata metadata = booleanDetails.getFlagMetadata(); @@ -361,8 +350,7 @@ void context_is_parsed_and_passed_to_grpc_service() { } }; final String STRUCT_ATTR_INNER_VALUE = "struct-inner-value"; - final Structure STRUCT_ATTR_VALUE = new MutableStructure().add(STRUCT_ATTR_INNER_KEY, - STRUCT_ATTR_INNER_VALUE); + final Structure STRUCT_ATTR_VALUE = new MutableStructure().add(STRUCT_ATTR_INNER_KEY, STRUCT_ATTR_INNER_VALUE); final String DEFAULT_STRING = "DEFAULT"; ResolveBooleanResponse booleanResponse = ResolveBooleanResponse.newBuilder() @@ -374,27 +362,31 @@ void context_is_parsed_and_passed_to_grpc_service() { ServiceBlockingStub serviceBlockingStubMock = mock(ServiceBlockingStub.class); when(serviceBlockingStubMock.withDeadlineAfter(anyLong(), any(TimeUnit.class))) .thenReturn(serviceBlockingStubMock); - when(serviceBlockingStubMock.resolveBoolean(argThat( - x -> { + when(serviceBlockingStubMock.resolveBoolean(argThat(x -> { final Struct struct = x.getContext(); final Map valueMap = struct.getFieldsMap(); - return STRING_ATTR_VALUE.equals(valueMap.get(STRING_ATTR_KEY).getStringValue()) + return STRING_ATTR_VALUE.equals( + valueMap.get(STRING_ATTR_KEY).getStringValue()) && INT_ATTR_VALUE == valueMap.get(INT_ATTR_KEY).getNumberValue() - && DOUBLE_ATTR_VALUE == valueMap.get(DOUBLE_ATTR_KEY) - .getNumberValue() + && DOUBLE_ATTR_VALUE + == valueMap.get(DOUBLE_ATTR_KEY).getNumberValue() && valueMap.get(BOOLEAN_ATTR_KEY).getBoolValue() - && "MY_TARGETING_KEY".equals( - valueMap.get("targetingKey").getStringValue()) - && LIST_ATTR_VALUE.get(0).asInteger() == valueMap - .get(LIST_ATTR_KEY).getListValue() - .getValuesList().get(0).getNumberValue() - && STRUCT_ATTR_INNER_VALUE.equals( - valueMap.get(STRUCT_ATTR_KEY).getStructValue() - .getFieldsMap() - .get(STRUCT_ATTR_INNER_KEY) - .getStringValue()); - }))).thenReturn(booleanResponse); + && "MY_TARGETING_KEY" + .equals(valueMap.get("targetingKey").getStringValue()) + && LIST_ATTR_VALUE.get(0).asInteger() + == valueMap.get(LIST_ATTR_KEY) + .getListValue() + .getValuesList() + .get(0) + .getNumberValue() + && STRUCT_ATTR_INNER_VALUE.equals(valueMap.get(STRUCT_ATTR_KEY) + .getStructValue() + .getFieldsMap() + .get(STRUCT_ATTR_INNER_KEY) + .getStringValue()); + }))) + .thenReturn(booleanResponse); GrpcConnector grpc = mock(GrpcConnector.class); when(grpc.getResolver()).thenReturn(serviceBlockingStubMock); @@ -409,8 +401,7 @@ void context_is_parsed_and_passed_to_grpc_service() { context.add(STRING_ATTR_KEY, STRING_ATTR_VALUE); context.add(STRUCT_ATTR_KEY, STRUCT_ATTR_VALUE); - FlagEvaluationDetails booleanDetails = api.getClient().getBooleanDetails(FLAG_KEY, false, - context); + FlagEvaluationDetails booleanDetails = api.getClient().getBooleanDetails(FLAG_KEY, false, context); assertTrue(booleanDetails.getValue()); assertEquals(BOOL_VARIANT, booleanDetails.getVariant()); assertEquals(DEFAULT.toString(), booleanDetails.getReason()); @@ -434,7 +425,9 @@ void null_context_handling() { when(serviceBlockingStubMock.withDeadlineAfter(anyLong(), any(TimeUnit.class))) .thenReturn(serviceBlockingStubMock); when(serviceBlockingStubMock.resolveBoolean(any())) - .thenReturn(ResolveBooleanResponse.newBuilder().setValue(expectedVariant).build()); + .thenReturn(ResolveBooleanResponse.newBuilder() + .setValue(expectedVariant) + .build()); GrpcConnector grpc = mock(GrpcConnector.class); when(grpc.getResolver()).thenReturn(serviceBlockingStubMock); @@ -467,10 +460,10 @@ void reason_mapped_correctly_if_unknown() { OpenFeatureAPI.getInstance().setProviderAndWait(createProvider(grpc)); - FlagEvaluationDetails booleanDetails = api.getClient() - .getBooleanDetails(FLAG_KEY, false, new MutableContext()); + FlagEvaluationDetails booleanDetails = + api.getClient().getBooleanDetails(FLAG_KEY, false, new MutableContext()); assertEquals(Reason.UNKNOWN.toString(), booleanDetails.getReason()); // reason should be converted to - // UNKNOWN + // UNKNOWN } @Test @@ -509,53 +502,46 @@ void invalidate_cache() { ServiceStub serviceStubMock = mock(ServiceStub.class); when(serviceStubMock.withWaitForReady()).thenReturn(serviceStubMock); doNothing().when(serviceStubMock).eventStream(any(), any()); - when(serviceStubMock.withDeadline(any(Deadline.class))) - .thenReturn(serviceStubMock); + when(serviceStubMock.withDeadline(any(Deadline.class))).thenReturn(serviceStubMock); when(serviceBlockingStubMock.withWaitForReady()).thenReturn(serviceBlockingStubMock); - when(serviceBlockingStubMock - .withDeadline(any(Deadline.class))) - .thenReturn(serviceBlockingStubMock); + when(serviceBlockingStubMock.withDeadline(any(Deadline.class))).thenReturn(serviceBlockingStubMock); when(serviceBlockingStubMock.withDeadlineAfter(anyLong(), any(TimeUnit.class))) .thenReturn(serviceBlockingStubMock); - when(serviceBlockingStubMock - .resolveBoolean(argThat(x -> FLAG_KEY_BOOLEAN.equals(x.getFlagKey())))) + when(serviceBlockingStubMock.resolveBoolean(argThat(x -> FLAG_KEY_BOOLEAN.equals(x.getFlagKey())))) .thenReturn(booleanResponse); - when(serviceBlockingStubMock - .resolveFloat(argThat(x -> FLAG_KEY_DOUBLE.equals(x.getFlagKey())))) + when(serviceBlockingStubMock.resolveFloat(argThat(x -> FLAG_KEY_DOUBLE.equals(x.getFlagKey())))) .thenReturn(floatResponse); - when(serviceBlockingStubMock - .resolveInt(argThat(x -> FLAG_KEY_INTEGER.equals(x.getFlagKey())))) + when(serviceBlockingStubMock.resolveInt(argThat(x -> FLAG_KEY_INTEGER.equals(x.getFlagKey())))) .thenReturn(intResponse); - when(serviceBlockingStubMock - .resolveString(argThat(x -> FLAG_KEY_STRING.equals(x.getFlagKey())))) + when(serviceBlockingStubMock.resolveString(argThat(x -> FLAG_KEY_STRING.equals(x.getFlagKey())))) .thenReturn(stringResponse); - when(serviceBlockingStubMock - .resolveObject(argThat(x -> FLAG_KEY_OBJECT.equals(x.getFlagKey())))) + when(serviceBlockingStubMock.resolveObject(argThat(x -> FLAG_KEY_OBJECT.equals(x.getFlagKey())))) .thenReturn(objectResponse); GrpcConnector grpc; try (MockedStatic mockStaticService = mockStatic(ServiceGrpc.class)) { - mockStaticService.when(() -> ServiceGrpc.newBlockingStub(any(Channel.class))) + mockStaticService + .when(() -> ServiceGrpc.newBlockingStub(any(Channel.class))) .thenReturn(serviceBlockingStubMock); - mockStaticService.when(() -> ServiceGrpc.newStub(any())) - .thenReturn(serviceStubMock); + mockStaticService.when(() -> ServiceGrpc.newStub(any())).thenReturn(serviceStubMock); final Cache cache = new Cache("lru", 5); class NoopInitGrpcConnector extends GrpcConnector { - public NoopInitGrpcConnector(FlagdOptions options, Cache cache, + public NoopInitGrpcConnector( + FlagdOptions options, + Cache cache, Supplier connectedSupplier, Consumer onConnectionEvent) { super(options, cache, connectedSupplier, onConnectionEvent); } - public void initialize() throws Exception { - }; + public void initialize() throws Exception {} + ; } - grpc = new NoopInitGrpcConnector(FlagdOptions.builder().build(), cache, () -> true, - (connectionEvent) -> { - }); + grpc = new NoopInitGrpcConnector( + FlagdOptions.builder().build(), cache, () -> true, (connectionEvent) -> {}); } FlagdProvider provider = createProvider(grpc); @@ -564,14 +550,27 @@ public void initialize() throws Exception { HashMap flagsMap = new HashMap(); HashMap structMap = new HashMap(); - flagsMap.put(FLAG_KEY_BOOLEAN, com.google.protobuf.Value.newBuilder().setStringValue("foo").build()); - flagsMap.put(FLAG_KEY_STRING, com.google.protobuf.Value.newBuilder().setStringValue("foo").build()); - flagsMap.put(FLAG_KEY_INTEGER, com.google.protobuf.Value.newBuilder().setStringValue("foo").build()); - flagsMap.put(FLAG_KEY_DOUBLE, com.google.protobuf.Value.newBuilder().setStringValue("foo").build()); - flagsMap.put(FLAG_KEY_OBJECT, com.google.protobuf.Value.newBuilder().setStringValue("foo").build()); - - structMap.put("flags", com.google.protobuf.Value.newBuilder() - .setStructValue(Struct.newBuilder().putAllFields(flagsMap)).build()); + flagsMap.put( + FLAG_KEY_BOOLEAN, + com.google.protobuf.Value.newBuilder().setStringValue("foo").build()); + flagsMap.put( + FLAG_KEY_STRING, + com.google.protobuf.Value.newBuilder().setStringValue("foo").build()); + flagsMap.put( + FLAG_KEY_INTEGER, + com.google.protobuf.Value.newBuilder().setStringValue("foo").build()); + flagsMap.put( + FLAG_KEY_DOUBLE, + com.google.protobuf.Value.newBuilder().setStringValue("foo").build()); + flagsMap.put( + FLAG_KEY_OBJECT, + com.google.protobuf.Value.newBuilder().setStringValue("foo").build()); + + structMap.put( + "flags", + com.google.protobuf.Value.newBuilder() + .setStructValue(Struct.newBuilder().putAllFields(flagsMap)) + .build()); // should cache results FlagEvaluationDetails booleanDetails; @@ -602,8 +601,14 @@ public void initialize() throws Exception { assertEquals(STATIC_REASON, floatDetails.getReason()); objectDetails = api.getClient().getObjectDetails(FLAG_KEY_OBJECT, new Value()); - assertEquals(INNER_STRUCT_VALUE, objectDetails.getValue().asStructure() - .asMap().get(INNER_STRUCT_KEY).asString()); + assertEquals( + INNER_STRUCT_VALUE, + objectDetails + .getValue() + .asStructure() + .asMap() + .get(INNER_STRUCT_KEY) + .asString()); assertEquals(OBJECT_VARIANT, objectDetails.getVariant()); assertEquals(STATIC_REASON, objectDetails.getReason()); } @@ -647,20 +652,15 @@ private void do_resolvers_cache_responses(String reason, Boolean eventStreamAliv ServiceBlockingStub serviceBlockingStubMock = mock(ServiceBlockingStub.class); when(serviceBlockingStubMock.withDeadlineAfter(anyLong(), any(TimeUnit.class))) .thenReturn(serviceBlockingStubMock); - when(serviceBlockingStubMock - .resolveBoolean(argThat(x -> FLAG_KEY_BOOLEAN.equals(x.getFlagKey())))) + when(serviceBlockingStubMock.resolveBoolean(argThat(x -> FLAG_KEY_BOOLEAN.equals(x.getFlagKey())))) .thenReturn(booleanResponse); - when(serviceBlockingStubMock - .resolveFloat(argThat(x -> FLAG_KEY_DOUBLE.equals(x.getFlagKey())))) + when(serviceBlockingStubMock.resolveFloat(argThat(x -> FLAG_KEY_DOUBLE.equals(x.getFlagKey())))) .thenReturn(floatResponse); - when(serviceBlockingStubMock - .resolveInt(argThat(x -> FLAG_KEY_INTEGER.equals(x.getFlagKey())))) + when(serviceBlockingStubMock.resolveInt(argThat(x -> FLAG_KEY_INTEGER.equals(x.getFlagKey())))) .thenReturn(intResponse); - when(serviceBlockingStubMock - .resolveString(argThat(x -> FLAG_KEY_STRING.equals(x.getFlagKey())))) + when(serviceBlockingStubMock.resolveString(argThat(x -> FLAG_KEY_STRING.equals(x.getFlagKey())))) .thenReturn(stringResponse); - when(serviceBlockingStubMock - .resolveObject(argThat(x -> FLAG_KEY_OBJECT.equals(x.getFlagKey())))) + when(serviceBlockingStubMock.resolveObject(argThat(x -> FLAG_KEY_OBJECT.equals(x.getFlagKey())))) .thenReturn(objectResponse); GrpcConnector grpc = mock(GrpcConnector.class); @@ -670,17 +670,15 @@ private void do_resolvers_cache_responses(String reason, Boolean eventStreamAliv // stream is alive OpenFeatureAPI.getInstance().setProviderAndWait(provider); - FlagEvaluationDetails booleanDetails = api.getClient().getBooleanDetails(FLAG_KEY_BOOLEAN, - false); - booleanDetails = api.getClient() - .getBooleanDetails(FLAG_KEY_BOOLEAN, false); // should retrieve from cache on second - // invocation + FlagEvaluationDetails booleanDetails = api.getClient().getBooleanDetails(FLAG_KEY_BOOLEAN, false); + booleanDetails = + api.getClient().getBooleanDetails(FLAG_KEY_BOOLEAN, false); // should retrieve from cache on second + // invocation assertTrue(booleanDetails.getValue()); assertEquals(BOOL_VARIANT, booleanDetails.getVariant()); assertEquals(expectedReason, booleanDetails.getReason()); - FlagEvaluationDetails stringDetails = api.getClient().getStringDetails(FLAG_KEY_STRING, - "wrong"); + FlagEvaluationDetails stringDetails = api.getClient().getStringDetails(FLAG_KEY_STRING, "wrong"); stringDetails = api.getClient().getStringDetails(FLAG_KEY_STRING, "wrong"); assertEquals(STRING_VALUE, stringDetails.getValue()); assertEquals(STRING_VARIANT, stringDetails.getVariant()); @@ -698,11 +696,16 @@ private void do_resolvers_cache_responses(String reason, Boolean eventStreamAliv assertEquals(DOUBLE_VARIANT, floatDetails.getVariant()); assertEquals(expectedReason, floatDetails.getReason()); - FlagEvaluationDetails objectDetails = api.getClient().getObjectDetails(FLAG_KEY_OBJECT, - new Value()); + FlagEvaluationDetails objectDetails = api.getClient().getObjectDetails(FLAG_KEY_OBJECT, new Value()); objectDetails = api.getClient().getObjectDetails(FLAG_KEY_OBJECT, new Value()); - assertEquals(INNER_STRUCT_VALUE, objectDetails.getValue().asStructure() - .asMap().get(INNER_STRUCT_KEY).asString()); + assertEquals( + INNER_STRUCT_VALUE, + objectDetails + .getValue() + .asStructure() + .asMap() + .get(INNER_STRUCT_KEY) + .asString()); assertEquals(OBJECT_VARIANT, objectDetails.getVariant()); assertEquals(expectedReason, objectDetails.getReason()); } @@ -742,27 +745,20 @@ void disabled_cache() { ServiceBlockingStub serviceBlockingStubMock = mock(ServiceBlockingStub.class); ServiceStub serviceStubMock = mock(ServiceStub.class); when(serviceStubMock.withWaitForReady()).thenReturn(serviceStubMock); - when(serviceStubMock.withDeadline(any(Deadline.class))) - .thenReturn(serviceStubMock); + when(serviceStubMock.withDeadline(any(Deadline.class))).thenReturn(serviceStubMock); when(serviceBlockingStubMock.withWaitForReady()).thenReturn(serviceBlockingStubMock); - when(serviceBlockingStubMock.withDeadline(any(Deadline.class))) - .thenReturn(serviceBlockingStubMock); + when(serviceBlockingStubMock.withDeadline(any(Deadline.class))).thenReturn(serviceBlockingStubMock); when(serviceBlockingStubMock.withDeadlineAfter(anyLong(), any(TimeUnit.class))) .thenReturn(serviceBlockingStubMock); - when(serviceBlockingStubMock - .resolveBoolean(argThat(x -> FLAG_KEY_BOOLEAN.equals(x.getFlagKey())))) + when(serviceBlockingStubMock.resolveBoolean(argThat(x -> FLAG_KEY_BOOLEAN.equals(x.getFlagKey())))) .thenReturn(booleanResponse); - when(serviceBlockingStubMock - .resolveFloat(argThat(x -> FLAG_KEY_DOUBLE.equals(x.getFlagKey())))) + when(serviceBlockingStubMock.resolveFloat(argThat(x -> FLAG_KEY_DOUBLE.equals(x.getFlagKey())))) .thenReturn(floatResponse); - when(serviceBlockingStubMock - .resolveInt(argThat(x -> FLAG_KEY_INTEGER.equals(x.getFlagKey())))) + when(serviceBlockingStubMock.resolveInt(argThat(x -> FLAG_KEY_INTEGER.equals(x.getFlagKey())))) .thenReturn(intResponse); - when(serviceBlockingStubMock - .resolveString(argThat(x -> FLAG_KEY_STRING.equals(x.getFlagKey())))) + when(serviceBlockingStubMock.resolveString(argThat(x -> FLAG_KEY_STRING.equals(x.getFlagKey())))) .thenReturn(stringResponse); - when(serviceBlockingStubMock - .resolveObject(argThat(x -> FLAG_KEY_OBJECT.equals(x.getFlagKey())))) + when(serviceBlockingStubMock.resolveObject(argThat(x -> FLAG_KEY_OBJECT.equals(x.getFlagKey())))) .thenReturn(objectResponse); // disabled cache @@ -770,25 +766,26 @@ void disabled_cache() { GrpcConnector grpc; try (MockedStatic mockStaticService = mockStatic(ServiceGrpc.class)) { - mockStaticService.when(() -> ServiceGrpc.newBlockingStub(any(Channel.class))) + mockStaticService + .when(() -> ServiceGrpc.newBlockingStub(any(Channel.class))) .thenReturn(serviceBlockingStubMock); - mockStaticService.when(() -> ServiceGrpc.newStub(any())) - .thenReturn(serviceStubMock); + mockStaticService.when(() -> ServiceGrpc.newStub(any())).thenReturn(serviceStubMock); class NoopInitGrpcConnector extends GrpcConnector { - public NoopInitGrpcConnector(FlagdOptions options, Cache cache, + public NoopInitGrpcConnector( + FlagdOptions options, + Cache cache, Supplier connectedSupplier, Consumer onConnectionEvent) { super(options, cache, connectedSupplier, onConnectionEvent); } - public void initialize() throws Exception { - }; + public void initialize() throws Exception {} + ; } - grpc = new NoopInitGrpcConnector(FlagdOptions.builder().build(), cache, () -> true, - (connectionEvent) -> { - }); + grpc = new NoopInitGrpcConnector( + FlagdOptions.builder().build(), cache, () -> true, (connectionEvent) -> {}); } FlagdProvider provider = createProvider(grpc, cache, () -> true); @@ -804,21 +801,24 @@ public void initialize() throws Exception { HashMap flagsMap = new HashMap<>(); HashMap structMap = new HashMap<>(); - flagsMap.put("foo", com.google.protobuf.Value.newBuilder().setStringValue("foo") - .build()); // assert that a configuration_change event works + flagsMap.put( + "foo", + com.google.protobuf.Value.newBuilder() + .setStringValue("foo") + .build()); // assert that a configuration_change event works - structMap.put("flags", com.google.protobuf.Value.newBuilder() - .setStructValue(Struct.newBuilder().putAllFields(flagsMap)).build()); + structMap.put( + "flags", + com.google.protobuf.Value.newBuilder() + .setStructValue(Struct.newBuilder().putAllFields(flagsMap)) + .build()); // should not cache results - FlagEvaluationDetails booleanDetails = api.getClient().getBooleanDetails(FLAG_KEY_BOOLEAN, - false); - FlagEvaluationDetails stringDetails = api.getClient().getStringDetails(FLAG_KEY_STRING, - "wrong"); + FlagEvaluationDetails booleanDetails = api.getClient().getBooleanDetails(FLAG_KEY_BOOLEAN, false); + FlagEvaluationDetails stringDetails = api.getClient().getStringDetails(FLAG_KEY_STRING, "wrong"); FlagEvaluationDetails intDetails = api.getClient().getIntegerDetails(FLAG_KEY_INTEGER, 0); FlagEvaluationDetails floatDetails = api.getClient().getDoubleDetails(FLAG_KEY_DOUBLE, 0.1); - FlagEvaluationDetails objectDetails = api.getClient().getObjectDetails(FLAG_KEY_OBJECT, - new Value()); + FlagEvaluationDetails objectDetails = api.getClient().getObjectDetails(FLAG_KEY_OBJECT, new Value()); // assert values are not cached booleanDetails = api.getClient().getBooleanDetails(FLAG_KEY_BOOLEAN, false); @@ -842,8 +842,14 @@ public void initialize() throws Exception { assertEquals(STATIC_REASON, floatDetails.getReason()); objectDetails = api.getClient().getObjectDetails(FLAG_KEY_OBJECT, new Value()); - assertEquals(INNER_STRUCT_VALUE, objectDetails.getValue().asStructure() - .asMap().get(INNER_STRUCT_KEY).asString()); + assertEquals( + INNER_STRUCT_VALUE, + objectDetails + .getValue() + .asStructure() + .asMap() + .get(INNER_STRUCT_KEY) + .asString()); assertEquals(OBJECT_VARIANT, objectDetails.getVariant()); assertEquals(STATIC_REASON, objectDetails.getReason()); } @@ -887,29 +893,33 @@ void contextEnrichment() throws Exception { final Function mockEnricher = mock(Function.class); // mock a resolver - try (MockedConstruction mockResolver = mockConstruction(InProcessResolver.class, - (mock, context) -> { + try (MockedConstruction mockResolver = + mockConstruction(InProcessResolver.class, (mock, context) -> { Consumer onConnectionEvent; // get a reference to the onConnectionEvent callback - onConnectionEvent = (Consumer) context - .arguments().get(2); + onConnectionEvent = + (Consumer) context.arguments().get(2); // when our mock resolver initializes, it runs the passed onConnectionEvent // callback doAnswer(invocation -> { - onConnectionEvent.accept( - new ConnectionEvent(true, metadata)); - return null; - }).when(mock).init(); + onConnectionEvent.accept(new ConnectionEvent(true, metadata)); + return null; + }) + .when(mock) + .init(); })) { - final FlagdProvider provider = new FlagdProvider( - FlagdOptions.builder().resolverType(Config.Resolver.IN_PROCESS).contextEnricher(mockEnricher).build()); + final FlagdProvider provider = new FlagdProvider(FlagdOptions.builder() + .resolverType(Config.Resolver.IN_PROCESS) + .contextEnricher(mockEnricher) + .build()); provider.initialize(ctx); // the enricher should run with init events, and be passed the metadata - verify(mockEnricher).apply(argThat(arg -> arg.getValue(key).asString().equals(val))); + verify(mockEnricher) + .apply(argThat(arg -> arg.getValue(key).asString().equals(val))); } } @@ -923,25 +933,27 @@ void updatesSyncMetadataWithCallback() throws Exception { metadata.add(key, val); // mock a resolver - try (MockedConstruction mockResolver = mockConstruction(InProcessResolver.class, - (mock, context) -> { + try (MockedConstruction mockResolver = + mockConstruction(InProcessResolver.class, (mock, context) -> { Consumer onConnectionEvent; // get a reference to the onConnectionEvent callback - onConnectionEvent = (Consumer) context - .arguments().get(2); + onConnectionEvent = + (Consumer) context.arguments().get(2); // when our mock resolver initializes, it runs the passed onConnectionEvent // callback doAnswer(invocation -> { - onConnectionEvent.accept( - new ConnectionEvent(true, metadata)); - return null; - }).when(mock).init(); + onConnectionEvent.accept(new ConnectionEvent(true, metadata)); + return null; + }) + .when(mock) + .init(); })) { - FlagdProvider provider = new FlagdProvider( - FlagdOptions.builder().resolverType(Config.Resolver.IN_PROCESS).build()); + FlagdProvider provider = new FlagdProvider(FlagdOptions.builder() + .resolverType(Config.Resolver.IN_PROCESS) + .build()); provider.initialize(ctx); // the onConnectionEvent should have updated the sync metadata and the @@ -949,9 +961,16 @@ void updatesSyncMetadataWithCallback() throws Exception { assertEquals(val, provider.getEnrichedContext().getValue(key).asString()); // call the hook manually and make sure the enriched context is returned - Optional contextFromHook = provider.getProviderHooks().get(0) - .before(HookContext.builder().flagKey("some-flag").defaultValue(false) - .type(FlagValueType.BOOLEAN).ctx(new ImmutableContext()).build(), Collections.emptyMap()); + Optional contextFromHook = provider.getProviderHooks() + .get(0) + .before( + HookContext.builder() + .flagKey("some-flag") + .defaultValue(false) + .type(FlagValueType.BOOLEAN) + .ctx(new ImmutableContext()) + .build(), + Collections.emptyMap()); assertEquals(val, contextFromHook.get().getValue(key).asString()); } } @@ -973,9 +992,7 @@ private FlagdProvider createProvider(GrpcConnector grpc, Supplier getCo // create provider with given grpc provider, cache and state supplier private FlagdProvider createProvider(GrpcConnector grpc, Cache cache, Supplier getConnected) { final FlagdOptions flagdOptions = FlagdOptions.builder().build(); - final GrpcResolver grpcResolver = new GrpcResolver(flagdOptions, cache, getConnected, - (connectionEvent) -> { - }); + final GrpcResolver grpcResolver = new GrpcResolver(flagdOptions, cache, getConnected, (connectionEvent) -> {}); final FlagdProvider provider = new FlagdProvider(); @@ -1002,9 +1019,9 @@ private FlagdProvider createInProcessProvider() { .deadline(1000) .build(); final FlagdProvider provider = new FlagdProvider(flagdOptions); - final MockStorage mockStorage = new MockStorage(new HashMap(), - new LinkedBlockingQueue( - Arrays.asList(new StorageStateChange(StorageState.OK)))); + final MockStorage mockStorage = new MockStorage( + new HashMap(), + new LinkedBlockingQueue(Arrays.asList(new StorageStateChange(StorageState.OK)))); try { final Field flagResolver = FlagdProvider.class.getDeclaredField("flagResolver"); @@ -1020,5 +1037,4 @@ private FlagdProvider createInProcessProvider() { return provider; } - } diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/SyncMetadataHookTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/SyncMetadataHookTest.java index 731164b2b..8294c017f 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/SyncMetadataHookTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/SyncMetadataHookTest.java @@ -2,20 +2,16 @@ import static org.junit.Assert.assertEquals; -import java.util.Optional; -import java.util.function.Supplier; - -import org.junit.Test; - import dev.openfeature.sdk.EvaluationContext; import dev.openfeature.sdk.FlagValueType; import dev.openfeature.sdk.HookContext; import dev.openfeature.sdk.ImmutableContext; import dev.openfeature.sdk.MutableContext; +import java.util.Optional; +import java.util.function.Supplier; +import org.junit.Test; -/** - * SyncMetadataHookTest - */ +/** SyncMetadataHookTest */ public class SyncMetadataHookTest { @Test @@ -29,8 +25,14 @@ public void shouldCallContextSupplierAndReturnContext() { // when(contextSupplier.get()).thenReturn(new ImmutableContext("some-key")); SyncMetadataHook hook = new SyncMetadataHook(contextSupplier); - Optional context = hook.before(HookContext.builder().flagKey("some-flag").defaultValue(false) - .type(FlagValueType.BOOLEAN).ctx(new ImmutableContext()).build(), null); + Optional context = hook.before( + HookContext.builder() + .flagKey("some-flag") + .defaultValue(false) + .type(FlagValueType.BOOLEAN) + .ctx(new ImmutableContext()) + .build(), + null); assertEquals(val1, context.get().getValue(key1).asString()); } -} \ No newline at end of file +} diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/ContainerConfig.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/ContainerConfig.java index 705802ebf..e5231b1bc 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/ContainerConfig.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/ContainerConfig.java @@ -1,5 +1,8 @@ package dev.openfeature.contrib.providers.flagd.e2e; +import java.io.File; +import java.nio.file.Files; +import java.util.List; import org.apache.logging.log4j.util.Strings; import org.jetbrains.annotations.NotNull; import org.testcontainers.containers.GenericContainer; @@ -7,10 +10,6 @@ import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MountableFile; -import java.io.File; -import java.nio.file.Files; -import java.util.List; - public class ContainerConfig { private static final String version; private static final Network network = Network.newNetwork(); @@ -26,23 +25,26 @@ public class ContainerConfig { } } - /** - * @return a {@link org.testcontainers.containers.GenericContainer} instance of a stable sync flagd server with the port 9090 exposed + * @return a {@link org.testcontainers.containers.GenericContainer} instance of a stable sync + * flagd server with the port 9090 exposed */ public static GenericContainer sync() { return sync(false, false); } /** - * @param unstable if an unstable version of the container, which terminates the connection regularly should be used. - * @param addNetwork if set to true a custom network is attached for cross container access e.g. envoy --> sync:8015 - * @return a {@link org.testcontainers.containers.GenericContainer} instance of a sync flagd server with the port 8015 exposed + * @param unstable if an unstable version of the container, which terminates the connection + * regularly should be used. + * @param addNetwork if set to true a custom network is attached for cross container access e.g. + * envoy --> sync:8015 + * @return a {@link org.testcontainers.containers.GenericContainer} instance of a sync flagd + * server with the port 8015 exposed */ public static GenericContainer sync(boolean unstable, boolean addNetwork) { String container = generateContainerName("flagd", unstable ? "unstable" : ""); - GenericContainer genericContainer = new GenericContainer(DockerImageName.parse(container)) - .withExposedPorts(8015); + GenericContainer genericContainer = + new GenericContainer(DockerImageName.parse(container)).withExposedPorts(8015); if (addNetwork) { genericContainer.withNetwork(network); @@ -53,32 +55,33 @@ public static GenericContainer sync(boolean unstable, boolean addNetwork) { } /** - * @return a {@link org.testcontainers.containers.GenericContainer} instance of a stable flagd server with the port 8013 exposed + * @return a {@link org.testcontainers.containers.GenericContainer} instance of a stable flagd + * server with the port 8013 exposed */ public static GenericContainer flagd() { return flagd(false); } /** - * @param unstable if an unstable version of the container, which terminates the connection regularly should be used. - * @return a {@link org.testcontainers.containers.GenericContainer} instance of a flagd server with the port 8013 exposed + * @param unstable if an unstable version of the container, which terminates the connection + * regularly should be used. + * @return a {@link org.testcontainers.containers.GenericContainer} instance of a flagd server + * with the port 8013 exposed */ public static GenericContainer flagd(boolean unstable) { String container = generateContainerName("flagd", unstable ? "unstable" : ""); - return new GenericContainer(DockerImageName.parse(container)) - .withExposedPorts(8013); + return new GenericContainer(DockerImageName.parse(container)).withExposedPorts(8013); } - /** - * @return a {@link org.testcontainers.containers.GenericContainer} instance of envoy container using - * flagd sync service as backend expose on port 9211 + * @return a {@link org.testcontainers.containers.GenericContainer} instance of envoy container + * using flagd sync service as backend expose on port 9211 */ public static GenericContainer envoy() { final String container = "envoyproxy/envoy:v1.31.0"; return new GenericContainer(DockerImageName.parse(container)) - .withCopyFileToContainer(MountableFile.forClasspathResource("/envoy-config/envoy-custom.yaml"), - "/etc/envoy/envoy.yaml") + .withCopyFileToContainer( + MountableFile.forClasspathResource("/envoy-config/envoy-custom.yaml"), "/etc/envoy/envoy.yaml") .withExposedPorts(9211) .withNetwork(network) .withNetworkAliases("envoy"); diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunConfigCucumberTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunConfigCucumberTest.java index a09c17e54..754ac7e32 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunConfigCucumberTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunConfigCucumberTest.java @@ -1,16 +1,17 @@ package dev.openfeature.contrib.providers.flagd.e2e; +import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME; +import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME; + import org.junit.jupiter.api.Order; import org.junit.platform.suite.api.ConfigurationParameter; import org.junit.platform.suite.api.IncludeEngines; import org.junit.platform.suite.api.SelectClasspathResource; import org.junit.platform.suite.api.Suite; -import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME; -import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME; - /** - * Class for running the tests associated with "stable" e2e tests (no fake disconnection) for the in-process provider + * Class for running the tests associated with "stable" e2e tests (no fake disconnection) for the + * in-process provider */ @Order(value = Integer.MAX_VALUE) @Suite @@ -18,7 +19,4 @@ @SelectClasspathResource("features/config.feature") @ConfigurationParameter(key = PLUGIN_PROPERTY_NAME, value = "pretty") @ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "dev.openfeature.contrib.providers.flagd.e2e.steps.config") -public class RunConfigCucumberTest { - - -} +public class RunConfigCucumberTest {} diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFlagdInProcessCucumberTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFlagdInProcessCucumberTest.java index 0930a3710..82b2b238b 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFlagdInProcessCucumberTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFlagdInProcessCucumberTest.java @@ -1,5 +1,8 @@ package dev.openfeature.contrib.providers.flagd.e2e; +import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME; +import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME; + import org.junit.jupiter.api.Order; import org.junit.platform.suite.api.ConfigurationParameter; import org.junit.platform.suite.api.IncludeEngines; @@ -7,11 +10,9 @@ import org.junit.platform.suite.api.Suite; import org.testcontainers.junit.jupiter.Testcontainers; -import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME; -import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME; - /** - * Class for running the tests associated with "stable" e2e tests (no fake disconnection) for the in-process provider + * Class for running the tests associated with "stable" e2e tests (no fake disconnection) for the + * in-process provider */ @Order(value = Integer.MAX_VALUE) @Suite @@ -20,8 +21,9 @@ @SelectClasspathResource("features/flagd-json-evaluator.feature") @SelectClasspathResource("features/flagd.feature") @ConfigurationParameter(key = PLUGIN_PROPERTY_NAME, value = "pretty") -@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "dev.openfeature.contrib.providers.flagd.e2e.process.core,dev.openfeature.contrib.providers.flagd.e2e.steps") +@ConfigurationParameter( + key = GLUE_PROPERTY_NAME, + value = + "dev.openfeature.contrib.providers.flagd.e2e.process.core,dev.openfeature.contrib.providers.flagd.e2e.steps") @Testcontainers -public class RunFlagdInProcessCucumberTest { - -} +public class RunFlagdInProcessCucumberTest {} diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFlagdInProcessEnvoyCucumberTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFlagdInProcessEnvoyCucumberTest.java index 2771f3546..bf7c5c243 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFlagdInProcessEnvoyCucumberTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFlagdInProcessEnvoyCucumberTest.java @@ -1,5 +1,8 @@ package dev.openfeature.contrib.providers.flagd.e2e; +import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME; +import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME; + import org.junit.jupiter.api.Order; import org.junit.platform.suite.api.ConfigurationParameter; import org.junit.platform.suite.api.IncludeEngines; @@ -7,11 +10,9 @@ import org.junit.platform.suite.api.Suite; import org.testcontainers.junit.jupiter.Testcontainers; -import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME; -import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME; - /** - * Class for running the tests associated with "stable" e2e tests (no fake disconnection) for the in-process provider + * Class for running the tests associated with "stable" e2e tests (no fake disconnection) for the + * in-process provider */ @Order(value = Integer.MAX_VALUE) @Suite @@ -20,8 +21,9 @@ @SelectClasspathResource("features/flagd-json-evaluator.feature") @SelectClasspathResource("features/flagd.feature") @ConfigurationParameter(key = PLUGIN_PROPERTY_NAME, value = "pretty") -@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "dev.openfeature.contrib.providers.flagd.e2e.process.envoy,dev.openfeature.contrib.providers.flagd.e2e.steps") +@ConfigurationParameter( + key = GLUE_PROPERTY_NAME, + value = + "dev.openfeature.contrib.providers.flagd.e2e.process.envoy,dev.openfeature.contrib.providers.flagd.e2e.steps") @Testcontainers -public class RunFlagdInProcessEnvoyCucumberTest { - -} +public class RunFlagdInProcessEnvoyCucumberTest {} diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFlagdInProcessReconnectCucumberTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFlagdInProcessReconnectCucumberTest.java index 97fe2d355..8a6dfbe47 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFlagdInProcessReconnectCucumberTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFlagdInProcessReconnectCucumberTest.java @@ -1,5 +1,8 @@ package dev.openfeature.contrib.providers.flagd.e2e; +import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME; +import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME; + import org.junit.jupiter.api.Order; import org.junit.platform.suite.api.ConfigurationParameter; import org.junit.platform.suite.api.IncludeEngines; @@ -7,19 +10,15 @@ import org.junit.platform.suite.api.Suite; import org.testcontainers.junit.jupiter.Testcontainers; -import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME; -import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME; - -/** - * Class for running the reconnection tests for the in-process provider - */ +/** Class for running the reconnection tests for the in-process provider */ @Order(value = Integer.MAX_VALUE) @Suite @IncludeEngines("cucumber") @SelectClasspathResource("features/flagd-reconnect.feature") @ConfigurationParameter(key = PLUGIN_PROPERTY_NAME, value = "pretty") -@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "dev.openfeature.contrib.providers.flagd.e2e.reconnect.process,dev.openfeature.contrib.providers.flagd.e2e.reconnect.steps") +@ConfigurationParameter( + key = GLUE_PROPERTY_NAME, + value = + "dev.openfeature.contrib.providers.flagd.e2e.reconnect.process,dev.openfeature.contrib.providers.flagd.e2e.reconnect.steps") @Testcontainers -public class RunFlagdInProcessReconnectCucumberTest { - -} +public class RunFlagdInProcessReconnectCucumberTest {} diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFlagdInProcessSSLCucumberTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFlagdInProcessSSLCucumberTest.java index 212eeb598..86b49125e 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFlagdInProcessSSLCucumberTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFlagdInProcessSSLCucumberTest.java @@ -1,27 +1,23 @@ package dev.openfeature.contrib.providers.flagd.e2e; +import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME; +import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME; + import org.apache.logging.log4j.core.config.Order; -import org.junit.jupiter.api.Disabled; import org.junit.platform.suite.api.ConfigurationParameter; import org.junit.platform.suite.api.IncludeEngines; -import org.junit.platform.suite.api.SelectClasspathResource; import org.junit.platform.suite.api.Suite; import org.testcontainers.junit.jupiter.Testcontainers; -import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME; -import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME; - -/** - * Class for running the reconnection tests for the RPC provider - */ +/** Class for running the reconnection tests for the RPC provider */ @Order(value = Integer.MAX_VALUE) @Suite(failIfNoTests = false) @IncludeEngines("cucumber") -//@SelectClasspathResource("features/evaluation.feature") +// @SelectClasspathResource("features/evaluation.feature") @ConfigurationParameter(key = PLUGIN_PROPERTY_NAME, value = "pretty") -@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "dev.openfeature.contrib.providers.flagd.e2e.ssl.process,dev.openfeature.contrib.providers.flagd.e2e.steps") +@ConfigurationParameter( + key = GLUE_PROPERTY_NAME, + value = + "dev.openfeature.contrib.providers.flagd.e2e.ssl.process,dev.openfeature.contrib.providers.flagd.e2e.steps") @Testcontainers -public class RunFlagdInProcessSSLCucumberTest { - -} - +public class RunFlagdInProcessSSLCucumberTest {} diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFlagdRpcCucumberTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFlagdRpcCucumberTest.java index 8f8bad579..602159ab9 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFlagdRpcCucumberTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFlagdRpcCucumberTest.java @@ -1,5 +1,8 @@ package dev.openfeature.contrib.providers.flagd.e2e; +import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME; +import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME; + import org.apache.logging.log4j.core.config.Order; import org.junit.platform.suite.api.ConfigurationParameter; import org.junit.platform.suite.api.IncludeEngines; @@ -7,11 +10,9 @@ import org.junit.platform.suite.api.Suite; import org.testcontainers.junit.jupiter.Testcontainers; -import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME; -import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME; - /** - * Class for running the tests associated with "stable" e2e tests (no fake disconnection) for the RPC provider + * Class for running the tests associated with "stable" e2e tests (no fake disconnection) for the + * RPC provider */ @Order(value = Integer.MAX_VALUE) @Suite @@ -21,9 +22,8 @@ @SelectClasspathResource("features/flagd.feature") @SelectClasspathResource("features/flagd-rpc-caching.feature") @ConfigurationParameter(key = PLUGIN_PROPERTY_NAME, value = "pretty") -@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "dev.openfeature.contrib.providers.flagd.e2e.rpc,dev.openfeature.contrib.providers.flagd.e2e.steps") +@ConfigurationParameter( + key = GLUE_PROPERTY_NAME, + value = "dev.openfeature.contrib.providers.flagd.e2e.rpc,dev.openfeature.contrib.providers.flagd.e2e.steps") @Testcontainers -public class RunFlagdRpcCucumberTest { - -} - +public class RunFlagdRpcCucumberTest {} diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFlagdRpcReconnectCucumberTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFlagdRpcReconnectCucumberTest.java index fa226c1a6..89eebe724 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFlagdRpcReconnectCucumberTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFlagdRpcReconnectCucumberTest.java @@ -1,5 +1,8 @@ package dev.openfeature.contrib.providers.flagd.e2e; +import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME; +import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME; + import org.apache.logging.log4j.core.config.Order; import org.junit.platform.suite.api.ConfigurationParameter; import org.junit.platform.suite.api.IncludeEngines; @@ -7,20 +10,15 @@ import org.junit.platform.suite.api.Suite; import org.testcontainers.junit.jupiter.Testcontainers; -import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME; -import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME; - -/** - * Class for running the reconnection tests for the RPC provider - */ +/** Class for running the reconnection tests for the RPC provider */ @Order(value = Integer.MAX_VALUE) @Suite @IncludeEngines("cucumber") @SelectClasspathResource("features/flagd-reconnect.feature") @ConfigurationParameter(key = PLUGIN_PROPERTY_NAME, value = "pretty") -@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "dev.openfeature.contrib.providers.flagd.e2e.reconnect.rpc,dev.openfeature.contrib.providers.flagd.e2e.reconnect.steps") +@ConfigurationParameter( + key = GLUE_PROPERTY_NAME, + value = + "dev.openfeature.contrib.providers.flagd.e2e.reconnect.rpc,dev.openfeature.contrib.providers.flagd.e2e.reconnect.steps") @Testcontainers -public class RunFlagdRpcReconnectCucumberTest { - -} - +public class RunFlagdRpcReconnectCucumberTest {} diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFlagdRpcSSLCucumberTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFlagdRpcSSLCucumberTest.java index a971f3e05..4a30f6b9f 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFlagdRpcSSLCucumberTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFlagdRpcSSLCucumberTest.java @@ -1,5 +1,8 @@ package dev.openfeature.contrib.providers.flagd.e2e; +import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME; +import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME; + import org.apache.logging.log4j.core.config.Order; import org.junit.platform.suite.api.ConfigurationParameter; import org.junit.platform.suite.api.IncludeEngines; @@ -7,20 +10,14 @@ import org.junit.platform.suite.api.Suite; import org.testcontainers.junit.jupiter.Testcontainers; -import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME; -import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME; - -/** - * Class for running the reconnection tests for the RPC provider - */ +/** Class for running the reconnection tests for the RPC provider */ @Order(value = Integer.MAX_VALUE) @Suite @IncludeEngines("cucumber") @SelectClasspathResource("features/evaluation.feature") @ConfigurationParameter(key = PLUGIN_PROPERTY_NAME, value = "pretty") -@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "dev.openfeature.contrib.providers.flagd.e2e.ssl.rpc,dev.openfeature.contrib.providers.flagd.e2e.steps") +@ConfigurationParameter( + key = GLUE_PROPERTY_NAME, + value = "dev.openfeature.contrib.providers.flagd.e2e.ssl.rpc,dev.openfeature.contrib.providers.flagd.e2e.steps") @Testcontainers -public class RunFlagdRpcSSLCucumberTest { - -} - +public class RunFlagdRpcSSLCucumberTest {} diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/process/core/FlagdInProcessSetup.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/process/core/FlagdInProcessSetup.java index fa9bac43d..e23a95965 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/process/core/FlagdInProcessSetup.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/process/core/FlagdInProcessSetup.java @@ -1,17 +1,16 @@ package dev.openfeature.contrib.providers.flagd.e2e.process.core; -import dev.openfeature.contrib.providers.flagd.e2e.ContainerConfig; -import io.cucumber.java.AfterAll; -import io.cucumber.java.Before; -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.parallel.Isolated; - import dev.openfeature.contrib.providers.flagd.Config; import dev.openfeature.contrib.providers.flagd.FlagdOptions; import dev.openfeature.contrib.providers.flagd.FlagdProvider; +import dev.openfeature.contrib.providers.flagd.e2e.ContainerConfig; import dev.openfeature.contrib.providers.flagd.e2e.steps.StepDefinitions; import dev.openfeature.sdk.FeatureProvider; +import io.cucumber.java.AfterAll; +import io.cucumber.java.Before; import io.cucumber.java.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.parallel.Isolated; import org.testcontainers.containers.GenericContainer; @Isolated() @@ -30,11 +29,11 @@ public static void setup() throws InterruptedException { @Before() public static void setupTest() throws InterruptedException { FlagdInProcessSetup.provider = new FlagdProvider(FlagdOptions.builder() - .resolverType(Config.Resolver.IN_PROCESS) - .deadline(1000) - .streamDeadlineMs(0) // this makes reconnect tests more predictable - .port(flagdContainer.getFirstMappedPort()) - .build()); + .resolverType(Config.Resolver.IN_PROCESS) + .deadline(1000) + .streamDeadlineMs(0) // this makes reconnect tests more predictable + .port(flagdContainer.getFirstMappedPort()) + .build()); StepDefinitions.setProvider(provider); } diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/process/envoy/FlagdInProcessEnvoySetup.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/process/envoy/FlagdInProcessEnvoySetup.java index 347bff725..37381a682 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/process/envoy/FlagdInProcessEnvoySetup.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/process/envoy/FlagdInProcessEnvoySetup.java @@ -25,8 +25,8 @@ public class FlagdInProcessEnvoySetup { public static void setup() throws InterruptedException { flagdContainer.start(); envoyContainer.start(); - final String targetUri = String.format("envoy://localhost:%s/flagd-sync.service", - envoyContainer.getFirstMappedPort()); + final String targetUri = + String.format("envoy://localhost:%s/flagd-sync.service", envoyContainer.getFirstMappedPort()); FlagdInProcessEnvoySetup.provider = new FlagdProvider(FlagdOptions.builder() .resolverType(Config.Resolver.IN_PROCESS) @@ -42,4 +42,4 @@ public static void tearDown() { flagdContainer.stop(); envoyContainer.stop(); } -} \ No newline at end of file +} diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/reconnect/process/FlagdInProcessSetup.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/reconnect/process/FlagdInProcessSetup.java index 2ff58eb34..4ab76dd40 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/reconnect/process/FlagdInProcessSetup.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/reconnect/process/FlagdInProcessSetup.java @@ -1,17 +1,16 @@ package dev.openfeature.contrib.providers.flagd.e2e.reconnect.process; -import dev.openfeature.contrib.providers.flagd.e2e.ContainerConfig; -import io.cucumber.java.AfterAll; -import io.cucumber.java.Before; -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.parallel.Isolated; - import dev.openfeature.contrib.providers.flagd.Config; import dev.openfeature.contrib.providers.flagd.FlagdOptions; import dev.openfeature.contrib.providers.flagd.FlagdProvider; +import dev.openfeature.contrib.providers.flagd.e2e.ContainerConfig; import dev.openfeature.contrib.providers.flagd.e2e.reconnect.steps.StepDefinitions; import dev.openfeature.sdk.FeatureProvider; +import io.cucumber.java.AfterAll; +import io.cucumber.java.Before; import io.cucumber.java.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.parallel.Isolated; import org.testcontainers.containers.GenericContainer; @Isolated() @@ -19,25 +18,27 @@ public class FlagdInProcessSetup { private static final GenericContainer flagdContainer = ContainerConfig.sync(true, false); + @BeforeAll() public static void setup() throws InterruptedException { flagdContainer.start(); } + @Before() public static void setupTest() throws InterruptedException { FeatureProvider workingProvider = new FlagdProvider(FlagdOptions.builder() - .resolverType(Config.Resolver.IN_PROCESS) - .port(flagdContainer.getFirstMappedPort()) - // set a generous deadline, to prevent timeouts in actions - .deadline(3000) - .build()); + .resolverType(Config.Resolver.IN_PROCESS) + .port(flagdContainer.getFirstMappedPort()) + // set a generous deadline, to prevent timeouts in actions + .deadline(3000) + .build()); StepDefinitions.setUnstableProvider(workingProvider); FeatureProvider unavailableProvider = new FlagdProvider(FlagdOptions.builder() - .resolverType(Config.Resolver.IN_PROCESS) - .deadline(100) - .port(9092) // this port isn't serving anything, error expected - .build()); + .resolverType(Config.Resolver.IN_PROCESS) + .deadline(100) + .port(9092) // this port isn't serving anything, error expected + .build()); StepDefinitions.setUnavailableProvider(unavailableProvider); } diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/reconnect/steps/StepDefinitions.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/reconnect/steps/StepDefinitions.java index 1ba647e71..d438e7d0f 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/reconnect/steps/StepDefinitions.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/reconnect/steps/StepDefinitions.java @@ -3,13 +3,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; -import java.time.Duration; -import java.util.function.Consumer; - -import org.awaitility.Awaitility; -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.parallel.Isolated; - import dev.openfeature.sdk.Client; import dev.openfeature.sdk.EventDetails; import dev.openfeature.sdk.FeatureProvider; @@ -18,10 +11,15 @@ import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; import io.cucumber.java.en.When; +import java.time.Duration; +import java.util.function.Consumer; +import org.awaitility.Awaitility; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.parallel.Isolated; /** - * Test suite for testing flagd provider reconnect functionality. - * The associated container run a flagd instance which restarts every 5s. + * Test suite for testing flagd provider reconnect functionality. The associated container run a + * flagd instance which restarts every 5s. */ @Isolated() @Order(value = Integer.MAX_VALUE) @@ -42,10 +40,9 @@ public class StepDefinitions { }; /** - * Injects the client to use for this test. - * Tests run one at a time, but just in case, a lock is used to make sure the - * client is not updated mid-test. - * + * Injects the client to use for this test. Tests run one at a time, but just in case, a lock is + * used to make sure the client is not updated mid-test. + * * @param provider client to inject into test. */ public static void setUnstableProvider(FeatureProvider provider) { @@ -84,29 +81,25 @@ public void the_provider_ready_handler_must_run_when_the_provider_connects() { // no errors expected yet assertEquals(0, errorHandlerRunCount); // wait up to 240 seconds for a connect (PROVIDER_READY event) - Awaitility.await().atMost(Duration.ofSeconds(240)) - .until(() -> { - return this.readyHandlerRunCount == 1; - }); - + Awaitility.await().atMost(Duration.ofSeconds(240)).until(() -> { + return this.readyHandlerRunCount == 1; + }); } @Then("the PROVIDER_ERROR handler must run when the provider's connection is lost") public void the_provider_error_handler_must_run_when_the_provider_s_connection_is_lost() { // wait up to 240 seconds for a disconnect (PROVIDER_ERROR event) - Awaitility.await().atMost(Duration.ofSeconds(240)) - .until(() -> { - return this.errorHandlerRunCount > 0; - }); + Awaitility.await().atMost(Duration.ofSeconds(240)).until(() -> { + return this.errorHandlerRunCount > 0; + }); } @Then("when the connection is reestablished the PROVIDER_READY handler must run again") public void when_the_connection_is_reestablished_the_provider_ready_handler_must_run_again() { // wait up to 240 seconds for a reconnect (PROVIDER_READY event) - Awaitility.await().atMost(Duration.ofSeconds(240)) - .until(() -> { - return this.readyHandlerRunCount > 1; - }); + Awaitility.await().atMost(Duration.ofSeconds(240)).until(() -> { + return this.readyHandlerRunCount > 1; + }); } @Given("flagd is unavailable") diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/rpc/FlagdRpcSetup.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/rpc/FlagdRpcSetup.java index bf9f4fc80..3d61dc034 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/rpc/FlagdRpcSetup.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/rpc/FlagdRpcSetup.java @@ -39,5 +39,4 @@ public static void test_setup() { public static void tearDown() { flagdContainer.stop(); } - } diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/ssl/process/FlagdInProcessSetup.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/ssl/process/FlagdInProcessSetup.java index ecd4e4b72..e75c92394 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/ssl/process/FlagdInProcessSetup.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/ssl/process/FlagdInProcessSetup.java @@ -5,27 +5,22 @@ import dev.openfeature.contrib.providers.flagd.FlagdProvider; import dev.openfeature.contrib.providers.flagd.e2e.ContainerConfig; import dev.openfeature.contrib.providers.flagd.e2e.steps.StepDefinitions; -import dev.openfeature.contrib.providers.flagd.resolver.grpc.cache.CacheType; import dev.openfeature.sdk.FeatureProvider; import io.cucumber.java.AfterAll; import io.cucumber.java.Before; import io.cucumber.java.BeforeAll; +import java.io.File; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.parallel.Isolated; import org.testcontainers.containers.GenericContainer; import org.testcontainers.utility.DockerImageName; -import java.io.File; - @Isolated() @Order(value = Integer.MAX_VALUE) public class FlagdInProcessSetup { - private static final GenericContainer flagdContainer = - new GenericContainer( - DockerImageName.parse( - ContainerConfig.generateContainerName("flagd", "ssl") - ) - ).withExposedPorts(8015); + private static final GenericContainer flagdContainer = new GenericContainer( + DockerImageName.parse(ContainerConfig.generateContainerName("flagd", "ssl"))) + .withExposedPorts(8015); @BeforeAll() public static void setups() throws InterruptedException { @@ -47,7 +42,6 @@ public static void setupTest() throws InterruptedException { .certPath(absolutePath) .build()); StepDefinitions.setProvider(workingProvider); - } @AfterAll diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/ssl/rpc/FlagdRpcSetup.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/ssl/rpc/FlagdRpcSetup.java index 1cdc518ef..6b318ae9e 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/ssl/rpc/FlagdRpcSetup.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/ssl/rpc/FlagdRpcSetup.java @@ -5,27 +5,22 @@ import dev.openfeature.contrib.providers.flagd.FlagdProvider; import dev.openfeature.contrib.providers.flagd.e2e.ContainerConfig; import dev.openfeature.contrib.providers.flagd.e2e.steps.StepDefinitions; -import dev.openfeature.contrib.providers.flagd.resolver.grpc.cache.CacheType; import dev.openfeature.sdk.FeatureProvider; import io.cucumber.java.AfterAll; import io.cucumber.java.Before; import io.cucumber.java.BeforeAll; +import java.io.File; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.parallel.Isolated; import org.testcontainers.containers.GenericContainer; import org.testcontainers.utility.DockerImageName; -import java.io.File; - @Isolated() @Order(value = Integer.MAX_VALUE) public class FlagdRpcSetup { - private static final GenericContainer flagdContainer = - new GenericContainer( - DockerImageName.parse( - ContainerConfig.generateContainerName("flagd", "ssl") - ) - ).withExposedPorts(8013); + private static final GenericContainer flagdContainer = new GenericContainer( + DockerImageName.parse(ContainerConfig.generateContainerName("flagd", "ssl"))) + .withExposedPorts(8013); @BeforeAll() public static void setups() throws InterruptedException { @@ -47,7 +42,6 @@ public static void setupTest() throws InterruptedException { .certPath(absolutePath) .build()); StepDefinitions.setProvider(workingProvider); - } @AfterAll diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/StepDefinitions.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/StepDefinitions.java index 73d4de87d..c3f894a08 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/StepDefinitions.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/StepDefinitions.java @@ -2,15 +2,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import java.time.Duration; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Consumer; - -import org.awaitility.Awaitility; -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.parallel.Isolated; - import dev.openfeature.sdk.Client; import dev.openfeature.sdk.EvaluationContext; import dev.openfeature.sdk.EventDetails; @@ -27,10 +18,15 @@ import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; import io.cucumber.java.en.When; +import java.time.Duration; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; +import org.awaitility.Awaitility; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.parallel.Isolated; -/** - * Common test suite used by both RPC and in-process flagd providers. - */ +/** Common test suite used by both RPC and in-process flagd providers. */ @Isolated() @Order(value = Integer.MAX_VALUE) public class StepDefinitions { @@ -72,9 +68,8 @@ public class StepDefinitions { private Consumer readyHandler; /** - * Injects the client to use for this test. - * Tests run one at a time, but just in case, a lock is used to make sure the - * client is not updated mid-test. + * Injects the client to use for this test. Tests run one at a time, but just in case, a lock is + * used to make sure the client is not updated mid-test. * * @param client client to inject into test. */ @@ -106,8 +101,8 @@ public static void cleanUp() throws InterruptedException { // boolean value @When("a boolean flag with key {string} is evaluated with default value {string}") - public void a_boolean_flag_with_key_boolean_flag_is_evaluated_with_default_value_false(String flagKey, - String defaultValue) { + public void a_boolean_flag_with_key_boolean_flag_is_evaluated_with_default_value_false( + String flagKey, String defaultValue) { this.booleanFlagKey = flagKey; this.booleanFlagDefaultValue = Boolean.valueOf(defaultValue); } @@ -165,13 +160,20 @@ public void an_object_flag_with_key_is_evaluated_with_a_null_default_value(Strin this.objectFlagDefaultValue = new Value(); // empty value is equivalent to null } - @Then("the resolved object value should be contain fields {string}, {string}, and {string}, with values {string}, {string} and {int}, respectively") - public void the_resolved_object_value_should_be_contain_fields_and_with_values_and_respectively(String boolField, - String stringField, String numberField, String boolValue, String stringValue, int numberValue) { + @Then( + "the resolved object value should be contain fields {string}, {string}, and {string}, with values {string}, {string} and {int}, respectively") + public void the_resolved_object_value_should_be_contain_fields_and_with_values_and_respectively( + String boolField, + String stringField, + String numberField, + String boolValue, + String stringValue, + int numberValue) { Value value = client.getObjectValue(this.objectFlagKey, this.objectFlagDefaultValue); Structure structure = value.asStructure(); - assertEquals(Boolean.valueOf(boolValue), structure.asMap().get(boolField).asBoolean()); + assertEquals( + Boolean.valueOf(boolValue), structure.asMap().get(boolField).asBoolean()); assertEquals(stringValue, structure.asMap().get(stringField).asString()); assertEquals(numberValue, structure.asMap().get(numberField).asInteger()); } @@ -182,18 +184,18 @@ public void the_resolved_object_value_should_be_contain_fields_and_with_values_a // boolean details @When("a boolean flag with key {string} is evaluated with details and default value {string}") - public void a_boolean_flag_with_key_is_evaluated_with_details_and_default_value(String flagKey, - String defaultValue) { + public void a_boolean_flag_with_key_is_evaluated_with_details_and_default_value( + String flagKey, String defaultValue) { this.booleanFlagKey = flagKey; this.booleanFlagDefaultValue = Boolean.valueOf(defaultValue); } - @Then("the resolved boolean details value should be {string}, the variant should be {string}, and the reason should be {string}") + @Then( + "the resolved boolean details value should be {string}, the variant should be {string}, and the reason should be {string}") public void the_resolved_boolean_value_should_be_the_variant_should_be_and_the_reason_should_be( - String expectedValue, - String expectedVariant, String expectedReason) { - FlagEvaluationDetails details = client.getBooleanDetails(this.booleanFlagKey, - Boolean.valueOf(this.booleanFlagDefaultValue)); + String expectedValue, String expectedVariant, String expectedReason) { + FlagEvaluationDetails details = + client.getBooleanDetails(this.booleanFlagKey, Boolean.valueOf(this.booleanFlagDefaultValue)); assertEquals(Boolean.valueOf(expectedValue), details.getValue()); assertEquals(expectedVariant, details.getVariant()); @@ -202,8 +204,8 @@ public void the_resolved_boolean_value_should_be_the_variant_should_be_and_the_r // string details @When("a string flag with key {string} is evaluated with details and default value {string}") - public void a_string_flag_with_key_is_evaluated_with_details_and_default_value(String flagKey, - String defaultValue) { + public void a_string_flag_with_key_is_evaluated_with_details_and_default_value( + String flagKey, String defaultValue) { this.stringFlagKey = flagKey; this.stringFlagDefaultValue = defaultValue; } @@ -214,11 +216,12 @@ public void a_string_flag_with_key_is_evaluated_with_details(String flagKey) { this.stringFlagDefaultValue = ""; } - @Then("the resolved string details value should be {string}, the variant should be {string}, and the reason should be {string}") - public void the_resolved_string_value_should_be_the_variant_should_be_and_the_reason_should_be(String expectedValue, - String expectedVariant, String expectedReason) { - FlagEvaluationDetails details = client.getStringDetails(this.stringFlagKey, - this.stringFlagDefaultValue); + @Then( + "the resolved string details value should be {string}, the variant should be {string}, and the reason should be {string}") + public void the_resolved_string_value_should_be_the_variant_should_be_and_the_reason_should_be( + String expectedValue, String expectedVariant, String expectedReason) { + FlagEvaluationDetails details = + client.getStringDetails(this.stringFlagKey, this.stringFlagDefaultValue); assertEquals(expectedValue, details.getValue()); assertEquals(expectedVariant, details.getVariant()); @@ -232,9 +235,10 @@ public void an_integer_flag_with_key_is_evaluated_with_details_and_default_value this.intFlagDefaultValue = defaultValue; } - @Then("the resolved integer details value should be {int}, the variant should be {string}, and the reason should be {string}") - public void the_resolved_integer_value_should_be_the_variant_should_be_and_the_reason_should_be(int expectedValue, - String expectedVariant, String expectedReason) { + @Then( + "the resolved integer details value should be {int}, the variant should be {string}, and the reason should be {string}") + public void the_resolved_integer_value_should_be_the_variant_should_be_and_the_reason_should_be( + int expectedValue, String expectedVariant, String expectedReason) { FlagEvaluationDetails details = client.getIntegerDetails(this.intFlagKey, this.intFlagDefaultValue); assertEquals(expectedValue, details.getValue()); @@ -249,11 +253,12 @@ public void a_float_flag_with_key_is_evaluated_with_details_and_default_value(St this.doubleFlagDefaultValue = defaultValue; } - @Then("the resolved float details value should be {double}, the variant should be {string}, and the reason should be {string}") - public void the_resolved_float_value_should_be_the_variant_should_be_and_the_reason_should_be(double expectedValue, - String expectedVariant, String expectedReason) { - FlagEvaluationDetails details = client.getDoubleDetails(this.doubleFlagKey, - this.doubleFlagDefaultValue); + @Then( + "the resolved float details value should be {double}, the variant should be {string}, and the reason should be {string}") + public void the_resolved_float_value_should_be_the_variant_should_be_and_the_reason_should_be( + double expectedValue, String expectedVariant, String expectedReason) { + FlagEvaluationDetails details = + client.getDoubleDetails(this.doubleFlagKey, this.doubleFlagDefaultValue); assertEquals(expectedValue, details.getValue()); assertEquals(expectedVariant, details.getVariant()); @@ -267,14 +272,20 @@ public void an_object_flag_with_key_is_evaluated_with_details_and_a_null_default this.objectFlagDefaultValue = new Value(); } - @Then("the resolved object details value should be contain fields {string}, {string}, and {string}, with values {string}, {string} and {int}, respectively") + @Then( + "the resolved object details value should be contain fields {string}, {string}, and {string}, with values {string}, {string} and {int}, respectively") public void the_resolved_object_value_should_be_contain_fields_and_with_values_and_respectively_again( String boolField, - String stringField, String numberField, String boolValue, String stringValue, int numberValue) { + String stringField, + String numberField, + String boolValue, + String stringValue, + int numberValue) { this.objectFlagDetails = client.getObjectDetails(this.objectFlagKey, this.objectFlagDefaultValue); Structure structure = this.objectFlagDetails.getValue().asStructure(); - assertEquals(Boolean.valueOf(boolValue), structure.asMap().get(boolField).asBoolean()); + assertEquals( + Boolean.valueOf(boolValue), structure.asMap().get(boolField).asBoolean()); assertEquals(stringValue, structure.asMap().get(stringField).asString()); assertEquals(numberValue, structure.asMap().get(numberField).asInteger()); } @@ -289,9 +300,17 @@ public void the_variant_should_be_and_the_reason_should_be(String expectedVarian * Context-aware evaluation */ - @When("context contains keys {string}, {string}, {string}, {string} with values {string}, {string}, {int}, {string}") - public void context_contains_keys_with_values(String field1, String field2, String field3, String field4, - String value1, String value2, Integer value3, String value4) { + @When( + "context contains keys {string}, {string}, {string}, {string} with values {string}, {string}, {int}, {string}") + public void context_contains_keys_with_values( + String field1, + String field2, + String field3, + String field4, + String value1, + String value2, + Integer value3, + String value4) { Map attributes = new HashMap<>(); attributes.put(field1, new Value(value1)); attributes.put(field2, new Value(value2)); @@ -305,7 +324,6 @@ public void an_a_flag_with_key_is_evaluated(String flagKey, String defaultValue) contextAwareFlagKey = flagKey; contextAwareDefaultValue = defaultValue; contextAwareValue = client.getStringValue(flagKey, contextAwareDefaultValue, context); - } @Then("the resolved string response should be {string}") @@ -315,8 +333,8 @@ public void the_resolved_string_response_should_be(String expected) { @Then("the resolved flag value is {string} when the context is empty") public void the_resolved_flag_value_is_when_the_context_is_empty(String expected) { - String emptyContextValue = client.getStringValue(contextAwareFlagKey, contextAwareDefaultValue, - new ImmutableContext()); + String emptyContextValue = + client.getStringValue(contextAwareFlagKey, contextAwareDefaultValue, new ImmutableContext()); assertEquals(expected, emptyContextValue); } @@ -326,8 +344,8 @@ public void the_resolved_flag_value_is_when_the_context_is_empty(String expected // not found @When("a non-existent string flag with key {string} is evaluated with details and a default value {string}") - public void a_non_existent_string_flag_with_key_is_evaluated_with_details_and_a_default_value(String flagKey, - String defaultValue) { + public void a_non_existent_string_flag_with_key_is_evaluated_with_details_and_a_default_value( + String flagKey, String defaultValue) { notFoundFlagKey = flagKey; notFoundDefaultValue = defaultValue; notFoundDetails = client.getStringDetails(notFoundFlagKey, notFoundDefaultValue); @@ -346,14 +364,13 @@ public void the_reason_should_indicate_an_error_and_the_error_code_should_be_fla // type mismatch @When("a string flag with key {string} is evaluated as an integer, with details and a default value {int}") - public void a_string_flag_with_key_is_evaluated_as_an_integer_with_details_and_a_default_value(String flagKey, - int defaultValue) { + public void a_string_flag_with_key_is_evaluated_as_an_integer_with_details_and_a_default_value( + String flagKey, int defaultValue) { typeErrorFlagKey = flagKey; typeErrorDefaultValue = defaultValue; typeErrorDetails = client.getIntegerDetails(typeErrorFlagKey, typeErrorDefaultValue); } - @Then("the default integer value should be returned") public void then_the_default_integer_value_should_be_returned() { assertEquals(typeErrorDefaultValue, typeErrorDetails.getValue()); @@ -370,8 +387,8 @@ public void the_reason_should_indicate_an_error_and_the_error_code_should_be_typ */ @And("a context containing a nested property with outer key {string} and inner key {string}, with value {string}") - public void a_context_containing_a_nested_property_with_outer_key_and_inner_key_with_value(String outerKey, - String innerKey, String value) throws InstantiationException { + public void a_context_containing_a_nested_property_with_outer_key_and_inner_key_with_value( + String outerKey, String innerKey, String value) throws InstantiationException { Map innerMap = new HashMap(); innerMap.put(innerKey, new Value(value)); Map outerMap = new HashMap(); @@ -380,8 +397,8 @@ public void a_context_containing_a_nested_property_with_outer_key_and_inner_key_ } @And("a context containing a nested property with outer key {string} and inner key {string}, with value {int}") - public void a_context_containing_a_nested_property_with_outer_key_and_inner_key_with_value_int(String outerKey, - String innerKey, Integer value) throws InstantiationException { + public void a_context_containing_a_nested_property_with_outer_key_and_inner_key_with_value_int( + String outerKey, String innerKey, Integer value) throws InstantiationException { Map innerMap = new HashMap(); innerMap.put(innerKey, new Value(value)); Map outerMap = new HashMap(); @@ -389,7 +406,6 @@ public void a_context_containing_a_nested_property_with_outer_key_and_inner_key_ this.context = new ImmutableContext(outerMap); } - @And("a context containing a key {string}, with value {string}") public void a_context_containing_a_key_with_value(String key, String value) { Map attrs = new HashMap(); @@ -406,15 +422,13 @@ public void a_context_containing_a_key_with_value_double(String key, Double valu @Then("the returned value should be {string}") public void the_returned_value_should_be(String expected) { - String value = client.getStringValue(this.stringFlagKey, this.stringFlagDefaultValue, - this.context); + String value = client.getStringValue(this.stringFlagKey, this.stringFlagDefaultValue, this.context); assertEquals(expected, value); } @Then("the returned value should be {int}") public void the_returned_value_should_be(Integer expectedValue) { - Integer value = client.getIntegerValue(this.intFlagKey, this.intFlagDefaultValue, - this.context); + Integer value = client.getIntegerValue(this.intFlagKey, this.intFlagDefaultValue, this.context); assertEquals(expectedValue, value); } @@ -434,7 +448,6 @@ public void a_provider_configuration_changed_handler_is_added() { } }; client.onProviderConfigurationChanged(this.changeHandler); - } @When("a flag with key {string} is modified") @@ -444,11 +457,9 @@ public void a_flag_with_key_is_modified(String flagKey) { @Then("the PROVIDER_CONFIGURATION_CHANGED handler must run") public void the_provider_configuration_changed_handler_must_run() { - Awaitility.await() - .atMost(Duration.ofSeconds(2)) - .until(() -> { - return this.isChangeHandlerRun; - }); + Awaitility.await().atMost(Duration.ofSeconds(2)).until(() -> { + return this.isChangeHandlerRun; + }); } @Then("the event details must indicate {string} was altered") @@ -467,11 +478,9 @@ public void a_provider_ready_handler_is_added() { @Then("the PROVIDER_READY handler must run") public void the_provider_ready_handler_must_run() { - Awaitility.await() - .atMost(Duration.ofSeconds(2)) - .until(() -> { - return this.isReadyHandlerRun; - }); + Awaitility.await().atMost(Duration.ofSeconds(2)).until(() -> { + return this.isReadyHandlerRun; + }); } /* @@ -480,8 +489,8 @@ public void the_provider_ready_handler_must_run() { // boolean value @When("a zero-value boolean flag with key {string} is evaluated with default value {string}") - public void a_zero_value_boolean_flag_with_key_is_evaluated_with_default_value(String flagKey, - String defaultValue) { + public void a_zero_value_boolean_flag_with_key_is_evaluated_with_default_value( + String flagKey, String defaultValue) { this.booleanFlagKey = flagKey; this.booleanFlagDefaultValue = Boolean.valueOf(defaultValue); } @@ -501,15 +510,14 @@ public void a_zero_value_float_flag_with_key_is_evaluated_with_default_value(Str @Then("the resolved float zero-value should be {double}") public void the_resolved_float_zero_value_should_be(Double expected) { - FlagEvaluationDetails details = - client.getDoubleDetails("float-zero-flag", this.doubleFlagDefaultValue); + FlagEvaluationDetails details = client.getDoubleDetails("float-zero-flag", this.doubleFlagDefaultValue); assertEquals(expected, details.getValue()); } // integer value @When("a zero-value integer flag with key {string} is evaluated with default value {int}") - public void a_zero_value_integer_flag_with_key_is_evaluated_with_default_value(String flagKey, - Integer defaultValue) { + public void a_zero_value_integer_flag_with_key_is_evaluated_with_default_value( + String flagKey, Integer defaultValue) { this.intFlagKey = flagKey; this.intFlagDefaultValue = defaultValue; } @@ -540,8 +548,8 @@ public void a_context_containing_a_targeting_key_with_value(String targetingKey) @Then("the returned reason should be {string}") public void the_returned_reason_should_be(String reason) { - FlagEvaluationDetails details = client.getStringDetails(this.stringFlagKey, this.stringFlagDefaultValue, - this.context); + FlagEvaluationDetails details = + client.getStringDetails(this.stringFlagKey, this.stringFlagDefaultValue, this.context); assertEquals(reason, details.getReason()); } } diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/config/ConfigSteps.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/config/ConfigSteps.java index 659641ed8..bee803a11 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/config/ConfigSteps.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/config/ConfigSteps.java @@ -1,14 +1,13 @@ package dev.openfeature.contrib.providers.flagd.e2e.steps.config; +import static org.assertj.core.api.Assertions.assertThat; + import dev.openfeature.contrib.providers.flagd.Config; import dev.openfeature.contrib.providers.flagd.FlagdOptions; import dev.openfeature.contrib.providers.flagd.resolver.grpc.cache.CacheType; import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; import io.cucumber.java.en.When; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; @@ -17,12 +16,13 @@ import java.util.List; import java.util.Map; import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThat; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class ConfigSteps { /** - * Not all properties are correctly implemented, hence that we need to ignore them till this is fixed + * Not all properties are correctly implemented, hence that we need to ignore them till this is + * fixed */ public static final List IGNORED_FOR_NOW = new ArrayList() { { @@ -31,6 +31,7 @@ public class ConfigSteps { add("retryBackoffMaxMs"); } }; + private static final Logger LOG = LoggerFactory.getLogger(ConfigSteps.class); FlagdOptions.FlagdOptionsBuilder builder = FlagdOptions.builder(); @@ -56,8 +57,9 @@ public void we_initialize_a_config_for(String string) { } @Given("an option {string} of type {string} with value {string}") - public void we_have_an_option_of_type_with_value(String option, String type, String value) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { - if(IGNORED_FOR_NOW.contains(option)) { + public void we_have_an_option_of_type_with_value(String option, String type, String value) + throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + if (IGNORED_FOR_NOW.contains(option)) { LOG.error("option '{}' is not supported", option); return; } @@ -70,11 +72,11 @@ public void we_have_an_option_of_type_with_value(String option, String type, Str method.invoke(builder, converted); } - Map envVarsSet = new HashMap<>(); @Given("an environment variable {string} with value {string}") - public void we_have_an_environment_variable_with_value(String varName, String value) throws IllegalAccessException, NoSuchFieldException { + public void we_have_an_environment_variable_with_value(String varName, String value) + throws IllegalAccessException, NoSuchFieldException { String getenv = System.getenv(varName); envVarsSet.put(varName, getenv); EnvironmentVariableUtils.set(varName, value); @@ -110,19 +112,17 @@ private Object convert(String value, String type) throws ClassNotFoundException public void the_option_of_type_should_have_the_value(String option, String type, String value) throws Throwable { Object convert = convert(value, type); - if(IGNORED_FOR_NOW.contains(option)) { + if (IGNORED_FOR_NOW.contains(option)) { LOG.error("option '{}' is not supported", option); return; } - option = mapOptionNames(option); assertThat(options).hasFieldOrPropertyWithValue(option, convert); // Resetting env vars - for ( - Map.Entry envVar : envVarsSet.entrySet()) { + for (Map.Entry envVar : envVarsSet.entrySet()) { if (envVar.getValue() == null) { EnvironmentVariableUtils.clear(envVar.getKey()); } else { diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/config/EnvironmentVariableUtils.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/config/EnvironmentVariableUtils.java index 802c8781e..6d1946134 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/config/EnvironmentVariableUtils.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/config/EnvironmentVariableUtils.java @@ -8,12 +8,11 @@ import java.lang.reflect.Field; import java.util.Map; import java.util.function.Consumer; - import org.junit.platform.commons.PreconditionViolationException; /** - * This class modifies the internals of the environment variables map with reflection. - * Warning: If your {@link SecurityManager} does not allow modifications, it fails. + * This class modifies the internals of the environment variables map with reflection. Warning: If + * your {@link SecurityManager} does not allow modifications, it fails. */ class EnvironmentVariableUtils { @@ -24,7 +23,7 @@ private EnvironmentVariableUtils() { /** * Set a value of an environment variable. * - * @param name of the environment variable + * @param name of the environment variable * @param value of the environment variable */ public static void set(String name, String value) { @@ -43,18 +42,16 @@ public static void clear(String name) { private static void modifyEnvironmentVariables(Consumer> consumer) { try { setInProcessEnvironmentClass(consumer); - } - catch (ReflectiveOperationException ex) { + } catch (ReflectiveOperationException ex) { trySystemEnvClass(consumer, ex); } } - private static void trySystemEnvClass(Consumer> consumer, - ReflectiveOperationException processEnvironmentClassEx) { + private static void trySystemEnvClass( + Consumer> consumer, ReflectiveOperationException processEnvironmentClassEx) { try { setInSystemEnvClass(consumer); - } - catch (ReflectiveOperationException ex) { + } catch (ReflectiveOperationException ex) { ex.addSuppressed(processEnvironmentClassEx); throw new PreconditionViolationException("Could not modify environment variables", ex); } @@ -66,13 +63,16 @@ private static void trySystemEnvClass(Consumer> consumer, private static void setInProcessEnvironmentClass(Consumer> consumer) throws ReflectiveOperationException { Class processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment"); - // The order of operations is critical here: On some operating systems, theEnvironment is present but + // The order of operations is critical here: On some operating systems, theEnvironment is + // present but // theCaseInsensitiveEnvironment is not present. In such cases, this method must throw a - // ReflectiveOperationException without modifying theEnvironment. Otherwise, the contents of theEnvironment will - // be corrupted. For this reason, both fields are fetched by reflection before either field is modified. + // ReflectiveOperationException without modifying theEnvironment. Otherwise, the contents of + // theEnvironment will + // be corrupted. For this reason, both fields are fetched by reflection before either field is + // modified. Map theEnvironment = getFieldValue(processEnvironmentClass, null, "theEnvironment"); - Map theCaseInsensitiveEnvironment = getFieldValue(processEnvironmentClass, null, - "theCaseInsensitiveEnvironment"); + Map theCaseInsensitiveEnvironment = + getFieldValue(processEnvironmentClass, null, "theCaseInsensitiveEnvironment"); consumer.accept(theEnvironment); consumer.accept(theCaseInsensitiveEnvironment); } @@ -82,7 +82,7 @@ private static void setInProcessEnvironmentClass(Consumer> c */ private static void setInSystemEnvClass(Consumer> consumer) throws ReflectiveOperationException { - Map env = System.getenv(); //NOSONAR access required to implement the extension + Map env = System.getenv(); // NOSONAR access required to implement the extension consumer.accept(getFieldValue(env.getClass(), env, "m")); } @@ -91,9 +91,8 @@ private static Map getFieldValue(Class clazz, Object object, throws ReflectiveOperationException { Field field = clazz.getDeclaredField(name); try { - field.setAccessible(true); //NOSONAR illegal access required to implement the extension - } - catch (Exception ex) { + field.setAccessible(true); // NOSONAR illegal access required to implement the extension + } catch (Exception ex) { throw new PreconditionViolationException( "Cannot access and modify JDK internals to modify environment variables. " + "Have a look at the documentation for possible solutions: " @@ -102,5 +101,4 @@ private static Map getFieldValue(Class clazz, Object object, } return (Map) field.get(object); } - } diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/BackoffServiceTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/BackoffServiceTest.java index 0bc0ab968..76fc20caa 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/BackoffServiceTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/BackoffServiceTest.java @@ -1,12 +1,11 @@ package dev.openfeature.contrib.providers.flagd.resolver.common.backoff; -import org.junit.jupiter.api.Test; - -import java.time.Instant; - import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; +import java.time.Instant; +import org.junit.jupiter.api.Test; + class BackoffServiceTest { @Test void getCurrentBackoffMillisReturnsBackoffMillisFromStrategy() { @@ -68,4 +67,4 @@ void shouldRetryReturnsFalseIfStrategyIsExhausted() { assertFalse(backoffService.shouldRetry()); verify(mockStrategy).isExhausted(); } -} \ No newline at end of file +} diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/CombinedBackoffTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/CombinedBackoffTest.java index f6c5a2da1..793239e7c 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/CombinedBackoffTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/CombinedBackoffTest.java @@ -1,17 +1,15 @@ package dev.openfeature.contrib.providers.flagd.resolver.common.backoff; -import org.junit.jupiter.api.Test; - import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; +import org.junit.jupiter.api.Test; + class CombinedBackoffTest { @Test void currentBackoffIsFirstBackoff() { - BackoffStrategy[] backoffStrategies = new BackoffStrategy[]{ - new ConstantTimeBackoff(1), - new ConstantTimeBackoff(2) - }; + BackoffStrategy[] backoffStrategies = + new BackoffStrategy[] {new ConstantTimeBackoff(1), new ConstantTimeBackoff(2)}; CombinedBackoff target = new CombinedBackoff(backoffStrategies); @@ -23,11 +21,8 @@ void currentBackoffIsFirstNonExhaustedBackoff() { BackoffStrategy exhaustedBackoff = mock(BackoffStrategy.class); when(exhaustedBackoff.isExhausted()).thenReturn(true); - BackoffStrategy[] backoffStrategies = new BackoffStrategy[]{ - exhaustedBackoff, - exhaustedBackoff, - new ConstantTimeBackoff(3) - }; + BackoffStrategy[] backoffStrategies = + new BackoffStrategy[] {exhaustedBackoff, exhaustedBackoff, new ConstantTimeBackoff(3)}; CombinedBackoff target = new CombinedBackoff(backoffStrategies); @@ -42,10 +37,7 @@ void currentBackoffIsLastBackoffIfAllBackoffsAreExhausted() { BackoffStrategy secondBackoff = mock(BackoffStrategy.class); when(secondBackoff.isExhausted()).thenReturn(true); - BackoffStrategy[] backoffStrategies = new BackoffStrategy[]{ - firstBackoff, - secondBackoff - }; + BackoffStrategy[] backoffStrategies = new BackoffStrategy[] {firstBackoff, secondBackoff}; CombinedBackoff target = new CombinedBackoff(backoffStrategies); @@ -54,9 +46,8 @@ void currentBackoffIsLastBackoffIfAllBackoffsAreExhausted() { @Test void currentBackoffSwitchesToNextBackoffWhenExhausted() { - BackoffStrategy[] backoffStrategies = new BackoffStrategy[]{ - new NumberOfRetriesBackoff(2, new ConstantTimeBackoff(1)), - new ConstantTimeBackoff(2) + BackoffStrategy[] backoffStrategies = new BackoffStrategy[] { + new NumberOfRetriesBackoff(2, new ConstantTimeBackoff(1)), new ConstantTimeBackoff(2) }; CombinedBackoff target = new CombinedBackoff(backoffStrategies); @@ -76,9 +67,9 @@ void currentBackoffSwitchesToNextBackoffWhenExhausted() { @Test void isExhaustedIsTrueAfterAllBackoffsAreExhausted() { - BackoffStrategy[] backoffStrategies = new BackoffStrategy[]{ - new NumberOfRetriesBackoff(2, new ConstantTimeBackoff(1)), - new NumberOfRetriesBackoff(2, new ConstantTimeBackoff(2)) + BackoffStrategy[] backoffStrategies = new BackoffStrategy[] { + new NumberOfRetriesBackoff(2, new ConstantTimeBackoff(1)), + new NumberOfRetriesBackoff(2, new ConstantTimeBackoff(2)) }; CombinedBackoff target = new CombinedBackoff(backoffStrategies); @@ -106,10 +97,10 @@ void resetCallsResetOnAllUsedBackoffsAndswitchesBacktoFirstBackoff() { BackoffStrategy secondBackoff = mock(BackoffStrategy.class); BackoffStrategy thirdBackoff = mock(BackoffStrategy.class); - BackoffStrategy[] backoffStrategies = new BackoffStrategy[]{ - new NumberOfRetriesBackoff(1, firstBackoff), - new NumberOfRetriesBackoff(1, secondBackoff), - new NumberOfRetriesBackoff(1, thirdBackoff) + BackoffStrategy[] backoffStrategies = new BackoffStrategy[] { + new NumberOfRetriesBackoff(1, firstBackoff), + new NumberOfRetriesBackoff(1, secondBackoff), + new NumberOfRetriesBackoff(1, thirdBackoff) }; CombinedBackoff target = new CombinedBackoff(backoffStrategies); @@ -126,4 +117,4 @@ void resetCallsResetOnAllUsedBackoffsAndswitchesBacktoFirstBackoff() { verify(secondBackoff).reset(); verify(thirdBackoff, never()).reset(); } -} \ No newline at end of file +} diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/ConstantTimeBackoffTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/ConstantTimeBackoffTest.java index daaa5b467..1d4e2feae 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/ConstantTimeBackoffTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/ConstantTimeBackoffTest.java @@ -1,11 +1,10 @@ package dev.openfeature.contrib.providers.flagd.resolver.common.backoff; - -import org.junit.jupiter.api.Test; - import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import org.junit.jupiter.api.Test; + class ConstantTimeBackoffTest { @Test void isNotExhaustedInitially() { @@ -48,4 +47,4 @@ void resetDoesNotChangeIsExhausted() { constantTimeBackoff.reset(); assertFalse(constantTimeBackoff.isExhausted()); } -} \ No newline at end of file +} diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/ExponentialTimeBackoffTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/ExponentialTimeBackoffTest.java index 056651852..8c07d6115 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/ExponentialTimeBackoffTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/ExponentialTimeBackoffTest.java @@ -1,11 +1,11 @@ package dev.openfeature.contrib.providers.flagd.resolver.common.backoff; +import static org.junit.jupiter.api.Assertions.*; + import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import static org.junit.jupiter.api.Assertions.*; - class ExponentialTimeBackoffTest { @Test void isNotExhaustedInitially() { @@ -42,9 +42,9 @@ void backoffIncreasesExponentially(int iteration) { void getCurrentBackoffMillisDoesNotIncreaseBeyondMaxBackoff() { ExponentialTimeBackoff target = new ExponentialTimeBackoff(1000, 5000); - target.nextBackoff(); // 2000 - target.nextBackoff(); // 4000 - target.nextBackoff(); // 5000 (8000) + target.nextBackoff(); // 2000 + target.nextBackoff(); // 4000 + target.nextBackoff(); // 5000 (8000) assertEquals(5000, target.getCurrentBackoffMillis()); } @@ -76,4 +76,4 @@ void resetDoesNotChangeIsExhausted() { target.reset(); assertFalse(target.isExhausted()); } -} \ No newline at end of file +} diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/NumberOfRetriesBackoffTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/NumberOfRetriesBackoffTest.java index 42d71e174..ba3fdf514 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/NumberOfRetriesBackoffTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/common/backoff/NumberOfRetriesBackoffTest.java @@ -1,10 +1,10 @@ package dev.openfeature.contrib.providers.flagd.resolver.common.backoff; -import org.junit.jupiter.api.Test; - import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; +import org.junit.jupiter.api.Test; + class NumberOfRetriesBackoffTest { @Test void currentBackoffMillisIsReadFromInnerBackoff() { @@ -109,4 +109,4 @@ void resetResetsRetryCount() { target.reset(); assertEquals(0, target.getRetryCount()); } -} \ No newline at end of file +} diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/common/nameresolvers/EnvoyResolverProviderTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/common/nameresolvers/EnvoyResolverProviderTest.java index 271fb281e..aa913fb2e 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/common/nameresolvers/EnvoyResolverProviderTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/common/nameresolvers/EnvoyResolverProviderTest.java @@ -1,28 +1,24 @@ package dev.openfeature.contrib.providers.flagd.resolver.common.nameresolvers; +import java.net.URI; +import java.util.stream.Stream; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.stream.Stream; - class EnvoyResolverProviderTest { private final EnvoyResolverProvider provider = new EnvoyResolverProvider(); @Test void envoyProviderTestScheme() { Assertions.assertTrue(provider.isAvailable()); - Assertions.assertNotNull(provider.newNameResolver(URI.create("envoy://localhost:1234/foo.service"), - null)); - Assertions.assertNull(provider.newNameResolver(URI.create("invalid-scheme://localhost:1234/foo.service"), - null)); + Assertions.assertNotNull(provider.newNameResolver(URI.create("envoy://localhost:1234/foo.service"), null)); + Assertions.assertNull( + provider.newNameResolver(URI.create("invalid-scheme://localhost:1234/foo.service"), null)); } - @ParameterizedTest @MethodSource("getInvalidPaths") void invalidTargetUriTests(String mockUri) { @@ -39,8 +35,6 @@ private static Stream getInvalidPaths() { Arguments.of("envoy://localhost:1234/"), Arguments.of("envoy://localhost:1234"), Arguments.of("envoy://localhost/test.service"), - Arguments.of("envoy:///test.service") - ); + Arguments.of("envoy:///test.service")); } - } diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/common/nameresolvers/EnvoyResolverTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/common/nameresolvers/EnvoyResolverTest.java index c02ddf8a2..d36d00734 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/common/nameresolvers/EnvoyResolverTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/common/nameresolvers/EnvoyResolverTest.java @@ -1,8 +1,8 @@ package dev.openfeature.contrib.providers.flagd.resolver.common.nameresolvers; +import java.net.URI; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import java.net.URI; class EnvoyResolverTest { @Test diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/EventStreamObserverTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/EventStreamObserverTest.java index 2f42d4fd3..061910af4 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/EventStreamObserverTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/EventStreamObserverTest.java @@ -12,23 +12,20 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.google.protobuf.Struct; +import com.google.protobuf.Value; +import dev.openfeature.contrib.providers.flagd.resolver.grpc.cache.Cache; +import dev.openfeature.flagd.grpc.evaluation.Evaluation.EventStreamResponse; +import io.grpc.Status; +import io.grpc.StatusRuntimeException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.function.Supplier; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import com.google.protobuf.Struct; -import com.google.protobuf.Value; - -import dev.openfeature.contrib.providers.flagd.resolver.grpc.cache.Cache; -import dev.openfeature.flagd.grpc.evaluation.Evaluation.EventStreamResponse; -import io.grpc.Status; -import io.grpc.StatusRuntimeException; - class EventStreamObserverTest { @Nested @@ -49,7 +46,8 @@ void setUp() { reconnect = mock(Runnable.class); when(cache.getEnabled()).thenReturn(true); shouldRetrySilently = mock(Supplier.class); - when(shouldRetrySilently.get()).thenReturn(true, false); // 1st time we should retry silently, subsequent calls should not + when(shouldRetrySilently.get()) + .thenReturn(true, false); // 1st time we should retry silently, subsequent calls should not stream = new EventStreamObserver(sync, cache, (state, changed) -> states.add(state), shouldRetrySilently); } @@ -141,4 +139,4 @@ public void cacheBustingForKnownKeys() { verify(cache, times(1)).remove(eq(key2)); } } -} \ No newline at end of file +} diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/GrpcConnectorTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/GrpcConnectorTest.java index 7e552d05d..cfd82e958 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/GrpcConnectorTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/GrpcConnectorTest.java @@ -3,26 +3,11 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.*; -import java.lang.reflect.Field; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Consumer; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.EnabledOnOs; -import org.junit.jupiter.api.condition.OS; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; -import org.mockito.MockedConstruction; -import org.mockito.MockedStatic; -import org.mockito.invocation.InvocationOnMock; - import dev.openfeature.contrib.providers.flagd.FlagdOptions; import dev.openfeature.contrib.providers.flagd.resolver.common.ConnectionEvent; import dev.openfeature.contrib.providers.flagd.resolver.grpc.cache.Cache; @@ -37,12 +22,24 @@ import io.netty.channel.EventLoopGroup; import io.netty.channel.epoll.EpollEventLoopGroup; import io.netty.channel.unix.DomainSocketAddress; +import java.lang.reflect.Field; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.MockedConstruction; +import org.mockito.MockedStatic; +import org.mockito.invocation.InvocationOnMock; import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; class GrpcConnectorTest { @ParameterizedTest - @ValueSource(ints = { 1, 2, 3 }) + @ValueSource(ints = {1, 2, 3}) void validate_retry_calls(int retries) throws Exception { final int backoffMs = 100; @@ -57,9 +54,7 @@ void validate_retry_calls(int retries) throws Exception { final ServiceGrpc.ServiceStub mockStub = createServiceStubMock(); doAnswer(invocation -> null).when(mockStub).eventStream(any(), any()); - final GrpcConnector connector = new GrpcConnector(options, cache, () -> true, - (connectionEvent) -> { - }); + final GrpcConnector connector = new GrpcConnector(options, cache, () -> true, (connectionEvent) -> {}); Field serviceStubField = GrpcConnector.class.getDeclaredField("serviceStub"); serviceStubField.setAccessible(true); @@ -89,26 +84,30 @@ void initialization_succeed_with_connected_status() { final ServiceGrpc.ServiceStub mockStub = createServiceStubMock(); Consumer onConnectionEvent = mock(Consumer.class); doAnswer((InvocationOnMock invocation) -> { - EventStreamObserver eventStreamObserver = (EventStreamObserver) invocation.getArgument(1); - eventStreamObserver - .onNext(EventStreamResponse.newBuilder().setType(Constants.PROVIDER_READY).build()); - return null; - }).when(mockStub).eventStream(any(), any()); + EventStreamObserver eventStreamObserver = (EventStreamObserver) invocation.getArgument(1); + eventStreamObserver.onNext(EventStreamResponse.newBuilder() + .setType(Constants.PROVIDER_READY) + .build()); + return null; + }) + .when(mockStub) + .eventStream(any(), any()); try (MockedStatic mockStaticService = mockStatic(ServiceGrpc.class)) { - mockStaticService.when(() -> ServiceGrpc.newStub(any())) - .thenReturn(mockStub); + mockStaticService.when(() -> ServiceGrpc.newStub(any())).thenReturn(mockStub); // pass true in connected lambda - final GrpcConnector connector = new GrpcConnector(FlagdOptions.builder().build(), cache, () -> { - try { - Thread.sleep(100); - return true; - } catch (Exception e) { - } - return false; - - }, + final GrpcConnector connector = new GrpcConnector( + FlagdOptions.builder().build(), + cache, + () -> { + try { + Thread.sleep(100); + return true; + } catch (Exception e) { + } + return false; + }, onConnectionEvent); assertDoesNotThrow(connector::initialize); @@ -123,27 +122,28 @@ void stream_does_not_fail_on_first_error() { final ServiceStub mockStub = createServiceStubMock(); Consumer onConnectionEvent = mock(Consumer.class); doAnswer((InvocationOnMock invocation) -> { - EventStreamObserver eventStreamObserver = (EventStreamObserver) invocation.getArgument(1); - eventStreamObserver - .onError(new Exception("fake")); - return null; - }).when(mockStub).eventStream(any(), any()); + EventStreamObserver eventStreamObserver = (EventStreamObserver) invocation.getArgument(1); + eventStreamObserver.onError(new Exception("fake")); + return null; + }) + .when(mockStub) + .eventStream(any(), any()); try (MockedStatic mockStaticService = mockStatic(ServiceGrpc.class)) { - mockStaticService.when(() -> ServiceGrpc.newStub(any())) - .thenReturn(mockStub); + mockStaticService.when(() -> ServiceGrpc.newStub(any())).thenReturn(mockStub); // pass true in connected lambda - final GrpcConnector connector = new GrpcConnector(FlagdOptions.builder().build(), cache, + final GrpcConnector connector = new GrpcConnector( + FlagdOptions.builder().build(), + cache, () -> { - try { - Thread.sleep(100); - return true; - } catch (Exception e) { - } - return false; - - }, + try { + Thread.sleep(100); + return true; + } catch (Exception e) { + } + return false; + }, onConnectionEvent); assertDoesNotThrow(connector::initialize); @@ -164,11 +164,12 @@ void stream_fails_on_second_error_in_a_row() throws Exception { final ServiceGrpc.ServiceStub mockStub = createServiceStubMock(); doAnswer((InvocationOnMock invocation) -> { - EventStreamObserver eventStreamObserver = (EventStreamObserver) invocation.getArgument(1); - eventStreamObserver - .onError(new Exception("fake")); - return null; - }).when(mockStub).eventStream(any(), any()); + EventStreamObserver eventStreamObserver = (EventStreamObserver) invocation.getArgument(1); + eventStreamObserver.onError(new Exception("fake")); + return null; + }) + .when(mockStub) + .eventStream(any(), any()); final GrpcConnector connector = new GrpcConnector(options, cache, () -> true, onConnectionEvent); @@ -194,7 +195,6 @@ void stream_fails_on_second_error_in_a_row() throws Exception { // 2nd try verify(mockStub, timeout(300).times(2)).eventStream(any(), any()); verify(onConnectionEvent, timeout(300).times(1)).accept(argThat(arg -> !arg.isConnected())); - } @Test @@ -210,17 +210,19 @@ void stream_does_not_fail_when_message_between_errors() throws Exception { final AtomicBoolean successMessage = new AtomicBoolean(false); final ServiceGrpc.ServiceStub mockStub = createServiceStubMock(); doAnswer((InvocationOnMock invocation) -> { - EventStreamObserver eventStreamObserver = (EventStreamObserver) invocation.getArgument(1); - - if (successMessage.get()) { - eventStreamObserver - .onNext(EventStreamResponse.newBuilder().setType(Constants.PROVIDER_READY).build()); - } else { - eventStreamObserver - .onError(new Exception("fake")); - } - return null; - }).when(mockStub).eventStream(any(), any()); + EventStreamObserver eventStreamObserver = (EventStreamObserver) invocation.getArgument(1); + + if (successMessage.get()) { + eventStreamObserver.onNext(EventStreamResponse.newBuilder() + .setType(Constants.PROVIDER_READY) + .build()); + } else { + eventStreamObserver.onError(new Exception("fake")); + } + return null; + }) + .when(mockStub) + .eventStream(any(), any()); final GrpcConnector connector = new GrpcConnector(options, cache, () -> true, onConnectionEvent); @@ -253,7 +255,6 @@ void stream_does_not_fail_when_message_between_errors() throws Exception { syncObject.notify(); } - // 3nd message with error verify(mockStub, timeout(300).times(2)).eventStream(any(), any()); verify(onConnectionEvent, timeout(300).times(0)).accept(argThat(arg -> !arg.isConnected())); @@ -265,26 +266,28 @@ void stream_does_not_fail_with_deadline_error() throws Exception { final ServiceStub mockStub = createServiceStubMock(); Consumer onConnectionEvent = mock(Consumer.class); doAnswer((InvocationOnMock invocation) -> { - EventStreamObserver eventStreamObserver = (EventStreamObserver) invocation.getArgument(1); - eventStreamObserver - .onError(new StatusRuntimeException(Status.DEADLINE_EXCEEDED)); - return null; - }).when(mockStub).eventStream(any(), any()); + EventStreamObserver eventStreamObserver = (EventStreamObserver) invocation.getArgument(1); + eventStreamObserver.onError(new StatusRuntimeException(Status.DEADLINE_EXCEEDED)); + return null; + }) + .when(mockStub) + .eventStream(any(), any()); try (MockedStatic mockStaticService = mockStatic(ServiceGrpc.class)) { - mockStaticService.when(() -> ServiceGrpc.newStub(any())) - .thenReturn(mockStub); + mockStaticService.when(() -> ServiceGrpc.newStub(any())).thenReturn(mockStub); // pass true in connected lambda - final GrpcConnector connector = new GrpcConnector(FlagdOptions.builder().build(), cache, () -> { - try { - Thread.sleep(100); - return true; - } catch (Exception e) { - } - return false; - - }, + final GrpcConnector connector = new GrpcConnector( + FlagdOptions.builder().build(), + cache, + () -> { + try { + Thread.sleep(100); + return true; + } catch (Exception e) { + } + return false; + }, onConnectionEvent); assertDoesNotThrow(connector::initialize); @@ -304,22 +307,24 @@ void host_and_port_arg_should_build_tcp_socket() { NettyChannelBuilder mockChannelBuilder = getMockChannelBuilderSocket(); try (MockedStatic mockStaticService = mockStatic(ServiceGrpc.class)) { - mockStaticService.when(() -> ServiceGrpc.newBlockingStub(any(Channel.class))) + mockStaticService + .when(() -> ServiceGrpc.newBlockingStub(any(Channel.class))) .thenReturn(mockBlockingStub); - mockStaticService.when(() -> ServiceGrpc.newStub(any())) - .thenReturn(mockStub); + mockStaticService.when(() -> ServiceGrpc.newStub(any())).thenReturn(mockStub); try (MockedStatic mockStaticChannelBuilder = mockStatic(NettyChannelBuilder.class)) { - mockStaticChannelBuilder.when(() -> NettyChannelBuilder - .forTarget(anyString())).thenReturn(mockChannelBuilder); + mockStaticChannelBuilder + .when(() -> NettyChannelBuilder.forTarget(anyString())) + .thenReturn(mockChannelBuilder); - final FlagdOptions flagdOptions = FlagdOptions.builder().host(host).port(port).tls(false).build(); + final FlagdOptions flagdOptions = + FlagdOptions.builder().host(host).port(port).tls(false).build(); new GrpcConnector(flagdOptions, null, null, null); // verify host/port matches - mockStaticChannelBuilder.verify(() -> NettyChannelBuilder - .forTarget(String.format(targetUri)), times(1)); + mockStaticChannelBuilder.verify( + () -> NettyChannelBuilder.forTarget(String.format(targetUri)), times(1)); } } } @@ -336,16 +341,17 @@ void no_args_host_and_port_env_set_should_build_tcp_socket() throws Exception { NettyChannelBuilder mockChannelBuilder = getMockChannelBuilderSocket(); try (MockedStatic mockStaticService = mockStatic(ServiceGrpc.class)) { - mockStaticService.when(() -> ServiceGrpc.newBlockingStub(any(Channel.class))) + mockStaticService + .when(() -> ServiceGrpc.newBlockingStub(any(Channel.class))) .thenReturn(mockBlockingStub); - mockStaticService.when(() -> ServiceGrpc.newStub(any())) - .thenReturn(mockStub); + mockStaticService.when(() -> ServiceGrpc.newStub(any())).thenReturn(mockStub); - try (MockedStatic mockStaticChannelBuilder = mockStatic( - NettyChannelBuilder.class)) { + try (MockedStatic mockStaticChannelBuilder = + mockStatic(NettyChannelBuilder.class)) { - mockStaticChannelBuilder.when(() -> NettyChannelBuilder - .forTarget(anyString())).thenReturn(mockChannelBuilder); + mockStaticChannelBuilder + .when(() -> NettyChannelBuilder.forTarget(anyString())) + .thenReturn(mockChannelBuilder); new GrpcConnector(FlagdOptions.builder().build(), null, null, null); @@ -356,10 +362,7 @@ void no_args_host_and_port_env_set_should_build_tcp_socket() throws Exception { }); } - /** - * OS Specific test - This test is valid only on Linux system as it rely on - * epoll availability - */ + /** OS Specific test - This test is valid only on Linux system as it rely on epoll availability */ @Test @EnabledOnOs(OS.LINUX) void path_arg_should_build_domain_socket_with_correct_path() { @@ -370,70 +373,66 @@ void path_arg_should_build_domain_socket_with_correct_path() { NettyChannelBuilder mockChannelBuilder = getMockChannelBuilderSocket(); try (MockedStatic mockStaticService = mockStatic(ServiceGrpc.class)) { - mockStaticService.when(() -> ServiceGrpc.newBlockingStub(any(Channel.class))) + mockStaticService + .when(() -> ServiceGrpc.newBlockingStub(any(Channel.class))) .thenReturn(mockBlockingStub); - mockStaticService.when(() -> ServiceGrpc.newStub(any())) - .thenReturn(mockStub); + mockStaticService.when(() -> ServiceGrpc.newStub(any())).thenReturn(mockStub); try (MockedStatic mockStaticChannelBuilder = mockStatic(NettyChannelBuilder.class)) { - try (MockedConstruction mockEpollEventLoopGroup = mockConstruction( - EpollEventLoopGroup.class, - (mock, context) -> { - })) { - when(NettyChannelBuilder.forAddress(any(DomainSocketAddress.class))).thenReturn(mockChannelBuilder); + try (MockedConstruction mockEpollEventLoopGroup = + mockConstruction(EpollEventLoopGroup.class, (mock, context) -> {})) { + when(NettyChannelBuilder.forAddress(any(DomainSocketAddress.class))) + .thenReturn(mockChannelBuilder); new GrpcConnector(FlagdOptions.builder().socketPath(path).build(), null, null, null); // verify path matches - mockStaticChannelBuilder.verify(() -> NettyChannelBuilder - .forAddress(argThat((DomainSocketAddress d) -> { + mockStaticChannelBuilder.verify( + () -> NettyChannelBuilder.forAddress(argThat((DomainSocketAddress d) -> { assertEquals(d.path(), path); // path should match return true; - })), times(1)); + })), + times(1)); } } } } - /** - * OS Specific test - This test is valid only on Linux system as it rely on - * epoll availability - */ + /** OS Specific test - This test is valid only on Linux system as it rely on epoll availability */ @Test @EnabledOnOs(OS.LINUX) void no_args_socket_env_should_build_domain_socket_with_correct_path() throws Exception { final String path = "/some/other/path"; new EnvironmentVariables("FLAGD_SOCKET_PATH", path).execute(() -> { - ServiceBlockingStub mockBlockingStub = mock(ServiceBlockingStub.class); ServiceStub mockStub = mock(ServiceStub.class); NettyChannelBuilder mockChannelBuilder = getMockChannelBuilderSocket(); try (MockedStatic mockStaticService = mockStatic(ServiceGrpc.class)) { - mockStaticService.when(() -> ServiceGrpc.newBlockingStub(any(Channel.class))) + mockStaticService + .when(() -> ServiceGrpc.newBlockingStub(any(Channel.class))) .thenReturn(mockBlockingStub); - mockStaticService.when(() -> ServiceGrpc.newStub(any())) - .thenReturn(mockStub); + mockStaticService.when(() -> ServiceGrpc.newStub(any())).thenReturn(mockStub); - try (MockedStatic mockStaticChannelBuilder = mockStatic( - NettyChannelBuilder.class)) { + try (MockedStatic mockStaticChannelBuilder = + mockStatic(NettyChannelBuilder.class)) { - try (MockedConstruction mockEpollEventLoopGroup = mockConstruction( - EpollEventLoopGroup.class, - (mock, context) -> { - })) { - mockStaticChannelBuilder.when(() -> NettyChannelBuilder - .forAddress(any(DomainSocketAddress.class))).thenReturn(mockChannelBuilder); + try (MockedConstruction mockEpollEventLoopGroup = + mockConstruction(EpollEventLoopGroup.class, (mock, context) -> {})) { + mockStaticChannelBuilder + .when(() -> NettyChannelBuilder.forAddress(any(DomainSocketAddress.class))) + .thenReturn(mockChannelBuilder); new GrpcConnector(FlagdOptions.builder().build(), null, null, null); // verify path matches & called times(= 1 as we rely on reusable channel) - mockStaticChannelBuilder.verify(() -> NettyChannelBuilder - .forAddress(argThat((DomainSocketAddress d) -> { + mockStaticChannelBuilder.verify( + () -> NettyChannelBuilder.forAddress(argThat((DomainSocketAddress d) -> { return d.path() == path; - })), times(1)); + })), + times(1)); } } } @@ -442,9 +441,8 @@ void no_args_socket_env_should_build_domain_socket_with_correct_path() throws Ex @Test void initialization_with_stream_deadline() throws NoSuchFieldException, IllegalAccessException { - final FlagdOptions options = FlagdOptions.builder() - .streamDeadlineMs(16983) - .build(); + final FlagdOptions options = + FlagdOptions.builder().streamDeadlineMs(16983).build(); final Cache cache = new Cache("disabled", 0); final ServiceGrpc.ServiceStub mockStub = createServiceStubMock(); @@ -461,9 +459,7 @@ void initialization_with_stream_deadline() throws NoSuchFieldException, IllegalA @Test void initialization_without_stream_deadline() throws NoSuchFieldException, IllegalAccessException { - final FlagdOptions options = FlagdOptions.builder() - .streamDeadlineMs(0) - .build(); + final FlagdOptions options = FlagdOptions.builder().streamDeadlineMs(0).build(); final Cache cache = new Cache("disabled", 0); final ServiceGrpc.ServiceStub mockStub = createServiceStubMock(); diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/cache/CacheTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/cache/CacheTest.java index 26e3ea616..e8fd727ef 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/cache/CacheTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/cache/CacheTest.java @@ -1,8 +1,5 @@ package dev.openfeature.contrib.providers.flagd.resolver.grpc.cache; -import dev.openfeature.sdk.ProviderEvaluation; -import org.junit.jupiter.api.Test; - import static dev.openfeature.contrib.providers.flagd.resolver.grpc.cache.CacheType.DISABLED; import static dev.openfeature.contrib.providers.flagd.resolver.grpc.cache.CacheType.LRU; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -10,6 +7,9 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import dev.openfeature.sdk.ProviderEvaluation; +import org.junit.jupiter.api.Test; + class CacheTest { @Test @@ -25,17 +25,14 @@ void cacheTypeTest() { assertFalse(undefined.getEnabled()); } - @Test void lruOperationValidation() { // given final Cache lru = new Cache(LRU.getValue(), 1); // when - final ProviderEvaluation evaluation = ProviderEvaluation.builder() - .value("value") - .variant("one") - .build(); + final ProviderEvaluation evaluation = + ProviderEvaluation.builder().value("value").variant("one").build(); lru.put("key", evaluation); // then @@ -47,4 +44,4 @@ void lruOperationValidation() { // then assertNull(lru.get("key")); } -} \ No newline at end of file +} diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/strategy/ResolveFactoryTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/strategy/ResolveFactoryTest.java index c62986e23..054418d0e 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/strategy/ResolveFactoryTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/strategy/ResolveFactoryTest.java @@ -1,12 +1,12 @@ package dev.openfeature.contrib.providers.flagd.resolver.grpc.strategy; +import static org.junit.jupiter.api.Assertions.assertEquals; + import dev.openfeature.contrib.providers.flagd.FlagdOptions; import io.opentelemetry.api.OpenTelemetry; import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import static org.junit.jupiter.api.Assertions.assertEquals; - class ResolveFactoryTest { @Test @@ -21,13 +21,13 @@ public void simpleResolverAsDefault() { assertEquals(SimpleResolving.class, strategy.getClass()); } - @Test public void tracedResolverWhenOTelSdkIsSet() { // given final OpenTelemetry telemetry = Mockito.mock(OpenTelemetry.class); - final FlagdOptions options = FlagdOptions.builder().openTelemetry(telemetry).build(); + final FlagdOptions options = + FlagdOptions.builder().openTelemetry(telemetry).build(); // when final ResolveStrategy strategy = ResolveFactory.getStrategy(options); @@ -39,7 +39,8 @@ public void tracedResolverWhenOTelSdkIsSet() { @Test public void tracedResolverWhenGlobalTelemetryIsSet() { // given - final FlagdOptions options = FlagdOptions.builder().withGlobalTelemetry(true).build(); + final FlagdOptions options = + FlagdOptions.builder().withGlobalTelemetry(true).build(); // when final ResolveStrategy strategy = ResolveFactory.getStrategy(options); @@ -47,4 +48,4 @@ public void tracedResolverWhenGlobalTelemetryIsSet() { // then assertEquals(TracedResolving.class, strategy.getClass()); } -} \ No newline at end of file +} diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/strategy/TracedResolvingTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/strategy/TracedResolvingTest.java index 6eab05c6a..89519f6d5 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/strategy/TracedResolvingTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/strategy/TracedResolvingTest.java @@ -7,15 +7,13 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import org.junit.jupiter.api.Test; - import com.google.protobuf.Message; - import dev.openfeature.flagd.grpc.evaluation.Evaluation.ResolveBooleanRequest; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanBuilder; import io.opentelemetry.api.trace.Tracer; +import org.junit.jupiter.api.Test; class TracedResolvingTest { @@ -48,5 +46,4 @@ public void basicTest() { verify(span, times(1)).setAttribute("feature_flag.provider_name", "flagd"); verify(span, times(1)).end(); } - -} \ No newline at end of file +} diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/InProcessResolverTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/InProcessResolverTest.java index 4b9bd824e..08c758c0b 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/InProcessResolverTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/InProcessResolverTest.java @@ -16,19 +16,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively; -import java.lang.reflect.Field; -import java.time.Duration; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - import dev.openfeature.contrib.providers.flagd.Config; import dev.openfeature.contrib.providers.flagd.FlagdOptions; import dev.openfeature.contrib.providers.flagd.resolver.common.ConnectionEvent; @@ -48,408 +35,406 @@ import dev.openfeature.sdk.Value; import dev.openfeature.sdk.exceptions.ParseError; import dev.openfeature.sdk.exceptions.TypeMismatchError; +import java.lang.reflect.Field; +import java.time.Duration; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; class InProcessResolverTest { - @Test - public void connectorSetup() { - // given - FlagdOptions forGrpcOptions = FlagdOptions.builder().resolverType(Config.Resolver.IN_PROCESS) - .host("localhost") - .port(8080).build(); - FlagdOptions forOfflineOptions = FlagdOptions.builder().resolverType(Config.Resolver.IN_PROCESS) - .offlineFlagSourcePath("path").build(); - FlagdOptions forCustomConnectorOptions = FlagdOptions.builder().resolverType(Config.Resolver.IN_PROCESS) - .customConnector(new MockConnector(null)).build(); - - // then - assertInstanceOf(GrpcStreamConnector.class, InProcessResolver.getConnector(forGrpcOptions)); - assertInstanceOf(FileConnector.class, InProcessResolver.getConnector(forOfflineOptions)); - assertInstanceOf(MockConnector.class, InProcessResolver.getConnector(forCustomConnectorOptions)); - } - - @Test - public void eventHandling() throws Throwable { - // given - // note - queues with adequate capacity - final BlockingQueue sender = new LinkedBlockingQueue<>(5); - final BlockingQueue receiver = new LinkedBlockingQueue<>(5); - final String key = "key1"; - final String val = "val1"; - final MutableStructure syncMetadata = new MutableStructure(); - syncMetadata.add(key, val); - - InProcessResolver inProcessResolver = getInProcessResolverWth(new MockStorage(new HashMap<>(), sender), - (connectionEvent) -> receiver.offer(new StorageStateChange( - connectionEvent.isConnected() ? StorageState.OK : StorageState.ERROR, - connectionEvent.getFlagsChanged(), connectionEvent.getSyncMetadata()))); - - // when - init and emit events - Thread initThread = new Thread(() -> { - try { - inProcessResolver.init(); - } catch (Exception e) { - } - }); - initThread.start(); - if (!sender.offer(new StorageStateChange(StorageState.OK, Collections.emptyList(), syncMetadata), 100, - TimeUnit.MILLISECONDS)) { - Assertions.fail("failed to send the event"); - } - if (!sender.offer(new StorageStateChange(StorageState.ERROR), 100, TimeUnit.MILLISECONDS)) { - Assertions.fail("failed to send the event"); - } - - // then - receive events in order - assertTimeoutPreemptively(Duration.ofMillis(200), () -> { - StorageStateChange storageState = receiver.take(); - assertEquals(StorageState.OK, storageState.getStorageState()); - assertEquals(val, storageState.getSyncMetadata().getValue(key).asString()); - }); - - assertTimeoutPreemptively(Duration.ofMillis(200), () -> { - assertEquals(StorageState.ERROR, receiver.take().getStorageState()); - }); - } - - @Test - public void simpleBooleanResolving() throws Exception { - // given - final Map flagMap = new HashMap<>(); - flagMap.put("booleanFlag", BOOLEAN_FLAG); - - InProcessResolver inProcessResolver = getInProcessResolverWth(new MockStorage(flagMap), - (connectionEvent) -> { - }); - - // when - ProviderEvaluation providerEvaluation = inProcessResolver.booleanEvaluation("booleanFlag", - false, - new ImmutableContext()); - - // then - assertEquals(true, providerEvaluation.getValue()); - assertEquals("on", providerEvaluation.getVariant()); - assertEquals(Reason.STATIC.toString(), providerEvaluation.getReason()); - } - - @Test - public void simpleDoubleResolving() throws Exception { - // given - final Map flagMap = new HashMap<>(); - flagMap.put("doubleFlag", DOUBLE_FLAG); - - InProcessResolver inProcessResolver = getInProcessResolverWth(new MockStorage(flagMap), - (connectionEvent) -> { - }); - - // when - ProviderEvaluation providerEvaluation = inProcessResolver.doubleEvaluation("doubleFlag", 0d, - new ImmutableContext()); - - // then - assertEquals(3.141d, providerEvaluation.getValue()); - assertEquals("one", providerEvaluation.getVariant()); - assertEquals(Reason.STATIC.toString(), providerEvaluation.getReason()); - } - - @Test - public void fetchIntegerAsDouble() throws Exception { - // given - final Map flagMap = new HashMap<>(); - flagMap.put("doubleFlag", DOUBLE_FLAG); - - InProcessResolver inProcessResolver = getInProcessResolverWth(new MockStorage(flagMap), - (connectionEvent) -> { - }); - - // when - ProviderEvaluation providerEvaluation = inProcessResolver.integerEvaluation("doubleFlag", 0, - new ImmutableContext()); - - // then - assertEquals(3, providerEvaluation.getValue()); - assertEquals("one", providerEvaluation.getVariant()); - assertEquals(Reason.STATIC.toString(), providerEvaluation.getReason()); - } - - @Test - public void fetchDoubleAsInt() throws Exception { - // given - final Map flagMap = new HashMap<>(); - flagMap.put("integerFlag", INT_FLAG); - - InProcessResolver inProcessResolver = getInProcessResolverWth(new MockStorage(flagMap), - (connectionEvent) -> { - }); - - // when - ProviderEvaluation providerEvaluation = inProcessResolver.doubleEvaluation("integerFlag", 0d, - new ImmutableContext()); - - // then - assertEquals(1d, providerEvaluation.getValue()); - assertEquals("one", providerEvaluation.getVariant()); - assertEquals(Reason.STATIC.toString(), providerEvaluation.getReason()); - } - - @Test - public void simpleIntResolving() throws Exception { - // given - final Map flagMap = new HashMap<>(); - flagMap.put("integerFlag", INT_FLAG); - - InProcessResolver inProcessResolver = getInProcessResolverWth(new MockStorage(flagMap), - (connectionEvent) -> { - }); - - // when - ProviderEvaluation providerEvaluation = inProcessResolver.integerEvaluation("integerFlag", 0, - new ImmutableContext()); - - // then - assertEquals(1, providerEvaluation.getValue()); - assertEquals("one", providerEvaluation.getVariant()); - assertEquals(Reason.STATIC.toString(), providerEvaluation.getReason()); - } - - @Test - public void simpleObjectResolving() throws Exception { - // given - final Map flagMap = new HashMap<>(); - flagMap.put("objectFlag", OBJECT_FLAG); - - InProcessResolver inProcessResolver = getInProcessResolverWth(new MockStorage(flagMap), - (connectionEvent) -> { - }); - - Map typeDefault = new HashMap<>(); - typeDefault.put("key", "0164"); - typeDefault.put("date", "01.01.1990"); - - // when - ProviderEvaluation providerEvaluation = inProcessResolver.objectEvaluation("objectFlag", - Value.objectToValue(typeDefault), new ImmutableContext()); - - // then - Value value = providerEvaluation.getValue(); - Map valueMap = value.asStructure().asMap(); - - assertEquals("0165", valueMap.get("key").asString()); - assertEquals("01.01.2000", valueMap.get("date").asString()); - assertEquals(Reason.STATIC.toString(), providerEvaluation.getReason()); - assertEquals("typeA", providerEvaluation.getVariant()); - } - - @Test - public void missingFlag() throws Exception { - // given - final Map flagMap = new HashMap<>(); - - InProcessResolver inProcessResolver = getInProcessResolverWth(new MockStorage(flagMap), - (connectionEvent) -> { - }); - - // when/then - ProviderEvaluation missingFlag = inProcessResolver.booleanEvaluation("missingFlag", false, - new ImmutableContext()); - assertEquals(ErrorCode.FLAG_NOT_FOUND, missingFlag.getErrorCode()); - } - - @Test - public void disabledFlag() throws Exception { - // given - final Map flagMap = new HashMap<>(); - flagMap.put("disabledFlag", DISABLED_FLAG); - - InProcessResolver inProcessResolver = getInProcessResolverWth(new MockStorage(flagMap), - (connectionEvent) -> { - }); - - // when/then - ProviderEvaluation disabledFlag = inProcessResolver.booleanEvaluation("disabledFlag", false, - new ImmutableContext()); - assertEquals(ErrorCode.FLAG_NOT_FOUND, disabledFlag.getErrorCode()); - } - - @Test - public void variantMismatchFlag() throws Exception { - // given - final Map flagMap = new HashMap<>(); - flagMap.put("mismatchFlag", VARIANT_MISMATCH_FLAG); - - InProcessResolver inProcessResolver = getInProcessResolverWth(new MockStorage(flagMap), - (connectionEvent) -> { - }); - - // when/then - assertThrows(TypeMismatchError.class, () -> { - inProcessResolver.booleanEvaluation("mismatchFlag", false, new ImmutableContext()); - }); - } - - @Test - public void typeMismatchEvaluation() throws Exception { - // given - final Map flagMap = new HashMap<>(); - flagMap.put("stringFlag", BOOLEAN_FLAG); - - InProcessResolver inProcessResolver = getInProcessResolverWth(new MockStorage(flagMap), - (connectionEvent) -> { - }); - - // when/then - assertThrows(TypeMismatchError.class, () -> { - inProcessResolver.stringEvaluation("stringFlag", "false", new ImmutableContext()); - }); - } - - @Test - public void booleanShorthandEvaluation() throws Exception { - // given - final Map flagMap = new HashMap<>(); - flagMap.put("shorthand", FLAG_WIH_SHORTHAND_TARGETING); - - InProcessResolver inProcessResolver = getInProcessResolverWth(new MockStorage(flagMap), - (connectionEvent) -> { - }); - - ProviderEvaluation providerEvaluation = inProcessResolver.booleanEvaluation("shorthand", false, - new ImmutableContext()); - - // then - assertEquals(true, providerEvaluation.getValue()); - assertEquals("true", providerEvaluation.getVariant()); - assertEquals(Reason.TARGETING_MATCH.toString(), providerEvaluation.getReason()); - } - - @Test - public void targetingMatchedEvaluationFlag() throws Exception { - // given - final Map flagMap = new HashMap<>(); - flagMap.put("stringFlag", FLAG_WIH_IF_IN_TARGET); - - InProcessResolver inProcessResolver = getInProcessResolverWth(new MockStorage(flagMap), - (connectionEvent) -> { - }); - - // when - ProviderEvaluation providerEvaluation = inProcessResolver.stringEvaluation("stringFlag", - "loopAlg", - new MutableContext().add("email", "abc@faas.com")); - - // then - assertEquals("binetAlg", providerEvaluation.getValue()); - assertEquals("binet", providerEvaluation.getVariant()); - assertEquals(Reason.TARGETING_MATCH.toString(), providerEvaluation.getReason()); + @Test + public void connectorSetup() { + // given + FlagdOptions forGrpcOptions = FlagdOptions.builder() + .resolverType(Config.Resolver.IN_PROCESS) + .host("localhost") + .port(8080) + .build(); + FlagdOptions forOfflineOptions = FlagdOptions.builder() + .resolverType(Config.Resolver.IN_PROCESS) + .offlineFlagSourcePath("path") + .build(); + FlagdOptions forCustomConnectorOptions = FlagdOptions.builder() + .resolverType(Config.Resolver.IN_PROCESS) + .customConnector(new MockConnector(null)) + .build(); + + // then + assertInstanceOf(GrpcStreamConnector.class, InProcessResolver.getConnector(forGrpcOptions)); + assertInstanceOf(FileConnector.class, InProcessResolver.getConnector(forOfflineOptions)); + assertInstanceOf(MockConnector.class, InProcessResolver.getConnector(forCustomConnectorOptions)); + } + + @Test + public void eventHandling() throws Throwable { + // given + // note - queues with adequate capacity + final BlockingQueue sender = new LinkedBlockingQueue<>(5); + final BlockingQueue receiver = new LinkedBlockingQueue<>(5); + final String key = "key1"; + final String val = "val1"; + final MutableStructure syncMetadata = new MutableStructure(); + syncMetadata.add(key, val); + + InProcessResolver inProcessResolver = getInProcessResolverWth( + new MockStorage(new HashMap<>(), sender), + (connectionEvent) -> receiver.offer(new StorageStateChange( + connectionEvent.isConnected() ? StorageState.OK : StorageState.ERROR, + connectionEvent.getFlagsChanged(), + connectionEvent.getSyncMetadata()))); + + // when - init and emit events + Thread initThread = new Thread(() -> { + try { + inProcessResolver.init(); + } catch (Exception e) { + } + }); + initThread.start(); + if (!sender.offer( + new StorageStateChange(StorageState.OK, Collections.emptyList(), syncMetadata), + 100, + TimeUnit.MILLISECONDS)) { + Assertions.fail("failed to send the event"); } - - @Test - public void targetingUnmatchedEvaluationFlag() throws Exception { - // given - final Map flagMap = new HashMap<>(); - flagMap.put("stringFlag", FLAG_WIH_IF_IN_TARGET); - - InProcessResolver inProcessResolver = getInProcessResolverWth(new MockStorage(flagMap), - (connectionEvent) -> { - }); - - // when - ProviderEvaluation providerEvaluation = inProcessResolver.stringEvaluation("stringFlag", - "loopAlg", - new MutableContext().add("email", "abc@abc.com")); - - // then - assertEquals("loopAlg", providerEvaluation.getValue()); - assertEquals("loop", providerEvaluation.getVariant()); - assertEquals(Reason.DEFAULT.toString(), providerEvaluation.getReason()); - } - - @Test - public void explicitTargetingKeyHandling() throws NoSuchFieldException, IllegalAccessException { - // given - final Map flagMap = new HashMap<>(); - flagMap.put("stringFlag", FLAG_WITH_TARGETING_KEY); - - InProcessResolver inProcessResolver = getInProcessResolverWth(new MockStorage(flagMap), - (connectionEvent) -> { - }); - - // when - ProviderEvaluation providerEvaluation = inProcessResolver.stringEvaluation("stringFlag", "loop", - new MutableContext("xyz")); - - // then - assertEquals("binetAlg", providerEvaluation.getValue()); - assertEquals("binet", providerEvaluation.getVariant()); - assertEquals(Reason.TARGETING_MATCH.toString(), providerEvaluation.getReason()); - } - - @Test - public void targetingErrorEvaluationFlag() throws Exception { - // given - final Map flagMap = new HashMap<>(); - flagMap.put("targetingErrorFlag", FLAG_WIH_INVALID_TARGET); - - InProcessResolver inProcessResolver = getInProcessResolverWth(new MockStorage(flagMap), - (connectionEvent) -> { - }); - - // when/then - assertThrows(ParseError.class, () -> { - inProcessResolver.booleanEvaluation("targetingErrorFlag", false, new ImmutableContext()); - }); - } - - @Test - public void validateMetadataInEvaluationResult() throws Exception { - // given - final String scope = "appName=myApp"; - final Map flagMap = new HashMap<>(); - flagMap.put("booleanFlag", BOOLEAN_FLAG); - - InProcessResolver inProcessResolver = getInProcessResolverWth( - FlagdOptions.builder().selector(scope).build(), - new MockStorage(flagMap)); - - // when - ProviderEvaluation providerEvaluation = inProcessResolver.booleanEvaluation("booleanFlag", - false, - new ImmutableContext()); - - // then - final ImmutableMetadata metadata = providerEvaluation.getFlagMetadata(); - assertNotNull(metadata); - assertEquals(scope, metadata.getString("scope")); - } - - private InProcessResolver getInProcessResolverWth(final FlagdOptions options, final MockStorage storage) - throws NoSuchFieldException, IllegalAccessException { - - final InProcessResolver resolver = new InProcessResolver(options, () -> true, - (connectionEvent) -> { - }); - return injectFlagStore(resolver, storage); - } - - private InProcessResolver getInProcessResolverWth(final MockStorage storage, - final Consumer onConnectionEvent) - throws NoSuchFieldException, IllegalAccessException { - - final InProcessResolver resolver = new InProcessResolver( - FlagdOptions.builder().deadline(1000).build(), () -> true, onConnectionEvent); - return injectFlagStore(resolver, storage); - } - - // helper to inject flagStore override - private InProcessResolver injectFlagStore(final InProcessResolver resolver, final MockStorage storage) - throws NoSuchFieldException, IllegalAccessException { - - final Field flagStore = InProcessResolver.class.getDeclaredField("flagStore"); - flagStore.setAccessible(true); - flagStore.set(resolver, storage); - - return resolver; + if (!sender.offer(new StorageStateChange(StorageState.ERROR), 100, TimeUnit.MILLISECONDS)) { + Assertions.fail("failed to send the event"); } + // then - receive events in order + assertTimeoutPreemptively(Duration.ofMillis(200), () -> { + StorageStateChange storageState = receiver.take(); + assertEquals(StorageState.OK, storageState.getStorageState()); + assertEquals(val, storageState.getSyncMetadata().getValue(key).asString()); + }); + + assertTimeoutPreemptively(Duration.ofMillis(200), () -> { + assertEquals(StorageState.ERROR, receiver.take().getStorageState()); + }); + } + + @Test + public void simpleBooleanResolving() throws Exception { + // given + final Map flagMap = new HashMap<>(); + flagMap.put("booleanFlag", BOOLEAN_FLAG); + + InProcessResolver inProcessResolver = + getInProcessResolverWth(new MockStorage(flagMap), (connectionEvent) -> {}); + + // when + ProviderEvaluation providerEvaluation = + inProcessResolver.booleanEvaluation("booleanFlag", false, new ImmutableContext()); + + // then + assertEquals(true, providerEvaluation.getValue()); + assertEquals("on", providerEvaluation.getVariant()); + assertEquals(Reason.STATIC.toString(), providerEvaluation.getReason()); + } + + @Test + public void simpleDoubleResolving() throws Exception { + // given + final Map flagMap = new HashMap<>(); + flagMap.put("doubleFlag", DOUBLE_FLAG); + + InProcessResolver inProcessResolver = + getInProcessResolverWth(new MockStorage(flagMap), (connectionEvent) -> {}); + + // when + ProviderEvaluation providerEvaluation = + inProcessResolver.doubleEvaluation("doubleFlag", 0d, new ImmutableContext()); + + // then + assertEquals(3.141d, providerEvaluation.getValue()); + assertEquals("one", providerEvaluation.getVariant()); + assertEquals(Reason.STATIC.toString(), providerEvaluation.getReason()); + } + + @Test + public void fetchIntegerAsDouble() throws Exception { + // given + final Map flagMap = new HashMap<>(); + flagMap.put("doubleFlag", DOUBLE_FLAG); + + InProcessResolver inProcessResolver = + getInProcessResolverWth(new MockStorage(flagMap), (connectionEvent) -> {}); + + // when + ProviderEvaluation providerEvaluation = + inProcessResolver.integerEvaluation("doubleFlag", 0, new ImmutableContext()); + + // then + assertEquals(3, providerEvaluation.getValue()); + assertEquals("one", providerEvaluation.getVariant()); + assertEquals(Reason.STATIC.toString(), providerEvaluation.getReason()); + } + + @Test + public void fetchDoubleAsInt() throws Exception { + // given + final Map flagMap = new HashMap<>(); + flagMap.put("integerFlag", INT_FLAG); + + InProcessResolver inProcessResolver = + getInProcessResolverWth(new MockStorage(flagMap), (connectionEvent) -> {}); + + // when + ProviderEvaluation providerEvaluation = + inProcessResolver.doubleEvaluation("integerFlag", 0d, new ImmutableContext()); + + // then + assertEquals(1d, providerEvaluation.getValue()); + assertEquals("one", providerEvaluation.getVariant()); + assertEquals(Reason.STATIC.toString(), providerEvaluation.getReason()); + } + + @Test + public void simpleIntResolving() throws Exception { + // given + final Map flagMap = new HashMap<>(); + flagMap.put("integerFlag", INT_FLAG); + + InProcessResolver inProcessResolver = + getInProcessResolverWth(new MockStorage(flagMap), (connectionEvent) -> {}); + + // when + ProviderEvaluation providerEvaluation = + inProcessResolver.integerEvaluation("integerFlag", 0, new ImmutableContext()); + + // then + assertEquals(1, providerEvaluation.getValue()); + assertEquals("one", providerEvaluation.getVariant()); + assertEquals(Reason.STATIC.toString(), providerEvaluation.getReason()); + } + + @Test + public void simpleObjectResolving() throws Exception { + // given + final Map flagMap = new HashMap<>(); + flagMap.put("objectFlag", OBJECT_FLAG); + + InProcessResolver inProcessResolver = + getInProcessResolverWth(new MockStorage(flagMap), (connectionEvent) -> {}); + + Map typeDefault = new HashMap<>(); + typeDefault.put("key", "0164"); + typeDefault.put("date", "01.01.1990"); + + // when + ProviderEvaluation providerEvaluation = inProcessResolver.objectEvaluation( + "objectFlag", Value.objectToValue(typeDefault), new ImmutableContext()); + + // then + Value value = providerEvaluation.getValue(); + Map valueMap = value.asStructure().asMap(); + + assertEquals("0165", valueMap.get("key").asString()); + assertEquals("01.01.2000", valueMap.get("date").asString()); + assertEquals(Reason.STATIC.toString(), providerEvaluation.getReason()); + assertEquals("typeA", providerEvaluation.getVariant()); + } + + @Test + public void missingFlag() throws Exception { + // given + final Map flagMap = new HashMap<>(); + + InProcessResolver inProcessResolver = + getInProcessResolverWth(new MockStorage(flagMap), (connectionEvent) -> {}); + + // when/then + ProviderEvaluation missingFlag = + inProcessResolver.booleanEvaluation("missingFlag", false, new ImmutableContext()); + assertEquals(ErrorCode.FLAG_NOT_FOUND, missingFlag.getErrorCode()); + } + + @Test + public void disabledFlag() throws Exception { + // given + final Map flagMap = new HashMap<>(); + flagMap.put("disabledFlag", DISABLED_FLAG); + + InProcessResolver inProcessResolver = + getInProcessResolverWth(new MockStorage(flagMap), (connectionEvent) -> {}); + + // when/then + ProviderEvaluation disabledFlag = + inProcessResolver.booleanEvaluation("disabledFlag", false, new ImmutableContext()); + assertEquals(ErrorCode.FLAG_NOT_FOUND, disabledFlag.getErrorCode()); + } + + @Test + public void variantMismatchFlag() throws Exception { + // given + final Map flagMap = new HashMap<>(); + flagMap.put("mismatchFlag", VARIANT_MISMATCH_FLAG); + + InProcessResolver inProcessResolver = + getInProcessResolverWth(new MockStorage(flagMap), (connectionEvent) -> {}); + + // when/then + assertThrows(TypeMismatchError.class, () -> { + inProcessResolver.booleanEvaluation("mismatchFlag", false, new ImmutableContext()); + }); + } + + @Test + public void typeMismatchEvaluation() throws Exception { + // given + final Map flagMap = new HashMap<>(); + flagMap.put("stringFlag", BOOLEAN_FLAG); + + InProcessResolver inProcessResolver = + getInProcessResolverWth(new MockStorage(flagMap), (connectionEvent) -> {}); + + // when/then + assertThrows(TypeMismatchError.class, () -> { + inProcessResolver.stringEvaluation("stringFlag", "false", new ImmutableContext()); + }); + } + + @Test + public void booleanShorthandEvaluation() throws Exception { + // given + final Map flagMap = new HashMap<>(); + flagMap.put("shorthand", FLAG_WIH_SHORTHAND_TARGETING); + + InProcessResolver inProcessResolver = + getInProcessResolverWth(new MockStorage(flagMap), (connectionEvent) -> {}); + + ProviderEvaluation providerEvaluation = + inProcessResolver.booleanEvaluation("shorthand", false, new ImmutableContext()); + + // then + assertEquals(true, providerEvaluation.getValue()); + assertEquals("true", providerEvaluation.getVariant()); + assertEquals(Reason.TARGETING_MATCH.toString(), providerEvaluation.getReason()); + } + + @Test + public void targetingMatchedEvaluationFlag() throws Exception { + // given + final Map flagMap = new HashMap<>(); + flagMap.put("stringFlag", FLAG_WIH_IF_IN_TARGET); + + InProcessResolver inProcessResolver = + getInProcessResolverWth(new MockStorage(flagMap), (connectionEvent) -> {}); + + // when + ProviderEvaluation providerEvaluation = inProcessResolver.stringEvaluation( + "stringFlag", "loopAlg", new MutableContext().add("email", "abc@faas.com")); + + // then + assertEquals("binetAlg", providerEvaluation.getValue()); + assertEquals("binet", providerEvaluation.getVariant()); + assertEquals(Reason.TARGETING_MATCH.toString(), providerEvaluation.getReason()); + } + + @Test + public void targetingUnmatchedEvaluationFlag() throws Exception { + // given + final Map flagMap = new HashMap<>(); + flagMap.put("stringFlag", FLAG_WIH_IF_IN_TARGET); + + InProcessResolver inProcessResolver = + getInProcessResolverWth(new MockStorage(flagMap), (connectionEvent) -> {}); + + // when + ProviderEvaluation providerEvaluation = inProcessResolver.stringEvaluation( + "stringFlag", "loopAlg", new MutableContext().add("email", "abc@abc.com")); + + // then + assertEquals("loopAlg", providerEvaluation.getValue()); + assertEquals("loop", providerEvaluation.getVariant()); + assertEquals(Reason.DEFAULT.toString(), providerEvaluation.getReason()); + } + + @Test + public void explicitTargetingKeyHandling() throws NoSuchFieldException, IllegalAccessException { + // given + final Map flagMap = new HashMap<>(); + flagMap.put("stringFlag", FLAG_WITH_TARGETING_KEY); + + InProcessResolver inProcessResolver = + getInProcessResolverWth(new MockStorage(flagMap), (connectionEvent) -> {}); + + // when + ProviderEvaluation providerEvaluation = + inProcessResolver.stringEvaluation("stringFlag", "loop", new MutableContext("xyz")); + + // then + assertEquals("binetAlg", providerEvaluation.getValue()); + assertEquals("binet", providerEvaluation.getVariant()); + assertEquals(Reason.TARGETING_MATCH.toString(), providerEvaluation.getReason()); + } + + @Test + public void targetingErrorEvaluationFlag() throws Exception { + // given + final Map flagMap = new HashMap<>(); + flagMap.put("targetingErrorFlag", FLAG_WIH_INVALID_TARGET); + + InProcessResolver inProcessResolver = + getInProcessResolverWth(new MockStorage(flagMap), (connectionEvent) -> {}); + + // when/then + assertThrows(ParseError.class, () -> { + inProcessResolver.booleanEvaluation("targetingErrorFlag", false, new ImmutableContext()); + }); + } + + @Test + public void validateMetadataInEvaluationResult() throws Exception { + // given + final String scope = "appName=myApp"; + final Map flagMap = new HashMap<>(); + flagMap.put("booleanFlag", BOOLEAN_FLAG); + + InProcessResolver inProcessResolver = + getInProcessResolverWth(FlagdOptions.builder().selector(scope).build(), new MockStorage(flagMap)); + + // when + ProviderEvaluation providerEvaluation = + inProcessResolver.booleanEvaluation("booleanFlag", false, new ImmutableContext()); + + // then + final ImmutableMetadata metadata = providerEvaluation.getFlagMetadata(); + assertNotNull(metadata); + assertEquals(scope, metadata.getString("scope")); + } + + private InProcessResolver getInProcessResolverWth(final FlagdOptions options, final MockStorage storage) + throws NoSuchFieldException, IllegalAccessException { + + final InProcessResolver resolver = new InProcessResolver(options, () -> true, (connectionEvent) -> {}); + return injectFlagStore(resolver, storage); + } + + private InProcessResolver getInProcessResolverWth( + final MockStorage storage, final Consumer onConnectionEvent) + throws NoSuchFieldException, IllegalAccessException { + + final InProcessResolver resolver = + new InProcessResolver(FlagdOptions.builder().deadline(1000).build(), () -> true, onConnectionEvent); + return injectFlagStore(resolver, storage); + } + + // helper to inject flagStore override + private InProcessResolver injectFlagStore(final InProcessResolver resolver, final MockStorage storage) + throws NoSuchFieldException, IllegalAccessException { + + final Field flagStore = InProcessResolver.class.getDeclaredField("flagStore"); + flagStore.setAccessible(true); + flagStore.set(resolver, storage); + + return resolver; + } } diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/MockFlags.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/MockFlags.java index 52255ea25..fef135cc9 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/MockFlags.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/MockFlags.java @@ -1,7 +1,6 @@ package dev.openfeature.contrib.providers.flagd.resolver.process; import dev.openfeature.contrib.providers.flagd.resolver.process.model.FeatureFlag; - import java.util.HashMap; import java.util.Map; @@ -70,17 +69,23 @@ public class MockFlags { static final FeatureFlag VARIANT_MISMATCH_FLAG = new FeatureFlag("ENABLED", "true", stringVariants, null); // flag with targeting rule - string - static final FeatureFlag FLAG_WIH_IF_IN_TARGET = new FeatureFlag("ENABLED", "loop", stringVariants, + static final FeatureFlag FLAG_WIH_IF_IN_TARGET = new FeatureFlag( + "ENABLED", + "loop", + stringVariants, "{\"if\":[{\"in\":[\"@faas.com\",{\"var\":[\"email\"]}]},\"binet\",null]}"); - static final FeatureFlag FLAG_WITH_TARGETING_KEY = new FeatureFlag("ENABLED", "loop", stringVariants, + static final FeatureFlag FLAG_WITH_TARGETING_KEY = new FeatureFlag( + "ENABLED", + "loop", + stringVariants, "{\"if\":[{\"==\":[{\"var\":\"targetingKey\"},\"xyz\"]},\"binet\",null]}"); // flag with incorrect targeting rule - static final FeatureFlag FLAG_WIH_INVALID_TARGET = new FeatureFlag("ENABLED", "loop", stringVariants, - "{if this, then that}"); + static final FeatureFlag FLAG_WIH_INVALID_TARGET = + new FeatureFlag("ENABLED", "loop", stringVariants, "{if this, then that}"); // flag with shorthand rule - static final FeatureFlag FLAG_WIH_SHORTHAND_TARGETING = new FeatureFlag("ENABLED", "false", shorthandVariant, - "{ \"if\": [true, true, false] }"); + static final FeatureFlag FLAG_WIH_SHORTHAND_TARGETING = + new FeatureFlag("ENABLED", "false", shorthandVariant, "{ \"if\": [true, true, false] }"); } diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/MockStorage.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/MockStorage.java index 3e183d04c..a48a05d12 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/MockStorage.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/MockStorage.java @@ -3,10 +3,9 @@ import dev.openfeature.contrib.providers.flagd.resolver.process.model.FeatureFlag; import dev.openfeature.contrib.providers.flagd.resolver.process.storage.Storage; import dev.openfeature.contrib.providers.flagd.resolver.process.storage.StorageStateChange; - -import javax.annotation.Nullable; import java.util.Map; import java.util.concurrent.BlockingQueue; +import javax.annotation.Nullable; public class MockStorage implements Storage { @@ -35,9 +34,7 @@ public FeatureFlag getFlag(String key) { return mockFlags.get(key); } - - @Nullable - public BlockingQueue getStateQueue() { + @Nullable public BlockingQueue getStateQueue() { return mockQueue; } } diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/TestUtils.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/TestUtils.java index e489e6d0d..c0b6795c8 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/TestUtils.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/TestUtils.java @@ -1,7 +1,6 @@ package dev.openfeature.contrib.providers.flagd.resolver.process; import dev.openfeature.contrib.providers.flagd.resolver.process.model.FlagParser; - import java.io.IOException; import java.net.URISyntaxException; import java.net.URL; diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/model/FlagParserTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/model/FlagParserTest.java index 27d42dd7b..30453a80e 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/model/FlagParserTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/model/FlagParserTest.java @@ -1,10 +1,5 @@ package dev.openfeature.contrib.providers.flagd.resolver.process.model; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.util.Map; - import static dev.openfeature.contrib.providers.flagd.resolver.process.TestUtils.INVALID_CFG; import static dev.openfeature.contrib.providers.flagd.resolver.process.TestUtils.INVALID_FLAG; import static dev.openfeature.contrib.providers.flagd.resolver.process.TestUtils.VALID_LONG; @@ -15,6 +10,10 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import java.io.IOException; +import java.util.Map; +import org.junit.jupiter.api.Test; + class FlagParserTest { @Test public void validJsonConfigurationParsing() throws IOException { @@ -62,11 +61,10 @@ public void validJsonConfigurationWithTargetingRulesParsing() throws IOException assertEquals("loop", variants.get("loop")); assertEquals("binet", variants.get("binet")); - assertEquals("{\"if\":[{\"in\":[\"@faas.com\",{\"var\":[\"email\"]}]},\"binet\",null]}", - stringFlag.getTargeting()); + assertEquals( + "{\"if\":[{\"in\":[\"@faas.com\",{\"var\":[\"email\"]}]},\"binet\",null]}", stringFlag.getTargeting()); } - @Test public void invalidFlagThrowsError() { assertThrows(IllegalArgumentException.class, () -> { diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/FlagStoreTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/FlagStoreTest.java index 571ffdb5e..d0ac07a75 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/FlagStoreTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/FlagStoreTest.java @@ -7,22 +7,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively; +import dev.openfeature.contrib.providers.flagd.resolver.process.model.FeatureFlag; +import dev.openfeature.contrib.providers.flagd.resolver.process.model.FlagParser; +import dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.QueuePayload; +import dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.QueuePayloadType; +import dev.openfeature.flagd.grpc.sync.Sync.GetMetadataResponse; import java.time.Duration; import java.util.HashSet; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.stream.Collectors; - import org.junit.Assert; import org.junit.jupiter.api.Test; -import dev.openfeature.contrib.providers.flagd.resolver.process.model.FeatureFlag; -import dev.openfeature.contrib.providers.flagd.resolver.process.model.FlagParser; -import dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.QueuePayload; -import dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.QueuePayloadType; -import dev.openfeature.flagd.grpc.sync.Sync.GetMetadataResponse; - class FlagStoreTest { @Test @@ -36,46 +34,53 @@ public void connectorHandling() throws Exception { final BlockingQueue states = store.getStateQueue(); // OK for simple flag - assertTimeoutPreemptively(Duration.ofMillis(maxDelay), ()-> { - payload.offer(new QueuePayload(QueuePayloadType.DATA, getFlagsFromResource(VALID_SIMPLE), GetMetadataResponse.getDefaultInstance())); + assertTimeoutPreemptively(Duration.ofMillis(maxDelay), () -> { + payload.offer(new QueuePayload( + QueuePayloadType.DATA, + getFlagsFromResource(VALID_SIMPLE), + GetMetadataResponse.getDefaultInstance())); }); - assertTimeoutPreemptively(Duration.ofMillis(maxDelay), ()-> { - assertEquals(StorageState.OK, states.take().getStorageState()); + assertTimeoutPreemptively(Duration.ofMillis(maxDelay), () -> { + assertEquals(StorageState.OK, states.take().getStorageState()); }); // STALE for invalid flag - assertTimeoutPreemptively(Duration.ofMillis(maxDelay), ()-> { - payload.offer(new QueuePayload(QueuePayloadType.DATA, getFlagsFromResource(INVALID_FLAG), GetMetadataResponse.getDefaultInstance())); + assertTimeoutPreemptively(Duration.ofMillis(maxDelay), () -> { + payload.offer(new QueuePayload( + QueuePayloadType.DATA, + getFlagsFromResource(INVALID_FLAG), + GetMetadataResponse.getDefaultInstance())); }); - assertTimeoutPreemptively(Duration.ofMillis(maxDelay), ()-> { - assertEquals(StorageState.STALE, states.take().getStorageState()); + assertTimeoutPreemptively(Duration.ofMillis(maxDelay), () -> { + assertEquals(StorageState.STALE, states.take().getStorageState()); }); // OK again for next payload - assertTimeoutPreemptively(Duration.ofMillis(maxDelay), ()-> { - payload.offer(new QueuePayload(QueuePayloadType.DATA, getFlagsFromResource(VALID_LONG), GetMetadataResponse.getDefaultInstance())); + assertTimeoutPreemptively(Duration.ofMillis(maxDelay), () -> { + payload.offer(new QueuePayload( + QueuePayloadType.DATA, getFlagsFromResource(VALID_LONG), GetMetadataResponse.getDefaultInstance())); }); - assertTimeoutPreemptively(Duration.ofMillis(maxDelay), ()-> { - assertEquals(StorageState.OK, states.take().getStorageState()); + assertTimeoutPreemptively(Duration.ofMillis(maxDelay), () -> { + assertEquals(StorageState.OK, states.take().getStorageState()); }); // ERROR is propagated correctly - assertTimeoutPreemptively(Duration.ofMillis(maxDelay), ()-> { + assertTimeoutPreemptively(Duration.ofMillis(maxDelay), () -> { payload.offer(new QueuePayload(QueuePayloadType.ERROR, null, GetMetadataResponse.getDefaultInstance())); }); - assertTimeoutPreemptively(Duration.ofMillis(maxDelay), ()-> { - assertEquals(StorageState.ERROR, states.take().getStorageState()); + assertTimeoutPreemptively(Duration.ofMillis(maxDelay), () -> { + assertEquals(StorageState.ERROR, states.take().getStorageState()); }); // Shutdown handling store.shutdown(); - assertTimeoutPreemptively(Duration.ofMillis(maxDelay), ()-> { - assertEquals(StorageState.ERROR, states.take().getStorageState()); + assertTimeoutPreemptively(Duration.ofMillis(maxDelay), () -> { + assertEquals(StorageState.ERROR, states.take().getStorageState()); }); } @@ -87,23 +92,27 @@ public void changedFlags() throws Exception { store.init(); final BlockingQueue storageStateDTOS = store.getStateQueue(); - assertTimeoutPreemptively(Duration.ofMillis(maxDelay), ()-> { - payload.offer(new QueuePayload(QueuePayloadType.DATA, getFlagsFromResource(VALID_SIMPLE), GetMetadataResponse.getDefaultInstance())); + assertTimeoutPreemptively(Duration.ofMillis(maxDelay), () -> { + payload.offer(new QueuePayload( + QueuePayloadType.DATA, + getFlagsFromResource(VALID_SIMPLE), + GetMetadataResponse.getDefaultInstance())); }); // flags changed for first time - assertEquals(FlagParser.parseString( - getFlagsFromResource(VALID_SIMPLE), true).keySet().stream().collect(Collectors.toList()), + assertEquals( + FlagParser.parseString(getFlagsFromResource(VALID_SIMPLE), true).keySet().stream() + .collect(Collectors.toList()), storageStateDTOS.take().getChangedFlagsKeys()); - assertTimeoutPreemptively(Duration.ofMillis(maxDelay), ()-> { - payload.offer(new QueuePayload(QueuePayloadType.DATA, getFlagsFromResource(VALID_LONG), GetMetadataResponse.getDefaultInstance())); + assertTimeoutPreemptively(Duration.ofMillis(maxDelay), () -> { + payload.offer(new QueuePayload( + QueuePayloadType.DATA, getFlagsFromResource(VALID_LONG), GetMetadataResponse.getDefaultInstance())); }); - Map expectedChangedFlags = - FlagParser.parseString(getFlagsFromResource(VALID_LONG),true); + Map expectedChangedFlags = FlagParser.parseString(getFlagsFromResource(VALID_LONG), true); expectedChangedFlags.remove("myBoolFlag"); // flags changed from initial VALID_SIMPLE flag, as a set because we don't care about order - Assert.assertEquals(expectedChangedFlags.keySet().stream().collect(Collectors.toSet()), - new HashSet<>(storageStateDTOS.take().getChangedFlagsKeys())); + Assert.assertEquals( + expectedChangedFlags.keySet().stream().collect(Collectors.toSet()), + new HashSet<>(storageStateDTOS.take().getChangedFlagsKeys())); } - } diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/MockConnector.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/MockConnector.java index e94d7ed3f..ff7e01b83 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/MockConnector.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/MockConnector.java @@ -1,11 +1,10 @@ package dev.openfeature.contrib.providers.flagd.resolver.process.storage; -import java.util.concurrent.BlockingQueue; - import dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.Connector; import dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.QueuePayload; import dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.QueuePayloadType; import dev.openfeature.flagd.grpc.sync.Sync.GetMetadataResponse; +import java.util.concurrent.BlockingQueue; import lombok.extern.slf4j.Slf4j; @Slf4j @@ -27,7 +26,8 @@ public BlockingQueue getStream() { public void shutdown() { // Emit error mocking closed connection scenario - if (!mockQueue.offer(new QueuePayload(QueuePayloadType.ERROR, "shutdown invoked", GetMetadataResponse.getDefaultInstance()))) { + if (!mockQueue.offer(new QueuePayload( + QueuePayloadType.ERROR, "shutdown invoked", GetMetadataResponse.getDefaultInstance()))) { log.warn("Failed to offer shutdown status"); } } diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/file/FileConnectorTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/file/FileConnectorTest.java index a2ad795c4..50759c319 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/file/FileConnectorTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/file/FileConnectorTest.java @@ -1,10 +1,14 @@ package dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.file; +import static dev.openfeature.contrib.providers.flagd.resolver.process.TestUtils.UPDATABLE_FILE; +import static dev.openfeature.contrib.providers.flagd.resolver.process.TestUtils.VALID_LONG; +import static dev.openfeature.contrib.providers.flagd.resolver.process.TestUtils.getResourcePath; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively; + import dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.QueuePayload; import dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.QueuePayloadType; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -12,13 +16,8 @@ import java.nio.file.StandardOpenOption; import java.time.Duration; import java.util.concurrent.BlockingQueue; - -import static dev.openfeature.contrib.providers.flagd.resolver.process.TestUtils.UPDATABLE_FILE; -import static dev.openfeature.contrib.providers.flagd.resolver.process.TestUtils.VALID_LONG; -import static dev.openfeature.contrib.providers.flagd.resolver.process.TestUtils.getResourcePath; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; class FileConnectorTest { @@ -67,8 +66,10 @@ void emitErrorStateForInvalidPath() throws IOException { @Test @Disabled("Disabled as unstable on GH Action. Useful for functionality validation") void watchForFileUpdatesAndEmitThem() throws IOException { - final String initial = "{\"flags\":{\"myBoolFlag\":{\"state\":\"ENABLED\",\"variants\":{\"on\":true,\"off\":false},\"defaultVariant\":\"on\"}}}"; - final String updatedFlags = "{\"flags\":{\"myBoolFlag\":{\"state\":\"ENABLED\",\"variants\":{\"on\":true,\"off\":false},\"defaultVariant\":\"off\"}}}"; + final String initial = + "{\"flags\":{\"myBoolFlag\":{\"state\":\"ENABLED\",\"variants\":{\"on\":true,\"off\":false},\"defaultVariant\":\"on\"}}}"; + final String updatedFlags = + "{\"flags\":{\"myBoolFlag\":{\"state\":\"ENABLED\",\"variants\":{\"on\":true,\"off\":false},\"defaultVariant\":\"off\"}}}"; // given final Path updPath = Paths.get(getResourcePath(UPDATABLE_FILE)); @@ -100,5 +101,4 @@ void watchForFileUpdatesAndEmitThem() throws IOException { assertEquals(updatedFlags, payload[0].getFlagData()); } - } diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/grpc/GrpcStreamConnectorTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/grpc/GrpcStreamConnectorTest.java index c737184a4..f17e09304 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/grpc/GrpcStreamConnectorTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/grpc/GrpcStreamConnectorTest.java @@ -9,15 +9,7 @@ import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.*; -import java.lang.reflect.Field; -import java.time.Duration; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.TimeUnit; - -import org.junit.jupiter.api.Test; - import com.google.protobuf.Struct; - import dev.openfeature.contrib.providers.flagd.FlagdOptions; import dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.QueuePayload; import dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.QueuePayloadType; @@ -26,6 +18,11 @@ import dev.openfeature.flagd.grpc.sync.Sync.GetMetadataResponse; import dev.openfeature.flagd.grpc.sync.Sync.SyncFlagsRequest; import dev.openfeature.flagd.grpc.sync.Sync.SyncFlagsResponse; +import java.lang.reflect.Field; +import java.time.Duration; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.Test; class GrpcStreamConnectorTest { @@ -46,9 +43,11 @@ public void connectionParameters() throws Throwable { final SyncFlagsRequest[] request = new SyncFlagsRequest[1]; doAnswer(invocation -> { - request[0] = invocation.getArgument(0, SyncFlagsRequest.class); - return null; - }).when(stubMock).syncFlags(any(), any()); + request[0] = invocation.getArgument(0, SyncFlagsRequest.class); + return null; + }) + .when(stubMock) + .syncFlags(any(), any()); // when connector.init(); @@ -62,14 +61,11 @@ public void connectionParameters() throws Throwable { assertEquals("selector", flagsRequest.getSelector()); } - @Test public void disableStreamDeadline() throws Throwable { // given - final FlagdOptions options = FlagdOptions.builder() - .selector("selector") - .streamDeadlineMs(0) - .build(); + final FlagdOptions options = + FlagdOptions.builder().selector("selector").streamDeadlineMs(0).build(); final GrpcStreamConnector connector = new GrpcStreamConnector(options); final FlagSyncServiceStub stubMock = mockStubAndReturn(connector); @@ -77,9 +73,11 @@ public void disableStreamDeadline() throws Throwable { final SyncFlagsRequest[] request = new SyncFlagsRequest[1]; doAnswer(invocation -> { - request[0] = invocation.getArgument(0, SyncFlagsRequest.class); - return null; - }).when(stubMock).syncFlags(any(), any()); + request[0] = invocation.getArgument(0, SyncFlagsRequest.class); + return null; + }) + .when(stubMock) + .syncFlags(any(), any()); // when connector.init(); @@ -97,24 +95,31 @@ public void grpcConnectionStatus() throws Throwable { // given final String key = "key1"; final String val = "value1"; - final GrpcStreamConnector connector = new GrpcStreamConnector(FlagdOptions.builder().build()); + final GrpcStreamConnector connector = + new GrpcStreamConnector(FlagdOptions.builder().build()); final FlagSyncServiceStub stubMock = mockStubAndReturn(connector); final FlagSyncServiceBlockingStub blockingStubMock = mockBlockingStubAndReturn(connector); final Struct metadata = Struct.newBuilder() - .putFields(key, - com.google.protobuf.Value.newBuilder().setStringValue(val).build()) + .putFields( + key, + com.google.protobuf.Value.newBuilder() + .setStringValue(val) + .build()) .build(); when(blockingStubMock.withDeadlineAfter(anyLong(), any())).thenReturn(blockingStubMock); when(blockingStubMock.getMetadata(any())) - .thenReturn(GetMetadataResponse.newBuilder().setMetadata(metadata).build()); + .thenReturn( + GetMetadataResponse.newBuilder().setMetadata(metadata).build()); final GrpcStreamHandler[] injectedHandler = new GrpcStreamHandler[1]; doAnswer(invocation -> { - injectedHandler[0] = invocation.getArgument(1, GrpcStreamHandler.class); - return null; - }).when(stubMock).syncFlags(any(), any()); + injectedHandler[0] = invocation.getArgument(1, GrpcStreamHandler.class); + return null; + }) + .when(stubMock) + .syncFlags(any(), any()); // when connector.init(); @@ -129,25 +134,24 @@ public void grpcConnectionStatus() throws Throwable { final BlockingQueue streamPayloads = connector.getStream(); // accepted data - grpcStreamHandler.onNext( - SyncFlagsResponse.newBuilder() - .build()); + grpcStreamHandler.onNext(SyncFlagsResponse.newBuilder().build()); assertTimeoutPreemptively(MAX_WAIT_MS, () -> { QueuePayload payload = streamPayloads.take(); assertEquals(QueuePayloadType.DATA, payload.getType()); - assertEquals(val ,convertProtobufMapToStructure(payload.getMetadataResponse().getMetadata().getFieldsMap()).asObjectMap().get(key)); + assertEquals( + val, + convertProtobufMapToStructure( + payload.getMetadataResponse().getMetadata().getFieldsMap()) + .asObjectMap() + .get(key)); }); // ping must be ignored - grpcStreamHandler.onNext( - SyncFlagsResponse.newBuilder() - .build()); + grpcStreamHandler.onNext(SyncFlagsResponse.newBuilder().build()); // accepted data - grpcStreamHandler.onNext( - SyncFlagsResponse.newBuilder() - .build()); + grpcStreamHandler.onNext(SyncFlagsResponse.newBuilder().build()); assertTimeoutPreemptively(MAX_WAIT_MS, () -> { QueuePayload payload = streamPayloads.take(); @@ -158,7 +162,8 @@ public void grpcConnectionStatus() throws Throwable { @Test public void listenerExitOnShutdown() throws Throwable { // given - final GrpcStreamConnector connector = new GrpcStreamConnector(FlagdOptions.builder().build()); + final GrpcStreamConnector connector = + new GrpcStreamConnector(FlagdOptions.builder().build()); final FlagSyncServiceStub stubMock = mockStubAndReturn(connector); final FlagSyncServiceBlockingStub blockingStubMock = mockBlockingStubAndReturn(connector); final GrpcStreamHandler[] injectedHandler = new GrpcStreamHandler[1]; @@ -166,12 +171,15 @@ public void listenerExitOnShutdown() throws Throwable { when(blockingStubMock.withDeadlineAfter(anyLong(), any())).thenReturn(blockingStubMock); when(blockingStubMock.getMetadata(any())) - .thenReturn(GetMetadataResponse.newBuilder().setMetadata(metadata).build()); + .thenReturn( + GetMetadataResponse.newBuilder().setMetadata(metadata).build()); when(stubMock.withDeadlineAfter(anyLong(), any())).thenReturn(stubMock); doAnswer(invocation -> { - injectedHandler[0] = invocation.getArgument(1, GrpcStreamHandler.class); - return null; - }).when(stubMock).syncFlags(any(), any()); + injectedHandler[0] = invocation.getArgument(1, GrpcStreamHandler.class); + return null; + }) + .when(stubMock) + .syncFlags(any(), any()); // when connector.init(); @@ -191,17 +199,15 @@ public void listenerExitOnShutdown() throws Throwable { // Validate mock calls & no more event propagation verify(stubMock, times(1)).syncFlags(any(), any()); - grpcStreamHandler.onNext( - SyncFlagsResponse.newBuilder() - // .setState(SyncService.SyncState.SYNC_STATE_ALL) - .build()); + grpcStreamHandler.onNext(SyncFlagsResponse.newBuilder() + // .setState(SyncService.SyncState.SYNC_STATE_ALL) + .build()); // there should be no data assertNull(connector.getStream().poll(100, TimeUnit.MILLISECONDS)); } - private static FlagSyncServiceStub mockStubAndReturn(final GrpcStreamConnector connector) - throws Throwable { + private static FlagSyncServiceStub mockStubAndReturn(final GrpcStreamConnector connector) throws Throwable { final Field serviceStubField = GrpcStreamConnector.class.getDeclaredField("serviceStub"); serviceStubField.setAccessible(true); @@ -224,5 +230,4 @@ private static FlagSyncServiceBlockingStub mockBlockingStubAndReturn(final GrpcS return blockingStubMock; } - } diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/FractionalTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/FractionalTest.java index 9f1002fe2..ac3e6e19e 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/FractionalTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/FractionalTest.java @@ -1,14 +1,13 @@ package dev.openfeature.contrib.providers.flagd.resolver.process.targeting; +import static dev.openfeature.contrib.providers.flagd.resolver.process.targeting.Operator.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Named.named; +import static org.junit.jupiter.params.provider.Arguments.arguments; + import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.ObjectMapper; import io.github.jamsesso.jsonlogic.evaluator.JsonLogicEvaluationException; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.converter.ArgumentConversionException; -import org.junit.jupiter.params.converter.ConvertWith; -import org.junit.jupiter.params.converter.TypedArgumentConverter; -import org.junit.jupiter.params.provider.MethodSource; - import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -18,11 +17,11 @@ import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; - -import static dev.openfeature.contrib.providers.flagd.resolver.process.targeting.Operator.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Named.named; -import static org.junit.jupiter.params.provider.Arguments.arguments; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.converter.ArgumentConversionException; +import org.junit.jupiter.params.converter.ConvertWith; +import org.junit.jupiter.params.converter.TypedArgumentConverter; +import org.junit.jupiter.params.provider.MethodSource; class FractionalTest { @@ -75,6 +74,7 @@ protected TestData convert(Path path) throws ArgumentConversionException { static class TestData { @JsonProperty("result") Object result; + @JsonProperty("rule") List rule; } diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/OperatorTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/OperatorTest.java index f9dfb424d..7ef1b6a83 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/OperatorTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/OperatorTest.java @@ -4,16 +4,14 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import dev.openfeature.sdk.ImmutableContext; +import dev.openfeature.sdk.Value; +import java.time.Instant; import java.util.HashMap; import java.util.Map; -import java.time.Instant; - import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import dev.openfeature.sdk.ImmutableContext; -import dev.openfeature.sdk.Value; - class OperatorTest { private static Operator OPERATOR; @@ -82,31 +80,31 @@ void fractionalTestA() throws TargetingRuleException { // given // fractional rule with email as expression key - final String targetingRule = "" + - "{\n" + - " \"fractional\": [\n" + - " {\"cat\":[\n" + - " {\"var\":\"$flagd.flagKey\"},\n" + - " {\"var\": \"email\"}\n" + - " ]},\n" + - " [\n" + - " \"red\",\n" + - " 25\n" + - " ],\n" + - " [\n" + - " \"blue\",\n" + - " 25\n" + - " ],\n" + - " [\n" + - " \"green\",\n" + - " 25\n" + - " ],\n" + - " [\n" + - " \"yellow\",\n" + - " 25\n" + - " ]\n" + - " ]\n" + - "}"; + final String targetingRule = "" + + "{\n" + + " \"fractional\": [\n" + + " {\"cat\":[\n" + + " {\"var\":\"$flagd.flagKey\"},\n" + + " {\"var\": \"email\"}\n" + + " ]},\n" + + " [\n" + + " \"red\",\n" + + " 25\n" + + " ],\n" + + " [\n" + + " \"blue\",\n" + + " 25\n" + + " ],\n" + + " [\n" + + " \"green\",\n" + + " 25\n" + + " ],\n" + + " [\n" + + " \"yellow\",\n" + + " 25\n" + + " ]\n" + + " ]\n" + + "}"; Map ctxData = new HashMap<>(); ctxData.put("email", new Value("rachel@faas.com")); @@ -123,31 +121,31 @@ void fractionalTestB() throws TargetingRuleException { // given // fractional rule with email as expression key - final String targetingRule = "" + - "{\n" + - " \"fractional\": [\n" + - " {\"cat\":[\n" + - " {\"var\":\"$flagd.flagKey\"},\n" + - " {\"var\": \"email\"}\n" + - " ]},\n" + - " [\n" + - " \"red\",\n" + - " 25\n" + - " ],\n" + - " [\n" + - " \"blue\",\n" + - " 25\n" + - " ],\n" + - " [\n" + - " \"green\",\n" + - " 25\n" + - " ],\n" + - " [\n" + - " \"yellow\",\n" + - " 25\n" + - " ]\n" + - " ]\n" + - "}"; + final String targetingRule = "" + + "{\n" + + " \"fractional\": [\n" + + " {\"cat\":[\n" + + " {\"var\":\"$flagd.flagKey\"},\n" + + " {\"var\": \"email\"}\n" + + " ]},\n" + + " [\n" + + " \"red\",\n" + + " 25\n" + + " ],\n" + + " [\n" + + " \"blue\",\n" + + " 25\n" + + " ],\n" + + " [\n" + + " \"green\",\n" + + " 25\n" + + " ],\n" + + " [\n" + + " \"yellow\",\n" + + " 25\n" + + " ]\n" + + " ]\n" + + "}"; Map ctxData = new HashMap<>(); ctxData.put("email", new Value("monica@faas.com")); @@ -164,31 +162,31 @@ void fractionalTestC() throws TargetingRuleException { // given // fractional rule with email as expression key - final String targetingRule = "" + - "{\n" + - " \"fractional\": [\n" + - " {\"cat\":[\n" + - " {\"var\":\"$flagd.flagKey\"},\n" + - " {\"var\": \"email\"}\n" + - " ]},\n" + - " [\n" + - " \"red\",\n" + - " 25\n" + - " ],\n" + - " [\n" + - " \"blue\",\n" + - " 25\n" + - " ],\n" + - " [\n" + - " \"green\",\n" + - " 25\n" + - " ],\n" + - " [\n" + - " \"yellow\",\n" + - " 25\n" + - " ]\n" + - " ]\n" + - "}"; + final String targetingRule = "" + + "{\n" + + " \"fractional\": [\n" + + " {\"cat\":[\n" + + " {\"var\":\"$flagd.flagKey\"},\n" + + " {\"var\": \"email\"}\n" + + " ]},\n" + + " [\n" + + " \"red\",\n" + + " 25\n" + + " ],\n" + + " [\n" + + " \"blue\",\n" + + " 25\n" + + " ],\n" + + " [\n" + + " \"green\",\n" + + " 25\n" + + " ],\n" + + " [\n" + + " \"yellow\",\n" + + " 25\n" + + " ]\n" + + " ]\n" + + "}"; Map ctxData = new HashMap<>(); ctxData.put("email", new Value("joey@faas.com")); @@ -205,13 +203,13 @@ void stringCompStartsWith() throws TargetingRuleException { // given // starts with rule with email as expression key - final String targetingRule = "" + - "{\n" + - " \"starts_with\": [\n" + - " {\"var\": \"email\"},\n" + - " \"admin\"\n" + - " ]\n" + - "}"; + final String targetingRule = "" + + "{\n" + + " \"starts_with\": [\n" + + " {\"var\": \"email\"},\n" + + " \"admin\"\n" + + " ]\n" + + "}"; Map ctxData = new HashMap<>(); ctxData.put("email", new Value("admin@faas.com")); @@ -228,13 +226,13 @@ void stringCompEndsWith() throws TargetingRuleException { // given // ends with rule with email as expression key - final String targetingRule = "" + - "{\n" + - " \"ends_with\": [\n" + - " {\"var\": \"email\"},\n" + - " \"@faas.com\"\n" + - " ]\n" + - "}"; + final String targetingRule = "" + + "{\n" + + " \"ends_with\": [\n" + + " {\"var\": \"email\"},\n" + + " \"@faas.com\"\n" + + " ]\n" + + "}"; Map ctxData = new HashMap<>(); ctxData.put("email", new Value("admin@faas.com")); @@ -251,14 +249,14 @@ void semVerA() throws TargetingRuleException { // given // sem_ver rule with version as expression key - final String targetingRule = "{\n" + - " \"if\": [\n" + - " {\n" + - " \"sem_ver\": [{\"var\": \"version\"}, \">=\", \"1.0.0\"]\n" + - " },\n" + - " \"red\", null\n" + - " ]\n" + - "}"; + final String targetingRule = "{\n" + + " \"if\": [\n" + + " {\n" + + " \"sem_ver\": [{\"var\": \"version\"}, \">=\", \"1.0.0\"]\n" + + " },\n" + + " \"red\", null\n" + + " ]\n" + + "}"; Map ctxData = new HashMap<>(); ctxData.put("version", new Value("1.1.0")); @@ -269,5 +267,4 @@ void semVerA() throws TargetingRuleException { // then assertEquals("red", evalVariant); } - -} \ No newline at end of file +} diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/SemVerTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/SemVerTest.java index a1e842345..36a199e96 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/SemVerTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/SemVerTest.java @@ -1,17 +1,16 @@ package dev.openfeature.contrib.providers.flagd.resolver.process.targeting; -import io.github.jamsesso.jsonlogic.evaluator.JsonLogicEvaluationException; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; +import static org.assertj.core.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import io.github.jamsesso.jsonlogic.evaluator.JsonLogicEvaluationException; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; - -import static org.assertj.core.api.Assertions.fail; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; class SemVerTest { @@ -24,8 +23,7 @@ static Stream validInputs() { Arguments.of(Arrays.asList("v1.2.3", ">=", "v1.2.3")), Arguments.of(Arrays.asList("v1.2.3", "^", "v1.0.0")), Arguments.of(Arrays.asList("v5.0.3", "~", "v5.0.8")), - Arguments.of(Arrays.asList("v5.0.3", "~", "v5.0.8")) - ); + Arguments.of(Arrays.asList("v5.0.3", "~", "v5.0.8"))); } @ParameterizedTest @@ -50,11 +48,9 @@ static Stream invalidInputs() { Arguments.of(Arrays.asList("1.2.3", "=", 1.2)), Arguments.of(Arrays.asList("1.2", "=", "1.2.3")), Arguments.of(Arrays.asList("1.2.3", "*", "1.2.3")), - Arguments.of(Arrays.asList("1.2.3", "=", "1.2")) - ); + Arguments.of(Arrays.asList("1.2.3", "=", "1.2"))); } - @ParameterizedTest @MethodSource("invalidInputs") void testInvalidCases(List args) throws JsonLogicEvaluationException { @@ -64,5 +60,4 @@ void testInvalidCases(List args) throws JsonLogicEvaluationException { // then assertNull(semVer.evaluate(args, new Object())); } - -} \ No newline at end of file +} diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/StringCompTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/StringCompTest.java index 21c638ca0..2ba8268dc 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/StringCompTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/targeting/StringCompTest.java @@ -1,15 +1,13 @@ package dev.openfeature.contrib.providers.flagd.resolver.process.targeting; -import io.github.jamsesso.jsonlogic.evaluator.JsonLogicEvaluationException; -import org.junit.jupiter.api.Test; - -import java.util.Arrays; - import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; -class StringCompTest { +import io.github.jamsesso.jsonlogic.evaluator.JsonLogicEvaluationException; +import java.util.Arrays; +import org.junit.jupiter.api.Test; +class StringCompTest { @Test public void startsWithEvaluation() throws JsonLogicEvaluationException { @@ -20,7 +18,7 @@ public void startsWithEvaluation() throws JsonLogicEvaluationException { Object result = startsWith.evaluate(Arrays.asList("abc@123.com", "abc"), new Object()); // then - if (!(result instanceof Boolean)){ + if (!(result instanceof Boolean)) { fail("Result is not of type Boolean"); } @@ -33,10 +31,10 @@ public void endsWithEvaluation() throws JsonLogicEvaluationException { final StringComp endsWith = new StringComp(StringComp.Type.ENDS_WITH); // when - Object result = endsWith.evaluate( Arrays.asList("abc@123.com", "123.com"), new Object()); + Object result = endsWith.evaluate(Arrays.asList("abc@123.com", "123.com"), new Object()); // then - if (!(result instanceof Boolean)){ + if (!(result instanceof Boolean)) { fail("Result is not of type Boolean"); } @@ -78,5 +76,4 @@ public void invalidNumberOfArgs() throws JsonLogicEvaluationException { // then assertThat(result).isNull(); } - -} \ No newline at end of file +} diff --git a/providers/flagsmith/src/main/java/dev.openfeature.contrib.providers.flagsmith/FlagsmithClientConfigurer.java b/providers/flagsmith/src/main/java/dev.openfeature.contrib.providers.flagsmith/FlagsmithClientConfigurer.java index d6a0005b3..fba393820 100644 --- a/providers/flagsmith/src/main/java/dev.openfeature.contrib.providers.flagsmith/FlagsmithClientConfigurer.java +++ b/providers/flagsmith/src/main/java/dev.openfeature.contrib.providers.flagsmith/FlagsmithClientConfigurer.java @@ -8,8 +8,8 @@ import dev.openfeature.contrib.providers.flagsmith.exceptions.InvalidOptionsException; /** - * FlagsmithClientConfigurer helps set up and validate the options for the FlagsmithClient - * used by the FlagsmithProvider class. + * FlagsmithClientConfigurer helps set up and validate the options for the FlagsmithClient used by + * the FlagsmithProvider class. */ public class FlagsmithClientConfigurer { @@ -22,8 +22,7 @@ static FlagsmithClient initializeProvider(FlagsmithProviderOptions options) { validateOptions(options); - FlagsmithClient.Builder flagsmithBuilder = FlagsmithClient - .newBuilder(); + FlagsmithClient.Builder flagsmithBuilder = FlagsmithClient.newBuilder(); // Set main configuration settings flagsmithBuilder.setApiKey(options.getApiKey()); @@ -43,8 +42,7 @@ static FlagsmithClient initializeProvider(FlagsmithProviderOptions options) { } /** - * Sets the cache related configuration for the provider using - * the FlagsmithCacheConfig builder. + * Sets the cache related configuration for the provider using the FlagsmithCacheConfig builder. * * @param options the options used to create the provider * @return a FlagsmithCacheConfig object containing the FlagsmithClient cache options @@ -57,18 +55,14 @@ private static FlagsmithCacheConfig initializeCacheConfig(FlagsmithProviderOptio flagsmithCacheConfig.enableEnvLevelCaching(options.getEnvFlagsCacheKey()); } - if (options.getExpireCacheAfterWrite() > -1 - && options.getExpireCacheAfterWriteTimeUnit() != null) { + if (options.getExpireCacheAfterWrite() > -1 && options.getExpireCacheAfterWriteTimeUnit() != null) { flagsmithCacheConfig.expireAfterAccess( - options.getExpireCacheAfterWrite(), - options.getExpireCacheAfterWriteTimeUnit()); + options.getExpireCacheAfterWrite(), options.getExpireCacheAfterWriteTimeUnit()); } - if (options.getExpireCacheAfterAccess() > -1 - && options.getExpireCacheAfterAccessTimeUnit() != null) { + if (options.getExpireCacheAfterAccess() > -1 && options.getExpireCacheAfterAccessTimeUnit() != null) { flagsmithCacheConfig.expireAfterAccess( - options.getExpireCacheAfterAccess(), - options.getExpireCacheAfterAccessTimeUnit()); + options.getExpireCacheAfterAccess(), options.getExpireCacheAfterAccessTimeUnit()); } if (options.getMaxCacheSize() > -1) { @@ -83,8 +77,7 @@ private static FlagsmithCacheConfig initializeCacheConfig(FlagsmithProviderOptio } /** - * Set the configuration options for the FlagsmithClient using - * the FlagsmithConfig builder. + * Set the configuration options for the FlagsmithClient using the FlagsmithConfig builder. * * @param options The options used to create the provider * @return a FlagsmithConfig object with the FlagsmithClient settings @@ -110,8 +103,7 @@ private static FlagsmithConfig initializeConfig(FlagsmithProviderOptions options } if (options.getSslSocketFactory() != null && options.getTrustManager() != null) { - flagsmithConfig - .sslSocketFactory(options.getSslSocketFactory(), options.getTrustManager()); + flagsmithConfig.sslSocketFactory(options.getSslSocketFactory(), options.getTrustManager()); } if (options.getHttpInterceptor() != null) { @@ -127,15 +119,15 @@ private static FlagsmithConfig initializeConfig(FlagsmithProviderOptions options } if (options.getEnvironmentRefreshIntervalSeconds() > -1) { - flagsmithConfig.withEnvironmentRefreshIntervalSeconds(options - .getEnvironmentRefreshIntervalSeconds()); + flagsmithConfig.withEnvironmentRefreshIntervalSeconds(options.getEnvironmentRefreshIntervalSeconds()); } if (options.isEnableAnalytics()) { flagsmithConfig.withEnableAnalytics(options.isEnableAnalytics()); } - if (options.getSupportedProtocols() != null && !options.getSupportedProtocols().isEmpty()) { + if (options.getSupportedProtocols() != null + && !options.getSupportedProtocols().isEmpty()) { flagsmithConfig.withSupportedProtocols(options.getSupportedProtocols()); } @@ -143,8 +135,8 @@ private static FlagsmithConfig initializeConfig(FlagsmithProviderOptions options } /** - * Check the options that have been provided to see if there are any issues. - * Exceptions will be thrown if there are issues found with the options. + * Check the options that have been provided to see if there are any issues. Exceptions will be + * thrown if there are issues found with the options. * * @param options the options used to create the provider */ @@ -158,13 +150,12 @@ private static void validateOptions(FlagsmithProviderOptions options) { } if (options.getEnvFlagsCacheKey() == null - && (options.getExpireCacheAfterWrite() > -1 - || options.getExpireCacheAfterAccess() > -1 - || options.getMaxCacheSize() > -1 - || options.isRecordCacheStats())) { + && (options.getExpireCacheAfterWrite() > -1 + || options.getExpireCacheAfterAccess() > -1 + || options.getMaxCacheSize() > -1 + || options.isRecordCacheStats())) { throw new InvalidCacheOptionsException( - "No Flagsmith cache key provided but other cache settings have been set." - ); + "No Flagsmith cache key provided but other cache settings have been set."); } } } diff --git a/providers/flagsmith/src/main/java/dev.openfeature.contrib.providers.flagsmith/FlagsmithProvider.java b/providers/flagsmith/src/main/java/dev.openfeature.contrib.providers.flagsmith/FlagsmithProvider.java index 9d37c2b02..a361ccdbd 100644 --- a/providers/flagsmith/src/main/java/dev.openfeature.contrib.providers.flagsmith/FlagsmithProvider.java +++ b/providers/flagsmith/src/main/java/dev.openfeature.contrib.providers.flagsmith/FlagsmithProvider.java @@ -49,7 +49,7 @@ public Metadata getMetadata() { @Override public ProviderEvaluation getBooleanEvaluation( - String key, Boolean defaultValue, EvaluationContext evaluationContext) { + String key, Boolean defaultValue, EvaluationContext evaluationContext) { // When isUsingBooleanConfigValue the feature_state_value will be used as the flag value if (this.options.isUsingBooleanConfigValue()) { return resolveFlagsmithEvaluation(key, defaultValue, evaluationContext, Boolean.class); @@ -67,62 +67,58 @@ public ProviderEvaluation getBooleanEvaluation( @Override public ProviderEvaluation getStringEvaluation( - String key, String defaultValue, EvaluationContext evaluationContext) { + String key, String defaultValue, EvaluationContext evaluationContext) { return resolveFlagsmithEvaluation(key, defaultValue, evaluationContext, String.class); } @Override public ProviderEvaluation getIntegerEvaluation( - String key, Integer defaultValue, EvaluationContext evaluationContext) { + String key, Integer defaultValue, EvaluationContext evaluationContext) { return resolveFlagsmithEvaluation(key, defaultValue, evaluationContext, Integer.class); } @Override public ProviderEvaluation getDoubleEvaluation( - String key, Double defaultValue, EvaluationContext evaluationContext) { + String key, Double defaultValue, EvaluationContext evaluationContext) { return resolveFlagsmithEvaluation(key, defaultValue, evaluationContext, Double.class); } @Override public ProviderEvaluation getObjectEvaluation( - String key, Value defaultValue, EvaluationContext evaluationContext) { + String key, Value defaultValue, EvaluationContext evaluationContext) { return resolveFlagsmithEvaluation(key, defaultValue, evaluationContext, Value.class); } /** - * Get all the flags for a given environment or identity. The Method will use - * getEnvironmentFlags from the Flagsmith sdk if no targeting key is provided - * in the EvaluationContext. If a targeting key is provided then the - * getIdentityFlags method will be used. + * Get all the flags for a given environment or identity. The Method will use getEnvironmentFlags + * from the Flagsmith sdk if no targeting key is provided in the EvaluationContext. If a targeting + * key is provided then the getIdentityFlags method will be used. * * @param ctx an EvaluationContext object with flag evaluation options * @return a Flagsmith Flags object with all the respective flags - * @throws FlagsmithClientError Thrown when there are issue retrieving the flags - * from Flagsmith + * @throws FlagsmithClientError Thrown when there are issue retrieving the flags from Flagsmith */ private Flags getFlags(EvaluationContext ctx) throws FlagsmithClientError { return Objects.isNull(ctx.getTargetingKey()) || ctx.getTargetingKey().isEmpty() - ? flagsmith.getEnvironmentFlags() - : flagsmith.getIdentityFlags(ctx.getTargetingKey(), ctx.asObjectMap()); + ? flagsmith.getEnvironmentFlags() + : flagsmith.getIdentityFlags(ctx.getTargetingKey(), ctx.asObjectMap()); } /** - * Using the Flagsmith SDK this method resolves any type of flag into - * a ProviderEvaluation. Since Flagsmith's sdk is agnostic of type - * the flag needs to be cast to the correct type for OpenFeature's - * ProviderEvaluation object. + * Using the Flagsmith SDK this method resolves any type of flag into a ProviderEvaluation. Since + * Flagsmith's sdk is agnostic of type the flag needs to be cast to the correct type for + * OpenFeature's ProviderEvaluation object. * - * @param key the string identifier for the flag being resolved + * @param key the string identifier for the flag being resolved * @param defaultValue the backup value if the flag can't be resolved - * @param ctx an EvaluationContext object with flag evaluation options + * @param ctx an EvaluationContext object with flag evaluation options * @param expectedType the expected data type of the flag as a class - * @param the data type of the flag + * @param the data type of the flag * @return a ProviderEvaluation object for the given flag type * @throws OpenFeatureError when flag evaluation fails */ private ProviderEvaluation resolveFlagsmithEvaluation( - String key, T defaultValue, EvaluationContext ctx, Class expectedType - ) throws OpenFeatureError { + String key, T defaultValue, EvaluationContext ctx, Class expectedType) throws OpenFeatureError { T flagValue = null; ErrorCode errorCode = null; Reason reason = null; @@ -150,21 +146,19 @@ private ProviderEvaluation resolveFlagsmithEvaluation( } /** - * Build a ProviderEvaluation object from the results provided by the - * Flagsmith sdk. + * Build a ProviderEvaluation object from the results provided by the Flagsmith sdk. * - * @param flagValue the resolved flag either retrieved or set to the default - * @param errorCode error type for failed flag resolution, null if no issue - * @param reason description of issue resolving flag, null if no issue + * @param flagValue the resolved flag either retrieved or set to the default + * @param errorCode error type for failed flag resolution, null if no issue + * @param reason description of issue resolving flag, null if no issue * @param variationType contains the name of the variation used for this flag - * @param the data type of the flag + * @param the data type of the flag * @return a ProviderEvaluation object for the given flag type */ private ProviderEvaluation buildEvaluation( - T flagValue, ErrorCode errorCode, Reason reason, String variationType) { + T flagValue, ErrorCode errorCode, Reason reason, String variationType) { ProviderEvaluation.ProviderEvaluationBuilder providerEvaluationBuilder = - ProviderEvaluation.builder() - .value(flagValue); + ProviderEvaluation.builder().value(flagValue); if (errorCode != null) { providerEvaluationBuilder.errorCode(errorCode); @@ -182,21 +176,21 @@ private ProviderEvaluation buildEvaluation( /** * The method convertValue is converting the object return by the Flagsmith client. * - * @param value the value we have received from Flagsmith + * @param value the value we have received from Flagsmith * @param expectedType the type we expect for this value - * @param the type we want to convert to + * @param the type we want to convert to * @return A converted object */ private T convertValue(Object value, Class expectedType) { boolean isPrimitive = expectedType == Boolean.class - || expectedType == String.class - || expectedType == Integer.class - || expectedType == Double.class; + || expectedType == String.class + || expectedType == Integer.class + || expectedType == Double.class; T flagValue = isPrimitive ? (T) value : (T) objectToValue(value); if (flagValue.getClass() != expectedType) { - throw new TypeMismatchError("Flag value had an unexpected type " - + flagValue.getClass() + ", expected " + expectedType + "."); + throw new TypeMismatchError( + "Flag value had an unexpected type " + flagValue.getClass() + ", expected " + expectedType + "."); } return flagValue; } @@ -225,9 +219,8 @@ private Value objectToValue(Object object) { return new Value((Structure) object); } else if (object instanceof List) { // need to translate each elem in list to a value - return new Value(((List) object).stream() - .map(this::objectToValue) - .collect(Collectors.toList())); + return new Value( + ((List) object).stream().map(this::objectToValue).collect(Collectors.toList())); } else if (object instanceof Instant) { return new Value((Instant) object); } else if (object instanceof Map) { @@ -236,8 +229,7 @@ private Value objectToValue(Object object) { ObjectNode objectNode = (ObjectNode) object; return objectToValue(new ObjectMapper().convertValue(objectNode, Object.class)); } else { - throw new TypeMismatchError("Flag value " + object + " had unexpected type " - + object.getClass() + "."); + throw new TypeMismatchError("Flag value " + object + " had unexpected type " + object.getClass() + "."); } } @@ -248,9 +240,8 @@ private Value objectToValue(Object object) { * @return a Structure object in the SDK format */ private Structure mapToStructure(Map map) { - return new MutableStructure( - map.entrySet().stream() - .filter(e -> e.getValue() != null) - .collect(Collectors.toMap(Map.Entry::getKey, e -> objectToValue(e.getValue())))); + return new MutableStructure(map.entrySet().stream() + .filter(e -> e.getValue() != null) + .collect(Collectors.toMap(Map.Entry::getKey, e -> objectToValue(e.getValue())))); } -} \ No newline at end of file +} diff --git a/providers/flagsmith/src/main/java/dev.openfeature.contrib.providers.flagsmith/FlagsmithProviderOptions.java b/providers/flagsmith/src/main/java/dev.openfeature.contrib.providers.flagsmith/FlagsmithProviderOptions.java index 57ce4ef73..c991496d8 100644 --- a/providers/flagsmith/src/main/java/dev.openfeature.contrib.providers.flagsmith/FlagsmithProviderOptions.java +++ b/providers/flagsmith/src/main/java/dev.openfeature.contrib.providers.flagsmith/FlagsmithProviderOptions.java @@ -1,199 +1,143 @@ package dev.openfeature.contrib.providers.flagsmith; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; - import com.flagsmith.config.FlagsmithConfig.Protocol; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; - -import lombok.Builder; -import lombok.Getter; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.X509TrustManager; +import lombok.Builder; +import lombok.Getter; import okhttp3.Interceptor; -/** - * FlagsmithProviderOptions contains the options to initialise the Flagsmith provider. - */ -@SuppressFBWarnings(value = {"EI_EXPOSE_REP", "EI_EXPOSE_REP2"}, justification = "The headers need to be mutable") +/** FlagsmithProviderOptions contains the options to initialise the Flagsmith provider. */ +@SuppressFBWarnings( + value = {"EI_EXPOSE_REP", "EI_EXPOSE_REP2"}, + justification = "The headers need to be mutable") @Builder(toBuilder = true) @Getter public class FlagsmithProviderOptions { /** - * Your API Token. - * Note that this is either the `Environment API` key or the `Server Side SDK Token` - * depending on if you are using Local or Remote Evaluation - * Required. + * Your API Token. Note that this is either the `Environment API` key or the `Server Side SDK + * Token` depending on if you are using Local or Remote Evaluation Required. */ private String apiKey; /** - * Add custom headers which will be sent with each network request - * to the Flagsmith API. - * Optional. + * Add custom headers which will be sent with each network request to the Flagsmith API. Optional. * Defaults to no custom headers. */ private HashMap headers; // Cache based properties - /** - * Enable in-memory caching for the Flagsmith API. - * Optional. - * Default: not caching anything. - */ + /** Enable in-memory caching for the Flagsmith API. Optional. Default: not caching anything. */ private String envFlagsCacheKey; - /** - * The time unit used for cache expiry after write. - * Optional - * Default: TimeUnit.MINUTES - */ + /** The time unit used for cache expiry after write. Optional Default: TimeUnit.MINUTES */ private TimeUnit expireCacheAfterWriteTimeUnit; - /** - * The integer time for cache expiry after write. - * Optional - * Default: -1 - */ + /** The integer time for cache expiry after write. Optional Default: -1 */ @Builder.Default private int expireCacheAfterWrite = -1; - /** - * The time unit used for cache expiry after reading. - * Optional - * Default: TimeUnit.MINUTES - */ + /** The time unit used for cache expiry after reading. Optional Default: TimeUnit.MINUTES */ private TimeUnit expireCacheAfterAccessTimeUnit; - /** - * The integer time for cache expiry after reading. - * Optional - * Default: -1 - */ + /** The integer time for cache expiry after reading. Optional Default: -1 */ @Builder.Default private int expireCacheAfterAccess = -1; - /** - * The maximum size of the cache in MB. - * Optional - * Default: -1 - */ + /** The maximum size of the cache in MB. Optional Default: -1 */ @Builder.Default private int maxCacheSize = -1; - /** - * Whether cache statistics should be recorded. - * Optional - * Default: false - */ + /** Whether cache statistics should be recorded. Optional Default: false */ @Builder.Default private boolean recordCacheStats = false; /** - * Override the default Flagsmith API URL if you are self-hosting. - * Optional. - * Default: https://edge.api.flagsmith.com/api/v1/ + * Override the default Flagsmith API URL if you are self-hosting. Optional. Default: + * https://edge.api.flagsmith.com/api/v1/ */ @Builder.Default private String baseUri = "https://edge.api.flagsmith.com/api/v1/"; /** - * The network timeout in milliseconds. - * See https://square.github.io/okhttp/4.x/okhttp/okhttp3/ for details - * Defaults: 2000 + * The network timeout in milliseconds. See https://square.github.io/okhttp/4.x/okhttp/okhttp3/ + * for details Defaults: 2000 */ @Builder.Default private int connectTimeout = 2000; /** - * The network timeout in milliseconds when writing. - * See https://square.github.io/okhttp/4.x/okhttp/okhttp3/ for details - * Defaults: 5000 + * The network timeout in milliseconds when writing. See + * https://square.github.io/okhttp/4.x/okhttp/okhttp3/ for details Defaults: 5000 */ @Builder.Default private int writeTimeout = 5000; /** - * The network timeout in milliseconds when reading. - * See https://square.github.io/okhttp/4.x/okhttp/okhttp3/ for details - * Defaults: 5000 + * The network timeout in milliseconds when reading. See + * https://square.github.io/okhttp/4.x/okhttp/okhttp3/ for details Defaults: 5000 */ @Builder.Default private int readTimeout = 5000; /** - * Override the sslSocketFactory - * See https://square.github.io/okhttp/4.x/okhttp/okhttp3/ for details - * Optional, must include trustManager if provided. + * Override the sslSocketFactory See https://square.github.io/okhttp/4.x/okhttp/okhttp3/ for + * details Optional, must include trustManager if provided. */ private SSLSocketFactory sslSocketFactory; /** - * X509TrustManager used when overriding the sslSocketFactory - * See https://square.github.io/okhttp/4.x/okhttp/okhttp3/ for details - * Optional, must include sslSocketFactory if provided. + * X509TrustManager used when overriding the sslSocketFactory See + * https://square.github.io/okhttp/4.x/okhttp/okhttp3/ for details Optional, must include + * sslSocketFactory if provided. */ private X509TrustManager trustManager; - /** - * Add a custom HTTP interceptor in the form of an okhttp3.Interceptor object. - * Optional. - */ + /** Add a custom HTTP interceptor in the form of an okhttp3.Interceptor object. Optional. */ private Interceptor httpInterceptor; /** - * Add a custom com.flagsmith.config.Retry object to configure the - * backoff / retry configuration. - * Optional. - * Default: 3 + * Add a custom com.flagsmith.config.Retry object to configure the backoff / retry configuration. + * Optional. Default: 3 */ @Builder.Default private int retries = 3; - /** - * Controls which mode to run in; local or remote evaluation. - * Optional. - * Default: false. - */ + /** Controls which mode to run in; local or remote evaluation. Optional. Default: false. */ @Builder.Default private boolean localEvaluation = false; /** - * Set environment refresh rate with polling manager. - * Only needed when local evaluation is true. - * Optional. - * Default: 60 + * Set environment refresh rate with polling manager. Only needed when local evaluation is true. + * Optional. Default: 60 */ private int environmentRefreshIntervalSeconds = 60; /** - * Controls whether Flag Analytics data is sent to the Flagsmith API - * See https://docs.flagsmith.com/advanced-use/flag-analytics. - * Optional. - * Default: false + * Controls whether Flag Analytics data is sent to the Flagsmith API See + * https://docs.flagsmith.com/advanced-use/flag-analytics. Optional. Default: false */ @Builder.Default private boolean enableAnalytics = false; /** - * Determines whether to resolve a feature value as a boolean or use - * the isFeatureEnabled as the flag itself. These values will be false - * and true respectively. - * Optional. - * Default: false + * Determines whether to resolve a feature value as a boolean or use the isFeatureEnabled as the + * flag itself. These values will be false and true respectively. Optional. Default: false */ @Builder.Default private boolean usingBooleanConfigValue = false; /** - * Set the list of supported protocols that should be used. - * Optional. - * Default: All the enum protocols from FlagsmithConfig. + * Set the list of supported protocols that should be used. Optional. Default: All the enum + * protocols from FlagsmithConfig. */ @Builder.Default private List supportedProtocols = Arrays.stream(Protocol.values()).collect(Collectors.toList()); -} \ No newline at end of file +} diff --git a/providers/flagsmith/src/main/java/dev/openfeature/contrib/providers/flagsmith/exceptions/FlagsmithProviderException.java b/providers/flagsmith/src/main/java/dev/openfeature/contrib/providers/flagsmith/exceptions/FlagsmithProviderException.java index 171e51ac2..6c54b94b4 100644 --- a/providers/flagsmith/src/main/java/dev/openfeature/contrib/providers/flagsmith/exceptions/FlagsmithProviderException.java +++ b/providers/flagsmith/src/main/java/dev/openfeature/contrib/providers/flagsmith/exceptions/FlagsmithProviderException.java @@ -4,9 +4,7 @@ import dev.openfeature.sdk.exceptions.GeneralError; import lombok.Getter; -/** - * A Flagsmith provider exception is the main exception for the provider. - */ +/** A Flagsmith provider exception is the main exception for the provider. */ @Getter public class FlagsmithProviderException extends GeneralError { private static final long serialVersionUID = 1L; @@ -15,5 +13,4 @@ public class FlagsmithProviderException extends GeneralError { public FlagsmithProviderException(String message) { super(message); } - } diff --git a/providers/flagsmith/src/main/java/dev/openfeature/contrib/providers/flagsmith/exceptions/InvalidCacheOptionsException.java b/providers/flagsmith/src/main/java/dev/openfeature/contrib/providers/flagsmith/exceptions/InvalidCacheOptionsException.java index 7f8bdf4f4..142828e16 100644 --- a/providers/flagsmith/src/main/java/dev/openfeature/contrib/providers/flagsmith/exceptions/InvalidCacheOptionsException.java +++ b/providers/flagsmith/src/main/java/dev/openfeature/contrib/providers/flagsmith/exceptions/InvalidCacheOptionsException.java @@ -14,5 +14,4 @@ public class InvalidCacheOptionsException extends FlagsmithProviderException { public InvalidCacheOptionsException(String message) { super(message); } - } diff --git a/providers/flagsmith/src/main/java/dev/openfeature/contrib/providers/flagsmith/exceptions/InvalidOptionsException.java b/providers/flagsmith/src/main/java/dev/openfeature/contrib/providers/flagsmith/exceptions/InvalidOptionsException.java index f61317d8e..f909e8253 100644 --- a/providers/flagsmith/src/main/java/dev/openfeature/contrib/providers/flagsmith/exceptions/InvalidOptionsException.java +++ b/providers/flagsmith/src/main/java/dev/openfeature/contrib/providers/flagsmith/exceptions/InvalidOptionsException.java @@ -3,9 +3,7 @@ import dev.openfeature.sdk.ErrorCode; import lombok.Getter; -/** - * InvalidOptionsException is the super Exception used when we have a configuration exception. - */ +/** InvalidOptionsException is the super Exception used when we have a configuration exception. */ @Getter public class InvalidOptionsException extends FlagsmithProviderException { private static final long serialVersionUID = 1L; diff --git a/providers/flagsmith/src/test/java/dev.openfeature.contrib.providers.flagsmith/FlagsmithProviderTest.java b/providers/flagsmith/src/test/java/dev.openfeature.contrib.providers.flagsmith/FlagsmithProviderTest.java index 74ab12d9c..83e60c0d0 100644 --- a/providers/flagsmith/src/test/java/dev.openfeature.contrib.providers.flagsmith/FlagsmithProviderTest.java +++ b/providers/flagsmith/src/test/java/dev.openfeature.contrib.providers.flagsmith/FlagsmithProviderTest.java @@ -1,5 +1,10 @@ package dev.openfeature.contrib.providers.flagsmith; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.flagsmith.config.FlagsmithConfig; @@ -17,7 +22,6 @@ import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Paths; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -35,10 +39,6 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class FlagsmithProviderTest { @@ -53,18 +53,18 @@ public class FlagsmithProviderTest { public MockResponse dispatch(RecordedRequest request) { if (request.getPath().startsWith("/flags/")) { return new MockResponse() - .setBody(readMockResponse("valid_flags_response.json")) - .addHeader("Content-Type", "application/json"); + .setBody(readMockResponse("valid_flags_response.json")) + .addHeader("Content-Type", "application/json"); } if (request.getPath().startsWith("/identities/")) { return new MockResponse() - .setBody(readMockResponse("valid_identity_response.json")) - .addHeader("Content-Type", "application/json"); + .setBody(readMockResponse("valid_identity_response.json")) + .addHeader("Content-Type", "application/json"); } if (request.getPath().startsWith("/environment-document/")) { return new MockResponse() - .setBody(readMockResponse("environment-document.json")) - .addHeader("Content-Type", "application/json"); + .setBody(readMockResponse("environment-document.json")) + .addHeader("Content-Type", "application/json"); } return new MockResponse().setResponseCode(404); } @@ -80,58 +80,57 @@ public MockResponse dispatch(RecordedRequest request) { private static Stream provideKeysForFlagResolution() { return Stream.of( - Arguments.of("true_key", "getBooleanEvaluation", Boolean.class, "true"), - Arguments.of("false_key", "getBooleanEvaluation", Boolean.class, "false"), - Arguments.of("string_key", "getStringEvaluation", String.class, "string_value"), - Arguments.of("int_key", "getIntegerEvaluation", Integer.class, "1"), - Arguments.of("double_key", "getDoubleEvaluation", Double.class, "3.141"), - Arguments.of("object_key", "getObjectEvaluation", Value.class, "{\"name\":\"json\"}") - ); + Arguments.of("true_key", "getBooleanEvaluation", Boolean.class, "true"), + Arguments.of("false_key", "getBooleanEvaluation", Boolean.class, "false"), + Arguments.of("string_key", "getStringEvaluation", String.class, "string_value"), + Arguments.of("int_key", "getIntegerEvaluation", Integer.class, "1"), + Arguments.of("double_key", "getDoubleEvaluation", Double.class, "3.141"), + Arguments.of("object_key", "getObjectEvaluation", Value.class, "{\"name\":\"json\"}")); } private static Stream provideDisabledKeysForFlagResolution() { return Stream.of( - Arguments.of("true_key_disabled", "getBooleanEvaluation", Boolean.class, "false"), - Arguments.of("false_key_disabled", "getBooleanEvaluation", Boolean.class, "true"), - Arguments - .of("string_key_disabled", "getStringEvaluation", String.class, "no_string_value"), - Arguments.of("int_key_disabled", "getIntegerEvaluation", Integer.class, "2"), - Arguments.of("double_key_disabled", "getDoubleEvaluation", Double.class, "1.47"), - Arguments - .of("object_key_disabled", "getObjectEvaluation", Value.class, "{\"name\":\"not_json\"}") - ); + Arguments.of("true_key_disabled", "getBooleanEvaluation", Boolean.class, "false"), + Arguments.of("false_key_disabled", "getBooleanEvaluation", Boolean.class, "true"), + Arguments.of("string_key_disabled", "getStringEvaluation", String.class, "no_string_value"), + Arguments.of("int_key_disabled", "getIntegerEvaluation", Integer.class, "2"), + Arguments.of("double_key_disabled", "getDoubleEvaluation", Double.class, "1.47"), + Arguments.of("object_key_disabled", "getObjectEvaluation", Value.class, "{\"name\":\"not_json\"}")); } private static Stream provideBooleanKeysForEnabledFlagResolution() { return Stream.of( - Arguments.of("true_key", "true", null), - Arguments.of("false_key", "true", null), - Arguments.of("true_key_disabled", "false", Reason.DISABLED.name()), - Arguments.of("false_key_disabled", "false", Reason.DISABLED.name()) - ); + Arguments.of("true_key", "true", null), + Arguments.of("false_key", "true", null), + Arguments.of("true_key_disabled", "false", Reason.DISABLED.name()), + Arguments.of("false_key_disabled", "false", Reason.DISABLED.name())); } private static Stream invalidOptions() { return Stream.of( - null, - Arguments.of(FlagsmithProviderOptions.builder().build()), - Arguments.of(FlagsmithProviderOptions.builder().apiKey("").build()) - ); + null, + Arguments.of(FlagsmithProviderOptions.builder().build()), + Arguments.of(FlagsmithProviderOptions.builder().apiKey("").build())); } private static Stream invalidCacheOptions() { return Stream.of( - Arguments - .of(FlagsmithProviderOptions.builder().apiKey("API_KEY").expireCacheAfterAccess(1) - .build()), - Arguments - .of(FlagsmithProviderOptions.builder().apiKey("API_KEY").maxCacheSize(1).build()), - Arguments - .of(FlagsmithProviderOptions.builder().apiKey("API_KEY").expireCacheAfterWrite(1) - .build()), - Arguments.of(FlagsmithProviderOptions.builder().apiKey("API_KEY").recordCacheStats(true) - .build()) - ); + Arguments.of(FlagsmithProviderOptions.builder() + .apiKey("API_KEY") + .expireCacheAfterAccess(1) + .build()), + Arguments.of(FlagsmithProviderOptions.builder() + .apiKey("API_KEY") + .maxCacheSize(1) + .build()), + Arguments.of(FlagsmithProviderOptions.builder() + .apiKey("API_KEY") + .expireCacheAfterWrite(1) + .build()), + Arguments.of(FlagsmithProviderOptions.builder() + .apiKey("API_KEY") + .recordCacheStats(true) + .build())); } @BeforeEach @@ -147,13 +146,10 @@ void setUp() throws IOException { mockFlagsmithErrorServer.start(); FlagsmithProviderOptions options = FlagsmithProviderOptions.builder() - .apiKey("API_KEY") - .baseUri(String - .format("http://localhost:%s", - mockFlagsmithServer - .getPort())) - .usingBooleanConfigValue(true) - .build(); + .apiKey("API_KEY") + .baseUri(String.format("http://localhost:%s", mockFlagsmithServer.getPort())) + .usingBooleanConfigValue(true) + .build(); flagsmithProvider = new FlagsmithProvider(options); } @@ -165,46 +161,46 @@ void tearDown() throws IOException { @Test void shouldInitializeProviderWhenAllOptionsSet() { - HashMap headers = - new HashMap() {{ + HashMap headers = new HashMap() { + { put("header", "string"); - }}; - - FlagsmithProviderOptions options = - FlagsmithProviderOptions.builder() - .apiKey("ser.API_KEY") - .baseUri(String - .format("http://localhost:%s", - mockFlagsmithServer - .getPort())) - .headers(headers) - .envFlagsCacheKey("CACHE_KEY") - .expireCacheAfterWriteTimeUnit(TimeUnit.MINUTES) - .expireCacheAfterWrite(10000) - .expireCacheAfterAccessTimeUnit(TimeUnit.MINUTES) - .expireCacheAfterAccess(10000) - .maxCacheSize(1) - .recordCacheStats(true) - .httpInterceptor(null) - .connectTimeout(10000) - .writeTimeout(10000) - .readTimeout(10000) - .retries(1) - .localEvaluation(true) - .environmentRefreshIntervalSeconds(1) - .enableAnalytics(true) - .usingBooleanConfigValue(false) - .supportedProtocols(Collections.singletonList(FlagsmithConfig.Protocol.HTTP_1_1)) - .build(); + } + }; + + FlagsmithProviderOptions options = FlagsmithProviderOptions.builder() + .apiKey("ser.API_KEY") + .baseUri(String.format("http://localhost:%s", mockFlagsmithServer.getPort())) + .headers(headers) + .envFlagsCacheKey("CACHE_KEY") + .expireCacheAfterWriteTimeUnit(TimeUnit.MINUTES) + .expireCacheAfterWrite(10000) + .expireCacheAfterAccessTimeUnit(TimeUnit.MINUTES) + .expireCacheAfterAccess(10000) + .maxCacheSize(1) + .recordCacheStats(true) + .httpInterceptor(null) + .connectTimeout(10000) + .writeTimeout(10000) + .readTimeout(10000) + .retries(1) + .localEvaluation(true) + .environmentRefreshIntervalSeconds(1) + .enableAnalytics(true) + .usingBooleanConfigValue(false) + .supportedProtocols(Collections.singletonList(FlagsmithConfig.Protocol.HTTP_1_1)) + .build(); assertDoesNotThrow(() -> new FlagsmithProvider(options)); } @Test void shouldGetMetadataAndValidateName() { - assertEquals("Flagsmith Provider", new FlagsmithProvider(FlagsmithProviderOptions.builder() - .apiKey("API_KEY") - .build()) - .getMetadata().getName()); + assertEquals( + "Flagsmith Provider", + new FlagsmithProvider(FlagsmithProviderOptions.builder() + .apiKey("API_KEY") + .build()) + .getMetadata() + .getName()); } @ParameterizedTest @@ -223,14 +219,14 @@ void shouldThrowAnExceptionWhenCacheOptionsInvalid(FlagsmithProviderOptions opti @ParameterizedTest @MethodSource("provideKeysForFlagResolution") void shouldResolveFlagCorrectlyWithCorrectFlagType( - String key, String methodName, Class expectedType, String flagsmithResult) { + String key, String methodName, Class expectedType, String flagsmithResult) { // Given Object result = null; EvaluationContext evaluationContext = new MutableContext(); // When - Method method = flagsmithProvider.getClass() - .getMethod(methodName, String.class, expectedType, EvaluationContext.class); + Method method = + flagsmithProvider.getClass().getMethod(methodName, String.class, expectedType, EvaluationContext.class); result = method.invoke(flagsmithProvider, key, null, evaluationContext); // Then @@ -246,7 +242,7 @@ void shouldResolveFlagCorrectlyWithCorrectFlagType( @ParameterizedTest @MethodSource("provideKeysForFlagResolution") void shouldResolveIdentityFlagCorrectlyWithCorrectFlagType( - String key, String methodName, Class expectedType, String flagsmithResult) { + String key, String methodName, Class expectedType, String flagsmithResult) { // Given Object result = null; MutableContext evaluationContext = new MutableContext(); @@ -254,8 +250,8 @@ void shouldResolveIdentityFlagCorrectlyWithCorrectFlagType( evaluationContext.add("trait1", "value1"); // When - Method method = flagsmithProvider.getClass() - .getMethod(methodName, String.class, expectedType, EvaluationContext.class); + Method method = + flagsmithProvider.getClass().getMethod(methodName, String.class, expectedType, EvaluationContext.class); result = method.invoke(flagsmithProvider, key, null, evaluationContext); // Then @@ -271,14 +267,13 @@ void shouldResolveIdentityFlagCorrectlyWithCorrectFlagType( @ParameterizedTest @MethodSource("provideDisabledKeysForFlagResolution") void shouldNotResolveFlagIfFlagIsInactiveInFlagsmithInsteadUsingDefaultValue( - String key, String methodName, Class expectedType, String defaultValueString) { + String key, String methodName, Class expectedType, String defaultValueString) { // Given Object defaultValue; if (expectedType == String.class) { defaultValue = defaultValueString; } else if (expectedType == Value.class) { - Map map = new ObjectMapper() - .readValue(defaultValueString, HashMap.class); + Map map = new ObjectMapper().readValue(defaultValueString, HashMap.class); defaultValue = new Value(new MutableStructure(map)); } else { Method castMethod = expectedType.getMethod("valueOf", String.class); @@ -289,8 +284,8 @@ void shouldNotResolveFlagIfFlagIsInactiveInFlagsmithInsteadUsingDefaultValue( EvaluationContext evaluationContext = new MutableContext(); // When - Method method = flagsmithProvider.getClass() - .getMethod(methodName, String.class, expectedType, EvaluationContext.class); + Method method = + flagsmithProvider.getClass().getMethod(methodName, String.class, expectedType, EvaluationContext.class); result = method.invoke(flagsmithProvider, key, defaultValue, evaluationContext); // Then @@ -308,30 +303,23 @@ void shouldNotResolveFlagIfExceptionThrownInFlagsmithInsteadUsingDefaultValue() String key = "missing_key"; EvaluationContext evaluationContext = new MutableContext(); assertThrows( - FlagNotFoundError.class, - () -> flagsmithProvider - .getBooleanEvaluation(key, true, new MutableContext()) - ); + FlagNotFoundError.class, () -> flagsmithProvider.getBooleanEvaluation(key, true, new MutableContext())); } @SneakyThrows @ParameterizedTest @MethodSource("provideBooleanKeysForEnabledFlagResolution") - void shouldResolveBooleanFlagUsingEnabledField( - String key, String flagsmithResult, String reason) { + void shouldResolveBooleanFlagUsingEnabledField(String key, String flagsmithResult, String reason) { // Given FlagsmithProviderOptions options = FlagsmithProviderOptions.builder() - .apiKey("API_KEY") - .baseUri(String - .format("http://localhost:%s", - mockFlagsmithServer - .getPort())) - .build(); + .apiKey("API_KEY") + .baseUri(String.format("http://localhost:%s", mockFlagsmithServer.getPort())) + .build(); FlagsmithProvider booleanFlagsmithProvider = new FlagsmithProvider(options); // When ProviderEvaluation result = - booleanFlagsmithProvider.getBooleanEvaluation(key, true, new MutableContext()); + booleanFlagsmithProvider.getBooleanEvaluation(key, true, new MutableContext()); // Then String resultString = getResultString(result.getValue(), Boolean.class); @@ -345,57 +333,44 @@ void shouldResolveBooleanFlagUsingEnabledField( void shouldNotResolveBooleanFlagValueIfFlagsmithErrorThrown() { // Given FlagsmithProviderOptions options = FlagsmithProviderOptions.builder() - .apiKey("API_KEY") - .baseUri(String - .format("http://localhost:%s", - mockFlagsmithErrorServer - .getPort())) - .usingBooleanConfigValue(false) - .build(); + .apiKey("API_KEY") + .baseUri(String.format("http://localhost:%s", mockFlagsmithErrorServer.getPort())) + .usingBooleanConfigValue(false) + .build(); FlagsmithProvider booleanFlagsmithProvider = new FlagsmithProvider(options); // When assertThrows( - GeneralError.class, - () -> - booleanFlagsmithProvider.getBooleanEvaluation( - "true_key", false, new MutableContext() - ) - ); + GeneralError.class, + () -> booleanFlagsmithProvider.getBooleanEvaluation("true_key", false, new MutableContext())); } @Test void shouldNotResolveFlagValueIfFlagsmithErrorThrown() { // Given FlagsmithProviderOptions options = FlagsmithProviderOptions.builder() - .apiKey("API_KEY") - .baseUri(String - .format("http://localhost:%s", - mockFlagsmithErrorServer - .getPort())) - .usingBooleanConfigValue(true) - .build(); + .apiKey("API_KEY") + .baseUri(String.format("http://localhost:%s", mockFlagsmithErrorServer.getPort())) + .usingBooleanConfigValue(true) + .build(); FlagsmithProvider booleanFlagsmithProvider = new FlagsmithProvider(options); // When assertThrows( - GeneralError.class, - () -> - booleanFlagsmithProvider.getBooleanEvaluation( - "true_key", false, new MutableContext() - ) - ); + GeneralError.class, + () -> booleanFlagsmithProvider.getBooleanEvaluation("true_key", false, new MutableContext())); } private String readMockResponse(String filename) throws IOException { - String file = getClass().getClassLoader().getResource("mock_responses/" + filename) - .getFile(); + String file = getClass() + .getClassLoader() + .getResource("mock_responses/" + filename) + .getFile(); byte[] bytes = Files.readAllBytes(Paths.get(file)); return new String(bytes); } - private String getResultString(Object responseValue, Class expectedType) - throws JsonProcessingException { + private String getResultString(Object responseValue, Class expectedType) throws JsonProcessingException { String resultString = ""; if (expectedType == Value.class) { Value value = (Value) responseValue; diff --git a/providers/flipt/src/main/java/dev/openfeature/contrib/providers/flipt/ContextTransformer.java b/providers/flipt/src/main/java/dev/openfeature/contrib/providers/flipt/ContextTransformer.java index 97f0143a3..6a7634052 100644 --- a/providers/flipt/src/main/java/dev/openfeature/contrib/providers/flipt/ContextTransformer.java +++ b/providers/flipt/src/main/java/dev/openfeature/contrib/providers/flipt/ContextTransformer.java @@ -1,13 +1,10 @@ package dev.openfeature.contrib.providers.flipt; import dev.openfeature.sdk.EvaluationContext; - import java.util.HashMap; import java.util.Map; -/** - * Transformer from OpenFeature context to Flipt context. - */ +/** Transformer from OpenFeature context to Flipt context. */ public class ContextTransformer { protected static Map transform(EvaluationContext ctx) { diff --git a/providers/flipt/src/main/java/dev/openfeature/contrib/providers/flipt/FliptProvider.java b/providers/flipt/src/main/java/dev/openfeature/contrib/providers/flipt/FliptProvider.java index f3cc354ca..49cf980c9 100644 --- a/providers/flipt/src/main/java/dev/openfeature/contrib/providers/flipt/FliptProvider.java +++ b/providers/flipt/src/main/java/dev/openfeature/contrib/providers/flipt/FliptProvider.java @@ -3,9 +3,6 @@ import static dev.openfeature.sdk.Reason.DEFAULT; import static dev.openfeature.sdk.Reason.TARGETING_MATCH; -import java.util.Map; -import java.util.concurrent.atomic.AtomicBoolean; - import dev.openfeature.sdk.EvaluationContext; import dev.openfeature.sdk.EventProvider; import dev.openfeature.sdk.ImmutableMetadata; @@ -17,19 +14,20 @@ import io.flipt.api.evaluation.models.BooleanEvaluationResponse; import io.flipt.api.evaluation.models.EvaluationRequest; import io.flipt.api.evaluation.models.VariantEvaluationResponse; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; -/** - * Provider implementation for Flipt. - */ +/** Provider implementation for Flipt. */ @Slf4j public class FliptProvider extends EventProvider { @Getter private static final String NAME = "Flipt"; + public static final String PROVIDER_NOT_YET_INITIALIZED = "provider not yet initialized"; public static final String UNKNOWN_ERROR = "unknown error"; @@ -39,11 +37,12 @@ public class FliptProvider extends EventProvider { @Setter(AccessLevel.PROTECTED) @Getter private FliptClient fliptClient; + private final AtomicBoolean isInitialized = new AtomicBoolean(false); /** * Constructor. - * + * * @param fliptProviderConfig FliptProviderConfig */ public FliptProvider(FliptProviderConfig fliptProviderConfig) { @@ -52,7 +51,7 @@ public FliptProvider(FliptProviderConfig fliptProviderConfig) { /** * Initialize the provider. - * + * * @param evaluationContext evaluation context * @throws Exception on error */ @@ -76,8 +75,12 @@ public Metadata getMetadata() { @Override public ProviderEvaluation getBooleanEvaluation(String key, Boolean defaultValue, EvaluationContext ctx) { Map contextMap = ContextTransformer.transform(ctx); - EvaluationRequest request = EvaluationRequest.builder().namespaceKey(fliptProviderConfig.getNamespace()) - .flagKey(key).entityId(ctx.getTargetingKey()).context(contextMap).build(); + EvaluationRequest request = EvaluationRequest.builder() + .namespaceKey(fliptProviderConfig.getNamespace()) + .flagKey(key) + .entityId(ctx.getTargetingKey()) + .context(contextMap) + .build(); BooleanEvaluationResponse response = null; try { @@ -95,8 +98,8 @@ public ProviderEvaluation getBooleanEvaluation(String key, Boolean defa @Override public ProviderEvaluation getStringEvaluation(String key, String defaultValue, EvaluationContext ctx) { - ProviderEvaluation valueProviderEvaluation = evaluateVariant(String.class, key, new Value(defaultValue), - ctx); + ProviderEvaluation valueProviderEvaluation = + evaluateVariant(String.class, key, new Value(defaultValue), ctx); return ProviderEvaluation.builder() .value(valueProviderEvaluation.getValue().asString()) .variant(valueProviderEvaluation.getVariant()) @@ -108,8 +111,8 @@ public ProviderEvaluation getStringEvaluation(String key, String default @Override public ProviderEvaluation getIntegerEvaluation(String key, Integer defaultValue, EvaluationContext ctx) { - ProviderEvaluation valueProviderEvaluation = evaluateVariant(Integer.class, key, new Value(defaultValue), - ctx); + ProviderEvaluation valueProviderEvaluation = + evaluateVariant(Integer.class, key, new Value(defaultValue), ctx); Integer value = getIntegerValue(valueProviderEvaluation, defaultValue); return ProviderEvaluation.builder() .value(value) @@ -131,8 +134,8 @@ private static Integer getIntegerValue(ProviderEvaluation valueProviderEv @Override public ProviderEvaluation getDoubleEvaluation(String key, Double defaultValue, EvaluationContext ctx) { - ProviderEvaluation valueProviderEvaluation = evaluateVariant(Double.class, key, new Value(defaultValue), - ctx); + ProviderEvaluation valueProviderEvaluation = + evaluateVariant(Double.class, key, new Value(defaultValue), ctx); Double value = getDoubleValue(valueProviderEvaluation, defaultValue); return ProviderEvaluation.builder() .value(value) @@ -157,12 +160,16 @@ public ProviderEvaluation getObjectEvaluation(String key, Value defaultVa return evaluateVariant(Value.class, key, defaultValue, ctx); } - private ProviderEvaluation evaluateVariant(Class clazz, String key, Value defaultValue, - EvaluationContext ctx) { + private ProviderEvaluation evaluateVariant( + Class clazz, String key, Value defaultValue, EvaluationContext ctx) { Map contextMap = ContextTransformer.transform(ctx); - EvaluationRequest request = EvaluationRequest.builder().namespaceKey(fliptProviderConfig.getNamespace()) - .flagKey(key).entityId(ctx.getTargetingKey()).context(contextMap).build(); + EvaluationRequest request = EvaluationRequest.builder() + .namespaceKey(fliptProviderConfig.getNamespace()) + .flagKey(key) + .entityId(ctx.getTargetingKey()) + .context(contextMap) + .build(); VariantEvaluationResponse response; try { @@ -182,7 +189,8 @@ private ProviderEvaluation evaluateVariant(Class clazz, String key Value value = new Value(response.getVariantKey()); ImmutableMetadata.ImmutableMetadataBuilder flagMetadataBuilder = ImmutableMetadata.builder(); - if (response.getVariantAttachment() != null && !response.getVariantAttachment().isEmpty()) { + if (response.getVariantAttachment() != null + && !response.getVariantAttachment().isEmpty()) { flagMetadataBuilder.addString("variant-attachment", response.getVariantAttachment()); if (clazz.isAssignableFrom(Value.class)) { diff --git a/providers/flipt/src/main/java/dev/openfeature/contrib/providers/flipt/FliptProviderConfig.java b/providers/flipt/src/main/java/dev/openfeature/contrib/providers/flipt/FliptProviderConfig.java index 9d26bb3d7..779972c94 100644 --- a/providers/flipt/src/main/java/dev/openfeature/contrib/providers/flipt/FliptProviderConfig.java +++ b/providers/flipt/src/main/java/dev/openfeature/contrib/providers/flipt/FliptProviderConfig.java @@ -4,9 +4,7 @@ import lombok.Builder; import lombok.Getter; -/** - * FliptProvider config. - */ +/** FliptProvider config. */ @Getter @Builder public class FliptProviderConfig { diff --git a/providers/flipt/src/test/java/dev/openfeature/contrib/providers/flipt/FliptProviderTest.java b/providers/flipt/src/test/java/dev/openfeature/contrib/providers/flipt/FliptProviderTest.java index fb3c3da53..9a2dde268 100644 --- a/providers/flipt/src/test/java/dev/openfeature/contrib/providers/flipt/FliptProviderTest.java +++ b/providers/flipt/src/test/java/dev/openfeature/contrib/providers/flipt/FliptProviderTest.java @@ -1,42 +1,34 @@ package dev.openfeature.contrib.providers.flipt; -import io.flipt.api.FliptClient; -import io.flipt.api.FliptClient.FliptClientBuilder; +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static org.junit.jupiter.api.Assertions.*; + import com.github.tomakehurst.wiremock.client.WireMock; import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; import com.github.tomakehurst.wiremock.junit5.WireMockTest; import dev.openfeature.sdk.Client; import dev.openfeature.sdk.FlagEvaluationDetails; -import dev.openfeature.sdk.ImmutableContext; import dev.openfeature.sdk.ImmutableMetadata; import dev.openfeature.sdk.MutableContext; import dev.openfeature.sdk.OpenFeatureAPI; import dev.openfeature.sdk.ProviderEvaluation; -import dev.openfeature.sdk.ProviderEventDetails; -import dev.openfeature.sdk.ProviderState; import dev.openfeature.sdk.Value; -import dev.openfeature.sdk.exceptions.GeneralError; -import dev.openfeature.sdk.exceptions.ProviderNotReadyError; +import io.flipt.api.FliptClient; +import io.flipt.api.FliptClient.FliptClientBuilder; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Paths; import lombok.SneakyThrows; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Paths; - -import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; -import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; -import static com.github.tomakehurst.wiremock.client.WireMock.post; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static org.junit.jupiter.api.Assertions.*; - -/** - * FliptProvider test, based on APIs mocking. - */ +/** FliptProvider test, based on APIs mocking. */ @WireMockTest @TestInstance(TestInstance.Lifecycle.PER_CLASS) class FliptProviderTest { @@ -70,15 +62,13 @@ public void shutdown() { } private void mockFliptAPI(String url, String resourceName, String flagKey) { - stubFor( - post(urlEqualTo(url)) - .withHeader("Content-Type", equalTo("application/json; charset=UTF-8")) - .withRequestBody(WireMock.containing(flagKey)) - .willReturn( - aResponse() - .withStatus(200) - .withHeader("Content-Type", "application/json; charset=UTF-8") - .withBody(readResourceFileContent(resourceName)))); + stubFor(post(urlEqualTo(url)) + .withHeader("Content-Type", equalTo("application/json; charset=UTF-8")) + .withRequestBody(WireMock.containing(flagKey)) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json; charset=UTF-8") + .withBody(readResourceFileContent(resourceName)))); } @SneakyThrows @@ -111,8 +101,11 @@ void getStringVariantEvaluation() { mockFliptAPI("/evaluate/v1/variant", "variant.json", VARIANT_FLAG_NAME); MutableContext evaluationContext = new MutableContext(); evaluationContext.setTargetingKey(TARGETING_KEY); - assertEquals(VARIANT_FLAG_VALUE, fliptProvider.getStringEvaluation(VARIANT_FLAG_NAME, "", - evaluationContext).getValue()); + assertEquals( + VARIANT_FLAG_VALUE, + fliptProvider + .getStringEvaluation(VARIANT_FLAG_NAME, "", evaluationContext) + .getValue()); assertEquals(VARIANT_FLAG_VALUE, client.getStringValue(VARIANT_FLAG_NAME, "", evaluationContext)); assertEquals("fallback_str", client.getStringValue("non-existing", "fallback_str", evaluationContext)); } @@ -123,8 +116,11 @@ void getIntegerEvaluation() { MutableContext evaluationContext = new MutableContext(); evaluationContext.setTargetingKey(TARGETING_KEY); evaluationContext.add("userId", "int"); - assertEquals(INT_FLAG_VALUE, fliptProvider.getIntegerEvaluation(INT_FLAG_NAME, 1, - evaluationContext).getValue()); + assertEquals( + INT_FLAG_VALUE, + fliptProvider + .getIntegerEvaluation(INT_FLAG_NAME, 1, evaluationContext) + .getValue()); assertEquals(INT_FLAG_VALUE, client.getIntegerValue(INT_FLAG_NAME, 1, evaluationContext)); assertEquals(1, client.getIntegerValue("non-existing", 1, evaluationContext)); @@ -138,8 +134,11 @@ void getDoubleEvaluation() { MutableContext evaluationContext = new MutableContext(); evaluationContext.setTargetingKey(TARGETING_KEY); evaluationContext.add("userId", "double"); - assertEquals(DOUBLE_FLAG_VALUE, fliptProvider.getDoubleEvaluation(DOUBLE_FLAG_NAME, 1.1, - evaluationContext).getValue()); + assertEquals( + DOUBLE_FLAG_VALUE, + fliptProvider + .getDoubleEvaluation(DOUBLE_FLAG_NAME, 1.1, evaluationContext) + .getValue()); assertEquals(DOUBLE_FLAG_VALUE, client.getDoubleValue(DOUBLE_FLAG_NAME, 1.1, evaluationContext)); assertEquals(1.1, client.getDoubleValue("non-existing", 1.1, evaluationContext)); @@ -153,8 +152,11 @@ void getStringEvaluationByUser() { MutableContext evaluationContext = new MutableContext(); evaluationContext.setTargetingKey(TARGETING_KEY); evaluationContext.add("userId", "111"); - assertEquals(VARIANT_FLAG_VALUE, - fliptProvider.getStringEvaluation(USERS_FLAG_NAME, "", evaluationContext).getValue()); + assertEquals( + VARIANT_FLAG_VALUE, + fliptProvider + .getStringEvaluation(USERS_FLAG_NAME, "", evaluationContext) + .getValue()); assertEquals(VARIANT_FLAG_VALUE, client.getStringValue(USERS_FLAG_NAME, "", evaluationContext)); evaluationContext.add("userId", "2"); assertEquals("", client.getStringValue(USERS_FLAG_NAME, "", evaluationContext)); @@ -165,12 +167,12 @@ void getEvaluationMetadataTest() { mockFliptAPI("/evaluate/v1/variant", "variant.json", VARIANT_FLAG_NAME); MutableContext evaluationContext = new MutableContext(); evaluationContext.setTargetingKey(TARGETING_KEY); - ProviderEvaluation stringEvaluation = fliptProvider.getStringEvaluation(VARIANT_FLAG_NAME, "", - evaluationContext); + ProviderEvaluation stringEvaluation = + fliptProvider.getStringEvaluation(VARIANT_FLAG_NAME, "", evaluationContext); ImmutableMetadata flagMetadata = stringEvaluation.getFlagMetadata(); assertEquals("attachment-1", flagMetadata.getString("variant-attachment")); - FlagEvaluationDetails nonExistingFlagEvaluation = client.getStringDetails("non-existing", "", - evaluationContext); + FlagEvaluationDetails nonExistingFlagEvaluation = + client.getStringDetails("non-existing", "", evaluationContext); assertNull(nonExistingFlagEvaluation.getFlagMetadata().getBoolean("variant-attachment")); } @@ -191,4 +193,4 @@ void getObjectEvaluationTest() { // non-object flag value assertEquals(emptyValue, client.getObjectValue(VARIANT_FLAG_NAME, emptyValue, evaluationContext)); } -} \ No newline at end of file +} diff --git a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/GoFeatureFlagProvider.java b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/GoFeatureFlagProvider.java index 6a4704c20..bb18ff3a5 100644 --- a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/GoFeatureFlagProvider.java +++ b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/GoFeatureFlagProvider.java @@ -1,13 +1,6 @@ package dev.openfeature.contrib.providers.gofeatureflag; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import org.jetbrains.annotations.NotNull; - import com.fasterxml.jackson.core.JsonProcessingException; - import dev.openfeature.contrib.providers.gofeatureflag.bean.ConfigurationChange; import dev.openfeature.contrib.providers.gofeatureflag.controller.CacheController; import dev.openfeature.contrib.providers.gofeatureflag.controller.GoFeatureFlagController; @@ -30,10 +23,15 @@ import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.subjects.PublishSubject; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; /** - * GoFeatureFlagProvider is the JAVA provider implementation for the feature flag solution GO Feature Flag. + * GoFeatureFlagProvider is the JAVA provider implementation for the feature flag solution GO + * Feature Flag. */ @Slf4j @SuppressWarnings({"checkstyle:NoFinalizer"}) @@ -73,43 +71,39 @@ public List getProviderHooks() { @Override public ProviderEvaluation getBooleanEvaluation( - String key, Boolean defaultValue, EvaluationContext evaluationContext - ) { + String key, Boolean defaultValue, EvaluationContext evaluationContext) { return getEvaluation(key, defaultValue, evaluationContext, Boolean.class); } @Override public ProviderEvaluation getStringEvaluation( - String key, String defaultValue, EvaluationContext evaluationContext - ) { + String key, String defaultValue, EvaluationContext evaluationContext) { return getEvaluation(key, defaultValue, evaluationContext, String.class); } @Override public ProviderEvaluation getIntegerEvaluation( - String key, Integer defaultValue, EvaluationContext evaluationContext - ) { + String key, Integer defaultValue, EvaluationContext evaluationContext) { return getEvaluation(key, defaultValue, evaluationContext, Integer.class); } @Override public ProviderEvaluation getDoubleEvaluation( - String key, Double defaultValue, EvaluationContext evaluationContext - ) { + String key, Double defaultValue, EvaluationContext evaluationContext) { return getEvaluation(key, defaultValue, evaluationContext, Double.class); } @Override public ProviderEvaluation getObjectEvaluation( - String key, Value defaultValue, EvaluationContext evaluationContext - ) { + String key, Value defaultValue, EvaluationContext evaluationContext) { return getEvaluation(key, defaultValue, evaluationContext, Value.class); } @Override public void initialize(EvaluationContext evaluationContext) throws Exception { super.initialize(evaluationContext); - this.gofeatureflagController = GoFeatureFlagController.builder().options(options).build(); + this.gofeatureflagController = + GoFeatureFlagController.builder().options(options).build(); if (options.getEnableCache() == null || options.getEnableCache()) { this.cacheCtrl = CacheController.builder().options(options).build(); @@ -122,23 +116,24 @@ public void initialize(EvaluationContext evaluationContext) throws Exception { .build()); this.hooks.add(this.dataCollectorHook); } - this.flagChangeDisposable = - this.startCheckFlagConfigurationChangesDaemon(); + this.flagChangeDisposable = this.startCheckFlagConfigurationChangesDaemon(); } - super.emitProviderReady(ProviderEventDetails.builder().message("Provider is ready to call the API").build()); + super.emitProviderReady(ProviderEventDetails.builder() + .message("Provider is ready to call the API") + .build()); log.info("finishing initializing provider"); } - /** - * startCheckFlagConfigurationChangesDaemon is a daemon that will check if the flag configuration has changed. + * startCheckFlagConfigurationChangesDaemon is a daemon that will check if the flag configuration + * has changed. * * @return Disposable - the subscription to the observable */ - @NotNull - private Disposable startCheckFlagConfigurationChangesDaemon() { + @NotNull private Disposable startCheckFlagConfigurationChangesDaemon() { long pollingIntervalMs = options.getFlagChangePollingIntervalMs() != null - ? options.getFlagChangePollingIntervalMs() : DEFAULT_POLLING_CONFIG_FLAG_CHANGE_INTERVAL_MS; + ? options.getFlagChangePollingIntervalMs() + : DEFAULT_POLLING_CONFIG_FLAG_CHANGE_INTERVAL_MS; PublishSubject stopSignal = PublishSubject.create(); Observable intervalObservable = Observable.interval(pollingIntervalMs, TimeUnit.MILLISECONDS); @@ -156,31 +151,30 @@ private Disposable startCheckFlagConfigurationChangesDaemon() { })) .subscribeOn(Schedulers.io()); - return apiCallObservable - .subscribe( - response -> { - if (response == ConfigurationChange.FLAG_CONFIGURATION_UPDATED) { - log.info("clean up the cache because the flag configuration has changed"); - this.cacheCtrl.invalidateAll(); - super.emitProviderConfigurationChanged(ProviderEventDetails.builder() - .message("GO Feature Flag Configuration changed, clearing the cache").build()); - } else { - log.debug("flag configuration has not changed: {}", response); - } - }, - throwable -> log.error("error while calling flag change API, error: {}", throwable.getMessage()) - ); + return apiCallObservable.subscribe( + response -> { + if (response == ConfigurationChange.FLAG_CONFIGURATION_UPDATED) { + log.info("clean up the cache because the flag configuration has changed"); + this.cacheCtrl.invalidateAll(); + super.emitProviderConfigurationChanged(ProviderEventDetails.builder() + .message("GO Feature Flag Configuration changed, clearing the cache") + .build()); + } else { + log.debug("flag configuration has not changed: {}", response); + } + }, + throwable -> log.error("error while calling flag change API, error: {}", throwable.getMessage())); } /** - * getEvaluation is the function resolving the flag, it will 1st check in the cache and if it is not available - * will call the evaluation endpoint to get the value of the flag. + * getEvaluation is the function resolving the flag, it will 1st check in the cache and if it is + * not available will call the evaluation endpoint to get the value of the flag. * - * @param key - name of the feature flag - * @param defaultValue - value used if something is not working as expected + * @param key - name of the feature flag + * @param defaultValue - value used if something is not working as expected * @param evaluationContext - EvaluationContext used for the request - * @param expectedType - type expected for the value - * @param the type of your evaluation + * @param expectedType - type expected for the value + * @param the type of your evaluation * @return a ProviderEvaluation that contains the open-feature response */ @SuppressWarnings("unchecked") @@ -195,8 +189,8 @@ private ProviderEvaluation getEvaluation( ProviderEvaluation cachedProviderEvaluation = this.cacheCtrl.getIfPresent(key, evaluationContext); if (cachedProviderEvaluation == null) { - EvaluationResponse proxyRes = this.gofeatureflagController.evaluateFlag( - key, defaultValue, evaluationContext, expectedType); + EvaluationResponse proxyRes = + this.gofeatureflagController.evaluateFlag(key, defaultValue, evaluationContext, expectedType); if (Boolean.TRUE.equals(proxyRes.getCacheable())) { this.cacheCtrl.put(key, evaluationContext, proxyRes.getProviderEvaluation()); @@ -205,7 +199,8 @@ private ProviderEvaluation getEvaluation( } cachedProviderEvaluation.setReason(CACHED_REASON); if (cachedProviderEvaluation.getValue().getClass() != expectedType) { - throw new InvalidTypeInCache(expectedType, cachedProviderEvaluation.getValue().getClass()); + throw new InvalidTypeInCache( + expectedType, cachedProviderEvaluation.getValue().getClass()); } return (ProviderEvaluation) cachedProviderEvaluation; } catch (JsonProcessingException e) { @@ -239,7 +234,7 @@ public void shutdown() { * validateInputOptions is validating the different options provided when creating the provider. * * @param options - Options used while creating the provider - * @throws InvalidOptions - if no options are provided + * @throws InvalidOptions - if no options are provided * @throws InvalidEndpoint - if the endpoint provided is not valid */ private void validateInputOptions(GoFeatureFlagProviderOptions options) throws InvalidOptions { diff --git a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/GoFeatureFlagProviderOptions.java b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/GoFeatureFlagProviderOptions.java index 552713038..4e77cb7f5 100644 --- a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/GoFeatureFlagProviderOptions.java +++ b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/GoFeatureFlagProviderOptions.java @@ -1,32 +1,27 @@ package dev.openfeature.contrib.providers.gofeatureflag; import com.github.benmanes.caffeine.cache.Caffeine; - import dev.openfeature.sdk.ProviderEvaluation; import lombok.Builder; import lombok.Getter; -/** - * GoFeatureFlagProviderOptions contains the options to initialise the provider. - */ +/** GoFeatureFlagProviderOptions contains the options to initialise the provider. */ @Builder @Getter public class GoFeatureFlagProviderOptions { /** - * (mandatory) endpoint contains the DNS of your GO Feature Flag relay proxy - * example: https://mydomain.com/gofeatureflagproxy/ + * (mandatory) endpoint contains the DNS of your GO Feature Flag relay proxy example: + * https://mydomain.com/gofeatureflagproxy/ */ private String endpoint; /** - * (optional) timeout in millisecond we are waiting when calling the - * go-feature-flag relay proxy API. - * Default: 10000 ms + * (optional) timeout in millisecond we are waiting when calling the go-feature-flag relay proxy + * API. Default: 10000 ms */ private int timeout; - /** * (optional) maxIdleConnections is the maximum number of connexions in the connexion pool. * Default: 1000 @@ -34,24 +29,23 @@ public class GoFeatureFlagProviderOptions { private int maxIdleConnections; /** - * (optional) keepAliveDuration is the time in millisecond we keep the connexion open. - * Default: 7200000 (2 hours) + * (optional) keepAliveDuration is the time in millisecond we keep the connexion open. Default: + * 7200000 (2 hours) */ private Long keepAliveDuration; /** - * (optional) If the relay proxy is configured to authenticate the requests, you should provide - * an API Key to the provider. - * Please ask the administrator of the relay proxy to provide an API Key. + * (optional) If the relay proxy is configured to authenticate the requests, you should provide an + * API Key to the provider. Please ask the administrator of the relay proxy to provide an API Key. * (This feature is available only if you are using GO Feature Flag relay proxy v1.7.0 or above) * Default: null */ private String apiKey; /** - * (optional) If cache custom configuration is wanted, you should provide - * a cache configuration caffeine object. - * Example: + * (optional) If cache custom configuration is wanted, you should provide a cache configuration + * caffeine object. Example: + * *
      * GoFeatureFlagProviderOptions.builder()
      *   .caffeineConfig(
@@ -64,46 +58,36 @@ public class GoFeatureFlagProviderOptions {
      *   .build();
      * 
      * 
- * Default: - * CACHE_TTL_MS: 5min - * CACHE_INITIAL_CAPACITY: 100 - * CACHE_MAXIMUM_SIZE: 100000 + * Default: CACHE_TTL_MS: 5min CACHE_INITIAL_CAPACITY: 100 CACHE_MAXIMUM_SIZE: 100000 */ private Caffeine> cacheConfig; - /** - * (optional) enable cache value. - * Default: true - */ + /** (optional) enable cache value. Default: true */ private Boolean enableCache; /** - * (optional) interval time we publish statistics collection data to the proxy. - * The parameter is used only if the cache is enabled, otherwise the collection of the data is done directly - * when calling the evaluation API. - * default: 1000 ms + * (optional) interval time we publish statistics collection data to the proxy. The parameter is + * used only if the cache is enabled, otherwise the collection of the data is done directly when + * calling the evaluation API. default: 1000 ms */ private Long flushIntervalMs; /** * (optional) max pending events aggregated before publishing for collection data to the proxy. - * When an event is added while an events collection is full, the event is omitted. - * default: 10000 + * When an event is added while an events collection is full, the event is omitted. default: 10000 */ private Integer maxPendingEvents; /** - * (optional) interval time we poll the proxy to check if the configuration has changed. - * If the cache is enabled, we will poll the relay-proxy every X milliseconds - * to check if the configuration has changed. - * default: 120000 + * (optional) interval time we poll the proxy to check if the configuration has changed. If the + * cache is enabled, we will poll the relay-proxy every X milliseconds to check if the + * configuration has changed. default: 120000 */ private Long flagChangePollingIntervalMs; /** - * (optional) disableDataCollection set to true if you don't want to collect the usage of - * flags retrieved in the cache. - * default: false + * (optional) disableDataCollection set to true if you don't want to collect the usage of flags + * retrieved in the cache. default: false */ private boolean disableDataCollection; } diff --git a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/bean/BeanUtils.java b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/bean/BeanUtils.java index e405bd3b2..f69d2d608 100644 --- a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/bean/BeanUtils.java +++ b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/bean/BeanUtils.java @@ -6,9 +6,7 @@ import lombok.AccessLevel; import lombok.NoArgsConstructor; -/** - * Bean utils. - */ +/** Bean utils. */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class BeanUtils { diff --git a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/bean/ConfigurationChange.java b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/bean/ConfigurationChange.java index ec39208a5..0b77ea63a 100644 --- a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/bean/ConfigurationChange.java +++ b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/bean/ConfigurationChange.java @@ -1,8 +1,6 @@ package dev.openfeature.contrib.providers.gofeatureflag.bean; -/** - * ConfigurationChange is an enum to represent the change of the configuration. - */ +/** ConfigurationChange is an enum to represent the change of the configuration. */ public enum ConfigurationChange { FLAG_CONFIGURATION_INITIALIZED, FLAG_CONFIGURATION_UPDATED, diff --git a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/bean/GoFeatureFlagResponse.java b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/bean/GoFeatureFlagResponse.java index 0b55e04f1..79e2c99b5 100644 --- a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/bean/GoFeatureFlagResponse.java +++ b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/bean/GoFeatureFlagResponse.java @@ -1,12 +1,9 @@ package dev.openfeature.contrib.providers.gofeatureflag.bean; -import lombok.Data; - import java.util.Map; +import lombok.Data; -/** - * GoFeatureFlagResponse is the response returned by the relay proxy. - */ +/** GoFeatureFlagResponse is the response returned by the relay proxy. */ @Data public class GoFeatureFlagResponse { private boolean trackEvents; @@ -19,5 +16,3 @@ public class GoFeatureFlagResponse { private Boolean cacheable; private Map metadata; } - - diff --git a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/bean/GoFeatureFlagUser.java b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/bean/GoFeatureFlagUser.java index 4d106830e..e8ce69945 100644 --- a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/bean/GoFeatureFlagUser.java +++ b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/bean/GoFeatureFlagUser.java @@ -3,15 +3,12 @@ import dev.openfeature.sdk.EvaluationContext; import dev.openfeature.sdk.Value; import dev.openfeature.sdk.exceptions.TargetingKeyMissingError; -import lombok.Builder; -import lombok.Getter; - import java.util.HashMap; import java.util.Map; +import lombok.Builder; +import lombok.Getter; -/** - * GoFeatureFlagUser is the representation of a user for GO Feature Flag. - */ +/** GoFeatureFlagUser is the representation of a user for GO Feature Flag. */ @Builder @Getter public class GoFeatureFlagUser { @@ -36,7 +33,11 @@ public static GoFeatureFlagUser fromEvaluationContext(EvaluationContext ctx) { if (ctx.getValue(anonymousFieldName) != null) { custom.remove(anonymousFieldName); } - return GoFeatureFlagUser.builder().anonymous(anonymous).key(key).custom(custom).build(); + return GoFeatureFlagUser.builder() + .anonymous(anonymous) + .key(key) + .custom(custom) + .build(); } /** diff --git a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/controller/CacheController.java b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/controller/CacheController.java index 450e69517..6f5611a4a 100644 --- a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/controller/CacheController.java +++ b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/controller/CacheController.java @@ -1,20 +1,16 @@ package dev.openfeature.contrib.providers.gofeatureflag.controller; -import java.time.Duration; - import com.fasterxml.jackson.core.JsonProcessingException; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; - import dev.openfeature.contrib.providers.gofeatureflag.GoFeatureFlagProviderOptions; import dev.openfeature.contrib.providers.gofeatureflag.bean.BeanUtils; import dev.openfeature.sdk.EvaluationContext; import dev.openfeature.sdk.ProviderEvaluation; +import java.time.Duration; import lombok.Builder; -/** - * CacheController is a controller to manage the cache of the provider. - */ +/** CacheController is a controller to manage the cache of the provider. */ public class CacheController { public static final long DEFAULT_CACHE_TTL_MS = 5L * 60L * 1000L; public static final int DEFAULT_CACHE_INITIAL_CAPACITY = 100; @@ -34,8 +30,9 @@ private Cache> buildDefaultCache() { .build(); } - public void put(final String key, final EvaluationContext evaluationContext, - final ProviderEvaluation providerEvaluation) throws JsonProcessingException { + public void put( + final String key, final EvaluationContext evaluationContext, final ProviderEvaluation providerEvaluation) + throws JsonProcessingException { this.cache.put(buildCacheKey(key, evaluationContext), providerEvaluation); } diff --git a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/controller/GoFeatureFlagController.java b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/controller/GoFeatureFlagController.java index 648af2d69..184706e9d 100644 --- a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/controller/GoFeatureFlagController.java +++ b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/controller/GoFeatureFlagController.java @@ -1,5 +1,7 @@ package dev.openfeature.contrib.providers.gofeatureflag.controller; +import static dev.openfeature.sdk.Value.objectToValue; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; @@ -27,6 +29,10 @@ import dev.openfeature.sdk.exceptions.GeneralError; import dev.openfeature.sdk.exceptions.OpenFeatureError; import dev.openfeature.sdk.exceptions.TypeMismatchError; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.util.List; +import java.util.concurrent.TimeUnit; import lombok.Builder; import lombok.extern.slf4j.Slf4j; import okhttp3.ConnectionPool; @@ -38,25 +44,17 @@ import okhttp3.Response; import okhttp3.ResponseBody; -import java.io.IOException; -import java.net.HttpURLConnection; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static dev.openfeature.sdk.Value.objectToValue; - - /** - * GoFeatureFlagController is the layer to contact the APIs and get the data - * from the GoFeatureFlagProvider. + * GoFeatureFlagController is the layer to contact the APIs and get the data from the + * GoFeatureFlagProvider. */ @Slf4j @SuppressWarnings({"checkstyle:NoFinalizer"}) public class GoFeatureFlagController { public static final String APPLICATION_JSON = "application/json"; public static final ObjectMapper requestMapper = new ObjectMapper(); - private static final ObjectMapper responseMapper = new ObjectMapper() - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + private static final ObjectMapper responseMapper = + new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); private static final String BEARER_TOKEN = "Bearer "; private static final String HTTP_HEADER_CONTENT_TYPE = "Content-Type"; @@ -64,24 +62,22 @@ public class GoFeatureFlagController { private static final String HTTP_HEADER_ETAG = "ETag"; private static final String HTTP_HEADER_IF_NONE_MATCH = "If-None-Match"; - /** - * apiKey contains the token to use while calling GO Feature Flag relay proxy. - */ + /** apiKey contains the token to use while calling GO Feature Flag relay proxy. */ private final String apiKey; - /** - * httpClient is the instance of the OkHttpClient used by the provider. - */ + /** httpClient is the instance of the OkHttpClient used by the provider. */ private final OkHttpClient httpClient; + private final HttpUrl parsedEndpoint; /** - * etag contains the etag of the configuration, if null, it means that the configuration has never been retrieved. + * etag contains the etag of the configuration, if null, it means that the configuration has never + * been retrieved. */ private String etag; - /** - * GoFeatureFlagController is the constructor of the controller to contact the GO Feature Flag relay proxy. + * GoFeatureFlagController is the constructor of the controller to contact the GO Feature Flag + * relay proxy. * * @param options - options to initialise the controller * @throws InvalidOptions - if the options are invalid @@ -117,22 +113,23 @@ private GoFeatureFlagController(final GoFeatureFlagProviderOptions options) thro /** * evaluateFlag is calling the GO Feature Flag relay proxy to get the evaluation of a flag. * - * @param key - name of the flag - * @param defaultValue - default value + * @param key - name of the flag + * @param defaultValue - default value * @param evaluationContext - context of the evaluation - * @param expectedType - expected type of the flag - * @param - type of the flag + * @param expectedType - expected type of the flag + * @param - type of the flag * @return EvaluationResponse with the evaluation of the flag * @throws OpenFeatureError - if an error occurred while evaluating the flag */ public EvaluationResponse evaluateFlag( - String key, T defaultValue, EvaluationContext evaluationContext, Class expectedType - ) throws OpenFeatureError { + String key, T defaultValue, EvaluationContext evaluationContext, Class expectedType) + throws OpenFeatureError { try { GoFeatureFlagUser user = GoFeatureFlagUser.fromEvaluationContext(evaluationContext); GoFeatureFlagRequest goffRequest = new GoFeatureFlagRequest<>(user, defaultValue); - HttpUrl url = this.parsedEndpoint.newBuilder() + HttpUrl url = this.parsedEndpoint + .newBuilder() .addEncodedPathSegment("v1") .addEncodedPathSegment("feature") .addEncodedPathSegment(key) @@ -160,19 +157,22 @@ public EvaluationResponse evaluateFlag( ResponseBody responseBody = response.body(); String body = responseBody != null ? responseBody.string() : ""; - GoFeatureFlagResponse goffResp = - responseMapper.readValue(body, GoFeatureFlagResponse.class); + GoFeatureFlagResponse goffResp = responseMapper.readValue(body, GoFeatureFlagResponse.class); if (Reason.DISABLED.name().equalsIgnoreCase(goffResp.getReason())) { - // we don't set a variant since we are using the default value, and we are not able to know + // we don't set a variant since we are using the default value, and we are not able to + // know // which variant it is. ProviderEvaluation providerEvaluation = ProviderEvaluation.builder() .value(defaultValue) .variant(goffResp.getVariationType()) - .reason(Reason.DISABLED.name()).build(); + .reason(Reason.DISABLED.name()) + .build(); return EvaluationResponse.builder() - .providerEvaluation(providerEvaluation).cacheable(goffResp.getCacheable()).build(); + .providerEvaluation(providerEvaluation) + .cacheable(goffResp.getCacheable()) + .build(); } if (ErrorCode.FLAG_NOT_FOUND.name().equalsIgnoreCase(goffResp.getErrorCode())) { @@ -183,8 +183,13 @@ public EvaluationResponse evaluateFlag( T flagValue = convertValue(goffResp.getValue(), expectedType); if (flagValue.getClass() != expectedType) { - throw new TypeMismatchError("Flag value " + key + " had unexpected type " - + flagValue.getClass() + ", expected " + expectedType + "."); + throw new TypeMismatchError("Flag value " + + key + + " had unexpected type " + + flagValue.getClass() + + ", expected " + + expectedType + + "."); } ProviderEvaluation providerEvaluation = ProviderEvaluation.builder() @@ -196,23 +201,26 @@ public EvaluationResponse evaluateFlag( .build(); return EvaluationResponse.builder() - .providerEvaluation(providerEvaluation).cacheable(goffResp.getCacheable()).build(); + .providerEvaluation(providerEvaluation) + .cacheable(goffResp.getCacheable()) + .build(); } } catch (IOException e) { throw new GeneralError("unknown error while retrieving flag " + key, e); } } - /** - * sendEventToDataCollector is calling the GO Feature Flag data/collector api to store the flag usage for analytics. + * sendEventToDataCollector is calling the GO Feature Flag data/collector api to store the flag + * usage for analytics. * * @param eventsList - list of the event to send to GO Feature Flag */ public void sendEventToDataCollector(List eventsList) { try { Events events = new Events(eventsList); - HttpUrl url = this.parsedEndpoint.newBuilder() + HttpUrl url = this.parsedEndpoint + .newBuilder() .addEncodedPathSegment("v1") .addEncodedPathSegment("data") .addEncodedPathSegment("collector") @@ -222,8 +230,7 @@ public void sendEventToDataCollector(List eventsList) { .url(url) .addHeader(HTTP_HEADER_CONTENT_TYPE, APPLICATION_JSON) .post(RequestBody.create( - requestMapper.writeValueAsBytes(events), - MediaType.get("application/json; charset=utf-8"))); + requestMapper.writeValueAsBytes(events), MediaType.get("application/json; charset=utf-8"))); if (this.apiKey != null && !this.apiKey.isEmpty()) { reqBuilder.addHeader(HTTP_HEADER_AUTHORIZATION, BEARER_TOKEN + this.apiKey); @@ -255,7 +262,8 @@ public void sendEventToDataCollector(List eventsList) { * @throws GoFeatureFlagException if an error occurred while retrieving the ETAG */ public ConfigurationChange configurationHasChanged() throws GoFeatureFlagException { - HttpUrl url = this.parsedEndpoint.newBuilder() + HttpUrl url = this.parsedEndpoint + .newBuilder() .addEncodedPathSegment("v1") .addEncodedPathSegment("flag") .addEncodedPathSegment("change") @@ -297,7 +305,8 @@ public ConfigurationChange configurationHasChanged() throws GoFeatureFlagExcepti } /** - * mapErrorCode is mapping the errorCode in string received by the API to our internal SDK ErrorCode enum. + * mapErrorCode is mapping the errorCode in string received by the API to our internal SDK + * ErrorCode enum. * * @param errorCode - string of the errorCode received from the API * @return an item from the enum @@ -306,7 +315,7 @@ private ErrorCode mapErrorCode(String errorCode) { if (errorCode == null || errorCode.isEmpty()) { return null; } - + try { return ErrorCode.valueOf(errorCode); } catch (IllegalArgumentException e) { @@ -317,9 +326,9 @@ private ErrorCode mapErrorCode(String errorCode) { /** * convertValue is converting the object return by the proxy response in the right type. * - * @param value - The value we have received + * @param value - The value we have received * @param expectedType - the type we expect for this value - * @param the type we want to convert to. + * @param the type we want to convert to. * @return A converted object */ private T convertValue(Object value, Class expectedType) { diff --git a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/exception/ConfigurationChangeEndpointNotFound.java b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/exception/ConfigurationChangeEndpointNotFound.java index 7bbb05e91..ab6ea03dc 100644 --- a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/exception/ConfigurationChangeEndpointNotFound.java +++ b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/exception/ConfigurationChangeEndpointNotFound.java @@ -2,9 +2,6 @@ import lombok.experimental.StandardException; -/** - * InvalidEndpoint is thrown when we don't have any endpoint in the configuration. - */ +/** InvalidEndpoint is thrown when we don't have any endpoint in the configuration. */ @StandardException -public class ConfigurationChangeEndpointNotFound extends GoFeatureFlagException { -} +public class ConfigurationChangeEndpointNotFound extends GoFeatureFlagException {} diff --git a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/exception/ConfigurationChangeEndpointUnknownErr.java b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/exception/ConfigurationChangeEndpointUnknownErr.java index 10590ad94..34f78f702 100644 --- a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/exception/ConfigurationChangeEndpointUnknownErr.java +++ b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/exception/ConfigurationChangeEndpointUnknownErr.java @@ -2,9 +2,6 @@ import lombok.experimental.StandardException; -/** - * InvalidEndpoint is thrown when we don't have any endpoint in the configuration. - */ +/** InvalidEndpoint is thrown when we don't have any endpoint in the configuration. */ @StandardException -public class ConfigurationChangeEndpointUnknownErr extends GoFeatureFlagException { -} +public class ConfigurationChangeEndpointUnknownErr extends GoFeatureFlagException {} diff --git a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/exception/GoFeatureFlagException.java b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/exception/GoFeatureFlagException.java index 272914a7b..e65b4db72 100644 --- a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/exception/GoFeatureFlagException.java +++ b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/exception/GoFeatureFlagException.java @@ -2,9 +2,6 @@ import lombok.experimental.StandardException; -/** - * GoFeatureFlagException is the main exception for the provider. - */ +/** GoFeatureFlagException is the main exception for the provider. */ @StandardException -public class GoFeatureFlagException extends Exception { -} +public class GoFeatureFlagException extends Exception {} diff --git a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/exception/InvalidEndpoint.java b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/exception/InvalidEndpoint.java index e1762929d..e38ad9b0c 100644 --- a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/exception/InvalidEndpoint.java +++ b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/exception/InvalidEndpoint.java @@ -2,9 +2,6 @@ import lombok.experimental.StandardException; -/** - * InvalidEndpoint is thrown when we don't have any endpoint in the configuration. - */ +/** InvalidEndpoint is thrown when we don't have any endpoint in the configuration. */ @StandardException -public class InvalidEndpoint extends InvalidOptions { -} +public class InvalidEndpoint extends InvalidOptions {} diff --git a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/exception/InvalidOptions.java b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/exception/InvalidOptions.java index 432d5a8cc..b07d77042 100644 --- a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/exception/InvalidOptions.java +++ b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/exception/InvalidOptions.java @@ -2,9 +2,6 @@ import lombok.experimental.StandardException; -/** - * InvalidOptions is the super Exception used when we have a configuration exception. - */ +/** InvalidOptions is the super Exception used when we have a configuration exception. */ @StandardException -public class InvalidOptions extends GoFeatureFlagException { -} +public class InvalidOptions extends GoFeatureFlagException {} diff --git a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/exception/InvalidTypeInCache.java b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/exception/InvalidTypeInCache.java index 6dde787d2..9bccbdc97 100644 --- a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/exception/InvalidTypeInCache.java +++ b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/exception/InvalidTypeInCache.java @@ -5,7 +5,7 @@ */ public class InvalidTypeInCache extends GoFeatureFlagException { public InvalidTypeInCache(Class expected, Class got) { - super("cache value is not from the expected type, we try a remote evaluation," - + " expected: " + expected + ", got: " + got); + super("cache value is not from the expected type, we try a remote evaluation," + " expected: " + expected + + ", got: " + got); } } diff --git a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/hook/DataCollectorHook.java b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/hook/DataCollectorHook.java index 181ddd3d6..f9302ac7b 100644 --- a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/hook/DataCollectorHook.java +++ b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/hook/DataCollectorHook.java @@ -8,28 +8,24 @@ import dev.openfeature.sdk.Hook; import dev.openfeature.sdk.HookContext; import dev.openfeature.sdk.Reason; -import lombok.extern.slf4j.Slf4j; - import java.time.Duration; import java.util.List; import java.util.Map; import java.util.function.Consumer; +import lombok.extern.slf4j.Slf4j; /** - * DataCollectorHook is an OpenFeature Hook in charge of sending the usage of the flag to GO Feature Flag. + * DataCollectorHook is an OpenFeature Hook in charge of sending the usage of the flag to GO Feature + * Flag. */ @Slf4j @SuppressWarnings({"checkstyle:NoFinalizer"}) public class DataCollectorHook implements Hook> { public static final long DEFAULT_FLUSH_INTERVAL_MS = Duration.ofMinutes(1).toMillis(); public static final int DEFAULT_MAX_PENDING_EVENTS = 10000; - /** - * options contains all the options of this hook. - */ + /** options contains all the options of this hook. */ private final DataCollectorHookOptions options; - /** - * eventsPublisher is the system collecting all the information to send to GO Feature Flag. - */ + /** eventsPublisher is the system collecting all the information to send to GO Feature Flag. */ private final EventsPublisher eventsPublisher; /** @@ -42,10 +38,10 @@ public DataCollectorHook(DataCollectorHookOptions options) throws InvalidOptions if (options == null) { throw new InvalidOptions("No options provided"); } - long flushIntervalMs = options.getFlushIntervalMs() == null - ? DEFAULT_FLUSH_INTERVAL_MS : options.getFlushIntervalMs(); - int maxPendingEvents = options.getMaxPendingEvents() == null - ? DEFAULT_MAX_PENDING_EVENTS : options.getMaxPendingEvents(); + long flushIntervalMs = + options.getFlushIntervalMs() == null ? DEFAULT_FLUSH_INTERVAL_MS : options.getFlushIntervalMs(); + int maxPendingEvents = + options.getMaxPendingEvents() == null ? DEFAULT_MAX_PENDING_EVENTS : options.getMaxPendingEvents(); Consumer> publisher = this::publishEvents; eventsPublisher = new EventsPublisher<>(publisher, flushIntervalMs, maxPendingEvents); this.options = options; @@ -87,7 +83,8 @@ public void error(HookContext ctx, Exception error, Map hints) { } /** - * publishEvents is calling the GO Feature Flag data/collector api to store the flag usage for analytics. + * publishEvents is calling the GO Feature Flag data/collector api to store the flag usage for + * analytics. * * @param eventsList - list of the event to send to GO Feature Flag */ @@ -95,9 +92,7 @@ private void publishEvents(List eventsList) { this.options.getGofeatureflagController().sendEventToDataCollector(eventsList); } - /** - * shutdown should be called when we stop the hook, it will publish the remaining event. - */ + /** shutdown should be called when we stop the hook, it will publish the remaining event. */ public void shutdown() { try { if (eventsPublisher != null) { diff --git a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/hook/DataCollectorHookOptions.java b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/hook/DataCollectorHookOptions.java index e81e4722b..0c709789b 100644 --- a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/hook/DataCollectorHookOptions.java +++ b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/hook/DataCollectorHookOptions.java @@ -7,35 +7,34 @@ import lombok.SneakyThrows; /** - * DataCollectorHookOptions is the object containing all the options needed for the Data Collector Hook. + * DataCollectorHookOptions is the object containing all the options needed for the Data Collector + * Hook. */ @Builder @Getter public class DataCollectorHookOptions { - /** - * GoFeatureFlagController is the controller to contact the APIs. - */ + /** GoFeatureFlagController is the controller to contact the APIs. */ private final GoFeatureFlagController gofeatureflagController; /** - * (optional) interval time we publish statistics collection data to the proxy. - * The parameter is used only if the cache is enabled, otherwise the collection of the data is done directly - * when calling the evaluation API. - * default: 1000 ms + * (optional) interval time we publish statistics collection data to the proxy. The parameter is + * used only if the cache is enabled, otherwise the collection of the data is done directly when + * calling the evaluation API. default: 1000 ms */ private Long flushIntervalMs; /** * (optional) max pending events aggregated before publishing for collection data to the proxy. - * When an event is added while events collection is full, the event is omitted. - * default: 10000 + * When an event is added while events collection is full, the event is omitted. default: 10000 */ private Integer maxPendingEvents; /** - * collectUnCachedEvent (optional) set to true if you want to send all events not only the cached evaluations. + * collectUnCachedEvent (optional) set to true if you want to send all events not only the cached + * evaluations. */ private Boolean collectUnCachedEvaluation; /** - * Override the builder() method to return our custom builder instead of the Lombok generated builder class. + * Override the builder() method to return our custom builder instead of the Lombok generated + * builder class. * * @return a custom builder with validation */ @@ -43,15 +42,10 @@ public static DataCollectorHookOptionsBuilder builder() { return new CustomBuilder(); } - /** - * used only for the javadoc not to complain. - */ - public static class DataCollectorHookOptionsBuilder { - } + /** used only for the javadoc not to complain. */ + public static class DataCollectorHookOptionsBuilder {} - /** - * CustomBuilder is ensuring the validation in the build method. - */ + /** CustomBuilder is ensuring the validation in the build method. */ private static class CustomBuilder extends DataCollectorHookOptionsBuilder { @SneakyThrows public DataCollectorHookOptions build() { diff --git a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/hook/events/Event.java b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/hook/events/Event.java index e812f6f41..efa50512d 100644 --- a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/hook/events/Event.java +++ b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/hook/events/Event.java @@ -4,9 +4,7 @@ import lombok.Builder; import lombok.Data; -/** - * Event data. - */ +/** Event data. */ @Builder @Data public class Event { diff --git a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/hook/events/Events.java b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/hook/events/Events.java index ab9b8c05f..4b123ad5c 100644 --- a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/hook/events/Events.java +++ b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/hook/events/Events.java @@ -1,15 +1,12 @@ package dev.openfeature.contrib.providers.gofeatureflag.hook.events; -import lombok.Getter; - import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import lombok.Getter; -/** - * Events data. - */ +/** Events data. */ @Getter public class Events { private static final Map meta = new HashMap<>(); diff --git a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/hook/events/EventsPublisher.java b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/hook/events/EventsPublisher.java index f14ac12d5..c6c9e60ad 100644 --- a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/hook/events/EventsPublisher.java +++ b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/hook/events/EventsPublisher.java @@ -1,9 +1,6 @@ package dev.openfeature.contrib.providers.gofeatureflag.hook.events; - import dev.openfeature.contrib.providers.gofeatureflag.util.ConcurrentUtils; -import lombok.extern.slf4j.Slf4j; - import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @@ -15,6 +12,7 @@ import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Consumer; +import lombok.extern.slf4j.Slf4j; /** * Events publisher. @@ -39,7 +37,7 @@ public class EventsPublisher { /** * Constructor. * - * @param publisher events publisher + * @param publisher events publisher * @param flushIntervalMs data flush interval */ public EventsPublisher(Consumer> publisher, long flushIntervalMs, int maxPendingEvents) { @@ -98,9 +96,7 @@ public int publish() { return publishedEvents; } - /** - * Shutdown. - */ + /** Shutdown. */ public void shutdown() { log.info("shutdown"); try { diff --git a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/util/ConcurrentUtils.java b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/util/ConcurrentUtils.java index 1f9bd80b5..bea06f4e7 100644 --- a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/util/ConcurrentUtils.java +++ b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/util/ConcurrentUtils.java @@ -1,12 +1,11 @@ package dev.openfeature.contrib.providers.gofeatureflag.util; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.TimeUnit; - /** * Concurrent / Concurrency utilities. * @@ -18,12 +17,14 @@ public class ConcurrentUtils { /** * Graceful shutdown a thread pool.
- * See + * See * https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html * - * @param pool thread pool - * @param timeoutSeconds grace period timeout in seconds - timeout can be twice than this value, as first it - * waits for existing tasks to terminate, then waits for cancelled tasks to terminate. + * @param pool thread pool + * @param timeoutSeconds grace period timeout in seconds - timeout can be twice than this value, + * as first it waits for existing tasks to terminate, then waits for cancelled tasks to + * terminate. */ public static void shutdownAndAwaitTermination(ExecutorService pool, int timeoutSeconds) { @@ -34,7 +35,8 @@ public static void shutdownAndAwaitTermination(ExecutorService pool, int timeout // Wait a while for existing tasks to terminate if (!pool.awaitTermination(timeoutSeconds, TimeUnit.SECONDS)) { - // Cancel currently executing tasks - best effort, based on interrupt handling implementation. + // Cancel currently executing tasks - best effort, based on interrupt handling + // implementation. pool.shutdownNow(); // Wait a while for tasks to respond to being cancelled @@ -53,5 +55,4 @@ public static void shutdownAndAwaitTermination(ExecutorService pool, int timeout Thread.currentThread().interrupt(); } } - } diff --git a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/util/MetadataUtil.java b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/util/MetadataUtil.java index af4cad073..965e55b9a 100644 --- a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/util/MetadataUtil.java +++ b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/util/MetadataUtil.java @@ -1,20 +1,19 @@ package dev.openfeature.contrib.providers.gofeatureflag.util; import dev.openfeature.sdk.ImmutableMetadata; +import java.util.Map; import lombok.AccessLevel; import lombok.NoArgsConstructor; -import java.util.Map; - /** - * MetadataUtil is a utility class to convert the metadata received from the server - * to an ImmutableMetadata format known by Open Feature. + * MetadataUtil is a utility class to convert the metadata received from the server to an + * ImmutableMetadata format known by Open Feature. */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class MetadataUtil { /** - * convertFlagMetadata is converting the flagMetadata object received from the server - * to an ImmutableMetadata format known by Open Feature. + * convertFlagMetadata is converting the flagMetadata object received from the server to an + * ImmutableMetadata format known by Open Feature. * * @param flagMetadata - metadata received from the server * @return a converted metadata object. diff --git a/providers/go-feature-flag/src/test/java/dev/openfeature/contrib/providers/gofeatureflag/GoFeatureFlagProviderTest.java b/providers/go-feature-flag/src/test/java/dev/openfeature/contrib/providers/gofeatureflag/GoFeatureFlagProviderTest.java index 64f2c7f07..ea086f466 100644 --- a/providers/go-feature-flag/src/test/java/dev/openfeature/contrib/providers/gofeatureflag/GoFeatureFlagProviderTest.java +++ b/providers/go-feature-flag/src/test/java/dev/openfeature/contrib/providers/gofeatureflag/GoFeatureFlagProviderTest.java @@ -5,25 +5,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; -import java.io.IOException; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; - import com.github.benmanes.caffeine.cache.Caffeine; import com.google.common.net.HttpHeaders; - import dev.openfeature.contrib.providers.gofeatureflag.exception.InvalidEndpoint; import dev.openfeature.contrib.providers.gofeatureflag.exception.InvalidOptions; import dev.openfeature.sdk.Client; @@ -36,6 +19,15 @@ import dev.openfeature.sdk.OpenFeatureAPI; import dev.openfeature.sdk.Reason; import dev.openfeature.sdk.Value; +import java.io.IOException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import okhttp3.HttpUrl; @@ -43,6 +35,11 @@ import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.RecordedRequest; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; @Slf4j class GoFeatureFlagProviderTest { @@ -52,8 +49,7 @@ class GoFeatureFlagProviderTest { // Dispatcher is the configuration of the mock server to test the provider. final Dispatcher dispatcher = new Dispatcher() { - @NotNull - @SneakyThrows + @NotNull @SneakyThrows @Override public MockResponse dispatch(RecordedRequest request) { assert request.getPath() != null; @@ -65,9 +61,7 @@ public MockResponse dispatch(RecordedRequest request) { } if (request.getPath().startsWith("/v1/feature/")) { String flagName = request.getPath().replace("/v1/feature/", "").replace("/eval", ""); - return new MockResponse() - .setResponseCode(200) - .setBody(readMockResponse(flagName + ".json")); + return new MockResponse().setResponseCode(200).setBody(readMockResponse(flagName + ".json")); } if (request.getPath().startsWith("/v1/data/collector")) { String requestBody = request.getBody().readString(StandardCharsets.UTF_8); @@ -88,7 +82,7 @@ public MockResponse dispatch(RecordedRequest request) { } if (request.getHeader(HttpHeaders.IF_NONE_MATCH) != null && (request.getHeader(HttpHeaders.IF_NONE_MATCH).equals("123456") - || request.getHeader(HttpHeaders.IF_NONE_MATCH).equals("7891011"))) { + || request.getHeader(HttpHeaders.IF_NONE_MATCH).equals("7891011"))) { return new MockResponse().setResponseCode(304); } @@ -101,11 +95,10 @@ public MockResponse dispatch(RecordedRequest request) { private HttpUrl baseUrl; private MutableContext evaluationContext; - private final static ImmutableMetadata defaultMetadata = - ImmutableMetadata.builder() - .addString("pr_link", "https://github.com/thomaspoignant/go-feature-flag/pull/916") - .addInteger("version", 1) - .build(); + private static final ImmutableMetadata defaultMetadata = ImmutableMetadata.builder() + .addString("pr_link", "https://github.com/thomaspoignant/go-feature-flag/pull/916") + .addInteger("version", 1) + .build(); private String testName; @@ -128,7 +121,8 @@ void beforeEach(TestInfo testInfo) throws IOException { this.evaluationContext.add("professional", true); this.evaluationContext.add("rate", 3.14); this.evaluationContext.add("age", 30); - this.evaluationContext.add("company_info", new MutableStructure().add("name", "my_company").add("size", 120)); + this.evaluationContext.add( + "company_info", new MutableStructure().add("name", "my_company").add("size", 120)); List labels = new ArrayList<>(); labels.add(new Value("pro")); labels.add(new Value("beta")); @@ -146,7 +140,14 @@ void afterEach() throws IOException { @SneakyThrows @Test void getMetadata_validate_name() { - assertEquals("GO Feature Flag Provider", new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()).getMetadata().getName()); + assertEquals( + "GO Feature Flag Provider", + new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder() + .endpoint(this.baseUrl.toString()) + .timeout(1000) + .build()) + .getMetadata() + .getName()); } @Test @@ -156,49 +157,72 @@ void constructor_options_null() { @Test void constructor_options_empty() { - assertThrows(InvalidOptions.class, () -> new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().build())); + assertThrows( + InvalidOptions.class, + () -> new GoFeatureFlagProvider( + GoFeatureFlagProviderOptions.builder().build())); } @SneakyThrows @Test void constructor_options_empty_endpoint() { - assertThrows(InvalidEndpoint.class, () -> new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint("").build())); + assertThrows( + InvalidEndpoint.class, + () -> new GoFeatureFlagProvider( + GoFeatureFlagProviderOptions.builder().endpoint("").build())); } @SneakyThrows @Test void constructor_options_only_timeout() { - assertThrows(InvalidEndpoint.class, () -> new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().timeout(10000).build())); + assertThrows( + InvalidEndpoint.class, + () -> new GoFeatureFlagProvider( + GoFeatureFlagProviderOptions.builder().timeout(10000).build())); } @SneakyThrows @Test void constructor_options_valid_endpoint() { - assertDoesNotThrow(() -> new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint("http://localhost:1031").build())); + assertDoesNotThrow(() -> new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder() + .endpoint("http://localhost:1031") + .build())); } @SneakyThrows @Test void client_test() { - GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()); + GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder() + .endpoint(this.baseUrl.toString()) + .timeout(1000) + .build()); String providerName = "clientTest"; OpenFeatureAPI.getInstance().setProviderAndWait(providerName, g); Client client = OpenFeatureAPI.getInstance().getClient(providerName); Boolean value = client.getBooleanValue("bool_targeting_match", false); assertEquals(Boolean.FALSE, value, "should evaluate to default value without context"); - FlagEvaluationDetails booleanFlagEvaluationDetails = client.getBooleanDetails("bool_targeting_match", - false, new ImmutableContext()); - assertEquals(Boolean.FALSE, booleanFlagEvaluationDetails.getValue(), "should evaluate to default value with empty context"); - assertEquals(ErrorCode.TARGETING_KEY_MISSING, booleanFlagEvaluationDetails.getErrorCode(), "should evaluate to default value with empty context"); - booleanFlagEvaluationDetails = client.getBooleanDetails("bool_targeting_match", false, new ImmutableContext("targetingKey")); + FlagEvaluationDetails booleanFlagEvaluationDetails = + client.getBooleanDetails("bool_targeting_match", false, new ImmutableContext()); + assertEquals( + Boolean.FALSE, + booleanFlagEvaluationDetails.getValue(), + "should evaluate to default value with empty context"); + assertEquals( + ErrorCode.TARGETING_KEY_MISSING, + booleanFlagEvaluationDetails.getErrorCode(), + "should evaluate to default value with empty context"); + booleanFlagEvaluationDetails = + client.getBooleanDetails("bool_targeting_match", false, new ImmutableContext("targetingKey")); assertEquals(Boolean.TRUE, booleanFlagEvaluationDetails.getValue(), "should evaluate with a valid context"); } - @SneakyThrows @Test void should_throw_an_error_if_endpoint_not_available() { - GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()); + GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder() + .endpoint(this.baseUrl.toString()) + .timeout(1000) + .build()); String providerName = this.testName; OpenFeatureAPI.getInstance().setProviderAndWait(providerName, g); Client client = OpenFeatureAPI.getInstance().getClient(providerName); @@ -215,12 +239,11 @@ void should_throw_an_error_if_endpoint_not_available() { @SneakyThrows @Test void should_throw_an_error_if_invalid_api_key() { - GoFeatureFlagProvider g = new GoFeatureFlagProvider( - GoFeatureFlagProviderOptions.builder() - .endpoint(this.baseUrl.toString()) - .timeout(1000) - .apiKey("invalid_api_key") - .build()); + GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder() + .endpoint(this.baseUrl.toString()) + .timeout(1000) + .apiKey("invalid_api_key") + .build()); String providerName = this.testName; OpenFeatureAPI.getInstance().setProviderAndWait(providerName, g); Client client = OpenFeatureAPI.getInstance().getClient(providerName); @@ -237,7 +260,10 @@ void should_throw_an_error_if_invalid_api_key() { @SneakyThrows @Test void should_throw_an_error_if_flag_does_not_exists() { - GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()); + GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder() + .endpoint(this.baseUrl.toString()) + .timeout(1000) + .build()); String providerName = this.testName; OpenFeatureAPI.getInstance().setProviderAndWait(providerName, g); Client client = OpenFeatureAPI.getInstance().getClient(providerName); @@ -254,7 +280,10 @@ void should_throw_an_error_if_flag_does_not_exists() { @SneakyThrows @Test void should_throw_an_error_if_we_expect_a_boolean_and_got_another_type() { - GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()); + GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder() + .endpoint(this.baseUrl.toString()) + .timeout(1000) + .build()); String providerName = this.testName; OpenFeatureAPI.getInstance().setProviderAndWait(providerName, g); Client client = OpenFeatureAPI.getInstance().getClient(providerName); @@ -263,7 +292,8 @@ void should_throw_an_error_if_we_expect_a_boolean_and_got_another_type() { .value(false) .reason(Reason.ERROR.name()) .errorCode(ErrorCode.TYPE_MISMATCH) - .errorMessage("Flag value string_key had unexpected type class java.lang.String, expected class java.lang.Boolean.") + .errorMessage( + "Flag value string_key had unexpected type class java.lang.String, expected class java.lang.Boolean.") .build(); assertEquals(want, got); } @@ -271,11 +301,15 @@ void should_throw_an_error_if_we_expect_a_boolean_and_got_another_type() { @SneakyThrows @Test void should_resolve_a_valid_boolean_flag_with_TARGETING_MATCH_reason() { - GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()); + GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder() + .endpoint(this.baseUrl.toString()) + .timeout(1000) + .build()); String providerName = this.testName; OpenFeatureAPI.getInstance().setProviderAndWait(providerName, g); Client client = OpenFeatureAPI.getInstance().getClient(providerName); - FlagEvaluationDetails got = client.getBooleanDetails("bool_targeting_match", false, this.evaluationContext); + FlagEvaluationDetails got = + client.getBooleanDetails("bool_targeting_match", false, this.evaluationContext); FlagEvaluationDetails want = FlagEvaluationDetails.builder() .value(true) .variant("True") @@ -289,11 +323,15 @@ void should_resolve_a_valid_boolean_flag_with_TARGETING_MATCH_reason() { @SneakyThrows @Test void should_resolve_a_valid_boolean_flag_with_TARGETING_MATCH_reason_without_error_code_in_payload() { - GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()); + GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder() + .endpoint(this.baseUrl.toString()) + .timeout(1000) + .build()); String providerName = this.testName; OpenFeatureAPI.getInstance().setProviderAndWait(providerName, g); Client client = OpenFeatureAPI.getInstance().getClient(providerName); - FlagEvaluationDetails got = client.getBooleanDetails("bool_targeting_match_no_error_code", false, this.evaluationContext); + FlagEvaluationDetails got = + client.getBooleanDetails("bool_targeting_match_no_error_code", false, this.evaluationContext); FlagEvaluationDetails want = FlagEvaluationDetails.builder() .value(true) .variant("True") @@ -315,7 +353,8 @@ void should_resolve_a_valid_boolean_flag_with_TARGETING_MATCH_reason_cache_disab String providerName = this.testName; OpenFeatureAPI.getInstance().setProviderAndWait(providerName, g); Client client = OpenFeatureAPI.getInstance().getClient(providerName); - FlagEvaluationDetails got = client.getBooleanDetails("bool_targeting_match", false, this.evaluationContext); + FlagEvaluationDetails got = + client.getBooleanDetails("bool_targeting_match", false, this.evaluationContext); FlagEvaluationDetails want = FlagEvaluationDetails.builder() .value(true) .variant("True") @@ -331,11 +370,15 @@ void should_resolve_a_valid_boolean_flag_with_TARGETING_MATCH_reason_cache_disab @SneakyThrows @Test void should_resolve_from_cache() { - GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()); + GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder() + .endpoint(this.baseUrl.toString()) + .timeout(1000) + .build()); String providerName = this.testName; OpenFeatureAPI.getInstance().setProviderAndWait(providerName, g); Client client = OpenFeatureAPI.getInstance().getClient(providerName); - FlagEvaluationDetails got = client.getBooleanDetails("bool_targeting_match", false, this.evaluationContext); + FlagEvaluationDetails got = + client.getBooleanDetails("bool_targeting_match", false, this.evaluationContext); FlagEvaluationDetails want = FlagEvaluationDetails.builder() .value(true) .variant("True") @@ -360,11 +403,15 @@ void should_resolve_from_cache() { void should_resolve_from_cache_max_size() { Caffeine caffeine = Caffeine.newBuilder().maximumSize(1); GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder() - .endpoint(this.baseUrl.toString()).timeout(1000).cacheConfig(caffeine).build()); + .endpoint(this.baseUrl.toString()) + .timeout(1000) + .cacheConfig(caffeine) + .build()); String providerName = this.testName; OpenFeatureAPI.getInstance().setProviderAndWait(providerName, g); Client client = OpenFeatureAPI.getInstance().getClient(providerName); - FlagEvaluationDetails got = client.getBooleanDetails("bool_targeting_match", false, this.evaluationContext); + FlagEvaluationDetails got = + client.getBooleanDetails("bool_targeting_match", false, this.evaluationContext); FlagEvaluationDetails want = FlagEvaluationDetails.builder() .value(true) .variant("True") @@ -384,7 +431,8 @@ void should_resolve_from_cache_max_size() { .build(); assertEquals(want2, got); - FlagEvaluationDetails gotStr = client.getStringDetails("string_key", "defaultValue", this.evaluationContext); + FlagEvaluationDetails gotStr = + client.getStringDetails("string_key", "defaultValue", this.evaluationContext); FlagEvaluationDetails wantStr = FlagEvaluationDetails.builder() .value("CC0000") .variant("True") @@ -408,7 +456,10 @@ void should_resolve_from_cache_max_size() { @SneakyThrows @Test void should_return_custom_reason_if_returned_by_relay_proxy() { - GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()); + GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder() + .endpoint(this.baseUrl.toString()) + .timeout(1000) + .build()); String providerName = this.testName; OpenFeatureAPI.getInstance().setProviderAndWait(providerName, g); Client client = OpenFeatureAPI.getInstance().getClient(providerName); @@ -426,7 +477,10 @@ void should_return_custom_reason_if_returned_by_relay_proxy() { @SneakyThrows @Test void should_use_boolean_default_value_if_the_flag_is_disabled() { - GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()); + GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder() + .endpoint(this.baseUrl.toString()) + .timeout(1000) + .build()); String providerName = this.testName; OpenFeatureAPI.getInstance().setProviderAndWait(providerName, g); Client client = OpenFeatureAPI.getInstance().getClient(providerName); @@ -443,15 +497,20 @@ void should_use_boolean_default_value_if_the_flag_is_disabled() { @SneakyThrows @Test void should_throw_an_error_if_we_expect_a_string_and_got_another_type() { - GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()); + GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder() + .endpoint(this.baseUrl.toString()) + .timeout(1000) + .build()); String providerName = this.testName; OpenFeatureAPI.getInstance().setProviderAndWait(providerName, g); Client client = OpenFeatureAPI.getInstance().getClient(providerName); - FlagEvaluationDetails got = client.getStringDetails("bool_targeting_match", "defaultValue", this.evaluationContext); + FlagEvaluationDetails got = + client.getStringDetails("bool_targeting_match", "defaultValue", this.evaluationContext); FlagEvaluationDetails want = FlagEvaluationDetails.builder() .value("defaultValue") .reason(Reason.ERROR.name()) - .errorMessage("Flag value bool_targeting_match had unexpected type class java.lang.Boolean, expected class java.lang.String.") + .errorMessage( + "Flag value bool_targeting_match had unexpected type class java.lang.Boolean, expected class java.lang.String.") .errorCode(ErrorCode.TYPE_MISMATCH) .build(); assertEquals(want, got); @@ -460,11 +519,15 @@ void should_throw_an_error_if_we_expect_a_string_and_got_another_type() { @SneakyThrows @Test void should_resolve_a_valid_string_flag_with_TARGETING_MATCH_reason() { - GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()); + GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder() + .endpoint(this.baseUrl.toString()) + .timeout(1000) + .build()); String providerName = this.testName; OpenFeatureAPI.getInstance().setProviderAndWait(providerName, g); Client client = OpenFeatureAPI.getInstance().getClient(providerName); - FlagEvaluationDetails got = client.getStringDetails("string_key", "defaultValue", this.evaluationContext); + FlagEvaluationDetails got = + client.getStringDetails("string_key", "defaultValue", this.evaluationContext); FlagEvaluationDetails want = FlagEvaluationDetails.builder() .value("CC0000") .flagKey("string_key") @@ -478,7 +541,10 @@ void should_resolve_a_valid_string_flag_with_TARGETING_MATCH_reason() { @SneakyThrows @Test void should_use_string_default_value_if_the_flag_is_disabled() { - GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()); + GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder() + .endpoint(this.baseUrl.toString()) + .timeout(1000) + .build()); String providerName = this.testName; OpenFeatureAPI.getInstance().setProviderAndWait(providerName, g); Client client = OpenFeatureAPI.getInstance().getClient(providerName); @@ -495,15 +561,20 @@ void should_use_string_default_value_if_the_flag_is_disabled() { @SneakyThrows @Test void should_throw_an_error_if_we_expect_a_integer_and_got_another_type() { - GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()); + GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder() + .endpoint(this.baseUrl.toString()) + .timeout(1000) + .build()); String providerName = this.testName; OpenFeatureAPI.getInstance().setProviderAndWait(providerName, g); Client client = OpenFeatureAPI.getInstance().getClient(providerName); - FlagEvaluationDetails got = client.getIntegerDetails("bool_targeting_match", 200, this.evaluationContext); + FlagEvaluationDetails got = + client.getIntegerDetails("bool_targeting_match", 200, this.evaluationContext); FlagEvaluationDetails want = FlagEvaluationDetails.builder() .value(200) .reason(Reason.ERROR.name()) - .errorMessage("Flag value bool_targeting_match had unexpected type class java.lang.Boolean, expected class java.lang.Integer.") + .errorMessage( + "Flag value bool_targeting_match had unexpected type class java.lang.Boolean, expected class java.lang.Integer.") .errorCode(ErrorCode.TYPE_MISMATCH) .build(); assertEquals(want, got); @@ -512,7 +583,10 @@ void should_throw_an_error_if_we_expect_a_integer_and_got_another_type() { @SneakyThrows @Test void should_resolve_a_valid_integer_flag_with_TARGETING_MATCH_reason() { - GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()); + GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder() + .endpoint(this.baseUrl.toString()) + .timeout(1000) + .build()); String providerName = this.testName; OpenFeatureAPI.getInstance().setProviderAndWait(providerName, g); Client client = OpenFeatureAPI.getInstance().getClient(providerName); @@ -530,7 +604,10 @@ void should_resolve_a_valid_integer_flag_with_TARGETING_MATCH_reason() { @SneakyThrows @Test void should_use_integer_default_value_if_the_flag_is_disabled() { - GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()); + GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder() + .endpoint(this.baseUrl.toString()) + .timeout(1000) + .build()); String providerName = this.testName; OpenFeatureAPI.getInstance().setProviderAndWait(providerName, g); Client client = OpenFeatureAPI.getInstance().getClient(providerName); @@ -547,7 +624,10 @@ void should_use_integer_default_value_if_the_flag_is_disabled() { @SneakyThrows @Test void should_throw_an_error_if_we_expect_a_integer_and_double_type() { - GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()); + GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder() + .endpoint(this.baseUrl.toString()) + .timeout(1000) + .build()); String providerName = this.testName; OpenFeatureAPI.getInstance().setProviderAndWait(providerName, g); Client client = OpenFeatureAPI.getInstance().getClient(providerName); @@ -555,7 +635,8 @@ void should_throw_an_error_if_we_expect_a_integer_and_double_type() { FlagEvaluationDetails want = FlagEvaluationDetails.builder() .value(200) .reason(Reason.ERROR.name()) - .errorMessage("Flag value double_key had unexpected type class java.lang.Double, expected class java.lang.Integer.") + .errorMessage( + "Flag value double_key had unexpected type class java.lang.Double, expected class java.lang.Integer.") .errorCode(ErrorCode.TYPE_MISMATCH) .build(); assertEquals(want, got); @@ -564,7 +645,10 @@ void should_throw_an_error_if_we_expect_a_integer_and_double_type() { @SneakyThrows @Test void should_resolve_a_valid_double_flag_with_TARGETING_MATCH_reason() { - GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()); + GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder() + .endpoint(this.baseUrl.toString()) + .timeout(1000) + .build()); String providerName = this.testName; OpenFeatureAPI.getInstance().setProviderAndWait(providerName, g); Client client = OpenFeatureAPI.getInstance().getClient(providerName); @@ -582,11 +666,15 @@ void should_resolve_a_valid_double_flag_with_TARGETING_MATCH_reason() { @SneakyThrows @Test void should_resolve_a_valid_double_flag_with_TARGETING_MATCH_reason_if_value_point_zero() { - GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()); + GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder() + .endpoint(this.baseUrl.toString()) + .timeout(1000) + .build()); String providerName = this.testName; OpenFeatureAPI.getInstance().setProviderAndWait(providerName, g); Client client = OpenFeatureAPI.getInstance().getClient(providerName); - FlagEvaluationDetails got = client.getDoubleDetails("double_point_zero_key", 200.20, this.evaluationContext); + FlagEvaluationDetails got = + client.getDoubleDetails("double_point_zero_key", 200.20, this.evaluationContext); FlagEvaluationDetails want = FlagEvaluationDetails.builder() .value(100.0) .reason(Reason.TARGETING_MATCH.name()) @@ -600,7 +688,10 @@ void should_resolve_a_valid_double_flag_with_TARGETING_MATCH_reason_if_value_poi @SneakyThrows @Test void should_use_double_default_value_if_the_flag_is_disabled() { - GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()); + GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder() + .endpoint(this.baseUrl.toString()) + .timeout(1000) + .build()); String providerName = this.testName; OpenFeatureAPI.getInstance().setProviderAndWait(providerName, g); Client client = OpenFeatureAPI.getInstance().getClient(providerName); @@ -617,13 +708,20 @@ void should_use_double_default_value_if_the_flag_is_disabled() { @SneakyThrows @Test void should_resolve_a_valid_value_flag_with_TARGETING_MATCH_reason() { - GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()); + GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder() + .endpoint(this.baseUrl.toString()) + .timeout(1000) + .build()); String providerName = this.testName; OpenFeatureAPI.getInstance().setProviderAndWait(providerName, g); Client client = OpenFeatureAPI.getInstance().getClient(providerName); FlagEvaluationDetails got = client.getObjectDetails("object_key", new Value(), this.evaluationContext); FlagEvaluationDetails want = FlagEvaluationDetails.builder() - .value(new Value(new MutableStructure().add("test", "test1").add("test2", false).add("test3", 123.3).add("test4", 1))) + .value(new Value(new MutableStructure() + .add("test", "test1") + .add("test2", false) + .add("test3", 123.3) + .add("test4", 1))) .reason(Reason.TARGETING_MATCH.name()) .variant("True") .flagMetadata(defaultMetadata) @@ -635,7 +733,10 @@ void should_resolve_a_valid_value_flag_with_TARGETING_MATCH_reason() { @SneakyThrows @Test void should_wrap_into_value_if_wrong_type() { - GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()); + GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder() + .endpoint(this.baseUrl.toString()) + .timeout(1000) + .build()); String providerName = this.testName; OpenFeatureAPI.getInstance().setProviderAndWait(providerName, g); Client client = OpenFeatureAPI.getInstance().getClient(providerName); @@ -653,11 +754,15 @@ void should_wrap_into_value_if_wrong_type() { @SneakyThrows @Test void should_throw_an_error_if_no_targeting_key() { - GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()); + GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder() + .endpoint(this.baseUrl.toString()) + .timeout(1000) + .build()); String providerName = this.testName; OpenFeatureAPI.getInstance().setProviderAndWait(providerName, g); Client client = OpenFeatureAPI.getInstance().getClient(providerName); - FlagEvaluationDetails got = client.getObjectDetails("string_key", new Value("CC0000"), new MutableContext()); + FlagEvaluationDetails got = + client.getObjectDetails("string_key", new Value("CC0000"), new MutableContext()); FlagEvaluationDetails want = FlagEvaluationDetails.builder() .value(new Value("CC0000")) .errorCode(ErrorCode.TARGETING_KEY_MISSING) @@ -669,18 +774,21 @@ void should_throw_an_error_if_no_targeting_key() { @SneakyThrows @Test void should_resolve_a_valid_value_flag_with_a_list() { - GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()); + GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder() + .endpoint(this.baseUrl.toString()) + .timeout(1000) + .build()); String providerName = this.testName; OpenFeatureAPI.getInstance().setProviderAndWait(providerName, g); Client client = OpenFeatureAPI.getInstance().getClient(providerName); FlagEvaluationDetails got = client.getObjectDetails("list_key", new Value(), this.evaluationContext); FlagEvaluationDetails want = FlagEvaluationDetails.builder() - .value(new Value(new ArrayList<>( - Arrays.asList(new Value("test"), - new Value("test1"), - new Value("test2"), - new Value("false"), - new Value("test3"))))) + .value(new Value(new ArrayList<>(Arrays.asList( + new Value("test"), + new Value("test1"), + new Value("test2"), + new Value("false"), + new Value("test3"))))) .reason(Reason.TARGETING_MATCH.name()) .variant("True") .flagMetadata(defaultMetadata) @@ -692,7 +800,10 @@ void should_resolve_a_valid_value_flag_with_a_list() { @SneakyThrows @Test void should_not_fail_if_receive_an_unknown_field_in_response() { - GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()); + GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder() + .endpoint(this.baseUrl.toString()) + .timeout(1000) + .build()); String providerName = this.testName; OpenFeatureAPI.getInstance().setProviderAndWait(providerName, g); Client client = OpenFeatureAPI.getInstance().getClient(providerName); @@ -710,7 +821,10 @@ void should_not_fail_if_receive_an_unknown_field_in_response() { @SneakyThrows @Test void should_not_fail_if_no_metadata_in_response() { - GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()); + GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder() + .endpoint(this.baseUrl.toString()) + .timeout(1000) + .build()); String providerName = this.testName; OpenFeatureAPI.getInstance().setProviderAndWait(providerName, g); Client client = OpenFeatureAPI.getInstance().getClient(providerName); @@ -743,7 +857,10 @@ void should_publish_events() { client.getBooleanDetails("bool_targeting_match", false, this.evaluationContext); client.getBooleanDetails("bool_targeting_match", false, this.evaluationContext); Thread.sleep(50L); - assertEquals(1, publishEventsRequestsReceived, "Nothing should be added in the waiting to be published list (stay to 1)"); + assertEquals( + 1, + publishEventsRequestsReceived, + "Nothing should be added in the waiting to be published list (stay to 1)"); Thread.sleep(100); assertEquals(3, publishEventsRequestsReceived, "We pass the flush interval, we should have 3 events"); client.getBooleanDetails("bool_targeting_match", false, this.evaluationContext); @@ -753,7 +870,10 @@ void should_publish_events() { client.getBooleanDetails("bool_targeting_match", false, this.evaluationContext); client.getBooleanDetails("bool_targeting_match", false, this.evaluationContext); Thread.sleep(150); - assertEquals(6, publishEventsRequestsReceived, "we have call 6 time more, so we should consider only those new calls"); + assertEquals( + 6, + publishEventsRequestsReceived, + "we have call 6 time more, so we should consider only those new calls"); } @SneakyThrows @@ -794,7 +914,8 @@ void should_not_get_cached_value_if_flag_configuration_changed() { String providerName = this.testName; OpenFeatureAPI.getInstance().setProviderAndWait(providerName, g); Client client = OpenFeatureAPI.getInstance().getClient(providerName); - FlagEvaluationDetails got = client.getBooleanDetails("bool_targeting_match", false, this.evaluationContext); + FlagEvaluationDetails got = + client.getBooleanDetails("bool_targeting_match", false, this.evaluationContext); assertEquals(Reason.TARGETING_MATCH.name(), got.getReason()); got = client.getBooleanDetails("bool_targeting_match", false, this.evaluationContext); assertEquals(Reason.CACHED.name(), got.getReason()); diff --git a/providers/jsonlogic-eval-provider/src/main/java/dev/openfeature/contrib/providers/jsonlogic/FileBasedFetcher.java b/providers/jsonlogic-eval-provider/src/main/java/dev/openfeature/contrib/providers/jsonlogic/FileBasedFetcher.java index 4186bd516..4d43a3c38 100644 --- a/providers/jsonlogic-eval-provider/src/main/java/dev/openfeature/contrib/providers/jsonlogic/FileBasedFetcher.java +++ b/providers/jsonlogic-eval-provider/src/main/java/dev/openfeature/contrib/providers/jsonlogic/FileBasedFetcher.java @@ -2,23 +2,19 @@ import dev.openfeature.sdk.EvaluationContext; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import org.json.JSONException; -import org.json.JSONObject; - import java.io.IOException; import java.net.URI; import java.nio.file.Files; import java.nio.file.Paths; import java.util.logging.Logger; +import org.json.JSONException; +import org.json.JSONObject; /** - * A {@link RuleFetcher} which reads in the rules from a file. It assumes that the keys are the flag keys and the - * values are the json logic rules. + * A {@link RuleFetcher} which reads in the rules from a file. It assumes that the keys are the flag + * keys and the values are the json logic rules. */ -@SuppressFBWarnings( - value = "PATH_TRAVERSAL_IN", - justification = "This is expected to read files based on user input" -) +@SuppressFBWarnings(value = "PATH_TRAVERSAL_IN", justification = "This is expected to read files based on user input") @SuppressWarnings({"checkstyle:NoFinalizer"}) public class FileBasedFetcher implements RuleFetcher { private static final Logger log = Logger.getLogger(String.valueOf(FileBasedFetcher.class)); @@ -30,6 +26,7 @@ protected final void finalize() { /** * Create a file based fetcher give a file URI. + * * @param filename URI to a given file. * @throws IOException when we can't load the file correctly */ @@ -48,6 +45,6 @@ public String getRuleForKey(String key) { return null; } - @Override public void initialize(EvaluationContext initialContext) { - } + @Override + public void initialize(EvaluationContext initialContext) {} } diff --git a/providers/jsonlogic-eval-provider/src/main/java/dev/openfeature/contrib/providers/jsonlogic/JsonlogicProvider.java b/providers/jsonlogic-eval-provider/src/main/java/dev/openfeature/contrib/providers/jsonlogic/JsonlogicProvider.java index 3a02598d9..2129edbb9 100644 --- a/providers/jsonlogic-eval-provider/src/main/java/dev/openfeature/contrib/providers/jsonlogic/JsonlogicProvider.java +++ b/providers/jsonlogic-eval-provider/src/main/java/dev/openfeature/contrib/providers/jsonlogic/JsonlogicProvider.java @@ -9,17 +9,13 @@ import dev.openfeature.sdk.exceptions.ParseError; import io.github.jamsesso.jsonlogic.JsonLogic; import io.github.jamsesso.jsonlogic.JsonLogicException; - import java.util.function.Function; -/** - * A provider which evaluates JsonLogic rules provided by a {@link RuleFetcher}. - */ +/** A provider which evaluates JsonLogic rules provided by a {@link RuleFetcher}. */ public class JsonlogicProvider implements FeatureProvider { private final JsonLogic logic; private final RuleFetcher fetcher; - public void initialize(EvaluationContext initialContext) { fetcher.initialize(initialContext); } diff --git a/providers/jsonlogic-eval-provider/src/main/java/dev/openfeature/contrib/providers/jsonlogic/RuleFetcher.java b/providers/jsonlogic-eval-provider/src/main/java/dev/openfeature/contrib/providers/jsonlogic/RuleFetcher.java index b802e4046..9f1054a07 100644 --- a/providers/jsonlogic-eval-provider/src/main/java/dev/openfeature/contrib/providers/jsonlogic/RuleFetcher.java +++ b/providers/jsonlogic-eval-provider/src/main/java/dev/openfeature/contrib/providers/jsonlogic/RuleFetcher.java @@ -1,26 +1,27 @@ package dev.openfeature.contrib.providers.jsonlogic; import dev.openfeature.sdk.EvaluationContext; - import javax.annotation.Nullable; /** - * A RuleFetcher exists to fetch rules from a likely remote location which will be used for local evaluation. + * A RuleFetcher exists to fetch rules from a likely remote location which will be used for local + * evaluation. */ public interface RuleFetcher { /** - * Called to set up the client initially. This is used to pre-fetch initial data as well as setup mechanisms - * to stay up to date. + * Called to set up the client initially. This is used to pre-fetch initial data as well as setup + * mechanisms to stay up to date. + * * @param initialContext application context known thus far */ void initialize(EvaluationContext initialContext); /** * Given a key name, return the JSONLogic rules for it. + * * @param key The key to fetch logic for * @return json logic rules or null */ - @Nullable - String getRuleForKey(String key); + @Nullable String getRuleForKey(String key); } diff --git a/providers/jsonlogic-eval-provider/src/test/java/dev/openfeature/contrib/providers/jsonlogic/FileBasedFetcherTest.java b/providers/jsonlogic-eval-provider/src/test/java/dev/openfeature/contrib/providers/jsonlogic/FileBasedFetcherTest.java index 46a0204d3..ae91401b1 100644 --- a/providers/jsonlogic-eval-provider/src/test/java/dev/openfeature/contrib/providers/jsonlogic/FileBasedFetcherTest.java +++ b/providers/jsonlogic-eval-provider/src/test/java/dev/openfeature/contrib/providers/jsonlogic/FileBasedFetcherTest.java @@ -1,16 +1,16 @@ package dev.openfeature.contrib.providers.jsonlogic; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertNull; import java.net.URI; - -import static org.junit.jupiter.api.Assertions.assertNull; +import org.junit.jupiter.api.Test; class FileBasedFetcherTest { - @Test public void testNullValueForRule() throws Exception { + @Test + public void testNullValueForRule() throws Exception { URI uri = this.getClass().getResource("/test-rules.json").toURI(); FileBasedFetcher f = new FileBasedFetcher(uri); assertNull(f.getRuleForKey("malformed")); } -} \ No newline at end of file +} diff --git a/providers/jsonlogic-eval-provider/src/test/java/dev/openfeature/contrib/providers/jsonlogic/JsonlogicProviderTest.java b/providers/jsonlogic-eval-provider/src/test/java/dev/openfeature/contrib/providers/jsonlogic/JsonlogicProviderTest.java index 5a0f51e00..3cfcf56c0 100644 --- a/providers/jsonlogic-eval-provider/src/test/java/dev/openfeature/contrib/providers/jsonlogic/JsonlogicProviderTest.java +++ b/providers/jsonlogic-eval-provider/src/test/java/dev/openfeature/contrib/providers/jsonlogic/JsonlogicProviderTest.java @@ -1,18 +1,17 @@ package dev.openfeature.contrib.providers.jsonlogic; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.*; + import dev.openfeature.sdk.ImmutableContext; import dev.openfeature.sdk.ProviderEvaluation; import dev.openfeature.sdk.Value; import io.github.jamsesso.jsonlogic.JsonLogic; -import org.junit.jupiter.api.Test; - import java.net.URL; import java.util.Collections; import java.util.HashMap; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.*; +import org.junit.jupiter.api.Test; class JsonlogicProviderTest { @Test @@ -40,7 +39,8 @@ public void jsonlogicReturnTypes() throws Exception { assertEquals(true, logic.apply(rule, Collections.singletonMap("bool", "true"))); } - @Test public void providerTest() throws Exception { + @Test + public void providerTest() throws Exception { URL v = this.getClass().getResource("/test-rules.json"); JsonlogicProvider iep = new JsonlogicProvider(new FileBasedFetcher(v.toURI())); ImmutableContext evalCtx = new ImmutableContext(Collections.singletonMap("userId", new Value(2))); @@ -49,7 +49,8 @@ public void jsonlogicReturnTypes() throws Exception { assertTrue(result.getValue(), result.getReason()); } - @Test public void missingKey() throws Exception { + @Test + public void missingKey() throws Exception { URL v = this.getClass().getResource("/test-rules.json"); JsonlogicProvider iep = new JsonlogicProvider(new FileBasedFetcher(v.toURI())); @@ -58,10 +59,11 @@ public void jsonlogicReturnTypes() throws Exception { assertEquals("ERROR", result.getReason()); } - @Test public void callsFetcherInitialize() { + @Test + public void callsFetcherInitialize() { RuleFetcher mockFetcher = mock(RuleFetcher.class); JsonlogicProvider iep = new JsonlogicProvider(mockFetcher); iep.initialize(null); verify(mockFetcher).initialize(any()); } -} \ No newline at end of file +} diff --git a/providers/multiprovider/src/main/java/dev/openfeature/contrib/providers/multiprovider/FirstMatchStrategy.java b/providers/multiprovider/src/main/java/dev/openfeature/contrib/providers/multiprovider/FirstMatchStrategy.java index 3c90b3154..064e39140 100644 --- a/providers/multiprovider/src/main/java/dev/openfeature/contrib/providers/multiprovider/FirstMatchStrategy.java +++ b/providers/multiprovider/src/main/java/dev/openfeature/contrib/providers/multiprovider/FirstMatchStrategy.java @@ -1,25 +1,22 @@ package dev.openfeature.contrib.providers.multiprovider; +import static dev.openfeature.sdk.ErrorCode.FLAG_NOT_FOUND; + import dev.openfeature.sdk.EvaluationContext; import dev.openfeature.sdk.FeatureProvider; import dev.openfeature.sdk.ProviderEvaluation; import dev.openfeature.sdk.exceptions.FlagNotFoundError; -import lombok.NoArgsConstructor; -import lombok.extern.slf4j.Slf4j; - import java.util.Map; import java.util.function.Function; - -import static dev.openfeature.sdk.ErrorCode.FLAG_NOT_FOUND; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; /** - * First match strategy. - * Return the first result returned by a provider. Skip providers that indicate they had no value due to - * FLAG_NOT_FOUND. - * In all other cases, use the value returned by the provider. - * If any provider returns an error result other than FLAG_NOT_FOUND, the whole evaluation should error and - * “bubble up” the individual provider’s error in the result. - * As soon as a value is returned by a provider, the rest of the operation should short-circuit and not call + * First match strategy. Return the first result returned by a provider. Skip providers that + * indicate they had no value due to FLAG_NOT_FOUND. In all other cases, use the value returned by + * the provider. If any provider returns an error result other than FLAG_NOT_FOUND, the whole + * evaluation should error and “bubble up” the individual provider’s error in the result. As soon as + * a value is returned by a provider, the rest of the operation should short-circuit and not call * the rest of the providers. */ @Slf4j @@ -27,17 +24,21 @@ public class FirstMatchStrategy implements Strategy { /** - * Represents a strategy that evaluates providers based on a first-match approach. - * Provides a method to evaluate providers using a specified function and return the evaluation result. + * Represents a strategy that evaluates providers based on a first-match approach. Provides a + * method to evaluate providers using a specified function and return the evaluation result. * * @param providerFunction provider function * @param ProviderEvaluation type * @return the provider evaluation */ @Override - public ProviderEvaluation evaluate(Map providers, String key, T defaultValue, - EvaluationContext ctx, Function> providerFunction) { - for (FeatureProvider provider: providers.values()) { + public ProviderEvaluation evaluate( + Map providers, + String key, + T defaultValue, + EvaluationContext ctx, + Function> providerFunction) { + for (FeatureProvider provider : providers.values()) { try { ProviderEvaluation res = providerFunction.apply(provider); if (!FLAG_NOT_FOUND.equals(res.getErrorCode())) { diff --git a/providers/multiprovider/src/main/java/dev/openfeature/contrib/providers/multiprovider/FirstSuccessfulStrategy.java b/providers/multiprovider/src/main/java/dev/openfeature/contrib/providers/multiprovider/FirstSuccessfulStrategy.java index ec23746fb..377cc61ef 100644 --- a/providers/multiprovider/src/main/java/dev/openfeature/contrib/providers/multiprovider/FirstSuccessfulStrategy.java +++ b/providers/multiprovider/src/main/java/dev/openfeature/contrib/providers/multiprovider/FirstSuccessfulStrategy.java @@ -4,26 +4,28 @@ import dev.openfeature.sdk.FeatureProvider; import dev.openfeature.sdk.ProviderEvaluation; import dev.openfeature.sdk.exceptions.GeneralError; -import lombok.NoArgsConstructor; -import lombok.extern.slf4j.Slf4j; - import java.util.Map; import java.util.function.Function; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; /** - * First Successful Strategy. - * Similar to “First Match”, except that errors from evaluated providers do not halt execution. - * Instead, it will return the first successful result from a provider. - * If no provider successfully responds, it will throw an error result. + * First Successful Strategy. Similar to “First Match”, except that errors from evaluated providers + * do not halt execution. Instead, it will return the first successful result from a provider. If no + * provider successfully responds, it will throw an error result. */ @Slf4j @NoArgsConstructor public class FirstSuccessfulStrategy implements Strategy { @Override - public ProviderEvaluation evaluate(Map providers, String key, T defaultValue, - EvaluationContext ctx, Function> providerFunction) { - for (FeatureProvider provider: providers.values()) { + public ProviderEvaluation evaluate( + Map providers, + String key, + T defaultValue, + EvaluationContext ctx, + Function> providerFunction) { + for (FeatureProvider provider : providers.values()) { try { ProviderEvaluation res = providerFunction.apply(provider); if (res.getErrorCode() == null) { diff --git a/providers/multiprovider/src/main/java/dev/openfeature/contrib/providers/multiprovider/MultiProvider.java b/providers/multiprovider/src/main/java/dev/openfeature/contrib/providers/multiprovider/MultiProvider.java index 65d24cb41..2b660aec0 100644 --- a/providers/multiprovider/src/main/java/dev/openfeature/contrib/providers/multiprovider/MultiProvider.java +++ b/providers/multiprovider/src/main/java/dev/openfeature/contrib/providers/multiprovider/MultiProvider.java @@ -7,10 +7,6 @@ import dev.openfeature.sdk.ProviderEvaluation; import dev.openfeature.sdk.Value; import dev.openfeature.sdk.exceptions.GeneralError; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -import org.json.JSONObject; - import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -21,15 +17,17 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.json.JSONObject; -/** - * Provider implementation for Multi-provider. - */ +/** Provider implementation for Multi-provider. */ @Slf4j public class MultiProvider extends EventProvider { @Getter private static final String NAME = "multiprovider"; + public static final int INIT_THREADS_COUNT = 8; private final Map providers; private final Strategy strategy; @@ -61,8 +59,9 @@ public MultiProvider(List providers, Strategy strategy) { protected static Map buildProviders(List providers) { Map providersMap = new LinkedHashMap<>(providers.size()); - for (FeatureProvider provider: providers) { - FeatureProvider prevProvider = providersMap.put(provider.getMetadata().getName(), provider); + for (FeatureProvider provider : providers) { + FeatureProvider prevProvider = + providersMap.put(provider.getMetadata().getName(), provider); if (prevProvider != null) { log.warn("duplicated provider name: {}", provider.getMetadata().getName()); } @@ -72,6 +71,7 @@ protected static Map buildProviders(List> tasks = new ArrayList<>(providers.size()); - for (FeatureProvider provider: providers.values()) { + for (FeatureProvider provider : providers.values()) { tasks.add(() -> { provider.initialize(evaluationContext); return true; @@ -93,7 +93,7 @@ public void initialize(EvaluationContext evaluationContext) throws Exception { providersMetadata.put(provider.getMetadata().getName(), providerMetadata); } List> results = initPool.invokeAll(tasks); - for (Future result: results) { + for (Future result : results) { if (!result.get()) { throw new GeneralError("init failed"); } @@ -108,38 +108,35 @@ public Metadata getMetadata() { @Override public ProviderEvaluation getBooleanEvaluation(String key, Boolean defaultValue, EvaluationContext ctx) { - return strategy.evaluate(providers, key, defaultValue, ctx, - p -> p.getBooleanEvaluation(key, defaultValue, ctx)); + return strategy.evaluate( + providers, key, defaultValue, ctx, p -> p.getBooleanEvaluation(key, defaultValue, ctx)); } @Override public ProviderEvaluation getStringEvaluation(String key, String defaultValue, EvaluationContext ctx) { - return strategy.evaluate(providers, key, defaultValue, ctx, - p -> p.getStringEvaluation(key, defaultValue, ctx)); + return strategy.evaluate(providers, key, defaultValue, ctx, p -> p.getStringEvaluation(key, defaultValue, ctx)); } @Override public ProviderEvaluation getIntegerEvaluation(String key, Integer defaultValue, EvaluationContext ctx) { - return strategy.evaluate(providers, key, defaultValue, ctx, - p -> p.getIntegerEvaluation(key, defaultValue, ctx)); + return strategy.evaluate( + providers, key, defaultValue, ctx, p -> p.getIntegerEvaluation(key, defaultValue, ctx)); } @Override public ProviderEvaluation getDoubleEvaluation(String key, Double defaultValue, EvaluationContext ctx) { - return strategy.evaluate(providers, key, defaultValue, ctx, - p -> p.getDoubleEvaluation(key, defaultValue, ctx)); + return strategy.evaluate(providers, key, defaultValue, ctx, p -> p.getDoubleEvaluation(key, defaultValue, ctx)); } @Override public ProviderEvaluation getObjectEvaluation(String key, Value defaultValue, EvaluationContext ctx) { - return strategy.evaluate(providers, key, defaultValue, ctx, - p -> p.getObjectEvaluation(key, defaultValue, ctx)); + return strategy.evaluate(providers, key, defaultValue, ctx, p -> p.getObjectEvaluation(key, defaultValue, ctx)); } @Override public void shutdown() { log.debug("shutdown begin"); - for (FeatureProvider provider: providers.values()) { + for (FeatureProvider provider : providers.values()) { try { provider.shutdown(); } catch (Exception e) { @@ -148,5 +145,4 @@ public void shutdown() { } log.debug("shutdown end"); } - } diff --git a/providers/multiprovider/src/main/java/dev/openfeature/contrib/providers/multiprovider/Strategy.java b/providers/multiprovider/src/main/java/dev/openfeature/contrib/providers/multiprovider/Strategy.java index 85fc06747..7363c1266 100644 --- a/providers/multiprovider/src/main/java/dev/openfeature/contrib/providers/multiprovider/Strategy.java +++ b/providers/multiprovider/src/main/java/dev/openfeature/contrib/providers/multiprovider/Strategy.java @@ -3,14 +3,15 @@ import dev.openfeature.sdk.EvaluationContext; import dev.openfeature.sdk.FeatureProvider; import dev.openfeature.sdk.ProviderEvaluation; - import java.util.Map; import java.util.function.Function; -/** - * strategy. - */ +/** strategy. */ public interface Strategy { - ProviderEvaluation evaluate(Map providers, String key, T defaultValue, - EvaluationContext ctx, Function> providerFunction); + ProviderEvaluation evaluate( + Map providers, + String key, + T defaultValue, + EvaluationContext ctx, + Function> providerFunction); } diff --git a/providers/multiprovider/src/test/java/dev/openfeature/contrib/providers/multiprovider/MultiProviderTest.java b/providers/multiprovider/src/test/java/dev/openfeature/contrib/providers/multiprovider/MultiProviderTest.java index 943728c6a..92ef19915 100644 --- a/providers/multiprovider/src/test/java/dev/openfeature/contrib/providers/multiprovider/MultiProviderTest.java +++ b/providers/multiprovider/src/test/java/dev/openfeature/contrib/providers/multiprovider/MultiProviderTest.java @@ -1,5 +1,14 @@ package dev.openfeature.contrib.providers.multiprovider; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import dev.openfeature.sdk.EvaluationContext; import dev.openfeature.sdk.FeatureProvider; import dev.openfeature.sdk.Metadata; @@ -10,24 +19,14 @@ import dev.openfeature.sdk.exceptions.GeneralError; import dev.openfeature.sdk.providers.memory.Flag; import dev.openfeature.sdk.providers.memory.InMemoryProvider; -import lombok.SneakyThrows; -import org.junit.jupiter.api.Test; - import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.function.Function; - -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import lombok.SneakyThrows; +import org.junit.jupiter.api.Test; class MultiProviderTest { @@ -47,8 +46,9 @@ public void testInit() { multiProvider.initialize(null); assertNotNull(multiProvider); - assertEquals("{\"originalMetadata\":{\"provider1\":{\"name\":\"provider1\"}," + - "\"provider2\":{\"name\":\"provider2\"}},\"name\":\"multiprovider\"}", + assertEquals( + "{\"originalMetadata\":{\"provider1\":{\"name\":\"provider1\"}," + + "\"provider2\":{\"name\":\"provider2\"}},\"name\":\"multiprovider\"}", multiProvider.getMetadata().getName()); } @@ -96,36 +96,50 @@ public void testRetrieveMetadataName() { MultiProvider multiProvider = new MultiProvider(providers, mockStrategy); multiProvider.initialize(null); - assertEquals("{\"originalMetadata\":{\"MockProvider\":{\"name\":\"MockProvider\"}}," + - "\"name\":\"multiprovider\"}", multiProvider.getMetadata().getName()); + assertEquals( + "{\"originalMetadata\":{\"MockProvider\":{\"name\":\"MockProvider\"}}," + "\"name\":\"multiprovider\"}", + multiProvider.getMetadata().getName()); } @SneakyThrows @Test public void testEvaluations() { Map> flags1 = new HashMap<>(); - flags1.put("b1", Flag.builder().variant("true", true) - .variant("false", false).defaultVariant("true").build()); + flags1.put( + "b1", + Flag.builder() + .variant("true", true) + .variant("false", false) + .defaultVariant("true") + .build()); flags1.put("i1", Flag.builder().variant("v", 1).defaultVariant("v").build()); flags1.put("d1", Flag.builder().variant("v", 1.0).defaultVariant("v").build()); flags1.put("s1", Flag.builder().variant("v", "str1").defaultVariant("v").build()); - flags1.put("o1", Flag.builder().variant("v", new Value("v1")) - .defaultVariant("v").build()); + flags1.put( + "o1", + Flag.builder().variant("v", new Value("v1")).defaultVariant("v").build()); InMemoryProvider provider1 = new InMemoryProvider(flags1) { public Metadata getMetadata() { return () -> "old-provider"; } }; Map> flags2 = new HashMap<>(); - flags2.put("b1", Flag.builder().variant("true", true) - .variant("false", false).defaultVariant("false").build()); + flags2.put( + "b1", + Flag.builder() + .variant("true", true) + .variant("false", false) + .defaultVariant("false") + .build()); flags2.put("i1", Flag.builder().variant("v", 2).defaultVariant("v").build()); flags2.put("d1", Flag.builder().variant("v", 2.0).defaultVariant("v").build()); flags2.put("s1", Flag.builder().variant("v", "str2").defaultVariant("v").build()); - flags2.put("o1", Flag.builder().variant("v", new Value("v2")) - .defaultVariant("v").build()); + flags2.put( + "o1", + Flag.builder().variant("v", new Value("v2")).defaultVariant("v").build()); - flags2.put("s2", Flag.builder().variant("v", "s2str2").defaultVariant("v").build()); + flags2.put( + "s2", Flag.builder().variant("v", "s2str2").defaultVariant("v").build()); InMemoryProvider provider2 = new InMemoryProvider(flags2) { public Metadata getMetadata() { return () -> "new-provider"; @@ -137,49 +151,45 @@ public Metadata getMetadata() { MultiProvider multiProvider = new MultiProvider(providers); multiProvider.initialize(null); - assertEquals(true, multiProvider.getBooleanEvaluation("b1", false, null) - .getValue()); - assertEquals(1, multiProvider.getIntegerEvaluation("i1", 0, null) - .getValue()); - assertEquals(1.0, multiProvider.getDoubleEvaluation("d1", 0.0, null) - .getValue()); - assertEquals("str1", multiProvider.getStringEvaluation("s1", "", null) - .getValue()); - assertEquals("v1", multiProvider.getObjectEvaluation("o1", null, null) - .getValue().asString()); - - assertEquals("s2str2", multiProvider.getStringEvaluation("s2", "", null) - .getValue()); + assertEquals(true, multiProvider.getBooleanEvaluation("b1", false, null).getValue()); + assertEquals(1, multiProvider.getIntegerEvaluation("i1", 0, null).getValue()); + assertEquals(1.0, multiProvider.getDoubleEvaluation("d1", 0.0, null).getValue()); + assertEquals("str1", multiProvider.getStringEvaluation("s1", "", null).getValue()); + assertEquals( + "v1", + multiProvider.getObjectEvaluation("o1", null, null).getValue().asString()); + + assertEquals("s2str2", multiProvider.getStringEvaluation("s2", "", null).getValue()); MultiProvider finalMultiProvider1 = multiProvider; - assertThrows(FlagNotFoundError.class, () -> - finalMultiProvider1.getStringEvaluation("non-existing", "", null)); + assertThrows(FlagNotFoundError.class, () -> finalMultiProvider1.getStringEvaluation("non-existing", "", null)); multiProvider.shutdown(); multiProvider = new MultiProvider(providers, new FirstSuccessfulStrategy()); multiProvider.initialize(null); - assertEquals(true, multiProvider.getBooleanEvaluation("b1", false, null) - .getValue()); - assertEquals(1, multiProvider.getIntegerEvaluation("i1", 0, null) - .getValue()); - assertEquals(1.0, multiProvider.getDoubleEvaluation("d1", 0.0, null) - .getValue()); - assertEquals("str1", multiProvider.getStringEvaluation("s1", "", null) - .getValue()); - assertEquals("v1", multiProvider.getObjectEvaluation("o1", null, null) - .getValue().asString()); - - assertEquals("s2str2", multiProvider.getStringEvaluation("s2", "", null) - .getValue()); + assertEquals(true, multiProvider.getBooleanEvaluation("b1", false, null).getValue()); + assertEquals(1, multiProvider.getIntegerEvaluation("i1", 0, null).getValue()); + assertEquals(1.0, multiProvider.getDoubleEvaluation("d1", 0.0, null).getValue()); + assertEquals("str1", multiProvider.getStringEvaluation("s1", "", null).getValue()); + assertEquals( + "v1", + multiProvider.getObjectEvaluation("o1", null, null).getValue().asString()); + + assertEquals("s2str2", multiProvider.getStringEvaluation("s2", "", null).getValue()); MultiProvider finalMultiProvider2 = multiProvider; - assertThrows(GeneralError.class, () -> - finalMultiProvider2.getStringEvaluation("non-existing", "", null)); + assertThrows(GeneralError.class, () -> finalMultiProvider2.getStringEvaluation("non-existing", "", null)); multiProvider.shutdown(); Strategy customStrategy = new Strategy() { final FirstMatchStrategy fallbackStrategy = new FirstMatchStrategy(); + @Override - public ProviderEvaluation evaluate(Map providers, String key, T defaultValue, EvaluationContext ctx, Function> providerFunction) { + public ProviderEvaluation evaluate( + Map providers, + String key, + T defaultValue, + EvaluationContext ctx, + Function> providerFunction) { Value contextProvider = null; if (ctx != null) { contextProvider = ctx.getValue("provider"); @@ -194,9 +204,8 @@ public ProviderEvaluation evaluate(Map providers multiProvider.initialize(null); EvaluationContext context = new MutableContext().add("provider", "new-provider"); - assertEquals(false, multiProvider.getBooleanEvaluation("b1", true, context) - .getValue()); - assertEquals(true, multiProvider.getBooleanEvaluation("b1", true, null) - .getValue()); + assertEquals( + false, multiProvider.getBooleanEvaluation("b1", true, context).getValue()); + assertEquals(true, multiProvider.getBooleanEvaluation("b1", true, null).getValue()); } -} \ No newline at end of file +} diff --git a/providers/statsig/src/main/java/dev/openfeature/contrib/providers/statsig/ContextTransformer.java b/providers/statsig/src/main/java/dev/openfeature/contrib/providers/statsig/ContextTransformer.java index fc45b4168..bc5d75ce5 100644 --- a/providers/statsig/src/main/java/dev/openfeature/contrib/providers/statsig/ContextTransformer.java +++ b/providers/statsig/src/main/java/dev/openfeature/contrib/providers/statsig/ContextTransformer.java @@ -5,14 +5,11 @@ import dev.openfeature.sdk.Structure; import dev.openfeature.sdk.Value; import dev.openfeature.sdk.exceptions.TargetingKeyMissingError; - import java.util.HashMap; import java.util.Map; import java.util.Objects; -/** - * Transformer from OpenFeature context to statsig User. - */ +/** Transformer from OpenFeature context to statsig User. */ class ContextTransformer { public static final String CONTEXT_APP_VERSION = "appVersion"; public static final String CONTEXT_COUNTRY = "country"; @@ -69,5 +66,4 @@ static StatsigUser transform(EvaluationContext ctx) { } return user; } - } diff --git a/providers/statsig/src/main/java/dev/openfeature/contrib/providers/statsig/StatsigProvider.java b/providers/statsig/src/main/java/dev/openfeature/contrib/providers/statsig/StatsigProvider.java index 05cbc8f10..8c1dd5a8f 100644 --- a/providers/statsig/src/main/java/dev/openfeature/contrib/providers/statsig/StatsigProvider.java +++ b/providers/statsig/src/main/java/dev/openfeature/contrib/providers/statsig/StatsigProvider.java @@ -1,18 +1,11 @@ package dev.openfeature.contrib.providers.statsig; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Future; - -import org.jetbrains.annotations.NotNull; - import com.statsig.sdk.APIFeatureGate; import com.statsig.sdk.DynamicConfig; import com.statsig.sdk.EvaluationReason; import com.statsig.sdk.Layer; import com.statsig.sdk.Statsig; import com.statsig.sdk.StatsigUser; - import dev.openfeature.sdk.EvaluationContext; import dev.openfeature.sdk.EventProvider; import dev.openfeature.sdk.Metadata; @@ -21,24 +14,28 @@ import dev.openfeature.sdk.Structure; import dev.openfeature.sdk.Value; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Future; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; -/** - * Provider implementation for Statsig. - */ +/** Provider implementation for Statsig. */ @Slf4j public class StatsigProvider extends EventProvider { @Getter private static final String NAME = "Statsig"; + private static final String FEATURE_CONFIG_KEY = "feature_config"; private final StatsigProviderConfig statsigProviderConfig; /** * Constructor. + * * @param statsigProviderConfig StatsigProvider Config */ public StatsigProvider(StatsigProviderConfig statsigProviderConfig) { @@ -47,13 +44,14 @@ public StatsigProvider(StatsigProviderConfig statsigProviderConfig) { /** * Initialize the provider. + * * @param evaluationContext evaluation context * @throws Exception on error */ @Override public void initialize(EvaluationContext evaluationContext) throws Exception { - Future initFuture = Statsig.initializeAsync(statsigProviderConfig.getSdkKey(), - statsigProviderConfig.getOptions()); + Future initFuture = + Statsig.initializeAsync(statsigProviderConfig.getSdkKey(), statsigProviderConfig.getOptions()); initFuture.get(); statsigProviderConfig.postInit(); @@ -67,7 +65,9 @@ public Metadata getMetadata() { @SneakyThrows @Override - @SuppressFBWarnings(value = {"NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE"}, justification = "reason can be null") + @SuppressFBWarnings( + value = {"NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE"}, + justification = "reason can be null") public ProviderEvaluation getBooleanEvaluation(String key, Boolean defaultValue, EvaluationContext ctx) { StatsigUser user = ContextTransformer.transform(ctx); Boolean evaluatedValue = defaultValue; @@ -98,21 +98,21 @@ public ProviderEvaluation getBooleanEvaluation(String key, Boolean defa } return ProviderEvaluation.builder() - .value(evaluatedValue) - .reason(reason) - .build(); + .value(evaluatedValue) + .reason(reason) + .build(); } /* https://github.com/statsig-io/java-server-sdk/issues/22#issuecomment-2002346349 failure is assumed by reason, since success status is not returned. - */ + */ private boolean assumeFailure(APIFeatureGate featureGate) { EvaluationReason reason = featureGate.getReason(); return EvaluationReason.DEFAULT.equals(reason) - || EvaluationReason.UNINITIALIZED.equals(reason) - || EvaluationReason.UNRECOGNIZED.equals(reason) - || EvaluationReason.UNSUPPORTED.equals(reason); + || EvaluationReason.UNINITIALIZED.equals(reason) + || EvaluationReason.UNRECOGNIZED.equals(reason) + || EvaluationReason.UNSUPPORTED.equals(reason); } @Override @@ -132,9 +132,7 @@ public ProviderEvaluation getStringEvaluation(String key, String default default: break; } - return ProviderEvaluation.builder() - .value(evaluatedValue) - .build(); + return ProviderEvaluation.builder().value(evaluatedValue).build(); } @Override @@ -154,9 +152,7 @@ public ProviderEvaluation getIntegerEvaluation(String key, Integer defa default: break; } - return ProviderEvaluation.builder() - .value(evaluatedValue) - .build(); + return ProviderEvaluation.builder().value(evaluatedValue).build(); } @Override @@ -176,9 +172,7 @@ public ProviderEvaluation getDoubleEvaluation(String key, Double default default: break; } - return ProviderEvaluation.builder() - .value(evaluatedValue) - .build(); + return ProviderEvaluation.builder().value(evaluatedValue).build(); } @SneakyThrows @@ -199,9 +193,7 @@ public ProviderEvaluation getObjectEvaluation(String key, Value defaultVa default: break; } - return ProviderEvaluation.builder() - .value(evaluatedValue) - .build(); + return ProviderEvaluation.builder().value(evaluatedValue).build(); } @SneakyThrows @@ -223,9 +215,8 @@ private Value toValue(DynamicConfig dynamicConfig) { List secondaryExposures = new ArrayList<>(); dynamicConfig.getSecondaryExposures().forEach(secondaryExposure -> { Value value = Value.objectToValue(secondaryExposure); - secondaryExposures.add(value); - } - ); + secondaryExposures.add(value); + }); mutableContext.add("secondaryExposures", secondaryExposures); return new Value(mutableContext); } @@ -238,17 +229,15 @@ private Value toValue(Layer layer) { mutableContext.add("groupName", layer.getGroupName()); List secondaryExposures = new ArrayList<>(); layer.getSecondaryExposures().forEach(secondaryExposure -> { - Value value = Value.objectToValue(secondaryExposure); - secondaryExposures.add(value); - } - ); + Value value = Value.objectToValue(secondaryExposure); + secondaryExposures.add(value); + }); mutableContext.add("secondaryExposures", secondaryExposures); mutableContext.add("allocatedExperiment", layer.getAllocatedExperiment()); return new Value(mutableContext); } - @NotNull - private static FeatureConfig parseFeatureConfig(EvaluationContext ctx) { + @NotNull private static FeatureConfig parseFeatureConfig(EvaluationContext ctx) { Value featureConfigValue = ctx.getValue(FEATURE_CONFIG_KEY); if (featureConfigValue == null) { throw new IllegalArgumentException("feature config not found at evaluation context."); @@ -277,20 +266,15 @@ public void shutdown() { Statsig.shutdown(); } - /** - * Feature config, as required for evaluation. - */ + /** Feature config, as required for evaluation. */ @AllArgsConstructor @Getter public static class FeatureConfig { - /** - * Type. - * CONFIG: Dynamic Config - * LAYER: Layer - */ + /** Type. CONFIG: Dynamic Config LAYER: Layer */ public enum Type { - CONFIG, LAYER + CONFIG, + LAYER } private Type type; diff --git a/providers/statsig/src/main/java/dev/openfeature/contrib/providers/statsig/StatsigProviderConfig.java b/providers/statsig/src/main/java/dev/openfeature/contrib/providers/statsig/StatsigProviderConfig.java index 52a3adcbf..0f43402f6 100644 --- a/providers/statsig/src/main/java/dev/openfeature/contrib/providers/statsig/StatsigProviderConfig.java +++ b/providers/statsig/src/main/java/dev/openfeature/contrib/providers/statsig/StatsigProviderConfig.java @@ -4,10 +4,7 @@ import lombok.Builder; import lombok.Getter; - -/** - * Configuration for initializing statsig provider. - */ +/** Configuration for initializing statsig provider. */ @Getter @Builder public class StatsigProviderConfig { diff --git a/providers/statsig/src/test/java/dev/openfeature/contrib/providers/statsig/StatsigProviderTest.java b/providers/statsig/src/test/java/dev/openfeature/contrib/providers/statsig/StatsigProviderTest.java index 4ac4721b9..f13155159 100644 --- a/providers/statsig/src/test/java/dev/openfeature/contrib/providers/statsig/StatsigProviderTest.java +++ b/providers/statsig/src/test/java/dev/openfeature/contrib/providers/statsig/StatsigProviderTest.java @@ -1,5 +1,18 @@ package dev.openfeature.contrib.providers.statsig; +import static dev.openfeature.contrib.providers.statsig.ContextTransformer.CONTEXT_APP_VERSION; +import static dev.openfeature.contrib.providers.statsig.ContextTransformer.CONTEXT_COUNTRY; +import static dev.openfeature.contrib.providers.statsig.ContextTransformer.CONTEXT_EMAIL; +import static dev.openfeature.contrib.providers.statsig.ContextTransformer.CONTEXT_IP; +import static dev.openfeature.contrib.providers.statsig.ContextTransformer.CONTEXT_LOCALE; +import static dev.openfeature.contrib.providers.statsig.ContextTransformer.CONTEXT_PRIVATE_ATTRIBUTES; +import static dev.openfeature.contrib.providers.statsig.ContextTransformer.CONTEXT_USER_AGENT; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + import com.statsig.sdk.DynamicConfig; import com.statsig.sdk.Layer; import com.statsig.sdk.Statsig; @@ -12,34 +25,18 @@ import dev.openfeature.sdk.OpenFeatureAPI; import dev.openfeature.sdk.ProviderEvaluation; import dev.openfeature.sdk.Value; -import dev.openfeature.sdk.exceptions.ProviderNotReadyError; -import lombok.SneakyThrows; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Map; - -import static dev.openfeature.contrib.providers.statsig.ContextTransformer.CONTEXT_APP_VERSION; -import static dev.openfeature.contrib.providers.statsig.ContextTransformer.CONTEXT_COUNTRY; -import static dev.openfeature.contrib.providers.statsig.ContextTransformer.CONTEXT_EMAIL; -import static dev.openfeature.contrib.providers.statsig.ContextTransformer.CONTEXT_IP; -import static dev.openfeature.contrib.providers.statsig.ContextTransformer.CONTEXT_LOCALE; -import static dev.openfeature.contrib.providers.statsig.ContextTransformer.CONTEXT_PRIVATE_ATTRIBUTES; -import static dev.openfeature.contrib.providers.statsig.ContextTransformer.CONTEXT_USER_AGENT; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; +import lombok.SneakyThrows; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; /** - * StatsigProvider test, based on local config file evaluation. - * Configuration file test by statsig tests. + * StatsigProvider test, based on local config file evaluation. Configuration file test by statsig + * tests. */ class StatsigProviderTest { @@ -65,8 +62,10 @@ static void setUp() { String sdkKey = "test"; StatsigOptions statsigOptions = new StatsigOptions(); statsigOptions.setLocalMode(true); - StatsigProviderConfig statsigProviderConfig = StatsigProviderConfig.builder().sdkKey(sdkKey) - .options(statsigOptions).build(); + StatsigProviderConfig statsigProviderConfig = StatsigProviderConfig.builder() + .sdkKey(sdkKey) + .options(statsigOptions) + .build(); statsigProvider = spy(new StatsigProvider(statsigProviderConfig)); OpenFeatureAPI.getInstance().setProviderAndWait(statsigProvider); client = OpenFeatureAPI.getInstance().getClient(); @@ -86,28 +85,44 @@ private static void buildFlags() { ArrayList> secondaryExposures = new ArrayList<>(); secondaryExposures.add(Collections.singletonMap("test-exposure", "test-exposure-value")); - DynamicConfig dynamicConfig = new DynamicConfig("object-config-name", - Collections.singletonMap("value-key", "test-value"), "test-rule-id", "test-group-name", + DynamicConfig dynamicConfig = new DynamicConfig( + "object-config-name", + Collections.singletonMap("value-key", "test-value"), + "test-rule-id", + "test-group-name", secondaryExposures); doAnswer(invocation -> { - if ("object-config-name".equals(invocation.getArgument(1, - StatsigProvider.FeatureConfig.class).getName())) { - return dynamicConfig; - } - return invocation.callRealMethod(); - }).when(statsigProvider).fetchDynamicConfig(any(), any()); - - Layer layer = new Layer("layer-name", "test-rule-id", "test-group-name", - Collections.singletonMap("value-key", "test-value"), secondaryExposures, "allocated", - null); + if ("object-config-name" + .equals(invocation + .getArgument(1, StatsigProvider.FeatureConfig.class) + .getName())) { + return dynamicConfig; + } + return invocation.callRealMethod(); + }) + .when(statsigProvider) + .fetchDynamicConfig(any(), any()); + + Layer layer = new Layer( + "layer-name", + "test-rule-id", + "test-group-name", + Collections.singletonMap("value-key", "test-value"), + secondaryExposures, + "allocated", + null); doAnswer(invocation -> { - if ("layer-name".equals(invocation.getArgument(1, StatsigProvider.FeatureConfig.class).getName())) { - return layer; - } - return invocation.callRealMethod(); - }).when(statsigProvider).fetchLayer(any(), any()); - + if ("layer-name" + .equals(invocation + .getArgument(1, StatsigProvider.FeatureConfig.class) + .getName())) { + return layer; + } + return invocation.callRealMethod(); + }) + .when(statsigProvider) + .fetchLayer(any(), any()); } @AfterAll @@ -117,15 +132,24 @@ static void shutdown() { @Test void getBooleanEvaluation() { - FlagEvaluationDetails flagEvaluationDetails = client.getBooleanDetails(FLAG_NAME, false, new ImmutableContext()); + FlagEvaluationDetails flagEvaluationDetails = + client.getBooleanDetails(FLAG_NAME, false, new ImmutableContext()); assertEquals(false, flagEvaluationDetails.getValue()); assertEquals("ERROR", flagEvaluationDetails.getReason()); MutableContext evaluationContext = new MutableContext(); evaluationContext.setTargetingKey(TARGETING_KEY); - assertEquals(true, statsigProvider.getBooleanEvaluation(FLAG_NAME, false, evaluationContext).getValue()); + assertEquals( + true, + statsigProvider + .getBooleanEvaluation(FLAG_NAME, false, evaluationContext) + .getValue()); assertEquals(true, client.getBooleanValue(FLAG_NAME, false, evaluationContext)); - assertEquals(false, statsigProvider.getBooleanEvaluation("non-existing", false, evaluationContext).getValue()); + assertEquals( + false, + statsigProvider + .getBooleanEvaluation("non-existing", false, evaluationContext) + .getValue()); assertEquals(false, client.getBooleanValue("non-existing", false, evaluationContext)); assertEquals(true, client.getBooleanValue("non-existing", true)); @@ -133,8 +157,11 @@ void getBooleanEvaluation() { featureConfig.add("type", "CONFIG"); featureConfig.add("name", "product"); evaluationContext.add("feature_config", featureConfig); - assertEquals(true, statsigProvider.getBooleanEvaluation("boolean", false, - evaluationContext).getValue()); + assertEquals( + true, + statsigProvider + .getBooleanEvaluation("boolean", false, evaluationContext) + .getValue()); } @Test @@ -145,10 +172,16 @@ void getStringEvaluation() { featureConfig.add("type", "CONFIG"); featureConfig.add("name", "product"); evaluationContext.add("feature_config", featureConfig); - assertEquals(CONFIG_FLAG_VALUE, statsigProvider.getStringEvaluation(CONFIG_FLAG_NAME, "", - evaluationContext).getValue()); - assertEquals(CONFIG_FLAG_VALUE, statsigProvider.getStringEvaluation(LAYER_FLAG_NAME, "", - evaluationContext).getValue()); + assertEquals( + CONFIG_FLAG_VALUE, + statsigProvider + .getStringEvaluation(CONFIG_FLAG_NAME, "", evaluationContext) + .getValue()); + assertEquals( + CONFIG_FLAG_VALUE, + statsigProvider + .getStringEvaluation(LAYER_FLAG_NAME, "", evaluationContext) + .getValue()); assertEquals("fallback_str", client.getStringValue("non-existing", "fallback_str")); } @@ -160,12 +193,15 @@ void getObjectConfigEvaluation() { featureConfig.add("type", "CONFIG"); featureConfig.add("name", "object-config-name"); evaluationContext.add("feature_config", featureConfig); - Value objectEvaluation = statsigProvider.getObjectEvaluation("dummy", new Value("fallback"), - evaluationContext).getValue(); - - String expectedObjectEvaluation = "{groupName=test-group-name, name=object-config-name, secondaryExposures=" + - "[{test-exposure=test-exposure-value}], ruleID=test-rule-id, value={value-key=test-value}}"; - assertEquals(expectedObjectEvaluation, objectEvaluation.asStructure().asObjectMap().toString()); + Value objectEvaluation = statsigProvider + .getObjectEvaluation("dummy", new Value("fallback"), evaluationContext) + .getValue(); + + String expectedObjectEvaluation = "{groupName=test-group-name, name=object-config-name, secondaryExposures=" + + "[{test-exposure=test-exposure-value}], ruleID=test-rule-id, value={value-key=test-value}}"; + assertEquals( + expectedObjectEvaluation, + objectEvaluation.asStructure().asObjectMap().toString()); } @Test @@ -176,13 +212,16 @@ void getObjectLayerEvaluation() { featureConfig.add("type", "LAYER"); featureConfig.add("name", "layer-name"); evaluationContext.add("feature_config", featureConfig); - Value objectEvaluation = statsigProvider.getObjectEvaluation("dummy", new Value("fallback"), - evaluationContext).getValue(); + Value objectEvaluation = statsigProvider + .getObjectEvaluation("dummy", new Value("fallback"), evaluationContext) + .getValue(); String expectedObjectEvaluation = "{groupName=test-group-name, name=layer-name, secondaryExposures=" - + "[{test-exposure=test-exposure-value}], allocatedExperiment=allocated, ruleID=test-rule-id, " - + "value={value-key=test-value}}"; - assertEquals(expectedObjectEvaluation, objectEvaluation.asStructure().asObjectMap().toString()); + + "[{test-exposure=test-exposure-value}], allocatedExperiment=allocated, ruleID=test-rule-id, " + + "value={value-key=test-value}}"; + assertEquals( + expectedObjectEvaluation, + objectEvaluation.asStructure().asObjectMap().toString()); } @Test @@ -193,10 +232,16 @@ void getIntegerEvaluation() { featureConfig.add("type", "CONFIG"); featureConfig.add("name", "product"); evaluationContext.add("feature_config", featureConfig); - assertEquals(INT_FLAG_VALUE, statsigProvider.getIntegerEvaluation(INT_FLAG_NAME, 1, - evaluationContext).getValue()); - assertEquals(INT_FLAG_VALUE, statsigProvider.getIntegerEvaluation(LAYER_INT_FLAG_NAME, 1, - evaluationContext).getValue()); + assertEquals( + INT_FLAG_VALUE, + statsigProvider + .getIntegerEvaluation(INT_FLAG_NAME, 1, evaluationContext) + .getValue()); + assertEquals( + INT_FLAG_VALUE, + statsigProvider + .getIntegerEvaluation(LAYER_INT_FLAG_NAME, 1, evaluationContext) + .getValue()); assertEquals(1, client.getIntegerValue("non-existing", 1)); // non-number flag value @@ -211,10 +256,16 @@ void getDoubleEvaluation() { featureConfig.add("type", "CONFIG"); featureConfig.add("name", "product"); evaluationContext.add("feature_config", featureConfig); - assertEquals(DOUBLE_FLAG_VALUE, statsigProvider.getDoubleEvaluation(DOUBLE_FLAG_NAME, 1.1, - evaluationContext).getValue()); - assertEquals(DOUBLE_FLAG_VALUE, statsigProvider.getDoubleEvaluation(LAYER_DOUBLE_FLAG_NAME, 1.1, - evaluationContext).getValue()); + assertEquals( + DOUBLE_FLAG_VALUE, + statsigProvider + .getDoubleEvaluation(DOUBLE_FLAG_NAME, 1.1, evaluationContext) + .getValue()); + assertEquals( + DOUBLE_FLAG_VALUE, + statsigProvider + .getDoubleEvaluation(LAYER_DOUBLE_FLAG_NAME, 1.1, evaluationContext) + .getValue()); assertEquals(1.1, client.getDoubleValue("non-existing", 1.1)); // non-number flag value @@ -233,23 +284,30 @@ void getBooleanEvaluationByUser() { evaluationContext.setTargetingKey(expectedTargetingKey); when(statsigProvider.getBooleanEvaluation(USERS_FLAG_NAME, false, evaluationContext)) - .thenAnswer(invocation -> { - if (!USERS_FLAG_NAME.equals(invocation.getArgument(0, String.class))) { - invocation.callRealMethod(); - } - boolean evaluatedValue = invocation.getArgument(2, MutableContext.class).getTargetingKey() - .equals(expectedTargetingKey); - return ProviderEvaluation.builder() - .value(evaluatedValue) - .build(); - } - ); - - assertEquals(true, statsigProvider.getBooleanEvaluation(USERS_FLAG_NAME, false, - evaluationContext).getValue()); + .thenAnswer(invocation -> { + if (!USERS_FLAG_NAME.equals(invocation.getArgument(0, String.class))) { + invocation.callRealMethod(); + } + boolean evaluatedValue = invocation + .getArgument(2, MutableContext.class) + .getTargetingKey() + .equals(expectedTargetingKey); + return ProviderEvaluation.builder() + .value(evaluatedValue) + .build(); + }); + + assertEquals( + true, + statsigProvider + .getBooleanEvaluation(USERS_FLAG_NAME, false, evaluationContext) + .getValue()); evaluationContext.setTargetingKey("other-id"); - assertEquals(false, statsigProvider.getBooleanEvaluation(USERS_FLAG_NAME, false, - evaluationContext).getValue()); + assertEquals( + false, + statsigProvider + .getBooleanEvaluation(USERS_FLAG_NAME, false, evaluationContext) + .getValue()); } @Test @@ -271,36 +329,55 @@ void getBooleanEvaluationByProperties() { evaluationContext.add(CONTEXT_PRIVATE_ATTRIBUTES, privateAttributes); when(statsigProvider.getBooleanEvaluation(PROPERTIES_FLAG_NAME, false, evaluationContext)) - .thenAnswer(invocation -> { - if (!PROPERTIES_FLAG_NAME.equals(invocation.getArgument(0, String.class))) { - invocation.callRealMethod(); - } - boolean evaluatedValue = invocation.getArgument(2, MutableContext.class) - .getValue(CONTEXT_EMAIL).asString().equals(expectedEmail); - if (invocation.getArgument(2, MutableContext.class).getValue(CONTEXT_PRIVATE_ATTRIBUTES) - .asStructure().getValue(CONTEXT_IP).asString().equals(expectedIp)) { - evaluatedValue = true; - } - return ProviderEvaluation.builder() - .value(evaluatedValue) - .build(); - } - ); - - assertEquals(true, statsigProvider.getBooleanEvaluation(PROPERTIES_FLAG_NAME, false, - evaluationContext).getValue()); + .thenAnswer(invocation -> { + if (!PROPERTIES_FLAG_NAME.equals(invocation.getArgument(0, String.class))) { + invocation.callRealMethod(); + } + boolean evaluatedValue = invocation + .getArgument(2, MutableContext.class) + .getValue(CONTEXT_EMAIL) + .asString() + .equals(expectedEmail); + if (invocation + .getArgument(2, MutableContext.class) + .getValue(CONTEXT_PRIVATE_ATTRIBUTES) + .asStructure() + .getValue(CONTEXT_IP) + .asString() + .equals(expectedIp)) { + evaluatedValue = true; + } + return ProviderEvaluation.builder() + .value(evaluatedValue) + .build(); + }); + + assertEquals( + true, + statsigProvider + .getBooleanEvaluation(PROPERTIES_FLAG_NAME, false, evaluationContext) + .getValue()); evaluationContext.add(CONTEXT_EMAIL, "non-match@test.com"); - assertEquals(false, statsigProvider.getBooleanEvaluation(PROPERTIES_FLAG_NAME, false, - evaluationContext).getValue()); + assertEquals( + false, + statsigProvider + .getBooleanEvaluation(PROPERTIES_FLAG_NAME, false, evaluationContext) + .getValue()); privateAttributes.add(CONTEXT_IP, expectedIp); - assertEquals(true, statsigProvider.getBooleanEvaluation(PROPERTIES_FLAG_NAME, false, - evaluationContext).getValue()); + assertEquals( + true, + statsigProvider + .getBooleanEvaluation(PROPERTIES_FLAG_NAME, false, evaluationContext) + .getValue()); privateAttributes.add(CONTEXT_IP, "1.2.3.5"); - assertEquals(false, statsigProvider.getBooleanEvaluation(PROPERTIES_FLAG_NAME, false, - evaluationContext).getValue()); + assertEquals( + false, + statsigProvider + .getBooleanEvaluation(PROPERTIES_FLAG_NAME, false, evaluationContext) + .getValue()); } - + @SneakyThrows @Test void contextTransformTest() { @@ -328,7 +405,7 @@ void contextTransformTest() { evaluationContext.add(customPropertyKey, customPropertyValue); - HashMap customMap = new HashMap<>(); + HashMap customMap = new HashMap<>(); customMap.put(customPropertyKey, customPropertyValue); StatsigUser expectedUser = new StatsigUser(evaluationContext.getTargetingKey()); expectedUser.setEmail(email); @@ -345,5 +422,4 @@ void contextTransformTest() { // equals not implemented for User, using toString assertEquals(expectedUser.toString(), transformedUser.toString()); } - -} \ No newline at end of file +} diff --git a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/ContextTransformer.java b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/ContextTransformer.java index 2e8f68806..e831324ed 100644 --- a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/ContextTransformer.java +++ b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/ContextTransformer.java @@ -2,12 +2,9 @@ import dev.openfeature.sdk.EvaluationContext; import io.getunleash.UnleashContext; - import java.time.ZonedDateTime; -/** - * Transformer from Unleash context to OpenFeature context and vice versa. - */ +/** Transformer from Unleash context to OpenFeature context and vice versa. */ public class ContextTransformer { public static final String CONTEXT_APP_NAME = "appName"; @@ -46,5 +43,4 @@ protected static UnleashContext transform(EvaluationContext ctx) { }); return unleashContextBuilder.build(); } - } diff --git a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java index 436c131df..2a4411752 100644 --- a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java +++ b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProvider.java @@ -2,8 +2,6 @@ import static io.getunleash.Variant.DISABLED_VARIANT; -import java.util.concurrent.atomic.AtomicBoolean; - import dev.openfeature.sdk.EvaluationContext; import dev.openfeature.sdk.EventProvider; import dev.openfeature.sdk.ImmutableMetadata; @@ -16,21 +14,21 @@ import io.getunleash.UnleashContext; import io.getunleash.Variant; import io.getunleash.util.UnleashConfig; +import java.util.concurrent.atomic.AtomicBoolean; import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; -/** - * Provider implementation for Unleash. - */ +/** Provider implementation for Unleash. */ @Slf4j public class UnleashProvider extends EventProvider { @Getter private static final String NAME = "Unleash"; + public static final String NOT_IMPLEMENTED = - "Not implemented - provider does not support this type. Only boolean is supported."; + "Not implemented - provider does not support this type. Only boolean is supported."; public static final String PROVIDER_NOT_YET_INITIALIZED = "provider not yet initialized"; public static final String UNKNOWN_ERROR = "unknown error"; @@ -46,6 +44,7 @@ public class UnleashProvider extends EventProvider { /** * Constructor. + * * @param unleashProviderConfig UnleashProviderConfig */ public UnleashProvider(UnleashProviderConfig unleashProviderConfig) { @@ -54,6 +53,7 @@ public UnleashProvider(UnleashProviderConfig unleashProviderConfig) { /** * Initialize the provider. + * * @param evaluationContext evaluation context * @throws Exception on error */ @@ -65,9 +65,10 @@ public void initialize(EvaluationContext evaluationContext) throws Exception { } super.initialize(evaluationContext); UnleashSubscriberWrapper unleashSubscriberWrapper = new UnleashSubscriberWrapper( - unleashProviderConfig.getUnleashConfigBuilder().build().getSubscriber(), this); + unleashProviderConfig.getUnleashConfigBuilder().build().getSubscriber(), this); unleashProviderConfig.getUnleashConfigBuilder().subscriber(unleashSubscriberWrapper); - UnleashConfig unleashConfig = unleashProviderConfig.getUnleashConfigBuilder().build(); + UnleashConfig unleashConfig = + unleashProviderConfig.getUnleashConfigBuilder().build(); unleash = new DefaultUnleash(unleashConfig); // Unleash is per definition ready after it is initialized. @@ -79,26 +80,23 @@ public Metadata getMetadata() { return () -> NAME; } - @Override public ProviderEvaluation getBooleanEvaluation(String key, Boolean defaultValue, EvaluationContext ctx) { UnleashContext context = ctx == null ? UnleashContext.builder().build() : ContextTransformer.transform(ctx); boolean featureBooleanValue = unleash.isEnabled(key, context, defaultValue); - return ProviderEvaluation.builder() - .value(featureBooleanValue) - .build(); + return ProviderEvaluation.builder().value(featureBooleanValue).build(); } @Override public ProviderEvaluation getStringEvaluation(String key, String defaultValue, EvaluationContext ctx) { ProviderEvaluation valueProviderEvaluation = getObjectEvaluation(key, new Value(defaultValue), ctx); return ProviderEvaluation.builder() - .value(valueProviderEvaluation.getValue().asString()) - .variant(valueProviderEvaluation.getVariant()) + .value(valueProviderEvaluation.getValue().asString()) + .variant(valueProviderEvaluation.getVariant()) .errorCode(valueProviderEvaluation.getErrorCode()) .reason(valueProviderEvaluation.getReason()) .flagMetadata(valueProviderEvaluation.getFlagMetadata()) - .build(); + .build(); } @Override @@ -106,12 +104,12 @@ public ProviderEvaluation getIntegerEvaluation(String key, Integer defa ProviderEvaluation valueProviderEvaluation = getObjectEvaluation(key, new Value(defaultValue), ctx); Integer value = getIntegerValue(valueProviderEvaluation, defaultValue); return ProviderEvaluation.builder() - .value(value) - .variant(valueProviderEvaluation.getVariant()) - .errorCode(valueProviderEvaluation.getErrorCode()) - .reason(valueProviderEvaluation.getReason()) - .flagMetadata(valueProviderEvaluation.getFlagMetadata()) - .build(); + .value(value) + .variant(valueProviderEvaluation.getVariant()) + .errorCode(valueProviderEvaluation.getErrorCode()) + .reason(valueProviderEvaluation.getReason()) + .flagMetadata(valueProviderEvaluation.getFlagMetadata()) + .build(); } private static Integer getIntegerValue(ProviderEvaluation valueProviderEvaluation, Integer defaultValue) { @@ -128,12 +126,12 @@ public ProviderEvaluation getDoubleEvaluation(String key, Double default ProviderEvaluation valueProviderEvaluation = getObjectEvaluation(key, new Value(defaultValue), ctx); Double value = getDoubleValue(valueProviderEvaluation, defaultValue); return ProviderEvaluation.builder() - .value(value) - .variant(valueProviderEvaluation.getVariant()) - .errorCode(valueProviderEvaluation.getErrorCode()) - .reason(valueProviderEvaluation.getReason()) - .flagMetadata(valueProviderEvaluation.getFlagMetadata()) - .build(); + .value(value) + .variant(valueProviderEvaluation.getVariant()) + .errorCode(valueProviderEvaluation.getErrorCode()) + .reason(valueProviderEvaluation.getReason()) + .flagMetadata(valueProviderEvaluation.getFlagMetadata()) + .build(); } private static Double getDoubleValue(ProviderEvaluation valueProviderEvaluation, Double defaultValue) { @@ -156,19 +154,23 @@ public ProviderEvaluation getObjectEvaluation(String key, Value defaultVa value = defaultValue; } else { variantName = evaluatedVariant.getName(); - value = evaluatedVariant.getPayload().map(p -> new Value(p.getValue())).orElse(null); + value = evaluatedVariant + .getPayload() + .map(p -> new Value(p.getValue())) + .orElse(null); } - ImmutableMetadata.ImmutableMetadataBuilder flagMetadataBuilder = ImmutableMetadata.builder() - .addString("variant-stickiness", evaluatedVariant.getStickiness()); + ImmutableMetadata.ImmutableMetadataBuilder flagMetadataBuilder = + ImmutableMetadata.builder().addString("variant-stickiness", evaluatedVariant.getStickiness()); flagMetadataBuilder.addBoolean("enabled", evaluatedVariant.isEnabled()); if (evaluatedVariant.getPayload().isPresent()) { - flagMetadataBuilder.addString("payload-type", evaluatedVariant.getPayload().get().getType()); + flagMetadataBuilder.addString( + "payload-type", evaluatedVariant.getPayload().get().getType()); } return ProviderEvaluation.builder() - .value(value) - .variant(variantName) - .flagMetadata(flagMetadataBuilder.build()) - .build(); + .value(value) + .variant(variantName) + .flagMetadata(flagMetadataBuilder.build()) + .build(); } @Override diff --git a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProviderConfig.java b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProviderConfig.java index 39cb9b87e..4f217c178 100644 --- a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProviderConfig.java +++ b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashProviderConfig.java @@ -4,10 +4,7 @@ import lombok.Builder; import lombok.Getter; - -/** - * Options for initializing Unleash provider. - */ +/** Options for initializing Unleash provider. */ @Getter @Builder public class UnleashProviderConfig { diff --git a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashSubscriberWrapper.java b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashSubscriberWrapper.java index 92c0d5949..8c8c1bafe 100644 --- a/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashSubscriberWrapper.java +++ b/providers/unleash/src/main/java/dev/openfeature/contrib/providers/unleash/UnleashSubscriberWrapper.java @@ -1,7 +1,5 @@ package dev.openfeature.contrib.providers.unleash; -import javax.annotation.Nullable; - import dev.openfeature.sdk.EventProvider; import dev.openfeature.sdk.ImmutableMetadata; import dev.openfeature.sdk.ProviderEventDetails; @@ -17,12 +15,11 @@ import io.getunleash.repository.FeatureCollection; import io.getunleash.repository.FeatureToggleResponse; import io.getunleash.repository.ToggleCollection; +import javax.annotation.Nullable; import lombok.Generated; import lombok.extern.slf4j.Slf4j; -/** - * UnleashSubscriber wrapper for emitting event provider events. - */ +/** UnleashSubscriber wrapper for emitting event provider events. */ @Slf4j @Generated public class UnleashSubscriberWrapper implements UnleashSubscriber { @@ -32,11 +29,11 @@ public class UnleashSubscriberWrapper implements UnleashSubscriber { /** * Constructor. - * + * * @param unleashSubscriber subscriber - * @param eventProvider events provider for emitting events. + * @param eventProvider events provider for emitting events. */ - @SuppressFBWarnings(value = { "EI_EXPOSE_REP" }) + @SuppressFBWarnings(value = {"EI_EXPOSE_REP"}) public UnleashSubscriberWrapper(@Nullable UnleashSubscriber unleashSubscriber, EventProvider eventProvider) { this.unleashSubscriber = unleashSubscriber; this.eventProvider = eventProvider; @@ -47,7 +44,8 @@ public void onError(UnleashException unleashException) { unleashSubscriber.onError(unleashException); log.info("unleashException: ", unleashException); - // Not emitting provider error, since some unleashException not expects to change provider state to error + // Not emitting provider error, since some unleashException not expects to change provider state + // to error } @Override @@ -59,8 +57,8 @@ public void on(UnleashEvent unleashEvent) { public void onReady(UnleashReady unleashReady) { unleashSubscriber.onReady(unleashReady); eventProvider.emitProviderReady(ProviderEventDetails.builder() - .eventMetadata(ImmutableMetadata.builder() - .build()).build()); + .eventMetadata(ImmutableMetadata.builder().build()) + .build()); } @Override @@ -73,8 +71,8 @@ public void togglesFetched(FeatureToggleResponse toggleResponse) { unleashSubscriber.togglesFetched(toggleResponse); if (FeatureToggleResponse.Status.CHANGED.equals(toggleResponse.getStatus())) { eventProvider.emitProviderConfigurationChanged(ProviderEventDetails.builder() - .eventMetadata(ImmutableMetadata.builder() - .build()).build()); + .eventMetadata(ImmutableMetadata.builder().build()) + .build()); } } diff --git a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java index ff2d68c2f..efb27f805 100644 --- a/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java +++ b/providers/unleash/src/test/java/dev/openfeature/contrib/providers/unleash/UnleashProviderTest.java @@ -10,22 +10,8 @@ import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static org.junit.jupiter.api.Assertions.assertEquals; -import java.net.URI; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.List; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; - import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; import com.github.tomakehurst.wiremock.junit5.WireMockTest; - import dev.openfeature.sdk.Client; import dev.openfeature.sdk.ImmutableContext; import dev.openfeature.sdk.ImmutableMetadata; @@ -40,12 +26,20 @@ import io.getunleash.event.UnleashSubscriber; import io.getunleash.repository.FeatureToggleResponse; import io.getunleash.util.UnleashConfig; +import java.net.URI; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.List; import lombok.SneakyThrows; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; -/** - * UnleashProvider test, based on APIs mocking. - * Inspired by Unleash tests. - */ +/** UnleashProvider test, based on APIs mocking. Inspired by Unleash tests. */ @WireMockTest @TestInstance(TestInstance.Lifecycle.PER_CLASS) class UnleashProviderTest { @@ -67,9 +61,7 @@ class UnleashProviderTest { @BeforeAll void setUp(WireMockRuntimeInfo wmRuntimeInfo) { - stubFor(any(anyUrl()).willReturn(aResponse() - .withStatus(200) - .withBody("{}"))); + stubFor(any(anyUrl()).willReturn(aResponse().withStatus(200).withBody("{}"))); String unleashAPI = "http://localhost:" + wmRuntimeInfo.getHttpPort() + "/api/"; String backupFileContent = readBackupFile(); mockUnleashAPI(backupFileContent); @@ -84,11 +76,9 @@ public void shutdown() { } private void mockUnleashAPI(String backupFileContent) { - stubFor( - get(urlEqualTo("/api/client/features")) + stubFor(get(urlEqualTo("/api/client/features")) .withHeader("Accept", equalTo("application/json")) - .willReturn( - aResponse() + .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") .withBody(backupFileContent))); @@ -96,16 +86,17 @@ private void mockUnleashAPI(String backupFileContent) { } @SneakyThrows - private UnleashProvider buildUnleashProvider(boolean synchronousFetchOnInitialisation, String unleashAPI, TestSubscriber testSubscriber) { - UnleashConfig.Builder unleashConfigBuilder = - UnleashConfig.builder().unleashAPI(new URI(unleashAPI)) + private UnleashProvider buildUnleashProvider( + boolean synchronousFetchOnInitialisation, String unleashAPI, TestSubscriber testSubscriber) { + UnleashConfig.Builder unleashConfigBuilder = UnleashConfig.builder() + .unleashAPI(new URI(unleashAPI)) .appName("fakeApp") .subscriber(testSubscriber) .synchronousFetchOnInitialisation(synchronousFetchOnInitialisation); UnleashProviderConfig unleashProviderConfig = UnleashProviderConfig.builder() - .unleashConfigBuilder(unleashConfigBuilder) - .build(); + .unleashConfigBuilder(unleashConfigBuilder) + .build(); return new UnleashProvider(unleashProviderConfig); } @@ -117,19 +108,33 @@ private String readBackupFile() { @Test void getBooleanEvaluation() { - assertEquals(true, unleashProvider.getBooleanEvaluation(FLAG_NAME, false, new ImmutableContext()).getValue()); + assertEquals( + true, + unleashProvider + .getBooleanEvaluation(FLAG_NAME, false, new ImmutableContext()) + .getValue()); assertEquals(true, client.getBooleanValue(FLAG_NAME, false)); - assertEquals(false, unleashProvider.getBooleanEvaluation("non-existing", false, new ImmutableContext()).getValue()); + assertEquals( + false, + unleashProvider + .getBooleanEvaluation("non-existing", false, new ImmutableContext()) + .getValue()); assertEquals(false, client.getBooleanValue("non-existing", false)); } @Test void getStringVariantEvaluation() { - assertEquals(VARIANT_FLAG_VALUE, unleashProvider.getStringEvaluation(VARIANT_FLAG_NAME, "", - new ImmutableContext()).getValue()); + assertEquals( + VARIANT_FLAG_VALUE, + unleashProvider + .getStringEvaluation(VARIANT_FLAG_NAME, "", new ImmutableContext()) + .getValue()); assertEquals(VARIANT_FLAG_VALUE, client.getStringValue(VARIANT_FLAG_NAME, "")); - assertEquals("fallback_str", unleashProvider.getStringEvaluation("non-existing", - "fallback_str", new ImmutableContext()).getValue()); + assertEquals( + "fallback_str", + unleashProvider + .getStringEvaluation("non-existing", "fallback_str", new ImmutableContext()) + .getValue()); assertEquals("fallback_str", client.getStringValue("non-existing", "fallback_str")); } @@ -137,8 +142,11 @@ void getStringVariantEvaluation() { void getIntegerEvaluation() { MutableContext evaluationContext = new MutableContext(); evaluationContext.add("userId", "int"); - assertEquals(INT_FLAG_VALUE, unleashProvider.getIntegerEvaluation(INT_FLAG_NAME, 1, - evaluationContext).getValue()); + assertEquals( + INT_FLAG_VALUE, + unleashProvider + .getIntegerEvaluation(INT_FLAG_NAME, 1, evaluationContext) + .getValue()); assertEquals(INT_FLAG_VALUE, client.getIntegerValue(INT_FLAG_NAME, 1)); assertEquals(1, client.getIntegerValue("non-existing", 1)); @@ -150,8 +158,11 @@ void getIntegerEvaluation() { void getDoubleEvaluation() { MutableContext evaluationContext = new MutableContext(); evaluationContext.add("userId", "double"); - assertEquals(DOUBLE_FLAG_VALUE, unleashProvider.getDoubleEvaluation(DOUBLE_FLAG_NAME, 1.1, - evaluationContext).getValue()); + assertEquals( + DOUBLE_FLAG_VALUE, + unleashProvider + .getDoubleEvaluation(DOUBLE_FLAG_NAME, 1.1, evaluationContext) + .getValue()); assertEquals(DOUBLE_FLAG_VALUE, client.getDoubleValue(DOUBLE_FLAG_NAME, 1.1)); assertEquals(1.1, client.getDoubleValue("non-existing", 1.1)); @@ -161,21 +172,37 @@ void getDoubleEvaluation() { @Test void getJsonVariantEvaluation() { - assertEquals(JSON_VARIANT_FLAG_VALUE, unleashProvider.getObjectEvaluation(JSON_VARIANT_FLAG_NAME, new Value(""), - new ImmutableContext()).getValue().asString()); + assertEquals( + JSON_VARIANT_FLAG_VALUE, + unleashProvider + .getObjectEvaluation(JSON_VARIANT_FLAG_NAME, new Value(""), new ImmutableContext()) + .getValue() + .asString()); assertEquals(new Value(JSON_VARIANT_FLAG_VALUE), client.getObjectValue(JSON_VARIANT_FLAG_NAME, new Value(""))); - assertEquals("fallback_str", unleashProvider.getObjectEvaluation("non-existing", - new Value("fallback_str"), new ImmutableContext()).getValue().asString()); + assertEquals( + "fallback_str", + unleashProvider + .getObjectEvaluation("non-existing", new Value("fallback_str"), new ImmutableContext()) + .getValue() + .asString()); assertEquals(new Value("fallback_str"), client.getObjectValue("non-existing", new Value("fallback_str"))); } @Test void getCSVVariantEvaluation() { - assertEquals(CSV_VARIANT_FLAG_VALUE, unleashProvider.getObjectEvaluation(CSV_VARIANT_FLAG_NAME, new Value(""), - new ImmutableContext()).getValue().asString()); + assertEquals( + CSV_VARIANT_FLAG_VALUE, + unleashProvider + .getObjectEvaluation(CSV_VARIANT_FLAG_NAME, new Value(""), new ImmutableContext()) + .getValue() + .asString()); assertEquals(new Value(CSV_VARIANT_FLAG_VALUE), client.getObjectValue(CSV_VARIANT_FLAG_NAME, new Value(""))); - assertEquals("fallback_str", unleashProvider.getObjectEvaluation("non-existing", - new Value("fallback_str"), new ImmutableContext()).getValue().asString()); + assertEquals( + "fallback_str", + unleashProvider + .getObjectEvaluation("non-existing", new Value("fallback_str"), new ImmutableContext()) + .getValue() + .asString()); assertEquals(new Value("fallback_str"), client.getObjectValue("non-existing", new Value("fallback_str"))); } @@ -183,23 +210,31 @@ void getCSVVariantEvaluation() { void getBooleanEvaluationByUser() { MutableContext evaluationContext = new MutableContext(); evaluationContext.add("userId", "111"); - assertEquals(true, unleashProvider.getBooleanEvaluation(USERS_FLAG_NAME, false, evaluationContext).getValue()); + assertEquals( + true, + unleashProvider + .getBooleanEvaluation(USERS_FLAG_NAME, false, evaluationContext) + .getValue()); assertEquals(true, client.getBooleanValue(USERS_FLAG_NAME, false, evaluationContext)); evaluationContext.add("userId", "2"); - assertEquals(false, unleashProvider.getBooleanEvaluation(USERS_FLAG_NAME, false, evaluationContext).getValue()); + assertEquals( + false, + unleashProvider + .getBooleanEvaluation(USERS_FLAG_NAME, false, evaluationContext) + .getValue()); assertEquals(false, client.getBooleanValue(USERS_FLAG_NAME, false, evaluationContext)); } @Test void getEvaluationMetadataTest() { - ProviderEvaluation stringEvaluation = unleashProvider.getStringEvaluation(VARIANT_FLAG_NAME, "", - new ImmutableContext()); + ProviderEvaluation stringEvaluation = + unleashProvider.getStringEvaluation(VARIANT_FLAG_NAME, "", new ImmutableContext()); ImmutableMetadata flagMetadata = stringEvaluation.getFlagMetadata(); assertEquals("default", flagMetadata.getString("variant-stickiness")); assertEquals("string", flagMetadata.getString("payload-type")); assertEquals(true, flagMetadata.getBoolean("enabled")); - ProviderEvaluation nonExistingFlagEvaluation = unleashProvider.getStringEvaluation("non-existing", - "", new ImmutableContext()); + ProviderEvaluation nonExistingFlagEvaluation = + unleashProvider.getStringEvaluation("non-existing", "", new ImmutableContext()); assertEquals(false, nonExistingFlagEvaluation.getFlagMetadata().getBoolean("enabled")); } @@ -227,20 +262,23 @@ void contextTransformTest() { UnleashContext transformedUnleashContext = ContextTransformer.transform(evaluationContext); assertEquals(appNameValue, transformedUnleashContext.getAppName().get()); assertEquals(userIdValue, transformedUnleashContext.getUserId().get()); - assertEquals(environmentValue, transformedUnleashContext.getEnvironment().get()); - assertEquals(remoteAddressValue, transformedUnleashContext.getRemoteAddress().get()); + assertEquals( + environmentValue, transformedUnleashContext.getEnvironment().get()); + assertEquals( + remoteAddressValue, transformedUnleashContext.getRemoteAddress().get()); assertEquals(sessionIdValue, transformedUnleashContext.getSessionId().get()); - assertEquals(currentTimeValue, transformedUnleashContext.getCurrentTime().get()); - assertEquals(customPropertyValue, transformedUnleashContext.getProperties().get(customPropertyKey)); + assertEquals( + currentTimeValue, transformedUnleashContext.getCurrentTime().get()); + assertEquals( + customPropertyValue, transformedUnleashContext.getProperties().get(customPropertyKey)); } @SneakyThrows @Test void subscriberWrapperTest() { - UnleashProvider asyncInitUnleashProvider = buildUnleashProvider(false, - "http://fakeAPI", null); - UnleashSubscriberWrapper unleashSubscriberWrapper = new UnleashSubscriberWrapper( - new TestSubscriber(), asyncInitUnleashProvider); + UnleashProvider asyncInitUnleashProvider = buildUnleashProvider(false, "http://fakeAPI", null); + UnleashSubscriberWrapper unleashSubscriberWrapper = + new UnleashSubscriberWrapper(new TestSubscriber(), asyncInitUnleashProvider); unleashSubscriberWrapper.clientMetrics(null); unleashSubscriberWrapper.clientRegistered(null); unleashSubscriberWrapper.featuresBackedUp(null); @@ -248,8 +286,8 @@ void subscriberWrapperTest() { unleashSubscriberWrapper.featuresBootstrapped(null); unleashSubscriberWrapper.impression(null); unleashSubscriberWrapper.toggleEvaluated(new ToggleEvaluated("dummy", false)); - unleashSubscriberWrapper.togglesFetched(new FeatureToggleResponse(FeatureToggleResponse.Status.NOT_CHANGED, -200)); + unleashSubscriberWrapper.togglesFetched( + new FeatureToggleResponse(FeatureToggleResponse.Status.NOT_CHANGED, 200)); unleashSubscriberWrapper.toggleBackupRestored(null); unleashSubscriberWrapper.togglesBackedUp(null); unleashSubscriberWrapper.togglesBootstrapped(null); @@ -286,4 +324,4 @@ public void togglesFetched(FeatureToggleResponse toggleResponse) { this.status = toggleResponse.getStatus(); } } -} \ No newline at end of file +} diff --git a/tools/junit-openfeature/src/main/java/dev/openfeature/contrib/tools/junitopenfeature/Flag.java b/tools/junit-openfeature/src/main/java/dev/openfeature/contrib/tools/junitopenfeature/Flag.java index 5870c60cd..249fd171c 100644 --- a/tools/junit-openfeature/src/main/java/dev/openfeature/contrib/tools/junitopenfeature/Flag.java +++ b/tools/junit-openfeature/src/main/java/dev/openfeature/contrib/tools/junitopenfeature/Flag.java @@ -1,12 +1,11 @@ package dev.openfeature.contrib.tools.junitopenfeature; -import org.junit.jupiter.api.extension.ExtendWith; - import java.lang.annotation.ElementType; import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.junit.jupiter.api.extension.ExtendWith; /** * Annotation for Flag Configuration for the default domain. @@ -32,5 +31,3 @@ */ Class valueType() default Boolean.class; } - - diff --git a/tools/junit-openfeature/src/main/java/dev/openfeature/contrib/tools/junitopenfeature/Flags.java b/tools/junit-openfeature/src/main/java/dev/openfeature/contrib/tools/junitopenfeature/Flags.java index eecdba428..3305c348d 100644 --- a/tools/junit-openfeature/src/main/java/dev/openfeature/contrib/tools/junitopenfeature/Flags.java +++ b/tools/junit-openfeature/src/main/java/dev/openfeature/contrib/tools/junitopenfeature/Flags.java @@ -1,11 +1,10 @@ package dev.openfeature.contrib.tools.junitopenfeature; -import org.junit.jupiter.api.extension.ExtendWith; - import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.junit.jupiter.api.extension.ExtendWith; /** * Collection of {@link Flag} configurations. @@ -19,5 +18,3 @@ */ Flag[] value() default {}; } - - diff --git a/tools/junit-openfeature/src/main/java/dev/openfeature/contrib/tools/junitopenfeature/OpenFeature.java b/tools/junit-openfeature/src/main/java/dev/openfeature/contrib/tools/junitopenfeature/OpenFeature.java index aaae83302..2e7ab0586 100644 --- a/tools/junit-openfeature/src/main/java/dev/openfeature/contrib/tools/junitopenfeature/OpenFeature.java +++ b/tools/junit-openfeature/src/main/java/dev/openfeature/contrib/tools/junitopenfeature/OpenFeature.java @@ -1,18 +1,17 @@ package dev.openfeature.contrib.tools.junitopenfeature; -import org.junit.jupiter.api.extension.ExtendWith; - import java.lang.annotation.ElementType; import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.junit.jupiter.api.extension.ExtendWith; /** * Annotation for generating an extended configuration for OpenFeature. * This annotation allows you to specify a list of flags for a specific domain. */ -@Target({ ElementType.METHOD, ElementType.TYPE }) +@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Repeatable(value = OpenFeatures.class) @ExtendWith(OpenFeatureExtension.class) @@ -26,5 +25,3 @@ */ Flag[] value(); } - - diff --git a/tools/junit-openfeature/src/main/java/dev/openfeature/contrib/tools/junitopenfeature/OpenFeatureDefaultDomain.java b/tools/junit-openfeature/src/main/java/dev/openfeature/contrib/tools/junitopenfeature/OpenFeatureDefaultDomain.java index f88fcbbe4..3f7ab1999 100644 --- a/tools/junit-openfeature/src/main/java/dev/openfeature/contrib/tools/junitopenfeature/OpenFeatureDefaultDomain.java +++ b/tools/junit-openfeature/src/main/java/dev/openfeature/contrib/tools/junitopenfeature/OpenFeatureDefaultDomain.java @@ -1,11 +1,10 @@ package dev.openfeature.contrib.tools.junitopenfeature; -import org.junit.jupiter.api.extension.ExtendWith; - import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.junit.jupiter.api.extension.ExtendWith; /** * Configuration of a default domain for standalone {@link Flag} configurations. @@ -19,5 +18,3 @@ */ String value() default ""; } - - diff --git a/tools/junit-openfeature/src/main/java/dev/openfeature/contrib/tools/junitopenfeature/OpenFeatureExtension.java b/tools/junit-openfeature/src/main/java/dev/openfeature/contrib/tools/junitopenfeature/OpenFeatureExtension.java index 00f8bb82b..cebf5e6b0 100644 --- a/tools/junit-openfeature/src/main/java/dev/openfeature/contrib/tools/junitopenfeature/OpenFeatureExtension.java +++ b/tools/junit-openfeature/src/main/java/dev/openfeature/contrib/tools/junitopenfeature/OpenFeatureExtension.java @@ -2,6 +2,10 @@ import dev.openfeature.sdk.OpenFeatureAPI; import dev.openfeature.sdk.providers.memory.Flag; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; import org.apache.commons.lang3.BooleanUtils; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; @@ -10,11 +14,6 @@ import org.junit.jupiter.api.extension.ReflectiveInvocationContext; import org.junitpioneer.internal.PioneerAnnotationUtils; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - /** * JUnit5 Extension for OpenFeature. */ @@ -23,11 +22,8 @@ public class OpenFeatureExtension implements BeforeEachCallback, AfterEachCallba OpenFeatureAPI api = OpenFeatureAPI.getInstance(); private static Map>> handleExtendedConfiguration( - ExtensionContext extensionContext, - Map>> configuration - ) { - PioneerAnnotationUtils - .findAllEnclosingRepeatableAnnotations(extensionContext, OpenFeature.class) + ExtensionContext extensionContext, Map>> configuration) { + PioneerAnnotationUtils.findAllEnclosingRepeatableAnnotations(extensionContext, OpenFeature.class) .forEachOrdered(annotation -> { Map> domainFlags = configuration.getOrDefault(annotation.domain(), new HashMap<>()); @@ -44,13 +40,12 @@ private static Map>> handleExtendedConfiguration( private static Map>> handleSimpleConfiguration(ExtensionContext extensionContext) { Map>> configuration = new HashMap<>(); - String defaultDomain = PioneerAnnotationUtils - .findClosestEnclosingAnnotation(extensionContext, OpenFeatureDefaultDomain.class) - .map(OpenFeatureDefaultDomain::value).orElse(""); - PioneerAnnotationUtils - .findAllEnclosingRepeatableAnnotations( - extensionContext, - dev.openfeature.contrib.tools.junitopenfeature.Flag.class) + String defaultDomain = PioneerAnnotationUtils.findClosestEnclosingAnnotation( + extensionContext, OpenFeatureDefaultDomain.class) + .map(OpenFeatureDefaultDomain::value) + .orElse(""); + PioneerAnnotationUtils.findAllEnclosingRepeatableAnnotations( + extensionContext, dev.openfeature.contrib.tools.junitopenfeature.Flag.class) .forEachOrdered(flag -> { Map> domainFlags = configuration.getOrDefault(defaultDomain, new HashMap<>()); if (!domainFlags.containsKey(flag.name())) { @@ -63,9 +58,7 @@ private static Map>> handleSimpleConfiguration(Exten return configuration; } - private static Flag.FlagBuilder generateFlagBuilder( - dev.openfeature.contrib.tools.junitopenfeature.Flag flag - ) { + private static Flag.FlagBuilder generateFlagBuilder(dev.openfeature.contrib.tools.junitopenfeature.Flag flag) { Flag.FlagBuilder builder; switch (flag.valueType().getSimpleName()) { case "Boolean": @@ -95,8 +88,8 @@ private static Flag.FlagBuilder generateFlagBuilder( public void interceptTestMethod( Invocation invocation, ReflectiveInvocationContext invocationContext, - ExtensionContext extensionContext - ) throws Throwable { + ExtensionContext extensionContext) + throws Throwable { executeWithNamespace(invocation, extensionContext); } @@ -104,13 +97,13 @@ public void interceptTestMethod( public void interceptTestTemplateMethod( Invocation invocation, ReflectiveInvocationContext invocationContext, - ExtensionContext extensionContext) throws Throwable { + ExtensionContext extensionContext) + throws Throwable { executeWithNamespace(invocation, extensionContext); } @Override - public void afterEach(ExtensionContext extensionContext) throws Exception { - } + public void afterEach(ExtensionContext extensionContext) throws Exception {} @Override public void beforeEach(ExtensionContext extensionContext) throws Exception { @@ -125,41 +118,31 @@ public void beforeEach(ExtensionContext extensionContext) throws Exception { ((TestProvider) api.getProvider(domain)) .addConfigurationForTest(getNamespace(extensionContext), stringMapEntry.getValue()); } else { - api.setProviderAndWait(domain, new TestProvider( - getNamespace(extensionContext), - stringMapEntry.getValue())); + api.setProviderAndWait( + domain, new TestProvider(getNamespace(extensionContext), stringMapEntry.getValue())); } } else { if (api.getProvider() instanceof TestProvider) { ((TestProvider) api.getProvider()) .addConfigurationForTest(getNamespace(extensionContext), stringMapEntry.getValue()); } else { - api.setProviderAndWait(new TestProvider( - getNamespace(extensionContext), - stringMapEntry.getValue())); + api.setProviderAndWait(new TestProvider(getNamespace(extensionContext), stringMapEntry.getValue())); } } - } getStore(extensionContext).put("config", configuration); - } private ExtensionContext.Namespace getNamespace(ExtensionContext extensionContext) { - return ExtensionContext.Namespace.create( - getClass(), - extensionContext.getRequiredTestMethod() - ); + return ExtensionContext.Namespace.create(getClass(), extensionContext.getRequiredTestMethod()); } private ExtensionContext.Store getStore(ExtensionContext context) { return context.getStore(ExtensionContext.Namespace.create(getClass())); } - private void executeWithNamespace( - Invocation invocation, - ExtensionContext extensionContext) throws Throwable { + private void executeWithNamespace(Invocation invocation, ExtensionContext extensionContext) throws Throwable { TestProvider.setCurrentNamespace(getNamespace(extensionContext)); try { invocation.proceed(); diff --git a/tools/junit-openfeature/src/main/java/dev/openfeature/contrib/tools/junitopenfeature/OpenFeatures.java b/tools/junit-openfeature/src/main/java/dev/openfeature/contrib/tools/junitopenfeature/OpenFeatures.java index c06002378..d5dd5e598 100644 --- a/tools/junit-openfeature/src/main/java/dev/openfeature/contrib/tools/junitopenfeature/OpenFeatures.java +++ b/tools/junit-openfeature/src/main/java/dev/openfeature/contrib/tools/junitopenfeature/OpenFeatures.java @@ -1,11 +1,10 @@ package dev.openfeature.contrib.tools.junitopenfeature; -import org.junit.jupiter.api.extension.ExtendWith; - import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.junit.jupiter.api.extension.ExtendWith; /** * Collection of {@link OpenFeature} configurations. @@ -19,5 +18,3 @@ */ OpenFeature[] value() default {}; } - - diff --git a/tools/junit-openfeature/src/main/java/dev/openfeature/contrib/tools/junitopenfeature/TestProvider.java b/tools/junit-openfeature/src/main/java/dev/openfeature/contrib/tools/junitopenfeature/TestProvider.java index 60378fba0..e684e4806 100644 --- a/tools/junit-openfeature/src/main/java/dev/openfeature/contrib/tools/junitopenfeature/TestProvider.java +++ b/tools/junit-openfeature/src/main/java/dev/openfeature/contrib/tools/junitopenfeature/TestProvider.java @@ -1,10 +1,5 @@ package dev.openfeature.contrib.tools.junitopenfeature; -import java.util.HashMap; -import java.util.Map; - -import org.junit.jupiter.api.extension.ExtensionContext; - import dev.openfeature.sdk.EvaluationContext; import dev.openfeature.sdk.EventProvider; import dev.openfeature.sdk.ImmutableContext; @@ -13,9 +8,12 @@ import dev.openfeature.sdk.Value; import dev.openfeature.sdk.providers.memory.Flag; import dev.openfeature.sdk.providers.memory.InMemoryProvider; +import java.util.HashMap; +import java.util.Map; import lombok.Getter; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.extension.ExtensionContext; /** * TestProvider based on InMemoryProvider but with another dimension added to the maps of flags. @@ -61,16 +59,16 @@ public void addConfigurationForTest(ExtensionContext.Namespace namespace, Map getBooleanEvaluation(String key, Boolean defaultValue, - EvaluationContext evaluationContext) { + public ProviderEvaluation getBooleanEvaluation( + String key, Boolean defaultValue, EvaluationContext evaluationContext) { return providerMap .getOrDefault(CURRENT_NAMESPACE.get(), FALLBACK_PROVIDER) .getBooleanEvaluation(key, defaultValue, evaluationContext); } @Override - public ProviderEvaluation getStringEvaluation(String key, String defaultValue, - EvaluationContext evaluationContext) { + public ProviderEvaluation getStringEvaluation( + String key, String defaultValue, EvaluationContext evaluationContext) { return providerMap .getOrDefault(CURRENT_NAMESPACE.get(), FALLBACK_PROVIDER) @@ -78,16 +76,16 @@ public ProviderEvaluation getStringEvaluation(String key, String default } @Override - public ProviderEvaluation getIntegerEvaluation(String key, Integer defaultValue, - EvaluationContext evaluationContext) { + public ProviderEvaluation getIntegerEvaluation( + String key, Integer defaultValue, EvaluationContext evaluationContext) { return providerMap .getOrDefault(CURRENT_NAMESPACE.get(), FALLBACK_PROVIDER) .getIntegerEvaluation(key, defaultValue, evaluationContext); } @Override - public ProviderEvaluation getDoubleEvaluation(String key, Double defaultValue, - EvaluationContext evaluationContext) { + public ProviderEvaluation getDoubleEvaluation( + String key, Double defaultValue, EvaluationContext evaluationContext) { return providerMap .getOrDefault(CURRENT_NAMESPACE.get(), FALLBACK_PROVIDER) .getDoubleEvaluation(key, defaultValue, evaluationContext); @@ -95,8 +93,8 @@ public ProviderEvaluation getDoubleEvaluation(String key, Double default @SneakyThrows @Override - public ProviderEvaluation getObjectEvaluation(String key, Value defaultValue, - EvaluationContext evaluationContext) { + public ProviderEvaluation getObjectEvaluation( + String key, Value defaultValue, EvaluationContext evaluationContext) { return providerMap .getOrDefault(CURRENT_NAMESPACE.get(), FALLBACK_PROVIDER) .getObjectEvaluation(key, defaultValue, evaluationContext); diff --git a/tools/junit-openfeature/src/test/java/dev/openfeature/contrib/tools/junitopenfeature/BooleanFlagTest.java b/tools/junit-openfeature/src/test/java/dev/openfeature/contrib/tools/junitopenfeature/BooleanFlagTest.java index d62b6ac98..e3b77701c 100644 --- a/tools/junit-openfeature/src/test/java/dev/openfeature/contrib/tools/junitopenfeature/BooleanFlagTest.java +++ b/tools/junit-openfeature/src/test/java/dev/openfeature/contrib/tools/junitopenfeature/BooleanFlagTest.java @@ -1,5 +1,7 @@ package dev.openfeature.contrib.tools.junitopenfeature; +import static org.assertj.core.api.Assertions.assertThat; + import dev.openfeature.sdk.Client; import dev.openfeature.sdk.OpenFeatureAPI; import org.junit.jupiter.api.Nested; @@ -7,8 +9,6 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import static org.assertj.core.api.Assertions.assertThat; - class BooleanFlagTest { private static final String FLAG = "boolean-flag"; @@ -104,27 +104,21 @@ void existingSimpleFlagIsRetrievedOnParameterizedTest() { @Nested class ExtendedConfig { @Test - @OpenFeature({ - @Flag(name = FLAG, value = "true") - }) + @OpenFeature({@Flag(name = FLAG, value = "true")}) void existingFlagIsRetrieved() { Client client = OpenFeatureAPI.getInstance().getClient(); assertThat(client.getBooleanValue(FLAG, false)).isTrue(); } @Test - @OpenFeature( - @Flag(name = FLAG, value = "truesadf") - ) + @OpenFeature(@Flag(name = FLAG, value = "truesadf")) void strangeFlagValue() { Client client = OpenFeatureAPI.getInstance().getClient(); assertThat(client.getBooleanValue(FLAG, false)).isFalse(); } @Test - @OpenFeature( - @Flag(name = FLAG, value = "true") - ) + @OpenFeature(@Flag(name = FLAG, value = "true")) void nonExistingFlagIsFallbacked() { Client client = OpenFeatureAPI.getInstance().getClient(); assertThat(client.getBooleanValue("nonSetFlag", false)).isFalse(); @@ -132,9 +126,9 @@ void nonExistingFlagIsFallbacked() { @Test @OpenFeature({ - @Flag(name = FLAG, value = "true"), - @Flag(name = FLAG + "2", value = "true"), - @Flag(name = FLAG + "3", value = "true"), + @Flag(name = FLAG, value = "true"), + @Flag(name = FLAG + "2", value = "true"), + @Flag(name = FLAG + "3", value = "true"), }) void multipleFlags() { Client client = OpenFeatureAPI.getInstance().getClient(); @@ -145,9 +139,7 @@ void multipleFlags() { @ParameterizedTest @ValueSource(ints = {1, 2}) - @OpenFeature({ - @Flag(name = FLAG, value = "true") - }) + @OpenFeature({@Flag(name = FLAG, value = "true")}) void existingFlagIsRetrievedOnParameterizedTest() { Client client = OpenFeatureAPI.getInstance().getClient(); assertThat(client.getBooleanValue(FLAG, false)).isTrue(); @@ -155,14 +147,14 @@ void existingFlagIsRetrievedOnParameterizedTest() { @Nested @OpenFeature({ - @Flag(name = FLAG, value = "true"), - @Flag(name = FLAG + "2", value = "false"), + @Flag(name = FLAG, value = "true"), + @Flag(name = FLAG + "2", value = "false"), }) class MultipleFlags { @Test @OpenFeature({ - @Flag(name = FLAG + "2", value = "true"), - @Flag(name = FLAG + "3", value = "true"), + @Flag(name = FLAG + "2", value = "true"), + @Flag(name = FLAG + "3", value = "true"), }) void multipleFlags() { Client client = OpenFeatureAPI.getInstance().getClient(); @@ -175,8 +167,8 @@ void multipleFlags() { @OpenFeature( domain = "testSpecific", value = { - @Flag(name = FLAG + "2", value = "true"), - @Flag(name = FLAG + "3", value = "true"), + @Flag(name = FLAG + "2", value = "true"), + @Flag(name = FLAG + "3", value = "true"), }) void multipleFlagsOnMultipleDomains() { Client client = OpenFeatureAPI.getInstance().getClient(); diff --git a/tools/junit-openfeature/src/test/java/dev/openfeature/contrib/tools/junitopenfeature/DoubleFlagTest.java b/tools/junit-openfeature/src/test/java/dev/openfeature/contrib/tools/junitopenfeature/DoubleFlagTest.java index ee56a01b1..e2f8db57a 100644 --- a/tools/junit-openfeature/src/test/java/dev/openfeature/contrib/tools/junitopenfeature/DoubleFlagTest.java +++ b/tools/junit-openfeature/src/test/java/dev/openfeature/contrib/tools/junitopenfeature/DoubleFlagTest.java @@ -1,5 +1,7 @@ package dev.openfeature.contrib.tools.junitopenfeature; +import static org.assertj.core.api.Assertions.assertThat; + import dev.openfeature.sdk.Client; import dev.openfeature.sdk.OpenFeatureAPI; import org.junit.jupiter.api.Nested; @@ -7,8 +9,6 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import static org.assertj.core.api.Assertions.assertThat; - class DoubleFlagTest { private static final String FLAG = "double-flag"; @@ -19,7 +19,6 @@ class DoubleFlagTest { private static final String FLAG_VALUE_STRING_ALTERNATIVE = "0"; private static final String SPECIFIC_DOMAIN = "testSpecific"; - @Nested class SimpleConfig { @@ -42,7 +41,7 @@ void multipleFlagsSimple() { } @Nested - @Flag(name = FLAG, value = FLAG_VALUE_STRING , valueType = Double.class) + @Flag(name = FLAG, value = FLAG_VALUE_STRING, valueType = Double.class) @Flag(name = FLAG + "2", value = FLAG_VALUE_STRING, valueType = Double.class) @Flag(name = FLAG + "3", value = FLAG_VALUE_STRING, valueType = Double.class) class onClass { @@ -111,27 +110,21 @@ void existingSimpleFlagIsRetrievedOnParameterizedTest() { @Nested class ExtendedConfig { @Test - @OpenFeature({ - @Flag(name = FLAG, value = FLAG_VALUE_STRING, valueType = Double.class) - }) + @OpenFeature({@Flag(name = FLAG, value = FLAG_VALUE_STRING, valueType = Double.class)}) void existingFlagIsRetrieved() { Client client = OpenFeatureAPI.getInstance().getClient(); assertThat(client.getDoubleValue(FLAG, FALLBACK)).isEqualTo(FLAG_VALUE); } @Test - @OpenFeature( - @Flag(name = FLAG, value = "truesadf") - ) + @OpenFeature(@Flag(name = FLAG, value = "truesadf")) void strangeFlagValue() { Client client = OpenFeatureAPI.getInstance().getClient(); assertThat(client.getDoubleValue(FLAG, FALLBACK)).isEqualTo(FALLBACK); } @Test - @OpenFeature( - @Flag(name = FLAG, value = FLAG_VALUE_STRING, valueType = Double.class) - ) + @OpenFeature(@Flag(name = FLAG, value = FLAG_VALUE_STRING, valueType = Double.class)) void nonExistingFlagIsFallbacked() { Client client = OpenFeatureAPI.getInstance().getClient(); assertThat(client.getDoubleValue("nonSetFlag", FALLBACK)).isEqualTo(FALLBACK); @@ -139,9 +132,9 @@ void nonExistingFlagIsFallbacked() { @Test @OpenFeature({ - @Flag(name = FLAG, value = FLAG_VALUE_STRING, valueType = Double.class), - @Flag(name = FLAG + "2", value = FLAG_VALUE_STRING, valueType = Double.class), - @Flag(name = FLAG + "3", value = FLAG_VALUE_STRING, valueType = Double.class), + @Flag(name = FLAG, value = FLAG_VALUE_STRING, valueType = Double.class), + @Flag(name = FLAG + "2", value = FLAG_VALUE_STRING, valueType = Double.class), + @Flag(name = FLAG + "3", value = FLAG_VALUE_STRING, valueType = Double.class), }) void multipleFlags() { Client client = OpenFeatureAPI.getInstance().getClient(); @@ -152,9 +145,7 @@ void multipleFlags() { @ParameterizedTest @ValueSource(ints = {1, 2}) - @OpenFeature({ - @Flag(name = FLAG, value = FLAG_VALUE_STRING, valueType = Double.class) - }) + @OpenFeature({@Flag(name = FLAG, value = FLAG_VALUE_STRING, valueType = Double.class)}) void existingFlagIsRetrievedOnParameterizedTest() { Client client = OpenFeatureAPI.getInstance().getClient(); assertThat(client.getDoubleValue(FLAG, FALLBACK)).isEqualTo(FLAG_VALUE); @@ -162,14 +153,14 @@ void existingFlagIsRetrievedOnParameterizedTest() { @Nested @OpenFeature({ - @Flag(name = FLAG, value = FLAG_VALUE_STRING, valueType = Double.class), - @Flag(name = FLAG + "2", value = FLAG_VALUE_STRING_ALTERNATIVE, valueType = Double.class), + @Flag(name = FLAG, value = FLAG_VALUE_STRING, valueType = Double.class), + @Flag(name = FLAG + "2", value = FLAG_VALUE_STRING_ALTERNATIVE, valueType = Double.class), }) class MultipleFlags { @Test @OpenFeature({ - @Flag(name = FLAG + "2", value = FLAG_VALUE_STRING, valueType = Double.class), - @Flag(name = FLAG + "3", value = FLAG_VALUE_STRING, valueType = Double.class), + @Flag(name = FLAG + "2", value = FLAG_VALUE_STRING, valueType = Double.class), + @Flag(name = FLAG + "3", value = FLAG_VALUE_STRING, valueType = Double.class), }) void multipleFlags() { Client client = OpenFeatureAPI.getInstance().getClient(); @@ -182,8 +173,8 @@ void multipleFlags() { @OpenFeature( domain = SPECIFIC_DOMAIN, value = { - @Flag(name = FLAG + "2", value = FLAG_VALUE_STRING, valueType = Double.class), - @Flag(name = FLAG + "3", value = FLAG_VALUE_STRING, valueType = Double.class), + @Flag(name = FLAG + "2", value = FLAG_VALUE_STRING, valueType = Double.class), + @Flag(name = FLAG + "3", value = FLAG_VALUE_STRING, valueType = Double.class), }) void multipleFlagsOnMultipleDomains() { Client client = OpenFeatureAPI.getInstance().getClient(); diff --git a/tools/junit-openfeature/src/test/java/dev/openfeature/contrib/tools/junitopenfeature/IntegerFlagTest.java b/tools/junit-openfeature/src/test/java/dev/openfeature/contrib/tools/junitopenfeature/IntegerFlagTest.java index cb74090b1..5db7be9b3 100644 --- a/tools/junit-openfeature/src/test/java/dev/openfeature/contrib/tools/junitopenfeature/IntegerFlagTest.java +++ b/tools/junit-openfeature/src/test/java/dev/openfeature/contrib/tools/junitopenfeature/IntegerFlagTest.java @@ -1,5 +1,7 @@ package dev.openfeature.contrib.tools.junitopenfeature; +import static org.assertj.core.api.Assertions.assertThat; + import dev.openfeature.sdk.Client; import dev.openfeature.sdk.OpenFeatureAPI; import org.junit.jupiter.api.Nested; @@ -7,8 +9,6 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import static org.assertj.core.api.Assertions.assertThat; - class IntegerFlagTest { private static final String FLAG = "integer-flag"; @@ -19,7 +19,6 @@ class IntegerFlagTest { private static final String FLAG_VALUE_STRING_ALTERNATIVE = "0"; private static final String SPECIFIC_DOMAIN = "testSpecific"; - @Nested class SimpleConfig { @@ -42,7 +41,7 @@ void multipleFlagsSimple() { } @Nested - @Flag(name = FLAG, value = FLAG_VALUE_STRING , valueType = Integer.class) + @Flag(name = FLAG, value = FLAG_VALUE_STRING, valueType = Integer.class) @Flag(name = FLAG + "2", value = FLAG_VALUE_STRING, valueType = Integer.class) @Flag(name = FLAG + "3", value = FLAG_VALUE_STRING, valueType = Integer.class) class onClass { @@ -111,27 +110,21 @@ void existingSimpleFlagIsRetrievedOnParameterizedTest() { @Nested class ExtendedConfig { @Test - @OpenFeature({ - @Flag(name = FLAG, value = FLAG_VALUE_STRING, valueType = Integer.class) - }) + @OpenFeature({@Flag(name = FLAG, value = FLAG_VALUE_STRING, valueType = Integer.class)}) void existingFlagIsRetrieved() { Client client = OpenFeatureAPI.getInstance().getClient(); assertThat(client.getIntegerValue(FLAG, FALLBACK)).isEqualTo(FLAG_VALUE); } @Test - @OpenFeature( - @Flag(name = FLAG, value = "truesadf") - ) + @OpenFeature(@Flag(name = FLAG, value = "truesadf")) void strangeFlagValue() { Client client = OpenFeatureAPI.getInstance().getClient(); assertThat(client.getIntegerValue(FLAG, FALLBACK)).isEqualTo(FALLBACK); } @Test - @OpenFeature( - @Flag(name = FLAG, value = FLAG_VALUE_STRING, valueType = Integer.class) - ) + @OpenFeature(@Flag(name = FLAG, value = FLAG_VALUE_STRING, valueType = Integer.class)) void nonExistingFlagIsFallbacked() { Client client = OpenFeatureAPI.getInstance().getClient(); assertThat(client.getIntegerValue("nonSetFlag", FALLBACK)).isEqualTo(FALLBACK); @@ -139,9 +132,9 @@ void nonExistingFlagIsFallbacked() { @Test @OpenFeature({ - @Flag(name = FLAG, value = FLAG_VALUE_STRING, valueType = Integer.class), - @Flag(name = FLAG + "2", value = FLAG_VALUE_STRING, valueType = Integer.class), - @Flag(name = FLAG + "3", value = FLAG_VALUE_STRING, valueType = Integer.class), + @Flag(name = FLAG, value = FLAG_VALUE_STRING, valueType = Integer.class), + @Flag(name = FLAG + "2", value = FLAG_VALUE_STRING, valueType = Integer.class), + @Flag(name = FLAG + "3", value = FLAG_VALUE_STRING, valueType = Integer.class), }) void multipleFlags() { Client client = OpenFeatureAPI.getInstance().getClient(); @@ -152,9 +145,7 @@ void multipleFlags() { @ParameterizedTest @ValueSource(ints = {1, 2}) - @OpenFeature({ - @Flag(name = FLAG, value = FLAG_VALUE_STRING, valueType = Integer.class) - }) + @OpenFeature({@Flag(name = FLAG, value = FLAG_VALUE_STRING, valueType = Integer.class)}) void existingSimpleFlagIsRetrievedOnParameterizedTest() { Client client = OpenFeatureAPI.getInstance().getClient(); assertThat(client.getIntegerValue(FLAG, FALLBACK)).isEqualTo(FLAG_VALUE); @@ -162,14 +153,14 @@ void existingSimpleFlagIsRetrievedOnParameterizedTest() { @Nested @OpenFeature({ - @Flag(name = FLAG, value = FLAG_VALUE_STRING, valueType = Integer.class), - @Flag(name = FLAG + "2", value = FLAG_VALUE_STRING_ALTERNATIVE, valueType = Integer.class), + @Flag(name = FLAG, value = FLAG_VALUE_STRING, valueType = Integer.class), + @Flag(name = FLAG + "2", value = FLAG_VALUE_STRING_ALTERNATIVE, valueType = Integer.class), }) class MultipleFlags { @Test @OpenFeature({ - @Flag(name = FLAG + "2", value = FLAG_VALUE_STRING, valueType = Integer.class), - @Flag(name = FLAG + "3", value = FLAG_VALUE_STRING, valueType = Integer.class), + @Flag(name = FLAG + "2", value = FLAG_VALUE_STRING, valueType = Integer.class), + @Flag(name = FLAG + "3", value = FLAG_VALUE_STRING, valueType = Integer.class), }) void multipleFlags() { Client client = OpenFeatureAPI.getInstance().getClient(); @@ -182,8 +173,8 @@ void multipleFlags() { @OpenFeature( domain = SPECIFIC_DOMAIN, value = { - @Flag(name = FLAG + "2", value = FLAG_VALUE_STRING, valueType = Integer.class), - @Flag(name = FLAG + "3", value = FLAG_VALUE_STRING, valueType = Integer.class), + @Flag(name = FLAG + "2", value = FLAG_VALUE_STRING, valueType = Integer.class), + @Flag(name = FLAG + "3", value = FLAG_VALUE_STRING, valueType = Integer.class), }) void multipleFlagsOnMultipleDomains() { Client client = OpenFeatureAPI.getInstance().getClient(); diff --git a/tools/junit-openfeature/src/test/java/dev/openfeature/contrib/tools/junitopenfeature/OpenFeatureExtensionTest.java b/tools/junit-openfeature/src/test/java/dev/openfeature/contrib/tools/junitopenfeature/OpenFeatureExtensionTest.java index ecd85828d..f555e5d0f 100644 --- a/tools/junit-openfeature/src/test/java/dev/openfeature/contrib/tools/junitopenfeature/OpenFeatureExtensionTest.java +++ b/tools/junit-openfeature/src/test/java/dev/openfeature/contrib/tools/junitopenfeature/OpenFeatureExtensionTest.java @@ -1,12 +1,12 @@ package dev.openfeature.contrib.tools.junitopenfeature; +import static org.assertj.core.api.Assertions.assertThat; + import dev.openfeature.sdk.Client; import dev.openfeature.sdk.OpenFeatureAPI; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import static org.assertj.core.api.Assertions.assertThat; - class OpenFeatureExtensionTest { OpenFeatureAPI api = OpenFeatureAPI.getInstance(); @@ -24,7 +24,9 @@ void clientIsSet() { } @OpenFeature({}) - @OpenFeature(domain = "test", value = {}) + @OpenFeature( + domain = "test", + value = {}) void clientIsSetMultipleTimes() { assertThat(api).isNotNull(); assertThat(api.getProvider()).isInstanceOf(TestProvider.class); @@ -36,7 +38,9 @@ void clientIsSetMultipleTimes() { } @Test - @OpenFeature(domain = "domain", value = {}) + @OpenFeature( + domain = "domain", + value = {}) void clientIsSetWithDomain() { assertThat(api).isNotNull(); assertThat(api.getProvider("domain")).isInstanceOf(TestProvider.class); @@ -58,7 +62,9 @@ void clientIsSet() { } @Nested - @OpenFeature(domain = "domain", value = {}) + @OpenFeature( + domain = "domain", + value = {}) class OnClassWithDomain { @Test void clientIsSetWithDomain() { diff --git a/tools/junit-openfeature/src/test/java/dev/openfeature/contrib/tools/junitopenfeature/StringFlagTest.java b/tools/junit-openfeature/src/test/java/dev/openfeature/contrib/tools/junitopenfeature/StringFlagTest.java index b30a6b092..b7ce8e203 100644 --- a/tools/junit-openfeature/src/test/java/dev/openfeature/contrib/tools/junitopenfeature/StringFlagTest.java +++ b/tools/junit-openfeature/src/test/java/dev/openfeature/contrib/tools/junitopenfeature/StringFlagTest.java @@ -1,5 +1,7 @@ package dev.openfeature.contrib.tools.junitopenfeature; +import static org.assertj.core.api.Assertions.assertThat; + import dev.openfeature.sdk.Client; import dev.openfeature.sdk.OpenFeatureAPI; import org.junit.jupiter.api.Nested; @@ -7,8 +9,6 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import static org.assertj.core.api.Assertions.assertThat; - class StringFlagTest { private static final String FLAG = "string-flag"; @@ -17,7 +17,6 @@ class StringFlagTest { private static final String FLAG_VALUE_ALTERNATIVE = "false"; private static final String SPECIFIC_DOMAIN = "testSpecific"; - @Nested class SimpleConfig { @@ -66,9 +65,9 @@ void existingSimpleFlagIsRetrievedOnParameterizedTest() { @OpenFeatureDefaultDomain(SPECIFIC_DOMAIN) class SimpleConfigWithDefault { @Nested - @Flag(name = FLAG , value = FLAG_VALUE, valueType = String.class) - @Flag(name = FLAG + "2" , value = FLAG_VALUE, valueType = String.class) - @Flag(name = FLAG + "3" , value = FLAG_VALUE, valueType = String.class) + @Flag(name = FLAG, value = FLAG_VALUE, valueType = String.class) + @Flag(name = FLAG + "2", value = FLAG_VALUE, valueType = String.class) + @Flag(name = FLAG + "3", value = FLAG_VALUE, valueType = String.class) class onClass { @Test void multipleFlagsSimple() { @@ -80,16 +79,16 @@ void multipleFlagsSimple() { } @Test - @Flag(name = FLAG , value = FLAG_VALUE, valueType = String.class) + @Flag(name = FLAG, value = FLAG_VALUE, valueType = String.class) void existingSimpleFlagIsRetrieved() { Client client = OpenFeatureAPI.getInstance().getClient(SPECIFIC_DOMAIN); assertThat(client.getStringValue(FLAG, FALLBACK)).isEqualTo(FLAG_VALUE); } @Test - @Flag(name = FLAG , value = FLAG_VALUE, valueType = String.class) - @Flag(name = FLAG + "2" , value = FLAG_VALUE, valueType = String.class) - @Flag(name = FLAG + "3" , value = FLAG_VALUE, valueType = String.class) + @Flag(name = FLAG, value = FLAG_VALUE, valueType = String.class) + @Flag(name = FLAG + "2", value = FLAG_VALUE, valueType = String.class) + @Flag(name = FLAG + "3", value = FLAG_VALUE, valueType = String.class) void multipleFlagsSimple() { Client client = OpenFeatureAPI.getInstance().getClient(SPECIFIC_DOMAIN); assertThat(client.getStringValue(FLAG, FALLBACK)).isEqualTo(FLAG_VALUE); @@ -109,27 +108,21 @@ void existingSimpleFlagIsRetrievedOnParameterizedTest() { @Nested class ExtendedConfig { @Test - @OpenFeature({ - @Flag(name = FLAG , value = FLAG_VALUE, valueType = String.class) - }) + @OpenFeature({@Flag(name = FLAG, value = FLAG_VALUE, valueType = String.class)}) void existingFlagIsRetrieved() { Client client = OpenFeatureAPI.getInstance().getClient(); assertThat(client.getStringValue(FLAG, FALLBACK)).isEqualTo(FLAG_VALUE); } @Test - @OpenFeature( - @Flag(name = FLAG, value = "truesadf") - ) + @OpenFeature(@Flag(name = FLAG, value = "truesadf")) void strangeFlagValue() { Client client = OpenFeatureAPI.getInstance().getClient(); assertThat(client.getStringValue(FLAG, FALLBACK)).isEqualTo(FALLBACK); } @Test - @OpenFeature( - @Flag(name = FLAG , value = FLAG_VALUE, valueType = String.class) - ) + @OpenFeature(@Flag(name = FLAG, value = FLAG_VALUE, valueType = String.class)) void nonExistingFlagIsFallbacked() { Client client = OpenFeatureAPI.getInstance().getClient(); assertThat(client.getStringValue("nonSetFlag", FALLBACK)).isEqualTo(FALLBACK); @@ -137,9 +130,9 @@ void nonExistingFlagIsFallbacked() { @Test @OpenFeature({ - @Flag(name = FLAG , value = FLAG_VALUE, valueType = String.class), - @Flag(name = FLAG + "2" , value = FLAG_VALUE, valueType = String.class), - @Flag(name = FLAG + "3" , value = FLAG_VALUE, valueType = String.class), + @Flag(name = FLAG, value = FLAG_VALUE, valueType = String.class), + @Flag(name = FLAG + "2", value = FLAG_VALUE, valueType = String.class), + @Flag(name = FLAG + "3", value = FLAG_VALUE, valueType = String.class), }) void multipleFlags() { Client client = OpenFeatureAPI.getInstance().getClient(); @@ -150,9 +143,7 @@ void multipleFlags() { @ParameterizedTest @ValueSource(ints = {1, 2}) - @OpenFeature({ - @Flag(name = FLAG , value = FLAG_VALUE, valueType = String.class) - }) + @OpenFeature({@Flag(name = FLAG, value = FLAG_VALUE, valueType = String.class)}) void existingSimpleFlagIsRetrievedOnParameterizedTest() { Client client = OpenFeatureAPI.getInstance().getClient(); assertThat(client.getStringValue(FLAG, FALLBACK)).isEqualTo(FLAG_VALUE); @@ -160,14 +151,14 @@ void existingSimpleFlagIsRetrievedOnParameterizedTest() { @Nested @OpenFeature({ - @Flag(name = FLAG , value = FLAG_VALUE, valueType = String.class), - @Flag(name = FLAG + "2", value = FLAG_VALUE_ALTERNATIVE, valueType = String.class), + @Flag(name = FLAG, value = FLAG_VALUE, valueType = String.class), + @Flag(name = FLAG + "2", value = FLAG_VALUE_ALTERNATIVE, valueType = String.class), }) class MultipleFlags { @Test @OpenFeature({ - @Flag(name = FLAG + "2" , value = FLAG_VALUE, valueType = String.class), - @Flag(name = FLAG + "3" , value = FLAG_VALUE, valueType = String.class), + @Flag(name = FLAG + "2", value = FLAG_VALUE, valueType = String.class), + @Flag(name = FLAG + "3", value = FLAG_VALUE, valueType = String.class), }) void multipleFlags() { Client client = OpenFeatureAPI.getInstance().getClient(); @@ -180,8 +171,8 @@ void multipleFlags() { @OpenFeature( domain = SPECIFIC_DOMAIN, value = { - @Flag(name = FLAG + "2" , value = FLAG_VALUE, valueType = String.class), - @Flag(name = FLAG + "3" , value = FLAG_VALUE, valueType = String.class), + @Flag(name = FLAG + "2", value = FLAG_VALUE, valueType = String.class), + @Flag(name = FLAG + "3", value = FLAG_VALUE, valueType = String.class), }) void multipleFlagsOnMultipleDomains() { Client client = OpenFeatureAPI.getInstance().getClient(); From f03b39d7dff197748559fdc53072e4ec9cbf8c2b Mon Sep 17 00:00:00 2001 From: Simon Schrottner Date: Thu, 2 Jan 2025 13:33:39 +0100 Subject: [PATCH 3/4] fixup: add contributing section Signed-off-by: Simon Schrottner --- CONTRIBUTING.md | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b4420545f..1486a4e7c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -66,6 +66,38 @@ Sample pom.xml: Any published modules must have documentation in their root directory, explaining the basic purpose of the module as well as installation and usage instructions. Instructions for how to develop a module should also be included (required system dependencies, instructions for testing locally, etc). +## Code Styles + +### Overview +Our project follows strict code formatting standards to maintain consistency and readability across the codebase. We use [Spotless](https://github.com/diffplug/spotless) integrated with the [Palantir Java Format](https://github.com/palantir/palantir-java-format) for code formatting. + +**Spotless** ensures that all code complies with the formatting rules automatically, reducing style-related issues during code reviews. + +### How to Format Your Code +1. **Before Committing Changes:** + Run the Spotless plugin to format your code. This will apply the Palantir Java Format style: + ```bash + mvn spotless:apply + ``` + +2. **Verify Formatting:** + To check if your code adheres to the style guidelines without making changes: + ```bash + mvn spotless:check + ``` + + - If this command fails, your code does not follow the required formatting. Use `mvn spotless:apply` to fix it. + +### CI/CD Integration +Our Continuous Integration (CI) pipeline automatically checks code formatting using the Spotless plugin. Any code that does not pass the `spotless:check` step will cause the build to fail. + +### Best Practices +- Regularly run `mvn spotless:apply` during your work to ensure your code remains aligned with the standards. +- Configure your IDE (e.g., IntelliJ IDEA or Eclipse) to follow the Palantir Java format guidelines to reduce discrepancies during development. + +### Support +If you encounter issues with code formatting, please raise a GitHub issue or contact the maintainers. + ## Testing Any published modules must have reasonable test coverage. @@ -106,4 +138,4 @@ The following vscode settings are recommended (create a workspace settings file "java.format.settings.url": "${workspaceFolder}/eclipse-java-google-style.xml", "java.format.enabled": false } -``` \ No newline at end of file +``` From d5a030b513da2c8d121e988f9dd3a36f84544e97 Mon Sep 17 00:00:00 2001 From: Simon Schrottner Date: Fri, 3 Jan 2025 16:48:18 +0100 Subject: [PATCH 4/4] fixup: fix maven target Signed-off-by: Simon Schrottner --- pom.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pom.xml b/pom.xml index 96f8d6ac2..18e894d73 100644 --- a/pom.xml +++ b/pom.xml @@ -483,6 +483,13 @@ + + + + check + + +