Skip to content
Open
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package us.muit.fs.a4i.control.strategies;

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

import org.json.JSONArray;

public class GetDataPullRequest {

private final HttpClient httpClient;

public GetDataPullRequest() {
this.httpClient = HttpClient.newHttpClient();
}

public int getTotalPullRequests(String owner, String repo) throws IOException, InterruptedException {
JSONArray prArray = fetchPullRequests(owner, repo, "all");
return prArray.length();
}

public int getClosedPullRequests(String owner, String repo) throws IOException, InterruptedException {
JSONArray prArray = fetchPullRequests(owner, repo, "closed");
return prArray.length();
}

private JSONArray fetchPullRequests(String owner, String repo, String state) throws IOException, InterruptedException {
String url = String.format("https://api.github.com/repos/%s/%s/pulls?state=%s&per_page=100", owner, repo, state);

HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("Accept", "application/vnd.github.v3+json")
.build();

HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());

if (response.statusCode() != 200) {
throw new IOException("GitHub API error: " + response.statusCode());
}

return new JSONArray(response.body());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import java.util.List;
import java.util.Optional;
import java.util.Arrays;
import us.muit.fs.a4i.exceptions.NotAvailableMetricException;
import us.muit.fs.a4i.exceptions.ReportItemException;
import us.muit.fs.a4i.model.entities.IndicatorI.IndicatorState;
import us.muit.fs.a4i.model.entities.ReportItem;

public class PullRequestIndicatorStrategy implements IndicatorStrategy<Double> { //this defines the indicator as a double

private static final Logger log = Logger.getLogger(PullRequestIndicatorStrategy.class.getName()); //to show error messages during the execution
private static final List<String> REQUIRED_METRICS = Arrays.asList("totalPullReq", "closedPullReq"); //this defines the mandatory metrics to evaluate the inidcator, so the total and closed pull requests

@Override
public ReportItem<Double> calcIndicator(List<ReportItem<Double>> metrics) throws NotAvailableMetricException { //method that evaluate the actual indicator, it obtains as an input the list of pullrequests

ReportItem<Double> indicatorReport = null;
IndicatorState estado = IndicatorState.UNDEFINED;

// Filter necessary metrics
Optional<ReportItem<Double>> totalPullReq = metrics.stream()
.filter(m -> REQUIRED_METRICS.get(0).equals(m.getName()))
.findAny();

Optional<ReportItem<Double>> closedPullReq = metrics.stream()
.filter(m -> REQUIRED_METRICS.get(1).equals(m.getName()))
.findAny();

//here search for the mandatory metrics, and saves them in an Optional

// If both metrics are present it evaluates the inidcaotr, if one of them is missing goes to the else and gives an error
if (totalPullReq.isPresent() && closedPullReq.isPresent()) {
// Calculate the indicator
Double pullRequestIndicator = 0.0;
if (totalPullReq.get().getValue() > 0) { //this is done to remove the possibility of dividign by zero
pullRequestIndicator = 100 * closedPullReq.get().getValue() / totalPullReq.get().getValue();
}

// Determine indicator state
if (pullRequestIndicator > 75) {
estado = IndicatorState.Correct;
} else if (pullRequestIndicator > 50) {
estado = IndicatorState.Precaution;
} else {
estado = IndicatorState.Critical;
}

// this create an object report that conatins the result of the class, so it has a name: PullRequestIndicator, the evaluated number, the metrics used for the calculation, the state
try {
indicatorReport = new ReportItem.ReportItemBuilder<Double>("pullRequestIndicator", pullRequestIndicator)
.metrics(Arrays.asList(totalPullReq.get(), closedPullReq.get()))
.indicator(estado)
.build();
} else {
log.info("Some required metrics are missing");
throw new NotAvailableMetricException(REQUIRED_METRICS.toString());
}

return indicatorReport; //return the indicator
}

@Override
public List<String> requiredMetrics() {
return REQUIRED_METRICS; //retrurn the metrics used to evaluate the indicator
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import static org.junit.jupiter.api.Assertions.*; //JUnit methods
import org.junit.jupiter.api.Test; //to indicate that is a test
import java.util.List;

public class GetDataPullRequestTest {

@Test
public void testGetPullRequests_basic() throws Exception {
GetDataPullRequest enquirer = new GetDataPullRequest("owner", "repo", "token"); //to get information about the repository

List<PullRequest> pullRequests = enquirer.getPullRequests(); //this calls the method to get the list of PUllRequest

assertNotNull(pullRequests, "the list cannot be null"); //control that the list is not null unless it gives the error message

// assertFalse(pullRequests.isEmpty(), "the list cannot be empty"); //this is to control that the list is not empty, only if we always want that at least one pull request is done

for (PullRequest pr : pullRequests) {
assertNotNull(pr.getState(), "every PullRequest must have a state");
assertTrue(pr.getState().equals("open") || pr.getState().equals("closed"),
"Lo stato deve essere 'open' o 'closed'");
//this controls the parameter of the pull request, so it control that the object are passed correctly from JSON to java, the pull request must have a state and the state has to be or open or closed
//the indicator then evalyate the percentage of closed pull request on the total number
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package us.muit.fs.a4i.test.control.strategies;

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

public class IndicatorPullRequestTest {

IndicatorPullRequest indicator = new IndicatorPullRequest();

@Test
public void testEfficiencyCalculationSimple() {
double efficiency = indicator.calculateEfficiency(75, 100);
assertEquals(75.0, efficiency);
}

@Test
public void testQualityLevelCorrecto() {
String level = indicator.evaluateQualityLevel(80.0);
assertEquals("Correcto", level);
}

@Test
public void testQualityLevelPrecaucion() {
String level = indicator.evaluateQualityLevel(60.0);
assertEquals("Precaución", level);
}

@Test
public void testQualityLevelCritico() {
String level = indicator.evaluateQualityLevel(40.0);
assertEquals("Crítico", level);
}

@Test
public void testZeroTotalPRs() {
double efficiency = indicator.calculateEfficiency(0, 0);
assertEquals(0.0, efficiency);
}

@Test
public void testMoreClosedThanTotal() {
Exception exception = assertThrows(IllegalArgumentException.class, () -> {
indicator.calculateEfficiency(110, 100);
});
assertEquals("Closed PRs cannot exceed total PRs", exception.getMessage());
}

@Test
public void testNegativeInputs() {
assertThrows(IllegalArgumentException.class, () -> {
indicator.calculateEfficiency(-1, 100);
});
assertThrows(IllegalArgumentException.class, () -> {
indicator.calculateEfficiency(10, -100);
});
}

@Test
public void testPerfectScore() {
double efficiency = indicator.calculateEfficiency(10, 10);
assertEquals(100.0, efficiency);
assertEquals("Correcto", indicator.evaluateQualityLevel(efficiency));
}
}
Loading