Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Number of open issue probe #353

Open
wants to merge 50 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
2773dcf
Fetched issue tracker from update center
Jagrutiti Jul 23, 2023
68aa505
Removed issueTracker from models
Jagrutiti Jul 24, 2023
54a82a3
Modified the existing test cases that were affected and updated then …
Jagrutiti Jul 24, 2023
abb1001
Fixing checkstyle issues
Jagrutiti Jul 24, 2023
4d8ae5d
Fixing checkstyle issues
Jagrutiti Jul 24, 2023
c4c1605
Updated probe names and added java docs
Jagrutiti Jul 24, 2023
99d9cf5
Added test and code to count open GitHub issues
Jagrutiti Jul 29, 2023
719c57d
Added test code and completed the test case
Jagrutiti Jul 30, 2023
925afa3
Removed conficting dependencies
Jagrutiti Jul 30, 2023
58b135a
Fixed incorrect artifact name
Jagrutiti Jul 30, 2023
c71a32a
Merge branch 'main' into feature/open-issue-probe
Jagrutiti Jul 30, 2023
5fd8b2d
Fixed JIRA API calling
Jagrutiti Aug 1, 2023
6f9ede3
Merge branch 'main' into feature/open-issue-probe
Jagrutiti Aug 1, 2023
1a54932
Added IssueTrackerDetectionProbe
Jagrutiti Aug 2, 2023
4a144e5
Update IssueTrackerDetectionProbe and added a test for the same
Jagrutiti Aug 5, 2023
d3fd18b
Added abstract probe class and jira and github issues count probe
Jagrutiti Aug 5, 2023
d960448
Fixing checkstyle issues
Jagrutiti Aug 5, 2023
eda1b13
Fixed the test case for open jira issues count
Jagrutiti Aug 6, 2023
97aed28
Removing the issueTracker field from update center
Jagrutiti Aug 6, 2023
528ab43
Added a narrowed depedency in pom file and removed unwanted mock in …
Jagrutiti Aug 6, 2023
5f22f38
Added more test case for IssueTrackerDetectionProbe
Jagrutiti Aug 6, 2023
e13f3a8
Fixed checkstyle issues
Jagrutiti Aug 6, 2023
9bcb49e
Adding more test case for jira open issues
Jagrutiti Aug 6, 2023
4989e81
Apply suggestions from code review
Jagrutiti Aug 8, 2023
7db7e7a
Fixed the abstract class and updated test cases
Jagrutiti Aug 9, 2023
e45ce77
Updated the test cases
Jagrutiti Aug 9, 2023
510d340
Modifying the abstract class to make it better
Jagrutiti Aug 9, 2023
3cf7406
Refactored code in GitHub open issues probe
Jagrutiti Aug 12, 2023
b9cda9c
Restructured IssueTrackerDetectionProbe and improved its readability
Jagrutiti Aug 13, 2023
37884d5
Added a new test case for IssueTrackerDertectionProbe
Jagrutiti Aug 13, 2023
898c86c
Refactored code, test cases, fixed border cases
Jagrutiti Aug 13, 2023
fdd1a4e
Updating IssueTrackerDetectionProbe ORDER
Jagrutiti Aug 13, 2023
1940e67
Fixed method names
Jagrutiti Aug 14, 2023
151df0d
Merge branch 'main' into feature/open-issue-probe
Jagrutiti Aug 14, 2023
12ef4aa
Removed unused imports
Jagrutiti Aug 14, 2023
7f6cb04
Merge branch 'feature/open-issue-probe' of https://github.com/Jagruti…
Jagrutiti Aug 14, 2023
6a7214a
Fixed JavaDocs
Jagrutiti Aug 15, 2023
8e0f12a
Update core/src/main/java/io/jenkins/pluginhealth/scoring/probes/GitH…
Jagrutiti Aug 19, 2023
12db24b
Removed unwanted dependencies and fixed log statements
Jagrutiti Aug 19, 2023
793114b
Adding a comment
Jagrutiti Aug 22, 2023
e13b17e
Update core/src/test/java/io/jenkins/pluginhealth/scoring/probes/GitH…
Jagrutiti Aug 23, 2023
f4ef76e
Made minor changes based on code review
Jagrutiti Aug 23, 2023
1e194e9
Merge branch 'feature/open-issue-probe' of https://github.com/Jagruti…
Jagrutiti Aug 23, 2023
d9fad0c
Adding null checks
Jagrutiti Aug 24, 2023
663282c
Apply suggestions from code review
Jagrutiti Sep 6, 2023
c099e40
Fixed foramatting, updated library uses based on code review
Jagrutiti Sep 6, 2023
ec28408
Updated syntax for null check
Jagrutiti Sep 6, 2023
6a740d2
Merge branch 'main' into feature/open-issue-probe
Jagrutiti Sep 7, 2023
9244d60
Fixing merge issues
Jagrutiti Sep 7, 2023
b722a87
Fixing checkstyle issues
Jagrutiti Sep 7, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -102,5 +102,9 @@
<artifactId>postgresql</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Jagrutiti marked this conversation as resolved.
Show resolved Hide resolved
</dependencies>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@

