Commit e56f0a7a authored by lepdou's avatar lepdou

rollback

parent 5acb8c76
......@@ -6,7 +6,6 @@ import com.ctrip.framework.apollo.biz.entity.Namespace;
import com.ctrip.framework.apollo.biz.entity.Release;
import com.ctrip.framework.apollo.biz.message.MessageSender;
import com.ctrip.framework.apollo.biz.message.Topics;
import com.ctrip.framework.apollo.biz.service.ConfigService;
import com.ctrip.framework.apollo.biz.service.NamespaceService;
import com.ctrip.framework.apollo.biz.service.ReleaseService;
import com.ctrip.framework.apollo.common.utils.BeanUtils;
......@@ -30,9 +29,6 @@ public class ReleaseController {
@Autowired
private ReleaseService releaseService;
@Autowired
private ConfigService configService;
@Autowired
private NamespaceService namespaceService;
......@@ -41,7 +37,7 @@ public class ReleaseController {
private static final Joiner STRING_JOINER = Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR);
@RequestMapping("/release/{releaseId}")
@RequestMapping("/releases/{releaseId}")
public ReleaseDTO get(@PathVariable("releaseId") long releaseId) {
Release release = releaseService.findOne(releaseId);
if (release == null) {
......@@ -50,12 +46,31 @@ public class ReleaseController {
return BeanUtils.transfrom(ReleaseDTO.class, release);
}
@RequestMapping("/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases/all")
public List<ReleaseDTO> findAllReleases(@PathVariable("appId") String appId,
@PathVariable("clusterName") String clusterName,
@PathVariable("namespaceName") String namespaceName,
Pageable page) {
List<Release> releases = releaseService.findAllReleases(appId, clusterName, namespaceName, page);
return BeanUtils.batchTransform(ReleaseDTO.class, releases);
}
// TODO: 16/7/25 兼容老接口,下版本删除
@RequestMapping("/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases")
public List<ReleaseDTO> find(@PathVariable("appId") String appId,
@PathVariable("clusterName") String clusterName,
@PathVariable("namespaceName") String namespaceName,
Pageable page) {
List<Release> releases = releaseService.findReleases(appId, clusterName, namespaceName, page);
public List<ReleaseDTO> findReleases(@PathVariable("appId") String appId,
@PathVariable("clusterName") String clusterName,
@PathVariable("namespaceName") String namespaceName,
Pageable page) {
List<Release> releases = releaseService.findAllReleases(appId, clusterName, namespaceName, page);
return BeanUtils.batchTransform(ReleaseDTO.class, releases);
}
@RequestMapping("/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases/active")
public List<ReleaseDTO> findActiveReleases(@PathVariable("appId") String appId,
@PathVariable("clusterName") String clusterName,
@PathVariable("namespaceName") String namespaceName,
Pageable page) {
List<Release> releases = releaseService.findActiveReleases(appId, clusterName, namespaceName, page);
return BeanUtils.batchTransform(ReleaseDTO.class, releases);
}
......@@ -63,14 +78,8 @@ public class ReleaseController {
public ReleaseDTO getLatest(@PathVariable("appId") String appId,
@PathVariable("clusterName") String clusterName,
@PathVariable("namespaceName") String namespaceName) {
Release release = configService.findRelease(appId, clusterName, namespaceName);
//// TODO: 16/7/22 返回null
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);
}
Release release = releaseService.findLatestActiveRelease(appId, clusterName, namespaceName);
return BeanUtils.transfrom(ReleaseDTO.class, release);
}
@RequestMapping(path = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases", method = RequestMethod.POST)
......@@ -83,14 +92,28 @@ public class ReleaseController {
Namespace namespace = namespaceService.findOne(appId, clusterName, namespaceName);
if (namespace == null) {
throw new NotFoundException(String.format("Could not find namespace for %s %s %s", appId,
clusterName, namespaceName));
clusterName, namespaceName));
}
Release release = releaseService.buildRelease(name, comment, namespace, operator);
messageSender.sendMessage(assembleKey(appId, clusterName, namespaceName),
Topics.APOLLO_RELEASE_TOPIC);
Topics.APOLLO_RELEASE_TOPIC);
return BeanUtils.transfrom(ReleaseDTO.class, release);
}
@RequestMapping(path = "/releases/{releaseId}/rollback", method = RequestMethod.PUT)
public void rollback(@PathVariable("releaseId") long releaseId,
@RequestParam("operator") String operator) {
Release release = releaseService.rollback(releaseId, operator);
String appId = release.getAppId();
String clusterName = release.getClusterName();
String namespaceName = release.getNamespaceName();
//send release message
messageSender.sendMessage(assembleKey(appId, clusterName, namespaceName),
Topics.APOLLO_RELEASE_TOPIC);
}
private String assembleKey(String appId, String cluster, String namespace) {
return STRING_JOINER.join(appId, cluster, namespace);
}
......
......@@ -40,6 +40,9 @@ public class Release extends BaseEntity {
@Column(name = "Comment", nullable = false)
private String comment;
@Column(name = "IsAbandoned", columnDefinition = "Bit default '0'")
private boolean isAbandoned;
public String getReleaseKey() {
return releaseKey;
}
......@@ -96,9 +99,17 @@ public class Release extends BaseEntity {
this.name = name;
}
public boolean isAbandoned() {
return isAbandoned;
}
public void setAbandoned(boolean abandoned) {
isAbandoned = abandoned;
}
public String toString() {
return toStringHelper().add("name", name).add("appId", appId).add("clusterName", clusterName)
.add("namespaceName", namespaceName).add("configurations", configurations)
.add("comment", comment).toString();
.add("comment", comment).add("isAbandoned", isAbandoned).toString();
}
}
......@@ -13,8 +13,10 @@ import com.ctrip.framework.apollo.biz.entity.Release;
*/
public interface ReleaseRepository extends PagingAndSortingRepository<Release, Long> {
Release findFirstByAppIdAndClusterNameAndNamespaceNameOrderByIdDesc(@Param("appId") String appId, @Param("clusterName") String clusterName,
@Param("namespaceName") String namespaceName);
Release findFirstByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc(@Param("appId") String appId, @Param("clusterName") String clusterName,
@Param("namespaceName") String namespaceName);
List<Release> findByAppIdAndClusterNameAndNamespaceNameOrderByIdDesc(String appId, String clusterName, String namespaceName, Pageable page);
List<Release> findByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc(String appId, String clusterName, String namespaceName, Pageable page);
}
package com.ctrip.framework.apollo.biz.service;
import com.ctrip.framework.apollo.biz.entity.Release;
import com.ctrip.framework.apollo.biz.repository.ReleaseRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* Config Service
*
* @author Jason Song(song_s@ctrip.com)
*/
@Service
public class ConfigService {
@Autowired
private ReleaseRepository releaseRepository;
public Release findRelease(String appId, String clusterName, String namespaceName) {
Release release = releaseRepository.findFirstByAppIdAndClusterNameAndNamespaceNameOrderByIdDesc(
appId, clusterName, namespaceName);
return release;
}
}
......@@ -9,9 +9,12 @@ import com.ctrip.framework.apollo.biz.entity.Release;
import com.ctrip.framework.apollo.biz.repository.ItemRepository;
import com.ctrip.framework.apollo.biz.repository.ReleaseRepository;
import com.ctrip.framework.apollo.biz.utils.ReleaseKeyGenerator;
import com.ctrip.framework.apollo.core.exception.BadRequestException;
import com.ctrip.framework.apollo.core.exception.NotFoundException;
import com.ctrip.framework.apollo.core.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
......@@ -27,19 +30,19 @@ import java.util.Map;
*/
@Service
public class ReleaseService {
private Gson gson = new Gson();
@Autowired
private ReleaseRepository releaseRepository;
@Autowired
private ItemRepository itemRepository;
@Autowired
private AuditService auditService;
@Autowired
private NamespaceLockService namespaceLockService;
@Autowired
private NamespaceService namespaceService;
public Release findOne(long releaseId) {
......@@ -47,9 +50,30 @@ public class ReleaseService {
return release;
}
public List<Release> findReleases(String appId, String clusterName, String namespaceName, Pageable page) {
public Release findLatestActiveRelease(String appId, String clusterName, String namespaceName) {
Release release = releaseRepository.findFirstByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc(
appId, clusterName, namespaceName);
return release;
}
public List<Release> findAllReleases(String appId, String clusterName, String namespaceName, Pageable page) {
List<Release> releases = releaseRepository.findByAppIdAndClusterNameAndNamespaceNameOrderByIdDesc(appId,
clusterName, namespaceName, page);
clusterName,
namespaceName,
page);
if (releases == null) {
return Collections.emptyList();
}
return releases;
}
public List<Release> findActiveReleases(String appId, String clusterName, String namespaceName, Pageable page) {
List<Release>
releases =
releaseRepository.findByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc(appId,
clusterName,
namespaceName,
page);
if (releases == null) {
return Collections.emptyList();
}
......@@ -57,7 +81,7 @@ public class ReleaseService {
}
@Transactional
public Release buildRelease(String name, String comment, Namespace namespace, String owner) {
public Release buildRelease(String name, String comment, Namespace namespace, String operator) {
List<Item> items = itemRepository.findByNamespaceIdOrderByLineNumAsc(namespace.getId());
Map<String, String> configurations = new HashMap<String, String>();
......@@ -71,8 +95,8 @@ public class ReleaseService {
Release release = new Release();
release.setReleaseKey(ReleaseKeyGenerator.generateReleaseKey(namespace));
release.setDataChangeCreatedTime(new Date());
release.setDataChangeCreatedBy(owner);
release.setDataChangeLastModifiedBy(owner);
release.setDataChangeCreatedBy(operator);
release.setDataChangeLastModifiedBy(operator);
release.setName(name);
release.setComment(comment);
release.setAppId(namespace.getAppId());
......@@ -83,9 +107,43 @@ public class ReleaseService {
namespaceLockService.unlock(namespace.getId());
auditService.audit(Release.class.getSimpleName(), release.getId(), Audit.OP.INSERT,
release.getDataChangeCreatedBy());
release.getDataChangeCreatedBy());
return release;
}
@Transactional
public Release rollback(long releaseId, String operator) {
Release release = findOne(releaseId);
if (release == null){
throw new NotFoundException("release not found");
}
if (release.isAbandoned()){
throw new BadRequestException("release is not active");
}
String appId = release.getAppId();
String clusterName = release.getClusterName();
String namespaceName = release.getNamespaceName();
Namespace namespace = namespaceService.findOne(appId, clusterName, namespaceName);
if (namespace == null) {
throw new BadRequestException(String.format("namespace not existed. (appId=%s, cluster=%s, namespace=%s)", appId,
clusterName, namespaceName));
}
PageRequest page = new PageRequest(0, 2);
List<Release> twoLatestActiveReleases = findActiveReleases(appId, clusterName, namespaceName, page);
if (twoLatestActiveReleases == null || twoLatestActiveReleases.size() < 2) {
throw new BadRequestException(String.format(
"Can't rollback namespace(appId=%s, clusterName=%s, namespaceName=%s) because only one active release", appId,
clusterName,
namespaceName));
}
release.setAbandoned(true);
release.setDataChangeLastModifiedBy(operator);
return releaseRepository.save(release);
}
}
......@@ -28,8 +28,10 @@ public class ConfigChangeContentBuilder {
}
public ConfigChangeContentBuilder updateItem(Item oldItem, Item newItem) {
ItemPair itemPair = new ItemPair(oldItem, newItem);
updateItems.add(itemPair);
if (!oldItem.getValue().equals(newItem.getValue())){
ItemPair itemPair = new ItemPair(oldItem, newItem);
updateItems.add(itemPair);
}
return this;
}
......
package com.ctrip.framework.apollo.biz;
import org.junit.runner.RunWith;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.WebIntegrationTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
@RunWith(SpringJUnit4ClassRunner.class)
@Rollback
@Transactional
@WebIntegrationTest(randomPort = true)
@SpringApplicationConfiguration(classes = BizTestConfiguration.class)
public class AbstractIntegrationTest {
}
package com.ctrip.framework.apollo.biz;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class AbstractUnitTest {
}
......@@ -8,7 +8,6 @@ import com.ctrip.framework.apollo.biz.repository.AppRepositoryTest;
import com.ctrip.framework.apollo.biz.service.AdminServiceTest;
import com.ctrip.framework.apollo.biz.service.AdminServiceTransactionTest;
import com.ctrip.framework.apollo.biz.service.ClusterServiceTest;
import com.ctrip.framework.apollo.biz.service.ConfigServiceTest;
import com.ctrip.framework.apollo.biz.service.PrivilegeServiceTest;
import com.ctrip.framework.apollo.biz.service.ServerConfigServiceTest;
import com.ctrip.framework.apollo.biz.utils.ReleaseKeyGeneratorTest;
......@@ -22,7 +21,6 @@ import org.junit.runners.Suite.SuiteClasses;
AppRepositoryTest.class,
AppNamespaceRepositoryTest.class,
AdminServiceTest.class,
ConfigServiceTest.class,
PrivilegeServiceTest.class,
AdminServiceTransactionTest.class,
DatabaseMessageSenderTest.class,
......
package com.ctrip.framework.apollo.biz.eureka;
import com.ctrip.framework.apollo.biz.AbstractUnitTest;
import com.ctrip.framework.apollo.biz.service.ServerConfigService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.core.env.Environment;
import org.springframework.test.util.ReflectionTestUtils;
......@@ -19,8 +18,7 @@ import static org.mockito.Mockito.when;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@RunWith(MockitoJUnitRunner.class)
public class ApolloEurekaClientConfigTest {
public class ApolloEurekaClientConfigTest extends AbstractUnitTest {
private ApolloEurekaClientConfig eurekaClientConfig;
@Mock
private ServerConfigService serverConfigService;
......
package com.ctrip.framework.apollo.biz.message;
import com.ctrip.framework.apollo.biz.AbstractUnitTest;
import com.ctrip.framework.apollo.biz.entity.ReleaseMessage;
import com.ctrip.framework.apollo.biz.repository.ReleaseMessageRepository;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.test.util.ReflectionTestUtils;
import static org.junit.Assert.assertEquals;
......@@ -20,8 +19,7 @@ import static org.mockito.Mockito.verify;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@RunWith(MockitoJUnitRunner.class)
public class DatabaseMessageSenderTest {
public class DatabaseMessageSenderTest extends AbstractUnitTest{
private DatabaseMessageSender messageSender;
@Mock
private ReleaseMessageRepository releaseMessageRepository;
......
......@@ -3,14 +3,13 @@ package com.ctrip.framework.apollo.biz.message;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.SettableFuture;
import com.ctrip.framework.apollo.biz.AbstractUnitTest;
import com.ctrip.framework.apollo.biz.entity.ReleaseMessage;
import com.ctrip.framework.apollo.biz.repository.ReleaseMessageRepository;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.core.env.Environment;
import org.springframework.test.util.ReflectionTestUtils;
......@@ -22,8 +21,7 @@ import static org.mockito.Mockito.when;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@RunWith(MockitoJUnitRunner.class)
public class ReleaseMessageScannerTest {
public class ReleaseMessageScannerTest extends AbstractUnitTest {
private ReleaseMessageScanner releaseMessageScanner;
@Mock
private ReleaseMessageRepository releaseMessageRepository;
......
package com.ctrip.framework.apollo.biz.repository;
import com.ctrip.framework.apollo.biz.BizTestConfiguration;
import com.ctrip.framework.apollo.biz.AbstractIntegrationTest;
import com.ctrip.framework.apollo.common.entity.AppNamespace;
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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = BizTestConfiguration.class)
@Transactional
@Rollback
public class AppNamespaceRepositoryTest {
public class AppNamespaceRepositoryTest extends AbstractIntegrationTest{
@Autowired
private AppNamespaceRepository repository;
......
......@@ -2,22 +2,12 @@ package com.ctrip.framework.apollo.biz.repository;
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 com.ctrip.framework.apollo.biz.BizTestConfiguration;
import com.ctrip.framework.apollo.biz.AbstractIntegrationTest;
import com.ctrip.framework.apollo.common.entity.App;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = BizTestConfiguration.class)
@Transactional
@Rollback
public class AppRepositoryTest {
public class AppRepositoryTest extends AbstractIntegrationTest{
@Autowired
private AppRepository appRepository;
......
......@@ -5,14 +5,9 @@ import java.util.List;
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 com.ctrip.framework.apollo.biz.BizTestConfiguration;
import com.ctrip.framework.apollo.biz.AbstractIntegrationTest;
import com.ctrip.framework.apollo.common.entity.App;
import com.ctrip.framework.apollo.biz.entity.Audit;
import com.ctrip.framework.apollo.biz.entity.Cluster;
......@@ -21,11 +16,7 @@ import com.ctrip.framework.apollo.biz.repository.AppRepository;
import com.ctrip.framework.apollo.core.ConfigConsts;
import com.ctrip.framework.apollo.core.exception.ServiceException;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = BizTestConfiguration.class)
@Transactional
@Rollback
public class AdminServiceTest {
public class AdminServiceTest extends AbstractIntegrationTest{
@Autowired
private AdminService adminService;
......
......@@ -6,28 +6,19 @@ import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
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.Commit;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.AfterTransaction;
import org.springframework.test.context.transaction.BeforeTransaction;
import org.springframework.transaction.annotation.Transactional;
import com.ctrip.framework.apollo.biz.BizTestConfiguration;
import com.ctrip.framework.apollo.biz.AbstractIntegrationTest;
import com.ctrip.framework.apollo.common.entity.App;
import com.ctrip.framework.apollo.biz.repository.AppNamespaceRepository;
import com.ctrip.framework.apollo.biz.repository.AppRepository;
import com.ctrip.framework.apollo.biz.repository.ClusterRepository;
import com.ctrip.framework.apollo.biz.repository.NamespaceRepository;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = BizTestConfiguration.class)
@Transactional
@Commit
public class AdminServiceTransactionTest {
public class AdminServiceTransactionTest extends AbstractIntegrationTest {
@Autowired
AdminService adminService;
......
......@@ -3,22 +3,13 @@ package com.ctrip.framework.apollo.biz.service;
import java.util.Date;
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 com.ctrip.framework.apollo.biz.BizTestConfiguration;
import com.ctrip.framework.apollo.biz.AbstractIntegrationTest;
import com.ctrip.framework.apollo.common.entity.App;
import com.ctrip.framework.apollo.core.exception.ServiceException;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = BizTestConfiguration.class)
@Transactional
@Rollback
public class ClusterServiceTest {
public class ClusterServiceTest extends AbstractIntegrationTest {
@Autowired
private AdminService adminService;
......
......@@ -5,24 +5,15 @@ import java.util.List;
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 com.ctrip.framework.apollo.biz.BizTestConfiguration;
import com.ctrip.framework.apollo.biz.AbstractIntegrationTest;
import com.ctrip.framework.apollo.common.entity.App;
import com.ctrip.framework.apollo.biz.entity.Cluster;
import com.ctrip.framework.apollo.biz.entity.Namespace;
import com.ctrip.framework.apollo.biz.entity.Privilege;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = BizTestConfiguration.class)
@Transactional
@Rollback
public class PrivilegeServiceTest {
public class PrivilegeServiceTest extends AbstractIntegrationTest {
@Autowired
private AdminService adminService;
......
package com.ctrip.framework.apollo.biz.service;
import com.ctrip.framework.apollo.biz.AbstractUnitTest;
import com.ctrip.framework.apollo.biz.entity.Namespace;
import com.ctrip.framework.apollo.biz.entity.Release;
import com.ctrip.framework.apollo.biz.repository.ReleaseRepository;
import com.ctrip.framework.apollo.core.exception.BadRequestException;
import org.junit.Assert;
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 org.springframework.test.util.ReflectionTestUtils;
import org.springframework.data.domain.PageRequest;
import java.util.Arrays;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
......@@ -16,22 +21,91 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@RunWith(MockitoJUnitRunner.class)
public class ConfigServiceTest {
public class ReleaseServiceTest extends AbstractUnitTest {
@Mock
private ReleaseRepository releaseRepository;
private ConfigService configService;
@Mock
private NamespaceService namespaceService;
@InjectMocks
private ReleaseService releaseService;
private String appId = "appId-test";
private String clusterName = "cluster-test";
private String namespaceName = "namespace-test";
private String user = "user-test";
private Namespace namespace;
private long releaseId = 1;
private Release firstRelease;
private Release secondRelease;
private PageRequest pageRequest;
@Before
public void setUp() throws Exception {
configService = new ConfigService();
ReflectionTestUtils.setField(configService, "releaseRepository", releaseRepository);
public void init() {
namespace = new Namespace();
namespace.setAppId(appId);
namespace.setClusterName(clusterName);
namespace.setNamespaceName(namespaceName);
firstRelease = new Release();
firstRelease.setId(releaseId);
firstRelease.setAppId(appId);
firstRelease.setClusterName(clusterName);
firstRelease.setNamespaceName(namespaceName);
firstRelease.setAbandoned(false);
secondRelease = new Release();
secondRelease.setAppId(appId);
secondRelease.setClusterName(clusterName);
secondRelease.setNamespaceName(namespaceName);
secondRelease.setAbandoned(false);
pageRequest = new PageRequest(0, 2);
}
@Test(expected = BadRequestException.class)
public void testNamespaceNotExist() {
when(releaseRepository.findOne(releaseId)).thenReturn(firstRelease);
when(namespaceService.findOne(appId, clusterName, namespaceName)).thenThrow(new BadRequestException("xx"));
releaseService.rollback(releaseId, user);
}
@Test(expected = BadRequestException.class)
public void testHasNoRelease() {
when(releaseRepository.findOne(releaseId)).thenReturn(firstRelease);
when(namespaceService.findOne(appId, clusterName, namespaceName)).thenReturn(namespace);
when(releaseRepository.findByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc(appId,
clusterName,
namespaceName,
pageRequest))
.thenReturn(null);
releaseService.rollback(releaseId, user);
}
@Test
public void testRollback() {
when(releaseRepository.findOne(releaseId)).thenReturn(firstRelease);
when(namespaceService.findOne(appId, clusterName, namespaceName)).thenReturn(namespace);
when(releaseRepository.findByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc(appId,
clusterName,
namespaceName,
pageRequest))
.thenReturn(
Arrays.asList(firstRelease, secondRelease));
releaseService.rollback(releaseId, user);
verify(releaseRepository).save(firstRelease);
Assert.assertEquals(true, firstRelease.isAbandoned());
Assert.assertEquals(user, firstRelease.getDataChangeLastModifiedBy());
}
@Test
public void testFindRelease() throws Exception {
String someAppId = "1";
......@@ -44,17 +118,19 @@ public class ConfigServiceTest {
Release
someRelease =
assembleRelease(someReleaseId, someReleaseKey, someAppId, someClusterName,
someNamespaceName,
someValidConfiguration);
someNamespaceName,
someValidConfiguration);
when(releaseRepository.findFirstByAppIdAndClusterNameAndNamespaceNameOrderByIdDesc(someAppId,
someClusterName, someNamespaceName)).thenReturn(someRelease);
when(releaseRepository.findFirstByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc(someAppId,
someClusterName,
someNamespaceName))
.thenReturn(someRelease);
Release result = configService.findRelease(someAppId, someClusterName, someNamespaceName);
Release result = releaseService.findLatestActiveRelease(someAppId, someClusterName, someNamespaceName);
verify(releaseRepository, times(1))
.findFirstByAppIdAndClusterNameAndNamespaceNameOrderByIdDesc(someAppId, someClusterName,
someNamespaceName);
.findFirstByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc(someAppId, someClusterName,
someNamespaceName);
assertEquals(someAppId, result.getAppId());
assertEquals(someClusterName, result.getClusterName());
assertEquals(someReleaseId, result.getId());
......@@ -68,13 +144,15 @@ public class ConfigServiceTest {
String someClusterName = "someClusterName";
String someNamespaceName = "someNamespaceName";
when(releaseRepository.findFirstByAppIdAndClusterNameAndNamespaceNameOrderByIdDesc(someAppId,
someClusterName, someNamespaceName)).thenReturn(null);
when(releaseRepository.findFirstByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc(someAppId,
someClusterName,
someNamespaceName))
.thenReturn(null);
Release result = configService.findRelease(someAppId, someClusterName, someNamespaceName);
Release result = releaseService.findLatestActiveRelease(someAppId, someClusterName, someNamespaceName);
assertNull(result);
verify(releaseRepository, times(1)).findFirstByAppIdAndClusterNameAndNamespaceNameOrderByIdDesc(
verify(releaseRepository, times(1)).findFirstByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc(
someAppId, someClusterName, someNamespaceName);
}
......@@ -91,4 +169,5 @@ public class ConfigServiceTest {
return release;
}
}
package com.ctrip.framework.apollo.biz.service;
import com.ctrip.framework.apollo.biz.AbstractUnitTest;
import com.ctrip.framework.apollo.biz.entity.ServerConfig;
import com.ctrip.framework.apollo.biz.repository.ServerConfigRepository;
import com.ctrip.framework.apollo.core.ConfigConsts;
......@@ -7,9 +8,7 @@ import com.ctrip.framework.apollo.core.ConfigConsts;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.test.util.ReflectionTestUtils;
import static org.junit.Assert.*;
......@@ -19,8 +18,7 @@ import static org.mockito.Mockito.when;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@RunWith(MockitoJUnitRunner.class)
public class ServerConfigServiceTest {
public class ServerConfigServiceTest extends AbstractUnitTest{
private ServerConfigService serverConfigService;
@Mock
private ServerConfigRepository serverConfigRepository;
......
......@@ -8,10 +8,10 @@ import com.google.common.collect.Maps;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.ctrip.framework.apollo.biz.service.ReleaseService;
import com.ctrip.framework.apollo.common.entity.AppNamespace;
import com.ctrip.framework.apollo.biz.entity.Release;
import com.ctrip.framework.apollo.biz.service.AppNamespaceService;
import com.ctrip.framework.apollo.biz.service.ConfigService;
import com.ctrip.framework.apollo.configservice.util.NamespaceUtil;
import com.ctrip.framework.apollo.core.ConfigConsts;
import com.ctrip.framework.apollo.core.dto.ApolloConfig;
......@@ -39,7 +39,7 @@ import javax.servlet.http.HttpServletResponse;
@RequestMapping("/configs")
public class ConfigController {
@Autowired
private ConfigService configService;
private ReleaseService releaseService;
@Autowired
private AppNamespaceService appNamespaceService;
@Autowired
......@@ -143,7 +143,7 @@ public class ConfigController {
//load from specified cluster fist
if (!Objects.equals(ConfigConsts.CLUSTER_NAME_DEFAULT, clusterName)) {
Release clusterRelease =
configService.findRelease(appId, clusterName, namespace);
releaseService.findLatestActiveRelease(appId, clusterName, namespace);
if (!Objects.isNull(clusterRelease)) {
return clusterRelease;
......@@ -153,15 +153,15 @@ public class ConfigController {
//try to load via data center
if (!Strings.isNullOrEmpty(dataCenter) && !Objects.equals(dataCenter, clusterName)) {
Release dataCenterRelease =
configService.findRelease(appId, dataCenter, namespace);
releaseService.findLatestActiveRelease(appId, dataCenter, namespace);
if (!Objects.isNull(dataCenterRelease)) {
return dataCenterRelease;
}
}
//fallback to default release
return configService
.findRelease(appId, ConfigConsts.CLUSTER_NAME_DEFAULT, namespace);
return releaseService
.findLatestActiveRelease(appId, ConfigConsts.CLUSTER_NAME_DEFAULT, namespace);
}
/**
......
......@@ -6,10 +6,10 @@ import com.google.common.collect.Lists;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.ctrip.framework.apollo.biz.service.ReleaseService;
import com.ctrip.framework.apollo.common.entity.AppNamespace;
import com.ctrip.framework.apollo.biz.entity.Release;
import com.ctrip.framework.apollo.biz.service.AppNamespaceService;
import com.ctrip.framework.apollo.biz.service.ConfigService;
import com.ctrip.framework.apollo.configservice.util.NamespaceUtil;
import com.ctrip.framework.apollo.core.ConfigConsts;
import com.ctrip.framework.apollo.core.dto.ApolloConfig;
......@@ -41,7 +41,7 @@ import static org.mockito.Mockito.when;
public class ConfigControllerTest {
private ConfigController configController;
@Mock
private ConfigService configService;
private ReleaseService releaseService;
@Mock
private AppNamespaceService appNamespaceService;
private String someAppId;
......@@ -61,7 +61,7 @@ public class ConfigControllerTest {
@Before
public void setUp() throws Exception {
configController = new ConfigController();
ReflectionTestUtils.setField(configController, "configService", configService);
ReflectionTestUtils.setField(configController, "releaseService", releaseService);
ReflectionTestUtils.setField(configController, "appNamespaceService", appNamespaceService);
ReflectionTestUtils.setField(configController, "namespaceUtil", namespaceUtil);
......@@ -89,7 +89,7 @@ public class ConfigControllerTest {
String someServerSideNewReleaseKey = "2";
HttpServletResponse someResponse = mock(HttpServletResponse.class);
when(configService.findRelease(someAppId, someClusterName, defaultNamespaceName))
when(releaseService.findLatestActiveRelease(someAppId, someClusterName, defaultNamespaceName))
.thenReturn(someRelease);
when(someRelease.getReleaseKey()).thenReturn(someServerSideNewReleaseKey);
......@@ -97,7 +97,7 @@ public class ConfigControllerTest {
defaultNamespaceName, someDataCenter, someClientSideReleaseKey,
someClientIp, someResponse);
verify(configService, times(1)).findRelease(someAppId, someClusterName, defaultNamespaceName);
verify(releaseService, times(1)).findLatestActiveRelease(someAppId, someClusterName, defaultNamespaceName);
assertEquals(someAppId, result.getAppId());
assertEquals(someClusterName, result.getCluster());
assertEquals(defaultNamespaceName, result.getNamespaceName());
......@@ -111,7 +111,7 @@ public class ConfigControllerTest {
HttpServletResponse someResponse = mock(HttpServletResponse.class);
String someNamespaceName = String.format("%s.%s", defaultClusterName, "properties");
when(configService.findRelease(someAppId, someClusterName, defaultNamespaceName))
when(releaseService.findLatestActiveRelease(someAppId, someClusterName, defaultNamespaceName))
.thenReturn(someRelease);
when(someRelease.getReleaseKey()).thenReturn(someServerSideNewReleaseKey);
when(namespaceUtil.filterNamespaceName(someNamespaceName)).thenReturn(defaultNamespaceName);
......@@ -120,7 +120,7 @@ public class ConfigControllerTest {
someNamespaceName, someDataCenter, someClientSideReleaseKey,
someClientIp, someResponse);
verify(configService, times(1)).findRelease(someAppId, someClusterName, defaultNamespaceName);
verify(releaseService, times(1)).findLatestActiveRelease(someAppId, someClusterName, defaultNamespaceName);
assertEquals(someAppId, result.getAppId());
assertEquals(someClusterName, result.getCluster());
assertEquals(someNamespaceName, result.getNamespaceName());
......@@ -136,7 +136,7 @@ public class ConfigControllerTest {
String somePrivateNamespaceName = String.format("%s.%s", somePrivateNamespace, "xml");
AppNamespace appNamespace = mock(AppNamespace.class);
when(configService.findRelease(someAppId, someClusterName, somePrivateNamespace))
when(releaseService.findLatestActiveRelease(someAppId, someClusterName, somePrivateNamespace))
.thenReturn(someRelease);
when(someRelease.getReleaseKey()).thenReturn(someServerSideNewReleaseKey);
when(namespaceUtil.filterNamespaceName(somePrivateNamespaceName)).thenReturn(somePrivateNamespace);
......@@ -158,7 +158,7 @@ public class ConfigControllerTest {
String someClientSideReleaseKey = "1";
HttpServletResponse someResponse = mock(HttpServletResponse.class);
when(configService.findRelease(someAppId, someClusterName, defaultNamespaceName))
when(releaseService.findLatestActiveRelease(someAppId, someClusterName, defaultNamespaceName))
.thenReturn(null);
ApolloConfig result = configController.queryConfig(someAppId, someClusterName,
......@@ -175,7 +175,7 @@ public class ConfigControllerTest {
String someServerSideReleaseKey = someClientSideReleaseKey;
HttpServletResponse someResponse = mock(HttpServletResponse.class);
when(configService.findRelease(someAppId, someClusterName, defaultNamespaceName))
when(releaseService.findLatestActiveRelease(someAppId, someClusterName, defaultNamespaceName))
.thenReturn(someRelease);
when(someRelease.getReleaseKey()).thenReturn(someServerSideReleaseKey);
......@@ -194,7 +194,7 @@ public class ConfigControllerTest {
String someServerSideNewReleaseKey = "2";
HttpServletResponse someResponse = mock(HttpServletResponse.class);
when(configService.findRelease(someAppId, someDataCenter, defaultNamespaceName))
when(releaseService.findLatestActiveRelease(someAppId, someDataCenter, defaultNamespaceName))
.thenReturn(someRelease);
when(someRelease.getReleaseKey()).thenReturn(someServerSideNewReleaseKey);
when(someRelease.getClusterName()).thenReturn(someDataCenter);
......@@ -203,7 +203,7 @@ public class ConfigControllerTest {
defaultNamespaceName, someDataCenter, someClientSideReleaseKey,
someClientIp, someResponse);
verify(configService, times(1)).findRelease(someAppId, someDataCenter, defaultNamespaceName);
verify(releaseService, times(1)).findLatestActiveRelease(someAppId, someDataCenter, defaultNamespaceName);
assertEquals(someAppId, result.getAppId());
assertEquals(someDataCenter, result.getCluster());
assertEquals(defaultNamespaceName, result.getNamespaceName());
......@@ -216,9 +216,9 @@ public class ConfigControllerTest {
String someServerSideNewReleaseKey = "2";
HttpServletResponse someResponse = mock(HttpServletResponse.class);
when(configService.findRelease(someAppId, someDataCenter, defaultNamespaceName))
when(releaseService.findLatestActiveRelease(someAppId, someDataCenter, defaultNamespaceName))
.thenReturn(null);
when(configService.findRelease(someAppId, defaultClusterName, defaultNamespaceName))
when(releaseService.findLatestActiveRelease(someAppId, defaultClusterName, defaultNamespaceName))
.thenReturn(someRelease);
when(someRelease.getReleaseKey()).thenReturn(someServerSideNewReleaseKey);
when(someRelease.getClusterName()).thenReturn(defaultClusterName);
......@@ -227,9 +227,9 @@ public class ConfigControllerTest {
defaultNamespaceName, someDataCenter, someClientSideReleaseKey,
someClientIp, someResponse);
verify(configService, times(1)).findRelease(someAppId, someDataCenter, defaultNamespaceName);
verify(configService, times(1))
.findRelease(someAppId, defaultClusterName, defaultNamespaceName);
verify(releaseService, times(1)).findLatestActiveRelease(someAppId, someDataCenter, defaultNamespaceName);
verify(releaseService, times(1))
.findLatestActiveRelease(someAppId, defaultClusterName, defaultNamespaceName);
assertEquals(someAppId, result.getAppId());
assertEquals(defaultClusterName, result.getCluster());
assertEquals(defaultNamespaceName, result.getNamespaceName());
......@@ -245,7 +245,7 @@ public class ConfigControllerTest {
AppNamespace someAppOwnNamespace =
assemblePublicAppNamespace(someAppId, someAppOwnNamespaceName);
when(configService.findRelease(someAppId, someClusterName, someAppOwnNamespaceName))
when(releaseService.findLatestActiveRelease(someAppId, someClusterName, someAppOwnNamespaceName))
.thenReturn(someRelease);
when(appNamespaceService.findPublicNamespaceByName(someAppOwnNamespaceName))
.thenReturn(someAppOwnNamespace);
......@@ -274,11 +274,11 @@ public class ConfigControllerTest {
AppNamespace somePublicAppNamespace =
assemblePublicAppNamespace(somePublicAppId, somePublicNamespaceName);
when(configService.findRelease(someAppId, someClusterName, somePublicNamespaceName))
when(releaseService.findLatestActiveRelease(someAppId, someClusterName, somePublicNamespaceName))
.thenReturn(null);
when(appNamespaceService.findPublicNamespaceByName(somePublicNamespaceName))
.thenReturn(somePublicAppNamespace);
when(configService.findRelease(somePublicAppId, someDataCenter, somePublicNamespaceName))
when(releaseService.findLatestActiveRelease(somePublicAppId, someDataCenter, somePublicNamespaceName))
.thenReturn(somePublicRelease);
when(somePublicRelease.getReleaseKey()).thenReturn(someServerSideReleaseKey);
......@@ -303,11 +303,11 @@ public class ConfigControllerTest {
AppNamespace somePublicAppNamespace =
assemblePublicAppNamespace(somePublicAppId, somePublicNamespaceName);
when(configService.findRelease(someAppId, someClusterName, somePublicNamespaceName))
when(releaseService.findLatestActiveRelease(someAppId, someClusterName, somePublicNamespaceName))
.thenReturn(null);
when(appNamespaceService.findPublicNamespaceByName(somePublicNamespaceName))
.thenReturn(somePublicAppNamespace);
when(configService.findRelease(somePublicAppId, someDataCenter, somePublicNamespaceName))
when(releaseService.findLatestActiveRelease(somePublicAppId, someDataCenter, somePublicNamespaceName))
.thenReturn(somePublicRelease);
when(somePublicRelease.getReleaseKey()).thenReturn(someServerSideReleaseKey);
when(namespaceUtil.filterNamespaceName(someNamespace)).thenReturn(somePublicNamespaceName);
......@@ -333,14 +333,14 @@ public class ConfigControllerTest {
AppNamespace somePublicAppNamespace =
assemblePublicAppNamespace(somePublicAppId, somePublicNamespaceName);
when(configService.findRelease(someAppId, someClusterName, somePublicNamespaceName))
when(releaseService.findLatestActiveRelease(someAppId, someClusterName, somePublicNamespaceName))
.thenReturn(null);
when(appNamespaceService.findPublicNamespaceByName(somePublicNamespaceName))
.thenReturn(somePublicAppNamespace);
when(configService.findRelease(somePublicAppId, someDataCenter, somePublicNamespaceName))
when(releaseService.findLatestActiveRelease(somePublicAppId, someDataCenter, somePublicNamespaceName))
.thenReturn(null);
when(configService
.findRelease(somePublicAppId, ConfigConsts.CLUSTER_NAME_DEFAULT, somePublicNamespaceName))
when(releaseService
.findLatestActiveRelease(somePublicAppId, ConfigConsts.CLUSTER_NAME_DEFAULT, somePublicNamespaceName))
.thenReturn(somePublicRelease);
when(somePublicRelease.getReleaseKey()).thenReturn(someServerSideReleaseKey);
......@@ -370,12 +370,12 @@ public class ConfigControllerTest {
when(somePublicRelease.getConfigurations())
.thenReturn("{\"apollo.public.foo\": \"foo\", \"apollo.public.bar\": \"bar\"}");
when(configService.findRelease(someAppId, someClusterName, somePublicNamespaceName))
when(releaseService.findLatestActiveRelease(someAppId, someClusterName, somePublicNamespaceName))
.thenReturn(someRelease);
when(someRelease.getReleaseKey()).thenReturn(someAppSideReleaseKey);
when(appNamespaceService.findPublicNamespaceByName(somePublicNamespaceName))
.thenReturn(somePublicAppNamespace);
when(configService.findRelease(somePublicAppId, someDataCenter, somePublicNamespaceName))
when(releaseService.findLatestActiveRelease(somePublicAppId, someDataCenter, somePublicNamespaceName))
.thenReturn(somePublicRelease);
when(somePublicRelease.getReleaseKey()).thenReturn(somePublicAppSideReleaseKey);
......
......@@ -17,6 +17,8 @@ public class ReleaseDTO extends BaseDTO{
private String comment;
private boolean isAbandoned;
public long getId() {
return id;
}
......@@ -81,4 +83,11 @@ public class ReleaseDTO extends BaseDTO{
this.namespaceName = namespaceName;
}
public boolean isAbandoned() {
return isAbandoned;
}
public void setAbandoned(boolean abandoned) {
isAbandoned = abandoned;
}
}
......@@ -144,10 +144,24 @@ public class AdminServiceAPI {
@Service
public static class ReleaseAPI extends API {
public List<ReleaseDTO> findReleases(String appId, Env env, String clusterName, String namespaceName, int page,
int size) {
public ReleaseDTO loadRelease(Env env, long releaseId) {
return restTemplate.get(env, "releases/{releaseId}", ReleaseDTO.class, releaseId);
}
public List<ReleaseDTO> findAllReleases(String appId, Env env, String clusterName, String namespaceName, int page,
int size) {
ReleaseDTO[] releaseDTOs = restTemplate.get(
env, "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases/all?page={page}&size={size}",
ReleaseDTO[].class,
appId, clusterName, namespaceName, page, size);
return Arrays.asList(releaseDTOs);
}
public List<ReleaseDTO> findActiveReleases(String appId, Env env, String clusterName, String namespaceName,
int page,
int size) {
ReleaseDTO[] releaseDTOs = restTemplate.get(
env, "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases?page={page}&size={size}",
env, "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases/active?page={page}&size={size}",
ReleaseDTO[].class,
appId, clusterName, namespaceName, page, size);
return Arrays.asList(releaseDTOs);
......@@ -161,8 +175,8 @@ public class AdminServiceAPI {
return releaseDTO;
}
public ReleaseDTO release(String appId, Env env, String clusterName, String namespace,
String releaseTitle, String comment, String operator) {
public ReleaseDTO createRelease(String appId, Env env, String clusterName, String namespace,
String releaseTitle, String comment, String operator) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType(MediaType.APPLICATION_FORM_URLENCODED_VALUE + ";charset=UTF-8"));
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
......@@ -177,6 +191,12 @@ public class AdminServiceAPI {
appId, clusterName, namespace);
return response;
}
public void rollback(Env env, long releaseId, String operator) {
restTemplate.put(env,
"releases/{releaseId}/rollback?operator={operator}",
null, releaseId, operator);
}
}
@Service
......
......@@ -31,7 +31,8 @@ import javax.annotation.PostConstruct;
public class AdminServiceAddressLocator {
private static final int DEFAULT_TIMEOUT_MS = 1000;
private static final long REFRESH_INTERVAL = 5 * 60 * 1000;
// private static final long REFRESH_INTERVAL = 5 * 60 * 1000;
private static final long REFRESH_INTERVAL = 1000;
private static final int RETRY_TIMES = 3;
private static final String ADMIN_SERVICE_URL_PATH = "/services/admin";
......
......@@ -66,6 +66,10 @@ public class RetryableRestTemplate {
private <T> T execute(HttpMethod method, Env env, String path, Object request, Class<T> responseType,
Object... uriVariables) {
if (path.startsWith("/")){
path = path.substring(1, path.length());
}
String uri = uriTemplateHandler.expand(path, uriVariables).getPath();
Transaction ct = Cat.newTransaction("AdminAPI", uri);
......
......@@ -138,5 +138,4 @@ public class AppController {
return response;
}
}
......@@ -4,6 +4,7 @@ import com.ctrip.framework.apollo.common.utils.RequestPrecondition;
import com.ctrip.framework.apollo.core.dto.ReleaseDTO;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.entity.form.NamespaceReleaseModel;
import com.ctrip.framework.apollo.portal.entity.vo.ReleaseCompareResult;
import com.ctrip.framework.apollo.portal.entity.vo.ReleaseVO;
import com.ctrip.framework.apollo.portal.service.ReleaseService;
......@@ -42,16 +43,46 @@ public class ReleaseController {
}
@RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/releases")
public List<ReleaseVO> findReleases(@PathVariable String appId,
@PathVariable String env, @PathVariable String clusterName,
@PathVariable String namespaceName,
@RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "5") int size){
@RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/releases/all")
public List<ReleaseVO> findAllReleases(@PathVariable String appId,
@PathVariable String env,
@PathVariable String clusterName,
@PathVariable String namespaceName,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "5") int size) {
RequestPrecondition.checkNumberPositive(size);
RequestPrecondition.checkNumberNotNegative(page);
return releaseService.findReleases(appId, Env.valueOf(env), clusterName, namespaceName, page, size);
return releaseService.findAllReleases(appId, Env.valueOf(env), clusterName, namespaceName, page, size);
}
@RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/releases/active")
public List<ReleaseDTO> findActiveReleases(@PathVariable String appId,
@PathVariable String env,
@PathVariable String clusterName,
@PathVariable String namespaceName,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "5") int size) {
RequestPrecondition.checkNumberPositive(size);
RequestPrecondition.checkNumberNotNegative(page);
return releaseService.findActiveReleases(appId, Env.valueOf(env), clusterName, namespaceName, page, size);
}
@RequestMapping(value = "/envs/{env}/releases/compare")
public ReleaseCompareResult compareRelease(@PathVariable String env,
@RequestParam long firstReleaseId,
@RequestParam long secondReleaseId) {
return releaseService.compare(Env.valueOf(env), firstReleaseId, secondReleaseId);
}
@RequestMapping(path = "/envs/{env}/releases/{releaseId}/rollback", method = RequestMethod.PUT)
public void rollback(@PathVariable String env,
@PathVariable long releaseId) {
releaseService.rollback(Env.valueOf(env), releaseId);
}
}
package com.ctrip.framework.apollo.portal.entity.vo;
public class EntityPair<E> {
private E firstEntity;
private E secondEntity;
public EntityPair(E firstEntity, E secondEntity){
this.firstEntity = firstEntity;
this.secondEntity = secondEntity;
}
public E getFirstEntity() {
return firstEntity;
}
public void setFirstEntity(E firstEntity) {
this.firstEntity = firstEntity;
}
public E getSecondEntity() {
return secondEntity;
}
public void setSecondEntity(E secondEntity) {
this.secondEntity = secondEntity;
}
}
package com.ctrip.framework.apollo.portal.entity.vo;
public class KVEntity {
private String key;
private String value;
public KVEntity(String key, String value) {
this.key = key;
this.value = value;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
package com.ctrip.framework.apollo.portal.entity.vo;
import com.ctrip.framework.apollo.core.dto.ItemDTO;
import com.ctrip.framework.apollo.core.dto.NamespaceDTO;
import com.ctrip.framework.apollo.core.enums.ConfigFileFormat;
import java.util.List;
public class NamespaceVO {
private NamespaceDTO namespace;
private NamespaceDTO baseInfo;
private int itemModifiedCnt;
private List<ItemVO> items;
private String format;
......@@ -14,12 +13,12 @@ public class NamespaceVO {
private String parentAppId;
public NamespaceDTO getNamespace() {
return namespace;
public NamespaceDTO getBaseInfo() {
return baseInfo;
}
public void setNamespace(NamespaceDTO namespace) {
this.namespace = namespace;
public void setBaseInfo(NamespaceDTO baseInfo) {
this.baseInfo = baseInfo;
}
public int getItemModifiedCnt() {
......
package com.ctrip.framework.apollo.portal.entity.vo;
import java.util.LinkedList;
import java.util.List;
public class ReleaseCompareResult {
private List<EntityPair<KVEntity>> changes = new LinkedList<>();
public void addEntityPair(KVEntity firstEntity, KVEntity secondEntity){
changes.add(new EntityPair<>(firstEntity, secondEntity));
}
public List<EntityPair<KVEntity>> getChanges() {
return changes;
}
public void setChanges(
List<EntityPair<KVEntity>> changes) {
this.changes = changes;
}
}
......@@ -26,29 +26,4 @@ public class ReleaseVO {
this.items = items;
}
public static class KVEntity{
String key;
String value;
public KVEntity(String key, String value){
this.key = key;
this.value = value;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
}
......@@ -4,7 +4,6 @@ import com.google.gson.Gson;
import com.ctrip.framework.apollo.common.entity.AppNamespace;
import com.ctrip.framework.apollo.common.utils.BeanUtils;
import com.ctrip.framework.apollo.common.utils.ExceptionUtils;
import com.ctrip.framework.apollo.core.dto.ItemDTO;
import com.ctrip.framework.apollo.core.dto.NamespaceDTO;
import com.ctrip.framework.apollo.core.dto.ReleaseDTO;
......@@ -20,9 +19,7 @@ import com.dianping.cat.Cat;
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;
......@@ -92,7 +89,7 @@ public class NamespaceService {
@SuppressWarnings("unchecked")
private NamespaceVO parseNamespace(String appId, Env env, String clusterName, NamespaceDTO namespace) {
NamespaceVO namespaceVO = new NamespaceVO();
namespaceVO.setNamespace(namespace);
namespaceVO.setBaseInfo(namespace);
fillFormatAndIsPublicAndParentAppField(namespaceVO);
......@@ -104,17 +101,9 @@ public class NamespaceService {
//latest Release
ReleaseDTO latestRelease = null;
Map<String, String> releaseItems = new HashMap<>();
try {
latestRelease = releaseAPI.loadLatestRelease(appId, env, clusterName, namespaceName);
if (latestRelease != null) {
releaseItems = gson.fromJson(latestRelease.getConfigurations(), Map.class);
}
} catch (HttpClientErrorException e) {
if (e.getStatusCode() == HttpStatus.NOT_FOUND) {
logger.warn(ExceptionUtils.toString(e));
} else {
throw e;
}
latestRelease = releaseAPI.loadLatestRelease(appId, env, clusterName, namespaceName);
if (latestRelease != null) {
releaseItems = gson.fromJson(latestRelease.getConfigurations(), Map.class);
}
//not Release config items
......@@ -143,7 +132,7 @@ public class NamespaceService {
private void fillFormatAndIsPublicAndParentAppField(NamespaceVO namespace) {
NamespaceDTO namespaceDTO = namespace.getNamespace();
NamespaceDTO namespaceDTO = namespace.getBaseInfo();
//先从当前appId下面找,包含私有的和公共的
AppNamespace appNamespace =
appNamespaceService.findByAppIdAndName(namespaceDTO.getAppId(), namespaceDTO.getNamespaceName());
......
......@@ -8,6 +8,8 @@ import com.ctrip.framework.apollo.portal.api.AdminServiceAPI;
import com.ctrip.framework.apollo.portal.auth.UserInfoHolder;
import com.ctrip.framework.apollo.portal.constant.CatEventType;
import com.ctrip.framework.apollo.portal.entity.form.NamespaceReleaseModel;
import com.ctrip.framework.apollo.portal.entity.vo.KVEntity;
import com.ctrip.framework.apollo.portal.entity.vo.ReleaseCompareResult;
import com.ctrip.framework.apollo.portal.entity.vo.ReleaseVO;
import com.dianping.cat.Cat;
......@@ -38,15 +40,16 @@ public class ReleaseService {
String clusterName = model.getClusterName();
String namespaceName = model.getNamespaceName();
ReleaseDTO releaseDTO =
releaseAPI.release(appId, env, clusterName, namespaceName, model.getReleaseTitle(), model.getReleaseComment()
, userInfoHolder.getUser().getUserId());
releaseAPI
.createRelease(appId, env, clusterName, namespaceName, model.getReleaseTitle(), model.getReleaseComment()
, userInfoHolder.getUser().getUserId());
Cat.logEvent(CatEventType.RELEASE_NAMESPACE, String.format("%s+%s+%s+%s", appId, env, clusterName, namespaceName));
return releaseDTO;
}
public List<ReleaseVO> findReleases(String appId, Env env, String clusterName, String namespaceName, int page,
int size) {
List<ReleaseDTO> releaseDTOs = releaseAPI.findReleases(appId, env, clusterName, namespaceName, page, size);
public List<ReleaseVO> findAllReleases(String appId, Env env, String clusterName, String namespaceName, int page,
int size) {
List<ReleaseDTO> releaseDTOs = releaseAPI.findAllReleases(appId, env, clusterName, namespaceName, page, size);
if (CollectionUtils.isEmpty(releaseDTOs)) {
return Collections.EMPTY_LIST;
......@@ -57,10 +60,10 @@ public class ReleaseService {
ReleaseVO release = new ReleaseVO();
release.setBaseInfo(releaseDTO);
Set<ReleaseVO.KVEntity> kvEntities = new LinkedHashSet<>();
Set<KVEntity> kvEntities = new LinkedHashSet<>();
Set<Map.Entry> entries = gson.fromJson(releaseDTO.getConfigurations(), Map.class).entrySet();
for (Map.Entry<String, String> entry : entries) {
kvEntities.add(new ReleaseVO.KVEntity(entry.getKey(), entry.getValue()));
kvEntities.add(new KVEntity(entry.getKey(), entry.getValue()));
}
release.setItems(kvEntities);
//为了减少数据量
......@@ -70,4 +73,45 @@ public class ReleaseService {
return releases;
}
public List<ReleaseDTO> findActiveReleases(String appId, Env env, String clusterName, String namespaceName, int page,
int size) {
return releaseAPI.findActiveReleases(appId, env, clusterName, namespaceName, page, size);
}
public void rollback(Env env, long releaseId) {
releaseAPI.rollback(env, releaseId, userInfoHolder.getUser().getUserId());
}
public ReleaseCompareResult compare(Env env, long firstReleaseId, long secondReleaseId) {
ReleaseDTO firstRelease = releaseAPI.loadRelease(env, firstReleaseId);
ReleaseDTO secondRelease = releaseAPI.loadRelease(env, secondReleaseId);
Map<String, String> firstItems = gson.fromJson(firstRelease.getConfigurations(), Map.class);
Map<String, String> secondItems = gson.fromJson(secondRelease.getConfigurations(), Map.class);
ReleaseCompareResult compareResult = new ReleaseCompareResult();
//added and modified in firstRelease
for (Map.Entry<String, String> entry : firstItems.entrySet()) {
String key = entry.getKey();
String firstValue = entry.getValue();
String secondValue = secondItems.get(key);
if (secondValue == null || !firstValue.equals(secondValue)) {
compareResult.addEntityPair(new KVEntity(key, firstValue), new KVEntity(key, secondValue));
}
}
//deleted in firstRelease
for (Map.Entry<String, String> entry : secondItems.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if (firstItems.get(key) == null) {
compareResult.addEntityPair(new KVEntity(key, ""), new KVEntity(key, value));
}
}
return compareResult;
}
}
......@@ -96,7 +96,8 @@
env="pageContext.env" cluster="pageContext.clusterName"
pre-release-ns="prepareReleaseNamespace"
create-item="createItem" edit-item="editItem"
pre-delete-item="preDeleteItem" commit-change="commitChange"></apollonspanel>
pre-delete-item="preDeleteItem" commit-change="commitChange"
show-rollback-tips="showRollbackTips"></apollonspanel>
</div>
<!-- delete modal-->
......@@ -114,7 +115,13 @@
<apolloconfirmdialog apollo-dialog-id="'releaseDenyDialog'" apollo-title="'发布受限'"
apollo-detail="'您不能发布哟~ 编辑和发布不能为同一个人'"
apollo-show-cancel-btn="false"></apolloconfirmdialog>
<!--create release modal-->
<apolloconfirmdialog apollo-dialog-id="'rollbackTips'" apollo-title="'回滚'"
apollo-detail="'此操作将会回滚到上一个发布版本,且作废当前版本。确定要回滚吗?'"
apollo-show-cancel-btn="true" apollo-confirm="preRollback"></apolloconfirmdialog>
<!--create createRelease modal-->
<form class="modal fade form-horizontal" id="releaseModal" tabindex="-1" role="dialog"
ng-submit="release()">
<div class="modal-dialog" role="document" style="width: 960px">
......@@ -308,12 +315,82 @@
<button type="button" class="btn btn-default" data-dismiss="modal">关闭
</button>
<button type="submit" class="btn btn-primary"
ng-show="tableViewOperType != 'retrieve'" ng-disabled="addItemBtnDisabled && tableViewOperType == 'create'">提交
ng-show="tableViewOperType != 'retrieve'"
ng-disabled="addItemBtnDisabled && tableViewOperType == 'create'">提交
</button>
</div>
</div>
</div>
</form>
<!--rollback-->
<form class="modal fade form-horizontal" id="rollbackModal" tabindex="-1" role="dialog"
ng-submit="rollback()">
<div class="modal-dialog" role="document" style="width: 960px">
<div class="modal-content">
<div class="modal-header panel-primary">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true">&times;</span></button>
<h4 class="modal-title">回滚</h4>
</div>
<div class="modal-body">
<div class="row text-center">
<span style="font-size: 18px;" ng-bind="firstRelease.name"></span>
<span style="font-size: 18px;"> &nbsp;回滚到&nbsp;</span>
<span style="font-size: 18px;" ng-bind="secondRelease.name"></span>
</div>
<div class="form-group" style="margin-top: 15px;">
<label class="col-sm-2 control-label">
Changes:</label>
<div class="col-sm-10" ng-if="releaseCompareResult.length > 0">
<table class="table table-bordered table-striped text-center table-hover">
<thead>
<tr>
<th>
Key
</th>
<th>
回滚前
</th>
<th>
回滚后
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="pair in releaseCompareResult">
<td width="20%" ng-bind="pair.firstEntity.key">
</td>
<td width="40%" ng-bind="pair.firstEntity.value">
</td>
<td width="40%" ng-bind="pair.secondEntity.value">
</td>
</tr>
</tbody>
</table>
</div>
<div class="col-sm-5">
<span ng-if="releaseCompareResult.length == 0">
配置没有变化
</span>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button type="submit" class="btn btn-danger" ng-if="releaseCompareResult.length > 0"
ng-disabled="rollbackBtnDisabled">回滚
</button>
</div>
</div>
</div>
</form>
</div>
</div>
......
......@@ -31,10 +31,16 @@
<div class="media-left media-middle badge-{{$index % 4}}">
</div>
<div class="media-body">
<div class="row text-right">
<span class="label label-info no-radius"
ng-show="release.baseInfo.isAbandoned">已废弃</span>
<span class="label label-primary no-radius"
ng-if="release.active">当前生效</span>
</div>
<div class="row">
<div class="col-md-2 user">
<img src="../img/user.png" class="i-20">
<span class="info" ng-bind="release.baseInfo.dataChangeCreatedBy"></span>
<span class="info" ng-bind="release.baseInfo.dataChangeCreatedBy"></span>
</div>
<div class="col-md-3 time">
<img src="../img/time.png" class="i-20">
......@@ -62,7 +68,8 @@
</td>
</tr>
</table>
<textarea class="form-control no-radius" rows="15" ng-show="isTextFile" ng-bind="release.items[0].value" disabled>
<textarea class="form-control no-radius" rows="15" ng-show="isTextFile"
ng-bind="release.items[0].value" disabled>
</textarea>
</div>
......
......@@ -27,18 +27,18 @@ index_module.controller('IndexController', ['$scope', '$window', 'toastr', 'AppS
}, function (result) {
toastr.error(AppUtil.errorMsg(result), "load apps error");
});
};
}
$scope.search = function () {
var key = $scope.searchKey;
var key = $scope.searchKey.toLocaleLowerCase();
if (key == '') {
$scope.apps = apps;
return;
}
var result = [];
apps.forEach(function (item) {
if (item.appId.indexOf(key) >= 0 || item.name.indexOf(key) >= 0) {
if (item.appId.toLocaleLowerCase().indexOf(key) >= 0 ||
item.name.toLocaleLowerCase().indexOf(key) >= 0) {
result.push(item);
}
});
......
......@@ -2,7 +2,7 @@ application_module.controller("ConfigNamespaceController",
['$rootScope', '$scope', '$window', '$location', 'toastr', 'AppUtil', 'ConfigService',
'PermissionService',
'CommitService', 'NamespaceLockService', 'UserService', 'ReleaseService',
function ($rootScope, $scope, $window, $location, toastr, AppUtil, ConfigService,
function ($rootScope, $scope, $window, $location, toastr, AppUtil, ConfigService,
PermissionService,
CommitService, NamespaceLockService, UserService, ReleaseService) {
......@@ -18,33 +18,37 @@ application_module.controller("ConfigNamespaceController",
UPDATE: 'update'
};
$rootScope.refreshNamespaces = refreshNamespaces;
$scope.commitChange = commitChange;
$scope.prepareReleaseNamespace = prepareReleaseNamespace;
$scope.release = release;
$scope.showRollbackTips = showRollbackTips;
$scope.preRollback = preRollback;
$scope.rollback = rollback;
$scope.retrieveItem = retrieveItem;
$scope.preDeleteItem = preDeleteItem;
$scope.deleteItem = deleteItem;
$scope.editItem = editItem;
$scope.createItem = createItem;
$scope.doItem = doItem;
$scope.doItem = doItem;
$scope.releaseBtnDisabled = false;
$scope.rollbackBtnDisabled = false;
$scope.addItemBtnDisabled = false;
$scope.commitChangeBtnDisabled = false;
PermissionService.get_app_role_users($rootScope.pageContext.appId)
.then(function (result) {
var masterUsers = '';
......@@ -55,7 +59,7 @@ application_module.controller("ConfigNamespaceController",
}, function (result) {
});
UserService.load_user().then(function (result) {
$scope.currentUser = result.userId;
});
......@@ -93,18 +97,19 @@ application_module.controller("ConfigNamespaceController",
function commitChange(namespace) {
var model = {
configText: namespace.editText,
namespaceId: namespace.namespace.id,
namespaceId: namespace.baseInfo.id,
format: namespace.format
};
//prevent repeat submit
if ($scope.commitChangeBtnDisabled){
if ($scope.commitChangeBtnDisabled) {
return;
}
$scope.commitChangeBtnDisabled = true;
ConfigService.modify_items($rootScope.pageContext.appId, $rootScope.pageContext.env,
ConfigService.modify_items($rootScope.pageContext.appId,
$rootScope.pageContext.env,
$rootScope.pageContext.clusterName,
namespace.namespace.namespaceName,
namespace.baseInfo.namespaceName,
model).then(
function (result) {
toastr.success("更新成功, 如需生效请发布");
......@@ -127,7 +132,7 @@ application_module.controller("ConfigNamespaceController",
if (!namespace.hasReleasePermission) {
$('#releaseNoPermissionDialog').modal('show');
return;
} else if (namespace.lockOwner && $scope.currentUser == namespace.lockOwner){
} else if (namespace.lockOwner && $scope.currentUser == namespace.lockOwner) {
//自己修改不能自己发布
$('#releaseDenyDialog').modal('show');
} else {
......@@ -141,10 +146,10 @@ application_module.controller("ConfigNamespaceController",
function release() {
$scope.releaseBtnDisabled = true;
ReleaseService.release($rootScope.pageContext.appId, $rootScope.pageContext.env,
$rootScope.pageContext.clusterName,
$scope.toReleaseNamespace.namespace.namespaceName,
$scope.releaseTitle,
$scope.releaseComment).then(
$rootScope.pageContext.clusterName,
$scope.toReleaseNamespace.baseInfo.namespaceName,
$scope.releaseTitle,
$scope.releaseComment).then(
function (result) {
releaseModal.modal('hide');
toastr.success("发布成功");
......@@ -160,7 +165,58 @@ application_module.controller("ConfigNamespaceController",
);
}
var toRollbackNamespace = {};
function showRollbackTips(namespace) {
toRollbackNamespace = namespace;
$("#rollbackTips").modal('show');
}
function preRollback() {
//load latest two active releases
ReleaseService.findActiveRelease($rootScope.pageContext.appId,
$rootScope.pageContext.env,
$rootScope.pageContext.clusterName,
toRollbackNamespace.baseInfo.namespaceName, 0, 2)
.then(function (result) {
if (result.length <= 1) {
toastr.error("没有可以回滚的发布历史");
return;
}
$scope.firstRelease = result[0];
$scope.secondRelease = result[1];
ReleaseService.compare($rootScope.pageContext.env,
$scope.firstRelease.id,
$scope.secondRelease.id)
.then(function (result) {
$scope.releaseCompareResult = result.changes;
$("#rollbackModal").modal('show');
}, function (result) {
toastr.error(AppUtil.errorMsg(result), "对比失败");
})
}, function (result) {
toastr.error(AppUtil.errorMsg(result), "加载最近两次发布失败");
});
}
function rollback() {
$scope.rollbackBtnDisabled = true;
ReleaseService.rollback(
$rootScope.pageContext.env,
$scope.firstRelease.id)
.then(function (result) {
toastr.success("回滚成功");
$scope.rollbackBtnDisabled = false;
$("#rollbackModal").modal("hide");
$rootScope.refreshNamespaces();
}, function (result) {
$scope.rollbackBtnDisabled = false;
toastr.error(AppUtil.errorMsg(result), "回滚失败");
})
}
$scope.tableViewOperType = '', $scope.item = {};
var toOperationNamespace;
......@@ -175,8 +231,9 @@ application_module.controller("ConfigNamespaceController",
}
var toDeleteItemId = 0;
function preDeleteItem(namespace, itemId) {
if (!lockCheck(namespace)){
if (!lockCheck(namespace)) {
return;
}
......@@ -190,7 +247,7 @@ application_module.controller("ConfigNamespaceController",
ConfigService.delete_item($rootScope.pageContext.appId,
$rootScope.pageContext.env,
$rootScope.pageContext.clusterName,
toOperationNamespace.namespace.namespaceName,
toOperationNamespace.baseInfo.namespaceName,
toDeleteItemId).then(
function (result) {
toastr.success("删除成功!");
......@@ -202,7 +259,7 @@ application_module.controller("ConfigNamespaceController",
//修改配置
function editItem(namespace, item) {
if (!lockCheck(namespace)){
if (!lockCheck(namespace)) {
return;
}
switchTableViewOperType(TABLE_VIEW_OPER_TYPE.UPDATE);
......@@ -214,7 +271,7 @@ application_module.controller("ConfigNamespaceController",
//新增配置
function createItem(namespace) {
if (!lockCheck(namespace)){
if (!lockCheck(namespace)) {
return;
}
......@@ -234,6 +291,7 @@ application_module.controller("ConfigNamespaceController",
}
var itemModal = $("#itemModal");
function doItem() {
if (selectedClusters.length == 0) {
......@@ -247,13 +305,13 @@ application_module.controller("ConfigNamespaceController",
//check key unique
var hasRepeatKey = false;
toOperationNamespace.items.forEach(function (item) {
if (!item.isDeleted && $scope.item.key == item.item.key){
toastr.error("key=" + $scope.item.key + " 已存在");
hasRepeatKey = true;
return;
}
if (!item.isDeleted && $scope.item.key == item.item.key) {
toastr.error("key=" + $scope.item.key + " 已存在");
hasRepeatKey = true;
return;
}
});
if (hasRepeatKey){
if (hasRepeatKey) {
return;
}
......@@ -261,7 +319,7 @@ application_module.controller("ConfigNamespaceController",
ConfigService.create_item($rootScope.pageContext.appId,
cluster.env,
cluster.name,
toOperationNamespace.namespace.namespaceName,
toOperationNamespace.baseInfo.namespaceName,
$scope.item).then(
function (result) {
toastr.success(cluster.env + " , " + $scope.item.key,
......@@ -282,7 +340,7 @@ application_module.controller("ConfigNamespaceController",
ConfigService.update_item($rootScope.pageContext.appId,
cluster.env,
cluster.name,
toOperationNamespace.namespace.namespaceName,
toOperationNamespace.baseInfo.namespaceName,
$scope.item).then(
function (result) {
toastr.success("更新成功, 如需生效请发布");
......
......@@ -14,29 +14,38 @@ release_history_module.controller("ReleaseHistoryController",
$scope.page = 0;
$scope.releases = [];
$scope.hasLoadAll = false;
$scope.findReleases = findReleases;
$scope.loadMore = loadMore;
findReleases($scope.page);
var hasFindActiveRelease = false;
function findReleases(page) {
ReleaseService.findRelease($scope.pageContext.appId,
$scope.pageContext.env,
$scope.pageContext.clusterName,
$scope.pageContext.namespaceName,
page)
ReleaseService.findAllRelease($scope.pageContext.appId,
$scope.pageContext.env,
$scope.pageContext.clusterName,
$scope.pageContext.namespaceName,
page)
.then(function (result) {
if (!result || result.length == 0){
if (!result || result.length == 0) {
$scope.hasLoadAll = true;
return;
}
var hasParseNamepaceType = false;
var hasParseNamespaceType = false;
result.forEach(function (release) {
if (!hasParseNamepaceType){
$scope.isTextFile = /\.(json|yaml|yml|xml)$/gi.test(release.baseInfo.namespaceName);
hasParseNamepaceType = true;
if (!hasParseNamespaceType) {
$scope.isTextFile =
/\.(json|yaml|yml|xml)$/gi.test(
release.baseInfo.namespaceName);
hasParseNamespaceType = true;
}
if (!hasFindActiveRelease && !release.baseInfo.isAbandoned) {
release.active = true;
hasFindActiveRelease = true;
}
$scope.releases.push(release);
})
......
......@@ -31,8 +31,10 @@ directive_module.directive('apollonav', function ($compile, $window, toastr, App
scope.changeSearchKey = function () {
scope.copyedApps = [];
var searchKey = scope.searchKey.toLocaleLowerCase();
scope.sourceApps.forEach(function (app) {
if (app.name.indexOf(scope.searchKey) > -1 || app.appId.indexOf(scope.searchKey) > -1) {
if (app.name.toLocaleLowerCase().indexOf(searchKey) > -1
|| app.appId.toLocaleLowerCase().indexOf(searchKey) > -1) {
scope.copyedApps.push(app);
}
});
......
......@@ -12,6 +12,7 @@ directive_module.directive('apollonspanel',
env: '=',
cluster: '=',
preReleaseNs: '=',
showRollbackTips: '=',
createItem: '=',
editItem: '=',
preDeleteItem: '=',
......@@ -74,7 +75,7 @@ directive_module.directive('apollonspanel',
CommitService.find_commits(scope.appId,
scope.env,
scope.cluster,
namespace.namespace.namespaceName,
namespace.baseInfo.namespaceName,
namespace.commitPage)
.then(function (result) {
if (result.length == 0) {
......@@ -116,7 +117,7 @@ directive_module.directive('apollonspanel',
"config/sync.html?#/appid=" + scope.appId + "&env="
+ scope.env + "&clusterName="
+ scope.cluster
+ "&namespaceName=" + namespace.namespace.namespaceName;
+ "&namespaceName=" + namespace.baseInfo.namespaceName;
}
function modifyByText(namespace) {
......@@ -139,7 +140,7 @@ directive_module.directive('apollonspanel',
//namespace view name hide suffix
namespace.viewName =
namespace.namespace.namespaceName.replace(".xml", "").replace(
namespace.baseInfo.namespaceName.replace(".xml", "").replace(
".properties", "");
if (!viewType) {
......@@ -155,7 +156,7 @@ directive_module.directive('apollonspanel',
//permission
PermissionService.has_modify_namespace_permission(
scope.appId,
namespace.namespace.namespaceName)
namespace.baseInfo.namespaceName)
.then(function (result) {
namespace.hasModifyPermission = result.hasPermission;
}, function (result) {
......@@ -164,7 +165,7 @@ directive_module.directive('apollonspanel',
PermissionService.has_release_namespace_permission(
scope.appId,
namespace.namespace.namespaceName)
namespace.baseInfo.namespaceName)
.then(function (result) {
namespace.hasReleasePermission = result.hasPermission;
}, function (result) {
......@@ -175,7 +176,7 @@ directive_module.directive('apollonspanel',
NamespaceLockService.get_namespace_lock(
scope.appId, scope.env,
scope.cluster,
namespace.namespace.namespaceName)
namespace.baseInfo.namespaceName)
.then(function (result) {
if (result.dataChangeCreatedBy) {
namespace.lockOwner = result.dataChangeCreatedBy;
......
appService.service('ReleaseService', ['$resource', '$q', function ($resource, $q) {
var resource = $resource('', {}, {
find_releases: {
find_all_releases: {
method: 'GET',
url: '/apps/:appId/envs/:env/clusters/:clusterName/namespaces/:namespaceName/releases',
url: '/apps/:appId/envs/:env/clusters/:clusterName/namespaces/:namespaceName/releases/all',
isArray: true
},
find_active_releases: {
method: 'GET',
url: '/apps/:appId/envs/:env/clusters/:clusterName/namespaces/:namespaceName/releases/active',
isArray: true
},
compare: {
method: 'GET',
url: '/envs/:env/releases/compare'
},
release: {
method: 'POST',
url: '/apps/:appId/envs/:env/clusters/:clusterName/namespaces/:namespaceName/release'
},
rollback: {
method: 'PUT',
url: "envs/:env/releases/:releaseId/rollback"
}
});
......@@ -29,15 +42,15 @@ appService.service('ReleaseService', ['$resource', '$q', function ($resource, $q
return d.promise;
}
function findReleases(appId, env, clusterName, namespaceName, page) {
function findAllReleases(appId, env, clusterName, namespaceName, page) {
var d = $q.defer();
resource.find_releases({
appId: appId,
env: env,
clusterName: clusterName,
namespaceName: namespaceName,
page: page
}, function (result) {
resource.find_all_releases({
appId: appId,
env: env,
clusterName: clusterName,
namespaceName: namespaceName,
page: page
}, function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
......@@ -45,8 +58,57 @@ appService.service('ReleaseService', ['$resource', '$q', function ($resource, $q
return d.promise;
}
function findActiveReleases(appId, env, clusterName, namespaceName, page, size) {
var d = $q.defer();
resource.find_active_releases({
appId: appId,
env: env,
clusterName: clusterName,
namespaceName: namespaceName,
page: page,
size: size
}, function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
});
return d.promise;
}
function compare(env, firstReleaseId, secondReleaseId) {
var d = $q.defer();
resource.compare({
env: env,
firstReleaseId: firstReleaseId,
secondReleaseId: secondReleaseId
}, function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
});
return d.promise;
}
function rollback(env, releaseId) {
var d = $q.defer();
resource.rollback({
env: env,
releaseId: releaseId
}, {}, function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
}
);
return d.promise;
}
return {
release: createRelease,
findRelease: findReleases
findAllRelease: findAllReleases,
findActiveRelease: findActiveReleases,
compare: compare,
rollback: rollback
}
}]);
......@@ -448,3 +448,7 @@ table th {
.release-history .badge-3{
background: #3478a8;
}
.release-history .label{
margin-right: 15px;
}
......@@ -2,7 +2,8 @@
<div class="row namespace-attribute-panel" ng-if="namespace.isPublic">
<div class="text-center namespace-attribute-public" data-tooltip="tooltip" data-placement="bottom"
title="点击跳转到公共Namespace" ng-click="goToParentAppConfigPage(namespace)">
公共
<span ng-show="namespace.parentAppId == namespace.baseInfo.appId">公共</span>
<span ng-show="namespace.parentAppId != namespace.baseInfo.appId">关联</span>
</div>
</div>
<header class="panel-heading">
......@@ -10,10 +11,11 @@
<div class="col-md-6">
<b ng-bind="namespace.viewName" style="font-size: 20px;"></b>
<span class="label label-info no-radius" ng-bind="namespace.format" ></span>
<span class="label label-primary no-radius" ng-show="namespace.itemModifiedCnt > 0" >有修改
<span class="label label-info no-radius" ng-bind="namespace.format"></span>
<span class="label label-primary no-radius" ng-show="namespace.itemModifiedCnt > 0">有修改
<span class="badge label" ng-bind="namespace.itemModifiedCnt"></span></span>
<span class="label label-warning no-radius" ng-show="namespace.lockOwner">当前修改者:{{namespace.lockOwner}}</span>
<span class="label label-warning no-radius"
ng-show="namespace.lockOwner">当前修改者:{{namespace.lockOwner}}</span>
</div>
<div class="col-md-6 text-right">
<button type="button"
......@@ -26,18 +28,19 @@
</button>
<button type="button"
class="btn btn-default btn-sm J_tableview_btn" disabled
ng-show="namespace.hasReleasePermission">
class="btn btn-default btn-sm J_tableview_btn"
ng-show="namespace.hasReleasePermission"
ng-click="showRollbackTips(namespace)">
<img src="img/rollback.png">
回滚
</button>
<a type="button" class="btn btn-default btn-sm J_tableview_btn"
href="/config/history.html?#/appid={{appId}}&env={{env}}&clusterName={{cluster}}&namespaceName={{namespace.namespace.namespaceName}}">
href="/config/history.html?#/appid={{appId}}&env={{env}}&clusterName={{cluster}}&namespaceName={{namespace.baseInfo.namespaceName}}">
<img src="img/release-history.png">
发布历史
</a>
<a type="button" class="btn btn-default btn-sm J_tableview_btn"
href="/namespace/role.html?#/appid={{appId}}&namespaceName={{namespace.namespace.namespaceName}}"
href="/namespace/role.html?#/appid={{appId}}&namespaceName={{namespace.baseInfo.namespaceName}}"
ng-show="hasAssignUserPermission">
<img src="img/assign.png">
授权
......
......@@ -8,8 +8,6 @@ import com.ctrip.framework.apollo.core.enums.ConfigFileFormat;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.api.AdminServiceAPI;
import com.ctrip.framework.apollo.portal.entity.vo.NamespaceVO;
import com.ctrip.framework.apollo.portal.service.AppNamespaceService;
import com.ctrip.framework.apollo.portal.service.NamespaceService;
import com.ctrip.framework.apollo.portal.service.txtresolver.PropertyResolver;
import org.junit.Before;
......@@ -94,9 +92,9 @@ public class NamespaceServiceTest {
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());
assertEquals(appId, namespaceVO.getBaseInfo().getAppId());
assertEquals(clusterName, namespaceVO.getBaseInfo().getClusterName());
assertEquals(namespaceName, namespaceVO.getBaseInfo().getNamespaceName());
}
......
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