Add support for monitoring multiple repositories
This commit is contained in:
@@ -16,8 +16,6 @@
|
||||
|
||||
package io.spring.issuebot;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.NestedConfigurationProperty;
|
||||
|
||||
@@ -29,20 +27,9 @@ import org.springframework.boot.context.properties.NestedConfigurationProperty;
|
||||
@ConfigurationProperties(prefix = "issuebot.github")
|
||||
public class GitHubProperties {
|
||||
|
||||
@NestedConfigurationProperty
|
||||
private Repository repository = new Repository();
|
||||
|
||||
@NestedConfigurationProperty
|
||||
private Credentials credentials = new Credentials();
|
||||
|
||||
public Repository getRepository() {
|
||||
return this.repository;
|
||||
}
|
||||
|
||||
public void setRepository(Repository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public Credentials getCredentials() {
|
||||
return this.credentials;
|
||||
}
|
||||
@@ -51,52 +38,6 @@ public class GitHubProperties {
|
||||
this.credentials = credentials;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration for a GitHub repository.
|
||||
*/
|
||||
public static class Repository {
|
||||
|
||||
/**
|
||||
* The name of the organization that owns the repository.
|
||||
*/
|
||||
private String organization;
|
||||
|
||||
/**
|
||||
* The name of the repository.
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* The names of the repository's collaborators.
|
||||
*/
|
||||
private List<String> collaborators;
|
||||
|
||||
public String getOrganization() {
|
||||
return this.organization;
|
||||
}
|
||||
|
||||
public void setOrganization(String organization) {
|
||||
this.organization = organization;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public List<String> getCollaborators() {
|
||||
return this.collaborators;
|
||||
}
|
||||
|
||||
public void setCollaborators(List<String> collaborators) {
|
||||
this.collaborators = collaborators;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration for the credentials used to authenticate with GitHub.
|
||||
*/
|
||||
|
||||
@@ -35,7 +35,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
@EnableConfigurationProperties(GitHubProperties.class)
|
||||
@EnableConfigurationProperties({ GitHubProperties.class, MonitoringProperties.class })
|
||||
public class IssueBotApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
@@ -50,11 +50,9 @@ public class IssueBotApplication {
|
||||
|
||||
@Bean
|
||||
RepositoryMonitor repositoryMonitor(GitHubOperations gitHub,
|
||||
GitHubProperties gitHubProperties, List<IssueListener> issueListeners) {
|
||||
return new RepositoryMonitor(gitHub,
|
||||
new MonitoredRepository(
|
||||
gitHubProperties.getRepository().getOrganization(),
|
||||
gitHubProperties.getRepository().getName()),
|
||||
MonitoringProperties monitoringProperties,
|
||||
List<MultiRepositoryIssueListener> issueListeners) {
|
||||
return new RepositoryMonitor(gitHub, monitoringProperties.getRepositories(),
|
||||
issueListeners);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,8 @@ package io.spring.issuebot;
|
||||
import io.spring.issuebot.github.Issue;
|
||||
|
||||
/**
|
||||
* An {@code IssueListener} is notified of issues found during repository monitoring.
|
||||
* An {@code IssueListener} is notified of issues found during monitoring of a specific
|
||||
* repository.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.spring.issuebot;
|
||||
|
||||
/**
|
||||
* A repository that should be monitored.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class MonitoredRepository {
|
||||
|
||||
/**
|
||||
* The name of the organization that owns the repository.
|
||||
*/
|
||||
private final String organization;
|
||||
|
||||
/**
|
||||
* The name of the repository.
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
public MonitoredRepository(String organization, String name) {
|
||||
this.organization = organization;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getOrganization() {
|
||||
return this.organization;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
}
|
||||
87
src/main/java/io/spring/issuebot/MonitoringProperties.java
Normal file
87
src/main/java/io/spring/issuebot/MonitoringProperties.java
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2015-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.spring.issuebot;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Properties for configuring repository monitoring.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "issuebot.monitoring")
|
||||
public class MonitoringProperties {
|
||||
|
||||
private List<Repository> repositories;
|
||||
|
||||
public List<Repository> getRepositories() {
|
||||
return this.repositories;
|
||||
}
|
||||
|
||||
public void setRepositories(List<Repository> repositories) {
|
||||
this.repositories = repositories;
|
||||
}
|
||||
|
||||
/**
|
||||
* A repository that is monitored.
|
||||
*/
|
||||
public static class Repository {
|
||||
|
||||
/**
|
||||
* The name of the organization that owns the repository.
|
||||
*/
|
||||
private String organization;
|
||||
|
||||
/**
|
||||
* The name of the repository.
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* The names of the repository's collaborators.
|
||||
*/
|
||||
private List<String> collaborators;
|
||||
|
||||
public String getOrganization() {
|
||||
return this.organization;
|
||||
}
|
||||
|
||||
public void setOrganization(String organization) {
|
||||
this.organization = organization;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public List<String> getCollaborators() {
|
||||
return this.collaborators;
|
||||
}
|
||||
|
||||
public void setCollaborators(List<String> collaborators) {
|
||||
this.collaborators = collaborators;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2015-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.spring.issuebot;
|
||||
|
||||
import io.spring.issuebot.MonitoringProperties.Repository;
|
||||
import io.spring.issuebot.github.Issue;
|
||||
|
||||
/**
|
||||
* An {@code IssueListener} is notified of issues found during monitoring of all
|
||||
* repositories.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public interface MultiRepositoryIssueListener {
|
||||
|
||||
/**
|
||||
* Notification that, in the given {@code repository}, the given {@code issue} is
|
||||
* open.
|
||||
* @param repository the repository to which the issue belongs
|
||||
* @param issue the open issue
|
||||
*/
|
||||
default void onOpenIssue(Repository repository, Issue issue) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification that, in the give {@code repository}, the given {@code issue} is being
|
||||
* closed.
|
||||
* @param repository the repository to which the issue belongs
|
||||
* @param issue the issue that is being closed
|
||||
*/
|
||||
default void onIssueClosure(Repository repository, Issue issue) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package io.spring.issuebot;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.spring.issuebot.MonitoringProperties.Repository;
|
||||
import io.spring.issuebot.github.GitHubOperations;
|
||||
import io.spring.issuebot.github.Issue;
|
||||
import io.spring.issuebot.github.Page;
|
||||
@@ -37,29 +38,34 @@ class RepositoryMonitor {
|
||||
|
||||
private final GitHubOperations gitHub;
|
||||
|
||||
private final MonitoredRepository repository;
|
||||
private final List<Repository> repositories;
|
||||
|
||||
private final List<IssueListener> issueListeners;
|
||||
private final List<MultiRepositoryIssueListener> issueListeners;
|
||||
|
||||
RepositoryMonitor(GitHubOperations gitHub, MonitoredRepository repository,
|
||||
List<IssueListener> issueListeners) {
|
||||
RepositoryMonitor(GitHubOperations gitHub, List<Repository> repositories,
|
||||
List<MultiRepositoryIssueListener> issueListeners) {
|
||||
this.gitHub = gitHub;
|
||||
this.repository = repository;
|
||||
this.repositories = repositories;
|
||||
this.issueListeners = issueListeners;
|
||||
}
|
||||
|
||||
@Scheduled(fixedRate = 5 * 60 * 1000)
|
||||
void monitor() {
|
||||
log.info("Monitoring {}/{}", this.repository.getOrganization(),
|
||||
this.repository.getName());
|
||||
for (Repository repository : this.repositories) {
|
||||
monitor(repository);
|
||||
}
|
||||
}
|
||||
|
||||
private void monitor(Repository repository) {
|
||||
log.info("Monitoring {}/{}", repository.getOrganization(), repository.getName());
|
||||
try {
|
||||
Page<Issue> page = this.gitHub.getIssues(this.repository.getOrganization(),
|
||||
this.repository.getName());
|
||||
Page<Issue> page = this.gitHub.getIssues(repository.getOrganization(),
|
||||
repository.getName());
|
||||
while (page != null) {
|
||||
for (Issue issue : page.getContent()) {
|
||||
for (IssueListener issueListener : this.issueListeners) {
|
||||
for (MultiRepositoryIssueListener issueListener : this.issueListeners) {
|
||||
try {
|
||||
issueListener.onOpenIssue(issue);
|
||||
issueListener.onOpenIssue(repository, issue);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
log.warn("Listener '{}' failed when handling issue '{}'",
|
||||
@@ -71,10 +77,11 @@ class RepositoryMonitor {
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
log.warn("A failure occurred during issue monitoring", ex);
|
||||
log.warn("A failure occurred during monitoring of {}/{}",
|
||||
repository.getOrganization(), repository.getName(), ex);
|
||||
}
|
||||
log.info("Monitoring of {}/{} completed", this.repository.getOrganization(),
|
||||
this.repository.getName());
|
||||
log.info("Monitoring of {}/{} completed", repository.getOrganization(),
|
||||
repository.getName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2015-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.spring.issuebot;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import io.spring.issuebot.MonitoringProperties.Repository;
|
||||
import io.spring.issuebot.github.Issue;
|
||||
|
||||
/**
|
||||
* A {@code MultiRepositoryIssueListener} that performs repository-based routing to
|
||||
* specific {@link IssueListener IssueListeners}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class RoutingMultiRepositoryIssueListener implements MultiRepositoryIssueListener {
|
||||
|
||||
private final Map<Repository, IssueListener> delegates;
|
||||
|
||||
public RoutingMultiRepositoryIssueListener(Map<Repository, IssueListener> delegates) {
|
||||
this.delegates = delegates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpenIssue(Repository repository, Issue issue) {
|
||||
IssueListener listener = this.delegates.get(repository);
|
||||
if (listener != null) {
|
||||
listener.onOpenIssue(issue);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIssueClosure(Repository repository, Issue issue) {
|
||||
IssueListener listener = this.delegates.get(repository);
|
||||
if (listener != null) {
|
||||
listener.onIssueClosure(issue);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,10 +16,17 @@
|
||||
|
||||
package io.spring.issuebot.feedback;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.spring.issuebot.GitHubProperties;
|
||||
import io.spring.issuebot.IssueListener;
|
||||
import io.spring.issuebot.MonitoringProperties;
|
||||
import io.spring.issuebot.MonitoringProperties.Repository;
|
||||
import io.spring.issuebot.MultiRepositoryIssueListener;
|
||||
import io.spring.issuebot.RoutingMultiRepositoryIssueListener;
|
||||
import io.spring.issuebot.github.GitHubOperations;
|
||||
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
@@ -37,18 +44,29 @@ import org.springframework.context.annotation.Configuration;
|
||||
class FeedbackConfiguration {
|
||||
|
||||
@Bean
|
||||
FeedbackIssueListener feedbackIssueListener(GitHubOperations gitHub,
|
||||
GitHubProperties githubProperties, FeedbackProperties feedbackProperties,
|
||||
List<IssueListener> issueListener) {
|
||||
MultiRepositoryIssueListener feedbackIssueListener(
|
||||
MonitoringProperties monitoringProperties, GitHubOperations gitHub,
|
||||
GitHubProperties githubProperties, FeedbackProperties feedbackProperties) {
|
||||
Map<Repository, IssueListener> delegates = monitoringProperties.getRepositories()
|
||||
.stream()
|
||||
.collect(Collectors.toMap(Function.identity(),
|
||||
(repository) -> createListener(repository, gitHub,
|
||||
githubProperties, feedbackProperties)));
|
||||
return new RoutingMultiRepositoryIssueListener(delegates);
|
||||
}
|
||||
|
||||
private FeedbackIssueListener createListener(Repository repository,
|
||||
GitHubOperations gitHub, GitHubProperties githubProperties,
|
||||
FeedbackProperties feedbackProperties) {
|
||||
return new FeedbackIssueListener(gitHub, feedbackProperties.getRequiredLabel(),
|
||||
githubProperties.getRepository().getCollaborators(),
|
||||
repository.getCollaborators(),
|
||||
githubProperties.getCredentials().getUsername(),
|
||||
new StandardFeedbackListener(gitHub,
|
||||
feedbackProperties.getProvidedLabel(),
|
||||
feedbackProperties.getRequiredLabel(),
|
||||
feedbackProperties.getReminderLabel(),
|
||||
feedbackProperties.getReminderComment(),
|
||||
feedbackProperties.getCloseComment(), issueListener));
|
||||
feedbackProperties.getCloseComment(), Collections.emptyList()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,8 +17,16 @@
|
||||
package io.spring.issuebot.triage;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.spring.issuebot.GitHubProperties;
|
||||
import io.spring.issuebot.IssueListener;
|
||||
import io.spring.issuebot.MonitoringProperties;
|
||||
import io.spring.issuebot.MonitoringProperties.Repository;
|
||||
import io.spring.issuebot.MultiRepositoryIssueListener;
|
||||
import io.spring.issuebot.RoutingMultiRepositoryIssueListener;
|
||||
import io.spring.issuebot.github.GitHubOperations;
|
||||
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
@@ -35,12 +43,23 @@ import org.springframework.context.annotation.Configuration;
|
||||
class TriageConfiguration {
|
||||
|
||||
@Bean
|
||||
TriageIssueListener triageIssueListener(GitHubOperations gitHubOperations,
|
||||
TriageProperties triageProperties, GitHubProperties gitHubProperties) {
|
||||
MultiRepositoryIssueListener triageIssueListener(GitHubOperations gitHubOperations,
|
||||
TriageProperties triageProperties, MonitoringProperties monitoringProperties,
|
||||
GitHubProperties gitHubProperties) {
|
||||
Map<Repository, IssueListener> delegates = monitoringProperties.getRepositories()
|
||||
.stream()
|
||||
.collect(Collectors.toMap(Function.identity(),
|
||||
(repository) -> createListener(repository, gitHubOperations,
|
||||
triageProperties)));
|
||||
return new RoutingMultiRepositoryIssueListener(delegates);
|
||||
}
|
||||
|
||||
private TriageIssueListener createListener(Repository repository,
|
||||
GitHubOperations gitHubOperations, TriageProperties triageProperties) {
|
||||
return new TriageIssueListener(
|
||||
Arrays.asList(
|
||||
new OpenedByCollaboratorTriageFilter(
|
||||
gitHubProperties.getRepository().getCollaborators()),
|
||||
repository.getCollaborators()),
|
||||
new LabelledTriageFilter(), new MilestoneAppliedTriageFilter()),
|
||||
new LabelApplyingTriageListener(gitHubOperations,
|
||||
triageProperties.getLabel()));
|
||||
|
||||
Reference in New Issue
Block a user