diff --git a/install-tool.ps1 b/install-tool.ps1
new file mode 100644
index 000000000..0e6521110
--- /dev/null
+++ b/install-tool.ps1
@@ -0,0 +1,7 @@
+$latest = Get-ChildItem .\artifacts\ Microsoft.OpenApi.Tool* | select-object -Last 1
+$version = $latest.Name.Split(".")[3..5] | join-string -Separator "."
+
+if (Test-Path -Path ./artifacts/openapi-parser.exe) {
+ dotnet tool uninstall --tool-path artifacts Microsoft.OpenApi.Tool
+}
+dotnet tool install --tool-path artifacts --add-source .\artifacts\ --version $version Microsoft.OpenApi.Tool
\ No newline at end of file
diff --git a/src/Microsoft.OpenApi.Tool/Microsoft.OpenApi.Tool.csproj b/src/Microsoft.OpenApi.Tool/Microsoft.OpenApi.Tool.csproj
index 5845ce4f9..40e46f1a4 100644
--- a/src/Microsoft.OpenApi.Tool/Microsoft.OpenApi.Tool.csproj
+++ b/src/Microsoft.OpenApi.Tool/Microsoft.OpenApi.Tool.csproj
@@ -4,13 +4,13 @@
Exe
netcoreapp3.1
true
- openapi
+ openapi-parser
./../../artifacts
1.3.0-preview
-
+
@@ -19,7 +19,7 @@
-
+
diff --git a/src/Microsoft.OpenApi.Tool/OpenApiService.cs b/src/Microsoft.OpenApi.Tool/OpenApiService.cs
index fd42da1a1..c52c08941 100644
--- a/src/Microsoft.OpenApi.Tool/OpenApiService.cs
+++ b/src/Microsoft.OpenApi.Tool/OpenApiService.cs
@@ -2,10 +2,13 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Net;
+using System.Net.Http;
using System.Text;
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers;
+using Microsoft.OpenApi.Services;
using Microsoft.OpenApi.Validations;
using Microsoft.OpenApi.Writers;
@@ -14,48 +17,57 @@ namespace Microsoft.OpenApi.Tool
static class OpenApiService
{
public static void ProcessOpenApiDocument(
- FileInfo input,
+ string input,
FileInfo output,
OpenApiSpecVersion version,
OpenApiFormat format,
bool inline,
bool resolveExternal)
{
+ if (input == null)
+ {
+ throw new ArgumentNullException("input");
+ }
+
+ var stream = GetStream(input);
+
OpenApiDocument document;
- using (Stream stream = input.OpenRead())
+
+ var result = new OpenApiStreamReader(new OpenApiReaderSettings
+ {
+ ReferenceResolution = resolveExternal == true ? ReferenceResolutionSetting.ResolveAllReferences : ReferenceResolutionSetting.ResolveLocalReferences,
+ RuleSet = ValidationRuleSet.GetDefaultRuleSet()
+ }
+ ).ReadAsync(stream).GetAwaiter().GetResult();
+
+ document = result.OpenApiDocument;
+ var context = result.OpenApiDiagnostic;
+
+ if (context.Errors.Count != 0)
{
+ var errorReport = new StringBuilder();
- document = new OpenApiStreamReader(new OpenApiReaderSettings
+ foreach (var error in context.Errors)
{
- ReferenceResolution = resolveExternal == true ? ReferenceResolutionSetting.ResolveAllReferences : ReferenceResolutionSetting.ResolveLocalReferences,
- RuleSet = ValidationRuleSet.GetDefaultRuleSet()
+ errorReport.AppendLine(error.ToString());
}
- ).Read(stream, out var context);
- if (context.Errors.Count != 0)
- {
- var errorReport = new StringBuilder();
-
- foreach (var error in context.Errors)
- {
- errorReport.AppendLine(error.ToString());
- }
- throw new ArgumentException(String.Join(Environment.NewLine, context.Errors.Select(e => e.Message).ToArray()));
- }
+ throw new ArgumentException(String.Join(Environment.NewLine, context.Errors.Select(e => e.Message).ToArray()));
}
using (var outputStream = output?.Create())
{
TextWriter textWriter;
- if (outputStream!=null)
+ if (outputStream != null)
{
textWriter = new StreamWriter(outputStream);
- } else
+ }
+ else
{
textWriter = Console.Out;
}
-
+
var settings = new OpenApiWriterSettings()
{
ReferenceInline = inline == true ? ReferenceInlineSetting.InlineLocalReferences : ReferenceInlineSetting.DoNotInlineReferences
@@ -72,11 +84,67 @@ public static void ProcessOpenApiDocument(
default:
throw new ArgumentException("Unknown format");
}
-
- document.Serialize(writer,version );
+
+ document.Serialize(writer, version);
textWriter.Flush();
}
}
-}
+
+ private static Stream GetStream(string input)
+ {
+ Stream stream;
+ if (input.StartsWith("http"))
+ {
+ var httpClient = new HttpClient(new HttpClientHandler()
+ {
+ SslProtocols = System.Security.Authentication.SslProtocols.Tls12,
+ })
+ {
+ DefaultRequestVersion = HttpVersion.Version20
+ };
+ stream = httpClient.GetStreamAsync(input).Result;
+ }
+ else
+ {
+ var fileInput = new FileInfo(input);
+ stream = fileInput.OpenRead();
+ }
+
+ return stream;
+ }
+
+ internal static void ValidateOpenApiDocument(string input)
+ {
+ if (input == null)
+ {
+ throw new ArgumentNullException("input");
+ }
+
+ var stream = GetStream(input);
+
+ OpenApiDocument document;
+
+ document = new OpenApiStreamReader(new OpenApiReaderSettings
+ {
+ //ReferenceResolution = resolveExternal == true ? ReferenceResolutionSetting.ResolveAllReferences : ReferenceResolutionSetting.ResolveLocalReferences,
+ RuleSet = ValidationRuleSet.GetDefaultRuleSet()
+ }
+ ).Read(stream, out var context);
+
+ if (context.Errors.Count != 0)
+ {
+ foreach (var error in context.Errors)
+ {
+ Console.WriteLine(error.ToString());
+ }
+ }
+
+ var statsVisitor = new StatsVisitor();
+ var walker = new OpenApiWalker(statsVisitor);
+ walker.Walk(document);
+
+ Console.WriteLine(statsVisitor.GetStatisticsReport());
+ }
+ }
}
diff --git a/src/Microsoft.OpenApi.Tool/Program.cs b/src/Microsoft.OpenApi.Tool/Program.cs
index 2c95b954f..446e2829a 100644
--- a/src/Microsoft.OpenApi.Tool/Program.cs
+++ b/src/Microsoft.OpenApi.Tool/Program.cs
@@ -9,23 +9,54 @@ namespace Microsoft.OpenApi.Tool
{
class Program
{
- static async Task Main(string[] args)
+ static async Task OldMain(string[] args)
{
+
var command = new RootCommand
{
- new Option("--input") { Argument = new Argument() },
- new Option("--output") { Argument = new Argument() },
- new Option("--version") { Argument = new Argument() },
- new Option("--format") { Argument = new Argument() },
- new Option("--inline") { Argument = new Argument() },
- new Option("--resolveExternal") { Argument = new Argument() }
+ new Option("--input", "Input OpenAPI description file path or URL", typeof(string) ),
+ new Option("--output","Output OpenAPI description file", typeof(FileInfo), arity: ArgumentArity.ZeroOrOne),
+ new Option("--version", "OpenAPI specification version", typeof(OpenApiSpecVersion)),
+ new Option("--format", "File format",typeof(OpenApiFormat) ),
+ new Option("--inline", "Inline $ref instances", typeof(bool) ),
+ new Option("--resolveExternal","Resolve external $refs", typeof(bool))
};
- command.Handler = CommandHandler.Create(
+ command.Handler = CommandHandler.Create(
OpenApiService.ProcessOpenApiDocument);
// Parse the incoming args and invoke the handler
return await command.InvokeAsync(args);
}
+
+ static async Task Main(string[] args)
+ {
+ var rootCommand = new RootCommand() {
+ };
+
+ var validateCommand = new Command("validate")
+ {
+ new Option("--input", "Input OpenAPI description file path or URL", typeof(string) )
+ };
+ validateCommand.Handler = CommandHandler.Create(OpenApiService.ValidateOpenApiDocument);
+
+ var transformCommand = new Command("transform")
+ {
+ new Option("--input", "Input OpenAPI description file path or URL", typeof(string) ),
+ new Option("--output","Output OpenAPI description file", typeof(FileInfo), arity: ArgumentArity.ZeroOrOne),
+ new Option("--version", "OpenAPI specification version", typeof(OpenApiSpecVersion)),
+ new Option("--format", "File format",typeof(OpenApiFormat) ),
+ new Option("--inline", "Inline $ref instances", typeof(bool) ),
+ new Option("--resolveExternal","Resolve external $refs", typeof(bool))
+ };
+ transformCommand.Handler = CommandHandler.Create(
+ OpenApiService.ProcessOpenApiDocument);
+
+ rootCommand.Add(transformCommand);
+ rootCommand.Add(validateCommand);
+
+ // Parse the incoming args and invoke the handler
+ return await rootCommand.InvokeAsync(args);
+ }
}
}
diff --git a/src/Microsoft.OpenApi.Tool/StatsVisitor.cs b/src/Microsoft.OpenApi.Tool/StatsVisitor.cs
new file mode 100644
index 000000000..3c633d860
--- /dev/null
+++ b/src/Microsoft.OpenApi.Tool/StatsVisitor.cs
@@ -0,0 +1,91 @@
+// 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.Models;
+using Microsoft.OpenApi.Services;
+
+namespace Microsoft.OpenApi.Tool
+{
+ internal class StatsVisitor : OpenApiVisitorBase
+ {
+ public int ParameterCount { get; set; } = 0;
+
+ public override void Visit(OpenApiParameter parameter)
+ {
+ ParameterCount++;
+ }
+
+ public int SchemaCount { get; set; } = 0;
+
+ public override void Visit(OpenApiSchema schema)
+ {
+ SchemaCount++;
+ }
+
+ public int HeaderCount { get; set; } = 0;
+
+ public override void Visit(IDictionary headers)
+ {
+ HeaderCount++;
+ }
+
+ public int PathItemCount { get; set; } = 0;
+
+ public override void Visit(OpenApiPathItem pathItem)
+ {
+ PathItemCount++;
+ }
+
+ public int RequestBodyCount { get; set; } = 0;
+
+ public override void Visit(OpenApiRequestBody requestBody)
+ {
+ RequestBodyCount++;
+ }
+
+ public int ResponseCount { get; set; } = 0;
+
+ public override void Visit(OpenApiResponses response)
+ {
+ ResponseCount++;
+ }
+
+ public int OperationCount { get; set; } = 0;
+
+ public override void Visit(OpenApiOperation operation)
+ {
+ OperationCount++;
+ }
+
+ public int LinkCount { get; set; } = 0;
+
+ public override void Visit(OpenApiLink operation)
+ {
+ LinkCount++;
+ }
+
+ public int CallbackCount { get; set; } = 0;
+
+ public override void Visit(OpenApiCallback callback)
+ {
+ CallbackCount++;
+ }
+
+ public string GetStatisticsReport()
+ {
+ return $"Path Items: {PathItemCount}" + Environment.NewLine
+ + $"Operations: {OperationCount}" + Environment.NewLine
+ + $"Parameters: {ParameterCount}" + Environment.NewLine
+ + $"Request Bodies: {RequestBodyCount}" + Environment.NewLine
+ + $"Responses: {ResponseCount}" + Environment.NewLine
+ + $"Links: {LinkCount}" + Environment.NewLine
+ + $"Callbacks: {CallbackCount}" + Environment.NewLine
+ + $"Schemas: {SchemaCount}" + Environment.NewLine;
+ }
+ }
+}