Skip to content

Commit

Permalink
fix: Add util method for detecting Hilla auto layout
Browse files Browse the repository at this point in the history
  • Loading branch information
mshabarov committed Oct 14, 2024
1 parent dad2795 commit e59a667
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand All @@ -44,6 +46,7 @@
import org.slf4j.LoggerFactory;

import com.vaadin.flow.component.Component;
import com.vaadin.flow.function.SerializableConsumer;
import com.vaadin.flow.router.BeforeEnterListener;
import com.vaadin.flow.router.MenuData;
import com.vaadin.flow.router.PageTitle;
Expand Down Expand Up @@ -303,39 +306,77 @@ public static Map<String, AvailableViewInfo> collectClientMenuItems(
boolean filterClientViews, AbstractConfiguration configuration,
VaadinRequest vaadinRequest) {

Map<String, AvailableViewInfo> configurations = new HashMap<>();

collectClientMenuItems(configuration,
clientViewConfig -> collectClientViews("", clientViewConfig,
configurations));

if (filterClientViews && !configurations.isEmpty()) {
filterClientViews(configurations, vaadinRequest);
}

return configurations;
}

/**
* Determines whether the application contains a Hilla auto layout.
* <p>
* This method checks if any of the client view configurations in the
* {@code file-routes.json} file correspond to a main menu layout, i.e. has
* children views.
*
* @param configuration
* the {@link AbstractConfiguration} containing the application
* configuration
* @return {@code true} if a Hilla auto layout is present in the
* configuration, {@code false} otherwise
*/
public static boolean hasHillaAutoLayout(
AbstractConfiguration configuration) {
AtomicBoolean hasHillaAutoLayout = new AtomicBoolean(false);
collectClientMenuItems(configuration, clientViewConfig -> {
if (!hasHillaAutoLayout.get() && isMainLayout(clientViewConfig)) {
hasHillaAutoLayout.compareAndSet(false, true);
}
});
return hasHillaAutoLayout.get();
}

private static boolean isMainLayout(AvailableViewInfo viewInfo) {
return (viewInfo.route() == null || viewInfo.route().isBlank())
&& viewInfo.children() != null;
}

private static void collectClientMenuItems(
AbstractConfiguration configuration,
SerializableConsumer<AvailableViewInfo> viewInfoConsumer) {
Objects.requireNonNull(configuration);
Objects.requireNonNull(viewInfoConsumer);
URL viewsJsonAsResource = getViewsJsonAsResource(configuration);
if (viewsJsonAsResource == null) {
LoggerFactory.getLogger(MenuRegistry.class).debug(
"No {} found under {} directory. Skipping client route registration.",
FILE_ROUTES_JSON_NAME,
configuration.isProductionMode() ? "'META-INF/VAADIN'"
: "'frontend/generated'");
return Collections.emptyMap();
return;
}

Map<String, AvailableViewInfo> configurations = new HashMap<>();

try (InputStream source = viewsJsonAsResource.openStream()) {
if (source != null) {
ObjectMapper mapper = new ObjectMapper().configure(
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
mapper.readValue(source,
new TypeReference<List<AvailableViewInfo>>() {
}).forEach(clientViewConfig -> collectClientViews("",
clientViewConfig, configurations));
}).forEach(viewInfoConsumer);
}
} catch (IOException e) {
LoggerFactory.getLogger(MenuRegistry.class).warn(
"Failed load {} from {}", FILE_ROUTES_JSON_NAME,
viewsJsonAsResource.getPath(), e);
}

if (filterClientViews) {
filterClientViews(configurations, vaadinRequest);
}

return configurations;
}

private static void collectClientViews(String basePath,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@
import com.vaadin.flow.server.VaadinSession;
import com.vaadin.flow.server.startup.ApplicationRouteRegistry;

import elemental.json.JsonArray;
import elemental.json.JsonObject;
import elemental.json.impl.JsonUtil;
import static com.vaadin.flow.server.frontend.FrontendUtils.GENERATED;
import static com.vaadin.flow.internal.menu.MenuRegistry.FILE_ROUTES_JSON_NAME;
import static com.vaadin.flow.internal.menu.MenuRegistry.FILE_ROUTES_JSON_PROD_PATH;
Expand Down Expand Up @@ -331,6 +334,38 @@ public void getMenuItemsList_assertOrder() {
new String[] { "/d", "/c", "/a", "/b", "/d/a", "/d/b" });
}

@Test
public void hasHillaAutoLayout_fileRoutesHasLayout_true()
throws IOException {
JsonArray fileRoutes = JsonUtil.parse(testClientRouteFile);
JsonObject layout = fileRoutes.getObject(0);
JsonArray children = layout.getArray("children");
Assert.assertNotNull(children);
Assert.assertTrue(children.length() > 0);

File generated = tmpDir.newFolder(GENERATED);
File clientFiles = new File(generated, FILE_ROUTES_JSON_NAME);
Files.writeString(clientFiles.toPath(), testClientRouteFile);

boolean hasHillaAutoLayout = MenuRegistry
.hasHillaAutoLayout(vaadinService.getDeploymentConfiguration());
Assert.assertTrue(hasHillaAutoLayout);
}

@Test
public void hasHillaAutoLayout_fileRoutesHasNoLayout_false()
throws IOException {
Assert.assertFalse(noLayoutsRouteFile.contains("\"children\""));

File generated = tmpDir.newFolder(GENERATED);
File clientFiles = new File(generated, FILE_ROUTES_JSON_NAME);
Files.writeString(clientFiles.toPath(), noLayoutsRouteFile);

boolean hasHillaAutoLayout = MenuRegistry
.hasHillaAutoLayout(vaadinService.getDeploymentConfiguration());
Assert.assertFalse(hasHillaAutoLayout);
}

private void assertOrder(List<AvailableViewInfo> menuItems,
String[] expectedOrder) {
for (int i = 0; i < menuItems.size(); i++) {
Expand Down Expand Up @@ -683,4 +718,19 @@ public Instantiator getInstantiator() {
}
]
""";

String noLayoutsRouteFile = """
[
{
"route": "",
"menu": {
"title": "Public page",
"icon": "vaadin:group"
},
"flowLayout": false,
"params": {},
"title": "Public"
}
]
""";
}

0 comments on commit e59a667

Please sign in to comment.