Skip to content

Commit

Permalink
feat: add feature flag for Hilla multi-module engine (#14289)
Browse files Browse the repository at this point in the history
Fixes #14266
  • Loading branch information
platosha committed Aug 9, 2022
1 parent d2d50b6 commit 1c661a4
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 139 deletions.
Expand Up @@ -68,6 +68,9 @@ public class FeatureFlags implements Serializable {
public static final Feature HILLA_PUSH = new Feature(
"Push support in Hilla", "hillaPush",
"https://github.com/vaadin/hilla/issues/56", true, null);
public static final Feature HILLA_ENGINE = new Feature(
"Multi-module engine in Hilla", "hillaEngine",
"https://github.com/vaadin/flow/issues/9010", true, null);
public static final Feature OFFLINE_LICENSE_CHECKER = new Feature(
"Offline license checker", "newLicenseChecker",
"https://github.com/vaadin/platform/issues/2938", false, null);
Expand Down Expand Up @@ -99,6 +102,7 @@ public FeatureFlags(Lookup lookup) {
features.add(new Feature(MAP_COMPONENT));
features.add(new Feature(SPREADSHEET_COMPONENT));
features.add(new Feature(HILLA_PUSH));
features.add(new Feature(HILLA_ENGINE));
features.add(new Feature(OFFLINE_LICENSE_CHECKER));
features.add(new Feature(COLLABORATION_ENGINE_BACKEND));
features.add(new Feature(WEBPACK));
Expand Down
Expand Up @@ -791,11 +791,11 @@ private NodeTasks(Builder builder) {
if (!builder.useLegacyV14Bootstrap) {
addBootstrapTasks(builder);

TaskGenerateHilla hillaTask = builder.lookup
.lookup(TaskGenerateHilla.class);
// use the new Hilla generator if available, otherwise the old
// use the new Hilla generator if enabled, otherwise use the old
// generator.
if (hillaTask != null) {
if (featureFlags.isEnabled(FeatureFlags.HILLA_ENGINE)) {
TaskGenerateHilla hillaTask = builder.lookup
.lookup(TaskGenerateHilla.class);
hillaTask.configure(builder.getNpmFolder(),
builder.getBuildDirectory());
commands.add(hillaTask);
Expand Down
@@ -0,0 +1,173 @@
/*
* Copyright 2000-2022 Vaadin Ltd.
*
* 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.vaadin.flow.server.frontend;

import com.vaadin.experimental.FeatureFlags;
import com.vaadin.flow.di.Lookup;
import com.vaadin.flow.server.ExecutionFailedException;
import com.vaadin.flow.server.frontend.NodeTasks.Builder;
import com.vaadin.flow.server.frontend.scanner.ClassFinder;
import com.vaadin.flow.server.frontend.scanner.ClassFinder.DefaultClassFinder;
import org.apache.commons.io.FileUtils;
import org.junit.*;
import org.junit.rules.TemporaryFolder;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

import static com.vaadin.flow.server.Constants.TARGET;
import static com.vaadin.flow.server.frontend.FrontendUtils.*;
import static org.mockito.Mockito.*;

public class NodeTasksHillaTest {

@Rule
public MockitoRule rule = MockitoJUnit.rule();

@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();

private static final String USER_DIR = "user.dir";

private static String globalUserDirValue;
private static String globalFrontendDirValue;
private static String globalGeneratedDirValue;

private String userDir;

private File propertiesDir;

@Mock
private Lookup lookup;

@Mock
private EndpointGeneratorTaskFactory endpointGeneratorTaskFactory;

@Mock
private TaskGenerateOpenAPI taskGenerateOpenAPI;

@Mock
private TaskGenerateEndpoint taskGenerateEndpoint;

@Mock
private TaskGenerateHilla taskGenerateHilla;

@Before
public void setup() throws Exception {
userDir = temporaryFolder.getRoot().getAbsolutePath();
System.setProperty(USER_DIR, userDir);
System.clearProperty(PARAM_FRONTEND_DIR);
System.clearProperty(PARAM_GENERATED_DIR);

propertiesDir = temporaryFolder.newFolder();

Mockito.doReturn(
new DefaultClassFinder(this.getClass().getClassLoader()))
.when(lookup).lookup(ClassFinder.class);
Mockito.doReturn(taskGenerateOpenAPI).when(endpointGeneratorTaskFactory)
.createTaskGenerateOpenAPI(any(), any(), any(), any());
Mockito.doReturn(taskGenerateEndpoint)
.when(endpointGeneratorTaskFactory)
.createTaskGenerateEndpoint(any(), any(), any(), any());
Mockito.doReturn(endpointGeneratorTaskFactory).when(lookup)
.lookup(EndpointGeneratorTaskFactory.class);

Mockito.doReturn(taskGenerateHilla).when(lookup)
.lookup(TaskGenerateHilla.class);
}

@BeforeClass
public static void setupBeforeClass() {
globalUserDirValue = System.getProperty(USER_DIR);
globalFrontendDirValue = System.getProperty(PARAM_FRONTEND_DIR);
globalGeneratedDirValue = System.getProperty(PARAM_GENERATED_DIR);
}

@AfterClass
public static void tearDownAfterClass() {
setPropertyIfPresent(USER_DIR, globalUserDirValue);
setPropertyIfPresent(PARAM_FRONTEND_DIR, globalFrontendDirValue);
setPropertyIfPresent(PARAM_GENERATED_DIR, globalGeneratedDirValue);
}

@Test
public void should_useHillaGeneartor_whenEnabled()
throws ExecutionFailedException, IOException {
runEndpointTasks(true);
verifyHillaTask(true);
verifyOldGenerator(false);
}

@Test
public void should_useOldGenerator_whenHillaGeneratorNotEnabled()
throws ExecutionFailedException, IOException {
runEndpointTasks(false);
verifyHillaTask(false);
verifyOldGenerator(true);
}

private void runEndpointTasks(boolean withHillaTask)
throws ExecutionFailedException, IOException {
FileUtils.write(
new File(propertiesDir, FeatureFlags.PROPERTIES_FILENAME),
String.format("com.vaadin.experimental.hillaEngine=%s\n",
withHillaTask),
StandardCharsets.UTF_8);

Builder builder = new Builder(lookup, new File(userDir), TARGET)
.enablePackagesUpdate(false).enableImportsUpdate(true)
.runNpmInstall(false).withEmbeddableWebComponents(false)
.withFlowResourcesFolder(
new File(userDir, TARGET + "flow-frontend"))
.withFrontendGeneratedFolder(new File(userDir))
.withEndpointSourceFolder(new File(userDir))
.withEndpointGeneratedOpenAPIFile(new File(userDir))
.setJavaResourceFolder(propertiesDir);

builder.build().execute();
}

private static void setPropertyIfPresent(String key, String value) {
if (value != null) {
System.setProperty(key, value);
}
}

private void verifyHillaTask(boolean expected)
throws ExecutionFailedException {
Mockito.verify(taskGenerateHilla, expected ? times(1) : never())
.execute();
}

private void verifyOldGenerator(boolean expected)
throws ExecutionFailedException {
Mockito.verify(endpointGeneratorTaskFactory,
expected ? times(1) : never())
.createTaskGenerateEndpoint(any(), any(), any(), any());
Mockito.verify(endpointGeneratorTaskFactory,
expected ? times(1) : never())
.createTaskGenerateOpenAPI(any(), any(), any(), any());
Mockito.verify(taskGenerateOpenAPI, expected ? times(1) : never())
.execute();
Mockito.verify(taskGenerateEndpoint, expected ? times(1) : never())
.execute();
}
}
Expand Up @@ -57,10 +57,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;

public class NodeTasksViteTest {

Expand Down Expand Up @@ -298,71 +295,6 @@ public void should_GenerateTsConfigAndTsDefinitions_When_Vaadin14BootstrapMode()
Assert.assertTrue(new File(userDir, "types.d.ts").exists());
}

@Test
public void should_useHillaGeneartor_whenAvailable()
throws ExecutionFailedException {
verifyEndpointGeneratorWithHillaTask(true);
}

@Test
public void should_useOldGenerator_whenHillaGeneratorNotAvailable()
throws ExecutionFailedException {
verifyEndpointGeneratorWithHillaTask(false);
}

private void verifyEndpointGeneratorWithHillaTask(boolean withHillaTask)
throws ExecutionFailedException {
Lookup mockedLookup = mock(Lookup.class);
Mockito.doReturn(
new DefaultClassFinder(this.getClass().getClassLoader()))
.when(mockedLookup).lookup(ClassFinder.class);
Builder builder = new Builder(mockedLookup, new File(userDir), TARGET)
.enablePackagesUpdate(false).enableImportsUpdate(true)
.runNpmInstall(false).withEmbeddableWebComponents(false)
.withFrontendGeneratedFolder(new File(userDir))
.withEndpointSourceFolder(new File(userDir))
.withEndpointGeneratedOpenAPIFile(new File(userDir))
.withFlowResourcesFolder(
new File(userDir, TARGET + "flow-frontend"));

EndpointGeneratorTaskFactory endpointGeneratorFactory = mock(
EndpointGeneratorTaskFactory.class);
TaskGenerateOpenAPI mockGenerateOpenAPI = mock(
TaskGenerateOpenAPI.class);
TaskGenerateEndpoint mockGenerateEndpoint = mock(
TaskGenerateEndpoint.class);
Mockito.doReturn(mockGenerateOpenAPI).when(endpointGeneratorFactory)
.createTaskGenerateOpenAPI(any(), any(), any(), any());
Mockito.doReturn(mockGenerateEndpoint).when(endpointGeneratorFactory)
.createTaskGenerateEndpoint(any(), any(), any(), any());
Mockito.doReturn(endpointGeneratorFactory).when(mockedLookup)
.lookup(EndpointGeneratorTaskFactory.class);

TaskGenerateHilla hillaTask = withHillaTask
? mock(TaskGenerateHilla.class)
: null;

Mockito.doReturn(hillaTask).when(mockedLookup)
.lookup(TaskGenerateHilla.class);

builder.build().execute();

if (withHillaTask) {
Mockito.verify(hillaTask, times(1)).execute();
}

Mockito.verify(endpointGeneratorFactory,
withHillaTask ? never() : times(1))
.createTaskGenerateEndpoint(any(), any(), any(), any());
Mockito.verify(endpointGeneratorFactory,
withHillaTask ? never() : times(1))
.createTaskGenerateOpenAPI(any(), any(), any(), any());
Mockito.verify(mockGenerateOpenAPI, withHillaTask ? never() : times(1))
.execute();
Mockito.verify(mockGenerateEndpoint, withHillaTask ? never() : times(1))
.execute();
}

private static void setPropertyIfPresent(String key, String value) {
if (value != null) {
System.setProperty(key, value);
Expand Down
Expand Up @@ -56,10 +56,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;

public class NodeTasksWebpackTest {

Expand Down Expand Up @@ -302,70 +299,6 @@ public void should_GenerateTsConfigAndTsDefinitions_When_Vaadin14BootstrapMode()
Assert.assertTrue(new File(userDir, "types.d.ts").exists());
}

@Test
public void should_useHillaGeneartor_whenAvailable()
throws ExecutionFailedException {
verifyEndpointGeneratorWithHillaTask(true);
}

@Test
public void should_useOldGenerator_whenHillaGeneratorNotAvailable()
throws ExecutionFailedException {
verifyEndpointGeneratorWithHillaTask(false);
}

private void verifyEndpointGeneratorWithHillaTask(boolean withHillaTask)
throws ExecutionFailedException {
Lookup mockedLookup = mock(Lookup.class);
Mockito.doReturn(
new DefaultClassFinder(this.getClass().getClassLoader()))
.when(mockedLookup).lookup(ClassFinder.class);
Builder builder = new Builder(mockedLookup, new File(userDir), TARGET)
.enablePackagesUpdate(false).enableImportsUpdate(true)
.runNpmInstall(false).withEmbeddableWebComponents(false)
.withFrontendGeneratedFolder(new File(userDir))
.withEndpointSourceFolder(new File(userDir))
.withEndpointGeneratedOpenAPIFile(new File(userDir))
.setJavaResourceFolder(propertiesDir);

EndpointGeneratorTaskFactory endpointGeneratorFactory = mock(
EndpointGeneratorTaskFactory.class);
TaskGenerateOpenAPI mockGenerateOpenAPI = mock(
TaskGenerateOpenAPI.class);
TaskGenerateEndpoint mockGenerateEndpoint = mock(
TaskGenerateEndpoint.class);
Mockito.doReturn(mockGenerateOpenAPI).when(endpointGeneratorFactory)
.createTaskGenerateOpenAPI(any(), any(), any(), any());
Mockito.doReturn(mockGenerateEndpoint).when(endpointGeneratorFactory)
.createTaskGenerateEndpoint(any(), any(), any(), any());
Mockito.doReturn(endpointGeneratorFactory).when(mockedLookup)
.lookup(EndpointGeneratorTaskFactory.class);

TaskGenerateHilla hillaTask = withHillaTask
? mock(TaskGenerateHilla.class)
: null;

Mockito.doReturn(hillaTask).when(mockedLookup)
.lookup(TaskGenerateHilla.class);

builder.build().execute();

if (withHillaTask) {
Mockito.verify(hillaTask, times(1)).execute();
}

Mockito.verify(endpointGeneratorFactory,
withHillaTask ? never() : times(1))
.createTaskGenerateEndpoint(any(), any(), any(), any());
Mockito.verify(endpointGeneratorFactory,
withHillaTask ? never() : times(1))
.createTaskGenerateOpenAPI(any(), any(), any(), any());
Mockito.verify(mockGenerateOpenAPI, withHillaTask ? never() : times(1))
.execute();
Mockito.verify(mockGenerateEndpoint, withHillaTask ? never() : times(1))
.execute();
}

private static void setPropertyIfPresent(String key, String value) {
if (value != null) {
System.setProperty(key, value);
Expand Down

0 comments on commit 1c661a4

Please sign in to comment.