public record UpdateCenter(Map<String, Plugin> plugins,
Map<String, Deprecation> deprecations,
List<SecurityWarning> warnings
List<SecurityWarning> warnings,
List<Map<String, String>> issueTrackers

Jagrutiti marked this conversation as resolved.
Show resolved Hide resolved
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* MIT License
*
* Copyright (c) 2023 Jenkins Infra
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

package io.jenkins.pluginhealth.scoring.probes;

import java.util.Map;
import java.util.stream.Collectors;

import io.jenkins.pluginhealth.scoring.model.Plugin;
import io.jenkins.pluginhealth.scoring.model.ProbeResult;

import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
@Order(value = IssueTrackerDetectionProbe.ORDER)
class IssueTrackerDetectionProbe extends Probe {
public static final String KEY = "issue-tracker-detection";
public static final int ORDER = UpdateCenterPluginPublicationProbe.ORDER + 100 ;

Check warning on line 40 in core/src/main/java/io/jenkins/pluginhealth/scoring/probes/IssueTrackerDetectionProbe.java

View check run for this annotation

ci.jenkins.io / CheckStyle

NoWhitespaceBeforeCheck

ERROR: ';' is preceded with whitespace.
Raw output
<p>Since Checkstyle 3.0</p><p> Checks that there is no whitespace before a token. More specifically, it checks that it is not preceded with whitespace, or (if linebreaks are allowed) all characters on the line before are whitespace. To allow linebreaks before a token, set property <code>allowLineBreaks</code> to <code>true</code>. No check occurs before semi-colons in empty for loop initializers or conditions. </p>

Check warning on line 40 in core/src/main/java/io/jenkins/pluginhealth/scoring/probes/IssueTrackerDetectionProbe.java

View check run for this annotation

infra.ci.jenkins.io / CheckStyle

NoWhitespaceBeforeCheck

ERROR: ';' is preceded with whitespace.
Raw output
<p>Since Checkstyle 3.0</p><p> Checks that there is no whitespace before a token. More specifically, it checks that it is not preceded with whitespace, or (if linebreaks are allowed) all characters on the line before are whitespace. To allow linebreaks before a token, set property <code>allowLineBreaks</code> to <code>true</code>. No check occurs before semi-colons in empty for loop initializers or conditions. </p>

@Override
protected ProbeResult doApply(Plugin plugin, ProbeContext context) {
Map<String, String> issueTrackerType = context.getUpdateCenter()
.issueTrackers().stream()
.collect(Collectors.toMap(
issueTracker -> issueTracker.get("type"),
issueTracker -> issueTracker.get("viewUrl")
));

context.setIssueTrack(issueTrackerType);
return ProbeResult.success(key(), "Issue tracker detected and returned successfully.");
Jagrutiti marked this conversation as resolved.
Show resolved Hide resolved
Jagrutiti marked this conversation as resolved.
Show resolved Hide resolved
}

@Override
public String key() {
return KEY;
}

@Override
public String getDescription() {
return "Detects the issues tracker type from Update Center.";
}

@Override
public String[] getProbeResultRequirement() {
return new String[]{UpdateCenterPluginPublicationProbe.KEY};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/*
* MIT License
*
* Copyright (c) 2023 Jenkins Infra
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

package io.jenkins.pluginhealth.scoring.probes;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import io.jenkins.pluginhealth.scoring.model.Plugin;
import io.jenkins.pluginhealth.scoring.model.ProbeResult;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.kohsuke.github.GHRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

/**
* This probe counts the total number of open issues in GitHub and JIRA
*/
@Component
@Order(value = NumberOfOpenIssuesProbe.ORDER)
public class NumberOfOpenIssuesProbe extends Probe {
Jagrutiti marked this conversation as resolved.
Show resolved Hide resolved
public static final String KEY = "open-issue";
public static final int ORDER = UpdateCenterPluginPublicationProbe.ORDER + 100;
private static final String JIRA_HOST = "https://issues.jenkins.io/rest/api/latest/search?";
private static final Logger LOGGER = LoggerFactory.getLogger(NumberOfOpenIssuesProbe.class);

@Override
protected ProbeResult doApply(Plugin plugin, ProbeContext context) {
List<String> issueTracker = getIssueTrackerData(context, IssueTrackerKeysEnum.TYPE.getKey());

if (issueTracker.size() > 1) {
return ProbeResult.success(key(),
getJiraIssues(issueTracker, plugin.getScm(), plugin.getName()).message().concat(" ") +
getGitHubIssues(context, plugin.getScm(), plugin.getName()).message());
}

return issueTracker.stream()
.filter(item -> item.equals("jira"))
.findFirst()
.map(entry -> getJiraIssues(getIssueTrackerData(context, IssueTrackerKeysEnum.VIEW.getKey()), plugin.getScm(), plugin.getName()))
.orElseGet(() -> issueTracker.stream()
.filter(item -> item.equals("github"))
.findFirst()
.map(entry -> getGitHubIssues(context, plugin.getScm(), plugin.getName()))
.orElse(ProbeResult.failure(key(), String.format("Cannot find issue tracker for %s in update center", plugin.getName()))));
}

/**
* Get issueTracker data from UpdateCenter and filter the type
*
* @param context the probe context data
* @return a list which contains a map of issue tracker type
*/
private List<String> getIssueTrackerData(ProbeContext context, String filter) {
return context.getUpdateCenter()
.issueTrackers().stream()
.flatMap(map -> map.entrySet().stream())
.filter(map -> map.getKey().equals(filter))
.map(map -> map.getValue())
.collect(Collectors.toList());
}

/**
* Get total number of open JIRA issues in a plugin
*/
private ProbeResult getJiraIssues(List<String> issueTrackerData, String scm, String pluginName) {
try {
URL url = new URL(issueTrackerData.get(0));
String api = JIRA_HOST + url.getQuery() + " AND status=open";

RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.getForEntity(api, String.class);
ObjectMapper objectMapper = new ObjectMapper();
String jsonResponse = response.getBody();
JsonNode jsonNode = objectMapper.readTree(jsonResponse);
int openJIRAIssues = jsonNode.get("total").asInt();
return ProbeResult.success(key(), String.format("%d open issues found in JIRA.", openJIRAIssues));
} catch (JsonMappingException e) {
LOGGER.error("Cannot map JSON returned by JIRA API for plugin {}.", pluginName, e);
} catch (JsonProcessingException e) {
LOGGER.error("Cannot process JSON returned by JIRA API for plugin {}.", pluginName, e);
} catch (MalformedURLException e) {
LOGGER.error("Cannot process malformed URL for plugin {}.", pluginName, e);
}
return ProbeResult.failure(key(), String.format("Cannot fetch information from JIRA API for plugin %s.", pluginName));
Jagrutiti marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Get total number of open GitHub issues in a plugin
*/
private ProbeResult getGitHubIssues(ProbeContext context, String scm, String pluginName) {
try {
final Optional<String> repositoryName = context.getRepositoryName(scm);
if (repositoryName.isPresent()) {
final GHRepository ghRepository = context.getGitHub().getRepository(repositoryName.get());
int openGitHubIssues = ghRepository.getOpenIssueCount();
return ProbeResult.success(key(), String.format("%d open issues found in GitHub.", openGitHubIssues));
}
} catch (IOException ex) {
return ProbeResult.error(key(), String.format("Cannot not read open issues on GitHub for plugin %s.", pluginName));
}
return ProbeResult.failure(key(), String.format("Cannot find GitHub repository for plugin %s.", pluginName));
}

@Override
public String key() {
return KEY;
}

@Override
public String getDescription() {
return "Returns the number of issues open in a plugin";
}

@Override
public String[] getProbeResultRequirement() {
return new String[]{SCMLinkValidationProbe.KEY, UpdateCenterPluginPublicationProbe.KEY};
}

private enum IssueTrackerKeysEnum {
REPORT("reportUrl"),
TYPE("type"),
VIEW("viewUrl");

private final String key;

IssueTrackerKeysEnum(String key) {
this.key = key;
}

public String getKey() {
return key;
}


}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public class ProbeContext {
private GitHub github;
private ZonedDateTime lastCommitDate;
private Map<String, String> pluginDocumentationLinks;
private Map<String, String> issueTrackerType;

public ProbeContext(String pluginName, UpdateCenter updateCenter) throws IOException {
this.updateCenter = updateCenter;
Expand Down Expand Up @@ -88,6 +89,14 @@ public Optional<String> getRepositoryName(String scm) {
return match.find() ? Optional.of(match.group("repo")) : Optional.empty();
}

public void setIssueTrack(Map<String, String> issueTrackerType) {
this.issueTrackerType = issueTrackerType;
}

public Map<String, String> getIssueTrackerType() {
return issueTrackerType;
}

/* default */ void cleanUp() throws IOException {
try (Stream<Path> paths = Files.walk(this.scmRepository)) {
paths.sorted(Comparator.reverseOrder())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ pluginName, new VersionNumber("1.0"), scmLink, ZonedDateTime.now(), List.of(), 0
)
),
Map.of(),
List.of(),
List.of()
));
when(ctx.getRepositoryName(plugin.getScm())).thenReturn(Optional.empty());
Expand Down Expand Up @@ -162,6 +163,7 @@ pluginName, new VersionNumber("1.0"), scmLink, ZonedDateTime.now(), List.of(), 0
)
),
Map.of(),
List.of(),
List.of()
));
when(ctx.getGitHub()).thenReturn(gh);
Expand Down Expand Up @@ -219,6 +221,7 @@ pluginName, new VersionNumber("1.0"), scmLink, ZonedDateTime.now(), List.of(), 0
)
),
Map.of(),
List.of(),
List.of()
));
when(ctx.getGitHub()).thenReturn(gh);
Expand Down Expand Up @@ -276,6 +279,7 @@ pluginName, new VersionNumber("1.0"), scmLink, ZonedDateTime.now(), List.of(), 0
)
),
Map.of(),
List.of(),
List.of()
));
when(ctx.getGitHub()).thenReturn(gh);
Expand Down Expand Up @@ -333,6 +337,7 @@ pluginName, new VersionNumber("1.0"), scmLink, ZonedDateTime.now(), List.of(), 0
)
),
Map.of(),
List.of(),
List.of()
));
when(ctx.getGitHub()).thenReturn(gh);
Expand Down Expand Up @@ -390,6 +395,7 @@ pluginName, new VersionNumber("1.0"), scmLink, ZonedDateTime.now(), List.of(), 0
)
),
Map.of(),
List.of(),
List.of()
));
when(ctx.getGitHub()).thenReturn(gh);
Expand Down Expand Up @@ -447,6 +453,7 @@ pluginName, new VersionNumber("1.0"), scmLink, ZonedDateTime.now(), List.of(), 0
)
),
Map.of(),
List.of(),
List.of()
));
when(ctx.getGitHub()).thenReturn(gh);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ void shouldBeAbleToDetectNonDeprecatedPlugin() {
when(ctx.getUpdateCenter()).thenReturn(new UpdateCenter(
Map.of("foo", new Plugin("foo", new VersionNumber("1.0"), "scm", ZonedDateTime.now().minusDays(1), Collections.emptyList(), 0, "", "main")),
Map.of("bar", new Deprecation("find-the-reason-here")),
Collections.emptyList(),
Collections.emptyList()
));

Expand All @@ -82,6 +83,7 @@ void shouldBeAbleToDetectDeprecatedPlugin() {
when(ctx.getUpdateCenter()).thenReturn(new UpdateCenter(
Map.of("foo", new Plugin("foo", new VersionNumber("1.0"), "scm", ZonedDateTime.now().minusDays(1), Collections.emptyList(), 0, "", "main")),
Map.of("bar", new Deprecation("find-the-reason-here"), "foo", new Deprecation("this-is-the-reason")),
Collections.emptyList(),
Collections.emptyList()
));

Expand All @@ -103,6 +105,7 @@ void shouldBeAbleToDetectDeprecatedPluginFromLabels() {
pluginName, new Plugin(pluginName, new VersionNumber("1.0"), "", ZonedDateTime.now(), List.of("deprecated"), 0, "2.361", "main")
),
Map.of(),
Collections.emptyList(),
Collections.emptyList()
));

Expand All @@ -125,6 +128,7 @@ void shouldSurviveIfPluginIsNotInUpdateCenter() {
when(ctx.getUpdateCenter()).thenReturn(new UpdateCenter(
Map.of(),
Map.of(),
Collections.emptyList(),
Collections.emptyList()
));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ void shouldBeAbleToFindInstallationCountInUpdateCenter() {
new io.jenkins.pluginhealth.scoring.model.updatecenter.Plugin(pluginName, null, null, null, List.of(), 100, "", "main")
),
Map.of(),
List.of(),
List.of()
));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ void shouldFailIfPluginNotInUpdateCenter() {
when(ctx.getUpdateCenter()).thenReturn(new UpdateCenter(
Map.of(),
Map.of(),
List.of(),
List.of()
));

Expand Down Expand Up @@ -99,6 +100,7 @@ void shouldBeAbleToExtractJenkinsVersionFromUpdateCenter() {
)
),
Map.of(),
List.of(),
List.of()
));

Expand Down
Loading
Loading