diff --git a/.rhcicd/clowdapp-aggregator.yaml b/.rhcicd/clowdapp-aggregator.yaml
index 525661785b..851719af7b 100644
--- a/.rhcicd/clowdapp-aggregator.yaml
+++ b/.rhcicd/clowdapp-aggregator.yaml
@@ -16,6 +16,7 @@ objects:
- notifications-backend
database:
sharedDbAppName: notifications-backend
+ featureFlags: true
kafkaTopics:
- topicName: platform.notifications.ingress
partitions: 3
@@ -47,6 +48,8 @@ objects:
env:
- name: ENV_NAME
value: ${ENV_NAME}
+ - name: NOTIFICATIONS_UNLEASH_ENABLED
+ value: ${NOTIFICATIONS_UNLEASH_ENABLED}
- name: QUARKUS_LOG_CATEGORY__COM_REDHAT_CLOUD_NOTIFICATIONS__LEVEL
value: ${NOTIFICATIONS_LOG_LEVEL}
- name: QUARKUS_LOG_CLOUDWATCH_ENABLED
@@ -59,6 +62,8 @@ objects:
value: ${SENTRY_DSN}${ENV_NAME}
- name: QUARKUS_LOG_SENTRY_ENVIRONMENT
value: ${ENV_NAME}
+ - name: QUARKUS_UNLEASH_ACTIVE
+ value: ${NOTIFICATIONS_UNLEASH_ENABLED}
- name: PROMETHEUS_PUSHGATEWAY_URL
value: ${PROMETHEUS_PUSHGATEWAY}
- name: NOTIFICATIONS_BUNDLE_LEVEL_DIGEST_ENABLED
@@ -94,6 +99,8 @@ parameters:
- name: NOTIFICATIONS_LOG_LEVEL
description: Log level for com.redhat.cloud.notifications
value: INFO
+- name: NOTIFICATIONS_UNLEASH_ENABLED
+ value: "false"
- name: SENTRY_DSN
description: The DSN to push data to Sentry — i.e. https://public_key@host/project_id?environment=
- name: SENTRY_ENABLED
diff --git a/.rhcicd/clowdapp-backend.yaml b/.rhcicd/clowdapp-backend.yaml
index 3bc0f50f5a..05297a6404 100644
--- a/.rhcicd/clowdapp-backend.yaml
+++ b/.rhcicd/clowdapp-backend.yaml
@@ -20,6 +20,7 @@ objects:
database:
name: notifications-backend
version: 15
+ featureFlags: true
testing:
iqePlugin: notifications
jobs:
@@ -118,8 +119,12 @@ objects:
name: notifications-ephemeral-data
key: ephemeral_data.json
optional: true
+ - name: NOTIFICATIONS_UNLEASH_ENABLED
+ value: ${NOTIFICATIONS_UNLEASH_ENABLED}
- name: QUARKUS_REST_CLIENT_RBAC_AUTHENTICATION_READ_TIMEOUT
value: ${RBAC_AUTHENTICATION_READ_TIMEOUT}
+ - name: QUARKUS_UNLEASH_ACTIVE
+ value: ${NOTIFICATIONS_UNLEASH_ENABLED}
- name: INTERNAL_RBAC_ENABLED
value: ${INTERNAL_RBAC_ENABLED}
- name: INTERNAL_ADMIN_ROLE
@@ -226,6 +231,8 @@ parameters:
- name: NOTIFICATIONS_EMAILS_ONLY_MODE_ENABLED
description: When this is true, all integration types except emails are disabled
value: "true"
+- name: NOTIFICATIONS_UNLEASH_ENABLED
+ value: "false"
- name: QUARKUS_LOG_CLOUDWATCH_API_CALL_TIMEOUT
description: Amount of time to allow the CloudWatch client to complete the execution of an API call expressed with the ISO-8601 duration format PnDTnHnMn.nS.
value: PT30S
diff --git a/.rhcicd/clowdapp-connector-drawer.yaml b/.rhcicd/clowdapp-connector-drawer.yaml
index a004379bec..f01a3eb486 100644
--- a/.rhcicd/clowdapp-connector-drawer.yaml
+++ b/.rhcicd/clowdapp-connector-drawer.yaml
@@ -12,6 +12,7 @@ objects:
dependencies:
- notifications-recipients-resolver
envName: ${ENV_NAME}
+ featureFlags: true
kafkaTopics:
- topicName: platform.notifications.tocamel
partitions: 3
@@ -73,6 +74,8 @@ objects:
value: ${NOTIFICATIONS_CONNECTOR_SEDA_CONCURRENT_CONSUMERS}
- name: NOTIFICATIONS_CONNECTOR_SEDA_QUEUE_SIZE
value: ${NOTIFICATIONS_CONNECTOR_SEDA_QUEUE_SIZE}
+ - name: NOTIFICATIONS_UNLEASH_ENABLED
+ value: ${NOTIFICATIONS_UNLEASH_ENABLED}
- name: QUARKUS_HTTP_PORT
value: ${QUARKUS_HTTP_PORT}
- name: QUARKUS_LOG_CATEGORY__COM_REDHAT_CLOUD_NOTIFICATIONS__LEVEL
@@ -99,6 +102,8 @@ objects:
value: ${SENTRY_DSN}${ENV_NAME}
- name: QUARKUS_LOG_SENTRY_ENVIRONMENT
value: ${ENV_NAME}
+ - name: QUARKUS_UNLEASH_ACTIVE
+ value: ${NOTIFICATIONS_UNLEASH_ENABLED}
parameters:
- name: CPU_LIMIT
description: CPU limit
@@ -152,6 +157,8 @@ parameters:
- name: NOTIFICATIONS_LOG_LEVEL
description: Log level of Notifications
value: INFO
+- name: NOTIFICATIONS_UNLEASH_ENABLED
+ value: "false"
- name: QUARKUS_HTTP_PORT
description: Quarkus HTTP server port, defaulting to the default Clowder private port
value: "9000"
diff --git a/.rhcicd/clowdapp-connector-email.yaml b/.rhcicd/clowdapp-connector-email.yaml
index 974506d8df..ad3ef2a434 100644
--- a/.rhcicd/clowdapp-connector-email.yaml
+++ b/.rhcicd/clowdapp-connector-email.yaml
@@ -12,6 +12,7 @@ objects:
dependencies:
- notifications-recipients-resolver
envName: ${ENV_NAME}
+ featureFlags: true
kafkaTopics:
- topicName: platform.notifications.tocamel
partitions: 3
@@ -97,6 +98,8 @@ objects:
value: ${NOTIFICATIONS_CONNECTOR_MAX_RECIPIENTS_PER_EMAIL}
- name: NOTIFICATIONS_EMAILS_INTERNAL_ONLY_ENABLED
value: ${NOTIFICATIONS_EMAILS_INTERNAL_ONLY_ENABLED}
+ - name: NOTIFICATIONS_UNLEASH_ENABLED
+ value: ${NOTIFICATIONS_UNLEASH_ENABLED}
- name: QUARKUS_HTTP_PORT
value: ${QUARKUS_HTTP_PORT}
- name: QUARKUS_LOG_CATEGORY__COM_REDHAT_CLOUD_NOTIFICATIONS__LEVEL
@@ -123,6 +126,8 @@ objects:
value: ${SENTRY_DSN}${ENV_NAME}
- name: QUARKUS_LOG_SENTRY_ENVIRONMENT
value: ${ENV_NAME}
+ - name: QUARKUS_UNLEASH_ACTIVE
+ value: ${NOTIFICATIONS_UNLEASH_ENABLED}
parameters:
- name: BACKOFFICE_CLIENT_ENV
description: Back-office client environment
@@ -205,6 +210,8 @@ parameters:
value: "50"
- name: NOTIFICATIONS_EMAILS_INTERNAL_ONLY_ENABLED
value: "false"
+- name: NOTIFICATIONS_UNLEASH_ENABLED
+ value: "false"
- name: QUARKUS_HTTP_PORT
description: Quarkus HTTP server port, defaulting to the default Clowder private port
value: "9000"
diff --git a/.rhcicd/clowdapp-connector-google-chat.yaml b/.rhcicd/clowdapp-connector-google-chat.yaml
index 5c788705bb..3fb7464fd8 100644
--- a/.rhcicd/clowdapp-connector-google-chat.yaml
+++ b/.rhcicd/clowdapp-connector-google-chat.yaml
@@ -10,6 +10,7 @@ objects:
name: notifications-connector-google-chat
spec:
envName: ${ENV_NAME}
+ featureFlags: true
kafkaTopics:
- topicName: platform.notifications.tocamel
partitions: 3
@@ -79,6 +80,8 @@ objects:
value: ${NOTIFICATIONS_CONNECTOR_SEDA_QUEUE_SIZE}
- name: NOTIFICATIONS_CONNECTOR_HTTP_DISABLE_FAULTY_ENDPOINTS
value: ${NOTIFICATIONS_CONNECTOR_HTTP_DISABLE_FAULTY_ENDPOINTS}
+ - name: NOTIFICATIONS_UNLEASH_ENABLED
+ value: ${NOTIFICATIONS_UNLEASH_ENABLED}
- name: QUARKUS_HTTP_PORT
value: ${QUARKUS_HTTP_PORT}
- name: QUARKUS_LOG_CATEGORY__COM_REDHAT_CLOUD_NOTIFICATIONS__LEVEL
@@ -105,6 +108,8 @@ objects:
value: ${SENTRY_DSN}${ENV_NAME}
- name: QUARKUS_LOG_SENTRY_ENVIRONMENT
value: ${ENV_NAME}
+ - name: QUARKUS_UNLEASH_ACTIVE
+ value: ${NOTIFICATIONS_UNLEASH_ENABLED}
parameters:
- name: CPU_LIMIT
description: CPU limit
@@ -170,6 +175,8 @@ parameters:
- name: NOTIFICATIONS_CONNECTOR_HTTP_DISABLE_FAULTY_ENDPOINTS
description: Add flags on connector response to let engine disable endpoint regarding HTTP error type
value: "true"
+- name: NOTIFICATIONS_UNLEASH_ENABLED
+ value: "false"
- name: QUARKUS_HTTP_PORT
description: Quarkus HTTP server port, defaulting to the default Clowder private port
value: "9000"
diff --git a/.rhcicd/clowdapp-connector-microsoft-teams.yaml b/.rhcicd/clowdapp-connector-microsoft-teams.yaml
index 4fc886a3d3..99bce42720 100644
--- a/.rhcicd/clowdapp-connector-microsoft-teams.yaml
+++ b/.rhcicd/clowdapp-connector-microsoft-teams.yaml
@@ -10,6 +10,7 @@ objects:
name: notifications-connector-microsoft-teams
spec:
envName: ${ENV_NAME}
+ featureFlags: true
kafkaTopics:
- topicName: platform.notifications.tocamel
partitions: 3
@@ -79,6 +80,8 @@ objects:
value: ${NOTIFICATIONS_CONNECTOR_SEDA_QUEUE_SIZE}
- name: NOTIFICATIONS_CONNECTOR_HTTP_DISABLE_FAULTY_ENDPOINTS
value: ${NOTIFICATIONS_CONNECTOR_HTTP_DISABLE_FAULTY_ENDPOINTS}
+ - name: NOTIFICATIONS_UNLEASH_ENABLED
+ value: ${NOTIFICATIONS_UNLEASH_ENABLED}
- name: QUARKUS_HTTP_PORT
value: ${QUARKUS_HTTP_PORT}
- name: QUARKUS_LOG_CATEGORY__COM_REDHAT_CLOUD_NOTIFICATIONS__LEVEL
@@ -105,6 +108,8 @@ objects:
value: ${SENTRY_DSN}${ENV_NAME}
- name: QUARKUS_LOG_SENTRY_ENVIRONMENT
value: ${ENV_NAME}
+ - name: QUARKUS_UNLEASH_ACTIVE
+ value: ${NOTIFICATIONS_UNLEASH_ENABLED}
parameters:
- name: CPU_LIMIT
description: CPU limit
@@ -170,6 +175,8 @@ parameters:
- name: NOTIFICATIONS_CONNECTOR_HTTP_DISABLE_FAULTY_ENDPOINTS
description: Add flags on connector response to let engine disable endpoint regarding HTTP error type
value: "true"
+- name: NOTIFICATIONS_UNLEASH_ENABLED
+ value: "false"
- name: QUARKUS_HTTP_PORT
description: Quarkus HTTP server port, defaulting to the default Clowder private port
value: "9000"
diff --git a/.rhcicd/clowdapp-connector-servicenow.yaml b/.rhcicd/clowdapp-connector-servicenow.yaml
index 8badb429a0..d42aaf3784 100644
--- a/.rhcicd/clowdapp-connector-servicenow.yaml
+++ b/.rhcicd/clowdapp-connector-servicenow.yaml
@@ -18,6 +18,7 @@ objects:
- drift
- notifications-backend
- policies-engine
+ featureFlags: true
kafkaTopics:
- topicName: platform.notifications.tocamel
partitions: 3
@@ -92,6 +93,8 @@ objects:
value: ${NOTIFICATIONS_CONNECTOR_SEDA_QUEUE_SIZE}
- name: NOTIFICATIONS_CONNECTOR_HTTP_DISABLE_FAULTY_ENDPOINTS
value: ${NOTIFICATIONS_CONNECTOR_HTTP_DISABLE_FAULTY_ENDPOINTS}
+ - name: NOTIFICATIONS_UNLEASH_ENABLED
+ value: ${NOTIFICATIONS_UNLEASH_ENABLED}
- name: QUARKUS_HTTP_PORT
value: ${QUARKUS_HTTP_PORT}
- name: QUARKUS_LOG_CATEGORY__COM_REDHAT_CLOUD_NOTIFICATIONS__LEVEL
@@ -118,6 +121,8 @@ objects:
value: ${SENTRY_DSN}${ENV_NAME}
- name: QUARKUS_LOG_SENTRY_ENVIRONMENT
value: ${ENV_NAME}
+ - name: QUARKUS_UNLEASH_ACTIVE
+ value: ${NOTIFICATIONS_UNLEASH_ENABLED}
parameters:
- name: CPU_LIMIT
description: CPU limit
@@ -183,6 +188,8 @@ parameters:
- name: NOTIFICATIONS_CONNECTOR_HTTP_DISABLE_FAULTY_ENDPOINTS
description: Add flags on connector response to let engine disable endpoint regarding HTTP error type
value: "true"
+- name: NOTIFICATIONS_UNLEASH_ENABLED
+ value: "false"
- name: QUARKUS_HTTP_PORT
description: Quarkus HTTP server port, defaulting to the default Clowder private port
value: "9000"
diff --git a/.rhcicd/clowdapp-connector-slack.yaml b/.rhcicd/clowdapp-connector-slack.yaml
index d66c09f327..8be2ff35f2 100644
--- a/.rhcicd/clowdapp-connector-slack.yaml
+++ b/.rhcicd/clowdapp-connector-slack.yaml
@@ -10,6 +10,7 @@ objects:
name: notifications-connector-slack
spec:
envName: ${ENV_NAME}
+ featureFlags: true
kafkaTopics:
- topicName: platform.notifications.tocamel
partitions: 3
@@ -73,6 +74,8 @@ objects:
value: ${NOTIFICATIONS_CONNECTOR_SEDA_CONCURRENT_CONSUMERS}
- name: NOTIFICATIONS_CONNECTOR_SEDA_QUEUE_SIZE
value: ${NOTIFICATIONS_CONNECTOR_SEDA_QUEUE_SIZE}
+ - name: NOTIFICATIONS_UNLEASH_ENABLED
+ value: ${NOTIFICATIONS_UNLEASH_ENABLED}
- name: QUARKUS_HTTP_PORT
value: ${QUARKUS_HTTP_PORT}
- name: QUARKUS_LOG_CATEGORY__COM_REDHAT_CLOUD_NOTIFICATIONS__LEVEL
@@ -99,6 +102,8 @@ objects:
value: ${SENTRY_DSN}${ENV_NAME}
- name: QUARKUS_LOG_SENTRY_ENVIRONMENT
value: ${ENV_NAME}
+ - name: QUARKUS_UNLEASH_ACTIVE
+ value: ${NOTIFICATIONS_UNLEASH_ENABLED}
parameters:
- name: CPU_LIMIT
description: CPU limit
@@ -155,6 +160,8 @@ parameters:
- name: NOTIFICATIONS_LOG_LEVEL
description: Log level of Notifications
value: INFO
+- name: NOTIFICATIONS_UNLEASH_ENABLED
+ value: "false"
- name: QUARKUS_HTTP_PORT
description: Quarkus HTTP server port, defaulting to the default Clowder private port
value: "9000"
diff --git a/.rhcicd/clowdapp-connector-splunk.yaml b/.rhcicd/clowdapp-connector-splunk.yaml
index 7ad5255f56..799e4f5b87 100644
--- a/.rhcicd/clowdapp-connector-splunk.yaml
+++ b/.rhcicd/clowdapp-connector-splunk.yaml
@@ -18,6 +18,7 @@ objects:
- drift
- notifications-backend
- policies-engine
+ featureFlags: true
kafkaTopics:
- topicName: platform.notifications.tocamel
partitions: 3
@@ -92,6 +93,8 @@ objects:
value: ${NOTIFICATIONS_CONNECTOR_SEDA_QUEUE_SIZE}
- name: NOTIFICATIONS_CONNECTOR_HTTP_DISABLE_FAULTY_ENDPOINTS
value: ${NOTIFICATIONS_CONNECTOR_HTTP_DISABLE_FAULTY_ENDPOINTS}
+ - name: NOTIFICATIONS_UNLEASH_ENABLED
+ value: ${NOTIFICATIONS_UNLEASH_ENABLED}
- name: QUARKUS_HTTP_PORT
value: ${QUARKUS_HTTP_PORT}
- name: QUARKUS_LOG_CATEGORY__COM_REDHAT_CLOUD_NOTIFICATIONS__LEVEL
@@ -118,6 +121,8 @@ objects:
value: ${SENTRY_DSN}${ENV_NAME}
- name: QUARKUS_LOG_SENTRY_ENVIRONMENT
value: ${ENV_NAME}
+ - name: QUARKUS_UNLEASH_ACTIVE
+ value: ${NOTIFICATIONS_UNLEASH_ENABLED}
parameters:
- name: CPU_LIMIT
description: CPU limit
@@ -183,6 +188,8 @@ parameters:
- name: NOTIFICATIONS_CONNECTOR_HTTP_DISABLE_FAULTY_ENDPOINTS
description: Add flags on connector response to let engine disable endpoint regarding HTTP error type
value: "true"
+- name: NOTIFICATIONS_UNLEASH_ENABLED
+ value: "false"
- name: QUARKUS_HTTP_PORT
description: Quarkus HTTP server port, defaulting to the default Clowder private port
value: "9000"
diff --git a/.rhcicd/clowdapp-connector-webhook.yaml b/.rhcicd/clowdapp-connector-webhook.yaml
index 2e38d3a7ba..a5b3367718 100644
--- a/.rhcicd/clowdapp-connector-webhook.yaml
+++ b/.rhcicd/clowdapp-connector-webhook.yaml
@@ -16,6 +16,7 @@ objects:
- sources-api
optionalDependencies:
- notifications-backend
+ featureFlags: true
kafkaTopics:
- topicName: platform.notifications.tocamel
partitions: 3
@@ -94,6 +95,8 @@ objects:
value: ${NOTIFICATIONS_CONNECTOR_SEDA_QUEUE_SIZE}
- name: NOTIFICATIONS_CONNECTOR_HTTP_DISABLE_FAULTY_ENDPOINTS
value: ${NOTIFICATIONS_CONNECTOR_HTTP_DISABLE_FAULTY_ENDPOINTS}
+ - name: NOTIFICATIONS_UNLEASH_ENABLED
+ value: ${NOTIFICATIONS_UNLEASH_ENABLED}
- name: QUARKUS_HTTP_PORT
value: ${QUARKUS_HTTP_PORT}
- name: QUARKUS_LOG_CATEGORY__COM_REDHAT_CLOUD_NOTIFICATIONS__LEVEL
@@ -120,6 +123,8 @@ objects:
value: ${SENTRY_DSN}${ENV_NAME}
- name: QUARKUS_LOG_SENTRY_ENVIRONMENT
value: ${ENV_NAME}
+ - name: QUARKUS_UNLEASH_ACTIVE
+ value: ${NOTIFICATIONS_UNLEASH_ENABLED}
parameters:
- name: CPU_LIMIT
description: CPU limit
@@ -191,6 +196,8 @@ parameters:
- name: NOTIFICATIONS_CONNECTOR_HTTP_DISABLE_FAULTY_ENDPOINTS
description: Add flags on connector response to let engine disable endpoint regarding HTTP error type
value: "true"
+- name: NOTIFICATIONS_UNLEASH_ENABLED
+ value: "false"
- name: QUARKUS_HTTP_PORT
description: Quarkus HTTP server port, defaulting to the default Clowder private port
value: "9000"
diff --git a/.rhcicd/clowdapp-recipients-resolver.yaml b/.rhcicd/clowdapp-recipients-resolver.yaml
index ae4e25b721..ea3df97aa6 100644
--- a/.rhcicd/clowdapp-recipients-resolver.yaml
+++ b/.rhcicd/clowdapp-recipients-resolver.yaml
@@ -14,6 +14,7 @@ objects:
envName: ${ENV_NAME}
dependencies:
- rbac
+ featureFlags: true
deployments:
- name: service
minReplicas: ${{MIN_REPLICAS}}
@@ -73,6 +74,8 @@ objects:
value: ${NOTIFICATIONS_RECIPIENTS_RESOLVER_FETCH_USERS_MBOP_ENABLED}
- name: NOTIFICATIONS_RECIPIENTS_RESOLVER_FETCH_USERS_RBAC_ENABLED
value: ${NOTIFICATIONS_RECIPIENTS_RESOLVER_FETCH_USERS_RBAC_ENABLED}
+ - name: NOTIFICATIONS_UNLEASH_ENABLED
+ value: ${NOTIFICATIONS_UNLEASH_ENABLED}
- name: QUARKUS_CACHE_CAFFEINE_RBAC_RECIPIENT_USERS_PROVIDER_GET_USERS_EXPIRE_AFTER_WRITE
value: ${RBAC_USERS_RETENTION_DELAY}
- name: QUARKUS_CACHE_CAFFEINE_RBAC_RECIPIENT_USERS_PROVIDER_GET_GROUP_USERS_EXPIRE_AFTER_WRITE
@@ -123,6 +126,8 @@ objects:
value: ${IT_S2S_READ_TIMEOUT}
- name: QUARKUS_REST_CLIENT_RBAC_S2S_READ_TIMEOUT
value: ${RBAC_S2S_READ_TIMEOUT}
+ - name: QUARKUS_UNLEASH_ACTIVE
+ value: ${NOTIFICATIONS_UNLEASH_ENABLED}
- name: RBAC_SERVICE_TO_SERVICE_APPLICATION
value: ${RBAC_SERVICE_TO_SERVICE_APP}
- name: RBAC_SERVICE_TO_SERVICE_SECRET_MAP
@@ -176,6 +181,8 @@ parameters:
- name: NOTIFICATIONS_RECIPIENTS_RESOLVER_FETCH_USERS_RBAC_ENABLED
description: Users from an organization will be retrieved from RBAC if true
value: "true"
+- name: NOTIFICATIONS_UNLEASH_ENABLED
+ value: "false"
- name: QUARKUS_LOG_CATEGORY__ORG_JBOSS_RESTEASY_REACTIVE_CLIENT_LOGGING__LEVEL
description: When QUARKUS_REST_CLIENT_LOGGING_SCOPE is set to 'request-response', this logger level needs to be set to DEBUG
value: INFO
diff --git a/aggregator/pom.xml b/aggregator/pom.xml
index 3df63d9ee4..8b5d5f3be1 100644
--- a/aggregator/pom.xml
+++ b/aggregator/pom.xml
@@ -61,6 +61,11 @@
quarkus-logging-cloudwatch
${quarkus-logging-cloudwatch.version}
+
+ io.quarkiverse.unleash
+ quarkus-unleash
+ ${quarkus-unleash.version}
+
diff --git a/aggregator/src/main/java/com/redhat/cloud/notifications/DailyEmailAggregationJob.java b/aggregator/src/main/java/com/redhat/cloud/notifications/DailyEmailAggregationJob.java
index e036164b78..de45b20f4b 100644
--- a/aggregator/src/main/java/com/redhat/cloud/notifications/DailyEmailAggregationJob.java
+++ b/aggregator/src/main/java/com/redhat/cloud/notifications/DailyEmailAggregationJob.java
@@ -1,6 +1,6 @@
package com.redhat.cloud.notifications;
-import com.redhat.cloud.notifications.config.FeatureFlipper;
+import com.redhat.cloud.notifications.config.AggregatorConfig;
import com.redhat.cloud.notifications.db.AggregationOrgConfigRepository;
import com.redhat.cloud.notifications.db.EmailAggregationRepository;
import com.redhat.cloud.notifications.ingress.Action;
@@ -44,7 +44,7 @@ public class DailyEmailAggregationJob {
public static final String EVENT_TYPE_NAME = "aggregation";
@Inject
- FeatureFlipper featureFlipper;
+ AggregatorConfig aggregatorConfig;
@Inject
EmailAggregationRepository emailAggregationResources;
@@ -80,7 +80,7 @@ public void processDailyEmail() {
aggregationOrgConfigRepository.createMissingDefaultConfiguration(defaultDailyDigestTime);
List aggregationCommands = processAggregateEmailsWithOrgPref(now, registry);
Log.infof("found %s commands", aggregationCommands.size());
- if (!featureFlipper.isSingleDailyDigestEnabled()) {
+ if (!aggregatorConfig.isSingleDailyDigestEnabled()) {
aggregationCommands.stream().forEach(aggregationCommand -> sendIt(List.of(aggregationCommand)));
} else {
aggregationCommands.stream().collect(Collectors.groupingBy(AggregationCommand::getOrgId))
@@ -147,7 +147,7 @@ private void sendIt(List aggregationCommands) {
.withTimestamp(LocalDateTime.now(UTC))
.withEvents(eventList)
.withContext(new Context.ContextBuilder()
- .withAdditionalProperty("single_daily_digest_enabled", featureFlipper.isSingleDailyDigestEnabled())
+ .withAdditionalProperty("single_daily_digest_enabled", aggregatorConfig.isSingleDailyDigestEnabled())
.build())
.build();
diff --git a/aggregator/src/main/java/com/redhat/cloud/notifications/config/AggregatorConfig.java b/aggregator/src/main/java/com/redhat/cloud/notifications/config/AggregatorConfig.java
new file mode 100644
index 0000000000..a2fc4d46a3
--- /dev/null
+++ b/aggregator/src/main/java/com/redhat/cloud/notifications/config/AggregatorConfig.java
@@ -0,0 +1,61 @@
+package com.redhat.cloud.notifications.config;
+
+import io.getunleash.Unleash;
+import io.quarkus.logging.Log;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.event.Observes;
+import jakarta.enterprise.event.Startup;
+import jakarta.inject.Inject;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+@ApplicationScoped
+public class AggregatorConfig {
+
+ /*
+ * Env vars configuration
+ */
+ private static final String UNLEASH = "notifications.unleash.enabled";
+
+ /*
+ * Unleash configuration
+ */
+ private static final String SINGLE_DAILY_DIGEST = toggleName("single-daily-digest");
+
+ private static String toggleName(String feature) {
+ return String.format("notifications-aggregator.%s.enabled", feature);
+ }
+
+ @ConfigProperty(name = UNLEASH, defaultValue = "false")
+ @Deprecated(forRemoval = true, since = "To be removed when we're done migrating to Unleash in all environments")
+ boolean unleashEnabled;
+
+ @ConfigProperty(name = "notifications.bundle.level.digest.enabled", defaultValue = "false")
+ @Deprecated(forRemoval = true, since = "To be removed when we're done migrating to Unleash in all environments")
+ boolean singleDailyDigestEnabled;
+
+ @Inject
+ Unleash unleash;
+
+ void logConfigAtStartup(@Observes Startup event) {
+
+ Map config = new TreeMap<>();
+ config.put(SINGLE_DAILY_DIGEST, isSingleDailyDigestEnabled());
+ config.put(UNLEASH, unleashEnabled);
+
+ Log.info("=== Startup configuration ===");
+ config.forEach((key, value) -> {
+ Log.infof("%s=%s", key, value);
+ });
+ }
+
+ public boolean isSingleDailyDigestEnabled() {
+ if (unleashEnabled) {
+ return unleash.isEnabled(SINGLE_DAILY_DIGEST, false);
+ } else {
+ return singleDailyDigestEnabled;
+ }
+ }
+}
diff --git a/aggregator/src/main/java/com/redhat/cloud/notifications/config/FeatureFlipper.java b/aggregator/src/main/java/com/redhat/cloud/notifications/config/FeatureFlipper.java
deleted file mode 100644
index 7b39d702ed..0000000000
--- a/aggregator/src/main/java/com/redhat/cloud/notifications/config/FeatureFlipper.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package com.redhat.cloud.notifications.config;
-
-import io.quarkus.logging.Log;
-import io.quarkus.runtime.StartupEvent;
-import io.quarkus.runtime.configuration.ProfileManager;
-import jakarta.enterprise.context.ApplicationScoped;
-import jakarta.enterprise.event.Observes;
-import org.eclipse.microprofile.config.inject.ConfigProperty;
-
-import static io.quarkus.runtime.LaunchMode.TEST;
-
-/**
- *
- * This class centralizes all configuration values used to enable or disable a feature.
- *
- *
- * Any config value used to flip a temporary or permanent feature can be added that way:
- *
- * @ApplicationScoped
- * public class FeatureFlipper {
- *
- * @ConfigProperty(name = "amazing-feature.enabled", defaultValue = "false")
- * boolean amazingFeatureEnabled;
- *
- * public boolean isAmazingFeatureEnabled() {
- * return amazingFeatureEnabled;
- * }
- *
- * public void setAmazingFeatureEnabled(boolean amazingFeatureEnabled) {
- * // Add this if the config value should only be overridden in TEST launch mode.
- * checkTestLaunchMode();
- * this.amazingFeatureEnabled = amazingFeatureEnabled;
- * }
- * }
- *
- *
- */
-@ApplicationScoped
-public class FeatureFlipper {
-
- @ConfigProperty(name = "notifications.bundle.level.digest.enabled", defaultValue = "false")
- boolean singleDailyDigestEnabled;
-
- void logFeaturesStatusAtStartup(@Observes StartupEvent event) {
- Log.infof("=== %s startup status ===", FeatureFlipper.class.getSimpleName());
- Log.infof("The daily digest at bundle level is %s", singleDailyDigestEnabled ? "enabled" : "disabled");
-
- }
-
- public boolean isSingleDailyDigestEnabled() {
- return singleDailyDigestEnabled;
- }
-
- public void setSingleDailyDigestEnabled(boolean singleDailyDigestEnabled) {
- checkTestLaunchMode();
- this.singleDailyDigestEnabled = singleDailyDigestEnabled;
- }
-
- /**
- * This method throws an {@link IllegalStateException} if it is invoked with a launch mode different from
- * {@link io.quarkus.runtime.LaunchMode#TEST TEST}. It should be added to methods that allow overriding a
- * config value from tests only, preventing doing so from runtime code.
- */
- private static void checkTestLaunchMode() {
- if (ProfileManager.getLaunchMode() != TEST) {
- throw new IllegalStateException("Illegal config value override detected");
- }
- }
-}
diff --git a/aggregator/src/main/resources/application.properties b/aggregator/src/main/resources/application.properties
index 9da22f374c..b0ff72a250 100644
--- a/aggregator/src/main/resources/application.properties
+++ b/aggregator/src/main/resources/application.properties
@@ -38,3 +38,7 @@ quarkus.log.cloudwatch.access-key-id=placeholder
quarkus.log.cloudwatch.access-key-secret=placeholder
%test.quarkus.flyway.migrate-at-start=true
+
+quarkus.unleash.active=false
+quarkus.unleash.synchronous-fetch-on-initialisation=true
+quarkus.unleash.url=http://localhost:4242
diff --git a/backend/src/main/java/com/redhat/cloud/notifications/config/BackendConfig.java b/backend/src/main/java/com/redhat/cloud/notifications/config/BackendConfig.java
new file mode 100644
index 0000000000..99693e27b0
--- /dev/null
+++ b/backend/src/main/java/com/redhat/cloud/notifications/config/BackendConfig.java
@@ -0,0 +1,133 @@
+package com.redhat.cloud.notifications.config;
+
+import io.getunleash.Unleash;
+import io.quarkus.logging.Log;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.event.Observes;
+import jakarta.enterprise.event.Startup;
+import jakarta.inject.Inject;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+@ApplicationScoped
+public class BackendConfig {
+
+ /*
+ * Env vars configuration
+ */
+ private static final String DEFAULT_TEMPLATE = "notifications.use-default-template";
+ private static final String EMAILS_ONLY_MODE = "notifications.emails-only-mode.enabled";
+ private static final String INSTANT_EMAILS = "notifications.instant-emails.enabled";
+ private static final String UNLEASH = "notifications.unleash.enabled";
+
+ /*
+ * Unleash configuration
+ */
+ private static final String DRAWER = toggleName("drawer");
+ private static final String FORBID_SLACK_CHANNEL_USAGE = toggleName("forbid-slack-channel-usage");
+ private static final String UNIQUE_BG_NAME = toggleName("unique-bg-name");
+ private static final String UNIQUE_INTEGRATION_NAME = toggleName("unique-integration-name");
+
+ private static String toggleName(String feature) {
+ return String.format("notifications-backend.%s.enabled", feature);
+ }
+
+ @ConfigProperty(name = UNLEASH, defaultValue = "false")
+ @Deprecated(forRemoval = true, since = "To be removed when we're done migrating to Unleash in all environments")
+ boolean unleashEnabled;
+
+ @ConfigProperty(name = "notifications.enforce-bg-name-unicity", defaultValue = "false")
+ @Deprecated(forRemoval = true, since = "To be removed when we're done migrating to Unleash in all environments")
+ boolean enforceBehaviorGroupNameUnicity;
+
+ @ConfigProperty(name = "notifications.enforce-integration-name-unicity", defaultValue = "false")
+ @Deprecated(forRemoval = true, since = "To be removed when we're done migrating to Unleash in all environments")
+ boolean enforceIntegrationNameUnicity;
+
+ @ConfigProperty(name = "notifications.drawer.enabled", defaultValue = "false")
+ @Deprecated(forRemoval = true, since = "To be removed when we're done migrating to Unleash in all environments")
+ boolean drawerEnabled;
+
+ @ConfigProperty(name = "notifications.slack.forbid.channel.usage.enabled", defaultValue = "false")
+ @Deprecated(forRemoval = true, since = "To be removed when we're done migrating to Unleash in all environments")
+ boolean slackForbidChannelUsageEnabled;
+
+ // Only used in stage environments.
+ @ConfigProperty(name = DEFAULT_TEMPLATE, defaultValue = "false")
+ boolean defaultTemplateEnabled;
+
+ // Only used in special environments.
+ @ConfigProperty(name = EMAILS_ONLY_MODE, defaultValue = "false")
+ boolean emailsOnlyModeEnabled;
+
+ // Only used in special environments.
+ @ConfigProperty(name = INSTANT_EMAILS, defaultValue = "false")
+ boolean instantEmailsEnabled;
+
+ @Inject
+ Unleash unleash;
+
+ void logConfigAtStartup(@Observes Startup event) {
+
+ Map config = new TreeMap<>();
+ config.put(DEFAULT_TEMPLATE, isDefaultTemplateEnabled());
+ config.put(DRAWER, isDrawerEnabled());
+ config.put(EMAILS_ONLY_MODE, isEmailsOnlyModeEnabled());
+ config.put(INSTANT_EMAILS, isInstantEmailsEnabled());
+ config.put(FORBID_SLACK_CHANNEL_USAGE, isForbidSlackChannelUsage());
+ config.put(UNIQUE_BG_NAME, isUniqueBgNameEnabled());
+ config.put(UNIQUE_INTEGRATION_NAME, isUniqueIntegrationNameEnabled());
+ config.put(UNLEASH, unleashEnabled);
+
+ Log.info("=== Startup configuration ===");
+ config.forEach((key, value) -> {
+ Log.infof("%s=%s", key, value);
+ });
+ }
+
+ public boolean isDefaultTemplateEnabled() {
+ return defaultTemplateEnabled;
+ }
+
+ public boolean isDrawerEnabled() {
+ if (unleashEnabled) {
+ return unleash.isEnabled(DRAWER, false);
+ } else {
+ return drawerEnabled;
+ }
+ }
+
+ public boolean isEmailsOnlyModeEnabled() {
+ return emailsOnlyModeEnabled;
+ }
+
+ public boolean isForbidSlackChannelUsage() {
+ if (unleashEnabled) {
+ return unleash.isEnabled(FORBID_SLACK_CHANNEL_USAGE, false);
+ } else {
+ return slackForbidChannelUsageEnabled;
+ }
+ }
+
+ public boolean isInstantEmailsEnabled() {
+ return instantEmailsEnabled;
+ }
+
+ public boolean isUniqueBgNameEnabled() {
+ if (unleashEnabled) {
+ return unleash.isEnabled(UNIQUE_BG_NAME, false);
+ } else {
+ return enforceBehaviorGroupNameUnicity;
+ }
+ }
+
+ public boolean isUniqueIntegrationNameEnabled() {
+ if (unleashEnabled) {
+ return unleash.isEnabled(UNIQUE_INTEGRATION_NAME, false);
+ } else {
+ return enforceIntegrationNameUnicity;
+ }
+ }
+}
diff --git a/backend/src/main/java/com/redhat/cloud/notifications/db/repositories/BehaviorGroupRepository.java b/backend/src/main/java/com/redhat/cloud/notifications/db/repositories/BehaviorGroupRepository.java
index 8839a57f5d..c5cca9cfa2 100644
--- a/backend/src/main/java/com/redhat/cloud/notifications/db/repositories/BehaviorGroupRepository.java
+++ b/backend/src/main/java/com/redhat/cloud/notifications/db/repositories/BehaviorGroupRepository.java
@@ -1,6 +1,6 @@
package com.redhat.cloud.notifications.db.repositories;
-import com.redhat.cloud.notifications.config.FeatureFlipper;
+import com.redhat.cloud.notifications.config.BackendConfig;
import com.redhat.cloud.notifications.db.Query;
import com.redhat.cloud.notifications.models.BehaviorGroup;
import com.redhat.cloud.notifications.models.Bundle;
@@ -39,7 +39,7 @@ public class BehaviorGroupRepository {
EntityManager entityManager;
@Inject
- FeatureFlipper featureFlipper;
+ BackendConfig backendConfig;
/**
* Counts all the behavior groups by their org id and event type id.
@@ -207,7 +207,7 @@ void update(String orgId, BehaviorGroup behaviorGroup, boolean isDefaultBehavior
}
private void checkBehaviorGroupDisplayNameDuplicate(String orgId, BehaviorGroup behaviorGroup, boolean isDefaultBehaviorGroup) {
- if (!featureFlipper.isEnforceBehaviorGroupNameUnicity()) {
+ if (!backendConfig.isUniqueBgNameEnabled()) {
// The check is disabled from configuration.
return;
}
diff --git a/backend/src/main/java/com/redhat/cloud/notifications/db/repositories/EndpointRepository.java b/backend/src/main/java/com/redhat/cloud/notifications/db/repositories/EndpointRepository.java
index fce8892a3c..783f08f1e0 100644
--- a/backend/src/main/java/com/redhat/cloud/notifications/db/repositories/EndpointRepository.java
+++ b/backend/src/main/java/com/redhat/cloud/notifications/db/repositories/EndpointRepository.java
@@ -1,6 +1,6 @@
package com.redhat.cloud.notifications.db.repositories;
-import com.redhat.cloud.notifications.config.FeatureFlipper;
+import com.redhat.cloud.notifications.config.BackendConfig;
import com.redhat.cloud.notifications.db.Query;
import com.redhat.cloud.notifications.db.builder.QueryBuilder;
import com.redhat.cloud.notifications.db.builder.WhereBuilder;
@@ -41,10 +41,10 @@ public class EndpointRepository {
EntityManager entityManager;
@Inject
- FeatureFlipper featureFlipper;
+ BackendConfig backendConfig;
public void checkEndpointNameDuplicate(Endpoint endpoint) {
- if (!featureFlipper.isEnforceIntegrationNameUnicity()) {
+ if (!backendConfig.isUniqueIntegrationNameEnabled()) {
// Check disabled from configuration
return;
}
diff --git a/backend/src/main/java/com/redhat/cloud/notifications/db/repositories/SubscriptionRepository.java b/backend/src/main/java/com/redhat/cloud/notifications/db/repositories/SubscriptionRepository.java
index 380575db1c..4562010783 100644
--- a/backend/src/main/java/com/redhat/cloud/notifications/db/repositories/SubscriptionRepository.java
+++ b/backend/src/main/java/com/redhat/cloud/notifications/db/repositories/SubscriptionRepository.java
@@ -1,6 +1,6 @@
package com.redhat.cloud.notifications.db.repositories;
-import com.redhat.cloud.notifications.config.FeatureFlipper;
+import com.redhat.cloud.notifications.config.BackendConfig;
import com.redhat.cloud.notifications.models.EventType;
import com.redhat.cloud.notifications.models.EventTypeEmailSubscription;
import com.redhat.cloud.notifications.models.SubscriptionType;
@@ -23,7 +23,7 @@ public class SubscriptionRepository {
EntityManager entityManager;
@Inject
- FeatureFlipper featureFlipper;
+ BackendConfig backendConfig;
public void subscribe(String orgId, String username, UUID eventTypeId, SubscriptionType subscriptionType) {
updateSubscription(orgId, username, eventTypeId, subscriptionType, true);
@@ -93,7 +93,7 @@ public List getEmailSubscriptionsPerEventTypeForUser
}
private List getAvailableTypes() {
- if (featureFlipper.isDrawerEnabled()) {
+ if (backendConfig.isDrawerEnabled()) {
return List.of(INSTANT, DAILY, DRAWER);
} else {
return List.of(INSTANT, DAILY);
diff --git a/backend/src/main/java/com/redhat/cloud/notifications/db/repositories/TemplateRepository.java b/backend/src/main/java/com/redhat/cloud/notifications/db/repositories/TemplateRepository.java
index 78e418fdaa..a2bebb42f5 100644
--- a/backend/src/main/java/com/redhat/cloud/notifications/db/repositories/TemplateRepository.java
+++ b/backend/src/main/java/com/redhat/cloud/notifications/db/repositories/TemplateRepository.java
@@ -1,6 +1,6 @@
package com.redhat.cloud.notifications.db.repositories;
-import com.redhat.cloud.notifications.config.FeatureFlipper;
+import com.redhat.cloud.notifications.config.BackendConfig;
import com.redhat.cloud.notifications.models.AggregationEmailTemplate;
import com.redhat.cloud.notifications.models.Application;
import com.redhat.cloud.notifications.models.EventType;
@@ -29,7 +29,7 @@ public class TemplateRepository {
EntityManager entityManager;
@Inject
- FeatureFlipper featureFlipper;
+ BackendConfig backendConfig;
@Transactional
public Template createTemplate(Template template) {
@@ -317,7 +317,7 @@ private Template findTemplate(UUID id, String notFoundMessage) {
public boolean isEmailSubscriptionSupported(String bundleName, String appName, SubscriptionType subscriptionType) {
switch (subscriptionType) {
case INSTANT:
- if (featureFlipper.isUseDefaultTemplate()) {
+ if (backendConfig.isDefaultTemplateEnabled()) {
return true;
}
@@ -330,7 +330,7 @@ public boolean isEmailSubscriptionSupported(String bundleName, String appName, S
case DAILY:
return isEmailAggregationSupported(bundleName, appName, List.of(subscriptionType));
case DRAWER:
- return featureFlipper.isDrawerEnabled();
+ return backendConfig.isDrawerEnabled();
default:
return false;
}
diff --git a/backend/src/main/java/com/redhat/cloud/notifications/routers/EndpointResource.java b/backend/src/main/java/com/redhat/cloud/notifications/routers/EndpointResource.java
index c01eaa3de6..fc9b6d3ca3 100644
--- a/backend/src/main/java/com/redhat/cloud/notifications/routers/EndpointResource.java
+++ b/backend/src/main/java/com/redhat/cloud/notifications/routers/EndpointResource.java
@@ -4,7 +4,7 @@
import com.redhat.cloud.notifications.auth.ConsoleIdentityProvider;
import com.redhat.cloud.notifications.auth.principal.rhid.RhIdPrincipal;
import com.redhat.cloud.notifications.auth.rbac.RbacGroupValidator;
-import com.redhat.cloud.notifications.config.FeatureFlipper;
+import com.redhat.cloud.notifications.config.BackendConfig;
import com.redhat.cloud.notifications.db.Query;
import com.redhat.cloud.notifications.db.repositories.EndpointRepository;
import com.redhat.cloud.notifications.db.repositories.NotificationRepository;
@@ -100,7 +100,7 @@ public class EndpointResource {
RbacGroupValidator rbacGroupValidator;
@Inject
- FeatureFlipper featureFlipper;
+ BackendConfig backendConfig;
/**
* Used to create the secrets in Sources and update the endpoint's properties' IDs.
@@ -296,7 +296,7 @@ public Endpoint createEndpoint(@Context SecurityContext sec,
private void checkSlackChannel(CamelProperties camelProperties, CamelProperties previousCamelProperties) {
String channel = camelProperties.getExtras().get("channel");
- if (featureFlipper.isSlackForbidChannelUsageEnabled()) {
+ if (backendConfig.isForbidSlackChannelUsage()) {
// throw an exception if we receive a channel on endpoint creation
if (null == previousCamelProperties && channel != null) {
throw new BadRequestException(DEPRECATED_SLACK_CHANNEL_ERROR);
@@ -562,7 +562,7 @@ private static void checkSystemEndpoint(EndpointType endpointType) {
}
private boolean isEndpointTypeAllowed(EndpointType endpointType) {
- return !featureFlipper.isEmailsOnlyMode() || endpointType.isSystemEndpointType;
+ return !backendConfig.isEmailsOnlyModeEnabled() || endpointType.isSystemEndpointType;
}
/**
diff --git a/backend/src/main/java/com/redhat/cloud/notifications/routers/UserConfigResource.java b/backend/src/main/java/com/redhat/cloud/notifications/routers/UserConfigResource.java
index 73bd09d355..011f974fc7 100644
--- a/backend/src/main/java/com/redhat/cloud/notifications/routers/UserConfigResource.java
+++ b/backend/src/main/java/com/redhat/cloud/notifications/routers/UserConfigResource.java
@@ -3,7 +3,7 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.redhat.cloud.notifications.Constants;
-import com.redhat.cloud.notifications.config.FeatureFlipper;
+import com.redhat.cloud.notifications.config.BackendConfig;
import com.redhat.cloud.notifications.db.repositories.ApplicationRepository;
import com.redhat.cloud.notifications.db.repositories.BundleRepository;
import com.redhat.cloud.notifications.db.repositories.EventTypeRepository;
@@ -66,7 +66,7 @@ public class UserConfigResource {
TemplateRepository templateRepository;
@Inject
- FeatureFlipper featureFlipper;
+ BackendConfig backendConfig;
@Path(Constants.API_NOTIFICATIONS_V_1_0 + "/user-config")
public static class V1 extends UserConfigResource {
@@ -92,7 +92,7 @@ public Response saveSettingsByEventType(@Context SecurityContext sec, @NotNull @
// If the instant emails are disabled, we need to check that the request
// does not contain any subscription with SubscriptionType.INSTANT.
- if (!featureFlipper.isInstantEmailsEnabled() && userSettings.bundles.values().stream()
+ if (!backendConfig.isInstantEmailsEnabled() && userSettings.bundles.values().stream()
.flatMap(bundleSettings -> bundleSettings.applications.values().stream())
.flatMap(appSettings -> appSettings.eventTypes.values().stream())
.flatMap(eventTypeSettings -> eventTypeSettings.emailSubscriptionTypes.keySet().stream())
@@ -218,7 +218,7 @@ private void addApplicationStructureDetails(final SettingsValuesByEventType sett
eventTypeSettingsValue.hasForcedEmail = withForcedEmails;
eventTypeSettingsValue.subscriptionLocked = eventType.isSubscriptionLocked();
for (SubscriptionType subscriptionType : SubscriptionType.values()) {
- if (featureFlipper.isInstantEmailsEnabled() || subscriptionType != INSTANT) {
+ if (backendConfig.isInstantEmailsEnabled() || subscriptionType != INSTANT) {
boolean supported = templateRepository.isEmailSubscriptionSupported(bundle.getName(), application.getName(), subscriptionType);
if (supported) {
boolean subscribedByDefault = subscriptionType.isSubscribedByDefault() || eventType.isSubscribedByDefault();
diff --git a/backend/src/main/java/com/redhat/cloud/notifications/routers/UserPreferencesForPolicy.java b/backend/src/main/java/com/redhat/cloud/notifications/routers/UserPreferencesForPolicy.java
index 066df696cc..2c336ade1e 100644
--- a/backend/src/main/java/com/redhat/cloud/notifications/routers/UserPreferencesForPolicy.java
+++ b/backend/src/main/java/com/redhat/cloud/notifications/routers/UserPreferencesForPolicy.java
@@ -1,7 +1,7 @@
package com.redhat.cloud.notifications.routers;
import com.redhat.cloud.notifications.Constants;
-import com.redhat.cloud.notifications.config.FeatureFlipper;
+import com.redhat.cloud.notifications.config.BackendConfig;
import com.redhat.cloud.notifications.db.repositories.SubscriptionRepository;
import com.redhat.cloud.notifications.models.EventTypeEmailSubscription;
import com.redhat.cloud.notifications.routers.models.UserConfigPreferences;
@@ -30,7 +30,7 @@ public class UserPreferencesForPolicy {
SubscriptionRepository subscriptionRepository;
@Inject
- FeatureFlipper featureFlipper;
+ BackendConfig backendConfig;
@Path(Constants.API_NOTIFICATIONS_V_1_0 + "/user-config")
public static class V1 extends UserPreferencesForPolicy {
@@ -64,7 +64,7 @@ public UserConfigPreferences getPreferences(
for (EventTypeEmailSubscription subscribedEvent : subscriptionTypes) {
if (DAILY == subscribedEvent.getType()) {
preferences.setDailyEmail(subscribedEvent.isSubscribed());
- } else if (featureFlipper.isInstantEmailsEnabled() && INSTANT == subscribedEvent.getType()) {
+ } else if (backendConfig.isInstantEmailsEnabled() && INSTANT == subscribedEvent.getType()) {
preferences.setInstantEmail(subscribedEvent.isSubscribed());
}
}
diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties
index 18fe864dc3..8abba48978 100644
--- a/backend/src/main/resources/application.properties
+++ b/backend/src/main/resources/application.properties
@@ -97,3 +97,7 @@ quarkus.otel.service.name=notifications-backend
# is missing. In the case that you are using a real Sources application to test the integration, you will need to
# set this PSK in the SOURCES_PSKS environment variable on that end.
sources.psk=development-value-123
+
+quarkus.unleash.active=false
+quarkus.unleash.synchronous-fetch-on-initialisation=true
+quarkus.unleash.url=http://localhost:4242
diff --git a/backend/src/test/java/com/redhat/cloud/notifications/db/repositories/BehaviorGroupRepositoryTest.java b/backend/src/test/java/com/redhat/cloud/notifications/db/repositories/BehaviorGroupRepositoryTest.java
index 5ec30358b0..b1de2d8144 100644
--- a/backend/src/test/java/com/redhat/cloud/notifications/db/repositories/BehaviorGroupRepositoryTest.java
+++ b/backend/src/test/java/com/redhat/cloud/notifications/db/repositories/BehaviorGroupRepositoryTest.java
@@ -2,7 +2,7 @@
import com.redhat.cloud.notifications.TestHelpers;
import com.redhat.cloud.notifications.TestLifecycleManager;
-import com.redhat.cloud.notifications.config.FeatureFlipper;
+import com.redhat.cloud.notifications.config.BackendConfig;
import com.redhat.cloud.notifications.db.DbIsolatedTest;
import com.redhat.cloud.notifications.db.Query;
import com.redhat.cloud.notifications.db.ResourceHelpers;
@@ -13,6 +13,7 @@
import com.redhat.cloud.notifications.models.Endpoint;
import com.redhat.cloud.notifications.models.EventType;
import com.redhat.cloud.notifications.models.EventTypeBehavior;
+import io.quarkus.test.InjectMock;
import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
@@ -42,6 +43,7 @@
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.when;
@QuarkusTest
@QuarkusTestResource(TestLifecycleManager.class)
@@ -58,49 +60,41 @@ public class BehaviorGroupRepositoryTest extends DbIsolatedTest {
@Inject
BehaviorGroupRepository behaviorGroupRepository;
- @Inject
- FeatureFlipper featureFlipper;
+ @InjectMock
+ BackendConfig backendConfig;
@Test
void shouldThrowExceptionWhenCreatingWithExistingDisplayNameAndSameOrgId() {
- try {
- featureFlipper.setEnforceBehaviorGroupNameUnicity(true);
- Bundle bundle = resourceHelpers.createBundle();
- BehaviorGroup behaviorGroup1 = resourceHelpers.createBehaviorGroup(DEFAULT_ACCOUNT_ID, DEFAULT_ORG_ID, "displayName", bundle.getId());
-
- BehaviorGroup behaviorGroup2 = new BehaviorGroup();
- behaviorGroup2.setAccountId(behaviorGroup1.getAccountId());
- behaviorGroup2.setOrgId(behaviorGroup1.getOrgId());
- behaviorGroup2.setDisplayName(behaviorGroup1.getDisplayName());
- behaviorGroup2.setBundleId(bundle.getId());
-
- BadRequestException e = assertThrows(BadRequestException.class, () -> {
- behaviorGroupRepository.create(behaviorGroup2.getAccountId(), behaviorGroup2.getOrgId(), behaviorGroup2);
- });
- assertEquals("A behavior group with display name [" + behaviorGroup1.getDisplayName() + "] already exists", e.getMessage());
- } finally {
- featureFlipper.setEnforceBehaviorGroupNameUnicity(false);
- }
+ when(backendConfig.isUniqueBgNameEnabled()).thenReturn(true);
+ Bundle bundle = resourceHelpers.createBundle();
+ BehaviorGroup behaviorGroup1 = resourceHelpers.createBehaviorGroup(DEFAULT_ACCOUNT_ID, DEFAULT_ORG_ID, "displayName", bundle.getId());
+
+ BehaviorGroup behaviorGroup2 = new BehaviorGroup();
+ behaviorGroup2.setAccountId(behaviorGroup1.getAccountId());
+ behaviorGroup2.setOrgId(behaviorGroup1.getOrgId());
+ behaviorGroup2.setDisplayName(behaviorGroup1.getDisplayName());
+ behaviorGroup2.setBundleId(bundle.getId());
+
+ BadRequestException e = assertThrows(BadRequestException.class, () -> {
+ behaviorGroupRepository.create(behaviorGroup2.getAccountId(), behaviorGroup2.getOrgId(), behaviorGroup2);
+ });
+ assertEquals("A behavior group with display name [" + behaviorGroup1.getDisplayName() + "] already exists", e.getMessage());
}
@Test
void shouldThrowExceptionWhenCreatingDefaultWithExistingDisplayName() {
- try {
- featureFlipper.setEnforceBehaviorGroupNameUnicity(true);
- Bundle bundle = resourceHelpers.createBundle();
- BehaviorGroup behaviorGroup1 = resourceHelpers.createDefaultBehaviorGroup("displayName", bundle.getId());
-
- BehaviorGroup behaviorGroup2 = new BehaviorGroup();
- behaviorGroup2.setDisplayName(behaviorGroup1.getDisplayName());
- behaviorGroup2.setBundleId(bundle.getId());
-
- BadRequestException e = assertThrows(BadRequestException.class, () -> {
- behaviorGroupRepository.createDefault(behaviorGroup2);
- });
- assertEquals("A behavior group with display name [" + behaviorGroup1.getDisplayName() + "] already exists", e.getMessage());
- } finally {
- featureFlipper.setEnforceBehaviorGroupNameUnicity(false);
- }
+ when(backendConfig.isUniqueBgNameEnabled()).thenReturn(true);
+ Bundle bundle = resourceHelpers.createBundle();
+ BehaviorGroup behaviorGroup1 = resourceHelpers.createDefaultBehaviorGroup("displayName", bundle.getId());
+
+ BehaviorGroup behaviorGroup2 = new BehaviorGroup();
+ behaviorGroup2.setDisplayName(behaviorGroup1.getDisplayName());
+ behaviorGroup2.setBundleId(bundle.getId());
+
+ BadRequestException e = assertThrows(BadRequestException.class, () -> {
+ behaviorGroupRepository.createDefault(behaviorGroup2);
+ });
+ assertEquals("A behavior group with display name [" + behaviorGroup1.getDisplayName() + "] already exists", e.getMessage());
}
@Test
@@ -119,38 +113,30 @@ void shouldNotThrowExceptionWhenCreatingWithExistingDisplayNameButDifferentOrgId
@Test
void shouldThrowExceptionWhenUpdatingToExistingDisplayNameAndSameOrgId() {
- try {
- featureFlipper.setEnforceBehaviorGroupNameUnicity(true);
- Bundle bundle = resourceHelpers.createBundle("name", "displayName");
- BehaviorGroup behaviorGroup1 = resourceHelpers.createBehaviorGroup(DEFAULT_ACCOUNT_ID, DEFAULT_ORG_ID, "displayName1", bundle.getId());
- BehaviorGroup behaviorGroup2 = resourceHelpers.createBehaviorGroup(behaviorGroup1.getAccountId(), behaviorGroup1.getOrgId(), "displayName2", bundle.getId());
- behaviorGroup2.setDisplayName(behaviorGroup1.getDisplayName());
-
- BadRequestException e = assertThrows(BadRequestException.class, () -> {
- behaviorGroupRepository.update(behaviorGroup2.getOrgId(), behaviorGroup2);
- });
- assertEquals("A behavior group with display name [" + behaviorGroup1.getDisplayName() + "] already exists", e.getMessage());
- } finally {
- featureFlipper.setEnforceBehaviorGroupNameUnicity(false);
- }
+ when(backendConfig.isUniqueBgNameEnabled()).thenReturn(true);
+ Bundle bundle = resourceHelpers.createBundle("name", "displayName");
+ BehaviorGroup behaviorGroup1 = resourceHelpers.createBehaviorGroup(DEFAULT_ACCOUNT_ID, DEFAULT_ORG_ID, "displayName1", bundle.getId());
+ BehaviorGroup behaviorGroup2 = resourceHelpers.createBehaviorGroup(behaviorGroup1.getAccountId(), behaviorGroup1.getOrgId(), "displayName2", bundle.getId());
+ behaviorGroup2.setDisplayName(behaviorGroup1.getDisplayName());
+
+ BadRequestException e = assertThrows(BadRequestException.class, () -> {
+ behaviorGroupRepository.update(behaviorGroup2.getOrgId(), behaviorGroup2);
+ });
+ assertEquals("A behavior group with display name [" + behaviorGroup1.getDisplayName() + "] already exists", e.getMessage());
}
@Test
void shouldThrowExceptionWhenUpdatingDefaultToExistingDisplayName() {
- try {
- featureFlipper.setEnforceBehaviorGroupNameUnicity(true);
- Bundle bundle = resourceHelpers.createBundle("name", "displayName");
- BehaviorGroup behaviorGroup1 = resourceHelpers.createDefaultBehaviorGroup("displayName1", bundle.getId());
- BehaviorGroup behaviorGroup2 = resourceHelpers.createDefaultBehaviorGroup("displayName2", bundle.getId());
- behaviorGroup2.setDisplayName(behaviorGroup1.getDisplayName());
-
- BadRequestException e = assertThrows(BadRequestException.class, () -> {
- behaviorGroupRepository.updateDefault(behaviorGroup2);
- });
- assertEquals("A behavior group with display name [" + behaviorGroup1.getDisplayName() + "] already exists", e.getMessage());
- } finally {
- featureFlipper.setEnforceBehaviorGroupNameUnicity(false);
- }
+ when(backendConfig.isUniqueBgNameEnabled()).thenReturn(true);
+ Bundle bundle = resourceHelpers.createBundle("name", "displayName");
+ BehaviorGroup behaviorGroup1 = resourceHelpers.createDefaultBehaviorGroup("displayName1", bundle.getId());
+ BehaviorGroup behaviorGroup2 = resourceHelpers.createDefaultBehaviorGroup("displayName2", bundle.getId());
+ behaviorGroup2.setDisplayName(behaviorGroup1.getDisplayName());
+
+ BadRequestException e = assertThrows(BadRequestException.class, () -> {
+ behaviorGroupRepository.updateDefault(behaviorGroup2);
+ });
+ assertEquals("A behavior group with display name [" + behaviorGroup1.getDisplayName() + "] already exists", e.getMessage());
}
@Test
diff --git a/backend/src/test/java/com/redhat/cloud/notifications/events/LifecycleITest.java b/backend/src/test/java/com/redhat/cloud/notifications/events/LifecycleITest.java
index 333da5bdf0..26c09425bf 100644
--- a/backend/src/test/java/com/redhat/cloud/notifications/events/LifecycleITest.java
+++ b/backend/src/test/java/com/redhat/cloud/notifications/events/LifecycleITest.java
@@ -4,7 +4,7 @@
import com.redhat.cloud.notifications.MockServerConfig;
import com.redhat.cloud.notifications.TestHelpers;
import com.redhat.cloud.notifications.TestLifecycleManager;
-import com.redhat.cloud.notifications.config.FeatureFlipper;
+import com.redhat.cloud.notifications.config.BackendConfig;
import com.redhat.cloud.notifications.db.DbIsolatedTest;
import com.redhat.cloud.notifications.models.Application;
import com.redhat.cloud.notifications.models.BehaviorGroup;
@@ -34,7 +34,6 @@
import jakarta.transaction.Transactional;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.rest.client.inject.RestClient;
-import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
@@ -66,6 +65,7 @@
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
+import static org.mockito.Mockito.when;
@QuarkusTest
@QuarkusTestResource(TestLifecycleManager.class)
@@ -90,8 +90,8 @@ public class LifecycleITest extends DbIsolatedTest {
@ConfigProperty(name = "internal.admin-role")
String adminRole;
- @Inject
- FeatureFlipper featureFlipper;
+ @InjectMock
+ BackendConfig backendConfig;
/**
* We mock the sources service's REST client because there are a few tests
@@ -104,12 +104,7 @@ public class LifecycleITest extends DbIsolatedTest {
@BeforeEach
void beforeEach() {
- featureFlipper.setInstantEmailsEnabled(true);
- }
-
- @AfterEach
- void afterEach() {
- featureFlipper.setInstantEmailsEnabled(false);
+ when(backendConfig.isInstantEmailsEnabled()).thenReturn(true);
}
private Header initRbacMock(String accountId, String orgId, String username, RbacAccess access) {
@@ -120,7 +115,7 @@ private Header initRbacMock(String accountId, String orgId, String username, Rba
@Test
void shouldReturn400AndBadRequestExceptionWhenDisplayNameIsAlreadyPresent() {
- if (!featureFlipper.isEnforceBehaviorGroupNameUnicity()) {
+ if (!backendConfig.isUniqueBgNameEnabled()) {
// The check is disabled from configuration.
return;
}
@@ -546,14 +541,13 @@ private String createWebhookEndpoint(Header identityHeader, String secretToken)
// The SecretUtils class follows the "basic authentication", "secret
// token" and "bearer token" order, so that is why we make the returns
// in that order for the mock.
- Mockito
- .when(this.sourcesServiceMock.create(Mockito.anyString(), Mockito.anyString(), Mockito.any()))
+ when(this.sourcesServiceMock.create(Mockito.anyString(), Mockito.anyString(), Mockito.any()))
.thenReturn(secretTokenSecret);
// Make sure that when the secrets are loaded when we fetch the
// endpoint again for checking the assertions, we simulate fetching
// the secrets from Sources too.
- Mockito.when(this.sourcesServiceMock.getById(Mockito.anyString(), Mockito.anyString(), Mockito.eq(secretTokenSecret.id))).thenReturn(secretTokenSecret);
+ when(this.sourcesServiceMock.getById(Mockito.anyString(), Mockito.anyString(), Mockito.eq(secretTokenSecret.id))).thenReturn(secretTokenSecret);
String responseBody = given()
.basePath(API_INTEGRATIONS_V_1_0)
diff --git a/backend/src/test/java/com/redhat/cloud/notifications/routers/EmailsOnlyModeTest.java b/backend/src/test/java/com/redhat/cloud/notifications/routers/EmailsOnlyModeTest.java
index 5c21310046..65109aa8d5 100644
--- a/backend/src/test/java/com/redhat/cloud/notifications/routers/EmailsOnlyModeTest.java
+++ b/backend/src/test/java/com/redhat/cloud/notifications/routers/EmailsOnlyModeTest.java
@@ -5,7 +5,7 @@
import com.redhat.cloud.notifications.MockServerConfig;
import com.redhat.cloud.notifications.TestHelpers;
import com.redhat.cloud.notifications.TestLifecycleManager;
-import com.redhat.cloud.notifications.config.FeatureFlipper;
+import com.redhat.cloud.notifications.config.BackendConfig;
import com.redhat.cloud.notifications.db.DbIsolatedTest;
import com.redhat.cloud.notifications.db.repositories.EndpointRepository;
import com.redhat.cloud.notifications.models.Endpoint;
@@ -13,7 +13,6 @@
import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.junit.QuarkusTest;
import io.restassured.http.Header;
-import jakarta.inject.Inject;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
@@ -34,8 +33,8 @@
@QuarkusTestResource(TestLifecycleManager.class)
public class EmailsOnlyModeTest extends DbIsolatedTest {
- @Inject
- FeatureFlipper featureFlipper;
+ @InjectMock
+ BackendConfig backendConfig;
@InjectMock
EndpointRepository endpointRepository;
@@ -43,149 +42,124 @@ public class EmailsOnlyModeTest extends DbIsolatedTest {
@ParameterizedTest
@ValueSource(strings = {Constants.API_INTEGRATIONS_V_1_0, Constants.API_INTEGRATIONS_V_2_0})
void testCreateUnsupportedEndpointType(String apiPath) {
- featureFlipper.setEmailsOnlyMode(true);
- try {
-
- String identityHeaderValue = TestHelpers.encodeRHIdentityInfo("account-id", "org-id", "username");
- Header identityHeader = TestHelpers.createRHIdentityHeader(identityHeaderValue);
- MockServerConfig.addMockRbacAccess(identityHeaderValue, FULL_ACCESS);
-
- Endpoint endpoint = new Endpoint();
- endpoint.setType(WEBHOOK);
- endpoint.setName("name");
- endpoint.setDescription("description");
-
- String responseBody = given()
- .basePath(apiPath)
- .header(identityHeader)
- .when()
- .contentType(JSON)
- .body(Json.encode(endpoint))
- .post("/endpoints")
- .then()
- .statusCode(400)
- .extract().asString();
- assertEquals(UNSUPPORTED_ENDPOINT_TYPE, responseBody);
-
- } finally {
- featureFlipper.setEmailsOnlyMode(false);
- }
+ when(backendConfig.isEmailsOnlyModeEnabled()).thenReturn(true);
+
+ String identityHeaderValue = TestHelpers.encodeRHIdentityInfo("account-id", "org-id", "username");
+ Header identityHeader = TestHelpers.createRHIdentityHeader(identityHeaderValue);
+ MockServerConfig.addMockRbacAccess(identityHeaderValue, FULL_ACCESS);
+
+ Endpoint endpoint = new Endpoint();
+ endpoint.setType(WEBHOOK);
+ endpoint.setName("name");
+ endpoint.setDescription("description");
+
+ String responseBody = given()
+ .basePath(apiPath)
+ .header(identityHeader)
+ .when()
+ .contentType(JSON)
+ .body(Json.encode(endpoint))
+ .post("/endpoints")
+ .then()
+ .statusCode(400)
+ .extract().asString();
+ assertEquals(UNSUPPORTED_ENDPOINT_TYPE, responseBody);
}
@ParameterizedTest
@ValueSource(strings = {Constants.API_INTEGRATIONS_V_1_0, Constants.API_INTEGRATIONS_V_2_0})
void testUpdateUnsupportedEndpointType(String apiPath) {
- featureFlipper.setEmailsOnlyMode(true);
- try {
-
- String identityHeaderValue = TestHelpers.encodeRHIdentityInfo("account-id", "org-id", "username");
- Header identityHeader = TestHelpers.createRHIdentityHeader(identityHeaderValue);
- MockServerConfig.addMockRbacAccess(identityHeaderValue, FULL_ACCESS);
-
- Endpoint endpoint = new Endpoint();
- endpoint.setType(WEBHOOK);
- endpoint.setName("name");
- endpoint.setDescription("description");
-
- String responseBody = given()
- .basePath(apiPath)
- .header(identityHeader)
- .pathParam("id", UUID.randomUUID().toString())
- .when()
- .contentType(JSON)
- .body(Json.encode(endpoint))
- .put("/endpoints/{id}")
- .then()
- .statusCode(400)
- .extract().asString();
- assertEquals(UNSUPPORTED_ENDPOINT_TYPE, responseBody);
-
- } finally {
- featureFlipper.setEmailsOnlyMode(false);
- }
+ when(backendConfig.isEmailsOnlyModeEnabled()).thenReturn(true);
+
+ String identityHeaderValue = TestHelpers.encodeRHIdentityInfo("account-id", "org-id", "username");
+ Header identityHeader = TestHelpers.createRHIdentityHeader(identityHeaderValue);
+ MockServerConfig.addMockRbacAccess(identityHeaderValue, FULL_ACCESS);
+
+ Endpoint endpoint = new Endpoint();
+ endpoint.setType(WEBHOOK);
+ endpoint.setName("name");
+ endpoint.setDescription("description");
+
+ String responseBody = given()
+ .basePath(apiPath)
+ .header(identityHeader)
+ .pathParam("id", UUID.randomUUID().toString())
+ .when()
+ .contentType(JSON)
+ .body(Json.encode(endpoint))
+ .put("/endpoints/{id}")
+ .then()
+ .statusCode(400)
+ .extract().asString();
+ assertEquals(UNSUPPORTED_ENDPOINT_TYPE, responseBody);
}
@ParameterizedTest
@ValueSource(strings = {Constants.API_INTEGRATIONS_V_1_0, Constants.API_INTEGRATIONS_V_2_0})
void testDeleteUnsupportedEndpointType(String apiPath) {
- featureFlipper.setEmailsOnlyMode(true);
- try {
-
- String identityHeaderValue = TestHelpers.encodeRHIdentityInfo("account-id", "org-id", "username");
- Header identityHeader = TestHelpers.createRHIdentityHeader(identityHeaderValue);
- MockServerConfig.addMockRbacAccess(identityHeaderValue, FULL_ACCESS);
-
- when(endpointRepository.getEndpointTypeById(anyString(), any(UUID.class))).thenReturn(CAMEL);
-
- String responseBody = given()
- .basePath(apiPath)
- .header(identityHeader)
- .pathParam("id", UUID.randomUUID().toString())
- .when()
- .delete("/endpoints/{id}")
- .then()
- .statusCode(400)
- .extract().asString();
- assertEquals(UNSUPPORTED_ENDPOINT_TYPE, responseBody);
-
- } finally {
- featureFlipper.setEmailsOnlyMode(false);
- }
+ when(backendConfig.isEmailsOnlyModeEnabled()).thenReturn(true);
+
+ String identityHeaderValue = TestHelpers.encodeRHIdentityInfo("account-id", "org-id", "username");
+ Header identityHeader = TestHelpers.createRHIdentityHeader(identityHeaderValue);
+ MockServerConfig.addMockRbacAccess(identityHeaderValue, FULL_ACCESS);
+
+ when(endpointRepository.getEndpointTypeById(anyString(), any(UUID.class))).thenReturn(CAMEL);
+
+ String responseBody = given()
+ .basePath(apiPath)
+ .header(identityHeader)
+ .pathParam("id", UUID.randomUUID().toString())
+ .when()
+ .delete("/endpoints/{id}")
+ .then()
+ .statusCode(400)
+ .extract().asString();
+ assertEquals(UNSUPPORTED_ENDPOINT_TYPE, responseBody);
}
@ParameterizedTest
@ValueSource(strings = {Constants.API_INTEGRATIONS_V_1_0, Constants.API_INTEGRATIONS_V_2_0})
void testEnableUnsupportedEndpointType(String apiPath) {
- featureFlipper.setEmailsOnlyMode(true);
- try {
-
- String identityHeaderValue = TestHelpers.encodeRHIdentityInfo("account-id", "org-id", "username");
- Header identityHeader = TestHelpers.createRHIdentityHeader(identityHeaderValue);
- MockServerConfig.addMockRbacAccess(identityHeaderValue, FULL_ACCESS);
-
- when(endpointRepository.getEndpointTypeById(anyString(), any(UUID.class))).thenReturn(CAMEL);
-
- String responseBody = given()
- .basePath(apiPath)
- .header(identityHeader)
- .pathParam("id", UUID.randomUUID().toString())
- .when()
- .put("/endpoints/{id}/enable")
- .then()
- .statusCode(400)
- .extract().asString();
- assertEquals(UNSUPPORTED_ENDPOINT_TYPE, responseBody);
-
- } finally {
- featureFlipper.setEmailsOnlyMode(false);
- }
+ when(backendConfig.isEmailsOnlyModeEnabled()).thenReturn(true);
+
+ String identityHeaderValue = TestHelpers.encodeRHIdentityInfo("account-id", "org-id", "username");
+ Header identityHeader = TestHelpers.createRHIdentityHeader(identityHeaderValue);
+ MockServerConfig.addMockRbacAccess(identityHeaderValue, FULL_ACCESS);
+
+ when(endpointRepository.getEndpointTypeById(anyString(), any(UUID.class))).thenReturn(CAMEL);
+
+ String responseBody = given()
+ .basePath(apiPath)
+ .header(identityHeader)
+ .pathParam("id", UUID.randomUUID().toString())
+ .when()
+ .put("/endpoints/{id}/enable")
+ .then()
+ .statusCode(400)
+ .extract().asString();
+ assertEquals(UNSUPPORTED_ENDPOINT_TYPE, responseBody);
}
@ParameterizedTest
@ValueSource(strings = {Constants.API_INTEGRATIONS_V_1_0, Constants.API_INTEGRATIONS_V_2_0})
void testDisableUnsupportedEndpointType(String apiPath) {
- featureFlipper.setEmailsOnlyMode(true);
- try {
-
- String identityHeaderValue = TestHelpers.encodeRHIdentityInfo("account-id", "org-id", "username");
- Header identityHeader = TestHelpers.createRHIdentityHeader(identityHeaderValue);
- MockServerConfig.addMockRbacAccess(identityHeaderValue, FULL_ACCESS);
-
- when(endpointRepository.getEndpointTypeById(anyString(), any(UUID.class))).thenReturn(CAMEL);
-
- String responseBody = given()
- .basePath(apiPath)
- .header(identityHeader)
- .pathParam("id", UUID.randomUUID().toString())
- .when()
- .delete("/endpoints/{id}/enable")
- .then()
- .statusCode(400)
- .extract().asString();
- assertEquals(UNSUPPORTED_ENDPOINT_TYPE, responseBody);
-
- } finally {
- featureFlipper.setEmailsOnlyMode(false);
- }
+ when(backendConfig.isEmailsOnlyModeEnabled()).thenReturn(true);
+
+ String identityHeaderValue = TestHelpers.encodeRHIdentityInfo("account-id", "org-id", "username");
+ Header identityHeader = TestHelpers.createRHIdentityHeader(identityHeaderValue);
+ MockServerConfig.addMockRbacAccess(identityHeaderValue, FULL_ACCESS);
+
+ when(endpointRepository.getEndpointTypeById(anyString(), any(UUID.class))).thenReturn(CAMEL);
+
+ String responseBody = given()
+ .basePath(apiPath)
+ .header(identityHeader)
+ .pathParam("id", UUID.randomUUID().toString())
+ .when()
+ .delete("/endpoints/{id}/enable")
+ .then()
+ .statusCode(400)
+ .extract().asString();
+ assertEquals(UNSUPPORTED_ENDPOINT_TYPE, responseBody);
}
}
diff --git a/backend/src/test/java/com/redhat/cloud/notifications/routers/EndpointResourceTest.java b/backend/src/test/java/com/redhat/cloud/notifications/routers/EndpointResourceTest.java
index f85a946e5e..709ca9fa6a 100644
--- a/backend/src/test/java/com/redhat/cloud/notifications/routers/EndpointResourceTest.java
+++ b/backend/src/test/java/com/redhat/cloud/notifications/routers/EndpointResourceTest.java
@@ -5,7 +5,7 @@
import com.redhat.cloud.notifications.TestConstants;
import com.redhat.cloud.notifications.TestHelpers;
import com.redhat.cloud.notifications.TestLifecycleManager;
-import com.redhat.cloud.notifications.config.FeatureFlipper;
+import com.redhat.cloud.notifications.config.BackendConfig;
import com.redhat.cloud.notifications.db.DbIsolatedTest;
import com.redhat.cloud.notifications.db.ResourceHelpers;
import com.redhat.cloud.notifications.db.repositories.EndpointRepository;
@@ -41,7 +41,6 @@
import jakarta.persistence.EntityManager;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.rest.client.inject.RestClient;
-import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -99,12 +98,7 @@ public class EndpointResourceTest extends DbIsolatedTest {
@BeforeEach
void beforeEach() {
RestAssured.basePath = TestConstants.API_INTEGRATIONS_V_1_0;
- featureFlipper.setInstantEmailsEnabled(true);
- }
-
- @AfterEach
- void afterEach() {
- featureFlipper.setInstantEmailsEnabled(false);
+ when(backendConfig.isInstantEmailsEnabled()).thenReturn(true);
}
@Inject
@@ -113,8 +107,8 @@ void afterEach() {
@Inject
ResourceHelpers resourceHelpers;
- @Inject
- FeatureFlipper featureFlipper;
+ @InjectMock
+ BackendConfig backendConfig;
@Inject
EndpointRepository endpointRepository;
@@ -296,123 +290,119 @@ void testEndpointAdding() {
@Test
void testRepeatedEndpointName() {
- try {
- featureFlipper.setEnforceIntegrationNameUnicity(true);
- String orgId = "repeatEndpoint";
- String userName = "user";
+ when(backendConfig.isUniqueIntegrationNameEnabled()).thenReturn(true);
+ String orgId = "repeatEndpoint";
+ String userName = "user";
- String identityHeaderValue = TestHelpers.encodeRHIdentityInfo(orgId, orgId, userName);
- Header identityHeader = TestHelpers.createRHIdentityHeader(identityHeaderValue);
+ String identityHeaderValue = TestHelpers.encodeRHIdentityInfo(orgId, orgId, userName);
+ Header identityHeader = TestHelpers.createRHIdentityHeader(identityHeaderValue);
- MockServerConfig.addMockRbacAccess(identityHeaderValue, FULL_ACCESS);
+ MockServerConfig.addMockRbacAccess(identityHeaderValue, FULL_ACCESS);
- // Endpoint1
- WebhookProperties properties = new WebhookProperties();
- properties.setMethod(POST);
- properties.setDisableSslVerification(false);
- properties.setSecretToken("my-super-secret-token");
- properties.setUrl(getMockServerUrl());
+ // Endpoint1
+ WebhookProperties properties = new WebhookProperties();
+ properties.setMethod(POST);
+ properties.setDisableSslVerification(false);
+ properties.setSecretToken("my-super-secret-token");
+ properties.setUrl(getMockServerUrl());
- Endpoint endpoint1 = new Endpoint();
- endpoint1.setType(EndpointType.WEBHOOK);
- endpoint1.setName("Endpoint1");
- endpoint1.setDescription("needle in the haystack");
- endpoint1.setEnabled(true);
- endpoint1.setProperties(properties);
- endpoint1.setServerErrors(3);
+ Endpoint endpoint1 = new Endpoint();
+ endpoint1.setType(EndpointType.WEBHOOK);
+ endpoint1.setName("Endpoint1");
+ endpoint1.setDescription("needle in the haystack");
+ endpoint1.setEnabled(true);
+ endpoint1.setProperties(properties);
+ endpoint1.setServerErrors(3);
- mockSources(properties);
+ mockSources(properties);
- Response response = given()
- .header(identityHeader)
- .when()
- .contentType(JSON)
- .body(Json.encode(endpoint1))
- .post("/endpoints")
- .then()
- .statusCode(200)
- .extract().response();
+ Response response = given()
+ .header(identityHeader)
+ .when()
+ .contentType(JSON)
+ .body(Json.encode(endpoint1))
+ .post("/endpoints")
+ .then()
+ .statusCode(200)
+ .extract().response();
- String endpoint1Id = new JsonObject(response.getBody().asString()).getString("id");
- assertNotNull(endpoint1Id);
+ String endpoint1Id = new JsonObject(response.getBody().asString()).getString("id");
+ assertNotNull(endpoint1Id);
- // Trying to add the same endpoint name again results in a 400 error
- given()
- .header(identityHeader)
- .when()
- .contentType(JSON)
- .body(Json.encode(endpoint1))
- .post("/endpoints")
- .then()
- .statusCode(400);
+ // Trying to add the same endpoint name again results in a 400 error
+ given()
+ .header(identityHeader)
+ .when()
+ .contentType(JSON)
+ .body(Json.encode(endpoint1))
+ .post("/endpoints")
+ .then()
+ .statusCode(400);
- // Endpoint2
- Endpoint ep = new Endpoint();
- ep.setType(EndpointType.WEBHOOK);
- ep.setName("Endpoint2");
- ep.setDescription("needle in the haystack");
- ep.setEnabled(true);
- ep.setProperties(properties);
- ep.setServerErrors(3);
+ // Endpoint2
+ Endpoint ep = new Endpoint();
+ ep.setType(EndpointType.WEBHOOK);
+ ep.setName("Endpoint2");
+ ep.setDescription("needle in the haystack");
+ ep.setEnabled(true);
+ ep.setProperties(properties);
+ ep.setServerErrors(3);
- given()
- .header(identityHeader)
- .when()
- .contentType(JSON)
- .body(Json.encode(ep))
- .post("/endpoints")
- .then()
- .statusCode(200);
-
- // Different endpoint type with same name
- CamelProperties camelProperties = new CamelProperties();
- camelProperties.setBasicAuthentication(new BasicAuthentication());
- camelProperties.setExtras(Map.of());
- camelProperties.setSecretToken("secret");
- camelProperties.setUrl("http://nowhere");
-
- ep = new Endpoint();
- ep.setType(EndpointType.CAMEL);
- ep.setSubType("stuff");
- ep.setName("Endpoint1");
- ep.setDescription("needle in the haystack");
- ep.setEnabled(true);
- ep.setProperties(camelProperties);
- ep.setServerErrors(3);
+ given()
+ .header(identityHeader)
+ .when()
+ .contentType(JSON)
+ .body(Json.encode(ep))
+ .post("/endpoints")
+ .then()
+ .statusCode(200);
- given()
- .header(identityHeader)
- .when()
- .contentType(JSON)
- .body(Json.encode(ep))
- .post("/endpoints")
- .then()
- .statusCode(400);
+ // Different endpoint type with same name
+ CamelProperties camelProperties = new CamelProperties();
+ camelProperties.setBasicAuthentication(new BasicAuthentication());
+ camelProperties.setExtras(Map.of());
+ camelProperties.setSecretToken("secret");
+ camelProperties.setUrl("http://nowhere");
- // Updating endpoint1 name is possible
- endpoint1.setName("Endpoint1-updated");
- given()
- .header(identityHeader)
- .contentType(JSON)
- .body(Json.encode(endpoint1))
- .when()
- .put("/endpoints/" + endpoint1Id)
- .then()
- .statusCode(200);
+ ep = new Endpoint();
+ ep.setType(EndpointType.CAMEL);
+ ep.setSubType("stuff");
+ ep.setName("Endpoint1");
+ ep.setDescription("needle in the haystack");
+ ep.setEnabled(true);
+ ep.setProperties(camelProperties);
+ ep.setServerErrors(3);
- // Updating to the name of an already existing endpoint is not possible
- endpoint1.setName("Endpoint2");
- given()
- .header(identityHeader)
- .contentType(JSON)
- .body(Json.encode(endpoint1))
- .when()
- .put("/endpoints/" + endpoint1Id)
- .then()
- .statusCode(400);
- } finally {
- featureFlipper.setEnforceIntegrationNameUnicity(false);
- }
+ given()
+ .header(identityHeader)
+ .when()
+ .contentType(JSON)
+ .body(Json.encode(ep))
+ .post("/endpoints")
+ .then()
+ .statusCode(400);
+
+ // Updating endpoint1 name is possible
+ endpoint1.setName("Endpoint1-updated");
+ given()
+ .header(identityHeader)
+ .contentType(JSON)
+ .body(Json.encode(endpoint1))
+ .when()
+ .put("/endpoints/" + endpoint1Id)
+ .then()
+ .statusCode(200);
+
+ // Updating to the name of an already existing endpoint is not possible
+ endpoint1.setName("Endpoint2");
+ given()
+ .header(identityHeader)
+ .contentType(JSON)
+ .body(Json.encode(endpoint1))
+ .when()
+ .put("/endpoints/" + endpoint1Id)
+ .then()
+ .statusCode(400);
}
private JsonObject fetchSingle(String id, Header identityHeader) {
@@ -689,77 +679,73 @@ void testMissingSlackChannel() {
@Test
void testForbidSlackChannelUsage() {
- try {
- featureFlipper.setSlackForbidChannelUsageEnabled(true);
- String identityHeaderValue = TestHelpers.encodeRHIdentityInfo("account-id", "org-id", "user");
- Header identityHeader = TestHelpers.createRHIdentityHeader(identityHeaderValue);
- MockServerConfig.addMockRbacAccess(identityHeaderValue, FULL_ACCESS);
+ when(backendConfig.isForbidSlackChannelUsage()).thenReturn(true);
- Map extras = new HashMap<>(Map.of("channel", "")); // Having a channel value is invalid.
- CamelProperties camelProperties = new CamelProperties();
- camelProperties.setUrl("https://foo.com");
- camelProperties.setExtras(extras);
+ String identityHeaderValue = TestHelpers.encodeRHIdentityInfo("account-id", "org-id", "user");
+ Header identityHeader = TestHelpers.createRHIdentityHeader(identityHeaderValue);
+ MockServerConfig.addMockRbacAccess(identityHeaderValue, FULL_ACCESS);
- Endpoint endpoint = new Endpoint();
- endpoint.setType(EndpointType.CAMEL);
- endpoint.setSubType("slack");
- endpoint.setName("name");
- endpoint.setDescription("description");
- endpoint.setProperties(camelProperties);
+ Map extras = new HashMap<>(Map.of("channel", "")); // Having a channel value is invalid.
+ CamelProperties camelProperties = new CamelProperties();
+ camelProperties.setUrl("https://foo.com");
+ camelProperties.setExtras(extras);
- String responseBody = given()
- .header(identityHeader)
- .when()
- .contentType(JSON)
- .body(Json.encode(endpoint))
- .post("/endpoints")
- .then()
- .statusCode(400)
- .extract().asString();
+ Endpoint endpoint = new Endpoint();
+ endpoint.setType(EndpointType.CAMEL);
+ endpoint.setSubType("slack");
+ endpoint.setName("name");
+ endpoint.setDescription("description");
+ endpoint.setProperties(camelProperties);
- assertEquals(DEPRECATED_SLACK_CHANNEL_ERROR, responseBody);
+ String responseBody = given()
+ .header(identityHeader)
+ .when()
+ .contentType(JSON)
+ .body(Json.encode(endpoint))
+ .post("/endpoints")
+ .then()
+ .statusCode(400)
+ .extract().asString();
- extras.remove("channel");
+ assertEquals(DEPRECATED_SLACK_CHANNEL_ERROR, responseBody);
- String createdEndpoint = given()
- .header(identityHeader)
- .when()
- .contentType(JSON)
- .body(Json.encode(endpoint))
- .post("/endpoints")
- .then()
- .statusCode(200)
- .extract().asString();
+ extras.remove("channel");
- final JsonObject jsonResponse = new JsonObject(createdEndpoint);
- final String endpointUuidRaw = jsonResponse.getString("id");
+ String createdEndpoint = given()
+ .header(identityHeader)
+ .when()
+ .contentType(JSON)
+ .body(Json.encode(endpoint))
+ .post("/endpoints")
+ .then()
+ .statusCode(200)
+ .extract().asString();
- // try to update endpoint without channel
- given()
- .header(identityHeader)
- .contentType(JSON)
- .pathParam("id", endpointUuidRaw)
- .body(Json.encode(endpoint))
- .when()
- .put("/endpoints/{id}")
- .then()
- .statusCode(200);
+ final JsonObject jsonResponse = new JsonObject(createdEndpoint);
+ final String endpointUuidRaw = jsonResponse.getString("id");
- // try to update endpoint with channel
- extras.put("channel", "refused");
- given()
- .header(identityHeader)
- .contentType(JSON)
- .pathParam("id", endpointUuidRaw)
- .body(Json.encode(endpoint))
- .when()
- .put("/endpoints/{id}")
- .then()
- .statusCode(400);
+ // try to update endpoint without channel
+ given()
+ .header(identityHeader)
+ .contentType(JSON)
+ .pathParam("id", endpointUuidRaw)
+ .body(Json.encode(endpoint))
+ .when()
+ .put("/endpoints/{id}")
+ .then()
+ .statusCode(200);
- } finally {
- featureFlipper.setSlackForbidChannelUsageEnabled(false);
- }
+ // try to update endpoint with channel
+ extras.put("channel", "refused");
+ given()
+ .header(identityHeader)
+ .contentType(JSON)
+ .pathParam("id", endpointUuidRaw)
+ .body(Json.encode(endpoint))
+ .when()
+ .put("/endpoints/{id}")
+ .then()
+ .statusCode(400);
}
@Test
diff --git a/backend/src/test/java/com/redhat/cloud/notifications/routers/NotificationResourceTest.java b/backend/src/test/java/com/redhat/cloud/notifications/routers/NotificationResourceTest.java
index 231f0e98ea..d4f35fdd36 100644
--- a/backend/src/test/java/com/redhat/cloud/notifications/routers/NotificationResourceTest.java
+++ b/backend/src/test/java/com/redhat/cloud/notifications/routers/NotificationResourceTest.java
@@ -6,7 +6,7 @@
import com.redhat.cloud.notifications.TestConstants;
import com.redhat.cloud.notifications.TestHelpers;
import com.redhat.cloud.notifications.TestLifecycleManager;
-import com.redhat.cloud.notifications.config.FeatureFlipper;
+import com.redhat.cloud.notifications.config.BackendConfig;
import com.redhat.cloud.notifications.db.DbIsolatedTest;
import com.redhat.cloud.notifications.db.ResourceHelpers;
import com.redhat.cloud.notifications.db.repositories.ApplicationRepository;
@@ -21,6 +21,7 @@
import com.redhat.cloud.notifications.routers.models.behaviorgroup.CreateBehaviorGroupRequest;
import com.redhat.cloud.notifications.routers.models.behaviorgroup.CreateBehaviorGroupResponse;
import com.redhat.cloud.notifications.routers.models.behaviorgroup.UpdateBehaviorGroupRequest;
+import io.quarkus.test.InjectMock;
import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.junit.QuarkusTest;
import io.restassured.RestAssured;
@@ -31,7 +32,6 @@
import io.vertx.core.json.JsonObject;
import jakarta.inject.Inject;
import jakarta.validation.constraints.Size;
-import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -61,6 +61,7 @@
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.when;
@QuarkusTest
@QuarkusTestResource(TestLifecycleManager.class)
@@ -86,19 +87,14 @@ public class NotificationResourceTest extends DbIsolatedTest {
@Inject
BehaviorGroupRepository behaviorGroupRepository;
- @Inject
- FeatureFlipper featureFlipper;
+ @InjectMock
+ BackendConfig backendConfig;
@BeforeEach
void beforeEach() {
RestAssured.basePath = TestConstants.API_NOTIFICATIONS_V_1_0;
MockServerConfig.clearRbac();
- featureFlipper.setEnforceBehaviorGroupNameUnicity(true);
- }
-
- @AfterEach
- void afterEach() {
- featureFlipper.setEnforceBehaviorGroupNameUnicity(false);
+ when(backendConfig.isUniqueBgNameEnabled()).thenReturn(true);
}
private Header initRbacMock(String accountId, String orgId, String username, RbacAccess access) {
@@ -934,52 +930,47 @@ void testBehaviorGroupsAffectedByRemovalOfUnknownEndpointId() {
@Test
void testBehaviorGroupSameName() {
- try {
- final String BEHAVIOR_GROUP_1_NAME = "BehaviorGroup1";
- final String BEHAVIOR_GROUP_2_NAME = "BehaviorGroup2";
+ final String BEHAVIOR_GROUP_1_NAME = "BehaviorGroup1";
+ final String BEHAVIOR_GROUP_2_NAME = "BehaviorGroup2";
- featureFlipper.setEnforceBehaviorGroupNameUnicity(true);
- Header identityHeader = initRbacMock("tenant", "sameBehaviorGroupName", "user", FULL_ACCESS);
+ when(backendConfig.isUniqueBgNameEnabled()).thenReturn(true);
+ Header identityHeader = initRbacMock("tenant", "sameBehaviorGroupName", "user", FULL_ACCESS);
- Bundle bundle1 = helpers.createBundle(TEST_BUNDLE_NAME, "Bundle1");
- Bundle bundle2 = helpers.createBundle(TEST_BUNDLE_2_NAME, "Bundle2");
+ Bundle bundle1 = helpers.createBundle(TEST_BUNDLE_NAME, "Bundle1");
+ Bundle bundle2 = helpers.createBundle(TEST_BUNDLE_2_NAME, "Bundle2");
- CreateBehaviorGroupRequest createBehaviorGroupRequest = new CreateBehaviorGroupRequest();
- createBehaviorGroupRequest.displayName = BEHAVIOR_GROUP_1_NAME;
- createBehaviorGroupRequest.bundleId = bundle1.getId();
+ CreateBehaviorGroupRequest createBehaviorGroupRequest = new CreateBehaviorGroupRequest();
+ createBehaviorGroupRequest.displayName = BEHAVIOR_GROUP_1_NAME;
+ createBehaviorGroupRequest.bundleId = bundle1.getId();
- UUID behaviorGroup1Id = createBehaviorGroup(identityHeader, createBehaviorGroupRequest, 200).get().id;
- assertNotNull(behaviorGroup1Id);
+ UUID behaviorGroup1Id = createBehaviorGroup(identityHeader, createBehaviorGroupRequest, 200).get().id;
+ assertNotNull(behaviorGroup1Id);
- // same display name in same bundle is not possible
- createBehaviorGroup(identityHeader, createBehaviorGroupRequest, 400);
+ // same display name in same bundle is not possible
+ createBehaviorGroup(identityHeader, createBehaviorGroupRequest, 400);
- // same display name in a different bundle is OK
- createBehaviorGroupRequest.bundleId = bundle2.getId();
- createBehaviorGroup(identityHeader, createBehaviorGroupRequest, 200);
+ // same display name in a different bundle is OK
+ createBehaviorGroupRequest.bundleId = bundle2.getId();
+ createBehaviorGroup(identityHeader, createBehaviorGroupRequest, 200);
- // Different display name in bundle1
- createBehaviorGroupRequest.bundleId = bundle1.getId();
- createBehaviorGroupRequest.displayName = BEHAVIOR_GROUP_2_NAME;
- createBehaviorGroup(identityHeader, createBehaviorGroupRequest, 200);
+ // Different display name in bundle1
+ createBehaviorGroupRequest.bundleId = bundle1.getId();
+ createBehaviorGroupRequest.displayName = BEHAVIOR_GROUP_2_NAME;
+ createBehaviorGroup(identityHeader, createBehaviorGroupRequest, 200);
- // Cannot update Behavior Group 1 name to "BehaviorGroup2" as it already exists
- UpdateBehaviorGroupRequest updateBehaviorGroupRequest = new UpdateBehaviorGroupRequest();
- updateBehaviorGroupRequest.displayName = BEHAVIOR_GROUP_2_NAME;
- updateBehaviorGroup(identityHeader, behaviorGroup1Id, updateBehaviorGroupRequest, 400);
+ // Cannot update Behavior Group 1 name to "BehaviorGroup2" as it already exists
+ UpdateBehaviorGroupRequest updateBehaviorGroupRequest = new UpdateBehaviorGroupRequest();
+ updateBehaviorGroupRequest.displayName = BEHAVIOR_GROUP_2_NAME;
+ updateBehaviorGroup(identityHeader, behaviorGroup1Id, updateBehaviorGroupRequest, 400);
- // Can update other properties without changing the name
- updateBehaviorGroupRequest.displayName = BEHAVIOR_GROUP_1_NAME;
- updateBehaviorGroupRequest.eventTypeIds = Set.of();
- updateBehaviorGroup(identityHeader, behaviorGroup1Id, updateBehaviorGroupRequest, 200);
+ // Can update other properties without changing the name
+ updateBehaviorGroupRequest.displayName = BEHAVIOR_GROUP_1_NAME;
+ updateBehaviorGroupRequest.eventTypeIds = Set.of();
+ updateBehaviorGroup(identityHeader, behaviorGroup1Id, updateBehaviorGroupRequest, 200);
- // Can use other name
- updateBehaviorGroupRequest.displayName = "OtherName";
- updateBehaviorGroup(identityHeader, behaviorGroup1Id, updateBehaviorGroupRequest, 200);
-
- } finally {
- featureFlipper.setEnforceBehaviorGroupNameUnicity(false);
- }
+ // Can use other name
+ updateBehaviorGroupRequest.displayName = "OtherName";
+ updateBehaviorGroup(identityHeader, behaviorGroup1Id, updateBehaviorGroupRequest, 200);
}
/**
diff --git a/backend/src/test/java/com/redhat/cloud/notifications/routers/UserConfigResourceTest.java b/backend/src/test/java/com/redhat/cloud/notifications/routers/UserConfigResourceTest.java
index 5b7dec84b6..0b380d8e59 100644
--- a/backend/src/test/java/com/redhat/cloud/notifications/routers/UserConfigResourceTest.java
+++ b/backend/src/test/java/com/redhat/cloud/notifications/routers/UserConfigResourceTest.java
@@ -6,7 +6,7 @@
import com.redhat.cloud.notifications.TestConstants;
import com.redhat.cloud.notifications.TestHelpers;
import com.redhat.cloud.notifications.TestLifecycleManager;
-import com.redhat.cloud.notifications.config.FeatureFlipper;
+import com.redhat.cloud.notifications.config.BackendConfig;
import com.redhat.cloud.notifications.db.DbIsolatedTest;
import com.redhat.cloud.notifications.db.repositories.ApplicationRepository;
import com.redhat.cloud.notifications.db.repositories.EventTypeRepository;
@@ -20,6 +20,7 @@
import com.redhat.cloud.notifications.routers.models.SettingsValueByEventTypeJsonForm;
import com.redhat.cloud.notifications.routers.models.SettingsValuesByEventType;
import com.redhat.cloud.notifications.routers.models.UserConfigPreferences;
+import io.quarkus.test.InjectMock;
import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.junit.mockito.InjectSpy;
@@ -32,7 +33,6 @@
import jakarta.transaction.Transactional;
import org.apache.commons.lang3.RandomStringUtils;
import org.eclipse.microprofile.config.inject.ConfigProperty;
-import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -70,16 +70,11 @@ public class UserConfigResourceTest extends DbIsolatedTest {
@BeforeEach
void beforeEach() {
RestAssured.basePath = TestConstants.API_NOTIFICATIONS_V_1_0;
- featureFlipper.setInstantEmailsEnabled(true);
+ when(backendConfig.isInstantEmailsEnabled()).thenReturn(true);
}
- @AfterEach
- void afterEach() {
- featureFlipper.setInstantEmailsEnabled(false);
- }
-
- @Inject
- FeatureFlipper featureFlipper;
+ @InjectMock
+ BackendConfig backendConfig;
@Inject
SubscriptionRepository subscriptionRepository;
@@ -128,7 +123,7 @@ private SettingsValuesByEventType createSettingsValue(String bundle, String appl
SettingsValuesByEventType.EventTypeSettingsValue eventTypeSettingsValue = new SettingsValuesByEventType.EventTypeSettingsValue();
eventTypeSettingsValue.emailSubscriptionTypes.put(DAILY, daily);
eventTypeSettingsValue.emailSubscriptionTypes.put(INSTANT, instant);
- if (featureFlipper.isDrawerEnabled()) {
+ if (backendConfig.isDrawerEnabled()) {
eventTypeSettingsValue.emailSubscriptionTypes.put(DRAWER, drawer);
}
@@ -151,12 +146,8 @@ void testLegacySettingsByEventType() {
@Test
void testSettingsByEventTypeWithDrawerEnabled() {
- try {
- featureFlipper.setDrawerEnabled(true);
- testSettingsByEventType();
- } finally {
- featureFlipper.setDrawerEnabled(false);
- }
+ when(backendConfig.isDrawerEnabled()).thenReturn(true);
+ testSettingsByEventType();
}
void testSettingsByEventType() {
@@ -216,21 +207,21 @@ void testSettingsByEventType() {
rhelPolicy = rhelPolicyForm(settingsValuesByEventType);
assertNotNull(rhelPolicy.eventTypes.get(0).fields.get(0).infoMessage);
- featureFlipper.setInstantEmailsEnabled(false);
+ when(backendConfig.isInstantEmailsEnabled()).thenReturn(false);
SettingsValuesByEventType settingsValues = createSettingsValue(bundle, application, eventType, true, true, false);
postPreferencesByEventType(path, identityHeader, settingsValues, 400);
- featureFlipper.setInstantEmailsEnabled(true);
+ when(backendConfig.isInstantEmailsEnabled()).thenReturn(true);
postPreferencesByEventType(path, identityHeader, settingsValues, 200);
- featureFlipper.setInstantEmailsEnabled(false);
+ when(backendConfig.isInstantEmailsEnabled()).thenReturn(false);
SettingsValueByEventTypeJsonForm settingsValue = getPreferencesByEventType(path, identityHeader);
rhelPolicy = rhelPolicyForm(settingsValue);
boolean instantEmailSettingsReturned = extractNotificationValues(rhelPolicy.eventTypes, bundle, application, eventType)
.keySet().stream().anyMatch(INSTANT::equals);
assertFalse(instantEmailSettingsReturned, "Instant email subscription settings should not be returned when instant emails are disabled");
- featureFlipper.setInstantEmailsEnabled(true);
+ when(backendConfig.isInstantEmailsEnabled()).thenReturn(true);
// Daily and Instant to false
updateAndCheckUserPreference(path, identityHeader, bundle, application, eventType, List.of(DRAWER), List.of(DRAWER));
@@ -253,7 +244,7 @@ void testSettingsByEventType() {
// Unsubscribing from everything should work this time.
updateAndCheckUserPreference(path, identityHeader, bundle, application, eventType, emptyList(), emptyList());
- if (featureFlipper.isDrawerEnabled()) {
+ if (backendConfig.isDrawerEnabled()) {
// Daily, Instant and drawer to false
updateAndCheckUserPreference(path, identityHeader, bundle, application, eventType, emptyList(), emptyList());
@@ -325,7 +316,7 @@ void testSettingsByEventType() {
Map notificationPreferenes = extractNotificationValues(rhelPolicy.eventTypes, bundle, application, eventType);
- if (featureFlipper.isDrawerEnabled()) {
+ if (backendConfig.isDrawerEnabled()) {
assertEquals(2, notificationPreferenes.size());
assertTrue(notificationPreferenes.containsKey(DRAWER));
} else {
@@ -343,7 +334,7 @@ void testSettingsByEventType() {
.contentType(JSON)
.extract().body().as(SettingsValueByEventTypeJsonForm.class);
rhelPolicy = rhelPolicyForm(settingsValueJsonForm);
- if (featureFlipper.isDrawerEnabled()) {
+ if (backendConfig.isDrawerEnabled()) {
// drawer type will be always supported
assertNotNull(rhelPolicy);
assertEquals(1, settingsValueJsonForm.bundles.size());
@@ -365,7 +356,7 @@ private void updateAndCheckUserPreference(String path, Header identityHeader, St
assertEquals(expectedResult.contains(DAILY), initialValues.get(DAILY));
assertEquals(expectedResult.contains(INSTANT), initialValues.get(INSTANT));
- if (featureFlipper.isDrawerEnabled()) {
+ if (backendConfig.isDrawerEnabled()) {
assertEquals(expectedResult.contains(DRAWER), initialValues.get(DRAWER));
}
@@ -381,7 +372,7 @@ private void updateAndCheckUserPreference(String path, Header identityHeader, St
Map notificationPreferenes = extractNotificationValues(preferences.eventTypes, bundle, application, eventType);
assertEquals(expectedResult.contains(DAILY), notificationPreferenes.get(DAILY));
assertEquals(expectedResult.contains(INSTANT), notificationPreferenes.get(INSTANT));
- if (featureFlipper.isDrawerEnabled()) {
+ if (backendConfig.isDrawerEnabled()) {
assertEquals(expectedResult.contains(DRAWER), notificationPreferenes.get(DRAWER));
}
}
diff --git a/common/pom.xml b/common/pom.xml
index aa71fb5b43..738bceb402 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -44,6 +44,13 @@
quarkus-smallrye-fault-tolerance
+
+
+ io.quarkiverse.unleash
+ quarkus-unleash
+ ${quarkus-unleash.version}
+
+
com.redhat.cloud.common
diff --git a/common/src/main/java/com/redhat/cloud/notifications/config/FeatureFlipper.java b/common/src/main/java/com/redhat/cloud/notifications/config/FeatureFlipper.java
deleted file mode 100644
index 0a4e623319..0000000000
--- a/common/src/main/java/com/redhat/cloud/notifications/config/FeatureFlipper.java
+++ /dev/null
@@ -1,241 +0,0 @@
-package com.redhat.cloud.notifications.config;
-
-import io.quarkus.logging.Log;
-import io.quarkus.runtime.StartupEvent;
-import io.quarkus.runtime.configuration.ProfileManager;
-import jakarta.enterprise.context.ApplicationScoped;
-import jakarta.enterprise.event.Observes;
-import org.eclipse.microprofile.config.inject.ConfigProperty;
-
-import static io.quarkus.runtime.LaunchMode.TEST;
-
-/**
- *
- * This class centralizes all configuration values used to enable or disable a feature.
- *
- *
- * Any config value used to flip a temporary or permanent feature can be added that way:
- *
- * @ApplicationScoped
- * public class FeatureFlipper {
- *
- * @ConfigProperty(name = "amazing-feature.enabled", defaultValue = "false")
- * boolean amazingFeatureEnabled;
- *
- * public boolean isAmazingFeatureEnabled() {
- * return amazingFeatureEnabled;
- * }
- *
- * public void setAmazingFeatureEnabled(boolean amazingFeatureEnabled) {
- * // Add this if the config value should only be overridden in TEST launch mode.
- * checkTestLaunchMode();
- * this.amazingFeatureEnabled = amazingFeatureEnabled;
- * }
- * }
- *
- *
- */
-@ApplicationScoped
-public class FeatureFlipper {
-
- @ConfigProperty(name = "notifications.enforce-bg-name-unicity", defaultValue = "false")
- boolean enforceBehaviorGroupNameUnicity;
-
- @ConfigProperty(name = "notifications.enforce-integration-name-unicity", defaultValue = "false")
- boolean enforceIntegrationNameUnicity;
-
- @ConfigProperty(name = "notifications.kafka-consumed-total-checker.enabled", defaultValue = "false")
- boolean kafkaConsumedTotalCheckerEnabled;
-
- @ConfigProperty(name = "notifications.use-default-template", defaultValue = "false")
- boolean useDefaultTemplate;
-
- @ConfigProperty(name = "notifications.use-rbac-for-fetching-users", defaultValue = "false")
- boolean useRbacForFetchingUsers;
-
- @ConfigProperty(name = "notifications.emails-only-mode.enabled", defaultValue = "false")
- boolean emailsOnlyMode;
-
- @ConfigProperty(name = "notifications.use-secured-email-templates.enabled", defaultValue = "false")
- boolean useSecuredEmailTemplates;
-
- @ConfigProperty(name = "notifications.instant-emails.enabled", defaultValue = "false")
- boolean instantEmailsEnabled;
-
- @ConfigProperty(name = "mp.messaging.incoming.exportrequests.enabled", defaultValue = "false")
- boolean exportServiceIntegrationEnabled;
-
- @ConfigProperty(name = "notifications.drawer.enabled", defaultValue = "false")
- boolean drawerEnabled;
-
- @ConfigProperty(name = "notifications.use-mbop-for-fetching-users", defaultValue = "false")
- boolean useMBOPForFetchingUsers;
-
- @ConfigProperty(name = "notifications.async-aggregation.enabled", defaultValue = "true")
- boolean asyncAggregation;
-
- @ConfigProperty(name = "processor.email.aggregation.use-recipients-resolver-clowdapp.enabled", defaultValue = "false")
- boolean useRecipientsResolverClowdappForDailyDigestEnabled;
-
- @ConfigProperty(name = "notifications.email.hcc-sender-name.enabled", defaultValue = "false")
- boolean hccEmailSenderNameEnabled;
-
- @ConfigProperty(name = "notifications.slack.forbid.channel.usage.enabled", defaultValue = "false")
- boolean slackForbidChannelUsageEnabled;
-
- void logFeaturesStatusAtStartup(@Observes StartupEvent event) {
- Log.infof("=== %s startup status ===", FeatureFlipper.class.getSimpleName());
- Log.infof("The behavior groups unique name constraint is %s", enforceBehaviorGroupNameUnicity ? "enabled" : "disabled");
- Log.infof("The integrations unique name constraint is %s", enforceIntegrationNameUnicity ? "enabled" : "disabled");
- Log.infof("The Kafka outage detector is %s", kafkaConsumedTotalCheckerEnabled ? "enabled" : "disabled");
- Log.infof("The use of default templates is %s", useDefaultTemplate ? "enabled" : "disabled");
- Log.infof("The use of rbac for fetching users is %s", useRbacForFetchingUsers ? "enabled" : "disabled");
- Log.infof("Emails only mode is %s", emailsOnlyMode ? "enabled" : "disabled");
- Log.infof("The use of secured email templates is %s", useSecuredEmailTemplates ? "enabled" : "disabled");
- Log.infof("Instant emails are %s", instantEmailsEnabled ? "enabled" : "disabled");
- Log.infof("The integration with the export service is %s", exportServiceIntegrationEnabled ? "enabled" : "disabled");
- Log.infof("Drawer feature is %s", drawerEnabled ? "enabled" : "disabled");
- Log.infof("The use of BOP/MBOP for fetching users is %s", useMBOPForFetchingUsers ? "enabled" : "disabled");
- Log.infof("The async aggregation is %s", asyncAggregation ? "enabled" : "disabled");
- Log.infof("The Recipients resolver usage for daily digest is %s", useRecipientsResolverClowdappForDailyDigestEnabled ? "enabled" : "disabled");
- Log.infof("HCC sender name is %s in emails", hccEmailSenderNameEnabled ? "enabled" : "disabled");
- Log.infof("The disable of channel field on Slack is %s", slackForbidChannelUsageEnabled ? "enabled" : "disabled");
- }
-
- public boolean isEnforceBehaviorGroupNameUnicity() {
- return enforceBehaviorGroupNameUnicity;
- }
-
- public void setEnforceBehaviorGroupNameUnicity(boolean enforceBehaviorGroupNameUnicity) {
- checkTestLaunchMode();
- this.enforceBehaviorGroupNameUnicity = enforceBehaviorGroupNameUnicity;
- }
-
- public boolean isEnforceIntegrationNameUnicity() {
- return enforceIntegrationNameUnicity;
- }
-
- public void setEnforceIntegrationNameUnicity(boolean enforceIntegrationNameUnicity) {
- checkTestLaunchMode();
- this.enforceIntegrationNameUnicity = enforceIntegrationNameUnicity;
- }
-
- public boolean isKafkaConsumedTotalCheckerEnabled() {
- return kafkaConsumedTotalCheckerEnabled;
- }
-
- public void setKafkaConsumedTotalCheckerEnabled(boolean kafkaConsumedTotalCheckerEnabled) {
- // It's ok to override this config value at runtime.
- this.kafkaConsumedTotalCheckerEnabled = kafkaConsumedTotalCheckerEnabled;
- }
-
- public boolean isUseDefaultTemplate() {
- return useDefaultTemplate;
- }
-
- public void setUseDefaultTemplate(boolean useDefaultTemplate) {
- checkTestLaunchMode();
- this.useDefaultTemplate = useDefaultTemplate;
- }
-
- public boolean isUseRbacForFetchingUsers() {
- return this.useRbacForFetchingUsers;
- }
-
- public void setUseRbacForFetchingUsers(boolean useRbacForFetchingUsers) {
- checkTestLaunchMode();
- this.useRbacForFetchingUsers = useRbacForFetchingUsers;
- }
-
- public boolean isEmailsOnlyMode() {
- return emailsOnlyMode;
- }
-
- public void setEmailsOnlyMode(boolean emailsOnlyMode) {
- checkTestLaunchMode();
- this.emailsOnlyMode = emailsOnlyMode;
- }
-
- public boolean isUseSecuredEmailTemplates() {
- return useSecuredEmailTemplates;
- }
-
- public void setUseSecuredEmailTemplates(boolean useSecuredEmailTemplates) {
- checkTestLaunchMode();
- this.useSecuredEmailTemplates = useSecuredEmailTemplates;
- }
-
- public boolean isInstantEmailsEnabled() {
- return instantEmailsEnabled;
- }
-
- public void setInstantEmailsEnabled(boolean instantEmailsEnabled) {
- checkTestLaunchMode();
- this.instantEmailsEnabled = instantEmailsEnabled;
- }
-
- public boolean isDrawerEnabled() {
- return drawerEnabled;
- }
-
- public void setDrawerEnabled(boolean drawerEnabled) {
- checkTestLaunchMode();
- this.drawerEnabled = drawerEnabled;
- }
-
- public boolean isUseMBOPForFetchingUsers() {
- return this.useMBOPForFetchingUsers;
- }
-
- public void setUseMBOPForFetchingUsers(final boolean useMBOPForFetchingUsers) {
- checkTestLaunchMode();
- this.useMBOPForFetchingUsers = useMBOPForFetchingUsers;
- }
-
- public boolean isAsyncAggregation() {
- return asyncAggregation;
- }
-
- public void setAsyncAggregation(boolean asyncAggregation) {
- checkTestLaunchMode();
- this.asyncAggregation = asyncAggregation;
- }
-
- public boolean isUseRecipientsResolverClowdappForDailyDigestEnabled() {
- return useRecipientsResolverClowdappForDailyDigestEnabled;
- }
-
- public void setUseRecipientsResolverClowdappForDailyDigestEnabled(boolean useRecipientsResolverClowdappForDailyDigestEnabled) {
- checkTestLaunchMode();
- this.useRecipientsResolverClowdappForDailyDigestEnabled = useRecipientsResolverClowdappForDailyDigestEnabled;
- }
-
- public boolean isHccEmailSenderNameEnabled() {
- return hccEmailSenderNameEnabled;
- }
-
- public void setHccEmailSenderNameEnabled(boolean hccEmailSenderNameEnabled) {
- checkTestLaunchMode();
- this.hccEmailSenderNameEnabled = hccEmailSenderNameEnabled;
- }
-
- public boolean isSlackForbidChannelUsageEnabled() {
- return slackForbidChannelUsageEnabled;
- }
-
- public void setSlackForbidChannelUsageEnabled(boolean slackForbidChannelUsageEnabled) {
- checkTestLaunchMode();
- this.slackForbidChannelUsageEnabled = slackForbidChannelUsageEnabled;
- }
-
- /**
- * This method throws an {@link IllegalStateException} if it is invoked with a launch mode different from
- * {@link io.quarkus.runtime.LaunchMode#TEST TEST}. It should be added to methods that allow overriding a
- * config value from tests only, preventing doing so from runtime code.
- */
- public static void checkTestLaunchMode() {
- if (ProfileManager.getLaunchMode() != TEST) {
- throw new IllegalStateException("Illegal config value override detected");
- }
- }
-}
diff --git a/connector-common-authentication/src/test/resources/application.properties b/connector-common-authentication/src/test/resources/application.properties
index b5f59b9355..d4b114467a 100644
--- a/connector-common-authentication/src/test/resources/application.properties
+++ b/connector-common-authentication/src/test/resources/application.properties
@@ -23,3 +23,7 @@ quarkus.rest-client.sources.url=http://localhost:8000
mp.messaging.tocamel.topic=platform.notifications.tocamel
mp.messaging.fromcamel.topic=platform.notifications.fromcamel
+
+quarkus.unleash.active=false
+quarkus.unleash.synchronous-fetch-on-initialisation=true
+quarkus.unleash.url=http://localhost:4242
diff --git a/connector-common-http/src/main/java/com/redhat/cloud/notifications/connector/http/HttpConnectorConfig.java b/connector-common-http/src/main/java/com/redhat/cloud/notifications/connector/http/HttpConnectorConfig.java
index 675f70bb88..ec7c56ab10 100644
--- a/connector-common-http/src/main/java/com/redhat/cloud/notifications/connector/http/HttpConnectorConfig.java
+++ b/connector-common-http/src/main/java/com/redhat/cloud/notifications/connector/http/HttpConnectorConfig.java
@@ -12,6 +12,9 @@
@ApplicationScoped
public class HttpConnectorConfig extends ConnectorConfig {
+ /*
+ * Env vars configuration
+ */
private static final String CLIENT_ERROR_LOG_LEVEL = "notifications.connector.http.client-error.log-level";
private static final String COMPONENTS = "notifications.connector.http.components";
private static final String CONNECT_TIMEOUT_MS = "notifications.connector.http.connect-timeout-ms";
@@ -22,6 +25,11 @@ public class HttpConnectorConfig extends ConnectorConfig {
private static final String SERVER_ERROR_LOG_LEVEL = "notifications.connector.http.server-error.log-level";
private static final String SOCKET_TIMEOUT_MS = "notifications.connector.http.socket-timeout-ms";
+ /*
+ * Unleash configuration
+ */
+ private final String disableFaultyEndpointsToggleName = toggleName("disable-faulty-endpoints");
+
@ConfigProperty(name = CLIENT_ERROR_LOG_LEVEL, defaultValue = "DEBUG")
Level clientErrorLogLevel;
@@ -35,6 +43,7 @@ public class HttpConnectorConfig extends ConnectorConfig {
int httpConnectionsPerRoute;
@ConfigProperty(name = DISABLE_FAULTY_ENDPOINTS, defaultValue = "true")
+ @Deprecated(forRemoval = true, since = "To be removed when we're done migrating to Unleash in all environments")
boolean disableFaultyEndpoints;
@ConfigProperty(name = FOLLOW_REDIRECTS, defaultValue = "false")
@@ -56,7 +65,7 @@ protected Map getLoggedConfiguration() {
config.put(COMPONENTS, httpComponents);
config.put(CONNECT_TIMEOUT_MS, httpConnectTimeout);
config.put(CONNECTIONS_PER_ROUTE, httpConnectionsPerRoute);
- config.put(DISABLE_FAULTY_ENDPOINTS, disableFaultyEndpoints);
+ config.put(disableFaultyEndpointsToggleName, isDisableFaultyEndpoints());
config.put(FOLLOW_REDIRECTS, followRedirects);
config.put(MAX_TOTAL_CONNECTIONS, httpMaxTotalConnections);
config.put(SERVER_ERROR_LOG_LEVEL, serverErrorLogLevel);
@@ -81,7 +90,11 @@ public int getHttpConnectionsPerRoute() {
}
public boolean isDisableFaultyEndpoints() {
- return disableFaultyEndpoints;
+ if (unleashEnabled) {
+ return unleash.isEnabled(disableFaultyEndpointsToggleName, true);
+ } else {
+ return disableFaultyEndpoints;
+ }
}
public boolean isFollowRedirects() {
diff --git a/connector-common-http/src/test/resources/application.properties b/connector-common-http/src/test/resources/application.properties
index 54a5c64b38..e6a2eb06ed 100644
--- a/connector-common-http/src/test/resources/application.properties
+++ b/connector-common-http/src/test/resources/application.properties
@@ -21,3 +21,7 @@ camel.context.name=notifications-connector-common-http
mp.messaging.tocamel.topic=platform.notifications.tocamel
mp.messaging.fromcamel.topic=platform.notifications.fromcamel
+
+quarkus.unleash.active=false
+quarkus.unleash.synchronous-fetch-on-initialisation=true
+quarkus.unleash.url=http://localhost:4242
diff --git a/connector-common/pom.xml b/connector-common/pom.xml
index 7b6917ceae..50323e82a9 100644
--- a/connector-common/pom.xml
+++ b/connector-common/pom.xml
@@ -93,6 +93,11 @@
quarkus-logging-sentry
${quarkus-logging-sentry.version}
+
+ io.quarkiverse.unleash
+ quarkus-unleash
+ ${quarkus-unleash.version}
+
diff --git a/connector-common/src/main/java/com/redhat/cloud/notifications/connector/ConnectorConfig.java b/connector-common/src/main/java/com/redhat/cloud/notifications/connector/ConnectorConfig.java
index 1a5014b993..828133ff49 100644
--- a/connector-common/src/main/java/com/redhat/cloud/notifications/connector/ConnectorConfig.java
+++ b/connector-common/src/main/java/com/redhat/cloud/notifications/connector/ConnectorConfig.java
@@ -1,16 +1,16 @@
package com.redhat.cloud.notifications.connector;
+import io.getunleash.Unleash;
import io.quarkus.arc.DefaultBean;
import io.quarkus.logging.Log;
import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
-import static java.util.Map.Entry;
-
@ApplicationScoped
@DefaultBean
public class ConnectorConfig {
@@ -30,6 +30,11 @@ public class ConnectorConfig {
private static final String SEDA_CONCURRENT_CONSUMERS = "notifications.connector.seda.concurrent-consumers";
private static final String SEDA_QUEUE_SIZE = "notifications.connector.seda.queue-size";
private static final String SUPPORTED_CONNECTOR_HEADERS = "notifications.connector.supported-connector-headers";
+ private static final String UNLEASH = "notifications.unleash.enabled";
+
+ @ConfigProperty(name = UNLEASH, defaultValue = "false")
+ @Deprecated(forRemoval = true, since = "To be removed when we're done migrating to Unleash in all environments")
+ protected boolean unleashEnabled;
@ConfigProperty(name = ENDPOINT_CACHE_MAX_SIZE, defaultValue = "100")
int endpointCacheMaxSize;
@@ -80,11 +85,18 @@ public class ConnectorConfig {
@ConfigProperty(name = SUPPORTED_CONNECTOR_HEADERS)
List supportedConnectorHeaders;
+ @Inject
+ protected Unleash unleash;
+
public void log() {
- Log.info("=== Connector configuration ===");
- for (Entry configEntry : getLoggedConfiguration().entrySet()) {
- Log.infof("%s=%s", configEntry.getKey(), configEntry.getValue());
- }
+ Log.info("=== Startup configuration ===");
+ getLoggedConfiguration().forEach((key, value) -> {
+ Log.infof("%s=%s", key, value);
+ });
+ }
+
+ protected String toggleName(String feature) {
+ return String.format("notifications-connector-%s.%s.enabled", getConnectorName(), feature);
}
protected Map getLoggedConfiguration() {
@@ -104,6 +116,7 @@ protected Map getLoggedConfiguration() {
config.put(SEDA_CONCURRENT_CONSUMERS, sedaConcurrentConsumers);
config.put(SEDA_QUEUE_SIZE, sedaQueueSize);
config.put(SUPPORTED_CONNECTOR_HEADERS, supportedConnectorHeaders);
+ config.put(UNLEASH, unleashEnabled);
return config;
}
diff --git a/connector-common/src/test/resources/application.properties b/connector-common/src/test/resources/application.properties
index 0b67634e68..908eb08314 100644
--- a/connector-common/src/test/resources/application.properties
+++ b/connector-common/src/test/resources/application.properties
@@ -21,3 +21,7 @@ camel.context.name=notifications-connector-common
mp.messaging.tocamel.topic=platform.notifications.tocamel
mp.messaging.fromcamel.topic=platform.notifications.fromcamel
+
+quarkus.unleash.active=false
+quarkus.unleash.synchronous-fetch-on-initialisation=true
+quarkus.unleash.url=http://localhost:4242
diff --git a/connector-drawer/src/main/resources/application.properties b/connector-drawer/src/main/resources/application.properties
index a616bcffe6..40ff612812 100644
--- a/connector-drawer/src/main/resources/application.properties
+++ b/connector-drawer/src/main/resources/application.properties
@@ -45,3 +45,7 @@ mp.messaging.todrawer.topic=platform.chrome
# The URL for the recipients-resolver.
notifications.connector.recipients-resolver.url=${clowder.endpoints.notifications-recipients-resolver-service.url:http://localhost:9008}
+
+quarkus.unleash.active=false
+quarkus.unleash.synchronous-fetch-on-initialisation=true
+quarkus.unleash.url=http://localhost:4242
diff --git a/connector-email/src/main/resources/application.properties b/connector-email/src/main/resources/application.properties
index 52266943b5..c64d56003e 100644
--- a/connector-email/src/main/resources/application.properties
+++ b/connector-email/src/main/resources/application.properties
@@ -48,3 +48,7 @@ notifications.connector.user-provider.bop.url=https://backoffice-proxy.apps.ext.
notifications.connector.recipients-resolver.url=${clowder.endpoints.notifications-recipients-resolver-service.url:http://localhost:9008}
%test.notifications.connector.max-recipients-per-email=4
+
+quarkus.unleash.active=false
+quarkus.unleash.synchronous-fetch-on-initialisation=true
+quarkus.unleash.url=http://localhost:4242
diff --git a/connector-google-chat/src/main/resources/application.properties b/connector-google-chat/src/main/resources/application.properties
index b8726c1614..88473cd431 100644
--- a/connector-google-chat/src/main/resources/application.properties
+++ b/connector-google-chat/src/main/resources/application.properties
@@ -33,3 +33,7 @@ camel.context.name=notifications-connector-google-chat
mp.messaging.tocamel.topic=platform.notifications.tocamel
mp.messaging.fromcamel.topic=platform.notifications.fromcamel
+
+quarkus.unleash.active=false
+quarkus.unleash.synchronous-fetch-on-initialisation=true
+quarkus.unleash.url=http://localhost:4242
diff --git a/connector-microsoft-teams/src/main/resources/application.properties b/connector-microsoft-teams/src/main/resources/application.properties
index ff5bbdc8bf..5304b54e85 100644
--- a/connector-microsoft-teams/src/main/resources/application.properties
+++ b/connector-microsoft-teams/src/main/resources/application.properties
@@ -33,3 +33,7 @@ camel.context.name=notifications-connector-microsoft-teams
mp.messaging.tocamel.topic=platform.notifications.tocamel
mp.messaging.fromcamel.topic=platform.notifications.fromcamel
+
+quarkus.unleash.active=false
+quarkus.unleash.synchronous-fetch-on-initialisation=true
+quarkus.unleash.url=http://localhost:4242
diff --git a/connector-servicenow/src/main/resources/application.properties b/connector-servicenow/src/main/resources/application.properties
index 1f5ee589ee..79b476f8f0 100644
--- a/connector-servicenow/src/main/resources/application.properties
+++ b/connector-servicenow/src/main/resources/application.properties
@@ -42,3 +42,7 @@ quarkus.rest-client.sources.url=${clowder.endpoints.sources-api-svc.url:http://l
quarkus.rest-client.sources.trust-store=${clowder.endpoints.sources-api-svc.trust-store-path}
quarkus.rest-client.sources.trust-store-password=${clowder.endpoints.sources-api-svc.trust-store-password}
quarkus.rest-client.sources.trust-store-type=${clowder.endpoints.sources-api-svc.trust-store-type}
+
+quarkus.unleash.active=false
+quarkus.unleash.synchronous-fetch-on-initialisation=true
+quarkus.unleash.url=http://localhost:4242
diff --git a/connector-slack/src/main/resources/application.properties b/connector-slack/src/main/resources/application.properties
index 07b85164ee..cfd59232c8 100644
--- a/connector-slack/src/main/resources/application.properties
+++ b/connector-slack/src/main/resources/application.properties
@@ -35,3 +35,7 @@ mp.messaging.tocamel.topic=platform.notifications.tocamel
mp.messaging.fromcamel.topic=platform.notifications.fromcamel
%test.quarkus.log.category."com.redhat.cloud.notifications".level=DEBUG
+
+quarkus.unleash.active=false
+quarkus.unleash.synchronous-fetch-on-initialisation=true
+quarkus.unleash.url=http://localhost:4242
diff --git a/connector-splunk/src/main/resources/application.properties b/connector-splunk/src/main/resources/application.properties
index 9cf85c57a2..97a0a38c17 100644
--- a/connector-splunk/src/main/resources/application.properties
+++ b/connector-splunk/src/main/resources/application.properties
@@ -42,3 +42,7 @@ quarkus.rest-client.sources.url=${clowder.endpoints.sources-api-svc.url:http://l
quarkus.rest-client.sources.trust-store=${clowder.endpoints.sources-api-svc.trust-store-path}
quarkus.rest-client.sources.trust-store-password=${clowder.endpoints.sources-api-svc.trust-store-password}
quarkus.rest-client.sources.trust-store-type=${clowder.endpoints.sources-api-svc.trust-store-type}
+
+quarkus.unleash.active=false
+quarkus.unleash.synchronous-fetch-on-initialisation=true
+quarkus.unleash.url=http://localhost:4242
diff --git a/connector-webhook/src/main/resources/application.properties b/connector-webhook/src/main/resources/application.properties
index b28278cfc1..227b7815a9 100644
--- a/connector-webhook/src/main/resources/application.properties
+++ b/connector-webhook/src/main/resources/application.properties
@@ -42,3 +42,7 @@ quarkus.rest-client.sources.url=${clowder.endpoints.sources-api-svc.url:http://l
quarkus.rest-client.sources.trust-store=${clowder.endpoints.sources-api-svc.trust-store-path}
quarkus.rest-client.sources.trust-store-password=${clowder.endpoints.sources-api-svc.trust-store-password}
quarkus.rest-client.sources.trust-store-type=${clowder.endpoints.sources-api-svc.trust-store-type}
+
+quarkus.unleash.active=false
+quarkus.unleash.synchronous-fetch-on-initialisation=true
+quarkus.unleash.url=http://localhost:4242
diff --git a/engine/pom.xml b/engine/pom.xml
index 9bad9bc7e6..42107302ce 100644
--- a/engine/pom.xml
+++ b/engine/pom.xml
@@ -126,13 +126,6 @@
${quarkus-logging-cloudwatch.version}
-
-
- io.quarkiverse.unleash
- quarkus-unleash
- 1.4.0
-
-
dev.failsafe
diff --git a/engine/src/main/java/com/redhat/cloud/notifications/EngineConfig.java b/engine/src/main/java/com/redhat/cloud/notifications/EngineConfig.java
deleted file mode 100644
index 37f7499fc1..0000000000
--- a/engine/src/main/java/com/redhat/cloud/notifications/EngineConfig.java
+++ /dev/null
@@ -1,125 +0,0 @@
-package com.redhat.cloud.notifications;
-
-import com.redhat.cloud.notifications.config.FeatureFlipper;
-import io.getunleash.Unleash;
-import io.getunleash.UnleashContext;
-import io.quarkus.logging.Log;
-import jakarta.enterprise.context.ApplicationScoped;
-import jakarta.enterprise.event.Observes;
-import jakarta.enterprise.event.Startup;
-import jakarta.inject.Inject;
-import org.eclipse.microprofile.config.inject.ConfigProperty;
-
-import java.util.Map;
-import java.util.function.Supplier;
-
-@ApplicationScoped
-public class EngineConfig {
-
- // TODO Remove this when we're fully migrated to Unleash in all environments.
- @ConfigProperty(name = "notifications.unleash.enabled", defaultValue = "false")
- boolean unleashEnabled;
-
- @Inject
- Unleash unleash;
-
- // TODO Remove this when we're fully migrated to Unleash in all environments.
- @Inject
- FeatureFlipper featureFlipper;
-
- private static String toggleName(String feature) {
- return String.format("notifications-engine.%s.enabled", feature);
- }
-
- // Unleash toggles names.
- private static final String AGGREGATION_WITH_RECIPIENTS_RESOLVER_ENABLED = toggleName("aggregation-with-recipients-resolver");
- private static final String ASYNC_AGGREGATION_ENABLED = toggleName("async-aggregation");
- private static final String DEFAULT_TEMPLATE_ENABLED = toggleName("default-template");
- private static final String DRAWER_ENABLED = toggleName("drawer");
- private static final String EMAILS_ONLY_MODE_ENABLED = toggleName("emails-only-mode");
- private static final String HCC_EMAIL_SENDER_NAME_ENABLED = toggleName("hcc-email-sender-name");
- private static final String KAFKA_CONSUMED_TOTAL_CHECKER_ENABLED = toggleName("kafka-consumed-total-checker");
- private static final String SECURED_EMAIL_TEMPLATES_ENABLED = toggleName("secured-email-templates");
-
- private final Map> loggedToggles = Map.of(
- AGGREGATION_WITH_RECIPIENTS_RESOLVER_ENABLED, this::isAggregationWithRecipientsResolverEnabled,
- ASYNC_AGGREGATION_ENABLED, this::isAsyncAggregationEnabled,
- DEFAULT_TEMPLATE_ENABLED, this::isDefaultTemplateEnabled,
- DRAWER_ENABLED, this::isDrawerEnabled,
- EMAILS_ONLY_MODE_ENABLED, this::isEmailsOnlyModeEnabled,
- KAFKA_CONSUMED_TOTAL_CHECKER_ENABLED, this::isKafkaConsumedTotalCheckerEnabled,
- SECURED_EMAIL_TEMPLATES_ENABLED, this::isSecuredEmailTemplatesEnabled
- );
-
- void logFeatureTogglesAtStartup(@Observes Startup event) {
- Log.info("=== Feature toggles startup status ===");
- loggedToggles.forEach((name, value) -> {
- Log.infof("%s=%s", name, value.get());
- });
- }
-
- public boolean isAggregationWithRecipientsResolverEnabled() {
- if (unleashEnabled) {
- return unleash.isEnabled(AGGREGATION_WITH_RECIPIENTS_RESOLVER_ENABLED, false);
- } else {
- return featureFlipper.isUseRecipientsResolverClowdappForDailyDigestEnabled();
- }
- }
-
- public boolean isAsyncAggregationEnabled() {
- if (unleashEnabled) {
- return unleash.isEnabled(ASYNC_AGGREGATION_ENABLED, false);
- } else {
- return featureFlipper.isAsyncAggregation();
- }
- }
-
- public boolean isDefaultTemplateEnabled() {
- if (unleashEnabled) {
- return unleash.isEnabled(DEFAULT_TEMPLATE_ENABLED, false);
- } else {
- return featureFlipper.isUseDefaultTemplate();
- }
- }
-
- public boolean isDrawerEnabled() {
- if (unleashEnabled) {
- return unleash.isEnabled(DRAWER_ENABLED, false);
- } else {
- return featureFlipper.isDrawerEnabled();
- }
- }
-
- public boolean isEmailsOnlyModeEnabled() {
- if (unleashEnabled) {
- return unleash.isEnabled(EMAILS_ONLY_MODE_ENABLED, true);
- } else {
- return featureFlipper.isEmailsOnlyMode();
- }
- }
-
- public boolean isHccEmailSenderNameEnabled(String orgId) {
- if (unleashEnabled) {
- UnleashContext unleashContext = UnleashContext.builder().addProperty("orgId", orgId).build();
- return unleash.isEnabled(HCC_EMAIL_SENDER_NAME_ENABLED, unleashContext, false);
- } else {
- return featureFlipper.isHccEmailSenderNameEnabled();
- }
- }
-
- public boolean isKafkaConsumedTotalCheckerEnabled() {
- if (unleashEnabled) {
- return unleash.isEnabled(KAFKA_CONSUMED_TOTAL_CHECKER_ENABLED, false);
- } else {
- return featureFlipper.isKafkaConsumedTotalCheckerEnabled();
- }
- }
-
- public boolean isSecuredEmailTemplatesEnabled() {
- if (unleashEnabled) {
- return unleash.isEnabled(SECURED_EMAIL_TEMPLATES_ENABLED, true);
- } else {
- return featureFlipper.isUseSecuredEmailTemplates();
- }
- }
-}
diff --git a/engine/src/main/java/com/redhat/cloud/notifications/config/EngineConfig.java b/engine/src/main/java/com/redhat/cloud/notifications/config/EngineConfig.java
new file mode 100644
index 0000000000..a218f09435
--- /dev/null
+++ b/engine/src/main/java/com/redhat/cloud/notifications/config/EngineConfig.java
@@ -0,0 +1,166 @@
+package com.redhat.cloud.notifications.config;
+
+import io.getunleash.Unleash;
+import io.getunleash.UnleashContext;
+import io.quarkus.logging.Log;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.event.Observes;
+import jakarta.enterprise.event.Startup;
+import jakarta.inject.Inject;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+@ApplicationScoped
+public class EngineConfig {
+
+ /*
+ * Env vars configuration
+ */
+ private static final String DEFAULT_TEMPLATE = "notifications.use-default-template";
+ private static final String EMAILS_ONLY_MODE = "notifications.emails-only-mode.enabled";
+ private static final String SECURED_EMAIL_TEMPLATES = "notifications.use-secured-email-templates.enabled";
+ private static final String UNLEASH = "notifications.unleash.enabled";
+
+ /*
+ * Unleash configuration
+ */
+ private static final String AGGREGATION_WITH_RECIPIENTS_RESOLVER = toggleName("aggregation-with-recipients-resolver");
+ private static final String ASYNC_AGGREGATION = toggleName("async-aggregation");
+ private static final String DRAWER = toggleName("drawer");
+ private static final String HCC_EMAIL_SENDER_NAME = toggleName("hcc-email-sender-name");
+ private static final String KAFKA_CONSUMED_TOTAL_CHECKER = toggleName("kafka-consumed-total-checker");
+
+ private static String toggleName(String feature) {
+ return String.format("notifications-engine.%s.enabled", feature);
+ }
+
+ @ConfigProperty(name = UNLEASH, defaultValue = "false")
+ @Deprecated(forRemoval = true, since = "To be removed when we're done migrating to Unleash in all environments")
+ boolean unleashEnabled;
+
+ @ConfigProperty(name = "notifications.drawer.enabled", defaultValue = "false")
+ @Deprecated(forRemoval = true, since = "To be removed when we're done migrating to Unleash in all environments")
+ boolean drawerEnabled;
+
+ @ConfigProperty(name = "notifications.async-aggregation.enabled", defaultValue = "true")
+ @Deprecated(forRemoval = true, since = "To be removed when we're done migrating to Unleash in all environments")
+ boolean asyncAggregation;
+
+ @ConfigProperty(name = "processor.email.aggregation.use-recipients-resolver-clowdapp.enabled", defaultValue = "false")
+ @Deprecated(forRemoval = true, since = "To be removed when we're done migrating to Unleash in all environments")
+ boolean useRecipientsResolverClowdappForDailyDigestEnabled;
+
+ @ConfigProperty(name = "notifications.kafka-consumed-total-checker.enabled", defaultValue = "false")
+ @Deprecated(forRemoval = true, since = "To be removed when we're done migrating to Unleash in all environments")
+ boolean kafkaConsumedTotalCheckerEnabled;
+
+ @ConfigProperty(name = "notifications.email.hcc-sender-name.enabled", defaultValue = "false")
+ @Deprecated(forRemoval = true, since = "To be removed when we're done migrating to Unleash in all environments")
+ boolean hccEmailSenderNameEnabled;
+
+ @ConfigProperty(name = "notifications.use-rbac-for-fetching-users", defaultValue = "false")
+ @Deprecated(forRemoval = true, since = "To be removed when we're done migrating to Unleash in all environments")
+ boolean useRbacForFetchingUsers;
+
+ @ConfigProperty(name = "notifications.use-mbop-for-fetching-users", defaultValue = "false")
+ @Deprecated(forRemoval = true, since = "To be removed when we're done migrating to Unleash in all environments")
+ boolean useMBOPForFetchingUsers;
+
+ // Only used in stage environments.
+ @ConfigProperty(name = DEFAULT_TEMPLATE, defaultValue = "false")
+ boolean defaultTemplateEnabled;
+
+ // Only used in special environments.
+ @ConfigProperty(name = EMAILS_ONLY_MODE, defaultValue = "false")
+ boolean emailsOnlyModeEnabled;
+
+ // Only used in special environments.
+ @ConfigProperty(name = SECURED_EMAIL_TEMPLATES, defaultValue = "false")
+ boolean useSecuredEmailTemplates;
+
+ @Inject
+ Unleash unleash;
+
+ void logConfigAtStartup(@Observes Startup event) {
+
+ Map config = new TreeMap<>();
+ config.put(AGGREGATION_WITH_RECIPIENTS_RESOLVER, isAggregationWithRecipientsResolverEnabled());
+ config.put(ASYNC_AGGREGATION, isAsyncAggregationEnabled());
+ config.put(DEFAULT_TEMPLATE, isDefaultTemplateEnabled());
+ config.put(DRAWER, isDrawerEnabled());
+ config.put(EMAILS_ONLY_MODE, isEmailsOnlyModeEnabled());
+ config.put(KAFKA_CONSUMED_TOTAL_CHECKER, isKafkaConsumedTotalCheckerEnabled());
+ config.put(SECURED_EMAIL_TEMPLATES, isSecuredEmailTemplatesEnabled());
+ config.put(UNLEASH, unleashEnabled);
+
+ Log.info("=== Startup configuration ===");
+ config.forEach((key, value) -> {
+ Log.infof("%s=%s", key, value);
+ });
+ }
+
+ public boolean isAggregationWithRecipientsResolverEnabled() {
+ if (unleashEnabled) {
+ return unleash.isEnabled(AGGREGATION_WITH_RECIPIENTS_RESOLVER, false);
+ } else {
+ return useRecipientsResolverClowdappForDailyDigestEnabled;
+ }
+ }
+
+ public boolean isAsyncAggregationEnabled() {
+ if (unleashEnabled) {
+ return unleash.isEnabled(ASYNC_AGGREGATION, false);
+ } else {
+ return asyncAggregation;
+ }
+ }
+
+ public boolean isDefaultTemplateEnabled() {
+ return defaultTemplateEnabled;
+ }
+
+ public boolean isDrawerEnabled() {
+ if (unleashEnabled) {
+ return unleash.isEnabled(DRAWER, false);
+ } else {
+ return drawerEnabled;
+ }
+ }
+
+ public boolean isEmailsOnlyModeEnabled() {
+ return emailsOnlyModeEnabled;
+ }
+
+ public boolean isHccEmailSenderNameEnabled(String orgId) {
+ if (unleashEnabled) {
+ UnleashContext unleashContext = UnleashContext.builder().addProperty("orgId", orgId).build();
+ return unleash.isEnabled(HCC_EMAIL_SENDER_NAME, unleashContext, false);
+ } else {
+ return hccEmailSenderNameEnabled;
+ }
+ }
+
+ public boolean isKafkaConsumedTotalCheckerEnabled() {
+ if (unleashEnabled) {
+ return unleash.isEnabled(KAFKA_CONSUMED_TOTAL_CHECKER, false);
+ } else {
+ return kafkaConsumedTotalCheckerEnabled;
+ }
+ }
+
+ public boolean isSecuredEmailTemplatesEnabled() {
+ return useSecuredEmailTemplates;
+ }
+
+ @Deprecated(forRemoval = true)
+ public boolean isUseMBOPForFetchingUsers() {
+ return useMBOPForFetchingUsers;
+ }
+
+ @Deprecated(forRemoval = true)
+ public boolean isUseRbacForFetchingUsers() {
+ return useRbacForFetchingUsers;
+ }
+}
diff --git a/engine/src/main/java/com/redhat/cloud/notifications/db/repositories/TemplateRepository.java b/engine/src/main/java/com/redhat/cloud/notifications/db/repositories/TemplateRepository.java
index 9eadcf4719..e5fc775d6d 100644
--- a/engine/src/main/java/com/redhat/cloud/notifications/db/repositories/TemplateRepository.java
+++ b/engine/src/main/java/com/redhat/cloud/notifications/db/repositories/TemplateRepository.java
@@ -1,6 +1,6 @@
package com.redhat.cloud.notifications.db.repositories;
-import com.redhat.cloud.notifications.EngineConfig;
+import com.redhat.cloud.notifications.config.EngineConfig;
import com.redhat.cloud.notifications.models.AggregationEmailTemplate;
import com.redhat.cloud.notifications.models.InstantEmailTemplate;
import com.redhat.cloud.notifications.models.IntegrationTemplate;
diff --git a/engine/src/main/java/com/redhat/cloud/notifications/events/ConnectorReceiver.java b/engine/src/main/java/com/redhat/cloud/notifications/events/ConnectorReceiver.java
index 74d10d3911..d71939d444 100644
--- a/engine/src/main/java/com/redhat/cloud/notifications/events/ConnectorReceiver.java
+++ b/engine/src/main/java/com/redhat/cloud/notifications/events/ConnectorReceiver.java
@@ -1,6 +1,6 @@
package com.redhat.cloud.notifications.events;
-import com.redhat.cloud.notifications.EngineConfig;
+import com.redhat.cloud.notifications.config.EngineConfig;
import com.redhat.cloud.notifications.db.repositories.NotificationHistoryRepository;
import com.redhat.cloud.notifications.models.Endpoint;
import com.redhat.cloud.notifications.processors.drawer.DrawerProcessor;
diff --git a/engine/src/main/java/com/redhat/cloud/notifications/health/KafkaConsumedTotalChecker.java b/engine/src/main/java/com/redhat/cloud/notifications/health/KafkaConsumedTotalChecker.java
index 63c32959ea..174ffa25af 100644
--- a/engine/src/main/java/com/redhat/cloud/notifications/health/KafkaConsumedTotalChecker.java
+++ b/engine/src/main/java/com/redhat/cloud/notifications/health/KafkaConsumedTotalChecker.java
@@ -1,7 +1,6 @@
package com.redhat.cloud.notifications.health;
-import com.redhat.cloud.notifications.EngineConfig;
-import com.redhat.cloud.notifications.config.FeatureFlipper;
+import com.redhat.cloud.notifications.config.EngineConfig;
import io.micrometer.core.instrument.FunctionCounter;
import io.micrometer.core.instrument.MeterRegistry;
import io.quarkus.logging.Log;
@@ -16,9 +15,6 @@ public class KafkaConsumedTotalChecker {
private static final String COUNTER_NAME = "kafka.consumer.fetch.manager.records.consumed.total";
private static final String INGRESS_TOPIC = "platform.notifications.ingress";
- @Inject
- FeatureFlipper featureFlipper;
-
@Inject
EngineConfig engineConfig;
@@ -35,15 +31,13 @@ void postConstruct() {
.tag("client.id", "kafka-consumer-ingress")
.functionCounter();
if (consumedTotalCounter == null) {
- // This will help prevent a NPE throw if the metrics happens to change in our dependency.
Log.warnf("%s counter not found, %s is disabled", COUNTER_NAME, KafkaConsumedTotalChecker.class.getSimpleName());
- featureFlipper.setKafkaConsumedTotalCheckerEnabled(false);
}
}
@Scheduled(every = "${notifications.kafka-consumed-total-checker.period:5m}", delayed = "${notifications.kafka-consumed-total-checker.initial-delay:5m}")
public void periodicCheck() {
- if (engineConfig.isKafkaConsumedTotalCheckerEnabled()) {
+ if (engineConfig.isKafkaConsumedTotalCheckerEnabled() && consumedTotalCounter != null) {
double currentTotal = consumedTotalCounter.count();
if (currentTotal == previousTotal) {
Log.debugf("Kafka records consumed total check failed for topic '%s'", INGRESS_TOPIC);
diff --git a/engine/src/main/java/com/redhat/cloud/notifications/processors/camel/CamelProcessor.java b/engine/src/main/java/com/redhat/cloud/notifications/processors/camel/CamelProcessor.java
index b8996cd400..c8d35b8a6d 100644
--- a/engine/src/main/java/com/redhat/cloud/notifications/processors/camel/CamelProcessor.java
+++ b/engine/src/main/java/com/redhat/cloud/notifications/processors/camel/CamelProcessor.java
@@ -3,7 +3,7 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.redhat.cloud.notifications.DelayedThrower;
-import com.redhat.cloud.notifications.EngineConfig;
+import com.redhat.cloud.notifications.config.EngineConfig;
import com.redhat.cloud.notifications.db.repositories.TemplateRepository;
import com.redhat.cloud.notifications.models.CamelProperties;
import com.redhat.cloud.notifications.models.Endpoint;
diff --git a/engine/src/main/java/com/redhat/cloud/notifications/processors/drawer/DrawerProcessor.java b/engine/src/main/java/com/redhat/cloud/notifications/processors/drawer/DrawerProcessor.java
index 83c08e169e..636ce0a0a4 100644
--- a/engine/src/main/java/com/redhat/cloud/notifications/processors/drawer/DrawerProcessor.java
+++ b/engine/src/main/java/com/redhat/cloud/notifications/processors/drawer/DrawerProcessor.java
@@ -2,7 +2,7 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.redhat.cloud.notifications.EngineConfig;
+import com.redhat.cloud.notifications.config.EngineConfig;
import com.redhat.cloud.notifications.db.repositories.BundleRepository;
import com.redhat.cloud.notifications.db.repositories.DrawerNotificationRepository;
import com.redhat.cloud.notifications.db.repositories.EndpointRepository;
diff --git a/engine/src/main/java/com/redhat/cloud/notifications/processors/email/EmailActorsResolver.java b/engine/src/main/java/com/redhat/cloud/notifications/processors/email/EmailActorsResolver.java
index 34a6a71673..196336962b 100644
--- a/engine/src/main/java/com/redhat/cloud/notifications/processors/email/EmailActorsResolver.java
+++ b/engine/src/main/java/com/redhat/cloud/notifications/processors/email/EmailActorsResolver.java
@@ -1,7 +1,6 @@
package com.redhat.cloud.notifications.processors.email;
-import com.redhat.cloud.notifications.EngineConfig;
-import com.redhat.cloud.notifications.config.FeatureFlipper;
+import com.redhat.cloud.notifications.config.EngineConfig;
import com.redhat.cloud.notifications.models.Event;
import io.quarkus.logging.Log;
import jakarta.enterprise.context.ApplicationScoped;
@@ -102,7 +101,6 @@ private boolean isPendoMessageEnabled() {
}
public void setShowPendoUntil(LocalDate showPendoUntil) {
- FeatureFlipper.checkTestLaunchMode();
this.showPendoUntil = showPendoUntil;
}
}
diff --git a/engine/src/main/java/com/redhat/cloud/notifications/processors/email/EmailAggregator.java b/engine/src/main/java/com/redhat/cloud/notifications/processors/email/EmailAggregator.java
index 307c1286bb..0cfa37fc93 100644
--- a/engine/src/main/java/com/redhat/cloud/notifications/processors/email/EmailAggregator.java
+++ b/engine/src/main/java/com/redhat/cloud/notifications/processors/email/EmailAggregator.java
@@ -1,6 +1,6 @@
package com.redhat.cloud.notifications.processors.email;
-import com.redhat.cloud.notifications.EngineConfig;
+import com.redhat.cloud.notifications.config.EngineConfig;
import com.redhat.cloud.notifications.db.repositories.EmailAggregationRepository;
import com.redhat.cloud.notifications.db.repositories.EndpointRepository;
import com.redhat.cloud.notifications.db.repositories.EventTypeRepository;
diff --git a/engine/src/main/java/com/redhat/cloud/notifications/processors/email/EmailSubscriptionTypeProcessor.java b/engine/src/main/java/com/redhat/cloud/notifications/processors/email/EmailSubscriptionTypeProcessor.java
index 3ef2e69ac1..c6c464c407 100644
--- a/engine/src/main/java/com/redhat/cloud/notifications/processors/email/EmailSubscriptionTypeProcessor.java
+++ b/engine/src/main/java/com/redhat/cloud/notifications/processors/email/EmailSubscriptionTypeProcessor.java
@@ -1,7 +1,7 @@
package com.redhat.cloud.notifications.processors.email;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.redhat.cloud.notifications.EngineConfig;
+import com.redhat.cloud.notifications.config.EngineConfig;
import com.redhat.cloud.notifications.db.repositories.ApplicationRepository;
import com.redhat.cloud.notifications.db.repositories.BundleRepository;
import com.redhat.cloud.notifications.db.repositories.EmailAggregationRepository;
diff --git a/engine/src/main/java/com/redhat/cloud/notifications/processors/eventing/EventingProcessor.java b/engine/src/main/java/com/redhat/cloud/notifications/processors/eventing/EventingProcessor.java
index ed9cc70bfd..5af6a83240 100644
--- a/engine/src/main/java/com/redhat/cloud/notifications/processors/eventing/EventingProcessor.java
+++ b/engine/src/main/java/com/redhat/cloud/notifications/processors/eventing/EventingProcessor.java
@@ -1,7 +1,7 @@
package com.redhat.cloud.notifications.processors.eventing;
import com.redhat.cloud.notifications.DelayedThrower;
-import com.redhat.cloud.notifications.EngineConfig;
+import com.redhat.cloud.notifications.config.EngineConfig;
import com.redhat.cloud.notifications.models.CamelProperties;
import com.redhat.cloud.notifications.models.Endpoint;
import com.redhat.cloud.notifications.models.Event;
diff --git a/engine/src/main/java/com/redhat/cloud/notifications/processors/webhooks/WebhookTypeProcessor.java b/engine/src/main/java/com/redhat/cloud/notifications/processors/webhooks/WebhookTypeProcessor.java
index 361ca26683..b53a5e4119 100644
--- a/engine/src/main/java/com/redhat/cloud/notifications/processors/webhooks/WebhookTypeProcessor.java
+++ b/engine/src/main/java/com/redhat/cloud/notifications/processors/webhooks/WebhookTypeProcessor.java
@@ -1,7 +1,7 @@
package com.redhat.cloud.notifications.processors.webhooks;
import com.redhat.cloud.notifications.DelayedThrower;
-import com.redhat.cloud.notifications.EngineConfig;
+import com.redhat.cloud.notifications.config.EngineConfig;
import com.redhat.cloud.notifications.models.Endpoint;
import com.redhat.cloud.notifications.models.Event;
import com.redhat.cloud.notifications.models.WebhookProperties;
diff --git a/engine/src/main/java/com/redhat/cloud/notifications/recipients/rbac/RbacRecipientUsersProvider.java b/engine/src/main/java/com/redhat/cloud/notifications/recipients/rbac/RbacRecipientUsersProvider.java
index 076b5f11f5..5a7c3db6a7 100644
--- a/engine/src/main/java/com/redhat/cloud/notifications/recipients/rbac/RbacRecipientUsersProvider.java
+++ b/engine/src/main/java/com/redhat/cloud/notifications/recipients/rbac/RbacRecipientUsersProvider.java
@@ -1,6 +1,6 @@
package com.redhat.cloud.notifications.recipients.rbac;
-import com.redhat.cloud.notifications.config.FeatureFlipper;
+import com.redhat.cloud.notifications.config.EngineConfig;
import com.redhat.cloud.notifications.recipients.User;
import com.redhat.cloud.notifications.recipients.itservice.ITUserService;
import com.redhat.cloud.notifications.recipients.itservice.pojo.request.ITUserRequest;
@@ -56,7 +56,7 @@ public class RbacRecipientUsersProvider {
ITUserService itUserService;
@Inject
- FeatureFlipper featureFlipper;
+ EngineConfig engineConfig;
@ConfigProperty(name = "recipient-provider.rbac.elements-per-page", defaultValue = "1000")
Integer rbacElementsPerPage;
@@ -171,10 +171,10 @@ public List getUsers(String orgId, boolean adminsOnly) {
Timer.Sample getUsersTotalTimer = Timer.start(meterRegistry);
List users;
- if (featureFlipper.isUseRbacForFetchingUsers()) {
+ if (engineConfig.isUseRbacForFetchingUsers()) {
users = getWithPagination(
page -> retryOnRbacError(() -> rbacServiceToService.getUsers(orgId, adminsOnly, page * rbacElementsPerPage, rbacElementsPerPage)));
- } else if (this.featureFlipper.isUseMBOPForFetchingUsers()) {
+ } else if (engineConfig.isUseMBOPForFetchingUsers()) {
final List mbopUsers = new ArrayList<>();
// Keep the offset to ask for more users in case we need it.
diff --git a/engine/src/main/java/com/redhat/cloud/notifications/templates/EmailTemplateMigrationService.java b/engine/src/main/java/com/redhat/cloud/notifications/templates/EmailTemplateMigrationService.java
index ad59b6fb1e..2350a5b49e 100644
--- a/engine/src/main/java/com/redhat/cloud/notifications/templates/EmailTemplateMigrationService.java
+++ b/engine/src/main/java/com/redhat/cloud/notifications/templates/EmailTemplateMigrationService.java
@@ -1,7 +1,7 @@
package com.redhat.cloud.notifications.templates;
import com.cronutils.utils.StringUtils;
-import com.redhat.cloud.notifications.EngineConfig;
+import com.redhat.cloud.notifications.config.EngineConfig;
import com.redhat.cloud.notifications.models.AggregationEmailTemplate;
import com.redhat.cloud.notifications.models.Application;
import com.redhat.cloud.notifications.models.EventType;
diff --git a/engine/src/main/resources/application.properties b/engine/src/main/resources/application.properties
index 5db07d4135..5879aee810 100644
--- a/engine/src/main/resources/application.properties
+++ b/engine/src/main/resources/application.properties
@@ -178,6 +178,8 @@ quarkus.cache.caffeine.get-bundle-by-id.expire-after-write=PT15M
quarkus.cache.caffeine.get-app-by-name.expire-after-write=PT15M
quarkus.cache.caffeine.aggregation-target-email-subscription-endpoints.expire-after-write=PT5M
+quarkus.unleash.active=false
+quarkus.unleash.synchronous-fetch-on-initialisation=true
quarkus.unleash.url=http://localhost:4242
%test.notifications.email.show.pendo.until.date=2000-01-01
diff --git a/engine/src/test/java/com/redhat/cloud/notifications/EmailTemplatesInDbHelper.java b/engine/src/test/java/com/redhat/cloud/notifications/EmailTemplatesInDbHelper.java
index 6d63543510..24c1f8d08a 100644
--- a/engine/src/test/java/com/redhat/cloud/notifications/EmailTemplatesInDbHelper.java
+++ b/engine/src/test/java/com/redhat/cloud/notifications/EmailTemplatesInDbHelper.java
@@ -1,6 +1,6 @@
package com.redhat.cloud.notifications;
-import com.redhat.cloud.notifications.config.FeatureFlipper;
+import com.redhat.cloud.notifications.config.EngineConfig;
import com.redhat.cloud.notifications.db.ResourceHelpers;
import com.redhat.cloud.notifications.db.repositories.TemplateRepository;
import com.redhat.cloud.notifications.ingress.Action;
@@ -17,10 +17,10 @@
import io.quarkus.mailer.Mail;
import io.quarkus.mailer.Mailer;
import io.quarkus.qute.TemplateInstance;
+import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.mockito.InjectSpy;
import jakarta.inject.Inject;
import jakarta.persistence.NoResultException;
-import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import java.time.LocalDateTime;
@@ -30,6 +30,7 @@
import java.util.UUID;
import static com.redhat.cloud.notifications.models.SubscriptionType.DAILY;
+import static org.mockito.Mockito.when;
public abstract class EmailTemplatesInDbHelper {
@@ -54,10 +55,7 @@ public abstract class EmailTemplatesInDbHelper {
@Inject
protected TemplateService templateService;
- @Inject
- FeatureFlipper featureFlipper;
-
- @Inject
+ @InjectMock
EngineConfig engineConfig;
@Inject
@@ -89,7 +87,7 @@ protected void initData() {
}
eventTypes.put(eventTypeToCreate, eventType.getId());
}
- featureFlipper.setUseSecuredEmailTemplates(useSecuredTemplates());
+ when(engineConfig.isSecuredEmailTemplatesEnabled()).thenReturn(useSecuredTemplates());
if (engineConfig.isSecuredEmailTemplatesEnabled()) {
emailTemplateMigrationService.deleteAllTemplates();
}
@@ -100,11 +98,6 @@ protected void migrate() {
emailTemplateMigrationService.migrate();
}
- @AfterEach
- void restoreEnv() {
- featureFlipper.setUseSecuredEmailTemplates(false);
- }
-
protected String generateEmailSubject(String eventTypeStr, Action action) {
return generateEmailSubject(eventTypeStr, (Object) action);
}
diff --git a/engine/src/test/java/com/redhat/cloud/notifications/processors/drawer/DrawerProcessorTest.java b/engine/src/test/java/com/redhat/cloud/notifications/processors/drawer/DrawerProcessorTest.java
index 3f6deea101..d006f33812 100644
--- a/engine/src/test/java/com/redhat/cloud/notifications/processors/drawer/DrawerProcessorTest.java
+++ b/engine/src/test/java/com/redhat/cloud/notifications/processors/drawer/DrawerProcessorTest.java
@@ -1,6 +1,6 @@
package com.redhat.cloud.notifications.processors.drawer;
-import com.redhat.cloud.notifications.config.FeatureFlipper;
+import com.redhat.cloud.notifications.config.EngineConfig;
import com.redhat.cloud.notifications.db.ResourceHelpers;
import com.redhat.cloud.notifications.db.repositories.DrawerNotificationRepository;
import com.redhat.cloud.notifications.db.repositories.NotificationHistoryRepository;
@@ -72,8 +72,8 @@ class DrawerProcessorTest {
@InjectSpy
NotificationHistoryRepository notificationHistoryRepository;
- @Inject
- FeatureFlipper featureFlipper;
+ @InjectMock
+ EngineConfig engineConfig;
@Inject
@Any
@@ -115,12 +115,9 @@ void shouldCreateTwoDrawerNotifications() {
Endpoint endpoint = new Endpoint();
endpoint.setProperties(new SystemSubscriptionProperties());
endpoint.setType(EndpointType.DRAWER);
- try {
- featureFlipper.setDrawerEnabled(true);
- testee.process(createdEvent, List.of(endpoint));
- } finally {
- featureFlipper.setDrawerEnabled(false);
- }
+
+ when(engineConfig.isDrawerEnabled()).thenReturn(true);
+ testee.process(createdEvent, List.of(endpoint));
verify(notificationHistoryRepository, times(1)).createNotificationHistory(any(NotificationHistory.class));
diff --git a/engine/src/test/java/com/redhat/cloud/notifications/processors/email/EmailActorsResolverTest.java b/engine/src/test/java/com/redhat/cloud/notifications/processors/email/EmailActorsResolverTest.java
index d3a46d3b04..5993da6514 100644
--- a/engine/src/test/java/com/redhat/cloud/notifications/processors/email/EmailActorsResolverTest.java
+++ b/engine/src/test/java/com/redhat/cloud/notifications/processors/email/EmailActorsResolverTest.java
@@ -1,10 +1,11 @@
package com.redhat.cloud.notifications.processors.email;
-import com.redhat.cloud.notifications.config.FeatureFlipper;
+import com.redhat.cloud.notifications.config.EngineConfig;
import com.redhat.cloud.notifications.models.Application;
import com.redhat.cloud.notifications.models.Bundle;
import com.redhat.cloud.notifications.models.Event;
import com.redhat.cloud.notifications.models.EventType;
+import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import org.junit.jupiter.api.Test;
@@ -20,6 +21,8 @@
import static com.redhat.cloud.notifications.processors.email.EmailActorsResolver.RH_INSIGHTS_SENDER;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
@QuarkusTest
public class EmailActorsResolverTest {
@@ -27,15 +30,15 @@ public class EmailActorsResolverTest {
@Inject
EmailActorsResolver emailActorsResolver;
- @Inject
- FeatureFlipper featureFlipper;
+ @InjectMock
+ EngineConfig engineConfig;
/**
* Tests that the "Red Hat Insights" sender is returned by default.
*/
@Test
void testDefaultEmailSenderInsights() {
- Event event = new Event();
+ Event event = buildEvent(null, "rhel", "policies");
assertEquals(RH_INSIGHTS_SENDER, emailActorsResolver.getEmailSender(event), "unexpected email sender returned from the function under test");
}
@@ -44,8 +47,8 @@ void testDefaultEmailSenderInsights() {
*/
@Test
void testDefaultEmailSenderHCC() {
- featureFlipper.setHccEmailSenderNameEnabled(true);
- Event event = new Event();
+ when(engineConfig.isHccEmailSenderNameEnabled(anyString())).thenReturn(true);
+ Event event = buildEvent(null, "rhel", "policies");
assertEquals(RH_HCC_SENDER, emailActorsResolver.getEmailSender(event), "unexpected email sender returned from the function under test");
}
@@ -54,7 +57,7 @@ void testDefaultEmailSenderHCC() {
*/
@Test
void testOpenshiftClusterManagerStageEmailSender() {
- Event event = buildOCMEvent("stage");
+ Event event = buildEvent("stage", "openshift", "cluster-manager");
assertEquals(OPENSHIFT_SENDER_STAGE, emailActorsResolver.getEmailSender(event), "unexpected email sender returned from the function under test");
}
@@ -63,7 +66,7 @@ void testOpenshiftClusterManagerStageEmailSender() {
*/
@Test
void testOpenshiftClusterManagerDefaultEmailSender() {
- Event event = buildOCMEvent("prod");
+ Event event = buildEvent("prod", "openshift", "cluster-manager");
assertEquals(OPENSHIFT_SENDER_PROD, emailActorsResolver.getEmailSender(event), "unexpected email sender returned from the function under test");
}
@@ -72,7 +75,7 @@ void testOpenshiftClusterManagerDefaultEmailSender() {
*/
@Test
void testDefaultEmailPendoMessage() {
- Event event = new Event();
+ Event event = buildEvent(null, "rhel", "policies");
assertNull(emailActorsResolver.getPendoEmailMessage(event), "unexpected email pendo message returned from the function under test");
try {
@@ -89,7 +92,7 @@ void testDefaultEmailPendoMessage() {
*/
@Test
void testOpenshiftClusterManagerPendoMessage() {
- Event event = buildOCMEvent("prod");
+ Event event = buildEvent("prod", "openshift", "cluster-manager");
assertNull(emailActorsResolver.getPendoEmailMessage(event), "unexpected email pendo message returned from the function under test");
try {
@@ -97,7 +100,7 @@ void testOpenshiftClusterManagerPendoMessage() {
assertEquals(String.format(OCM_PENDO_MESSAGE, "January 01, 2050"), emailActorsResolver.getPendoEmailMessage(event).getPendoMessage(), "unexpected email pendo message returned from the function under test");
assertEquals(OCM_PENDO_TITLE, emailActorsResolver.getPendoEmailMessage(event).getPendoTitle(), "unexpected email pendo title returned from the function under test");
- event = buildOCMEvent("stage");
+ event = buildEvent("stage", "openshift", "cluster-manager");
assertEquals(String.format(OCM_PENDO_MESSAGE, "January 01, 2050"), emailActorsResolver.getPendoEmailMessage(event).getPendoMessage(), "unexpected email pendo message returned from the function under test");
assertEquals(OCM_PENDO_TITLE, emailActorsResolver.getPendoEmailMessage(event).getPendoTitle(), "unexpected email pendo title returned from the function under test");
@@ -106,19 +109,20 @@ void testOpenshiftClusterManagerPendoMessage() {
}
}
- private static Event buildOCMEvent(String sourceEnvironment) {
+ private static Event buildEvent(String sourceEnvironment, String bundleName, String appName) {
Bundle bundle = new Bundle();
- bundle.setName("openshift");
+ bundle.setName(bundleName);
Application app = new Application();
app.setBundle(bundle);
- app.setName("cluster-manager");
+ app.setName(appName);
EventType eventType = new EventType();
eventType.setApplication(app);
Event event = new Event();
+ event.setOrgId("12345");
event.setEventType(eventType);
event.setSourceEnvironment(sourceEnvironment);
diff --git a/engine/src/test/java/com/redhat/cloud/notifications/processors/email/EmailAggregatorTest.java b/engine/src/test/java/com/redhat/cloud/notifications/processors/email/EmailAggregatorTest.java
index 0fbb15aad7..bf751cdb8c 100644
--- a/engine/src/test/java/com/redhat/cloud/notifications/processors/email/EmailAggregatorTest.java
+++ b/engine/src/test/java/com/redhat/cloud/notifications/processors/email/EmailAggregatorTest.java
@@ -1,8 +1,7 @@
package com.redhat.cloud.notifications.processors.email;
-import com.redhat.cloud.notifications.EngineConfig;
import com.redhat.cloud.notifications.TestHelpers;
-import com.redhat.cloud.notifications.config.FeatureFlipper;
+import com.redhat.cloud.notifications.config.EngineConfig;
import com.redhat.cloud.notifications.db.ResourceHelpers;
import com.redhat.cloud.notifications.db.repositories.EmailAggregationRepository;
import com.redhat.cloud.notifications.db.repositories.EndpointRepository;
@@ -65,10 +64,7 @@ class EmailAggregatorTest {
@RestClient
RecipientsResolverService recipientsResolverService;
- @Inject
- FeatureFlipper featureFlipper;
-
- @Inject
+ @InjectMock
EngineConfig engineConfig;
@InjectSpy
@@ -104,7 +100,7 @@ void afterEach() {
@ValueSource(booleans = {true, false})
void shouldTestRecipientsFromSubscription(boolean useRecipientsResolverClowdappForDailyDigestEnabled) {
- featureFlipper.setUseRecipientsResolverClowdappForDailyDigestEnabled(useRecipientsResolverClowdappForDailyDigestEnabled);
+ when(engineConfig.isAggregationWithRecipientsResolverEnabled()).thenReturn(useRecipientsResolverClowdappForDailyDigestEnabled);
// init test environment
application = resourceHelpers.findApp("rhel", "policies");
eventType1 = resourceHelpers.findOrCreateEventType(application.getId(), TestHelpers.eventType);
@@ -165,7 +161,7 @@ void shouldTestRecipientsFromSubscription(boolean useRecipientsResolverClowdappF
@Test
void shouldTestFallbackOnLegacyRecipientsResolverFetching() {
- featureFlipper.setUseRecipientsResolverClowdappForDailyDigestEnabled(true);
+ when(engineConfig.isAggregationWithRecipientsResolverEnabled()).thenReturn(true);
// init test environment
application = resourceHelpers.findApp("rhel", "policies");
eventType1 = resourceHelpers.findOrCreateEventType(application.getId(), TestHelpers.eventType);
diff --git a/engine/src/test/java/com/redhat/cloud/notifications/processors/eventing/EventingTypeProcessorTest.java b/engine/src/test/java/com/redhat/cloud/notifications/processors/eventing/EventingTypeProcessorTest.java
index 720f91cae8..68a89d5c72 100644
--- a/engine/src/test/java/com/redhat/cloud/notifications/processors/eventing/EventingTypeProcessorTest.java
+++ b/engine/src/test/java/com/redhat/cloud/notifications/processors/eventing/EventingTypeProcessorTest.java
@@ -2,7 +2,7 @@
import com.redhat.cloud.notifications.MicrometerAssertionHelper;
import com.redhat.cloud.notifications.TestLifecycleManager;
-import com.redhat.cloud.notifications.config.FeatureFlipper;
+import com.redhat.cloud.notifications.config.EngineConfig;
import com.redhat.cloud.notifications.db.ResourceHelpers;
import com.redhat.cloud.notifications.db.repositories.NotificationHistoryRepository;
import com.redhat.cloud.notifications.events.EventWrapperAction;
@@ -48,6 +48,7 @@
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
@QuarkusTest
@QuarkusTestResource(TestLifecycleManager.class)
@@ -103,8 +104,8 @@ class EventingTypeProcessorTest {
@InjectMock
NotificationHistoryRepository notificationHistoryRepository;
- @Inject
- FeatureFlipper featureFlipper;
+ @InjectMock
+ EngineConfig engineConfig;
private InMemorySink inMemorySink;
@@ -188,15 +189,10 @@ void testCamelEndpointProcessing() {
@Test
void testEmailsOnlyModeCamelProcessor() {
- featureFlipper.setEmailsOnlyMode(true);
- try {
+ when(engineConfig.isEmailsOnlyModeEnabled()).thenReturn(true);
- camelProcessor.process(buildEvent(), List.of(new Endpoint()));
- micrometerAssertionHelper.assertCounterIncrement(EventingProcessor.PROCESSED_COUNTER_NAME, 0);
-
- } finally {
- featureFlipper.setEmailsOnlyMode(false);
- }
+ camelProcessor.process(buildEvent(), List.of(new Endpoint()));
+ micrometerAssertionHelper.assertCounterIncrement(EventingProcessor.PROCESSED_COUNTER_NAME, 0);
}
private static Event buildEvent() {
diff --git a/engine/src/test/java/com/redhat/cloud/notifications/processors/webhook/WebhookTest.java b/engine/src/test/java/com/redhat/cloud/notifications/processors/webhook/WebhookTest.java
index 862cf82406..9fedb7f8d7 100644
--- a/engine/src/test/java/com/redhat/cloud/notifications/processors/webhook/WebhookTest.java
+++ b/engine/src/test/java/com/redhat/cloud/notifications/processors/webhook/WebhookTest.java
@@ -2,7 +2,7 @@
import com.redhat.cloud.notifications.MicrometerAssertionHelper;
import com.redhat.cloud.notifications.TestLifecycleManager;
-import com.redhat.cloud.notifications.config.FeatureFlipper;
+import com.redhat.cloud.notifications.config.EngineConfig;
import com.redhat.cloud.notifications.db.repositories.NotificationHistoryRepository;
import com.redhat.cloud.notifications.events.EventWrapperAction;
import com.redhat.cloud.notifications.ingress.Action;
@@ -43,6 +43,7 @@
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
@QuarkusTest
@QuarkusTestResource(TestLifecycleManager.class)
@@ -54,8 +55,8 @@ public class WebhookTest {
@Inject
MicrometerAssertionHelper micrometerAssertionHelper;
- @Inject
- FeatureFlipper featureFlipper;
+ @InjectMock
+ EngineConfig engineConfig;
@InjectMock
NotificationHistoryRepository notificationHistoryRepository;
@@ -116,18 +117,13 @@ void testWebhookUsingConnector() {
@Test
void testEmailsOnlyMode() {
- featureFlipper.setEmailsOnlyMode(true);
- try {
+ when(engineConfig.isEmailsOnlyModeEnabled()).thenReturn(true);
- Event event = new Event();
- event.setEventWrapper(new EventWrapperAction(buildWebhookAction()));
-
- webhookTypeProcessor.process(event, List.of(new Endpoint()));
- micrometerAssertionHelper.assertCounterIncrement(PROCESSED_WEBHOOK_COUNTER, 0);
+ Event event = new Event();
+ event.setEventWrapper(new EventWrapperAction(buildWebhookAction()));
- } finally {
- featureFlipper.setEmailsOnlyMode(false);
- }
+ webhookTypeProcessor.process(event, List.of(new Endpoint()));
+ micrometerAssertionHelper.assertCounterIncrement(PROCESSED_WEBHOOK_COUNTER, 0);
}
private static Action buildWebhookAction() {
diff --git a/engine/src/test/java/com/redhat/cloud/notifications/recipients/rbac/RbacRecipientUsersProviderTest.java b/engine/src/test/java/com/redhat/cloud/notifications/recipients/rbac/RbacRecipientUsersProviderTest.java
index 00f417bdc2..163b23035a 100644
--- a/engine/src/test/java/com/redhat/cloud/notifications/recipients/rbac/RbacRecipientUsersProviderTest.java
+++ b/engine/src/test/java/com/redhat/cloud/notifications/recipients/rbac/RbacRecipientUsersProviderTest.java
@@ -1,7 +1,7 @@
package com.redhat.cloud.notifications.recipients.rbac;
import com.redhat.cloud.notifications.TestConstants;
-import com.redhat.cloud.notifications.config.FeatureFlipper;
+import com.redhat.cloud.notifications.config.EngineConfig;
import com.redhat.cloud.notifications.recipients.User;
import com.redhat.cloud.notifications.recipients.itservice.ITUserService;
import com.redhat.cloud.notifications.recipients.itservice.pojo.request.ITUserRequest;
@@ -76,8 +76,8 @@ public class RbacRecipientUsersProviderTest {
@RestClient
MBOPService mbopService;
- @Inject
- FeatureFlipper featureFlipper;
+ @InjectMock
+ EngineConfig engineConfig;
@Test
void getGroupUsersShouldOnlyContainActiveUsers() {
@@ -185,24 +185,20 @@ public void getAllUsersFromDefaultGroup() {
@Test
public void getAllUsersFromDefaultGroupRBAC() {
- try {
- featureFlipper.setUseRbacForFetchingUsers(true);
- RbacGroup defaultGroup = new RbacGroup();
- defaultGroup.setPlatformDefault(true);
- defaultGroup.setUuid(UUID.randomUUID());
+ when(engineConfig.isUseRbacForFetchingUsers()).thenReturn(true);
+ RbacGroup defaultGroup = new RbacGroup();
+ defaultGroup.setPlatformDefault(true);
+ defaultGroup.setUuid(UUID.randomUUID());
- int elements = 133;
+ int elements = 133;
- mockGetGroup(defaultGroup);
- mockGetUsersRBAC(elements, false);
+ mockGetGroup(defaultGroup);
+ mockGetUsersRBAC(elements, false);
- List users = rbacRecipientUsersProvider.getGroupUsers(TestConstants.DEFAULT_ORG_ID, false, defaultGroup.getUuid());
- assertEquals(elements, users.size());
- for (int i = 0; i < elements; ++i) {
- assertEquals(String.format("username-%d", i), users.get(i).getUsername());
- }
- } finally {
- featureFlipper.setUseRbacForFetchingUsers(false);
+ List users = rbacRecipientUsersProvider.getGroupUsers(TestConstants.DEFAULT_ORG_ID, false, defaultGroup.getUuid());
+ assertEquals(elements, users.size());
+ for (int i = 0; i < elements; ++i) {
+ assertEquals(String.format("username-%d", i), users.get(i).getUsername());
}
}
@@ -240,31 +236,26 @@ public void getAllUsersCache() {
@Test
public void getAllUsersCacheRBAC() {
- try {
- featureFlipper.setUseRbacForFetchingUsers(true);
- int initialSize = 1095;
- int updatedSize = 1323;
- mockGetUsersRBAC(initialSize, false);
-
- List users = rbacRecipientUsersProvider.getUsers(TestConstants.DEFAULT_ORG_ID, false);
- assertEquals(initialSize, users.size());
- for (int i = 0; i < initialSize; ++i) {
- assertEquals(String.format("username-%d", i), users.get(i).getUsername());
- }
+ when(engineConfig.isUseRbacForFetchingUsers()).thenReturn(true);
+ int initialSize = 1095;
+ int updatedSize = 1323;
+ mockGetUsersRBAC(initialSize, false);
- mockGetUsersRBAC(updatedSize, false);
+ List users = rbacRecipientUsersProvider.getUsers(TestConstants.DEFAULT_ORG_ID, false);
+ assertEquals(initialSize, users.size());
+ for (int i = 0; i < initialSize; ++i) {
+ assertEquals(String.format("username-%d", i), users.get(i).getUsername());
+ }
- users = rbacRecipientUsersProvider.getUsers(TestConstants.DEFAULT_ORG_ID, false);
- // Should still have the initial size because of the cache
- assertEquals(initialSize, users.size());
- clearCached();
+ mockGetUsersRBAC(updatedSize, false);
- users = rbacRecipientUsersProvider.getUsers(TestConstants.DEFAULT_ORG_ID, false);
- assertEquals(updatedSize, users.size());
- } finally {
- featureFlipper.setUseRbacForFetchingUsers(false);
- }
+ users = rbacRecipientUsersProvider.getUsers(TestConstants.DEFAULT_ORG_ID, false);
+ // Should still have the initial size because of the cache
+ assertEquals(initialSize, users.size());
+ clearCached();
+ users = rbacRecipientUsersProvider.getUsers(TestConstants.DEFAULT_ORG_ID, false);
+ assertEquals(updatedSize, users.size());
}
@Test
@@ -301,52 +292,48 @@ public void getAllGroupsCache() {
*/
@Test
void testGetUsersMBOP() {
- try {
- this.featureFlipper.setUseMBOPForFetchingUsers(true);
-
- // Fake a REST call to MBOP.
- final List firstPageMBOPUsers = this.mockGetMBOPUsers(this.MBOPMaxResultsPerPage);
- final List secondPageMBOPUsers = this.mockGetMBOPUsers(this.MBOPMaxResultsPerPage);
- final List thirdPageMBOPUsers = this.mockGetMBOPUsers(this.MBOPMaxResultsPerPage / 2);
-
- final boolean adminsOnly = false;
-
- // Return a few pages of results, with the last one being less than
- // the configured maximum limit, so that we can test that the loop
- // is working as expected.
- Mockito
- .when(this.mbopService.getUsersByOrgId(Mockito.eq(this.bopApiToken), Mockito.eq(this.bopClientId), Mockito.eq(this.bopEnv), Mockito.eq(TestConstants.DEFAULT_ORG_ID), Mockito.eq(adminsOnly), Mockito.eq(RbacRecipientUsersProvider.MBOP_SORT_ORDER), Mockito.eq(this.MBOPMaxResultsPerPage), Mockito.anyInt()))
- .thenReturn(firstPageMBOPUsers, secondPageMBOPUsers, thirdPageMBOPUsers);
-
- // Call the function under test.
- final List result = this.rbacRecipientUsersProvider.getUsers(TestConstants.DEFAULT_ORG_ID, adminsOnly);
-
- // Verify that the offset argument, which is the one that changes
- // on each iteration, has been correctly incremented.
- final ArgumentCaptor capturedOffset = ArgumentCaptor.forClass(Integer.class);
- Mockito.verify(this.mbopService, Mockito.times(3)).getUsersByOrgId(Mockito.eq(this.bopApiToken), Mockito.eq(this.bopClientId), Mockito.eq(this.bopEnv), Mockito.eq(TestConstants.DEFAULT_ORG_ID), Mockito.eq(adminsOnly), Mockito.eq(RbacRecipientUsersProvider.MBOP_SORT_ORDER), Mockito.eq(this.MBOPMaxResultsPerPage), capturedOffset.capture());
-
- final List capturedValues = capturedOffset.getAllValues();
- assertIterableEquals(
- List.of(
- 0,
- this.MBOPMaxResultsPerPage,
- this.MBOPMaxResultsPerPage * 2
- ),
- capturedValues,
- "unexpected offset values used when calling MBOP"
- );
-
- // Transform the generated MBOP users in order to check that the
- // function under test did the transformations as expected.
- final List mockUsers = this.rbacRecipientUsersProvider.transformMBOPUserToUser(firstPageMBOPUsers);
- mockUsers.addAll(this.rbacRecipientUsersProvider.transformMBOPUserToUser(secondPageMBOPUsers));
- mockUsers.addAll(this.rbacRecipientUsersProvider.transformMBOPUserToUser(thirdPageMBOPUsers));
-
- assertIterableEquals(mockUsers, result, "the list of users returned by the function under test is not correct");
- } finally {
- this.featureFlipper.setUseMBOPForFetchingUsers(false);
- }
+ when(engineConfig.isUseMBOPForFetchingUsers()).thenReturn(true);
+
+ // Fake a REST call to MBOP.
+ final List firstPageMBOPUsers = this.mockGetMBOPUsers(this.MBOPMaxResultsPerPage);
+ final List secondPageMBOPUsers = this.mockGetMBOPUsers(this.MBOPMaxResultsPerPage);
+ final List thirdPageMBOPUsers = this.mockGetMBOPUsers(this.MBOPMaxResultsPerPage / 2);
+
+ final boolean adminsOnly = false;
+
+ // Return a few pages of results, with the last one being less than
+ // the configured maximum limit, so that we can test that the loop
+ // is working as expected.
+ Mockito
+ .when(this.mbopService.getUsersByOrgId(Mockito.eq(this.bopApiToken), Mockito.eq(this.bopClientId), Mockito.eq(this.bopEnv), Mockito.eq(TestConstants.DEFAULT_ORG_ID), Mockito.eq(adminsOnly), Mockito.eq(RbacRecipientUsersProvider.MBOP_SORT_ORDER), Mockito.eq(this.MBOPMaxResultsPerPage), Mockito.anyInt()))
+ .thenReturn(firstPageMBOPUsers, secondPageMBOPUsers, thirdPageMBOPUsers);
+
+ // Call the function under test.
+ final List result = this.rbacRecipientUsersProvider.getUsers(TestConstants.DEFAULT_ORG_ID, adminsOnly);
+
+ // Verify that the offset argument, which is the one that changes
+ // on each iteration, has been correctly incremented.
+ final ArgumentCaptor capturedOffset = ArgumentCaptor.forClass(Integer.class);
+ Mockito.verify(this.mbopService, Mockito.times(3)).getUsersByOrgId(Mockito.eq(this.bopApiToken), Mockito.eq(this.bopClientId), Mockito.eq(this.bopEnv), Mockito.eq(TestConstants.DEFAULT_ORG_ID), Mockito.eq(adminsOnly), Mockito.eq(RbacRecipientUsersProvider.MBOP_SORT_ORDER), Mockito.eq(this.MBOPMaxResultsPerPage), capturedOffset.capture());
+
+ final List capturedValues = capturedOffset.getAllValues();
+ assertIterableEquals(
+ List.of(
+ 0,
+ this.MBOPMaxResultsPerPage,
+ this.MBOPMaxResultsPerPage * 2
+ ),
+ capturedValues,
+ "unexpected offset values used when calling MBOP"
+ );
+
+ // Transform the generated MBOP users in order to check that the
+ // function under test did the transformations as expected.
+ final List mockUsers = this.rbacRecipientUsersProvider.transformMBOPUserToUser(firstPageMBOPUsers);
+ mockUsers.addAll(this.rbacRecipientUsersProvider.transformMBOPUserToUser(secondPageMBOPUsers));
+ mockUsers.addAll(this.rbacRecipientUsersProvider.transformMBOPUserToUser(thirdPageMBOPUsers));
+
+ assertIterableEquals(mockUsers, result, "the list of users returned by the function under test is not correct");
}
private void mockGetUsers(int elements, boolean adminsOnly) {
diff --git a/engine/src/test/java/com/redhat/cloud/notifications/templates/TestDefaultTemplate.java b/engine/src/test/java/com/redhat/cloud/notifications/templates/TestDefaultTemplate.java
index a3b81a594a..ad8c559a8e 100644
--- a/engine/src/test/java/com/redhat/cloud/notifications/templates/TestDefaultTemplate.java
+++ b/engine/src/test/java/com/redhat/cloud/notifications/templates/TestDefaultTemplate.java
@@ -2,12 +2,11 @@
import com.redhat.cloud.notifications.EmailTemplatesInDbHelper;
import com.redhat.cloud.notifications.TestHelpers;
-import com.redhat.cloud.notifications.config.FeatureFlipper;
+import com.redhat.cloud.notifications.config.EngineConfig;
import com.redhat.cloud.notifications.ingress.Action;
import com.redhat.cloud.notifications.models.NotificationsConsoleCloudEvent;
+import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
-import jakarta.inject.Inject;
-import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -15,14 +14,15 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.when;
@QuarkusTest
public class TestDefaultTemplate extends EmailTemplatesInDbHelper {
private static final String EVENT_TYPE_NAME = "event-type-without-template";
- @Inject
- FeatureFlipper featureFlipper;
+ @InjectMock
+ EngineConfig engineConfig;
@Override
protected String getApp() {
@@ -36,12 +36,7 @@ protected List getUsedEventTypeNames() {
@BeforeEach
void beforeEach() {
- featureFlipper.setUseDefaultTemplate(true);
- }
-
- @AfterEach
- void afterEach() {
- featureFlipper.setUseDefaultTemplate(false);
+ when(engineConfig.isDefaultTemplateEnabled()).thenReturn(true);
}
@Test
diff --git a/pom.xml b/pom.xml
index b9a7ad618d..e7fe545e14 100644
--- a/pom.xml
+++ b/pom.xml
@@ -69,6 +69,7 @@
6.7.1
2.0.5
+ 1.4.0
quarkus-bom
io.quarkus.platform
diff --git a/recipients-resolver/pom.xml b/recipients-resolver/pom.xml
index cf291086c2..a02637296e 100644
--- a/recipients-resolver/pom.xml
+++ b/recipients-resolver/pom.xml
@@ -53,6 +53,11 @@
quarkus-logging-sentry
${quarkus-logging-sentry.version}
+
+ io.quarkiverse.unleash
+ quarkus-unleash
+ ${quarkus-unleash.version}
+
diff --git a/recipients-resolver/src/main/java/com/redhat/cloud/notifications/recipients/config/RecipientsResolverConfig.java b/recipients-resolver/src/main/java/com/redhat/cloud/notifications/recipients/config/RecipientsResolverConfig.java
index 463389b4cc..a6b15dbb69 100644
--- a/recipients-resolver/src/main/java/com/redhat/cloud/notifications/recipients/config/RecipientsResolverConfig.java
+++ b/recipients-resolver/src/main/java/com/redhat/cloud/notifications/recipients/config/RecipientsResolverConfig.java
@@ -1,34 +1,65 @@
package com.redhat.cloud.notifications.recipients.config;
+import io.getunleash.Unleash;
import io.quarkus.logging.Log;
-import io.quarkus.runtime.StartupEvent;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;
+import jakarta.enterprise.event.Startup;
+import jakarta.inject.Inject;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import java.time.Duration;
+import java.util.Map;
+import java.util.TreeMap;
@ApplicationScoped
public class RecipientsResolverConfig {
+ /*
+ * Env vars configuration
+ */
+ private static final String MAX_RESULTS_PER_PAGE = "notifications.recipients-resolver.max-results-per-page";
+ private static final String MBOP_ENV = "mbop.env";
+ private static final String RETRY_INITIAL_BACKOFF = "notifications.recipients-resolver.retry.initial-backoff";
+ private static final String RETRY_MAX_ATTEMPTS = "notifications.recipients-resolver.retry.max-attempts";
+ private static final String RETRY_MAX_BACKOFF = "notifications.recipients-resolver.retry.max-backoff";
+ private static final String WARN_IF_DURATION_EXCEEDS = "notifications.recipients-resolver.warn-if-request-duration-exceeds";
+ private static final String UNLEASH = "notifications.unleash.enabled";
+
+ /*
+ * Unleash configuration
+ */
+ private static final String FETCH_USERS_WITH_MBOP = toggleName("fetch-users-with-mbop");
+ private static final String FETCH_USERS_WITH_RBAC = toggleName("fetch-users-with-rbac");
+
+ private static String toggleName(String feature) {
+ return String.format("notifications-engine.%s.enabled", feature);
+ }
+
+ @ConfigProperty(name = UNLEASH, defaultValue = "false")
+ @Deprecated(forRemoval = true, since = "To be removed when we're done migrating to Unleash in all environments")
+ boolean unleashEnabled;
+
@ConfigProperty(name = "notifications.recipients-resolver.fetch.users.rbac.enabled", defaultValue = "false")
- boolean fetchUsersWithRBAC;
+ @Deprecated(forRemoval = true, since = "To be removed when we're done migrating to Unleash in all environments")
+ boolean fetchUsersWithRbacEnabled;
@ConfigProperty(name = "notifications.recipients-resolver.fetch.users.mbop.enabled", defaultValue = "false")
- boolean fetchUsersWithMbop;
+ @Deprecated(forRemoval = true, since = "To be removed when we're done migrating to Unleash in all environments")
+ boolean fetchUsersWithMbopEnabled;
- @ConfigProperty(name = "notifications.recipients-resolver.max-results-per-page", defaultValue = "1000")
+ @ConfigProperty(name = MAX_RESULTS_PER_PAGE, defaultValue = "1000")
int maxResultsPerPage;
- @ConfigProperty(name = "notifications.recipients-resolver.retry.max-attempts", defaultValue = "3")
- int maxRetryAttempts;
-
- @ConfigProperty(name = "notifications.recipients-resolver.retry.initial-backoff", defaultValue = "0.1S")
+ @ConfigProperty(name = RETRY_INITIAL_BACKOFF, defaultValue = "0.1S")
Duration initialRetryBackoff;
- @ConfigProperty(name = "notifications.recipients-resolver.retry.max-backoff", defaultValue = "1S")
+ @ConfigProperty(name = RETRY_MAX_ATTEMPTS, defaultValue = "3")
+ int maxRetryAttempts;
+
+ @ConfigProperty(name = RETRY_MAX_BACKOFF, defaultValue = "1S")
Duration maxRetryBackoff;
- @ConfigProperty(name = "notifications.recipients-resolver.warn-if-request-duration-exceeds", defaultValue = "30S")
+ @ConfigProperty(name = WARN_IF_DURATION_EXCEEDS, defaultValue = "30S")
Duration logTooLongRequestLimit;
@ConfigProperty(name = "mbop.apitoken", defaultValue = "na")
@@ -37,36 +68,45 @@ public class RecipientsResolverConfig {
@ConfigProperty(name = "mbop.client_id", defaultValue = "na")
String mbopClientId;
- @ConfigProperty(name = "mbop.env", defaultValue = "na")
+ @ConfigProperty(name = MBOP_ENV, defaultValue = "na")
String mbopEnv;
- void logFeaturesStatusAtStartup(@Observes StartupEvent event) {
- Log.infof("=== %s startup status ===", RecipientsResolverConfig.class.getSimpleName());
- Log.infof("The fetching users with Rbac is %s", fetchUsersWithRBAC ? "enabled" : "disabled");
- Log.infof("The fetching users with Mbop is %s", fetchUsersWithMbop ? "enabled" : "disabled");
- Log.infof("The fetching users with IT Service is %s", !(fetchUsersWithRBAC || fetchUsersWithMbop) ? "enabled" : "disabled");
- Log.infof("The max result per page is %s", maxResultsPerPage);
- Log.infof("The max retry attempts is %s", maxRetryAttempts);
- Log.infof("The retry back-off initial value is %s", initialRetryBackoff);
- Log.infof("The max retry back-off value is %s", maxRetryBackoff);
- Log.infof("The MBOP env is %s", mbopEnv);
- Log.infof("The requests to external services will be logged it they exceed %s", logTooLongRequestLimit);
- }
-
- public boolean isFetchUsersWithMbop() {
- return fetchUsersWithMbop;
- }
-
- public boolean isFetchUsersWithRBAC() {
- return this.fetchUsersWithRBAC;
+ @Inject
+ Unleash unleash;
+
+ void logConfigAtStartup(@Observes Startup event) {
+
+ Map config = new TreeMap<>();
+ config.put(FETCH_USERS_WITH_MBOP, isFetchUsersWithMbopEnabled());
+ config.put(FETCH_USERS_WITH_RBAC, isFetchUsersWithRbacEnabled());
+ config.put(MAX_RESULTS_PER_PAGE, getMaxResultsPerPage());
+ config.put(MBOP_ENV, getMbopEnv());
+ config.put(RETRY_INITIAL_BACKOFF, getInitialRetryBackoff());
+ config.put(RETRY_MAX_ATTEMPTS, getMaxRetryAttempts());
+ config.put(RETRY_MAX_BACKOFF, getMaxRetryBackoff());
+ config.put(WARN_IF_DURATION_EXCEEDS, getLogTooLongRequestLimit());
+ config.put(UNLEASH, unleashEnabled);
+
+ Log.info("=== Startup configuration ===");
+ config.forEach((key, value) -> {
+ Log.infof("%s=%s", key, value);
+ });
}
- public void setFetchUsersWithRBAC(boolean fetchUsersWithRBAC) {
- this.fetchUsersWithRBAC = fetchUsersWithRBAC;
+ public boolean isFetchUsersWithMbopEnabled() {
+ if (unleashEnabled) {
+ return unleash.isEnabled(FETCH_USERS_WITH_MBOP, false);
+ } else {
+ return fetchUsersWithMbopEnabled;
+ }
}
- public void setFetchUsersWithMbop(boolean fetchUsersWithMbop) {
- this.fetchUsersWithMbop = fetchUsersWithMbop;
+ public boolean isFetchUsersWithRbacEnabled() {
+ if (unleashEnabled) {
+ return unleash.isEnabled(FETCH_USERS_WITH_RBAC, false);
+ } else {
+ return fetchUsersWithRbacEnabled;
+ }
}
public int getMaxResultsPerPage() {
diff --git a/recipients-resolver/src/main/java/com/redhat/cloud/notifications/recipients/resolver/FetchUsersFromExternalServices.java b/recipients-resolver/src/main/java/com/redhat/cloud/notifications/recipients/resolver/FetchUsersFromExternalServices.java
index 87ae57ee0f..34c32e2910 100644
--- a/recipients-resolver/src/main/java/com/redhat/cloud/notifications/recipients/resolver/FetchUsersFromExternalServices.java
+++ b/recipients-resolver/src/main/java/com/redhat/cloud/notifications/recipients/resolver/FetchUsersFromExternalServices.java
@@ -82,9 +82,9 @@ public class FetchUsersFromExternalServices {
@PostConstruct
public void postConstruct() {
- if (recipientsResolverConfig.isFetchUsersWithRBAC()) {
+ if (recipientsResolverConfig.isFetchUsersWithRbacEnabled()) {
failuresCounter = meterRegistry.counter("rbac.failures");
- } else if (recipientsResolverConfig.isFetchUsersWithMbop()) {
+ } else if (recipientsResolverConfig.isFetchUsersWithMbopEnabled()) {
failuresCounter = meterRegistry.counter("mbop.failures");
} else {
failuresCounter = meterRegistry.counter("it.failures");
@@ -106,7 +106,7 @@ public List getUsers(String orgId, boolean adminsOnly) {
Timer.Sample getUsersTotalTimer = Timer.start(meterRegistry);
List users;
- if (recipientsResolverConfig.isFetchUsersWithRBAC()) {
+ if (recipientsResolverConfig.isFetchUsersWithRbacEnabled()) {
users = getWithPagination(
page -> retryOnError(() -> {
LocalDateTime startTime = LocalDateTime.now();
@@ -117,7 +117,7 @@ public List getUsers(String orgId, boolean adminsOnly) {
}
return rbacUserPage;
}));
- } else if (recipientsResolverConfig.isFetchUsersWithMbop()) {
+ } else if (recipientsResolverConfig.isFetchUsersWithMbopEnabled()) {
users = fetchUsersWithMbop(orgId, adminsOnly);
} else {
users = fetchUsersWithItUserService(orgId, adminsOnly);
diff --git a/recipients-resolver/src/main/resources/application.properties b/recipients-resolver/src/main/resources/application.properties
index 5a0c1a51f5..f353a156f4 100644
--- a/recipients-resolver/src/main/resources/application.properties
+++ b/recipients-resolver/src/main/resources/application.properties
@@ -51,3 +51,7 @@ quarkus.cache.caffeine.recipients-users-provider-get-users.expire-after-write=PT
quarkus.cache.caffeine.recipients-users-provider-get-group-users.expire-after-write=PT10M
quarkus.cache.caffeine.find-recipients.expire-after-write=PT10M
quarkus.cache.caffeine.find-recipients.metrics-enabled=true
+
+quarkus.unleash.active=false
+quarkus.unleash.synchronous-fetch-on-initialisation=true
+quarkus.unleash.url=http://localhost:4242
diff --git a/recipients-resolver/src/test/java/com/redhat/cloud/notifications/recipients/resolver/FetchUsersFromExternalServicesTest.java b/recipients-resolver/src/test/java/com/redhat/cloud/notifications/recipients/resolver/FetchUsersFromExternalServicesTest.java
index f9eb103477..5301c27311 100644
--- a/recipients-resolver/src/test/java/com/redhat/cloud/notifications/recipients/resolver/FetchUsersFromExternalServicesTest.java
+++ b/recipients-resolver/src/test/java/com/redhat/cloud/notifications/recipients/resolver/FetchUsersFromExternalServicesTest.java
@@ -27,6 +27,8 @@
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
+
+import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -38,6 +40,9 @@
import static org.junit.jupiter.api.Assertions.assertIterableEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
@QuarkusTest
@@ -60,9 +65,21 @@ public class FetchUsersFromExternalServicesTest {
@RestClient
MBOPService mbopService;
- @Inject
+ @InjectMock
RecipientsResolverConfig recipientsResolverConfig;
+ @BeforeEach
+ void beforeEach() {
+ // This is the default config. It has to be set because we're mocking RecipientsResolverConfig.
+ when(recipientsResolverConfig.getInitialRetryBackoff()).thenReturn(Duration.ofMillis(100));
+ when(recipientsResolverConfig.getMaxResultsPerPage()).thenReturn(1000);
+ when(recipientsResolverConfig.getMaxRetryAttempts()).thenReturn(3);
+ when(recipientsResolverConfig.getMaxRetryBackoff()).thenReturn(Duration.ofSeconds(1));
+ when(recipientsResolverConfig.getMbopApiToken()).thenReturn("na");
+ when(recipientsResolverConfig.getMbopClientId()).thenReturn("na");
+ when(recipientsResolverConfig.getMbopEnv()).thenReturn("na");
+ }
+
@Test
void getGroupUsersShouldOnlyContainActiveUsers() {
RbacGroup defaultGroup = new RbacGroup();
@@ -168,24 +185,20 @@ public void getAllUsersFromDefaultGroup() {
@Test
public void getAllUsersFromDefaultGroupRBAC() {
- try {
- recipientsResolverConfig.setFetchUsersWithRBAC(true);
- RbacGroup defaultGroup = new RbacGroup();
- defaultGroup.setPlatformDefault(true);
- defaultGroup.setUuid(UUID.randomUUID());
+ when(recipientsResolverConfig.isFetchUsersWithRbacEnabled()).thenReturn(true);
+ RbacGroup defaultGroup = new RbacGroup();
+ defaultGroup.setPlatformDefault(true);
+ defaultGroup.setUuid(UUID.randomUUID());
- int elements = 133;
+ int elements = 133;
- mockGetGroup(defaultGroup);
- mockGetUsersRBAC(elements, false);
+ mockGetGroup(defaultGroup);
+ mockGetUsersRBAC(elements, false);
- List users = fetchUsersFromExternalServices.getGroupUsers(DEFAULT_ORG_ID, false, defaultGroup.getUuid());
- assertEquals(elements, users.size());
- for (int i = 0; i < elements; ++i) {
- assertEquals(String.format("username-%d", i), users.get(i).getUsername());
- }
- } finally {
- recipientsResolverConfig.setFetchUsersWithRBAC(false);
+ List users = fetchUsersFromExternalServices.getGroupUsers(DEFAULT_ORG_ID, false, defaultGroup.getUuid());
+ assertEquals(elements, users.size());
+ for (int i = 0; i < elements; ++i) {
+ assertEquals(String.format("username-%d", i), users.get(i).getUsername());
}
}
@@ -223,31 +236,27 @@ public void getAllUsersCache() {
@Test
public void getAllUsersCacheRBAC() {
- try {
- recipientsResolverConfig.setFetchUsersWithRBAC(true);
- int initialSize = 1095;
- int updatedSize = 1323;
- mockGetUsersRBAC(initialSize, false);
-
- List users = fetchUsersFromExternalServices.getUsers(DEFAULT_ORG_ID, false);
- assertEquals(initialSize, users.size());
- for (int i = 0; i < initialSize; ++i) {
- assertEquals(String.format("username-%d", i), users.get(i).getUsername());
- }
-
- mockGetUsersRBAC(updatedSize, false);
+ when(recipientsResolverConfig.isFetchUsersWithRbacEnabled()).thenReturn(true);
- users = fetchUsersFromExternalServices.getUsers(DEFAULT_ORG_ID, false);
- // Should still have the initial size because of the cache
- assertEquals(initialSize, users.size());
- clearCached();
+ int initialSize = 1095;
+ int updatedSize = 1323;
+ mockGetUsersRBAC(initialSize, false);
- users = fetchUsersFromExternalServices.getUsers(DEFAULT_ORG_ID, false);
- assertEquals(updatedSize, users.size());
- } finally {
- recipientsResolverConfig.setFetchUsersWithRBAC(false);
+ List users = fetchUsersFromExternalServices.getUsers(DEFAULT_ORG_ID, false);
+ assertEquals(initialSize, users.size());
+ for (int i = 0; i < initialSize; ++i) {
+ assertEquals(String.format("username-%d", i), users.get(i).getUsername());
}
+ mockGetUsersRBAC(updatedSize, false);
+
+ users = fetchUsersFromExternalServices.getUsers(DEFAULT_ORG_ID, false);
+ // Should still have the initial size because of the cache
+ assertEquals(initialSize, users.size());
+ clearCached();
+
+ users = fetchUsersFromExternalServices.getUsers(DEFAULT_ORG_ID, false);
+ assertEquals(updatedSize, users.size());
}
@Test
@@ -284,52 +293,48 @@ public void getAllGroupsCache() {
*/
@Test
void testGetUsersMBOP() {
- try {
- this.recipientsResolverConfig.setFetchUsersWithMbop(true);
-
- // Fake a REST call to MBOP.
- final List firstPageMBOPUsers = this.mockGetMBOPUsers(recipientsResolverConfig.getMaxResultsPerPage());
- final List secondPageMBOPUsers = this.mockGetMBOPUsers(recipientsResolverConfig.getMaxResultsPerPage());
- final List thirdPageMBOPUsers = this.mockGetMBOPUsers(recipientsResolverConfig.getMaxResultsPerPage() / 2);
-
- final boolean adminsOnly = false;
-
- // Return a few pages of results, with the last one being less than
- // the configured maximum limit, so that we can test that the loop
- // is working as expected.
- Mockito
- .when(this.mbopService.getUsersByOrgId(Mockito.eq(recipientsResolverConfig.getMbopApiToken()), Mockito.eq(recipientsResolverConfig.getMbopClientId()), Mockito.eq(recipientsResolverConfig.getMbopEnv()), Mockito.eq(DEFAULT_ORG_ID), Mockito.eq(adminsOnly), Mockito.eq(FetchUsersFromExternalServices.MBOP_SORT_ORDER), Mockito.eq(recipientsResolverConfig.getMaxResultsPerPage()), Mockito.anyInt()))
- .thenReturn(firstPageMBOPUsers, secondPageMBOPUsers, thirdPageMBOPUsers);
-
- // Call the function under test.
- final List result = this.fetchUsersFromExternalServices.getUsers(DEFAULT_ORG_ID, adminsOnly);
-
- // Verify that the offset argument, which is the one that changes
- // on each iteration, has been correctly incremented.
- final ArgumentCaptor capturedOffset = ArgumentCaptor.forClass(Integer.class);
- Mockito.verify(this.mbopService, Mockito.times(3)).getUsersByOrgId(Mockito.eq(recipientsResolverConfig.getMbopApiToken()), Mockito.eq(recipientsResolverConfig.getMbopClientId()), Mockito.eq(recipientsResolverConfig.getMbopEnv()), Mockito.eq(DEFAULT_ORG_ID), Mockito.eq(adminsOnly), Mockito.eq(FetchUsersFromExternalServices.MBOP_SORT_ORDER), Mockito.eq(recipientsResolverConfig.getMaxResultsPerPage()), capturedOffset.capture());
-
- final List capturedValues = capturedOffset.getAllValues();
- assertIterableEquals(
- List.of(
- 0,
- recipientsResolverConfig.getMaxResultsPerPage(),
- recipientsResolverConfig.getMaxResultsPerPage() * 2
- ),
- capturedValues,
- "unexpected offset values used when calling MBOP"
- );
-
- // Transform the generated MBOP users in order to check that the
- // function under test did the transformations as expected.
- final List mockUsers = this.fetchUsersFromExternalServices.transformMBOPUserToUser(firstPageMBOPUsers);
- mockUsers.addAll(this.fetchUsersFromExternalServices.transformMBOPUserToUser(secondPageMBOPUsers));
- mockUsers.addAll(this.fetchUsersFromExternalServices.transformMBOPUserToUser(thirdPageMBOPUsers));
-
- assertIterableEquals(mockUsers, result, "the list of users returned by the function under test is not correct");
- } finally {
- this.recipientsResolverConfig.setFetchUsersWithMbop(false);
- }
+ when(recipientsResolverConfig.isFetchUsersWithMbopEnabled()).thenReturn(true);
+
+ // Fake a REST call to MBOP.
+ final List firstPageMBOPUsers = this.mockGetMBOPUsers(recipientsResolverConfig.getMaxResultsPerPage());
+ final List secondPageMBOPUsers = this.mockGetMBOPUsers(recipientsResolverConfig.getMaxResultsPerPage());
+ final List thirdPageMBOPUsers = this.mockGetMBOPUsers(recipientsResolverConfig.getMaxResultsPerPage() / 2);
+
+ final boolean adminsOnly = false;
+
+ // Return a few pages of results, with the last one being less than
+ // the configured maximum limit, so that we can test that the loop
+ // is working as expected.
+ Mockito
+ .when(this.mbopService.getUsersByOrgId(anyString(), anyString(), anyString(), anyString(), anyBoolean(), anyString(), anyInt(), anyInt()))
+ .thenReturn(firstPageMBOPUsers, secondPageMBOPUsers, thirdPageMBOPUsers);
+
+ // Call the function under test.
+ final List result = this.fetchUsersFromExternalServices.getUsers(DEFAULT_ORG_ID, adminsOnly);
+
+ // Verify that the offset argument, which is the one that changes
+ // on each iteration, has been correctly incremented.
+ final ArgumentCaptor capturedOffset = ArgumentCaptor.forClass(Integer.class);
+ Mockito.verify(this.mbopService, Mockito.times(3)).getUsersByOrgId(anyString(), anyString(), anyString(), anyString(), anyBoolean(), anyString(), anyInt(), capturedOffset.capture());
+
+ final List capturedValues = capturedOffset.getAllValues();
+ assertIterableEquals(
+ List.of(
+ 0,
+ recipientsResolverConfig.getMaxResultsPerPage(),
+ recipientsResolverConfig.getMaxResultsPerPage() * 2
+ ),
+ capturedValues,
+ "unexpected offset values used when calling MBOP"
+ );
+
+ // Transform the generated MBOP users in order to check that the
+ // function under test did the transformations as expected.
+ final List mockUsers = this.fetchUsersFromExternalServices.transformMBOPUserToUser(firstPageMBOPUsers);
+ mockUsers.addAll(this.fetchUsersFromExternalServices.transformMBOPUserToUser(secondPageMBOPUsers));
+ mockUsers.addAll(this.fetchUsersFromExternalServices.transformMBOPUserToUser(thirdPageMBOPUsers));
+
+ assertIterableEquals(mockUsers, result, "the list of users returned by the function under test is not correct");
}
private void mockGetUsers(int elements, boolean adminsOnly) {