Commit e46d33ee authored by Jason Song's avatar Jason Song

Merge pull request #89 from lepdou/portal

create app & release config
parents f7191bfa 8720978c
...@@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.RequestParam; ...@@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import com.ctrip.apollo.biz.entity.App; import com.ctrip.apollo.biz.entity.App;
import com.ctrip.apollo.biz.service.AdminService;
import com.ctrip.apollo.biz.service.AppService; import com.ctrip.apollo.biz.service.AppService;
import com.ctrip.apollo.biz.utils.BeanUtils; import com.ctrip.apollo.biz.utils.BeanUtils;
import com.ctrip.apollo.core.dto.AppDTO; import com.ctrip.apollo.core.dto.AppDTO;
...@@ -25,11 +26,13 @@ public class AppController { ...@@ -25,11 +26,13 @@ public class AppController {
@Autowired @Autowired
private AppService appService; private AppService appService;
@Autowired
private AdminService adminService;
@RequestMapping(path = "/apps/", method = RequestMethod.POST) @RequestMapping(path = "/apps", method = RequestMethod.POST)
public ResponseEntity<AppDTO> create(@RequestBody AppDTO dto) { public ResponseEntity<AppDTO> create(@RequestBody AppDTO dto) {
App entity = BeanUtils.transfrom(App.class, dto); App entity = BeanUtils.transfrom(App.class, dto);
entity = appService.save(entity); entity = adminService.createNewApp(entity);
dto = BeanUtils.transfrom(AppDTO.class, entity); dto = BeanUtils.transfrom(AppDTO.class, entity);
return ResponseEntity.status(HttpStatus.CREATED).body(dto); return ResponseEntity.status(HttpStatus.CREATED).body(dto);
} }
......
package com.ctrip.apollo.biz.service; package com.ctrip.apollo.biz.service;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.metrics.CounterService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.boot.actuate.metrics.CounterService;
import com.ctrip.apollo.biz.entity.App; import com.ctrip.apollo.biz.entity.App;
import com.ctrip.apollo.biz.entity.AppNamespace; import com.ctrip.apollo.biz.entity.AppNamespace;
import com.ctrip.apollo.biz.entity.Cluster; import com.ctrip.apollo.biz.entity.Cluster;
...@@ -12,6 +12,9 @@ import com.ctrip.apollo.biz.repository.AppNamespaceRepository; ...@@ -12,6 +12,9 @@ import com.ctrip.apollo.biz.repository.AppNamespaceRepository;
import com.ctrip.apollo.biz.repository.AppRepository; import com.ctrip.apollo.biz.repository.AppRepository;
import com.ctrip.apollo.biz.repository.ClusterRepository; import com.ctrip.apollo.biz.repository.ClusterRepository;
import com.ctrip.apollo.biz.repository.NamespaceRepository; import com.ctrip.apollo.biz.repository.NamespaceRepository;
import com.ctrip.apollo.core.ConfigConsts;
import java.util.Date;
@Service @Service
public class AdminService { public class AdminService {
...@@ -27,36 +30,56 @@ public class AdminService { ...@@ -27,36 +30,56 @@ public class AdminService {
@Autowired @Autowired
private ClusterRepository clusterRepository; private ClusterRepository clusterRepository;
@Autowired @Autowired
private CounterService counter; private CounterService counter;
public App createNewApp(String appId, String appName, String ownerName, String ownerEmail, public App createNewApp(App app) {
String namespace) {
counter.increment("admin.createNewApp.start"); counter.increment("admin.createNewApp.start");
App app = new App();
app.setAppId(appId);
app.setName(appName);
app.setOwnerName(ownerName);
app.setOwnerEmail(ownerEmail);
appRepository.save(app);
String createBy = app.getDataChangeCreatedBy();
App createdApp = appRepository.save(app);
String appId = createdApp.getAppId();
createDefaultAppNamespace(appId, createBy);
createDefaultCluster(appId, createBy);
createDefaultNamespace(appId, createBy);
counter.increment("admin.createNewApp.success");
return app;
}
private void createDefaultAppNamespace(String appId, String createBy){
AppNamespace appNs = new AppNamespace(); AppNamespace appNs = new AppNamespace();
appNs.setAppId(appId); appNs.setAppId(appId);
appNs.setName(namespace); appNs.setName(ConfigConsts.NAMESPACE_APPLICATION);
appNs.setComment("default app namespace");
appNs.setDataChangeCreatedBy(createBy);
appNs.setDataChangeCreatedTime(new Date());
appNs.setDataChangeLastModifiedBy(createBy);
appNamespaceRepository.save(appNs); appNamespaceRepository.save(appNs);
}
private void createDefaultCluster(String appId, String createBy){
Cluster cluster = new Cluster(); Cluster cluster = new Cluster();
cluster.setName("default"); cluster.setName(ConfigConsts.CLUSTER_NAME_DEFAULT);
cluster.setAppId(appId); cluster.setAppId(appId);
cluster.setDataChangeCreatedBy(createBy);
cluster.setDataChangeCreatedTime(new Date());
cluster.setDataChangeLastModifiedBy(createBy);
clusterRepository.save(cluster); clusterRepository.save(cluster);
}
private void createDefaultNamespace(String appId, String createBy){
Namespace ns = new Namespace(); Namespace ns = new Namespace();
ns.setAppId(appId); ns.setAppId(appId);
ns.setClusterName(cluster.getName()); ns.setClusterName(ConfigConsts.CLUSTER_NAME_DEFAULT);
ns.setNamespaceName(namespace); ns.setNamespaceName(ConfigConsts.NAMESPACE_APPLICATION);
ns.setDataChangeCreatedBy(createBy);
ns.setDataChangeCreatedTime(new Date());
ns.setDataChangeLastModifiedBy(createBy);
namespaceRepository.save(ns); namespaceRepository.save(ns);
counter.increment("admin.createNewApp.success");
return app;
} }
} }
...@@ -20,6 +20,7 @@ public class ItemSetService { ...@@ -20,6 +20,7 @@ public class ItemSetService {
for (ItemDTO item : changeSet.getCreateItems()) { for (ItemDTO item : changeSet.getCreateItems()) {
Item entity = BeanUtils.transfrom(Item.class, item); Item entity = BeanUtils.transfrom(Item.class, item);
entity.setDataChangeCreatedBy(changeSet.getModifyBy()); entity.setDataChangeCreatedBy(changeSet.getModifyBy());
entity.setDataChangeLastModifiedBy(changeSet.getModifyBy());
itemRepository.save(entity); itemRepository.save(entity);
} }
} }
......
package com.ctrip.apollo.biz.service; package com.ctrip.apollo.biz.service;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
...@@ -53,6 +54,9 @@ public class ReleaseService { ...@@ -53,6 +54,9 @@ public class ReleaseService {
} }
Release release = new Release(); Release release = new Release();
release.setDataChangeCreatedTime(new Date());
release.setDataChangeCreatedBy(name);
release.setDataChangeLastModifiedBy(name);
release.setName(name); release.setName(name);
release.setComment(comment); release.setComment(comment);
release.setAppId(appId); release.setAppId(appId);
......
package com.ctrip.apollo.biz.service; package com.ctrip.apollo.biz.service;
import java.util.Date;
import java.util.List; import java.util.List;
import org.junit.Assert; import org.junit.Assert;
...@@ -27,12 +28,17 @@ public class AdminServiceTest { ...@@ -27,12 +28,17 @@ public class AdminServiceTest {
@Test @Test
public void testCreateNewApp() { public void testCreateNewApp() {
String appId = "someAppId"; String appId = "someAppId";
String appName = "someAppName"; App app = new App();
String ownerName = "someOwnerName"; app.setAppId(appId);
String ownerEmail = "someOwnerName@ctrip.com"; app.setName("someAppName");
String namespace = "someNamespace"; String owner = "someOwnerName";
app.setOwnerName(owner);
App app = adminService.createNewApp(appId, appName, ownerName, ownerEmail, namespace); app.setOwnerEmail("someOwnerName@ctrip.com");
app.setDataChangeCreatedBy(owner);
app.setDataChangeLastModifiedBy(owner);
app.setDataChangeCreatedTime(new Date());
app = adminService.createNewApp(app);
Assert.assertEquals(appId, app.getAppId()); Assert.assertEquals(appId, app.getAppId());
List<Cluster> clusters = viewService.findClusters(app.getAppId()); List<Cluster> clusters = viewService.findClusters(app.getAppId());
...@@ -41,6 +47,6 @@ public class AdminServiceTest { ...@@ -41,6 +47,6 @@ public class AdminServiceTest {
List<Namespace> namespaces = viewService.findNamespaces(appId, clusters.get(0).getName()); List<Namespace> namespaces = viewService.findNamespaces(appId, clusters.get(0).getName());
Assert.assertEquals(1, namespaces.size()); Assert.assertEquals(1, namespaces.size());
Assert.assertEquals(namespace, namespaces.get(0).getNamespaceName()); Assert.assertEquals("application", namespaces.get(0).getNamespaceName());
} }
} }
package com.ctrip.apollo.biz.service; package com.ctrip.apollo.biz.service;
import java.util.Date;
import java.util.List; import java.util.List;
import org.junit.Assert; import org.junit.Assert;
...@@ -30,10 +31,16 @@ public class PrivilegeServiceTest { ...@@ -30,10 +31,16 @@ public class PrivilegeServiceTest {
@Test @Test
public void testAddAndRemovePrivilege() { public void testAddAndRemovePrivilege() {
App newApp = adminService.createNewApp(String.valueOf(System.currentTimeMillis()), App app = new App();
"new app " + System.currentTimeMillis(), "owner " + System.currentTimeMillis(), app.setAppId(String.valueOf(System.currentTimeMillis()));
"owner " + System.currentTimeMillis() + "@ctrip.com", app.setName("new app " + System.currentTimeMillis());
"namespace " + System.currentTimeMillis()); String owner = "owner " + System.currentTimeMillis();
app.setOwnerName(owner);
app.setOwnerEmail("owner " + System.currentTimeMillis() + "@ctrip.com");
app.setDataChangeCreatedBy(owner);
app.setDataChangeLastModifiedBy(owner);
app.setDataChangeCreatedTime(new Date());
App newApp = adminService.createNewApp(app);
List<Cluster> clusters = viewService.findClusters(newApp.getAppId()); List<Cluster> clusters = viewService.findClusters(newApp.getAppId());
List<Namespace> namespaces = List<Namespace> namespaces =
...@@ -55,11 +62,16 @@ public class PrivilegeServiceTest { ...@@ -55,11 +62,16 @@ public class PrivilegeServiceTest {
@Test @Test
public void testCheckPrivilege() { public void testCheckPrivilege() {
App newApp = adminService.createNewApp(String.valueOf(System.currentTimeMillis()), App app = new App();
"new app " + System.currentTimeMillis(), "owner " + System.currentTimeMillis(), app.setAppId(String.valueOf(System.currentTimeMillis()));
"owner " + System.currentTimeMillis() + "@ctrip.com", app.setName("new app " + System.currentTimeMillis());
"namespace " + System.currentTimeMillis()); String owner = "owner " + System.currentTimeMillis();
app.setOwnerName(owner);
app.setOwnerEmail("owner " + System.currentTimeMillis() + "@ctrip.com");
app.setDataChangeCreatedBy(owner);
app.setDataChangeLastModifiedBy(owner);
app.setDataChangeCreatedTime(new Date());
App newApp = adminService.createNewApp(app);
List<Cluster> clusters = viewService.findClusters(newApp.getAppId()); List<Cluster> clusters = viewService.findClusters(newApp.getAppId());
List<Namespace> namespaces = List<Namespace> namespaces =
viewService.findNamespaces(newApp.getAppId(), clusters.get(0).getName()); viewService.findNamespaces(newApp.getAppId(), clusters.get(0).getName());
......
package com.ctrip.apollo.core.dto; package com.ctrip.apollo.core.dto;
import java.util.Date;
public class AppDTO { public class AppDTO {
private long id; private long id;
...@@ -12,6 +14,12 @@ public class AppDTO { ...@@ -12,6 +14,12 @@ public class AppDTO {
private String ownerEmail; private String ownerEmail;
private String dataChangeCreatedBy;
private Date dataChangeCreatedTime;
private String dataChangeLastModifiedBy;
public String getAppId() { public String getAppId() {
return appId; return appId;
} }
...@@ -51,4 +59,28 @@ public class AppDTO { ...@@ -51,4 +59,28 @@ public class AppDTO {
public void setOwnerName(String ownerName) { public void setOwnerName(String ownerName) {
this.ownerName = ownerName; this.ownerName = ownerName;
} }
public String getDataChangeCreatedBy() {
return dataChangeCreatedBy;
}
public void setDataChangeCreatedBy(String dataChangeCreatedBy) {
this.dataChangeCreatedBy = dataChangeCreatedBy;
}
public Date getDataChangeCreatedTime() {
return dataChangeCreatedTime;
}
public void setDataChangeCreatedTime(Date dataChangeCreatedTime) {
this.dataChangeCreatedTime = dataChangeCreatedTime;
}
public String getDataChangeLastModifiedBy() {
return dataChangeLastModifiedBy;
}
public void setDataChangeLastModifiedBy(String dataChangeLastModifiedBy) {
this.dataChangeLastModifiedBy = dataChangeLastModifiedBy;
}
} }
...@@ -16,6 +16,10 @@ public class ItemDTO{ ...@@ -16,6 +16,10 @@ public class ItemDTO{
private int lineNum; private int lineNum;
private String dataChangeCreatedBy;
private Date dataChangeCreatedTime;
private String dataChangeLastModifiedBy; private String dataChangeLastModifiedBy;
private Date dataChangeLastModifiedTime; private Date dataChangeLastModifiedTime;
...@@ -93,6 +97,22 @@ public class ItemDTO{ ...@@ -93,6 +97,22 @@ public class ItemDTO{
this.dataChangeLastModifiedTime = dataChangeLastModifiedTime; this.dataChangeLastModifiedTime = dataChangeLastModifiedTime;
} }
public String getDataChangeCreatedBy() {
return dataChangeCreatedBy;
}
public void setDataChangeCreatedBy(String dataChangeCreatedBy) {
this.dataChangeCreatedBy = dataChangeCreatedBy;
}
public Date getDataChangeCreatedTime() {
return dataChangeCreatedTime;
}
public void setDataChangeCreatedTime(Date dataChangeCreatedTime) {
this.dataChangeCreatedTime = dataChangeCreatedTime;
}
@Override @Override
public String toString() { public String toString() {
return "ItemDTO{" + return "ItemDTO{" +
...@@ -102,9 +122,10 @@ public class ItemDTO{ ...@@ -102,9 +122,10 @@ public class ItemDTO{
", value='" + value + '\'' + ", value='" + value + '\'' +
", comment='" + comment + '\'' + ", comment='" + comment + '\'' +
", lineNum=" + lineNum + ", lineNum=" + lineNum +
", dataChangeCreatedBy='" + dataChangeCreatedBy + '\'' +
", dataChangeCreatedTime=" + dataChangeCreatedTime +
", dataChangeLastModifiedBy='" + dataChangeLastModifiedBy + '\'' + ", dataChangeLastModifiedBy='" + dataChangeLastModifiedBy + '\'' +
", dataChangeLastModifiedTime=" + dataChangeLastModifiedTime + ", dataChangeLastModifiedTime=" + dataChangeLastModifiedTime +
'}'; '}';
} }
} }
...@@ -12,9 +12,13 @@ import com.ctrip.apollo.core.utils.StringUtils; ...@@ -12,9 +12,13 @@ import com.ctrip.apollo.core.utils.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
...@@ -22,6 +26,7 @@ import java.util.List; ...@@ -22,6 +26,7 @@ import java.util.List;
@Service @Service
public class AdminServiceAPI { public class AdminServiceAPI {
private static final Logger logger = LoggerFactory.getLogger(AdminServiceAPI.class); private static final Logger logger = LoggerFactory.getLogger(AdminServiceAPI.class);
@Service @Service
...@@ -32,6 +37,10 @@ public class AdminServiceAPI { ...@@ -32,6 +37,10 @@ public class AdminServiceAPI {
public List<AppDTO> getApps(Apollo.Env env) { public List<AppDTO> getApps(Apollo.Env env) {
return Arrays.asList(restTemplate.getForObject(getAdminServiceHost(env) + APP_API, AppDTO[].class)); return Arrays.asList(restTemplate.getForObject(getAdminServiceHost(env) + APP_API, AppDTO[].class));
} }
public AppDTO save(Apollo.Env env, AppDTO app) {
return restTemplate.postForEntity(getAdminServiceHost(env) + APP_API, app, AppDTO.class).getBody();
}
} }
...@@ -76,11 +85,12 @@ public class AdminServiceAPI { ...@@ -76,11 +85,12 @@ public class AdminServiceAPI {
public void updateItems(String appId, Apollo.Env env, String clusterName, String namespace, public void updateItems(String appId, Apollo.Env env, String clusterName, String namespace,
ItemChangeSets changeSets) { ItemChangeSets changeSets) {
if (StringUtils.isContainEmpty(appId, clusterName, namespace)){ if (StringUtils.isContainEmpty(appId, clusterName, namespace)) {
return; return;
} }
restTemplate.postForEntity(getAdminServiceHost(env) + String.format("apps/%s/clusters/%s/namespaces/%s/itemset", restTemplate.postForEntity(getAdminServiceHost(env) + String.format("apps/%s/clusters/%s/namespaces/%s/itemset",
appId,clusterName, namespace), changeSets, Void.class); appId, clusterName, namespace), changeSets,
Void.class);
} }
...@@ -107,14 +117,33 @@ public class AdminServiceAPI { ...@@ -107,14 +117,33 @@ public class AdminServiceAPI {
if (StringUtils.isContainEmpty(appId, clusterName, namespace)) { if (StringUtils.isContainEmpty(appId, clusterName, namespace)) {
return null; return null;
} }
try { try {
ReleaseDTO releaseDTO = restTemplate.getForObject(getAdminServiceHost(env) + String ReleaseDTO releaseDTO = restTemplate.getForObject(getAdminServiceHost(env) + String
.format("apps/%s/clusters/%s/namespaces/%s/releases/latest", appId, .format("apps/%s/clusters/%s/namespaces/%s/releases/latest", appId,
clusterName, namespace), ReleaseDTO.class); clusterName, namespace), ReleaseDTO.class);
return releaseDTO; return releaseDTO;
}catch (HttpClientErrorException e){ } catch (HttpClientErrorException e) {
logger.warn(" call [ReleaseAPI.loadLatestRelease] and return not fount exception.app id:{}, env:{}, clusterName:{}, namespace:{}", logger.warn(
appId, env, clusterName, namespace); " call [ReleaseAPI.loadLatestRelease] and return not fount exception.app id:{}, env:{}, clusterName:{}, namespace:{}",
appId, env, clusterName, namespace);
return null;
}
}
public ReleaseDTO release(String appId, Apollo.Env env, String clusterName, String namespace, String releaseBy,
String comment) {
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<String, String>();
parameters.add("name", releaseBy);
parameters.add("comment", comment);
HttpEntity<MultiValueMap<String, String>> entity =
new HttpEntity<MultiValueMap<String, String>>(parameters, null);
ResponseEntity<ReleaseDTO> response = restTemplate.postForEntity(getAdminServiceHost(env) + String.
format("apps/%s/clusters/%s/namespaces/%s/releases", appId, clusterName, namespace),
entity, ReleaseDTO.class);
if (response.getStatusCode() == HttpStatus.OK){
return response.getBody();
}else {
logger.error("release fail.id:{}, env:{}, clusterName:{}, namespace:{},releaseBy{}",appId, env, clusterName, namespace, releaseBy);
return null; return null;
} }
} }
......
...@@ -2,12 +2,18 @@ package com.ctrip.apollo.portal.controller; ...@@ -2,12 +2,18 @@ package com.ctrip.apollo.portal.controller;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.ctrip.apollo.core.dto.AppDTO;
import com.ctrip.apollo.core.utils.StringUtils;
import com.ctrip.apollo.portal.entity.ClusterNavTree; import com.ctrip.apollo.portal.entity.ClusterNavTree;
import com.ctrip.apollo.portal.service.AppService; import com.ctrip.apollo.portal.service.AppService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable; 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.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@RestController @RestController
...@@ -26,6 +32,23 @@ public class AppController { ...@@ -26,6 +32,23 @@ public class AppController {
return appService.buildClusterNavTree(appId); return appService.buildClusterNavTree(appId);
} }
@RequestMapping(value = "", method = RequestMethod.POST, consumes = {"application/json"})
public ResponseEntity<AppDTO> create(@RequestBody AppDTO app) {
if (isInvalidApp(app)){
return ResponseEntity.badRequest().body(null);
}
AppDTO createdApp = appService.save(app);
if (createdApp != null){
return ResponseEntity.ok().body(createdApp);
}else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
}
}
private boolean isInvalidApp(AppDTO app) {
return StringUtils.isContainEmpty(app.getName(), app.getAppId(), app.getOwnerEmail(), app.getOwnerName());
}
} }
......
...@@ -2,15 +2,20 @@ package com.ctrip.apollo.portal.controller; ...@@ -2,15 +2,20 @@ package com.ctrip.apollo.portal.controller;
import com.ctrip.apollo.Apollo; import com.ctrip.apollo.Apollo;
import com.ctrip.apollo.core.dto.ReleaseDTO;
import com.ctrip.apollo.core.utils.StringUtils; import com.ctrip.apollo.core.utils.StringUtils;
import com.ctrip.apollo.portal.entity.form.NamespaceModifyModel;
import com.ctrip.apollo.portal.entity.NamespaceVO; import com.ctrip.apollo.portal.entity.NamespaceVO;
import com.ctrip.apollo.portal.entity.SimpleMsg; import com.ctrip.apollo.portal.entity.SimpleMsg;
import com.ctrip.apollo.portal.entity.form.NamespaceReleaseModel;
import com.ctrip.apollo.portal.service.ConfigService; import com.ctrip.apollo.portal.service.ConfigService;
import com.ctrip.apollo.portal.service.txtresolver.TextResolverResult; import com.ctrip.apollo.portal.service.txtresolver.TextResolverResult;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
...@@ -34,13 +39,22 @@ public class ConfigController { ...@@ -34,13 +39,22 @@ public class ConfigController {
return configService.findNampspaces(appId, Apollo.Env.valueOf(env), clusterName); return configService.findNampspaces(appId, Apollo.Env.valueOf(env), clusterName);
} }
@RequestMapping(value = "/apps/{appId}/env/{env}/clusters/{clusterName}/namespaces/{namespaceName}/modify", method = RequestMethod.GET) @RequestMapping(value = "/apps/{appId}/env/{env}/clusters/{clusterName}/namespaces/{namespaceName}/items",method = RequestMethod.PUT, consumes = {"application/json"})
public ResponseEntity<SimpleMsg> modifyConfigs(@PathVariable String appId, @PathVariable String env, public ResponseEntity<SimpleMsg> modifyItems(@PathVariable String appId, @PathVariable String env,
@PathVariable String clusterName, @PathVariable String clusterName, @PathVariable String namespaceName,
@PathVariable String namespaceName, @RequestBody NamespaceModifyModel model) {
String configText) {
TextResolverResult result = model.setAppId(appId);
configService.resolve(appId, Apollo.Env.valueOf(env), clusterName, namespaceName, configText); model.setClusterName(clusterName);
model.setEnv(env);
model.setNamespaceName(namespaceName);
if (model == null || model.isInvalid()){
return ResponseEntity.badRequest().body(new SimpleMsg("form data exception."));
}
TextResolverResult result = configService.resolveConfigText(model);
if (result.isResolveSuccess()) { if (result.isResolveSuccess()) {
return ResponseEntity.ok().body(new SimpleMsg("success")); return ResponseEntity.ok().body(new SimpleMsg("success"));
} else { } else {
...@@ -48,4 +62,26 @@ public class ConfigController { ...@@ -48,4 +62,26 @@ public class ConfigController {
} }
} }
@RequestMapping(value = "/apps/{appId}/env/{env}/clusters/{clusterName}/namespaces/{namespaceName}/release", method = RequestMethod.POST, consumes = {"application/json"})
public ResponseEntity<SimpleMsg> createRelease(@PathVariable String appId, @PathVariable String env,
@PathVariable String clusterName, @PathVariable String namespaceName,
@RequestBody NamespaceReleaseModel model){
model.setAppId(appId);
model.setClusterName(clusterName);
model.setEnv(env);
model.setNamespaceName(namespaceName);
if (model == null || model.isInvalid()){
return ResponseEntity.badRequest().body(new SimpleMsg("form data exception."));
}
ReleaseDTO release = configService.release(model);
if (release == null){
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new SimpleMsg("oops! some error in server."));
}else {
return ResponseEntity.ok().body(new SimpleMsg("success"));
}
}
} }
package com.ctrip.apollo.portal.entity.form;
public interface FormModel {
boolean isInvalid();
}
package com.ctrip.apollo.portal.entity.form;
import com.ctrip.apollo.Apollo;
import com.ctrip.apollo.core.utils.StringUtils;
public class NamespaceModifyModel implements FormModel{
private String appId;
private String env;
private String clusterName;
private String namespaceName;
private int namespaceId;
private String configText;
private String modifyBy;
@Override
public boolean isInvalid(){
return StringUtils.isContainEmpty(appId, env, clusterName, namespaceName, configText, modifyBy) || namespaceId <= 0;
}
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public Apollo.Env getEnv() {
return Apollo.Env.valueOf(env);
}
public void setEnv(String env) {
this.env = env;
}
public String getClusterName() {
return clusterName;
}
public void setClusterName(String clusterName) {
this.clusterName = clusterName;
}
public String getNamespaceName() {
return namespaceName;
}
public void setNamespaceName(String namespaceName) {
this.namespaceName = namespaceName;
}
public int getNamespaceId() {
return namespaceId;
}
public void setNamespaceId(int namespaceId) {
this.namespaceId = namespaceId;
}
public String getConfigText() {
return configText;
}
public void setConfigText(String configText) {
this.configText = configText;
}
public String getModifyBy() {
return modifyBy;
}
public void setModifyBy(String modifyBy) {
this.modifyBy = modifyBy;
}
}
package com.ctrip.apollo.portal.entity.form;
import com.ctrip.apollo.Apollo;
import com.ctrip.apollo.core.utils.StringUtils;
public class NamespaceReleaseModel implements FormModel{
private String appId;
private String env;
private String clusterName;
private String namespaceName;
private String releaseBy;
private String releaseComment;
@Override
public boolean isInvalid() {
return StringUtils.isContainEmpty(appId, env, clusterName, namespaceName, releaseBy);
}
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public Apollo.Env getEnv() {
return Apollo.Env.valueOf(env);
}
public void setEnv(String env) {
this.env = env;
}
public String getClusterName() {
return clusterName;
}
public void setClusterName(String clusterName) {
this.clusterName = clusterName;
}
public String getNamespaceName() {
return namespaceName;
}
public void setNamespaceName(String namespaceName) {
this.namespaceName = namespaceName;
}
public String getReleaseBy() {
return releaseBy;
}
public void setReleaseBy(String releaseBy) {
this.releaseBy = releaseBy;
}
public String getReleaseComment() {
return releaseComment;
}
public void setReleaseComment(String releaseComment) {
this.releaseComment = releaseComment;
}
}
package com.ctrip.apollo.portal.service; package com.ctrip.apollo.portal.service;
import java.util.Date;
import java.util.List; import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.ctrip.apollo.Apollo.Env; import com.ctrip.apollo.Apollo.Env;
import com.ctrip.apollo.core.dto.AppDTO;
import com.ctrip.apollo.core.utils.StringUtils;
import com.ctrip.apollo.portal.PortalSettings; import com.ctrip.apollo.portal.PortalSettings;
import com.ctrip.apollo.portal.api.AdminServiceAPI;
import com.ctrip.apollo.portal.entity.ClusterNavTree; import com.ctrip.apollo.portal.entity.ClusterNavTree;
import com.ctrip.apollo.portal.entity.SimpleMsg;
@Service @Service
public class AppService { public class AppService {
private Logger logger = LoggerFactory.getLogger(AppService.class);
@Autowired @Autowired
private ClusterService clusterService; private ClusterService clusterService;
@Autowired @Autowired
private PortalSettings portalSettings; private PortalSettings portalSettings;
@Autowired
private AdminServiceAPI.AppAPI appAPI;
public ClusterNavTree buildClusterNavTree(String appId) { public ClusterNavTree buildClusterNavTree(String appId) {
ClusterNavTree tree = new ClusterNavTree(); ClusterNavTree tree = new ClusterNavTree();
...@@ -28,12 +40,20 @@ public class AppService { ...@@ -28,12 +40,20 @@ public class AppService {
clusterNode.setClusters(clusterService.findClusters(env, appId)); clusterNode.setClusters(clusterService.findClusters(env, appId));
tree.addNode(clusterNode); tree.addNode(clusterNode);
} }
// ClusterNavTree.Node uatNode = new ClusterNavTree.Node(Apollo.Env.UAT);
// List<ClusterDTO> uatClusters = new LinkedList<>();
// uatClusters.add(defaultCluster);
// uatNode.setClusters(uatClusters);
// tree.addNode(uatNode);
return tree; return tree;
} }
public AppDTO save(AppDTO app) {
String createBy = app.getOwnerName();
try {
app.setDataChangeCreatedBy(createBy);
app.setDataChangeCreatedTime(new Date());
app.setDataChangeLastModifiedBy(createBy);
return appAPI.save(Env.LOCAL, app);
} catch (Exception e) {
logger.error("oops! save app error. app id:{}", app.getAppId(), e);
return null;
}
}
} }
...@@ -11,14 +11,18 @@ import com.ctrip.apollo.core.dto.ItemChangeSets; ...@@ -11,14 +11,18 @@ import com.ctrip.apollo.core.dto.ItemChangeSets;
import com.ctrip.apollo.core.dto.ItemDTO; import com.ctrip.apollo.core.dto.ItemDTO;
import com.ctrip.apollo.core.dto.NamespaceDTO; import com.ctrip.apollo.core.dto.NamespaceDTO;
import com.ctrip.apollo.core.dto.ReleaseDTO; import com.ctrip.apollo.core.dto.ReleaseDTO;
import com.ctrip.apollo.core.utils.StringUtils;
import com.ctrip.apollo.portal.api.AdminServiceAPI; import com.ctrip.apollo.portal.api.AdminServiceAPI;
import com.ctrip.apollo.portal.entity.form.NamespaceModifyModel;
import com.ctrip.apollo.portal.entity.NamespaceVO; import com.ctrip.apollo.portal.entity.NamespaceVO;
import com.ctrip.apollo.portal.entity.form.NamespaceReleaseModel;
import com.ctrip.apollo.portal.service.txtresolver.ConfigTextResolver; import com.ctrip.apollo.portal.service.txtresolver.ConfigTextResolver;
import com.ctrip.apollo.portal.service.txtresolver.TextResolverResult; import com.ctrip.apollo.portal.service.txtresolver.TextResolverResult;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
...@@ -41,6 +45,13 @@ public class ConfigService { ...@@ -41,6 +45,13 @@ public class ConfigService {
private ObjectMapper objectMapper = new ObjectMapper(); private ObjectMapper objectMapper = new ObjectMapper();
/**
* load cluster all namespace info with items
* @param appId
* @param env
* @param clusterName
* @return
*/
public List<NamespaceVO> findNampspaces(String appId, Apollo.Env env, String clusterName) { public List<NamespaceVO> findNampspaces(String appId, Apollo.Env env, String clusterName) {
List<NamespaceDTO> namespaces = groupAPI.findGroupsByAppAndCluster(appId, env, clusterName); List<NamespaceDTO> namespaces = groupAPI.findGroupsByAppAndCluster(appId, env, clusterName);
...@@ -65,39 +76,6 @@ public class ConfigService { ...@@ -65,39 +76,6 @@ public class ConfigService {
return namespaceVOs; return namespaceVOs;
} }
public TextResolverResult resolve(String appId, Apollo.Env env, String clusterName, String namespaceName,
String configText) {
TextResolverResult result = new TextResolverResult();
try {
result = resolver.resolve(configText, itemAPI.findItems(appId, env, clusterName, namespaceName));
} catch (Exception e) {
logger
.error("resolve config text error. app id:{}, env:{}, clusterName:{}, namespace:{}", appId, env, clusterName,
namespaceName, e);
result.setResolveSuccess(false);
result.setMsg("oops! server resolve config text error.");
return result;
}
if (result.isResolveSuccess()) {
try {
// TODO: 16/4/13
result.getChangeSets().setModifyBy("lepdou");
itemAPI.updateItems(appId, env, clusterName, namespaceName, result.getChangeSets());
} catch (Exception e) {
logger.error("resolve config text error. app id:{}, env:{}, clusterName:{}, namespace:{}", appId, env,
clusterName, namespaceName, e);
result.setResolveSuccess(false);
result.setMsg("oops! server update config error.");
return result;
}
} else {
logger.warn("resolve config text error by format error. app id:{}, env:{}, clusterName:{}, namespace:{},cause:{}",
appId,env, clusterName, namespaceName, result.getMsg());
}
return result;
}
private NamespaceVO parseNamespace(String appId, Apollo.Env env, String clusterName, NamespaceDTO namespace) { private NamespaceVO parseNamespace(String appId, Apollo.Env env, String clusterName, NamespaceDTO namespace) {
NamespaceVO namespaceVO = new NamespaceVO(); NamespaceVO namespaceVO = new NamespaceVO();
...@@ -140,16 +118,75 @@ public class ConfigService { ...@@ -140,16 +118,75 @@ public class ConfigService {
} }
private NamespaceVO.ItemVO parseItemVO(ItemDTO itemDTO, Map<String, String> releaseItems) { private NamespaceVO.ItemVO parseItemVO(ItemDTO itemDTO, Map<String, String> releaseItems) {
String key = itemDTO.getKey();
NamespaceVO.ItemVO itemVO = new NamespaceVO.ItemVO(); NamespaceVO.ItemVO itemVO = new NamespaceVO.ItemVO();
itemVO.setItem(itemDTO); itemVO.setItem(itemDTO);
String key = itemDTO.getKey();
String newValue = itemDTO.getValue(); String newValue = itemDTO.getValue();
String oldValue = releaseItems.get(key); String oldValue = releaseItems.get(key);
if (oldValue == null || !newValue.equals(oldValue)) { if (!StringUtils.isEmpty(key) && (oldValue == null || !newValue.equals(oldValue))) {
itemVO.setModified(true); itemVO.setModified(true);
itemVO.setOldValue(oldValue == null ? "" : oldValue); itemVO.setOldValue(oldValue == null ? "" : oldValue);
itemVO.setNewValue(newValue); itemVO.setNewValue(newValue);
} }
return itemVO; return itemVO;
} }
/**
* parse config text and update config items
* @return parse result
*/
public TextResolverResult resolveConfigText(NamespaceModifyModel model) {
String appId = model.getAppId();
Apollo.Env env = model.getEnv();
String clusterName = model.getClusterName();
String namespaceName = model.getNamespaceName();
long namespaceId = model.getNamespaceId();
String configText = model.getConfigText();
TextResolverResult result = new TextResolverResult();
try {
result = resolver.resolve( namespaceId, configText, itemAPI.findItems(appId, env, clusterName, namespaceName));
} catch (Exception e) {
logger
.error("resolve config text error. app id:{}, env:{}, clusterName:{}, namespace:{}", appId, env, clusterName,
namespaceName, e);
result.setResolveSuccess(false);
result.setMsg("oops! server resolveConfigText config text error.");
return result;
}
if (result.isResolveSuccess()) {
try {
ItemChangeSets changeSets = result.getChangeSets();
changeSets.setModifyBy(model.getModifyBy());
enrichChangeSetBaseInfo(changeSets);
itemAPI.updateItems(appId, env, clusterName, namespaceName, changeSets);
} catch (Exception e) {
logger.error("resolve config text error. app id:{}, env:{}, clusterName:{}, namespace:{}", appId, env,
clusterName, namespaceName, e);
result.setResolveSuccess(false);
result.setMsg("oops! server update config error.");
return result;
}
} else {
logger.warn("resolve config text error by format error. app id:{}, env:{}, clusterName:{}, namespace:{},cause:{}",
appId,env, clusterName, namespaceName, result.getMsg());
}
return result;
}
private void enrichChangeSetBaseInfo(ItemChangeSets changeSets){
for (ItemDTO item: changeSets.getCreateItems()){
item.setDataChangeCreatedTime(new Date());
}
}
/**
* release config items
* @return
*/
public ReleaseDTO release(NamespaceReleaseModel model){
return releaseAPI.release(model.getAppId(), model.getEnv(), model.getClusterName(), model.getNamespaceName(),
model.getReleaseBy(), model.getReleaseComment());
}
} }
...@@ -5,10 +5,10 @@ import com.ctrip.apollo.core.dto.ItemDTO; ...@@ -5,10 +5,10 @@ import com.ctrip.apollo.core.dto.ItemDTO;
import java.util.List; import java.util.List;
/** /**
* users can modify config in text mode.so need resolve text. * users can modify config in text mode.so need resolveConfigText text.
*/ */
public interface ConfigTextResolver { public interface ConfigTextResolver {
TextResolverResult resolve(String configText, List<ItemDTO> baseItems); TextResolverResult resolve(long namespaceId, String configText, List<ItemDTO> baseItems);
} }
...@@ -8,8 +8,10 @@ import com.ctrip.apollo.portal.util.BeanUtils; ...@@ -8,8 +8,10 @@ import com.ctrip.apollo.portal.util.BeanUtils;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
/** /**
* normal property file resolver. * normal property file resolver.
...@@ -23,7 +25,7 @@ public class PropertyResolver implements ConfigTextResolver { ...@@ -23,7 +25,7 @@ public class PropertyResolver implements ConfigTextResolver {
private static final String ITEM_SEPARATOR = "\n"; private static final String ITEM_SEPARATOR = "\n";
@Override @Override
public TextResolverResult resolve(String configText, List<ItemDTO> baseItems) { public TextResolverResult resolve(long namespaceId, String configText, List<ItemDTO> baseItems) {
TextResolverResult result = new TextResolverResult(); TextResolverResult result = new TextResolverResult();
if (StringUtils.isEmpty(configText)){ if (StringUtils.isEmpty(configText)){
...@@ -40,6 +42,12 @@ public class PropertyResolver implements ConfigTextResolver { ...@@ -40,6 +42,12 @@ public class PropertyResolver implements ConfigTextResolver {
String[] newItems = configText.split(ITEM_SEPARATOR); String[] newItems = configText.split(ITEM_SEPARATOR);
if (isHasRepeatKey(newItems)){
result.setResolveSuccess(false);
result.setMsg("config text has repeat key please check.");
return result;
}
ItemChangeSets changeSets = new ItemChangeSets(); ItemChangeSets changeSets = new ItemChangeSets();
result.setChangeSets(changeSets); result.setChangeSets(changeSets);
Map<Integer, String> newLineNumMapItem = new HashMap();//use for delete blank and comment item Map<Integer, String> newLineNumMapItem = new HashMap();//use for delete blank and comment item
...@@ -52,16 +60,16 @@ public class PropertyResolver implements ConfigTextResolver { ...@@ -52,16 +60,16 @@ public class PropertyResolver implements ConfigTextResolver {
//comment item //comment item
if (isCommentItem(newItem)) { if (isCommentItem(newItem)) {
handleCommentLine(oldItemByLine, newItem, lineCounter, changeSets); handleCommentLine(namespaceId, oldItemByLine, newItem, lineCounter, changeSets);
//blank item //blank item
} else if (isBlankItem(newItem)) { } else if (isBlankItem(newItem)) {
handleBlankLine(oldItemByLine, lineCounter, changeSets); handleBlankLine(namespaceId, oldItemByLine, lineCounter, changeSets);
//normal line //normal item
} else { } else {
if (!handleNormalLine(oldKeyMapItem, newItem, lineCounter, result)) { if (!handleNormalLine(namespaceId, oldKeyMapItem, newItem, lineCounter, result)) {
return result; return result;
} }
} }
...@@ -77,42 +85,68 @@ public class PropertyResolver implements ConfigTextResolver { ...@@ -77,42 +85,68 @@ public class PropertyResolver implements ConfigTextResolver {
return result; return result;
} }
private void handleCommentLine(ItemDTO oldItemByLine, String newItem, int lineCounter, ItemChangeSets changeSets) { private boolean isHasRepeatKey(String[] newItems){
Set<String> keys = new HashSet<>();
int keyCount = 0;
for (String item: newItems){
if (!isCommentItem(item) && !isBlankItem(item)){
keyCount ++;
keys.add(parseKeyValueFromItem(item)[0]);
}
}
return keyCount > keys.size();
}
private String[] parseKeyValueFromItem(String item){
int kvSeparator = item.indexOf(KV_SEPARATOR);
if (kvSeparator == -1) {
return null;
}
String[] kv = new String[2];
kv[0] = item.substring(0, kvSeparator).trim();
kv[1] = item.substring(kvSeparator + 1, item.length()).trim();
return kv;
}
private void handleCommentLine(Long namespaceId, ItemDTO oldItemByLine, String newItem, int lineCounter, ItemChangeSets changeSets) {
String oldComment = oldItemByLine == null ? "" : oldItemByLine.getComment(); String oldComment = oldItemByLine == null ? "" : oldItemByLine.getComment();
//create comment. implement update comment by delete old comment and create new comment //create comment. implement update comment by delete old comment and create new comment
if (!(isCommentItem(oldItemByLine) && newItem.equals(oldComment))) { if (!(isCommentItem(oldItemByLine) && newItem.equals(oldComment))) {
changeSets.addCreateItem(buildCommentItem(0l, newItem, lineCounter)); changeSets.addCreateItem(buildCommentItem(0l, namespaceId, newItem, lineCounter));
} }
} }
private void handleBlankLine(ItemDTO oldItem, int lineCounter, ItemChangeSets changeSets) { private void handleBlankLine(Long namespaceId, ItemDTO oldItem, int lineCounter, ItemChangeSets changeSets) {
if (!isBlankItem(oldItem)) { if (!isBlankItem(oldItem)) {
changeSets.addCreateItem(buildBlankItem(0l, lineCounter)); changeSets.addCreateItem(buildBlankItem(0l, namespaceId, lineCounter));
} }
} }
private boolean handleNormalLine(Map<String, ItemDTO> keyMapOldItem, String newItem, private boolean handleNormalLine(Long namespaceId, Map<String, ItemDTO> keyMapOldItem, String newItem,
int lineCounter, TextResolverResult result) { int lineCounter, TextResolverResult result) {
ItemChangeSets changeSets = result.getChangeSets(); ItemChangeSets changeSets = result.getChangeSets();
int kvSeparator = newItem.indexOf(KV_SEPARATOR); String[] kv = parseKeyValueFromItem(newItem);
if (kvSeparator == -1) {
if (kv == null) {
result.setResolveSuccess(false); result.setResolveSuccess(false);
result.setMsg(" line:" + lineCounter + " key value must separate by '='"); result.setMsg(" line:" + lineCounter + " key value must separate by '='");
return false; return false;
} }
String newKey = newItem.substring(0, kvSeparator).trim(); String newKey = kv[0];
String newValue = newItem.substring(kvSeparator + 1, newItem.length()).trim(); String newValue = kv[1];
ItemDTO oldItem = keyMapOldItem.get(newKey); ItemDTO oldItem = keyMapOldItem.get(newKey);
if (oldItem == null) {//new item if (oldItem == null) {//new item
changeSets.addCreateItem(buildNormalItem(0l, newKey, newValue, "", lineCounter)); changeSets.addCreateItem(buildNormalItem(0l, namespaceId, newKey, newValue, "", lineCounter));
} else if (!newValue.equals(oldItem.getValue())){//update item } else if (!newValue.equals(oldItem.getValue()) || lineCounter != oldItem.getLineNum()){//update item
changeSets.addUpdateItem( changeSets.addUpdateItem(
buildNormalItem(oldItem.getId(), newKey, newValue, oldItem.getComment(), buildNormalItem(oldItem.getId(), namespaceId, newKey, newValue, oldItem.getComment(),
lineCounter)); lineCounter));
} }
keyMapOldItem.remove(newKey); keyMapOldItem.remove(newKey);
...@@ -154,23 +188,24 @@ public class PropertyResolver implements ConfigTextResolver { ...@@ -154,23 +188,24 @@ public class PropertyResolver implements ConfigTextResolver {
//1. old is blank by now is not //1. old is blank by now is not
//2.old is comment by now is not exist or modified //2.old is comment by now is not exist or modified
if ((isBlankItem(oldItem) && !isBlankItem(newItem)) if ((isBlankItem(oldItem) && !isBlankItem(newItem))
|| isCommentItem(oldItem) && (newItem == null || !newItem.equals(oldItem))) { || isCommentItem(oldItem) && (newItem == null || !newItem.equals(oldItem.getComment()))) {
changeSets.addDeleteItem(oldItem); changeSets.addDeleteItem(oldItem);
} }
} }
} }
private ItemDTO buildCommentItem(Long id, String comment, int lineNum) { private ItemDTO buildCommentItem(Long id, Long namespaceId, String comment, int lineNum) {
return buildNormalItem(id, "", "", comment, lineNum); return buildNormalItem(id, namespaceId, "", "", comment, lineNum);
} }
private ItemDTO buildBlankItem(Long id, int lineNum) { private ItemDTO buildBlankItem(Long id, Long namespaceId, int lineNum) {
return buildNormalItem(id, "", "", "", lineNum); return buildNormalItem(id, namespaceId, "", "", "", lineNum);
} }
private ItemDTO buildNormalItem(Long id, String key, String value, String comment, int lineNum) { private ItemDTO buildNormalItem(Long id, Long namespaceId, String key, String value, String comment, int lineNum) {
ItemDTO item = new ItemDTO(); ItemDTO item = new ItemDTO();
item.setId(id); item.setId(id);
item.setNamespaceId(namespaceId);
item.setKey(key); item.setKey(key);
item.setValue(value); item.setValue(value);
item.setComment(comment); item.setComment(comment);
......
...@@ -15,4 +15,4 @@ ctrip: ...@@ -15,4 +15,4 @@ ctrip:
apollo: apollo:
portal: portal:
env: local,dev env: local
...@@ -5,9 +5,8 @@ create_app_module.controller('CreateAppController', ['$scope', '$window', 'toast ...@@ -5,9 +5,8 @@ create_app_module.controller('CreateAppController', ['$scope', '$window', 'toast
$scope.app = { $scope.app = {
appId: 1001, appId: 1001,
name: 'lepdou', name: 'lepdou',
ownerPhone: '1111', ownerEmail: 'qqq@qq.com',
ownerMail: 'qqq@qq.com', ownerName: 'le'
owner: 'le'
}; };
$scope.save = function () { $scope.save = function () {
...@@ -17,7 +16,11 @@ create_app_module.controller('CreateAppController', ['$scope', '$window', 'toast ...@@ -17,7 +16,11 @@ create_app_module.controller('CreateAppController', ['$scope', '$window', 'toast
$window.location.href = '/views/app.html?#appid=' + result.appId; $window.location.href = '/views/app.html?#appid=' + result.appId;
}, 1000); }, 1000);
}, function (result) { }, function (result) {
toastr.error('添加失败!'); if (result.status == 400){
toastr.error('params error','添加失败!');
}else {
toastr.error('server error','添加失败!');
}
}); });
}; };
......
...@@ -3,29 +3,34 @@ application_module.controller("AppConfigController", ...@@ -3,29 +3,34 @@ application_module.controller("AppConfigController",
function ($scope, $location, toastr, AppService, ConfigService) { function ($scope, $location, toastr, AppService, ConfigService) {
var appId = $location.$$url.split("=")[1]; var appId = $location.$$url.split("=")[1];
var currentUser = 'lepdou';
var pageContext = { var pageContext = {
appId: appId, appId: appId,
env: 'LOCAL', env: 'LOCAL',
clusterName: 'default' clusterName: 'default'
}; };
$scope.pageEnv = pageContext; $scope.pageContext = pageContext;
/////////////
///////////// load cluster nav tree /////////
AppService.load_nav_tree($scope.pageEnv.appId).then(function (result) { AppService.load_nav_tree($scope.pageContext.appId).then(function (result) {
var navTree = []; var navTree = [];
var nodes = result.nodes; var nodes = result.nodes;
nodes.forEach(function (item) { nodes.forEach(function (item) {
var node = {}; var node = {};
//first nav //first nav
node.text = item.env; node.text = item.env;
node.selectable = false;
//second nav //second nav
var clusterNodes = []; var clusterNodes = [];
item.clusters.forEach(function (item) { item.clusters.forEach(function (item) {
var clusterNode = {}; var clusterNode = {},
parentNode = [];
clusterNode.text = item.name; clusterNode.text = item.name;
parentNode.push(node.text);
clusterNode.tags = parentNode;
clusterNodes.push(clusterNode); clusterNodes.push(clusterNode);
}); });
...@@ -36,33 +41,86 @@ application_module.controller("AppConfigController", ...@@ -36,33 +41,86 @@ application_module.controller("AppConfigController",
color: "#428bca", color: "#428bca",
showBorder: true, showBorder: true,
data: navTree, data: navTree,
levels: 99 levels: 99,
onNodeSelected: function (event, data) {
$scope.pageContext.env = data.tags[0];
$scope.pageContext.clusterName = data.text;
refreshNamespaces();
}
}); });
}, function (result) { }, function (result) {
toastr.error("加载导航出错:" + result); toastr.error("加载导航出错:" + result);
}); });
/////////// /////////// namespace ////////////
var namespace_view_type = {
TEXT:'text',
TABLE: 'table',
LOG: 'log'
};
refreshNamespaces();
function refreshNamespaces(viewType) {
ConfigService.load_all_namespaces($scope.pageContext.appId, $scope.pageContext.env,
$scope.pageContext.clusterName, viewType).then(
function (result) {
$scope.namespaces = result;
//初始化视图
if ($scope.namespaces) {
$scope.namespaces.forEach(function (item) {
item.isModify = false;
if (!viewType){//default text view
$scope.switchView(item, namespace_view_type.TEXT);
}else if (viewType == namespace_view_type.TABLE){
item.viewType = namespace_view_type.TABLE;
}
item.isTextEditing = false;
})
}
}, function (result) {
toastr.error("加载配置信息出错");
});
}
////////////global view oper /////////////
$scope.switchView = function (namespace, viewType) {
ConfigService.load_all_namespaces($scope.pageEnv.appId, $scope.pageEnv.env, if (namespace_view_type.TEXT == viewType) {
$scope.pageEnv.clusterName).then( namespace.text = parseModel2Text(namespace);
function (result) { } else if (namespace_view_type.TABLE == viewType) {
$scope.namespaces = result;
//初始化视图 }
if ($scope.namespaces) { namespace.viewType = viewType;
$scope.namespaces.forEach(function (item) { };
item.isModify = false;
item.viewType = 'table'; //把表格内容解析成文本
item.isTextEditing = false; function parseModel2Text(namespace) {
}) if (!namespace.items) {
return "无配置信息";
}
var result = "";
namespace.items.forEach(function (item) {
if (item.item.key) {
result +=
item.item.key + " = " + item.item.value + "\n";
} else {
result += item.item.comment + "\n";
} }
}, function (result) {
toastr.error("加载配置信息出错");
}); });
return result;
}
////////// text view oper /////////
$scope.draft = {}; $scope.draft = {};
//保存草稿 //保存草稿
$scope.saveDraft = function (namespace) { $scope.saveDraft = function (namespace) {
...@@ -71,12 +129,17 @@ application_module.controller("AppConfigController", ...@@ -71,12 +129,17 @@ application_module.controller("AppConfigController",
//更新配置 //更新配置
$scope.commitChange = function () { $scope.commitChange = function () {
ConfigService.modify_items($scope.pageEnv.appId, $scope.pageEnv.env, $scope.pageEnv.clusterName, ConfigService.modify_items($scope.pageContext.appId, $scope.pageContext.env, $scope.pageContext.clusterName,
$scope.draft.namespace.namespaceName, $scope.draft.text).then( $scope.draft.namespace.namespaceName, $scope.draft.text,
$scope.draft.namespace.id, 'lepdou').then(
function (result) { function (result) {
toastr.success("更新成功"); toastr.success("更新成功");
//refresh all namespace items
refreshNamespaces();
$scope.draft.backupText = '';//清空备份文本 $scope.draft.backupText = '';//清空备份文本
$scope.toggleTextEditStatus($scope.draft); $scope.toggleTextEditStatus($scope.draft);
}, function (result) { }, function (result) {
toastr.error(result.data.msg, "更新失败"); toastr.error(result.data.msg, "更新失败");
...@@ -84,7 +147,6 @@ application_module.controller("AppConfigController", ...@@ -84,7 +147,6 @@ application_module.controller("AppConfigController",
); );
}; };
/////////
//文本编辑框状态切换 //文本编辑框状态切换
$scope.toggleTextEditStatus = function (namespace) { $scope.toggleTextEditStatus = function (namespace) {
namespace.isTextEditing = !namespace.isTextEditing; namespace.isTextEditing = !namespace.isTextEditing;
...@@ -96,7 +158,10 @@ application_module.controller("AppConfigController", ...@@ -96,7 +158,10 @@ application_module.controller("AppConfigController",
} }
} }
}; };
////////// table view oper /////////
//查看旧值
$scope.queryOldValue = function (key, oldValue) { $scope.queryOldValue = function (key, oldValue) {
$scope.queryKey = key; $scope.queryKey = key;
if (oldValue == '') { if (oldValue == '') {
...@@ -105,40 +170,30 @@ application_module.controller("AppConfigController", ...@@ -105,40 +170,30 @@ application_module.controller("AppConfigController",
$scope.OldValue = oldValue; $scope.OldValue = oldValue;
} }
}; };
$scope.switchView = function (namespace, viewType) { /////// release ///////
var releaseNamespace = {};
if ('textarea' == viewType) {
namespace.text = parseTableModel2Text(namespace); $scope.prepareReleaseNamespace = function (namespace) {
} else if ('table' == viewType) { releaseNamespace = namespace;
}
namespace.viewType = viewType;
}; };
$scope.releaseComment = '';
$scope.release = function () {
ConfigService.release($scope.pageContext.appId, $scope.pageContext.env,
$scope.pageContext.clusterName,
releaseNamespace.namespace.namespaceName, currentUser,
$scope.releaseComment).then(
function (result) {
toastr.success("发布成功");
//refresh all namespace items
refreshNamespaces();
//把表格内容解析成文本 }, function (result) {
function parseTableModel2Text(namespace) { toastr.error(result.data.msg, "发布失败");
if (!namespace.items) {
return "无配置信息";
}
var result = "";
namespace.items.forEach(function (item) {
// if (item.modified) {
// result += "**";
// }
if (item.item.key) {
result +=
item.item.key + " = " + item.item.value + "\n";
} else {
result += item.item.comment + "\n";
}
});
return result; }
);
} }
//把文本内容解析成表格
}]); }]);
...@@ -6,12 +6,12 @@ appService.service("ConfigService", ['$resource', '$q', function ($resource, $q) ...@@ -6,12 +6,12 @@ appService.service("ConfigService", ['$resource', '$q', function ($resource, $q)
url: '/apps/:appId/env/:env/clusters/:clusterName/namespaces' url: '/apps/:appId/env/:env/clusters/:clusterName/namespaces'
}, },
modify_items: { modify_items: {
method: 'GET', method: 'PUT',
isArray: false, url: '/apps/:appId/env/:env/clusters/:clusterName/namespaces/:namespaceName/items'
url: '/apps/:appId/env/:env/clusters/:clusterName/namespaces/:namespaceName/modify', },
params: { release: {
configText: '@configText' method: 'POST',
} url:'/apps/:appId/env/:env/clusters/:clusterName/namespaces/:namespaceName/release'
} }
}); });
...@@ -29,17 +29,40 @@ appService.service("ConfigService", ['$resource', '$q', function ($resource, $q) ...@@ -29,17 +29,40 @@ appService.service("ConfigService", ['$resource', '$q', function ($resource, $q)
}); });
return d.promise; return d.promise;
}, },
modify_items: function (appId, env, clusterName, namespaceName, configText) {
modify_items: function (appId, env, clusterName, namespaceName, configText, namespaceId, modifyBy) {
var d = $q.defer(); var d = $q.defer();
config_source.modify_items({ config_source.modify_items({
appId: appId, appId: appId,
env: env, env: env,
clusterName: clusterName, clusterName: clusterName,
namespaceName: namespaceName, namespaceName: namespaceName
configText: configText },
{
configText: configText,
namespaceId: namespaceId,
modifyBy: modifyBy
}, function (result) { }, function (result) {
d.resolve(result); d.resolve(result);
}, function (result) {
d.reject(result);
});
return d.promise;
},
release: function (appId, env, clusterName, namespaceName, releaseBy, comment) {
var d = $q.defer();
config_source.release({
appId: appId,
env: env,
clusterName: clusterName,
namespaceName: namespaceName
}, {
releaseBy: releaseBy,
releaseComment: comment
}, function (result) {
d.resolve(result);
}, function (result) { }, function (result) {
d.reject(result); d.reject(result);
}); });
......
...@@ -72,27 +72,31 @@ table th { ...@@ -72,27 +72,31 @@ table th {
height: 500px; height: 500px;
overflow: scroll; overflow: scroll;
} }
.namespace-view-table{
max-height: 700px;
overflow: scroll;
}
.historyview { .history-view {
padding: 50px 20px; padding: 50px 20px;
} }
.historyview .commit { .history-view .commit {
padding: 5px 15px; padding: 5px 15px;
border: 1px solid #ddd; border: 1px solid #ddd;
} }
.historyview img { .history-view img {
position: absolute; position: absolute;
left: -28px; left: -28px;
} }
.historyview .media .row { .history-view .media .row {
padding-left: 35px; padding-left: 35px;
} }
.historyview .list { .history-view .list {
position: relative; position: relative;
border-left: 3px solid #ddd; border-left: 3px solid #ddd;
} }
......
...@@ -41,8 +41,10 @@ ...@@ -41,8 +41,10 @@
<div class="col-md-7"> <div class="col-md-7">
<div class="btn-toolbar" role="toolbar" aria-label="..."> <div class="btn-toolbar" role="toolbar" aria-label="...">
<div class="btn-group" role="group" aria-label="..."> <div class="btn-group" role="group" aria-label="...">
<button type="button" <button type="button" data-toggle="modal" data-target="#releaseModal"
class="btn btn-default btn-sm J_tableview_btn">发布 class="btn btn-default btn-sm J_tableview_btn"
ng-disabled="namespace.isTextEditing"
ng-click="prepareReleaseNamespace(namespace)">发布
</button> </button>
<button type="button" <button type="button"
class="btn btn-default btn-sm J_tableview_btn">回滚 class="btn btn-default btn-sm J_tableview_btn">回滚
...@@ -65,7 +67,7 @@ ...@@ -65,7 +67,7 @@
<div class="btn-group" role="group" aria-label="..."> <div class="btn-group" role="group" aria-label="...">
<button type="button" <button type="button"
class="btn btn-default btn-sm J_tableview_btn" class="btn btn-default btn-sm J_tableview_btn"
ng-click="switchView(namespace, 'textarea')">文本 ng-click="switchView(namespace, 'text')">文本
</button> </button>
<button type="button" <button type="button"
class="btn btn-default btn-sm J_tableview_btn" class="btn btn-default btn-sm J_tableview_btn"
...@@ -86,7 +88,7 @@ ...@@ -86,7 +88,7 @@
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span> <span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
</a> </a>
<a data-toggle="tooltip" data-placement="top" title="修改配置" <a data-toggle="tooltip" data-placement="top" title="修改配置"
ng-show="!namespace.isTextEditing" ng-show="!namespace.isTextEditing && namespace.viewType == 'text'"
ng-click="toggleTextEditStatus(namespace)"> ng-click="toggleTextEditStatus(namespace)">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
</a> </a>
...@@ -99,65 +101,66 @@ ...@@ -99,65 +101,66 @@
</div> </div>
</header> </header>
<div ng-show="namespace.viewType == 'textarea'"> <div ng-show="namespace.viewType == 'text'">
<textarea class="form-control" rows="20" ng-model="namespace.text" <textarea class="form-control" rows="30" ng-model="namespace.text"
ng-disabled="!namespace.isTextEditing"> ng-disabled="!namespace.isTextEditing">
{{namespace.text}} {{namespace.text}}
</textarea> </textarea>
</div> </div>
<table class="table table-bordered text-center table-hover" <div class="namespace-view-table">
ng-show="namespace.viewType == 'table'"> <table class="table table-bordered text-center table-hover"
<thead> ng-show="namespace.viewType == 'table'">
<tr> <thead>
<th> <tr>
Key <th>
</th> Key
<th> </th>
value <th>
</th> value
<th> </th>
备注 <th>
</th> 备注
<th> </th>
最后修改人 <th>
</th> 最后修改人
<th> </th>
最后修改时间 <th>
</th> 最后修改时间
</tr> </th>
</thead> </tr>
<tbody ng-repeat="config in namespace.items"> </thead>
<tbody ng-repeat="config in namespace.items">
<tr ng-class="{warning:config.modified}" ng-if="config.item.key">
<td> <tr ng-class="{warning:config.modified}" ng-if="config.item.key">
{{config.item.key}} <td>
</td> {{config.item.key}}
<td> </td>
<button data-placement="top" title="查看旧值" <td>
class="glyphicon glyphicon-eye-open" <button data-placement="top" title="查看旧值"
aria-hidden="true" data-toggle="modal" data-target="#oldValueModal" class="glyphicon glyphicon-eye-open"
ng-show="config.modified" aria-hidden="true" data-toggle="modal" data-target="#oldValueModal"
ng-click="queryOldValue(config.item.key, config.oldValue)"></button> ng-show="config.modified"
{{config.item.value}} ng-click="queryOldValue(config.item.key, config.oldValue)"></button>
</td> {{config.item.value}}
<td> </td>
{{config.item.comment}} <td>
</td> {{config.item.comment}}
<td> </td>
{{config.item.dataChangeLastModifiedBy}} <td>
</td> {{config.item.dataChangeLastModifiedBy}}
<td> </td>
{{config.item.dataChangeLastModifiedTime | date: 'yyyy-MM-dd HH:mm:ss'}} <td>
</td> {{config.item.dataChangeLastModifiedTime | date: 'yyyy-MM-dd HH:mm:ss'}}
</td>
</tr>
</tbody> </tr>
</table> </tbody>
</table>
</div>
<!--历史修改视图--> <!--历史修改视图-->
<div class="J_historyview historyview" ng-show="namespace.viewType == 'history'"> <div class="J_historyview history-view" ng-show="namespace.viewType == 'history'">
<div class="row"> <div class="row">
<div class="col-md-11 col-md-offset-1 list" style=""> <div class="col-md-11 col-md-offset-1 list" style="">
<div class="media"> <div class="media">
...@@ -208,7 +211,7 @@ ...@@ -208,7 +211,7 @@
</div> </div>
<!-- Modal --> <!-- view old value Modal -->
<div class="modal fade " id="oldValueModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"> <div class="modal fade " id="oldValueModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog modal-sm" role="document"> <div class="modal-dialog modal-sm" role="document">
<div class="modal-content"> <div class="modal-content">
...@@ -228,7 +231,7 @@ ...@@ -228,7 +231,7 @@
</div> </div>
<!-- commint modify config --> <!-- commit modify config modal-->
<div class="modal fade" id="commitModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel2"> <div class="modal fade" id="commitModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel2">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
...@@ -238,10 +241,29 @@ ...@@ -238,10 +241,29 @@
<h4 class="modal-title" id="myModalLabel2">Commit changes</h4> <h4 class="modal-title" id="myModalLabel2">Commit changes</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<textarea rows="4" style="width:570px;" placeholder="input change log...."></textarea> <textarea rows="4" class="form-control" style="width:570px;" placeholder="input change log...."></textarea>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-dismiss="modal" ng-click="commitChange()">提交</button>
</div>
</div>
</div>
</div>
<!--release modal-->
<div class="modal fade" id="releaseModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel3">
<div class="modal-dialog" role="document">
<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" id="myModalLabel3">发布</h4>
</div>
<div class="modal-body">
<textarea rows="4" class="form-control" style="width:570px;" ng-model = "releaseComment" placeholder="input release log...."></textarea>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal" ng-click="commitChange()">提交</button> <button type="button" class="btn btn-primary" data-dismiss="modal" ng-click="release()">提交</button>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -43,20 +43,13 @@ ...@@ -43,20 +43,13 @@
<div class="form-group"> <div class="form-group">
<label class="col-sm-2 control-label">Owner</label> <label class="col-sm-2 control-label">Owner</label>
<div class="col-sm-3"> <div class="col-sm-3">
<input type="text" class="form-control" name="appOwner" ng-model="app.owner"> <input type="text" class="form-control" name="appOwner" ng-model="app.ownerName">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">手机号</label>
<div class="col-sm-3">
<input type="text" class="form-control" name="appOwnerPhone"
ng-model="app.ownerPhone">
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="col-sm-2 control-label"> 邮箱地址</label> <label class="col-sm-2 control-label"> 邮箱地址</label>
<div class="col-sm-4"> <div class="col-sm-4">
<input type="email" class="form-control" ng-model="app.ownerMail"> <input type="email" class="form-control" ng-model="app.ownerEmail">
</div> </div>
</div> </div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment