Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Manifest library updates #4499

Merged
merged 10 commits into from
Apr 19, 2024
44 changes: 35 additions & 9 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,10 @@
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/src/kiota/bin/Debug/net8.0/kiota.dll",
"args": ["search", "microsoft"],
"args": [
"search",
"microsoft"
],
"cwd": "${workspaceFolder}/src/kiota",
"console": "internalConsole",
"stopAtEntry": false
Expand All @@ -207,7 +210,10 @@
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/src/kiota/bin/Debug/net8.0/kiota.dll",
"args": ["search", "test"],
"args": [
"search",
"test"
],
"cwd": "${workspaceFolder}/src/kiota",
"console": "internalConsole",
"stopAtEntry": false
Expand Down Expand Up @@ -249,7 +255,11 @@
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/src/kiota/bin/Debug/net8.0/kiota.dll",
"args": ["info", "-l", "CSharp"],
"args": [
"info",
"-l",
"CSharp"
],
"cwd": "${workspaceFolder}/src/kiota",
"console": "internalConsole",
"stopAtEntry": false
Expand All @@ -260,7 +270,11 @@
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/src/kiota/bin/Debug/net8.0/kiota.dll",
"args": ["update", "-o", "${workspaceFolder}/samples"],
"args": [
"update",
"-o",
"${workspaceFolder}/samples"
],
"cwd": "${workspaceFolder}/src/kiota",
"console": "internalConsole",
"stopAtEntry": false
Expand All @@ -271,7 +285,10 @@
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/src/kiota/bin/Debug/net8.0/kiota.dll",
"args": ["workspace", "migrate"],
"args": [
"workspace",
"migrate"
],
"cwd": "${workspaceFolder}/samples/msgraph-mail/dotnet",
"console": "internalConsole",
"stopAtEntry": false,
Expand All @@ -285,7 +302,10 @@
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/src/kiota/bin/Debug/net8.0/kiota.dll",
"args": ["client", "generate"],
"args": [
"client",
"generate"
],
"cwd": "${workspaceFolder}/samples/msgraph-mail/dotnet",
"console": "internalConsole",
"stopAtEntry": false,
Expand Down Expand Up @@ -330,7 +350,9 @@
"-i",
"**/messages",
"--type",
"APIManifest"
"ApiManifest",
"--type",
"microsoft"
],
"cwd": "${workspaceFolder}/samples/msgraph-mail/dotnet",
"console": "internalConsole",
Expand All @@ -345,7 +367,11 @@
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/src/kiota/bin/Debug/net8.0/kiota.dll",
"args": ["login", "github", "device"],
"args": [
"login",
"github",
"device"
],
"cwd": "${workspaceFolder}/src/kiota",
"console": "internalConsole",
"stopAtEntry": false
Expand All @@ -357,4 +383,4 @@
"processId": "${command:pickProcess}"
}
]
}
}
2 changes: 1 addition & 1 deletion src/Kiota.Builder/Kiota.Builder.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
<PackageReference Include="Microsoft.OpenApi" Version="1.6.14" />
<PackageReference Include="Microsoft.OpenApi.ApiManifest" Version="0.5.4-preview" />
<PackageReference Include="Microsoft.OpenApi.Readers" Version="1.6.14" />
<PackageReference Include="Microsoft.Plugins.Manifest" Version="0.0.1-preview" />
<PackageReference Include="Microsoft.Plugins.Manifest" Version="0.0.6-preview" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
<PackageReference Include="YamlDotNet" Version="15.1.2" />
<ProjectReference Include="..\Kiota.Generated\KiotaGenerated.csproj" OutputItemType="Analyzer"
Expand Down
3 changes: 2 additions & 1 deletion src/Kiota.Builder/PluginType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
public enum PluginType
{
OpenAI,
APIManifest
APIManifest,
Microsoft
}
2 changes: 1 addition & 1 deletion src/Kiota.Builder/Plugins/AuthComparer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ public bool Equals(Auth? x, Auth? y)
public int GetHashCode([DisallowNull] Auth obj)
{
if (obj == null) return 0;
return string.IsNullOrEmpty(obj.Type) ? 0 : StringComparer.OrdinalIgnoreCase.GetHashCode(obj.Type) * 3;
return obj.Type is null ? 0 : StringComparer.OrdinalIgnoreCase.GetHashCode(obj.Type.Value.ToString()) * 3;
}
}
9 changes: 5 additions & 4 deletions src/Kiota.Builder/Plugins/OpenAPiRuntimeComparer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,26 @@

