diff --git a/rest/taskana-rest-spring-example/src/main/java/pro/taskana/ldap/LdapCacheTestImpl.java b/rest/taskana-rest-spring-example/src/main/java/pro/taskana/ldap/LdapCacheTestImpl.java index eb1231c35..359e0c520 100644 --- a/rest/taskana-rest-spring-example/src/main/java/pro/taskana/ldap/LdapCacheTestImpl.java +++ b/rest/taskana-rest-spring-example/src/main/java/pro/taskana/ldap/LdapCacheTestImpl.java @@ -1,8 +1,6 @@ package pro.taskana.ldap; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; +import java.util.*; import java.util.stream.Collectors; import pro.taskana.rest.resource.AccessIdResource; @@ -14,23 +12,113 @@ import pro.taskana.rest.resource.AccessIdResource; */ public class LdapCacheTestImpl implements LdapCache { + /** + * Dictionary is a {@link Map} collection that contains {@link AccessIdResource} as key (user) + * and {@link List} as value (groups of which the user is a member) . + */ + private static Map> users; + @Override public List findMatchingAccessId(String searchFor, int maxNumerOfReturnedAccessIds) { + return findAcessIdResource(searchFor, maxNumerOfReturnedAccessIds, false); + } + + @Override + public List findGroupsOfUser(String searchFor, int maxNumerOfReturnedAccessIds) { + if (users == null) { + addUsersToGroups(); + } + return findAcessIdResource(searchFor, maxNumerOfReturnedAccessIds, true); + } + + private List findAcessIdResource (String searchFor, int maxNumerOfReturnedAccessIds, boolean groupMember) { List usersAndGroups = accessIds.stream() - .filter(t -> (t.getName().toLowerCase().contains(searchFor.toLowerCase()) - || t.getAccessId().toLowerCase().contains(searchFor.toLowerCase()))) - .collect(Collectors.toList()); + .filter(t -> (t.getName().toLowerCase().contains(searchFor.toLowerCase()) + || t.getAccessId().toLowerCase().contains(searchFor.toLowerCase()))) + .collect(Collectors.toList()); + + List usersAndGroupsAux = new ArrayList<>(usersAndGroups); + if (groupMember) { + usersAndGroupsAux.forEach(item -> { + if (users.get(item) != null) { + usersAndGroups.addAll(users.get(item)); + } + }); + } usersAndGroups.sort((AccessIdResource a, AccessIdResource b) -> { return a.getAccessId().compareToIgnoreCase(b.getAccessId()); }); List result = usersAndGroups.subList(0, - Math.min(usersAndGroups.size(), maxNumerOfReturnedAccessIds)); + Math.min(usersAndGroups.size(), maxNumerOfReturnedAccessIds)); return result; } + private void addUsersToGroups() { + List groups = new ArrayList<>(); + users = new HashMap<>(); + + accessIds.forEach(item -> { + if (!item.getAccessId().contains("ou=groups")) { + users.put(item, new ArrayList<>()); + } else { + groups.add(item); + } + }); + + int groupNumber = 0; + List group0 = new ArrayList<>(), group1 = new ArrayList<>(), group2 = new ArrayList<>(), group3 = new ArrayList<>(); + + for (AccessIdResource group : groups) { + switch (groupNumber) { + case 0: + group0.add(group); + break; + case 1: + group1.add(group); + break; + case 2: + group2.add(group); + break; + case 3: + group3.add(group); + break; + } + if (groupNumber != 3) { + groupNumber++; + } else { + groupNumber = 0; + } + } + + int countUser = 0; + for (AccessIdResource item : accessIds) { + if (!item.getAccessId().contains("ou=groups")) { + switch (countUser) { + case 0: + users.put(item, group0); + break; + case 1: + users.put(item, group1); + break; + case 2: + users.put(item, group2); + break; + case 3: + users.put(item, group3); + break; + } + } + if (countUser != 3) { + countUser++; + } else { + countUser = 0; + } + } + } + private static List accessIds = new ArrayList<>(Arrays.asList( new AccessIdResource("Martin, Rojas Miguel Angel", "user_1_1"), new AccessIdResource("Lengl, Marcel", "user_1_2"), diff --git a/rest/taskana-rest-spring-example/src/main/resources/application.properties b/rest/taskana-rest-spring-example/src/main/resources/application.properties index c31566ba4..ff557ed27 100644 --- a/rest/taskana-rest-spring-example/src/main/resources/application.properties +++ b/rest/taskana-rest-spring-example/src/main/resources/application.properties @@ -39,6 +39,7 @@ taskana.ldap.groupSearchFilterValue=groupOfUniqueNames taskana.ldap.groupNameAttribute=cn taskana.ldap.minSearchForLength=3 taskana.ldap.maxNumberOfReturnedAccessIds=50 +taskana.ldap.groupsOfUser=memberUid ####### JobScheduler cron expression that specifies when the JobSchedler runs taskana.jobscheduler.async.cron=0 * * * * * ####### cache static resources properties diff --git a/rest/taskana-rest-spring-example/src/test/java/pro/taskana/rest/WorkbasketAccessItemControllerIntTest.java b/rest/taskana-rest-spring-example/src/test/java/pro/taskana/rest/WorkbasketAccessItemControllerIntTest.java new file mode 100644 index 000000000..6a642753a --- /dev/null +++ b/rest/taskana-rest-spring-example/src/test/java/pro/taskana/rest/WorkbasketAccessItemControllerIntTest.java @@ -0,0 +1,121 @@ +package pro.taskana.rest; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.hateoas.Link; +import org.springframework.hateoas.PagedResources; +import org.springframework.hateoas.hal.Jackson2HalModule; +import org.springframework.http.*; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestTemplate; +import pro.taskana.rest.resource.WorkbasketAccesItemExtendedResource; + +import java.util.Collections; + +import static org.junit.Assert.*; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = RestConfiguration.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = {"devMode=true"}) +public class WorkbasketAccessItemControllerIntTest { + + String url = "http://127.0.0.1:"; + RestTemplate template; + HttpEntity request; + @LocalServerPort + int port; + + @Before + public void before() { + template = getRestTemplate(); + HttpHeaders headers = new HttpHeaders(); + headers.add("Authorization", "Basic dGVhbWxlYWRfMTp0ZWFtbGVhZF8x"); + request = new HttpEntity(headers); + } + + @Test + public void testGetAllWorkbasketAccessItems() { + ResponseEntity> response = template.exchange( + url + port + "/v1/workbasket-access", HttpMethod.GET, request, + new ParameterizedTypeReference>() { + }); + assertNotNull(response.getBody().getLink(Link.REL_SELF)); + } + + @Test + public void testGetWorkbasketAccessItemsKeepingFilters() { + String parameters = "/v1/workbasket-access/?sort-by=workbasket-key&order=asc&page=1&page-size=9&access-ids=user_1_1"; + ResponseEntity> response = template.exchange( + url + port + parameters, HttpMethod.GET, request, + new ParameterizedTypeReference>() { + }); + assertNotNull(response.getBody().getLink(Link.REL_SELF)); + assertTrue(response.getBody() + .getLink(Link.REL_SELF) + .getHref() + .endsWith(parameters)); + } + + @Test + public void testThrowsExceptionIfInvalidFilterIsUsed() { + try { + template.exchange( + url + port + "/v1/workbasket-access/?sort-by=workbasket-key&order=asc&page=1&page-size=9&invalid=user_1_1", HttpMethod.GET, request, + new ParameterizedTypeReference>() { + }); + fail(); + } catch (HttpClientErrorException e) { + assertEquals(HttpStatus.BAD_REQUEST, e.getStatusCode()); + assertTrue(e.getResponseBodyAsString().contains("[invalid]")); + } + } + + @Test + public void testGetSecondPageSortedByWorkbasketKey() { + String parameters = "/v1/workbasket-access/?sort-by=workbasket-key&order=asc&page=2&page-size=9&access-ids=user_1_1"; + ResponseEntity> response = template.exchange( + url + port + parameters, HttpMethod.GET, request, + new ParameterizedTypeReference>() { + }); + assertEquals(1, response.getBody().getContent().size()); + assertEquals("user_1_1", response.getBody().getContent().iterator().next().accessId); + assertNotNull(response.getBody().getLink(Link.REL_SELF)); + assertTrue(response.getBody() + .getLink(Link.REL_SELF) + .getHref() + .endsWith(parameters)); + assertNotNull(response.getBody().getLink(Link.REL_FIRST)); + assertNotNull(response.getBody().getLink(Link.REL_LAST)); + assertEquals(9, response.getBody().getMetadata().getSize()); + assertEquals(1, response.getBody().getMetadata().getTotalElements()); + assertEquals(1, response.getBody().getMetadata().getTotalPages()); + assertEquals(1, response.getBody().getMetadata().getNumber()); + } + + /** + * Return a REST template which is capable of dealing with responses in HAL format + * + * @return RestTemplate + */ + private RestTemplate getRestTemplate() { + ObjectMapper mapper = new ObjectMapper(); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + mapper.registerModule(new Jackson2HalModule()); + + MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); + converter.setSupportedMediaTypes(MediaType.parseMediaTypes("application/hal+json")); + converter.setObjectMapper(mapper); + + RestTemplate template = new RestTemplate(Collections.>singletonList(converter)); + return template; + } +} diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/ldap/LdapCache.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/ldap/LdapCache.java index 80d3cf2db..f53b13a6a 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/ldap/LdapCache.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/ldap/LdapCache.java @@ -21,4 +21,12 @@ public interface LdapCache { * @return a List of access ids for users and group where the name or id contains the search string. */ List findMatchingAccessId(String searchFor, int maxNumerOfReturnedAccessIds); + + /** + * + * @param searchFor the search string. The search is performed over names and ids of group . + * @param maxNumerOfReturnedAccessIds + * @return + */ + List findGroupsOfUser(String searchFor, int maxNumerOfReturnedAccessIds); } diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/ldap/LdapClient.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/ldap/LdapClient.java index 5ea6b9c86..b04135062 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/ldap/LdapClient.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/ldap/LdapClient.java @@ -1,10 +1,5 @@ package pro.taskana.ldap; -import java.util.List; - -import javax.annotation.PostConstruct; -import javax.naming.directory.SearchControls; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -17,12 +12,15 @@ import org.springframework.ldap.filter.EqualsFilter; import org.springframework.ldap.filter.OrFilter; import org.springframework.ldap.filter.WhitespaceWildcardsFilter; import org.springframework.stereotype.Component; - import pro.taskana.exceptions.InvalidArgumentException; import pro.taskana.exceptions.SystemException; import pro.taskana.impl.util.LoggerUtils; import pro.taskana.rest.resource.AccessIdResource; +import javax.annotation.PostConstruct; +import javax.naming.directory.SearchControls; +import java.util.List; + /** * Class for Ldap access. * @@ -51,6 +49,7 @@ public class LdapClient { private String groupSearchFilterName; private String groupSearchFilterValue; private String groupNameAttribute; + private String groupsOfUser; private int minSearchForLength; private int maxNumberOfReturnedAccessIds; @@ -85,6 +84,7 @@ public class LdapClient { groupSearchFilterName = getGroupSearchFilterName(); groupSearchFilterValue = getGroupSearchFilterValue(); groupNameAttribute = getGroupNameAttribute(); + groupsOfUser = getGroupsOfUser(); ldapTemplate.setDefaultCountLimit(maxNumberOfReturnedAccessIds); @@ -120,6 +120,9 @@ public class LdapClient { if (groupNameAttribute == null) { message += " taskana.ldap.groupNameAttribute is not configured."; } + if (groupsOfUser == null) { + message += " taskana.ldap.groupsOfUser is not configured."; + } if (!message.equals(emptyMessage)) { throw new SystemException(message); } @@ -131,12 +134,9 @@ public class LdapClient { LOGGER.debug("entry to searchUsersAndGroups(name = {})", name); if (!active) { throw new SystemException( - "LdapClient was called but is not active due to missing configuration: " + message); - } - if (name == null || name.length() < minSearchForLength) { - throw new InvalidArgumentException("searchFor string " + name + " is too short. Minimum Length = " - + getMinSearchForLength()); + "LdapClient was called but is not active due to missing configuration: " + message); } + testMinSearchForLength(name); List users = searchUsersByName(name); users.addAll(searchGroupsByName(name)); @@ -146,7 +146,7 @@ public class LdapClient { List result = users.subList(0, Math.min(users.size(), maxNumberOfReturnedAccessIds)); LOGGER.debug("exit from searchUsersAndGroups(name = {}). Returning {} users and groups: {}", name, users.size(), - LoggerUtils.listToString(result)); + LoggerUtils.listToString(result)); return result; } @@ -155,12 +155,9 @@ public class LdapClient { LOGGER.debug("entry to searchUsersByName(name = {}).", name); if (!active) { throw new SystemException( - "LdapClient was called but is not active due to missing configuration: " + message); - } - if (name == null || name.length() < minSearchForLength) { - throw new InvalidArgumentException("searchFor string " + name + " is too short. Minimum Length = " - + getMinSearchForLength()); + "LdapClient was called but is not active due to missing configuration: " + message); } + testMinSearchForLength(name); final AndFilter andFilter = new AndFilter(); andFilter.and(new EqualsFilter(getUserSearchFilterName(), getUserSearchFilterValue())); @@ -172,12 +169,12 @@ public class LdapClient { andFilter.and(orFilter); String[] userAttributesToReturn = {getUserFirstnameAttribute(), getUserLastnameAttribute(), - getUserIdAttribute()}; + getUserIdAttribute()}; final List accessIds = ldapTemplate.search(getUserSearchBase(), andFilter.encode(), - SearchControls.SUBTREE_SCOPE, userAttributesToReturn, new UserContextMapper()); + SearchControls.SUBTREE_SCOPE, userAttributesToReturn, new UserContextMapper()); LOGGER.debug("exit from searchUsersByName. Retrieved the following users: {}.", - LoggerUtils.listToString(accessIds)); + LoggerUtils.listToString(accessIds)); return accessIds; } @@ -186,12 +183,9 @@ public class LdapClient { LOGGER.debug("entry to searchGroupsByName(name = {}).", name); if (!active) { throw new SystemException( - "LdapClient was called but is not active due to missing configuration: " + message); - } - if (name == null || name.length() < minSearchForLength) { - throw new InvalidArgumentException("searchFor string " + name + " is too short. Minimum Length = " - + getMinSearchForLength()); + "LdapClient was called but is not active due to missing configuration: " + message); } + testMinSearchForLength(name); final AndFilter andFilter = new AndFilter(); andFilter.and(new EqualsFilter(getGroupSearchFilterName(), getGroupSearchFilterValue())); @@ -210,13 +204,42 @@ public class LdapClient { } final List accessIds = ldapTemplate.search(getGroupSearchBase(), andFilter.encode(), - SearchControls.SUBTREE_SCOPE, groupAttributesToReturn, new GroupContextMapper()); + SearchControls.SUBTREE_SCOPE, groupAttributesToReturn, new GroupContextMapper()); LOGGER.debug("Exit from searchGroupsByName. Retrieved the following groups: {}", - LoggerUtils.listToString(accessIds)); + LoggerUtils.listToString(accessIds)); return accessIds; } + public List searchGroupsofUsersIsMember(final String name) throws InvalidArgumentException { + LOGGER.debug("entry to searchGroupsofUsersIsMember(name = {}).", name); + if (!active) { + throw new SystemException( + "LdapClient was called but is not active due to missing configuration: " + message); + } + testMinSearchForLength(name); + + final AndFilter andFilter = new AndFilter(); + andFilter.and(new WhitespaceWildcardsFilter(getGroupNameAttribute(), "")); + andFilter.and(new EqualsFilter(getGroupsOfUser(), name)); + + String[] userAttributesToReturn = {getUserIdAttribute(), getGroupNameAttribute()}; + + final List accessIds = ldapTemplate.search(getGroupSearchBase(), andFilter.encode(), + SearchControls.SUBTREE_SCOPE, userAttributesToReturn, new GroupContextMapper()); + LOGGER.debug("exit from searchGroupsofUsersIsMember. Retrieved the following users: {}.", + LoggerUtils.listToString(accessIds)); + return accessIds; + + } + + private void testMinSearchForLength(final String name) throws InvalidArgumentException { + if (name == null || name.length() < minSearchForLength) { + throw new InvalidArgumentException("searchFor string " + name + " is too short. Minimum Length = " + + getMinSearchForLength()); + } + } + public boolean useLdap() { String useLdap = env.getProperty(TASKANA_USE_LDAP_PROP_NAME); if (useLdap == null || useLdap.isEmpty()) { @@ -282,6 +305,10 @@ public class LdapClient { return maxNumberOfReturnedAccessIds; } + public String getGroupsOfUser() { + return env.getProperty("taskana.ldap.groupsOfUser"); + } + /** * Context Mapper for user entries. */ diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/AccessIdController.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/AccessIdController.java index 9dcbaf03d..8463858fe 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/AccessIdController.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/AccessIdController.java @@ -35,17 +35,28 @@ public class AccessIdController { @GetMapping public ResponseEntity> validateAccessIds( - @RequestParam String searchFor) throws InvalidArgumentException { + @RequestParam String searchFor, @RequestParam(required = false) boolean searchInGroups) throws InvalidArgumentException { if (searchFor.length() < ldapClient.getMinSearchForLength()) { throw new InvalidArgumentException("searchFor string '" + searchFor + "' is too short. Minimum searchFor length = " + ldapClient.getMinSearchForLength()); } if (ldapClient.useLdap()) { - return new ResponseEntity<>(ldapClient.searchUsersAndGroups(searchFor), HttpStatus.OK); + List accessIdUsers = ldapClient.searchUsersAndGroups(searchFor); + if (searchInGroups) { + List accessIdGroups = ldapClient.searchGroupsofUsersIsMember(searchFor); + accessIdUsers.addAll(accessIdGroups); + } + return new ResponseEntity<>(accessIdUsers, HttpStatus.OK); } else if (ldapCache != null) { - return new ResponseEntity<>( - ldapCache.findMatchingAccessId(searchFor, ldapClient.getMaxNumberOfReturnedAccessIds()), - HttpStatus.OK); + if (searchInGroups) { + return new ResponseEntity<>( + ldapCache.findGroupsOfUser(searchFor, ldapClient.getMaxNumberOfReturnedAccessIds()), + HttpStatus.OK); + } else { + return new ResponseEntity<>( + ldapCache.findMatchingAccessId(searchFor, ldapClient.getMaxNumberOfReturnedAccessIds()), + HttpStatus.OK); + } } else { return new ResponseEntity<>(new ArrayList<>(), HttpStatus.NOT_FOUND); } diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/WorkbasketAccessItemController.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/WorkbasketAccessItemController.java new file mode 100644 index 000000000..348580bb4 --- /dev/null +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/WorkbasketAccessItemController.java @@ -0,0 +1,145 @@ +package pro.taskana.rest; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.hateoas.PagedResources; +import org.springframework.hateoas.config.EnableHypermediaSupport; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.util.MultiValueMap; +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 pro.taskana.BaseQuery; +import pro.taskana.WorkbasketAccessItemExtended; +import pro.taskana.WorkbasketAccessItemExtendedQuery; +import pro.taskana.WorkbasketService; +import pro.taskana.exceptions.InvalidArgumentException; +import pro.taskana.exceptions.NotAuthorizedException; +import pro.taskana.rest.resource.WorkbasketAccesItemExtendedResource; +import pro.taskana.rest.resource.assembler.WorkbasketAccessItemExtendedAssembler; + +import java.util.List; + +/** + * Controller for Workbasket access. + */ +@RestController +@EnableHypermediaSupport(type = EnableHypermediaSupport.HypermediaType.HAL) +@RequestMapping(path = "/v1/workbasket-access", produces = "application/hal+json") +public class WorkbasketAccessItemController extends AbstractPagingController { + + private static final String LIKE = "%"; + private static final String WORKBASKET_KEY = "workbasket-key"; + private static final String WORKBASKET_KEY_LIKE = "workbasket-key-like"; + private static final String ACCESS_ID = "access-id"; + private static final String ACCESS_ID_LIKE = "access-id-like"; + private static final String ACCESS_IDS = "access-ids"; + + private static final String SORT_BY = "sort-by"; + private static final String SORT_DIRECTION = "order"; + + private static final String PAGING_PAGE = "page"; + private static final String PAGING_PAGE_SIZE = "page-size"; + + @Autowired + private WorkbasketService workbasketService; + + @GetMapping + public ResponseEntity> getWorkbasketAccessItems( + @RequestParam MultiValueMap params) + throws NotAuthorizedException, InvalidArgumentException { + + WorkbasketAccessItemExtendedQuery query = workbasketService.createWorkbasketAccessItemExtendedQuery(); + query = getAccessIds(query, params); + query = applyFilterParams(query, params); + query = applySortingParams(query, params); + + PagedResources.PageMetadata pageMetadata = null; + List workbasketAccessItemsExtended; + String page = params.getFirst(PAGING_PAGE); + String pageSize = params.getFirst(PAGING_PAGE_SIZE); + params.remove(PAGING_PAGE); + params.remove(PAGING_PAGE_SIZE); + validateNoInvalidParameterIsLeft(params); + if (page != null && pageSize != null) { + // paging + long totalElements = query.count(); + pageMetadata = initPageMetadata(pageSize, page, totalElements); + workbasketAccessItemsExtended = query.listPage((int) pageMetadata.getNumber(), + (int) pageMetadata.getSize()); + } else if (page == null && pageSize == null) { + // not paging + workbasketAccessItemsExtended = query.list(); + } else { + throw new InvalidArgumentException("Paging information is incomplete."); + } + + WorkbasketAccessItemExtendedAssembler assembler = new WorkbasketAccessItemExtendedAssembler(); + PagedResources pagedResources = assembler.toResources(workbasketAccessItemsExtended, + pageMetadata); + + return new ResponseEntity<>(pagedResources, HttpStatus.OK); + } + + private WorkbasketAccessItemExtendedQuery getAccessIds(WorkbasketAccessItemExtendedQuery query, + MultiValueMap params) throws InvalidArgumentException { + if (params.containsKey(ACCESS_IDS)) { + String[] accessIds = extractCommaSeparatedFields(params.get(ACCESS_IDS)); + query.accessIdIn(accessIds); + params.remove(ACCESS_IDS); + } + return query; + } + + private WorkbasketAccessItemExtendedQuery applyFilterParams(WorkbasketAccessItemExtendedQuery query, + MultiValueMap params) throws InvalidArgumentException { + if (params.containsKey(WORKBASKET_KEY)) { + String[] keys = extractCommaSeparatedFields(params.get(WORKBASKET_KEY)); + query.workbasketKeyIn(keys); + params.remove(WORKBASKET_KEY); + } + if (params.containsKey(WORKBASKET_KEY_LIKE)) { + query.workbasketKeyLike(LIKE + params.get(WORKBASKET_KEY_LIKE).get(0) + LIKE); + params.remove(WORKBASKET_KEY_LIKE); + } + if (params.containsKey(ACCESS_ID)) { + String[] accessId = extractCommaSeparatedFields(params.get(ACCESS_ID)); + query.accessIdIn(accessId); + params.remove(ACCESS_ID); + } + if (params.containsKey(ACCESS_ID_LIKE)) { + query.accessIdLike(LIKE + params.get(ACCESS_ID_LIKE).get(0) + LIKE); + params.remove(ACCESS_ID_LIKE); + } + return query; + } + + private WorkbasketAccessItemExtendedQuery applySortingParams(WorkbasketAccessItemExtendedQuery query, MultiValueMap params) + throws IllegalArgumentException { + // sorting + String sortBy = params.getFirst(SORT_BY); + if (sortBy != null) { + BaseQuery.SortDirection sortDirection; + if (params.getFirst(SORT_DIRECTION) != null && "desc".equals(params.getFirst(SORT_DIRECTION))) { + sortDirection = BaseQuery.SortDirection.DESCENDING; + } else { + sortDirection = BaseQuery.SortDirection.ASCENDING; + } + switch (sortBy) { + case (WORKBASKET_KEY): + query = query.orderByWorkbasketKey(sortDirection); + break; + case (ACCESS_ID): + query = query.orderByAccessId(sortDirection); + break; + default: + throw new IllegalArgumentException("Unknown order '" + sortBy + "'"); + } + } + params.remove(SORT_BY); + params.remove(SORT_DIRECTION); + return query; + } + +} diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/WorkbasketController.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/WorkbasketController.java index 62c7367eb..08a56288d 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/WorkbasketController.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/WorkbasketController.java @@ -1,9 +1,5 @@ package pro.taskana.rest; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.hateoas.PagedResources; import org.springframework.hateoas.PagedResources.PageMetadata; @@ -14,40 +10,19 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.MultiValueMap; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -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.*; import pro.taskana.BaseQuery.SortDirection; -import pro.taskana.Workbasket; -import pro.taskana.WorkbasketAccessItem; -import pro.taskana.WorkbasketPermission; -import pro.taskana.WorkbasketQuery; -import pro.taskana.WorkbasketService; -import pro.taskana.WorkbasketSummary; -import pro.taskana.WorkbasketType; -import pro.taskana.exceptions.DomainNotFoundException; -import pro.taskana.exceptions.InvalidArgumentException; -import pro.taskana.exceptions.InvalidWorkbasketException; -import pro.taskana.exceptions.NotAuthorizedException; -import pro.taskana.exceptions.WorkbasketAlreadyExistException; -import pro.taskana.exceptions.WorkbasketInUseException; -import pro.taskana.exceptions.WorkbasketNotFoundException; +import pro.taskana.*; +import pro.taskana.exceptions.*; import pro.taskana.rest.resource.DistributionTargetResource; import pro.taskana.rest.resource.WorkbasketAccessItemResource; import pro.taskana.rest.resource.WorkbasketResource; import pro.taskana.rest.resource.WorkbasketSummaryResource; -import pro.taskana.rest.resource.assembler.DistributionTargetListAssembler; -import pro.taskana.rest.resource.assembler.WorkbasketAccessItemListAssembler; -import pro.taskana.rest.resource.assembler.WorkbasketAccessItemAssembler; -import pro.taskana.rest.resource.assembler.WorkbasketAssembler; -import pro.taskana.rest.resource.assembler.WorkbasketSummaryResourcesAssembler; +import pro.taskana.rest.resource.assembler.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; /** * Controller for all {@link Workbasket} related endpoints. @@ -94,7 +69,7 @@ public class WorkbasketController extends AbstractPagingController { @GetMapping @Transactional(readOnly = true, rollbackFor = Exception.class) public ResponseEntity> getWorkbaskets( - @RequestParam MultiValueMap params) throws InvalidArgumentException { + @RequestParam MultiValueMap params) throws InvalidArgumentException { WorkbasketQuery query = workbasketService.createWorkbasketQuery(); query = applySortingParams(query, params); @@ -112,7 +87,7 @@ public class WorkbasketController extends AbstractPagingController { long totalElements = query.count(); pageMetadata = initPageMetadata(pageSize, page, totalElements); workbasketSummaries = query.listPage((int) pageMetadata.getNumber(), - (int) pageMetadata.getSize()); + (int) pageMetadata.getSize()); } else if (page == null && pageSize == null) { // not paging workbasketSummaries = query.list(); @@ -122,7 +97,7 @@ public class WorkbasketController extends AbstractPagingController { WorkbasketSummaryResourcesAssembler assembler = new WorkbasketSummaryResourcesAssembler(); PagedResources pagedResources = assembler.toResources(workbasketSummaries, - pageMetadata); + pageMetadata); return new ResponseEntity<>(pagedResources, HttpStatus.OK); } @@ -130,7 +105,7 @@ public class WorkbasketController extends AbstractPagingController { @GetMapping(path = "/{workbasketId}") @Transactional(readOnly = true, rollbackFor = Exception.class) public ResponseEntity getWorkbasket(@PathVariable(value = "workbasketId") String workbasketId) - throws WorkbasketNotFoundException, NotAuthorizedException { + throws WorkbasketNotFoundException, NotAuthorizedException { ResponseEntity result; Workbasket workbasket = workbasketService.getWorkbasket(workbasketId); result = new ResponseEntity<>(workbasketAssembler.toResource(workbasket), HttpStatus.OK); @@ -140,7 +115,7 @@ public class WorkbasketController extends AbstractPagingController { @DeleteMapping(path = "/{workbasketId}") @Transactional(rollbackFor = Exception.class) public ResponseEntity deleteWorkbasket(@PathVariable(value = "workbasketId") String workbasketId) - throws WorkbasketNotFoundException, NotAuthorizedException, WorkbasketInUseException, InvalidArgumentException { + throws WorkbasketNotFoundException, NotAuthorizedException, WorkbasketInUseException, InvalidArgumentException { ResponseEntity result = ResponseEntity.status(HttpStatus.NO_CONTENT).build(); workbasketService.deleteWorkbasket(workbasketId); return result; @@ -149,8 +124,8 @@ public class WorkbasketController extends AbstractPagingController { @PostMapping @Transactional(rollbackFor = Exception.class) public ResponseEntity createWorkbasket(@RequestBody WorkbasketResource workbasketResource) - throws InvalidWorkbasketException, NotAuthorizedException, WorkbasketAlreadyExistException, - WorkbasketNotFoundException, DomainNotFoundException { + throws InvalidWorkbasketException, NotAuthorizedException, WorkbasketAlreadyExistException, + WorkbasketNotFoundException, DomainNotFoundException { Workbasket workbasket = workbasketAssembler.toModel(workbasketResource); workbasket = workbasketService.createWorkbasket(workbasket); return new ResponseEntity<>(workbasketAssembler.toResource(workbasket), HttpStatus.CREATED); @@ -159,9 +134,9 @@ public class WorkbasketController extends AbstractPagingController { @PutMapping(path = "/{workbasketId}") @Transactional(rollbackFor = Exception.class) public ResponseEntity updateWorkbasket( - @PathVariable(value = "workbasketId") String workbasketId, - @RequestBody WorkbasketResource workbasketResource) - throws InvalidWorkbasketException, WorkbasketNotFoundException, NotAuthorizedException { + @PathVariable(value = "workbasketId") String workbasketId, + @RequestBody WorkbasketResource workbasketResource) + throws InvalidWorkbasketException, WorkbasketNotFoundException, NotAuthorizedException { ResponseEntity result; if (workbasketId.equals(workbasketResource.workbasketId)) { Workbasket workbasket = workbasketAssembler.toModel(workbasketResource); @@ -169,9 +144,9 @@ public class WorkbasketController extends AbstractPagingController { result = ResponseEntity.ok(workbasketAssembler.toResource(workbasket)); } else { throw new InvalidWorkbasketException( - "Target-WB-ID('" + workbasketId - + "') is not identical with the WB-ID of to object which should be updated. ID=('" - + workbasketResource.getId() + "')"); + "Target-WB-ID('" + workbasketId + + "') is not identical with the WB-ID of to object which should be updated. ID=('" + + workbasketResource.getId() + "')"); } return result; @@ -180,14 +155,14 @@ public class WorkbasketController extends AbstractPagingController { @GetMapping(path = "/{workbasketId}/workbasketAccessItems") @Transactional(readOnly = true, rollbackFor = Exception.class) public ResponseEntity> getWorkbasketAccessItems( - @PathVariable(value = "workbasketId") String workbasketId) - throws NotAuthorizedException, WorkbasketNotFoundException { + @PathVariable(value = "workbasketId") String workbasketId) + throws NotAuthorizedException, WorkbasketNotFoundException { ResponseEntity> result; List accessItems = workbasketService.getWorkbasketAccessItems(workbasketId); Resources accessItemListResource = accessItemListAssembler - .toResource(workbasketId, accessItems); + .toResource(workbasketId, accessItems); result = new ResponseEntity<>(accessItemListResource, HttpStatus.OK); return result; } @@ -195,9 +170,9 @@ public class WorkbasketController extends AbstractPagingController { @PutMapping(value = "/{workbasketId}/workbasketAccessItems") @Transactional(rollbackFor = Exception.class) public ResponseEntity> setWorkbasketAccessItems( - @PathVariable(value = "workbasketId") String workbasketId, - @RequestBody List workbasketAccessResourceItems) - throws NotAuthorizedException, InvalidArgumentException, WorkbasketNotFoundException { + @PathVariable(value = "workbasketId") String workbasketId, + @RequestBody List workbasketAccessResourceItems) + throws NotAuthorizedException, InvalidArgumentException, WorkbasketNotFoundException { if (workbasketAccessResourceItems == null) { throw new InvalidArgumentException("Can“t create something with NULL body-value."); } @@ -208,7 +183,7 @@ public class WorkbasketController extends AbstractPagingController { List updatedWbAccessItems = workbasketService.getWorkbasketAccessItems(workbasketId); Resources accessItemListResource = accessItemListAssembler - .toResource(workbasketId, updatedWbAccessItems); + .toResource(workbasketId, updatedWbAccessItems); return new ResponseEntity<>(accessItemListResource, HttpStatus.OK); } @@ -216,13 +191,13 @@ public class WorkbasketController extends AbstractPagingController { @GetMapping(path = "/{workbasketId}/distribution-targets") @Transactional(readOnly = true, rollbackFor = Exception.class) public ResponseEntity> getDistributionTargets( - @PathVariable(value = "workbasketId") String workbasketId) - throws WorkbasketNotFoundException, NotAuthorizedException { + @PathVariable(value = "workbasketId") String workbasketId) + throws WorkbasketNotFoundException, NotAuthorizedException { ResponseEntity> result; List distributionTargets = workbasketService.getDistributionTargets(workbasketId); Resources distributionTargetListResource = distributionTargetListAssembler - .toResource(workbasketId, distributionTargets); + .toResource(workbasketId, distributionTargets); result = new ResponseEntity<>(distributionTargetListResource, HttpStatus.OK); return result; } @@ -230,13 +205,13 @@ public class WorkbasketController extends AbstractPagingController { @PutMapping(path = "/{workbasketId}/distribution-targets") @Transactional(rollbackFor = Exception.class) public ResponseEntity> setDistributionTargetsForWorkbasketId( - @PathVariable(value = "workbasketId") String sourceWorkbasketId, - @RequestBody List targetWorkbasketIds) throws WorkbasketNotFoundException, NotAuthorizedException { + @PathVariable(value = "workbasketId") String sourceWorkbasketId, + @RequestBody List targetWorkbasketIds) throws WorkbasketNotFoundException, NotAuthorizedException { workbasketService.setDistributionTargets(sourceWorkbasketId, targetWorkbasketIds); List distributionTargets = workbasketService.getDistributionTargets(sourceWorkbasketId); Resources distributionTargetListResource = distributionTargetListAssembler - .toResource(sourceWorkbasketId, distributionTargets); + .toResource(sourceWorkbasketId, distributionTargets); return new ResponseEntity<>(distributionTargetListResource, HttpStatus.OK); } @@ -244,8 +219,8 @@ public class WorkbasketController extends AbstractPagingController { @DeleteMapping(path = "/distribution-targets/{workbasketId}") @Transactional(rollbackFor = Exception.class) public ResponseEntity> removeDistributionTargetForWorkbasketId( - @PathVariable(value = "workbasketId") String targetWorkbasketId) - throws WorkbasketNotFoundException, NotAuthorizedException { + @PathVariable(value = "workbasketId") String targetWorkbasketId) + throws WorkbasketNotFoundException, NotAuthorizedException { List sourceWorkbaskets = workbasketService.getDistributionSources(targetWorkbasketId); for (WorkbasketSummary source : sourceWorkbaskets) { workbasketService.removeDistributionTarget(source.getId(), targetWorkbasketId); @@ -255,7 +230,7 @@ public class WorkbasketController extends AbstractPagingController { } private WorkbasketQuery applySortingParams(WorkbasketQuery query, MultiValueMap params) - throws IllegalArgumentException { + throws IllegalArgumentException { // sorting String sortBy = params.getFirst(SORT_BY); if (sortBy != null) { @@ -291,7 +266,7 @@ public class WorkbasketController extends AbstractPagingController { } private WorkbasketQuery applyFilterParams(WorkbasketQuery query, - MultiValueMap params) throws InvalidArgumentException { + MultiValueMap params) throws InvalidArgumentException { if (params.containsKey(NAME)) { String[] names = extractCommaSeparatedFields(params.get(NAME)); query.nameIn(names); diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/resource/WorkbasketAccesItemExtendedResource.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/resource/WorkbasketAccesItemExtendedResource.java new file mode 100644 index 000000000..0f2eb4fe9 --- /dev/null +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/resource/WorkbasketAccesItemExtendedResource.java @@ -0,0 +1,23 @@ +package pro.taskana.rest.resource; + +import org.springframework.hateoas.core.Relation; + +import javax.validation.constraints.NotNull; + +/** + * Resource class for {@link pro.taskana.WorkbasketAccessItem}. + */ +@Relation(collectionRelation = "accessItems") +public class WorkbasketAccesItemExtendedResource extends WorkbasketAccessItemResource { + + @NotNull + public String workbasketKey; + + public String getWorkbasketKey() { + return workbasketKey; + } + + public void setWorkbasketKey(String workbasketKey) { + this.workbasketKey = workbasketKey; + } +} diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/resource/assembler/WorkbasketAccessItemAssembler.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/resource/assembler/WorkbasketAccessItemAssembler.java index 897c53693..7a748f53c 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/resource/assembler/WorkbasketAccessItemAssembler.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/resource/assembler/WorkbasketAccessItemAssembler.java @@ -1,12 +1,8 @@ package pro.taskana.rest.resource.assembler; -import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; -import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; - import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; - import pro.taskana.WorkbasketAccessItem; import pro.taskana.WorkbasketService; import pro.taskana.exceptions.NotAuthorizedException; @@ -15,6 +11,9 @@ import pro.taskana.impl.WorkbasketAccessItemImpl; import pro.taskana.rest.WorkbasketController; import pro.taskana.rest.resource.WorkbasketAccessItemResource; +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; + /** * Transforms {@link WorkbasketAccessItem} to its resource counterpart {@link WorkbasketAccessItemResource} and vice * versa. @@ -26,7 +25,7 @@ public class WorkbasketAccessItemAssembler { private WorkbasketService workbasketService; public WorkbasketAccessItemResource toResource(WorkbasketAccessItem wbAccItem) - throws NotAuthorizedException, WorkbasketNotFoundException { + throws NotAuthorizedException, WorkbasketNotFoundException { WorkbasketAccessItemResource resource = new WorkbasketAccessItemResource(); BeanUtils.copyProperties(wbAccItem, resource); // property is named different, so it needs to be set by hand @@ -37,7 +36,7 @@ public class WorkbasketAccessItemAssembler { public WorkbasketAccessItem toModel(WorkbasketAccessItemResource wbAccItemRecource) { WorkbasketAccessItemImpl wbAccItemModel = (WorkbasketAccessItemImpl) workbasketService.newWorkbasketAccessItem( - wbAccItemRecource.workbasketId, wbAccItemRecource.accessId); + wbAccItemRecource.workbasketId, wbAccItemRecource.accessId); BeanUtils.copyProperties(wbAccItemRecource, wbAccItemModel); wbAccItemModel.setId(wbAccItemRecource.accessItemId); @@ -45,11 +44,11 @@ public class WorkbasketAccessItemAssembler { } private WorkbasketAccessItemResource addLinks(WorkbasketAccessItemResource resource, WorkbasketAccessItem wbAccItem) - throws NotAuthorizedException, WorkbasketNotFoundException { + throws NotAuthorizedException, WorkbasketNotFoundException { resource.add( - linkTo(methodOn(WorkbasketController.class).getWorkbasket(wbAccItem.getWorkbasketId())) - .withRel("workbasket")); + linkTo(methodOn(WorkbasketController.class).getWorkbasket(wbAccItem.getWorkbasketId())) + .withRel("workbasket")); return resource; } } diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/resource/assembler/WorkbasketAccessItemExtendedAssembler.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/resource/assembler/WorkbasketAccessItemExtendedAssembler.java new file mode 100644 index 000000000..51ddad7db --- /dev/null +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/resource/assembler/WorkbasketAccessItemExtendedAssembler.java @@ -0,0 +1,88 @@ +package pro.taskana.rest.resource.assembler; + +import org.springframework.beans.BeanUtils; +import org.springframework.hateoas.Link; +import org.springframework.hateoas.PagedResources; +import org.springframework.hateoas.mvc.ResourceAssemblerSupport; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; +import org.springframework.web.util.UriComponentsBuilder; +import pro.taskana.WorkbasketAccessItemExtended; +import pro.taskana.rest.WorkbasketAccessItemController; +import pro.taskana.rest.resource.WorkbasketAccesItemExtendedResource; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; +import java.util.Map; + +/** + * Transforms {@link WorkbasketAccessItemExtended} to its resource counterpart {@link WorkbasketAccesItemExtendedResource} and vice versa. + */ +@Component +public class WorkbasketAccessItemExtendedAssembler extends ResourceAssemblerSupport { + + /** + * Creates a new {@link ResourceAssemblerSupport} using the given controller class and resource type. + */ + public WorkbasketAccessItemExtendedAssembler() { + super(WorkbasketAccessItemController.class, WorkbasketAccesItemExtendedResource.class); + } + + @Override + public WorkbasketAccesItemExtendedResource toResource(WorkbasketAccessItemExtended workbasketAccessItemExtended) { + WorkbasketAccesItemExtendedResource resource = createResourceWithId(workbasketAccessItemExtended.getId(), workbasketAccessItemExtended); + resource.removeLinks(); + BeanUtils.copyProperties(workbasketAccessItemExtended, resource); + // named different so needs to be set by hand + resource.setAccessItemId(workbasketAccessItemExtended.getId()); + return resource; + } + + public PagedResources toResources(List workbasketAccessItems, + PagedResources.PageMetadata pageMetadata) { + + WorkbasketAccessItemExtendedAssembler assembler = new WorkbasketAccessItemExtendedAssembler(); + List resources = assembler.toResources(workbasketAccessItems); + + PagedResources pagedResources = new PagedResources( + resources, + pageMetadata); + + UriComponentsBuilder original = getBuilderForOriginalUri(); + pagedResources.add(new Link(original.toUriString()).withSelfRel()); + if (pageMetadata != null) { + pagedResources.add(new Link(original.replaceQueryParam("page", 1).toUriString()).withRel(Link.REL_FIRST)); + pagedResources.add(new Link(original.replaceQueryParam("page", pageMetadata.getTotalPages()).toUriString()) + .withRel(Link.REL_LAST)); + if (pageMetadata.getNumber() > 1) { + pagedResources + .add(new Link(original.replaceQueryParam("page", pageMetadata.getNumber() - 1).toUriString()) + .withRel(Link.REL_PREVIOUS)); + } + if (pageMetadata.getNumber() < pageMetadata.getTotalPages()) { + pagedResources + .add(new Link(original.replaceQueryParam("page", pageMetadata.getNumber() + 1).toUriString()) + .withRel(Link.REL_NEXT)); + } + } + + return pagedResources; + } + + private UriComponentsBuilder getBuilderForOriginalUri() { + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()) + .getRequest(); + UriComponentsBuilder baseUri = ServletUriComponentsBuilder.fromServletMapping(request) + .path(request.getRequestURI()); + for (Map.Entry entry : request.getParameterMap().entrySet()) { + for (String value : entry.getValue()) { + baseUri.queryParam(entry.getKey(), value); + } + } + UriComponentsBuilder original = baseUri; + return original; + } + +} diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/resource/assembler/WorkbasketAccessItemListAssembler.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/resource/assembler/WorkbasketAccessItemListAssembler.java index 8f3d59db4..2447c04cd 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/resource/assembler/WorkbasketAccessItemListAssembler.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/resource/assembler/WorkbasketAccessItemListAssembler.java @@ -1,22 +1,21 @@ package pro.taskana.rest.resource.assembler; -import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; -import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.hateoas.Resources; import org.springframework.stereotype.Component; - import pro.taskana.WorkbasketAccessItem; import pro.taskana.exceptions.NotAuthorizedException; import pro.taskana.exceptions.WorkbasketNotFoundException; import pro.taskana.rest.WorkbasketController; import pro.taskana.rest.resource.WorkbasketAccessItemResource; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; + /** * Mapper to convert from a list of WorkbasketAccessItem to a WorkbasketAccessItemResource. */ @@ -27,7 +26,7 @@ public class WorkbasketAccessItemListAssembler { private WorkbasketAccessItemAssembler workbasketAccessItemAssembler; public Resources toResource(String workbasketId, - Collection accessItems) throws NotAuthorizedException, WorkbasketNotFoundException { + Collection accessItems) throws NotAuthorizedException, WorkbasketNotFoundException { List resourceList = new ArrayList<>(); for (WorkbasketAccessItem accessItem : accessItems) { resourceList.add(workbasketAccessItemAssembler.toResource(accessItem)); @@ -36,11 +35,11 @@ public class WorkbasketAccessItemListAssembler { Resources accessItemListResource = new Resources<>(resourceList); accessItemListResource - .add(linkTo(methodOn(WorkbasketController.class).getWorkbasketAccessItems(workbasketId)) - .withSelfRel()); + .add(linkTo(methodOn(WorkbasketController.class).getWorkbasketAccessItems(workbasketId)) + .withSelfRel()); accessItemListResource - .add(linkTo(methodOn(WorkbasketController.class).getWorkbasket(workbasketId)) - .withRel("workbasket")); + .add(linkTo(methodOn(WorkbasketController.class).getWorkbasket(workbasketId)) + .withRel("workbasket")); return accessItemListResource; } diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/resource/assembler/WorkbasketAssembler.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/resource/assembler/WorkbasketAssembler.java index bf25cbaee..6fa93e38b 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/resource/assembler/WorkbasketAssembler.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/resource/assembler/WorkbasketAssembler.java @@ -1,14 +1,8 @@ package pro.taskana.rest.resource.assembler; -import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; -import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; - -import java.time.Instant; - import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; - import pro.taskana.Workbasket; import pro.taskana.WorkbasketService; import pro.taskana.exceptions.NotAuthorizedException; @@ -17,6 +11,11 @@ import pro.taskana.impl.WorkbasketImpl; import pro.taskana.rest.WorkbasketController; import pro.taskana.rest.resource.WorkbasketResource; +import java.time.Instant; + +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; + /** * Transforms {@link Workbasket} to its resource counterpart {@link WorkbasketResource} and vice versa. */ @@ -48,15 +47,15 @@ public class WorkbasketAssembler { } private WorkbasketResource addLinks(WorkbasketResource resource, Workbasket wb) - throws NotAuthorizedException, WorkbasketNotFoundException { + throws NotAuthorizedException, WorkbasketNotFoundException { resource.add(linkTo(methodOn(WorkbasketController.class).getWorkbasket(wb.getId())).withSelfRel()); resource.add(linkTo(methodOn(WorkbasketController.class).getDistributionTargets(wb.getId())) - .withRel("distributionTargets")); + .withRel("distributionTargets")); resource.add(linkTo(methodOn(WorkbasketController.class).getWorkbasketAccessItems(wb.getId())) - .withRel("accessItems")); + .withRel("accessItems")); resource.add(linkTo(WorkbasketController.class).withRel("allWorkbaskets")); resource.add(linkTo(methodOn(WorkbasketController.class).removeDistributionTargetForWorkbasketId(wb.getId())) - .withRel("removeDistributionTargets")); + .withRel("removeDistributionTargets")); return resource; } } diff --git a/rest/taskana-rest-spring/src/test/java/pro/taskana/rest/resource/assembler/WorkbasketAccessItemExtendedAssemblerTest.java b/rest/taskana-rest-spring/src/test/java/pro/taskana/rest/resource/assembler/WorkbasketAccessItemExtendedAssemblerTest.java new file mode 100644 index 000000000..ef34cd773 --- /dev/null +++ b/rest/taskana-rest-spring/src/test/java/pro/taskana/rest/resource/assembler/WorkbasketAccessItemExtendedAssemblerTest.java @@ -0,0 +1,84 @@ +package pro.taskana.rest.resource.assembler; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +<<<<<<< HEAD +import pro.taskana.WorkbasketAccessItem; +======= +>>>>>>> a89fa20c... TSK-694 - Show list of all access items valid for a user (REST) +import pro.taskana.WorkbasketAccessItemExtended; +import pro.taskana.WorkbasketService; +import pro.taskana.impl.WorkbasketAccessItemExtendedImpl; +import pro.taskana.rest.TestConfiguration; +import pro.taskana.rest.resource.WorkbasketAccesItemExtendedResource; + +/** + * Test for {@link WorkbasketAccessItemExtendedAssembler}. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = {TestConfiguration.class}) +@WebAppConfiguration +public class WorkbasketAccessItemExtendedAssemblerTest { + + @Autowired + WorkbasketAccessItemExtendedAssembler workbasketAccessItemExtendedAssembler; + + @Autowired + WorkbasketService workbasketService; + + @Test + public void workbasketAccessItemExtendedToResource() { + // given + WorkbasketAccessItemExtended workbasketAccessItemExtended = workbasketService.newWorkbasketAccessItemExtended("workbasketId", + "accessId"); + + ((WorkbasketAccessItemExtendedImpl) workbasketAccessItemExtended).setWorkbasketKey("workbasketKey"); + + ((WorkbasketAccessItemExtendedImpl) workbasketAccessItemExtended).setId("id"); + workbasketAccessItemExtended.setAccessName("Name"); + workbasketAccessItemExtended.setPermAppend(true); + workbasketAccessItemExtended.setPermCustom1(true); + workbasketAccessItemExtended.setPermCustom2(true); + workbasketAccessItemExtended.setPermCustom3(true); + workbasketAccessItemExtended.setPermCustom4(true); + workbasketAccessItemExtended.setPermCustom5(true); + workbasketAccessItemExtended.setPermCustom6(true); + workbasketAccessItemExtended.setPermCustom7(true); + workbasketAccessItemExtended.setPermCustom8(true); + workbasketAccessItemExtended.setPermCustom9(true); + workbasketAccessItemExtended.setPermCustom10(true); + workbasketAccessItemExtended.setPermCustom11(true); + workbasketAccessItemExtended.setPermCustom12(true); + workbasketAccessItemExtended.setPermDistribute(true); + workbasketAccessItemExtended.setPermOpen(true); + workbasketAccessItemExtended.setPermRead(true); + workbasketAccessItemExtended.setPermTransfer(true); + workbasketAccessItemExtended.setPermDistribute(true); + // when + WorkbasketAccesItemExtendedResource workbasketAccesItemExtendedResource = workbasketAccessItemExtendedAssembler.toResource(workbasketAccessItemExtended); + // then + Assert.assertEquals(workbasketAccessItemExtended.getWorkbasketId(), workbasketAccesItemExtendedResource.workbasketId); + Assert.assertEquals(workbasketAccessItemExtended.getWorkbasketKey(), workbasketAccesItemExtendedResource.workbasketKey); + Assert.assertEquals(workbasketAccessItemExtended.getId(), workbasketAccesItemExtendedResource.accessItemId); + Assert.assertEquals(workbasketAccessItemExtended.getAccessId(), workbasketAccesItemExtendedResource.accessId); + Assert.assertEquals(workbasketAccessItemExtended.getAccessName(), workbasketAccesItemExtendedResource.accessName); + Assert.assertEquals(workbasketAccessItemExtended.isPermAppend(), workbasketAccesItemExtendedResource.permAppend); + Assert.assertEquals(workbasketAccessItemExtended.isPermCustom1(), workbasketAccesItemExtendedResource.permCustom1); + Assert.assertEquals(workbasketAccessItemExtended.isPermCustom2(), workbasketAccesItemExtendedResource.permCustom2); + Assert.assertEquals(workbasketAccessItemExtended.isPermCustom3(), workbasketAccesItemExtendedResource.permCustom3); + Assert.assertEquals(workbasketAccessItemExtended.isPermCustom4(), workbasketAccesItemExtendedResource.permCustom4); + Assert.assertEquals(workbasketAccessItemExtended.isPermCustom5(), workbasketAccesItemExtendedResource.permCustom5); + Assert.assertEquals(workbasketAccessItemExtended.isPermCustom6(), workbasketAccesItemExtendedResource.permCustom6); + Assert.assertEquals(workbasketAccessItemExtended.isPermCustom7(), workbasketAccesItemExtendedResource.permCustom7); + Assert.assertEquals(workbasketAccessItemExtended.isPermCustom8(), workbasketAccesItemExtendedResource.permCustom8); + Assert.assertEquals(workbasketAccessItemExtended.isPermCustom9(), workbasketAccesItemExtendedResource.permCustom9); + Assert.assertEquals(workbasketAccessItemExtended.isPermCustom10(), workbasketAccesItemExtendedResource.permCustom10); + Assert.assertEquals(workbasketAccessItemExtended.isPermCustom11(), workbasketAccesItemExtendedResource.permCustom11); + Assert.assertEquals(workbasketAccessItemExtended.isPermCustom12(), workbasketAccesItemExtendedResource.permCustom12); + } +}