blob: 8b8a2525ff30c912db6ae3a232fffe81c7e1bd36 [file] [log] [blame]
Dave Borowitz9de65952012-08-13 16:09:45 -07001// Copyright 2012 Google Inc. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package com.google.gitiles;
16
17import static com.google.common.base.Preconditions.checkNotNull;
Dave Borowitz9de65952012-08-13 16:09:45 -070018
19import com.google.common.base.Strings;
20import com.google.common.collect.ImmutableMap;
21import com.google.common.collect.Sets;
Masaya Suzuki5cecb862019-03-25 17:35:44 -070022import com.google.gitiles.GitilesRequestFailureException.FailureReason;
Dave Borowitz9de65952012-08-13 16:09:45 -070023import com.google.gson.reflect.TypeToken;
24import com.google.template.soy.data.SoyListData;
25import com.google.template.soy.data.SoyMapData;
David Pursehousef35b06c2016-08-20 11:10:42 +090026import com.google.template.soy.data.restricted.NullData;
Dave Borowitz9de65952012-08-13 16:09:45 -070027import java.io.IOException;
Dave Borowitz673d1982014-05-02 12:30:49 -070028import java.io.Writer;
Dave Borowitz27058932014-12-03 15:44:46 -080029import java.util.Collections;
Shawn Pearcec709c4c2015-08-28 15:30:42 -070030import java.util.LinkedHashMap;
31import java.util.List;
Dave Borowitz9de65952012-08-13 16:09:45 -070032import java.util.Map;
David Pursehouse7a7f5472016-10-14 09:59:20 +090033import java.util.Optional;
Dave Borowitz9de65952012-08-13 16:09:45 -070034import java.util.Set;
Shawn Pearcec709c4c2015-08-28 15:30:42 -070035import javax.annotation.Nullable;
Dave Borowitz9de65952012-08-13 16:09:45 -070036import javax.servlet.http.HttpServletRequest;
37import javax.servlet.http.HttpServletResponse;
Dave Borowitz3b744b12016-08-19 16:11:10 -040038import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
39import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
Dave Borowitz9de65952012-08-13 16:09:45 -070040
41/** Serves the top level index page for a Gitiles host. */
42public class HostIndexServlet extends BaseServlet {
Chad Horohoead23f142012-11-12 09:45:39 -080043 private static final long serialVersionUID = 1L;
Dave Borowitz9de65952012-08-13 16:09:45 -070044
45 protected final GitilesUrls urls;
Dave Borowitz9de65952012-08-13 16:09:45 -070046
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020047 public HostIndexServlet(
48 GitilesAccess.Factory accessFactory, Renderer renderer, GitilesUrls urls) {
Dave Borowitz8d6d6872014-03-16 15:18:14 -070049 super(renderer, accessFactory);
Dave Borowitz9de65952012-08-13 16:09:45 -070050 this.urls = checkNotNull(urls, "urls");
Dave Borowitz9de65952012-08-13 16:09:45 -070051 }
52
Shawn Pearcec709c4c2015-08-28 15:30:42 -070053 private Map<String, RepositoryDescription> list(
David Pursehouse3484ab82019-05-13 06:55:07 +020054 HttpServletRequest req, String prefix, Set<String> branches) throws IOException {
Dave Borowitz9de65952012-08-13 16:09:45 -070055 Map<String, RepositoryDescription> descs;
56 try {
Shawn Pearcec709c4c2015-08-28 15:30:42 -070057 descs = getAccess(req).listRepositories(prefix, branches);
Dave Borowitz9de65952012-08-13 16:09:45 -070058 } catch (ServiceNotEnabledException e) {
Masaya Suzuki5cecb862019-03-25 17:35:44 -070059 throw new GitilesRequestFailureException(FailureReason.SERVICE_NOT_ENABLED, e);
Dave Borowitz9de65952012-08-13 16:09:45 -070060 } catch (ServiceNotAuthorizedException e) {
Masaya Suzuki5cecb862019-03-25 17:35:44 -070061 throw new GitilesRequestFailureException(FailureReason.NOT_AUTHORIZED, e);
Dave Borowitz9de65952012-08-13 16:09:45 -070062 }
Shawn Pearcec709c4c2015-08-28 15:30:42 -070063 if (prefix != null && descs.isEmpty()) {
Masaya Suzuki5cecb862019-03-25 17:35:44 -070064 throw new GitilesRequestFailureException(FailureReason.REPOSITORY_NOT_FOUND);
Shawn Pearcec709c4c2015-08-28 15:30:42 -070065 }
Dave Borowitzb1c628f2013-01-11 11:28:20 -080066 return descs;
Dave Borowitz9de65952012-08-13 16:09:45 -070067 }
68
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020069 private SoyMapData toSoyMapData(
70 RepositoryDescription desc, @Nullable String prefix, GitilesView view) {
Dave Borowitz9de65952012-08-13 16:09:45 -070071 return new SoyMapData(
Shawn Pearcec709c4c2015-08-28 15:30:42 -070072 "name", stripPrefix(prefix, desc.name),
Dave Borowitz9de65952012-08-13 16:09:45 -070073 "description", Strings.nullToEmpty(desc.description),
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020074 "url", GitilesView.repositoryIndex().copyFrom(view).setRepositoryName(desc.name).toUrl());
Dave Borowitz9de65952012-08-13 16:09:45 -070075 }
76
Dave Borowitzb1c628f2013-01-11 11:28:20 -080077 @Override
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020078 protected void doHead(HttpServletRequest req, HttpServletResponse res) throws IOException {
Shawn Pearce10e68e62016-01-02 09:37:58 -080079 Optional<FormatType> format = getFormat(req);
80 if (!format.isPresent()) {
Masaya Suzuki5cecb862019-03-25 17:35:44 -070081 throw new GitilesRequestFailureException(FailureReason.UNSUPPORTED_RESPONSE_FORMAT);
Shawn Pearce10e68e62016-01-02 09:37:58 -080082 }
83
84 GitilesView view = ViewFilter.getView(req);
85 String prefix = view.getRepositoryPrefix();
86 if (prefix != null) {
David Pursehouse3484ab82019-05-13 06:55:07 +020087 Map<String, RepositoryDescription> descs = list(req, prefix, Collections.emptySet());
Shawn Pearce10e68e62016-01-02 09:37:58 -080088 if (descs == null) {
89 return;
90 }
91 }
92 switch (format.get()) {
93 case HTML:
94 case JSON:
95 case TEXT:
96 res.setStatus(HttpServletResponse.SC_OK);
97 res.setContentType(format.get().getMimeType());
98 break;
David Pursehousecb91aaf2016-06-15 22:05:24 +090099 case DEFAULT:
Shawn Pearce10e68e62016-01-02 09:37:58 -0800100 default:
Masaya Suzuki5cecb862019-03-25 17:35:44 -0700101 throw new GitilesRequestFailureException(FailureReason.UNSUPPORTED_RESPONSE_FORMAT);
Shawn Pearce10e68e62016-01-02 09:37:58 -0800102 }
103 }
104
105 @Override
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800106 protected void doGetHtml(HttpServletRequest req, HttpServletResponse res) throws IOException {
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700107 GitilesView view = ViewFilter.getView(req);
108 String prefix = view.getRepositoryPrefix();
David Pursehouse3484ab82019-05-13 06:55:07 +0200109 Map<String, RepositoryDescription> descs = list(req, prefix, parseShowBranch(req));
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800110 if (descs == null) {
111 return;
112 }
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700113
Dave Borowitz9de65952012-08-13 16:09:45 -0700114 SoyListData repos = new SoyListData();
115 for (RepositoryDescription desc : descs.values()) {
David Pursehouse969bfc82015-12-08 15:11:17 +0900116 if (prefix == null || desc.name.startsWith(prefix)) {
117 repos.add(toSoyMapData(desc, prefix, view));
118 }
Dave Borowitz9de65952012-08-13 16:09:45 -0700119 }
120
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700121 String hostName = urls.getHostName(req);
122 List<Map<String, String>> breadcrumbs = null;
123 if (prefix != null) {
124 hostName = hostName + '/' + prefix;
125 breadcrumbs = view.getBreadcrumbs();
126 }
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200127 renderHtml(
128 req,
129 res,
130 "gitiles.hostIndex",
131 ImmutableMap.of(
132 "hostName",
133 hostName,
134 "breadcrumbs",
David Pursehousef35b06c2016-08-20 11:10:42 +0900135 breadcrumbs != null ? new SoyListData(breadcrumbs) : NullData.INSTANCE,
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200136 "prefix",
137 prefix != null ? prefix + '/' : "",
138 "repositories",
139 repos));
Dave Borowitz9de65952012-08-13 16:09:45 -0700140 }
141
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800142 @Override
143 protected void doGetText(HttpServletRequest req, HttpServletResponse res) throws IOException {
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700144 String prefix = ViewFilter.getView(req).getRepositoryPrefix();
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800145 Set<String> branches = parseShowBranch(req);
David Pursehouse3484ab82019-05-13 06:55:07 +0200146 Map<String, RepositoryDescription> descs = list(req, prefix, branches);
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800147 if (descs == null) {
148 return;
149 }
Dave Borowitz9de65952012-08-13 16:09:45 -0700150
David Pursehousec3e772a2016-06-15 21:49:35 +0900151 try (Writer writer = startRenderText(req, res)) {
152 for (RepositoryDescription repo : descs.values()) {
153 for (String name : branches) {
154 String ref = repo.branches.get(name);
155 if (ref == null) {
156 // Print stub (forty '-' symbols)
157 ref = "----------------------------------------";
158 }
159 writer.write(ref);
160 writer.write(' ');
Dave Borowitz9de65952012-08-13 16:09:45 -0700161 }
David Pursehousefa845722016-10-04 16:26:17 +0900162 writer.write(GitilesUrls.escapeName(stripPrefix(prefix, repo.name)));
David Pursehousec3e772a2016-06-15 21:49:35 +0900163 writer.write('\n');
Dave Borowitz9de65952012-08-13 16:09:45 -0700164 }
Dave Borowitz9de65952012-08-13 16:09:45 -0700165 }
Dave Borowitz9de65952012-08-13 16:09:45 -0700166 }
167
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800168 @Override
169 protected void doGetJson(HttpServletRequest req, HttpServletResponse res) throws IOException {
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700170 String prefix = ViewFilter.getView(req).getRepositoryPrefix();
David Pursehouse3484ab82019-05-13 06:55:07 +0200171 Map<String, RepositoryDescription> descs = list(req, prefix, parseShowBranch(req));
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800172 if (descs == null) {
173 return;
174 }
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700175 if (prefix != null) {
176 Map<String, RepositoryDescription> r = new LinkedHashMap<>();
177 for (Map.Entry<String, RepositoryDescription> e : descs.entrySet()) {
178 r.put(stripPrefix(prefix, e.getKey()), e.getValue());
179 }
180 descs = r;
181 }
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800182 renderJson(req, res, descs, new TypeToken<Map<String, RepositoryDescription>>() {}.getType());
Dave Borowitz9de65952012-08-13 16:09:45 -0700183 }
184
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700185 private static String stripPrefix(@Nullable String prefix, String name) {
David Pursehouse969bfc82015-12-08 15:11:17 +0900186 if (prefix != null && name.startsWith(prefix)) {
187 return name.substring(prefix.length() + 1);
188 }
189 return name;
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700190 }
191
Dave Borowitz9de65952012-08-13 16:09:45 -0700192 private static Set<String> parseShowBranch(HttpServletRequest req) {
193 // Roughly match Gerrit Code Review's /projects/ API by supporting
194 // both show-branch and b as query parameters.
195 Set<String> branches = Sets.newLinkedHashSet();
196 String[] values = req.getParameterValues("show-branch");
197 if (values != null) {
Dave Borowitz27058932014-12-03 15:44:46 -0800198 Collections.addAll(branches, values);
Dave Borowitz9de65952012-08-13 16:09:45 -0700199 }
200 values = req.getParameterValues("b");
201 if (values != null) {
Dave Borowitz27058932014-12-03 15:44:46 -0800202 Collections.addAll(branches, values);
Dave Borowitz9de65952012-08-13 16:09:45 -0700203 }
204 return branches;
205 }
206}