Skip to content

Commit 530e757

Browse files
tepimshabarov
andauthored
feat: improved endpoint detection (#21009)
Fixes #20983 --------- Co-authored-by: Mikhail Shabarov <[email protected]>
1 parent f849d20 commit 530e757

File tree

17 files changed

+214
-32
lines changed

17 files changed

+214
-32
lines changed

flow-plugins/flow-maven-plugin/src/test/java/com/vaadin/flow/plugin/maven/TestEndpointGeneratorTaskFactory.java

+12
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717

1818
import java.io.File;
1919
import java.io.IOException;
20+
import java.lang.annotation.Annotation;
2021
import java.nio.charset.StandardCharsets;
2122
import java.nio.file.Files;
2223
import java.nio.file.StandardOpenOption;
24+
import java.util.Set;
2325

2426
import org.slf4j.Logger;
2527
import org.slf4j.LoggerFactory;
@@ -47,6 +49,16 @@ public TaskGenerateOpenAPI createTaskGenerateOpenAPI(Options options) {
4749
return new TestTaskGenerateOpenAPI(options);
4850
}
4951

52+
@Override
53+
public Set<Class<? extends Annotation>> getBrowserCallableAnnotations() {
54+
return Set.of();
55+
}
56+
57+
@Override
58+
public boolean hasBrowserCallables(Options options) {
59+
return true;
60+
}
61+
5062
/**
5163
* An abstract parent for the test endpoints generator tasks.
5264
*/

flow-plugins/flow-plugin-base/src/test/java/com/vaadin/flow/plugin/base/BuildFrontendUtilTest.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ public void should_useHillaEngine_withNodeUpdater()
156156
EndpointRequestUtil.class, Mockito.CALLS_REAL_METHODS)) {
157157
util.when(() -> EndpointRequestUtil.isHillaAvailable(Mockito.any()))
158158
.thenReturn(true);
159+
util.when(() -> EndpointRequestUtil
160+
.areHillaEndpointsUsed(Mockito.any())).thenReturn(true);
159161
BuildFrontendUtil.runNodeUpdater(adapter);
160162
}
161163

@@ -168,10 +170,10 @@ public void should_useHillaEngine_withNodeUpdater()
168170
// Hilla Engine requires npm install, the order of execution is critical
169171
final TaskRunNpmInstall taskRunNpmInstall = construction.constructed()
170172
.get(0);
171-
InOrder inOrder = Mockito.inOrder(taskRunNpmInstall,
172-
taskGenerateOpenAPI, taskGenerateEndpoint);
173-
inOrder.verify(taskRunNpmInstall).execute();
173+
InOrder inOrder = Mockito.inOrder(taskGenerateOpenAPI,
174+
taskRunNpmInstall, taskGenerateEndpoint);
174175
inOrder.verify(taskGenerateOpenAPI).execute();
176+
inOrder.verify(taskRunNpmInstall).execute();
175177
inOrder.verify(taskGenerateEndpoint).execute();
176178
}
177179

flow-server/src/main/java/com/vaadin/flow/internal/hilla/EndpointRequestUtil.java

+20
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import jakarta.servlet.http.HttpServletRequest;
2020
import java.io.Serializable;
2121

22+
import com.vaadin.flow.server.frontend.EndpointGeneratorTaskFactory;
23+
import com.vaadin.flow.server.frontend.Options;
2224
import com.vaadin.flow.server.frontend.scanner.ClassFinder;
2325

2426
/**
@@ -86,4 +88,22 @@ static boolean isHillaAvailable(ClassFinder classFinder) {
8688
return false;
8789
}
8890
}
91+
92+
/**
93+
* Checks if Hilla is available and Hilla endpoints are used in the project.
94+
*
95+
* @return {@code true} if Hilla is available and Hilla endpoints are used,
96+
* {@code false} otherwise
97+
*/
98+
static boolean areHillaEndpointsUsed(Options options) {
99+
if (!EndpointRequestUtil.isHillaAvailable()) {
100+
return false;
101+
}
102+
EndpointGeneratorTaskFactory endpointGeneratorTaskFactory = options
103+
.getLookup().lookup(EndpointGeneratorTaskFactory.class);
104+
if (endpointGeneratorTaskFactory != null) {
105+
return endpointGeneratorTaskFactory.hasBrowserCallables(options);
106+
}
107+
return false;
108+
}
89109
}

