Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
7b42b1b
Use rules to validate required properties instead of reader
darrelmiller Jan 7, 2018
4ffe083
Use extension to validate
darrelmiller Jan 7, 2018
2789c0b
Added context to validators and removed required check from reader
darrelmiller Jan 7, 2018
fed72e4
Removed remnants of required
darrelmiller Jan 7, 2018
909a0d7
Merge remote-tracking branch 'origin/master' into dm/validation
darrelmiller Jan 7, 2018
7ca70f2
Added support for custom extensions
darrelmiller Jan 9, 2018
60a26b2
Merged validation code
darrelmiller Jan 9, 2018
41b049d
Implemented extension validation
darrelmiller Jan 12, 2018
048518a
Extension validation
darrelmiller Jan 13, 2018
a42e62a
Fixes based on PR reviews
darrelmiller Jan 16, 2018
7e1f151
Merge branch 'master' into dm/validatedextensions
darrelmiller Jan 17, 2018
e9d2569
Validation fixes based on PR review
darrelmiller Jan 22, 2018
3fd21d2
Merge remote-tracking branch 'origin/master' into dm/validatedextensions
darrelmiller Jan 22, 2018
824e3e2
Merge remote-tracking branch 'origin/master' into dm/validatedextensions
darrelmiller Jan 25, 2018
acd1a45
Fixed extension tests
darrelmiller Jan 26, 2018
d4597fc
Refactored walker
darrelmiller Jan 27, 2018
c19a883
Updated Walker to clean up
darrelmiller Jan 27, 2018
6d7eca5
Merge remote-tracking branch 'origin/master' into dm/validatedextensions
darrelmiller Jan 27, 2018
85ba08c
Walker tests and loop detection
darrelmiller Jan 29, 2018
0d68e70
Added copyright header
darrelmiller Jan 30, 2018
9dba5fd
Changed configuration of Lazy to make it threadsafe
darrelmiller Jan 30, 2018
2e7c33a
Updated visitbility on rules to be consistent
darrelmiller Jan 31, 2018
474944d
Streamlined rule loading code
darrelmiller Jan 31, 2018
576de46
Tried pre-loading the ruleset
darrelmiller Jan 31, 2018
8d44882
Removed use of Lazy
darrelmiller Jan 31, 2018
de7e355
temporarily fixed failing test
darrelmiller Jan 31, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "build",
"type": "shell",
"command": "msbuild",
"args": [
"/property:GenerateFullPaths=true",
"/t:build"
],
"group": "build",
"presentation": {
"reveal": "silent"
},
"problemMatcher": "$msCompile"
},
{
"label": "workbench",
"type": "shell",
"command": "src/Microsoft.OpenApi.WorkBench/bin/Debug/Microsoft.OpenApi.WorkBench.exe",
"problemMatcher": []
}
]
}
29 changes: 29 additions & 0 deletions src/Microsoft.OpenApi.Readers/OpenApiReaderSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Readers.ParseNodes;
using Microsoft.OpenApi.Validations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Microsoft.OpenApi.Readers
{
/// <summary>
/// Configuration settings to control how OpenAPI documents are parsed
/// </summary>
public class OpenApiReaderSettings
{
/// <summary>
/// Dictionary of parsers for converting extensions into strongly typed classes
/// </summary>
public Dictionary<string, Func<IOpenApiAny , IOpenApiExtension>> ExtensionParsers { get; set; } = new Dictionary<string, Func<IOpenApiAny, IOpenApiExtension>>();

/// <summary>
/// Rules to use for validating OpenAPI specification. If none are provided a default set of rules are applied.
/// </summary>
public ValidationRuleSet RuleSet { get; set; }

}
}
32 changes: 30 additions & 2 deletions src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@

using System.IO;
using System.Linq;
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers.Interface;
using Microsoft.OpenApi.Services;
using Microsoft.OpenApi.Validations;
using SharpYaml;
using SharpYaml.Serialization;

Expand All @@ -15,7 +18,17 @@ namespace Microsoft.OpenApi.Readers
/// </summary>
public class OpenApiStreamReader : IOpenApiReader<Stream, OpenApiDiagnostic>
{
private OpenApiReaderSettings _settings;

/// <summary>
/// Create stream reader with custom settings if desired.
/// </summary>
/// <param name="settings"></param>
public OpenApiStreamReader(OpenApiReaderSettings settings = null)
{
_settings = settings ?? new OpenApiReaderSettings();

}
/// <summary>
/// Reads the stream input and parses it into an Open API document.
/// </summary>
Expand All @@ -28,6 +41,7 @@ public OpenApiDocument Read(Stream input, out OpenApiDiagnostic diagnostic)
YamlDocument yamlDocument;
diagnostic = new OpenApiDiagnostic();

// Parse the YAML/JSON
try
{
yamlDocument = LoadYamlDocument(input);
Expand All @@ -38,8 +52,22 @@ public OpenApiDocument Read(Stream input, out OpenApiDiagnostic diagnostic)
return new OpenApiDocument();
}

context = new ParsingContext();
return context.Parse(yamlDocument, diagnostic);
context = new ParsingContext
{
ExtensionParsers = _settings.ExtensionParsers
};

// Parse the OpenAPI Document
var document = context.Parse(yamlDocument, diagnostic);

// Validate the document
var errors = document.Validate(_settings.RuleSet);
foreach (var item in errors)
{
diagnostic.Errors.Add(new OpenApiError(item.ErrorPath, item.ErrorMessage));
}

return document;
}

