TSK-433: Delete classification by id using REST API.

This commit is contained in:
Holger Hagen 2018-04-12 12:21:54 +02:00 committed by Martin Rojas Miguel Angel
parent b86392caa7
commit 1dd6dcddf1
10 changed files with 102 additions and 76 deletions

View File

@ -161,7 +161,7 @@
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.194</version>
<version>1.4.196</version>
<scope>test</scope>
</dependency>
<dependency>

View File

@ -38,6 +38,21 @@ public interface ClassificationService {
*/
Classification getClassification(String id) throws ClassificationNotFoundException;
/**
* Delete a classification with all child classifications.
*
* @param id
* the id of the searched-for classifications
* @throws ClassificationInUseException
* if there are Task existing, which refer to this classification.
* @throws ClassificationNotFoundException
* if for an domain no classification specification is found.
* @throws NotAuthorizedException
* if the current user is not member of role BUSINESS_ADMIN or ADMIN
*/
void deleteClassification(String id)
throws ClassificationInUseException, ClassificationNotFoundException, NotAuthorizedException;
/**
* Delete a classification with all child classifications.
*

View File

@ -7,12 +7,14 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.exceptions.PersistenceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pro.taskana.Classification;
import pro.taskana.ClassificationQuery;
import pro.taskana.ClassificationService;
import pro.taskana.ClassificationSummary;
import pro.taskana.TaskSummary;
import pro.taskana.TaskanaEngine;
import pro.taskana.TaskanaRole;
@ -23,10 +25,7 @@ import pro.taskana.exceptions.ConcurrencyException;
import pro.taskana.exceptions.DomainNotFoundException;
import pro.taskana.exceptions.InvalidArgumentException;
import pro.taskana.exceptions.NotAuthorizedException;
import pro.taskana.exceptions.NotAuthorizedToQueryWorkbasketException;
import pro.taskana.exceptions.SystemException;
import pro.taskana.impl.util.IdGenerator;
import pro.taskana.mappings.AttachmentMapper;
import pro.taskana.mappings.ClassificationMapper;
import pro.taskana.mappings.JobMapper;
import pro.taskana.mappings.TaskMapper;
@ -222,7 +221,7 @@ public class ClassificationServiceImpl implements ClassificationService {
try {
Duration.parse(classification.getServiceLevel());
} catch (Exception e) {
throw new InvalidArgumentException("Invalid duration. Please use the format defined by ISO 8601");
throw new InvalidArgumentException("Invalid service level. Please use the format defined by ISO 8601");
}
}
@ -342,50 +341,61 @@ public class ClassificationServiceImpl implements ClassificationService {
throw new ClassificationNotFoundException(classificationKey, domain,
"The classification " + classificationKey + "wasn't found in the domain " + domain);
}
deleteClassification(classification.getId());
} finally {
taskanaEngine.returnConnection();
LOGGER.debug("exit from deleteClassification(key,domain)");
}
}
List<AttachmentSummaryImpl> attachments = taskanaEngine.getSqlSession()
.getMapper(AttachmentMapper.class)
.findAttachmentSummariesByClassificationId(classification.getId());
if (!attachments.isEmpty()) {
throw new ClassificationInUseException("Classification " + classification.getId()
+ " is used by Attachment " + attachments.get(0).getId());
@Override
public void deleteClassification(String classificationId)
throws ClassificationInUseException, ClassificationNotFoundException, NotAuthorizedException {
LOGGER.debug("entry to deleteClassification(id = {})", classificationId);
taskanaEngine.checkRoleMembership(TaskanaRole.BUSINESS_ADMIN, TaskanaRole.ADMIN);
try {
taskanaEngine.openConnection();
Classification classification = this.classificationMapper.findById(classificationId);
if (classification == null) {
throw new ClassificationNotFoundException(classificationId,
"The classification " + classificationId + "wasn't found");
}
if (domain.equals("")) {
if (classification.getDomain().equals("")) {
// master mode - delete all associated classifications in every domain.
List<String> domains = this.classificationMapper.getDomainsForClassification(classificationKey);
domains.remove("");
for (String classificationDomain : domains) {
deleteClassification(classificationKey, classificationDomain);
List<ClassificationSummary> classificationsInDomain = createClassificationQuery()
.keyIn(classification.getKey()).list();
for (ClassificationSummary classificationInDomain : classificationsInDomain) {
if (!"".equals(classificationInDomain.getDomain())) {
deleteClassification(classificationInDomain.getId());
}
}
}
TaskServiceImpl taskService = (TaskServiceImpl) taskanaEngine.getTaskService();
List<ClassificationSummary> childClassifications = createClassificationQuery().parentIdIn(classificationId)
.list();
for (ClassificationSummary child : childClassifications) {
this.deleteClassification(child.getId());
}
try {
List<TaskSummary> classificationTasks = taskService.createTaskQuery()
.classificationKeyIn(classificationKey)
.list();
if (classificationTasks.stream()
.anyMatch(t -> domain.equals(t.getClassificationSummary().getDomain()))) {
throw new ClassificationInUseException(
"There are Tasks that belong to this classification or a child classification. Please complete them and try again.");
this.classificationMapper.deleteClassification(classificationId);
} catch (PersistenceException e) {
if (isReferentialIntegrityConstraintViolation(e)) {
throw new ClassificationInUseException("The classification " + classificationId
+ " is in use and cannot be deleted. There are either tasks or attachments associated with the classification.");
}
} catch (NotAuthorizedToQueryWorkbasketException e) {
LOGGER.error(
"ClassificationQuery unexpectedly returned NotauthorizedException. Throwing SystemException ");
throw new SystemException("ClassificationQuery unexpectedly returned NotauthorizedException.");
}
List<String> childKeys = this.classificationMapper
.getChildrenOfClassification(classification.getId());
for (String key : childKeys) {
this.deleteClassification(key, domain);
}
this.classificationMapper.deleteClassificationInDomain(classificationKey, domain);
} finally {
taskanaEngine.returnConnection();
LOGGER.debug("exit from deleteClassification()");
}
}
private boolean isReferentialIntegrityConstraintViolation(PersistenceException e) {
// DB2 check missing
return (e.getCause().getClass().getName().equals("org.h2.jdbc.JdbcSQLException")
&& e.getMessage().contains("23503"));
}
}

View File

@ -93,24 +93,6 @@ public interface AttachmentMapper {
})
List<AttachmentSummaryImpl> findAttachmentSummariesByTaskIds(String[] taskIds);
@Select("<script>SELECT ID, TASK_ID, CREATED, MODIFIED, CLASSIFICATION_KEY, CLASSIFICATION_ID, RECEIVED "
+ "FROM TASKANA.ATTACHMENT "
+ "<where>"
+ "CLASSIFICATION_ID = #{classificationId}"
+ "</where>"
+ "</script>")
@Results(value = {
@Result(property = "id", column = "ID"),
@Result(property = "taskId", column = "TASK_ID"),
@Result(property = "created", column = "CREATED"),
@Result(property = "modified", column = "MODIFIED"),
@Result(property = "classificationSummaryImpl.key", column = "CLASSIFICATION_KEY"),
@Result(property = "classificationSummaryImpl.id", column = "CLASSIFICATION_ID"),
@Result(property = "received", column = "RECEIVED"),
})
List<AttachmentSummaryImpl> findAttachmentSummariesByClassificationId(
@Param("classificationId") String classificationId);
@Delete("DELETE FROM TASKANA.ATTACHMENT WHERE ID=#{attachmentId}")
void deleteAttachment(@Param("attachmentId") String attachmentId);

View File

@ -85,10 +85,8 @@ public interface ClassificationMapper {
void update(@Param("classification") ClassificationImpl classification);
@Delete("DELETE FROM TASKANA.CLASSIFICATION "
+ "WHERE KEY = #{classificationKey}"
+ "AND DOMAIN = #{domain}")
void deleteClassificationInDomain(@Param("classificationKey") String classificationKey,
@Param("domain") String domain);
+ "WHERE ID = #{classificationId}")
void deleteClassification(@Param("classificationId") String classificationId);
@Select("<script>"
+ "SELECT ID, KEY, CATEGORY, TYPE, DOMAIN, NAME, PARENT_ID "
@ -108,18 +106,4 @@ public interface ClassificationMapper {
List<ClassificationSummaryImpl> getAllClassificationsWithKey(@Param("key") String key,
@Param("domain") String domain);
@Select("<script>"
+ "SELECT DOMAIN "
+ "FROM TASKANA.CLASSIFICATION "
+ "WHERE KEY = #{classification_key} "
+ "<if test=\"_databaseId == 'db2'\">with UR </if> "
+ "</script>")
List<String> getDomainsForClassification(@Param("classification_key") String classificationKey);
@Select("<script>SELECT KEY "
+ "FROM TASKANA.CLASSIFICATION "
+ "WHERE PARENT_ID = #{parentId} "
+ "<if test=\"_databaseId == 'db2'\">with UR </if> "
+ "</script>")
List<String> getChildrenOfClassification(@Param("parentId") String parentId);
}

View File

@ -154,7 +154,7 @@ public class QueryTasksAccTest extends AbstractAccTest {
List<TaskSummary> results = taskService.createTaskQuery()
.classificationKeyLike("L10%")
.list();
assertThat(results.size(), equalTo(65));
assertThat(results.size(), equalTo(64));
String[] ids = results.stream()
.map(t -> t.getClassificationSummary().getKey())
@ -164,7 +164,7 @@ public class QueryTasksAccTest extends AbstractAccTest {
List<TaskSummary> result2 = taskService.createTaskQuery()
.classificationKeyIn(ids)
.list();
assertThat(result2.size(), equalTo(65));
assertThat(result2.size(), equalTo(64));
}
@WithAccessId(

View File

@ -7,7 +7,7 @@ INSERT INTO TASKANA.ATTACHMENT VALUES('TAI:000000000000000000000000000000000003'
INSERT INTO TASKANA.ATTACHMENT VALUES('TAI:000000000000000000000000000000000004','TKI:000000000000000000000000000000000002', '2018-01-29 15:55:04', null , 'L110102' , 'CLI:000000000000000000000000000000000005', 'novatec' , 'novasys', 'nvinst', 'typ1', 'val1', 'ch1', null, null);
INSERT INTO TASKANA.ATTACHMENT VALUES('TAI:000000000000000000000000000000000005','TKI:000000000000000000000000000000000002', '2018-01-29 15:55:05', null , 'L110105' , 'CLI:000000000000000000000000000000000006', 'novatec' , 'novasys', 'nvinst', 'typ1', 'val1', 'ch1', null, null);
INSERT INTO TASKANA.ATTACHMENT VALUES('TAI:000000000000000000000000000000000006','TKI:000000000000000000000000000000000002', '2018-01-29 15:55:06', null , 'L110107' , 'CLI:000000000000000000000000000000000007', 'novatec' , 'novasys', 'nvinst', 'typ1', 'val1', 'ch1', null, null);
INSERT INTO TASKANA.ATTACHMENT VALUES('TAI:000000000000000000000000000000000007','TKI:000000000000000000000000000000000002', '2018-01-29 15:55:07', null , 'L12010' , 'CLI:000000000000000000000000000000000008', 'novatec' , 'novasys', 'nvinst', 'typ1', 'val1', 'ch1', null, null);
INSERT INTO TASKANA.ATTACHMENT VALUES('TAI:000000000000000000000000000000000007','TKI:000000000000000000000000000000000002', '2018-01-29 15:55:07', null , 'L12010' , 'CLI:100000000000000000000000000000000008', 'novatec' , 'novasys', 'nvinst', 'typ1', 'val1', 'ch1', null, null);
INSERT INTO TASKANA.ATTACHMENT VALUES('TAI:000000000000000000000000000000000008','TKI:000000000000000000000000000000000008', '2018-01-29 15:55:08', null , 'L140101' , 'CLI:000000000000000000000000000000000009', 'novatec' , 'novasys', 'nvinst', 'typ1', 'val1', 'ch1', null, null);
INSERT INTO TASKANA.ATTACHMENT VALUES('TAI:000000000000000000000000000000000009','TKI:000000000000000000000000000000000000', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'L1050' , 'CLI:100000000000000000000000000000000003', 'novatec' , 'novasys', 'nvinst', 'typ1', 'val1', 'ch1', null, null);
INSERT INTO TASKANA.ATTACHMENT VALUES('TAI:000000000000000000000000000000000010','TKI:000000000000000000000000000000000053', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'L1050' , 'CLI:100000000000000000000000000000000003', 'novatec' , 'novasys', 'nvinst', 'typ1', 'val1', 'ch1', null, null);

View File

@ -48,7 +48,7 @@ INSERT INTO TASKANA.TASK VALUES('TKI:000000000000000000000000000000000039', '201
INSERT INTO TASKANA.TASK VALUES('TKI:000000000000000000000000000000000040', '2018-01-29 15:55:24', '2018-01-30 15:55:24', '2018-01-30 16:55:24', '2018-01-30 16:55:24', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'COMPLETED' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000007' , 'USER_1_2' , 'DOMAIN_A', 'PI_0000000000040' , 'DOC_0000000000000000040' , 'user_1_2' , '00' , 'PASystem' , '00' , 'SDNR' , '00011122' , true , false , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null );
-- Tasks for QueryTasksWithSortingTest
INSERT INTO TASKANA.TASK VALUES('TKI:000000000000000000000000000000000041', '2018-01-29 15:55:00', '2018-01-30 15:55:00', null , '2018-01-30 15:55:00', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Task99' , 'creator_user_id' , 'Lorem ipsum was n Quatsch dolor sit amet.', 'Some custom Note' , 1 , 'CLAIMED' , 'AUTOMATIC' , 'T6310' , 'CLI:000000000000000000000000000000000011', 'WBI:100000000000000000000000000000000015' , 'USER_3_2' , 'DOMAIN_B', 'BPI21' , 'PBPI21' , 'user_3_1' , 'MyCompany1', 'MySystem1', 'MyInstance1' , 'MyType1', 'MyValue1' , true , false , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null );
INSERT INTO TASKANA.TASK VALUES('TKI:000000000000000000000000000000000042', '2018-01-29 15:55:01', '2018-01-30 15:55:00', null , '2018-01-30 15:55:01', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Task01' , 'creator_user_id' , 'Lorem ipsum was n Quatsch dolor sit amet.', 'Some custom Note' , 2 , 'CLAIMED' , 'EXTERN' , 'L10303' , 'CLI:100000000000000000000000000000000005', 'WBI:100000000000000000000000000000000015' , 'USER_3_2' , 'DOMAIN_B', 'BPI21' , 'PBPI21' , 'user_3_1' , 'MyCompany1', 'MySystem1', 'MyInstance1' , 'MyType1', 'MyValue1' , true , false , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null );
INSERT INTO TASKANA.TASK VALUES('TKI:000000000000000000000000000000000042', '2018-01-29 15:55:01', '2018-01-30 15:55:00', null , '2018-01-30 15:55:01', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Task01' , 'creator_user_id' , 'Lorem ipsum was n Quatsch dolor sit amet.', 'Some custom Note' , 2 , 'CLAIMED' , 'EXTERN' , 'L110102' , 'CLI:100000000000000000000000000000000005', 'WBI:100000000000000000000000000000000015' , 'USER_3_2' , 'DOMAIN_B', 'BPI21' , 'PBPI21' , 'user_3_1' , 'MyCompany1', 'MySystem1', 'MyInstance1' , 'MyType1', 'MyValue1' , true , false , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null );
INSERT INTO TASKANA.TASK VALUES('TKI:000000000000000000000000000000000043', '2018-01-29 15:55:02', '2018-01-30 15:55:00', null , '2018-01-30 15:55:02', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Task02' , 'creator_user_id' , 'Lorem ipsum was n Quatsch t. Aber stimmt.', 'Some custom Note' , 2 , 'CLAIMED' , 'EXTERN' , 'A12' , 'CLI:200000000000000000000000000000000001', 'WBI:100000000000000000000000000000000015' , 'USER_3_2' , 'DOMAIN_B', 'BPI21' , 'PBPI21' , 'user_3_1' , 'MyCompany1', 'MySystem1', 'MyInstance1' , 'MyType1', 'MyValue1' , true , false , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null );
INSERT INTO TASKANA.TASK VALUES('TKI:000000000000000000000000000000000044', '2018-01-29 15:55:03', null , null , '2018-01-29 15:55:03', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1060' , 'CLI:200000000000000000000000000000000017', 'WBI:100000000000000000000000000000000015' , 'USER_3_2' , 'DOMAIN_B', 'PI_0000000000003' , 'DOC_0000000000000000003' , null , '00' , 'PASystem' , '00' , 'VNR' , '11223344' , false , false , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null );
INSERT INTO TASKANA.TASK VALUES('TKI:000000000000000000000000000000000045', '2018-01-29 15:55:04', null , null , '2018-01-29 15:55:04', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1060' , 'CLI:200000000000000000000000000000000017', 'WBI:100000000000000000000000000000000015' , 'USER_3_2' , 'DOMAIN_B', 'PI_0000000000004' , 'DOC_0000000000000000004' , null , '00' , 'PASystem' , '00' , 'VNR' , '11223344' , false , false , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null );

View File

@ -19,11 +19,13 @@ import org.springframework.hateoas.hal.Jackson2HalModule;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
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 com.fasterxml.jackson.databind.DeserializationFeature;
@ -117,6 +119,29 @@ public class ClassificationControllerIntTest {
assertEquals("Zustimmungserklärung", response.getBody().name);
}
@Test(expected = HttpClientErrorException.class)
public void testDeleteClassification() {
RestTemplate template = getRestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Basic dGVhbWxlYWRfMTp0ZWFtbGVhZF8x");
HttpEntity<String> request = new HttpEntity<String>(headers);
ResponseEntity<ClassificationSummaryResource> response = template.exchange(
"http://127.0.0.1:" + port + "/v1/classifications/CLI:200000000000000000000000000000000004",
HttpMethod.DELETE,
request,
new ParameterizedTypeReference<ClassificationSummaryResource>() {
});
assertEquals(HttpStatus.NO_CONTENT, response.getStatusCode());
response = template.exchange(
"http://127.0.0.1:" + port + "/v1/classifications/CLI:200000000000000000000000000000000004",
HttpMethod.GET,
request,
new ParameterizedTypeReference<ClassificationSummaryResource>() {
});
}
/**
* Return a REST template which is capable of dealing with responses in HAL format
*

View File

@ -10,6 +10,7 @@ import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
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;
@ -25,6 +26,7 @@ import pro.taskana.ClassificationQuery;
import pro.taskana.ClassificationService;
import pro.taskana.ClassificationSummary;
import pro.taskana.exceptions.ClassificationAlreadyExistException;
import pro.taskana.exceptions.ClassificationInUseException;
import pro.taskana.exceptions.ClassificationNotFoundException;
import pro.taskana.exceptions.ConcurrencyException;
import pro.taskana.exceptions.DomainNotFoundException;
@ -138,6 +140,14 @@ public class ClassificationController extends AbstractPagingController {
return result;
}
@DeleteMapping(path = "/{classificationId}")
@Transactional(readOnly = true, rollbackFor = Exception.class)
public ResponseEntity<?> deleteClassification(@PathVariable String classificationId)
throws ClassificationNotFoundException, ClassificationInUseException, NotAuthorizedException {
classificationService.deleteClassification(classificationId);
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}
private void addSortingToQuery(ClassificationQuery query, String sortBy, String order)
throws IllegalArgumentException {
BaseQuery.SortDirection sortDirection = getSortDirection(order);