From ca4fc3c0f40904257f004c8ca1a8c3cfebddd0b1 Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Thu, 27 Mar 2025 12:03:23 +0100 Subject: [PATCH] enable feign client and feign config indexing and reconciling to keep track of dependencies and trigger re-reconciling --- .../ide/vscode/boot/app/JdtConfig.java | 5 + .../java/beans/FeignClientSymbolProvider.java | 22 ++- ...ConfigurationIfBeansPresentReconciler.java | 42 +++-- .../reconcilers/FeignClientReconciler.java | 86 ++++++++++ .../java/reconcilers/ReconcilingContext.java | 4 +- .../SpringIndexerJavaDependencyTracker.java | 4 + ...IfBeansPresentAdvancedReconcilingTest.java | 148 ++++++++++++++++++ .../test-projects/test-feign-indexing/pom.xml | 2 +- .../feign/demo/FeignConfigExample.java | 2 +- .../feign/demo/FeignConfigNotRegistered.java | 12 ++ 10 files changed, 310 insertions(+), 17 deletions(-) create mode 100644 headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/FeignClientReconciler.java create mode 100644 headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/reconcilers/test/AddConfigurationIfBeansPresentAdvancedReconcilingTest.java create mode 100644 headless-services/spring-boot-language-server/src/test/resources/test-projects/test-feign-indexing/src/main/java/com/example/feign/demo/FeignConfigNotRegistered.java diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/JdtConfig.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/JdtConfig.java index c25f2ae9b..e0b00badd 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/JdtConfig.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/JdtConfig.java @@ -38,6 +38,7 @@ import org.springframework.ide.vscode.boot.java.reconcilers.BeanPostProcessingIg import org.springframework.ide.vscode.boot.java.reconcilers.BeanRegistrarDeclarationReconciler; import org.springframework.ide.vscode.boot.java.reconcilers.Boot3NotSupportedTypeReconciler; import org.springframework.ide.vscode.boot.java.reconcilers.EntityIdForRepoReconciler; +import org.springframework.ide.vscode.boot.java.reconcilers.FeignClientReconciler; import org.springframework.ide.vscode.boot.java.reconcilers.HttpSecurityLambdaDslReconciler; import org.springframework.ide.vscode.boot.java.reconcilers.ImplicitWebAnnotationNamesReconciler; import org.springframework.ide.vscode.boot.java.reconcilers.ModulithTypeReferenceViolationReconciler; @@ -74,6 +75,10 @@ public class JdtConfig { return new BeanRegistrarDeclarationReconciler(server.getQuickfixRegistry(), springIndex); } + @Bean FeignClientReconciler feignClientReconciler(SpringMetamodelIndex springIndex) { + return new FeignClientReconciler(springIndex); + } + @Bean AutowiredFieldIntoConstructorParameterReconciler autowiredFieldIntoConstructorParameterReconciler(SimpleLanguageServer server) { return new AutowiredFieldIntoConstructorParameterReconciler(server.getQuickfixRegistry()); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/FeignClientSymbolProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/FeignClientSymbolProvider.java index 297153d2e..bc0f1352c 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/FeignClientSymbolProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/FeignClientSymbolProvider.java @@ -13,6 +13,7 @@ package org.springframework.ide.vscode.boot.java.beans; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -31,10 +32,13 @@ import org.eclipse.lsp4j.jsonrpc.messages.Tuple; import org.eclipse.lsp4j.jsonrpc.messages.Tuple.Two; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex; +import org.springframework.ide.vscode.boot.java.Annotations; import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider; import org.springframework.ide.vscode.boot.java.utils.ASTUtils; import org.springframework.ide.vscode.boot.java.utils.CachedSymbol; import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJavaContext; +import org.springframework.ide.vscode.commons.protocol.spring.AnnotationAttributeValue; import org.springframework.ide.vscode.commons.protocol.spring.AnnotationMetadata; import org.springframework.ide.vscode.commons.protocol.spring.Bean; import org.springframework.ide.vscode.commons.protocol.spring.InjectionPoint; @@ -53,8 +57,11 @@ public class FeignClientSymbolProvider implements SymbolProvider { WorkspaceSymbol symbol = result.getFirst(); Bean beanDefinition = result.getSecond(); + context.getGeneratedSymbols().add(new CachedSymbol(context.getDocURI(), context.getLastModified(), symbol)); context.getBeans().add(new CachedBean(context.getDocURI(), beanDefinition)); + + markNewFeignConfigTypeForReconciling(beanDefinition, context); } } catch (BadLocationException e) { @@ -62,6 +69,19 @@ public class FeignClientSymbolProvider implements SymbolProvider { } } + private void markNewFeignConfigTypeForReconciling(Bean beanDefinition, SpringIndexerJavaContext context) { + List configurationTypes = Arrays.stream(beanDefinition.getAnnotations()) + .filter(annotation -> annotation.getAnnotationType().equals(Annotations.FEIGN_CLIENT)) + .map(annotation -> annotation.getAttributes()) + .filter(attributes -> attributes.containsKey("configuration")) + .map(attributes -> attributes.get("configuration")) + .flatMap(attributeValues -> Arrays.stream(attributeValues)) + .map(attributeValue -> attributeValue.getName()) + .toList(); + + + } + private Two createSymbol(Annotation node, ITypeBinding annotationType, Collection metaAnnotations, TextDocument doc) throws BadLocationException { String annotationTypeName = annotationType.getName(); Collection metaAnnotationNames = metaAnnotations.stream() @@ -94,7 +114,7 @@ public class FeignClientSymbolProvider implements SymbolProvider { .toArray(AnnotationMetadata[]::new); Bean beanDefinition = new Bean(beanName, beanType == null ? "" : beanType.getQualifiedName(), location, injectionPoints, supertypes, annotations, false, symbol.getName()); - + return Tuple.two(symbol, beanDefinition); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/AddConfigurationIfBeansPresentReconciler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/AddConfigurationIfBeansPresentReconciler.java index 28ed4c426..bff2568cc 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/AddConfigurationIfBeansPresentReconciler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/AddConfigurationIfBeansPresentReconciler.java @@ -69,7 +69,7 @@ public class AddConfigurationIfBeansPresentReconciler implements JdtAstReconcile @Override public boolean visit(TypeDeclaration classDecl) { - if (isApplicableClass(project, cu, classDecl)) { + if (isApplicableClass(project, cu, classDecl, context)) { SimpleName nameAst = classDecl.getName(); ReconcileProblemImpl problem = new ReconcileProblemImpl(getProblemType(), PROBLEM_LABEL, nameAst.getStartPosition(), nameAst.getLength()); @@ -92,7 +92,7 @@ public class AddConfigurationIfBeansPresentReconciler implements JdtAstReconcile }; } - private boolean isApplicableClass(IJavaProject project, CompilationUnit cu, TypeDeclaration classDecl) { + private boolean isApplicableClass(IJavaProject project, CompilationUnit cu, TypeDeclaration classDecl, ReconcilingContext context) { if (classDecl.isInterface()) { return false; } @@ -128,15 +128,22 @@ public class AddConfigurationIfBeansPresentReconciler implements JdtAstReconcile // No '@Configuration' present. Check if any methods have '@Bean' annotation for (MethodDeclaration m : classDecl.getMethods()) { - if (isBeanMethod(m) && !isException(project, classDecl)) { - return true; + if (isBeanMethod(m)) { + if (context.isIndexComplete()) { + if (!isException(project, classDecl, context)) { + return true; + } + } + else { + throw new RequiredCompleteIndexException(); + } } } return false; } - private boolean isException(IJavaProject project, TypeDeclaration classDecl) { + private boolean isException(IJavaProject project, TypeDeclaration classDecl, ReconcilingContext context) { if (springIndex == null) { return false; } @@ -154,19 +161,30 @@ public class AddConfigurationIfBeansPresentReconciler implements JdtAstReconcile return true; } - boolean isConfiguredAsfeignConfigClass = Arrays.stream(beans) - .flatMap(bean -> Arrays.stream(bean.getAnnotations())) - .filter(annotation -> annotation.getAnnotationType().equals(Annotations.FEIGN_CLIENT)) - .map(annotation -> annotation.getAttributes().get("configuration")) - .flatMap(attributeValues -> attributeValues != null ? Arrays.stream(attributeValues) : Stream.empty()) - .anyMatch(attributeValue -> attributeValue.getName().equals(beanClassName)); + List feignClients = Arrays.stream(beans) + .filter(bean -> isConfiguredAsFeignConfigClass(bean, beanClassName)) + .toList(); - if (isConfiguredAsfeignConfigClass) { + if (!feignClients.isEmpty()) { + + // record dependency for feign clients that have this type configured as feign configuration + for (Bean feignClient : feignClients) { + context.addDependency(feignClient.getType()); + } + return true; } return false; } + + private boolean isConfiguredAsFeignConfigClass(Bean bean, String beanClassName) { + return Arrays.stream(bean.getAnnotations()) + .filter(annotation -> annotation.getAnnotationType().equals(Annotations.FEIGN_CLIENT)) + .map(annotation -> annotation.getAttributes().get("configuration")) + .flatMap(attributeValues -> attributeValues != null ? Arrays.stream(attributeValues) : Stream.empty()) + .anyMatch(attributeValue -> attributeValue.getName().equals(beanClassName)); + } private static boolean isBeanMethod(MethodDeclaration m) { AnnotationHierarchies annotationHierarchies = AnnotationHierarchies.get(m); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/FeignClientReconciler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/FeignClientReconciler.java new file mode 100644 index 000000000..284b625f0 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/FeignClientReconciler.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2025 Broadcom, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VMware, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.reconcilers; + +import java.net.URI; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.jdt.core.dom.ASTVisitor; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.TypeDeclaration; +import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex; +import org.springframework.ide.vscode.boot.java.Annotations; +import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchies; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.java.SpringProjectUtil; +import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType; +import org.springframework.ide.vscode.commons.protocol.spring.Bean; +import org.springframework.ide.vscode.commons.protocol.spring.BeanMethodContainerElement; +import org.springframework.ide.vscode.commons.protocol.spring.SpringIndexElement; +import org.springframework.ide.vscode.commons.util.UriUtil; + +public class FeignClientReconciler implements JdtAstReconciler { + + private final SpringMetamodelIndex springIndex; + + public FeignClientReconciler(SpringMetamodelIndex springIndex) { + this.springIndex = springIndex; + } + + @Override + public boolean isApplicable(IJavaProject project) { + return SpringProjectUtil.hasDependencyStartingWith(project, "spring-cloud-openfeign-core", null); + } + + @Override + public ProblemType getProblemType() { + return null; + } + + @Override + public ASTVisitor createVisitor(IJavaProject project, URI docURI, CompilationUnit cu, ReconcilingContext context) { + return new ASTVisitor() { + + @Override + public boolean visit(TypeDeclaration node) { + AnnotationHierarchies annotationHierarchy = AnnotationHierarchies.get(node); + + if (!annotationHierarchy.isAnnotatedWith(node.resolveBinding(), Annotations.FEIGN_CLIENT)) { + return true; + } + + List elements = context.getCreatedIndexElements(); + + List configurationTypes = elements.stream() + .filter(element -> element instanceof Bean) + .map(element -> (Bean) element) + .flatMap(bean -> Arrays.stream(bean.getAnnotations())) + .filter(annotation -> annotation.getAnnotationType().equals(Annotations.FEIGN_CLIENT)) + .map(annotation -> annotation.getAttributes()) + .filter(attributes -> attributes.containsKey("configuration")) + .flatMap(attributes -> Arrays.stream(attributes.get("configuration"))) + .map(attribute -> attribute.getName()) + .toList(); + + List beanMethodContainers = springIndex.getNodesOfType(BeanMethodContainerElement.class); + + beanMethodContainers.stream().filter(beanContainer -> configurationTypes.contains(beanContainer.getType())) + .map(beanContainer -> beanContainer.getLocation().getUri()).map(docURI -> UriUtil.toFileString(docURI)) + .forEach(file -> context.markForAffetcedFilesIndexing(file)); + + return true; + } + + }; + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/ReconcilingContext.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/ReconcilingContext.java index b7d9df695..a0cd5a2ee 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/ReconcilingContext.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/ReconcilingContext.java @@ -61,8 +61,8 @@ public class ReconcilingContext { return isIndexComplete; } - public void addDependency(String typeOfConfigClassWithImport) { - this.dependencies.add(typeOfConfigClassWithImport); + public void addDependency(String type) { + this.dependencies.add(type); } public Set getDependencies() { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJavaDependencyTracker.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJavaDependencyTracker.java index 94de83448..baed7906b 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJavaDependencyTracker.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJavaDependencyTracker.java @@ -41,6 +41,10 @@ public class SpringIndexerJavaDependencyTracker { public Multimap getAllDependencies() { return dependencies; } + + public Collection get(String file) { + return dependencies.get(file); + } public void update(String file, Set dependenciesForFile) { dependencies.replaceValues(file, dependenciesForFile); diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/reconcilers/test/AddConfigurationIfBeansPresentAdvancedReconcilingTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/reconcilers/test/AddConfigurationIfBeansPresentAdvancedReconcilingTest.java new file mode 100644 index 000000000..7ef3f157d --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/reconcilers/test/AddConfigurationIfBeansPresentAdvancedReconcilingTest.java @@ -0,0 +1,148 @@ +/******************************************************************************* + * Copyright (c) 2025 Broadcom + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Broadcom - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.reconcilers.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.File; +import java.nio.charset.Charset; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.io.FileUtils; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.PublishDiagnosticsParams; +import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Import; +import org.springframework.ide.vscode.boot.app.SpringSymbolIndex; +import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest; +import org.springframework.ide.vscode.boot.bootiful.SymbolProviderTestConf; +import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType; +import org.springframework.ide.vscode.boot.java.utils.test.TestFileScanListener; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.util.UriUtil; +import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness; +import org.springframework.ide.vscode.project.harness.ProjectsHarness; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +/** + * @author Martin Lippert + */ +@ExtendWith(SpringExtension.class) +@BootLanguageServerTest +@Import(SymbolProviderTestConf.class) +public class AddConfigurationIfBeansPresentAdvancedReconcilingTest { + + @Autowired private BootLanguageServerHarness harness; + @Autowired private JavaProjectFinder projectFinder; + @Autowired private SpringSymbolIndex indexer; + + private File directory; + + @BeforeEach + public void setup() throws Exception { + harness.intialize(null); + + directory = new File(ProjectsHarness.class.getResource("/test-projects/test-feign-indexing/").toURI()); + + String projectDir = directory.toURI().toString(); + + // trigger project creation + projectFinder.find(new TextDocumentIdentifier(projectDir)).get(); + + CompletableFuture initProject = indexer.waitOperation(); + initProject.get(5, TimeUnit.SECONDS); + } + + @Test + void testNoErrorOnReferencedFeignConfigClass() throws Exception { + String docUri = directory.toPath().resolve("src/main/java/com/example/feign/demo/FeignConfigExample.java").toUri().toString(); + + PublishDiagnosticsParams diagnosticsResult = harness.getDiagnostics(docUri); + List diagnostics = diagnosticsResult.getDiagnostics(); + + assertEquals(0, diagnostics.size()); + } + + @Test + void testErrorOnNotReferencedFeignConfigClass() throws Exception { + String docUri = directory.toPath().resolve("src/main/java/com/example/feign/demo/FeignConfigNotRegistered.java").toUri().toString(); + + PublishDiagnosticsParams diagnosticsResult = harness.getDiagnostics(docUri); + List diagnostics = diagnosticsResult.getDiagnostics(); + + assertEquals(1, diagnostics.size()); + assertEquals(Boot2JavaProblemType.MISSING_CONFIGURATION_ANNOTATION.getCode(), diagnostics.get(0).getCode().getLeft()); + } + + @Test + void testErrorGoesAwayWhenFeignClientMentionsFeignConfig() throws Exception { + String feignClientDocUri = directory.toPath().resolve("src/main/java/com/example/feign/demo/FeignClientExample.java").toUri().toString(); + String feignConfigRegisterd = directory.toPath().resolve("src/main/java/com/example/feign/demo/FeignConfigExample.java").toUri().toString(); + String feignConfigNotRegisterd = directory.toPath().resolve("src/main/java/com/example/feign/demo/FeignConfigNotRegistered.java").toUri().toString(); + + // now change the config class source code and update doc + TestFileScanListener fileScanListener = new TestFileScanListener(); + indexer.getJavaIndexer().setFileScanListener(fileScanListener); + + String feignClientSource = FileUtils.readFileToString(UriUtil.toFile(feignClientDocUri), Charset.defaultCharset()); + String updatedFeignClientSource = feignClientSource.replace("@FeignClient(name = \"stores\", configuration = FeignConfigExample.class)", + "@FeignClient(name = \"stores\", configuration = {FeignConfigNotRegistered.class, FeignConfigExample.class})"); + + CompletableFuture updateFuture = indexer.updateDocument(feignClientDocUri, updatedFeignClientSource, "test triggered"); + updateFuture.get(5, TimeUnit.SECONDS); + + // check if the bean registrar files have been re-scanned + fileScanListener.assertScannedUri(feignClientDocUri, 1); + fileScanListener.assertScannedUri(feignConfigNotRegisterd, 1); + fileScanListener.assertScannedUri(feignConfigRegisterd, 1); + fileScanListener.assertFileScanCount(3); + + // check diagnostics result + PublishDiagnosticsParams diagnosticsResult = harness.getDiagnostics(feignConfigNotRegisterd); + List diagnostics = diagnosticsResult.getDiagnostics(); + assertEquals(0, diagnostics.size()); + } + + @Test + void testErrorAppearsWhenFeignClientNotMentionsFeignConfigAnymore() throws Exception { + String feignClientDocUri = directory.toPath().resolve("src/main/java/com/example/feign/demo/FeignClientExample.java").toUri().toString(); + String feignConfigRegisterd = directory.toPath().resolve("src/main/java/com/example/feign/demo/FeignConfigExample.java").toUri().toString(); + + // now change the config class source code and update doc + TestFileScanListener fileScanListener = new TestFileScanListener(); + indexer.getJavaIndexer().setFileScanListener(fileScanListener); + + String feignClientSource = FileUtils.readFileToString(UriUtil.toFile(feignClientDocUri), Charset.defaultCharset()); + String updatedFeignClientSource = feignClientSource.replace("@FeignClient(name = \"stores\", configuration = FeignConfigExample.class)", + "@FeignClient(name = \"stores\")"); + + CompletableFuture updateFuture = indexer.updateDocument(feignClientDocUri, updatedFeignClientSource, "test triggered"); + updateFuture.get(5, TimeUnit.SECONDS); + + // check if the bean registrar files have been re-scanned + fileScanListener.assertScannedUri(feignClientDocUri, 1); + fileScanListener.assertScannedUri(feignConfigRegisterd, 1); + fileScanListener.assertFileScanCount(2); + + // check diagnostics result + PublishDiagnosticsParams diagnosticsResult = harness.getDiagnostics(feignConfigRegisterd); + List diagnostics = diagnosticsResult.getDiagnostics(); + assertEquals(1, diagnostics.size()); + assertEquals(Boot2JavaProblemType.MISSING_CONFIGURATION_ANNOTATION.getCode(), diagnostics.get(0).getCode().getLeft()); + } + +} diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-feign-indexing/pom.xml b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-feign-indexing/pom.xml index ff07d9f99..d0f3f277f 100644 --- a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-feign-indexing/pom.xml +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-feign-indexing/pom.xml @@ -9,7 +9,7 @@ com.example.qualifier - feign-demo + test-feign-indexing 0.0.1-SNAPSHOT feign-demo Demo project for Spring Boot diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-feign-indexing/src/main/java/com/example/feign/demo/FeignConfigExample.java b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-feign-indexing/src/main/java/com/example/feign/demo/FeignConfigExample.java index bde53ad14..2fedf0377 100644 --- a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-feign-indexing/src/main/java/com/example/feign/demo/FeignConfigExample.java +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-feign-indexing/src/main/java/com/example/feign/demo/FeignConfigExample.java @@ -5,7 +5,7 @@ import org.springframework.context.annotation.Bean; public class FeignConfigExample { @Bean - public BeanType specialBean() { + BeanType specialBean() { return new BeanType(); } diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-feign-indexing/src/main/java/com/example/feign/demo/FeignConfigNotRegistered.java b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-feign-indexing/src/main/java/com/example/feign/demo/FeignConfigNotRegistered.java new file mode 100644 index 000000000..6802aa7dd --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-feign-indexing/src/main/java/com/example/feign/demo/FeignConfigNotRegistered.java @@ -0,0 +1,12 @@ +package com.example.feign.demo; + +import org.springframework.context.annotation.Bean; + +public class FeignConfigNotRegistered { + + @Bean + BeanType specialBean2() { + return new BeanType(); + } + +}