Browse Source

api server exception management and code optimization (#397) (#2397)

* api server exception management and code optimization (#397)

* add unit test for exception handler.

* fix some style and naming issues

Co-authored-by: dailidong <dailidong66@gmail.com>
pull/2/head
Han Gao 5 years ago committed by GitHub
parent
commit
3c9ba0a3f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 37
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/AccessTokenController.java
  2. 2
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/enums/Status.java
  3. 34
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/exceptions/ApiException.java
  4. 48
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/exceptions/ApiExceptionHandler.java
  5. 3
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/AccessTokenService.java
  6. 51
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/utils/Result.java
  7. 17
      dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/AccessTokenControllerTest.java
  8. 42
      dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/exceptions/ApiExceptionHandlerTest.java
  9. 48
      dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/utils/ResultTest.java
  10. 2
      pom.xml

37
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/AccessTokenController.java

@ -18,6 +18,7 @@ package org.apache.dolphinscheduler.api.controller;
import org.apache.dolphinscheduler.api.enums.Status; import org.apache.dolphinscheduler.api.enums.Status;
import org.apache.dolphinscheduler.api.exceptions.ApiException;
import org.apache.dolphinscheduler.api.service.AccessTokenService; import org.apache.dolphinscheduler.api.service.AccessTokenService;
import org.apache.dolphinscheduler.api.utils.Result; import org.apache.dolphinscheduler.api.utils.Result;
import org.apache.dolphinscheduler.common.Constants; import org.apache.dolphinscheduler.common.Constants;
@ -37,6 +38,7 @@ import springfox.documentation.annotations.ApiIgnore;
import java.util.Map; import java.util.Map;
import static org.apache.dolphinscheduler.api.enums.Status.*; import static org.apache.dolphinscheduler.api.enums.Status.*;
/** /**
* access token controller * access token controller
*/ */
@ -54,6 +56,7 @@ public class AccessTokenController extends BaseController{
/** /**
* create token * create token
*
* @param loginUser login user * @param loginUser login user
* @param userId token for user id * @param userId token for user id
* @param expireTime expire time for the token * @param expireTime expire time for the token
@ -63,6 +66,7 @@ public class AccessTokenController extends BaseController{
@ApiIgnore @ApiIgnore
@PostMapping(value = "/create") @PostMapping(value = "/create")
@ResponseStatus(HttpStatus.CREATED) @ResponseStatus(HttpStatus.CREATED)
@ApiException(CREATE_ACCESS_TOKEN_ERROR)
public Result createToken(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser, public Result createToken(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
@RequestParam(value = "userId") int userId, @RequestParam(value = "userId") int userId,
@RequestParam(value = "expireTime") String expireTime, @RequestParam(value = "expireTime") String expireTime,
@ -70,17 +74,13 @@ public class AccessTokenController extends BaseController{
logger.info("login user {}, create token , userId : {} , token expire time : {} , token : {}", loginUser.getUserName(), logger.info("login user {}, create token , userId : {} , token expire time : {} , token : {}", loginUser.getUserName(),
userId, expireTime, token); userId, expireTime, token);
try {
Map<String, Object> result = accessTokenService.createToken(userId, expireTime, token); Map<String, Object> result = accessTokenService.createToken(userId, expireTime, token);
return returnDataList(result); return returnDataList(result);
}catch (Exception e){
logger.error(CREATE_ACCESS_TOKEN_ERROR.getMsg(),e);
return error(CREATE_ACCESS_TOKEN_ERROR.getCode(), CREATE_ACCESS_TOKEN_ERROR.getMsg());
}
} }
/** /**
* generate token string * generate token string
*
* @param loginUser login user * @param loginUser login user
* @param userId token for user * @param userId token for user
* @param expireTime expire time * @param expireTime expire time
@ -89,17 +89,13 @@ public class AccessTokenController extends BaseController{
@ApiIgnore @ApiIgnore
@PostMapping(value = "/generate") @PostMapping(value = "/generate")
@ResponseStatus(HttpStatus.CREATED) @ResponseStatus(HttpStatus.CREATED)
@ApiException(GENERATE_TOKEN_ERROR)
public Result generateToken(@RequestAttribute(value = Constants.SESSION_USER) User loginUser, public Result generateToken(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
@RequestParam(value = "userId") int userId, @RequestParam(value = "userId") int userId,
@RequestParam(value = "expireTime") String expireTime) { @RequestParam(value = "expireTime") String expireTime) {
logger.info("login user {}, generate token , userId : {} , token expire time : {}", loginUser, userId, expireTime); logger.info("login user {}, generate token , userId : {} , token expire time : {}", loginUser, userId, expireTime);
try {
Map<String, Object> result = accessTokenService.generateToken(userId, expireTime); Map<String, Object> result = accessTokenService.generateToken(userId, expireTime);
return returnDataList(result); return returnDataList(result);
}catch (Exception e){
logger.error(GENERATE_TOKEN_ERROR.getMsg(),e);
return error(GENERATE_TOKEN_ERROR.getCode(), GENERATE_TOKEN_ERROR.getMsg());
}
} }
/** /**
@ -119,13 +115,14 @@ public class AccessTokenController extends BaseController{
}) })
@GetMapping(value = "/list-paging") @GetMapping(value = "/list-paging")
@ResponseStatus(HttpStatus.OK) @ResponseStatus(HttpStatus.OK)
@ApiException(QUERY_ACCESSTOKEN_LIST_PAGING_ERROR)
public Result queryAccessTokenList(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser, public Result queryAccessTokenList(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
@RequestParam("pageNo") Integer pageNo, @RequestParam("pageNo") Integer pageNo,
@RequestParam(value = "searchVal", required = false) String searchVal, @RequestParam(value = "searchVal", required = false) String searchVal,
@RequestParam("pageSize") Integer pageSize) { @RequestParam("pageSize") Integer pageSize) {
logger.info("login user {}, list access token paging, pageNo: {}, searchVal: {}, pageSize: {}", logger.info("login user {}, list access token paging, pageNo: {}, searchVal: {}, pageSize: {}",
loginUser.getUserName(), pageNo, searchVal, pageSize); loginUser.getUserName(), pageNo, searchVal, pageSize);
try{
Map<String, Object> result = checkPageParams(pageNo, pageSize); Map<String, Object> result = checkPageParams(pageNo, pageSize);
if (result.get(Constants.STATUS) != Status.SUCCESS) { if (result.get(Constants.STATUS) != Status.SUCCESS) {
return returnDataListPaging(result); return returnDataListPaging(result);
@ -133,14 +130,11 @@ public class AccessTokenController extends BaseController{
searchVal = ParameterUtils.handleEscapes(searchVal); searchVal = ParameterUtils.handleEscapes(searchVal);
result = accessTokenService.queryAccessTokenList(loginUser, searchVal, pageNo, pageSize); result = accessTokenService.queryAccessTokenList(loginUser, searchVal, pageNo, pageSize);
return returnDataListPaging(result); return returnDataListPaging(result);
}catch (Exception e){
logger.error(QUERY_ACCESSTOKEN_LIST_PAGING_ERROR.getMsg(),e);
return error(QUERY_ACCESSTOKEN_LIST_PAGING_ERROR.getCode(),QUERY_ACCESSTOKEN_LIST_PAGING_ERROR.getMsg());
}
} }
/** /**
* delete access token by id * delete access token by id
*
* @param loginUser login user * @param loginUser login user
* @param id token id * @param id token id
* @return delete result code * @return delete result code
@ -148,21 +142,18 @@ public class AccessTokenController extends BaseController{
@ApiIgnore @ApiIgnore
@PostMapping(value = "/delete") @PostMapping(value = "/delete")
@ResponseStatus(HttpStatus.OK) @ResponseStatus(HttpStatus.OK)
@ApiException(DELETE_ACCESS_TOKEN_ERROR)
public Result delAccessTokenById(@RequestAttribute(value = Constants.SESSION_USER) User loginUser, public Result delAccessTokenById(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
@RequestParam(value = "id") int id) { @RequestParam(value = "id") int id) {
logger.info("login user {}, delete access token, id: {},", loginUser.getUserName(), id); logger.info("login user {}, delete access token, id: {},", loginUser.getUserName(), id);
try {
Map<String, Object> result = accessTokenService.delAccessTokenById(loginUser, id); Map<String, Object> result = accessTokenService.delAccessTokenById(loginUser, id);
return returnDataList(result); return returnDataList(result);
}catch (Exception e){
logger.error(DELETE_ACCESS_TOKEN_ERROR.getMsg(),e);
return error(Status.DELETE_ACCESS_TOKEN_ERROR.getCode(), Status.DELETE_ACCESS_TOKEN_ERROR.getMsg());
}
} }
/** /**
* update token * update token
*
* @param loginUser login user * @param loginUser login user
* @param id token id * @param id token id
* @param userId token for user * @param userId token for user
@ -173,6 +164,7 @@ public class AccessTokenController extends BaseController{
@ApiIgnore @ApiIgnore
@PostMapping(value = "/update") @PostMapping(value = "/update")
@ResponseStatus(HttpStatus.OK) @ResponseStatus(HttpStatus.OK)
@ApiException(UPDATE_ACCESS_TOKEN_ERROR)
public Result updateToken(@RequestAttribute(value = Constants.SESSION_USER) User loginUser, public Result updateToken(@RequestAttribute(value = Constants.SESSION_USER) User loginUser,
@RequestParam(value = "id") int id, @RequestParam(value = "id") int id,
@RequestParam(value = "userId") int userId, @RequestParam(value = "userId") int userId,
@ -181,13 +173,8 @@ public class AccessTokenController extends BaseController{
logger.info("login user {}, update token , userId : {} , token expire time : {} , token : {}", loginUser.getUserName(), logger.info("login user {}, update token , userId : {} , token expire time : {} , token : {}", loginUser.getUserName(),
userId, expireTime, token); userId, expireTime, token);
try {
Map<String, Object> result = accessTokenService.updateToken(id, userId, expireTime, token); Map<String, Object> result = accessTokenService.updateToken(id, userId, expireTime, token);
return returnDataList(result); return returnDataList(result);
}catch (Exception e){
logger.error(UPDATE_ACCESS_TOKEN_ERROR.getMsg(),e);
return error(UPDATE_ACCESS_TOKEN_ERROR.getCode(), UPDATE_ACCESS_TOKEN_ERROR.getMsg());
}
} }
} }

2
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/enums/Status.java

@ -27,6 +27,8 @@ public enum Status {
SUCCESS(0, "success", "成功"), SUCCESS(0, "success", "成功"),
INTERNAL_SERVER_ERROR_ARGS(10000, "Internal Server Error: {0}", "服务端异常: {0}"),
REQUEST_PARAMS_NOT_VALID_ERROR(10001, "request parameter {0} is not valid", "请求参数[{0}]无效"), REQUEST_PARAMS_NOT_VALID_ERROR(10001, "request parameter {0} is not valid", "请求参数[{0}]无效"),
TASK_TIMEOUT_PARAMS_ERROR(10002, "task timeout parameter is not valid", "任务超时参数无效"), TASK_TIMEOUT_PARAMS_ERROR(10002, "task timeout parameter is not valid", "任务超时参数无效"),
USER_NAME_EXIST(10003, "user name already exists", "用户名已存在"), USER_NAME_EXIST(10003, "user name already exists", "用户名已存在"),

34
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/exceptions/ApiException.java

@ -0,0 +1,34 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.api.exceptions;
import org.apache.dolphinscheduler.api.enums.Status;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* controller exception annotation
*/
@Retention(RUNTIME)
@Target(METHOD)
public @interface ApiException {
Status value();
}

48
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/exceptions/ApiExceptionHandler.java

@ -0,0 +1,48 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.api.exceptions;
import org.apache.dolphinscheduler.api.enums.Status;
import org.apache.dolphinscheduler.api.utils.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.HandlerMethod;
/**
* Exception Handler
*/
@ControllerAdvice
@ResponseBody
public class ApiExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(ApiExceptionHandler.class);
@ExceptionHandler(Exception.class)
public Result exceptionHandler(Exception e, HandlerMethod hm) {
logger.error(e.getMessage(), e);
ApiException ce = hm.getMethodAnnotation(ApiException.class);
if (ce == null) {
return Result.errorWithArgs(Status.INTERNAL_SERVER_ERROR_ARGS, e.getMessage());
}
Status st = ce.value();
return Result.error(st);
}
}

3
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/AccessTokenService.java

@ -83,6 +83,9 @@ public class AccessTokenService extends BaseService {
public Map<String, Object> createToken(int userId, String expireTime, String token) { public Map<String, Object> createToken(int userId, String expireTime, String token) {
Map<String, Object> result = new HashMap<>(5); Map<String, Object> result = new HashMap<>(5);
if (userId <= 0) {
throw new IllegalArgumentException("User id should not less than or equals to 0.");
}
AccessToken accessToken = new AccessToken(); AccessToken accessToken = new AccessToken();
accessToken.setUserId(userId); accessToken.setUserId(userId);
accessToken.setExpireTime(DateUtils.stringToDate(expireTime)); accessToken.setExpireTime(DateUtils.stringToDate(expireTime));

51
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/utils/Result.java

@ -16,6 +16,10 @@
*/ */
package org.apache.dolphinscheduler.api.utils; package org.apache.dolphinscheduler.api.utils;
import org.apache.dolphinscheduler.api.enums.Status;
import java.text.MessageFormat;
/** /**
* result * result
* *
@ -37,13 +41,58 @@ public class Result<T> {
*/ */
private T data; private T data;
public Result(){} public Result() {
}
public Result(Integer code, String msg) { public Result(Integer code, String msg) {
this.code = code; this.code = code;
this.msg = msg; this.msg = msg;
} }
private Result(T data) {
this.code = 0;
this.data = data;
}
private Result(Status status) {
if (status != null) {
this.code = status.getCode();
this.msg = status.getMsg();
}
}
/**
* Call this function if there is success
*
* @param data data
* @param <T> type
* @return resule
*/
public static <T> Result<T> success(T data) {
return new Result<>(data);
}
/**
* Call this function if there is any error
*
* @param status status
* @return result
*/
public static Result error(Status status) {
return new Result(status);
}
/**
* Call this function if there is any error
*
* @param status status
* @param args args
* @return result
*/
public static Result errorWithArgs(Status status, Object... args) {
return new Result(status.getCode(), MessageFormat.format(status.getMsg(), args));
}
public Integer getCode() { public Integer getCode() {
return code; return code;
} }

17
dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/AccessTokenControllerTest.java

@ -56,6 +56,23 @@ public class AccessTokenControllerTest extends AbstractControllerTest{
logger.info(mvcResult.getResponse().getContentAsString()); logger.info(mvcResult.getResponse().getContentAsString());
} }
@Test
public void testExceptionHandler() throws Exception {
MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>();
paramsMap.add("userId","-1");
paramsMap.add("expireTime","2019-12-18 00:00:00");
paramsMap.add("token","507f5aeaaa2093dbdff5d5522ce00510");
MvcResult mvcResult = mockMvc.perform(post("/access-token/create")
.header("sessionId", sessionId)
.params(paramsMap))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andReturn();
Result result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class);
Assert.assertEquals(Status.CREATE_ACCESS_TOKEN_ERROR.getCode(), result.getCode().intValue());
logger.info(mvcResult.getResponse().getContentAsString());
}
@Test @Test
public void testGenerateToken() throws Exception { public void testGenerateToken() throws Exception {
MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>(); MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>();

42
dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/exceptions/ApiExceptionHandlerTest.java

@ -0,0 +1,42 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.api.exceptions;
import org.apache.dolphinscheduler.api.controller.AccessTokenController;
import org.apache.dolphinscheduler.api.enums.Status;
import org.apache.dolphinscheduler.api.utils.Result;
import org.apache.dolphinscheduler.dao.entity.User;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.web.method.HandlerMethod;
import java.lang.reflect.Method;
import static org.junit.Assert.*;
public class ApiExceptionHandlerTest {
@Test
public void exceptionHandler() throws NoSuchMethodException {
ApiExceptionHandler handler = new ApiExceptionHandler();
AccessTokenController controller = new AccessTokenController();
Method method = controller.getClass().getMethod("createToken", User.class, int.class, String.class, String.class);
HandlerMethod hm = new HandlerMethod(controller, method);
Result result = handler.exceptionHandler(new RuntimeException("test exception"), hm);
Assert.assertEquals(Status.CREATE_ACCESS_TOKEN_ERROR.getCode(),result.getCode().intValue());
}
}

48
dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/utils/ResultTest.java

@ -0,0 +1,48 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.api.utils;
import org.apache.dolphinscheduler.api.enums.Status;
import org.junit.Assert;
import org.junit.Test;
import java.util.HashMap;
import static org.junit.Assert.*;
public class ResultTest {
@Test
public void success() {
HashMap<String, String> map = new HashMap<>();
map.put("testdata", "test");
Result ret = Result.success(map);
Assert.assertEquals(Status.SUCCESS.getCode(), ret.getCode().intValue());
}
@Test
public void error() {
Result ret = Result.error(Status.ACCESS_TOKEN_NOT_EXIST);
Assert.assertEquals(Status.ACCESS_TOKEN_NOT_EXIST.getCode(), ret.getCode().intValue());
}
@Test
public void errorWithArgs() {
Result ret = Result.errorWithArgs(Status.INTERNAL_SERVER_ERROR_ARGS, "test internal server error");
Assert.assertEquals(Status.INTERNAL_SERVER_ERROR_ARGS.getCode(), ret.getCode().intValue());
}
}

2
pom.xml

@ -694,6 +694,7 @@
<include>**/api/dto/resources/visitor/ResourceTreeVisitorTest.java</include> <include>**/api/dto/resources/visitor/ResourceTreeVisitorTest.java</include>
<include>**/api/enums/testGetEnum.java</include> <include>**/api/enums/testGetEnum.java</include>
<include>**/api/enums/StatusTest.java</include> <include>**/api/enums/StatusTest.java</include>
<include>**/api/exceptions/ApiExceptionHandlerTest.java</include>
<include>**/api/interceptor/LoginHandlerInterceptorTest.java</include> <include>**/api/interceptor/LoginHandlerInterceptorTest.java</include>
<include>**/api/security/PasswordAuthenticatorTest.java</include> <include>**/api/security/PasswordAuthenticatorTest.java</include>
<include>**/api/security/SecurityConfigTest.java</include> <include>**/api/security/SecurityConfigTest.java</include>
@ -726,6 +727,7 @@
<include>**/api/utils/FileUtilsTest.java</include> <include>**/api/utils/FileUtilsTest.java</include>
<include>**/api/utils/CheckUtilsTest.java</include> <include>**/api/utils/CheckUtilsTest.java</include>
<include>**/api/utils/CheckUtilsTest.java</include> <include>**/api/utils/CheckUtilsTest.java</include>
<include>**/api/utils/ResultTest.java</include>
<include>**/common/graph/DAGTest.java</include> <include>**/common/graph/DAGTest.java</include>
<include>**/common/os/OshiTest.java</include> <include>**/common/os/OshiTest.java</include>
<include>**/common/os/OSUtilsTest.java</include> <include>**/common/os/OSUtilsTest.java</include>

Loading…
Cancel
Save