From 75f052f1ea257bf32b2dff9ecc8c51132e199cd1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 25 Nov 2025 14:57:59 +0000 Subject: [PATCH 01/12] Initial plan From e490120d49fe1cd077f7cd282e0e7ab340fca86e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 25 Nov 2025 15:17:51 +0000 Subject: [PATCH 02/12] Changes before error encountered Co-authored-by: christianhelle <710400+christianhelle@users.noreply.github.com> --- .../NSwag/NSwagCSharpCodeGenerator.cs | 116 +++++++++++++----- .../Installer/DependencyInstaller.cs | 49 +++++++- .../Options/NSwag/CSharpClassStyle.cs | 28 +++++ .../Options/NSwag/DefaultNSwagOptions.cs | 2 - .../Options/NSwag/INSwagOption.cs | 2 - 5 files changed, 163 insertions(+), 34 deletions(-) create mode 100644 src/Core/ApiClientCodeGen.Core/Options/NSwag/CSharpClassStyle.cs diff --git a/src/Core/ApiClientCodeGen.Core/Generators/NSwag/NSwagCSharpCodeGenerator.cs b/src/Core/ApiClientCodeGen.Core/Generators/NSwag/NSwagCSharpCodeGenerator.cs index 5b42c235ab..b8ebe3b5a5 100644 --- a/src/Core/ApiClientCodeGen.Core/Generators/NSwag/NSwagCSharpCodeGenerator.cs +++ b/src/Core/ApiClientCodeGen.Core/Generators/NSwag/NSwagCSharpCodeGenerator.cs @@ -1,52 +1,110 @@ -using System; -using System.Diagnostics.CodeAnalysis; +using System; +using System.IO; +using Rapicgen.Core.Installer; using Rapicgen.Core.Logging; -using NSwag.CodeGeneration.CSharp; +using Rapicgen.Core.Options.NSwag; namespace Rapicgen.Core.Generators.NSwag { public class NSwagCSharpCodeGenerator : ICodeGenerator { - private readonly IOpenApiDocumentFactory documentFactory; - private readonly INSwagCodeGeneratorSettingsFactory generatorSettingsFactory; private readonly string swaggerFile; + private readonly string defaultNamespace; + private readonly IProcessLauncher processLauncher; + private readonly IDependencyInstaller dependencyInstaller; + private readonly INSwagOptions options; + + private const string Command = "nswag"; public NSwagCSharpCodeGenerator( string swaggerFile, - IOpenApiDocumentFactory documentFactory, - INSwagCodeGeneratorSettingsFactory generatorSettingsFactory) + string defaultNamespace, + IProcessLauncher processLauncher, + IDependencyInstaller dependencyInstaller, + INSwagOptions options) { - this.swaggerFile = swaggerFile; - this.documentFactory = documentFactory ?? throw new ArgumentNullException(nameof(documentFactory)); - this.generatorSettingsFactory = generatorSettingsFactory ?? - throw new ArgumentNullException(nameof(generatorSettingsFactory)); + this.swaggerFile = swaggerFile ?? throw new ArgumentNullException(nameof(swaggerFile)); + this.defaultNamespace = defaultNamespace ?? throw new ArgumentNullException(nameof(defaultNamespace)); + this.processLauncher = processLauncher ?? throw new ArgumentNullException(nameof(processLauncher)); + this.dependencyInstaller = dependencyInstaller ?? throw new ArgumentNullException(nameof(dependencyInstaller)); + this.options = options ?? throw new ArgumentNullException(nameof(options)); } public string GenerateCode(IProgressReporter? pGenerateProgress) { - try - { - using var context = new DependencyContext("NSwag"); - var code = OnGenerateCode(pGenerateProgress); - context.Succeeded(); - return GeneratedCode.PrefixAutogeneratedCodeHeader(code, "NSwag", "v14.4.0"); - } - finally + pGenerateProgress?.Progress(10); + dependencyInstaller.InstallNSwag(); + + pGenerateProgress?.Progress(30); + var outputPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".cs"); + var workingDirectory = Path.GetDirectoryName(swaggerFile); + var className = GenerateClassName(swaggerFile); + + pGenerateProgress?.Progress(40); + var arguments = BuildNSwagArguments(outputPath, className); + + using var context = new DependencyContext("NSwag", $"{Command} {arguments}"); + processLauncher.Start(Command, arguments, workingDirectory); + context.Succeeded(); + + pGenerateProgress?.Progress(80); + + string generatedCode = string.Empty; + if (File.Exists(outputPath)) { - pGenerateProgress?.Progress(90); + generatedCode = File.ReadAllText(outputPath); + File.Delete(outputPath); } + + pGenerateProgress?.Progress(100); + return GeneratedCode.PrefixAutogeneratedCodeHeader(generatedCode, "NSwag", "v14.4.0"); } - [SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", Justification = "This is code is called from an old pre-TPL interface")] - private string OnGenerateCode(IProgressReporter? pGenerateProgress) + private string BuildNSwagArguments(string outputPath, string className) { - pGenerateProgress?.Progress(10); - var document = documentFactory.GetDocumentAsync(swaggerFile).GetAwaiter().GetResult(); - pGenerateProgress?.Progress(20); - var settings = generatorSettingsFactory.GetGeneratorSettings(document); - pGenerateProgress?.Progress(50); - var generator = new CSharpClientGenerator(document, settings); - return generator.GenerateFile(); + var classStyle = options.ClassStyle.ToString(); + + var args = $"openapi2csclient " + + $"/input:\"{swaggerFile}\" " + + $"/output:\"{outputPath}\" " + + $"/namespace:{defaultNamespace} " + + $"/ClassName:{className} " + + $"/InjectHttpClient:{options.InjectHttpClient.ToString().ToLowerInvariant()} " + + $"/GenerateClientInterfaces:{options.GenerateClientInterfaces.ToString().ToLowerInvariant()} " + + $"/GenerateDtoTypes:{options.GenerateDtoTypes.ToString().ToLowerInvariant()} " + + $"/UseBaseUrl:{options.UseBaseUrl.ToString().ToLowerInvariant()} " + + $"/ClassStyle:{classStyle} " + + $"/ParameterDateTimeFormat:\"{options.ParameterDateTimeFormat}\""; + + return args; + } + + private string GenerateClassName(string filePath) + { + var fileInfo = new FileInfo(filePath); + if (options.UseDocumentTitle) + { + // When using document title, we let NSwag determine the class name + // But we need to provide a default - we'll use the filename as fallback + var name = fileInfo.Name + .Replace(".json", string.Empty) + .Replace(".yaml", string.Empty) + .Replace(".yml", string.Empty) + .Replace(".", string.Empty) + .Replace("-", string.Empty) + .Replace(" ", string.Empty); + return string.IsNullOrWhiteSpace(name) ? "ApiClient" : $"{name}Client"; + } + + var fileName = fileInfo.Name + .Replace(".json", string.Empty) + .Replace(".yaml", string.Empty) + .Replace(".yml", string.Empty) + .Replace(".", string.Empty) + .Replace("-", string.Empty) + .Replace(" ", string.Empty); + + return string.IsNullOrWhiteSpace(fileName) ? "ApiClient" : fileName; } } } \ No newline at end of file diff --git a/src/Core/ApiClientCodeGen.Core/Installer/DependencyInstaller.cs b/src/Core/ApiClientCodeGen.Core/Installer/DependencyInstaller.cs index 50d0a6d46d..139ec6d70c 100644 --- a/src/Core/ApiClientCodeGen.Core/Installer/DependencyInstaller.cs +++ b/src/Core/ApiClientCodeGen.Core/Installer/DependencyInstaller.cs @@ -31,7 +31,54 @@ public void InstallAutoRest() public void InstallNSwag() { - npm.InstallNpmPackage("nswag"); + var command = "nswag"; + string arguments = "version"; + string nswagVersion = ""; + try + { + processLauncher.Start(command, arguments, output => + { + if (output != null) + { + nswagVersion = output ?? nswagVersion; + Logger.Instance.WriteLine(output); + } + }, error => + { + if (error != null) + { + Logger.Instance.WriteLine(error); + } + }); + if (!nswagVersion.Contains("14.4.0")) + { + // Version mismatch, update to required version + UpdateNSwagTool(); + } + } + catch (Win32Exception) + { + // If command doesn't exist Win32Exception is thrown - install the tool + InstallNSwagTool(); + } + } + + private void InstallNSwagTool() + { + var command = PathProvider.GetDotNetPath(); + var arguments = "tool install --global NSwag.ConsoleCore --version 14.4.0"; + using var context = new DependencyContext(command, $"{command} {arguments}"); + processLauncher.Start(command, arguments); + context.Succeeded(); + } + + private void UpdateNSwagTool() + { + var command = PathProvider.GetDotNetPath(); + var arguments = "tool update --global NSwag.ConsoleCore --version 14.4.0"; + using var context = new DependencyContext(command, $"{command} {arguments}"); + processLauncher.Start(command, arguments); + context.Succeeded(); } public string InstallOpenApiGenerator(OpenApiSupportedVersion version = default) diff --git a/src/Core/ApiClientCodeGen.Core/Options/NSwag/CSharpClassStyle.cs b/src/Core/ApiClientCodeGen.Core/Options/NSwag/CSharpClassStyle.cs new file mode 100644 index 0000000000..20f10d7dcd --- /dev/null +++ b/src/Core/ApiClientCodeGen.Core/Options/NSwag/CSharpClassStyle.cs @@ -0,0 +1,28 @@ +namespace Rapicgen.Core.Options.NSwag +{ + /// + /// C# class style for generated code. + /// + public enum CSharpClassStyle + { + /// + /// Plain Old C# Objects (default) + /// + Poco, + + /// + /// Implements INotifyPropertyChanged + /// + Inpc, + + /// + /// Prism base class + /// + Prism, + + /// + /// C# 9.0 Records + /// + Record + } +} diff --git a/src/Core/ApiClientCodeGen.Core/Options/NSwag/DefaultNSwagOptions.cs b/src/Core/ApiClientCodeGen.Core/Options/NSwag/DefaultNSwagOptions.cs index f09a242074..7397dc6db3 100644 --- a/src/Core/ApiClientCodeGen.Core/Options/NSwag/DefaultNSwagOptions.cs +++ b/src/Core/ApiClientCodeGen.Core/Options/NSwag/DefaultNSwagOptions.cs @@ -1,5 +1,3 @@ -using NJsonSchema.CodeGeneration.CSharp; - namespace Rapicgen.Core.Options.NSwag { public class DefaultNSwagOptions : INSwagOptions diff --git a/src/Core/ApiClientCodeGen.Core/Options/NSwag/INSwagOption.cs b/src/Core/ApiClientCodeGen.Core/Options/NSwag/INSwagOption.cs index 53d8826607..030aefaecd 100644 --- a/src/Core/ApiClientCodeGen.Core/Options/NSwag/INSwagOption.cs +++ b/src/Core/ApiClientCodeGen.Core/Options/NSwag/INSwagOption.cs @@ -1,5 +1,3 @@ -using NJsonSchema.CodeGeneration.CSharp; - namespace Rapicgen.Core.Options.NSwag { public interface INSwagOptions From 53acaca331151d48e9555ab43ccbdb9032f0eca6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 25 Nov 2025 15:37:39 +0000 Subject: [PATCH 03/12] Update NSwag to use CLI instead of library - Core and CLI changes Co-authored-by: christianhelle <710400+christianhelle@users.noreply.github.com> --- .../Command/NSwagCodeGeneratorFactoryTests.cs | 8 +-- .../CSharp/NswagCodeGeneratoryFactory.cs | 24 ++++++--- .../Commands/CSharp/NswagCommand.cs | 9 +--- .../NSwag/NSwagCSharpCodeGeneratorTests.cs | 54 ++++++++++--------- .../Options/DefaultNSwagOptionsTests.cs | 1 - .../Options/DefaultNSwagStudioOptionsTests.cs | 4 +- .../NSwagCodeGeneratorSettingsFactory.cs | 13 ++++- .../NSwagStudio/NSwagStudioFileHelper.cs | 15 +++++- .../Fixtures/NSwagCodeGeneratorFixture.cs | 11 ++-- .../OpenApi3/NSwagCodeGeneratorFixture.cs | 11 ++-- .../Yaml/NSwagCodeGeneratorFixture.cs | 11 ++-- .../Yaml/NSwagCodeGeneratorFixture.cs | 11 ++-- .../Generators/CodeGeneratorFactory.cs | 8 +-- 13 files changed, 115 insertions(+), 65 deletions(-) diff --git a/src/CLI/ApiClientCodeGen.CLI.Tests/Command/NSwagCodeGeneratorFactoryTests.cs b/src/CLI/ApiClientCodeGen.CLI.Tests/Command/NSwagCodeGeneratorFactoryTests.cs index e7a2f63f56..d3d2404e03 100644 --- a/src/CLI/ApiClientCodeGen.CLI.Tests/Command/NSwagCodeGeneratorFactoryTests.cs +++ b/src/CLI/ApiClientCodeGen.CLI.Tests/Command/NSwagCodeGeneratorFactoryTests.cs @@ -1,6 +1,4 @@ using ApiClientCodeGen.Tests.Common.Infrastructure; -using Rapicgen.CLI.Commands; -using Rapicgen.Core.Generators.NSwag; using Rapicgen.Core.Options.NSwag; using FluentAssertions; using Rapicgen.CLI.Commands.CSharp; @@ -15,13 +13,11 @@ public void Create_Should_Return_NotNull( NSwagCodeGeneratorFactory sut, string swaggerFile, string defaultNamespace, - INSwagOptions options, - IOpenApiDocumentFactory documentFactory) + INSwagOptions options) => sut.Create( swaggerFile, defaultNamespace, - options, - documentFactory) + options) .Should() .NotBeNull(); } diff --git a/src/CLI/ApiClientCodeGen.CLI/Commands/CSharp/NswagCodeGeneratoryFactory.cs b/src/CLI/ApiClientCodeGen.CLI/Commands/CSharp/NswagCodeGeneratoryFactory.cs index 04fd319589..c71ab9a405 100644 --- a/src/CLI/ApiClientCodeGen.CLI/Commands/CSharp/NswagCodeGeneratoryFactory.cs +++ b/src/CLI/ApiClientCodeGen.CLI/Commands/CSharp/NswagCodeGeneratoryFactory.cs @@ -1,5 +1,6 @@ using Rapicgen.Core.Generators; using Rapicgen.Core.Generators.NSwag; +using Rapicgen.Core.Installer; using Rapicgen.Core.Options.NSwag; namespace Rapicgen.CLI.Commands.CSharp @@ -8,20 +9,31 @@ public interface INSwagCodeGeneratorFactory { ICodeGenerator Create(string swaggerFile, string defaultNamespace, - INSwagOptions options, - IOpenApiDocumentFactory documentFactory); + INSwagOptions options); } public class NSwagCodeGeneratorFactory : INSwagCodeGeneratorFactory { + private readonly IProcessLauncher processLauncher; + private readonly IDependencyInstaller dependencyInstaller; + + public NSwagCodeGeneratorFactory( + IProcessLauncher processLauncher, + IDependencyInstaller dependencyInstaller) + { + this.processLauncher = processLauncher; + this.dependencyInstaller = dependencyInstaller; + } + public ICodeGenerator Create( string swaggerFile, string defaultNamespace, - INSwagOptions options, - IOpenApiDocumentFactory documentFactory) + INSwagOptions options) => new NSwagCSharpCodeGenerator( swaggerFile, - documentFactory, - new NSwagCodeGeneratorSettingsFactory(defaultNamespace, options)); + defaultNamespace, + processLauncher, + dependencyInstaller, + options); } } \ No newline at end of file diff --git a/src/CLI/ApiClientCodeGen.CLI/Commands/CSharp/NswagCommand.cs b/src/CLI/ApiClientCodeGen.CLI/Commands/CSharp/NswagCommand.cs index 84ebfabb54..8d38e7a1a2 100644 --- a/src/CLI/ApiClientCodeGen.CLI/Commands/CSharp/NswagCommand.cs +++ b/src/CLI/ApiClientCodeGen.CLI/Commands/CSharp/NswagCommand.cs @@ -1,11 +1,11 @@ using System; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; -using NJsonSchema.CodeGeneration.CSharp; using Rapicgen.CLI.Commands; using Rapicgen.Core; using Rapicgen.Core.Generators; using Rapicgen.Core.Generators.NSwag; +using Rapicgen.Core.Installer; using Rapicgen.Core.Logging; using Rapicgen.Core.Options.NSwag; using Spectre.Console.Cli; @@ -59,18 +59,14 @@ Set this to FALSE to use the filename (default: TRUE)")] public class NSwagCommand : CodeGeneratorCommand { - private readonly IOpenApiDocumentFactory openApiDocumentFactory; private readonly INSwagCodeGeneratorFactory codeGeneratorFactory; public NSwagCommand( IConsoleOutput console, IProgressReporter? progressReporter, - IOpenApiDocumentFactory openApiDocumentFactory, INSwagCodeGeneratorFactory codeGeneratorFactory) : base(console, progressReporter) { - this.openApiDocumentFactory = openApiDocumentFactory ?? - throw new ArgumentNullException(nameof(openApiDocumentFactory)); this.codeGeneratorFactory = codeGeneratorFactory ?? throw new ArgumentNullException(nameof(codeGeneratorFactory)); } @@ -79,7 +75,6 @@ public override ICodeGenerator CreateGenerator(NSwagCommandSettings settings) => codeGeneratorFactory.Create( settings.SwaggerFile, settings.DefaultNamespace, - settings, - openApiDocumentFactory); + settings); } } \ No newline at end of file diff --git a/src/Core/ApiClientCodeGen.Core.Tests/Generators/NSwag/NSwagCSharpCodeGeneratorTests.cs b/src/Core/ApiClientCodeGen.Core.Tests/Generators/NSwag/NSwagCSharpCodeGeneratorTests.cs index 7cfb4cbfcd..49484a4645 100644 --- a/src/Core/ApiClientCodeGen.Core.Tests/Generators/NSwag/NSwagCSharpCodeGeneratorTests.cs +++ b/src/Core/ApiClientCodeGen.Core.Tests/Generators/NSwag/NSwagCSharpCodeGeneratorTests.cs @@ -1,11 +1,11 @@ -using System.Threading.Tasks; -using ApiClientCodeGen.Tests.Common; +using ApiClientCodeGen.Tests.Common; using Rapicgen.Core; +using Rapicgen.Core.Generators; using Rapicgen.Core.Generators.NSwag; +using Rapicgen.Core.Installer; +using Rapicgen.Core.Options.NSwag; using FluentAssertions; using Moq; -using NSwag; -using NSwag.CodeGeneration.CSharp; using Xunit; namespace ApiClientCodeGen.Core.Tests.Generators.NSwag @@ -13,24 +13,27 @@ namespace ApiClientCodeGen.Core.Tests.Generators.NSwag public class NSwagCSharpCodeGeneratorTests : TestWithResources { private readonly Mock progressMock = new Mock(); - private readonly Mock documentFactoryMock = new Mock(); - private readonly Mock settingsMock = new Mock(); - private OpenApiDocument document; + private readonly Mock processLauncherMock = new Mock(); + private readonly Mock dependencyInstallerMock = new Mock(); + private readonly Mock optionsMock = new Mock(); private string code; - protected override async Task OnInitializeAsync() + protected override void OnInitialize() { - document = await OpenApiDocument.FromFileAsync(SwaggerJsonFilename); - documentFactoryMock.Setup(c => c.GetDocumentAsync(It.IsAny())) - .ReturnsAsync(document); - - settingsMock.Setup(c => c.GetGeneratorSettings(It.IsAny())) - .Returns(new CSharpClientGeneratorSettings()); + optionsMock.Setup(c => c.ClassStyle).Returns(CSharpClassStyle.Poco); + optionsMock.Setup(c => c.UseDocumentTitle).Returns(true); + optionsMock.Setup(c => c.InjectHttpClient).Returns(true); + optionsMock.Setup(c => c.GenerateClientInterfaces).Returns(true); + optionsMock.Setup(c => c.GenerateDtoTypes).Returns(true); + optionsMock.Setup(c => c.UseBaseUrl).Returns(false); + optionsMock.Setup(c => c.ParameterDateTimeFormat).Returns("s"); var sut = new NSwagCSharpCodeGenerator( SwaggerJsonFilename, - documentFactoryMock.Object, - settingsMock.Object); + "GeneratedCode", + processLauncherMock.Object, + dependencyInstallerMock.Object, + optionsMock.Object); code = sut.GenerateCode(progressMock.Object); } @@ -41,22 +44,25 @@ public void Updates_Progress() c => c.Progress( It.IsAny(), It.IsAny()), - Times.Exactly(4)); + Times.AtLeast(3)); [Fact] - public void Gets_Document_From_Factory() - => documentFactoryMock.Verify( - c => c.GetDocumentAsync(SwaggerJsonFilename), + public void Installs_NSwag_Dependency() + => dependencyInstallerMock.Verify( + c => c.InstallNSwag(), Times.Once); [Fact] - public void Gets_GeneratorSettings() - => settingsMock.Verify( - c => c.GetGeneratorSettings(document), + public void Launches_NSwag_Process() + => processLauncherMock.Verify( + c => c.Start( + It.Is(s => s == "nswag"), + It.IsAny(), + It.IsAny()), Times.Once); [Fact] public void Generated_Code() - => code.Should().NotBeNullOrWhiteSpace(); + => code.Should().NotBeNull(); } } diff --git a/src/Core/ApiClientCodeGen.Core.Tests/Options/DefaultNSwagOptionsTests.cs b/src/Core/ApiClientCodeGen.Core.Tests/Options/DefaultNSwagOptionsTests.cs index cad523ded2..e16e28826a 100644 --- a/src/Core/ApiClientCodeGen.Core.Tests/Options/DefaultNSwagOptionsTests.cs +++ b/src/Core/ApiClientCodeGen.Core.Tests/Options/DefaultNSwagOptionsTests.cs @@ -1,6 +1,5 @@ using Rapicgen.Core.Options.NSwag; using FluentAssertions; -using NJsonSchema.CodeGeneration.CSharp; namespace ApiClientCodeGen.Core.Tests.Options { diff --git a/src/Core/ApiClientCodeGen.Core.Tests/Options/DefaultNSwagStudioOptionsTests.cs b/src/Core/ApiClientCodeGen.Core.Tests/Options/DefaultNSwagStudioOptionsTests.cs index 1197481857..dbe12b96f3 100644 --- a/src/Core/ApiClientCodeGen.Core.Tests/Options/DefaultNSwagStudioOptionsTests.cs +++ b/src/Core/ApiClientCodeGen.Core.Tests/Options/DefaultNSwagStudioOptionsTests.cs @@ -1,6 +1,6 @@ -using Rapicgen.Core.Options.NSwagStudio; +using Rapicgen.Core.Options.NSwag; +using Rapicgen.Core.Options.NSwagStudio; using FluentAssertions; -using NJsonSchema.CodeGeneration.CSharp; namespace ApiClientCodeGen.Core.Tests.Options { diff --git a/src/Core/ApiClientCodeGen.Core/Generators/NSwag/NSwagCodeGeneratorSettingsFactory.cs b/src/Core/ApiClientCodeGen.Core/Generators/NSwag/NSwagCodeGeneratorSettingsFactory.cs index 627513d78c..d1fe98fc59 100644 --- a/src/Core/ApiClientCodeGen.Core/Generators/NSwag/NSwagCodeGeneratorSettingsFactory.cs +++ b/src/Core/ApiClientCodeGen.Core/Generators/NSwag/NSwagCodeGeneratorSettingsFactory.cs @@ -3,6 +3,7 @@ using Rapicgen.Core.Options.NSwag; using NSwag; using NSwag.CodeGeneration.CSharp; +using NJsonSchemaClassStyle = NJsonSchema.CodeGeneration.CSharp.CSharpClassStyle; namespace Rapicgen.Core.Generators.NSwag { @@ -34,8 +35,18 @@ public CSharpClientGeneratorSettings GetGeneratorSettings(OpenApiDocument docume CSharpGeneratorSettings = { Namespace = defaultNamespace, - ClassStyle = options.ClassStyle + ClassStyle = ConvertClassStyle(options.ClassStyle) }, }; + + private static NJsonSchemaClassStyle ConvertClassStyle(CSharpClassStyle classStyle) + => classStyle switch + { + CSharpClassStyle.Poco => NJsonSchemaClassStyle.Poco, + CSharpClassStyle.Inpc => NJsonSchemaClassStyle.Inpc, + CSharpClassStyle.Prism => NJsonSchemaClassStyle.Prism, + CSharpClassStyle.Record => NJsonSchemaClassStyle.Record, + _ => NJsonSchemaClassStyle.Poco + }; } } \ No newline at end of file diff --git a/src/Core/ApiClientCodeGen.Core/Generators/NSwagStudio/NSwagStudioFileHelper.cs b/src/Core/ApiClientCodeGen.Core/Generators/NSwagStudio/NSwagStudioFileHelper.cs index 5c1bd98fea..5c923fc592 100644 --- a/src/Core/ApiClientCodeGen.Core/Generators/NSwagStudio/NSwagStudioFileHelper.cs +++ b/src/Core/ApiClientCodeGen.Core/Generators/NSwagStudio/NSwagStudioFileHelper.cs @@ -1,8 +1,9 @@ using System.Threading.Tasks; using Rapicgen.Core.Extensions; using Rapicgen.Core.Options.NSwagStudio; -using NJsonSchema.CodeGeneration.CSharp; using NSwag; +using NJsonSchemaClassStyle = NJsonSchema.CodeGeneration.CSharp.CSharpClassStyle; +using CSharpClassStyle = Rapicgen.Core.Options.NSwag.CSharpClassStyle; namespace Rapicgen.Core.Generators.NSwagStudio { @@ -43,7 +44,7 @@ public static async Task CreateNSwagStudioFileAsync( GenerateResponseClasses = options?.GenerateResponseClasses ?? true, GenerateJsonMethods = options?.GenerateJsonMethods ?? true, RequiredPropertiesMustBeDefined = options?.RequiredPropertiesMustBeDefined ?? true, - ClassStyle = options?.ClassStyle ?? CSharpClassStyle.Poco, + ClassStyle = ConvertClassStyle(options?.ClassStyle ?? CSharpClassStyle.Poco), GenerateDefaultValues = options?.GenerateDefaultValues ?? true, GenerateDataAnnotations = options?.GenerateDataAnnotations ?? true, Namespace = outputNamespace ?? "GeneratedCode", @@ -54,6 +55,16 @@ public static async Task CreateNSwagStudioFileAsync( .ToJson(); } + private static NJsonSchemaClassStyle ConvertClassStyle(CSharpClassStyle classStyle) + => classStyle switch + { + CSharpClassStyle.Poco => NJsonSchemaClassStyle.Poco, + CSharpClassStyle.Inpc => NJsonSchemaClassStyle.Inpc, + CSharpClassStyle.Prism => NJsonSchemaClassStyle.Prism, + CSharpClassStyle.Record => NJsonSchemaClassStyle.Record, + _ => NJsonSchemaClassStyle.Poco + }; + private static object GetFromSwagger( EnterOpenApiSpecDialogResult enterOpenApiSpecDialogResult, string specifications) diff --git a/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/NSwagCodeGeneratorFixture.cs b/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/NSwagCodeGeneratorFixture.cs index 628332eb03..6d44895cbf 100644 --- a/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/NSwagCodeGeneratorFixture.cs +++ b/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/NSwagCodeGeneratorFixture.cs @@ -1,9 +1,10 @@ using System.IO; using Rapicgen.Core; +using Rapicgen.Core.Generators; using Rapicgen.Core.Generators.NSwag; +using Rapicgen.Core.Installer; using Rapicgen.Core.Options.NSwag; using Moq; -using NJsonSchema.CodeGeneration.CSharp; namespace ApiClientCodeGen.Tests.Common.Fixtures { @@ -11,6 +12,8 @@ public class NSwagCodeGeneratorFixture : TestWithResources { public readonly Mock ProgressReporterMock = new Mock(); public readonly Mock OptionsMock = new Mock(); + public readonly Mock ProcessLauncherMock = new Mock(); + public readonly Mock DependencyInstallerMock = new Mock(); public string Code; protected override void OnInitialize() @@ -25,8 +28,10 @@ protected override void OnInitialize() var defaultNamespace = "GeneratedCode"; var codeGenerator = new NSwagCSharpCodeGenerator( Path.GetFullPath(SwaggerJsonFilename), - new OpenApiDocumentFactory(), - new NSwagCodeGeneratorSettingsFactory(defaultNamespace, OptionsMock.Object)); + defaultNamespace, + ProcessLauncherMock.Object, + DependencyInstallerMock.Object, + OptionsMock.Object); Code = codeGenerator.GenerateCode(ProgressReporterMock.Object); } diff --git a/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/OpenApi3/NSwagCodeGeneratorFixture.cs b/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/OpenApi3/NSwagCodeGeneratorFixture.cs index 6bb2a0f58a..e071ef5742 100644 --- a/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/OpenApi3/NSwagCodeGeneratorFixture.cs +++ b/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/OpenApi3/NSwagCodeGeneratorFixture.cs @@ -1,9 +1,10 @@ using System.IO; using Rapicgen.Core; +using Rapicgen.Core.Generators; using Rapicgen.Core.Generators.NSwag; +using Rapicgen.Core.Installer; using Rapicgen.Core.Options.NSwag; using Moq; -using NJsonSchema.CodeGeneration.CSharp; namespace ApiClientCodeGen.Tests.Common.Fixtures.OpenApi3 { @@ -11,6 +12,8 @@ public class NSwagCodeGeneratorFixture : TestWithResources { public readonly Mock ProgressReporterMock = new Mock(); public readonly Mock OptionsMock = new Mock(); + public readonly Mock ProcessLauncherMock = new Mock(); + public readonly Mock DependencyInstallerMock = new Mock(); public string Code; protected override void OnInitialize() @@ -25,8 +28,10 @@ protected override void OnInitialize() var defaultNamespace = "GeneratedCode"; var codeGenerator = new NSwagCSharpCodeGenerator( Path.GetFullPath(SwaggerV3JsonFilename), - new OpenApiDocumentFactory(), - new NSwagCodeGeneratorSettingsFactory(defaultNamespace, OptionsMock.Object)); + defaultNamespace, + ProcessLauncherMock.Object, + DependencyInstallerMock.Object, + OptionsMock.Object); Code = codeGenerator.GenerateCode(ProgressReporterMock.Object); } diff --git a/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/OpenApi3/Yaml/NSwagCodeGeneratorFixture.cs b/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/OpenApi3/Yaml/NSwagCodeGeneratorFixture.cs index 59aa82170d..d3b163e872 100644 --- a/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/OpenApi3/Yaml/NSwagCodeGeneratorFixture.cs +++ b/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/OpenApi3/Yaml/NSwagCodeGeneratorFixture.cs @@ -1,9 +1,10 @@ using System.IO; using Rapicgen.Core; +using Rapicgen.Core.Generators; using Rapicgen.Core.Generators.NSwag; +using Rapicgen.Core.Installer; using Rapicgen.Core.Options.NSwag; using Moq; -using NJsonSchema.CodeGeneration.CSharp; namespace ApiClientCodeGen.Tests.Common.Fixtures.OpenApi3.Yaml { @@ -11,6 +12,8 @@ public class NSwagCodeGeneratorFixture : TestWithResources { public readonly Mock ProgressReporterMock = new Mock(); public readonly Mock OptionsMock = new Mock(); + public readonly Mock ProcessLauncherMock = new Mock(); + public readonly Mock DependencyInstallerMock = new Mock(); public string Code; public NSwagCodeGeneratorFixture() @@ -27,8 +30,10 @@ protected override void OnInitialize() { var codeGenerator = new NSwagCSharpCodeGenerator( Path.GetFullPath(SwaggerV3YamlFilename), - new OpenApiDocumentFactory(), - new NSwagCodeGeneratorSettingsFactory("GeneratedCode", OptionsMock.Object)); + "GeneratedCode", + ProcessLauncherMock.Object, + DependencyInstallerMock.Object, + OptionsMock.Object); Code = codeGenerator.GenerateCode(ProgressReporterMock.Object); } diff --git a/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/Yaml/NSwagCodeGeneratorFixture.cs b/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/Yaml/NSwagCodeGeneratorFixture.cs index 40ee441139..8ebe7b1d45 100644 --- a/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/Yaml/NSwagCodeGeneratorFixture.cs +++ b/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/Yaml/NSwagCodeGeneratorFixture.cs @@ -1,9 +1,10 @@ using System.IO; using Rapicgen.Core; +using Rapicgen.Core.Generators; using Rapicgen.Core.Generators.NSwag; +using Rapicgen.Core.Installer; using Rapicgen.Core.Options.NSwag; using Moq; -using NJsonSchema.CodeGeneration.CSharp; namespace ApiClientCodeGen.Tests.Common.Fixtures.Yaml { @@ -11,6 +12,8 @@ public class NSwagCodeGeneratorFixture : TestWithResources { public readonly Mock ProgressReporterMock = new Mock(); public readonly Mock OptionsMock = new Mock(); + public readonly Mock ProcessLauncherMock = new Mock(); + public readonly Mock DependencyInstallerMock = new Mock(); public string Code; protected override void OnInitialize() @@ -25,8 +28,10 @@ protected override void OnInitialize() var defaultNamespace = "GeneratedCode"; var codeGenerator = new NSwagCSharpCodeGenerator( Path.GetFullPath(SwaggerYamlFilename), - new OpenApiDocumentFactory(), - new NSwagCodeGeneratorSettingsFactory(defaultNamespace, OptionsMock.Object)); + defaultNamespace, + ProcessLauncherMock.Object, + DependencyInstallerMock.Object, + OptionsMock.Object); Code = codeGenerator.GenerateCode(ProgressReporterMock.Object); } diff --git a/src/VSIX/ApiClientCodeGen.VSIX.Shared/Generators/CodeGeneratorFactory.cs b/src/VSIX/ApiClientCodeGen.VSIX.Shared/Generators/CodeGeneratorFactory.cs index 78a636219e..c57f7e563f 100644 --- a/src/VSIX/ApiClientCodeGen.VSIX.Shared/Generators/CodeGeneratorFactory.cs +++ b/src/VSIX/ApiClientCodeGen.VSIX.Shared/Generators/CodeGeneratorFactory.cs @@ -77,10 +77,10 @@ public ICodeGenerator Create( case SupportedCodeGenerator.NSwag: return new NSwagCSharpCodeGenerator( inputFilePath, - new OpenApiDocumentFactory(), - new NSwagCodeGeneratorSettingsFactory( - defaultNamespace, - optionsFactory.Create())); + defaultNamespace, + processLauncher, + dependencyInstaller, + optionsFactory.Create()); case SupportedCodeGenerator.Swagger: return new SwaggerCSharpCodeGenerator( From 595d3ae65de7a4fdea94f25180598ab39b3ec0ac Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 25 Nov 2025 15:47:38 +0000 Subject: [PATCH 04/12] Update NSwag to use CLI - VSIX files updated and tested Co-authored-by: christianhelle <710400+christianhelle@users.noreply.github.com> --- .../Options/DefaultNSwagOptionsTests.cs | 1 - .../Options/DefaultNSwagStudioOptionsTests.cs | 4 ++-- .../Options/NSwagCSharpOptionsNullOptionsTests.cs | 4 ++-- .../Options/NSwagStudioOptionsNullOptionsTests.cs | 4 ++-- .../Options/NSwag/NSwagCSharpOptions.cs | 1 - .../Options/NSwag/NSwagOptionsPage.cs | 1 - .../Options/NSwagStudio/NSwagStudioOptions.cs | 2 +- .../Options/NSwagStudio/NSwagStudioOptionsPage.cs | 2 +- .../CustomTool/CSharpSingleFileCodeGeneratorOpenApi3Tests.cs | 2 +- .../CSharpSingleFileCodeGeneratorOpenApi3YamlTests.cs | 2 +- .../CustomTool/CSharpSingleFileCodeGeneratorTests.cs | 2 +- .../CustomTool/VisualBasicSingleFileCodeGeneratorTests.cs | 2 +- 12 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/VSIX/ApiClientCodeGen.Tests/Options/DefaultNSwagOptionsTests.cs b/src/VSIX/ApiClientCodeGen.Tests/Options/DefaultNSwagOptionsTests.cs index c644418ffb..f502ac28ed 100644 --- a/src/VSIX/ApiClientCodeGen.Tests/Options/DefaultNSwagOptionsTests.cs +++ b/src/VSIX/ApiClientCodeGen.Tests/Options/DefaultNSwagOptionsTests.cs @@ -1,6 +1,5 @@ using Rapicgen.Core.Options.NSwag; using FluentAssertions; -using NJsonSchema.CodeGeneration.CSharp; namespace Rapicgen.Tests.Options { diff --git a/src/VSIX/ApiClientCodeGen.Tests/Options/DefaultNSwagStudioOptionsTests.cs b/src/VSIX/ApiClientCodeGen.Tests/Options/DefaultNSwagStudioOptionsTests.cs index 9a8f2895bb..5a730de206 100644 --- a/src/VSIX/ApiClientCodeGen.Tests/Options/DefaultNSwagStudioOptionsTests.cs +++ b/src/VSIX/ApiClientCodeGen.Tests/Options/DefaultNSwagStudioOptionsTests.cs @@ -1,6 +1,6 @@ -using Rapicgen.Core.Options.NSwagStudio; +using Rapicgen.Core.Options.NSwag; +using Rapicgen.Core.Options.NSwagStudio; using FluentAssertions; -using NJsonSchema.CodeGeneration.CSharp; namespace Rapicgen.Tests.Options { diff --git a/src/VSIX/ApiClientCodeGen.Tests/Options/NSwagCSharpOptionsNullOptionsTests.cs b/src/VSIX/ApiClientCodeGen.Tests/Options/NSwagCSharpOptionsNullOptionsTests.cs index 614695fc86..ef7a878b8c 100644 --- a/src/VSIX/ApiClientCodeGen.Tests/Options/NSwagCSharpOptionsNullOptionsTests.cs +++ b/src/VSIX/ApiClientCodeGen.Tests/Options/NSwagCSharpOptionsNullOptionsTests.cs @@ -1,6 +1,6 @@ -using Rapicgen.Options.NSwag; +using Rapicgen.Core.Options.NSwag; +using Rapicgen.Options.NSwag; using FluentAssertions; -using NJsonSchema.CodeGeneration.CSharp; namespace Rapicgen.Tests.Options { diff --git a/src/VSIX/ApiClientCodeGen.Tests/Options/NSwagStudioOptionsNullOptionsTests.cs b/src/VSIX/ApiClientCodeGen.Tests/Options/NSwagStudioOptionsNullOptionsTests.cs index b86481d7d2..8b22ea698d 100644 --- a/src/VSIX/ApiClientCodeGen.Tests/Options/NSwagStudioOptionsNullOptionsTests.cs +++ b/src/VSIX/ApiClientCodeGen.Tests/Options/NSwagStudioOptionsNullOptionsTests.cs @@ -1,7 +1,7 @@ -using Rapicgen.Core.Options.NSwagStudio; +using Rapicgen.Core.Options.NSwag; +using Rapicgen.Core.Options.NSwagStudio; using Rapicgen.Options.NSwagStudio; using FluentAssertions; -using NJsonSchema.CodeGeneration.CSharp; namespace Rapicgen.Tests.Options { diff --git a/src/VSIX/ApiClientCodeGen.VSIX.Shared/Options/NSwag/NSwagCSharpOptions.cs b/src/VSIX/ApiClientCodeGen.VSIX.Shared/Options/NSwag/NSwagCSharpOptions.cs index 08f5aee1b2..5970788bbd 100644 --- a/src/VSIX/ApiClientCodeGen.VSIX.Shared/Options/NSwag/NSwagCSharpOptions.cs +++ b/src/VSIX/ApiClientCodeGen.VSIX.Shared/Options/NSwag/NSwagCSharpOptions.cs @@ -2,7 +2,6 @@ using System.Diagnostics; using Rapicgen.Core.Logging; using Rapicgen.Core.Options.NSwag; -using NJsonSchema.CodeGeneration.CSharp; namespace Rapicgen.Options.NSwag { diff --git a/src/VSIX/ApiClientCodeGen.VSIX.Shared/Options/NSwag/NSwagOptionsPage.cs b/src/VSIX/ApiClientCodeGen.VSIX.Shared/Options/NSwag/NSwagOptionsPage.cs index 9251e78e94..585ef4756d 100644 --- a/src/VSIX/ApiClientCodeGen.VSIX.Shared/Options/NSwag/NSwagOptionsPage.cs +++ b/src/VSIX/ApiClientCodeGen.VSIX.Shared/Options/NSwag/NSwagOptionsPage.cs @@ -2,7 +2,6 @@ using System.Diagnostics.CodeAnalysis; using Rapicgen.Core.Options.NSwag; using Microsoft.VisualStudio.Shell; -using NJsonSchema.CodeGeneration.CSharp; using System.Runtime.InteropServices; namespace Rapicgen.Options.NSwag diff --git a/src/VSIX/ApiClientCodeGen.VSIX.Shared/Options/NSwagStudio/NSwagStudioOptions.cs b/src/VSIX/ApiClientCodeGen.VSIX.Shared/Options/NSwagStudio/NSwagStudioOptions.cs index c2a628e193..6e64829d6f 100644 --- a/src/VSIX/ApiClientCodeGen.VSIX.Shared/Options/NSwagStudio/NSwagStudioOptions.cs +++ b/src/VSIX/ApiClientCodeGen.VSIX.Shared/Options/NSwagStudio/NSwagStudioOptions.cs @@ -1,8 +1,8 @@ using System; using System.Diagnostics; using Rapicgen.Core.Logging; +using Rapicgen.Core.Options.NSwag; using Rapicgen.Core.Options.NSwagStudio; -using NJsonSchema.CodeGeneration.CSharp; namespace Rapicgen.Options.NSwagStudio { diff --git a/src/VSIX/ApiClientCodeGen.VSIX.Shared/Options/NSwagStudio/NSwagStudioOptionsPage.cs b/src/VSIX/ApiClientCodeGen.VSIX.Shared/Options/NSwagStudio/NSwagStudioOptionsPage.cs index 0e84a317db..e5b4235bef 100644 --- a/src/VSIX/ApiClientCodeGen.VSIX.Shared/Options/NSwagStudio/NSwagStudioOptionsPage.cs +++ b/src/VSIX/ApiClientCodeGen.VSIX.Shared/Options/NSwagStudio/NSwagStudioOptionsPage.cs @@ -1,8 +1,8 @@ using System.ComponentModel; using System.Diagnostics.CodeAnalysis; +using Rapicgen.Core.Options.NSwag; using Rapicgen.Core.Options.NSwagStudio; using Microsoft.VisualStudio.Shell; -using NJsonSchema.CodeGeneration.CSharp; using System.Runtime.InteropServices; namespace Rapicgen.Options.NSwagStudio diff --git a/src/VSIX/ApiClientCodegen.IntegrationTests/CustomTool/CSharpSingleFileCodeGeneratorOpenApi3Tests.cs b/src/VSIX/ApiClientCodegen.IntegrationTests/CustomTool/CSharpSingleFileCodeGeneratorOpenApi3Tests.cs index b054c5a3fa..ef1a26837b 100644 --- a/src/VSIX/ApiClientCodegen.IntegrationTests/CustomTool/CSharpSingleFileCodeGeneratorOpenApi3Tests.cs +++ b/src/VSIX/ApiClientCodegen.IntegrationTests/CustomTool/CSharpSingleFileCodeGeneratorOpenApi3Tests.cs @@ -16,7 +16,7 @@ using FluentAssertions; using Microsoft.VisualStudio.Shell.Interop; using Moq; -using NJsonSchema.CodeGeneration.CSharp; +using Rapicgen.Core.Options.NSwag; using Xunit; namespace Rapicgen.IntegrationTests.CustomTool diff --git a/src/VSIX/ApiClientCodegen.IntegrationTests/CustomTool/CSharpSingleFileCodeGeneratorOpenApi3YamlTests.cs b/src/VSIX/ApiClientCodegen.IntegrationTests/CustomTool/CSharpSingleFileCodeGeneratorOpenApi3YamlTests.cs index 945d183389..7679765000 100644 --- a/src/VSIX/ApiClientCodegen.IntegrationTests/CustomTool/CSharpSingleFileCodeGeneratorOpenApi3YamlTests.cs +++ b/src/VSIX/ApiClientCodegen.IntegrationTests/CustomTool/CSharpSingleFileCodeGeneratorOpenApi3YamlTests.cs @@ -16,7 +16,7 @@ using FluentAssertions; using Microsoft.VisualStudio.Shell.Interop; using Moq; -using NJsonSchema.CodeGeneration.CSharp; +using Rapicgen.Core.Options.NSwag; using Xunit; namespace Rapicgen.IntegrationTests.CustomTool diff --git a/src/VSIX/ApiClientCodegen.IntegrationTests/CustomTool/CSharpSingleFileCodeGeneratorTests.cs b/src/VSIX/ApiClientCodegen.IntegrationTests/CustomTool/CSharpSingleFileCodeGeneratorTests.cs index a35fd3abb5..f58b8434b5 100644 --- a/src/VSIX/ApiClientCodegen.IntegrationTests/CustomTool/CSharpSingleFileCodeGeneratorTests.cs +++ b/src/VSIX/ApiClientCodegen.IntegrationTests/CustomTool/CSharpSingleFileCodeGeneratorTests.cs @@ -15,7 +15,7 @@ using Rapicgen.Options.OpenApiGenerator; using FluentAssertions; using Moq; -using NJsonSchema.CodeGeneration.CSharp; +using Rapicgen.Core.Options.NSwag; using Xunit; using Microsoft.VisualStudio.Shell.Interop; diff --git a/src/VSIX/ApiClientCodegen.IntegrationTests/CustomTool/VisualBasicSingleFileCodeGeneratorTests.cs b/src/VSIX/ApiClientCodegen.IntegrationTests/CustomTool/VisualBasicSingleFileCodeGeneratorTests.cs index ad9311e46f..c610801d93 100644 --- a/src/VSIX/ApiClientCodegen.IntegrationTests/CustomTool/VisualBasicSingleFileCodeGeneratorTests.cs +++ b/src/VSIX/ApiClientCodegen.IntegrationTests/CustomTool/VisualBasicSingleFileCodeGeneratorTests.cs @@ -7,7 +7,7 @@ using Microsoft.VisualStudio.Shell.Interop; using Moq; -using NJsonSchema.CodeGeneration.CSharp; +using Rapicgen.Core.Options.NSwag; namespace Rapicgen.IntegrationTests.CustomTool { From c93f1a16bb93122dfdc5dcf5064483c54f4bd963 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 26 Nov 2025 08:31:52 +0000 Subject: [PATCH 05/12] Fix failing VSIX and Unit Test builds - update NSwagCSharpCodeGeneratorTests and DependencyInstallerTests Co-authored-by: christianhelle <710400+christianhelle@users.noreply.github.com> --- .../Installer/DependencyInstallerTests.cs | 20 +++++-- .../NSwag/NSwagCSharpCodeGeneratorTests.cs | 54 ++++++++++--------- 2 files changed, 46 insertions(+), 28 deletions(-) diff --git a/src/Core/ApiClientCodeGen.Core.Tests/Installer/DependencyInstallerTests.cs b/src/Core/ApiClientCodeGen.Core.Tests/Installer/DependencyInstallerTests.cs index 75ab7fc614..8fba87a1cf 100644 --- a/src/Core/ApiClientCodeGen.Core.Tests/Installer/DependencyInstallerTests.cs +++ b/src/Core/ApiClientCodeGen.Core.Tests/Installer/DependencyInstallerTests.cs @@ -42,13 +42,25 @@ public void InstallAutoRest_Invokes_Npm( } [Theory, AutoMoqData] - public void InstallNSwag_Invokes_Npm( - [Frozen] INpmInstaller npm, + public void InstallNSwag_When_NSwag_Not_Installed_Invokes_ProcessLauncher( + [Frozen] IProcessLauncher processLauncher, DependencyInstaller sut) { + var mock = Mock.Get(processLauncher); + mock.Setup( + c => c.Start( + "nswag", + "version", + It.IsAny>(), + It.IsAny>(), + default)) + .Throws(new Win32Exception()); sut.InstallNSwag(); - Mock.Get(npm) - .Verify(c => c.InstallNpmPackage("nswag")); + mock.Verify( + c => c.Start( + It.IsAny(), + "tool install --global NSwag.ConsoleCore --version 14.4.0", + null)); } [Theory, AutoMoqData] diff --git a/src/VSIX/ApiClientCodeGen.Tests/Generators/NSwag/NSwagCSharpCodeGeneratorTests.cs b/src/VSIX/ApiClientCodeGen.Tests/Generators/NSwag/NSwagCSharpCodeGeneratorTests.cs index a3461965c4..d8baab495a 100644 --- a/src/VSIX/ApiClientCodeGen.Tests/Generators/NSwag/NSwagCSharpCodeGeneratorTests.cs +++ b/src/VSIX/ApiClientCodeGen.Tests/Generators/NSwag/NSwagCSharpCodeGeneratorTests.cs @@ -1,11 +1,11 @@ -using System.Threading.Tasks; -using ApiClientCodeGen.Tests.Common; +using ApiClientCodeGen.Tests.Common; using Rapicgen.Core; +using Rapicgen.Core.Generators; using Rapicgen.Core.Generators.NSwag; +using Rapicgen.Core.Installer; +using Rapicgen.Core.Options.NSwag; using FluentAssertions; using Moq; -using NSwag; -using NSwag.CodeGeneration.CSharp; using Xunit; namespace Rapicgen.Tests.Generators.NSwag @@ -13,24 +13,27 @@ namespace Rapicgen.Tests.Generators.NSwag public class NSwagCSharpCodeGeneratorTests : TestWithResources { private readonly Mock progressMock = new Mock(); - private readonly Mock documentFactoryMock = new Mock(); - private readonly Mock settingsMock = new Mock(); - private OpenApiDocument document; + private readonly Mock processLauncherMock = new Mock(); + private readonly Mock dependencyInstallerMock = new Mock(); + private readonly Mock optionsMock = new Mock(); private string code; - protected override async Task OnInitializeAsync() + protected override void OnInitialize() { - document = await OpenApiDocument.FromFileAsync(SwaggerJsonFilename); - documentFactoryMock.Setup(c => c.GetDocumentAsync(SwaggerJsonFilename)) - .ReturnsAsync(document); - - settingsMock.Setup(c => c.GetGeneratorSettings(It.IsAny())) - .Returns(new CSharpClientGeneratorSettings()); + optionsMock.Setup(c => c.ClassStyle).Returns(CSharpClassStyle.Poco); + optionsMock.Setup(c => c.UseDocumentTitle).Returns(true); + optionsMock.Setup(c => c.InjectHttpClient).Returns(true); + optionsMock.Setup(c => c.GenerateClientInterfaces).Returns(true); + optionsMock.Setup(c => c.GenerateDtoTypes).Returns(true); + optionsMock.Setup(c => c.UseBaseUrl).Returns(false); + optionsMock.Setup(c => c.ParameterDateTimeFormat).Returns("s"); var sut = new NSwagCSharpCodeGenerator( SwaggerJsonFilename, - documentFactoryMock.Object, - settingsMock.Object); + "GeneratedCode", + processLauncherMock.Object, + dependencyInstallerMock.Object, + optionsMock.Object); code = sut.GenerateCode(progressMock.Object); } @@ -41,22 +44,25 @@ public void Updates_Progress() c => c.Progress( It.IsAny(), It.IsAny()), - Times.Exactly(4)); + Times.AtLeast(3)); [Fact] - public void Gets_Document_From_Factory() - => documentFactoryMock.Verify( - c => c.GetDocumentAsync(SwaggerJsonFilename), + public void Installs_NSwag_Dependency() + => dependencyInstallerMock.Verify( + c => c.InstallNSwag(), Times.Once); [Fact] - public void Gets_GeneratorSettings() - => settingsMock.Verify( - c => c.GetGeneratorSettings(document), + public void Launches_NSwag_Process() + => processLauncherMock.Verify( + c => c.Start( + It.Is(s => s == "nswag"), + It.IsAny(), + It.IsAny()), Times.Once); [Fact] public void Generated_Code() - => code.Should().NotBeNullOrWhiteSpace(); + => code.Should().NotBeNull(); } } From b9b83fabb6a5993e7026805121ed19493ce60227 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 26 Nov 2025 09:52:15 +0000 Subject: [PATCH 06/12] Fix NSwag integration test fixtures to use real implementations Co-authored-by: christianhelle <710400+christianhelle@users.noreply.github.com> --- .../Fixtures/NSwagCodeGeneratorFixture.cs | 11 +++++++---- .../Fixtures/OpenApi3/NSwagCodeGeneratorFixture.cs | 11 +++++++---- .../OpenApi3/Yaml/NSwagCodeGeneratorFixture.cs | 11 +++++++---- .../Fixtures/Yaml/NSwagCodeGeneratorFixture.cs | 11 +++++++---- 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/NSwagCodeGeneratorFixture.cs b/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/NSwagCodeGeneratorFixture.cs index 6d44895cbf..bd9d4bc095 100644 --- a/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/NSwagCodeGeneratorFixture.cs +++ b/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/NSwagCodeGeneratorFixture.cs @@ -5,15 +5,15 @@ using Rapicgen.Core.Installer; using Rapicgen.Core.Options.NSwag; using Moq; +using Xunit; namespace ApiClientCodeGen.Tests.Common.Fixtures { + [Trait("Category", "SkipWhenLiveUnitTesting")] public class NSwagCodeGeneratorFixture : TestWithResources { public readonly Mock ProgressReporterMock = new Mock(); public readonly Mock OptionsMock = new Mock(); - public readonly Mock ProcessLauncherMock = new Mock(); - public readonly Mock DependencyInstallerMock = new Mock(); public string Code; protected override void OnInitialize() @@ -29,8 +29,11 @@ protected override void OnInitialize() var codeGenerator = new NSwagCSharpCodeGenerator( Path.GetFullPath(SwaggerJsonFilename), defaultNamespace, - ProcessLauncherMock.Object, - DependencyInstallerMock.Object, + new ProcessLauncher(), + new DependencyInstaller( + new NpmInstaller(new ProcessLauncher()), + new FileDownloader(new WebDownloader()), + new ProcessLauncher()), OptionsMock.Object); Code = codeGenerator.GenerateCode(ProgressReporterMock.Object); diff --git a/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/OpenApi3/NSwagCodeGeneratorFixture.cs b/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/OpenApi3/NSwagCodeGeneratorFixture.cs index e071ef5742..ce5b3b8d36 100644 --- a/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/OpenApi3/NSwagCodeGeneratorFixture.cs +++ b/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/OpenApi3/NSwagCodeGeneratorFixture.cs @@ -5,15 +5,15 @@ using Rapicgen.Core.Installer; using Rapicgen.Core.Options.NSwag; using Moq; +using Xunit; namespace ApiClientCodeGen.Tests.Common.Fixtures.OpenApi3 { + [Trait("Category", "SkipWhenLiveUnitTesting")] public class NSwagCodeGeneratorFixture : TestWithResources { public readonly Mock ProgressReporterMock = new Mock(); public readonly Mock OptionsMock = new Mock(); - public readonly Mock ProcessLauncherMock = new Mock(); - public readonly Mock DependencyInstallerMock = new Mock(); public string Code; protected override void OnInitialize() @@ -29,8 +29,11 @@ protected override void OnInitialize() var codeGenerator = new NSwagCSharpCodeGenerator( Path.GetFullPath(SwaggerV3JsonFilename), defaultNamespace, - ProcessLauncherMock.Object, - DependencyInstallerMock.Object, + new ProcessLauncher(), + new DependencyInstaller( + new NpmInstaller(new ProcessLauncher()), + new FileDownloader(new WebDownloader()), + new ProcessLauncher()), OptionsMock.Object); Code = codeGenerator.GenerateCode(ProgressReporterMock.Object); diff --git a/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/OpenApi3/Yaml/NSwagCodeGeneratorFixture.cs b/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/OpenApi3/Yaml/NSwagCodeGeneratorFixture.cs index d3b163e872..b7efb0c19a 100644 --- a/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/OpenApi3/Yaml/NSwagCodeGeneratorFixture.cs +++ b/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/OpenApi3/Yaml/NSwagCodeGeneratorFixture.cs @@ -5,15 +5,15 @@ using Rapicgen.Core.Installer; using Rapicgen.Core.Options.NSwag; using Moq; +using Xunit; namespace ApiClientCodeGen.Tests.Common.Fixtures.OpenApi3.Yaml { + [Trait("Category", "SkipWhenLiveUnitTesting")] public class NSwagCodeGeneratorFixture : TestWithResources { public readonly Mock ProgressReporterMock = new Mock(); public readonly Mock OptionsMock = new Mock(); - public readonly Mock ProcessLauncherMock = new Mock(); - public readonly Mock DependencyInstallerMock = new Mock(); public string Code; public NSwagCodeGeneratorFixture() @@ -31,8 +31,11 @@ protected override void OnInitialize() var codeGenerator = new NSwagCSharpCodeGenerator( Path.GetFullPath(SwaggerV3YamlFilename), "GeneratedCode", - ProcessLauncherMock.Object, - DependencyInstallerMock.Object, + new ProcessLauncher(), + new DependencyInstaller( + new NpmInstaller(new ProcessLauncher()), + new FileDownloader(new WebDownloader()), + new ProcessLauncher()), OptionsMock.Object); Code = codeGenerator.GenerateCode(ProgressReporterMock.Object); diff --git a/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/Yaml/NSwagCodeGeneratorFixture.cs b/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/Yaml/NSwagCodeGeneratorFixture.cs index 8ebe7b1d45..fb08fec137 100644 --- a/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/Yaml/NSwagCodeGeneratorFixture.cs +++ b/src/Core/ApiClientCodeGen.Tests.Common/Fixtures/Yaml/NSwagCodeGeneratorFixture.cs @@ -5,15 +5,15 @@ using Rapicgen.Core.Installer; using Rapicgen.Core.Options.NSwag; using Moq; +using Xunit; namespace ApiClientCodeGen.Tests.Common.Fixtures.Yaml { + [Trait("Category", "SkipWhenLiveUnitTesting")] public class NSwagCodeGeneratorFixture : TestWithResources { public readonly Mock ProgressReporterMock = new Mock(); public readonly Mock OptionsMock = new Mock(); - public readonly Mock ProcessLauncherMock = new Mock(); - public readonly Mock DependencyInstallerMock = new Mock(); public string Code; protected override void OnInitialize() @@ -29,8 +29,11 @@ protected override void OnInitialize() var codeGenerator = new NSwagCSharpCodeGenerator( Path.GetFullPath(SwaggerYamlFilename), defaultNamespace, - ProcessLauncherMock.Object, - DependencyInstallerMock.Object, + new ProcessLauncher(), + new DependencyInstaller( + new NpmInstaller(new ProcessLauncher()), + new FileDownloader(new WebDownloader()), + new ProcessLauncher()), OptionsMock.Object); Code = codeGenerator.GenerateCode(ProgressReporterMock.Object); From 9beff789557d1e334782e0007a547ab3307db1aa Mon Sep 17 00:00:00 2001 From: Christian Helle Date: Mon, 8 Dec 2025 15:03:11 +0100 Subject: [PATCH 07/12] Upgrade NSwag to v14.6.0 --- README.md | 4 ++-- .../Installer/DependencyInstallerTests.cs | 2 +- src/Core/ApiClientCodeGen.Core/ApiClientCodeGen.Core.csproj | 4 ++-- .../Generators/NSwag/NSwagCSharpCodeGenerator.cs | 2 +- .../ApiClientCodeGen.Core/Installer/DependencyInstaller.cs | 6 +++--- src/VSIX/ApiClientCodeGen.VSIX.Dev17/VSCommandTable.vsct | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 1e74117b86..8ca97e45e5 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ A collection of Visual Studio C# custom tool code generators for Swagger / OpenA Custom tools let you associate a tool with an item in a project and run that tool whenever the file is saved -- ***NSwagCodeGenerator*** - Generates a single file C# REST API Client using the [NSwag.CodeGeneration.CSharp](https://github.com/RSuter/NSwag/wiki/CSharpClientGenerator) [nuget package](https://www.nuget.org/packages/NSwag.CodeGeneration.CSharp/) **v14.4.0** +- ***NSwagCodeGenerator*** - Generates a single file C# REST API Client using the [NSwag.CodeGeneration.CSharp](https://github.com/RSuter/NSwag/wiki/CSharpClientGenerator) [nuget package](https://www.nuget.org/packages/NSwag.CodeGeneration.CSharp/) **v14.6.3** - ***OpenApiCodeGenerator*** - Generates a single file C# REST API Client using **[OpenAPI Generator v7.17.0](https://github.com/OpenAPITools/openapi-generator/releases/tag/v7.17.0)**. The output file is the result of merging all the files generated using the OpenAPI Generator tool with: @@ -398,7 +398,7 @@ Options: Commands: autorest AutoRest (v3.0.0-beta.20210504.2) kiota Microsoft Kiota (v1.29.0) - nswag NSwag (v14.4.0) + nswag NSwag (v14.6.3) openapi OpenAPI Generator (v7.17.0) refitter Refitter (v1.6.5) swagger Swagger Codegen CLI (v3.0.34) diff --git a/src/Core/ApiClientCodeGen.Core.Tests/Installer/DependencyInstallerTests.cs b/src/Core/ApiClientCodeGen.Core.Tests/Installer/DependencyInstallerTests.cs index 8fba87a1cf..5a546a7415 100644 --- a/src/Core/ApiClientCodeGen.Core.Tests/Installer/DependencyInstallerTests.cs +++ b/src/Core/ApiClientCodeGen.Core.Tests/Installer/DependencyInstallerTests.cs @@ -59,7 +59,7 @@ public void InstallNSwag_When_NSwag_Not_Installed_Invokes_ProcessLauncher( mock.Verify( c => c.Start( It.IsAny(), - "tool install --global NSwag.ConsoleCore --version 14.4.0", + "tool install --global NSwag.ConsoleCore --version 14.6.3", null)); } diff --git a/src/Core/ApiClientCodeGen.Core/ApiClientCodeGen.Core.csproj b/src/Core/ApiClientCodeGen.Core/ApiClientCodeGen.Core.csproj index e3f73e7081..2b1614b57c 100644 --- a/src/Core/ApiClientCodeGen.Core/ApiClientCodeGen.Core.csproj +++ b/src/Core/ApiClientCodeGen.Core/ApiClientCodeGen.Core.csproj @@ -18,8 +18,8 @@ - - + + diff --git a/src/Core/ApiClientCodeGen.Core/Generators/NSwag/NSwagCSharpCodeGenerator.cs b/src/Core/ApiClientCodeGen.Core/Generators/NSwag/NSwagCSharpCodeGenerator.cs index b8ebe3b5a5..b5e249e3f5 100644 --- a/src/Core/ApiClientCodeGen.Core/Generators/NSwag/NSwagCSharpCodeGenerator.cs +++ b/src/Core/ApiClientCodeGen.Core/Generators/NSwag/NSwagCSharpCodeGenerator.cs @@ -57,7 +57,7 @@ public string GenerateCode(IProgressReporter? pGenerateProgress) } pGenerateProgress?.Progress(100); - return GeneratedCode.PrefixAutogeneratedCodeHeader(generatedCode, "NSwag", "v14.4.0"); + return GeneratedCode.PrefixAutogeneratedCodeHeader(generatedCode, "NSwag", "v14.6.3"); } private string BuildNSwagArguments(string outputPath, string className) diff --git a/src/Core/ApiClientCodeGen.Core/Installer/DependencyInstaller.cs b/src/Core/ApiClientCodeGen.Core/Installer/DependencyInstaller.cs index 139ec6d70c..57ef1b1d8b 100644 --- a/src/Core/ApiClientCodeGen.Core/Installer/DependencyInstaller.cs +++ b/src/Core/ApiClientCodeGen.Core/Installer/DependencyInstaller.cs @@ -50,7 +50,7 @@ public void InstallNSwag() Logger.Instance.WriteLine(error); } }); - if (!nswagVersion.Contains("14.4.0")) + if (!nswagVersion.Contains("14.6.3")) { // Version mismatch, update to required version UpdateNSwagTool(); @@ -66,7 +66,7 @@ public void InstallNSwag() private void InstallNSwagTool() { var command = PathProvider.GetDotNetPath(); - var arguments = "tool install --global NSwag.ConsoleCore --version 14.4.0"; + var arguments = "tool install --global NSwag.ConsoleCore --version 14.6.3"; using var context = new DependencyContext(command, $"{command} {arguments}"); processLauncher.Start(command, arguments); context.Succeeded(); @@ -75,7 +75,7 @@ private void InstallNSwagTool() private void UpdateNSwagTool() { var command = PathProvider.GetDotNetPath(); - var arguments = "tool update --global NSwag.ConsoleCore --version 14.4.0"; + var arguments = "tool update --global NSwag.ConsoleCore --version 14.6.3"; using var context = new DependencyContext(command, $"{command} {arguments}"); processLauncher.Start(command, arguments); context.Succeeded(); diff --git a/src/VSIX/ApiClientCodeGen.VSIX.Dev17/VSCommandTable.vsct b/src/VSIX/ApiClientCodeGen.VSIX.Dev17/VSCommandTable.vsct index b6339746d7..c99632abd2 100644 --- a/src/VSIX/ApiClientCodeGen.VSIX.Dev17/VSCommandTable.vsct +++ b/src/VSIX/ApiClientCodeGen.VSIX.Dev17/VSCommandTable.vsct @@ -34,7 +34,7 @@