/// <summary>
Expand Down
13 changes: 12 additions & 1 deletion src/Microsoft.OpenApi.Readers/OpenApiStringReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@ namespace Microsoft.OpenApi.Readers
/// </summary>
public class OpenApiStringReader : IOpenApiReader<string, OpenApiDiagnostic>
{
private readonly OpenApiReaderSettings _settings;

/// <summary>
/// Constructor tha allows reader to use non-default settings
/// </summary>
/// <param name="settings"></param>
public OpenApiStringReader(OpenApiReaderSettings settings = null)
{
_settings = settings ?? new OpenApiReaderSettings();
}

/// <summary>
/// Reads the string input and parses it into an Open API document.
/// </summary>
Expand All @@ -24,7 +35,7 @@ public OpenApiDocument Read(string input, out OpenApiDiagnostic diagnostic)
writer.Flush();
memoryStream.Position = 0;

return new OpenApiStreamReader().Read(memoryStream, out diagnostic);
return new OpenApiStreamReader(_settings).Read(memoryStream, out diagnostic);
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/Microsoft.OpenApi.Readers/ParsingContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers.Exceptions;
Expand All @@ -24,6 +25,9 @@ public class ParsingContext
private readonly Dictionary<string, IOpenApiReferenceable> _referenceStore = new Dictionary<string, IOpenApiReferenceable>();
private readonly Dictionary<string, object> _tempStorage = new Dictionary<string, object>();
private IOpenApiVersionService _versionService;

internal Dictionary<string, Func<IOpenApiAny, IOpenApiExtension>> ExtensionParsers { get; set; } = new Dictionary<string, Func<IOpenApiAny, IOpenApiExtension>>();

internal RootNode RootNode { get; set; }
internal List<OpenApiTag> Tags { get; private set; } = new List<OpenApiTag>();

Expand Down Expand Up @@ -63,6 +67,7 @@ internal OpenApiDocument Parse(YamlDocument yamlDocument, OpenApiDiagnostic diag
return doc;
}


/// <summary>
/// Gets the version of the Open API document.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,8 @@ public static OpenApiDocument LoadOpenApi(RootNode rootNode)

var openApiNode = rootNode.GetMap();

var required = new List<string> {"info", "swagger", "paths"};
ParseMap(openApiNode, openApidoc, _openApiFixedFields, _openApiPatternFields);

ParseMap(openApiNode, openApidoc, _openApiFixedFields, _openApiPatternFields, required);

ReportMissing(openApiNode, required);

// Post Process OpenApi Object
if (openApidoc.Servers == null)
Expand Down
5 changes: 1 addition & 4 deletions src/Microsoft.OpenApi.Readers/V2/OpenApiInfoDeserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,8 @@ public static OpenApiInfo LoadInfo(ParseNode node)
var mapNode = node.CheckMapNode("Info");

var info = new OpenApiInfo();
var required = new List<string> {"title", "version"};

ParseMap(mapNode, info, _infoFixedFields, _infoPatternFields, required);

ReportMissing(node, required);
ParseMap(mapNode, info, _infoFixedFields, _infoPatternFields);

return info;
}
Expand Down
11 changes: 0 additions & 11 deletions src/Microsoft.OpenApi.Readers/V2/OpenApiV2Deserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,6 @@ private static void ParseMap<T>(
}
}

private static void ReportMissing(ParseNode node, IList<string> required)
{
foreach (var error in required.Select(
r => new OpenApiError(
node.Context.GetLocation(),
$"{r} is a required property"))
.ToList())
{
node.Diagnostic.Errors.Add(error);
}
}

private static string LoadString(ParseNode node)
{
Expand Down
18 changes: 14 additions & 4 deletions src/Microsoft.OpenApi.Readers/V3/OpenApiDocumentDeserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
// Licensed under the MIT license.

using System.Collections.Generic;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers.ParseNodes;

Expand Down Expand Up @@ -42,13 +44,21 @@ public static OpenApiDocument LoadOpenApi(RootNode rootNode)

var openApiNode = rootNode.GetMap();

var required = new List<string> {"info", "openapi", "paths"};
ParseMap(openApiNode, openApidoc, _openApiFixedFields, _openApiPatternFields);

ParseMap(openApiNode, openApidoc, _openApiFixedFields, _openApiPatternFields, required);
return openApidoc;
}