namespace Kiota.Builder.Plugins;

internal class OpenAPIRuntimeComparer : IEqualityComparer<OpenAPIRuntime>
internal class OpenAPIRuntimeComparer : IEqualityComparer<OpenApiRuntime>
{
public bool EvaluateFunctions
{
get; init;
}
private static readonly StringIEnumerableDeepComparer _stringIEnumerableDeepComparer = new();
private static readonly AuthComparer _authComparer = new();
private static readonly OpenApiRuntimeSpecComparer _openApiRuntimeSpecComparer = new();
/// <inheritdoc/>
public bool Equals(OpenAPIRuntime? x, OpenAPIRuntime? y)
public bool Equals(OpenApiRuntime? x, OpenApiRuntime? y)
{
return x == null && y == null || x != null && y != null && GetHashCode(x) == GetHashCode(y);
}
/// <inheritdoc/>
public int GetHashCode([DisallowNull] OpenAPIRuntime obj)
public int GetHashCode([DisallowNull] OpenApiRuntime obj)
{
if (obj == null) return 0;
return (EvaluateFunctions ? _stringIEnumerableDeepComparer.GetHashCode(obj.RunForFunctions ?? Enumerable.Empty<string>()) * 7 : 0) +
obj.Spec.Select(static x => StringComparer.Ordinal.GetHashCode($"{x.Key}:{x.Value}")).Aggregate(0, (acc, next) => acc + next) * 5 +
(obj.Spec is null ? 0 : _openApiRuntimeSpecComparer.GetHashCode(obj.Spec) * 5) +
(obj.Auth is null ? 0 : _authComparer.GetHashCode(obj.Auth) * 3);
}
}
23 changes: 23 additions & 0 deletions src/Kiota.Builder/Plugins/OpenApiRuntimeSpecComparer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.Plugins.Manifest;

namespace Kiota.Builder.Plugins;

