Commit 24ab7fb2 by Jason Song

Merge pull request #75 from yiming187/app_update

add app CRUD logic in admin service
parents 18bd2a73 d46940e8
...@@ -4,8 +4,12 @@ import java.util.List; ...@@ -4,8 +4,12 @@ import java.util.List;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
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.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
...@@ -13,7 +17,8 @@ import com.ctrip.apollo.biz.entity.App; ...@@ -13,7 +17,8 @@ import com.ctrip.apollo.biz.entity.App;
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;
import com.google.common.base.Strings; import com.ctrip.apollo.core.exception.NotFoundException;
import com.ctrip.apollo.core.utils.StringUtils;
@RestController @RestController
public class AppController { public class AppController {
...@@ -21,21 +26,50 @@ public class AppController { ...@@ -21,21 +26,50 @@ public class AppController {
@Autowired @Autowired
private AppService appService; private AppService appService;
@RequestMapping("/apps/{appId}") @RequestMapping(path = "/apps/", method = RequestMethod.POST)
public AppDTO getApp(@PathVariable("appId") String appId) { public ResponseEntity<AppDTO> createApp(@RequestBody AppDTO appDTO) {
App app = BeanUtils.transfrom(App.class, appDTO);
app = appService.save(app);
AppDTO dto = BeanUtils.transfrom(AppDTO.class, app);
return ResponseEntity.status(HttpStatus.CREATED).body(dto);
}
@RequestMapping(path = "/apps/{appId}", method = RequestMethod.DELETE)
public void deleteApp(@PathVariable("appId") String appId) {
App app = appService.findOne(appId); App app = appService.findOne(appId);
return BeanUtils.transfrom(AppDTO.class, app); if (app == null) throw new NotFoundException("app not found for appId " + appId);
appService.delete(app.getId());
} }
@RequestMapping("/apps") @RequestMapping("/apps")
public List<AppDTO> findApps(@RequestParam(value = "name", required = false) String name, public List<AppDTO> findApps(@RequestParam(value = "name", required = false) String name,
Pageable pageable) { Pageable pageable) {
List<App> app = null; List<App> app = null;
if (Strings.isNullOrEmpty(name)) { if (StringUtils.isBlank(name)) {
app = appService.findAll(pageable); app = appService.findAll(pageable);
} else { } else {
app = appService.findByName(name); app = appService.findByName(name);
} }
return BeanUtils.batchTransform(AppDTO.class, app); return BeanUtils.batchTransform(AppDTO.class, app);
} }
@RequestMapping("/apps/{appId}")
public AppDTO getApp(@PathVariable("appId") String appId) {
App app = appService.findOne(appId);
if (app == null) throw new NotFoundException("app not found for appId " + appId);
return BeanUtils.transfrom(AppDTO.class, app);
}
@RequestMapping(path = "/apps/{appId}", method = RequestMethod.PUT)
public AppDTO updateApp(@PathVariable("appId") String appId, @RequestBody AppDTO appDTO) {
if (!appId.equals(appDTO.getAppId())) {
throw new IllegalArgumentException(String
.format("Path variable %s is not equals to object field %s", appId, appDTO.getAppId()));
}
App app = appService.findOne(appId);
if (app == null) throw new NotFoundException("app not found for appId " + appId);
app = appService.update(BeanUtils.transfrom(App.class, appDTO));
return BeanUtils.transfrom(AppDTO.class, app);
}
} }
package com.ctrip.apollo.adminservice.controller;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.HttpMediaTypeException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import com.ctrip.apollo.core.exception.NotFoundException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import static org.springframework.http.HttpStatus.NOT_FOUND;
import static org.springframework.http.MediaType.APPLICATION_JSON;
@ControllerAdvice
public class GlobalDefaultExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<Map<String, Object>> exception(HttpServletRequest request, Exception ex) {
return handleError(request, INTERNAL_SERVER_ERROR, ex);
}
private ResponseEntity<Map<String, Object>> handleError(HttpServletRequest request,
HttpStatus status, Throwable ex) {
return handleError(request, status, ex, ex.getMessage());
}
private ResponseEntity<Map<String, Object>> handleError(HttpServletRequest request,
HttpStatus status, Throwable ex,
String message) {
ex = resolveError(ex);
Map<String, Object> errorAttributes = new LinkedHashMap<>();
errorAttributes.put("status", status.value());
errorAttributes.put("message", message);
errorAttributes.put("timestamp",
LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
errorAttributes.put("exception", resolveError(ex).getClass().getName());
HttpHeaders headers = new HttpHeaders();
headers.setContentType(APPLICATION_JSON);
return new ResponseEntity<>(errorAttributes, headers, status);
}
@ExceptionHandler({HttpRequestMethodNotSupportedException.class, HttpMediaTypeException.class})
public ResponseEntity<Map<String, Object>> methodNotSupportedException(HttpServletRequest request,
ServletException ex) {
return handleError(request, BAD_REQUEST, ex);
}
@ExceptionHandler(NotFoundException.class)
@ResponseStatus(value = NOT_FOUND)
public void notFound(HttpServletRequest req, NotFoundException ex) {
}
private Throwable resolveError(Throwable ex) {
while (ex instanceof ServletException && ex.getCause() != null) {
ex = ((ServletException) ex).getCause();
}
return ex;
}
}
...@@ -8,7 +8,6 @@ import org.springframework.data.web.PageableHandlerMethodArgumentResolver; ...@@ -8,7 +8,6 @@ import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer; import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration @Configuration
......
package com.ctrip.apollo;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration;
@SpringBootApplication(exclude = {SampleAdminServiceApplication.class,
EurekaClientAutoConfiguration.class})
public class AdminServiceTestConfiguration {
}
package com.ctrip.apollo.adminservice.controller;
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.boot.test.TestRestTemplate;
import org.springframework.boot.test.WebIntegrationTest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.client.RestTemplate;
import com.ctrip.apollo.AdminServiceTestConfiguration;
import com.ctrip.apollo.biz.entity.App;
import com.ctrip.apollo.biz.repository.AppRepository;
import com.ctrip.apollo.biz.utils.BeanUtils;
import com.ctrip.apollo.core.dto.AppDTO;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = AdminServiceTestConfiguration.class)
@WebIntegrationTest
public class AppControllerTest {
RestTemplate restTemplate = new TestRestTemplate();
@Autowired
AppRepository appRepository;
@Test
public void testCreate() {
AppDTO dto = generateSampleDTOData();
ResponseEntity<AppDTO> response =
restTemplate.postForEntity("http://localhost:8090/apps/", dto, AppDTO.class);
AppDTO result = response.getBody();
Assert.assertEquals(HttpStatus.CREATED, response.getStatusCode());
Assert.assertEquals(dto.getAppId(), result.getAppId());
Assert.assertTrue(result.getId() > 0);
App savedApp = appRepository.findOne(result.getId());
Assert.assertEquals(dto.getAppId(), savedApp.getAppId());
Assert.assertNotNull(savedApp.getDataChangeCreatedTime());
appRepository.delete(savedApp.getId());
}
@Test
public void testFind() {
AppDTO dto = generateSampleDTOData();
App app = BeanUtils.transfrom(App.class, dto);
app = appRepository.save(app);
AppDTO result =
restTemplate.getForObject("http://localhost:8090/apps/" + dto.getAppId(), AppDTO.class);
Assert.assertEquals(dto.getAppId(), result.getAppId());
Assert.assertEquals(dto.getName(), result.getName());
appRepository.delete(app.getId());
}
@Test
public void testFindNotExist() {
ResponseEntity<AppDTO> result =
restTemplate.getForEntity("http://localhost:8090/apps/" + "notExists", AppDTO.class);
Assert.assertEquals(HttpStatus.NOT_FOUND, result.getStatusCode());
}
@Test
public void testDelete() {
AppDTO dto = generateSampleDTOData();
App app = BeanUtils.transfrom(App.class, dto);
app = appRepository.save(app);
restTemplate.delete("http://localhost:8090/apps/" + dto.getAppId());
App deletedApp = appRepository.findOne(app.getId());
Assert.assertNull(deletedApp);
}
@Test
public void testUpdate() {
AppDTO dto = generateSampleDTOData();
App app = BeanUtils.transfrom(App.class, dto);
app = appRepository.save(app);
dto.setName("newName");
restTemplate.put("http://localhost:8090/apps/" + dto.getAppId(), dto);
App updatedApp = appRepository.findOne(app.getId());
Assert.assertEquals(dto.getName(), updatedApp.getName());
Assert.assertNotNull(updatedApp.getDataChangeLastModifiedTime());
}
private AppDTO generateSampleDTOData() {
AppDTO dto = new AppDTO();
dto.setAppId("someAppId");
dto.setName("someName");
dto.setOwnerName("someOwner");
dto.setOwnerEmail("someOwner@ctrip.com");
return dto;
}
}
spring.datasource.url = jdbc:h2:mem:~/fxapolloconfigdb;mode=mysql spring.datasource.url = jdbc:h2:mem:~/fxapolloconfigdb;mode=mysql;DB_CLOSE_ON_EXIT=FALSE
spring.jpa.hibernate.naming_strategy=org.hibernate.cfg.EJB3NamingStrategy spring.jpa.hibernate.naming_strategy=org.hibernate.cfg.EJB3NamingStrategy
spring.h2.console.enabled = true spring.h2.console.enabled = true
spring.h2.console.settings.web-allow-others=true spring.h2.console.settings.web-allow-others=true
...@@ -9,6 +9,7 @@ import org.springframework.stereotype.Service; ...@@ -9,6 +9,7 @@ import org.springframework.stereotype.Service;
import com.ctrip.apollo.biz.entity.App; import com.ctrip.apollo.biz.entity.App;
import com.ctrip.apollo.biz.repository.AppRepository; import com.ctrip.apollo.biz.repository.AppRepository;
import com.ctrip.apollo.biz.utils.BeanUtils;
@Service @Service
public class AppService { public class AppService {
...@@ -16,20 +17,30 @@ public class AppService { ...@@ -16,20 +17,30 @@ public class AppService {
@Autowired @Autowired
private AppRepository appRepository; private AppRepository appRepository;
public App save(App entity){ public void delete(long id) {
return appRepository.save(entity); appRepository.delete(id);
} }
public List<App> findAll(Pageable pageable){ public List<App> findAll(Pageable pageable) {
Page<App> page = appRepository.findAll(pageable); Page<App> page = appRepository.findAll(pageable);
return page.getContent(); return page.getContent();
} }
public List<App> findByName(String name){ public List<App> findByName(String name) {
return appRepository.findByName(name); return appRepository.findByName(name);
} }
public App findOne(String appId){ public App findOne(String appId) {
return appRepository.findByAppId(appId); return appRepository.findByAppId(appId);
} }
public App save(App entity) {
return appRepository.save(entity);
}
public App update(App app) {
App managedApp = appRepository.findByAppId(app.getAppId());
BeanUtils.copyEntityProperties(app, managedApp);
return appRepository.save(managedApp);
}
} }
package com.ctrip.apollo.biz.utils; package com.ctrip.apollo.biz.utils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.util.CollectionUtils;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
...@@ -13,6 +9,10 @@ import java.util.List; ...@@ -13,6 +9,10 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.util.CollectionUtils;
public class BeanUtils { public class BeanUtils {
...@@ -23,7 +23,6 @@ public class BeanUtils { ...@@ -23,7 +23,6 @@ public class BeanUtils {
* </pre> * </pre>
*/ */
public static <T> List<T> batchTransform(final Class<T> clazz, List srcList) { public static <T> List<T> batchTransform(final Class<T> clazz, List srcList) {
if (CollectionUtils.isEmpty(srcList)) { if (CollectionUtils.isEmpty(srcList)) {
return Collections.EMPTY_LIST; return Collections.EMPTY_LIST;
} }
...@@ -44,7 +43,7 @@ public class BeanUtils { ...@@ -44,7 +43,7 @@ public class BeanUtils {
* </pre> * </pre>
*/ */
public static <T> T transfrom(Class<T> clazz, Object src) { public static <T> T transfrom(Class<T> clazz, Object src) {
if (src == null){ if (src == null) {
return null; return null;
} }
T instance = null; T instance = null;
...@@ -208,4 +207,14 @@ public class BeanUtils { ...@@ -208,4 +207,14 @@ public class BeanUtils {
public static List toPropertyList(String key, List list) { public static List toPropertyList(String key, List list) {
return new ArrayList(toPropertySet(key, list)); return new ArrayList(toPropertySet(key, list));
} }
/**
* The copy will ignore <em>id</em> field
*
* @param source
* @param target
*/
public static void copyEntityProperties(Object source, Object target) {
org.springframework.beans.BeanUtils.copyProperties(source, target, "id");
}
} }
...@@ -7,6 +7,6 @@ import org.springframework.context.annotation.Configuration; ...@@ -7,6 +7,6 @@ import org.springframework.context.annotation.Configuration;
@Configuration @Configuration
@EnableAutoConfiguration @EnableAutoConfiguration
@ComponentScan(basePackages = "com.ctrip.apollo.biz") @ComponentScan(basePackages = "com.ctrip.apollo.biz")
public class SpringTestConfiguration { public class BizTestConfiguration {
} }
...@@ -8,11 +8,11 @@ import org.springframework.beans.factory.annotation.Autowired; ...@@ -8,11 +8,11 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.ctrip.apollo.biz.SpringTestConfiguration; import com.ctrip.apollo.biz.BizTestConfiguration;
import com.ctrip.apollo.biz.entity.App; import com.ctrip.apollo.biz.entity.App;
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SpringTestConfiguration.class) @SpringApplicationConfiguration(classes = BizTestConfiguration.class)
public class AppRepositoryTest { public class AppRepositoryTest {
@Autowired @Autowired
......
...@@ -9,13 +9,13 @@ import org.springframework.beans.factory.annotation.Autowired; ...@@ -9,13 +9,13 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.ctrip.apollo.biz.SpringTestConfiguration; import com.ctrip.apollo.biz.BizTestConfiguration;
import com.ctrip.apollo.biz.entity.App; import com.ctrip.apollo.biz.entity.App;
import com.ctrip.apollo.biz.entity.Cluster; import com.ctrip.apollo.biz.entity.Cluster;
import com.ctrip.apollo.biz.entity.Namespace; import com.ctrip.apollo.biz.entity.Namespace;
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SpringTestConfiguration.class) @SpringApplicationConfiguration(classes = BizTestConfiguration.class)
public class AdminServiceTest { public class AdminServiceTest {
@Autowired @Autowired
......
...@@ -9,14 +9,14 @@ import org.springframework.beans.factory.annotation.Autowired; ...@@ -9,14 +9,14 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.ctrip.apollo.biz.SpringTestConfiguration; import com.ctrip.apollo.biz.BizTestConfiguration;
import com.ctrip.apollo.biz.entity.App; import com.ctrip.apollo.biz.entity.App;
import com.ctrip.apollo.biz.entity.Cluster; import com.ctrip.apollo.biz.entity.Cluster;
import com.ctrip.apollo.biz.entity.Namespace; import com.ctrip.apollo.biz.entity.Namespace;
import com.ctrip.apollo.biz.entity.Privilege; import com.ctrip.apollo.biz.entity.Privilege;
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SpringTestConfiguration.class) @SpringApplicationConfiguration(classes = BizTestConfiguration.class)
public class PrivilegeServiceTest { public class PrivilegeServiceTest {
@Autowired @Autowired
......
package com.ctrip.apollo.portal.exception; package com.ctrip.apollo.core.exception;
public class NotFoundException extends RuntimeException { public class NotFoundException extends RuntimeException {
/** /**
* *
*/ */
private static final long serialVersionUID = 7611357629749481796L; private static final long serialVersionUID = 7611357629749481796L;
public NotFoundException(){
}
public NotFoundException(String str){
super(str);
}
} }
package com.ctrip.apollo.core.exception; package com.ctrip.apollo.core.exception;
public class ServiceException extends Exception { public class ServiceException extends RuntimeException {
/** /**
* *
......
package com.ctrip.apollo.portal.controller; package com.ctrip.apollo.portal.controller;
import com.ctrip.apollo.portal.exception.NotFoundException;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
...@@ -11,6 +9,8 @@ import org.springframework.web.bind.annotation.ControllerAdvice; ...@@ -11,6 +9,8 @@ import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;
import com.ctrip.apollo.core.exception.NotFoundException;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
......
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