Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RHCLOUD-28942 #464

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .rhcicd/clowdapp-connector-email.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ objects:
env:
- name: ENV_NAME
value: ${ENV_NAME}
- name: NOTIFICATIONS_CONNECTOR_BOP_SKIP_USERS_RESOLUTION
value: ${NOTIFICATIONS_CONNECTOR_BOP_SKIP_USERS_RESOLUTION}
- name: NOTIFICATIONS_CONNECTOR_ENDPOINT_CACHE_MAX_SIZE
value: ${NOTIFICATIONS_CONNECTOR_ENDPOINT_CACHE_MAX_SIZE}
- name: NOTIFICATIONS_CONNECTOR_FETCH_USERS_RBAC_ENABLED
Expand Down Expand Up @@ -205,6 +207,9 @@ parameters:
- name: MIN_REPLICAS
value: "3"

- name: NOTIFICATIONS_CONNECTOR_BOP_SKIP_USERS_RESOLUTION
description: Should BOP skip transforming usernames from our payload into email addresses using the IT Users Service?
value: "false"
- name: NOTIFICATIONS_CONNECTOR_ENDPOINT_CACHE_MAX_SIZE
description: Maximum size of the Camel endpoints cache
value: "100"
Expand Down
5 changes: 5 additions & 0 deletions .rhcicd/clowdapp-engine.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ objects:
key: client-id
- name: PROCESSOR_EMAIL_BOP_ENV
value: ${BACKOFFICE_CLIENT_ENV}
- name: PROCESSOR_EMAIL_BOP_SKIP_USERS_RESOLUTION
value: ${PROCESSOR_EMAIL_BOP_SKIP_USERS_RESOLUTION}
- name: PROCESSOR_EMAIL_BOP_URL
value: ${BACKOFFICE_SCHEME}://${BACKOFFICE_HOST}:${BACKOFFICE_PORT}/v1/sendEmails
- name: PROCESSOR_EMAIL_NO_REPLY
Expand Down Expand Up @@ -330,6 +332,9 @@ parameters:
- name: NOTIFICATIONS_LOG_LEVEL
description: Log level for com.redhat.cloud.notifications
value: INFO
- name: PROCESSOR_EMAIL_BOP_SKIP_USERS_RESOLUTION
description: Should BOP skip transforming usernames from our payload into email addresses using the IT Users Service?
value: "false"
- name: QUARKUS_HIBERNATE_ORM_LOG_SQL
value: "false"
- name: QUARKUS_LOG_CLOUDWATCH_API_CALL_TIMEOUT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ public class FeatureFlipper {
@ConfigProperty(name = "notifications.async-aggregation.enabled", defaultValue = "true")
boolean asyncAggregation;

@ConfigProperty(name = "processor.email.bop.skip-users-resolution", defaultValue = "false")
boolean skipBopUsersResolution;

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");
Expand All @@ -120,6 +123,7 @@ void logFeaturesStatusAtStartup(@Observes StartupEvent event) {
Log.infof("The email connector is %s", emailConnectorEnabled ? "enabled" : "disabled");
Log.infof("The drawer connector is %s", drawerConnectorEnabled ? "enabled" : "disabled");
Log.infof("The async aggregation is %s", asyncAggregation ? "enabled" : "disabled");
Log.infof("The BOP users resolution is %s", !skipBopUsersResolution ? "enabled" : "disabled");
}

public boolean isEnforceBehaviorGroupNameUnicity() {
Expand Down Expand Up @@ -298,6 +302,15 @@ public void setAsyncAggregation(boolean asyncAggregation) {
this.asyncAggregation = asyncAggregation;
}

public boolean isSkipBopUsersResolution() {
return skipBopUsersResolution;
}

public void setSkipBopUsersResolution(boolean skipBopUsersResolution) {
checkTestLaunchMode();
this.skipBopUsersResolution = skipBopUsersResolution;
}

/**
* 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.redhat.cloud.notifications.connector.email.config.Environment;
import com.redhat.cloud.notifications.connector.email.constants.ExchangeProperty;
import com.redhat.cloud.notifications.connector.email.constants.Routes;
import com.redhat.cloud.notifications.connector.email.model.settings.User;
import com.redhat.cloud.notifications.connector.email.predicates.NotFinishedFetchingAllPages;
import com.redhat.cloud.notifications.connector.email.predicates.rbac.StatusCodeNotFound;
import com.redhat.cloud.notifications.connector.email.processors.bop.BOPRequestPreparer;
Expand Down Expand Up @@ -154,10 +155,10 @@ public void configureRoute() throws Exception {

from(seda(ENGINE_TO_CONNECTOR))
.routeId(this.connectorConfig.getConnectorName())
// Initialize the usernames hash set, where we will gather the
// Initialize the users hash set, where we will gather the
// fetched users from the user providers.
.process(exchange -> exchange.setProperty(ExchangeProperty.USERNAMES, new HashSet<String>()))
// Split each recipient setting and aggregate the usernames to end
.process(exchange -> exchange.setProperty(ExchangeProperty.USERS, new HashSet<User>()))
// Split each recipient setting and aggregate the users to end
// up with a single exchange.
.split(simpleF("${exchangeProperty.%s}", ExchangeProperty.RECIPIENT_SETTINGS), this.userAggregationStrategy).stopOnException()
// As the body of the exchange might change throughout the
Expand Down Expand Up @@ -195,13 +196,13 @@ public void configureRoute() throws Exception {
.setHeader(CaffeineConstants.ACTION, constant(CaffeineConstants.ACTION_GET))
.setHeader(CaffeineConstants.KEY, this.computeCacheKey())
.to(caffeineCache(Routes.FETCH_USERS_RBAC))
// Avoid calling RBAC if we do have the usernames cached.
// Avoid calling RBAC if we do have the users cached.
.choice()
.when(header(CaffeineConstants.ACTION_HAS_RESULT).isEqualTo(Boolean.TRUE))
// The cache engine leaves the usernames in the body of the
// The cache engine leaves the users in the body of the
// exchange, that is why we need to set them back in the
// property that the subsequent processors expect to find them.
.setProperty(ExchangeProperty.USERNAMES, body())
.setProperty(ExchangeProperty.USERS, body())
.otherwise()
// Clear all the headers that may come from the previous route.
.removeHeaders("*")
Expand All @@ -220,7 +221,7 @@ public void configureRoute() throws Exception {
// Store all the received recipients in the cache.
.setHeader(CaffeineConstants.ACTION, constant(CaffeineConstants.ACTION_PUT))
.setHeader(CaffeineConstants.KEY, this.computeCacheKey())
.setHeader(CaffeineConstants.VALUE, exchangeProperty(ExchangeProperty.USERNAMES))
.setHeader(CaffeineConstants.VALUE, exchangeProperty(ExchangeProperty.USERS))
.to(caffeineCache(Routes.FETCH_USERS_RBAC))
.endChoice()
.end()
Expand All @@ -243,13 +244,13 @@ public void configureRoute() throws Exception {
.setHeader(CaffeineConstants.ACTION, constant(CaffeineConstants.ACTION_GET))
.setHeader(CaffeineConstants.KEY, this.computeCacheKey())
.to(caffeineCache(Routes.FETCH_USERS_IT))
// Avoid calling IT if we do have the usernames cached.
// Avoid calling IT if we do have the users cached.
.choice()
.when(header(CaffeineConstants.ACTION_HAS_RESULT).isEqualTo(Boolean.TRUE))
// The cache engine leaves the usernames in the body of the
// The cache engine leaves the users in the body of the
// exchange, that is why we need to set them back in the
// property that the subsequent processors expect to find them.
.setProperty(ExchangeProperty.USERNAMES, body())
.setProperty(ExchangeProperty.USERS, body())
.otherwise()
// Clear all the headers that may come from the previous route.
.removeHeaders("*")
Expand All @@ -266,7 +267,7 @@ public void configureRoute() throws Exception {
// Store all the received recipients in the cache.
.setHeader(CaffeineConstants.ACTION, constant(CaffeineConstants.ACTION_PUT))
.setHeader(CaffeineConstants.KEY, this.computeCacheKey())
.setHeader(CaffeineConstants.VALUE, exchangeProperty(ExchangeProperty.USERNAMES))
.setHeader(CaffeineConstants.VALUE, exchangeProperty(ExchangeProperty.USERS))
.to(caffeineCache(Routes.FETCH_USERS_IT))
.endChoice()
.end()
Expand Down Expand Up @@ -306,13 +307,13 @@ public void configureRoute() throws Exception {
.setHeader(CaffeineConstants.ACTION, constant(CaffeineConstants.ACTION_GET))
.setHeader(CaffeineConstants.KEY, this.computeGroupPrincipalsCacheKey())
.to(caffeineCache(Routes.FETCH_GROUP_USERS))
// Avoid calling RBAC if we do have the usernames cached.
// Avoid calling RBAC if we do have the users cached.
.choice()
.when(header(CaffeineConstants.ACTION_HAS_RESULT).isEqualTo(Boolean.TRUE))
// The cache engine leaves the usernames in the body of the
// The cache engine leaves the users in the body of the
// exchange, that is why we need to set them back in the
// property that the subsequent processors expect to find them.
.setProperty(ExchangeProperty.USERNAMES, body())
.setProperty(ExchangeProperty.USERS, body())
.otherwise()
// Clear all the headers that may come from the previous route.
.removeHeaders("*")
Expand All @@ -328,7 +329,7 @@ public void configureRoute() throws Exception {
// Store all the received recipients in the cache.
.setHeader(CaffeineConstants.ACTION, constant(CaffeineConstants.ACTION_PUT))
.setHeader(CaffeineConstants.KEY, this.computeGroupPrincipalsCacheKey())
.setHeader(CaffeineConstants.VALUE, exchangeProperty(ExchangeProperty.USERNAMES))
.setHeader(CaffeineConstants.VALUE, exchangeProperty(ExchangeProperty.USERS))
.to(caffeineCache(Routes.FETCH_GROUP_USERS))
.endChoice()
.end()
Expand Down Expand Up @@ -366,7 +367,7 @@ public void configureRoute() throws Exception {
.routeId(Routes.SEND_EMAIL_BOP_SINGLE_PER_USER)
// Clear all the headers that may come from the previous route.
.removeHeaders("*")
.split(simpleF("${exchangeProperty.%s}", ExchangeProperty.FILTERED_USERNAMES))
.split(simpleF("${exchangeProperty.%s}", ExchangeProperty.FILTERED_USERS))
.setProperty(ExchangeProperty.SINGLE_EMAIL_PER_USER, constant(true))
.to(direct(Routes.SEND_EMAIL_BOP))
.end();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.redhat.cloud.notifications.connector.email.aggregation;

import com.redhat.cloud.notifications.connector.email.constants.ExchangeProperty;
import com.redhat.cloud.notifications.connector.email.model.settings.User;
import jakarta.enterprise.context.ApplicationScoped;
import org.apache.camel.AggregationStrategy;
import org.apache.camel.Exchange;
Expand All @@ -24,8 +25,8 @@ public Exchange aggregate(final Exchange oldExchange, final Exchange newExchange
return newExchange;
}

final Set<String> oldFilteredUsers = oldExchange.getProperty(ExchangeProperty.FILTERED_USERNAMES, Set.class);
final Set<String> newFilteredUsers = newExchange.getProperty(ExchangeProperty.FILTERED_USERNAMES, Set.class);
final Set<User> oldFilteredUsers = oldExchange.getProperty(ExchangeProperty.FILTERED_USERS, Set.class);
final Set<User> newFilteredUsers = newExchange.getProperty(ExchangeProperty.FILTERED_USERS, Set.class);

if (newFilteredUsers != null) {
oldFilteredUsers.addAll(newFilteredUsers);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public class EmailConnectorConfig extends ConnectorConfig {
private static final String RBAC_APPLICATION_KEY = "notifications.connector.user-provider.rbac.application-key";
private static final String RBAC_ELEMENTS_PAGE = "notifications.connector.user-provider.rbac.elements-per-page";
private static final String RBAC_URL = "notifications.connector.user-provider.rbac.url";
private static final String SKIP_BOP_USERS_RESOLUTION = "notifications.connector.bop.skip-users-resolution";
@Deprecated(forRemoval = true)
public static final String SINGLE_EMAIL_PER_USER = "notifications.connector.single-email-per-user.enabled";

Expand Down Expand Up @@ -97,6 +98,9 @@ public class EmailConnectorConfig extends ConnectorConfig {
@ConfigProperty(name = USER_PROVIDER_CACHE_EXPIRE_AFTER_WRITE, defaultValue = "600")
int userProviderCacheExpireAfterWrite;

@ConfigProperty(name = SKIP_BOP_USERS_RESOLUTION, defaultValue = "false")
boolean skipBopUsersResolution;

@Override
public void log() {
final Map<String, Object> additionalEntries = new HashMap<>();
Expand All @@ -115,6 +119,7 @@ public void log() {
additionalEntries.put(RBAC_URL, this.rbacURL);
additionalEntries.put(SINGLE_EMAIL_PER_USER, this.singleEmailPerUserEnabled);
additionalEntries.put(USER_PROVIDER_CACHE_EXPIRE_AFTER_WRITE, this.userProviderCacheExpireAfterWrite);
additionalEntries.put(SKIP_BOP_USERS_RESOLUTION, skipBopUsersResolution);

log(additionalEntries);
}
Expand Down Expand Up @@ -219,4 +224,12 @@ public boolean isSingleEmailPerUserEnabled() {
public int getUserProviderCacheExpireAfterWrite() {
return userProviderCacheExpireAfterWrite;
}

public boolean isSkipBopUsersResolution() {
return skipBopUsersResolution;
}

public void setSkipBopUsersResolution(boolean skipBopUsersResolution) {
this.skipBopUsersResolution = skipBopUsersResolution;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ public class ExchangeProperty {
*/
public static final String ELEMENTS_COUNT = "elements_count";
/**
* Holds the filtered usernames. It is used in order to avoid the set of
* cached usernames from being modified.
* Holds the filtered users. It is used in order to avoid the set of
* cached users from being modified.
*/
public static final String FILTERED_USERNAMES = "usernames_filtered";
public static final String FILTERED_USERS = "users_filtered";
/**
* Used to hold the received RBAC group's UUID.
*/
Expand Down Expand Up @@ -52,8 +52,7 @@ public class ExchangeProperty {
public static final String RENDERED_SUBJECT = "rendered_subject";
/**
* Represents the curated set of recipients that will end up receiving the
* notification through email. Since only usernames are required in order
* to send the emails, we will only grab those.
* notification through email.
*/
public static final String USERNAMES = "usernames";
public static final String USERS = "users";
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
/**
* Represents the payload to be sent to BOP/MBOP.
*/
@Deprecated(forRemoval = true)
public class Emails {

@JsonProperty("emails")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.redhat.cloud.notifications.connector.email.model.bop;

import com.fasterxml.jackson.annotation.JsonAutoDetect;

import java.util.HashSet;
import java.util.Set;

import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY;

@JsonAutoDetect(fieldVisibility = ANY)
public class SendEmailsRequest {

private final Set<Email> emails = new HashSet<>();
private final boolean skipUsersResolution = true;

public void addEmail(Email email) {
emails.add(email);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.redhat.cloud.notifications.connector.email.model.settings;

import java.util.Objects;

public class User {

private String id;
private String username;
private String email;
private boolean admin;

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public boolean isAdmin() {
return admin;
}

public void setAdmin(boolean admin) {
this.admin = admin;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}

if (!(o instanceof User)) {
return false;
}

User user = (User) o;
return Objects.equals(username, user.username);
}

@Override
public int hashCode() {
return Objects.hash(username);
}
}
Loading
Loading