Skip to content

Commit 5270c8a

Browse files
Merge pull request #20 from cqse/ts/43166_option_to_evaluate_threshold_for_sub_path
Option to evaluate findings and threshold for a specific sub path (`--uniform-path`)
2 parents 45cd7c9 + 01015e4 commit 5270c8a

File tree

4 files changed

+37
-20
lines changed

4 files changed

+37
-20
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ Example: master:1597845930000
4444
The version control commit revision for which analysis results should be obtained. This is typically the commit that the
4545
current CI pipeline is building. Can be either a Git SHA1, a SVN revision number or a Team Foundation changeset ID.
4646

47+
**--uniform-path**
48+
Set this option to filter the location of findings and metric assessments.
49+
4750
**-f**, **--evaluate-findings**
4851
If this option is set, findings introduced with the given commit will be evaluated.
4952

src/main/java/com/teamscale/buildbreaker/commandline/BuildBreaker.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ public void setTeamscaleServerUrl(String teamscaleServerUrl) {
9090
description = "The project ID or alias (NOT the project name!) relevant for the analysis.")
9191
private String project;
9292

93+
/** To get Metrics and Findings for a subpath of the project */
94+
@Option(names = {"--uniform-path"}, defaultValue = "",
95+
description = "Uniform path of requested file or directory.")
96+
public String uniformPath;
97+
9398
@ArgGroup(multiplicity = "1")
9499
private CommitOptions commitOptions;
95100

@@ -192,17 +197,17 @@ private EvaluationResult evaluateFindings() throws IOException, TooManyCommitsEx
192197
Pair<List<Finding>, List<Finding>> findingAssessments;
193198
if (StringUtils.isEmpty(targetBranchAndTimestamp) && StringUtils.isEmpty(baseBranchAndTimestamp)) {
194199
System.out.println("Evaluating findings for the current commit...");
195-
findingAssessments = teamscaleClient.fetchFindingsUsingCommitDetails(currentBranchAndTimestamp);
200+
findingAssessments = teamscaleClient.fetchFindingsUsingCommitDetails(currentBranchAndTimestamp, uniformPath);
196201
} else if (!StringUtils.isEmpty(targetBranchAndTimestamp)) {
197202
waitForAnalysisToFinish(targetBranchAndTimestamp);
198203
System.out.println("Evaluating findings by comparing the current commit with target commit '" +
199204
targetBranchAndTimestamp + "'...");
200-
findingAssessments = teamscaleClient.fetchFindingsUsingBranchMergeDelta(currentBranchAndTimestamp, targetBranchAndTimestamp);
205+
findingAssessments = teamscaleClient.fetchFindingsUsingBranchMergeDelta(currentBranchAndTimestamp, targetBranchAndTimestamp, uniformPath);
201206
} else {
202207
waitForAnalysisToFinish(baseBranchAndTimestamp);
203208
System.out.println("Evaluating findings by aggregating the findings from the base commit '" +
204209
baseBranchAndTimestamp + "' up to the current commit '" + currentBranchAndTimestamp + "' ...");
205-
findingAssessments = teamscaleClient.fetchFindingsUsingLinearDelta(baseBranchAndTimestamp, currentBranchAndTimestamp);
210+
findingAssessments = teamscaleClient.fetchFindingsUsingLinearDelta(baseBranchAndTimestamp, currentBranchAndTimestamp, uniformPath);
206211
}
207212

