diff --git a/pom.xml b/pom.xml
index 0a8f96e..57e456a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -50,6 +50,9 @@
gridsuite
org.gridsuite:computation
+
+
+ 1.34.0-SNAPSHOT
@@ -173,6 +176,7 @@
com.powsybl
powsybl-ws-commons
+ ${powsybl-ws-commons.version}
true
diff --git a/src/main/java/org/gridsuite/computation/ComputationException.java b/src/main/java/org/gridsuite/computation/ComputationException.java
deleted file mode 100644
index c19157f..0000000
--- a/src/main/java/org/gridsuite/computation/ComputationException.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
- * Copyright (c) 2025, RTE (http://www.rte-france.com)
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-package org.gridsuite.computation;
-
-import lombok.Getter;
-
-import java.util.Objects;
-
-/**
- * @author Anis Touri
- */
-@Getter
-public class ComputationException extends RuntimeException {
- public enum Type {
- RESULT_NOT_FOUND("Result not found."),
- INVALID_FILTER_FORMAT("The filter format is invalid."),
- INVALID_SORT_FORMAT("The sort format is invalid"),
- INVALID_FILTER("Invalid filter"),
- NETWORK_NOT_FOUND("Network not found"),
- PARAMETERS_NOT_FOUND("Computation parameters not found"),
- FILE_EXPORT_ERROR("Error exporting the file"),
- EVALUATE_FILTER_FAILED("Error evaluating the file"),
- LIMIT_REDUCTION_CONFIG_ERROR("Error int the configuration of the limit reduction"),
- SPECIFIC("Unknown error during the computation");
-
- private final String defaultMessage;
-
- Type(String defaultMessage) {
- this.defaultMessage = defaultMessage;
- }
- }
-
- private final Type exceptionType;
-
- public ComputationException(Type exceptionType) {
- super(Objects.requireNonNull(exceptionType.defaultMessage));
- this.exceptionType = Objects.requireNonNull(exceptionType);
- }
-
- public ComputationException(String message) {
- super(message);
- this.exceptionType = Type.SPECIFIC;
- }
-
- public ComputationException(String message, Throwable cause) {
- super(message, cause);
- this.exceptionType = Type.SPECIFIC;
- }
-
- public ComputationException(Type exceptionType, String message) {
- super(message);
- this.exceptionType = Objects.requireNonNull(exceptionType);
- }
-
- public ComputationException(Type exceptionType, String message, Throwable cause) {
- super(message, cause);
- this.exceptionType = Objects.requireNonNull(exceptionType);
- }
-}
diff --git a/src/main/java/org/gridsuite/computation/error/ComputationBusinessErrorCode.java b/src/main/java/org/gridsuite/computation/error/ComputationBusinessErrorCode.java
new file mode 100644
index 0000000..8345778
--- /dev/null
+++ b/src/main/java/org/gridsuite/computation/error/ComputationBusinessErrorCode.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+package org.gridsuite.computation.error;
+
+import com.powsybl.ws.commons.error.BusinessErrorCode;
+
+public enum ComputationBusinessErrorCode implements BusinessErrorCode {
+ RESULT_NOT_FOUND("computation.resultNotFound"),
+ PARAMETERS_NOT_FOUND("computation.parametersNotFound"),
+ INVALID_SORT_FORMAT("computation.invalidSortFormat"),
+ INVALID_EXPORT_PARAMS("computation.invalidExportParams"),
+ LIMIT_REDUCTION_CONFIG_ERROR("computation.limitReductionConfigError"),
+ RUNNER_ERROR("computation.runnerError");
+
+ private final String code;
+
+ ComputationBusinessErrorCode(String code) {
+ this.code = code;
+ }
+
+ public String value() {
+ return code;
+ }
+}
diff --git a/src/main/java/org/gridsuite/computation/error/ComputationException.java b/src/main/java/org/gridsuite/computation/error/ComputationException.java
new file mode 100644
index 0000000..71e54ef
--- /dev/null
+++ b/src/main/java/org/gridsuite/computation/error/ComputationException.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+package org.gridsuite.computation.error;
+
+import com.powsybl.ws.commons.error.AbstractBusinessException;
+import lombok.Getter;
+import lombok.NonNull;
+
+import java.util.Objects;
+
+/**
+ * @author Anis Touri
+ */
+@Getter
+public class ComputationException extends AbstractBusinessException {
+
+ private final ComputationBusinessErrorCode errorCode;
+
+ @NonNull
+ @Override
+ public ComputationBusinessErrorCode getBusinessErrorCode() {
+ return errorCode;
+ }
+
+ public ComputationException(ComputationBusinessErrorCode errorCode, String message, Throwable cause) {
+ super(message, cause);
+ this.errorCode = errorCode;
+ }
+
+ public ComputationException(ComputationBusinessErrorCode errorCode, String message) {
+ super(message);
+ this.errorCode = Objects.requireNonNull(errorCode);
+ }
+}
diff --git a/src/main/java/org/gridsuite/computation/error/ComputationRestResponseEntityExceptionHandler.java b/src/main/java/org/gridsuite/computation/error/ComputationRestResponseEntityExceptionHandler.java
new file mode 100644
index 0000000..9eab305
--- /dev/null
+++ b/src/main/java/org/gridsuite/computation/error/ComputationRestResponseEntityExceptionHandler.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+package org.gridsuite.computation.error;
+
+import com.powsybl.ws.commons.error.AbstractBusinessExceptionHandler;
+import com.powsybl.ws.commons.error.ServerNameProvider;
+import lombok.NonNull;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+
+/**
+ * @author Hugo Marcellin
+ */
+
+@ControllerAdvice
+public class ComputationRestResponseEntityExceptionHandler extends AbstractBusinessExceptionHandler {
+
+ protected ComputationRestResponseEntityExceptionHandler(ServerNameProvider serverNameProvider) {
+ super(serverNameProvider);
+ }
+
+ @Override
+ protected @NonNull ComputationBusinessErrorCode getBusinessCode(ComputationException e) {
+ return e.getBusinessErrorCode();
+ }
+
+ @Override
+ protected HttpStatus mapStatus(ComputationBusinessErrorCode businessErrorCode) {
+ return switch (businessErrorCode) {
+ case RESULT_NOT_FOUND, PARAMETERS_NOT_FOUND -> HttpStatus.NOT_FOUND;
+ case INVALID_SORT_FORMAT, INVALID_EXPORT_PARAMS -> HttpStatus.BAD_REQUEST;
+ default -> HttpStatus.INTERNAL_SERVER_ERROR;
+ };
+ }
+}
diff --git a/src/main/java/org/gridsuite/computation/service/AbstractWorkerService.java b/src/main/java/org/gridsuite/computation/service/AbstractWorkerService.java
index 77d9140..2dfdddb 100644
--- a/src/main/java/org/gridsuite/computation/service/AbstractWorkerService.java
+++ b/src/main/java/org/gridsuite/computation/service/AbstractWorkerService.java
@@ -16,7 +16,8 @@
import com.powsybl.network.store.client.PreloadingStrategy;
import com.powsybl.ws.commons.ZipUtils;
import org.apache.commons.lang3.StringUtils;
-import org.gridsuite.computation.ComputationException;
+import org.gridsuite.computation.error.ComputationBusinessErrorCode;
+import org.gridsuite.computation.error.ComputationException;
import org.gridsuite.computation.s3.ComputationS3Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -180,7 +181,7 @@ public Consumer> consumeRun() {
} catch (Exception e) {
resultService.delete(resultContext.getResultUuid());
this.handleNonCancellationException(resultContext, e, rootReporter);
- throw new ComputationException(String.format("%s: %s", NotificationService.getFailedMessage(getComputationType()), e.getMessage()), e.getCause());
+ throw new ComputationException(ComputationBusinessErrorCode.RUNNER_ERROR, String.format("%s: %s", NotificationService.getFailedMessage(getComputationType()), e.getMessage()), e.getCause());
} finally {
if (Boolean.TRUE.equals(resultContext.getRunContext().getDebug())) {
processDebug(resultContext);
diff --git a/src/main/java/org/gridsuite/computation/utils/FilterUtils.java b/src/main/java/org/gridsuite/computation/utils/FilterUtils.java
index 04416f3..6cead98 100644
--- a/src/main/java/org/gridsuite/computation/utils/FilterUtils.java
+++ b/src/main/java/org/gridsuite/computation/utils/FilterUtils.java
@@ -10,10 +10,10 @@
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
-import org.gridsuite.computation.ComputationException;
import org.gridsuite.computation.dto.GlobalFilter;
import org.gridsuite.computation.dto.ResourceFilterDTO;
+import java.io.UncheckedIOException;
import java.util.List;
/**
@@ -33,7 +33,7 @@ private static T fromStringToDTO(String jsonString, ObjectMapper objectMappe
try {
return objectMapper.readValue(jsonString, typeReference);
} catch (JsonProcessingException e) {
- throw new ComputationException(ComputationException.Type.INVALID_FILTER_FORMAT);
+ throw new UncheckedIOException(e);
}
}
diff --git a/src/test/java/org/gridsuite/computation/ComputationBusinessErrorCodeTest.java b/src/test/java/org/gridsuite/computation/ComputationBusinessErrorCodeTest.java
new file mode 100644
index 0000000..796f73c
--- /dev/null
+++ b/src/test/java/org/gridsuite/computation/ComputationBusinessErrorCodeTest.java
@@ -0,0 +1,21 @@
+/**
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+package org.gridsuite.computation;
+
+import org.gridsuite.computation.error.ComputationBusinessErrorCode;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+
+class ComputationBusinessErrorCodeTest {
+ @ParameterizedTest
+ @EnumSource(ComputationBusinessErrorCode.class)
+ void valueMatchesEnumName(ComputationBusinessErrorCode code) {
+ assertThat(code.value()).startsWith("computation.");
+ }
+}
diff --git a/src/test/java/org/gridsuite/computation/ComputationExceptionTest.java b/src/test/java/org/gridsuite/computation/ComputationExceptionTest.java
index fb9b670..75a2c37 100644
--- a/src/test/java/org/gridsuite/computation/ComputationExceptionTest.java
+++ b/src/test/java/org/gridsuite/computation/ComputationExceptionTest.java
@@ -6,8 +6,11 @@
*/
package org.gridsuite.computation;
+import org.gridsuite.computation.error.ComputationException;
import org.junit.jupiter.api.Test;
+import static org.gridsuite.computation.error.ComputationBusinessErrorCode.PARAMETERS_NOT_FOUND;
+import static org.gridsuite.computation.error.ComputationBusinessErrorCode.RUNNER_ERROR;
import static org.junit.jupiter.api.Assertions.*;
/**
@@ -16,16 +19,18 @@
class ComputationExceptionTest {
@Test
- void testMessageConstructor() {
- var e = new ComputationException("test");
+ void testMessageAndThrowableConstructor() {
+ var cause = new RuntimeException("test");
+ var e = new ComputationException(RUNNER_ERROR, "test", cause);
+ assertEquals(RUNNER_ERROR, e.getBusinessErrorCode());
assertEquals("test", e.getMessage());
+ assertEquals(cause, e.getCause());
}
@Test
- void testMessageAndThrowableConstructor() {
- var cause = new RuntimeException("test");
- var e = new ComputationException("test", cause);
+ void testBusinessErrorCodeConstructor() {
+ var e = new ComputationException(PARAMETERS_NOT_FOUND, "test");
assertEquals("test", e.getMessage());
- assertEquals(cause, e.getCause());
+ assertEquals(PARAMETERS_NOT_FOUND, e.getBusinessErrorCode());
}
}
diff --git a/src/test/java/org/gridsuite/computation/ComputationTest.java b/src/test/java/org/gridsuite/computation/ComputationTest.java
index d3b110f..4719575 100644
--- a/src/test/java/org/gridsuite/computation/ComputationTest.java
+++ b/src/test/java/org/gridsuite/computation/ComputationTest.java
@@ -26,6 +26,7 @@
import lombok.extern.slf4j.Slf4j;
import org.assertj.core.api.WithAssertions;
import org.gridsuite.computation.dto.ReportInfos;
+import org.gridsuite.computation.error.ComputationException;
import org.gridsuite.computation.s3.ComputationS3Service;
import org.gridsuite.computation.s3.S3InputStreamInfos;
import org.gridsuite.computation.service.*;
diff --git a/src/test/java/org/gridsuite/computation/error/RestResponseEntityExceptionHandlerTest.java b/src/test/java/org/gridsuite/computation/error/RestResponseEntityExceptionHandlerTest.java
new file mode 100644
index 0000000..f4c8ac7
--- /dev/null
+++ b/src/test/java/org/gridsuite/computation/error/RestResponseEntityExceptionHandlerTest.java
@@ -0,0 +1,52 @@
+/**
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+package org.gridsuite.computation.error;
+
+import com.powsybl.ws.commons.error.PowsyblWsProblemDetail;
+import org.gridsuite.computation.error.utils.TestRestResponseEntityExceptionHandler;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.mock.web.MockHttpServletRequest;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.gridsuite.computation.error.ComputationBusinessErrorCode.INVALID_SORT_FORMAT;
+import static org.gridsuite.computation.error.ComputationBusinessErrorCode.RESULT_NOT_FOUND;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class RestResponseEntityExceptionHandlerTest {
+
+ private TestRestResponseEntityExceptionHandler handler;
+
+ @BeforeEach
+ void setUp() {
+ handler = new TestRestResponseEntityExceptionHandler();
+ }
+
+ @Test
+ void mapsNotFoundBusinessErrorToStatus() {
+ MockHttpServletRequest request = new MockHttpServletRequest("GET", "/results-endpoint/uuid");
+ ComputationException exception = new ComputationException(RESULT_NOT_FOUND, "Result not found");
+ ResponseEntity response = handler.invokeHandleDomainException(exception, request);
+
+ assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
+ assertThat(response.getBody()).isNotNull();
+ assertEquals("computation.resultNotFound", response.getBody().getBusinessErrorCode());
+ }
+
+ @Test
+ void mapsBadRequestBusinessErrorToStatus() {
+ MockHttpServletRequest request = new MockHttpServletRequest("POST", "/results-endpoint/uuid");
+ ComputationException exception = new ComputationException(INVALID_SORT_FORMAT, "Invalid sort format");
+ ResponseEntity response = handler.invokeHandleDomainException(exception, request);
+
+ assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
+ assertThat(response.getBody()).isNotNull();
+ assertEquals("computation.invalidSortFormat", response.getBody().getBusinessErrorCode());
+ }
+}
diff --git a/src/test/java/org/gridsuite/computation/error/utils/TestRestResponseEntityExceptionHandler.java b/src/test/java/org/gridsuite/computation/error/utils/TestRestResponseEntityExceptionHandler.java
new file mode 100644
index 0000000..6ca094d
--- /dev/null
+++ b/src/test/java/org/gridsuite/computation/error/utils/TestRestResponseEntityExceptionHandler.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+package org.gridsuite.computation.error.utils;
+
+import com.powsybl.ws.commons.error.PowsyblWsProblemDetail;
+import org.gridsuite.computation.error.ComputationException;
+import org.gridsuite.computation.error.ComputationRestResponseEntityExceptionHandler;
+import org.springframework.http.ResponseEntity;
+import org.springframework.mock.web.MockHttpServletRequest;
+
+/**
+ * @author Hugo Marcellin
+ */
+public class TestRestResponseEntityExceptionHandler extends ComputationRestResponseEntityExceptionHandler {
+ public TestRestResponseEntityExceptionHandler() {
+ super(() -> "computation-server");
+ }
+
+ public ResponseEntity invokeHandleDomainException(ComputationException exception, MockHttpServletRequest request) {
+ return super.handleDomainException(exception, request);
+ }
+}
diff --git a/src/test/java/org/gridsuite/computation/utils/FilterUtilsTest.java b/src/test/java/org/gridsuite/computation/utils/FilterUtilsTest.java
index a392c7a..12a0e70 100644
--- a/src/test/java/org/gridsuite/computation/utils/FilterUtilsTest.java
+++ b/src/test/java/org/gridsuite/computation/utils/FilterUtilsTest.java
@@ -10,20 +10,16 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.powsybl.iidm.network.Country;
import com.powsybl.security.LimitViolationType;
-import org.gridsuite.computation.ComputationException;
import org.gridsuite.computation.dto.GlobalFilter;
import org.gridsuite.computation.dto.ResourceFilterDTO;
import org.junit.jupiter.api.Test;
+import java.io.UncheckedIOException;
import java.util.List;
import java.util.Map;
import java.util.UUID;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.*;
/**
* @author Franck Lecuyer
@@ -73,7 +69,7 @@ void testFromStringGlobalFiltersToDTO() throws Exception {
@Test
void testInvalidFilterFormat() {
- assertThrows(ComputationException.class, () -> FilterUtils.fromStringGlobalFiltersToDTO("titi", objectMapper), "The filter format is invalid.");
+ assertThrows(UncheckedIOException.class, () -> FilterUtils.fromStringGlobalFiltersToDTO("titi", objectMapper), "The filter format is invalid.");
}
@Test