ReportMissing(openApiNode, required);

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();
}
}
}
}
4 changes: 2 additions & 2 deletions src/Microsoft.OpenApi.Readers/V3/OpenApiInfoDeserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ internal static partial class OpenApiV3Deserializer

public static PatternFieldMap<OpenApiInfo> InfoPatternFields = new PatternFieldMap<OpenApiInfo>
{
{s => s.StartsWith("x-"), (o, k, n) => o.AddExtension(k, n.CreateAny())}
{s => s.StartsWith("x-"), (o, k, n) => o.Extensions.Add(k,LoadExtension(k, n))}
};

public static OpenApiInfo LoadInfo(ParseNode node)
Expand All @@ -67,7 +67,7 @@ public static OpenApiInfo LoadInfo(ParseNode node)
var info = new OpenApiInfo();
var required = new List<string> {"title", "version"};

ParseMap(mapNode, info, InfoFixedFields, InfoPatternFields, required);
ParseMap(mapNode, info, InfoFixedFields, InfoPatternFields);

return info;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ public static OpenApiParameter LoadParameter(ParseNode node)
var parameter = new OpenApiParameter();
var required = new List<string> {"name", "in"};

ParseMap(mapNode, parameter, _parameterFixedFields, _parameterPatternFields, required);
ParseMap(mapNode, parameter, _parameterFixedFields, _parameterPatternFields);

return parameter;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public static OpenApiResponse LoadResponse(ParseNode node)

var requiredFields = new List<string> {"description"};
var response = new OpenApiResponse();
ParseMap(mapNode, response, _responseFixedFields, _responsePatternFields, requiredFields);
ParseMap(mapNode, response, _responseFixedFields, _responsePatternFields);

return response;
}
Expand Down
20 changes: 1 addition & 19 deletions src/Microsoft.OpenApi.Readers/V3/OpenApiV3Deserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ private static void ParseMap<T>(
MapNode mapNode,
T domainObject,
FixedFieldMap<T> fixedFieldMap,
PatternFieldMap<T> patternFieldMap,
List<string> requiredFields = null)
PatternFieldMap<T> patternFieldMap)
{
if (mapNode == null)
{
Expand All @@ -30,10 +29,8 @@ private static void ParseMap<T>(
foreach (var propertyNode in mapNode)
{
propertyNode.ParseField(domainObject, fixedFieldMap, patternFieldMap);
requiredFields?.Remove(propertyNode.Name);
}

ReportMissing(mapNode, requiredFields);
}

private static RuntimeExpression LoadRuntimeExpression(ParseNode node)
Expand All @@ -60,22 +57,7 @@ private static RuntimeExpressionAnyWrapper LoadRuntimeExpressionAnyWrapper(Parse
};
}

private static void ReportMissing(ParseNode node, IList<string> required)
{
if (required == null || !required.Any())
{
return;
}

foreach (var error in required.Select(
r => new OpenApiError(
node.Context.GetLocation(),
$"{r} is a required property"))
.ToList())
{
node.Diagnostic.Errors.Add(error);
}
}

private static string LoadString(ParseNode node)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.OpenApi/Any/IOpenApiAny.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Microsoft.OpenApi.Any
/// <summary>
/// Base interface for all the types that represent Open API Any.
/// </summary>
public interface IOpenApiAny : IOpenApiElement
public interface IOpenApiAny : IOpenApiElement, IOpenApiExtension
{
/// <summary>
/// Type of an <see cref="IOpenApiAny"/>.
Expand Down
18 changes: 18 additions & 0 deletions src/Microsoft.OpenApi/Any/OpenApiArray.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using Microsoft.OpenApi.Writers;
using System.Collections.Generic;

namespace Microsoft.OpenApi.Any
Expand All @@ -14,5 +15,22 @@ public class OpenApiArray : List<IOpenApiAny>, IOpenApiAny
/// The type of <see cref="IOpenApiAny"/>
/// </summary>
public AnyType AnyType { get; } = AnyType.Array;

/// <summary>
/// Write out contents of OpenApiArray to passed writer
/// </summary>
/// <param name="writer">Instance of JSON or YAML writer.</param>
public void Write(IOpenApiWriter writer)
Copy link
Contributor

@xuzhg xuzhg Jan 13, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public? not internal? #WontFix

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it needs to be public because it is part of an interface that will be implemented by any custom extension implementation.


In reply to: 161353844 [](ancestors = 161353844)

{
writer.WriteStartArray();

foreach (var item in this)
{
writer.WriteAny(item);
}

writer.WriteEndArray();

}
}
}
Loading