Commit 7c02e344 by Yiming Liu

Merge pull request #187 from lepdou/multirequest

多环境操作容错处理
parents dc493792 b01275e1
package com.ctrip.apollo.common.http;
import org.springframework.http.HttpStatus;
import java.util.LinkedList;
import java.util.List;
/**
* 一个Response中包含多个ResponseEntity
*/
public class MultiResponseEntity<T> {
private int code;
private List<RichResponseEntity<T>> entities = new LinkedList<>();
private MultiResponseEntity(HttpStatus httpCode) {
this.code = httpCode.value();
}
public static <T> MultiResponseEntity<T> instance(HttpStatus statusCode) {
return new MultiResponseEntity<>(statusCode);
}
public static <T> MultiResponseEntity<T> ok() {
return new MultiResponseEntity<>(HttpStatus.OK);
}
public void addResponseEntity(RichResponseEntity<T> responseEntity) {
if (responseEntity == null){
throw new IllegalArgumentException("sub response entity can not be null");
}
entities.add(responseEntity);
}
}
package com.ctrip.apollo.common.http;
import org.springframework.http.HttpStatus;
public class RichResponseEntity<T>{
private int code;
private Object message;
private T body;
public static <T> RichResponseEntity<T> ok(T body){
RichResponseEntity<T> richResponseEntity = new RichResponseEntity<>();
richResponseEntity.message = HttpStatus.OK.getReasonPhrase();
richResponseEntity.code = HttpStatus.OK.value();
richResponseEntity.body = body;
return richResponseEntity;
}
public static <T> RichResponseEntity<T> error(HttpStatus httpCode, Object message){
RichResponseEntity<T> richResponseEntity = new RichResponseEntity<>();
richResponseEntity.message = message;
richResponseEntity.code = httpCode.value();
return richResponseEntity;
}
public int getCode() {
return code;
}
public Object getMessage() {
return message;
}
public T getBody() {
return body;
}
}
package com.ctrip.apollo.portal.controller; package com.ctrip.apollo.portal.controller;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable; 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.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.HttpClientErrorException;
import com.ctrip.apollo.common.http.MultiResponseEntity;
import com.ctrip.apollo.common.http.RichResponseEntity;
import com.ctrip.apollo.core.dto.AppDTO; import com.ctrip.apollo.core.dto.AppDTO;
import com.ctrip.apollo.core.enums.Env; import com.ctrip.apollo.core.enums.Env;
import com.ctrip.apollo.core.exception.BadRequestException; import com.ctrip.apollo.core.exception.BadRequestException;
import com.ctrip.apollo.core.utils.StringUtils; import com.ctrip.apollo.core.utils.StringUtils;
import com.ctrip.apollo.portal.entity.AppInfoVO; import com.ctrip.apollo.portal.PortalSettings;
import com.ctrip.apollo.portal.entity.ClusterNavTree; import com.ctrip.apollo.portal.entity.EnvClusterInfo;
import com.ctrip.apollo.portal.service.AppService; import com.ctrip.apollo.portal.service.AppService;
import java.util.List; import java.util.List;
...@@ -25,6 +29,8 @@ public class AppController { ...@@ -25,6 +29,8 @@ public class AppController {
@Autowired @Autowired
private AppService appService; private AppService appService;
@Autowired
private PortalSettings portalSettings;
@RequestMapping("/envs/{env}") @RequestMapping("/envs/{env}")
public List<AppDTO> findAllApp(@PathVariable String env){ public List<AppDTO> findAllApp(@PathVariable String env){
...@@ -35,12 +41,23 @@ public class AppController { ...@@ -35,12 +41,23 @@ public class AppController {
} }
@RequestMapping("/{appId}/navtree") @RequestMapping("/{appId}/navtree")
public ClusterNavTree nav(@PathVariable String appId) { public MultiResponseEntity<EnvClusterInfo> nav(@PathVariable String appId) {
if (StringUtils.isEmpty(appId)) { if (StringUtils.isEmpty(appId)) {
throw new BadRequestException("app id can not be empty."); throw new BadRequestException("app id can not be empty.");
} }
MultiResponseEntity<EnvClusterInfo> response = MultiResponseEntity.ok();
return appService.buildClusterNavTree(appId); List<Env> envs = portalSettings.getEnvs();
for (Env env : envs) {
try {
response.addResponseEntity(RichResponseEntity.ok(appService.createEnvNavNode(env, appId)));
} catch (Exception e) {
response.addResponseEntity(RichResponseEntity.error(HttpStatus.INTERNAL_SERVER_ERROR,
"load env:" + env.name() + " cluster error." + e
.getMessage()));
}
}
return response;
} }
@RequestMapping(value = "/envs/{env}", method = RequestMethod.POST, consumes = {"application/json"}) @RequestMapping(value = "/envs/{env}", method = RequestMethod.POST, consumes = {"application/json"})
...@@ -57,13 +74,38 @@ public class AppController { ...@@ -57,13 +74,38 @@ public class AppController {
} }
@RequestMapping(value = "/{appId}", method = RequestMethod.GET) @RequestMapping(value = "/{appId}", method = RequestMethod.GET)
public AppInfoVO load(@PathVariable String appId){ public AppDTO load(@PathVariable String appId){
if (StringUtils.isEmpty(appId)){ if (StringUtils.isEmpty(appId)){
throw new BadRequestException("app id can not be empty."); throw new BadRequestException("app id can not be empty.");
} }
return appService.load(appId); return appService.load(appId);
} }
@RequestMapping(value = "/{appId}/miss_envs")
public MultiResponseEntity<Env> findMissEnvs(@PathVariable String appId) {
MultiResponseEntity<Env> response = MultiResponseEntity.ok();
for (Env env : portalSettings.getEnvs()) {
try {
appService.load(env, appId);
} catch (Exception e) {
if (e instanceof HttpClientErrorException &&
((HttpClientErrorException)e).getStatusCode() == HttpStatus.NOT_FOUND){
response.addResponseEntity(RichResponseEntity.ok(env));
} else {
response.addResponseEntity(RichResponseEntity.error(HttpStatus.INTERNAL_SERVER_ERROR,
String
.format("load appId:%s from env %s error.", appId,
env)
+ e.getMessage()));
}
}
}
return response;
}
private boolean isInvalidApp(AppDTO app) { private boolean isInvalidApp(AppDTO app) {
return StringUtils.isContainEmpty(app.getName(), app.getAppId(), app.getOwnerEmail(), app.getOwnerName()); return StringUtils.isContainEmpty(app.getName(), app.getAppId(), app.getOwnerEmail(), app.getOwnerName());
} }
......
package com.ctrip.apollo.portal.entity;
import com.ctrip.apollo.core.dto.AppDTO;
import com.ctrip.apollo.core.enums.Env;
import java.util.List;
public class AppInfoVO {
private AppDTO app;
/**
* 在创建app的时候可能在某些环境下创建失败
*/
private List<Env> missEnvs;
public AppDTO getApp() {
return app;
}
public void setApp(AppDTO app) {
this.app = app;
}
public List<Env> getMissEnvs() {
return missEnvs;
}
public void setMissEnvs(List<Env> missEnvs) {
this.missEnvs = missEnvs;
}
}
package com.ctrip.apollo.portal.entity; package com.ctrip.apollo.portal.entity;
import com.ctrip.apollo.core.enums.Env;
import com.ctrip.apollo.core.dto.ClusterDTO; import com.ctrip.apollo.core.dto.ClusterDTO;
import com.ctrip.apollo.core.enums.Env;
import java.util.LinkedList;
import java.util.List; import java.util.List;
public class ClusterNavTree { public class EnvClusterInfo {
private List<Node> nodes;
public void addNode(Node node){
if (nodes == null){
nodes = new LinkedList<>();
}
nodes.add(node);
}
public static class Node{
private Env env; private Env env;
private List<ClusterDTO> clusters; private List<ClusterDTO> clusters;
public Node(Env env){ public EnvClusterInfo(Env env){
this.env = env; this.env = env;
} }
...@@ -41,14 +28,5 @@ public class ClusterNavTree { ...@@ -41,14 +28,5 @@ public class ClusterNavTree {
public void setClusters(List<ClusterDTO> clusters) { public void setClusters(List<ClusterDTO> clusters) {
this.clusters = clusters; this.clusters = clusters;
} }
}
public List<Node> getNodes() {
return nodes;
}
public void setNodes(List<Node> nodes) {
this.nodes = nodes;
}
} }
package com.ctrip.apollo.portal.service; package com.ctrip.apollo.portal.service;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import org.slf4j.Logger; import org.slf4j.Logger;
...@@ -18,8 +17,7 @@ import com.ctrip.apollo.core.exception.BadRequestException; ...@@ -18,8 +17,7 @@ import com.ctrip.apollo.core.exception.BadRequestException;
import com.ctrip.apollo.core.exception.ServiceException; import com.ctrip.apollo.core.exception.ServiceException;
import com.ctrip.apollo.portal.PortalSettings; import com.ctrip.apollo.portal.PortalSettings;
import com.ctrip.apollo.portal.api.AdminServiceAPI; import com.ctrip.apollo.portal.api.AdminServiceAPI;
import com.ctrip.apollo.portal.entity.AppInfoVO; import com.ctrip.apollo.portal.entity.EnvClusterInfo;
import com.ctrip.apollo.portal.entity.ClusterNavTree;
@Service @Service
public class AppService { public class AppService {
...@@ -27,60 +25,48 @@ public class AppService { ...@@ -27,60 +25,48 @@ public class AppService {
private Logger logger = LoggerFactory.getLogger(AppService.class); private Logger logger = LoggerFactory.getLogger(AppService.class);
@Autowired @Autowired
private ClusterService clusterService;
@Autowired
private PortalSettings portalSettings; private PortalSettings portalSettings;
@Autowired @Autowired
private AdminServiceAPI.AppAPI appAPI; private AdminServiceAPI.AppAPI appAPI;
@Autowired ClusterService clusterService;
public List<AppDTO> findAll(Env env) { public List<AppDTO> findAll(Env env) {
return appAPI.findApps(env); return appAPI.findApps(env);
} }
public AppInfoVO load(String appId) { public AppDTO load(String appId) {
//轮询环境直到能找到此app的信息 //轮询环境直到能找到此app的信息
AppDTO app = null; AppDTO app = null;
List<Env> missEnvs = new LinkedList<>(); boolean isCallAdminServiceError = false;
for (Env env : portalSettings.getEnvs()) { for (Env env : portalSettings.getEnvs()) {
try { try {
app = appAPI.loadApp(env, appId); app = appAPI.loadApp(env, appId);
break;
} catch (HttpClientErrorException e) { } catch (HttpClientErrorException e) {
//not exist maybe because create app fail. //not exist maybe because create app fail.
if (e.getStatusCode() == HttpStatus.NOT_FOUND) { if (e.getStatusCode() == HttpStatus.NOT_FOUND) {
missEnvs.add(env);
logger.warn("app:{} in {} not exist", appId, env); logger.warn("app:{} in {} not exist", appId, env);
} else { } else {
isCallAdminServiceError = true;
logger.error("load app info({}) from env:{} error.", appId, env); logger.error("load app info({}) from env:{} error.", appId, env);
throw new ServiceException("can not load app from all envs");
} }
} }
} }
if (app == null) { if (app == null) {
if (isCallAdminServiceError){
throw new ServiceException("call admin service error");
}else {
throw new BadRequestException(String.format("invalid app id %s", appId)); throw new BadRequestException(String.format("invalid app id %s", appId));
} }
AppInfoVO appInfo = new AppInfoVO();
appInfo.setApp(app);
appInfo.setMissEnvs(missEnvs);
return appInfo;
} }
public ClusterNavTree buildClusterNavTree(String appId) { return app;
ClusterNavTree tree = new ClusterNavTree();
List<Env> envs = portalSettings.getEnvs();
for (Env env : envs) {
ClusterNavTree.Node clusterNode = new ClusterNavTree.Node(env);
clusterNode.setClusters(clusterService.findClusters(env, appId));
tree.addNode(clusterNode);
} }
return tree;
public AppDTO load(Env env, String appId){
return appAPI.loadApp(env, appId);
} }
public void createAppInAllEnvs(AppDTO app) { public void createAppInAllEnvs(AppDTO app) {
...@@ -104,4 +90,10 @@ public class AppService { ...@@ -104,4 +90,10 @@ public class AppService {
} }
} }
public EnvClusterInfo createEnvNavNode(Env env, String appId){
EnvClusterInfo node = new EnvClusterInfo(env);
node.setClusters(clusterService.findClusters(env, appId));
return node;
}
} }
appUtil.service('AppUtil', [function () { appUtil.service('AppUtil', ['toastr', function (toastr) {
return { return {
errorMsg: function (response) { errorMsg: function (response) {
...@@ -22,6 +22,17 @@ appUtil.service('AppUtil', [function () { ...@@ -22,6 +22,17 @@ appUtil.service('AppUtil', [function () {
result[kv[0]] = kv[1]; result[kv[0]] = kv[1];
}); });
return result; return result;
},
collectData: function (response) {
var data = [];
response.entities.forEach(function (entity) {
if (entity.code == 200){
data.push(entity.body);
}else {
toastr.warning(entity.message);
}
});
return data;
} }
} }
}]); }]);
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
var appService = angular.module('app.service', ['ngResource']); var appService = angular.module('app.service', ['ngResource']);
/**utils*/ /**utils*/
var appUtil = angular.module('app.util', []); var appUtil = angular.module('app.util', ['toastr']);
/** directive */ /** directive */
var directive_module = angular.module('apollo.directive', ['app.service']); var directive_module = angular.module('apollo.directive', ['app.service']);
......
...@@ -11,7 +11,8 @@ namespace_module.controller("LinkNamespaceController", ...@@ -11,7 +11,8 @@ namespace_module.controller("LinkNamespaceController",
////// load env ////// ////// load env //////
AppService.load_nav_tree($scope.appId).then(function (result) { AppService.load_nav_tree($scope.appId).then(function (result) {
$scope.namespaceIdentifers = []; $scope.namespaceIdentifers = [];
result.nodes.forEach(function (node) { var envClusterInfo = AppUtil.collectData(result);
envClusterInfo.forEach(function (node) {
var env = node.env; var env = node.env;
node.clusters.forEach(function (cluster) { node.clusters.forEach(function (cluster) {
cluster.env = env; cluster.env = env;
......
...@@ -16,7 +16,8 @@ application_module.controller("ConfigBaseInfoController", ...@@ -16,7 +16,8 @@ application_module.controller("ConfigBaseInfoController",
AppService.load_nav_tree($rootScope.pageContext.appId).then(function (result) { AppService.load_nav_tree($rootScope.pageContext.appId).then(function (result) {
var navTree = []; var navTree = [];
var nodes = result.nodes; var nodes = AppUtil.collectData(result);
nodes.forEach(function (item) { nodes.forEach(function (item) {
var node = {}; var node = {};
//first nav //first nav
...@@ -41,6 +42,7 @@ application_module.controller("ConfigBaseInfoController", ...@@ -41,6 +42,7 @@ application_module.controller("ConfigBaseInfoController",
node.nodes = clusterNodes; node.nodes = clusterNodes;
navTree.push(node); navTree.push(node);
}); });
$('#treeview').treeview({ $('#treeview').treeview({
color: "#797979", color: "#797979",
showBorder: true, showBorder: true,
...@@ -57,6 +59,7 @@ application_module.controller("ConfigBaseInfoController", ...@@ -57,6 +59,7 @@ application_module.controller("ConfigBaseInfoController",
$rootScope.refreshNamespaces(); $rootScope.refreshNamespaces();
} }
}); });
}, function (result) { }, function (result) {
toastr.error(AppUtil.errorMsg(result), "加载导航出错"); toastr.error(AppUtil.errorMsg(result), "加载导航出错");
}); });
...@@ -64,15 +67,20 @@ application_module.controller("ConfigBaseInfoController", ...@@ -64,15 +67,20 @@ application_module.controller("ConfigBaseInfoController",
////// app info ////// ////// app info //////
AppService.load($rootScope.pageContext.appId).then(function (result) { AppService.load($rootScope.pageContext.appId).then(function (result) {
$scope.appBaseInfo = result.app; $scope.appBaseInfo = result;
$scope.missEnvs = result.missEnvs;
$scope.selectedEnvs = angular.copy($scope.missEnvs);
},function (result) { },function (result) {
toastr.error(AppUtil.errorMsg(result), "加载App信息出错"); toastr.error(AppUtil.errorMsg(result), "加载App信息出错");
}); });
////// 补缺失的环境 ////// ////// 补缺失的环境 //////
$scope.missEnvs = [];
AppService.find_miss_envs($rootScope.pageContext.appId).then(function (result) {
$scope.missEnvs = AppUtil.collectData(result);
},function (result) {
console.log(AppUtil.errorMsg(result));
});
$scope.toggleSelection = function toggleSelection(env) { $scope.toggleSelection = function toggleSelection(env) {
var idx = $scope.selectedEnvs.indexOf(env); var idx = $scope.selectedEnvs.indexOf(env);
......
...@@ -13,7 +13,8 @@ sync_item_module.controller("SyncItemController", ...@@ -13,7 +13,8 @@ sync_item_module.controller("SyncItemController",
////// load env ////// ////// load env //////
AppService.load_nav_tree($scope.pageContext.appId).then(function (result) { AppService.load_nav_tree($scope.pageContext.appId).then(function (result) {
$scope.namespaceIdentifers = []; $scope.namespaceIdentifers = [];
result.nodes.forEach(function (node) { var envClusterInfo = AppUtil.collectData(result);
envClusterInfo.forEach(function (node) {
var env = node.env; var env = node.env;
node.clusters.forEach(function (cluster) { node.clusters.forEach(function (cluster) {
cluster.env = env; cluster.env = env;
......
...@@ -17,6 +17,10 @@ appService.service('AppService', ['$resource', '$q', function ($resource, $q) { ...@@ -17,6 +17,10 @@ appService.service('AppService', ['$resource', '$q', function ($resource, $q) {
create_app: { create_app: {
method: 'POST', method: 'POST',
url: '/apps/envs/:env' url: '/apps/envs/:env'
},
find_miss_envs: {
method: 'GET',
url: '/apps/:appId/miss_envs'
} }
}); });
return { return {
...@@ -61,6 +65,17 @@ appService.service('AppService', ['$resource', '$q', function ($resource, $q) { ...@@ -61,6 +65,17 @@ appService.service('AppService', ['$resource', '$q', function ($resource, $q) {
d.reject(result); d.reject(result);
}); });
return d.promise; return d.promise;
},
find_miss_envs: function (appId) {
var d = $q.defer();
app_resource.find_miss_envs({
appId: appId
}, function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
});
return d.promise;
} }
} }
}]); }]);
...@@ -8,8 +8,8 @@ import org.junit.runners.Suite.SuiteClasses; ...@@ -8,8 +8,8 @@ import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class) @RunWith(Suite.class)
@SuiteClasses({ @SuiteClasses({
ConfigServiceTest.class, PropertyResolverTest.class, ConfigServiceTest.class, PropertyResolverTest.class,
AppServiceTest.class, NamespaceServiceTest.class NamespaceServiceTest.class
}) })
public class AllTests { public class AllTests {
} }
package com.ctrip.apollo.portal;
import com.ctrip.apollo.core.dto.ClusterDTO;
import com.ctrip.apollo.core.enums.Env;
import com.ctrip.apollo.portal.api.AdminServiceAPI;
import com.ctrip.apollo.portal.entity.ClusterNavTree;
import com.ctrip.apollo.portal.service.AppService;
import com.ctrip.apollo.portal.service.ClusterService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class AppServiceTest {
@Mock
private PortalSettings settings;
@Mock
private ClusterService clusterService;
@Mock
private AdminServiceAPI.AppAPI appAPI;
@InjectMocks
private AppService appService;
@Test
public void testBuildNavTree(){
String appId = "6666";
ClusterDTO c1 = new ClusterDTO();
c1.setAppId(appId);
c1.setName("default");
c1.setId(1);
ClusterDTO c2 = new ClusterDTO();
c2.setAppId(appId);
c2.setName("oy");
c2.setId(2);
List<ClusterDTO> clusterDTOs = Arrays.asList(c1, c2);
when(settings.getEnvs()).thenReturn(Arrays.asList(Env.DEV, Env.FAT));
when(clusterService.findClusters(Env.DEV, appId)).thenReturn(clusterDTOs);
when(clusterService.findClusters(Env.FAT, appId)).thenReturn(Arrays.asList(c1));
ClusterNavTree tree = appService.buildClusterNavTree(appId);
assertEquals(2, tree.getNodes().size());
ClusterNavTree.Node node1 = tree.getNodes().get(0);
assertEquals(Env.DEV, node1.getEnv());
assertEquals(2, node1.getClusters().size());
assertEquals("default", node1.getClusters().get(0).getName());
}
// @Test
// public void testSaveApp(){
// String appId = "6666";
// String appName = "hermas";
// AppDTO appDTO = new AppDTO();
// appDTO.setAppId(appId);
// appDTO.setName(appName);
// appDTO.setDataChangeLastModifiedBy("ll");
// appDTO.setDataChangeCreatedTime(new Date());
// appDTO.setOwnerEmail("qq@qq.com");
// appDTO.setOwnerName("zz");
//
// when(appService.createApp(appDTO)).thenReturn(appDTO);
//
// AppDTO createApp = appService.createApp(appDTO);
//
// assertEquals(appId, createApp.getAppId());
// assertEquals(appName, createApp.getName());
// }
}
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