Merge topic 'inline-3'
* changes: InlineEdit: Add POST /changes/<id>/edit REST endpoint InlineEdit: Add PUT /changes/<id>/edit/path%2fto%2ffile REST endpoint InlineEdit: Add GET /changes/<id>/edit REST endpoint
This commit is contained in:
@@ -1139,6 +1139,113 @@ Administrate Server] capability.
|
|||||||
HTTP/1.1 204 No Content
|
HTTP/1.1 204 No Content
|
||||||
----
|
----
|
||||||
|
|
||||||
|
[[edit-endpoints]]
|
||||||
|
== Change Edit Endpoints
|
||||||
|
|
||||||
|
These endpoints are considered to be unstable and can be changed in
|
||||||
|
backwards incompatible way any time without notice.
|
||||||
|
|
||||||
|
[[get-edit-detail]]
|
||||||
|
=== Get Change Edit Details
|
||||||
|
--
|
||||||
|
'GET /changes/link:#change-id[\{change-id\}]/edit
|
||||||
|
--
|
||||||
|
|
||||||
|
Retrieves a change edit details.
|
||||||
|
|
||||||
|
.Request
|
||||||
|
----
|
||||||
|
GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/edit HTTP/1.0
|
||||||
|
----
|
||||||
|
|
||||||
|
As response an link:#edit-info[EditInfo] entity is returned that
|
||||||
|
describes the change edit, or "`204 No Content`" when change edit doesn't
|
||||||
|
exist for this change. Change edits are stored on special branches and there
|
||||||
|
can be max one edit per user per change. Edits aren't tracked in the database.
|
||||||
|
|
||||||
|
.Response
|
||||||
|
----
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Content-Disposition: attachment
|
||||||
|
Content-Type: application/json;charset=UTF-8
|
||||||
|
|
||||||
|
)]}'
|
||||||
|
{
|
||||||
|
"commit":{
|
||||||
|
"parents":[
|
||||||
|
{
|
||||||
|
"commit":"1eee2c9d8f352483781e772f35dc586a69ff5646",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"author":{
|
||||||
|
"name":"Shawn O. Pearce",
|
||||||
|
"email":"sop@google.com",
|
||||||
|
"date":"2012-04-24 18:08:08.000000000",
|
||||||
|
"tz":-420
|
||||||
|
},
|
||||||
|
"committer":{
|
||||||
|
"name":"Shawn O. Pearce",
|
||||||
|
"email":"sop@google.com",
|
||||||
|
"date":"2012-04-24 18:08:08.000000000",
|
||||||
|
"tz":-420
|
||||||
|
},
|
||||||
|
"subject":"Use an EventBus to manage star icons",
|
||||||
|
"message":"Use an EventBus to manage star icons\n\nImage widgets that need to ..."
|
||||||
|
},
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
[[put-edit-file]]
|
||||||
|
=== Change file content in Change Edit
|
||||||
|
--
|
||||||
|
'PUT /changes/link:#change-id[\{change-id\}]/edit/path%2fto%2ffile
|
||||||
|
--
|
||||||
|
|
||||||
|
Put content of a file to a change edit.
|
||||||
|
|
||||||
|
.Request
|
||||||
|
----
|
||||||
|
PUT /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/edit/foo HTTP/1.0
|
||||||
|
----
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
.Response
|
||||||
|
----
|
||||||
|
HTTP/1.1 204 No Content
|
||||||
|
----
|
||||||
|
|
||||||
|
[[post-edit]]
|
||||||
|
=== Restore file content in Change Edit
|
||||||
|
--
|
||||||
|
'POST /changes/link:#change-id[\{change-id\}]/edit
|
||||||
|
--
|
||||||
|
|
||||||
|
Creates empty change edit or restores file content in change edit. The
|
||||||
|
request body needs to include a link:#change-edit-input[ChangeEditInput]
|
||||||
|
entity when a file within change edit should be restored.
|
||||||
|
|
||||||
|
.Request
|
||||||
|
----
|
||||||
|
POST /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/edit HTTP/1.0
|
||||||
|
Content-Type: application/json;charset=UTF-8
|
||||||
|
|
||||||
|
{
|
||||||
|
"path": "foo",
|
||||||
|
"restore": true
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
When change edit doesn't exist for this change yet it is created. When path
|
||||||
|
and restore flag are provided in request body, this file is restored. As
|
||||||
|
response "`204 No Content`" is returned.
|
||||||
|
|
||||||
|
.Response
|
||||||
|
----
|
||||||
|
HTTP/1.1 204 No Content
|
||||||
|
----
|
||||||
|
|
||||||
[[reviewer-endpoints]]
|
[[reviewer-endpoints]]
|
||||||
== Reviewer Endpoints
|
== Reviewer Endpoints
|
||||||
@@ -3589,6 +3696,28 @@ The `WebLinkInfo` entity describes a link to an external site.
|
|||||||
|`url` |The link URL.
|
|`url` |The link URL.
|
||||||
|======================
|
|======================
|
||||||
|
|
||||||
|
[[edit-info]]
|
||||||
|
=== EditInfo
|
||||||
|
The `EditInfo` entity contains information about a change edit.
|
||||||
|
|
||||||
|
[options="header",width="50%",cols="1,^1,5"]
|
||||||
|
|===========================
|
||||||
|
|Field Name ||Description
|
||||||
|
|`commit` ||The commit of change edit as
|
||||||
|
link:#commit-info[CommitInfo] entity.
|
||||||
|
|===========================
|
||||||
|
|
||||||
|
[[restore-path-input]]
|
||||||
|
=== RestorePathInput
|
||||||
|
The `RestorePathInput` entity contains information for restoring a
|
||||||
|
path within change edit.
|
||||||
|
|
||||||
|
[options="header",width="50%",cols="1,^1,5"]
|
||||||
|
|===========================
|
||||||
|
|Field Name ||Description
|
||||||
|
|`restore_path`|optional|Path to file to restore.
|
||||||
|
|===========================
|
||||||
|
|
||||||
GERRIT
|
GERRIT
|
||||||
------
|
------
|
||||||
Part of link:index.html[Gerrit Code Review]
|
Part of link:index.html[Gerrit Code Review]
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import org.apache.http.message.BasicHeader;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
public class RestSession extends HttpSession {
|
public class RestSession extends HttpSession {
|
||||||
|
|
||||||
@@ -91,12 +92,14 @@ public class RestSession extends HttpSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static RawInput newRawInput(final String content) throws IOException {
|
public static RawInput newRawInput(String content) throws IOException {
|
||||||
Preconditions.checkNotNull(content);
|
return newRawInput(content.getBytes(StandardCharsets.UTF_8));
|
||||||
Preconditions.checkArgument(!content.isEmpty());
|
}
|
||||||
return new RawInput() {
|
|
||||||
byte bytes[] = content.getBytes("UTF-8");
|
|
||||||
|
|
||||||
|
public static RawInput newRawInput(final byte[] bytes) throws IOException {
|
||||||
|
Preconditions.checkNotNull(bytes);
|
||||||
|
Preconditions.checkArgument(bytes.length > 0);
|
||||||
|
return new RawInput() {
|
||||||
@Override
|
@Override
|
||||||
public InputStream getInputStream() throws IOException {
|
public InputStream getInputStream() throws IOException {
|
||||||
return new ByteArrayInputStream(bytes);
|
return new ByteArrayInputStream(bytes);
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ package com.google.gerrit.acceptance.edit;
|
|||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||||
|
import static org.apache.http.HttpStatus.SC_NO_CONTENT;
|
||||||
|
import static org.apache.http.HttpStatus.SC_OK;
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
@@ -27,15 +29,18 @@ import com.google.common.base.Optional;
|
|||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.gerrit.acceptance.AbstractDaemonTest;
|
import com.google.gerrit.acceptance.AbstractDaemonTest;
|
||||||
import com.google.gerrit.acceptance.AcceptanceTestRequestScope;
|
import com.google.gerrit.acceptance.AcceptanceTestRequestScope;
|
||||||
import com.google.gerrit.acceptance.NoHttpd;
|
|
||||||
import com.google.gerrit.acceptance.PushOneCommit;
|
import com.google.gerrit.acceptance.PushOneCommit;
|
||||||
|
import com.google.gerrit.acceptance.RestResponse;
|
||||||
import com.google.gerrit.acceptance.RestSession;
|
import com.google.gerrit.acceptance.RestSession;
|
||||||
|
import com.google.gerrit.extensions.common.EditInfo;
|
||||||
import com.google.gerrit.extensions.restapi.BinaryResult;
|
import com.google.gerrit.extensions.restapi.BinaryResult;
|
||||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||||
import com.google.gerrit.reviewdb.client.Change;
|
import com.google.gerrit.reviewdb.client.Change;
|
||||||
import com.google.gerrit.reviewdb.client.PatchSet;
|
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||||
import com.google.gerrit.server.IdentifiedUser;
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
|
import com.google.gerrit.server.change.ChangeEdits.Put;
|
||||||
|
import com.google.gerrit.server.change.ChangeEdits.Post;
|
||||||
import com.google.gerrit.server.change.FileContentUtil;
|
import com.google.gerrit.server.change.FileContentUtil;
|
||||||
import com.google.gerrit.server.edit.ChangeEdit;
|
import com.google.gerrit.server.edit.ChangeEdit;
|
||||||
import com.google.gerrit.server.edit.ChangeEditModifier;
|
import com.google.gerrit.server.edit.ChangeEditModifier;
|
||||||
@@ -56,10 +61,10 @@ import org.junit.Before;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
@NoHttpd
|
|
||||||
public class ChangeEditIT extends AbstractDaemonTest {
|
public class ChangeEditIT extends AbstractDaemonTest {
|
||||||
|
|
||||||
private final static String FILE_NAME = "foo";
|
private final static String FILE_NAME = "foo";
|
||||||
@@ -211,6 +216,32 @@ public class ChangeEditIT extends AbstractDaemonTest {
|
|||||||
assertFalse(edit.isPresent());
|
assertFalse(edit.isPresent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void retrieveEdit() throws Exception {
|
||||||
|
RestResponse r = session.get(urlEdit());
|
||||||
|
assertEquals(SC_NO_CONTENT, r.getStatusCode());
|
||||||
|
assertEquals(RefUpdate.Result.NEW,
|
||||||
|
modifier.createEdit(
|
||||||
|
change,
|
||||||
|
ps));
|
||||||
|
Optional<ChangeEdit> edit = editUtil.byChange(change);
|
||||||
|
assertEquals(RefUpdate.Result.FORCED,
|
||||||
|
modifier.modifyFile(
|
||||||
|
edit.get(),
|
||||||
|
FILE_NAME,
|
||||||
|
CONTENT_NEW));
|
||||||
|
edit = editUtil.byChange(change);
|
||||||
|
EditInfo info = toEditInfo();
|
||||||
|
assertEquals(edit.get().getRevision().get(), info.commit.commit);
|
||||||
|
assertEquals(1, info.commit.parents.size());
|
||||||
|
|
||||||
|
edit = editUtil.byChange(change);
|
||||||
|
editUtil.delete(edit.get());
|
||||||
|
|
||||||
|
r = session.get(urlEdit());
|
||||||
|
assertEquals(SC_NO_CONTENT, r.getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void deleteExistingFile() throws Exception {
|
public void deleteExistingFile() throws Exception {
|
||||||
assertEquals(RefUpdate.Result.NEW,
|
assertEquals(RefUpdate.Result.NEW,
|
||||||
@@ -285,6 +316,18 @@ public class ChangeEditIT extends AbstractDaemonTest {
|
|||||||
edit.get().getRevision().get(), FILE_NAME)));
|
edit.get().getRevision().get(), FILE_NAME)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void restoreDeletedFileInPatchSetRest() throws Exception {
|
||||||
|
Post.Input in = new Post.Input();
|
||||||
|
in.restorePath = FILE_NAME;
|
||||||
|
assertEquals(SC_NO_CONTENT, session.post(urlEdit2(),
|
||||||
|
in).getStatusCode());
|
||||||
|
Optional<ChangeEdit> edit = editUtil.byChange(change2);
|
||||||
|
assertArrayEquals(CONTENT_OLD,
|
||||||
|
toBytes(fileUtil.getContent(edit.get().getChange().getProject(),
|
||||||
|
edit.get().getRevision().get(), FILE_NAME)));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void amendExistingFile() throws Exception {
|
public void amendExistingFile() throws Exception {
|
||||||
assertEquals(RefUpdate.Result.NEW,
|
assertEquals(RefUpdate.Result.NEW,
|
||||||
@@ -312,6 +355,63 @@ public class ChangeEditIT extends AbstractDaemonTest {
|
|||||||
edit.get().getRevision().get(), FILE_NAME)));
|
edit.get().getRevision().get(), FILE_NAME)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createAndChangeEditInOneRequestRest() throws Exception {
|
||||||
|
Put.Input in = new Put.Input();
|
||||||
|
in.content = RestSession.newRawInput(CONTENT_NEW);
|
||||||
|
assertEquals(SC_NO_CONTENT, session.putRaw(urlEditFile(),
|
||||||
|
in.content).getStatusCode());
|
||||||
|
Optional<ChangeEdit> edit = editUtil.byChange(change);
|
||||||
|
assertArrayEquals(CONTENT_NEW,
|
||||||
|
toBytes(fileUtil.getContent(edit.get().getChange().getProject(),
|
||||||
|
edit.get().getRevision().get(), FILE_NAME)));
|
||||||
|
in.content = RestSession.newRawInput(CONTENT_NEW2);
|
||||||
|
assertEquals(SC_NO_CONTENT, session.putRaw(urlEditFile(),
|
||||||
|
in.content).getStatusCode());
|
||||||
|
edit = editUtil.byChange(change);
|
||||||
|
assertArrayEquals(CONTENT_NEW2,
|
||||||
|
toBytes(fileUtil.getContent(edit.get().getChange().getProject(),
|
||||||
|
edit.get().getRevision().get(), FILE_NAME)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void changeEditRest() throws Exception {
|
||||||
|
assertEquals(RefUpdate.Result.NEW,
|
||||||
|
modifier.createEdit(
|
||||||
|
change,
|
||||||
|
ps));
|
||||||
|
Put.Input in = new Put.Input();
|
||||||
|
in.content = RestSession.newRawInput(CONTENT_NEW);
|
||||||
|
assertEquals(SC_NO_CONTENT, session.putRaw(urlEditFile(),
|
||||||
|
in.content).getStatusCode());
|
||||||
|
Optional<ChangeEdit> edit = editUtil.byChange(change);
|
||||||
|
assertArrayEquals(CONTENT_NEW,
|
||||||
|
toBytes(fileUtil.getContent(edit.get().getChange().getProject(),
|
||||||
|
edit.get().getRevision().get(), FILE_NAME)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void emptyPutRequest() throws Exception {
|
||||||
|
assertEquals(RefUpdate.Result.NEW,
|
||||||
|
modifier.createEdit(
|
||||||
|
change,
|
||||||
|
ps));
|
||||||
|
assertEquals(SC_NO_CONTENT, session.put(urlEditFile()).getStatusCode());
|
||||||
|
Optional<ChangeEdit> edit = editUtil.byChange(change);
|
||||||
|
assertArrayEquals("".getBytes(),
|
||||||
|
toBytes(fileUtil.getContent(edit.get().getChange().getProject(),
|
||||||
|
edit.get().getRevision().get(), FILE_NAME)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createEmptyEditRest() throws Exception {
|
||||||
|
assertEquals(SC_NO_CONTENT, session.post(urlEdit()).getStatusCode());
|
||||||
|
Optional<ChangeEdit> edit = editUtil.byChange(change);
|
||||||
|
assertArrayEquals(CONTENT_OLD,
|
||||||
|
toBytes(fileUtil.getContent(edit.get().getChange().getProject(),
|
||||||
|
edit.get().getRevision().get(), FILE_NAME)));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void addNewFile() throws Exception {
|
public void addNewFile() throws Exception {
|
||||||
assertEquals(RefUpdate.Result.NEW,
|
assertEquals(RefUpdate.Result.NEW,
|
||||||
@@ -410,4 +510,28 @@ public class ChangeEditIT extends AbstractDaemonTest {
|
|||||||
content.writeTo(os);
|
content.writeTo(os);
|
||||||
return os.toByteArray();
|
return os.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String urlEdit() {
|
||||||
|
return "/changes/"
|
||||||
|
+ change.getChangeId()
|
||||||
|
+ "/edit";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String urlEdit2() {
|
||||||
|
return "/changes/"
|
||||||
|
+ change2.getChangeId()
|
||||||
|
+ "/edit/";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String urlEditFile() {
|
||||||
|
return urlEdit()
|
||||||
|
+ "/"
|
||||||
|
+ FILE_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
private EditInfo toEditInfo() throws IOException {
|
||||||
|
RestResponse r = session.get(urlEdit());
|
||||||
|
assertEquals(SC_OK, r.getStatusCode());
|
||||||
|
return newGson().fromJson(r.getReader(), EditInfo.class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
// Copyright (C) 2014 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.common;
|
||||||
|
|
||||||
|
public class EditInfo {
|
||||||
|
public CommitInfo commit;
|
||||||
|
}
|
||||||
@@ -204,6 +204,14 @@ public class ChangeInfo extends JavaScriptObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class EditInfo extends JavaScriptObject {
|
||||||
|
public final native String name() /*-{ return this.name; }-*/;
|
||||||
|
public final native CommitInfo commit() /*-{ return this.commit; }-*/;
|
||||||
|
|
||||||
|
protected EditInfo() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class RevisionInfo extends JavaScriptObject {
|
public static class RevisionInfo extends JavaScriptObject {
|
||||||
public final native int _number() /*-{ return this._number; }-*/;
|
public final native int _number() /*-{ return this._number; }-*/;
|
||||||
public final native String name() /*-{ return this.name; }-*/;
|
public final native String name() /*-{ return this.name; }-*/;
|
||||||
|
|||||||
@@ -23,16 +23,27 @@ import com.google.gerrit.server.edit.ChangeEdit;
|
|||||||
import com.google.gerrit.server.project.ChangeControl;
|
import com.google.gerrit.server.project.ChangeControl;
|
||||||
import com.google.inject.TypeLiteral;
|
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 class ChangeEditResource implements RestResource {
|
||||||
public static final TypeLiteral<RestView<ChangeEditResource>> CHANGE_EDIT_KIND =
|
public static final TypeLiteral<RestView<ChangeEditResource>> CHANGE_EDIT_KIND =
|
||||||
new TypeLiteral<RestView<ChangeEditResource>>() {};
|
new TypeLiteral<RestView<ChangeEditResource>>() {};
|
||||||
|
|
||||||
private final ChangeResource change;
|
private final ChangeResource change;
|
||||||
private final ChangeEdit edit;
|
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.change = change;
|
||||||
this.edit = edit;
|
this.edit = edit;
|
||||||
|
this.path = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(davido): Make this cacheable.
|
// TODO(davido): Make this cacheable.
|
||||||
@@ -57,6 +68,10 @@ public class ChangeEditResource implements RestResource {
|
|||||||
return edit;
|
return edit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
Account.Id getAccountId() {
|
Account.Id getAccountId() {
|
||||||
return getUser().getAccountId();
|
return getUser().getAccountId();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,21 +14,65 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server.change;
|
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.registration.DynamicMap;
|
||||||
|
import com.google.gerrit.extensions.restapi.AcceptsCreate;
|
||||||
|
import com.google.gerrit.extensions.restapi.AcceptsPost;
|
||||||
|
import com.google.gerrit.extensions.restapi.AuthException;
|
||||||
|
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||||
import com.google.gerrit.extensions.restapi.ChildCollection;
|
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.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.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.Inject;
|
||||||
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
|
import com.google.inject.assistedinject.Assisted;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class ChangeEdits implements
|
public class ChangeEdits implements
|
||||||
ChildCollection<ChangeResource, ChangeEditResource> {
|
ChildCollection<ChangeResource, ChangeEditResource>,
|
||||||
|
AcceptsCreate<ChangeResource>,
|
||||||
|
AcceptsPost<ChangeResource> {
|
||||||
private final DynamicMap<RestView<ChangeEditResource>> views;
|
private final DynamicMap<RestView<ChangeEditResource>> views;
|
||||||
|
private final Create.Factory createFactory;
|
||||||
|
private final Detail detail;
|
||||||
|
private final ChangeEditUtil editUtil;
|
||||||
|
private final Post post;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ChangeEdits(DynamicMap<RestView<ChangeEditResource>> views) {
|
ChangeEdits(DynamicMap<RestView<ChangeEditResource>> views,
|
||||||
|
Create.Factory createFactory,
|
||||||
|
Detail detail,
|
||||||
|
ChangeEditUtil editUtil,
|
||||||
|
Post post) {
|
||||||
this.views = views;
|
this.views = views;
|
||||||
|
this.createFactory = createFactory;
|
||||||
|
this.detail = detail;
|
||||||
|
this.editUtil = editUtil;
|
||||||
|
this.post = post;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -38,11 +82,202 @@ class ChangeEdits implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RestView<ChangeResource> list() {
|
public RestView<ChangeResource> list() {
|
||||||
throw new IllegalStateException("not yet implemented");
|
return detail;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChangeEditResource parse(ChangeResource change, IdString id) {
|
public ChangeEditResource parse(ChangeResource rsrc, IdString id)
|
||||||
throw new IllegalStateException("not yet implemented");
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public Post post(ChangeResource parent) throws RestApiException {
|
||||||
|
return post;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
static class Detail implements RestReadView<ChangeResource> {
|
||||||
|
private final ChangeEditUtil editUtil;
|
||||||
|
private final ChangeEditJson editJson;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
Detail(ChangeEditJson editJson,
|
||||||
|
ChangeEditUtil editUtil) {
|
||||||
|
this.editJson = editJson;
|
||||||
|
this.editUtil = editUtil;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response<EditInfo> apply(ChangeResource rsrc) throws AuthException,
|
||||||
|
IOException, NoSuchChangeException, InvalidChangeOperationException,
|
||||||
|
ResourceNotFoundException {
|
||||||
|
Optional<ChangeEdit> edit = editUtil.byChange(rsrc.getChange());
|
||||||
|
if (edit.isPresent()) {
|
||||||
|
return Response.ok(editJson.toEditInfo(edit.get()));
|
||||||
|
}
|
||||||
|
return Response.none();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post to edit collection resource. Two different operations are
|
||||||
|
* supported:
|
||||||
|
* <ul>
|
||||||
|
* <li>Create non existing change edit</li>
|
||||||
|
* <li>Restore path in existing change edit</li>
|
||||||
|
* </ul>
|
||||||
|
* The combination of two operations in one request is supported.
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
public static class Post implements
|
||||||
|
RestModifyView<ChangeResource, Post.Input> {
|
||||||
|
public static class Input {
|
||||||
|
public String restorePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Provider<ReviewDb> db;
|
||||||
|
private final ChangeEditUtil editUtil;
|
||||||
|
private final ChangeEditModifier editModifier;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
Post(Provider<ReviewDb> db,
|
||||||
|
ChangeEditUtil editUtil,
|
||||||
|
ChangeEditModifier editModifier) {
|
||||||
|
this.db = db;
|
||||||
|
this.editUtil = editUtil;
|
||||||
|
this.editModifier = editModifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response<?> apply(ChangeResource resource, Post.Input input)
|
||||||
|
throws AuthException, InvalidChangeOperationException, IOException,
|
||||||
|
ResourceConflictException, OrmException {
|
||||||
|
Optional<ChangeEdit> edit = editUtil.byChange(resource.getChange());
|
||||||
|
if (!edit.isPresent()) {
|
||||||
|
edit = createEdit(resource.getChange());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input != null && !Strings.isNullOrEmpty(input.restorePath)) {
|
||||||
|
editModifier.restoreFile(edit.get(), input.restorePath);
|
||||||
|
}
|
||||||
|
return Response.none();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<ChangeEdit> createEdit(Change change)
|
||||||
|
throws AuthException, IOException, ResourceConflictException,
|
||||||
|
OrmException, InvalidChangeOperationException {
|
||||||
|
editModifier.createEdit(change,
|
||||||
|
db.get().patchSets().get(change.currentPatchSetId()));
|
||||||
|
return editUtil.byChange(change);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,13 +14,13 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server.change;
|
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.ChangeResource.CHANGE_KIND;
|
||||||
import static com.google.gerrit.server.change.CommentResource.COMMENT_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.DraftResource.DRAFT_KIND;
|
||||||
import static com.google.gerrit.server.change.FileResource.FILE_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.ReviewerResource.REVIEWER_KIND;
|
||||||
import static com.google.gerrit.server.change.RevisionResource.REVISION_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.registration.DynamicMap;
|
||||||
import com.google.gerrit.extensions.restapi.RestApiModule;
|
import com.google.gerrit.extensions.restapi.RestApiModule;
|
||||||
@@ -101,6 +101,9 @@ public class Module extends RestApiModule {
|
|||||||
get(FILE_KIND, "content").to(GetContent.class);
|
get(FILE_KIND, "content").to(GetContent.class);
|
||||||
get(FILE_KIND, "diff").to(GetDiff.class);
|
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() {
|
install(new FactoryModule() {
|
||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
@@ -109,6 +112,7 @@ public class Module extends RestApiModule {
|
|||||||
factory(EmailReviewComments.Factory.class);
|
factory(EmailReviewComments.Factory.class);
|
||||||
factory(ChangeInserter.Factory.class);
|
factory(ChangeInserter.Factory.class);
|
||||||
factory(PatchSetInserter.Factory.class);
|
factory(PatchSetInserter.Factory.class);
|
||||||
|
factory(ChangeEdits.Create.Factory.class);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
// Copyright (C) 2014 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.edit;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.gerrit.extensions.common.CommitInfo;
|
||||||
|
import com.google.gerrit.extensions.common.EditInfo;
|
||||||
|
import com.google.gerrit.server.CommonConverters;
|
||||||
|
import com.google.inject.Singleton;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class ChangeEditJson {
|
||||||
|
public EditInfo toEditInfo(ChangeEdit edit) throws IOException {
|
||||||
|
EditInfo out = new EditInfo();
|
||||||
|
out.commit = fillCommit(edit.getEditCommit());
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CommitInfo fillCommit(RevCommit editCommit) throws IOException {
|
||||||
|
CommitInfo commit = new CommitInfo();
|
||||||
|
commit.commit = editCommit.toObjectId().getName();
|
||||||
|
commit.parents = Lists.newArrayListWithCapacity(1);
|
||||||
|
commit.author = CommonConverters.toGitPerson(editCommit.getAuthorIdent());
|
||||||
|
commit.committer = CommonConverters.toGitPerson(
|
||||||
|
editCommit.getCommitterIdent());
|
||||||
|
commit.subject = editCommit.getShortMessage();
|
||||||
|
commit.message = editCommit.getFullMessage();
|
||||||
|
|
||||||
|
CommitInfo i = new CommitInfo();
|
||||||
|
i.commit = editCommit.getParent(0).toObjectId().getName();
|
||||||
|
commit.parents.add(i);
|
||||||
|
|
||||||
|
return commit;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user