Add REST endpoint to get an arbitrary commit from a project

Bug: issue 2604
Change-Id: I138e60c5291d61c379e947ea26b0fd9d5555c743
Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
This commit is contained in:
Edwin Kempin
2014-07-08 16:18:45 +02:00
committed by Edwin Kempin
parent 990662d0b8
commit 1b99360cc7
6 changed files with 363 additions and 1 deletions

View File

@@ -972,6 +972,59 @@ describes the child project.
}
----
[[commit-endpoints]]
== Commit Endpoints
[[get-commit]]
=== Get Commit
--
'GET /projects/link:#project-name[\{project-name\}]/commits/link:#commit-id[\{commit-id\}]'
--
Retrieves a commit of a project.
The commit must be visible to the caller.
.Request
----
GET /projects/work%2Fmy-project/commits/a8a477efffbbf3b44169bb9a1d3a334cbbd9aa96 HTTP/1.0
----
As response a link:rest-api-changes.html#commit-info[CommitInfo] entity
is returned that describes the commit.
.Response
----
HTTP/1.1 200 OK
Content-Disposition: attachment
Content-Type: application/json;charset=UTF-8
)]}'
{
"commit": "184ebe53805e102605d11f6b143486d15c23a09c",
"parents": [
{
"commit": "1eee2c9d8f352483781e772f35dc586a69ff5646",
"subject": "Migrate contributor agreements to All-Projects."
}
],
"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 ..."
}
----
[[dashboard-endpoints]]
== Dashboard Endpoints
@@ -1199,6 +1252,10 @@ requests.
The name of a branch or `HEAD`. The prefix `refs/heads/` can be
omitted.
[[commit-id]]
=== \{commit-id\}
Commit ID.
[[dashboard-id]]
=== \{dashboard-id\}
The ID of a dashboard in the format '<ref>:<path>'.

View File

@@ -0,0 +1,111 @@
// 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.acceptance.rest.project;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.RestResponse;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.common.CommitInfo;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.project.ListBranches.BranchInfo;
import com.google.gerrit.server.project.ProjectCache;
import com.google.inject.Inject;
import org.apache.http.HttpStatus;
import org.eclipse.jgit.lib.ObjectId;
import org.junit.Test;
import java.io.IOException;
public class GetCommitIT extends AbstractDaemonTest {
@Inject
private ProjectCache projectCache;
@Inject
private AllProjectsName allProjects;
@Inject
private MetaDataUpdate.Server metaDataUpdateFactory;
@Test
public void getCommit() throws IOException {
RestResponse r =
adminSession.get("/projects/" + project.get() + "/branches/"
+ IdString.fromDecoded(RefNames.REFS_CONFIG).encoded());
assertEquals(HttpStatus.SC_OK, r.getStatusCode());
BranchInfo branchInfo =
newGson().fromJson(r.getReader(), BranchInfo.class);
r.consume();
r = adminSession.get("/projects/" + project.get() + "/commits/"
+ branchInfo.revision);
assertEquals(HttpStatus.SC_OK, r.getStatusCode());
CommitInfo commitInfo =
newGson().fromJson(r.getReader(), CommitInfo.class);
assertEquals(branchInfo.revision, commitInfo.commit);
assertEquals("Created project", commitInfo.subject);
assertEquals("Created project\n", commitInfo.message);
assertNotNull(commitInfo.author);
assertEquals("Administrator", commitInfo.author.name);
assertNotNull(commitInfo.committer);
assertEquals("Gerrit Code Review", commitInfo.committer.name);
assertTrue(commitInfo.parents.isEmpty());
}
@Test
public void getNonExistingCommit_NotFound() throws IOException {
RestResponse r = adminSession.get("/projects/" + project.get() + "/commits/"
+ ObjectId.zeroId().name());
assertEquals(HttpStatus.SC_NOT_FOUND, r.getStatusCode());
}
@Test
public void getNonVisibleCommit_NotFound() throws IOException {
RestResponse r =
adminSession.get("/projects/" + project.get() + "/branches/"
+ IdString.fromDecoded(RefNames.REFS_CONFIG).encoded());
assertEquals(HttpStatus.SC_OK, r.getStatusCode());
BranchInfo branchInfo =
newGson().fromJson(r.getReader(), BranchInfo.class);
r.consume();
ProjectConfig cfg = projectCache.checkedGet(allProjects).getConfig();
cfg.getAccessSection("refs/*", false).removePermission(Permission.READ);
saveProjectConfig(cfg);
projectCache.evict(cfg.getProject());
r = adminSession.get("/projects/" + project.get() + "/commits/"
+ branchInfo.revision);
assertEquals(HttpStatus.SC_NOT_FOUND, r.getStatusCode());
}
private void saveProjectConfig(ProjectConfig cfg) throws IOException {
MetaDataUpdate md = metaDataUpdateFactory.create(allProjects);
try {
cfg.commit(md);
} finally {
md.close();
}
}
}

View File

