Fix #28: values 'latest' and 'every' should be accepted for 'version' in getstep

This commit is contained in:
Kris De Volder
2017-12-13 13:41:53 -08:00
parent 2e912dfb19
commit 3658f87b84
7 changed files with 213 additions and 13 deletions

View File

@@ -10,6 +10,8 @@
*******************************************************************************/
package org.springframework.ide.vscode.commons.util;
import static org.assertj.core.api.Assertions.setAllowComparingPrivateFields;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.Callable;
@@ -150,4 +152,40 @@ public class PartialCollection<T> {
public PartialCollection<T> add(@SuppressWarnings("unchecked") T... values) {
return addAll(Arrays.asList(values));
}
public PartialCollection<T> addAll(PartialCollection<T> values) {
PartialCollection<T> merged = this.addAll(values.getElements());
if (!values.isComplete()) {
merged = merged.addUncertainty();
if (merged.getExplanation()==null && values.getExplanation()!=null) {
merged = merged.withExplanation(values.getExplanation());
}
}
return merged;
}
private PartialCollection<T> withExplanation(Throwable explanation) {
return new PartialCollection<>(knownElements, isComplete, explanation);
}
@Override
public String toString() {
StringBuilder s = new StringBuilder("PartialCollection(");
boolean first = true;
for (T e : knownElements) {
if (!first) {
s.append(", ");
}
s.append(e.toString());
first = false;
}
if (!isComplete) {
if (!first) {
s.append(", ");
}
s.append("...");
}
s.append(")");
return s.toString();
}
}

View File

@@ -18,6 +18,7 @@ import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST;
import org.springframework.ide.vscode.commons.yaml.path.YamlPath;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.ScalarNode;
/**
* Adapts a SnakeYaml ast node as a {@link DynamicSchemaContext} (so it
@@ -30,11 +31,13 @@ public class ASTDynamicSchemaContext extends CachingSchemaContext {
private MappingNode mapNode;
private YamlPath path;
private YamlFileAST ast;
private Node node;
public ASTDynamicSchemaContext(YamlFileAST ast, YamlPath path, Node node) {
this.ast = ast;
this.path = path;
this.mapNode = as(MappingNode.class, node);
this.node = node;
}
@SuppressWarnings("unchecked")
@@ -59,9 +62,19 @@ public class ASTDynamicSchemaContext extends CachingSchemaContext {
public YamlPath getPath() {
return path;
}
@Override
public YamlFileAST getAST() {
return ast;
}
@Override
public boolean isAtomic() {
return node instanceof ScalarNode;
}
@Override
public boolean isMap() {
return mapNode!=null;
}
}

View File

@@ -31,7 +31,7 @@ import com.google.common.collect.ImmutableSet;
public interface DynamicSchemaContext {
DynamicSchemaContext NULL = new DynamicSchemaContext() {
@Override
public Set<String> getDefinedProperties() {
return ImmutableSet.of();
@@ -46,7 +46,16 @@ public interface DynamicSchemaContext {
public YamlPath getPath() {
return null;
}
@Override
public boolean isAtomic() {
return false;
}
@Override
public boolean isMap() {
return false;
}
};
/**
@@ -54,7 +63,7 @@ public interface DynamicSchemaContext {
* available (e.g. because of parsing errors)
*/
default YamlFileAST getAST() { return null; }
/**
* Returns the set of property names that are already defined in the current context.
* <p>
@@ -67,7 +76,7 @@ public interface DynamicSchemaContext {
* properties are defined in the surrounding object.
*/
Set<String> 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
@@ -80,4 +89,14 @@ public interface DynamicSchemaContext {
*/
YamlPath getPath();
/**
* Returns true if the current AST node is a scalar value
*/
boolean isAtomic();
/**
* Returns true if the current node is a Mapping node
*/
boolean isMap();
}

View File

@@ -74,4 +74,14 @@ public class SNodeDynamicSchemaContext extends CachingSchemaContext {
return "SNodeDynamicSchemaContext("+contextPath+")";
}
@Override
public boolean isAtomic() {
return false;
}
@Override
public boolean isMap() {
return false;
}
}

View File

