diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/common/config/ErrorHandlingConfig.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/common/config/ErrorHandlingConfig.java index 1754be4a3b..f6810ef9e0 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/common/config/ErrorHandlingConfig.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/common/config/ErrorHandlingConfig.java @@ -22,6 +22,8 @@ package org.eclipse.tractusx.traceability.common.config; import assets.importpoc.ErrorResponse; +import assets.importpoc.IRSErrorResponse; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -57,6 +59,7 @@ import org.eclipse.tractusx.traceability.notification.domain.notification.exception.NotificationSenderAndReceiverBPNEqualException; import org.eclipse.tractusx.traceability.notification.domain.notification.exception.NotificationStatusTransitionNotAllowed; import org.eclipse.tractusx.traceability.submodel.domain.model.SubmodelNotFoundException; +import org.jetbrains.annotations.Nullable; import org.springframework.context.support.DefaultMessageSourceResolvable; import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatusCode; @@ -69,7 +72,6 @@ import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpServerErrorException; @@ -109,16 +111,64 @@ ResponseEntity handleHttpClientErrorException(HttpClientErrorExce String errorMessage; if (status.equals(BAD_REQUEST)) { - return ResponseEntity.status(BAD_REQUEST) + try { + ResponseEntity errorResponse = mapIRSBadRequestToErrorResponse(exception); + if (errorResponse != null) return errorResponse; + } + catch (Exception e) { + ResponseEntity body = ResponseEntity.status(BAD_REQUEST) .body(new ErrorResponse(exception.getMessage())); + return body; // Handle the case where the message cannot be mapped to IRSErrorResponse + } + } else if (status.equals(NOT_FOUND)) { - return ResponseEntity.status(NOT_FOUND) - .body(new ErrorResponse(exception.getMessage())); + try { + ResponseEntity errorResponse = mapIRSNotFoundToErrorResponse(exception); + if (errorResponse != null) return errorResponse; + } + catch (Exception e) { + ResponseEntity body = ResponseEntity.status(NOT_FOUND) + .body(new ErrorResponse(exception.getMessage())); + return body; // Handle the case where the message cannot be mapped to IRSErrorResponse + } } else { errorMessage = exception.getMessage(); return ResponseEntity.status(status) .body(new ErrorResponse(errorMessage)); } + return ResponseEntity.status(status).body(new ErrorResponse(exception.getMessage())); + } + + private @Nullable ResponseEntity mapIRSBadRequestToErrorResponse(HttpClientErrorException exception) throws JsonProcessingException { + String rawMessage = exception.getMessage().replaceAll("", ""); + // Extract the JSON part of the message + int jsonStart = rawMessage.indexOf("{"); + int jsonEnd = rawMessage.lastIndexOf("}"); + if (jsonStart != -1 && jsonEnd != -1) { + String jsonString = rawMessage.substring(jsonStart, jsonEnd + 1); + IRSErrorResponse badRequestResponse = objectMapper.readValue(jsonString, IRSErrorResponse.class); + if (badRequestResponse != null && badRequestResponse.getMessage() != null) { + ErrorResponse errorResponse = new ErrorResponse(badRequestResponse.getMessage()); + return ResponseEntity.status(400).body(errorResponse); + } + } + return null; + } + + private @Nullable ResponseEntity mapIRSNotFoundToErrorResponse(HttpClientErrorException exception) throws JsonProcessingException { + String rawMessage = exception.getMessage().replaceAll("", ""); + // Extract the JSON part of the message + int jsonStart = rawMessage.indexOf("{"); + int jsonEnd = rawMessage.lastIndexOf("}"); + if (jsonStart != -1 && jsonEnd != -1) { + String jsonString = rawMessage.substring(jsonStart, jsonEnd + 1); + IRSErrorResponse irsError = objectMapper.readValue(jsonString, IRSErrorResponse.class); + if (irsError != null && irsError.getMessage() != null) { + ErrorResponse errorResponse = new ErrorResponse(irsError.getMessage()); + return ResponseEntity.status(404).body(errorResponse); + } + } + return null; } @ExceptionHandler(PolicyBadRequestException.class) diff --git a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/common/support/IrsApiSupport.java b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/common/support/IrsApiSupport.java index 4eddd26b45..ceda511dee 100644 --- a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/common/support/IrsApiSupport.java +++ b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/common/support/IrsApiSupport.java @@ -113,6 +113,16 @@ public void irsApiReturnsPolicies() { ); } + public void irsApiReturnsPoliciesNotFound(String id) { + whenHttp(restitoProvider.stubServer()).match( + Condition.get("/irs/policies/" +id) + ).then( + Action.status(HttpStatus.NOT_FOUND_404), + Action.header("Content-Type", "application/json"), + restitoProvider.jsonResponseFromFile("./stubs/irs/policies/response_404_get_policyById.json") + ); + } + public void irsApiReturnsPolicyById(String policyId) { whenHttp(restitoProvider.stubServer()).match( Condition.get("/irs/policies/" + policyId) @@ -132,6 +142,15 @@ public void irsApiCreatesPolicy() { restitoProvider.jsonResponseFromFile("./stubs/irs/policies/response_200_createPolicy.json") ); } + public void irsApiCreatesPolicyBadRequest() { + whenHttp(restitoProvider.stubServer()).match( + Condition.post("/irs/policies") + ).then( + Action.status(HttpStatus.BAD_REQUEST_400), + Action.header("Content-Type", "application/json"), + restitoProvider.jsonResponseFromFile("./stubs/irs/policies/response_400_createPolicy.json") + ); + } public void irsApiUpdatesPolicy() { whenHttp(restitoProvider.stubServer()).match( diff --git a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/policy/PolicyControllerIT.java b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/policy/PolicyControllerIT.java index 22f6895b32..623098ca52 100644 --- a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/policy/PolicyControllerIT.java +++ b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/policy/PolicyControllerIT.java @@ -29,11 +29,12 @@ import policies.request.UpdatePolicyRequest; import java.time.Instant; -import java.time.LocalDateTime; +import java.util.Arrays; import java.util.List; import static io.restassured.RestAssured.given; import static org.eclipse.tractusx.traceability.common.security.JwtRole.ADMIN; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; class PolicyControllerIT extends IntegrationTestSpecification { @@ -74,6 +75,23 @@ void shouldReturnGetPolicyById() throws JoseException { .log().all(); } + @Test + void shouldReturnNotFoundGetPolicyById() throws JoseException { + // GIVEN + String policyId = "default-policy-not-exist"; + irsApiSupport.irsApiReturnsPoliciesNotFound(policyId); + + given() + .header(oAuth2Support.jwtAuthorization(ADMIN)) + .contentType(ContentType.JSON) + .when() + .get("/api/policies/" + policyId) + .then() + .statusCode(404) + .body(containsString("404 Not Found: [no body]")) + .log().all(); + } + @Test void shouldCreatePolicy() throws JoseException { // GIVEN @@ -93,6 +111,32 @@ void shouldCreatePolicy() throws JoseException { .log().all(); } + + @Test + void shouldThorwBadRequestCreatePolicy() throws JoseException { + // GIVEN + irsApiSupport.irsApiCreatesPolicyBadRequest(); + + // Sample data + List businessPartnerNumbers = Arrays.asList("BPN-123", "BPN-456"); + List policyIds = Arrays.asList("POLICY-789", "POLICY-101"); + Instant validUntil = Instant.parse("2025-12-31T23:59:59.00Z"); + + // WRONG REQUEST OBJECT + UpdatePolicyRequest request = new UpdatePolicyRequest(businessPartnerNumbers, policyIds, validUntil); + // when/then + given() + .header(oAuth2Support.jwtAuthorization(ADMIN)) + .contentType(ContentType.JSON) + .body(request) + .when() + .post("/api/policies") + .then() + .statusCode(400) + .body(containsString("Request does not contain all required fields. Missing: odrl:permission")) + .log().all(); + } + @Test void shouldUpdatePolicy() throws JoseException { // GIVEN diff --git a/tx-backend/src/test/resources/stubs/irs/policies/response_400_createPolicy.json b/tx-backend/src/test/resources/stubs/irs/policies/response_400_createPolicy.json new file mode 100644 index 0000000000..e2e36cc54e --- /dev/null +++ b/tx-backend/src/test/resources/stubs/irs/policies/response_400_createPolicy.json @@ -0,0 +1,4 @@ +{ + "message" : "Request does not contain all required fields. Missing: odrl:permission" +} + diff --git a/tx-backend/src/test/resources/stubs/irs/policies/response_400_get_policyById.json b/tx-backend/src/test/resources/stubs/irs/policies/response_400_get_policyById.json new file mode 100644 index 0000000000..1becca72c9 --- /dev/null +++ b/tx-backend/src/test/resources/stubs/irs/policies/response_400_get_policyById.json @@ -0,0 +1,7 @@ +{ + "statusCode": "BAD_REQUEST", + "error": "Bad Request", + "messages": [ + "Request does not contain all required fields. Missing: odrl:permission" + ] +} diff --git a/tx-backend/src/test/resources/stubs/irs/policies/response_404_get_policyById.json b/tx-backend/src/test/resources/stubs/irs/policies/response_404_get_policyById.json new file mode 100644 index 0000000000..6fae0461e2 --- /dev/null +++ b/tx-backend/src/test/resources/stubs/irs/policies/response_404_get_policyById.json @@ -0,0 +1,3 @@ +{ + "message": "404 : \"{\"statusCode\":\"NOT_FOUND\",\"error\":\"Not Found\",\"messages\":[\"Policy with id 'default-paafdsasfdolicy' not found\"]}\"" +} diff --git a/tx-models/src/main/java/assets/importpoc/IRSErrorResponse.java b/tx-models/src/main/java/assets/importpoc/IRSErrorResponse.java new file mode 100644 index 0000000000..a068072110 --- /dev/null +++ b/tx-models/src/main/java/assets/importpoc/IRSErrorResponse.java @@ -0,0 +1,30 @@ +/******************************************************************************** + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +package assets.importpoc; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class IRSErrorResponse { + private String message; +} +