diff --git a/build.cmd b/build.cmd
index d0d68b5d7..0657b8e1e 100644
--- a/build.cmd
+++ b/build.cmd
@@ -1,7 +1,7 @@
@echo off
if "%~1"=="" goto :error
-SET %VERSION%=%~1
+SET VERSION=%~1
Echo Building Microsoft.OpenApi
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
new file mode 100644
index 000000000..adb086cc1
--- /dev/null
+++ b/src/Directory.Build.props
@@ -0,0 +1,8 @@
+
+
+ $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Microsoft.OpenApi.Readers/Exceptions/OpenApiReaderException.cs b/src/Microsoft.OpenApi.Readers/Exceptions/OpenApiReaderException.cs
index 25aac4978..ee829d6b3 100644
--- a/src/Microsoft.OpenApi.Readers/Exceptions/OpenApiReaderException.cs
+++ b/src/Microsoft.OpenApi.Readers/Exceptions/OpenApiReaderException.cs
@@ -2,6 +2,8 @@
// Licensed under the MIT license.
using System;
+using Microsoft.OpenApi.Exceptions;
+using SharpYaml.Serialization;
namespace Microsoft.OpenApi.Readers.Exceptions
{
@@ -9,7 +11,7 @@ namespace Microsoft.OpenApi.Readers.Exceptions
/// Defines an exception indicating OpenAPI Reader encountered an issue while reading.
///
[Serializable]
- public class OpenApiReaderException : Exception
+ public class OpenApiReaderException : OpenApiException
{
///
/// Initializes the class.
@@ -22,6 +24,18 @@ public OpenApiReaderException() { }
/// Plain text error message for this exception.
public OpenApiReaderException(string message) : base(message) { }
+ ///
+ /// Initializes the class with a message and line, column location of error.
+ ///
+ /// Plain text error message for this exception.
+ /// Parsing node where error occured
+ public OpenApiReaderException(string message, YamlNode node) : base(message)
+ {
+ // This only includes line because using a char range causes tests to break due to CR/LF & LF differences
+ // See https://tools.ietf.org/html/rfc5147 for syntax
+ Pointer = $"#line={node.Start.Line}";
+ }
+
///
/// Initializes the class with a custom message and inner exception.
///
diff --git a/src/Microsoft.OpenApi.Readers/Exceptions/OpenApiUnsupportedSpecVersionException.cs b/src/Microsoft.OpenApi.Readers/Exceptions/OpenApiUnsupportedSpecVersionException.cs
index 84faf9d4e..199020784 100644
--- a/src/Microsoft.OpenApi.Readers/Exceptions/OpenApiUnsupportedSpecVersionException.cs
+++ b/src/Microsoft.OpenApi.Readers/Exceptions/OpenApiUnsupportedSpecVersionException.cs
@@ -10,9 +10,9 @@ namespace Microsoft.OpenApi.Readers.Exceptions
/// Defines an exception indicating OpenAPI Reader encountered an unsupported specification version while reading.
///
[Serializable]
- public class OpenApiUnsupportedSpecVersionException : OpenApiReaderException
+ public class OpenApiUnsupportedSpecVersionException : Exception
{
- const string messagePattern = "OpenAPI specification version {0} is not supported.";
+ const string messagePattern = "OpenAPI specification version '{0}' is not supported.";
///
/// Initializes the class with a specification version.
@@ -21,11 +21,6 @@ public class OpenApiUnsupportedSpecVersionException : OpenApiReaderException
public OpenApiUnsupportedSpecVersionException(string specificationVersion)
: base(string.Format(CultureInfo.InvariantCulture, messagePattern, specificationVersion))
{
- if (string.IsNullOrWhiteSpace(specificationVersion))
- {
- throw new ArgumentException("Value cannot be null or white space.", nameof(specificationVersion));
- }
-
this.SpecificationVersion = specificationVersion;
}
@@ -38,11 +33,6 @@ public OpenApiUnsupportedSpecVersionException(string specificationVersion)
public OpenApiUnsupportedSpecVersionException(string specificationVersion, Exception innerException)
: base(string.Format(CultureInfo.InvariantCulture, messagePattern, specificationVersion), innerException)
{
- if (string.IsNullOrWhiteSpace(specificationVersion))
- {
- throw new ArgumentException("Value cannot be null or white space.", nameof(specificationVersion));
- }
-
this.SpecificationVersion = specificationVersion;
}
diff --git a/src/Microsoft.OpenApi.Readers/Interface/IOpenApiVersionService.cs b/src/Microsoft.OpenApi.Readers/Interface/IOpenApiVersionService.cs
index 80ac9d28f..32dd420f4 100644
--- a/src/Microsoft.OpenApi.Readers/Interface/IOpenApiVersionService.cs
+++ b/src/Microsoft.OpenApi.Readers/Interface/IOpenApiVersionService.cs
@@ -23,9 +23,12 @@ internal interface IOpenApiVersionService
OpenApiReference ConvertToOpenApiReference(string reference, ReferenceType? type);
///
- /// Function that converts a MapNode into a Tag object in a version specific way
+ /// Loads an OpenAPI Element from a document fragment
///
- Func TagLoader { get; }
+ /// Type of element to load
+ /// document fragment node
+ /// Instance of OpenAPIElement
+ T LoadElement(ParseNode node) where T : IOpenApiElement;
///
/// Converts a generic RootNode instance into a strongly typed OpenApiDocument
diff --git a/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj b/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj
index 26091ae22..6fa86c0d7 100644
--- a/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj
+++ b/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj
@@ -10,7 +10,7 @@
Microsoft
Microsoft.OpenApi.Readers
Microsoft.OpenApi.Readers
- 1.0.1
+ 1.1.0
OpenAPI.NET Readers for JSON and YAML documents
© Microsoft Corporation. All rights reserved.
OpenAPI .NET
diff --git a/src/Microsoft.OpenApi.Readers/OpenApiReaderError.cs b/src/Microsoft.OpenApi.Readers/OpenApiReaderError.cs
deleted file mode 100644
index b06a01f31..000000000
--- a/src/Microsoft.OpenApi.Readers/OpenApiReaderError.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT license.
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Microsoft.OpenApi.Exceptions;
-using Microsoft.OpenApi.Models;
-using SharpYaml;
-
-namespace Microsoft.OpenApi.Readers
-{
- ///
- /// Error detected during the reading of some input and converting to an OpenApiDocument
- ///
- public class OpenApiReaderError : OpenApiError
- {
-
- ///
- /// Creates error object from thrown exception
- ///
- ///
- public OpenApiReaderError(OpenApiException exception) : base(exception)
- {
-
- }
-
- ///
- /// Create error object from YAML SyntaxErrorException
- ///
- ///
- public OpenApiReaderError(SyntaxErrorException exception) : base(String.Empty, exception.Message)
- {
-
- }
- }
-}
diff --git a/src/Microsoft.OpenApi.Readers/OpenApiReaderSettings.cs b/src/Microsoft.OpenApi.Readers/OpenApiReaderSettings.cs
index 544fce2fd..1b1c2f367 100644
--- a/src/Microsoft.OpenApi.Readers/OpenApiReaderSettings.cs
+++ b/src/Microsoft.OpenApi.Readers/OpenApiReaderSettings.cs
@@ -42,12 +42,16 @@ public class OpenApiReaderSettings
///
/// Dictionary of parsers for converting extensions into strongly typed classes
///
- public Dictionary> ExtensionParsers { get; set; } = new Dictionary>();
+ public Dictionary> ExtensionParsers { get; set; } = new Dictionary>();
///
/// Rules to use for validating OpenAPI specification. If none are provided a default set of rules are applied.
///
public ValidationRuleSet RuleSet { get; set; } = ValidationRuleSet.GetDefaultRuleSet();
+ ///
+ /// URL where relative references should be resolved from if the description does not contain Server definitions
+ ///
+ public Uri BaseUrl { get; set; }
}
}
diff --git a/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs b/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs
index 3aa1057fc..1e0c08695 100644
--- a/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs
+++ b/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs
@@ -6,6 +6,7 @@
using System.Linq;
using Microsoft.OpenApi.Exceptions;
using Microsoft.OpenApi.Extensions;
+using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers.Interface;
using Microsoft.OpenApi.Readers.Services;
@@ -48,15 +49,16 @@ public OpenApiDocument Read(Stream input, out OpenApiDiagnostic diagnostic)
{
yamlDocument = LoadYamlDocument(input);
}
- catch (SyntaxErrorException ex)
+ catch (YamlException ex)
{
- diagnostic.Errors.Add(new OpenApiReaderError(ex));
+ diagnostic.Errors.Add(new OpenApiError($"#char={ex.Start.Line}", ex.Message));
return new OpenApiDocument();
}
context = new ParsingContext
{
- ExtensionParsers = _settings.ExtensionParsers
+ ExtensionParsers = _settings.ExtensionParsers,
+ BaseUrl = _settings.BaseUrl
};
OpenApiDocument document = null;
@@ -102,6 +104,60 @@ public OpenApiDocument Read(Stream input, out OpenApiDiagnostic diagnostic)
return document;
}
+ ///
+ /// Reads the stream input and parses the fragment of an OpenAPI description into an Open API Element.
+ ///
+ /// Stream containing OpenAPI description to parse.
+ /// Version of the OpenAPI specification that the fragment conforms to.
+ /// Returns diagnostic object containing errors detected during parsing
+ /// Instance of newly created OpenApiDocument
+ public T ReadFragment(Stream input, OpenApiSpecVersion version, out OpenApiDiagnostic diagnostic) where T : IOpenApiElement
+ {
+ ParsingContext context;
+ YamlDocument yamlDocument;
+ diagnostic = new OpenApiDiagnostic();
+
+ // Parse the YAML/JSON
+ try
+ {
+ yamlDocument = LoadYamlDocument(input);
+ }
+ catch (YamlException ex)
+ {
+ diagnostic.Errors.Add(new OpenApiError($"#line={ex.Start.Line}", ex.Message));
+ return default(T);
+ }
+
+ context = new ParsingContext
+ {
+ ExtensionParsers = _settings.ExtensionParsers
+ };
+
+ IOpenApiElement element = null;
+
+ try
+ {
+ // Parse the OpenAPI element
+ element = context.ParseFragment(yamlDocument, version, diagnostic);
+ }
+ catch (OpenApiException ex)
+ {
+ diagnostic.Errors.Add(new OpenApiError(ex));
+ }
+
+ // Validate the element
+ if (_settings.RuleSet != null && _settings.RuleSet.Rules.Count > 0)
+ {
+ var errors = element.Validate(_settings.RuleSet);
+ foreach (var item in errors)
+ {
+ diagnostic.Errors.Add(item);
+ }
+ }
+
+ return (T)element;
+ }
+
///
/// Helper method to turn streams into YamlDocument
///
diff --git a/src/Microsoft.OpenApi.Readers/OpenApiStringReader.cs b/src/Microsoft.OpenApi.Readers/OpenApiStringReader.cs
index 8530ca467..82b3a3ce7 100644
--- a/src/Microsoft.OpenApi.Readers/OpenApiStringReader.cs
+++ b/src/Microsoft.OpenApi.Readers/OpenApiStringReader.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using System.IO;
+using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers.Interface;
@@ -20,7 +21,7 @@ public class OpenApiStringReader : IOpenApiReader
///
public OpenApiStringReader(OpenApiReaderSettings settings = null)
{
- _settings = settings ?? new OpenApiReaderSettings();
+ _settings = settings ?? new OpenApiReaderSettings();
}
///
@@ -38,5 +39,21 @@ public OpenApiDocument Read(string input, out OpenApiDiagnostic diagnostic)
return new OpenApiStreamReader(_settings).Read(memoryStream, out diagnostic);
}
}
+
+ ///
+ /// Reads the string input and parses it into an Open API element.
+ ///
+ public T ReadFragment(string input, OpenApiSpecVersion version, out OpenApiDiagnostic diagnostic) where T : IOpenApiElement
+ {
+ using (var memoryStream = new MemoryStream())
+ {
+ var writer = new StreamWriter(memoryStream);
+ writer.Write(input);
+ writer.Flush();
+ memoryStream.Position = 0;
+
+ return new OpenApiStreamReader(_settings).ReadFragment(memoryStream, version, out diagnostic);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/Microsoft.OpenApi.Readers/ParseNodes/ListNode.cs b/src/Microsoft.OpenApi.Readers/ParseNodes/ListNode.cs
index f7630c617..ee3553ad4 100644
--- a/src/Microsoft.OpenApi.Readers/ParseNodes/ListNode.cs
+++ b/src/Microsoft.OpenApi.Readers/ParseNodes/ListNode.cs
@@ -52,7 +52,7 @@ public override List CreateSimpleList(Func map)
$"Expected list at line {_nodeList.Start.Line} while parsing {typeof(T).Name}");
}
- return _nodeList.Select(n => map(new ValueNode(Context, Diagnostic, (YamlScalarNode)n))).ToList();
+ return _nodeList.Select(n => map(new ValueNode(Context, Diagnostic, n))).ToList();
}
public IEnumerator GetEnumerator()
diff --git a/src/Microsoft.OpenApi.Readers/ParseNodes/MapNode.cs b/src/Microsoft.OpenApi.Readers/ParseNodes/MapNode.cs
index ef18cf11a..95aa4ff62 100644
--- a/src/Microsoft.OpenApi.Readers/ParseNodes/MapNode.cs
+++ b/src/Microsoft.OpenApi.Readers/ParseNodes/MapNode.cs
@@ -9,6 +9,7 @@
using Microsoft.OpenApi.Exceptions;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Readers.Exceptions;
using SharpYaml.Schemas;
using SharpYaml.Serialization;
@@ -27,16 +28,16 @@ public MapNode(ParsingContext context, OpenApiDiagnostic diagnostic, string yaml
{
}
- public MapNode(ParsingContext context, OpenApiDiagnostic diagnostic, YamlMappingNode node) : base(
+ public MapNode(ParsingContext context, OpenApiDiagnostic diagnostic, YamlNode node) : base(
context,
diagnostic)
{
- if (node == null)
+ if (!(node is YamlMappingNode mapNode))
{
- throw new OpenApiException("Expected map");
+ throw new OpenApiReaderException("Expected map.", node);
}
- this._node = node;
+ this._node = mapNode;
_nodes = this._node.Children
.Select(kvp => new PropertyNode(Context, Diagnostic, kvp.Key.GetScalarValue(), kvp.Value))
diff --git a/src/Microsoft.OpenApi.Readers/ParseNodes/ParseNode.cs b/src/Microsoft.OpenApi.Readers/ParseNodes/ParseNode.cs
index a80176317..abeee3d26 100644
--- a/src/Microsoft.OpenApi.Readers/ParseNodes/ParseNode.cs
+++ b/src/Microsoft.OpenApi.Readers/ParseNodes/ParseNode.cs
@@ -8,6 +8,7 @@
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
@@ -26,10 +27,9 @@ protected ParseNode(ParsingContext parsingContext, OpenApiDiagnostic diagnostic)
public MapNode CheckMapNode(string nodeName)
{
- var mapNode = this as MapNode;
- if (mapNode == null)
+ if (!(this is MapNode mapNode))
{
- throw new OpenApiException($"{nodeName} must be a map/object");
+ throw new OpenApiReaderException($"{nodeName} must be a map/object");
}
return mapNode;
@@ -37,15 +37,13 @@ public MapNode CheckMapNode(string nodeName)
public static ParseNode Create(ParsingContext context, OpenApiDiagnostic diagnostic, YamlNode node)
{
- var listNode = node as YamlSequenceNode;
- if (listNode != null)
+ if (node is YamlSequenceNode listNode)
{
return new ListNode(context, diagnostic, listNode);
}
- var mapNode = node as YamlMappingNode;
- if (mapNode != null)
+ if (node is YamlMappingNode mapNode)
{
return new MapNode(context, diagnostic, mapNode);
}
@@ -55,12 +53,12 @@ public static ParseNode Create(ParsingContext context, OpenApiDiagnostic diagnos
public virtual List CreateList(Func map)
{
- throw new OpenApiException("Cannot create list from this type of node.");
+ throw new OpenApiReaderException("Cannot create list from this type of node.");
}
public virtual Dictionary CreateMap(Func map)
{
- throw new OpenApiException("Cannot create map from this type of node.");
+ throw new OpenApiReaderException("Cannot create map from this type of node.");
}
public virtual Dictionary CreateMapWithReference(
@@ -68,37 +66,37 @@ public virtual Dictionary CreateMapWithReference(
Func map)
where T : class, IOpenApiReferenceable
{
- throw new OpenApiException("Cannot create map from this reference.");
+ throw new OpenApiReaderException("Cannot create map from this reference.");
}
public virtual List CreateSimpleList(Func map)
{
- throw new OpenApiException("Cannot create simple list from this type of node.");
+ throw new OpenApiReaderException("Cannot create simple list from this type of node.");
}
public virtual Dictionary CreateSimpleMap(Func map)
{
- throw new OpenApiException("Cannot create simple map from this type of node.");
+ throw new OpenApiReaderException("Cannot create simple map from this type of node.");
}
public virtual IOpenApiAny CreateAny()
{
- throw new OpenApiException("Cannot create an Any object this type of node.");
+ throw new OpenApiReaderException("Cannot create an Any object this type of node.");
}
public virtual string GetRaw()
{
- throw new OpenApiException("Cannot get raw value from this type of node.");
+ throw new OpenApiReaderException("Cannot get raw value from this type of node.");
}
public virtual string GetScalarValue()
{
- throw new OpenApiException("Cannot create a scalar value from this type of node.");
+ throw new OpenApiReaderException("Cannot create a scalar value from this type of node.");
}
public virtual List CreateListOfAny()
{
- throw new OpenApiException("Cannot create a list from this type of node.");
+ throw new OpenApiReaderException("Cannot create a list from this type of node.");
}
}
diff --git a/src/Microsoft.OpenApi.Readers/ParseNodes/PropertyNode.cs b/src/Microsoft.OpenApi.Readers/ParseNodes/PropertyNode.cs
index 1f4c9adcb..39b9370f8 100644
--- a/src/Microsoft.OpenApi.Readers/ParseNodes/PropertyNode.cs
+++ b/src/Microsoft.OpenApi.Readers/ParseNodes/PropertyNode.cs
@@ -7,6 +7,7 @@
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Exceptions;
using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Readers.Exceptions;
using SharpYaml.Serialization;
namespace Microsoft.OpenApi.Readers.ParseNodes
@@ -40,10 +41,14 @@ public void ParseField(
Context.StartObject(Name);
fixedFieldMap(parentInstance, Value);
}
+ catch (OpenApiReaderException ex)
+ {
+ Diagnostic.Errors.Add(new OpenApiError(ex));
+ }
catch (OpenApiException ex)
{
ex.Pointer = Context.GetLocation();
- Diagnostic.Errors.Add(new OpenApiReaderError(ex));
+ Diagnostic.Errors.Add(new OpenApiError(ex));
}
finally
{
@@ -60,10 +65,14 @@ public void ParseField(
Context.StartObject(Name);
map(parentInstance, Name, Value);
}
+ catch (OpenApiReaderException ex)
+ {
+ Diagnostic.Errors.Add(new OpenApiError(ex));
+ }
catch (OpenApiException ex)
{
ex.Pointer = Context.GetLocation();
- Diagnostic.Errors.Add(new OpenApiReaderError(ex));
+ Diagnostic.Errors.Add(new OpenApiError(ex));
}
finally
{
diff --git a/src/Microsoft.OpenApi.Readers/ParseNodes/ValueNode.cs b/src/Microsoft.OpenApi.Readers/ParseNodes/ValueNode.cs
index 44d23d856..f17b49f22 100644
--- a/src/Microsoft.OpenApi.Readers/ParseNodes/ValueNode.cs
+++ b/src/Microsoft.OpenApi.Readers/ParseNodes/ValueNode.cs
@@ -4,6 +4,7 @@
using System;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Exceptions;
+using Microsoft.OpenApi.Readers.Exceptions;
using SharpYaml.Serialization;
namespace Microsoft.OpenApi.Readers.ParseNodes
@@ -12,23 +13,20 @@ internal class ValueNode : ParseNode
{
private readonly YamlScalarNode _node;
- public ValueNode(ParsingContext context, OpenApiDiagnostic diagnostic, YamlScalarNode scalarNode) : base(
+ public ValueNode(ParsingContext context, OpenApiDiagnostic diagnostic, YamlNode node) : base(
context,
diagnostic)
{
+ if (!(node is YamlScalarNode scalarNode))
+ {
+ throw new OpenApiReaderException("Expected a value.", node);
+ }
_node = scalarNode;
}
public override string GetScalarValue()
{
- var scalarNode = _node;
-
- if (scalarNode == null)
- {
- throw new OpenApiException($"Expected scalar at line {_node.Start.Line}");
- }
-
- return scalarNode.Value;
+ return _node.Value;
}
///
diff --git a/src/Microsoft.OpenApi.Readers/ParsingContext.cs b/src/Microsoft.OpenApi.Readers/ParsingContext.cs
index e58ebde04..f693bfddc 100644
--- a/src/Microsoft.OpenApi.Readers/ParsingContext.cs
+++ b/src/Microsoft.OpenApi.Readers/ParsingContext.cs
@@ -25,15 +25,16 @@ public class ParsingContext
private readonly Dictionary _tempStorage = new Dictionary();
private IOpenApiVersionService _versionService;
private readonly Dictionary> _loopStacks = new Dictionary>();
- internal Dictionary> ExtensionParsers { get; set; } = new Dictionary>();
+ internal Dictionary> ExtensionParsers { get; set; } = new Dictionary>();
internal RootNode RootNode { get; set; }
internal List Tags { get; private set; } = new List();
+ internal Uri BaseUrl { get; set; }
///
/// Initiates the parsing process. Not thread safe and should only be called once on a parsing context
///
- ///
- ///
+ /// Yaml document to parse.
+ /// Diagnostic object which will return diagnostic results of the operation.
/// An OpenApiDocument populated based on the passed yamlDocument
internal OpenApiDocument Parse(YamlDocument yamlDocument, OpenApiDiagnostic diagnostic)
{
@@ -47,13 +48,13 @@ internal OpenApiDocument Parse(YamlDocument yamlDocument, OpenApiDiagnostic diag
{
case string version when version == "2.0":
VersionService = new OpenApiV2VersionService();
- doc = this.VersionService.LoadDocument(this.RootNode);
+ doc = VersionService.LoadDocument(RootNode);
diagnostic.SpecificationVersion = OpenApiSpecVersion.OpenApi2_0;
break;
case string version when version.StartsWith("3.0"):
- this.VersionService = new OpenApiV3VersionService();
- doc = this.VersionService.LoadDocument(this.RootNode);
+ VersionService = new OpenApiV3VersionService();
+ doc = VersionService.LoadDocument(RootNode);
diagnostic.SpecificationVersion = OpenApiSpecVersion.OpenApi3_0;
break;
@@ -64,6 +65,34 @@ internal OpenApiDocument Parse(YamlDocument yamlDocument, OpenApiDiagnostic diag
return doc;
}
+ ///
+ /// Initiates the parsing process of a fragment. Not thread safe and should only be called once on a parsing context
+ ///
+ ///
+ /// OpenAPI version of the fragment
+ /// Diagnostic object which will return diagnostic results of the operation.
+ /// An OpenApiDocument populated based on the passed yamlDocument
+ internal T ParseFragment(YamlDocument yamlDocument, OpenApiSpecVersion version, OpenApiDiagnostic diagnostic) where T: IOpenApiElement
+ {
+ var node = ParseNode.Create(this, diagnostic, yamlDocument.RootNode);
+
+ T element = default(T);
+
+ switch (version)
+ {
+ case OpenApiSpecVersion.OpenApi2_0:
+ VersionService = new OpenApiV2VersionService();
+ element = this.VersionService.LoadElement(node);
+ break;
+
+ case OpenApiSpecVersion.OpenApi3_0:
+ this.VersionService = new OpenApiV3VersionService();
+ element = this.VersionService.LoadElement(node);
+ break;
+ }
+
+ return element;
+ }
///
/// Gets the version of the Open API document.
@@ -82,20 +111,6 @@ private static string GetVersion(RootNode rootNode)
return versionNode?.GetScalarValue();
}
- private void ComputeTags(List tags, Func loadTag)
- {
- // Precompute the tags array so that each tag reference does not require a new deserialization.
- var tagListPointer = new JsonPointer("#/tags");
-
- var tagListNode = RootNode.Find(tagListPointer);
-
- if (tagListNode != null && tagListNode is ListNode)
- {
- var tagListNodeAsListNode = (ListNode)tagListNode;
- tags.AddRange(tagListNodeAsListNode.CreateList(loadTag));
- }
- }
-
///
/// Service providing all Version specific conversion functions
///
@@ -108,7 +123,6 @@ internal IOpenApiVersionService VersionService
set
{
_versionService = value;
- ComputeTags(Tags, VersionService.TagLoader);
}
}
diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiContactDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiContactDeserializer.cs
index 3bcddffc0..2469c887f 100644
--- a/src/Microsoft.OpenApi.Readers/V2/OpenApiContactDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiContactDeserializer.cs
@@ -38,7 +38,7 @@ internal static partial class OpenApiV2Deserializer
private static PatternFieldMap _contactPatternFields = new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n))}
};
public static OpenApiContact LoadContact(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiDocumentDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiDocumentDeserializer.cs
index 559f325ce..525a3f52f 100644
--- a/src/Microsoft.OpenApi.Readers/V2/OpenApiDocumentDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiDocumentDeserializer.cs
@@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Extensions;
+using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers.ParseNodes;
using Microsoft.OpenApi.Services;
@@ -116,24 +117,96 @@ internal static partial class OpenApiV2Deserializer
private static PatternFieldMap _openApiPatternFields = new PatternFieldMap
{
// We have no semantics to verify X- nodes, therefore treat them as just values.
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n))}
};
- private static void MakeServers(IList servers, ParsingContext context)
+ private static void MakeServers(IList servers, ParsingContext context, Uri defaultUrl)
{
var host = context.GetFromTempStorage("host");
var basePath = context.GetFromTempStorage("basePath");
var schemes = context.GetFromTempStorage>("schemes");
- if (schemes != null)
+ // If nothing is provided, don't create a server
+ if (host == null && basePath == null && schemes == null)
+ {
+ return;
+ }
+
+ // Fill in missing information based on the defaultUrl
+ if (defaultUrl != null)
+ {
+ host = host ?? defaultUrl.GetComponents(UriComponents.NormalizedHost, UriFormat.SafeUnescaped);
+ basePath = basePath ?? defaultUrl.GetComponents(UriComponents.Path, UriFormat.SafeUnescaped);
+ schemes = schemes ?? new List { defaultUrl.GetComponents(UriComponents.Scheme, UriFormat.SafeUnescaped) };
+ }
+ else if (String.IsNullOrEmpty(host) && String.IsNullOrEmpty(basePath))
+ {
+ return; // Can't make a server object out of just a Scheme
+ }
+
+ // Create the Server objects
+ if (schemes != null && schemes.Count > 0)
{
foreach (var scheme in schemes)
{
- var server = new OpenApiServer();
- server.Url = scheme + "://" + (host ?? "example.org/") + (basePath ?? "/");
+ var server = new OpenApiServer
+ {
+ Url = BuildUrl(scheme, host, basePath)
+ };
+
servers.Add(server);
}
}
+ else
+ {
+ var server = new OpenApiServer
+ {
+ Url = BuildUrl(null, host, basePath)
+ };
+
+ servers.Add(server);
+ }
+
+ foreach (var server in servers)
+ {
+ // Server Urls are always appended to Paths and Paths must start with /
+ // so removing the slash prevents a double slash.
+ if (server.Url.EndsWith("/"))
+ {
+ server.Url = server.Url.Substring(0, server.Url.Length - 1);
+ }
+ }
+ }
+
+ private static string BuildUrl(string scheme, string host, string basePath)
+ {
+ if (String.IsNullOrEmpty(scheme) && !String.IsNullOrEmpty(host))
+ {
+ host = "//" + host; // The double slash prefix creates a relative url where the scheme is defined by the BaseUrl
+ }
+
+ int? port = null;
+
+ if (!String.IsNullOrEmpty(host) && host.Contains(":"))
+ {
+ var pieces = host.Split(':');
+ host = pieces.First();
+ port = int.Parse(pieces.Last());
+ }
+
+ var uriBuilder = new UriBuilder()
+ {
+ Scheme = scheme,
+ Host = host,
+ Path = basePath
+ };
+
+ if (port != null)
+ {
+ uriBuilder.Port = port.Value;
+ }
+
+ return uriBuilder.ToString();
}
public static OpenApiDocument LoadOpenApi(RootNode rootNode)
@@ -151,7 +224,7 @@ public static OpenApiDocument LoadOpenApi(RootNode rootNode)
openApidoc.Servers = new List();
}
- MakeServers(openApidoc.Servers, openApiNode.Context);
+ MakeServers(openApidoc.Servers, openApiNode.Context, rootNode.Context.BaseUrl);
FixRequestBodyReferences(openApidoc);
return openApidoc;
@@ -167,7 +240,6 @@ private static void FixRequestBodyReferences(OpenApiDocument doc)
var walker = new OpenApiWalker(fixer);
walker.Walk(doc);
}
-
}
}
@@ -197,5 +269,7 @@ public override void Visit(OpenApiOperation operation)
};
}
}
+
+
}
}
\ No newline at end of file
diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiHeaderDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiHeaderDeserializer.cs
index 3d8f2e2f6..f23746a0a 100644
--- a/src/Microsoft.OpenApi.Readers/V2/OpenApiHeaderDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiHeaderDeserializer.cs
@@ -130,7 +130,7 @@ internal static partial class OpenApiV2Deserializer
private static readonly PatternFieldMap _headerPatternFields = new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n))}
};
public static OpenApiHeader LoadHeader(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiInfoDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiInfoDeserializer.cs
index c546254cb..b2a3083f7 100644
--- a/src/Microsoft.OpenApi.Readers/V2/OpenApiInfoDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiInfoDeserializer.cs
@@ -57,7 +57,7 @@ internal static partial class OpenApiV2Deserializer
private static PatternFieldMap _infoPatternFields = new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n))}
};
public static OpenApiInfo LoadInfo(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiLicenseDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiLicenseDeserializer.cs
index 963c572a5..567d37de1 100644
--- a/src/Microsoft.OpenApi.Readers/V2/OpenApiLicenseDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiLicenseDeserializer.cs
@@ -32,7 +32,7 @@ internal static partial class OpenApiV2Deserializer
private static PatternFieldMap _licensePatternFields = new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n))}
};
public static OpenApiLicense LoadLicense(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiOperationDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiOperationDeserializer.cs
index 838c4e892..9b8507105 100644
--- a/src/Microsoft.OpenApi.Readers/V2/OpenApiOperationDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiOperationDeserializer.cs
@@ -90,7 +90,7 @@ internal static partial class OpenApiV2Deserializer
private static readonly PatternFieldMap _operationPatternFields =
new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n))}
};
private static readonly FixedFieldMap _responsesFixedFields =
@@ -100,7 +100,7 @@ internal static partial class OpenApiV2Deserializer
new PatternFieldMap
{
{s => !s.StartsWith("x-"), (o, p, n) => o.Add(p, LoadResponse(n))},
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n))}
};
internal static OpenApiOperation LoadOperation(ParseNode node)
@@ -131,7 +131,12 @@ internal static OpenApiOperation LoadOperation(ParseNode node)
operation.RequestBody = CreateFormBody(node.Context, formParameters);
}
}
-
+
+ foreach (var response in operation.Responses.Values)
+ {
+ ProcessProduces(response, node.Context);
+ }
+
return operation;
}
diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiParameterDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiParameterDeserializer.cs
index b5f459a34..12b033f73 100644
--- a/src/Microsoft.OpenApi.Readers/V2/OpenApiParameterDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiParameterDeserializer.cs
@@ -146,7 +146,7 @@ internal static partial class OpenApiV2Deserializer
private static readonly PatternFieldMap _parameterPatternFields =
new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n))}
};
private static void LoadStyle(OpenApiParameter p, string v)
diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiPathItemDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiPathItemDeserializer.cs
index 19c806cbf..5b3a782e9 100644
--- a/src/Microsoft.OpenApi.Readers/V2/OpenApiPathItemDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiPathItemDeserializer.cs
@@ -39,7 +39,7 @@ internal static partial class OpenApiV2Deserializer
private static readonly PatternFieldMap _pathItemPatternFields =
new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())},
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n))},
};
public static OpenApiPathItem LoadPathItem(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiPathsDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiPathsDeserializer.cs
index d11843e8d..5b8a1a24a 100644
--- a/src/Microsoft.OpenApi.Readers/V2/OpenApiPathsDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiPathsDeserializer.cs
@@ -18,7 +18,7 @@ internal static partial class OpenApiV2Deserializer
private static PatternFieldMap _pathsPatternFields = new PatternFieldMap
{
{s => s.StartsWith("/"), (o, k, n) => o.Add(k, LoadPathItem(n))},
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n))}
};
public static OpenApiPaths LoadPaths(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs
index 7fc5170ec..e6fd39f89 100644
--- a/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs
@@ -45,7 +45,7 @@ internal static partial class OpenApiV2Deserializer
private static readonly PatternFieldMap _responsePatternFields =
new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n))}
};
private static void ProcessProduces(OpenApiResponse response, ParsingContext context)
@@ -53,15 +53,22 @@ private static void ProcessProduces(OpenApiResponse response, ParsingContext con
var produces = context.GetFromTempStorage>(TempStorageKeys.OperationProduces) ??
context.GetFromTempStorage>(TempStorageKeys.GlobalProduces) ?? new List();
- response.Content = new Dictionary();
+ if (response.Content == null)
+ {
+ response.Content = new Dictionary();
+ }
+
foreach (var produce in produces)
{
- var mediaType = new OpenApiMediaType
+ if (!response.Content.ContainsKey(produce))
{
- Schema = context.GetFromTempStorage(TempStorageKeys.ResponseSchema)
- };
+ var mediaType = new OpenApiMediaType
+ {
+ Schema = context.GetFromTempStorage(TempStorageKeys.ResponseSchema)
+ };
- response.Content.Add(produce, mediaType);
+ response.Content.Add(produce, mediaType);
+ }
}
}
diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiSchemaDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiSchemaDeserializer.cs
index 79f878463..4d08dd29d 100644
--- a/src/Microsoft.OpenApi.Readers/V2/OpenApiSchemaDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiSchemaDeserializer.cs
@@ -206,7 +206,7 @@ internal static partial class OpenApiV2Deserializer
private static readonly PatternFieldMap _schemaPatternFields = new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n))}
};
public static OpenApiSchema LoadSchema(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiSecuritySchemeDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiSecuritySchemeDeserializer.cs
index 5b88f33a8..117d1f3c4 100644
--- a/src/Microsoft.OpenApi.Readers/V2/OpenApiSecuritySchemeDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiSecuritySchemeDeserializer.cs
@@ -77,7 +77,7 @@ internal static partial class OpenApiV2Deserializer
private static readonly PatternFieldMap _securitySchemePatternFields =
new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n))}
};
public static OpenApiSecurityScheme LoadSecurityScheme(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiTagDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiTagDeserializer.cs
index 636a06796..380999475 100644
--- a/src/Microsoft.OpenApi.Readers/V2/OpenApiTagDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiTagDeserializer.cs
@@ -37,7 +37,7 @@ internal static partial class OpenApiV2Deserializer
private static readonly PatternFieldMap _tagPatternFields = new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n))}
};
public static OpenApiTag LoadTag(ParseNode n)
diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiV2Deserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiV2Deserializer.cs
index 31d9f96ee..4ebf2f693 100644
--- a/src/Microsoft.OpenApi.Readers/V2/OpenApiV2Deserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiV2Deserializer.cs
@@ -3,6 +3,8 @@
using System.Collections.Generic;
using System.Linq;
+using Microsoft.OpenApi.Any;
+using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Readers.ParseNodes;
namespace Microsoft.OpenApi.Readers.V2
@@ -32,6 +34,22 @@ private static void ParseMap(
}
}
+ public static IOpenApiAny LoadAny(ParseNode node)
+ {
+ return node.CreateAny();
+ }
+
+ private static IOpenApiExtension LoadExtension(string name, ParseNode node)
+ {
+ if (node.Context.ExtensionParsers.TryGetValue(name, out var parser))
+ {
+ return parser(node.CreateAny(), OpenApiSpecVersion.OpenApi2_0);
+ }
+ else
+ {
+ return node.CreateAny();
+ }
+ }
private static string LoadString(ParseNode node)
{
diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiV2VersionService.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiV2VersionService.cs
index 3d3acff5b..e9561b367 100644
--- a/src/Microsoft.OpenApi.Readers/V2/OpenApiV2VersionService.cs
+++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiV2VersionService.cs
@@ -2,9 +2,12 @@
// Licensed under the MIT license.
using System;
+using System.Collections.Generic;
+using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Exceptions;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Readers.Exceptions;
using Microsoft.OpenApi.Readers.Interface;
using Microsoft.OpenApi.Readers.ParseNodes;
using Microsoft.OpenApi.Readers.Properties;
@@ -17,10 +20,25 @@ namespace Microsoft.OpenApi.Readers.V2
///
internal class OpenApiV2VersionService : IOpenApiVersionService
{
- ///
- /// Return a function that converts a MapNode into a V2 OpenApiTag
- ///
- public Func TagLoader => OpenApiV2Deserializer.LoadTag;
+ private IDictionary> _loaders = new Dictionary>
+ {
+ [typeof(IOpenApiAny)] = OpenApiV2Deserializer.LoadAny,
+ [typeof(OpenApiExternalDocs)] = OpenApiV2Deserializer.LoadExternalDocs,
+ [typeof(OpenApiHeader)] = OpenApiV2Deserializer.LoadHeader,
+ [typeof(OpenApiInfo)] = OpenApiV2Deserializer.LoadInfo,
+ [typeof(OpenApiLicense)] = OpenApiV2Deserializer.LoadLicense,
+ [typeof(OpenApiOperation)] = OpenApiV2Deserializer.LoadOperation,
+ [typeof(OpenApiParameter)] = OpenApiV2Deserializer.LoadParameter,
+ [typeof(OpenApiPathItem)] = OpenApiV2Deserializer.LoadPathItem,
+ [typeof(OpenApiPaths)] = OpenApiV2Deserializer.LoadPaths,
+ [typeof(OpenApiResponse)] = OpenApiV2Deserializer.LoadResponse,
+ [typeof(OpenApiResponses)] = OpenApiV2Deserializer.LoadResponses,
+ [typeof(OpenApiSchema)] = OpenApiV2Deserializer.LoadSchema,
+ [typeof(OpenApiSecurityRequirement)] = OpenApiV2Deserializer.LoadSecurityRequirement,
+ [typeof(OpenApiSecurityScheme)] = OpenApiV2Deserializer.LoadSecurityScheme,
+ [typeof(OpenApiTag)] = OpenApiV2Deserializer.LoadTag,
+ [typeof(OpenApiXml)] = OpenApiV2Deserializer.LoadXml
+ };
private static OpenApiReference ParseLocalReference(string localReference)
{
@@ -73,7 +91,7 @@ private static ReferenceType ParseReferenceType(string referenceTypeName)
return ReferenceType.SecurityScheme;
default:
- throw new ArgumentException();
+ throw new OpenApiReaderException($"Unknown reference type '{referenceTypeName}'");
}
}
@@ -155,5 +173,10 @@ public OpenApiDocument LoadDocument(RootNode rootNode)
{
return OpenApiV2Deserializer.LoadOpenApi(rootNode);
}
+
+ public T LoadElement(ParseNode node) where T : IOpenApiElement
+ {
+ return (T)_loaders[typeof(T)](node);
+ }
}
}
\ No newline at end of file
diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiXmlDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiXmlDeserializer.cs
index 75c7d4658..6e2273839 100644
--- a/src/Microsoft.OpenApi.Readers/V2/OpenApiXmlDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiXmlDeserializer.cs
@@ -2,8 +2,10 @@
// Licensed under the MIT license.
using System;
+using Microsoft.OpenApi.Exceptions;
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Readers.Exceptions;
using Microsoft.OpenApi.Readers.ParseNodes;
namespace Microsoft.OpenApi.Readers.V2
@@ -25,7 +27,14 @@ internal static partial class OpenApiV2Deserializer
{
"namespace", (o, n) =>
{
- o.Namespace = new Uri(n.GetScalarValue(), UriKind.Absolute);
+ if (Uri.IsWellFormedUriString(n.GetScalarValue(), UriKind.Absolute))
+ {
+ o.Namespace = new Uri(n.GetScalarValue(), UriKind.Absolute);
+ }
+ else
+ {
+ throw new OpenApiReaderException($"Xml Namespace requires absolute URL. '{n.GetScalarValue()}' is not valid.");
+ }
}
},
{
@@ -51,7 +60,7 @@ internal static partial class OpenApiV2Deserializer
private static readonly PatternFieldMap _xmlPatternFields =
new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p,n))}
};
public static OpenApiXml LoadXml(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiCallbackDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiCallbackDeserializer.cs
index ddc9d3d78..697740b6f 100644
--- a/src/Microsoft.OpenApi.Readers/V3/OpenApiCallbackDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiCallbackDeserializer.cs
@@ -21,7 +21,7 @@ internal static partial class OpenApiV3Deserializer
new PatternFieldMap
{
{s => s.StartsWith("$"), (o, p, n) => o.AddPathItem(RuntimeExpression.Build(p), LoadPathItem(n))},
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())},
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p,n))},
};
public static OpenApiCallback LoadCallback(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiComponentsDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiComponentsDeserializer.cs
index 018618efd..febeda730 100644
--- a/src/Microsoft.OpenApi.Readers/V3/OpenApiComponentsDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiComponentsDeserializer.cs
@@ -34,7 +34,7 @@ internal static partial class OpenApiV3Deserializer
private static PatternFieldMap _componentsPatternFields =
new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n))}
};
public static OpenApiComponents LoadComponents(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiContactDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiContactDeserializer.cs
index 2d873461a..154ed23c0 100644
--- a/src/Microsoft.OpenApi.Readers/V3/OpenApiContactDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiContactDeserializer.cs
@@ -38,7 +38,7 @@ internal static partial class OpenApiV3Deserializer
private static PatternFieldMap _contactPatternFields = new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p,n))}
};
public static OpenApiContact LoadContact(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiDocumentDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiDocumentDeserializer.cs
index 220164c13..2221584f1 100644
--- a/src/Microsoft.OpenApi.Readers/V3/OpenApiDocumentDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiDocumentDeserializer.cs
@@ -44,7 +44,7 @@ internal static partial class OpenApiV3Deserializer
private static PatternFieldMap _openApiPatternFields = new PatternFieldMap
{
// We have no semantics to verify X- nodes, therefore treat them as just values.
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n))}
};
public static OpenApiDocument LoadOpenApi(RootNode rootNode)
@@ -57,17 +57,5 @@ public static OpenApiDocument LoadOpenApi(RootNode rootNode)
return openApidoc;
}
-
-
- public static IOpenApiExtension LoadExtension(string name, ParseNode node)
- {
- if (node.Context.ExtensionParsers.TryGetValue(name, out var parser)) {
- return parser(node.CreateAny());
- }
- else
- {
- return node.CreateAny();
- }
- }
}
}
\ No newline at end of file
diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiEncodingDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiEncodingDeserializer.cs
index 279b3c0d4..18f1154c3 100644
--- a/src/Microsoft.OpenApi.Readers/V3/OpenApiEncodingDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiEncodingDeserializer.cs
@@ -59,7 +59,7 @@ internal static partial class OpenApiV3Deserializer
private static readonly PatternFieldMap _encodingPatternFields =
new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p,n))}
};
public static OpenApiEncoding LoadEncoding(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiExampleDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiExampleDeserializer.cs
index e204f034d..36f591a9a 100644
--- a/src/Microsoft.OpenApi.Readers/V3/OpenApiExampleDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiExampleDeserializer.cs
@@ -45,7 +45,7 @@ internal static partial class OpenApiV3Deserializer
private static readonly PatternFieldMap _examplePatternFields =
new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p,n))}
};
public static OpenApiExample LoadExample(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiHeaderDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiHeaderDeserializer.cs
index 868330a8f..95ffb78b0 100644
--- a/src/Microsoft.OpenApi.Readers/V3/OpenApiHeaderDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiHeaderDeserializer.cs
@@ -55,7 +55,7 @@ internal static partial class OpenApiV3Deserializer
private static readonly PatternFieldMap _headerPatternFields = new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p,n))}
};
public static OpenApiHeader LoadHeader(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiInfoDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiInfoDeserializer.cs
index 4a65f261d..17a97c117 100644
--- a/src/Microsoft.OpenApi.Readers/V3/OpenApiInfoDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiInfoDeserializer.cs
@@ -57,7 +57,7 @@ internal static partial class OpenApiV3Deserializer
public static PatternFieldMap InfoPatternFields = new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, k, n) => o.Extensions.Add(k,LoadExtension(k, n))}
+ {s => s.StartsWith("x-"), (o, k, n) => o.AddExtension(k,LoadExtension(k, n))}
};
public static OpenApiInfo LoadInfo(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiLicenseDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiLicenseDeserializer.cs
index 35e11c243..6bd352f53 100644
--- a/src/Microsoft.OpenApi.Readers/V3/OpenApiLicenseDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiLicenseDeserializer.cs
@@ -32,7 +32,7 @@ internal static partial class OpenApiV3Deserializer
private static PatternFieldMap _licensePatternFields = new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p,n))}
};
internal static OpenApiLicense LoadLicense(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiLinkDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiLinkDeserializer.cs
index c2a205089..06ec8de3e 100644
--- a/src/Microsoft.OpenApi.Readers/V3/OpenApiLinkDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiLinkDeserializer.cs
@@ -50,7 +50,7 @@ internal static partial class OpenApiV3Deserializer
private static readonly PatternFieldMap _linkPatternFields = new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())},
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p,n))},
};
public static OpenApiLink LoadLink(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiMediaTypeDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiMediaTypeDeserializer.cs
index 983d67848..1cf671777 100644
--- a/src/Microsoft.OpenApi.Readers/V3/OpenApiMediaTypeDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiMediaTypeDeserializer.cs
@@ -18,35 +18,40 @@ internal static partial class OpenApiV3Deserializer
new FixedFieldMap
{
{
- "schema", (o, n) =>
+ OpenApiConstants.Schema, (o, n) =>
{
o.Schema = LoadSchema(n);
}
},
{
- "examples", (o, n) =>
+ OpenApiConstants.Examples, (o, n) =>
{
o.Examples = n.CreateMap(LoadExample);
}
},
{
- "example", (o, n) =>
+ OpenApiConstants.Example, (o, n) =>
{
o.Example = n.CreateAny();
}
},
- //Encoding
+ {
+ OpenApiConstants.Encoding, (o, n) =>
+ {
+ o.Encoding = n.CreateMap(LoadEncoding);
+ }
+ },
};
private static readonly PatternFieldMap _mediaTypePatternFields =
new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p,n))}
};
public static OpenApiMediaType LoadMediaType(ParseNode node)
{
- var mapNode = node.CheckMapNode("content");
+ var mapNode = node.CheckMapNode(OpenApiConstants.Content);
if (!mapNode.Any())
{
diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiOAuthFlowDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiOAuthFlowDeserializer.cs
index 96cd9af8d..adc814f33 100644
--- a/src/Microsoft.OpenApi.Readers/V3/OpenApiOAuthFlowDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiOAuthFlowDeserializer.cs
@@ -41,7 +41,7 @@ internal static partial class OpenApiV3Deserializer
private static readonly PatternFieldMap _oAuthFlowPatternFields =
new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p,n))}
};
public static OpenApiOAuthFlow LoadOAuthFlow(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiOAuthFlowsDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiOAuthFlowsDeserializer.cs
index 5d88a9175..022ed35fd 100644
--- a/src/Microsoft.OpenApi.Readers/V3/OpenApiOAuthFlowsDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiOAuthFlowsDeserializer.cs
@@ -25,7 +25,7 @@ internal static partial class OpenApiV3Deserializer
private static readonly PatternFieldMap _oAuthFlowsPatternFields =
new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p,n))}
};
public static OpenApiOAuthFlows LoadOAuthFlows(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiOperationDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiOperationDeserializer.cs
index 579063278..3ab39c828 100644
--- a/src/Microsoft.OpenApi.Readers/V3/OpenApiOperationDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiOperationDeserializer.cs
@@ -95,7 +95,7 @@ internal static partial class OpenApiV3Deserializer
private static readonly PatternFieldMap _operationPatternFields =
new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())},
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p,n))},
};
internal static OpenApiOperation LoadOperation(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiParameterDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiParameterDeserializer.cs
index e713d00b7..89f9b4366 100644
--- a/src/Microsoft.OpenApi.Readers/V3/OpenApiParameterDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiParameterDeserializer.cs
@@ -101,7 +101,7 @@ internal static partial class OpenApiV3Deserializer
private static readonly PatternFieldMap _parameterPatternFields =
new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p,n))}
};
public static OpenApiParameter LoadParameter(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiPathItemDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiPathItemDeserializer.cs
index ca6c0d73e..ff08ce186 100644
--- a/src/Microsoft.OpenApi.Readers/V3/OpenApiPathItemDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiPathItemDeserializer.cs
@@ -43,7 +43,7 @@ internal static partial class OpenApiV3Deserializer
private static readonly PatternFieldMap _pathItemPatternFields =
new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p,n))}
};
public static OpenApiPathItem LoadPathItem(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiPathsDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiPathsDeserializer.cs
index 207c5228b..2020628d0 100644
--- a/src/Microsoft.OpenApi.Readers/V3/OpenApiPathsDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiPathsDeserializer.cs
@@ -18,7 +18,7 @@ internal static partial class OpenApiV3Deserializer
private static PatternFieldMap _pathsPatternFields = new PatternFieldMap
{
{s => s.StartsWith("/"), (o, k, n) => o.Add(k, LoadPathItem(n))},
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p,n))}
};
public static OpenApiPaths LoadPaths(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiRequestBodyDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiRequestBodyDeserializer.cs
index 15b53473e..efd54b101 100644
--- a/src/Microsoft.OpenApi.Readers/V3/OpenApiRequestBodyDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiRequestBodyDeserializer.cs
@@ -39,7 +39,7 @@ internal static partial class OpenApiV3Deserializer
private static readonly PatternFieldMap _requestBodyPatternFields =
new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p,n))}
};
public static OpenApiRequestBody LoadRequestBody(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiResponseDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiResponseDeserializer.cs
index e5c483a77..30ea4a52e 100644
--- a/src/Microsoft.OpenApi.Readers/V3/OpenApiResponseDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiResponseDeserializer.cs
@@ -45,7 +45,7 @@ internal static partial class OpenApiV3Deserializer
private static readonly PatternFieldMap _responsePatternFields =
new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p,n))}
};
public static OpenApiResponse LoadResponse(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiResponsesDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiResponsesDeserializer.cs
index 9ee33015e..580d3ff67 100644
--- a/src/Microsoft.OpenApi.Readers/V3/OpenApiResponsesDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiResponsesDeserializer.cs
@@ -18,7 +18,7 @@ internal static partial class OpenApiV3Deserializer
public static PatternFieldMap ResponsesPatternFields = new PatternFieldMap
{
{s => !s.StartsWith("x-"), (o, p, n) => o.Add(p, LoadResponse(n))},
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p,n))}
};
public static OpenApiResponses LoadResponses(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiSchemaDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiSchemaDeserializer.cs
index da67820a3..a86ebcb50 100644
--- a/src/Microsoft.OpenApi.Readers/V3/OpenApiSchemaDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiSchemaDeserializer.cs
@@ -239,7 +239,7 @@ internal static partial class OpenApiV3Deserializer
private static readonly PatternFieldMap _schemaPatternFields = new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p,n))}
};
public static OpenApiSchema LoadSchema(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiSecuritySchemeDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiSecuritySchemeDeserializer.cs
index 6779750d1..8657faceb 100644
--- a/src/Microsoft.OpenApi.Readers/V3/OpenApiSecuritySchemeDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiSecuritySchemeDeserializer.cs
@@ -70,7 +70,7 @@ internal static partial class OpenApiV3Deserializer
private static readonly PatternFieldMap _securitySchemePatternFields =
new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p,n))}
};
public static OpenApiSecurityScheme LoadSecurityScheme(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiServerDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiServerDeserializer.cs
index a1cc85b36..39842c974 100644
--- a/src/Microsoft.OpenApi.Readers/V3/OpenApiServerDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiServerDeserializer.cs
@@ -37,7 +37,7 @@ internal static partial class OpenApiV3Deserializer
private static readonly PatternFieldMap _serverPatternFields = new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p,n))}
};
public static OpenApiServer LoadServer(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiServerVariableDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiServerVariableDeserializer.cs
index d8fb35ef1..9fc955a78 100644
--- a/src/Microsoft.OpenApi.Readers/V3/OpenApiServerVariableDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiServerVariableDeserializer.cs
@@ -39,7 +39,7 @@ internal static partial class OpenApiV3Deserializer
private static readonly PatternFieldMap _serverVariablePatternFields =
new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p,n))}
};
public static OpenApiServerVariable LoadServerVariable(ParseNode node)
diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiTagDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiTagDeserializer.cs
index 8e8c329e3..1f3a36afb 100644
--- a/src/Microsoft.OpenApi.Readers/V3/OpenApiTagDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiTagDeserializer.cs
@@ -37,7 +37,7 @@ internal static partial class OpenApiV3Deserializer
private static readonly PatternFieldMap _tagPatternFields = new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p,n))}
};
public static OpenApiTag LoadTag(ParseNode n)
diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiV3Deserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiV3Deserializer.cs
index cde82d53d..cdf175090 100644
--- a/src/Microsoft.OpenApi.Readers/V3/OpenApiV3Deserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiV3Deserializer.cs
@@ -3,7 +3,9 @@
using System.Collections.Generic;
using System.Linq;
+using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Expressions;
+using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers.ParseNodes;
@@ -57,7 +59,22 @@ private static RuntimeExpressionAnyWrapper LoadRuntimeExpressionAnyWrapper(Parse
};
}
+ public static IOpenApiAny LoadAny(ParseNode node)
+ {
+ return node.CreateAny();
+ }
+ private static IOpenApiExtension LoadExtension(string name, ParseNode node)
+ {
+ if (node.Context.ExtensionParsers.TryGetValue(name, out var parser))
+ {
+ return parser(node.CreateAny(), OpenApiSpecVersion.OpenApi3_0);
+ }
+ else
+ {
+ return node.CreateAny();
+ }
+ }
private static string LoadString(ParseNode node)
{
diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiV3VersionService.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiV3VersionService.cs
index 905123961..891822a21 100644
--- a/src/Microsoft.OpenApi.Readers/V3/OpenApiV3VersionService.cs
+++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiV3VersionService.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
+using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Exceptions;
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Interfaces;
@@ -18,11 +19,36 @@ namespace Microsoft.OpenApi.Readers.V3
///
internal class OpenApiV3VersionService : IOpenApiVersionService
{
- ///
- /// Return a function that converts a MapNode into a V3 OpenApiTag
- ///
- public Func TagLoader => OpenApiV3Deserializer.LoadTag;
-
+ private IDictionary> _loaders = new Dictionary> {
+ [typeof(IOpenApiAny)] = OpenApiV3Deserializer.LoadAny,
+ [typeof(OpenApiCallback)] = OpenApiV3Deserializer.LoadCallback,
+ [typeof(OpenApiComponents)] = OpenApiV3Deserializer.LoadComponents,
+ [typeof(OpenApiEncoding)] = OpenApiV3Deserializer.LoadEncoding,
+ [typeof(OpenApiExample)] = OpenApiV3Deserializer.LoadExample,
+ [typeof(OpenApiExternalDocs)] = OpenApiV3Deserializer.LoadExternalDocs,
+ [typeof(OpenApiHeader)] = OpenApiV3Deserializer.LoadHeader,
+ [typeof(OpenApiInfo)] = OpenApiV3Deserializer.LoadInfo,
+ [typeof(OpenApiLicense)] = OpenApiV3Deserializer.LoadLicense,
+ [typeof(OpenApiLink)] = OpenApiV3Deserializer.LoadLink,
+ [typeof(OpenApiMediaType)] = OpenApiV3Deserializer.LoadMediaType,
+ [typeof(OpenApiOAuthFlow)] = OpenApiV3Deserializer.LoadOAuthFlow,
+ [typeof(OpenApiOAuthFlows)] = OpenApiV3Deserializer.LoadOAuthFlows,
+ [typeof(OpenApiOperation)] = OpenApiV3Deserializer.LoadOperation,
+ [typeof(OpenApiParameter)] = OpenApiV3Deserializer.LoadParameter,
+ [typeof(OpenApiPathItem)] = OpenApiV3Deserializer.LoadPathItem,
+ [typeof(OpenApiPaths)] = OpenApiV3Deserializer.LoadPaths,
+ [typeof(OpenApiRequestBody)] = OpenApiV3Deserializer.LoadRequestBody,
+ [typeof(OpenApiResponse)] = OpenApiV3Deserializer.LoadResponse,
+ [typeof(OpenApiResponses)] = OpenApiV3Deserializer.LoadResponses,
+ [typeof(OpenApiSchema)] = OpenApiV3Deserializer.LoadSchema,
+ [typeof(OpenApiSecurityRequirement)] = OpenApiV3Deserializer.LoadSecurityRequirement,
+ [typeof(OpenApiSecurityScheme)] = OpenApiV3Deserializer.LoadSecurityScheme,
+ [typeof(OpenApiServer)] = OpenApiV3Deserializer.LoadServer,
+ [typeof(OpenApiServerVariable)] = OpenApiV3Deserializer.LoadServerVariable,
+ [typeof(OpenApiTag)] = OpenApiV3Deserializer.LoadTag,
+ [typeof(OpenApiXml)] = OpenApiV3Deserializer.LoadXml
+ };
+
///
/// Parse the string to a object.
///
@@ -80,6 +106,11 @@ public OpenApiDocument LoadDocument(RootNode rootNode)
return OpenApiV3Deserializer.LoadOpenApi(rootNode);
}
+ public T LoadElement(ParseNode node) where T : IOpenApiElement
+ {
+ return (T)_loaders[typeof(T)](node);
+ }
+
private OpenApiReference ParseLocalReference(string localReference)
{
if (string.IsNullOrWhiteSpace(localReference))
diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiXmlDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiXmlDeserializer.cs
index 0ce7ea2eb..0b6196c65 100644
--- a/src/Microsoft.OpenApi.Readers/V3/OpenApiXmlDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiXmlDeserializer.cs
@@ -51,7 +51,7 @@ internal static partial class OpenApiV3Deserializer
private static readonly PatternFieldMap _xmlPatternFields =
new PatternFieldMap
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
+ {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p,n))}
};
public static OpenApiXml LoadXml(ParseNode node)
diff --git a/src/Microsoft.OpenApi/Any/OpenApiArray.cs b/src/Microsoft.OpenApi/Any/OpenApiArray.cs
index 73c7a721e..abd4ae099 100644
--- a/src/Microsoft.OpenApi/Any/OpenApiArray.cs
+++ b/src/Microsoft.OpenApi/Any/OpenApiArray.cs
@@ -20,7 +20,8 @@ public class OpenApiArray : List, IOpenApiAny
/// Write out contents of OpenApiArray to passed writer
///
/// Instance of JSON or YAML writer.
- public void Write(IOpenApiWriter writer)
+ /// Version of the OpenAPI specification that that will be output.
+ public void Write(IOpenApiWriter writer, OpenApiSpecVersion specVersion)
{
writer.WriteStartArray();
diff --git a/src/Microsoft.OpenApi/Any/OpenApiNull.cs b/src/Microsoft.OpenApi/Any/OpenApiNull.cs
index 5ff43acfa..de6d4ff79 100644
--- a/src/Microsoft.OpenApi/Any/OpenApiNull.cs
+++ b/src/Microsoft.OpenApi/Any/OpenApiNull.cs
@@ -19,7 +19,8 @@ public class OpenApiNull : IOpenApiAny
/// Write out null representation
///
///
- public void Write(IOpenApiWriter writer)
+ /// Version of the OpenAPI specification that that will be output.
+ public void Write(IOpenApiWriter writer, OpenApiSpecVersion specVersion)
{
writer.WriteAny(this);
}
diff --git a/src/Microsoft.OpenApi/Any/OpenApiObject.cs b/src/Microsoft.OpenApi/Any/OpenApiObject.cs
index 0f1aee397..795f9dbe6 100644
--- a/src/Microsoft.OpenApi/Any/OpenApiObject.cs
+++ b/src/Microsoft.OpenApi/Any/OpenApiObject.cs
@@ -20,7 +20,8 @@ public class OpenApiObject : Dictionary, IOpenApiAny
/// Serialize OpenApiObject to writer
///
///
- public void Write(IOpenApiWriter writer)
+ /// Version of the OpenAPI specification that that will be output.
+ public void Write(IOpenApiWriter writer, OpenApiSpecVersion specVersion)
{
writer.WriteStartObject();
diff --git a/src/Microsoft.OpenApi/Any/OpenApiPrimitive.cs b/src/Microsoft.OpenApi/Any/OpenApiPrimitive.cs
index dd6be1b95..60cbbb9e2 100644
--- a/src/Microsoft.OpenApi/Any/OpenApiPrimitive.cs
+++ b/src/Microsoft.OpenApi/Any/OpenApiPrimitive.cs
@@ -41,7 +41,8 @@ public OpenApiPrimitive(T value)
/// Write out content of primitive element
///
///
- public void Write(IOpenApiWriter writer)
+ ///
+ public void Write(IOpenApiWriter writer, OpenApiSpecVersion specVersion)
{
switch (this.PrimitiveType)
{
diff --git a/src/Microsoft.OpenApi/Expressions/CompositeExpression.cs b/src/Microsoft.OpenApi/Expressions/CompositeExpression.cs
new file mode 100644
index 000000000..4a08761db
--- /dev/null
+++ b/src/Microsoft.OpenApi/Expressions/CompositeExpression.cs
@@ -0,0 +1,48 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+
+namespace Microsoft.OpenApi.Expressions
+{
+ ///
+ /// String literal with embedded expressions
+ ///
+ public class CompositeExpression : RuntimeExpression
+ {
+ private readonly string template;
+ private Regex expressionPattern = new Regex("{(?[^}]+)");
+ ///
+ /// Expressions embedded into string literal
+ ///
+ public List ContainedExpressions = new List();
+
+ ///
+ /// Create a composite expression from a string literal with an embedded expression
+ ///
+ ///
+ public CompositeExpression(string expression)
+ {
+ template = expression;
+
+ // Extract subexpressions and convert to RuntimeExpressions
+ var matches = expressionPattern.Matches(expression);
+
+ foreach (var item in matches.Cast())
+ {
+ var value = item.Groups["exp"].Captures.Cast().First().Value;
+ ContainedExpressions.Add(RuntimeExpression.Build(value));
+ }
+ }
+
+ ///
+ /// Return original string literal with embedded expression
+ ///
+ public override string Expression => template;
+ }
+}
diff --git a/src/Microsoft.OpenApi/Expressions/RuntimeExpression.cs b/src/Microsoft.OpenApi/Expressions/RuntimeExpression.cs
index a28b95fc5..e039ad478 100644
--- a/src/Microsoft.OpenApi/Expressions/RuntimeExpression.cs
+++ b/src/Microsoft.OpenApi/Expressions/RuntimeExpression.cs
@@ -34,6 +34,11 @@ public static RuntimeExpression Build(string expression)
throw Error.ArgumentNullOrWhiteSpace(nameof(expression));
}
+ if (expression.Contains("{$"))
+ {
+ return new CompositeExpression(expression);
+ }
+
if (!expression.StartsWith(Prefix))
{
throw new OpenApiException(string.Format(SRResource.RuntimeExpressionMustBeginWithDollar, expression));
diff --git a/src/Microsoft.OpenApi/Extensions/OpenApiExtensibleExtensions.cs b/src/Microsoft.OpenApi/Extensions/OpenApiExtensibleExtensions.cs
index 6f39041a2..b41802794 100644
--- a/src/Microsoft.OpenApi/Extensions/OpenApiExtensibleExtensions.cs
+++ b/src/Microsoft.OpenApi/Extensions/OpenApiExtensibleExtensions.cs
@@ -21,7 +21,7 @@ public static class OpenApiExtensibleExtensions
/// The extensible Open API element.
/// The extension name.
/// The extension value.
- public static void AddExtension(this T element, string name, IOpenApiAny any)
+ public static void AddExtension(this T element, string name, IOpenApiExtension any)
where T : IOpenApiExtensible
{
if (element == null)
@@ -41,6 +41,5 @@ public static void AddExtension(this T element, string name, IOpenApiAny any)
element.Extensions[name] = any ?? throw Error.ArgumentNull(nameof(any));
}
-
}
}
\ No newline at end of file
diff --git a/src/Microsoft.OpenApi/Extensions/OpenApiSerializableExtensions.cs b/src/Microsoft.OpenApi/Extensions/OpenApiSerializableExtensions.cs
index a072753a4..2cd1eb18e 100644
--- a/src/Microsoft.OpenApi/Extensions/OpenApiSerializableExtensions.cs
+++ b/src/Microsoft.OpenApi/Extensions/OpenApiSerializableExtensions.cs
@@ -83,7 +83,7 @@ public static void Serialize(
/// the
/// The Open API element.
/// The output writer.
- /// The Open API specification version.
+ /// Version of the specification the output should conform to
public static void Serialize(this T element, IOpenApiWriter writer, OpenApiSpecVersion specVersion)
where T : IOpenApiSerializable
{
diff --git a/src/Microsoft.OpenApi/Interfaces/IOpenApiExtension.cs b/src/Microsoft.OpenApi/Interfaces/IOpenApiExtension.cs
index e9e92fb5d..a9ea04a39 100644
--- a/src/Microsoft.OpenApi/Interfaces/IOpenApiExtension.cs
+++ b/src/Microsoft.OpenApi/Interfaces/IOpenApiExtension.cs
@@ -14,6 +14,7 @@ public interface IOpenApiExtension
/// Write out contents of custom extension
///
///
- void Write(IOpenApiWriter writer);
+ /// Version of the OpenAPI specification that that will be output.
+ void Write(IOpenApiWriter writer, OpenApiSpecVersion specVersion);
}
}
diff --git a/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj b/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj
index 291eb72cc..390fb7a19 100644
--- a/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj
+++ b/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj
@@ -10,7 +10,7 @@
Microsoft
Microsoft.OpenApi
Microsoft.OpenApi
- 1.0.1
+ 1.1.0
.NET models with JSON and YAML writers for OpenAPI specification
© Microsoft Corporation. All rights reserved.
OpenAPI .NET
diff --git a/src/Microsoft.OpenApi/Models/OpenApiCallback.cs b/src/Microsoft.OpenApi/Models/OpenApiCallback.cs
index 221881798..fc6db76dc 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiCallback.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiCallback.cs
@@ -94,7 +94,7 @@ public void SerializeAsV3WithoutReference(IOpenApiWriter writer)
}
// extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi3_0);
writer.WriteEndObject();
}
diff --git a/src/Microsoft.OpenApi/Models/OpenApiComponents.cs b/src/Microsoft.OpenApi/Models/OpenApiComponents.cs
index 5bc87c1dd..6adab4b13 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiComponents.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiComponents.cs
@@ -244,7 +244,7 @@ public void SerializeAsV3(IOpenApiWriter writer)
});
// extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi3_0);
writer.WriteEndObject();
}
diff --git a/src/Microsoft.OpenApi/Models/OpenApiContact.cs b/src/Microsoft.OpenApi/Models/OpenApiContact.cs
index 52f56dc29..ecba3d3c4 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiContact.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiContact.cs
@@ -40,7 +40,7 @@ public class OpenApiContact : IOpenApiSerializable, IOpenApiExtensible
///
public void SerializeAsV3(IOpenApiWriter writer)
{
- WriteInternal(writer);
+ WriteInternal(writer, OpenApiSpecVersion.OpenApi3_0);
}
///
@@ -48,10 +48,10 @@ public void SerializeAsV3(IOpenApiWriter writer)
///
public void SerializeAsV2(IOpenApiWriter writer)
{
- WriteInternal(writer);
+ WriteInternal(writer, OpenApiSpecVersion.OpenApi2_0);
}
- private void WriteInternal(IOpenApiWriter writer)
+ private void WriteInternal(IOpenApiWriter writer, OpenApiSpecVersion specVersion)
{
if (writer == null)
{
@@ -70,7 +70,7 @@ private void WriteInternal(IOpenApiWriter writer)
writer.WriteProperty(OpenApiConstants.Email, Email);
// extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, specVersion);
writer.WriteEndObject();
}
diff --git a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs
index 60db434f5..78d532f22 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs
@@ -29,7 +29,7 @@ public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible
///
/// REQUIRED. The available paths and operations for the API.
///
- public OpenApiPaths Paths { get; set; } = new OpenApiPaths();
+ public OpenApiPaths Paths { get; set; }
///
/// An element to hold various schemas for the specification.
@@ -97,7 +97,7 @@ public void SerializeAsV3(IOpenApiWriter writer)
writer.WriteOptionalObject(OpenApiConstants.ExternalDocs, ExternalDocs, (w, e) => e.SerializeAsV3(w));
// extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi3_0);
writer.WriteEndObject();
}
@@ -214,7 +214,7 @@ public void SerializeAsV2(IOpenApiWriter writer)
writer.WriteOptionalObject(OpenApiConstants.ExternalDocs, ExternalDocs, (w, e) => e.SerializeAsV2(w));
// extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi2_0);
writer.WriteEndObject();
}
@@ -240,7 +240,10 @@ private static void WriteHostInfoV2(IOpenApiWriter writer, IList
firstServerUrl.GetComponents(UriComponents.Host | UriComponents.Port, UriFormat.SafeUnescaped));
// basePath
- writer.WriteProperty(OpenApiConstants.BasePath, firstServerUrl.AbsolutePath);
+ if (firstServerUrl.AbsolutePath != "/")
+ {
+ writer.WriteProperty(OpenApiConstants.BasePath, firstServerUrl.AbsolutePath);
+ }
// Consider all schemes of the URLs in the server list that have the same
// host, port, and base path as the first server.
@@ -302,6 +305,10 @@ public IOpenApiReferenceable ResolveReference(OpenApiReference reference)
return null;
}
+ if (this.Components == null) {
+ throw new OpenApiException(string.Format(Properties.SRResource.InvalidReferenceId, reference.Id));
+ }
+
try
{
switch (reference.Type)
diff --git a/src/Microsoft.OpenApi/Models/OpenApiEncoding.cs b/src/Microsoft.OpenApi/Models/OpenApiEncoding.cs
index ccb36ef77..74fb943b6 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiEncoding.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiEncoding.cs
@@ -81,7 +81,7 @@ public void SerializeAsV3(IOpenApiWriter writer)
writer.WriteProperty(OpenApiConstants.AllowReserved, AllowReserved, false);
// extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi3_0);
writer.WriteEndObject();
}
diff --git a/src/Microsoft.OpenApi/Models/OpenApiError.cs b/src/Microsoft.OpenApi/Models/OpenApiError.cs
index 2b6d85635..dbd34845c 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiError.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiError.cs
@@ -41,7 +41,7 @@ public OpenApiError(string pointer, string message)
///
public override string ToString()
{
- return Message + (!string.IsNullOrEmpty(Pointer) ? " at " + Pointer : "");
+ return Message + (!string.IsNullOrEmpty(Pointer) ? " [" + Pointer + "]" : "" );
}
}
}
\ No newline at end of file
diff --git a/src/Microsoft.OpenApi/Models/OpenApiExample.cs b/src/Microsoft.OpenApi/Models/OpenApiExample.cs
index ae6cfa000..0ebce156b 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiExample.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiExample.cs
@@ -93,7 +93,7 @@ public void SerializeAsV3WithoutReference(IOpenApiWriter writer)
writer.WriteProperty(OpenApiConstants.ExternalValue, ExternalValue);
// extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi3_0);
writer.WriteEndObject();
}
diff --git a/src/Microsoft.OpenApi/Models/OpenApiExtensibleDictionary.cs b/src/Microsoft.OpenApi/Models/OpenApiExtensibleDictionary.cs
index 643c4bb14..d3fc5117c 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiExtensibleDictionary.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiExtensibleDictionary.cs
@@ -39,7 +39,7 @@ public void SerializeAsV3(IOpenApiWriter writer)
writer.WriteRequiredObject(item.Key, item.Value, (w, p) => p.SerializeAsV3(w));
}
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi3_0);
writer.WriteEndObject();
}
@@ -61,7 +61,7 @@ public void SerializeAsV2(IOpenApiWriter writer)
writer.WriteRequiredObject(item.Key, item.Value, (w, p) => p.SerializeAsV2(w));
}
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi2_0);
writer.WriteEndObject();
}
diff --git a/src/Microsoft.OpenApi/Models/OpenApiExternalDocs.cs b/src/Microsoft.OpenApi/Models/OpenApiExternalDocs.cs
index 58e283fbf..9485eea55 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiExternalDocs.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiExternalDocs.cs
@@ -34,7 +34,7 @@ public class OpenApiExternalDocs : IOpenApiSerializable, IOpenApiExtensible
///
public void SerializeAsV3(IOpenApiWriter writer)
{
- WriteInternal(writer);
+ WriteInternal(writer, OpenApiSpecVersion.OpenApi3_0);
}
///
@@ -42,10 +42,10 @@ public void SerializeAsV3(IOpenApiWriter writer)
///
public void SerializeAsV2(IOpenApiWriter writer)
{
- WriteInternal(writer);
+ WriteInternal(writer, OpenApiSpecVersion.OpenApi2_0);
}
- private void WriteInternal(IOpenApiWriter writer)
+ private void WriteInternal(IOpenApiWriter writer, OpenApiSpecVersion specVersion)
{
if (writer == null)
{
@@ -61,7 +61,7 @@ private void WriteInternal(IOpenApiWriter writer)
writer.WriteProperty(OpenApiConstants.Url, Url?.OriginalString);
// extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, specVersion);
writer.WriteEndObject();
}
diff --git a/src/Microsoft.OpenApi/Models/OpenApiHeader.cs b/src/Microsoft.OpenApi/Models/OpenApiHeader.cs
index c04135efa..c5fa288ae 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiHeader.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiHeader.cs
@@ -146,7 +146,7 @@ public void SerializeAsV3WithoutReference(IOpenApiWriter writer)
writer.WriteOptionalMap(OpenApiConstants.Content, Content, (w, c) => c.SerializeAsV3(w));
// extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi3_0);
writer.WriteEndObject();
}
@@ -205,7 +205,7 @@ public void SerializeAsV2WithoutReference(IOpenApiWriter writer)
writer.WriteOptionalObject(OpenApiConstants.Example, Example, (w, s) => w.WriteAny(s));
// extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi2_0);
writer.WriteEndObject();
}
diff --git a/src/Microsoft.OpenApi/Models/OpenApiInfo.cs b/src/Microsoft.OpenApi/Models/OpenApiInfo.cs
index 126b60d46..03020cf56 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiInfo.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiInfo.cs
@@ -80,7 +80,7 @@ public void SerializeAsV3(IOpenApiWriter writer)
writer.WriteProperty(OpenApiConstants.Version, Version);
// specification extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi3_0);
writer.WriteEndObject();
}
@@ -116,7 +116,7 @@ public void SerializeAsV2(IOpenApiWriter writer)
writer.WriteProperty(OpenApiConstants.Version, Version);
// specification extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi2_0);
writer.WriteEndObject();
}
diff --git a/src/Microsoft.OpenApi/Models/OpenApiLicense.cs b/src/Microsoft.OpenApi/Models/OpenApiLicense.cs
index 5c147f42e..dcd2223b5 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiLicense.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiLicense.cs
@@ -34,7 +34,7 @@ public class OpenApiLicense : IOpenApiSerializable, IOpenApiExtensible
///
public void SerializeAsV3(IOpenApiWriter writer)
{
- WriteInternal(writer);
+ WriteInternal(writer, OpenApiSpecVersion.OpenApi3_0);
}
///
@@ -42,10 +42,10 @@ public void SerializeAsV3(IOpenApiWriter writer)
///
public void SerializeAsV2(IOpenApiWriter writer)
{
- WriteInternal(writer);
+ WriteInternal(writer, OpenApiSpecVersion.OpenApi2_0);
}
- private void WriteInternal(IOpenApiWriter writer)
+ private void WriteInternal(IOpenApiWriter writer, OpenApiSpecVersion specVersion)
{
if (writer == null)
{
@@ -61,7 +61,7 @@ private void WriteInternal(IOpenApiWriter writer)
writer.WriteProperty(OpenApiConstants.Url, Url?.OriginalString);
// specification extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, specVersion);
writer.WriteEndObject();
}
diff --git a/src/Microsoft.OpenApi/Models/OpenApiMediaType.cs b/src/Microsoft.OpenApi/Models/OpenApiMediaType.cs
index 6f6e05106..93098f836 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiMediaType.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiMediaType.cs
@@ -68,7 +68,7 @@ public void SerializeAsV3(IOpenApiWriter writer)
writer.WriteOptionalMap(OpenApiConstants.Encoding, Encoding, (w, e) => e.SerializeAsV3(w));
// extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi3_0);
writer.WriteEndObject();
}
diff --git a/src/Microsoft.OpenApi/Models/OpenApiOAuthFlow.cs b/src/Microsoft.OpenApi/Models/OpenApiOAuthFlow.cs
index 0c4072330..ee16dc7ba 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiOAuthFlow.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiOAuthFlow.cs
@@ -66,7 +66,7 @@ public void SerializeAsV3(IOpenApiWriter writer)
writer.WriteRequiredMap(OpenApiConstants.Scopes, Scopes, (w, s) => w.WriteValue(s));
// extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi3_0);
writer.WriteEndObject();
}
diff --git a/src/Microsoft.OpenApi/Models/OpenApiOAuthFlows.cs b/src/Microsoft.OpenApi/Models/OpenApiOAuthFlows.cs
index 97cff9b34..91e17368b 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiOAuthFlows.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiOAuthFlows.cs
@@ -69,7 +69,7 @@ public void SerializeAsV3(IOpenApiWriter writer)
(w, o) => o.SerializeAsV3(w));
// extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi3_0);
writer.WriteEndObject();
}
diff --git a/src/Microsoft.OpenApi/Models/OpenApiOperation.cs b/src/Microsoft.OpenApi/Models/OpenApiOperation.cs
index 5d3a16e33..d90637945 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiOperation.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiOperation.cs
@@ -161,7 +161,7 @@ public void SerializeAsV3(IOpenApiWriter writer)
writer.WriteOptionalCollection(OpenApiConstants.Servers, Servers, (w, s) => s.SerializeAsV3(w));
// specification extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi3_0);
writer.WriteEndObject();
}
@@ -247,7 +247,7 @@ public void SerializeAsV2(IOpenApiWriter writer)
// V2 spec actually allows the body to have custom name.
// Our library does not support this at the moment.
Name = "body",
- Schema = content?.Schema,
+ Schema = content?.Schema ?? new OpenApiSchema(),
Required = RequestBody.Required
};
@@ -309,7 +309,7 @@ public void SerializeAsV2(IOpenApiWriter writer)
writer.WriteOptionalCollection(OpenApiConstants.Security, Security, (w, s) => s.SerializeAsV2(w));
// specification extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi2_0);
writer.WriteEndObject();
}
diff --git a/src/Microsoft.OpenApi/Models/OpenApiParameter.cs b/src/Microsoft.OpenApi/Models/OpenApiParameter.cs
index 31d4f13e8..b717f0ec5 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiParameter.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiParameter.cs
@@ -195,7 +195,7 @@ public void SerializeAsV3WithoutReference(IOpenApiWriter writer)
writer.WriteOptionalMap(OpenApiConstants.Content, Content, (w, c) => c.SerializeAsV3(w));
// extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi3_0);
writer.WriteEndObject();
}
@@ -307,7 +307,7 @@ public void SerializeAsV2WithoutReference(IOpenApiWriter writer)
// extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi2_0);
writer.WriteEndObject();
}
diff --git a/src/Microsoft.OpenApi/Models/OpenApiPathItem.cs b/src/Microsoft.OpenApi/Models/OpenApiPathItem.cs
index 4da8363d0..aecabdefb 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiPathItem.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiPathItem.cs
@@ -90,7 +90,7 @@ public void SerializeAsV3(IOpenApiWriter writer)
writer.WriteOptionalCollection(OpenApiConstants.Parameters, Parameters, (w, p) => p.SerializeAsV3(w));
// specification extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi3_0);
writer.WriteEndObject();
}
@@ -131,7 +131,7 @@ public void SerializeAsV2(IOpenApiWriter writer)
Description);
// specification extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi2_0);
writer.WriteEndObject();
}
diff --git a/src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs b/src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs
index 80f9a0a7f..9b2842e8b 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs
@@ -81,7 +81,7 @@ public void SerializeAsV3WithoutReference(IOpenApiWriter writer)
writer.WriteProperty(OpenApiConstants.Required, Required, false);
// extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi3_0);
writer.WriteEndObject();
}
diff --git a/src/Microsoft.OpenApi/Models/OpenApiResponse.cs b/src/Microsoft.OpenApi/Models/OpenApiResponse.cs
index 1b9064345..4a4c5491e 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiResponse.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiResponse.cs
@@ -91,7 +91,7 @@ public void SerializeAsV3WithoutReference(IOpenApiWriter writer)
writer.WriteOptionalMap(OpenApiConstants.Links, Links, (w, l) => l.SerializeAsV3(w));
// extension
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi3_0);
writer.WriteEndObject();
}
@@ -136,12 +136,20 @@ public void SerializeAsV2WithoutReference(IOpenApiWriter writer)
(w, s) => s.SerializeAsV2(w));
// examples
- if (mediatype.Value.Example != null)
+ if (Content.Values.Any(m => m.Example != null))
{
writer.WritePropertyName(OpenApiConstants.Examples);
writer.WriteStartObject();
- writer.WritePropertyName(mediatype.Key);
- writer.WriteAny(mediatype.Value.Example);
+
+ foreach (var mediaTypePair in Content)
+ {
+ if (mediaTypePair.Value.Example != null)
+ {
+ writer.WritePropertyName(mediaTypePair.Key);
+ writer.WriteAny(mediaTypePair.Value.Example);
+ }
+ }
+
writer.WriteEndObject();
}
}
@@ -151,7 +159,7 @@ public void SerializeAsV2WithoutReference(IOpenApiWriter writer)
writer.WriteOptionalMap(OpenApiConstants.Headers, Headers, (w, h) => h.SerializeAsV2(w));
// extension
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi2_0);
writer.WriteEndObject();
}
diff --git a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs
index 45a7874df..008f4ae21 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs
@@ -383,7 +383,7 @@ public void SerializeAsV3WithoutReference(IOpenApiWriter writer)
writer.WriteProperty(OpenApiConstants.Deprecated, Deprecated, false);
// extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi3_0);
writer.WriteEndObject();
}
@@ -516,7 +516,7 @@ internal void WriteAsItemsProperties(IOpenApiWriter writer)
writer.WriteProperty(OpenApiConstants.MultipleOf, MultipleOf);
// extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi2_0);
}
internal void WriteAsSchemaProperties(
@@ -626,7 +626,7 @@ internal void WriteAsSchemaProperties(
writer.WriteOptionalObject(OpenApiConstants.Example, Example, (w, e) => w.WriteAny(e));
// extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi2_0);
}
}
}
\ No newline at end of file
diff --git a/src/Microsoft.OpenApi/Models/OpenApiSecurityScheme.cs b/src/Microsoft.OpenApi/Models/OpenApiSecurityScheme.cs
index 79d87d976..f77893592 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiSecurityScheme.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiSecurityScheme.cs
@@ -134,7 +134,7 @@ public void SerializeAsV3WithoutReference(IOpenApiWriter writer)
}
// extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi3_0);
writer.WriteEndObject();
}
@@ -212,7 +212,7 @@ public void SerializeAsV2WithoutReference(IOpenApiWriter writer)
writer.WriteProperty(OpenApiConstants.Description, Description);
// extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi2_0);
writer.WriteEndObject();
}
diff --git a/src/Microsoft.OpenApi/Models/OpenApiServer.cs b/src/Microsoft.OpenApi/Models/OpenApiServer.cs
index a29d17f06..72cf492d5 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiServer.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiServer.cs
@@ -58,7 +58,7 @@ public void SerializeAsV3(IOpenApiWriter writer)
writer.WriteOptionalMap(OpenApiConstants.Variables, Variables, (w, v) => v.SerializeAsV3(w));
// specification extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi3_0);
writer.WriteEndObject();
}
diff --git a/src/Microsoft.OpenApi/Models/OpenApiServerVariable.cs b/src/Microsoft.OpenApi/Models/OpenApiServerVariable.cs
index 695f09965..3a8c462c5 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiServerVariable.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiServerVariable.cs
@@ -56,7 +56,7 @@ public void SerializeAsV3(IOpenApiWriter writer)
writer.WriteOptionalCollection(OpenApiConstants.Enum, Enum, (w, s) => w.WriteValue(s));
// specification extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi3_0);
writer.WriteEndObject();
}
diff --git a/src/Microsoft.OpenApi/Models/OpenApiTag.cs b/src/Microsoft.OpenApi/Models/OpenApiTag.cs
index dc3462191..10e4bf0d1 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiTag.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiTag.cs
@@ -79,7 +79,7 @@ public void SerializeAsV3WithoutReference(IOpenApiWriter writer)
writer.WriteOptionalObject(OpenApiConstants.ExternalDocs, ExternalDocs, (w, e) => e.SerializeAsV3(w));
// extensions.
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi3_0);
writer.WriteEndObject();
}
@@ -120,7 +120,7 @@ public void SerializeAsV2WithoutReference(IOpenApiWriter writer)
writer.WriteOptionalObject(OpenApiConstants.ExternalDocs, ExternalDocs, (w, e) => e.SerializeAsV2(w));
// extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi2_0);
writer.WriteEndObject();
}
diff --git a/src/Microsoft.OpenApi/Models/OpenApiXml.cs b/src/Microsoft.OpenApi/Models/OpenApiXml.cs
index 3d4ae7bf9..24d084eb9 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiXml.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiXml.cs
@@ -51,7 +51,7 @@ public class OpenApiXml : IOpenApiSerializable, IOpenApiExtensible
///
public void SerializeAsV3(IOpenApiWriter writer)
{
- Write(writer);
+ Write(writer, OpenApiSpecVersion.OpenApi3_0);
}
///
@@ -59,10 +59,10 @@ public void SerializeAsV3(IOpenApiWriter writer)
///
public void SerializeAsV2(IOpenApiWriter writer)
{
- Write(writer);
+ Write(writer, OpenApiSpecVersion.OpenApi2_0);
}
- private void Write(IOpenApiWriter writer)
+ private void Write(IOpenApiWriter writer, OpenApiSpecVersion specVersion)
{
if (writer == null)
{
@@ -87,7 +87,7 @@ private void Write(IOpenApiWriter writer)
writer.WriteProperty(OpenApiConstants.Wrapped, Wrapped, false);
// extensions
- writer.WriteExtensions(Extensions);
+ writer.WriteExtensions(Extensions, specVersion);
writer.WriteEndObject();
}
diff --git a/src/Microsoft.OpenApi/OpenApiSerializerSettings.cs b/src/Microsoft.OpenApi/OpenApiSerializerSettings.cs
deleted file mode 100644
index 1357af105..000000000
--- a/src/Microsoft.OpenApi/OpenApiSerializerSettings.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT license.
-
-namespace Microsoft.OpenApi
-{
- ///
- /// Configuration settings for Open API writers.
- ///
- public sealed class OpenApiSerializerSettings
- {
- ///
- /// Open Api specification version
- ///
- public OpenApiSpecVersion SpecVersion { get; set; } = OpenApiSpecVersion.OpenApi3_0;
-
- ///
- /// Open Api document format.
- ///
- public OpenApiFormat Format { get; set; } = OpenApiFormat.Json;
- }
-}
\ No newline at end of file
diff --git a/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs b/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs
index 3bf99abab..9ed912e4a 100644
--- a/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs
+++ b/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs
@@ -15,7 +15,12 @@ namespace Microsoft.OpenApi.Services
public abstract class OpenApiVisitorBase
{
private readonly Stack _path = new Stack();
-
+
+ ///
+ /// Properties available to identify context of where an object is within OpenAPI Document
+ ///
+ public CurrentKeys CurrentKeys { get; } = new CurrentKeys();
+
///
/// Allow Rule to indicate validation error occured at a deeper context level.
///
@@ -44,8 +49,6 @@ public string PathString
}
}
-
-
///
/// Visits
///
diff --git a/src/Microsoft.OpenApi/Services/OpenApiWalker.cs b/src/Microsoft.OpenApi/Services/OpenApiWalker.cs
index 0e120037d..565d7a34b 100644
--- a/src/Microsoft.OpenApi/Services/OpenApiWalker.cs
+++ b/src/Microsoft.OpenApi/Services/OpenApiWalker.cs
@@ -18,6 +18,7 @@ public class OpenApiWalker
private readonly OpenApiVisitorBase _visitor;
private readonly Stack _schemaLoop = new Stack();
private readonly Stack _pathItemLoop = new Stack();
+
private bool _inComponents = false;
///
@@ -53,6 +54,7 @@ public void Walk(OpenApiDocument doc)
Walk(OpenApiConstants.ExternalDocs, () => Walk(doc.ExternalDocs));
Walk(OpenApiConstants.Tags, () => Walk(doc.Tags));
Walk(doc as IOpenApiExtensible);
+
}
///
@@ -75,7 +77,6 @@ internal void Walk(IList tags)
Walk(i.ToString(), () => Walk(tags[i]));
}
}
-
}
///
@@ -219,7 +220,9 @@ internal void Walk(OpenApiPaths paths)
{
foreach (var pathItem in paths)
{
+ _visitor.CurrentKeys.Path = pathItem.Key;
Walk(pathItem.Key, () => Walk(pathItem.Value));// JSON Pointer uses ~1 as an escape character for /
+ _visitor.CurrentKeys.Path = null;
}
}
}
@@ -280,7 +283,9 @@ internal void Walk(IOpenApiExtensible openApiExtensible)
{
foreach (var item in openApiExtensible.Extensions)
{
+ _visitor.CurrentKeys.Extension = item.Key;
Walk(item.Key, () => Walk(item.Value));
+ _visitor.CurrentKeys.Extension = null;
}
}
}
@@ -340,8 +345,10 @@ internal void Walk(OpenApiCallback callback)
{
foreach (var item in callback.PathItems)
{
+ _visitor.CurrentKeys.Callback = item.Key.ToString();
var pathItem = item.Value;
Walk(item.Key.ToString(), () => Walk(pathItem));
+ _visitor.CurrentKeys.Callback = null;
}
}
}
@@ -392,7 +399,9 @@ internal void Walk(IDictionary serverVariables)
{
foreach (var variable in serverVariables)
{
+ _visitor.CurrentKeys.ServerVariable = variable.Key;
Walk(variable.Key, () => Walk(variable.Value));
+ _visitor.CurrentKeys.ServerVariable = null;
}
}
}
@@ -457,7 +466,9 @@ internal void Walk(IDictionary operations)
{
foreach (var operation in operations)
{
+ _visitor.CurrentKeys.Operation = operation.Key;
Walk(operation.Key.GetDisplayName(), () => Walk(operation.Value));
+ _visitor.CurrentKeys.Operation = null;
}
}
}
@@ -561,7 +572,9 @@ internal void Walk(OpenApiResponses responses)
{
foreach (var response in responses)
{
+ _visitor.CurrentKeys.Response = response.Key;
Walk(response.Key, () => Walk(response.Value));
+ _visitor.CurrentKeys.Response = null;
}
}
Walk(responses as IOpenApiExtensible);
@@ -622,7 +635,9 @@ internal void Walk(IDictionary headers)
{
foreach (var header in headers)
{
+ _visitor.CurrentKeys.Header = header.Key;
Walk(header.Key, () => Walk(header.Value));
+ _visitor.CurrentKeys.Header = null;
}
}
}
@@ -640,9 +655,11 @@ internal void Walk(IDictionary callbacks)
_visitor.Visit(callbacks);
if (callbacks != null)
{
- foreach (var header in callbacks)
+ foreach (var callback in callbacks)
{
- Walk(header.Key, () => Walk(header.Value));
+ _visitor.CurrentKeys.Callback = callback.Key;
+ Walk(callback.Key, () => Walk(callback.Value));
+ _visitor.CurrentKeys.Callback = null;
}
}
}
@@ -662,7 +679,9 @@ internal void Walk(IDictionary content)
{
foreach (var mediaType in content)
{
+ _visitor.CurrentKeys.Content = mediaType.Key;
Walk(mediaType.Key, () => Walk(mediaType.Value));
+ _visitor.CurrentKeys.Content = null;
}
}
}
@@ -701,7 +720,9 @@ internal void Walk(IDictionary encodings)
{
foreach (var item in encodings)
{
+ _visitor.CurrentKeys.Encoding = item.Key;
Walk(item.Key, () => Walk(item.Value));
+ _visitor.CurrentKeys.Encoding = null;
}
}
}
@@ -787,7 +808,9 @@ internal void Walk(IDictionary examples)
{
foreach (var example in examples)
{
+ _visitor.CurrentKeys.Example = example.Key;
Walk(example.Key, () => Walk(example.Value));
+ _visitor.CurrentKeys.Example = null;
}
}
}
@@ -904,7 +927,9 @@ internal void Walk(IDictionary links)
{
foreach (var item in links)
{
+ _visitor.CurrentKeys.Link = item.Key;
Walk(item.Key, () => Walk(item.Value));
+ _visitor.CurrentKeys.Link = null;
}
}
}
@@ -1045,4 +1070,65 @@ private void ExitComponents()
_inComponents = false;
}
}
+
+ ///
+ /// Object containing contextual information based on where the walker is currently referencing in an OpenApiDocument
+ ///
+ public class CurrentKeys
+ {
+ ///
+ /// Current Path key
+ ///
+ public string Path { get; set; }
+
+ ///
+ /// Current Operation Type
+ ///
+ public OperationType? Operation { get; set; }
+
+ ///
+ /// Current Response Status Code
+ ///
+ public string Response { get; set; }
+
+ ///
+ /// Current Content Media Type
+ ///
+ public string Content { get; set; }
+
+ ///
+ /// Current Callback Key
+ ///
+ public string Callback { get; set; }
+
+ ///
+ /// Current Link Key
+ ///
+ public string Link { get; set; }
+
+ ///
+ /// Current Header Key
+ ///
+ public string Header { get; internal set; }
+
+ ///
+ /// Current Encoding Key
+ ///
+ public string Encoding { get; internal set; }
+
+ ///
+ /// Current Example Key
+ ///
+ public string Example { get; internal set; }
+
+ ///
+ /// Current Extension Key
+ ///
+ public string Extension { get; internal set; }
+
+ ///
+ /// Current ServerVariable
+ ///
+ public string ServerVariable { get; internal set; }
+ }
}
\ No newline at end of file
diff --git a/src/Microsoft.OpenApi/Writers/OpenApiJsonWriter.cs b/src/Microsoft.OpenApi/Writers/OpenApiJsonWriter.cs
index 1a7276d39..19e5b16ab 100644
--- a/src/Microsoft.OpenApi/Writers/OpenApiJsonWriter.cs
+++ b/src/Microsoft.OpenApi/Writers/OpenApiJsonWriter.cs
@@ -14,18 +14,7 @@ public class OpenApiJsonWriter : OpenApiWriterBase
/// Initializes a new instance of the class.
///
/// The text writer.
- public OpenApiJsonWriter(TextWriter textWriter)
- : this(textWriter, new OpenApiSerializerSettings())
- {
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The text writer.
- /// The writer settings.
- public OpenApiJsonWriter(TextWriter textWriter, OpenApiSerializerSettings settings)
- : base(textWriter, settings)
+ public OpenApiJsonWriter(TextWriter textWriter) : base(textWriter)
{
}
diff --git a/src/Microsoft.OpenApi/Writers/OpenApiWriterAnyExtensions.cs b/src/Microsoft.OpenApi/Writers/OpenApiWriterAnyExtensions.cs
index f5551cea5..0b39abc51 100644
--- a/src/Microsoft.OpenApi/Writers/OpenApiWriterAnyExtensions.cs
+++ b/src/Microsoft.OpenApi/Writers/OpenApiWriterAnyExtensions.cs
@@ -19,7 +19,8 @@ public static class OpenApiWriterAnyExtensions
///
/// The Open API writer.
/// The specification extensions.
- public static void WriteExtensions(this IOpenApiWriter writer, IDictionary extensions)
+ /// Version of the OpenAPI specification that that will be output.
+ public static void WriteExtensions(this IOpenApiWriter writer, IDictionary extensions, OpenApiSpecVersion specVersion)
{
if (writer == null)
{
@@ -31,7 +32,7 @@ public static void WriteExtensions(this IOpenApiWriter writer, IDictionary
private int _indentLevel;
- ///
- /// Settings controlling the format and the version of the serialization.
- ///
- private OpenApiSerializerSettings _settings;
-
///
/// Initializes a new instance of the class.
///
/// The text writer.
- /// The writer settings.
- public OpenApiWriterBase(TextWriter textWriter, OpenApiSerializerSettings settings)
+ public OpenApiWriterBase(TextWriter textWriter)
{
Writer = textWriter;
Writer.NewLine = "\n";
Scopes = new Stack();
- this._settings = settings;
}
///
diff --git a/src/Microsoft.OpenApi/Writers/OpenApiYamlWriter.cs b/src/Microsoft.OpenApi/Writers/OpenApiYamlWriter.cs
index d6a259514..d213e6154 100644
--- a/src/Microsoft.OpenApi/Writers/OpenApiYamlWriter.cs
+++ b/src/Microsoft.OpenApi/Writers/OpenApiYamlWriter.cs
@@ -14,20 +14,10 @@ public class OpenApiYamlWriter : OpenApiWriterBase
/// Initializes a new instance of the class.
///
/// The text writer.
- public OpenApiYamlWriter(TextWriter textWriter)
- : this(textWriter, new OpenApiSerializerSettings())
+ public OpenApiYamlWriter(TextWriter textWriter) : base(textWriter)
{
}
- ///
- /// Initializes a new instance of the class.
- ///
- /// The text writer.
- /// The writer settings.
- public OpenApiYamlWriter(TextWriter textWriter, OpenApiSerializerSettings settings)
- : base(textWriter, settings)
- {
- }
///
/// Base Indentation Level.
diff --git a/src/Microsoft.OpenApi/Writers/SpecialCharacterStringExtensions.cs b/src/Microsoft.OpenApi/Writers/SpecialCharacterStringExtensions.cs
index c9fc3d144..d63b42e32 100644
--- a/src/Microsoft.OpenApi/Writers/SpecialCharacterStringExtensions.cs
+++ b/src/Microsoft.OpenApi/Writers/SpecialCharacterStringExtensions.cs
@@ -196,6 +196,11 @@ internal static string GetYamlCompatibleString(this string input)
///
internal static string GetJsonCompatibleString(this string value)
{
+ if (value == null)
+ {
+ return "null";
+ }
+
// Show the control characters as strings
// http://json.org/
diff --git a/test/Microsoft.OpenApi.Readers.Tests/ParseNodeTests.cs b/test/Microsoft.OpenApi.Readers.Tests/ParseNodeTests.cs
new file mode 100644
index 000000000..a6a8e124c
--- /dev/null
+++ b/test/Microsoft.OpenApi.Readers.Tests/ParseNodeTests.cs
@@ -0,0 +1,35 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license.
+
+using System.Collections.Generic;
+using FluentAssertions;
+using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Readers;
+using Microsoft.OpenApi.Readers.Exceptions;
+using Xunit;
+
+namespace Microsoft.OpenApi.Tests
+{
+ public class ParseNodeTests
+ {
+ [Fact]
+ public void BrokenSimpleList()
+ {
+ var input = @"swagger: 2.0
+info:
+ title: hey
+ version: 1.0.0
+schemes: [ { ""hello"" }]
+paths: { }";
+
+ var reader = new OpenApiStringReader();
+ reader.Read(input, out var diagnostic);
+
+ diagnostic.Errors.ShouldBeEquivalentTo(new List() {
+ new OpenApiError(new OpenApiReaderException("Expected a value.") {
+ Pointer = "#line=4"
+ })
+ });
+ }
+ }
+}
diff --git a/test/Microsoft.OpenApi.Readers.Tests/TestCustomExtension.cs b/test/Microsoft.OpenApi.Readers.Tests/TestCustomExtension.cs
index a7eddb672..e3dbfa19b 100644
--- a/test/Microsoft.OpenApi.Readers.Tests/TestCustomExtension.cs
+++ b/test/Microsoft.OpenApi.Readers.Tests/TestCustomExtension.cs
@@ -26,7 +26,7 @@ public void ParseCustomExtension()
";
var settings = new OpenApiReaderSettings()
{
- ExtensionParsers = { { "x-foo", (a) => {
+ ExtensionParsers = { { "x-foo", (a,v) => {
var fooNode = (OpenApiObject)a;
return new FooExtension() {
Bar = (fooNode["bar"] as OpenApiString)?.Value,
@@ -54,7 +54,7 @@ internal class FooExtension : IOpenApiExtension, IOpenApiElement
public string Bar { get; set; }
- public void Write(IOpenApiWriter writer)
+ public void Write(IOpenApiWriter writer, OpenApiSpecVersion specVersion)
{
writer.WriteStartObject();
writer.WriteProperty("baz", Baz);
diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiDocumentTests.cs
new file mode 100644
index 000000000..6850628bd
--- /dev/null
+++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiDocumentTests.cs
@@ -0,0 +1,70 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license.
+
+using System.Collections.Generic;
+using FluentAssertions;
+using Microsoft.OpenApi.Exceptions;
+using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Readers.Exceptions;
+using Xunit;
+
+namespace Microsoft.OpenApi.Readers.Tests.V2Tests
+{
+ public class OpenApiDocumentTests
+ {
+ [Fact]
+ public void ShouldThrowWhenReferenceTypeIsInvalid()
+ {
+ var input = @"
+swagger: 2.0
+info:
+ title: test
+ version: 1.0.0
+paths:
+ '/':
+ get:
+ responses:
+ '200':
+ description: ok
+ schema:
+ $ref: '#/defi888nition/does/notexist'
+";
+
+ var reader = new OpenApiStringReader();
+ var doc = reader.Read(input, out var diagnostic);
+
+ diagnostic.Errors.ShouldBeEquivalentTo(new List {
+ new OpenApiError( new OpenApiException("Unknown reference type 'defi888nition'")) });
+ doc.Should().NotBeNull();
+ }
+
+
+ [Fact]
+ public void ShouldThrowWhenReferenceDoesNotExist()
+ {
+ var input = @"
+swagger: 2.0
+info:
+ title: test
+ version: 1.0.0
+paths:
+ '/':
+ get:
+ produces: ['application/json']
+ responses:
+ '200':
+ description: ok
+ schema:
+ $ref: '#/definitions/doesnotexist'
+";
+
+ var reader = new OpenApiStringReader();
+
+ var doc = reader.Read(input, out var diagnostic);
+
+ diagnostic.Errors.ShouldBeEquivalentTo(new List {
+ new OpenApiError( new OpenApiException("Invalid Reference identifier 'doesnotexist'.")) });
+ doc.Should().NotBeNull();
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiServerTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiServerTests.cs
new file mode 100644
index 000000000..141f30053
--- /dev/null
+++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiServerTests.cs
@@ -0,0 +1,261 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace Microsoft.OpenApi.Readers.Tests.V2Tests
+{
+ public class OpenApiServerTests
+ {
+ [Fact]
+ public void NoServer()
+ {
+ var input = @"
+swagger: 2.0
+info:
+ title: test
+ version: 1.0.0
+paths: {}
+";
+ var reader = new OpenApiStringReader(new OpenApiReaderSettings() {
+ });
+
+ var doc = reader.Read(input, out var diagnostic);
+
+ Assert.Empty(doc.Servers);
+ }
+
+ [Fact]
+ public void JustSchemeNoDefault()
+ {
+ var input = @"
+swagger: 2.0
+info:
+ title: test
+ version: 1.0.0
+schemes:
+ - http
+paths: {}
+";
+ var reader = new OpenApiStringReader(new OpenApiReaderSettings()
+ {
+ });
+
+ var doc = reader.Read(input, out var diagnostic);
+
+ Assert.Equal(0, doc.Servers.Count);
+ }
+
+ [Fact]
+ public void JustHostNoDefault()
+ {
+ var input = @"
+swagger: 2.0
+info:
+ title: test
+ version: 1.0.0
+host: www.foo.com
+paths: {}
+";
+ var reader = new OpenApiStringReader(new OpenApiReaderSettings()
+ {
+ });
+
+ var doc = reader.Read(input, out var diagnostic);
+
+ var server = doc.Servers.First();
+ Assert.Equal(1, doc.Servers.Count);
+ Assert.Equal("//www.foo.com", server.Url);
+ }
+
+ [Fact]
+ public void JustBasePathNoDefault()
+ {
+ var input = @"
+swagger: 2.0
+info:
+ title: test
+ version: 1.0.0
+basePath: /baz
+paths: {}
+";
+ var reader = new OpenApiStringReader(new OpenApiReaderSettings()
+ {
+ });
+
+ var doc = reader.Read(input, out var diagnostic);
+
+ var server = doc.Servers.First();
+ Assert.Equal(1, doc.Servers.Count);
+ Assert.Equal("/baz", server.Url);
+ }
+
+ [Fact]
+ public void JustSchemeWithCustomHost()
+ {
+ var input = @"
+swagger: 2.0
+info:
+ title: test
+ version: 1.0.0
+schemes:
+ - http
+paths: {}
+";
+ var reader = new OpenApiStringReader(new OpenApiReaderSettings()
+ {
+ BaseUrl = new Uri("https://bing.com/foo")
+ });
+
+ var doc = reader.Read(input, out var diagnostic);
+
+ var server = doc.Servers.First();
+ Assert.Equal(1, doc.Servers.Count);
+ Assert.Equal("http://bing.com/foo", server.Url);
+ }
+
+ [Fact]
+ public void JustSchemeWithCustomHostWithEmptyPath()
+ {
+ var input = @"
+swagger: 2.0
+info:
+ title: test
+ version: 1.0.0
+schemes:
+ - http
+paths: {}
+";
+ var reader = new OpenApiStringReader(new OpenApiReaderSettings()
+ {
+ BaseUrl = new Uri("https://bing.com")
+ });
+
+ var doc = reader.Read(input, out var diagnostic);
+
+ var server = doc.Servers.First();
+ Assert.Equal(1, doc.Servers.Count);
+ Assert.Equal("http://bing.com", server.Url);
+ }
+
+ [Fact]
+ public void JustBasePathWithCustomHost()
+ {
+ var input = @"
+swagger: 2.0
+info:
+ title: test
+ version: 1.0.0
+basePath: /api
+paths: {}
+";
+ var reader = new OpenApiStringReader(new OpenApiReaderSettings()
+ {
+ BaseUrl = new Uri("https://bing.com")
+ });
+
+ var doc = reader.Read(input, out var diagnostic);
+
+ var server = doc.Servers.First();
+ Assert.Equal(1, doc.Servers.Count);
+ Assert.Equal("https://bing.com/api", server.Url);
+ }
+
+ [Fact]
+ public void JustHostWithCustomHost()
+ {
+ var input = @"
+swagger: 2.0
+info:
+ title: test
+ version: 1.0.0
+host: www.example.com
+paths: {}
+";
+ var reader = new OpenApiStringReader(new OpenApiReaderSettings()
+ {
+ BaseUrl = new Uri("https://bing.com")
+ });
+
+ var doc = reader.Read(input, out var diagnostic);
+
+ var server = doc.Servers.First();
+ Assert.Equal(1, doc.Servers.Count);
+ Assert.Equal("https://www.example.com", server.Url);
+ }
+
+ [Fact]
+ public void JustHostWithCustomHostWithApi()
+ {
+ var input = @"
+swagger: 2.0
+info:
+ title: test
+ version: 1.0.0
+host: prod.bing.com
+paths: {}
+";
+ var reader = new OpenApiStringReader(new OpenApiReaderSettings()
+ {
+ BaseUrl = new Uri("https://dev.bing.com/api")
+ });
+
+ var doc = reader.Read(input, out var diagnostic);
+
+ var server = doc.Servers.First();
+ Assert.Equal(1, doc.Servers.Count);
+ Assert.Equal("https://prod.bing.com/api", server.Url);
+ }
+
+ [Fact]
+ public void MultipleServers()
+ {
+ var input = @"
+swagger: 2.0
+info:
+ title: test
+ version: 1.0.0
+schemes:
+ - http
+ - https
+paths: {}
+";
+ var reader = new OpenApiStringReader(new OpenApiReaderSettings()
+ {
+ BaseUrl = new Uri("https://dev.bing.com/api")
+ });
+
+ var doc = reader.Read(input, out var diagnostic);
+
+ var server = doc.Servers.First();
+ Assert.Equal(2, doc.Servers.Count);
+ Assert.Equal("http://dev.bing.com/api", server.Url);
+ Assert.Equal("https://dev.bing.com/api", doc.Servers.Last().Url);
+ }
+
+ [Fact]
+ public void LocalHostWithCustomHost()
+ {
+ var input = @"
+swagger: 2.0
+info:
+ title: test
+ version: 1.0.0
+host: localhost:23232
+paths: {}
+";
+ var reader = new OpenApiStringReader(new OpenApiReaderSettings()
+ {
+ BaseUrl = new Uri("https://bing.com")
+ });
+
+ var doc = reader.Read(input, out var diagnostic);
+
+ var server = doc.Servers.First();
+ Assert.Equal(1, doc.Servers.Count);
+ Assert.Equal("https://localhost:23232", server.Url);
+ }
+ }
+}
diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs
index b8ed90a87..8117e1c57 100644
--- a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs
+++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs
@@ -46,7 +46,8 @@ public void ParseDocumentFromInlineStringShouldSucceed()
{
Title = "Simple Document",
Version = "0.9.1"
- }
+ },
+ Paths = new OpenApiPaths()
});
context.ShouldBeEquivalentTo(
@@ -83,7 +84,8 @@ public void ParseBasicDocumentWithMultipleServersShouldSucceed()
Url = new Uri("https://www.example.org/api").ToString(),
Description = "The https endpoint"
}
- }
+ },
+ Paths = new OpenApiPaths()
});
}
}
@@ -101,7 +103,8 @@ public void ParseBrokenMinimalDocumentShouldYieldExpectedDiagnostic()
Info = new OpenApiInfo
{
Version = "0.9"
- }
+ },
+ Paths = new OpenApiPaths()
});
diagnostic.ShouldBeEquivalentTo(
@@ -130,7 +133,8 @@ public void ParseMinimalDocumentShouldSucceed()
{
Title = "Simple Document",
Version = "0.9.1"
- }
+ },
+ Paths = new OpenApiPaths()
});
diagnostic.ShouldBeEquivalentTo(
diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiSchemaTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiSchemaTests.cs
index 6dffd33f6..7fdc2145d 100644
--- a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiSchemaTests.cs
+++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiSchemaTests.cs
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
+using System.Collections.Generic;
using System.IO;
using System.Linq;
using FluentAssertions;
@@ -47,6 +48,109 @@ public void ParsePrimitiveSchemaShouldSucceed()
}
}
+ [Fact]
+ public void ParsePrimitiveSchemaFragmentShouldSucceed()
+ {
+ using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "primitiveSchema.yaml")))
+ {
+ var reader = new OpenApiStreamReader();
+ var diagnostic = new OpenApiDiagnostic();
+
+ // Act
+ var schema = reader.ReadFragment(stream, OpenApiSpecVersion.OpenApi3_0, out diagnostic);
+
+ // Assert
+ diagnostic.ShouldBeEquivalentTo(new OpenApiDiagnostic());
+
+ schema.ShouldBeEquivalentTo(
+ new OpenApiSchema
+ {
+ Type = "string",
+ Format = "email"
+ });
+ }
+ }
+
+ [Fact]
+ public void ParsePrimitiveStringSchemaFragmentShouldSucceed()
+ {
+ var input = @"
+{ ""type"": ""integer"",
+""format"": ""int64"",
+""default"": 88
+}
+";
+ var reader = new OpenApiStringReader();
+ var diagnostic = new OpenApiDiagnostic();
+
+ // Act
+ var schema = reader.ReadFragment(input, OpenApiSpecVersion.OpenApi3_0, out diagnostic);
+
+ // Assert
+ diagnostic.ShouldBeEquivalentTo(new OpenApiDiagnostic());
+
+ schema.ShouldBeEquivalentTo(
+ new OpenApiSchema
+ {
+ Type = "integer",
+ Format = "int64",
+ Default = new OpenApiInteger(88)
+ });
+ }
+
+ [Fact]
+ public void ParseExampleStringFragmentShouldSucceed()
+ {
+ var input = @"
+{
+ ""foo"": ""bar"",
+ ""baz"": [ 1,2]
+}";
+ var reader = new OpenApiStringReader();
+ var diagnostic = new OpenApiDiagnostic();
+
+ // Act
+ var openApiAny = reader.ReadFragment(input, OpenApiSpecVersion.OpenApi3_0, out diagnostic);
+
+ // Assert
+ diagnostic.ShouldBeEquivalentTo(new OpenApiDiagnostic());
+
+ openApiAny.ShouldBeEquivalentTo(
+ new OpenApiObject
+ {
+ ["foo"] = new OpenApiString("bar"),
+ ["baz"] = new OpenApiArray() {
+ new OpenApiInteger(1),
+ new OpenApiInteger(2)
+ }
+ });
+ }
+
+ [Fact]
+ public void ParseEnumFragmentShouldSucceed()
+ {
+ var input = @"
+[
+ ""foo"",
+ ""baz""
+]";
+ var reader = new OpenApiStringReader();
+ var diagnostic = new OpenApiDiagnostic();
+
+ // Act
+ var openApiAny = reader.ReadFragment(input, OpenApiSpecVersion.OpenApi3_0, out diagnostic);
+
+ // Assert
+ diagnostic.ShouldBeEquivalentTo(new OpenApiDiagnostic());
+
+ openApiAny.ShouldBeEquivalentTo(
+ new OpenApiArray
+ {
+ new OpenApiString("foo"),
+ new OpenApiString("baz")
+ });
+ }
+
[Fact]
public void ParseSimpleSchemaShouldSucceed()
{
@@ -97,6 +201,44 @@ public void ParseSimpleSchemaShouldSucceed()
}
}
+ [Fact]
+ public void ParsePathFragmentShouldSucceed()
+ {
+ var input = @"
+summary: externally referenced path item
+get:
+ responses:
+ '200':
+ description: Ok
+";
+ var reader = new OpenApiStringReader();
+ var diagnostic = new OpenApiDiagnostic();
+
+ // Act
+ var openApiAny = reader.ReadFragment(input, OpenApiSpecVersion.OpenApi3_0, out diagnostic);
+
+ // Assert
+ diagnostic.ShouldBeEquivalentTo(new OpenApiDiagnostic());
+
+ openApiAny.ShouldBeEquivalentTo(
+ new OpenApiPathItem
+ {
+ Summary = "externally referenced path item",
+ Operations = new Dictionary
+ {
+ [OperationType.Get] = new OpenApiOperation()
+ {
+ Responses = new OpenApiResponses
+ {
+ ["200"] = new OpenApiResponse {
+ Description = "Ok"
+ }
+ }
+ }
+ }
+ });
+ }
+
[Fact]
public void ParseDictionarySchemaShouldSucceed()
{
diff --git a/test/Microsoft.OpenApi.SmokeTests/ApiGurus.cs b/test/Microsoft.OpenApi.SmokeTests/ApiGurus.cs
index 1dbb24f16..7c8418115 100644
--- a/test/Microsoft.OpenApi.SmokeTests/ApiGurus.cs
+++ b/test/Microsoft.OpenApi.SmokeTests/ApiGurus.cs
@@ -27,6 +27,7 @@ public ApisGuruTests(ITestOutputHelper output)
static ApisGuruTests()
{
+ System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
_httpClient = new HttpClient(new HttpClientHandler()
{
AutomaticDecompression = DecompressionMethods.GZip
@@ -69,7 +70,7 @@ JToken GetProp(JToken obj, string prop)
}
}
- [Theory(DisplayName = "APIs.guru")]
+ // Disable as some APIs are currently invalid [Theory(DisplayName = "APIs.guru")]
[MemberData(nameof(GetSchemas))]
public async Task EnsureThatICouldParse(string url)
{
diff --git a/test/Microsoft.OpenApi.Tests/Expressions/RuntimeExpressionTests.cs b/test/Microsoft.OpenApi.Tests/Expressions/RuntimeExpressionTests.cs
index c583213bc..d5ac2fb96 100644
--- a/test/Microsoft.OpenApi.Tests/Expressions/RuntimeExpressionTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Expressions/RuntimeExpressionTests.cs
@@ -5,6 +5,7 @@
using Microsoft.OpenApi.Expressions;
using Microsoft.OpenApi.Properties;
using System;
+using System.Linq;
using Xunit;
namespace Microsoft.OpenApi.Tests.Writers
@@ -171,5 +172,83 @@ public void CompareRuntimeExpressionWorks(string expression)
Assert.NotSame(runtimeExpression1, runtimeExpression2);
Assert.Equal(runtimeExpression1, runtimeExpression2);
}
+
+
+ [Fact]
+ public void CompositeRuntimeExpressionContainsExpression()
+ {
+ // Arrange
+ string expression = "This is a composite expression {$url} yay";
+
+ // Act
+ var runtimeExpression = RuntimeExpression.Build(expression);
+
+ // Assert
+ Assert.NotNull(runtimeExpression);
+ var response = Assert.IsType(runtimeExpression);
+ Assert.Equal(expression, response.Expression);
+
+ var compositeExpression = runtimeExpression as CompositeExpression;
+ Assert.Single(compositeExpression.ContainedExpressions);
+
+ }
+
+ [Fact]
+ public void CompositeRuntimeExpressionContainsMultipleExpressions()
+ {
+ // Arrange
+ string expression = "This is a composite expression {$url} yay and {$request.header.foo}";
+
+ // Act
+ var runtimeExpression = RuntimeExpression.Build(expression);
+
+ // Assert
+ Assert.NotNull(runtimeExpression);
+ var response = Assert.IsType(runtimeExpression);
+ Assert.Equal(expression, response.Expression);
+
+ var compositeExpression = runtimeExpression as CompositeExpression;
+ Assert.Equal(2,compositeExpression.ContainedExpressions.Count);
+
+ Assert.IsType(compositeExpression.ContainedExpressions.First());
+ Assert.IsType(compositeExpression.ContainedExpressions.Last());
+ }
+
+
+
+ [Fact]
+ public void CompositeRuntimeExpressionForWebHook()
+ {
+ // Arrange
+ string expression = "http://notificationServer.com?transactionId={$request.body#/id}&email={$request.body#/email}";
+
+ // Act
+ var runtimeExpression = RuntimeExpression.Build(expression);
+
+ // Assert
+ Assert.NotNull(runtimeExpression);
+ var response = Assert.IsType(runtimeExpression);
+ Assert.Equal(expression, response.Expression);
+
+ var compositeExpression = runtimeExpression as CompositeExpression;
+ Assert.Equal(2, compositeExpression.ContainedExpressions.Count);
+
+ Assert.IsType(compositeExpression.ContainedExpressions.First());
+ Assert.IsType(compositeExpression.ContainedExpressions.Last());
+ }
+
+ [Theory]
+ [InlineData("This is a composite expression yay and {} and {$sddsd}")]
+ [InlineData("This is a composite expression {url} yay and {} and {$url}")]
+ public void CompositeRuntimeExpressionContainsInvalidExpressions(string expression)
+ {
+ // Arrange
+
+ // Act
+ Action test = () => RuntimeExpression.Build(expression);
+
+ // Assert
+ Assert.Throws(test);
+ }
}
}
diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs
index 6d24d71a8..ec7e24063 100644
--- a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs
@@ -2496,5 +2496,60 @@ public void SerializeSimpleDocumentWithTopLevelSelfReferencingWithOtherPropertie
expected = expected.MakeLineBreaksEnvironmentNeutral();
actual.Should().Be(expected);
}
+
+ [Fact]
+ public void SerializeDocumentWithReferenceButNoComponents()
+ {
+ // Arrange
+ var document = new OpenApiDocument()
+ {
+ Info = new OpenApiInfo
+ {
+ Title = "Test",
+ Version = "1.0.0"
+ },
+ Paths = new OpenApiPaths
+ {
+ ["/"] = new OpenApiPathItem
+ {
+ Operations = new Dictionary
+ {
+ [OperationType.Get] = new OpenApiOperation
+ {
+ Responses = new OpenApiResponses
+ {
+ ["200"] = new OpenApiResponse
+ {
+ Content = new Dictionary()
+ {
+ ["application/json"] = new OpenApiMediaType
+ {
+ Schema = new OpenApiSchema
+ {
+ Reference = new OpenApiReference
+ {
+ Id = "test",
+ Type = ReferenceType.Schema
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ };
+
+
+ var reference = document.Paths["/"].Operations[OperationType.Get].Responses["200"].Content["application/json"].Schema.Reference;
+
+ // Act
+ var actual = document.Serialize(OpenApiSpecVersion.OpenApi2_0, OpenApiFormat.Json);
+
+ // Assert
+ Assert.NotEmpty(actual);
+ }
}
}
\ No newline at end of file
diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiValidatorTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiValidatorTests.cs
index 8cec27ad4..fba3965cf 100644
--- a/test/Microsoft.OpenApi.Tests/Services/OpenApiValidatorTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiValidatorTests.cs
@@ -35,6 +35,7 @@ public void ResponseMustHaveADescription()
Title = "foo",
Version = "1.2.2"
};
+ openApiDocument.Paths = new OpenApiPaths();
openApiDocument.Paths.Add(
"/test",
new OpenApiPathItem
@@ -66,13 +67,14 @@ public void ResponseMustHaveADescription()
[Fact]
public void ServersShouldBeReferencedByIndex()
{
- var openApiDocument = new OpenApiDocument();
- openApiDocument.Info = new OpenApiInfo()
+ var openApiDocument = new OpenApiDocument
{
- Title = "foo",
- Version = "1.2.2"
- };
- openApiDocument.Servers = new List {
+ Info = new OpenApiInfo()
+ {
+ Title = "foo",
+ Version = "1.2.2"
+ },
+ Servers = new List {
new OpenApiServer
{
Url = "http://example.org"
@@ -80,9 +82,11 @@ public void ServersShouldBeReferencedByIndex()
new OpenApiServer
{
- }
+ },
+ },
+ Paths = new OpenApiPaths()
};
-
+
var validator = new OpenApiValidator(ValidationRuleSet.GetDefaultRuleSet());
var walker = new OpenApiWalker(validator);
walker.Walk(openApiDocument);
@@ -111,11 +115,14 @@ public void ValidateCustomExtension()
}
}));
- var openApiDocument = new OpenApiDocument();
- openApiDocument.Info = new OpenApiInfo()
+ var openApiDocument = new OpenApiDocument
{
- Title = "foo",
- Version = "1.2.2"
+ Info = new OpenApiInfo()
+ {
+ Title = "foo",
+ Version = "1.2.2"
+ },
+ Paths = new OpenApiPaths()
};
var fooExtension = new FooExtension()
@@ -145,7 +152,7 @@ internal class FooExtension : IOpenApiExtension, IOpenApiElement
public string Bar { get; set; }
- public void Write(IOpenApiWriter writer)
+ public void Write(IOpenApiWriter writer, OpenApiSpecVersion specVersion)
{
writer.WriteStartObject();
writer.WriteProperty("baz", Baz);
diff --git a/test/Microsoft.OpenApi.Tests/Walkers/WalkerLocationTests.cs b/test/Microsoft.OpenApi.Tests/Walkers/WalkerLocationTests.cs
index 933a25274..9606f0029 100644
--- a/test/Microsoft.OpenApi.Tests/Walkers/WalkerLocationTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Walkers/WalkerLocationTests.cs
@@ -23,7 +23,6 @@ public void LocateTopLevelObjects()
locator.Locations.ShouldBeEquivalentTo(new List {
"#/servers",
- "#/paths",
"#/tags"
});
}
@@ -37,6 +36,7 @@ public void LocateTopLevelArrayItems()
new OpenApiServer(),
new OpenApiServer()
},
+ Paths = new OpenApiPaths(),
Tags = new List()
{
new OpenApiTag()
@@ -61,6 +61,7 @@ public void LocateTopLevelArrayItems()
public void LocatePathOperationContentSchema()
{
var doc = new OpenApiDocument();
+ doc.Paths = new OpenApiPaths();
doc.Paths.Add("/test", new OpenApiPathItem()
{
Operations = new Dictionary()
@@ -106,6 +107,8 @@ public void LocatePathOperationContentSchema()
"#/paths/~1test/get/responses/200/content/application~1json/schema",
});
+
+ locator.Keys.ShouldAllBeEquivalentTo(new List { "/test","Get","200", "application/json" });
}
[Fact]
@@ -124,6 +127,7 @@ public void WalkDOMWithCycles()
var doc = new OpenApiDocument()
{
+ Paths = new OpenApiPaths(),
Components = new OpenApiComponents()
{
Schemas = new Dictionary
@@ -151,6 +155,8 @@ public void WalkDOMWithCycles()
internal class LocatorVisitor : OpenApiVisitorBase
{
public List Locations = new List();
+ public List Keys = new List();
+
public override void Visit(OpenApiInfo info)
{
Locations.Add(this.PathString);
@@ -173,6 +179,7 @@ public override void Visit(OpenApiPaths paths)
public override void Visit(OpenApiPathItem pathItem)
{
+ Keys.Add(CurrentKeys.Path);
Locations.Add(this.PathString);
}
@@ -183,10 +190,12 @@ public override void Visit(OpenApiResponses responses)
public override void Visit(OpenApiOperation operation)
{
+ Keys.Add(CurrentKeys.Operation.ToString());
Locations.Add(this.PathString);
}
public override void Visit(OpenApiResponse response)
{
+ Keys.Add(CurrentKeys.Response);
Locations.Add(this.PathString);
}
@@ -197,6 +206,7 @@ public override void Visit(IDictionary content)
public override void Visit(OpenApiMediaType mediaType)
{
+ Keys.Add(CurrentKeys.Content);
Locations.Add(this.PathString);
}