208213
EvaluationResult findingsResult = new FindingsEvaluator()
@@ -249,7 +254,7 @@ private HttpUrl buildFindingsUiUrl(String targetBranchAndTimestamp, String baseB
249254
private EvaluationResult evaluateMetrics() throws IOException, HttpRedirectException, HttpStatusCodeException, TooManyCommitsException, CommitCouldNotBeResolvedException, ParserException {
250255
System.out.println("Evaluating thresholds...");
251256
String currentBranchAndTimestamp = determineBranchAndTimestamp();
252-
List<MetricViolation> metricAssessments = teamscaleClient.fetchMetricAssessments(currentBranchAndTimestamp, thresholdEvalOptions.thresholdConfig);
257+
List<MetricViolation> metricAssessments = teamscaleClient.fetchMetricAssessments(currentBranchAndTimestamp, thresholdEvalOptions.thresholdConfig, uniformPath);
253258
EvaluationResult metricResult =
254259
new MetricsEvaluator().evaluate(metricAssessments, thresholdEvalOptions.failOnYellowMetrics);
255260
System.out.println(metricResult);

src/main/java/com/teamscale/buildbreaker/evaluation/MetricViolation.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ public MetricViolation(String displayName, String yellowThreshold, String redThr
1818
@Override
1919
public String toString() {
2020
return String.format("%s: \n\tThresholds (yellow/red): %s/%s\n\tActual value: %s",
21-
displayName, yellowThreshold, redThreshold, formattedTextValue);
21+
displayName,
22+
yellowThreshold.isEmpty() ? "-" : yellowThreshold,
23+
redThreshold.isEmpty() ? "-" : redThreshold,
24+
formattedTextValue
25+
);
2226
}
2327
}

src/main/java/com/teamscale/buildbreaker/teamscale_client/TeamscaleClient.java

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,7 @@
2727
import org.conqat.lib.commons.string.StringUtils;
2828

2929
import java.io.IOException;
30-
import java.util.ArrayList;
31-
import java.util.HashMap;
32-
import java.util.List;
33-
import java.util.Map;
34-
import java.util.Objects;
35-
import java.util.Optional;
30+
import java.util.*;
3631
import java.util.function.Supplier;
3732
import java.util.regex.Matcher;
3833
import java.util.regex.Pattern;
@@ -70,7 +65,7 @@ public TeamscaleClient(OkHttpClient client, HttpUrl teamscaleServerUrl, String u
7065
* @throws HttpStatusCodeException if an HTTP error code was returned by Teamscale
7166
* @throws ParserException if there was an error parsing Teamscale's response
7267
*/
73-
public Pair<List<Finding>, List<Finding>> fetchFindingsUsingCommitDetails(String branchAndTimestamp) throws IOException, HttpRedirectException, HttpStatusCodeException, ParserException {
68+
public Pair<List<Finding>, List<Finding>> fetchFindingsUsingCommitDetails(String branchAndTimestamp, String uniformPath) throws IOException, HttpRedirectException, HttpStatusCodeException, ParserException {
7469
HttpUrl.Builder builder =
7570
teamscaleServerUrl.newBuilder()
7671
.addPathSegment("api")
@@ -82,7 +77,7 @@ public Pair<List<Finding>, List<Finding>> fetchFindingsUsingCommitDetails(String
8277
HttpUrl url = builder.build();
8378
Request request = createAuthenticatedGetRequest(url);
8479
String response = sendRequest(request);
85-
return parseCommitFindingResponse(response);
80+
return parseCommitFindingResponse(response, uniformPath);
8681
}
8782

8883
/**
@@ -92,13 +87,13 @@ public Pair<List<Finding>, List<Finding>> fetchFindingsUsingCommitDetails(String
9287
* @throws ParserException if there was an error parsing Teamscale's response
9388
* @implNote The API we use here is not yet a public API (<a href="https://cqse.atlassian.net/browse/TS-44014">TS-44014</a>), so we don't use a versioned endpoint.
9489
*/
95-
public Pair<List<Finding>, List<Finding>> fetchFindingsUsingLinearDelta(String startBranchAndTimestamp, String endBranchAndTimestamp) throws IOException, HttpRedirectException, HttpStatusCodeException, ParserException {
90+
public Pair<List<Finding>, List<Finding>> fetchFindingsUsingLinearDelta(String startBranchAndTimestamp, String endBranchAndTimestamp, String uniformPath) throws IOException, HttpRedirectException, HttpStatusCodeException, ParserException {
9691
HttpUrl.Builder builder =
9792
teamscaleServerUrl.newBuilder().addPathSegments("api/projects").addPathSegment(project)
9893
.addPathSegments("findings/delta")
9994
.addQueryParameter("t2", endBranchAndTimestamp)
10095
.addQueryParameter("t1", startBranchAndTimestamp)
101-
.addQueryParameter("uniform-path", "");
96+
.addQueryParameter("uniform-path", uniformPath);
10297

10398
HttpUrl url = builder.build();
10499
Request request = createAuthenticatedGetRequest(url);
@@ -113,7 +108,7 @@ public Pair<List<Finding>, List<Finding>> fetchFindingsUsingLinearDelta(String s
113108
* @throws ParserException if there was an error parsing Teamscale's response
114109
* @implNote The API we use here is not yet a public API (<a href="https://cqse.atlassian.net/browse/TS-44014">TS-44014</a>), so we don't use a versioned endpoint.
115110
*/
116-
public Pair<List<Finding>, List<Finding>> fetchFindingsUsingBranchMergeDelta(String sourceBranchAndTimestamp, String targetBranchAndTimestamp) throws IOException, HttpRedirectException, HttpStatusCodeException, ParserException {
111+
public Pair<List<Finding>, List<Finding>> fetchFindingsUsingBranchMergeDelta(String sourceBranchAndTimestamp, String targetBranchAndTimestamp, String uniformPath) throws IOException, HttpRedirectException, HttpStatusCodeException, ParserException {
117112

118113
HttpUrl.Builder builder =
119114
teamscaleServerUrl.newBuilder()
@@ -122,6 +117,9 @@ public Pair<List<Finding>, List<Finding>> fetchFindingsUsingBranchMergeDelta(Str
122117
.addPathSegments("merge-requests/finding-churn")
123118
.addQueryParameter("source", sourceBranchAndTimestamp)
124119
.addQueryParameter("target", targetBranchAndTimestamp);
120+
if(!uniformPath.isEmpty()) {
121+
builder.addQueryParameter("included-paths", uniformPath);
122+
}
125123

126124
HttpUrl url = builder.build();
127125
Request request = createAuthenticatedGetRequest(url);
@@ -135,13 +133,13 @@ public Pair<List<Finding>, List<Finding>> fetchFindingsUsingBranchMergeDelta(Str
135133
* @throws HttpStatusCodeException if an HTTP error code was returned by Teamscale
136134
* @throws ParserException if there was an error parsing Teamscale's response
137135
*/
138-
public List<MetricViolation> fetchMetricAssessments(String branchAndTimestamp, String thresholdConfig) throws IOException, HttpRedirectException, HttpStatusCodeException, ParserException {
136+
public List<MetricViolation> fetchMetricAssessments(String branchAndTimestamp, String thresholdConfig, String uniformPath) throws IOException, HttpRedirectException, HttpStatusCodeException, ParserException {
139137
HttpUrl.Builder builder =
140138
teamscaleServerUrl.newBuilder()
141139
.addPathSegments("api/projects")
142140
.addPathSegment(project)
143141
.addPathSegment("metric-assessments")
144-
.addQueryParameter("uniform-path", "")
142+
.addQueryParameter("uniform-path", uniformPath)
145143
.addQueryParameter("t", branchAndTimestamp)
146144
.addQueryParameter("configuration-name", thresholdConfig);
147145
HttpUrl url = builder.build();
@@ -251,14 +249,19 @@ private boolean isAnalysisFinished(String branch, long timestamp) throws IOExcep
251249
}
252250
}
253251

254-
private Pair<List<Finding>, List<Finding>> parseCommitFindingResponse(String response) throws ParserException {
252+
private Pair<List<Finding>, List<Finding>> parseCommitFindingResponse(String response, String uniformPath) throws ParserException {
255253
try {
256-
return tryParseFindingsResponse(response, "$.addedFindings.*", "$.findingsInChangedCode.*");
254+
Pair<List<Finding>, List<Finding>> result = tryParseFindingsResponse(response, "$.addedFindings.*", "$.findingsInChangedCode.*");
255+
return Pair.createPair(filterFindingLocations(result.getFirst(), uniformPath), filterFindingLocations(result.getSecond(), uniformPath));
257256
} catch (ParserException | PathNotFoundException e) {
258257
throw new ParserException("Could not parse findings JSON response:\n" + response + "\n\nPlease contact CQSE with an error report.", e);
259258
}
260259
}
261260

261+
private List<Finding> filterFindingLocations(List<Finding> findings, String uniformPath) {
262+
return findings.stream().filter(finding -> finding.uniformPath.startsWith(uniformPath)).collect(Collectors.toList());
263+
}
264+
262265
/**
263266
* @implNote The APIs we use in {@link #fetchFindingsUsingBranchMergeDelta} and {@link #fetchFindingsUsingLinearDelta}
264267
* are not yet public APIs (<a href="https://cqse.atlassian.net/browse/TS-44014">TS-44014</a>).
@@ -353,6 +356,8 @@ private List<MetricViolation> parseMetricResponse(String response) throws Parser
353356
assessment.add(ETrafficLightColor.values()[i], mapping[i]);
354357
}
355358
formattedTextValue = assessment.toFormattedColorDistributionString();
359+
} else if ("NUMERIC".equals(schemaEntry.get("valueType"))) {
360+
formattedTextValue = String.format(Locale.ENGLISH, "%.4f", metricViolation.get("value"));
356361
} else {
357362
formattedTextValue = metricViolation.get("value").toString();
358363
}

0 commit comments

Comments
 (0)