From 90c62fa8ff77bdea92d817a54b17c65820db6f21 Mon Sep 17 00:00:00 2001 From: ojasdubey Date: Tue, 17 Jul 2018 18:58:13 +0530 Subject: Pagination of workflow list API Implemented spring boot pagination support Change-Id: Id56eb7c72af1622348708b7303605db8914be564 Issue-ID: SDC-1483 Signed-off-by: ojasdubey --- .../org/onap/sdc/workflow/api/RestConstants.java | 6 + .../onap/sdc/workflow/api/WorkflowController.java | 37 ++++- .../CustomizedResponseEntityExceptionHandler.java | 20 +++ .../sdc/workflow/api/types/CollectionWrapper.java | 12 ++ .../sdc/workflow/services/WorkflowManager.java | 3 +- .../workflow/services/WorkflowNameComparator.java | 16 +++ .../InvalidPaginationParameterException.java | 8 ++ .../services/impl/WorkflowManagerImpl.java | 52 ++++++- .../test/java/org/onap/sdc/workflow/RestPath.java | 36 +++++ .../sdc/workflow/api/WorkflowControllerTest.java | 154 ++++++++++++++++++++- .../services/impl/WorkflowManagerImplTest.java | 100 ++++++++++++- 11 files changed, 431 insertions(+), 13 deletions(-) create mode 100644 workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/WorkflowNameComparator.java create mode 100644 workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/exceptions/InvalidPaginationParameterException.java diff --git a/workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/api/RestConstants.java b/workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/api/RestConstants.java index 8f02be0f..47a757e2 100644 --- a/workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/api/RestConstants.java +++ b/workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/api/RestConstants.java @@ -6,4 +6,10 @@ public class RestConstants { } public static final String USER_ID_HEADER_PARAM = "USER_ID"; + public static final String LIMIT_PARAM = "size"; + public static final String OFFSET_PARAM = "page"; + public static final String SORT_PARAM = "sort"; + public static final String SORT_FIELD_NAME = "name"; + public static final int LIMIT_DEFAULT = 20; + public static final int OFFSET_DEFAULT = 0; } diff --git a/workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/api/WorkflowController.java b/workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/api/WorkflowController.java index b224e84b..de35320d 100644 --- a/workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/api/WorkflowController.java +++ b/workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/api/WorkflowController.java @@ -1,14 +1,28 @@ package org.onap.sdc.workflow.api; +import static org.onap.sdc.workflow.api.RestConstants.LIMIT_DEFAULT; +import static org.onap.sdc.workflow.api.RestConstants.SORT_FIELD_NAME; +import static org.onap.sdc.workflow.api.RestConstants.SORT_PARAM; import static org.onap.sdc.workflow.api.RestConstants.USER_ID_HEADER_PARAM; +import com.google.common.collect.ImmutableSet; + +import java.util.Arrays; +import java.util.Set; + import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.onap.sdc.workflow.api.types.CollectionWrapper; import org.onap.sdc.workflow.persistence.types.Workflow; import org.onap.sdc.workflow.services.WorkflowManager; +import org.onap.sdc.workflow.services.exceptions.InvalidPaginationParameterException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.web.PageableDefault; +import org.springframework.data.web.SortDefault; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -35,8 +49,14 @@ public class WorkflowController { @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) @ApiOperation("List workflows") - public CollectionWrapper list(@RequestHeader(USER_ID_HEADER_PARAM) String user) { - return new CollectionWrapper<>(workflowManager.list()); + public CollectionWrapper list(@RequestHeader(USER_ID_HEADER_PARAM) String user, + @PageableDefault(size = LIMIT_DEFAULT) + @SortDefault.SortDefaults({ + @SortDefault(sort = SORT_FIELD_NAME, direction = Sort.Direction.ASC) + }) Pageable pageable) { + PageRequest pageRequest = createPageRequest(pageable); + return new CollectionWrapper<>(pageRequest.getPageSize(), pageRequest.getPageNumber(), + workflowManager.list(pageRequest)); } @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) @@ -63,4 +83,17 @@ public class WorkflowController { workflowManager.update(workflow); return workflow; } + + private PageRequest createPageRequest(Pageable pageable) { + Set validSortFields = ImmutableSet.of(SORT_FIELD_NAME); + Sort sort = pageable.getSort(); + for (Sort.Order order : sort) { + String sortFieldName = order.getProperty(); + if (!sortFieldName.equalsIgnoreCase(SORT_FIELD_NAME)) { + throw new InvalidPaginationParameterException(SORT_PARAM, sortFieldName, + "is not supported. Supported values are: " + Arrays.toString(validSortFields.toArray())); + } + } + return PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), sort); + } } diff --git a/workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/api/exceptionshandlers/CustomizedResponseEntityExceptionHandler.java b/workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/api/exceptionshandlers/CustomizedResponseEntityExceptionHandler.java index 68fd41a5..bcc28d6d 100644 --- a/workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/api/exceptionshandlers/CustomizedResponseEntityExceptionHandler.java +++ b/workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/api/exceptionshandlers/CustomizedResponseEntityExceptionHandler.java @@ -1,19 +1,25 @@ package org.onap.sdc.workflow.api.exceptionshandlers; +import static org.springframework.http.HttpStatus.BAD_REQUEST; import static org.springframework.http.HttpStatus.FORBIDDEN; import static org.springframework.http.HttpStatus.NOT_FOUND; import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY; import org.onap.sdc.workflow.services.exceptions.EntityNotFoundException; import org.onap.sdc.workflow.services.exceptions.InvalidArtifactException; +import org.onap.sdc.workflow.services.exceptions.InvalidPaginationParameterException; import org.onap.sdc.workflow.services.exceptions.UniqueValueViolationException; import org.onap.sdc.workflow.services.exceptions.VersionCreationException; import org.onap.sdc.workflow.services.exceptions.VersionModificationException; import org.onap.sdc.workflow.services.exceptions.VersionStateModificationException; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.ServletRequestBindingException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.context.request.WebRequest; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; @ControllerAdvice @@ -32,6 +38,20 @@ public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExce return new ResponseEntity<>(exception.getMessage(), NOT_FOUND); } + @ExceptionHandler({InvalidPaginationParameterException.class}) + public final ResponseEntity handlePaginationException(InvalidPaginationParameterException exception) { + return new ResponseEntity<>(exception.getMessage(), BAD_REQUEST); + } + + //For missing header exceptions + @Override + public ResponseEntity handleServletRequestBindingException(ServletRequestBindingException ex, + HttpHeaders headers, HttpStatus status, + WebRequest request) { + return new ResponseEntity<>(ex.getMessage(), BAD_REQUEST); + } + + @ExceptionHandler({InvalidArtifactException.class, VersionModificationException.class, VersionStateModificationException.class}) public final ResponseEntity handleInvalidArtifactException( diff --git a/workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/api/types/CollectionWrapper.java b/workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/api/types/CollectionWrapper.java index 653b0dcf..1d583dd9 100644 --- a/workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/api/types/CollectionWrapper.java +++ b/workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/api/types/CollectionWrapper.java @@ -11,6 +11,18 @@ public class CollectionWrapper { private int offset; private Collection results; + + public CollectionWrapper() { + //Default constructor for object mappers + } + + public CollectionWrapper(int limit, int offset, Collection results) { + this.results = results; + this.limit = limit; + this.offset = offset; + this.total = results.size(); + } + public CollectionWrapper(Collection results) { this.results = results; this.total = results.size(); diff --git a/workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/WorkflowManager.java b/workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/WorkflowManager.java index 01c0b053..e2572b78 100644 --- a/workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/WorkflowManager.java +++ b/workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/WorkflowManager.java @@ -2,10 +2,11 @@ package org.onap.sdc.workflow.services; import java.util.Collection; import org.onap.sdc.workflow.persistence.types.Workflow; +import org.springframework.data.domain.Pageable; public interface WorkflowManager { - Collection list(); + Collection list(Pageable pageable); Workflow get(Workflow workflow); diff --git a/workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/WorkflowNameComparator.java b/workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/WorkflowNameComparator.java new file mode 100644 index 00000000..1ba4dcda --- /dev/null +++ b/workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/WorkflowNameComparator.java @@ -0,0 +1,16 @@ +package org.onap.sdc.workflow.services; + +import java.util.Comparator; + +import org.onap.sdc.workflow.persistence.types.Workflow; + +public class WorkflowNameComparator implements Comparator{ + + @Override + public int compare(Workflow workflow1, Workflow workflow2) { + String workflowName1 = workflow1.getName().toLowerCase(); + String workflowName2 = workflow2.getName().toLowerCase(); + //ascending order + return workflowName1.compareTo(workflowName2); + } +} diff --git a/workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/exceptions/InvalidPaginationParameterException.java b/workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/exceptions/InvalidPaginationParameterException.java new file mode 100644 index 00000000..fde8fd97 --- /dev/null +++ b/workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/exceptions/InvalidPaginationParameterException.java @@ -0,0 +1,8 @@ +package org.onap.sdc.workflow.services.exceptions; + +public class InvalidPaginationParameterException extends RuntimeException { + + public InvalidPaginationParameterException(String parameterName, String parameterValue, String message) { + super(String.format("Requested %s: %s %s", parameterName, parameterValue, message)); + } +} \ No newline at end of file diff --git a/workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/impl/WorkflowManagerImpl.java b/workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/impl/WorkflowManagerImpl.java index 8ac5025a..2cb897fe 100644 --- a/workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/impl/WorkflowManagerImpl.java +++ b/workflow/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/impl/WorkflowManagerImpl.java @@ -1,11 +1,18 @@ package org.onap.sdc.workflow.services.impl; +import static org.onap.sdc.workflow.api.RestConstants.SORT_FIELD_NAME; + +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; import org.onap.sdc.workflow.persistence.types.Workflow; import org.onap.sdc.workflow.services.UniqueValueService; import org.onap.sdc.workflow.services.WorkflowManager; +import org.onap.sdc.workflow.services.WorkflowNameComparator; import org.onap.sdc.workflow.services.exceptions.EntityNotFoundException; import org.onap.sdc.workflow.services.impl.mappers.WorkflowMapper; import org.openecomp.sdc.versioning.ItemManager; @@ -13,6 +20,8 @@ import org.openecomp.sdc.versioning.types.Item; import org.openecomp.sdc.versioning.types.ItemStatus; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; @Service("workflowManager") @@ -36,9 +45,11 @@ public class WorkflowManagerImpl implements WorkflowManager { } @Override - public Collection list() { - return itemManager.list(ITEM_PREDICATE).stream().map(workflowMapper::itemToWorkflow) - .collect(Collectors.toList()); + public Collection list(Pageable pageRequest) { + List workflowList = itemManager.list(ITEM_PREDICATE).stream() + .map( workflowMapper::itemToWorkflow).collect(Collectors.toList()); + sortWorkflowList(workflowList, pageRequest); + return applyLimitAndOffset(workflowList, pageRequest); } @Override @@ -76,4 +87,39 @@ public class WorkflowManagerImpl implements WorkflowManager { item.setVersionStatusCounters(retrievedItem.getVersionStatusCounters()); itemManager.update(item); } + + private List applyLimitAndOffset(List workflowList, Pageable pageRequest) { + int limit = pageRequest.getPageSize(); + int offset = pageRequest.getPageNumber(); + int totalNumOfWorkflows = workflowList.size(); + List selectedWorkflows; + try { + if (limit > totalNumOfWorkflows) { + limit = totalNumOfWorkflows; + } + int startIndex = offset * limit; + int endIndex = startIndex + limit; + if (endIndex > totalNumOfWorkflows) { + endIndex = totalNumOfWorkflows; + } + selectedWorkflows = workflowList.subList(startIndex, endIndex); + } catch (IndexOutOfBoundsException | IllegalArgumentException ex) { + selectedWorkflows = new ArrayList<>(); + } + return selectedWorkflows; + } + + private void sortWorkflowList(List workflowList, Pageable pageRequest) { + Comparator comparator = getWorkflowListComparator(); + if (pageRequest.getSort().getOrderFor(SORT_FIELD_NAME).getDirection() == Sort.Direction.ASC) { + workflowList.sort(comparator); + } else { + workflowList.sort(Collections.reverseOrder(comparator)); + } + } + + private Comparator getWorkflowListComparator() { + //More comparators can be added if required based on sort field name + return new WorkflowNameComparator(); + } } diff --git a/workflow/workflow-designer-be/src/test/java/org/onap/sdc/workflow/RestPath.java b/workflow/workflow-designer-be/src/test/java/org/onap/sdc/workflow/RestPath.java index 266ca91b..ba99996a 100644 --- a/workflow/workflow-designer-be/src/test/java/org/onap/sdc/workflow/RestPath.java +++ b/workflow/workflow-designer-be/src/test/java/org/onap/sdc/workflow/RestPath.java @@ -1,10 +1,46 @@ package org.onap.sdc.workflow; +import static org.onap.sdc.workflow.api.RestConstants.LIMIT_PARAM; +import static org.onap.sdc.workflow.api.RestConstants.OFFSET_PARAM; +import static org.onap.sdc.workflow.api.RestConstants.SORT_PARAM; + public class RestPath { + private RestPath() { + //Hiding implicit constructor + } + private static final String WORKFLOWS_URL = "/workflows"; private static final String WORKFLOW_URL_FORMATTER = WORKFLOWS_URL + "/%s"; private static final String VERSIONS_URL_FORMATTER = WORKFLOWS_URL + "/%s/versions"; private static final String VERSION_URL_FORMATTER = WORKFLOWS_URL + "/%s/versions/%s"; + private static final String SORT_QUERY_STRING_FORMATTER = SORT_PARAM + "=%s"; + private static final String LIMIT_QUERY_STRING_FORMATTER = LIMIT_PARAM + "=%s"; + private static final String OFFSET_QUERY_STRING_FORMATTER = OFFSET_PARAM + "=%s"; + private static final String WORKFLOW_URL_FORMATTER_QUERY_PARAMS_ALL = + WORKFLOWS_URL + "?" + SORT_QUERY_STRING_FORMATTER+ "&" + LIMIT_QUERY_STRING_FORMATTER + "&" + + OFFSET_QUERY_STRING_FORMATTER; + private static final String WORKFLOW_URL_FORMATTER_QUERY_PARAMS_NO_SORT = + WORKFLOWS_URL + "?" + LIMIT_QUERY_STRING_FORMATTER + "&" + OFFSET_QUERY_STRING_FORMATTER; + private static final String WORKFLOW_URL_FORMATTER_QUERY_PARAMS_NO_SORT_AND_LIMIT = + WORKFLOWS_URL + "?" + OFFSET_QUERY_STRING_FORMATTER; + private static final String WORKFLOW_URL_FORMATTER_QUERY_PARAMS_NO_SORT_AND_OFFSET = + WORKFLOWS_URL + "?" + LIMIT_QUERY_STRING_FORMATTER; + + public static String getWorkflowsPathAllQueryParams(String sort, String limit, String offset){ + return String.format(WORKFLOW_URL_FORMATTER_QUERY_PARAMS_ALL, sort, limit, offset); + } + + public static String getWorkflowsPathNoSort(String limit, String offset){ + return String.format(WORKFLOW_URL_FORMATTER_QUERY_PARAMS_NO_SORT, limit, offset); + } + + public static String getWorkflowsPathNoSortAndLimit(String offset){ + return String.format(WORKFLOW_URL_FORMATTER_QUERY_PARAMS_NO_SORT_AND_LIMIT, offset); + } + + public static String getWorkflowsPathNoSortAndOffset(String limit){ + return String.format(WORKFLOW_URL_FORMATTER_QUERY_PARAMS_NO_SORT_AND_OFFSET, limit); + } public static String getWorkflowsPath(){ return WORKFLOWS_URL; diff --git a/workflow/workflow-designer-be/src/test/java/org/onap/sdc/workflow/api/WorkflowControllerTest.java b/workflow/workflow-designer-be/src/test/java/org/onap/sdc/workflow/api/WorkflowControllerTest.java index 69b25b01..7c275052 100644 --- a/workflow/workflow-designer-be/src/test/java/org/onap/sdc/workflow/api/WorkflowControllerTest.java +++ b/workflow/workflow-designer-be/src/test/java/org/onap/sdc/workflow/api/WorkflowControllerTest.java @@ -8,6 +8,10 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.onap.sdc.workflow.TestUtil.createWorkflow; +import static org.onap.sdc.workflow.api.RestConstants.LIMIT_DEFAULT; +import static org.onap.sdc.workflow.api.RestConstants.OFFSET_DEFAULT; +import static org.onap.sdc.workflow.api.RestConstants.SORT_FIELD_NAME; +import static org.onap.sdc.workflow.api.RestConstants.SORT_PARAM; import static org.onap.sdc.workflow.api.RestConstants.USER_ID_HEADER_PARAM; import static org.springframework.http.MediaType.APPLICATION_JSON; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; @@ -16,9 +20,14 @@ import static org.springframework.test.web.servlet.result.MockMvcResultHandlers. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableSet; import com.google.gson.Gson; + import java.util.ArrayList; import java.util.List; +import java.util.Set; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -26,9 +35,12 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.onap.sdc.workflow.RestPath; +import org.onap.sdc.workflow.api.exceptionshandlers.CustomizedResponseEntityExceptionHandler; +import org.onap.sdc.workflow.api.types.CollectionWrapper; import org.onap.sdc.workflow.persistence.types.Workflow; import org.onap.sdc.workflow.services.WorkflowManager; import org.openecomp.sdc.versioning.types.Item; +import org.springframework.data.web.PageableHandlerMethodArgumentResolver; import org.springframework.http.MediaType; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.test.web.servlet.MockMvc; @@ -42,6 +54,11 @@ public class WorkflowControllerTest { "Missing request header '%s' for method parameter of type String"; private static final String USER_ID = "userId"; private static final Gson GSON = new Gson(); + private static final String USER_ID_HEADER = "USER_ID"; + private static final String INVALID_PAGINATION_PARAMETER_FORMAT = "Requested %s: %s %s"; + private static final String PAGINATION_PARAMETER_INVALID_SORT_FIELD_SUFFIX = + "is not supported. Supported values are: "; + private static final String DEFAULT_SORT_VALUE = "name,asc"; private MockMvc mockMvc; @@ -55,6 +72,10 @@ public class WorkflowControllerTest { @Before public void setUp() { mockMvc = MockMvcBuilders.standaloneSetup(workflowController).build(); + mockMvc = MockMvcBuilders.standaloneSetup(workflowController) + .setCustomArgumentResolvers(new PageableHandlerMethodArgumentResolver()) + .setControllerAdvice(new CustomizedResponseEntityExceptionHandler()) + .build(); } @Test @@ -64,7 +85,7 @@ public class WorkflowControllerTest { mockMvc.perform(get(RestPath.getWorkflowPath(workflowMock.getId())).contentType(APPLICATION_JSON)) .andDo(print()).andExpect(status().isBadRequest()).andExpect(status().is(400)).andReturn() .getResponse(); - assertEquals(String.format(MISSING_REQUEST_HEADER_ERRROR_FORMAT, "USER_ID"), response.getErrorMessage()); + assertEquals(String.format(MISSING_REQUEST_HEADER_ERRROR_FORMAT, USER_ID_HEADER), response.getContentAsString()); } @Test @@ -83,19 +104,129 @@ public class WorkflowControllerTest { MockHttpServletResponse response = mockMvc.perform(get(RestPath.getWorkflowsPath()).contentType(APPLICATION_JSON)).andDo(print()) .andExpect(status().isBadRequest()).andExpect(status().is(400)).andReturn().getResponse(); - assertEquals(String.format(MISSING_REQUEST_HEADER_ERRROR_FORMAT, USER_ID_HEADER_PARAM), response.getErrorMessage()); + assertEquals(String.format(MISSING_REQUEST_HEADER_ERRROR_FORMAT, USER_ID_HEADER_PARAM), response.getContentAsString()); } @Test public void shouldReturn5WorkflowWhen5WorkflowsExists() throws Exception { int numOfWorkflows = 5; List workflowMocks = createWorkflows(numOfWorkflows); - doReturn(workflowMocks).when(workflowManagerMock).list(); + doReturn(workflowMocks).when(workflowManagerMock).list(any()); mockMvc.perform( get(RestPath.getWorkflowsPath()).header(USER_ID_HEADER_PARAM, USER_ID).contentType(APPLICATION_JSON)) .andDo(print()).andExpect(status().isOk()).andExpect(jsonPath("$.results", hasSize(numOfWorkflows))); } + @Test + public void shouldReturnSortedLimitOffsetAppliedWorkflows() throws Exception { + List workflowMocks = createLimit2AndOffset1For5WorkflowList(); + doReturn(workflowMocks).when(workflowManagerMock).list(any()); + mockMvc.perform( + get(RestPath.getWorkflowsPathAllQueryParams(DEFAULT_SORT_VALUE, "2", "1")) + .header(RestConstants.USER_ID_HEADER_PARAM, USER_ID).contentType(APPLICATION_JSON)) + .andDo(print()).andExpect(status().isOk()).andExpect(jsonPath("$.results", hasSize(2))); + } + + @Test + public void shouldReturnResultsWithDefaultWhenLimitIsNegative() throws Exception { + List workflowMocks = createLimit2AndOffset1For5WorkflowList(); + doReturn(workflowMocks).when(workflowManagerMock).list(any()); + MockHttpServletResponse response = mockMvc.perform( + get(RestPath.getWorkflowsPathAllQueryParams(DEFAULT_SORT_VALUE, "-2", "1")) + .header(RestConstants.USER_ID_HEADER_PARAM, USER_ID).contentType(APPLICATION_JSON)) + .andDo(print()).andExpect(status().isOk()).andExpect(status().is(200)).andReturn() + .getResponse(); + CollectionWrapper workflowListResponse = + new ObjectMapper().readValue(response.getContentAsString(), CollectionWrapper.class); + assertEquals(LIMIT_DEFAULT, workflowListResponse.getLimit()); + assertEquals(1, workflowListResponse.getOffset()); + assertEquals(2, workflowListResponse.getTotal()); + } + + @Test + public void shouldFallbackOnDefaultOffsetWhenOffsetIsNegative() throws Exception { + MockHttpServletResponse response = mockMvc.perform( + get(RestPath.getWorkflowsPathAllQueryParams(DEFAULT_SORT_VALUE, "2", "-1")) + .header(RestConstants.USER_ID_HEADER_PARAM, USER_ID).contentType(APPLICATION_JSON)) + .andDo(print()).andExpect(status().isOk()).andExpect(status().is(200)).andReturn() + .getResponse(); + CollectionWrapper workflowListResponse = + new ObjectMapper().readValue(response.getContentAsString(), CollectionWrapper.class); + assertEquals(2, workflowListResponse.getLimit()); + assertEquals(OFFSET_DEFAULT, workflowListResponse.getOffset()); + assertEquals(0, workflowListResponse.getTotal()); + } + + @Test + public void shouldFallbackOnDefaultLimitWhenLimitIsNotAnInteger() throws Exception { + MockHttpServletResponse response = mockMvc.perform( + get(RestPath.getWorkflowsPathAllQueryParams(DEFAULT_SORT_VALUE, "abc", "0")) + .header(RestConstants.USER_ID_HEADER_PARAM, USER_ID).contentType(APPLICATION_JSON)) + .andDo(print()).andExpect(status().isOk()).andExpect(status().is(200)).andReturn() + .getResponse(); + CollectionWrapper workflowListResponse = + new ObjectMapper().readValue(response.getContentAsString(), CollectionWrapper.class); + assertEquals(LIMIT_DEFAULT, workflowListResponse.getLimit()); + assertEquals(0, workflowListResponse.getOffset()); + assertEquals(0, workflowListResponse.getTotal()); + } + + @Test + public void shouldFallbackOnDefaultOffsetWhenOffsetIsNotAnInteger() throws Exception { + MockHttpServletResponse response = mockMvc.perform( + get(RestPath.getWorkflowsPathAllQueryParams(DEFAULT_SORT_VALUE, "2", "abc")) + .header(RestConstants.USER_ID_HEADER_PARAM, USER_ID).contentType(APPLICATION_JSON)) + .andDo(print()).andExpect(status().isOk()).andExpect(status().is(200)).andReturn() + .getResponse(); + CollectionWrapper workflowListResponse = + new ObjectMapper().readValue(response.getContentAsString(), CollectionWrapper.class); + assertEquals(2, workflowListResponse.getLimit()); + assertEquals(OFFSET_DEFAULT, workflowListResponse.getOffset()); + assertEquals(0, workflowListResponse.getTotal()); + } + + @Test + public void shouldThrowExceptionWhenSortFieldIsInvalid() throws Exception { + MockHttpServletResponse response = mockMvc.perform( + get(RestPath.getWorkflowsPathAllQueryParams("invalidSortField,asc", "2", "1")) + .header(RestConstants.USER_ID_HEADER_PARAM, USER_ID).contentType(APPLICATION_JSON)) + .andDo(print()).andExpect(status().isBadRequest()).andExpect(status().is(400)).andReturn() + .getResponse(); + assertEquals(String.format(INVALID_PAGINATION_PARAMETER_FORMAT, SORT_PARAM, "invalidSortField", + PAGINATION_PARAMETER_INVALID_SORT_FIELD_SUFFIX + getSupportedSortFields()), + response.getContentAsString()); + } + + @Test + public void shouldReturnAscSortedLimitOffsetAppliedWorkflowsWhenSortIsNotSpecified() throws Exception { + List workflowMocks = createLimit2AndOffset1For5WorkflowList(); + doReturn(workflowMocks).when(workflowManagerMock).list(any()); + mockMvc.perform( + get(RestPath.getWorkflowsPathNoSort("2", "1")) + .header(RestConstants.USER_ID_HEADER_PARAM, USER_ID).contentType(APPLICATION_JSON)) + .andDo(print()).andExpect(status().isOk()).andExpect(jsonPath("$.results", hasSize(2))); + } + + @Test + public void shouldReturnDefaultLimitOffsetAppliedWorkflowsWhenLimitIsNotSpecified() throws Exception { + List workflowMocks = createLimit2AndOffset1For5WorkflowList(); + doReturn(workflowMocks).when(workflowManagerMock).list(any()); + mockMvc.perform( + get(RestPath.getWorkflowsPathNoSortAndLimit("1")) + .header(RestConstants.USER_ID_HEADER_PARAM, USER_ID).contentType(APPLICATION_JSON)) + .andDo(print()).andExpect(status().isOk()).andExpect(jsonPath("$.results", hasSize(2))); + } + + @Test + public void shouldReturnDefaultOffsetAppliedWorkflowsWhenOffsetIsNotSpecified() throws Exception { + List workflowMocks = createLimit1WorkflowList(); + doReturn(workflowMocks).when(workflowManagerMock).list(any()); + mockMvc.perform( + get(RestPath.getWorkflowsPathNoSortAndOffset("1")) + .header(RestConstants.USER_ID_HEADER_PARAM, USER_ID).contentType(APPLICATION_JSON)) + .andDo(print()).andExpect(status().isOk()).andExpect(jsonPath("$.results", hasSize(1))); + } + @Test public void shouldCreateWorkflowWhenCallingPostRESTRequest() throws Exception { Item item = new Item(); @@ -117,5 +248,22 @@ public class WorkflowControllerTest { return workflowList; } + private List createLimit2AndOffset1For5WorkflowList() { + List workflowList = new ArrayList<>(); + workflowList.add(createWorkflow(2, true)); + workflowList.add(createWorkflow(3, true)); + return workflowList; + } + + private List createLimit1WorkflowList() { + List workflowList = new ArrayList<>(); + workflowList.add(createWorkflow(0, true)); + return workflowList; + } + + + private Set getSupportedSortFields() { + return ImmutableSet.of(SORT_FIELD_NAME); + } } \ No newline at end of file diff --git a/workflow/workflow-designer-be/src/test/java/org/onap/sdc/workflow/services/impl/WorkflowManagerImplTest.java b/workflow/workflow-designer-be/src/test/java/org/onap/sdc/workflow/services/impl/WorkflowManagerImplTest.java index 17037d9b..500011b4 100644 --- a/workflow/workflow-designer-be/src/test/java/org/onap/sdc/workflow/services/impl/WorkflowManagerImplTest.java +++ b/workflow/workflow-designer-be/src/test/java/org/onap/sdc/workflow/services/impl/WorkflowManagerImplTest.java @@ -6,9 +6,14 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.onap.sdc.workflow.TestUtil.createItem; import static org.onap.sdc.workflow.TestUtil.createWorkflow; +import static org.onap.sdc.workflow.api.RestConstants.SORT_FIELD_NAME; import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; import java.util.List; + +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -21,6 +26,8 @@ import org.onap.sdc.workflow.services.impl.mappers.WorkflowMapper; import org.openecomp.sdc.versioning.ItemManager; import org.openecomp.sdc.versioning.types.Item; import org.openecomp.sdc.versioning.types.ItemStatus; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @@ -30,6 +37,7 @@ public class WorkflowManagerImplTest { private static final String WORKFLOW_TYPE = "WORKFLOW"; private static final String WORKFLOW_NAME_UNIQUE_TYPE = "WORKFLOW_NAME"; private List itemList; + private List workflowList; @Mock private WorkflowMapper workflowMapperMock; @@ -46,16 +54,21 @@ public class WorkflowManagerImplTest { @Before public void setUp() { - itemList = Arrays.asList(createItem(1, true, true), createItem(2, true, true), createItem(3, true, true)); - + itemList = Arrays.asList(createItem(1, true, true), createItem(2, true, true), createItem(3, true, true), + createItem(4, true, true), createItem(5, true, true)); + workflowList = Arrays.asList(createWorkflow(1, true), createWorkflow(2, true), createWorkflow(3, true), + createWorkflow(4, true), createWorkflow(5, true)); } @Test public void shouldReturnWorkflowVersionList() { - + PageRequest pageRequest = createPageRequest(2, 1, Sort.Direction.DESC, SORT_FIELD_NAME); doReturn(itemList).when(itemManagerMock).list(WorkflowManagerImpl.ITEM_PREDICATE); - workflowManager.list(); + for (int i=0; i workflows = workflowManager.list(pageRequest); + Assert.assertEquals(2, workflows.size()); + Iterator workflowIterator = workflows.iterator(); + Assert.assertEquals("workflowName3", workflowIterator.next().getName()); + Assert.assertEquals("workflowName2", workflowIterator.next().getName()); + } + + private PageRequest createPageRequest(int limit, int offset, + Sort.Direction sortOrder, String sortField) { + return PageRequest.of(offset, limit, sortOrder, sortField); + } + } \ No newline at end of file -- cgit 1.2.3-korg