Merge "Add support for uploading binary content through the edit rest api"
This commit is contained in:
@@ -2728,6 +2728,20 @@ Put content of a file to a change edit.
|
||||
PUT /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/edit/foo HTTP/1.0
|
||||
----
|
||||
|
||||
To upload a file as binary data in the request body:
|
||||
|
||||
.Request
|
||||
----
|
||||
PUT /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/edit/foo HTTP/1.0
|
||||
Content-Type: application/json; charset=UTF-8
|
||||
|
||||
{
|
||||
"binary_content": "data:text/plain;base64,SGVsbG8sIFdvcmxkIQ=="
|
||||
}
|
||||
----
|
||||
|
||||
Note that it must be base-64 encoded data uri.
|
||||
|
||||
When change edit doesn't exist for this change yet it is created. When file
|
||||
content isn't provided, it is wiped out for that file. As response
|
||||
"`204 No Content`" is returned.
|
||||
|
||||
@@ -20,4 +20,5 @@ import com.google.gerrit.extensions.restapi.RawInput;
|
||||
/** Content to be added to a file (new or existing) via change edit. */
|
||||
public class FileContentInput {
|
||||
@DefaultInput public RawInput content;
|
||||
public String binary_content;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.gerrit.common.RawInputUtil;
|
||||
import com.google.gerrit.entities.Change;
|
||||
import com.google.gerrit.entities.Patch;
|
||||
import com.google.gerrit.entities.PatchSet;
|
||||
@@ -35,6 +36,7 @@ import com.google.gerrit.extensions.restapi.BinaryResult;
|
||||
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;
|
||||
@@ -66,9 +68,12 @@ import com.google.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.eclipse.jgit.revwalk.RevWalk;
|
||||
import org.eclipse.jgit.util.Base64;
|
||||
import org.kohsuke.args4j.Option;
|
||||
|
||||
@Singleton
|
||||
@@ -277,6 +282,10 @@ public class ChangeEdits implements ChildCollection<ChangeResource, ChangeEditRe
|
||||
/** Put handler that is activated when PUT request is called on collection element. */
|
||||
@Singleton
|
||||
public static class Put implements RestModifyView<ChangeEditResource, FileContentInput> {
|
||||
private static final Pattern BINARY_DATA_PATTERN =
|
||||
Pattern.compile("data:([\\w/.-]*);([\\w]+),(.*)");
|
||||
private static final String BASE64 = "base64";
|
||||
|
||||
private final ChangeEditModifier editModifier;
|
||||
private final GitRepositoryManager repositoryManager;
|
||||
private final EditMessage editMessage;
|
||||
@@ -301,22 +310,36 @@ public class ChangeEdits implements ChildCollection<ChangeResource, ChangeEditRe
|
||||
public Response<Object> apply(ChangeResource rsrc, String path, FileContentInput input)
|
||||
throws AuthException, BadRequestException, ResourceConflictException, IOException,
|
||||
PermissionBackendException {
|
||||
if (input.content == null) {
|
||||
throw new BadRequestException("new content required");
|
||||
|
||||
if (input.content == null && input.binary_content == null) {
|
||||
throw new BadRequestException("either content or binary_content is required");
|
||||
}
|
||||
|
||||
if (Patch.COMMIT_MSG.equals(path)) {
|
||||
RawInput newContent;
|
||||
if (input.binary_content != null) {
|
||||
Matcher m = BINARY_DATA_PATTERN.matcher(input.binary_content);
|
||||
if (m.matches() && BASE64.equals(m.group(2))) {
|
||||
newContent = RawInputUtil.create(Base64.decode(m.group(3)));
|
||||
} else {
|
||||
throw new BadRequestException("binary_content must be encoded as base64 data uri");
|
||||
}
|
||||
} else {
|
||||
newContent = input.content;
|
||||
}
|
||||
|
||||
if (Patch.COMMIT_MSG.equals(path) && input.binary_content == null) {
|
||||
EditMessage.Input editCommitMessageInput = new EditMessage.Input();
|
||||
editCommitMessageInput.message =
|
||||
new String(ByteStreams.toByteArray(input.content.getInputStream()), UTF_8);
|
||||
new String(ByteStreams.toByteArray(newContent.getInputStream()), UTF_8);
|
||||
return editMessage.apply(rsrc, editCommitMessageInput);
|
||||
}
|
||||
|
||||
if (Strings.isNullOrEmpty(path) || path.charAt(0) == '/') {
|
||||
throw new ResourceConflictException("Invalid path: " + path);
|
||||
}
|
||||
|
||||
try (Repository repository = repositoryManager.openRepository(rsrc.getProject())) {
|
||||
editModifier.modifyFile(repository, rsrc.getNotes(), path, input.content);
|
||||
editModifier.modifyFile(repository, rsrc.getNotes(), path, newContent);
|
||||
} catch (InvalidChangeOperationException e) {
|
||||
throw new ResourceConflictException(e.getMessage());
|
||||
}
|
||||
|
||||
@@ -96,6 +96,15 @@ public class ChangeEditIT extends AbstractDaemonTest {
|
||||
private static final byte[] CONTENT_NEW = "baz".getBytes(UTF_8);
|
||||
private static final String CONTENT_NEW2_STR = "quxÄÜÖßµ";
|
||||
private static final byte[] CONTENT_NEW2 = CONTENT_NEW2_STR.getBytes(UTF_8);
|
||||
private static final String CONTENT_BINARY_ENCODED_NEW =
|
||||
"data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==";
|
||||
private static final byte[] CONTENT_BINARY_DECODED_NEW = "Hello, World!".getBytes(UTF_8);
|
||||
private static final String CONTENT_BINARY_ENCODED_NEW2 =
|
||||
"data:text/plain;base64,VXBsb2FkaW5nIHRvIGFuIGVkaXQgd29ya2VkIQ==";
|
||||
private static final byte[] CONTENT_BINARY_DECODED_NEW2 =
|
||||
"Uploading to an edit worked!".getBytes(UTF_8);
|
||||
private static final String CONTENT_BINARY_ENCODED_NEW3 =
|
||||
"data:text/plain,VXBsb2FkaW5nIHRvIGFuIGVkaXQgd29ya2VkIQ==";
|
||||
|
||||
@Inject private ProjectOperations projectOperations;
|
||||
@Inject private RequestScopeOperations requestScopeOperations;
|
||||
@@ -316,7 +325,7 @@ public class ChangeEditIT extends AbstractDaemonTest {
|
||||
assertThrows(
|
||||
BadRequestException.class,
|
||||
() -> gApi.changes().id(changeId).edit().modifyFile(Patch.COMMIT_MSG, (RawInput) null));
|
||||
assertThat(ex).hasMessageThat().isEqualTo("new content required");
|
||||
assertThat(ex).hasMessageThat().isEqualTo("either content or binary_content is required");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -559,12 +568,31 @@ public class ChangeEditIT extends AbstractDaemonTest {
|
||||
ensureSameBytes(getFileContentOfEdit(changeId, FILE_NAME), CONTENT_NEW);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createAndUploadBinaryInChangeEditOneRequestRest() throws Exception {
|
||||
FileContentInput in = new FileContentInput();
|
||||
in.binary_content = CONTENT_BINARY_ENCODED_NEW;
|
||||
adminRestSession.put(urlEditFile(changeId, FILE_NAME), in).assertNoContent();
|
||||
ensureSameBytes(getFileContentOfEdit(changeId, FILE_NAME), CONTENT_BINARY_DECODED_NEW);
|
||||
in.binary_content = CONTENT_BINARY_ENCODED_NEW2;
|
||||
adminRestSession.put(urlEditFile(changeId, FILE_NAME), in).assertNoContent();
|
||||
ensureSameBytes(getFileContentOfEdit(changeId, FILE_NAME), CONTENT_BINARY_DECODED_NEW2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidBase64UploadBinaryInChangeEditOneRequestRest() throws Exception {
|
||||
FileContentInput in = new FileContentInput();
|
||||
in.binary_content = CONTENT_BINARY_ENCODED_NEW3;
|
||||
adminRestSession.put(urlEditFile(changeId, FILE_NAME), in).assertBadRequest();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeEditNoContentProvidedRest() throws Exception {
|
||||
createEmptyEditFor(changeId);
|
||||
adminRestSession
|
||||
.put(urlEditFile(changeId, FILE_NAME), new FileContentInput())
|
||||
.assertBadRequest();
|
||||
|
||||
FileContentInput in = new FileContentInput();
|
||||
in.binary_content = null;
|
||||
adminRestSession.put(urlEditFile(changeId, FILE_NAME), in).assertBadRequest();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user