Validate that concourse job groups assignment is complete

This commit is contained in:
Kris De Volder
2017-12-22 10:09:17 -08:00
parent 13d900b58f
commit 70f2db136b
3 changed files with 99 additions and 7 deletions

View File

@@ -22,6 +22,7 @@ import java.util.Optional;
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.reconcile.IProblemCollector;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
@@ -32,30 +33,26 @@ import org.springframework.ide.vscode.commons.util.Log;
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.ast.YamlASTProvider;
import org.springframework.ide.vscode.commons.yaml.ast.YamlAstCache;
import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST;
import org.springframework.ide.vscode.commons.yaml.ast.YamlParser;
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.path.YamlTraversal;
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.reconcile.ASTTypeCache.NodeTypes;
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.YType;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.AbstractType;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YBeanUnionType;
import org.springframework.ide.vscode.commons.yaml.schema.constraints.Constraint;
import org.springframework.ide.vscode.commons.yaml.schema.YTypedProperty;
import org.springframework.ide.vscode.commons.yaml.schema.YValueHint;
import org.springframework.ide.vscode.commons.yaml.util.StaleFallbackCache;
import org.springframework.ide.vscode.commons.yaml.schema.constraints.Constraint;
import org.springframework.ide.vscode.commons.yaml.util.Streams;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.error.YAMLException;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
@@ -131,6 +128,25 @@ public class ConcourseModel {
}
}
/**
* Verification of constraint: if at least one job is assigned to a group, then all jobs must be assigned to a group.
*/
public final void jobAssignmentIsComplete(DynamicSchemaContext dc, Node parent, Node node, YType type, IProblemCollector problems) {
Multiset<String> assignedJobs = getStringsFromAst(dc.getDocument(), JOBS_ASSIGNED_TO_GROUPS);
if (assignedJobs!=null && !assignedJobs.isEmpty()) {
getJobNameNodes(dc).forEach(jobDefName -> {
String name = NodeUtil.asScalar(jobDefName);
if (StringUtil.hasText(name)) { //'not assigned to a group' errors for empty names are a bit silly, so avoid that
if (!assignedJobs.contains(name)) {
problems.accept(YamlSchemaProblems.schemaProblem("'"+name+"' belongs to no group", jobDefName));
}
}
});
}
}
/**
* Get the job with given name. If there is no such job, or if there is more than one, this will return null.
*/
@@ -238,6 +254,14 @@ public class ConcourseModel {
anyChild()
);
public static final YamlPath JOBS_ASSIGNED_TO_GROUPS = new YamlPath(
anyChild(),
valueAt("groups"),
anyChild(),
valueAt("jobs"),
anyChild()
);
public static final YamlPath JOB_NAMES_PATH = new YamlPath(
anyChild(),
valueAt("jobs"),
@@ -333,6 +357,11 @@ public class ConcourseModel {
return getStringsFromAst(dc.getDocument(), JOB_NAMES_PATH);
}
public Stream<Node> getJobNameNodes(DynamicSchemaContext dc) {
return getFromAst(dc.getDocument(), ast -> JOB_NAMES_PATH.traverseAmbiguously(ast));
}
private Multiset<String> getStringsFromAst(IDocument doc, YamlPath path) {
return getFromAst(doc, (ast) -> {
return path

View File

@@ -399,7 +399,7 @@ public class PipelineYmlSchema implements YamlSchema {
addProp(TOPLEVEL_TYPE, "resources", f.yseq(t_resource));
addProp(TOPLEVEL_TYPE, "jobs", f.yseq(job));
addProp(TOPLEVEL_TYPE, "resource_types", f.yseq(resourceType));
addProp(TOPLEVEL_TYPE, "groups", f.yseq(group));
addProp(TOPLEVEL_TYPE, "groups", f.yseq(group).require(models::jobAssignmentIsComplete));
definitionTypes = ImmutableList.of(
jobNameDef,

View File

@@ -30,6 +30,7 @@ import org.springframework.ide.vscode.commons.util.IOUtil;
import org.springframework.ide.vscode.commons.util.Unicodes;
import org.springframework.ide.vscode.commons.util.text.LanguageId;
import org.springframework.ide.vscode.commons.yaml.completion.YamlCompletionEngineOptions;
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems;
import org.springframework.ide.vscode.languageserver.testharness.CodeAction;
import org.springframework.ide.vscode.languageserver.testharness.Editor;
import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness;
@@ -4313,6 +4314,68 @@ public class ConcourseEditorTest {
editor.assertProblems("not-a-valid-version|Valid values are: [every, latest]");
}
@Test public void jobGroupsCompletenessReconcile() throws Exception {
//1) if all jobs are assigned to a group... don't complain:
Editor editor = harness.newEditor(
"jobs:\n" +
"- name: build-snapshot\n" +
"- name: build-release\n" +
"- name: test-snapshot\n" +
"- name: test-release\n" +
"- name: publish-snapshot\n" +
"- name: publish-release\n" +
"groups:\n" +
"- name: snapshot\n" +
" jobs:\n" +
" - build-snapshot\n" +
" - test-snapshot\n" +
" - publish-snapshot\n" +
"- name: release\n" +
" jobs:\n" +
" - build-release\n" +
" - test-release\n" +
" - publish-release\n"
);
editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY);
editor.assertProblems(/*NONE*/);
//If there are no groups then, don't complain about inconmplete jobs partitioning
editor.setText(
"jobs:\n" +
"- name: build-snapshot\n" +
"- name: build-release\n" +
"- name: test-snapshot\n" +
"- name: test-release\n" +
"- name: publish-snapshot\n" +
"- name: publish-release\n"
);
//If at least one job is in a group, check that all jobs are in a group
editor.setText(
"jobs:\n" +
"- name: build-snapshot\n" +
"- name: build-release\n" +
"- name: test-snapshot\n" +
"- name: test-release\n" +
"- name: publish-snapshot\n" +
"- name: publish-release\n" +
"groups:\n" +
"- name: snapshot\n" +
" jobs:\n" +
" - build-snapshot\n" +
"- name: release\n" +
" jobs:\n" +
" - build-release"
);
editor.assertProblems(
"test-snapshot|'test-snapshot' belongs to no group",
"test-release|'test-release' belongs to no group",
"publish-snapshot|'publish-snapshot' belongs to no group",
"publish-release|'publish-release' belongs to no group"
);
}
//////////////////////////////////////////////////////////////////////////////
private void assertContextualCompletions(String conText, String textBefore, String... textAfter) throws Exception {