diff --git a/apollo-adminservice/src/main/java/com/ctrip/apollo/adminservice/controller/AppNamespaceController.java b/apollo-adminservice/src/main/java/com/ctrip/apollo/adminservice/controller/AppNamespaceController.java index c0e63b83a2ab718923fc30ca6f22c66f93ced32f..fb9f8c076567ca067f22c2ab1d707fe0d66ac00d 100644 --- a/apollo-adminservice/src/main/java/com/ctrip/apollo/adminservice/controller/AppNamespaceController.java +++ b/apollo-adminservice/src/main/java/com/ctrip/apollo/adminservice/controller/AppNamespaceController.java @@ -1,11 +1,17 @@ package com.ctrip.apollo.adminservice.controller; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Pageable; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import com.ctrip.apollo.biz.entity.AppNamespace; import com.ctrip.apollo.biz.service.AppNamespaceService; +import com.ctrip.apollo.common.utils.BeanUtils; +import com.ctrip.apollo.core.dto.AppNamespaceDTO; + +import java.util.List; @RestController public class AppNamespaceController { @@ -18,4 +24,11 @@ public class AppNamespaceController { @PathVariable("appnamespace") String appnamespace) { return appNamespaceService.isAppNamespaceNameUnique(appId, appnamespace); } + + @RequestMapping("/appnamespaces/public") + public List findPublicAppNamespaces(){ + List appNamespaces = appNamespaceService.findPublicAppNamespaces(); + return BeanUtils.batchTransform(AppNamespaceDTO.class, appNamespaces); + } + } diff --git a/apollo-biz/src/main/java/com/ctrip/apollo/biz/repository/AppNamespaceRepository.java b/apollo-biz/src/main/java/com/ctrip/apollo/biz/repository/AppNamespaceRepository.java index 35d1612c4c26fd4f6a7d6e524044b083840e7b33..a15cc35b2c8617c829d8518fead40c72ceffd63e 100644 --- a/apollo-biz/src/main/java/com/ctrip/apollo/biz/repository/AppNamespaceRepository.java +++ b/apollo-biz/src/main/java/com/ctrip/apollo/biz/repository/AppNamespaceRepository.java @@ -4,10 +4,14 @@ import org.springframework.data.repository.PagingAndSortingRepository; import com.ctrip.apollo.biz.entity.AppNamespace; +import java.util.List; + public interface AppNamespaceRepository extends PagingAndSortingRepository{ AppNamespace findByAppIdAndName(String appId, String namespaceName); AppNamespace findByName(String namespaceName); + List findByNameNot(String namespaceName); + } diff --git a/apollo-biz/src/main/java/com/ctrip/apollo/biz/service/AppNamespaceService.java b/apollo-biz/src/main/java/com/ctrip/apollo/biz/service/AppNamespaceService.java index 0d087c315a8f700ac54dda577bfd0269f4f1b82a..8e075ead858cb3bd987132384ba2c290bcba41a3 100644 --- a/apollo-biz/src/main/java/com/ctrip/apollo/biz/service/AppNamespaceService.java +++ b/apollo-biz/src/main/java/com/ctrip/apollo/biz/service/AppNamespaceService.java @@ -2,9 +2,12 @@ package com.ctrip.apollo.biz.service; import com.google.common.base.Preconditions; +import java.util.List; import java.util.Objects; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -50,4 +53,8 @@ public class AppNamespaceService { auditService.audit(AppNamespace.class.getSimpleName(), appNs.getId(), Audit.OP.INSERT, createBy); } + + public List findPublicAppNamespaces(){ + return appNamespaceRepository.findByNameNot(ConfigConsts.NAMESPACE_DEFAULT); + } } diff --git a/apollo-biz/src/test/java/com/ctrip/apollo/biz/AllTests.java b/apollo-biz/src/test/java/com/ctrip/apollo/biz/AllTests.java index ec86b5f38ade60d641710e983f7299ae3967b7ab..79a35d36b199665259473a71fc9ec706f83aad92 100644 --- a/apollo-biz/src/test/java/com/ctrip/apollo/biz/AllTests.java +++ b/apollo-biz/src/test/java/com/ctrip/apollo/biz/AllTests.java @@ -1,6 +1,7 @@ package com.ctrip.apollo.biz; import com.ctrip.apollo.biz.message.DatabaseMessageSenderTest; +import com.ctrip.apollo.biz.repository.AppNamespaceRepositoryTest; import com.ctrip.apollo.biz.repository.AppRepositoryTest; import com.ctrip.apollo.biz.service.AdminServiceTest; import com.ctrip.apollo.biz.service.AdminServiceTransactionTest; @@ -14,6 +15,7 @@ import org.junit.runners.Suite.SuiteClasses; @RunWith(Suite.class) @SuiteClasses({ AppRepositoryTest.class, + AppNamespaceRepositoryTest.class, AdminServiceTest.class, ConfigServiceTest.class, PrivilegeServiceTest.class, diff --git a/apollo-biz/src/test/java/com/ctrip/apollo/biz/repository/AppNamespaceRepositoryTest.java b/apollo-biz/src/test/java/com/ctrip/apollo/biz/repository/AppNamespaceRepositoryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..80682a38bbf355ed6b1ec179db91b4d6ec94c254 --- /dev/null +++ b/apollo-biz/src/test/java/com/ctrip/apollo/biz/repository/AppNamespaceRepositoryTest.java @@ -0,0 +1,33 @@ +package com.ctrip.apollo.biz.repository; + +import com.ctrip.apollo.biz.BizTestConfiguration; +import com.ctrip.apollo.biz.entity.AppNamespace; +import com.ctrip.apollo.core.ConfigConsts; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.test.annotation.Rollback; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = BizTestConfiguration.class) +@Transactional +@Rollback +public class AppNamespaceRepositoryTest { + + @Autowired + private AppNamespaceRepository repository; + + @Test + public void testFindAllPublicAppNamespaces(){ + List appNamespaceList = repository.findByNameNot(ConfigConsts.NAMESPACE_DEFAULT); + Assert.assertEquals(4, appNamespaceList.size()); + } + +} diff --git a/apollo-biz/src/test/java/com/ctrip/apollo/biz/service/AdminServiceTransactionTest.java b/apollo-biz/src/test/java/com/ctrip/apollo/biz/service/AdminServiceTransactionTest.java index 34c74e2011498d08fa572047cc91e698f10e6ce3..3f944c7ef2f6bdadec024f547bd72d57dd6a5281 100644 --- a/apollo-biz/src/test/java/com/ctrip/apollo/biz/service/AdminServiceTransactionTest.java +++ b/apollo-biz/src/test/java/com/ctrip/apollo/biz/service/AdminServiceTransactionTest.java @@ -50,7 +50,7 @@ public class AdminServiceTransactionTest { System.out.println(app.getAppId()); } Assert.assertEquals(0, appRepository.count()); - Assert.assertEquals(0, appNamespaceRepository.count()); + Assert.assertEquals(7, appNamespaceRepository.count()); Assert.assertEquals(0, namespaceRepository.count()); Assert.assertEquals(0, clusterRepository.count()); } @@ -58,7 +58,7 @@ public class AdminServiceTransactionTest { @Before public void setUpTestDataWithinTransaction() { Assert.assertEquals(0, appRepository.count()); - Assert.assertEquals(0, appNamespaceRepository.count()); + Assert.assertEquals(7, appNamespaceRepository.count()); Assert.assertEquals(0, namespaceRepository.count()); Assert.assertEquals(0, clusterRepository.count()); } @@ -82,7 +82,7 @@ public class AdminServiceTransactionTest { @After public void tearDownWithinTransaction() { Assert.assertEquals(1, appRepository.count()); - Assert.assertEquals(1, appNamespaceRepository.count()); + Assert.assertEquals(8, appNamespaceRepository.count()); Assert.assertEquals(1, namespaceRepository.count()); Assert.assertEquals(1, clusterRepository.count()); } @@ -90,7 +90,7 @@ public class AdminServiceTransactionTest { @AfterTransaction public void verifyFinalDatabaseState() { Assert.assertEquals(0, appRepository.count()); - Assert.assertEquals(0, appNamespaceRepository.count()); + Assert.assertEquals(7, appNamespaceRepository.count()); Assert.assertEquals(0, namespaceRepository.count()); Assert.assertEquals(0, clusterRepository.count()); } diff --git a/apollo-biz/src/test/resources/data.sql b/apollo-biz/src/test/resources/data.sql new file mode 100644 index 0000000000000000000000000000000000000000..15d328f43141020db1b7d7548b419accb93231a9 --- /dev/null +++ b/apollo-biz/src/test/resources/data.sql @@ -0,0 +1,8 @@ + +INSERT INTO AppNamespace (AppId, Name) VALUES ('100003171', 'application'); +INSERT INTO AppNamespace (AppId, Name) VALUES ('100003171', 'fx.apollo.config'); +INSERT INTO AppNamespace (AppId, Name) VALUES ('100003172', 'application'); +INSERT INTO AppNamespace (AppId, Name) VALUES ('100003172', 'fx.apollo.admin'); +INSERT INTO AppNamespace (AppId, Name) VALUES ('100003173', 'application'); +INSERT INTO AppNamespace (AppId, Name) VALUES ('100003173', 'fx.apollo.portal'); +INSERT INTO AppNamespace (AppID, Name) VALUES ('fxhermesproducer', 'fx.hermes.producer'); diff --git a/apollo-core/src/main/java/com/ctrip/apollo/core/dto/AppNamespaceDTO.java b/apollo-core/src/main/java/com/ctrip/apollo/core/dto/AppNamespaceDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..0776202da7d86b14a16f5947bb1d938e80460673 --- /dev/null +++ b/apollo-core/src/main/java/com/ctrip/apollo/core/dto/AppNamespaceDTO.java @@ -0,0 +1,34 @@ +package com.ctrip.apollo.core.dto; + +public class AppNamespaceDTO { + + private String name; + + private String appId; + + private String comment; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } +} diff --git a/apollo-portal/src/main/java/com/ctrip/apollo/portal/PortalSettings.java b/apollo-portal/src/main/java/com/ctrip/apollo/portal/PortalSettings.java index 598e9168e63921c8fde5e2886a6927a0ea9b4376..4bbcc87b92715761b28f4305a5e5e2fd742b7660 100644 --- a/apollo-portal/src/main/java/com/ctrip/apollo/portal/PortalSettings.java +++ b/apollo-portal/src/main/java/com/ctrip/apollo/portal/PortalSettings.java @@ -28,4 +28,8 @@ public class PortalSettings { public List getEnvs() { return envs; } + + public Env getFirstEnv(){ + return envs.get(0); + } } diff --git a/apollo-portal/src/main/java/com/ctrip/apollo/portal/api/AdminServiceAPI.java b/apollo-portal/src/main/java/com/ctrip/apollo/portal/api/AdminServiceAPI.java index 4709cfe656f881b279a133d96f48207cde5dc57c..1b700105901d4d99260bd2ddf6a16df7a53f56a1 100644 --- a/apollo-portal/src/main/java/com/ctrip/apollo/portal/api/AdminServiceAPI.java +++ b/apollo-portal/src/main/java/com/ctrip/apollo/portal/api/AdminServiceAPI.java @@ -1,6 +1,7 @@ package com.ctrip.apollo.portal.api; +import com.ctrip.apollo.core.dto.AppNamespaceDTO; import com.ctrip.apollo.core.enums.Env; import com.ctrip.apollo.core.dto.AppDTO; import com.ctrip.apollo.core.dto.ClusterDTO; @@ -61,6 +62,21 @@ public class AdminServiceAPI { + String.format("apps/%s/clusters/%s/namespaces/%s", appId, clusterName, namespaceName), NamespaceDTO.class); } + + public List findPublicAppNamespaces(Env env){ + AppNamespaceDTO[] appNamespaceDTOs = restTemplate.getForObject( + getAdminServiceHost(env)+ "appnamespaces/public", + AppNamespaceDTO[].class); + return Arrays.asList(appNamespaceDTOs); + } + + public NamespaceDTO save(Env env, NamespaceDTO namespace) { + return restTemplate.postForEntity(getAdminServiceHost(env) + + String.format("/apps/%s/clusters/%s/namespaces", namespace.getAppId(), + namespace.getClusterName()), namespace, NamespaceDTO.class) + .getBody(); + } + } @Service diff --git a/apollo-portal/src/main/java/com/ctrip/apollo/portal/controller/ConfigController.java b/apollo-portal/src/main/java/com/ctrip/apollo/portal/controller/ConfigController.java index 467d1d4ed894374293ef73aca1c617bbeffc6bfc..937071e2333a3ddbf2faeb68d04ea5dd7bcfd045 100644 --- a/apollo-portal/src/main/java/com/ctrip/apollo/portal/controller/ConfigController.java +++ b/apollo-portal/src/main/java/com/ctrip/apollo/portal/controller/ConfigController.java @@ -31,16 +31,6 @@ public class ConfigController { @Autowired private ConfigService configService; - @RequestMapping("/apps/{appId}/env/{env}/clusters/{clusterName}/namespaces") - public List findNamespaces(@PathVariable String appId, @PathVariable String env, - @PathVariable String clusterName) { - if (StringUtils.isContainEmpty(appId, env, clusterName)) { - throw new BadRequestException("app id and cluster name can not be empty"); - } - - return configService.findNampspaces(appId, Env.valueOf(env), clusterName); - } - @RequestMapping(value = "/apps/{appId}/env/{env}/clusters/{clusterName}/namespaces/{namespaceName}/items", method = RequestMethod.PUT, consumes = { "application/json"}) public void modifyItems(@PathVariable String appId, @PathVariable String env, diff --git a/apollo-portal/src/main/java/com/ctrip/apollo/portal/controller/NamespaceController.java b/apollo-portal/src/main/java/com/ctrip/apollo/portal/controller/NamespaceController.java new file mode 100644 index 0000000000000000000000000000000000000000..b746d1e6f4591f69a63547a8c4afdd0876bea308 --- /dev/null +++ b/apollo-portal/src/main/java/com/ctrip/apollo/portal/controller/NamespaceController.java @@ -0,0 +1,50 @@ +package com.ctrip.apollo.portal.controller; + +import com.ctrip.apollo.core.dto.AppNamespaceDTO; +import com.ctrip.apollo.core.dto.NamespaceDTO; +import com.ctrip.apollo.core.enums.Env; +import com.ctrip.apollo.core.exception.BadRequestException; +import com.ctrip.apollo.core.utils.StringUtils; +import com.ctrip.apollo.portal.entity.NamespaceVO; +import com.ctrip.apollo.portal.service.NamespaceService; + +import org.springframework.beans.factory.annotation.Autowired; +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; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +public class NamespaceController { + + @Autowired + private NamespaceService namespaceService; + + @RequestMapping("/appnamespaces/public") + public List findPublicAppNamespaces(){ + return namespaceService.findPublicAppNamespaces(); + } + + @RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces", method = RequestMethod.POST) + public NamespaceDTO save(@PathVariable String env, @RequestBody NamespaceDTO namespace){ + if (StringUtils.isContainEmpty(env, namespace.getAppId(), namespace.getClusterName(), namespace.getNamespaceName())){ + throw new BadRequestException("request payload contains empty"); + } + return namespaceService.save(Env.valueOf(env), namespace); + + } + + @RequestMapping("/apps/{appId}/env/{env}/clusters/{clusterName}/namespaces") + public List findNamespaces(@PathVariable String appId, @PathVariable String env, + @PathVariable String clusterName) { + if (StringUtils.isContainEmpty(appId, env, clusterName)) { + throw new BadRequestException("app id and cluster name can not be empty"); + } + + return namespaceService.findNampspaces(appId, Env.valueOf(env), clusterName); + } + +} diff --git a/apollo-portal/src/main/java/com/ctrip/apollo/portal/service/ConfigService.java b/apollo-portal/src/main/java/com/ctrip/apollo/portal/service/ConfigService.java index 60bdaf02d37308c15b52f8a6abd8f1b14b78a8e4..f70bfcb7da723f44dd0e1730e9cc3eeade7652a1 100644 --- a/apollo-portal/src/main/java/com/ctrip/apollo/portal/service/ConfigService.java +++ b/apollo-portal/src/main/java/com/ctrip/apollo/portal/service/ConfigService.java @@ -50,90 +50,6 @@ public class ConfigService { @Autowired private ConfigTextResolver resolver; - private Gson gson = new Gson(); - - /** - * load cluster all namespace info with items - */ - public List findNampspaces(String appId, Env env, String clusterName) { - - List namespaces = namespaceAPI.findNamespaceByCluster(appId, env, clusterName); - if (namespaces == null || namespaces.size() == 0) { - return Collections.emptyList(); - } - - List namespaceVOs = new LinkedList<>(); - for (NamespaceDTO namespace : namespaces) { - - 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); - throw e; - } - } - - return namespaceVOs; - } - - @SuppressWarnings("unchecked") - private NamespaceVO parseNamespace(String appId, Env env, String clusterName, NamespaceDTO namespace) { - NamespaceVO namespaceVO = new NamespaceVO(); - namespaceVO.setNamespace(namespace); - - List itemVos = new LinkedList<>(); - namespaceVO.setItems(itemVos); - - String namespaceName = namespace.getNamespaceName(); - - //latest Release - ReleaseDTO release = null; - Map releaseItems = new HashMap<>(); - try { - release = releaseAPI.loadLatestRelease(appId, env, clusterName, namespaceName); - releaseItems = gson.fromJson(release.getConfigurations(), Map.class); - } catch (HttpClientErrorException e) { - if (e.getStatusCode() == HttpStatus.NOT_FOUND) { - logger.warn(ExceptionUtils.toString(e)); - } else { - throw e; - } - } - - //not Release config items - List items = itemAPI.findItems(appId, env, clusterName, namespaceName); - int modifiedItemCnt = 0; - for (ItemDTO itemDTO : items) { - - NamespaceVO.ItemVO itemVO = parseItemVO(itemDTO, releaseItems); - - if (itemVO.isModified()) { - modifiedItemCnt++; - } - - itemVos.add(itemVO); - } - namespaceVO.setItemModifiedCnt(modifiedItemCnt); - - return namespaceVO; - } - - private NamespaceVO.ItemVO parseItemVO(ItemDTO itemDTO, Map releaseItems) { - String key = itemDTO.getKey(); - NamespaceVO.ItemVO itemVO = new NamespaceVO.ItemVO(); - itemVO.setItem(itemDTO); - String newValue = itemDTO.getValue(); - String oldValue = releaseItems.get(key); - if (!StringUtils.isEmpty(key) && (oldValue == null || !newValue.equals(oldValue))) { - itemVO.setModified(true); - itemVO.setOldValue(oldValue == null ? "" : oldValue); - itemVO.setNewValue(newValue); - } - return itemVO; - } /** * parse config text and update config items @@ -160,10 +76,8 @@ public class ConfigService { namespaceName); throw new ServiceException(e.getMessage()); } - } - /** * createRelease config items */ @@ -190,7 +104,6 @@ public class ConfigService { throw new ServiceException(String.format("sync item error. env:%s, clusterName:%s", namespaceIdentifer.getEnv(), namespaceIdentifer.getClusterName()), e); } - } } diff --git a/apollo-portal/src/main/java/com/ctrip/apollo/portal/service/NamespaceService.java b/apollo-portal/src/main/java/com/ctrip/apollo/portal/service/NamespaceService.java new file mode 100644 index 0000000000000000000000000000000000000000..fb0ae0e259b4bde45959225249d765161d33bbd6 --- /dev/null +++ b/apollo-portal/src/main/java/com/ctrip/apollo/portal/service/NamespaceService.java @@ -0,0 +1,140 @@ +package com.ctrip.apollo.portal.service; + +import com.google.gson.Gson; + +import com.ctrip.apollo.common.utils.ExceptionUtils; +import com.ctrip.apollo.core.dto.AppNamespaceDTO; +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.enums.Env; +import com.ctrip.apollo.core.utils.StringUtils; +import com.ctrip.apollo.portal.PortalSettings; +import com.ctrip.apollo.portal.api.AdminServiceAPI; +import com.ctrip.apollo.portal.entity.NamespaceVO; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.springframework.web.client.HttpClientErrorException; + +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +@Service +public class NamespaceService { + + private Logger logger = LoggerFactory.getLogger(NamespaceService.class); + + @Autowired + private AdminServiceAPI.ItemAPI itemAPI; + + @Autowired + private AdminServiceAPI.ReleaseAPI releaseAPI; + + @Autowired + private AdminServiceAPI.NamespaceAPI namespaceAPI; + + @Autowired + private PortalSettings portalSettings; + + private Gson gson = new Gson(); + + + public List findPublicAppNamespaces(){ + return namespaceAPI.findPublicAppNamespaces(portalSettings.getFirstEnv()); + } + + public NamespaceDTO save(Env env, NamespaceDTO namespace){ + return namespaceAPI.save(env, namespace); + } + + /** + * load cluster all namespace info with items + */ + public List findNampspaces(String appId, Env env, String clusterName) { + + List namespaces = namespaceAPI.findNamespaceByCluster(appId, env, clusterName); + if (namespaces == null || namespaces.size() == 0) { + return Collections.emptyList(); + } + + List namespaceVOs = new LinkedList<>(); + for (NamespaceDTO namespace : namespaces) { + + 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); + throw e; + } + } + + return namespaceVOs; + } + + @SuppressWarnings("unchecked") + private NamespaceVO parseNamespace(String appId, Env env, String clusterName, NamespaceDTO namespace) { + NamespaceVO namespaceVO = new NamespaceVO(); + namespaceVO.setNamespace(namespace); + + List itemVos = new LinkedList<>(); + namespaceVO.setItems(itemVos); + + String namespaceName = namespace.getNamespaceName(); + + //latest Release + ReleaseDTO release = null; + Map releaseItems = new HashMap<>(); + try { + release = releaseAPI.loadLatestRelease(appId, env, clusterName, namespaceName); + releaseItems = gson.fromJson(release.getConfigurations(), Map.class); + } catch (HttpClientErrorException e) { + if (e.getStatusCode() == HttpStatus.NOT_FOUND) { + logger.warn(ExceptionUtils.toString(e)); + } else { + throw e; + } + } + + //not Release config items + List items = itemAPI.findItems(appId, env, clusterName, namespaceName); + int modifiedItemCnt = 0; + for (ItemDTO itemDTO : items) { + + NamespaceVO.ItemVO itemVO = parseItemVO(itemDTO, releaseItems); + + if (itemVO.isModified()) { + modifiedItemCnt++; + } + + itemVos.add(itemVO); + } + namespaceVO.setItemModifiedCnt(modifiedItemCnt); + + return namespaceVO; + } + + private NamespaceVO.ItemVO parseItemVO(ItemDTO itemDTO, Map releaseItems) { + String key = itemDTO.getKey(); + NamespaceVO.ItemVO itemVO = new NamespaceVO.ItemVO(); + itemVO.setItem(itemDTO); + String newValue = itemDTO.getValue(); + String oldValue = releaseItems.get(key); + if (!StringUtils.isEmpty(key) && (oldValue == null || !newValue.equals(oldValue))) { + itemVO.setModified(true); + itemVO.setOldValue(oldValue == null ? "" : oldValue); + itemVO.setNewValue(newValue); + } + return itemVO; + } + +} diff --git a/apollo-portal/src/main/resources/static/views/create-app.html b/apollo-portal/src/main/resources/static/app.html similarity index 68% rename from apollo-portal/src/main/resources/static/views/create-app.html rename to apollo-portal/src/main/resources/static/app.html index 79376323e90d3fd2b54ff1c97b37abdd9dc65599..7d1c74043cfe9ba1d1ce0eeb4c81e655e3a895d2 100644 --- a/apollo-portal/src/main/resources/static/views/create-app.html +++ b/apollo-portal/src/main/resources/static/app.html @@ -3,16 +3,16 @@ - - - - + + + + 新建项目 -
+
@@ -64,25 +64,25 @@
-
+
- - - - - + + + + + - + - + - - - + + + - + diff --git a/apollo-portal/src/main/resources/static/views/app.html b/apollo-portal/src/main/resources/static/config.html similarity index 87% rename from apollo-portal/src/main/resources/static/views/app.html rename to apollo-portal/src/main/resources/static/config.html index 1b7d8b0746cbf9921a7a585b92c755f026fe0a50..dfbf4a9390ed59679d075e55a00ca44a82bff814 100644 --- a/apollo-portal/src/main/resources/static/views/app.html +++ b/apollo-portal/src/main/resources/static/config.html @@ -4,15 +4,15 @@ apollo - - - - + + + + -
+
@@ -28,7 +28,7 @@
- 应用信息 + 应用信息 @@ -68,14 +68,13 @@
-
+
-
- + @@ -84,15 +83,14 @@ - - - - - - - - - + +
+
+ +
+
@@ -128,7 +126,7 @@ 同步
@@ -236,7 +234,7 @@
- +
2016-02-23 @@ -254,7 +252,7 @@
- +
2016-02-23 @@ -316,7 +314,7 @@
-
+
- + - + - + - - - - - + + + + + - - + + - + - - - + + + - + - + diff --git a/apollo-portal/src/main/resources/static/views/sync.html b/apollo-portal/src/main/resources/static/config/sync.html similarity index 98% rename from apollo-portal/src/main/resources/static/views/sync.html rename to apollo-portal/src/main/resources/static/config/sync.html index 24e3407ac1bab7a42c774b0aa167528989ea32e8..7a0a94f042cb8b51cbc070c7d0a79b92918c073f 100644 --- a/apollo-portal/src/main/resources/static/views/sync.html +++ b/apollo-portal/src/main/resources/static/config/sync.html @@ -12,7 +12,7 @@ -
+
@@ -175,7 +175,7 @@
-
+
diff --git a/apollo-portal/src/main/resources/static/index.html b/apollo-portal/src/main/resources/static/index.html index e579eac463548b5ee9c77331e822e4064e1093b7..7ba2b42fcf4ce8b89e7b89872704bfc47a785b18 100644 --- a/apollo-portal/src/main/resources/static/index.html +++ b/apollo-portal/src/main/resources/static/index.html @@ -25,7 +25,7 @@

Apollo

携程统一配置中心
共收录了 {{appsCount}} 个项目 - 创建项目 + 创建项目

@@ -42,7 +42,7 @@
-

{{app.appId}}

diff --git a/apollo-portal/src/main/resources/static/namespace.html b/apollo-portal/src/main/resources/static/namespace.html new file mode 100644 index 0000000000000000000000000000000000000000..cf540ef720e085feaae3763f90ec01458bf0dd5e --- /dev/null +++ b/apollo-portal/src/main/resources/static/namespace.html @@ -0,0 +1,124 @@ + + + + + + + + + + + 新建Namespace + + + + +
+ +
+ +
+
+
+
+ 新建Namespace +
+ +
+ +
+ +
+ {{appId}} +
+
+
+
+ +
+ + + + + + + + + + + + + + + + +
环境集群
{{namespaceIdentifer.env}}{{namespaceIdentifer.name}}
+
+
+
+
+ +
+ + +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apollo-portal/src/main/resources/static/scripts/controller/CreateAppController.js b/apollo-portal/src/main/resources/static/scripts/controller/CreateAppController.js index bb92c4c53d173724c110d8098e80d357332a3745..3fb55b18cb0d1180798f2d647f62f0705739ded0 100644 --- a/apollo-portal/src/main/resources/static/scripts/controller/CreateAppController.js +++ b/apollo-portal/src/main/resources/static/scripts/controller/CreateAppController.js @@ -5,7 +5,7 @@ create_app_module.controller('CreateAppController', ['$scope', '$window', 'toast AppService.create('ALL', $scope.app).then(function (result) { toastr.success('添加成功!'); setInterval(function () { - $window.location.href = '/views/app.html?#appid=' + result.appId; + $window.location.href = '/views/config.html?#appid=' + result.appId; }, 1000); }, function (result) { toastr.error(AppUtil.errorMsg(result), '添加失败!'); diff --git a/apollo-portal/src/main/resources/static/scripts/controller/LinkNamespaceController.js b/apollo-portal/src/main/resources/static/scripts/controller/LinkNamespaceController.js new file mode 100644 index 0000000000000000000000000000000000000000..f06713dc5106201b37618f5832f76e0328211e95 --- /dev/null +++ b/apollo-portal/src/main/resources/static/scripts/controller/LinkNamespaceController.js @@ -0,0 +1,90 @@ +application_module.controller("LinkNamespaceController", + ['$scope', '$location', '$window', 'toastr', 'AppService', 'AppUtil', 'NamespaceService', + function ($scope, $location, $window, toastr, AppService, AppUtil, NamespaceService) { + + var params = AppUtil.parseParams($location.$$url); + $scope.appId = params.appid; + $scope.isRootUser = params.root ? true : false; + + ////// load env ////// + AppService.load_nav_tree($scope.appId).then(function (result) { + $scope.namespaceIdentifers = []; + result.nodes.forEach(function (node) { + var env = node.env; + node.clusters.forEach(function (cluster) { + cluster.env = env; + cluster.checked = false; + $scope.namespaceIdentifers.push(cluster); + }) + }); + }, function (result) { + toastr.error(AppUtil.errorMsg(result), "加载环境出错"); + }); + + NamespaceService.find_public_namespaces().then(function (result) { + var publicNamespaces = []; + result.forEach(function (item) { + var namespace = {}; + namespace.id = item.name; + namespace.text = item.name; + publicNamespaces.push(namespace); + }); + $('#namespaces').select2({ + width: '250px', + data: publicNamespaces + }); + }, function (result) { + toastr.error(AppUtil.errorMsg(result), "load public namespace error"); + }); + + $scope.saveNamespace = function () { + var selectedClusters = collectSelectedClusters(); + if (selectedClusters.length == 0){ + toastr.warning("请选择集群"); + return; + } + var namespaceName = $('#namespaces').select2('data')[0].id; + selectedClusters.forEach(function (cluster) { + NamespaceService.save($scope.appId, cluster.env, cluster.clusterName, + namespaceName).then(function (result) { + toastr.success( + cluster.env + "_" + result.clusterName + "_" + result.namespaceName + + "创建成功"); + }, function (result) { + toastr.error(AppUtil.errorMsg(result), + cluster.env + "_" + cluster.clusterName + "_" + + namespaceName + "创建失败"); + }); + }) + }; + + var envAllSelected = false; + $scope.toggleEnvsCheckedStatus = function () { + envAllSelected = !envAllSelected; + $scope.namespaceIdentifers.forEach(function (namespaceIdentifer) { + namespaceIdentifer.checked = envAllSelected; + }) + }; + + function collectSelectedClusters() { + var selectedClusters = []; + $scope.namespaceIdentifers.forEach(function (namespaceIdentifer) { + if (namespaceIdentifer.checked){ + namespaceIdentifer.clusterName = namespaceIdentifer.name; + selectedClusters.push(namespaceIdentifer); + } + }); + return selectedClusters; + } + + + $scope.namespaceType = 1; + $scope.selectNamespaceType = function (type) { + $scope.namespaceType = type; + }; + + $scope.switchSelect = function (o) { + o.checked = !o.checked; + } + }]); + diff --git a/apollo-portal/src/main/resources/static/scripts/controller/app/SyncConfigController.js b/apollo-portal/src/main/resources/static/scripts/controller/app/SyncConfigController.js index d517812b8eaa5d174fc7e5a96158bd0f5c2b3877..190a947a652f588d283fb4edc8ca59192a7fb2ac 100644 --- a/apollo-portal/src/main/resources/static/scripts/controller/app/SyncConfigController.js +++ b/apollo-portal/src/main/resources/static/scripts/controller/app/SyncConfigController.js @@ -3,7 +3,6 @@ sync_item_module.controller("SyncItemController", function ($scope, $location, $window, toastr, AppService, AppUtil, ConfigService) { var params = AppUtil.parseParams($location.$$url); - var currentUser = 'test_user'; $scope.pageContext = { appId: params.appid, env: params.env, @@ -115,7 +114,7 @@ sync_item_module.controller("SyncItemController", }; $scope.backToAppHomePage = function () { - $window.location.href = '/views/app.html?#appid=' + $scope.pageContext.appId; + $window.location.href = '/views/config.html?#appid=' + $scope.pageContext.appId; }; $scope.switchSelect = function (o) { diff --git a/apollo-portal/src/main/resources/static/scripts/services/NamespaceService.js b/apollo-portal/src/main/resources/static/scripts/services/NamespaceService.js new file mode 100644 index 0000000000000000000000000000000000000000..b987940bb3550e78b76fe483dd7b23dff59220cf --- /dev/null +++ b/apollo-portal/src/main/resources/static/scripts/services/NamespaceService.js @@ -0,0 +1,44 @@ +appService.service("NamespaceService", ['$resource', '$q', function ($resource, $q) { + var namespace_source = $resource("", {}, { + find_public_namespaces: { + method: 'GET', + isArray: true, + url: '/appnamespaces/public' + }, + save: { + method: 'POST', + url: '/apps/:appId/envs/:env/clusters/:clusterName/namespaces', + isArray: false + } + }); + + return { + find_public_namespaces: function () { + var d = $q.defer(); + namespace_source.find_public_namespaces({}, function (result) { + d.resolve(result); + }, function (result) { + d.reject(result); + }); + return d.promise; + }, + save: function (appId, env, clusterName, namespaceName) { + var d = $q.defer(); + namespace_source.save({ + appId: appId, + env: env, + clusterName: clusterName + }, { + appId: appId, + clusterName: clusterName, + namespaceName: namespaceName + }, function (result) { + d.resolve(result); + }, function (result) { + d.reject(result); + }); + return d.promise; + } + } + +}]); diff --git a/apollo-portal/src/main/resources/static/styles/common-style.css b/apollo-portal/src/main/resources/static/styles/common-style.css index 69d59b71464e5b0b926af6ae937b90bd633850ce..0339bc3e66a1ca57bb40f96f00da4d7718545955 100644 --- a/apollo-portal/src/main/resources/static/styles/common-style.css +++ b/apollo-portal/src/main/resources/static/styles/common-style.css @@ -259,3 +259,9 @@ table th { padding-bottom: 4px; } +.list-group-item .btn-title{ + color: gray; + font-family: "Apple Color Emoji"; + font-size: 16px; +} + diff --git a/apollo-portal/src/test/java/com/ctrip/apollo/portal/AllTests.java b/apollo-portal/src/test/java/com/ctrip/apollo/portal/AllTests.java index 7e3210ad2b4f2d7039d41b0440bae916b03982c3..31713fbf954d9406f180864094d9681407b4d58f 100644 --- a/apollo-portal/src/test/java/com/ctrip/apollo/portal/AllTests.java +++ b/apollo-portal/src/test/java/com/ctrip/apollo/portal/AllTests.java @@ -8,7 +8,7 @@ import org.junit.runners.Suite.SuiteClasses; @RunWith(Suite.class) @SuiteClasses({ ConfigServiceTest.class, PropertyResolverTest.class, - AppServiceTest.class + AppServiceTest.class, NamespaceServiceTest.class }) public class AllTests { diff --git a/apollo-portal/src/test/java/com/ctrip/apollo/portal/ConfigServiceTest.java b/apollo-portal/src/test/java/com/ctrip/apollo/portal/ConfigServiceTest.java index a4018a9180c7985819d06921e9392bf5d0268879..56c435157625cc37c002a41dbcc7f90004853eaa 100644 --- a/apollo-portal/src/test/java/com/ctrip/apollo/portal/ConfigServiceTest.java +++ b/apollo-portal/src/test/java/com/ctrip/apollo/portal/ConfigServiceTest.java @@ -4,12 +4,10 @@ import com.ctrip.apollo.core.ConfigConsts; 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.enums.Env; import com.ctrip.apollo.portal.api.AdminServiceAPI; import com.ctrip.apollo.portal.entity.ItemDiffs; import com.ctrip.apollo.portal.entity.NamespaceIdentifer; -import com.ctrip.apollo.portal.entity.NamespaceVO; import com.ctrip.apollo.portal.entity.form.NamespaceTextModel; import com.ctrip.apollo.portal.service.ConfigService; import com.ctrip.apollo.portal.service.txtresolver.PropertyResolver; @@ -47,51 +45,6 @@ public class ConfigServiceTest { public void setup() { } - @Test - public void testFindNamespace() { - String appId = "6666"; - String clusterName = "default"; - String namespaceName = "application"; - - NamespaceDTO application = new NamespaceDTO(); - application.setId(1); - application.setClusterName(clusterName); - application.setAppId(appId); - application.setNamespaceName(namespaceName); - - NamespaceDTO hermas = new NamespaceDTO(); - hermas.setId(2); - hermas.setClusterName("default"); - hermas.setAppId(appId); - hermas.setNamespaceName("hermas"); - List namespaces = Arrays.asList(application, hermas); - - ReleaseDTO someRelease = new ReleaseDTO(); - someRelease.setConfigurations("{\"a\":\"123\",\"b\":\"123\"}"); - - ItemDTO i1 = new ItemDTO("a", "123", "", 1); - ItemDTO i2 = new ItemDTO("b", "1", "", 2); - ItemDTO i3 = new ItemDTO("", "", "#dddd", 3); - ItemDTO i4 = new ItemDTO("c", "1", "", 4); - List someItems = Arrays.asList(i1, i2, i3, i4); - - when(namespaceAPI.findNamespaceByCluster(appId, Env.DEV, clusterName)).thenReturn(namespaces); - when(releaseAPI.loadLatestRelease(appId, Env.DEV, clusterName, namespaceName)).thenReturn(someRelease); - when(releaseAPI.loadLatestRelease(appId, Env.DEV, clusterName, "hermas")).thenReturn(someRelease); - when(itemAPI.findItems(appId, Env.DEV, clusterName, namespaceName)).thenReturn(someItems); - - List namespaceVOs = configService.findNampspaces(appId, Env.DEV, clusterName); - assertEquals(2, namespaceVOs.size()); - NamespaceVO namespaceVO = namespaceVOs.get(0); - assertEquals(4, namespaceVO.getItems().size()); - assertEquals("a", namespaceVO.getItems().get(0).getItem().getKey()); - assertEquals(2, namespaceVO.getItemModifiedCnt()); - assertEquals(appId, namespaceVO.getNamespace().getAppId()); - assertEquals(clusterName, namespaceVO.getNamespace().getClusterName()); - assertEquals(namespaceName, namespaceVO.getNamespace().getNamespaceName()); - - } - @Test public void testUpdateConfigByText() { String appId = "6666"; diff --git a/apollo-portal/src/test/java/com/ctrip/apollo/portal/NamespaceServiceTest.java b/apollo-portal/src/test/java/com/ctrip/apollo/portal/NamespaceServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ce3d2639a7a6d0a9b13032989cfda24d569bcece --- /dev/null +++ b/apollo-portal/src/test/java/com/ctrip/apollo/portal/NamespaceServiceTest.java @@ -0,0 +1,90 @@ +package com.ctrip.apollo.portal; + +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.enums.Env; +import com.ctrip.apollo.portal.api.AdminServiceAPI; +import com.ctrip.apollo.portal.entity.NamespaceVO; +import com.ctrip.apollo.portal.service.NamespaceService; +import com.ctrip.apollo.portal.service.txtresolver.PropertyResolver; + +import org.junit.Before; +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 NamespaceServiceTest { + + @Mock + private AdminServiceAPI.NamespaceAPI namespaceAPI; + @Mock + private AdminServiceAPI.ReleaseAPI releaseAPI; + @Mock + private AdminServiceAPI.ItemAPI itemAPI; + @Mock + private PropertyResolver resolver; + + @InjectMocks + private NamespaceService namespaceService; + + @Before + public void setup() { + } + + @Test + public void testFindNamespace() { + String appId = "6666"; + String clusterName = "default"; + String namespaceName = "application"; + + NamespaceDTO application = new NamespaceDTO(); + application.setId(1); + application.setClusterName(clusterName); + application.setAppId(appId); + application.setNamespaceName(namespaceName); + + NamespaceDTO hermas = new NamespaceDTO(); + hermas.setId(2); + hermas.setClusterName("default"); + hermas.setAppId(appId); + hermas.setNamespaceName("hermas"); + List namespaces = Arrays.asList(application, hermas); + + ReleaseDTO someRelease = new ReleaseDTO(); + someRelease.setConfigurations("{\"a\":\"123\",\"b\":\"123\"}"); + + ItemDTO i1 = new ItemDTO("a", "123", "", 1); + ItemDTO i2 = new ItemDTO("b", "1", "", 2); + ItemDTO i3 = new ItemDTO("", "", "#dddd", 3); + ItemDTO i4 = new ItemDTO("c", "1", "", 4); + List someItems = Arrays.asList(i1, i2, i3, i4); + + when(namespaceAPI.findNamespaceByCluster(appId, Env.DEV, clusterName)).thenReturn(namespaces); + when(releaseAPI.loadLatestRelease(appId, Env.DEV, clusterName, namespaceName)).thenReturn(someRelease); + when(releaseAPI.loadLatestRelease(appId, Env.DEV, clusterName, "hermas")).thenReturn(someRelease); + when(itemAPI.findItems(appId, Env.DEV, clusterName, namespaceName)).thenReturn(someItems); + + List namespaceVOs = namespaceService.findNampspaces(appId, Env.DEV, clusterName); + assertEquals(2, namespaceVOs.size()); + NamespaceVO namespaceVO = namespaceVOs.get(0); + assertEquals(4, namespaceVO.getItems().size()); + assertEquals("a", namespaceVO.getItems().get(0).getItem().getKey()); + assertEquals(2, namespaceVO.getItemModifiedCnt()); + assertEquals(appId, namespaceVO.getNamespace().getAppId()); + assertEquals(clusterName, namespaceVO.getNamespace().getClusterName()); + assertEquals(namespaceName, namespaceVO.getNamespace().getNamespaceName()); + + } + + +}