#161 - Add GitHub LabelConfiguration.

Also added GitHub command to create and update labels.
This commit is contained in:
Mark Paluch
2020-11-12 14:07:21 +01:00
parent 2c2fb40dc6
commit 41f1c73f1f
10 changed files with 814 additions and 61 deletions

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2014-2020 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
*
* https://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 org.springframework.data.release.cli;
import java.util.List;
import org.springframework.data.release.model.Project;
import org.springframework.data.release.model.Projects;
import org.springframework.shell.core.Completion;
import org.springframework.shell.core.Converter;
import org.springframework.shell.core.MethodTarget;
import org.springframework.stereotype.Component;
/**
* @author Mark Paluch
*/
@Component
public class ProjectConverter implements Converter<Project> {
/*
* (non-Javadoc)
* @see org.springframework.shell.core.Converter#supports(java.lang.Class, java.lang.String)
*/
@Override
public boolean supports(Class<?> type, String optionContext) {
return Project.class.isAssignableFrom(type);
}
/*
* (non-Javadoc)
* @see org.springframework.shell.core.Converter#convertFromText(java.lang.String, java.lang.Class, java.lang.String)
*/
@Override
public Project convertFromText(String value, Class<?> targetType, String optionContext) {
return Projects.requiredByName(value);
}
/*
* (non-Javadoc)
* @see org.springframework.shell.core.Converter#getAllPossibleValues(java.util.List, java.lang.Class, java.lang.String, java.lang.String, org.springframework.shell.core.MethodTarget)
*/
@Override
public boolean getAllPossibleValues(List<Completion> completions, Class<?> targetType, String existingData,
String optionContext, MethodTarget target) {
for (Project project : Projects.all()) {
completions.add(new Completion(project.getName()));
}
return true;
}
}

View File

