GitResourceSource type: all properties defined.

This commit is contained in:
Kris De Volder
2017-01-16 08:34:56 -08:00
parent 5aa7c4076e
commit f47020af5e
5 changed files with 195 additions and 89 deletions

View File

@@ -36,6 +36,7 @@ import org.springframework.ide.vscode.commons.yaml.schema.YTypedProperty;
import org.springframework.ide.vscode.commons.yaml.schema.YamlSchema;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.NodeId;
import org.yaml.snakeyaml.nodes.NodeTuple;
import org.yaml.snakeyaml.nodes.ScalarNode;
import org.yaml.snakeyaml.nodes.SequenceNode;
@@ -67,7 +68,7 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler {
if (type!=null) {
DynamicSchemaContext schemaContext = new ASTDynamicSchemaContext(doc, path, node);
type = typeUtil.inferMoreSpecificType(type, schemaContext);
switch (node.getNodeId()) {
switch (getNodeId(node)) {
case mapping:
MappingNode map = (MappingNode) node;
checkForDuplicateKeys(map);
@@ -113,7 +114,10 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler {
ValueParser parser = typeUtil.getValueParser(type, schemaContext);
if (parser!=null) {
try {
parser.parse(NodeUtil.asScalar(node));
String value = NodeUtil.asScalar(node);
if (value!=null) {
parser.parse(value);
}
} catch (Exception e) {
String msg = ExceptionUtil.getMessage(e);
valueParseError(type, node, msg);
@@ -129,6 +133,36 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler {
}
}
protected NodeId getNodeId(Node node) {
NodeId id = node.getNodeId();
if (id==NodeId.mapping && isMoustacheVar((MappingNode)node)) {
return NodeId.scalar;
}
return id;
}
/**
* 'Moustache' variables look like `{{name}}` and unfortuately when
* parsed these will parse as a kind of map. But since these vars are meant to be replaced
* with some kind of string we should treat them as scalar instead.
* <p>
* This function recognizes a mapping node that actually is moustache var pattern.
*/
private boolean isMoustacheVar(Node node) {
return NodeUtil.asScalar(debrace(debrace(node))) != null;
}
private Node debrace(Node _node) {
MappingNode node = NodeUtil.asMapping(_node);
if (node!=null && node.getFlowStyle() && node.getValue().size()==1) {
NodeTuple entry = node.getValue().get(0);
if ("".equals(NodeUtil.asScalar(entry.getValueNode()))) {
return entry.getKeyNode();
}
}
return null;
}
private YamlPath keyAt(YamlPath path, String key) {
if (path!=null && key!=null) {
return path.append(YamlPathSegment.keyAt(key));

View File

@@ -607,8 +607,9 @@ public class YTypeFactory {
return description;
}
public void setDescriptionProvider(Renderable description) {
public YTypedPropertyImpl setDescriptionProvider(Renderable description) {
this.description = description;
return this;
}
}

View File

@@ -10,8 +10,6 @@
*******************************************************************************/
package org.springframework.ide.vscode.concourse;
import java.util.Set;
import org.springframework.ide.vscode.commons.util.Renderable;
import org.springframework.ide.vscode.commons.util.Renderables;
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
@@ -25,7 +23,9 @@ import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.AbstractT
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YAtomicType;
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 org.springframework.ide.vscode.commons.yaml.schema.YTypeUtil;
import org.springframework.ide.vscode.commons.yaml.schema.YTypedProperty;
import org.springframework.ide.vscode.commons.yaml.schema.YamlSchema;
/**
@@ -36,7 +36,15 @@ public class PipelineYmlSchema implements YamlSchema {
private final YBeanType TOPLEVEL_TYPE;
private final YTypeUtil TYPE_UTIL;
private final YTypeFactory f = new YTypeFactory();
public final YTypeFactory f = new YTypeFactory();
public final YType t_string = f.yatomic("String");
public final YType t_strings = f.yseq(t_string);
public final YAtomicType t_boolean = f.yenum("boolean", "true", "false");
public final YType t_any = f.yany("Object");
public final YType t_params = f.ymap(t_string, t_any);
public final YType t_string_params = f.ymap(t_string, t_string);
private final ResourceTypeRegistry resourceTypes = new ResourceTypeRegistry();
public PipelineYmlSchema(ConcourseModel models) {
TYPE_UTIL = f.TYPE_UTIL;
@@ -44,20 +52,13 @@ public class PipelineYmlSchema implements YamlSchema {
// define schema types
TOPLEVEL_TYPE = f.ybean("Pipeline");
YType t_string = f.yatomic("String");
YAtomicType t_ne_string = f.yatomic("String");
t_ne_string.parseWith(ValueParsers.NE_STRING);
YType t_strings = f.yseq(t_string);
YAtomicType t_boolean = f.yenum("boolean", "true", "false");
YAtomicType t_pos_integer = f.yatomic("Positive Integer");
t_pos_integer.parseWith(ValueParsers.POS_INTEGER);
YAtomicType t_strictly_pos_integer = f.yatomic("Strictly Positive Integer");
t_strictly_pos_integer.parseWith(ValueParsers.integerAtLeast(1));
YType t_any = f.yany("Object");
YType t_params = f.ymap(t_string, t_any);
YType t_string_params = f.ymap(t_string, t_string);
YAtomicType t_duration = f.yatomic("Duration");
t_duration.parseWith(ValueParsers.DURATION);
@@ -119,28 +120,28 @@ public class PipelineYmlSchema implements YamlSchema {
jobNameDef.parseWith(ValueParsers.jobNameDef(models));
YBeanType getStep = f.ybean("GetStep");
prop(getStep, "get", resourceName);
prop(getStep, "resource", t_string);
prop(getStep, "version", t_version);
prop(getStep, "passed", f.yseq(jobName));
prop(getStep, "params", t_params);
prop(getStep, "trigger", t_boolean);
addProp(getStep, "get", resourceName);
addProp(getStep, "resource", t_string);
addProp(getStep, "version", t_version);
addProp(getStep, "passed", f.yseq(jobName));
addProp(getStep, "params", t_params);
addProp(getStep, "trigger", t_boolean);
YBeanType putStep = f.ybean("PutStep");
prop(putStep, "put", resourceName);
prop(putStep, "resource", jobName);
prop(putStep, "params", t_params);
prop(putStep, "get_params", t_params);
addProp(putStep, "put", resourceName);
addProp(putStep, "resource", jobName);
addProp(putStep, "params", t_params);
addProp(putStep, "get_params", t_params);
YBeanType taskStep = f.ybean("TaskStep");
prop(taskStep, "task", t_ne_string);
prop(taskStep, "file", t_string);
prop(taskStep, "config", t_any);
prop(taskStep, "privileged", t_boolean);
prop(taskStep, "params", t_params);
prop(taskStep, "image", t_ne_string);
prop(taskStep, "input_mapping", f.ymap(t_ne_string, resourceName));
prop(taskStep, "output_mapping", t_string_params);
addProp(taskStep, "task", t_ne_string);
addProp(taskStep, "file", t_string);
addProp(taskStep, "config", t_any);
addProp(taskStep, "privileged", t_boolean);
addProp(taskStep, "params", t_params);
addProp(taskStep, "image", t_ne_string);
addProp(taskStep, "input_mapping", f.ymap(t_ne_string, resourceName));
addProp(taskStep, "output_mapping", t_string_params);
YBeanType aggregateStep = f.ybean("AggregateStep");
YBeanType doStep = f.ybean("DoStep");
@@ -155,83 +156,111 @@ public class PipelineYmlSchema implements YamlSchema {
tryStep
};
YBeanUnionType step = f.yunion("Step", stepTypes);
prop(aggregateStep, "aggregate", f.yseq(step));
prop(doStep, "do", f.yseq(step));
prop(tryStep, "try", step);
addProp(aggregateStep, "aggregate", f.yseq(step));
addProp(doStep, "do", f.yseq(step));
addProp(tryStep, "try", step);
// shared properties applicable for any subtype of Step:
for (YBeanType subStep : stepTypes) {
prop(step, subStep, "on_success", step);
prop(step, subStep, "on_failure", step);
prop(step, subStep, "ensure", step);
prop(step, subStep, "attempts", t_strictly_pos_integer);
prop(step, subStep, "tags", t_strings);
prop(step, subStep, "timeout", t_duration);
addProp(step, subStep, "on_success", step);
addProp(step, subStep, "on_failure", step);
addProp(step, subStep, "ensure", step);
addProp(step, subStep, "attempts", t_strictly_pos_integer);
addProp(step, subStep, "tags", t_strings);
addProp(step, subStep, "timeout", t_duration);
}
YType gitResourceSource = t_any;
YType resourceSource = f.contextAware("ResourceSource", (dc) -> {
YamlPath path = dc.getPath();
if (path!=null) {
YamlFileAST root = models.getSafeAst(dc.getDocument());
if (root!=null) {
String typeTag = NodeUtil.asScalar(path.dropLast().append(YamlPathSegment.valueAt("type")).traverseToNode(root));
switch (typeTag) {
case "git":
return gitResourceSource;
default:
break;
}
}
String typeTag = getResourceTypeTag(models, dc);
if (typeTag!=null) {
return resourceTypes.getSourceType(typeTag);
}
return t_any;
});
YBeanType resource = f.ybean("Resource");
prop(resource, "name", resourceNameDef);
prop(resource, "type", t_resource_type_name);
prop(resource, "source", resourceSource);
prop(resource, "check_every", t_duration);
addProp(resource, "name", resourceNameDef);
addProp(resource, "type", t_resource_type_name);
addProp(resource, "source", resourceSource);
addProp(resource, "check_every", t_duration);
YBeanType job = f.ybean("Job");
prop(job, "name", jobNameDef);
prop(job, "serial", t_boolean);
prop(job, "build_logs_to_retain", t_pos_integer);
prop(job, "serial_groups", t_strings);
prop(job, "max_in_flight", t_pos_integer);
prop(job, "public", t_boolean);
prop(job, "disable_manual_trigger", t_boolean);
prop(job, "plan", f.yseq(step));
addProp(job, "name", jobNameDef);
addProp(job, "serial", t_boolean);
addProp(job, "build_logs_to_retain", t_pos_integer);
addProp(job, "serial_groups", t_strings);
addProp(job, "max_in_flight", t_pos_integer);
addProp(job, "public", t_boolean);
addProp(job, "disable_manual_trigger", t_boolean);
addProp(job, "plan", f.yseq(step));
YBeanType resourceType = f.ybean("ResourceType");
prop(resourceType, "name", t_ne_string);
prop(resourceType, "type", t_image_type);
prop(resourceType, "source", t_any);
addProp(resourceType, "name", t_ne_string);
addProp(resourceType, "type", t_image_type);
addProp(resourceType, "source", t_any);
YBeanType group = f.ybean("Group");
prop(group, "name", t_ne_string);
prop(group, "resources", f.yseq(resourceName));
prop(group, "jobs", f.yseq(jobName));
prop(TOPLEVEL_TYPE, "resources", f.yseq(resource));
prop(TOPLEVEL_TYPE, "jobs", f.yseq(job));
prop(TOPLEVEL_TYPE, "resource_types", f.yseq(resourceType));
prop(TOPLEVEL_TYPE, "groups", f.yseq(group));
addProp(group, "name", t_ne_string);
addProp(group, "resources", f.yseq(resourceName));
addProp(group, "jobs", f.yseq(jobName));
addProp(TOPLEVEL_TYPE, "resources", f.yseq(resource));
addProp(TOPLEVEL_TYPE, "jobs", f.yseq(job));
addProp(TOPLEVEL_TYPE, "resource_types", f.yseq(resourceType));
addProp(TOPLEVEL_TYPE, "groups", f.yseq(group));
initializeDefaultResourceTypes();
}
private void prop(AbstractType superType, AbstractType bean, String name, YType type) {
bean.addProperty(name, type, descriptionFor(superType, name));
private void initializeDefaultResourceTypes() {
YBeanType gitSource = f.ybean("GitResourceSource");
addProp(gitSource, "uri", t_string);
addProp(gitSource, "branch", t_string);
addProp(gitSource, "private_key", t_string);
addProp(gitSource, "username", t_string);
addProp(gitSource, "password", t_string);
addProp(gitSource, "paths", t_strings);
addProp(gitSource, "ignore_paths", t_strings);
addProp(gitSource, "skip_ssl_verification", t_boolean);
addProp(gitSource, "tag_filter", t_string);
addProp(gitSource, "git_config", t_string);
addProp(gitSource, "disable_ci_skip", t_boolean);
addProp(gitSource, "commit_verification_keys", t_strings);
addProp(gitSource, "commit_verification_key_ids", t_strings);
addProp(gitSource, "gpg_keyserver", t_strings);
resourceTypes.def("git", gitSource);
}
private void prop(AbstractType bean, String name, YType type) {
prop(bean, bean, name, type);
private String getResourceTypeTag(ConcourseModel models, DynamicSchemaContext dc) {
YamlPath path = dc.getPath();
if (path!=null) {
YamlFileAST root = models.getSafeAst(dc.getDocument());
if (root!=null) {
return NodeUtil.asScalar(path.dropLast().append(YamlPathSegment.valueAt("type")).traverseToNode(root));
}
}
return null;
}
private Renderable descriptionFor(YType owner, String propName) {
private YTypedProperty prop(AbstractType beanType, String name, YType type) {
YTypedPropertyImpl prop = f.yprop(name, type);
prop.setDescriptionProvider(descriptionFor(beanType, name));
return prop;
}
private void addProp(AbstractType superType, AbstractType bean, String name, YType type) {
bean.addProperty(prop(superType, name, type));
}
private void addProp(AbstractType bean, String name, YType type) {
addProp(bean, bean, name, type);
}
public static Renderable descriptionFor(YType owner, String propName) {
String typeName = owner.toString();
return Renderables.fromClasspath(this.getClass(), "/desc/"+typeName+"/"+propName);
return Renderables.fromClasspath(PipelineYmlSchema.class, "/desc/"+typeName+"/"+propName);
}
@Override

View File

@@ -0,0 +1,42 @@
/*******************************************************************************
* Copyright (c) 2017 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.concourse;
import java.util.HashMap;
import java.util.Map;
import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.ide.vscode.commons.yaml.schema.YType;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YBeanType;
/**
* Keeps track of known resource types. For now this only keeps track of the resource-types that
* are built-in to concourse.
*
* @author Kris De Volder
*/
public class ResourceTypeRegistry {
private Map<String, YType> sourceTypes = new HashMap<>();
public ResourceTypeRegistry() {
}
public void def(String resourceTypeName, YBeanType sourceType) {
Assert.isLegal(!sourceTypes.containsKey(resourceTypeName), "Multiple definitions for '"+resourceTypeName+"'");
sourceTypes.put(resourceTypeName, sourceType);
}
public YType getSourceType(String typeTag) {
return sourceTypes.get(typeTag);
}
}

View File

@@ -470,7 +470,7 @@ public class PipelineYamlEditorTest {
"- name: sts4\n" +
" type: git\n" +
" source:\n" +
" repository: https://github.com/kdvolder/somestuff\n" +
" uri: https://github.com/kdvolder/somestuff\n" +
"jobs:\n" +
"- name: job1\n" +
" plan:\n" +
@@ -502,15 +502,15 @@ public class PipelineYamlEditorTest {
"- name: sts4\n" +
" type: git\n" +
" source:\n" +
" repository: https://github.com/kdvolder/somestuff\n" +
" uri: https://github.com/kdvolder/somestuff\n" +
"- name: utils\n" +
" type: git\n" +
" source:\n" +
" repository: https://github.com/kdvolder/someutils\n" +
" uri: https://github.com/kdvolder/someutils\n" +
"- name: sts4\n" +
" type: git\n" +
" source:\n" +
" repository: https://github.com/kdvolder/extras\n"
" uri: https://github.com/kdvolder/extras\n"
);
editor.assertProblems(
"sts4|Duplicate resource name",
@@ -572,13 +572,13 @@ public class PipelineYamlEditorTest {
"- name: my-repo\n" +
" type: git\n" +
" source:\n" +
" repository: https://github.com/kdvolder/my-repo\n" +
" uri: https://github.com/kdvolder/my-repo\n" +
"resources:\n" +
"- name: your-repo\n" +
" type: git\n" +
" type: git\n" +
" source:\n" +
" repository: https://github.com/kdvolder/forked-repo\n"
" uri: https://github.com/kdvolder/forked-repo\n"
);
editor.assertProblems(