Add patch set description api endpoints
Adds GET and PUT endpoints for the patch set description. Feature: Issue 4544 Change-Id: I8b6d1bd46b711212f63d2b9e0847b8872a1df706
This commit is contained in:
@@ -2792,6 +2792,64 @@ describes the revision.
|
||||
Adding query parameter `links` (for example `/changes/.../commit?links`)
|
||||
returns a link:#commit-info[CommitInfo] with the additional field `web_links`.
|
||||
|
||||
[[get-description]]
|
||||
=== Get Description
|
||||
--
|
||||
'GET /changes/link:#change-id[\{change-id\}]/revisions/link:#revision-id[\{revision-id\}]/description'
|
||||
--
|
||||
|
||||
Retrieves the description of a patch set.
|
||||
|
||||
.Request
|
||||
----
|
||||
GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/revisions/674ac754f91e64a0efb8087e59a176484bd534d1/description HTTP/1.0
|
||||
----
|
||||
|
||||
.Response
|
||||
----
|
||||
HTTP/1.1 200 OK
|
||||
Content-Disposition: attachment
|
||||
Content-Type: application/json; charset=UTF-8
|
||||
|
||||
)]}'
|
||||
"Added Documentation"
|
||||
----
|
||||
|
||||
If the patch set does not have a description an empty string is returned.
|
||||
|
||||
[[set-description]]
|
||||
=== Set Description
|
||||
--
|
||||
'PUT /changes/link:#change-id[\{change-id\}]/revisions/link:#revision-id[\{revision-id\}]/description'
|
||||
--
|
||||
|
||||
Sets the description of a patch set.
|
||||
|
||||
The new description must be provided in the request body inside a
|
||||
link:#description-input[DescriptionInput] entity.
|
||||
|
||||
.Request
|
||||
----
|
||||
PUT /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/revisions/674ac754f91e64a0efb8087e59a176484bd534d1/description HTTP/1.0
|
||||
Content-Type: application/json; charset=UTF-8
|
||||
|
||||
{
|
||||
"description": "Added Documentation"
|
||||
}
|
||||
----
|
||||
|
||||
As response the new description is returned.
|
||||
|
||||
.Response
|
||||
----
|
||||
HTTP/1.1 200 OK
|
||||
Content-Disposition: attachment
|
||||
Content-Type: application/json; charset=UTF-8
|
||||
|
||||
)]}'
|
||||
"Added Documentation"
|
||||
----
|
||||
|
||||
[[get-merge-list]]
|
||||
=== Get Merge List
|
||||
--
|
||||
@@ -5097,6 +5155,16 @@ Allowed values are `NONE`, `OWNER`, `OWNER_REVIEWERS` and `ALL`. +
|
||||
If not set, the default is `ALL`.
|
||||
|=======================
|
||||
|
||||
[[description-input]]
|
||||
=== DescriptionInput
|
||||
The `DescriptionInput` entity contains information for setting a description.
|
||||
|
||||
[options="header",cols="1,6"]
|
||||
|===========================
|
||||
|Field Name |Description
|
||||
|`description` |The description text.
|
||||
|===========================
|
||||
|
||||
[[diff-content]]
|
||||
=== DiffContent
|
||||
The `DiffContent` entity contains information about the content differences
|
||||
|
@@ -770,6 +770,31 @@ public class RevisionIT extends AbstractDaemonTest {
|
||||
assertThat(diff.metaB.lines).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void description() throws Exception {
|
||||
PushOneCommit.Result r = createChange();
|
||||
assertThat(gApi.changes()
|
||||
.id(r.getChangeId())
|
||||
.revision(r.getCommit().name())
|
||||
.description()).isEqualTo("");
|
||||
gApi.changes()
|
||||
.id(r.getChangeId())
|
||||
.revision(r.getCommit().name())
|
||||
.description("test");
|
||||
assertThat(gApi.changes()
|
||||
.id(r.getChangeId())
|
||||
.revision(r.getCommit().name())
|
||||
.description()).isEqualTo("test");
|
||||
gApi.changes()
|
||||
.id(r.getChangeId())
|
||||
.revision(r.getCommit().name())
|
||||
.description("");
|
||||
assertThat(gApi.changes()
|
||||
.id(r.getChangeId())
|
||||
.revision(r.getCommit().name())
|
||||
.description()).isEqualTo("");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void content() throws Exception {
|
||||
PushOneCommit.Result r = createChange();
|
||||
@@ -946,11 +971,11 @@ public class RevisionIT extends AbstractDaemonTest {
|
||||
public void actions() throws Exception {
|
||||
PushOneCommit.Result r = createChange();
|
||||
assertThat(current(r).actions().keySet())
|
||||
.containsExactly("cherrypick", "rebase");
|
||||
.containsExactly("cherrypick", "description", "rebase");
|
||||
|
||||
current(r).review(ReviewInput.approve());
|
||||
assertThat(current(r).actions().keySet())
|
||||
.containsExactly("submit", "cherrypick", "rebase");
|
||||
.containsExactly("submit", "cherrypick", "description", "rebase");
|
||||
|
||||
current(r).submit();
|
||||
assertThat(current(r).actions().keySet())
|
||||
|
@@ -84,9 +84,10 @@ public class ActionsIT extends AbstractDaemonTest {
|
||||
public void revisionActionsOneChangePerTopicUnapproved() throws Exception {
|
||||
String changeId = createChangeWithTopic().getChangeId();
|
||||
Map<String, ActionInfo> actions = getActions(changeId);
|
||||
assertThat(actions).hasSize(3);
|
||||
assertThat(actions).containsKey("cherrypick");
|
||||
assertThat(actions).containsKey("rebase");
|
||||
assertThat(actions).hasSize(2);
|
||||
assertThat(actions).containsKey("description");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -436,9 +437,10 @@ public class ActionsIT extends AbstractDaemonTest {
|
||||
}
|
||||
|
||||
private void commonActionsAssertions(Map<String, ActionInfo> actions) {
|
||||
assertThat(actions).hasSize(3);
|
||||
assertThat(actions).hasSize(4);
|
||||
assertThat(actions).containsKey("cherrypick");
|
||||
assertThat(actions).containsKey("submit");
|
||||
assertThat(actions).containsKey("description");
|
||||
assertThat(actions).containsKey("rebase");
|
||||
}
|
||||
|
||||
|
@@ -33,6 +33,9 @@ import java.util.Set;
|
||||
public interface RevisionApi {
|
||||
void delete() throws RestApiException;
|
||||
|
||||
String description() throws RestApiException;
|
||||
void description(String description) throws RestApiException;
|
||||
|
||||
void review(ReviewInput in) throws RestApiException;
|
||||
|
||||
void submit() throws RestApiException;
|
||||
@@ -283,5 +286,15 @@ public interface RevisionApi {
|
||||
public MergeListRequest getMergeList() throws RestApiException {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void description(String description) throws RestApiException {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() throws RestApiException {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -64,6 +64,8 @@ public class ChangeMessagesUtil {
|
||||
"autogenerated:gerrit:revert";
|
||||
public static final String TAG_SET_ASSIGNEE =
|
||||
"autogenerated:gerrit:setAssignee";
|
||||
public static final String TAG_SET_DESCRIPTION =
|
||||
"autogenerated:gerrit:setPsDescription";
|
||||
public static final String TAG_SET_HASHTAGS =
|
||||
"autogenerated:gerrit:setHashtag";
|
||||
public static final String TAG_SET_TOPIC =
|
||||
|
@@ -47,6 +47,7 @@ import com.google.gerrit.server.change.DeleteDraftPatchSet;
|
||||
import com.google.gerrit.server.change.DraftComments;
|
||||
import com.google.gerrit.server.change.FileResource;
|
||||
import com.google.gerrit.server.change.Files;
|
||||
import com.google.gerrit.server.change.GetDescription;
|
||||
import com.google.gerrit.server.change.GetMergeList;
|
||||
import com.google.gerrit.server.change.GetPatch;
|
||||
import com.google.gerrit.server.change.GetRevisionActions;
|
||||
@@ -57,6 +58,7 @@ import com.google.gerrit.server.change.Mergeable;
|
||||
import com.google.gerrit.server.change.PostReview;
|
||||
import com.google.gerrit.server.change.PreviewSubmit;
|
||||
import com.google.gerrit.server.change.PublishDraftPatchSet;
|
||||
import com.google.gerrit.server.change.PutDescription;
|
||||
import com.google.gerrit.server.change.Rebase;
|
||||
import com.google.gerrit.server.change.RebaseUtil;
|
||||
import com.google.gerrit.server.change.Reviewed;
|
||||
@@ -118,6 +120,8 @@ class RevisionApiImpl implements RevisionApi {
|
||||
private final TestSubmitType testSubmitType;
|
||||
private final TestSubmitType.Get getSubmitType;
|
||||
private final Provider<GetMergeList> getMergeList;
|
||||
private final PutDescription putDescription;
|
||||
private final GetDescription getDescription;
|
||||
|
||||
@Inject
|
||||
RevisionApiImpl(GitRepositoryManager repoManager,
|
||||
@@ -151,6 +155,8 @@ class RevisionApiImpl implements RevisionApi {
|
||||
TestSubmitType testSubmitType,
|
||||
TestSubmitType.Get getSubmitType,
|
||||
Provider<GetMergeList> getMergeList,
|
||||
PutDescription putDescription,
|
||||
GetDescription getDescription,
|
||||
@Assisted RevisionResource r) {
|
||||
this.repoManager = repoManager;
|
||||
this.changes = changes;
|
||||
@@ -183,6 +189,8 @@ class RevisionApiImpl implements RevisionApi {
|
||||
this.testSubmitType = testSubmitType;
|
||||
this.getSubmitType = getSubmitType;
|
||||
this.getMergeList = getMergeList;
|
||||
this.putDescription = putDescription;
|
||||
this.getDescription = getDescription;
|
||||
this.revision = r;
|
||||
}
|
||||
|
||||
@@ -515,4 +523,20 @@ class RevisionApiImpl implements RevisionApi {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void description(String description) throws RestApiException {
|
||||
PutDescription.Input in = new PutDescription.Input();
|
||||
in.description = description;
|
||||
try {
|
||||
putDescription.apply(revision, in);
|
||||
} catch (UpdateException e) {
|
||||
throw new RestApiException("Cannot set description", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() throws RestApiException {
|
||||
return getDescription.apply(revision);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,27 @@
|
||||
// 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.change;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.gerrit.extensions.restapi.RestReadView;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class GetDescription implements RestReadView<RevisionResource> {
|
||||
@Override
|
||||
public String apply(RevisionResource rsrc) {
|
||||
return Strings.nullToEmpty(rsrc.getPatchSet().getDescription());
|
||||
}
|
||||
}
|
@@ -104,6 +104,8 @@ public class Module extends RestApiModule {
|
||||
get(REVISION_KIND, "preview_submit").to(PreviewSubmit.class);
|
||||
post(REVISION_KIND, "submit").to(Submit.class);
|
||||
post(REVISION_KIND, "rebase").to(Rebase.class);
|
||||
put(REVISION_KIND, "description").to(PutDescription.class);
|
||||
get(REVISION_KIND, "description").to(GetDescription.class);
|
||||
get(REVISION_KIND, "patch").to(GetPatch.class);
|
||||
get(REVISION_KIND, "submit_type").to(TestSubmitType.Get.class);
|
||||
post(REVISION_KIND, "test.submit_rule").to(TestSubmitRule.class);
|
||||
|
@@ -0,0 +1,132 @@
|
||||
// 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.change;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.gerrit.common.TimeUtil;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.DefaultInput;
|
||||
import com.google.gerrit.extensions.restapi.Response;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
import com.google.gerrit.extensions.restapi.RestModifyView;
|
||||
import com.google.gerrit.extensions.webui.UiAction;
|
||||
import com.google.gerrit.reviewdb.client.ChangeMessage;
|
||||
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.ChangeMessagesUtil;
|
||||
import com.google.gerrit.server.git.BatchUpdate;
|
||||
import com.google.gerrit.server.git.BatchUpdate.ChangeContext;
|
||||
import com.google.gerrit.server.git.UpdateException;
|
||||
import com.google.gerrit.server.notedb.ChangeUpdate;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.gerrit.server.PatchSetUtil;
|
||||
import com.google.gerrit.server.project.ChangeControl;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
@Singleton
|
||||
public class PutDescription implements RestModifyView<RevisionResource,
|
||||
PutDescription.Input>, UiAction<RevisionResource> {
|
||||
private final Provider<ReviewDb> dbProvider;
|
||||
private final ChangeMessagesUtil cmUtil;
|
||||
private final BatchUpdate.Factory batchUpdateFactory;
|
||||
private final PatchSetUtil psUtil;
|
||||
|
||||
public static class Input {
|
||||
@DefaultInput
|
||||
public String description;
|
||||
}
|
||||
|
||||
@Inject
|
||||
PutDescription(Provider<ReviewDb> dbProvider,
|
||||
ChangeMessagesUtil cmUtil,
|
||||
BatchUpdate.Factory batchUpdateFactory,
|
||||
PatchSetUtil psUtil) {
|
||||
this.dbProvider = dbProvider;
|
||||
this.cmUtil = cmUtil;
|
||||
this.batchUpdateFactory = batchUpdateFactory;
|
||||
this.psUtil = psUtil;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response<String> apply(RevisionResource rsrc, Input input)
|
||||
throws UpdateException, RestApiException {
|
||||
ChangeControl ctl = rsrc.getControl();
|
||||
if (!ctl.canEditDescription()) {
|
||||
throw new AuthException("changing description not permitted");
|
||||
}
|
||||
Op op =
|
||||
new Op(input != null ? input : new Input(), rsrc.getPatchSet().getId());
|
||||
try (BatchUpdate u = batchUpdateFactory.create(dbProvider.get(),
|
||||
rsrc.getChange().getProject(), ctl.getUser(), TimeUtil.nowTs())) {
|
||||
u.addOp(rsrc.getChange().getId(), op);
|
||||
u.execute();
|
||||
}
|
||||
return Strings.isNullOrEmpty(op.newDescription) ? Response.none()
|
||||
: Response.ok(op.newDescription);
|
||||
}
|
||||
|
||||
private class Op extends BatchUpdate.Op {
|
||||
private final Input input;
|
||||
private final PatchSet.Id psId;
|
||||
|
||||
private String oldDescription;
|
||||
private String newDescription;
|
||||
|
||||
Op(Input input, PatchSet.Id psId) {
|
||||
this.input = input;
|
||||
this.psId = psId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateChange(ChangeContext ctx) throws OrmException {
|
||||
PatchSet ps = psUtil.get(ctx.getDb(), ctx.getNotes(), psId);
|
||||
ChangeUpdate update = ctx.getUpdate(psId);
|
||||
newDescription = Strings.nullToEmpty(input.description);
|
||||
oldDescription = Strings.nullToEmpty(ps.getDescription());
|
||||
if (oldDescription.equals(newDescription)) {
|
||||
return false;
|
||||
}
|
||||
String summary;
|
||||
if (oldDescription.isEmpty()) {
|
||||
summary = "Description set to \"" + newDescription + "\"";
|
||||
} else if (newDescription.isEmpty()) {
|
||||
summary = "Description \"" + oldDescription + "\" removed";
|
||||
} else {
|
||||
summary = "Description changed to \"" + newDescription + "\"";
|
||||
}
|
||||
|
||||
ps.setDescription(newDescription);
|
||||
update.setPsDescription(newDescription);
|
||||
|
||||
ctx.getDb().patchSets().update(Collections.singleton(ps));
|
||||
|
||||
ChangeMessage cmsg =
|
||||
ChangeMessagesUtil.newMessage(ctx.getDb(), psId, ctx.getUser(),
|
||||
ctx.getWhen(), summary, ChangeMessagesUtil.TAG_SET_DESCRIPTION);
|
||||
cmUtil.addChangeMessage(ctx.getDb(), update, cmsg);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UiAction.Description getDescription(RevisionResource rsrc) {
|
||||
return new UiAction.Description().setLabel("Edit Description")
|
||||
.setVisible(rsrc.getControl().canEditDescription());
|
||||
}
|
||||
}
|
@@ -441,6 +441,18 @@ public class ChangeControl {
|
||||
return getRefControl().canForceEditTopicName();
|
||||
}
|
||||
|
||||
/** Can this user edit the description? */
|
||||
public boolean canEditDescription() {
|
||||
if (getChange().getStatus().isOpen()) {
|
||||
return isOwner() // owner (aka creator) of the change can edit desc
|
||||
|| getRefControl().isOwner() // branch owner can edit desc
|
||||
|| getProjectControl().isOwner() // project owner can edit desc
|
||||
|| getUser().getCapabilities().canAdministrateServer() // site administers are god
|
||||
;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean canEditAssignee() {
|
||||
return isOwner()
|
||||
|| getProjectControl().isOwner()
|
||||
|
Reference in New Issue
Block a user