[REFACTOR] Improved Structure For Email Module (#17755)

* [fix] code structure

* fix test
This commit is contained in:
Siddhant 2024-09-11 18:23:39 +05:30 committed by GitHub
parent 504b84138b
commit 25175089fc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 232 additions and 200 deletions

View File

@ -31,8 +31,8 @@ import org.openmetadata.service.exception.CatalogExceptionMessage;
import org.openmetadata.service.formatter.decorators.EmailMessageDecorator;
import org.openmetadata.service.formatter.decorators.MessageDecorator;
import org.openmetadata.service.jdbi3.CollectionDAO;
import org.openmetadata.service.util.EmailUtil;
import org.openmetadata.service.util.JsonUtils;
import org.openmetadata.service.util.email.EmailUtil;
@Slf4j
public class EmailPublisher implements Destination<ChangeEvent> {

View File

@ -6,6 +6,7 @@ import static org.openmetadata.service.Entity.TEAM;
import static org.openmetadata.service.apps.scheduler.AppScheduler.APP_NAME;
import static org.openmetadata.service.util.SubscriptionUtil.getAdminsData;
import static org.openmetadata.service.util.Utilities.getMonthAndDateFromEpoch;
import static org.openmetadata.service.util.email.TemplateConstants.DATA_INSIGHT_REPORT_TEMPLATE;
import java.io.IOException;
import java.util.HashMap;
@ -41,10 +42,10 @@ import org.openmetadata.service.jdbi3.KpiRepository;
import org.openmetadata.service.jdbi3.ListFilter;
import org.openmetadata.service.search.SearchClient;
import org.openmetadata.service.search.SearchRepository;
import org.openmetadata.service.util.EmailUtil;
import org.openmetadata.service.util.JsonUtils;
import org.openmetadata.service.util.ResultList;
import org.openmetadata.service.util.Utilities;
import org.openmetadata.service.util.email.EmailUtil;
import org.openmetadata.service.workflows.searchIndex.PaginatedEntitiesSource;
import org.quartz.JobExecutionContext;
@ -142,7 +143,7 @@ public class DataInsightsReportApp extends AbstractNativeApplication {
ownershipTemplate,
tierTemplate,
EmailUtil.getDataInsightReportSubject(),
EmailUtil.DATA_INSIGHT_REPORT_TEMPLATE);
DATA_INSIGHT_REPORT_TEMPLATE);
} catch (Exception ex) {
LOG.error(
"[DataInsightReport] Failed for Team: {}, Reason : {}",
@ -177,7 +178,7 @@ public class DataInsightsReportApp extends AbstractNativeApplication {
ownershipTemplate,
tierTemplate,
EmailUtil.getDataInsightReportSubject(),
EmailUtil.DATA_INSIGHT_REPORT_TEMPLATE);
DATA_INSIGHT_REPORT_TEMPLATE);
} catch (Exception ex) {
LOG.error("[DataInsightReport] Failed for Admin, Reason : {}", ex.getMessage(), ex);
}

View File

@ -14,7 +14,7 @@
package org.openmetadata.service.formatter.decorators;
import static org.openmetadata.common.utils.CommonUtil.nullOrEmpty;
import static org.openmetadata.service.util.EmailUtil.getSmtpSettings;
import static org.openmetadata.service.util.email.EmailUtil.getSmtpSettings;
import java.util.ArrayList;
import java.util.Collections;

View File

@ -14,7 +14,7 @@
package org.openmetadata.service.formatter.decorators;
import static org.openmetadata.common.utils.CommonUtil.nullOrEmpty;
import static org.openmetadata.service.util.EmailUtil.getSmtpSettings;
import static org.openmetadata.service.util.email.EmailUtil.getSmtpSettings;
import java.util.ArrayList;
import java.util.List;

View File

@ -14,7 +14,7 @@
package org.openmetadata.service.formatter.decorators;
import static org.openmetadata.common.utils.CommonUtil.nullOrEmpty;
import static org.openmetadata.service.util.EmailUtil.getSmtpSettings;
import static org.openmetadata.service.util.email.EmailUtil.getSmtpSettings;
import java.util.ArrayList;
import java.util.List;

View File

@ -14,7 +14,7 @@
package org.openmetadata.service.formatter.decorators;
import static org.openmetadata.common.utils.CommonUtil.nullOrEmpty;
import static org.openmetadata.service.util.EmailUtil.getSmtpSettings;
import static org.openmetadata.service.util.email.EmailUtil.getSmtpSettings;
import java.util.ArrayList;
import java.util.List;

View File

