TSK-1741: WorkbasketPriorityReport now accepts column headers as request params
This commit is contained in:
parent
ea864474ad
commit
4ae94ff108
|
@ -1,7 +1,6 @@
|
|||
package pro.taskana.common.test.rest;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
@ -16,7 +15,6 @@ import org.springframework.test.context.ActiveProfiles;
|
|||
// DirtiesContext is required to make the integration tests run with embedded LDAP.
|
||||
// Otherwise the LDAP server is not shut down correctly and will not come up again. (socket busy)
|
||||
@DirtiesContext(classMode = ClassMode.AFTER_CLASS)
|
||||
@Inherited
|
||||
@ActiveProfiles({"test"})
|
||||
@SpringBootTest(
|
||||
classes = TestConfiguration.class,
|
||||
|
|
|
@ -27,4 +27,12 @@ public class PriorityColumnHeader implements ColumnHeader<PriorityQueryItem> {
|
|||
public boolean fits(PriorityQueryItem item) {
|
||||
return lowerBoundInc <= item.getPriority() && upperBoundInc >= item.getPriority();
|
||||
}
|
||||
|
||||
public int getLowerBoundInc() {
|
||||
return lowerBoundInc;
|
||||
}
|
||||
|
||||
public int getUpperBoundInc() {
|
||||
return upperBoundInc;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
package pro.taskana.common.rest;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import java.beans.PropertyEditorSupport;
|
||||
import java.net.URLDecoder;
|
||||
|
||||
public class JsonPropertyEditor extends PropertyEditorSupport {
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
private final Class<?> requiredType;
|
||||
|
||||
public JsonPropertyEditor(ObjectMapper objectMapper, Class<?> requiredType) {
|
||||
this.objectMapper = objectMapper;
|
||||
this.requiredType = requiredType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAsText(String text) throws IllegalArgumentException {
|
||||
if (text != null && !text.isEmpty()) {
|
||||
try {
|
||||
setValue(objectMapper.readValue(URLDecoder.decode(text, "UTF-8"), requiredType));
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package pro.taskana.common.rest;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
|
||||
import pro.taskana.monitor.rest.models.PriorityColumnHeaderRepresentationModel;
|
||||
|
||||
@ControllerAdvice
|
||||
public class JsonPropertyEditorRegistrator {
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
@Autowired
|
||||
public JsonPropertyEditorRegistrator(ObjectMapper objectMapper) {
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
@InitBinder
|
||||
public void initBinder(WebDataBinder binder) {
|
||||
binder.registerCustomEditor(
|
||||
PriorityColumnHeaderRepresentationModel.class,
|
||||
new JsonPropertyEditor(objectMapper, PriorityColumnHeaderRepresentationModel.class));
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ package pro.taskana.monitor.rest;
|
|||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.hateoas.config.EnableHypermediaSupport;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
@ -24,7 +25,9 @@ import pro.taskana.monitor.api.reports.TimestampReport;
|
|||
import pro.taskana.monitor.api.reports.WorkbasketPriorityReport;
|
||||
import pro.taskana.monitor.api.reports.WorkbasketReport;
|
||||
import pro.taskana.monitor.api.reports.header.PriorityColumnHeader;
|
||||
import pro.taskana.monitor.rest.assembler.PriorityColumnHeaderRepresentationModelAssembler;
|
||||
import pro.taskana.monitor.rest.assembler.ReportRepresentationModelAssembler;
|
||||
import pro.taskana.monitor.rest.models.PriorityColumnHeaderRepresentationModel;
|
||||
import pro.taskana.monitor.rest.models.ReportRepresentationModel;
|
||||
import pro.taskana.task.api.TaskCustomField;
|
||||
import pro.taskana.task.api.TaskState;
|
||||
|
@ -38,13 +41,19 @@ public class MonitorController {
|
|||
private final MonitorService monitorService;
|
||||
|
||||
private final ReportRepresentationModelAssembler reportRepresentationModelAssembler;
|
||||
private final PriorityColumnHeaderRepresentationModelAssembler
|
||||
priorityColumnHeaderRepresentationModelAssembler;
|
||||
|
||||
@Autowired
|
||||
MonitorController(
|
||||
MonitorService monitorService,
|
||||
ReportRepresentationModelAssembler reportRepresentationModelAssembler) {
|
||||
ReportRepresentationModelAssembler reportRepresentationModelAssembler,
|
||||
PriorityColumnHeaderRepresentationModelAssembler
|
||||
priorityColumnHeaderRepresentationModelAssembler) {
|
||||
this.monitorService = monitorService;
|
||||
this.reportRepresentationModelAssembler = reportRepresentationModelAssembler;
|
||||
this.priorityColumnHeaderRepresentationModelAssembler =
|
||||
priorityColumnHeaderRepresentationModelAssembler;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -82,23 +91,15 @@ public class MonitorController {
|
|||
}
|
||||
|
||||
/**
|
||||
* This endpoint generates a Workbasket Report by priority ranges.
|
||||
* This endpoint generates a Workbasket Priority Report.
|
||||
*
|
||||
* <p>Each Row represents a Workbasket.
|
||||
*
|
||||
* <p>Each Column Header represents a priority range. <br>
|
||||
* <br>
|
||||
*
|
||||
* <p><b>Default ranges</b>
|
||||
*
|
||||
* <p>High: priority > 500
|
||||
*
|
||||
* <p>Medium: 250 ≥ priority ≤ 500
|
||||
*
|
||||
* <p>Low: priority < 250
|
||||
* <p>Each Column Header represents a priority range.
|
||||
*
|
||||
* @title Compute a Workbasket Priority Report
|
||||
* @param workbasketTypes determine the WorkbasketTypes to include in the report
|
||||
* @param columnHeaders the column headers for the report
|
||||
* @return the computed Report
|
||||
* @throws NotAuthorizedException if the current user is not authorized to compute the Report
|
||||
* @throws InvalidArgumentException if topicWorkbaskets or useDefaultValues are false
|
||||
|
@ -106,21 +107,25 @@ public class MonitorController {
|
|||
@GetMapping(path = RestEndpoints.URL_MONITOR_WORKBASKET_PRIORITY_REPORT)
|
||||
@Transactional(readOnly = true, rollbackFor = Exception.class)
|
||||
public ResponseEntity<ReportRepresentationModel> computePriorityWorkbasketReport(
|
||||
@RequestParam(name = "workbasket-type", required = false) WorkbasketType[] workbasketTypes)
|
||||
@RequestParam(name = "workbasket-type", required = false) WorkbasketType[] workbasketTypes,
|
||||
@RequestParam(name = "columnHeader", required = false)
|
||||
PriorityColumnHeaderRepresentationModel[] columnHeaders)
|
||||
throws NotAuthorizedException, InvalidArgumentException {
|
||||
|
||||
WorkbasketPriorityReport.Builder builder =
|
||||
monitorService
|
||||
.createWorkbasketPriorityReportBuilder()
|
||||
.withColumnHeaders(
|
||||
Arrays.asList(
|
||||
new PriorityColumnHeader(Integer.MIN_VALUE, 249),
|
||||
new PriorityColumnHeader(250, 500),
|
||||
new PriorityColumnHeader(501, Integer.MAX_VALUE)))
|
||||
.workbasketTypeIn(workbasketTypes);
|
||||
monitorService.createWorkbasketPriorityReportBuilder().workbasketTypeIn(workbasketTypes);
|
||||
|
||||
if (columnHeaders != null) {
|
||||
List<PriorityColumnHeader> priorityColumnHeaders =
|
||||
Arrays.stream(columnHeaders)
|
||||
.map(priorityColumnHeaderRepresentationModelAssembler::toEntityModel)
|
||||
.collect(Collectors.toList());
|
||||
builder.withColumnHeaders(priorityColumnHeaders);
|
||||
}
|
||||
|
||||
ReportRepresentationModel report =
|
||||
reportRepresentationModelAssembler.toModel(builder.buildReport(), workbasketTypes);
|
||||
reportRepresentationModelAssembler.toModel(
|
||||
builder.buildReport(), workbasketTypes, columnHeaders);
|
||||
|
||||
return ResponseEntity.status(HttpStatus.OK).body(report);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package pro.taskana.monitor.rest.assembler;
|
||||
|
||||
import org.springframework.hateoas.server.RepresentationModelAssembler;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import pro.taskana.monitor.api.reports.header.PriorityColumnHeader;
|
||||
import pro.taskana.monitor.rest.models.PriorityColumnHeaderRepresentationModel;
|
||||
|
||||
@Component
|
||||
public class PriorityColumnHeaderRepresentationModelAssembler
|
||||
implements RepresentationModelAssembler<
|
||||
PriorityColumnHeader, PriorityColumnHeaderRepresentationModel> {
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public PriorityColumnHeaderRepresentationModel toModel(@NonNull PriorityColumnHeader entity) {
|
||||
return new PriorityColumnHeaderRepresentationModel(
|
||||
entity.getLowerBoundInc(), entity.getUpperBoundInc());
|
||||
}
|
||||
|
||||
public PriorityColumnHeader toEntityModel(PriorityColumnHeaderRepresentationModel repModel) {
|
||||
return new PriorityColumnHeader(repModel.getLowerBound(), repModel.getUpperBound());
|
||||
}
|
||||
}
|
|
@ -32,6 +32,7 @@ import pro.taskana.monitor.api.reports.row.Row;
|
|||
import pro.taskana.monitor.api.reports.row.SingleRow;
|
||||
import pro.taskana.monitor.rest.MonitorController;
|
||||
import pro.taskana.monitor.rest.TimeIntervalReportFilterParameter;
|
||||
import pro.taskana.monitor.rest.models.PriorityColumnHeaderRepresentationModel;
|
||||
import pro.taskana.monitor.rest.models.ReportRepresentationModel;
|
||||
import pro.taskana.monitor.rest.models.ReportRepresentationModel.RowRepresentationModel;
|
||||
import pro.taskana.task.api.TaskCustomField;
|
||||
|
@ -59,11 +60,15 @@ public class ReportRepresentationModelAssembler {
|
|||
|
||||
@NonNull
|
||||
public ReportRepresentationModel toModel(
|
||||
@NonNull WorkbasketPriorityReport report, @NonNull WorkbasketType[] workbasketTypes)
|
||||
throws NotAuthorizedException, InvalidArgumentException {
|
||||
@NonNull WorkbasketPriorityReport report,
|
||||
WorkbasketType[] workbasketTypes,
|
||||
PriorityColumnHeaderRepresentationModel[] columnHeaders)
|
||||
throws InvalidArgumentException, NotAuthorizedException {
|
||||
ReportRepresentationModel resource = toReportResource(report);
|
||||
resource.add(
|
||||
linkTo(methodOn(MonitorController.class).computePriorityWorkbasketReport(workbasketTypes))
|
||||
linkTo(
|
||||
methodOn(MonitorController.class)
|
||||
.computePriorityWorkbasketReport(workbasketTypes, columnHeaders))
|
||||
.withSelfRel());
|
||||
return resource;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package pro.taskana.monitor.rest.models;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import java.beans.ConstructorProperties;
|
||||
import org.springframework.hateoas.RepresentationModel;
|
||||
|
||||
@JsonIgnoreProperties("links")
|
||||
public class PriorityColumnHeaderRepresentationModel
|
||||
extends RepresentationModel<PriorityColumnHeaderRepresentationModel> {
|
||||
|
||||
/** Determine the lower priority for this column header. This value is inclusive. */
|
||||
private final int lowerBound;
|
||||
|
||||
/** Determine the upper priority for this column header. This value is inclusive. */
|
||||
private final int upperBound;
|
||||
|
||||
@ConstructorProperties({"lowerBound", "upperBound"})
|
||||
public PriorityColumnHeaderRepresentationModel(int lowerBound, int upperBound) {
|
||||
this.lowerBound = lowerBound;
|
||||
this.upperBound = upperBound;
|
||||
}
|
||||
|
||||
public int getLowerBound() {
|
||||
return lowerBound;
|
||||
}
|
||||
|
||||
public int getUpperBound() {
|
||||
return upperBound;
|
||||
}
|
||||
}
|
|
@ -1,8 +1,13 @@
|
|||
package pro.taskana.monitor.rest;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static pro.taskana.common.test.rest.RestHelper.TEMPLATE;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
|
@ -10,11 +15,12 @@ import org.springframework.hateoas.IanaLinkRelations;
|
|||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.client.HttpClientErrorException.BadRequest;
|
||||
|
||||
import pro.taskana.common.api.TaskanaEngine;
|
||||
import pro.taskana.common.rest.RestEndpoints;
|
||||
import pro.taskana.common.test.rest.RestHelper;
|
||||
import pro.taskana.common.test.rest.TaskanaSpringBootTest;
|
||||
import pro.taskana.monitor.rest.models.PriorityColumnHeaderRepresentationModel;
|
||||
import pro.taskana.monitor.rest.models.ReportRepresentationModel;
|
||||
import pro.taskana.monitor.rest.models.ReportRepresentationModel.RowRepresentationModel;
|
||||
|
||||
|
@ -23,12 +29,12 @@ import pro.taskana.monitor.rest.models.ReportRepresentationModel.RowRepresentati
|
|||
class MonitorControllerIntTest {
|
||||
|
||||
private final RestHelper restHelper;
|
||||
private final TaskanaEngine taskanaEngine;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
@Autowired
|
||||
MonitorControllerIntTest(RestHelper restHelper, TaskanaEngine taskanaEngine) {
|
||||
MonitorControllerIntTest(RestHelper restHelper, ObjectMapper objectMapper) {
|
||||
this.restHelper = restHelper;
|
||||
this.taskanaEngine = taskanaEngine;
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -106,10 +112,10 @@ class MonitorControllerIntTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
void should_ComputeReport_When_QueryingForAWorkbasketPriorityReport() {
|
||||
void should_ComputeWorkbasketPriorityReport_When_QueryingForAWorkbasketPriorityReport() {
|
||||
String url =
|
||||
restHelper.toUrl(RestEndpoints.URL_MONITOR_WORKBASKET_PRIORITY_REPORT)
|
||||
+ "?workbasket-type=TOPIC,GROUP";
|
||||
+ "?workbasket-type=TOPIC&workbasket-type=GROUP";
|
||||
HttpEntity<?> auth = new HttpEntity<>(RestHelper.generateHeadersForUser("monitor"));
|
||||
|
||||
ResponseEntity<ReportRepresentationModel> response =
|
||||
|
@ -123,8 +129,50 @@ class MonitorControllerIntTest {
|
|||
|
||||
assertThat(report).isNotNull();
|
||||
|
||||
assertThat(report.getSumRow())
|
||||
.extracting(RowRepresentationModel::getCells)
|
||||
.containsExactly(new int[] {25, 1, 0});
|
||||
assertThat(report.getSumRow()).extracting(RowRepresentationModel::getTotal).containsExactly(26);
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_DetectPriorityColumnHeader_When_HeaderIsPassedAsQueryParameter() throws Exception {
|
||||
PriorityColumnHeaderRepresentationModel columnHeader =
|
||||
new PriorityColumnHeaderRepresentationModel(10, 20);
|
||||
|
||||
String url =
|
||||
restHelper.toUrl(RestEndpoints.URL_MONITOR_WORKBASKET_PRIORITY_REPORT)
|
||||
+ "?columnHeader="
|
||||
+ URLEncoder.encode(
|
||||
objectMapper.writeValueAsString(columnHeader), StandardCharsets.UTF_8);
|
||||
|
||||
HttpEntity<?> auth = new HttpEntity<>(RestHelper.generateHeadersForUser("monitor"));
|
||||
|
||||
ResponseEntity<ReportRepresentationModel> response =
|
||||
TEMPLATE.exchange(
|
||||
url,
|
||||
HttpMethod.GET,
|
||||
auth,
|
||||
ParameterizedTypeReference.forType(ReportRepresentationModel.class));
|
||||
|
||||
ReportRepresentationModel report = response.getBody();
|
||||
assertThat(report).isNotNull();
|
||||
assertThat(report.getMeta().getHeader()).containsExactly("10 - 20");
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_ReturnBadRequest_When_PriorityColumnHeaderIsNotAValidJson() {
|
||||
String url =
|
||||
restHelper.toUrl(RestEndpoints.URL_MONITOR_WORKBASKET_PRIORITY_REPORT)
|
||||
+ "?columnHeader=invalidJson";
|
||||
|
||||
HttpEntity<?> auth = new HttpEntity<>(RestHelper.generateHeadersForUser("monitor"));
|
||||
|
||||
ThrowingCallable httpCall =
|
||||
() ->
|
||||
TEMPLATE.exchange(
|
||||
url,
|
||||
HttpMethod.GET,
|
||||
auth,
|
||||
ParameterizedTypeReference.forType(ReportRepresentationModel.class));
|
||||
|
||||
assertThatThrownBy(httpCall).isInstanceOf(BadRequest.class);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
package pro.taskana.monitor.rest.assembler;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import pro.taskana.common.test.rest.TaskanaSpringBootTest;
|
||||
import pro.taskana.monitor.api.reports.header.PriorityColumnHeader;
|
||||
import pro.taskana.monitor.rest.models.PriorityColumnHeaderRepresentationModel;
|
||||
|
||||
@TaskanaSpringBootTest
|
||||
class PriorityColumnHeaderRepresentationModelAssemblerTest {
|
||||
|
||||
private final PriorityColumnHeaderRepresentationModelAssembler assembler;
|
||||
|
||||
@Autowired
|
||||
public PriorityColumnHeaderRepresentationModelAssemblerTest(
|
||||
PriorityColumnHeaderRepresentationModelAssembler assembler) {
|
||||
this.assembler = assembler;
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_convertEntityToRepresentationModel() {
|
||||
PriorityColumnHeader columnHeader = new PriorityColumnHeader(10, 20);
|
||||
PriorityColumnHeaderRepresentationModel expectedRepModel =
|
||||
new PriorityColumnHeaderRepresentationModel(10, 20);
|
||||
|
||||
PriorityColumnHeaderRepresentationModel repModel = assembler.toModel(columnHeader);
|
||||
|
||||
assertThat(repModel).usingRecursiveComparison().isEqualTo(expectedRepModel);
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_convertRepresentationModelToEntity() {
|
||||
PriorityColumnHeaderRepresentationModel repModel =
|
||||
new PriorityColumnHeaderRepresentationModel(10, 20);
|
||||
PriorityColumnHeader expectedColumnHeader = new PriorityColumnHeader(10, 20);
|
||||
|
||||
PriorityColumnHeader columnHeader = assembler.toEntityModel(repModel);
|
||||
|
||||
assertThat(columnHeader).usingRecursiveComparison().isEqualTo(expectedColumnHeader);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue