Skip to content

Commit b40339f

Browse files
committed
Merge pull request #86 from steven-sheehy
* pr/86: Polish 'Add an issue type filter' Add an issue type filter Closes gh-86
2 parents f7c5dbf + 07dcf72 commit b40339f

File tree

10 files changed

+191
-44
lines changed

10 files changed

+191
-44
lines changed

src/main/java/io/spring/githubchangeloggenerator/ApplicationProperties.java

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2018-2024 the original author or authors.
2+
* Copyright 2018-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -34,6 +34,7 @@
3434
* @author Phillip Webb
3535
* @author Mahendra Bishnoi
3636
* @author Gary Russell
37+
* @author Steven Sheehy
3738
*/
3839
@ConfigurationProperties(prefix = "changelog")
3940
public class ApplicationProperties {
@@ -140,11 +141,18 @@ public static class Section {
140141
*/
141142
private final Set<String> labels;
142143

143-
public Section(String title, @DefaultValue("default") String group, IssueSort sort, Set<String> labels) {
144+
/**
145+
* Whether issues, pull requests or both should be included in this section.
146+
*/
147+
private final IssueType type;
148+
149+
public Section(String title, @DefaultValue("default") String group, IssueSort sort, Set<String> labels,
150+
@DefaultValue("any") IssueType type) {
144151
this.title = title;
145152
this.group = (group != null) ? group : "default";
146153
this.sort = sort;
147154
this.labels = labels;
155+
this.type = type;
148156
}
149157

150158
public String getTitle() {
@@ -163,6 +171,10 @@ public Set<String> getLabels() {
163171
return this.labels;
164172
}
165173

174+
public IssueType getType() {
175+
return this.type;
176+
}
177+
166178
}
167179

168180
/**
@@ -361,4 +373,26 @@ public enum IssueSort {
361373

362374
}
363375

376+
/**
377+
* The type of changelog entry.
378+
*/
379+
public enum IssueType {
380+
381+
/**
382+
* Either issue or pull requests.
383+
*/
384+
ANY,
385+
386+
/**
387+
* GitHub issue.
388+
*/
389+
ISSUE,
390+
391+
/**
392+
* GitHub pull request.
393+
*/
394+
PULL_REQUEST
395+
396+
}
397+
364398
}
Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2018-2022 the original author or authors.
2+
* Copyright 2018-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,21 +16,18 @@
1616

1717
package io.spring.githubchangeloggenerator;
1818

19-
import java.util.Arrays;
20-
import java.util.LinkedHashSet;
21-
import java.util.Set;
19+
import java.util.function.Predicate;
2220

2321
import org.springframework.util.Assert;
24-
import org.springframework.util.CollectionUtils;
2522

2623
import io.spring.githubchangeloggenerator.ApplicationProperties.IssueSort;
2724
import io.spring.githubchangeloggenerator.github.payload.Issue;
28-
import io.spring.githubchangeloggenerator.github.payload.Label;
2925

3026
/**
3127
* A single section of a changelog report.
3228
*
3329
* @author Phillip Webb
30+
* @author Steven Sheehy
3431
*/
3532
class ChangelogSection {
3633

@@ -40,19 +37,15 @@ class ChangelogSection {
4037

4138
private final IssueSort sort;
4239

43-
private final Set<String> labels;
40+
private Predicate<Issue> filter;
4441

45-
ChangelogSection(String title, String group, IssueSort sort, String... labels) {
46-
this(title, group, sort, new LinkedHashSet<>(Arrays.asList(labels)));
47-
}
48-
49-
ChangelogSection(String title, String group, IssueSort sort, Set<String> labels) {
42+
ChangelogSection(String title, String group, IssueSort sort, Predicate<Issue> filter) {
5043
Assert.hasText(title, "Title must not be empty");
51-
Assert.isTrue(!CollectionUtils.isEmpty(labels), "Labels must not be empty");
44+
Assert.notNull(filter, "Filter must not be null");
5245
this.title = title;
5346
this.group = group;
5447
this.sort = sort;
55-
this.labels = labels;
48+
this.filter = filter;
5649
}
5750

5851
String getGroup() {
@@ -63,20 +56,13 @@ IssueSort getSort() {
6356
return this.sort;
6457
}
6558

66-
boolean isMatchFor(Issue issue) {
67-
for (String candidate : this.labels) {
68-
for (Label label : issue.getLabels()) {
69-
if (label.getName().contains(candidate)) {
70-
return true;
71-
}
72-
}
73-
}
74-
return false;
75-
}
76-
7759
@Override
7860
public String toString() {
7961
return this.title;
8062
}
8163

64+
boolean isMatchFor(Issue issue) {
65+
return this.filter.test(issue);
66+
}
67+
8268
}

src/main/java/io/spring/githubchangeloggenerator/ChangelogSections.java

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2018-2022 the original author or authors.
2+
* Copyright 2018-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@
2525
import java.util.Set;
2626
import java.util.SortedMap;
2727
import java.util.TreeMap;
28+
import java.util.function.Predicate;
2829
import java.util.stream.Collectors;
2930

3031
import org.springframework.util.CollectionUtils;
@@ -36,6 +37,7 @@
3637
*
3738
* @author Phillip Webb
3839
* @author Gary Russell
40+
* @author Steven Sheehy
3941
*/
4042
class ChangelogSections {
4143

@@ -49,8 +51,8 @@ class ChangelogSections {
4951
DEFAULT_SECTIONS = Collections.unmodifiableList(sections);
5052
}
5153

52-
private static void add(List<ChangelogSection> sections, String title, String... labels) {
53-
sections.add(new ChangelogSection(title, null, null, labels));
54+
private static void add(List<ChangelogSection> sections, String title, String... labelNameContent) {
55+
sections.add(new ChangelogSection(title, null, null, SelectIssues.withLabelNamesContaining(labelNameContent)));
5456
}
5557

5658
private final List<ChangelogSection> sections;
@@ -73,9 +75,10 @@ private List<ChangelogSection> adapt(ApplicationProperties properties) {
7375
return customSections;
7476
}
7577

76-
private ChangelogSection adapt(ApplicationProperties.Section propertySection) {
77-
return new ChangelogSection(propertySection.getTitle(), propertySection.getGroup(), propertySection.getSort(),
78-
propertySection.getLabels());
78+
private ChangelogSection adapt(ApplicationProperties.Section section) {
79+
Predicate<Issue> filter = SelectIssues.withLabelNamesContaining(section.getLabels());
80+
filter = filter.and(SelectIssues.withType(section.getType()));
81+
return new ChangelogSection(section.getTitle(), section.getGroup(), section.getSort(), filter);
7982
}
8083

8184
Map<ChangelogSection, List<Issue>> collate(List<Issue> issues) {
@@ -92,9 +95,9 @@ Map<ChangelogSection, List<Issue>> collate(List<Issue> issues) {
9295

9396
private List<ChangelogSection> getSections(Issue issue) {
9497
List<ChangelogSection> result = new ArrayList<>();
95-
Set<String> groupClaimes = new HashSet<>();
98+
Set<String> groupClaims = new HashSet<>();
9699
for (ChangelogSection section : this.sections) {
97-
if (section.isMatchFor(issue) && groupClaimes.add(section.getGroup())) {
100+
if (section.isMatchFor(issue) && groupClaims.add(section.getGroup())) {
98101
result.add(section);
99102
}
100103
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright 2018-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.spring.githubchangeloggenerator;
18+
19+
import java.util.Collection;
20+
import java.util.Set;
21+
import java.util.function.Predicate;
22+
23+
import io.spring.githubchangeloggenerator.ApplicationProperties.IssueType;
24+
import io.spring.githubchangeloggenerator.github.payload.Issue;
25+
import io.spring.githubchangeloggenerator.github.payload.Label;
26+
27+
/**
28+
* Utility to select issues.
29+
*
30+
* @author Phillip Webb
31+
* @author Steven Sheehy
32+
*/
33+
final class SelectIssues {
34+
35+
private SelectIssues() {
36+
}
37+
38+
static Predicate<Issue> withLabelNamesContaining(String... nameContent) {
39+
return withLabelNamesContaining(Set.of(nameContent));
40+
}
41+
42+
static Predicate<Issue> withLabelNamesContaining(Collection<String> nameContent) {
43+
return (issue) -> issue.getLabels()
44+
.stream()
45+
.map(Label::getName)
46+
.anyMatch((name) -> nameContent.stream().anyMatch(name::contains));
47+
}
48+
49+
static Predicate<? super Issue> withType(IssueType type) {
50+
return (issue) -> {
51+
return switch (type) {
52+
case ANY -> true;
53+
case ISSUE -> issue.getPullRequest() == null;
54+
case PULL_REQUEST -> issue.getPullRequest() != null;
55+
};
56+
};
57+
}
58+
59+
}

src/test/java/io/spring/githubchangeloggenerator/ApplicationPropertiesTests.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.springframework.core.io.ClassPathResource;
2727

2828
import io.spring.githubchangeloggenerator.ApplicationProperties.IssueSort;
29+
import io.spring.githubchangeloggenerator.ApplicationProperties.IssueType;
2930
import io.spring.githubchangeloggenerator.ApplicationProperties.Section;
3031
import io.spring.githubchangeloggenerator.github.service.Repository;
3132

@@ -35,6 +36,7 @@
3536
* Tests for {@link ApplicationProperties}.
3637
*
3738
* @author Phillip Webb
39+
* @author Steven Sheehy
3840
*/
3941
class ApplicationPropertiesTests {
4042

@@ -54,10 +56,12 @@ void loadYaml() throws Exception {
5456
assertThat(sections.get(0).getLabels()).containsExactly("enhancement");
5557
assertThat(sections.get(0).getGroup()).isEqualTo("default");
5658
assertThat(sections.get(0).getSort()).isEqualTo(IssueSort.CREATED);
59+
assertThat(sections.get(0).getType()).isEqualTo(IssueType.ISSUE);
5760
assertThat(sections.get(1).getTitle()).isEqualTo("Bugs");
5861
assertThat(sections.get(1).getLabels()).containsExactly("bug");
5962
assertThat(sections.get(1).getGroup()).isEqualTo("test");
6063
assertThat(sections.get(1).getSort()).isNull();
64+
assertThat(sections.get(1).getType()).isEqualTo(IssueType.ANY);
6165
assertThat(properties.getIssues().getExcludes().getLabels()).containsExactly("hide");
6266
assertThat(properties.getIssues().getSort()).isEqualTo(IssueSort.TITLE);
6367
assertThat(properties.getContributors().getTitle()).isEqualTo("Nice one!");

src/test/java/io/spring/githubchangeloggenerator/ChangelogGeneratorTests.java

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import io.spring.githubchangeloggenerator.ApplicationProperties.ContributorsExclude;
4040
import io.spring.githubchangeloggenerator.ApplicationProperties.ExternalLink;
4141
import io.spring.githubchangeloggenerator.ApplicationProperties.IssueSort;
42+
import io.spring.githubchangeloggenerator.ApplicationProperties.IssueType;
4243
import io.spring.githubchangeloggenerator.ApplicationProperties.Issues;
4344
import io.spring.githubchangeloggenerator.ApplicationProperties.IssuesExclude;
4445
import io.spring.githubchangeloggenerator.ApplicationProperties.PortedIssue;
@@ -61,6 +62,7 @@
6162
* @author Phillip Webb
6263
* @author Mahendra Bishnoi
6364
* @author Gary Russell
65+
* @author Steven Sheehy
6466
*/
6567
class ChangelogGeneratorTests {
6668

@@ -322,7 +324,7 @@ void generateWhenEscapedMarkdownStylingIsInIssueTitleItIsNotEscapedAgain() throw
322324
void generateWhenSectionSortedByTitle() throws Exception {
323325
List<Section> sections = new ArrayList<>();
324326
Set<String> labels = Collections.singleton("type: enhancement");
325-
sections.add(new Section("Enhancements", null, IssueSort.TITLE, labels));
327+
sections.add(new Section("Enhancements", null, IssueSort.TITLE, labels, IssueType.ANY));
326328
ApplicationProperties properties = new ApplicationProperties(REPO, MilestoneReference.ID, sections,
327329
new Issues(null, null, null, true), null, null, false);
328330
this.generator = new ChangelogGenerator(this.service, properties);
@@ -338,7 +340,7 @@ void generateWhenSectionSortedByTitle() throws Exception {
338340
void generateWhenAllIssuesSortedByTitle() throws Exception {
339341
List<Section> sections = new ArrayList<>();
340342
Set<String> labels = Collections.singleton("type: enhancement");
341-
sections.add(new Section("Enhancements", null, null, labels));
343+
sections.add(new Section("Enhancements", null, null, labels, IssueType.ANY));
342344
ApplicationProperties properties = new ApplicationProperties(REPO, MilestoneReference.ID, sections,
343345
new Issues(IssueSort.TITLE, null, null, true), null, null, false);
344346
this.generator = new ChangelogGenerator(this.service, properties);
@@ -399,6 +401,42 @@ void generateWhenIssueLinksDisabled() throws Exception {
399401
assertChangelog("23").hasContent(from("output-without-issue-links"));
400402
}
401403

404+
@Test
405+
void generateWhenIssuesOnly() throws Exception {
406+
List<Section> sections = new ArrayList<>();
407+
Set<String> labels = Collections.singleton("type: enhancement");
408+
sections.add(new Section("Enhancements", null, IssueSort.TITLE, labels, IssueType.ISSUE));
409+
ApplicationProperties properties = new ApplicationProperties(REPO, MilestoneReference.ID, sections,
410+
new Issues(null, null, null, true), null, null, false);
411+
this.generator = new ChangelogGenerator(this.service, properties);
412+
User contributor1 = createUser("contributor1");
413+
List<Issue> issues = new ArrayList<>();
414+
issues.add(newIssue("Issue 1", "1", "issue-1-url", Type.ENHANCEMENT));
415+
issues.add(newIssue("Issue 2", "2", "issue-2-url", Type.ENHANCEMENT));
416+
issues.add(newPullRequest("PR 3", "3", Type.ENHANCEMENT, "pr-3-url", contributor1));
417+
issues.add(newPullRequest("PR 4", "4", Type.ENHANCEMENT, "pr-4-url", contributor1));
418+
given(this.service.getIssuesForMilestone(23, REPO)).willReturn(issues);
419+
assertChangelog("23").hasContent(from("output-with-issues-only"));
420+
}
421+
422+
@Test
423+
void generateWhenPullRequestsOnly() throws Exception {
424+
List<Section> sections = new ArrayList<>();
425+
Set<String> labels = Collections.singleton("type: enhancement");
426+
sections.add(new Section("Enhancements", null, IssueSort.TITLE, labels, IssueType.PULL_REQUEST));
427+
ApplicationProperties properties = new ApplicationProperties(REPO, MilestoneReference.ID, sections,
428+
new Issues(null, null, null, true), null, null, false);
429+
this.generator = new ChangelogGenerator(this.service, properties);
430+
User contributor1 = createUser("contributor1");
431+
List<Issue> issues = new ArrayList<>();
432+
issues.add(newIssue("Issue 1", "1", "issue-1-url", Type.ENHANCEMENT));
433+
issues.add(newIssue("Issue 2", "2", "issue-2-url", Type.ENHANCEMENT));
434+
issues.add(newPullRequest("PR 3", "3", Type.ENHANCEMENT, "pr-3-url", contributor1));
435+
issues.add(newPullRequest("PR 4", "4", Type.ENHANCEMENT, "pr-4-url", contributor1));
436+
given(this.service.getIssuesForMilestone(23, REPO)).willReturn(issues);
437+
assertChangelog("23").hasContent(from("output-with-pull-requests-only"));
438+
}
439+
402440
private void setupGenerator(MilestoneReference id) {
403441
Set<String> labels = new HashSet<>(Arrays.asList("duplicate", "wontfix"));
404442
PortedIssue forwardPort = new PortedIssue("status: forward-port", "Forward port of issue #(\\d+)");

0 commit comments

Comments
 (0)