InlineEdit: Add PUT /changes/<id>/edit/path%2fto%2ffile REST endpoint

Add put REST endpoint to put content of a file to a change edit.

Change-Id: Ib60b38e0a76b73e2d61e2267c49c475ea27447fd
This commit is contained in:
David Ostrovsky
2014-08-01 02:11:39 +02:00
parent 1a49f62750
commit a5ab829a4c
6 changed files with 247 additions and 20 deletions

View File

@@ -23,16 +23,27 @@ import com.google.gerrit.server.edit.ChangeEdit;
import com.google.gerrit.server.project.ChangeControl;
import com.google.inject.TypeLiteral;
/**
* Represents change edit resource, that is actualy two kinds of resources:
* <ul>
* <li>the change edit itself</li>
* <li>a path within the edit</li>
* </ul>
* distinguished by whether path is null or not.
*/
public class ChangeEditResource implements RestResource {
public static final TypeLiteral<RestView<ChangeEditResource>> CHANGE_EDIT_KIND =
new TypeLiteral<RestView<ChangeEditResource>>() {};
private final ChangeResource change;
private final ChangeEdit edit;
private final String path;
public ChangeEditResource(ChangeResource change, ChangeEdit edit) {
public ChangeEditResource(ChangeResource change, ChangeEdit edit,
String path) {
this.change = change;
this.edit = edit;
this.path = path;
}
// TODO(davido): Make this cacheable.
@@ -57,6 +68,10 @@ public class ChangeEditResource implements RestResource {
return edit;
}
public String getPath() {
return path;
}
Account.Id getAccountId() {
return getUser().getAccountId();
}

View File

@@ -15,36 +15,58 @@
package com.google.gerrit.server.change;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.google.common.io.ByteStreams;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.common.EditInfo;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AcceptsCreate;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ChildCollection;
import com.google.gerrit.extensions.restapi.DefaultInput;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.RawInput;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
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.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.edit.ChangeEdit;
import com.google.gerrit.server.edit.ChangeEditJson;
import com.google.gerrit.server.edit.ChangeEditModifier;
import com.google.gerrit.server.edit.ChangeEditUtil;
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.Singleton;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
@Singleton
class ChangeEdits implements
ChildCollection<ChangeResource, ChangeEditResource> {
public class ChangeEdits implements
ChildCollection<ChangeResource, ChangeEditResource>,
AcceptsCreate<ChangeResource> {
private final DynamicMap<RestView<ChangeEditResource>> views;
private final Create.Factory createFactory;
private final Detail detail;
private final ChangeEditUtil editUtil;
@Inject
ChangeEdits(DynamicMap<RestView<ChangeEditResource>> views,
Detail detail) {
Create.Factory createFactory,
Detail detail,
ChangeEditUtil editUtil) {
this.views = views;
this.createFactory = createFactory;
this.detail = detail;
this.editUtil = editUtil;
}
@Override
@@ -58,8 +80,83 @@ class ChangeEdits implements
}
@Override
public ChangeEditResource parse(ChangeResource change, IdString id) {
throw new IllegalStateException("not yet implemented");
public ChangeEditResource parse(ChangeResource rsrc, IdString id)
throws ResourceNotFoundException, AuthException, IOException,
InvalidChangeOperationException {
Optional<ChangeEdit> edit = editUtil.byChange(rsrc.getChange());
if (!edit.isPresent()) {
throw new ResourceNotFoundException(id);
}
return new ChangeEditResource(rsrc, edit.get(), id.get());
}
@SuppressWarnings("unchecked")
@Override
public Create create(ChangeResource parent, IdString id)
throws RestApiException {
return createFactory.create(parent.getChange(), id.get());
}
/**
* Create handler that is activated when collection element is accessed
* but doesn't exist, e. g. PUT request with a path was called but
* change edit wasn't created yet. Change edit is created and PUT
* handler is called.
*/
static class Create implements
RestModifyView<ChangeResource, Put.Input> {
interface Factory {
Create create(Change change, String path);
}
private final Provider<ReviewDb> db;
private final ChangeEditUtil editUtil;
private final ChangeEditModifier editModifier;
private final Put putEdit;
private final Change change;
private final String path;
@Inject
Create(Provider<ReviewDb> db,
ChangeEditUtil editUtil,
ChangeEditModifier editModifier,
Put putEdit,
@Assisted Change change,
@Assisted @Nullable String path) {
this.db = db;
this.editUtil = editUtil;
this.editModifier = editModifier;
this.putEdit = putEdit;
this.change = change;
this.path = path;
}
@Override
public Response<?> apply(ChangeResource resource, Put.Input input)
throws AuthException, IOException, ResourceConflictException,
OrmException, InvalidChangeOperationException {
Optional<ChangeEdit> edit = editUtil.byChange(change);
if (edit.isPresent()) {
throw new ResourceConflictException(String.format(
"edit already exists for the change %s",
resource.getChange().getChangeId()));
}
edit = createEdit();
if (!Strings.isNullOrEmpty(path)) {
putEdit.apply(new ChangeEditResource(resource, edit.get(), path),
input);
}
return Response.none();
}
private Optional<ChangeEdit> createEdit() throws AuthException,
IOException, ResourceConflictException, OrmException,
InvalidChangeOperationException {
editModifier.createEdit(change,
db.get().patchSets().get(change.currentPatchSetId()));
return editUtil.byChange(change);
}
}
@Singleton
@@ -85,4 +182,36 @@ class ChangeEdits implements
return Response.none();
}
}
/**
* Put handler that is activated when PUT request is called on
* collection element.
*/
@Singleton
public static class Put implements
RestModifyView<ChangeEditResource, Put.Input> {
public static class Input {
@DefaultInput
public RawInput content;
}
private final ChangeEditModifier editModifier;
@Inject
Put(ChangeEditModifier editModifier) {
this.editModifier = editModifier;
}
@Override
public Response<?> apply(ChangeEditResource rsrc, Input input)
throws AuthException, ResourceConflictException, IOException {
try {
editModifier.modifyFile(rsrc.getChangeEdit(), rsrc.getPath(),
ByteStreams.toByteArray(input.content.getInputStream()));
} catch(InvalidChangeOperationException | IOException e) {
throw new ResourceConflictException(e.getMessage());
}
return Response.none();
}
}
}

View File

@@ -14,13 +14,13 @@
package com.google.gerrit.server.change;
import static com.google.gerrit.server.change.ChangeEditResource.CHANGE_EDIT_KIND;
import static com.google.gerrit.server.change.ChangeResource.CHANGE_KIND;
import static com.google.gerrit.server.change.CommentResource.COMMENT_KIND;
import static com.google.gerrit.server.change.DraftResource.DRAFT_KIND;
import static com.google.gerrit.server.change.FileResource.FILE_KIND;
import static com.google.gerrit.server.change.ReviewerResource.REVIEWER_KIND;
import static com.google.gerrit.server.change.RevisionResource.REVISION_KIND;
import static com.google.gerrit.server.change.ChangeEditResource.CHANGE_EDIT_KIND;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.RestApiModule;
@@ -102,6 +102,7 @@ public class Module extends RestApiModule {
get(FILE_KIND, "diff").to(GetDiff.class);
child(CHANGE_KIND, "edit").to(ChangeEdits.class);
put(CHANGE_EDIT_KIND, "/").to(ChangeEdits.Put.class);
install(new FactoryModule() {
@Override
@@ -111,6 +112,7 @@ public class Module extends RestApiModule {
factory(EmailReviewComments.Factory.class);
factory(ChangeInserter.Factory.class);
factory(PatchSetInserter.Factory.class);
factory(ChangeEdits.Create.Factory.class);
}
});
}