From 3a9a065f5004a4b79b99f9ba538460185df62a44 Mon Sep 17 00:00:00 2001 From: Jens Pelzetter Date: Wed, 11 Jan 2023 20:08:27 +0100 Subject: [PATCH] ImExporters update (will not compile yet!) --- .../CategorizationImExporter.java | 51 ++++- .../categorization/CategoryImExporter.java | 171 ++++++++++++++--- .../categorization/DomainImExporter.java | 102 +++++++++- .../DomainOwnershipImExporter.java | 44 ++++- .../libreccm/core/ResourceTypeImExporter.java | 59 +++++- .../imexport/AbstractEntityImExporter.java | 115 +++++++++++- .../org/libreccm/imexport/ImportExport.java | 29 ++- .../libreccm/security/GroupImExporter.java | 67 ++++++- .../security/GroupMembershipImExporter.java | 21 ++- .../security/PermissionImExporter.java | 55 ++++++ .../org/libreccm/security/RoleImExporter.java | 175 +++++++++++++++++- .../security/RoleMembershipImExporter.java | 21 ++- .../org/libreccm/security/UserImExporter.java | 56 +++++- .../libreccm/web/ApplicationImExporter.java | 64 ++++++- .../workflow/AssignableTaskImExporter.java | 61 +++++- .../libreccm/workflow/WorkflowImExporter.java | 99 +++++++++- 16 files changed, 1098 insertions(+), 92 deletions(-) diff --git a/ccm-core/src/main/java/org/libreccm/categorization/CategorizationImExporter.java b/ccm-core/src/main/java/org/libreccm/categorization/CategorizationImExporter.java index bd6993126..6c2e5ef78 100644 --- a/ccm-core/src/main/java/org/libreccm/categorization/CategorizationImExporter.java +++ b/ccm-core/src/main/java/org/libreccm/categorization/CategorizationImExporter.java @@ -22,6 +22,7 @@ import org.libreccm.imexport.AbstractEntityImExporter; import org.libreccm.imexport.Processes; import java.util.Objects; +import java.util.Optional; import java.util.Set; import javax.annotation.PostConstruct; @@ -29,7 +30,6 @@ import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.persistence.EntityManager; import javax.persistence.NoResultException; -import javax.transaction.Transactional; /** * Exporter/Importer for {@link Categorization} entities. @@ -41,6 +41,10 @@ import javax.transaction.Transactional; public class CategorizationImExporter extends AbstractEntityImExporter { + + @Inject + private CategorizationRepository categoriationRepo; + @Inject private EntityManager entityManager; @@ -60,7 +64,50 @@ public class CategorizationImExporter } @Override - @Transactional(Transactional.TxType.REQUIRED) + protected Optional findExistingEntity(final String uuid) { + return categoriationRepo.findByUuid(uuid); + } + + @Override + protected void updateExistingEntity( + final Categorization existingEntity, final Categorization importedEntity + ) { + if (!Objects.equals( + existingEntity.getCategory(), importedEntity.getCategory() + )) { + existingEntity.setCategory(importedEntity.getCategory()); + } + + if (!Objects.equals( + existingEntity.getCategorizedObject(), + importedEntity.getCategorizedObject() + )) { + existingEntity.setCategorizedObject( + importedEntity.getCategorizedObject() + ); + } + + if (existingEntity.isIndexObject() != importedEntity.isIndexObject()) { + existingEntity.setIndexObject(importedEntity.isIndexObject()); + } + + if (existingEntity.getCategoryOrder() != importedEntity + .getCategoryOrder()) { + existingEntity.setCategoryOrder(importedEntity.getCategoryOrder()); + } + + if (existingEntity.getObjectOrder() != importedEntity.getObjectOrder()) { + existingEntity.setObjectOrder(importedEntity.getObjectOrder()); + } + + if (!Objects.equals(existingEntity.getType(), importedEntity.getType())) { + existingEntity.setType(importedEntity.getType()); + } + + entityManager.merge(existingEntity); + } + + @Override protected void saveImportedEntity(final Categorization entity) { entityManager.merge(entity); } diff --git a/ccm-core/src/main/java/org/libreccm/categorization/CategoryImExporter.java b/ccm-core/src/main/java/org/libreccm/categorization/CategoryImExporter.java index facd6ebc3..c72767c41 100644 --- a/ccm-core/src/main/java/org/libreccm/categorization/CategoryImExporter.java +++ b/ccm-core/src/main/java/org/libreccm/categorization/CategoryImExporter.java @@ -18,18 +18,21 @@ */ package org.libreccm.categorization; +import org.libreccm.core.UnexpectedErrorException; import org.libreccm.imexport.AbstractEntityImExporter; -import org.libreccm.imexport.Exportable; import org.libreccm.imexport.Processes; +import java.util.List; +import java.util.Locale; +import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import javax.annotation.PostConstruct; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; -import javax.transaction.Transactional; /** * Exporter/Importer for {@link Category} entities. @@ -40,6 +43,9 @@ import javax.transaction.Transactional; @Processes(Category.class) public class CategoryImExporter extends AbstractEntityImExporter { + @Inject + private CategoryManager categoryManager; + @Inject private CategoryRepository categoryRepository; @@ -59,31 +65,146 @@ public class CategoryImExporter extends AbstractEntityImExporter { } @Override - @Transactional(Transactional.TxType.REQUIRED) - protected void saveImportedEntity(final Category entity) { - final Optional result = categoryRepository.findByUuid( - entity.getUuid() - ); + protected Optional findExistingEntity(final String uuid) { + return categoryRepository.findByUuid(uuid); + } - final Category category; - if (result.isPresent()) { - category = result.get(); - category.setAbstractCategory(entity.isAbstractCategory()); - category.setCategoryOrder(entity.getCategoryOrder()); - category.setDescription(entity.getDescription()); - category.setDisplayName(entity.getDisplayName()); - category.setEnabled(entity.isEnabled()); - category.setName(entity.getName()); - category.setObjects(entity.getObjects()); - category.setParentCategory(entity.getParentCategory()); - category.setSubCategories(entity.getSubCategories()); - category.setTitle(entity.getTitle()); - category.setUniqueId(entity.getUniqueId()); - category.setVisible(entity.isVisible()); - } else { - category = entity; + @Override + protected void saveImportedEntity(final Category entity) { + categoryRepository.save(entity); + } + + @Override + protected void updateExistingEntity( + final Category existingEntity, + final Category importedEntity + ) { + if (existingEntity.isAbstractCategory() != importedEntity + .isAbstractCategory()) { + existingEntity.setAbstractCategory( + importedEntity.isAbstractCategory() + ); } - categoryRepository.save(category); + + if (existingEntity.getCategoryOrder() != importedEntity + .getCategoryOrder()) { + existingEntity.setCategoryOrder(importedEntity.getCategoryOrder()); + } + + if (!Objects.equals( + existingEntity.getDescription(), + importedEntity.getDescription() + )) { + syncLocalizedStrings( + importedEntity.getDescription(), + existingEntity.getDescription() + ); + } + + if (!Objects.equals( + existingEntity.getDisplayName(), + importedEntity.getDisplayName() + )) { + existingEntity.setDisplayName(importedEntity.getDisplayName()); + } + + if (existingEntity.isEnabled() != importedEntity.isEnabled()) { + existingEntity.setEnabled(importedEntity.isEnabled()); + } + + if (!Objects.equals( + existingEntity.getName(), + importedEntity.getName() + )) { + existingEntity.setName(importedEntity.getName()); + } + + if (!Objects.equals( + existingEntity.getObjects(), + importedEntity.getObjects() + )) { + final Set categorizationsToRemove = existingEntity + .getObjects() + .stream() + .filter( + categorization -> !importedEntity.getObjects().contains( + categorization + ) + ) + .collect(Collectors.toSet()); + + for (Categorization toRemove : categorizationsToRemove) { + try { + categoryManager.removeObjectFromCategory( + toRemove.getCategorizedObject(), + existingEntity + ); + } catch (ObjectNotAssignedToCategoryException ex) { + throw new UnexpectedErrorException(ex); + } + } + } + + if (!Objects.equals( + existingEntity.getParentCategory(), + importedEntity.getParentCategory() + )) { + final Category oldParentCategory = existingEntity + .getParentCategory(); + final Category newParentCategory = importedEntity + .getParentCategory(); + + categoryManager.removeSubCategoryFromCategory( + existingEntity, + oldParentCategory + ); + + categoryManager.addSubCategoryToCategory( + existingEntity, + newParentCategory + ); + } + + if (!Objects.equals( + existingEntity.getSubCategories(), + importedEntity.getSubCategories() + )) { + final Set subCategoriesToRemove = existingEntity + .getSubCategories() + .stream() + .filter( + subCat -> !importedEntity.getSubCategories().contains( + subCat + ) + ) + .collect(Collectors.toSet()); + + for (final Category toRemove : subCategoriesToRemove) { + try { + categoryManager.removeObjectFromCategory( + toRemove, existingEntity + ); + } catch (ObjectNotAssignedToCategoryException ex) { + throw new UnexpectedErrorException(ex); + } + } + } + + if (!Objects.equals( + existingEntity.getTitle(), + importedEntity.getTitle() + )) { + syncLocalizedStrings( + importedEntity.getTitle(), + existingEntity.getTitle() + ); + } + + if (existingEntity.isVisible() != importedEntity.isVisible()) { + existingEntity.setVisible(importedEntity.isVisible()); + } + + categoryRepository.save(existingEntity); } @Override diff --git a/ccm-core/src/main/java/org/libreccm/categorization/DomainImExporter.java b/ccm-core/src/main/java/org/libreccm/categorization/DomainImExporter.java index 7416aaebf..c96100e0e 100644 --- a/ccm-core/src/main/java/org/libreccm/categorization/DomainImExporter.java +++ b/ccm-core/src/main/java/org/libreccm/categorization/DomainImExporter.java @@ -19,12 +19,14 @@ package org.libreccm.categorization; import org.libreccm.imexport.AbstractEntityImExporter; -import org.libreccm.imexport.Exportable; import org.libreccm.imexport.Processes; -import java.util.Collections; +import java.util.Locale; +import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import javax.annotation.PostConstruct; import javax.enterprise.context.RequestScoped; @@ -39,6 +41,9 @@ import javax.inject.Inject; @Processes(Domain.class) public class DomainImExporter extends AbstractEntityImExporter { + @Inject + private DomainManager domainManager; + @Inject private DomainRepository domainRepository; @@ -50,16 +55,105 @@ public class DomainImExporter extends AbstractEntityImExporter { @Override public Class getEntityClass() { - return Domain.class; } @Override - protected void saveImportedEntity(final Domain entity) { + protected Optional findExistingEntity(final String uuid) { + return domainRepository.findByUuid(uuid); + } + @Override + protected void saveImportedEntity(final Domain entity) { domainRepository.save(entity); } + @Override + protected void updateExistingEntity( + final Domain existingEntity, + final Domain importedEntity + ) { + if (!Objects.equals( + existingEntity.getDomainKey(), + importedEntity.getDomainKey() + )) { + existingEntity.setDomainKey(importedEntity.getDomainKey()); + } + + if (!Objects.equals(existingEntity.getUri(), importedEntity.getUri())) { + existingEntity.setUri(importedEntity.getUri()); + } + + if (!Objects.equals( + existingEntity.getTitle(), + importedEntity.getTitle() + )) { + syncLocalizedStrings( + importedEntity.getTitle(), + existingEntity.getTitle() + ); + } + + if (!Objects.equals( + existingEntity.getDescription(), + importedEntity.getDescription() + )) { + for (final Map.Entry entry : importedEntity + .getDescription().getValues().entrySet()) { + syncLocalizedStrings( + importedEntity.getDescription(), + existingEntity.getDescription() + ); + } + } + + if (!Objects.equals( + existingEntity.getVersion(), + importedEntity.getVersion() + )) { + existingEntity.setVersion(importedEntity.getVersion()); + } + + if (!Objects.equals( + existingEntity.getReleased(), + importedEntity.getReleased() + )) { + existingEntity.setReleased(importedEntity.getReleased()); + } + + if (!Objects.equals( + existingEntity.getOwners(), + importedEntity.getOwners() + )) { + final Set ownersToAdd = importedEntity + .getOwners() + .stream() + .filter(owner -> !existingEntity.getOwners().contains(owner)) + .collect(Collectors.toSet()); + final Set ownersToRemove = existingEntity + .getOwners() + .stream() + .filter(owner -> !importedEntity.getOwners().contains(owner)) + .collect(Collectors.toSet()); + + for (final DomainOwnership toRemove : ownersToRemove) { + domainManager.removeDomainOwner( + toRemove.getOwner(), + existingEntity + ); + } + + for (final DomainOwnership toAdd : ownersToAdd) { + domainManager.addDomainOwner(toAdd.getOwner(), + existingEntity, + toAdd.getContext() + ); + } + } + + domainRepository.save(existingEntity); + } + @Override protected Domain reloadEntity(final Domain entity) { return domainRepository diff --git a/ccm-core/src/main/java/org/libreccm/categorization/DomainOwnershipImExporter.java b/ccm-core/src/main/java/org/libreccm/categorization/DomainOwnershipImExporter.java index 07f9660a3..6f97b793a 100644 --- a/ccm-core/src/main/java/org/libreccm/categorization/DomainOwnershipImExporter.java +++ b/ccm-core/src/main/java/org/libreccm/categorization/DomainOwnershipImExporter.java @@ -19,12 +19,11 @@ package org.libreccm.categorization; import org.libreccm.imexport.AbstractEntityImExporter; -import org.libreccm.imexport.Exportable; import org.libreccm.imexport.Processes; import org.libreccm.web.CcmApplication; -import java.util.HashSet; import java.util.Objects; +import java.util.Optional; import java.util.Set; import javax.annotation.PostConstruct; @@ -32,7 +31,6 @@ import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.persistence.EntityManager; import javax.persistence.NoResultException; -import javax.transaction.Transactional; /** * Exporter/Importer for {@link DomainOwnership} entities. @@ -45,6 +43,12 @@ import javax.transaction.Transactional; public class DomainOwnershipImExporter extends AbstractEntityImExporter { + @Inject + private DomainManager domainManager; + + @Inject + private DomainOwnershipRepository domainOwnershipRepo; + @Inject private EntityManager entityManager; @@ -65,10 +69,38 @@ public class DomainOwnershipImExporter } @Override - @Transactional(Transactional.TxType.REQUIRED) - protected void saveImportedEntity(final DomainOwnership entity) { - entityManager.persist(entity); + protected Optional findExistingEntity(final String uuid) { + return domainOwnershipRepo.findByUuid(uuid); } + + @Override + protected void updateExistingEntity( + final DomainOwnership existingEntity, + final DomainOwnership importedEnity + ) { + if (!Objects.equals( + existingEntity.getContext(), + importedEnity.getContext() + )) { + existingEntity.setContext(importedEnity.getContext()); + } + + if (existingEntity.getOwnerOrder() != importedEnity.getOwnerOrder()) { + existingEntity.setOwnerOrder(importedEnity.getOwnerOrder()); + } + + if (existingEntity.getDomainOrder() != importedEnity.getDomainOrder()) { + existingEntity.setDomainOrder(importedEnity.getDomainOrder()); + } + + domainOwnershipRepo.save(existingEntity); + } + + @Override + protected void saveImportedEntity(final DomainOwnership entity) { + domainOwnershipRepo.save(entity); + } + @Override protected DomainOwnership reloadEntity(final DomainOwnership entity) { diff --git a/ccm-core/src/main/java/org/libreccm/core/ResourceTypeImExporter.java b/ccm-core/src/main/java/org/libreccm/core/ResourceTypeImExporter.java index cde6f980f..cb4e9acc9 100644 --- a/ccm-core/src/main/java/org/libreccm/core/ResourceTypeImExporter.java +++ b/ccm-core/src/main/java/org/libreccm/core/ResourceTypeImExporter.java @@ -19,12 +19,10 @@ package org.libreccm.core; import org.libreccm.imexport.AbstractEntityImExporter; -import org.libreccm.imexport.Exportable; import org.libreccm.imexport.Processes; -import java.util.Collections; import java.util.Objects; -import java.util.Set; +import java.util.Optional; import javax.annotation.PostConstruct; import javax.inject.Inject; @@ -51,11 +49,66 @@ public class ResourceTypeImExporter return ResourceType.class; } + @Override + protected Optional findExistingEntity(final String uuid) { + return repository.findByUuid(uuid); + } + @Override protected void saveImportedEntity(final ResourceType entity) { repository.save(entity); } + @Override + protected void updateExistingEntity( + final ResourceType existingEntity, + final ResourceType importedEntity + ) { + if (!Objects.equals( + existingEntity.getTitle(), + importedEntity.getTitle() + )) { + existingEntity.setTitle(importedEntity.getTitle()); + } + + if (!Objects.equals( + existingEntity.getDescription(), + importedEntity.getDescription() + )) { + syncLocalizedStrings( + importedEntity.getDescription(), + importedEntity.getDescription() + ); + } + + if (existingEntity.isWorkspaceApplication() != importedEntity + .isWorkspaceApplication()) { + existingEntity.setWorkspaceApplication( + importedEntity.isWorkspaceApplication() + ); + } + + if (existingEntity.isViewableAsFullPage() != importedEntity + .isViewableAsFullPage()) { + existingEntity.setViewableAsFullPage( + importedEntity.isViewableAsFullPage() + ); + } + + if (existingEntity.isViewableAsEmbedded() != importedEntity + .isViewableAsEmbedded()) { + existingEntity.setViewableAsEmbedded( + importedEntity.isViewableAsEmbedded() + ); + } + + if (existingEntity.isSingleton() != importedEntity.isSingleton()) { + existingEntity.setSingleton(importedEntity.isSingleton()); + } + + repository.save(importedEntity); + } + @Override protected ResourceType reloadEntity(final ResourceType entity) { return repository diff --git a/ccm-core/src/main/java/org/libreccm/imexport/AbstractEntityImExporter.java b/ccm-core/src/main/java/org/libreccm/imexport/AbstractEntityImExporter.java index a1d44d25c..3f4ac15af 100644 --- a/ccm-core/src/main/java/org/libreccm/imexport/AbstractEntityImExporter.java +++ b/ccm-core/src/main/java/org/libreccm/imexport/AbstractEntityImExporter.java @@ -22,11 +22,15 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.libreccm.l10n.LocalizedString; import java.io.IOException; import java.util.Collections; import java.util.HashSet; +import java.util.Locale; +import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import javax.annotation.PostConstruct; import javax.enterprise.context.RequestScoped; @@ -124,19 +128,93 @@ public abstract class AbstractEntityImExporter { this.requiredEntities.addAll(requiredEntities); } + /** + * Imports an entity from the provided {@code data} and either creates a new + * entity or updates an existing entity. + * + * This method is not intended to be overwritten by implementations + * of this abstract class.. Due to technical limitations of the + * Java language it is not possible to mark this method as final. + * + * @param data The entity data to import + * + * @return The imported entity. + * + * @throws ImportExpection If the import fails. + * + * @see #findExistingEntity(java.lang.String) + * @see #saveImportedEntity(org.libreccm.imexport.Exportable) + * @see #updateExistingEntity(org.libreccm.imexport.Exportable, + * org.libreccm.imexport.Exportable) + */ @Transactional(Transactional.TxType.REQUIRED) public T importEntity(final String data) throws ImportExpection { try { - final T entity = objectMapper.readValue(data, getEntityClass()); - saveImportedEntity(entity); - return entity; + final T importedEntity = objectMapper.readValue(data, + getEntityClass()); + final Optional existingEntityResult = findExistingEntity( + importedEntity.getUuid() + ); + if (existingEntityResult.isPresent()) { + final T existingEntity = existingEntityResult.get(); + updateExistingEntity(existingEntity, importedEntity); + return existingEntity; + } else { + saveImportedEntity(importedEntity); + return importedEntity; + } } catch (IOException ex) { throw new ImportExpection(ex); } } + /** + * Retrieve the existing instance of the entity identified by the provided + * UUID. + * + * This method is called by {@link #importEntity(java.lang.String)}. + * + * @param uuid The UUID of the imported entity. + * + * @return An {@link Optional} with the existing entity or an empty + * {@link Optional} if the entity does not yet exist in this + * instance of LibreCCM. + * + * @see #updateExistingEntity(org.libreccm.imexport.Exportable, + * org.libreccm.imexport.Exportable) + * @see #importEntity(java.lang.String) + */ + protected abstract Optional findExistingEntity(final String uuid); + + /** + * Save a newly created entity. + * + * This method is called by {@link #importEntity(java.lang.String)}. + * + * @param entity The entity to save. + * + * @see #importEntity(java.lang.String) + */ protected abstract void saveImportedEntity(T entity); + /** + * Updates an existing entity. An implementation of this method is expected + * to persist the updated entity. Only properties that differ between the + * imported entity and the existing entity should be updated to match the + * values of the imported entity. Related entities must not be updated by an + * implementation, expect for embedded entities. + * + * @param existingEntity The existing entity. + * @param withImportedEntity The imported entity. + * + * @see #importEntity(java.lang.String) + * @see #findExistingEntity(java.lang.String) + * + */ + protected abstract void updateExistingEntity( + T existingEntity, T withImportedEntity + ); + /** * Export an entity (as JSON). There should be no need to overwrite this * method. @@ -167,7 +245,8 @@ public abstract class AbstractEntityImExporter { /** * Reloads the entity to export. Entities become detacted for several - * reasons before they are passed to the null null null null null null null null {@link #exportEntity(org.libreccm.imexport.Exportable) method. The + * reasons before they are passed to the null null null null null null null + * null null null null null null {@link #exportEntity(org.libreccm.imexport.Exportable) method. The * implementation of this should reload the passed entity. * * @param entity The entity to reload. @@ -175,5 +254,33 @@ public abstract class AbstractEntityImExporter { * @return The reloaded entity */ protected abstract T reloadEntity(final T entity); + + + protected final void syncLocalizedStrings( + final LocalizedString source, + final LocalizedString target + ) { + final Set localesToAdd = source + .getAvailableLocales() + .stream() + .filter( + locale -> !target.getAvailableLocales().contains(locale) + ) + .collect(Collectors.toSet()); + + final Set localesToRemove = target + .getAvailableLocales() + .stream() + .filter(locale -> !source.getAvailableLocales().contains(locale)) + .collect(Collectors.toSet()); + + for(final Locale toRemove : localesToRemove) { + target.removeValue(toRemove); + } + + for(final Locale toAdd : localesToAdd) { + target.putValue(toAdd, source.getValue(toAdd)); + } + } } diff --git a/ccm-core/src/main/java/org/libreccm/imexport/ImportExport.java b/ccm-core/src/main/java/org/libreccm/imexport/ImportExport.java index 48fbba905..c380eaaa8 100644 --- a/ccm-core/src/main/java/org/libreccm/imexport/ImportExport.java +++ b/ccm-core/src/main/java/org/libreccm/imexport/ImportExport.java @@ -61,6 +61,7 @@ import javax.json.JsonObject; import javax.json.JsonObjectBuilder; import javax.json.JsonReader; import javax.json.JsonString; +import javax.json.JsonValue; import javax.json.JsonWriter; import javax.transaction.Transactional; @@ -205,7 +206,6 @@ public class ImportExport { * * @see CcmFilesConfiguration#dataPath */ - @Transactional(Transactional.TxType.REQUIRED) public void importEntities(final String importName) { final String importsPath = String.format("imports/%s", importName); @@ -242,15 +242,12 @@ public class ImportExport { .filter(node -> filterImporters(manifest, node)) .collect(Collectors.toList()); - importers - .stream() - .map(EntityImExporterTreeNode::getEntityImExporter) - .forEach( - imExporter -> importEntitiesOfType( - importName, - imExporter - ) + for(EntityImExporterTreeNode importer : importers) { + importEntitiesOfType( + importName, + importer.getEntityImExporter() ); + } } catch (DependencyException ex) { throw new UnexpectedErrorException(ex); } @@ -287,14 +284,14 @@ public class ImportExport { final JsonObject toc = tocReader.readObject(); final JsonArray files = toc.getJsonArray("files"); - files.forEach( - value -> importEntity( - importName, - type, - ((JsonString) value).getString(), + for(JsonValue value : files) { + importEntity( + importName, + type, + ((JsonString) value).getString(), entityImExporter - ) - ); + ); + } } catch (IOException | FileDoesNotExistException | FileAccessException diff --git a/ccm-core/src/main/java/org/libreccm/security/GroupImExporter.java b/ccm-core/src/main/java/org/libreccm/security/GroupImExporter.java index a06d3e328..bb40d7c39 100644 --- a/ccm-core/src/main/java/org/libreccm/security/GroupImExporter.java +++ b/ccm-core/src/main/java/org/libreccm/security/GroupImExporter.java @@ -19,18 +19,17 @@ package org.libreccm.security; import org.libreccm.imexport.AbstractEntityImExporter; -import org.libreccm.imexport.Exportable; import org.libreccm.imexport.Processes; -import java.util.Collections; import java.util.Objects; +import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import javax.annotation.PostConstruct; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.persistence.EntityManager; -import javax.transaction.Transactional; /** * Exporter/Importer for {@link Group}s. @@ -44,6 +43,9 @@ public class GroupImExporter extends AbstractEntityImExporter { @Inject private EntityManager entityManager; + @Inject + private GroupManager groupManager; + @Inject private GroupRepository groupRepository; @@ -59,12 +61,69 @@ public class GroupImExporter extends AbstractEntityImExporter { } @Override - @Transactional(Transactional.TxType.REQUIRED) + protected Optional findExistingEntity(final String uuid) { + return groupRepository.findByUuid(uuid); + } + + @Override protected void saveImportedEntity(final Group entity) { entity.setPartyId(0); entityManager.persist(entity); } + @Override + protected void updateExistingEntity( + final Group existingEntity, + final Group importedEntity + ) { + if (!Objects.equals( + existingEntity.getName(), + importedEntity.getName() + )) { + existingEntity.setName(importedEntity.getName()); + } + + if (!Objects.equals( + existingEntity.getMemberships(), + importedEntity.getMemberships() + )) { + final Set membershipsToRemove = existingEntity + .getMemberships() + .stream() + .filter( + membership -> !importedEntity.getMemberships().contains( + membership + ) + ) + .collect(Collectors.toSet()); + final Set membershipsToAdd = importedEntity + .getMemberships() + .stream() + .filter( + membership -> !existingEntity.getMemberships().contains( + membership + ) + ) + .collect(Collectors.toSet()); + + for (final GroupMembership membership : membershipsToRemove) { + groupManager.removeMemberFromGroup( + membership.getMember(), + existingEntity + ); + } + + for(final GroupMembership membership: membershipsToAdd) { + groupManager.addMemberToGroup( + membership.getMember(), + existingEntity + ); + } + } + + groupRepository.save(existingEntity); + } + @Override protected Group reloadEntity(final Group entity) { return groupRepository diff --git a/ccm-core/src/main/java/org/libreccm/security/GroupMembershipImExporter.java b/ccm-core/src/main/java/org/libreccm/security/GroupMembershipImExporter.java index 0a80d3f51..498b7a6c5 100644 --- a/ccm-core/src/main/java/org/libreccm/security/GroupMembershipImExporter.java +++ b/ccm-core/src/main/java/org/libreccm/security/GroupMembershipImExporter.java @@ -22,13 +22,13 @@ import org.libreccm.imexport.AbstractEntityImExporter; import org.libreccm.imexport.Processes; import java.util.Objects; +import java.util.Optional; import java.util.Set; import javax.annotation.PostConstruct; import javax.inject.Inject; import javax.persistence.EntityManager; import javax.persistence.NoResultException; -import javax.transaction.Transactional; /** * Exporter/Importer for {@link GroupMembership} entities. @@ -40,6 +40,9 @@ import javax.transaction.Transactional; public class GroupMembershipImExporter extends AbstractEntityImExporter { + @Inject + private GroupMembershipRepository groupMembershipRepo; + @Inject private EntityManager entityManager; @@ -60,12 +63,26 @@ public class GroupMembershipImExporter } @Override - @Transactional(Transactional.TxType.REQUIRED) + protected Optional findExistingEntity( + final String uuid + ) { + return groupMembershipRepo.findByUuid(uuid); + } + + @Override protected void saveImportedEntity(final GroupMembership entity) { entity.setMembershipId(0); entityManager.persist(entity); } + @Override + protected void updateExistingEntity( + final GroupMembership existingEntity, + final GroupMembership importedEntity + ) { + // No update, update is handled by GroupImporter + } + @Override protected GroupMembership reloadEntity(final GroupMembership entity) { try { diff --git a/ccm-core/src/main/java/org/libreccm/security/PermissionImExporter.java b/ccm-core/src/main/java/org/libreccm/security/PermissionImExporter.java index 318873378..3a2a6e09b 100644 --- a/ccm-core/src/main/java/org/libreccm/security/PermissionImExporter.java +++ b/ccm-core/src/main/java/org/libreccm/security/PermissionImExporter.java @@ -22,6 +22,7 @@ import org.libreccm.imexport.AbstractEntityImExporter; import org.libreccm.imexport.Processes; import java.util.Objects; +import java.util.Optional; import java.util.Set; import javax.annotation.PostConstruct; @@ -55,11 +56,65 @@ public class PermissionImExporter extends AbstractEntityImExporter { return Permission.class; } + @Override + protected Optional findExistingEntity(final String uuid) { + return permissionRepository.findByUuid(uuid); + } + @Override protected void saveImportedEntity(final Permission entity) { permissionRepository.save(entity); } + @Override + protected void updateExistingEntity( + final Permission existingEntity, + final Permission importedEntity + ) { + if (!Objects.equals( + existingEntity.getGrantedPrivilege(), + importedEntity.getGrantedPrivilege() + )) { + existingEntity.setGrantedPrivilege( + importedEntity.getGrantedPrivilege() + ); + } + + if (!Objects.equals( + existingEntity.getCreationUser(), + importedEntity.getCreationUser() + )) { + existingEntity.setCreationUser(importedEntity.getCreationUser()); + } + + if (!Objects.equals( + existingEntity.getCreationDate(), + importedEntity.getCreationDate() + )) { + existingEntity.setCreationDate(importedEntity.getCreationDate()); + } + + if (!Objects.equals( + existingEntity.getCreationIp(), + importedEntity.getCreationIp() + )) { + existingEntity.setCreationIp(importedEntity.getCreationIp()); + } + + if (existingEntity.isInherited() != importedEntity.isInherited()) { + existingEntity.setInherited(importedEntity.isInherited()); + } + + if (!Objects.equals( + existingEntity.getInheritedFrom(), + importedEntity.getInheritedFrom() + )) { + existingEntity.setInheritedFrom(importedEntity.getInheritedFrom()); + } + + permissionRepository.save(existingEntity); + } + @Override protected Permission reloadEntity(final Permission entity) { return permissionRepository diff --git a/ccm-core/src/main/java/org/libreccm/security/RoleImExporter.java b/ccm-core/src/main/java/org/libreccm/security/RoleImExporter.java index d999079f3..7d07618f6 100644 --- a/ccm-core/src/main/java/org/libreccm/security/RoleImExporter.java +++ b/ccm-core/src/main/java/org/libreccm/security/RoleImExporter.java @@ -20,21 +20,22 @@ package org.libreccm.security; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.libreccm.core.UnexpectedErrorException; import org.libreccm.imexport.AbstractEntityImExporter; import org.libreccm.imexport.ExportException; import org.libreccm.imexport.Exportable; import org.libreccm.imexport.Processes; +import org.libreccm.workflow.AssignableTaskManager; +import org.libreccm.workflow.TaskAssignment; -import java.util.Collections; import java.util.Objects; +import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import javax.annotation.PostConstruct; import javax.enterprise.context.Dependent; import javax.inject.Inject; import javax.persistence.EntityManager; -import javax.transaction.Transactional; /** * Exporter/Importer for {@link Role}s. @@ -46,14 +47,24 @@ import javax.transaction.Transactional; public class RoleImExporter extends AbstractEntityImExporter { private static final Logger LOGGER = LogManager.getLogger( - RoleImExporter.class); + RoleImExporter.class + ); @Inject private EntityManager entityManager; + @Inject + private RoleManager roleManager; + @Inject private RoleRepository roleRepository; + @Inject + private PermissionManager permissionManager; + + @Inject + private AssignableTaskManager taskManager; + @PostConstruct @Override protected void init() { @@ -65,7 +76,11 @@ public class RoleImExporter extends AbstractEntityImExporter { return Role.class; } - @Transactional(Transactional.TxType.REQUIRED) + @Override + protected Optional findExistingEntity(final String uuid) { + return roleRepository.findByUuid(uuid); + } + @Override public String exportEntity(final Exportable entity) throws ExportException { final Role role = roleRepository @@ -89,6 +104,155 @@ public class RoleImExporter extends AbstractEntityImExporter { roleRepository.save(entity); } + @Override + protected void updateExistingEntity( + final Role existingEntity, + final Role importedEntity + ) { + if (!Objects.equals( + existingEntity.getName(), + importedEntity.getName() + )) { + existingEntity.setName(importedEntity.getName()); + } + + if (!Objects.equals( + existingEntity.getDescription(), + importedEntity.getDescription() + )) { + syncLocalizedStrings( + importedEntity.getDescription(), + existingEntity.getDescription() + ); + } + + roleRepository.save(existingEntity); + + if (!Objects.equals( + existingEntity.getMemberships(), + importedEntity.getMemberships() + )) { + final Set membershipsToRemove = existingEntity + .getMemberships() + .stream() + .filter( + membership -> !importedEntity.getMemberships().contains( + membership + ) + ).collect(Collectors.toSet()); + final Set membershipsToAdd = importedEntity + .getMemberships() + .stream() + .filter( + membership -> !existingEntity.getMemberships().contains( + membership + ) + ) + .collect(Collectors.toSet()); + + for (final RoleMembership membership : membershipsToRemove) { + roleManager.removeRoleFromParty( + existingEntity, + membership.getMember() + ); + } + + for (final RoleMembership membership : membershipsToAdd) { + roleManager.assignRoleToParty( + existingEntity, + membership.getMember() + ); + } + } + + if (!Objects.equals( + existingEntity.getPermissions(), + importedEntity.getPermissions() + )) { + final Set permissionsToRemove = existingEntity + .getPermissions() + .stream() + .filter( + permission -> !importedEntity.getPermissions().contains( + permission + ) + ) + .collect(Collectors.toSet()); + + final Set permissionToAdd = importedEntity + .getPermissions() + .stream() + .filter( + permission -> !existingEntity.getPermissions().contains( + permission + ) + ).collect(Collectors.toSet()); + + for (final Permission permission : permissionsToRemove) { + if (permission.getObject() == null) { + permissionManager.revokePrivilege( + permission.getGrantedPrivilege(), + existingEntity + ); + } else { + permissionManager.revokePrivilege( + permission.getGrantedPrivilege(), + existingEntity, + permission.getObject() + ); + } + } + + for (final Permission permission : permissionToAdd) { + if (permission.getObject() == null) { + permissionManager.grantPrivilege( + permission.getGrantedPrivilege(), + existingEntity + ); + } else { + permissionManager.grantPrivilege( + permission.getGrantedPrivilege(), + existingEntity, + permission.getObject() + ); + } + } + } + + if (!Objects.equals( + existingEntity.getAssignedTasks(), + importedEntity.getAssignedTasks() + )) { + final Set assignmentsToRemove = existingEntity + .getAssignedTasks() + .stream() + .filter( + assignment -> !importedEntity.getAssignedTasks().contains( + assignment + ) + ) + .collect(Collectors.toSet()); + + final Set assignmentsToAdd = importedEntity + .getAssignedTasks() + .stream() + .filter( + assignment -> !existingEntity.getAssignedTasks().contains( + assignment + ) + ) + .collect(Collectors.toSet()); + + for(final TaskAssignment assignment : assignmentsToRemove) { + taskManager.retractTask(assignment.getTask(), existingEntity); + } + + for(final TaskAssignment assignment : assignmentsToAdd) { + taskManager.assignTask(assignment.getTask(), existingEntity); + } + } + } + @Override protected Role reloadEntity(final Role entity) { return roleRepository @@ -104,3 +268,4 @@ public class RoleImExporter extends AbstractEntityImExporter { } } + diff --git a/ccm-core/src/main/java/org/libreccm/security/RoleMembershipImExporter.java b/ccm-core/src/main/java/org/libreccm/security/RoleMembershipImExporter.java index 0d15cbfc0..3e890a039 100644 --- a/ccm-core/src/main/java/org/libreccm/security/RoleMembershipImExporter.java +++ b/ccm-core/src/main/java/org/libreccm/security/RoleMembershipImExporter.java @@ -19,18 +19,16 @@ package org.libreccm.security; import org.libreccm.imexport.AbstractEntityImExporter; -import org.libreccm.imexport.Exportable; import org.libreccm.imexport.Processes; -import java.util.HashSet; import java.util.Objects; +import java.util.Optional; import java.util.Set; import javax.annotation.PostConstruct; import javax.inject.Inject; import javax.persistence.EntityManager; import javax.persistence.NoResultException; -import javax.transaction.Transactional; /** * Exporter/Importer for {@link RoleMembership}s. @@ -43,6 +41,9 @@ public class RoleMembershipImExporter @Inject private EntityManager entityManager; + + @Inject + private RoleMembershipRepository membershipRepo; @PostConstruct @Override @@ -60,9 +61,13 @@ public class RoleMembershipImExporter public Class getEntityClass() { return RoleMembership.class; } + + @Override + protected Optional findExistingEntity(final String uuid) { + return membershipRepo.findByUuid(uuid); + } @Override - @Transactional(Transactional.TxType.REQUIRED) protected void saveImportedEntity(final RoleMembership entity) { if (entity.getMembershipId() == 0) { @@ -71,6 +76,14 @@ public class RoleMembershipImExporter entityManager.merge(entity); } } + + @Override + protected void updateExistingEntity( + final RoleMembership existingEntity, + final RoleMembership importedEntity + ) { + // Nothing + } @Override protected RoleMembership reloadEntity(final RoleMembership entity) { diff --git a/ccm-core/src/main/java/org/libreccm/security/UserImExporter.java b/ccm-core/src/main/java/org/libreccm/security/UserImExporter.java index f51b42e7e..faa7bd874 100644 --- a/ccm-core/src/main/java/org/libreccm/security/UserImExporter.java +++ b/ccm-core/src/main/java/org/libreccm/security/UserImExporter.java @@ -19,12 +19,10 @@ package org.libreccm.security; import org.libreccm.imexport.AbstractEntityImExporter; -import org.libreccm.imexport.Exportable; import org.libreccm.imexport.Processes; -import java.util.Collections; import java.util.Objects; -import java.util.Set; +import java.util.Optional; import javax.annotation.PostConstruct; import javax.enterprise.context.RequestScoped; @@ -58,14 +56,64 @@ public class UserImExporter extends AbstractEntityImExporter { return User.class; } + @Override + protected Optional findExistingEntity(final String uuid) { + return userRepository.findByUuid(uuid); + } + @Override @Transactional(Transactional.TxType.REQUIRED) protected void saveImportedEntity(final User entity) { // Reset partyId. entity.setPartyId(0); -// userRepository.save(entity); entityManager.persist(entity); } + + @Override + protected void updateExistingEntity( + final User existingEntity, + final User importedEntity + ) { + if (!Objects.equals( + existingEntity.getName(), + importedEntity.getName() + )) { + existingEntity.setName(importedEntity.getName()); + } + + if (!Objects.equals( + existingEntity.getGivenName(), + importedEntity.getGivenName() + )) { + existingEntity.setGivenName(importedEntity.getGivenName()); + } + + if (!Objects.equals( + existingEntity.getFamilyName(), + importedEntity.getFamilyName() + )) { + existingEntity.setFamilyName(importedEntity.getFamilyName()); + } + + if (!Objects.equals( + existingEntity.getEmailAddresses(), + importedEntity.getEmailAddresses() + )) { + existingEntity.setEmailAddresses(importedEntity.getEmailAddresses()); + } + + if (existingEntity.isBanned() != importedEntity.isBanned()) { + existingEntity.setBanned(importedEntity.isBanned()); + } + + if (existingEntity.isPasswordResetRequired() != importedEntity.isPasswordResetRequired()) { + existingEntity.setPasswordResetRequired( + importedEntity.isPasswordResetRequired() + ); + } + + userRepository.save(existingEntity); + } @Override protected User reloadEntity(final User entity) { diff --git a/ccm-core/src/main/java/org/libreccm/web/ApplicationImExporter.java b/ccm-core/src/main/java/org/libreccm/web/ApplicationImExporter.java index acd4ae54b..72d65ed40 100644 --- a/ccm-core/src/main/java/org/libreccm/web/ApplicationImExporter.java +++ b/ccm-core/src/main/java/org/libreccm/web/ApplicationImExporter.java @@ -19,17 +19,14 @@ package org.libreccm.web; import org.libreccm.imexport.AbstractEntityImExporter; -import org.libreccm.imexport.Exportable; import org.libreccm.imexport.Processes; -import java.util.Collections; import java.util.Objects; -import java.util.Set; +import java.util.Optional; import javax.annotation.PostConstruct; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; -import javax.transaction.Transactional; /** * Exporter/Importer for application instances. @@ -54,12 +51,69 @@ public class ApplicationImExporter public Class getEntityClass() { return CcmApplication.class; } + + @Override + protected Optional findExistingEntity( + final String uuid + ) { + return applicationRepository.findByUuid(uuid); + } @Override - @Transactional(Transactional.TxType.REQUIRED) protected void saveImportedEntity(final CcmApplication entity) { applicationRepository.save(entity); } + + @Override + protected void updateExistingEntity( + final CcmApplication existingEntity, + final CcmApplication importedEntity + ) { + if (!Objects.equals( + existingEntity.getTitle(), + importedEntity.getTitle() + )) { + syncLocalizedStrings( + importedEntity.getTitle(), + existingEntity.getTitle() + ); + } + + if (!Objects.equals( + existingEntity.getDescription(), + importedEntity.getDescription() + )) { + syncLocalizedStrings( + importedEntity.getDescription(), + existingEntity.getDescription() + ); + } + + if (!Objects.equals( + existingEntity.getCreated(), + importedEntity.getCreated() + )) { + existingEntity.setCreated(importedEntity.getCreated()); + } + + if(!Objects.equals( + existingEntity.getApplicationType(), + importedEntity.getApplicationType() + )) { + existingEntity.setApplicationType( + importedEntity.getApplicationType() + ); + } + + if (!Objects.equals( + existingEntity.getPrimaryUrl(), + importedEntity.getPrimaryUrl() + )) { + existingEntity.setPrimaryUrl(importedEntity.getPrimaryUrl()); + } + + applicationRepository.save(existingEntity); + } @Override protected CcmApplication reloadEntity(final CcmApplication entity) { diff --git a/ccm-core/src/main/java/org/libreccm/workflow/AssignableTaskImExporter.java b/ccm-core/src/main/java/org/libreccm/workflow/AssignableTaskImExporter.java index 0d6d0923f..e2a05ed54 100644 --- a/ccm-core/src/main/java/org/libreccm/workflow/AssignableTaskImExporter.java +++ b/ccm-core/src/main/java/org/libreccm/workflow/AssignableTaskImExporter.java @@ -19,16 +19,15 @@ package org.libreccm.workflow; import org.libreccm.imexport.AbstractEntityImExporter; -import org.libreccm.imexport.Exportable; import org.libreccm.imexport.Processes; import java.util.Objects; +import java.util.Optional; import java.util.Set; import javax.annotation.PostConstruct; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; -import javax.transaction.Transactional; /** * Exporter/Importer for {@link AssignableTask}s. @@ -44,6 +43,12 @@ public class AssignableTaskImExporter @Inject private AssignableTaskRepository assignableTaskRepository; + + @Inject + private TaskManager taskManager; + + @Inject + private AssignableTaskManager assignableTaskManager; @PostConstruct @Override @@ -60,12 +65,62 @@ public class AssignableTaskImExporter public Class getEntityClass() { return AssignableTask.class; } + + @Override + protected Optional findExistingEntity(final String uuid) { + return assignableTaskRepository.findByUuid(uuid); + } @Override - @Transactional(Transactional.TxType.REQUIRED) protected void saveImportedEntity(final AssignableTask entity) { assignableTaskRepository.save(entity); } + + @Override + protected void updateExistingEntity( + final AssignableTask existingEntity, + final AssignableTask importedEntity + ) { + if(!Objects.equals( + existingEntity.getLabel(), + importedEntity.getLabel() + )) { + syncLocalizedStrings( + importedEntity.getLabel(), + existingEntity.getLabel() + ); + } + + if(!Objects.equals( + existingEntity.getDescription(), + importedEntity.getDescription() + )) { + syncLocalizedStrings( + importedEntity.getDescription(), + existingEntity.getDescription() + ); + } + + if (existingEntity.isActive() != importedEntity.isActive()) { + existingEntity.setActive(importedEntity.isActive()); + } + + if (!Objects.equals( + existingEntity.getWorkflow(), + importedEntity.getWorkflow() + )) { + taskManager.removeTask( + existingEntity.getWorkflow(), + existingEntity + ); + taskManager.addTask( + importedEntity.getWorkflow(), + existingEntity + ); + + // ToDo + } + } @Override protected AssignableTask reloadEntity(final AssignableTask entity) { diff --git a/ccm-core/src/main/java/org/libreccm/workflow/WorkflowImExporter.java b/ccm-core/src/main/java/org/libreccm/workflow/WorkflowImExporter.java index 318938116..ad970b23e 100644 --- a/ccm-core/src/main/java/org/libreccm/workflow/WorkflowImExporter.java +++ b/ccm-core/src/main/java/org/libreccm/workflow/WorkflowImExporter.java @@ -19,17 +19,16 @@ package org.libreccm.workflow; import org.libreccm.imexport.AbstractEntityImExporter; -import org.libreccm.imexport.Exportable; import org.libreccm.imexport.Processes; -import java.util.Collections; import java.util.Objects; +import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import javax.annotation.PostConstruct; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; -import javax.transaction.Transactional; /** * Importer/Exporter for {@link Workflow}s. @@ -40,6 +39,9 @@ import javax.transaction.Transactional; @Processes(Workflow.class) public class WorkflowImExporter extends AbstractEntityImExporter { + @Inject + private TaskManager taskManager; + @Inject private WorkflowRepository workflowRepository; @@ -51,16 +53,103 @@ public class WorkflowImExporter extends AbstractEntityImExporter { @Override public Class getEntityClass() { - return Workflow.class; } + + @Override + protected Optional findExistingEntity(final String uuid) { + return workflowRepository.findByUuid(uuid); + } @Override - @Transactional(Transactional.TxType.REQUIRED) protected void saveImportedEntity(final Workflow entity) { workflowRepository.save(entity); } + @Override + protected void updateExistingEntity( + final Workflow existingEntity, + final Workflow importedEntity + ) { + if (existingEntity.isAbstractWorkflow() != importedEntity.isAbstractWorkflow()) { + existingEntity.setAbstractWorkflow( + importedEntity.isAbstractWorkflow() + ); + } + + if (!Objects.equals( + existingEntity.getTemplate(), + importedEntity.getTemplate() + )) { + existingEntity.setTemplate(importedEntity.getTemplate()); + } + + if (!Objects.equals( + existingEntity.getName(), + importedEntity.getName() + )) { + syncLocalizedStrings( + importedEntity.getName(), + existingEntity.getName() + ); + } + + if (!Objects.equals( + existingEntity.getDescription(), + importedEntity.getDescription() + )) { + syncLocalizedStrings( + importedEntity.getDescription(), + existingEntity.getDescription() + ); + } + + if (existingEntity.getState() != importedEntity.getState()) { + existingEntity.setState(importedEntity.getState()); + } + + if (existingEntity.isActive() != importedEntity.isActive()) { + existingEntity.setActive(importedEntity.isActive()); + } + + if (existingEntity.getTasksState() != importedEntity.getTasksState()) { + existingEntity.setTasksState(importedEntity.getTasksState()); + } + + if (!Objects.equals( + existingEntity.getObject(), + importedEntity.getObject() + )) { + existingEntity.setObject(importedEntity.getObject()); + } + + workflowRepository.save(importedEntity); + + if (!Objects.equals( + existingEntity.getTasks(), + importedEntity.getTasks() + )) { + final Set tasksToRemove = existingEntity + .getTasks() + .stream() + .filter(task -> !importedEntity.getTasks().contains(task)) + .collect(Collectors.toSet()); + final Set tasksToAdd = importedEntity + .getTasks() + .stream( ) + .filter(task -> !existingEntity.getTasks().contains(task)) + .collect(Collectors.toSet()); + + for(final Task task : tasksToRemove) { + taskManager.removeTask(existingEntity, task); + } + + for(final Task task : tasksToAdd) { + taskManager.addTask(existingEntity, task); + } + } + } + @Override protected Workflow reloadEntity(final Workflow entity) { return workflowRepository