From 80b1510668d217be562b4d38656168a01269ccbf Mon Sep 17 00:00:00 2001 From: Tobias Oberlies Date: Tue, 10 Nov 2015 12:02:41 +0100 Subject: [PATCH 1/3] Ignore invalid keys in the parser The validator already detects invalid keys, so the parser doesn't need to report them by throwing an exception. This allows to read the valid parts from an invalid RAML file. --- .../parser/builder/DefaultTupleBuilder.java | 2 +- .../parser/visitor/YamlDocumentBuilder.java | 4 ++++ .../parser/visitor/YamlDocumentSuggester.java | 6 +++++- .../raml/validation/ValidationTestCase.java | 18 ++++++++++++++++++ .../org/raml/validation/unknown-key.yaml | 7 +++++++ 5 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 src/test/resources/org/raml/validation/unknown-key.yaml diff --git a/src/main/java/org/raml/parser/builder/DefaultTupleBuilder.java b/src/main/java/org/raml/parser/builder/DefaultTupleBuilder.java index 7cb523ee..22b903ee 100644 --- a/src/main/java/org/raml/parser/builder/DefaultTupleBuilder.java +++ b/src/main/java/org/raml/parser/builder/DefaultTupleBuilder.java @@ -65,7 +65,7 @@ public NodeBuilder getBuilderForTuple(NodeTuple tuple) return tupleBuilder; } } - throw new RuntimeException("Builder not found for " + tuple); + return null; } protected Map> getBuilders() diff --git a/src/main/java/org/raml/parser/visitor/YamlDocumentBuilder.java b/src/main/java/org/raml/parser/visitor/YamlDocumentBuilder.java index ecb9d601..860ac0c1 100644 --- a/src/main/java/org/raml/parser/visitor/YamlDocumentBuilder.java +++ b/src/main/java/org/raml/parser/visitor/YamlDocumentBuilder.java @@ -270,6 +270,10 @@ public boolean onTupleStart(NodeTuple nodeTuple) if (currentBuilder != null) { NodeBuilder builder = currentBuilder.getBuilderForTuple(nodeTuple); + if (builder == null) + { + return false; + } builderContext.push(builder); } else diff --git a/src/main/java/org/raml/parser/visitor/YamlDocumentSuggester.java b/src/main/java/org/raml/parser/visitor/YamlDocumentSuggester.java index 5290dc31..1a393efd 100644 --- a/src/main/java/org/raml/parser/visitor/YamlDocumentSuggester.java +++ b/src/main/java/org/raml/parser/visitor/YamlDocumentSuggester.java @@ -307,7 +307,11 @@ public boolean onTupleStart(NodeTuple nodeTuple) { try { - builder.onTupleStart(nodeTuple); + boolean found = builder.onTupleStart(nodeTuple); + if (!found) + { + return false; + } MappingNode mapping = nodeTuple.getValueNode().getNodeId() == NodeId.mapping ? (MappingNode) nodeTuple.getValueNode() : null; pushNode(nodeTuple.getKeyNode(), mapping); } diff --git a/src/test/java/org/raml/validation/ValidationTestCase.java b/src/test/java/org/raml/validation/ValidationTestCase.java index 2800465c..3572ffa3 100644 --- a/src/test/java/org/raml/validation/ValidationTestCase.java +++ b/src/test/java/org/raml/validation/ValidationTestCase.java @@ -16,6 +16,7 @@ package org.raml.validation; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.junit.Assert.assertThat; import static org.junit.matchers.JUnitMatchers.containsString; @@ -28,11 +29,13 @@ import org.junit.Ignore; import org.junit.Test; +import org.raml.model.ActionType; import org.raml.model.Raml; import org.raml.parser.builder.AbstractRamlTestCase; import org.raml.parser.rule.ValidationResult; import org.raml.parser.tagresolver.ContextPath; import org.raml.parser.visitor.IncludeInfo; +import org.raml.parser.visitor.RamlDocumentBuilder; public class ValidationTestCase extends AbstractRamlTestCase { @@ -222,6 +225,21 @@ public void circularInclude() assertThat(includeInfo.getLine() + 1, is(3)); } + @Test + public void unknownKey() + { + String resource = "org/raml/validation/unknown-key.yaml"; + + // validation reports the unknown key... + List validationResults = validateRaml(resource); + assertThat(validationResults.size(), is(1)); + assertThat(validationResults.get(0).getMessage(), is("Unknown key: unknown")); + + // ... but the parser doesn't choke on it + Raml validContent = new RamlDocumentBuilder().build(resource); + assertThat(validContent.getResource("/partiallyInvalid").getAction(ActionType.POST), is(notNullValue())); + } + @Test public void badMediaTypeName() { diff --git a/src/test/resources/org/raml/validation/unknown-key.yaml b/src/test/resources/org/raml/validation/unknown-key.yaml new file mode 100644 index 00000000..a4499700 --- /dev/null +++ b/src/test/resources/org/raml/validation/unknown-key.yaml @@ -0,0 +1,7 @@ +#%RAML 0.8 +title: unknown key +/partiallyInvalid: + post: + unknown: + body: + application/json: From e812601b279a59c3f4bb2f76a7c502c8d649ac86 Mon Sep 17 00:00:00 2001 From: Tobias Oberlies Date: Tue, 10 Nov 2015 12:02:41 +0100 Subject: [PATCH 2/3] Allow parsing RAML files with model extensions Add constructor so that the RamlDocumentBuilder can also parse custom sub-classes of Raml. In this way, the RAML model can be extended. --- .../parser/visitor/RamlDocumentBuilder.java | 12 ++++- .../parser/visitor/YamlDocumentBuilder.java | 4 +- .../visitor/RamlDocumentBuilderTestCase.java | 52 +++++++++++++++++++ .../org/raml/parser/visitor/extended.yaml | 3 ++ 4 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 src/test/java/org/raml/parser/visitor/RamlDocumentBuilderTestCase.java create mode 100644 src/test/resources/org/raml/parser/visitor/extended.yaml diff --git a/src/main/java/org/raml/parser/visitor/RamlDocumentBuilder.java b/src/main/java/org/raml/parser/visitor/RamlDocumentBuilder.java index 9b73acac..b5a7f9ae 100644 --- a/src/main/java/org/raml/parser/visitor/RamlDocumentBuilder.java +++ b/src/main/java/org/raml/parser/visitor/RamlDocumentBuilder.java @@ -49,9 +49,19 @@ public RamlDocumentBuilder() this(new DefaultResourceLoader()); } + public RamlDocumentBuilder(Class documentClass) + { + this(documentClass, new DefaultResourceLoader()); + } + public RamlDocumentBuilder(ResourceLoader resourceLoader, TagResolver... tagResolvers) { - super(Raml.class, resourceLoader, defaultResolver(tagResolvers)); + this(Raml.class, resourceLoader, tagResolvers); + } + + public RamlDocumentBuilder(Class documentClass, ResourceLoader resourceLoader, TagResolver... tagResolvers) + { + super(documentClass, resourceLoader, defaultResolver(tagResolvers)); } private static TagResolver[] defaultResolver(TagResolver[] tagResolvers) diff --git a/src/main/java/org/raml/parser/visitor/YamlDocumentBuilder.java b/src/main/java/org/raml/parser/visitor/YamlDocumentBuilder.java index 860ac0c1..4b84813f 100644 --- a/src/main/java/org/raml/parser/visitor/YamlDocumentBuilder.java +++ b/src/main/java/org/raml/parser/visitor/YamlDocumentBuilder.java @@ -56,7 +56,7 @@ public class YamlDocumentBuilder implements NodeHandler, ContextPathAware { - private Class documentClass; + private Class documentClass; private T documentObject; private Stack> builderContext = new Stack>(); private Stack documentContext = new Stack(); @@ -65,7 +65,7 @@ public class YamlDocumentBuilder implements NodeHandler, ContextPathAware private TagResolver[] tagResolvers; private ContextPath contextPath; - public YamlDocumentBuilder(Class documentClass, ResourceLoader resourceLoader, TagResolver... tagResolvers) + public YamlDocumentBuilder(Class documentClass, ResourceLoader resourceLoader, TagResolver... tagResolvers) { this.documentClass = documentClass; this.resourceLoader = resourceLoader; diff --git a/src/test/java/org/raml/parser/visitor/RamlDocumentBuilderTestCase.java b/src/test/java/org/raml/parser/visitor/RamlDocumentBuilderTestCase.java new file mode 100644 index 00000000..ebfae1bd --- /dev/null +++ b/src/test/java/org/raml/parser/visitor/RamlDocumentBuilderTestCase.java @@ -0,0 +1,52 @@ +/* + * Copyright 2015 (c) SAP SE + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + */ +package org.raml.parser.visitor; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import org.junit.Test; +import org.raml.model.Raml; +import org.raml.parser.annotation.Scalar; +import org.raml.parser.builder.AbstractRamlTestCase; + +public class RamlDocumentBuilderTestCase extends AbstractRamlTestCase +{ + + @Test + public void parseExtendedModel() + { + RamlExt raml = (RamlExt) new RamlDocumentBuilder(RamlExt.class).build("org/raml/parser/visitor/extended.yaml"); + assertThat(raml.getTitle(), is("extended model")); // standard property + assertThat(raml.getExtension(), is("additional data")); // non-standard property + } + + public static class RamlExt extends Raml + { + private static final long serialVersionUID = 533345138584973337L; + + @Scalar + private String extension; + + public String getExtension() { + return extension; + } + + public void setExtension(String extension) { + this.extension = extension; + } + } +} diff --git a/src/test/resources/org/raml/parser/visitor/extended.yaml b/src/test/resources/org/raml/parser/visitor/extended.yaml new file mode 100644 index 00000000..30b573f4 --- /dev/null +++ b/src/test/resources/org/raml/parser/visitor/extended.yaml @@ -0,0 +1,3 @@ +#%RAML 0.8 +title: extended model +extension: additional data From 44df0e414f09183f72c63986687eb30de619d832 Mon Sep 17 00:00:00 2001 From: Tobias Oberlies Date: Fri, 13 Nov 2015 13:23:00 +0100 Subject: [PATCH 3/3] Also enable model extensions in existing keys Use a LinkedHashMap to ensure that fields from model sub-classes are preferred over fields from base classes. --- .../parser/builder/TupleBuilderFactory.java | 4 +- .../visitor/RamlDocumentBuilderTestCase.java | 49 +++++++++++++++++++ .../org/raml/parser/visitor/extended.yaml | 7 +++ 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/raml/parser/builder/TupleBuilderFactory.java b/src/main/java/org/raml/parser/builder/TupleBuilderFactory.java index 749315d7..7dc767be 100644 --- a/src/main/java/org/raml/parser/builder/TupleBuilderFactory.java +++ b/src/main/java/org/raml/parser/builder/TupleBuilderFactory.java @@ -18,7 +18,7 @@ import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -38,7 +38,7 @@ public class TupleBuilderFactory extends AbstractFactory public void addBuildersTo(Class pojoClass, TupleBuilder parent) { final List declaredFields = ReflectionUtils.getInheritedFields(pojoClass); - final Map> innerBuilders = new HashMap>(); + final Map> innerBuilders = new LinkedHashMap>(); for (Field declaredField : declaredFields) { Scalar scalar = declaredField.getAnnotation(Scalar.class); diff --git a/src/test/java/org/raml/parser/visitor/RamlDocumentBuilderTestCase.java b/src/test/java/org/raml/parser/visitor/RamlDocumentBuilderTestCase.java index ebfae1bd..c323c7ce 100644 --- a/src/test/java/org/raml/parser/visitor/RamlDocumentBuilderTestCase.java +++ b/src/test/java/org/raml/parser/visitor/RamlDocumentBuilderTestCase.java @@ -16,11 +16,19 @@ package org.raml.parser.visitor; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; import static org.junit.Assert.assertThat; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + import org.junit.Test; import org.raml.model.Raml; +import org.raml.model.SecurityScheme; +import org.raml.parser.annotation.Mapping; import org.raml.parser.annotation.Scalar; +import org.raml.parser.annotation.Sequence; import org.raml.parser.builder.AbstractRamlTestCase; public class RamlDocumentBuilderTestCase extends AbstractRamlTestCase @@ -34,6 +42,15 @@ public void parseExtendedModel() assertThat(raml.getExtension(), is("additional data")); // non-standard property } + @Test + public void parseModelWithExtensionInExistingKey() + { + RamlExt2 raml = (RamlExt2) new RamlDocumentBuilder(RamlExt2.class).build("org/raml/parser/visitor/extended.yaml"); + SecuritySchemeExt scheme = raml.getSecuritySchemesExt().get(0).get("extended"); + assertThat(scheme.getDescription(), is(notNullValue())); + assertThat(scheme.getExtension().get("key1"), is("foo")); + } + public static class RamlExt extends Raml { private static final long serialVersionUID = 533345138584973337L; @@ -49,4 +66,36 @@ public void setExtension(String extension) { this.extension = extension; } } + + public static class RamlExt2 extends Raml + { + private static final long serialVersionUID = 1451208177799874616L; + + @Sequence(alias = "securitySchemes") + private List> securitySchemesExt = new ArrayList>(); + + public List> getSecuritySchemesExt() { + return securitySchemesExt; + } + + public void setSecuritySchemesExt(List> securitySchemesExt) { + this.securitySchemesExt = securitySchemesExt; + } + } + + public static class SecuritySchemeExt extends SecurityScheme + { + private static final long serialVersionUID = -7059558387326732177L; + + @Mapping + private Map extension; + + public Map getExtension() { + return extension; + } + + public void setExtension(Map extension) { + this.extension = extension; + } + } } diff --git a/src/test/resources/org/raml/parser/visitor/extended.yaml b/src/test/resources/org/raml/parser/visitor/extended.yaml index 30b573f4..a95f546b 100644 --- a/src/test/resources/org/raml/parser/visitor/extended.yaml +++ b/src/test/resources/org/raml/parser/visitor/extended.yaml @@ -1,3 +1,10 @@ #%RAML 0.8 title: extended model extension: additional data + +securitySchemes: + - extended: + description: Security schema documentation + extension: + key1: foo + key2: bar