Refactor / simplify how Yaml schema Constraints work:
- simplify access to DynamicSchemaContext. - allow constraints on non-map type nodes. These changes pave the way to checking inertaction constraint on job-name used in 'passed' attribute.
This commit is contained in:
@@ -20,8 +20,8 @@ import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SnippetBuilder;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
@@ -35,10 +35,12 @@ 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.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.YTypedProperty;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YValueHint;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.constraints.Constraint;
|
||||
import org.springframework.ide.vscode.concourse.util.CollectorUtil;
|
||||
import org.springframework.ide.vscode.concourse.util.StaleFallbackCache;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
@@ -58,6 +60,14 @@ import com.google.common.collect.Multiset;
|
||||
*/
|
||||
public class ConcourseModel {
|
||||
|
||||
// /**
|
||||
// * Verification of contraint: a job used in the 'passed' attribute of a step
|
||||
// * must interact with the resource in question.
|
||||
// */
|
||||
// public Constraint jobHasInteractionWithResource() {
|
||||
// return (IDocument doc, Node parent, Node node, YType type, Set<String> foundProps, IProblemCollector problems) -> {
|
||||
// }
|
||||
|
||||
/**
|
||||
* Wraps around a Node in the AST that represents a 'step' and
|
||||
* provides methods for accessing information from the node.
|
||||
|
||||
@@ -240,7 +240,7 @@ public class PipelineYmlSchema implements YamlSchema {
|
||||
addProp(task, "outputs", f.yseq(t_output));
|
||||
addProp(task, "run", t_command).isRequired(true);
|
||||
addProp(task, "params", t_string_params);
|
||||
task.require((dc) -> {
|
||||
task.require(Constraints.schemaContextAware((DynamicSchemaContext dc) -> {
|
||||
LanguageId languageId = dc.getDocument().getLanguageId();
|
||||
if (LanguageId.CONCOURSE_PIPELINE.equals(languageId)) {
|
||||
Node parentImageDef = models.getParentPropertyNode("image", dc);
|
||||
@@ -256,7 +256,7 @@ public class PipelineYmlSchema implements YamlSchema {
|
||||
} else {
|
||||
return Constraints.requireAtMostOneOf("image_resource", "image");
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
AbstractType t_put_get_name = f.contextAware("Name", (dc) -> {
|
||||
if (models.getParentPropertyNode("resource", dc)!=null) {
|
||||
@@ -287,17 +287,20 @@ public class PipelineYmlSchema implements YamlSchema {
|
||||
addProp(putStep, "get_params", f.contextAware("GetParams", (dc) ->
|
||||
resourceTypes.getInParamsType(getResourceType("put", models, dc))
|
||||
));
|
||||
putStep.require((dc) -> (IDocument doc, Node parent, MappingNode map, YType type, Set<String> foundProps, IProblemCollector problems) -> {
|
||||
StepModel step = models.newStep("put", map);
|
||||
String resourceName = step.getResourceName();
|
||||
if (resourceName!=null) {
|
||||
ResourceModel resource = models.getResource(doc, resourceName);
|
||||
if (resource!=null) {
|
||||
if ("git".equals(resource.getType()) && !resource.hasSourceProperty("branch")) {
|
||||
problems.accept(YamlSchemaProblems.schemaProblem(
|
||||
"Resource of type 'git' is used in a 'put' step, so it should define 'branch' attribute in its 'source', but it doesn't.",
|
||||
step.getResourceNameNode()
|
||||
));
|
||||
putStep.require((DynamicSchemaContext dc, Node parent, Node _map, YType type, IProblemCollector problems) -> {
|
||||
if (_map instanceof MappingNode) {
|
||||
MappingNode map = (MappingNode) _map;
|
||||
StepModel step = models.newStep("put", map);
|
||||
String resourceName = step.getResourceName();
|
||||
if (resourceName!=null) {
|
||||
ResourceModel resource = models.getResource(dc.getDocument(), resourceName);
|
||||
if (resource!=null) {
|
||||
if ("git".equals(resource.getType()) && !resource.hasSourceProperty("branch")) {
|
||||
problems.accept(YamlSchemaProblems.schemaProblem(
|
||||
"Resource of type 'git' is used in a 'put' step, so it should define 'branch' attribute in its 'source', but it doesn't.",
|
||||
step.getResourceNameNode()
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3116,6 +3116,108 @@ public class ConcourseEditorTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Ignore @Test public void reconcilerJobFromPassedAttributeMustInteractWithResource() throws Exception {
|
||||
Editor editor;
|
||||
|
||||
editor = harness.newEditor(
|
||||
"resources:\n" +
|
||||
"- name: version\n" +
|
||||
" type: semver\n" +
|
||||
"- name: source-repo\n" +
|
||||
" type: git\n" +
|
||||
"jobs:\n" +
|
||||
"- name: build-it\n" +
|
||||
" plan:\n" +
|
||||
" - aggregate:\n" +
|
||||
" # - put: version\n" +
|
||||
" - get: source-repo\n" +
|
||||
"- name: test-it\n" +
|
||||
" plan:\n" +
|
||||
" - get: source-repo\n" +
|
||||
" passed:\n" +
|
||||
" - build-it # <- good\n" +
|
||||
" - get: version\n" +
|
||||
" passed:\n" +
|
||||
" - build-it # <- bad\n"
|
||||
);
|
||||
editor.assertProblems(
|
||||
"build-it^ # <- bad|Job 'build-it' doesn't interact with the resource 'version'"
|
||||
);
|
||||
|
||||
editor = harness.newEditor(
|
||||
"resources:\n" +
|
||||
"- name: version\n" +
|
||||
" type: semver\n" +
|
||||
"- name: source-repo\n" +
|
||||
" type: git\n" +
|
||||
"jobs:\n" +
|
||||
"- name: build-it\n" +
|
||||
" plan:\n" +
|
||||
" - aggregate:\n" +
|
||||
" - put: version\n" +
|
||||
" # - get: source-repo\n" +
|
||||
"- name: test-it\n" +
|
||||
" plan:\n" +
|
||||
" - get: source-repo\n" +
|
||||
" passed:\n" +
|
||||
" - build-it # <- bad\n" +
|
||||
" - get: version\n" +
|
||||
" passed:\n" +
|
||||
" - build-it # <- good\n"
|
||||
);
|
||||
editor.assertProblems(
|
||||
"build-it^ # <- bad|Job 'build-it' doesn't interact with the resource 'source-repo'"
|
||||
);
|
||||
|
||||
//Check that we find interactions in steps that are at the top-level of the plan:
|
||||
editor = harness.newEditor(
|
||||
"resources:\n" +
|
||||
"- name: version\n" +
|
||||
" type: semver\n" +
|
||||
"- name: source-repo\n" +
|
||||
" type: git\n" +
|
||||
"jobs:\n" +
|
||||
"- name: build-it\n" +
|
||||
" plan:\n" +
|
||||
" - aggregate:\n" +
|
||||
" - put: version\n" +
|
||||
" - get: source-repo\n" +
|
||||
"- name: test-it\n" +
|
||||
" plan:\n" +
|
||||
" - get: source-repo\n" +
|
||||
" passed:\n" +
|
||||
" - build-it\n" +
|
||||
" - get: version\n" +
|
||||
" passed:\n" +
|
||||
" - build-it\n"
|
||||
);
|
||||
editor.assertProblems(/*NONE*/);
|
||||
|
||||
//Check that we find interactions in steps that are nested in other steps
|
||||
editor = harness.newEditor(
|
||||
"resources:\n" +
|
||||
"- name: version\n" +
|
||||
" type: semver\n" +
|
||||
"- name: source-repo\n" +
|
||||
" type: git\n" +
|
||||
"jobs:\n" +
|
||||
"- name: build-it\n" +
|
||||
" plan:\n" +
|
||||
" - put: version\n" +
|
||||
" - get: source-repo\n" +
|
||||
"- name: test-it\n" +
|
||||
" plan:\n" +
|
||||
" - get: source-repo\n" +
|
||||
" passed:\n" +
|
||||
" - build-it\n" +
|
||||
" - get: version\n" +
|
||||
" passed:\n" +
|
||||
" - build-it\n"
|
||||
);
|
||||
editor.assertProblems(/*NONE*/);
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void assertContextualCompletions(String conText, String textBefore, String... textAfter) throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user