diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/couchdb/SummaryAwareRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/couchdb/SummaryAwareRepository.java index 077150e8a4..41c1604b65 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/couchdb/SummaryAwareRepository.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/couchdb/SummaryAwareRepository.java @@ -86,4 +86,14 @@ public Set getFullDocsById(Set docIds) { } return docs; } + + public List getFullDocsByListIds(SummaryType type, Collection ids) { + if (ids == null) { + return Collections.emptyList(); + } + + List documents = getDocsByListIds(ids); + + return makeSummaryFromFullDocs(type, documents); + } } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentDatabaseHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentDatabaseHandler.java index 60d7dcc87f..58627bcf61 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentDatabaseHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentDatabaseHandler.java @@ -2886,4 +2886,45 @@ public ByteBuffer getComponentReportDataStream(User user, boolean extendedByRele throw new SW360Exception(e.getMessage()); } } + + public List getReleaseByIds(List ids) { + return releaseRepository.getFullDocsByListIds(SummaryType.SHORT, ids); + } + + public List getReleaseRelationNetworkOfRelease(Release release, User user) { + ReleaseNode dependencyNetwork = new ReleaseNode(release.getId()); + getReleaseNodes(dependencyNetwork, user); + return Collections.singletonList(dependencyNetwork); + } + + private ReleaseNode getReleaseNodes(ReleaseNode releaseNode, User user) { + Release releaseById = null; + try { + releaseById = getAccessibleRelease(releaseNode.getReleaseId(), user); + List releaseList = new ArrayList<>(); + if (releaseById.getReleaseIdToRelationship() != null) { + releaseList = getAccessibleReleases(releaseById.getReleaseIdToRelationship().keySet().stream().collect(Collectors.toSet()), user); + } + List linkedReleasesJSON = new ArrayList<>(); + releaseNode.setMainlineState(MainlineState.OPEN.toString()); + releaseNode.setReleaseRelationship(ReleaseRelationship.CONTAINED.toString()); + releaseNode.setCreateOn(SW360Utils.getCreatedOn()); + releaseNode.setCreateBy(user.getEmail()); + releaseNode.setComment(""); + for (Release release : releaseList) { + ReleaseNode node = new ReleaseNode(release.getId()); + node.setMainlineState(MainlineState.OPEN.toString()); + node.setReleaseRelationship(ReleaseRelationship.CONTAINED.toString()); + node.setComment(""); + node.setCreateOn(SW360Utils.getCreatedOn()); + node.setCreateBy(user.getEmail()); + linkedReleasesJSON.add(getReleaseNodes(node, user)); + } + releaseNode.setReleaseLink(linkedReleasesJSON); + + } catch (TException e) { + log.error("Error when get Release: " + releaseNode.getReleaseId()); + } + return releaseNode; + } } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectDatabaseHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectDatabaseHandler.java index 91efc481e9..7f88b9cafd 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectDatabaseHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectDatabaseHandler.java @@ -10,6 +10,8 @@ */ package org.eclipse.sw360.datahandler.db; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.cloudant.client.api.CloudantClient; import com.google.common.annotations.VisibleForTesting; @@ -512,6 +514,13 @@ private boolean isDependenciesExists(Project project, User user) { isValidDependentIds = DatabaseHandlerUtil.isAllIdInSetExists(releaseIds, releaseRepository); } + if (SW360Constants.ENABLE_FLEXIBLE_PROJECT_RELEASE_RELATIONSHIP) { + if (project.isSetReleaseRelationNetwork()) { + Set releaseIds = SW360Utils.getReleaseIdsLinkedWithProject(project); + isValidDependentIds = DatabaseHandlerUtil.isAllIdInSetExists(releaseIds, releaseRepository); + } + } + if (isValidDependentIds && project.isSetLinkedProjects()) { Set projectIds = project.getLinkedProjects().keySet(); isValidDependentIds = DatabaseHandlerUtil.isAllIdInSetExists(projectIds, repository) && verifyLinkedProjectsAreAccessible(projectIds, user); @@ -852,8 +861,14 @@ private List iterateProjectRelationShips(Map visitedIds, int maxDepth, User user) { List out = new ArrayList<>(); for (Map.Entry entry : relations.entrySet()) { - Optional projectLinkOptional = createProjectLink(entry.getKey(), entry.getValue(), - parentNodeId, visitedIds, maxDepth, user); + Optional projectLinkOptional; + if (!SW360Constants.ENABLE_FLEXIBLE_PROJECT_RELEASE_RELATIONSHIP) { + projectLinkOptional = createProjectLink(entry.getKey(), entry.getValue(), + parentNodeId, visitedIds, maxDepth, user); + } else { + projectLinkOptional = createProjectLinkForDependencyNetwork(entry.getKey(), entry.getValue(), + parentNodeId, visitedIds, maxDepth, user, true); + } projectLinkOptional.ifPresent(out::add); } out.sort(Comparator.comparing(ProjectLink::getName).thenComparing(ProjectLink::getVersion)); @@ -1997,4 +2012,246 @@ public ByteBuffer downloadExcel(User user, boolean extendedByReleases, String to throw new SW360Exception(e.getMessage()); } } + + public List getReleaseLinksOfProjectNetWorkByTrace(List trace, String projectId, User user) throws TException{ + Project project = repository.get(projectId); + String releaseNetwork = project.getReleaseRelationNetwork(); + List listReleaseLinkJson; + List linkedReleases = new ArrayList<>(); + try { + listReleaseLinkJson = mapper.readValue(releaseNetwork, new TypeReference>() { + }); + ReleaseNode previousNode = listReleaseLinkJson.get(Integer.parseInt(trace.get(0))); + for (int i = 1; i < trace.size(); i++){ + previousNode = previousNode.getReleaseLink().get(Integer.parseInt(trace.get(i))); + } + linkedReleases = convertFromReleaseNodeToReleaseLink(previousNode.getReleaseLink(), projectId, user, previousNode.getReleaseId(), trace.size()); + } catch (JsonProcessingException e) { + log.error("JsonProcessingException: " + e); + } + return linkedReleases; + } + + protected List convertFromReleaseNodeToReleaseLink(List releaseLinkJSONs, String projectId, User user, String parentId, int layer) throws TException { + List releaseLinks = new ArrayList<>(); + int index = 0; + for (ReleaseNode releaseNode : releaseLinkJSONs) { + Release releaseById = componentDatabaseHandler.getRelease(releaseNode.getReleaseId(), user); + Component componentById = componentDatabaseHandler.getComponent(releaseById.getComponentId(), user); + + ReleaseLink releaseLink = new ReleaseLink(); + releaseLink.setId(releaseNode.getReleaseId()); + releaseLink.setReleaseRelationship(ReleaseRelationship.valueOf(releaseNode.getReleaseRelationship())); + releaseLink.setMainlineState(MainlineState.valueOf(releaseNode.getMainlineState())); + releaseLink.setComment(releaseNode.getComment()); + releaseLink.setHasSubreleases(!releaseNode.getReleaseLink().isEmpty()); + releaseLink.setName(releaseById.getName()); + releaseLink.setVersion(releaseById.getVersion()); + releaseLink.setLongName(SW360Utils.printFullname(releaseById)); + releaseLink.setClearingState(releaseById.getClearingState()); + releaseLink.setComponentType(componentById.getComponentType()); + releaseLink.setLicenseIds(releaseById.getMainLicenseIds()); + releaseLink.setOtherLicenseIds(releaseById.getOtherLicenseIds()); + releaseLink.setAccessible(componentDatabaseHandler.isReleaseActionAllowed(releaseById, user, RequestedAction.READ)); + releaseLink.setNodeId(releaseById.getId() + "_" + UUID.randomUUID()); + releaseLink.setParentNodeId(parentId); + releaseLink.setLayer(layer); + releaseLink.setProjectId(projectId); + releaseLink.setIndex(index); + releaseLink.setReleaseMainLineState(releaseById.getMainlineState()); + releaseLink.setAttachments(releaseById.getAttachments() != null ? Lists.newArrayList(releaseById.getAttachments()) : Collections.emptyList()); + if (releaseById.getVendor() != null) { + releaseLink.setVendor(releaseById.getVendor().getFullname()); + } else { + releaseLink.setVendor(""); + } + index++; + releaseLinks.add(releaseLink); + } + return releaseLinks; + } + + public List> getClearingStateForDependencyNetworkListView(String projectId, User user, boolean isInaccessibleLinkMasked) + throws SW360Exception { + Project projectById = getProjectById(projectId, user); + List> clearingStatusList = new ArrayList<>(); + LinkedHashMap projectOrigin = new LinkedHashMap<>(); + projectOrigin.put(projectId, SW360Utils.printName(projectById)); + LinkedHashMap releaseOrigin = new LinkedHashMap<>(); + Map linkedProjects = projectById.getLinkedProjects(); + + String releaseNetwork = projectById.getReleaseRelationNetwork(); + List listReleaseLinkJson; + try { + listReleaseLinkJson = mapper.readValue(releaseNetwork, new TypeReference>() { + }); + flattenLinkedReleaseOfRelease(listReleaseLinkJson, projectOrigin, releaseOrigin, clearingStatusList, user, isInaccessibleLinkMasked); + } catch (JsonProcessingException e) { + log.error("JsonProcessingException: " + e); + } + + if (linkedProjects != null && !linkedProjects.isEmpty()) { + flattenDependencyNetworkForLinkedProject(linkedProjects, projectOrigin, releaseOrigin, clearingStatusList, + user, isInaccessibleLinkMasked); + } + + return clearingStatusList; + } + + private void flattenLinkedReleaseOfRelease(List listReleaseLinkJson, + LinkedHashMap projectOrigin, LinkedHashMap releaseOrigin, + List> clearingStatusList, User user, boolean isInaccessibleLinkMasked) { + listReleaseLinkJson.forEach(rl -> wrapTException(() -> { + String relation = ThriftEnumUtils.enumToString(ReleaseRelationship.valueOf(rl.getReleaseRelationship())); + String projectMailLineState = ThriftEnumUtils.enumToString(MainlineState.valueOf(rl.getMainlineState())); + String comment = rl.getComment(); + String releaseId = rl.getReleaseId(); + if (releaseOrigin.containsKey(releaseId)) + return; + Release rel = componentDatabaseHandler.getRelease(releaseId, user); + List listLinkedRelease = rl.getReleaseLink(); + if (!isInaccessibleLinkMasked || componentDatabaseHandler.isReleaseActionAllowed(rel, user, RequestedAction.READ)) { + releaseOrigin.put(releaseId, SW360Utils.printName(rel)); + Map row = createReleaseCSRow(relation, projectMailLineState, rel, clearingStatusList, user, comment); + if (CommonUtils.isNotEmpty(listLinkedRelease)) { + flattenLinkedReleaseOfRelease(listLinkedRelease, projectOrigin, releaseOrigin, + clearingStatusList, user, isInaccessibleLinkMasked); + } + releaseOrigin.remove(releaseId); + row.put("projectOrigin", String.join(" -> ", projectOrigin.values())); + row.put("releaseOrigin", String.join(" -> ", releaseOrigin.values())); + } else { + Map row = createInaccessibleReleaseCSRow(clearingStatusList); + row.put("projectOrigin", ""); + row.put("releaseOrigin", ""); + } + })); + } + + private void flattenDependencyNetworkForLinkedProject(Map linkedProjects, + LinkedHashMap projectOrigin, LinkedHashMap releaseOrigin, + List> clearingStatusList, User user, boolean isInaccessibleLinkMasked) { + + linkedProjects.entrySet().stream().forEach(lp -> wrapTException(() -> { + String projId = lp.getKey(); + String relation = ThriftEnumUtils.enumToString(lp.getValue().getProjectRelationship()); + if (projectOrigin.containsKey(projId)) + return; + Project linkedProjectById = getProjectById(projId, user); + projectOrigin.put(projId, SW360Utils.printName(linkedProjectById)); + Map row = createProjectCSRow(relation, linkedProjectById, clearingStatusList); + Map subprojects = linkedProjectById.getLinkedProjects(); + + String releaseNetwork = linkedProjectById.getReleaseRelationNetwork(); + List listReleaseLinkJson; + try { + listReleaseLinkJson = mapper.readValue(releaseNetwork, new TypeReference>() { + }); + flattenLinkedReleaseOfRelease(listReleaseLinkJson, projectOrigin, releaseOrigin, clearingStatusList, user, isInaccessibleLinkMasked); + } catch (JsonProcessingException e) { + log.error("JsonProcessingException: " + e); + } + + if (subprojects != null && !subprojects.isEmpty()) { + flattenClearingStatusForLinkedProject(subprojects, projectOrigin, releaseOrigin, clearingStatusList, + user, isInaccessibleLinkMasked); + } + + projectOrigin.remove(projId); + row.put("projectOrigin", String.join(" -> ", projectOrigin.values())); + })); + } + + private Optional createProjectLinkForDependencyNetwork(String id, ProjectProjectRelationship projectProjectRelationship, String parentNodeId, + Deque visitedIds, int maxDepth, User user, boolean withRelease) { + ProjectLink projectLink = null; + if (!visitedIds.contains(id) && (maxDepth < 0 || visitedIds.size() < maxDepth)) { + visitedIds.push(id); + Project project = repository.get(id); + if (project != null + && (user == null || !makePermission(project, user).isActionAllowed(RequestedAction.READ))) { + log.error("User " + (user == null ? "" : user.getEmail()) + + " requested not accessible project " + printName(project)); + project = null; + } + if (project != null) { + projectLink = new ProjectLink(id, project.name); + if (withRelease) { + if (project.getReleaseRelationNetwork() != null && project.getReleaseRelationNetwork().length() > 0 && (maxDepth < 0 || visitedIds.size() < maxDepth)) { + String releaseNetwork = project.getReleaseRelationNetwork(); + List listReleaseLinkJson; + List linkedReleases = new ArrayList<>(); + try { + listReleaseLinkJson = mapper.readValue(releaseNetwork, new TypeReference<>() { + }); + linkedReleases = convertFromReleaseNodeToReleaseLink(listReleaseLinkJson, id, user, "", 0); + } catch (JsonProcessingException e) { + log.error("JsonProcessingException: " + e); + } catch (TException e) { + log.error(e.getMessage()); + } + projectLink.setLinkedReleases(nullToEmptyList(linkedReleases)); + } + } else { + projectLink.setLinkedReleases(Collections.emptyList()); + } + projectLink + .setNodeId(generateNodeId(id)) + .setParentNodeId(parentNodeId) + .setRelation(projectProjectRelationship.getProjectRelationship()) + .setEnableSvm(projectProjectRelationship.isEnableSvm()) + .setVersion(project.getVersion()) + .setState(project.getState()) + .setProjectType(project.getProjectType()) + .setClearingState(project.getClearingState()) + .setTreeLevel(visitedIds.size() - 1); + if (project.isSetLinkedProjects()) { + List subprojectLinks = iterateProjectRelationShips(project.getLinkedProjects(), + projectLink.getNodeId(), visitedIds, maxDepth, user, withRelease); + projectLink.setSubprojects(subprojectLinks); + } + } else { + log.error("Broken ProjectLink in project with id: " + parentNodeId + ". Linked project with id " + id + " was not found"); + } + visitedIds.pop(); + } + return Optional.ofNullable(projectLink); + } + + public List getLinkedProjectsWithoutReleases(Map relations, boolean depth, User user) { + List out; + + Deque visitedIds = new ArrayDeque<>(); + out = iterateProjectRelationShips(relations, null, visitedIds, depth ? -1 : 1, user, false); + + return out; + } + + private List iterateProjectRelationShips(Map relations, + String parentNodeId, Deque visitedIds, int maxDepth, + User user, boolean withRelease) { + List out = new ArrayList<>(); + for (Map.Entry entry : relations.entrySet()) { + Optional projectLinkOptional; + if (!SW360Constants.ENABLE_FLEXIBLE_PROJECT_RELEASE_RELATIONSHIP) { + projectLinkOptional = createProjectLink(entry.getKey(), entry.getValue(), + parentNodeId, visitedIds, maxDepth, user); + } else { + projectLinkOptional = createProjectLinkForDependencyNetwork(entry.getKey(), entry.getValue(), + parentNodeId, visitedIds, maxDepth, user, withRelease); + } + projectLinkOptional.ifPresent(out::add); + } + out.sort(Comparator.comparing(ProjectLink::getName).thenComparing(ProjectLink::getVersion)); + return out; + } + + public List getLinkedProjectsWithoutReleases(Project project, boolean deep, User user) { + Deque visitedIds = new ArrayDeque<>(); + + Map fakeRelations = new HashMap<>(); + fakeRelations.put(project.isSetId() ? project.getId() : DUMMY_NEW_PROJECT_ID, new ProjectProjectRelationship(ProjectRelationship.UNKNOWN)); + List out = iterateProjectRelationShips(fakeRelations, null, visitedIds, deep ? -1 : 2, user, false); + return out; + } } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectSearchHandler.java index 356909a3a5..03c49e1446 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectSearchHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectSearchHandler.java @@ -22,7 +22,11 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Collections; import java.util.function.Supplier; +import java.util.stream.Collectors; import static org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector.prepareWildcardQuery; @@ -75,6 +79,9 @@ public class ProjectSearchHandler { " for(var [key, value] in doc.additionalData) {" + " ret.add(doc.additionalData[key], {\"field\": \"additionalData\"} );" + " }" + + " if(doc.releaseRelationNetwork !== undefined && doc.releaseRelationNetwork != null && doc.releaseRelationNetwork.length > 0) { "+ + " ret.add(doc.releaseRelationNetwork, {\"field\": \"releaseRelationNetwork\"} );" + + " }" + " return ret;" + "}"); @@ -95,4 +102,34 @@ public List search(String searchText) { return connector.searchView(Project.class, luceneSearchView, prepareWildcardQuery(searchText)); } + public List search(String text, final Map> subQueryRestrictions) { + return connector.searchViewWithRestrictions(Project.class, luceneSearchView, text, subQueryRestrictions); + } + + public Set searchByReleaseId(String id, User user) { + return searchByReleaseIds(Collections.singleton(id), user); + } + + public Set searchByReleaseIds(Set ids, User user) { + Map> filterMap = getFilterMapForSetReleaseIds(ids); + List projectsByReleaseIds; + if (user != null) { + projectsByReleaseIds = connector.searchProjectViewWithRestrictionsAndFilter(luceneSearchView, null, filterMap, user); + } else { + projectsByReleaseIds = connector.searchViewWithRestrictions(Project.class, luceneSearchView, null, filterMap); + } + return new HashSet<>(projectsByReleaseIds); + } + + private static Map> getFilterMapForSetReleaseIds(Set releaseIds) { + Map> filterMap = new HashMap<>(); + Set values = new HashSet<>(); + for(String releaseId : releaseIds) { + values.add("\"releaseId\":\"" + releaseId + "\""); + values.add("\"releaseId\": \"" + releaseId + "\""); + } + values = values.stream().map(LuceneAwareDatabaseConnector::prepareWildcardQuery).collect(Collectors.toSet()); + filterMap.put(Project._Fields.RELEASE_RELATION_NETWORK.getFieldName(), values); + return filterMap; + } } diff --git a/backend/src/src-components/src/main/java/org/eclipse/sw360/components/ComponentHandler.java b/backend/src/src-components/src/main/java/org/eclipse/sw360/components/ComponentHandler.java index 3cd038d6b5..98c9560103 100644 --- a/backend/src/src-components/src/main/java/org/eclipse/sw360/components/ComponentHandler.java +++ b/backend/src/src-components/src/main/java/org/eclipse/sw360/components/ComponentHandler.java @@ -20,7 +20,9 @@ import org.eclipse.sw360.datahandler.thrift.components.ComponentService; import org.eclipse.sw360.datahandler.thrift.components.Release; import org.eclipse.sw360.datahandler.thrift.components.ReleaseLink; +import org.eclipse.sw360.datahandler.thrift.components.ReleaseNode; import org.eclipse.sw360.datahandler.thrift.users.User; +import org.eclipse.sw360.datahandler.thrift.users.RequestedAction; import org.ektorp.http.HttpClient; import com.cloudant.client.api.CloudantClient; @@ -733,4 +735,21 @@ public ByteBuffer getComponentReportDataStream(User user, boolean extendedByRele public String getComponentReportInEmail(User user, boolean extendedByReleases) throws TException { return handler.getComponentReportInEmail(user,extendedByReleases); } + + @Override + public boolean isReleaseActionAllowed(Release release, User user, RequestedAction action) { + return handler.isReleaseActionAllowed(release, user, action); + } + + @Override + public List getReleasesByListIds(List ids, User user) throws TException { + assertUser(user); + assertNotNull(ids); + return handler.getReleaseByIds(ids); + } + + @Override + public List getReleaseRelationNetworkOfRelease(Release release, User user) { + return handler.getReleaseRelationNetworkOfRelease(release, user); + } } diff --git a/backend/src/src-projects/src/main/java/org/eclipse/sw360/projects/ProjectHandler.java b/backend/src/src-projects/src/main/java/org/eclipse/sw360/projects/ProjectHandler.java index 1c3499fd9e..2004c3e85e 100644 --- a/backend/src/src-projects/src/main/java/org/eclipse/sw360/projects/ProjectHandler.java +++ b/backend/src/src-projects/src/main/java/org/eclipse/sw360/projects/ProjectHandler.java @@ -15,6 +15,7 @@ import org.apache.thrift.TException; import org.eclipse.sw360.datahandler.common.CommonUtils; import org.eclipse.sw360.datahandler.common.DatabaseSettings; +import org.eclipse.sw360.datahandler.common.SW360Constants; import org.eclipse.sw360.datahandler.db.ProjectDatabaseHandler; import org.eclipse.sw360.datahandler.db.ProjectSearchHandler; import org.eclipse.sw360.datahandler.thrift.AddDocumentRequestSummary; @@ -24,6 +25,7 @@ import org.eclipse.sw360.datahandler.thrift.SW360Exception; import org.eclipse.sw360.datahandler.thrift.attachments.Attachment; import org.eclipse.sw360.datahandler.thrift.components.ReleaseClearingStatusData; +import org.eclipse.sw360.datahandler.thrift.components.ReleaseLink; import org.eclipse.sw360.datahandler.thrift.projects.ClearingRequest; import org.eclipse.sw360.datahandler.thrift.projects.ProjectProjectRelationship; import org.eclipse.sw360.datahandler.thrift.projects.Project; @@ -150,11 +152,17 @@ public ProjectData searchByType(String type, User user) throws SW360Exception { @Override public Set searchByReleaseId(String id, User user) throws TException { + if (SW360Constants.ENABLE_FLEXIBLE_PROJECT_RELEASE_RELATIONSHIP) { + return searchHandler.searchByReleaseId(id, user); + } return handler.searchByReleaseId(id, user); } @Override public Set searchByReleaseIds(Set ids, User user) throws TException { + if (SW360Constants.ENABLE_FLEXIBLE_PROJECT_RELEASE_RELATIONSHIP) { + return searchHandler.searchByReleaseIds(ids, user); + } return handler.searchByReleaseId(ids, user); } @@ -500,4 +508,34 @@ public String getReportInEmail(User user, boolean extendedByReleases) throws TException { return handler.getReportInEmail(user, extendedByReleases); } + + @Override + public List getReleaseLinksOfProjectNetWorkByTrace(String projectId, List trace, User user) throws TException { + return handler.getReleaseLinksOfProjectNetWorkByTrace(trace, projectId, user); + } + + @Override + public List> getAccessibleDependencyNetworkForListView(String projectId, User user) throws SW360Exception { + assertNotNull(projectId); + return handler.getClearingStateForDependencyNetworkListView(projectId, user, true); + } + + @Override + public List refineSearchWithoutUser(String text, Map> subQueryRestrictions) { + return searchHandler.search(text, subQueryRestrictions); + } + + @Override + public List getLinkedProjectsWithoutReleases(Map relations, boolean depth, User user) throws TException { + assertNotNull(relations); + assertUser(user); + + return handler.getLinkedProjectsWithoutReleases(relations, depth, user); + } + + @Override + public List getLinkedProjectsOfProjectWithoutReleases(Project project, boolean deep, User user) throws TException { + assertNotNull(project); + return handler.getLinkedProjectsWithoutReleases(project, deep, user); + } } diff --git a/frontend/sw360-portlet/bnd.bnd b/frontend/sw360-portlet/bnd.bnd index 8af4f15bb2..03018e5a67 100644 --- a/frontend/sw360-portlet/bnd.bnd +++ b/frontend/sw360-portlet/bnd.bnd @@ -72,7 +72,9 @@ Import-Package: \ com.cloudant.*, \ org.osgi.service.cm, \ org.osgi.service.component.*, \ - org.slf4j + org.slf4j, \ + com.fasterxml.jackson.core.*,\ + com.google.gson.* Conditional-Package: \ org.apache.http.*, \ diff --git a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/common/PortalConstants.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/common/PortalConstants.java index 17b550f435..9dd0ab8908 100644 --- a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/common/PortalConstants.java +++ b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/common/PortalConstants.java @@ -769,6 +769,39 @@ public class PortalConstants { public static final String ACTUAL_PACKAGE_INFO = "actual_PackageInfo"; public static final Set SET_RELATIONSHIP_TYPE; + //! Specialized keys for Flexible project and releases relationship configuration + public static final String RELEASE_ID_ARRAY = "releaseIdArray[]"; + public static final String RELEASES_WITH_SAME_COMPONENT_ID = "releaseWithSameComponentId"; + public static final String CURRENT_NETWORK = "currentNetwork"; + public static final String CHECK_DIFF_DEPENDENCY_NETWORK_WITH_RELEASES_RELATIONSHIP = "checkDiffDependencyNetworkWithReleasesRelationship"; + public static final String RESTRICTED_RELEASE = "Restricted release"; + public static final String GET_HTML_RELEASE_ROWS = "getHtmlReleaseRows"; + public static final String DEFAULT_RELEASE_RELATION_NETWORK = "[]"; + public static final String ATTACHMENT_USAGE_ON_CLICK = "attachmentUsageOnClick"; + public static final String SUB_PROJECTS_LINK_TRANSITIVE = "subProjectsLinkTransitive"; + public static final String LOGIN_USER = "loginUser"; + public static final String IS_FLEXIBLE_PROJECT_RELEASE_RELATIONSHIP_ENABLED = "isFlexibleProjectReleaseRelationshipEnabled"; + public static final String NETWORK_PARENT_BRANCH_ID = "networkParentBranchId"; + public static final String NETWORK_RELEASE_LIST = "networkReleaseList"; + public static final String NETWORK_TOTAL_INACCESSIBLE_ROWS = "netWorkTotalInAccessibleRow"; + public static final String DEPENDENCY_NETWORK_LIST = "dependencyNetworkList"; + public static final String DEPENDENCY_NETWORK_ON_LOAD = "dependencyNetworkOnLoad"; + public static final String IS_OBLIGATION_PRESENT = "isObligationPresent"; + public static final String CREATE_LINKED_RELEASE_ROW = "createLinkedReleaseRow"; + public static final String PARENT_NODE_ID = "parentNodeIds[]"; + public static final String LAYER = "layer[]"; + public static final String RELEASE_RELATION_SHIP = "releaseRelationShip[]"; + public static final String MAINLINE_STATE = "mainlineState[]"; + public static final String INDEXES = "indexes[]"; + public static final String COMMENTS = "comments[]"; + public static final String FIND_LINKED_RELEASE_OF_NODE = "findLinkedReleaseOfNode"; + public static final String RELEASES_IN_NETWORK = "releasesInNetwork"; + public static final String NUMBER_LINKED_RELEASE = "numberLinkedRelease"; + public static final String TOTAL_RELEASE_COUNT = "totalReleaseCount"; + public static final String CHECK_RELEASE_EXIST = "checkReleaseExist"; + public static final String RELEASE_USAGE = "releaseUsage"; + public static final String CYCLIC_LINKED_RELEASE_PATH = "cyclicLinkedReleasePath"; + public static final String CHILD_RELEASE_ID_ARRAY = "childReleaseId[]"; static { Properties props = CommonUtils.loadProperties(PortalConstants.class, PROPERTIES_FILE_PATH); diff --git a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/LinkedReleasesAndProjectsAwarePortlet.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/LinkedReleasesAndProjectsAwarePortlet.java index 15f11b4467..b4a6cd35d4 100644 --- a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/LinkedReleasesAndProjectsAwarePortlet.java +++ b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/LinkedReleasesAndProjectsAwarePortlet.java @@ -13,6 +13,7 @@ import org.apache.logging.log4j.Logger; import org.apache.thrift.TException; import org.eclipse.sw360.datahandler.common.CommonUtils; +import org.eclipse.sw360.datahandler.common.SW360Constants; import org.eclipse.sw360.datahandler.common.SW360Utils; import org.eclipse.sw360.datahandler.common.ThriftEnumUtils; import org.eclipse.sw360.datahandler.thrift.MainlineState; @@ -43,6 +44,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.Arrays; import java.util.function.Function; import java.util.stream.Collectors; @@ -95,9 +97,19 @@ protected void dealWithLinkedObjects(ResourceRequest request, ResourceResponse r if (PortalConstants.LOAD_LINKED_PROJECTS_ROWS.equals(action)) { boolean overrideToRelease = Boolean.parseBoolean(request.getParameter("overrideToRelease")); if (overrideToRelease) { - loadLinkedReleasesRows(request, response); - include("/html/utils/ajax/linkedReleasesClearingStatusRows.jsp", request, response, - PortletRequest.RESOURCE_PHASE); + if (!SW360Constants.ENABLE_FLEXIBLE_PROJECT_RELEASE_RELATIONSHIP) { + loadLinkedReleasesRows(request, response); + include("/html/utils/ajax/linkedReleasesClearingStatusRows.jsp", request, response, + PortletRequest.RESOURCE_PHASE); + } else { + try { + loadLinkedReleaseOfProject(request); + include("/html/utils/ajax/linkedReleaseInNetworkRows.jsp", request, response, + PortletRequest.RESOURCE_PHASE); + } catch (TException e) { + log.error("Error when create new release row for tree view"); + } + } return; } serveLoadLinkedProjectsRows(request, response); @@ -177,7 +189,12 @@ protected List createLinkedProjects(Project project, } protected void putDirectlyLinkedProjectsInRequest(PortletRequest request, Project project, User user) { - final Collection linkedProjects = SW360Utils.getLinkedProjects(project, false, thriftClients, log, user); + Collection linkedProjects; + if (!SW360Constants.ENABLE_FLEXIBLE_PROJECT_RELEASE_RELATIONSHIP) { + linkedProjects = SW360Utils.getLinkedProjects(project, false, thriftClients, log, user); + } else { + linkedProjects = SW360Utils.getLinkedProjectWithoutReleases(project, false, thriftClients, log, user); + } List secondLevelLinks = linkedProjects .stream() .map(ProjectLink::getSubprojects) @@ -200,8 +217,13 @@ public Function createProjectLinkMapper(Function fillMainLineState(Set releaseIds, ComponentSe })); return relMainLineState; } + + protected void loadLinkedReleaseOfProject(ResourceRequest request) throws TException { + String projectId = request.getParameter("projectId"); + String[] trace = request.getParameterValues("trace[]"); + String branchId = request.getParameter(PortalConstants.NETWORK_PARENT_BRANCH_ID); + final User user = UserCacheHolder.getUserFromRequest(request); + try { + ProjectService.Iface projectClient = thriftClients.makeProjectClient(); + List linkedReleases = projectClient.getReleaseLinksOfProjectNetWorkByTrace(projectId, Arrays.asList(trace), user); + request.setAttribute(PortalConstants.NETWORK_PARENT_BRANCH_ID, branchId); + request.setAttribute(PortalConstants.PARENT_SCOPE_GROUP_ID, request.getParameter(PortalConstants.PARENT_SCOPE_GROUP_ID)); + request.setAttribute(PortalConstants.NETWORK_RELEASE_LIST, linkedReleases); + int totalInaccessibleRow = 0; + for (ReleaseLink link : linkedReleases) { + if (!link.isAccessible()) { + totalInaccessibleRow++; + } + } + request.setAttribute(PortalConstants.NETWORK_TOTAL_INACCESSIBLE_ROWS, totalInaccessibleRow); + } catch (TException e) { + log.error("Error getting releases!", e); + request.setAttribute(PortalConstants.NETWORK_PARENT_BRANCH_ID, branchId); + request.setAttribute(PortalConstants.PARENT_SCOPE_GROUP_ID, request.getParameter(PortalConstants.PARENT_SCOPE_GROUP_ID)); + request.setAttribute(PortalConstants.NETWORK_RELEASE_LIST, Collections.emptyList()); + request.setAttribute(PortalConstants.NETWORK_TOTAL_INACCESSIBLE_ROWS, 0); + } + } + + protected void prepareLinkedProjectDependencyNetwork(ResourceRequest request) throws PortletException { + final User user = UserCacheHolder.getUserFromRequest(request); + String branchId = request.getParameter(PortalConstants.NETWORK_PARENT_BRANCH_ID); + Optional projectIdOpt = getProjectIdFromBranchId(branchId); + request.setAttribute(PortalConstants.NETWORK_PARENT_BRANCH_ID, branchId); + final Project project; + if (projectIdOpt.isPresent()) { + try { + ProjectService.Iface client = thriftClients.makeProjectClient(); + project = client.getProjectById(projectIdOpt.get(), user); + } catch (TException e) { + log.error("Error getting projects!", e); + throw new PortletException("cannot load project " + projectIdOpt.get(), e); + } + String parentProjectPath = request.getParameter(PortalConstants.PARENT_PROJECT_PATH); + if (parentProjectPath != null) { + request.setAttribute(PortalConstants.PARENT_PROJECT_PATH, + parentProjectPath.concat(":").concat(projectIdOpt.get())); + } + } else { + project = new Project(); + } + List mappedProjectLinks = createLinkedProjects(project, user); + + mappedProjectLinks = sortProjectLink(mappedProjectLinks); + request.setAttribute(PROJECT_LIST, mappedProjectLinks); + request.setAttribute(PortalConstants.PARENT_SCOPE_GROUP_ID, request.getParameter(PortalConstants.PARENT_SCOPE_GROUP_ID)); + } } diff --git a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/components/ComponentPortlet.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/components/ComponentPortlet.java index 0e747b224e..751becd10c 100644 --- a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/components/ComponentPortlet.java +++ b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/components/ComponentPortlet.java @@ -558,8 +558,12 @@ private void serveDeleteComponent(ResourceRequest request, ResourceResponse resp } private void serveDeleteRelease(PortletRequest request, ResourceResponse response) throws IOException { - final RequestStatus requestStatus = ComponentPortletUtils.deleteRelease(request, log); - serveRequestStatus(request, response, requestStatus, "Problem removing release", log); + if (!SW360Constants.ENABLE_FLEXIBLE_PROJECT_RELEASE_RELATIONSHIP) { + final RequestStatus requestStatus = ComponentPortletUtils.deleteRelease(request, log); + serveRequestStatus(request, response, requestStatus, "Problem removing release", log); + } else { + serveDeleteReleaseWithDependencyNetwork(request, response); + } } private void exportExcel(ResourceRequest request, ResourceResponse response) { @@ -956,7 +960,11 @@ private void prepareComponentEdit(RenderRequest request) { addEditDocumentMessage(request, permissions, documentState); Set releaseIds = SW360Utils.getReleaseIds(component.getReleases()); - setUsingDocs(request, user, client, releaseIds); + if (!SW360Constants.ENABLE_FLEXIBLE_PROJECT_RELEASE_RELATIONSHIP) { + setUsingDocs(request, user, client, releaseIds); + } else { + setUsingDocsWithFlexibleDependencyNetwork(request, user, client, releaseIds); + } } catch (TException e) { if (e instanceof SW360Exception) { @@ -980,7 +988,11 @@ private void prepareComponentEdit(RenderRequest request) { } request.setAttribute(COMPONENT,component); PortletUtils.setCustomFieldsEdit(request, user, component); - setUsingDocs(request, user, null, component.getReleaseIds()); + if (!SW360Constants.ENABLE_FLEXIBLE_PROJECT_RELEASE_RELATIONSHIP) { + setUsingDocs(request, user, null, component.getReleaseIds()); + } else { + setUsingDocsWithFlexibleDependencyNetwork(request, user, thriftClients.makeComponentClient(), component.getReleaseIds()); + } setAttachmentsInRequest(request, component); SessionMessages.add(request, "request_processed", LanguageUtil.get(resourceBundle,"new.component")); } @@ -1020,7 +1032,11 @@ private void prepareReleaseEdit(RenderRequest request, RenderResponse response) putDirectlyLinkedReleaseRelationsWithAccessibilityInRequest(request, release, user); Map permissions = release.getPermissions(); DocumentState documentState = release.getDocumentState(); - setUsingDocs(request, releaseId, user, client); + if (!SW360Constants.ENABLE_FLEXIBLE_PROJECT_RELEASE_RELATIONSHIP) { + setUsingDocs(request, releaseId, user, client); + } else { + setUsingDocsWithFlexibleDependencyNetwork(request, releaseId, user, client); + } addEditDocumentMessage(request, permissions, documentState); if (isNullOrEmpty(id)) { @@ -1059,7 +1075,11 @@ private void prepareReleaseEdit(RenderRequest request, RenderResponse response) request.setAttribute(RELEASE, release); putDirectlyLinkedReleaseRelationsWithAccessibilityInRequest(request, release, user); setAttachmentsInRequest(request, release); - setUsingDocs(request, null, user, client); + if (!SW360Constants.ENABLE_FLEXIBLE_PROJECT_RELEASE_RELATIONSHIP) { + setUsingDocs(request, null, user, client); + } else { + setUsingDocsWithFlexibleDependencyNetwork(request, null, user, client); + } SessionMessages.add(request, "request_processed", LanguageUtil.get(resourceBundle,"new.license")); } } @@ -1241,7 +1261,11 @@ private void prepareReleaseDuplicate(RenderRequest request, RenderResponse respo request.setAttribute(COMPONENT, component); request.setAttribute(RELEASE_LIST, Collections.emptyList()); request.setAttribute(TOTAL_INACCESSIBLE_ROWS, 0); - setUsingDocs(request, null, user, client); + if (!SW360Constants.ENABLE_FLEXIBLE_PROJECT_RELEASE_RELATIONSHIP) { + setUsingDocs(request, null, user, client); + } else { + setUsingDocsWithFlexibleDependencyNetwork(request, null, user, client); + } release.unsetExternalIds(); request.setAttribute(RELEASE, release); request.setAttribute(PortalConstants.ATTACHMENTS, Collections.emptySet()); @@ -1715,7 +1739,12 @@ private void prepareDetailView(RenderRequest request, RenderResponse response) { setAttachmentsInRequest(request, component); Set releaseIds = SW360Utils.getReleaseIds(component.getReleases()); - setUsingDocs(request, user, client, releaseIds); + if (!SW360Constants.ENABLE_FLEXIBLE_PROJECT_RELEASE_RELATIONSHIP) { + setUsingDocs(request, user, client, releaseIds); + } else { + setUsingDocsWithFlexibleDependencyNetwork(request, user, client, releaseIds); + } + if (IS_COMPONENT_VISIBILITY_RESTRICTION_ENABLED) { request.setAttribute(IS_USER_ALLOWED_TO_MERGE, PermissionUtils.isUserAtLeast(UserGroup.ADMIN, user)); } else { @@ -1814,7 +1843,12 @@ private void prepareReleaseDetailView(RenderRequest request, RenderResponse resp setAttachmentsInRequest(request, release); setSpdxAttachmentsInRequest(request, release); - setUsingDocs(request, releaseId, user, client); + if (!SW360Constants.ENABLE_FLEXIBLE_PROJECT_RELEASE_RELATIONSHIP) { + setUsingDocs(request, releaseId, user, client); + } else { + setUsingDocsWithFlexibleDependencyNetwork(request, releaseId, user, client); + } + putDirectlyLinkedReleaseRelationsWithAccessibilityInRequest(request, release, user); if (IS_COMPONENT_VISIBILITY_RESTRICTION_ENABLED) { @@ -2824,4 +2858,49 @@ private String sortAndConcat(Set strings) { return CommonUtils.COMMA_JOINER.join(strings.stream().sorted().collect(Collectors.toList())); } } + + private void serveDeleteReleaseWithDependencyNetwork(PortletRequest request, ResourceResponse response) { + String releaseId = request.getParameter(PortalConstants.RELEASE_ID); + Set releaseIdToSet = Collections.singleton(releaseId); + if (SW360Utils.getUsingProjectByReleaseIds(releaseIdToSet, null).size() > 0) { + serveRequestStatus(request, response, RequestStatus.IN_USE, "Problem removing release", log); + } else { + final RequestStatus requestStatus = ComponentPortletUtils.deleteRelease(request, log); + serveRequestStatus(request, response, requestStatus, "Problem removing release", log); + } + } + + private void setUsingDocsWithFlexibleDependencyNetwork(RenderRequest request, User user, ComponentService.Iface client, Set releaseIds) { + Set usingComponentsForComponent = null; + List usingProjectInDependencyNetwork; + if (CommonUtils.isNotEmpty(releaseIds)) { + try { + usingComponentsForComponent = client.getUsingComponentsWithAccessibilityForComponent(releaseIds, user); + usingProjectInDependencyNetwork = SW360Utils.getUsingProjectByReleaseIds(releaseIds, user); + request.setAttribute(USING_PROJECTS, new HashSet<>(usingProjectInDependencyNetwork)); + request.setAttribute(ALL_USING_PROJECTS_COUNT, SW360Utils.getUsingProjectByReleaseIds(releaseIds, null).size()); + } catch (TException e) { + log.error("Problem filling using docs", e); + } + } else { + request.setAttribute(USING_PROJECTS, Collections.emptySet()); + request.setAttribute(ALL_USING_PROJECTS_COUNT, 0); + } + request.setAttribute(USING_COMPONENTS, nullToEmptySet(usingComponentsForComponent)); + } + + private void setUsingDocsWithFlexibleDependencyNetwork(RenderRequest request, String releaseId, User user, ComponentService.Iface client) throws TException { + if (releaseId != null) { + final Set usingComponentsForRelease = client.getUsingComponentsWithAccessibilityForRelease(releaseId, user); + Set releaseIdSet = Collections.singleton(releaseId); + List usingProjectInDependencyNetwork = SW360Utils.getUsingProjectByReleaseIds(releaseIdSet, user); + request.setAttribute(USING_COMPONENTS, nullToEmptySet(usingComponentsForRelease)); + request.setAttribute(USING_PROJECTS, new HashSet<>(usingProjectInDependencyNetwork)); + request.setAttribute(ALL_USING_PROJECTS_COUNT, SW360Utils.getUsingProjectByReleaseIds(releaseIdSet, null).size()); + } else { + request.setAttribute(USING_PROJECTS, Collections.emptySet()); + request.setAttribute(ALL_USING_PROJECTS_COUNT, 0); + request.setAttribute(USING_COMPONENTS, Collections.emptySet()); + } + } } diff --git a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/projects/ProjectPortlet.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/projects/ProjectPortlet.java index c0a70119b7..9eb289214a 100644 --- a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/projects/ProjectPortlet.java +++ b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/projects/ProjectPortlet.java @@ -12,11 +12,15 @@ import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Predicate; import com.google.common.base.Strings; import com.google.common.collect.*; +import com.google.gson.Gson; import com.liferay.portal.kernel.json.*; import com.liferay.portal.kernel.model.Organization; import com.liferay.portal.kernel.portlet.LiferayPortletURL; @@ -299,6 +303,10 @@ else if ((PortalConstants.LOAD_OBLIGATIONS_EDIT.equals(action) serveAddVendor(request, response); } else if (VIEW_DEPARTMENT.equals(action)) { serveViewDepartment(request, response); + } else if (DEPENDENCY_NETWORK_LIST.equals(action)) { + serveDependencyNetworkList(request, response); + } else if (PortalConstants.DEPENDENCY_NETWORK_ON_LOAD.equals(action)) { + serveDependencyNetworkOnLoad(request, response); } } @@ -1307,6 +1315,53 @@ private void serveLinkedReleases(ResourceRequest request, ResourceResponse respo serveReleaseSearchResults(request, response, where); } else if (PortalConstants.RELEASE_LIST_FROM_LINKED_PROJECTS.equals(what)) { serveReleasesFromLinkedProjects(request, response, projectId); + } else if (PortalConstants.CREATE_LINKED_RELEASE_ROW.equals(what)) { + String[] where = request.getParameterValues(PortalConstants.WHERE_ARRAY); + String[] parentIds = request.getParameterValues(PARENT_NODE_ID); + String[] layers = request.getParameterValues(PortalConstants.LAYER); + String[] mainlineStates = request.getParameterValues(PortalConstants.MAINLINE_STATE); + String[] releaseRelationShips= request.getParameterValues(PortalConstants.RELEASE_RELATION_SHIP); + String[] indexes = request.getParameterValues(PortalConstants.INDEXES); + String[] comments = request.getParameterValues(PortalConstants.COMMENTS); + serveNewTableRowLinkedRelease(request, response, where, parentIds, layers, mainlineStates, releaseRelationShips, indexes, comments); + } else if (PortalConstants.FIND_LINKED_RELEASE_OF_NODE.equals(what)) { + String releaseId = request.getParameter(RELEASE_ID); + try { + serveReleaseRelationNetworkOfNode(request, response, releaseId); + } catch (TException e) { + log.error(e.getMessage()); + } + } else if (PortalConstants.CHECK_RELEASE_EXIST.equals(what)) { + String releaseId = request.getParameter(PortalConstants.RELEASE_ID); + severCheckReleaseExistOrAccessibleToLink(request, response, releaseId); + } else if (PortalConstants.CYCLIC_LINKED_RELEASE_PATH.equals(what)) { + getCyclicLinkedOfReleases(request, response); + } else if (PortalConstants.RELEASES_WITH_SAME_COMPONENT_ID.equals(what)) { + String releaseId = request.getParameter(PortalConstants.RELEASE_ID); + serveReleasesWithSameComponent(request, response, releaseId); + } else if (PortalConstants.CHECK_DIFF_DEPENDENCY_NETWORK_WITH_RELEASES_RELATIONSHIP.equals(what)) { + String currentNetwork = request.getParameter(PortalConstants.CURRENT_NETWORK); + checkDiffDependencyNetworkAndReleasesRelationship(request, response, currentNetwork); + } else if (PortalConstants.GET_HTML_RELEASE_ROWS.equals(what)) { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + String network = request.getParameter(WHERE); + List releaseIds = new ArrayList<>(); + List parentIds = new ArrayList<>(); + List layers = new ArrayList<>(); + List mainlineStates = new ArrayList<>(); + List releaseRelationShips = new ArrayList<>(); + List indexes = new ArrayList<>(); + List comments = new ArrayList<>(); + + List dependencyNetwork = objectMapper.readValue(network, new TypeReference>() { + }); + + getInformationFromNetwork(dependencyNetwork, releaseIds, parentIds, layers, mainlineStates, releaseRelationShips, indexes, comments, 0, ""); + serveNewTableRowLinkedRelease(request, response, releaseIds.toArray(String[]::new), + parentIds.toArray(String[]::new), layers.toArray(String[]::new), + mainlineStates.toArray(String[]::new), releaseRelationShips.toArray(String[]::new), + indexes.toArray(String[]::new), comments.toArray(String[]::new)); } } @@ -1401,8 +1456,12 @@ private void serveReleasesFromLinkedProjects(ResourceRequest request, ResourceRe Project linkedProject = projectClient.getProjectById(linkedProjectId, user); if (linkedProject != null) { - Map releaseIdToUsage = CommonUtils.nullToEmptyMap(linkedProject.getReleaseIdToUsage()); - releaseIdsFromLinkedProjects.addAll(releaseIdToUsage.keySet()); + if (!SW360Constants.ENABLE_FLEXIBLE_PROJECT_RELEASE_RELATIONSHIP) { + Map releaseIdToUsage = CommonUtils.nullToEmptyMap(linkedProject.getReleaseIdToUsage()); + releaseIdsFromLinkedProjects.addAll(releaseIdToUsage.keySet()); + } else { + releaseIdsFromLinkedProjects.addAll(SW360Utils.getReleaseIdsLinkedWithProject(linkedProject)); + } } } @@ -1523,12 +1582,15 @@ public void doView(RenderRequest request, RenderResponse response) throws IOExce String pageName = request.getParameter(PAGENAME); if (PAGENAME_DETAIL.equals(pageName)) { prepareDetailView(request, response); + request.setAttribute(IS_FLEXIBLE_PROJECT_RELEASE_RELATIONSHIP_ENABLED, SW360Constants.ENABLE_FLEXIBLE_PROJECT_RELEASE_RELATIONSHIP); include("/html/projects/detail.jsp", request, response); } else if (PAGENAME_EDIT.equals(pageName)) { prepareProjectEdit(request); + request.setAttribute(IS_FLEXIBLE_PROJECT_RELEASE_RELATIONSHIP_ENABLED, SW360Constants.ENABLE_FLEXIBLE_PROJECT_RELEASE_RELATIONSHIP); include("/html/projects/edit.jsp", request, response); } else if (PAGENAME_DUPLICATE.equals(pageName)) { prepareProjectDuplicate(request); + request.setAttribute(IS_FLEXIBLE_PROJECT_RELEASE_RELATIONSHIP_ENABLED, SW360Constants.ENABLE_FLEXIBLE_PROJECT_RELEASE_RELATIONSHIP); include("/html/projects/edit.jsp", request, response); } else if (PAGENAME_LICENSE_INFO.equals(pageName)) { prepareLicenseInfo(request, response); @@ -2351,6 +2413,9 @@ private void prepareProjectEdit(RenderRequest request) { } setDefaultRequestAttributes(request, project.getBusinessUnit()); Map sortedAdditionalData = getSortedMap(project.getAdditionalData(), true); + if (project.getReleaseRelationNetwork() == null) { + project.setReleaseRelationNetwork(DEFAULT_RELEASE_RELATION_NETWORK); + } project.setAdditionalData(sortedAdditionalData); usingProjects = client.searchLinkingProjects(id, user); allUsingProjectCount = client.getCountByProjectId(id); @@ -2369,7 +2434,11 @@ private void prepareProjectEdit(RenderRequest request) { setAttachmentsInRequest(request, project); try { if (isUpdateOrCreateProjectFailed && project.isSetLinkedProjects()) { - projectlink = client.getLinkedProjects(project.getLinkedProjects(), false, user); + if (!SW360Constants.ENABLE_FLEXIBLE_PROJECT_RELEASE_RELATIONSHIP) { + projectlink = client.getLinkedProjects(project.getLinkedProjects(), false, user); + } else { + projectlink = client.getLinkedProjectsWithoutReleases(project.getLinkedProjects(), false, user); + } request.setAttribute(PROJECT_LIST, projectlink); } else { putDirectlyLinkedProjectsInRequest(request, project, user); @@ -2381,6 +2450,7 @@ private void prepareProjectEdit(RenderRequest request) { } request.setAttribute(USING_PROJECTS, usingProjects); request.setAttribute(ALL_USING_PROJECTS_COUNT, allUsingProjectCount); + request.setAttribute(LOGIN_USER, user); Map permissions = project.getPermissions(); DocumentState documentState = project.getDocumentState(); request.setAttribute(IS_PROJECT_MEMBER, SW360Utils.isUserAllowedToEditClosedProject(project, user)); @@ -2393,11 +2463,16 @@ private void prepareProjectEdit(RenderRequest request) { } request.setAttribute(PROJECT, project); setDefaultRequestAttributes(request, user.getDepartment()); + project.setReleaseRelationNetwork(DEFAULT_RELEASE_RELATION_NETWORK); PortletUtils.setCustomFieldsEdit(request, user, project); setAttachmentsInRequest(request, project); try { if (project.isSetLinkedProjects()) { - projectlink = client.getLinkedProjects(project.getLinkedProjects(), false, user); + if (!SW360Constants.ENABLE_FLEXIBLE_PROJECT_RELEASE_RELATIONSHIP) { + projectlink = client.getLinkedProjects(project.getLinkedProjects(), false, user); + } else { + projectlink = client.getLinkedProjectsWithoutReleases(project.getLinkedProjects(), false, user); + } } request.setAttribute(PROJECT_LIST, projectlink); putDirectlyLinkedReleasesWithAccessibilityInRequest(request, project, user); @@ -2406,7 +2481,7 @@ private void prepareProjectEdit(RenderRequest request) { } request.setAttribute(USING_PROJECTS, Collections.emptySet()); request.setAttribute(ALL_USING_PROJECTS_COUNT, 0); - + request.setAttribute(LOGIN_USER, user); SessionMessages.add(request, "request_processed", LanguageUtil.get(resourceBundle,"new.project")); } @@ -2429,6 +2504,9 @@ private void prepareProjectDuplicate(RenderRequest request) { setDefaultRequestAttributes(request, newProject.getBusinessUnit()); setAttachmentsInRequest(request, newProject); PortletUtils.setCustomFieldsEdit(request, user, newProject); + if (newProject.getReleaseRelationNetwork() == null) { + newProject.setReleaseRelationNetwork(DEFAULT_RELEASE_RELATION_NETWORK); + } request.setAttribute(PROJECT, newProject); putDirectlyLinkedProjectsInRequest(request, newProject, user); putDirectlyLinkedReleasesWithAccessibilityInRequest(request, newProject, user); @@ -2437,6 +2515,7 @@ private void prepareProjectDuplicate(RenderRequest request) { request.setAttribute(ALL_USING_PROJECTS_COUNT, 0); request.setAttribute(SOURCE_PROJECT_ID, id); request.setAttribute(PortalConstants.IS_SBOM_IMPORT_EXPORT_ACCESS_USER, SW360Utils.isUserAtleastDesiredRoleInPrimaryOrSecondaryGroup(user, SW360Constants.SBOM_IMPORT_EXPORT_ACCESS_USER_ROLE)); + request.setAttribute(LOGIN_USER, user); } else { Project project = new Project(); project.setBusinessUnit(user.getDepartment()); @@ -2444,11 +2523,13 @@ private void prepareProjectDuplicate(RenderRequest request) { setDefaultRequestAttributes(request, user.getDepartment()); request.setAttribute(PROJECT, project); PortletUtils.setCustomFieldsEdit(request, user, project); + project.setReleaseRelationNetwork(DEFAULT_RELEASE_RELATION_NETWORK); putDirectlyLinkedProjectsInRequest(request, project, user); putDirectlyLinkedReleasesWithAccessibilityInRequest(request, project, user); request.setAttribute(USING_PROJECTS, Collections.emptySet()); request.setAttribute(ALL_USING_PROJECTS_COUNT, 0); + request.setAttribute(LOGIN_USER, user); } } catch (TException e) { log.error("Error fetching project from backend!", e); @@ -2484,6 +2565,12 @@ public void update(ActionRequest request, ActionResponse response) throws Portle prepareRequestForEditAfterDuplicateError(request, project, user); return; } + + if (SW360Constants.ENABLE_FLEXIBLE_PROJECT_RELEASE_RELATIONSHIP) { + project.unsetReleaseIdToUsage(); + SW360Utils.syncReleaseRelationNetworkAndReleaseIdToUsage(project, user); + } + requestStatus = client.updateProject(project, user); setSessionMessage(request, requestStatus, "Project", "update", printName(project)); if (RequestStatus.SUCCESS.equals(requestStatus) && CommonUtils.isNotNullEmptyOrWhitespace(request.getParameter(OBLIGATION_DATA))) { @@ -2518,6 +2605,12 @@ else if (RequestStatus.NAMINGERROR.equals(requestStatus)) prepareRequestForEditAfterDuplicateError(request, project, user); return; } + + if (SW360Constants.ENABLE_FLEXIBLE_PROJECT_RELEASE_RELATIONSHIP) { + project.unsetReleaseIdToUsage(); + SW360Utils.syncReleaseRelationNetworkAndReleaseIdToUsage(project, user); + } + AddDocumentRequestSummary summary = client.addProject(project, user); String newProjectId = summary.getId(); String sourceProjectId = request.getParameter(SOURCE_PROJECT_ID); @@ -3372,4 +3465,265 @@ private Set getProjectGroups(List projects) { } return businessUnit; } + + private void serveDependencyNetworkList(ResourceRequest request, ResourceResponse response) { + ProjectService.Iface client = thriftClients.makeProjectClient(); + User user = UserCacheHolder.getUserFromRequest(request); + String projectId = request.getParameter(DOCUMENT_ID); + List> clearingStatusList = new ArrayList<>(); + try { + clearingStatusList = client.getAccessibleDependencyNetworkForListView(projectId, user); + } catch (TException e) { + log.error("Problem getting flat view of Clearing Status", e); + } + JSONArray clearingStatusData = createJSONArray(); + for (int i = 0; i < clearingStatusList.size(); i++) { + JSONObject jsonObject = JSONFactoryUtil.createJSONObject(); + clearingStatusList.get(i).entrySet().stream() + .forEach(entry -> jsonObject.put(entry.getKey(), entry.getValue())); + clearingStatusData.put(jsonObject); + } + JSONObject jsonResult = createJSONObject(); + jsonResult.put("data", clearingStatusData); + try { + writeJSON(request, response, jsonResult); + } catch (IOException e) { + log.error("Problem rendering Clearing Status", e); + } + } + + private void serveDependencyNetworkOnLoad(ResourceRequest request, ResourceResponse response) + throws IOException, PortletException { + User user = UserCacheHolder.getUserFromRequest(request); + String id = request.getParameter(PROJECT_ID); + ProjectService.Iface client = thriftClients.makeProjectClient(); + Project project = null; + try { + project = client.getProjectById(id, user); + } catch (TException exp) { + log.error("Error while fetching Project id : " + id, exp); + return; + } + + List mappedProjectLinks = createLinkedProjects(project, user); + mappedProjectLinks = sortProjectLink(mappedProjectLinks); + request.setAttribute(PROJECT_LIST, mappedProjectLinks); + include("/html/utils/ajax/linkedProjectRowsDependencyNetwork.jsp", request, response, PortletRequest.RESOURCE_PHASE); + } + + private void serveNewTableRowLinkedRelease(ResourceRequest request, ResourceResponse response, String[] linkedIds, + String[] parentIds, String[] layers, String[] mainlineStates, String[] releaseRelationShips, + String[] indexes, String[] comments) throws IOException, PortletException { + final User user = UserCacheHolder.getUserFromRequest(request); + request.setAttribute(IS_USER_AT_LEAST_CLEARING_ADMIN, PermissionUtils.isUserAtLeast(UserGroup.CLEARING_ADMIN, user)); + + List linkedReleases = new ArrayList<>(); + ComponentService.Iface client = thriftClients.makeComponentClient(); + try { + List listReleaseIds = Arrays.asList(linkedIds); + List releases = client.getReleasesByListIds(listReleaseIds, user); + for (int index = 0; index < releases.size(); index++) { + Release loadingRelease = releases.get(index); + List releasesWithSameComponent = Collections.singletonList(loadingRelease); + ReleaseLink linkedRelease = new ReleaseLink(); + linkedRelease.setId(releases.get(index).getId()); + linkedRelease.setName(releases.get(index).getName()); + linkedRelease.setVersion(releases.get(index).getVersion()); + linkedRelease.setLongName(SW360Utils.printFullname(releases.get(index))); + linkedRelease.setReleaseWithSameComponent(releasesWithSameComponent); + linkedRelease.setLayer(Integer.parseInt(layers[index])); + linkedRelease.setParentNodeId(parentIds[index]); + linkedRelease.setMainlineState(MainlineState.valueOf(mainlineStates[index])); + linkedRelease.setReleaseRelationship(ReleaseRelationship.valueOf(releaseRelationShips[index])); + linkedRelease.setIndex(Integer.parseInt(indexes[index])); + linkedRelease.setComment(comments[index]); + linkedRelease.setAccessible(client.isReleaseActionAllowed(loadingRelease, user, RequestedAction.READ)); + linkedReleases.add(linkedRelease); + } + } catch (TException e) { + log.error("Error getting releases!", e); + throw new PortletException("cannot get releases " + Arrays.toString(linkedIds), e); + } + request.setAttribute(RELEASES_IN_NETWORK, linkedReleases); + include("/html/utils/ajax/linkedReleaseInNetwork.jsp", request, response, PortletRequest.RESOURCE_PHASE); + } + + private void serveReleaseRelationNetworkOfNode(ResourceRequest request, ResourceResponse response, String releaseId) throws TException { + ComponentService.Iface releaseClient = thriftClients.makeComponentClient(); + User user = UserCacheHolder.getUserFromRequest(request); + JSONObject jsonObject = JSONFactoryUtil.createJSONObject(); + Release release = releaseClient.getReleaseById(releaseId,user); + List releaseLinkJSONS = releaseClient.getReleaseRelationNetworkOfRelease(release, user); + jsonObject.put(PortalConstants.RESULT, new Gson().toJson(releaseLinkJSONS)); + try { + writeJSON(request, response, jsonObject); + } catch (IOException e) { + log.error(e.getMessage()); + } + } + + private void severCheckReleaseExistOrAccessibleToLink(ResourceRequest request, ResourceResponse response, String releaseId) { + ComponentService.Iface releaseClient = thriftClients.makeComponentClient(); + User user = UserCacheHolder.getUserFromRequest(request); + JSONObject jsonObject = JSONFactoryUtil.createJSONObject(); + try { + releaseClient.getAccessibleReleaseById(releaseId, user); + jsonObject.put(PortalConstants.RESULT, true); + } catch (TException e) { + jsonObject.put(PortalConstants.RESULT, false); + } + try { + writeJSON(request, response, jsonObject); + } catch (IOException e) { + log.error(e.getMessage()); + } + } + + private void getCyclicLinkedOfReleases(ResourceRequest request, ResourceResponse response) throws IOException { + User user = UserCacheHolder.getUserFromRequest(request); + String[] childReleaseIds = request.getParameterValues(CHILD_RELEASE_ID_ARRAY); + String[] releaseIds = request.getParameterValues(RELEASE_ID_ARRAY); + final ComponentService.Iface releaseClient = thriftClients.makeComponentClient(); + Map> result = new HashMap<>(); + if (releaseIds == null || childReleaseIds == null || releaseIds.length == 0 || childReleaseIds.length == 0) { + result.put("data", new HashSet<>()); + } else { + try { + Set hierarchiesPath = new HashSet<>(); + Map idsToReleaseMap = new HashMap<>(); + Map releaseIdsToMap = releaseClient.getReleasesById(new HashSet<>(Arrays.asList(releaseIds)), user).stream().collect(Collectors.toMap(Release::getId, Function.identity())); + Map childReleaseIdsToMap = releaseClient.getReleasesById(new HashSet<>(Arrays.asList(childReleaseIds)), user).stream().collect(Collectors.toMap(Release::getId, Function.identity())); + idsToReleaseMap.putAll(releaseIdsToMap); + idsToReleaseMap.putAll(childReleaseIdsToMap); + for (int i = 0; i < releaseIds.length; i++) { + for (int j = 0; j < childReleaseIds.length; j++) { + Release releaseById = idsToReleaseMap.get(releaseIds[i]); + Map releaseRelationshipMap = new HashMap<>(); + releaseRelationshipMap.put(childReleaseIds[j], ReleaseRelationship.CONTAINED); + releaseById.setReleaseIdToRelationship(releaseRelationshipMap); + String cyclicLinkedReleasePath = releaseClient.getCyclicLinkedReleasePath(releaseById, user); + hierarchiesPath.add(cyclicLinkedReleasePath); + } + } + result.put("data", hierarchiesPath); + } catch (TException e) { + log.error("Cannot fetch release from backend", e); + result.put("data", new HashSet<>()); + } + } + + JSONObject jsonObject = JSONFactoryUtil.createJSONObject(); + try { + jsonObject = createJSONObject(PortletUtils.convertObjectToJsonStr(result)); + } catch (JSONException e) { + log.error("Error occured while creating JSON.", e); + } + writeJSON(request, response, jsonObject); + } + + private void serveReleasesWithSameComponent(ResourceRequest request, ResourceResponse response, String releaseId) { + ComponentService.Iface releaseClient = thriftClients.makeComponentClient(); + User user = UserCacheHolder.getUserFromRequest(request); + JSONObject jsonObject = JSONFactoryUtil.createJSONObject(); + Map releaseIdWithVersion = null; + + try { + Release release = releaseClient.getAccessibleReleaseById(releaseId, user); + List releasesWithSameComponentId = releaseClient.getReleasesByComponentId(release.getComponentId(), user); + releaseIdWithVersion = releasesWithSameComponentId.stream().filter(rel -> !rel.getId().equals(releaseId)) + .collect(Collectors.toMap(Release::getId, Release::getVersion)); + } catch (TException e) { + log.error(e.getMessage()); + } + + if (releaseIdWithVersion == null) { + releaseIdWithVersion = Collections.emptyMap(); + } + + jsonObject.put(PortalConstants.RESULT, releaseIdWithVersion); + + try { + writeJSON(request, response, jsonObject); + } catch (IOException e) { + log.error(e.getMessage()); + } + } + + private void getInformationFromNetwork(List network, List releaseIds, List parentIds, + List layers, List mainlineStates, List releaseRelationShips, + List indexes, Listcomments, int currentLayer, String parentId) { + int index = 0; + for (ReleaseNode releaseNode : network) { + releaseIds.add(releaseNode.getReleaseId()); + layers.add(currentLayer + ""); + mainlineStates.add(releaseNode.getMainlineState()); + releaseRelationShips.add(releaseNode.getReleaseRelationship()); + indexes.add(index + ""); + comments.add(releaseNode.getComment() != null ? releaseNode.getComment() : ""); + parentIds.add(parentId); + getInformationFromNetwork(releaseNode.getReleaseLink(), releaseIds, parentIds, layers, mainlineStates, releaseRelationShips, indexes, comments, currentLayer + 1, releaseNode.getReleaseId()); + index ++; + } + } + + private void checkDiffDependencyNetworkAndReleasesRelationship(ResourceRequest request, ResourceResponse response, String currentNetwork) { + ObjectMapper objectMapper = new ObjectMapper(); + User user = UserCacheHolder.getUserFromRequest(request); + List dependencyNetwork = new ArrayList<>(); + List rowsHaveDiff = new ArrayList<>(); + List checkedReleaseIds = new ArrayList<>(); + if (currentNetwork != null) { + try { + dependencyNetwork = objectMapper.readValue(currentNetwork, new TypeReference>() { + }); + } catch (JsonProcessingException e) { + log.error("Error when parse string to object"); + } + } + + for (ReleaseNode releaseNode : dependencyNetwork) { + checkedReleaseIds.add(releaseNode.getReleaseId()); + checkSubNodes(releaseNode, rowsHaveDiff, checkedReleaseIds, user); + } + + JSONObject jsonObject = JSONFactoryUtil.createJSONObject(); + jsonObject.put(PortalConstants.RESULT, rowsHaveDiff); + try { + writeJSON(request, response, jsonObject); + } catch (IOException e) { + log.error(e.getMessage()); + } + } + + private void checkSubNodes(ReleaseNode releaseNode, List rowsHaveDiff, List checkedReleaseIds, User user) { + ComponentService.Iface releaseClient = thriftClients.makeComponentClient(); + try { + Release releaseById = releaseClient.getReleaseById(releaseNode.getReleaseId(), user); + Map linkedReleases = releaseById.getReleaseIdToRelationship(); + List releaseIdsInRelationShip = (linkedReleases != null) ? linkedReleases.keySet().stream().collect(Collectors.toList()) : Collections.emptyList(); + if ((releaseNode.getReleaseLink() != null) && !releaseNode.getReleaseLink().isEmpty()) { + for (ReleaseNode subNode : releaseNode.getReleaseLink()) { + checkedReleaseIds.add(subNode.getReleaseId()); + if (!releaseIdsInRelationShip.contains(subNode.getReleaseId())) { + rowsHaveDiff.add(checkedReleaseIds.size()); + subNodesIsDiff(subNode, rowsHaveDiff, checkedReleaseIds); + } else { + checkSubNodes(subNode, rowsHaveDiff, checkedReleaseIds, user); + } + } + } + } catch (TException e) { + log.error("Could not fetch release: " + releaseNode.getReleaseId(), e); + } + } + + private void subNodesIsDiff(ReleaseNode releaseNode, List rowsHaveDiff, List checkedReleaseIds) { + if ((releaseNode.getReleaseLink() != null) && !releaseNode.getReleaseLink().isEmpty()) { + for (ReleaseNode subNode : releaseNode.getReleaseLink()) { + checkedReleaseIds.add(subNode.getReleaseId()); + rowsHaveDiff.add(checkedReleaseIds.size()); + subNodesIsDiff(subNode, rowsHaveDiff, checkedReleaseIds); + } + } + } } diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/css/components/_tooltips.scss b/frontend/sw360-portlet/src/main/resources/META-INF/resources/css/components/_tooltips.scss index 0bb3160098..cdb8c0f61f 100644 --- a/frontend/sw360-portlet/src/main/resources/META-INF/resources/css/components/_tooltips.scss +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/css/components/_tooltips.scss @@ -901,3 +901,7 @@ .sw360-tt-ObligationLevel-LICENSE_OBLIGATION:hover:after { content: attr(data-content); } + +.sw360-tt-ReloadInfo:hover:after { + content: attr(data-content); +} diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/projects/edit.jsp b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/projects/edit.jsp index 1432fe03ec..2e0bf9488a 100644 --- a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/projects/edit.jsp +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/projects/edit.jsp @@ -79,6 +79,7 @@ +