Fix #28: values 'latest' and 'every' should be accepted for 'version' in getstep
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
@@ -74,4 +74,14 @@ public class SNodeDynamicSchemaContext extends CachingSchemaContext {
|
||||
return "SNodeDynamicSchemaContext("+contextPath+")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAtomic() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMap() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user