Define API to invoke the REST API from within extensions and plugins

The REST API available over HTTP is planned to be supported for a long
time in the future.  It is very well documented and has fairly clean
resource semantics.  Extension and plugin authors want to use this API
from within the server itself to access resources, shielding them from
any internal changes.

GerritApi is a simple API exposed to plugins as an interface. The
API mirrors the REST API resource tree. Posting a review on a commit
is similar:

  @Inject
  private GerritApi api;
  [...]

  ReviewInput input = new ReviewInput();
  input.message = "Looks good!";
  input.labels = new HashMap<String, Short>();
  input.labels.put("Code-Review", (short) 2);

  api.changes().id(12345).revision("c0ffee....").review(input);

In this commit we provide only the basic skeleton and a single API
call for posting review comments.

An alternative approach is to use reflection to generate a proxy
implementation of the interfaces and bind everything dynamically at
runtime similar to the way RestApiServlet is implemented.  The hand
coded implementation offered here provides some compile-time
assurances the server has each API implemented and the API accepts the
correct input type.

Change-Id: Ic25c69c0660e2796d090a4a37445820726e543d2
This commit is contained in:
Shawn Pearce
2013-08-10 15:48:53 -07:00
parent 4dbf281d67
commit db99f5415f
20 changed files with 483 additions and 87 deletions

View File

@@ -0,0 +1,21 @@
// Copyright (C) 2013 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.api;
import com.google.gerrit.extensions.api.changes.Changes;
public interface GerritApi {
public Changes changes();
}

View File

@@ -0,0 +1,22 @@
// Copyright (C) 2013 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.api.changes;
import com.google.gerrit.extensions.restapi.RestApiException;
public interface ChangeApi {
RevisionApi revision(int id) throws RestApiException;
RevisionApi revision(String id) throws RestApiException;
}

View File

@@ -0,0 +1,24 @@
// Copyright (C) 2013 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.api.changes;
import com.google.gerrit.extensions.restapi.RestApiException;
public interface Changes {
ChangeApi id(int id) throws RestApiException;
ChangeApi id(String triplet) throws RestApiException;
ChangeApi id(String project, String branch, String id)
throws RestApiException;
}

View File

@@ -0,0 +1,88 @@
// Copyright (C) 2013 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.api.changes;
import com.google.gerrit.extensions.restapi.DefaultInput;
import java.util.List;
import java.util.Map;
/** Input passed to {@code POST /changes/{id}/revisions/{id}/review}. */
public class ReviewInput {
@DefaultInput
public String message;
public Map<String, Short> labels;
public Map<String, List<Comment>> comments;
/**
* If true require all labels to be within the user's permitted ranges based
* on access controls, attempting to use a label not granted to the user
* will fail the entire modify operation early. If false the operation will
* execute anyway, but the proposed labels given by the user will be
* modified to be the "best" value allowed by the access controls, or
* ignored if the label does not exist.
*/
public boolean strictLabels = true;
/**
* How to process draft comments already in the database that were not also
* described in this input request.
*/
public DraftHandling drafts = DraftHandling.DELETE;
/** Who to send email notifications to after review is stored. */
public NotifyHandling notify = NotifyHandling.ALL;
/**
* Account ID, name, email address or username of another user. The review
* will be posted/updated on behalf of this named user instead of the
* caller. Caller must have the labelAs-$NAME permission granted for each
* label that appears in {@link #labels}. This is in addition to the named
* user also needing to have permission to use the labels.
* <p>
* {@link #strictLabels} impacts how labels is processed for the named user,
* not the caller.
*/
public String onBehalfOf;
public static enum DraftHandling {
DELETE, PUBLISH, KEEP;
}
public static enum NotifyHandling {
NONE, OWNER, OWNER_REVIEWERS, ALL;
}
public static enum Side {
PARENT, REVISION;
}
public static class Comment {
public String id;
public Side side;
public int line;
public String inReplyTo;
public String message;
public Range range;
public static class Range {
public int startLine;
public int startCharacter;
public int endLine;
public int endCharacter;
}
}
}

