Commit f5218b17 authored by lepdou's avatar lepdou

link namespace show public namespace's item & refactor bean & improve log

parent b6996c0a
...@@ -78,4 +78,15 @@ public class NamespaceController { ...@@ -78,4 +78,15 @@ public class NamespaceController {
return BeanUtils.transfrom(NamespaceDTO.class, namespace); return BeanUtils.transfrom(NamespaceDTO.class, namespace);
} }
@RequestMapping("/clusters/{clusterName}/namespaces/{namespaceName}/public")
public NamespaceDTO findPublicNamespace(@PathVariable String clusterName,
@PathVariable String namespaceName) {
Namespace namespace = namespaceService.findPublicNamespace(clusterName, namespaceName);
if (namespace == null) {
throw new NotFoundException(String.format("public namespace not found. namespace:%s", namespaceName));
}
return BeanUtils.transfrom(NamespaceDTO.class, namespace);
}
} }
...@@ -6,8 +6,10 @@ import com.ctrip.framework.apollo.biz.entity.Namespace; ...@@ -6,8 +6,10 @@ import com.ctrip.framework.apollo.biz.entity.Namespace;
import com.ctrip.framework.apollo.biz.repository.NamespaceRepository; import com.ctrip.framework.apollo.biz.repository.NamespaceRepository;
import com.ctrip.framework.apollo.common.constants.NamespaceBranchStatus; import com.ctrip.framework.apollo.common.constants.NamespaceBranchStatus;
import com.ctrip.framework.apollo.common.entity.AppNamespace; import com.ctrip.framework.apollo.common.entity.AppNamespace;
import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.common.exception.ServiceException; import com.ctrip.framework.apollo.common.exception.ServiceException;
import com.ctrip.framework.apollo.common.utils.BeanUtils; import com.ctrip.framework.apollo.common.utils.BeanUtils;
import com.ctrip.framework.apollo.core.ConfigConsts;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
...@@ -57,6 +59,23 @@ public class NamespaceService { ...@@ -57,6 +59,23 @@ public class NamespaceService {
namespaceName); namespaceName);
} }
public Namespace findPublicNamespace(String clusterName, String namespaceName) {
AppNamespace appNamespace = appNamespaceService.findPublicNamespaceByName(namespaceName);
if (appNamespace == null) {
throw new BadRequestException("namespace not exist");
}
String appId = appNamespace.getAppId();
Namespace namespace = findOne(appId, clusterName, namespaceName);
if (namespace == null) {
namespace = findOne(appId, ConfigConsts.CLUSTER_NAME_DEFAULT, namespaceName);
}
return namespace;
}
public List<Namespace> findNamespaces(String appId, String clusterName) { public List<Namespace> findNamespaces(String appId, String clusterName) {
List<Namespace> namespaces = namespaceRepository.findByAppIdAndClusterNameOrderByIdAsc(appId, clusterName); List<Namespace> namespaces = namespaceRepository.findByAppIdAndClusterNameOrderByIdAsc(appId, clusterName);
if (namespaces == null) { if (namespaces == null) {
......
...@@ -12,7 +12,8 @@ import com.ctrip.framework.apollo.openapi.dto.OpenItemDTO; ...@@ -12,7 +12,8 @@ import com.ctrip.framework.apollo.openapi.dto.OpenItemDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceDTO; import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceLockDTO; import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceLockDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenReleaseDTO; import com.ctrip.framework.apollo.openapi.dto.OpenReleaseDTO;
import com.ctrip.framework.apollo.portal.entity.vo.NamespaceVO; import com.ctrip.framework.apollo.portal.entity.bo.ItemBO;
import com.ctrip.framework.apollo.portal.entity.bo.NamespaceBO;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
...@@ -51,22 +52,22 @@ public class OpenApiBeanUtils { ...@@ -51,22 +52,22 @@ public class OpenApiBeanUtils {
return openReleaseDTO; return openReleaseDTO;
} }
public static OpenNamespaceDTO transformFromNamespaceVO(NamespaceVO namespaceVO) { public static OpenNamespaceDTO transformFromNamespaceBO(NamespaceBO namespaceBO) {
Preconditions.checkArgument(namespaceVO != null); Preconditions.checkArgument(namespaceBO != null);
OpenNamespaceDTO openNamespaceDTO = BeanUtils.transfrom(OpenNamespaceDTO.class, namespaceVO OpenNamespaceDTO openNamespaceDTO = BeanUtils.transfrom(OpenNamespaceDTO.class, namespaceBO
.getBaseInfo()); .getBaseInfo());
//app namespace info //app namespace info
openNamespaceDTO.setFormat(namespaceVO.getFormat()); openNamespaceDTO.setFormat(namespaceBO.getFormat());
openNamespaceDTO.setComment(namespaceVO.getComment()); openNamespaceDTO.setComment(namespaceBO.getComment());
openNamespaceDTO.setPublic(namespaceVO.isPublic()); openNamespaceDTO.setPublic(namespaceBO.isPublic());
//items //items
List<OpenItemDTO> items = new LinkedList<>(); List<OpenItemDTO> items = new LinkedList<>();
List<NamespaceVO.ItemVO> itemVOs = namespaceVO.getItems(); List<ItemBO> itemBOs = namespaceBO.getItems();
if (!CollectionUtils.isEmpty(itemVOs)) { if (!CollectionUtils.isEmpty(itemBOs)) {
items.addAll(itemVOs.stream().map(itemVO -> transformFromItemDTO(itemVO.getItem())).collect items.addAll(itemBOs.stream().map(itemBO -> transformFromItemDTO(itemBO.getItem())).collect
(Collectors.toList())); (Collectors.toList()));
} }
openNamespaceDTO.setItems(items); openNamespaceDTO.setItems(items);
...@@ -74,14 +75,14 @@ public class OpenApiBeanUtils { ...@@ -74,14 +75,14 @@ public class OpenApiBeanUtils {
} }
public static List<OpenNamespaceDTO> batchTransformFromNamespaceVOs(List<NamespaceVO> public static List<OpenNamespaceDTO> batchTransformFromNamespaceBOs(List<NamespaceBO>
namespaceVOs) { namespaceBOs) {
if (CollectionUtils.isEmpty(namespaceVOs)) { if (CollectionUtils.isEmpty(namespaceBOs)) {
return Collections.emptyList(); return Collections.emptyList();
} }
List<OpenNamespaceDTO> openNamespaceDTOs = List<OpenNamespaceDTO> openNamespaceDTOs =
namespaceVOs.stream().map(OpenApiBeanUtils::transformFromNamespaceVO) namespaceBOs.stream().map(OpenApiBeanUtils::transformFromNamespaceBO)
.collect(Collectors.toCollection(LinkedList::new)); .collect(Collectors.toCollection(LinkedList::new));
return openNamespaceDTOs; return openNamespaceDTOs;
......
...@@ -7,7 +7,7 @@ import com.ctrip.framework.apollo.core.enums.Env; ...@@ -7,7 +7,7 @@ import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceDTO; import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceLockDTO; import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceLockDTO;
import com.ctrip.framework.apollo.openapi.util.OpenApiBeanUtils; import com.ctrip.framework.apollo.openapi.util.OpenApiBeanUtils;
import com.ctrip.framework.apollo.portal.entity.vo.NamespaceVO; import com.ctrip.framework.apollo.portal.entity.bo.NamespaceBO;
import com.ctrip.framework.apollo.portal.service.NamespaceLockService; import com.ctrip.framework.apollo.portal.service.NamespaceLockService;
import com.ctrip.framework.apollo.portal.service.NamespaceService; import com.ctrip.framework.apollo.portal.service.NamespaceService;
...@@ -33,7 +33,7 @@ public class NamespaceController { ...@@ -33,7 +33,7 @@ public class NamespaceController {
@PathVariable String clusterName) { @PathVariable String clusterName) {
return OpenApiBeanUtils return OpenApiBeanUtils
.batchTransformFromNamespaceVOs(namespaceService.findNamespaces(appId, Env .batchTransformFromNamespaceBOs(namespaceService.findNamespaceBOs(appId, Env
.fromString(env), clusterName)); .fromString(env), clusterName));
} }
...@@ -41,12 +41,12 @@ public class NamespaceController { ...@@ -41,12 +41,12 @@ public class NamespaceController {
public OpenNamespaceDTO loadNamespace(@PathVariable String appId, @PathVariable String env, public OpenNamespaceDTO loadNamespace(@PathVariable String appId, @PathVariable String env,
@PathVariable String clusterName, @PathVariable String @PathVariable String clusterName, @PathVariable String
namespaceName) { namespaceName) {
NamespaceVO namespaceVO = namespaceService.loadNamespace(appId, Env.fromString NamespaceBO namespaceBO = namespaceService.loadNamespaceBO(appId, Env.fromString
(env), clusterName, namespaceName); (env), clusterName, namespaceName);
if (namespaceVO == null) { if (namespaceBO == null) {
return null; return null;
} }
return OpenApiBeanUtils.transformFromNamespaceVO(namespaceVO); return OpenApiBeanUtils.transformFromNamespaceBO(namespaceBO);
} }
@RequestMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/lock", method = RequestMethod.GET) @RequestMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/lock", method = RequestMethod.GET)
......
...@@ -71,12 +71,16 @@ public class AdminServiceAPI { ...@@ -71,12 +71,16 @@ public class AdminServiceAPI {
public NamespaceDTO loadNamespace(String appId, Env env, String clusterName, public NamespaceDTO loadNamespace(String appId, Env env, String clusterName,
String namespaceName) { String namespaceName) {
NamespaceDTO dto = return
restTemplate.get(env, "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}", restTemplate.get(env, "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}",
NamespaceDTO.class, appId, clusterName, namespaceName); NamespaceDTO.class, appId, clusterName, namespaceName);
return dto;
} }
public NamespaceDTO loadPublicNamespace(Env env, String clusterName, String namespaceName) {
return
restTemplate.get(env, "/clusters/{clusterName}/namespaces/{namespaceName}/public",
NamespaceDTO.class, clusterName, namespaceName);
}
public NamespaceDTO createNamespace(Env env, NamespaceDTO namespace) { public NamespaceDTO createNamespace(Env env, NamespaceDTO namespace) {
return restTemplate return restTemplate
...@@ -378,23 +382,26 @@ public class AdminServiceAPI { ...@@ -378,23 +382,26 @@ public class AdminServiceAPI {
public static class ReleaseHistoryAPI extends API { public static class ReleaseHistoryAPI extends API {
private ParameterizedTypeReference<PageDTO<ReleaseHistoryDTO>> type = private ParameterizedTypeReference<PageDTO<ReleaseHistoryDTO>> type =
new ParameterizedTypeReference<PageDTO<ReleaseHistoryDTO>>() {}; new ParameterizedTypeReference<PageDTO<ReleaseHistoryDTO>>() {
};
public PageDTO<ReleaseHistoryDTO> findReleaseHistoriesByNamespace(String appId, Env env, String clusterName, public PageDTO<ReleaseHistoryDTO> findReleaseHistoriesByNamespace(String appId, Env env, String clusterName,
String namespaceName, int page, int size) { String namespaceName, int page, int size) {
return restTemplate.get(env, return restTemplate.get(env,
"/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases/histories?page={page}&size={size}", "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases/histories?page={page}&size={size}",
type, appId, clusterName, namespaceName, page, size).getBody(); type, appId, clusterName, namespaceName, page, size).getBody();
} }
public PageDTO<ReleaseHistoryDTO> findByReleaseIdAndOperation(Env env, long releaseId, int operation, int page, int size) { public PageDTO<ReleaseHistoryDTO> findByReleaseIdAndOperation(Env env, long releaseId, int operation, int page,
int size) {
return restTemplate.get(env, return restTemplate.get(env,
"/releases/histories/by_release_id_and_operation?releaseId={releaseId}&operation={operation}&page={page}&size={size}", "/releases/histories/by_release_id_and_operation?releaseId={releaseId}&operation={operation}&page={page}&size={size}",
type, releaseId, operation, page, size).getBody(); type, releaseId, operation, page, size).getBody();
} }
public PageDTO<ReleaseHistoryDTO> findByPreviousReleaseIdAndOperation(Env env, long previousReleaseId, int operation, int page, int size) { public PageDTO<ReleaseHistoryDTO> findByPreviousReleaseIdAndOperation(Env env, long previousReleaseId,
int operation, int page, int size) {
return restTemplate.get(env, return restTemplate.get(env,
"/releases/histories/by_previous_release_id_and_operation?previousReleaseId={releaseId}&operation={operation}&page={page}&size={size}", "/releases/histories/by_previous_release_id_and_operation?previousReleaseId={releaseId}&operation={operation}&page={page}&size={size}",
type, previousReleaseId, operation, page, size).getBody(); type, previousReleaseId, operation, page, size).getBody();
......
package com.ctrip.framework.apollo.portal.components; package com.ctrip.framework.apollo.portal.components;
import com.ctrip.framework.apollo.core.MetaDomainConsts;
import com.ctrip.framework.apollo.core.enums.Env; import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.core.utils.ApolloThreadFactory; import com.ctrip.framework.apollo.core.utils.ApolloThreadFactory;
import com.ctrip.framework.apollo.portal.api.AdminServiceAPI; import com.ctrip.framework.apollo.portal.api.AdminServiceAPI;
...@@ -110,13 +111,15 @@ public class PortalSettings { ...@@ -110,13 +111,15 @@ public class PortalSettings {
logger.info("Env revived because env health check success. env: {}", env); logger.info("Env revived because env health check success. env: {}", env);
} }
} else { } else {
logger.warn("Env health check failed, maybe because of admin server down. env: {}", env); logger.error("Env health check failed, maybe because of admin server down. env: {}, meta server address: {}", env,
MetaDomainConsts.getDomain(env));
handleEnvDown(env); handleEnvDown(env);
} }
} catch (Exception e) { } catch (Exception e) {
logger.warn("Env health check failed, maybe because of meta server down " logger.error("Env health check failed, maybe because of meta server down "
+ "or config error meta server address. env: {}", env); + "or configure wrong meta server address. env: {}, meta server address: {}", env,
MetaDomainConsts.getDomain(env), e);
handleEnvDown(env); handleEnvDown(env);
} }
} }
...@@ -133,15 +136,18 @@ public class PortalSettings { ...@@ -133,15 +136,18 @@ public class PortalSettings {
healthCheckFailedCounter.put(env, ++failedTimes); healthCheckFailedCounter.put(env, ++failedTimes);
if (!envStatusMark.get(env)) { if (!envStatusMark.get(env)) {
logger.error("Env is down. env: {}, failed times: {}", env, failedTimes); logger.error("Env is down. env: {}, failed times: {}, meta server address: {}", env, failedTimes,
MetaDomainConsts.getDomain(env));
} else { } else {
if (failedTimes >= ENV_DOWN_THRESHOLD) { if (failedTimes >= ENV_DOWN_THRESHOLD) {
envStatusMark.put(env, false); envStatusMark.put(env, false);
logger.error("Env is down because health check failed for {} times, " logger.error("Env is down because health check failed for {} times, "
+ "which equals to down threshold. env: {}", ENV_DOWN_THRESHOLD, env); + "which equals to down threshold. env: {}, meta server address: {}", ENV_DOWN_THRESHOLD, env,
MetaDomainConsts.getDomain(env));
} else { } else {
logger.warn("Env health check failed for {} times which less than down threshold. down threshold:{}, env: {}", logger.error(
failedTimes, ENV_DOWN_THRESHOLD, env); "Env health check failed for {} times which less than down threshold. down threshold:{}, env: {}, meta server address: {}",
failedTimes, ENV_DOWN_THRESHOLD, env, MetaDomainConsts.getDomain(env));
} }
} }
......
package com.ctrip.framework.apollo.portal.components; package com.ctrip.framework.apollo.portal.components;
import com.ctrip.framework.apollo.common.exception.ServiceException; import com.ctrip.framework.apollo.common.exception.ServiceException;
import com.ctrip.framework.apollo.core.MetaDomainConsts;
import com.ctrip.framework.apollo.core.dto.ServiceDTO; import com.ctrip.framework.apollo.core.dto.ServiceDTO;
import com.ctrip.framework.apollo.core.enums.Env; import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.constant.CatEventType; import com.ctrip.framework.apollo.portal.constant.CatEventType;
...@@ -109,7 +110,9 @@ public class RetryableRestTemplate { ...@@ -109,7 +110,9 @@ public class RetryableRestTemplate {
} }
//all admin server down //all admin server down
ServiceException e = new ServiceException("No available admin service"); ServiceException e =
new ServiceException(String.format("Admin servers are unresponsive. meta server address: %s, admin servers: %s",
MetaDomainConsts.getDomain(env), services));
ct.setStatus(e); ct.setStatus(e);
ct.complete(); ct.complete();
throw e; throw e;
...@@ -138,9 +141,9 @@ public class RetryableRestTemplate { ...@@ -138,9 +141,9 @@ public class RetryableRestTemplate {
} catch (Throwable t) { } catch (Throwable t) {
logger.error("Http request failed, uri: {}, method: {}", uri, HttpMethod.GET, t); logger.error("Http request failed, uri: {}, method: {}", uri, HttpMethod.GET, t);
Tracer.logError(t); Tracer.logError(t);
if (canRetry(t, HttpMethod.GET)){ if (canRetry(t, HttpMethod.GET)) {
Tracer.logEvent(CatEventType.API_RETRY, uri); Tracer.logEvent(CatEventType.API_RETRY, uri);
}else {// biz exception rethrow } else {// biz exception rethrow
ct.setStatus(t); ct.setStatus(t);
ct.complete(); ct.complete();
throw t; throw t;
...@@ -150,7 +153,9 @@ public class RetryableRestTemplate { ...@@ -150,7 +153,9 @@ public class RetryableRestTemplate {
} }
//all admin server down //all admin server down
ServiceException e = new ServiceException("No available admin service"); ServiceException e =
new ServiceException(String.format("Admin servers are unresponsive. meta server address: %s, admin servers: %s",
MetaDomainConsts.getDomain(env), services));
ct.setStatus(e); ct.setStatus(e);
ct.complete(); ct.complete();
throw e; throw e;
...@@ -162,7 +167,10 @@ public class RetryableRestTemplate { ...@@ -162,7 +167,10 @@ public class RetryableRestTemplate {
List<ServiceDTO> services = adminServiceAddressLocator.getServiceList(env); List<ServiceDTO> services = adminServiceAddressLocator.getServiceList(env);
if (CollectionUtils.isEmpty(services)) { if (CollectionUtils.isEmpty(services)) {
ServiceException e = new ServiceException("No available admin service"); ServiceException e = new ServiceException(String.format("No available admin server."
+ " Maybe because of meta server down or all admin server down. "
+ "Meta server address: %s",
MetaDomainConsts.getDomain(env)));
ct.setStatus(e); ct.setStatus(e);
ct.complete(); ct.complete();
throw e; throw e;
......
...@@ -6,7 +6,7 @@ import com.ctrip.framework.apollo.common.dto.ReleaseDTO; ...@@ -6,7 +6,7 @@ import com.ctrip.framework.apollo.common.dto.ReleaseDTO;
import com.ctrip.framework.apollo.core.enums.Env; import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.components.PermissionValidator; import com.ctrip.framework.apollo.portal.components.PermissionValidator;
import com.ctrip.framework.apollo.portal.entity.model.NamespaceReleaseModel; import com.ctrip.framework.apollo.portal.entity.model.NamespaceReleaseModel;
import com.ctrip.framework.apollo.portal.entity.vo.NamespaceVO; import com.ctrip.framework.apollo.portal.entity.bo.NamespaceBO;
import com.ctrip.framework.apollo.portal.listener.ConfigPublishEvent; import com.ctrip.framework.apollo.portal.listener.ConfigPublishEvent;
import com.ctrip.framework.apollo.portal.service.NamespaceBranchService; import com.ctrip.framework.apollo.portal.service.NamespaceBranchService;
import com.ctrip.framework.apollo.portal.service.ReleaseService; import com.ctrip.framework.apollo.portal.service.ReleaseService;
...@@ -35,7 +35,7 @@ public class NamespaceBranchController { ...@@ -35,7 +35,7 @@ public class NamespaceBranchController {
private ApplicationEventPublisher publisher; private ApplicationEventPublisher publisher;
@RequestMapping("/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/branches") @RequestMapping("/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/branches")
public NamespaceVO findBranch(@PathVariable String appId, public NamespaceBO findBranch(@PathVariable String appId,
@PathVariable String env, @PathVariable String env,
@PathVariable String clusterName, @PathVariable String clusterName,
@PathVariable String namespaceName) { @PathVariable String namespaceName) {
......
...@@ -13,7 +13,7 @@ import com.ctrip.framework.apollo.core.enums.Env; ...@@ -13,7 +13,7 @@ import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.core.utils.StringUtils; import com.ctrip.framework.apollo.core.utils.StringUtils;
import com.ctrip.framework.apollo.portal.constant.RoleType; import com.ctrip.framework.apollo.portal.constant.RoleType;
import com.ctrip.framework.apollo.portal.entity.model.NamespaceCreationModel; import com.ctrip.framework.apollo.portal.entity.model.NamespaceCreationModel;
import com.ctrip.framework.apollo.portal.entity.vo.NamespaceVO; import com.ctrip.framework.apollo.portal.entity.bo.NamespaceBO;
import com.ctrip.framework.apollo.portal.listener.AppNamespaceCreationEvent; import com.ctrip.framework.apollo.portal.listener.AppNamespaceCreationEvent;
import com.ctrip.framework.apollo.portal.service.AppNamespaceService; import com.ctrip.framework.apollo.portal.service.AppNamespaceService;
import com.ctrip.framework.apollo.portal.service.AppService; import com.ctrip.framework.apollo.portal.service.AppService;
...@@ -35,6 +35,7 @@ import org.springframework.web.bind.annotation.PathVariable; ...@@ -35,6 +35,7 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import java.util.List; import java.util.List;
...@@ -67,17 +68,25 @@ public class NamespaceController { ...@@ -67,17 +68,25 @@ public class NamespaceController {
} }
@RequestMapping("/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces") @RequestMapping("/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces")
public List<NamespaceVO> findNamespaces(@PathVariable String appId, @PathVariable String env, public List<NamespaceBO> findNamespaces(@PathVariable String appId, @PathVariable String env,
@PathVariable String clusterName) { @PathVariable String clusterName) {
return namespaceService.findNamespaces(appId, Env.valueOf(env), clusterName); return namespaceService.findNamespaceBOs(appId, Env.valueOf(env), clusterName);
} }
@RequestMapping("/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName:.+}") @RequestMapping("/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName:.+}")
public NamespaceVO findNamespaces(@PathVariable String appId, @PathVariable String env, public NamespaceBO findNamespace(@PathVariable String appId, @PathVariable String env,
@PathVariable String clusterName, @PathVariable String namespaceName) { @PathVariable String clusterName, @PathVariable String namespaceName) {
return namespaceService.loadNamespace(appId, Env.valueOf(env), clusterName, namespaceName); return namespaceService.loadNamespaceBO(appId, Env.valueOf(env), clusterName, namespaceName);
}
@RequestMapping("/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/public")
public NamespaceBO findPublicNamespace(@PathVariable String env,
@PathVariable String namespaceName,
@PathVariable String clusterName) {
return namespaceService.loadPublicNamespaceBO(Env.valueOf(env), clusterName, namespaceName);
} }
@PreAuthorize(value = "@permissionValidator.hasCreateNamespacePermission(#appId)") @PreAuthorize(value = "@permissionValidator.hasCreateNamespacePermission(#appId)")
......
...@@ -5,7 +5,7 @@ import com.ctrip.framework.apollo.common.utils.RequestPrecondition; ...@@ -5,7 +5,7 @@ import com.ctrip.framework.apollo.common.utils.RequestPrecondition;
import com.ctrip.framework.apollo.core.enums.Env; import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.entity.model.NamespaceReleaseModel; import com.ctrip.framework.apollo.portal.entity.model.NamespaceReleaseModel;
import com.ctrip.framework.apollo.portal.entity.vo.ReleaseCompareResult; import com.ctrip.framework.apollo.portal.entity.vo.ReleaseCompareResult;
import com.ctrip.framework.apollo.portal.entity.vo.ReleaseVO; import com.ctrip.framework.apollo.portal.entity.bo.ReleaseBO;
import com.ctrip.framework.apollo.portal.listener.ConfigPublishEvent; import com.ctrip.framework.apollo.portal.listener.ConfigPublishEvent;
import com.ctrip.framework.apollo.portal.service.ReleaseService; import com.ctrip.framework.apollo.portal.service.ReleaseService;
...@@ -89,7 +89,7 @@ public class ReleaseController { ...@@ -89,7 +89,7 @@ public class ReleaseController {
@RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/releases/all") @RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/releases/all")
public List<ReleaseVO> findAllReleases(@PathVariable String appId, public List<ReleaseBO> findAllReleases(@PathVariable String appId,
@PathVariable String env, @PathVariable String env,
@PathVariable String clusterName, @PathVariable String clusterName,
@PathVariable String namespaceName, @PathVariable String namespaceName,
......
package com.ctrip.framework.apollo.portal.entity.bo;
import com.ctrip.framework.apollo.common.dto.ItemDTO;
public class ItemBO {
private ItemDTO item;
private boolean isModified;
private boolean isDeleted;
private String oldValue;
private String newValue;
public ItemDTO getItem() {
return item;
}
public void setItem(ItemDTO item) {
this.item = item;
}
public boolean isDeleted() {
return isDeleted;
}
public void setDeleted(boolean deleted) {
isDeleted = deleted;
}
public boolean isModified() {
return isModified;
}
public void setModified(boolean isModified) {
this.isModified = isModified;
}
public String getOldValue() {
return oldValue;
}
public void setOldValue(String oldValue) {
this.oldValue = oldValue;
}
public String getNewValue() {
return newValue;
}
public void setNewValue(String newValue) {
this.newValue = newValue;
}
}
package com.ctrip.framework.apollo.portal.entity.vo; package com.ctrip.framework.apollo.portal.entity.bo;
public class KVEntity { public class KVEntity {
......
package com.ctrip.framework.apollo.portal.entity.vo; package com.ctrip.framework.apollo.portal.entity.bo;
import com.ctrip.framework.apollo.common.dto.ItemDTO;
import com.ctrip.framework.apollo.common.dto.NamespaceDTO; import com.ctrip.framework.apollo.common.dto.NamespaceDTO;
import java.util.List; import java.util.List;
public class NamespaceVO { public class NamespaceBO {
private NamespaceDTO baseInfo; private NamespaceDTO baseInfo;
private int itemModifiedCnt; private int itemModifiedCnt;
private List<ItemVO> items; private List<ItemBO> items;
private String format; private String format;
private boolean isPublic; private boolean isPublic;
private String parentAppId; private String parentAppId;
...@@ -31,11 +30,11 @@ public class NamespaceVO { ...@@ -31,11 +30,11 @@ public class NamespaceVO {
this.itemModifiedCnt = itemModifiedCnt; this.itemModifiedCnt = itemModifiedCnt;
} }
public List<ItemVO> getItems() { public List<ItemBO> getItems() {
return items; return items;
} }
public void setItems(List<ItemVO> items) { public void setItems(List<ItemBO> items) {
this.items = items; this.items = items;
} }
...@@ -71,54 +70,4 @@ public class NamespaceVO { ...@@ -71,54 +70,4 @@ public class NamespaceVO {
this.comment = comment; this.comment = comment;
} }
public static class ItemVO {
private ItemDTO item;
private boolean isModified;
private boolean isDeleted;
private String oldValue;
private String newValue;
public ItemDTO getItem() {
return item;
}
public void setItem(ItemDTO item) {
this.item = item;
}
public boolean isDeleted() {
return isDeleted;
}
public void setDeleted(boolean deleted) {
isDeleted = deleted;
}
public boolean isModified() {
return isModified;
}
public void setModified(boolean isModified) {
this.isModified = isModified;
}
public String getOldValue() {
return oldValue;
}
public void setOldValue(String oldValue) {
this.oldValue = oldValue;
}
public String getNewValue() {
return newValue;
}
public void setNewValue(String newValue) {
this.newValue = newValue;
}
}
} }
package com.ctrip.framework.apollo.portal.entity.vo; package com.ctrip.framework.apollo.portal.entity.bo;
import com.ctrip.framework.apollo.common.dto.ReleaseDTO; import com.ctrip.framework.apollo.common.dto.ReleaseDTO;
import com.ctrip.framework.apollo.portal.entity.bo.KVEntity;
import java.util.Set; import java.util.Set;
public class ReleaseVO { public class ReleaseBO {
private ReleaseDTO baseInfo; private ReleaseDTO baseInfo;
......
package com.ctrip.framework.apollo.portal.entity.vo; package com.ctrip.framework.apollo.portal.entity.vo;
import com.ctrip.framework.apollo.common.entity.EntityPair; import com.ctrip.framework.apollo.common.entity.EntityPair;
import com.ctrip.framework.apollo.portal.entity.bo.KVEntity;
import com.ctrip.framework.apollo.portal.enums.ChangeType; import com.ctrip.framework.apollo.portal.enums.ChangeType;
public class Change { public class Change {
......
package com.ctrip.framework.apollo.portal.entity.vo; package com.ctrip.framework.apollo.portal.entity.vo;
import com.ctrip.framework.apollo.common.entity.EntityPair; import com.ctrip.framework.apollo.common.entity.EntityPair;
import com.ctrip.framework.apollo.portal.entity.bo.KVEntity;
import com.ctrip.framework.apollo.portal.enums.ChangeType; import com.ctrip.framework.apollo.portal.enums.ChangeType;
import java.util.LinkedList; import java.util.LinkedList;
......
...@@ -11,7 +11,7 @@ import com.ctrip.framework.apollo.portal.api.AdminServiceAPI; ...@@ -11,7 +11,7 @@ import com.ctrip.framework.apollo.portal.api.AdminServiceAPI;
import com.ctrip.framework.apollo.portal.components.ItemsComparator; import com.ctrip.framework.apollo.portal.components.ItemsComparator;
import com.ctrip.framework.apollo.portal.components.PermissionValidator; import com.ctrip.framework.apollo.portal.components.PermissionValidator;
import com.ctrip.framework.apollo.portal.constant.CatEventType; import com.ctrip.framework.apollo.portal.constant.CatEventType;
import com.ctrip.framework.apollo.portal.entity.vo.NamespaceVO; import com.ctrip.framework.apollo.portal.entity.bo.NamespaceBO;
import com.ctrip.framework.apollo.portal.spi.UserInfoHolder; import com.ctrip.framework.apollo.portal.spi.UserInfoHolder;
import com.ctrip.framework.apollo.tracer.Tracer; import com.ctrip.framework.apollo.tracer.Tracer;
...@@ -100,7 +100,7 @@ public class NamespaceBranchService { ...@@ -100,7 +100,7 @@ public class NamespaceBranchService {
private ItemChangeSets calculateBranchChangeSet(String appId, Env env, String clusterName, String namespaceName, private ItemChangeSets calculateBranchChangeSet(String appId, Env env, String clusterName, String namespaceName,
String branchName) { String branchName) {
NamespaceVO parentNamespace = namespaceService.loadNamespace(appId, env, clusterName, namespaceName); NamespaceBO parentNamespace = namespaceService.loadNamespaceBO(appId, env, clusterName, namespaceName);
if (parentNamespace == null) { if (parentNamespace == null) {
throw new BadRequestException("base namespace not existed"); throw new BadRequestException("base namespace not existed");
...@@ -125,12 +125,12 @@ public class NamespaceBranchService { ...@@ -125,12 +125,12 @@ public class NamespaceBranchService {
return namespaceBranchAPI.findBranch(appId, env, clusterName, namespaceName); return namespaceBranchAPI.findBranch(appId, env, clusterName, namespaceName);
} }
public NamespaceVO findBranch(String appId, Env env, String clusterName, String namespaceName) { public NamespaceBO findBranch(String appId, Env env, String clusterName, String namespaceName) {
NamespaceDTO namespaceDTO = findBranchBaseInfo(appId, env, clusterName, namespaceName); NamespaceDTO namespaceDTO = findBranchBaseInfo(appId, env, clusterName, namespaceName);
if (namespaceDTO == null) { if (namespaceDTO == null) {
return null; return null;
} }
return namespaceService.loadNamespace(appId, env, namespaceDTO.getClusterName(), namespaceName); return namespaceService.loadNamespaceBO(appId, env, namespaceDTO.getClusterName(), namespaceName);
} }
} }
package com.ctrip.framework.apollo.portal.service; package com.ctrip.framework.apollo.portal.service;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.ctrip.framework.apollo.common.constants.GsonType;
import com.ctrip.framework.apollo.common.dto.ItemDTO; import com.ctrip.framework.apollo.common.dto.ItemDTO;
import com.ctrip.framework.apollo.common.dto.NamespaceDTO; import com.ctrip.framework.apollo.common.dto.NamespaceDTO;
import com.ctrip.framework.apollo.common.dto.ReleaseDTO; import com.ctrip.framework.apollo.common.dto.ReleaseDTO;
import com.ctrip.framework.apollo.common.entity.AppNamespace; import com.ctrip.framework.apollo.common.entity.AppNamespace;
import com.ctrip.framework.apollo.common.exception.BadRequestException; import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.common.utils.BeanUtils; import com.ctrip.framework.apollo.common.utils.BeanUtils;
import com.ctrip.framework.apollo.core.ConfigConsts;
import com.ctrip.framework.apollo.core.enums.ConfigFileFormat; import com.ctrip.framework.apollo.core.enums.ConfigFileFormat;
import com.ctrip.framework.apollo.core.enums.Env; import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.core.utils.StringUtils; import com.ctrip.framework.apollo.core.utils.StringUtils;
import com.ctrip.framework.apollo.portal.api.AdminServiceAPI; import com.ctrip.framework.apollo.portal.api.AdminServiceAPI;
import com.ctrip.framework.apollo.portal.constant.CatEventType; import com.ctrip.framework.apollo.portal.constant.CatEventType;
import com.ctrip.framework.apollo.portal.entity.vo.NamespaceVO; import com.ctrip.framework.apollo.portal.entity.bo.ItemBO;
import com.ctrip.framework.apollo.portal.entity.bo.NamespaceBO;
import com.ctrip.framework.apollo.portal.spi.UserInfoHolder; import com.ctrip.framework.apollo.portal.spi.UserInfoHolder;
import com.ctrip.framework.apollo.tracer.Tracer; import com.ctrip.framework.apollo.tracer.Tracer;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.HttpClientErrorException;
import java.lang.reflect.Type;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
...@@ -35,8 +38,6 @@ public class NamespaceService { ...@@ -35,8 +38,6 @@ public class NamespaceService {
private Logger logger = LoggerFactory.getLogger(NamespaceService.class); private Logger logger = LoggerFactory.getLogger(NamespaceService.class);
private Gson gson = new Gson(); private Gson gson = new Gson();
private static Type mapType = new TypeToken<Map<String, String>>() {
}.getType();
@Autowired @Autowired
private UserInfoHolder userInfoHolder; private UserInfoHolder userInfoHolder;
...@@ -58,8 +59,8 @@ public class NamespaceService { ...@@ -58,8 +59,8 @@ public class NamespaceService {
NamespaceDTO createdNamespace = namespaceAPI.createNamespace(env, namespace); NamespaceDTO createdNamespace = namespaceAPI.createNamespace(env, namespace);
Tracer.logEvent(CatEventType.CREATE_NAMESPACE, Tracer.logEvent(CatEventType.CREATE_NAMESPACE,
String.format("%s+%s+%s+%s", namespace.getAppId(), env, namespace.getClusterName(), String.format("%s+%s+%s+%s", namespace.getAppId(), env, namespace.getClusterName(),
namespace.getNamespaceName())); namespace.getNamespaceName()));
return createdNamespace; return createdNamespace;
} }
...@@ -68,7 +69,7 @@ public class NamespaceService { ...@@ -68,7 +69,7 @@ public class NamespaceService {
public void deleteNamespace(String appId, Env env, String clusterName, String namespaceName) { public void deleteNamespace(String appId, Env env, String clusterName, String namespaceName) {
AppNamespace appNamespace = appNamespaceService.findByAppIdAndName(appId, namespaceName); AppNamespace appNamespace = appNamespaceService.findByAppIdAndName(appId, namespaceName);
if (appNamespace != null && !appNamespace.isPublic()){ if (appNamespace != null && !appNamespace.isPublic()) {
throw new BadRequestException("private namespace can not be deleted"); throw new BadRequestException("private namespace can not be deleted");
} }
...@@ -89,20 +90,20 @@ public class NamespaceService { ...@@ -89,20 +90,20 @@ public class NamespaceService {
/** /**
* load cluster all namespace info with items * load cluster all namespace info with items
*/ */
public List<NamespaceVO> findNamespaces(String appId, Env env, String clusterName) { public List<NamespaceBO> findNamespaceBOs(String appId, Env env, String clusterName) {
List<NamespaceDTO> namespaces = namespaceAPI.findNamespaceByCluster(appId, env, clusterName); List<NamespaceDTO> namespaces = namespaceAPI.findNamespaceByCluster(appId, env, clusterName);
if (namespaces == null || namespaces.size() == 0) { if (namespaces == null || namespaces.size() == 0) {
throw new BadRequestException("namespaces not exist"); throw new BadRequestException("namespaces not exist");
} }
List<NamespaceVO> namespaceVOs = new LinkedList<>(); List<NamespaceBO> namespaceBOs = new LinkedList<>();
for (NamespaceDTO namespace : namespaces) { for (NamespaceDTO namespace : namespaces) {
NamespaceVO namespaceVO = null; NamespaceBO namesapceBO = null;
try { try {
namespaceVO = parseNamespace(appId, env, clusterName, namespace); namesapceBO = transformNamespace2BO(appId, env, clusterName, namespace);
namespaceVOs.add(namespaceVO); namespaceBOs.add(namesapceBO);
} catch (Exception e) { } catch (Exception e) {
logger.error("parse namespace error. app id:{}, env:{}, clusterName:{}, namespace:{}", logger.error("parse namespace error. app id:{}, env:{}, clusterName:{}, namespace:{}",
appId, env, clusterName, namespace.getNamespaceName(), e); appId, env, clusterName, namespace.getNamespaceName(), e);
...@@ -110,25 +111,34 @@ public class NamespaceService { ...@@ -110,25 +111,34 @@ public class NamespaceService {
} }
} }
return namespaceVOs; return namespaceBOs;
} }
public NamespaceVO loadNamespace(String appId, Env env, String clusterName, String namespaceName) { public NamespaceBO loadNamespaceBO(String appId, Env env, String clusterName, String namespaceName) {
NamespaceDTO namespace = namespaceAPI.loadNamespace(appId, env, clusterName, namespaceName); NamespaceDTO namespace = namespaceAPI.loadNamespace(appId, env, clusterName, namespaceName);
if (namespace == null) { if (namespace == null) {
throw new BadRequestException("namespaces not exist"); throw new BadRequestException("namespaces not exist");
} }
return parseNamespace(appId, env, clusterName, namespace); return transformNamespace2BO(appId, env, clusterName, namespace);
} }
private NamespaceVO parseNamespace(String appId, Env env, String clusterName, NamespaceDTO namespace) { public NamespaceBO loadPublicNamespaceBO(Env env, String clusterName, String namespaceName) {
NamespaceVO namespaceVO = new NamespaceVO(); NamespaceDTO namespace = namespaceAPI.loadPublicNamespace(env, clusterName, namespaceName);
namespaceVO.setBaseInfo(namespace);
fillAppNamespaceProperties(namespaceVO); String appId = namespace.getAppId();
String actualClusterName = namespace.getClusterName();
List<NamespaceVO.ItemVO> itemVos = new LinkedList<>(); return transformNamespace2BO(appId, env, actualClusterName, namespace);
namespaceVO.setItems(itemVos); }
private NamespaceBO transformNamespace2BO(String appId, Env env, String clusterName, NamespaceDTO namespace) {
NamespaceBO namespaceBO = new NamespaceBO();
namespaceBO.setBaseInfo(namespace);
fillAppNamespaceProperties(namespaceBO);
List<ItemBO> itemBOs = new LinkedList<>();
namespaceBO.setItems(itemBOs);
String namespaceName = namespace.getNamespaceName(); String namespaceName = namespace.getNamespaceName();
...@@ -137,7 +147,7 @@ public class NamespaceService { ...@@ -137,7 +147,7 @@ public class NamespaceService {
Map<String, String> releaseItems = new HashMap<>(); Map<String, String> releaseItems = new HashMap<>();
latestRelease = releaseService.loadLatestRelease(appId, env, clusterName, namespaceName); latestRelease = releaseService.loadLatestRelease(appId, env, clusterName, namespaceName);
if (latestRelease != null) { if (latestRelease != null) {
releaseItems = gson.fromJson(latestRelease.getConfigurations(), mapType); releaseItems = gson.fromJson(latestRelease.getConfigurations(), GsonType.CONFIG);
} }
//not Release config items //not Release config items
...@@ -145,26 +155,26 @@ public class NamespaceService { ...@@ -145,26 +155,26 @@ public class NamespaceService {
int modifiedItemCnt = 0; int modifiedItemCnt = 0;
for (ItemDTO itemDTO : items) { for (ItemDTO itemDTO : items) {
NamespaceVO.ItemVO itemVO = parseItemVO(itemDTO, releaseItems); ItemBO itemBO = transformItem2BO(itemDTO, releaseItems);
if (itemVO.isModified()) { if (itemBO.isModified()) {
modifiedItemCnt++; modifiedItemCnt++;
} }
itemVos.add(itemVO); itemBOs.add(itemBO);
} }
//deleted items //deleted items
List<NamespaceVO.ItemVO> deletedItems = parseDeletedItems(items, releaseItems); List<ItemBO> deletedItems = parseDeletedItems(items, releaseItems);
itemVos.addAll(deletedItems); itemBOs.addAll(deletedItems);
modifiedItemCnt += deletedItems.size(); modifiedItemCnt += deletedItems.size();
namespaceVO.setItemModifiedCnt(modifiedItemCnt); namespaceBO.setItemModifiedCnt(modifiedItemCnt);
return namespaceVO; return namespaceBO;
} }
private void fillAppNamespaceProperties(NamespaceVO namespace) { private void fillAppNamespaceProperties(NamespaceBO namespace) {
NamespaceDTO namespaceDTO = namespace.getBaseInfo(); NamespaceDTO namespaceDTO = namespace.getBaseInfo();
//先从当前appId下面找,包含私有的和公共的 //先从当前appId下面找,包含私有的和公共的
...@@ -190,14 +200,14 @@ public class NamespaceService { ...@@ -190,14 +200,14 @@ public class NamespaceService {
namespace.setPublic(isPublic); namespace.setPublic(isPublic);
} }
private List<NamespaceVO.ItemVO> parseDeletedItems(List<ItemDTO> newItems, Map<String, String> releaseItems) { private List<ItemBO> parseDeletedItems(List<ItemDTO> newItems, Map<String, String> releaseItems) {
Map<String, ItemDTO> newItemMap = BeanUtils.mapByKey("key", newItems); Map<String, ItemDTO> newItemMap = BeanUtils.mapByKey("key", newItems);
List<NamespaceVO.ItemVO> deletedItems = new LinkedList<>(); List<ItemBO> deletedItems = new LinkedList<>();
for (Map.Entry<String, String> entry : releaseItems.entrySet()) { for (Map.Entry<String, String> entry : releaseItems.entrySet()) {
String key = entry.getKey(); String key = entry.getKey();
if (newItemMap.get(key) == null) { if (newItemMap.get(key) == null) {
NamespaceVO.ItemVO deletedItem = new NamespaceVO.ItemVO(); ItemBO deletedItem = new ItemBO();
deletedItem.setDeleted(true); deletedItem.setDeleted(true);
ItemDTO deletedItemDto = new ItemDTO(); ItemDTO deletedItemDto = new ItemDTO();
...@@ -215,19 +225,19 @@ public class NamespaceService { ...@@ -215,19 +225,19 @@ public class NamespaceService {
return deletedItems; return deletedItems;
} }
private NamespaceVO.ItemVO parseItemVO(ItemDTO itemDTO, Map<String, String> releaseItems) { private ItemBO transformItem2BO(ItemDTO itemDTO, Map<String, String> releaseItems) {
String key = itemDTO.getKey(); String key = itemDTO.getKey();
NamespaceVO.ItemVO itemVO = new NamespaceVO.ItemVO(); ItemBO itemBO = new ItemBO();
itemVO.setItem(itemDTO); itemBO.setItem(itemDTO);
String newValue = itemDTO.getValue(); String newValue = itemDTO.getValue();
String oldValue = releaseItems.get(key); String oldValue = releaseItems.get(key);
//new item or modified //new item or modified
if (!StringUtils.isEmpty(key) && (oldValue == null || !newValue.equals(oldValue))) { if (!StringUtils.isEmpty(key) && (oldValue == null || !newValue.equals(oldValue))) {
itemVO.setModified(true); itemBO.setModified(true);
itemVO.setOldValue(oldValue == null ? "" : oldValue); itemBO.setOldValue(oldValue == null ? "" : oldValue);
itemVO.setNewValue(newValue); itemBO.setNewValue(newValue);
} }
return itemVO; return itemBO;
} }
} }
...@@ -11,9 +11,9 @@ import com.ctrip.framework.apollo.core.utils.StringUtils; ...@@ -11,9 +11,9 @@ import com.ctrip.framework.apollo.core.utils.StringUtils;
import com.ctrip.framework.apollo.portal.api.AdminServiceAPI; import com.ctrip.framework.apollo.portal.api.AdminServiceAPI;
import com.ctrip.framework.apollo.portal.constant.CatEventType; import com.ctrip.framework.apollo.portal.constant.CatEventType;
import com.ctrip.framework.apollo.portal.entity.model.NamespaceReleaseModel; import com.ctrip.framework.apollo.portal.entity.model.NamespaceReleaseModel;
import com.ctrip.framework.apollo.portal.entity.vo.KVEntity; import com.ctrip.framework.apollo.portal.entity.bo.KVEntity;
import com.ctrip.framework.apollo.portal.entity.vo.ReleaseCompareResult; import com.ctrip.framework.apollo.portal.entity.vo.ReleaseCompareResult;
import com.ctrip.framework.apollo.portal.entity.vo.ReleaseVO; import com.ctrip.framework.apollo.portal.entity.bo.ReleaseBO;
import com.ctrip.framework.apollo.portal.enums.ChangeType; import com.ctrip.framework.apollo.portal.enums.ChangeType;
import com.ctrip.framework.apollo.portal.spi.UserInfoHolder; import com.ctrip.framework.apollo.portal.spi.UserInfoHolder;
import com.ctrip.framework.apollo.tracer.Tracer; import com.ctrip.framework.apollo.tracer.Tracer;
...@@ -65,7 +65,7 @@ public class ReleaseService { ...@@ -65,7 +65,7 @@ public class ReleaseService {
deleteBranch, changeSets); deleteBranch, changeSets);
} }
public List<ReleaseVO> findAllReleases(String appId, Env env, String clusterName, String namespaceName, int page, public List<ReleaseBO> findAllReleases(String appId, Env env, String clusterName, String namespaceName, int page,
int size) { int size) {
List<ReleaseDTO> releaseDTOs = releaseAPI.findAllReleases(appId, env, clusterName, namespaceName, page, size); List<ReleaseDTO> releaseDTOs = releaseAPI.findAllReleases(appId, env, clusterName, namespaceName, page, size);
...@@ -73,9 +73,9 @@ public class ReleaseService { ...@@ -73,9 +73,9 @@ public class ReleaseService {
return Collections.emptyList(); return Collections.emptyList();
} }
List<ReleaseVO> releases = new LinkedList<>(); List<ReleaseBO> releases = new LinkedList<>();
for (ReleaseDTO releaseDTO : releaseDTOs) { for (ReleaseDTO releaseDTO : releaseDTOs) {
ReleaseVO release = new ReleaseVO(); ReleaseBO release = new ReleaseBO();
release.setBaseInfo(releaseDTO); release.setBaseInfo(releaseDTO);
Set<KVEntity> kvEntities = new LinkedHashSet<>(); Set<KVEntity> kvEntities = new LinkedHashSet<>();
......
...@@ -62,6 +62,7 @@ function controller($rootScope, $scope, toastr, AppUtil, EventManager, ConfigSer ...@@ -62,6 +62,7 @@ function controller($rootScope, $scope, toastr, AppUtil, EventManager, ConfigSer
function (result) { function (result) {
$scope.namespaces = result; $scope.namespaces = result;
$('.config-item-container').removeClass('hide');
}, function (result) { }, function (result) {
toastr.error(AppUtil.errorMsg(result), "加载配置信息出错"); toastr.error(AppUtil.errorMsg(result), "加载配置信息出错");
...@@ -138,10 +139,9 @@ function controller($rootScope, $scope, toastr, AppUtil, EventManager, ConfigSer ...@@ -138,10 +139,9 @@ function controller($rootScope, $scope, toastr, AppUtil, EventManager, ConfigSer
$scope.item = _.clone(toEditItem); $scope.item = _.clone(toEditItem);
if (namespace.isBranch) { if (namespace.isBranch || namespace.isLinkedNamespace) {
var existedItem = false; var existedItem = false;
namespace.items.forEach(function (item) { namespace.items.forEach(function (item) {
//branch items contain the item
if (!item.isDeleted && item.item.key == toEditItem.key) { if (!item.isDeleted && item.item.key == toEditItem.key) {
existedItem = true; existedItem = true;
} }
...@@ -270,7 +270,7 @@ function controller($rootScope, $scope, toastr, AppUtil, EventManager, ConfigSer ...@@ -270,7 +270,7 @@ function controller($rootScope, $scope, toastr, AppUtil, EventManager, ConfigSer
$('.config-item-container').removeClass('hide');
new Clipboard('.clipboard'); new Clipboard('.clipboard');
} }
......
...@@ -40,7 +40,6 @@ function directive($window, toastr, AppUtil, EventManager, PermissionService, Na ...@@ -40,7 +40,6 @@ function directive($window, toastr, AppUtil, EventManager, PermissionService, Na
ALL: 'all' ALL: 'all'
}; };
var operate_branch_storage_key = 'OperateBranch'; var operate_branch_storage_key = 'OperateBranch';
scope.switchView = switchView; scope.switchView = switchView;
...@@ -101,6 +100,7 @@ function directive($window, toastr, AppUtil, EventManager, PermissionService, Na ...@@ -101,6 +100,7 @@ function directive($window, toastr, AppUtil, EventManager, PermissionService, Na
initNamespaceLock(namespace); initNamespaceLock(namespace);
initNamespaceInstancesCount(namespace); initNamespaceInstancesCount(namespace);
initPermission(namespace); initPermission(namespace);
initLinkedNamespace(namespace);
function initNamespaceBranch(namespace) { function initNamespaceBranch(namespace) {
NamespaceBranchService.findNamespaceBranch(scope.appId, scope.env, NamespaceBranchService.findNamespaceBranch(scope.appId, scope.env,
...@@ -221,6 +221,44 @@ function directive($window, toastr, AppUtil, EventManager, PermissionService, Na ...@@ -221,6 +221,44 @@ function directive($window, toastr, AppUtil, EventManager, PermissionService, Na
}); });
} }
function initLinkedNamespace(namespace) {
if (!namespace.isPublic || !namespace.isLinkedNamespace) {
return;
}
//load public namespace
ConfigService.load_public_namespace(scope.env, scope.cluster, namespace.baseInfo.namespaceName)
.then(function (result) {
var publicNamespace = result;
namespace.publicNamespace = publicNamespace;
var linkNamespaceItemKeys = [];
namespace.items.forEach(function (item) {
var key = item.item.key;
linkNamespaceItemKeys.push(key);
});
publicNamespace.viewItems = [];
publicNamespace.items.forEach(function (item) {
var key = item.item.key;
if (key){
publicNamespace.viewItems.push(item);
}
item.covered = linkNamespaceItemKeys.indexOf(key) >= 0;
if (item.isModified || item.isDeleted) {
publicNamespace.isModified = true;
} else if (key){
publicNamespace.hasPublishedItem = true;
}
});
});
}
function initNamespaceViewName(namespace) { function initNamespaceViewName(namespace) {
//namespace view name hide suffix //namespace view name hide suffix
namespace.viewName = namespace.viewName =
...@@ -270,7 +308,7 @@ function directive($window, toastr, AppUtil, EventManager, PermissionService, Na ...@@ -270,7 +308,7 @@ function directive($window, toastr, AppUtil, EventManager, PermissionService, Na
switchBranch(operateBranchStorage[namespaceId]); switchBranch(operateBranchStorage[namespaceId]);
} }
} }
function initNamespaceInstancesCount(namespace) { function initNamespaceInstancesCount(namespace) {
...@@ -497,11 +535,11 @@ function directive($window, toastr, AppUtil, EventManager, PermissionService, Na ...@@ -497,11 +535,11 @@ function directive($window, toastr, AppUtil, EventManager, PermissionService, Na
scope.namespace.baseInfo.namespaceName, scope.namespace.baseInfo.namespaceName,
branch.baseInfo.clusterName) branch.baseInfo.clusterName)
.then(function (result) { .then(function (result) {
if (result.appId) { if (result.appId) {
branch.rules = result; branch.rules = result;
} }
}, function (result) { }, function (result) {
toastr.error(AppUtil.errorMsg(result), "加载灰度规则出错"); toastr.error(AppUtil.errorMsg(result), "加载灰度规则出错");
}); });
...@@ -684,7 +722,7 @@ function directive($window, toastr, AppUtil, EventManager, PermissionService, Na ...@@ -684,7 +722,7 @@ function directive($window, toastr, AppUtil, EventManager, PermissionService, Na
namespace.itemCnt = itemCnt; namespace.itemCnt = itemCnt;
return result; return result;
} }
function toggleItemSearchInput(namespace) { function toggleItemSearchInput(namespace) {
namespace.showSearchInput = !namespace.showSearchInput; namespace.showSearchInput = !namespace.showSearchInput;
} }
...@@ -727,7 +765,7 @@ function directive($window, toastr, AppUtil, EventManager, PermissionService, Na ...@@ -727,7 +765,7 @@ function directive($window, toastr, AppUtil, EventManager, PermissionService, Na
var parentNamespace = branch.parentNamespace; var parentNamespace = branch.parentNamespace;
if (!parentNamespace.hasReleasePermission) { if (!parentNamespace.hasReleasePermission) {
AppUtil.showModal('#releaseNoPermissionDialog'); AppUtil.showModal('#releaseNoPermissionDialog');
}else if (branch.lockOwner && scope.user == branch.lockOwner) { } else if (branch.lockOwner && scope.user == branch.lockOwner) {
AppUtil.showModal('#releaseDenyDialog'); AppUtil.showModal('#releaseDenyDialog');
} else if (parentNamespace.itemModifiedCnt > 0) { } else if (parentNamespace.itemModifiedCnt > 0) {
AppUtil.showModal('#mergeAndReleaseDenyDialog'); AppUtil.showModal('#mergeAndReleaseDenyDialog');
...@@ -741,6 +779,10 @@ function directive($window, toastr, AppUtil, EventManager, PermissionService, Na ...@@ -741,6 +779,10 @@ function directive($window, toastr, AppUtil, EventManager, PermissionService, Na
EventManager.emit(EventManager.EventType.PRE_ROLLBACK_NAMESPACE, {namespace: namespace}); EventManager.emit(EventManager.EventType.PRE_ROLLBACK_NAMESPACE, {namespace: namespace});
} }
setTimeout(function () {
scope.namespace.show = true;
}, 200);
} }
} }
} }
......
...@@ -5,6 +5,11 @@ appService.service("ConfigService", ['$resource', '$q', function ($resource, $q) ...@@ -5,6 +5,11 @@ appService.service("ConfigService", ['$resource', '$q', function ($resource, $q)
isArray: false, isArray: false,
url: '/apps/:appId/envs/:env/clusters/:clusterName/namespaces/:namespaceName' url: '/apps/:appId/envs/:env/clusters/:clusterName/namespaces/:namespaceName'
}, },
load_public_namespace: {
method: 'GET',
isArray: false,
url: '/envs/:env/clusters/:clusterName/namespaces/:namespaceName/public'
},
load_all_namespaces: { load_all_namespaces: {
method: 'GET', method: 'GET',
isArray: true, isArray: true,
...@@ -58,6 +63,19 @@ appService.service("ConfigService", ['$resource', '$q', function ($resource, $q) ...@@ -58,6 +63,19 @@ appService.service("ConfigService", ['$resource', '$q', function ($resource, $q)
}); });
return d.promise; return d.promise;
}, },
load_public_namespace: function (env, clusterName, namespaceName) {
var d = $q.defer();
config_source.load_public_namespace({
env: env,
clusterName: clusterName,
namespaceName: namespaceName
}, function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
});
return d.promise;
},
load_all_namespaces: function (appId, env, clusterName) { load_all_namespaces: function (appId, env, clusterName) {
var d = $q.defer(); var d = $q.defer();
config_source.load_all_namespaces({ config_source.load_all_namespaces({
......
...@@ -98,6 +98,29 @@ appService.service('ReleaseService', ['$resource', '$q', function ($resource, $q ...@@ -98,6 +98,29 @@ appService.service('ReleaseService', ['$resource', '$q', function ($resource, $q
}); });
return d.promise; return d.promise;
} }
function findLatestActiveRelease(appId, env, clusterName, namespaceName) {
var d = $q.defer();
resource.find_active_releases({
appId: appId,
env: env,
clusterName: clusterName,
namespaceName: namespaceName,
page: 0,
size: 1
}, function (result) {
if (result && result.length){
d.resolve(result[0]);
}
d.resolve(undefined);
}, function (result) {
d.reject(result);
});
return d.promise;
}
function compare(env, baseReleaseId, toCompareReleaseId) { function compare(env, baseReleaseId, toCompareReleaseId) {
var d = $q.defer(); var d = $q.defer();
...@@ -133,6 +156,7 @@ appService.service('ReleaseService', ['$resource', '$q', function ($resource, $q ...@@ -133,6 +156,7 @@ appService.service('ReleaseService', ['$resource', '$q', function ($resource, $q
grayPublish: createGrayRelease, grayPublish: createGrayRelease,
findAllRelease: findAllReleases, findAllRelease: findAllReleases,
findActiveReleases: findActiveReleases, findActiveReleases: findActiveReleases,
findLatestActiveRelease: findLatestActiveRelease,
compare: compare, compare: compare,
rollback: rollback rollback: rollback
} }
......
...@@ -50,6 +50,14 @@ p, td, span { ...@@ -50,6 +50,14 @@ p, td, span {
border: solid 1px #c3c3c3; border: solid 1px #c3c3c3;
} }
.padding-top-5 {
padding-top: 5px;
}
.border-top {
border-top: 1px solid #ddd;
}
.active { .active {
background: #f5f5f5; background: #f5f5f5;
} }
...@@ -336,6 +344,7 @@ table th { ...@@ -336,6 +344,7 @@ table th {
.namespace-panel { .namespace-panel {
border-top: 0; border-top: 0;
border-bottom: 0;
} }
.namespace-panel .namespace-name { .namespace-panel .namespace-name {
...@@ -388,6 +397,10 @@ table th { ...@@ -388,6 +397,10 @@ table th {
} }
.namespace-panel .no-config-panel {
padding: 15px 0;
}
.namespace-panel .history-view { .namespace-panel .history-view {
padding: 10px 20px; padding: 10px 20px;
...@@ -724,4 +737,10 @@ table th { ...@@ -724,4 +737,10 @@ table th {
width: auto !important; width: auto !important;
} }
.search-onblur {
background: #f5f5f5;
}
.search-focus {
background: #fff;
}
<section class="branch-panel-body" ng-if="namespace.hasBranch && namespace.currentOperateBranch != 'master'">
<!--main header-->
<header class="panel-heading">
<div class="row">
<div class="col-md-6 header-namespace">
<b class="namespace-name" ng-bind="namespace.viewName" data-tooltip="tooltip"
data-placement="bottom"
title="{{namespace.comment}}"></b>
<span class="label label-info no-radius namespace-label" ng-bind="namespace.format"></span>
<span class="label label-warning no-radius namespace-label"
ng-show="namespace.branch.itemModifiedCnt > 0">有修改
<span class="badge label badge-white namespace-label"
ng-bind="namespace.branch.itemModifiedCnt"></span>
</span>
<span class="label label-primary no-radius namespace-label"
ng-show="namespace.branch.lockOwner">当前修改者:
<span ng-bind="namespace.branch.lockOwner"></span>
</span>
</div>
<div class="col-md-6 text-right header-buttons">
<a type="button" class="btn btn-success btn-sm"
data-tooltip="tooltip" data-placement="bottom" title="继续灰度发布"
ng-show="(namespace.hasReleasePermission || namespace.hasModifyPermission)"
ng-click="publish(namespace.branch)">
灰度发布
</a>
<a type="button" class="btn btn-primary btn-sm"
data-tooltip="tooltip" data-placement="bottom" title="合并到主版本并发布主版本配置"
ng-show="(namespace.hasReleasePermission || namespace.hasModifyPermission)"
ng-click="mergeAndPublish(namespace.branch)">
全量发布
</a>
<a type="button" class="btn btn-warning btn-sm"
data-tooltip="tooltip" data-placement="bottom" title="废弃灰度版本"
ng-show="(namespace.hasReleasePermission
|| (!namespace.branch.latestRelease && namespace.hasModifyPermission))"
ng-click="preDeleteBranch(namespace.branch)">
放弃灰度
</a>
</div>
</div>
</header>
<!--second header-->
<header class="panel-heading second-panel-heading">
<div class="row">
<div class="col-md-8 pull-left">
<ul class="nav nav-tabs">
<li role="presentation" ng-click="switchView(namespace.branch, 'table')"
ng-show="namespace.isPropertiesFormat">
<a ng-class="{node_active:namespace.branch.viewType == 'table'}">
<img src="img/table.png">
配置
</a>
</li>
<li role="presentation" ng-click="switchView(namespace.branch, 'rule')">
<a ng-class="{node_active:namespace.branch.viewType == 'rule'}">
<img src="img/rule.png">
灰度规则
<span class="badge badge-grey"
ng-bind="namespace.branch.grayIps.length + namespace.branch.grayApps.length"></span>
</a>
</li>
<li role="presentation" ng-click="switchView(namespace.branch, 'instance')">
<a ng-class="{node_active:namespace.branch.viewType == 'instance'}">
<img src="img/machine.png">
灰度实例列表
<span class="badge badge-grey"
ng-bind="namespace.branch.latestReleaseInstances.total"></span>
</a>
</li>
<li role="presentation" ng-click="switchView(namespace.branch, 'history')">
<a ng-class="{node_active:namespace.branch.viewType == 'history'}">
<img src="img/change.png">
更改历史
</a>
</li>
</ul>
</div>
</div>
</header>
<section>
<!--items-->
<div class="namespace-view-table" ng-show="namespace.branch.viewType == 'table'">
<div class="panel panel-default" ng-if="namespace.hasBranch">
<div class="panel-heading">
灰度的配置
<button type="button" class="btn btn-primary btn-sm pull-right" style="margin-top: -4px;"
ng-show="namespace.hasModifyPermission"
ng-click="createItem(namespace.branch)">
<img src="img/plus.png">
新增灰度配置
</button>
</div>
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>发布状态</th>
<th class="hover" title="排序"
ng-click="col='item.key';desc=!desc;">
Key&nbsp;
<span class="glyphicon glyphicon-sort"></span>
</th>
<th>
主版本的值
</th>
<th>
灰度的值
</th>
<th>
备注
</th>
<th class="hover" title="排序"
ng-click="col='item.dataChangeLastModifiedBy';desc=!desc;">
最后修改人
<span class="glyphicon glyphicon-sort"></span>
</th>
<th class="hover" title="排序"
ng-click="col='item.dataChangeLastModifiedTime';desc=!desc;">
最后修改时间
<span class="glyphicon glyphicon-sort"></span>
</th>
<th>
操作
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="config in namespace.branch.branchItems |orderBy:col:desc"
ng-if="config.item.key">
<td width="7%" class="text-center">
<span class="label label-warning no-radius cursor-pointer"
data-tooltip="tooltip" data-placement="bottom" title="点击查看已发布的值"
ng-if="config.isModified || config.isDeleted"
ng-click="showText(config.oldValue)">未发布</span>
<span class="label label-default-light no-radius"
data-tooltip="tooltip" data-placement="bottom" title="已生效的配置"
ng-if="!config.isModified">已发布</span>
</td>
<td width="15%" class="cursor-pointer" title="点击查看" ng-click="showText(config.item.key)">
<span ng-bind="config.item.key | limitTo: 250"></span>
<span ng-bind="config.item.key.length > 250 ? '...' :''"></span>
<span class="label label-danger" ng-if="config.isDeleted"
data-tooltip="tooltip" data-placement="bottom" title="删除的配置"></span>
<span class="label label-info" ng-if="!config.isDeleted && config.masterReleaseValue"
data-tooltip="tooltip" data-placement="bottom" title="修改主版本的配置"></span>
<span class="label label-success"
ng-if="!config.isDeleted && !config.masterReleaseValue"
data-tooltip="tooltip" data-placement="bottom" title="灰度版本特有的配置"></span>
</td>
<td width="20%" class="cursor-pointer" title="点击查看"
ng-click="showText(config.masterReleaseValue)">
<span ng-bind="config.masterReleaseValue | limitTo: 250"></span>
<span ng-bind="config.item.value.length > 250 ? '...': ''"></span>
</td>
<td width="20%" class="cursor-pointer" title="点击查看" ng-click="showText(config.item.value)">
<span ng-bind="config.item.value | limitTo: 250"></span>
<span ng-bind="config.item.value.length > 250 ? '...': ''"></span>
</td>
<td width="10%" title="{{config.item.comment}}">
<span ng-bind="config.item.comment | limitTo: 250"></span>
<span ng-bind="config.item.comment.length > 250 ?'...' : ''"></span>
</td>
<td width="10%" ng-bind="config.item.dataChangeLastModifiedBy">
</td>
<td width="10%"
ng-bind="config.item.dataChangeLastModifiedTime | date: 'yyyy-MM-dd HH:mm:ss'">
</td>
<td width="9%" class="text-center">
<img src="img/edit.png"
data-tooltip="tooltip" data-placement="bottom" title="修改"
ng-if="!config.isDeleted"
ng-click="editItem(namespace.branch, config.item)"
ng-show="namespace.hasModifyPermission">
<img style="margin-left: 5px;" src="img/cancel.png"
data-tooltip="tooltip" data-placement="bottom" title="删除"
ng-if="!config.isDeleted"
ng-click="preDeleteItem(namespace.branch, config.item.id)"
ng-show="namespace.hasModifyPermission">
</td>
</tr>
</tbody>
</table>
</div>
<div class="panel panel-default"
ng-if="namespace.branch.masterItems && namespace.branch.masterItems.length > 0">
<div class="panel-heading">
主版本的配置
</div>
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>发布状态</th>
<th class="hover" title="排序"
ng-click="col='item.key';desc=!desc;">
Key&nbsp;
<span class="glyphicon glyphicon-sort"></span>
</th>
<th>
Value
</th>
<th>
备注
</th>
<th class="hover" title="排序"
ng-click="col='item.dataChangeLastModifiedBy';desc=!desc;">
最后修改人
<span class="glyphicon glyphicon-sort"></span>
</th>
<th class="hover" title="排序"
ng-click="col='item.dataChangeLastModifiedTime';desc=!desc;">
最后修改时间
<span class="glyphicon glyphicon-sort"></span>
</th>
<th>
操作
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="config in namespace.branch.masterItems |orderBy:col:desc"
ng-if="config.item.key">
<td width="8%" class="text-center">
<span class="label label-warning no-radius cursor-pointer"
data-tooltip="tooltip" data-placement="bottom" title="点击查看已发布的值"
ng-if="config.isModified || config.isDeleted"
ng-click="showText(config.oldValue)">未发布</span>
<span class="label label-default-light no-radius"
data-tooltip="tooltip" data-placement="bottom" title="已生效的配置"
ng-if="!config.isModified">已发布</span>
</td>
<td width="15%" class="cursor-pointer" title="点击查看" ng-click="showText(config.item.key)">
<span ng-bind="config.item.key | limitTo: 250"></span>
<span ng-bind="config.item.key.length > 250 ? '...' :''"></span>
<span class="label label-success" ng-if="config.isModified && !config.oldValue"
data-tooltip="tooltip" data-placement="bottom" title="新增的配置"></span>
<span class="label label-info"
ng-if="config.isModified && config.oldValue && !config.isDeleted"
data-tooltip="tooltip" data-placement="bottom" title="修改的配置"></span>
<span class="label label-danger" ng-if="config.isDeleted"
data-tooltip="tooltip" data-placement="bottom" title="删除的配置"></span>
</td>
<td width="35%" class="cursor-pointer" title="点击查看" ng-click="showText(config.item.value)">
<span ng-bind="config.item.value | limitTo: 250"></span>
<span ng-bind="config.item.value.length > 250 ? '...': ''"></span>
</td>
<td width="12%" title="{{config.item.comment}}">
<span ng-bind="config.item.comment | limitTo: 250"></span>
<span ng-bind="config.item.comment.length > 250 ?'...' : ''"></span>
</td>
<td width="10%" ng-bind="config.item.dataChangeLastModifiedBy">
</td>
<td width="15%"
ng-bind="config.item.dataChangeLastModifiedTime | date: 'yyyy-MM-dd HH:mm:ss'">
</td>
<td width="5%" class="text-center">
<img src="img/gray.png"
data-tooltip="tooltip" data-placement="bottom" title="对此配置灰度"
ng-if="!config.isDeleted"
ng-click="editItem(namespace.branch, config.item)"
ng-show="namespace.hasModifyPermission">
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!--gray rules-->
<div class="rules-manage-view row" ng-show="namespace.branch.viewType == 'rule'">
<div class="alert alert-warning no-radius"
ng-show="!namespace.hasModifyPermission && !namespace.hasReleasePermission">
<strong>Tips:</strong>
您没有权限编辑灰度规则, 具有namespace修改权或者发布权的人员才可以编辑灰度规则. 如需要编辑灰度规则,请找项目管理员申请权限.
</div>
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>灰度的AppId</th>
<th>灰度的IP列表</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="ruleItem in namespace.branch.rules.ruleItems">
<td width="20%" ng-bind="ruleItem.clientAppId"></td>
<td width="70%" ng-show="!ruleItem.ApplyToAllInstances"
ng-bind="ruleItem.clientIpList.join(', ')"></td>
<td width="70%" ng-show="ruleItem.ApplyToAllInstances">ALL</td>
<td class="text-center" width="10%">
<img src="img/edit.png" class="i-20 hover"
data-tooltip="tooltip" data-placement="bottom" title="修改"
ng-show="namespace.hasModifyPermission || namespace.hasReleasePermission"
ng-click="editRuleItem(namespace.branch, ruleItem)">
<img src="img/cancel.png" class="i-20 hover" style="margin-left: 5px;"
data-tooltip="tooltip" data-placement="bottom" title="删除"
ng-show="namespace.hasModifyPermission || namespace.hasReleasePermission"
ng-click="deleteRuleItem(namespace.branch, ruleItem)">
</td>
</tr>
</tbody>
</table>
<button class="btn btn-primary"
ng-if="namespace.hasModifyPermission || namespace.hasReleasePermission"
ng-show="(namespace.isPublic && !namespace.isLinkedNamespace) ||
((!namespace.isPublic || namespace.isLinkedNamespace)
&& (!namespace.branch.rules
|| !namespace.branch.rules.ruleItems
|| !namespace.branch.rules.ruleItems.length))"
ng-click="addRuleItem(namespace.branch)">新增规则
</button>
</div>
<!--instances -->
<div class="panel panel-default" ng-show="namespace.branch.viewType == 'instance'">
<div class="panel-heading text-right">
<button class="btn btn-default btn-sm"
data-tooltip="tooltip" data-placement="bottom" title="刷新列表"
ng-click="refreshInstancesInfo(namespace.branch)">
<img src="../../img/refresh.png"/>
</button>
</div>
<div class="panel-body">
<div class="panel-default" ng-if="namespace.branch.latestReleaseInstances.total > 0">
<div class="panel-heading">
<a target="_blank" data-tooltip="tooltip" data-placement="bottom" title="查看配置"
href="/config/history.html?#/appid={{appId}}&env={{env}}&clusterName={{namespace.baseInfo.clusterName}}&namespaceName={{namespace.baseInfo.namespaceName}}&releaseId={{namespace.branch.latestRelease.id}}">
{{namespace.branch.latestRelease.name}}
</a>
</div>
<table class="table table-bordered table-striped">
<thead>
<tr>
<td>App ID</td>
<td>Cluster Name</td>
<td>Data Center</td>
<td>IP</td>
<td>配置获取时间</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="instance in namespace.branch.latestReleaseInstances.content">
<td width="20%" ng-bind="instance.appId"></td>
<td width="20%" ng-bind="instance.clusterName"></td>
<td width="20%" ng-bind="instance.dataCenter"></td>
<td width="20%" ng-bind="instance.ip"></td>
<td width="20%">{{instance.configs && instance.configs.length ?
(instance.configs[0].releaseDeliveryTime | date: 'yyyy-MM-dd HH:mm:ss') : ''}}
</td>
</tr>
</tbody>
</table>
<div class="row text-center"
ng-show="namespace.branch.latestReleaseInstances.content.length < namespace.branch.latestReleaseInstances.total">
<button class="btn btn-default" ng-click="loadInstanceInfo(namespace.branch)">加载更多</button>
</div>
</div>
<div class="text-center" ng-if="namespace.branch.latestReleaseInstances.total == 0">
无实例信息
</div>
</div>
</div>
<!--history view-->
<div class="J_historyview history-view" ng-show="namespace.branch.viewType == 'history'">
<div class="media" ng-repeat="commits in namespace.branch.commits">
<div class="media-body">
<div class="row">
<div class="col-md-6"><h3 class="media-heading"
ng-bind="commits.dataChangeCreatedBy"></h3>
</div>
<div class="col-md-6 text-right">
<h5 class="media-heading"
ng-bind="commits.dataChangeCreatedTime | date: 'yyyy-MM-dd HH:mm:ss'"></h5>
</div>
</div>
<!--properties format-->
<table class="table table-bordered table-striped text-center table-hover"
style="margin-top: 5px;"
ng-if="namespace.isPropertiesFormat">
<thead>
<tr>
<th>
Type
</th>
<th>
Key
</th>
<th>
Old Value
</th>
<th>
New Value
</th>
<th>
Comment
</th>
</tr>
</thead>
<tbody>
<!--兼容老数据,不显示item类型为空行和注释的item-->
<tr ng-repeat="item in commits.changeSets.createItems" ng-show="item.key">
<td width="2%">
新增
</td>
<td width="20%" title="{{item.key}}">
<span ng-bind="item.key | limitTo: 250"></span>
<span ng-bind="item.key.length > 250 ? '...' :''"></span>
</td>
<td width="30%">
</td>
<td width="30%" class="cursor-pointer" title="{{item.value}}"
ng-click="showText(item.value)">
<span ng-bind="item.value | limitTo: 250"></span>
<span ng-bind="item.value.length > 250 ? '...': ''"></span>
</td>
<td width="18%" title="{{item.comment}}">
<span ng-bind="item.comment | limitTo: 250"></span>
<span ng-bind="item.comment.length > 250 ?'...' : ''"></span>
</td>
</tr>
<tr ng-repeat="item in commits.changeSets.updateItems">
<td width="2%">
更新
</td>
<td width="20%" title="{{item.newItem.key}}">
<span ng-bind="item.newItem.key | limitTo: 250"></span>
<span ng-bind="item.newItem.key.length > 250 ? '...' :''"></span>
</td>
<td width="30%" class="cursor-pointer" title="{{item.oldItem.value}}"
ng-click="showText(item.oldItem.value)">
<span ng-bind="item.oldItem.value | limitTo: 250"></span>
<span ng-bind="item.oldItem.value.length > 250 ? '...': ''"></span>
</td>
<td width="30%" class="cursor-pointer" title="{{item.newItem.value}}"
ng-click="showText(item.newItem.value)">
<span ng-bind="item.newItem.value | limitTo: 250"></span>
<span ng-bind="item.newItem.value.length > 250 ? '...': ''"></span>
</td>
<td width="18%" title="{{item.newItem.comment}}">
<span ng-bind="item.newItem.comment | limitTo: 250"></span>
<span ng-bind="item.newItem.comment.length > 250 ?'...' : ''"></span>
</td>
</tr>
<tr ng-repeat="item in commits.changeSets.deleteItems"
ng-show="item.key || item.comment">
<td width="2%">
删除
</td>
<td width="20%" title="{{item.key}}">
<span ng-bind="item.key | limitTo: 250"></span>
<span ng-bind="item.key.length > 250 ? '...' :''"></span>
</td>
<td width="30%" title="{{item.value}}">
<span ng-bind="item.value | limitTo: 250"></span>
<span ng-bind="item.value.length > 250 ? '...': ''"></span>
</td>
<td width="30%">
</td>
<td width="18%" title="{{item.comment}}">
<span ng-bind="item.comment | limitTo: 250"></span>
<span ng-bind="item.comment.length > 250 ?'...' : ''"></span>
</td>
</tr>
</tbody>
</table>
<!--not properties format-->
<div ng-if="!namespace.isPropertiesFormat">
<div ng-repeat="item in commits.changeSets.createItems">
<textarea class="form-control no-radius" rows="20"
ng-disabled="true" ng-bind="item.value">
</textarea>
</div>
<div ng-repeat="item in commits.changeSets.updateItems">
<textarea class="form-control no-radius" rows="20"
ng-disabled="true" ng-bind="item.newItem.value">
</textarea>
</div>
</div>
</div>
<hr>
</div>
<div class="text-center">
<button type="button" class="btn btn-default" ng-show="!namespace.branch.hasLoadAllCommit"
ng-click="loadCommitHistory(namespace.branch)">加载更多
<span class="glyphicon glyphicon-menu-down"></span></button>
</div>
</div>
</section>
</section>
<header class="row namespace-attribute-panel">
<div class="text-center namespace-attribute-public">
<span data-tooltip="tooltip" data-placement="bottom"
title="私有namespace({{namespace.baseInfo.namespaceName}})的配置只能被AppId为{{appId}}的客户端读取到"
ng-show="!namespace.isPublic">私有</span>
<span data-tooltip="tooltip" data-placement="top"
title="namespace({{namespace.baseInfo.namespaceName}})的配置能被任何客户端读取到"
ng-show="namespace.isPublic && namespace.parentAppId == namespace.baseInfo.appId">公共</span>
<span data-tooltip="tooltip" data-placement="top"
title="namespace({{namespace.baseInfo.namespaceName}})的配置将会覆盖公共namespace的配置, 且合并之后的配置只能被AppId为{{appId}}的客户端读取到"
ng-show="namespace.isPublic && namespace.isLinkedNamespace"
ng-click="goToParentAppConfigPage(namespace)">关联</span>
</div>
</header>
<!--branch nav-->
<header class="panel-heading second-panel-heading" ng-show="namespace.hasBranch">
<div class="row">
<div class="col-md-8 pull-left">
<ul class="nav nav-tabs">
<li role="presentation">
<a ng-class="{'node_active': namespace.currentOperateBranch == 'master'}"
ng-click="switchBranch('master')">
<img src="img/branch.png">
主版本
</a>
</li>
<li role="presentation">
<a ng-class="{'node_active': namespace.currentOperateBranch != 'master'}"
ng-click="switchBranch(namespace.branchName)">
<img src="img/branch.png">
灰度版本
</a>
</li>
</ul>
</div>
</div>
</header>
<!--master panel body-->
<section class="master-panel-body"
ng-if="namespace.hasBranch
&& namespace.currentOperateBranch == 'master' ||
!namespace.hasBranch">
<!--main header-->
<header class="panel-heading">
<div class="row">
<div class="col-md-6 header-namespace">
<b class="namespace-name" ng-bind="namespace.viewName"
data-tooltip="tooltip" data-placement="bottom" title="{{namespace.comment}}"></b>
<span class="label label-info no-radius namespace-label" ng-bind="namespace.format"></span>
<span class="label label-warning no-radius namespace-label" ng-show="namespace.itemModifiedCnt > 0">有修改
<span class="badge label badge-white namespace-label" ng-bind="namespace.itemModifiedCnt"></span>
</span>
<span class="label label-primary no-radius namespace-label"
ng-show="namespace.lockOwner">当前修改者:{{namespace.lockOwner}}</span>
</div>
<div class="col-md-6 text-right header-buttons">
<button type="button" class="btn btn-success btn-sm"
data-tooltip="tooltip" data-placement="bottom" title="发布配置"
ng-show="(namespace.hasReleasePermission || namespace.hasModifyPermission)"
ng-disabled="namespace.isTextEditing"
ng-click="publish(namespace)">
<img src="img/release.png">
发布
</button>
<button type="button" class="btn btn-default btn-sm"
data-tooltip="tooltip" data-placement="bottom" title="回滚已发布配置"
ng-show="namespace.hasReleasePermission"
ng-click="rollback(namespace)">
<img src="img/rollback.png">
回滚
</button>
<a type="button" class="btn btn-default btn-sm"
data-tooltip="tooltip" data-placement="bottom" title="查看发布历史"
href="/config/history.html?#/appid={{appId}}&env={{env}}&clusterName={{cluster}}&namespaceName={{namespace.baseInfo.namespaceName}}">
<img src="img/release-history.png">
发布历史
</a>
<a type="button" class="btn btn-default btn-sm"
data-tooltip="tooltip" data-placement="bottom" title="配置修改、发布权限"
href="/namespace/role.html?#/appid={{appId}}&namespaceName={{namespace.baseInfo.namespaceName}}"
ng-show="hasAssignUserPermission">
<img src="img/assign.png">
授权
</a>
<a type="button" class="btn btn-default btn-sm"
data-tooltip="tooltip" data-placement="bottom" title="创建测试版本"
ng-show="!namespace.hasBranch && namespace.isPropertiesFormat && namespace.hasModifyPermission"
ng-click="preCreateBranch(namespace)">
<img src="img/test.png">
创建灰度
</a>
<a type="button" class="btn btn-default btn-sm J_tableview_btn"
data-tooltip="tooltip" data-placement="bottom" title="您没有任何配置权限,请申请"
ng-click="showNoModifyPermissionDialog()"
ng-show="!namespace.hasModifyPermission && !namespace.hasReleasePermission">
申请配置权限
</a>
</div>
</div>
</header>
<!--second header-->
<header class="panel-heading second-panel-heading">
<div class="row">
<div class="col-md-8 pull-left">
<!--master branch nav tabs-->
<ul class="nav nav-tabs">
<li role="presentation" ng-click="switchView(namespace, 'table')"
ng-show="namespace.isPropertiesFormat">
<a ng-class="{node_active:namespace.viewType == 'table'}">
<img src="img/table.png">
表格
</a>
</li>
<li role="presentation"
ng-click="switchView(namespace, 'text')">
<a ng-class="{node_active:namespace.viewType == 'text'}">
<img src="img/text.png">
文本
</a>
</li>
<li role="presentation" ng-click="switchView(namespace, 'history')">
<a ng-class="{node_active:namespace.viewType == 'history'}">
<img src="img/change.png">
更改历史
</a>
</li>
<li role="presentation" ng-click="switchView(namespace, 'instance')">
<a ng-class="{node_active:namespace.viewType == 'instance'}">
<img src="img/machine.png">
实例列表
<span class="badge badge-grey" ng-bind="namespace.instancesCount"></span>
</a>
</li>
</ul>
</div>
<div class="col-md-4 text-right">
<a class="clipboard"
data-clipboard-text="{{namespace.text}}"
data-tooltip="tooltip" data-placement="bottom" title="复制文本"
ng-show="!namespace.isTextEditing && namespace.viewType == 'text' && namespace.hasModifyPermission">
<img src="img/copy.png" class="ns_btn">
</a>
&nbsp;
<a data-tooltip="tooltip" data-placement="bottom" title="取消修改"
ng-show="namespace.isTextEditing && namespace.viewType == 'text'"
ng-click="toggleTextEditStatus(namespace)">
<img src="img/cancel.png" class="ns_btn">
</a>
<a data-tooltip="tooltip" data-placement="bottom" title="修改配置"
ng-show="!namespace.isTextEditing && namespace.viewType == 'text' && namespace.hasModifyPermission"
ng-click="toggleTextEditStatus(namespace)">
<img src="img/edit.png" class="ns_btn">
</a>
&nbsp;
<a data-tooltip="tooltip" data-placement="bottom" title="提交修改"
data-toggle="modal" data-target="#commitModal"
ng-show="namespace.isTextEditing && namespace.viewType == 'text'"
ng-click="modifyByText(namespace)">
<img src="img/submit.png" class="ns_btn">
</a>
<button type="button" class="btn btn-default btn-sm"
data-tooltip="tooltip" data-placement="bottom" title="按Key过滤配置"
ng-show="namespace.viewType == 'table' && namespace.currentOperateBranch == 'master'
&& !namespace.isLinkedNamespace"
ng-click="toggleItemSearchInput(namespace)">
<span class="glyphicon glyphicon-filter"></span>
过滤配置
</button>
<button type="button" class="btn btn-default btn-sm J_tableview_btn"
data-tooltip="tooltip" data-placement="bottom" title="同步各环境间配置"
ng-click="goToSyncPage(namespace)"
ng-show="namespace.viewType == 'table' && namespace.currentOperateBranch == 'master'
&& namespace.hasModifyPermission && namespace.isPropertiesFormat">
<img src="img/sync.png">
同步配置
</button>
<button type="button" class="btn btn-primary btn-sm"
ng-show="!namespace.isLinkedNamespace
&& namespace.viewType == 'table'
&& namespace.currentOperateBranch == 'master'
&& namespace.hasModifyPermission"
ng-click="createItem(namespace)">
<img src="img/plus.png">
新增配置
</button>
</div>
</div>
</header>
<section>
<!--table view-->
<div class="namespace-view-table" ng-show="namespace.viewType == 'table'">
<!--not link namespace-->
<div ng-if="!namespace.isLinkedNamespace">
<div class="col-md-8 col-lg-offset-2 search-input" ng-show="namespace.showSearchInput">
<input type="text" class="form-control" placeholder="输入key过滤"
ng-model="namespace.searchKey"
ng-change="searchItems(namespace)">
</div>
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>发布状态</th>
<th class="hover" title="排序"
ng-click="col='item.key';desc=!desc;">
Key&nbsp;
<span class="glyphicon glyphicon-sort"></span>
</th>
<th>
Value
</th>
<th>
备注
</th>
<th class="hover" title="排序"
ng-click="col='item.dataChangeLastModifiedBy';desc=!desc;">
最后修改人
<span class="glyphicon glyphicon-sort"></span>
</th>
<th class="hover" title="排序"
ng-click="col='item.dataChangeLastModifiedTime';desc=!desc;">
最后修改时间
<span class="glyphicon glyphicon-sort"></span>
</th>
<th>
操作
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="config in namespace.viewItems |orderBy:col:desc"
ng-if="config.item.key">
<td width="8%" class="text-center">
<span class="label label-warning no-radius cursor-pointer" ng-if="config.isModified"
data-tooltip="tooltip" data-placement="bottom" title="点击查看已发布的值"
ng-click="showText(config.oldValue?config.oldValue:'新增的配置,无发布的值')">未发布</span>
<span class="label label-default-light no-radius"
data-tooltip="tooltip" data-placement="bottom" title="已生效的配置"
ng-if="!config.isModified">已发布</span>
</td>
<td width="15%" class="cursor-pointer" title="点击查看" ng-click="showText(config.item.key)">
<span ng-bind="config.item.key | limitTo: 250"></span>
<span ng-bind="config.item.key.length > 250 ? '...' :''"></span>
<span class="label label-default cursor-pointer" ng-if="config.hasBranchValue"
data-tooltip="tooltip" data-placement="bottom" title="该配置有灰度配置,点击查看灰度的值"
ng-click="namespace.currentOperateBranch=namespace.branchName;namespace.branch.viewType='table'"></span>
<span class="label label-success" ng-if="config.isModified && !config.oldValue"
data-tooltip="tooltip" data-placement="bottom" title="新增的配置"></span>
<span class="label label-info"
ng-if="config.isModified && config.oldValue && !config.isDeleted"
data-tooltip="tooltip" data-placement="bottom" title="修改的配置"></span>
<span class="label label-danger" ng-if="config.isDeleted"
data-tooltip="tooltip" data-placement="bottom" title="删除的配置"></span>
</td>
<td width="30%" class="cursor-pointer" title="点击查看" ng-click="showText(config.item.value)">
<span ng-bind="config.item.value | limitTo: 250"></span>
<span ng-bind="config.item.value.length > 250 ? '...': ''"></span>
</td>
<td width="13%" title="{{config.item.comment}}">
<span ng-bind="config.item.comment | limitTo: 250"></span>
<span ng-bind="config.item.comment.length > 250 ?'...' : ''"></span>
</td>
<td width="10%" ng-bind="config.item.dataChangeLastModifiedBy">
</td>
<td width="16%"
ng-bind="config.item.dataChangeLastModifiedTime | date: 'yyyy-MM-dd HH:mm:ss'">
</td>
<td width="8%" class="text-center" ng-if="!config.isDeleted">
<img src="img/edit.png" data-tooltip="tooltip" data-placement="bottom" title="修改"
ng-click="editItem(namespace, config.item)"
ng-show="namespace.hasModifyPermission">
<img style="margin-left: 5px;" src="img/cancel.png"
data-tooltip="tooltip" data-placement="bottom" title="删除"
ng-click="preDeleteItem(namespace, config.item.id)"
ng-show="namespace.hasModifyPermission">
</td>
<td width="6%" class="text-center" ng-if="config.isDeleted">
</td>
</tr>
</tbody>
</table>
</div>
<!--link namespace-->
<div class="panel panel-default" ng-if="namespace.isLinkedNamespace">
<div class="panel-heading">
<div class="row">
<div class="padding-top-5 col-md-4">
覆盖的配置
</div>
<div class="col-md-2 col-lg-offset-6 text-right">
<input type="text" class="form-control" placeholder="filter by key ..."
ng-class="{'search-onblur': namespace.searchStatus == 'OFF' || !namespace.searchStatus,
'search-focus': namespace.searchStatus == 'ON'}"
ng-model="namespace.searchKey"
ng-change="searchItems(namespace)"
ng-focus="namespace.searchStatus='ON'"
ng-blur="namespace.searchStatus='OFF'">
</div>
</div>
</div>
<table class="table table-bordered table-striped table-hover"
ng-if="namespace.viewItems && namespace.viewItems.length">
<thead>
<tr>
<th>发布状态</th>
<th class="hover" title="排序"
ng-click="col='item.key';desc=!desc;">
Key&nbsp;
<span class="glyphicon glyphicon-sort"></span>
</th>
<th>
Value
</th>
<th>
备注
</th>
<th class="hover" title="排序"
ng-click="col='item.dataChangeLastModifiedBy';desc=!desc;">
最后修改人
<span class="glyphicon glyphicon-sort"></span>
</th>
<th class="hover" title="排序"
ng-click="col='item.dataChangeLastModifiedTime';desc=!desc;">
最后修改时间
<span class="glyphicon glyphicon-sort"></span>
</th>
<th>
操作
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="config in namespace.viewItems |orderBy:col:desc"
ng-if="config.item.key">
<td width="8%" class="text-center">
<span class="label label-warning no-radius cursor-pointer" ng-if="config.isModified"
data-tooltip="tooltip" data-placement="bottom" title="点击查看已发布的值"
ng-click="showText(config.oldValue?config.oldValue:'新增的配置,无发布的值')">未发布</span>
<span class="label label-default-light no-radius"
data-tooltip="tooltip" data-placement="bottom" title="已生效的配置"
ng-if="!config.isModified">已发布</span>
</td>
<td width="15%" class="cursor-pointer" title="点击查看" ng-click="showText(config.item.key)">
<span ng-bind="config.item.key | limitTo: 250"></span>
<span ng-bind="config.item.key.length > 250 ? '...' :''"></span>
<span class="label label-default cursor-pointer" ng-if="config.hasBranchValue"
data-tooltip="tooltip" data-placement="bottom" title="该配置有灰度配置,点击查看灰度的值"
ng-click="namespace.currentOperateBranch=namespace.branchName;namespace.branch.viewType='table'"></span>
<span class="label label-success" ng-if="config.isModified && !config.oldValue"
data-tooltip="tooltip" data-placement="bottom" title="新增的配置"></span>
<span class="label label-info"
ng-if="config.isModified && config.oldValue && !config.isDeleted"
data-tooltip="tooltip" data-placement="bottom" title="修改的配置"></span>
<span class="label label-danger" ng-if="config.isDeleted"
data-tooltip="tooltip" data-placement="bottom" title="删除的配置"></span>
</td>
<td width="30%" class="cursor-pointer" title="点击查看" ng-click="showText(config.item.value)">
<span ng-bind="config.item.value | limitTo: 250"></span>
<span ng-bind="config.item.value.length > 250 ? '...': ''"></span>
</td>
<td width="13%" title="{{config.item.comment}}">
<span ng-bind="config.item.comment | limitTo: 250"></span>
<span ng-bind="config.item.comment.length > 250 ?'...' : ''"></span>
</td>
<td width="10%" ng-bind="config.item.dataChangeLastModifiedBy">
</td>
<td width="16%"
ng-bind="config.item.dataChangeLastModifiedTime | date: 'yyyy-MM-dd HH:mm:ss'">
</td>
<td width="8%" class="text-center" ng-if="!config.isDeleted">
<img src="img/edit.png" data-tooltip="tooltip" data-placement="bottom" title="修改"
ng-click="editItem(namespace, config.item)"
ng-show="namespace.hasModifyPermission">
<img style="margin-left: 5px;" src="img/cancel.png"
data-tooltip="tooltip" data-placement="bottom" title="删除"
ng-click="preDeleteItem(namespace, config.item.id)"
ng-show="namespace.hasModifyPermission">
</td>
<td width="6%" class="text-center" ng-if="config.isDeleted">
</td>
</tr>
</tbody>
</table>
<div class="text-center no-config-panel"
ng-if="!namespace.viewItems || !namespace.viewItems.length">
<h5>无覆盖的配置</h5>
</div>
</div>
<!--link namespace's public namespace-->
<div class="panel panel-default" ng-if="namespace.isLinkedNamespace">
<div class="panel-heading">
<div class="row">
<div class="padding-top-5 col-md-4">
公共的配置
<a href="/config.html?#/appid={{namespace.publicNamespace.baseInfo.appId}}" target="_blank">
<small>
(AppId:{{namespace.publicNamespace.baseInfo.appId}},
Cluster:{{namespace.publicNamespace.baseInfo.clusterName}})
</small>
</a>
</div>
<div class="col-md-4 text-center">
<div class="btn-group btn-group-sm" role="group" ng-show="namespace.publicNamespace.isModified">
<button type="button" class="btn btn-default"
ng-class="{'active':namespace.publicNamespaceViewType == 'RELEASE'
|| !namespace.publicNamespaceViewType}"
ng-click="namespace.publicNamespaceViewType = 'RELEASE'">
已发布的配置
</button>
<button type="button" class="btn btn-default"
ng-class="{'active':namespace.publicNamespaceViewType == 'NOT_RELEASE'}"
ng-click="namespace.publicNamespaceViewType = 'NOT_RELEASE'">
未发布的配置
</button>
</div>
</div>
<div class="col-md-2 col-lg-offset-2 text-right">
<input type="text" class="form-control" placeholder="filter by key ..."
ng-class="{'search-onblur': namespace.publicNamespace.searchStatus == 'OFF'
|| !namespace.publicNamespace.searchStatus,
'search-focus': namespace.publicNamespace.searchStatus == 'ON'}"
ng-model="namespace.publicNamespace.searchKey"
ng-change="searchItems(namespace.publicNamespace)"
ng-blur="namespace.publicNamespace.searchStatus='OFF'"
ng-focus="namespace.publicNamespace.searchStatus='ON'"/>
</div>
</div>
</div>
<!--published items-->
<div ng-show="!namespace.publicNamespaceViewType || namespace.publicNamespaceViewType == 'RELEASE'">
<table class="table table-bordered table-striped table-hover"
ng-show="namespace.publicNamespace.hasPublishedItem">
<thead>
<tr>
<th class="hover" title="排序"
ng-click="col='item.key';desc=!desc;">
Key&nbsp;
<span class="glyphicon glyphicon-sort"></span>
</th>
<th>
Value
</th>
<th>
备注
</th>
<th class="hover" title="排序"
ng-click="col='item.dataChangeLastModifiedBy';desc=!desc;">
最后修改人
<span class="glyphicon glyphicon-sort"></span>
</th>
<th class="hover" title="排序"
ng-click="col='item.dataChangeLastModifiedTime';desc=!desc;">
最后修改时间
<span class="glyphicon glyphicon-sort"></span>
</th>
<th>
操作
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="config in namespace.publicNamespace.viewItems |orderBy:col:desc"
ng-if="config.item.key && !config.isModified && !config.isDeleted">
<td width="15%" class="cursor-pointer" title="点击查看" ng-click="showText(config.item.key)">
<span ng-bind="config.item.key | limitTo: 250"></span>
<span ng-bind="config.item.key.length > 250 ? '...' :''"></span>
</td>
<td width="35%" class="cursor-pointer" title="点击查看" ng-click="showText(config.item.value)">
<span ng-bind="config.item.value | limitTo: 250"></span>
<span ng-bind="config.item.value.length > 250 ? '...': ''"></span>
</td>
<td width="15%" title="{{config.item.comment}}">
<span ng-bind="config.item.comment | limitTo: 250"></span>
<span ng-bind="config.item.comment.length > 250 ?'...' : ''"></span>
</td>
<td width="10%" ng-bind="config.item.dataChangeLastModifiedBy">
</td>
<td width="15%"
ng-bind="config.item.dataChangeLastModifiedTime | date: 'yyyy-MM-dd HH:mm:ss'">
</td>
<td width="10%" class="text-center" ng-if="!config.isDeleted">
<img src="img/gray.png" data-tooltip="tooltip" data-placement="bottom" title="覆盖此配置"
ng-click="editItem(namespace, config.item)"
ng-show="namespace.hasModifyPermission && !config.covered">
</td>
<td width="6%" class="text-center" ng-if="config.isDeleted">
</td>
</tr>
</tbody>
</table>
<div class="text-center no-config-panel"
ng-if="namespace.publicNamespace.viewItems
&& namespace.publicNamespace.viewItems.length
&& !namespace.publicNamespace.hasPublishedItem">
<h5>无发布的配置</h5>
</div>
</div>
<!--not published items-->
<table class="table table-bordered table-striped table-hover"
ng-show="namespace.publicNamespaceViewType == 'NOT_RELEASE'">
<thead>
<tr>
<th class="hover" title="排序"
ng-click="col='item.key';desc=!desc;">
Key&nbsp;
<span class="glyphicon glyphicon-sort"></span>
</th>
<th>
Old Value
</th>
<th>
New Value
</th>
<th>
备注
</th>
<th class="hover" title="排序"
ng-click="col='item.dataChangeLastModifiedTime';desc=!desc;">
最后修改时间
<span class="glyphicon glyphicon-sort"></span>
</th>
<th>
操作
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="config in namespace.publicNamespace.viewItems |orderBy:col:desc"
ng-if="config.item.key && (config.isModified || config.isDeleted)">
<td width="20%" class="cursor-pointer" title="点击查看" ng-click="showText(config.item.key)">
<span ng-bind="config.item.key | limitTo: 250"></span>
<span ng-bind="config.item.key.length > 250 ? '...' :''"></span>
<span class="label label-success" ng-if="config.isModified && !config.oldValue"
data-tooltip="tooltip" data-placement="bottom" title="新增的配置"></span>
<span class="label label-info"
ng-if="config.isModified && config.oldValue && !config.isDeleted"
data-tooltip="tooltip" data-placement="bottom" title="修改的配置"></span>
<span class="label label-danger" ng-if="config.isDeleted"
data-tooltip="tooltip" data-placement="bottom" title="删除的配置"></span>
</td>
<td width="25%" class="cursor-pointer" title="点击查看" ng-click="showText(config.oldValue)">
<span ng-bind="config.oldValue | limitTo: 250"></span>
<span ng-bind="config.oldValue.length > 250 ? '...': ''"></span>
</td>
<td width="25%" class="cursor-pointer" title="点击查看" ng-click="showText(config.item.value)">
<span ng-bind="config.item.value | limitTo: 250"></span>
<span ng-bind="config.item.value.length > 250 ? '...': ''"></span>
</td>
<td width="10%" title="{{config.item.comment}}">
<span ng-bind="config.item.comment | limitTo: 250"></span>
<span ng-bind="config.item.comment.length > 250 ?'...' : ''"></span>
</td>
<td width="15%"
ng-bind="config.item.dataChangeLastModifiedTime | date: 'yyyy-MM-dd HH:mm:ss'">
</td>
<td width="5%" class="text-center" ng-if="!config.isDeleted">
<img src="img/gray.png" data-tooltip="tooltip" data-placement="bottom" title="覆盖此配置"
ng-click="editItem(namespace, config.item)"
ng-show="namespace.hasModifyPermission && !config.covered">
</td>
</tr>
</tbody>
</table>
<div class="text-center no-config-panel"
ng-if="!namespace.publicNamespace.viewItems || !namespace.publicNamespace.viewItems.length">
<h5>无公共的配置</h5>
</div>
</div>
</div>
<!--text view-->
<!--只读模式下的文本内容,不替换换行符-->
<textarea class="form-control no-radius"
rows="{{namespace.itemCnt < 10 ? 10: namespace.itemCnt>20 ? 20:namespace.itemCnt}}"
ng-show="namespace.viewType == 'text' && !namespace.isTextEditing"
ng-disabled="!namespace.isTextEditing"
ng-bind="namespace.text">
</textarea>
<!--编辑状态下的文本内容,会过滤掉换行符-->
<textarea class="form-control no-radius"
rows="{{namespace.itemCnt < 10 ? 10: namespace.itemCnt>20 ? 20:namespace.itemCnt}}"
ng-show="namespace.viewType == 'text' && namespace.isTextEditing"
ng-disabled="!namespace.isTextEditing" ng-model="namespace.editText">
</textarea>
<!--history view-->
<div class="J_historyview history-view" ng-show="namespace.viewType == 'history'">
<div class="media" ng-repeat="commits in namespace.commits">
<div class="media-body">
<div class="row">
<div class="col-md-6"><h3 class="media-heading" ng-bind="commits.dataChangeCreatedBy"></h3>
</div>
<div class="col-md-6 text-right"><h5 class="media-heading"
ng-bind="commits.dataChangeCreatedTime | date: 'yyyy-MM-dd HH:mm:ss'"></h5>
</div>
</div>
<!--properties format-->
<table class="table table-bordered table-striped text-center table-hover"
style="margin-top: 5px;"
ng-if="namespace.isPropertiesFormat">
<thead>
<tr>
<th>
Type
</th>
<th>
Key
</th>
<th>
Old Value
</th>
<th>
New Value
</th>
<th>
Comment
</th>
</tr>
</thead>
<tbody>
<!--兼容老数据,不显示item类型为空行和注释的item-->
<tr ng-repeat="item in commits.changeSets.createItems" ng-show="item.key">
<td width="2%">
新增
</td>
<td width="20%" title="{{item.key}}">
<span ng-bind="item.key | limitTo: 250"></span>
<span ng-bind="item.key.length > 250 ? '...' :''"></span>
</td>
<td width="30%">
</td>
<td width="30%" class="cursor-pointer" title="{{item.value}}"
ng-click="showText(item.value)">
<span ng-bind="item.value | limitTo: 250"></span>
<span ng-bind="item.value.length > 250 ? '...': ''"></span>
</td>
<td width="18%" title="{{item.comment}}">
<span ng-bind="item.comment | limitTo: 250"></span>
<span ng-bind="item.comment.length > 250 ?'...' : ''"></span>
</td>
</tr>
<tr ng-repeat="item in commits.changeSets.updateItems">
<td width="2%">
更新
</td>
<td width="20%" title="{{item.newItem.key}}">
<span ng-bind="item.newItem.key | limitTo: 250"></span>
<span ng-bind="item.newItem.key.length > 250 ? '...' :''"></span>
</td>
<td width="30%" class="cursor-pointer" title="{{item.oldItem.value}}"
ng-click="showText(item.oldItem.value)">
<span ng-bind="item.oldItem.value | limitTo: 250"></span>
<span ng-bind="item.oldItem.value.length > 250 ? '...': ''"></span>
</td>
<td width="30%" class="cursor-pointer" title="{{item.newItem.value}}"
ng-click="showText(item.newItem.value)">
<span ng-bind="item.newItem.value | limitTo: 250"></span>
<span ng-bind="item.newItem.value.length > 250 ? '...': ''"></span>
</td>
<td width="18%" title="{{item.newItem.comment}}">
<span ng-bind="item.newItem.comment | limitTo: 250"></span>
<span ng-bind="item.newItem.comment.length > 250 ?'...' : ''"></span>
</td>
</tr>
<tr ng-repeat="item in commits.changeSets.deleteItems"
ng-show="item.key || item.comment">
<td width="2%">
删除
</td>
<td width="20%" title="{{item.key}}">
<span ng-bind="item.key | limitTo: 250"></span>
<span ng-bind="item.key.length > 250 ? '...' :''"></span>
</td>
<td width="30%" title="{{item.value}}">
<span ng-bind="item.value | limitTo: 250"></span>
<span ng-bind="item.value.length > 250 ? '...': ''"></span>
</td>
<td width="30%">
</td>
<td width="18%" title="{{item.comment}}">
<span ng-bind="item.comment | limitTo: 250"></span>
<span ng-bind="item.comment.length > 250 ?'...' : ''"></span>
</td>
</tr>
</tbody>
</table>
<!--not properties format-->
<div ng-if="!namespace.isPropertiesFormat">
<div ng-repeat="item in commits.changeSets.createItems">
<textarea class="form-control no-radius" rows="20"
ng-disabled="true" ng-bind="item.value">
</textarea>
</div>
<div ng-repeat="item in commits.changeSets.updateItems">
<textarea class="form-control no-radius" rows="20"
ng-disabled="true" ng-bind="item.newItem.value">
</textarea>
</div>
</div>
</div>
<hr>
</div>
<div class="text-center">
<button type="button" class="btn btn-default" ng-show="!namespace.hasLoadAllCommit"
ng-click="loadCommitHistory(namespace)">加载更多
<span class="glyphicon glyphicon-menu-down"></span></button>
</div>
</div>
<!--instance view-->
<div class="panel panel-default instance-view" ng-show="namespace.viewType == 'instance'">
<div class="panel-heading">
<div class="row text-right" style="padding-right: 15px;">
<div class="btn-group btn-group-sm" role="group">
<button type="button" class="btn btn-default"
ng-class="{'btn-primary':namespace.instanceViewType == 'latest_release'}"
ng-click="switchInstanceViewType(namespace, 'latest_release')"> 使用最新配置的实例
<span class="badge" ng-bind="namespace.latestReleaseInstances.total"></span>
</button>
<button type="button" class="btn btn-default"
ng-class="{'btn-primary':namespace.instanceViewType == 'not_latest_release'}"
ng-click="switchInstanceViewType(namespace, 'not_latest_release')">使用非最新配置的实例
<span class="badge"
ng-bind="namespace.instancesCount - namespace.latestReleaseInstances.total"></span>
</button>
<button type="button" class="btn btn-default"
ng-class="{'btn-primary':namespace.instanceViewType == 'all'}"
ng-click="switchInstanceViewType(namespace, 'all')">所有实例
<span class="badge" ng-bind="namespace.instancesCount"></span>
</button>
</div>
<button class="btn btn-default btn-sm"
data-tooltip="tooltip" data-placement="bottom" title="刷新列表"
ng-click="refreshInstancesInfo(namespace)">
<img src="../../img/refresh.png"/>
</button>
</div>
</div>
<!--latest release instances-->
<div class="panel-body" ng-show="namespace.instanceViewType == 'latest_release'">
<div class="panel-default" ng-if="namespace.latestReleaseInstances.total > 0">
<div class="panel-heading">
<a target="_blank" data-tooltip="tooltip" data-placement="bottom" title="查看配置"
href="/config/history.html?#/appid={{appId}}&env={{env}}&clusterName={{cluster}}&namespaceName={{namespace.baseInfo.namespaceName}}&releaseId={{namespace.latestRelease.id}}">
{{namespace.latestRelease.name}}
</a>
</div>
<table class="table table-bordered table-striped">
<thead>
<tr>
<td>App ID</td>
<td>Cluster Name</td>
<td>Data Center</td>
<td>IP</td>
<td>配置获取时间</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="instance in namespace.latestReleaseInstances.content">
<td width="20%" ng-bind="instance.appId"></td>
<td width="20%" ng-bind="instance.clusterName"></td>
<td width="20%" ng-bind="instance.dataCenter"></td>
<td width="20%" ng-bind="instance.ip"></td>
<td width="20%">{{instance.configs && instance.configs.length ?
(instance.configs[0].releaseDeliveryTime | date: 'yyyy-MM-dd HH:mm:ss') : ''}}
</td>
</tr>
</tbody>
</table>
<div class="row text-center"
ng-show="namespace.latestReleaseInstances.content.length < namespace.latestReleaseInstances.total">
<button class="btn btn-default" ng-click="loadInstanceInfo(namespace)">加载更多</button>
</div>
</div>
<div class="text-center" ng-if="namespace.latestReleaseInstances.total == 0">
无实例信息
</div>
</div>
<!--not latest release instances-->
<div class="panel-body" ng-show="namespace.instanceViewType == 'not_latest_release'">
<div class="panel-default"
ng-if="namespace.instancesCount - namespace.latestReleaseInstances.total > 0"
ng-repeat="release in namespace.notLatestReleases">
<div class="panel-heading">
<a target="_blank" data-tooltip="tooltip" data-placement="bottom" title="查看配置"
href="/config/history.html?#/appid={{appId}}&env={{env}}&clusterName={{cluster}}&namespaceName={{namespace.baseInfo.namespaceName}}&releaseId={{release.id}}">
{{release.name}}
</a>
</div>
<table class="table table-bordered table-striped">
<thead>
<tr>
<td>App ID</td>
<td>Cluster Name</td>
<td>Data Center</td>
<td>IP</td>
<td>配置获取时间</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="instance in namespace.notLatestReleaseInstances[release.id]">
<td width="20%" ng-bind="instance.appId"></td>
<td width="20%" ng-bind="instance.clusterName"></td>
<td width="20%" ng-bind="instance.dataCenter"></td>
<td width="20%" ng-bind="instance.ip"></td>
<td width="20%">{{instance.configs && instance.configs.length ?
(instance.configs[0].releaseDeliveryTime | date: 'yyyy-MM-dd HH:mm:ss') : ''}}
</td>
</tr>
</tbody>
</table>
</div>
<div class="text-center"
ng-if="namespace.instancesCount - namespace.latestReleaseInstances.total == 0">
无实例信息
</div>
</div>
<!--all instances-->
<div class="panel-body" ng-show="namespace.instanceViewType == 'all'">
<div class="panel-default" ng-if="namespace.instancesCount > 0">
<table class="table table-bordered table-striped" ng-if="namespace.allInstances">
<thead>
<tr>
<td>App ID</td>
<td>Cluster Name</td>
<td>Data Center</td>
<td>IP</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="instance in namespace.allInstances">
<td width="25%" ng-bind="instance.appId"></td>
<td width="25%" ng-bind="instance.clusterName"></td>
<td width="25%" ng-bind="instance.dataCenter"></td>
<td width="25%" ng-bind="instance.ip"></td>
</tr>
</tbody>
</table>
<div class="row text-center" ng-show="namespace.allInstances.length < namespace.instancesCount">
<button class="btn btn-default" ng-click="loadInstanceInfo(namespace)">加载更多</button>
</div>
</div>
<div class="text-center" ng-if="namespace.instancesCount == 0">
无实例信息
</div>
</div>
</div>
</section>
</section>
<section class="panel namespace-panel"> <section class="panel namespace-panel" ng-class="{'hidden': !namespace.show}">
<!--public or link label--> <!--public or link label-->
<header class="row namespace-attribute-panel"> <ng-include src="'views/component/namespace-panel-header.html'"></ng-include>
<div class="text-center namespace-attribute-public">
<span data-tooltip="tooltip" data-placement="bottom"
title="私有namespace({{namespace.baseInfo.namespaceName}})的配置只能被AppId为{{appId}}的客户端读取到"
ng-show="!namespace.isPublic">私有</span>
<span data-tooltip="tooltip" data-placement="top" <ng-include src="'views/component/namespace-panel-master-tab.html'"></ng-include>
title="namespace({{namespace.baseInfo.namespaceName}})的配置能被任何客户端读取到"
ng-show="namespace.isPublic && namespace.parentAppId == namespace.baseInfo.appId">公共</span>
<span data-tooltip="tooltip" data-placement="top"
title="namespace({{namespace.baseInfo.namespaceName}})的配置将会覆盖公共namespace的配置, 且合并之后的配置只能被AppId为{{appId}}的客户端读取到"
ng-show="namespace.isPublic && namespace.isLinkedNamespace"
ng-click="goToParentAppConfigPage(namespace)">关联</span>
</div>
</header>
<!--branch nav-->
<header class="panel-heading second-panel-heading" ng-show="namespace.hasBranch">
<div class="row">
<div class="col-md-8 pull-left">
<ul class="nav nav-tabs">
<li role="presentation">
<a ng-class="{'node_active': namespace.currentOperateBranch == 'master'}"
ng-click="switchBranch('master')">
<img src="img/branch.png">
主版本
</a>
</li>
<li role="presentation">
<a ng-class="{'node_active': namespace.currentOperateBranch != 'master'}"
ng-click="switchBranch(namespace.branchName)">
<img src="img/branch.png">
灰度版本
</a>
</li>
</ul>
</div>
</div>
</header>
<!--master panel body-->
<section class="master-panel-body"
ng-if="namespace.hasBranch && namespace.currentOperateBranch == 'master' || !namespace.hasBranch">
<!--main header-->
<header class="panel-heading">
<div class="row">
<div class="col-md-6 header-namespace">
<b class="namespace-name" ng-bind="namespace.viewName"
data-tooltip="tooltip" data-placement="bottom" title="{{namespace.comment}}"></b>
<span class="label label-info no-radius namespace-label" ng-bind="namespace.format"></span>
<span class="label label-warning no-radius namespace-label" ng-show="namespace.itemModifiedCnt > 0">有修改
<span class="badge label badge-white namespace-label" ng-bind="namespace.itemModifiedCnt"></span>
</span>
<span class="label label-primary no-radius namespace-label"
ng-show="namespace.lockOwner">当前修改者:{{namespace.lockOwner}}</span>
</div>
<div class="col-md-6 text-right header-buttons">
<button type="button" class="btn btn-success btn-sm"
data-tooltip="tooltip" data-placement="bottom" title="发布配置"
ng-show="(namespace.hasReleasePermission || namespace.hasModifyPermission)"
ng-disabled="namespace.isTextEditing"
ng-click="publish(namespace)">
<img src="img/release.png">
发布
</button>
<button type="button" class="btn btn-default btn-sm"
data-tooltip="tooltip" data-placement="bottom" title="回滚已发布配置"
ng-show="namespace.hasReleasePermission"
ng-click="rollback(namespace)">
<img src="img/rollback.png">
回滚
</button>
<a type="button" class="btn btn-default btn-sm"
data-tooltip="tooltip" data-placement="bottom" title="查看发布历史"
href="/config/history.html?#/appid={{appId}}&env={{env}}&clusterName={{cluster}}&namespaceName={{namespace.baseInfo.namespaceName}}">
<img src="img/release-history.png">
发布历史
</a>
<a type="button" class="btn btn-default btn-sm"
data-tooltip="tooltip" data-placement="bottom" title="配置修改、发布权限"
href="/namespace/role.html?#/appid={{appId}}&namespaceName={{namespace.baseInfo.namespaceName}}"
ng-show="hasAssignUserPermission">
<img src="img/assign.png">
授权
</a>
<a type="button" class="btn btn-default btn-sm"
data-tooltip="tooltip" data-placement="bottom" title="创建测试版本"
ng-show="!namespace.hasBranch && namespace.isPropertiesFormat && namespace.hasModifyPermission"
ng-click="preCreateBranch(namespace)">
<img src="img/test.png">
创建灰度
</a>
<a type="button" class="btn btn-default btn-sm J_tableview_btn"
data-tooltip="tooltip" data-placement="bottom" title="您没有任何配置权限,请申请"
ng-click="showNoModifyPermissionDialog()"
ng-show="!namespace.hasModifyPermission && !namespace.hasReleasePermission">
申请配置权限
</a>
</div>
</div>
</header>
<!--second header-->
<header class="panel-heading second-panel-heading">
<div class="row">
<div class="col-md-8 pull-left">
<!--master branch nav tabs-->
<ul class="nav nav-tabs">
<li role="presentation" ng-click="switchView(namespace, 'table')"
ng-show="namespace.isPropertiesFormat">
<a ng-class="{node_active:namespace.viewType == 'table'}">
<img src="img/table.png">
表格
</a>
</li>
<li role="presentation"
ng-click="switchView(namespace, 'text')">
<a ng-class="{node_active:namespace.viewType == 'text'}">
<img src="img/text.png">
文本
</a>
</li>
<li role="presentation" ng-click="switchView(namespace, 'history')">
<a ng-class="{node_active:namespace.viewType == 'history'}">
<img src="img/change.png">
更改历史
</a>
</li>
<li role="presentation" ng-click="switchView(namespace, 'instance')">
<a ng-class="{node_active:namespace.viewType == 'instance'}">
<img src="img/machine.png">
实例列表
<span class="badge badge-grey" ng-bind="namespace.instancesCount"></span>
</a>
</li>
</ul>
</div>
<div class="col-md-4 text-right">
<a class="clipboard"
data-clipboard-text="{{namespace.text}}"
data-tooltip="tooltip" data-placement="bottom" title="复制文本"
ng-show="!namespace.isTextEditing && namespace.viewType == 'text' && namespace.hasModifyPermission">
<img src="img/copy.png" class="ns_btn">
</a>
&nbsp;
<a data-tooltip="tooltip" data-placement="bottom" title="取消修改"
ng-show="namespace.isTextEditing && namespace.viewType == 'text'"
ng-click="toggleTextEditStatus(namespace)">
<img src="img/cancel.png" class="ns_btn">
</a>
<a data-tooltip="tooltip" data-placement="bottom" title="修改配置"
ng-show="!namespace.isTextEditing && namespace.viewType == 'text' && namespace.hasModifyPermission"
ng-click="toggleTextEditStatus(namespace)">
<img src="img/edit.png" class="ns_btn">
</a>
&nbsp;
<a data-tooltip="tooltip" data-placement="bottom" title="提交修改"
data-toggle="modal" data-target="#commitModal"
ng-show="namespace.isTextEditing && namespace.viewType == 'text'"
ng-click="modifyByText(namespace)">
<img src="img/submit.png" class="ns_btn">
</a>
<button type="button" class="btn btn-default btn-sm"
data-tooltip="tooltip" data-placement="bottom" title="按Key过滤配置"
ng-show="namespace.viewType == 'table' && namespace.currentOperateBranch == 'master'"
ng-click="toggleItemSearchInput(namespace)">
<span class="glyphicon glyphicon-filter"></span>
过滤配置
</button>
<button type="button" class="btn btn-default btn-sm J_tableview_btn"
data-tooltip="tooltip" data-placement="bottom" title="同步各环境间配置"
ng-click="goToSyncPage(namespace)"
ng-show="namespace.viewType == 'table' && namespace.currentOperateBranch == 'master'
&& namespace.hasModifyPermission && namespace.isPropertiesFormat">
<img src="img/sync.png">
同步配置
</button>
<button type="button" class="btn btn-primary btn-sm"
ng-show="namespace.viewType == 'table' && namespace.currentOperateBranch == 'master'
&& namespace.hasModifyPermission"
ng-click="createItem(namespace)">
<img src="img/plus.png">
新增配置
</button>
</div>
</div>
</header>
<section>
<!--table view-->
<div class="namespace-view-table" ng-show="namespace.viewType == 'table'">
<div class="col-md-8 col-lg-offset-2 search-input" ng-show="namespace.showSearchInput">
<input type="text" class="form-control" placeholder="输入key过滤"
ng-model="namespace.searchKey"
ng-change="searchItems(namespace)">
</div>
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>发布状态</th>
<th class="hover" title="排序"
ng-click="col='item.key';desc=!desc;">
Key&nbsp;
<span class="glyphicon glyphicon-sort"></span>
</th>
<th>
Value
</th>
<th>
备注
</th>
<th class="hover" title="排序"
ng-click="col='item.dataChangeLastModifiedBy';desc=!desc;">
最后修改人
<span class="glyphicon glyphicon-sort"></span>
</th>
<th class="hover" title="排序"
ng-click="col='item.dataChangeLastModifiedTime';desc=!desc;">
最后修改时间
<span class="glyphicon glyphicon-sort"></span>
</th>
<th>
操作
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="config in namespace.viewItems |orderBy:col:desc"
ng-if="config.item.key">
<td width="8%" class="text-center">
<span class="label label-warning no-radius cursor-pointer" ng-if="config.isModified"
data-tooltip="tooltip" data-placement="bottom" title="点击查看已发布的值"
ng-click="showText(config.oldValue?config.oldValue:'新增的配置,无发布的值')">未发布</span>
<span class="label label-default-light no-radius"
data-tooltip="tooltip" data-placement="bottom" title="已生效的配置"
ng-if="!config.isModified">已发布</span>
</td>
<td width="15%" class="cursor-pointer" title="点击查看" ng-click="showText(config.item.key)">
<span ng-bind="config.item.key | limitTo: 250"></span>
<span ng-bind="config.item.key.length > 250 ? '...' :''"></span>
<span class="label label-default cursor-pointer" ng-if="config.hasBranchValue"
data-tooltip="tooltip" data-placement="bottom" title="该配置有灰度配置,点击查看灰度的值"
ng-click="namespace.currentOperateBranch=namespace.branchName;namespace.branch.viewType='table'"></span>
<span class="label label-success" ng-if="config.isModified && !config.oldValue"
data-tooltip="tooltip" data-placement="bottom" title="新增的配置"></span>
<span class="label label-info"
ng-if="config.isModified && config.oldValue && !config.isDeleted"
data-tooltip="tooltip" data-placement="bottom" title="修改的配置"></span>
<span class="label label-danger" ng-if="config.isDeleted"
data-tooltip="tooltip" data-placement="bottom" title="删除的配置"></span>
</td>
<td width="33%" class="cursor-pointer" title="点击查看" ng-click="showText(config.item.value)">
<span ng-bind="config.item.value | limitTo: 250"></span>
<span ng-bind="config.item.value.length > 250 ? '...': ''"></span>
</td>
<td width="15%" title="{{config.item.comment}}">
<span ng-bind="config.item.comment | limitTo: 250"></span>
<span ng-bind="config.item.comment.length > 250 ?'...' : ''"></span>
</td>
<td width="10%" ng-bind="config.item.dataChangeLastModifiedBy">
</td>
<td width="11%"
ng-bind="config.item.dataChangeLastModifiedTime | date: 'yyyy-MM-dd HH:mm:ss'">
</td>
<td width="8%" class="text-center" ng-if="!config.isDeleted">
<img src="img/edit.png" data-tooltip="tooltip" data-placement="bottom" title="修改"
ng-click="editItem(namespace, config.item)"
ng-show="namespace.hasModifyPermission">
<img style="margin-left: 5px;" src="img/cancel.png"
data-tooltip="tooltip" data-placement="bottom" title="删除"
ng-click="preDeleteItem(namespace, config.item.id)"
ng-show="namespace.hasModifyPermission">
</td>
<td width="6%" class="text-center" ng-if="config.isDeleted">
</td>
</tr>
</tbody>
</table>
</div>
<!--text view-->
<!--只读模式下的文本内容,不替换换行符-->
<textarea class="form-control no-radius"
rows="{{namespace.itemCnt < 10 ? 10: namespace.itemCnt>20 ? 20:namespace.itemCnt}}"
ng-show="namespace.viewType == 'text' && !namespace.isTextEditing"
ng-disabled="!namespace.isTextEditing"
ng-bind="namespace.text">
</textarea>
<!--编辑状态下的文本内容,会过滤掉换行符-->
<textarea class="form-control no-radius"
rows="{{namespace.itemCnt < 10 ? 10: namespace.itemCnt>20 ? 20:namespace.itemCnt}}"
ng-show="namespace.viewType == 'text' && namespace.isTextEditing"
ng-disabled="!namespace.isTextEditing" ng-model="namespace.editText">
</textarea>
<!--history view-->
<div class="J_historyview history-view" ng-show="namespace.viewType == 'history'">
<div class="media" ng-repeat="commits in namespace.commits">
<div class="media-body">
<div class="row">
<div class="col-md-6"><h3 class="media-heading" ng-bind="commits.dataChangeCreatedBy"></h3>
</div>
<div class="col-md-6 text-right"><h5 class="media-heading"
ng-bind="commits.dataChangeCreatedTime | date: 'yyyy-MM-dd HH:mm:ss'"></h5>
</div>
</div>
<!--properties format-->
<table class="table table-bordered table-striped text-center table-hover"
style="margin-top: 5px;"
ng-if="namespace.isPropertiesFormat">
<thead>
<tr>
<th>
Type
</th>
<th>
Key
</th>
<th>
Old Value
</th>
<th>
New Value
</th>
<th>
Comment
</th>
</tr>
</thead>
<tbody>
<!--兼容老数据,不显示item类型为空行和注释的item-->
<tr ng-repeat="item in commits.changeSets.createItems" ng-show="item.key">
<td width="2%">
新增
</td>
<td width="20%" title="{{item.key}}">
<span ng-bind="item.key | limitTo: 250"></span>
<span ng-bind="item.key.length > 250 ? '...' :''"></span>
</td>
<td width="30%">
</td>
<td width="30%" class="cursor-pointer" title="{{item.value}}"
ng-click="showText(item.value)">
<span ng-bind="item.value | limitTo: 250"></span>
<span ng-bind="item.value.length > 250 ? '...': ''"></span>
</td>
<td width="18%" title="{{item.comment}}">
<span ng-bind="item.comment | limitTo: 250"></span>
<span ng-bind="item.comment.length > 250 ?'...' : ''"></span>
</td>
</tr>
<tr ng-repeat="item in commits.changeSets.updateItems">
<td width="2%">
更新
</td>
<td width="20%" title="{{item.newItem.key}}">
<span ng-bind="item.newItem.key | limitTo: 250"></span>
<span ng-bind="item.newItem.key.length > 250 ? '...' :''"></span>
</td>
<td width="30%" class="cursor-pointer" title="{{item.oldItem.value}}"
ng-click="showText(item.oldItem.value)">
<span ng-bind="item.oldItem.value | limitTo: 250"></span>
<span ng-bind="item.oldItem.value.length > 250 ? '...': ''"></span>
</td>
<td width="30%" class="cursor-pointer" title="{{item.newItem.value}}"
ng-click="showText(item.newItem.value)">
<span ng-bind="item.newItem.value | limitTo: 250"></span>
<span ng-bind="item.newItem.value.length > 250 ? '...': ''"></span>
</td>
<td width="18%" title="{{item.newItem.comment}}">
<span ng-bind="item.newItem.comment | limitTo: 250"></span>
<span ng-bind="item.newItem.comment.length > 250 ?'...' : ''"></span>
</td>
</tr>
<tr ng-repeat="item in commits.changeSets.deleteItems"
ng-show="item.key || item.comment">
<td width="2%">
删除
</td>
<td width="20%" title="{{item.key}}">
<span ng-bind="item.key | limitTo: 250"></span>
<span ng-bind="item.key.length > 250 ? '...' :''"></span>
</td>
<td width="30%" title="{{item.value}}">
<span ng-bind="item.value | limitTo: 250"></span>
<span ng-bind="item.value.length > 250 ? '...': ''"></span>
</td>
<td width="30%">
</td>
<td width="18%" title="{{item.comment}}">
<span ng-bind="item.comment | limitTo: 250"></span>
<span ng-bind="item.comment.length > 250 ?'...' : ''"></span>
</td>
</tr>
</tbody>
</table>
<!--not properties format-->
<div ng-if="!namespace.isPropertiesFormat">
<div ng-repeat="item in commits.changeSets.createItems">
<textarea class="form-control no-radius" rows="20"
ng-disabled="true" ng-bind="item.value">
</textarea>
</div>
<div ng-repeat="item in commits.changeSets.updateItems">
<textarea class="form-control no-radius" rows="20"
ng-disabled="true" ng-bind="item.newItem.value">
</textarea>
</div>
</div>
</div>
<hr>
</div>
<div class="text-center">
<button type="button" class="btn btn-default" ng-show="!namespace.hasLoadAllCommit"
ng-click="loadCommitHistory(namespace)">加载更多
<span class="glyphicon glyphicon-menu-down"></span></button>
</div>
</div>
<!--instance view-->
<div class="panel panel-default instance-view" ng-show="namespace.viewType == 'instance'">
<div class="panel-heading">
<div class="row text-right" style="padding-right: 15px;">
<div class="btn-group btn-group-sm" role="group">
<button type="button" class="btn btn-default"
ng-class="{'btn-primary':namespace.instanceViewType == 'latest_release'}"
ng-click="switchInstanceViewType(namespace, 'latest_release')"> 使用最新配置的实例
<span class="badge" ng-bind="namespace.latestReleaseInstances.total"></span>
</button>
<button type="button" class="btn btn-default"
ng-class="{'btn-primary':namespace.instanceViewType == 'not_latest_release'}"
ng-click="switchInstanceViewType(namespace, 'not_latest_release')">使用非最新配置的实例
<span class="badge"
ng-bind="namespace.instancesCount - namespace.latestReleaseInstances.total"></span>
</button>
<button type="button" class="btn btn-default"
ng-class="{'btn-primary':namespace.instanceViewType == 'all'}"
ng-click="switchInstanceViewType(namespace, 'all')">所有实例
<span class="badge" ng-bind="namespace.instancesCount"></span>
</button>
</div>
<button class="btn btn-default btn-sm"
data-tooltip="tooltip" data-placement="bottom" title="刷新列表"
ng-click="refreshInstancesInfo(namespace)">
<img src="../../img/refresh.png"/>
</button>
</div>
</div>
<!--latest release instances-->
<div class="panel-body" ng-show="namespace.instanceViewType == 'latest_release'">
<div class="panel-default" ng-if="namespace.latestReleaseInstances.total > 0">
<div class="panel-heading">
<a target="_blank" data-tooltip="tooltip" data-placement="bottom" title="查看配置"
href="/config/history.html?#/appid={{appId}}&env={{env}}&clusterName={{cluster}}&namespaceName={{namespace.baseInfo.namespaceName}}&releaseId={{namespace.latestRelease.id}}">
{{namespace.latestRelease.name}}
</a>
</div>
<table class="table table-bordered table-striped">
<thead>
<tr>
<td>App ID</td>
<td>Cluster Name</td>
<td>Data Center</td>
<td>IP</td>
<td>配置获取时间</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="instance in namespace.latestReleaseInstances.content">
<td width="20%" ng-bind="instance.appId"></td>
<td width="20%" ng-bind="instance.clusterName"></td>
<td width="20%" ng-bind="instance.dataCenter"></td>
<td width="20%" ng-bind="instance.ip"></td>
<td width="20%">{{instance.configs && instance.configs.length ?
(instance.configs[0].releaseDeliveryTime | date: 'yyyy-MM-dd HH:mm:ss') : ''}}
</td>
</tr>
</tbody>
</table>
<div class="row text-center"
ng-show="namespace.latestReleaseInstances.content.length < namespace.latestReleaseInstances.total">
<button class="btn btn-default" ng-click="loadInstanceInfo(namespace)">加载更多</button>
</div>
</div>
<div class="text-center" ng-if="namespace.latestReleaseInstances.total == 0">
无实例信息
</div>
</div>
<!--not latest release instances-->
<div class="panel-body" ng-show="namespace.instanceViewType == 'not_latest_release'">
<div class="panel-default"
ng-if="namespace.instancesCount - namespace.latestReleaseInstances.total > 0"
ng-repeat="release in namespace.notLatestReleases">
<div class="panel-heading">
<a target="_blank" data-tooltip="tooltip" data-placement="bottom" title="查看配置"
href="/config/history.html?#/appid={{appId}}&env={{env}}&clusterName={{cluster}}&namespaceName={{namespace.baseInfo.namespaceName}}&releaseId={{release.id}}">
{{release.name}}
</a>
</div>
<table class="table table-bordered table-striped">
<thead>
<tr>
<td>App ID</td>
<td>Cluster Name</td>
<td>Data Center</td>
<td>IP</td>
<td>配置获取时间</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="instance in namespace.notLatestReleaseInstances[release.id]">
<td width="20%" ng-bind="instance.appId"></td>
<td width="20%" ng-bind="instance.clusterName"></td>
<td width="20%" ng-bind="instance.dataCenter"></td>
<td width="20%" ng-bind="instance.ip"></td>
<td width="20%">{{instance.configs && instance.configs.length ?
(instance.configs[0].releaseDeliveryTime | date: 'yyyy-MM-dd HH:mm:ss') : ''}}
</td>
</tr>
</tbody>
</table>
</div>
<div class="text-center"
ng-if="namespace.instancesCount - namespace.latestReleaseInstances.total == 0">
无实例信息
</div>
</div>
<!--all instances-->
<div class="panel-body" ng-show="namespace.instanceViewType == 'all'">
<div class="panel-default" ng-if="namespace.instancesCount > 0">
<table class="table table-bordered table-striped" ng-if="namespace.allInstances">
<thead>
<tr>
<td>App ID</td>
<td>Cluster Name</td>
<td>Data Center</td>
<td>IP</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="instance in namespace.allInstances">
<td width="25%" ng-bind="instance.appId"></td>
<td width="25%" ng-bind="instance.clusterName"></td>
<td width="25%" ng-bind="instance.dataCenter"></td>
<td width="25%" ng-bind="instance.ip"></td>
</tr>
</tbody>
</table>
<div class="row text-center" ng-show="namespace.allInstances.length < namespace.instancesCount">
<button class="btn btn-default" ng-click="loadInstanceInfo(namespace)">加载更多</button>
</div>
</div>
<div class="text-center" ng-if="namespace.instancesCount == 0">
无实例信息
</div>
</div>
</div>
</section>
</section>
<!--branch panel body--> <!--branch panel body-->
<section class="branch-panel-body" ng-if="namespace.hasBranch && namespace.currentOperateBranch != 'master'"> <ng-include src="'views/component/namespace-panel-branch-tab.html'"></ng-include>
<!--main header-->
<header class="panel-heading">
<div class="row">
<div class="col-md-6 header-namespace">
<b class="namespace-name" ng-bind="namespace.viewName" data-tooltip="tooltip"
data-placement="bottom"
title="{{namespace.comment}}"></b>
<span class="label label-info no-radius namespace-label" ng-bind="namespace.format"></span>
<span class="label label-warning no-radius namespace-label"
ng-show="namespace.branch.itemModifiedCnt > 0">有修改
<span class="badge label badge-white namespace-label"
ng-bind="namespace.branch.itemModifiedCnt"></span>
</span>
<span class="label label-primary no-radius namespace-label"
ng-show="namespace.branch.lockOwner">当前修改者:
<span ng-bind="namespace.branch.lockOwner"></span>
</span>
</div>
<div class="col-md-6 text-right header-buttons">
<a type="button" class="btn btn-success btn-sm"
data-tooltip="tooltip" data-placement="bottom" title="继续灰度发布"
ng-show="(namespace.hasReleasePermission || namespace.hasModifyPermission)"
ng-click="publish(namespace.branch)">
灰度发布
</a>
<a type="button" class="btn btn-primary btn-sm"
data-tooltip="tooltip" data-placement="bottom" title="合并到主版本并发布主版本配置"
ng-show="(namespace.hasReleasePermission || namespace.hasModifyPermission)"
ng-click="mergeAndPublish(namespace.branch)">
全量发布
</a>
<a type="button" class="btn btn-warning btn-sm"
data-tooltip="tooltip" data-placement="bottom" title="废弃灰度版本"
ng-show="(namespace.hasReleasePermission
|| (!namespace.branch.latestRelease && namespace.hasModifyPermission))"
ng-click="preDeleteBranch(namespace.branch)">
放弃灰度
</a>
</div>
</div>
</header>
<!--second header-->
<header class="panel-heading second-panel-heading">
<div class="row">
<div class="col-md-8 pull-left">
<ul class="nav nav-tabs">
<li role="presentation" ng-click="switchView(namespace.branch, 'table')"
ng-show="namespace.isPropertiesFormat">
<a ng-class="{node_active:namespace.branch.viewType == 'table'}">
<img src="img/table.png">
配置
</a>
</li>
<li role="presentation" ng-click="switchView(namespace.branch, 'rule')">
<a ng-class="{node_active:namespace.branch.viewType == 'rule'}">
<img src="img/rule.png">
灰度规则
<span class="badge badge-grey"
ng-bind="namespace.branch.grayIps.length + namespace.branch.grayApps.length"></span>
</a>
</li>
<li role="presentation" ng-click="switchView(namespace.branch, 'instance')">
<a ng-class="{node_active:namespace.branch.viewType == 'instance'}">
<img src="img/machine.png">
灰度实例列表
<span class="badge badge-grey"
ng-bind="namespace.branch.latestReleaseInstances.total"></span>
</a>
</li>
<li role="presentation" ng-click="switchView(namespace.branch, 'history')">
<a ng-class="{node_active:namespace.branch.viewType == 'history'}">
<img src="img/change.png">
更改历史
</a>
</li>
</ul>
</div>
</div>
</header>
<section>
<!--items-->
<div class="namespace-view-table" ng-show="namespace.branch.viewType == 'table'">
<div class="panel panel-default" ng-if="namespace.hasBranch">
<div class="panel-heading">
灰度的配置
<button type="button" class="btn btn-primary btn-sm pull-right" style="margin-top: -4px;"
ng-show="namespace.hasModifyPermission"
ng-click="createItem(namespace.branch)">
<img src="img/plus.png">
新增灰度配置
</button>
</div>
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>发布状态</th>
<th class="hover" title="排序"
ng-click="col='item.key';desc=!desc;">
Key&nbsp;
<span class="glyphicon glyphicon-sort"></span>
</th>
<th>
主版本的值
</th>
<th>
灰度的值
</th>
<th>
备注
</th>
<th class="hover" title="排序"
ng-click="col='item.dataChangeLastModifiedBy';desc=!desc;">
最后修改人
<span class="glyphicon glyphicon-sort"></span>
</th>
<th class="hover" title="排序"
ng-click="col='item.dataChangeLastModifiedTime';desc=!desc;">
最后修改时间
<span class="glyphicon glyphicon-sort"></span>
</th>
<th>
操作
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="config in namespace.branch.branchItems |orderBy:col:desc"
ng-if="config.item.key">
<td width="7%" class="text-center">
<span class="label label-warning no-radius cursor-pointer"
data-tooltip="tooltip" data-placement="bottom" title="点击查看已发布的值"
ng-if="config.isModified || config.isDeleted"
ng-click="showText(config.oldValue)">未发布</span>
<span class="label label-default-light no-radius"
data-tooltip="tooltip" data-placement="bottom" title="已生效的配置"
ng-if="!config.isModified">已发布</span>
</td>
<td width="15%" class="cursor-pointer" title="点击查看" ng-click="showText(config.item.key)">
<span ng-bind="config.item.key | limitTo: 250"></span>
<span ng-bind="config.item.key.length > 250 ? '...' :''"></span>
<span class="label label-danger" ng-if="config.isDeleted"
data-tooltip="tooltip" data-placement="bottom" title="删除的配置"></span>
<span class="label label-info" ng-if="!config.isDeleted && config.masterReleaseValue"
data-tooltip="tooltip" data-placement="bottom" title="修改主版本的配置"></span>
<span class="label label-success"
ng-if="!config.isDeleted && !config.masterReleaseValue"
data-tooltip="tooltip" data-placement="bottom" title="灰度版本特有的配置"></span>
</td>
<td width="20%" class="cursor-pointer" title="点击查看"
ng-click="showText(config.masterReleaseValue)">
<span ng-bind="config.masterReleaseValue | limitTo: 250"></span>
<span ng-bind="config.item.value.length > 250 ? '...': ''"></span>
</td>
<td width="20%" class="cursor-pointer" title="点击查看" ng-click="showText(config.item.value)">
<span ng-bind="config.item.value | limitTo: 250"></span>
<span ng-bind="config.item.value.length > 250 ? '...': ''"></span>
</td>
<td width="10%" title="{{config.item.comment}}">
<span ng-bind="config.item.comment | limitTo: 250"></span>
<span ng-bind="config.item.comment.length > 250 ?'...' : ''"></span>
</td>
<td width="10%" ng-bind="config.item.dataChangeLastModifiedBy">
</td>
<td width="10%"
ng-bind="config.item.dataChangeLastModifiedTime | date: 'yyyy-MM-dd HH:mm:ss'">
</td>
<td width="9%" class="text-center">
<img src="img/edit.png"
data-tooltip="tooltip" data-placement="bottom" title="修改"
ng-if="!config.isDeleted"
ng-click="editItem(namespace.branch, config.item)"
ng-show="namespace.hasModifyPermission">
<img style="margin-left: 5px;" src="img/cancel.png"
data-tooltip="tooltip" data-placement="bottom" title="删除"
ng-if="!config.isDeleted"
ng-click="preDeleteItem(namespace.branch, config.item.id)"
ng-show="namespace.hasModifyPermission">
</td>
</tr>
</tbody>
</table>
</div>
<div class="panel panel-default"
ng-if="namespace.branch.masterItems && namespace.branch.masterItems.length > 0">
<div class="panel-heading">
主版本的配置
</div>
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>发布状态</th>
<th class="hover" title="排序"
ng-click="col='item.key';desc=!desc;">
Key&nbsp;
<span class="glyphicon glyphicon-sort"></span>
</th>
<th>
Value
</th>
<th>
备注
</th>
<th class="hover" title="排序"
ng-click="col='item.dataChangeLastModifiedBy';desc=!desc;">
最后修改人
<span class="glyphicon glyphicon-sort"></span>
</th>
<th class="hover" title="排序"
ng-click="col='item.dataChangeLastModifiedTime';desc=!desc;">
最后修改时间
<span class="glyphicon glyphicon-sort"></span>
</th>
<th>
操作
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="config in namespace.branch.masterItems |orderBy:col:desc"
ng-if="config.item.key">
<td width="8%" class="text-center">
<span class="label label-warning no-radius cursor-pointer"
data-tooltip="tooltip" data-placement="bottom" title="点击查看已发布的值"
ng-if="config.isModified || config.isDeleted"
ng-click="showText(config.oldValue)">未发布</span>
<span class="label label-default-light no-radius"
data-tooltip="tooltip" data-placement="bottom" title="已生效的配置"
ng-if="!config.isModified">已发布</span>
</td>
<td width="15%" class="cursor-pointer" title="点击查看" ng-click="showText(config.item.key)">
<span ng-bind="config.item.key | limitTo: 250"></span>
<span ng-bind="config.item.key.length > 250 ? '...' :''"></span>
<span class="label label-success" ng-if="config.isModified && !config.oldValue"
data-tooltip="tooltip" data-placement="bottom" title="新增的配置"></span>
<span class="label label-info"
ng-if="config.isModified && config.oldValue && !config.isDeleted"
data-tooltip="tooltip" data-placement="bottom" title="修改的配置"></span>
<span class="label label-danger" ng-if="config.isDeleted"
data-tooltip="tooltip" data-placement="bottom" title="删除的配置"></span>
</td>
<td width="35%" class="cursor-pointer" title="点击查看" ng-click="showText(config.item.value)">
<span ng-bind="config.item.value | limitTo: 250"></span>
<span ng-bind="config.item.value.length > 250 ? '...': ''"></span>
</td>
<td width="15%" title="{{config.item.comment}}">
<span ng-bind="config.item.comment | limitTo: 250"></span>
<span ng-bind="config.item.comment.length > 250 ?'...' : ''"></span>
</td>
<td width="10%" ng-bind="config.item.dataChangeLastModifiedBy">
</td>
<td width="12%"
ng-bind="config.item.dataChangeLastModifiedTime | date: 'yyyy-MM-dd HH:mm:ss'">
</td>
<td width="5%" class="text-center">
<img src="img/gray.png"
data-tooltip="tooltip" data-placement="bottom" title="对此配置灰度"
ng-if="!config.isDeleted"
ng-click="editItem(namespace.branch, config.item)"
ng-show="namespace.hasModifyPermission">
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!--gray rules-->
<div class="rules-manage-view row" ng-show="namespace.branch.viewType == 'rule'">
<div class="alert alert-warning no-radius"
ng-show="!namespace.hasModifyPermission && !namespace.hasReleasePermission">
<strong>Tips:</strong>
您没有权限编辑灰度规则, 具有namespace修改权或者发布权的人员才可以编辑灰度规则. 如需要编辑灰度规则,请找项目管理员申请权限.
</div>
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>灰度的AppId</th>
<th>灰度的IP列表</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="ruleItem in namespace.branch.rules.ruleItems">
<td width="20%" ng-bind="ruleItem.clientAppId"></td>
<td width="70%" ng-show="!ruleItem.ApplyToAllInstances"
ng-bind="ruleItem.clientIpList.join(', ')"></td>
<td width="70%" ng-show="ruleItem.ApplyToAllInstances">ALL</td>
<td class="text-center" width="10%">
<img src="img/edit.png" class="i-20 hover"
data-tooltip="tooltip" data-placement="bottom" title="修改"
ng-show="namespace.hasModifyPermission || namespace.hasReleasePermission"
ng-click="editRuleItem(namespace.branch, ruleItem)">
<img src="img/cancel.png" class="i-20 hover" style="margin-left: 5px;"
data-tooltip="tooltip" data-placement="bottom" title="删除"
ng-show="namespace.hasModifyPermission || namespace.hasReleasePermission"
ng-click="deleteRuleItem(namespace.branch, ruleItem)">
</td>
</tr>
</tbody>
</table>
<button class="btn btn-primary"
ng-if="namespace.hasModifyPermission || namespace.hasReleasePermission"
ng-show="(namespace.isPublic && !namespace.isLinkedNamespace) ||
((!namespace.isPublic || namespace.isLinkedNamespace)
&& (!namespace.branch.rules
|| !namespace.branch.rules.ruleItems
|| !namespace.branch.rules.ruleItems.length))"
ng-click="addRuleItem(namespace.branch)">新增规则
</button>
</div>
<!--instances -->
<div class="panel panel-default" ng-show="namespace.branch.viewType == 'instance'">
<div class="panel-heading text-right">
<button class="btn btn-default btn-sm"
data-tooltip="tooltip" data-placement="bottom" title="刷新列表"
ng-click="refreshInstancesInfo(namespace.branch)">
<img src="../../img/refresh.png"/>
</button>
</div>
<div class="panel-body">
<div class="panel-default" ng-if="namespace.branch.latestReleaseInstances.total > 0">
<div class="panel-heading">
<a target="_blank" data-tooltip="tooltip" data-placement="bottom" title="查看配置"
href="/config/history.html?#/appid={{appId}}&env={{env}}&clusterName={{namespace.baseInfo.clusterName}}&namespaceName={{namespace.baseInfo.namespaceName}}&releaseId={{namespace.branch.latestRelease.id}}">
{{namespace.branch.latestRelease.name}}
</a>
</div>
<table class="table table-bordered table-striped">
<thead>
<tr>
<td>App ID</td>
<td>Cluster Name</td>
<td>Data Center</td>
<td>IP</td>
<td>配置获取时间</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="instance in namespace.branch.latestReleaseInstances.content">
<td width="20%" ng-bind="instance.appId"></td>
<td width="20%" ng-bind="instance.clusterName"></td>
<td width="20%" ng-bind="instance.dataCenter"></td>
<td width="20%" ng-bind="instance.ip"></td>
<td width="20%">{{instance.configs && instance.configs.length ?
(instance.configs[0].releaseDeliveryTime | date: 'yyyy-MM-dd HH:mm:ss') : ''}}
</td>
</tr>
</tbody>
</table>
<div class="row text-center"
ng-show="namespace.branch.latestReleaseInstances.content.length < namespace.branch.latestReleaseInstances.total">
<button class="btn btn-default" ng-click="loadInstanceInfo(namespace.branch)">加载更多</button>
</div>
</div>
<div class="text-center" ng-if="namespace.branch.latestReleaseInstances.total == 0">
无实例信息
</div>
</div>
</div>
<!--history view-->
<div class="J_historyview history-view" ng-show="namespace.branch.viewType == 'history'">
<div class="media" ng-repeat="commits in namespace.branch.commits">
<div class="media-body">
<div class="row">
<div class="col-md-6"><h3 class="media-heading"
ng-bind="commits.dataChangeCreatedBy"></h3>
</div>
<div class="col-md-6 text-right">
<h5 class="media-heading"
ng-bind="commits.dataChangeCreatedTime | date: 'yyyy-MM-dd HH:mm:ss'"></h5>
</div>
</div>
<!--properties format-->
<table class="table table-bordered table-striped text-center table-hover"
style="margin-top: 5px;"
ng-if="namespace.isPropertiesFormat">
<thead>
<tr>
<th>
Type
</th>
<th>
Key
</th>
<th>
Old Value
</th>
<th>
New Value
</th>
<th>
Comment
</th>
</tr>
</thead>
<tbody>
<!--兼容老数据,不显示item类型为空行和注释的item-->
<tr ng-repeat="item in commits.changeSets.createItems" ng-show="item.key">
<td width="2%">
新增
</td>
<td width="20%" title="{{item.key}}">
<span ng-bind="item.key | limitTo: 250"></span>
<span ng-bind="item.key.length > 250 ? '...' :''"></span>
</td>
<td width="30%">
</td>
<td width="30%" class="cursor-pointer" title="{{item.value}}"
ng-click="showText(item.value)">
<span ng-bind="item.value | limitTo: 250"></span>
<span ng-bind="item.value.length > 250 ? '...': ''"></span>
</td>
<td width="18%" title="{{item.comment}}">
<span ng-bind="item.comment | limitTo: 250"></span>
<span ng-bind="item.comment.length > 250 ?'...' : ''"></span>
</td>
</tr>
<tr ng-repeat="item in commits.changeSets.updateItems">
<td width="2%">
更新
</td>
<td width="20%" title="{{item.newItem.key}}">
<span ng-bind="item.newItem.key | limitTo: 250"></span>
<span ng-bind="item.newItem.key.length > 250 ? '...' :''"></span>
</td>
<td width="30%" class="cursor-pointer" title="{{item.oldItem.value}}"
ng-click="showText(item.oldItem.value)">
<span ng-bind="item.oldItem.value | limitTo: 250"></span>
<span ng-bind="item.oldItem.value.length > 250 ? '...': ''"></span>
</td>
<td width="30%" class="cursor-pointer" title="{{item.newItem.value}}"
ng-click="showText(item.newItem.value)">
<span ng-bind="item.newItem.value | limitTo: 250"></span>
<span ng-bind="item.newItem.value.length > 250 ? '...': ''"></span>
</td>
<td width="18%" title="{{item.newItem.comment}}">
<span ng-bind="item.newItem.comment | limitTo: 250"></span>
<span ng-bind="item.newItem.comment.length > 250 ?'...' : ''"></span>
</td>
</tr>
<tr ng-repeat="item in commits.changeSets.deleteItems"
ng-show="item.key || item.comment">
<td width="2%">
删除
</td>
<td width="20%" title="{{item.key}}">
<span ng-bind="item.key | limitTo: 250"></span>
<span ng-bind="item.key.length > 250 ? '...' :''"></span>
</td>
<td width="30%" title="{{item.value}}">
<span ng-bind="item.value | limitTo: 250"></span>
<span ng-bind="item.value.length > 250 ? '...': ''"></span>
</td>
<td width="30%">
</td>
<td width="18%" title="{{item.comment}}">
<span ng-bind="item.comment | limitTo: 250"></span>
<span ng-bind="item.comment.length > 250 ?'...' : ''"></span>
</td>
</tr>
</tbody>
</table>
<!--not properties format-->
<div ng-if="!namespace.isPropertiesFormat">
<div ng-repeat="item in commits.changeSets.createItems">
<textarea class="form-control no-radius" rows="20"
ng-disabled="true" ng-bind="item.value">
</textarea>
</div>
<div ng-repeat="item in commits.changeSets.updateItems">
<textarea class="form-control no-radius" rows="20"
ng-disabled="true" ng-bind="item.newItem.value">
</textarea>
</div>
</div>
</div>
<hr>
</div>
<div class="text-center">
<button type="button" class="btn btn-default" ng-show="!namespace.branch.hasLoadAllCommit"
ng-click="loadCommitHistory(namespace.branch)">加载更多
<span class="glyphicon glyphicon-menu-down"></span></button>
</div>
</div>
</section>
</section>
</section> </section>
...@@ -8,7 +8,7 @@ import com.ctrip.framework.apollo.core.enums.ConfigFileFormat; ...@@ -8,7 +8,7 @@ import com.ctrip.framework.apollo.core.enums.ConfigFileFormat;
import com.ctrip.framework.apollo.core.enums.Env; import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.api.AdminServiceAPI; import com.ctrip.framework.apollo.portal.api.AdminServiceAPI;
import com.ctrip.framework.apollo.portal.components.txtresolver.PropertyResolver; import com.ctrip.framework.apollo.portal.components.txtresolver.PropertyResolver;
import com.ctrip.framework.apollo.portal.entity.vo.NamespaceVO; import com.ctrip.framework.apollo.portal.entity.bo.NamespaceBO;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
...@@ -87,9 +87,9 @@ public class NamespaceServiceTest { ...@@ -87,9 +87,9 @@ public class NamespaceServiceTest {
when(releaseService.loadLatestRelease(appId, Env.DEV, clusterName, "hermes")).thenReturn(someRelease); when(releaseService.loadLatestRelease(appId, Env.DEV, clusterName, "hermes")).thenReturn(someRelease);
when(itemService.findItems(appId, Env.DEV, clusterName, namespaceName)).thenReturn(someItems); when(itemService.findItems(appId, Env.DEV, clusterName, namespaceName)).thenReturn(someItems);
List<NamespaceVO> namespaceVOs = namespaceService.findNamespaces(appId, Env.DEV, clusterName); List<NamespaceBO> namespaceVOs = namespaceService.findNamespaceBOs(appId, Env.DEV, clusterName);
assertEquals(2, namespaceVOs.size()); assertEquals(2, namespaceVOs.size());
NamespaceVO namespaceVO = namespaceVOs.get(0); NamespaceBO namespaceVO = namespaceVOs.get(0);
assertEquals(4, namespaceVO.getItems().size()); assertEquals(4, namespaceVO.getItems().size());
assertEquals("a", namespaceVO.getItems().get(0).getItem().getKey()); assertEquals("a", namespaceVO.getItems().get(0).getItem().getKey());
assertEquals(2, namespaceVO.getItemModifiedCnt()); assertEquals(2, namespaceVO.getItemModifiedCnt());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment