From 0a904ac2201a6a170ee7a28fcfa38afa51e5bb1b Mon Sep 17 00:00:00 2001 From: Martin Rojas Miguel Angel Date: Wed, 14 Feb 2018 13:36:33 +0100 Subject: [PATCH] TSK-218 add to workbasketSummary GET endpoint filtering and sorting feature with hal+json support --- admin/src/app/model/workbasketSummary.ts | 2 +- .../app/services/workbasketservice.service.ts | 2 +- .../workbasket-information.component.ts | 4 +- .../list/workbasket-list.component.html | 4 +- .../list/workbasket-list.component.ts | 4 +- rest/pom.xml | 13 +- .../pro/taskana/rest/RestApplication.java | 6 + .../taskana/rest/WorkbasketController.java | 275 ++++++++++++------ .../rest/dto/WorkbasketSummaryDto.java | 107 +++++++ .../rest/mapper/WorkbasketSummaryMapper.java | 21 ++ 10 files changed, 342 insertions(+), 96 deletions(-) create mode 100644 rest/src/main/java/pro/taskana/rest/dto/WorkbasketSummaryDto.java create mode 100644 rest/src/main/java/pro/taskana/rest/mapper/WorkbasketSummaryMapper.java diff --git a/admin/src/app/model/workbasketSummary.ts b/admin/src/app/model/workbasketSummary.ts index 93260d9b5..8be649b9e 100644 --- a/admin/src/app/model/workbasketSummary.ts +++ b/admin/src/app/model/workbasketSummary.ts @@ -1,6 +1,6 @@ export class WorkbasketSummary { constructor( - public id: string, + public workbasketId: string, public key: string, public name: string, public description: string, diff --git a/admin/src/app/services/workbasketservice.service.ts b/admin/src/app/services/workbasketservice.service.ts index 50927fc47..6d432c0d9 100644 --- a/admin/src/app/services/workbasketservice.service.ts +++ b/admin/src/app/services/workbasketservice.service.ts @@ -35,7 +35,7 @@ export class WorkbasketService { } updateWorkbasket(workbasket: WorkbasketSummary): Observable { - return this.http.put(environment.taskanaRestUrl + "/v1/workbaskets/" + workbasket.id, workbasket, this.createAuthorizationHeader()) + return this.http.put(environment.taskanaRestUrl + "/v1/workbaskets/" + workbasket.workbasketId, workbasket, this.createAuthorizationHeader()) .map(res => res.json()); } diff --git a/admin/src/app/workbasket/details/information/workbasket-information.component.ts b/admin/src/app/workbasket/details/information/workbasket-information.component.ts index b67a3fca2..215169efe 100644 --- a/admin/src/app/workbasket/details/information/workbasket-information.component.ts +++ b/admin/src/app/workbasket/details/information/workbasket-information.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit, Input, Output } from '@angular/core'; -import { WorkbasketSummary } from '../../../model/workbasketSummary'; +import { Workbasket } from '../../../model/workbasket'; import { WorkbasketService } from '../../../services/workbasketservice.service'; @Component({ @@ -10,7 +10,7 @@ import { WorkbasketService } from '../../../services/workbasketservice.service'; export class WorkbasketInformationComponent implements OnInit { @Input() - workbasket: WorkbasketSummary; + workbasket: Workbasket; constructor(private service: WorkbasketService) { } diff --git a/admin/src/app/workbasket/list/workbasket-list.component.html b/admin/src/app/workbasket/list/workbasket-list.component.html index c00c8d060..63da9260e 100644 --- a/admin/src/app/workbasket/list/workbasket-list.component.html +++ b/admin/src/app/workbasket/list/workbasket-list.component.html @@ -107,10 +107,10 @@ -
  • +
  • -
    +
    {{workbasket.name}} ({{workbasket.key}})
    diff --git a/admin/src/app/workbasket/list/workbasket-list.component.ts b/admin/src/app/workbasket/list/workbasket-list.component.ts index a6c97721e..5e56db8c8 100644 --- a/admin/src/app/workbasket/list/workbasket-list.component.ts +++ b/admin/src/app/workbasket/list/workbasket-list.component.ts @@ -37,7 +37,7 @@ export class WorkbasketListComponent implements OnInit { } onDelete(workbasket: WorkbasketSummary) { - this.workbasketService.deleteWorkbasket(workbasket.id).subscribe(result => { + this.workbasketService.deleteWorkbasket(workbasket.workbasketId).subscribe(result => { var index = this.workbaskets.indexOf(workbasket); if (index > -1) { this.workbaskets.splice(index, 1); @@ -53,7 +53,7 @@ export class WorkbasketListComponent implements OnInit { } onClear() { - this.newWorkbasket.id = ""; + this.newWorkbasket.workbasketId = ""; this.newWorkbasket.name = ""; this.newWorkbasket.description = ""; this.newWorkbasket.owner = ""; diff --git a/rest/pom.xml b/rest/pom.xml index ae5dcc500..456f3456f 100644 --- a/rest/pom.xml +++ b/rest/pom.xml @@ -46,11 +46,22 @@ test + + org.modelmapper + modelmapper + 1.1.2 + + com.h2database h2 - + + org.springframework.hateoas + spring-hateoas + 0.16.0.RELEASE + + diff --git a/rest/src/main/java/pro/taskana/rest/RestApplication.java b/rest/src/main/java/pro/taskana/rest/RestApplication.java index de0458d70..9f277867b 100644 --- a/rest/src/main/java/pro/taskana/rest/RestApplication.java +++ b/rest/src/main/java/pro/taskana/rest/RestApplication.java @@ -4,6 +4,7 @@ import java.sql.SQLException; import javax.annotation.PostConstruct; +import org.modelmapper.ModelMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory; @@ -103,4 +104,9 @@ public class RestApplication { return new SpringHandlerInstantiator(context.getAutowireCapableBeanFactory()); } + @Bean + public ModelMapper modelMapper() { + return new ModelMapper(); + } + } diff --git a/rest/src/main/java/pro/taskana/rest/WorkbasketController.java b/rest/src/main/java/pro/taskana/rest/WorkbasketController.java index b3264b2a2..d7b367f63 100644 --- a/rest/src/main/java/pro/taskana/rest/WorkbasketController.java +++ b/rest/src/main/java/pro/taskana/rest/WorkbasketController.java @@ -1,109 +1,75 @@ package pro.taskana.rest; - -import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.hateoas.Link; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.util.MultiValueMap; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; -import pro.taskana.Workbasket; -import pro.taskana.WorkbasketAccessItem; -import pro.taskana.WorkbasketService; -import pro.taskana.WorkbasketSummary; -import pro.taskana.exceptions.InvalidArgumentException; -import pro.taskana.exceptions.InvalidWorkbasketException; -import pro.taskana.exceptions.NotAuthorizedException; -import pro.taskana.exceptions.WorkbasketNotFoundException; +import pro.taskana.*; +import pro.taskana.exceptions.*; import pro.taskana.model.WorkbasketAuthorization; +import pro.taskana.model.WorkbasketType; +import pro.taskana.rest.dto.WorkbasketSummaryDto; +import pro.taskana.rest.mapper.WorkbasketSummaryMapper; +import pro.taskana.security.CurrentUserContext; + +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; @RestController @RequestMapping(path = "/v1/workbaskets", produces = {MediaType.APPLICATION_JSON_VALUE}) public class WorkbasketController { + private static final String LIKE = "%"; + private static final String NAME = "name"; + private static final String KEY = "key"; + private static final String DESCRIPTION = "description"; + private static final String OWNER = "owner"; + private static final String TYPE = "type"; + private static final String DESC = "desc"; + @Autowired private WorkbasketService workbasketService; + @Autowired + private WorkbasketSummaryMapper workbasketSummaryMapper; - @GetMapping - public ResponseEntity> getWorkbaskets(@RequestParam MultiValueMap params) { - List workbaskets = new ArrayList<>(); - if (params.containsKey("requiredPermission")) { - List authorizations = new ArrayList<>(); - params.get("requiredPermission").stream().forEach(item -> { - for (String authorization : Arrays.asList(item.split(","))) { - switch (authorization.trim()) { - case "READ": - authorizations.add(WorkbasketAuthorization.READ); - break; - case "OPEN": - authorizations.add(WorkbasketAuthorization.OPEN); - break; - case "APPEND": - authorizations.add(WorkbasketAuthorization.APPEND); - break; - case "TRANSFER": - authorizations.add(WorkbasketAuthorization.TRANSFER); - break; - case "DISTRIBUTE": - authorizations.add(WorkbasketAuthorization.DISTRIBUTE); - break; - case "DELETE": - authorizations.add(WorkbasketAuthorization.DELETE); - break; - case "CUSTOM_1": - authorizations.add(WorkbasketAuthorization.CUSTOM_1); - break; - case "CUSTOM_2": - authorizations.add(WorkbasketAuthorization.CUSTOM_2); - break; - case "CUSTOM_3": - authorizations.add(WorkbasketAuthorization.CUSTOM_3); - break; - case "CUSTOM_4": - authorizations.add(WorkbasketAuthorization.CUSTOM_4); - break; - case "CUSTOM_5": - authorizations.add(WorkbasketAuthorization.CUSTOM_5); - break; - case "CUSTOM_6": - authorizations.add(WorkbasketAuthorization.CUSTOM_6); - break; - case "CUSTOM_7": - authorizations.add(WorkbasketAuthorization.CUSTOM_7); - break; - case "CUSTOM_8": - authorizations.add(WorkbasketAuthorization.CUSTOM_8); - break; - case "CUSTOM_9": - authorizations.add(WorkbasketAuthorization.CUSTOM_9); - break; - case "CUSTOM_10": - authorizations.add(WorkbasketAuthorization.CUSTOM_10); - break; - case "CUSTOM_11": - authorizations.add(WorkbasketAuthorization.CUSTOM_11); - break; - case "CUSTOM_12": - authorizations.add(WorkbasketAuthorization.CUSTOM_12); - break; - } - } - }); - workbaskets = workbasketService.getWorkbaskets(authorizations); - } else { - workbaskets = workbasketService.getWorkbaskets(); + + @RequestMapping(method = RequestMethod.GET) + public ResponseEntity> GetWorkbaskets(@RequestParam(value = "sortBy", defaultValue = "name", required = false) String sortBy , + @RequestParam(value = "order", defaultValue = "asc", required = false) String order , + @RequestParam(value = "name", defaultValue = "", required = false) String name , + @RequestParam(value = "nameLike", defaultValue = "", required = false) String nameLike , + @RequestParam(value = "descLike", defaultValue = "", required = false) String descLike , + @RequestParam(value = "owner", defaultValue = "", required = false) String owner , + @RequestParam(value = "ownerLike", defaultValue = "", required = false) String ownerLike , + @RequestParam(value = "type", defaultValue = "", required = false) String type , + @RequestParam(value = "requiredPermission", defaultValue = "", required = false) String requiredPermission) { + + List workbasketsSummary; + WorkbasketQuery query = workbasketService.createWorkbasketQuery(); + + try{ + + AddSortByQuery(query, sortBy, order); + AddFilterQuery(query, name, nameLike, descLike, owner, ownerLike, type); + AddAuthorization(query, requiredPermission); + workbasketsSummary = query.list(); + + } catch (InvalidArgumentException e) { + return new ResponseEntity<>(HttpStatus.CONFLICT); + } catch (InvalidRequestException e) { + return new ResponseEntity<>(HttpStatus.CONFLICT); + } catch (NotAuthorizedException e) { + return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); } - return new ResponseEntity<>(workbaskets, HttpStatus.OK); + + + return new ResponseEntity<>(workbasketsSummary.stream().map(workbasket -> workbasketSummaryMapper.convertToDto(workbasket)) + .map(WorkbasketController::WorkbasketSummaryLink).collect(Collectors.toList()), HttpStatus.OK); } @RequestMapping(value = "/{workbasketid}") @@ -166,4 +132,139 @@ public class WorkbasketController { workbasketService.deleteWorkbasketAuthorization(authId); return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); } + + + private void AddAuthorization(WorkbasketQuery query, String requiredPermission) throws InvalidArgumentException { + if (requiredPermission.isEmpty()) {return;} + + String[] accessIds = GetCurrentUserAccessIds(); + for (String authorization : Arrays.asList(requiredPermission.split(","))) { + try { + switch (authorization.trim()) { + case "READ": + query.accessIdsHavePermission(WorkbasketAuthorization.READ, accessIds); + break; + case "OPEN": + query.accessIdsHavePermission(WorkbasketAuthorization.OPEN, accessIds); + break; + case "APPEND": + query.accessIdsHavePermission(WorkbasketAuthorization.APPEND, accessIds); + break; + case "TRANSFER": + query.accessIdsHavePermission(WorkbasketAuthorization.TRANSFER, accessIds); + break; + case "DISTRIBUTE": + query.accessIdsHavePermission(WorkbasketAuthorization.DISTRIBUTE, accessIds); + break; + case "DELETE": + query.accessIdsHavePermission(WorkbasketAuthorization.DELETE, accessIds); + break; + case "CUSTOM_1": + query.accessIdsHavePermission(WorkbasketAuthorization.CUSTOM_1, accessIds); + break; + case "CUSTOM_2": + query.accessIdsHavePermission(WorkbasketAuthorization.CUSTOM_2, accessIds); + break; + case "CUSTOM_3": + query.accessIdsHavePermission(WorkbasketAuthorization.CUSTOM_3, accessIds); + break; + case "CUSTOM_4": + query.accessIdsHavePermission(WorkbasketAuthorization.CUSTOM_4, accessIds); + break; + case "CUSTOM_5": + query.accessIdsHavePermission(WorkbasketAuthorization.CUSTOM_5, accessIds); + break; + case "CUSTOM_6": + query.accessIdsHavePermission(WorkbasketAuthorization.CUSTOM_6, accessIds); + break; + case "CUSTOM_7": + query.accessIdsHavePermission(WorkbasketAuthorization.CUSTOM_7, accessIds); + break; + case "CUSTOM_8": + query.accessIdsHavePermission(WorkbasketAuthorization.CUSTOM_8, accessIds); + break; + case "CUSTOM_9": + query.accessIdsHavePermission(WorkbasketAuthorization.CUSTOM_9, accessIds); + break; + case "CUSTOM_10": + query.accessIdsHavePermission(WorkbasketAuthorization.CUSTOM_10, accessIds); + break; + case "CUSTOM_11": + query.accessIdsHavePermission(WorkbasketAuthorization.CUSTOM_11, accessIds); + break; + case "CUSTOM_12": + query.accessIdsHavePermission(WorkbasketAuthorization.CUSTOM_12, accessIds); + break; + } + } catch (InvalidArgumentException e) { + e.printStackTrace(); + } + } + } + + + private void AddSortByQuery(WorkbasketQuery query, String sortBy, String order) throws InvalidRequestException, InvalidArgumentException { + BaseQuery.SortDirection sortDirection = GetSortDirecction(order); + + if (sortBy.equals(NAME)) { + query.orderByKey(sortDirection); + } else if (sortBy.equals(KEY)){ + query.orderByKey(sortDirection); + } else if (sortBy.equals(DESCRIPTION)){ + query.orderByDescription(sortDirection); + } else if (sortBy.equals(OWNER)){ + query.orderByOwner(sortDirection); + } else if (sortBy.equals(TYPE)){ + query.orderByType(sortDirection); + } + } + + private BaseQuery.SortDirection GetSortDirecction(String order) throws InvalidRequestException { + if (order.equals(DESC)){ + return BaseQuery.SortDirection.DESCENDING; + } + return BaseQuery.SortDirection.ASCENDING; + } + + + private void AddFilterQuery(WorkbasketQuery query, + String name, String nameLike, + String descLike, String owner, + String ownerLike, String type) throws NotAuthorizedException, InvalidArgumentException { + if(!name.isEmpty())query.nameIn(name); + if(!nameLike.isEmpty())query.nameLike(LIKE + nameLike + LIKE); + if(!owner.isEmpty())query.ownerIn(owner); + if(!ownerLike.isEmpty())query.ownerLike(LIKE + ownerLike + LIKE); + if(!descLike.isEmpty())query.descriptionLike(LIKE + descLike + LIKE); + switch (type) { + case "PERSONAL": + query.typeIn(WorkbasketType.PERSONAL); + case "GROUP": + query.typeIn(WorkbasketType.GROUP); + case "CLEARANCE": + query.typeIn(WorkbasketType.CLEARANCE); + case "TOPIC": + query.typeIn(WorkbasketType.TOPIC); + } + } + + private String[] GetCurrentUserAccessIds() throws InvalidArgumentException{ + String[] accessIds; + List ucAccessIds = CurrentUserContext.getAccessIds(); + if (ucAccessIds != null && !ucAccessIds.isEmpty()) { + accessIds = new String[ucAccessIds.size()]; + accessIds = ucAccessIds.toArray(accessIds); + } else { + throw new InvalidArgumentException("CurrentUserContext need to have at least one accessId."); + } + return accessIds; + } + + private static WorkbasketSummaryDto WorkbasketSummaryLink(WorkbasketSummaryDto workbasketSummaryDto){ + + Link selfLink = linkTo(WorkbasketController.class).slash(workbasketSummaryDto.getWorkbasketId()).withSelfRel(); + workbasketSummaryDto.add(selfLink); + return workbasketSummaryDto; + } + } diff --git a/rest/src/main/java/pro/taskana/rest/dto/WorkbasketSummaryDto.java b/rest/src/main/java/pro/taskana/rest/dto/WorkbasketSummaryDto.java new file mode 100644 index 000000000..5e6396d3a --- /dev/null +++ b/rest/src/main/java/pro/taskana/rest/dto/WorkbasketSummaryDto.java @@ -0,0 +1,107 @@ +package pro.taskana.rest.dto; + +import org.springframework.hateoas.ResourceSupport; +import pro.taskana.model.WorkbasketType; + +public class WorkbasketSummaryDto extends ResourceSupport{ + + private String workbasketId; + private String key; + private String name; + private String description; + private String owner; + private String domain; + private WorkbasketType type; + private String orgLevel1; + private String orgLevel2; + private String orgLevel3; + private String orgLevel4; + + public String getWorkbasketId() { + return workbasketId; + } + + public void setWorkbasketId(String workbasketId) { + this.workbasketId = workbasketId; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.owner = owner; + } + + public String getDomain() { + return domain; + } + + public void setDomain(String domain) { + this.domain = domain; + } + + public WorkbasketType getType() { + return type; + } + + public void setType(WorkbasketType type) { + this.type = type; + } + + public String getOrgLevel1() { + return orgLevel1; + } + + public void setOrgLevel1(String orgLevel1) { + this.orgLevel1 = orgLevel1; + } + + public String getOrgLevel2() { + return orgLevel2; + } + + public void setOrgLevel2(String orgLevel2) { + this.orgLevel2 = orgLevel2; + } + + public String getOrgLevel3() { + return orgLevel3; + } + + public void setOrgLevel3(String orgLevel3) { + this.orgLevel3 = orgLevel3; + } + + public String getOrgLevel4() { + return orgLevel4; + } + + public void setOrgLevel4(String orgLevel4) { + this.orgLevel4 = orgLevel4; + } +} diff --git a/rest/src/main/java/pro/taskana/rest/mapper/WorkbasketSummaryMapper.java b/rest/src/main/java/pro/taskana/rest/mapper/WorkbasketSummaryMapper.java new file mode 100644 index 000000000..c57a8ad6c --- /dev/null +++ b/rest/src/main/java/pro/taskana/rest/mapper/WorkbasketSummaryMapper.java @@ -0,0 +1,21 @@ +package pro.taskana.rest.mapper; + +import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.hateoas.ResourceSupport; +import org.springframework.stereotype.Component; +import pro.taskana.WorkbasketSummary; +import pro.taskana.rest.dto.WorkbasketSummaryDto; + +@Component +public class WorkbasketSummaryMapper extends ResourceSupport { + + @Autowired + private ModelMapper modelMapper; + + public WorkbasketSummaryDto convertToDto(WorkbasketSummary workbasketSummary){ + WorkbasketSummaryDto dto = modelMapper.map(workbasketSummary, WorkbasketSummaryDto.class); + dto.setWorkbasketId(workbasketSummary.getId()); + return dto; + } +}