From 385874a6fd067cb17323a27ecd1af62b4176aecc Mon Sep 17 00:00:00 2001 From: Dan Timoney Date: Fri, 26 Jun 2020 15:56:48 -0400 Subject: [PATCH] Add RFC 8040 compliant error handler Add a new error handler that formats error responses consistent with RFC 8040 (RESTCONF) standards. Change-Id: I67a6ab626d943115570f2e74d0a8132933726bc8 Issue-ID: CCSDK-2482 Signed-off-by: Dan Timoney --- ms/sliboot/pom.xml | 1 + ms/sliboot/src/main/dc/docker-compose.yaml | 6 +- .../java/org/onap/ccsdk/apps/ms/sliboot/App.java | 3 +- .../org/onap/ccsdk/apps/ms/sliboot/WebConfig.java | 3 +- .../sliboot/controllers/RestconfApiController.java | 12 +- ms/sliboot/src/main/resources/startSliboot.sh | 18 +- ms/sliboot/src/main/templates/api.mustache | 136 ++++++++ .../apps/ms/sliboot/RestconfApiControllerTest.java | 12 +- services/pom.xml | 15 + .../ccsdk/apps/services/RestApplicationError.java | 31 ++ .../apps/services/RestApplicationException.java | 14 + .../org/onap/ccsdk/apps/services/RestError.java | 57 ++++ .../org/onap/ccsdk/apps/services/RestErrors.java | 28 ++ .../onap/ccsdk/apps/services/RestException.java | 14 + .../ccsdk/apps/services/RestExceptionHandler.java | 131 ++++++++ .../ccsdk/apps/services/RestProtocolError.java | 30 ++ .../ccsdk/apps/services/RestProtocolException.java | 14 + .../org/onap/ccsdk/apps/services/RestRpcError.java | 31 ++ .../onap/ccsdk/apps/services/RestRpcException.java | 14 + .../ccsdk/apps/services/RestTransportError.java | 30 ++ .../apps/services/RestTransportException.java | 14 + .../apps/services/RestExceptionHandlerTest.java | 362 +++++++++++++++++++++ services/src/test/resources/log4j2-test.xml | 14 + 23 files changed, 968 insertions(+), 22 deletions(-) create mode 100644 ms/sliboot/src/main/templates/api.mustache create mode 100644 services/src/main/java/org/onap/ccsdk/apps/services/RestApplicationError.java create mode 100644 services/src/main/java/org/onap/ccsdk/apps/services/RestApplicationException.java create mode 100644 services/src/main/java/org/onap/ccsdk/apps/services/RestError.java create mode 100644 services/src/main/java/org/onap/ccsdk/apps/services/RestErrors.java create mode 100644 services/src/main/java/org/onap/ccsdk/apps/services/RestException.java create mode 100644 services/src/main/java/org/onap/ccsdk/apps/services/RestExceptionHandler.java create mode 100644 services/src/main/java/org/onap/ccsdk/apps/services/RestProtocolError.java create mode 100644 services/src/main/java/org/onap/ccsdk/apps/services/RestProtocolException.java create mode 100644 services/src/main/java/org/onap/ccsdk/apps/services/RestRpcError.java create mode 100644 services/src/main/java/org/onap/ccsdk/apps/services/RestRpcException.java create mode 100644 services/src/main/java/org/onap/ccsdk/apps/services/RestTransportError.java create mode 100644 services/src/main/java/org/onap/ccsdk/apps/services/RestTransportException.java create mode 100644 services/src/test/java/org/onap/ccsdk/apps/services/RestExceptionHandlerTest.java create mode 100644 services/src/test/resources/log4j2-test.xml diff --git a/ms/sliboot/pom.xml b/ms/sliboot/pom.xml index 2d150805..213f02c6 100644 --- a/ms/sliboot/pom.xml +++ b/ms/sliboot/pom.xml @@ -210,6 +210,7 @@ true ${project.basedir}/.swagger-codegen-ignore true + ${project.basedir}/src/main/templates true 2.2.4-RELEASE diff --git a/ms/sliboot/src/main/dc/docker-compose.yaml b/ms/sliboot/src/main/dc/docker-compose.yaml index f48af3af..b1f25293 100755 --- a/ms/sliboot/src/main/dc/docker-compose.yaml +++ b/ms/sliboot/src/main/dc/docker-compose.yaml @@ -2,13 +2,16 @@ version: '2.1' services: db: - image: mariadb:10.3 + image: mariadb:10.5 container_name: sliboot_db_container ports: - "13306:3306" environment: - MYSQL_ROOT_PASSWORD=openECOMP1.0 - MYSQL_ROOT_HOST=% + - MYSQL_USER=sli + - MYSQL_PASSWORD=abc123 + - MYSQL_DATABASE=sdnctl logging: driver: "json-file" options: @@ -28,7 +31,6 @@ services: - db:dbhost environment: - MYSQL_DB_HOST=dbhost - - MYSQL_ROOT_PASSWORD=openECOMP1.0 - MYSQL_DB_USER=sli - MYSQL_DB_PASSWD=abc123 - MYSQL_DB_DATABASE=sdnctl diff --git a/ms/sliboot/src/main/java/org/onap/ccsdk/apps/ms/sliboot/App.java b/ms/sliboot/src/main/java/org/onap/ccsdk/apps/ms/sliboot/App.java index d2ef5e77..b93c2f9f 100644 --- a/ms/sliboot/src/main/java/org/onap/ccsdk/apps/ms/sliboot/App.java +++ b/ms/sliboot/src/main/java/org/onap/ccsdk/apps/ms/sliboot/App.java @@ -35,8 +35,7 @@ import org.onap.aaf.cadi.shiro.AAFRealm; @SpringBootApplication @EnableSwagger2 -@ComponentScan(basePackages = { "org.onap.ccsdk.apps.ms.sliboot.*" }) - +@ComponentScan(basePackages = { "org.onap.ccsdk.apps.ms.sliboot.*", "org.onap.ccsdk.apps.services" }) public class App { private static final Logger log = LoggerFactory.getLogger(App.class); diff --git a/ms/sliboot/src/main/java/org/onap/ccsdk/apps/ms/sliboot/WebConfig.java b/ms/sliboot/src/main/java/org/onap/ccsdk/apps/ms/sliboot/WebConfig.java index 3b0b2a20..1cef3161 100644 --- a/ms/sliboot/src/main/java/org/onap/ccsdk/apps/ms/sliboot/WebConfig.java +++ b/ms/sliboot/src/main/java/org/onap/ccsdk/apps/ms/sliboot/WebConfig.java @@ -28,10 +28,9 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -@EnableWebMvc @Configuration @EnableJpaRepositories("org.onap.ccsdk.apps.ms.sliboot.*") -@ComponentScan(basePackages = {"org.onap.ccsdk.apps.ms.sliboot.*"}) +@ComponentScan(basePackages = {"org.onap.ccsdk.apps.ms.sliboot.*", "org.onap.ccsdk.apps.services"}) @EntityScan("org.onap.ccsdk.apps.ms.sliboot.*") @EnableTransactionManagement public class WebConfig implements WebMvcConfigurer { diff --git a/ms/sliboot/src/main/java/org/onap/ccsdk/apps/ms/sliboot/controllers/RestconfApiController.java b/ms/sliboot/src/main/java/org/onap/ccsdk/apps/ms/sliboot/controllers/RestconfApiController.java index 4b78b2d1..f37fe137 100644 --- a/ms/sliboot/src/main/java/org/onap/ccsdk/apps/ms/sliboot/controllers/RestconfApiController.java +++ b/ms/sliboot/src/main/java/org/onap/ccsdk/apps/ms/sliboot/controllers/RestconfApiController.java @@ -29,6 +29,9 @@ import org.onap.ccsdk.apps.ms.sliboot.swagger.OperationalApi; import org.onap.ccsdk.apps.ms.sliboot.swagger.OperationsApi; import org.onap.ccsdk.apps.ms.sliboot.data.TestResultsOperationalRepository; import org.onap.ccsdk.apps.ms.sliboot.swagger.model.*; +import org.onap.ccsdk.apps.services.RestApplicationException; +import org.onap.ccsdk.apps.services.RestException; +import org.onap.ccsdk.apps.services.RestProtocolException; import org.onap.ccsdk.sli.core.sli.SvcLogicContext; import org.onap.ccsdk.sli.core.sli.SvcLogicException; import org.onap.ccsdk.sli.core.sli.provider.base.SvcLogicServiceBase; @@ -42,6 +45,7 @@ import org.springframework.context.annotation.ComponentScan; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; @@ -49,7 +53,7 @@ import java.util.*; @javax.annotation.Generated(value = "io.swagger.codegen.languages.SpringCodegen", date = "2020-02-20T12:50:11.207-05:00") -@Controller +@RestController @ComponentScan(basePackages = {"org.onap.ccsdk.apps.ms.sliboot.*", "org.onap.ccsdk.apps.services"}) @EntityScan("org.onap.ccsdk.apps.ms.sliboot.*") public class RestconfApiController implements ConfigApi, OperationalApi, OperationsApi { @@ -273,7 +277,7 @@ public class RestconfApiController implements ConfigApi, OperationalApi, Operati } @Override - public ResponseEntity configSLIAPItestResultsGet() { + public ResponseEntity configSLIAPItestResultsGet() throws RestException { if(getObjectMapper().isPresent() && getAcceptHeader().isPresent()) { } else { @@ -282,6 +286,10 @@ public class RestconfApiController implements ConfigApi, OperationalApi, Operati SliApiTestResults results = new SliApiTestResults(); + if (testResultsConfigRepository.count() == 0) { + throw new RestApplicationException("data-missing", "Request could not be completed because the relevant data model content does not exist", 404); + } + testResultsConfigRepository.findAll().forEach(testResult -> { SliApiTestresultsTestResult item = null; try { diff --git a/ms/sliboot/src/main/resources/startSliboot.sh b/ms/sliboot/src/main/resources/startSliboot.sh index 3ca2ad6d..a4cbd25c 100644 --- a/ms/sliboot/src/main/resources/startSliboot.sh +++ b/ms/sliboot/src/main/resources/startSliboot.sh @@ -42,17 +42,17 @@ echo -e "\nDatabase ready" # Create tablespace and user account -mysql -h ${MYSQL_DB_HOST} -u root -p${MYSQL_ROOT_PASSWORD} mysql <<-END -CREATE DATABASE ${MYSQL_DB_DATABASE}; -CREATE USER '${MYSQL_DB_USER}'@'localhost' IDENTIFIED BY '${MYSQL_DB_PASSWD}'; -CREATE USER '${MYSQL_DB_USER}'@'%' IDENTIFIED BY '${MYSQL_DB_PASSWD}'; -GRANT ALL PRIVILEGES ON ${MYSQL_DB_DATABASE}.* TO '${MYSQL_DB_USER}'@'localhost' WITH GRANT OPTION; -GRANT ALL PRIVILEGES ON ${MYSQL_DB_DATABASE}.* TO '${MYSQL_DB_USER}'@'%' WITH GRANT OPTION; -commit; -END +#mysql -h ${MYSQL_DB_HOST} -u root -p${MYSQL_ROOT_PASSWORD} mysql <<-END +#CREATE DATABASE ${MYSQL_DB_DATABASE} IF NOT EXISTS; +#CREATE USER '${MYSQL_DB_USER}'@'localhost' IDENTIFIED BY '${MYSQL_DB_PASSWD}'; +#CREATE USER '${MYSQL_DB_USER}'@'%' IDENTIFIED BY '${MYSQL_DB_PASSWD}'; +#GRANT ALL PRIVILEGES ON ${MYSQL_DB_DATABASE}.* TO '${MYSQL_DB_USER}'@'localhost' WITH GRANT OPTION; +#GRANT ALL PRIVILEGES ON ${MYSQL_DB_DATABASE}.* TO '${MYSQL_DB_USER}'@'%' WITH GRANT OPTION; +#commit; +#END # Initialize schema -mysql -h ${MYSQL_DB_HOST} -u ${MYSQL_DB_USER} -p${MYSQL_DB_PASSWD} ${MYSQL_DB_DATABASE} < ${CCSDK_HOME}/config/schema.sql +# mysql -h ${MYSQL_DB_HOST} -u ${MYSQL_DB_USER} -p${MYSQL_DB_PASSWD} ${MYSQL_DB_DATABASE} < ${CCSDK_HOME}/config/schema.sql echo -e "\nCerts ready" diff --git a/ms/sliboot/src/main/templates/api.mustache b/ms/sliboot/src/main/templates/api.mustache new file mode 100644 index 00000000..c28642cf --- /dev/null +++ b/ms/sliboot/src/main/templates/api.mustache @@ -0,0 +1,136 @@ +/** +* NOTE: This class is auto generated by the swagger code generator program ({{{generatorVersion}}}). +* https://github.com/swagger-api/swagger-codegen +* Do not edit the class manually. +*/ +package {{package}}; + +{{#imports}}import {{import}}; +{{/imports}} +{{#jdk8-no-delegate}} + import com.fasterxml.jackson.databind.ObjectMapper; +{{/jdk8-no-delegate}} +import io.swagger.annotations.*; +{{#jdk8-no-delegate}} + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import org.springframework.http.HttpStatus; +{{/jdk8-no-delegate}} +import org.springframework.http.ResponseEntity; +{{#useBeanValidation}} + import org.springframework.validation.annotation.Validated; +{{/useBeanValidation}} +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +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.RequestPart; +import org.springframework.web.multipart.MultipartFile; +import org.onap.ccsdk.apps.services.RestException; + +{{#jdk8-no-delegate}} + import javax.servlet.http.HttpServletRequest; +{{/jdk8-no-delegate}} +{{#useBeanValidation}} + import javax.validation.Valid; + import javax.validation.constraints.*; +{{/useBeanValidation}} +{{#jdk8-no-delegate}} + import java.io.IOException; +{{/jdk8-no-delegate}} +import java.util.List; +{{#jdk8-no-delegate}} + import java.util.Optional; +{{/jdk8-no-delegate}} +{{^jdk8-no-delegate}} + {{#useOptional}} + import java.util.Optional; + {{/useOptional}} +{{/jdk8-no-delegate}} +{{#async}} + import java.util.concurrent.{{^jdk8}}Callable{{/jdk8}}{{#jdk8}}CompletableFuture{{/jdk8}}; +{{/async}} +{{>generatedAnnotation}} +@Api(value = "{{{baseName}}}", description = "the {{{baseName}}} API") +{{#operations}} + public interface {{classname}} { + {{#jdk8}} + + {{^isDelegate}} + Logger log = LoggerFactory.getLogger({{classname}}.class); + + default Optional getObjectMapper() { + return Optional.empty(); + } + + default Optional getRequest() { + return Optional.empty(); + } + + default Optional getAcceptHeader() { + return getRequest().map(r -> r.getHeader("Accept")); + } + {{/isDelegate}} + {{#isDelegate}} + {{classname}}Delegate getDelegate(); + {{/isDelegate}} + {{/jdk8}} + {{#operation}} + + @ApiOperation(value = "{{{summary}}}", nickname = "{{{operationId}}}", notes = "{{{notes}}}"{{#returnBaseType}}, response = {{{returnBaseType}}}.class{{/returnBaseType}}{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}}{{#hasAuthMethods}}, authorizations = { + {{#authMethods}}@Authorization(value = "{{name}}"{{#isOAuth}}, scopes = { + {{#scopes}}@AuthorizationScope(scope = "{{scope}}", description = "{{description}}"){{#hasMore}}, + {{/hasMore}}{{/scopes}} + }{{/isOAuth}}){{#hasMore}}, + {{/hasMore}}{{/authMethods}} + }{{/hasAuthMethods}}, tags={ {{#vendorExtensions.x-tags}}"{{tag}}",{{/vendorExtensions.x-tags}} }) + @ApiResponses(value = { {{#responses}} + @ApiResponse(code = {{{code}}}, message = "{{{message}}}"{{#baseType}}, response = {{{baseType}}}.class{{/baseType}}{{#containerType}}, responseContainer = "{{{containerType}}}"{{/containerType}}){{#hasMore}},{{/hasMore}}{{/responses}} }) + {{#implicitHeaders}} + @ApiImplicitParams({ + {{#headerParams}} + {{>implicitHeader}} + {{/headerParams}} + }) + {{/implicitHeaders}} + @RequestMapping(value = "{{{path}}}",{{#singleContentTypes}} + produces = "{{{vendorExtensions.x-accepts}}}", + consumes = "{{{vendorExtensions.x-contentType}}}",{{/singleContentTypes}}{{^singleContentTypes}}{{#hasProduces}} + produces = { {{#produces}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/produces}} }, {{/hasProduces}}{{#hasConsumes}} + consumes = { {{#consumes}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/consumes}} },{{/hasConsumes}}{{/singleContentTypes}} + method = RequestMethod.{{httpMethod}}) + {{#jdk8}}default {{/jdk8}}{{#responseWrapper}}{{.}}<{{/responseWrapper}}ResponseEntity<{{>returnTypes}}>{{#responseWrapper}}>{{/responseWrapper}} {{#delegate-method}}_{{/delegate-method}}{{operationId}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{#hasMore}},{{/hasMore}}{{/allParams}}){{^jdk8}};{{/jdk8}}{{#jdk8}} throws RestException { + {{#delegate-method}} + return {{operationId}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); + } + + // Override this method + default {{#responseWrapper}}{{.}}<{{/responseWrapper}}ResponseEntity<{{>returnTypes}}>{{#responseWrapper}}>{{/responseWrapper}} {{operationId}}({{#allParams}}{{^isFile}}{{{dataType}}}{{/isFile}}{{#isFile}}MultipartFile{{/isFile}} {{paramName}}{{#hasMore}},{{/hasMore}}{{/allParams}}) throws RestException { + {{/delegate-method}} + {{^isDelegate}} + if(getObjectMapper().isPresent() && getAcceptHeader().isPresent()) { + {{#examples}} + if (getAcceptHeader().get().contains("{{{contentType}}}")) { + try { + return {{#async}}CompletableFuture.completedFuture({{/async}}new ResponseEntity<>(getObjectMapper().get().readValue("{{#lambdaRemoveLineBreak}}{{#lambdaEscapeDoubleQuote}}{{{example}}}{{/lambdaEscapeDoubleQuote}}{{/lambdaRemoveLineBreak}}", {{>exampleReturnTypes}}.class), HttpStatus.NOT_IMPLEMENTED){{#async}}){{/async}}; + } catch (IOException e) { + log.error("Couldn't serialize response for content type {{{contentType}}}", e); + return {{#async}}CompletableFuture.completedFuture({{/async}}new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR){{#async}}){{/async}}; + } + } + {{/examples}} + } else { + log.warn("ObjectMapper or HttpServletRequest not configured in default {{classname}} interface so no example is generated"); + } + return {{#async}}CompletableFuture.completedFuture({{/async}}new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED){{#async}}){{/async}}; + {{/isDelegate}} + {{#isDelegate}} + return getDelegate().{{operationId}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); + {{/isDelegate}} + }{{/jdk8}} + + {{/operation}} + } +{{/operations}} \ No newline at end of file diff --git a/ms/sliboot/src/test/java/org/onap/ccsdk/apps/ms/sliboot/RestconfApiControllerTest.java b/ms/sliboot/src/test/java/org/onap/ccsdk/apps/ms/sliboot/RestconfApiControllerTest.java index 4a7a7ec8..7d380887 100644 --- a/ms/sliboot/src/test/java/org/onap/ccsdk/apps/ms/sliboot/RestconfApiControllerTest.java +++ b/ms/sliboot/src/test/java/org/onap/ccsdk/apps/ms/sliboot/RestconfApiControllerTest.java @@ -123,14 +123,15 @@ public class RestconfApiControllerTest { public void testTestResultAdd() throws Exception { String url = "/config/SLI-API:test-results/"; - MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.get(url).contentType(MediaType.APPLICATION_JSON_VALUE)).andReturn(); - - assertEquals(200, mvcResult.getResponse().getStatus()); - // Delete any existing content before testing insert - mvcResult = mvc.perform(MockMvcRequestBuilders.delete(url).contentType(MediaType.APPLICATION_JSON)).andReturn(); + MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.delete(url).contentType(MediaType.APPLICATION_JSON)).andReturn(); assertEquals(200, mvcResult.getResponse().getStatus()); + mvcResult = mvc.perform(MockMvcRequestBuilders.get(url).contentType(MediaType.APPLICATION_JSON_VALUE)).andReturn(); + + assertEquals(404, mvcResult.getResponse().getStatus()); + log.info("Empty test-results returns error - {}", mvcResult.getResponse().getContentAsString()); + String jsonString = "{\n" + " \"test-result\" : [\n" + " {\n" + @@ -146,6 +147,7 @@ public class RestconfApiControllerTest { mvcResult = mvc.perform(MockMvcRequestBuilders.get(url).contentType(MediaType.APPLICATION_JSON)).andReturn(); assertEquals(200, mvcResult.getResponse().getStatus()); + ObjectMapper objectMapper = new ObjectMapper(); SliApiTestResults testResults = objectMapper.readValue(mvcResult.getResponse().getContentAsString(), SliApiTestResults.class); assertNotNull(testResults); diff --git a/services/pom.xml b/services/pom.xml index d7e60f83..004e55e0 100644 --- a/services/pom.xml +++ b/services/pom.xml @@ -39,6 +39,12 @@ org.springframework.boot spring-boot-starter-test test + + + org.springframework.boot + spring-boot-starter-logging + + org.springframework.boot @@ -96,6 +102,10 @@ javax.ws.rs javax.ws.rs-api + + javax.servlet + javax.servlet-api + org.onap.ccsdk.sli.core sliPluginUtils-provider @@ -116,6 +126,11 @@ org.glassfish.jersey.inject jersey-hk2 + + com.fasterxml.jackson.core + jackson-databind + test + diff --git a/services/src/main/java/org/onap/ccsdk/apps/services/RestApplicationError.java b/services/src/main/java/org/onap/ccsdk/apps/services/RestApplicationError.java new file mode 100644 index 00000000..e63e1d91 --- /dev/null +++ b/services/src/main/java/org/onap/ccsdk/apps/services/RestApplicationError.java @@ -0,0 +1,31 @@ +package org.onap.ccsdk.apps.services; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.fasterxml.jackson.annotation.JsonValue; + + +@JsonRootName(value="error") +public class RestApplicationError extends RestError { + + + public RestApplicationError() { + this.errorType = "application"; + } + + public RestApplicationError(String errorTag, String errorMessage) { + this.errorType = "application"; + this.errorTag = errorTag; + this.errorMessage = errorMessage; + this.errorInfo = errorMessage; + } + + public RestApplicationError(String errorTag, String errorMessage, Throwable t) { + this.errorType = "application"; + this.errorTag = errorTag; + this.errorMessage = errorMessage; + this.errorInfo = t.getLocalizedMessage(); + } + + +} diff --git a/services/src/main/java/org/onap/ccsdk/apps/services/RestApplicationException.java b/services/src/main/java/org/onap/ccsdk/apps/services/RestApplicationException.java new file mode 100644 index 00000000..73613399 --- /dev/null +++ b/services/src/main/java/org/onap/ccsdk/apps/services/RestApplicationException.java @@ -0,0 +1,14 @@ +package org.onap.ccsdk.apps.services; + +public class RestApplicationException extends RestException { + + public RestApplicationException(String errorTag, String errorMessage, int status) { + this.restError = new RestApplicationError(errorTag, errorMessage); + this.status = status; + } + + public RestApplicationException(String errorTag, String errorMessage, Throwable t, int status) { + this.restError = new RestApplicationError(errorTag, errorMessage, t); + this.status = status; + } +} diff --git a/services/src/main/java/org/onap/ccsdk/apps/services/RestError.java b/services/src/main/java/org/onap/ccsdk/apps/services/RestError.java new file mode 100644 index 00000000..d2e4c7ab --- /dev/null +++ b/services/src/main/java/org/onap/ccsdk/apps/services/RestError.java @@ -0,0 +1,57 @@ +package org.onap.ccsdk.apps.services; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; + +@JsonRootName(value="error") +public abstract class RestError { + + protected String errorType; + protected String errorTag; + protected String errorPath; + protected String errorMessage; + protected String errorInfo; + + + @JsonProperty("error-type") + public String getErrorType() { + return errorType; + } + + @JsonProperty("error-tag") + public String getErrorTag() { + return errorTag; + } + + public void setErrorTag(String errorTag) { + this.errorTag = errorTag; + } + + @JsonProperty("error-path") + public String getErrorPath() { + return errorPath; + } + + public void setErrorPath(String errorPath) { + this.errorPath = errorPath; + } + + @JsonProperty("error-message") + public String getErrorMessage() { + return errorMessage; + } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + + @JsonProperty("error-info") + public String getErrorInfo() { + return errorInfo; + } + + public void setErrorInfo(String errorInfo) { + this.errorInfo = errorInfo; + } + +} diff --git a/services/src/main/java/org/onap/ccsdk/apps/services/RestErrors.java b/services/src/main/java/org/onap/ccsdk/apps/services/RestErrors.java new file mode 100644 index 00000000..5fac3618 --- /dev/null +++ b/services/src/main/java/org/onap/ccsdk/apps/services/RestErrors.java @@ -0,0 +1,28 @@ +package org.onap.ccsdk.apps.services; + +import com.fasterxml.jackson.annotation.JsonRootName; + +import java.util.LinkedList; +import java.util.List; + +@JsonRootName(value="errors") +public class RestErrors { + List errors; + + public RestErrors() + { + this.errors = new LinkedList(); + } + + public RestErrors(RestError error) { + this.errors = new LinkedList(); + errors.add(error); + } + public void addError(RestError error) { + errors.add(error); + } + + public List getErrors() { + return errors; + } +} diff --git a/services/src/main/java/org/onap/ccsdk/apps/services/RestException.java b/services/src/main/java/org/onap/ccsdk/apps/services/RestException.java new file mode 100644 index 00000000..52eab71b --- /dev/null +++ b/services/src/main/java/org/onap/ccsdk/apps/services/RestException.java @@ -0,0 +1,14 @@ +package org.onap.ccsdk.apps.services; + +abstract public class RestException extends Exception { + protected RestError restError; + protected int status; + + public int getStatus() { + return status; + } + + public RestError getRestError() { + return restError; + } +} diff --git a/services/src/main/java/org/onap/ccsdk/apps/services/RestExceptionHandler.java b/services/src/main/java/org/onap/ccsdk/apps/services/RestExceptionHandler.java new file mode 100644 index 00000000..c517f402 --- /dev/null +++ b/services/src/main/java/org/onap/ccsdk/apps/services/RestExceptionHandler.java @@ -0,0 +1,131 @@ +package org.onap.ccsdk.apps.services; + +import org.jvnet.hk2.annotations.Service; +import org.springframework.beans.ConversionNotSupportedException; +import org.springframework.beans.TypeMismatchException; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.http.converter.HttpMessageNotWritableException; +import org.springframework.validation.BindException; +import org.springframework.web.HttpMediaTypeNotAcceptableException; +import org.springframework.web.HttpMediaTypeNotSupportedException; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingPathVariableException; +import org.springframework.web.bind.MissingServletRequestParameterException; +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.context.request.async.AsyncRequestTimeoutException; +import org.springframework.web.multipart.support.MissingServletRequestPartException; +import org.springframework.web.servlet.NoHandlerFoundException; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; + +import javax.servlet.http.HttpServletRequest; + +@Order(Ordered.HIGHEST_PRECEDENCE) +@RestControllerAdvice +public class RestExceptionHandler extends ResponseEntityExceptionHandler { + + @Override + protected ResponseEntity handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + return createResponseEntity(new RestProtocolError("bad-method", "Method not supported", ex), status); + } + + @Override + protected ResponseEntity handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + return createResponseEntity(new RestProtocolError("bad-media-type", "Media type not supported", ex), status); + } + + @Override + protected ResponseEntity handleHttpMediaTypeNotAcceptable(HttpMediaTypeNotAcceptableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + return createResponseEntity(new RestProtocolError("bad-media-type", "Media type not acceptable", ex), status); + } + + @Override + protected ResponseEntity handleMissingPathVariable(MissingPathVariableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + return createResponseEntity(new RestApplicationError("missing-path", "Missing path variable", ex), status); + } + + @Override + protected ResponseEntity handleMissingServletRequestParameter(MissingServletRequestParameterException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + return createResponseEntity(new RestProtocolError("missing-param", "Missing servlet request parameter", ex), status); + } + + @Override + protected ResponseEntity handleServletRequestBindingException(ServletRequestBindingException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + return createResponseEntity(new RestApplicationError("request-binding", "Servlet binding exception", ex), status); + } + + @Override + protected ResponseEntity handleConversionNotSupported(ConversionNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + return createResponseEntity(new RestApplicationError("conversion", "Conversion not supported", ex), status); + } + + @Override + protected ResponseEntity handleTypeMismatch(TypeMismatchException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + return createResponseEntity(new RestProtocolError("type-mismatch", "Type mismatch", ex), status); + } + + @Override + protected ResponseEntity handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + return createResponseEntity(new RestProtocolError("bad-message", "HTTP message not readable", ex), status); + } + + @Override + protected ResponseEntity handleHttpMessageNotWritable(HttpMessageNotWritableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + return createResponseEntity(new RestProtocolError("bad-message", "HTTP message not writable", ex), status); + } + + @Override + protected ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + return createResponseEntity(new RestProtocolError("bad-message", "Method argument not valid", ex), status); + } + + @Override + protected ResponseEntity handleMissingServletRequestPart(MissingServletRequestPartException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + return createResponseEntity(new RestProtocolError("bad-message", "Missing servlet request part", ex), status); + } + + @Override + protected ResponseEntity handleBindException(BindException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + return createResponseEntity(new RestApplicationError("binding-error", "Missing servlet request part", ex), status); + } + + @Override + protected ResponseEntity handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + return createResponseEntity(new RestApplicationError("binding-error", "No handler found", ex), status); + } + + @Override + protected ResponseEntity handleAsyncRequestTimeoutException(AsyncRequestTimeoutException ex, HttpHeaders headers, HttpStatus status, WebRequest webRequest) { + return createResponseEntity(new RestApplicationError("timeout", "Async request timeout", ex), status); + } + + @Override + protected ResponseEntity handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) { + return createResponseEntity(new RestApplicationError("internal-error", "Internal error", ex), status); + } + + @ExceptionHandler({RestException.class}) + protected ResponseEntity handleRestException(RestException ex, HttpServletRequest request) { + RestError error = ex.getRestError(); + if (request != null) { + error.setErrorPath(request.getServletPath()); + } + return createResponseEntity(error,HttpStatus.valueOf(ex.getStatus())); + } + + private ResponseEntity createResponseEntity(RestError restError, HttpStatus status) { + + return new ResponseEntity<>(new RestErrors(restError), status); + } +} diff --git a/services/src/main/java/org/onap/ccsdk/apps/services/RestProtocolError.java b/services/src/main/java/org/onap/ccsdk/apps/services/RestProtocolError.java new file mode 100644 index 00000000..2e927b6a --- /dev/null +++ b/services/src/main/java/org/onap/ccsdk/apps/services/RestProtocolError.java @@ -0,0 +1,30 @@ +package org.onap.ccsdk.apps.services; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.fasterxml.jackson.annotation.JsonValue; + + +@JsonRootName(value="error") +public class RestProtocolError extends RestError { + + + public RestProtocolError() { + this.errorType = "protocol"; + } + + public RestProtocolError(String errorTag, String errorMessage) { + this.errorType = "protocol"; + this.errorTag = errorTag; + this.errorMessage = errorMessage; + this.errorInfo = errorMessage; + } + + public RestProtocolError(String errorTag, String errorMessage, Throwable t) { + this.errorType = "protocol"; + this.errorTag = errorTag; + this.errorMessage = errorMessage; + this.errorInfo = t.getLocalizedMessage(); + } + +} diff --git a/services/src/main/java/org/onap/ccsdk/apps/services/RestProtocolException.java b/services/src/main/java/org/onap/ccsdk/apps/services/RestProtocolException.java new file mode 100644 index 00000000..3de356bc --- /dev/null +++ b/services/src/main/java/org/onap/ccsdk/apps/services/RestProtocolException.java @@ -0,0 +1,14 @@ +package org.onap.ccsdk.apps.services; + +public class RestProtocolException extends RestException { + + public RestProtocolException(String errorTag, String errorMessage, int status) { + this.restError = new RestProtocolError(errorTag, errorMessage); + this.status = status; + } + + public RestProtocolException(String errorTag, String errorMessage, Throwable t, int status) { + this.restError = new RestProtocolError(errorTag, errorMessage, t); + this.status = status; + } +} diff --git a/services/src/main/java/org/onap/ccsdk/apps/services/RestRpcError.java b/services/src/main/java/org/onap/ccsdk/apps/services/RestRpcError.java new file mode 100644 index 00000000..63611d8d --- /dev/null +++ b/services/src/main/java/org/onap/ccsdk/apps/services/RestRpcError.java @@ -0,0 +1,31 @@ +package org.onap.ccsdk.apps.services; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.fasterxml.jackson.annotation.JsonValue; + + +@JsonRootName(value="error") +public class RestRpcError extends RestError { + + + public RestRpcError() { + this.errorType = "rpc"; + } + + public RestRpcError(String errorTag, String errorMessage) { + this.errorType = "rpc"; + this.errorTag = errorTag; + this.errorMessage = errorMessage; + this.errorInfo = errorMessage; + } + + public RestRpcError(String errorTag, String errorMessage, Throwable t) { + this.errorType = "rpc"; + this.errorTag = errorTag; + this.errorMessage = errorMessage; + this.errorInfo = t.getLocalizedMessage(); + } + + +} diff --git a/services/src/main/java/org/onap/ccsdk/apps/services/RestRpcException.java b/services/src/main/java/org/onap/ccsdk/apps/services/RestRpcException.java new file mode 100644 index 00000000..d69f3eb4 --- /dev/null +++ b/services/src/main/java/org/onap/ccsdk/apps/services/RestRpcException.java @@ -0,0 +1,14 @@ +package org.onap.ccsdk.apps.services; + +public class RestRpcException extends RestException { + + public RestRpcException(String errorTag, String errorMessage, int status) { + this.restError = new RestRpcError(errorTag, errorMessage); + this.status = status; + } + + public RestRpcException(String errorTag, String errorMessage, Throwable t, int status) { + this.restError = new RestRpcError(errorTag, errorMessage, t); + this.status = status; + } +} diff --git a/services/src/main/java/org/onap/ccsdk/apps/services/RestTransportError.java b/services/src/main/java/org/onap/ccsdk/apps/services/RestTransportError.java new file mode 100644 index 00000000..1fd539a6 --- /dev/null +++ b/services/src/main/java/org/onap/ccsdk/apps/services/RestTransportError.java @@ -0,0 +1,30 @@ +package org.onap.ccsdk.apps.services; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.fasterxml.jackson.annotation.JsonValue; + + +@JsonRootName(value = "error") +public class RestTransportError extends RestError { + + + public RestTransportError() { + this.errorType = "transport"; + } + + public RestTransportError(String errorTag, String errorMessage) { + this.errorType = "transport"; + this.errorTag = errorTag; + this.errorMessage = errorMessage; + this.errorInfo = errorMessage; + } + + public RestTransportError(String errorTag, String errorMessage, Throwable t) { + this.errorType = "transport"; + this.errorTag = errorTag; + this.errorMessage = errorMessage; + this.errorInfo = t.getLocalizedMessage(); + } + +} diff --git a/services/src/main/java/org/onap/ccsdk/apps/services/RestTransportException.java b/services/src/main/java/org/onap/ccsdk/apps/services/RestTransportException.java new file mode 100644 index 00000000..971ee9b7 --- /dev/null +++ b/services/src/main/java/org/onap/ccsdk/apps/services/RestTransportException.java @@ -0,0 +1,14 @@ +package org.onap.ccsdk.apps.services; + +public class RestTransportException extends RestException { + + public RestTransportException(String errorTag, String errorMessage, int status) { + this.restError = new RestTransportError(errorTag, errorMessage); + this.status = status; + } + + public RestTransportException(String errorTag, String errorMessage, Throwable t, int status) { + this.restError = new RestTransportError(errorTag, errorMessage, t); + this.status = status; + } +} diff --git a/services/src/test/java/org/onap/ccsdk/apps/services/RestExceptionHandlerTest.java b/services/src/test/java/org/onap/ccsdk/apps/services/RestExceptionHandlerTest.java new file mode 100644 index 00000000..242a6015 --- /dev/null +++ b/services/src/test/java/org/onap/ccsdk/apps/services/RestExceptionHandlerTest.java @@ -0,0 +1,362 @@ +package org.onap.ccsdk.apps.services; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.ConversionNotSupportedException; +import org.springframework.beans.TypeMismatchException; +import org.springframework.core.MethodParameter; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.http.converter.HttpMessageNotWritableException; +import org.springframework.validation.BindException; +import org.springframework.web.HttpMediaTypeNotAcceptableException; +import org.springframework.web.HttpMediaTypeNotSupportedException; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingPathVariableException; +import org.springframework.web.bind.MissingServletRequestParameterException; +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.context.request.async.AsyncRequestTimeoutException; +import org.springframework.web.multipart.support.MissingServletRequestPartException; +import org.springframework.web.servlet.NoHandlerFoundException; + +import java.util.Arrays; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + + + +public class RestExceptionHandlerTest { + private static final Logger log = LoggerFactory.getLogger("RestExceptionHandler"); + private class RestExceptionHandlerWrapper extends RestExceptionHandler { + @Override + public ResponseEntity handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + return super.handleHttpRequestMethodNotSupported(ex, headers, status, request); + } + + @Override + public ResponseEntity handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + return super.handleHttpMediaTypeNotSupported(ex, headers, status, request); + } + + @Override + public ResponseEntity handleHttpMediaTypeNotAcceptable(HttpMediaTypeNotAcceptableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + return super.handleHttpMediaTypeNotAcceptable(ex, headers, status, request); + } + + @Override + protected ResponseEntity handleMissingPathVariable(MissingPathVariableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + return super.handleMissingPathVariable(ex, headers, status, request); + } + + @Override + public ResponseEntity handleMissingServletRequestParameter(MissingServletRequestParameterException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + return super.handleMissingServletRequestParameter(ex, headers, status, request); + } + + @Override + public ResponseEntity handleServletRequestBindingException(ServletRequestBindingException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + return super.handleServletRequestBindingException(ex, headers, status, request); + } + + @Override + public ResponseEntity handleConversionNotSupported(ConversionNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + return super.handleConversionNotSupported(ex, headers, status, request); + } + + @Override + public ResponseEntity handleTypeMismatch(TypeMismatchException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + return super.handleTypeMismatch(ex, headers, status, request); + } + + @Override + public ResponseEntity handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + return super.handleHttpMessageNotReadable(ex, headers, status, request); + } + + @Override + public ResponseEntity handleHttpMessageNotWritable(HttpMessageNotWritableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + return super.handleHttpMessageNotWritable(ex, headers, status, request); + } + + @Override + public ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + return super.handleMethodArgumentNotValid(ex, headers, status, request); + } + + @Override + public ResponseEntity handleMissingServletRequestPart(MissingServletRequestPartException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + return super.handleMissingServletRequestPart(ex, headers, status, request); + } + + @Override + public ResponseEntity handleBindException(BindException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + return super.handleBindException(ex, headers, status, request); + } + + @Override + public ResponseEntity handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + return super.handleNoHandlerFoundException(ex, headers, status, request); + } + + @Override + public ResponseEntity handleAsyncRequestTimeoutException(AsyncRequestTimeoutException ex, HttpHeaders headers, HttpStatus status, WebRequest webRequest) { + return super.handleAsyncRequestTimeoutException(ex, headers, status, webRequest); + } + + @Override + public ResponseEntity handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) { + return super.handleExceptionInternal(ex, body, headers, status, request); + } + } + + RestExceptionHandler handler; + + @Before + public void setUp() { + handler = new RestExceptionHandlerWrapper(); + } + + @Test + public void handleHttpRequestMethodNotSupported() throws JsonProcessingException { + String[] supportedMethods = {"GET", "POST", "PUT", "DELETE"}; + + + ResponseEntity respEntity = handler.handleHttpRequestMethodNotSupported(new HttpRequestMethodNotSupportedException("PATCH", supportedMethods), + null, HttpStatus.METHOD_NOT_ALLOWED, null); + + assertTrue(respEntity.hasBody()); + assertTrue(respEntity.getBody() instanceof RestErrors); + + RestErrors restErrors = (RestErrors) respEntity.getBody(); + + ObjectMapper objectMapper = new ObjectMapper(); + log.info("response : {}", objectMapper.writeValueAsString(restErrors)); + + + } + + @Test + public void handleHttpMediaTypeNotSupported() throws JsonProcessingException { + MediaType[] supportedMediaTypeArray = { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}; + + ResponseEntity respEntity = handler.handleHttpMediaTypeNotSupported(new HttpMediaTypeNotSupportedException(MediaType.MULTIPART_MIXED, Arrays.asList(supportedMediaTypeArray)), + null, HttpStatus.UNSUPPORTED_MEDIA_TYPE, null); + + assertTrue(respEntity.hasBody()); + assertTrue(respEntity.getBody() instanceof RestErrors); + + RestErrors restErrors = (RestErrors) respEntity.getBody(); + ObjectMapper objectMapper = new ObjectMapper(); + log.info("response : {}", objectMapper.writeValueAsString(restErrors)); + } + + @Test + public void handleHttpMediaTypeNotAcceptable() throws JsonProcessingException { + MediaType[] supportedMediaTypeArray = { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}; + + ResponseEntity respEntity = handler.handleHttpMediaTypeNotAcceptable(new HttpMediaTypeNotAcceptableException(Arrays.asList(supportedMediaTypeArray)), + null, HttpStatus.UNSUPPORTED_MEDIA_TYPE, null); + + assertTrue(respEntity.hasBody()); + assertTrue(respEntity.getBody() instanceof RestErrors); + + RestErrors restErrors = (RestErrors) respEntity.getBody(); + + ObjectMapper objectMapper = new ObjectMapper(); + log.info("response : {}", objectMapper.writeValueAsString(restErrors)); + + } + + @Test + public void handleMissingPathVariable() throws JsonProcessingException { + ResponseEntity respEntity = handler.handleMissingPathVariable(new MissingPathVariableException("test", new MethodParameter(RestApplicationError.class.getDeclaredConstructors()[0],-1)), + null, HttpStatus.INTERNAL_SERVER_ERROR, null); + + assertTrue(respEntity.hasBody()); + assertTrue(respEntity.getBody() instanceof RestErrors); + + RestErrors restErrors = (RestErrors) respEntity.getBody(); + ObjectMapper objectMapper = new ObjectMapper(); + log.info("response : {}", objectMapper.writeValueAsString(restErrors)); + } + + @Test + public void handleMissingServletRequestParameter() throws JsonProcessingException { + ResponseEntity respEntity = handler.handleMissingServletRequestParameter(new MissingServletRequestParameterException("test", "string"), + null, HttpStatus.INTERNAL_SERVER_ERROR, null); + + assertTrue(respEntity.hasBody()); + assertTrue(respEntity.getBody() instanceof RestErrors); + + RestErrors restErrors = (RestErrors) respEntity.getBody(); + ObjectMapper objectMapper = new ObjectMapper(); + log.info("response : {}", objectMapper.writeValueAsString(restErrors)); + } + + @Test + public void handleServletRequestBindingException() throws JsonProcessingException { + ResponseEntity respEntity = handler.handleServletRequestBindingException(new ServletRequestBindingException("servlet request binding error"), + null, HttpStatus.INTERNAL_SERVER_ERROR, null); + + assertTrue(respEntity.hasBody()); + assertTrue(respEntity.getBody() instanceof RestErrors); + + RestErrors restErrors = (RestErrors) respEntity.getBody(); + ObjectMapper objectMapper = new ObjectMapper(); + log.info("response : {}", objectMapper.writeValueAsString(restErrors)); + } + + @Test + public void handleConversionNotSupported() throws JsonProcessingException { + ResponseEntity respEntity = handler.handleConversionNotSupported(new ConversionNotSupportedException("hello", Integer.class, new NumberFormatException()), + null, HttpStatus.INTERNAL_SERVER_ERROR, null); + + assertTrue(respEntity.hasBody()); + assertTrue(respEntity.getBody() instanceof RestErrors); + + RestErrors restErrors = (RestErrors) respEntity.getBody(); + ObjectMapper objectMapper = new ObjectMapper(); + log.info("response : {}", objectMapper.writeValueAsString(restErrors)); + } + + @Test + public void handleTypeMismatch() throws JsonProcessingException { + ResponseEntity respEntity = handler.handleTypeMismatch(new TypeMismatchException("hello", Integer.class, new NumberFormatException()), + null, HttpStatus.INTERNAL_SERVER_ERROR, null); + + assertTrue(respEntity.hasBody()); + assertTrue(respEntity.getBody() instanceof RestErrors); + + RestErrors restErrors = (RestErrors) respEntity.getBody(); + ObjectMapper objectMapper = new ObjectMapper(); + log.info("response : {}", objectMapper.writeValueAsString(restErrors)); + } + + @Test + public void handleHttpMessageNotReadable() throws JsonProcessingException { + ResponseEntity respEntity = handler.handleHttpMessageNotReadable(new HttpMessageNotReadableException("Message not readable"), + null, HttpStatus.INTERNAL_SERVER_ERROR, null); + + assertTrue(respEntity.hasBody()); + assertTrue(respEntity.getBody() instanceof RestErrors); + + RestErrors restErrors = (RestErrors) respEntity.getBody(); + ObjectMapper objectMapper = new ObjectMapper(); + log.info("response : {}", objectMapper.writeValueAsString(restErrors)); + } + + @Test + public void handleHttpMessageNotWritable() throws JsonProcessingException { + ResponseEntity respEntity = handler.handleHttpMessageNotWritable(new HttpMessageNotWritableException("Message not writable"), + null, HttpStatus.INTERNAL_SERVER_ERROR, null); + + assertTrue(respEntity.hasBody()); + assertTrue(respEntity.getBody() instanceof RestErrors); + + RestErrors restErrors = (RestErrors) respEntity.getBody(); + ObjectMapper objectMapper = new ObjectMapper(); + log.info("response : {}", objectMapper.writeValueAsString(restErrors)); + } + + @Test + public void handleMethodArgumentNotValid() throws JsonProcessingException { + ResponseEntity respEntity = handler.handleMethodArgumentNotValid(new MethodArgumentNotValidException(new MethodParameter(RestApplicationError.class.getDeclaredConstructors()[0],-1), + new BindException("target", "objectName")), + null, HttpStatus.INTERNAL_SERVER_ERROR, null); + + assertTrue(respEntity.hasBody()); + assertTrue(respEntity.getBody() instanceof RestErrors); + + RestErrors restErrors = (RestErrors) respEntity.getBody(); + ObjectMapper objectMapper = new ObjectMapper(); + log.info("response : {}", objectMapper.writeValueAsString(restErrors)); + } + + @Test + public void handleMissingServletRequestPart() throws JsonProcessingException { + ResponseEntity respEntity = handler.handleMissingServletRequestPart(new MissingServletRequestPartException("test"), + null, HttpStatus.INTERNAL_SERVER_ERROR, null); + + assertTrue(respEntity.hasBody()); + assertTrue(respEntity.getBody() instanceof RestErrors); + + RestErrors restErrors = (RestErrors) respEntity.getBody(); + ObjectMapper objectMapper = new ObjectMapper(); + log.info("response : {}", objectMapper.writeValueAsString(restErrors)); + } + + @Test + public void handleBindException() throws JsonProcessingException { + ResponseEntity respEntity = handler.handleBindException(new BindException("target", "objectName"), + null, HttpStatus.INTERNAL_SERVER_ERROR, null); + + assertTrue(respEntity.hasBody()); + assertTrue(respEntity.getBody() instanceof RestErrors); + + RestErrors restErrors = (RestErrors) respEntity.getBody(); + ObjectMapper objectMapper = new ObjectMapper(); + log.info("response : {}", objectMapper.writeValueAsString(restErrors)); + } + + @Test + public void handleNoHandlerFoundException() throws JsonProcessingException { + ResponseEntity respEntity = handler.handleNoHandlerFoundException(new NoHandlerFoundException("GET", "restconf/bogus", null), + null, HttpStatus.INTERNAL_SERVER_ERROR, null); + + assertTrue(respEntity.hasBody()); + assertTrue(respEntity.getBody() instanceof RestErrors); + + RestErrors restErrors = (RestErrors) respEntity.getBody(); + ObjectMapper objectMapper = new ObjectMapper(); + log.info("response : {}", objectMapper.writeValueAsString(restErrors)); + } + + @Test + public void handleAsyncRequestTimeoutException() throws JsonProcessingException { + ResponseEntity respEntity = handler.handleAsyncRequestTimeoutException(new AsyncRequestTimeoutException(), + null, HttpStatus.INTERNAL_SERVER_ERROR, null); + + assertTrue(respEntity.hasBody()); + assertTrue(respEntity.getBody() instanceof RestErrors); + + RestErrors restErrors = (RestErrors) respEntity.getBody(); + ObjectMapper objectMapper = new ObjectMapper(); + log.info("response : {}", objectMapper.writeValueAsString(restErrors)); + } + + @Test + public void handleExceptionInternal() throws JsonProcessingException { + ResponseEntity respEntity = handler.handleExceptionInternal(new NullPointerException(), null, + null, HttpStatus.INTERNAL_SERVER_ERROR, null); + + assertTrue(respEntity.hasBody()); + assertTrue(respEntity.getBody() instanceof RestErrors); + + RestErrors restErrors = (RestErrors) respEntity.getBody(); + ObjectMapper objectMapper = new ObjectMapper(); + log.info("response : {}", objectMapper.writeValueAsString(restErrors)); + } + + @Test + public void handleRestException() throws JsonProcessingException { + + ResponseEntity respEntity = handler.handleRestException(new RestApplicationException("no-data-found", "No data found", HttpStatus.NOT_FOUND.value()), null); + assertTrue(respEntity.hasBody()); + assertTrue(respEntity.getBody() instanceof RestErrors); + + RestErrors restErrors = (RestErrors) respEntity.getBody(); + ObjectMapper objectMapper = new ObjectMapper(); + log.info("response : {}", objectMapper.writeValueAsString(restErrors)); + } + +} \ No newline at end of file diff --git a/services/src/test/resources/log4j2-test.xml b/services/src/test/resources/log4j2-test.xml new file mode 100644 index 00000000..1408cf2a --- /dev/null +++ b/services/src/test/resources/log4j2-test.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + \ No newline at end of file -- 2.16.6