@ -30,10 +30,10 @@ import org.openmetadata.service.Entity;
import org.openmetadata.service.exception.EntityNotFoundException;
import org.openmetadata.service.resources.docstore.DocStoreResource;
import org.openmetadata.service.resources.settings.SettingsCache;
import org.openmetadata.service.util.DefaultTemplateProvider;
import org.openmetadata.service.util.EntityUtil.Fields;
import org.openmetadata.service.util.JsonUtils;
import org.openmetadata.service.util.TemplateProvider;
import org.openmetadata.service.util.email.DefaultTemplateProvider;
import org.openmetadata.service.util.email.TemplateProvider;
@Slf4j
public class DocumentRepository extends EntityRepository<Document> {

View File

@ -99,13 +99,8 @@ public class SystemRepository {
if (fetchedSettings == null) {
return null;
}
if (fetchedSettings.getConfigType() == SettingsType.EMAIL_CONFIGURATION) {
SmtpSettings emailConfig = (SmtpSettings) fetchedSettings.getConfigValue();
emailConfig.setPassword("***********");
fetchedSettings.setConfigValue(emailConfig);
}
return fetchedSettings;
return fetchedSettings;
} catch (Exception ex) {
LOG.error("Error while trying fetch Settings ", ex);
}

View File

@ -64,9 +64,9 @@ import org.openmetadata.service.limits.Limits;
import org.openmetadata.service.resources.Collection;
import org.openmetadata.service.resources.EntityResource;
import org.openmetadata.service.security.Authorizer;
import org.openmetadata.service.util.DefaultTemplateProvider;
import org.openmetadata.service.util.JsonUtils;
import org.openmetadata.service.util.ResultList;
import org.openmetadata.service.util.email.DefaultTemplateProvider;
@Slf4j
@Path("/v1/docStore")

View File

@ -46,8 +46,8 @@ import org.openmetadata.service.jdbi3.SystemRepository;
import org.openmetadata.service.resources.Collection;
import org.openmetadata.service.security.Authorizer;
import org.openmetadata.service.security.JwtFilter;
import org.openmetadata.service.util.EmailUtil;
import org.openmetadata.service.util.ResultList;
import org.openmetadata.service.util.email.EmailUtil;
@Path("/v1/system")
@Tag(name = "System", description = "APIs related to System configuration and settings.")

View File

@ -27,12 +27,12 @@ import static org.openmetadata.service.exception.CatalogExceptionMessage.EMAIL_S
import static org.openmetadata.service.jdbi3.UserRepository.AUTH_MECHANISM_FIELD;
import static org.openmetadata.service.secrets.ExternalSecretsManager.NULL_SECRET_STRING;
import static org.openmetadata.service.security.jwt.JWTTokenGenerator.getExpiryDate;
import static org.openmetadata.service.util.EmailUtil.getSmtpSettings;
import static org.openmetadata.service.util.UserUtil.getRoleListFromUser;
import static org.openmetadata.service.util.UserUtil.getRolesFromAuthorizationToken;
import static org.openmetadata.service.util.UserUtil.getUser;
import static org.openmetadata.service.util.UserUtil.reSyncUserRolesFromToken;
import static org.openmetadata.service.util.UserUtil.validateAndGetRolesRef;
import static org.openmetadata.service.util.email.EmailUtil.getSmtpSettings;
import at.favre.lib.crypto.bcrypt.BCrypt;
import freemarker.template.TemplateException;
@ -146,7 +146,6 @@ import org.openmetadata.service.security.mask.PIIMasker;
import org.openmetadata.service.security.policyevaluator.OperationContext;
import org.openmetadata.service.security.policyevaluator.ResourceContext;
import org.openmetadata.service.security.saml.JwtTokenCacheManager;
import org.openmetadata.service.util.EmailUtil;
import org.openmetadata.service.util.EntityUtil;
import org.openmetadata.service.util.EntityUtil.Fields;
import org.openmetadata.service.util.JsonUtils;
@ -154,6 +153,8 @@ import org.openmetadata.service.util.PasswordUtil;
import org.openmetadata.service.util.RestUtil.PutResponse;
import org.openmetadata.service.util.ResultList;
import org.openmetadata.service.util.TokenUtil;
import org.openmetadata.service.util.email.EmailUtil;
import org.openmetadata.service.util.email.TemplateConstants;
@Slf4j
@Path("/v1/users")
@ -644,7 +645,7 @@ public class UserResource extends EntityResource<User, UserRepository> {
authHandler.sendInviteMailToUser(
uriInfo,
user,
String.format("Welcome to %s", EmailUtil.getEmailingEntity()),
String.format("Welcome to %s", EmailUtil.getSmtpSettings().getEmailingEntity()),
create.getCreatePasswordType(),
create.getPassword());
} catch (Exception ex) {
@ -1074,7 +1075,7 @@ public class UserResource extends EntityResource<User, UserRepository> {
uriInfo,
registeredUser,
EmailUtil.getPasswordResetSubject(),
EmailUtil.PASSWORD_RESET_TEMPLATE_FILE);
TemplateConstants.RESET_LINK_TEMPLATE);
} catch (Exception ex) {
LOG.error("Error in sending mail for reset password" + ex.getMessage());
return Response.status(424).entity(new ErrorMessage(424, EMAIL_SENDING_ISSUE)).build();

View File

@ -37,9 +37,17 @@ import static org.openmetadata.service.exception.CatalogExceptionMessage.SELF_SI
import static org.openmetadata.service.exception.CatalogExceptionMessage.TOKEN_EXPIRED;
import static org.openmetadata.service.exception.CatalogExceptionMessage.TOKEN_EXPIRY_ERROR;
import static org.openmetadata.service.resources.teams.UserResource.USER_PROTECTED_FIELDS;
import static org.openmetadata.service.util.EmailUtil.getSmtpSettings;
import static org.openmetadata.service.util.UserUtil.getRoleListFromUser;
import static org.openmetadata.service.util.UserUtil.getUser;
import static org.openmetadata.service.util.email.EmailUtil.getSmtpSettings;
import static org.openmetadata.service.util.email.EmailUtil.sendAccountStatus;
import static org.openmetadata.service.util.email.TemplateConstants.APPLICATION_LOGIN_LINK;
import static org.openmetadata.service.util.email.TemplateConstants.ENTITY;
import static org.openmetadata.service.util.email.TemplateConstants.INVITE_CREATE_PASSWORD_TEMPLATE;
import static org.openmetadata.service.util.email.TemplateConstants.INVITE_RANDOM_PASSWORD_TEMPLATE;
import static org.openmetadata.service.util.email.TemplateConstants.PASSWORD;
import static org.openmetadata.service.util.email.TemplateConstants.SUPPORT_URL;
import static org.openmetadata.service.util.email.TemplateConstants.USERNAME;
import at.favre.lib.crypto.bcrypt.BCrypt;
import freemarker.template.TemplateException;
@ -81,12 +89,12 @@ import org.openmetadata.service.jdbi3.UserRepository;
import org.openmetadata.service.security.AuthenticationException;
import org.openmetadata.service.security.SecurityUtil;
import org.openmetadata.service.security.jwt.JWTTokenGenerator;
import org.openmetadata.service.util.EmailUtil;
import org.openmetadata.service.util.EntityUtil;
import org.openmetadata.service.util.JsonUtils;
import org.openmetadata.service.util.PasswordUtil;
import org.openmetadata.service.util.RestUtil.PutResponse;
import org.openmetadata.service.util.TokenUtil;
import org.openmetadata.service.util.email.EmailUtil;
@Slf4j
public class BasicAuthenticator implements AuthenticatorHandler {
@ -253,7 +261,7 @@ public class BasicAuthenticator implements AuthenticatorHandler {
// Update user about Password Change
try {
EmailUtil.sendAccountStatus(storedUser, "Update Password", "Change Successful");
sendAccountStatus(storedUser, "Update Password", "Change Successful");
} catch (TemplateException ex) {
LOG.error("Error in sending Password Change Mail to User. Reason : " + ex.getMessage(), ex);
throw new CustomExceptionMessage(424, FAILED_SEND_EMAIL, EMAIL_SENDING_ISSUE);
@ -311,7 +319,7 @@ public class BasicAuthenticator implements AuthenticatorHandler {
sendInviteMailToUser(
uriInfo,
response.getEntity(),
String.format("%s: Password Update", EmailUtil.getEmailingEntity()),
String.format("%s: Password Update", getSmtpSettings().getEmailingEntity()),
ADMIN_CREATE,
request.getNewPassword());
}
@ -328,14 +336,14 @@ public class BasicAuthenticator implements AuthenticatorHandler {
switch (requestType) {
case ADMIN_CREATE -> {
Map<String, Object> templatePopulator = new HashMap<>();
templatePopulator.put(EmailUtil.ENTITY, EmailUtil.getEmailingEntity());
templatePopulator.put(EmailUtil.SUPPORT_URL, EmailUtil.getSupportUrl());
templatePopulator.put(EmailUtil.USERNAME, user.getName());
templatePopulator.put(EmailUtil.PASSWORD, pwd);
templatePopulator.put(EmailUtil.APPLICATION_LOGIN_LINK, EmailUtil.getOMUrl());
templatePopulator.put(ENTITY, getSmtpSettings().getEmailingEntity());
templatePopulator.put(SUPPORT_URL, getSmtpSettings().getSupportUrl());
templatePopulator.put(USERNAME, user.getName());
templatePopulator.put(PASSWORD, pwd);
templatePopulator.put(APPLICATION_LOGIN_LINK, getSmtpSettings().getOpenMetadataUrl());
try {
EmailUtil.sendMail(
subject, templatePopulator, user.getEmail(), EmailUtil.INVITE_RANDOM_PWD, true);
subject, templatePopulator, user.getEmail(), INVITE_RANDOM_PASSWORD_TEMPLATE, true);
} catch (TemplateException ex) {
LOG.error(
"Failed in sending Mail to user [{}]. Reason : {}",
@ -345,7 +353,7 @@ public class BasicAuthenticator implements AuthenticatorHandler {
}
}
case USER_CREATE -> sendPasswordResetLink(
uriInfo, user, subject, EmailUtil.INVITE_CREATE_PWD);
uriInfo, user, subject, INVITE_CREATE_PASSWORD_TEMPLATE);
default -> LOG.error("Invalid Password Create Type");
}
}
@ -478,7 +486,7 @@ public class BasicAuthenticator implements AuthenticatorHandler {
loginAttemptCache.recordFailedLogin(providedIdentity);
int failedLoginAttempt = loginAttemptCache.getUserFailedLoginCount(providedIdentity);
if (failedLoginAttempt == SecurityUtil.getLoginConfiguration().getMaxLoginFailAttempts()) {
EmailUtil.sendAccountStatus(
sendAccountStatus(
storedUser,
"Multiple Failed Login Attempts.",
String.format(

View File

@ -68,11 +68,11 @@ import org.openmetadata.service.jdbi3.UserRepository;
import org.openmetadata.service.security.AuthenticationException;
import org.openmetadata.service.security.SecurityUtil;
import org.openmetadata.service.security.jwt.JWTTokenGenerator;
import org.openmetadata.service.util.EmailUtil;
import org.openmetadata.service.util.JsonUtils;
import org.openmetadata.service.util.LdapUtil;
import org.openmetadata.service.util.TokenUtil;
import org.openmetadata.service.util.UserUtil;
import org.openmetadata.service.util.email.EmailUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;

View File

@ -51,6 +51,7 @@ import org.openmetadata.service.security.auth.CatalogSecurityContext;
import org.openmetadata.service.security.jwt.JWTTokenGenerator;
import org.openmetadata.service.util.EntityUtil.Fields;
import org.openmetadata.service.util.RestUtil.PutResponse;
import org.openmetadata.service.util.email.EmailUtil;
@Slf4j
public final class UserUtil {

View File

@ -1,12 +1,12 @@
package org.openmetadata.service.util;
package org.openmetadata.service.util.email;
import static org.openmetadata.common.utils.CommonUtil.nullOrEmpty;
import freemarker.template.Configuration;
import freemarker.template.Template;
import java.io.IOException;
import java.io.StringReader;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -15,7 +15,6 @@ import lombok.extern.slf4j.Slf4j;
import org.openmetadata.schema.email.EmailTemplate;
import org.openmetadata.schema.email.EmailTemplatePlaceholder;
import org.openmetadata.schema.email.TemplateValidationResponse;
import org.openmetadata.schema.entities.docStore.Document;
import org.openmetadata.service.Entity;
import org.openmetadata.service.jdbi3.DocumentRepository;
@ -32,7 +31,7 @@ public class DefaultTemplateProvider implements TemplateProvider {
public Template getTemplate(String templateName) throws IOException {
EmailTemplate emailTemplate = documentRepository.fetchEmailTemplateByName(templateName);
String template = emailTemplate.getTemplate();
if (template == null || template.isEmpty()) {
if (nullOrEmpty(template)) {
throw new IOException("Template content not found for template: " + templateName);
}
@ -40,33 +39,6 @@ public class DefaultTemplateProvider implements TemplateProvider {
templateName, new StringReader(template), new Configuration(Configuration.VERSION_2_3_31));
}
public Map<String, Set<EmailTemplatePlaceholder>> getDocumentPlaceHolders() {
List<Document> documents = documentRepository.fetchAllEmailTemplates();
return documents.stream()
.collect(
Collectors.toMap(
Document::getName,
document -> {
EmailTemplate emailTemplate =
JsonUtils.convertValue(document.getData(), EmailTemplate.class);
return emailTemplate.getPlaceHolders();
}));
}
public Map<String, Set<String>> getPlaceholdersFromTemplate() {
List<Document> listOfDocuments = documentRepository.fetchAllEmailTemplates();
return listOfDocuments.stream()
.collect(
Collectors.toMap(
Document::getName,
document ->
extractPlaceholders(
JsonUtils.convertValue(document.getData(), EmailTemplate.class)
.getTemplate())));
}
@Override
public TemplateValidationResponse validateEmailTemplate(String docName, String actualContent) {
Set<String> expectedPlaceholders =

View File

@ -11,8 +11,32 @@
* limitations under the License.
*/
package org.openmetadata.service.util;
package org.openmetadata.service.util.email;
import static org.openmetadata.service.util.email.TemplateConstants.ACCOUNT_ACTIVITY_CHANGE_TEMPLATE;
import static org.openmetadata.service.util.email.TemplateConstants.ACCOUNT_STATUS_SUBJECT;
import static org.openmetadata.service.util.email.TemplateConstants.ACTION_KEY;
import static org.openmetadata.service.util.email.TemplateConstants.ACTION_STATUS_KEY;
import static org.openmetadata.service.util.email.TemplateConstants.APPLICATION_LOGIN_LINK;
import static org.openmetadata.service.util.email.TemplateConstants.CHANGE_EVENT_TEMPLATE;
import static org.openmetadata.service.util.email.TemplateConstants.CHANGE_EVENT_UPDATE;
import static org.openmetadata.service.util.email.TemplateConstants.DEFAULT_EXPIRATION_TIME;
import static org.openmetadata.service.util.email.TemplateConstants.EMAIL_IGNORE_MSG;
import static org.openmetadata.service.util.email.TemplateConstants.EMAIL_VERIFICATION_LINKKEY;
import static org.openmetadata.service.util.email.TemplateConstants.EMAIL_VERIFICATION_SUBJECT;
import static org.openmetadata.service.util.email.TemplateConstants.EMAIL_VERIFICATION_TEMPLATE;
import static org.openmetadata.service.util.email.TemplateConstants.ENTITY;
import static org.openmetadata.service.util.email.TemplateConstants.EXPIRATION_TIME_KEY;
import static org.openmetadata.service.util.email.TemplateConstants.INVITE_RANDOM_PASSWORD_TEMPLATE;
import static org.openmetadata.service.util.email.TemplateConstants.INVITE_SUBJECT;
import static org.openmetadata.service.util.email.TemplateConstants.PASSWORD;
import static org.openmetadata.service.util.email.TemplateConstants.PASSWORD_RESET_SUBJECT;
import static org.openmetadata.service.util.email.TemplateConstants.REPORT_SUBJECT;
import static org.openmetadata.service.util.email.TemplateConstants.SUPPORT_URL;
import static org.openmetadata.service.util.email.TemplateConstants.TASK_SUBJECT;
import static org.openmetadata.service.util.email.TemplateConstants.TEST_EMAIL_SUBJECT;
import static org.openmetadata.service.util.email.TemplateConstants.TEST_MAIL_TEMPLATE;
import static org.openmetadata.service.util.email.TemplateConstants.USERNAME;
import static org.simplejavamail.api.mailer.config.TransportStrategy.SMTP;
import static org.simplejavamail.api.mailer.config.TransportStrategy.SMTPS;
import static org.simplejavamail.api.mailer.config.TransportStrategy.SMTP_TLS;
@ -22,6 +46,7 @@ import freemarker.template.TemplateException;
import java.io.IOException;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@ -46,46 +71,8 @@ import org.simplejavamail.mailer.MailerBuilder;
@Slf4j
public class EmailUtil {
public static final String USERNAME = "userName";
public static final String ENTITY = "entity";
public static final String SUPPORT_URL = "supportUrl";
// Email Verification
private static final String EMAIL_VERIFICATION_SUBJECT =
"%s: Verify your Email Address (Action Required)";
public static final String EMAIL_VERIFICATION_LINKKEY = "userEmailTokenVerificationLink";
public static final String EMAIL_VERIFICATION_TEMPLATE = "email-verification";
// Password Reset Link
private static final String PASSWORD_RESET_SUBJECT = "%s: Reset your Password";
public static final String PASSWORD_RESET_LINKKEY = "userResetPasswordLink";
public static final String EXPIRATION_TIME_KEY = "expirationTime";
public static final String DEFAULT_EXPIRATION_TIME = "60";
public static final String PASSWORD = "password";
public static final String APPLICATION_LOGIN_LINK = "applicationLoginLink";
public static final String PASSWORD_RESET_TEMPLATE_FILE = "reset-link";
// Account Change Status
private static final String ACCOUNT_STATUS_SUBJECT = "%s: Change in Account Status";
public static final String ACTION_KEY = "action";
public static final String ACTION_STATUS_KEY = "actionStatus";
public static final String ACCOUNT_STATUS_TEMPLATE_FILE = "account-activity-change";
private static final String INVITE_SUBJECT = "Welcome to %s";
private static final String CHANGE_EVENT_UPDATE = "[%s] - Change Event Update from %s";
private static final String TASK_SUBJECT = "%s : Task Assignment Notification";
public static final String INVITE_RANDOM_PWD = "invite-randompwd";
public static final String CHANGE_EVENT_TEMPLATE = "changeEvent";
public static final String INVITE_CREATE_PWD = "invite-createPassword";
public static final String TASK_NOTIFICATION_TEMPLATE = "taskAssignment";
private static final String REPORT_SUBJECT = "%s: Data Insights Weekly - %s";
public static final String DATA_INSIGHT_REPORT_TEMPLATE = "dataInsightReport";
public static final String TEST_EMAIL_TEMPLATE = "testMail";
public static final String TEST_EMAIL_SUBJECT = "%s : Test Email";
private static SmtpSettings storedSmtpSettings;
private static Mailer mailer;
private static final String EMAIL_IGNORE_MSG =
"Email was not sent to {} as SMTP setting is not enabled";
private static SmtpSettings storedSmtpSettings;
private static TemplateProvider templateProvider;
static {
@ -93,7 +80,6 @@ public class EmailUtil {
initializeTemplateProvider();
}
// initialize template provider
private static void initializeTemplateProvider() {
templateProvider = new DefaultTemplateProvider();
}
@ -102,7 +88,7 @@ public class EmailUtil {
try {
getSmtpSettings();
initializeTemplateProvider();
LOG.info("Email Util cache is initialized");
LOG.info("Email Util Cache is initialized");
} catch (Exception ex) {
LOG.warn("[MAILER] Smtp Configurations are missing : Reason {} ", ex.getMessage(), ex);
}
@ -137,18 +123,22 @@ public class EmailUtil {
public static void sendAccountStatus(User user, String action, String status)
throws IOException, TemplateException {
if (Boolean.TRUE.equals(getSmtpSettings().getEnableSmtpServer())) {
Map<String, Object> templatePopulator = new HashMap<>();
templatePopulator.put(ENTITY, getEmailingEntity());
templatePopulator.put(SUPPORT_URL, getSupportUrl());
templatePopulator.put(USERNAME, user.getName());
templatePopulator.put(ACTION_KEY, action);
templatePopulator.put(ACTION_STATUS_KEY, status);
Map<String, Object> templatePopulator =
new TemplatePopulatorBuilder()
.add(ENTITY, getSmtpSettings().getEmailingEntity())
.add(SUPPORT_URL, getSmtpSettings().getSupportUrl())
.add(USERNAME, user.getName())
.add(ACTION_KEY, action)
.add(ACTION_STATUS_KEY, status)
.build();
sendMail(
getAccountStatusChangeSubject(),
templatePopulator,
user.getEmail(),
ACCOUNT_STATUS_TEMPLATE_FILE,
ACCOUNT_ACTIVITY_CHANGE_TEMPLATE,
true);
} else {
LOG.warn(EMAIL_IGNORE_MSG, user.getEmail());
@ -158,12 +148,16 @@ public class EmailUtil {
public static void sendEmailVerification(String emailVerificationLink, User user)
throws IOException, TemplateException {
if (Boolean.TRUE.equals(getSmtpSettings().getEnableSmtpServer())) {
Map<String, Object> templatePopulator = new HashMap<>();
templatePopulator.put(ENTITY, getEmailingEntity());
templatePopulator.put(SUPPORT_URL, getSupportUrl());
templatePopulator.put(USERNAME, user.getName());
templatePopulator.put(EMAIL_VERIFICATION_LINKKEY, emailVerificationLink);
templatePopulator.put(EXPIRATION_TIME_KEY, "24");
Map<String, Object> templatePopulator =
new TemplatePopulatorBuilder()
.add(ENTITY, getSmtpSettings().getEmailingEntity())
.add(SUPPORT_URL, getSmtpSettings().getSupportUrl())
.add(USERNAME, user.getName())
.add(EMAIL_VERIFICATION_LINKKEY, emailVerificationLink)
.add(EXPIRATION_TIME_KEY, "24")
.build();
sendMail(
getEmailVerificationSubject(),
templatePopulator,
@ -179,12 +173,15 @@ public class EmailUtil {
String passwordResetLink, User user, String subject, String templateFilePath)
throws IOException, TemplateException {
if (Boolean.TRUE.equals(getSmtpSettings().getEnableSmtpServer())) {
Map<String, Object> templatePopulator = new HashMap<>();
templatePopulator.put(ENTITY, getEmailingEntity());
templatePopulator.put(SUPPORT_URL, getSupportUrl());
templatePopulator.put(USERNAME, user.getName());
templatePopulator.put(PASSWORD_RESET_LINKKEY, passwordResetLink);
templatePopulator.put(EXPIRATION_TIME_KEY, DEFAULT_EXPIRATION_TIME);
Map<String, Object> templatePopulator =
new TemplatePopulatorBuilder()
.add(ENTITY, getSmtpSettings().getEmailingEntity())
.add(SUPPORT_URL, getSmtpSettings().getSupportUrl())
.add(USERNAME, user.getName())
.add(EMAIL_VERIFICATION_LINKKEY, passwordResetLink)
.add(EXPIRATION_TIME_KEY, DEFAULT_EXPIRATION_TIME)
.build();
sendMail(subject, templatePopulator, user.getEmail(), templateFilePath, true);
} else {
@ -201,15 +198,18 @@ public class EmailUtil {
String templateFilePath)
throws IOException, TemplateException {
if (Boolean.TRUE.equals(getSmtpSettings().getEnableSmtpServer())) {
Map<String, Object> templatePopulator = new HashMap<>();
templatePopulator.put("assignee", assigneeName);
templatePopulator.put("createdBy", thread.getCreatedBy());
templatePopulator.put("taskName", thread.getMessage());
templatePopulator.put("taskStatus", thread.getTask().getStatus().toString());
templatePopulator.put("taskType", thread.getTask().getType().toString());
templatePopulator.put("fieldOldValue", thread.getTask().getOldValue());
templatePopulator.put("fieldNewValue", thread.getTask().getSuggestion());
templatePopulator.put("taskLink", taskLink);
Map<String, Object> templatePopulator =
new TemplatePopulatorBuilder()
.add("assignee", assigneeName)
.add("createdBy", thread.getCreatedBy())
.add("taskName", thread.getMessage())
.add("taskStatus", thread.getTask().getStatus().toString())
.add("taskType", thread.getTask().getType().toString())
.add("fieldOldValue", thread.getTask().getOldValue())
.add("fieldNewValue", thread.getTask().getSuggestion())
.add("taskLink", taskLink)
.build();
sendMail(subject, templatePopulator, email, templateFilePath, true);
} else {
@ -267,20 +267,24 @@ public class EmailUtil {
}
}
public static void sendInviteMailToAdmin(User user, String pwd) {
public static void sendInviteMailToAdmin(User user, String password) {
if (Boolean.TRUE.equals(getSmtpSettings().getEnableSmtpServer())) {
Map<String, Object> templatePopulator = new HashMap<>();
templatePopulator.put(EmailUtil.ENTITY, EmailUtil.getEmailingEntity());
templatePopulator.put(EmailUtil.SUPPORT_URL, EmailUtil.getSupportUrl());
templatePopulator.put(EmailUtil.USERNAME, user.getName());
templatePopulator.put(EmailUtil.PASSWORD, pwd);
templatePopulator.put(EmailUtil.APPLICATION_LOGIN_LINK, EmailUtil.getOMUrl());
Map<String, Object> templatePopulator =
new TemplatePopulatorBuilder()
.add(ENTITY, getSmtpSettings().getEmailingEntity())
.add(SUPPORT_URL, getSmtpSettings().getSupportUrl())
.add(USERNAME, user.getName())
.add(PASSWORD, password)
.add(APPLICATION_LOGIN_LINK, getSmtpSettings().getOpenMetadataUrl())
.build();
try {
EmailUtil.sendMail(
EmailUtil.getEmailInviteSubject(),
templatePopulator,
user.getEmail(),
EmailUtil.INVITE_RANDOM_PWD,
INVITE_RANDOM_PASSWORD_TEMPLATE,
true);
} catch (Exception ex) {
LOG.error(
@ -294,22 +298,27 @@ public class EmailUtil {
public static void sendChangeEventMail(
String publisherName, String receiverMail, EmailMessage emailMessaged) {
if (Boolean.TRUE.equals(getSmtpSettings().getEnableSmtpServer())) {
Map<String, Object> templatePopulator = new HashMap<>();
templatePopulator.put(EmailUtil.USERNAME, receiverMail.split("@")[0]);
templatePopulator.put("updatedBy", emailMessaged.getUpdatedBy());
templatePopulator.put("entityUrl", emailMessaged.getEntityUrl());
StringBuilder buff = new StringBuilder();
for (String cmessage : emailMessaged.getChangeMessage()) {
buff.append(cmessage);
buff.append("\n");
}
templatePopulator.put("changeMessage", buff.toString());
Map<String, Object> templatePopulator =
new TemplatePopulatorBuilder()
.add(USERNAME, receiverMail.split("@")[0])
.add("updatedBy", emailMessaged.getUpdatedBy())
.add("entityUrl", emailMessaged.getEntityUrl())
.add("changeMessage", buff.toString())
.build();
try {
EmailUtil.sendMail(
EmailUtil.getChangeEventTemplate(publisherName),
templatePopulator,
receiverMail,
EmailUtil.CHANGE_EVENT_TEMPLATE,
CHANGE_EVENT_TEMPLATE,
true);
} catch (Exception ex) {
LOG.error(
@ -332,16 +341,21 @@ public class EmailUtil {
String templateFilePath)
throws IOException, TemplateException {
if (Boolean.TRUE.equals(getSmtpSettings().getEnableSmtpServer())) {
Map<String, Object> templatePopulator = new HashMap<>();
templatePopulator.put("startDate", startDate);
templatePopulator.put("endDate", endDate);
templatePopulator.put("totalAssetObj", totalAssetObj);
templatePopulator.put("descriptionObj", descriptionObj);
templatePopulator.put("ownershipObj", ownerShipObj);
templatePopulator.put("tierObj", tierObj);
templatePopulator.put(
"viewReportUrl",
String.format("%s/data-insights/data-assets", getSmtpSettings().getOpenMetadataUrl()));
Map<String, Object> templatePopulator =
new TemplatePopulatorBuilder()
.add("startDate", startDate)
.add("endDate", endDate)
.add("totalAssetObj", totalAssetObj)
.add("descriptionObj", descriptionObj)
.add("ownershipObj", ownerShipObj)
.add("tierObj", tierObj)
.add(
"viewReportUrl",
String.format(
"%s/data-insights/data-assets", getSmtpSettings().getOpenMetadataUrl()))
.build();
sendMailToMultiple(subject, templatePopulator, emails, templateFilePath);
} else {
LOG.warn(EMAIL_IGNORE_MSG, emails.toString());
@ -351,11 +365,15 @@ public class EmailUtil {
public static void sendTestEmail(String email, boolean async)
throws IOException, TemplateException {
if (Boolean.TRUE.equals(getSmtpSettings().getEnableSmtpServer())) {
Map<String, Object> templatePopulator = new HashMap<>();
templatePopulator.put("userName", email.split("@")[0]);
templatePopulator.put("entity", getSmtpSettings().getEmailingEntity());
templatePopulator.put("supportUrl", getSmtpSettings().getSupportUrl());
sendMail(getTestEmailSubject(), templatePopulator, email, TEST_EMAIL_TEMPLATE, async);
Map<String, Object> templatePopulator =
new TemplatePopulatorBuilder()
.add("userName", email.split("@")[0])
.add("entity", getSmtpSettings().getEmailingEntity())
.add("supportUrl", getSmtpSettings().getSupportUrl())
.build();
sendMail(getTestEmailSubject(), templatePopulator, email, TEST_MAIL_TEMPLATE, async);
} else {
LOG.warn(EMAIL_IGNORE_MSG, email);
}
@ -404,18 +422,6 @@ public class EmailUtil {
new SimpleDateFormat("dd-MM-yy").format(new Date()));
}
public static String getEmailingEntity() {
return getSmtpSettings().getEmailingEntity();
}
public static String getSupportUrl() {
return getSmtpSettings().getSupportUrl();
}
public static String getOMUrl() {
return getSmtpSettings().getOpenMetadataUrl();
}
public static SmtpSettings getSmtpSettings() {
SmtpSettings emailConfig =
SettingsCache.getSetting(SettingsType.EMAIL_CONFIGURATION, SmtpSettings.class);
@ -426,16 +432,27 @@ public class EmailUtil {
return emailConfig;
}
/**
* Check if given email address is valid
*
* @param email email address
* @return true if valid, false otherwise
*/
public static Boolean isValidEmail(String email) {
if (StringUtils.isBlank(email)) {
return false;
}
return email.matches("^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$");
}
static class TemplatePopulatorBuilder {
private final Map<String, Object> templatePopulator;
public TemplatePopulatorBuilder() {
this.templatePopulator = new HashMap<>();
}
public TemplatePopulatorBuilder add(String key, Object value) {
templatePopulator.put(key, value);
return this;
}
public Map<String, Object> build() {
return Collections.unmodifiableMap(templatePopulator);
}
}
}

View File

@ -0,0 +1,46 @@
package org.openmetadata.service.util.email;
public class TemplateConstants {
public static final String USERNAME = "userName";
public static final String ENTITY = "entity";
public static final String SUPPORT_URL = "supportUrl";
// templates
public static final String ACCOUNT_ACTIVITY_CHANGE_TEMPLATE = "account-activity-change";
public static final String CHANGE_EVENT_TEMPLATE = "changeEvent";
public static final String DATA_INSIGHT_REPORT_TEMPLATE = "dataInsightReport";
public static final String EMAIL_VERIFICATION_TEMPLATE = "email-verification";
public static final String INVITE_CREATE_PASSWORD_TEMPLATE = "invite-createPassword";
public static final String INVITE_RANDOM_PASSWORD_TEMPLATE = "invite-randompwd";
public static final String RESET_LINK_TEMPLATE = "reset-link";
public static final String TASK_ASSIGNMENT_TEMPLATE = "taskAssignment";
public static final String TEST_MAIL_TEMPLATE = "testMail";
public static final String TEST_RESULT_STATUS_TEMPLATE = "testResultStatusTemplate";
// Email Verification
public static final String EMAIL_VERIFICATION_SUBJECT =
"%s: Verify your Email Address (Action Required)";
public static final String EMAIL_VERIFICATION_LINKKEY = "userEmailTokenVerificationLink";
// Password Reset Link
public static final String PASSWORD_RESET_SUBJECT = "%s: Reset your Password";
public static final String PASSWORD_RESET_LINKKEY = "userResetPasswordLink";
public static final String EXPIRATION_TIME_KEY = "expirationTime";
public static final String DEFAULT_EXPIRATION_TIME = "60";
public static final String PASSWORD = "password";
public static final String APPLICATION_LOGIN_LINK = "applicationLoginLink";
// Account Change Status
public static final String ACCOUNT_STATUS_SUBJECT = "%s: Change in Account Status";
public static final String ACTION_KEY = "action";
public static final String ACTION_STATUS_KEY = "actionStatus";
public static final String INVITE_SUBJECT = "Welcome to %s";
public static final String CHANGE_EVENT_UPDATE = "[%s] - Change Event Update from %s";
public static final String TASK_SUBJECT = "%s : Task Assignment Notification";
public static final String REPORT_SUBJECT = "%s: Data Insights Weekly - %s";
public static final String TEST_EMAIL_SUBJECT = "%s : Test Email";
public static final String EMAIL_IGNORE_MSG =
"Email was not sent to {} as SMTP setting is not enabled";
}

View File

@ -1,10 +1,7 @@
package org.openmetadata.service.util;
package org.openmetadata.service.util.email;
import freemarker.template.Template;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import org.openmetadata.schema.email.EmailTemplatePlaceholder;
import org.openmetadata.schema.email.TemplateValidationResponse;
public interface TemplateProvider {
@ -18,10 +15,4 @@ public interface TemplateProvider {
* - "missingParameters" (List<String>): If validation fails, lists the placeholders that are missing.
*/
TemplateValidationResponse validateEmailTemplate(String docName, String actualContent);
/**
* Maps each template's name to a list of
* {@link EmailTemplatePlaceholder}s extracted from the template data.
*/
Map<String, Set<EmailTemplatePlaceholder>> getDocumentPlaceHolders();
}

View File

@ -8,7 +8,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.openmetadata.service.Entity.DOCUMENT;
import static org.openmetadata.service.Entity.PERSONA;
import static org.openmetadata.service.exception.CatalogExceptionMessage.permissionNotAllowed;
import static org.openmetadata.service.util.EmailUtil.EMAIL_VERIFICATION_TEMPLATE;
import static org.openmetadata.service.util.EntityUtil.fieldUpdated;
import static org.openmetadata.service.util.TestUtils.ADMIN_AUTH_HEADERS;
import static org.openmetadata.service.util.TestUtils.TEST_AUTH_HEADERS;
@ -17,6 +16,7 @@ import static org.openmetadata.service.util.TestUtils.UpdateType.MINOR_UPDATE;
import static org.openmetadata.service.util.TestUtils.assertListNotNull;
import static org.openmetadata.service.util.TestUtils.assertResponse;
import static org.openmetadata.service.util.TestUtils.put;
import static org.openmetadata.service.util.email.TemplateConstants.EMAIL_VERIFICATION_TEMPLATE;
import java.io.IOException;
import java.util.ArrayList;

View File

@ -1,6 +1,6 @@
package org.openmetadata.service.resources.events;
import static org.openmetadata.service.util.EmailUtil.getSmtpSettings;
import static org.openmetadata.service.util.email.EmailUtil.getSmtpSettings;
import javax.ws.rs.Consumes;
import javax.ws.rs.Path;

View File

@ -1,7 +1,7 @@
package org.openmetadata.service.resources.events;
import static org.openmetadata.common.utils.CommonUtil.nullOrEmpty;
import static org.openmetadata.service.util.EmailUtil.getSmtpSettings;
import static org.openmetadata.service.util.email.EmailUtil.getSmtpSettings;
import javax.ws.rs.Consumes;
import javax.ws.rs.Path;

View File

@ -183,10 +183,10 @@ public class SystemResourceTest extends OpenMetadataApplicationTest {
// Test Email Config
Settings emailSettings = getSystemConfig(SettingsType.EMAIL_CONFIGURATION);
SmtpSettings smtp = JsonUtils.convertValue(emailSettings.getConfigValue(), SmtpSettings.class);
// Password for Email is always sent in hidden
// Password for Email is encrypted using fernet
SmtpSettings expected = config.getSmtpSettings();
expected.setPassword("***********");
assertEquals(expected, smtp);
expected.setPassword(smtp.getPassword());
assertEquals(config.getSmtpSettings(), smtp);
// Test Custom Ui Theme Preference Config
Settings uiThemeConfigWrapped = getSystemConfig(SettingsType.CUSTOM_UI_THEME_PREFERENCE);