// Copyright (c) Microsoft. All rights reserved. using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.Plugins.MsGraph.Connectors.CredentialManagers; using Microsoft.SemanticKernel.Plugins.OpenApi; using Microsoft.SemanticKernel.Plugins.OpenApi.Extensions; using Xunit; using Xunit.Abstractions; namespace Examples; // This example shows how to use the ApiManifest based plugins public class Example83_ApiManifest : BaseTest { public Example83_ApiManifest(ITestOutputHelper output) : base(output) { } public static readonly IEnumerable s_parameters = new List { // function names are sanitized operationIds from the OpenAPI document new object[] { "MessagesPlugin", "meListMessages", new KernelArguments { { "_top", "1" } }, "MessagesPlugin" }, }; [Theory, MemberData(nameof(s_parameters))] public async Task RunSampleWithPlannerAsync(string pluginToTest, string functionToTest, KernelArguments? arguments, params string[] pluginsToLoad) { WriteSampleHeadingToConsole(pluginToTest, functionToTest, arguments, pluginsToLoad); var kernel = Kernel.CreateBuilder().Build(); await AddApiManifestPluginsAsync(kernel, pluginsToLoad); var result = await kernel.InvokeAsync(pluginToTest, functionToTest, arguments); this.WriteLine("--------------------"); this.WriteLine($"\nResult:\n{result}\n"); this.WriteLine("--------------------"); } private void WriteSampleHeadingToConsole(string pluginToTest, string functionToTest, KernelArguments? arguments, params string[] pluginsToLoad) { this.WriteLine(); this.WriteLine("======== [ApiManifest Plugins Sample] ========"); this.WriteLine($"======== Loading Plugins: {string.Join(" ", pluginsToLoad)} ========"); this.WriteLine($"======== Calling Plugin Function: {pluginToTest}.{functionToTest} with parameters {arguments?.Select(x => x.Key + " = " + x.Value).Aggregate((x, y) => x + ", " + y)} ========"); this.WriteLine(); } private async Task AddApiManifestPluginsAsync(Kernel kernel, params string[] pluginNames) { #pragma warning disable SKEXP0053 if (TestConfiguration.MSGraph.Scopes == null) { throw new InvalidOperationException("Missing Scopes configuration for Microsoft Graph API."); } LocalUserMSALCredentialManager credentialManager = await LocalUserMSALCredentialManager.CreateAsync().ConfigureAwait(false); var token = await credentialManager.GetTokenAsync( TestConfiguration.MSGraph.ClientId, TestConfiguration.MSGraph.TenantId, TestConfiguration.MSGraph.Scopes.ToArray(), TestConfiguration.MSGraph.RedirectUri).ConfigureAwait(false); #pragma warning restore SKEXP0053 BearerAuthenticationProviderWithCancellationToken authenticationProvider = new(() => Task.FromResult(token)); foreach (var pluginName in pluginNames) { try { #pragma warning disable SKEXP0042 #pragma warning disable SKEXP0043 KernelPlugin plugin = await kernel.ImportPluginFromApiManifestAsync( pluginName, $"Plugins/ApiManifestPlugins/{pluginName}/apimanifest.json", new OpenApiFunctionExecutionParameters(authCallback: authenticationProvider.AuthenticateRequestAsync , serverUrlOverride: new Uri("https://graph.microsoft.com/v1.0"))) .ConfigureAwait(false); this.WriteLine($">> {pluginName} is created."); #pragma warning restore SKEXP0042 #pragma warning restore SKEXP0043 } catch (Exception ex) { kernel.LoggerFactory.CreateLogger("Plugin Creation").LogError(ex, "Plugin creation failed. Message: {0}", ex.Message); throw new AggregateException($"Plugin creation failed for {pluginName}", ex); } } } } /// /// Retrieves a token via the provided delegate and applies it to HTTP requests using the /// "bearer" authentication scheme. /// public class BearerAuthenticationProviderWithCancellationToken { private readonly Func> _bearerToken; /// /// Creates an instance of the class. /// /// Delegate to retrieve the bearer token. public BearerAuthenticationProviderWithCancellationToken(Func> bearerToken) { this._bearerToken = bearerToken; } /// /// Applies the token to the provided HTTP request message. /// /// The HTTP request message. /// public async Task AuthenticateRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken = default) { var token = await this._bearerToken().ConfigureAwait(false); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); } }