Remove /type REST API projections

Clients can acquire the MIME type from the content using the
X-FYI-Content-Type header. If only the headers are necessary,
callers should use HEAD instead of GET.

Change-Id: I3be4aeb009fabb7899c6018ed232ef9c7fd347cb
This commit is contained in:
Shawn Pearce 2015-01-01 23:42:12 -05:00
parent 38df42f051
commit fb2b36b5f1
6 changed files with 21 additions and 178 deletions

View File

@ -1408,8 +1408,16 @@ Retrieves content of a file from a change edit.
GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/edit/foo HTTP/1.0 GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/edit/foo HTTP/1.0
---- ----
The content of the file is returned as text encoded inside base64. When The content of the file is returned as text encoded inside base64.
specified file was deleted in the change edit "`204 No Content`" is returned. The Content-Type header will always be `text/plain` reflecting the
outer base64 encoding. A Gerrit-specific `X-FYI-Content-Type` header
can be examined to find the server detected content type of the file.
When the specified file was deleted in the change edit
"`204 No Content`" is returned.
If only the content type is required, callers should use HEAD to
avoid downloading the encoded file contents.
.Response .Response
---- ----
@ -1417,33 +1425,11 @@ specified file was deleted in the change edit "`204 No Content`" is returned.
Content-Disposition: attachment Content-Disposition: attachment
Content-Type: text/plain; charset=ISO-8859-1 Content-Type: text/plain; charset=ISO-8859-1
X-FYI-Content-Encoding: base64 X-FYI-Content-Encoding: base64
X-FYI-Content-Type: text/xml
RnJvbSA3ZGFkY2MxNTNmZGVhMTdhYTg0ZmYzMmE2ZTI0NWRiYjY... RnJvbSA3ZGFkY2MxNTNmZGVhMTdhYTg0ZmYzMmE2ZTI0NWRiYjY...
---- ----
[[get-edit-file-mime-type]]
=== Retrieve file content MIME type from Change Edit
--
'GET /changes/link:#change-id[\{change-id\}]/edit/path%2fto%2ffile/type
--
Retrieves content MIME type of a file from a change edit.
.Request
----
GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/edit/foo%2fbar%2fbaz%2fqux.txt/type HTTP/1.0
----
.Response
----
HTTP/1.1 200 OK
Content-Disposition: attachment
Content-Type: application/json; charset=UTF-8
)]}'
"text/plain"
----
[[get-edit-message]] [[get-edit-message]]
=== Retrieve commit message from Change Edit or current patch set of the change === Retrieve commit message from Change Edit or current patch set of the change
-- --
@ -2789,43 +2775,25 @@ Gets the content of a file from a certain revision.
GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/revisions/674ac754f91e64a0efb8087e59a176484bd534d1/files/gerrit-server%2Fsrc%2Fmain%2Fjava%2Fcom%2Fgoogle%2Fgerrit%2Fserver%2Fproject%2FRefControl.java/content HTTP/1.0 GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/revisions/674ac754f91e64a0efb8087e59a176484bd534d1/files/gerrit-server%2Fsrc%2Fmain%2Fjava%2Fcom%2Fgoogle%2Fgerrit%2Fserver%2Fproject%2FRefControl.java/content HTTP/1.0
---- ----
The content is returned as base64 encoded string. The content is returned as base64 encoded string. The HTTP response
Content-Type is always `text/plain`, reflecting the base64 wrapping.
A Gerrit-specific `X-FYI-Content-Type` header is returned describing
the server detected content type of the file.
If only the content type is required, callers should use HEAD to
avoid downloading the encoded file contents.
.Response .Response
---- ----
HTTP/1.1 200 OK HTTP/1.1 200 OK
Content-Disposition: attachment Content-Disposition: attachment
Content-Type: text/plain; charset=UTF-8 Content-Type: text/plain; charset=ISO-8859-1
X-FYI-Content-Encoding: base64
X-FYI-Content-Type: text/xml
Ly8gQ29weXJpZ2h0IChDKSAyMDEwIFRoZSBBbmRyb2lkIE9wZW4gU291cmNlIFByb2plY... Ly8gQ29weXJpZ2h0IChDKSAyMDEwIFRoZSBBbmRyb2lkIE9wZW4gU291cmNlIFByb2plY...
---- ----
[[get-content-type]]
=== Get Content MIME Type
--
'GET /changes/link:#change-id[\{change-id\}]/revisions/link:#revision-id[\{revision-id\}]/files/link:#file-id[\{file-id\}]/type'
--
Gets the content MIME type of a file from a certain revision.
.Request
----
GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/revisions/674ac754f91e64a0efb8087e59a176484bd534d1/files/readme.txt/type HTTP/1.0
----
The content MIME type is returned as string. The content type for the commit
message (`/COMMIT_MSG`) is always returned as "text/plain".
.Response
----
HTTP/1.1 200 OK
Content-Disposition: attachment
Content-Type: application/json; charset=UTF-8
)]}'
"text/plain"
----
[[get-diff]] [[get-diff]]
=== Get Diff === Get Diff
-- --

View File

