Merge branch 'main' into andrueastman/cleanupExtensions

This commit is contained in:
Vincent Biret 2024-07-26 07:52:46 -04:00 коммит произвёл GitHub
Родитель 4e374270b1 827d97d793
Коммит c33ac7f4ce
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
3 изменённых файлов: 28 добавлений и 17 удалений

Просмотреть файл

@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed a bug for the docker image where the volume path would not match the expected configuration for the description.
- Fixed a bug in Go where certain namespaces were escaped unexpectedly. [#5012](https://github.com/microsoft/kiota/issues/5012)
- Exclude the `x-openai-isConsequential` extension from cleanup. [#4962](https://github.com/microsoft/kiota/issues/4962)
- Fixed file name and namespace sanitization when generating plugins. [#5019](https://github.com/microsoft/kiota/issues/5019)
## [1.16.0] - 2024-07-05

Просмотреть файл

@ -4,6 +4,7 @@ using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Kiota.Builder.Configuration;
@ -18,7 +19,7 @@ using Microsoft.OpenApi.Writers;
using Microsoft.Plugins.Manifest;
namespace Kiota.Builder.Plugins;
public class PluginsGenerationService
public partial class PluginsGenerationService
{
private readonly OpenApiDocument OAIDocument;
private readonly OpenApiUrlTreeNode TreeNode;
@ -43,7 +44,9 @@ public class PluginsGenerationService
private const string AppManifestFileName = "manifest.json";
public async Task GenerateManifestAsync(CancellationToken cancellationToken = default)
{
// 1. write the OpenApi description
// 1. cleanup any namings to be used later on.
Configuration.ClientClassName = PluginNameCleanupRegex().Replace(Configuration.ClientClassName, string.Empty); //drop any special characters
// 2. write the OpenApi description
var descriptionRelativePath = $"{Configuration.ClientClassName.ToLowerInvariant()}-{DescriptionPathSuffix}";
var descriptionFullPath = Path.Combine(Configuration.OutputPath, descriptionRelativePath);
var directory = Path.GetDirectoryName(descriptionFullPath);
@ -58,7 +61,7 @@ public class PluginsGenerationService
trimmedPluginDocument.SerializeAsV3(descriptionWriter);
descriptionWriter.Flush();
// 2. write the plugins
// 3. write the plugins
foreach (var pluginType in Configuration.PluginTypes)
{
@ -98,7 +101,7 @@ public class PluginsGenerationService
await writer.FlushAsync(cancellationToken).ConfigureAwait(false);
}
// 3. write the app manifest if its an Api Plugin
// 4. write the app manifest if its an Api Plugin
if (Configuration.PluginTypes.Any(static plugin => plugin == PluginType.APIPlugin))
{
var manifestFullPath = Path.Combine(Configuration.OutputPath, AppManifestFileName);
@ -111,6 +114,9 @@ public class PluginsGenerationService
}
}
[GeneratedRegex(@"[^a-zA-Z0-9_]+", RegexOptions.IgnoreCase | RegexOptions.Singleline, 2000)]
private static partial Regex PluginNameCleanupRegex();
private async Task<AppManifestModel> GetAppManifestModelAsync(string pluginFileName, string manifestFullPath, CancellationToken cancellationToken)
{
var manifestInfo = ExtractInfoFromDocument(OAIDocument.Info);

Просмотреть файл

@ -33,8 +33,11 @@ public sealed class PluginsGenerationServiceTests : IDisposable
_httpClient.Dispose();
}
[Fact]
public async Task GeneratesManifest()
[Theory]
[InlineData("client", "client")]
[InlineData("Budget Tracker", "BudgetTracker")]//drop the space
[InlineData("My-Super complex() %@#$& Name", "MySupercomplexName")]//drop the space and special characters
public async Task GeneratesManifest(string inputPluginName, string expectedPluginName)
{
var simpleDescriptionContent = @"openapi: 3.0.0
info:
@ -76,7 +79,7 @@ paths:
OutputPath = outputDirectory,
OpenAPIFilePath = "openapiPath",
PluginTypes = [PluginType.APIPlugin, PluginType.APIManifest, PluginType.OpenAI],
ClientClassName = "client",
ClientClassName = inputPluginName,
ApiRootUrl = "http://localhost/", //Kiota builder would set this for us
};
var (openAPIDocumentStream, _) = await openAPIDocumentDS.LoadStreamAsync(simpleDescriptionPath, generationConfiguration, null, false);
@ -87,19 +90,20 @@ paths:
var pluginsGenerationService = new PluginsGenerationService(openApiDocument, urlTreeNode, generationConfiguration, workingDirectory);
await pluginsGenerationService.GenerateManifestAsync();
Assert.True(File.Exists(Path.Combine(outputDirectory, ManifestFileName)));
Assert.True(File.Exists(Path.Combine(outputDirectory, "client-apimanifest.json")));
Assert.True(File.Exists(Path.Combine(outputDirectory, $"{expectedPluginName.ToLower()}-apiplugin.json")));
Assert.True(File.Exists(Path.Combine(outputDirectory, $"{expectedPluginName.ToLower()}-apimanifest.json")));
Assert.True(File.Exists(Path.Combine(outputDirectory, OpenAIPluginFileName)));
Assert.True(File.Exists(Path.Combine(outputDirectory, OpenApiFileName)));
Assert.True(File.Exists(Path.Combine(outputDirectory, $"{expectedPluginName.ToLower()}-openapi.yml")));
Assert.True(File.Exists(Path.Combine(outputDirectory, AppManifestFileName)));
// Validate the v2 plugin
var manifestContent = await File.ReadAllTextAsync(Path.Combine(outputDirectory, ManifestFileName));
var manifestContent = await File.ReadAllTextAsync(Path.Combine(outputDirectory, $"{expectedPluginName.ToLower()}-apiplugin.json"));
using var jsonDocument = JsonDocument.Parse(manifestContent);
var resultingManifest = PluginManifestDocument.Load(jsonDocument.RootElement);
Assert.NotNull(resultingManifest.Document);
Assert.Equal(OpenApiFileName, resultingManifest.Document.Runtimes.OfType<OpenApiRuntime>().First().Spec.Url);
Assert.Equal($"{expectedPluginName.ToLower()}-openapi.yml", resultingManifest.Document.Runtimes.OfType<OpenApiRuntime>().First().Spec.Url);
Assert.Equal(2, resultingManifest.Document.Functions.Count);// all functions are generated despite missing operationIds
Assert.Equal(expectedPluginName, resultingManifest.Document.Namespace);// namespace is cleaned up.
Assert.Empty(resultingManifest.Problems);// no problems are expected with names
// Validate the v1 plugin
@ -107,20 +111,20 @@ paths:
using var v1JsonDocument = JsonDocument.Parse(v1ManifestContent);
var v1Manifest = PluginManifestDocument.Load(v1JsonDocument.RootElement);
Assert.NotNull(resultingManifest.Document);
Assert.Equal(OpenApiFileName, v1Manifest.Document.Api.URL);
Assert.Equal($"{expectedPluginName.ToLower()}-openapi.yml", v1Manifest.Document.Api.URL);
Assert.Empty(v1Manifest.Problems);
// Validate the manifest file
var appManifestFile = await File.ReadAllTextAsync(Path.Combine(outputDirectory, AppManifestFileName));
var appManifestModelObject = JsonSerializer.Deserialize(appManifestFile, PluginsGenerationService.AppManifestModelGenerationContext.AppManifestModel);
Assert.Equal("com.microsoft.kiota.plugin.client", appManifestModelObject.PackageName);
Assert.Equal("client", appManifestModelObject.Name.ShortName);
Assert.Equal($"com.microsoft.kiota.plugin.{expectedPluginName}", appManifestModelObject.PackageName);
Assert.Equal(expectedPluginName, appManifestModelObject.Name.ShortName);
Assert.Equal("Microsoft Kiota.", appManifestModelObject.Developer.Name);
Assert.Equal("color.png", appManifestModelObject.Icons.Color);
Assert.NotNull(appManifestModelObject.CopilotExtensions.Plugins);
Assert.Single(appManifestModelObject.CopilotExtensions.Plugins);
Assert.Equal("client", appManifestModelObject.CopilotExtensions.Plugins[0].Id);
Assert.Equal(ManifestFileName, appManifestModelObject.CopilotExtensions.Plugins[0].File);
Assert.Equal(expectedPluginName, appManifestModelObject.CopilotExtensions.Plugins[0].Id);
Assert.Equal($"{expectedPluginName.ToLower()}-apiplugin.json", appManifestModelObject.CopilotExtensions.Plugins[0].File);
}
private const string ManifestFileName = "client-apiplugin.json";
private const string OpenAIPluginFileName = "openai-plugins.json";