Allow defining commentlinks in project.config

These are inherited from parent projects, including the system-wide
commentlinks defined in project.config in all projects. Child projects
may override commentlinks defined in parents by name, which project
administrators can discover via GET /projects/myparent/config.

Change-Id: I96dd6701350761a0af6e3d9babdef4f74ad4e29f
This commit is contained in:
Dave Borowitz
2013-04-08 12:03:29 -07:00
parent 2002789f41
commit 13b38004e9
4 changed files with 60 additions and 7 deletions

View File

@@ -14,12 +14,14 @@
package com.google.gerrit.server.git;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.gerrit.common.data.Permission.isPermission;
import com.google.common.base.CharMatcher;
import com.google.common.base.Joiner;
import com.google.common.base.Objects;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
@@ -66,6 +68,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
public class ProjectConfig extends VersionedMetaData {
public static final String COMMENTLINK = "commentlink";
@@ -138,6 +141,7 @@ public class ProjectConfig extends VersionedMetaData {
private Map<String, ContributorAgreement> contributorAgreements;
private Map<String, NotifyConfig> notifySections;
private Map<String, LabelType> labelSections;
private List<CommentLinkInfo> commentLinkSections;
private List<ValidationError> validationErrors;
private ObjectId rulesId;
@@ -155,8 +159,8 @@ public class ProjectConfig extends VersionedMetaData {
return r;
}
public static CommentLinkInfo buildCommentLink(Config cfg, String name)
throws IllegalArgumentException {
public static CommentLinkInfo buildCommentLink(Config cfg, String name,
boolean allowRaw) throws IllegalArgumentException {
String match = cfg.getString(COMMENTLINK, name, KEY_MATCH);
// Unfortunately this validation isn't entirely complete. Clients
@@ -168,6 +172,8 @@ public class ProjectConfig extends VersionedMetaData {
String link = cfg.getString(COMMENTLINK, name, KEY_LINK);
String html = cfg.getString(COMMENTLINK, name, KEY_HTML);
checkArgument(allowRaw || Strings.isNullOrEmpty(html),
"Raw html replacement not allowed");
return new CommentLinkInfo(name, match, link, html);
}
@@ -256,6 +262,10 @@ public class ProjectConfig extends VersionedMetaData {
return labelSections;
}
public Collection<CommentLinkInfo> getCommentLinkSections() {
return commentLinkSections;
}
public GroupReference resolve(AccountGroup group) {
return resolve(GroupReference.forGroup(group));
}
@@ -356,6 +366,7 @@ public class ProjectConfig extends VersionedMetaData {
loadAccessSections(rc, groupsByName);
loadNotifySections(rc, groupsByName);
loadLabelSections(rc);
loadCommentLinkSections(rc);
}
private void loadAccountsSection(
@@ -613,6 +624,25 @@ public class ProjectConfig extends VersionedMetaData {
}
}
private void loadCommentLinkSections(Config rc) {
Set<String> subsections = rc.getSubsections(COMMENTLINK);
commentLinkSections = Lists.newArrayListWithCapacity(subsections.size());
for (String name : subsections) {
try {
commentLinkSections.add(buildCommentLink(rc, name, false));
} catch (PatternSyntaxException e) {
error(new ValidationError(PROJECT_CONFIG, String.format(
"Invalid pattern \"%s\" in commentlink.%s.match: %s",
rc.getString(COMMENTLINK, name, KEY_MATCH), name, e.getMessage())));
} catch (IllegalArgumentException e) {
error(new ValidationError(PROJECT_CONFIG, String.format(
"Error in pattern \"%s\" in commentlink.%s.match: %s",
rc.getString(COMMENTLINK, name, KEY_MATCH), name, e.getMessage())));
}
}
commentLinkSections = ImmutableList.copyOf(commentLinkSections);
}
private Map<String, GroupReference> readGroupList() throws IOException {
groupsByUUID = new HashMap<AccountGroup.UUID, GroupReference>();
Map<String, GroupReference> groupsByName =

View File

@@ -40,7 +40,7 @@ public class CommentLinkProvider implements Provider<List<CommentLinkInfo>> {
List<CommentLinkInfo> cls =
Lists.newArrayListWithCapacity(subsections.size());
for (String name : subsections) {
cls.add(ProjectConfig.buildCommentLink(cfg, name));
cls.add(ProjectConfig.buildCommentLink(cfg, name, true));
}
return ImmutableList.copyOf(cls);
}

View File

@@ -16,6 +16,7 @@ package com.google.gerrit.server.project;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@@ -295,6 +296,16 @@ public class ProjectState {
};
}
/**
* @return an iterable that walks in-order from All-Projects through the
* project hierarchy to this project.
*/
public Iterable<ProjectState> treeInOrder() {
List<ProjectState> projects = Lists.newArrayList(tree());
Collections.reverse(projects);
return projects;
}
/**
* @return an iterable that walks through the parents of this project. Starts
* from the immediate parent of this project and progresses up the
@@ -346,9 +357,7 @@ public class ProjectState {
public LabelTypes getLabelTypes() {
Map<String, LabelType> types = Maps.newLinkedHashMap();
List<ProjectState> projects = Lists.newArrayList(tree());
Collections.reverse(projects);
for (ProjectState s : projects) {
for (ProjectState s : treeInOrder()) {
for (LabelType type : s.getConfig().getLabelSections().values()) {
String lower = type.getName().toLowerCase();
LabelType old = types.get(lower);
@@ -367,7 +376,16 @@ public class ProjectState {
}
public List<CommentLinkInfo> getCommentLinks() {
return commentLinks;
Map<String, CommentLinkInfo> cls = Maps.newLinkedHashMap();
for (CommentLinkInfo cl : commentLinks) {
cls.put(cl.name.toLowerCase(), cl);
}
for (ProjectState s : treeInOrder()) {
for (CommentLinkInfo cl : s.getConfig().getCommentLinkSections()) {
cls.put(cl.name.toLowerCase(), cl);
}
}
return ImmutableList.copyOf(cls.values());
}
private boolean getInheritableBoolean(Function<Project, InheritableBoolean> func) {