@ -508,18 +508,6 @@ public class ChangeEditIT extends AbstractDaemonTest {
.isEqualTo(StringUtils.newStringUtf8(CONTENT_NEW2)); .isEqualTo(StringUtils.newStringUtf8(CONTENT_NEW2));
} }
@Test
public void getFileContentTypeRest() throws Exception {
Put.Input in = new Put.Input();
in.content = RestSession.newRawInput(CONTENT_NEW);
assertThat(adminSession.putRaw(urlEditFile(), in.content).getStatusCode())
.isEqualTo(SC_NO_CONTENT);
RestResponse r = adminSession.get(urlEditFileContentType());
assertThat(r.getStatusCode()).isEqualTo(SC_OK);
String res = newGson().fromJson(r.getReader(), String.class);
assertThat(res).isEqualTo("application/octet-stream");
}
@Test @Test
public void getFileNotFoundRest() throws Exception { public void getFileNotFoundRest() throws Exception {
assertThat(modifier.createEdit(change, ps)).isEqualTo(RefUpdate.Result.NEW); assertThat(modifier.createEdit(change, ps)).isEqualTo(RefUpdate.Result.NEW);
@ -672,13 +660,6 @@ public class ChangeEditIT extends AbstractDaemonTest {
+ FILE_NAME; + FILE_NAME;
} }
private String urlEditFileContentType() {
return urlEdit()
+ "/"
+ FILE_NAME
+ "/type";
}
private String urlGetFiles() { private String urlGetFiles() {
return urlEdit() return urlEdit()
+ "?list"; + "?list";

View File

@ -511,23 +511,4 @@ public class ChangeEdits implements
throw new ResourceNotFoundException(); throw new ResourceNotFoundException();
} }
} }
@Singleton
public static class GetType implements RestReadView<ChangeEditResource> {
private final FileContentUtil fileContentUtil;
@Inject
GetType(FileContentUtil fileContentUtil) {
this.fileContentUtil = fileContentUtil;
}
@Override
public String apply(ChangeEditResource rsrc)
throws ResourceNotFoundException, IOException {
return fileContentUtil.getContentType(
rsrc.getControl().getProjectControl().getProjectState(),
rsrc.getChangeEdit().getEditCommit(),
rsrc.getPath());
}
}
} }

View File

@ -119,43 +119,6 @@ public class FileContentUtil {
return result; return result;
} }
public String getContentType(ProjectState project, ObjectId rev,
String path) throws ResourceNotFoundException, IOException {
Repository repo = openRepository(project);
try {
RevWalk rw = new RevWalk(repo);
try {
ObjectReader reader = rw.getObjectReader();
RevCommit commit = rw.parseCommit(rev);
TreeWalk tw = TreeWalk.forPath(reader, path, commit.getTree());
if (tw == null) {
throw new ResourceNotFoundException();
}
org.eclipse.jgit.lib.FileMode mode = tw.getFileMode(0);
if (mode == org.eclipse.jgit.lib.FileMode.GITLINK) {
return X_GIT_GITLINK;
} else if (mode == org.eclipse.jgit.lib.FileMode.SYMLINK) {
return X_GIT_SYMLINK;
}
ObjectLoader blob = reader.open(tw.getObjectId(0), OBJ_BLOB);
byte[] raw;
try {
raw = blob.getCachedBytes(MAX_SIZE);
} catch (LargeObjectException e) {
raw = null;
}
String type = registry.getMimeType(path, raw).toString();
return resolveContentType(project, path, FileMode.FILE, type);
} finally {
rw.release();
}
} finally {
repo.close();
}
}
public static String resolveContentType(ProjectState project, String path, public static String resolveContentType(ProjectState project, String path,
FileMode fileMode, String mimeType) { FileMode fileMode, String mimeType) {
switch (fileMode) { switch (fileMode) {

View File

@ -1,48 +0,0 @@
// 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.change;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.reviewdb.client.Patch;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.jgit.lib.ObjectId;
import java.io.IOException;
@Singleton
public class GetContentType implements RestReadView<FileResource> {
private final FileContentUtil fileContentUtil;
@Inject
GetContentType(FileContentUtil fileContentUtil) {
this.fileContentUtil = fileContentUtil;
}
@Override
public String apply(FileResource rsrc)
throws ResourceNotFoundException, IOException {
String path = rsrc.getPatchKey().get();
if (Patch.COMMIT_MSG.equals(path)) {
return FileContentUtil.TEXT_X_GERRIT_COMMIT_MESSAGE;
}
return fileContentUtil.getContentType(
rsrc.getRevision().getControl().getProjectControl().getProjectState(),
ObjectId.fromString(rsrc.getRevision().getPatchSet().getRevision().get()),
path);
}
}

View File

@ -103,7 +103,6 @@ public class Module extends RestApiModule {
put(FILE_KIND, "reviewed").to(PutReviewed.class); put(FILE_KIND, "reviewed").to(PutReviewed.class);
delete(FILE_KIND, "reviewed").to(DeleteReviewed.class); delete(FILE_KIND, "reviewed").to(DeleteReviewed.class);
get(FILE_KIND, "content").to(GetContent.class); get(FILE_KIND, "content").to(GetContent.class);
get(FILE_KIND, "type").to(GetContentType.class);
get(FILE_KIND, "diff").to(GetDiff.class); get(FILE_KIND, "diff").to(GetDiff.class);
child(CHANGE_KIND, "edit").to(ChangeEdits.class); child(CHANGE_KIND, "edit").to(ChangeEdits.class);
@ -115,7 +114,6 @@ public class Module extends RestApiModule {
put(CHANGE_EDIT_KIND, "/").to(ChangeEdits.Put.class); put(CHANGE_EDIT_KIND, "/").to(ChangeEdits.Put.class);
delete(CHANGE_EDIT_KIND).to(ChangeEdits.DeleteContent.class); delete(CHANGE_EDIT_KIND).to(ChangeEdits.DeleteContent.class);
get(CHANGE_EDIT_KIND, "/").to(ChangeEdits.Get.class); get(CHANGE_EDIT_KIND, "/").to(ChangeEdits.Get.class);
get(CHANGE_EDIT_KIND, "type").to(ChangeEdits.GetType.class);
install(new FactoryModule() { install(new FactoryModule() {
@Override @Override