@@ -0,0 +1,36 @@
// 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.project;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.inject.TypeLiteral;
import org.eclipse.jgit.revwalk.RevCommit;
public class CommitResource extends ProjectResource {
public static final TypeLiteral<RestView<CommitResource>> COMMIT_KIND =
new TypeLiteral<RestView<CommitResource>>() {};
private final RevCommit commit;
public CommitResource(ProjectControl control, RevCommit commit) {
super(control);
this.commit = commit;
}
public RevCommit getCommit() {
return commit;
}
}

View File

@@ -0,0 +1,89 @@
// 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.project;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.ChildCollection;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import java.io.IOException;
@Singleton
public class CommitsCollection implements
ChildCollection<ProjectResource, CommitResource> {
private final DynamicMap<RestView<CommitResource>> views;
private final GitRepositoryManager repoManager;
@Inject
public CommitsCollection(DynamicMap<RestView<CommitResource>> views,
GitRepositoryManager repoManager) {
this.views = views;
this.repoManager = repoManager;
}
@Override
public RestView<ProjectResource> list() throws ResourceNotFoundException {
throw new ResourceNotFoundException();
}
@Override
public CommitResource parse(ProjectResource parent, IdString id)
throws ResourceNotFoundException, IOException {
ObjectId objectId;
try {
objectId = ObjectId.fromString(id.get());
} catch (IllegalArgumentException e) {
throw new ResourceNotFoundException(id);
}
Repository repo = repoManager.openRepository(parent.getNameKey());
try {
RevWalk rw = new RevWalk(repo);
try {
RevCommit commit = rw.parseCommit(objectId);
if (!parent.getControl().canReadCommit(rw, commit)) {
throw new ResourceNotFoundException(id);
}
for (int i = 0; i < commit.getParentCount(); i++) {
rw.parseCommit(commit.getParent(i));
}
return new CommitResource(parent.getControl(), commit);
} catch (MissingObjectException | IncorrectObjectTypeException e) {
throw new ResourceNotFoundException(id);
} finally {
rw.release();
}
} finally {
repo.close();
}
}
@Override
public DynamicMap<RestView<CommitResource>> views() {
return views;
}
}

View File

@@ -0,0 +1,64 @@
// 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.project;
import com.google.gerrit.extensions.common.CommitInfo;
import com.google.gerrit.extensions.common.GitPerson;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.inject.Singleton;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.revwalk.RevCommit;
import java.sql.Timestamp;
import java.util.ArrayList;
@Singleton
public class GetCommit implements RestReadView<CommitResource> {
@Override
public CommitInfo apply(CommitResource rsrc) {
RevCommit c = rsrc.getCommit();
CommitInfo info = toCommitInfo(c);
return info;
}
private static CommitInfo toCommitInfo(RevCommit commit) {
CommitInfo info = new CommitInfo();
info.commit = commit.getName();
info.author = toGitPerson(commit.getAuthorIdent());
info.committer = toGitPerson(commit.getCommitterIdent());
info.subject = commit.getShortMessage();
info.message = commit.getFullMessage();
info.parents = new ArrayList<>(commit.getParentCount());
for (int i = 0; i < commit.getParentCount(); i++) {
RevCommit p = commit.getParent(i);
CommitInfo parentInfo = new CommitInfo();
parentInfo.commit = p.getName();
parentInfo.subject = p.getShortMessage();
info.parents.add(parentInfo);
}
return info;
}
private static GitPerson toGitPerson(PersonIdent ident) {
GitPerson gp = new GitPerson();
gp.name = ident.getName();
gp.email = ident.getEmailAddress();
gp.date = new Timestamp(ident.getWhen().getTime());
gp.tz = ident.getTimeZoneOffset();
return gp;
}
}

View File

@@ -16,9 +16,10 @@ package com.google.gerrit.server.project;
import static com.google.gerrit.server.project.BranchResource.BRANCH_KIND;
import static com.google.gerrit.server.project.ChildProjectResource.CHILD_PROJECT_KIND;
import static com.google.gerrit.server.project.CommitResource.COMMIT_KIND;
import static com.google.gerrit.server.project.DashboardResource.DASHBOARD_KIND;
import static com.google.gerrit.server.project.ProjectResource.PROJECT_KIND;
import static com.google.gerrit.server.project.FileResource.FILE_KIND;
import static com.google.gerrit.server.project.ProjectResource.PROJECT_KIND;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.RestApiModule;
@@ -35,6 +36,7 @@ public class Module extends RestApiModule {
DynamicMap.mapOf(binder(), BRANCH_KIND);
DynamicMap.mapOf(binder(), DASHBOARD_KIND);
DynamicMap.mapOf(binder(), FILE_KIND);
DynamicMap.mapOf(binder(), COMMIT_KIND);
put(PROJECT_KIND).to(PutProject.class);
get(PROJECT_KIND).to(GetProject.class);
@@ -63,6 +65,9 @@ public class Module extends RestApiModule {
child(BRANCH_KIND, "files").to(FilesCollection.class);
get(FILE_KIND, "content").to(GetContent.class);
child(PROJECT_KIND, "commits").to(CommitsCollection.class);
get(COMMIT_KIND).to(GetCommit.class);
child(PROJECT_KIND, "dashboards").to(DashboardsCollection.class);
get(DASHBOARD_KIND).to(GetDashboard.class);
put(DASHBOARD_KIND).to(SetDashboard.class);