resource-type name reconciling
This commit is contained in:
@@ -18,9 +18,6 @@ package org.springframework.ide.vscode.commons.languageserver.reconcile;
|
||||
*/
|
||||
public class ReconcileException extends Exception implements ProblemTypeProvider {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final ProblemType problemType;
|
||||
|
||||
|
||||
@@ -23,15 +23,20 @@ import java.util.concurrent.Callable;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Provider;
|
||||
|
||||
import org.springframework.ide.vscode.commons.util.Assert;
|
||||
import org.springframework.ide.vscode.commons.util.EnumValueParser;
|
||||
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.schema.YTypeFactory.YAtomicType;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableList.Builder;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Multiset;
|
||||
|
||||
/**
|
||||
* Static utility method for creating YType objects representing either
|
||||
@@ -607,6 +612,21 @@ public class YTypeFactory {
|
||||
return new YTypedPropertyImpl(name, type);
|
||||
}
|
||||
|
||||
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.parseWith((DynamicSchemaContext dc) -> {
|
||||
Collection<String> strings = YTypeFactory.values(values.withContext(dc));
|
||||
return new EnumValueParser("blah", strings) {
|
||||
@Override
|
||||
protected String createErrorMessage(String parseString, Collection<String> values) {
|
||||
return errorMessageFormatter.apply(parseString, values);
|
||||
}
|
||||
};
|
||||
});
|
||||
return t;
|
||||
}
|
||||
|
||||
public YAtomicType yenum(String name, BiFunction<String, Collection<String>, String> errorMessageFormatter, SchemaContextAware<Collection<String>> values) {
|
||||
YAtomicType t = yatomic(name);
|
||||
t.addHintProvider((dc) -> {
|
||||
@@ -629,6 +649,10 @@ public class YTypeFactory {
|
||||
return t;
|
||||
}
|
||||
|
||||
public static Collection<String> values(Collection<YValueHint> hints) {
|
||||
return hints.stream().map(YValueHint::getValue).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public YAtomicType yenum(String name, String... values) {
|
||||
YAtomicType t = yatomic(name);
|
||||
t.addHints(values);
|
||||
@@ -636,8 +660,35 @@ public class YTypeFactory {
|
||||
return t;
|
||||
}
|
||||
|
||||
public YValueHint hint(String value, String label) {
|
||||
public static Callable<Collection<String>> valuesFromHintProvider(Callable<Collection<YValueHint>> hintProvider) {
|
||||
Callable<Collection<String>> values = () -> {
|
||||
Collection<YValueHint> hints = hintProvider.call();
|
||||
if (hints != null) {
|
||||
ImmutableSet.Builder<String> builder = ImmutableSet.builder();
|
||||
for (YValueHint hint : hints ) {
|
||||
builder.add(hint.getValue());
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
return null;
|
||||
};
|
||||
return values;
|
||||
}
|
||||
|
||||
public static YValueHint hint(String value, String label) {
|
||||
return new BasicYValueHint(value, label);
|
||||
}
|
||||
|
||||
public static YValueHint hint(String value) {
|
||||
return new BasicYValueHint(value);
|
||||
}
|
||||
|
||||
public static Collection<YValueHint> hints(Collection<String> values) {
|
||||
if (values!=null) {
|
||||
return values.stream().map(YTypeFactory::hint).collect(Collectors.toList());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -13,7 +13,12 @@ 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.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
|
||||
@@ -28,14 +33,22 @@ import org.springframework.ide.vscode.commons.yaml.path.ASTRootCursor;
|
||||
import org.springframework.ide.vscode.commons.yaml.path.NodeCursor;
|
||||
import org.springframework.ide.vscode.commons.yaml.path.YamlPath;
|
||||
import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YValueHint;
|
||||
import org.springframework.ide.vscode.concourse.util.CollectorUtil;
|
||||
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;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMultiset;
|
||||
import com.google.common.collect.ImmutableMultiset.Builder;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Multiset;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* ConcourseModels is responsible for extracting various bits of information
|
||||
* out of .yml documents and caching them for use by various tools (reconcile engine
|
||||
@@ -55,6 +68,12 @@ public class ConcourseModel {
|
||||
valueAt("name")
|
||||
);
|
||||
|
||||
private static final YamlPath RESOURCE_TYPE_NAMES_PATH = new YamlPath(
|
||||
valueAt("resource_types"),
|
||||
anyChild(),
|
||||
valueAt("name")
|
||||
);
|
||||
|
||||
private static final YamlPath RESOURCES_PATH = new YamlPath(
|
||||
anyChild(), // skip over the root node which contains multiple doces
|
||||
valueAt("resources"),
|
||||
@@ -140,6 +159,26 @@ public class ConcourseModel {
|
||||
});
|
||||
}
|
||||
|
||||
public Multiset<String> getResourceTypeNames(IDocument doc) {
|
||||
Collection<YValueHint> hints = getResourceTypeNameHints(doc);
|
||||
if (hints!=null) {
|
||||
return ImmutableMultiset.copyOf(YTypeFactory.values(hints));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Collection<YValueHint> getResourceTypeNameHints(IDocument doc) {
|
||||
Multiset<String> userDefined = getStringsFromAst(doc, RESOURCE_TYPE_NAMES_PATH);
|
||||
if (userDefined!=null) {
|
||||
Builder<YValueHint> builder = ImmutableMultiset.builder();
|
||||
builder.addAll(YTypeFactory.hints(userDefined));
|
||||
builder.addAll(Arrays.asList(PipelineYmlSchema.BUILT_IN_RESOURCE_TYPES));
|
||||
return builder.build();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private <T> T getFromAst(IDocument doc, Function<YamlFileAST, T> astFunction) {
|
||||
try {
|
||||
if (doc!=null) {
|
||||
|
||||
@@ -10,14 +10,20 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.concourse;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.ide.vscode.commons.util.EnumValueParser;
|
||||
import org.springframework.ide.vscode.commons.util.RegexpParser;
|
||||
import org.springframework.ide.vscode.commons.util.ValueParser;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.SchemaContextAware;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YValueHint;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Multiset;
|
||||
import com.google.common.collect.ImmutableSet.Builder;
|
||||
|
||||
/**
|
||||
* Methods and constants to create/get parsers for some atomic types
|
||||
@@ -27,6 +33,17 @@ import com.google.common.collect.Multiset;
|
||||
*/
|
||||
public class ConcourseValueParsers {
|
||||
|
||||
// public static final SchemaContextAware<ValueParser> resourceTypeName(ConcourseModel models) {
|
||||
// return (dc) -> {
|
||||
// return new EnumValueParser("ResourceType Name", models.getResourceTypeNames(dc.getDocument())) {
|
||||
// @Override
|
||||
// protected String createErrorMessage(String value, Collection<String> validValues) {
|
||||
// return "The '"+value+"' Resource Type does not exist. Existing resource types: "+validValues;
|
||||
// }
|
||||
// };
|
||||
// };
|
||||
// };
|
||||
|
||||
public static final SchemaContextAware<ValueParser> resourceNameDef(ConcourseModel models) {
|
||||
return acceptOnlyUniqueNames(models::getResourceNames, "resource name");
|
||||
}
|
||||
@@ -61,6 +78,4 @@ public class ConcourseValueParsers {
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.concourse;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.ide.vscode.commons.util.Renderable;
|
||||
import org.springframework.ide.vscode.commons.util.Renderables;
|
||||
import org.springframework.ide.vscode.commons.util.ValueParsers;
|
||||
@@ -25,7 +28,11 @@ import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YAtomicTy
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YBeanType;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YBeanUnionType;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YTypedPropertyImpl;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypeUtil;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YValueHint;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YamlSchema;
|
||||
|
||||
/**
|
||||
@@ -33,6 +40,33 @@ import org.springframework.ide.vscode.commons.yaml.schema.YamlSchema;
|
||||
*/
|
||||
public class PipelineYmlSchema implements YamlSchema {
|
||||
|
||||
//TODO: the infos for composing this should probably be integrated somehow in the ResourceTypeRegistry so
|
||||
// we only have a list of built-in resource types in a single place.
|
||||
public static final YValueHint[] BUILT_IN_RESOURCE_TYPES = {
|
||||
hint("git", "The 'git' resource can pull and push to git repositories."),
|
||||
hint("hg", "The 'hg' resource can pull and push to Mercurial repositories."),
|
||||
hint("time", "The 'time' resource can start jobs on a schedule or timestamp outputs."),
|
||||
hint("s3", "The 's3' resource can fetch from and upload to S3 buckets."),
|
||||
hint("archive", "The 'archive' resource can fetch and extract .tar.gz archives."),
|
||||
hint("semver", "The 'semver' resource can set or bump version numbers."),
|
||||
hint("github-release", "The 'github-release' resource can fetch and publish versioned GitHub resources."),
|
||||
hint("docker-image", "The 'docker-image' resource can fetch, build, and push Docker images."),
|
||||
hint("tracker", "The 'tracker' resource can deliver stories and bugs on Pivotal Tracker."),
|
||||
hint("pool", "The 'pool' resource allows you to configure how to serialize use of an external system. "
|
||||
+ "This lets you prevent test interference or overwork on shared systems."),
|
||||
hint("cf", "The cf resource can deploy an application to Cloud Foundry."),
|
||||
hint("bosh-io-release", "The bosh-io-release resource can track and fetch new BOSH releases from bosh.io."),
|
||||
hint("bosh-io-stemcell", "The bosh-io-stemcell resource can track and fetch new BOSH stemcells from bosh.io."),
|
||||
hint("bosh-deployment", "The bosh-deployment resource can deploy BOSH stemcells and releases."),
|
||||
hint("vagrant-cloud", "The vagrant-cloud resource can fetch and publish Vagrant boxes to Atlas.")
|
||||
};
|
||||
|
||||
public static final Set<String> BUILT_IN_RESOURCE_TYPE_NAMES = Flux.fromArray(PipelineYmlSchema.BUILT_IN_RESOURCE_TYPES)
|
||||
.map(YValueHint::getValue)
|
||||
.collect(Collectors.toSet())
|
||||
.block();
|
||||
|
||||
|
||||
private final YBeanType TOPLEVEL_TYPE;
|
||||
private final YTypeUtil TYPE_UTIL;
|
||||
|
||||
@@ -76,32 +110,13 @@ public class PipelineYmlSchema implements YamlSchema {
|
||||
YAtomicType t_image_type = f.yatomic("ImageType");
|
||||
t_image_type.addHints("docker_image");
|
||||
|
||||
YAtomicType t_resource_type_name = f.yatomic("ResourceType Name");
|
||||
t_resource_type_name.addHints(
|
||||
f.hint("archive", "archive - The 'archive' resource can fetch and extract .tar.gz archives."),
|
||||
f.hint("git", "git - The 'git' resource can pull and push to git repositories"),
|
||||
f.hint("s3", "s3 - The 's3' resource can fetch from and upload to S3 buckets."),
|
||||
f.hint("semver", "semver - The 'semver' resource can set or bump version numbers."),
|
||||
f.hint("time", "time - The 'time' resource can start jobs on a schedule or timestamp outputs."),
|
||||
f.hint("docker-image", "docker-image - The 'docker-image' resource can fetch, build, and push Docker images")
|
||||
//TODO: add more resource types and descriptions.
|
||||
//
|
||||
// The github-release resource can fetch and publish versioned GitHub resources.
|
||||
//
|
||||
//
|
||||
// The tracker resource can deliver stories and bugs on Pivotal Tracker
|
||||
//
|
||||
// The pool resource allows you to configure how to serialize use of an external system. This lets you prevent test interference or overwork on shared systems.
|
||||
//
|
||||
// The cf resource can deploy an application to Cloud Foundry.
|
||||
//
|
||||
// The bosh-io-release resource can track and fetch new BOSH releases from bosh.io.
|
||||
//
|
||||
// The bosh-io-stemcell resource can track and fetch new BOSH stemcells from bosh.io.
|
||||
//
|
||||
// The bosh-deployment resource can deploy BOSH stemcells and releases.
|
||||
//
|
||||
// The vagrant-cloud r
|
||||
YAtomicType t_resource_type_name = f.yenumFromHints("ResourceType Name",
|
||||
(parseString, validValues) -> {
|
||||
return "The '"+parseString+"' Resource Type does not exist. Existing types: "+validValues;
|
||||
},
|
||||
(DynamicSchemaContext dc) -> {
|
||||
return models.getResourceTypeNameHints(dc.getDocument());
|
||||
}
|
||||
);
|
||||
|
||||
this.t_resource_name = f.yenum("Resource Name",
|
||||
@@ -109,7 +124,7 @@ public class PipelineYmlSchema implements YamlSchema {
|
||||
return "The '"+parseString+"' resource does not exist. Existing resources: "+validValues;
|
||||
},
|
||||
(DynamicSchemaContext dc) -> {
|
||||
return models.getResourceNames(dc.getDocument());
|
||||
return (models.getResourceNames(dc.getDocument()));
|
||||
}
|
||||
);
|
||||
|
||||
@@ -221,6 +236,10 @@ public class PipelineYmlSchema implements YamlSchema {
|
||||
initializeDefaultResourceTypes();
|
||||
}
|
||||
|
||||
private static YValueHint hint(String value, String description) {
|
||||
return YTypeFactory.hint(value, value + " - " + description);
|
||||
}
|
||||
|
||||
private void initializeDefaultResourceTypes() {
|
||||
// git :
|
||||
{
|
||||
|
||||
@@ -17,7 +17,6 @@ import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.lsp4j.Diagnostic;
|
||||
import org.eclipse.lsp4j.DiagnosticSeverity;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -396,22 +395,26 @@ public class PipelineYamlEditorTest {
|
||||
|
||||
@Test
|
||||
public void valueCompletions() throws Exception {
|
||||
String [] builtInResourceTypes = {
|
||||
"git", "hg", "time", "s3",
|
||||
"archive", "semver", "github-release",
|
||||
"docker-image", "tracker", "pool", "cf", "bosh-io-release",
|
||||
"bosh-io-stemcell", "bosh-deployment", "vagrant-cloud"
|
||||
};
|
||||
Arrays.sort(builtInResourceTypes);
|
||||
|
||||
String[] expectedCompletions = new String[builtInResourceTypes.length];
|
||||
for (int i = 0; i < expectedCompletions.length; i++) {
|
||||
expectedCompletions[i] =
|
||||
"resources:\n" +
|
||||
"- type: "+builtInResourceTypes[i]+"<*>";
|
||||
}
|
||||
|
||||
assertCompletions(
|
||||
"resources:\n" +
|
||||
"- type: <*>"
|
||||
, //=>
|
||||
"resources:\n" +
|
||||
"- type: archive<*>",
|
||||
"resources:\n" +
|
||||
"- type: docker-image<*>",
|
||||
"resources:\n" +
|
||||
"- type: git<*>",
|
||||
"resources:\n" +
|
||||
"- type: s3<*>",
|
||||
"resources:\n" +
|
||||
"- type: semver<*>",
|
||||
"resources:\n" +
|
||||
"- type: time<*>"
|
||||
expectedCompletions
|
||||
);
|
||||
assertCompletions(
|
||||
"jobs:\n" +
|
||||
@@ -1051,9 +1054,9 @@ public class PipelineYamlEditorTest {
|
||||
//addProp(resource, "name", resourceNameDef).isRequired(true);
|
||||
editor = harness.newEditor(
|
||||
"resources:\n" +
|
||||
"- type: foo"
|
||||
"- type: git"
|
||||
);
|
||||
editor.assertProblems("type: foo|'name' is required");
|
||||
editor.assertProblems("type: git|'name' is required");
|
||||
|
||||
//addProp(resource, "type", t_resource_type_name).isRequired(true);
|
||||
editor = harness.newEditor(
|
||||
@@ -1312,6 +1315,54 @@ public class PipelineYamlEditorTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void reconcileResourceTypeNames() throws Exception {
|
||||
String userDefinedResourceTypesSnippet =
|
||||
"resource_types:\n" +
|
||||
"- name: s3-multi\n" +
|
||||
" type: docker-image\n" +
|
||||
" source:\n" +
|
||||
" repository: kdvolder/s3-resource-simple\n" +
|
||||
"- name: slack-notification\n" +
|
||||
" type: docker-image\n" +
|
||||
" source:\n" +
|
||||
" repository: cfcommunity/slack-notification-resource\n" +
|
||||
" tag: latest\n";
|
||||
String[] goodNames = {
|
||||
//user-defined:
|
||||
"s3-multi", "slack-notification",
|
||||
//built-in:
|
||||
"git", "hg", "time", "s3",
|
||||
"archive", "semver", "github-release",
|
||||
"docker-image", "tracker", "pool", "cf", "bosh-io-release",
|
||||
"bosh-io-stemcell", "bosh-deployment", "vagrant-cloud"
|
||||
};
|
||||
String[] badNames = {
|
||||
"bogus", "wrong", "not-defined-resource-type"
|
||||
};
|
||||
|
||||
//All the bad names are detected and flagged:
|
||||
for (String badName : badNames) {
|
||||
Editor editor = harness.newEditor(
|
||||
userDefinedResourceTypesSnippet +
|
||||
"resources:\n" +
|
||||
"- name: the-resource\n" +
|
||||
" type: "+badName
|
||||
);
|
||||
editor.assertProblems(badName+"|Resource Type does not exist");
|
||||
}
|
||||
|
||||
//All the good names are accepted:
|
||||
for (String goodName : goodNames) {
|
||||
Editor editor = harness.newEditor(
|
||||
userDefinedResourceTypesSnippet +
|
||||
"resources:\n" +
|
||||
"- name: the-resource\n" +
|
||||
" type: "+goodName
|
||||
);
|
||||
editor.assertProblems(/*None*/);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void assertContextualCompletions(String conText, String textBefore, String... textAfter) throws Exception {
|
||||
|
||||
@@ -68,7 +68,7 @@ public class ManifestYmlSchema implements YamlSchema {
|
||||
if (servicesProvider != null) {
|
||||
t_service_string.addHintProvider(servicesProvider);
|
||||
t_service_string.parseWith(new CFServicesValueParser(t_service_string.toString(),
|
||||
ManifestYmlValueParsers.getValuesFromHints(servicesProvider)));
|
||||
YTypeFactory.valuesFromHintProvider(servicesProvider)));
|
||||
}
|
||||
YType t_services = f.yseq(t_service_string);
|
||||
|
||||
|
||||
@@ -92,27 +92,4 @@ public class ManifestYmlValueParsers {
|
||||
};
|
||||
}
|
||||
|
||||
public static ValueParser fromHints(String typeName, Callable<Collection<YValueHint>> hintProvider) {
|
||||
Callable<Collection<String>> values = getValuesFromHints(hintProvider);
|
||||
|
||||
return new EnumValueParser(typeName, values);
|
||||
}
|
||||
|
||||
public static Callable<Collection<String>> getValuesFromHints(Callable<Collection<YValueHint>> hintProvider) {
|
||||
Callable<Collection<String>> values= () -> {
|
||||
Collection<YValueHint> hints = hintProvider.call();
|
||||
if (hints != null) {
|
||||
Builder<String> builder = ImmutableSet.builder();
|
||||
|
||||
for (YValueHint hint : hints ) {
|
||||
builder.add(hint.getValue());
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
return values;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user