public class OpenApiRuntimeSpecComparer : IEqualityComparer<OpenApiRuntimeSpec>
{
/// <inheritdoc/>
public bool Equals(OpenApiRuntimeSpec? x, OpenApiRuntimeSpec? y)
{
return x == null && y == null || x != null && y != null && GetHashCode(x) == GetHashCode(y);
}
/// <inheritdoc/>
public int GetHashCode([DisallowNull] OpenApiRuntimeSpec obj)
{
if (obj == null) return 0;
return (string.IsNullOrEmpty(obj.Url) ? 0 : StringComparer.OrdinalIgnoreCase.GetHashCode(obj.Url) * 5) +
(string.IsNullOrEmpty(obj.ApiDescription) ? 0 : StringComparer.OrdinalIgnoreCase.GetHashCode(obj.ApiDescription) * 3);
}
}
96 changes: 64 additions & 32 deletions src/Kiota.Builder/Plugins/PluginsGenerationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
using Kiota.Builder.Configuration;
using Kiota.Builder.Extensions;
using Kiota.Builder.OpenApiExtensions;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.OpenApi.ApiManifest;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Services;
using Microsoft.OpenApi.Writers;
Expand All @@ -30,31 +32,52 @@
Configuration = configuration;
}
private static readonly OpenAPIRuntimeComparer _openAPIRuntimeComparer = new();
private const string ManifestFileName = "manifest.json";
private const string ManifestFileNameSuffix = ".json";
private const string DescriptionRelativePath = "./openapi.yml";
public async Task GenerateManifestAsync(CancellationToken cancellationToken = default)
{
var manifestOutputPath = Path.Combine(Configuration.OutputPath, ManifestFileName);
var directory = Path.GetDirectoryName(manifestOutputPath);
// write the decription
var descriptionFullPath = Path.Combine(Configuration.OutputPath, DescriptionRelativePath);
var directory = Path.GetDirectoryName(descriptionFullPath);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
Directory.CreateDirectory(directory);

var descriptionFullPath = Path.Combine(Configuration.OutputPath, DescriptionRelativePath);
#pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task
await using var descriptionStream = File.Create(descriptionFullPath, 4096);
await using var fileWriter = new StreamWriter(descriptionStream);
#pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task
var descriptionWriter = new OpenApiYamlWriter(fileWriter);
OAIDocument.SerializeAsV3(descriptionWriter);
descriptionWriter.Flush();

var pluginDocument = GetManifestDocument(DescriptionRelativePath);
await using var fileStream = File.Create(manifestOutputPath, 4096);
await using var writer = new Utf8JsonWriter(fileStream, new JsonWriterOptions { Indented = true });
// write the plugins
foreach (var pluginType in Configuration.PluginTypes)
{
var manifestOutputPath = Path.Combine(Configuration.OutputPath, $"{Configuration.ClientClassName.ToLowerInvariant()}-{pluginType.ToString().ToLowerInvariant()}{ManifestFileNameSuffix}");
#pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task
await using var fileStream = File.Create(manifestOutputPath, 4096);
await using var writer = new Utf8JsonWriter(fileStream, new JsonWriterOptions { Indented = true });
#pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task
pluginDocument.Write(writer);
await writer.FlushAsync(cancellationToken).ConfigureAwait(false);

switch (pluginType)
{
case PluginType.Microsoft:
var pluginDocument = GetManifestDocument(DescriptionRelativePath);
pluginDocument.Write(writer);
break;
case PluginType.APIManifest:
var apiManifest = new ApiManifestDocument("application"); //TODO add application name

Check warning on line 68 in src/Kiota.Builder/Plugins/PluginsGenerationService.cs

View workflow job for this annotation

GitHub Actions / Build

Complete the task associated to this 'TODO' comment. (https://rules.sonarsource.com/csharp/RSPEC-1135)
apiManifest.ApiDependencies.AddOrReplace(Configuration.ClientClassName, Configuration.ToApiDependency(OAIDocument.HashCode ?? string.Empty, TreeNode?.GetRequestInfo().ToDictionary(static x => x.Key, static x => x.Value) ?? []));
apiManifest.Write(writer);
break;
case PluginType.OpenAI:
andrueastman marked this conversation as resolved.
Show resolved Hide resolved
//TODO add support for OpenAI plugin type generation

Check warning on line 73 in src/Kiota.Builder/Plugins/PluginsGenerationService.cs

View workflow job for this annotation

GitHub Actions / Build

Complete the task associated to this 'TODO' comment. (https://rules.sonarsource.com/csharp/RSPEC-1135)
default:
continue;
}
await writer.FlushAsync(cancellationToken).ConfigureAwait(false);
}
}
private ManifestDocument GetManifestDocument(string openApiDocumentPath)
private PluginManifestDocument GetManifestDocument(string openApiDocumentPath)
{
var (runtimes, functions) = GetRuntimesAndFunctionsFromTree(TreeNode, openApiDocumentPath);
var descriptionForHuman = OAIDocument.Info?.Description.CleanupXMLString() is string d && !string.IsNullOrEmpty(d) ? d : $"Description for {OAIDocument.Info?.Title.CleanupXMLString()}";
Expand All @@ -64,7 +87,6 @@
string? privacyUrl = null;
if (OAIDocument.Info is not null)
{

if (OAIDocument.Info.Extensions.TryGetValue(OpenApiDescriptionForModelExtension.Name, out var descriptionExtension) &&
descriptionExtension is OpenApiDescriptionForModelExtension extension &&
!string.IsNullOrEmpty(extension.Description))
Expand All @@ -76,11 +98,11 @@
if (OAIDocument.Info.Extensions.TryGetValue(OpenApiPrivacyPolicyUrlExtension.Name, out var privacyExtension) && privacyExtension is OpenApiPrivacyPolicyUrlExtension privacy)
privacyUrl = privacy.Privacy;
}
return new ManifestDocument
return new PluginManifestDocument
{
SchemaVersion = "v2",
NameForHuman = OAIDocument.Info?.Title.CleanupXMLString(),
// TODO name for model ???

Check warning on line 105 in src/Kiota.Builder/Plugins/PluginsGenerationService.cs

View workflow job for this annotation

GitHub Actions / Build

Complete the task associated to this 'TODO' comment. (https://rules.sonarsource.com/csharp/RSPEC-1135)
DescriptionForHuman = descriptionForHuman,
DescriptionForModel = descriptionForModel,
ContactEmail = OAIDocument.Info?.Contact?.Email,
Expand All @@ -100,42 +122,52 @@
Functions = [.. functions.OrderBy(static x => x.Name, StringComparer.OrdinalIgnoreCase)]
};
}
private (OpenAPIRuntime[], Function[]) GetRuntimesAndFunctionsFromTree(OpenApiUrlTreeNode currentNode, string openApiDocumentPath)
private (OpenApiRuntime[], Function[]) GetRuntimesAndFunctionsFromTree(OpenApiUrlTreeNode currentNode, string openApiDocumentPath)
{
var runtimes = new List<OpenAPIRuntime>();
var runtimes = new List<OpenApiRuntime>();
var functions = new List<Function>();
if (currentNode.PathItems.TryGetValue(Constants.DefaultOpenApiLabel, out var pathItem))
{
foreach (var operation in pathItem.Operations.Values.Where(static x => !string.IsNullOrEmpty(x.OperationId)))
{
runtimes.Add(new OpenAPIRuntime
runtimes.Add(new OpenApiRuntime
{
Auth = new Auth("none"),
Spec = new Dictionary<string, string> { { "url", openApiDocumentPath } },
Auth = new AnonymousAuth(),
Spec = new OpenApiRuntimeSpec()
{
Url = openApiDocumentPath
},
RunForFunctions = [operation.OperationId]
});
var oasParameters = operation.Parameters
.Union(pathItem.Parameters.Where(static x => x.In is ParameterLocation.Path))
.Where(static x => x.Schema?.Type is not null && scalarTypes.Contains(x.Schema.Type))
.ToArray();
//TODO add request body

Check warning on line 146 in src/Kiota.Builder/Plugins/PluginsGenerationService.cs

View workflow job for this annotation

GitHub Actions / Build

Complete the task associated to this 'TODO' comment. (https://rules.sonarsource.com/csharp/RSPEC-1135)

functions.Add(new Function
{
Name = operation.OperationId,
Description = operation.Summary.CleanupXMLString() is string summary && !string.IsNullOrEmpty(summary) ? summary : operation.Description.CleanupXMLString(),
Parameters = oasParameters.Length == 0 ? null :
new Parameters(
"object",
new Properties(oasParameters.ToDictionary(
static x => x.Name,
static x => new Property(
x.Schema.Type ?? string.Empty,
x.Description.CleanupXMLString(),
x.Schema.Default?.ToString() ?? string.Empty,
null), //TODO enums
StringComparer.OrdinalIgnoreCase)),
oasParameters.Where(static x => x.Required).Select(static x => x.Name).ToList()),
Description =
operation.Summary.CleanupXMLString() is string summary && !string.IsNullOrEmpty(summary)
? summary
: operation.Description.CleanupXMLString(),
Parameters = oasParameters.Length == 0
? null
: new Parameters
{
Type = "object",
Properties = new Properties(oasParameters.ToDictionary(
static x => x.Name,
static x => new FunctionParameter()
{
Type = x.Schema.Type ?? string.Empty,
Description = x.Description.CleanupXMLString(),
Default = x.Schema.Default?.ToString() ?? string.Empty,
//TODO enums
})),
Required = oasParameters.Where(static x => x.Required).Select(static x => x.Name).ToList()
},
States = GetStatesFromOperation(operation),
});
}
Expand Down Expand Up @@ -177,7 +209,7 @@
{
return new State
{
Instructions = instructionsExtractor(rExt).Where(static x => !string.IsNullOrEmpty(x)).Select(static x => x.CleanupXMLString()).ToList()
Instructions = new Instructions(instructionsExtractor(rExt).Where(static x => !string.IsNullOrEmpty(x)).Select(static x => x.CleanupXMLString()).ToList())
};
}
return null;
Expand Down
1 change: 0 additions & 1 deletion src/kiota/Handlers/Plugin/AddHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,5 @@ public override async Task<int> InvokeAsync(InvocationContext context)
#endif
}
}
throw new NotImplementedException();
}
}
2 changes: 1 addition & 1 deletion src/kiota/kiota.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" />
<PackageReference Include="Microsoft.OpenApi.ApiManifest" Version="0.5.4-preview" />
<PackageReference Include="Microsoft.OpenApi.ApiManifest" Version="0.5.5-preview" />
<PackageReference Include="StreamJsonRpc" Version="2.17.11" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ public void Defensive()
[Fact]
public void GetsHashCode()
{
var runtime1 = new OpenAPIRuntime { Spec = new() { { "key1", "value1" } } };
var runtime2 = new OpenAPIRuntime { Spec = new() { { "key2", "value2" } }, Auth = new() { Type = "type" } };
var runtime1 = new OpenApiRuntime { Spec = new() { Url = "url", ApiDescription = "description" } };
var runtime2 = new OpenApiRuntime { Spec = new() { Url = "url", ApiDescription = "description" }, Auth = new AnonymousAuth() };
Assert.NotEqual(_comparer.GetHashCode(runtime1), _comparer.GetHashCode(runtime2));
}
}
Loading
Loading