From 651b98cd7a415a5139e540dadba9911592bc71b7 Mon Sep 17 00:00:00 2001 From: Andrew Omondi Date: Fri, 26 Jul 2024 11:16:31 +0300 Subject: [PATCH 1/2] Generates default icons when not present --- CHANGELOG.md | 2 ++ resources/color.png | Bin 0 -> 8038 bytes resources/outline.png | Bin 0 -> 512 bytes src/Kiota.Builder/Kiota.Builder.csproj | 6 ++++ .../Plugins/PluginsGenerationService.cs | 32 ++++++++++++++++++ .../Plugins/PluginsGenerationServiceTests.cs | 8 +++++ 6 files changed, 48 insertions(+) create mode 100644 resources/color.png create mode 100644 resources/outline.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 30df685762..203e685c3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Adds generation of default 'color.png` and `outline.png` files when generating plugins. [#4993](https://github.com/microsoft/kiota/issues/4993) + ### Changed - Fixed a bug in dotnet where CS1587 warnings are generated in generated enums with descriptions [#4957](https://github.com/microsoft/kiota/issues/4957) diff --git a/resources/color.png b/resources/color.png new file mode 100644 index 0000000000000000000000000000000000000000..fea3285f4d8ef36a2044c844a28b2c564a1f135d GIT binary patch literal 8038 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 JS>7_@{{W+ISONe5 literal 0 HcmV?d00001 diff --git a/resources/outline.png b/resources/outline.png new file mode 100644 index 0000000000000000000000000000000000000000..6530cb998ae4620b6740fe992eb89f35d7587597 GIT binary patch literal 512 zcmV+b0{{JqP)Px$x=BPqR9HvNm%lGWK@i91MXr$e1GI{By}}=G_y?2Q7A|e6+xE*5vZRHMN zagR^x)G(wL8z4*oK|BT#M6qD(f81nv0B!X){y33C5^>L-UfyBtcqq2bRY3)y ztG2enRYZ$50fZ$u($S9*wd9&Go`_0wJ{7~sxi-)k$&f4nXFSe`)P&M+Yzz^=1yXvc zyxcSCohX3jaH*u+Ucf;V2k6Yt!P)k*ud<~LjkI94H3J`?U*lFMw;*h*VFK2BS8)6M z<`e#z38`3YDvFGuWh)M~#DY1~5IYaDw&K`$;^fldh2zXy@nCBSHcqeoyQ71yKkKzo z9X`CmX8*=^7-?bCZ!>=KFx41bA9pakJFf;G#HC0&if5oNMrzTCjn0Y0000..\Microsoft.OpenApi.snk true All + true @@ -49,6 +50,7 @@ + @@ -58,4 +60,8 @@ + + + + diff --git a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs index 0bb6641c57..02ab52cf5c 100644 --- a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs +++ b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Reflection; using System.Text.Json; using System.Text.Json.Serialization; using System.Threading; @@ -10,6 +11,7 @@ using Kiota.Builder.Extensions; using Kiota.Builder.OpenApiExtensions; using Kiota.Builder.Plugins.Models; +using Microsoft.Extensions.FileProviders; using Microsoft.Kiota.Abstractions.Extensions; using Microsoft.OpenApi.ApiManifest; using Microsoft.OpenApi.Models; @@ -111,6 +113,9 @@ public async Task GenerateManifestAsync(CancellationToken cancellationToken = de } } + private const string ColorFileName = "color.png"; + private const string OutlineFileName = "outline.png"; + private async Task GetAppManifestModelAsync(string pluginFileName, string manifestFullPath, CancellationToken cancellationToken) { var manifestInfo = ExtractInfoFromDocument(OAIDocument.Info); @@ -149,6 +154,33 @@ private async Task GetAppManifestModelAsync(string pluginFileN if (manifestModelFromFile != null) manifestModel = manifestModelFromFile; } + else + { + // The manifest file did not exist, so setup any dependencies needed. + // If it already existed, the user has setup them up in another way. + + // 1. Check if icons exist and write them out. + var embeddedProvider = new EmbeddedFileProvider(Assembly.GetExecutingAssembly()); + var iconFilePath = Path.Combine(Configuration.OutputPath, ColorFileName); + if (!File.Exists(iconFilePath)) + { +#pragma warning disable CA2007 + await using var reader = embeddedProvider.GetFileInfo(ColorFileName).CreateReadStream(); + await using var defaultColorFile = File.Open(iconFilePath, FileMode.Create); +#pragma warning restore CA2007 + await reader.CopyToAsync(defaultColorFile, cancellationToken).ConfigureAwait(false); + } + // 2. Check if outline exist and write them out. + var outlineFilePath = Path.Combine(Configuration.OutputPath, OutlineFileName); + if (!File.Exists(outlineFilePath)) + { +#pragma warning disable CA2007 + await using var reader = embeddedProvider.GetFileInfo(OutlineFileName).CreateReadStream(); + await using var defaultColorFile = File.Open(outlineFilePath, FileMode.Create); +#pragma warning restore CA2007 + await reader.CopyToAsync(defaultColorFile, cancellationToken).ConfigureAwait(false); + } + } manifestModel.CopilotExtensions ??= new CopilotExtensions();// ensure its not null. diff --git a/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs b/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs index c04055de9e..ee642bce54 100644 --- a/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs +++ b/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs @@ -92,6 +92,8 @@ public async Task GeneratesManifest() Assert.True(File.Exists(Path.Combine(outputDirectory, OpenAIPluginFileName))); Assert.True(File.Exists(Path.Combine(outputDirectory, OpenApiFileName))); Assert.True(File.Exists(Path.Combine(outputDirectory, AppManifestFileName))); + Assert.True(File.Exists(Path.Combine(outputDirectory, "color.png"))); + Assert.True(File.Exists(Path.Combine(outputDirectory, "outline.png"))); // Validate the v2 plugin var manifestContent = await File.ReadAllTextAsync(Path.Combine(outputDirectory, ManifestFileName)); @@ -503,6 +505,8 @@ public async Task GeneratesManifestAndUpdatesExistingAppManifest() Assert.True(File.Exists(Path.Combine(outputDirectory, ManifestFileName))); Assert.True(File.Exists(Path.Combine(outputDirectory, OpenApiFileName))); + Assert.False(File.Exists(Path.Combine(outputDirectory, "color.png"))); // manifest already existed and specifed the path to a file, so we did not add it. + Assert.False(File.Exists(Path.Combine(outputDirectory, "outline.png")));// manifest already existed and specifed the path to a file, so we did not add it. Assert.True(File.Exists(Path.Combine(outputDirectory, "manifest.json")));// Assert manifest exists after generation // Validate the manifest file @@ -636,6 +640,8 @@ public async Task GeneratesManifestAndUpdatesExistingAppManifestWithExistingPlug Assert.True(File.Exists(Path.Combine(outputDirectory, ManifestFileName))); Assert.True(File.Exists(Path.Combine(outputDirectory, OpenApiFileName))); Assert.True(File.Exists(Path.Combine(outputDirectory, "manifest.json")));// Assert manifest exists after generation + Assert.False(File.Exists(Path.Combine(outputDirectory, "color.png"))); // manifest already existed and specifed the path to a file, so we did not add it. + Assert.False(File.Exists(Path.Combine(outputDirectory, "outline.png")));// manifest already existed and specifed the path to a file, so we did not add it. // Validate the manifest file var appManifestFile = await File.ReadAllTextAsync(Path.Combine(outputDirectory, AppManifestFileName)); @@ -739,6 +745,8 @@ public async Task GeneratesManifestAndCleansUpInputDescription() Assert.True(File.Exists(Path.Combine(outputDirectory, ManifestFileName))); Assert.True(File.Exists(Path.Combine(outputDirectory, OpenApiFileName))); + Assert.True(File.Exists(Path.Combine(outputDirectory, "color.png"))); + Assert.True(File.Exists(Path.Combine(outputDirectory, "outline.png"))); // Validate the v2 plugin var manifestContent = await File.ReadAllTextAsync(Path.Combine(outputDirectory, ManifestFileName)); From f3060f4eb007517836d07ba1955a214db9ff9003 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 26 Jul 2024 08:09:03 -0400 Subject: [PATCH 2/2] chore: refactors file copy to avoid duplication --- .../Plugins/PluginsGenerationService.cs | 34 ++++++++----------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs index 72b18b7a90..c446a99ad1 100644 --- a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs +++ b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs @@ -166,25 +166,8 @@ private async Task GetAppManifestModelAsync(string pluginFileN // 1. Check if icons exist and write them out. var embeddedProvider = new EmbeddedFileProvider(Assembly.GetExecutingAssembly()); - var iconFilePath = Path.Combine(Configuration.OutputPath, ColorFileName); - if (!File.Exists(iconFilePath)) - { -#pragma warning disable CA2007 - await using var reader = embeddedProvider.GetFileInfo(ColorFileName).CreateReadStream(); - await using var defaultColorFile = File.Open(iconFilePath, FileMode.Create); -#pragma warning restore CA2007 - await reader.CopyToAsync(defaultColorFile, cancellationToken).ConfigureAwait(false); - } - // 2. Check if outline exist and write them out. - var outlineFilePath = Path.Combine(Configuration.OutputPath, OutlineFileName); - if (!File.Exists(outlineFilePath)) - { -#pragma warning disable CA2007 - await using var reader = embeddedProvider.GetFileInfo(OutlineFileName).CreateReadStream(); - await using var defaultColorFile = File.Open(outlineFilePath, FileMode.Create); -#pragma warning restore CA2007 - await reader.CopyToAsync(defaultColorFile, cancellationToken).ConfigureAwait(false); - } + await CopyResourceFileToDirectoryIfNotExistsAsync(ColorFileName, embeddedProvider, cancellationToken).ConfigureAwait(false); + await CopyResourceFileToDirectoryIfNotExistsAsync(OutlineFileName, embeddedProvider, cancellationToken).ConfigureAwait(false); } manifestModel.CopilotExtensions ??= new CopilotExtensions();// ensure its not null. @@ -206,7 +189,18 @@ private async Task GetAppManifestModelAsync(string pluginFileN return manifestModel; } - + private async Task CopyResourceFileToDirectoryIfNotExistsAsync(string fileName, EmbeddedFileProvider embeddedProvider, CancellationToken cancellationToken) + { + var targetPath = Path.Combine(Configuration.OutputPath, fileName); + if (!File.Exists(targetPath)) + { +#pragma warning disable CA2007 + await using var reader = embeddedProvider.GetFileInfo(fileName).CreateReadStream(); + await using var defaultColorFile = File.Open(targetPath, FileMode.Create); +#pragma warning restore CA2007 + await reader.CopyToAsync(defaultColorFile, cancellationToken).ConfigureAwait(false); + } + } internal static readonly AppManifestModelGenerationContext AppManifestModelGenerationContext = new(new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase,