From 7442db205ded2d35fce5e3f80a18b17a4df11068 Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Tue, 20 Dec 2016 16:44:25 -0800 Subject: [PATCH] Concourse: reconcile checking for non-existent resources --- .../vscode/commons/util/EnumValueParser.java | 16 ++- .../vscode/commons/util/ExceptionUtil.java | 11 ++ .../ide/vscode/commons/yaml/ast/NodeUtil.java | 16 +++ .../vscode/commons/yaml/ast/YamlFileAST.java | 11 +- .../vscode/commons/yaml/ast/YamlParser.java | 2 +- .../vscode/commons/yaml/path/NodeCursor.java | 94 +++++++++++++ .../vscode/commons/yaml/path/YamlPath.java | 10 ++ .../commons/yaml/path/YamlPathSegment.java | 37 ++++- .../SchemaBasedYamlASTReconciler.java | 24 ++-- .../yaml/schema/ASTDynamicSchemaContext.java | 21 ++- .../yaml/schema/DynamicSchemaContext.java | 15 +- .../schema/SNodeDynamicSchemaContext.java | 6 + .../yaml/schema/SchemaContextAware.java | 22 +++ .../commons/yaml/schema/YTypeFactory.java | 40 +++++- .../vscode/commons/yaml/schema/YTypeUtil.java | 4 +- .../yaml/structure/YamlStructureParser.java | 5 + .../concourse/ConcourseLanguageServer.java | 24 ++-- .../ide/vscode/concourse/ConcourseModel.java | 109 +++++++++++++++ .../vscode/concourse/PipelineYmlSchema.java | 16 ++- .../concourse/util/StaleFallbackCache.java | 91 ++++++++++++ .../ConcourseLanguageServerTest.java | 2 +- .../PipelineYamlEditorTest.java | 34 ++++- .../manifest/yaml/PipelineYmlSchemaTest.java | 129 ------------------ .../workspace/pipeline-with-bad-resources.yml | 11 ++ 24 files changed, 574 insertions(+), 176 deletions(-) create mode 100644 vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/path/NodeCursor.java create mode 100644 vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/SchemaContextAware.java create mode 100644 vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/ConcourseModel.java create mode 100644 vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/util/StaleFallbackCache.java rename vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/{manifest/yaml => concourse}/ConcourseLanguageServerTest.java (96%) rename vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/{manifest/yaml => concourse}/PipelineYamlEditorTest.java (93%) delete mode 100644 vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/manifest/yaml/PipelineYmlSchemaTest.java create mode 100644 vscode-extensions/vscode-concourse/src/test/resources/workspace/pipeline-with-bad-resources.yml diff --git a/vscode-extensions/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/EnumValueParser.java b/vscode-extensions/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/EnumValueParser.java index 97827e0c0..4af3f7924 100644 --- a/vscode-extensions/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/EnumValueParser.java +++ b/vscode-extensions/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/EnumValueParser.java @@ -11,7 +11,6 @@ package org.springframework.ide.vscode.commons.util; import java.util.Collection; -import java.util.Set; import com.google.common.collect.ImmutableSet; @@ -23,7 +22,8 @@ import com.google.common.collect.ImmutableSet; public class EnumValueParser implements ValueParser { private String typeName; - private Set values; + private Collection values; + public EnumValueParser(String typeName, String... values) { this(typeName, ImmutableSet.copyOf(values)); @@ -31,15 +31,21 @@ public class EnumValueParser implements ValueParser { public EnumValueParser(String typeName, Collection values) { this.typeName = typeName; - this.values = ImmutableSet.copyOf(values); + this.values = values; } public Object parse(String str) { - if (values.contains(str)) { + Collection values = this.values; + //If values is not known (null) then just assume the str is acceptable. + if (values==null || values.contains(str)) { return str; } else { - throw new IllegalArgumentException("'"+str+"' is not valid for Enum '"+typeName+"'. Valid values are: "+values); + throw new IllegalArgumentException(createErrorMessage(str, values)); } } + protected String createErrorMessage(String parseString, Collection values2) { + return "'"+parseString+"' is not valid for Enum '"+typeName+"'. Valid values are: "+values; + } + } diff --git a/vscode-extensions/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/ExceptionUtil.java b/vscode-extensions/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/ExceptionUtil.java index 74ac8c66c..7135a9c0d 100644 --- a/vscode-extensions/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/ExceptionUtil.java +++ b/vscode-extensions/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/ExceptionUtil.java @@ -3,6 +3,7 @@ package org.springframework.ide.vscode.commons.util; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; /** * Utility methods to convert exceptions into other types of exceptions, status @@ -65,4 +66,14 @@ public class ExceptionUtil { return dump.toString(); } + /** + * Convert throwables into exception try not to wrap if not needing to. + */ + public static Exception exception(Throwable cause) { + if (cause instanceof Exception) { + return (Exception)cause; + } + return new ExecutionException(cause); + } + } diff --git a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/ast/NodeUtil.java b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/ast/NodeUtil.java index 02b06b84a..7bc823603 100644 --- a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/ast/NodeUtil.java +++ b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/ast/NodeUtil.java @@ -1,8 +1,10 @@ package org.springframework.ide.vscode.commons.yaml.ast; +import org.yaml.snakeyaml.nodes.MappingNode; import org.yaml.snakeyaml.nodes.Node; import org.yaml.snakeyaml.nodes.NodeId; import org.yaml.snakeyaml.nodes.ScalarNode; +import org.yaml.snakeyaml.nodes.SequenceNode; /** * @author Kris De Volder @@ -47,4 +49,18 @@ public class NodeUtil { return null; } + public static MappingNode asMapping(Node node) { + if (node!=null && node.getNodeId()==NodeId.mapping) { + return (MappingNode) node; + } + return null; + } + + public static SequenceNode asSequence(Node node) { + if (node!=null && node.getNodeId()==NodeId.sequence) { + return (SequenceNode) node; + } + return null; + } + } diff --git a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/ast/YamlFileAST.java b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/ast/YamlFileAST.java index 89ee1283a..876497ce1 100644 --- a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/ast/YamlFileAST.java +++ b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/ast/YamlFileAST.java @@ -9,6 +9,7 @@ import java.util.List; import org.springframework.ide.vscode.commons.util.Collector; import org.springframework.ide.vscode.commons.util.IRequestor; import org.springframework.ide.vscode.commons.util.RememberLast; +import org.springframework.ide.vscode.commons.util.text.IDocument; import org.springframework.ide.vscode.commons.yaml.ast.NodeRef.RootRef; import org.springframework.ide.vscode.commons.yaml.ast.NodeRef.SeqRef; import org.springframework.ide.vscode.commons.yaml.ast.NodeRef.TupleKeyRef; @@ -25,9 +26,11 @@ import org.yaml.snakeyaml.nodes.SequenceNode; public class YamlFileAST { private static final List> NO_CHILDREN = Collections.emptyList(); - private List nodes; + private final List nodes; + private final IDocument doc; - public YamlFileAST(Iterable iter) { + public YamlFileAST(IDocument doc, Iterable iter) { + this.doc = doc; nodes = new ArrayList(); for (Node node : iter) { nodes.add(node); @@ -141,5 +144,9 @@ public class YamlFileAST { nodes.set(index, value); } + public IDocument getDocument() { + return doc; + } + } diff --git a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/ast/YamlParser.java b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/ast/YamlParser.java index da03f9fcc..5184775e8 100644 --- a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/ast/YamlParser.java +++ b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/ast/YamlParser.java @@ -17,7 +17,7 @@ public class YamlParser implements YamlASTProvider { public YamlFileAST getAST(IDocument doc) throws Exception { CharSequenceReader reader = new CharSequenceReader(); reader.setInput(doc.get()); - return new YamlFileAST(yaml.composeAll(reader)); + return new YamlFileAST(doc, yaml.composeAll(reader)); } } diff --git a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/path/NodeCursor.java b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/path/NodeCursor.java new file mode 100644 index 000000000..732d1eeec --- /dev/null +++ b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/path/NodeCursor.java @@ -0,0 +1,94 @@ +/******************************************************************************* + * Copyright (c) 2016 Pivotal, 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.yaml.path; + +import java.util.stream.Stream; + +import org.springframework.ide.vscode.commons.util.Assert; +import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil; +import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment.KeyAtKey; +import org.yaml.snakeyaml.nodes.CollectionNode; +import org.yaml.snakeyaml.nodes.MappingNode; +import org.yaml.snakeyaml.nodes.Node; +import org.yaml.snakeyaml.nodes.SequenceNode; + +/** + * Pointer to a specific {@link Node} in Snake yaml parse tree. Supports navigation + * using {@link YamlPath}s, including support for 'ambiguous' steps like + * {@link YamlPathSegment}.anyChild() + * + * @author Kris De Volder + */ +public class NodeCursor implements YamlNavigable { + + private final Node currentNode; + + public NodeCursor(Node node) { + Assert.isNotNull(node); + this.currentNode = node; + } + + @Override + public Stream traverseAmbiguously(YamlPathSegment s) { + switch (s.getType()) { + case KEY_AT_KEY: { + String key = s.toPropString(); + MappingNode mappingNode = NodeUtil.asMapping(getNode()); + if (mappingNode!=null) { + return mappingNode.getValue().stream() + .filter((c) -> key.equals(NodeUtil.asScalar(c.getKeyNode()))) + .map((c) -> new NodeCursor(c.getKeyNode())); + + } + return Stream.empty(); + } + case ANY_CHILD: { + MappingNode mappingNode = NodeUtil.asMapping(getNode()); + if (mappingNode!=null) { + return mappingNode.getValue().stream() + .map((c) -> new NodeCursor(c.getValueNode())); + } + SequenceNode sequenceNode = NodeUtil.asSequence(getNode()); + if (sequenceNode!=null) { + return sequenceNode.getValue().stream().map(NodeCursor::new); + } + return Stream.empty(); + } + case VAL_AT_INDEX: { + SequenceNode seq = NodeUtil.asSequence(getNode()); + int index = s.toIndex(); + int size = seq.getValue().size(); + if (index= 0) { + return Stream.of(new NodeCursor(seq.getValue().get(index))); + } + return Stream.empty(); + } + case VAL_AT_KEY: { + MappingNode mappingNode = NodeUtil.asMapping(getNode()); + if (mappingNode!=null) { + String key = s.toPropString(); + return mappingNode.getValue().stream() + .filter((c) -> key.equals(NodeUtil.asScalar(c.getKeyNode()))) + .map((c) -> new NodeCursor(c.getValueNode())); + } + return Stream.empty(); + } + default: + Assert.isLegal(false, "Bug? Missing switch case?"); + return Stream.empty(); + } + } + + public Node getNode() { + return currentNode; + } + +} diff --git a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/path/YamlPath.java b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/path/YamlPath.java index b4a7acd50..9c601d744 100644 --- a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/path/YamlPath.java +++ b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/path/YamlPath.java @@ -21,6 +21,7 @@ import org.springframework.ide.vscode.commons.yaml.ast.NodeRef.RootRef; import org.springframework.ide.vscode.commons.yaml.ast.NodeRef.SeqRef; import org.springframework.ide.vscode.commons.yaml.ast.NodeRef.TupleValueRef; import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment.YamlPathSegmentType; +import org.yaml.snakeyaml.nodes.Node; /** * @author Kris De Volder @@ -125,6 +126,15 @@ public class YamlPath { return traverseAmbiguously(startNode).findFirst().orElse(null); } + public Stream traverseAmbiguously(Node startNode) { + if (startNode!=null) { + return traverseAmbiguously(new NodeCursor(startNode)) + .map(NodeCursor::getNode); + + } + return Stream.empty(); + } + public > Stream traverseAmbiguously(T startNode) { if (startNode!=null) { Stream result = Stream.of(startNode); diff --git a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/path/YamlPathSegment.java b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/path/YamlPathSegment.java index 18b66755e..73906f99d 100644 --- a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/path/YamlPathSegment.java +++ b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/path/YamlPathSegment.java @@ -22,7 +22,35 @@ public abstract class YamlPathSegment { public static enum YamlPathSegmentType { VAL_AT_KEY, //Go to value associate with given key in a map. KEY_AT_KEY, //Go to the key node associated with a given key in a map. - VAL_AT_INDEX //Go to value associate with given index in a sequence + VAL_AT_INDEX, //Go to value associate with given index in a sequence + ANY_CHILD // Go to any child (assumes you are using ambiguous traversal method, otherwise this is probably not very useful) + } + + public static class AnyChild extends YamlPathSegment { + + private static AnyChild INSTANCE = new AnyChild(); + + private AnyChild() {} + + @Override + public String toNavString() { + return ".*"; + } + + @Override + public String toPropString() { + return "*"; + } + + @Override + public Integer toIndex() { + return null; + } + + @Override + public YamlPathSegmentType getType() { + return YamlPathSegmentType.ANY_CHILD; + }; } public static class AtIndex extends YamlPathSegment { @@ -133,7 +161,7 @@ public abstract class YamlPathSegment { } } - private static class KeyAtKey extends ValAtKey { + public static class KeyAtKey extends ValAtKey { public KeyAtKey(String key) { super(key); @@ -144,6 +172,7 @@ public abstract class YamlPathSegment { return YamlPathSegmentType.KEY_AT_KEY; } + } public String toString() { @@ -165,5 +194,9 @@ public abstract class YamlPathSegment { public static YamlPathSegment keyAt(String key) { return new KeyAtKey(key); } + + public static YamlPathSegment anyChild() { + return AnyChild.INSTANCE; + } } diff --git a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/SchemaBasedYamlASTReconciler.java b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/SchemaBasedYamlASTReconciler.java index 6469226ec..3a3f17d4a 100644 --- a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/SchemaBasedYamlASTReconciler.java +++ b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/SchemaBasedYamlASTReconciler.java @@ -8,6 +8,7 @@ import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemC import org.springframework.ide.vscode.commons.util.ExceptionUtil; import org.springframework.ide.vscode.commons.util.StringUtil; import org.springframework.ide.vscode.commons.util.ValueParser; +import org.springframework.ide.vscode.commons.util.text.IDocument; import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil; import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST; import org.springframework.ide.vscode.commons.yaml.schema.ASTDynamicSchemaContext; @@ -39,23 +40,23 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler { List nodes = ast.getNodes(); if (nodes!=null && !nodes.isEmpty()) { for (Node node : nodes) { - reconcile(node, schema.getTopLevelType()); + reconcile(ast.getDocument(), node, schema.getTopLevelType()); } } } - private void reconcile(Node node, YType type) { + private void reconcile(IDocument doc, Node node, YType type) { if (type!=null) { + DynamicSchemaContext schemaContext = new ASTDynamicSchemaContext(doc, node); switch (node.getNodeId()) { case mapping: MappingNode map = (MappingNode) node; if (typeUtil.isMap(type)) { for (NodeTuple entry : map.getValue()) { - reconcile(entry.getKeyNode(), typeUtil.getKeyType(type)); - reconcile(entry.getValueNode(), typeUtil.getDomainType(type)); + reconcile(doc, entry.getKeyNode(), typeUtil.getKeyType(type)); + reconcile(doc, entry.getValueNode(), typeUtil.getDomainType(type)); } } else if (typeUtil.isBean(type)) { - DynamicSchemaContext schemaContext = new ASTDynamicSchemaContext(map); Map beanProperties = typeUtil.getPropertiesMap(type, schemaContext); for (NodeTuple entry : map.getValue()) { Node keyNode = entry.getKeyNode(); @@ -68,7 +69,7 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler { type = typeUtil.inferMoreSpecificType(type, schemaContext); unknownBeanProperty(keyNode, type, key); } else { - reconcile(entry.getValueNode(), prop.getType()); + reconcile(doc, entry.getValueNode(), prop.getType()); } } } @@ -80,7 +81,7 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler { SequenceNode seq = (SequenceNode) node; if (typeUtil.isSequencable(type)) { for (Node el : seq.getValue()) { - reconcile(el, typeUtil.getDomainType(type)); + reconcile(doc, el, typeUtil.getDomainType(type)); } } else { expectTypeButFoundSequence(type, node); @@ -88,7 +89,7 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler { break; case scalar: if (typeUtil.isAtomic(type)) { - ValueParser parser = typeUtil.getValueParser(type); + ValueParser parser = typeUtil.getValueParser(type, schemaContext); if (parser!=null) { try { parser.parse(NodeUtil.asScalar(node)); @@ -108,11 +109,10 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler { } private void valueParseError(YType type, Node node, String parseErrorMsg) { - String msg= "Couldn't parse as '"+describe(type)+"'"; - if (StringUtil.hasText(parseErrorMsg)) { - msg += " ("+parseErrorMsg+")"; + if (!StringUtil.hasText(parseErrorMsg)) { + parseErrorMsg= "Couldn't parse as '"+describe(type)+"'"; } - problem(node, msg); + problem(node, parseErrorMsg); } private void unknownBeanProperty(Node keyNode, YType type, String name) { diff --git a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/ASTDynamicSchemaContext.java b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/ASTDynamicSchemaContext.java index 1fb50c7d6..d6b7aed09 100644 --- a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/ASTDynamicSchemaContext.java +++ b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/ASTDynamicSchemaContext.java @@ -13,8 +13,10 @@ package org.springframework.ide.vscode.commons.yaml.schema; import java.util.Collections; import java.util.Set; +import org.springframework.ide.vscode.commons.util.text.IDocument; import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil; import org.yaml.snakeyaml.nodes.MappingNode; +import org.yaml.snakeyaml.nodes.Node; import org.yaml.snakeyaml.nodes.NodeTuple; import com.google.common.collect.ImmutableSet; @@ -28,9 +30,19 @@ import com.google.common.collect.ImmutableSet; public class ASTDynamicSchemaContext extends CachingSchemaContext { private MappingNode mapNode; + private IDocument doc; - public ASTDynamicSchemaContext(MappingNode map) { - this.mapNode = map; + public ASTDynamicSchemaContext(IDocument doc, Node node) { + this.doc = doc; + this.mapNode = as(MappingNode.class, node); + } + + @SuppressWarnings("unchecked") + private T as(Class klass, Node node) { + if (node!=null && klass.isInstance(node)) { + return (T) node; + } + return null; } @Override @@ -47,4 +59,9 @@ public class ASTDynamicSchemaContext extends CachingSchemaContext { } return Collections.emptySet(); } + + @Override + public IDocument getDocument() { + return doc; + } } diff --git a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/DynamicSchemaContext.java b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/DynamicSchemaContext.java index d84a97094..fcf5450a9 100644 --- a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/DynamicSchemaContext.java +++ b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/DynamicSchemaContext.java @@ -12,6 +12,8 @@ package org.springframework.ide.vscode.commons.yaml.schema; import java.util.Set; +import org.springframework.ide.vscode.commons.util.text.IDocument; + import com.google.common.collect.ImmutableSet; /** @@ -31,6 +33,11 @@ public interface DynamicSchemaContext { public Set getDefinedProperties() { return ImmutableSet.of(); } + + @Override + public IDocument getDocument() { + return null; + } }; /** @@ -45,6 +52,12 @@ public interface DynamicSchemaContext { * properties are defined in the surrounding object. */ Set getDefinedProperties(); - + + /** + * Returns the IDocument the current context is in. This allows for some 'schemas' to have + * arbitrarily complex analysis of anyhting in the IDocument or even documents related + * to it based on its uri. + */ + IDocument getDocument(); } diff --git a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/SNodeDynamicSchemaContext.java b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/SNodeDynamicSchemaContext.java index 299dcb045..7f25f284a 100644 --- a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/SNodeDynamicSchemaContext.java +++ b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/SNodeDynamicSchemaContext.java @@ -17,6 +17,7 @@ import java.util.Set; import org.springframework.ide.vscode.commons.util.CollectionUtil; import org.springframework.ide.vscode.commons.util.Log; +import org.springframework.ide.vscode.commons.util.text.IDocument; import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureParser.SChildBearingNode; import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureParser.SKeyNode; import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureParser.SNode; @@ -55,5 +56,10 @@ public class SNodeDynamicSchemaContext extends CachingSchemaContext { return Collections.emptySet(); } + @Override + public IDocument getDocument() { + return contextNode.getDocument(); + } + } diff --git a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/SchemaContextAware.java b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/SchemaContextAware.java new file mode 100644 index 000000000..2a611f1b6 --- /dev/null +++ b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/SchemaContextAware.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2016 Pivotal, 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.yaml.schema; + +/** + * Interface that can be implemented by something producing another + * component (of some type `T`) where the returned component needs to + * configured with a {@link DynamicSchemaContext}. + * + * @author Kris De Volder + */ +public interface SchemaContextAware { + T withContext(DynamicSchemaContext dc); +} 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 c9e7cde6d..281d3d446 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 @@ -20,6 +20,8 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.function.BiFunction; +import java.util.stream.Collectors; import javax.inject.Provider; @@ -120,8 +122,8 @@ public class YTypeFactory { } @Override - public ValueParser getValueParser(YType type) { - return ((AbstractType)type).getParser(); + public ValueParser getValueParser(YType type, DynamicSchemaContext dc) { + return ((AbstractType)type).getParser(dc); } @Override @@ -137,7 +139,7 @@ public class YTypeFactory { */ public static abstract class AbstractType implements YType { - private ValueParser parser; + private SchemaContextAware parser; private List propertyList = new ArrayList<>(); private final List hints = new ArrayList<>(); private Map cachedPropertyMap; @@ -246,11 +248,15 @@ public class YTypeFactory { } } - public void parseWith(ValueParser parser) { + public void parseWith(SchemaContextAware parser) { this.parser = parser; } - public ValueParser getParser() { - return parser; + + public void parseWith(ValueParser parser) { + parseWith((DynamicSchemaContext dc) -> parser); + } + private ValueParser getParser(DynamicSchemaContext dc) { + return parser == null ? null : parser.withContext(dc); } } @@ -577,6 +583,28 @@ public class YTypeFactory { return new YTypedPropertyImpl(name, type); } + public YAtomicType yenum(String name, BiFunction, String> errorMessageFormatter, SchemaContextAware> values) { + YAtomicType t = yatomic(name); + t.addHintProvider(() -> { + Collection strings = values.withContext(DynamicSchemaContext.NULL); //TODO: make this really context aware! + return strings==null + ? null + : strings.stream() + .map((s) -> new BasicYValueHint(s)) + .collect(Collectors.toSet()); + }); + t.parseWith((DynamicSchemaContext dc) -> { + EnumValueParser enumParser = new EnumValueParser(name, values.withContext(dc)) { + @Override + protected String createErrorMessage(String parseString, Collection values) { + return errorMessageFormatter.apply(parseString, values); + } + }; + return enumParser; + }); + return t; + } + public YAtomicType yenum(String name, String... values) { YAtomicType t = yatomic(name); t.addHints(values); diff --git a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/YTypeUtil.java b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/YTypeUtil.java index f6acd0cee..6a6ca7aec 100644 --- a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/YTypeUtil.java +++ b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/YTypeUtil.java @@ -32,7 +32,7 @@ public interface YTypeUtil { YValueHint[] getHintValues(YType yType); String niceTypeName(YType type); YType getKeyType(YType type); - ValueParser getValueParser(YType type); + ValueParser getValueParser(YType type, DynamicSchemaContext dc); //TODO: only one of these two should be enough? List getProperties(YType type, DynamicSchemaContext dc); @@ -44,5 +44,5 @@ public interface YTypeUtil { * present in the context to narrow the type, then the type itself * should be returned. */ - YType inferMoreSpecificType(YType type, DynamicSchemaContext schemaContext); + YType inferMoreSpecificType(YType type, DynamicSchemaContext dc); } diff --git a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/structure/YamlStructureParser.java b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/structure/YamlStructureParser.java index 2c267ef58..15c271870 100644 --- a/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/structure/YamlStructureParser.java +++ b/vscode-extensions/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/structure/YamlStructureParser.java @@ -15,6 +15,7 @@ import org.springframework.ide.vscode.commons.util.Assert; import org.springframework.ide.vscode.commons.util.CollectionUtil; import org.springframework.ide.vscode.commons.util.Log; import org.springframework.ide.vscode.commons.util.StringUtil; +import org.springframework.ide.vscode.commons.util.text.IDocument; import org.springframework.ide.vscode.commons.util.text.IRegion; import org.springframework.ide.vscode.commons.yaml.path.KeyAliases; import org.springframework.ide.vscode.commons.yaml.path.YamlNavigable; @@ -257,6 +258,10 @@ public class YamlStructureParser { return getStart()<=offset && offset<=getTreeEnd(); } + public IDocument getDocument() { + return doc.getDocument(); + } + public String getText() throws Exception { return doc.textBetween(start, end); } diff --git a/vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/ConcourseLanguageServer.java b/vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/ConcourseLanguageServer.java index 84b3f2060..334ec3782 100644 --- a/vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/ConcourseLanguageServer.java +++ b/vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/ConcourseLanguageServer.java @@ -1,3 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2016 Pivotal, 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ package org.springframework.ide.vscode.concourse; import org.eclipse.lsp4j.CompletionOptions; @@ -25,22 +35,20 @@ import org.yaml.snakeyaml.Yaml; public class ConcourseLanguageServer extends SimpleLanguageServer { - private Yaml yaml = new Yaml(); - private YamlSchema schema = new PipelineYmlSchema(); - - public ConcourseLanguageServer() { SimpleTextDocumentService documents = getTextDocumentService(); - - YamlASTProvider parser = new YamlParser(yaml); + + ConcourseModel models = new ConcourseModel(documents); + YamlASTProvider currentAsts = models.getAstProvider(false); YamlStructureProvider structureProvider = YamlStructureProvider.DEFAULT; + YamlSchema schema = new PipelineYmlSchema(models); YamlAssistContextProvider contextProvider = new SchemaBasedYamlAssistContextProvider(schema); YamlCompletionEngine yamlCompletionEngine = new YamlCompletionEngine(structureProvider, contextProvider); VscodeCompletionEngine completionEngine = new VscodeCompletionEngineAdapter(this, yamlCompletionEngine); - HoverInfoProvider infoProvider = new YamlHoverInfoProvider(parser, structureProvider, contextProvider); + HoverInfoProvider infoProvider = new YamlHoverInfoProvider(currentAsts, structureProvider, contextProvider); VscodeHoverEngine hoverEngine = new VscodeHoverEngineAdapter(this, infoProvider); - IReconcileEngine engine = new YamlSchemaBasedReconcileEngine(parser, schema); + IReconcileEngine engine = new YamlSchemaBasedReconcileEngine(currentAsts, schema); // SimpleWorkspaceService workspace = getWorkspaceService(); documents.onDidChangeContent(params -> { diff --git a/vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/ConcourseModel.java b/vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/ConcourseModel.java new file mode 100644 index 000000000..261018ddb --- /dev/null +++ b/vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/ConcourseModel.java @@ -0,0 +1,109 @@ +/******************************************************************************* + * Copyright (c) 2016 Pivotal, 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.concourse; + +import static org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment.anyChild; +import static org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment.valueAt; + +import java.util.Set; +import java.util.stream.Collectors; + +import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService; +import org.springframework.ide.vscode.commons.languageserver.util.TextDocumentContentChange; +import org.springframework.ide.vscode.commons.util.Log; +import org.springframework.ide.vscode.commons.util.text.IDocument; +import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil; +import org.springframework.ide.vscode.commons.yaml.ast.YamlASTProvider; +import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST; +import org.springframework.ide.vscode.commons.yaml.ast.YamlParser; +import org.springframework.ide.vscode.commons.yaml.path.YamlPath; +import org.springframework.ide.vscode.concourse.util.StaleFallbackCache; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.error.YAMLException; +import org.yaml.snakeyaml.nodes.Node; + +/** + * ConcourseModels is responsible for extracting various bits of information + * out of .yml documents and caching them for use by various tools (reconcile engine + * and completion engine). + */ +public class ConcourseModel { + + private static final YamlPath RESOURCE_NAMES_PATH = new YamlPath( + valueAt("resources"), + anyChild(), + valueAt("name") + ); + + private final YamlParser parser; + private StaleFallbackCache asts = new StaleFallbackCache<>(); + + public ConcourseModel(SimpleTextDocumentService documents) { + Yaml yaml = new Yaml(); + this.parser = new YamlParser(yaml); + documents.onDidChangeContent(this::documentChanged); + } + + private void documentChanged(TextDocumentContentChange changeEvent) { + String uri = changeEvent.getDocument().getUri(); + if (uri!=null) { + asts.invalidate(uri); + } + } + + /** + * Returns the resource names that are defined by given IDocument. If the contents + * of IDocument is not currently parseable then this may return stale information + * retained from a previous successful parse. + *

+ * It may also return null if its not currently possible to obtain the list of resource + * names (e.g. because there hasn't been a successful parse yet and current document contents + * can not be parsed). + */ + public Set getResourceNames(IDocument doc) { + try { + if (doc!=null) { + String uri = doc.getUri(); + if (uri!=null) { + YamlFileAST ast = getAst(doc); + Node root = ast.get(0); + return RESOURCE_NAMES_PATH + .traverseAmbiguously(root) + .map(NodeUtil::asScalar) + .filter((string) -> string!=null) + .collect(Collectors.toSet()); + } + } + } catch (YAMLException e) { + // ignore: garbage in the doc. Can't compute stuff and that's to be expected. + } catch (Exception e) { + Log.log(e); + } + return null; + } + + private YamlFileAST getAst(IDocument doc) throws Exception { + return getAstProvider(true).getAST(doc); + } + + public YamlASTProvider getAstProvider(boolean allowStaleAsts) { + return (IDocument doc) -> { + String uri = doc.getUri(); + if (uri!=null) { + return asts.get(uri, allowStaleAsts, () -> { + return parser.getAST(doc); + }); + } + return null; + }; + } + +} 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 5c241be3c..fbbfedd88 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 @@ -12,6 +12,7 @@ package org.springframework.ide.vscode.concourse; import org.springframework.ide.vscode.commons.util.Renderable; import org.springframework.ide.vscode.commons.util.Renderables; +import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext; import org.springframework.ide.vscode.commons.yaml.schema.YType; import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory; import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.AbstractType; @@ -31,7 +32,7 @@ public class PipelineYmlSchema implements YamlSchema { private final YTypeFactory f = new YTypeFactory(); - public PipelineYmlSchema() { + public PipelineYmlSchema(ConcourseModel models) { TYPE_UTIL = f.TYPE_UTIL; // define schema types @@ -81,9 +82,18 @@ public class PipelineYmlSchema implements YamlSchema { // // The vagrant-cloud r ); + + YType resourceName = f.yenum("ResourceName", + (parseString, validValues) -> { + return "The '"+parseString+"' resource does not exist. Existing resources: "+validValues; + }, + (DynamicSchemaContext dc) -> { + return models.getResourceNames(dc.getDocument()); + } + ); YBeanType getStep = f.ybean("GetStep"); - prop(getStep, "get", t_ne_string); + prop(getStep, "get", resourceName); prop(getStep, "resource", t_string); prop(getStep, "version", t_version); prop(getStep, "passed", t_strings); @@ -91,7 +101,7 @@ public class PipelineYmlSchema implements YamlSchema { prop(getStep, "trigger", t_boolean); YBeanType putStep = f.ybean("PutStep"); - prop(putStep, "put", t_ne_string); + prop(putStep, "put", resourceName); prop(putStep, "resource", t_string); prop(putStep, "params", t_params); prop(putStep, "get_params", t_params); diff --git a/vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/util/StaleFallbackCache.java b/vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/util/StaleFallbackCache.java new file mode 100644 index 000000000..a589ba15f --- /dev/null +++ b/vscode-extensions/vscode-concourse/src/main/java/org/springframework/ide/vscode/concourse/util/StaleFallbackCache.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2016 Pivotal, 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.concourse.util; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import org.springframework.ide.vscode.commons.util.Assert; +import org.springframework.ide.vscode.commons.util.ExceptionUtil; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; + +/** + * Simple cache implementation that falls back on 'stale' cache entry if + * a new entry can not be computed. The api is loosely modeled after + * guava's Cache interface (but only the subset we use is implemented to reduce the + * complexity of its implementation). + */ +public class StaleFallbackCache{ + + Map staleEntries = new HashMap<>(); + Cache> validEntries = CacheBuilder.newBuilder().build(); + + + public synchronized V get(K key, boolean allowStaleEntries, Callable valueLoader) throws Exception { + CompletableFuture valid = validEntries.get(key, () -> load(valueLoader)); + if (!allowStaleEntries) { + return future_get(valid); + } else { + if (valid.isCompletedExceptionally()) { + V staleValue = staleEntries.get(key); + if (staleValue!=null) { + return staleValue; + } + } + return future_get(valid); + } + } + + public synchronized void invalidate(K key) { + CompletableFuture staleEntry = validEntries.getIfPresent(key); + if (staleEntry!=null) { + validEntries.invalidate(key); + try { + staleEntries.put(key, future_get(staleEntry)); + } catch (Exception e) { + //ignore. Don't overwrite stale entry if current entry represents an error. + // We only keep 'good quality' stale entries not failed attempts to compute a value. + // as it is kind of the point to fall back on a 'good' old entry when the current + // entry is unavailable because of a problem (e.g. problems parsing the AST). + } + } + } + + + + private V future_get(CompletableFuture f) throws Exception { + try { + return f.get(); + } catch (InterruptedException e) { + throw e; + } catch (ExecutionException e) { + throw ExceptionUtil.exception(e.getCause()); + } + } + + private CompletableFuture load(Callable valueLoader) { + CompletableFuture future = new CompletableFuture(); + try { + V value = valueLoader.call(); + Assert.isNotNull(value); + future.complete(value); + } catch (Throwable e) { + future.completeExceptionally(e); + } + return future; + } + +} diff --git a/vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/manifest/yaml/ConcourseLanguageServerTest.java b/vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/concourse/ConcourseLanguageServerTest.java similarity index 96% rename from vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/manifest/yaml/ConcourseLanguageServerTest.java rename to vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/concourse/ConcourseLanguageServerTest.java index 6333dd670..70755872b 100644 --- a/vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/manifest/yaml/ConcourseLanguageServerTest.java +++ b/vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/concourse/ConcourseLanguageServerTest.java @@ -1,4 +1,4 @@ -package org.springframework.ide.vscode.manifest.yaml; +package org.springframework.ide.vscode.concourse; import static org.assertj.core.api.Assertions.assertThat; diff --git a/vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/manifest/yaml/PipelineYamlEditorTest.java b/vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/concourse/PipelineYamlEditorTest.java similarity index 93% rename from vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/manifest/yaml/PipelineYamlEditorTest.java rename to vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/concourse/PipelineYamlEditorTest.java index 7ae0fef0c..f601ca914 100644 --- a/vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/manifest/yaml/PipelineYamlEditorTest.java +++ b/vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/concourse/PipelineYamlEditorTest.java @@ -8,7 +8,7 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.manifest.yaml; +package org.springframework.ide.vscode.concourse; import static org.springframework.ide.vscode.languageserver.testharness.TestAsserts.assertContains; @@ -259,12 +259,15 @@ public class PipelineYamlEditorTest { ); editor.assertProblems( "boohoo|boolean", - "-1|Positive Integer", + "-1|must be positive", + "git|resource does not exist", "yohoho|boolean" ); //check that correct values are indeed accepted editor = harness.newEditor( + "resources:\n" + + "- name: git\n" + "jobs:\n" + "- name: foo\n" + " serial: true\n" + @@ -379,6 +382,33 @@ public class PipelineYamlEditorTest { editor.assertHoverContains("jobs", "At a high level, a job describes some actions to perform"); } + @Test + public void reconcileResourceReferences() throws Exception { + Editor editor = harness.newEditor( + "resources:\n" + + "- name: sts4\n" + + " type: git\n" + + " source:\n" + + " repository: https://github.com/kdvolder/somestuff\n" + + "jobs:\n" + + "- name: job1\n" + + " plan:\n" + + " - get: sts4\n" + + " - get: bogus-get\n" + + " - put: bogus-put\n" + ); + editor.assertProblems( + "bogus-get|resource does not exist", + "bogus-put|resource does not exist" + ); + + editor.assertProblems( + "bogus-get|[sts4]", + "bogus-put|[sts4]" + ); + + } + ////////////////////////////////////////////////////////////////////////////// private void assertContextualCompletions(String conText, String textBefore, String... textAfter) throws Exception { diff --git a/vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/manifest/yaml/PipelineYmlSchemaTest.java b/vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/manifest/yaml/PipelineYmlSchemaTest.java deleted file mode 100644 index afe9c34f1..000000000 --- a/vscode-extensions/vscode-concourse/src/test/java/org/springframework/ide/vscode/manifest/yaml/PipelineYmlSchemaTest.java +++ /dev/null @@ -1,129 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015, 2016 Pivotal, 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 - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Pivotal, Inc. - initial API and implementation - *******************************************************************************/ -package org.springframework.ide.vscode.manifest.yaml; - -import static org.junit.Assert.fail; - -import org.junit.Test; -import org.springframework.ide.vscode.concourse.PipelineYmlSchema; - -/** - * @author Kris De Volder - */ -public class PipelineYmlSchemaTest { - -// @Test -// public void shouldMakeSomeTests() { -// fail("We should make some tests for this"); -// } -// -// private static final String[] NESTED_PROP_NAMES = { -//// "applications", -// "buildpack", -// "command", -// "disk_quota", -// "domain", -// "domains", -// "env", -// "health-check-type", -// "host", -// "hosts", -//// "inherit", -// "instances", -// "memory", -// "name", -// "no-hostname", -// "no-route", -// "path", -// "random-route", -// "services", -// "stack", -// "timeout" -// }; -// - private static final String[] TOPLEVEL_PROP_NAMES = { - "resources", - "jobs", - "resource-types" - //groups - }; - - PipelineYmlSchema schema = new PipelineYmlSchema(); -// -// @Test -// public void toplevelProperties() throws Exception { -// assertPropNames(schema.getTopLevelType().getProperties(DynamicSchemaContext.NULL), TOPLEVEL_PROP_NAMES); -// assertPropNames(schema.getTopLevelType().getPropertiesMap(DynamicSchemaContext.NULL), TOPLEVEL_PROP_NAMES); -// } -// -// @Test -// public void nestedProperties() throws Exception { -// assertPropNames(getNestedProps(), NESTED_PROP_NAMES); -// } -// -// @Test -// public void toplevelPropertiesHaveDescriptions() { -// for (YTypedProperty p : schema.getTopLevelType().getProperties(DynamicSchemaContext.NULL)) { -// if (!p.getName().equals("applications")) { -// assertHasRealDescription(p); -// } -// } -// } -// -// @Test -// public void nestedPropertiesHaveDescriptions() { -// for (YTypedProperty p : getNestedProps()) { -// assertHasRealDescription(p); -// } -// } -// -// ////////////////////////////////////////////////////////////////////////////// -// -// private void assertHasRealDescription(YTypedProperty p) { -// { -// String noDescriptionText = Renderables.NO_DESCRIPTION.toHtml(); -// String actual = p.getDescription().toHtml(); -// String msg = "Description missing for '"+p.getName()+"'"; -// assertTrue(msg, StringUtil.hasText(actual)); -// assertFalse(msg, noDescriptionText.equals(actual)); -// } -// { -// String noDescriptionText = Renderables.NO_DESCRIPTION.toMarkdown(); -// String actual = p.getDescription().toMarkdown(); -// String msg = "Description missing for '"+p.getName()+"'"; -// assertTrue(msg, StringUtil.hasText(actual)); -// assertFalse(msg, noDescriptionText.equals(actual)); -// } -// } -// -// private List getNestedProps() { -// YSeqType applications = (YSeqType) schema.getTopLevelType().getPropertiesMap().get("applications").getType(); -// YBeanType application = (YBeanType) applications.getDomainType(); -// return application.getProperties(); -// } -// -// private void assertPropNames(List properties, String... expectedNames) { -// assertEquals(ImmutableSet.copyOf(expectedNames), getNames(properties)); -// } -// -// private void assertPropNames(Map propertiesMap, String[] toplevelPropNames) { -// assertEquals(ImmutableSet.copyOf(toplevelPropNames), ImmutableSet.copyOf(propertiesMap.keySet())); -// } -// -// private ImmutableSet getNames(Iterable properties) { -// Builder builder = ImmutableSet.builder(); -// for (YTypedProperty p : properties) { -// builder.add(p.getName()); -// } -// return builder.build(); -// } - -} diff --git a/vscode-extensions/vscode-concourse/src/test/resources/workspace/pipeline-with-bad-resources.yml b/vscode-extensions/vscode-concourse/src/test/resources/workspace/pipeline-with-bad-resources.yml new file mode 100644 index 000000000..2f99314d4 --- /dev/null +++ b/vscode-extensions/vscode-concourse/src/test/resources/workspace/pipeline-with-bad-resources.yml @@ -0,0 +1,11 @@ +resources: +- name: sts4 + type: git + source: + repository: https://github.com/kdvolder/somestuff +jobs: +- name: job1 + plan: + - get: sts4 + - get: bogus-get + - put: bogus-put