Always suggest / accept 'latest' as a valid stemcell version

This commit is contained in:
Kris De Volder
2017-07-25 14:31:17 -07:00
parent 358b23f75d
commit 8f6c75de03
5 changed files with 115 additions and 37 deletions

View File

@@ -193,9 +193,13 @@ public class BoshDeploymentManifestSchema implements YamlSchema {
YBeanType t_stemcell = f.ybean("Stemcell");
YType t_stemcell_name_ref = f.yenumFromDynamicValues("StemcellName", (dc) -> stemcellsProvider.getModel(dc).getStemcellNames());
YType t_stemcell_os_ref = f.yenumFromDynamicValues("StemcellOs", (dc) -> stemcellsProvider.getModel(dc).getStemcellOss());
YType t_stemcell_version_ref = f.yenumFromDynamicValues("StemcellVersion", (dc) -> {
YType t_stemcell_name_ref = f.yenumFromDynamicValues("StemcellName", (dc) ->
stemcellsProvider.getModel(dc).getStemcellNames()
);
YType t_stemcell_os_ref = f.yenumFromDynamicValues("StemcellOs", (dc) ->
stemcellsProvider.getModel(dc).getStemcellOss()
);
YAtomicType t_stemcell_version_ref = f.yenumFromDynamicValues("StemcellVersion", (dc) -> {
StemcellModel currentStemcell = getCurrentStemcell(dc);
if (currentStemcell!=null) {
return stemcellsProvider.getModel(dc).getStemcells().stream()
@@ -207,6 +211,8 @@ public class BoshDeploymentManifestSchema implements YamlSchema {
//Troubles determining the filter. So return all stemcells.
return stemcellsProvider.getModel(dc).getVersions();
});
t_stemcell_version_ref.addHints("latest");
t_stemcell_version_ref.alsoAccept("latest");
addProp(t_stemcell, "alias", t_stemcell_alias_def).isRequired(true);
addProp(t_stemcell, "version", t_stemcell_version_ref).isRequired(true);
@@ -260,7 +266,7 @@ public class BoshDeploymentManifestSchema implements YamlSchema {
YBeanType t_variable = f.ybean("Variable");
addProp(t_variable, "name", t_var_name_def).isPrimary(true);
YType t_variable_type = f.yenum("VariableType", "certificate", "password", "rsa", "ssh")
.parseWith(ValueParsers.NE_STRING); //Overrid the parser -> no errors / warnings... in theory there could be other valid values.
.parseWith(ValueParsers.NE_STRING); //Override the parser -> no errors / warnings... in theory there could be other valid values.
addProp(t_variable, "type", t_variable_type).isRequired(true);
addProp(t_variable, "options", t_params);
addProp(v2Schema, "variables", f.yseq(t_variable));

View File

@@ -954,6 +954,22 @@ public class BoshEditorTest {
);
}
@SuppressWarnings("unchecked")
@Test public void contentAssistStemcellVersionNoDirector() throws Exception {
Editor editor;
stemcellsProvider = mock(DynamicModelProvider.class);
when(stemcellsProvider.getModel(any())).thenThrow(new IOException("Couldn't connect to bosh"));
editor = harness.newEditor(
"stemcells:\n" +
"- alias: good\n" +
" name: centos-agent\n" +
" version: <*>\n"
);
editor.assertContextualCompletions("<*>",
"latest<*>"
);
}
@Test public void contentAssistStemcellVersionFromDirector() throws Exception {
Editor editor;
stemcellsProvider = provideStemcellsFrom(
@@ -970,7 +986,7 @@ public class BoshEditorTest {
" version: <*>\n"
);
editor.assertContextualCompletions("<*>",
"222.2<*>", "333.3<*>"
"222.2<*>", "333.3<*>", "latest<*>"
);
editor = harness.newEditor(
@@ -980,7 +996,7 @@ public class BoshEditorTest {
" version: <*>\n"
);
editor.assertContextualCompletions("<*>",
"123.4<*>", "222.2<*>"
"123.4<*>", "222.2<*>", "latest<*>"
);
editor = harness.newEditor(
@@ -989,7 +1005,7 @@ public class BoshEditorTest {
" version: <*>\n"
);
editor.assertContextualCompletions("<*>",
"123.4<*>", "222.2<*>", "333.3<*>"
"123.4<*>", "222.2<*>", "333.3<*>", "latest<*>"
);
//when os or name are 'bogus' at least suggest proposals based on other prop
@@ -1001,7 +1017,7 @@ public class BoshEditorTest {
" version: <*>\n"
);
editor.assertContextualCompletions("<*>",
"222.2<*>", "333.3<*>"
"222.2<*>", "333.3<*>", "latest<*>"
);
editor = harness.newEditor(
@@ -1012,7 +1028,7 @@ public class BoshEditorTest {
" version: <*>\n"
);
editor.assertContextualCompletions("<*>",
"222.2<*>", "333.3<*>"
"222.2<*>", "333.3<*>", "latest<*>"
);
//when the os and name disagree, merge the proposals for both:
@@ -1024,10 +1040,34 @@ public class BoshEditorTest {
" version: <*>\n"
);
editor.assertContextualCompletions("<*>",
"123.4<*>", "222.2<*>", "333.3<*>"
"123.4<*>", "222.2<*>", "333.3<*>", "latest<*>"
);
}
@SuppressWarnings("unchecked")
@Test public void reconcileStemcellVersionNoDirector() throws Exception {
Editor editor;
stemcellsProvider = mock(DynamicModelProvider.class);
when(stemcellsProvider.getModel(any())).thenThrow(new IOException("Couldn't connect to bosh"));
editor = harness.newEditor(
"stemcells:\n" +
"- alias: aaa\n" +
" name: centos-agent\n" +
" version: 222.2\n" +
"- alias: bbb\n" +
" name: centos-agent\n" +
" version: 123.4\n" +
"- alias: ddd\n" +
" os: ubuntu\n" +
" version: 333.3\n" +
"- alias: eee\n" +
" os: ubuntu\n" +
" version: latest\n"
);
editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY);
editor.assertProblems(/*NONE*/);
}
@Test public void reconcileStemcellVersionFromDirector() throws Exception {
Editor editor;
stemcellsProvider = provideStemcellsFrom(
@@ -1047,7 +1087,10 @@ public class BoshEditorTest {
" version: 123.4\n" +
"- alias: ddd\n" +
" os: ubuntu\n" +
" version: 333.3\n"
" version: 333.3\n" +
"- alias: eee\n" +
" os: ubuntu\n" +
" version: latest\n"
);
editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY);
editor.assertProblems(

View File

@@ -11,6 +11,7 @@
package org.springframework.ide.vscode.commons.yaml.schema;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -24,12 +25,12 @@ 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.ReconcileException;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReplacementQuickfix;
import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.ide.vscode.commons.util.EnumValueParser;
import org.springframework.ide.vscode.commons.util.Log;
import org.springframework.ide.vscode.commons.util.Renderable;
import org.springframework.ide.vscode.commons.util.Renderables;
import org.springframework.ide.vscode.commons.util.ValueParser;
@@ -291,19 +292,30 @@ public class YTypeFactory {
return null;
}
public AbstractType addHintProvider(Callable<Collection<YValueHint>> hintProvider) {
addHintProvider((DynamicSchemaContext dc) -> hintProvider);
public AbstractType setHintProvider(Callable<Collection<YValueHint>> hintProvider) {
setHintProvider((DynamicSchemaContext dc) -> hintProvider);
return this;
}
public AbstractType addHintProvider(SchemaContextAware<Callable<Collection<YValueHint>>> hintProvider) {
public AbstractType setHintProvider(SchemaContextAware<Callable<Collection<YValueHint>>> hintProvider) {
//TODO: SchemaContextAware now allows throwing exceptions so should be able to simplify the above to SchemaContextAware<Collection<YValueHint>>
this.hintProvider = hintProvider;
return this;
}
public YValueHint[] getHintValues(DynamicSchemaContext dc) throws Exception {
Collection<YValueHint> providerHints=getProviderHints(dc);
Collection<YValueHint> providerHints = null;
try {
providerHints=getProviderHints(dc);
} catch (Exception e) {
if (!hints.isEmpty()) {
Log.log(e);
//Recover from error returning just the static hints.
return hints.toArray(new YValueHint[hints.size()]);
} else {
throw e;
}
}
if (providerHints == null || providerHints.isEmpty()) {
return hints.toArray(new YValueHint[hints.size()]);
@@ -324,6 +336,9 @@ public class YTypeFactory {
}
}
/**
* Prevents adding additional hints.
*/
public void sealHints() {
hints = ImmutableList.copyOf(hints);
}
@@ -382,23 +397,16 @@ public class YTypeFactory {
addProperty(new YTypedPropertyImpl(name, type));
}
public AbstractType addHints(String... strings) {
if (strings != null) {
for (String value : strings) {
BasicYValueHint hint = new BasicYValueHint(value);
if (!hints.contains(hint)) {
hints.add(hint);
}
}
}
return this;
return addHints(Arrays.stream(strings).map(s -> new BasicYValueHint(s)).toArray(BasicYValueHint[]::new));
}
public void addHints(YValueHint... extraHints) {
public AbstractType addHints(YValueHint... extraHints) {
for (YValueHint h : extraHints) {
if (!hints.contains(h)) {
hints.add(h);
}
}
return this;
}
public void parseWith(SchemaContextAware<ValueParser> parser) {
@@ -413,6 +421,29 @@ public class YTypeFactory {
return parser;
}
/**
* Modifies currently installed parser so it is guaranteed to accept at least given values.
* @return
*/
public AbstractType alsoAccept(String... _values) {
if (parser!=null) {
ImmutableSet<String> values = ImmutableSet.copyOf(_values);
final SchemaContextAware<ValueParser> oldParserProvider = parser;
parser = (dc) -> (s) -> {
if (values.contains(s)) {
return s;
} else {
ValueParser oldParser = oldParserProvider.safeWithContext(dc).orElse(null);
if (oldParser!=null) {
return oldParser.parse(s);
}
return s;
}
};
}
return this;
}
public AbstractType require(Constraint dynamicConstraint) {
this.constraints.add(dynamicConstraint);
return this;
@@ -849,7 +880,7 @@ public class YTypeFactory {
public YAtomicType yenumFromHints(String name, BiFunction<String, Collection<String>, String> errorMessageFormatter, SchemaContextAware<Collection<YValueHint>> values) {
YAtomicType t = yatomic(name);
t.addHintProvider((dc) -> () -> values.withContext(dc));
t.setHintProvider((dc) -> () -> values.withContext(dc));
t.parseWith((DynamicSchemaContext dc) -> {
Collection<String> strings = YTypeFactory.values(values.withContext(dc));
return new EnumValueParser(name, strings) {
@@ -867,9 +898,7 @@ public class YTypeFactory {
//Error message formatter:
(parseString, validValues) -> "'"+parseString+"' is an unknown '"+name+"'. Valid values are: "+validValues,
//Hints provider:
(dc) ->
hints(values.withContext(dc)
)
(dc) -> hints(values.withContext(dc))
);
}
@@ -879,7 +908,7 @@ public class YTypeFactory {
public YAtomicType yenum(String name, BiFunction<String, Collection<String>, String> errorMessageFormatter, SchemaContextAware<Collection<String>> values) {
YAtomicType t = yatomic(name);
t.addHintProvider((dc) -> {
t.setHintProvider((dc) -> {
Collection<String> strings = values.withContext(dc);
return strings==null
? null

View File

@@ -118,7 +118,7 @@ public class PipelineYmlSchema implements YamlSchema {
public final YType t_location = f.yatomic("Location")
//Note: we could have used f.yenum here too. But it saves memory if we don't keep the large set of ValueHints in memory.
// That's why we attach custom hint provider and parser here that do essentially the same thing.
.addHintProvider(() -> {
.setHintProvider(() -> {
return ZoneId.getAvailableZoneIds().stream()
.map(BasicYValueHint::new)
.collect(Collectors.toList());
@@ -187,7 +187,7 @@ public class PipelineYmlSchema implements YamlSchema {
}
);
t_maybe_resource_name = f.yatomic("ResourceName | TaskOutput");
t_maybe_resource_name.addHintProvider((DynamicSchemaContext dc) -> {
t_maybe_resource_name.setHintProvider((DynamicSchemaContext dc) -> {
//Putting the Callable into a local variable is strange, but the compiler doesn't like it if
// we return it directly. Too much complexity for Java type-inference?
Callable<Collection<YValueHint>> callable = () -> YTypeFactory.hints(models.getResourceNames(dc));

View File

@@ -122,25 +122,25 @@ public final class ManifestYmlSchema implements YamlSchema {
YAtomicType t_buildpack = f.yatomic("Buildpack");
if (buildpackProvider != null) {
t_buildpack.addHintProvider(buildpackProvider);
t_buildpack.setHintProvider(buildpackProvider);
// t_buildpack.parseWith(ManifestYmlValueParsers.fromHints(t_buildpack.toString(), buildpackProvider));
}
YAtomicType t_stack = f.yatomic("Stack");
if (stacksProvider!=null) {
t_stack.addHintProvider(stacksProvider);
t_stack.setHintProvider(stacksProvider);
t_stack.parseWith(ManifestYmlValueParsers.fromValueHints(stacksProvider, t_stack, ManifestYamlSchemaProblemsTypes.UNKNOWN_STACK_PROBLEM));
}
YAtomicType t_domain = f.yatomic("Domain");
if (domainsProvider != null) {
t_domain.addHintProvider(domainsProvider);
t_domain.setHintProvider(domainsProvider);
t_domain.parseWith(ManifestYmlValueParsers.fromValueHints(domainsProvider, t_domain, ManifestYamlSchemaProblemsTypes.UNKNOWN_DOMAIN_PROBLEM));
}
YAtomicType t_service = f.yatomic("Service");
if (servicesProvider != null) {
t_service.addHintProvider(servicesProvider);
t_service.setHintProvider(servicesProvider);
t_service.parseWith(new CFServicesValueParser(t_service.toString(),
YTypeFactory.valuesFromHintProvider(servicesProvider)));
}