From ad27b01f93708076d22656bb74fd4668d215204f Mon Sep 17 00:00:00 2001 From: Abdullah Habes <33485177+AHabes@users.noreply.github.com> Date: Thu, 5 Oct 2023 13:33:16 -0400 Subject: [PATCH] Update scan and file Rest endpoints to return proper error codes (#123) --- .../mapper/DataCollectionFileMapper.java | 13 +++++ .../model/DataCollectionFileDTO.java | 14 ++++++ .../rest/DataCollectionFileController.java | 24 ++++++++++ .../DataCollectionFileControllerImpl.java | 21 ++++---- .../scanManager/rest/EMAControllerImpl.java | 48 +++++++++---------- .../scanManager/rest/RestErrorHandler.java | 28 ++++++----- .../scanManager/rest/EMAControllerTest.java | 25 ++++------ .../route/exceptions/ClientException.java | 6 ++- 8 files changed, 114 insertions(+), 65 deletions(-) create mode 100644 service/application/src/main/java/com/solace/maas/ep/event/management/agent/scanManager/mapper/DataCollectionFileMapper.java create mode 100644 service/application/src/main/java/com/solace/maas/ep/event/management/agent/scanManager/model/DataCollectionFileDTO.java create mode 100644 service/application/src/main/java/com/solace/maas/ep/event/management/agent/scanManager/rest/DataCollectionFileController.java diff --git a/service/application/src/main/java/com/solace/maas/ep/event/management/agent/scanManager/mapper/DataCollectionFileMapper.java b/service/application/src/main/java/com/solace/maas/ep/event/management/agent/scanManager/mapper/DataCollectionFileMapper.java new file mode 100644 index 00000000..6f89a5dc --- /dev/null +++ b/service/application/src/main/java/com/solace/maas/ep/event/management/agent/scanManager/mapper/DataCollectionFileMapper.java @@ -0,0 +1,13 @@ +package com.solace.maas.ep.event.management.agent.scanManager.mapper; + +import com.solace.maas.ep.event.management.agent.scanManager.model.DataCollectionFileBO; +import com.solace.maas.ep.event.management.agent.scanManager.model.DataCollectionFileDTO; +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; + +@Mapper(componentModel = "spring", collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED) +public interface DataCollectionFileMapper { + DataCollectionFileBO map(DataCollectionFileDTO dto); + + DataCollectionFileDTO map(DataCollectionFileBO bo); +} diff --git a/service/application/src/main/java/com/solace/maas/ep/event/management/agent/scanManager/model/DataCollectionFileDTO.java b/service/application/src/main/java/com/solace/maas/ep/event/management/agent/scanManager/model/DataCollectionFileDTO.java new file mode 100644 index 00000000..81e27a8d --- /dev/null +++ b/service/application/src/main/java/com/solace/maas/ep/event/management/agent/scanManager/model/DataCollectionFileDTO.java @@ -0,0 +1,14 @@ +package com.solace.maas.ep.event.management.agent.scanManager.model; + +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class DataCollectionFileDTO { + private String id; + + private String path; + + private boolean purged; +} diff --git a/service/application/src/main/java/com/solace/maas/ep/event/management/agent/scanManager/rest/DataCollectionFileController.java b/service/application/src/main/java/com/solace/maas/ep/event/management/agent/scanManager/rest/DataCollectionFileController.java new file mode 100644 index 00000000..fd4f26a9 --- /dev/null +++ b/service/application/src/main/java/com/solace/maas/ep/event/management/agent/scanManager/rest/DataCollectionFileController.java @@ -0,0 +1,24 @@ +package com.solace.maas.ep.event.management.agent.scanManager.rest; + +import com.solace.maas.ep.event.management.agent.scanManager.model.DataCollectionFileDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestParam; + +public interface DataCollectionFileController { + + @Operation( + summary = "Retrieves the details about scan files", + description = "Use this API to retrieve the details about the files generated by a scan request.", + responses = { + @ApiResponse( + responseCode = "200", + description = "Details about the scan files have been retrieved successfully." + ) + } + ) + ResponseEntity> list(@RequestParam("scanId") String scanId, Pageable pageable); +} diff --git a/service/application/src/main/java/com/solace/maas/ep/event/management/agent/scanManager/rest/DataCollectionFileControllerImpl.java b/service/application/src/main/java/com/solace/maas/ep/event/management/agent/scanManager/rest/DataCollectionFileControllerImpl.java index 2e6a3c0f..83d34c81 100644 --- a/service/application/src/main/java/com/solace/maas/ep/event/management/agent/scanManager/rest/DataCollectionFileControllerImpl.java +++ b/service/application/src/main/java/com/solace/maas/ep/event/management/agent/scanManager/rest/DataCollectionFileControllerImpl.java @@ -1,34 +1,39 @@ package com.solace.maas.ep.event.management.agent.scanManager.rest; import com.solace.maas.ep.event.management.agent.constants.RestEndpoint; -import com.solace.maas.ep.event.management.agent.scanManager.model.DataCollectionFileBO; +import com.solace.maas.ep.event.management.agent.scanManager.mapper.DataCollectionFileMapper; +import com.solace.maas.ep.event.management.agent.scanManager.model.DataCollectionFileDTO; import com.solace.maas.ep.event.management.agent.service.DataCollectionFileService; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.bind.annotation.CrossOrigin; @Slf4j @Validated @CrossOrigin @RestController @RequestMapping(RestEndpoint.DATA_COLLECTION_FILE_URL) -public class DataCollectionFileControllerImpl { - private final DataCollectionFileService dataCollectionFileService; +public class DataCollectionFileControllerImpl implements DataCollectionFileController { + private final DataCollectionFileService dataCollectionFileService; + private final DataCollectionFileMapper dataCollectionFileMapper; - public DataCollectionFileControllerImpl(DataCollectionFileService dataCollectionFileService) { + public DataCollectionFileControllerImpl(DataCollectionFileService dataCollectionFileService, + DataCollectionFileMapper dataCollectionFileMapper) { this.dataCollectionFileService = dataCollectionFileService; + this.dataCollectionFileMapper = dataCollectionFileMapper; } + @Override @GetMapping("/query") - public ResponseEntity> list(@RequestParam("scanId") String scanId, - Pageable pageable) { - return ResponseEntity.ok().body(dataCollectionFileService.findByScanId(scanId, pageable)); + public ResponseEntity> list(@RequestParam("scanId") String scanId, Pageable pageable) { + return ResponseEntity.ok().body(dataCollectionFileService.findByScanId(scanId, pageable) + .map(dataCollectionFileMapper::map)); } } diff --git a/service/application/src/main/java/com/solace/maas/ep/event/management/agent/scanManager/rest/EMAControllerImpl.java b/service/application/src/main/java/com/solace/maas/ep/event/management/agent/scanManager/rest/EMAControllerImpl.java index 2f6dc165..92b6383b 100644 --- a/service/application/src/main/java/com/solace/maas/ep/event/management/agent/scanManager/rest/EMAControllerImpl.java +++ b/service/application/src/main/java/com/solace/maas/ep/event/management/agent/scanManager/rest/EMAControllerImpl.java @@ -3,13 +3,14 @@ import com.solace.maas.ep.common.model.ScanRequestDTO; import com.solace.maas.ep.event.management.agent.config.eventPortal.EventPortalProperties; import com.solace.maas.ep.event.management.agent.constants.RestEndpoint; +import com.solace.maas.ep.event.management.agent.plugin.route.exceptions.ClientException; import com.solace.maas.ep.event.management.agent.scanManager.ScanManager; import com.solace.maas.ep.event.management.agent.scanManager.mapper.ScanRequestMapper; import com.solace.maas.ep.event.management.agent.scanManager.model.ScanRequestBO; import com.solace.maas.ep.event.management.agent.util.IDGenerator; +import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.CrossOrigin; @@ -19,7 +20,6 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import jakarta.validation.Valid; import java.util.List; @Slf4j @@ -47,34 +47,30 @@ public EMAControllerImpl(ScanRequestMapper scanRequestMapper, ScanManager scanMa @PostMapping(value = "/{resourceId}/scan") public ResponseEntity scan(@PathVariable(value = "resourceId") String messagingServiceId, @RequestBody @Valid ScanRequestDTO body) { - try { - ScanRequestBO scanRequestBO = scanRequestMapper.map(body); - scanRequestBO.setMessagingServiceId(messagingServiceId); - scanRequestBO.setScanId(idGenerator.generateRandomUniqueId()); - - boolean isEMAStandalone = eventPortalProperties.getGateway().getMessaging().isStandalone(); - List destinations = scanRequestBO.getDestinations(); - - if (!isEMAStandalone) { - throw new RestErrorHandler.RestException("Scan requests via REST endpoint could not be initiated in connected mode."); - } + ScanRequestBO scanRequestBO = scanRequestMapper.map(body); + scanRequestBO.setMessagingServiceId(messagingServiceId); + scanRequestBO.setScanId(idGenerator.generateRandomUniqueId()); - if (destinations.contains("EVENT_PORTAL")) { - throw new RestErrorHandler.RestException("Scan data could not be streamed to EP in standalone mode."); - } + boolean isEMAStandalone = eventPortalProperties.getGateway().getMessaging().isStandalone(); + List destinations = scanRequestBO.getDestinations(); - log.info("Scan request [{}]: Received, request details: {}", scanRequestBO.getScanId(), scanRequestBO); - - String scanId = scanManager.scan(scanRequestBO); + if (!isEMAStandalone) { + throw ClientException.builder() + .message("Scan requests via REST endpoint could not be initiated in connected mode.") + .build(); + } + if (destinations.contains("EVENT_PORTAL")) { + throw ClientException.builder() + .message("Scan data could not be streamed to the Event Portal in standalone mode.") + .build(); + } - String message = String.format("Scan request [%s]: Scan started.", scanId); - log.info(message); + log.info("Scan request [{}]: Received, request details: {}", scanRequestBO.getScanId(), scanRequestBO); - return ResponseEntity.ok().body(message); + String scanId = scanManager.scan(scanRequestBO); + String message = String.format("Scan request [%s]: Scan started.", scanId); + log.info(message); - } catch (Exception e) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST) - .body(e.getMessage()); - } + return ResponseEntity.ok().body(message); } } diff --git a/service/application/src/main/java/com/solace/maas/ep/event/management/agent/scanManager/rest/RestErrorHandler.java b/service/application/src/main/java/com/solace/maas/ep/event/management/agent/scanManager/rest/RestErrorHandler.java index ad763a16..ed56adff 100644 --- a/service/application/src/main/java/com/solace/maas/ep/event/management/agent/scanManager/rest/RestErrorHandler.java +++ b/service/application/src/main/java/com/solace/maas/ep/event/management/agent/scanManager/rest/RestErrorHandler.java @@ -1,29 +1,33 @@ package com.solace.maas.ep.event.management.agent.scanManager.rest; import com.solace.maas.ep.common.model.EventErrorDTO; +import com.solace.maas.ep.event.management.agent.plugin.route.exceptions.ClientException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.data.mapping.PropertyReferenceException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; +@Slf4j +@Order(Ordered.HIGHEST_PRECEDENCE) +@RestControllerAdvice public class RestErrorHandler { - @ExceptionHandler({RestException.class}) + @ExceptionHandler(ClientException.class) @ResponseBody - public ResponseEntity handleRestException(RestException restException) { + public ResponseEntity handleRestException(ClientException restException) { EventErrorDTO eventErrorDTO = new EventErrorDTO(restException.getMessage()); return new ResponseEntity<>(eventErrorDTO, HttpStatus.BAD_REQUEST); } - @ResponseStatus(value = HttpStatus.BAD_REQUEST) - public static class RestException extends RuntimeException { - public RestException(String message) { - super(message); - } - - public RestException(String message, Exception cause) { - super(message, cause); - } + @ExceptionHandler(PropertyReferenceException.class) + @ResponseBody + public ResponseEntity handlePropertyReferenceException(PropertyReferenceException exception) { + EventErrorDTO eventErrorDTO = new EventErrorDTO(exception.getMessage()); + return new ResponseEntity<>(eventErrorDTO, HttpStatus.BAD_REQUEST); } } diff --git a/service/application/src/test/java/com/solace/maas/ep/event/management/agent/scanManager/rest/EMAControllerTest.java b/service/application/src/test/java/com/solace/maas/ep/event/management/agent/scanManager/rest/EMAControllerTest.java index 65103661..e250d258 100644 --- a/service/application/src/test/java/com/solace/maas/ep/event/management/agent/scanManager/rest/EMAControllerTest.java +++ b/service/application/src/test/java/com/solace/maas/ep/event/management/agent/scanManager/rest/EMAControllerTest.java @@ -5,13 +5,12 @@ import com.solace.maas.ep.event.management.agent.config.eventPortal.EventPortalProperties; import com.solace.maas.ep.event.management.agent.config.eventPortal.GatewayMessagingProperties; import com.solace.maas.ep.event.management.agent.config.eventPortal.GatewayProperties; +import com.solace.maas.ep.event.management.agent.plugin.route.exceptions.ClientException; import com.solace.maas.ep.event.management.agent.scanManager.ScanManager; import com.solace.maas.ep.event.management.agent.scanManager.mapper.ScanRequestMapper; import com.solace.maas.ep.event.management.agent.scanManager.model.ScanRequestBO; import com.solace.maas.ep.event.management.agent.util.IDGenerator; -import org.junit.Rule; import org.junit.jupiter.api.Test; -import org.junit.rules.ExpectedException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.HttpStatus; @@ -22,14 +21,14 @@ import static org.assertj.core.api.Assertions.assertThatNoException; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.Assert.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @ActiveProfiles("TEST") @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = TestConfig.class) public class EMAControllerTest { - @Rule - public ExpectedException exception = ExpectedException.none(); @Autowired private ScanRequestMapper scanRequestMapper; @@ -57,16 +56,12 @@ public void testEMAControllerInConnectedMode() { when(scanManager.scan(scanRequestBO)) .thenReturn("scanId"); - EMAController controller = - new EMAControllerImpl(scanRequestMapper, scanManager, idGenerator, eventPortalProperties); - - ResponseEntity reply = controller.scan("id", scanRequestDTO); + EMAController controller = new EMAControllerImpl(scanRequestMapper, scanManager, idGenerator, eventPortalProperties); // Scan requests via REST endpoints are prevented in connected mode, e.g., standalone=false - assertThat(reply.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); - assertThat(reply.getBody()).contains("Scan requests via REST endpoint could not be initiated in connected mode."); + ClientException thrown = assertThrows(ClientException.class, () -> controller.scan("id", scanRequestDTO)); - exception.expect(Exception.class); + assertTrue(thrown.getMessage().contains("Scan requests via REST endpoint could not be initiated in connected mode.")); } @Test @@ -93,13 +88,9 @@ public void testEMAControllerInStandAloneModeWithEventPortalDestination() { EMAController controller = new EMAControllerImpl(scanRequestMapper, scanManager, idGenerator, eventPortalProperties); - ResponseEntity reply = - controller.scan("id", scanRequestDTO); - - assertThat(reply.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); - assertThat(reply.getBody()).contains("Scan data could not be streamed to EP in standalone mode."); + ClientException thrown = assertThrows(ClientException.class, () -> controller.scan("id", scanRequestDTO)); - exception.expect(Exception.class); + assertThat(thrown.getMessage()).contains("Scan data could not be streamed to the Event Portal in standalone mode."); } @Test diff --git a/service/plugin/src/main/java/com/solace/maas/ep/event/management/agent/plugin/route/exceptions/ClientException.java b/service/plugin/src/main/java/com/solace/maas/ep/event/management/agent/plugin/route/exceptions/ClientException.java index 740212e2..503598a6 100644 --- a/service/plugin/src/main/java/com/solace/maas/ep/event/management/agent/plugin/route/exceptions/ClientException.java +++ b/service/plugin/src/main/java/com/solace/maas/ep/event/management/agent/plugin/route/exceptions/ClientException.java @@ -1,6 +1,7 @@ package com.solace.maas.ep.event.management.agent.plugin.route.exceptions; import com.solace.maas.ep.event.management.agent.plugin.jacoco.ExcludeFromJacocoGeneratedReport; +import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; @@ -10,9 +11,10 @@ @EqualsAndHashCode(callSuper = true) @Data @ExcludeFromJacocoGeneratedReport +@Builder public class ClientException extends RuntimeException { - private String message; - private Map> exceptionStore; + private final String message; + private final Map> exceptionStore; ClientException(final String message, final Map> validationDetails) { super();