summaryrefslogtreecommitdiffstats
path: root/java/com/google/gerrit/server/restapi/project/ListTags.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/google/gerrit/server/restapi/project/ListTags.java')
-rw-r--r--java/com/google/gerrit/server/restapi/project/ListTags.java235
1 files changed, 235 insertions, 0 deletions
diff --git a/java/com/google/gerrit/server/restapi/project/ListTags.java b/java/com/google/gerrit/server/restapi/project/ListTags.java
new file mode 100644
index 0000000000..f59e98421c
--- /dev/null
+++ b/java/com/google/gerrit/server/restapi/project/ListTags.java
@@ -0,0 +1,235 @@
+// 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.restapi.project;
+
+import static com.google.gerrit.reviewdb.client.RefNames.isConfigRef;
+import static java.util.Comparator.comparing;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.gerrit.extensions.api.projects.ProjectApi.ListRefsRequest;
+import com.google.gerrit.extensions.api.projects.TagInfo;
+import com.google.gerrit.extensions.common.WebLinkInfo;
+import com.google.gerrit.extensions.restapi.IdString;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.CommonConverters;
+import com.google.gerrit.server.WebLinks;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackend.RefFilterOptions;
+import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.permissions.RefPermission;
+import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.project.RefFilter;
+import com.google.inject.Inject;
+import java.io.IOException;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevTag;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.kohsuke.args4j.Option;
+
+public class ListTags implements RestReadView<ProjectResource> {
+ private final GitRepositoryManager repoManager;
+ private final PermissionBackend permissionBackend;
+ private final WebLinks links;
+
+ @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", "-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, PermissionBackend permissionBackend, WebLinks webLinks) {
+ this.repoManager = repoManager;
+ this.permissionBackend = permissionBackend;
+ this.links = webLinks;
+ }
+
+ public ListTags request(ListRefsRequest<TagInfo> request) {
+ this.setLimit(request.getLimit());
+ this.setStart(request.getStart());
+ this.setMatchSubstring(request.getSubstring());
+ this.setMatchRegex(request.getRegex());
+ return this;
+ }
+
+ @Override
+ public List<TagInfo> apply(ProjectResource resource)
+ throws IOException, ResourceNotFoundException, RestApiException, PermissionBackendException {
+ resource.getProjectState().checkStatePermitsRead();
+
+ List<TagInfo> tags = new ArrayList<>();
+
+ PermissionBackend.ForProject perm =
+ permissionBackend.currentUser().project(resource.getNameKey());
+ try (Repository repo = getRepository(resource.getNameKey());
+ RevWalk rw = new RevWalk(repo)) {
+ Map<String, Ref> all =
+ visibleTags(resource.getNameKey(), repo, repo.getRefDatabase().getRefs(Constants.R_TAGS));
+ for (Ref ref : all.values()) {
+ tags.add(
+ createTagInfo(perm.ref(ref.getName()), ref, rw, resource.getProjectState(), links));
+ }
+ }
+
+ tags.sort(comparing(t -> t.ref));
+
+ return new RefFilter<TagInfo>(Constants.R_TAGS)
+ .start(start)
+ .limit(limit)
+ .subString(matchSubstring)
+ .regex(matchRegex)
+ .filter(tags);
+ }
+
+ public TagInfo get(ProjectResource resource, IdString id)
+ throws ResourceNotFoundException, IOException, PermissionBackendException {
+ try (Repository repo = getRepository(resource.getNameKey());
+ RevWalk rw = new RevWalk(repo)) {
+ String tagName = id.get();
+ if (!tagName.startsWith(Constants.R_TAGS)) {
+ tagName = Constants.R_TAGS + tagName;
+ }
+ Ref ref = repo.getRefDatabase().exactRef(tagName);
+ if (ref != null
+ && !visibleTags(resource.getNameKey(), repo, ImmutableMap.of(ref.getName(), ref))
+ .isEmpty()) {
+ return createTagInfo(
+ permissionBackend
+ .user(resource.getUser())
+ .project(resource.getNameKey())
+ .ref(ref.getName()),
+ ref,
+ rw,
+ resource.getProjectState(),
+ links);
+ }
+ }
+ throw new ResourceNotFoundException(id);
+ }
+
+ public static TagInfo createTagInfo(
+ PermissionBackend.ForRef perm, Ref ref, RevWalk rw, ProjectState projectState, WebLinks links)
+ throws IOException {
+ RevObject object = rw.parseAny(ref.getObjectId());
+
+ Boolean canDelete = null;
+ if (!isConfigRef(ref.getName())) {
+ // Never allow to delete the meta config branch.
+ canDelete =
+ perm.testOrFalse(RefPermission.DELETE) && projectState.statePermitsWrite() ? true : null;
+ }
+
+ List<WebLinkInfo> webLinks = links.getTagLinks(projectState.getName(), ref.getName());
+ if (object instanceof RevTag) {
+ // Annotated or signed tag
+ RevTag tag = (RevTag) object;
+ PersonIdent tagger = tag.getTaggerIdent();
+ return new TagInfo(
+ ref.getName(),
+ tag.getName(),
+ tag.getObject().getName(),
+ tag.getFullMessage().trim(),
+ tagger != null ? CommonConverters.toGitPerson(tagger) : null,
+ canDelete,
+ webLinks.isEmpty() ? null : webLinks,
+ tagger != null ? new Timestamp(tagger.getWhen().getTime()) : null);
+ }
+
+ Timestamp timestamp =
+ object instanceof RevCommit
+ ? new Timestamp(((RevCommit) object).getCommitterIdent().getWhen().getTime())
+ : null;
+
+ // Lightweight tag
+ return new TagInfo(
+ ref.getName(),
+ ref.getObjectId().getName(),
+ canDelete,
+ webLinks.isEmpty() ? null : webLinks,
+ timestamp);
+ }
+
+ private Repository getRepository(Project.NameKey project)
+ throws ResourceNotFoundException, IOException {
+ try {
+ return repoManager.openRepository(project);
+ } catch (RepositoryNotFoundException noGitRepository) {
+ throw new ResourceNotFoundException();
+ }
+ }
+
+ private Map<String, Ref> visibleTags(
+ Project.NameKey project, Repository repo, Map<String, Ref> tags)
+ throws PermissionBackendException {
+ return permissionBackend
+ .currentUser()
+ .project(project)
+ .filter(
+ tags,
+ repo,
+ RefFilterOptions.builder().setFilterMeta(true).setFilterTagsSeparately(true).build());
+ }
+}