From a379a3e47a7fdbc23aa2dc5d2c3d044fac89e1e4 Mon Sep 17 00:00:00 2001 From: Senn Geerts Date: Sun, 7 Jul 2024 00:32:09 +0200 Subject: [PATCH] #197 Produce AsyncAPI documents at build time --- Saunter.sln | 17 ++++- examples/StreetlightsAPI/.gitignore | 1 + .../StreetlightsAPI/StreetlightsAPI.csproj | 4 ++ .../AsyncAPI.Saunter.Generator.Build.csproj | 63 +++++++++++++++++++ .../AsyncAPI.Saunter.Generator.Build.props | 11 ++++ .../AsyncAPI.Saunter.Generator.Build.targets | 17 +++++ .../readme.md | 27 ++++++++ .../AsyncAPI.Saunter.Generator.Cli.csproj | 2 +- .../Commands/Tofile.cs | 2 +- .../Commands/TofileInternal.cs | 17 +++-- .../SwashbuckleImport/CommandRunner.cs | 1 + 11 files changed, 149 insertions(+), 13 deletions(-) create mode 100644 examples/StreetlightsAPI/.gitignore create mode 100644 src/AsyncAPI.Saunter.Generator.Build/AsyncAPI.Saunter.Generator.Build.csproj create mode 100644 src/AsyncAPI.Saunter.Generator.Build/build/AsyncAPI.Saunter.Generator.Build.props create mode 100644 src/AsyncAPI.Saunter.Generator.Build/build/AsyncAPI.Saunter.Generator.Build.targets create mode 100644 src/AsyncAPI.Saunter.Generator.Build/readme.md diff --git a/Saunter.sln b/Saunter.sln index 43967dd8..c746b5ea 100644 --- a/Saunter.sln +++ b/Saunter.sln @@ -31,7 +31,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Saunter.Tests.MarkerTypeTes EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AsyncAPI.Saunter.Generator.Cli", "src\AsyncAPI.Saunter.Generator.Cli\AsyncAPI.Saunter.Generator.Cli.csproj", "{6C102D4D-3DA4-4763-B75E-C15E33E7E94A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AsyncAPI.Saunter.Generator.Cli.Tests", "test\AsyncAPI.Saunter.Generator.Cli.Tests\AsyncAPI.Saunter.Generator.Cli.Tests.csproj", "{18AD0249-0436-4A26-9972-B97BA6905A54}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AsyncAPI.Saunter.Generator.Cli.Tests", "test\AsyncAPI.Saunter.Generator.Cli.Tests\AsyncAPI.Saunter.Generator.Cli.Tests.csproj", "{18AD0249-0436-4A26-9972-B97BA6905A54}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AsyncAPI.Saunter.Generator.Build", "src\AsyncAPI.Saunter.Generator.Build\AsyncAPI.Saunter.Generator.Build.csproj", "{A320E670-5CB0-4815-AF67-D8D09FC92A2A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -127,6 +129,18 @@ Global {18AD0249-0436-4A26-9972-B97BA6905A54}.Release|x64.Build.0 = Release|Any CPU {18AD0249-0436-4A26-9972-B97BA6905A54}.Release|x86.ActiveCfg = Release|Any CPU {18AD0249-0436-4A26-9972-B97BA6905A54}.Release|x86.Build.0 = Release|Any CPU + {A320E670-5CB0-4815-AF67-D8D09FC92A2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A320E670-5CB0-4815-AF67-D8D09FC92A2A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A320E670-5CB0-4815-AF67-D8D09FC92A2A}.Debug|x64.ActiveCfg = Debug|Any CPU + {A320E670-5CB0-4815-AF67-D8D09FC92A2A}.Debug|x64.Build.0 = Debug|Any CPU + {A320E670-5CB0-4815-AF67-D8D09FC92A2A}.Debug|x86.ActiveCfg = Debug|Any CPU + {A320E670-5CB0-4815-AF67-D8D09FC92A2A}.Debug|x86.Build.0 = Debug|Any CPU + {A320E670-5CB0-4815-AF67-D8D09FC92A2A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A320E670-5CB0-4815-AF67-D8D09FC92A2A}.Release|Any CPU.Build.0 = Release|Any CPU + {A320E670-5CB0-4815-AF67-D8D09FC92A2A}.Release|x64.ActiveCfg = Release|Any CPU + {A320E670-5CB0-4815-AF67-D8D09FC92A2A}.Release|x64.Build.0 = Release|Any CPU + {A320E670-5CB0-4815-AF67-D8D09FC92A2A}.Release|x86.ActiveCfg = Release|Any CPU + {A320E670-5CB0-4815-AF67-D8D09FC92A2A}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -139,6 +153,7 @@ Global {02284473-6DE7-4EE0-8433-2AC295045549} = {6491E321-2D02-44AB-9116-D722FE169595} {6C102D4D-3DA4-4763-B75E-C15E33E7E94A} = {28D4C365-FDED-49AE-A97D-36202E24A55A} {18AD0249-0436-4A26-9972-B97BA6905A54} = {6491E321-2D02-44AB-9116-D722FE169595} + {A320E670-5CB0-4815-AF67-D8D09FC92A2A} = {28D4C365-FDED-49AE-A97D-36202E24A55A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2F85D9DA-DBCF-4F13-8C42-5719F1469B2E} diff --git a/examples/StreetlightsAPI/.gitignore b/examples/StreetlightsAPI/.gitignore new file mode 100644 index 00000000..78c834f3 --- /dev/null +++ b/examples/StreetlightsAPI/.gitignore @@ -0,0 +1 @@ +specs/ \ No newline at end of file diff --git a/examples/StreetlightsAPI/StreetlightsAPI.csproj b/examples/StreetlightsAPI/StreetlightsAPI.csproj index 43f0b54f..3185dffc 100644 --- a/examples/StreetlightsAPI/StreetlightsAPI.csproj +++ b/examples/StreetlightsAPI/StreetlightsAPI.csproj @@ -3,6 +3,10 @@ net6.0 false + true + json,yml + streetlights.{extension} + specs diff --git a/src/AsyncAPI.Saunter.Generator.Build/AsyncAPI.Saunter.Generator.Build.csproj b/src/AsyncAPI.Saunter.Generator.Build/AsyncAPI.Saunter.Generator.Build.csproj new file mode 100644 index 00000000..e9591e8b --- /dev/null +++ b/src/AsyncAPI.Saunter.Generator.Build/AsyncAPI.Saunter.Generator.Build.csproj @@ -0,0 +1,63 @@ + + + + net6.0;net8.0 + enable + 12 + true + + AsyncAPI Build Tools + AsyncAPI Initiative + AsyncAPI.Saunter.Generator.Build + asyncapi;aspnetcore;openapi;documentation;amqp;generator;build;tool + readme.md + logo.png + https://github.com/asyncapi/saunter + true + true + false + false + https://github.com/asyncapi/saunter + MIT + false + 0.0.24 + tools/$(Configuration) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/AsyncAPI.Saunter.Generator.Build/build/AsyncAPI.Saunter.Generator.Build.props b/src/AsyncAPI.Saunter.Generator.Build/build/AsyncAPI.Saunter.Generator.Build.props new file mode 100644 index 00000000..f59b5b6e --- /dev/null +++ b/src/AsyncAPI.Saunter.Generator.Build/build/AsyncAPI.Saunter.Generator.Build.props @@ -0,0 +1,11 @@ + + + + true + json + ./ + + + + + \ No newline at end of file diff --git a/src/AsyncAPI.Saunter.Generator.Build/build/AsyncAPI.Saunter.Generator.Build.targets b/src/AsyncAPI.Saunter.Generator.Build/build/AsyncAPI.Saunter.Generator.Build.targets new file mode 100644 index 00000000..19f35357 --- /dev/null +++ b/src/AsyncAPI.Saunter.Generator.Build/build/AsyncAPI.Saunter.Generator.Build.targets @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/AsyncAPI.Saunter.Generator.Build/readme.md b/src/AsyncAPI.Saunter.Generator.Build/readme.md new file mode 100644 index 00000000..6b5971d1 --- /dev/null +++ b/src/AsyncAPI.Saunter.Generator.Build/readme.md @@ -0,0 +1,27 @@ +# AsyncApi Generator.Build Nuget Package +A nuget package to generate AsyncAPI specification files at build time, based on code-first attributes. + +# Customizations +The AsyncAPI spec generation can be configured through properties in the csproj-file (or .props files): +``` + + + + + + + + +``` + +Defaults are the same as the underlying [Generator.Cli tool](https://www.nuget.org/packages/AsyncAPI.Saunter.Generator.Cli). + +If the ```AsyncAPI.Saunter.Generator.Build``` Nuget package is referenced, the default is to generate AsyncAPI spec files at build time. + +- AsyncAPIGenerateDocumentsOnBuild: Whether to actually generate AsyncAPI spec files on build (true or false, default: true) +- AsyncAPIDocumentFormats: Format of the expected AsyncAPI spec files (json, yml or yaml, default: json) +- AsyncAPIDocumentOutputPath: Output path for the AsyncAPI spec files, relative to the csproj location. (default is the csproj root path: ./) +- AsyncAPIDocumentNames: The AsyncAPI documents to generate. (default: generate all known documents) +- AsyncAPIDocumentFilename: Template of the AsyncAPI spec files (default: "{document}_asyncapi.{extension}") +- AsyncAPIDocumentEnvVars: Environment variable(s) to set during generation of the AsyncAPI spec files (default: none, Example: "ASPNETCORE_ENVIRONMENT=Development") +None of these properties are mandatory. By only referencing the [AsyncAPI.Saunter.Generator.Build](https://www.nuget.org/packages/AsyncAPI.Saunter.Generator.Build) Nuget package a json AsyncAPI spec file will be generated for all AsyncAPI documents. \ No newline at end of file diff --git a/src/AsyncAPI.Saunter.Generator.Cli/AsyncAPI.Saunter.Generator.Cli.csproj b/src/AsyncAPI.Saunter.Generator.Cli/AsyncAPI.Saunter.Generator.Cli.csproj index 0400c5ab..a4d8449f 100644 --- a/src/AsyncAPI.Saunter.Generator.Cli/AsyncAPI.Saunter.Generator.Cli.csproj +++ b/src/AsyncAPI.Saunter.Generator.Cli/AsyncAPI.Saunter.Generator.Cli.csproj @@ -2,7 +2,7 @@ Exe - net8.0;net6.0 + net6.0;net8.0 enable 12 AsyncAPI.Saunter.Generator.Cli diff --git a/src/AsyncAPI.Saunter.Generator.Cli/Commands/Tofile.cs b/src/AsyncAPI.Saunter.Generator.Cli/Commands/Tofile.cs index 9faa30c9..a447ce43 100644 --- a/src/AsyncAPI.Saunter.Generator.Cli/Commands/Tofile.cs +++ b/src/AsyncAPI.Saunter.Generator.Cli/Commands/Tofile.cs @@ -41,6 +41,6 @@ internal static Func, int> Run(string[] args) => nam private static string EscapePath(string path) { - return path.Contains(' ') ? "\"" + path + "\"" : path; + return (path.Contains(' ') || string.IsNullOrWhiteSpace(path)) ? "\"" + path + "\"" : path; } } diff --git a/src/AsyncAPI.Saunter.Generator.Cli/Commands/TofileInternal.cs b/src/AsyncAPI.Saunter.Generator.Cli/Commands/TofileInternal.cs index a3477c7a..74fbaec3 100644 --- a/src/AsyncAPI.Saunter.Generator.Cli/Commands/TofileInternal.cs +++ b/src/AsyncAPI.Saunter.Generator.Cli/Commands/TofileInternal.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Diagnostics; using LEGO.AsyncAPI.Readers; using Microsoft.Extensions.Options; using Saunter.Serialization; @@ -31,7 +32,7 @@ internal static int Run(IDictionary namedArgs) var startupAssembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.Combine(Directory.GetCurrentDirectory(), namedArgs[StartupAssemblyArgument])); // 2) Build a service container that's based on the startup assembly - var envVars = namedArgs.TryGetValue(EnvOption, out var x) ? x.Split(',').Select(x => x.Trim()) : Array.Empty(); + var envVars = (namedArgs.TryGetValue(EnvOption, out var x) && !string.IsNullOrWhiteSpace(x)) ? x.Split(',').Select(x => x.Trim()) : Array.Empty(); foreach (var envVar in envVars.Select(x => x.Split('=').Select(x => x.Trim()).ToList())) { if (envVar.Count == 2) @@ -50,8 +51,8 @@ internal static int Run(IDictionary namedArgs) var asyncapiOptions = serviceProvider.GetService>().Value; var documentSerializer = serviceProvider.GetRequiredService(); - var documentNames = namedArgs.TryGetValue(DocOption, out var doc) ? [doc] : asyncapiOptions.NamedApis.Keys; - var fileTemplate = namedArgs.TryGetValue(FileNameOption, out var template) ? template : "{document}_asyncapi.{extension}"; + var documentNames = (namedArgs.TryGetValue(DocOption, out var doc) && !string.IsNullOrWhiteSpace(doc)) ? [doc] : asyncapiOptions.NamedApis.Keys; + var fileTemplate = (namedArgs.TryGetValue(FileNameOption, out var template) && !string.IsNullOrWhiteSpace(template)) ? template : "{document}_asyncapi.{extension}"; if (documentNames.Count == 0) { if (asyncapiOptions.AssemblyMarkerTypes.Any()) @@ -86,20 +87,16 @@ internal static int Run(IDictionary namedArgs) } // 4) Serialize to specified output location or stdout - var outputPath = namedArgs.TryGetValue(OutputOption, out var arg1) ? Path.Combine(Directory.GetCurrentDirectory(), arg1) : null; + var outputPath = (namedArgs.TryGetValue(OutputOption, out var path) && !string.IsNullOrWhiteSpace(path)) ? Path.Combine(Directory.GetCurrentDirectory(), path) : null; if (!string.IsNullOrEmpty(outputPath)) { - var directoryPath = Path.GetDirectoryName(outputPath); - if (!string.IsNullOrEmpty(directoryPath) && !Directory.Exists(directoryPath)) - { - Directory.CreateDirectory(directoryPath); - } + Directory.CreateDirectory(outputPath); } var exportJson = true; var exportYml = false; var exportYaml = false; - if (namedArgs.TryGetValue(FormatOption, out var format)) + if (namedArgs.TryGetValue(FormatOption, out var format) && !string.IsNullOrWhiteSpace(format)) { var splitted = format.Split(',').Select(x => x.Trim()).ToList(); exportJson = splitted.Any(x => x.Equals("json", StringComparison.OrdinalIgnoreCase)); diff --git a/src/AsyncAPI.Saunter.Generator.Cli/SwashbuckleImport/CommandRunner.cs b/src/AsyncAPI.Saunter.Generator.Cli/SwashbuckleImport/CommandRunner.cs index c3c8eca0..29c982da 100644 --- a/src/AsyncAPI.Saunter.Generator.Cli/SwashbuckleImport/CommandRunner.cs +++ b/src/AsyncAPI.Saunter.Generator.Cli/SwashbuckleImport/CommandRunner.cs @@ -56,6 +56,7 @@ public int Run(IEnumerable args) if (_subRunners.Any() || !TryParseArgs(args, out IDictionary namedArgs)) { + _output.WriteLine($"Input: {string.Join(' ', args)}"); PrintUsage(); return 1; }