package com.ctrip.framework.apollo.portal.component.emailbuilder;
import com.google.common.collect.Lists;
import com.ctrip.framework.apollo.common.constants.ReleaseOperation;
import com.ctrip.framework.apollo.common.constants.ReleaseOperationContext;
import com.ctrip.framework.apollo.common.dto.ReleaseDTO;
import com.ctrip.framework.apollo.common.entity.AppNamespace;
import com.ctrip.framework.apollo.core.enums.ConfigFileFormat;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.component.config.PortalConfig;
import com.ctrip.framework.apollo.portal.constant.RoleType;
import com.ctrip.framework.apollo.portal.entity.bo.Email;
import com.ctrip.framework.apollo.portal.entity.bo.ReleaseHistoryBO;
import com.ctrip.framework.apollo.portal.entity.bo.UserInfo;
import com.ctrip.framework.apollo.portal.entity.vo.Change;
import com.ctrip.framework.apollo.portal.entity.vo.ReleaseCompareResult;
import com.ctrip.framework.apollo.portal.service.AppNamespaceService;
import com.ctrip.framework.apollo.portal.service.ReleaseService;
import com.ctrip.framework.apollo.portal.service.RolePermissionService;
import com.ctrip.framework.apollo.portal.spi.UserService;
import com.ctrip.framework.apollo.portal.util.RoleUtils;
import org.apache.commons.lang.time.FastDateFormat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
public abstract class ConfigPublishEmailBuilder {
private static final String EMERGENCY_PUBLISH_TAG = "<span style='color:red'>(紧急发布)</span>";
//email content common field placeholder
private static final String EMAIL_CONTENT_FIELD_APPID = "#\\{appId\\}";
private static final String EMAIL_CONTENT_FIELD_ENV = "#\\{env}";
private static final String EMAIL_CONTENT_FIELD_CLUSTER = "#\\{clusterName}";
private static final String EMAIL_CONTENT_FIELD_NAMESPACE = "#\\{namespaceName}";
private static final String EMAIL_CONTENT_FIELD_OPERATOR = "#\\{operator}";
private static final String EMAIL_CONTENT_FIELD_RELEASE_TIME = "#\\{releaseTime}";
private static final String EMAIL_CONTENT_FIELD_RELEASE_ID = "#\\{releaseId}";
private static final String EMAIL_CONTENT_FIELD_RELEASE_HISTORY_ID = "#\\{releaseHistoryId}";
private static final String EMAIL_CONTENT_FIELD_RELEASE_TITLE = "#\\{releaseTitle}";
private static final String EMAIL_CONTENT_FIELD_RELEASE_COMMENT = "#\\{releaseComment}";
private static final String EMAIL_CONTENT_FIELD_APOLLO_SERVER_ADDRESS = "#\\{apollo.portal.address}";
private static final String EMAIL_CONTENT_FIELD_DIFF_CONTENT = "#\\{diffContent}";
private static final String EMAIL_CONTENT_FIELD_EMERGENCY_PUBLISH = "#\\{emergencyPublish}";
private static final String EMAIL_CONTENT_DIFF_MODULE = "#\\{diffModule}";
protected static final String EMAIL_CONTENT_GRAY_RULES_MODULE = "#\\{rulesModule}";
//email content special field placeholder
protected static final String EMAIL_CONTENT_GRAY_RULES_CONTENT = "#\\{rulesContent}";
//set config's value max length to protect email.
protected static final int VALUE_MAX_LENGTH = 100;
protected FastDateFormat dateFormat = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss");
@Autowired
private RolePermissionService rolePermissionService;
@Autowired
private ReleaseService releaseService;
@Autowired
private AppNamespaceService appNamespaceService;
@Autowired
private UserService userService;
@Autowired
protected PortalConfig portalConfig;
/**
* email subject
*/
protected abstract String subject();
/**
* email body content
*/
protected abstract String emailContent(Env env, ReleaseHistoryBO releaseHistory);
/**
* email body template framework
*/
protected abstract String getTemplateFramework();
/**
* email body diff module template
*/
protected abstract String getDiffModuleTemplate();
public Email build(Env env, ReleaseHistoryBO releaseHistory) {
Email email = new Email();
email.setSubject(subject());
email.setSenderEmailAddress(portalConfig.emailSender());
email.setRecipients(recipients(releaseHistory.getAppId(), releaseHistory.getNamespaceName()));
String emailBody = emailContent(env, releaseHistory);
//clear not used module
emailBody = emailBody.replaceAll(EMAIL_CONTENT_DIFF_MODULE, "");
emailBody = emailBody.replaceAll(EMAIL_CONTENT_GRAY_RULES_MODULE, "");
email.setBody(emailBody);
return email;
}
protected String renderEmailCommonContent(Env env, ReleaseHistoryBO releaseHistory) {
String template = getTemplateFramework();
String renderResult = renderReleaseBasicInfo(template, env, releaseHistory);
renderResult = renderDiffModule(renderResult, env, releaseHistory);
return renderResult;
}
private String renderReleaseBasicInfo(String template, Env env, ReleaseHistoryBO releaseHistory) {
String renderResult = template;
Map<String, Object> operationContext = releaseHistory.getOperationContext();
boolean isEmergencyPublish = operationContext.containsKey(ReleaseOperationContext.IS_EMERGENCY_PUBLISH) &&
(boolean) operationContext.get(ReleaseOperationContext.IS_EMERGENCY_PUBLISH);
if (isEmergencyPublish) {
renderResult = renderResult.replaceAll(EMAIL_CONTENT_FIELD_EMERGENCY_PUBLISH, Matcher.quoteReplacement(EMERGENCY_PUBLISH_TAG));
} else {
renderResult = renderResult.replaceAll(EMAIL_CONTENT_FIELD_EMERGENCY_PUBLISH, "");
}
renderResult = renderResult.replaceAll(EMAIL_CONTENT_FIELD_APPID, Matcher.quoteReplacement(releaseHistory.getAppId()));
renderResult = renderResult.replaceAll(EMAIL_CONTENT_FIELD_ENV, Matcher.quoteReplacement(env.toString()));
renderResult = renderResult.replaceAll(EMAIL_CONTENT_FIELD_CLUSTER, Matcher.quoteReplacement(releaseHistory.getClusterName()));
renderResult = renderResult.replaceAll(EMAIL_CONTENT_FIELD_NAMESPACE, Matcher.quoteReplacement(releaseHistory.getNamespaceName()));
renderResult = renderResult.replaceAll(EMAIL_CONTENT_FIELD_OPERATOR, Matcher.quoteReplacement(releaseHistory.getOperator()));
renderResult = renderResult.replaceAll(EMAIL_CONTENT_FIELD_RELEASE_TITLE, Matcher.quoteReplacement(releaseHistory.getReleaseTitle()));
renderResult =
renderResult.replaceAll(EMAIL_CONTENT_FIELD_RELEASE_ID, String.valueOf(releaseHistory.getReleaseId()));
renderResult =
renderResult.replaceAll(EMAIL_CONTENT_FIELD_RELEASE_HISTORY_ID, String.valueOf(releaseHistory.getId()));
renderResult = renderResult.replaceAll(EMAIL_CONTENT_FIELD_RELEASE_COMMENT, Matcher.quoteReplacement(releaseHistory.getReleaseComment()));
renderResult = renderResult.replaceAll(EMAIL_CONTENT_FIELD_APOLLO_SERVER_ADDRESS, getApolloPortalAddress());
return renderResult
.replaceAll(EMAIL_CONTENT_FIELD_RELEASE_TIME, dateFormat.format(releaseHistory.getReleaseTime()));
}
private String renderDiffModule(String bodyTemplate, Env env, ReleaseHistoryBO releaseHistory) {
String appId = releaseHistory.getAppId();
String namespaceName = releaseHistory.getNamespaceName();
AppNamespace appNamespace = appNamespaceService.findByAppIdAndName(appId, namespaceName);
if (appNamespace == null) {
appNamespace = appNamespaceService.findPublicAppNamespace(namespaceName);
}
//don't show diff content if namespace's format is file
if (appNamespace == null ||
!appNamespace.getFormat().equals(ConfigFileFormat.Properties.getValue())) {
return bodyTemplate.replaceAll(EMAIL_CONTENT_DIFF_MODULE, "<br><h4>变更内容请点击链接到Apollo上查看</h4>");
}
ReleaseCompareResult result = getReleaseCompareResult(env, releaseHistory);
if (!result.hasContent()) {
return bodyTemplate.replaceAll(EMAIL_CONTENT_DIFF_MODULE, "<br><h4>无配置变更</h4>");
}
List<Change> changes = result.getChanges();
StringBuilder changesHtmlBuilder = new StringBuilder();
for (Change change : changes) {
String key = change.getEntity().getFirstEntity().getKey();
String oldValue = change.getEntity().getFirstEntity().getValue();
String newValue = change.getEntity().getSecondEntity().getValue();
newValue = newValue == null ? "" : newValue;
changesHtmlBuilder.append("<tr>");
changesHtmlBuilder.append("<td width=\"10%\">").append(change.getType().toString()).append("</td>");
changesHtmlBuilder.append("<td width=\"20%\">").append(cutOffString(key)).append("</td>");
changesHtmlBuilder.append("<td width=\"35%\">").append(cutOffString(oldValue)).append("</td>");
changesHtmlBuilder.append("<td width=\"35%\">").append(cutOffString(newValue)).append("</td>");
changesHtmlBuilder.append("</tr>");
}
String diffContent = Matcher.quoteReplacement(changesHtmlBuilder.toString());
String diffModuleTemplate = getDiffModuleTemplate();
String diffModuleRenderResult = diffModuleTemplate.replaceAll(EMAIL_CONTENT_FIELD_DIFF_CONTENT, diffContent);
return bodyTemplate.replaceAll(EMAIL_CONTENT_DIFF_MODULE, diffModuleRenderResult);
}
private ReleaseCompareResult getReleaseCompareResult(Env env, ReleaseHistoryBO releaseHistory) {
if (releaseHistory.getOperation() == ReleaseOperation.GRAY_RELEASE
&& releaseHistory.getPreviousReleaseId() == 0) {
ReleaseDTO masterLatestActiveRelease = releaseService.loadLatestRelease(
releaseHistory.getAppId(), env, releaseHistory.getClusterName(), releaseHistory.getNamespaceName());
ReleaseDTO branchLatestActiveRelease = releaseService.findReleaseById(env, releaseHistory.getReleaseId());
return releaseService.compare(masterLatestActiveRelease, branchLatestActiveRelease);
}
return releaseService.compare(env, releaseHistory.getPreviousReleaseId(), releaseHistory.getReleaseId());
}
private List<String> recipients(String appId, String namespaceName) {
Set<UserInfo> modifyRoleUsers =
rolePermissionService
.queryUsersWithRole(RoleUtils.buildNamespaceRoleName(appId, namespaceName, RoleType.MODIFY_NAMESPACE));
Set<UserInfo> releaseRoleUsers =
rolePermissionService
.queryUsersWithRole(RoleUtils.buildNamespaceRoleName(appId, namespaceName, RoleType.RELEASE_NAMESPACE));
Set<UserInfo> owners = rolePermissionService.queryUsersWithRole(RoleUtils.buildAppMasterRoleName(appId));
Set<String> userIds = new HashSet<>(modifyRoleUsers.size() + releaseRoleUsers.size() + owners.size());
for (UserInfo userInfo : modifyRoleUsers) {
userIds.add(userInfo.getUserId());
}
for (UserInfo userInfo : releaseRoleUsers) {
userIds.add(userInfo.getUserId());
}
for (UserInfo userInfo : owners) {
userIds.add(userInfo.getUserId());
}
List<UserInfo> userInfos = userService.findByUserIds(Lists.newArrayList(userIds));
if (CollectionUtils.isEmpty(userInfos)) {
return Collections.emptyList();
}
List<String> recipients = new ArrayList<>(userInfos.size());
for (UserInfo userInfo : userInfos) {
recipients.add(userInfo.getEmail());
}
return recipients;
}
protected String getApolloPortalAddress() {
return portalConfig.portalAddress();
}
private String cutOffString(String source) {
if (source.length() > VALUE_MAX_LENGTH) {
return source.substring(0, VALUE_MAX_LENGTH) + "...";
}
return source;
}
}