From 2d45297acd546d84583c750b6031f63843adf62f Mon Sep 17 00:00:00 2001 From: BVier <26220150+BVier@users.noreply.github.com> Date: Wed, 17 Jan 2018 13:55:23 +0100 Subject: [PATCH] TSK-83: delete Classification (with tests) --- .../pro/taskana/ClassificationService.java | 16 +++ .../ClassificationInUseException.java | 13 +++ .../impl/ClassificationServiceImpl.java | 35 +++++++ .../pro/taskana/impl/TaskServiceImpl.java | 6 +- .../model/mappings/ClassificationMapper.java | 46 ++++----- .../taskana/model/mappings/TaskMapper.java | 6 ++ .../DeleteClassificationAccTest.java | 97 ++++++++++++------- .../src/test/resources/sql/classification.sql | 2 +- 8 files changed, 155 insertions(+), 66 deletions(-) create mode 100644 lib/taskana-core/src/main/java/pro/taskana/exceptions/ClassificationInUseException.java diff --git a/lib/taskana-core/src/main/java/pro/taskana/ClassificationService.java b/lib/taskana-core/src/main/java/pro/taskana/ClassificationService.java index df0561db0..af52f522d 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/ClassificationService.java +++ b/lib/taskana-core/src/main/java/pro/taskana/ClassificationService.java @@ -3,6 +3,7 @@ package pro.taskana; import java.util.List; import pro.taskana.exceptions.ClassificationAlreadyExistException; +import pro.taskana.exceptions.ClassificationInUseException; import pro.taskana.exceptions.ClassificationNotFoundException; /** @@ -44,6 +45,21 @@ public interface ClassificationService { */ Classification getClassification(String key, String domain) throws ClassificationNotFoundException; + /** + * Delete a classification with all child classifications. + * + * @param classificationKey + * the key of the classification you want to delete. + * @param domain + * the domains for which you want to delete the classification. + * if "", the function tries to delete the "master domain" classification and any other classification with this key. + * @throws ClassificationInUseException + * if there are Task existing, which refer to this classification. + * @throws ClassificationNotFoundException + * if for an domain no classification specification is found. + */ + void deleteClassification(String classificationKey, String domain) throws ClassificationInUseException, ClassificationNotFoundException; + /** * Persists a new classification after adding default values.
* The classification will be added to root-domain, too - if not already existing. diff --git a/lib/taskana-core/src/main/java/pro/taskana/exceptions/ClassificationInUseException.java b/lib/taskana-core/src/main/java/pro/taskana/exceptions/ClassificationInUseException.java new file mode 100644 index 000000000..c7052f1b7 --- /dev/null +++ b/lib/taskana-core/src/main/java/pro/taskana/exceptions/ClassificationInUseException.java @@ -0,0 +1,13 @@ +package pro.taskana.exceptions; + +/** + * Thrown if a specific task is not in the database. + */ +public class ClassificationInUseException extends TaskanaException { + + public ClassificationInUseException(String msg) { + super(msg); + } + + private static final long serialVersionUID = 1L; +} diff --git a/lib/taskana-core/src/main/java/pro/taskana/impl/ClassificationServiceImpl.java b/lib/taskana-core/src/main/java/pro/taskana/impl/ClassificationServiceImpl.java index 74057ce3d..695db1eaf 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/impl/ClassificationServiceImpl.java +++ b/lib/taskana-core/src/main/java/pro/taskana/impl/ClassificationServiceImpl.java @@ -14,6 +14,7 @@ import pro.taskana.ClassificationService; import pro.taskana.ClassificationSummary; import pro.taskana.TaskanaEngine; import pro.taskana.exceptions.ClassificationAlreadyExistException; +import pro.taskana.exceptions.ClassificationInUseException; import pro.taskana.exceptions.ClassificationNotFoundException; import pro.taskana.exceptions.NotAuthorizedException; import pro.taskana.exceptions.SystemException; @@ -291,4 +292,38 @@ public class ClassificationServiceImpl implements ClassificationService { } return isExisting; } + + @Override + public void deleteClassification(String classificationKey, String domain) throws ClassificationInUseException, ClassificationNotFoundException { + try { + taskanaEngineImpl.openConnection(); + if (this.classificationMapper.findByKeyAndDomain(classificationKey, domain) == null) { + throw new ClassificationNotFoundException("The classification " + classificationKey + "wasn't found in the domain " + domain); + } + + if (domain.equals("")) { + //master mode - delete all associated classifications in every domain. + List domains = this.classificationMapper.getDomainsForClassification(classificationKey); + domains.remove(""); + for (String classificationDomain : domains) { + deleteClassification(classificationKey, classificationDomain); + } + } + + TaskServiceImpl taskService = (TaskServiceImpl) taskanaEngineImpl.getTaskService(); + int countTasks = taskService.countTasksByClassificationAndDomain(classificationKey, domain); + if (countTasks > 0) { + throw new ClassificationInUseException("There are " + countTasks + " Tasks which belong to this classification or a child classification. Please complete them and try again."); + } + + List childKeys = this.classificationMapper.getChildrenOfClassification(classificationKey, domain); + for (String key : childKeys) { + this.deleteClassification(key, domain); + } + + this.classificationMapper.deleteClassificationInDomain(classificationKey, domain); + } finally { + taskanaEngineImpl.returnConnection(); + } + } } diff --git a/lib/taskana-core/src/main/java/pro/taskana/impl/TaskServiceImpl.java b/lib/taskana-core/src/main/java/pro/taskana/impl/TaskServiceImpl.java index 61fda7ae0..72ad7109e 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/impl/TaskServiceImpl.java +++ b/lib/taskana-core/src/main/java/pro/taskana/impl/TaskServiceImpl.java @@ -835,9 +835,9 @@ public class TaskServiceImpl implements TaskService { } } - private void initAttachment(AttachmentImpl attachment, Task newTask) { - if (attachment.getId() == null) { - attachment.setId(IdGenerator.generateWithPrefix(ID_PREFIX_ATTACHMENT)); + public int countTasksByClassificationAndDomain(String classificationKey, String domain) { + return taskMapper.countTasksByClassificationAndDomain(classificationKey, domain); + } } if (attachment.getCreated() == null) { attachment.setCreated(Instant.now()); diff --git a/lib/taskana-core/src/main/java/pro/taskana/model/mappings/ClassificationMapper.java b/lib/taskana-core/src/main/java/pro/taskana/model/mappings/ClassificationMapper.java index 03c113e4c..2de384a18 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/model/mappings/ClassificationMapper.java +++ b/lib/taskana-core/src/main/java/pro/taskana/model/mappings/ClassificationMapper.java @@ -2,6 +2,7 @@ package pro.taskana.model.mappings; import java.util.List; +import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Result; @@ -51,6 +52,11 @@ public interface ClassificationMapper { value = "UPDATE CLASSIFICATION SET KEY = #{classification.key}, PARENT_CLASSIFICATION_KEY = #{classification.parentClassificationKey}, CATEGORY = #{classification.category}, TYPE = #{classification.type}, NAME = #{classification.name}, DESCRIPTION = #{classification.description}, PRIORITY = #{classification.priority}, SERVICE_LEVEL = #{classification.serviceLevel}, DOMAIN = #{classification.domain}, VALID_IN_DOMAIN = #{classification.isValidInDomain}, APPLICATION_ENTRY_POINT = #{classification.applicationEntryPoint}, CUSTOM_1 = #{classification.custom1}, CUSTOM_2 = #{classification.custom2}, CUSTOM_3 = #{classification.custom3}, CUSTOM_4 = #{classification.custom4}, CUSTOM_5 = #{classification.custom5}, CUSTOM_6 = #{classification.custom6}, CUSTOM_7 = #{classification.custom7}, CUSTOM_8 = #{classification.custom8} WHERE ID = #{classification.id}") void update(@Param("classification") ClassificationImpl classification); + @Delete("DELETE FROM CLASSIFICATION " + + "WHERE KEY = #{classificationKey}" + + "AND DOMAIN = #{domain}") + void deleteClassificationInDomain(@Param("classificationKey") String classificationKey, @Param("domain") String domain); + @Select("") + List getDomainsForClassification(@Param("classification_key") String classificationKey); + + @Select("SELECT KEY " + + "FROM CLASSIFICATION " + + "WHERE PARENT_CLASSIFICATION_KEY = #{classification_key} " + + "AND DOMAIN = #{domain}") + List getChildrenOfClassification(@Param("classification_key") String classificationKey, @Param("domain") String domain); } diff --git a/lib/taskana-core/src/main/java/pro/taskana/model/mappings/TaskMapper.java b/lib/taskana-core/src/main/java/pro/taskana/model/mappings/TaskMapper.java index 0136a872f..80cf47f80 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/model/mappings/TaskMapper.java +++ b/lib/taskana-core/src/main/java/pro/taskana/model/mappings/TaskMapper.java @@ -170,4 +170,10 @@ public interface TaskMapper { @Result(property = "custom9", column = "CUSTOM_9"), @Result(property = "custom10", column = "CUSTOM_10") }) List findTaskSummariesByWorkbasketKey(@Param("workbasketKey") String workbasketKey); + + @Select("SELECT COUNT(ID) " + + "FROM TASK " + + "WHERE CLASSIFICATION_KEY = #{classificationKey} " + + "AND DOMAIN = #{domain}") + int countTasksByClassificationAndDomain(@Param("classificationKey") String classificationKey, @Param("domain") String domain); } diff --git a/lib/taskana-core/src/test/java/acceptance/classification/DeleteClassificationAccTest.java b/lib/taskana-core/src/test/java/acceptance/classification/DeleteClassificationAccTest.java index a1bbba98f..6bd15465a 100644 --- a/lib/taskana-core/src/test/java/acceptance/classification/DeleteClassificationAccTest.java +++ b/lib/taskana-core/src/test/java/acceptance/classification/DeleteClassificationAccTest.java @@ -5,17 +5,15 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.sql.SQLException; -import java.util.List; import org.h2.store.fs.FileUtils; import org.junit.AfterClass; -import org.junit.Ignore; import org.junit.Test; import acceptance.AbstractAccTest; import pro.taskana.Classification; import pro.taskana.ClassificationService; -import pro.taskana.ClassificationSummary; +import pro.taskana.exceptions.ClassificationInUseException; import pro.taskana.exceptions.ClassificationNotFoundException; import pro.taskana.exceptions.NotAuthorizedException; @@ -24,54 +22,85 @@ import pro.taskana.exceptions.NotAuthorizedException; */ public class DeleteClassificationAccTest extends AbstractAccTest { + private ClassificationService classificationService; + public DeleteClassificationAccTest() { super(); + classificationService = taskanaEngine.getClassificationService(); } - @Ignore @Test public void testDeleteClassificationInDomain() - throws SQLException, ClassificationNotFoundException, NotAuthorizedException { - ClassificationService classificationService = taskanaEngine.getClassificationService(); - // classificationService.deleteClassification("L140101", "DOMAIN_A"); + throws SQLException, ClassificationNotFoundException, NotAuthorizedException, ClassificationInUseException { + classificationService.deleteClassification("L140101", "DOMAIN_A"); Classification classification = classificationService.getClassification("L140101", "DOMAIN_A"); assertNotNull(classification); assertEquals("", classification.getDomain()); - List classifications = classificationService.getAllClassifications("L140101", - "DOMAIN_A"); - ClassificationSummary temp = classifications.get(classifications.size() - 1); - classification = classificationService.getClassification(temp.getKey(), temp.getDomain()); + classification = classificationService.getClassification("L140101", "DOMAIN_A"); + assertTrue(classification.getDomain() == ""); + assertTrue("DOMAIN_A" != classification.getDomain()); + } + + @Test (expected = ClassificationInUseException.class) + public void testThrowExeptionIfDeleteClassificationWithExistingTasks() + throws SQLException, ClassificationNotFoundException, NotAuthorizedException, ClassificationInUseException { + classificationService.deleteClassification("L1050", "DOMAIN_A"); + } + + @Test (expected = ClassificationInUseException.class) + public void testThrowExeptionIfDeleteMasterClassificationWithExistingTasks() + throws SQLException, ClassificationNotFoundException, NotAuthorizedException, ClassificationInUseException { + classificationService.deleteClassification("L1050", ""); } - @Ignore @Test - public void testDeleteClassificationFromRootDomain() - throws SQLException, ClassificationNotFoundException, NotAuthorizedException { - ClassificationService classificationService = taskanaEngine.getClassificationService(); - // classificationService.deleteClassification("L1050", "DOMAIN_A"); - // classificationService.deleteClassification("L1050", ""); + public void testDeleteMasterClassification() + throws SQLException, ClassificationNotFoundException, NotAuthorizedException, ClassificationInUseException { + classificationService.deleteClassification("L12010", ""); - Classification classification = classificationService.getClassification("L140101", "DOMAIN_A"); - assertTrue(classification == null); + boolean classificationNotFound = false; + try { + classificationService.getClassification("L12010", "DOMAIN_A"); + } catch (ClassificationNotFoundException e) { + classificationNotFound = true; + } + assertTrue(classificationNotFound); } - // @Ignore - // @Test(expected = ClassificationInUseException.class) - // public void testGetExceptionIfDeletedClassificationFromRootDomainIsStillUsedInDomain() - // throws SQLException, ClassificationNotFoundException, NotAuthorizedException, ClassificationInUseException { - // ClassificationService classificationService = taskanaEngine.getClassificationService(); - // classificationService.deleteClassification("L10000", ""); - // } - // - // @Ignore - // @Test(expected = ClassificationInUseException.class) - // public void testGetExceptionIfDeletedClassificationIsStillUsedInTask() - // throws SQLException, ClassificationNotFoundException, NotAuthorizedException, ClassificationInUseException { - // ClassificationService classificationService = taskanaEngine.getClassificationService(); - // classificationService.deleteClassification("L10000", "DOMAIN_A"); - // } + @Test + public void testThrowExceptionWhenChildClassificationIsInUseAndRollback() + throws ClassificationInUseException, NotAuthorizedException, ClassificationNotFoundException { + boolean classificationInUse = false; + try { + classificationService.deleteClassification("L11010", "DOMAIN_A"); + } catch (ClassificationInUseException e) { + classificationInUse = true; + } + assertTrue(classificationInUse); + classificationService.getClassification("L11010", "DOMAIN_A"); + + classificationInUse = false; + try { + classificationService.deleteClassification("L11010", ""); + } catch (ClassificationInUseException e) { + classificationInUse = true; + } + assertTrue(classificationInUse); + classificationService.getClassification("L11010", ""); + classificationService.getClassification("L11010", "DOMAIN_A"); + } + + @Test(expected = ClassificationNotFoundException.class) + public void testThrowClassificationNotFoundIfClassificationNotExists() throws ClassificationNotFoundException, ClassificationInUseException { + classificationService.deleteClassification("not existing classification key", ""); + } + + @Test(expected = ClassificationNotFoundException.class) + public void testThrowClassificationNotFoundIfClassificationNotExistsInDomain() throws ClassificationNotFoundException, ClassificationInUseException { + classificationService.deleteClassification("L10000", "DOMAIN_B"); + } @AfterClass public static void cleanUpClass() { diff --git a/lib/taskana-core/src/test/resources/sql/classification.sql b/lib/taskana-core/src/test/resources/sql/classification.sql index 76a7bf0e4..75ea9fd5d 100644 --- a/lib/taskana-core/src/test/resources/sql/classification.sql +++ b/lib/taskana-core/src/test/resources/sql/classification.sql @@ -24,7 +24,7 @@ INSERT INTO CLASSIFICATION VALUES('CLI:100000000000000000000000000000000008', 'L INSERT INTO CLASSIFICATION VALUES('CLI:100000000000000000000000000000000009', 'L140101', '', 'EXTERN', 'TASK', 'DOMAIN_A', TRUE, CURRENT_TIMESTAMP, 'Zustimmungserklärung', 'Zustimmungserklärung', 2, 'P2D', '', 'VNR', '', '', '', '', '', '', ''); INSERT INTO CLASSIFICATION VALUES('CLI:100000000000000000000000000000000010', 'T2100', '', 'MANUAL', 'TASK', 'DOMAIN_A', TRUE, CURRENT_TIMESTAMP, 'T-Vertragstermin VERA', 'T-Vertragstermin VERA', 2, 'P2D', '', 'VNR', '', '', '', '', '', '', ''); INSERT INTO CLASSIFICATION VALUES('CLI:100000000000000000000000000000000011', 'T6310', '', 'AUTOMATIC', 'TASK', 'DOMAIN_A', TRUE, CURRENT_TIMESTAMP, 'T-GUK Honorarrechnung erstellen', 'Generali Unterstützungskasse Honorar wird fällig', 2, 'P2D', '', 'VNR', '', '', '', '', '', '', ''); -INSERT INTO CLASSIFICATION VALUES('CLI:100000000000000000000000000000000013', 'DOCTYPE_DEFAULT', '', 'EXTERN', 'DOCUMENT', 'DOMAIN_A', TRUE, CURRENT_TIMESTAMP, 'EP allgemein', 'EP allgemein', 99, 'P2000D', '', 'VNR', '', '', '', '', '', '', ''); +INSERT INTO CLASSIFICATION VALUES('CLI:100000000000000000000000000000000013', 'DOKTYP_DEFAULT', '', 'EXTERN', 'DOCUMENT', 'DOMAIN_A', TRUE, CURRENT_TIMESTAMP, 'EP allgemein', 'EP allgemein', 99, 'P2000D', '', 'VNR', '', '', '', '', '', '', ''); INSERT INTO CLASSIFICATION VALUES('CLI:100000000000000000000000000000000014', 'L10000', '', 'EXTERN', 'TASK', 'DOMAIN_A', TRUE, CURRENT_TIMESTAMP, 'BUZ-Leistungsfall', 'BUZ-Leistungsfall', 1, 'P1D', '', 'VNR,RVNR,KOLVNR', '', '', '', '', '', '', ''); INSERT INTO CLASSIFICATION VALUES('CLI:100000000000000000000000000000000016', 'T2000', '', 'MANUAL', 'TASK', 'DOMAIN_A', TRUE, CURRENT_TIMESTAMP, 'T-Vertragstermin', 'T-Vertragstermin', 1, 'P1D', '', 'VNR,RVNR,KOLVNR', '', '', '', '', '', '', '');