Merge changes from topic 'rebuild-fixes'
* changes:
ChangeNotesParser: Don't open the repository
AbstractChangeNotes: Disallow null Change.Id
Disable hashtag tests under GERRIT_NOTEDB=write
NoteDb: Don't convert entities whose patch set is missing
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index ae42477..423d87d 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -1437,6 +1437,142 @@
}
----
+[[get-watched-projects]]
+=== Get Watched Projects
+--
+'GET /accounts/link:#account-id[\{account-id\}]/watched.projects'
+--
+
+Retrieves all projects a user is watching.
+
+.Request
+----
+ GET /a/accounts/self/watched.projects HTTP/1.0
+----
+
+As result the watched projects of the user are returned as a list of
+link:#project-watch-info[ProjectWatchInfo] entities.
+The result is sorted by project name in ascending order.
+
+.Response
+----
+ HTTP/1.1 200 OK
+ Content-Disposition: attachment
+ Content-Type: application/json; charset=UTF-8
+
+ )]}'
+ [
+ {
+ "project": "Test Project 1",
+ "notify_new_changes": true,
+ "notify_new_patch_sets": true,
+ "notify_all_comments": true,
+ },
+ {
+ "project": "Test Project 2",
+ "filter": "branch:experimental",
+ "notify_all_comments": true,
+ "notify_submitted_changes": true,
+ "notify_abandoned_changes": true
+ }
+ ]
+----
+
+[[set-watched-projects]]
+=== Add/Update a List of Watched Project Entities
+--
+'POST /accounts/link:#account-id[\{account-id\}]/watched.projects'
+--
+
+Add new projects to watch or update existing watched projects.
+Projects that are already watched by a user will be updated with
+the provided configuration. All other projects in the request
+will be watched using the provided configuration. The posted body
+can contain link:#project-watch-info[ProjectWatchInfo] entities.
+Omitted boolean values will be set to false.
+
+.Request
+----
+ POST /a/accounts/self/watched.projects HTTP/1.0
+ Content-Type: application/json;charset=UTF-8
+
+ [
+ {
+ "project": "Test Project 1",
+ "notify_new_changes": true,
+ "notify_new_patch_sets": true,
+ "notify_all_comments": true,
+ }
+ ]
+----
+
+As result the watched projects of the user are returned as a list of
+link:#project-watch-info[ProjectWatchInfo] entities.
+The result is sorted by project name in ascending order.
+
+.Response
+----
+ HTTP/1.1 200 OK
+ Content-Disposition: attachment
+ Content-Type: application/json; charset=UTF-8
+
+ )]}'
+ [
+ {
+ "project": "Test Project 1",
+ "notify_new_changes": true,
+ "notify_new_patch_sets": true,
+ "notify_all_comments": true,
+ },
+ {
+ "project": "Test Project 2",
+ "notify_new_changes": true,
+ "notify_new_patch_sets": true,
+ "notify_all_comments": true,
+ }
+ ]
+----
+
+[[delete-watched-projects]]
+=== Delete Watched Projects
+--
+'POST /accounts/link:#account-id[\{account-id\}]/watched.projects:delete'
+--
+
+Projects posted to this endpoint will no longer be watched. The posted body
+can contain an array of project names as strings.
+
+.Request
+----
+ POST /a/accounts/self/watched.projects:delete HTTP/1.0
+ Content-Type: application/json;charset=UTF-8
+
+ [
+ "Test Project 1"
+ ]
+----
+
+As result the watched projects of the user are returned as a list of
+link:#project-watch-info[ProjectWatchInfo] entities.
+The result is sorted by project name in ascending order.
+
+.Response
+----
+ HTTP/1.1 200 OK
+ Content-Disposition: attachment
+ Content-Type: application/json; charset=UTF-8
+
+ )]}'
+ [
+ {
+ "project": "Test Project 2",
+ "notify_new_changes": true,
+ "notify_new_patch_sets": true,
+ "notify_all_comments": true,
+ }
+ ]
+----
+
[[get-starred-changes]]
=== Get Starred Changes
--
@@ -2095,6 +2231,22 @@
|`username` |The new username of the account.
|=======================
+[[project-watch-info]]
+=== ProjectWatchInfo
+The `WatchedProjectsInfo` entity contains information about a project watch
+for a user.
+
+[options="header",cols="1,^1,5"]
+|=======================
+|Field Name | |Description
+|`project` | |The name of the project.
+|`filter` |optional|A filter string to be applied to the project.
+|`notify_new_changes` |optional|Notify on new changes.
+|`notify_new_patch_sets` |optional|Notify on new patch sets.
+|`notify_all_comments` |optional|Notify on comments.
+|`notify_submitted_changes` |optional|Notify on submitted changes.
+|`notify_abandoned_changes` |optional|Notify on abandoned changes.
+|=======================
GERRIT
------
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/WatchedProjectsIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/WatchedProjectsIT.java
new file mode 100644
index 0000000..879430a
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/WatchedProjectsIT.java
@@ -0,0 +1,169 @@
+// Copyright (C) 2016 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.google.gerrit.acceptance.rest.account;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.Lists;
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.extensions.client.ProjectWatchInfo;
+import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+public class WatchedProjectsIT extends AbstractDaemonTest {
+
+ private static final String NEW_PROJECT_NAME = "newProjectAccess";
+
+ @Test
+ public void setAndGetWatchedProjects() throws Exception {
+ String projectName1 = createProject(NEW_PROJECT_NAME).get();
+ String projectName2 = createProject(NEW_PROJECT_NAME + "2").get();
+
+ List<ProjectWatchInfo> projectsToWatch = new ArrayList<>(2);
+
+ ProjectWatchInfo pwi = new ProjectWatchInfo();
+ pwi.project = projectName1;
+ pwi.notifyAbandonedChanges = true;
+ pwi.notifyNewChanges = true;
+ pwi.notifyAllComments = true;
+ projectsToWatch.add(pwi);
+
+ pwi = new ProjectWatchInfo();
+ pwi.project = projectName2;
+ pwi.filter = "branch:master";
+ pwi.notifySubmittedChanges = true;
+ pwi.notifyNewPatchSets = true;
+ projectsToWatch.add(pwi);
+
+ List<ProjectWatchInfo> persistedWatchedProjects =
+ gApi.accounts().self().setWatchedProjects(projectsToWatch);
+ assertThat(persistedWatchedProjects)
+ .containsAllIn(projectsToWatch).inOrder();
+ }
+
+ @Test
+ public void setAndDeleteWatchedProjects() throws Exception {
+ String projectName1 = createProject(NEW_PROJECT_NAME).get();
+ String projectName2 = createProject(NEW_PROJECT_NAME + "2").get();
+
+ List<ProjectWatchInfo> projectsToWatch = new LinkedList<>();
+
+ ProjectWatchInfo pwi = new ProjectWatchInfo();
+ pwi.project = projectName1;
+ pwi.notifyAbandonedChanges = true;
+ pwi.notifyNewChanges = true;
+ pwi.notifyAllComments = true;
+ projectsToWatch.add(pwi);
+
+ pwi = new ProjectWatchInfo();
+ pwi.project = projectName2;
+ pwi.filter = "branch:master";
+ pwi.notifySubmittedChanges = true;
+ pwi.notifyNewPatchSets = true;
+ projectsToWatch.add(pwi);
+
+ // Persist watched projects
+ gApi.accounts().self().setWatchedProjects(projectsToWatch);
+
+ List<String> d = Lists.newArrayList(projectName2);
+ gApi.accounts().self().deleteWatchedProjects(d);
+ projectsToWatch.remove(pwi);
+
+ List<ProjectWatchInfo> persistedWatchedProjects =
+ gApi.accounts().self().getWatchedProjects();
+
+ assertThat(persistedWatchedProjects).doesNotContain(pwi);
+ assertThat(persistedWatchedProjects).containsAllIn(projectsToWatch);
+ }
+
+ @Test
+ public void watchNonExistingProject() throws Exception {
+ String projectName = NEW_PROJECT_NAME + "3";
+
+ List<ProjectWatchInfo> projectsToWatch = new ArrayList<>(2);
+
+ ProjectWatchInfo pwi = new ProjectWatchInfo();
+ pwi.project = projectName;
+ pwi.notifyAbandonedChanges = true;
+ pwi.notifyNewChanges = true;
+ pwi.notifyAllComments = true;
+ projectsToWatch.add(pwi);
+
+ exception.expect(UnprocessableEntityException.class);
+ gApi.accounts().self().setWatchedProjects(projectsToWatch);
+ }
+
+ @Test
+ public void deleteNonExistingProject() throws Exception {
+ String projectName = project.get();
+
+ // Let another user watch a project
+ setApiUser(admin);
+ List<ProjectWatchInfo> projectsToWatch = new LinkedList<>();
+
+ ProjectWatchInfo pwi = new ProjectWatchInfo();
+ pwi.project = projectName;
+ pwi.notifyAbandonedChanges = true;
+ pwi.notifyNewChanges = true;
+ pwi.notifyAllComments = true;
+ projectsToWatch.add(pwi);
+
+ gApi.accounts().self().setWatchedProjects(projectsToWatch);
+
+ // Try to delete a watched project using a different user
+ List<String> d = Lists.newArrayList(projectName);
+ gApi.accounts().self().deleteWatchedProjects(d);
+
+ setApiUser(user);
+ exception.expect(UnprocessableEntityException.class);
+ gApi.accounts().self().deleteWatchedProjects(d);
+ }
+
+ @Test
+ public void modifyProjectWatchUsingOmittedValues() throws Exception {
+ String projectName = project.get();
+
+ // Let another user watch a project
+ setApiUser(admin);
+ List<ProjectWatchInfo> projectsToWatch = new LinkedList<>();
+
+ ProjectWatchInfo pwi = new ProjectWatchInfo();
+ pwi.project = projectName;
+ pwi.notifyAbandonedChanges = true;
+ pwi.notifyNewChanges = true;
+ pwi.notifyAllComments = true;
+ projectsToWatch.add(pwi);
+
+ // Persist a defined state
+ gApi.accounts().self().setWatchedProjects(projectsToWatch);
+
+ // Omit previously set value - will set it to false on the server
+ // The response will not carry this field then as we omit sending
+ // false values in JSON
+ pwi.notifyNewChanges = null;
+
+ // Perform update
+ gApi.accounts().self().setWatchedProjects(projectsToWatch);
+
+ List<ProjectWatchInfo> watchedProjects =
+ gApi.accounts().self().getWatchedProjects();
+
+ assertThat(watchedProjects).containsAllIn(projectsToWatch);
+ }
+}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/accounts/AccountApi.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/accounts/AccountApi.java
index a6e54b2..2105b8c 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/accounts/AccountApi.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/accounts/AccountApi.java
@@ -17,6 +17,7 @@
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.extensions.client.EditPreferencesInfo;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
+import com.google.gerrit.extensions.client.ProjectWatchInfo;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.GpgKeyInfo;
import com.google.gerrit.extensions.common.SshKeyInfo;
@@ -43,6 +44,12 @@
EditPreferencesInfo setEditPreferences(EditPreferencesInfo in)
throws RestApiException;
+ List<ProjectWatchInfo> getWatchedProjects() throws RestApiException;
+ List<ProjectWatchInfo> setWatchedProjects(List<ProjectWatchInfo> in)
+ throws RestApiException;
+ void deleteWatchedProjects(List<String> in)
+ throws RestApiException;
+
void starChange(String id) throws RestApiException;
void unstarChange(String id) throws RestApiException;
void addEmail(EmailInput input) throws RestApiException;
@@ -105,6 +112,24 @@
}
@Override
+ public List<ProjectWatchInfo> getWatchedProjects()
+ throws RestApiException {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public List<ProjectWatchInfo> setWatchedProjects(
+ List<ProjectWatchInfo> in) throws RestApiException {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public void deleteWatchedProjects(List<String> in)
+ throws RestApiException {
+ throw new NotImplementedException();
+ }
+
+ @Override
public void starChange(String id) throws RestApiException {
throw new NotImplementedException();
}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/ProjectWatchInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/ProjectWatchInfo.java
new file mode 100644
index 0000000..beb869e
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/ProjectWatchInfo.java
@@ -0,0 +1,50 @@
+// Copyright (C) 2016 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.google.gerrit.extensions.client;
+
+import java.util.Objects;
+
+public class ProjectWatchInfo {
+ public String project;
+ public String filter;
+
+ public Boolean notifyNewChanges;
+ public Boolean notifyNewPatchSets;
+ public Boolean notifyAllComments;
+ public Boolean notifySubmittedChanges;
+ public Boolean notifyAbandonedChanges;
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof ProjectWatchInfo) {
+ ProjectWatchInfo w = (ProjectWatchInfo) obj;
+ return Objects.equals(project, w.project)
+ && Objects.equals(filter, w.filter)
+ && Objects.equals(notifyNewChanges, w.notifyNewChanges)
+ && Objects.equals(notifyNewPatchSets, w.notifyNewPatchSets)
+ && Objects.equals(notifyAllComments, w.notifyAllComments)
+ && Objects.equals(notifySubmittedChanges, w.notifySubmittedChanges)
+ && Objects.equals(notifyAbandonedChanges, w.notifyAbandonedChanges);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects
+ .hash(project, filter, notifyNewChanges, notifyNewPatchSets,
+ notifyAllComments, notifySubmittedChanges, notifyAbandonedChanges);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteWatchedProjects.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteWatchedProjects.java
new file mode 100644
index 0000000..674cba6
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteWatchedProjects.java
@@ -0,0 +1,77 @@
+// Copyright (C) 2016 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.google.gerrit.server.account;
+
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
+import com.google.gerrit.reviewdb.client.AccountProjectWatch;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gwtorm.server.OrmException;
+import com.google.gwtorm.server.ResultSet;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+
+@Singleton
+public class DeleteWatchedProjects
+ implements RestModifyView<AccountResource, List<String>> {
+
+ private final Provider<ReviewDb> dbProvider;
+ private final Provider<IdentifiedUser> self;
+
+ @Inject
+ DeleteWatchedProjects(Provider<ReviewDb> dbProvider,
+ Provider<IdentifiedUser> self) {
+ this.dbProvider = dbProvider;
+ this.self = self;
+ }
+
+ @Override
+ public Response<?> apply(
+ AccountResource rsrc, List<String> input)
+ throws UnprocessableEntityException, OrmException, AuthException {
+ if (self.get() != rsrc.getUser()) {
+ throw new AuthException("It is not allowed to edit project watches "
+ + "of other users");
+ }
+ ResultSet<AccountProjectWatch> watchedProjects =
+ dbProvider.get().accountProjectWatches()
+ .byAccount(rsrc.getUser().getAccountId());
+ HashMap<String, AccountProjectWatch> watchedProjectsMap = new HashMap<>();
+ for (AccountProjectWatch watchedProject : watchedProjects) {
+ watchedProjectsMap
+ .put(watchedProject.getProjectNameKey().get(), watchedProject);
+ }
+
+ if (input != null) {
+ List<AccountProjectWatch.Key> keysToDelete = new LinkedList<>();
+ for (String projectKeyToDelete : input) {
+ if (!watchedProjectsMap.containsKey(projectKeyToDelete))
+ throw new UnprocessableEntityException(projectKeyToDelete
+ + " is not currently watched by this user.");
+ keysToDelete.add(watchedProjectsMap.get(projectKeyToDelete).getKey());
+ }
+ dbProvider.get().accountProjectWatches().deleteKeys(keysToDelete);
+ }
+
+ return Response.none();
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetWatchedProjects.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetWatchedProjects.java
new file mode 100644
index 0000000..fd00b7a
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetWatchedProjects.java
@@ -0,0 +1,78 @@
+// Copyright (C) 2016 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.google.gerrit.server.account;
+
+import com.google.gerrit.extensions.client.ProjectWatchInfo;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.reviewdb.client.AccountProjectWatch;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+
+import java.util.LinkedList;
+import java.util.List;
+
+@Singleton
+public class GetWatchedProjects implements RestReadView<AccountResource> {
+
+ private final Provider<ReviewDb> dbProvider;
+ private final Provider<IdentifiedUser> self;
+
+ @Inject
+ public GetWatchedProjects(Provider<ReviewDb> dbProvider,
+ Provider<IdentifiedUser> self) {
+ this.dbProvider = dbProvider;
+ this.self = self;
+ }
+
+ @Override
+ public List<ProjectWatchInfo> apply(AccountResource rsrc)
+ throws OrmException, AuthException {
+ if (self.get() != rsrc.getUser()) {
+ throw new AuthException("It is not allowed to list project watches "
+ + "of other users");
+ }
+ List<ProjectWatchInfo> projectWatchInfos = new LinkedList<>();
+ Iterable<AccountProjectWatch> projectWatches =
+ dbProvider.get().accountProjectWatches()
+ .byAccount(rsrc.getUser().getAccountId());
+ for (AccountProjectWatch a : projectWatches) {
+ ProjectWatchInfo pwi = new ProjectWatchInfo();
+ pwi.filter = a.getFilter();
+ pwi.project = a.getProjectNameKey().get();
+ pwi.notifyAbandonedChanges =
+ toBoolean(
+ a.isNotify(AccountProjectWatch.NotifyType.ABANDONED_CHANGES));
+ pwi.notifyNewChanges =
+ toBoolean(a.isNotify(AccountProjectWatch.NotifyType.NEW_CHANGES));
+ pwi.notifyNewPatchSets =
+ toBoolean(a.isNotify(AccountProjectWatch.NotifyType.NEW_PATCHSETS));
+ pwi.notifySubmittedChanges =
+ toBoolean(
+ a.isNotify(AccountProjectWatch.NotifyType.SUBMITTED_CHANGES));
+ pwi.notifyAllComments =
+ toBoolean(a.isNotify(AccountProjectWatch.NotifyType.ALL_COMMENTS));
+ projectWatchInfos.add(pwi);
+ }
+ return projectWatchInfos;
+ }
+
+ private static Boolean toBoolean(boolean value) {
+ return value ? true : null;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java
index 54d4cc0..b11be39 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java
@@ -56,6 +56,10 @@
delete(ACCOUNT_KIND, "password.http").to(PutHttpPassword.class);
child(ACCOUNT_KIND, "sshkeys").to(SshKeys.class);
post(ACCOUNT_KIND, "sshkeys").to(AddSshKey.class);
+ get(ACCOUNT_KIND, "watched.projects").to(GetWatchedProjects.class);
+ post(ACCOUNT_KIND, "watched.projects").to(PostWatchedProjects.class);
+ post(ACCOUNT_KIND, "watched.projects:delete")
+ .to(DeleteWatchedProjects.class);
get(SSH_KEY_KIND).to(GetSshKey.class);
delete(SSH_KEY_KIND).to(DeleteSshKey.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PostWatchedProjects.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PostWatchedProjects.java
new file mode 100644
index 0000000..cf67ad9
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/PostWatchedProjects.java
@@ -0,0 +1,102 @@
+// Copyright (C) 2016 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.google.gerrit.server.account;
+
+import com.google.gerrit.extensions.client.ProjectWatchInfo;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.AccountProjectWatch;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.project.ProjectsCollection;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+
+@Singleton
+public class PostWatchedProjects
+ implements RestModifyView<AccountResource, List<ProjectWatchInfo>> {
+ private final Provider<IdentifiedUser> self;
+ private GetWatchedProjects getWatchedProjects;
+ private Provider<ReviewDb> dbProvider;
+ private ProjectsCollection projectsCollection;
+
+ @Inject
+ public PostWatchedProjects(GetWatchedProjects getWatchedProjects,
+ Provider<ReviewDb> dbProvider,
+ ProjectsCollection projectsCollection,
+ Provider<IdentifiedUser> self) {
+ this.getWatchedProjects = getWatchedProjects;
+ this.dbProvider = dbProvider;
+ this.projectsCollection = projectsCollection;
+ this.self = self;
+ }
+
+ @Override
+ public List<ProjectWatchInfo> apply(AccountResource rsrc,
+ List<ProjectWatchInfo> input)
+ throws OrmException, RestApiException, IOException {
+ if (self.get() != rsrc.getUser()) {
+ throw new AuthException("not allowed to edit project watches");
+ }
+ List<AccountProjectWatch> accountProjectWatchList =
+ getAccountProjectWatchList(input, rsrc.getUser().getAccountId());
+ dbProvider.get().accountProjectWatches().upsert(accountProjectWatchList);
+ return getWatchedProjects.apply(rsrc);
+ }
+
+ private List<AccountProjectWatch> getAccountProjectWatchList(
+ List<ProjectWatchInfo> input, Account.Id accountId)
+ throws UnprocessableEntityException, BadRequestException, IOException {
+ List<AccountProjectWatch> watchedProjects = new LinkedList<>();
+ for (ProjectWatchInfo a : input) {
+ if (a.project == null) {
+ throw new BadRequestException("project name must be specified");
+ }
+
+ Project.NameKey projectKey =
+ projectsCollection.parse(a.project).getNameKey();
+
+ AccountProjectWatch.Key key =
+ new AccountProjectWatch.Key(accountId, projectKey, a.filter);
+ AccountProjectWatch apw = new AccountProjectWatch(key);
+ apw.setNotify(AccountProjectWatch.NotifyType.ABANDONED_CHANGES,
+ toBoolean(a.notifyAbandonedChanges));
+ apw.setNotify(AccountProjectWatch.NotifyType.ALL_COMMENTS,
+ toBoolean(a.notifyAllComments));
+ apw.setNotify(AccountProjectWatch.NotifyType.NEW_CHANGES,
+ toBoolean(a.notifyNewChanges));
+ apw.setNotify(AccountProjectWatch.NotifyType.NEW_PATCHSETS,
+ toBoolean(a.notifyNewPatchSets));
+ apw.setNotify(AccountProjectWatch.NotifyType.SUBMITTED_CHANGES,
+ toBoolean(a.notifySubmittedChanges));
+ watchedProjects.add(apw);
+ }
+ return watchedProjects;
+ }
+
+ private boolean toBoolean(Boolean b) {
+ return b == null ? false : b;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java
index 709020f..5860391 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java
@@ -22,6 +22,7 @@
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.extensions.client.EditPreferencesInfo;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
+import com.google.gerrit.extensions.client.ProjectWatchInfo;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.GpgKeyInfo;
import com.google.gerrit.extensions.common.SshKeyInfo;
@@ -39,10 +40,13 @@
import com.google.gerrit.server.account.GetEditPreferences;
import com.google.gerrit.server.account.GetPreferences;
import com.google.gerrit.server.account.GetSshKeys;
+import com.google.gerrit.server.account.GetWatchedProjects;
+import com.google.gerrit.server.account.DeleteWatchedProjects;
import com.google.gerrit.server.account.SetDiffPreferences;
import com.google.gerrit.server.account.SetEditPreferences;
import com.google.gerrit.server.account.SetPreferences;
import com.google.gerrit.server.account.SshKeys;
+import com.google.gerrit.server.account.PostWatchedProjects;
import com.google.gerrit.server.account.StarredChanges;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.ChangesCollection;
@@ -71,6 +75,9 @@
private final SetDiffPreferences setDiffPreferences;
private final GetEditPreferences getEditPreferences;
private final SetEditPreferences setEditPreferences;
+ private final GetWatchedProjects getWatchedProjects;
+ private final PostWatchedProjects postWatchedProjects;
+ private final DeleteWatchedProjects deleteWatchedProjects;
private final StarredChanges.Create starredChangesCreate;
private final StarredChanges.Delete starredChangesDelete;
private final CreateEmail.Factory createEmailFactory;
@@ -90,6 +97,9 @@
SetDiffPreferences setDiffPreferences,
GetEditPreferences getEditPreferences,
SetEditPreferences setEditPreferences,
+ GetWatchedProjects getWatchedProjects,
+ PostWatchedProjects postWatchedProjects,
+ DeleteWatchedProjects deleteWatchedProjects,
StarredChanges.Create starredChangesCreate,
StarredChanges.Delete starredChangesDelete,
CreateEmail.Factory createEmailFactory,
@@ -109,6 +119,9 @@
this.setDiffPreferences = setDiffPreferences;
this.getEditPreferences = getEditPreferences;
this.setEditPreferences = setEditPreferences;
+ this.getWatchedProjects = getWatchedProjects;
+ this.postWatchedProjects = postWatchedProjects;
+ this.deleteWatchedProjects = deleteWatchedProjects;
this.starredChangesCreate = starredChangesCreate;
this.starredChangesDelete = starredChangesDelete;
this.createEmailFactory = createEmailFactory;
@@ -192,6 +205,35 @@
}
@Override
+ public List<ProjectWatchInfo> getWatchedProjects() throws RestApiException {
+ try {
+ return getWatchedProjects.apply(account);
+ } catch (OrmException e) {
+ throw new RestApiException("Cannot get watched projects", e);
+ }
+ }
+
+ @Override
+ public List<ProjectWatchInfo> setWatchedProjects(
+ List<ProjectWatchInfo> in) throws RestApiException {
+ try {
+ return postWatchedProjects.apply(account, in);
+ } catch (OrmException | IOException e) {
+ throw new RestApiException("Cannot update watched projects", e);
+ }
+ }
+
+ @Override
+ public void deleteWatchedProjects(List<String> in)
+ throws RestApiException {
+ try {
+ deleteWatchedProjects.apply(account, in);
+ } catch (OrmException e) {
+ throw new RestApiException("Cannot delete watched projects", e);
+ }
+ }
+
+ @Override
public void starChange(String id) throws RestApiException {
try {
ChangeResource rsrc = changes.parse(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOpRepoManager.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOpRepoManager.java
index 0482034..1f8930c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOpRepoManager.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOpRepoManager.java
@@ -49,7 +49,7 @@
* a transaction of a submission, this caches open repositories to satisfy
* that requirement.
*/
-class MergeOpRepoManager implements AutoCloseable {
+public class MergeOpRepoManager implements AutoCloseable {
public class OpenRepo {
final Repository repo;
final CodeReviewRevWalk rw;