blob: cc11638a04574ba7e95cd6593f33c8a601c3ba4f [file] [log] [blame]
// Copyright (C) 2020 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.httpd.raw;
import static com.google.common.collect.ImmutableList.toImmutableList;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.primitives.Ints;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.UsedAt;
import com.google.gerrit.common.UsedAt.Project;
import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.restapi.Url;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
/** Helper for generating preloading parts of {@code index.html}. */
@UsedAt(Project.GOOGLE)
public class IndexPreloadingUtil {
enum RequestedPage {
CHANGE,
DIFF,
DASHBOARD,
PROFILE,
PAGE_WITHOUT_PRELOADING,
}
public static final String CHANGE_CANONICAL_PATH = "/c/(?<project>.+)/\\+/(?<changeNum>\\d+)";
public static final String BASE_PATCH_NUM_PATH_PART = "(/(-?\\d+|edit)(\\.\\.(\\d+|edit))?)";
public static final Pattern CHANGE_URL_PATTERN =
Pattern.compile(CHANGE_CANONICAL_PATH + BASE_PATCH_NUM_PATH_PART + "?" + "/?$");
public static final Pattern DIFF_URL_PATTERN =
Pattern.compile(CHANGE_CANONICAL_PATH + BASE_PATCH_NUM_PATH_PART + "(/(.+))" + "/?$");
public static final Pattern DASHBOARD_PATTERN = Pattern.compile("/dashboard/self$");
public static final Pattern PROFILE_PATTERN = Pattern.compile("/profile/self$");
public static final String ROOT_PATH = "/";
// These queries should be kept in sync with PolyGerrit:
// polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.ts
public static final String DASHBOARD_HAS_UNPUBLISHED_DRAFTS_QUERY = "has:draft limit:10";
public static final String YOUR_TURN = "attention:${user} limit:25";
public static final String DASHBOARD_WORK_IN_PROGRESS_QUERY =
"is:open owner:${user} is:wip limit:25";
public static final String DASHBOARD_OUTGOING_QUERY = "is:open owner:${user} -is:wip limit:25";
public static final String DASHBOARD_INCOMING_QUERY =
"is:open -owner:${user} -is:wip reviewer:${user} limit:25";
public static final String CC_QUERY = "is:open -is:wip cc:${user} limit:10";
public static final String DASHBOARD_RECENTLY_CLOSED_QUERY =
"is:closed (-is:wip OR owner:self) "
+ "(owner:${user} OR reviewer:${user} OR cc:${user}) "
+ "-age:4w limit:10";
public static final String NEW_USER = "owner:${user} limit:1";
public static final String SELF_DASHBOARD_HAS_UNPUBLISHED_DRAFTS_QUERY =
DASHBOARD_HAS_UNPUBLISHED_DRAFTS_QUERY.replaceAll("\\$\\{user}", "self");
public static final String SELF_YOUR_TURN = YOUR_TURN.replaceAll("\\$\\{user}", "self");
public static final ImmutableList<String> SELF_DASHBOARD_QUERIES =
Stream.of(
DASHBOARD_WORK_IN_PROGRESS_QUERY,
DASHBOARD_OUTGOING_QUERY,
DASHBOARD_INCOMING_QUERY,
CC_QUERY,
DASHBOARD_RECENTLY_CLOSED_QUERY,
NEW_USER)
.map(query -> query.replaceAll("\\$\\{user}", "self"))
.collect(toImmutableList());
public static final ImmutableSet<ListChangesOption> DASHBOARD_OPTIONS =
ImmutableSet.of(
ListChangesOption.LABELS,
ListChangesOption.DETAILED_ACCOUNTS,
ListChangesOption.SUBMIT_REQUIREMENTS,
ListChangesOption.STAR);
public static final ImmutableSet<ListChangesOption> CHANGE_DETAIL_OPTIONS =
ImmutableSet.of(
ListChangesOption.ALL_COMMITS,
ListChangesOption.ALL_REVISIONS,
ListChangesOption.CHANGE_ACTIONS,
ListChangesOption.DETAILED_ACCOUNTS,
ListChangesOption.DETAILED_LABELS,
ListChangesOption.DOWNLOAD_COMMANDS,
ListChangesOption.MESSAGES,
ListChangesOption.REVIEWER_UPDATES,
ListChangesOption.SUBMITTABLE,
ListChangesOption.WEB_LINKS,
ListChangesOption.SKIP_DIFFSTAT,
ListChangesOption.SUBMIT_REQUIREMENTS,
ListChangesOption.PARENTS);
@Nullable
public static String getPath(@Nullable String requestedURL) throws URISyntaxException {
if (requestedURL == null) {
return null;
}
URI uri = new URI(requestedURL);
return uri.getPath();
}
public static RequestedPage parseRequestedPage(@Nullable String requestedPath) {
if (requestedPath == null) {
return RequestedPage.PAGE_WITHOUT_PRELOADING;
}
Optional<String> changeRequestsPath =
computeChangeRequestsPath(requestedPath, RequestedPage.CHANGE);
if (changeRequestsPath.isPresent()) {
return RequestedPage.CHANGE;
}
changeRequestsPath = computeChangeRequestsPath(requestedPath, RequestedPage.DIFF);
if (changeRequestsPath.isPresent()) {
return RequestedPage.DIFF;
}
Matcher dashboardMatcher = IndexPreloadingUtil.DASHBOARD_PATTERN.matcher(requestedPath);
if (dashboardMatcher.matches()) {
return RequestedPage.DASHBOARD;
}
Matcher profileMatcher = IndexPreloadingUtil.PROFILE_PATTERN.matcher(requestedPath);
if (profileMatcher.matches()) {
return RequestedPage.PROFILE;
}
if (ROOT_PATH.equals(requestedPath)) {
return RequestedPage.DASHBOARD;
}
return RequestedPage.PAGE_WITHOUT_PRELOADING;
}
public static Optional<String> computeChangeRequestsPath(
String requestedURL, RequestedPage page) {
Matcher matcher;
switch (page) {
case CHANGE:
matcher = CHANGE_URL_PATTERN.matcher(requestedURL);
break;
case DIFF:
matcher = DIFF_URL_PATTERN.matcher(requestedURL);
break;
case DASHBOARD:
case PROFILE:
case PAGE_WITHOUT_PRELOADING:
default:
return Optional.empty();
}
if (matcher.matches()) {
Integer changeId = Ints.tryParse(matcher.group("changeNum"));
if (changeId != null) {
return Optional.of("changes/" + Url.encode(matcher.group("project")) + "~" + changeId);
}
}
return Optional.empty();
}
public static Optional<Integer> computeChangeNum(String requestedURL, RequestedPage page) {
Matcher matcher;
switch (page) {
case CHANGE:
matcher = CHANGE_URL_PATTERN.matcher(requestedURL);
break;
case DIFF:
matcher = DIFF_URL_PATTERN.matcher(requestedURL);
break;
case DASHBOARD:
case PROFILE:
case PAGE_WITHOUT_PRELOADING:
default:
return Optional.empty();
}
if (matcher.matches()) {
Integer changeNum = Ints.tryParse(matcher.group("changeNum"));
if (changeNum != null) {
return Optional.of(changeNum);
}
}
return Optional.empty();
}
public static List<String> computeDashboardQueryList() {
List<String> queryList = new ArrayList<>();
queryList.add(SELF_DASHBOARD_HAS_UNPUBLISHED_DRAFTS_QUERY);
queryList.add(SELF_YOUR_TURN);
queryList.addAll(SELF_DASHBOARD_QUERIES);
return queryList;
}
private IndexPreloadingUtil() {}
}