From 92af64dc195532dfff939bf2dabc643f24f73424 Mon Sep 17 00:00:00 2001 From: Shi Qiu Date: Wed, 17 Apr 2024 11:14:29 +0000 Subject: [PATCH] feat(rest): create new endpoint for bulk delete function Signed-off-by: Shi Qiu --- .../sw360/datahandler/db/BulkDeleteUtil.java | 7 ++- .../components/db/BulkDeleteUtilTest.java | 58 +++++++++++++++++++ .../core/JacksonCustomizations.java | 21 ++++++- .../release/ReleaseController.java | 21 +++++++ .../release/Sw360ReleaseService.java | 5 ++ 5 files changed, 109 insertions(+), 3 deletions(-) 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..da464142c5 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,7 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import com.google.common.collect.ImmutableMap; import java.io.BufferedWriter; import java.io.FileWriter; @@ -517,6 +521,60 @@ public void testDeleteBulkRelease002() throws Exception { } } + @Test + public void testDeleteBulkRelease_ExternalLink001() throws Exception { + if (!isFeatureEnable()) { + System.out.println("BulkReleaseDeletion is disabled. these test is Skipped."); + return; + } + + List 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()) { 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 8ff923a543..59116c3607 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 @@ -126,6 +126,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() @@ -180,7 +181,8 @@ public Sw360Module() { .replaceWithClass(ReleaseNode.class, ReleaseNodeMixin.class) .replaceWithClass(RestrictedResource.class, RestrictedResourceMixin.class) .replaceWithClass(RestApiToken.class, Sw360Module.RestApiTokenMixin.class) - .replaceWithClass(ProjectLink.class, ProjectLinkMixin.class); + .replaceWithClass(ProjectLink.class, ProjectLinkMixin.class) + .replaceWithClass(BulkOperationNode.class, BulkOperationNodeMixin.class); } @JsonInclude(JsonInclude.Include.NON_NULL) @@ -2337,5 +2339,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 87bb48f92f..103fce8943 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 @@ -61,6 +61,7 @@ import org.eclipse.sw360.datahandler.thrift.licenseinfo.LicenseInfoParsingResult; import org.eclipse.sw360.datahandler.thrift.licenseinfo.LicenseNameWithText; import org.eclipse.sw360.datahandler.thrift.projects.Project; +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; @@ -1466,4 +1467,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 98a0dd7da4..465db0e37c 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 @@ -279,6 +279,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);