diff --git a/addons/common/rest-exception-handler/src/main/java/org/kie/kogito/resource/exceptions/BaseExceptionsHandler.java b/addons/common/rest-exception-handler/src/main/java/org/kie/kogito/resource/exceptions/BaseExceptionsHandler.java index 9c3f51d3b3a..30409c0f6a8 100644 --- a/addons/common/rest-exception-handler/src/main/java/org/kie/kogito/resource/exceptions/BaseExceptionsHandler.java +++ b/addons/common/rest-exception-handler/src/main/java/org/kie/kogito/resource/exceptions/BaseExceptionsHandler.java @@ -33,6 +33,7 @@ import org.kie.kogito.process.workitem.InvalidLifeCyclePhaseException; import org.kie.kogito.process.workitem.InvalidTransitionException; import org.kie.kogito.process.workitem.NotAuthorizedException; +import org.kie.kogito.process.workitem.WorkItemExecutionException; public abstract class BaseExceptionsHandler { @@ -48,9 +49,9 @@ public abstract class BaseExceptionsHandler { private static class FunctionHolder { private final Function contentGenerator; - private final Function responseGenerator; + private final Function> responseGenerator; - public FunctionHolder(Function contentGenerator, Function responseGenerator) { + public FunctionHolder(Function contentGenerator, Function> responseGenerator) { this.contentGenerator = contentGenerator; this.responseGenerator = responseGenerator; } @@ -59,20 +60,20 @@ public Function getContentGenerator() { return contentGenerator; } - public Function getResponseGenerator() { + public Function> getResponseGenerator() { return responseGenerator; } } - private final FunctionHolder defaultHolder = new FunctionHolder<>(ex -> ex, BaseExceptionsHandler.this::internalError); + private final FunctionHolder defaultHolder = new FunctionHolder<>(ex -> ex, ex -> BaseExceptionsHandler.this::internalError); protected BaseExceptionsHandler() { mapper = new HashMap<>(); mapper.put(InvalidLifeCyclePhaseException.class, new FunctionHolder<>( - ex -> Collections.singletonMap(MESSAGE, ex.getMessage()), BaseExceptionsHandler.this::badRequest)); + ex -> Collections.singletonMap(MESSAGE, ex.getMessage()), ex -> BaseExceptionsHandler.this::badRequest)); mapper.put(InvalidTransitionException.class, new FunctionHolder<>( - ex -> Collections.singletonMap(MESSAGE, ex.getMessage()), BaseExceptionsHandler.this::badRequest)); + ex -> Collections.singletonMap(MESSAGE, ex.getMessage()), ex -> BaseExceptionsHandler.this::badRequest)); mapper.put(NodeInstanceNotFoundException.class, new FunctionHolder<>( ex -> { @@ -82,7 +83,7 @@ protected BaseExceptionsHandler() { response.put(PROCESS_INSTANCE_ID, exception.getProcessInstanceId()); response.put(NODE_INSTANCE_ID, exception.getNodeInstanceId()); return response; - }, BaseExceptionsHandler.this::notFound)); + }, ex -> BaseExceptionsHandler.this::notFound)); mapper.put(NodeNotFoundException.class, new FunctionHolder<>( ex -> { @@ -92,10 +93,10 @@ protected BaseExceptionsHandler() { response.put(PROCESS_INSTANCE_ID, exception.getProcessInstanceId()); response.put(NODE_ID, exception.getNodeId()); return response; - }, BaseExceptionsHandler.this::notFound)); + }, ex -> BaseExceptionsHandler.this::notFound)); mapper.put(NotAuthorizedException.class, new FunctionHolder<>( - ex -> Collections.singletonMap(MESSAGE, ex.getMessage()), BaseExceptionsHandler.this::forbidden)); + ex -> Collections.singletonMap(MESSAGE, ex.getMessage()), ex -> BaseExceptionsHandler.this::forbidden)); mapper.put(ProcessInstanceDuplicatedException.class, new FunctionHolder<>( ex -> { @@ -104,7 +105,7 @@ protected BaseExceptionsHandler() { response.put(MESSAGE, exception.getMessage()); response.put(PROCESS_INSTANCE_ID, exception.getProcessInstanceId()); return response; - }, BaseExceptionsHandler.this::conflict)); + }, ex -> BaseExceptionsHandler.this::conflict)); mapper.put(ProcessInstanceExecutionException.class, new FunctionHolder<>( ex -> { @@ -114,7 +115,7 @@ protected BaseExceptionsHandler() { response.put(FAILED_NODE_ID, exception.getFailedNodeId()); response.put(MESSAGE, exception.getErrorMessage()); return response; - }, BaseExceptionsHandler.this::internalError)); + }, ex -> BaseExceptionsHandler.this::internalError)); mapper.put(ProcessInstanceNotFoundException.class, new FunctionHolder<>( ex -> { @@ -123,12 +124,12 @@ protected BaseExceptionsHandler() { response.put(MESSAGE, exception.getMessage()); response.put(PROCESS_INSTANCE_ID, exception.getProcessInstanceId()); return response; - }, BaseExceptionsHandler.this::notFound)); + }, ex -> BaseExceptionsHandler.this::notFound)); mapper.put(WorkItemNotFoundException.class, new FunctionHolder<>(ex -> { WorkItemNotFoundException exception = (WorkItemNotFoundException) ex; return Map.of(MESSAGE, exception.getMessage(), TASK_ID, exception.getWorkItemId()); - }, BaseExceptionsHandler.this::notFound)); + }, ex -> BaseExceptionsHandler.this::notFound)); mapper.put(VariableViolationException.class, new FunctionHolder<>( ex -> { @@ -138,9 +139,28 @@ protected BaseExceptionsHandler() { response.put(PROCESS_INSTANCE_ID, exception.getProcessInstanceId()); response.put(VARIABLE, exception.getVariableName()); return response; - }, BaseExceptionsHandler.this::badRequest)); + }, ex -> BaseExceptionsHandler.this::badRequest)); - mapper.put(IllegalArgumentException.class, new FunctionHolder<>(ex -> Collections.singletonMap(MESSAGE, ex.getMessage()), BaseExceptionsHandler.this::badRequest)); + mapper.put(WorkItemExecutionException.class, new FunctionHolder<>( + ex -> Map.of(MESSAGE, ex.getMessage()), + ex -> fromErrorCode(((WorkItemExecutionException) ex).getErrorCode()))); + + mapper.put(IllegalArgumentException.class, new FunctionHolder<>(ex -> Collections.singletonMap(MESSAGE, ex.getMessage()), ex -> BaseExceptionsHandler.this::badRequest)); + } + + private Function fromErrorCode(String errorCode) { + switch (errorCode) { + case "400": + return this::badRequest; + case "403": + return this::forbidden; + case "404": + return this::notFound; + case "409": + return this::conflict; + default: + return this::internalError; + } } protected abstract T badRequest(R body); @@ -156,8 +176,8 @@ protected BaseExceptionsHandler() { public T mapException(R exception) { FunctionHolder holder = (FunctionHolder) mapper.getOrDefault(exception.getClass(), defaultHolder); U body = holder.getContentGenerator().apply(exception); - if (exception instanceof ProcessInstanceExecutionException) { - Throwable rootCause = ((ProcessInstanceExecutionException) exception).getCause(); + if (exception instanceof ProcessInstanceExecutionException || exception instanceof WorkItemExecutionException) { + Throwable rootCause = exception.getCause(); while (rootCause != null) { if (mapper.containsKey(rootCause.getClass())) { @@ -167,6 +187,6 @@ public T mapException(R exception) { rootCause = rootCause.getCause(); } } - return holder.getResponseGenerator().apply(body); + return holder.getResponseGenerator().apply(exception).apply(body); } } diff --git a/quarkus/addons/rest-exception-handler/src/main/java/org/kie/kogito/resource/exceptions/WorkItemExecutionExceptionMapper.java b/quarkus/addons/rest-exception-handler/src/main/java/org/kie/kogito/resource/exceptions/WorkItemExecutionExceptionMapper.java new file mode 100644 index 00000000000..73bdac8cc7f --- /dev/null +++ b/quarkus/addons/rest-exception-handler/src/main/java/org/kie/kogito/resource/exceptions/WorkItemExecutionExceptionMapper.java @@ -0,0 +1,33 @@ +/* + * 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.kie.kogito.resource.exceptions; + +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.Provider; + +import org.kie.kogito.process.workitem.WorkItemExecutionException; + +@Provider +public class WorkItemExecutionExceptionMapper extends BaseExceptionMapper { + + @Override + public Response toResponse(WorkItemExecutionException exception) { + return exceptionsHandler.mapException(exception); + } +} diff --git a/springboot/addons/rest-exception-handler/src/main/java/org/kie/kogito/resource/exceptions/springboot/ExceptionsHandler.java b/springboot/addons/rest-exception-handler/src/main/java/org/kie/kogito/resource/exceptions/springboot/ExceptionsHandler.java index 524b613ea88..49ab414f0ab 100644 --- a/springboot/addons/rest-exception-handler/src/main/java/org/kie/kogito/resource/exceptions/springboot/ExceptionsHandler.java +++ b/springboot/addons/rest-exception-handler/src/main/java/org/kie/kogito/resource/exceptions/springboot/ExceptionsHandler.java @@ -28,6 +28,7 @@ import org.kie.kogito.process.workitem.InvalidLifeCyclePhaseException; import org.kie.kogito.process.workitem.InvalidTransitionException; import org.kie.kogito.process.workitem.NotAuthorizedException; +import org.kie.kogito.process.workitem.WorkItemExecutionException; import org.kie.kogito.resource.exceptions.BaseExceptionsHandler; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -123,6 +124,11 @@ public ResponseEntity toResponse(WorkItemNotFoundException exception) { return mapException(exception); } + @ExceptionHandler(WorkItemExecutionException.class) + public ResponseEntity toResponse(WorkItemExecutionException exception) { + return mapException(exception); + } + @ExceptionHandler(VariableViolationException.class) public ResponseEntity toResponse(VariableViolationException exception) { return mapException(exception);