@@ -24,6 +24,7 @@ import java.util.TreeSet;
import java.util.concurrent.Callable;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileException;
@@ -35,6 +36,7 @@ import org.springframework.ide.vscode.commons.util.PartialCollection;
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.yaml.ast.YamlFileAST;
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems;
import org.springframework.ide.vscode.commons.yaml.schema.constraints.Constraint;
import org.springframework.ide.vscode.commons.yaml.schema.constraints.Constraints;
@@ -153,9 +155,34 @@ public class YTypeFactory {
return new YBeanType(name, properties);
}
public YBeanUnionType yunion(String name, YBeanType... types) {
public YBeanUnionType yBeanUnion(String name, YBeanType[] types) {
return (YBeanUnionType) yunion(name, types);
}
public YType yunion(String name, YType... types) {
Assert.isLegal(types.length>1);
return new YBeanUnionType(name, types);
if (Stream.of(types).allMatch(t -> t instanceof YBeanType)) {
YBeanType[] beanTypes = new YBeanType[types.length];
for (int i = 0; i < beanTypes.length; i++) {
beanTypes[i] = (YBeanType) types[i];
}
return new YBeanUnionType(name, beanTypes);
}
ArrayList<YMapType> maps = new ArrayList<>(types.length);
ArrayList<YAtomicType> atoms = new ArrayList<>(types.length);
for (YType t : types) {
if (t instanceof YMapType) {
maps.add((YMapType) t);
} else if (t instanceof YAtomicType) {
atoms.add((YAtomicType) t);
} else {
throw new IllegalArgumentException("Union of this kind of types is not (yet) supported: "+t);
}
}
if (atoms.size()==1 && maps.size()==1) {
return new YAtomAndMapUnion(name, atoms.get(0), maps.get(0));
}
throw new IllegalArgumentException("Union of this kind of types is not (yet) supported: "+types);
}
/**
@@ -763,6 +790,49 @@ public class YTypeFactory {
}
}
public class YAtomAndMapUnion extends AbstractType {
private String name;
private YAtomicType atom;
private YMapType map;
public YAtomAndMapUnion(String name, YAtomicType atom, YMapType map) {
this.name = name;
this.atom = atom;
this.map = map;
}
@Override
public String toString() {
return name;
}
@Override
public boolean isAtomic() {
return true;
}
@Override
public boolean isMap() {
return true;
}
@Override
public YType inferMoreSpecificType(DynamicSchemaContext dc) {
if (dc.isAtomic()) {
return atom;
} else if (dc.isMap()) {
return map;
}
return super.inferMoreSpecificType(dc);
}
@Override
public PartialCollection<YValueHint> getHintValues(DynamicSchemaContext dc) {
return atom.getHintValues(dc).addAll(map.getHintValues(dc));
}
}
public static class YTypedPropertyImpl implements YTypedProperty, Cloneable {
@@ -1001,5 +1071,4 @@ public class YTypeFactory {
return this;
}
}

View File

@@ -12,9 +12,7 @@ package org.springframework.ide.vscode.concourse;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
@@ -34,7 +32,6 @@ import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment;
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems;
import org.springframework.ide.vscode.commons.yaml.schema.BasicYValueHint;
import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext;
import org.springframework.ide.vscode.commons.yaml.schema.SchemaContextAware;
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;
@@ -99,7 +96,10 @@ public class PipelineYmlSchema implements YamlSchema {
public final YType t_any = f.yany("Object");
public final YType t_params = f.ymap(t_string, t_any);
public final YType t_string_params = f.ymap(t_string, t_string);
public final YType t_resource_version = f.ymap(t_string, t_string);
public final YType t_resource_version = f.yunion("ResourceVersion",
f.yenum("ResourceVersionString", "latest", "every"),
t_string_params
);
public final YType t_pos_integer = f.yatomic("Positive Integer")
.parseWith(ValueParsers.POS_INTEGER);
public final YType t_strictly_pos_integer = f.yatomic("Strictly Positive Integer")
@@ -346,7 +346,7 @@ public class PipelineYmlSchema implements YamlSchema {
doStep,
tryStep
};
YBeanUnionType step = f.yunion("Step", stepTypes);
YBeanUnionType step = f.yBeanUnion("Step", stepTypes);
addProp(aggregateStep, "aggregate", f.yseq(step));
addProp(doStep, "do", f.yseq(step));
addProp(tryStep, "try", step);

View File

@@ -4078,6 +4078,27 @@ public class ConcourseEditorTest {
);
}
@Test public void getStepVersionShouldAcceptLatestAndEvery() throws Exception {
//See https://github.com/spring-projects/sts4/pull/24
Editor editor = harness.newEditor(
"jobs:\n" +
"- name: do-stuff\n" +
" plan:\n" +
" - get: cf-deployment-git\n" +
" version: latest\n" +
" - get: some-resource\n" +
" version: every\n" +
" - get: other-resource\n" +
" version: bogus"
);
editor.assertProblems(
"cf-deployment-git|resource does not exist",
"some-resource|resource does not exist",
"other-resource|resource does not exist",
"bogus|Valid values are: [every, latest]"
);
}
@Test public void getStepVersionShouldAcceptMap() throws Exception {
//See https://github.com/spring-projects/sts4/pull/24
Editor editor = harness.newEditor(
@@ -4090,6 +4111,36 @@ public class ConcourseEditorTest {
editor.assertProblems("cf-deployment-git|resource does not exist");
}
@Test public void getStepVersionCompletionsSuggestLatestAndEvery() throws Exception {
//See https://github.com/spring-projects/sts4/pull/24
Editor editor = harness.newEditor(
"jobs:\n" +
"- name: do-stuff\n" +
" plan:\n" +
" - get: cf-deployment-git\n" +
" version: <*>"
);
editor.assertCompletionLabels("every", "latest");
}
@Test public void getStepVersionMapStringStringValidation() throws Exception {
//See https://github.com/spring-projects/sts4/pull/24
Editor editor = harness.newEditor(
"jobs:\n" +
"- name: do-stuff\n" +
" plan:\n" +
" - get: cf-deployment-git\n" +
" version: { ref: good}\n" +
" - get: other-rsrc\n" +
" version: { ref: [bad]}\n"
);
editor.assertProblems(
"cf-deployment-git|resource does not exist",
"other-rsrc|resource does not exist",
"[bad]|Expecting a 'String' but found a 'Sequence'"
);
}
//////////////////////////////////////////////////////////////////////////////
private void assertContextualCompletions(String conText, String textBefore, String... textAfter) throws Exception {