Skip to content

Commit

Permalink
Merge branch 'main' into fix/flow-router-check-client-route-conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
tepi committed Oct 11, 2024
2 parents c77298c + 4924daa commit cacbdd7
Show file tree
Hide file tree
Showing 16 changed files with 521 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -116,16 +116,9 @@ public void start(ValueMap initialUidl) {
registry.getMessageHandler().handleMessage(initialUidl);
}

if (BrowserInfo.get().isFirefox()) {
// Sends in beforeunload in FF (don't support beacon in pagehide)
Browser.getWindow().addEventListener("beforeunload", e -> {
registry.getMessageSender().sendUnloadBeacon();
});
} else {
Browser.getWindow().addEventListener("pagehide", e -> {
registry.getMessageSender().sendUnloadBeacon();
});
}
Browser.getWindow().addEventListener("pagehide", e -> {
registry.getMessageSender().sendUnloadBeacon();
});

Browser.getWindow().addEventListener("pageshow", e -> {
// Currently only Safari gets here, sometimes when going back/foward
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ private UIRefreshStrategy computeRefreshStrategy(UI ui,
// applied auto layout changed
RouteUtil.isAutolayoutEnabled(routeTarget.getTarget(),
currentPath)
&& registry.hasLayout(currentPath)
&& RouteUtil
.collectRouteParentLayouts(
registry.getLayout(currentPath))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import com.vaadin.flow.server.VaadinRequest;
import com.vaadin.flow.server.VaadinResponse;
import com.vaadin.flow.server.VaadinSession;
import com.vaadin.flow.shared.JsonConstants;

import elemental.json.Json;
import elemental.json.JsonObject;
import org.slf4j.Logger;
Expand Down Expand Up @@ -106,6 +108,7 @@ private void handleFound(VaadinResponse response,
response.setStatus(HttpStatusCode.OK.getCode());
response.setHeader(RETRIEVED_LOCALE_HEADER_NAME,
translationPropertyFile.getLocale().toLanguageTag());
response.setHeader("Content-Type", JsonConstants.JSON_CONTENT_TYPE);
writeFileToResponse(response, translationPropertyFile);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -615,11 +615,11 @@ public <T> void forwardTo(String location, List<T> locationParams) {
*/
public void forwardTo(String locationString,
QueryParameters queryParameters) {
final Optional<Class<? extends Component>> target = getSource()
.getRegistry().getNavigationTarget(locationString);
final Optional<NavigationState> navigationState = getSource()
.resolveNavigationTarget(new Location(locationString));
this.redirectQueryParameters = queryParameters;
if (target.isPresent()) {
forwardTo(getNavigationState(locationString, List.of()));
if (navigationState.isPresent()) {
forwardTo(navigationState.get());
} else {
// Inform that forward target location is not known.
unknownForward = PathUtil.trimPath(locationString);
Expand Down Expand Up @@ -910,12 +910,11 @@ public <T> void rerouteTo(String route, List<T> routeParams) {
* query parameters for the target
*/
public void rerouteTo(String route, QueryParameters queryParameters) {
final Optional<Class<? extends Component>> target = getSource()
.getRegistry().getNavigationTarget(route);

final Optional<NavigationState> navigationState = getSource()
.resolveNavigationTarget(new Location(route));
this.redirectQueryParameters = queryParameters;
if (target.isPresent()) {
rerouteTo(getNavigationState(route, List.of()));
if (navigationState.isPresent()) {
rerouteTo(navigationState.get());
} else {
// Inform that reroute target location is not known.
unknownReroute = PathUtil.trimPath(route);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -50,12 +51,14 @@
import com.vaadin.flow.router.RoutePathProvider;
import com.vaadin.flow.router.RoutePrefix;
import com.vaadin.flow.router.RouterLayout;
import com.vaadin.flow.server.AbstractConfiguration;
import com.vaadin.flow.server.InvalidRouteConfigurationException;
import com.vaadin.flow.server.RouteRegistry;
import com.vaadin.flow.server.SessionRouteRegistry;
import com.vaadin.flow.server.VaadinContext;
import com.vaadin.flow.server.VaadinService;
import com.vaadin.flow.server.frontend.FrontendUtils;
import com.vaadin.flow.server.menu.AvailableViewInfo;

/**
* Utility class with methods for route handling.
Expand Down Expand Up @@ -589,8 +592,17 @@ public static boolean isAutolayoutEnabled(Class<?> target, String path) {

}

public static void checkForClientRouteCollisions(
List<RouteData> flowRoutes) {
/**
* Checks the given list of Flow routes for potential collisions with Hilla
* routes.
*
* @param flowRoutes
* Flow routes to check against
* @throws InvalidRouteConfigurationException
* if a collision is detected
*/
public static void checkForClientRouteCollisions(List<RouteData> flowRoutes)
throws InvalidRouteConfigurationException {
VaadinService service = VaadinService.getCurrent();
if (service == null) {
return;
Expand All @@ -603,8 +615,18 @@ public static void checkForClientRouteCollisions(
}
}

/**
* Checks the given array of Flow route templates for potential collisions
* with Hilla routes.
*
* @param flowRouteTemplates
* Flow routes to check against
* @throws InvalidRouteConfigurationException
* if a collision is detected
*/
public static void checkForClientRouteCollisions(
String... flowRouteTemplates) {
String... flowRouteTemplates)
throws InvalidRouteConfigurationException {
VaadinService service = VaadinService.getCurrent();
if (service == null) {
return;
Expand All @@ -626,6 +648,63 @@ public static void checkForClientRouteCollisions(
}
}

/**
* Check if the given registry has any auto layouts added
* with @{@link Layout} annotation.
*
* @param registry
* the registry to check
* @return {@code true} if the registry has any auto layouts
*/
public static boolean hasAutoLayout(AbstractRouteRegistry registry) {
return !registry.getLayouts().isEmpty();
}

/**
* Check if currently registered client routes use auto layout based on
* {@link AvailableViewInfo#flowLayout()}.
*
* @param configuration
* deployment configuration
* @return {@code true} if any client route has auto layout
*/
public static boolean hasClientRouteWithAutoLayout(
AbstractConfiguration configuration) {
return MenuRegistry.collectClientMenuItems(false, configuration)
.values().stream().anyMatch(AvailableViewInfo::flowLayout);
}

/**
* Check if the given registry has any routes using auto layout.
*
* @param registry
* the registry to check
* @return {@code true} if the registry has any auto layouts
*/
public static boolean hasServerRouteWithAutoLayout(
AbstractRouteRegistry registry) {
Collection<?> layouts = registry.getLayouts();
return registry.getRegisteredRoutes().stream().anyMatch(routeData -> {
String path;
if (routeData.getNavigationTarget()
.getAnnotation(Route.class) != null) {
path = getRoutePath(registry.getContext(),
routeData.getNavigationTarget());
} else {
path = resolve(registry.getContext(),
routeData.getNavigationTarget());
List<String> parentRoutePrefixes = getRoutePrefixes(
routeData.getNavigationTarget(), null, path);
path = String.join("/", parentRoutePrefixes);
}
return RouteUtil
.isAutolayoutEnabled(routeData.getNavigationTarget(), path)
&& registry.hasLayout(path)
&& collectRouteParentLayouts(registry.getLayout(path))
.stream().anyMatch(layouts::contains);
});
}

/**
* Get optional dynamic page title from the active router targets chain of a
* given UI instance.
Expand Down
18 changes: 18 additions & 0 deletions flow-server/src/main/java/com/vaadin/flow/server/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,24 @@ public final class Constants implements Serializable {
*/
public static final String STATISTIC_HAS_FLOW_ROUTE = "has-flow-route";

/**
* UsageEntry name for automatic layout. Marked used, if Layout annotation
* is used or RouteRegistry#setLayout is used directly.
*/
public static final String STATISTIC_HAS_AUTO_LAYOUT = "has-auto-layout";

/**
* UsageEntry name for client route using automatic layout. Marked used, if
* AvailableViewInfo#flowLayout is true for any client route.
*/
public static final String STATISTIC_HAS_CLIENT_ROUTE_WITH_AUTO_LAYOUT = "has-auto-layout/client";

/**
* UsageEntry name for server route using automatic layout. Marked used, if
* any server route's layout matches Layout annotated layout.
*/
public static final String STATISTIC_HAS_SERVER_ROUTE_WITH_AUTO_LAYOUT = "has-auto-layout/server";

/**
* UsageEntry name for exported web components. Marked used, if either
* WebComponentExporter or WebComponentExporterFactory is found in a project
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@
import com.vaadin.flow.router.RouteData;
import com.vaadin.flow.router.Router;
import com.vaadin.flow.router.internal.AbstractNavigationStateRenderer;
import com.vaadin.flow.router.internal.AbstractRouteRegistry;
import com.vaadin.flow.router.internal.RouteUtil;
import com.vaadin.flow.server.HandlerHelper.RequestType;
import com.vaadin.flow.server.communication.AtmospherePushConnection;
import com.vaadin.flow.server.communication.HeartbeatHandler;
Expand Down Expand Up @@ -304,6 +306,7 @@ public void init() throws ServiceException {
addRouterUsageStatistics();
}
routeDataList.stream().map(Object::toString).forEach(logger::debug);
addAutoLayoutUsageStatistics();
DevToolsToken.init(this);
}
if (getDeploymentConfiguration().isPnpmEnabled()) {
Expand Down Expand Up @@ -331,6 +334,25 @@ private void addRouterUsageStatistics() {
UsageStatistics.markAsUsed(Constants.STATISTIC_HAS_FLOW_ROUTE, null);
}

private void addAutoLayoutUsageStatistics() {
if (getRouteRegistry() instanceof AbstractRouteRegistry registry
&& RouteUtil.hasAutoLayout(registry)) {
UsageStatistics.markAsUsed(Constants.STATISTIC_HAS_AUTO_LAYOUT,
null);
if (RouteUtil.hasClientRouteWithAutoLayout(
getDeploymentConfiguration())) {
UsageStatistics.markAsUsed(
Constants.STATISTIC_HAS_CLIENT_ROUTE_WITH_AUTO_LAYOUT,
null);
}
if (RouteUtil.hasServerRouteWithAutoLayout(registry)) {
UsageStatistics.markAsUsed(
Constants.STATISTIC_HAS_SERVER_ROUTE_WITH_AUTO_LAYOUT,
null);
}
}
}

/**
* Find a route registry to use for this service.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@

import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.internal.UsageStatistics;
import com.vaadin.flow.internal.menu.MenuRegistry;
import com.vaadin.flow.router.HasDynamicTitle;
import com.vaadin.flow.router.PageTitle;
Expand All @@ -38,13 +40,16 @@
*/
public final class MenuConfiguration {

private static final String STATISTICS_DYNAMIC_MENU_ENTRIES = "flow/dynamic-menu-entries";

/**
* Collect ordered list of menu entries for menu population. All client
* views are collected and any accessible server views.
*
* @return ordered list of {@link MenuEntry} instances
*/
public static List<MenuEntry> getMenuEntries() {
UsageStatistics.markAsUsed(STATISTICS_DYNAMIC_MENU_ENTRIES, null);
return MenuRegistry.collectMenuItemsList().stream()
.map(MenuConfiguration::createMenuEntry).toList();
}
Expand All @@ -59,6 +64,7 @@ public static List<MenuEntry> getMenuEntries() {
* @return ordered list of {@link MenuEntry} instances
*/
public static List<MenuEntry> getMenuEntries(Locale locale) {
UsageStatistics.markAsUsed(STATISTICS_DYNAMIC_MENU_ENTRIES, null);
return MenuRegistry.collectMenuItemsList(locale).stream()
.map(MenuConfiguration::createMenuEntry).toList();
}
Expand Down Expand Up @@ -154,13 +160,14 @@ private static Optional<String> getPageHeaderFromMenuItems() {
String activeLocation = PathUtil.trimPath(
ui.getInternals().getActiveViewLocation().getPath());

List<AvailableViewInfo> menuItems = MenuRegistry.getMenuItems(false)
.values().stream().toList();
Map<String, AvailableViewInfo> menuItems = MenuRegistry
.getMenuItems(false);

return menuItems.stream()
.filter(menuItem -> PathUtil.trimPath(menuItem.route())
return menuItems.entrySet().stream()
.filter(menuEntry -> PathUtil.trimPath(menuEntry.getKey())
.equals(activeLocation))
.map(AvailableViewInfo::title).findFirst();
.map(Map.Entry::getValue).map(AvailableViewInfo::title)
.findFirst();
}
return Optional.empty();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,22 @@ public void onHotswap_pushDisabled_routeClassChanged_UINotRefreshedButLiveReload
Mockito.verify(liveReload).refresh(anyBoolean());
}

@Test
public void onHotswap_pushDisabled_autoLayout_classUnrelatedToUIChanged_noReload()
throws ServiceException {
VaadinSession session = createMockVaadinSession();
hotswapper.sessionInit(new SessionInitEvent(service, session, null));
RefreshTestingUI ui = initUIAndNavigateTo(session,
MyAutoLayoutRoute.class);

hotswapper.onHotswap(new String[] { MyRouteWithChild.class.getName() },
true);

ui.assertNotRefreshed();
Mockito.verify(liveReload, never()).refresh(anyBoolean());
Mockito.verify(liveReload, never()).reload();
}

@Test
public void onHotswap_pushDisabled_routeLayoutClassChanged_UINotRefreshedButLiveReloadTriggered()
throws ServiceException {
Expand Down Expand Up @@ -537,6 +553,23 @@ class NewLayout extends Component implements RouterLayout {
Mockito.verify(liveReload, never()).refresh(anyBoolean());
}

@Test
public void onHotswap_pushEnabled_autoLayout_classUnrelatedToUIChanged_noReload()
throws ServiceException {
VaadinSession session = createMockVaadinSession();
hotswapper.sessionInit(new SessionInitEvent(service, session, null));
RefreshTestingUI ui = initUIAndNavigateTo(session,
MyAutoLayoutRoute.class);
ui.enablePush();

hotswapper.onHotswap(new String[] { MyRouteWithChild.class.getName() },
true);

ui.assertNotRefreshed();
Mockito.verify(liveReload, never()).refresh(anyBoolean());
Mockito.verify(liveReload, never()).reload();
}

@Test
public void onHotswap_pushEnabled_routeChildrenClassChanged_routeRefreshed()
throws ServiceException {
Expand Down
Loading

0 comments on commit cacbdd7

Please sign in to comment.