blob: 6851688ede023f7d663fa9315d9b0950162f7a3f [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;
David Pursehouse6740b3e2019-07-17 11:06:08 +090020import com.google.common.collect.ImmutableList;
Dave Borowitz9de65952012-08-13 16:09:45 -070021import com.google.common.collect.ImmutableMap;
22import com.google.common.collect.Sets;
Masaya Suzuki5cecb862019-03-25 17:35:44 -070023import com.google.gitiles.GitilesRequestFailureException.FailureReason;
Dave Borowitz9de65952012-08-13 16:09:45 -070024import com.google.gson.reflect.TypeToken;
David Pursehousef35b06c2016-08-20 11:10:42 +090025import com.google.template.soy.data.restricted.NullData;
Dave Borowitz9de65952012-08-13 16:09:45 -070026import java.io.IOException;
Dave Borowitz673d1982014-05-02 12:30:49 -070027import java.io.Writer;
Dave Borowitz27058932014-12-03 15:44:46 -080028import java.util.Collections;
Shawn Pearcec709c4c2015-08-28 15:30:42 -070029import java.util.LinkedHashMap;
30import java.util.List;
Dave Borowitz9de65952012-08-13 16:09:45 -070031import java.util.Map;
David Pursehouse7a7f5472016-10-14 09:59:20 +090032import java.util.Optional;
Dave Borowitz9de65952012-08-13 16:09:45 -070033import java.util.Set;
Shawn Pearcec709c4c2015-08-28 15:30:42 -070034import javax.annotation.Nullable;
Dave Borowitz9de65952012-08-13 16:09:45 -070035import javax.servlet.http.HttpServletRequest;
36import javax.servlet.http.HttpServletResponse;
Dave Borowitz3b744b12016-08-19 16:11:10 -040037import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
38import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
Dave Borowitz9de65952012-08-13 16:09:45 -070039
40/** Serves the top level index page for a Gitiles host. */
41public class HostIndexServlet extends BaseServlet {
Chad Horohoead23f142012-11-12 09:45:39 -080042 private static final long serialVersionUID = 1L;
Dave Borowitz9de65952012-08-13 16:09:45 -070043
44 protected final GitilesUrls urls;
Dave Borowitz9de65952012-08-13 16:09:45 -070045
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020046 public HostIndexServlet(
47 GitilesAccess.Factory accessFactory, Renderer renderer, GitilesUrls urls) {
Dave Borowitz8d6d6872014-03-16 15:18:14 -070048 super(renderer, accessFactory);
Dave Borowitz9de65952012-08-13 16:09:45 -070049 this.urls = checkNotNull(urls, "urls");
Dave Borowitz9de65952012-08-13 16:09:45 -070050 }
51
Shawn Pearcec709c4c2015-08-28 15:30:42 -070052 private Map<String, RepositoryDescription> list(
David Pursehouse3484ab82019-05-13 06:55:07 +020053 HttpServletRequest req, String prefix, Set<String> branches) throws IOException {
Dave Borowitz9de65952012-08-13 16:09:45 -070054 Map<String, RepositoryDescription> descs;
55 try {
Shawn Pearcec709c4c2015-08-28 15:30:42 -070056 descs = getAccess(req).listRepositories(prefix, branches);
Dave Borowitz9de65952012-08-13 16:09:45 -070057 } catch (ServiceNotEnabledException e) {
Masaya Suzuki5cecb862019-03-25 17:35:44 -070058 throw new GitilesRequestFailureException(FailureReason.SERVICE_NOT_ENABLED, e);
Dave Borowitz9de65952012-08-13 16:09:45 -070059 } catch (ServiceNotAuthorizedException e) {
Masaya Suzuki5cecb862019-03-25 17:35:44 -070060 throw new GitilesRequestFailureException(FailureReason.NOT_AUTHORIZED, e);
Dave Borowitz9de65952012-08-13 16:09:45 -070061 }
Shawn Pearcec709c4c2015-08-28 15:30:42 -070062 if (prefix != null && descs.isEmpty()) {
Masaya Suzuki5cecb862019-03-25 17:35:44 -070063 throw new GitilesRequestFailureException(FailureReason.REPOSITORY_NOT_FOUND);
Shawn Pearcec709c4c2015-08-28 15:30:42 -070064 }
Dave Borowitzb1c628f2013-01-11 11:28:20 -080065 return descs;
Dave Borowitz9de65952012-08-13 16:09:45 -070066 }
67
David Pursehouse6740b3e2019-07-17 11:06:08 +090068 private Map<String, Object> toMapData(
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020069 RepositoryDescription desc, @Nullable String prefix, GitilesView view) {
David Pursehouse6740b3e2019-07-17 11:06:08 +090070 return ImmutableMap.<String, Object>builder()
71 .put("name", stripPrefix(prefix, desc.name))
72 .put("description", Strings.nullToEmpty(desc.description))
73 .put(
74 "url",
75 GitilesView.repositoryIndex().copyFrom(view).setRepositoryName(desc.name).toUrl())
76 .build();
Dave Borowitz9de65952012-08-13 16:09:45 -070077 }
78
Dave Borowitzb1c628f2013-01-11 11:28:20 -080079 @Override
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020080 protected void doHead(HttpServletRequest req, HttpServletResponse res) throws IOException {
Shawn Pearce10e68e62016-01-02 09:37:58 -080081 Optional<FormatType> format = getFormat(req);
82 if (!format.isPresent()) {
Masaya Suzuki5cecb862019-03-25 17:35:44 -070083 throw new GitilesRequestFailureException(FailureReason.UNSUPPORTED_RESPONSE_FORMAT);
Shawn Pearce10e68e62016-01-02 09:37:58 -080084 }
85
86 GitilesView view = ViewFilter.getView(req);
87 String prefix = view.getRepositoryPrefix();
88 if (prefix != null) {
David Pursehouse3484ab82019-05-13 06:55:07 +020089 Map<String, RepositoryDescription> descs = list(req, prefix, Collections.emptySet());
Shawn Pearce10e68e62016-01-02 09:37:58 -080090 if (descs == null) {
91 return;
92 }
93 }
94 switch (format.get()) {
95 case HTML:
96 case JSON:
97 case TEXT:
98 res.setStatus(HttpServletResponse.SC_OK);
99 res.setContentType(format.get().getMimeType());
100 break;
David Pursehousecb91aaf2016-06-15 22:05:24 +0900101 case DEFAULT:
Shawn Pearce10e68e62016-01-02 09:37:58 -0800102 default:
Masaya Suzuki5cecb862019-03-25 17:35:44 -0700103 throw new GitilesRequestFailureException(FailureReason.UNSUPPORTED_RESPONSE_FORMAT);
Shawn Pearce10e68e62016-01-02 09:37:58 -0800104 }
105 }
106
107 @Override
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800108 protected void doGetHtml(HttpServletRequest req, HttpServletResponse res) throws IOException {
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700109 GitilesView view = ViewFilter.getView(req);
110 String prefix = view.getRepositoryPrefix();
David Pursehouse3484ab82019-05-13 06:55:07 +0200111 Map<String, RepositoryDescription> descs = list(req, prefix, parseShowBranch(req));
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800112 if (descs == null) {
113 return;
114 }
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700115
David Pursehouse6740b3e2019-07-17 11:06:08 +0900116 ImmutableList.Builder<Map<String, Object>> repos = ImmutableList.builder();
Dave Borowitz9de65952012-08-13 16:09:45 -0700117 for (RepositoryDescription desc : descs.values()) {
David Pursehouse969bfc82015-12-08 15:11:17 +0900118 if (prefix == null || desc.name.startsWith(prefix)) {
David Pursehouse6740b3e2019-07-17 11:06:08 +0900119 repos.add(toMapData(desc, prefix, view));
David Pursehouse969bfc82015-12-08 15:11:17 +0900120 }
Dave Borowitz9de65952012-08-13 16:09:45 -0700121 }
122
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700123 String hostName = urls.getHostName(req);
124 List<Map<String, String>> breadcrumbs = null;
125 if (prefix != null) {
126 hostName = hostName + '/' + prefix;
127 breadcrumbs = view.getBreadcrumbs();
128 }
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200129 renderHtml(
130 req,
131 res,
132 "gitiles.hostIndex",
133 ImmutableMap.of(
134 "hostName",
135 hostName,
136 "breadcrumbs",
David Pursehouse6740b3e2019-07-17 11:06:08 +0900137 breadcrumbs != null ? breadcrumbs : NullData.INSTANCE,
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200138 "prefix",
139 prefix != null ? prefix + '/' : "",
140 "repositories",
David Pursehouse6740b3e2019-07-17 11:06:08 +0900141 repos.build()));
Dave Borowitz9de65952012-08-13 16:09:45 -0700142 }
143
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800144 @Override
145 protected void doGetText(HttpServletRequest req, HttpServletResponse res) throws IOException {
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700146 String prefix = ViewFilter.getView(req).getRepositoryPrefix();
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800147 Set<String> branches = parseShowBranch(req);
David Pursehouse3484ab82019-05-13 06:55:07 +0200148 Map<String, RepositoryDescription> descs = list(req, prefix, branches);
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800149 if (descs == null) {
150 return;
151 }
Dave Borowitz9de65952012-08-13 16:09:45 -0700152
David Pursehousec3e772a2016-06-15 21:49:35 +0900153 try (Writer writer = startRenderText(req, res)) {
154 for (RepositoryDescription repo : descs.values()) {
155 for (String name : branches) {
156 String ref = repo.branches.get(name);
157 if (ref == null) {
158 // Print stub (forty '-' symbols)
159 ref = "----------------------------------------";
160 }
161 writer.write(ref);
162 writer.write(' ');
Dave Borowitz9de65952012-08-13 16:09:45 -0700163 }
David Pursehousefa845722016-10-04 16:26:17 +0900164 writer.write(GitilesUrls.escapeName(stripPrefix(prefix, repo.name)));
David Pursehousec3e772a2016-06-15 21:49:35 +0900165 writer.write('\n');
Dave Borowitz9de65952012-08-13 16:09:45 -0700166 }
Dave Borowitz9de65952012-08-13 16:09:45 -0700167 }
Dave Borowitz9de65952012-08-13 16:09:45 -0700168 }
169
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800170 @Override
171 protected void doGetJson(HttpServletRequest req, HttpServletResponse res) throws IOException {
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700172 String prefix = ViewFilter.getView(req).getRepositoryPrefix();
David Pursehouse3484ab82019-05-13 06:55:07 +0200173 Map<String, RepositoryDescription> descs = list(req, prefix, parseShowBranch(req));
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800174 if (descs == null) {
175 return;
176 }
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700177 if (prefix != null) {
178 Map<String, RepositoryDescription> r = new LinkedHashMap<>();
179 for (Map.Entry<String, RepositoryDescription> e : descs.entrySet()) {
180 r.put(stripPrefix(prefix, e.getKey()), e.getValue());
181 }
182 descs = r;
183 }
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800184 renderJson(req, res, descs, new TypeToken<Map<String, RepositoryDescription>>() {}.getType());
Dave Borowitz9de65952012-08-13 16:09:45 -0700185 }
186
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700187 private static String stripPrefix(@Nullable String prefix, String name) {
David Pursehouse969bfc82015-12-08 15:11:17 +0900188 if (prefix != null && name.startsWith(prefix)) {
189 return name.substring(prefix.length() + 1);
190 }
191 return name;
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700192 }
193
Dave Borowitz9de65952012-08-13 16:09:45 -0700194 private static Set<String> parseShowBranch(HttpServletRequest req) {
195 // Roughly match Gerrit Code Review's /projects/ API by supporting
196 // both show-branch and b as query parameters.
197 Set<String> branches = Sets.newLinkedHashSet();
198 String[] values = req.getParameterValues("show-branch");
199 if (values != null) {
Dave Borowitz27058932014-12-03 15:44:46 -0800200 Collections.addAll(branches, values);
Dave Borowitz9de65952012-08-13 16:09:45 -0700201 }
202 values = req.getParameterValues("b");
203 if (values != null) {
Dave Borowitz27058932014-12-03 15:44:46 -0800204 Collections.addAll(branches, values);
Dave Borowitz9de65952012-08-13 16:09:45 -0700205 }
206 return branches;
207 }
208}