diff --git a/backend/common/src/main/java/org/eclipse/sw360/datahandler/db/BulkDeleteUtil.java b/backend/common/src/main/java/org/eclipse/sw360/datahandler/db/BulkDeleteUtil.java index 3887813356..0c46dad05e 100644 --- a/backend/common/src/main/java/org/eclipse/sw360/datahandler/db/BulkDeleteUtil.java +++ b/backend/common/src/main/java/org/eclipse/sw360/datahandler/db/BulkDeleteUtil.java @@ -173,8 +173,11 @@ public BulkOperationNode deleteBulkRelease(String releaseId, User user, boolean Release referencingRelease = workReleaseMap.get(referencingReleaseId); Map relationMap = referencingRelease.getReleaseIdToRelationship(); relationMap.remove(leafReleaseId); - if (!isPreview) { - releaseRepository.update(referencingRelease); + //Do not update the repository link if both the referencing Release and the referenced Release are externally referenced. + if (!externalLinkMap.get(referencingReleaseId)) { + if (!isPreview) { + releaseRepository.update(referencingRelease); + } } } } diff --git a/backend/components/src/test/java/org/eclipse/sw360/components/db/BulkDeleteUtilTest.java b/backend/components/src/test/java/org/eclipse/sw360/components/db/BulkDeleteUtilTest.java index c35a71357c..94908b01a9 100644 --- a/backend/components/src/test/java/org/eclipse/sw360/components/db/BulkDeleteUtilTest.java +++ b/backend/components/src/test/java/org/eclipse/sw360/components/db/BulkDeleteUtilTest.java @@ -24,6 +24,9 @@ import org.eclipse.sw360.datahandler.thrift.*; import org.eclipse.sw360.datahandler.thrift.components.*; import org.eclipse.sw360.datahandler.thrift.projects.Project; +import org.eclipse.sw360.datahandler.thrift.projects.ProjectProjectRelationship; +import org.eclipse.sw360.datahandler.thrift.projects.ProjectRelationship; +import org.eclipse.sw360.datahandler.thrift.projects.ProjectType; import org.eclipse.sw360.datahandler.thrift.users.User; import org.eclipse.sw360.datahandler.thrift.users.UserGroup; import org.eclipse.sw360.datahandler.thrift.vendors.Vendor; @@ -34,6 +37,10 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import com.google.common.collect.ImmutableMap; import java.io.BufferedWriter; import java.io.FileWriter; @@ -48,6 +55,8 @@ @RunWith(MockitoJUnitRunner.class) public class BulkDeleteUtilTest { + private static final Logger log = LogManager.getLogger(BulkDeleteUtilTest.class); + private static final String dbName = DatabaseSettingsTest.COUCH_DB_DATABASE; private static final String attachmentsDbName = DatabaseSettingsTest.COUCH_DB_ATTACHMENTS; private static final String changeLogsDbName = DatabaseSettingsTest.COUCH_DB_CHANGELOGS; @@ -154,7 +163,7 @@ public void tearDown() throws Exception { @Test public void testGetAllLinkedReleaseMap() throws Exception { if (!isFeatureEnable()) { - System.out.println("BulkReleaseDeletion is disabled. these test is Skipped."); + log.warn("BulkReleaseDeletion is disabled. these test is Skipped."); return; } @@ -230,7 +239,7 @@ public void testGetExternalLinkMap() throws Exception { @Test public void testDeleteBulkRelease001() throws Exception { if (!isFeatureEnable()) { - System.out.println("BulkReleaseDeletion is disabled. these test is Skipped."); + log.warn("BulkReleaseDeletion is disabled. these test is Skipped."); return; } @@ -461,7 +470,7 @@ private void checkBulkOperationNode(BulkOperationNode node, Map releaseIdList = new ArrayList(); + List componentIdList = new ArrayList(); + + createTestRecords002(1, 2, releaseIdList, componentIdList); + String rootReleaseId = releaseIdList.get(0); + assertEquals(3, releaseIdList.size()); + assertEquals(3, componentIdList.size()); + + Project project = new Project().setId("P1").setName("project1").setVisbility(Visibility.EVERYONE).setProjectType(ProjectType.CUSTOMER); + project.putToReleaseIdToUsage(rootReleaseId, new ProjectReleaseRelationship(ReleaseRelationship.CONTAINED, MainlineState.OPEN)); + databaseConnector.add(project); + + BulkOperationNode level1Component = bulkDeleteUtil.deleteBulkRelease(rootReleaseId, user1, false); + assertNotNull(level1Component); + + //Check the BulkOperationNode status + //Object[0] : NodeType, Object[1] : ResultState + Map expectedResults = new HashMap(); + expectedResults.put(releaseIdList.get(0), new Object[]{BulkOperationNodeType.RELEASE, BulkOperationResultState.EXCLUDED}); + expectedResults.put(releaseIdList.get(1), new Object[]{BulkOperationNodeType.RELEASE, BulkOperationResultState.EXCLUDED}); + expectedResults.put(releaseIdList.get(2), new Object[]{BulkOperationNodeType.RELEASE, BulkOperationResultState.EXCLUDED}); + expectedResults.put(componentIdList.get(0), new Object[]{BulkOperationNodeType.COMPONENT, BulkOperationResultState.EXCLUDED}); + expectedResults.put(componentIdList.get(1), new Object[]{BulkOperationNodeType.COMPONENT, BulkOperationResultState.EXCLUDED}); + expectedResults.put(componentIdList.get(2), new Object[]{BulkOperationNodeType.COMPONENT, BulkOperationResultState.EXCLUDED}); + checkBulkOperationNode(level1Component, expectedResults); + + //Releases to be undeleted + for (String releaseId : releaseIdList) { + assertTrue(this.releaseExists(releaseId)); + } + + //Release links to be undeleted + Release release0 = databaseConnector.get(Release.class, releaseIdList.get(0)); + assertEquals(1, release0.getReleaseIdToRelationshipSize()); + Release release1 = databaseConnector.get(Release.class, releaseIdList.get(1)); + assertEquals(1, release1.getReleaseIdToRelationshipSize()); + Release release2 = databaseConnector.get(Release.class, releaseIdList.get(2)); + assertEquals(0, release2.getReleaseIdToRelationshipSize()); + + //Components and links to be undeleted + for (String componentId : componentIdList) { + assertTrue(this.componentExists(componentId)); + Component component = databaseConnector.get(Component.class, componentId); + assertEquals(1, component.getReleaseIdsSize()); + } + } + @Test public void testDeleteBulkRelease_ConflictError001() throws Exception { if (!isFeatureEnable()) { - System.out.println("BulkReleaseDeletion is disabled. these test is Skipped."); + log.warn("BulkReleaseDeletion is disabled. these test is Skipped."); return; } @@ -743,7 +806,7 @@ public void checkDeletedReleaseListInLoop(int loopCount, List deletedRe @Test public void testDeleteBulkRelease_ConflictError002() throws Exception { if (!isFeatureEnable()) { - System.out.println("BulkReleaseDeletion is disabled. these test is Skipped."); + log.warn("BulkReleaseDeletion is disabled. these test is Skipped."); return; } @@ -931,7 +994,7 @@ public void checkDeletedReleaseListInLoop(int loopCount, List deletedRe @Test public void testDeleteBulkRelease_ConflictError003() throws Exception { if (!isFeatureEnable()) { - System.out.println("BulkReleaseDeletion is disabled. these test is Skipped."); + log.warn("BulkReleaseDeletion is disabled. these test is Skipped."); return; } diff --git a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/core/JacksonCustomizations.java b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/core/JacksonCustomizations.java index 90e5882725..2cdad031bb 100644 --- a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/core/JacksonCustomizations.java +++ b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/core/JacksonCustomizations.java @@ -152,6 +152,7 @@ public Sw360Module() { setMixInAnnotation(RestrictedResource.class, Sw360Module.RestrictedResourceMixin.class); setMixInAnnotation(RestApiToken.class, Sw360Module.RestApiTokenMixin.class); setMixInAnnotation(ProjectLink.class, Sw360Module.ProjectLinkMixin.class); + setMixInAnnotation(BulkOperationNode.class, Sw360Module.BulkOperationNodeMixin.class); // Make spring doc aware of the mixin(s) SpringDocUtils.getConfig() @@ -207,6 +208,7 @@ public Sw360Module() { .replaceWithClass(RestrictedResource.class, RestrictedResourceMixin.class) .replaceWithClass(RestApiToken.class, Sw360Module.RestApiTokenMixin.class) .replaceWithClass(ProjectLink.class, ProjectLinkMixin.class) + .replaceWithClass(BulkOperationNode.class, BulkOperationNodeMixin.class) .replaceWithClass(SPDXDocument.class, Sw360Module.SPDXDocumentMixin.class) .replaceWithClass(DocumentCreationInformation.class, Sw360Module.DocumentCreationInformationMixin.class) .replaceWithClass(PackageInformation.class, Sw360Module.PackageInformationMixin.class) @@ -2637,5 +2639,22 @@ public abstract static class RestApiTokenMixin extends RestApiToken { "setTreeLevel", }) abstract static class ProjectLinkMixin extends ProjectLink {} + + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonIgnoreProperties(value = { + "setId", + "setName", + "setVersion", + "setType", + "setParentId", + "setChildList", + "childListSize", + "childListIterator", + "setState", + "setAdditionalData", + "additionalDataSize" + }) + static abstract class BulkOperationNodeMixin extends BulkOperationNode { + } } } diff --git a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/release/ReleaseController.java b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/release/ReleaseController.java index 6c011f71ec..fafd18b911 100644 --- a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/release/ReleaseController.java +++ b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/release/ReleaseController.java @@ -68,6 +68,7 @@ import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.DocumentCreationInformation; import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument; import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageInformation; +import org.eclipse.sw360.datahandler.thrift.components.BulkOperationNode; import org.eclipse.sw360.datahandler.thrift.users.User; import org.eclipse.sw360.datahandler.thrift.vendors.Vendor; import org.eclipse.sw360.datahandler.thrift.vulnerabilities.ReleaseVulnerabilityRelation; @@ -1768,4 +1769,24 @@ private Set getAttachmentsFromRequest(Object attachmentData, ObjectM }) .collect(Collectors.toSet()); } + + @PreAuthorize("hasAuthority('WRITE')") + @Operation( + summary = "Bulk delete releases.", + description = "Bulk delete existing releases.", + tags = {"Releases"} + ) + @DeleteMapping(value = RELEASES_URL + "/{id}/bulkDelete") + public ResponseEntity bulkDeleteReleases( + @Parameter(description = "The release id to be bulk-deleted.") + @PathVariable("id") String id, + @Parameter(description = "isPreview flag for bulk deletion.") + @RequestParam(value = "isPreview", defaultValue="false", required = false) boolean isPreview + ) throws TException { + User user = restControllerHelper.getSw360UserFromAuthentication(); + BulkOperationNode result = releaseService.deleteBulkRelease(id, user, isPreview); + + return new ResponseEntity(result, HttpStatus.OK); + } + } diff --git a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/release/Sw360ReleaseService.java b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/release/Sw360ReleaseService.java index 87a356653f..1348777452 100644 --- a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/release/Sw360ReleaseService.java +++ b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/release/Sw360ReleaseService.java @@ -819,6 +819,11 @@ public RequestStatus deleteRelease(String releaseId, User sw360User) throws TExc } return deleteStatus; } + + public BulkOperationNode deleteBulkRelease(String releaseId, User sw360User, boolean isPreview) throws TException { + ComponentService.Iface sw360ComponentClient = getThriftComponentClient(); + return sw360ComponentClient.deleteBulkRelease(releaseId, sw360User, isPreview); + } public Set getProjectsByRelease(String releaseId, User sw360User) throws TException { return projectService.getProjectsByRelease(releaseId, sw360User);