GFlisch / Arc4u.Guidance.Doc

Other
5 stars 1 forks source link

(Bug, guidance 2022.2.1.9, 04/11/2022) facade.nswag for Yarp or service generated incorrectly #83

Closed vvdb-architecture closed 1 year ago

vvdb-architecture commented 1 year ago

When generating a new solution X, the facade.nswag file in the X.[Yarp|Service].Facade.Sdk project contains the following:

{
  ...
  "documentGenerator": {
    "aspNetCoreToOpenApi": {
      ...
      "workingDirectory": "../../../Solution2.Yarp.Host/Configs",

This causes the execution of [Yarp|Service] (launched by nswag.exe) to fail with the error:

1>[14:51:08 FTL] Unhandled exception
1>System.Collections.Generic.KeyNotFoundException: The given key 'ida_MetadataAddress' was not present in the dictionary.
1>   at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
1>   at Program.<Main>$(String[] args) in C:\PRJ\AuthorizationViewer\BE\Yarp\AuthorizationViewer.Yarp.Host\Program.cs:line 107
1>[14:51:08 INF] Shut down complete
1>System.InvalidOperationException: NSwag requires the assembly AuthorizationViewer.Yarp.Host, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null to have either an BuildWebHost or CreateWebHostBuilder/CreateHostBuilder method. See https://docs.microsoft.com/en-us/aspnet/core/fundamentals/hosting?tabs=aspnetcore2x for suggestions on ways to refactor your startup type.
1>   at NSwag.Commands.ServiceProviderResolver.GetServiceProvider(Assembly assembly) in /_/src/NSwag.Commands/HostApplication.cs:line 87
1>   at NSwag.Commands.Generation.OpenApiGeneratorCommandBase`1.GetServiceProvider(AssemblyLoader assemblyLoader) in /_/src/NSwag.Commands/Commands/Generation/OpenApiGeneratorCommandBase.cs:line 313
1>   at NSwag.Commands.Generation.AspNetCore.AspNetCoreToSwaggerCommand.RunIsolatedAsync(AssemblyLoader assemblyLoader) in /_/src/NSwag.Commands/Commands/Generation/AspNetCore/AspNetCoreToOpenApiCommand.cs:line 323
1>   at NSwag.Commands.IsolatedCommandBase`1.IsolatedCommandAssemblyLoader.Run(String commandType, String commandData, String[] assemblyPaths, String[] referencePaths) in /_/src/NSwag.Commands/Commands/IsolatedCommandBase.cs:line 76
1>   at NSwag.Commands.IsolatedCommandBase`1.RunIsolatedAsync(String configurationFile) in /_/src/NSwag.Commands/Commands/IsolatedCommandBase.cs:line 61
1>   at NSwag.Commands.IsolatedSwaggerOutputCommandBase`1.RunAsync(CommandLineProcessor processor, IConsoleHost host) in /_/src/NSwag.Commands/Commands/IsolatedSwaggerOutputCommandBase.cs:line 51
1>   at NSwag.Commands.Generation.AspNetCore.AspNetCoreToSwaggerCommand.RunAsync(CommandLineProcessor processor, IConsoleHost host) in /_/src/NSwag.Commands/Commands/Generation/AspNetCore/AspNetCoreToOpenApiCommand.cs:line 98
1>   at NSwag.Commands.NSwagDocumentBase.GenerateSwaggerDocumentAsync() in /_/src/NSwag.Commands/NSwagDocumentBase.cs:line 275
1>   at NSwag.Commands.NSwagDocument.ExecuteAsync() in /_/src/NSwag.Commands/NSwagDocument.cs:line 81
1>   at NSwag.Commands.Document.ExecuteDocumentCommand.ExecuteDocumentAsync(IConsoleHost host, String filePath) in /_/src/NSwag.Commands/Commands/Document/ExecuteDocumentCommand.cs:line 85
1>   at NSwag.Commands.Document.ExecuteDocumentCommand.RunAsync(CommandLineProcessor processor, IConsoleHost host) in /_/src/NSwag.Commands/Commands/Document/ExecuteDocumentCommand.cs:line 32
1>   at NConsole.CommandLineProcessor.ProcessSingleAsync(String[] args, Object input)
1>   at NConsole.CommandLineProcessor.ProcessAsync(String[] args, Object input)
1>   at NSwag.Commands.NSwagCommandProcessor.ProcessAsync(String[] args) in /_/src/NSwag.Commands/NSwagCommandProcessor.cs:line 61

.. because the configuration files are not found. The correct entry must be:

  "documentGenerator": {
    "aspNetCoreToOpenApi": {
      ...
      "workingDirectory": "../../../Solution2.Yarp.Host",

This was OK before. The bug is recent as of this time of writing.

fcc753 commented 1 year ago

I noticed that too.

GFlisch commented 1 year ago

Hi @vvdb-architecture , are on running the guidance on the acc environment or production? I am indeed changing this. When adding a database EF Core project if I didn't add the /Configs the msbuild task failed.

Just to know. I will test also the remark of @fcc753.

GFlisch commented 1 year ago

The NSwagStudio mention this

image

And the config folder are under Configs. But indeed it fails which was not the case when I tested => but the class was already generated...

vvdb-architecture commented 1 year ago

I'm running the production guidance. @fcc753 's remark suggestion would avoid such troubles indeed. Suggestions can also be found at https://github.com/RicoSuter/NSwag/wiki/NSwag.MSBuild

GFlisch commented 1 year ago

Prod environment issue fixed. I don't close to implement in acc first the msbuild on the facade and interface

fcc753 commented 1 year ago

But working on just the facade/interface one by one seems this has become difficult to do... There's a long thread of issues on https://github.com/RicoSuter/NSwag/issues/3794 related to the error messages people started to receive with Net6 a year ago.

Running this with just the facade triggers: NSwag requires the assembly LoadManagement.Yarp.Facade, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null to have either an BuildWebHost or CreateWebHostBuilder/CreateHostBuilder method.

It seems the app has to be really running for NSWAg to do its job now...

GFlisch commented 1 year ago

The issue is when we add Hangfire. The error is located at the level of Hangfire. The current work around is to create another environment file with a hangfire connection string connecting to a localhost database for example and setting the new Hangfire environment in the nswag file => When the build task will run, Hangfire will silently skip itself and code will be generated correctly.

vvdb-architecture commented 1 year ago

Here is another workaround, based on the need to detect when the Host is being run by Swagger, while keeping the ASPNETCORE_ENVIRONMENT as-is.

The current invocation in the host's .csproj file is like this:

    <Target Name="NSwagFacade" AfterTargets="PostBuildEvent" Condition=" '$(Configuration)' == 'Debug' ">
        <Exec WorkingDirectory="$(ProjectDir)" EnvironmentVariables="ASPNETCORE_ENVIRONMENT=Localhost" Command="$(NSwagExe_Net60) run ..\Sdks\HappyFlow.Service.Facade.Sdk\Generator\facade.nswag  /variables:Configuration=$(Configuration)" />
    </Target>

We could change this so that we add another environment variable called NSwag and set it to true like this:

    <Target Name="NSwagFacade" AfterTargets="PostBuildEvent" Condition=" '$(Configuration)' == 'Debug' ">
        <Exec WorkingDirectory="$(ProjectDir)" EnvironmentVariables="ASPNETCORE_ENVIRONMENT=Localhost;NSwag=true" Command="$(NSwagExe_Net60) run ..\Sdks\HappyFlow.Service.Facade.Sdk\Generator\facade.nswag  /variables:Configuration=$(Configuration)" />
    </Target>

Then, in the Host startup code (or elsewhere), we can define the following method:

static bool IsInvokedByNSwag()
{
    var env = System.Environment.GetEnvironmentVariable("NSwag");
    return env != null && bool.TryParse(env, out var nswag) && nswag;
}

In our Host startup code, we can simply check if we are being started by NSwag and omit any declaration which may cause the run to fail:

    if (!IsInvokedByNSwag())
    {
        // add HangFire or other stuff that could fail when NSwag is calling us
    }

Since environment variables become part of the configuration by default, we could also write Configuration.GetValue<bool>("NSwag"), but this requires access to an IConfiguration, which is not always the case. It is cleaner to encapsulate the test in its own method.

vvdb-architecture commented 1 year ago

I think we can close this, since 2022.2.1.12-rc3 implemented NSwag=true with associated tests