diff --git a/simulator-spring-boot/src/main/java/org/citrusframework/simulator/repository/ScenarioExecutionRepository.java b/simulator-spring-boot/src/main/java/org/citrusframework/simulator/repository/ScenarioExecutionRepository.java index f9e5928b..71b10778 100644 --- a/simulator-spring-boot/src/main/java/org/citrusframework/simulator/repository/ScenarioExecutionRepository.java +++ b/simulator-spring-boot/src/main/java/org/citrusframework/simulator/repository/ScenarioExecutionRepository.java @@ -36,13 +36,13 @@ public interface ScenarioExecutionRepository extends JpaRepository, JpaSpecificationExecutor { @Override - @EntityGraph(attributePaths = {"testResult", "scenarioParameters", "scenarioActions", "scenarioMessages"}) + @EntityGraph(attributePaths = {"testResult", "scenarioParameters", "scenarioActions", "scenarioMessages", "scenarioMessages.headers"}) Page findAll(Pageable pageable); - @EntityGraph(attributePaths = {"testResult", "scenarioParameters", "scenarioActions", "scenarioMessages"}) + @EntityGraph(attributePaths = {"testResult", "scenarioParameters", "scenarioActions", "scenarioMessages", "scenarioMessages.headers"}) Optional findOneByExecutionId(@Param("executionId") Long executionId); @Query("FROM ScenarioExecution WHERE executionId IN :scenarioExecutionIds") - @EntityGraph(attributePaths = {"testResult", "scenarioParameters", "scenarioActions", "scenarioMessages"}) + @EntityGraph(attributePaths = {"testResult", "scenarioParameters", "scenarioActions", "scenarioMessages", "scenarioMessages.headers"}) Page findAllWhereExecutionIdIn(@Param("scenarioExecutionIds") List scenarioExecutionIds, Pageable pageable); } diff --git a/simulator-spring-boot/src/main/java/org/citrusframework/simulator/web/rest/ScenarioExecutionResource.java b/simulator-spring-boot/src/main/java/org/citrusframework/simulator/web/rest/ScenarioExecutionResource.java index 6430e799..ce157b72 100644 --- a/simulator-spring-boot/src/main/java/org/citrusframework/simulator/web/rest/ScenarioExecutionResource.java +++ b/simulator-spring-boot/src/main/java/org/citrusframework/simulator/web/rest/ScenarioExecutionResource.java @@ -22,7 +22,6 @@ import org.citrusframework.simulator.service.criteria.ScenarioExecutionCriteria; import org.citrusframework.simulator.web.rest.dto.ScenarioExecutionDTO; import org.citrusframework.simulator.web.rest.dto.mapper.ScenarioExecutionMapper; -import org.citrusframework.simulator.web.util.PaginationUtil; import org.citrusframework.simulator.web.util.ResponseUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,12 +33,16 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; 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.servlet.support.ServletUriComponentsBuilder; import java.util.List; import java.util.Optional; +import static java.lang.Boolean.FALSE; +import static org.citrusframework.simulator.web.util.PaginationUtil.generatePaginationHttpHeaders; + /** * REST controller for managing {@link ScenarioExecution}. */ @@ -56,7 +59,8 @@ public class ScenarioExecutionResource { public ScenarioExecutionResource( ScenarioExecutionService scenarioExecutionService, - ScenarioExecutionQueryService scenarioExecutionQueryService, ScenarioExecutionMapper scenarioExecutionMapper + ScenarioExecutionQueryService scenarioExecutionQueryService, + ScenarioExecutionMapper scenarioExecutionMapper ) { this.scenarioExecutionService = scenarioExecutionService; this.scenarioExecutionQueryService = scenarioExecutionQueryService; @@ -71,12 +75,23 @@ public ScenarioExecutionResource( * @return the {@link ResponseEntity} with status {@code 200 (OK)} and the list of scenarioExecutions in body. */ @GetMapping("/scenario-executions") - public ResponseEntity> getAllScenarioExecutions(ScenarioExecutionCriteria criteria, @ParameterObject Pageable pageable) { + public ResponseEntity> getAllScenarioExecutions( + ScenarioExecutionCriteria criteria, + @RequestParam(name = "includeActions", required = false, defaultValue = "false") Boolean includeActions, + @RequestParam(name = "includeMessages", required = false, defaultValue = "false") Boolean includeMessages, + @RequestParam(name = "includeParameters", required = false, defaultValue = "false") Boolean includeParameters, + @ParameterObject Pageable pageable + ) { logger.debug("REST request to get ScenarioExecutions by criteria: {}", criteria); Page page = scenarioExecutionQueryService.findByCriteria(criteria, pageable); - HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page); - return ResponseEntity.ok().headers(headers).body(page.getContent().stream().map(scenarioExecutionMapper::toDto).toList()); + HttpHeaders headers = generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page); + return ResponseEntity.ok() + .headers(headers) + .body(page.getContent().stream() + .map(scenarioExecution -> stripPageContents(scenarioExecution, includeActions, includeMessages, includeParameters)) + .map(scenarioExecutionMapper::toDto) + .toList()); } /** @@ -103,4 +118,17 @@ public ResponseEntity getScenarioExecution(@PathVariable(" Optional scenarioExecution = scenarioExecutionService.findOne(id); return ResponseUtil.wrapOrNotFound(scenarioExecution.map(scenarioExecutionMapper::toDto)); } + + private ScenarioExecution stripPageContents(ScenarioExecution scenarioExecution, Boolean includeActions, Boolean includeMessages, Boolean includeParameters) { + if (FALSE.equals(includeActions)) { + scenarioExecution.getScenarioActions().clear(); + } + if (FALSE.equals(includeMessages)) { + scenarioExecution.getScenarioMessages().clear(); + } + if (FALSE.equals(includeParameters)) { + scenarioExecution.getScenarioParameters().clear(); + } + return scenarioExecution; + } } diff --git a/simulator-spring-boot/src/test/java/org/citrusframework/simulator/TechnicalStructureTest.java b/simulator-spring-boot/src/test/java/org/citrusframework/simulator/TechnicalStructureTest.java index 61f7eea6..256f308e 100644 --- a/simulator-spring-boot/src/test/java/org/citrusframework/simulator/TechnicalStructureTest.java +++ b/simulator-spring-boot/src/test/java/org/citrusframework/simulator/TechnicalStructureTest.java @@ -40,5 +40,5 @@ public class TechnicalStructureTest { .layer("Scenario").definedBy("..scenario..") .whereLayer("Web").mayOnlyBeAccessedByLayers("Config") - .whereLayer("Domain").mayOnlyBeAccessedByLayers("Config", "DTO", "Service", "Persistence", "Endpoint", "Listener", "Scenario"); + .whereLayer("Domain").mayOnlyBeAccessedByLayers("Config", "DTO", "Service", "Persistence", "Endpoint", "Listener", "Scenario", "Web"); } diff --git a/simulator-spring-boot/src/test/java/org/citrusframework/simulator/web/rest/ScenarioExecutionResourceIT.java b/simulator-spring-boot/src/test/java/org/citrusframework/simulator/web/rest/ScenarioExecutionResourceIT.java index 89081b49..bad31e1b 100644 --- a/simulator-spring-boot/src/test/java/org/citrusframework/simulator/web/rest/ScenarioExecutionResourceIT.java +++ b/simulator-spring-boot/src/test/java/org/citrusframework/simulator/web/rest/ScenarioExecutionResourceIT.java @@ -515,7 +515,7 @@ void getAllScenarioExecutionsByScenarioParametersIsEqualToSomething() throws Exc */ private void defaultScenarioExecutionShouldBeFound(String filter) throws Exception { mockMvc - .perform(get(ENTITY_API_URL + "?sort=executionId,desc&" + filter)) + .perform(get(ENTITY_API_URL + "?sort=executionId,desc&" + filter + "&includeActions=true&includeMessages=true&includeParameters=true")) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) .andExpect(jsonPath("$.[*].executionId").value(hasItem(scenarioExecution.getExecutionId().intValue()))) @@ -560,6 +560,7 @@ void getNonExistingScenarioExecution() throws Exception { @Nested class CorrectTimeOnScenarioExecution { + public static final TemporalUnitLessThanOffset LESS_THAN_5_SECONDS = new TemporalUnitLessThanOffset(5, SECONDS); @Autowired @@ -580,7 +581,7 @@ void shouldInvokeScenario() throws Exception { assertThat(mockEndpointResult).contains("E5a084sOZw7"); String scenarioExecutionsResult = mockMvc - .perform(get("/api/scenario-executions")) + .perform(get("/api/scenario-executions?includeActions=true&includeMessages=true&includeParameters=true")) .andExpect(status().isOk()) .andReturn() .getResponse() diff --git a/simulator-spring-boot/src/test/java/org/citrusframework/simulator/web/rest/ScenarioExecutionResourceTest.java b/simulator-spring-boot/src/test/java/org/citrusframework/simulator/web/rest/ScenarioExecutionResourceTest.java new file mode 100644 index 00000000..79e8109b --- /dev/null +++ b/simulator-spring-boot/src/test/java/org/citrusframework/simulator/web/rest/ScenarioExecutionResourceTest.java @@ -0,0 +1,148 @@ +/* + * Copyright the original author or authors. + * + * Licensed 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.citrusframework.simulator.web.rest; + +import org.citrusframework.simulator.model.Message; +import org.citrusframework.simulator.model.ScenarioAction; +import org.citrusframework.simulator.model.ScenarioExecution; +import org.citrusframework.simulator.model.ScenarioParameter; +import org.citrusframework.simulator.service.ScenarioExecutionQueryService; +import org.citrusframework.simulator.service.ScenarioExecutionService; +import org.citrusframework.simulator.service.criteria.ScenarioExecutionCriteria; +import org.citrusframework.simulator.web.rest.dto.ScenarioExecutionDTO; +import org.citrusframework.simulator.web.rest.dto.mapper.ScenarioExecutionMapper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import static java.lang.Boolean.FALSE; +import static java.lang.Boolean.TRUE; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.springframework.http.HttpStatus.OK; + +@ExtendWith({MockitoExtension.class}) +class ScenarioExecutionResourceTest { + + @Mock + private ScenarioExecutionService scenarioExecutionServiceMock; + + @Mock + private ScenarioExecutionQueryService scenarioExecutionQueryServiceMock; + + @Mock + private ScenarioExecutionMapper scenarioExecutionMapperMock; + + private ScenarioExecutionResource fixture; + + @BeforeEach + void beforeEachSetup() { + fixture = new ScenarioExecutionResource(scenarioExecutionServiceMock, scenarioExecutionQueryServiceMock, scenarioExecutionMapperMock); + } + + @Nested + class GetAllScenarioExecutions { + + @Mock + private ScenarioExecutionCriteria criteriaMock; + + @Mock + private Pageable pageableMock; + + @Mock + private ScenarioExecutionDTO scenarioExecutionDTOMock; + + private ScenarioExecution scenarioExecution; + + @BeforeEach + void beforeEachSetup() { + var request = new MockHttpServletRequest(); + RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request)); + + scenarioExecution = new ScenarioExecution(); + scenarioExecution.getScenarioActions().add(mock(ScenarioAction.class)); + scenarioExecution.getScenarioMessages().add(mock(Message.class)); + scenarioExecution.getScenarioParameters().add(mock(ScenarioParameter.class)); + + var scenarioExecutions = new PageImpl<>(singletonList(scenarioExecution)); + doReturn(scenarioExecutions).when(scenarioExecutionQueryServiceMock).findByCriteria(criteriaMock, pageableMock); + + doReturn(scenarioExecutionDTOMock).when(scenarioExecutionMapperMock).toDto(scenarioExecution); + } + + @Test + void stripsIncludedActions() { + var response = fixture.getAllScenarioExecutions(criteriaMock, FALSE, TRUE, TRUE, pageableMock); + + assertThat(response) + .satisfies( + r -> assertThat(r.getStatusCode()).isEqualTo(OK), + r -> assertThat(r.getBody()) + .singleElement() + .isEqualTo(scenarioExecutionDTOMock) + ); + + assertThat(scenarioExecution.getScenarioActions()).isEmpty(); + assertThat(scenarioExecution.getScenarioMessages()).isNotEmpty(); + assertThat(scenarioExecution.getScenarioParameters()).isNotEmpty(); + } + + @Test + void stripsIncludedMessages() { + var response = fixture.getAllScenarioExecutions(criteriaMock, TRUE, FALSE, TRUE, pageableMock); + + assertThat(response) + .satisfies( + r -> assertThat(r.getStatusCode()).isEqualTo(OK), + r -> assertThat(r.getBody()) + .singleElement() + .isEqualTo(scenarioExecutionDTOMock) + ); + + assertThat(scenarioExecution.getScenarioActions()).isNotEmpty(); + assertThat(scenarioExecution.getScenarioMessages()).isEmpty(); + assertThat(scenarioExecution.getScenarioParameters()).isNotEmpty(); + } + + @Test + void stripsIncludedParameters() { + var response = fixture.getAllScenarioExecutions(criteriaMock, TRUE, TRUE, FALSE, pageableMock); + + assertThat(response) + .satisfies( + r -> assertThat(r.getStatusCode()).isEqualTo(OK), + r -> assertThat(r.getBody()) + .singleElement() + .isEqualTo(scenarioExecutionDTOMock) + ); + + assertThat(scenarioExecution.getScenarioActions()).isNotEmpty(); + assertThat(scenarioExecution.getScenarioMessages()).isNotEmpty(); + assertThat(scenarioExecution.getScenarioParameters()).isEmpty(); + } + } +} diff --git a/simulator-spring-boot/src/test/java/org/citrusframework/simulator/web/rest/ScenarioResourceTest.java b/simulator-spring-boot/src/test/java/org/citrusframework/simulator/web/rest/ScenarioResourceTest.java index 5493d832..eee032cf 100644 --- a/simulator-spring-boot/src/test/java/org/citrusframework/simulator/web/rest/ScenarioResourceTest.java +++ b/simulator-spring-boot/src/test/java/org/citrusframework/simulator/web/rest/ScenarioResourceTest.java @@ -42,6 +42,7 @@ import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.InstanceOfAssertFactories.LIST; import static org.assertj.core.api.InstanceOfAssertFactories.type; import static org.citrusframework.simulator.web.rest.ScenarioResource.Scenario.ScenarioType.STARTER; import static org.junit.jupiter.params.provider.Arguments.arguments; @@ -122,7 +123,7 @@ void doesFilterCacheWithNameContains(String filterLetter, String expectedScenari assertThat(result) .extracting(ResponseEntity::getBody) - .asList() + .asInstanceOf(LIST) .hasSize(1) .first() .asInstanceOf(type(Scenario.class)) @@ -141,7 +142,7 @@ void doesFilterCacheWithNameStartsOrEndsWith() { assertThat(result) .extracting(ResponseEntity::getBody) - .asList() + .asInstanceOf(LIST) .hasSize(2) .noneSatisfy(scenario -> assertThat(scenario) @@ -161,7 +162,7 @@ void doesNotFilterCacheWithoutNameContains() { assertThat(result) .extracting(ResponseEntity::getBody) - .asList() + .asInstanceOf(LIST) .isEqualTo(SCENARIO_CACHE); }