diff --git a/src/Microsoft.OpenApi.Readers/Exceptions/OpenApiReaderException.cs b/src/Microsoft.OpenApi.Readers/Exceptions/OpenApiReaderException.cs
index 03f80c93e..e90137ad3 100644
--- a/src/Microsoft.OpenApi.Readers/Exceptions/OpenApiReaderException.cs
+++ b/src/Microsoft.OpenApi.Readers/Exceptions/OpenApiReaderException.cs
@@ -24,6 +24,15 @@ public OpenApiReaderException() { }
/// Plain text error message for this exception.
public OpenApiReaderException(string message) : base(message) { }
+ ///
+ /// Initializes the class with a custom message.
+ ///
+ /// Plain text error message for this exception.
+ /// Context of current parsing process.
+ public OpenApiReaderException(string message, ParsingContext context) : base(message) {
+ Pointer = context.GetLocation();
+ }
+
///
/// Initializes the class with a message and line, column location of error.
///
@@ -42,5 +51,6 @@ public OpenApiReaderException(string message, YamlNode node) : base(message)
/// Plain text error message for this exception.
/// Inner exception that caused this exception to be thrown.
public OpenApiReaderException(string message, Exception innerException) : base(message, innerException) { }
+
}
}
diff --git a/src/Microsoft.OpenApi.Readers/ParseNodes/ListNode.cs b/src/Microsoft.OpenApi.Readers/ParseNodes/ListNode.cs
index e1149cc5a..d11ff4c04 100644
--- a/src/Microsoft.OpenApi.Readers/ParseNodes/ListNode.cs
+++ b/src/Microsoft.OpenApi.Readers/ParseNodes/ListNode.cs
@@ -6,9 +6,7 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Any;
-using Microsoft.OpenApi.Exceptions;
-using Microsoft.OpenApi.Interfaces;
-using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Readers.Exceptions;
using SharpYaml.Serialization;
namespace Microsoft.OpenApi.Readers.ParseNodes
@@ -27,8 +25,8 @@ public override List CreateList(Func map)
{
if (_nodeList == null)
{
- throw new OpenApiException(
- $"Expected list at line {_nodeList.Start.Line} while parsing {typeof(T).Name}");
+ throw new OpenApiReaderException(
+ $"Expected list at line {_nodeList.Start.Line} while parsing {typeof(T).Name}", _nodeList);
}
return _nodeList.Select(n => map(new MapNode(Context, n as YamlMappingNode)))
@@ -47,8 +45,8 @@ public override List CreateSimpleList(Func map)
{
if (_nodeList == null)
{
- throw new OpenApiException(
- $"Expected list at line {_nodeList.Start.Line} while parsing {typeof(T).Name}");
+ throw new OpenApiReaderException(
+ $"Expected list at line {_nodeList.Start.Line} while parsing {typeof(T).Name}", _nodeList);
}
return _nodeList.Select(n => map(new ValueNode(Context, n))).ToList();
diff --git a/src/Microsoft.OpenApi.Readers/ParseNodes/MapNode.cs b/src/Microsoft.OpenApi.Readers/ParseNodes/MapNode.cs
index 26fc81076..f1a9c2cf3 100644
--- a/src/Microsoft.OpenApi.Readers/ParseNodes/MapNode.cs
+++ b/src/Microsoft.OpenApi.Readers/ParseNodes/MapNode.cs
@@ -6,7 +6,6 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Any;
-using Microsoft.OpenApi.Exceptions;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers.Exceptions;
@@ -33,7 +32,7 @@ public MapNode(ParsingContext context, YamlNode node) : base(
{
if (!(node is YamlMappingNode mapNode))
{
- throw new OpenApiReaderException("Expected map.", node);
+ throw new OpenApiReaderException("Expected map.", Context);
}
this._node = mapNode;
@@ -48,10 +47,10 @@ public PropertyNode this[string key]
{
get
{
- YamlNode node = null;
+ YamlNode node;
if (this._node.Children.TryGetValue(new YamlScalarNode(key), out node))
{
- return new PropertyNode(Context, key, this._node.Children[new YamlScalarNode(key)]);
+ return new PropertyNode(Context, key, node);
}
return null;
@@ -63,16 +62,30 @@ public override Dictionary CreateMap(Func map)
var yamlMap = _node;
if (yamlMap == null)
{
- throw new OpenApiException($"Expected map at line {yamlMap.Start.Line} while parsing {typeof(T).Name}");
+ throw new OpenApiReaderException($"Expected map while parsing {typeof(T).Name}", Context);
}
var nodes = yamlMap.Select(
- n => new
- {
- key = n.Key.GetScalarValue(),
- value = n.Value as YamlMappingNode == null
- ? default(T)
- : map(new MapNode(Context, n.Value as YamlMappingNode))
+ n => {
+
+ var key = n.Key.GetScalarValue();
+ T value;
+ try
+ {
+ Context.StartObject(key);
+ value = n.Value as YamlMappingNode == null
+ ? default(T)
+ : map(new MapNode(Context, n.Value as YamlMappingNode));
+ }
+ finally
+ {
+ Context.EndObject();
+ }
+ return new
+ {
+ key = key,
+ value = value
+ };
});
return nodes.ToDictionary(k => k.key, v => v.value);
@@ -85,7 +98,7 @@ public override Dictionary CreateMapWithReference(
var yamlMap = _node;
if (yamlMap == null)
{
- throw new OpenApiException($"Expected map at line {yamlMap.Start.Line} while parsing {typeof(T).Name}");
+ throw new OpenApiReaderException($"Expected map while parsing {typeof(T).Name}", Context);
}
var nodes = yamlMap.Select(
@@ -119,8 +132,8 @@ public override Dictionary CreateSimpleMap(Func map)
{
var yamlMap = _node;
if (yamlMap == null)
- {
- throw new OpenApiException($"Expected map at line {yamlMap.Start.Line} while parsing {typeof(T).Name}");
+ {
+ throw new OpenApiReaderException($"Expected map while parsing {typeof(T).Name}", Context);
}
var nodes = yamlMap.Select(
@@ -175,7 +188,7 @@ public string GetScalarValue(ValueNode key)
var scalarNode = _node.Children[new YamlScalarNode(key.GetScalarValue())] as YamlScalarNode;
if (scalarNode == null)
{
- throw new OpenApiException($"Expected scalar at line {_node.Start.Line} for key {key.GetScalarValue()}");
+ throw new OpenApiReaderException($"Expected scalar at line {_node.Start.Line} for key {key.GetScalarValue()}", Context);
}
return scalarNode.Value;
diff --git a/src/Microsoft.OpenApi.Readers/ParseNodes/ParseNode.cs b/src/Microsoft.OpenApi.Readers/ParseNodes/ParseNode.cs
index 25f0eabc1..295b02bf3 100644
--- a/src/Microsoft.OpenApi.Readers/ParseNodes/ParseNode.cs
+++ b/src/Microsoft.OpenApi.Readers/ParseNodes/ParseNode.cs
@@ -26,7 +26,7 @@ public MapNode CheckMapNode(string nodeName)
{
if (!(this is MapNode mapNode))
{
- throw new OpenApiReaderException($"{nodeName} must be a map/object");
+ throw new OpenApiReaderException($"{nodeName} must be a map/object", Context);
}
return mapNode;
@@ -50,12 +50,12 @@ public static ParseNode Create(ParsingContext context, YamlNode node)
public virtual List CreateList(Func map)
{
- throw new OpenApiReaderException("Cannot create list from this type of node.");
+ throw new OpenApiReaderException("Cannot create list from this type of node.", Context);
}
public virtual Dictionary CreateMap(Func map)
{
- throw new OpenApiReaderException("Cannot create map from this type of node.");
+ throw new OpenApiReaderException("Cannot create map from this type of node.", Context);
}
public virtual Dictionary CreateMapWithReference(
@@ -63,38 +63,37 @@ public virtual Dictionary CreateMapWithReference(
Func map)
where T : class, IOpenApiReferenceable
{
- throw new OpenApiReaderException("Cannot create map from this reference.");
+ throw new OpenApiReaderException("Cannot create map from this reference.", Context);
}
public virtual List CreateSimpleList(Func map)
{
- throw new OpenApiReaderException("Cannot create simple list from this type of node.");
+ throw new OpenApiReaderException("Cannot create simple list from this type of node.", Context);
}
public virtual Dictionary CreateSimpleMap(Func map)
{
- throw new OpenApiReaderException("Cannot create simple map from this type of node.");
+ throw new OpenApiReaderException("Cannot create simple map from this type of node.", Context);
}
public virtual IOpenApiAny CreateAny()
{
- throw new OpenApiReaderException("Cannot create an Any object this type of node.");
+ throw new OpenApiReaderException("Cannot create an Any object this type of node.", Context);
}
public virtual string GetRaw()
{
- throw new OpenApiReaderException("Cannot get raw value from this type of node.");
+ throw new OpenApiReaderException("Cannot get raw value from this type of node.", Context);
}
public virtual string GetScalarValue()
{
- throw new OpenApiReaderException("Cannot create a scalar value from this type of node.");
+ throw new OpenApiReaderException("Cannot create a scalar value from this type of node.", Context);
}
public virtual List CreateListOfAny()
{
- throw new OpenApiReaderException("Cannot create a list from this type of node.");
+ throw new OpenApiReaderException("Cannot create a list from this type of node.", Context);
}
-
}
}
diff --git a/src/Microsoft.OpenApi.Readers/ParsingContext.cs b/src/Microsoft.OpenApi.Readers/ParsingContext.cs
index 9c7277136..b7cfb6acb 100644
--- a/src/Microsoft.OpenApi.Readers/ParsingContext.cs
+++ b/src/Microsoft.OpenApi.Readers/ParsingContext.cs
@@ -141,7 +141,7 @@ public void EndObject()
///
public string GetLocation()
{
- return "#/" + string.Join("/", _currentLocation.Reverse().ToArray());
+ return "#/" + string.Join("/", _currentLocation.Reverse().Select(s=> s.Replace("~","~0").Replace("/","~1")).ToArray());
}
///
diff --git a/src/Microsoft.OpenApi/Exceptions/OpenApiException.cs b/src/Microsoft.OpenApi/Exceptions/OpenApiException.cs
index 410e0dd78..4d1ca4f62 100644
--- a/src/Microsoft.OpenApi/Exceptions/OpenApiException.cs
+++ b/src/Microsoft.OpenApi/Exceptions/OpenApiException.cs
@@ -39,8 +39,14 @@ public OpenApiException(string message, Exception innerException)
}
///
- /// The reference pointer.
+ /// The reference pointer. This is a fragment identifier used to point to where the error occurred in the document.
+ /// If the document has been parsed as JSON/YAML then the identifier will be a
+ /// JSON Pointer as per https://tools.ietf.org/html/rfc6901
+ /// If the document fails to parse as JSON/YAML then the fragment will be based on
+ /// a text/plain pointer as defined in https://tools.ietf.org/html/rfc5147
+ /// Currently only line= is provided because using char= causes tests to break due to CR/LF & LF differences
///
public string Pointer { get; set; }
+
}
}
diff --git a/test/Microsoft.OpenApi.Readers.Tests/ParseNodeTests.cs b/test/Microsoft.OpenApi.Readers.Tests/ParseNodeTests.cs
index 04a400b40..677232ac4 100644
--- a/test/Microsoft.OpenApi.Readers.Tests/ParseNodeTests.cs
+++ b/test/Microsoft.OpenApi.Readers.Tests/ParseNodeTests.cs
@@ -31,5 +31,34 @@ public void BrokenSimpleList()
})
});
}
+
+ [Fact]
+ public void BadSchema()
+ {
+ var input = @"openapi: 3.0.0
+info:
+ title: foo
+ version: bar
+paths:
+ '/foo':
+ get:
+ responses:
+ 200:
+ description: ok
+ content:
+ application/json:
+ schema: asdasd
+";
+
+ var reader = new OpenApiStringReader();
+ reader.Read(input, out var diagnostic);
+
+ diagnostic.Errors.Should().BeEquivalentTo(new List() {
+ new OpenApiError(new OpenApiReaderException("schema must be a map/object") {
+ Pointer = "#/paths/~1foo/get/responses/200/content/application~1json/schema"
+ })
+ });
+ }
}
}
+