From e5d8ff37cd78d8aaea7ea41ff9134d137bdf58f3 Mon Sep 17 00:00:00 2001 From: Oleg Kopysov Date: Tue, 27 Aug 2024 22:42:18 +0300 Subject: [PATCH] feat: Implement updated pull request comments (HTML and MD) Signed-off-by: Oleg Kopysov --- .../lpvs/entity/report/LPVSReportBuilder.java | 222 ++++++++++++++++++ .../com/lpvs/service/LPVSGitHubService.java | 38 +-- .../entity/report/LPVSReportBuilderTest.java | 64 ++++- .../lpvs/service/LPVSGitHubServiceTest.java | 18 +- 4 files changed, 289 insertions(+), 53 deletions(-) diff --git a/src/main/java/com/lpvs/entity/report/LPVSReportBuilder.java b/src/main/java/com/lpvs/entity/report/LPVSReportBuilder.java index ae24e0c3..f8f7cc53 100644 --- a/src/main/java/com/lpvs/entity/report/LPVSReportBuilder.java +++ b/src/main/java/com/lpvs/entity/report/LPVSReportBuilder.java @@ -248,6 +248,228 @@ public static void saveHTMLToFile(String htmlContent, String filePath) { } } + /** + * Generates a comment to the pull request for publication to the VCS. + * + * @param scanResults the results of the license scan + * @param conflicts a list of license conflicts found during the scan + * @param webhookConfig configuration related to the repository and webhook + * @param vcs the string representation of the version control system + * @return the code of the generated VCS comment + */ + public String generatePullRequestComment(List scanResults, + List> conflicts, + LPVSQueue webhookConfig, + LPVSVcs vcs) { + + StringBuilder commitCommentBuilder = new StringBuilder(); + commitCommentBuilder.append("**Detected licenses:**\n\n"); + + Map> detectedLicenseInfo = groupScanResultsForLicenseTable(scanResults); + long prohibitedLicenses = getDetectedLicenseCountByType(detectedLicenseInfo, prohibited); + long restrictedLicenses = getDetectedLicenseCountByType(detectedLicenseInfo, restricted); + long unreviewedLicenses = getDetectedLicenseCountByType(detectedLicenseInfo, unreviewed); + long licenseDetected = prohibitedLicenses + restrictedLicenses + unreviewedLicenses; + + if (licenseDetected > 0) { + commitCommentBuilder.append("Potential license problem(s) detected:\n"); + if (prohibitedLicenses > 0) { + commitCommentBuilder.append(" - Prohibited license(s): " + prohibitedLicenses + "\n"); + } + if (restrictedLicenses > 0) { + commitCommentBuilder.append(" - Restricted license(s): " + restrictedLicenses + "\n"); + } + if (unreviewedLicenses > 0) { + commitCommentBuilder.append(" - Unreviewed license(s): " + unreviewedLicenses + "\n"); + } + } else { + commitCommentBuilder.append("No license problems detected.\n"); + } + if (scanResults != null && !scanResults.isEmpty()) { + commitCommentBuilder.append("\n") + .append(generateLicenseTable(detectedLicenseInfo, webhookConfig, vcs)) + .append("\n"); + } + commitCommentBuilder.append("\n"); + commitCommentBuilder.append("**Detected License Conflicts:**\n\n"); + if (conflicts != null && !conflicts.isEmpty()) { + commitCommentBuilder.append( + "Potential license conflict(s) detected: " + + conflicts.size() + + "\n"); + commitCommentBuilder.append("\n") + .append(generateLicenseConflictsTable(conflicts, vcs)) + .append("\n"); + } else { + commitCommentBuilder.append("No license conflicts detected.\n"); + } + commitCommentBuilder.append("\n"); + + return commitCommentBuilder.toString(); + } + + /** + * Generates an HTML code of the license table to pull request comments + * + * @param detectedLicenseInfo grouped scan results by license SPDX ID and access type, component name and vendor + * @param webhookConfig configuration related to the repository and webhook + * @param vcs the string representation of the version control system + * @return the HTML code of the generated license table + */ + private String generateLicenseTable(Map> detectedLicenseInfo, LPVSQueue webhookConfig, LPVSVcs vcs) { + if (vcs != null && vcs.equals(LPVSVcs.GITHUB)) { + return "
" + + "\n" + + "" + + "Detailed description of detected licenses" + + "\n" + + "" + + "\n" + + generateLicenseTableHTML(detectedLicenseInfo, webhookConfig, vcs) + + "\n" + + "
"; + } + return generateLicenseTableMD(detectedLicenseInfo, webhookConfig, vcs); + } + + /** + * Generates an HTML code of the license conflicts table to pull request comments + * + * @param conflicts a list of license conflicts found during the scan + * @param vcs the string representation of the version control system + * @return the code of the generated license conflicts table + */ + private String generateLicenseConflictsTable(List> conflicts, LPVSVcs vcs) { + if (vcs != null && vcs.equals(LPVSVcs.GITHUB)) { + return "
" + + "\n" + + "" + + "Detailed description of detected license conflicts" + + "\n" + + "" + + "\n" + + generateLicenseConflictsTableHTML(conflicts) + + "\n" + + "
"; + } + return generateLicenseConflictsTableMD(conflicts); + } + + /** + * Generates an MD code of the license table to pull request comments + * + * @param detectedLicenseInfo grouped scan results by license SPDX ID and access type, component name and vendor + * @param webhookConfig configuration related to the repository and webhook + * @param vcs the string representation of the version control system + * @return the MD code of the generated license table + */ + private String generateLicenseTableMD(Map> detectedLicenseInfo, LPVSQueue webhookConfig, LPVSVcs vcs) { + StringBuilder mdBuilder = new StringBuilder(); + mdBuilder.append("|License Type / Explanation") + .append("|License SPDX ID") + .append("|Vendor / Component") + .append("|Version") + .append("|Repository File Path") + .append("|Component File Path") + .append("|Matched Lines") + .append("|Match Value|") + .append("\n") + .append("|-|-|-|-|-|-|-|-|") + .append("\n"); + // Prohibited licenses + addBlockOfTableForLicenseTypeMD(mdBuilder, detectedLicenseInfo, prohibited, webhookConfig, vcs); + // Restricted licenses + addBlockOfTableForLicenseTypeMD(mdBuilder, detectedLicenseInfo, restricted, webhookConfig, vcs); + // Unreviewed licenses + addBlockOfTableForLicenseTypeMD(mdBuilder, detectedLicenseInfo, unreviewed, webhookConfig, vcs); + // Permitted licenses + addBlockOfTableForLicenseTypeMD(mdBuilder, detectedLicenseInfo, permitted, webhookConfig, vcs); + + return mdBuilder.toString(); + } + + /** + * Generates the license conflicts table MD content. + * + * @param conflicts a list of license conflicts + * @return the MDmvn content for the license conflicts table + */ + private String generateLicenseConflictsTableMD( + List> conflicts) { + StringBuilder mdBuilder = new StringBuilder(); + mdBuilder + .append("|Conflict>") + .append("|Explanation|") + .append("\n") + .append("|-|-|") + .append("\n"); + + for (LPVSLicenseService.Conflict conflict : conflicts) { + mdBuilder + .append("|") + .append(conflict.l1) + .append(" and ") + .append(conflict.l2) + .append("|") + .append(getExplanationForLicenseConflict(conflict.l1, conflict.l2)) + .append("|") + .append("\n"); + } + return mdBuilder.toString(); + } + + /** + * Generates an MD code of the license conflicts table to pull request comments + * + * @param mdBuilder the StringBuilder object to which the MD content will be appended + * @param detectedLicenseInfo grouped scan results by license SPDX ID and access type, component name and vendor + * @param webhookConfig configuration related to the repository and webhook + * @param vcs the string representation of the version control system + */ + private void addBlockOfTableForLicenseTypeMD(StringBuilder mdBuilder, Map> detectedLicenseInfo, String type, LPVSQueue webhookConfig, LPVSVcs vcs) { + long detectedLicenseCountByType = getDetectedLicenseCountByType(detectedLicenseInfo, type); + if (detectedLicenseCountByType > 0) { + // license spdx id + Map> licenseSpdxIds = + (Map>) detectedLicenseInfo.get(type).elements; + for (String licenseSpdxId : licenseSpdxIds.keySet()) { + // vendor + component + Map> componentAndVendor = + (Map>) licenseSpdxIds.get(licenseSpdxId).elements; + for (String componentInfo : componentAndVendor.keySet()) { + // file info + List fileInfos = + (List) componentAndVendor.get(componentInfo).elements; + for (LPVSFile fileInfo : fileInfos) { + mdBuilder.append("|") + .append(type) + .append(" / ") + .append(getExplanationForLicenseType(type)) + .append("|") + .append(licenseSpdxId) + .append("|") + .append(componentInfo.split(":::")[0]) + .append(" (") + .append(componentInfo.split(":::")[1]) + .append(")") + .append("|") + .append(fileInfo.getComponentVersion()) + .append("|") + .append(fileInfo.getFilePath()) + .append("|") + .append(fileInfo.getComponentFilePath()) + .append("|") + .append(getMatchedLinesAsLink(webhookConfig, fileInfo, vcs)) + .append("|") + .append(fileInfo.getSnippetMatch()) + .append("|") + .append("\n"); + } + } + } + } + } + /** * Generates the license conflicts table HTML content. * diff --git a/src/main/java/com/lpvs/service/LPVSGitHubService.java b/src/main/java/com/lpvs/service/LPVSGitHubService.java index f5bd5563..b4b6cc86 100644 --- a/src/main/java/com/lpvs/service/LPVSGitHubService.java +++ b/src/main/java/com/lpvs/service/LPVSGitHubService.java @@ -10,6 +10,7 @@ import com.lpvs.entity.LPVSQueue; import com.lpvs.entity.enums.LPVSPullRequestStatus; import com.lpvs.entity.enums.LPVSVcs; +import com.lpvs.entity.report.LPVSReportBuilder; import com.lpvs.repository.LPVSDetectedLicenseRepository; import com.lpvs.repository.LPVSLicenseConflictRepository; import com.lpvs.repository.LPVSLicenseRepository; @@ -249,25 +250,12 @@ public void commentResults( boolean hasProhibitedOrRestricted = false; boolean hasConflicts = false; - String commitComment = ""; + LPVSReportBuilder reportBuilder = new LPVSReportBuilder(null); + String commitComment = reportBuilder.generatePullRequestComment(scanResults, conflicts, webhookConfig, LPVSVcs.GITHUB); - if (scanResults != null && scanResults.size() != 0) { - commitComment = "**Detected licenses:**\n\n\n"; + + if (scanResults != null && !scanResults.isEmpty()) { for (LPVSFile file : scanResults) { - commitComment += "**File:** " + file.getFilePath() + "\n"; - commitComment += "**License(s):** " + file.convertLicensesToString(LPVSVcs.GITHUB); - commitComment += - "**Component:** " - + file.getComponentName() - + " (" - + file.getComponentFilePath() - + ")\n"; - commitComment += - "**Matched Lines:** " - + LPVSCommentUtil.getMatchedLinesAsLink( - webhookConfig, file, LPVSVcs.GITHUB) - + "\n"; - commitComment += "**Snippet Match:** " + file.getSnippetMatch() + "\n\n\n\n"; for (LPVSLicense license : file.getLicenses()) { LPVSDetectedLicense detectedIssue = new LPVSDetectedLicense(); detectedIssue.setPullRequest(lpvsPullRequest); @@ -297,13 +285,9 @@ public void commentResults( } } - StringBuilder commitCommentBuilder = new StringBuilder(); - if (conflicts != null && conflicts.size() > 0) { + if (conflicts != null && !conflicts.isEmpty()) { hasConflicts = true; - commitCommentBuilder.append("**Detected license conflicts:**\n\n\n"); - commitCommentBuilder.append("
    "); for (LPVSLicenseService.Conflict conflict : conflicts) { - commitCommentBuilder.append("
  • " + conflict.l1 + " and " + conflict.l2 + "
  • "); LPVSDetectedLicense detectedIssue = new LPVSDetectedLicense(); detectedIssue.setPullRequest(lpvsPullRequest); Long l1 = @@ -330,15 +314,7 @@ public void commentResults( detectedIssue.setIssue(true); lpvsDetectedLicenseRepository.saveAndFlush(detectedIssue); } - commitCommentBuilder.append("
"); - } - if (null != webhookConfig.getHubLink()) { - commitCommentBuilder.append( - "\n\n######

Check the validation details at the [link]("); - commitCommentBuilder.append(webhookConfig.getHubLink()); - commitCommentBuilder.append(")

"); } - commitComment += commitCommentBuilder.toString(); if (hasProhibitedOrRestricted || hasConflicts) { lpvsPullRequest.setStatus(LPVSPullRequestStatus.ISSUES_DETECTED.toString()); @@ -362,7 +338,7 @@ public void commentResults( webhookConfig.getHeadCommitSHA(), GHCommitState.SUCCESS, null, - "No license issue detected", + "No license issue(s) detected", "[License Pre-Validation Service]"); } } diff --git a/src/test/java/com/lpvs/entity/report/LPVSReportBuilderTest.java b/src/test/java/com/lpvs/entity/report/LPVSReportBuilderTest.java index 54c0808c..5b23624c 100644 --- a/src/test/java/com/lpvs/entity/report/LPVSReportBuilderTest.java +++ b/src/test/java/com/lpvs/entity/report/LPVSReportBuilderTest.java @@ -8,6 +8,8 @@ import com.lpvs.entity.LPVSFile; import com.lpvs.entity.LPVSLicense; +import com.lpvs.entity.LPVSQueue; +import com.lpvs.entity.enums.LPVSVcs; import com.lpvs.service.LPVSLicenseService; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.BeforeEach; @@ -332,25 +334,57 @@ void testReportCommentBuilder_LicenseDetectedNoConflicts() { List> conflicts = new ArrayList<>(); scanResults.add(createSampleFile("testPath1", "test1", "UNREVIEWED")); - String comment = - reportBuilder.generateCommandLineComment("testPath1", scanResults, conflicts); + scanResults.add(createSampleFile("testPath1", "test1", "PROHIBITED")); + scanResults.add(createSampleFile("testPath1", "test1", "RESTRICTED")); + scanResults.add(createSampleFile("testPath1", "test1", "PERMITTED")); + String comment = reportBuilder.generateCommandLineComment("testPath1", scanResults, conflicts); assertNotNull(comment); + } + @Test + void testReportCommentBuilder_LicenseDetectedConflictsDetected() { + LPVSReportBuilder reportBuilder = new LPVSReportBuilder(null); + List scanResults = new ArrayList<>(); scanResults.add(createSampleFile("testPath1", "test1", "PROHIBITED")); - comment = reportBuilder.generateCommandLineComment("testPath1", scanResults, conflicts); + LPVSLicenseService.Conflict conflict_1 = + new LPVSLicenseService.Conflict<>("MIT", "Apache-2.0"); + List> conflicts = + List.of(conflict_1, conflict_1); + String comment = reportBuilder.generateCommandLineComment("testPath1", scanResults, conflicts); assertNotNull(comment); + } - scanResults.add(createSampleFile("testPath1", "test1", "RESTRICTED")); - comment = reportBuilder.generateCommandLineComment("testPath1", scanResults, conflicts); + @Test + void testReportCommentBuilder_NoLicenseDetectedNoConflicts() { + LPVSReportBuilder reportBuilder = new LPVSReportBuilder(null); + String comment = reportBuilder.generateCommandLineComment("testPath1", null, null); assertNotNull(comment); + } + + @Test + void testGeneratePullRequestComment_LicenseDetectedNoConflicts() { + LPVSReportBuilder reportBuilder = new LPVSReportBuilder(null); + List scanResults = new ArrayList<>(); + List> conflicts = new ArrayList<>(); + + LPVSQueue webhookConfig = new LPVSQueue(); + webhookConfig.setPullRequestUrl("https://github.com/Samsung/LPVS/pull/1"); + webhookConfig.setRepositoryUrl("https://github.com/Samsung/LPVS"); + scanResults.add(createSampleFile("testPath1", "test1", "UNREVIEWED")); + scanResults.add(createSampleFile("testPath1", "test1", "PROHIBITED")); + scanResults.add(createSampleFile("testPath1", "test1", "RESTRICTED")); scanResults.add(createSampleFile("testPath1", "test1", "PERMITTED")); - comment = reportBuilder.generateCommandLineComment("testPath1", scanResults, conflicts); + + String comment = reportBuilder.generatePullRequestComment(scanResults, conflicts, webhookConfig, LPVSVcs.GITHUB); + assertNotNull(comment); + + comment = reportBuilder.generatePullRequestComment(scanResults, conflicts, webhookConfig, LPVSVcs.GERRIT); assertNotNull(comment); } @Test - void testReportCommentBuilder_LicenseDetectedConflictsDetected() { + void testGeneratePullRequestComment_LicenseDetectedConflictsDetected() { LPVSReportBuilder reportBuilder = new LPVSReportBuilder(null); List scanResults = new ArrayList<>(); scanResults.add(createSampleFile("testPath1", "test1", "PROHIBITED")); @@ -358,14 +392,24 @@ void testReportCommentBuilder_LicenseDetectedConflictsDetected() { new LPVSLicenseService.Conflict<>("MIT", "Apache-2.0"); List> conflicts = List.of(conflict_1, conflict_1); - String comment = reportBuilder.generateCommandLineComment("testPath1", null, null); + LPVSQueue webhookConfig = new LPVSQueue(); + webhookConfig.setPullRequestUrl("https://github.com/Samsung/LPVS/pull/1"); + webhookConfig.setRepositoryUrl("https://github.com/Samsung/LPVS"); + + String comment = reportBuilder.generatePullRequestComment(scanResults, conflicts, webhookConfig, LPVSVcs.GITHUB); + assertNotNull(comment); + + comment = reportBuilder.generatePullRequestComment(scanResults, conflicts, webhookConfig, LPVSVcs.GERRIT); assertNotNull(comment); } @Test - void testReportCommentBuilder_NoLicenseDetectedNoConflicts() { + void testGeneratePullRequestComment_NoLicenseDetectedNoConflicts() { LPVSReportBuilder reportBuilder = new LPVSReportBuilder(null); - String comment = reportBuilder.generateCommandLineComment("testPath1", null, null); + String comment = reportBuilder.generatePullRequestComment( null, null, null, LPVSVcs.GITHUB); + assertNotNull(comment); + + comment = reportBuilder.generatePullRequestComment(null, null, null, LPVSVcs.GERRIT); assertNotNull(comment); } } diff --git a/src/test/java/com/lpvs/service/LPVSGitHubServiceTest.java b/src/test/java/com/lpvs/service/LPVSGitHubServiceTest.java index 8d5854d0..70a059f1 100644 --- a/src/test/java/com/lpvs/service/LPVSGitHubServiceTest.java +++ b/src/test/java/com/lpvs/service/LPVSGitHubServiceTest.java @@ -2470,30 +2470,24 @@ class TestCommentResults__EmptyPresentConflictsPresent { // `lpvs_file_1` LPVSFile lpvs_file_1; - final String file_url_1 = - "https://github.com/Samsung/LPVS/tree/main/src/main/java/com/lpvs/service/LPVSGitHubService.java"; final String file_path_1 = "src/main/java/com/lpvs/service/LPVSGitHubService.java"; final String absolute_file_path_1 = "src/main/java/com/lpvs/service/LPVSGitHubService.java"; final String snippet_type_1 = "snippet"; - final String snippet_match_1 = - "/**\n" - + " * Copyright (c) 2022, Samsung Electronics Co., Ltd. All rights reserved.\n" - + " *\n" - + " * Use of this source code is governed by a MIT license that can be\n" - + " * found in the LICENSE file.\n" - + " */\n"; + final String snippet_match_1 = "30%"; final String matched_lines_1 = "1-6"; final String component_1 = "LPVS::Services"; final String component_file_path_1 = "src/main/java/com/lpvs/service/LPVSGitHubService.java"; final String component_file_url_1 = "src/main/java/com/lpvs/service/LPVSGitHubService.java"; + final String component_vendor_1 = "vendor1"; + final String component_version_1 = "version_1"; // `lpvs_license_1` LPVSLicense lpvs_license_1; final String license_name_1 = "MIT License"; final String spdx_id_1 = "MIT"; final String spdx_id_2 = "GPL-2.0-only"; - final String access_1 = ""; + final String access_1 = "PERMITTED"; final String alternativeName_1 = ""; final String checklist_url_1 = "https://opensource.org/licenses/MIT"; @@ -2589,8 +2583,8 @@ void setUp() { component_1, null, null, - null, - null); + component_version_1, + component_vendor_1); conflict_1 = new LPVSLicenseService.Conflict<>(conflict_1_l1, conflict_1_l2); when(mocked_lpvsLicenseRepository.findFirstBySpdxIdOrderByLicenseIdDesc(anyString()))