Commit c99fd43f by Yiming Liu

Merge pull request #82 from lepdou/v2

portal can edit config
parents 719d67d0 7b596e07
......@@ -3,6 +3,7 @@ package com.ctrip.apollo.adminservice.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
......@@ -18,8 +19,9 @@ public class ItemSetController {
private ItemSetService itemSetService;
@RequestMapping(path = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/itemset", method = RequestMethod.POST)
public ResponseEntity<Void> create(@RequestBody ItemChangeSets changeSet) {
itemSetService.updateSet(changeSet);
public ResponseEntity<Void> create(@PathVariable String appId, @PathVariable String clusterName,
@PathVariable String namespaceName, @RequestBody ItemChangeSets changeSet) {
itemSetService.updateSet(appId, clusterName, namespaceName, changeSet);
return ResponseEntity.status(HttpStatus.OK).build();
}
}
......@@ -47,12 +47,15 @@ public class ReleaseController {
@RequestMapping("/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases/latest")
public ReleaseDTO getLatest(@PathVariable("appId") String appId,
@PathVariable("clusterName") String clusterName,
@PathVariable("namespaceName") String namespaceName) {
@PathVariable("clusterName") String clusterName,
@PathVariable("namespaceName") String namespaceName) {
Release release = configService.findRelease(appId, clusterName, namespaceName);
if (release == null) throw new NotFoundException(
String.format("latest release not found for %s %s %s", appId, clusterName, namespaceName));
return BeanUtils.transfrom(ReleaseDTO.class, release);
if (release == null) {
throw new NotFoundException(
String.format("latest release not found for %s %s %s", appId, clusterName, namespaceName));
} else {
return BeanUtils.transfrom(ReleaseDTO.class, release);
}
}
@RequestMapping(path = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases", method = RequestMethod.POST)
......
......@@ -97,4 +97,5 @@ public abstract class BaseEntity {
private void preRemove() {
this.dataChangeLastModifiedTime = new Date();
}
}
......@@ -23,6 +23,9 @@ public class Item extends BaseEntity {
@Column
private String comment;
@Column
private int lineNum;
public String getComment() {
return comment;
}
......@@ -55,4 +58,11 @@ public class Item extends BaseEntity {
this.value = value;
}
public int getLineNum() {
return lineNum;
}
public void setLineNum(int lineNum) {
this.lineNum = lineNum;
}
}
......@@ -10,6 +10,6 @@ public interface ItemRepository extends PagingAndSortingRepository<Item, Long> {
List<Item> findByNamespaceIdIsIn(List<Long> namespaceIds);
List<Item> findByNamespaceId(Long namespaceId);
List<Item> findByNamespaceIdOrderByLineNumAsc(Long namespaceId);
}
......@@ -17,5 +17,5 @@ public interface ReleaseRepository extends PagingAndSortingRepository<Release, L
@Param("namespaceName") String namespaceName);
List<Release> findByAppIdAndClusterNameAndNamespaceName(String appId, String clusterName,
String namespaceName);
String namespaceName);
}
......@@ -4,32 +4,50 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ctrip.apollo.biz.entity.Item;
import com.ctrip.apollo.biz.entity.Namespace;
import com.ctrip.apollo.biz.repository.ItemRepository;
import com.ctrip.apollo.biz.repository.NamespaceRepository;
import com.ctrip.apollo.biz.utils.BeanUtils;
import com.ctrip.apollo.core.dto.ItemChangeSets;
import com.ctrip.apollo.core.dto.ItemDTO;
import java.util.Date;
@Service
public class ItemSetService {
@Autowired
private ItemRepository itemRepository;
@Autowired
private NamespaceRepository namespaceRepository;
public void updateSet(ItemChangeSets changeSet) {
public void updateSet(String appId, String clusterName, String namespaceName, ItemChangeSets changeSet) {
Namespace namespace = namespaceRepository.findByAppIdAndClusterNameAndNamespaceName(appId, clusterName, namespaceName);
String modifyBy = changeSet.getModifyBy();
for (ItemDTO item : changeSet.getCreateItems()) {
Item entity = BeanUtils.transfrom(Item.class, item);
entity.setNamespaceId(namespace.getId());
entity.setDataChangeCreatedBy(modifyBy);
entity.setDataChangeCreatedTime(new Date());
entity.setDataChangeLastModifiedBy(modifyBy);
itemRepository.save(entity);
}
for (ItemDTO item : changeSet.getUpdateItems()) {
Item entity = BeanUtils.transfrom(Item.class, item);
Item managedItem = itemRepository.findOne(entity.getId());
BeanUtils.copyEntityProperties(entity, managedItem);
if (managedItem != null){
BeanUtils.copyEntityProperties(entity, managedItem, "id", "namespaceId", "key", "dataChangeCreatedBy", "dataChangeCreatedTime");
managedItem.setDataChangeLastModifiedBy(modifyBy);
}
itemRepository.save(managedItem);
}
for (ItemDTO item : changeSet.getDeletedItems()) {
Item entity = BeanUtils.transfrom(Item.class, item);
entity.setDataChangeLastModifiedBy(modifyBy);
itemRepository.delete(entity.getId());
}
}
......
......@@ -46,7 +46,7 @@ public class ReleaseService {
throw new NotFoundException(String.format("Could not find namespace for %s %s %s", appId,
clusterName, namespaceName));
}
List<Item> items = itemRepository.findByNamespaceId(namespace.getId());
List<Item> items = itemRepository.findByNamespaceIdOrderByLineNumAsc(namespace.getId());
Map<String, String> configurations = new HashMap<String, String>();
for (Item item : items) {
configurations.put(item.getKey(), item.getValue());
......
......@@ -65,7 +65,7 @@ public class ViewService {
}
public List<Item> findItems(Long namespaceId) {
List<Item> items = itemRepository.findByNamespaceId(namespaceId);
List<Item> items = itemRepository.findByNamespaceIdOrderByLineNumAsc(namespaceId);
if (items == null) {
return Collections.EMPTY_LIST;
}
......
......@@ -214,7 +214,7 @@ public class BeanUtils {
* @param source
* @param target
*/
public static void copyEntityProperties(Object source, Object target) {
org.springframework.beans.BeanUtils.copyProperties(source, target, "id");
public static void copyEntityProperties(Object source, Object target, String... ignoreProperties) {
org.springframework.beans.BeanUtils.copyProperties(source, target, ignoreProperties);
}
}
......@@ -9,28 +9,19 @@ import java.util.List;
public class ItemChangeSets {
private String modifyBy;
private List<ItemDTO> createItems;
private List<ItemDTO> updateItems;
private List<ItemDTO> deletedItems;
private List<ItemDTO> createItems = new LinkedList<>();
private List<ItemDTO> updateItems = new LinkedList<>();
private List<ItemDTO> deletedItems = new LinkedList<>();
public void addCreatedItem(ItemDTO item) {
if (createItems == null) {
createItems = new LinkedList<>();
}
createItems.add(item);
}
public void addupdateItem(ItemDTO item) {
if (updateItems == null) {
updateItems = new LinkedList<>();
}
public void addUpdateItem(ItemDTO item) {
updateItems.add(item);
}
public void addDeletedItem(ItemDTO item) {
if (deletedItems == null) {
deletedItems = new LinkedList<>();
}
deletedItems.add(item);
}
......
......@@ -2,7 +2,7 @@ package com.ctrip.apollo.core.dto;
import java.util.Date;
public class ItemDTO {
public class ItemDTO{
private long id;
......@@ -14,6 +14,8 @@ public class ItemDTO {
private String comment;
private int lineNum;
private String dataChangeLastModifiedBy;
private Date dataChangeLastModifiedTime;
......@@ -67,6 +69,14 @@ public class ItemDTO {
this.value = value;
}
public int getLineNum() {
return lineNum;
}
public void setLineNum(int lineNum) {
this.lineNum = lineNum;
}
public String getDataChangeLastModifiedBy() {
return dataChangeLastModifiedBy;
}
......@@ -91,8 +101,10 @@ public class ItemDTO {
", key='" + key + '\'' +
", value='" + value + '\'' +
", comment='" + comment + '\'' +
", lineNum=" + lineNum +
", dataChangeLastModifiedBy='" + dataChangeLastModifiedBy + '\'' +
", dataChangeLastModifiedTime=" + dataChangeLastModifiedTime +
'}';
}
}
package com.ctrip.apollo.core.entity;
/**
* declare biz code and simple msg. maybe http response code is 200.
*/
public class SimpleRestfulResponse {
private int code;
private String msg;
public SimpleRestfulResponse(int code, String msg){
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
......@@ -4,12 +4,17 @@ package com.ctrip.apollo.portal.api;
import com.ctrip.apollo.Apollo;
import com.ctrip.apollo.core.dto.AppDTO;
import com.ctrip.apollo.core.dto.ClusterDTO;
import com.ctrip.apollo.core.dto.ItemChangeSets;
import com.ctrip.apollo.core.dto.ItemDTO;
import com.ctrip.apollo.core.dto.NamespaceDTO;
import com.ctrip.apollo.core.dto.ReleaseDTO;
import com.ctrip.apollo.core.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import java.util.Arrays;
import java.util.List;
......@@ -17,6 +22,7 @@ import java.util.List;
@Service
public class AdminServiceAPI {
private static final Logger logger = LoggerFactory.getLogger(AdminServiceAPI.class);
@Service
public static class AppAPI extends API {
......@@ -33,7 +39,7 @@ public class AdminServiceAPI {
public static class NamespaceAPI extends API {
public List<NamespaceDTO> findGroupsByAppAndCluster(String appId, Apollo.Env env,
String clusterName) {
String clusterName) {
if (StringUtils.isContainEmpty(appId, clusterName)) {
return null;
}
......@@ -48,52 +54,70 @@ public class AdminServiceAPI {
if (StringUtils.isContainEmpty(appId, clusterName, namespaceName)) {
return null;
}
return restTemplate.getForObject(getAdminServiceHost(env) +
String.format("apps/%s/clusters/%s/namespaces/%s", appId, clusterName,
namespaceName), NamespaceDTO.class);
}
return restTemplate.getForObject(getAdminServiceHost(env) +
String.format("apps/%s/clusters/%s/namespaces/%s", appId, clusterName,
namespaceName), NamespaceDTO.class);
}
}
@Service
public static class ItemAPI extends API {
public List<ItemDTO> findItems(String appId, Apollo.Env env, String clusterName, String namespace) {
if (StringUtils.isContainEmpty(appId, clusterName, namespace)) {
return null;
}
@Service
public static class ItemAPI extends API {
return Arrays.asList(restTemplate.getForObject(getAdminServiceHost(env) + String
.format("apps/%s/clusters/%s/namespaces/%s/items", appId,
clusterName, namespace),
ItemDTO[].class));
public List<ItemDTO> findItems(String appId, Apollo.Env env, String clusterName, String namespace) {
if (StringUtils.isContainEmpty(appId, clusterName, namespace)) {
return null;
}
return Arrays.asList(restTemplate.getForObject(getAdminServiceHost(env) + String
.format("apps/%s/clusters/%s/namespaces/%s/items", appId,
clusterName, namespace),
ItemDTO[].class));
}
@Service
public static class ClusterAPI extends API {
public void updateItems(String appId, Apollo.Env env, String clusterName, String namespace,
ItemChangeSets changeSets) {
if (StringUtils.isContainEmpty(appId, clusterName, namespace)){
return;
}
restTemplate.postForEntity(getAdminServiceHost(env) + String.format("apps/%s/clusters/%s/namespaces/%s/itemset",
appId,clusterName, namespace), changeSets, Void.class);
}
}
public List<ClusterDTO> findClustersByApp(String appId, Apollo.Env env) {
if (StringUtils.isContainEmpty(appId)) {
return null;
}
@Service
public static class ClusterAPI extends API {
return Arrays.asList(restTemplate.getForObject(getAdminServiceHost(env) + String.format("apps/%s/clusters", appId),
ClusterDTO[].class));
public List<ClusterDTO> findClustersByApp(String appId, Apollo.Env env) {
if (StringUtils.isContainEmpty(appId)) {
return null;
}
return Arrays
.asList(restTemplate.getForObject(getAdminServiceHost(env) + String.format("apps/%s/clusters", appId),
ClusterDTO[].class));
}
}
@Service
public static class ReleaseAPI extends API {
@Service
public static class ReleaseAPI extends API {
public ReleaseDTO loadLatestRelease(String appId, Apollo.Env env, String clusterName, String namespace) {
if (StringUtils.isContainEmpty(appId, clusterName, namespace)) {
return null;
}
return restTemplate.getForObject(getAdminServiceHost(env) + String
.format("apps/%s/clusters/%s/namespaces/%s/releases/latest", appId,
clusterName, namespace), ReleaseDTO.class);
public ReleaseDTO loadLatestRelease(String appId, Apollo.Env env, String clusterName, String namespace) {
if (StringUtils.isContainEmpty(appId, clusterName, namespace)) {
return null;
}
try {
ReleaseDTO releaseDTO = restTemplate.getForObject(getAdminServiceHost(env) + String
.format("apps/%s/clusters/%s/namespaces/%s/releases/latest", appId,
clusterName, namespace), ReleaseDTO.class);
return releaseDTO;
}catch (HttpClientErrorException e){
logger.warn(" call [ReleaseAPI.loadLatestRelease] and return not fount exception.app id:{}, env:{}, clusterName:{}, namespace:{}",
appId, env, clusterName, namespace);
return null;
}
}
}
}
......@@ -26,5 +26,7 @@ public class AppController {
return appService.buildClusterNavTree(appId);
}
}
......@@ -2,15 +2,13 @@ package com.ctrip.apollo.portal.controller;
import com.ctrip.apollo.Apollo;
import com.ctrip.apollo.core.entity.SimpleRestfulResponse;
import com.ctrip.apollo.core.utils.StringUtils;
import com.ctrip.apollo.portal.entity.NamespaceVO;
import com.ctrip.apollo.portal.entity.SimpleResponse;
import com.ctrip.apollo.portal.entity.SimpleMsg;
import com.ctrip.apollo.portal.service.ConfigService;
import com.ctrip.apollo.portal.service.txtresolver.TextResolverResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
......@@ -37,19 +35,16 @@ public class ConfigController {
}
@RequestMapping(value = "/apps/{appId}/env/{env}/clusters/{clusterName}/namespaces/{namespaceName}/modify", method = RequestMethod.GET)
public ResponseEntity<SimpleRestfulResponse> modifyConfigs(@PathVariable String appId, @PathVariable String env,
@PathVariable String clusterName,
@PathVariable String namespaceName,
String configText) {
public ResponseEntity<SimpleMsg> modifyConfigs(@PathVariable String appId, @PathVariable String env,
@PathVariable String clusterName,
@PathVariable String namespaceName,
String configText) {
TextResolverResult result =
configService.resolve(appId, Apollo.Env.valueOf(env), clusterName, namespaceName, configText);
TextResolverResult.Code code = result.getCode();
if (code == TextResolverResult.Code.OK) {
return ResponseEntity.status(HttpStatus.OK).body(new SimpleRestfulResponse(code.getValue(), "success"));
if (result.isResolveSuccess()) {
return ResponseEntity.ok().body(new SimpleMsg("success"));
} else {
return ResponseEntity.status(HttpStatus.OK)
.body(new SimpleRestfulResponse(code.getValue(), code.getBaseMsg() + result.getExtensionMsg()));
return ResponseEntity.badRequest().body(new SimpleMsg(result.getMsg()));
}
}
......
......@@ -71,6 +71,7 @@ public class NamespaceVO {
public void setNewValue(String newValue) {
this.newValue = newValue;
}
}
}
package com.ctrip.apollo.portal.entity;
public class SimpleResponse {
public class SimpleMsg {
private int code;
private String msg;
public SimpleResponse(int code, String msg){
this.code = code;
public SimpleMsg(String msg){
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
......
......@@ -50,19 +50,51 @@ public class ConfigService {
List<NamespaceVO> namespaceVOs = new LinkedList<>();
for (NamespaceDTO namespace : namespaces) {
namespaceVOs.add(parseNamespace(appId, env, clusterName, namespace));
NamespaceVO namespaceVO = null;
try {
namespaceVO = parseNamespace(appId, env, clusterName, namespace);
namespaceVOs.add(namespaceVO);
} catch (Exception e) {
logger.error("parse namespace error. app id:{}, env:{}, clusterName:{}, namespace:{}", appId, env, clusterName,
namespace.getNamespaceName(), e);
return namespaceVOs;
}
}
return namespaceVOs;
}
public TextResolverResult resolve(String appId, Apollo.Env env, String clusterName, String namespaceName,
String configText) {
TextResolverResult result = resolver.resolve(configText, itemAPI.findItems(appId, env, clusterName, namespaceName));
if (result.getCode() == TextResolverResult.Code.OK) {
ItemChangeSets changeSets = result.getChangeSets();
//invoke admin service
String configText) {
TextResolverResult result = new TextResolverResult();
try {
result = resolver.resolve(configText, itemAPI.findItems(appId, env, clusterName, namespaceName));
} catch (Exception e) {
logger
.error("resolve config text error. app id:{}, env:{}, clusterName:{}, namespace:{}", appId, env, clusterName,
namespaceName, e);
result.setResolveSuccess(false);
result.setMsg("oops! server resolve config text error.");
return result;
}
if (result.isResolveSuccess()) {
try {
// TODO: 16/4/13
result.getChangeSets().setModifyBy("lepdou");
itemAPI.updateItems(appId, env, clusterName, namespaceName, result.getChangeSets());
} catch (Exception e) {
logger.error("resolve config text error. app id:{}, env:{}, clusterName:{}, namespace:{}", appId, env,
clusterName, namespaceName, e);
result.setResolveSuccess(false);
result.setMsg("oops! server update config error.");
return result;
}
} else {
logger.warn("resolve config text error by format error. app id:{}, env:{}, clusterName:{}, namespace:{},cause:{}",
appId,env, clusterName, namespaceName, result.getMsg());
}
return result;
}
......@@ -72,6 +104,7 @@ public class ConfigService {
namespaceVO.setNamespace(namespace);
List<NamespaceVO.ItemVO> itemVos = new LinkedList<>();
namespaceVO.setItems(itemVos);
String namespaceName = namespace.getNamespaceName();
......@@ -84,6 +117,7 @@ public class ConfigService {
} catch (IOException e) {
logger.error("parse release json error. appId:{},env:{},clusterName:{},namespace:{}", appId,
env, clusterName, namespaceName);
return namespaceVO;
}
}
......@@ -101,7 +135,6 @@ public class ConfigService {
itemVos.add(itemVO);
}
namespaceVO.setItemModifiedCnt(modifiedItemCnt);
namespaceVO.setItems(itemVos);
return namespaceVO;
}
......
package com.ctrip.apollo.portal.service.txtresolver;
import com.ctrip.apollo.core.dto.ItemChangeSets;
import com.ctrip.apollo.core.dto.ItemDTO;
import com.ctrip.apollo.core.utils.StringUtils;
import com.ctrip.apollo.portal.util.BeanUtils;
import com.sun.tools.javac.util.Assert;
import org.apache.commons.collections.map.HashedMap;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
/**
* normal property file resolver.
* update comment and blank item implement by create new item and delete old item.
* update normal key/value item implement by update.
*/
@Component
public class PropertyResolver implements ConfigTextResolver {
private static final String KV_SEPARATOR = "=";
private static final String ITEM_SEPARATOR = "\n";
@Override
public TextResolverResult resolve(String configText, List<ItemDTO> baseItems) {
TextResolverResult result = new TextResolverResult();
if (StringUtils.isEmpty(configText)){
result.setResolveSuccess(false);
result.setMsg("config text can not be empty");
return result;
}
Map<Integer, ItemDTO> oldLineNumMapItem = BeanUtils.mapByKey("lineNum", baseItems);
Map<String, ItemDTO> oldKeyMapItem = BeanUtils.mapByKey("key", baseItems);
//remove comment and blank item map.
oldKeyMapItem.remove("");
String[] newItems = configText.split(ITEM_SEPARATOR);
ItemChangeSets changeSets = new ItemChangeSets();
result.setChangeSets(changeSets);
Map<Integer, String> newLineNumMapItem = new HashedMap();//use for delete blank and comment item
int lineCounter = 1;
for (String newItem : newItems) {
newItem = newItem.trim();
newLineNumMapItem.put(lineCounter, newItem);
ItemDTO oldItemByLine = oldLineNumMapItem.get(lineCounter);
//comment item
if (isCommentItem(newItem)) {
handleCommentLine(oldItemByLine, newItem, lineCounter, changeSets);
//blank item
} else if (isBlankItem(newItem)) {
handleBlankLine(oldItemByLine, lineCounter, changeSets);
//normal line
} else {
if (!handleNormalLine(oldKeyMapItem, newItem, lineCounter, result)) {
return result;
}
}
lineCounter++;
}
deleteCommentAndBlankItem(oldLineNumMapItem, newLineNumMapItem, changeSets);
deleteNormalKVItem(oldKeyMapItem, changeSets);
result.setResolveSuccess(true);
return result;
}
private void handleCommentLine(ItemDTO oldItemByLine, String newItem, int lineCounter, ItemChangeSets changeSets) {
String oldComment = oldItemByLine == null ? "" : oldItemByLine.getComment();
//create comment. implement update comment by delete old comment and create new comment
if (!(isCommentItem(oldItemByLine) && newItem.equals(oldComment))) {
changeSets.addCreatedItem(buildCommentItem(0l, newItem, lineCounter));
}
}
private void handleBlankLine(ItemDTO oldItem, int lineCounter, ItemChangeSets changeSets) {
if (!isBlankItem(oldItem)) {
changeSets.addCreatedItem(buildBlankItem(0l, lineCounter));
}
}
private boolean handleNormalLine(Map<String, ItemDTO> keyMapOldItem, String newItem,
int lineCounter, TextResolverResult result) {
ItemChangeSets changeSets = result.getChangeSets();
int kvSeparator = newItem.indexOf(KV_SEPARATOR);
if (kvSeparator == -1) {
result.setResolveSuccess(false);
result.setMsg(" line:" + lineCounter + " key value must separate by '='");
return false;
}
String newKey = newItem.substring(0, kvSeparator).trim();
String newValue = newItem.substring(kvSeparator + 1, newItem.length()).trim();
ItemDTO oldItem = keyMapOldItem.get(newKey);
if (oldItem == null) {//new item
changeSets.addCreatedItem(buildNormalItem(0l, newKey, newValue, "", lineCounter));
} else if (!newValue.equals(oldItem.getValue())){//update item
changeSets.addUpdateItem(
buildNormalItem(oldItem.getId(), newKey, newValue, oldItem.getComment(),
lineCounter));
}
keyMapOldItem.remove(newKey);
return true;
}
private boolean isCommentItem(ItemDTO item) {
return item != null && "".equals(item.getKey()) && item.getComment().startsWith("#");
}
private boolean isCommentItem(String line) {
return line != null && line.startsWith("#");
}
private boolean isBlankItem(ItemDTO item) {
return item != null && "".equals(item.getKey()) && "".equals(item.getComment());
}
private boolean isBlankItem(String line) {
return "".equals(line);
}
private void deleteNormalKVItem(Map<String, ItemDTO> baseKeyMapItem, ItemChangeSets changeSets) {
//surplus item is to be deleted
for (Map.Entry<String, ItemDTO> entry : baseKeyMapItem.entrySet()) {
changeSets.addDeletedItem(entry.getValue());
}
}
private void deleteCommentAndBlankItem(Map<Integer, ItemDTO> oldLineNumMapItem,
Map<Integer, String> newLineNumMapItem,
ItemChangeSets changeSets) {
for (Map.Entry<Integer, ItemDTO> entry : oldLineNumMapItem.entrySet()) {
int lineNum = entry.getKey();
ItemDTO oldItem = entry.getValue();
String newItem = newLineNumMapItem.get(lineNum);
//1. old is blank by now is not
//2.old is comment by now is not exist or modified
if ((isBlankItem(oldItem) && !isBlankItem(newItem))
|| isCommentItem(oldItem) && (newItem == null || !newItem.equals(oldItem))) {
changeSets.addDeletedItem(oldItem);
}
}
}
private ItemDTO buildCommentItem(Long id, String comment, int lineNum) {
return buildNormalItem(id, "", "", comment, lineNum);
}
private ItemDTO buildBlankItem(Long id, int lineNum) {
return buildNormalItem(id, "", "", "", lineNum);
}
private ItemDTO buildNormalItem(Long id, String key, String value, String comment, int lineNum) {
ItemDTO item = new ItemDTO();
item.setId(id);
item.setKey(key);
item.setValue(value);
item.setComment(comment);
item.setLineNum(lineNum);
return item;
}
}
package com.ctrip.apollo.portal.service.txtresolver;
import com.ctrip.apollo.core.dto.ItemChangeSets;
import com.ctrip.apollo.core.dto.ItemDTO;
import com.ctrip.apollo.core.utils.StringUtils;
import com.ctrip.apollo.portal.util.BeanUtils;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
/**
* config item format is K:V##C
*
* @Autor lepdou
*/
@Component
public class SimpleKVCResolver implements ConfigTextResolver {
private static final String KV_SEPARATOR = ":";
private static final String VC_SEPARATOR = "##";
private static final String ITEM_SEPARATOR = "\n";
@Override
public TextResolverResult resolve(String configText, List<ItemDTO> baseItems) {
TextResolverResult result = new TextResolverResult();
if (StringUtils.isEmpty(configText)) {
result.setCode(TextResolverResult.Code.SIMPLE_KVC_TEXT_EMPTY);
return result;
}
Map<String, ItemDTO> baseKeyMapItem = BeanUtils.mapByKey("key", baseItems);
String[] items = configText.split(ITEM_SEPARATOR);
ItemChangeSets changeSets = new ItemChangeSets();
int lineCounter = 1;
int kvSeparator, vcSeparator;
String key, value, comment;
for (String item : items) {
kvSeparator = item.indexOf(KV_SEPARATOR);
vcSeparator = item.indexOf(VC_SEPARATOR);
if (kvSeparator == -1 || vcSeparator == -1) {
result.setCode(TextResolverResult.Code.SIMPLTE_KVC_INVALID_FORMAT);
result.setExtensionMsg(" line:" + lineCounter);
return result;
}
key = item.substring(0, kvSeparator).trim();
value = item.substring(kvSeparator + 1, vcSeparator).trim();
comment = item.substring(vcSeparator + 2, item.length()).trim();
ItemDTO baseItem = baseKeyMapItem.get(key);
if (baseItem == null) {//new item
changeSets.addCreatedItem(buildItem(key, value, comment));
} else if (!value.equals(baseItem.getValue()) || !comment.equals(baseItem.getComment())) {//update item
changeSets.addupdateItem(buildItem(key, value, comment));
}
//deleted items:items in baseItems but not in configText
baseKeyMapItem.remove(key);
lineCounter ++;
}
//deleted items
for (Map.Entry<String, ItemDTO> entry : baseKeyMapItem.entrySet()) {
changeSets.addDeletedItem(entry.getValue());
}
result.setCode(TextResolverResult.Code.OK);
result.setChangeSets(changeSets);
return result;
}
private ItemDTO buildItem(String key, String value, String comment) {
ItemDTO item = new ItemDTO();
item.setKey(key);
item.setValue(value);
item.setComment(comment);
return item;
}
}
......@@ -4,28 +4,27 @@ import com.ctrip.apollo.core.dto.ItemChangeSets;
public class TextResolverResult {
private Code code;
private boolean isResolveSuccess;
/**
* extension msg. for example line number.
* error msg
*/
private String extensionMsg = "";
private String msg = "";
private ItemChangeSets changeSets;
public Code getCode() {
return code;
public boolean isResolveSuccess() {
return isResolveSuccess;
}
public void setCode(Code code) {
this.code = code;
public void setResolveSuccess(boolean resolveSuccess) {
isResolveSuccess = resolveSuccess;
}
public String getExtensionMsg() {
return extensionMsg;
public String getMsg() {
return msg;
}
public void setExtensionMsg(String extensionMsg) {
this.extensionMsg = extensionMsg;
public void setMsg(String msg) {
this.msg = msg;
}
public ItemChangeSets getChangeSets() {
......@@ -36,32 +35,4 @@ public class TextResolverResult {
this.changeSets = changeSets;
}
public enum Code {
OK(200, "success"), SIMPLTE_KVC_INVALID_FORMAT(40001, "item pattern must key:value##comment.pelease check!"),
SIMPLE_KVC_TEXT_EMPTY(40002, "config text empty");
private int value;
private String baseMsg;
Code(int value, String msg) {
this.value = value;
this.baseMsg = msg;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public String getBaseMsg() {
return baseMsg;
}
public void setBaseMsg(String baseMsg) {
this.baseMsg = baseMsg;
}
}
}
......@@ -14,7 +14,7 @@ create_app_module.controller('CreateAppController', ['$scope', '$window', 'toast
AppService.add($scope.app).then(function (result) {
toastr.success('添加成功!');
setInterval(function () {
$window.location.href = '/views/app/index.html?#appid=' + result.appId;
$window.location.href = '/views/app.html?#appid=' + result.appId;
}, 1000);
}, function (result) {
toastr.error('添加失败!');
......
......@@ -2,11 +2,18 @@ application_module.controller("AppConfigController",
['$scope', '$location', 'toastr', 'AppService', 'ConfigService',
function ($scope, $location, toastr, AppService, ConfigService) {
$scope.appId = $location.$$url.split("=")[1];
var appId = $location.$$url.split("=")[1];
var pageContext = {
appId: appId,
env: 'LOCAL',
clusterName: 'default'
};
$scope.pageEnv = pageContext;
/////////////
AppService.load_nav_tree($scope.appId).then(function (result) {
AppService.load_nav_tree($scope.pageEnv.appId).then(function (result) {
var navTree = [];
var nodes = result.nodes;
nodes.forEach(function (item) {
......@@ -37,9 +44,8 @@ application_module.controller("AppConfigController",
///////////
$scope.env = 'LOCAL';
$scope.clusterName = 'default';
ConfigService.load_all_namespaces($scope.appId, $scope.env, $scope.clusterName).then(
ConfigService.load_all_namespaces($scope.pageEnv.appId, $scope.pageEnv.env,
$scope.pageEnv.clusterName).then(
function (result) {
$scope.namespaces = result;
......@@ -48,30 +54,49 @@ application_module.controller("AppConfigController",
$scope.namespaces.forEach(function (item) {
item.isModify = false;
item.viewType = 'table';
item.isTextEditing = false;
})
}
}, function (result) {
toastr.error("加载配置信息出错:" + result);
toastr.error("加载配置信息出错");
});
$scope.draft = {};
//保存草稿
$scope.saveDraft = function (namespace) {
$scope.draft = namespace;
};
//更新配置
$scope.modifyItems = function (namespace) {
ConfigService.modify_items($scope.appId, $scope.env, $scope.clusterName,
namespace.namespace.namespaceName, namespace.text).then(
$scope.commitChange = function () {
ConfigService.modify_items($scope.pageEnv.appId, $scope.pageEnv.env, $scope.pageEnv.clusterName,
$scope.draft.namespace.namespaceName, $scope.draft.text).then(
function (result) {
if (result.code == 200){
toastr.success("更新成功");
}else {
toastr.error("更新失败. code:" + result.code + " msg:" + result.msg);
}
},function (result) {
toastr.success("更新成功");
$scope.draft.backupText = '';//清空备份文本
$scope.toggleTextEditStatus($scope.draft);
}, function (result) {
toastr.error(result.data.msg, "更新失败");
}
);
};
/////////
//文本编辑框状态切换
$scope.toggleTextEditStatus = function (namespace) {
namespace.isTextEditing = !namespace.isTextEditing;
if (namespace.isTextEditing){//切换为编辑状态,保存一下原来值
$scope.draft.backupText = namespace.text;
}else {
if ($scope.draft.backupText){//取消编辑,则复原
namespace.text = $scope.draft.backupText;
}
}
};
$scope.queryOldValue = function (key, oldValue) {
$scope.queryKey = key;
if (oldValue == '') {
......@@ -101,8 +126,13 @@ application_module.controller("AppConfigController",
// if (item.modified) {
// result += "**";
// }
result +=
item.item.key + ":" + item.item.value + " ##" + item.item.comment + "\n";
if (item.item.key) {
result +=
item.item.key + " = " + item.item.value + "\n";
} else {
result += item.item.comment + "\n";
}
});
return result;
......
application_module.controller("AppInfoController", ["$scope", '$rootScope', '$state', '$location', 'toastr', 'AppService',
function ($scope, $rootScope, $state, $location, toastr, AppService) {
$rootScope.breadcrumb.nav = '应用信息';
$rootScope.breadcrumb.env = '';
AppService.load($scope.appId).then(function (result) {
$scope.app = result;
}, function (result) {
toastr.error("加载出错");
});
}]);
//page context ctl
application_module.controller("AppPageController", ['$rootScope', '$location',
function ($rootScope, $location) {
$rootScope.appId = $location.$$url.split("=")[1];
if (!$rootScope.appId) {
$rootScope.appId = 6666;
}
$rootScope.breadcrumb = {
project: '6666-apollo',
nav: '配置',
env: 'uat'
}
}]);
appService.service('EnvService', ['$resource', '$q', function ($resource, $q) {
var env_resource = $resource('/envs', {}, {
all: {
method: 'GET',
isArray: true
}
});
return {
getAllEnvs: function getAllEnvs() {
var d = $q.defer();
env_resource.all({}, function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
});
return d.promise;
}
}
}]);
appService.service("VersionService", ['$resource', '$q', function ($resource, $q) {
var config_source = $resource("/version/:appId/:env", {}, {
load_config: {
method: 'GET',
isArray: true
}
});
return {
load: function (appId, env) {
var d = $q.defer();
config_source.load_config({
appId: appId,
env: env
}, function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
});
return d.promise;
}
}
}]);
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel">
<div class="panel-body">
<form class="form-horizontal">
<div class="form-group">
<label class="col-sm-2 control-label">应用ID:</label>
<label class="col-sm-3 control-label text-left">
{{app.appId}}
</label>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">应用名称:</label>
<label class="col-sm-3 control-label text-left">
{{app.name}}
</label>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Owner:</label>
<label class="col-sm-3 control-label text-left">
{{app.owner}}
</label>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">手机号:</label>
<label class="col-sm-3 control-label text-left">
{{app.ownerPhone}}
</label>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">邮箱地址:</label>
<label class="col-sm-3 control-label text-left">
{{app.ownerMail}}
</label>
</div>
</form>
</div>
</div>
</div>
</div>
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