diff --git a/README.md b/README.md index cd4353eafd..575eb03511 100644 --- a/README.md +++ b/README.md @@ -50,13 +50,16 @@ This Visual Studio Extension will automatically add the required nuget packages ![NSwag Studio Context Menu](https://github.com/christianhelle/apiclientcodegen/raw/master/images/nswagstudio-context-menu.jpg) -![AutoRestCodeGenerator](https://github.com/christianhelle/apiclientcodegen/raw/master/images/autorestcodegenerator-custom-tool.jpg) -![NSwagCodeGenerator](https://github.com/christianhelle/apiclientcodegen/raw/master/images/nswagcodegenerator-custom-tool.jpg) +**Settings** -![SwaggerCodeGenerator](https://github.com/christianhelle/apiclientcodegen/raw/master/images/swaggercodegenerator-custom-tool.jpg) +This extension will by default make some assumptions on the installation paths for **Java**, **NSwag** and **NPM** but also provides option pages for configuring this -![OpenApiCodeGenerator](https://github.com/christianhelle/apiclientcodegen/raw/master/images/openapicodegenerator-custom-tool.jpg) +![Options - General](https://github.com/christianhelle/apiclientcodegen/raw/master/images/options-general.png) + +![Options - NSwag](https://github.com/christianhelle/apiclientcodegen/raw/master/images/options-nswag.png) + +![Options - NSwag Studio](https://github.com/christianhelle/apiclientcodegen/raw/master/images/options-nswagstudio.png) For tips and tricks on software development, check out [my blog](https://christian-helle.blogspot.com) diff --git a/images/options-general.png b/images/options-general.png new file mode 100644 index 0000000000..c0187073c4 Binary files /dev/null and b/images/options-general.png differ diff --git a/images/options-nswag.png b/images/options-nswag.png new file mode 100644 index 0000000000..82272e1491 Binary files /dev/null and b/images/options-nswag.png differ diff --git a/images/options-nswagstudio.png b/images/options-nswagstudio.png new file mode 100644 index 0000000000..89b7010f52 Binary files /dev/null and b/images/options-nswagstudio.png differ diff --git a/src/ApiClientCodeGen.Tests/ApiClientCodeGen.Tests.csproj b/src/ApiClientCodeGen.Tests/ApiClientCodeGen.Tests.csproj index d219d2d54e..e6a1457f00 100644 --- a/src/ApiClientCodeGen.Tests/ApiClientCodeGen.Tests.csproj +++ b/src/ApiClientCodeGen.Tests/ApiClientCodeGen.Tests.csproj @@ -39,20 +39,64 @@ 4 + + ..\packages\AutoFixture.4.9.0\lib\net452\AutoFixture.dll + + + ..\packages\Castle.Core.4.4.0\lib\net45\Castle.Core.dll + + + ..\packages\DotLiquid.2.0.254\lib\net45\DotLiquid.dll + + + ..\packages\Fare.2.1.1\lib\net35\Fare.dll + ..\packages\FluentAssertions.5.6.0\lib\net45\FluentAssertions.dll + + + ..\packages\Microsoft.VisualStudio.OLE.Interop.7.10.6071\lib\Microsoft.VisualStudio.OLE.Interop.dll + + + ..\packages\Microsoft.VisualStudio.Shell.Interop.7.10.6072\lib\net11\Microsoft.VisualStudio.Shell.Interop.dll + ..\packages\MSTest.TestFramework.1.4.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll ..\packages\MSTest.TestFramework.1.4.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll + + ..\packages\Microsoft.VisualStudio.TextManager.Interop.7.10.6071\lib\net11\Microsoft.VisualStudio.TextManager.Interop.dll + + + ..\packages\Moq.4.11.0\lib\net45\Moq.dll + ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll + + ..\packages\NJsonSchema.9.13.37\lib\net45\NJsonSchema.dll + + + ..\packages\NJsonSchema.CodeGeneration.9.13.37\lib\net451\NJsonSchema.CodeGeneration.dll + + + ..\packages\NJsonSchema.CodeGeneration.CSharp.9.13.37\lib\net451\NJsonSchema.CodeGeneration.CSharp.dll + + + + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll + + + + ..\packages\System.Threading.Tasks.Extensions.4.5.1\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll + ..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll @@ -60,12 +104,26 @@ - - + + + + + + + + + + + + + + + + diff --git a/src/ApiClientCodeGen.Tests/CustomTool/SingleFileCodeGeneratorTests.cs b/src/ApiClientCodeGen.Tests/CustomTool/SingleFileCodeGeneratorTests.cs new file mode 100644 index 0000000000..b17c63a604 --- /dev/null +++ b/src/ApiClientCodeGen.Tests/CustomTool/SingleFileCodeGeneratorTests.cs @@ -0,0 +1,63 @@ +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Core; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Generators; +using FluentAssertions; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System; + +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Tests.CustomTool +{ + [TestClass] + public class SingleFileCodeGeneratorTests + { + private const SupportedLanguage lang = SupportedLanguage.CSharp; + + [DataTestMethod] + [DataRow(SupportedCodeGenerator.AutoRest)] + [DataRow(SupportedCodeGenerator.NSwag)] + [DataRow(SupportedCodeGenerator.Swagger)] + [DataRow(SupportedCodeGenerator.OpenApi)] + public void Generate_Test(SupportedCodeGenerator generator) + { + var code = Test.CreateAnnonymous(); + var input = Test.CreateAnnonymous(); + var contents = Test.CreateAnnonymous(); + var @namespace = Test.CreateAnnonymous(); + var rgbOutputFileContents = new[] { IntPtr.Zero }; + + var progressMock = new Mock(); + + var generatorMock = new Mock(); + generatorMock + .Setup(c => c.GenerateCode(progressMock.Object)) + .Returns(code); + + var factoryMock = new Mock(); + factoryMock + .Setup(c => c.Create(@namespace, contents, input, lang, generator)) + .Returns(generatorMock.Object); + + var sut = new TestSingleFileCodeGenerator(generator) + { + Factory = factoryMock.Object + }; + + var result = sut.Generate( + input, + contents, + @namespace, + rgbOutputFileContents, + out var pcbOutput, + progressMock.Object); + + result.Should().Be(0); + pcbOutput.Should().Be((uint)code.Length); + rgbOutputFileContents[0].Should().NotBe(IntPtr.Zero); + + progressMock.Verify( + c => c.Progress(It.IsAny(), It.IsAny()), + Times.Exactly(2)); + } + } +} diff --git a/src/ApiClientCodeGen.Tests/CustomTool/TestSingleFileCodeGenerator.cs b/src/ApiClientCodeGen.Tests/CustomTool/TestSingleFileCodeGenerator.cs new file mode 100644 index 0000000000..6777370d76 --- /dev/null +++ b/src/ApiClientCodeGen.Tests/CustomTool/TestSingleFileCodeGenerator.cs @@ -0,0 +1,20 @@ +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Core; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.CustomTool; + +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Tests.CustomTool +{ + internal class TestSingleFileCodeGenerator : SingleFileCodeGenerator + { + internal TestSingleFileCodeGenerator( + SupportedCodeGenerator supportedCodeGenerator) + : base(supportedCodeGenerator, SupportedLanguage.CSharp) + { + } + + public override int DefaultExtension(out string pbstrDefaultExtension) + { + pbstrDefaultExtension = ".cs"; + return 0; + } + } +} diff --git a/src/ApiClientCodeGen.Tests/Extensions/GetCustomToolNameTests.cs b/src/ApiClientCodeGen.Tests/Extensions/GetCustomToolNameTests.cs new file mode 100644 index 0000000000..a2318c088f --- /dev/null +++ b/src/ApiClientCodeGen.Tests/Extensions/GetCustomToolNameTests.cs @@ -0,0 +1,39 @@ +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Core; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Extensions; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Tests.Extensions +{ + [TestClass] + public class GetCustomToolNameTests + { + [TestMethod] + public void GetCustomToolName_AutoRest() + => SupportedCodeGenerator.AutoRest + .GetCustomToolName() + .Should() + .Contain("AutoRest"); + + [TestMethod] + public void GetCustomToolName_NSwag() + => SupportedCodeGenerator.NSwag + .GetCustomToolName() + .Should() + .Contain("NSwag"); + + [TestMethod] + public void GetCustomToolName_Swagger() + => SupportedCodeGenerator.Swagger + .GetCustomToolName() + .Should() + .Contain("Swagger"); + + [TestMethod] + public void GetCustomToolName_OpenApi() + => SupportedCodeGenerator.OpenApi + .GetCustomToolName() + .Should() + .Contain("OpenApi"); + } +} diff --git a/src/ApiClientCodeGen.Tests/Extensions/GetDependenciesTests.cs b/src/ApiClientCodeGen.Tests/Extensions/GetDependenciesTests.cs new file mode 100644 index 0000000000..0bc934eb58 --- /dev/null +++ b/src/ApiClientCodeGen.Tests/Extensions/GetDependenciesTests.cs @@ -0,0 +1,44 @@ +using System.Linq; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Core; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Extensions; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Tests.Extensions +{ + [TestClass] + public class GetDependenciesTests + { + [TestMethod] + public void GetDependencies_AutoRest() + => SupportedCodeGenerator.AutoRest + .GetDependencies() + .Any(c => c.Name == "Microsoft.Rest.ClientRuntime") + .Should() + .BeTrue(); + + [TestMethod] + public void GetDependencies_NSwag() + => SupportedCodeGenerator.NSwag + .GetDependencies() + .Any(c => c.Name == "Newtonsoft.Json") + .Should() + .BeTrue(); + + [TestMethod] + public void GetDependencies_Swagger() + => SupportedCodeGenerator.Swagger + .GetDependencies() + .Any(c => c.Name == "RestSharp" || c.Name == "JsonSubTypes") + .Should() + .BeTrue(); + + [TestMethod] + public void GetDependencies_OpenApi() + => SupportedCodeGenerator.OpenApi + .GetDependencies() + .Any(c => c.Name == "RestSharp" || c.Name == "JsonSubTypes") + .Should() + .BeTrue(); + } +} diff --git a/src/ApiClientCodeGen.Tests/Extensions/GetSupportedCodeGeneratorTests.cs b/src/ApiClientCodeGen.Tests/Extensions/GetSupportedCodeGeneratorTests.cs new file mode 100644 index 0000000000..6b9f139c8e --- /dev/null +++ b/src/ApiClientCodeGen.Tests/Extensions/GetSupportedCodeGeneratorTests.cs @@ -0,0 +1,43 @@ +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Core; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.CustomTool.AutoRest; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.CustomTool.NSwag; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.CustomTool.OpenApi; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.CustomTool.Swagger; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Extensions; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Tests.Extensions +{ + [TestClass] + public class GetSupportedCodeGeneratorTests + { + [TestMethod] + public void GetSupportedCodeGenerator_AutoRest() + => typeof(AutoRestCodeGenerator) + .GetSupportedCodeGenerator() + .Should() + .Be(SupportedCodeGenerator.AutoRest); + + [TestMethod] + public void GetSupportedCodeGenerator_NSwag() + => typeof(NSwagCodeGenerator) + .GetSupportedCodeGenerator() + .Should() + .Be(SupportedCodeGenerator.NSwag); + + [TestMethod] + public void GetSupportedCodeGenerator_Swagger() + => typeof(SwaggerCodeGenerator) + .GetSupportedCodeGenerator() + .Should() + .Be(SupportedCodeGenerator.Swagger); + + [TestMethod] + public void GetSupportedCodeGenerator_OpenApi() + => typeof(OpenApiCodeGenerator) + .GetSupportedCodeGenerator() + .Should() + .Be(SupportedCodeGenerator.OpenApi); + } +} \ No newline at end of file diff --git a/src/ApiClientCodeGen.Tests/Extensions/StringToIntPtrTests.cs b/src/ApiClientCodeGen.Tests/Extensions/StringToIntPtrTests.cs new file mode 100644 index 0000000000..5cd60e4edc --- /dev/null +++ b/src/ApiClientCodeGen.Tests/Extensions/StringToIntPtrTests.cs @@ -0,0 +1,34 @@ +using System; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Extensions; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Tests.Extensions +{ + [TestClass] + public class StringToIntPtrTests + { + private string str; + private IntPtr result; + private uint length; + + [TestInitialize] + public void Init() + { + str = Test.CreateAnnonymous(); + result = str.ConvertToIntPtr(out length); + } + + [TestMethod] + public void IntPtr_Not_Zero() + => result.Should().NotBe(IntPtr.Zero); + + [TestMethod] + public void Length_Not_Zero() + => length.Should().BeGreaterThan(0); + + [TestMethod] + public void Length_Matches_String_Length() + => length.Should().Be((uint)str.Length); + } +} diff --git a/src/ApiClientCodeGen.Tests/Extensions/ToJsonTests.cs b/src/ApiClientCodeGen.Tests/Extensions/ToJsonTests.cs new file mode 100644 index 0000000000..56fdf1bf62 --- /dev/null +++ b/src/ApiClientCodeGen.Tests/Extensions/ToJsonTests.cs @@ -0,0 +1,35 @@ +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Extensions; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Tests.Extensions +{ + [TestClass] + public class ToJsonTests + { + string json; + [TestInitialize] + public void Init() + { + json = new + { + Str1 = Test.CreateAnnonymous(), + Str2 = Test.CreateAnnonymous(), + Str3 = Test.CreateAnnonymous(), + Null = (object)null + }.ToJson(); + } + + [TestMethod] + public void NotNull() + => json.Should().NotBeNullOrWhiteSpace(); + + [TestMethod] + public void Is_CamelCase() + => json.Should().NotContain("Str").And.Contain("str"); + + [TestMethod] + public void Ignores_Null_Values() + => json.Should().NotContain("null"); + } +} \ No newline at end of file diff --git a/src/ApiClientCodeGen.Tests/CSharpFileMergerTests.cs b/src/ApiClientCodeGen.Tests/Generators/CSharpFileMergerTests.cs similarity index 97% rename from src/ApiClientCodeGen.Tests/CSharpFileMergerTests.cs rename to src/ApiClientCodeGen.Tests/Generators/CSharpFileMergerTests.cs index 960e9f50f3..cc07d2385b 100644 --- a/src/ApiClientCodeGen.Tests/CSharpFileMergerTests.cs +++ b/src/ApiClientCodeGen.Tests/Generators/CSharpFileMergerTests.cs @@ -3,7 +3,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Tests +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Tests.Generators { [TestClass] public class CSharpFileMergerTests diff --git a/src/ApiClientCodeGen.Tests/CodeGeneratorFactoryTests.cs b/src/ApiClientCodeGen.Tests/Generators/CodeGeneratorFactoryTests.cs similarity index 59% rename from src/ApiClientCodeGen.Tests/CodeGeneratorFactoryTests.cs rename to src/ApiClientCodeGen.Tests/Generators/CodeGeneratorFactoryTests.cs index a48d0f7001..a1d9b2ffef 100644 --- a/src/ApiClientCodeGen.Tests/CodeGeneratorFactoryTests.cs +++ b/src/ApiClientCodeGen.Tests/Generators/CodeGeneratorFactoryTests.cs @@ -1,17 +1,34 @@ using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Core; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Generators; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Generators.AutoRest; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Generators.NSwag; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Generators.OpenApi; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Generators.Swagger; -using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Generators; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; -namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Tests +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Tests.Generators { [TestClass] public class CodeGeneratorFactoryTests { - private readonly CodeGeneratorFactory sut = new CodeGeneratorFactory(); + private CodeGeneratorFactory sut; + + [TestInitialize] + public void Init() + { + var mockFactory = new Mock(); + mockFactory + .Setup(c => c.Create()) + .Returns(Test.CreateDummy()); + mockFactory + .Setup(c => c.Create()) + .Returns(Test.CreateDummy()); + + sut = new CodeGeneratorFactory(mockFactory.Object); + } [TestMethod] public void Can_Create_NSwagCodeGenerator() @@ -45,5 +62,16 @@ public void Can_Create_SwaggerCodeGenerator() SupportedCodeGenerator.Swagger) .Should() .BeOfType(); + + [TestMethod] + public void Can_Create_OpenApiCodeGenerator() + => sut.Create( + string.Empty, + string.Empty, + string.Empty, + SupportedLanguage.CSharp, + SupportedCodeGenerator.OpenApi) + .Should() + .BeOfType(); } } \ No newline at end of file diff --git a/src/ApiClientCodeGen.Tests/Generators/CodeGeneratorTests.cs b/src/ApiClientCodeGen.Tests/Generators/CodeGeneratorTests.cs new file mode 100644 index 0000000000..1a0c968bca --- /dev/null +++ b/src/ApiClientCodeGen.Tests/Generators/CodeGeneratorTests.cs @@ -0,0 +1,76 @@ +using AutoFixture; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Generators; +using FluentAssertions; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; + +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Tests.Generators +{ + [TestClass] + [DeploymentItem("Resources/Swagger.json")] + public class CodeGeneratorTests + { + private Mock mock; + private CodeGenerator sut; + + [TestInitialize] + public void Init() + { + mock = new Mock(); + + var fixture = new Fixture(); + sut = new TestCodeGenerator( + "Swagger.json", + fixture.Create()); + + try + { + sut.GenerateCode(mock.Object); + } + catch + { + // ignored + } + } + + [TestMethod] + public void GenerateCode_ReportsProgress() + => mock.Verify( + c => c.Progress( + It.IsAny(), + It.IsAny())); + + [TestMethod] + public void GetsCommand() + => ((TestCodeGenerator) sut).GetCommandCalled.Should().BeGreaterThan(0); + + [TestMethod] + public void GetsArgument() + => ((TestCodeGenerator) sut).GetArgumentsCalled.Should().BeGreaterThan(0); + + internal class TestCodeGenerator : CodeGenerator + { + public TestCodeGenerator(string swaggerFile, string defaultNamespace) + : base(swaggerFile, defaultNamespace) + { + } + + protected override string GetArguments(string outputFile) + { + GetArgumentsCalled++; + return string.Empty; + } + + protected override string GetCommand() + { + GetCommandCalled++; + return string.Empty; + } + + public int GetArgumentsCalled { get; private set; } + + public int GetCommandCalled { get; private set; } + } + } +} diff --git a/src/ApiClientCodeGen.Tests/Generators/NSwagStudio/NSwagStudioFileHelperTests.cs b/src/ApiClientCodeGen.Tests/Generators/NSwagStudio/NSwagStudioFileHelperTests.cs new file mode 100644 index 0000000000..3cd3ac2a32 --- /dev/null +++ b/src/ApiClientCodeGen.Tests/Generators/NSwagStudio/NSwagStudioFileHelperTests.cs @@ -0,0 +1,68 @@ +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Generators.NSwagStudio; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System.IO; +using System.Threading.Tasks; + +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Tests.Generators.NSwagStudio +{ + [TestClass] + [DeploymentItem("Resources/Swagger.nswag")] + [DeploymentItem("Resources/Swagger.json")] + public class NSwagStudioFileHelperTests + { + private Mock mock; + + [TestInitialize] + public async Task Init() + { + mock = new Mock(); + + await NSwagStudioFileHelper.CreateNSwagStudioFileAsync( + File.ReadAllText("Swagger.json"), + "https://petstore.swagger.io/v2/swagger.json", + mock.Object); + } + + [TestMethod] + public void Reads_InjectHttpClient_From_Options() + => mock.Verify(c => c.InjectHttpClient); + + [TestMethod] + public void Reads_GenerateClientInterfaces_From_Options() + => mock.Verify(c => c.GenerateClientInterfaces); + + [TestMethod] + public void Reads_GenerateDtoTypes_From_Options() + => mock.Verify(c => c.GenerateDtoTypes); + + [TestMethod] + public void Reads_UseBaseUrl_From_Options() + => mock.Verify(c => c.UseBaseUrl); + + [TestMethod] + public void Reads_ClassStyle_From_Options() + => mock.Verify(c => c.ClassStyle); + + [TestMethod] + public void Reads_GenerateResponseClasses_From_Options() + => mock.Verify(c => c.GenerateResponseClasses); + + [TestMethod] + public void Reads_GenerateJsonMethods_From_Options() + => mock.Verify(c => c.GenerateJsonMethods); + + [TestMethod] + public void Reads_RequiredPropertiesMustBeDefined_From_Options() + => mock.Verify(c => c.RequiredPropertiesMustBeDefined); + + [TestMethod] + public void Reads_GenerateDefaultValues_From_Options() + => mock.Verify(c => c.GenerateDefaultValues); + + [TestMethod] + public void Reads_GenerateDataAnnotations_From_Options() + => mock.Verify(c => c.GenerateDataAnnotations); + } +} diff --git a/src/ApiClientCodeGen.Tests/Options/JavaPathProviderTests.cs b/src/ApiClientCodeGen.Tests/Options/JavaPathProviderTests.cs new file mode 100644 index 0000000000..9653d1146c --- /dev/null +++ b/src/ApiClientCodeGen.Tests/Options/JavaPathProviderTests.cs @@ -0,0 +1,34 @@ +using AutoFixture; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; + +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Tests.Options +{ + [TestClass] + public class JavaPathProviderTests + { + private Mock mock; + private string result; + + [TestInitialize] + public void Init() + { + mock = new Mock(); + mock.Setup(c => c.JavaPath) + .Returns(Test.CreateAnnonymous()); + + result = new JavaPathProvider(mock.Object) + .GetJavaExePath(); + } + + [TestMethod] + public void GetJavaExePath_Should_NotBeNull() + => result.Should().NotBeNullOrWhiteSpace(); + + [TestMethod] + public void GetJavaExePath_Should_Read_JavaPath_Option() + => mock.Verify(c => c.JavaPath); + } +} diff --git a/src/ApiClientCodeGen.Tests/Options/NSwagCSharpOptionsTests.cs b/src/ApiClientCodeGen.Tests/Options/NSwagCSharpOptionsTests.cs new file mode 100644 index 0000000000..80e5d342f7 --- /dev/null +++ b/src/ApiClientCodeGen.Tests/Options/NSwagCSharpOptionsTests.cs @@ -0,0 +1,57 @@ +using AutoFixture; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; + +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Tests.Options +{ + [TestClass] + public class NSwagCSharpOptionsTests + { + private INSwagOptions options; + + [TestInitialize] + public void Init() + { + var fixture = new Fixture(); + var mock = new Mock(); + options = mock.Object; + } + + [TestMethod] + public void Reads_InjectHttpClient_From_Options() + => new NSwagCSharpOptions(options) + .InjectHttpClient + .Should() + .Be(options.InjectHttpClient); + + [TestMethod] + public void Reads_GenerateClientInterfaces_From_Options() + => new NSwagCSharpOptions(options) + .GenerateClientInterfaces + .Should() + .Be(options.GenerateClientInterfaces); + + [TestMethod] + public void Reads_GenerateDtoTypes_From_Options() + => new NSwagCSharpOptions(options) + .GenerateDtoTypes + .Should() + .Be(options.GenerateDtoTypes); + + [TestMethod] + public void Reads_UseBaseUrl_From_Options() + => new NSwagCSharpOptions(options) + .UseBaseUrl + .Should() + .Be(options.UseBaseUrl); + + [TestMethod] + public void Reads_ClassStyle_From_Options() + => new NSwagCSharpOptions(options) + .ClassStyle + .Should() + .Be(options.ClassStyle); + } +} diff --git a/src/ApiClientCodeGen.Tests/Options/NSwagStudioOptionsTests.cs b/src/ApiClientCodeGen.Tests/Options/NSwagStudioOptionsTests.cs new file mode 100644 index 0000000000..523fbe008d --- /dev/null +++ b/src/ApiClientCodeGen.Tests/Options/NSwagStudioOptionsTests.cs @@ -0,0 +1,97 @@ +using AutoFixture; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; + +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Tests.Options +{ + [TestClass] + public class NSwagStudioOptionsTests + { + private INSwagStudioOptions options; + + [TestInitialize] + public void Init() + { + var mock = new Mock(); + options = mock.Object; + } + + [TestMethod] + public void Extends_NSwagCSharpOptions() + => typeof(NSwagStudioOptions) + .Should() + .BeAssignableTo(); + + [TestMethod] + public void Reads_InjectHttpClient_From_Options() + => new NSwagStudioOptions(options) + .InjectHttpClient + .Should() + .Be(options.InjectHttpClient); + + [TestMethod] + public void Reads_GenerateClientInterfaces_From_Options() + => new NSwagStudioOptions(options) + .GenerateClientInterfaces + .Should() + .Be(options.GenerateClientInterfaces); + + [TestMethod] + public void Reads_GenerateDtoTypes_From_Options() + => new NSwagStudioOptions(options) + .GenerateDtoTypes + .Should() + .Be(options.GenerateDtoTypes); + + [TestMethod] + public void Reads_UseBaseUrl_From_Options() + => new NSwagStudioOptions(options) + .UseBaseUrl + .Should() + .Be(options.UseBaseUrl); + + [TestMethod] + public void Reads_ClassStyle_From_Options() + => new NSwagStudioOptions(options) + .ClassStyle + .Should() + .Be(options.ClassStyle); + + [TestMethod] + public void Reads_GenerateResponseClasses_From_Options() + => new NSwagStudioOptions(options) + .GenerateResponseClasses + .Should() + .Be(options.GenerateResponseClasses); + + [TestMethod] + public void Reads_GenerateJsonMethods_From_Options() + => new NSwagStudioOptions(options) + .GenerateJsonMethods + .Should() + .Be(options.GenerateJsonMethods); + + [TestMethod] + public void Reads_RequiredPropertiesMustBeDefined_From_Options() + => new NSwagStudioOptions(options) + .RequiredPropertiesMustBeDefined + .Should() + .Be(options.RequiredPropertiesMustBeDefined); + + [TestMethod] + public void Reads_GenerateDefaultValues_From_Options() + => new NSwagStudioOptions(options) + .GenerateDefaultValues + .Should() + .Be(options.GenerateDefaultValues); + + [TestMethod] + public void Reads_GenerateDataAnnotations_From_Options() + => new NSwagStudioOptions(options) + .GenerateDataAnnotations + .Should() + .Be(options.GenerateDataAnnotations); + } +} diff --git a/src/ApiClientCodeGen.Tests/Options/PathProviderTests.cs b/src/ApiClientCodeGen.Tests/Options/PathProviderTests.cs new file mode 100644 index 0000000000..e45280e000 --- /dev/null +++ b/src/ApiClientCodeGen.Tests/Options/PathProviderTests.cs @@ -0,0 +1,35 @@ +using System.IO; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Tests.Options +{ + [TestClass] + public class PathProviderTests + { + [TestMethod] + public void GetJavaPath_Exists() + { + var path = PathProvider.GetJavaPath(); + path.Should().NotBeNullOrWhiteSpace(); + File.Exists(path).Should().BeTrue(); + } + + [TestMethod] + public void GetNpmPath_Exists() + { + var path = PathProvider.GetNpmPath(); + path.Should().NotBeNullOrWhiteSpace(); + File.Exists(path).Should().BeTrue(); + } + + [TestMethod] + public void GetNSwagPath_Exists() + { + var path = PathProvider.GetNSwagPath(); + path.Should().NotBeNullOrWhiteSpace(); + File.Exists(path).Should().BeTrue(); + } + } +} diff --git a/src/ApiClientCodeGen.Tests/TestClassBase.cs b/src/ApiClientCodeGen.Tests/TestClassBase.cs new file mode 100644 index 0000000000..92cf401ea5 --- /dev/null +++ b/src/ApiClientCodeGen.Tests/TestClassBase.cs @@ -0,0 +1,12 @@ +using AutoFixture; +using Moq; + +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Tests +{ + public static class Test + { + public static T CreateDummy() where T : class => new Mock().Object; + + public static T CreateAnnonymous() where T : class => new Fixture().Create(); + } +} diff --git a/src/ApiClientCodeGen.Tests/packages.config b/src/ApiClientCodeGen.Tests/packages.config index 3e728d7b9b..1b05afc518 100644 --- a/src/ApiClientCodeGen.Tests/packages.config +++ b/src/ApiClientCodeGen.Tests/packages.config @@ -1,7 +1,21 @@  + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/ApiClientCodeGen.VSIX/ApiClientCodeGen.VSIX.csproj b/src/ApiClientCodeGen.VSIX/ApiClientCodeGen.VSIX.csproj index c5ab32208d..9ed2969f07 100644 --- a/src/ApiClientCodeGen.VSIX/ApiClientCodeGen.VSIX.csproj +++ b/src/ApiClientCodeGen.VSIX/ApiClientCodeGen.VSIX.csproj @@ -95,12 +95,31 @@ + + + + + + + + + + Component + + + Component + + + + Component + + @@ -113,6 +132,12 @@ + + UserControl + + + GeneralOptionsPageCustom.cs + @@ -327,6 +352,9 @@ true VSPackage + + GeneralOptionsPageCustom.cs + diff --git a/src/ApiClientCodeGen.VSIX/Commands/AddNew/NewAutoRestClientCommand.cs b/src/ApiClientCodeGen.VSIX/Commands/AddNew/NewAutoRestClientCommand.cs index fbccd36dad..743063380d 100644 --- a/src/ApiClientCodeGen.VSIX/Commands/AddNew/NewAutoRestClientCommand.cs +++ b/src/ApiClientCodeGen.VSIX/Commands/AddNew/NewAutoRestClientCommand.cs @@ -1,7 +1,9 @@ using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Core; +using System.Diagnostics.CodeAnalysis; namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Commands.AddNew { + [ExcludeFromCodeCoverage] public class NewAutoRestClientCommand : NewRestClientCommand { protected override int CommandId { get; } = 0x0200; diff --git a/src/ApiClientCodeGen.VSIX/Commands/AddNew/NewNSwagClientCommand.cs b/src/ApiClientCodeGen.VSIX/Commands/AddNew/NewNSwagClientCommand.cs index 4bec4b5fb0..4c5b2d62f4 100644 --- a/src/ApiClientCodeGen.VSIX/Commands/AddNew/NewNSwagClientCommand.cs +++ b/src/ApiClientCodeGen.VSIX/Commands/AddNew/NewNSwagClientCommand.cs @@ -1,7 +1,9 @@ using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Core; +using System.Diagnostics.CodeAnalysis; namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Commands.AddNew { + [ExcludeFromCodeCoverage] public class NewNSwagClientCommand : NewRestClientCommand { protected override int CommandId { get; } = 0x0300; diff --git a/src/ApiClientCodeGen.VSIX/Commands/AddNew/NewNSwagStudioClientCommand.cs b/src/ApiClientCodeGen.VSIX/Commands/AddNew/NewNSwagStudioClientCommand.cs index 2c805518de..cb794395ac 100644 --- a/src/ApiClientCodeGen.VSIX/Commands/AddNew/NewNSwagStudioClientCommand.cs +++ b/src/ApiClientCodeGen.VSIX/Commands/AddNew/NewNSwagStudioClientCommand.cs @@ -1,7 +1,9 @@ using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Core; +using System.Diagnostics.CodeAnalysis; namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Commands.AddNew { + [ExcludeFromCodeCoverage] public class NewNSwagStudioClientCommand : NewRestClientCommand { protected override int CommandId { get; } = 0x0600; diff --git a/src/ApiClientCodeGen.VSIX/Commands/AddNew/NewOpenApiClientCommand.cs b/src/ApiClientCodeGen.VSIX/Commands/AddNew/NewOpenApiClientCommand.cs index 99a282f70b..cb6dbcb76e 100644 --- a/src/ApiClientCodeGen.VSIX/Commands/AddNew/NewOpenApiClientCommand.cs +++ b/src/ApiClientCodeGen.VSIX/Commands/AddNew/NewOpenApiClientCommand.cs @@ -1,7 +1,9 @@ using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Core; +using System.Diagnostics.CodeAnalysis; namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Commands.AddNew { + [ExcludeFromCodeCoverage] public class NewOpenApiClientCommand : NewRestClientCommand { protected override int CommandId { get; } = 0x0500; diff --git a/src/ApiClientCodeGen.VSIX/Commands/AddNew/NewRestClientCommand.cs b/src/ApiClientCodeGen.VSIX/Commands/AddNew/NewRestClientCommand.cs index 17156d1637..7215e5f424 100644 --- a/src/ApiClientCodeGen.VSIX/Commands/AddNew/NewRestClientCommand.cs +++ b/src/ApiClientCodeGen.VSIX/Commands/AddNew/NewRestClientCommand.cs @@ -1,10 +1,12 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Threading; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Core; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Extensions; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Generators.NSwagStudio; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Windows; using EnvDTE; using Microsoft.VisualStudio.Shell; @@ -14,6 +16,7 @@ namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Commands.AddNew { + [ExcludeFromCodeCoverage] public abstract class NewRestClientCommand : ICommandInitializer { protected Guid CommandSet { get; } = new Guid("E4B99F94-D11F-4CAA-ADCD-24302C232938"); @@ -47,6 +50,7 @@ private async Task OnExecuteAsync(DTE dte, AsyncPackage package) contents = await NSwagStudioFileHelper.CreateNSwagStudioFileAsync( result.OpenApiSpecification, result.Url, + new NSwagStudioOptions(), outputNamespace); filename = filename.Replace(".json", ".nswag"); } @@ -66,7 +70,7 @@ private async Task OnExecuteAsync(DTE dte, AsyncPackage package) } else { - var generator = new NSwagStudioCodeGenerator(filePath); + var generator = new NSwagStudioCodeGenerator(filePath, new CustomPathOptions()); generator.GenerateCode(null); dynamic nswag = JsonConvert.DeserializeObject(contents); var nswagOutput = nswag.codeGenerators.swaggerToCSharpClient.output.ToString(); diff --git a/src/ApiClientCodeGen.VSIX/Commands/AddNew/NewSwaggerClientCommand.cs b/src/ApiClientCodeGen.VSIX/Commands/AddNew/NewSwaggerClientCommand.cs index 8d047e725d..e2eb7d11f8 100644 --- a/src/ApiClientCodeGen.VSIX/Commands/AddNew/NewSwaggerClientCommand.cs +++ b/src/ApiClientCodeGen.VSIX/Commands/AddNew/NewSwaggerClientCommand.cs @@ -1,7 +1,9 @@ using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Core; +using System.Diagnostics.CodeAnalysis; namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Commands.AddNew { + [ExcludeFromCodeCoverage] public class NewSwaggerClientCommand : NewRestClientCommand { protected override int CommandId { get; } = 0x0400; diff --git a/src/ApiClientCodeGen.VSIX/Commands/NSwagStudio/NSwagStudioCommand.cs b/src/ApiClientCodeGen.VSIX/Commands/NSwagStudio/NSwagStudioCommand.cs index d68f0638cd..456b6209e5 100644 --- a/src/ApiClientCodeGen.VSIX/Commands/NSwagStudio/NSwagStudioCommand.cs +++ b/src/ApiClientCodeGen.VSIX/Commands/NSwagStudio/NSwagStudioCommand.cs @@ -3,6 +3,7 @@ using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Core; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Extensions; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Generators.NSwagStudio; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options; using EnvDTE; using Microsoft.VisualStudio.Shell; using Task = System.Threading.Tasks.Task; @@ -30,7 +31,7 @@ private static async Task OnExecute(DTE dte, AsyncPackage package) { var item = dte.SelectedItems.Item(1).ProjectItem; var nswagStudioFile = item.FileNames[0]; - var codeGenerator = new NSwagStudioCodeGenerator(nswagStudioFile); + var codeGenerator = new NSwagStudioCodeGenerator(nswagStudioFile, new CustomPathOptions()); codeGenerator.GenerateCode(null); var project = ProjectExtensions.GetActiveProject(dte); diff --git a/src/ApiClientCodeGen.VSIX/Core/DependencyDownloader.cs b/src/ApiClientCodeGen.VSIX/Core/DependencyDownloader.cs index 3bc0cc8898..38d18198a5 100644 --- a/src/ApiClientCodeGen.VSIX/Core/DependencyDownloader.cs +++ b/src/ApiClientCodeGen.VSIX/Core/DependencyDownloader.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using System.IO; +using System.Net; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Generators; namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Core @@ -25,5 +26,38 @@ public static void InstallAutoRest() ProcessHelper.StartProcess(npmCommand, "install -g autorest"); Trace.WriteLine("AutoRest installed successfully through NPM"); } + + public static string InstallOpenApiGenerator(string path = null) + { + const string md5 = "61574C43BEC9B6EDD54E2DD0993F81D5"; + const string url = "http://central.maven.org/maven2/org/openapitools/openapi-generator-cli/4.0.0/openapi-generator-cli-4.0.0.jar"; + const string jar = "openapi-generator-cli.jar"; + + return InstallJarFile(path, jar, md5, url); + } + + public static string InstallSwaggerCodegenCli(string path = null) + { + const string md5 = "219F1453FF22482D9E080EFFBFA7FA81"; + const string url = "https://repo1.maven.org/maven2/io/swagger/swagger-codegen-cli/2.4.5/swagger-codegen-cli-2.4.5.jar"; + const string jar = "swagger-codegen-cli.jar"; + + return InstallJarFile(path, jar, md5, url); + } + + private static string InstallJarFile(string path, string jar, string md5, string url) + { + if (string.IsNullOrWhiteSpace(path)) + path = Path.Combine(Path.GetTempPath(), jar); + + if (!File.Exists(path) || FileHelper.CalculateMd5(path) != md5) + { + Trace.WriteLine($"{jar} not found. Attempting to download {jar}"); + new WebClient().DownloadFile(url, path); + Trace.WriteLine($"{jar} downloaded successfully"); + } + + return path; + } } } \ No newline at end of file diff --git a/src/ApiClientCodeGen.VSIX/CustomTool/SingleFileCodeGenerator.cs b/src/ApiClientCodeGen.VSIX/CustomTool/SingleFileCodeGenerator.cs index 3ab51581c0..c76a22ec7e 100644 --- a/src/ApiClientCodeGen.VSIX/CustomTool/SingleFileCodeGenerator.cs +++ b/src/ApiClientCodeGen.VSIX/CustomTool/SingleFileCodeGenerator.cs @@ -27,6 +27,8 @@ protected SingleFileCodeGenerator( public abstract int DefaultExtension(out string pbstrDefaultExtension); + public ICodeGeneratorFactory Factory { get; set; } = new CodeGeneratorFactory(); + public int Generate( string wszInputFilePath, string bstrInputFileContents, @@ -39,8 +41,7 @@ public int Generate( { pGenerateProgress.Progress(5); - var factory = new CodeGeneratorFactory(); - var codeGenerator = factory.Create( + var codeGenerator = Factory.Create( wszDefaultNamespace, bstrInputFileContents, wszInputFilePath, @@ -60,7 +61,7 @@ public int Generate( catch (Exception e) { pGenerateProgress.GeneratorError(e); - MessageBox.Show(e.Message, "Unable to generate code"); + Trace.WriteLine("Unable to generate code"); Trace.WriteLine(e); throw; } diff --git a/src/ApiClientCodeGen.VSIX/Extensions/AsyncPackageExtensions.cs b/src/ApiClientCodeGen.VSIX/Extensions/AsyncPackageExtensions.cs index 2e831cb181..3540c43120 100644 --- a/src/ApiClientCodeGen.VSIX/Extensions/AsyncPackageExtensions.cs +++ b/src/ApiClientCodeGen.VSIX/Extensions/AsyncPackageExtensions.cs @@ -1,6 +1,7 @@ using System; using System.ComponentModel.Design; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Threading; using EnvDTE; using Microsoft.VisualStudio.Shell; @@ -8,6 +9,7 @@ namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Extensions { + [ExcludeFromCodeCoverage] public static class AsyncPackageExtensions { public static async Task SetupCommandAsync( diff --git a/src/ApiClientCodeGen.VSIX/Extensions/IVsExtensions.cs b/src/ApiClientCodeGen.VSIX/Extensions/IVsExtensions.cs index cdfebe86b3..5339f48c81 100644 --- a/src/ApiClientCodeGen.VSIX/Extensions/IVsExtensions.cs +++ b/src/ApiClientCodeGen.VSIX/Extensions/IVsExtensions.cs @@ -1,10 +1,12 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Extensions { + [ExcludeFromCodeCoverage] public static class IVsExtensions { public static void Progress( diff --git a/src/ApiClientCodeGen.VSIX/Extensions/ProjectExtensions.cs b/src/ApiClientCodeGen.VSIX/Extensions/ProjectExtensions.cs index 3622129b50..87f851dff2 100644 --- a/src/ApiClientCodeGen.VSIX/Extensions/ProjectExtensions.cs +++ b/src/ApiClientCodeGen.VSIX/Extensions/ProjectExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Runtime.InteropServices; @@ -15,6 +16,7 @@ namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Extensions { + [ExcludeFromCodeCoverage] public static class ProjectExtensions { public static string GetRootFolder(this Project project, DTE Dte) diff --git a/src/ApiClientCodeGen.VSIX/Extensions/StringExtensions.cs b/src/ApiClientCodeGen.VSIX/Extensions/StringExtensions.cs index 6479d819a2..0cada04e88 100644 --- a/src/ApiClientCodeGen.VSIX/Extensions/StringExtensions.cs +++ b/src/ApiClientCodeGen.VSIX/Extensions/StringExtensions.cs @@ -2,12 +2,26 @@ using System.Runtime.InteropServices; using System.Text; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Extensions { public static class StringExtension { + private static readonly JsonSerializerSettings jsonSettings; + + static StringExtension() + { + jsonSettings = new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore, + ContractResolver = new CamelCasePropertyNamesContractResolver() + }; + + jsonSettings.Converters.Add(new StringEnumConverter()); + } + public static IntPtr ConvertToIntPtr(this string code, out uint pcbOutput) { var data = Encoding.Default.GetBytes(code); @@ -22,10 +36,6 @@ public static IntPtr ConvertToIntPtr(this string code, out uint pcbOutput) public static string ToJson(this object value) => JsonConvert.SerializeObject( value, - new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore, - ContractResolver = new CamelCasePropertyNamesContractResolver() - }); + jsonSettings); } } diff --git a/src/ApiClientCodeGen.VSIX/Generators/AutoRest/AutoRestCSharpCodeGenerator.cs b/src/ApiClientCodeGen.VSIX/Generators/AutoRest/AutoRestCSharpCodeGenerator.cs index b6638719df..d94dee701a 100644 --- a/src/ApiClientCodeGen.VSIX/Generators/AutoRest/AutoRestCSharpCodeGenerator.cs +++ b/src/ApiClientCodeGen.VSIX/Generators/AutoRest/AutoRestCSharpCodeGenerator.cs @@ -1,6 +1,7 @@ using System; using System.IO; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Core; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options; namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Generators.AutoRest { @@ -20,9 +21,7 @@ protected override string GetArguments(string outputFile) protected override string GetCommand() { - var autorestCmd = Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), - "npm\\autorest.cmd"); + var autorestCmd = PathProvider.GetAutoRestPath(); if (!File.Exists(autorestCmd)) DependencyDownloader.InstallAutoRest(); diff --git a/src/ApiClientCodeGen.VSIX/Generators/CSharpFileMerger.cs b/src/ApiClientCodeGen.VSIX/Generators/CSharpFileMerger.cs index 52c8f51498..5784268311 100644 --- a/src/ApiClientCodeGen.VSIX/Generators/CSharpFileMerger.cs +++ b/src/ApiClientCodeGen.VSIX/Generators/CSharpFileMerger.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Text; @@ -15,6 +16,25 @@ public static string MergeFiles(string folder) return GenerateCombinedSource(namespaces, filesToParse); } + public static string MergeFilesAndDeleteSource(string output) + { + try + { + return MergeFiles(output); + } + finally + { + try + { + Directory.Delete(output, true); + } + catch (Exception e) + { + Trace.WriteLine(e); + } + } + } + private static string GenerateCombinedSource(IEnumerable namespaces, IEnumerable files) { var sb = new StringBuilder(); diff --git a/src/ApiClientCodeGen.VSIX/Generators/CodeGeneratorFactory.cs b/src/ApiClientCodeGen.VSIX/Generators/CodeGeneratorFactory.cs index a66db1211c..7b14f00039 100644 --- a/src/ApiClientCodeGen.VSIX/Generators/CodeGeneratorFactory.cs +++ b/src/ApiClientCodeGen.VSIX/Generators/CodeGeneratorFactory.cs @@ -1,14 +1,32 @@ using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Core; -using System; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Generators.AutoRest; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Generators.NSwag; -using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Generators.Swagger; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Generators.OpenApi; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Generators.Swagger; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options; +using System; namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Generators { - public class CodeGeneratorFactory + public interface ICodeGeneratorFactory { + ICodeGenerator Create( + string defaultNamespace, + string inputFileContents, + string inputFilePath, + SupportedLanguage language, + SupportedCodeGenerator generator); + } + + public class CodeGeneratorFactory : ICodeGeneratorFactory + { + private readonly IOptionsFactory optionsFactory; + + public CodeGeneratorFactory(IOptionsFactory optionsFactory = null) + { + this.optionsFactory = optionsFactory ?? new OptionsFactory(); + } + public ICodeGenerator Create( string defaultNamespace, string inputFileContents, @@ -20,22 +38,33 @@ public ICodeGenerator Create( { case SupportedCodeGenerator.AutoRest: if (language == SupportedLanguage.CSharp) - return new AutoRestCSharpCodeGenerator(inputFilePath, defaultNamespace); + return new AutoRestCSharpCodeGenerator( + inputFilePath, + defaultNamespace); break; case SupportedCodeGenerator.NSwag: if (language == SupportedLanguage.CSharp) - return new NSwagCSharpCodeGenerator(inputFilePath, defaultNamespace); + return new NSwagCSharpCodeGenerator( + inputFilePath, + defaultNamespace, + optionsFactory.Create()); break; case SupportedCodeGenerator.Swagger: if (language == SupportedLanguage.CSharp) - return new SwaggerCSharpCodeGenerator(inputFilePath, defaultNamespace); + return new SwaggerCSharpCodeGenerator( + inputFilePath, + defaultNamespace, + optionsFactory.Create()); break; case SupportedCodeGenerator.OpenApi: if (language == SupportedLanguage.CSharp) - return new OpenApiCSharpCodeGenerator(inputFilePath, defaultNamespace); + return new OpenApiCSharpCodeGenerator( + inputFilePath, + defaultNamespace, + optionsFactory.Create()); break; } diff --git a/src/ApiClientCodeGen.VSIX/Generators/NSwag/NSwagCSharpCodeGenerator.cs b/src/ApiClientCodeGen.VSIX/Generators/NSwag/NSwagCSharpCodeGenerator.cs index 3265e8aa39..ec629852ac 100644 --- a/src/ApiClientCodeGen.VSIX/Generators/NSwag/NSwagCSharpCodeGenerator.cs +++ b/src/ApiClientCodeGen.VSIX/Generators/NSwag/NSwagCSharpCodeGenerator.cs @@ -1,9 +1,8 @@ using System; using System.Linq; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Extensions; -using Microsoft.VisualStudio.Shell; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options; using Microsoft.VisualStudio.Shell.Interop; -using NJsonSchema.CodeGeneration.CSharp; using NSwag; using NSwag.CodeGeneration.CSharp; @@ -13,11 +12,13 @@ public class NSwagCSharpCodeGenerator : ICodeGenerator { private readonly string swaggerFile; private readonly string defaultNamespace; + private readonly NSwagCSharpOptions options; - public NSwagCSharpCodeGenerator(string swaggerFile, string defaultNamespace) + public NSwagCSharpCodeGenerator(string swaggerFile, string defaultNamespace, INSwagOptions options) { this.swaggerFile = swaggerFile ?? throw new ArgumentNullException(nameof(swaggerFile)); this.defaultNamespace = defaultNamespace ?? throw new ArgumentNullException(nameof(defaultNamespace)); + this.options = new NSwagCSharpOptions(options); } public string GenerateCode(IVsGeneratorProgress pGenerateProgress) @@ -36,14 +37,14 @@ public string GenerateCode(IVsGeneratorProgress pGenerateProgress) var settings = new SwaggerToCSharpClientGeneratorSettings { ClassName = GetClassName(document), - InjectHttpClient = true, - GenerateClientInterfaces = true, - GenerateDtoTypes = true, - UseBaseUrl = false, + InjectHttpClient = options.InjectHttpClient, + GenerateClientInterfaces = options.GenerateClientInterfaces, + GenerateDtoTypes = options.GenerateDtoTypes, + UseBaseUrl = options.UseBaseUrl, CSharpGeneratorSettings = { Namespace = defaultNamespace, - ClassStyle = CSharpClassStyle.Inpc + ClassStyle = options.ClassStyle }, }; diff --git a/src/ApiClientCodeGen.VSIX/Generators/NSwagStudio/NSwagStudioCodeGenerator.cs b/src/ApiClientCodeGen.VSIX/Generators/NSwagStudio/NSwagStudioCodeGenerator.cs index b1394a3b54..4d30e14b67 100644 --- a/src/ApiClientCodeGen.VSIX/Generators/NSwagStudio/NSwagStudioCodeGenerator.cs +++ b/src/ApiClientCodeGen.VSIX/Generators/NSwagStudio/NSwagStudioCodeGenerator.cs @@ -3,6 +3,7 @@ using System.IO; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Core; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Extensions; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options; using Microsoft.VisualStudio.Shell.Interop; using Newtonsoft.Json; @@ -11,22 +12,25 @@ namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Generators.NSwa public class NSwagStudioCodeGenerator : ICodeGenerator { private readonly string nswagStudioFile; + private readonly CustomPathOptions options; - public NSwagStudioCodeGenerator(string nswagStudioFile) + public NSwagStudioCodeGenerator(string nswagStudioFile, IGeneralOptions options) { this.nswagStudioFile = nswagStudioFile ?? throw new ArgumentNullException(nameof(nswagStudioFile)); + this.options = new CustomPathOptions(options); } public string GenerateCode(IVsGeneratorProgress pGenerateProgress) { pGenerateProgress?.Progress(10); - var command = Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), - "Rico Suter\\NSwagStudio\\Win\\NSwag.exe"); - + var command = options.NSwagPath; if (!File.Exists(command)) - throw new NotInstalledException("NSwag not installed. Please install NSwagStudio"); + { + command = PathProvider.GetNSwagPath(); + if (!File.Exists(command)) + throw new NotInstalledException("NSwag not installed. Please install NSwagStudio"); + } TryRemoveSwaggerJsonSpec(nswagStudioFile); ProcessHelper.StartProcess(command, $"run \"{nswagStudioFile}\""); diff --git a/src/ApiClientCodeGen.VSIX/Generators/NSwagStudio/NSwagStudioFileHelper.cs b/src/ApiClientCodeGen.VSIX/Generators/NSwagStudio/NSwagStudioFileHelper.cs index e4141ed093..d0e646b074 100644 --- a/src/ApiClientCodeGen.VSIX/Generators/NSwagStudio/NSwagStudioFileHelper.cs +++ b/src/ApiClientCodeGen.VSIX/Generators/NSwagStudio/NSwagStudioFileHelper.cs @@ -1,5 +1,7 @@ using System.Threading.Tasks; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Extensions; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options; +using NJsonSchema.CodeGeneration.CSharp; using NSwag; namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Generators.NSwagStudio @@ -9,6 +11,7 @@ public sealed class NSwagStudioFileHelper public static async Task CreateNSwagStudioFileAsync( string openApiSpec, string openApiSpecUrl, + INSwagStudioOptions options = null, string outputNamespace = null) { var className = (await SwaggerDocument.FromJsonAsync(openApiSpec)).GenerateClassName(); @@ -28,17 +31,17 @@ public static async Task CreateNSwagStudioFileAsync( SwaggerToCSharpClient = new { ClassName = className, - InjectHttpClient = true, - GenerateClientInterfaces = true, - GenerateDtoTypes = true, - UseBaseUrl = false, + InjectHttpClient = options?.InjectHttpClient ?? true, + GenerateClientInterfaces = options?.GenerateClientInterfaces ?? true, + GenerateDtoTypes = options?.GenerateDtoTypes ?? true, + UseBaseUrl = options?.UseBaseUrl ?? false, OperationGenerationMode = "MultipleClientsFromOperationId", - GenerateResponseClasses = true, - GenerateJsonMethods = true, - RequiredPropertiesMustBeDefined = true, - classStyle = "Inpc", - GenerateDefaultValues = true, - GenerateDataAnnotations = true, + GenerateResponseClasses = options?.GenerateResponseClasses ?? true, + GenerateJsonMethods = options?.GenerateJsonMethods ?? true, + RequiredPropertiesMustBeDefined = options?.RequiredPropertiesMustBeDefined ?? true, + ClassStyle = options?.ClassStyle ?? CSharpClassStyle.Poco, + GenerateDefaultValues = options?.GenerateDefaultValues ?? true, + GenerateDataAnnotations = options?.GenerateDataAnnotations ?? true, Namespace = outputNamespace ?? "GeneratedCode", Output = $"{className}.cs" } diff --git a/src/ApiClientCodeGen.VSIX/Generators/OpenApi/OpenApiCSharpCodeGenerator.cs b/src/ApiClientCodeGen.VSIX/Generators/OpenApi/OpenApiCSharpCodeGenerator.cs index c4d5b37e27..2fc200792d 100644 --- a/src/ApiClientCodeGen.VSIX/Generators/OpenApi/OpenApiCSharpCodeGenerator.cs +++ b/src/ApiClientCodeGen.VSIX/Generators/OpenApi/OpenApiCSharpCodeGenerator.cs @@ -1,25 +1,23 @@ using System; -using System.Diagnostics; using System.IO; -using System.Net; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Core; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Extensions; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options; using Microsoft.VisualStudio.Shell.Interop; namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Generators.OpenApi { public class OpenApiCSharpCodeGenerator : ICodeGenerator { - private const string DownloadUrl = "http://central.maven.org/maven2/org/openapitools/openapi-generator-cli/4.0.0/openapi-generator-cli-4.0.0.jar"; - private const string Checksum = "61574C43BEC9B6EDD54E2DD0993F81D5"; private readonly string swaggerFile; private readonly string defaultNamespace; - private readonly string cliPath; + private readonly JavaPathProvider javaPathProvider; - public OpenApiCSharpCodeGenerator(string swaggerFile, string defaultNamespace) + public OpenApiCSharpCodeGenerator(string swaggerFile, string defaultNamespace, IGeneralOptions options) { this.swaggerFile = swaggerFile ?? throw new ArgumentNullException(nameof(swaggerFile)); this.defaultNamespace = defaultNamespace ?? throw new ArgumentNullException(nameof(defaultNamespace)); - cliPath = Path.Combine(Path.GetTempPath(), "openapi-generator-cli.jar"); + javaPathProvider = new JavaPathProvider(options ?? throw new ArgumentNullException(nameof(options))); } public string GenerateCode(IVsGeneratorProgress pGenerateProgress) @@ -28,7 +26,7 @@ public string GenerateCode(IVsGeneratorProgress pGenerateProgress) { pGenerateProgress.Progress(10); - VerifySwaggerCodegenCli(); + var cliPath = DependencyDownloader.InstallOpenApiGenerator(); pGenerateProgress.Progress(30); var output = Path.Combine( @@ -47,42 +45,15 @@ public string GenerateCode(IVsGeneratorProgress pGenerateProgress) $"-DpackageName={defaultNamespace} " + "--skip-overwrite "; - ProcessHelper.StartProcess("java", arguments); + ProcessHelper.StartProcess(javaPathProvider.GetJavaExePath(), arguments); pGenerateProgress.Progress(80); - return MergeFilesAndDeleteFolder(output); + return CSharpFileMerger.MergeFilesAndDeleteSource(output); } finally { pGenerateProgress.Progress(90); } } - - private void VerifySwaggerCodegenCli() - { - if (File.Exists(cliPath) && FileHelper.CalculateMd5(cliPath) == Checksum) - return; - - new WebClient().DownloadFile(DownloadUrl, cliPath); - } - - private static string MergeFilesAndDeleteFolder(string output) - { - try - { - return CSharpFileMerger.MergeFiles(output); - } - finally - { - try - { - Directory.Delete(output, true); - } - catch (Exception e) - { - Trace.WriteLine(e); - } - } - } } } diff --git a/src/ApiClientCodeGen.VSIX/Generators/Swagger/SwaggerCSharpCodeGenerator.cs b/src/ApiClientCodeGen.VSIX/Generators/Swagger/SwaggerCSharpCodeGenerator.cs index 99d2895e5a..e113e0d145 100644 --- a/src/ApiClientCodeGen.VSIX/Generators/Swagger/SwaggerCSharpCodeGenerator.cs +++ b/src/ApiClientCodeGen.VSIX/Generators/Swagger/SwaggerCSharpCodeGenerator.cs @@ -1,8 +1,8 @@ using System; -using System.Diagnostics; using System.IO; -using System.Net; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Core; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Extensions; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options; using Microsoft.VisualStudio.Shell.Interop; namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Generators.Swagger @@ -11,13 +11,13 @@ public class SwaggerCSharpCodeGenerator : ICodeGenerator { private readonly string swaggerFile; private readonly string defaultNamespace; - private readonly string cliPath; + private readonly JavaPathProvider javaPathProvider; - public SwaggerCSharpCodeGenerator(string swaggerFile, string defaultNamespace) + public SwaggerCSharpCodeGenerator(string swaggerFile, string defaultNamespace, IGeneralOptions options) { this.swaggerFile = swaggerFile ?? throw new ArgumentNullException(nameof(swaggerFile)); this.defaultNamespace = defaultNamespace ?? throw new ArgumentNullException(nameof(defaultNamespace)); - cliPath = Path.Combine(Path.GetTempPath(), "swagger-codegen-cli.jar"); + javaPathProvider = new JavaPathProvider(options ?? throw new ArgumentNullException(nameof(options))); } public string GenerateCode(IVsGeneratorProgress pGenerateProgress) @@ -25,8 +25,8 @@ public string GenerateCode(IVsGeneratorProgress pGenerateProgress) try { pGenerateProgress.Progress(10); - - VerifySwaggerCodegenCli(); + + var cliPath = DependencyDownloader.InstallSwaggerCodegenCli(); pGenerateProgress.Progress(30); var output = Path.Combine( @@ -44,49 +44,16 @@ public string GenerateCode(IVsGeneratorProgress pGenerateProgress) $"-DapiTests=false -DmodelTests=false " + $"-DpackageName={defaultNamespace} " + $"--skip-overwrite "; - - ProcessHelper.StartProcess("java", arguments); + + ProcessHelper.StartProcess(javaPathProvider.GetJavaExePath(), arguments); pGenerateProgress.Progress(80); - return MergeFilesAndDeleteFolder(output); + return CSharpFileMerger.MergeFilesAndDeleteSource(output); } finally { pGenerateProgress.Progress(90); } } - - private void VerifySwaggerCodegenCli() - { - if (File.Exists(cliPath)) - return; - - const string swaggerCliUrl = - "https://repo1.maven.org/maven2/io/swagger/swagger-codegen-cli/2.4.5/swagger-codegen-cli-2.4.5.jar"; - - new WebClient() - .DownloadFile( - swaggerCliUrl, - cliPath); - } - - private static string MergeFilesAndDeleteFolder(string output) - { - try - { - return CSharpFileMerger.MergeFiles(output); - } - finally - { - try - { - Directory.Delete(output, true); - } - catch (Exception e) - { - Trace.WriteLine(e); - } - } - } } } diff --git a/src/ApiClientCodeGen.VSIX/Options/CustomPathOptions.cs b/src/ApiClientCodeGen.VSIX/Options/CustomPathOptions.cs new file mode 100644 index 0000000000..a3a600e066 --- /dev/null +++ b/src/ApiClientCodeGen.VSIX/Options/CustomPathOptions.cs @@ -0,0 +1,38 @@ +using System; +using System.Diagnostics; + +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options +{ + public class CustomPathOptions : IGeneralOptions + { + public CustomPathOptions(IGeneralOptions options = null) + { + try + { + if (options == null) + options = (IGeneralOptions)VsPackage.Instance.GetDialogPage(typeof(GeneralOptionPage)); + + JavaPath = options.JavaPath; + NpmPath = options.NpmPath; + NSwagPath = options.NSwagPath; + } + catch (Exception e) + { + Trace.WriteLine(e); + Trace.WriteLine(Environment.NewLine); + Trace.WriteLine("Error reading user options. Reverting to default values"); + Trace.WriteLine($"JavaPath = {PathProvider.GetJavaPath()}"); + Trace.WriteLine($"NpmPath = {PathProvider.GetNpmPath()}"); + Trace.WriteLine($"NSwagPath = {PathProvider.GetNSwagPath()}"); + + JavaPath = PathProvider.GetJavaPath(); + NpmPath = PathProvider.GetNpmPath(); + NSwagPath = PathProvider.GetNSwagPath(); + } + } + + public string JavaPath { get; set; } + public string NpmPath { get; set; } + public string NSwagPath { get; set; } + } +} diff --git a/src/ApiClientCodeGen.VSIX/Options/GeneralOptionPage.cs b/src/ApiClientCodeGen.VSIX/Options/GeneralOptionPage.cs new file mode 100644 index 0000000000..a8f4eeaff8 --- /dev/null +++ b/src/ApiClientCodeGen.VSIX/Options/GeneralOptionPage.cs @@ -0,0 +1,37 @@ +using System.ComponentModel; +using System.Windows.Forms; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Windows; +using Microsoft.VisualStudio.Shell; + +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options +{ + public class GeneralOptionPage : DialogPage, IGeneralOptions + { + public const string Name = "General"; + + public GeneralOptionPage() + { + JavaPath = PathProvider.GetJavaPath(); + NpmPath = PathProvider.GetNpmPath(); + NSwagPath = PathProvider.GetNSwagPath(); + } + + [Category("File Paths")] + [DisplayName("Java Path")] + [Description("Full path to java.exe. Leave empty to get path from JAVA_HOME")] + public string JavaPath { get; set; } + + [Category("File Paths")] + [DisplayName("NPM Path")] + [Description("Full path to npm.cmd")] + public string NpmPath { get; set; } + + [Category("File Paths")] + [DisplayName("NSwag Path")] + [Description("Full path to nswag.exe")] + public string NSwagPath { get; set; } + + protected override IWin32Window Window + => new GeneralOptionsPageCustom(this); + } +} diff --git a/src/ApiClientCodeGen.VSIX/Options/IGeneralOptions.cs b/src/ApiClientCodeGen.VSIX/Options/IGeneralOptions.cs new file mode 100644 index 0000000000..d7589864fe --- /dev/null +++ b/src/ApiClientCodeGen.VSIX/Options/IGeneralOptions.cs @@ -0,0 +1,9 @@ +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options +{ + public interface IGeneralOptions + { + string JavaPath { get; set; } + string NpmPath { get; set; } + string NSwagPath { get; set; } + } +} \ No newline at end of file diff --git a/src/ApiClientCodeGen.VSIX/Options/INSwagOption.cs b/src/ApiClientCodeGen.VSIX/Options/INSwagOption.cs new file mode 100644 index 0000000000..d52bc25f54 --- /dev/null +++ b/src/ApiClientCodeGen.VSIX/Options/INSwagOption.cs @@ -0,0 +1,13 @@ +using NJsonSchema.CodeGeneration.CSharp; + +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options +{ + public interface INSwagOptions + { + bool InjectHttpClient { get; } + bool GenerateClientInterfaces { get; } + bool GenerateDtoTypes { get; } + bool UseBaseUrl { get; } + CSharpClassStyle ClassStyle { get; } + } +} \ No newline at end of file diff --git a/src/ApiClientCodeGen.VSIX/Options/INSwagStudioOptions.cs b/src/ApiClientCodeGen.VSIX/Options/INSwagStudioOptions.cs new file mode 100644 index 0000000000..5523c7f473 --- /dev/null +++ b/src/ApiClientCodeGen.VSIX/Options/INSwagStudioOptions.cs @@ -0,0 +1,11 @@ +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options +{ + public interface INSwagStudioOptions : INSwagOptions + { + bool GenerateResponseClasses { get; } + bool GenerateJsonMethods { get; } + bool RequiredPropertiesMustBeDefined { get; } + bool GenerateDefaultValues { get; } + bool GenerateDataAnnotations { get; } + } +} \ No newline at end of file diff --git a/src/ApiClientCodeGen.VSIX/Options/IOptionsFactory.cs b/src/ApiClientCodeGen.VSIX/Options/IOptionsFactory.cs new file mode 100644 index 0000000000..886ccf4963 --- /dev/null +++ b/src/ApiClientCodeGen.VSIX/Options/IOptionsFactory.cs @@ -0,0 +1,8 @@ +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options +{ + public interface IOptionsFactory + { + TOptions Create() + where TOptions : class; + } +} \ No newline at end of file diff --git a/src/ApiClientCodeGen.VSIX/Options/JavaPathProvider.cs b/src/ApiClientCodeGen.VSIX/Options/JavaPathProvider.cs new file mode 100644 index 0000000000..430899db2f --- /dev/null +++ b/src/ApiClientCodeGen.VSIX/Options/JavaPathProvider.cs @@ -0,0 +1,48 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Net; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Core; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Generators; + +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options +{ + public class JavaPathProvider + { + private readonly CustomPathOptions options; + + public JavaPathProvider(IGeneralOptions options) + { + this.options = new CustomPathOptions(options); + } + + public string GetJavaExePath() + { + var javaPath = options.JavaPath; + if (!string.IsNullOrWhiteSpace(javaPath)) + { + if (File.Exists(javaPath)) + return javaPath; + } + + try + { + Trace.WriteLine("Checking Java version"); + ProcessHelper.StartProcess("java", "-version"); + return "java"; + } + catch (Exception e) + { + Trace.WriteLine("Java not installed using default settings"); + } + + if (string.IsNullOrWhiteSpace(options.JavaPath)) + javaPath = PathProvider.GetJavaPath(); + + if (File.Exists(javaPath)) + return javaPath; + + throw new NotInstalledException("Unable to find Java"); + } + } +} diff --git a/src/ApiClientCodeGen.VSIX/Options/NSwagCSharpOptions.cs b/src/ApiClientCodeGen.VSIX/Options/NSwagCSharpOptions.cs new file mode 100644 index 0000000000..41c7f4634b --- /dev/null +++ b/src/ApiClientCodeGen.VSIX/Options/NSwagCSharpOptions.cs @@ -0,0 +1,51 @@ +using System; +using System.Diagnostics; +using NJsonSchema.CodeGeneration.CSharp; + +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options +{ + public class NSwagCSharpOptions : INSwagOptions + { + public NSwagCSharpOptions(INSwagOptions options = null) + { + try + { + if (options == null) + options = (INSwagOptions)VsPackage.Instance.GetDialogPage(typeof(NSwagOptionsPage)); + + InjectHttpClient = options.InjectHttpClient; + GenerateClientInterfaces = options.GenerateClientInterfaces; + GenerateDtoTypes = options.GenerateDtoTypes; + UseBaseUrl = options.UseBaseUrl; + ClassStyle = options.ClassStyle; + } + catch (Exception e) + { + Trace.WriteLine(e); + Trace.WriteLine(Environment.NewLine); + Trace.WriteLine("Error reading user options. Reverting to default values"); + Trace.WriteLine("InjectHttpClient = true"); + Trace.WriteLine("GenerateClientInterfaces = true"); + Trace.WriteLine("GenerateDtoTypes = true"); + Trace.WriteLine("UseBaseUrl = false"); + Trace.WriteLine("ClassStyle = CSharpClassStyle.Poco"); + + InjectHttpClient = true; + GenerateClientInterfaces = true; + GenerateDtoTypes = true; + UseBaseUrl = false; + ClassStyle = CSharpClassStyle.Poco; + } + } + + public bool InjectHttpClient { get; } + + public bool GenerateClientInterfaces { get; } + + public bool GenerateDtoTypes { get; } + + public bool UseBaseUrl { get; } + + public CSharpClassStyle ClassStyle { get; } + } +} \ No newline at end of file diff --git a/src/ApiClientCodeGen.VSIX/Options/NSwagOptionsPage.cs b/src/ApiClientCodeGen.VSIX/Options/NSwagOptionsPage.cs new file mode 100644 index 0000000000..1c77be2527 --- /dev/null +++ b/src/ApiClientCodeGen.VSIX/Options/NSwagOptionsPage.cs @@ -0,0 +1,36 @@ +using System.ComponentModel; +using Microsoft.VisualStudio.Shell; +using NJsonSchema.CodeGeneration.CSharp; + +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options +{ + public class NSwagOptionsPage : DialogPage, INSwagOptions + { + public const string Name = "NSwag"; + + [Category(Name)] + [DisplayName("Inject HttpClient")] + [Description("Set this to TRUE to generate the constructor that accepts HttpClient")] + public bool InjectHttpClient { get; set; } = true; + + [Category(Name)] + [DisplayName("Generate Interfaces")] + [Description("Set this to TRUE to generate client interfaces")] + public bool GenerateClientInterfaces { get; set; } = true; + + [Category(Name)] + [DisplayName("Generate DTO types")] + [Description("Set this to TRUE to generate DTO types")] + public bool GenerateDtoTypes { get; set; } = true; + + [Category(Name)] + [DisplayName("Use Base URL")] + [Description("Set this to TRUE to include a base URL for every HTTP request")] + public bool UseBaseUrl { get; set; } = false; + + [Category(Name)] + [DisplayName("C# Class Style")] + [Description("POCO (Plain Old C# Objects), Inpc (Implements INotifyPropertyChanged), Prism (Prism base class), Records (readonly POCO)")] + public CSharpClassStyle ClassStyle { get; set; } + } +} diff --git a/src/ApiClientCodeGen.VSIX/Options/NSwagStudioOptions.cs b/src/ApiClientCodeGen.VSIX/Options/NSwagStudioOptions.cs new file mode 100644 index 0000000000..6399a3a352 --- /dev/null +++ b/src/ApiClientCodeGen.VSIX/Options/NSwagStudioOptions.cs @@ -0,0 +1,47 @@ +using System; +using System.Diagnostics; + +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options +{ + public class NSwagStudioOptions : NSwagCSharpOptions, INSwagStudioOptions + { + public NSwagStudioOptions(INSwagStudioOptions options = null) + : base(options) + { + try + { + if (options == null) + options = (INSwagStudioOptions)VsPackage.Instance.GetDialogPage(typeof(NSwagStudioOptionsPage)); + + GenerateResponseClasses = options.GenerateResponseClasses; + GenerateJsonMethods = options.GenerateJsonMethods; + RequiredPropertiesMustBeDefined = options.RequiredPropertiesMustBeDefined; + GenerateDefaultValues = options.GenerateDefaultValues; + GenerateDataAnnotations = options.GenerateDataAnnotations; + } + catch (Exception e) + { + Trace.WriteLine(e); + Trace.WriteLine(Environment.NewLine); + Trace.WriteLine("Error reading user options. Reverting to default values"); + Trace.WriteLine("GenerateResponseClasses = true"); + Trace.WriteLine("GenerateJsonMethods = true"); + Trace.WriteLine("RequiredPropertiesMustBeDefined = true"); + Trace.WriteLine("GenerateDefaultValues = true"); + Trace.WriteLine("GenerateDataAnnotations = true"); + + GenerateResponseClasses = true; + GenerateJsonMethods = true; + RequiredPropertiesMustBeDefined = true; + GenerateDefaultValues = true; + GenerateDataAnnotations = true; + } + } + + public bool GenerateResponseClasses { get; set; } + public bool GenerateJsonMethods { get; set; } + public bool RequiredPropertiesMustBeDefined { get; set; } + public bool GenerateDefaultValues { get; set; } + public bool GenerateDataAnnotations { get; set; } + } +} \ No newline at end of file diff --git a/src/ApiClientCodeGen.VSIX/Options/NSwagStudioOptionsPage.cs b/src/ApiClientCodeGen.VSIX/Options/NSwagStudioOptionsPage.cs new file mode 100644 index 0000000000..e853737ff6 --- /dev/null +++ b/src/ApiClientCodeGen.VSIX/Options/NSwagStudioOptionsPage.cs @@ -0,0 +1,61 @@ +using System.ComponentModel; +using Microsoft.VisualStudio.Shell; +using NJsonSchema.CodeGeneration.CSharp; + +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options +{ + public class NSwagStudioOptionsPage : DialogPage, INSwagStudioOptions + { + public const string Name = "NSwag Studio"; + + [Category(Name)] + [DisplayName("Inject HttpClient")] + [Description("Set this to TRUE to generate the constructor that accepts HttpClient")] + public bool InjectHttpClient { get; set; } = true; + + [Category(Name)] + [DisplayName("Generate Interfaces")] + [Description("Set this to TRUE to generate client interfaces")] + public bool GenerateClientInterfaces { get; set; } = true; + + [Category(Name)] + [DisplayName("Generate DTO types")] + [Description("Set this to TRUE to generate DTO types")] + public bool GenerateDtoTypes { get; set; } = true; + + [Category(Name)] + [DisplayName("Use Base URL")] + [Description("Set this to TRUE to include a base URL for every HTTP request")] + public bool UseBaseUrl { get; set; } = false; + + [Category(Name)] + [DisplayName("C# Class Style")] + [Description("POCO (Plain Old C# Objects), Inpc (Implements INotifyPropertyChanged), Prism (Prism base class), Records (readonly POCO)")] + public CSharpClassStyle ClassStyle { get; set; } + + [Category(Name)] + [DisplayName("Generate Response Classes")] + [Description("Set this to TRUE to generate response classes")] + public bool GenerateResponseClasses { get; set; } = true; + + [Category(Name)] + [DisplayName("Generate JSON methods")] + [Description("Set this to TRUE to generate JSON methods")] + public bool GenerateJsonMethods { get; set; } = true; + + [Category(Name)] + [DisplayName("Required properties must be defined")] + [Description("Set this to TRUE if required properties must be defined")] + public bool RequiredPropertiesMustBeDefined { get; set; } = true; + + [Category(Name)] + [DisplayName("Generate Default Values")] + [Description("Set this to TRUE to generate default values")] + public bool GenerateDefaultValues { get; set; } = true; + + [Category(Name)] + [DisplayName("Generate Default Values")] + [Description("Set this to TRUE to generate data annotations")] + public bool GenerateDataAnnotations { get; set; } = true; + } +} diff --git a/src/ApiClientCodeGen.VSIX/Options/OptionsFactory.cs b/src/ApiClientCodeGen.VSIX/Options/OptionsFactory.cs new file mode 100644 index 0000000000..7f045b46d0 --- /dev/null +++ b/src/ApiClientCodeGen.VSIX/Options/OptionsFactory.cs @@ -0,0 +1,9 @@ +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options +{ + public class OptionsFactory : IOptionsFactory + { + public TOptions Create() + where TOptions : class + => VsPackage.Instance.GetDialogPage(typeof(TDialogPage)) as TOptions; + } +} diff --git a/src/ApiClientCodeGen.VSIX/Options/PathProvider.cs b/src/ApiClientCodeGen.VSIX/Options/PathProvider.cs new file mode 100644 index 0000000000..e1a9ef6031 --- /dev/null +++ b/src/ApiClientCodeGen.VSIX/Options/PathProvider.cs @@ -0,0 +1,49 @@ +using System; +using System.Diagnostics; +using System.IO; + +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options +{ + public static class PathProvider + { + public static string GetJavaPath() + { + try + { + var javaHome = Environment.GetEnvironmentVariable("JAVA_HOME"); + var javaExe = Path.Combine(javaHome, "bin\\java.exe"); + return javaExe; + } + catch (Exception e) + { + Trace.WriteLine(e); + Trace.WriteLine(Environment.NewLine); + Trace.WriteLine("Unable to find JAVA_HOME environment variable"); + return null; + } + } + + public static string GetNpmPath() + { + var programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles); + var programFiles64 = programFiles.Replace(" (x86)", newValue: string.Empty); + + var npmCommand = Path.Combine(programFiles, "nodejs\\npm.cmd"); + if (File.Exists(npmCommand)) + return npmCommand; + + npmCommand = Path.Combine(programFiles64, "nodejs\\npm.cmd"); + return !File.Exists(npmCommand) ? null : npmCommand; + } + + public static string GetNSwagPath() + => Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), + "Rico Suter\\NSwagStudio\\Win\\NSwag.exe"); + + public static string GetAutoRestPath() + => Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + "npm\\autorest.cmd"); + } +} diff --git a/src/ApiClientCodeGen.VSIX/Utility/OutputWindow.cs b/src/ApiClientCodeGen.VSIX/Utility/OutputWindow.cs index eb5e85219d..a609e21f09 100644 --- a/src/ApiClientCodeGen.VSIX/Utility/OutputWindow.cs +++ b/src/ApiClientCodeGen.VSIX/Utility/OutputWindow.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; @@ -8,6 +9,7 @@ namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Utility { + [ExcludeFromCodeCoverage] public static class OutputWindow { private static string name; diff --git a/src/ApiClientCodeGen.VSIX/Utility/OutputWindowTraceListener.cs b/src/ApiClientCodeGen.VSIX/Utility/OutputWindowTraceListener.cs index 252f553c02..74e74054bd 100644 --- a/src/ApiClientCodeGen.VSIX/Utility/OutputWindowTraceListener.cs +++ b/src/ApiClientCodeGen.VSIX/Utility/OutputWindowTraceListener.cs @@ -1,7 +1,9 @@ using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Utility { + [ExcludeFromCodeCoverage] public class OutputWindowTraceListener : TraceListener { public override void Write(string message) diff --git a/src/ApiClientCodeGen.VSIX/VSPackage.cs b/src/ApiClientCodeGen.VSIX/VSPackage.cs index a5513d2475..6e285da295 100644 --- a/src/ApiClientCodeGen.VSIX/VSPackage.cs +++ b/src/ApiClientCodeGen.VSIX/VSPackage.cs @@ -1,16 +1,20 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using System.Threading; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Commands; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Commands.AddNew; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Commands.CustomTool; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Commands.NSwagStudio; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options; +using EnvDTE; using Microsoft.VisualStudio.Shell; using OutputWindow = ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Utility.OutputWindow; using Task = System.Threading.Tasks.Task; namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient { + [ExcludeFromCodeCoverage] [Guid("47AFE4E1-5A52-4FE1-8CA7-EDB8310BDA4A")] [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] [InstalledProductRegistration(VsixName, "", "1.0")] @@ -27,6 +31,27 @@ namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient NSwagStudioCommand.Expression, new[] { NSwagStudioCommand.Expression }, new[] { NSwagStudioCommand.TermValue })] + [ProvideOptionPage( + typeof(GeneralOptionPage), + VsixName, + GeneralOptionPage.Name, + 0, + 0, + true)] + [ProvideOptionPage( + typeof(NSwagOptionsPage), + VsixName, + NSwagOptionsPage.Name, + 0, + 0, + true)] + [ProvideOptionPage( + typeof(NSwagStudioOptionsPage), + VsixName, + NSwagStudioOptionsPage.Name, + 0, + 0, + true)] public sealed class VsPackage : AsyncPackage { public const string VsixName = "REST API Client Code Generator"; @@ -44,6 +69,8 @@ public sealed class VsPackage : AsyncPackage new NewNSwagStudioClientCommand() }; + public static AsyncPackage Instance { get; private set; } + protected override async Task InitializeAsync( CancellationToken cancellationToken, IProgress progress) @@ -51,6 +78,7 @@ protected override async Task InitializeAsync( await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); await base.InitializeAsync(cancellationToken, progress); OutputWindow.Initialize(this, VsixName); + Instance = this; foreach (var command in commands) await command.InitializeAsync(this, cancellationToken); diff --git a/src/ApiClientCodeGen.VSIX/Windows/EnterOpenApiSpecDialog.cs b/src/ApiClientCodeGen.VSIX/Windows/EnterOpenApiSpecDialog.cs index 406f82a0a5..e21c47a0f5 100644 --- a/src/ApiClientCodeGen.VSIX/Windows/EnterOpenApiSpecDialog.cs +++ b/src/ApiClientCodeGen.VSIX/Windows/EnterOpenApiSpecDialog.cs @@ -1,11 +1,13 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Net; using System.Threading.Tasks; using System.Windows.Forms; namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Windows { + [ExcludeFromCodeCoverage] public partial class EnterOpenApiSpecDialog : Form { public EnterOpenApiSpecDialog() diff --git a/src/ApiClientCodeGen.VSIX/Windows/EnterOpenApiSpecDialogResult.cs b/src/ApiClientCodeGen.VSIX/Windows/EnterOpenApiSpecDialogResult.cs index 9bd5c8577d..37ea5e3550 100644 --- a/src/ApiClientCodeGen.VSIX/Windows/EnterOpenApiSpecDialogResult.cs +++ b/src/ApiClientCodeGen.VSIX/Windows/EnterOpenApiSpecDialogResult.cs @@ -1,5 +1,8 @@ -namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Windows +using System.Diagnostics.CodeAnalysis; + +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Windows { + [ExcludeFromCodeCoverage] public class EnterOpenApiSpecDialogResult { public EnterOpenApiSpecDialogResult( diff --git a/src/ApiClientCodeGen.VSIX/Windows/GeneralOptionsPageCustom.Designer.cs b/src/ApiClientCodeGen.VSIX/Windows/GeneralOptionsPageCustom.Designer.cs new file mode 100644 index 0000000000..c7cabb262c --- /dev/null +++ b/src/ApiClientCodeGen.VSIX/Windows/GeneralOptionsPageCustom.Designer.cs @@ -0,0 +1,167 @@ +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Windows +{ + partial class GeneralOptionsPageCustom + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.lblJavaPath = new System.Windows.Forms.Label(); + this.tbJavaPath = new System.Windows.Forms.TextBox(); + this.btnJavaPath = new System.Windows.Forms.Button(); + this.btnNpmPath = new System.Windows.Forms.Button(); + this.tbNpmPath = new System.Windows.Forms.TextBox(); + this.lblNpmPath = new System.Windows.Forms.Label(); + this.btnNSwagPath = new System.Windows.Forms.Button(); + this.tbNSwagPath = new System.Windows.Forms.TextBox(); + this.lblNSwagPath = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // lblJavaPath + // + this.lblJavaPath.AutoSize = true; + this.lblJavaPath.Location = new System.Drawing.Point(13, 12); + this.lblJavaPath.Name = "lblJavaPath"; + this.lblJavaPath.Size = new System.Drawing.Size(295, 13); + this.lblJavaPath.TabIndex = 0; + this.lblJavaPath.Text = "Path to java.exe. Leave empty to get path from JAVA_HOME"; + // + // tbJavaPath + // + this.tbJavaPath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tbJavaPath.Location = new System.Drawing.Point(16, 28); + this.tbJavaPath.Name = "tbJavaPath"; + this.tbJavaPath.Size = new System.Drawing.Size(325, 20); + this.tbJavaPath.TabIndex = 1; + this.tbJavaPath.TextChanged += new System.EventHandler(this.TbJavaPath_TextChanged); + // + // btnJavaPath + // + this.btnJavaPath.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnJavaPath.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btnJavaPath.Location = new System.Drawing.Point(347, 28); + this.btnJavaPath.Name = "btnJavaPath"; + this.btnJavaPath.Size = new System.Drawing.Size(30, 20); + this.btnJavaPath.TabIndex = 2; + this.btnJavaPath.Text = "..."; + this.btnJavaPath.UseVisualStyleBackColor = true; + this.btnJavaPath.Click += new System.EventHandler(this.BtnJavaPath_Click); + // + // btnNpmPath + // + this.btnNpmPath.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnNpmPath.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btnNpmPath.Location = new System.Drawing.Point(347, 79); + this.btnNpmPath.Name = "btnNpmPath"; + this.btnNpmPath.Size = new System.Drawing.Size(30, 20); + this.btnNpmPath.TabIndex = 5; + this.btnNpmPath.Text = "..."; + this.btnNpmPath.UseVisualStyleBackColor = true; + this.btnNpmPath.Click += new System.EventHandler(this.BtnNpmPath_Click); + // + // tbNpmPath + // + this.tbNpmPath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tbNpmPath.Location = new System.Drawing.Point(16, 79); + this.tbNpmPath.Name = "tbNpmPath"; + this.tbNpmPath.Size = new System.Drawing.Size(325, 20); + this.tbNpmPath.TabIndex = 4; + this.tbNpmPath.TextChanged += new System.EventHandler(this.TbNpmPath_TextChanged); + // + // lblNpmPath + // + this.lblNpmPath.AutoSize = true; + this.lblNpmPath.Location = new System.Drawing.Point(13, 63); + this.lblNpmPath.Name = "lblNpmPath"; + this.lblNpmPath.Size = new System.Drawing.Size(105, 13); + this.lblNpmPath.TabIndex = 3; + this.lblNpmPath.Text = "Full path to npm.cmd"; + // + // btnNSwagPath + // + this.btnNSwagPath.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnNSwagPath.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btnNSwagPath.Location = new System.Drawing.Point(347, 134); + this.btnNSwagPath.Name = "btnNSwagPath"; + this.btnNSwagPath.Size = new System.Drawing.Size(30, 20); + this.btnNSwagPath.TabIndex = 8; + this.btnNSwagPath.Text = "..."; + this.btnNSwagPath.UseVisualStyleBackColor = true; + this.btnNSwagPath.Click += new System.EventHandler(this.BtnNSwagPath_Click); + // + // tbNSwagPath + // + this.tbNSwagPath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tbNSwagPath.Location = new System.Drawing.Point(16, 134); + this.tbNSwagPath.Name = "tbNSwagPath"; + this.tbNSwagPath.Size = new System.Drawing.Size(325, 20); + this.tbNSwagPath.TabIndex = 7; + this.tbNSwagPath.TextChanged += new System.EventHandler(this.TbNSwagPath_TextChanged); + // + // lblNSwagPath + // + this.lblNSwagPath.AutoSize = true; + this.lblNSwagPath.Location = new System.Drawing.Point(13, 118); + this.lblNSwagPath.Name = "lblNSwagPath"; + this.lblNSwagPath.Size = new System.Drawing.Size(113, 13); + this.lblNSwagPath.TabIndex = 6; + this.lblNSwagPath.Text = "Full path to nswag.exe"; + // + // GeneralOptionsPageCustom + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.btnNSwagPath); + this.Controls.Add(this.tbNSwagPath); + this.Controls.Add(this.lblNSwagPath); + this.Controls.Add(this.btnNpmPath); + this.Controls.Add(this.tbNpmPath); + this.Controls.Add(this.lblNpmPath); + this.Controls.Add(this.btnJavaPath); + this.Controls.Add(this.tbJavaPath); + this.Controls.Add(this.lblJavaPath); + this.Name = "GeneralOptionsPageCustom"; + this.Size = new System.Drawing.Size(390, 176); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label lblJavaPath; + private System.Windows.Forms.TextBox tbJavaPath; + private System.Windows.Forms.Button btnJavaPath; + private System.Windows.Forms.Button btnNpmPath; + private System.Windows.Forms.TextBox tbNpmPath; + private System.Windows.Forms.Label lblNpmPath; + private System.Windows.Forms.Button btnNSwagPath; + private System.Windows.Forms.TextBox tbNSwagPath; + private System.Windows.Forms.Label lblNSwagPath; + } +} diff --git a/src/ApiClientCodeGen.VSIX/Windows/GeneralOptionsPageCustom.cs b/src/ApiClientCodeGen.VSIX/Windows/GeneralOptionsPageCustom.cs new file mode 100644 index 0000000000..9cc2a5087e --- /dev/null +++ b/src/ApiClientCodeGen.VSIX/Windows/GeneralOptionsPageCustom.cs @@ -0,0 +1,58 @@ +using System; +using System.IO; +using System.Windows.Forms; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options; + +namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Windows +{ + public partial class GeneralOptionsPageCustom : UserControl + { + private readonly GeneralOptionPage options; + + public GeneralOptionsPageCustom() + { + InitializeComponent(); + } + + public GeneralOptionsPageCustom( + GeneralOptionPage options) + : this() + { + this.options = options; + tbJavaPath.Text = options.JavaPath; + tbNpmPath.Text = options.NpmPath; + tbNSwagPath.Text = options.NSwagPath; + } + + private void OpenFileDialog(Control output) + { + using (var dialog = new OpenFileDialog()) + { + var result = dialog.ShowDialog(this); + if (result != DialogResult.OK) + return; + + if (File.Exists(dialog.FileName)) + output.Text = dialog.FileName; + } + } + + private void TbJavaPath_TextChanged(object sender, EventArgs e) + => options.JavaPath = tbJavaPath.Text; + + private void TbNpmPath_TextChanged(object sender, EventArgs e) + => options.NpmPath = tbNpmPath.Text; + + private void TbNSwagPath_TextChanged(object sender, EventArgs e) + => options.NSwagPath = tbNSwagPath.Text; + + private void BtnJavaPath_Click(object sender, EventArgs e) + => OpenFileDialog(tbJavaPath); + + private void BtnNpmPath_Click(object sender, EventArgs e) + => OpenFileDialog(tbNpmPath); + + private void BtnNSwagPath_Click(object sender, EventArgs e) + => OpenFileDialog(tbNSwagPath); + } +} diff --git a/src/ApiClientCodeGen.VSIX/Windows/GeneralOptionsPageCustom.resx b/src/ApiClientCodeGen.VSIX/Windows/GeneralOptionsPageCustom.resx new file mode 100644 index 0000000000..1af7de150c --- /dev/null +++ b/src/ApiClientCodeGen.VSIX/Windows/GeneralOptionsPageCustom.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/ApiClientCodegen.IntegrationTests/ApiClientCodegen.IntegrationTests.csproj b/src/ApiClientCodegen.IntegrationTests/ApiClientCodegen.IntegrationTests.csproj index 3993b0cf28..7d20dd1afa 100644 --- a/src/ApiClientCodegen.IntegrationTests/ApiClientCodegen.IntegrationTests.csproj +++ b/src/ApiClientCodegen.IntegrationTests/ApiClientCodegen.IntegrationTests.csproj @@ -42,9 +42,13 @@ ..\packages\Castle.Core.4.3.1\lib\net45\Castle.Core.dll + + ..\packages\DotLiquid.2.0.254\lib\net45\DotLiquid.dll + ..\packages\FluentAssertions.5.6.0\lib\net45\FluentAssertions.dll + ..\packages\Microsoft.VisualStudio.OLE.Interop.7.10.6071\lib\Microsoft.VisualStudio.OLE.Interop.dll @@ -66,12 +70,23 @@ ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll + + ..\packages\NJsonSchema.9.13.37\lib\net45\NJsonSchema.dll + + + ..\packages\NJsonSchema.CodeGeneration.9.13.37\lib\net451\NJsonSchema.CodeGeneration.dll + + + ..\packages\NJsonSchema.CodeGeneration.CSharp.9.13.37\lib\net451\NJsonSchema.CodeGeneration.CSharp.dll + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll + ..\packages\System.Threading.Tasks.Extensions.4.5.2\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll diff --git a/src/ApiClientCodegen.IntegrationTests/AutoRestCodeGeneratorTests.cs b/src/ApiClientCodegen.IntegrationTests/AutoRestCodeGeneratorTests.cs index 5fcf4f9f87..034d054282 100644 --- a/src/ApiClientCodegen.IntegrationTests/AutoRestCodeGeneratorTests.cs +++ b/src/ApiClientCodegen.IntegrationTests/AutoRestCodeGeneratorTests.cs @@ -9,32 +9,33 @@ namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.IntegrationTests { [TestClass] + [TestCategory("SkipWhenLiveUnitTesting")] [DeploymentItem("Resources/Swagger.json")] public class AutoRestCodeGeneratorTests { - private readonly Mock mock = new Mock(); - private string code = null; + private static readonly Mock mock = new Mock(); + private static string code = null; - [TestInitialize] - public void Init() + [ClassInitialize] + public static void Init(TestContext testContext) { var codeGenerator = new AutoRestCSharpCodeGenerator( Path.GetFullPath("Swagger.json"), - GetType().Namespace); + typeof(AutoRestCodeGeneratorTests).Namespace); code = codeGenerator.GenerateCode(mock.Object); } - [TestCleanup] - public void CleanUp() + [ClassCleanup] + public static void CleanUp() => DependencyUninstaller.UninstallAutoRest(); [TestMethod] - public void Generated_Code_NotNullOrWhitespace() + public void AutoRest_Generated_Code_NotNullOrWhitespace() => code.Should().NotBeNullOrWhiteSpace(); [TestMethod] - public void Reports_Progres() + public void AutoRest_Reports_Progres() => mock.Verify( c => c.Progress(It.IsAny(), It.IsAny()), Times.AtLeastOnce); diff --git a/src/ApiClientCodegen.IntegrationTests/NSwagCodeGeneratorTests.cs b/src/ApiClientCodegen.IntegrationTests/NSwagCodeGeneratorTests.cs index 393cf6e3bf..461d4ff4d8 100644 --- a/src/ApiClientCodegen.IntegrationTests/NSwagCodeGeneratorTests.cs +++ b/src/ApiClientCodegen.IntegrationTests/NSwagCodeGeneratorTests.cs @@ -1,5 +1,6 @@ using System.IO; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Generators.NSwag; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options; using FluentAssertions; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -8,30 +9,53 @@ namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.IntegrationTests { [TestClass] + [TestCategory("SkipWhenLiveUnitTesting")] [DeploymentItem("Resources/Swagger.json")] public class NSwagCodeGeneratorTests { - private readonly Mock mock = new Mock(); - private string code = null; + private static readonly Mock mock = new Mock(); + private static readonly Mock optionsMock = new Mock(); + private static string code = null; - [TestInitialize] - public void Init() + [ClassInitialize] + public static void Init(TestContext testContext) { var codeGenerator = new NSwagCSharpCodeGenerator( Path.GetFullPath("Swagger.json"), - GetType().Namespace); + typeof(NSwagCodeGeneratorTests).Namespace, + optionsMock.Object); code = codeGenerator.GenerateCode(mock.Object); } [TestMethod] - public void Generated_Code_NotNullOrWhitespace() + public void NSwag_Generated_Code_NotNullOrWhitespace() => code.Should().NotBeNullOrWhiteSpace(); [TestMethod] - public void Reports_Progres() + public void NSwag_Reports_Progres() => mock.Verify( - c => c.Progress(It.IsAny(), It.IsAny()), + c => c.Progress(It.IsAny(), It.IsAny()), Times.AtLeastOnce); + + [TestMethod] + public void Reads_InjectHttpClient_From_Options() + => optionsMock.Verify(c => c.InjectHttpClient); + + [TestMethod] + public void Reads_GenerateClientInterfaces_From_Options() + => optionsMock.Verify(c => c.GenerateClientInterfaces); + + [TestMethod] + public void Reads_GenerateDtoTypes_From_Options() + => optionsMock.Verify(c => c.GenerateDtoTypes); + + [TestMethod] + public void Reads_UseBaseUrl_From_Options() + => optionsMock.Verify(c => c.UseBaseUrl); + + [TestMethod] + public void Reads_ClassStyle_From_Options() + => optionsMock.Verify(c => c.ClassStyle); } } diff --git a/src/ApiClientCodegen.IntegrationTests/NSwagStudioCodeGeneratorTests.cs b/src/ApiClientCodegen.IntegrationTests/NSwagStudioCodeGeneratorTests.cs index 2a6e45413e..d2ee67d27a 100644 --- a/src/ApiClientCodegen.IntegrationTests/NSwagStudioCodeGeneratorTests.cs +++ b/src/ApiClientCodegen.IntegrationTests/NSwagStudioCodeGeneratorTests.cs @@ -1,6 +1,7 @@ using System.IO; using System.Threading.Tasks; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Generators.NSwagStudio; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options; using FluentAssertions; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -9,28 +10,38 @@ namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.IntegrationTests { [TestClass] + [TestCategory("SkipWhenLiveUnitTesting")] [DeploymentItem("Resources/Swagger.nswag")] [DeploymentItem("Resources/Swagger.json")] public class NSwagStudioCodeGeneratorTests { + private Mock optionsMock; + private IGeneralOptions options; + + [TestInitialize] + public void Init() + { + optionsMock = new Mock(); + optionsMock.Setup(c => c.NSwagPath).Returns(PathProvider.GetNSwagPath()); + options = optionsMock.Object; + } + [TestMethod] - public void IntegrationTest_Generate_Code_Using_NSwagStudio() - => new NSwagStudioCodeGenerator( - Path.GetFullPath("Swagger.nswag")) + public void NSwagStudio_Generate_Code_Using_NSwagStudio() + => new NSwagStudioCodeGenerator(Path.GetFullPath("Swagger.nswag"), options) .GenerateCode(new Mock().Object) .Should() .BeNull(); [TestMethod] - public async Task IntegrationTest_Generate_Code_Using_NSwagStudio_From_SwaggerSpec() + public async Task NSwagStudio_Generate_Code_Using_NSwagStudio_From_SwaggerSpec() { var contents = await NSwagStudioFileHelper.CreateNSwagStudioFileAsync( File.ReadAllText("Swagger.json"), "https://petstore.swagger.io/v2/swagger.json"); File.WriteAllText("Petstore.nswag", contents); - new NSwagStudioCodeGenerator( - Path.GetFullPath("Petstore.nswag")) + new NSwagStudioCodeGenerator(Path.GetFullPath("Petstore.nswag"), options) .GenerateCode(new Mock().Object) .Should() .BeNull(); @@ -39,5 +50,16 @@ public async Task IntegrationTest_Generate_Code_Using_NSwagStudio_From_SwaggerSp .Should() .BeTrue(); } + + [TestMethod] + public void Reads_NSwagPath_From_Options() + { + new NSwagStudioCodeGenerator( + Path.GetFullPath("Swagger.nswag"), + options) + .GenerateCode(new Mock().Object); + + optionsMock.Verify(c => c.NSwagPath); + } } } diff --git a/src/ApiClientCodegen.IntegrationTests/OpenApiCodeGeneratorTests.cs b/src/ApiClientCodegen.IntegrationTests/OpenApiCodeGeneratorTests.cs index b9a23d4f4e..c075c12a26 100644 --- a/src/ApiClientCodegen.IntegrationTests/OpenApiCodeGeneratorTests.cs +++ b/src/ApiClientCodegen.IntegrationTests/OpenApiCodeGeneratorTests.cs @@ -1,6 +1,7 @@ using System.IO; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Generators.OpenApi; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.IntegrationTests.Utility; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options; using FluentAssertions; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -9,34 +10,44 @@ namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.IntegrationTests { [TestClass] + [TestCategory("SkipWhenLiveUnitTesting")] [DeploymentItem("Resources/Swagger.json")] public class OpenApiCodeGeneratorTests { - private readonly Mock mock = new Mock(); - private string code = null; + private static readonly Mock mock = new Mock(); + private static Mock optionsMock; + private static string code = null; - [TestInitialize] - public void Init() + [ClassInitialize] + public static void Init(TestContext testContext) { + optionsMock = new Mock(); + optionsMock.Setup(c => c.NSwagPath).Returns(PathProvider.GetJavaPath()); + var codeGenerator = new OpenApiCSharpCodeGenerator( Path.GetFullPath("Swagger.json"), - GetType().Namespace); + typeof(OpenApiCodeGeneratorTests).Namespace, + optionsMock.Object); code = codeGenerator.GenerateCode(mock.Object); } - [TestCleanup] - public void CleanUp() + [ClassCleanup] + public static void CleanUp() => DependencyUninstaller.UninstallOpenApiGenerator(); [TestMethod] - public void Generated_Code_NotNullOrWhitespace() + public void OpenApi_Generated_Code_NotNullOrWhitespace() => code.Should().NotBeNullOrWhiteSpace(); [TestMethod] - public void Reports_Progres() + public void OpenApi_Reports_Progres() => mock.Verify( c => c.Progress(It.IsAny(), It.IsAny()), Times.AtLeastOnce); + + [TestMethod] + public void Reads_JavaPath_From_Options() + => optionsMock.Verify(c => c.JavaPath); } } diff --git a/src/ApiClientCodegen.IntegrationTests/SwaggerCodeGeneratorTests.cs b/src/ApiClientCodegen.IntegrationTests/SwaggerCodeGeneratorTests.cs index bce7e8f5ff..262c92be1f 100644 --- a/src/ApiClientCodegen.IntegrationTests/SwaggerCodeGeneratorTests.cs +++ b/src/ApiClientCodegen.IntegrationTests/SwaggerCodeGeneratorTests.cs @@ -1,6 +1,7 @@ using System.IO; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Generators.Swagger; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.IntegrationTests.Utility; +using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Options; using FluentAssertions; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -9,34 +10,44 @@ namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.IntegrationTests { [TestClass] + [TestCategory("SkipWhenLiveUnitTesting")] [DeploymentItem("Resources/Swagger.json")] public class SwaggerCodeGeneratorTests { - private readonly Mock mock = new Mock(); - private string code = null; + private static readonly Mock mock = new Mock(); + private static Mock optionsMock; + private static string code = null; - [TestInitialize] - public void Init() + [ClassInitialize] + public static void Init(TestContext testContext) { + optionsMock = new Mock(); + optionsMock.Setup(c => c.NSwagPath).Returns(PathProvider.GetJavaPath()); + var codeGenerator = new SwaggerCSharpCodeGenerator( Path.GetFullPath("Swagger.json"), - GetType().Namespace); + typeof(SwaggerCodeGeneratorTests).Namespace, + optionsMock.Object); code = codeGenerator.GenerateCode(mock.Object); } - [TestCleanup] - public void CleanUp() + [ClassCleanup] + public static void CleanUp() => DependencyUninstaller.UninstallSwaggerCodegen(); [TestMethod] - public void Generated_Code_NotNullOrWhitespace() + public void Swagger_Generated_Code_NotNullOrWhitespace() => code.Should().NotBeNullOrWhiteSpace(); [TestMethod] - public void Reports_Progres() + public void Swagger_Reports_Progres() => mock.Verify( - c => c.Progress(It.IsAny(), It.IsAny()), + c => c.Progress(It.IsAny(), It.IsAny()), Times.AtLeastOnce); + + [TestMethod] + public void Reads_JavaPath_From_Options() + => optionsMock.Verify(c => c.JavaPath); } } diff --git a/src/ApiClientCodegen.IntegrationTests/Utility/DependencyUninstaller.cs b/src/ApiClientCodegen.IntegrationTests/Utility/DependencyUninstaller.cs index a6e19a2630..60dd5d5d9a 100644 --- a/src/ApiClientCodegen.IntegrationTests/Utility/DependencyUninstaller.cs +++ b/src/ApiClientCodegen.IntegrationTests/Utility/DependencyUninstaller.cs @@ -1,11 +1,13 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Core; using ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.Generators; namespace ChristianHelle.DeveloperTools.CodeGenerators.ApiClient.IntegrationTests.Utility { + [ExcludeFromCodeCoverage] public static class DependencyUninstaller { public static void UninstallAutoRest() diff --git a/src/ApiClientCodegen.IntegrationTests/packages.config b/src/ApiClientCodegen.IntegrationTests/packages.config index 4b20216ca7..484be75188 100644 --- a/src/ApiClientCodegen.IntegrationTests/packages.config +++ b/src/ApiClientCodegen.IntegrationTests/packages.config @@ -1,6 +1,7 @@  + @@ -9,6 +10,9 @@ + + +