diff --git a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/CodeAction.java b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/CodeAction.java index 675ffa616..6117a2bed 100644 --- a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/CodeAction.java +++ b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/CodeAction.java @@ -10,7 +10,12 @@ *******************************************************************************/ package org.springframework.ide.vscode.languageserver.testharness; +import static org.junit.Assert.assertEquals; + +import java.util.List; + import org.eclipse.lsp4j.Command; +import org.springframework.ide.vscode.commons.util.Assert; /** * Wrapper for the test harness to refer to and manipulate a @@ -18,7 +23,7 @@ import org.eclipse.lsp4j.Command; */ public class CodeAction { - private final Command command; + public final Command command; private LanguageServerHarness harness; public CodeAction(LanguageServerHarness harness, Command command) { diff --git a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java index 6ddb47794..aab5f25a1 100644 --- a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java +++ b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java @@ -814,7 +814,7 @@ public class Editor { return ca; } - public void assertQuickfixes(Diagnostic problem, String... expectedLabels) throws Exception { + public List assertQuickfixes(Diagnostic problem, String... expectedLabels) throws Exception { List actions = getCodeActions(problem); StringBuilder expecteds = new StringBuilder(); for (String l : expectedLabels) { @@ -825,6 +825,7 @@ public class Editor { actuals.append(a.getLabel()+"\n"); } assertEquals(expecteds.toString(), actuals.toString()); + return actions; } public void assertText(String expected) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/quickfix/CommonQuickfixes.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/quickfix/CommonQuickfixes.java index 6cdcd3729..5f95a5255 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/quickfix/CommonQuickfixes.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/quickfix/CommonQuickfixes.java @@ -104,10 +104,11 @@ public class CommonQuickfixes { IJavaProject project = p.get(); List sourceFolders = IClasspathUtil.getSourceFolders(project.getClasspath()).collect(Collectors.toList()); if (!sourceFolders.isEmpty()) { + File preferredSourceFolder = getPreferredMetadataSourceFolder(sourceFolders); WorkspaceEdit we = new WorkspaceEdit(new ArrayList>()); Path metadataFilePath = sourceFolders.stream().map(f -> f.toPath()).map(path -> path.resolve(METADATA_PATH)).filter(path -> Files.exists(path)).findFirst().orElse(null); if (metadataFilePath == null) { - metadataFilePath = sourceFolders.get(0).toPath().resolve(METADATA_PATH); + metadataFilePath = preferredSourceFolder.toPath().resolve(METADATA_PATH); we.getDocumentChanges().add(Either.forRight(new CreateFile(metadataFilePath.toUri().toString()))); } if (metadataFilePath != null) { @@ -143,4 +144,19 @@ public class CommonQuickfixes { } } + private File getPreferredMetadataSourceFolder(List sourceFolders) { + Path mainResources = Paths.get("main", "resources"); + for (File file : sourceFolders) { + if (file.toPath().endsWith(mainResources)) { + return file; + } + } + return sourceFolders.get(0); + } + + private void selectMetadataSourceFolder(List sourceFolders) { + // TODO Auto-generated method stub + + } + } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/ApplicationYamlASTReconciler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/ApplicationYamlASTReconciler.java index 65c932c3c..585c76837 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/ApplicationYamlASTReconciler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/ApplicationYamlASTReconciler.java @@ -357,7 +357,7 @@ public class ApplicationYamlASTReconciler implements YamlASTReconciler { case CommonQuickfixes.MISSING_PROPERTY_APP_QF_ID: for (String missingProp : getUnknownProperties(name, entry.getValueNode(), new ArrayList<>())) { p.addQuickfix(new QuickfixData<>(fixType, - new MissingPropertyData(new TextDocumentIdentifier(docUri), name), + new MissingPropertyData(new TextDocumentIdentifier(docUri), missingProp), "Create metadata for `" + missingProp +"`")); } break; diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java index 876c9a2fa..085a2c8fa 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java @@ -11,16 +11,19 @@ package org.springframework.ide.vscode.boot.test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.springframework.ide.vscode.boot.test.DefinitionLinkAsserts.field; import static org.springframework.ide.vscode.boot.test.DefinitionLinkAsserts.method; import static org.springframework.ide.vscode.languageserver.testharness.Editor.INDENTED_COMPLETION; +import java.io.File; import java.time.Duration; -import java.util.HashMap; +import java.util.List; import java.util.Optional; +import org.apache.commons.io.FileUtils; import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.DiagnosticSeverity; import org.junit.After; @@ -38,20 +41,19 @@ import org.springframework.ide.vscode.boot.configurationmetadata.Deprecation.Lev import org.springframework.ide.vscode.boot.editor.harness.AbstractPropsEditorTest; import org.springframework.ide.vscode.boot.metadata.CachingValueProvider; import org.springframework.ide.vscode.boot.metadata.PropertyInfo; +import org.springframework.ide.vscode.boot.properties.quickfix.MissingPropertyData; +import org.springframework.ide.vscode.commons.jandex.MethodImpl; +import org.springframework.ide.vscode.commons.jandex.TestDataProvider; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject; import org.springframework.ide.vscode.commons.util.RunnableWithException; import org.springframework.ide.vscode.commons.util.StringUtil; import org.springframework.ide.vscode.commons.util.text.LanguageId; +import org.springframework.ide.vscode.commons.yaml.reconcile.MissingPropertiesData; import org.springframework.ide.vscode.languageserver.testharness.CodeAction; import org.springframework.ide.vscode.languageserver.testharness.Editor; import org.springframework.test.context.junit4.SpringRunner; -import com.google.common.collect.ImmutableList; - -import org.springframework.ide.vscode.commons.jandex.MethodImpl; -import org.springframework.ide.vscode.commons.jandex.TestDataProvider; - /** * This class is a placeholder where we will attempt to copy and port @@ -3020,31 +3022,101 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { assertCompletionDetailsWithDeprecation("foo:\n nam<*>", "alt-name", "String", null, Boolean.TRUE); } - @Test public void missingPropertyQuickfix() throws Exception { + @Test public void missingPropertyQuickfix_1() throws Exception { IJavaProject p = createPredefinedMavenProject("empty-boot-2.1.0-app"); useProject(p); + File location = new File(p.getLocationUri()); - { + File metadata_rsrc = new File(location, "src/main/resources/META-INF/additional-spring-configuration-metadata.json"); + File metadata_java = new File(location, "src/main/java/META-INF/additional-spring-configuration-metadata.json"); + + assertFalse( + metadata_rsrc.exists() + ); + assertFalse( + metadata_java.exists() + ); + try { Editor editor = newEditor( "myapp:\n" + " orders:\n" + " pages: 10" ); Diagnostic problem = editor.assertProblems("myapp|Unknown").get(0); - editor.assertQuickfixes(problem, "Create metadata for `myapp.orders.pages`"); + CodeAction fix = editor.assertQuickfixes(problem, "Create metadata for `myapp.orders.pages`").get(0); + assertEquals("myapp.orders.pages", getMissingPropertyName(fix)); + + fix.perform(); + assertTrue(metadata_rsrc.exists()); + assertEquals( + "{\"properties\": [{\n" + + " \"name\": \"myapp.orders.pages\",\n" + + " \"type\": \"java.lang.String\",\n" + + " \"description\": \"A description for 'myapp.orders.pages'\"\n" + + "}]}", + FileUtils.readFileToString(metadata_rsrc, "utf8") + ); + + } finally { + FileUtils.deleteQuietly(new File(location, "src/main/resources/META-INF")); + FileUtils.deleteQuietly(new File(location, "src/main/java/META-INF")); } + } + + private String getMissingPropertyName(CodeAction fix) { + String cmd = fix.command.getCommand(); + assertEquals("sts.vscode-spring-boot.codeAction", cmd); + List args = fix.command.getArguments(); + assertEquals("MISSING_PROPERTY_APP", args.get(0)); + MissingPropertyData data = (MissingPropertyData) args.get(1); + return data.getProperty(); + } + + @Test public void missingPropertyQuickfix_2() throws Exception { + IJavaProject p = createPredefinedMavenProject("empty-boot-2.1.0-app"); + useProject(p); + File location = new File(p.getLocationUri()); - { + File metadata_rsrc = new File(location, "src/main/resources/META-INF/additional-spring-configuration-metadata.json"); + File metadata_java = new File(location, "src/main/java/META-INF/additional-spring-configuration-metadata.json"); + + assertFalse( + metadata_rsrc.exists() + ); + assertFalse( + metadata_java.exists() + ); + try { Editor editor = newEditor( "myapp:\n" + " orders:\n" + " pageSize: 10" ); Diagnostic problem = editor.assertProblems("myapp|Unknown").get(0); - editor.assertQuickfixes(problem, "Create metadata for `myapp.orders.page-size`"); + CodeAction fix = editor.assertQuickfixes(problem, "Create metadata for `myapp.orders.page-size`").get(0); + assertEquals("myapp.orders.page-size", getMissingPropertyName(fix)); + } finally { + FileUtils.deleteQuietly(new File(location, "src/main/resources/META-INF")); + FileUtils.deleteQuietly(new File(location, "src/main/java/META-INF")); } + } - { + @Test public void missingPropertyQuickfix_3() throws Exception { + IJavaProject p = createPredefinedMavenProject("empty-boot-2.1.0-app"); + useProject(p); + File location = new File(p.getLocationUri()); + + File metadata_rsrc = new File(location, "src/main/resources/META-INF/additional-spring-configuration-metadata.json"); + File metadata_java = new File(location, "src/main/java/META-INF/additional-spring-configuration-metadata.json"); + + assertFalse( + metadata_rsrc.exists() + ); + assertFalse( + metadata_java.exists() + ); + try { + Editor editor = newEditor( "myapp:\n" + " orders:\n" + @@ -3056,13 +3128,13 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { "Create metadata for `myapp.orders.page-size`", "Create metadata for `myapp.orders.start`" ); + + } finally { + FileUtils.deleteQuietly(new File(location, "src/main/resources/META-INF")); + FileUtils.deleteQuietly(new File(location, "src/main/java/META-INF")); } - - //TODO: a test case that verifies codeAction.perform has expected result. - // Carefull though, if you do add this you must make sure to cleanup any side-effects the quickfix does - // to files in the project. - } + @Test public void testDeprecatedPropertyQuickfixSimple() throws Exception { //A simple case for starters. The path edits aren't too complicated since there's