Merge branch 'stable-3.2' into master
* stable-3.2:
Bump Bazel version to 3.7.0
Upgrade bazlets to latest stable-3.0 to build with 3.0.13 API
Change-Id: I70f574683e355ce19c35688a5e481b53261f6b5f
diff --git a/BUILD b/BUILD
index 6379a0d..b0ef90f 100644
--- a/BUILD
+++ b/BUILD
@@ -1,5 +1,5 @@
load("@rules_java//java:defs.bzl", "java_library")
-load("@npm_bazel_rollup//:index.bzl", "rollup_bundle")
+load("@npm//@bazel/rollup:index.bzl", "rollup_bundle")
load("//tools/bzl:junit.bzl", "junit_tests")
load("//tools/js:eslint.bzl", "eslint")
load(
diff --git a/rv-reviewers/rv-filter-section.js b/rv-reviewers/rv-filter-section.js
index 3bf6d06..fab5420 100644
--- a/rv-reviewers/rv-filter-section.js
+++ b/rv-reviewers/rv-filter-section.js
@@ -83,7 +83,7 @@
} else {
const index = e.model.index;
const deleted = this.reviewers[index];
- this._putReviewer(deleted, 'DELETE');
+ this._putReviewer(deleted, 'REMOVE');
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/AddReviewers.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/AddReviewers.java
index 30568dc..90f5841 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/AddReviewers.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/AddReviewers.java
@@ -19,6 +19,7 @@
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
import com.google.gerrit.extensions.api.changes.ReviewInput;
+import com.google.gerrit.extensions.client.ReviewerState;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.server.util.ManualRequestContext;
@@ -36,9 +37,13 @@
private final OneOffRequestContext requestContext;
private final ChangeInfo changeInfo;
private final Set<Account.Id> reviewers;
+ private final Set<Account.Id> ccs;
interface Factory {
- AddReviewers create(ChangeInfo changeInfo, Set<Account.Id> reviewers);
+ AddReviewers create(
+ ChangeInfo changeInfo,
+ @Assisted("reviewers") Set<Account.Id> reviewers,
+ @Assisted("ccs") Set<Account.Id> ccs);
}
@Inject
@@ -46,11 +51,13 @@
GerritApi gApi,
OneOffRequestContext requestContext,
@Assisted ChangeInfo changeInfo,
- @Assisted Set<Account.Id> reviewers) {
+ @Assisted("reviewers") Set<Account.Id> reviewers,
+ @Assisted("ccs") Set<Account.Id> ccs) {
this.gApi = gApi;
this.requestContext = requestContext;
this.changeInfo = changeInfo;
this.reviewers = reviewers;
+ this.ccs = ccs;
}
@Override
@@ -66,12 +73,18 @@
// TODO(davido): Switch back to using changes API again,
// when it supports batch mode for adding reviewers
ReviewInput in = new ReviewInput();
- in.reviewers = new ArrayList<>(reviewers.size());
+ in.reviewers = new ArrayList<>(reviewers.size() + ccs.size());
for (Account.Id account : reviewers) {
AddReviewerInput addReviewerInput = new AddReviewerInput();
addReviewerInput.reviewer = account.toString();
in.reviewers.add(addReviewerInput);
}
+ for (Account.Id account : ccs) {
+ AddReviewerInput input = new AddReviewerInput();
+ input.state = ReviewerState.CC;
+ input.reviewer = account.toString();
+ in.reviewers.add(input);
+ }
gApi.changes().id(changeInfo._number).current().review(in);
} catch (RestApiException e) {
logger.atSevere().withCause(e).log("Couldn't add reviewers to the change");
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/GetReviewers.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/GetReviewers.java
index 4aa8602..212b544 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/GetReviewers.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/GetReviewers.java
@@ -20,11 +20,12 @@
import com.google.gerrit.server.project.ProjectResource;
import com.google.inject.Inject;
import com.google.inject.Singleton;
+import com.googlesource.gerrit.plugins.reviewers.config.ReviewersConfig;
import java.util.List;
/**
- * GET REST end-point for getting all configured {@link ReviewerFilterSection}s of a project, local
- * and inherited.
+ * GET REST end-point for getting all configured {@link ReviewerFilter}s of a project, local and
+ * inherited.
*/
@Singleton
class GetReviewers implements RestReadView<ProjectResource> {
@@ -36,8 +37,7 @@
}
@Override
- public Response<List<ReviewerFilterSection>> apply(ProjectResource resource)
- throws RestApiException {
- return Response.ok(config.forProject(resource.getNameKey()).getReviewerFilterSections());
+ public Response<List<ReviewerFilter>> apply(ProjectResource resource) throws RestApiException {
+ return Response.ok(config.filtersWithInheritance(resource.getNameKey()));
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/Module.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/Module.java
index 4bee073..8de2cc4 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/Module.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/Module.java
@@ -30,6 +30,7 @@
import com.google.gerrit.server.change.ReviewerSuggestion;
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
+import com.googlesource.gerrit.plugins.reviewers.config.ReviewersConfig;
public class Module extends FactoryModule {
private final boolean enableREST;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/PutReviewers.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/PutReviewers.java
index 5f55db2..d815633 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/PutReviewers.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/PutReviewers.java
@@ -41,18 +41,20 @@
import com.google.inject.Provider;
import com.google.inject.Singleton;
import com.googlesource.gerrit.plugins.reviewers.PutReviewers.Input;
+import com.googlesource.gerrit.plugins.reviewers.config.ReviewersConfig;
import java.io.IOException;
import java.util.List;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
-/** PUT REST end-point that removes or adds a reveiwer to a {@link ReviewerFilterSection}. */
+/** PUT REST end-point that removes or adds a reviewer to a {@link ReviewerFilter}. */
@Singleton
class PutReviewers implements RestModifyView<ProjectResource, Input> {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
protected static class Input {
public Action action;
+ public ReviewerType type;
public String filter;
public String reviewer;
}
@@ -89,13 +91,10 @@
}
@Override
- public Response<List<ReviewerFilterSection>> apply(ProjectResource rsrc, Input input)
+ public Response<List<ReviewerFilter>> apply(ProjectResource rsrc, Input input)
throws RestApiException, PermissionBackendException {
Project.NameKey projectName = rsrc.getNameKey();
- ReviewersConfig.ForProject cfg = config.forProject(projectName);
- if (cfg == null) {
- throw new ResourceNotFoundException("Project" + projectName.get() + " not found");
- }
+ ReviewersConfig.ForProject forProject = new ReviewersConfig.ForProject();
PermissionBackend.WithUser userPermission = permissionBackend.user(rsrc.getUser());
if (!userPermission.project(rsrc.getNameKey()).testOrFalse(ProjectPermission.WRITE_CONFIG)
@@ -107,28 +106,34 @@
if (input.action == Action.ADD) {
validateReviewer(input.reviewer);
}
+ if (input.type == null) {
+ input.type = ReviewerType.REVIEWER;
+ }
try {
StringBuilder message = new StringBuilder(pluginName).append(" plugin: ");
- cfg.load(md);
+ forProject.load(md);
if (input.action == Action.ADD) {
message
- .append("Add reviewer ")
+ .append("Add ")
+ .append(input.type.name)
+ .append(" ")
.append(input.reviewer)
.append(" to filter ")
.append(input.filter);
- cfg.addReviewer(input.filter, input.reviewer);
+ forProject.addReviewer(input.filter, input.reviewer, input.type);
} else {
message
- .append("Remove reviewer ")
- .append(input.reviewer)
+ .append("Remove ")
+ .append(input.type.name)
+ .append(" ")
.append(" from filter ")
.append(input.filter);
- cfg.removeReviewer(input.filter, input.reviewer);
+ forProject.removeReviewer(input.filter, input.reviewer, input.type);
}
message.append("\n");
md.setMessage(message.toString());
try {
- cfg.commit(md);
+ forProject.commit(md);
projectCache.evict(projectName);
} catch (IOException e) {
if (e.getCause() instanceof ConfigInvalidException) {
@@ -149,7 +154,7 @@
} catch (IOException err) {
throw new ResourceNotFoundException(projectName.get(), err);
}
- return Response.ok(cfg.getReviewerFilterSections());
+ return Response.ok(config.filtersWithInheritance(projectName));
}
private void validateReviewer(String reviewer) throws RestApiException {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewerFilterSection.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewerFilter.java
similarity index 68%
rename from src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewerFilterSection.java
rename to src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewerFilter.java
index a083239..b991760 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewerFilterSection.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewerFilter.java
@@ -21,19 +21,15 @@
* Representation of a filter section in reviewers.config. Example:
*
* <pre>
- * [filter "'"]
+ * [filter "*"]
* reviewer = joe
* reviewer = jane
* </pre>
*/
-class ReviewerFilterSection {
- private final String filter;
- private final Set<String> reviewers;
-
- ReviewerFilterSection(String filter, Set<String> reviewers) {
- this.filter = filter;
- this.reviewers = reviewers;
- }
+public abstract class ReviewerFilter {
+ protected String filter;
+ protected Set<String> reviewers;
+ protected Set<String> ccs;
String getFilter() {
return filter;
@@ -43,17 +39,23 @@
return reviewers;
}
+ Set<String> getCcs() {
+ return ccs;
+ }
+
@Override
public boolean equals(Object o) {
- if (o instanceof ReviewerFilterSection) {
- ReviewerFilterSection other = ((ReviewerFilterSection) o);
- return Objects.equals(filter, other.filter) && Objects.equals(reviewers, other.reviewers);
+ if (o instanceof ReviewerFilter) {
+ ReviewerFilter other = ((ReviewerFilter) o);
+ return Objects.equals(filter, other.filter)
+ && Objects.equals(reviewers, other.reviewers)
+ && Objects.equals(ccs, other.ccs);
}
return false;
}
@Override
public int hashCode() {
- return Objects.hash(filter, reviewers);
+ return Objects.hash(filter, reviewers, ccs);
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewerType.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewerType.java
new file mode 100644
index 0000000..2d8f943
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewerType.java
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// 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 com.googlesource.gerrit.plugins.reviewers;
+
+public enum ReviewerType {
+ REVIEWER("reviewer"),
+ CC("cc");
+
+ String name;
+
+ ReviewerType(String name) {
+ this.name = name;
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/Reviewers.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/Reviewers.java
index 04e301b..42548a7 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/Reviewers.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/Reviewers.java
@@ -20,6 +20,7 @@
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.entities.Account;
@@ -41,6 +42,7 @@
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
+import com.googlesource.gerrit.plugins.reviewers.config.ReviewersConfig;
import java.util.List;
import java.util.Set;
@@ -100,14 +102,14 @@
@Nullable Change.Id changeId,
@Nullable String query,
Set<Account.Id> candidates) {
- List<ReviewerFilterSection> sections = getSections(projectName);
+ List<ReviewerFilter> filters = getFilters(projectName);
- if (sections.isEmpty() || changeId == null) {
+ if (filters.isEmpty() || changeId == null) {
return ImmutableSet.of();
}
try {
- Set<String> reviewers = findReviewers(changeId.get(), sections);
+ Set<String> reviewers = findReviewers(changeId.get(), filters);
if (!reviewers.isEmpty()) {
return resolver.resolve(reviewers, projectName, changeId.get(), null).stream()
.map(a -> suggestedReviewer(a))
@@ -126,9 +128,9 @@
return reviewer;
}
- private List<ReviewerFilterSection> getSections(Project.NameKey projectName) {
+ private List<ReviewerFilter> getFilters(Project.NameKey projectName) {
// TODO(davido): we have to cache per project configuration
- return config.forProject(projectName).getReviewerFilterSections();
+ return config.filtersWithInheritance(projectName);
}
private void onEvent(ChangeEvent event) {
@@ -142,22 +144,27 @@
}
Project.NameKey projectName = Project.nameKey(c.project);
- List<ReviewerFilterSection> sections = getSections(projectName);
+ List<ReviewerFilter> filters = getFilters(projectName);
- if (sections.isEmpty()) {
+ if (filters.isEmpty()) {
return;
}
AccountInfo uploader = event.getWho();
int changeNumber = c._number;
try {
- Set<String> reviewers = findReviewers(changeNumber, sections);
- if (reviewers.isEmpty()) {
+ List<ReviewerFilter> matching = findMatchingFilters(changeNumber, filters);
+ Set<String> reviewers = getReviewersFrom(matching);
+ Set<String> ccs = getCcsFrom(matching);
+ ccs.removeAll(reviewers);
+ if (reviewers.isEmpty() && ccs.isEmpty()) {
return;
}
final AddReviewers addReviewers =
addReviewersFactory.create(
- c, resolver.resolve(reviewers, projectName, changeNumber, uploader));
+ c,
+ resolver.resolve(reviewers, projectName, changeNumber, uploader),
+ resolver.resolve(ccs, projectName, changeNumber, uploader));
workQueue.submit(addReviewers);
} catch (QueryParseException e) {
logger.atWarning().log(
@@ -168,21 +175,31 @@
}
}
- private Set<String> findReviewers(int change, List<ReviewerFilterSection> sections)
+ private Set<String> findReviewers(int change, List<ReviewerFilter> filters)
throws StorageException, QueryParseException {
+ return getReviewersFrom(findMatchingFilters(change, filters));
+ }
+
+ private Set<String> getReviewersFrom(List<ReviewerFilter> filters) throws StorageException {
ImmutableSet.Builder<String> reviewers = ImmutableSet.builder();
- List<ReviewerFilterSection> found = findReviewerSections(change, sections);
- for (ReviewerFilterSection s : found) {
- reviewers.addAll(s.getReviewers());
+ for (ReviewerFilter f : filters) {
+ reviewers.addAll(f.getReviewers());
}
return reviewers.build();
}
- private List<ReviewerFilterSection> findReviewerSections(
- int change, List<ReviewerFilterSection> sections)
+ private Set<String> getCcsFrom(List<ReviewerFilter> filters) throws StorageException {
+ Set<String> ccs = Sets.newHashSet();
+ for (ReviewerFilter f : filters) {
+ ccs.addAll(f.getCcs());
+ }
+ return ccs;
+ }
+
+ private List<ReviewerFilter> findMatchingFilters(int change, List<ReviewerFilter> filters)
throws StorageException, QueryParseException {
- ImmutableList.Builder<ReviewerFilterSection> found = ImmutableList.builder();
- for (ReviewerFilterSection s : sections) {
+ ImmutableList.Builder<ReviewerFilter> found = ImmutableList.builder();
+ for (ReviewerFilter s : filters) {
if (Strings.isNullOrEmpty(s.getFilter()) || s.getFilter().equals("*")) {
found.add(s);
} else if (filterMatch(change, s.getFilter())) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/config/ReviewerFilterCollection.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/config/ReviewerFilterCollection.java
new file mode 100644
index 0000000..cd41373
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/config/ReviewerFilterCollection.java
@@ -0,0 +1,86 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// 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 com.googlesource.gerrit.plugins.reviewers.config;
+
+import static com.googlesource.gerrit.plugins.reviewers.config.ReviewersConfig.KEY_CC;
+import static com.googlesource.gerrit.plugins.reviewers.config.ReviewersConfig.KEY_REVIEWER;
+import static com.googlesource.gerrit.plugins.reviewers.config.ReviewersConfig.SECTION_FILTER;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.googlesource.gerrit.plugins.reviewers.ReviewerFilter;
+import java.util.List;
+import org.eclipse.jgit.lib.Config;
+
+/** Representation of the collection of {@link ReviewerFilter}s in a {@link Config}. */
+class ReviewerFilterCollection {
+
+ private final Config cfg;
+
+ ReviewerFilterCollection(Config cfg) {
+ this.cfg = cfg;
+ }
+
+ List<ReviewerFilter> getAll() {
+ ImmutableList.Builder<ReviewerFilter> b = ImmutableList.builder();
+ for (String f : cfg.getSubsections(SECTION_FILTER)) {
+ b.add(new ReviewerFilterSection(f));
+ }
+ return b.build();
+ }
+
+ ReviewerFilterSection get(String filter) {
+ return new ReviewerFilterSection(filter);
+ }
+
+ class ReviewerFilterSection extends ReviewerFilter {
+
+ private ReviewerFilterSection(String filter) {
+ this.filter = filter;
+ this.reviewers = Sets.newHashSet(cfg.getStringList(SECTION_FILTER, filter, KEY_REVIEWER));
+ this.ccs = Sets.newHashSet(cfg.getStringList(SECTION_FILTER, filter, KEY_CC));
+ }
+
+ public void removeReviewer(String reviewer) {
+ reviewers.remove(reviewer);
+ save();
+ }
+
+ public void addReviewer(String reviewer) {
+ reviewers.add(reviewer);
+ save();
+ }
+
+ public void removeCc(String cc) {
+ ccs.remove(cc);
+ save();
+ }
+
+ public void addCc(String cc) {
+ ccs.add(cc);
+ save();
+ }
+
+ private void save() {
+ if (this.reviewers.isEmpty() && this.ccs.isEmpty()) {
+ cfg.unsetSection(SECTION_FILTER, filter);
+ } else {
+ cfg.setStringList(SECTION_FILTER, filter, KEY_REVIEWER, Lists.newArrayList(this.reviewers));
+ cfg.setStringList(SECTION_FILTER, filter, KEY_CC, Lists.newArrayList(this.ccs));
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewersConfig.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/config/ReviewersConfig.java
similarity index 61%
rename from src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewersConfig.java
rename to src/main/java/com/googlesource/gerrit/plugins/reviewers/config/ReviewersConfig.java
index 96a4f6b..c81cbd5 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewersConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/config/ReviewersConfig.java
@@ -12,11 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.googlesource.gerrit.plugins.reviewers;
+package com.googlesource.gerrit.plugins.reviewers.config;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.entities.Project;
import com.google.gerrit.entities.RefNames;
@@ -26,9 +25,9 @@
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
+import com.googlesource.gerrit.plugins.reviewers.ReviewerFilter;
+import com.googlesource.gerrit.plugins.reviewers.ReviewerType;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.CommitBuilder;
@@ -39,9 +38,10 @@
public class ReviewersConfig {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- static final String FILENAME = "reviewers.config";
- static final String SECTION_FILTER = "filter";
- static final String KEY_REVIEWER = "reviewer";
+ @VisibleForTesting public static final String FILENAME = "reviewers.config";
+ @VisibleForTesting public static final String SECTION_FILTER = "filter";
+ @VisibleForTesting public static final String KEY_CC = "cc";
+ @VisibleForTesting public static final String KEY_REVIEWER = "reviewer";
private static final String KEY_ENABLE_REST = "enableREST";
private static final String KEY_SUGGEST_ONLY = "suggestOnly";
private static final String KEY_IGNORE_WIP = "ignoreWip";
@@ -63,7 +63,7 @@
this.ignoreWip = cfg.getBoolean(pluginName, null, KEY_IGNORE_WIP, true);
}
- public ForProject forProject(Project.NameKey projectName) {
+ public List<ReviewerFilter> filtersWithInheritance(Project.NameKey projectName) {
Config cfg;
try {
cfg = cfgFactory.getProjectPluginConfigWithMergedInheritance(projectName, pluginName);
@@ -71,7 +71,7 @@
logger.atSevere().log("Unable to get config for project %s", projectName.get());
cfg = new Config();
}
- return new ForProject(cfg);
+ return new ReviewerFilterCollection(cfg).getAll();
}
public boolean enableREST() {
@@ -86,51 +86,30 @@
return ignoreWip;
}
- static class ForProject extends VersionedMetaData {
+ public static class ForProject extends VersionedMetaData {
private Config cfg;
+ private ReviewerFilterCollection filters;
- ForProject(Config cfg) {
- this.cfg = cfg;
- }
-
- List<ReviewerFilterSection> getReviewerFilterSections() {
- ImmutableList.Builder<ReviewerFilterSection> b = ImmutableList.builder();
- for (String f : cfg.getSubsections(SECTION_FILTER)) {
- b.add(newReviewerFilterSection(f));
- }
- return b.build();
- }
-
- void addReviewer(String filter, String reviewer) {
- if (!newReviewerFilterSection(filter).getReviewers().contains(reviewer)) {
- List<String> values =
- new ArrayList<>(Arrays.asList(cfg.getStringList(SECTION_FILTER, filter, KEY_REVIEWER)));
- values.add(reviewer);
- cfg.setStringList(SECTION_FILTER, filter, KEY_REVIEWER, values);
+ public void addReviewer(String filter, String reviewer, ReviewerType type) {
+ switch (type) {
+ case REVIEWER:
+ filters.get(filter).addReviewer(reviewer);
+ break;
+ case CC:
+ filters.get(filter).addCc(reviewer);
}
}
- void removeReviewer(String filter, String reviewer) {
- if (newReviewerFilterSection(filter).getReviewers().contains(reviewer)) {
- List<String> values =
- new ArrayList<>(Arrays.asList(cfg.getStringList(SECTION_FILTER, filter, KEY_REVIEWER)));
- values.remove(reviewer);
- if (values.isEmpty()) {
- cfg.unsetSection(SECTION_FILTER, filter);
- } else {
- cfg.setStringList(SECTION_FILTER, filter, KEY_REVIEWER, values);
- }
+ public void removeReviewer(String filter, String reviewer, ReviewerType type) {
+ switch (type) {
+ case REVIEWER:
+ filters.get(filter).removeReviewer(reviewer);
+ break;
+ case CC:
+ filters.get(filter).removeCc(reviewer);
}
}
- private ReviewerFilterSection newReviewerFilterSection(String filter) {
- ImmutableSet.Builder<String> b = ImmutableSet.builder();
- for (String reviewer : cfg.getStringList(SECTION_FILTER, filter, KEY_REVIEWER)) {
- b.add(reviewer);
- }
- return new ReviewerFilterSection(filter, b.build());
- }
-
@Override
protected String getRefName() {
return RefNames.REFS_CONFIG;
@@ -138,7 +117,8 @@
@Override
protected void onLoad() throws IOException, ConfigInvalidException {
- cfg = readConfig(FILENAME);
+ this.cfg = readConfig(FILENAME);
+ this.filters = new ReviewerFilterCollection(cfg);
}
@Override
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index 1088535..1b51181 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -38,6 +38,7 @@
```
[filter "*"]
reviewer = john.doe@example.com
+ cc = DevGroup
[filter "branch:main file:^lib/.*"]
reviewer = jane.doe@example.com
@@ -52,9 +53,16 @@
account's email address or username, or the group name. Multiple `reviewer`
occurrences are allowed.
+filter.\<filter\>.cc
+: An account or a group name. Must be an exact match (case sensitive) with the
+ account's email address or username, or the group name. Multiple `cc`
+ occurrences are allowed.
+
##Multiple filter matches
The plugin supports multiple filter matches.
+If a reviewer, according to filter matches, should be added as both `reviewer` and `cc`,
+`reviewer` takes precedence.
###Example
diff --git a/src/main/resources/Documentation/rest-api.md b/src/main/resources/Documentation/rest-api.md
index ebd04d4..957318f 100644
--- a/src/main/resources/Documentation/rest-api.md
+++ b/src/main/resources/Documentation/rest-api.md
@@ -20,7 +20,7 @@
GET /projects/myproject/@PLUGIN@ HTTP/1.0
```
-As response a List of [ReviewerFilterSection](#reviewer-filter-section) is returned
+As response a List of [ReviewerFilter](#reviewer-filter) is returned
that describes the default reviewers for myproject.
#### Response
@@ -36,6 +36,9 @@
"reviewers": [
"UserA",
"UserB"
+ ],
+ "cc": [
+ "DevGroup"
]
},
{
@@ -66,13 +69,14 @@
Content-Type: application/json;charset=UTF-8
{
"action": "ADD",
- "filter": "branch:master"
- "reviewer": "UserA"
+ "type": "REVIEWER"
+ "filter": "branch:master",
+ "reviewer": "UserA",
}
```
As response the default reviewers are returned as a list of
-[ReviewerFilterSection](#reviewer-filter-section).
+[ReviewerFilter](#reviewer-filter).
#### Response
@@ -88,6 +92,9 @@
"UserA",
"UserB"
]
+ "ccs": [
+ "DevGroup"
+ ]
},
{
"filter": "file:^lib/*",
@@ -104,14 +111,15 @@
<a id="json-entities">JSON Entities
-----------------------------------
-### <a id="reviewer-filter-section"></a>ReviewerFilterSection
+### <a id="reviewer-filter"></a>ReviewerFilter
-The `ReviewerFilterSection` entity contains a filter section of the
-default reviewers.
+The `ReviewerFilter` entity contains a filter of the default reviewers.
* _filter_: A filter that is used to assign default reviewers.
* _reviewers_: List of usernames which are assigned as default reviewers
under the filter.
+* _ccs_: List of usernames which are assigned as default ccs
+ under the filter.
### <a id="config-reviewers-input"></a>ConfigReviewersInput
@@ -119,6 +127,7 @@
reviewers.
* _action_: Indicates whether to add or remove the input reviewer
+* _type_: Which type to add/remove the user as, (REVIEWER|CC), defaults to REVIEWER.
* _filter_: The filter associated with the input reviewer.
* _reviewer_: The user to add or remove from the default reviewers.
diff --git a/src/test/java/com/googlesource/gerrit/plugins/reviewers/AbstractReviewersPluginTest.java b/src/test/java/com/googlesource/gerrit/plugins/reviewers/AbstractReviewersPluginTest.java
index a8b308a..1407b48 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/reviewers/AbstractReviewersPluginTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/reviewers/AbstractReviewersPluginTest.java
@@ -15,19 +15,20 @@
package com.googlesource.gerrit.plugins.reviewers;
import static com.google.gerrit.acceptance.GitUtil.fetch;
-import static com.googlesource.gerrit.plugins.reviewers.ReviewersConfig.FILENAME;
-import static com.googlesource.gerrit.plugins.reviewers.ReviewersConfig.KEY_REVIEWER;
-import static com.googlesource.gerrit.plugins.reviewers.ReviewersConfig.SECTION_FILTER;
+import static com.googlesource.gerrit.plugins.reviewers.config.ReviewersConfig.FILENAME;
+import static com.googlesource.gerrit.plugins.reviewers.config.ReviewersConfig.KEY_CC;
+import static com.googlesource.gerrit.plugins.reviewers.config.ReviewersConfig.KEY_REVIEWER;
+import static com.googlesource.gerrit.plugins.reviewers.config.ReviewersConfig.SECTION_FILTER;
-import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
import com.google.gerrit.acceptance.LightweightPluginDaemonTest;
import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.entities.RefNames;
import com.google.inject.Inject;
import java.util.Arrays;
-import java.util.List;
+import java.util.Set;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Config;
import org.junit.Ignore;
@@ -37,16 +38,21 @@
public class AbstractReviewersPluginTest extends LightweightPluginDaemonTest {
@Inject protected ProjectOperations projectOperations;
- protected void createFilters(FilterData... filters) throws Exception {
+ protected void createFilters(TestFilter... filters) throws Exception {
createFiltersFor(testRepo, filters);
}
- protected void createFiltersFor(TestRepository<?> repo, FilterData... filters) throws Exception {
+ protected void createFiltersFor(TestRepository<?> repo, TestFilter... filters) throws Exception {
String previousHead = repo.getRepository().getBranch();
checkoutRefsMetaConfig(repo);
Config cfg = new Config();
Arrays.stream(filters)
- .forEach(f -> cfg.setStringList(SECTION_FILTER, f.filter, KEY_REVIEWER, f.reviewers));
+ .forEach(
+ f -> {
+ cfg.setStringList(
+ SECTION_FILTER, f.filter, KEY_REVIEWER, Lists.newArrayList(f.reviewers));
+ cfg.setStringList(SECTION_FILTER, f.filter, KEY_CC, Lists.newArrayList(f.ccs));
+ });
pushFactory
.create(admin.newIdent(), repo, "Add reviewers", FILENAME, cfg.toText())
.to(RefNames.REFS_CONFIG)
@@ -60,31 +66,42 @@
return repo;
}
- protected FilterData filter(String filter) {
- return new FilterData(filter);
+ private TestFilter filter(String filter, Set<String> reviewers, Set<String> ccs) {
+ return new TestFilter(filter, reviewers, ccs);
}
- /** Assists tests to define a filter. */
- protected static class FilterData {
- List<String> reviewers;
- String filter;
+ protected TestFilter filter(String filter) {
+ return new TestFilter(filter);
+ }
- FilterData(String filter) {
+ protected static class TestFilter extends ReviewerFilter {
+
+ public TestFilter(String filter, Set<String> reviewers, Set<String> ccs) {
this.filter = filter;
- this.reviewers = Lists.newArrayList();
+ this.reviewers = reviewers;
+ this.ccs = ccs;
}
- FilterData reviewer(TestAccount reviewer) {
- return reviewer(reviewer.email());
+ public TestFilter(String filter) {
+ this(filter, Sets.newHashSet(), Sets.newHashSet());
}
- FilterData reviewer(String reviewer) {
- reviewers.add(reviewer);
+ public TestFilter reviewer(String reviewerId) {
+ reviewers.add(reviewerId);
return this;
}
- public ReviewerFilterSection asSection() {
- return new ReviewerFilterSection(filter, ImmutableSet.copyOf(reviewers));
+ public TestFilter reviewer(TestAccount reviewer) {
+ return reviewer(reviewer.email());
+ }
+
+ public TestFilter cc(String ccId) {
+ ccs.add(ccId);
+ return this;
+ }
+
+ public TestFilter cc(TestAccount cc) {
+ return cc(cc.email());
}
}
}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/reviewers/ReviewersIT.java b/src/test/java/com/googlesource/gerrit/plugins/reviewers/ReviewersIT.java
index a2a7035..1cb511e 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/reviewers/ReviewersIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/reviewers/ReviewersIT.java
@@ -15,6 +15,7 @@
package com.googlesource.gerrit.plugins.reviewers;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.extensions.client.ReviewerState.CC;
import static com.google.gerrit.extensions.client.ReviewerState.REVIEWER;
import static java.util.stream.Collectors.toSet;
@@ -27,6 +28,7 @@
import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.client.ChangeStatus;
+import com.google.gerrit.extensions.client.ReviewerState;
import com.google.gerrit.extensions.common.ChangeInfo;
import java.util.Set;
import org.junit.Test;
@@ -38,10 +40,19 @@
@Test
public void addReviewers() throws Exception {
TestAccount user2 = accountCreator.user2();
- createFilters(filter("*").reviewer(user).reviewer(user2));
+ createFilters(filter("*").reviewer(user).cc(user2));
String changeId = createChange().getChangeId();
- assertThat(reviewersFor(changeId))
- .containsExactlyElementsIn(ImmutableSet.of(user.id(), user2.id()));
+ assertThat(reviewersFor(changeId)).containsExactlyElementsIn(ImmutableSet.of(user.id()));
+ assertThat(ccsFor(changeId)).containsExactlyElementsIn(ImmutableSet.of(user2.id()));
+ }
+
+ @Test
+ public void addReviewerMatchingReviewerAndCc() throws Exception {
+ TestAccount user2 = accountCreator.user2();
+ createFilters(filter("*").cc(user).cc(user2), filter("^a.txt").reviewer(user2));
+ String changeId = createChange().getChangeId();
+ assertThat(reviewersFor(changeId)).containsExactlyElementsIn(ImmutableSet.of(user2.id()));
+ assertThat(ccsFor(changeId)).containsExactlyElementsIn(ImmutableSet.of(user.id()));
}
@Test
@@ -54,8 +65,9 @@
}
@Test
- public void doNotAddReviewersFromNonMatchingFilters() throws Exception {
- createFilters(filter("branch:master").reviewer(user));
+ public void doNotAddReviewersOrCcFromNonMatchingFilters() throws Exception {
+ TestAccount user2 = accountCreator.user2();
+ createFilters(filter("branch:master").reviewer(user).cc(user2));
createBranch(BranchNameKey.create(project, "other-branch"));
// Create a change that matches the filter section.
createChange("refs/for/master");
@@ -113,13 +125,23 @@
assertThat(reviewersFor(changeId)).containsExactlyElementsIn(ImmutableSet.of(user.id()));
}
+ private Set<Account.Id> ccsFor(String changeId) throws Exception {
+ return reviewersFor(changeId, CC);
+ }
+
private Set<Account.Id> reviewersFor(String changeId) throws Exception {
- return gApi.changes().id(changeId).get().reviewers.get(REVIEWER).stream()
+ return reviewersFor(changeId, REVIEWER);
+ }
+
+ private Set<Account.Id> reviewersFor(String changeId, ReviewerState reviewerState)
+ throws Exception {
+ return gApi.changes().id(changeId).get().reviewers.get(reviewerState).stream()
.map(a -> Account.id(a._accountId))
.collect(toSet());
}
private void assertNoReviewersAddedFor(String changeId) throws Exception {
assertThat(gApi.changes().id(changeId).get().reviewers.get(REVIEWER)).isNull();
+ assertThat(gApi.changes().id(changeId).get().reviewers.get(CC)).isNull();
}
}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/reviewers/ReviewersConfigIT.java b/src/test/java/com/googlesource/gerrit/plugins/reviewers/config/ReviewersConfigIT.java
similarity index 65%
rename from src/test/java/com/googlesource/gerrit/plugins/reviewers/ReviewersConfigIT.java
rename to src/test/java/com/googlesource/gerrit/plugins/reviewers/config/ReviewersConfigIT.java
index 19bb268..cd593c8 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/reviewers/ReviewersConfigIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/reviewers/config/ReviewersConfigIT.java
@@ -12,15 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.googlesource.gerrit.plugins.reviewers;
+package com.googlesource.gerrit.plugins.reviewers.config;
-import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.truth.Truth.assertThat;
+import com.google.common.collect.ImmutableList;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.TestPlugin;
import com.google.gerrit.entities.Project;
-import java.util.Arrays;
+import com.googlesource.gerrit.plugins.reviewers.AbstractReviewersPluginTest;
import org.eclipse.jgit.junit.TestRepository;
import org.junit.Before;
import org.junit.Test;
@@ -47,28 +47,39 @@
}
@Test
- public void reviewersConfigWithMergedInheritance() throws Exception {
+ public void reviewersConfigSingleWithCc() throws Exception {
+ createFilters(filter(NO_FILTER).reviewer(JOHN_DOE).cc(JANE_DOE));
+
+ assertProjectHasFilters(project, filter(NO_FILTER).reviewer(JOHN_DOE).cc(JANE_DOE));
+ }
+
+ @Test
+ public void reviewersConfigSingleWithCcSeparateFilters() throws Exception {
+ createFilters(filter(NO_FILTER).reviewer(JOHN_DOE), filter(BRANCH_MAIN).cc(JANE_DOE));
+
+ assertProjectHasFilters(
+ project, filter(NO_FILTER).reviewer(JOHN_DOE), filter(BRANCH_MAIN).cc(JANE_DOE));
+ }
+
+ @Test
+ public void reviewersConfigWithMergedInheritanceWithCc() throws Exception {
Project.NameKey childProject = projectOperations.newProject().parent(project).create();
TestRepository<?> childTestRepo = checkoutRefsMetaConfig(cloneProject(childProject));
createFiltersFor(
- childTestRepo,
- filter(NO_FILTER).reviewer(JANE_DOE),
- filter(BRANCH_MAIN).reviewer(JANE_DOE));
+ childTestRepo, filter(NO_FILTER).reviewer(JANE_DOE), filter(BRANCH_MAIN).cc(JANE_DOE));
- createFilters(filter(NO_FILTER).reviewer(JOHN_DOE), filter(BRANCH_MAIN).reviewer(JOHN_DOE));
+ createFilters(filter(NO_FILTER).cc(JOHN_DOE), filter(BRANCH_MAIN).reviewer(JOHN_DOE));
assertProjectHasFilters(
childProject,
- filter(NO_FILTER).reviewer(JOHN_DOE).reviewer(JANE_DOE),
- filter(BRANCH_MAIN).reviewer(JOHN_DOE).reviewer(JANE_DOE));
+ filter(NO_FILTER).cc(JOHN_DOE).reviewer(JANE_DOE),
+ filter(BRANCH_MAIN).reviewer(JOHN_DOE).cc(JANE_DOE));
}
- private void assertProjectHasFilters(Project.NameKey project, FilterData... filters) {
- assertThat(reviewersConfig().forProject(project).getReviewerFilterSections())
- .containsExactlyElementsIn(
- Arrays.stream(filters).map(f -> f.asSection()).collect(toImmutableList()))
- .inOrder();
+ private void assertProjectHasFilters(Project.NameKey project, TestFilter... filters) {
+ assertThat(reviewersConfig().filtersWithInheritance(project))
+ .containsExactlyElementsIn(ImmutableList.copyOf(filters));
}
private ReviewersConfig reviewersConfig() {