From e612a65ef8500e7fbad83cc0c466d7cd260a129d Mon Sep 17 00:00:00 2001 From: Simon Schrottner Date: Fri, 3 Jan 2025 18:34:57 +0100 Subject: [PATCH] build: change checkstyle to google code format, plus adding spotless (#1121) Signed-off-by: Simon Schrottner Co-authored-by: Todd Baert --- .editorconfig | 72 ++ CONTRIBUTING.md | 34 +- checkstyle.xml | 266 ++++-- .../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 +- pom.xml | 106 ++- .../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 +- 185 files changed, 3955 insertions(+), 3996 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/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 +``` diff --git a/checkstyle.xml b/checkstyle.xml index 2a7a8d05c..3b01278d3 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"> - + + - + @@ -34,8 +35,16 @@ - - + + + + + @@ -59,59 +68,77 @@ + default="checkstyle-xpath-suppressions.xml" /> - - - + + 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 +146,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 +185,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 +201,13 @@ - + - + @@ -175,22 +220,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,38 +246,53 @@ + 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."/> @@ -241,44 +302,62 @@ + + + + + + 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 +369,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/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/MetricHookOptions.java b/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/MetricHookOptions.java index 618c47b22..c7a1d4ead 100644 --- a/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/MetricHookOptions.java +++ b/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/MetricHookOptions.java @@ -3,20 +3,20 @@ import dev.openfeature.sdk.ImmutableMetadata; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.opentelemetry.api.common.Attributes; -import lombok.Builder; -import lombok.Getter; - import java.util.Collections; import java.util.List; import java.util.function.Function; +import lombok.Builder; +import lombok.Getter; /** * OpenTelemetry hook options. */ @Builder @Getter -@SuppressFBWarnings(value = {"EI_EXPOSE_REP"}, justification = "Dimension list can be mutable") - +@SuppressFBWarnings( + value = {"EI_EXPOSE_REP"}, + justification = "Dimension list can be mutable") public class MetricHookOptions { /** diff --git a/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/MetricsHook.java b/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/MetricsHook.java index 54404ea93..10b1bf288 100644 --- a/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/MetricsHook.java +++ b/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/MetricsHook.java @@ -1,5 +1,10 @@ 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 dev.openfeature.sdk.EvaluationContext; import dev.openfeature.sdk.FlagEvaluationDetails; import dev.openfeature.sdk.Hook; @@ -11,18 +16,12 @@ import io.opentelemetry.api.metrics.LongCounter; import io.opentelemetry.api.metrics.LongUpDownCounter; import io.opentelemetry.api.metrics.Meter; -import lombok.extern.slf4j.Slf4j; - import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.function.Function; - -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 lombok.extern.slf4j.Slf4j; /** * OpenTelemetry metric hook records metrics at different {@link Hook} stages. @@ -58,9 +57,9 @@ public MetricsHook(final OpenTelemetry openTelemetry) { public MetricsHook(final OpenTelemetry openTelemetry, final MetricHookOptions options) { final Meter meter = openTelemetry.getMeter(METER_NAME); - activeFlagEvaluationsCounter = - meter.upDownCounterBuilder(EVALUATION_ACTIVE_COUNT).setDescription("active flag evaluations counter") - .build(); + activeFlagEvaluationsCounter = meter.upDownCounterBuilder(EVALUATION_ACTIVE_COUNT) + .setDescription("active flag evaluations counter") + .build(); evaluationRequestCounter = meter.counterBuilder(EVALUATION_REQUESTS_TOTAL) .setDescription("feature flag evaluation request counter") @@ -81,13 +80,17 @@ public MetricsHook(final OpenTelemetry openTelemetry, final MetricHookOptions op extraDimensions = options.getExtraAttributes(); } - @Override public Optional 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/pom.xml b/pom.xml index ec662a10f..18e894d73 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,55 @@ + + com.diffplug.spotless + spotless-maven-plugin + 2.30.0 + + + + + + + + + .gitattributes + .gitignore + + + + + + true + 4 + + + + + + + + + true + 4 + + + + + + + + + + + + check + + + + + @@ -489,7 +541,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 + 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();