View File

@@ -0,0 +1,21 @@
// Copyright (C) 2013 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.api.changes;
import com.google.gerrit.extensions.restapi.RestApiException;
public interface RevisionApi {
void review(ReviewInput in) throws RestApiException;
}

View File

@@ -15,7 +15,7 @@
package com.google.gerrit.extensions.restapi;
/** Root exception type for JSON API failures. */
public abstract class RestApiException extends Exception {
public class RestApiException extends Exception {
private static final long serialVersionUID = 1L;
private CacheControl caching = CacheControl.NONE;

View File

@@ -0,0 +1,34 @@
// Copyright (C) 2013 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.api;
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.api.changes.Changes;
import com.google.inject.Inject;
import com.google.inject.Provider;
class GerritApiImpl implements GerritApi {
private final Provider<Changes> changes;
@Inject
GerritApiImpl(Provider<Changes> changes) {
this.changes = changes;
}
@Override
public Changes changes() {
return changes.get();
}
}

View File

@@ -0,0 +1,27 @@
// Copyright (C) 2013 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.api;
import com.google.gerrit.extensions.api.GerritApi;
import com.google.inject.AbstractModule;
public class Module extends AbstractModule {
@Override
protected void configure() {
bind(GerritApi.class).to(GerritApiImpl.class);
install(new com.google.gerrit.server.api.changes.Module());
}
}

View File

@@ -0,0 +1,59 @@
// Copyright (C) 2013 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.api.changes;
import com.google.gerrit.extensions.api.changes.ChangeApi;
import com.google.gerrit.extensions.api.changes.RevisionApi;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.Revisions;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
class ChangeApiImpl implements ChangeApi {
interface Factory {
ChangeApiImpl create(ChangeResource change);
}
private final Revisions revisions;
private final RevisionApiImpl.Factory revisionApi;
private final ChangeResource change;
@Inject
ChangeApiImpl(Revisions revisions,
RevisionApiImpl.Factory api,
@Assisted ChangeResource change) {
this.revisions = revisions;
this.revisionApi = api;
this.change = change;
}
@Override
public RevisionApi revision(int id) throws RestApiException {
return revision(String.valueOf(id));
}
@Override
public RevisionApi revision(String id) throws RestApiException {
try {
return revisionApi.create(
revisions.parse(change, IdString.fromDecoded(id)));
} catch (OrmException e) {
throw new RestApiException("Cannot parse revision", e);
}
}
}

View File

@@ -0,0 +1,63 @@
// Copyright (C) 2013 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.api.changes;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.extensions.api.changes.ChangeApi;
import com.google.gerrit.extensions.api.changes.Changes;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.extensions.restapi.Url;
import com.google.gerrit.server.change.ChangesCollection;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
class ChangesImpl implements Changes {
private final ChangesCollection changes;
private final ChangeApiImpl.Factory api;
@Inject
ChangesImpl(ChangesCollection changes, ChangeApiImpl.Factory api) {
this.changes = changes;
this.api = api;
}
@Override
public ChangeApi id(int id) throws RestApiException {
return id(String.valueOf(id));
}
@Override
public ChangeApi id(String project, String branch, String id)
throws RestApiException {
return id(Joiner.on('~').join(ImmutableList.of(
Url.encode(project),
Url.encode(branch),
Url.encode(id))));
}
@Override
public ChangeApi id(String id) throws RestApiException {
try {
return api.create(changes.parse(
TopLevelResource.INSTANCE,
IdString.fromUrl(id)));
} catch (OrmException e) {
throw new RestApiException("Cannot parse change", e);
}
}
}

View File

@@ -0,0 +1,28 @@
// Copyright (C) 2013 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.api.changes;
import com.google.gerrit.extensions.api.changes.Changes;
import com.google.gerrit.server.config.FactoryModule;
public class Module extends FactoryModule {
@Override
protected void configure() {
bind(Changes.class).to(ChangesImpl.class);
factory(ChangeApiImpl.Factory.class);
factory(RevisionApiImpl.Factory.class);
}
}

View File

@@ -0,0 +1,54 @@
// Copyright (C) 2013 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.api.changes;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.changes.RevisionApi;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.server.change.PostReview;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
class RevisionApiImpl implements RevisionApi {
interface Factory {
RevisionApiImpl create(RevisionResource r);
}
private final Provider<PostReview> review;
private final RevisionResource revision;
@Inject
RevisionApiImpl(Provider<PostReview> review,
@Assisted RevisionResource r) {
this.review = review;
this.revision = r;
}
@Override
public void review(ReviewInput in) throws RestApiException {
try {
review.get().apply(revision, in);
} catch (OrmException e) {
throw new RestApiException("Cannot post review", e);
} catch (IOException e) {
throw new RestApiException("Cannot post review", e);
}
}
}

View File

@@ -31,7 +31,6 @@ import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.UnsupportedEncodingException;
import java.util.Collections;
import java.util.List;
@@ -69,8 +68,7 @@ public class ChangesCollection implements
@Override
public ChangeResource parse(TopLevelResource root, IdString id)
throws ResourceNotFoundException, OrmException,
UnsupportedEncodingException {
throws ResourceNotFoundException, OrmException {
List<Change> changes = findChanges(id.encoded());
if (changes.size() != 1) {
throw new ResourceNotFoundException(id);

View File

@@ -15,6 +15,7 @@
package com.google.gerrit.server.change;
import com.google.common.collect.Lists;
import com.google.gerrit.extensions.api.changes.ReviewInput.NotifyHandling;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.ChangeMessage;
@@ -22,7 +23,6 @@ import com.google.gerrit.reviewdb.client.PatchLineComment;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.change.PostReview.NotifyHandling;
import com.google.gerrit.server.git.WorkQueue;
import com.google.gerrit.server.mail.CommentSender;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
@@ -61,7 +61,7 @@ class EmailReviewComments implements Runnable, RequestContext {
private final SchemaFactory<ReviewDb> schemaFactory;
private final ThreadLocalRequestContext requestContext;
private final PostReview.NotifyHandling notify;
private final NotifyHandling notify;
private final Change change;
private final PatchSet patchSet;
private final Account.Id authorId;

View File

@@ -23,14 +23,17 @@ import com.google.common.collect.Maps;
import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.Futures;
import com.google.gerrit.common.ChangeHooks;
import com.google.gerrit.common.changes.Side;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelTypes;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRange;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.changes.ReviewInput.Comment;
import com.google.gerrit.extensions.api.changes.ReviewInput.DraftHandling;
import com.google.gerrit.extensions.api.changes.ReviewInput.NotifyHandling;
import com.google.gerrit.extensions.api.changes.ReviewInput.Side;
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.RestModifyView;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.extensions.restapi.Url;
@@ -44,7 +47,6 @@ import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountsCollection;
import com.google.gerrit.server.change.PostReview.Input;
import com.google.gerrit.server.index.ChangeIndexer;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.util.TimeUtil;
@@ -62,67 +64,11 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class PostReview implements RestModifyView<RevisionResource, Input> {
public class PostReview implements RestModifyView<RevisionResource, ReviewInput> {
private static final Logger log = LoggerFactory.getLogger(PostReview.class);
public static class Input {
@DefaultInput
public String message;
public Map<String, Short> labels;
public Map<String, List<Comment>> comments;
/**
* If true require all labels to be within the user's permitted ranges based
* on access controls, attempting to use a label not granted to the user
* will fail the entire modify operation early. If false the operation will
* execute anyway, but the proposed labels given by the user will be
* modified to be the "best" value allowed by the access controls, or
* ignored if the label does not exist.
*/
public boolean strictLabels = true;
/**
* How to process draft comments already in the database that were not also
* described in this input request.
*/
public DraftHandling drafts = DraftHandling.DELETE;
/** Who to send email notifications to after review is stored. */
public NotifyHandling notify = NotifyHandling.ALL;
/**
* Account ID, name, email address or username of another user. The review
* will be posted/updated on behalf of this named user instead of the
* caller. Caller must have the labelAs-$NAME permission granted for each
* label that appears in {@link #labels}. This is in addition to the named
* user also needing to have permission to use the labels.
* <p>
* {@link #strictLabels} impacts how labels is processed for the named user,
* not the caller.
*/
public String onBehalfOf;
}
public static enum DraftHandling {
DELETE, PUBLISH, KEEP;
}
public static enum NotifyHandling {
NONE, OWNER, OWNER_REVIEWERS, ALL;
}
public static class Comment {
public String id;
public Side side;
public int line;
public String inReplyTo;
public String message;
public CommentRange range;
}
public static class Output {
public Map<String, Short> labels;
static class Output {
Map<String, Short> labels;
}
private final Provider<ReviewDb> db;
@@ -152,7 +98,7 @@ public class PostReview implements RestModifyView<RevisionResource, Input> {
}
@Override
public Object apply(RevisionResource revision, Input input)
public Object apply(RevisionResource revision, ReviewInput input)
throws AuthException, BadRequestException, OrmException,
UnprocessableEntityException, IOException {
if (input.onBehalfOf != null) {
@@ -210,7 +156,7 @@ public class PostReview implements RestModifyView<RevisionResource, Input> {
return output;
}
private RevisionResource onBehalfOf(RevisionResource rev, Input in)
private RevisionResource onBehalfOf(RevisionResource rev, ReviewInput in)
throws BadRequestException, AuthException, UnprocessableEntityException,
OrmException {
if (in.labels == null || in.labels.isEmpty()) {
@@ -370,7 +316,13 @@ public class PostReview implements RestModifyView<RevisionResource, Input> {
e.setWrittenOn(timestamp);
e.setSide(c.side == Side.PARENT ? (short) 0 : (short) 1);
e.setMessage(c.message);
e.setRange(c.range);
if (c.range != null) {
e.setRange(new CommentRange(
c.range.startLine,
c.range.startCharacter,
c.range.endLine,
c.range.endCharacter));
}
(create ? ins : upd).add(e);
}
}

View File

@@ -226,6 +226,7 @@ public class GerritGlobalModule extends FactoryModule {
install(new AuditModule());
install(new com.google.gerrit.server.access.Module());
install(new com.google.gerrit.server.account.Module());
install(new com.google.gerrit.server.api.Module());
install(new com.google.gerrit.server.change.Module());
install(new com.google.gerrit.server.config.Module());
install(new com.google.gerrit.server.group.Module());

View File

@@ -17,13 +17,13 @@ package com.google.gerrit.server.mail;
import com.google.common.base.Strings;
import com.google.common.collect.Ordering;
import com.google.gerrit.common.errors.EmailException;
import com.google.gerrit.extensions.api.changes.ReviewInput.NotifyHandling;
import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.CommentRange;
import com.google.gerrit.reviewdb.client.Patch;
import com.google.gerrit.reviewdb.client.PatchLineComment;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.change.PostReview.NotifyHandling;
import com.google.gerrit.server.patch.PatchFile;
import com.google.gerrit.server.patch.PatchList;
import com.google.gerrit.server.patch.PatchListNotAvailableException;

View File

@@ -18,10 +18,10 @@ import static org.junit.Assert.assertTrue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.change.ChangeInserter;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.PostReview;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.project.ChangeControl;
@@ -70,13 +70,13 @@ public abstract class AbstractIndexQueryChangesTest
Change change = ins.insert();
ChangeControl ctl = changeControlFactory.controlFor(change, user);
PostReview.Input input = new PostReview.Input();
ReviewInput input = new ReviewInput();
input.message = "toplevel";
PostReview.Comment comment = new PostReview.Comment();
ReviewInput.Comment comment = new ReviewInput.Comment();
comment.line = 1;
comment.message = "inline";
input.comments = ImmutableMap.<String, List<PostReview.Comment>> of(
"Foo.java", ImmutableList.<PostReview.Comment> of(comment));
input.comments = ImmutableMap.<String, List<ReviewInput.Comment>> of(
"Foo.java", ImmutableList.<ReviewInput.Comment> of(comment));
postReview.apply(new RevisionResource(
new ChangeResource(ctl), ins.getPatchSet()), input);

View File

@@ -26,6 +26,7 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.hash.Hashing;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.lifecycle.LifecycleManager;
import com.google.gerrit.reviewdb.client.Account;
@@ -359,7 +360,7 @@ public abstract class AbstractQueryChangesTest {
Change change = ins.insert();
ChangeControl ctl = changeControlFactory.controlFor(change, user);
PostReview.Input input = new PostReview.Input();
ReviewInput input = new ReviewInput();
input.message = "toplevel";
input.labels = ImmutableMap.<String, Short> of("Code-Review", (short) 1);
postReview.apply(new RevisionResource(
@@ -476,7 +477,7 @@ public abstract class AbstractQueryChangesTest {
assertResultEquals(change2, results.get(0));
assertResultEquals(change1, results.get(1));
PostReview.Input input = new PostReview.Input();
ReviewInput input = new ReviewInput();
input.message = "toplevel";
postReview.apply(new RevisionResource(
new ChangeResource(ctl1), ins1.getPatchSet()), input);
@@ -509,7 +510,7 @@ public abstract class AbstractQueryChangesTest {
assertResultEquals(change2, results.get(0));
assertResultEquals(change1, results.get(1));
PostReview.Input input = new PostReview.Input();
ReviewInput input = new ReviewInput();
input.message = "toplevel";
postReview.apply(new RevisionResource(
new ChangeResource(ctl1), ins1.getPatchSet()), input);

View File

@@ -22,6 +22,8 @@ import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelValue;
import com.google.gerrit.common.data.ReviewResult;
import com.google.gerrit.common.data.ReviewResult.Error.Type;
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -32,7 +34,6 @@ import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.change.Abandon;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.DeleteDraftPatchSet;
import com.google.gerrit.server.change.PostReview;
import com.google.gerrit.server.change.Restore;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.change.Submit;
@@ -146,7 +147,7 @@ public class ReviewCommand extends SshCommand {
private Provider<Abandon> abandonProvider;
@Inject
private Provider<PostReview> reviewProvider;
private Provider<GerritApi> api;
@Inject
private PublishDraft.Factory publishDraftFactory;
@@ -213,9 +214,11 @@ public class ReviewCommand extends SshCommand {
}
private void applyReview(final ChangeControl ctl, final PatchSet patchSet,
final PostReview.Input review) throws Exception {
reviewProvider.get().apply(new RevisionResource(
new ChangeResource(ctl), patchSet), review);
final ReviewInput review) throws Exception {
api.get().changes()
.id(ctl.getChange().getChangeId())
.revision(patchSet.getRevision().get())
.review(review);
}
private void approveOne(final PatchSet patchSet) throws Exception {
@@ -224,10 +227,10 @@ public class ReviewCommand extends SshCommand {
changeComment = "";
}
PostReview.Input review = new PostReview.Input();
ReviewInput review = new ReviewInput();
review.message = Strings.emptyToNull(changeComment);
review.labels = Maps.newTreeMap();
review.drafts = PostReview.DraftHandling.PUBLISH;
review.drafts = ReviewInput.DraftHandling.PUBLISH;
review.strictLabels = false;
for (ApproveOption ao : optionList) {
Short v = ao.value();