TSK-743: Removed HATEOAS from classification- and workbasketdefinition exports

This commit is contained in:
julian.schallenmueller 2018-11-29 15:06:57 +01:00 committed by Martin Rojas Miguel Angel
parent b3cbb82039
commit cdaf0c715a
12 changed files with 81 additions and 140 deletions

View File

@ -188,6 +188,11 @@
<version>2.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.7</version>
</dependency>
</dependencies>
<build>
<pluginManagement>

View File

@ -2,9 +2,14 @@ package pro.taskana;
import java.time.Instant;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import pro.taskana.impl.ClassificationImpl;
/**
* Interface used to specify the Classification-Model.
*/
@JsonDeserialize(as = ClassificationImpl.class)
public interface Classification {
/**

View File

@ -2,9 +2,14 @@ package pro.taskana;
import java.time.Instant;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import pro.taskana.impl.WorkbasketImpl;
/**
* Workbasket entity interface.
*/
@JsonDeserialize(as = WorkbasketImpl.class)
public interface Workbasket {
/**

View File

@ -364,15 +364,15 @@ A `GET` request is used to get all classification definitions.
==== Example Request
include::../../../{snippets}/GetAllClassificationdefinitionsDocTest/http-request.adoc[]
include::../../../{snippets}/ExportClassificationdefinitionsDocTest/http-request.adoc[]
==== Example Response
include::../../../{snippets}/GetAllClassificationdefinitionsDocTest/http-response.adoc[]
include::../../../{snippets}/ExportClassificationdefinitionsDocTest/http-response.adoc[]
==== Response Structure
include::../../../{snippets}/GetAllClassificationdefinitionsDocTest/response-fields.adoc[]
include::../../../{snippets}/ExportClassificationdefinitionsDocTest/response-fields.adoc[]
=== Import new classification-definitions
@ -548,16 +548,16 @@ A `GET` request is used to get all workbasket definitions.
==== Example Request
include::../../../{snippets}/GetAllWorkbasktdefinitionsDocTest/http-request.adoc[]
include::../../../{snippets}/ExportWorkbasktdefinitionsDocTest/http-request.adoc[]
==== Example Response
include::../../../{snippets}/GetAllWorkbasktdefinitionsDocTest/http-response.adoc[]
include::../../../{snippets}/ExportWorkbasktdefinitionsDocTest/http-response.adoc[]
[[workbasket-definitions]]
==== Response Structure
include::../../../{snippets}/GetAllWorkbasktdefinitionsDocTest/response-fields.adoc[]
include::../../../{snippets}/ExportWorkbasktdefinitionsDocTest/response-fields.adoc[]
=== Import new workbasket-definitions

View File

@ -66,21 +66,21 @@ public class ClassificationDefinitionControllerRestDocumentation {
}
@Test
public void getAllClassificationdefinitions() throws Exception {
public void exportAllClassificationdefinitions() throws Exception {
this.mockMvc.perform(RestDocumentationRequestBuilders
.get("http://127.0.0.1:" + port + "/v1/classificationdefinitions")
.accept("application/json")
.header("Authorization", "Basic dGVhbWxlYWRfMTp0ZWFtbGVhZF8x"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcRestDocumentation.document("GetAllClassificationdefinitionsDocTest",
.andDo(MockMvcRestDocumentation.document("ExportClassificationdefinitionsDocTest",
responseFields(classificationdefinitionsFieldDescriptors)));
}
@Test
public void importClassificationdefinition() throws Exception {
public void importClassificationdefinitions() throws Exception {
String definitionString = "[{\"key\":\"Key0815\", \"domain\":\"DOMAIN_B\"}]";
this.mockMvc.perform(RestDocumentationRequestBuilders
.post("http://127.0.0.1:" + port + "/v1/classificationdefinitions/import")
.post("http://127.0.0.1:" + port + "/v1/classificationdefinitions/")
.header("Authorization", "Basic dGVhbWxlYWRfMTp0ZWFtbGVhZF8x")
.contentType("application/json")
.content(definitionString))

View File

@ -11,7 +11,6 @@ import static org.springframework.restdocs.payload.PayloadDocumentation.subsecti
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -68,29 +67,27 @@ public class WorkbasketDefinitionControllerRestDocumentation {
}
@Test
public void getAllWorkbasketDefinitions() throws Exception {
public void exportAllWorkbasketDefinitions() throws Exception {
this.mockMvc.perform(RestDocumentationRequestBuilders
.get("http://127.0.0.1:" + port + "/v1/workbasketdefinitions")
.accept("application/json")
.header("Authorization", "Basic dGVhbWxlYWRfMTp0ZWFtbGVhZF8x"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcRestDocumentation.document("GetAllWorkbasktdefinitionsDocTest",
.andDo(MockMvcRestDocumentation.document("ExportWorkbasktdefinitionsDocTest",
responseFields(workbasketdefinitionsFieldDescriptors)));
}
@Test
public void importWorkbasketdefinition() throws Exception {
this.mockMvc.perform(RestDocumentationRequestBuilders
.post("http://127.0.0.1:" + port + "/v1/workbasketdefinitions/import")
.post("http://127.0.0.1:" + port + "/v1/workbasketdefinitions")
.header("Authorization", "Basic dGVhbWxlYWRfMTp0ZWFtbGVhZF8x")
.contentType("application/json")
.content("["
+ "{"
+ "\"distributionTargets\":[], "
+ "\"authorizations\":[], "
+ "\"workbasketResource\":"
+ "{\"name\":\"ich\", \"key\":\"neuerKeyXy\", \"domain\":\"DOMAIN_A\", \"type\":\"GROUP\","
+ "\"created\":\"2018-02-01T11:00:00Z\", \"modified\":\"2018-02-01T11:00:00Z\", \"workbasketId\":\"gibtsNed\"}"
+ "\"workbasket\": {\"name\":\"wbblabla\", \"key\":\"neuerKeyXy\", \"domain\": \"DOMAIN_A\", \"type\":\"GROUP\"}"
+ "}"
+ "]"))
.andExpect(MockMvcResultMatchers.status().isOk())

View File

@ -1,5 +1,9 @@
package pro.taskana.rest;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
@ -12,6 +16,7 @@ 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 pro.taskana.Classification;
import pro.taskana.ClassificationQuery;
import pro.taskana.ClassificationService;
@ -22,13 +27,6 @@ import pro.taskana.exceptions.ConcurrencyException;
import pro.taskana.exceptions.DomainNotFoundException;
import pro.taskana.exceptions.InvalidArgumentException;
import pro.taskana.exceptions.NotAuthorizedException;
import pro.taskana.rest.resource.ClassificationResource;
import pro.taskana.rest.resource.ClassificationResourceAssembler;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Controller for Importing / Exporting classifications.
@ -40,43 +38,30 @@ public class ClassificationDefinitionController {
@Autowired
private ClassificationService classificationService;
@Autowired
private ClassificationResourceAssembler classificationResourceAssembler;
@GetMapping
@Transactional(readOnly = true, rollbackFor = Exception.class)
public ResponseEntity<List<ClassificationResource>> getClassifications(
@RequestParam(required = false) String domain) throws ClassificationNotFoundException, NotAuthorizedException,
ClassificationAlreadyExistException, ConcurrencyException, DomainNotFoundException, InvalidArgumentException {
public ResponseEntity<List<ClassificationSummary>> exportClassifications(
@RequestParam(required = false) String domain) {
ClassificationQuery query = classificationService.createClassificationQuery();
List<ClassificationSummary> summaries = domain != null ? query.domainIn(domain).list() : query.list();
List<ClassificationResource> export = new ArrayList<>();
for (ClassificationSummary summary : summaries) {
Classification classification = classificationService.getClassification(summary.getKey(),
summary.getDomain());
export.add(classificationResourceAssembler.toResource(classification));
}
return new ResponseEntity<>(export, HttpStatus.OK);
return new ResponseEntity<>(summaries, HttpStatus.OK);
}
@PostMapping(path = "/import")
@PostMapping
@Transactional(rollbackFor = Exception.class)
public ResponseEntity<String> importClassifications(
@RequestBody List<ClassificationResource> classificationResources) throws InvalidArgumentException {
@RequestBody List<Classification> classifications) throws InvalidArgumentException {
Map<String, String> systemIds = classificationService.createClassificationQuery()
.list()
.stream()
.collect(Collectors.toMap(i -> i.getKey() + "|" + i.getDomain(), ClassificationSummary::getId));
try {
for (ClassificationResource classificationResource : classificationResources) {
if (systemIds.containsKey(classificationResource.key + "|" + classificationResource.domain)) {
classificationService.updateClassification(classificationResourceAssembler.toModel(classificationResource));
for (Classification classification : classifications) {
if (systemIds.containsKey(classification.getKey() + "|" + classification.getDomain())) {
classificationService.updateClassification(classification);
} else {
classificationResource.classificationId = null;
classificationService.createClassification(classificationResourceAssembler.toModel(classificationResource));
classificationService.createClassification(classification);
}
}
} catch (NotAuthorizedException e) {

View File

@ -1,5 +1,11 @@
package pro.taskana.rest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
@ -12,6 +18,7 @@ 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 pro.taskana.Workbasket;
import pro.taskana.WorkbasketAccessItem;
import pro.taskana.WorkbasketQuery;
@ -23,18 +30,7 @@ import pro.taskana.exceptions.InvalidWorkbasketException;
import pro.taskana.exceptions.NotAuthorizedException;
import pro.taskana.exceptions.WorkbasketAlreadyExistException;
import pro.taskana.exceptions.WorkbasketNotFoundException;
import pro.taskana.rest.resource.WorkbasketAccessItemResource;
import pro.taskana.rest.resource.WorkbasketDefinition;
import pro.taskana.rest.resource.WorkbasketResource;
import pro.taskana.rest.resource.WorkbasketAccessItemAssembler;
import pro.taskana.rest.resource.WorkbasketDefinitionAssembler;
import pro.taskana.rest.resource.WorkbasketResourceAssembler;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Controller for all {@link WorkbasketDefinition} related endpoints.
@ -46,36 +42,14 @@ public class WorkbasketDefinitionController {
@Autowired
private WorkbasketService workbasketService;
@Autowired
private WorkbasketDefinitionAssembler workbasketDefinitionAssembler;
@Autowired
private WorkbasketResourceAssembler workbasketResourceAssembler;
@Autowired
private WorkbasketAccessItemAssembler workbasketAccessItemAssembler;
@GetMapping
@Transactional(readOnly = true, rollbackFor = Exception.class)
public ResponseEntity<List<WorkbasketDefinition>> exportWorkbaskets(@RequestParam(required = false) String domain) {
try {
WorkbasketQuery workbasketQuery = workbasketService.createWorkbasketQuery();
List<WorkbasketSummary> workbasketSummaryList = domain != null
? workbasketQuery.domainIn(domain).list()
: workbasketQuery.list();
List<WorkbasketDefinition> basketExports = new ArrayList<>();
for (WorkbasketSummary summary : workbasketSummaryList) {
Workbasket workbasket = workbasketService.getWorkbasket(summary.getId());
basketExports.add(workbasketDefinitionAssembler.toResource(workbasket));
}
return new ResponseEntity<>(basketExports, HttpStatus.OK);
} catch (WorkbasketNotFoundException e) {
TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
} catch (NotAuthorizedException e) {
TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
}
public ResponseEntity<List<WorkbasketSummary>> exportWorkbaskets(@RequestParam(required = false) String domain) {
WorkbasketQuery workbasketQuery = workbasketService.createWorkbasketQuery();
List<WorkbasketSummary> workbasketSummaryList = domain != null
? workbasketQuery.domainIn(domain).list()
: workbasketQuery.list();
return new ResponseEntity<>(workbasketSummaryList, HttpStatus.OK);
}
/**
@ -87,7 +61,7 @@ public class WorkbasketDefinitionController {
* @return Return answer is determined by the status code: 200 - all good 400 - list state error (referring to non
* existing id's) 401 - not authorized
*/
@PostMapping(path = "/import")
@PostMapping
@Transactional(rollbackFor = Exception.class)
public ResponseEntity<String> importWorkbaskets(@RequestBody List<WorkbasketDefinition> definitions) {
try {
@ -104,30 +78,23 @@ public class WorkbasketDefinitionController {
// STEP 1: update or create workbaskets from the import
for (WorkbasketDefinition definition : definitions) {
WorkbasketResource res = definition.workbasketResource;
Workbasket importedWb = definition.workbasket;
Workbasket workbasket;
String oldId = res.workbasketId;
if (systemIds.containsKey(logicalId(res))) {
res.workbasketId = systemIds.get(logicalId(res));
workbasket = workbasketService.updateWorkbasket(
workbasketResourceAssembler.toModel(res));
if (systemIds.containsKey(logicalId(importedWb))) {
workbasket = workbasketService.updateWorkbasket(importedWb);
} else {
res.workbasketId = null;
workbasket = workbasketService.createWorkbasket(
workbasketResourceAssembler.toModel(res));
workbasket = workbasketService.createWorkbasket(importedWb);
}
res.workbasketId = oldId;
// Since we would have a n² runtime when doing a lookup and updating the access items we decided to
// simply delete all existing accessItems and create new ones.
for (WorkbasketAccessItem accessItem : workbasketService.getWorkbasketAccessItems(workbasket.getId())) {
workbasketService.deleteWorkbasketAccessItem(accessItem.getId());
}
for (WorkbasketAccessItemResource authorization : definition.authorizations) {
workbasketService.createWorkbasketAccessItem(
workbasketAccessItemAssembler.toModel(authorization));
for (WorkbasketAccessItem authorization : definition.authorizations) {
workbasketService.createWorkbasketAccessItem(authorization);
}
idConversion.put(definition.workbasketResource.workbasketId, workbasket.getId());
idConversion.put(definition.workbasket.getId(), workbasket.getId());
}
// STEP 2: update distribution targets
@ -147,7 +114,7 @@ public class WorkbasketDefinitionController {
workbasketService.setDistributionTargets(
// no verification necessary since the workbasket was already imported in step 1.
idConversion.get(definition.workbasketResource.workbasketId), distributionTargets);
idConversion.get(definition.workbasket.getId()), distributionTargets);
}
return new ResponseEntity<>(HttpStatus.OK);
} catch (WorkbasketNotFoundException e) {
@ -175,8 +142,8 @@ public class WorkbasketDefinitionController {
return logicalId(workbasket.getKey(), workbasket.getDomain());
}
private String logicalId(WorkbasketResource resource) {
return logicalId(resource.key, resource.domain);
private String logicalId(Workbasket workbasket) {
return logicalId(workbasket.getKey(), workbasket.getDomain());
}
private String logicalId(String key, String domain) {

View File

@ -3,26 +3,27 @@ package pro.taskana.rest.resource;
import java.util.List;
import java.util.Set;
import org.springframework.hateoas.ResourceSupport;
import pro.taskana.Workbasket;
import pro.taskana.WorkbasketAccessItem;
/**
* this class represents a workbasket including its distro targets and authorisations.
*/
public class WorkbasketDefinition extends ResourceSupport {
public class WorkbasketDefinition {
public Set<String> distributionTargets;
public List<WorkbasketAccessItemResource> authorizations;
public WorkbasketResource workbasketResource;
public List<WorkbasketAccessItem> authorizations;
public Workbasket workbasket;
public WorkbasketDefinition() {
// necessary for de-serializing
}
public WorkbasketDefinition(WorkbasketResource workbasketResource,
public WorkbasketDefinition(Workbasket workbasket,
Set<String> distributionTargets,
List<WorkbasketAccessItemResource> authorizations) {
List<WorkbasketAccessItem> authorizations) {
super();
this.workbasketResource = workbasketResource;
this.workbasket = workbasket;
this.distributionTargets = distributionTargets;
this.authorizations = authorizations;
}

View File

@ -1,10 +1,6 @@
package pro.taskana.rest.resource;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@ -18,7 +14,6 @@ import pro.taskana.WorkbasketService;
import pro.taskana.WorkbasketSummary;
import pro.taskana.exceptions.NotAuthorizedException;
import pro.taskana.exceptions.WorkbasketNotFoundException;
import pro.taskana.rest.WorkbasketDefinitionController;
/**
* Transforms {@link Workbasket} into a {@link WorkbasketDefinition}
@ -30,12 +25,6 @@ public class WorkbasketDefinitionAssembler {
@Autowired
private WorkbasketService workbasketService;
@Autowired
private WorkbasketResourceAssembler workbasketResourceAssembler;
@Autowired
private WorkbasketAccessItemAssembler workbasketAccessItemAssembler;
/**
* maps the distro targets to their id to remove overhead.
*
@ -48,29 +37,16 @@ public class WorkbasketDefinitionAssembler {
* @throws WorkbasketNotFoundException
* if {@code basket} is an unknown workbasket
*/
public WorkbasketDefinition toResource(Workbasket basket)
public WorkbasketDefinition toDefinition(Workbasket basket)
throws NotAuthorizedException, WorkbasketNotFoundException {
List<WorkbasketAccessItemResource> authorizations = new ArrayList<>();
List<WorkbasketAccessItem> authorizations = new ArrayList<>();
for (WorkbasketAccessItem accessItem : workbasketService.getWorkbasketAccessItems(basket.getKey())) {
authorizations.add(workbasketAccessItemAssembler.toResource(accessItem));
authorizations.add(accessItem);
}
Set<String> distroTargets = workbasketService.getDistributionTargets(basket.getId())
.stream()
.map(WorkbasketSummary::getId)
.collect(Collectors.toSet());
WorkbasketDefinition resource = new WorkbasketDefinition(workbasketResourceAssembler.toResource(basket), distroTargets,
authorizations);
return addLinks(resource, basket);
}
private WorkbasketDefinition addLinks(WorkbasketDefinition resource, Workbasket workbasket) {
resource.add(
linkTo(methodOn(WorkbasketDefinitionController.class).exportWorkbaskets(workbasket.getDomain()))
.withRel("exportWorkbaskets"));
resource.add(
linkTo(
methodOn(WorkbasketDefinitionController.class).importWorkbaskets(Collections.singletonList(resource)))
.withRel("importWorkbaskets"));
return resource;
return new WorkbasketDefinition(basket, distroTargets, authorizations);
}
}

View File

@ -30,7 +30,7 @@ export class ClassificationDefinitionService {
// POST
// TODO handle error
importClassifications(classifications: any) {
this.httpClient.post(this.url + '/import',
this.httpClient.post(this.url,
JSON.parse(classifications)).subscribe(
classificationsUpdated => this.alertService.triggerAlert(new AlertModel(AlertType.SUCCESS, 'Import was successful')),
error => this.alertService.triggerAlert(new AlertModel(AlertType.DANGER, 'Import was not successful'))

View File

@ -31,7 +31,7 @@ export class WorkbasketDefinitionService {
// POST
importWorkbasketDefinitions(workbasketDefinitions: any) {
this.httpClient.post(environment.taskanaRestUrl + '/v1/workbasketdefinitions/import',
this.httpClient.post(environment.taskanaRestUrl + '/v1/workbasketdefinitions',
JSON.parse(workbasketDefinitions)).subscribe(
workbasketsUpdated => this.alertService.triggerAlert(new AlertModel(AlertType.SUCCESS, 'Import was successful')),
error => this.errorModalService.triggerError(new ErrorModel(