Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/Microsoft.OpenApi.Readers/Exceptions/OpenApiReaderException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ public OpenApiReaderException() { }
/// <param name="message">Plain text error message for this exception.</param>
public OpenApiReaderException(string message) : base(message) { }

/// <summary>
/// Initializes the <see cref="OpenApiReaderException"/> class with a custom message.
/// </summary>
/// <param name="message">Plain text error message for this exception.</param>
/// <param name="context">Context of current parsing process.</param>
public OpenApiReaderException(string message, ParsingContext context) : base(message) {
Pointer = context.GetLocation();
}

/// <summary>
/// Initializes the <see cref="OpenApiReaderException"/> class with a message and line, column location of error.
/// </summary>
Expand All @@ -42,5 +51,6 @@ public OpenApiReaderException(string message, YamlNode node) : base(message)
/// <param name="message">Plain text error message for this exception.</param>
/// <param name="innerException">Inner exception that caused this exception to be thrown.</param>
public OpenApiReaderException(string message, Exception innerException) : base(message, innerException) { }

}
}
12 changes: 5 additions & 7 deletions src/Microsoft.OpenApi.Readers/ParseNodes/ListNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -27,8 +25,8 @@ public override List<T> CreateList<T>(Func<MapNode, T> 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)))
Expand All @@ -47,8 +45,8 @@ public override List<T> CreateSimpleList<T>(Func<ValueNode, T> 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();
Expand Down
43 changes: 28 additions & 15 deletions src/Microsoft.OpenApi.Readers/ParseNodes/MapNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -63,16 +62,30 @@ public override Dictionary<string, T> CreateMap<T>(Func<MapNode, T> 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);
Expand All @@ -85,7 +98,7 @@ public override Dictionary<string, T> CreateMapWithReference<T>(
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(
Expand Down Expand Up @@ -119,8 +132,8 @@ public override Dictionary<string, T> CreateSimpleMap<T>(Func<ValueNode, T> 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(
Expand Down Expand Up @@ -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;
Expand Down
21 changes: 10 additions & 11 deletions src/Microsoft.OpenApi.Readers/ParseNodes/ParseNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -50,51 +50,50 @@ public static ParseNode Create(ParsingContext context, YamlNode node)

public virtual List<T> CreateList<T>(Func<MapNode, T> 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<string, T> CreateMap<T>(Func<MapNode, T> 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<string, T> CreateMapWithReference<T>(
ReferenceType referenceType,
Func<MapNode, T> 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<T> CreateSimpleList<T>(Func<ValueNode, T> 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<string, T> CreateSimpleMap<T>(Func<ValueNode, T> 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<IOpenApiAny> 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);
}

}
}
2 changes: 1 addition & 1 deletion src/Microsoft.OpenApi.Readers/ParsingContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ public void EndObject()
/// </summary>
public string GetLocation()
{
return "#/" + string.Join("/", _currentLocation.Reverse().ToArray());
return "#/" + string.Join("/", _currentLocation.Reverse().Select(s=> s.Replace("~","~0").Replace("/","~1")).ToArray());
}

/// <summary>
Expand Down
8 changes: 7 additions & 1 deletion src/Microsoft.OpenApi/Exceptions/OpenApiException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,14 @@ public OpenApiException(string message, Exception innerException)
}

/// <summary>
/// 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
/// </summary>
public string Pointer { get; set; }

}
}
29 changes: 29 additions & 0 deletions test/Microsoft.OpenApi.Readers.Tests/ParseNodeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<OpenApiError>() {
new OpenApiError(new OpenApiReaderException("schema must be a map/object") {
Pointer = "#/paths/~1foo/get/responses/200/content/application~1json/schema"
})
});
}
}
}