From 6d48bcf7275172115821f1629cdb659c54a4350f Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Tue, 17 Jun 2025 11:23:37 +0200 Subject: [PATCH 01/20] Remove UriService and related components This commit removes the entire UriService, its related interfaces, controllers, configuration, tests, and associated functionality. The changes streamline the codebase by eliminating unused URI Node management logic and legacy features. --- .../logic/action/ActionDelegate.groovy | 175 +++++------ .../engine/startup/ImportHelper.groovy | 12 +- .../UriServiceConfiguration.java | 19 -- .../elastic/service/ElasticCaseService.java | 13 - .../interfaces/IElasticCaseService.java | 2 - .../petrinet/service/PetriNetService.java | 49 +-- .../engine/petrinet/service/UriService.java | 285 ------------------ .../service/interfaces/IPetriNetService.java | 9 - .../service/interfaces/IUriService.java | 104 ------- .../petrinet/web/PetriNetController.java | 4 +- .../engine/petrinet/web/UriController.java | 79 ----- .../web/responsebodies/UriNodeResource.java | 27 -- .../web/responsebodies/UriNodeResources.java | 19 -- .../engine/startup/runner/UriRunner.java | 24 -- .../domain/repositories/CaseRepository.java | 2 - .../workflow/service/WorkflowService.java | 9 - .../service/interfaces/IWorkflowService.java | 2 - .../engine-processes/preference_item.xml | 7 +- .../application/engine/TestHelper.groovy | 4 - .../engine/action/MenuItemApiTest.groovy | 30 +- .../service/PetriNetServiceTest.groovy | 17 -- .../petrinet/service/UriServiceTest.groovy | 170 ----------- .../workflow/WorkflowServiceTest.groovy | 1 - .../workflow/service/TaskServiceTest.java | 5 - .../objects/elastic/domain/ElasticCase.java | 6 - .../engine/objects/workflow/domain/Case.java | 3 - .../workflow/domain/menu/MenuItemBody.java | 18 +- .../domain/menu/MenuItemConstants.java | 6 +- .../spring/elastic/domain/ElasticCase.java | 5 - 29 files changed, 105 insertions(+), 1001 deletions(-) delete mode 100644 application-engine/src/main/java/com/netgrif/application/engine/configuration/UriServiceConfiguration.java delete mode 100644 application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/UriService.java delete mode 100644 application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IUriService.java delete mode 100644 application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/UriController.java delete mode 100644 application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/responsebodies/UriNodeResource.java delete mode 100644 application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/responsebodies/UriNodeResources.java delete mode 100644 application-engine/src/main/java/com/netgrif/application/engine/startup/runner/UriRunner.java delete mode 100644 application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/UriServiceTest.groovy diff --git a/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index d90dc30fedf..7bca0436693 100644 --- a/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -51,7 +51,6 @@ import com.netgrif.application.engine.objects.workflow.service.InitValueExpressi import com.netgrif.application.engine.pdf.generator.config.PdfResource import com.netgrif.application.engine.pdf.generator.service.interfaces.IPdfGenerator import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService -import com.netgrif.application.engine.petrinet.service.interfaces.IUriService import com.netgrif.application.engine.startup.ImportHelper import com.netgrif.application.engine.startup.runner.DefaultFiltersRunner import com.netgrif.application.engine.startup.runner.FilterRunner @@ -179,9 +178,6 @@ class ActionDelegate { @Autowired ExportConfiguration exportConfiguration - @Autowired - IUriService uriService - @Autowired IImpersonationService impersonationService @@ -1468,18 +1464,6 @@ class ActionDelegate { return this.dataService.getFile(useCase, task, field, forPreview) } - def getUri(String uri) { - return uriService.findByUri(uri) - } - - def createUri(String uri, UriContentType type) { - return uriService.getOrCreate(uri, type) - } - - def moveUri(String uri, String dest) { - return uriService.move(uri, dest) - } - /** * Action API case search function using Elasticsearch database * @param requests the CaseSearchRequest list @@ -1702,12 +1686,6 @@ class ActionDelegate { def icon = cl() as String filter.setIcon(icon) workflowService.save(filter) - }, - uri : { cl -> - filter = workflowService.findOne(filter.stringId) - def uri = cl() as String - filter.setUriNodeId(uriService.findByUri(uri).stringId) - workflowService.save(filter) }] } @@ -2108,11 +2086,10 @@ class ActionDelegate { throw new IllegalArgumentException("Menu item identifier $sanitizedIdentifier is not unique!") } - Case parentItemCase = getOrCreateFolderItem(body.uri) + Case parentItemCase = getOrCreateFolderItem(body.path) I18nString newName = body.menuName ?: (body.filter?.dataSet[FILTER_FIELD_I18N_FILTER_NAME].value as I18nString) Case menuItemCase = createCase(FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER, newName?.defaultValue) - menuItemCase.setUriNodeId(uriService.findByUri(body.uri).stringId) menuItemCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_ALLOWED_ROLES.attributeId].options = body.allowedRoles menuItemCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_BANNED_ROLES.attributeId].options = body.bannedRoles if (parentItemCase != null) { @@ -2120,8 +2097,7 @@ class ActionDelegate { } menuItemCase = workflowService.save(menuItemCase) Task newItemTask = taskService.findOne(menuItemCase.tasks.find { it.transition == MenuItemConstants.PREFERENCE_ITEM_FIELD_INIT_TRANS_ID.attributeId }.task) - String nodePath = createNodePath(body.uri, sanitizedIdentifier) - uriService.getOrCreate(nodePath, UriContentType.CASE) + String nodePath = createNodePath(body.path, sanitizedIdentifier) newItemTask = assignTask(newItemTask) setData(newItemTask, body.toDataSet(parentItemCase.stringId, nodePath)) @@ -2138,22 +2114,22 @@ class ActionDelegate { .toLowerCase() } - protected String createNodePath(String uri, String identifier) { - if (uri == uriService.getUriSeparator()) { - return uri + identifier + protected String createNodePath(String path, String identifier) { + if (path == MenuItemConstants.PATH_SEPARATOR.attributeId) { + return path + identifier } else { - return uri + uriService.getUriSeparator() + identifier + return path + MenuItemConstants.PATH_SEPARATOR.attributeId + identifier } } - protected Case getOrCreateFolderItem(String uri) { - UriNode node = uriService.getOrCreate(uri, UriContentType.CASE) - MenuItemBody body = new MenuItemBody(new I18nString(node.name), "folder") - return getOrCreateFolderRecursive(node, body) + protected Case getOrCreateFolderItem(String path) { + String pathName = path.substring(path.lastIndexOf(MenuItemConstants.PATH_SEPARATOR.attributeId) + 1); + MenuItemBody body = new MenuItemBody(new I18nString(pathNUame), "folder") + return getOrCreateFolderRecursive(path, body) } - protected Case getOrCreateFolderRecursive(UriNode node, MenuItemBody body, Case childFolderCase = null) { - Case folder = findFolderCase(node) + protected Case getOrCreateFolderRecursive(String path, MenuItemBody body, Case childFolderCase = null) { + Case folder = findFolderCase(path) if (folder != null) { if (childFolderCase != null) { folder = appendChildCaseIdAndSave(folder, childFolderCase.stringId) @@ -2163,7 +2139,6 @@ class ActionDelegate { } folder = createCase(FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER, body.menuName.toString()) - folder.setUriNodeId(node.parentId) if (childFolderCase != null) { folder = appendChildCaseIdAndSave(folder, childFolderCase.stringId) initializeParentId(childFolderCase, folder.stringId) @@ -2172,20 +2147,43 @@ class ActionDelegate { } Task newItemTask = taskService.findOne(folder.tasks.find { it.transition == MenuItemConstants.PREFERENCE_ITEM_FIELD_INIT_TRANS_ID.attributeId }.task) assignTask(newItemTask) - setData(newItemTask, body.toDataSet(null, node.path)) + setData(newItemTask, body.toDataSet(null, path)) finishTask(newItemTask) folder = workflowService.findOne(folder.stringId) - if (node.parentId != null) { - UriNode parentNode = uriService.findById(node.parentId) - body = new MenuItemBody(new I18nString(parentNode.name), "folder") - - getOrCreateFolderRecursive(parentNode, body, folder) + if (hasParent(path)) { + body = new MenuItemBody(new I18nString(nameFromPath(path)), "folder") + String parentPath = parentPath(path) + getOrCreateFolderRecursive(parentPath, body, folder) } return folder } + protected String nameFromPath(String path) { + if (path == null || path == MenuItemConstants.PATH_SEPARATOR.attributeId || path.length() == 0) { + return "" + } + if (path.lastIndexOf(MenuItemConstants.PATH_SEPARATOR.attributeId) == 0) { + return path.replace(MenuItemConstants.PATH_SEPARATOR.attributeId, "") + } + return path.substring(path.lastIndexOf(MenuItemConstants.PATH_SEPARATOR.attributeId)) + } + + protected String parentPath(String path) { + if (path == null || path == MenuItemConstants.PATH_SEPARATOR.attributeId || path.length() == 0 || path.lastIndexOf(MenuItemConstants.PATH_SEPARATOR.attributeId) == 0) { + return MenuItemConstants.PATH_SEPARATOR.attributeId + } + return path.substring(0, path.lastIndexOf(MenuItemConstants.PATH_SEPARATOR.attributeId)) + } + + protected boolean hasParent(String path) { + if (path == null || path == MenuItemConstants.PATH_SEPARATOR.attributeId || path.length() == 0) { + return false + } + return true + } + /** * Changes location of menu item. If non-existing location is provided, the new location is created and then the * item is moved. Cyclic destination path is forbidden (f.e. from "/my_node" to @@ -2207,8 +2205,7 @@ class ActionDelegate { casesToSave.add(oldParent) } - UriNode destNode = uriService.getOrCreate(destUri, UriContentType.CASE) - Case newParent = getOrCreateFolderItem(destNode.path) + Case newParent = getOrCreateFolderItem(destUri) if (newParent != null) { item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID.attributeId].value = [newParent.stringId] as ArrayList newParent = appendChildCaseId(newParent, item.stringId) @@ -2217,8 +2214,7 @@ class ActionDelegate { item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID.attributeId].value = null } - item.uriNodeId = destNode.stringId - item = resolveAndHandleNewNodePath(item, destNode.path) + item = resolveAndHandleNewNodePath(item, destUri) casesToSave.add(item) if (hasChildren(item)) { @@ -2256,14 +2252,11 @@ class ActionDelegate { } Case duplicated = createCase(FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER, newTitle.defaultValue) - duplicated.uriNodeId = originItem.uriNodeId duplicated.dataSet = originItem.dataSet duplicated.title = newTitle.defaultValue duplicated = workflowService.save(duplicated) - UriNode node = uriService.findById(originItem.uriNodeId) - String newNodePath = createNodePath(node.path, sanitizedIdentifier) - uriService.getOrCreate(newNodePath, UriContentType.CASE) + String newNodePath = createNodePath((String) originItem.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH].value, sanitizedIdentifier) Task newItemTask = taskService.findOne(duplicated.tasks.find { it.transition == MenuItemConstants.PREFERENCE_ITEM_FIELD_INIT_TRANS_ID.attributeId }.task) Map updatedDataSet = [ @@ -2315,9 +2308,7 @@ class ActionDelegate { List casesToSave = new ArrayList<>() for (child in children) { - UriNode parentNode = uriService.getOrCreate(parentFolder.getFieldValue(MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.attributeId) as String, UriContentType.CASE) - child.uriNodeId = parentNode.stringId - child = resolveAndHandleNewNodePath(child, parentNode.path) + child = resolveAndHandleNewNodePath(child, (String) parentFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH].value) casesToSave.add(child) casesToSave.addAll(updateNodeInChildrenFoldersRecursive(child)) @@ -2328,15 +2319,14 @@ class ActionDelegate { private Case resolveAndHandleNewNodePath(Case folderItem, String destUri) { String newNodePath = resolveNewNodePath(folderItem, destUri) - UriNode newNode = uriService.getOrCreate(newNodePath, UriContentType.CASE) - folderItem.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.attributeId].value = newNode.path + folderItem.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.attributeId].value = newNodePath return folderItem } private String resolveNewNodePath(Case folderItem, String destUri) { return destUri + - uriService.getUriSeparator() + + MenuItemConstants.PATH_SEPARATOR.attributeId + folderItem.getFieldValue(MenuItemConstants.PREFERENCE_ITEM_FIELD_IDENTIFIER.attributeId) as String } @@ -2380,8 +2370,8 @@ class ActionDelegate { return workflowService.save(childFolderCase) } - protected Case findFolderCase(UriNode node) { - return findCaseElastic("processIdentifier:$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER AND dataSet.nodePath.textValue.keyword:\"$node.path\"") + protected Case findFolderCase(String path) { + return findCaseElastic("processIdentifier:$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER AND dataSet.nodePath.textValue.keyword:\"$path\"") } /** @@ -2423,9 +2413,8 @@ class ActionDelegate { * @param name * @return */ - Case findMenuItem(String uri, String name) { - UriNode uriNode = uriService.findByUri(uri) - return findCaseElastic("processIdentifier:\"$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER\" AND title.keyword:\"$name\" AND uriNodeId:\"$uriNode.stringId\"") + Case findMenuItem(String path, String name) { + return findCaseElastic("processIdentifier:\"$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER\" AND title.keyword:\"$name\" AND dataSet.nodePath.fulltextValue.keyword:\"$path\"") } Case findMenuItemByUriAndIdentifier(String uri, String identifier) { @@ -2590,8 +2579,6 @@ class ActionDelegate { Case menuItem = findMenuItem(sanitize(id)) if (!menuItem) { Case filter = createFilter(title, query, type, allowedNets, icon, DefaultFiltersRunner.FILTER_VISIBILITY_PRIVATE, null) - createUri(uri, UriContentType.DEFAULT) - return createMenuItem(uri, id, title, icon, filter, roles, bannedRoles) } else { Case filter = getFilterFromMenuItem(menuItem) @@ -2803,67 +2790,57 @@ class ActionDelegate { return "${publicViewUrl}/${Base64.getEncoder().encodeToString(identifier.bytes)}" as String } - void updateMultichoiceWithCurrentNode(MultichoiceMapField field, UriNode node) { - List splitPathList = splitUriPath(node.path) + void updateMultichoiceWithCurrentNode(MultichoiceMapField field, String path) { + List splitPathList = splitUriPath(path) - change field options { findOptionsBasedOnSelectedNode(node, splitPathList) } + change field options { findOptionsBasedOnSelectedNode(path, splitPathList) } change field value { splitPathList } } - List splitUriPath(String uri) { - String rootUri = uriService.getUriSeparator() - String[] splitPath = uri.split(uriService.getUriSeparator()) - if (splitPath.length == 0 && uri == rootUri) { - splitPath = [rootUri] + List splitUriPath(String path) { + String rootPath = MenuItemConstants.PATH_SEPARATOR.attributeId + String[] splitPath = path.split(MenuItemConstants.PATH_SEPARATOR.attributeId) + if (splitPath.length == 0 && path == rootPath) { + splitPath = [rootPath] } else if (splitPath.length == 0) { - throw new IllegalArgumentException("Wrong uri value: \"${uri}\"") + throw new IllegalArgumentException("Wrong path value: \"${path}\"") } else { - splitPath[0] = rootUri + splitPath[0] = rootPath } return splitPath as ArrayList } - Map findOptionsBasedOnSelectedNode(UriNode node) { - return findOptionsBasedOnSelectedNode(node, splitUriPath(node.path)) + Map findOptionsBasedOnSelectedNode(String path) { + return findOptionsBasedOnSelectedNode(path, splitUriPath(path)) } - Map findOptionsBasedOnSelectedNode(UriNode node, List splitPathList) { + Map findOptionsBasedOnSelectedNode(String path, List splitPathList) { Map options = new HashMap<>() options.putAll(splitPathList.collectEntries { [(it): new I18nString(it)] }) - Set childrenIds = node.getChildrenId() + Case caseByPath = findCaseElastic("processIdentifier:$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER AND dataSet.nodePath.textValue.keyword:\"$path\"") + Set childrenIds = caseByPath.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.attributeId].value as Set if (!childrenIds.isEmpty()) { for (String id : childrenIds) { - UriNode childNode = uriService.findById(id) - options.put(childNode.name, new I18nString(childNode.name)) + Case childFolderCase = workflowService.findOne(id) + options.put(childFolderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_NAME.attributeId].value as String, new I18nString(childFolderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_NAME.attributeId].value as String)) } } return options } - String getCorrectedUri(String uncheckedUri) { - String rootUri = uriService.getUriSeparator() - if (uncheckedUri == "") { - return rootUri + String getCorrectedUri(String uncheckedPath) { + String rootPath = MenuItemConstants.PATH_SEPARATOR.attributeId + if (uncheckedPath == "") { + return rootPath } - - UriNode node = uriService.findByUri(uncheckedUri) - - while (node == null) { - int lastIdx = uncheckedUri.lastIndexOf(uriService.getUriSeparator()) - if (lastIdx == -1) { - return rootUri - } - uncheckedUri = uncheckedUri.substring(0, uncheckedUri.lastIndexOf(uriService.getUriSeparator())) - if (uncheckedUri == "") { - return rootUri - } - node = uriService.findByUri(uncheckedUri) + int lastIdx = uncheckedPath.lastIndexOf(MenuItemConstants.PATH_SEPARATOR.attributeId) + if (lastIdx == -1) { + return rootPath } - - return node.path + return uncheckedPath } Field getFieldOfTask(String taskId, String fieldId) { diff --git a/application-engine/src/main/groovy/com/netgrif/application/engine/startup/ImportHelper.groovy b/application-engine/src/main/groovy/com/netgrif/application/engine/startup/ImportHelper.groovy index e059f5b5bb2..5e45678abb4 100644 --- a/application-engine/src/main/groovy/com/netgrif/application/engine/startup/ImportHelper.groovy +++ b/application-engine/src/main/groovy/com/netgrif/application/engine/startup/ImportHelper.groovy @@ -5,7 +5,6 @@ import com.fasterxml.jackson.databind.node.ObjectNode import com.netgrif.application.engine.adapter.spring.petrinet.service.ProcessRoleService import com.netgrif.application.engine.petrinet.domain.repositories.PetriNetRepository import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService -import com.netgrif.application.engine.petrinet.service.interfaces.IUriService import com.netgrif.application.engine.startup.runner.SuperCreatorRunner import com.netgrif.application.engine.workflow.domain.repositories.CaseRepository import com.netgrif.application.engine.workflow.service.interfaces.IDataService @@ -91,9 +90,6 @@ class ImportHelper { @Autowired private ProcessRoleService processRoleService - @Autowired - private IUriService uriService - private final ClassLoader loader = ImportHelper.getClassLoader() @@ -113,13 +109,13 @@ class ImportHelper { return authorityService.getOrCreate(name) } - Optional createNet(String fileName, String release, LoggedUser author = userService.transformToLoggedUser(userService.getSystem()), String uriNodeId = uriService.getDefault().stringId) { - return createNet(fileName, VersionType.valueOf(release.trim().toUpperCase()), author, uriNodeId) + Optional createNet(String fileName, String release, LoggedUser author = userService.transformToLoggedUser(userService.getSystem())) { + return createNet(fileName, VersionType.valueOf(release.trim().toUpperCase()), author) } - Optional createNet(String fileName, VersionType release = VersionType.MAJOR, LoggedUser author = userService.transformToLoggedUser(userService.getSystem()), String uriNodeId = uriService.getDefault().stringId) { + Optional createNet(String fileName, VersionType release = VersionType.MAJOR, LoggedUser author = userService.transformToLoggedUser(userService.getSystem())) { InputStream netStream = new ClassPathResource("petriNets/$fileName" as String).inputStream - PetriNet petriNet = petriNetService.importPetriNet(netStream, release, author, uriNodeId).getNet() + PetriNet petriNet = petriNetService.importPetriNet(netStream, release, author).getNet() log.info("Imported '${petriNet?.title?.defaultValue}' ['${petriNet?.identifier}', ${petriNet?.stringId}]") return Optional.of(petriNet) } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/configuration/UriServiceConfiguration.java b/application-engine/src/main/java/com/netgrif/application/engine/configuration/UriServiceConfiguration.java deleted file mode 100644 index 663bef1c32e..00000000000 --- a/application-engine/src/main/java/com/netgrif/application/engine/configuration/UriServiceConfiguration.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.netgrif.application.engine.configuration; - -import com.netgrif.application.engine.configuration.properties.UriProperties; -import com.netgrif.application.engine.petrinet.domain.repository.UriNodeRepository; -import com.netgrif.application.engine.petrinet.service.UriService; -import com.netgrif.application.engine.petrinet.service.interfaces.IUriService; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class UriServiceConfiguration { - - @Bean - @ConditionalOnMissingBean - public IUriService uriService(UriNodeRepository uriNodeRepository, UriProperties uriProperties) { - return new UriService(uriNodeRepository, uriProperties); - } -} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseService.java b/application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseService.java index 3fd66f168c5..62c91cd1874 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseService.java @@ -164,19 +164,6 @@ public long count(List requests, LoggedUser user, Locale loca } } - public String findUriNodeId(Case aCase) { - if (aCase == null) { - return null; - } - ElasticCase elasticCase = repository.findByStringId(aCase.getStringId()); - if (elasticCase == null) { - log.warn("[" + aCase.getStringId() + "] Case with id [" + aCase.getStringId() + "] is not indexed."); - return null; - } - - return elasticCase.getUriNodeId(); - } - protected NativeQuery buildQuery(List requests, LoggedUser user, Pageable pageable, Locale locale, Boolean isIntersection) { List singleQueries = requests.stream().map(request -> buildSingleQuery(request, user, locale)).collect(Collectors.toList()); diff --git a/application-engine/src/main/java/com/netgrif/application/engine/elastic/service/interfaces/IElasticCaseService.java b/application-engine/src/main/java/com/netgrif/application/engine/elastic/service/interfaces/IElasticCaseService.java index 4d4c11df2b9..8750dff1391 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/elastic/service/interfaces/IElasticCaseService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/elastic/service/interfaces/IElasticCaseService.java @@ -25,6 +25,4 @@ public interface IElasticCaseService { void remove(String caseId); void removeByPetriNetId(String processId); - - String findUriNodeId(Case aCase); } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java index e6f3702ce53..5f445da963b 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java @@ -30,7 +30,6 @@ import com.netgrif.application.engine.objects.petrinet.domain.throwable.MissingPetriNetMetaDataException; import com.netgrif.application.engine.objects.petrinet.domain.version.Version; import com.netgrif.application.engine.adapter.spring.petrinet.service.ProcessRoleService; -import com.netgrif.application.engine.petrinet.service.interfaces.IUriService; import com.netgrif.application.engine.petrinet.web.responsebodies.*; import com.netgrif.application.engine.objects.workflow.domain.Case; import com.netgrif.application.engine.workflow.domain.FileStorageConfiguration; @@ -128,9 +127,6 @@ public class PetriNetService implements IPetriNetService { @Autowired protected IElasticPetriNetMappingService petriNetMappingService; - @Autowired - protected IUriService uriService; - protected ApplicationEventPublisher publisher; protected IElasticPetriNetService elasticPetriNetService; @@ -184,36 +180,19 @@ public List get(List petriNetIds) { return self.get(petriNetIds.stream().map(ObjectId::new).collect(Collectors.toList())); } - @Override @Deprecated public ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, String releaseType, LoggedUser author) throws IOException, MissingPetriNetMetaDataException, MissingIconKeyException { - return importPetriNet(xmlFile, VersionType.valueOf(releaseType.trim().toUpperCase()), author, uriService.getDefault().getStringId()); - } - - @Override - @Deprecated - public ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, String releaseType, LoggedUser author, String uriNodeId) throws IOException, MissingPetriNetMetaDataException, MissingIconKeyException { - return importPetriNet(xmlFile, VersionType.valueOf(releaseType.trim().toUpperCase()), author, uriNodeId); + return importPetriNet(xmlFile, VersionType.valueOf(releaseType.trim().toUpperCase()), author); } @Override public ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, VersionType releaseType, LoggedUser author) throws IOException, MissingPetriNetMetaDataException, MissingIconKeyException { - return importPetriNet(xmlFile, releaseType, author, uriService.getDefault().getStringId()); + return importPetriNet(xmlFile, releaseType, author, new HashMap<>()); } @Override public ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, VersionType releaseType, LoggedUser author, Map params) throws IOException, MissingPetriNetMetaDataException, MissingIconKeyException { - return importPetriNet(xmlFile, releaseType, author, uriService.getDefault().getStringId(), params); - } - - @Override - public ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, VersionType releaseType, LoggedUser author, String uriNodeId) throws IOException, MissingPetriNetMetaDataException, MissingIconKeyException { - return importPetriNet(xmlFile, releaseType, author, uriNodeId, new HashMap<>()); - } - - @Override - public ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, VersionType releaseType, LoggedUser author, String uriNodeId, Map params) throws IOException, MissingPetriNetMetaDataException, MissingIconKeyException { ImportPetriNetEventOutcome outcome = new ImportPetriNetEventOutcome(); ByteArrayOutputStream xmlCopy = new ByteArrayOutputStream(); IOUtils.copy(xmlFile, xmlCopy); @@ -222,7 +201,6 @@ public ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, VersionTyp return outcome; } PetriNet net = imported.get(); - net.setUriNodeId(uriNodeId); PetriNet existingNet = getNewestVersionByIdentifier(net.getIdentifier()); if (existingNet != null) { @@ -245,18 +223,6 @@ public ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, VersionTyp return outcome; } - private ImportPetriNetEventOutcome addMessageToOutcome(PetriNet net, ProcessEventType type, ImportPetriNetEventOutcome outcome) { - if (net.getProcessEvents().containsKey(type)) { - outcome.setMessage(net.getProcessEvents().get(type).getMessage()); - } - return outcome; - } - - protected void evaluateRules(Event event) { - publisher.publishEvent(event); - - } - @Override public Optional save(PetriNet petriNet) { petriNet.initializeArcs(); @@ -301,16 +267,9 @@ public List getByIdentifier(String identifier) { return nets; } - @Override - public List findAllByUriNodeId(String uriNodeId) { - List nets = elasticPetriNetService.findAllByUriNodeId(uriNodeId); - nets.forEach(PetriNet::initializeArcs); - return nets; - } - @Override public List findAllById(List ids) { - return StreamSupport.stream(repository.findAllById(ids).spliterator(), false).collect(Collectors.toList()); + return new ArrayList<>(repository.findAllById(ids)); } @Override @@ -320,7 +279,7 @@ public PetriNet getNewestVersionByIdentifier(String identifier) { if (nets.isEmpty()) { return null; } - return nets.get(0); + return nets.getFirst(); } /** diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/UriService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/UriService.java deleted file mode 100644 index 9076c410187..00000000000 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/UriService.java +++ /dev/null @@ -1,285 +0,0 @@ -package com.netgrif.application.engine.petrinet.service; - -import com.netgrif.application.engine.configuration.properties.UriProperties; -import com.netgrif.application.engine.objects.petrinet.domain.UriContentType; -import com.netgrif.application.engine.objects.petrinet.domain.UriNode; -import com.netgrif.application.engine.petrinet.domain.repository.UriNodeRepository; -import com.netgrif.application.engine.petrinet.service.interfaces.IUriService; -import lombok.Getter; -import org.apache.commons.lang3.StringUtils; - -import java.util.*; -import java.util.stream.Collectors; - -/** - * Service for managing UriNode objects - */ -//@Service -public class UriService implements IUriService { - - private static final int FIRST_LEVEL = 0; - - @Getter - private final UriNodeRepository uriNodeRepository; - - @Getter - private final UriProperties uriProperties; - - public UriService(UriNodeRepository uriNodeRepository, UriProperties uriProperties) { - this.uriNodeRepository = uriNodeRepository; - this.uriProperties = uriProperties; - } - - - /** - * Saves UriNode object to database - * - * @param uriNode to be saved - */ - @Override - public UriNode save(UriNode uriNode) { - return uriNodeRepository.save((com.netgrif.application.engine.adapter.spring.petrinet.domain.UriNode) uriNode); - } - - /** - * Retrieves all UriNode based on parent ID - * - * @param parentId ID of parent UriNode - * @return list of UriNode - */ - @Override - public List findAllByParent(String parentId) { - return uriNodeRepository.findAllByParentId(parentId).stream().map(UriNode.class::cast).toList(); - } - - /** - * Retrieves all UriNode that are root nodes - * - * @return list of UriNode - */ - @Override - public UriNode getRoot() { - List nodes = uriNodeRepository.findAllByLevel(FIRST_LEVEL).stream().map(UriNode.class::cast).toList(); - if (nodes.size() != 1) { - throw new IllegalStateException("Exactly one root uri node must exist!"); - } - return nodes.getFirst(); - } - - /** - * Retrieves all default uri node for app - * - * @return default uriNode - */ - @Override - public UriNode getDefault() { - return uriNodeRepository.findByPath(uriProperties.getSeparator() + uriProperties.getName()); - } - - /** - * Retrieves all UriNode based on level - * - * @param level of UriNodes - * @return list of UriNodes - */ - @Override - public List findByLevel(int level) { - return uriNodeRepository.findAllByLevel(level).stream().map(UriNode.class::cast).collect(Collectors.toList()); - } - - /** - * Retrieves UriNode based on ID - * - * @param id ID of UriNode - * @return UriNode - */ - @Override - public UriNode findById(String id) { - Optional navNodeOptional = uriNodeRepository.findById(id); - if (navNodeOptional.isEmpty()) { - throw new IllegalArgumentException("Could not find NavNode with id [" + id + "]"); - } - return navNodeOptional.get(); - } - - /** - * Retrieves UriNode based on uri - * - * @param uri ID of UriNode - * @return UriNode - */ - @Override - public UriNode findByUri(String uri) { - return uriNodeRepository.findByPath(uri); - } - - /** - * Collects direct relatives (parent and children) of input UriNode and returns filled object - * - * @param uriNode to be filled with relatives - * @return filled UriNode - */ - @Override - public UriNode populateDirectRelatives(UriNode uriNode) { - if (uriNode == null) { - return null; - } - if (uriNode.getLevel() != FIRST_LEVEL) { - UriNode parent = findById(uriNode.getParentId()); - uriNode.setParent(parent); - } - Set children = new HashSet<>(uriNodeRepository.findAllById(uriNode.getChildrenId())); - uriNode.setChildren(children); - return uriNode; - } - - /** - * Moves UriNode to other destination - * - * @param uri to be moved - * @param destUri the destination URI - * @return result UriNode object - */ - @Override - public UriNode move(String uri, String destUri) { - UriNode uriNode = findByUri(uri); - return move(uriNode, destUri); - } - - /** - * Moves UriNode to other destination - * - * @param node to be moved - * @param destUri the destination URI - * @return result UriNode object - */ - @Override - public UriNode move(UriNode node, String destUri) { - if (isPathCycle(node.getPath(), destUri)) { - throw new IllegalArgumentException("Uri node with path " + node.getPath() + " cannot be moved to path " + destUri + " due to cyclic paths"); - } - - UriNode newParent = getOrCreate(destUri, null); - UriNode oldParent = findById(node.getParentId()); - - if (destUri.indexOf(uriProperties.getSeparator()) != 0) { - destUri = uriProperties.getSeparator() + destUri; - } - String oldNodePath = node.getPath(); - String newNodePath = destUri + (destUri.equals(uriProperties.getSeparator()) ? "" : uriProperties.getSeparator()) + node.getName(); - node.setPath(newNodePath); - node.setParentId(newParent.getStringId()); - node.setLevel(newParent.getLevel() + 1); - - oldParent.getChildrenId().remove(oldNodePath); - newParent.getChildrenId().add(newNodePath); - uriNodeRepository.saveAll(List.of((com.netgrif.application.engine.adapter.spring.petrinet.domain.UriNode) oldParent, (com.netgrif.application.engine.adapter.spring.petrinet.domain.UriNode) newParent, (com.netgrif.application.engine.adapter.spring.petrinet.domain.UriNode) node)); - - List childrenToSave = new ArrayList<>(); - if (!node.getChildrenId().isEmpty()) { - node = populateDirectRelatives(node); - childrenToSave.addAll(moveChildrenRecursive(oldNodePath, newNodePath, node.getChildren())); - } - - uriNodeRepository.saveAll(List.of((com.netgrif.application.engine.adapter.spring.petrinet.domain.UriNode) oldParent, (com.netgrif.application.engine.adapter.spring.petrinet.domain.UriNode) newParent, (com.netgrif.application.engine.adapter.spring.petrinet.domain.UriNode) node)); - uriNodeRepository.saveAll(childrenToSave); - return node; - } - - private boolean isPathCycle(String picked, String dest) { - return dest.startsWith(picked); - } - - private List moveChildrenRecursive(String oldParentPath, String newParentPath, Set nodes) { - List updated = new ArrayList<>(); - - if (nodes == null || nodes.isEmpty()) { - return new ArrayList<>(); - } - - for (UriNode node : nodes) { - String oldPath = node.getPath(); - String diff = calcPathDifference(oldPath, oldParentPath); - String newPath = newParentPath + diff; - node.setPath(newPath); - node.setParentId(newParentPath); - updated.add((com.netgrif.application.engine.adapter.spring.petrinet.domain.UriNode) node); - - node = populateDirectRelatives(node); - - updated.addAll(moveChildrenRecursive(oldPath, newPath, node.getChildren())); - } - - return updated; - } - - private String calcPathDifference(String path1, String path2) { - return StringUtils.difference(path2, path1); - } - - /** - * Creates new UriNode from URI path, or retrieves existing one - * - * @param uri to be used for creating UriNode - * @param contentType to decide the content type of UriNode - * @return the UriNode that was created or modified /netgrif/process/test/all_data, /netgrif/process - */ - @Override - public UriNode getOrCreate(String uri, UriContentType contentType) { - if (!uri.startsWith(uriProperties.getSeparator())) { - uri = uriProperties.getSeparator() + uri; - } - - LinkedList uriNodeList = new LinkedList<>(); - String[] uriComponents = uri.split(uriProperties.getSeparator()); - if (uriComponents.length == 0) { - uriComponents = new String[]{uriProperties.getSeparator()}; - } else { - uriComponents[0] = uriProperties.getSeparator(); - } - StringBuilder uriBuilder = new StringBuilder(); - int pathLength = uriComponents.length; - UriNode parent = null; - - for (int i = 0; i < pathLength; i++) { - uriBuilder.append(uriComponents[i]); - UriNode uriNode = findByUri(uriBuilder.toString()); - if (uriNode == null) { - uriNode = new com.netgrif.application.engine.adapter.spring.petrinet.domain.UriNode(); - uriNode.setName(uriComponents[i]); - uriNode.setLevel(i); - uriNode.setPath(uriBuilder.toString()); - uriNode.setParentId(parent != null ? parent.getStringId() : null); - } - if (i == pathLength - 1 && contentType != null) { - uriNode.addContentType(contentType); - } - uriNode = uriNodeRepository.save((com.netgrif.application.engine.adapter.spring.petrinet.domain.UriNode) uriNode); - if (parent != null) { - parent.getChildrenId().add(uriNode.getStringId()); - uriNodeRepository.save((com.netgrif.application.engine.adapter.spring.petrinet.domain.UriNode) parent); - } - if (i > 0) { - uriBuilder.append(uriProperties.getSeparator()); - } - uriNodeList.add(uriNode); - parent = uriNode; - } - return uriNodeList.getLast(); - } - - /** - * Creates default UriNode - * - * @return the UriNode that was created or modified - */ - @Override - public UriNode createDefault() { - return getOrCreate(uriProperties.getSeparator() + uriProperties.getName(), UriContentType.DEFAULT); - } - - @Override - public String getUriSeparator() { - return uriProperties.getSeparator(); - } -} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java index c11ff71c84b..606cce75c58 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java @@ -44,17 +44,10 @@ static DataFieldReference transformToReference(PetriNet net, Transition transiti @Deprecated ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, String releaseType, LoggedUser user) throws IOException, MissingPetriNetMetaDataException, MissingIconKeyException; - @Deprecated - ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, String releaseType, LoggedUser user, String uriNodeId) throws IOException, MissingPetriNetMetaDataException, MissingIconKeyException; - ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, VersionType releaseType, LoggedUser user) throws IOException, MissingPetriNetMetaDataException, MissingIconKeyException; ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, VersionType releaseType, LoggedUser user, Map params) throws IOException, MissingPetriNetMetaDataException, MissingIconKeyException; - ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, VersionType releaseType, LoggedUser user, String uriNodeId) throws IOException, MissingPetriNetMetaDataException, MissingIconKeyException; - - ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, VersionType releaseType, LoggedUser user, String uriNodeId, Map params) throws IOException, MissingPetriNetMetaDataException, MissingIconKeyException; - Optional save(PetriNet petriNet); PetriNet getPetriNet(String id); @@ -63,8 +56,6 @@ static DataFieldReference transformToReference(PetriNet net, Transition transiti List getByIdentifier(String identifier); - List findAllByUriNodeId(String uriNodeId); - List findAllById(List ids); PetriNet getNewestVersionByIdentifier(String identifier); diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IUriService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IUriService.java deleted file mode 100644 index ec8d97183c9..00000000000 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IUriService.java +++ /dev/null @@ -1,104 +0,0 @@ -package com.netgrif.application.engine.petrinet.service.interfaces; - -import com.netgrif.application.engine.objects.petrinet.domain.UriContentType; -import com.netgrif.application.engine.objects.petrinet.domain.UriNode; - -import java.util.List; - -public interface IUriService { - - /** - * Saves UriNode object to database - * - * @param uriNode to be saved - */ - UriNode save(UriNode uriNode); - - /** - * Retrieves all UriNode based on parent ID - * - * @param parentId ID of parent UriNode - * @return list of UriNode - */ - List findAllByParent(String parentId); - - /** - * Retrieves UriNode that is root node - * - * @return root UriNode - */ - UriNode getRoot(); - - UriNode getDefault(); - - /** - * Retrieves UriNode based on level - * - * @param level of UriNode - * @return UriNode - */ - List findByLevel(int level); - - /** - * Retrieves UriNode based on ID - * - * @param id ID of UriNode - * @return UriNode - */ - UriNode findById(String id); - - /** - * Retrieves UriNode based on uri - * - * @param path of UriNode - * @return UriNode - */ - UriNode findByUri(String path); - - /** - * Collects direct relatives (parent and children) of input UriNode and returns filled object - * - * @param uriNode to be filled with relatives - * @return filled UriNode - */ - UriNode populateDirectRelatives(UriNode uriNode); - - /** - * Moves UriNode to other destination - * - * @param uri to be moved - * @param destUri the destination URI - * @return result UriNode object - */ - UriNode move(String uri, String destUri); - - /** - * Moves UriNode to other destination - * - * @param node to be moved - * @param destUri the destination URI - * @return result UriNode object - */ - UriNode move(UriNode node, String destUri); - - /** - * Creates new UriNode from URI path, or retrieves existing one - * - * @param uri to be used for creating UriNode - * @param contentType to decide the content type of UriNode - * @return the UriNode that was created or modified - */ - UriNode getOrCreate(String uri, UriContentType contentType); - - /** - * Creates default UriNode - * - * @return the UriNode that was created or modified - */ - UriNode createDefault(); - - /** - * Returns configured uri separator - */ - String getUriSeparator(); -} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java index 46e23c8a33e..472ba909497 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java @@ -105,13 +105,11 @@ public static String decodeUrl(String s1) { @PostMapping(value = "/import", produces = MediaTypes.HAL_JSON_VALUE) public EntityModel importPetriNet( @RequestParam(value = "file", required = true) MultipartFile multipartFile, - @RequestParam(value = "uriNodeId", required = true) String uriNodeId, @RequestParam(value = "meta", required = false) String releaseType, Authentication auth, Locale locale) throws MissingPetriNetMetaDataException, MissingIconKeyException { try { - String decodedUriNodeId = new String(Base64.decodeBase64(uriNodeId)); VersionType release = releaseType == null ? VersionType.MAJOR : VersionType.valueOf(releaseType.trim().toUpperCase()); - ImportPetriNetEventOutcome importPetriNetOutcome = service.importPetriNet(multipartFile.getInputStream(), release, (LoggedUser) auth.getPrincipal(), decodedUriNodeId); + ImportPetriNetEventOutcome importPetriNetOutcome = service.importPetriNet(multipartFile.getInputStream(), release, (LoggedUser) auth.getPrincipal()); return EventOutcomeWithMessageResource.successMessage("Petri net " + multipartFile.getOriginalFilename() + " imported successfully", LocalisedEventOutcomeFactory.from(importPetriNetOutcome, locale)); } catch (IOException e) { diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/UriController.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/UriController.java deleted file mode 100644 index 07bd3de8e63..00000000000 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/UriController.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.netgrif.application.engine.petrinet.web; - -import com.netgrif.application.engine.objects.petrinet.domain.UriNode; -import com.netgrif.application.engine.petrinet.service.interfaces.IUriService; -import com.netgrif.application.engine.petrinet.web.responsebodies.UriNodeResource; -import com.netgrif.application.engine.petrinet.web.responsebodies.UriNodeResources; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.responses.ApiResponses; -import io.swagger.v3.oas.annotations.security.SecurityRequirement; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.hateoas.CollectionModel; -import org.springframework.hateoas.EntityModel; -import org.springframework.hateoas.MediaTypes; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import java.util.Base64; -import java.util.List; - -@RestController -@RequestMapping("/api/uri") -@Tag(name = "Process URI") -public class UriController { - - private final IUriService uriService; - - - public UriController(IUriService uriService) { - this.uriService = uriService; - } - - @Operation(summary = "Get root UriNodes", security = {@SecurityRequirement(name = "BasicAuth")}) - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "OK"), - }) - @GetMapping(value = "/root", produces = MediaTypes.HAL_JSON_VALUE) - public EntityModel getRoot() { - UriNode uriNode = uriService.getRoot(); - uriService.populateDirectRelatives(uriNode); - return new UriNodeResource(uriNode); - } - - @Operation(summary = "Get one UriNode by URI path", security = {@SecurityRequirement(name = "BasicAuth")}) - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "OK"), - }) - @GetMapping(value = "/{uri}", produces = MediaTypes.HAL_JSON_VALUE) - public EntityModel getOne(@PathVariable("uri") String uri) { - uri = new String(Base64.getDecoder().decode(uri)); - UriNode uriNode = uriService.findByUri(uri); - uriNode = uriService.populateDirectRelatives(uriNode); - return new UriNodeResource(uriNode); - } - - @Operation(summary = "Get UriNodes by parent id", security = {@SecurityRequirement(name = "BasicAuth")}) - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "OK"), - }) - @GetMapping(value = "/parent/{parentId}", produces = MediaTypes.HAL_JSON_VALUE) - public CollectionModel getByParent(@PathVariable("parentId") String parentId) { - List uriNodes = uriService.findAllByParent(parentId); - uriNodes.forEach(uriService::populateDirectRelatives); - return new UriNodeResources(uriNodes); - } - - @Operation(summary = "Get UriNodes by on the same level", security = {@SecurityRequirement(name = "BasicAuth")}) - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "OK"), - }) - @GetMapping(value = "/level/{level}", produces = MediaTypes.HAL_JSON_VALUE) - public CollectionModel getByLevel(@PathVariable("level") int level) { - List uriNodes = uriService.findByLevel(level); - uriNodes.forEach(uriService::populateDirectRelatives); - return new UriNodeResources(uriNodes); - } -} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/responsebodies/UriNodeResource.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/responsebodies/UriNodeResource.java deleted file mode 100644 index cdf6e685150..00000000000 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/responsebodies/UriNodeResource.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.netgrif.application.engine.petrinet.web.responsebodies; - -import com.netgrif.application.engine.objects.petrinet.domain.UriNode; -import com.netgrif.application.engine.petrinet.web.UriController; -import org.springframework.hateoas.EntityModel; -import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; - -public class UriNodeResource extends EntityModel { - - public UriNodeResource(UriNode content) { - super(content); - buildLinks(); - } - - private void buildLinks() { - UriNode content = getContent(); - if (content != null) { - add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder - .methodOn(UriController.class).getRoot()) - .withSelfRel()); - - add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder - .methodOn(UriController.class).getOne(content.getPath())) - .withSelfRel()); - } - } -} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/responsebodies/UriNodeResources.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/responsebodies/UriNodeResources.java deleted file mode 100644 index f6102fde6a2..00000000000 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/responsebodies/UriNodeResources.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.netgrif.application.engine.petrinet.web.responsebodies; - -import com.netgrif.application.engine.objects.petrinet.domain.UriNode; -import com.netgrif.application.engine.petrinet.web.UriController; -import org.springframework.hateoas.CollectionModel; -import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; - -public class UriNodeResources extends CollectionModel { - - public UriNodeResources(Iterable content) { - super(content); - buildLinks(); - } - - private void buildLinks() { - add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(UriController.class) - .getRoot()).withRel("root")); - } -} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/UriRunner.java b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/UriRunner.java deleted file mode 100644 index 40cf9c633e3..00000000000 --- a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/UriRunner.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.netgrif.application.engine.startup.runner; - -import com.netgrif.application.engine.petrinet.service.interfaces.IUriService; -import com.netgrif.application.engine.startup.ApplicationEngineStartupRunner; -import com.netgrif.application.engine.startup.annotation.RunnerOrder; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.ApplicationArguments; -import org.springframework.stereotype.Component; - -@Slf4j -@Component -@RunnerOrder(80) -@RequiredArgsConstructor -public class UriRunner implements ApplicationEngineStartupRunner { - - private final IUriService uriService; - - @Override - public void run(ApplicationArguments args) throws Exception { - uriService.createDefault(); - } - -} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/domain/repositories/CaseRepository.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/domain/repositories/CaseRepository.java index 875ba17451e..306bf0b83d4 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/domain/repositories/CaseRepository.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/domain/repositories/CaseRepository.java @@ -26,8 +26,6 @@ public interface CaseRepository extends MongoRepository, QuerydslP @Query("{ '_id.objectId': { $in: ?0 } }") List findAllByObjectIdsIn(List objectIds); - Page findAllByUriNodeId(String uri, Pageable pageable); - List findAllByPetriNetObjectId(ObjectId petriNetObjectId); void deleteAllByPetriNetObjectId(ObjectId petriNetObjectId); diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java index 9e533aac5ab..507d2ec96c4 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java @@ -194,14 +194,6 @@ public Page getAll(Pageable pageable) { return setImmediateDataFields(page); } - @Override - public Page findAllByUri(String uri, Pageable pageable) { - Page page = repository.findAllByUriNodeId(uri, pageable); - page.getContent().forEach(this::setPetriNet); - decryptDataSets(page.getContent()); - return setImmediateDataFields(page); - } - @Override public Page search(Predicate predicate, Pageable pageable) { Page page = repository.findAll(predicate, pageable); @@ -336,7 +328,6 @@ public CreateCaseEventOutcome createCase(String netId, Function ma useCase.setAuthor(loggedOrImpersonated.transformToAuthor()); useCase.setCreationDate(LocalDateTime.now()); useCase.setTitle(makeTitle.apply(useCase)); - useCase.setUriNodeId(petriNet.getUriNodeId()); CreateCaseEventOutcome outcome = new CreateCaseEventOutcome(); outcome.addOutcomes(eventService.runActions(petriNet.getPreCreateActions(), null, Optional.empty(), params)); diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IWorkflowService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IWorkflowService.java index 6b676a1b002..436a376bc4a 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IWorkflowService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IWorkflowService.java @@ -83,7 +83,5 @@ public interface IWorkflowService { Case decrypt(Case useCase); - Page findAllByUri(String uri, Pageable pageable); - Page search(Predicate predicate, Pageable pageable); } diff --git a/application-engine/src/main/resources/petriNets/engine-processes/preference_item.xml b/application-engine/src/main/resources/petriNets/engine-processes/preference_item.xml index b7ce81cef97..719c2bbe784 100644 --- a/application-engine/src/main/resources/petriNets/engine-processes/preference_item.xml +++ b/application-engine/src/main/resources/petriNets/engine-processes/preference_item.xml @@ -161,8 +161,7 @@ String corrected = getCorrectedUri(newUri) if (corrected == newUri) { - def node = uriService.findByUri(newUri) - change moveDestUri options { findOptionsBasedOnSelectedNode(node) } + change moveDestUri options { findOptionsBasedOnSelectedNode(newUri) } } else { change moveDestUri value { splitUriPath(corrected) } } @@ -191,7 +190,7 @@ String prefixUri = selectedUri.value.join("/") prefixUri = prefixUri.replace("//","/") - String newUri = prefixUri + uriService.getUriSeparator() + newNodeName.value + String newUri = prefixUri + "/" + newNodeName.value def newNode = uriService.getOrCreate(newUri, com.netgrif.application.engine.objects.petrinet.domain.UriContentType.CASE) change selectedUri value { splitUriPath(newNode.uriPath) } @@ -249,7 +248,7 @@ menu_item_identifier: f.menu_item_identifier; change menu_item_identifier value { - def idx = nodePath.value.lastIndexOf(uriService.getUriSeparator()) + def idx = nodePath.value.lastIndexOf("/") return nodePath.value.substring(idx + 1) } diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/TestHelper.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/TestHelper.groovy index 028d315abae..aade9cdcd3c 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/TestHelper.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/TestHelper.groovy @@ -71,9 +71,6 @@ class TestHelper { @Autowired private ImpersonationRunner impersonationRunner - @Autowired - private UriRunner uriRunner - @Autowired private ElasticsearchRunner elasticsearchRunner @@ -101,7 +98,6 @@ class TestHelper { defaultRealmRunner.run() anonymousRoleRunner.run() systemUserRunner.run() - uriRunner.run() groupRunner.run() filterRunner.run() impersonationRunner.run() diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy index 3d5afeb0cb7..909a08a77f0 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy @@ -9,7 +9,6 @@ import com.netgrif.application.engine.elastic.web.requestbodies.CaseSearchReques import com.netgrif.application.engine.objects.petrinet.domain.I18nString import com.netgrif.application.engine.objects.petrinet.domain.UriContentType import com.netgrif.application.engine.objects.petrinet.domain.UriNode -import com.netgrif.application.engine.petrinet.service.interfaces.IUriService import com.netgrif.application.engine.startup.runner.FilterRunner import com.netgrif.application.engine.startup.ImportHelper import com.netgrif.application.engine.objects.workflow.domain.Case @@ -56,9 +55,6 @@ class MenuItemApiTest { @Autowired private IDataService dataService - @Autowired - private IUriService uriService - @Autowired private GroupService groupService @@ -81,8 +77,6 @@ class MenuItemApiTest { Case filter = getFilter(caze) Thread.sleep(4000) - UriNode leafNode = uriService.findByUri("/netgrif/test/new_menu_item") - assert item.uriNodeId == uriService.findByUri("/netgrif/test").stringId assert item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_MENU_ICON.attributeId].value == "device_hub" assert item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_MENU_NAME.attributeId].value == new I18nString("FILTER") assert item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_IDENTIFIER.attributeId].value.toString() == "new_menu_item" @@ -96,20 +90,16 @@ class MenuItemApiTest { assert filter.dataSet["filter"].allowedNets == ["filter", "preference_item"] assert filter.dataSet["filter"].value == "processIdentifier:filter OR processIdentifier:preference_item" assert filter.dataSet["filter_type"].value == "Case" - assert leafNode != null Case testFolder = findCasesElastic("processIdentifier:$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER AND dataSet.${MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.attributeId}.textValue.keyword:\"/netgrif/test\"", PageRequest.of(0, 1))[0] Case netgrifFolder = findCasesElastic("processIdentifier:$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER AND dataSet.${MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.attributeId}.textValue.keyword:\"/netgrif\"", PageRequest.of(0, 1))[0] - UriNode testNode = uriService.findByUri("/netgrif") - UriNode netgrifNode = uriService.getRoot() + Case rootFolder = findCasesElastic("processIdentifier:$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER AND dataSet.${MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.attributeId}.textValue.keyword:\"/\"", PageRequest.of(0, 1))[0] - assert testFolder != null && testNode != null - assert testFolder.uriNodeId == testNode.stringId + assert testFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID.attributeId].value == [netgrifFolder.stringId] assert (testFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.attributeId].value as ArrayList).contains(item.stringId) assert item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID.attributeId].value == [testFolder.stringId] - assert netgrifFolder.uriNodeId == netgrifNode.stringId assert netgrifFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID.attributeId].value == [rootFolder.stringId] assert (netgrifFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.attributeId].value as ArrayList).contains(testFolder.stringId) assert rootFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID.attributeId].value == [] @@ -120,9 +110,8 @@ class MenuItemApiTest { void testChangeFilterAndMenuItems() { Case caze = createMenuItem() Thread.sleep(3000) - def newUri = uriService.getOrCreate("/netgrif/test_new", UriContentType.DEFAULT) caze = setData(caze, [ - "uri": newUri.path, + "uri": "/netgrif/test_new", "title": "CHANGED FILTER", "allowed_nets": "filter", "query": "processIdentifier:filter", @@ -137,7 +126,6 @@ class MenuItemApiTest { assert item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_ALLOWED_ROLES.attributeId].options.entrySet()[0].key.contains("role_2") assert item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CASE_DEFAULT_HEADERS.attributeId].value == "meta-title,meta-title,meta-title" assert item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_TASK_DEFAULT_HEADERS.attributeId].value == "meta-title,meta-title,meta-title" - assert item.uriNodeId == newUri.stringId assert filter.dataSet["filter"].allowedNets == ["filter"] assert filter.dataSet["filter"].filterMetadata["defaultSearchCategories"] == false @@ -176,10 +164,8 @@ class MenuItemApiTest { Case viewCase = workflowService.findOne(viewId) Thread.sleep(2000) - UriNode node = uriService.findByUri("/netgrif2") Case folderCase = findCasesElastic("processIdentifier:$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER AND dataSet.${MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.attributeId}.textValue:\"/netgrif2\"", PageRequest.of(0, 1))[0] - assert viewCase.uriNodeId == node.stringId ArrayList childIds = folderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.attributeId].value as ArrayList assert childIds.contains(viewId) && childIds.size() == 2 @@ -210,22 +196,15 @@ class MenuItemApiTest { folderCase = findCasesElastic("processIdentifier:$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER AND dataSet.${MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.attributeId}.textValue:\"/netgrif/test3/netgrif2\"", PageRequest.of(0, 1))[0] assert folderCase != null - node = uriService.findByUri("/netgrif/test3") - assert node != null - assert folderCase.uriNodeId == node.stringId assert folderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.attributeId].value == "/netgrif/test3/netgrif2" childIds = folderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.attributeId].value as ArrayList assert childIds.size() == 2 folderCase = workflowService.findOne(childIds[0]) - node = uriService.findByUri("/netgrif/test3/netgrif2") assert folderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.attributeId].value == "/netgrif/test3/netgrif2/test2" - assert folderCase.uriNodeId == node.stringId viewCase = workflowService.findOne(viewId2) - node = uriService.findByUri("/netgrif/test3/netgrif2/test2") - assert viewCase.uriNodeId == node.stringId } @Test @@ -264,10 +243,7 @@ class MenuItemApiTest { Case duplicated = workflowService.searchOne(QCase.case$.processIdentifier.eq("preference_item").and(QCase.case$.dataSet.get(MenuItemConstants.PREFERENCE_ITEM_FIELD_IDENTIFIER.attributeId).value.eq(newIdentifier))) assert duplicated != null - UriNode leafNode = uriService.findByUri("/netgrif/" + newIdentifier) - assert duplicated.uriNodeId == testFolder.uriNodeId - assert leafNode != null assert duplicated.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_DUPLICATE_TITLE.attributeId].value == null assert duplicated.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_DUPLICATE_IDENTIFIER.attributeId].value == null assert duplicated.title == newTitle diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy index d05881f3f2f..088363f180d 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy @@ -75,9 +75,6 @@ class PetriNetServiceTest { @Autowired private PetriNetRepository petriNetRepository - @Autowired - private UriService uriService - @Autowired private CaseRepository caseRepository @@ -114,8 +111,6 @@ class PetriNetServiceTest { PetriNet testNet = testNetOptional.getNet() Thread.sleep(5000) ElasticPetriNet elasticTestNet = elasticPetriNetRepository.findByStringId(testNet.stringId) - assert elasticTestNet != null && elasticTestNet.getUriNodeId() == uriService.getRoot().id - assert testNet.getUriNodeId() == uriService.getRoot().id assert petriNetRepository.findById(testNet.stringId).get().uriNodeId == null importHelper.createCase("Case 1", testNet) @@ -154,18 +149,6 @@ class PetriNetServiceTest { assert exceptionThrown } - @Test - void findAllByUriNodeIdTest() { - UriNode myNode = uriService.getOrCreate("/test", UriContentType.DEFAULT) - petriNetService.importPetriNet(stream(NET_FILE), VersionType.MAJOR, superCreator.getLoggedSuper(), myNode.id) - petriNetService.importPetriNet(stream(NET_FILE), VersionType.MAJOR, superCreator.getLoggedSuper(), myNode.id) - - Thread.sleep(2000) - - List petriNets = petriNetService.findAllByUriNodeId(myNode.id) - assert petriNets.size() == 2 - } - @Test void processSearch() { long processCount = petriNetRepository.count() diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/UriServiceTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/UriServiceTest.groovy deleted file mode 100644 index 6d1f22a44d6..00000000000 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/UriServiceTest.groovy +++ /dev/null @@ -1,170 +0,0 @@ -package com.netgrif.application.engine.petrinet.service - -import com.netgrif.application.engine.TestHelper -import com.netgrif.application.engine.configuration.properties.UriProperties -import com.netgrif.application.engine.objects.petrinet.domain.UriContentType -import com.netgrif.application.engine.objects.petrinet.domain.UriNode -import com.netgrif.application.engine.petrinet.domain.repository.UriNodeRepository -import com.netgrif.application.engine.petrinet.service.interfaces.IUriService -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.test.context.ActiveProfiles -import org.springframework.test.context.junit.jupiter.SpringExtension - -import static org.junit.jupiter.api.Assertions.assertThrows - -@SpringBootTest -@ActiveProfiles(["test"]) -@ExtendWith(SpringExtension.class) -class UriServiceTest { - - private static final String testUri1 = "/test/uri/child1" - private static final String testUri2 = "/test/uri/child2" - private static final String testUri3 = "/test/uri/child1/grandchild" - private static final String testUri4 = "/test/uri/child1/grandchild" - private static final String testUri5wrong = "test/uri/child3/grandchild" - private static final String testUri5 = "/test/uri/child3/grandchild" - private static final String testUri5parent = "/test/uri/child3" - private static final String testUri6wrong = "" - private static final String testUri6 = "/" - private static final String destination = "destination/path" - - @Autowired - private IUriService uriService - - @Autowired - private UriProperties uriProperties - - @Autowired - private TestHelper testHelper - - @Autowired - UriNodeRepository uriNodeRepository - - @BeforeEach - void init() { - testHelper.truncateDbs() - uriService.getOrCreate(testUri1, UriContentType.DEFAULT) - uriService.getOrCreate(testUri2, UriContentType.DEFAULT) - uriService.getOrCreate(testUri3, UriContentType.DEFAULT) - uriService.getOrCreate(testUri4, UriContentType.DEFAULT) - } - - @Test - void getOrCreateTest() { - String[] splitUri = testUri3.split(uriProperties.separator) - splitUri[0] = "/" - UriNode uriNode = uriService.getOrCreate(testUri3, UriContentType.CASE) - assert uriNode != null && uriNode.getName() == splitUri[splitUri.length - 1] - assert uriNode.parentId == uriNodeRepository.findByPath(testUri1).stringId - assert uriNode.contentTypes.size() == 2 && uriNode.contentTypes.contains(UriContentType.DEFAULT) - assert uriNode.getChildrenId().isEmpty() - assert uriNode.level == 4 - - uriNode = uriService.getOrCreate(testUri1, UriContentType.CASE) - assert uriNode.getChildrenId().size() == 1 && uriNode.getChildrenId().contains(uriNodeRepository.findByPath(testUri3).stringId) - assert uriNode.containsCase() - - uriNode = uriService.getOrCreate(testUri5wrong, UriContentType.PROCESS) - assert uriNode != null && uriNode.stringId == uriNodeRepository.findByPath(testUri5).stringId - assert uriNode.parentId == uriNodeRepository.findByPath(testUri5parent).stringId - assert uriNode.containsNet() - - uriNode = uriService.getOrCreate(testUri6wrong, UriContentType.DEFAULT) - assert uriNode.stringId == uriNodeRepository.findByPath(testUri6).stringId - assert uriNode.parentId == null - assert uriNode.level == 0 - } - - @Test - void getRootsTest() { - UriNode root = uriService.getRoot() - assert root.getParentId() == null - - UriNode root2 = new com.netgrif.application.engine.adapter.spring.petrinet.domain.UriNode() - root2.setLevel(0) - uriService.save(root2) - - assertThrows(IllegalStateException.class, () -> { - uriService.getRoot() - }) - } - - @Test - void populateDirectRelativesTest() { - UriNode uriNode = uriService.getOrCreate(testUri1, UriContentType.DEFAULT) - uriNode = uriService.populateDirectRelatives(uriNode) - - assert uriNode.parent != null && uriNode.parent.stringId == uriNode.parentId - assert uriNode.children.size() == 1 && uriNode.children.find {it.stringId == uriNode.childrenId[0]} != null - } - - @Test - void moveTest() { - prepareDatabase(List.of("/a/b/c")) - UriNode cNode = uriService.move("/a/b/c", "/a") - UriNode aNode = uriService.findByUri("/a") - UriNode bNode = uriService.findByUri("/a/b") - assert cNode.parentId == aNode.stringId && bNode.childrenId.size() == 0 - - prepareDatabase(List.of("/a/b/c", "/a/b/d")) - bNode = uriService.move("/a/b", "/") - aNode = uriService.findByUri("/a") - cNode = uriService.findByUri("/b/c") - UriNode rootNode = uriService.findByUri("/") - assert aNode.childrenId.size() == 0 && bNode.childrenId.size() == 2 && cNode.parentId == bNode.stringId - assert rootNode.childrenId.size() == 2 - - prepareDatabase(List.of("/a/b/c")) - assertThrows(IllegalArgumentException.class, () -> { - uriService.move("/a/b", "/a/b/c/d") - }) - - prepareDatabase(List.of("/a/b/c")) - bNode = uriService.move("/a/b", "d") - aNode = uriService.findByUri("/a") - UriNode dNode = uriService.findByUri("/d") - assert aNode.childrenId.size() == 0 - assert dNode.childrenId.size() == 1 - assert bNode.childrenId.size() == 1 && bNode.parentId == dNode.stringId - } - - private prepareDatabase(List listOfUriPaths) { - uriNodeRepository.deleteAll() - listOfUriPaths.each {path -> - uriService.getOrCreate(path, UriContentType.DEFAULT) - } - } - - @Test - void findTest() { - UriNode uriNode = uriService.findByUri(testUri3) - assert uriNode != null - - assertThrows(IllegalArgumentException.class, () -> { - uriService.findById("notSavedId") - }) - } - - @Test - void createDefaultTest() { - UriNode uriNode = uriService.createDefault() - assert uriNode != null && uriNode.level == 1 - - uriNodeRepository.deleteById(uriNode.stringId) - - uriNode = uriService.createDefault() - assert uriNode != null && uriNode.level == 1 - } - - @Test - void getRootTest() { - UriNode uriNode = uriService.createDefault() - assert uriNode != null && uriNode.level == 1 - uriNode = uriService.getRoot() - assert uriNode != null && uriNode.level == 0 - } -} diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/workflow/WorkflowServiceTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/workflow/WorkflowServiceTest.groovy index 93b5942f9d8..c009207af15 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/workflow/WorkflowServiceTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/workflow/WorkflowServiceTest.groovy @@ -102,7 +102,6 @@ class WorkflowServiceTest { def net = testNet.getNet() Case aCase = workflowService.createCase(net.stringId, null, null, superCreator.getLoggedSuper(), new Locale('sk')).getCase() assert aCase.title.equals("Slovenský preklad") - assert workflowService.findOne(aCase.stringId).uriNodeId == net.uriNodeId Case enCase = workflowService.createCase(net.stringId, null, null, superCreator.getLoggedSuper(), new Locale('en')).getCase() assert enCase.title.equals("English translation") diff --git a/application-engine/src/test/java/com/netgrif/application/engine/workflow/service/TaskServiceTest.java b/application-engine/src/test/java/com/netgrif/application/engine/workflow/service/TaskServiceTest.java index eaa7e5ba9e0..9b0e6986b04 100644 --- a/application-engine/src/test/java/com/netgrif/application/engine/workflow/service/TaskServiceTest.java +++ b/application-engine/src/test/java/com/netgrif/application/engine/workflow/service/TaskServiceTest.java @@ -17,7 +17,6 @@ import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; import com.netgrif.application.engine.startup.runner.SuperCreatorRunner; import com.netgrif.application.engine.startup.runner.SystemUserRunner; -import com.netgrif.application.engine.startup.runner.UriRunner; import com.netgrif.application.engine.objects.workflow.domain.Case; import com.netgrif.application.engine.objects.workflow.domain.Task; import com.netgrif.application.engine.objects.workflow.domain.eventoutcomes.caseoutcomes.CreateCaseEventOutcome; @@ -71,9 +70,6 @@ public class TaskServiceTest { @Autowired private SystemUserRunner userRunner; - @Autowired - private UriRunner uriRunner; - @Autowired private IPetriNetService petriNetService; @@ -89,7 +85,6 @@ public void setUp() throws Exception { taskRepository.deleteAll(); realmRunner.run(null); userRunner.run(null); - uriRunner.run(null); petriNetService.importPetriNet(new FileInputStream("src/test/resources/prikladFM.xml"), VersionType.MAJOR, superCreator.getLoggedSuper()); PetriNet net = petriNetRepository.findAll().get(0); diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java index f5c0d3246c6..53b16b2d14b 100644 --- a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java @@ -31,8 +31,6 @@ public abstract class ElasticCase implements Serializable { private String id; - private String uriNodeId; - private Long version; private Long lastModified; @@ -92,7 +90,6 @@ public abstract class ElasticCase implements Serializable { public ElasticCase(Case useCase) { stringId = useCase.getStringId(); - uriNodeId = useCase.getUriNodeId(); mongoId = useCase.getStringId(); //TODO: Duplication lastModified = Timestamp.valueOf(useCase.getLastModified()).getTime(); processIdentifier = useCase.getProcessIdentifier(); @@ -121,9 +118,6 @@ public ElasticCase(Case useCase) { public void update(ElasticCase useCase) { version++; lastModified = useCase.getLastModified(); - if (useCase.getUriNodeId() != null) { - uriNodeId = useCase.getUriNodeId(); - } title = useCase.getTitle(); taskIds = useCase.getTaskIds(); taskMongoIds = useCase.getTaskMongoIds(); diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/Case.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/Case.java index 51d11b58465..c6454cf8056 100644 --- a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/Case.java +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/Case.java @@ -30,9 +30,6 @@ public abstract class Case implements Serializable { @Setter private ProcessResourceId _id; - @Setter - private String uriNodeId; - @Setter private LocalDateTime lastModified; diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/menu/MenuItemBody.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/menu/MenuItemBody.java index 90369639070..dca2a07dfdb 100644 --- a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/menu/MenuItemBody.java +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/menu/MenuItemBody.java @@ -26,7 +26,7 @@ public class MenuItemBody { private I18nString tabName; private String menuIcon = "filter_none"; private String tabIcon; - private String uri; + private String path; private String identifier; private Case filter; private Map allowedRoles; @@ -76,8 +76,8 @@ public MenuItemBody(I18nString menuName, I18nString tabName, String menuIcon, St this.tabIcon = tabIcon; } - public MenuItemBody(String uri, String identifier, I18nString name, String icon) { - this.uri = uri; + public MenuItemBody(String path, String identifier, I18nString name, String icon) { + this.path = path; this.identifier = identifier; this.menuName = name; this.tabName = name; @@ -85,8 +85,8 @@ public MenuItemBody(String uri, String identifier, I18nString name, String icon) this.tabIcon = icon; } - public MenuItemBody(String uri, String identifier, I18nString menuName, I18nString tabName, String menuIcon, String tabIcon) { - this.uri = uri; + public MenuItemBody(String path, String identifier, I18nString menuName, I18nString tabName, String menuIcon, String tabIcon) { + this.path = path; this.identifier = identifier; this.menuName = menuName; this.tabName = tabName; @@ -94,8 +94,8 @@ public MenuItemBody(String uri, String identifier, I18nString menuName, I18nStri this.tabIcon = tabIcon; } - public MenuItemBody(String uri, String identifier, String name, String icon) { - this.uri = uri; + public MenuItemBody(String path, String identifier, String name, String icon) { + this.path = path; this.identifier = identifier; this.menuName = new I18nString(name); this.tabName = new I18nString(name); @@ -103,8 +103,8 @@ public MenuItemBody(String uri, String identifier, String name, String icon) { this.tabIcon = icon; } - public MenuItemBody(String uri, String identifier, String menuName, String tabName, String menuIcon, String tabIcon) { - this.uri = uri; + public MenuItemBody(String path, String identifier, String menuName, String tabName, String menuIcon, String tabIcon) { + this.path = path; this.identifier = identifier; this.menuName = new I18nString(menuName); this.tabName = new I18nString(tabName); diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/menu/MenuItemConstants.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/menu/MenuItemConstants.java index 1484b4847f1..3e0a8868a12 100644 --- a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/menu/MenuItemConstants.java +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/menu/MenuItemConstants.java @@ -55,7 +55,11 @@ public enum MenuItemConstants { // TRANSITIONS PREFERENCE_ITEM_SETTINGS_TRANS_ID("item_settings"), - PREFERENCE_ITEM_FIELD_INIT_TRANS_ID("initialize"); + PREFERENCE_ITEM_FIELD_INIT_TRANS_ID("initialize"), + + // SEPARATOR + PATH_SEPARATOR("/"); + @Getter private final String attributeId; diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java index b67c0461773..52284d0a626 100644 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java @@ -33,11 +33,6 @@ public String getId() { return super.getId(); } - @Field(type = Keyword) - public String getUriNodeId() { - return super.getUriNodeId(); - } - @Version public Long getVersion() { return super.getVersion(); From af13957298233076240073d0aa3b765b74658661 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Tue, 17 Jun 2025 11:34:20 +0200 Subject: [PATCH 02/20] Add evaluateRules method to PetriNetService Introduce a protected evaluateRules method that publishes events using the publisher. This allows for better modular handling of event-driven logic within PetriNetService. --- .../application/engine/petrinet/service/PetriNetService.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java index 5f445da963b..bfb92d2be02 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java @@ -223,6 +223,11 @@ public ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, VersionTyp return outcome; } + protected void evaluateRules(Event event) { + publisher.publishEvent(event); + + } + @Override public Optional save(PetriNet petriNet) { petriNet.initializeArcs(); From 5ae20f5cdc80b2c7b1a1cc9f20451904d123e86a Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Tue, 17 Jun 2025 13:41:01 +0200 Subject: [PATCH 03/20] Fix typo in variable name causing incorrect folder creation Corrected a misspelled variable `pathNUame` to `pathName` in the `getOrCreateFolderItem` method. This fixes an issue where folder creation logic could break due to the typo. --- .../petrinet/domain/dataset/logic/action/ActionDelegate.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index 7bca0436693..aeafc54acb1 100644 --- a/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -2124,7 +2124,7 @@ class ActionDelegate { protected Case getOrCreateFolderItem(String path) { String pathName = path.substring(path.lastIndexOf(MenuItemConstants.PATH_SEPARATOR.attributeId) + 1); - MenuItemBody body = new MenuItemBody(new I18nString(pathNUame), "folder") + MenuItemBody body = new MenuItemBody(new I18nString(pathName), "folder") return getOrCreateFolderRecursive(path, body) } From 83ce147513265739fa8efb2380784e6e85e5fe5c Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Fri, 20 Jun 2025 09:05:41 +0200 Subject: [PATCH 04/20] Add annotations and domain models for plugin architecture Introduced annotations (`EntryPoint` and `EntryPointMethod`) and corresponding domain models (`Plugin`, `EntryPoint`, `Method`, and `ListenerFilter`) to support a plugin architecture. These enable defining and managing plugins, their methods, and listener filters for remote invocation. --- .../objects/plugin/domain/EntryPoint.java | 40 +++++++++++++ .../objects/plugin/domain/ListenerFilter.java | 32 +++++++++++ .../engine/objects/plugin/domain/Method.java | 38 +++++++++++++ .../engine/objects/plugin/domain/Plugin.java | 57 +++++++++++++++++++ .../spring/plugin/annotations/EntryPoint.java | 28 +++++++++ .../plugin/annotations/EntryPointMethod.java | 19 +++++++ .../plugin/annotations/ListenerFilter.java | 10 ++++ 7 files changed, 224 insertions(+) create mode 100644 nae-object-library/src/main/java/com/netgrif/application/engine/objects/plugin/domain/EntryPoint.java create mode 100644 nae-object-library/src/main/java/com/netgrif/application/engine/objects/plugin/domain/ListenerFilter.java create mode 100644 nae-object-library/src/main/java/com/netgrif/application/engine/objects/plugin/domain/Method.java create mode 100644 nae-object-library/src/main/java/com/netgrif/application/engine/objects/plugin/domain/Plugin.java create mode 100644 nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/annotations/EntryPoint.java create mode 100644 nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/annotations/EntryPointMethod.java create mode 100644 nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/annotations/ListenerFilter.java diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/plugin/domain/EntryPoint.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/plugin/domain/EntryPoint.java new file mode 100644 index 00000000000..cb24e307fb5 --- /dev/null +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/plugin/domain/EntryPoint.java @@ -0,0 +1,40 @@ +package com.netgrif.application.engine.objects.plugin.domain; + +import lombok.Builder; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +/** + * Domain class for entry points. Entry points are beans annotated with {@link com.netgrif.plugin.core.annotations.EntryPoint} + * annotation. These beans contain methods, that can be run from server where the plugin is registered. + * */ +@Data +public class EntryPoint implements Serializable { + + @Serial + private static final long serialVersionUID = -4312516499873834830L; + + private String name; + + private String pluginName; + + /** + * Map of {@link Method}, key of the map is equivalent to {@link Method#getName()} + * */ + private Map methods; + + public EntryPoint() { + methods = new HashMap<>(); + } + + @Builder + public EntryPoint(String name, Map methods, String pluginName) { + this.name = name; + this.methods = methods; + this.pluginName = pluginName; + } +} diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/plugin/domain/ListenerFilter.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/plugin/domain/ListenerFilter.java new file mode 100644 index 00000000000..780ea1d4fb0 --- /dev/null +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/plugin/domain/ListenerFilter.java @@ -0,0 +1,32 @@ +package com.netgrif.application.engine.objects.plugin.domain; + +import com.netgrif.application.engine.objects.event.dispatchers.common.AbstractDispatcher; +import lombok.Builder; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.EventObject; + +/** + * Domain class for listener filters. This filter define, which listener will call methods of this plugin. + * */ +@Data +public class ListenerFilter implements Serializable { + + @Serial + private static final long serialVersionUID = 892495676687111427L; + + private Class eventType; + + private AbstractDispatcher.DispatchMethod dispatchMethod; + + public ListenerFilter() { + } + + @Builder + public ListenerFilter(Class eventType, AbstractDispatcher.DispatchMethod dispatchMethod) { + this.eventType = eventType; + this.dispatchMethod = dispatchMethod; + } +} diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/plugin/domain/Method.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/plugin/domain/Method.java new file mode 100644 index 00000000000..555174ac5a4 --- /dev/null +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/plugin/domain/Method.java @@ -0,0 +1,38 @@ +package com.netgrif.application.engine.objects.plugin.domain; + +import lombok.Builder; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * Domain class for plugin methods. This domain represents methods, that are implemented inside entry points + * and are annotated with {@code com.netgrif.application.engine.adapter.spring.plugin.annotations.EntryPointMethod} annotations. These methods + * can be run from server where the plugin is registered. + * */ +@Data +public class Method implements Serializable { + @Serial + private static final long serialVersionUID = -5007466888924505057L; + + private String name; + private List argTypes; + private String returnType; + private List listenerFilters; + + public Method() { + argTypes = new ArrayList<>(); + listenerFilters = new ArrayList<>(); + } + + @Builder + public Method(String name, List argTypes, String returnType, List listenerFilters) { + this.name = name; + this.argTypes = argTypes; + this.returnType = returnType; + this.listenerFilters = listenerFilters; + } +} diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/plugin/domain/Plugin.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/plugin/domain/Plugin.java new file mode 100644 index 00000000000..8aac391e067 --- /dev/null +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/plugin/domain/Plugin.java @@ -0,0 +1,57 @@ +package com.netgrif.application.engine.objects.plugin.domain; + +import lombok.Builder; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +/** + * Domain class that represent the given plugin + * */ +@Data +public class Plugin implements Serializable { + + @Serial + private static final long serialVersionUID = -8675728293188640186L; + + private String identifier; + + private String name; + + private String version; + + private String description; + + private String url; + + private int restPort; + + private int grpcPort; + + private boolean active; + + /** + * Map of {@link EntryPoint}, key of the map is equivalent to {@link EntryPoint#getName()} + * */ + private Map entryPoints; + + public Plugin() { + entryPoints = new HashMap<>(); + } + + @Builder + public Plugin(String identifier, String name, String version, String description, String url, int restPort, int grpcPort, Map entryPoints, boolean active) { + this.identifier = identifier; + this.name = name; + this.version = version; + this.description = description; + this.url = url; + this.restPort = restPort; + this.grpcPort = grpcPort; + this.entryPoints = entryPoints; + this.active = active; + } +} diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/annotations/EntryPoint.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/annotations/EntryPoint.java new file mode 100644 index 00000000000..e97e9ba96ee --- /dev/null +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/annotations/EntryPoint.java @@ -0,0 +1,28 @@ +package com.netgrif.application.engine.adapter.spring.plugin.annotations; + +import org.springframework.core.annotation.AliasFor; +import org.springframework.stereotype.Service; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation is used in NAE Plugin library to mark classes as they are Spring Beans (kind of + * {@link Service}), that implement remotely callable plugin methods. These methods have + * to be marked with {@link EntryPointMethod}. + * */ +@Service +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface EntryPoint { + @AliasFor("name") + String value() default ""; + @AliasFor("value") + String name() default ""; + /** + * This is only considered when plugin is loaded as module. + * */ + String pluginName() default ""; +} diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/annotations/EntryPointMethod.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/annotations/EntryPointMethod.java new file mode 100644 index 00000000000..cb04c613499 --- /dev/null +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/annotations/EntryPointMethod.java @@ -0,0 +1,19 @@ +package com.netgrif.application.engine.adapter.spring.plugin.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation is used in NAE Plugin library to mark methods, that can be called from NAE application, + * that the given plugin is attached to. These methods must be implemented as member of class, that is marked + * with {@link EntryPoint}. + * */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface EntryPointMethod { + String name() default ""; + String description() default ""; + ListenerFilter[] listeners() default {}; +} diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/annotations/ListenerFilter.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/annotations/ListenerFilter.java new file mode 100644 index 00000000000..9734b93a110 --- /dev/null +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/annotations/ListenerFilter.java @@ -0,0 +1,10 @@ +package com.netgrif.application.engine.adapter.spring.plugin.annotations; + +import com.netgrif.application.engine.objects.event.dispatchers.common.AbstractDispatcher; + +import java.util.EventObject; + +public @interface ListenerFilter { + Class eventType(); + AbstractDispatcher.DispatchMethod dispatchMethod(); +} From 9baa0733d8f12b23da33f41820f4a2479aa4738f Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Fri, 20 Jun 2025 09:28:19 +0200 Subject: [PATCH 05/20] Add PluginRegistrationConfiguration interface Introduced a new interface to define plugin registration configuration. It includes methods for retrieving the plugin name, version, and entry points. This provides a foundation for standardizing plugin integration. --- .../plugin/config/PluginRegistrationConfiguration.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/config/PluginRegistrationConfiguration.java diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/config/PluginRegistrationConfiguration.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/config/PluginRegistrationConfiguration.java new file mode 100644 index 00000000000..515eff058f9 --- /dev/null +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/config/PluginRegistrationConfiguration.java @@ -0,0 +1,7 @@ +package com.netgrif.application.engine.adapter.spring.plugin.config; + +public interface PluginRegistrationConfiguration { + String getPluginName(); + String getVersion(); + Object getEntryPoints(); +} \ No newline at end of file From 0a0dd9a13109458cf1858f3690e575b317f34a31 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Fri, 20 Jun 2025 09:30:23 +0200 Subject: [PATCH 06/20] - added new return type --- .../plugin/config/PluginRegistrationConfiguration.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/config/PluginRegistrationConfiguration.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/config/PluginRegistrationConfiguration.java index 515eff058f9..1c27ecbc7b9 100644 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/config/PluginRegistrationConfiguration.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/config/PluginRegistrationConfiguration.java @@ -1,7 +1,11 @@ package com.netgrif.application.engine.adapter.spring.plugin.config; +import com.netgrif.application.engine.objects.plugin.domain.EntryPoint; + +import java.util.Map; + public interface PluginRegistrationConfiguration { String getPluginName(); String getVersion(); - Object getEntryPoints(); + Map getEntryPoints(); } \ No newline at end of file From f1004802520bb3a97e19c421b6be5e6aa2f93ceb Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Fri, 20 Jun 2025 10:07:08 +0200 Subject: [PATCH 07/20] Add plugin support framework with injection capability Introduced core classes and services to enable a plugin architecture. This includes plugin injection, entry point management, and dynamic meta-class updates for ActionDelegate. The changes set the foundation for extensible plugin support within the system. --- .../engine/plugin/PluginInjector.groovy | 48 ++++++++++++++ .../engine/plugin/meta/EntryPointMeta.groovy | 8 +++ .../engine/plugin/meta/PluginHolder.groovy | 5 ++ .../engine/plugin/meta/PluginMeta.groovy | 5 ++ .../engine/startup/runner/PluginRunner.java | 62 +++++++++++++++++++ .../service/EntryPointLoaderService.java | 9 +++ .../spring/plugin/service/PluginService.java | 7 +++ 7 files changed, 144 insertions(+) create mode 100644 application-engine/src/main/groovy/com/netgrif/application/engine/plugin/PluginInjector.groovy create mode 100644 application-engine/src/main/groovy/com/netgrif/application/engine/plugin/meta/EntryPointMeta.groovy create mode 100644 application-engine/src/main/groovy/com/netgrif/application/engine/plugin/meta/PluginHolder.groovy create mode 100644 application-engine/src/main/groovy/com/netgrif/application/engine/plugin/meta/PluginMeta.groovy create mode 100644 application-engine/src/main/java/com/netgrif/application/engine/startup/runner/PluginRunner.java create mode 100644 nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/service/EntryPointLoaderService.java create mode 100644 nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/service/PluginService.java diff --git a/application-engine/src/main/groovy/com/netgrif/application/engine/plugin/PluginInjector.groovy b/application-engine/src/main/groovy/com/netgrif/application/engine/plugin/PluginInjector.groovy new file mode 100644 index 00000000000..ba4275f050c --- /dev/null +++ b/application-engine/src/main/groovy/com/netgrif/application/engine/plugin/PluginInjector.groovy @@ -0,0 +1,48 @@ +package com.netgrif.application.engine.plugin + +import com.netgrif.application.engine.adapter.spring.plugin.service.PluginService +import com.netgrif.application.engine.objects.plugin.domain.EntryPoint +import com.netgrif.application.engine.objects.plugin.domain.Method +import com.netgrif.application.engine.objects.plugin.domain.Plugin +import com.netgrif.application.engine.petrinet.domain.dataset.logic.action.ActionDelegate +import com.netgrif.application.engine.plugin.meta.EntryPointMeta +import com.netgrif.application.engine.plugin.meta.PluginHolder +import com.netgrif.application.engine.plugin.meta.PluginMeta +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.stereotype.Component + +@Component +class PluginInjector { + + @Autowired + protected PluginService pluginService + + /** + * Injects provided plugin into meta class of {@link ActionDelegate} + * + * @param plugin case of plugin to be injected + * */ + void inject(Plugin plugin) { + updateMetaClasses(plugin) + } + + protected void updateMetaClasses(Plugin plugin) { + MetaClass keyClassMeta = PluginHolder.metaClass + MetaClass pluginMetaClass = PluginMeta.metaClass + + List entryPoints = plugin.entryPoints.values() + + entryPoints.each { ep -> + MetaClass entryPointMetaClass = EntryPointMeta.metaClass + List methods = ep.methods.values() + + methods.each { method-> + entryPointMetaClass[method.name] = { Serializable... args -> + return pluginService.call(plugin.identifier, ep.name, method.name, args) + } + } + pluginMetaClass[ep.name] = new EntryPointMeta() + } + keyClassMeta[plugin.name] = new PluginMeta() + } +} diff --git a/application-engine/src/main/groovy/com/netgrif/application/engine/plugin/meta/EntryPointMeta.groovy b/application-engine/src/main/groovy/com/netgrif/application/engine/plugin/meta/EntryPointMeta.groovy new file mode 100644 index 00000000000..72e5e3a3b67 --- /dev/null +++ b/application-engine/src/main/groovy/com/netgrif/application/engine/plugin/meta/EntryPointMeta.groovy @@ -0,0 +1,8 @@ +package com.netgrif.application.engine.plugin.meta + +/** + * Class, that has modified meta class and is injected into + * {@link com.netgrif.application.engine.petrinet.domain.dataset.logic.action.ActionDelegate}. No class-attributes needed. + * */ +class EntryPointMeta { +} diff --git a/application-engine/src/main/groovy/com/netgrif/application/engine/plugin/meta/PluginHolder.groovy b/application-engine/src/main/groovy/com/netgrif/application/engine/plugin/meta/PluginHolder.groovy new file mode 100644 index 00000000000..2c924869966 --- /dev/null +++ b/application-engine/src/main/groovy/com/netgrif/application/engine/plugin/meta/PluginHolder.groovy @@ -0,0 +1,5 @@ +package com.netgrif.application.engine.plugin.meta; + + +class PluginHolder { +} diff --git a/application-engine/src/main/groovy/com/netgrif/application/engine/plugin/meta/PluginMeta.groovy b/application-engine/src/main/groovy/com/netgrif/application/engine/plugin/meta/PluginMeta.groovy new file mode 100644 index 00000000000..6cc368b21eb --- /dev/null +++ b/application-engine/src/main/groovy/com/netgrif/application/engine/plugin/meta/PluginMeta.groovy @@ -0,0 +1,5 @@ +package com.netgrif.application.engine.plugin.meta + + +class PluginMeta { +} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/PluginRunner.java b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/PluginRunner.java new file mode 100644 index 00000000000..283565b9bf9 --- /dev/null +++ b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/PluginRunner.java @@ -0,0 +1,62 @@ +package com.netgrif.application.engine.startup.runner; + +import com.netgrif.application.engine.adapter.spring.plugin.service.EntryPointLoaderService; +import com.netgrif.application.engine.objects.plugin.domain.Plugin; +import com.netgrif.application.engine.plugin.PluginInjector; +import com.netgrif.application.engine.startup.ApplicationEngineStartupRunner; +import com.netgrif.application.engine.startup.ImportHelper; +import com.netgrif.application.engine.startup.annotation.RunnerOrder; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +@Slf4j +@Component +@RunnerOrder(85) +@ConditionalOnProperty( + value = "nae.plugin.enabled", + havingValue = "true", + matchIfMissing = true +) +public class PluginRunner implements ApplicationEngineStartupRunner { + + private final EntryPointLoaderService entryPointLoaderService; + + private final PluginInjector pluginInjector; + + public PluginRunner(@Autowired(required = false) EntryPointLoaderService entryPointLoaderService, + PluginInjector pluginInjector) { + this.entryPointLoaderService = entryPointLoaderService; + this.pluginInjector = pluginInjector; + } + + @Override + public void run(ApplicationArguments args) throws Exception { + log.info("Registering new plugins."); + Map pluginMap = new HashMap<>(); + + entryPointLoaderService.getAll().forEach(entryPoint -> { + if (!pluginMap.containsKey(entryPoint.getPluginName())) { + Plugin plugin = Plugin.builder() + .identifier(entryPoint.getPluginName()) + .name(entryPoint.getPluginName()) + .version("0.0.1") + .description(StringUtils.EMPTY) + .entryPoints(new HashMap<>()) + .build(); + pluginMap.put(entryPoint.getPluginName(), plugin); + } + pluginMap.get(entryPoint.getPluginName()).getEntryPoints().put(entryPoint.getName(), entryPoint); + }); + pluginMap.values().forEach(pluginInjector::inject); + } + +} + diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/service/EntryPointLoaderService.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/service/EntryPointLoaderService.java new file mode 100644 index 00000000000..d9ed75c4719 --- /dev/null +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/service/EntryPointLoaderService.java @@ -0,0 +1,9 @@ +package com.netgrif.application.engine.adapter.spring.plugin.service; + +import com.netgrif.application.engine.objects.plugin.domain.EntryPoint; + +import java.util.List; + +public interface EntryPointLoaderService { + List getAll(); +} diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/service/PluginService.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/service/PluginService.java new file mode 100644 index 00000000000..b94ab27db1c --- /dev/null +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/service/PluginService.java @@ -0,0 +1,7 @@ +package com.netgrif.application.engine.adapter.spring.plugin.service; + +import java.io.Serializable; + +public interface PluginService { + Object call(String pluginCaseId, String entryPoint, String method, Serializable... args) throws IllegalArgumentException; +} From a6f06342428b2bcab096b991a7bf66b1d0a2d462 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Fri, 20 Jun 2025 10:07:47 +0200 Subject: [PATCH 08/20] Add plugin support framework with injection capability Introduced core classes and services to enable a plugin architecture. This includes plugin injection, entry point management, and dynamic meta-class updates for ActionDelegate. The changes set the foundation for extensible plugin support within the system. --- .../netgrif/application/engine/startup/runner/PluginRunner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/PluginRunner.java b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/PluginRunner.java index 283565b9bf9..ac765614b44 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/PluginRunner.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/PluginRunner.java @@ -39,7 +39,7 @@ public PluginRunner(@Autowired(required = false) EntryPointLoaderService entryPo @Override public void run(ApplicationArguments args) throws Exception { - log.info("Registering new plugins."); + log.info("Registering plugins."); Map pluginMap = new HashMap<>(); entryPointLoaderService.getAll().forEach(entryPoint -> { From be67be278dd8635478a9213d09c3996d2e13e82c Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Fri, 20 Jun 2025 10:12:59 +0200 Subject: [PATCH 09/20] Remove EntryPointLoaderService interface The EntryPointLoaderService interface was unused and redundant, so it has been deleted to simplify the codebase. This helps maintain cleaner and more maintainable code. --- .../spring/plugin/service/EntryPointLoaderService.java | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/service/EntryPointLoaderService.java diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/service/EntryPointLoaderService.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/service/EntryPointLoaderService.java deleted file mode 100644 index d9ed75c4719..00000000000 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/service/EntryPointLoaderService.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.netgrif.application.engine.adapter.spring.plugin.service; - -import com.netgrif.application.engine.objects.plugin.domain.EntryPoint; - -import java.util.List; - -public interface EntryPointLoaderService { - List getAll(); -} From 13ca6b0d49b50fb9fe083f5f9a87c1b34da8dd3a Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Fri, 20 Jun 2025 10:17:18 +0200 Subject: [PATCH 10/20] Refactor plugin registration to use ApplicationContext. Replaced the use of EntryPointLoaderService with ApplicationContext to retrieve PluginRegistrationConfiguration beans. This simplifies the process of loading plugins and their entry points, consolidating configuration management and improving maintainability. --- .../engine/startup/runner/PluginRunner.java | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/PluginRunner.java b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/PluginRunner.java index ac765614b44..c8dcb327361 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/PluginRunner.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/PluginRunner.java @@ -1,17 +1,16 @@ package com.netgrif.application.engine.startup.runner; -import com.netgrif.application.engine.adapter.spring.plugin.service.EntryPointLoaderService; +import com.netgrif.application.engine.adapter.spring.plugin.config.PluginRegistrationConfiguration; import com.netgrif.application.engine.objects.plugin.domain.Plugin; import com.netgrif.application.engine.plugin.PluginInjector; import com.netgrif.application.engine.startup.ApplicationEngineStartupRunner; -import com.netgrif.application.engine.startup.ImportHelper; import com.netgrif.application.engine.startup.annotation.RunnerOrder; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; import java.util.HashMap; @@ -27,13 +26,12 @@ ) public class PluginRunner implements ApplicationEngineStartupRunner { - private final EntryPointLoaderService entryPointLoaderService; - + private final ApplicationContext applicationContext; private final PluginInjector pluginInjector; - public PluginRunner(@Autowired(required = false) EntryPointLoaderService entryPointLoaderService, + public PluginRunner(ApplicationContext applicationContext, PluginInjector pluginInjector) { - this.entryPointLoaderService = entryPointLoaderService; + this.applicationContext = applicationContext; this.pluginInjector = pluginInjector; } @@ -42,18 +40,17 @@ public void run(ApplicationArguments args) throws Exception { log.info("Registering plugins."); Map pluginMap = new HashMap<>(); - entryPointLoaderService.getAll().forEach(entryPoint -> { - if (!pluginMap.containsKey(entryPoint.getPluginName())) { + applicationContext.getBeansOfType(PluginRegistrationConfiguration.class).forEach((key, config) -> { + if (!pluginMap.containsKey(config.getPluginName())) { Plugin plugin = Plugin.builder() - .identifier(entryPoint.getPluginName()) - .name(entryPoint.getPluginName()) + .identifier(config.getPluginName()) + .name(config.getPluginName()) .version("0.0.1") .description(StringUtils.EMPTY) - .entryPoints(new HashMap<>()) + .entryPoints(config.getEntryPoints()) .build(); - pluginMap.put(entryPoint.getPluginName(), plugin); + pluginMap.put(config.getPluginName(), plugin); } - pluginMap.get(entryPoint.getPluginName()).getEntryPoints().put(entryPoint.getName(), entryPoint); }); pluginMap.values().forEach(pluginInjector::inject); } From fa4153e3ca655989dd6d5f57e5cae4052adc9c3c Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Fri, 20 Jun 2025 10:17:47 +0200 Subject: [PATCH 11/20] Set plugin version dynamically from configuration Updated the PluginRunner to assign the plugin version from the configuration file instead of using a hardcoded value. This ensures better flexibility and easier version management for plugins. --- .../netgrif/application/engine/startup/runner/PluginRunner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/PluginRunner.java b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/PluginRunner.java index c8dcb327361..b0088cf1917 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/PluginRunner.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/PluginRunner.java @@ -45,7 +45,7 @@ public void run(ApplicationArguments args) throws Exception { Plugin plugin = Plugin.builder() .identifier(config.getPluginName()) .name(config.getPluginName()) - .version("0.0.1") + .version(config.getVersion()) .description(StringUtils.EMPTY) .entryPoints(config.getEntryPoints()) .build(); From 9a759d7289bf6db4e0d3a4b1b06706caea934bc7 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Fri, 20 Jun 2025 11:10:55 +0200 Subject: [PATCH 12/20] - implemented PluginService --- .../engine/plugin/PluginInjector.groovy | 4 +- .../application/engine/PluginServiceImpl.java | 81 +++++++++++++++++++ .../spring/plugin/service/PluginService.java | 3 +- 3 files changed, 85 insertions(+), 3 deletions(-) create mode 100644 application-engine/src/main/java/com/netgrif/application/engine/PluginServiceImpl.java diff --git a/application-engine/src/main/groovy/com/netgrif/application/engine/plugin/PluginInjector.groovy b/application-engine/src/main/groovy/com/netgrif/application/engine/plugin/PluginInjector.groovy index ba4275f050c..962bd6755a3 100644 --- a/application-engine/src/main/groovy/com/netgrif/application/engine/plugin/PluginInjector.groovy +++ b/application-engine/src/main/groovy/com/netgrif/application/engine/plugin/PluginInjector.groovy @@ -30,11 +30,11 @@ class PluginInjector { MetaClass keyClassMeta = PluginHolder.metaClass MetaClass pluginMetaClass = PluginMeta.metaClass - List entryPoints = plugin.entryPoints.values() + List entryPoints = new ArrayList(plugin.entryPoints.values()) entryPoints.each { ep -> MetaClass entryPointMetaClass = EntryPointMeta.metaClass - List methods = ep.methods.values() + List methods = new ArrayList(ep.methods.values()) methods.each { method-> entryPointMetaClass[method.name] = { Serializable... args -> diff --git a/application-engine/src/main/java/com/netgrif/application/engine/PluginServiceImpl.java b/application-engine/src/main/java/com/netgrif/application/engine/PluginServiceImpl.java new file mode 100644 index 00000000000..d739d124a4f --- /dev/null +++ b/application-engine/src/main/java/com/netgrif/application/engine/PluginServiceImpl.java @@ -0,0 +1,81 @@ +package com.netgrif.application.engine; + +import com.netgrif.application.engine.adapter.spring.plugin.service.PluginService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Service; + +import java.io.Serializable; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; + +@Slf4j +@Service +@RequiredArgsConstructor +public class PluginServiceImpl implements PluginService { + + private final ApplicationContext applicationContext; + + @Override + public Object call(String pluginId, String entryPoint, String method, Serializable... args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { + log.info("Executing entry point [{}] with method [{}]...", entryPoint, method); + Class[] paramTypesFromRequest = Arrays.stream(args).map(Object::getClass).toArray(Class[]::new); + Object bean = applicationContext.getBean(entryPoint); + Method methodToInvoke = findMethod(bean, method, paramTypesFromRequest); + return methodToInvoke.invoke(bean, Arrays.stream(args).toArray()); + } + + private Method findMethod(Object bean, String methodToExecute, Class[] requestParamTypes) + throws NoSuchMethodException, IllegalArgumentException { + try { + return bean.getClass().getMethod(methodToExecute, requestParamTypes); + } catch (NoSuchMethodException e) { + return findMethodWithSuperClassParams(bean, methodToExecute, requestParamTypes, e); + } + } + + private Method findMethodWithSuperClassParams(Object bean, String methodToExecute, Class[] requestParamTypes, + NoSuchMethodException caughtException) + throws NoSuchMethodException, IllegalArgumentException { + Class cls = bean.getClass(); + Method[] methods = cls.getMethods(); + Method methodToInvoke = null; + outerLoop: for (Method method : methods) { + if (!methodToExecute.equals(method.getName())) { + continue; + } + + Class[] paramTypes = method.getParameterTypes(); + int requestParamsLen = requestParamTypes.length; + int paramsLen = paramTypes.length; + + if (requestParamsLen == 0 && paramsLen == 0) { + methodToInvoke = method; + break; + } else if (paramsLen == 0 || paramsLen != requestParamsLen) { + continue; + } + + for (int i = 0; i < requestParamTypes.length; ++i) { + if (!paramTypes[i].isAssignableFrom(requestParamTypes[i])) { + continue outerLoop; + } + } + + if (methodToInvoke != null) { + throw new IllegalArgumentException(String.format("Method %s is ambiguous for the param types %s", + methodToExecute, Arrays.toString(requestParamTypes))); + } + methodToInvoke = method; + } + + if (methodToInvoke == null) { + throw caughtException; + } else { + return methodToInvoke; + } + } +} diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/service/PluginService.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/service/PluginService.java index b94ab27db1c..9f68c173fad 100644 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/service/PluginService.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/service/PluginService.java @@ -1,7 +1,8 @@ package com.netgrif.application.engine.adapter.spring.plugin.service; import java.io.Serializable; +import java.lang.reflect.InvocationTargetException; public interface PluginService { - Object call(String pluginCaseId, String entryPoint, String method, Serializable... args) throws IllegalArgumentException; + Object call(String pluginCaseId, String entryPoint, String method, Serializable... args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException; } From e7e98d77282f147d110af2ef7bd5374ca26b1534 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Fri, 20 Jun 2025 11:18:30 +0200 Subject: [PATCH 13/20] Add PluginHolder to ActionDelegate initialization Introduced PluginHolder as a new field in ActionDelegate and initialized it in the constructor. This change supports enhanced plugin management within the ActionDelegate logic. --- .../domain/dataset/logic/action/ActionDelegate.groovy | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index d90dc30fedf..6f327378236 100644 --- a/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -52,6 +52,7 @@ import com.netgrif.application.engine.pdf.generator.config.PdfResource import com.netgrif.application.engine.pdf.generator.service.interfaces.IPdfGenerator import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService import com.netgrif.application.engine.petrinet.service.interfaces.IUriService +import com.netgrif.application.engine.plugin.meta.PluginHolder import com.netgrif.application.engine.startup.ImportHelper import com.netgrif.application.engine.startup.runner.DefaultFiltersRunner import com.netgrif.application.engine.startup.runner.FilterRunner @@ -195,6 +196,8 @@ class ActionDelegate { ModuleHolder NaeModule + PluginHolder Plugin + /** * Reference of case and task in which current action is taking place. */ @@ -217,6 +220,7 @@ class ActionDelegate { this.outcomes = new ArrayList<>() this.Frontend = new FrontendActionOutcome(this.useCase, this.task, this.outcomes) this.NaeModule = new ModuleHolder() + this.Plugin = new PluginHolder() } def initFieldsMap(Map fieldIds) { From 3cd9c0e3f259d864508ee156e53134c6be10fcd5 Mon Sep 17 00:00:00 2001 From: Milan Mladoniczky <6153201+tuplle@users.noreply.github.com> Date: Sat, 21 Jun 2025 23:12:16 +0200 Subject: [PATCH 14/20] Refactor PluginInjector and PluginRegistrationConfiguration - Enhanced PluginInjector with improved meta-class injection logic and documentation. - Updated PluginRegistrationConfiguration to include metadata retrieval. --- .../engine/plugin/PluginInjector.groovy | 55 ++++++++++++++----- .../PluginRegistrationConfiguration.java | 5 ++ 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/application-engine/src/main/groovy/com/netgrif/application/engine/plugin/PluginInjector.groovy b/application-engine/src/main/groovy/com/netgrif/application/engine/plugin/PluginInjector.groovy index 962bd6755a3..43fc1825e12 100644 --- a/application-engine/src/main/groovy/com/netgrif/application/engine/plugin/PluginInjector.groovy +++ b/application-engine/src/main/groovy/com/netgrif/application/engine/plugin/PluginInjector.groovy @@ -11,38 +11,63 @@ import com.netgrif.application.engine.plugin.meta.PluginMeta import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Component +/** + * Component responsible for injecting plugin entry points and methods into + * the application's meta-class system at runtime. Uses Groovy's metaClass + * mechanism to dynamically add closures that delegate calls to PluginService. + * + * @see ActionDelegate + * @see PluginService + */ @Component class PluginInjector { + /** + * Service used to invoke methods on the injected plugins. + */ @Autowired protected PluginService pluginService /** - * Injects provided plugin into meta class of {@link ActionDelegate} + * Injects the provided plugin into the application's meta-class system. + * This will allow calls to plugin entry points and methods via dynamic + * properties on {@link PluginHolder}. * - * @param plugin case of plugin to be injected - * */ + * @param plugin the Plugin instance to be injected + */ void inject(Plugin plugin) { updateMetaClasses(plugin) } + /** + * Updates meta-class definitions for the given plugin by registering + * each entry point and its methods as closures. Each generated closure + * delegates invocation to the {@link PluginService}. + * + * @param plugin the Plugin instance whose entry points and methods + * are to be exposed dynamically + */ protected void updateMetaClasses(Plugin plugin) { - MetaClass keyClassMeta = PluginHolder.metaClass - MetaClass pluginMetaClass = PluginMeta.metaClass + def pluginMeta = new PluginMeta() - List entryPoints = new ArrayList(plugin.entryPoints.values()) + plugin.entryPoints.values().each { EntryPoint ep -> + def epMeta = new EntryPointMeta() - entryPoints.each { ep -> - MetaClass entryPointMetaClass = EntryPointMeta.metaClass - List methods = new ArrayList(ep.methods.values()) - - methods.each { method-> - entryPointMetaClass[method.name] = { Serializable... args -> - return pluginService.call(plugin.identifier, ep.name, method.name, args) + ep.methods.values().each { Method method -> + /** + * Dynamically generated method closure for entry point invocation. + * + * @param args variable-length list of Serializable arguments + * @return the result returned by PluginService.call(...) + */ + epMeta.metaClass."${method.name}" = { Serializable... args -> + pluginService.call(plugin.identifier, ep.name, method.name, args) } } - pluginMetaClass[ep.name] = new EntryPointMeta() + + pluginMeta.metaClass."${ep.name}" = epMeta } - keyClassMeta[plugin.name] = new PluginMeta() + + PluginHolder.metaClass."${plugin.name}" = pluginMeta } } diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/config/PluginRegistrationConfiguration.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/config/PluginRegistrationConfiguration.java index 1c27ecbc7b9..43888adead3 100644 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/config/PluginRegistrationConfiguration.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/config/PluginRegistrationConfiguration.java @@ -5,7 +5,12 @@ import java.util.Map; public interface PluginRegistrationConfiguration { + String getPluginName(); + String getVersion(); + Map getEntryPoints(); + + Map getMetadata(); } \ No newline at end of file From 53de537d861d5d4160d2421865f4cc8489fcf8dd Mon Sep 17 00:00:00 2001 From: Milan Mladoniczky <6153201+tuplle@users.noreply.github.com> Date: Sat, 21 Jun 2025 23:34:54 +0200 Subject: [PATCH 15/20] Add annotations for ListenerFilter - Introduced @Target and @Retention annotations for ListenerFilter definition. - Ensured compatibility with method-level usage and set retention policy at runtime. --- .../adapter/spring/plugin/annotations/ListenerFilter.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/annotations/ListenerFilter.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/annotations/ListenerFilter.java index 9734b93a110..22d513504d6 100644 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/annotations/ListenerFilter.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/plugin/annotations/ListenerFilter.java @@ -2,8 +2,14 @@ import com.netgrif.application.engine.objects.event.dispatchers.common.AbstractDispatcher; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.EventObject; +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) public @interface ListenerFilter { Class eventType(); AbstractDispatcher.DispatchMethod dispatchMethod(); From 208238eb7945b4ff33cb1b1b307f79967ba234cd Mon Sep 17 00:00:00 2001 From: Machac Date: Mon, 23 Jun 2025 11:14:53 +0200 Subject: [PATCH 16/20] [NAE-2125] Remove URI service usage from admin and menu items - Deleted the unused `IUriService` import from `ActionDelegate.groovy` to improve code cleanliness. - No changes to functionality or behavior, ensuring adherence to best practices. --- .../petrinet/domain/dataset/logic/action/ActionDelegate.groovy | 1 - 1 file changed, 1 deletion(-) diff --git a/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index 77988977404..21a3ff15d40 100644 --- a/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -51,7 +51,6 @@ import com.netgrif.application.engine.objects.workflow.service.InitValueExpressi import com.netgrif.application.engine.pdf.generator.config.PdfResource import com.netgrif.application.engine.pdf.generator.service.interfaces.IPdfGenerator import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService -import com.netgrif.application.engine.petrinet.service.interfaces.IUriService import com.netgrif.application.engine.plugin.meta.PluginHolder import com.netgrif.application.engine.startup.ImportHelper import com.netgrif.application.engine.startup.runner.DefaultFiltersRunner From 96b38c141536fad9be7a8b81233615522fda267d Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Mon, 23 Jun 2025 15:43:19 +0200 Subject: [PATCH 17/20] Refactor `attributeId` to `value` in `MenuItemConstants` Replaced all instances of `MenuItemConstants.attributeId` with `value` for improved clarity and consistency in referencing constants. Updated related logic in workflow and dataset handling across multiple files to align with the new structure. --- .../logic/action/ActionDelegate.groovy | 134 +++++++++--------- .../petrinet/web/PetriNetController.java | 2 +- .../workflow/domain/menu/MenuItemBody.java | 2 +- .../domain/menu/MenuItemConstants.java | 6 +- 4 files changed, 72 insertions(+), 72 deletions(-) diff --git a/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index 21a3ff15d40..53164b5dd19 100644 --- a/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -1876,27 +1876,27 @@ class ActionDelegate { */ def changeMenuItem(Case item) { [allowedRoles : { cl -> - updateMenuItemRoles(item, cl as Closure, MenuItemConstants.PREFERENCE_ITEM_FIELD_ALLOWED_ROLES.attributeId) + updateMenuItemRoles(item, cl as Closure, MenuItemConstants.PREFERENCE_ITEM_FIELD_ALLOWED_ROLES.value) }, bannedRoles : { cl -> - updateMenuItemRoles(item, cl as Closure, MenuItemConstants.PREFERENCE_ITEM_FIELD_BANNED_ROLES.attributeId) + updateMenuItemRoles(item, cl as Closure, MenuItemConstants.PREFERENCE_ITEM_FIELD_BANNED_ROLES.value) }, caseDefaultHeaders : { cl -> String defaultHeaders = cl() as String - setData(MenuItemConstants.PREFERENCE_ITEM_SETTINGS_TRANS_ID.attributeId, item, [ - (MenuItemConstants.PREFERENCE_ITEM_FIELD_CASE_DEFAULT_HEADERS.attributeId): ["type": "text", "value": defaultHeaders] + setData(MenuItemConstants.PREFERENCE_ITEM_SETTINGS_TRANS_ID.value, item, [ + (MenuItemConstants.PREFERENCE_ITEM_FIELD_CASE_DEFAULT_HEADERS.value): ["type": "text", "value": defaultHeaders] ]) }, taskDefaultHeaders : { cl -> String defaultHeaders = cl() as String - setData(MenuItemConstants.PREFERENCE_ITEM_SETTINGS_TRANS_ID.attributeId, item, [ - (MenuItemConstants.PREFERENCE_ITEM_FIELD_TASK_DEFAULT_HEADERS.attributeId): ["type": "text", "value": defaultHeaders] + setData(MenuItemConstants.PREFERENCE_ITEM_SETTINGS_TRANS_ID.value, item, [ + (MenuItemConstants.PREFERENCE_ITEM_FIELD_TASK_DEFAULT_HEADERS.value): ["type": "text", "value": defaultHeaders] ]) }, filter : { cl -> def filter = cl() as Case setData("change_filter", item, [ - (MenuItemConstants.PREFERENCE_ITEM_FIELD_NEW_FILTER_ID.attributeId): ["type": "text", "value": filter.stringId] + (MenuItemConstants.PREFERENCE_ITEM_FIELD_NEW_FILTER_ID.value): ["type": "text", "value": filter.stringId] ]) }, uri : { cl -> @@ -1910,38 +1910,38 @@ class ActionDelegate { title : { cl -> def value = cl() I18nString newName = (value instanceof I18nString) ? value : new I18nString(value as String) - setData(MenuItemConstants.PREFERENCE_ITEM_SETTINGS_TRANS_ID.attributeId, item, [ - (MenuItemConstants.PREFERENCE_ITEM_FIELD_MENU_NAME.attributeId): ["type": "i18n", "value": newName] + setData(MenuItemConstants.PREFERENCE_ITEM_SETTINGS_TRANS_ID.value, item, [ + (MenuItemConstants.PREFERENCE_ITEM_FIELD_MENU_NAME.value): ["type": "i18n", "value": newName] ]) }, menuIcon : { cl -> def value = cl() - setData(MenuItemConstants.PREFERENCE_ITEM_SETTINGS_TRANS_ID.attributeId, item, [ - (MenuItemConstants.PREFERENCE_ITEM_FIELD_MENU_ICON.attributeId): ["type": "text", "value": value] + setData(MenuItemConstants.PREFERENCE_ITEM_SETTINGS_TRANS_ID.value, item, [ + (MenuItemConstants.PREFERENCE_ITEM_FIELD_MENU_ICON.value): ["type": "text", "value": value] ]) }, tabIcon : { cl -> def value = cl() - setData(MenuItemConstants.PREFERENCE_ITEM_SETTINGS_TRANS_ID.attributeId, item, [ - (MenuItemConstants.PREFERENCE_ITEM_FIELD_TAB_ICON.attributeId): ["type": "text", "value": value] + setData(MenuItemConstants.PREFERENCE_ITEM_SETTINGS_TRANS_ID.value, item, [ + (MenuItemConstants.PREFERENCE_ITEM_FIELD_TAB_ICON.value): ["type": "text", "value": value] ]) }, requireTitleInCreation: { cl -> def value = cl() - setData(MenuItemConstants.PREFERENCE_ITEM_SETTINGS_TRANS_ID.attributeId, item, [ - (MenuItemConstants.PREFERENCE_ITEM_FIELD_REQUIRE_TITLE_IN_CREATION.attributeId): ["type": "boolean", "value": value] + setData(MenuItemConstants.PREFERENCE_ITEM_SETTINGS_TRANS_ID.value, item, [ + (MenuItemConstants.PREFERENCE_ITEM_FIELD_REQUIRE_TITLE_IN_CREATION.value): ["type": "boolean", "value": value] ]) }, useCustomView : { cl -> def value = cl() - setData(MenuItemConstants.PREFERENCE_ITEM_SETTINGS_TRANS_ID.attributeId, item, [ - (MenuItemConstants.PREFERENCE_ITEM_FIELD_USE_CUSTOM_VIEW.attributeId): ["type": "boolean", "value": value] + setData(MenuItemConstants.PREFERENCE_ITEM_SETTINGS_TRANS_ID.value, item, [ + (MenuItemConstants.PREFERENCE_ITEM_FIELD_USE_CUSTOM_VIEW.value): ["type": "boolean", "value": value] ]) }, customViewSelector : { cl -> def value = cl() - setData(MenuItemConstants.PREFERENCE_ITEM_SETTINGS_TRANS_ID.attributeId, item, [ - (MenuItemConstants.PREFERENCE_ITEM_FIELD_CUSTOM_VIEW_SELECTOR.attributeId): ["type": "text", "value": value] + setData(MenuItemConstants.PREFERENCE_ITEM_SETTINGS_TRANS_ID.value, item, [ + (MenuItemConstants.PREFERENCE_ITEM_FIELD_CUSTOM_VIEW_SELECTOR.value): ["type": "text", "value": value] ]) }] @@ -2094,13 +2094,13 @@ class ActionDelegate { I18nString newName = body.menuName ?: (body.filter?.dataSet[FILTER_FIELD_I18N_FILTER_NAME].value as I18nString) Case menuItemCase = createCase(FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER, newName?.defaultValue) - menuItemCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_ALLOWED_ROLES.attributeId].options = body.allowedRoles - menuItemCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_BANNED_ROLES.attributeId].options = body.bannedRoles + menuItemCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_ALLOWED_ROLES.value].options = body.allowedRoles + menuItemCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_BANNED_ROLES.value].options = body.bannedRoles if (parentItemCase != null) { parentItemCase = appendChildCaseIdAndSave(parentItemCase, menuItemCase.stringId) } menuItemCase = workflowService.save(menuItemCase) - Task newItemTask = taskService.findOne(menuItemCase.tasks.find { it.transition == MenuItemConstants.PREFERENCE_ITEM_FIELD_INIT_TRANS_ID.attributeId }.task) + Task newItemTask = taskService.findOne(menuItemCase.tasks.find { it.transition == MenuItemConstants.PREFERENCE_ITEM_FIELD_INIT_TRANS_ID.value }.task) String nodePath = createNodePath(body.path, sanitizedIdentifier) newItemTask = assignTask(newItemTask) @@ -2119,15 +2119,15 @@ class ActionDelegate { } protected String createNodePath(String path, String identifier) { - if (path == MenuItemConstants.PATH_SEPARATOR.attributeId) { + if (path == MenuItemConstants.PATH_SEPARATOR.value) { return path + identifier } else { - return path + MenuItemConstants.PATH_SEPARATOR.attributeId + identifier + return path + MenuItemConstants.PATH_SEPARATOR.value + identifier } } protected Case getOrCreateFolderItem(String path) { - String pathName = path.substring(path.lastIndexOf(MenuItemConstants.PATH_SEPARATOR.attributeId) + 1); + String pathName = path.substring(path.lastIndexOf(MenuItemConstants.PATH_SEPARATOR.value) + 1); MenuItemBody body = new MenuItemBody(new I18nString(pathName), "folder") return getOrCreateFolderRecursive(path, body) } @@ -2149,7 +2149,7 @@ class ActionDelegate { } else { folder = workflowService.save(folder) } - Task newItemTask = taskService.findOne(folder.tasks.find { it.transition == MenuItemConstants.PREFERENCE_ITEM_FIELD_INIT_TRANS_ID.attributeId }.task) + Task newItemTask = taskService.findOne(folder.tasks.find { it.transition == MenuItemConstants.PREFERENCE_ITEM_FIELD_INIT_TRANS_ID.value }.task) assignTask(newItemTask) setData(newItemTask, body.toDataSet(null, path)) finishTask(newItemTask) @@ -2165,24 +2165,24 @@ class ActionDelegate { } protected String nameFromPath(String path) { - if (path == null || path == MenuItemConstants.PATH_SEPARATOR.attributeId || path.length() == 0) { + if (path == null || path == MenuItemConstants.PATH_SEPARATOR.value || path.length() == 0) { return "" } - if (path.lastIndexOf(MenuItemConstants.PATH_SEPARATOR.attributeId) == 0) { - return path.replace(MenuItemConstants.PATH_SEPARATOR.attributeId, "") + if (path.lastIndexOf(MenuItemConstants.PATH_SEPARATOR.value) == 0) { + return path.replace(MenuItemConstants.PATH_SEPARATOR.value, "") } - return path.substring(path.lastIndexOf(MenuItemConstants.PATH_SEPARATOR.attributeId)) + return path.substring(path.lastIndexOf(MenuItemConstants.PATH_SEPARATOR.value)) } protected String parentPath(String path) { - if (path == null || path == MenuItemConstants.PATH_SEPARATOR.attributeId || path.length() == 0 || path.lastIndexOf(MenuItemConstants.PATH_SEPARATOR.attributeId) == 0) { - return MenuItemConstants.PATH_SEPARATOR.attributeId + if (path == null || path == MenuItemConstants.PATH_SEPARATOR.value || path.length() == 0 || path.lastIndexOf(MenuItemConstants.PATH_SEPARATOR.value) == 0) { + return MenuItemConstants.PATH_SEPARATOR.value } - return path.substring(0, path.lastIndexOf(MenuItemConstants.PATH_SEPARATOR.attributeId)) + return path.substring(0, path.lastIndexOf(MenuItemConstants.PATH_SEPARATOR.value)) } protected boolean hasParent(String path) { - if (path == null || path == MenuItemConstants.PATH_SEPARATOR.attributeId || path.length() == 0) { + if (path == null || path == MenuItemConstants.PATH_SEPARATOR.value || path.length() == 0) { return false } return true @@ -2203,7 +2203,7 @@ class ActionDelegate { List casesToSave = new ArrayList<>() - List parentIdList = item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID.attributeId].value as ArrayList + List parentIdList = item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID.value].value as ArrayList if (parentIdList != null && parentIdList.size() > 0) { Case oldParent = removeChildItemFromParent(parentIdList[0], item) casesToSave.add(oldParent) @@ -2211,11 +2211,11 @@ class ActionDelegate { Case newParent = getOrCreateFolderItem(destUri) if (newParent != null) { - item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID.attributeId].value = [newParent.stringId] as ArrayList + item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID.value].value = [newParent.stringId] as ArrayList newParent = appendChildCaseId(newParent, item.stringId) casesToSave.add(newParent) } else { - item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID.attributeId].value = null + item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID.value].value = null } item = resolveAndHandleNewNodePath(item, destUri) @@ -2262,30 +2262,30 @@ class ActionDelegate { String newNodePath = createNodePath((String) originItem.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH].value, sanitizedIdentifier) - Task newItemTask = taskService.findOne(duplicated.tasks.find { it.transition == MenuItemConstants.PREFERENCE_ITEM_FIELD_INIT_TRANS_ID.attributeId }.task) + Task newItemTask = taskService.findOne(duplicated.tasks.find { it.transition == MenuItemConstants.PREFERENCE_ITEM_FIELD_INIT_TRANS_ID.value }.task) Map updatedDataSet = [ - (MenuItemConstants.PREFERENCE_ITEM_FIELD_DUPLICATE_TITLE.attributeId) : [ + (MenuItemConstants.PREFERENCE_ITEM_FIELD_DUPLICATE_TITLE.value) : [ "value": null, "type" : "text" ], - (MenuItemConstants.PREFERENCE_ITEM_FIELD_DUPLICATE_IDENTIFIER.attributeId) : [ + (MenuItemConstants.PREFERENCE_ITEM_FIELD_DUPLICATE_IDENTIFIER.value) : [ "value": null, "type" : "text" ], - (MenuItemConstants.PREFERENCE_ITEM_FIELD_MENU_NAME.attributeId) : [ + (MenuItemConstants.PREFERENCE_ITEM_FIELD_MENU_NAME.value) : [ "value": newTitle, "type" : "i18n" ], - (MenuItemConstants.PREFERENCE_ITEM_FIELD_TAB_NAME.attributeId) : [ + (MenuItemConstants.PREFERENCE_ITEM_FIELD_TAB_NAME.value) : [ "value": newTitle, "type" : "i18n" ], - (MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.attributeId) : [ + (MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.value) : [ "value": newNodePath, "type" : "text" ], // Must be reset by button, because we have the same dataSet reference between originItem and duplicated - (MenuItemConstants.PREFERENCE_ITEM_FIELD_DUPLICATE_RESET_CHILD_ITEM_IDS.attributeId): [ + (MenuItemConstants.PREFERENCE_ITEM_FIELD_DUPLICATE_RESET_CHILD_ITEM_IDS.value): [ "value": 0, "type" : "button" ], @@ -2294,7 +2294,7 @@ class ActionDelegate { dataService.setData(newItemTask, ImportHelper.populateDataset(updatedDataSet)) finishTask(newItemTask) - String parentId = (originItem.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID.attributeId].value as ArrayList).get(0) + String parentId = (originItem.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID.value].value as ArrayList).get(0) if (parentId) { Case parent = workflowService.findOne(parentId) appendChildCaseIdAndSave(parent, duplicated.stringId) @@ -2303,7 +2303,7 @@ class ActionDelegate { } private List updateNodeInChildrenFoldersRecursive(Case parentFolder) { - List childItemIds = parentFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.attributeId].value as List + List childItemIds = parentFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.value].value as List if (childItemIds == null || childItemIds.isEmpty()) { return new ArrayList() } @@ -2323,31 +2323,31 @@ class ActionDelegate { private Case resolveAndHandleNewNodePath(Case folderItem, String destUri) { String newNodePath = resolveNewNodePath(folderItem, destUri) - folderItem.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.attributeId].value = newNodePath + folderItem.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.value].value = newNodePath return folderItem } private String resolveNewNodePath(Case folderItem, String destUri) { return destUri + - MenuItemConstants.PATH_SEPARATOR.attributeId + - folderItem.getFieldValue(MenuItemConstants.PREFERENCE_ITEM_FIELD_IDENTIFIER.attributeId) as String + MenuItemConstants.PATH_SEPARATOR.value + + folderItem.getFieldValue(MenuItemConstants.PREFERENCE_ITEM_FIELD_IDENTIFIER.value) as String } private Case removeChildItemFromParent(String folderId, Case childItem) { Case parentFolder = workflowService.findOne(folderId) - (parentFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.attributeId].value as List).remove(childItem.stringId) - parentFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_HAS_CHILDREN.attributeId].value = hasChildren(parentFolder) + (parentFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.value].value as List).remove(childItem.stringId) + parentFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_HAS_CHILDREN.value].value = hasChildren(parentFolder) workflowService.save(parentFolder) } private boolean isCyclicNodePath(Case folderItem, String destUri) { - String oldNodePath = folderItem.getFieldValue(MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.attributeId) + String oldNodePath = folderItem.getFieldValue(MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.value) return destUri.contains(oldNodePath) } private boolean hasChildren(Case folderItem) { - List children = folderItem.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.attributeId].value as List + List children = folderItem.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.value].value as List return children != null && children.size() > 0 } @@ -2357,20 +2357,20 @@ class ActionDelegate { } private Case appendChildCaseId(Case folderCase, String childItemCaseId) { - List childIds = folderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.attributeId].value as ArrayList + List childIds = folderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.value].value as ArrayList if (childIds == null) { - folderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.attributeId].value = [childItemCaseId] as ArrayList + folderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.value].value = [childItemCaseId] as ArrayList } else { - folderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.attributeId].value = childIds + [childItemCaseId] as ArrayList + folderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.value].value = childIds + [childItemCaseId] as ArrayList } - folderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_HAS_CHILDREN.attributeId].value = hasChildren(folderCase) + folderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_HAS_CHILDREN.value].value = hasChildren(folderCase) return folderCase } private Case initializeParentId(Case childFolderCase, String parentFolderCaseId) { - childFolderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID.attributeId].value = [parentFolderCaseId] as ArrayList + childFolderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID.value].value = [parentFolderCaseId] as ArrayList return workflowService.save(childFolderCase) } @@ -2423,7 +2423,7 @@ class ActionDelegate { Case findMenuItemByUriAndIdentifier(String uri, String identifier) { String nodePath = createNodePath(uri, identifier) - return findCaseElastic("processIdentifier:\"$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER\" AND dataSet.${MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.attributeId}.textValue.keyword:\"$nodePath\"") + return findCaseElastic("processIdentifier:\"$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER\" AND dataSet.${MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.value}.textValue.keyword:\"$nodePath\"") } /** @@ -2458,7 +2458,7 @@ class ActionDelegate { * @return found filter instance. If not found, null is returned */ Case getFilterFromMenuItem(Case item) { - String filterId = (item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_FILTER_CASE.attributeId].value as List)[0] as String + String filterId = (item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_FILTER_CASE.value].value as List)[0] as String return filterId ? workflowService.findOne(filterId) : null } @@ -2776,7 +2776,7 @@ class ActionDelegate { * @return updated menu item instance * */ Case updateMenuItem(Case item, MenuItemBody body) { - def outcome = setData(MenuItemConstants.PREFERENCE_ITEM_SETTINGS_TRANS_ID.attributeId, item, body.toDataSet()) + def outcome = setData(MenuItemConstants.PREFERENCE_ITEM_SETTINGS_TRANS_ID.value, item, body.toDataSet()) return outcome.case } @@ -2802,8 +2802,8 @@ class ActionDelegate { } List splitUriPath(String path) { - String rootPath = MenuItemConstants.PATH_SEPARATOR.attributeId - String[] splitPath = path.split(MenuItemConstants.PATH_SEPARATOR.attributeId) + String rootPath = MenuItemConstants.PATH_SEPARATOR.value + String[] splitPath = path.split(MenuItemConstants.PATH_SEPARATOR.value) if (splitPath.length == 0 && path == rootPath) { splitPath = [rootPath] } else if (splitPath.length == 0) { @@ -2824,11 +2824,11 @@ class ActionDelegate { options.putAll(splitPathList.collectEntries { [(it): new I18nString(it)] }) Case caseByPath = findCaseElastic("processIdentifier:$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER AND dataSet.nodePath.textValue.keyword:\"$path\"") - Set childrenIds = caseByPath.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.attributeId].value as Set + Set childrenIds = caseByPath.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.value].value as Set if (!childrenIds.isEmpty()) { for (String id : childrenIds) { Case childFolderCase = workflowService.findOne(id) - options.put(childFolderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_NAME.attributeId].value as String, new I18nString(childFolderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_NAME.attributeId].value as String)) + options.put(childFolderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_NAME.value].value as String, new I18nString(childFolderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_NAME.value].value as String)) } } @@ -2836,11 +2836,11 @@ class ActionDelegate { } String getCorrectedUri(String uncheckedPath) { - String rootPath = MenuItemConstants.PATH_SEPARATOR.attributeId + String rootPath = MenuItemConstants.PATH_SEPARATOR.value if (uncheckedPath == "") { return rootPath } - int lastIdx = uncheckedPath.lastIndexOf(MenuItemConstants.PATH_SEPARATOR.attributeId) + int lastIdx = uncheckedPath.lastIndexOf(MenuItemConstants.PATH_SEPARATOR.value) if (lastIdx == -1) { return rootPath } diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java index 472ba909497..de7f2489a27 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java @@ -112,7 +112,7 @@ public EntityModel importPetriNet( ImportPetriNetEventOutcome importPetriNetOutcome = service.importPetriNet(multipartFile.getInputStream(), release, (LoggedUser) auth.getPrincipal()); return EventOutcomeWithMessageResource.successMessage("Petri net " + multipartFile.getOriginalFilename() + " imported successfully", LocalisedEventOutcomeFactory.from(importPetriNetOutcome, locale)); - } catch (IOException e) { + } catch (IOException | IllegalArgumentException e) { log.error("Importing Petri net failed: ", e); return EventOutcomeWithMessageResource.errorMessage("IO error while importing Petri net"); } catch (MissingPetriNetMetaDataException e) { diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/menu/MenuItemBody.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/menu/MenuItemBody.java index dca2a07dfdb..9ad4f884ea4 100644 --- a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/menu/MenuItemBody.java +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/menu/MenuItemBody.java @@ -117,7 +117,7 @@ private static void putDataSetEntry(Map> dataSet, Me Map fieldMap = new LinkedHashMap<>(); fieldMap.put("type", fieldType.getName()); fieldMap.put("value", fieldValue); - dataSet.put(fieldId.getAttributeId(), fieldMap); + dataSet.put(fieldId.getValue(), fieldMap); } private static String sanitize(String input) { diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/menu/MenuItemConstants.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/menu/MenuItemConstants.java index 3e0a8868a12..d31b84d8f77 100644 --- a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/menu/MenuItemConstants.java +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/menu/MenuItemConstants.java @@ -61,9 +61,9 @@ public enum MenuItemConstants { PATH_SEPARATOR("/"); @Getter - private final String attributeId; + private final String value; - MenuItemConstants(String attributeId) { - this.attributeId = attributeId; + MenuItemConstants(String value) { + this.value = value; } } From d2424e87026d83652350b557851ee7c84960f507 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Mon, 23 Jun 2025 15:58:30 +0200 Subject: [PATCH 18/20] Add getter and setter for 'path' in MenuItemBody Introduced a @Getter annotation for the 'path' field to expose it externally and implemented a setter method to ensure the value is trimmed before assignment. These changes improve data encapsulation and ensure the 'path' field is managed consistently. --- .../engine/objects/workflow/domain/menu/MenuItemBody.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/menu/MenuItemBody.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/menu/MenuItemBody.java index 9ad4f884ea4..f123d4af9e2 100644 --- a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/menu/MenuItemBody.java +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/menu/MenuItemBody.java @@ -4,6 +4,7 @@ import com.netgrif.application.engine.objects.petrinet.domain.dataset.FieldType; import com.netgrif.application.engine.objects.workflow.domain.Case; import lombok.Data; +import lombok.Getter; import lombok.NoArgsConstructor; import jakarta.annotation.Nullable; @@ -26,6 +27,7 @@ public class MenuItemBody { private I18nString tabName; private String menuIcon = "filter_none"; private String tabIcon; + @Getter private String path; private String identifier; private Case filter; @@ -159,6 +161,10 @@ public Map> toDataSet(String parentId, String nodePa return toDataSet(parentId, nodePath, false); } + public void setPath(String path) { + this.path = path != null ? path.trim() : null; + } + private Map> toDataSet(String parentId, String nodePath, boolean ignoreParentId) { Map> dataSet = new LinkedHashMap<>(); From 6d3ad8a90a549e91aa6eda74aba4ad5482569005 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Mon, 23 Jun 2025 16:01:02 +0200 Subject: [PATCH 19/20] Handle null PetriNet imports gracefully in ImportHelper If the import service does not return a PetriNet object, a warning is logged, and an empty Optional is returned. This prevents potential null pointer issues and improves logging for debugging. --- .../application/engine/startup/ImportHelper.groovy | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/application-engine/src/main/groovy/com/netgrif/application/engine/startup/ImportHelper.groovy b/application-engine/src/main/groovy/com/netgrif/application/engine/startup/ImportHelper.groovy index 5e45678abb4..8aaef7a58e1 100644 --- a/application-engine/src/main/groovy/com/netgrif/application/engine/startup/ImportHelper.groovy +++ b/application-engine/src/main/groovy/com/netgrif/application/engine/startup/ImportHelper.groovy @@ -115,8 +115,13 @@ class ImportHelper { Optional createNet(String fileName, VersionType release = VersionType.MAJOR, LoggedUser author = userService.transformToLoggedUser(userService.getSystem())) { InputStream netStream = new ClassPathResource("petriNets/$fileName" as String).inputStream - PetriNet petriNet = petriNetService.importPetriNet(netStream, release, author).getNet() - log.info("Imported '${petriNet?.title?.defaultValue}' ['${petriNet?.identifier}', ${petriNet?.stringId}]") + def outcome = petriNetService.importPetriNet(netStream, release, author) + PetriNet petriNet = outcome.getNet() + if (petriNet == null) { + log.warn("Import of [$fileName] produced no PetriNet object") + return Optional.empty() + } + log.info("Imported '${petriNet.title?.defaultValue}' ['${petriNet.identifier}', ${petriNet.stringId}]") return Optional.of(petriNet) } From f0c23d0cfc0542468077007077481d1817b2e48b Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Tue, 24 Jun 2025 09:36:08 +0200 Subject: [PATCH 20/20] Refactor node path creation logic in ActionDelegate Simplified and optimized the `createNodePath` method by normalizing the input path before concatenation. This reduces redundancy and ensures consistent handling of path separators. --- .../domain/dataset/logic/action/ActionDelegate.groovy | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index 53164b5dd19..edb6892eaf6 100644 --- a/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -2119,11 +2119,10 @@ class ActionDelegate { } protected String createNodePath(String path, String identifier) { - if (path == MenuItemConstants.PATH_SEPARATOR.value) { - return path + identifier - } else { - return path + MenuItemConstants.PATH_SEPARATOR.value + identifier - } + String normalized = path.endsWith(MenuItemConstants.PATH_SEPARATOR.value) && path != MenuItemConstants.PATH_SEPARATOR.value + ? path.substring(0, path.length() - 1) + : path + return normalized + MenuItemConstants.PATH_SEPARATOR.value + identifier } protected Case getOrCreateFolderItem(String path) {