-
Notifications
You must be signed in to change notification settings - Fork 57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
#197 Produce AsyncAPI documents at build time (AsyncAPI.Saunter.Generator.Build) #219
Draft
smoerijf
wants to merge
75
commits into
asyncapi:main
Choose a base branch
from
smoerijf:#197-more-logging
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+2,130
−165
Draft
Changes from all commits
Commits
Show all changes
75 commits
Select commit
Hold shift + click to select a range
6318f80
#196 Publish a global dotnet tool
2162d72
#196 Publish a global dotnet tool -- Api/API casing fix
55895a2
#196 .NET8
6ba3409
#196 .NET6
74c9cff
#196 I'd like .NET8 more, request both for ci
cc5d952
#196 I'd like .NET8 more, request both for ci?
569a30a
#196 Fixed resolving, added support for multiple asyncAPI documents, …
0419101
#196 Test for dotnet cli tool, added support for default asyncapi doc…
b29a1ab
#196 Pack_Install_Run_Uninstall Test
12593dc
#196 Pack_Install_Run_Uninstall fix casing in test, add meta data to …
93dc17e
#196 fix accident
e701660
#196 fix warnings, fix permission
d9b558c
#196 unix needs a shebang
f0af6d1
#196 sourcelink properties
a379a3e
#197 Produce AsyncAPI documents at build time
02ffbcd
#197 Produce AsyncAPI documents at build time
1ebb77c
#196 renamed tool to make up a more logical name, fixed empty paramet…
ba1e856
#196 fix readme error
88c5caf
Merge remote-tracking branch 'remotes/smoerijf/#196-Publish-a-global-…
a2813e2
#197 Produce AsyncAPI documents at build time - test + setup with loc…
a42ab3f
#197 local nuget source folder should exist
d3f0472
#197 update readme's
e10112b
#197 set streetlights example to depend on the Generator.Build projec…
4741f56
#197 undo streetlights example order, use 2 step build approach
195dc14
#197 use better path to the dotnet dll in tools and hopefully fix the…
8583f08
#197 need more logging
2f2c172
#197 need more logging - dir logs
0c033f1
#197 slashes?
31bacc6
#197 idk
fbbf601
Merge branch '#197-more-logging' of https://github.com/smoerijf/saunt…
6cc92e3
#197 try by installing local tool
df5636e
#197 bump net version
829d5a5
#197 install dotnet in .nuget iso the csproj root
49fb31f
#197 use 999.* for local nuget package buils to use in tests and exam…
f110afe
#197 readme style
1b7087a
#197 pin .Cli version to beta in .Build
90dcff1
Merge remote-tracking branch 'remotes/origin/main' into #196-Publish-…
a746e4d
#196 Fixed formatting etc + some PR remarks
8e7a01f
#196 Tool rewrite to make its components testable, removed Swachbuckl…
240c9ed
#196 formatting
715a62d
#196 add unitTests for tofile classes
e181850
#196 add unitTests for tofile
ab5b375
#196-Publish-a-global-dotnet-tool
183a4e8
#196 formatting
8d2fd49
#196 ALL tests should run
a9d544a
#196 Tests use streetlights DLL in integegration tests, add dependency
72dc742
#196 release also the CLI tool
2c6e2d9
#197 formatting
5823431
Merge remote-tracking branch 'remotes/smoerijf/#196-Publish-a-global-…
38ac1eb
#197 .build nuget package
6099e65
#197 fix test
e804aaf
Merge branch '#197-more-logging' of https://github.com/smoerijf/saunt…
1bb7d30
#196 fix wrong ci.yml merge
0babe05
#196 2 phase build: first src, then the others
9ee1100
#196 fix formatting
7c5095a
#196 test after build
d30c503
#196 test needs build
e739072
#196 trying...
ba95b28
#196 seperate job is inpossible, giving up on it
a9cc0de
#197 wording
e16fcf1
#197 common.props
7b22ce0
#196 Add example project with top level statement, because that faile…
ba8dd8e
#196 Ignore expected warning (ASP0014 needs #173)
3d6c0ea
#196 formatting and readme wording
8a4f5c6
#196 ExitCode test problems
07b4273
#196 ExitCode test problems -- the issue is a missing reference
e846dd9
#196 GetStreamFor logging
fc7cccd
#196 GetStreamFor try recreate directory?
4f1064e
#196 GetStreamFor try recreate directory??
74260a8
#196 permission issue?
e937716
Merge branch '#196-Publish-a-global-dotnet-tool' into #197-more-logging
a2cee95
#197 cleanup csproj
b043a77
#197 cleanup csproj
5fe8c6e
#196 cleanup csproj
68e06ca
Merge remote-tracking branch 'remotes/smoerijf/#196-Publish-a-global-…
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
* text eol=lf |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,4 +7,6 @@ runs: | |
id: setup-dotnet | ||
uses: actions/setup-dotnet@v1 | ||
with: | ||
dotnet-version: "6.0.x" | ||
dotnet-version: | | ||
6.0.x | ||
8.0.x |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<Project> | ||
<ItemGroup> | ||
<!-- Make it possible for NSubstitube to make substitues of internal classes --> | ||
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" /> | ||
|
||
<!-- Make project internals visible to their respective .Tests project --> | ||
<InternalsVisibleTo Include="$(AssemblyName).Tests" /> | ||
</ItemGroup> | ||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
specs/ | ||
streetlights.json | ||
streetlights.yml | ||
streetlights.yaml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
using System.Linq; | ||
using Microsoft.AspNetCore.Builder; | ||
using Microsoft.AspNetCore.Http; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.Hosting; | ||
using Microsoft.Extensions.Logging; | ||
using Microsoft.Extensions.Options; | ||
using NLog; | ||
using NLog.Web; | ||
using Saunter; | ||
using Saunter.AsyncApiSchema.v2; | ||
using StreetlightsAPI; | ||
|
||
LogManager.Setup().LoadConfigurationFromAppSettings(); | ||
|
||
var builder = WebApplication.CreateBuilder(args); | ||
builder.Logging.AddSimpleConsole(console => console.SingleLine = true); | ||
builder.Host.UseNLog(); | ||
|
||
// Add Saunter to the application services. | ||
builder.Services.AddAsyncApiSchemaGeneration(options => | ||
{ | ||
options.AssemblyMarkerTypes = [typeof(StreetlightMessageBus)]; | ||
|
||
options.Middleware.UiTitle = "Streetlights API"; | ||
|
||
options.AsyncApi = new AsyncApiDocument | ||
{ | ||
Info = new Info("Streetlights API", "1.0.0") | ||
{ | ||
Description = "The Smartylighting Streetlights API allows you to remotely manage the city lights.", | ||
License = new License("Apache 2.0") | ||
{ | ||
Url = "https://www.apache.org/licenses/LICENSE-2.0" | ||
} | ||
}, | ||
Servers = | ||
{ | ||
["mosquitto"] = new Server("test.mosquitto.org", "mqtt"), | ||
["webapi"] = new Server("localhost:5000", "http"), | ||
}, | ||
}; | ||
}); | ||
|
||
builder.Services.AddScoped<IStreetlightMessageBus, StreetlightMessageBus>(); | ||
builder.Services.AddControllers(); | ||
|
||
var app = builder.Build(); | ||
|
||
app.UseDeveloperExceptionPage(); | ||
|
||
app.UseRouting(); | ||
app.UseCors(configure => configure.AllowAnyOrigin().AllowAnyMethod()); | ||
|
||
// to be fixed with issue #173 | ||
#pragma warning disable ASP0014 // Suggest using top level route registrations instead of UseEndpoints | ||
app.UseEndpoints(endpoints => | ||
{ | ||
endpoints.MapAsyncApiDocuments(); | ||
endpoints.MapAsyncApiUi(); | ||
|
||
endpoints.MapControllers(); | ||
}); | ||
#pragma warning restore ASP0014 // Suggest using top level route registrations instead of UseEndpoints | ||
|
||
await app.StartAsync(); | ||
|
||
// Print the AsyncAPI doc location | ||
var logger = app.Services.GetService<ILoggerFactory>().CreateLogger<Program>(); | ||
var options = app.Services.GetService<IOptions<AsyncApiOptions>>(); | ||
var addresses = app.Urls; | ||
logger.LogInformation("AsyncAPI doc available at: {URL}", $"{addresses.FirstOrDefault()}{options.Value.Middleware.Route}"); | ||
logger.LogInformation("AsyncAPI UI available at: {URL}", $"{addresses.FirstOrDefault()}{options.Value.Middleware.UiBaseRoute}"); | ||
|
||
// Redirect base url to AsyncAPI UI | ||
app.Map("/", () => Results.Redirect("index.html")); | ||
app.Map("/index.html", () => Results.Redirect(options.Value.Middleware.UiBaseRoute)); | ||
|
||
await app.WaitForShutdownAsync(); |
63 changes: 63 additions & 0 deletions
63
examples/StreetlightsAPI.TopLevelStatement/StreetlightsAPI.TopLevelStatement.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
<Project Sdk="Microsoft.NET.Sdk.Web"> | ||
|
||
<PropertyGroup> | ||
<!-- This project is targeting .NET8 intentionally (The 'old school' Startup-class project is .NET6), to prove that | ||
the AsyncAPI.Saunter.Generator.Cli tool can generate specs for projects targetting .NET6 and .NET8. --> | ||
<TargetFramework>net8.0</TargetFramework> | ||
<IsPackable>false</IsPackable> | ||
|
||
<!-- Example settings for "AsyncAPI.Saunter.Generator.Build", they are all option though --> | ||
<AsyncAPIGenerateDocumentsOnBuild>true</AsyncAPIGenerateDocumentsOnBuild> | ||
<AsyncAPIDocumentFormats>json,yml</AsyncAPIDocumentFormats> | ||
<AsyncAPIDocumentFilename>streetlights.{extension}</AsyncAPIDocumentFilename> | ||
<AsyncAPIDocumentOutputPath>specs</AsyncAPIDocumentOutputPath> | ||
</PropertyGroup> | ||
|
||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> | ||
<DocumentationFile>bin\Debug\StreetlightsAPI.TopLevelStatement.xml</DocumentationFile> | ||
<NoWarn>1701;1702;1591</NoWarn> | ||
</PropertyGroup> | ||
|
||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> | ||
<DocumentationFile>bin\Release\StreetlightsAPI.TopLevelStatement.xml</DocumentationFile> | ||
<NoWarn>1701;1702;1591</NoWarn> | ||
</PropertyGroup> | ||
|
||
<!-- Get the latest local build nuget package from source code, from the local nuget packages source as configured in nuget.config | ||
999.* are the 'special' version number for local builds --> | ||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' "> | ||
<PackageReference Include="AsyncAPI.Saunter.Generator.Build" Version="999.*"> | ||
<PrivateAssets>all</PrivateAssets> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
</PackageReference> | ||
</ItemGroup> | ||
|
||
<!-- For release: use a real published nuget package --> | ||
<ItemGroup Condition=" '$(Configuration)' == 'Release' "> | ||
<PackageReference Include="AsyncAPI.Saunter.Generator.Build" Version="*-*"> | ||
<PrivateAssets>all</PrivateAssets> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
</PackageReference> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\..\src\Saunter\Saunter.csproj" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<Folder Include="specs\" /> | ||
|
||
<Compile Include="../StreetlightsAPI/API.cs" /> | ||
<Compile Include="../StreetlightsAPI/Messaging.cs" /> | ||
|
||
<None Include="../StreetlightsAPI/nlog.config"> | ||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||
</None> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="NLog" Version="5.3.2" /> | ||
<PackageReference Include="NLog.Web.AspNetCore" Version="5.3.11" /> | ||
</ItemGroup> | ||
|
||
</Project> |
19 changes: 19 additions & 0 deletions
19
examples/StreetlightsAPI.TopLevelStatement/appsettings.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
{ | ||
"Logging": { | ||
"LogLevel": { | ||
"Default": "Information", | ||
"Microsoft": "Warning", | ||
"Microsoft.Hosting.Lifetime": "Information" | ||
} | ||
}, | ||
|
||
"AllowedHosts": "*", | ||
|
||
"Kestrel": { | ||
"EndPoints": { | ||
"Http": { | ||
"Url": "http://localhost:5001" | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
specs/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<?xml version="1.0" encoding="utf-8" ?> | ||
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
autoReload="true"> | ||
|
||
<variable name="BaseDir" value="./" /> | ||
|
||
<!-- the targets to write to --> | ||
<targets> | ||
<!-- File Target for all log messages with basic details --> | ||
<target xsi:type="File" name="allfile" fileName="${BaseDir}/streetlights.nlog" | ||
layout="${longdate}|${level:uppercase=true}|${activity:property=TraceId}|${logger}|${message} ${exception:format=tostring}" | ||
maxArchiveFiles="5" archiveAboveSize="104857600" archiveNumbering="DateAndSequence" archiveDateFormat="yyyy-MM-dd" archiveFileName="${BaseDir}/streetlights-{#}.nlog" /> | ||
|
||
<!--Console Target for hosting lifetime messages to improve Docker / Visual Studio startup detection --> | ||
<target xsi:type="Console" name="lifetimeConsole" layout="${MicrosoftConsoleLayout}" /> | ||
</targets> | ||
|
||
<!-- rules to map from logger name to target --> | ||
<rules> | ||
<logger name="*" minlevel="Debug" writeTo="allfile" /> | ||
</rules> | ||
</nlog> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Place holder, so the local-nuget-source exists. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<configuration> | ||
<packageSources> | ||
<clear /> | ||
<!-- Adds the folder "local-nuget-source" in root of the project as a nuget source. | ||
Projects that build a nuget package publish in this folder so dependent projects can use the local build packages --> | ||
<add key="Local-sauter" value="local-nuget-source" /> | ||
|
||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" /> | ||
</packageSources> | ||
</configuration> |
51 changes: 51 additions & 0 deletions
51
src/AsyncAPI.Saunter.Generator.Build/AsyncAPI.Saunter.Generator.Build.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<Import Project="../Common.NugetPackage.props" /> | ||
|
||
<PropertyGroup> | ||
<TargetFrameworks>net8.0</TargetFrameworks> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<LangVersion>12</LangVersion> | ||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> | ||
<NoWarn>$(NoWarn);NU5128</NoWarn> | ||
|
||
<Description>AsyncAPI Build Tools: Generate AsyncAPI spec file(s) during build from a startup project.</Description> | ||
<Authors>AsyncAPI Initiative</Authors> | ||
<IsTool>true</IsTool> | ||
<PackageId>AsyncAPI.Saunter.Generator.Build</PackageId> | ||
<PackageTags>asyncapi;aspnetcore;openapi;documentation;amqp;generator;build;tool</PackageTags> | ||
<PackageReadmeFile>readme.md</PackageReadmeFile> | ||
<PackageIcon>logo.png</PackageIcon> | ||
<RepositoryUrl>https://github.com/asyncapi/saunter</RepositoryUrl> | ||
<PublishRepositoryUrl>true</PublishRepositoryUrl> | ||
<EmbedUntrackedSources>true</EmbedUntrackedSources> | ||
<IncludeBuildOutput>false</IncludeBuildOutput> | ||
<IncludeSymbols>false</IncludeSymbols> | ||
<PackageProjectUrl>https://github.com/asyncapi/saunter</PackageProjectUrl> | ||
<PackageLicenseExpression>MIT</PackageLicenseExpression> | ||
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance> | ||
<DevelopmentDependency>true</DevelopmentDependency> | ||
</PropertyGroup> | ||
|
||
<PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'"> | ||
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<!-- Development Dependencies --> | ||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" /> | ||
<PackageReference Update="@(PackageReference)" PrivateAssets="All" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<None Include="readme.md" Pack="true" PackagePath="/" /> | ||
<None Include="../../assets/logo.png" Pack="true" PackagePath="/" /> | ||
<None Include="build/*" Pack="true" PackagePath="/build" /> | ||
<None Include="bin/$(Configuration)/$(TargetFramework)" Pack="true" PackagePath="/tools" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\AsyncAPI.Saunter.Generator.Cli\AsyncAPI.Saunter.Generator.Cli.csproj" PrivateAssets="All" /> | ||
</ItemGroup> | ||
|
||
</Project> |
14 changes: 14 additions & 0 deletions
14
src/AsyncAPI.Saunter.Generator.Build/build/AsyncAPI.Saunter.Generator.Build.props
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?xml version="1.0" encoding="utf-8" standalone="no"?> | ||
<Project> | ||
|
||
<!-- List all available properties to customize the AsyncAPI.Generator.Build package --> | ||
<PropertyGroup> | ||
<AsyncAPIGenerateDocumentsOnBuild Condition=" '$(AsyncAPIGenerateDocumentsOnBuild)' == '' ">true</AsyncAPIGenerateDocumentsOnBuild> | ||
<AsyncAPIDocumentFormats Condition=" '$(AsyncAPIDocumentFormats)' == '' ">json</AsyncAPIDocumentFormats> | ||
<AsyncAPIDocumentOutputPath Condition=" '$(AsyncAPIDocumentOutputPath)' == '' ">./</AsyncAPIDocumentOutputPath> | ||
<AsyncAPIDocumentFilename Condition=" '$(AsyncAPIDocumentFilename)' == '' "></AsyncAPIDocumentFilename> | ||
<AsyncAPIDocumentNames Condition=" '$(AsyncAPIDocumentNames)' == '' "></AsyncAPIDocumentNames> | ||
<AsyncAPIDocumentEnvVars Condition=" '$(AsyncAPIDocumentEnvVars)' == '' "></AsyncAPIDocumentEnvVars> | ||
</PropertyGroup> | ||
|
||
</Project> |
35 changes: 35 additions & 0 deletions
35
src/AsyncAPI.Saunter.Generator.Build/build/AsyncAPI.Saunter.Generator.Build.targets
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<?xml version="1.0" encoding="utf-8" standalone="no"?> | ||
<Project> | ||
|
||
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition=" '$(AsyncAPIGenerateDocumentsOnBuild)' == 'true' "> | ||
<!-- Calculate some paths --> | ||
<PropertyGroup> | ||
<AsyncAPIBuildToolBuildDir>$([System.IO.Path]::GetDirectoryName($(MSBuildThisFileDirectory)))</AsyncAPIBuildToolBuildDir> | ||
<AsyncAPIBuildToolRoot>$([System.IO.Path]::GetDirectoryName($(AsyncAPIBuildToolBuildDir)))</AsyncAPIBuildToolRoot> | ||
<AsyncAPICliToolPath>$([System.IO.Path]::Combine($(AsyncAPIBuildToolRoot), tools, net8.0, AsyncAPI.Saunter.Generator.Cli.dll))</AsyncAPICliToolPath> | ||
<AsyncAPICliToolStartupAssembly>$([System.IO.Path]::Combine($(MSBuildProjectDirectory), $(OutputPath), $(AssemblyTitle).dll))</AsyncAPICliToolStartupAssembly> | ||
<AsyncAPICliToolOutputPath>$([System.IO.Path]::Combine($(MSBuildProjectDirectory), $(AsyncAPIDocumentOutputPath)))</AsyncAPICliToolOutputPath> | ||
</PropertyGroup> | ||
|
||
<!-- Debug output: print some paths, set -v flag (verbosity) at least to [n]ormal to show in build output --> | ||
<Message Text="AsyncAPI.Generator.Build; AsyncAPICliToolPath: $(AsyncAPICliToolPath)" /> | ||
<Message Text="AsyncAPI.Generator.Build; AsyncAPICliToolStartupAssembly: $(AsyncAPICliToolStartupAssembly)" /> | ||
<Message Text="AsyncAPI.Generator.Build; AsyncAPICliToolOutputPath: $(AsyncAPICliToolOutputPath)" /> | ||
|
||
<Message Text="AsyncAPI.Generator.Build; AsyncAPIDocumentFormats: $(AsyncAPIDocumentFormats)" /> | ||
<Message Text="AsyncAPI.Generator.Build; AsyncAPIDocumentOutputPath: $(AsyncAPIDocumentOutputPath)" /> | ||
<Message Text="AsyncAPI.Generator.Build; AsyncAPIDocumentNames: $(AsyncAPIDocumentNames)" /> | ||
<Message Text="AsyncAPI.Generator.Build; AsyncAPIDocumentFilename: $(AsyncAPIDocumentFilename)" /> | ||
<Message Text="AsyncAPI.Generator.Build; AsyncAPIDocumentEnvVars: $(AsyncAPIDocumentEnvVars)" /> | ||
<Message Text="AsyncAPI.Generator.Build; AsyncAPIBuildToolBuildDir: $(AsyncAPIBuildToolBuildDir)" /> | ||
<Message Text="AsyncAPI.Generator.Build; AsyncAPIBuildToolRoot: $(AsyncAPIBuildToolRoot)" /> | ||
<Message Text="AsyncAPI.Generator.Build; MSBuildThisFile: $(MSBuildThisFile)" /> | ||
<Message Text="AsyncAPI.Generator.Build; MSBuildThisFileDirectory: $(MSBuildThisFileDirectory)" /> | ||
<Message Text="AsyncAPI.Generator.Build; MSBuildProjectFullPath: $(MSBuildProjectFullPath)" /> | ||
<Message Text="AsyncAPI.Generator.Build; MSBuildProjectDirectory: $(MSBuildProjectDirectory)" /> | ||
|
||
<Exec Command="dotnet "$(AsyncAPICliToolPath)" tofile "$(AsyncAPICliToolStartupAssembly)" --output "$(AsyncAPICliToolOutputPath)" --format "$(AsyncAPIDocumentFormats)" --doc "$(AsyncAPIDocumentNames)" --filename "$(AsyncAPIDocumentFilename)" --env "$(AsyncAPIDocumentEnvVars)"" | ||
WorkingDirectory="$(AsyncAPIBuildToolRoot)" /> | ||
</Target> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# AsyncApi Generator.Build Nuget Package | ||
A nuget package to generate AsyncAPI specification files at build time, based on code-first attributes. This nuget package requires .NET8.0 runtime in order to work. The consuming csproj doesn't need to target .NET8.0. | ||
|
||
This nuget packages can help to better control API changes by commiting the AsyncAPI spec to source control. By always generating spec files at build, it will be clear when the api changes. | ||
|
||
# Customization Properties | ||
The AsyncAPI spec generation can be configured through project properties in the csproj-file (or included via [.props files](https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-your-build)): | ||
``` | ||
<PropertyGroup> | ||
<AsyncAPIGenerateDocumentsOnBuild></AsyncAPIGenerateDocumentsOnBuild> | ||
<AsyncAPIDocumentFormats></AsyncAPIDocumentFormats> | ||
<AsyncAPIDocumentOutputPath></AsyncAPIDocumentOutputPath> | ||
<AsyncAPIDocumentNames></AsyncAPIDocumentNames> | ||
<AsyncAPIDocumentFilename></AsyncAPIDocumentFilename> | ||
<AsyncAPIDocumentEnvVars></AsyncAPIDocumentEnvVars> | ||
</PropertyGroup> | ||
``` | ||
|
||
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_: the output formats to generate, can be a combination of json, yml and/or yaml. | ||
- _AsyncAPIDocumentOutputPath_: relative path where the AsyncAPI will be output (default is the csproj root path: ./) | ||
- _AsyncAPIDocumentNames_: The AsyncAPI documents to generate. (default: generate all known documents) | ||
- _AsyncAPIDocumentFilename_: the template for the outputted file names. Default: "{document}_asyncapi.{extension}" | ||
- _AsyncAPIDocumentEnvVars_: define environment variable(s) for the application. Formatted as a comma separated list of _key=value_ pairs, example: ```ASPNETCORE_ENVIRONMENT=AsyncAPI,CONNECT_TO_DATABASE=false```. | ||
|
||
None of these properties are mandatory. Only referencing the [AsyncAPI.Saunter.Generator.Build](https://www.nuget.org/packages/AsyncAPI.Saunter.Generator.Build) Nuget package will generate a json AsyncAPI spec file for all AsyncAPI documents. |
59 changes: 59 additions & 0 deletions
59
src/AsyncAPI.Saunter.Generator.Cli/AsyncAPI.Saunter.Generator.Cli.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<Import Project="../Common.NugetPackage.props" /> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<TargetFrameworks>net8.0</TargetFrameworks> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<LangVersion>12</LangVersion> | ||
<RootNamespace>AsyncAPI.Saunter.Generator.Cli</RootNamespace> | ||
<NoWarn>$(NoWarn);EF1001</NoWarn> | ||
|
||
<Description>AsyncAPI Command Line Tools: Dotnet tool to generate AsyncAPI spec file(s) from dotnet startup assembly.</Description> | ||
<Authors>AsyncAPI Initiative</Authors> | ||
<PackAsTool>true</PackAsTool> | ||
<PackageId>AsyncAPI.Saunter.Generator.Cli</PackageId> | ||
<PackageType>DotnetTool</PackageType> | ||
<ToolCommandName>dotnet-asyncapi</ToolCommandName> | ||
<PackageTags>asyncapi;aspnetcore;openapi;documentation;amqp;generator;cli;tool</PackageTags> | ||
<PackageReadmeFile>readme.md</PackageReadmeFile> | ||
<PackageIcon>logo.png</PackageIcon> | ||
<RepositoryUrl>https://github.com/asyncapi/saunter</RepositoryUrl> | ||
<PublishRepositoryUrl>true</PublishRepositoryUrl> | ||
<EmbedUntrackedSources>true</EmbedUntrackedSources> | ||
<PackageProjectUrl>https://github.com/asyncapi/saunter</PackageProjectUrl> | ||
<PackageLicenseExpression>MIT</PackageLicenseExpression> | ||
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance> | ||
<IncludeSymbols>true</IncludeSymbols> | ||
<SymbolPackageFormat>snupkg</SymbolPackageFormat> | ||
</PropertyGroup> | ||
|
||
<PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'"> | ||
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild> | ||
</PropertyGroup> | ||
|
||
<ItemGroup Condition=" '$(TargetFramework)' != 'netstandard2.0' "> | ||
<FrameworkReference Include="Microsoft.AspNetCore.App" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="AsyncAPI.NET.Readers" Version="5.2.1" /> | ||
<PackageReference Include="ConsoleAppFramework" Version="5.2.1"> | ||
<PrivateAssets>all</PrivateAssets> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
</PackageReference> | ||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.7" /> | ||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\Saunter\Saunter.csproj" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<None Include="readme.md" Pack="true" PackagePath="/" /> | ||
<None Include="../../assets/logo.png" Pack="true" PackagePath="/" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
using AsyncAPI.Saunter.Generator.Cli.ToFile; | ||
using ConsoleAppFramework; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.Logging; | ||
|
||
var services = new ServiceCollection(); | ||
services.AddLogging(builder => builder.AddSimpleConsole(x => x.SingleLine = true).SetMinimumLevel(LogLevel.Trace)); | ||
services.AddToFileCommand(); | ||
|
||
using var serviceProvider = services.BuildServiceProvider(); | ||
var logger = serviceProvider.GetRequiredService<ILogger<Program>>(); | ||
ConsoleApp.LogError = msg => logger.LogError(msg); | ||
ConsoleApp.ServiceProvider = serviceProvider; | ||
|
||
var app = ConsoleApp.Create(); | ||
app.Add<ToFileCommand>(); | ||
app.Run(args); | ||
|
||
Environment.ExitCode = 0; |
72 changes: 72 additions & 0 deletions
72
src/AsyncAPI.Saunter.Generator.Cli/ToFile/AsyncApiDocumentExtractor.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
using LEGO.AsyncAPI.Models; | ||
using LEGO.AsyncAPI.Readers; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.Logging; | ||
using Microsoft.Extensions.Options; | ||
using Saunter; | ||
using Saunter.Serialization; | ||
|
||
namespace AsyncAPI.Saunter.Generator.Cli.ToFile; | ||
|
||
internal interface IAsyncApiDocumentExtractor | ||
{ | ||
IEnumerable<(string name, AsyncApiDocument document)> GetAsyncApiDocument(IServiceProvider serviceProvider, string[] requestedDocuments); | ||
} | ||
|
||
internal class AsyncApiDocumentExtractor(ILogger<AsyncApiDocumentExtractor> logger) : IAsyncApiDocumentExtractor | ||
{ | ||
private IEnumerable<string> GetDocumentNames(string[] requestedDocuments, AsyncApiOptions asyncApiOptions) | ||
{ | ||
var documentNames = requestedDocuments ?? asyncApiOptions.NamedApis.Keys; | ||
if (documentNames.Count == 0) | ||
{ | ||
if (asyncApiOptions.AssemblyMarkerTypes.Any()) | ||
{ | ||
documentNames = [null]; // null marks the default, unnamed, document | ||
} | ||
else | ||
{ | ||
logger.LogCritical($"AsyncAPI documents found. Known named document(s): {string.Join(", ", asyncApiOptions.NamedApis.Keys)}."); | ||
} | ||
} | ||
return documentNames; | ||
} | ||
|
||
public IEnumerable<(string name, AsyncApiDocument document)> GetAsyncApiDocument(IServiceProvider serviceProvider, string[] requestedDocuments) | ||
{ | ||
var documentProvider = serviceProvider.GetService<IAsyncApiDocumentProvider>(); | ||
var asyncApiOptions = serviceProvider.GetService<IOptions<AsyncApiOptions>>().Value; | ||
var documentSerializer = serviceProvider.GetRequiredService<IAsyncApiDocumentSerializer>(); | ||
|
||
foreach (var documentName in GetDocumentNames(requestedDocuments, asyncApiOptions)) | ||
{ | ||
if (documentName == null || !asyncApiOptions.NamedApis.TryGetValue(documentName, out var prototype)) | ||
{ | ||
prototype = asyncApiOptions.AsyncApi; | ||
} | ||
|
||
var schema = documentProvider.GetDocument(asyncApiOptions, prototype); | ||
var asyncApiSchemaJson = documentSerializer.Serialize(schema); | ||
var asyncApiDocument = new AsyncApiStringReader().Read(asyncApiSchemaJson, out var diagnostic); | ||
if (diagnostic.Errors.Any()) | ||
{ | ||
logger.LogError($"AsyncAPI Schema '{documentName ?? "default"}' is not valid ({diagnostic.Errors.Count} Error(s))"); | ||
foreach (var error in diagnostic.Errors) | ||
{ | ||
logger.LogError($"- {error}"); | ||
} | ||
} | ||
if (diagnostic.Warnings.Any()) | ||
{ | ||
logger.LogWarning($"AsyncAPI Schema '{documentName ?? "default"}' has {diagnostic.Warnings.Count} Warning(s):"); | ||
foreach (var warning in diagnostic.Warnings) | ||
{ | ||
logger.LogWarning($"- {warning}"); | ||
} | ||
} | ||
|
||
yield return (documentName, asyncApiDocument); | ||
} | ||
} | ||
} | ||
|
22 changes: 22 additions & 0 deletions
22
src/AsyncAPI.Saunter.Generator.Cli/ToFile/DependencyResolver.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
using System.Reflection; | ||
|
||
namespace AsyncAPI.Saunter.Generator.Cli.ToFile; | ||
|
||
internal static class DependencyResolver | ||
{ | ||
public static void Init(string startupAssemblyBasePath) | ||
{ | ||
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => | ||
{ | ||
var requestedAssembly = new AssemblyName(args.Name); | ||
var fullPath = Path.Combine(startupAssemblyBasePath, $"{requestedAssembly.Name}.dll"); | ||
if (File.Exists(fullPath)) | ||
{ | ||
var assembly = Assembly.LoadFile(fullPath); | ||
return assembly; | ||
} | ||
Console.WriteLine($"Could not resolve assembly: {args.Name}, requested by {args.RequestingAssembly?.FullName}"); | ||
return default; | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace AsyncAPI.Saunter.Generator.Cli.ToFile; | ||
|
||
internal interface IEnvironmentBuilder | ||
{ | ||
void SetEnvironmentVariables(string env); | ||
} | ||
|
||
internal class EnvironmentBuilder(ILogger<EnvironmentBuilder> logger) : IEnvironmentBuilder | ||
{ | ||
public void SetEnvironmentVariables(string env) | ||
{ | ||
var envVars = !string.IsNullOrWhiteSpace(env) ? env.Split(',').Select(x => x.Trim()) : Array.Empty<string>(); | ||
var keyValues = envVars.Select(x => x.Split('=').Select(x => x.Trim()).ToList()); | ||
foreach (var envVar in keyValues) | ||
{ | ||
if (envVar.Count == 2 && !string.IsNullOrWhiteSpace(envVar[0])) | ||
{ | ||
Environment.SetEnvironmentVariable(envVar[0], envVar[1], EnvironmentVariableTarget.Process); | ||
logger.LogDebug($"Set environment variable: {envVar[0]} = {envVar[1]}"); | ||
} | ||
else | ||
{ | ||
logger.LogCritical("Environment variables should be in the format: env1=value1,env2=value2"); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace AsyncAPI.Saunter.Generator.Cli.ToFile; | ||
|
||
internal interface IFileWriter | ||
{ | ||
void Write(string outputPath, string fileTemplate, string documentName, string format, Action<Stream> streamWriter); | ||
} | ||
|
||
internal class FileWriter(IStreamProvider streamProvider, ILogger<FileWriter> logger) : IFileWriter | ||
{ | ||
public void Write(string outputPath, string fileTemplate, string documentName, string format, Action<Stream> streamWriter) | ||
{ | ||
var fullFileName = AddFileExtension(outputPath, fileTemplate, documentName, format); | ||
this.WriteFile(fullFileName, streamWriter); | ||
} | ||
|
||
private void WriteFile(string outputPath, Action<Stream> writeAction) | ||
{ | ||
using var stream = streamProvider.GetStreamFor(outputPath); | ||
writeAction(stream); | ||
|
||
if (outputPath != null) | ||
{ | ||
logger.LogInformation($"AsyncAPI {Path.GetExtension(outputPath).TrimStart('.')} successfully written to {outputPath}"); | ||
} | ||
} | ||
|
||
private static string AddFileExtension(string outputPath, string fileTemplate, string documentName, string extension) | ||
{ | ||
if (outputPath == null) | ||
{ | ||
return outputPath; | ||
} | ||
|
||
return Path.GetFullPath(Path.Combine(outputPath, fileTemplate.Replace("{document}", documentName ?? "") | ||
.Replace("{extension}", extension).TrimStart('_'))); | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
src/AsyncAPI.Saunter.Generator.Cli/ToFile/ServiceExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
using Microsoft.Extensions.DependencyInjection; | ||
|
||
namespace AsyncAPI.Saunter.Generator.Cli.ToFile; | ||
|
||
internal static class ServiceExtensions | ||
{ | ||
public static IServiceCollection AddToFileCommand(this IServiceCollection services) | ||
{ | ||
services.AddTransient<IEnvironmentBuilder, EnvironmentBuilder>(); | ||
services.AddTransient<IServiceProviderBuilder, ServiceProviderBuilder>(); | ||
services.AddTransient<IAsyncApiDocumentExtractor, AsyncApiDocumentExtractor>(); | ||
services.AddTransient<IStreamProvider, StreamProvider>(); | ||
services.AddTransient<IFileWriter, FileWriter>(); | ||
return services; | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
src/AsyncAPI.Saunter.Generator.Cli/ToFile/ServiceProviderBuilder.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
using System.Runtime.Loader; | ||
using Microsoft.EntityFrameworkCore.Design; | ||
using Microsoft.EntityFrameworkCore.Design.Internal; | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace AsyncAPI.Saunter.Generator.Cli.ToFile; | ||
|
||
internal interface IServiceProviderBuilder | ||
{ | ||
IServiceProvider BuildServiceProvider(string startupAssembly); | ||
} | ||
|
||
internal class ServiceProviderBuilder(ILogger<ServiceProviderBuilder> logger) : IServiceProviderBuilder | ||
{ | ||
public IServiceProvider BuildServiceProvider(string startupAssembly) | ||
{ | ||
var fullPath = Path.GetFullPath(startupAssembly); | ||
var basePath = Path.GetDirectoryName(fullPath); | ||
DependencyResolver.Init(basePath); | ||
|
||
logger.LogInformation($"Loading startup assembly: {fullPath}"); | ||
var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(fullPath); | ||
var reporter = new OperationReporter(new OperationReportHandler( | ||
m => logger.LogError(m), | ||
m => logger.LogWarning(m), | ||
m => logger.LogInformation(m), | ||
m => logger.LogDebug(m))); | ||
var appServiceProvider = new AppServiceProviderFactory(assembly, reporter); | ||
var serviceProvider = appServiceProvider.Create([]); | ||
|
||
return serviceProvider; | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
src/AsyncAPI.Saunter.Generator.Cli/ToFile/StreamProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
using System.Diagnostics; | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace AsyncAPI.Saunter.Generator.Cli.ToFile; | ||
|
||
internal interface IStreamProvider | ||
{ | ||
Stream GetStreamFor(string path); | ||
} | ||
|
||
internal class StreamProvider(ILogger<StreamProvider> logger) : IStreamProvider | ||
{ | ||
public Stream GetStreamFor(string path) | ||
{ | ||
logger.LogDebug($"GetStreamFor(path: {path})"); | ||
|
||
if (!string.IsNullOrEmpty(path)) | ||
{ | ||
Directory.CreateDirectory(Path.GetDirectoryName(path)); | ||
} | ||
|
||
return path != null ? File.Create(path) : Console.OpenStandardOutput(); | ||
} | ||
} |
73 changes: 73 additions & 0 deletions
73
src/AsyncAPI.Saunter.Generator.Cli/ToFile/ToFileCommand.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
using ConsoleAppFramework; | ||
using LEGO.AsyncAPI; | ||
using LEGO.AsyncAPI.Models; | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace AsyncAPI.Saunter.Generator.Cli.ToFile; | ||
|
||
internal class ToFileCommand(ILogger<ToFileCommand> logger, IEnvironmentBuilder environment, IServiceProviderBuilder builder, IAsyncApiDocumentExtractor docExtractor, IFileWriter fileWriter) | ||
{ | ||
private const string DEFAULT_FILENAME = "{document}_asyncapi.{extension}"; | ||
|
||
/// <summary> | ||
/// Retrieves AsyncAPI spec from a startup assembly and writes to file. | ||
/// </summary> | ||
/// <param name="startupassembly">relative path to the application's startup assembly</param> | ||
/// <param name="output">-o,relative path where the AsyncAPI documents will be exported to</param> | ||
/// <param name="doc">-d,name(s) of the AsyncAPI documents you want to export as configured in your startup class. To export all documents using null.</param> | ||
/// <param name="format">exports AsyncAPI in json and/or yml format</param> | ||
/// <param name="filename">defines the file name template, {document} and {extension} template variables can be used</param> | ||
/// <param name="env">define environment variable(s) for the application. Formatted as a comma separated list of _key=value_ pairs</param> | ||
[Command("tofile")] | ||
public int ToFile([Argument] string startupassembly, string output = "./", string doc = null, string format = "json", string filename = DEFAULT_FILENAME, string env = "") | ||
{ | ||
if (!File.Exists(startupassembly)) | ||
{ | ||
throw new FileNotFoundException(startupassembly); | ||
} | ||
|
||
// Prepare environment | ||
environment.SetEnvironmentVariables(env); | ||
|
||
// Get service provider from startup assembly | ||
var serviceProvider = builder.BuildServiceProvider(startupassembly); | ||
|
||
// Retrieve AsyncAPI via service provider | ||
var documents = docExtractor.GetAsyncApiDocument(serviceProvider, !string.IsNullOrWhiteSpace(doc) ? doc.Split(',', StringSplitOptions.RemoveEmptyEntries) : null); | ||
foreach (var (documentName, asyncApiDocument) in documents) | ||
{ | ||
// Serialize to specified output location or stdout | ||
var outputPath = !string.IsNullOrWhiteSpace(output) ? Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), output)) : null; | ||
|
||
var exportJson = true; | ||
var exportYml = false; | ||
var exportYaml = false; | ||
if (!string.IsNullOrWhiteSpace(format)) | ||
{ | ||
var splitted = format.Split(',').Select(x => x.Trim()).ToList(); | ||
exportJson = splitted.Any(x => x.Equals("json", StringComparison.OrdinalIgnoreCase)); | ||
exportYml = splitted.Any(x => x.Equals("yml", StringComparison.OrdinalIgnoreCase)); | ||
exportYaml = splitted.Any(x => x.Equals("yaml", StringComparison.OrdinalIgnoreCase)); | ||
} | ||
logger.LogDebug($"Format: exportJson={exportJson}, exportYml={exportYml}, exportYaml={exportYaml}."); | ||
|
||
var fileTemplate = !string.IsNullOrWhiteSpace(filename) ? filename : DEFAULT_FILENAME; | ||
if (exportJson) | ||
{ | ||
fileWriter.Write(outputPath, fileTemplate, documentName, "json", stream => asyncApiDocument.SerializeAsJson(stream, AsyncApiVersion.AsyncApi2_0)); | ||
} | ||
|
||
if (exportYml) | ||
{ | ||
fileWriter.Write(outputPath, fileTemplate, documentName, "yml", stream => asyncApiDocument.SerializeAsYaml(stream, AsyncApiVersion.AsyncApi2_0)); | ||
} | ||
|
||
if (exportYaml) | ||
{ | ||
fileWriter.Write(outputPath, fileTemplate, documentName, "yaml", stream => asyncApiDocument.SerializeAsYaml(stream, AsyncApiVersion.AsyncApi2_0)); | ||
} | ||
} | ||
|
||
return 0; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# AsyncApi Generator.Cli Tool | ||
A dotnet tool to generate AsyncAPI specification files based of a dotnet assembly (The application itself). | ||
|
||
This tool can used to manually generate spec files from a startup assembly or to setup a custom build pipeline. | ||
If you are intressed in always generating spec files at build time, see [AsyncAPI.Saunter.Generator.Build](https://www.nuget.org/packages/AsyncAPI.Saunter.Generator.Build). | ||
|
||
## Tool usage | ||
``` | ||
dotnet asyncapi tofile [startup-assembly] --output [output-path] --format [json,yml,yaml] --doc [asyncapi-document-name] | ||
``` | ||
- _startup-assembly_: the file path to the dotnet startup assembly (DLL) that hosts AsyncAPI document(s). | ||
|
||
## Tool options | ||
- _--doc_: The name of the AsyncAPI document as defined in the startup class by the ```.ConfigureNamedAsyncApi()```-method. If only ```.AddAsyncApiSchemaGeneration()``` is used, the document is unnamed and will always be exported. If not specified, all documents will be exported. | ||
- _--output_: relative path where the AsyncAPI documents will be exported to (Default: the csproj root "./"). | ||
- _--filename_: the template for the outputted file names (Default: "{document}_asyncapi.{extension}"). | ||
- _--format_: the output formats to generate, can be a combination of json, yml and/or yaml (Default: "json"). | ||
- _--env_: define environment variable(s) for the application. Formatted as a comma separated list of _key=value_ pairs, example: ```ASPNETCORE_ENVIRONMENT=AsyncAPI,CONNECT_TO_DATABASE=false```. | ||
|
||
## Install the Generator.Cli dotnet Tool | ||
``` | ||
dotnet tool install --global AsyncAPI.Saunter.Generator.Cli | ||
``` | ||
After installing the tool globally, it is available using commands: ```dotnet asyncapi``` or ```dotnet-asyncapi``` | ||
|
||
Want to learn more about .NET tools? Or want to install it local using a manifest? | ||
[Check out this Microsoft page on how to manage .NET tools](https://learn.microsoft.com/en-us/dotnet/core/tools/global-tools) | ||
|
||
## Internals | ||
How does the tool work internally? It tries to exact an ```IServiceProvider``` from the provided _startup-assembly_ and exports AsyncApiDocument(s) as registered with the services provider. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<Project> | ||
|
||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> | ||
<!-- 999.* are local builds numbers, they must be unique each build to bypass all nuget caching --> | ||
<Version>999.$([System.DateTime]::Now.ToString("yy"))$([System.DateTime]::Now.DayOfYear).$([System.DateTime]::Now.ToString("HHmm"))</Version> | ||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> | ||
<PackageOutputPath>../../local-nuget-source</PackageOutputPath> | ||
</PropertyGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{ | ||
"solution": { | ||
"path": "..\\Saunter.sln", | ||
"projects": [ | ||
"src\\AsyncAPI.Saunter.Generator.Build\\AsyncAPI.Saunter.Generator.Build.csproj", | ||
"src\\AsyncAPI.Saunter.Generator.Cli\\AsyncAPI.Saunter.Generator.Cli.csproj", | ||
"src\\Saunter\\Saunter.csproj" | ||
] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
31 changes: 31 additions & 0 deletions
31
test/AsyncAPI.Saunter.Generator.Build.Tests/AsyncAPI.Saunter.Generator.Build.Tests.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net8.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
|
||
<IsPackable>false</IsPackable> | ||
<IsTestProject>true</IsTestProject> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="coverlet.collector" Version="6.0.0" /> | ||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" /> | ||
<PackageReference Include="xunit" Version="2.9.0" /> | ||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2"> | ||
<PrivateAssets>all</PrivateAssets> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
</PackageReference> | ||
<PackageReference Include="Shouldly" Version="4.2.1" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\..\examples\StreetlightsAPI\StreetlightsAPI.csproj" /> | ||
<ProjectReference Include="..\AsyncAPI.Saunter.Generator.Cli.Tests\AsyncAPI.Saunter.Generator.Cli.Tests.csproj" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<Using Include="Xunit" /> | ||
</ItemGroup> | ||
|
||
</Project> |
74 changes: 74 additions & 0 deletions
74
test/AsyncAPI.Saunter.Generator.Build.Tests/StreetlightsApiBuildTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// 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 AsyncAPI.Saunter.Generator.Cli.Tests; | ||
using Shouldly; | ||
using Xunit.Abstractions; | ||
|
||
namespace AsyncAPI.Saunter.Generator.Build.Tests; | ||
|
||
public class IntegrationTests(ITestOutputHelper output) | ||
{ | ||
private string Run(string file, string args, string workingDirectory, int expectedExitCode = 0) | ||
{ | ||
var process = Process.Start(new ProcessStartInfo(file) | ||
{ | ||
Arguments = args, | ||
WorkingDirectory = workingDirectory, | ||
RedirectStandardOutput = true, | ||
RedirectStandardError = true, | ||
UseShellExecute = false, | ||
}); | ||
process.WaitForExit(TimeSpan.FromSeconds(20)); | ||
var stdOut = process.StandardOutput.ReadToEnd().Trim(); | ||
var stdError = process.StandardError.ReadToEnd().Trim(); | ||
output.WriteLine($"### Output of \"{file} {args}\""); | ||
output.WriteLine(stdOut); | ||
output.WriteLine(stdError); | ||
|
||
process.ExitCode.ShouldBe(expectedExitCode); | ||
return stdOut; | ||
} | ||
|
||
[Theory] | ||
[InlineData("StreetlightsAPI")] | ||
[InlineData("StreetlightsAPI.TopLevelStatement")] | ||
public void BuildingCsprojGeneratesSpecFilesTest(string csproj) | ||
{ | ||
var pwd = Directory.GetCurrentDirectory(); | ||
var csprojFullPath = Path.GetFullPath(Path.Combine(pwd, $"../../../../../examples/{csproj}/{csproj}.csproj")); | ||
output.WriteLine($"Current working directory: {pwd}"); | ||
output.WriteLine($"Csproj under test: {csprojFullPath}"); | ||
File.Exists(csprojFullPath).ShouldBeTrue(); | ||
|
||
var csprojDir = Path.GetDirectoryName(csprojFullPath); | ||
var specDir = Path.Combine(csprojDir, "specs"); | ||
|
||
// Spec files should have been generated during the builds of the solution | ||
Directory.GetFiles(specDir).Length.ShouldBe(2, $"#Spec files initial, path: {specDir}"); | ||
File.ReadAllText(Path.Combine(specDir, "streetlights.yml")).ShouldBe(ExpectedSpecFiles.Yml_v2_6, "yml"); | ||
File.ReadAllText(Path.Combine(specDir, "streetlights.json")).ShouldBe(ExpectedSpecFiles.Json_v2_6, "json"); | ||
|
||
// Delete spec files | ||
foreach (var file in Directory.EnumerateFiles(specDir)) | ||
{ | ||
File.Delete(file); | ||
} | ||
Directory.GetFiles(specDir).Length.ShouldBe(0, $"#Spec files after deleting them all, path: {specDir}"); | ||
|
||
// Run build | ||
var stdOut = this.Run("dotnet", "build", csprojDir).Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar); | ||
stdOut.ShouldContain($"AsyncAPI json successfully written to {Path.Combine(specDir, "streetlights.json")}"); | ||
stdOut.ShouldContain($"AsyncAPI yml successfully written to {Path.Combine(specDir, "streetlights.yml")}"); | ||
stdOut.ShouldContain("Build succeeded."); | ||
stdOut.ShouldContain("0 Warning(s)"); | ||
stdOut.ShouldContain("0 Error(s)"); | ||
|
||
// Check that spec files are actually re-generated | ||
Directory.GetFiles(specDir).Length.ShouldBe(2, $"#Spec files after running build, path: {specDir}"); | ||
File.ReadAllText(Path.Combine(specDir, "streetlights.yml")).ShouldBe(ExpectedSpecFiles.Yml_v2_6, "yml"); | ||
File.ReadAllText(Path.Combine(specDir, "streetlights.json")).ShouldBe(ExpectedSpecFiles.Json_v2_6, "json"); | ||
} | ||
} |
44 changes: 44 additions & 0 deletions
44
test/AsyncAPI.Saunter.Generator.Cli.Tests/AsyncAPI.Saunter.Generator.Cli.Tests.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net8.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
|
||
<IsPackable>false</IsPackable> | ||
<IsTestProject>true</IsTestProject> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="AsyncAPI.NET" Version="5.2.1" /> | ||
<PackageReference Include="coverlet.collector" Version="6.0.0" /> | ||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" /> | ||
<PackageReference Include="NSubstitute.Community.Logging" Version="0.0.1" /> | ||
<PackageReference Include="xunit" Version="2.9.0" /> | ||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2"> | ||
<PrivateAssets>all</PrivateAssets> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
</PackageReference> | ||
<PackageReference Include="Shouldly" Version="4.2.1" /> | ||
<PackageReference Include="NSubstitute" Version="5.1.0" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\..\examples\StreetlightsAPI.TopLevelStatement\StreetlightsAPI.TopLevelStatement.csproj" /> | ||
<ProjectReference Include="..\..\examples\StreetlightsAPI\StreetlightsAPI.csproj" /> | ||
<ProjectReference Include="..\..\src\AsyncAPI.Saunter.Generator.Cli\AsyncAPI.Saunter.Generator.Cli.csproj" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<Using Include="Xunit" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<None Update="Specs\streetlights_v2.6.json"> | ||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||
</None> | ||
<None Update="Specs\streetlights_v2.6.yml"> | ||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||
</None> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
using System.Diagnostics; | ||
using Shouldly; | ||
using Xunit.Abstractions; | ||
|
||
namespace AsyncAPI.Saunter.Generator.Cli.Tests; | ||
|
||
public class E2ETests(ITestOutputHelper output) | ||
{ | ||
private string Run(string file, string args, string workingDirectory, int expectedExitCode = 0) | ||
{ | ||
var process = Process.Start(new ProcessStartInfo(file) | ||
{ | ||
Arguments = args, | ||
WorkingDirectory = workingDirectory, | ||
RedirectStandardOutput = true, | ||
RedirectStandardError = true, | ||
UseShellExecute = false, | ||
}); | ||
process.WaitForExit(TimeSpan.FromSeconds(20)); | ||
var stdOut = process.StandardOutput.ReadToEnd().Trim(); | ||
var stdError = process.StandardError.ReadToEnd().Trim(); | ||
output.WriteLine($"### Output of \"{file} {args}\""); | ||
output.WriteLine(stdOut); | ||
output.WriteLine(stdError); | ||
|
||
process.ExitCode.ShouldBe(expectedExitCode); | ||
return stdOut; | ||
} | ||
|
||
[Fact(Skip = "Manual verification only")] | ||
public void Pack_Install_Run_Uninstall_Test() | ||
{ | ||
var workingDirectory = "../../../../../src/AsyncAPI.Saunter.Generator.Cli"; | ||
var stdOut = this.Run("dotnet", "pack", workingDirectory); | ||
stdOut.ShouldContain("Successfully created package"); | ||
|
||
// use --force flag to ensure the test starts clean every run | ||
stdOut = this.Run("dotnet", "new tool-manifest --force", workingDirectory); | ||
stdOut.ShouldContain("The template \"Dotnet local tool manifest file\" was created successfully"); | ||
|
||
stdOut = this.Run("dotnet", "tool install --local --add-source ./bin/Release AsyncAPI.Saunter.Generator.Cli", workingDirectory); | ||
stdOut = stdOut.Replace("Skipping NuGet package signature verification.", "").Trim(); | ||
stdOut.ShouldContain("You can invoke the tool from this directory using the following commands: 'dotnet tool run dotnet-asyncapi"); | ||
stdOut.ShouldContain("was successfully installed."); | ||
|
||
stdOut = this.Run("dotnet", "tool list --local asyncapi.saunter.generator.cli", workingDirectory); | ||
stdOut.ShouldContain("dotnet-asyncapi"); | ||
|
||
stdOut = this.Run("dotnet", "tool run dotnet-asyncapi", workingDirectory, 1); | ||
stdOut.ShouldContain("tofile: retrieves AsyncAPI from a startup assembly, and writes to file"); | ||
|
||
stdOut = this.Run("dotnet", "tool uninstall --local asyncapi.saunter.generator.cli", workingDirectory); | ||
stdOut.ShouldContain(" was successfully uninstalled"); | ||
stdOut.ShouldContain("removed from manifest file"); | ||
|
||
stdOut = this.Run("dotnet", "tool list --local asyncapi.saunter.generator.cli", workingDirectory, 1); | ||
stdOut.ShouldNotContain("dotnet-asyncapi"); | ||
} | ||
} |
85 changes: 85 additions & 0 deletions
85
test/AsyncAPI.Saunter.Generator.Cli.Tests/IntegrationTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
using Shouldly; | ||
using Xunit.Abstractions; | ||
|
||
namespace AsyncAPI.Saunter.Generator.Cli.Tests; | ||
|
||
public class IntegrationTests(ITestOutputHelper output) | ||
{ | ||
private string RunTool(string args, int expectedExitCode = 0) | ||
{ | ||
using var outWriter = new StringWriter(); | ||
using var errorWriter = new StringWriter(); | ||
Console.SetOut(outWriter); | ||
Console.SetError(errorWriter); | ||
|
||
var entryPoint = typeof(Program).Assembly.EntryPoint!; | ||
entryPoint.Invoke(null, new object[] { args.Split(' ') }); | ||
|
||
var stdOut = outWriter.ToString(); | ||
var stdError = errorWriter.ToString(); | ||
output.WriteLine($"RUN: {args}"); | ||
output.WriteLine("### STD OUT"); | ||
output.WriteLine(stdOut); | ||
output.WriteLine("### STD ERROR"); | ||
output.WriteLine(stdError); | ||
|
||
Environment.ExitCode.ShouldBe(expectedExitCode); | ||
//stdError.ShouldBeEmpty(); LEGO lib doesn't like id: "id is not a valid property at #/components/schemas/lightMeasuredEvent"" | ||
return stdOut; | ||
} | ||
|
||
[Fact] | ||
public void DefaultCallPrintsCommandInfo() | ||
{ | ||
var stdOut = RunTool("tofile").Trim(); | ||
|
||
stdOut.ShouldBe(""" | ||
Usage: tofile [arguments...] [options...] [-h|--help] [--version] | ||
Retrieves AsyncAPI spec from a startup assembly and writes to file. | ||
Arguments: | ||
[0] <string> relative path to the application's startup assembly | ||
Options: | ||
-o|--output <string> relative path where the AsyncAPI documents will be exported to (Default: "./") | ||
-d|--doc <string> name(s) of the AsyncAPI documents you want to export as configured in your startup class. To export all documents using null. (Default: null) | ||
--format <string> exports AsyncAPI in json and/or yml format (Default: "json") | ||
--filename <string> defines the file name template, {document} and {extension} template variables can be used (Default: "{document}_asyncapi.{extension}") | ||
--env <string> define environment variable(s) for the application. Formatted as a comma separated list of _key=value_ pairs (Default: "") | ||
""", StringCompareShould.IgnoreLineEndings); | ||
} | ||
|
||
/// <remarks> | ||
/// Both example projects are used to check whether AsyncAPI spec generation is working because they are targeting different .NET versions and are using different hosting strategies. | ||
/// - StreetlightsAPI project is targeting NET6 using the 'old school' Startup-class hosting mechanism. | ||
/// - StreetlightsAPI.TopLevelStatement project is targeting NET8 using the new Top Level Statement hosting mechanism. | ||
/// </remarks> | ||
[Theory] | ||
[InlineData("StreetlightsAPI", "net6.0")] | ||
[InlineData("StreetlightsAPI.TopLevelStatement", "net8.0")] | ||
public void Streetlights_ExportSpecTest(string csprojName, string targetFramework) | ||
{ | ||
var path = Path.Combine(Directory.GetCurrentDirectory()); | ||
output.WriteLine($"Output path: {path}"); | ||
var stdOut = RunTool($"tofile ../../../../../examples/{csprojName}/bin/Debug/{targetFramework}/{csprojName}.dll --output {path} --filename {csprojName}.{{extension}} --format json,yml,yaml"); | ||
|
||
stdOut.ShouldNotBeEmpty(); | ||
stdOut.ShouldContain($"AsyncAPI yaml successfully written to {Path.Combine(path, $"{csprojName}.yaml")}"); | ||
stdOut.ShouldContain($"AsyncAPI yml successfully written to {Path.Combine(path, $"{csprojName}.yml")}"); | ||
stdOut.ShouldContain($"AsyncAPI json successfully written to {Path.Combine(path, $"{csprojName}.json")}"); | ||
|
||
File.Exists(Path.Combine(path, $"{csprojName}.yml")).ShouldBeTrue(); | ||
File.Exists(Path.Combine(path, $"{csprojName}.yaml")).ShouldBeTrue(); | ||
File.Exists(Path.Combine(path, $"{csprojName}.json")).ShouldBeTrue(); | ||
|
||
var yml = File.ReadAllText(Path.Combine(path, $"{csprojName}.yml")); | ||
yml.ShouldBe(ExpectedSpecFiles.Yml_v2_6, "yml"); | ||
|
||
var yaml = File.ReadAllText(Path.Combine(path, $"{csprojName}.yaml")); | ||
yaml.ShouldBe(yml, "yaml"); | ||
|
||
var json = File.ReadAllText(Path.Combine(path, $"{csprojName}.json")); | ||
json.ShouldBe(ExpectedSpecFiles.Json_v2_6, "json"); | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
test/AsyncAPI.Saunter.Generator.Cli.Tests/Specs/ExpectedSpecFiles.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
namespace AsyncAPI.Saunter.Generator.Cli.Tests; | ||
|
||
public static class ExpectedSpecFiles | ||
{ | ||
public static string Json_v2_6 => File.ReadAllText("Specs/streetlights_v2.6.json"); | ||
|
||
public static string Yml_v2_6 => File.ReadAllText("Specs/streetlights_v2.6.yml"); | ||
} |
94 changes: 94 additions & 0 deletions
94
test/AsyncAPI.Saunter.Generator.Cli.Tests/Specs/streetlights_v2.6.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
{ | ||
"asyncapi": "2.6.0", | ||
"info": { | ||
"title": "Streetlights API", | ||
"version": "1.0.0", | ||
"description": "The Smartylighting Streetlights API allows you to remotely manage the city lights.", | ||
"license": { | ||
"name": "Apache 2.0", | ||
"url": "https://www.apache.org/licenses/LICENSE-2.0" | ||
} | ||
}, | ||
"servers": { | ||
"mosquitto": { | ||
"url": "test.mosquitto.org", | ||
"protocol": "mqtt" | ||
}, | ||
"webapi": { | ||
"url": "localhost:5000", | ||
"protocol": "http" | ||
} | ||
}, | ||
"defaultContentType": "application/json", | ||
"channels": { | ||
"publish/light/measured": { | ||
"servers": [ | ||
"webapi" | ||
], | ||
"publish": { | ||
"operationId": "MeasureLight", | ||
"summary": "Inform about environmental lighting conditions for a particular streetlight.", | ||
"tags": [ | ||
{ | ||
"name": "Light" | ||
} | ||
], | ||
"message": { | ||
"$ref": "#/components/messages/lightMeasuredEvent" | ||
} | ||
} | ||
}, | ||
"subscribe/light/measured": { | ||
"servers": [ | ||
"mosquitto" | ||
], | ||
"subscribe": { | ||
"operationId": "PublishLightMeasurement", | ||
"summary": "Subscribe to environmental lighting conditions for a particular streetlight.", | ||
"tags": [ | ||
{ | ||
"name": "Light" | ||
} | ||
], | ||
"message": { | ||
"payload": { | ||
"$ref": "#/components/schemas/lightMeasuredEvent" | ||
} | ||
} | ||
} | ||
} | ||
}, | ||
"components": { | ||
"schemas": { | ||
"lightMeasuredEvent": { | ||
"type": "object", | ||
"properties": { | ||
"id": { | ||
"type": "integer", | ||
"format": "int32", | ||
"description": "Id of the streetlight." | ||
}, | ||
"lumens": { | ||
"type": "integer", | ||
"format": "int32", | ||
"description": "Light intensity measured in lumens." | ||
}, | ||
"sentAt": { | ||
"type": "string", | ||
"format": "date-time", | ||
"description": "Light intensity measured in lumens." | ||
} | ||
}, | ||
"additionalProperties": false | ||
} | ||
}, | ||
"messages": { | ||
"lightMeasuredEvent": { | ||
"payload": { | ||
"$ref": "#/components/schemas/lightMeasuredEvent" | ||
}, | ||
"name": "lightMeasuredEvent" | ||
} | ||
} | ||
} | ||
} |
61 changes: 61 additions & 0 deletions
61
test/AsyncAPI.Saunter.Generator.Cli.Tests/Specs/streetlights_v2.6.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
asyncapi: 2.6.0 | ||
info: | ||
title: Streetlights API | ||
version: 1.0.0 | ||
description: The Smartylighting Streetlights API allows you to remotely manage the city lights. | ||
license: | ||
name: Apache 2.0 | ||
url: https://www.apache.org/licenses/LICENSE-2.0 | ||
servers: | ||
mosquitto: | ||
url: test.mosquitto.org | ||
protocol: mqtt | ||
webapi: | ||
url: localhost:5000 | ||
protocol: http | ||
defaultContentType: application/json | ||
channels: | ||
publish/light/measured: | ||
servers: | ||
- webapi | ||
publish: | ||
operationId: MeasureLight | ||
summary: Inform about environmental lighting conditions for a particular streetlight. | ||
tags: | ||
- name: Light | ||
message: | ||
$ref: '#/components/messages/lightMeasuredEvent' | ||
subscribe/light/measured: | ||
servers: | ||
- mosquitto | ||
subscribe: | ||
operationId: PublishLightMeasurement | ||
summary: Subscribe to environmental lighting conditions for a particular streetlight. | ||
tags: | ||
- name: Light | ||
message: | ||
payload: | ||
$ref: '#/components/schemas/lightMeasuredEvent' | ||
components: | ||
schemas: | ||
lightMeasuredEvent: | ||
type: object | ||
properties: | ||
id: | ||
type: integer | ||
format: int32 | ||
description: Id of the streetlight. | ||
lumens: | ||
type: integer | ||
format: int32 | ||
description: Light intensity measured in lumens. | ||
sentAt: | ||
type: string | ||
format: date-time | ||
description: Light intensity measured in lumens. | ||
additionalProperties: false | ||
messages: | ||
lightMeasuredEvent: | ||
payload: | ||
$ref: '#/components/schemas/lightMeasuredEvent' | ||
name: lightMeasuredEvent |
152 changes: 152 additions & 0 deletions
152
test/AsyncAPI.Saunter.Generator.Cli.Tests/ToFile/AsyncApiDocumentExtractorTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
using AsyncAPI.Saunter.Generator.Cli.ToFile; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.Logging; | ||
using Microsoft.Extensions.Options; | ||
using NSubstitute; | ||
using NSubstitute.Community.Logging; | ||
using Saunter; | ||
using Saunter.AsyncApiSchema.v2; | ||
using Saunter.Serialization; | ||
using Shouldly; | ||
|
||
namespace AsyncAPI.Saunter.Generator.Cli.Tests.ToFile; | ||
|
||
public class AsyncApiDocumentExtractorTests | ||
{ | ||
private readonly AsyncApiDocumentExtractor _extractor; | ||
private readonly ILogger<AsyncApiDocumentExtractor> _logger; | ||
private readonly IServiceProvider _serviceProvider; | ||
private readonly IAsyncApiDocumentProvider _documentProvider; | ||
private readonly IOptions<AsyncApiOptions> _asyncApiOptions; | ||
private readonly IAsyncApiDocumentSerializer _documentSerializer; | ||
|
||
public AsyncApiDocumentExtractorTests() | ||
{ | ||
var services = new ServiceCollection(); | ||
this._documentProvider = Substitute.For<IAsyncApiDocumentProvider>(); | ||
this._asyncApiOptions = Substitute.For<IOptions<AsyncApiOptions>>(); | ||
var options = new AsyncApiOptions(); | ||
this._asyncApiOptions.Value.Returns(options); | ||
this._documentSerializer = Substitute.For<IAsyncApiDocumentSerializer>(); | ||
services.AddSingleton(this._documentProvider); | ||
services.AddSingleton(this._asyncApiOptions); | ||
services.AddSingleton(this._documentSerializer); | ||
this._serviceProvider = services.BuildServiceProvider(); | ||
|
||
this._logger = Substitute.For<ILogger<AsyncApiDocumentExtractor>>(); | ||
this._extractor = new AsyncApiDocumentExtractor(this._logger); | ||
} | ||
|
||
[Fact] | ||
public void GetAsyncApiDocument_Null_NoMarkerAssemblies() | ||
{ | ||
var documents = this._extractor.GetAsyncApiDocument(this._serviceProvider, null).ToList(); | ||
|
||
this._logger.Received(1).CallToLog(LogLevel.Critical); | ||
} | ||
|
||
[Fact] | ||
public void GetAsyncApiDocument_Default_WithMarkerAssembly() | ||
{ | ||
this._asyncApiOptions.Value.AssemblyMarkerTypes = [typeof(AsyncApiDocumentExtractorTests)]; | ||
var doc = new AsyncApiDocument(); | ||
this._documentProvider.GetDocument(default, default).ReturnsForAnyArgs(doc); | ||
this._documentSerializer.Serialize(doc).ReturnsForAnyArgs(""" | ||
asyncapi: 2.6.0 | ||
info: | ||
title: Streetlights API | ||
"""); | ||
|
||
var documents = this._extractor.GetAsyncApiDocument(this._serviceProvider, null).ToList(); | ||
|
||
this._logger.Received(0).CallToLog(LogLevel.Critical); | ||
documents.Count.ShouldBe(1); | ||
documents[0].name.ShouldBeNull(); | ||
documents[0].document.Info.Title.ShouldBe("Streetlights API"); | ||
} | ||
|
||
[Fact] | ||
public void GetAsyncApiDocument_1NamedDocument() | ||
{ | ||
this._asyncApiOptions.Value.AssemblyMarkerTypes = [typeof(AsyncApiDocumentExtractorTests)]; | ||
var doc = new AsyncApiDocument(); | ||
this._asyncApiOptions.Value.NamedApis["service 1"] = doc; | ||
this._documentProvider.GetDocument(default, default).ReturnsForAnyArgs(doc); | ||
this._documentSerializer.Serialize(doc).ReturnsForAnyArgs(""" | ||
asyncapi: 2.6.0 | ||
info: | ||
title: Streetlights API | ||
"""); | ||
|
||
var documents = this._extractor.GetAsyncApiDocument(this._serviceProvider, null).ToList(); | ||
|
||
this._logger.Received(0).CallToLog(LogLevel.Critical); | ||
documents.Count.ShouldBe(1); | ||
documents[0].name.ShouldBe("service 1"); | ||
documents[0].document.Info.Title.ShouldBe("Streetlights API"); | ||
} | ||
|
||
[Fact] | ||
public void GetAsyncApiDocument_2NamedDocument() | ||
{ | ||
this._asyncApiOptions.Value.AssemblyMarkerTypes = [typeof(AsyncApiDocumentExtractorTests)]; | ||
var doc1 = new AsyncApiDocument { Id = "1" }; | ||
var doc2 = new AsyncApiDocument { Id = "2" }; | ||
this._asyncApiOptions.Value.NamedApis["service 1"] = doc1; | ||
this._asyncApiOptions.Value.NamedApis["service 2"] = doc2; | ||
this._documentProvider.GetDocument(Arg.Any<AsyncApiOptions>(), Arg.Is(doc1)).Returns(doc1); | ||
this._documentProvider.GetDocument(Arg.Any<AsyncApiOptions>(), Arg.Is(doc2)).Returns(doc2); | ||
this._documentSerializer.Serialize(doc1).Returns(""" | ||
asyncapi: 2.6.0 | ||
info: | ||
title: Streetlights API 1 | ||
"""); | ||
this._documentSerializer.Serialize(doc2).Returns(""" | ||
asyncapi: 2.6.0 | ||
info: | ||
title: Streetlights API 2 | ||
"""); | ||
|
||
var documents = this._extractor.GetAsyncApiDocument(this._serviceProvider, null).OrderBy(x => x.name).ToList(); | ||
|
||
this._logger.Received(0).CallToLog(LogLevel.Critical); | ||
documents.Count.ShouldBe(2); | ||
documents[0].name.ShouldBe("service 1"); | ||
documents[0].document.Info.Title.ShouldBe("Streetlights API 1"); | ||
documents[1].name.ShouldBe("service 2"); | ||
documents[1].document.Info.Title.ShouldBe("Streetlights API 2"); | ||
} | ||
|
||
[Fact] | ||
public void GetAsyncApiDocument_LogErrors() | ||
{ | ||
this._asyncApiOptions.Value.AssemblyMarkerTypes = [typeof(AsyncApiDocumentExtractorTests)]; | ||
var doc = new AsyncApiDocument(); | ||
this._documentProvider.GetDocument(default, default).ReturnsForAnyArgs(doc); | ||
this._documentSerializer.Serialize(doc).ReturnsForAnyArgs(""" | ||
asyncapi: 2.6.0 | ||
info: | ||
title: Streetlights API | ||
channels: | ||
publish/light/measured: | ||
servers: | ||
- webapi | ||
publish: | ||
operationId: MeasureLight | ||
summary: Inform about environmental lighting conditions for a particular streetlight. | ||
tags: | ||
- name: Light | ||
message: | ||
$ref: '#/components/messages/lightMeasuredEvent' | ||
"""); | ||
|
||
var documents = this._extractor.GetAsyncApiDocument(this._serviceProvider, null).ToList(); | ||
|
||
this._logger.Received(0).CallToLog(LogLevel.Critical); | ||
this._logger.Received(3).CallToLog(LogLevel.Error); | ||
this._logger.Received(0).CallToLog(LogLevel.Warning); | ||
documents.Count.ShouldBe(1); | ||
documents[0].name.ShouldBeNull(); | ||
documents[0].document.Info.Title.ShouldBe("Streetlights API"); | ||
} | ||
} |
94 changes: 94 additions & 0 deletions
94
test/AsyncAPI.Saunter.Generator.Cli.Tests/ToFile/EnvironmentBuilderTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
using System.Collections; | ||
using AsyncAPI.Saunter.Generator.Cli.ToFile; | ||
using Microsoft.Extensions.Logging; | ||
using NSubstitute; | ||
using NSubstitute.Community.Logging; | ||
using Shouldly; | ||
|
||
namespace AsyncAPI.Saunter.Generator.Cli.Tests.ToFile; | ||
|
||
public class EnvironmentBuilderTests : IDisposable | ||
{ | ||
private readonly IDictionary _variablesBefore = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Process); | ||
private readonly EnvironmentBuilder _environment; | ||
private readonly ILogger<EnvironmentBuilder> _logger; | ||
|
||
public EnvironmentBuilderTests() | ||
{ | ||
this._logger = Substitute.For<ILogger<EnvironmentBuilder>>(); | ||
this._environment = new EnvironmentBuilder(this._logger); | ||
} | ||
|
||
private Dictionary<string, string> GetAddedEnvironmentVariables() | ||
{ | ||
var after = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Process); | ||
return after.Cast<DictionaryEntry>().ExceptBy(this._variablesBefore.Keys.Cast<string>(), x => x.Key).ToDictionary(x => x.Key.ToString(), x => x.Value?.ToString()); | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
foreach (var variable in this.GetAddedEnvironmentVariables()) | ||
{ | ||
Environment.SetEnvironmentVariable(variable.Key, null, EnvironmentVariableTarget.Process); | ||
} | ||
} | ||
|
||
[Theory] | ||
[InlineData(null)] | ||
[InlineData("")] | ||
[InlineData(" ")] | ||
public void EmptyEnvStringProvided(string env) | ||
{ | ||
this._environment.SetEnvironmentVariables(env); | ||
|
||
this._logger.ReceivedCalls().Count().ShouldBe(0); | ||
this.GetAddedEnvironmentVariables().ShouldBeEmpty(); | ||
} | ||
|
||
[Theory] | ||
[InlineData("env1=val1", 1)] | ||
[InlineData("a=b,b=c", 2)] | ||
public void ValidEnvStringProvided(string env, int expectedSets) | ||
{ | ||
this._environment.SetEnvironmentVariables(env); | ||
|
||
this._logger.Received(expectedSets).CallToLog(LogLevel.Debug); | ||
this.GetAddedEnvironmentVariables().ShouldNotBeEmpty(); | ||
} | ||
|
||
[Theory] | ||
[InlineData(",", 2)] | ||
[InlineData(",,,,", 5)] | ||
[InlineData("=a", 1)] | ||
[InlineData("b", 1)] | ||
[InlineData("=", 1)] | ||
[InlineData("====", 1)] | ||
public void InvalidEnvStringProvided(string env, int expectedSets) | ||
{ | ||
this._environment.SetEnvironmentVariables(env); | ||
|
||
this._logger.Received(expectedSets).CallToLog(LogLevel.Critical); | ||
this.GetAddedEnvironmentVariables().ShouldBeEmpty(); | ||
} | ||
|
||
[Fact] | ||
public void ValidateEnvValues() | ||
{ | ||
this._environment.SetEnvironmentVariables("ENV=1,,Test=two"); | ||
|
||
Environment.GetEnvironmentVariable("ENV").ShouldBe("1"); | ||
Environment.GetEnvironmentVariable("Test").ShouldBe("two"); | ||
} | ||
|
||
[Theory] | ||
[InlineData(null)] | ||
[InlineData("")] | ||
[InlineData(" ")] | ||
[InlineData(" ")] | ||
public void EmptyValueDeletesEnvValue(string value) | ||
{ | ||
this._environment.SetEnvironmentVariables($"ENV=1,,ENV={value}"); | ||
|
||
Environment.GetEnvironmentVariable("ENV").ShouldBe(null); | ||
} | ||
} |
107 changes: 107 additions & 0 deletions
107
test/AsyncAPI.Saunter.Generator.Cli.Tests/ToFile/FileWriterTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
using System.Text; | ||
using AsyncAPI.Saunter.Generator.Cli.ToFile; | ||
using Microsoft.Extensions.Logging; | ||
using NSubstitute; | ||
using Shouldly; | ||
using Xunit.Abstractions; | ||
|
||
namespace AsyncAPI.Saunter.Generator.Cli.Tests.ToFile; | ||
|
||
public class FileWriterTests | ||
{ | ||
private readonly Action<Stream> _testContextWriter = stream => stream.Write(Encoding.Default.GetBytes("ananas")); | ||
|
||
private readonly FileWriter _writer; | ||
private readonly IStreamProvider _streamProvider; | ||
private readonly ILogger<FileWriter> _logger; | ||
private readonly MemoryStream _stream = new(); | ||
|
||
public FileWriterTests(ITestOutputHelper output) | ||
{ | ||
this._logger = Substitute.For<ILogger<FileWriter>>(); | ||
this._streamProvider = Substitute.For<IStreamProvider>(); | ||
this._streamProvider.GetStreamFor(default).ReturnsForAnyArgs(x => | ||
{ | ||
output.WriteLine($"GetStreamFor({x.Args()[0]})"); | ||
return this._stream; | ||
}); | ||
this._writer = new FileWriter(this._streamProvider, this._logger); | ||
} | ||
|
||
[Fact] | ||
public void CheckStreamContents() | ||
{ | ||
this._writer.Write("/", "", "", "", _testContextWriter); | ||
|
||
this._streamProvider.Received(1).GetStreamFor(Path.GetFullPath("/")); | ||
Encoding.Default.GetString(this._stream.GetBuffer().Take(6).ToArray()).ShouldBe("ananas"); | ||
} | ||
|
||
[Fact] | ||
public void CheckName_NoVariablesInTemplate() | ||
{ | ||
this._writer.Write("/some/path", "fixed_name", "doc", "json", _testContextWriter); | ||
|
||
this._streamProvider.Received(1).GetStreamFor(Path.GetFullPath("/some/path/fixed_name")); | ||
} | ||
|
||
[Theory] | ||
[InlineData("./")] | ||
[InlineData("/")] | ||
[InlineData("/test/")] | ||
[InlineData("/test/1/2/3/4/")] | ||
public void CheckOutputPath_BaseOutputPath_Absolute(string path) | ||
{ | ||
this._writer.Write(path, "document.something", "", "", _testContextWriter); | ||
|
||
this._streamProvider.Received(1).GetStreamFor(Path.GetFullPath($"{path}document.something")); | ||
} | ||
|
||
[Theory] | ||
[InlineData(".")] | ||
[InlineData("")] | ||
[InlineData("asyncApi/")] | ||
[InlineData("service-1/")] | ||
[InlineData("service 1/")] | ||
[InlineData("service 1/spec")] | ||
public void CheckOutputPath_BaseOutputPath_Relative(string path) | ||
{ | ||
this._writer.Write(path, "document.something", "", "", _testContextWriter); | ||
|
||
this._streamProvider.Received(1).GetStreamFor(Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), path, "document.something"))); | ||
} | ||
|
||
[Theory] | ||
[InlineData("json")] | ||
[InlineData("yml")] | ||
[InlineData("txt")] | ||
public void CheckOutputPath_FormatTemplate(string format) | ||
{ | ||
this._writer.Write("/some/path", "{extension}_name.{extension}", "doc", format, _testContextWriter); | ||
|
||
this._streamProvider.Received(1).GetStreamFor(Path.GetFullPath($"/some/path/{format}_name.{format}")); | ||
} | ||
|
||
[Theory] | ||
[InlineData("")] | ||
[InlineData(null)] | ||
public void CheckOutputPath_FormatTemplate_trimmed(string format) | ||
{ | ||
this._writer.Write("/some/path", "{extension}_name.{extension}", "doc", format, _testContextWriter); | ||
|
||
this._streamProvider.Received(1).GetStreamFor(Path.GetFullPath("/some/path/name.")); | ||
} | ||
|
||
[Theory] | ||
[InlineData(null)] | ||
[InlineData("")] | ||
[InlineData("asyncApi")] | ||
[InlineData("service-1")] | ||
[InlineData("service 1")] | ||
public void CheckOutputPath_DocumentNameTemplate(string documentName) | ||
{ | ||
this._writer.Write("/some/path", "{document}.something", documentName, "", _testContextWriter); | ||
|
||
this._streamProvider.Received(1).GetStreamFor(Path.GetFullPath($"/some/path/{documentName}.something")); | ||
} | ||
} |
50 changes: 50 additions & 0 deletions
50
test/AsyncAPI.Saunter.Generator.Cli.Tests/ToFile/StreamProviderTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
using AsyncAPI.Saunter.Generator.Cli.ToFile; | ||
using Microsoft.Extensions.Logging; | ||
using NSubstitute; | ||
using NSubstitute.Community.Logging; | ||
using Shouldly; | ||
|
||
namespace AsyncAPI.Saunter.Generator.Cli.Tests.ToFile; | ||
|
||
public class StreamProviderTests | ||
{ | ||
private readonly IStreamProvider _streamProvider; | ||
private readonly ILogger<StreamProvider> _logger; | ||
|
||
public StreamProviderTests() | ||
{ | ||
this._logger = Substitute.For<ILogger<StreamProvider>>(); | ||
this._streamProvider = new StreamProvider(this._logger); | ||
} | ||
|
||
[Fact] | ||
public void NullPathIsStdOut() | ||
{ | ||
using var stream = this._streamProvider.GetStreamFor(null); | ||
|
||
stream.ShouldNotBeNull(); | ||
Assert.False(stream is FileStream); | ||
this._logger.Received(1).CallToLog(LogLevel.Debug); | ||
} | ||
|
||
[Fact] | ||
public void StringPathIsFileStream() | ||
{ | ||
var path = Path.GetFullPath("./test"); | ||
File.Delete(path); | ||
try | ||
{ | ||
using var stream = this._streamProvider.GetStreamFor(path); | ||
|
||
stream.ShouldNotBeNull(); | ||
Assert.True(stream is FileStream); | ||
File.Exists(path); | ||
} | ||
finally | ||
{ | ||
File.Delete(path); | ||
} | ||
|
||
this._logger.Received(1).CallToLog(LogLevel.Debug); | ||
} | ||
} |
205 changes: 205 additions & 0 deletions
205
test/AsyncAPI.Saunter.Generator.Cli.Tests/ToFile/ToFileCommandTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
using AsyncAPI.Saunter.Generator.Cli.ToFile; | ||
using LEGO.AsyncAPI.Models; | ||
using Microsoft.Extensions.Logging; | ||
using NSubstitute; | ||
using Shouldly; | ||
using Xunit.Abstractions; | ||
|
||
namespace AsyncAPI.Saunter.Generator.Cli.Tests.ToFile; | ||
|
||
public class ToFileCommandTests | ||
{ | ||
private readonly ToFileCommand _command; | ||
private readonly IEnvironmentBuilder _environment; | ||
private readonly IServiceProviderBuilder _builder; | ||
private readonly IAsyncApiDocumentExtractor _docExtractor; | ||
private readonly IFileWriter _fileWriter; | ||
private readonly ILogger<ToFileCommand> _logger; | ||
private readonly ITestOutputHelper _output; | ||
|
||
public ToFileCommandTests(ITestOutputHelper output) | ||
{ | ||
this._output = output; | ||
this._logger = Substitute.For<ILogger<ToFileCommand>>(); | ||
this._environment = Substitute.For<IEnvironmentBuilder>(); | ||
this._builder = Substitute.For<IServiceProviderBuilder>(); | ||
this._docExtractor = Substitute.For<IAsyncApiDocumentExtractor>(); | ||
this._fileWriter = Substitute.For<IFileWriter>(); | ||
this._command = new ToFileCommand(this._logger, _environment, _builder, _docExtractor, _fileWriter); | ||
} | ||
|
||
[Fact] | ||
public void StartupAssembly_FileNotFoundException() | ||
{ | ||
Assert.Throws<FileNotFoundException>(() => this._command.ToFile("")); | ||
} | ||
|
||
[Fact] | ||
public void SetEnvironmentVariables() | ||
{ | ||
var me = typeof(ToFileCommandTests).Assembly.Location; | ||
|
||
this._command.ToFile(me, env: "env=value"); | ||
|
||
this._environment.Received(1).SetEnvironmentVariables("env=value"); | ||
} | ||
|
||
[Fact] | ||
public void BuildServiceProvider() | ||
{ | ||
var me = typeof(ToFileCommandTests).Assembly.Location; | ||
this._output.WriteLine($"Assembly: {me}"); | ||
|
||
this._command.ToFile(me); | ||
|
||
this._builder.Received(1).BuildServiceProvider(me); | ||
} | ||
|
||
[Fact] | ||
public void GetAsyncApiDocument_DefaultDocParam() | ||
{ | ||
var me = typeof(ToFileCommandTests).Assembly.Location; | ||
this._output.WriteLine($"Assembly: {me}"); | ||
var sp = Substitute.For<IServiceProvider>(); | ||
this._builder.BuildServiceProvider(default).ReturnsForAnyArgs(sp); | ||
|
||
this._command.ToFile(me); | ||
|
||
this._docExtractor.Received(1).GetAsyncApiDocument(sp, null); | ||
} | ||
|
||
[Fact] | ||
public void GetAsyncApiDocument_DocParam() | ||
{ | ||
var me = typeof(ToFileCommandTests).Assembly.Location; | ||
this._output.WriteLine($"Assembly: {me}"); | ||
var sp = Substitute.For<IServiceProvider>(); | ||
this._builder.BuildServiceProvider(default).ReturnsForAnyArgs(sp); | ||
|
||
this._command.ToFile(me, doc: "a"); | ||
|
||
this._docExtractor.Received(1).GetAsyncApiDocument(sp, Arg.Is<string[]>(x => x.SequenceEqual(new[] { "a" }))); ; | ||
} | ||
|
||
[Fact] | ||
public void GetAsyncApiDocument_DocParamMultiple() | ||
{ | ||
var me = typeof(ToFileCommandTests).Assembly.Location; | ||
this._output.WriteLine($"Assembly: {me}"); | ||
var sp = Substitute.For<IServiceProvider>(); | ||
this._builder.BuildServiceProvider(default).ReturnsForAnyArgs(sp); | ||
|
||
this._command.ToFile(me, doc: "a,b, c ,,"); | ||
|
||
this._docExtractor.Received(1).GetAsyncApiDocument(sp, Arg.Is<string[]>(x => x.SequenceEqual(new[] { "a", "b", " c " }))); | ||
} | ||
|
||
[Fact] | ||
public void WriteFile_DefaultParams() | ||
{ | ||
var me = typeof(ToFileCommandTests).Assembly.Location; | ||
this._output.WriteLine($"Assembly: {me}"); | ||
this._docExtractor.GetAsyncApiDocument(default, default).ReturnsForAnyArgs([(null, new AsyncApiDocument { Info = new AsyncApiInfo { Title = "a" } })]); | ||
|
||
this._command.ToFile(me); | ||
|
||
this._fileWriter.ReceivedCalls().Count().ShouldBe(1); | ||
this._fileWriter.Received(1).Write(Path.GetFullPath("./"), "{document}_asyncapi.{extension}", null, "json", Arg.Any<Action<Stream>>()); | ||
} | ||
|
||
[Theory] | ||
[InlineData("json")] | ||
[InlineData("yml")] | ||
[InlineData("yaml")] | ||
public void WriteFile_FormatParam(string format) | ||
{ | ||
var me = typeof(ToFileCommandTests).Assembly.Location; | ||
this._output.WriteLine($"Assembly: {me}"); | ||
this._docExtractor.GetAsyncApiDocument(default, default).ReturnsForAnyArgs([(null, new AsyncApiDocument { Info = new AsyncApiInfo { Title = "a" } })]); | ||
|
||
this._command.ToFile(me, format: format); | ||
|
||
this._fileWriter.ReceivedCalls().Count().ShouldBe(1); | ||
this._fileWriter.Received(1).Write(Path.GetFullPath("./"), "{document}_asyncapi.{extension}", null, format, Arg.Any<Action<Stream>>()); | ||
} | ||
|
||
[Theory] | ||
[InlineData("")] | ||
[InlineData(" ")] | ||
[InlineData(null)] | ||
public void WriteFile_EmptyFormatParamVariants_FallbackToJson(string format) | ||
{ | ||
var me = typeof(ToFileCommandTests).Assembly.Location; | ||
this._output.WriteLine($"Assembly: {me}"); | ||
this._docExtractor.GetAsyncApiDocument(default, default).ReturnsForAnyArgs([(null, new AsyncApiDocument { Info = new AsyncApiInfo { Title = "a" } })]); | ||
|
||
this._command.ToFile(me, format: format); | ||
|
||
this._fileWriter.ReceivedCalls().Count().ShouldBe(1); | ||
this._fileWriter.Received(1).Write(Path.GetFullPath("./"), "{document}_asyncapi.{extension}", null, "json", Arg.Any<Action<Stream>>()); | ||
} | ||
|
||
[Theory] | ||
[InlineData("a")] | ||
[InlineData("json1")] | ||
[InlineData(".json")] | ||
public void WriteFile_InvalidFormatParam_FallbackToJson(string format) | ||
{ | ||
var me = typeof(ToFileCommandTests).Assembly.Location; | ||
this._output.WriteLine($"Assembly: {me}"); | ||
this._docExtractor.GetAsyncApiDocument(default, default).ReturnsForAnyArgs([(null, new AsyncApiDocument { Info = new AsyncApiInfo { Title = "a" } })]); | ||
|
||
this._command.ToFile(me, format: format); | ||
|
||
this._fileWriter.ReceivedCalls().Count().ShouldBe(0); | ||
} | ||
|
||
[Fact] | ||
public void WriteFile_FormatParamMultiple() | ||
{ | ||
var me = typeof(ToFileCommandTests).Assembly.Location; | ||
this._output.WriteLine($"Assembly: {me}"); | ||
this._docExtractor.GetAsyncApiDocument(default, default).ReturnsForAnyArgs([(null, new AsyncApiDocument { Info = new AsyncApiInfo { Title = "a" } })]); | ||
|
||
this._command.ToFile(me, format: " json , yaml,yml ,,a, "); | ||
|
||
this._fileWriter.ReceivedCalls().Count().ShouldBe(3); | ||
this._fileWriter.Received(1).Write(Path.GetFullPath("./"), "{document}_asyncapi.{extension}", null, "json", Arg.Any<Action<Stream>>()); | ||
this._fileWriter.Received(1).Write(Path.GetFullPath("./"), "{document}_asyncapi.{extension}", null, "yml", Arg.Any<Action<Stream>>()); | ||
this._fileWriter.Received(1).Write(Path.GetFullPath("./"), "{document}_asyncapi.{extension}", null, "yaml", Arg.Any<Action<Stream>>()); | ||
} | ||
|
||
[Theory] | ||
[InlineData("doc")] | ||
[InlineData("{document}")] | ||
[InlineData("{extension}")] | ||
[InlineData("{document}.{extension}")] | ||
public void WriteFile_FileTemplateParam(string template) | ||
{ | ||
var me = typeof(ToFileCommandTests).Assembly.Location; | ||
this._output.WriteLine($"Assembly: {me}"); | ||
this._docExtractor.GetAsyncApiDocument(default, default).ReturnsForAnyArgs([(null, new AsyncApiDocument { Info = new AsyncApiInfo { Title = "a" } })]); | ||
|
||
this._command.ToFile(me, filename: template); | ||
|
||
this._fileWriter.ReceivedCalls().Count().ShouldBe(1); | ||
this._fileWriter.Received(1).Write(Path.GetFullPath("./"), template, null, "json", Arg.Any<Action<Stream>>()); | ||
} | ||
|
||
[Theory] | ||
[InlineData("./")] | ||
[InlineData("/")] | ||
[InlineData("a/")] | ||
[InlineData("/a/b")] | ||
public void WriteFile_OutputPathParam(string path) | ||
{ | ||
var me = typeof(ToFileCommandTests).Assembly.Location; | ||
this._output.WriteLine($"Assembly: {me}"); | ||
this._docExtractor.GetAsyncApiDocument(default, default).ReturnsForAnyArgs([(null, new AsyncApiDocument { Info = new AsyncApiInfo { Title = "a" } })]); | ||
|
||
this._command.ToFile(me, output: path); | ||
|
||
this._fileWriter.ReceivedCalls().Count().ShouldBe(1); | ||
this._fileWriter.Received(1).Write(Path.GetFullPath(path), "{document}_asyncapi.{extension}", null, "json", Arg.Any<Action<Stream>>()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
210 changes: 103 additions & 107 deletions
210
test/Saunter.Tests/Generation/DocumentGeneratorTests/InterfaceAttributeTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,107 +1,103 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// 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; | ||
using System.Reflection; | ||
using Saunter.AsyncApiSchema.v2; | ||
using Saunter.Attributes; | ||
using Saunter.Generation; | ||
using Shouldly; | ||
using Xunit; | ||
using System.Linq; | ||
|
||
namespace Saunter.Tests.Generation.DocumentGeneratorTests | ||
{ | ||
public class InterfaceAttributeTests | ||
{ | ||
[Theory] | ||
[InlineData(typeof(IServiceEvents))] | ||
[InlineData(typeof(ServiceEventsFromInterface))] | ||
[InlineData(typeof(ServiceEventsFromAnnotatedInterface))] // Check that annotations are not inherited from the interface | ||
public void NonAnnotatedTypesTest(Type type) | ||
{ | ||
// Arrange | ||
var options = new AsyncApiOptions(); | ||
var documentGenerator = new DocumentGenerator(); | ||
|
||
// Act | ||
var document = documentGenerator.GenerateDocument(new[] { type.GetTypeInfo() }, options, options.AsyncApi, ActivatorServiceProvider.Instance); | ||
|
||
// Assert | ||
document.ShouldNotBeNull(); | ||
document.Channels.Count.ShouldBe(0); | ||
} | ||
|
||
[Theory] | ||
[InlineData(typeof(IAnnotatedServiceEvents), "interface")] | ||
[InlineData(typeof(AnnotatedServiceEventsFromInterface), "class")] | ||
[InlineData(typeof(AnnotatedServiceEventsFromAnnotatedInterface), "class")] // Check that the actual type's annotation takes precedence of the inherited interface | ||
public void AnnotatedTypesTest(Type type, string source) | ||
{ | ||
// Arrange | ||
var options = new AsyncApiOptions(); | ||
var documentGenerator = new DocumentGenerator(); | ||
|
||
// Act | ||
var document = documentGenerator.GenerateDocument(new[] { type.GetTypeInfo() }, options, options.AsyncApi, ActivatorServiceProvider.Instance); | ||
|
||
// Assert | ||
document.ShouldNotBeNull(); | ||
document.Channels.Count.ShouldBe(1); | ||
|
||
var channel = document.Channels.First(); | ||
channel.Key.ShouldBe($"{source}.event"); | ||
channel.Value.Description.ShouldBeNull(); | ||
|
||
var publish = channel.Value.Publish; | ||
publish.ShouldNotBeNull(); | ||
publish.OperationId.ShouldBe("PublishEvent"); | ||
publish.Description.ShouldBe($"({source}) Subscribe to domains events about a tenant."); | ||
|
||
var messageRef = publish.Message.ShouldBeOfType<MessageReference>(); | ||
messageRef.Id.ShouldBe("tenantEvent"); | ||
} | ||
|
||
[AsyncApi] | ||
private interface IAnnotatedServiceEvents | ||
{ | ||
[Channel("interface.event")] | ||
[PublishOperation(typeof(TenantEvent), Description = "(interface) Subscribe to domains events about a tenant.")] | ||
void PublishEvent(TenantEvent evt); | ||
} | ||
|
||
private interface IServiceEvents | ||
{ | ||
void PublishEvent(TenantEvent evt); | ||
} | ||
|
||
private class ServiceEventsFromInterface : IServiceEvents | ||
{ | ||
public void PublishEvent(TenantEvent evt) { } | ||
} | ||
|
||
private class ServiceEventsFromAnnotatedInterface : IAnnotatedServiceEvents | ||
{ | ||
public void PublishEvent(TenantEvent evt) { } | ||
} | ||
|
||
[AsyncApi] | ||
private class AnnotatedServiceEventsFromInterface : IAnnotatedServiceEvents | ||
{ | ||
[Channel("class.event")] | ||
[PublishOperation(typeof(TenantEvent), Description = "(class) Subscribe to domains events about a tenant.")] | ||
public void PublishEvent(TenantEvent evt) { } | ||
} | ||
|
||
[AsyncApi] | ||
private class AnnotatedServiceEventsFromAnnotatedInterface : IAnnotatedServiceEvents | ||
{ | ||
[Channel("class.event")] | ||
[PublishOperation(typeof(TenantEvent), Description = "(class) Subscribe to domains events about a tenant.")] | ||
public void PublishEvent(TenantEvent evt) { } | ||
} | ||
|
||
private class TenantEvent { } | ||
} | ||
} | ||
using System; | ||
using System.Linq; | ||
using System.Reflection; | ||
using Saunter.AsyncApiSchema.v2; | ||
using Saunter.Attributes; | ||
using Saunter.Generation; | ||
using Shouldly; | ||
using Xunit; | ||
|
||
namespace Saunter.Tests.Generation.DocumentGeneratorTests | ||
{ | ||
public class InterfaceAttributeTests | ||
{ | ||
[Theory] | ||
[InlineData(typeof(IServiceEvents))] | ||
[InlineData(typeof(ServiceEventsFromInterface))] | ||
[InlineData(typeof(ServiceEventsFromAnnotatedInterface))] // Check that annotations are not inherited from the interface | ||
public void NonAnnotatedTypesTest(Type type) | ||
{ | ||
// Arrange | ||
var options = new AsyncApiOptions(); | ||
var documentGenerator = new DocumentGenerator(); | ||
|
||
// Act | ||
var document = documentGenerator.GenerateDocument(new[] { type.GetTypeInfo() }, options, options.AsyncApi, ActivatorServiceProvider.Instance); | ||
|
||
// Assert | ||
document.ShouldNotBeNull(); | ||
document.Channels.Count.ShouldBe(0); | ||
} | ||
|
||
[Theory] | ||
[InlineData(typeof(IAnnotatedServiceEvents), "interface")] | ||
[InlineData(typeof(AnnotatedServiceEventsFromInterface), "class")] | ||
[InlineData(typeof(AnnotatedServiceEventsFromAnnotatedInterface), "class")] // Check that the actual type's annotation takes precedence of the inherited interface | ||
public void AnnotatedTypesTest(Type type, string source) | ||
{ | ||
// Arrange | ||
var options = new AsyncApiOptions(); | ||
var documentGenerator = new DocumentGenerator(); | ||
|
||
// Act | ||
var document = documentGenerator.GenerateDocument(new[] { type.GetTypeInfo() }, options, options.AsyncApi, ActivatorServiceProvider.Instance); | ||
|
||
// Assert | ||
document.ShouldNotBeNull(); | ||
document.Channels.Count.ShouldBe(1); | ||
|
||
var channel = document.Channels.First(); | ||
channel.Key.ShouldBe($"{source}.event"); | ||
channel.Value.Description.ShouldBeNull(); | ||
|
||
var publish = channel.Value.Publish; | ||
publish.ShouldNotBeNull(); | ||
publish.OperationId.ShouldBe("PublishEvent"); | ||
publish.Description.ShouldBe($"({source}) Subscribe to domains events about a tenant."); | ||
|
||
var messageRef = publish.Message.ShouldBeOfType<MessageReference>(); | ||
messageRef.Id.ShouldBe("tenantEvent"); | ||
} | ||
|
||
[AsyncApi] | ||
private interface IAnnotatedServiceEvents | ||
{ | ||
[Channel("interface.event")] | ||
[PublishOperation(typeof(TenantEvent), Description = "(interface) Subscribe to domains events about a tenant.")] | ||
void PublishEvent(TenantEvent evt); | ||
} | ||
|
||
private interface IServiceEvents | ||
{ | ||
void PublishEvent(TenantEvent evt); | ||
} | ||
|
||
private class ServiceEventsFromInterface : IServiceEvents | ||
{ | ||
public void PublishEvent(TenantEvent evt) { } | ||
} | ||
|
||
private class ServiceEventsFromAnnotatedInterface : IAnnotatedServiceEvents | ||
{ | ||
public void PublishEvent(TenantEvent evt) { } | ||
} | ||
|
||
[AsyncApi] | ||
private class AnnotatedServiceEventsFromInterface : IAnnotatedServiceEvents | ||
{ | ||
[Channel("class.event")] | ||
[PublishOperation(typeof(TenantEvent), Description = "(class) Subscribe to domains events about a tenant.")] | ||
public void PublishEvent(TenantEvent evt) { } | ||
} | ||
|
||
[AsyncApi] | ||
private class AnnotatedServiceEventsFromAnnotatedInterface : IAnnotatedServiceEvents | ||
{ | ||
[Channel("class.event")] | ||
[PublishOperation(typeof(TenantEvent), Description = "(class) Subscribe to domains events about a tenant.")] | ||
public void PublishEvent(TenantEvent evt) { } | ||
} | ||
|
||
private class TenantEvent { } | ||
} | ||
} |
1 change: 0 additions & 1 deletion
1
test/Saunter.Tests/Generation/DocumentProviderTests/AsyncApiTypesTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed separate unittest step because it causes a lot duplicate work: