Check for duplicate defintions in bosh manifest

This commit is contained in:
Kris De Volder
2017-07-17 16:39:05 -07:00
parent 53ca2f5642
commit 2ffaed4436
4 changed files with 107 additions and 3 deletions

View File

@@ -18,6 +18,8 @@ import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.ide.vscode.commons.util.Renderable;
import org.springframework.ide.vscode.commons.util.Renderables;
import org.springframework.ide.vscode.commons.util.ValueParsers;
import org.springframework.ide.vscode.commons.yaml.reconcile.ASTTypeCache;
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems;
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;
@@ -72,8 +74,10 @@ public class BoshDeploymentManifestSchema implements YamlSchema {
private YType t_stemcell_alias_name_def;
private YType t_release_name_def;
private YType t_var_name_def;
private final ASTTypeCache astTypes;
public BoshDeploymentManifestSchema() {
public BoshDeploymentManifestSchema(ASTTypeCache astTypes) {
this.astTypes = astTypes;
TYPE_UTIL = f.TYPE_UTIL;
V2_TOPLEVEL_TYPE = createV2Schema();
@@ -228,6 +232,10 @@ public class BoshDeploymentManifestSchema implements YamlSchema {
for (String v1Prop : DEPRECATED_V1_PROPS) {
addProp(v2Schema, v1Prop, t_any).isDeprecated("Deprecated: '"+v1Prop+"' is a V1 schema property. Consider migrating your deployment manifest to V2");
}
for (YType defType : getDefinitionTypes()) {
v2Schema.require(Constraints.uniqueDefinition(this.astTypes, defType, YamlSchemaProblems.problemType("BOSH_DUPLICATE_"+defType)));
}
return v2Schema;
}

View File

@@ -44,7 +44,8 @@ public class BoshLanguageServer extends SimpleLanguageServer {
super("vscode-bosh");
YamlASTProvider parser = new YamlParser(yaml);
SimpleTextDocumentService documents = getTextDocumentService();
BoshDeploymentManifestSchema schema = new BoshDeploymentManifestSchema();
ASTTypeCache astTypeCache = new ASTTypeCache();
BoshDeploymentManifestSchema schema = new BoshDeploymentManifestSchema(astTypeCache);
YamlStructureProvider structureProvider = YamlStructureProvider.DEFAULT;
YamlAssistContextProvider contextProvider = new SchemaBasedYamlAssistContextProvider(schema);
@@ -54,7 +55,6 @@ public class BoshLanguageServer extends SimpleLanguageServer {
HoverInfoProvider infoProvider = new YamlHoverInfoProvider(parser, structureProvider, contextProvider);
VscodeHoverEngine hoverEngine = new VscodeHoverEngineAdapter(this, infoProvider);
YamlQuickfixes quickfixes = new YamlQuickfixes(getQuickfixRegistry(), getTextDocumentService(), structureProvider);
ASTTypeCache astTypeCache = new ASTTypeCache();
YamlSchemaBasedReconcileEngine engine = new YamlSchemaBasedReconcileEngine(parser, schema, quickfixes);
engine.setTypeCollector(astTypeCache);
documents.onDocumentSymbol(new TypeBasedYamlSymbolHandler(documents, astTypeCache, schema.getDefinitionTypes()));

View File

@@ -681,4 +681,62 @@ public class BoshEditorTest {
"blobstore_secure_link_secret|Variable"
);
}
@Test public void duplicateSymbolChecking() throws Exception {
Editor editor = harness.newEditor(
"name: foo\n" +
"variables:\n" +
"- name: dup_var\n" +
" type: password\n" +
"- name: blobstore_admin_users_password\n" +
" type: password\n" +
"- name: blobstore_secure_link_secret\n" +
" type: password\n" +
"- name: dup_var\n" +
" type: ssh\n" +
"stemcells:\n" +
"- alias: default\n" +
" os: ubuntu-trusty\n" +
" version: '3421.11'\n" +
"- alias: dup_cell\n" +
" os: ubuntu-trusty\n" +
" version: '3421.11'\n" +
"- alias: dup_cell\n" +
" os: ubuntu-trusty\n" +
" version: '3421.11'\n" +
"instance_groups:\n" +
"- name: foo-group\n" +
" networks:\n" +
" - name: the-network\n" +
" static_ips: []\n" +
" default: []\n" +
"- name: bar-group\n" +
" networks:\n" +
" - name: the-network\n" +
" static_ips: []\n" +
" default: []\n" +
"- name: bar-group\n" +
" networks:\n" +
" - name: the-network\n" +
" static_ips: []\n" +
" default: []\n" +
"releases:\n" +
"- name: one-release\n" +
"- name: other-release\n" +
"- name: one-release\n"
);
editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY);
editor.assertProblems(
"dup_var|Duplicate 'VariableName'",
"dup_var|Duplicate 'VariableName'",
"dup_cell|Duplicate 'StemcellAliasName'",
"dup_cell|Duplicate 'StemcellAliasName'",
"bar-group|Duplicate 'InstanceGroupName'",
"bar-group|Duplicate 'InstanceGroupName'",
"one-release|Duplicate 'ReleaseName'",
"one-release|Duplicate 'ReleaseName'"
);
}
}

View File

@@ -16,15 +16,20 @@ import static org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaPr
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.ide.vscode.commons.util.StringUtil;
import org.springframework.ide.vscode.commons.util.text.IDocument;
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
import org.springframework.ide.vscode.commons.yaml.reconcile.ASTTypeCache;
import org.springframework.ide.vscode.commons.yaml.reconcile.ASTTypeCache.NodeTypes;
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems;
import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext;
import org.springframework.ide.vscode.commons.yaml.schema.SchemaContextAware;
@@ -33,7 +38,11 @@ import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.NodeTuple;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
import com.google.common.collect.Multiset;
/**
* Various static methods for constructing/composing {@link Constraint}s.
@@ -156,4 +165,33 @@ public class Constraints {
};
}
/**
* Check that all nodes of a given type, across the AST represent unique names.
*/
public static Constraint uniqueDefinition(ASTTypeCache astTypes, YType defType, ProblemType problemType) {
return (DynamicSchemaContext dc, Node parent, Node _ignored_node, YType type, IProblemCollector problems) -> {
NodeTypes nodeTypes = astTypes.getNodeTypes(dc.getDocument().getUri());
if (nodeTypes!=null) {
Collection<Node> nodes = nodeTypes.getNodes(defType);
if (nodes!=null && !nodes.isEmpty()) {
Multimap<String, Node> name2nodes = ArrayListMultimap.create();
for (Node node : nodes) {
String name = NodeUtil.asScalar(node);
if (StringUtil.hasText(name)) {
name2nodes.put(name, node);
}
}
for (String name : name2nodes.keys()) {
Collection<Node> nodesForName = name2nodes.get(name);
if (nodesForName.size()>1) {
for (Node duplicateNode : nodesForName) {
problems.accept(YamlSchemaProblems.problem(problemType, "Duplicate '"+defType+"'", duplicateNode));
}
}
}
}
}
};
}
}