content: package com.ctrip.framework.apollo.biz.service;
  
  import com.google.common.collect.Maps;
  
  import com.ctrip.framework.apollo.biz.entity.Audit;
  import com.ctrip.framework.apollo.biz.entity.Cluster;
  import com.ctrip.framework.apollo.biz.entity.GrayReleaseRule;
  import com.ctrip.framework.apollo.biz.entity.Namespace;
  import com.ctrip.framework.apollo.biz.entity.Release;
  import com.ctrip.framework.apollo.biz.repository.GrayReleaseRuleRepository;
  import com.ctrip.framework.apollo.common.constants.NamespaceBranchStatus;
  import com.ctrip.framework.apollo.common.constants.ReleaseOperation;
  import com.ctrip.framework.apollo.common.constants.ReleaseOperationContext;
  import com.ctrip.framework.apollo.common.exception.BadRequestException;
  import com.ctrip.framework.apollo.common.utils.GrayReleaseRuleItemTransformer;
  import com.ctrip.framework.apollo.common.utils.UniqueKeyGenerator;
  
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.stereotype.Service;
  import org.springframework.transaction.annotation.Transactional;
  
  import java.util.Map;
  
  @Service
  public class NamespaceBranchService {
  
    @Autowired
    private AuditService auditService;
    @Autowired
    private GrayReleaseRuleRepository grayReleaseRuleRepository;
    @Autowired
    private ClusterService clusterService;
    @Autowired
    private ReleaseService releaseService;
    @Autowired
    private NamespaceService namespaceService;
    @Autowired
    private ReleaseHistoryService releaseHistoryService;
  
    @Transactional
    public Namespace createBranch(String appId, String parentClusterName, String namespaceName, String operator){
      Namespace childNamespace = findBranch(appId, parentClusterName, namespaceName);
      if (childNamespace != null){
        throw new BadRequestException("namespace already has branch");
      }
  
      Cluster parentCluster = clusterService.findOne(appId, parentClusterName);
      if (parentCluster == null || parentCluster.getParentClusterId() != 0) {
        throw new BadRequestException("cluster not exist or illegal cluster");
      }
  
      //create child cluster
      Cluster childCluster = createChildCluster(appId, parentCluster, namespaceName, operator);
  
      Cluster createdChildCluster = clusterService.saveWithoutInstanceOfAppNamespaces(childCluster);
  
      //create child namespace
      childNamespace = createNamespaceBranch(appId, createdChildCluster.getName(),
                                                          namespaceName, operator);
      return namespaceService.save(childNamespace);
    }
  
    public Namespace findBranch(String appId, String parentClusterName, String namespaceName) {
      return namespaceService.findChildNamespace(appId, parentClusterName, namespaceName);
    }
  
    public GrayReleaseRule findBranchGrayRules(String appId, String clusterName, String namespaceName,
                                               String branchName) {
      return grayReleaseRuleRepository
          .findTopByAppIdAndClusterNameAndNamespaceNameAndBranchNameOrderByIdDesc(appId, clusterName, namespaceName, branchName);
    }
  
    @Transactional
    public void updateBranchGrayRules(String appId, String clusterName, String namespaceName,
                                      String branchName, GrayReleaseRule newRules) {
      doUpdateBranchGrayRules(appId, clusterName, namespaceName, branchName, newRules, true, ReleaseOperation.APPLY_GRAY_RULES);
    }
  
    private void doUpdateBranchGrayRules(String appId, String clusterName, String namespaceName,
                                                String branchName, GrayReleaseRule newRules, boolean recordReleaseHistory, int releaseOperation) {
      GrayReleaseRule oldRules = grayReleaseRuleRepository
          .findTopByAppIdAndClusterNameAndNamespaceNameAndBranchNameOrderByIdDesc(appId, clusterName, namespaceName, branchName);
  
      Release latestBranchRelease = releaseService.findLatestActiveRelease(appId, branchName, namespaceName);
  
      long latestBranchReleaseId = latestBranchRelease != null ? latestBranchRelease.getId() : 0;
  
      newRules.setReleaseId(latestBranchReleaseId);
  
      grayReleaseRuleRepository.save(newRules);
  
      //delete old rules
      if (oldRules != null) {
        grayReleaseRuleRepository.delete(oldRules);
      }
  
      if (recordReleaseHistory) {
        Map<String, Object> releaseOperationContext = Maps.newHashMap();
        releaseOperationContext.put(ReleaseOperationContext.RULES, GrayReleaseRuleItemTransformer
            .batchTransformFromJSON(newRules.getRules()));
        if (oldRules != null) {
          releaseOperationContext.put(ReleaseOperationContext.OLD_RULES,
              GrayReleaseRuleItemTransformer.batchTransformFromJSON(oldRules.getRules()));
        }
        releaseHistoryService.createReleaseHistory(appId, clusterName, namespaceName, branchName, latestBranchReleaseId,
            latestBranchReleaseId, releaseOperation, releaseOperationContext, newRules.getDataChangeLastModifiedBy());
      }
    }
  
    @Transactional
    public GrayReleaseRule updateRulesReleaseId(String appId, String clusterName,
                                     String namespaceName, String branchName,
                                     long latestReleaseId, String operator) {
      GrayReleaseRule oldRules = grayReleaseRuleRepository.
          findTopByAppIdAndClusterNameAndNamespaceNameAndBranchNameOrderByIdDesc(appId, clusterName, namespaceName, branchName);
  
      if (oldRules == null) {
        return null;
      }
  
      GrayReleaseRule newRules = new GrayReleaseRule();
      newRules.setBranchStatus(NamespaceBranchStatus.ACTIVE);
      newRules.setReleaseId(latestReleaseId);
      newRules.setRules(oldRules.getRules());
      newRules.setAppId(oldRules.getAppId());
      newRules.setClusterName(oldRules.getClusterName());
      newRules.setNamespaceName(oldRules.getNamespaceName());
      newRules.setBranchName(oldRules.getBranchName());
      newRules.setDataChangeCreatedBy(operator);
      newRules.setDataChangeLastModifiedBy(operator);
  
      grayReleaseRuleRepository.save(newRules);
  
      grayReleaseRuleRepository.delete(oldRules);
  
      return newRules;
    }
  
    @Transactional
    public void deleteBranch(String appId, String clusterName, String namespaceName,
                             String branchName, int branchStatus, String operator) {
      Cluster toDeleteCluster = clusterService.findOne(appId, branchName);
      if (toDeleteCluster == null) {
        return;
      }
  
      Release latestBranchRelease = releaseService.findLatestActiveRelease(appId, branchName, namespaceName);
  
      long latestBranchReleaseId = latestBranchRelease != null ? latestBranchRelease.getId() : 0;
  
      //update branch rules
      GrayReleaseRule deleteRule = new GrayReleaseRule();
      deleteRule.setRules("[]");
      deleteRule.setAppId(appId);
      deleteRule.setClusterName(clusterName);
      deleteRule.setNamespaceName(namespaceName);
      deleteRule.setBranchName(branchName);
      deleteRule.setBranchStatus(branchStatus);
      deleteRule.setDataChangeLastModifiedBy(operator);
      deleteRule.setDataChangeCreatedBy(operator);
  
      doUpdateBranchGrayRules(appId, clusterName, namespaceName, branchName, deleteRule, false, -1);
  
      //delete branch cluster
      clusterService.delete(toDeleteCluster.getId(), operator);
  
      int releaseOperation = branchStatus == NamespaceBranchStatus.MERGED ? ReleaseOperation
          .GRAY_RELEASE_DELETED_AFTER_MERGE : ReleaseOperation.ABANDON_GRAY_RELEASE;
  
      releaseHistoryService.createReleaseHistory(appId, clusterName, namespaceName, branchName, latestBranchReleaseId,
          latestBranchReleaseId, releaseOperation, null, operator);
  
      auditService.audit("Branch", toDeleteCluster.getId(), Audit.OP.DELETE, operator);
    }
  
    private Cluster createChildCluster(String appId, Cluster parentCluster,
                                       String namespaceName, String operator) {
  
      Cluster childCluster = new Cluster();
      childCluster.setAppId(appId);
      childCluster.setParentClusterId(parentCluster.getId());
      childCluster.setName(UniqueKeyGenerator.generate(appId, parentCluster.getName(), namespaceName));
      childCluster.setDataChangeCreatedBy(operator);
      childCluster.setDataChangeLastModifiedBy(operator);
  
      return childCluster;
    }
  
  
    private Namespace createNamespaceBranch(String appId, String clusterName, String namespaceName, String operator) {
      Namespace childNamespace = new Namespace();
      childNamespace.setAppId(appId);
      childNamespace.setClusterName(clusterName);
      childNamespace.setNamespaceName(namespaceName);
      childNamespace.setDataChangeLastModifiedBy(operator);
      childNamespace.setDataChangeCreatedBy(operator);
      return childNamespace;
    }
  
  }
  