Merge changes from topic 'tags-api' * changes: ListTags: Support filtering by substring and regex ProjectApi: Refactor to reduce duplicate code in tags and branches ListTags: Add support for pagination with --start and --limit ListBranches: Split filtering and pagination out to a utility class Make BranchInfo and TagInfo share a common base class Implement tags API ListTags: Create RevWalk in try-with-resource RefNames: Add support for refs/tags/ in shortName()
diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt index 4658e2c..7ae320d 100644 --- a/Documentation/rest-api-projects.txt +++ b/Documentation/rest-api-projects.txt
@@ -1453,6 +1453,80 @@ ] ---- +[[tag-options]] +==== Tag Options + +Limit(n):: +Limit the number of tags to be included in the results. ++ +.Request +---- + GET /projects/work%2Fmy-project/tags?n=2 HTTP/1.0 +---- ++ +.Response +---- + HTTP/1.1 200 OK + Content-Disposition: attachment + Content-Type: application/json; charset=UTF-8 + + )]}' + [ + { + "ref": "refs/tags/v1.0", + "revision": "49ce77fdcfd3398dc0dedbe016d1a425fd52d666", + "object": "1624f5af8ae89148d1a3730df8c290413e3dcf30", + "message": "Annotated tag", + "tagger": { + "name": "David Pursehouse", + "email": "david.pursehouse@sonymobile.com", + "date": "2014-10-06 07:35:03.000000000", + "tz": 540 + } + }, + { + "ref": "refs/tags/v2.0", + "revision": "1624f5af8ae89148d1a3730df8c290413e3dcf30" + } + ] +---- + +Skip(s):: +Skip the given number of tags from the beginning of the list. ++ +.Request +---- + GET /projects/work%2Fmy-project/tags?n=2&s=1 HTTP/1.0 +---- ++ +.Response +---- + HTTP/1.1 200 OK + Content-Disposition: attachment + Content-Type: application/json; charset=UTF-8 + + )]}' + [ + { + "ref": "refs/tags/v2.0", + "revision": "1624f5af8ae89148d1a3730df8c290413e3dcf30" + }, + { + "ref": "refs/tags/v3.0", + "revision": "c628685b3c5a3614571ecb5c8fceb85db9112313", + "object": "1624f5af8ae89148d1a3730df8c290413e3dcf30", + "message": "Signed tag\n-----BEGIN PGP SIGNATURE-----\nVersion: GnuPG v1.4.11 (GNU/Linux)\n\niQEcBAABAgAGBQJUMlqYAAoJEPI2qVPgglptp7MH/j+KFcittFbxfSnZjUl8n5IZ\nveZo7wE+syjD9sUbMH4EGv0WYeVjphNTyViBof+stGTNkB0VQzLWI8+uWmOeiJ4a\nzj0LsbDOxodOEMI5tifo02L7r4Lzj++EbqtKv8IUq2kzYoQ2xjhKfFiGjeYOn008\n9PGnhNblEHZgCHguGR6GsfN8bfA2XNl9B5Ysl5ybX1kAVs/TuLZR4oDMZ/pW2S75\nhuyNnSgcgq7vl2gLGefuPs9lxkg5Fj3GZr7XPZk4pt/x1oiH7yXxV4UzrUwg2CC1\nfHrBlNbQ4uJNY8TFcwof52Z0cagM5Qb/ZSLglHbqEDGA68HPqbwf5z2hQyU2/U4\u003d\n\u003dZtUX\n-----END PGP SIGNATURE-----", + "tagger": { + "name": "David Pursehouse", + "email": "david.pursehouse@sonymobile.com", + "date": "2014-10-06 09:02:16.000000000", + "tz": 540 + } + } + ] +---- + + [[get-tag]] === Get Tag --
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java index 1b79bc3..6617127 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java
@@ -22,7 +22,7 @@ import com.google.gerrit.acceptance.NoHttpd; import com.google.gerrit.acceptance.TestProjectInput; import com.google.gerrit.extensions.api.projects.BranchInfo; -import com.google.gerrit.extensions.api.projects.ProjectApi.ListBranchesRequest; +import com.google.gerrit.extensions.api.projects.ProjectApi.ListRefsRequest; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import org.junit.Test; @@ -155,7 +155,7 @@ list().withRegex(".*ast.*r").get()); } - private ListBranchesRequest list() throws Exception { + private ListRefsRequest<BranchInfo> list() throws Exception { return gApi.projects().name(project.get()).branches(); }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/TagsIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/TagsIT.java index 7efefa7..72584b7 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/TagsIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/TagsIT.java
@@ -16,12 +16,14 @@ import static com.google.common.truth.Truth.assertThat; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableList; import com.google.gerrit.acceptance.AbstractDaemonTest; import com.google.gerrit.acceptance.PushOneCommit; -import com.google.gerrit.acceptance.RestResponse; import com.google.gerrit.common.data.Permission; -import com.google.gerrit.extensions.common.TagInfo; -import com.google.gson.reflect.TypeToken; +import com.google.gerrit.extensions.api.projects.ProjectApi.ListRefsRequest; +import com.google.gerrit.extensions.api.projects.TagInfo; +import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import org.apache.http.HttpStatus; import org.junit.Test; @@ -29,14 +31,25 @@ import java.util.List; public class TagsIT extends AbstractDaemonTest { + private static final List<String> testTags = ImmutableList.of( + "tag-A", "tag-B", "tag-C", "tag-D", "tag-E", "tag-F", "tag-G", "tag-H"); + @Test - public void listTagsOfNonExistingProject_NotFound() throws Exception { + public void listTagsOfNonExistingProject() throws Exception { assertThat(adminSession.get("/projects/non-existing/tags").getStatusCode()) .isEqualTo(HttpStatus.SC_NOT_FOUND); } @Test - public void listTagsOfNonVisibleProject_NotFound() throws Exception { + public void listTagsOfNonExistingProjectWithApi() throws Exception { + exception.expect(ResourceNotFoundException.class); + gApi.projects().name("does-not-exist").tags(); + exception.expect(ResourceNotFoundException.class); + gApi.projects().name("does-not-exist").tag("tag").get(); + } + + @Test + public void listTagsOfNonVisibleProject() throws Exception { blockRead(project, "refs/*"); assertThat( userSession.get("/projects/" + project.get() + "/tags").getStatusCode()) @@ -44,6 +57,15 @@ } @Test + public void listTagsOfNonVisibleProjectWithApi() throws Exception { + blockRead(project, "refs/*"); + exception.expect(ResourceNotFoundException.class); + gApi.projects().name(project.get()).tags(); + exception.expect(ResourceNotFoundException.class); + gApi.projects().name(project.get()).tag("tag").get(); + } + + @Test public void listTags() throws Exception { grant(Permission.SUBMIT, project, "refs/for/refs/heads/master"); grant(Permission.CREATE, project, "refs/tags/*"); @@ -62,8 +84,7 @@ PushOneCommit.Result r2 = push2.to("refs/for/master%submit"); r2.assertOkStatus(); - List<TagInfo> result = - toTagInfoList(adminSession.get("/projects/" + project.get() + "/tags")); + List<TagInfo> result = getTags().get(); assertThat(result).hasSize(2); TagInfo t = result.get(0); @@ -78,6 +99,61 @@ assertThat(t.tagger.email).isEqualTo(tag2.tagger.getEmailAddress()); } + private void assertTagList(FluentIterable<String> expected, List<TagInfo> actual) + throws Exception { + assertThat(actual).hasSize(expected.size()); + for (int i = 0; i < expected.size(); i ++) { + assertThat(actual.get(i).ref).isEqualTo("refs/tags/" + expected.get(i)); + } + } + + @Test + public void listTagsWithoutOptions() throws Exception { + createTags(); + List<TagInfo> result = getTags().get(); + assertTagList(FluentIterable.from(testTags), result); + } + + @Test + public void listTagsWithStartOption() throws Exception { + createTags(); + List<TagInfo> result = getTags().withStart(1).get(); + assertTagList(FluentIterable.from(testTags).skip(1), result); + } + + @Test + public void listTagsWithLimitOption() throws Exception { + createTags(); + int limit = testTags.size() - 1; + List<TagInfo> result = getTags().withLimit(limit).get(); + assertTagList(FluentIterable.from(testTags).limit(limit), result); + } + + @Test + public void listTagsWithLimitAndStartOption() throws Exception { + createTags(); + int limit = testTags.size() - 3; + List<TagInfo> result = getTags().withStart(1).withLimit(limit).get(); + assertTagList(FluentIterable.from(testTags).skip(1).limit(limit), result); + } + + @Test + public void listTagsWithRegexFilter() throws Exception { + createTags(); + List<TagInfo> result = getTags().withRegex("^tag-[C|D]$").get(); + assertTagList( + FluentIterable.from(ImmutableList.of("tag-C", "tag-D")), result); + } + + @Test + public void listTagsWithSubstringFilter() throws Exception { + createTags(); + List<TagInfo> result = getTags().withSubstring("tag-").get(); + assertTagList(FluentIterable.from(testTags), result); + result = getTags().withSubstring("ag-B").get(); + assertTagList(FluentIterable.from(ImmutableList.of("tag-B")), result); + } + @Test public void listTagsOfNonVisibleBranch() throws Exception { grant(Permission.SUBMIT, project, "refs/for/refs/heads/master"); @@ -98,8 +174,7 @@ PushOneCommit.Result r2 = push2.to("refs/for/hidden%submit"); r2.assertOkStatus(); - List<TagInfo> result = - toTagInfoList(adminSession.get("/projects/" + project.get() + "/tags")); + List<TagInfo> result = getTags().get(); assertThat(result).hasSize(2); assertThat(result.get(0).ref).isEqualTo("refs/tags/" + tag1.name); assertThat(result.get(0).revision).isEqualTo(r1.getCommitId().getName()); @@ -107,8 +182,7 @@ assertThat(result.get(1).revision).isEqualTo(r2.getCommitId().getName()); blockRead(project, "refs/heads/hidden"); - result = - toTagInfoList(adminSession.get("/projects/" + project.get() + "/tags")); + result = getTags().get(); assertThat(result).hasSize(1); assertThat(result.get(0).ref).isEqualTo("refs/tags/" + tag1.name); assertThat(result.get(0).revision).isEqualTo(r1.getCommitId().getName()); @@ -126,18 +200,29 @@ PushOneCommit.Result r1 = push1.to("refs/for/master%submit"); r1.assertOkStatus(); - RestResponse response = - adminSession.get("/projects/" + project.get() + "/tags/" + tag1.name); - TagInfo tagInfo = - newGson().fromJson(response.getReader(), TagInfo.class); + TagInfo tagInfo = getTag(tag1.name); assertThat(tagInfo.ref).isEqualTo("refs/tags/" + tag1.name); assertThat(tagInfo.revision).isEqualTo(r1.getCommitId().getName()); } - private static List<TagInfo> toTagInfoList(RestResponse r) throws Exception { - List<TagInfo> result = - newGson().fromJson(r.getReader(), - new TypeToken<List<TagInfo>>() {}.getType()); - return result; + private void createTags() throws Exception { + grant(Permission.SUBMIT, project, "refs/for/refs/heads/master"); + grant(Permission.CREATE, project, "refs/tags/*"); + grant(Permission.PUSH, project, "refs/tags/*"); + for (String tagname : testTags) { + PushOneCommit.Tag tag = new PushOneCommit.Tag(tagname); + PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo); + push.setTag(tag); + PushOneCommit.Result result = push.to("refs/for/master%submit"); + result.assertOkStatus(); + } + } + + private ListRefsRequest<TagInfo> getTags() throws Exception { + return gApi.projects().name(project.get()).tags(); + } + + private TagInfo getTag(String ref) throws Exception { + return gApi.projects().name(project.get()).tag(ref).get(); } }
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/BranchInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/BranchInfo.java index b973806..77513a2 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/BranchInfo.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/BranchInfo.java
@@ -20,9 +20,7 @@ import java.util.List; import java.util.Map; -public class BranchInfo { - public String ref; - public String revision; +public class BranchInfo extends RefInfo { public Boolean canDelete; public Map<String, ActionInfo> actions; public List<WebLinkInfo> webLinks;
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ProjectApi.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ProjectApi.java index 102b1ce..e3eb4be 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ProjectApi.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/ProjectApi.java
@@ -28,32 +28,33 @@ String description() throws RestApiException; void description(PutDescriptionInput in) throws RestApiException; - ListBranchesRequest branches(); + ListRefsRequest<BranchInfo> branches(); + ListRefsRequest<TagInfo> tags(); - public abstract class ListBranchesRequest { - private int limit; - private int start; - private String substring; - private String regex; + public abstract class ListRefsRequest<T extends RefInfo> { + protected int limit; + protected int start; + protected String substring; + protected String regex; - public abstract List<BranchInfo> get() throws RestApiException; + public abstract List<T> get() throws RestApiException; - public ListBranchesRequest withLimit(int limit) { + public ListRefsRequest<T> withLimit(int limit) { this.limit = limit; return this; } - public ListBranchesRequest withStart(int start) { + public ListRefsRequest<T> withStart(int start) { this.start = start; return this; } - public ListBranchesRequest withSubstring(String substring) { + public ListRefsRequest<T> withSubstring(String substring) { this.substring = substring; return this; } - public ListBranchesRequest withRegex(String regex) { + public ListRefsRequest<T> withRegex(String regex) { this.regex = regex; return this; } @@ -73,7 +74,6 @@ public String getRegex() { return regex; } - } List<ProjectInfo> children() throws RestApiException; @@ -96,6 +96,15 @@ BranchApi branch(String ref) throws RestApiException; /** + * Look up a tag by refname. + * <p> + * @param ref tag name, with or without "refs/tags/" prefix. + * @throws RestApiException if a problem occurred reading the project. + * @return API for accessing the tag. + */ + TagApi tag(String ref) throws RestApiException; + + /** * A default implementation which allows source compatibility * when adding new methods to the interface. **/ @@ -127,7 +136,12 @@ } @Override - public ListBranchesRequest branches() { + public ListRefsRequest<BranchInfo> branches() { + throw new NotImplementedException(); + } + + @Override + public ListRefsRequest<TagInfo> tags() { throw new NotImplementedException(); } @@ -150,5 +164,10 @@ public BranchApi branch(String ref) throws RestApiException { throw new NotImplementedException(); } + + @Override + public TagApi tag(String ref) throws RestApiException { + throw new NotImplementedException(); + } } }
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/RefInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/RefInfo.java new file mode 100644 index 0000000..1844a76 --- /dev/null +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/RefInfo.java
@@ -0,0 +1,20 @@ +// Copyright (C) 2015 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.api.projects; + +public class RefInfo { + public String ref; + public String revision; +}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/TagApi.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/TagApi.java new file mode 100644 index 0000000..6cc1ba4 --- /dev/null +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/TagApi.java
@@ -0,0 +1,33 @@ +// Copyright (C) 2015 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.api.projects; + +import com.google.gerrit.extensions.restapi.NotImplementedException; +import com.google.gerrit.extensions.restapi.RestApiException; + +public interface TagApi { + TagInfo get() throws RestApiException; + + /** + * A default implementation which allows source compatibility + * when adding new methods to the interface. + **/ + public class NotImplemented implements TagApi { + @Override + public TagInfo get() throws RestApiException { + throw new NotImplementedException(); + } + } +}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/TagInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/TagInfo.java similarity index 87% rename from gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/TagInfo.java rename to gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/TagInfo.java index 3e3d8db..b531d67 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/TagInfo.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/TagInfo.java
@@ -12,11 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -package com.google.gerrit.extensions.common; +package com.google.gerrit.extensions.api.projects; -public class TagInfo { - public String ref; - public String revision; +import com.google.gerrit.extensions.common.GitPerson; + +public class TagInfo extends RefInfo { public String object; public String message; public GitPerson tagger;
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/RefNames.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/RefNames.java index da66929..5d2a1fd 100644 --- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/RefNames.java +++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/RefNames.java
@@ -21,6 +21,8 @@ public static final String REFS_HEADS = "refs/heads/"; + public static final String REFS_TAGS = "refs/tags/"; + public static final String REFS_CHANGES = "refs/changes/"; /** Note tree listing commits we refuse {@code refs/meta/reject-commits} */ @@ -62,9 +64,12 @@ } public static final String shortName(String ref) { - return ref.startsWith(REFS_HEADS) - ? ref.substring(REFS_HEADS.length()) - : ref; + if (ref.startsWith(REFS_HEADS)) { + return ref.substring(REFS_HEADS.length()); + } else if (ref.startsWith(REFS_TAGS)) { + return ref.substring(REFS_TAGS.length()); + } + return ref; } public static String refsUsers(Account.Id accountId) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/Module.java index 3113d07..975e6c1 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/Module.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/Module.java
@@ -23,6 +23,7 @@ bind(Projects.class).to(ProjectsImpl.class); factory(BranchApiImpl.Factory.class); + factory(TagApiImpl.Factory.class); factory(ProjectApiImpl.Factory.class); factory(ChildProjectApiImpl.Factory.class); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java index 84b219b5..dbd246c 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
@@ -22,6 +22,8 @@ import com.google.gerrit.extensions.api.projects.ProjectApi; import com.google.gerrit.extensions.api.projects.ProjectInput; import com.google.gerrit.extensions.api.projects.PutDescriptionInput; +import com.google.gerrit.extensions.api.projects.TagApi; +import com.google.gerrit.extensions.api.projects.TagInfo; import com.google.gerrit.extensions.common.ProjectInfo; import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.IdString; @@ -35,6 +37,7 @@ import com.google.gerrit.server.project.GetDescription; import com.google.gerrit.server.project.ListBranches; import com.google.gerrit.server.project.ListChildProjects; +import com.google.gerrit.server.project.ListTags; import com.google.gerrit.server.project.ProjectJson; import com.google.gerrit.server.project.ProjectResource; import com.google.gerrit.server.project.ProjectsCollection; @@ -66,7 +69,9 @@ private final ProjectJson projectJson; private final String name; private final BranchApiImpl.Factory branchApi; + private final TagApiImpl.Factory tagApi; private final Provider<ListBranches> listBranchesProvider; + private final Provider<ListTags> listTagsProvider; @AssistedInject ProjectApiImpl(Provider<CurrentUser> user, @@ -79,11 +84,13 @@ ChildProjectsCollection children, ProjectJson projectJson, BranchApiImpl.Factory branchApiFactory, + TagApiImpl.Factory tagApiFactory, Provider<ListBranches> listBranchesProvider, + Provider<ListTags> listTagsProvider, @Assisted ProjectResource project) { this(user, createProjectFactory, projectApi, projects, getDescription, putDescription, childApi, children, projectJson, branchApiFactory, - listBranchesProvider, project, null); + tagApiFactory, listBranchesProvider, listTagsProvider, project, null); } @AssistedInject @@ -97,11 +104,13 @@ ChildProjectsCollection children, ProjectJson projectJson, BranchApiImpl.Factory branchApiFactory, + TagApiImpl.Factory tagApiFactory, Provider<ListBranches> listBranchesProvider, + Provider<ListTags> listTagsProvider, @Assisted String name) { this(user, createProjectFactory, projectApi, projects, getDescription, putDescription, childApi, children, projectJson, branchApiFactory, - listBranchesProvider, null, name); + tagApiFactory, listBranchesProvider, listTagsProvider, null, name); } private ProjectApiImpl(Provider<CurrentUser> user, @@ -114,7 +123,9 @@ ChildProjectsCollection children, ProjectJson projectJson, BranchApiImpl.Factory branchApiFactory, + TagApiImpl.Factory tagApiFactory, Provider<ListBranches> listBranchesProvider, + Provider<ListTags> listTagsProvider, ProjectResource project, String name) { this.user = user; @@ -129,7 +140,9 @@ this.project = project; this.name = name; this.branchApi = branchApiFactory; + this.tagApi = tagApiFactory; this.listBranchesProvider = listBranchesProvider; + this.listTagsProvider = listTagsProvider; } @Override @@ -179,8 +192,8 @@ } @Override - public ListBranchesRequest branches() { - return new ListBranchesRequest() { + public ListRefsRequest<BranchInfo> branches() { + return new ListRefsRequest<BranchInfo>() { @Override public List<BranchInfo> get() throws RestApiException { return listBranches(this); @@ -188,7 +201,7 @@ }; } - private List<BranchInfo> listBranches(ListBranchesRequest request) + private List<BranchInfo> listBranches(ListRefsRequest<BranchInfo> request) throws RestApiException { ListBranches list = listBranchesProvider.get(); list.setLimit(request.getLimit()); @@ -203,6 +216,30 @@ } @Override + public ListRefsRequest<TagInfo> tags() { + return new ListRefsRequest<TagInfo>() { + @Override + public List<TagInfo> get() throws RestApiException { + return listTags(this); + } + }; + } + + private List<TagInfo> listTags(ListRefsRequest<TagInfo> request) + throws RestApiException { + ListTags list = listTagsProvider.get(); + list.setLimit(request.getLimit()); + list.setStart(request.getStart()); + list.setMatchSubstring(request.getSubstring()); + list.setMatchRegex(request.getRegex()); + try { + return list.apply(checkExists()); + } catch (IOException e) { + throw new RestApiException("Cannot list tags", e); + } + } + + @Override public List<ProjectInfo> children() throws RestApiException { return children(false); } @@ -229,6 +266,11 @@ return branchApi.create(checkExists(), ref); } + @Override + public TagApi tag(String ref) throws ResourceNotFoundException { + return tagApi.create(checkExists(), ref); + } + private ProjectResource checkExists() throws ResourceNotFoundException { if (project == null) { throw new ResourceNotFoundException(name);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/TagApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/TagApiImpl.java new file mode 100644 index 0000000..086447d --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/TagApiImpl.java
@@ -0,0 +1,54 @@ +// Copyright (C) 2015 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.api.projects; + +import com.google.gerrit.extensions.api.projects.TagApi; +import com.google.gerrit.extensions.api.projects.TagInfo; +import com.google.gerrit.extensions.restapi.IdString; +import com.google.gerrit.extensions.restapi.RestApiException; +import com.google.gerrit.server.project.ListTags; +import com.google.gerrit.server.project.ProjectResource; +import com.google.inject.Inject; +import com.google.inject.assistedinject.Assisted; + +import java.io.IOException; + +public class TagApiImpl implements TagApi { + interface Factory { + TagApiImpl create(ProjectResource project, String ref); + } + + private final ListTags listTags; + private final String ref; + private final ProjectResource project; + + @Inject + TagApiImpl(ListTags listTags, + @Assisted ProjectResource project, + @Assisted String ref) { + this.listTags = listTags; + this.project = project; + this.ref = ref; + } + + @Override + public TagInfo get() throws RestApiException { + try { + return listTags.get(project, IdString.fromDecoded(ref)); + } catch (IOException e) { + throw new RestApiException(e.getMessage()); + } + } +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetTag.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetTag.java index 5b78e08..a94d17e 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetTag.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetTag.java
@@ -14,7 +14,7 @@ package com.google.gerrit.server.project; -import com.google.gerrit.extensions.common.TagInfo; +import com.google.gerrit.extensions.api.projects.TagInfo; import com.google.gerrit.extensions.restapi.RestReadView; import com.google.inject.Singleton;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListBranches.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListBranches.java index f7914e9..370ced2 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListBranches.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListBranches.java
@@ -14,8 +14,6 @@ package com.google.gerrit.server.project; -import com.google.common.base.Predicate; -import com.google.common.base.Strings; import com.google.common.collect.ComparisonChain; import com.google.common.collect.FluentIterable; import com.google.common.collect.Sets; @@ -35,9 +33,6 @@ import com.google.inject.Inject; import com.google.inject.util.Providers; -import dk.brics.automaton.RegExp; -import dk.brics.automaton.RunAutomaton; - import org.eclipse.jgit.errors.RepositoryNotFoundException; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Ref; @@ -50,7 +45,6 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; -import java.util.Locale; import java.util.Set; import java.util.TreeMap; @@ -96,18 +90,15 @@ @Override public List<BranchInfo> apply(ProjectResource rsrc) throws ResourceNotFoundException, IOException, BadRequestException { - FluentIterable<BranchInfo> branches = allBranches(rsrc); - branches = filterBranches(branches); - if (start > 0) { - branches = branches.skip(start); - } - if (limit > 0) { - branches = branches.limit(limit); - } - return branches.toList(); + return new RefFilter<BranchInfo>(Constants.R_HEADS) + .subString(matchSubstring) + .regex(matchRegex) + .start(start) + .limit(limit) + .filter(allBranches(rsrc)); } - private FluentIterable<BranchInfo> allBranches(ProjectResource rsrc) + private List<BranchInfo> allBranches(ProjectResource rsrc) throws IOException, ResourceNotFoundException { List<Ref> refs; try (Repository db = repoManager.openRepository(rsrc.getNameKey())) { @@ -162,7 +153,7 @@ } } Collections.sort(branches, new BranchComparator()); - return FluentIterable.from(branches); + return branches; } private static class BranchComparator implements Comparator<BranchInfo> { @@ -184,61 +175,6 @@ } } - private FluentIterable<BranchInfo> filterBranches( - FluentIterable<BranchInfo> branches) throws BadRequestException { - if (!Strings.isNullOrEmpty(matchSubstring)) { - branches = branches.filter(new SubstringPredicate(matchSubstring)); - } else if (!Strings.isNullOrEmpty(matchRegex)) { - branches = branches.filter(new RegexPredicate(matchRegex)); - } - return branches; - } - - private static class SubstringPredicate implements Predicate<BranchInfo> { - private final String substring; - - private SubstringPredicate(String substring) { - this.substring = substring.toLowerCase(Locale.US); - } - - @Override - public boolean apply(BranchInfo in) { - String ref = in.ref; - if (ref.startsWith(Constants.R_HEADS)) { - ref = ref.substring(Constants.R_HEADS.length()); - } - ref = ref.toLowerCase(Locale.US); - return ref.contains(substring); - } - } - - private static class RegexPredicate implements Predicate<BranchInfo> { - private final RunAutomaton a; - - private RegexPredicate(String regex) throws BadRequestException { - if (regex.startsWith("^")) { - regex = regex.substring(1); - if (regex.endsWith("$") && !regex.endsWith("\\$")) { - regex = regex.substring(0, regex.length() - 1); - } - } - try { - a = new RunAutomaton(new RegExp(regex).toAutomaton()); - } catch (IllegalArgumentException e) { - throw new BadRequestException(e.getMessage()); - } - } - - @Override - public boolean apply(BranchInfo in) { - if (!in.ref.startsWith(Constants.R_HEADS)){ - return a.run(in.ref); - } else { - return a.run(in.ref.substring(Constants.R_HEADS.length())); - } - } - } - private BranchInfo createBranchInfo(Ref ref, RefControl refControl, Set<String> targets) { BranchInfo info = new BranchInfo();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListTags.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListTags.java index 1c140e3..83759d1 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListTags.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListTags.java
@@ -16,7 +16,8 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; -import com.google.gerrit.extensions.common.TagInfo; +import com.google.gerrit.extensions.api.projects.TagInfo; +import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.extensions.restapi.RestReadView; @@ -29,7 +30,6 @@ import com.google.gerrit.server.git.VisibleRefFilter; import com.google.inject.Inject; import com.google.inject.Provider; -import com.google.inject.Singleton; import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.errors.RepositoryNotFoundException; @@ -39,6 +39,7 @@ import org.eclipse.jgit.revwalk.RevObject; import org.eclipse.jgit.revwalk.RevTag; import org.eclipse.jgit.revwalk.RevWalk; +import org.kohsuke.args4j.Option; import java.io.IOException; import java.util.Collections; @@ -46,13 +47,37 @@ import java.util.List; import java.util.Map; -@Singleton public class ListTags implements RestReadView<ProjectResource> { private final GitRepositoryManager repoManager; private final Provider<ReviewDb> dbProvider; private final TagCache tagCache; private final ChangeCache changeCache; + @Option(name = "--limit", aliases = {"-n"}, metaVar = "CNT", usage = "maximum number of tags to list") + public void setLimit(int limit) { + this.limit = limit; + } + + @Option(name = "--start", aliases = {"-s"}, metaVar = "CNT", usage = "number of tags to skip") + public void setStart(int start) { + this.start = start; + } + + @Option(name = "--match", aliases = {"-m"}, metaVar = "MATCH", usage = "match tags substring") + public void setMatchSubstring(String matchSubstring) { + this.matchSubstring = matchSubstring; + } + + @Option(name = "--regex", aliases = {"-r"}, metaVar = "REGEX", usage = "match tags regex") + public void setMatchRegex(String matchRegex) { + this.matchRegex = matchRegex; + } + + private int limit; + private int start; + private String matchSubstring; + private String matchRegex; + @Inject public ListTags(GitRepositoryManager repoManager, Provider<ReviewDb> dbProvider, @@ -66,19 +91,15 @@ @Override public List<TagInfo> apply(ProjectResource resource) throws IOException, - ResourceNotFoundException { + ResourceNotFoundException, BadRequestException { List<TagInfo> tags = Lists.newArrayList(); - try (Repository repo = getRepository(resource.getNameKey())) { - RevWalk rw = new RevWalk(repo); - try { - Map<String, Ref> all = visibleTags(resource.getControl(), repo, - repo.getRefDatabase().getRefs(Constants.R_TAGS)); - for (Ref ref : all.values()) { - tags.add(createTagInfo(ref, rw)); - } - } finally { - rw.dispose(); + try (Repository repo = getRepository(resource.getNameKey()); + RevWalk rw = new RevWalk(repo)) { + Map<String, Ref> all = visibleTags(resource.getControl(), repo, + repo.getRefDatabase().getRefs(Constants.R_TAGS)); + for (Ref ref : all.values()) { + tags.add(createTagInfo(ref, rw)); } } @@ -89,7 +110,12 @@ } }); - return tags; + return new RefFilter<TagInfo>(Constants.R_TAGS) + .start(start) + .limit(limit) + .subString(matchSubstring) + .regex(matchRegex) + .filter(tags); } public TagInfo get(ProjectResource resource, IdString id)
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefFilter.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefFilter.java new file mode 100644 index 0000000..63fb595 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefFilter.java
@@ -0,0 +1,120 @@ +// Copyright (C) 2015 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.common.base.Predicate; +import com.google.common.base.Strings; +import com.google.common.collect.FluentIterable; +import com.google.gerrit.extensions.api.projects.RefInfo; +import com.google.gerrit.extensions.restapi.BadRequestException; + +import dk.brics.automaton.RegExp; +import dk.brics.automaton.RunAutomaton; + +import java.util.List; +import java.util.Locale; + +public class RefFilter<T extends RefInfo> { + private final String prefix; + private String matchSubstring; + private String matchRegex; + private int start; + private int limit; + + public RefFilter(String prefix) { + this.prefix = prefix; + } + + public RefFilter<T> subString(String subString) { + this.matchSubstring = subString; + return this; + } + + public RefFilter<T> regex(String regex) { + this.matchRegex = regex; + return this; + } + + public RefFilter<T> start(int start) { + this.start = start; + return this; + } + + public RefFilter<T> limit(int limit) { + this.limit = limit; + return this; + } + + public List<T> filter(List<T> refs) throws BadRequestException { + FluentIterable<T> results = FluentIterable.from(refs); + if (!Strings.isNullOrEmpty(matchSubstring)) { + results = results.filter(new SubstringPredicate(matchSubstring)); + } else if (!Strings.isNullOrEmpty(matchRegex)) { + results = results.filter(new RegexPredicate(matchRegex)); + } + if (start > 0) { + results = results.skip(start); + } + if (limit > 0) { + results = results.limit(limit); + } + return results.toList(); + } + + private class SubstringPredicate implements Predicate<T> { + private final String substring; + + private SubstringPredicate(String substring) { + this.substring = substring.toLowerCase(Locale.US); + } + + @Override + public boolean apply(T in) { + String ref = in.ref; + if (ref.startsWith(prefix)) { + ref = ref.substring(prefix.length()); + } + ref = ref.toLowerCase(Locale.US); + return ref.contains(substring); + } + } + + private class RegexPredicate implements Predicate<T> { + private final RunAutomaton a; + + private RegexPredicate(String regex) throws BadRequestException { + if (regex.startsWith("^")) { + regex = regex.substring(1); + if (regex.endsWith("$") && !regex.endsWith("\\$")) { + regex = regex.substring(0, regex.length() - 1); + } + } + try { + a = new RunAutomaton(new RegExp(regex).toAutomaton()); + } catch (IllegalArgumentException e) { + throw new BadRequestException(e.getMessage()); + } + } + + @Override + public boolean apply(T in) { + String ref = in.ref; + if (ref.startsWith(prefix)) { + ref = ref.substring(prefix.length()); + } + return a.run(ref); + } + } +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/TagResource.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/TagResource.java index 12be5d3..afbd3be 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/TagResource.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/TagResource.java
@@ -14,7 +14,7 @@ package com.google.gerrit.server.project; -import com.google.gerrit.extensions.common.TagInfo; +import com.google.gerrit.extensions.api.projects.TagInfo; import com.google.gerrit.extensions.restapi.RestView; import com.google.inject.TypeLiteral;