Merge pull request #5028 from microsoft/andrueastman/cleanupExtensions

Exclude the `x-openai-isConsequential` extension from plugin cleanup
This commit is contained in:
Vincent Biret 2024-07-26 07:57:43 -04:00 коммит произвёл GitHub
Родитель 827d97d793 c33ac7f4ce
Коммит 1983ca4d02
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
6 изменённых файлов: 101 добавлений и 31 удалений

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

@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed a bug where the copilot teams toolkit integration would serialize empty declarative copilots. [#4974](https://github.com/microsoft/kiota/issues/4974)
- 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

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

@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Kiota.Builder.OpenApiExtensions;
using Microsoft.OpenApi.Readers;
@ -18,4 +20,24 @@ public static class OpenApiSettingsExtensions
settings.ExtensionParsers.TryAdd(OpenApiAiReasoningInstructionsExtension.Name, static (i, _) => OpenApiAiReasoningInstructionsExtension.Parse(i));
settings.ExtensionParsers.TryAdd(OpenApiAiRespondingInstructionsExtension.Name, static (i, _) => OpenApiAiRespondingInstructionsExtension.Parse(i));
}
public static void AddGenerationExtensions(this OpenApiReaderSettings settings)
{
ArgumentNullException.ThrowIfNull(settings);
settings.AddMicrosoftExtensionParsers();
settings.ExtensionParsers.TryAdd(OpenApiKiotaExtension.Name, static (i, _) => OpenApiKiotaExtension.Parse(i));
}
public static HashSet<string> KiotaSupportedExtensions()
{
var dummySettings = new OpenApiReaderSettings();
dummySettings.AddGenerationExtensions();
dummySettings.AddPluginsExtensions();
var supportedExtensions = dummySettings.ExtensionParsers.Keys.ToHashSet(StringComparer.OrdinalIgnoreCase);
supportedExtensions.Add("x-openai-isConsequential");// add extension we don't parse to the list
return supportedExtensions;
}
}

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

@ -112,10 +112,12 @@ internal class OpenApiDocumentDownloadService
{
RuleSet = ruleSet,
};
settings.AddMicrosoftExtensionParsers();
// Add all extensions for generation
settings.AddGenerationExtensions();
if (config.IsPluginConfiguration)
settings.AddPluginsExtensions();
settings.ExtensionParsers.TryAdd(OpenApiKiotaExtension.Name, static (i, _) => OpenApiKiotaExtension.Parse(i));
settings.AddPluginsExtensions();// Add all extensions for plugins
try
{
var rawUri = config.OpenAPIFilePath.TrimEnd(KiotaBuilder.ForwardSlash);

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

@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Kiota.Builder.Extensions;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Services;
namespace Kiota.Builder.Plugins;
public class OpenApiPluginWalker : OpenApiVisitorBase
{
private static readonly HashSet<string> SupportedExtensions = OpenApiSettingsExtensions.KiotaSupportedExtensions();
/// <summary>
/// Visits <see cref="OpenApiSchema"/>
/// </summary>
public override void Visit(IOpenApiExtensible openApiExtensible)
{
ArgumentNullException.ThrowIfNull(openApiExtensible);
// remove any extensions we do not support
foreach (var extension in openApiExtensible.Extensions.Where(static extension => !SupportedExtensions.Contains(extension.Key)))
{
openApiExtensible.Extensions.Remove(extension.Key);
}
}
/// <summary>
/// Visits the operations.
/// </summary>
public override void Visit(IDictionary<OperationType, OpenApiOperation> operations)
{
ArgumentNullException.ThrowIfNull(operations);
// Cleanup responses for the operation
foreach (var operation in operations.Values)
{
var responseDescription = operation.Responses.Values.Select(static response => response.Description)
.FirstOrDefault(static desc => !string.IsNullOrEmpty(desc)) ?? "Api Response";
operation.Responses = new OpenApiResponses()
{
{
"2XX",new OpenApiResponse
{
Description = responseDescription,
Content = new Dictionary<string, OpenApiMediaType>
{
{
"text/plain", new OpenApiMediaType
{
Schema = new OpenApiSchema
{
Type = "string"
}
}
}
}
}
}
};
}
}
}

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

@ -192,35 +192,11 @@ public partial class PluginsGenerationService
if (string.IsNullOrEmpty(doc.Info?.Version)) // filtering fails if there's no version.
doc.Info!.Version = "1.0";
//empty out all the responses with a single empty 2XX
foreach (var operation in doc.Paths.SelectMany(static item => item.Value.Operations.Values))
{
var responseDescription = operation.Responses.Values.Select(static response => response.Description)
.FirstOrDefault(static desc => !string.IsNullOrEmpty(desc)) ?? "Api Response";
operation.Responses = new OpenApiResponses()
{
{
"2XX",new OpenApiResponse
{
Description = responseDescription,
Content = new Dictionary<string, OpenApiMediaType>
{
{
"text/plain", new OpenApiMediaType
{
Schema = new OpenApiSchema
{
Type = "string"
}
}
}
}
}
}
};
}
//empty out all the responses with a single empty 2XX and cleanup the extensions
var openApiWalker = new OpenApiWalker(new OpenApiPluginWalker());
openApiWalker.Walk(doc);
// remove unused components using the OpenApi.Net
// remove unused components using the OpenApi.Net library
var requestUrls = new Dictionary<string, List<string>>();
var basePath = doc.GetAPIRootUrl(Configuration.OpenAPIFilePath);
foreach (var path in doc.Paths.Where(static path => path.Value.Operations.Count > 0))

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

@ -685,6 +685,7 @@ paths:
/test:
get:
description: description for test path
x-random-extension: true
responses:
'200':
description: test
@ -694,6 +695,7 @@ paths:
get:
description: description for test path with id
operationId: test.WithId
x-openai-isConsequential: true
parameters:
- name: id
in: path
@ -764,7 +766,9 @@ components:
Assert.Single(originalDocument.Extensions); // single unsupported extension at root
Assert.Equal(2, originalDocument.Paths.Count); // document has only two paths
Assert.Equal(2, originalDocument.Paths["/test"].Operations[OperationType.Get].Responses.Count); // 2 responses originally
Assert.Single(originalDocument.Paths["/test"].Operations[OperationType.Get].Extensions); // 1 UNsupported extension
Assert.Equal(2, originalDocument.Paths["/test/{id}"].Operations[OperationType.Get].Responses.Count); // 2 responses originally
Assert.Single(originalDocument.Paths["/test/{id}"].Operations[OperationType.Get].Extensions); // 1 supported extension
// Validate the output open api file
var resultOpenApiFile = File.OpenRead(Path.Combine(outputDirectory, OpenApiFileName));
@ -777,7 +781,9 @@ components:
Assert.Equal(2, resultDocument.Paths.Count); // document has only two paths
Assert.Single(resultDocument.Paths["/test"].Operations[OperationType.Get].Responses); // other responses are removed from the document
Assert.NotEmpty(resultDocument.Paths["/test"].Operations[OperationType.Get].Responses["2XX"].Description); // response description string is not empty
Assert.Empty(resultDocument.Paths["/test"].Operations[OperationType.Get].Extensions); // NO UNsupported extension
Assert.Single(resultDocument.Paths["/test/{id}"].Operations[OperationType.Get].Responses); // 2 responses originally
Assert.NotEmpty(resultDocument.Paths["/test/{id}"].Operations[OperationType.Get].Responses["2XX"].Description);// response description string is not empty
Assert.Single(resultDocument.Paths["/test/{id}"].Operations[OperationType.Get].Extensions); // 1 supported extension still present in operation
}
}