@@ -25,9 +25,6 @@ import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -54,19 +51,16 @@ import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestOperations;
import org.springframework.web.util.DefaultUriBuilderFactory;
/**
* @author Oliver Gierke
* @author Mark Paluch
*/
@Component
class GitHub implements IssueTracker {
class GitHub extends GitHubSupport implements IssueTracker {
private static final String MILESTONE_URI = "/repos/spring-projects/{repoName}/milestones?state={state}";
private static final String ISSUES_BY_MILESTONE_AND_ASSIGNEE_URI_TEMPLATE = "/repos/spring-projects/{repoName}/issues?milestone={id}&state=all&assignee={assignee}";
@@ -80,7 +74,6 @@ class GitHub implements IssueTracker {
private static final ParameterizedTypeReference<List<GitHubIssue>> ISSUES_TYPE = new ParameterizedTypeReference<List<GitHubIssue>>() {};
private static final ParameterizedTypeReference<GitHubIssue> ISSUE_TYPE = new ParameterizedTypeReference<GitHubIssue>() {};
private final RestOperations operations;
private final Logger logger;
private final GitHubProperties properties;
private final ExecutorService executorService;
@@ -88,7 +81,7 @@ class GitHub implements IssueTracker {
public GitHub(@Qualifier("tracker") RestTemplateBuilder templateBuilder, Logger logger, GitHubProperties properties,
ExecutorService executorService) {
this.operations = templateBuilder.uriTemplateHandler(new DefaultUriBuilderFactory(properties.getApiUrl())).build();
super(createOperations(templateBuilder, properties));
this.logger = logger;
this.properties = properties;
this.executorService = executorService;
@@ -390,58 +383,6 @@ class GitHub implements IssueTracker {
return Optional.ofNullable(milestoneRef.get());
}
/**
* Apply a {@link Predicate callback} with GitHub paging starting at {@code endpointUri}. The given
* {@link Predicate#test(Object)} outcome controls whether paging continues by returning {@literal true} or stops.
*
* @param endpointUri
* @param method
* @param parameters
* @param entity
* @param type
* @param callbackContinue
* @param <T>
*/
private <T> void doWithPaging(String endpointUri, HttpMethod method, Map<String, Object> parameters,
HttpEntity<?> entity, ParameterizedTypeReference<T> type, Predicate<T> callbackContinue) {
ResponseEntity<T> exchange = operations.exchange(endpointUri, method, entity, type, parameters);
Pattern pattern = Pattern.compile("<([^ ]*)>; rel=\"(\\w+)\"");
while (true) {
if (!callbackContinue.test(exchange.getBody())) {
return;
}
HttpHeaders responseHeaders = exchange.getHeaders();
List<String> links = responseHeaders.getValuesAsList("Link");
if (links.isEmpty()) {
return;
}
String nextLink = null;
for (String link : links) {
Matcher matcher = pattern.matcher(link);
if (matcher.find()) {
if (matcher.group(2).equals("next")) {
nextLink = matcher.group(1);
break;
}
}
}
if (nextLink == null) {
return;
}
exchange = operations.exchange(nextLink, method, entity, type, parameters);
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.release.issues.IssueTracker#closeIteration(org.springframework.data.release.model.ModuleIteration)

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2014-2020 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
*
* https://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 org.springframework.data.release.issues.github;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import java.util.concurrent.Executor;
import org.springframework.data.release.CliComponent;
import org.springframework.data.release.TimedCommand;
import org.springframework.data.release.issues.IssueTracker;
import org.springframework.data.release.model.Project;
import org.springframework.plugin.core.PluginRegistry;
import org.springframework.shell.core.annotation.CliCommand;
import org.springframework.shell.core.annotation.CliOption;
/**
* @author Mark Paluch
*/
@CliComponent
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
class GitHubCommands extends TimedCommand {
@NonNull PluginRegistry<IssueTracker, Project> tracker;
@NonNull GitHubLabels gitHubLabels;
@NonNull Executor executor;
@CliCommand(value = "github update labels")
public void createOrUpdateLabels(@CliOption(key = "", mandatory = false) Project project) {
if (project == null) {
// Projects.all().forEach(gitHubLabels::createOrUpdateLabels);
} else {
gitHubLabels.createOrUpdateLabels(project);
}
}
}

View File

@@ -0,0 +1,121 @@
/*
* Copyright 2020 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 org.springframework.data.release.issues.github;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.data.release.git.GitProject;
import org.springframework.data.release.model.Project;
import org.springframework.data.release.utils.Logger;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.databind.JsonNode;
/**
* Methods to interact with GitHub labels.
*
* @author Mark Paluch
*/
@Component
public class GitHubLabels extends GitHubSupport {
private static final String LABELS_URI = "/repos/spring-projects/{repoName}/labels";
private static final String LABEL_URI = "/repos/spring-projects/{repoName}/labels/{label}";
private static final ParameterizedTypeReference<List<Label>> LABELS_TYPE = new ParameterizedTypeReference<List<Label>>() {};
private final Logger logger;
public GitHubLabels(@Qualifier("tracker") RestTemplateBuilder templateBuilder, Logger logger,
GitHubProperties properties) {
super(createOperations(templateBuilder, properties));
this.logger = logger;
}
/**
* Creates or updates GitHub labels for a {@link Project}. The actual {@link LabelConfiguration} is obtained from
* {@link ProjectLabelConfiguration}.
*
* @param project the project to process.
*/
public void createOrUpdateLabels(Project project) {
logger.log(project, "Obtaining labels…");
Map<String, Object> parameters = Collections.singletonMap("repoName", GitProject.of(project).getRepositoryName());
List<Label> existsOnGitHub = getLabelsFromGitHub(parameters);
LabelConfiguration configuration = ProjectLabelConfiguration.forProject(project);
List<Label> newLabels = configuration.getNewLabels(existsOnGitHub);
List<Label> existingLabels = configuration.getExistingLabels(existsOnGitHub);
List<Label> additionalLabels = configuration.getAdditionalLabels(existsOnGitHub);
if (!newLabels.isEmpty()) {
logger.log(project, "Creating new labels (%d)…", newLabels.size());
newLabels.forEach(it -> {
operations.postForObject(LABELS_URI, it, JsonNode.class, parameters);
});
}
newLabels.forEach(it -> {
operations.postForObject(LABELS_URI, it, String.class, parameters);
});
existingLabels.stream().filter(it -> {
Label existing = existsOnGitHub.get(existsOnGitHub.indexOf(it));
return existing.requiresUpdate(it);
}).forEach(it -> {
logger.log(project, "Updating label %s…", it.getName());
Map<String, Object> updateLabel = new HashMap<>(parameters);
updateLabel.put("label", it.getName());
operations.postForObject(LABEL_URI, it, JsonNode.class, updateLabel);
});
if (!additionalLabels.isEmpty()) {
logger.log(project, "Found additional labels: %s",
additionalLabels.stream().map(Label::getName).collect(Collectors.joining(", ")));
}
}
protected List<Label> getLabelsFromGitHub(Map<String, Object> parameters) {
List<Label> existsOnGitHub = new ArrayList<>();
doWithPaging(LABELS_URI, HttpMethod.GET, parameters, new HttpEntity<>(new HttpHeaders()), LABELS_TYPE, labels -> {
existsOnGitHub.addAll(labels);
return true;
});
return existsOnGitHub;
}
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright 2020 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 org.springframework.data.release.issues.github;
import lombok.RequiredArgsConstructor;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestOperations;
import org.springframework.web.util.DefaultUriBuilderFactory;
/**
* Support class for GitHub operations.
*
* @author Mark Paluch
*/
@RequiredArgsConstructor
class GitHubSupport {
final RestOperations operations;
static RestOperations createOperations(RestTemplateBuilder templateBuilder, GitHubProperties properties) {
return templateBuilder.uriTemplateHandler(new DefaultUriBuilderFactory(properties.getApiUrl())).build();
}
/**
* Apply a {@link Predicate callback} with GitHub paging starting at {@code endpointUri}. The given
* {@link Predicate#test(Object)} outcome controls whether paging continues by returning {@literal true} or stops.
*
* @param endpointUri
* @param method
* @param parameters
* @param entity
* @param type
* @param callbackContinue
* @param <T>
*/
<T> void doWithPaging(String endpointUri, HttpMethod method, Map<String, Object> parameters, HttpEntity<?> entity,
ParameterizedTypeReference<T> type, Predicate<T> callbackContinue) {
ResponseEntity<T> exchange = operations.exchange(endpointUri, method, entity, type, parameters);
Pattern pattern = Pattern.compile("<([^ ]*)>; rel=\"(\\w+)\"");
while (true) {
if (!callbackContinue.test(exchange.getBody())) {
return;
}
HttpHeaders responseHeaders = exchange.getHeaders();
List<String> links = responseHeaders.getValuesAsList("Link");
if (links.isEmpty()) {
return;
}
String nextLink = null;
for (String link : links) {
Matcher matcher = pattern.matcher(link);
if (matcher.find()) {
if (matcher.group(2).equals("next")) {
nextLink = matcher.group(1);
break;
}
}
}
if (nextLink == null) {
return;
}
exchange = operations.exchange(nextLink, method, entity, type, parameters);
}
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2020 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 org.springframework.data.release.issues.github;
import lombok.EqualsAndHashCode;
import lombok.Value;
/**
* GitHub label.
*
* @author Mark Paluch
*/
@Value
@EqualsAndHashCode(of = "name")
public class Label {
String name;
String description;
String color;
public boolean requiresUpdate(Label other) {
if (!other.getName().equals(getName())) {
return true;
}
if (!other.getDescription().equals(getDescription())) {
return true;
}
if (!other.getColor().equals(getColor())) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,202 @@
/*
* Copyright 2020 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 org.springframework.data.release.issues.github;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.springframework.data.release.issues.github.LabelFactories.LabelFactory;
import org.springframework.data.util.Streamable;
/**
* GitHub label configurations.
*
* @author Mark Paluch
*/
public class LabelConfiguration implements Streamable<Label> {
private final Set<Label> labels;
private LabelConfiguration(Consumer<LabelConfigurer> configurerConsumer) {
LabelConfigurer configurer = new LabelConfigurer();
configurerConsumer.accept(configurer);
this.labels = configurer.labels;
}
private LabelConfiguration(Set<Label> labels) {
this.labels = labels;
}
/**
* Create an empty {@link LabelConfiguration} without any labels.
*
* @return
*/
public static LabelConfiguration empty() {
return new LabelConfiguration(Collections.emptySet());
}
/**
* Create a new {@link LabelConfiguration} by merging this and the outcome of {@code consumerConfigurer}.
*
* @param configurerConsumer
* @return
*/
public LabelConfiguration mergeWith(Consumer<LabelConfigurer> configurerConsumer) {
return mergeWith(LabelConfiguration.of(configurerConsumer));
}
/**
* Create a new {@link LabelConfiguration} by merging this and {@link LabelConfiguration other configuration}.
*
* @param other
* @return
*/
public LabelConfiguration mergeWith(LabelConfiguration other) {
Set<Label> merged = new HashSet<>(this.labels);
merged.addAll(other.labels);
return new LabelConfiguration(merged);
}
/**
* Create a new {@link LabelConfiguration} applying {@code configurerConsumer}.
*
* @param configurerConsumer
* @return
*/
public static LabelConfiguration of(Consumer<LabelConfigurer> configurerConsumer) {
return new LabelConfiguration(configurerConsumer);
}
/**
* Common label configuration.
*
* @return
*/
public static LabelConfiguration commonLabels() {
LabelConfiguration configuration = LabelConfiguration.of(configurer -> {
configurer.register(LabelFactories.FOR_LABEL, "external-project",
"For an external project and not something we can fix");
configurer.register(LabelFactories.FOR_LABEL, "stackoverflow",
"A question that's better suited to stackoverflow.com");
configurer.register(LabelFactories.FOR_LABEL, "team-attention",
"An issue we need to discuss as a team to make progress");
configurer.register(LabelFactories.FOR_LABEL, "external-project", "An issue to discuss face-to-face");
configurer.register(LabelFactories.STATUS_LABEL, "blocked",
"An issue that's blocked on an external project change");
configurer.register(LabelFactories.STATUS_LABEL, "declined",
"A suggestion or change that we don't feel we should currently apply");
configurer.register(LabelFactories.STATUS_LABEL, "duplicate", "A duplicate of another issue");
configurer.register(LabelFactories.STATUS_LABEL, "feedback-provided", "Feedback has been provided");
configurer.register(LabelFactories.STATUS_LABEL, "feedback-reminder",
"We've sent a reminder that we need additional information before we can continue");
configurer.register(LabelFactories.STATUS_LABEL, "first-timers-only",
"An issue that can only be worked on by brand new contributors");
configurer.register(LabelFactories.STATUS_LABEL, "ideal-for-contribution",
"An issue that a contributor can help us with");
configurer.register(LabelFactories.STATUS_LABEL, "invalid", "An issue that we don't feel is valid");
configurer.register(LabelFactories.STATUS_LABEL, "on-hold", "We cannot start working on this issue yet");
configurer.register(LabelFactories.STATUS_LABEL, "pending-design-work",
"Needs design work before any code can be developed");
configurer.register(LabelFactories.STATUS_LABEL, "superseded", "An issue that has been superseded by another");
configurer.register(LabelFactories.STATUS_LABEL, "waiting-for-feedback",
"We need additional information before we can continue");
configurer.register(LabelFactories.STATUS_LABEL, "waiting-for-triage", "An issue we've not yet triaged");
configurer.register(LabelFactories.TYPE_LABEL, "blocker", "An issue that is blocking us from releasing");
configurer.register(LabelFactories.TYPE_LABEL, "bug", "A general bug");
configurer.register(LabelFactories.TYPE_LABEL, "dependency-upgrade", "A dependency upgrade");
configurer.register(LabelFactories.TYPE_LABEL, "documentation", "A documentation update");
configurer.register(LabelFactories.TYPE_LABEL, "enhancement", "A general enhancement");
configurer.register(LabelFactories.TYPE_LABEL, "regression", "A regression from a previous release");
configurer.register(LabelFactories.TYPE_LABEL, "task", "A general task");
});
return configuration;
}
/**
* Retrieve a required {@link Label}.
*
* @param labelFactory
* @param name
* @return
* @throws IllegalArgumentException if the label was not found.
*/
public Label getRequiredLabel(LabelFactory labelFactory, String name) {
Label toFind = labelFactory.apply(name, "");
return this.labels.stream().filter(it -> it.equals(toFind)).findFirst()
.orElseThrow(() -> new IllegalArgumentException(String.format("Cannot find label %s", toFind.getName())));
}
@Override
public Iterator<Label> iterator() {
return labels.iterator();
}
public List<Label> getNewLabels(Collection<Label> existingLabels) {
return labels.stream().filter(o -> !existingLabels.contains(o)).collect(Collectors.toList());
}
public List<Label> getExistingLabels(List<Label> existingLabels) {
return labels.stream().filter(existingLabels::contains).collect(Collectors.toList());
}
public List<Label> getAdditionalLabels(List<Label> existingLabels) {
List<Label> result = new ArrayList<>(existingLabels);
result.removeAll(getNewLabels(existingLabels));
result.removeAll(getExistingLabels(existingLabels));
return result;
}
/**
* Configuration utility to register labels for a {@link LabelFactory}.
*/
class LabelConfigurer {
private final Set<Label> labels = new HashSet<>();
public void register(LabelFactory factory, String name, String description) {
Label label = factory.apply(name, description);
labels.add(label);
}
public void register(Label label) {
labels.add(label);
}
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2020 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 org.springframework.data.release.issues.github;
import java.util.function.BiFunction;
public class LabelFactories {
public static final LabelFactory FOR_LABEL = (name, description) -> create("for:", name, description, "c5def5");
public static final LabelFactory STATUS_LABEL = (name, description) -> create("status:", name, description, "fef2c0");
public static final LabelFactory IN_LABEL = (name, description) -> create("in:", name, description, "e8f9de");
public static final LabelFactory TYPE_LABEL = (name, description) -> create("type:", name, description, "e3d9fc");
public static final LabelFactory HAS_LABEL = (name, description) -> create("has:", name, description, "dfdfdf");
private static Label create(String prefix, String labelName, String description, String color) {
return new Label(String.format("%s %s", prefix, labelName), description, color);
}
interface LabelFactory extends BiFunction<String, String, Label> {
}
}

View File

@@ -0,0 +1,170 @@
/*
* Copyright 2020 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 org.springframework.data.release.issues.github;
import java.util.HashMap;
import java.util.Map;
import org.springframework.data.release.model.Project;
import org.springframework.data.release.model.Projects;
/**
* {@link Project-specifc} Label Configuration.
*
* @author Mark Paluch
*/
public class ProjectLabelConfiguration {
static final Map<Project, LabelConfiguration> labelConfigurations = new HashMap<>();
static {
LabelConfiguration commonLabels = LabelConfiguration.commonLabels();
LabelConfiguration coreMappingRepository = LabelConfiguration.of(config -> {
config.register(LabelFactories.IN_LABEL, "core", "Issues in core support");
config.register(LabelFactories.IN_LABEL, "mapping", "Mapping and conversion infrastructure");
config.register(LabelFactories.IN_LABEL, "repository", "Repositories abstraction");
});
labelConfigurations.put(Projects.BOM, LabelConfiguration.empty().mergeWith(config -> {
config.register(commonLabels.getRequiredLabel(LabelFactories.TYPE_LABEL, "task"));
}));
labelConfigurations.put(Projects.BUILD, LabelConfiguration.empty().mergeWith(config -> {
config.register(commonLabels.getRequiredLabel(LabelFactories.TYPE_LABEL, "dependency-upgrade"));
config.register(commonLabels.getRequiredLabel(LabelFactories.TYPE_LABEL, "task"));
}));
labelConfigurations.put(Projects.CASSANDRA, commonLabels.mergeWith(coreMappingRepository).mergeWith(config -> {
config.register(LabelFactories.IN_LABEL, "core", "Issues in core support");
config.register(LabelFactories.IN_LABEL, "map", "");
config.register(LabelFactories.IN_LABEL, "repository", "Repositories abstraction");
config.register(LabelFactories.IN_LABEL, "kotlin", "Kotlin support");
}));
labelConfigurations.put(Projects.COMMONS, commonLabels.mergeWith(coreMappingRepository).mergeWith(config -> {
config.register(LabelFactories.IN_LABEL, "web", "Integration with Spring MVC");
config.register(LabelFactories.IN_LABEL, "kotlin", "Kotlin support");
}));
labelConfigurations.put(Projects.COUCHBASE, commonLabels.mergeWith(coreMappingRepository));
labelConfigurations.put(Projects.ELASTICSEARCH, commonLabels.mergeWith(coreMappingRepository));
labelConfigurations.put(Projects.ENVERS, commonLabels.mergeWith(config -> {
config.register(LabelFactories.IN_LABEL, "core", "Issues in core support");
}));
labelConfigurations.put(Projects.GEODE, commonLabels.mergeWith(coreMappingRepository).mergeWith(config -> {
config.register(LabelFactories.IN_LABEL, "core", "Issues in core support");
config.register(LabelFactories.IN_LABEL, "map", "");
config.register(LabelFactories.IN_LABEL, "repository", "Repositories abstraction");
}));
labelConfigurations.put(Projects.GEMFIRE, commonLabels.mergeWith(coreMappingRepository).mergeWith(config -> {
config.register(LabelFactories.IN_LABEL, "core", "Issues in core support");
config.register(LabelFactories.IN_LABEL, "map", "");
config.register(LabelFactories.IN_LABEL, "repository", "Repositories abstraction");
}));
labelConfigurations.put(Projects.JDBC, commonLabels.mergeWith(coreMappingRepository).mergeWith(config -> {
config.register(LabelFactories.IN_LABEL, "statement-builder", "");
config.register(LabelFactories.IN_LABEL, "relational", "");
}));
labelConfigurations.put(Projects.JPA, commonLabels.mergeWith(coreMappingRepository).mergeWith(config -> {
config.register(LabelFactories.IN_LABEL, "core", "Issues in core support");
config.register(LabelFactories.IN_LABEL, "query-parser", "Everything related to parsing JPQL or SQL");
config.register(LabelFactories.IN_LABEL, "querydsl", "Querydsl integration");
}));
labelConfigurations.put(Projects.KEY_VALUE, commonLabels.mergeWith(config -> {
config.register(LabelFactories.IN_LABEL, "core", "Issues in core support");
config.register(LabelFactories.IN_LABEL, "map", "");
config.register(LabelFactories.IN_LABEL, "repository", "Repositories abstraction");
}));
labelConfigurations.put(Projects.LDAP, commonLabels.mergeWith(coreMappingRepository).mergeWith(config -> {
config.register(LabelFactories.IN_LABEL, "core", "Issues in core support");
config.register(LabelFactories.IN_LABEL, "repository", "Repositories abstraction");
}));
labelConfigurations.put(Projects.NEO4J, commonLabels.mergeWith(coreMappingRepository));
labelConfigurations.put(Projects.MONGO_DB, commonLabels.mergeWith(coreMappingRepository).mergeWith(config -> {
config.register(LabelFactories.IN_LABEL, "aggregation-framework", "Aggregation framework support");
config.register(LabelFactories.IN_LABEL, "gridfs", "");
config.register(LabelFactories.IN_LABEL, "kotlin", "Kotlin support");
}));
labelConfigurations.put(Projects.R2DBC, commonLabels.mergeWith(coreMappingRepository));
labelConfigurations.put(Projects.REST, commonLabels.mergeWith(config -> {
config.register(LabelFactories.IN_LABEL, "api-documentation", "");
config.register(LabelFactories.IN_LABEL, "content-negotiation", "");
config.register(LabelFactories.IN_LABEL, "query-parser", "Everything related to parsing JPQL or SQL");
config.register(LabelFactories.IN_LABEL, "querydsl", "Querydsl integration");
}));
labelConfigurations.put(Projects.REDIS, commonLabels.mergeWith(coreMappingRepository).mergeWith(config -> {
config.register(LabelFactories.IN_LABEL, "cache", "RedisCache and CacheManager");
config.register(LabelFactories.IN_LABEL, "lettuce", "Lettuce driver");
config.register(LabelFactories.IN_LABEL, "jedis", "Jedis driver");
config.register(LabelFactories.IN_LABEL, "kotlin", "Kotlin support");
}));
labelConfigurations.put(Projects.SOLR, commonLabels.mergeWith(coreMappingRepository));
verify();
}
/**
* Retrieve the {@link LabelConfiguration} for a {@link Project}.
*
* @param project must not be {@literal null}.
* @return
*/
public static LabelConfiguration forProject(Project project) {
return labelConfigurations.get(project);
}
static void verify() {
for (Project project : Projects.all()) {
if (!ProjectLabelConfiguration.labelConfigurations.containsKey(project)) {
throw new IllegalStateException(String.format("No LabelConfiguration for %s", project.getName()));
}
}
}
}

View File

@@ -20,6 +20,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.jgrapht.graph.DefaultDirectedGraph;
import org.jgrapht.graph.DefaultEdge;
@@ -140,4 +141,9 @@ public class Projects {
return byName(name).//
orElseThrow(() -> new IllegalArgumentException(String.format("No project named %s available!", name)));
}
public static List<Project> all() {
return PROJECTS.stream().collect(Collectors.toList());
}
}