From 32d1e6fd36b7d6320a7b3bec78162ef33bcc192b Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Tue, 23 Jul 2019 11:43:41 -0700 Subject: [PATCH] Add support for 'docker' attribute to CF editor --- ...ManifestYamlLanguageServerInitializer.java | 11 +++ .../manifest/yaml/ManifestYmlSchema.java | 14 +++- .../description-by-prop-name/docker.md | 16 ++++ .../manifest/yaml/ManifestYamlEditorTest.java | 80 ++++++++++++++++++- .../manifest/yaml/ManifestYmlSchemaTest.java | 1 + .../editor/harness/AdHocPropertyHarness.java | 9 +-- .../boot/metadata/IndexNavigatorTest.java | 2 +- .../boot/test/ApplicationYamlEditorTest.java | 33 ++++++++ 8 files changed, 155 insertions(+), 11 deletions(-) create mode 100644 headless-services/manifest-yaml-language-server/src/main/resources/description-by-prop-name/docker.md diff --git a/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlLanguageServerInitializer.java b/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlLanguageServerInitializer.java index 361360246..451c1827c 100644 --- a/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlLanguageServerInitializer.java +++ b/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlLanguageServerInitializer.java @@ -50,6 +50,7 @@ import org.springframework.ide.vscode.commons.yaml.reconcile.ASTTypeCache; import org.springframework.ide.vscode.commons.yaml.reconcile.TypeBasedYamlSymbolHandler; import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaBasedReconcileEngine; import org.springframework.ide.vscode.commons.yaml.schema.YValueHint; +import org.springframework.ide.vscode.commons.yaml.snippet.SchemaBasedSnippetGenerator; import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureProvider; import org.springframework.stereotype.Component; @@ -80,6 +81,7 @@ public class ManifestYamlLanguageServerInitializer implements InitializingBean { YamlASTProvider parser = new YamlParser(); schema = new ManifestYmlSchema(getHintProviders()); + enableSnippets(schema, true); YamlStructureProvider structureProvider = YamlStructureProvider.DEFAULT; YamlAssistContextProvider contextProvider = new SchemaBasedYamlAssistContextProvider(schema); @@ -194,6 +196,15 @@ public class ManifestYamlLanguageServerInitializer implements InitializingBean { }; } + public void enableSnippets(ManifestYmlSchema schema, boolean enable) { + //TODO: move to where schema bean is defined? + if (enable) { + schema.f.setSnippetProvider(new SchemaBasedSnippetGenerator(schema.getTypeUtil(), server::createSnippetBuilder)); + } else { + schema.f.setSnippetProvider(null); + } + } + private CFTargetCache getCfTargetCache() { return this.cfTargetCache; } diff --git a/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchema.java b/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchema.java index 80abec00a..8c7f4a70d 100644 --- a/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchema.java +++ b/headless-services/manifest-yaml-language-server/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchema.java @@ -34,6 +34,7 @@ import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YTypedPro import org.springframework.ide.vscode.commons.yaml.schema.YTypeUtil; import org.springframework.ide.vscode.commons.yaml.schema.YValueHint; import org.springframework.ide.vscode.commons.yaml.schema.YamlSchema; +import org.springframework.ide.vscode.commons.yaml.schema.constraints.Constraints; import org.yaml.snakeyaml.nodes.Node; import com.google.common.collect.ImmutableList; @@ -54,9 +55,10 @@ public final class ManifestYmlSchema implements YamlSchema { private final YAtomicType t_application_name; private ImmutableList definitionTypes = null; + public final YTypeFactory f; private static final Set TOPLEVEL_EXCLUDED = ImmutableSet.of( - "name", "host", "hosts", "routes" + "name", "host", "hosts", "routes", "docker" ); @Override @@ -106,7 +108,7 @@ public final class ManifestYmlSchema implements YamlSchema { Callable> stacksProvider = providers.getStacksProvider(); - YTypeFactory f = new YTypeFactory(); + f = new YTypeFactory(); TYPE_UTIL = f.TYPE_UTIL; // define schema types @@ -188,6 +190,10 @@ public final class ManifestYmlSchema implements YamlSchema { AbstractType t_host = f.yatomic("Host").parseWith(ValueParsers.NE_STRING); + YType t_docker = f.ybean("Docker", + f.yprop("image", t_ne_string).isRequired(true).setDescriptionProvider(descriptionFor("docker")), + f.yprop("username", t_ne_string).setDescriptionProvider(descriptionFor("docker")) + ); YTypedPropertyImpl[] props = { f.yprop("buildpack", t_buildpack), //TODO: replace the above with the below to make 'buildpack' deprecated once we have proper support for `buildpacks` in cf push. @@ -196,6 +202,7 @@ public final class ManifestYmlSchema implements YamlSchema { f.yprop("buildpacks", f.yseq(t_buildpack)), f.yprop("command", t_string), f.yprop("disk_quota", t_memory), + f.yprop("docker", t_docker), f.yprop("domain", t_domain), f.yprop("domains", f.yseq(t_domain)), f.yprop("env", t_env), @@ -226,6 +233,9 @@ public final class ManifestYmlSchema implements YamlSchema { } application.addProperty(prop); } + application.require(Constraints.mutuallyExclusive("docker", "path")); + application.require(Constraints.mutuallyExclusive("docker", "buildpack")); + application.require(Constraints.mutuallyExclusive("docker", "buildpacks")); } private Renderable descriptionFor(String propName) { diff --git a/headless-services/manifest-yaml-language-server/src/main/resources/description-by-prop-name/docker.md b/headless-services/manifest-yaml-language-server/src/main/resources/description-by-prop-name/docker.md new file mode 100644 index 000000000..835f76abc --- /dev/null +++ b/headless-services/manifest-yaml-language-server/src/main/resources/description-by-prop-name/docker.md @@ -0,0 +1,16 @@ +If your app is contained in a Docker image, then you may use the `docker` attribute to specify it and an optional Docker `username`. + +This attribute is a combination of `push` options that include `--docker-image` and `--docker-username`. + +``` +--- + ... + docker: + image: docker-image-repository/docker-image-name + username: docker-user-name +``` + +The command line option `--docker-image` or `-o` overrides `docker.image`. The command line option `--docker-username` overrides `docker.username`. + +The manifest attribute `docker.username` is optional. If it is used, then the password must be provided in the environment variable `CF_DOCKER_PASSWORD`. Additionally, if a Docker username is specified, then a Docker image must also be specified. + diff --git a/headless-services/manifest-yaml-language-server/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlEditorTest.java b/headless-services/manifest-yaml-language-server/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlEditorTest.java index 4c6a61adf..20e93ddc8 100644 --- a/headless-services/manifest-yaml-language-server/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlEditorTest.java +++ b/headless-services/manifest-yaml-language-server/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlEditorTest.java @@ -442,6 +442,11 @@ public class ManifestYamlEditorTest { // --------------- "applications:\n" + "- name: foo\n" + + " docker:\n" + + " image: <*>", + // --------------- + "applications:\n" + + "- name: foo\n" + " domain: <*>", // --------------- "applications:\n" + @@ -1787,7 +1792,7 @@ public class ManifestYamlEditorTest { ); } - @Test public void gotoSymbolInPipeline() throws Exception { + @Test public void gotoSymbolInDocument() throws Exception { Editor editor = harness.newEditor( "applications:\n" + "- name: my-app\n" + @@ -1979,7 +1984,80 @@ public class ManifestYamlEditorTest { } + @Test + public void dockerAttributesValidation() throws Exception { + Editor editor; + + editor = harness.newEditor( + "applications:\n" + + "- name: my-app\n" + + " docker:\n" + + " image: docker-image-repository/docker-image-name\n" + + " bogus: bad" + ); + editor.assertProblems("bogus|Unknown"); + editor = harness.newEditor( + "applications:\n" + + "- name: my-app\n" + + " docker:\n" + + " username: myself\n" + ); + editor.assertProblems( + "docker|'image' is required" + ); + + editor = harness.newEditor( + "applications:\n" + + "- name: my-app\n" + + " buildpacks:\n" + + " - java-buildpack\n" + + " docker:\n" + + " image: somewhere/someimage\n" + ); + editor.assertProblems( + "buildpacks|Only one of 'docker' and 'buildpacks'", + "docker|Only one of 'docker' and 'buildpacks'" + ); + + editor = harness.newEditor( + "applications:\n" + + "- name: my-app\n" + + " buildpack: java-buildpack\n" + + " docker:\n" + + " image: somewhere/someimage\n" + ); + editor.assertProblems( + "buildpack|Only one of 'docker' and 'buildpack'", + "docker|Only one of 'docker' and 'buildpack'" + ); + + editor = harness.newEditor( + "applications:\n" + + "- name: my-app\n" + + " path: /somehere/in/filesystem\n" + + " docker:\n" + + " image: somewhere/someimage\n" + ); + editor.assertProblems( + "path|Only one of 'docker' and 'path'", + "docker|Only one of 'docker' and 'path'" + ); + } + + @Test + public void dockerAttributesHovers() throws Exception { + Editor editor = harness.newEditor( + "applications:\n" + + "- name: my-app\n" + + " docker:\n" + + " image: docker-image-repository/docker-image-name\n" + + " username: myself" + ); + editor.assertHoverContains("docker", "If your app is contained in a Docker image"); + editor.assertHoverContains("image", "Docker image"); + editor.assertHoverContains("username", "If your app is contained in a Docker image"); + } ////////////////////////////////////////////////////////////////////////////// diff --git a/headless-services/manifest-yaml-language-server/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchemaTest.java b/headless-services/manifest-yaml-language-server/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchemaTest.java index f6bcf7ac4..d87f06589 100644 --- a/headless-services/manifest-yaml-language-server/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchemaTest.java +++ b/headless-services/manifest-yaml-language-server/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchemaTest.java @@ -42,6 +42,7 @@ public class ManifestYmlSchemaTest { "buildpacks", "command", "disk_quota", + "docker", "domain", "domains", "env", diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/editor/harness/AdHocPropertyHarness.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/editor/harness/AdHocPropertyHarness.java index a78e3397b..cae618988 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/editor/harness/AdHocPropertyHarness.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/editor/harness/AdHocPropertyHarness.java @@ -10,19 +10,14 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.editor.harness; +import org.springframework.ide.vscode.boot.metadata.FuzzyMapPropertyInfo; import org.springframework.ide.vscode.boot.metadata.ProjectBasedPropertyIndexProvider; import org.springframework.ide.vscode.boot.metadata.PropertyInfo; -import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider; import org.springframework.ide.vscode.commons.util.FuzzyMap; public class AdHocPropertyHarness { - private FuzzyMap adHocProperties = new FuzzyMap() { - @Override - protected String getKey(PropertyInfo entry) { - return entry.getId(); - } - }; + private FuzzyMap adHocProperties = new FuzzyMapPropertyInfo(); protected final ProjectBasedPropertyIndexProvider adHocIndexProvider = project -> adHocProperties; diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/metadata/IndexNavigatorTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/metadata/IndexNavigatorTest.java index 4d60329fe..b3936db47 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/metadata/IndexNavigatorTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/metadata/IndexNavigatorTest.java @@ -24,7 +24,7 @@ import org.springframework.ide.vscode.boot.editor.harness.PropertyIndexHarness; * */ public class IndexNavigatorTest { - + @Test public void testSimple() throws Exception { PropertyIndexHarness harness = indexHarness(); 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 53576f06e..c3975ddc6 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 @@ -71,6 +71,39 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { //////////////////////////////////////////////////////////////////////////////////////// + @Ignore //Ignored because this bug is not yet fixed so the test fails. + @Test public void bug_GH_327() throws Exception { + //See https://github.com/spring-projects/sts4/issues/327 + data("spring.resources.static-locations", "java.lang.String[]", null, "Blah"); + data("spring.devtools.restart.additional-paths", "java.util.List", null, "Blah blah"); + + Editor editor; + + editor = harness.newEditor( + "spring:\n" + + " resources:\n" + + " static_locations: []\n" + + " devtools:\n" + + " restart:\n" + + " additional_paths: []\n" + ); + editor.assertProblems(/*NONE*/); + + //Also check whether reconciler understands the structure when inside of a 'relaxed' name key + editor = harness.newEditor( + "spring:\n" + + " resources:\n" + + " static_locations: not-a-list-1\n" + + " devtools:\n" + + " restart:\n" + + " additional_paths: not-a-list-2\n" + ); + editor.assertProblems( + "not-a-list-1|XXXX", //TODO: fill in proper expected message instead of XXXX + "not-a-list-2|XXXX" //TODO: fill in proper expected message instead of XXXX + ); + } + @Test public void bug_165724475() throws Exception { //See: // https://www.pivotaltracker.com/story/show/165724475