Skip to content

Commit 944f41f

Browse files
authored
Merge pull request #9 from thepracticaldeveloper/feature-filters
#7 Ability to set up included/excluded projects
2 parents 94622d9 + 22c4e16 commit 944f41f

File tree

8 files changed

+217
-18
lines changed

8 files changed

+217
-18
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@
9898
<dependency>
9999
<groupId>junit</groupId>
100100
<artifactId>junit</artifactId>
101-
<version>4.11</version>
101+
<version>4.13.1</version>
102102
<scope>test</scope>
103103
</dependency>
104104
<dependency>

src/main/java/io/tpd/quboo/sonarplugin/dtos/IssuesWrapper.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import io.tpd.quboo.sonarplugin.pojos.Issue;
44
import io.tpd.quboo.sonarplugin.pojos.Issues;
55
import io.tpd.quboo.sonarplugin.util.QubooCache;
6+
import org.sonar.api.utils.log.Logger;
7+
import org.sonar.api.utils.log.Loggers;
68

79
import java.util.ArrayList;
810
import java.util.List;
@@ -12,6 +14,8 @@
1214

1315
public class IssuesWrapper {
1416

17+
private static final Logger log = Loggers.get(IssuesWrapper.class);
18+
1519
private List<Issue> issues;
1620
private String version = QUBOO_API_VERSION;
1721
private String sonarVersion;
@@ -20,9 +24,25 @@ public IssuesWrapper() {
2024
this.issues = new ArrayList<>();
2125
}
2226

23-
public void filterAndAddIssues(final Issues issues, final String sonarVersion) {
27+
public void filterAndAddIssues(final Issues issues,
28+
final List<String> selectedProjects,
29+
final List<String> excludedProjects,
30+
final String sonarVersion) {
31+
log.info("Quboo project filters - Included: {} | Excluded: {}",
32+
selectedProjects, excludedProjects);
2433
this.issues.addAll(
25-
issues.getIssues().stream().filter(issue -> !QubooCache.INSTANCE.inCache(issue)).collect(Collectors.toList())
34+
issues.getIssues().stream()
35+
.filter(issue -> issue.getProject() == null ||
36+
selectedProjects == null || selectedProjects.isEmpty() ||
37+
selectedProjects.contains(issue.getProject())
38+
)
39+
.filter(issue -> issue.getProject() == null ||
40+
(selectedProjects != null && !selectedProjects.isEmpty()) ||
41+
excludedProjects == null || excludedProjects.isEmpty() ||
42+
!excludedProjects.contains(issue.getProject())
43+
)
44+
.filter(issue -> !QubooCache.INSTANCE.inCache(issue))
45+
.collect(Collectors.toList())
2646
);
2747
this.sonarVersion = sonarVersion;
2848
}

src/main/java/io/tpd/quboo/sonarplugin/hooks/QubooConnector.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@
1111
import io.tpd.quboo.sonarplugin.settings.QubooProperties;
1212
import io.tpd.quboo.sonarplugin.util.QubooCache;
1313
import okhttp3.*;
14+
import org.apache.commons.lang3.StringUtils;
1415
import org.sonar.api.ce.posttask.PostProjectAnalysisTask;
1516
import org.sonar.api.platform.Server;
1617
import org.sonar.api.utils.log.Logger;
1718
import org.sonar.api.utils.log.Loggers;
1819

20+
import java.util.ArrayList;
1921
import java.util.Base64;
22+
import java.util.List;
2023

2124
import static io.tpd.quboo.sonarplugin.settings.QubooProperties.DEFAULT_ACCESS_KEY;
2225
import static org.apache.commons.lang3.StringUtils.isEmpty;
@@ -30,6 +33,8 @@ public class QubooConnector implements PostProjectAnalysisTask {
3033
private final Logger log = Loggers.get(getClass());
3134
private final ObjectMapper mapper;
3235
private OkHttpClient http;
36+
private List<String> selectedProjects;
37+
private List<String> rejectedProjects;
3338

3439
public QubooConnector(final Server server) {
3540
this.http = new HttpClients().getQubooTrustedOkHttpClient();
@@ -50,6 +55,8 @@ public void finished(ProjectAnalysis analysis) {
5055
final String qubooKey = analysis.getScannerContext().getProperties().get(QubooProperties.ACCESS_KEY);
5156
final String qubooSecret = analysis.getScannerContext().getProperties().get(QubooProperties.SECRET_KEY);
5257
final String token = analysis.getScannerContext().getProperties().get(QubooProperties.TOKEN_KEY);
58+
this.selectedProjects = extractProjectList(analysis.getScannerContext().getProperties().get(QubooProperties.SELECTED_PROJECTS_KEY));
59+
this.rejectedProjects = extractProjectList(analysis.getScannerContext().getProperties().get(QubooProperties.REJECTED_PROJECTS_KEY));
5360
if (!isEmpty(token)) {
5461
log.info("A token will be used to connect to SonarQube server");
5562
}
@@ -68,6 +75,19 @@ public void finished(ProjectAnalysis analysis) {
6875
}
6976
}
7077

78+
private List<String> extractProjectList(final String commaSeparatedProjects) {
79+
if (StringUtils.isBlank(commaSeparatedProjects)) {
80+
return new ArrayList<>();
81+
} else {
82+
List<String> projects = new ArrayList<>();
83+
String[] projectArray = commaSeparatedProjects.trim().split(",");
84+
for (String p : projectArray) {
85+
projects.add(p.trim()); // in case of spaces between commas
86+
}
87+
return projects;
88+
}
89+
}
90+
7191
private void sendIssuesToQuboo(final IssuesWrapper allIssues, final String qubooKey, final String qubooSecret) throws Exception {
7292
if (!allIssues.getIssues().isEmpty()) {
7393
final Request request = new Request.Builder()
@@ -104,7 +124,7 @@ private IssuesWrapper getIssues(final String token) {
104124
final String body = response.body().string();
105125
final Issues issues = mapper.readValue(body, Issues.class);
106126
log.info("Quboo plugin got {} issues", issues.getIssues().size());
107-
wrapper.filterAndAddIssues(issues, server.getVersion());
127+
wrapper.filterAndAddIssues(issues, selectedProjects, rejectedProjects, server.getVersion());
108128
moreData = moreData(issues.getPaging(), issues.getIssues().size());
109129
pageNumber++;
110130
if (pageNumber > 50) { // there is a hard limit in Sonar API that doesn't allow querying more than 10K results

src/main/java/io/tpd/quboo/sonarplugin/hooks/QubooSensor.java

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,22 @@ public void describe(final SensorDescriptor descriptor) {
2020

2121
@Override
2222
public void execute(final SensorContext context) {
23-
final Optional<String> key = context.config().get(QubooProperties.ACCESS_KEY);
24-
final Optional<String> secret = context.config().get(QubooProperties.SECRET_KEY);
25-
final Optional<String> token = context.config().get(QubooProperties.TOKEN_KEY);
26-
key.ifPresent(accessKey -> context.addContextProperty(QubooProperties.ACCESS_KEY, accessKey));
27-
secret.ifPresent(s -> context.addContextProperty(QubooProperties.SECRET_KEY, s));
28-
token.ifPresent(s -> context.addContextProperty(QubooProperties.TOKEN_KEY, s));
29-
if (key.isPresent() && key.get().equals(QubooProperties.DEFAULT_ACCESS_KEY)) {
23+
toContextPropertyIfPresent(context, QubooProperties.ACCESS_KEY);
24+
toContextPropertyIfPresent(context, QubooProperties.SECRET_KEY);
25+
toContextPropertyIfPresent(context, QubooProperties.TOKEN_KEY);
26+
toContextPropertyIfPresent(context, QubooProperties.SELECTED_PROJECTS_KEY);
27+
toContextPropertyIfPresent(context, QubooProperties.REJECTED_PROJECTS_KEY);
28+
Optional<String> accessKeyOptional = context.config().get(QubooProperties.ACCESS_KEY);
29+
if (accessKeyOptional.isPresent() && accessKeyOptional.get().equals(QubooProperties.DEFAULT_ACCESS_KEY)) {
3030
log.warn("WARNING: Quboo will ignore this analysis because you haven't set the Quboo Access (and Secret) Keys. Go to your Sonarqube server (as admin), Administration -> Configuration -> Quboo and enter the values you find in your Quboo account settings.");
3131
} else {
32-
log.info("Access key is " + key.orElse("NOT PRESENT"));
32+
log.info("Access key is " + accessKeyOptional.orElse("NOT PRESENT"));
3333
}
3434
}
35+
36+
private static void toContextPropertyIfPresent(final SensorContext context,
37+
final String propertyName) {
38+
context.config().get(propertyName).ifPresent(p ->
39+
context.addContextProperty(propertyName, p));
40+
}
3541
}

src/main/java/io/tpd/quboo/sonarplugin/settings/QubooProperties.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ public class QubooProperties {
3434
public static final String DEFAULT_ACCESS_KEY = "your-access-key";
3535
public static final String DEFAULT_SECRET_KEY = "your-secret-key";
3636
public static final String CATEGORY = "Quboo";
37+
public static final String SELECTED_PROJECTS_KEY = "sonar.quboo.selected-projects";
38+
public static final String REJECTED_PROJECTS_KEY = "sonar.quboo.rejected-projects";
3739

3840
private QubooProperties() {
3941
// only statics
@@ -46,22 +48,46 @@ public static List<PropertyDefinition> getProperties() {
4648
.description("Your organization account access key to export report summary to Quboo")
4749
.defaultValue(DEFAULT_ACCESS_KEY)
4850
.category(CATEGORY)
51+
.subCategory("Account Keys")
4952
.index(1)
5053
.build(),
5154
PropertyDefinition.builder(SECRET_KEY)
5255
.name("Quboo Secret Key")
5356
.description("Your organization account secret key to export report summary to Quboo")
5457
.defaultValue(DEFAULT_SECRET_KEY)
5558
.category(CATEGORY)
59+
.subCategory("Account Keys")
5660
.index(2)
5761
.build(),
5862
PropertyDefinition.builder(TOKEN_KEY)
5963
.name("API Token")
6064
.description("You need to enter a valid API token if your SonarQube server requires authentication")
6165
.defaultValue("")
62-
.category(CATEGORY)
66+
.subCategory("Secured servers")
6367
.type(PropertyType.PASSWORD)
6468
.index(3)
69+
.build(),
70+
PropertyDefinition.builder(SELECTED_PROJECTS_KEY)
71+
.name("Selected projects")
72+
.description("If you want to select the Sonarqube projects that should be processed by Quboo, enter here" +
73+
" their project names separated by commas, for example: my-project-1,my-project-2. Leave it empty" +
74+
" to include all projects.")
75+
.defaultValue("")
76+
.category(CATEGORY)
77+
.type(PropertyType.STRING)
78+
.subCategory("Filters")
79+
.index(4)
80+
.build(),
81+
PropertyDefinition.builder(REJECTED_PROJECTS_KEY)
82+
.name("Excluded projects")
83+
.description("If you want to exclude Sonarqube projects from Quboo, enter here" +
84+
" their project names separated by commas, for example: my-project-3,my-project-4. Leave it empty" +
85+
" to include all projects. Note: If you're using the 'Selected projects' list, this list has no effect.")
86+
.defaultValue("")
87+
.category(CATEGORY)
88+
.subCategory("Filters")
89+
.type(PropertyType.STRING)
90+
.index(5)
6591
.build()
6692
);
6793
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package io.tpd.quboo.sonarplugin.dtos;
2+
3+
import io.tpd.quboo.sonarplugin.pojos.Issue;
4+
import io.tpd.quboo.sonarplugin.pojos.Issues;
5+
import org.junit.Before;
6+
import org.junit.Test;
7+
8+
import java.util.ArrayList;
9+
import java.util.Arrays;
10+
import java.util.List;
11+
12+
import static org.assertj.core.api.Assertions.assertThat;
13+
14+
public class IssuesWrapperTest {
15+
16+
private IssuesWrapper w;
17+
18+
@Before
19+
public void setup() {
20+
w = new IssuesWrapper();
21+
}
22+
23+
@Test
24+
public void issueWithoutProjectIncluded() {
25+
Issues issues = buildIssuesWithProjects(null, null);
26+
List<String> selected = projects("project1");
27+
List<String> rejected = projects("project2");
28+
w.filterAndAddIssues(issues, selected, rejected, null);
29+
30+
assertThat(w.getIssues()).hasSize(2);
31+
}
32+
33+
@Test
34+
public void issueWithNoMatchIncluded() {
35+
Issues issues = buildIssuesWithProjects("project3");
36+
List<String> selected = projects();
37+
List<String> rejected = projects("project2");
38+
w.filterAndAddIssues(issues, selected, rejected, null);
39+
40+
assertThat(w.getIssues()).hasSize(1);
41+
}
42+
43+
@Test
44+
public void issueWithSelectedMatchIncluded() {
45+
Issues issues = buildIssuesWithProjects("project1");
46+
List<String> selected = projects("project1");
47+
List<String> rejected = projects("project3");
48+
w.filterAndAddIssues(issues, selected, rejected, null);
49+
50+
assertThat(w.getIssues()).hasSize(1);
51+
}
52+
53+
@Test
54+
public void issueWithNoSelectedMatchExcluded() {
55+
Issues issues = buildIssuesWithProjects("project2");
56+
List<String> selected = projects("project1");
57+
List<String> rejected = projects();
58+
w.filterAndAddIssues(issues, selected, rejected, null);
59+
60+
assertThat(w.getIssues()).isEmpty();
61+
}
62+
63+
@Test
64+
public void issueWithDoubleMatchNotExcluded() {
65+
Issues issues = buildIssuesWithProjects("project1");
66+
List<String> selected = projects("project1");
67+
List<String> rejected = projects("project1");
68+
w.filterAndAddIssues(issues, selected, rejected, null);
69+
70+
assertThat(w.getIssues()).hasSize(1);
71+
}
72+
73+
@Test
74+
public void allIssuesIncludedWithEmptyFilters() {
75+
Issues issues = buildIssuesWithProjects("project1", null, "project2", "project3");
76+
List<String> selected = projects();
77+
List<String> rejected = projects();
78+
w.filterAndAddIssues(issues, selected, rejected, null);
79+
80+
assertThat(w.getIssues()).hasSize(4);
81+
}
82+
83+
@Test
84+
public void multipleExcluded() {
85+
Issues issues = buildIssuesWithProjects("project1", null, "project2", "project3");
86+
List<String> selected = projects();
87+
List<String> rejected = projects("project1", "project3");
88+
w.filterAndAddIssues(issues, selected, rejected, null);
89+
90+
assertThat(w.getIssues()).extracting("project").containsExactlyInAnyOrder(null, "project2");
91+
}
92+
93+
@Test
94+
public void bothFilters1() {
95+
Issues issues = buildIssuesWithProjects("project1", null, "project2", "project3", "project5");
96+
List<String> selected = projects("project2");
97+
List<String> rejected = projects("project1", "project3");
98+
w.filterAndAddIssues(issues, selected, rejected, null);
99+
100+
assertThat(w.getIssues()).extracting("project").containsExactlyInAnyOrder(null, "project2");
101+
}
102+
103+
private List<String> projects(String... s) {
104+
List<String> selected = new ArrayList<>();
105+
if(s != null) {
106+
selected.addAll(Arrays.asList(s));
107+
}
108+
return selected;
109+
}
110+
111+
private Issues buildIssuesWithProjects(String... projects) {
112+
Issues issues = new Issues();
113+
issues.setIssues(new ArrayList<>());
114+
for (String project : projects) {
115+
Issue i = new Issue();
116+
i.setProject(project);
117+
issues.getIssues().add(i);
118+
}
119+
return issues;
120+
}
121+
}

src/test/java/io/tpd/quboo/sonarplugin/hooks/QubooConnectorTest.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,15 +177,18 @@ private Issues generateIssuesPage1() {
177177
final Issue issue2 = new Issue().withAssignee("player2").withResolution("closed").withDebt("1h")
178178
.withRule("InsufficientCoverage").withKey("issue-2").withProject("project").withAuthor("author")
179179
.withSeverity("major").withStatus("fixed");
180-
final Paging paging = new Paging().withPageIndex(1).withPageSize(2).withTotal(3);
181-
return new Issues().withIssues(Arrays.asList(issue1, issue2)).withPaging(paging);
180+
final Issue issue3 = new Issue().withAssignee("player2").withResolution("closed").withDebt("1h")
181+
.withRule("InsufficientCoverage").withKey("issue-2").withProject("project-x").withAuthor("author")
182+
.withSeverity("major").withStatus("fixed"); // should be excluded
183+
final Paging paging = new Paging().withPageIndex(1).withPageSize(3).withTotal(4);
184+
return new Issues().withIssues(Arrays.asList(issue1, issue2, issue3)).withPaging(paging);
182185
}
183186

184187
private Issues generateIssuesPage2() {
185188
final Issue issue1 = new Issue().withAssignee("player1").withResolution("open").withDebt("1h")
186189
.withRule("InsufficientCoverage").withKey("issue-3").withProject("project").withAuthor("author")
187190
.withSeverity("major").withStatus("open").withType("RELIABILITY").withTags(Lists.list("tag"));
188-
final Paging paging = new Paging().withPageIndex(2).withPageSize(2).withTotal(3);
191+
final Paging paging = new Paging().withPageIndex(2).withPageSize(3).withTotal(4);
189192
return new Issues().withIssues(Collections.singletonList(issue1)).withPaging(paging);
190193
}
191194

@@ -246,6 +249,8 @@ private Map<String, String> fullPropertiesMap() {
246249
map.put(QubooProperties.ACCESS_KEY, ACCESS_KEY_VALUE);
247250
map.put(QubooProperties.SECRET_KEY, SECRET_KEY_VALUE);
248251
map.put(QubooProperties.TOKEN_KEY, TOKEN_VALUE);
252+
map.put(QubooProperties.SELECTED_PROJECTS_KEY, "");
253+
map.put(QubooProperties.REJECTED_PROJECTS_KEY, "project-y, project-x");
249254
return map;
250255
}
251256

src/test/java/io/tpd/quboo/sonarplugin/settings/QubooPropertiesTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ public class QubooPropertiesTest {
1515
public void allPropertiesConfigured() {
1616
final List<PropertyDefinition> properties = QubooProperties.getProperties();
1717

18-
assertThat(properties.size()).isEqualTo(3);
18+
assertThat(properties.size()).isEqualTo(5);
1919
assertThat(properties.stream().map(PropertyDefinition::key).collect(Collectors.toList()))
20-
.containsExactly(QubooProperties.ACCESS_KEY, QubooProperties.SECRET_KEY, QubooProperties.TOKEN_KEY);
20+
.containsExactly(QubooProperties.ACCESS_KEY, QubooProperties.SECRET_KEY, QubooProperties.TOKEN_KEY,
21+
QubooProperties.SELECTED_PROJECTS_KEY, QubooProperties.REJECTED_PROJECTS_KEY);
2122
assertThat(properties.get(2).type()).isEqualTo(PropertyType.PASSWORD);
2223
}
2324
}

0 commit comments

Comments
 (0)