package com.ctrip.framework.apollo.portal.service; import com.ctrip.framework.apollo.common.dto.ItemChangeSets; import com.ctrip.framework.apollo.common.dto.ItemDTO; import com.ctrip.framework.apollo.common.dto.NamespaceDTO; import com.ctrip.framework.apollo.common.exception.BadRequestException; import com.ctrip.framework.apollo.common.utils.BeanUtils; import com.ctrip.framework.apollo.core.enums.ConfigFileFormat; import com.ctrip.framework.apollo.core.enums.Env; import com.ctrip.framework.apollo.core.utils.StringUtils; import com.ctrip.framework.apollo.portal.api.AdminServiceAPI; import com.ctrip.framework.apollo.portal.component.txtresolver.ConfigTextResolver; import com.ctrip.framework.apollo.portal.constant.CatEventType; import com.ctrip.framework.apollo.portal.entity.model.NamespaceTextModel; import com.ctrip.framework.apollo.portal.entity.vo.ItemDiffs; import com.ctrip.framework.apollo.portal.entity.vo.NamespaceIdentifier; import com.ctrip.framework.apollo.portal.spi.UserInfoHolder; import com.ctrip.framework.apollo.tracer.Tracer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import org.springframework.web.client.HttpClientErrorException; import java.util.LinkedList; import java.util.List; import java.util.Map; @Service public class ItemService { @Autowired private UserInfoHolder userInfoHolder; @Autowired private AdminServiceAPI.NamespaceAPI namespaceAPI; @Autowired private AdminServiceAPI.ItemAPI itemAPI; @Autowired @Qualifier("fileTextResolver") private ConfigTextResolver fileTextResolver; @Autowired @Qualifier("propertyResolver") private ConfigTextResolver propertyResolver; /** * parse config text and update config items * * @return parse result */ public void updateConfigItemByText(NamespaceTextModel model) { String appId = model.getAppId(); Env env = model.getEnv(); String clusterName = model.getClusterName(); String namespaceName = model.getNamespaceName(); long namespaceId = model.getNamespaceId(); String configText = model.getConfigText(); ConfigTextResolver resolver = model.getFormat() == ConfigFileFormat.Properties ? propertyResolver : fileTextResolver; ItemChangeSets changeSets = resolver.resolve(namespaceId, configText, itemAPI.findItems(appId, env, clusterName, namespaceName)); if (changeSets.isEmpty()) { return; } changeSets.setDataChangeLastModifiedBy(userInfoHolder.getUser().getUserId()); updateItems(appId, env, clusterName, namespaceName, changeSets); Tracer.logEvent(CatEventType.MODIFY_NAMESPACE_BY_TEXT, String.format("%s+%s+%s+%s", appId, env, clusterName, namespaceName)); Tracer.logEvent(CatEventType.MODIFY_NAMESPACE, String.format("%s+%s+%s+%s", appId, env, clusterName, namespaceName)); } public void updateItems(String appId, Env env, String clusterName, String namespaceName, ItemChangeSets changeSets){ itemAPI.updateItemsByChangeSet(appId, env, clusterName, namespaceName, changeSets); } public ItemDTO createItem(String appId, Env env, String clusterName, String namespaceName, ItemDTO item) { NamespaceDTO namespace = namespaceAPI.loadNamespace(appId, env, clusterName, namespaceName); if (namespace == null) { throw new BadRequestException( "namespace:" + namespaceName + " not exist in env:" + env + ", cluster:" + clusterName); } item.setNamespaceId(namespace.getId()); ItemDTO itemDTO = itemAPI.createItem(appId, env, clusterName, namespaceName, item); Tracer.logEvent(CatEventType.MODIFY_NAMESPACE, String.format("%s+%s+%s+%s", appId, env, clusterName, namespaceName)); return itemDTO; } public void updateItem(String appId, Env env, String clusterName, String namespaceName, ItemDTO item) { itemAPI.updateItem(appId, env, clusterName, namespaceName, item.getId(), item); } public void deleteItem(Env env, long itemId, String userId) { itemAPI.deleteItem(env, itemId, userId); } public List<ItemDTO> findItems(String appId, Env env, String clusterName, String namespaceName) { return itemAPI.findItems(appId, env, clusterName, namespaceName); } public ItemDTO loadItem(Env env, String appId, String clusterName, String namespaceName, String key) { return itemAPI.loadItem(env, appId, clusterName, namespaceName, key); } public void syncItems(List<NamespaceIdentifier> comparedNamespaces, List<ItemDTO> sourceItems) { List<ItemDiffs> itemDiffs = compare(comparedNamespaces, sourceItems); for (ItemDiffs itemDiff : itemDiffs) { NamespaceIdentifier namespaceIdentifier = itemDiff.getNamespace(); ItemChangeSets changeSets = itemDiff.getDiffs(); changeSets.setDataChangeLastModifiedBy(userInfoHolder.getUser().getUserId()); String appId = namespaceIdentifier.getAppId(); Env env = namespaceIdentifier.getEnv(); String clusterName = namespaceIdentifier.getClusterName(); String namespaceName = namespaceIdentifier.getNamespaceName(); itemAPI.updateItemsByChangeSet(appId, env, clusterName, namespaceName, changeSets); Tracer.logEvent(CatEventType.SYNC_NAMESPACE, String.format("%s+%s+%s+%s", appId, env, clusterName, namespaceName)); } } public List<ItemDiffs> compare(List<NamespaceIdentifier> comparedNamespaces, List<ItemDTO> sourceItems) { List<ItemDiffs> result = new LinkedList<>(); for (NamespaceIdentifier namespace : comparedNamespaces) { ItemDiffs itemDiffs = new ItemDiffs(namespace); try { itemDiffs.setDiffs(parseChangeSets(namespace, sourceItems)); } catch (BadRequestException e) { itemDiffs.setDiffs(new ItemChangeSets()); itemDiffs.setExtInfo("该集群下没有名为 " + namespace.getNamespaceName() + " 的namespace"); } result.add(itemDiffs); } return result; } private long getNamespaceId(NamespaceIdentifier namespaceIdentifier) { String appId = namespaceIdentifier.getAppId(); String clusterName = namespaceIdentifier.getClusterName(); String namespaceName = namespaceIdentifier.getNamespaceName(); Env env = namespaceIdentifier.getEnv(); NamespaceDTO namespaceDTO = null; try { namespaceDTO = namespaceAPI.loadNamespace(appId, env, clusterName, namespaceName); } catch (HttpClientErrorException e) { if (e.getStatusCode() == HttpStatus.NOT_FOUND) { throw new BadRequestException(String.format( "namespace not exist. appId:%s, env:%s, clusterName:%s, namespaceName:%s", appId, env, clusterName, namespaceName)); } } return namespaceDTO.getId(); } private ItemChangeSets parseChangeSets(NamespaceIdentifier namespace, List<ItemDTO> sourceItems) { ItemChangeSets changeSets = new ItemChangeSets(); List<ItemDTO> targetItems = itemAPI.findItems(namespace.getAppId(), namespace.getEnv(), namespace.getClusterName(), namespace.getNamespaceName()); long namespaceId = getNamespaceId(namespace); if (CollectionUtils.isEmpty(targetItems)) {//all source items is added int lineNum = 1; for (ItemDTO sourceItem : sourceItems) { changeSets.addCreateItem(buildItem(namespaceId, lineNum++, sourceItem)); } } else { Map<String, ItemDTO> targetItemMap = BeanUtils.mapByKey("key", targetItems); String key, sourceValue, sourceComment; ItemDTO targetItem = null; int maxLineNum = targetItems.size();//append to last for (ItemDTO sourceItem : sourceItems) { key = sourceItem.getKey(); sourceValue = sourceItem.getValue(); sourceComment = sourceItem.getComment(); targetItem = targetItemMap.get(key); if (targetItem == null) {//added items changeSets.addCreateItem(buildItem(namespaceId, ++maxLineNum, sourceItem)); } else if (isModified(sourceValue, targetItem.getValue(), sourceComment, targetItem.getComment())) {//modified items targetItem.setValue(sourceValue); targetItem.setComment(sourceComment); changeSets.addUpdateItem(targetItem); } } } return changeSets; } private ItemDTO buildItem(long namespaceId, int lineNum, ItemDTO sourceItem) { ItemDTO createdItem = new ItemDTO(); BeanUtils.copyEntityProperties(sourceItem, createdItem); createdItem.setLineNum(lineNum); createdItem.setNamespaceId(namespaceId); return createdItem; } private boolean isModified(String sourceValue, String targetValue, String sourceComment, String targetComment) { if (!sourceValue.equals(targetValue)) { return true; } if (sourceComment == null) { return !StringUtils.isEmpty(targetComment); } else if (targetComment != null) { return !sourceComment.equals(targetComment); } else { return false; } } }