From 4df86968c6e3fd58a8a3779ef248c647d1ac275d Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Sun, 29 Jan 2017 12:35:42 -0800 Subject: [PATCH] Make put/get resource name conditional on absence of explicit resource attribute --- .../commons/yaml/schema/YTypeFactory.java | 44 ++++- .../vscode/concourse/PipelineYmlSchema.java | 29 +++- .../vscode/concourse/ConcourseEditorTest.java | 153 ++++++++++++++++++ 3 files changed, 217 insertions(+), 9 deletions(-) diff --git a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/YTypeFactory.java b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/YTypeFactory.java index 1dcce0c41..1c2d18625 100644 --- a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/YTypeFactory.java +++ b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/YTypeFactory.java @@ -44,7 +44,7 @@ import com.google.common.collect.ImmutableSet; */ public class YTypeFactory { - public YType contextAware(String name, SchemaContextAware guessType) { + public YContextSensitive contextAware(String name, SchemaContextAware guessType) { return new YContextSensitive(name, guessType); } @@ -180,8 +180,9 @@ public class YTypeFactory { addHintProvider((DynamicSchemaContext dc) -> hintProvider); } - public void addHintProvider(SchemaContextAware>> hintProvider) { + public AbstractType addHintProvider(SchemaContextAware>> hintProvider) { this.hintProvider = hintProvider; + return this; } public YValueHint[] getHintValues(DynamicSchemaContext dc) throws Exception { @@ -307,12 +308,15 @@ public class YTypeFactory { /** * Represents a type that depends on the DynamicSchemaContext */ - public static class YContextSensitive extends YAny { + public static class YContextSensitive extends AbstractType { private final SchemaContextAware typeGuesser; + private final String name; + + boolean treatAsAtomic = false; public YContextSensitive(String name, SchemaContextAware typeGuesser) { - super(name); + this.name = name; this.typeGuesser = typeGuesser; } @@ -327,6 +331,38 @@ public class YTypeFactory { return this; } + @Override + public boolean isAtomic() { + return true; + } + + @Override + public boolean isSequenceable() { + return !treatAsAtomic; + } + + @Override + public boolean isMap() { + return !treatAsAtomic; + } + + @Override + public String toString() { + return name; + } + + /** + * If set to false (which is the default), then this type (when not yet inferred to a + * more specific version of itself) will be treated as if it can be anything (i.e atomic, map or sequence) + *

+ * If set to true, then it is treated as strictly atomic type instead (i.e it isn't valid to + * use a map or sequence for its value). + */ + public YContextSensitive treatAsAtomic(boolean isAtomic) { + this.treatAsAtomic = isAtomic; + return this; + } + } diff --git a/vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/PipelineYmlSchema.java b/vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/PipelineYmlSchema.java index 8dd62c0a0..71635665d 100644 --- a/vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/PipelineYmlSchema.java +++ b/vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/PipelineYmlSchema.java @@ -16,6 +16,7 @@ import java.util.stream.Collectors; import org.springframework.ide.vscode.commons.util.MimeTypes; import org.springframework.ide.vscode.commons.util.Renderable; import org.springframework.ide.vscode.commons.util.Renderables; +import org.springframework.ide.vscode.commons.util.ValueParser; import org.springframework.ide.vscode.commons.util.ValueParsers; import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil; import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST; @@ -36,6 +37,7 @@ import org.springframework.ide.vscode.commons.yaml.schema.YTypeUtil; import org.springframework.ide.vscode.commons.yaml.schema.YTypedProperty; import org.springframework.ide.vscode.commons.yaml.schema.YValueHint; import org.springframework.ide.vscode.commons.yaml.schema.YamlSchema; +import org.yaml.snakeyaml.nodes.Node; /** * @author Kris De Volder @@ -196,9 +198,19 @@ public class PipelineYmlSchema implements YamlSchema { addProp(task, "params", t_string_params); task.requireOneOf("image_resource", "image"); + AbstractType t_put_get_name = f.contextAware("Name", (dc) -> { + if (getParentPropertyNode("resource", models, dc)!=null) { + return null; + } else { + return t_resource_name; + } + }) + .treatAsAtomic(true) + .parseWith(ValueParsers.NE_STRING); + YBeanType getStep = f.ybean("GetStep"); - addProp(getStep, "get", t_resource_name); - addProp(getStep, "resource", t_string); + addProp(getStep, "get", t_put_get_name); + addProp(getStep, "resource", t_resource_name); addProp(getStep, "version", t_version); addProp(getStep, "passed", f.yseq(t_job_name)); addProp(getStep, "params", f.contextAware("GetParams", (dc) -> @@ -207,7 +219,7 @@ public class PipelineYmlSchema implements YamlSchema { addProp(getStep, "trigger", t_boolean); YBeanType putStep = f.ybean("PutStep"); - addProp(putStep, "put", t_resource_name); + addProp(putStep, "put", t_put_get_name); addProp(putStep, "resource", t_resource_name); addProp(putStep, "params", f.contextAware("PutParams", (dc) -> resourceTypes.getOutParamsType(getResourceType("put", models, dc)) @@ -441,7 +453,10 @@ public class PipelineYmlSchema implements YamlSchema { } private String getResourceType(String resourceNameProp, ConcourseModel models, DynamicSchemaContext dc) { - String resourceName = getParentPropertyValue(resourceNameProp, models, dc); + String resourceName = getParentPropertyValue("resource", models, dc); + if (resourceName==null) { + resourceName = getParentPropertyValue(resourceNameProp, models, dc); + } if (resourceName!=null) { return models.getResourceType(dc.getDocument(), resourceName); } @@ -453,11 +468,15 @@ public class PipelineYmlSchema implements YamlSchema { } private String getParentPropertyValue(String propName, ConcourseModel models, DynamicSchemaContext dc) { + return NodeUtil.asScalar(getParentPropertyNode(propName, models, dc)); + } + + private Node getParentPropertyNode(String propName, ConcourseModel models, DynamicSchemaContext dc) { YamlPath path = dc.getPath(); if (path!=null) { YamlFileAST root = models.getSafeAst(dc.getDocument()); if (root!=null) { - return NodeUtil.asScalar(path.dropLast().append(YamlPathSegment.valueAt(propName)).traverseToNode(root)); + return path.dropLast().append(YamlPathSegment.valueAt(propName)).traverseToNode(root); } } return null; diff --git a/vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/concourse/ConcourseEditorTest.java b/vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/concourse/ConcourseEditorTest.java index 643fe9a70..153dbf74c 100644 --- a/vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/concourse/ConcourseEditorTest.java +++ b/vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/concourse/ConcourseEditorTest.java @@ -1603,6 +1603,159 @@ public class ConcourseEditorTest { editor.assertHoverContains("remove", "remove the given lock from the pool"); } + @Test public void reconcileExplicitResourceAttributeInPutStep() throws Exception { + //See: https://www.pivotaltracker.com/story/show/138568839 + Editor editor; + + editor = harness.newEditor( + "resources:\n" + + "- name: aws-environments\n" + + " type: pool\n" + + "jobs:\n" + + "- name: test-multi-aws\n" + + " plan:\n" + + " - put: environment-1\n" + + " resource: aws-environments\n" + + " params:\n" + + " acquire: true\n" + + " bogus_param: bad\n" + + " get_params:\n" + + " bogus_get_param: bad" + ); + editor.assertProblems( + "bogus_param|Unknown property", + "bogus_get_param|Unknown property" + ); + + editor = harness.newEditor( + "resources:\n" + + "- name: aws-environments\n" + + " type: pool\n" + + "jobs:\n" + + "- name: test-multi-aws\n" + + " plan:\n" + + " - get: environment-1\n" + + " resource: aws-environments\n" + + " params:\n" + + " bogus_param: bad\n" + ); + editor.assertProblems( + "bogus_param|Unknown property" + ); + + editor = harness.newEditor( + "resources:\n" + + "- name: aws-environments\n" + + " type: pool\n" + + " source:\n" + + " uri: git@github.com:concourse/locks.git\n" + + " branch: master\n" + + " pool: aws\n" + + " private_key: |\n" + + " -----BEGIN RSA PRIVATE KEY-----\n" + + " MIIEowIBAAKCAQEAtCS10/f7W7lkQaSgD/mVeaSOvSF9ql4hf/zfMwfVGgHWjj+W\n" + + " ...\n" + + " DWiJL+OFeg9kawcUL6hQ8JeXPhlImG6RTUffma9+iGQyyBMCGd1l\n" + + " -----END RSA PRIVATE KEY-----\n" + + " username: jonhsmith\n" + + " password: his-password\n" + + " retry_delay: 10s\n" + + "jobs:\n" + + "- name: test-multi-aws\n" + + " plan:\n" + + " - get: aws-environments\n" + + " params: {} \n" + + " - put: environment-1\n" + + " resource: aws-environments\n" + + " params: {acquire: true}\n" + + " - put: environment-2\n" + + " resource: aws-environments\n" + + " params: \n" + + " acquire: true\n" + + " bogus_param: blah\n" + + " - task: test-multi-aws\n" + + " file: my-scripts/test-multi-aws.yml\n" + + " - put: aws-environments\n" + + " params: {release: environment-1}\n" + + " - put: aws-environments\n" + + " params: {release: environment-2}" + ); + editor.assertProblems( + "bogus_param|Unknown property" + ); + } + + @Test + public void resourceNameContentAssist() throws Exception { + String conText; + + conText = + "resources:\n" + + "- name: foo-resource\n" + + "- name: bar-resource\n" + + "- name: other-resource\n" + + "jobs:\n" + + "- name: test-multi-aws\n" + + " plan:\n" + + " - <*>"; + assertContextualCompletions(conText + , // ============== + "get: <*>" + , + "get: bar-resource<*>", + "get: foo-resource<*>", + "get: other-resource<*>" + ); + assertContextualCompletions(conText + , // ============== + "put: <*>" + , // ==> + "put: bar-resource<*>", + "put: foo-resource<*>", + "put: other-resource<*>" + ); + + + conText = + "resources:\n" + + "- name: foo-resource\n" + + "- name: bar-resource\n" + + "- name: other-resource\n" + + "jobs:\n" + + "- name: test-multi-aws\n" + + " plan:\n" + + " - put: something\n" + + " <*>"; + assertContextualCompletions(conText + , // ============== + "resource: <*>" + , // ==> + "resource: bar-resource<*>", + "resource: foo-resource<*>", + "resource: other-resource<*>" + ); + + conText = + "resources:\n" + + "- name: foo-resource\n" + + "- name: bar-resource\n" + + "- name: other-resource\n" + + "jobs:\n" + + "- name: test-multi-aws\n" + + " plan:\n" + + " - <*>\n" + + " resource: foo-resource\n"; // presence of explicit 'resource' attribute should disable treating the name in put/get as a resource-name + + assertContextualCompletions(conText, + "put: <*>" + // ==> NONE + ); + assertContextualCompletions(conText, + "get: <*>" + // ==> NONE + ); + } + @Test public void gotoResourceDefinition() throws Exception { Editor editor = harness.newEditor(