flow-server/src/main/java/com/vaadin/flow/server/frontend/BundleValidationUtil.java

+6-3
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import com.vaadin.flow.internal.JacksonUtils;
3131
import com.vaadin.flow.internal.StringUtil;
3232
import com.vaadin.flow.internal.UsageStatistics;
33+
import com.vaadin.flow.internal.hilla.EndpointRequestUtil;
3334
import com.vaadin.flow.server.Constants;
3435
import com.vaadin.flow.server.LoadDependenciesOnStartup;
3536
import com.vaadin.flow.server.Mode;
@@ -68,9 +69,11 @@ public static boolean needsBuild(Options options,
6869
try {
6970
boolean needsBuild;
7071
if (mode.isProduction()) {
71-
if (options.isForceProductionBuild() || FrontendUtils
72-
.isHillaUsed(options.getFrontendDirectory(),
73-
options.getClassFinder())) {
72+
if (options.isForceProductionBuild()
73+
|| FrontendUtils.isHillaUsed(
74+
options.getFrontendDirectory(),
75+
options.getClassFinder())
76+
|| EndpointRequestUtil.areHillaEndpointsUsed(options)) {
7477
if (options.isForceProductionBuild()) {
7578
UsageStatistics.markAsUsed("flow/prod-build-requested",
7679
null);

flow-server/src/main/java/com/vaadin/flow/server/frontend/EndpointGeneratorTaskFactory.java

+20
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717
package com.vaadin.flow.server.frontend;
1818

19+
import java.lang.annotation.Annotation;
20+
import java.util.Set;
21+
1922
/**
2023
* A factory for creating Vaadin Endpoint generator tasks.
2124
* <p>
@@ -43,4 +46,21 @@ public interface EndpointGeneratorTaskFactory {
4346
* @return an endpoint task that generates open api json file.
4447
*/
4548
TaskGenerateOpenAPI createTaskGenerateOpenAPI(Options options);
49+
50+
/**
51+
* Fetches all endpoint-type annotations from Hilla configuration
52+
*
53+
* @return Set of endpoint-type annotations
54+
*/
55+
Set<Class<? extends Annotation>> getBrowserCallableAnnotations();
56+
57+
/**
58+
* Determines the presence of annotations (e.g. BrowserCallable or Endpoint)
59+
* which require Flow to add Hilla packages to the build.
60+
*
61+
* @param options
62+
* the task options
63+
* @return {@code true} if annotations are present, {@code false} otherwise
64+
*/
65+
boolean hasBrowserCallables(Options options);
4666
}

flow-server/src/main/java/com/vaadin/flow/server/frontend/FrontendUtils.java

-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
import java.util.stream.Stream;
4141

4242
import com.fasterxml.jackson.databind.JsonNode;
43-
import com.fasterxml.jackson.databind.node.ObjectNode;
4443
import jakarta.servlet.ServletContext;
4544
import org.apache.commons.io.FileUtils;
4645
import org.apache.commons.io.IOUtils;

flow-server/src/main/java/com/vaadin/flow/server/frontend/NodeTasks.java

+17-14
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@ public class NodeTasks implements FallibleCommand {
7373
TaskGenerateWebComponentBootstrap.class,
7474
TaskGenerateFeatureFlags.class,
7575
TaskInstallFrontendBuildPlugins.class,
76+
TaskGenerateOpenAPI.class,
7677
TaskUpdatePackages.class,
7778
TaskRunNpmInstall.class,
78-
TaskGenerateOpenAPI.class,
7979
TaskGenerateEndpoint.class,
8080
TaskCopyFrontendFiles.class,
8181
TaskCopyLocalFrontendFiles.class,
@@ -319,19 +319,22 @@ private void addEndpointServicesTasks(Options options) {
319319
if (!EndpointRequestUtil.isHillaAvailable(options.getClassFinder())) {
320320
return;
321321
}
322-
Lookup lookup = options.getLookup();
323-
EndpointGeneratorTaskFactory endpointGeneratorTaskFactory = lookup
324-
.lookup(EndpointGeneratorTaskFactory.class);
325-
326-
if (endpointGeneratorTaskFactory != null) {
327-
TaskGenerateOpenAPI taskGenerateOpenAPI = endpointGeneratorTaskFactory
328-
.createTaskGenerateOpenAPI(options);
329-
commands.add(taskGenerateOpenAPI);
330-
331-
if (options.getFrontendGeneratedFolder() != null) {
332-
TaskGenerateEndpoint taskGenerateEndpoint = endpointGeneratorTaskFactory
333-
.createTaskGenerateEndpoint(options);
334-
commands.add(taskGenerateEndpoint);
322+
if (EndpointRequestUtil.areHillaEndpointsUsed(options)
323+
|| FrontendUtils.isHillaUsed(options.getFrontendDirectory())) {
324+
Lookup lookup = options.getLookup();
325+
EndpointGeneratorTaskFactory endpointGeneratorTaskFactory = lookup
326+
.lookup(EndpointGeneratorTaskFactory.class);
327+
328+
if (endpointGeneratorTaskFactory != null) {
329+
TaskGenerateOpenAPI taskGenerateOpenAPI = endpointGeneratorTaskFactory
330+
.createTaskGenerateOpenAPI(options);
331+
commands.add(taskGenerateOpenAPI);
332+
333+
if (options.getFrontendGeneratedFolder() != null) {
334+
TaskGenerateEndpoint taskGenerateEndpoint = endpointGeneratorTaskFactory
335+
.createTaskGenerateEndpoint(options);
336+
commands.add(taskGenerateEndpoint);
337+
}
335338
}
336339
}
337340
}

flow-server/src/main/java/com/vaadin/flow/server/frontend/NodeUpdater.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import com.vaadin.experimental.FeatureFlags;
4444
import com.vaadin.flow.internal.JacksonUtils;
4545
import com.vaadin.flow.internal.JsonDecodingException;
46+
import com.vaadin.flow.internal.hilla.EndpointRequestUtil;
4647
import com.vaadin.flow.server.Constants;
4748
import com.vaadin.flow.server.frontend.scanner.ClassFinder;
4849
import com.vaadin.flow.server.frontend.scanner.FrontendDependencies;
@@ -620,7 +621,8 @@ private ObjectNode generateVersionsFromPackageJson(JsonNode packageJson) {
620621
private void putHillaComponentsDependencies(
621622
Map<String, String> dependencies, String packageJsonKey) {
622623
if (FrontendUtils.isHillaUsed(options.getFrontendDirectory(),
623-
options.getClassFinder())) {
624+
options.getClassFinder())
625+
|| EndpointRequestUtil.areHillaEndpointsUsed(options)) {
624626
if (options.isReactEnabled()) {
625627
dependencies.putAll(readDependenciesIfAvailable(
626628
"hilla/components/react", packageJsonKey));

flow-server/src/test/java/com/vaadin/flow/server/frontend/NodeTasksHillaTest.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,8 @@ public void should_useHillaEngine_whenEnabled()
122122
EndpointRequestUtil.class, Mockito.CALLS_REAL_METHODS)) {
123123
util.when(() -> EndpointRequestUtil.isHillaAvailable(Mockito.any()))
124124
.thenReturn(true);
125-
125+
util.when(() -> EndpointRequestUtil
126+
.areHillaEndpointsUsed(Mockito.any())).thenReturn(true);
126127
new NodeTasks(options).execute();
127128
}
128129

vaadin-dev-server/src/main/java/com/vaadin/base/devserver/startup/DevModeInitializer.java

+20-3
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
import com.vaadin.flow.server.Mode;
7070
import com.vaadin.flow.server.VaadinContext;
7171
import com.vaadin.flow.server.VaadinServlet;
72+
import com.vaadin.flow.server.frontend.EndpointGeneratorTaskFactory;
7273
import com.vaadin.flow.server.frontend.FrontendUtils;
7374
import com.vaadin.flow.server.frontend.NodeTasks;
7475
import com.vaadin.flow.server.frontend.Options;
@@ -100,9 +101,12 @@ static class DevModeClassFinder extends DefaultClassFinder {
100101

101102
private static final Set<String> APPLICABLE_CLASS_NAMES = Collections
102103
.unmodifiableSet(calculateApplicableClassNames());
104+
private final Set<Class<? extends Annotation>> browserCallableAnnotations;
103105

104-
public DevModeClassFinder(Set<Class<?>> classes) {
106+
public DevModeClassFinder(Set<Class<?>> classes,
107+
Set<Class<? extends Annotation>> browserCallableAnnotations) {
105108
super(classes);
109+
this.browserCallableAnnotations = browserCallableAnnotations;
106110
}
107111

108112
@Override
@@ -119,7 +123,8 @@ public <T> Set<Class<? extends T>> getSubTypesOf(Class<T> type) {
119123
}
120124

121125
private void ensureImplementation(Class<?> clazz) {
122-
if (!APPLICABLE_CLASS_NAMES.contains(clazz.getName())) {
126+
if (!APPLICABLE_CLASS_NAMES.contains(clazz.getName())
127+
&& !browserCallableAnnotations.contains(clazz)) {
123128
throw new IllegalArgumentException("Unexpected class name "
124129
+ clazz + ". Implementation error: the class finder "
125130
+ "instance is not aware of this class. "
@@ -129,6 +134,7 @@ private void ensureImplementation(Class<?> clazz) {
129134
}
130135

131136
private static Set<String> calculateApplicableClassNames() {
137+
132138
HandlesTypes handlesTypes = DevModeStartupListener.class
133139
.getAnnotation(HandlesTypes.class);
134140
return Stream.of(handlesTypes.value()).map(Class::getName)
@@ -243,7 +249,18 @@ public static DevModeHandler initDevModeHandler(Set<Class<?>> classes,
243249
File frontendFolder = config.getFrontendFolder();
244250

245251
Lookup lookupFromContext = context.getAttribute(Lookup.class);
246-
Lookup lookupForClassFinder = Lookup.of(new DevModeClassFinder(classes),
252+
253+
EndpointGeneratorTaskFactory endpointGeneratorTaskFactory = lookupFromContext
254+
.lookup(EndpointGeneratorTaskFactory.class);
255+
256+
Set<Class<? extends Annotation>> browserCallableAnnotations = new HashSet<>();
257+
if (endpointGeneratorTaskFactory != null) {
258+
browserCallableAnnotations.addAll(endpointGeneratorTaskFactory
259+
.getBrowserCallableAnnotations());
260+
}
261+
262+
Lookup lookupForClassFinder = Lookup.of(
263+
new DevModeClassFinder(classes, browserCallableAnnotations),
247264
ClassFinder.class);
248265
Lookup lookup = Lookup.compose(lookupForClassFinder, lookupFromContext);
249266
Options options = new Options(lookup, baseDir)

vaadin-dev-server/src/test/java/com/vaadin/base/devserver/DevModeEndpointTest.java

+4
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ public void should_generateOpenApi() throws Exception {
112112
EndpointRequestUtil.class, Mockito.CALLS_REAL_METHODS)) {
113113
util.when(() -> EndpointRequestUtil.isHillaAvailable(Mockito.any()))
114114
.thenReturn(true);
115+
util.when(() -> EndpointRequestUtil
116+
.areHillaEndpointsUsed(Mockito.any())).thenReturn(true);
115117
devModeStartupListener.onStartup(classes, servletContext);
116118
handler = getDevModeHandler();
117119
waitForDevServer();
@@ -144,6 +146,8 @@ public void should_generateTs_files() throws Exception {
144146
EndpointRequestUtil.class, Mockito.CALLS_REAL_METHODS)) {
145147
util.when(() -> EndpointRequestUtil.isHillaAvailable(Mockito.any()))
146148
.thenReturn(true);
149+
util.when(() -> EndpointRequestUtil
150+
.areHillaEndpointsUsed(Mockito.any())).thenReturn(true);
147151
devModeStartupListener.onStartup(classes, servletContext);
148152
handler = getDevModeHandler();
149153
waitForDevServer();

vaadin-dev-server/src/test/java/com/vaadin/base/devserver/TestEndpointGeneratorTaskFactory.java

+12
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717

1818
import java.io.File;
1919
import java.io.IOException;
20+
import java.lang.annotation.Annotation;
2021
import java.nio.charset.StandardCharsets;
2122
import java.nio.file.Files;
2223
import java.nio.file.StandardOpenOption;
24+
import java.util.Set;
2325

2426
import org.slf4j.Logger;
2527
import org.slf4j.LoggerFactory;
@@ -47,6 +49,16 @@ public TaskGenerateOpenAPI createTaskGenerateOpenAPI(Options options) {
4749
return new TestTaskGenerateOpenAPI(options);
4850
}
4951

52+
@Override
53+
public Set<Class<? extends Annotation>> getBrowserCallableAnnotations() {
54+
return Set.of();
55+
}
56+
57+
@Override
58+
public boolean hasBrowserCallables(Options options) {
59+
return true;
60+
}
61+
5062
/**
5163
* An abstract parent for the test endpoints generator tasks.
5264
*/

vaadin-dev-server/src/test/java/com/vaadin/base/devserver/startup/DevModeClassFinderTest.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.Arrays;
2121
import java.util.Collection;
2222
import java.util.Collections;
23+
import java.util.HashSet;
2324
import java.util.List;
2425
import java.util.stream.Collectors;
2526
import java.util.stream.Stream;
@@ -55,7 +56,7 @@
5556
public class DevModeClassFinderTest {
5657

5758
private DevModeClassFinder classFinder = new DevModeClassFinder(
58-
Collections.emptySet());
59+
Collections.emptySet(), Collections.emptySet());
5960

6061
@Test
6162
public void applicableClasses_knownClasses() {
@@ -127,7 +128,7 @@ public void callGetgetAnnotatedClassesByName_unexpectedType_throw()
127128
@Test(expected = IllegalArgumentException.class)
128129
public void callGetSubTypesOfByClass_unexpectedType_throw() {
129130
DevModeClassFinder classFinder = new DevModeClassFinder(
130-
Collections.emptySet());
131+
Collections.emptySet(), Collections.emptySet());
131132
classFinder.getSubTypesOf(Object.class);
132133
}
133134

vaadin-dev-server/src/test/java/com/vaadin/base/devserver/startup/DevModeInitializerTest.java

+4
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,8 @@ public void should_generateOpenApi_when_EndpointPresents()
357357
EndpointRequestUtil.class, Mockito.CALLS_REAL_METHODS)) {
358358
util.when(() -> EndpointRequestUtil
359359
.isHillaAvailable(Mockito.any())).thenReturn(true);
360+
util.when(() -> EndpointRequestUtil
361+
.areHillaEndpointsUsed(Mockito.any())).thenReturn(true);
360362
devModeStartupListener.onStartup(classes, servletContext);
361363
handler = getDevModeHandler();
362364
waitForDevServer();
@@ -425,6 +427,8 @@ public void should_generateTs_files() throws Exception {
425427
EndpointRequestUtil.class, Mockito.CALLS_REAL_METHODS)) {
426428
util.when(() -> EndpointRequestUtil
427429
.isHillaAvailable(Mockito.any())).thenReturn(true);
430+
util.when(() -> EndpointRequestUtil
431+
.areHillaEndpointsUsed(Mockito.any())).thenReturn(true);
428432
devModeStartupListener.onStartup(classes, servletContext);
429433
handler = getDevModeHandler();
430434
waitForDevServer();

vaadin-spring/src/main/java/com/vaadin/flow/spring/SpringBootAutoConfiguration.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
* @author Vaadin Ltd
4848
*
4949
*/
50-
@Configuration
50+
@Configuration(proxyBeanMethods = false)
5151
@AutoConfigureBefore(WebMvcAutoConfiguration.class)
5252
@ConditionalOnClass(ServletContextInitializer.class)
5353
@EnableConfigurationProperties(VaadinConfigurationProperties.class)

0 commit comments

Comments
 (0)