Support GET, PUT, DELETE on /changes/{id}/topic
The topic is now modified by REST API calls against /topic. The web UI
uses PUT to alter the topic with a JSON payload, but the server
accepts quite a few different REST forms that clients can easily send.
Change-Id: Ia5edbb232bc288acdad8b145956ad275d170629a
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetailService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetailService.java
index 4905ce9..c50d2e3 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetailService.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetailService.java
@@ -42,9 +42,4 @@
@SignInRequired
void patchSetPublishDetail(PatchSet.Id key,
AsyncCallback<PatchSetPublishDetail> callback);
-
- @Audit
- @SignInRequired
- void alterTopic(Change.Id id, String topic, String message,
- AsyncCallback<ChangeDetail> callback);
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
index bcd755f..679b81e 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
@@ -14,6 +14,7 @@
package com.google.gerrit.client.changes;
+import com.google.gerrit.client.rpc.NativeString;
import com.google.gerrit.client.rpc.RestApi;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwtjsonrpc.common.AsyncCallback;
@@ -25,10 +26,6 @@
public class ChangeApi {
private static final String URI = "/changes/";
- protected static class Message extends JavaScriptObject {
- public final native void setMessage(String value) /*-{ this.message = value; }-*/;
- }
-
/**
* Sends a REST call to abandon a change and notify a callback. TODO: switch
* to use the new id triplet (project~branch~change) once that data is
@@ -36,8 +33,31 @@
*/
public static void abandon(int changeId, String message,
AsyncCallback<ChangeInfo> callback) {
- Message msg = (Message) JavaScriptObject.createObject();
- msg.setMessage(message);
- new RestApi(URI + changeId + "/abandon").data(msg).post(callback);
+ Input input = Input.create();
+ input.setMessage(emptyToNull(message));
+ new RestApi(URI + changeId + "/abandon").data(input).post(callback);
+ }
+
+ public static void topic(int id, String topic, String msg, AsyncCallback<String> cb) {
+ Input input = Input.create();
+ input.setTopic(emptyToNull(topic));
+ input.setMessage(emptyToNull(msg));
+ new RestApi(URI + id + "/topic").data(input).put(NativeString.unwrap(cb));
+ }
+
+ private static class Input extends JavaScriptObject {
+ final native void setTopic(String t) /*-{ this.topic = t; }-*/;
+ final native void setMessage(String m) /*-{ this.message = m; }-*/;
+
+ static Input create() {
+ return (Input) JavaScriptObject.createObject();
+ }
+
+ protected Input() {
+ }
+ }
+
+ private static String emptyToNull(String str) {
+ return str == null || str.isEmpty() ? null : str;
}
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfoBlock.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfoBlock.java
index c197af2..34e1bf2 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfoBlock.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfoBlock.java
@@ -21,6 +21,7 @@
import com.google.gerrit.client.ui.CommentedActionDialog;
import com.google.gerrit.client.ui.BranchLink;
import com.google.gerrit.client.ui.ProjectLink;
+import com.google.gerrit.common.PageLinks;
import com.google.gerrit.common.data.AccountInfoCache;
import com.google.gerrit.common.data.ChangeDetail;
import com.google.gerrit.common.data.SubmitTypeRecord;
@@ -39,6 +40,7 @@
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwtexpui.clippy.client.CopyableLabel;
+import com.google.gwtjsonrpc.common.AsyncCallback;
public class ChangeInfoBlock extends Composite {
private static final int R_CHANGE_ID = 0;
@@ -185,9 +187,19 @@
@Override
public void onSend() {
String topic = newTopic.getText();
- Util.DETAIL_SVC.alterTopic(change.getId(), topic,
- getMessageText(), createCallback());
+ ChangeApi.topic(change.getId().get(), topic, getMessageText(),
+ new AsyncCallback<String>() {
+ @Override
+ public void onSuccess(String result) {
+ sent = true;
+ Gerrit.display(PageLinks.toChange(change.getId()));
+ hide();
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ enableButtons(true);
+ }});
}
}
-
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/NativeString.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/NativeString.java
new file mode 100644
index 0000000..5242e9d
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/NativeString.java
@@ -0,0 +1,41 @@
+// Copyright (C) 2012 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.client.rpc;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwtjsonrpc.common.AsyncCallback;
+
+/** Wraps a String that was returned from a JSON API. */
+public final class NativeString extends JavaScriptObject {
+ public final native String asString() /*-{ return this; }-*/;
+
+ public static final AsyncCallback<NativeString>
+ unwrap(final AsyncCallback<String> cb) {
+ return new AsyncCallback<NativeString>() {
+ @Override
+ public void onSuccess(NativeString result) {
+ cb.onSuccess(result.asString());
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ cb.onFailure(caught);
+ }
+ };
+ }
+
+ protected NativeString() {
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/AlterTopicHandler.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/AlterTopicHandler.java
deleted file mode 100644
index 3b095b9..0000000
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/AlterTopicHandler.java
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright (C) 2012 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.httpd.rpc.changedetail;
-
-import com.google.gerrit.common.data.ChangeDetail;
-import com.google.gerrit.common.errors.NoSuchEntityException;
-import com.google.gerrit.httpd.rpc.Handler;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.server.changedetail.AlterTopic;
-import com.google.gerrit.server.mail.EmailException;
-import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
-import com.google.gerrit.server.project.InvalidChangeOperationException;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.assistedinject.Assisted;
-
-import org.eclipse.jgit.errors.RepositoryNotFoundException;
-
-import java.io.IOException;
-
-import javax.annotation.Nullable;
-
-class AlterTopicHandler extends Handler<ChangeDetail> {
-
- interface Factory {
- AlterTopicHandler create(@Assisted Change.Id changeId,
- @Assisted("topic") String topic,
- @Assisted("message") @Nullable String message);
- }
-
- private final Provider<AlterTopic> alterTopicProvider;
- private final ChangeDetailFactory.Factory changeDetailFactory;
-
- private final Change.Id changeId;
- private final String topic;
- @Nullable
- private final String message;
-
- @Inject
- AlterTopicHandler(final Provider<AlterTopic> alterTopicProvider,
- final ChangeDetailFactory.Factory changeDetailFactory,
- @Assisted final Change.Id changeId,
- @Assisted("topic") final String topic,
- @Assisted("message") @Nullable final String message) {
- this.alterTopicProvider = alterTopicProvider;
- this.changeDetailFactory = changeDetailFactory;
-
- this.changeId = changeId;
- this.topic = topic;
- this.message = message;
- }
-
- @Override
- public ChangeDetail call() throws EmailException, IOException,
- NoSuchChangeException, NoSuchEntityException, OrmException,
- PatchSetInfoNotAvailableException, RepositoryNotFoundException,
- InvalidChangeOperationException {
- final AlterTopic alterTopic = alterTopicProvider.get();
- alterTopic.setChangeId(changeId);
- alterTopic.setTopic(topic);
- alterTopic.setMessage(message);
- alterTopic.call();
- return changeDetailFactory.create(changeId).call();
- }
-}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailServiceImpl.java
index fd559b2..8090451 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailServiceImpl.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailServiceImpl.java
@@ -30,19 +30,16 @@
private final IncludedInDetailFactory.Factory includedInDetail;
private final PatchSetDetailFactory.Factory patchSetDetail;
private final PatchSetPublishDetailFactory.Factory patchSetPublishDetail;
- private final AlterTopicHandler.Factory alterTopic;
@Inject
ChangeDetailServiceImpl(final ChangeDetailFactory.Factory changeDetail,
final IncludedInDetailFactory.Factory includedInDetail,
final PatchSetDetailFactory.Factory patchSetDetail,
- final PatchSetPublishDetailFactory.Factory patchSetPublishDetail,
- final AlterTopicHandler.Factory alterTopic) {
+ final PatchSetPublishDetailFactory.Factory patchSetPublishDetail) {
this.changeDetail = changeDetail;
this.includedInDetail = includedInDetail;
this.patchSetDetail = patchSetDetail;
this.patchSetPublishDetail = patchSetPublishDetail;
- this.alterTopic = alterTopic;
}
public void changeDetail(final Change.Id id,
@@ -69,9 +66,4 @@
final AsyncCallback<PatchSetPublishDetail> callback) {
patchSetPublishDetail.create(id).to(callback);
}
-
- public void alterTopic(final Change.Id id, final String topic,
- final String message, final AsyncCallback<ChangeDetail> callback) {
- alterTopic.create(id, topic, message).to(callback);
- }
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeModule.java
index d8fb5a5..d3085aa 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeModule.java
@@ -28,7 +28,6 @@
install(new FactoryModule() {
@Override
protected void configure() {
- factory(AlterTopicHandler.Factory.class);
factory(RestoreChangeHandler.Factory.class);
factory(RevertChange.Factory.class);
factory(RebaseChangeHandler.Factory.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetTopic.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetTopic.java
new file mode 100644
index 0000000..96a5c76
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetTopic.java
@@ -0,0 +1,26 @@
+// Copyright (C) 2012 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.change;
+
+import com.google.common.base.Strings;
+import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gwtorm.server.OrmException;
+
+class GetTopic implements RestReadView<ChangeResource> {
+ @Override
+ public Object apply(ChangeResource rsrc) throws OrmException {
+ return Strings.nullToEmpty(rsrc.getChange().getTopic());
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
index 7981400..f0e7e46 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
@@ -30,6 +30,9 @@
DynamicMap.mapOf(binder(), REVISION_KIND);
get(CHANGE_KIND).to(GetChange.class);
+ get(CHANGE_KIND, "topic").to(GetTopic.class);
+ put(CHANGE_KIND, "topic").to(PutTopic.class);
+ delete(CHANGE_KIND, "topic").to(PutTopic.class);
post(CHANGE_KIND, "abandon").to(Abandon.class);
child(CHANGE_KIND, "reviewers").to(Reviewers.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PutTopic.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PutTopic.java
new file mode 100644
index 0000000..50c0011
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PutTopic.java
@@ -0,0 +1,120 @@
+// Copyright (C) 2012 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.change;
+
+import com.google.common.base.Strings;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.DefaultInput;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.ChangeMessage;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.ChangeUtil;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.change.PutTopic.Input;
+import com.google.gerrit.server.project.ChangeControl;
+import com.google.gwtorm.server.AtomicUpdate;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import java.util.Collections;
+
+class PutTopic implements RestModifyView<ChangeResource, Input> {
+ private final Provider<ReviewDb> dbProvider;
+
+ static class Input {
+ @DefaultInput
+ String topic;
+ String message;
+ }
+
+ @Inject
+ PutTopic(Provider<ReviewDb> dbProvider) {
+ this.dbProvider = dbProvider;
+ }
+
+ @Override
+ public Class<Input> inputType() {
+ return Input.class;
+ }
+
+ @Override
+ public Object apply(ChangeResource req, Input input)
+ throws BadRequestException, AuthException,
+ ResourceConflictException, Exception {
+ if (input == null) {
+ input = new Input();
+ }
+
+ ChangeControl control = req.getControl();
+ Change change = req.getChange();
+ if (!control.canEditTopicName()) {
+ throw new AuthException("changing topic not permitted");
+ } else if (!change.getStatus().isOpen()) {
+ throw new ResourceConflictException("change is " + status(change));
+ }
+
+ ReviewDb db = dbProvider.get();
+ final String newTopicName = Strings.nullToEmpty(input.topic);
+ String oldTopicName = Strings.nullToEmpty(change.getTopic());
+ if (!oldTopicName.equals(newTopicName)) {
+ String summary;
+ if (oldTopicName.isEmpty()) {
+ summary = "Topic set to \"" + newTopicName + "\".";
+ } else if (newTopicName.isEmpty()) {
+ summary = "Topic \"" + oldTopicName + "\" removed.";
+ } else {
+ summary = String.format(
+ "Topic updated from \"%s\" to \"%s\".",
+ oldTopicName, newTopicName);
+ }
+
+ ChangeMessage cmsg = new ChangeMessage(
+ new ChangeMessage.Key(change.getId(), ChangeUtil.messageUUID(db)),
+ ((IdentifiedUser) control.getCurrentUser()).getAccountId(),
+ change.currentPatchSetId());
+ StringBuilder msgBuf = new StringBuilder(summary);
+ if (!Strings.isNullOrEmpty(input.message)) {
+ msgBuf.append("\n\n");
+ msgBuf.append(input.message);
+ }
+ cmsg.setMessage(msgBuf.toString());
+
+ Change updatedChange = db.changes().atomicUpdate(change.getId(),
+ new AtomicUpdate<Change>() {
+ @Override
+ public Change update(Change change) {
+ if (change.getStatus().isOpen()) {
+ change.setTopic(Strings.emptyToNull(newTopicName));
+ return change;
+ }
+ return null;
+ }
+ });
+ if (updatedChange == null) {
+ change = db.changes().get(change.getId());
+ throw new ResourceConflictException("change is " + status(change));
+ }
+ db.changeMessages().insert(Collections.singleton(cmsg));
+ }
+ return Strings.nullToEmpty(newTopicName);
+ }
+
+ private static String status(Change change) {
+ return change != null ? change.getStatus().name().toLowerCase() : "deleted";
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/AlterTopic.java b/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/AlterTopic.java
deleted file mode 100644
index ad3fc7d..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/AlterTopic.java
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright (C) 2012 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.changedetail;
-
-import com.google.gerrit.common.data.ReviewResult;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.ChangeUtil;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.mail.EmailException;
-import com.google.gerrit.server.project.ChangeControl;
-import com.google.gerrit.server.project.InvalidChangeOperationException;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gwtorm.server.AtomicUpdate;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-
-import java.util.Collections;
-import java.util.concurrent.Callable;
-
-import org.kohsuke.args4j.Argument;
-import org.kohsuke.args4j.Option;
-
-public class AlterTopic implements Callable<ReviewResult> {
-
- private final ChangeControl.Factory changeControlFactory;
- private final ReviewDb db;
- private final IdentifiedUser currentUser;
-
- @Argument(index = 0, required = true, multiValued = false,
- usage = "change with topic to change")
- private Change.Id changeId;
-
- public void setChangeId(final Change.Id changeId) {
- this.changeId = changeId;
- }
-
- @Argument(index = 1, required = true, multiValued = false, usage = "new topic")
- private String newTopicName;
-
- public void setTopic(final String topic) {
- this.newTopicName = topic.trim();
- }
-
- @Option(name = "--message", aliases = {"-m"},
- usage = "optional message to append to change")
- private String message;
-
- public void setMessage(final String message) {
- this.message = message;
- }
-
- @Inject
- AlterTopic(final ChangeControl.Factory changeControlFactory, final ReviewDb db,
- final IdentifiedUser currentUser) {
- this.changeControlFactory = changeControlFactory;
- this.db = db;
- this.currentUser = currentUser;
-
- changeId = null;
- newTopicName = null;
- message = null;
- }
-
- @Override
- public ReviewResult call() throws EmailException,
- InvalidChangeOperationException, NoSuchChangeException, OrmException {
- final ChangeControl control = changeControlFactory.validateFor(changeId);
- final ReviewResult result = new ReviewResult();
- result.setChangeId(changeId);
-
- if (!control.canAddPatchSet()) {
- throw new NoSuchChangeException(changeId);
- }
- if (!control.canEditTopicName()) {
- result.addError(new ReviewResult.Error(
- ReviewResult.Error.Type.EDIT_TOPIC_NAME_NOT_PERMITTED));
- return result;
- }
-
- final Change change = db.changes().get(changeId);
- final String oldTopicName = change.getTopic() != null ? change.getTopic() : "";
- if (!oldTopicName.equals(newTopicName)) {
- String summary;
- if (oldTopicName.isEmpty()) {
- summary = "Topic set to \"" + newTopicName + "\"";
- } else if (newTopicName.isEmpty()) {
- summary = "Topic \"" + oldTopicName + "\" removed";
- } else {
- summary = "Topic changed from \"" + oldTopicName //
- + "\" to \"" + newTopicName + "\"";
- }
- final ChangeMessage cmsg = new ChangeMessage(
- new ChangeMessage.Key(changeId, ChangeUtil.messageUUID(db)),
- currentUser.getAccountId(), change.currentPatchSetId());
- final StringBuilder msgBuf = new StringBuilder(summary);
- if (message != null && message.length() > 0) {
- msgBuf.append("\n\n");
- msgBuf.append(message);
- }
- cmsg.setMessage(msgBuf.toString());
-
- final Change updatedChange = db.changes().atomicUpdate(changeId,
- new AtomicUpdate<Change>() {
- @Override
- public Change update(Change change) {
- change.setTopic(newTopicName);
- return change;
- }
- });
-
- if (updatedChange == null) {
- String err = "Patchset is not latest";
- throw new InvalidChangeOperationException(err);
- }
- db.changeMessages().insert(Collections.singleton(cmsg));
- }
-
- return result;
- }
-}