diff --git a/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/YTypeFactory.java b/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/YTypeFactory.java index b4e1cdc04..2c628f4d7 100644 --- a/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/YTypeFactory.java +++ b/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/YTypeFactory.java @@ -393,6 +393,7 @@ public class YTypeFactory { public abstract String toString(); // force each sublcass to implement a (nice) toString method. public void addProperty(YTypedProperty p) { + Assert.isNotNull(p); cachedPropertyMap = null; propertyList.add(p); } diff --git a/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/PipelineYmlSchema.java b/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/PipelineYmlSchema.java index 0b6da217c..d18518142 100644 --- a/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/PipelineYmlSchema.java +++ b/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/PipelineYmlSchema.java @@ -13,6 +13,7 @@ package org.springframework.ide.vscode.concourse; import java.time.ZoneId; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector; @@ -168,9 +169,6 @@ public class PipelineYmlSchema implements YamlSchema { // define schema types TOPLEVEL_TYPE = f.ybean("Pipeline"); - YAtomicType t_version = f.yatomic("Version"); - t_version.addHints("latest", "every"); - t_resource_type_name = f.yenumFromHints("ResourceType Name", (dc) -> (parseString, validValues) -> { return "The '"+parseString+"' Resource Type does not exist. Existing types: "+validValues; @@ -226,10 +224,19 @@ public class PipelineYmlSchema implements YamlSchema { addProp(t_resource, "webhook_token", t_ne_string); AbstractType t_image_resource = f.ybean("ImageResource"); - for (YTypedProperty p : t_resource.getProperties()) { - if (!"name".equals(p.getName())) { - t_image_resource.addProperty(p); - } + { + Map resourceProperties = t_resource.getPropertiesMap(); + //Some of the properties in 'image_resource' are just like the ones in regular resource. + // So let's copy them... + t_image_resource.addProperty(resourceProperties.get("type")); + t_image_resource.addProperty(resourceProperties.get("source")); + addProp(t_image_resource, "params", t_params); + //TODO: make ImageResourceParams dynamic based on resource type. Somewhat like below, but that code isn't exactly + // right (yet :-) +// addProp(t_image_resource, "params", f.contextAware("ImageResourceParams", (dc) -> +// resourceTypes.getInParamsType(getImageResourceType(models, dc)) +// )); + addProp(t_image_resource, "version", t_resource_version); } YAtomicType t_platform = f.yenum("Platform", "windows", "linux", "darwin"); diff --git a/headless-services/concourse-language-server/src/test/java/org/springframework/ide/vscode/concourse/ConcourseEditorTest.java b/headless-services/concourse-language-server/src/test/java/org/springframework/ide/vscode/concourse/ConcourseEditorTest.java index 64fd83428..3c0f89faf 100644 --- a/headless-services/concourse-language-server/src/test/java/org/springframework/ide/vscode/concourse/ConcourseEditorTest.java +++ b/headless-services/concourse-language-server/src/test/java/org/springframework/ide/vscode/concourse/ConcourseEditorTest.java @@ -4196,6 +4196,122 @@ public class ConcourseEditorTest { editor.assertHoverContains("path", "The path to a directory to be cached"); } + @Test public void image_resource_completions() throws Exception { + Editor editor = harness.newEditor(LanguageId.CONCOURSE_TASK, + "platform: linux\n" + + "run:\n" + + " path: blah\n" + + "<*>" + ); + editor.assertContextualCompletions(PLAIN_COMPLETION, + "imgrs<*>" + , // ==> + "image_resource:\n" + + " type: <*>" + ); + + editor.setText( + "platform: linux\n" + + "run:\n" + + " path: blah\n" + + "image_resource:\n" + + " type: <*>" + ); + editor.assertContextualCompletions("dckr<*>", + "docker-image\n" + + " source:\n" + + " repository: <*>" + ); + + editor.setText( + "platform: linux\n" + + "run:\n" + + " path: blah\n" + + "image_resource:\n" + + " type: docker-image\n" + + " <*>" + ); + editor.assertContextualCompletions(PLAIN_COMPLETION, "<*>" + , // => + "params:\n" + + " <*>" + , // ---- + "source:\n" + + " <*>" //Actually... would expect 'repository' to be filled in by snippet here! + , // ---- + "version:\n" + + " <*>" + ); + } + + @Test public void image_resource_subHovers() throws Exception { + Editor editor = harness.newEditor(LanguageId.CONCOURSE_TASK, + "platform: linux\n" + + "run:\n" + + " path: blah\n" + + "image_resource:\n" + + " type: docker-image\n" + + " source:\n" + + " repository: some-docker-image\n" + + " version: latest" + ); + editor.assertHoverContains("type", "type of the resource. Usually `docker-image`."); + editor.assertHoverContains("source", "Configuration for the resource"); + editor.assertHoverContains("params", "A map of arbitrary configuration to forward to the resource"); + editor.assertHoverContains("version", "A specific version of the resource to fetch"); + } + + @Test public void image_resource_version_reconcile() throws Exception { + Editor editor = harness.newEditor(LanguageId.CONCOURSE_TASK, + "platform: linux\n" + + "run:\n" + + " path: blah\n" + + "image_resource:\n" + + " type: docker-image\n" + + " source:\n" + + " repository: some-docker-image\n" + + " version: latest" + ); + editor.assertProblems(/*NONE*/); + + editor = harness.newEditor(LanguageId.CONCOURSE_TASK, + "platform: linux\n" + + "run:\n" + + " path: blah\n" + + "image_resource:\n" + + " type: docker-image\n" + + " source:\n" + + " repository: some-docker-image\n" + + " version: every" + ); + editor.assertProblems(/*NONE*/); + + editor = harness.newEditor(LanguageId.CONCOURSE_TASK, + "platform: linux\n" + + "run:\n" + + " path: blah\n" + + "image_resource:\n" + + " type: docker-image\n" + + " source:\n" + + " repository: some-docker-image\n" + + " version:\n"+ + " anystring: goes\n" + ); + editor.assertProblems(/*NONE*/); + + editor = harness.newEditor(LanguageId.CONCOURSE_TASK, + "platform: linux\n" + + "run:\n" + + " path: blah\n" + + "image_resource:\n" + + " type: docker-image\n" + + " source:\n" + + " repository: some-docker-image\n" + + " version: not-a-valid-version" + ); + editor.assertProblems("not-a-valid-version|Valid values are: [every, latest]"); + } + ////////////////////////////////////////////////////////////////////////////// private void assertContextualCompletions(String conText, String textBefore, String... textAfter) throws Exception {