From b520fdc577ac1fac75afef399ad8b059dba04622 Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Fri, 28 Jul 2017 14:48:57 -0700 Subject: [PATCH] Some simplifications of dynamic CA/Reconcilng using PartialCollection --- .../bosh/BoshDeploymentManifestSchema.java | 85 ++++++++----------- .../ide/vscode/bosh/models/StemcellModel.java | 21 ++--- .../ide/vscode/bosh/BoshEditorTest.java | 62 ++++++++++++-- .../vscode/commons/util/EnumValueParser.java | 2 +- .../commons/util/PartialCollection.java | 4 + .../commons/yaml/schema/YTypeFactory.java | 35 ++++++-- .../vscode/concourse/PipelineYmlSchema.java | 2 +- 7 files changed, 139 insertions(+), 72 deletions(-) diff --git a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshDeploymentManifestSchema.java b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshDeploymentManifestSchema.java index 8e2b1a08c..c75ec38aa 100644 --- a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshDeploymentManifestSchema.java +++ b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshDeploymentManifestSchema.java @@ -192,36 +192,30 @@ public class BoshDeploymentManifestSchema implements YamlSchema { addProp(t_instance_group_env, "bosh", t_params); addProp(t_instance_group_env, "password", t_ne_string); - YType t_release_version = f.contextAware("ReleaseVersion", new SchemaContextAware() { - private AbstractType no_dynamic_checks = f.yenumFromDynamicValues("ReleaseVersion", - dc -> PartialCollection.compute(() -> releasesProvider.getModel(dc).getVersions()) - ) - .addHints("latest") - .parseWith(ValueParsers.NE_STRING); - private AbstractType base_type = f.yenumFromDynamicValues("ReleaseVersion", - dc -> PartialCollection.compute(() -> releasesProvider.getModel(dc).getVersions()) - ) - .addHints("latest") - .alsoAccept("latest"); - - @Override - public YType withContext(DynamicSchemaContext dc) throws Exception { - if (StringUtil.hasText(getCurrentEntityProperty(dc,"url"))) { - return no_dynamic_checks; + YType t_release_version = f.yenumFromDynamicValues("ReleaseVersion", + //message formatter: + dc -> (s, values) -> { + String name = getCurrentEntityProperty(dc, "name"); + if (StringUtil.hasText(name)) { + return "'"+s+"' is an unknown 'ReleaseVersion[name="+name+"]'. Valid values are: "+values; + } else { + return "'"+s+"' is an unknown 'ReleaseVersion'. Valid values are: "+values; + } + }, + //value provider: + dc -> { + PartialCollection releases = PartialCollection.compute(() -> releasesProvider.getModel(dc).getReleases()); + if (StringUtil.hasText(getCurrentEntityProperty(dc, "url"))) { + releases = releases.addUncertainty(); } else { String name = getCurrentEntityProperty(dc, "name"); if (StringUtil.hasText(name)) { - return f.yenumFromDynamicValues("ReleaseVersion[name="+name+"]", (_dc) -> - PartialCollection.compute(() -> releasesProvider.getModel(dc).getReleases()) - .map(r -> name.equals(r.getName()) ? r.getVersion() : null) - ) - .addHints("latest") - .alsoAccept("latest"); + releases = releases.map(r -> name.equals(r.getName()) ? r : null); } } - return base_type; + return releases.map(r -> r.getVersion()).add("latest"); } - }).treatAsAtomic(); + ); YBeanType t_release = f.ybean("Release"); addProp(t_release, "name", t_release_name_def).isPrimary(true); @@ -244,34 +238,25 @@ public class BoshDeploymentManifestSchema implements YamlSchema { YType t_stemcell_os_ref = f.yenumFromDynamicValues("StemcellOs", (dc) -> PartialCollection.compute(() -> stemcellsProvider.getModel(dc).getStemcellOss()) ); - YType t_stemcell_version_ref = f.contextAware("StemcellVersion", new SchemaContextAware() { - - YAtomicType baseType = f.yenumFromDynamicValues("StemcellVersion", (dc) -> - PartialCollection.compute(() -> stemcellsProvider.getModel(dc).getVersions()) - ); - { - baseType.addHints("latest"); - baseType.alsoAccept("latest"); - } - - @Override - public YType withContext(DynamicSchemaContext dc) throws Exception { - StemcellModel currentStemcell = getCurrentStemcell(dc); - if (StringUtil.hasText(currentStemcell.getName())||StringUtil.hasText(currentStemcell.getOs())) { - Predicate filter = currentStemcell.createVersionFilter(); - YAtomicType filteredType = f.yenumFromDynamicValues("StemcellVersion["+filter+"]", (_dc) -> { - //Note: it doesn't really matter whether we use _dc or dc in code below as they should be the same. - Predicate versionFilter = currentStemcell.createVersionFilter(); - return PartialCollection.compute(() -> stemcellsProvider.getModel(dc).getStemcells()) - .map(sc -> versionFilter.test(sc) ? sc.getVersion() : null); - }); - filteredType.addHints("latest"); - filteredType.alsoAccept("latest"); - return filteredType; + YType t_stemcell_version_ref = f.yenumFromDynamicValues("StemcellVersion", + (dc) -> (parseString, validValues) -> { + try { + Predicate filter = getCurrentStemcell(dc).createVersionFilter(); + if (filter!=StemcellModel.ALLWAYS_TRUE_FILTER) { + return "'"+parseString+"' is an unknown 'StemcellVersion["+filter+"]'. Valid values are: "+validValues; + } + } catch (Exception e) { + //ignore (parse error most likely) } - return baseType; + return "'"+parseString+"' is an unknown 'StemcellVersion'. Valid values are: "+validValues; + }, + (dc) -> { + Predicate filter = getCurrentStemcell(dc).createVersionFilter(); + return PartialCollection.compute(() -> stemcellsProvider.getModel(dc).getStemcells()) + .map(sc -> filter.test(sc) ? sc.getVersion() : null) + .add("latest"); } - }).treatAsAtomic(); + ); addProp(t_stemcell, "alias", t_stemcell_alias_def).isRequired(true); addProp(t_stemcell, "version", t_stemcell_version_ref).isRequired(true); diff --git a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/models/StemcellModel.java b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/models/StemcellModel.java index 47aad85f1..9d3266dfd 100644 --- a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/models/StemcellModel.java +++ b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/models/StemcellModel.java @@ -21,6 +21,16 @@ import org.yaml.snakeyaml.nodes.Node; */ public class StemcellModel { + public static final Predicate ALLWAYS_TRUE_FILTER = new Predicate() { + @Override + public boolean test(StemcellData sc) { + return true; + } + @Override + public String toString() { + return "true"; + } + }; private Node node; public StemcellModel(Node node) { @@ -79,16 +89,7 @@ public class StemcellModel { } }; } else { - return new Predicate() { - @Override - public boolean test(StemcellData sc) { - return true; - } - @Override - public String toString() { - return "true"; - } - }; + return ALLWAYS_TRUE_FILTER; } } diff --git a/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/BoshEditorTest.java b/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/BoshEditorTest.java index 1a2a19f92..e0ffbfc0b 100644 --- a/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/BoshEditorTest.java +++ b/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/BoshEditorTest.java @@ -1122,8 +1122,8 @@ public class BoshEditorTest { ); editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY); editor.assertProblems( - "123.4|unknown 'StemcellVersion[name=centos-agent]'. Valid values are: [222.2, 333.3]", - "333.3|unknown 'StemcellVersion[os=ubuntu]'. Valid values are: [123.4, 222.2]" + "123.4|unknown 'StemcellVersion[name=centos-agent]'. Valid values are: [222.2, 333.3, latest]", + "333.3|unknown 'StemcellVersion[os=ubuntu]'. Valid values are: [123.4, 222.2, latest]" ); } @@ -1200,6 +1200,17 @@ public class BoshEditorTest { "latest<*>" ); + editor = harness.newEditor( + "releases:\n" + + "- name: foo\n" + + " version: <*>" + ); + editor.assertContextualCompletions("<*>", + "123.4<*>", + "222.2<*>", + "latest<*>" + ); + //Still get all suggestions even when 'url' property is added editor = harness.newEditor( "releases:\n" + @@ -1213,6 +1224,19 @@ public class BoshEditorTest { "latest<*>" ); + editor = harness.newEditor( + "releases:\n" + + "- name: foo\n" + + " url: blah\n" + + " version: <*>" + ); + editor.assertContextualCompletions("<*>", + "123.4<*>", + "222.2<*>", + "333.3<*>", + "latest<*>" + ); + } @Test public void reconcileReleaseVersion() throws Exception { @@ -1228,10 +1252,13 @@ public class BoshEditorTest { "releases:\n" + "- version: bogus\n" + "- version: url-makes-this-possibly-correct\n" + - " url: file:///relesease-folder/blah-release.tar.gz" + " url: file:///relesease-folder/blah-release.tar.gz\n"+ + "- name: bar\n" + + " version: url-makes-this-also-possibly-correct\n" + + " url: file:///relesease-folder/other-release.tar.gz" ); editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY); - editor.assertProblems("bogus|unknown 'ReleaseVersion'. Valid values are: [123.4, 222.2, 333.3]"); + editor.assertProblems("bogus|unknown 'ReleaseVersion'. Valid values are: [123.4, 222.2, 333.3, latest]"); editor = harness.newEditor( "releases:\n" + @@ -1240,7 +1267,32 @@ public class BoshEditorTest { "- version: bogus\n" ); editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY); - editor.assertProblems("bogus|unknown 'ReleaseVersion'. Valid values are: [123.4, 222.2, 333.3]"); + editor.assertProblems("bogus|unknown 'ReleaseVersion'. Valid values are: [123.4, 222.2, 333.3, latest]"); + + editor = harness.newEditor( + "releases:\n" + + "- name: foo\n" + + " version: 123.4\n" + ); + editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY); + editor.assertProblems(/*NONE*/); + + editor = harness.newEditor( + "releases:\n" + + "- name: foo\n" + + " version: 222.2\n" + ); + editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY); + editor.assertProblems(/*NONE*/); + + editor = harness.newEditor( + "releases:\n" + + "- name: foo\n" + + " version: 333.3\n" + ); + editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY); + editor.assertProblems("333.3|unknown 'ReleaseVersion[name=foo]'. Valid values are: [123.4, 222.2, latest]"); + } diff --git a/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/EnumValueParser.java b/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/EnumValueParser.java index eec309abf..52856e0d5 100644 --- a/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/EnumValueParser.java +++ b/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/EnumValueParser.java @@ -64,7 +64,7 @@ public class EnumValueParser implements ValueParser { PartialCollection values = this.values.get(); - // If values is not known (null) then just assume the str is acceptable. + // If values is not fully known then just assume the str is acceptable. if (values == null || !values.isComplete() || values.getElements().contains(str)) { return str; } else { diff --git a/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/PartialCollection.java b/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/PartialCollection.java index 48d1e2ac3..3b0ddc065 100644 --- a/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/PartialCollection.java +++ b/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/PartialCollection.java @@ -10,6 +10,7 @@ *******************************************************************************/ package org.springframework.ide.vscode.commons.util; +import java.util.Arrays; import java.util.Collection; import java.util.concurrent.Callable; @@ -132,4 +133,7 @@ public class PartialCollection { return explanation; } + public PartialCollection add(@SuppressWarnings("unchecked") T... values) { + return addAll(Arrays.asList(values)); + } } diff --git a/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/YTypeFactory.java b/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/YTypeFactory.java index 5832b8b05..dec12c1c9 100644 --- a/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/YTypeFactory.java +++ b/headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/schema/YTypeFactory.java @@ -855,7 +855,7 @@ public class YTypeFactory { return ((YTypedPropertyImpl)prop).copy(); } - public YAtomicType yenumFromHints(String name, BiFunction, String> errorMessageFormatter, SchemaContextAware> values) { + public YAtomicType yenumFromHints(String name, SchemaContextAware, String>> errorMessageFormatter, SchemaContextAware> values) { YAtomicType t = yatomic(name); t.setHintProvider(values); t.parseWith((DynamicSchemaContext dc) -> { @@ -865,7 +865,11 @@ public class YTypeFactory { return new EnumValueParser(name, strings) { @Override protected String createErrorMessage(String parseString, Collection values) { - return errorMessageFormatter.apply(parseString, values); + try { + return errorMessageFormatter.withContext(dc).apply(parseString, values); + } catch (Exception e) { + return super.createErrorMessage(parseString, values); + } } }; } @@ -874,10 +878,22 @@ public class YTypeFactory { return t; } + public YAtomicType yenumFromDynamicValues(String name, + SchemaContextAware, String>> errorMessageFormatter, + SchemaContextAware> values + ) { + return yenumFromHints(name, + //Error message formatter: + errorMessageFormatter, + //Hints provider: + (dc) -> hints(values.withContext(dc)) + ); + } + public YAtomicType yenumFromDynamicValues(String name, SchemaContextAware> values) { return yenumFromHints(name, //Error message formatter: - (parseString, validValues) -> "'"+parseString+"' is an unknown '"+name+"'. Valid values are: "+validValues, + (dc) -> (parseString, validValues) -> "'"+parseString+"' is an unknown '"+name+"'. Valid values are: "+validValues, //Hints provider: (dc) -> hints(values.withContext(dc)) ); @@ -887,7 +903,7 @@ public class YTypeFactory { return new EnumTypeBuilder(name, values); } - public YAtomicType yenum(String name, BiFunction, String> errorMessageFormatter, SchemaContextAware> values) { + public YAtomicType yenum(String name, SchemaContextAware, String>> errorMessageFormatter, SchemaContextAware> values) { YAtomicType t = yatomic(name); t.setHintProvider((dc) -> { return PartialCollection.compute(() -> values.withContext(dc)) @@ -897,12 +913,21 @@ public class YTypeFactory { EnumValueParser enumParser = new EnumValueParser(name, values.withContext(dc)) { @Override protected String createErrorMessage(String parseString, Collection values) { - return errorMessageFormatter.apply(parseString, values); + try { + return errorMessageFormatter.withContext(dc).apply(parseString, values); + } catch (Exception e) { + return super.createErrorMessage(parseString, values); + } } }; return enumParser; }); return t; + + } + + public YAtomicType yenum(String name, BiFunction, String> errorMessageFormatter, SchemaContextAware> values) { + return yenum(name, (dc) -> errorMessageFormatter, values); } public static Collection values(Collection hints) { diff --git a/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/PipelineYmlSchema.java b/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/PipelineYmlSchema.java index 2f141b2d0..71af2b38a 100644 --- a/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/PipelineYmlSchema.java +++ b/headless-services/concourse-language-server/src/main/java/org/springframework/ide/vscode/concourse/PipelineYmlSchema.java @@ -171,7 +171,7 @@ public class PipelineYmlSchema implements YamlSchema { t_version.addHints("latest", "every"); t_resource_type_name = f.yenumFromHints("ResourceType Name", - (parseString, validValues) -> { + (dc) -> (parseString, validValues) -> { return "The '"+parseString+"' Resource Type does not exist. Existing types: "+validValues; }, (DynamicSchemaContext dc) -> {