From 44ca5be7d12edf61811036ee1ae2d318c32fb56f Mon Sep 17 00:00:00 2001 From: Kunal22shah Date: Mon, 12 Aug 2024 16:38:34 -0400 Subject: [PATCH 1/7] chore: kiota support --- Uno.Extensions-packageonly.slnf | 1 + Uno.Extensions.sln | 19 +++ src/Directory.Packages.props | 131 ++++++++--------- .../ServiceCollectionExtensions.cs | 132 ++++++++++++++++++ .../TokenProvider.cs | 56 ++++++++ .../Uno.Extensions.Http.Kiota.csproj | 25 ++++ 6 files changed, 299 insertions(+), 65 deletions(-) create mode 100644 src/Uno.Extensions.Http.Kiota/ServiceCollectionExtensions.cs create mode 100644 src/Uno.Extensions.Http.Kiota/TokenProvider.cs create mode 100644 src/Uno.Extensions.Http.Kiota/Uno.Extensions.Http.Kiota.csproj diff --git a/Uno.Extensions-packageonly.slnf b/Uno.Extensions-packageonly.slnf index 07b26d4c30..31161e14a9 100644 --- a/Uno.Extensions-packageonly.slnf +++ b/Uno.Extensions-packageonly.slnf @@ -13,6 +13,7 @@ "src\\Uno.Extensions.Core\\Uno.Extensions.Core.csproj", "src\\Uno.Extensions.Hosting.UI\\Uno.Extensions.Hosting.WinUI.csproj", "src\\Uno.Extensions.Hosting\\Uno.Extensions.Hosting.csproj", + "src\\Uno.Extensions.Http.Kiota\\Uno.Extensions.Http.Kiota.csproj", "src\\Uno.Extensions.Http.Refit\\Uno.Extensions.Http.Refit.csproj", "src\\Uno.Extensions.Http.UI\\Uno.Extensions.Http.WinUI.csproj", "src\\Uno.Extensions.Http\\Uno.Extensions.Http.csproj", diff --git a/Uno.Extensions.sln b/Uno.Extensions.sln index 18ff07b455..28fe75668a 100644 --- a/Uno.Extensions.sln +++ b/Uno.Extensions.sln @@ -136,6 +136,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Uno.Extensions.RuntimeTests EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Uno.Extensions.RuntimeTests.Core", "src\Uno.Extensions.RuntimeTests\Uno.Extensions.RuntimeTests.Core\Uno.Extensions.RuntimeTests.Core.csproj", "{869C9E5B-0F85-4316-BC4B-CB6CBFCC02A3}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Uno.Extensions.Http.Kiota", "src\Uno.Extensions.Http.Kiota\Uno.Extensions.Http.Kiota.csproj", "{C9827C80-312B-4E81-B539-2D305D893A6C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -732,6 +734,22 @@ Global {869C9E5B-0F85-4316-BC4B-CB6CBFCC02A3}.Release|x64.Build.0 = Release|Any CPU {869C9E5B-0F85-4316-BC4B-CB6CBFCC02A3}.Release|x86.ActiveCfg = Release|Any CPU {869C9E5B-0F85-4316-BC4B-CB6CBFCC02A3}.Release|x86.Build.0 = Release|Any CPU + {C9827C80-312B-4E81-B539-2D305D893A6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C9827C80-312B-4E81-B539-2D305D893A6C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C9827C80-312B-4E81-B539-2D305D893A6C}.Debug|arm64.ActiveCfg = Debug|Any CPU + {C9827C80-312B-4E81-B539-2D305D893A6C}.Debug|arm64.Build.0 = Debug|Any CPU + {C9827C80-312B-4E81-B539-2D305D893A6C}.Debug|x64.ActiveCfg = Debug|Any CPU + {C9827C80-312B-4E81-B539-2D305D893A6C}.Debug|x64.Build.0 = Debug|Any CPU + {C9827C80-312B-4E81-B539-2D305D893A6C}.Debug|x86.ActiveCfg = Debug|Any CPU + {C9827C80-312B-4E81-B539-2D305D893A6C}.Debug|x86.Build.0 = Debug|Any CPU + {C9827C80-312B-4E81-B539-2D305D893A6C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C9827C80-312B-4E81-B539-2D305D893A6C}.Release|Any CPU.Build.0 = Release|Any CPU + {C9827C80-312B-4E81-B539-2D305D893A6C}.Release|arm64.ActiveCfg = Release|Any CPU + {C9827C80-312B-4E81-B539-2D305D893A6C}.Release|arm64.Build.0 = Release|Any CPU + {C9827C80-312B-4E81-B539-2D305D893A6C}.Release|x64.ActiveCfg = Release|Any CPU + {C9827C80-312B-4E81-B539-2D305D893A6C}.Release|x64.Build.0 = Release|Any CPU + {C9827C80-312B-4E81-B539-2D305D893A6C}.Release|x86.ActiveCfg = Release|Any CPU + {C9827C80-312B-4E81-B539-2D305D893A6C}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -778,6 +796,7 @@ Global {B72698C5-6706-4275-8A2B-A1D39FE9B13E} = {2197ADCE-59C4-465A-B380-0B06BF68BBBC} {A42362AF-8A61-4BBA-AA8A-E43323D5A063} = {FB399485-A0B1-4416-A494-E19AC7F5A665} {869C9E5B-0F85-4316-BC4B-CB6CBFCC02A3} = {FB399485-A0B1-4416-A494-E19AC7F5A665} + {C9827C80-312B-4E81-B539-2D305D893A6C} = {45179294-70DC-47E8-AD22-1296F897B594} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {6E7B035D-9A64-4D95-89AA-9D4653F17C42} diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index e0027d4f7c..8a5003b331 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -1,76 +1,77 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Uno.Extensions.Http.Kiota/ServiceCollectionExtensions.cs b/src/Uno.Extensions.Http.Kiota/ServiceCollectionExtensions.cs new file mode 100644 index 0000000000..359f47f70b --- /dev/null +++ b/src/Uno.Extensions.Http.Kiota/ServiceCollectionExtensions.cs @@ -0,0 +1,132 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Kiota.Abstractions; +using Microsoft.Kiota.Abstractions.Authentication; +using Microsoft.Kiota.Http.HttpClientLibrary; +using Uno.Extensions.Authentication; + +namespace Uno.Extensions.Http.Kiota; + +/// +/// Provides extension methods for registering Kiota clients within the . +/// +public static class ServiceCollectionExtensions +{ + /// + /// Registers a Kiota client with the specified and endpoint options. + /// + /// The Kiota client type to register. + /// The to register the client with. + /// The providing the hosting context. + /// [Optional] The endpoint options for the client (loaded from appsettings if not specified). + /// [Optional] The name for locating endpoint information in appsettings. + /// [Optional] A callback for configuring the endpoint. + /// The updated with the registered Kiota client. + public static IServiceCollection AddKiotaClient( + this IServiceCollection services, + HostBuilderContext context, + EndpointOptions? options = null, + string? name = null, + Func? configure = null + ) + where TClient : class => + services.AddKiotaClientWithEndpoint(context, options, name, configure); + + /// + /// Registers a Kiota client with the specified and supports additional endpoint options. + /// + /// The Kiota client type to register. + /// The type of endpoint to register. + /// The to register the client with. + /// The providing the hosting context. + /// [Optional] The endpoint options for the client (loaded from appsettings if not specified). + /// [Optional] The name for locating endpoint information in appsettings. + /// [Optional] A callback for configuring the endpoint. + /// The updated with the registered Kiota client. + public static IServiceCollection AddKiotaClientWithEndpoint( + this IServiceCollection services, + HostBuilderContext context, + TEndpoint? options = null, + string? name = null, + Func? configure = null + ) + where TClient : class + where TEndpoint : EndpointOptions, new() + { + services.AddKiotaHandlers(); + services.AddSingleton(sp => new AllowedHostsValidator()); + + return services.AddClientWithEndpoint( + context, + options, + name: name ?? typeof(TClient).FullName ?? "DefaultClient", + httpClientFactory: (s, c) => s.AddHttpClient(name ?? typeof(TClient).FullName ?? "DefaultClient") + .AttachKiotaHandlers() + .ConfigureHttpClient(client => + { + if (options?.Url != null) + { + client.BaseAddress = new Uri(options.Url); + } + }), + configure: configure + ) + .AddSingleton(sp => + { + var tokenCache = sp.GetRequiredService(); + var allowedHostsValidator = sp.GetRequiredService(); + var accessTokenProvider = new TokenProvider(tokenCache, allowedHostsValidator); + var authProvider = new BaseBearerTokenAuthenticationProvider(accessTokenProvider); + + var parseNodeFactory = new Microsoft.Kiota.Serialization.Json.JsonParseNodeFactory(); + var serializationWriterFactory = new Microsoft.Kiota.Serialization.Json.JsonSerializationWriterFactory(); + var httpClient = sp.GetRequiredService().CreateClient(name ?? typeof(TClient).FullName ?? "DefaultClient"); + + var requestAdapter = new HttpClientRequestAdapter(authProvider, parseNodeFactory, serializationWriterFactory, httpClient); + + if (options?.Url != null) + { + requestAdapter.BaseUrl = options.Url; + } + + return requestAdapter; + }) + .AddSingleton(sp => + { + var requestAdapter = sp.GetRequiredService(); + return (TClient)Activator.CreateInstance(typeof(TClient), requestAdapter)!; + }); + } + + /// + /// Dynamically adds Kiota handlers to the service collection. + /// + /// The to register the handlers with. + /// The updated with the registered Kiota handlers. + private static IServiceCollection AddKiotaHandlers(this IServiceCollection services) + { + var kiotaHandlers = KiotaClientFactory.GetDefaultHandlerTypes(); + foreach (var handler in kiotaHandlers) + { + services.AddTransient(handler); + } + + return services; + } + + /// + /// Attaches Kiota handlers to the . + /// + /// The to attach the handlers to. + /// The updated with the attached Kiota handlers. + private static IHttpClientBuilder AttachKiotaHandlers(this IHttpClientBuilder builder) + { + var kiotaHandlers = KiotaClientFactory.GetDefaultHandlerTypes(); + foreach (var handler in kiotaHandlers) + { + builder.AddHttpMessageHandler((sp) => (DelegatingHandler)sp.GetRequiredService(handler)); + } + + return builder; + } +} diff --git a/src/Uno.Extensions.Http.Kiota/TokenProvider.cs b/src/Uno.Extensions.Http.Kiota/TokenProvider.cs new file mode 100644 index 0000000000..45c7754a08 --- /dev/null +++ b/src/Uno.Extensions.Http.Kiota/TokenProvider.cs @@ -0,0 +1,56 @@ +using Microsoft.Kiota.Abstractions.Authentication; +using Uno.Extensions.Authentication; + +namespace Uno.Extensions.Http.Kiota; + +/// +/// A custom implementation of for managing access tokens in Kiota clients. +/// +public class TokenProvider : IAccessTokenProvider +{ + private readonly ITokenCache _tokenCache; + + /// + /// Initializes a new instance of the class. + /// + /// The token cache used to store and retrieve tokens. + /// The validator used to verify if a host is allowed. + public TokenProvider(ITokenCache tokenCache, AllowedHostsValidator allowedHostsValidator) + { + _tokenCache = tokenCache; + AllowedHostsValidator = allowedHostsValidator; + } + + /// + /// Gets the used to validate allowed hosts. + /// + public AllowedHostsValidator AllowedHostsValidator { get; } + + /// + /// Asynchronously retrieves the authorization token for the specified URI. + /// + /// The URI for which the token is requested. + /// Additional context for authentication, if any. + /// A token to cancel the operation. + /// A task that represents the asynchronous operation, returning the authorization token. + /// Thrown if the URI host is not allowed or if no access token is available. + public async Task GetAuthorizationTokenAsync( + Uri? uri = null, + Dictionary? additionalAuthenticationContext = null, + CancellationToken cancellationToken = default) + { + if (uri != null && !AllowedHostsValidator.IsUrlHostValid(uri)) + { + throw new InvalidOperationException("The URI host is not allowed."); + } + + var token = await _tokenCache.AccessTokenAsync(cancellationToken); + + if (string.IsNullOrEmpty(token)) + { + throw new InvalidOperationException("No access token is available."); + } + + return token; + } +} diff --git a/src/Uno.Extensions.Http.Kiota/Uno.Extensions.Http.Kiota.csproj b/src/Uno.Extensions.Http.Kiota/Uno.Extensions.Http.Kiota.csproj new file mode 100644 index 0000000000..482fa81d73 --- /dev/null +++ b/src/Uno.Extensions.Http.Kiota/Uno.Extensions.Http.Kiota.csproj @@ -0,0 +1,25 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + + + + + + From d0cbc94545adf6ba4787bdd0410dfc67cc5d8cf1 Mon Sep 17 00:00:00 2001 From: Kunal22shah Date: Thu, 22 Aug 2024 12:21:45 -0400 Subject: [PATCH 2/7] chore: resolving PR comments --- .../KiotaAuthenticationAdapter.cs | 73 +++++++++++++++++++ .../ServiceCollectionExtensions.cs | 5 +- .../TokenProvider.cs | 56 -------------- 3 files changed, 75 insertions(+), 59 deletions(-) create mode 100644 src/Uno.Extensions.Http.Kiota/KiotaAuthenticationAdapter.cs delete mode 100644 src/Uno.Extensions.Http.Kiota/TokenProvider.cs diff --git a/src/Uno.Extensions.Http.Kiota/KiotaAuthenticationAdapter.cs b/src/Uno.Extensions.Http.Kiota/KiotaAuthenticationAdapter.cs new file mode 100644 index 0000000000..0112947420 --- /dev/null +++ b/src/Uno.Extensions.Http.Kiota/KiotaAuthenticationAdapter.cs @@ -0,0 +1,73 @@ +using Microsoft.Kiota.Abstractions; +using Microsoft.Kiota.Abstractions.Authentication; +using Uno.Extensions.Authentication; + +namespace Uno.Extensions.Http.Kiota +{ + + public class KiotaAuthenticationAdapter : IAuthenticationProvider, IAccessTokenProvider + { + private readonly ITokenCache _tokenCache; + + /// + /// Initializes a new instance of the class. + /// + /// The token cache used to store and retrieve tokens. + /// The validator used to verify if a host is allowed. + public KiotaAuthenticationAdapter(ITokenCache tokenCache, AllowedHostsValidator allowedHostsValidator) + { + _tokenCache = tokenCache; + AllowedHostsValidator = allowedHostsValidator; + } + + /// + /// Gets the used to validate allowed hosts. + /// + public AllowedHostsValidator AllowedHostsValidator { get; } + + /// + /// Asynchronously retrieves the authorization token for the specified URI. + /// + /// The URI for which the token is requested. + /// Additional context for authentication, if any. + /// A token to cancel the operation. + /// A task that represents the asynchronous operation, returning the authorization token. + /// Thrown if the URI host is not allowed or if no access token is available. + public async Task GetAuthorizationTokenAsync( + Uri? uri = null, + Dictionary? additionalAuthenticationContext = null, + CancellationToken cancellationToken = default) + { + if (uri != null && !AllowedHostsValidator.IsUrlHostValid(uri)) + { + throw new InvalidOperationException("The URI host is not allowed."); + } + + var token = await _tokenCache.AccessTokenAsync(cancellationToken); + + if (string.IsNullOrEmpty(token)) + { + throw new InvalidOperationException("No access token is available."); + } + + return token; + } + + /// + /// Adds the authorization token to the request headers. + /// + /// The request to add the authorization header to. + /// Additional context for authentication, if any. + /// A token to cancel the operation. + /// A task representing the asynchronous operation. + public async Task AuthenticateRequestAsync(RequestInformation request, Dictionary? additionalAuthenticationContext = null, + CancellationToken cancellationToken = default) + { + var token = await GetAuthorizationTokenAsync(null, additionalAuthenticationContext, cancellationToken); + if (!string.IsNullOrEmpty(token)) + { + request.Headers.Add("Authorization", $"Bearer {token}"); + } + } + } +} diff --git a/src/Uno.Extensions.Http.Kiota/ServiceCollectionExtensions.cs b/src/Uno.Extensions.Http.Kiota/ServiceCollectionExtensions.cs index 359f47f70b..8a20a26186 100644 --- a/src/Uno.Extensions.Http.Kiota/ServiceCollectionExtensions.cs +++ b/src/Uno.Extensions.Http.Kiota/ServiceCollectionExtensions.cs @@ -75,12 +75,11 @@ public static IServiceCollection AddKiotaClientWithEndpoint( { var tokenCache = sp.GetRequiredService(); var allowedHostsValidator = sp.GetRequiredService(); - var accessTokenProvider = new TokenProvider(tokenCache, allowedHostsValidator); - var authProvider = new BaseBearerTokenAuthenticationProvider(accessTokenProvider); + var authProvider = new KiotaAuthenticationAdapter(tokenCache, allowedHostsValidator); + var httpClient = sp.GetRequiredService(); var parseNodeFactory = new Microsoft.Kiota.Serialization.Json.JsonParseNodeFactory(); var serializationWriterFactory = new Microsoft.Kiota.Serialization.Json.JsonSerializationWriterFactory(); - var httpClient = sp.GetRequiredService().CreateClient(name ?? typeof(TClient).FullName ?? "DefaultClient"); var requestAdapter = new HttpClientRequestAdapter(authProvider, parseNodeFactory, serializationWriterFactory, httpClient); diff --git a/src/Uno.Extensions.Http.Kiota/TokenProvider.cs b/src/Uno.Extensions.Http.Kiota/TokenProvider.cs deleted file mode 100644 index 45c7754a08..0000000000 --- a/src/Uno.Extensions.Http.Kiota/TokenProvider.cs +++ /dev/null @@ -1,56 +0,0 @@ -using Microsoft.Kiota.Abstractions.Authentication; -using Uno.Extensions.Authentication; - -namespace Uno.Extensions.Http.Kiota; - -/// -/// A custom implementation of for managing access tokens in Kiota clients. -/// -public class TokenProvider : IAccessTokenProvider -{ - private readonly ITokenCache _tokenCache; - - /// - /// Initializes a new instance of the class. - /// - /// The token cache used to store and retrieve tokens. - /// The validator used to verify if a host is allowed. - public TokenProvider(ITokenCache tokenCache, AllowedHostsValidator allowedHostsValidator) - { - _tokenCache = tokenCache; - AllowedHostsValidator = allowedHostsValidator; - } - - /// - /// Gets the used to validate allowed hosts. - /// - public AllowedHostsValidator AllowedHostsValidator { get; } - - /// - /// Asynchronously retrieves the authorization token for the specified URI. - /// - /// The URI for which the token is requested. - /// Additional context for authentication, if any. - /// A token to cancel the operation. - /// A task that represents the asynchronous operation, returning the authorization token. - /// Thrown if the URI host is not allowed or if no access token is available. - public async Task GetAuthorizationTokenAsync( - Uri? uri = null, - Dictionary? additionalAuthenticationContext = null, - CancellationToken cancellationToken = default) - { - if (uri != null && !AllowedHostsValidator.IsUrlHostValid(uri)) - { - throw new InvalidOperationException("The URI host is not allowed."); - } - - var token = await _tokenCache.AccessTokenAsync(cancellationToken); - - if (string.IsNullOrEmpty(token)) - { - throw new InvalidOperationException("No access token is available."); - } - - return token; - } -} From af8f321b8db1c48e4717cdf71409e5acabc3c53d Mon Sep 17 00:00:00 2001 From: Kunal22shah Date: Thu, 12 Dec 2024 15:02:17 -0500 Subject: [PATCH 3/7] chore: remove auth adapter + refactor --- .../KiotaAuthenticationAdapter.cs | 73 ------------------- .../ServiceCollectionExtensions.cs | 57 ++------------- 2 files changed, 5 insertions(+), 125 deletions(-) delete mode 100644 src/Uno.Extensions.Http.Kiota/KiotaAuthenticationAdapter.cs diff --git a/src/Uno.Extensions.Http.Kiota/KiotaAuthenticationAdapter.cs b/src/Uno.Extensions.Http.Kiota/KiotaAuthenticationAdapter.cs deleted file mode 100644 index 0112947420..0000000000 --- a/src/Uno.Extensions.Http.Kiota/KiotaAuthenticationAdapter.cs +++ /dev/null @@ -1,73 +0,0 @@ -using Microsoft.Kiota.Abstractions; -using Microsoft.Kiota.Abstractions.Authentication; -using Uno.Extensions.Authentication; - -namespace Uno.Extensions.Http.Kiota -{ - - public class KiotaAuthenticationAdapter : IAuthenticationProvider, IAccessTokenProvider - { - private readonly ITokenCache _tokenCache; - - /// - /// Initializes a new instance of the class. - /// - /// The token cache used to store and retrieve tokens. - /// The validator used to verify if a host is allowed. - public KiotaAuthenticationAdapter(ITokenCache tokenCache, AllowedHostsValidator allowedHostsValidator) - { - _tokenCache = tokenCache; - AllowedHostsValidator = allowedHostsValidator; - } - - /// - /// Gets the used to validate allowed hosts. - /// - public AllowedHostsValidator AllowedHostsValidator { get; } - - /// - /// Asynchronously retrieves the authorization token for the specified URI. - /// - /// The URI for which the token is requested. - /// Additional context for authentication, if any. - /// A token to cancel the operation. - /// A task that represents the asynchronous operation, returning the authorization token. - /// Thrown if the URI host is not allowed or if no access token is available. - public async Task GetAuthorizationTokenAsync( - Uri? uri = null, - Dictionary? additionalAuthenticationContext = null, - CancellationToken cancellationToken = default) - { - if (uri != null && !AllowedHostsValidator.IsUrlHostValid(uri)) - { - throw new InvalidOperationException("The URI host is not allowed."); - } - - var token = await _tokenCache.AccessTokenAsync(cancellationToken); - - if (string.IsNullOrEmpty(token)) - { - throw new InvalidOperationException("No access token is available."); - } - - return token; - } - - /// - /// Adds the authorization token to the request headers. - /// - /// The request to add the authorization header to. - /// Additional context for authentication, if any. - /// A token to cancel the operation. - /// A task representing the asynchronous operation. - public async Task AuthenticateRequestAsync(RequestInformation request, Dictionary? additionalAuthenticationContext = null, - CancellationToken cancellationToken = default) - { - var token = await GetAuthorizationTokenAsync(null, additionalAuthenticationContext, cancellationToken); - if (!string.IsNullOrEmpty(token)) - { - request.Headers.Add("Authorization", $"Bearer {token}"); - } - } - } -} diff --git a/src/Uno.Extensions.Http.Kiota/ServiceCollectionExtensions.cs b/src/Uno.Extensions.Http.Kiota/ServiceCollectionExtensions.cs index 8a20a26186..7160113970 100644 --- a/src/Uno.Extensions.Http.Kiota/ServiceCollectionExtensions.cs +++ b/src/Uno.Extensions.Http.Kiota/ServiceCollectionExtensions.cs @@ -3,7 +3,6 @@ using Microsoft.Kiota.Abstractions; using Microsoft.Kiota.Abstractions.Authentication; using Microsoft.Kiota.Http.HttpClientLibrary; -using Uno.Extensions.Authentication; namespace Uno.Extensions.Http.Kiota; @@ -51,33 +50,19 @@ public static IServiceCollection AddKiotaClientWithEndpoint( Func? configure = null ) where TClient : class - where TEndpoint : EndpointOptions, new() - { - services.AddKiotaHandlers(); - services.AddSingleton(sp => new AllowedHostsValidator()); - - return services.AddClientWithEndpoint( + where TEndpoint : EndpointOptions, new() => + services.AddClientWithEndpoint( context, options, name: name ?? typeof(TClient).FullName ?? "DefaultClient", - httpClientFactory: (s, c) => s.AddHttpClient(name ?? typeof(TClient).FullName ?? "DefaultClient") - .AttachKiotaHandlers() - .ConfigureHttpClient(client => - { - if (options?.Url != null) - { - client.BaseAddress = new Uri(options.Url); - } - }), + httpClientFactory: null, configure: configure ) .AddSingleton(sp => { - var tokenCache = sp.GetRequiredService(); - var allowedHostsValidator = sp.GetRequiredService(); - var authProvider = new KiotaAuthenticationAdapter(tokenCache, allowedHostsValidator); - var httpClient = sp.GetRequiredService(); + var authProvider = new AnonymousAuthenticationProvider(); + var parseNodeFactory = new Microsoft.Kiota.Serialization.Json.JsonParseNodeFactory(); var serializationWriterFactory = new Microsoft.Kiota.Serialization.Json.JsonSerializationWriterFactory(); @@ -95,37 +80,5 @@ public static IServiceCollection AddKiotaClientWithEndpoint( var requestAdapter = sp.GetRequiredService(); return (TClient)Activator.CreateInstance(typeof(TClient), requestAdapter)!; }); - } - - /// - /// Dynamically adds Kiota handlers to the service collection. - /// - /// The to register the handlers with. - /// The updated with the registered Kiota handlers. - private static IServiceCollection AddKiotaHandlers(this IServiceCollection services) - { - var kiotaHandlers = KiotaClientFactory.GetDefaultHandlerTypes(); - foreach (var handler in kiotaHandlers) - { - services.AddTransient(handler); - } - - return services; - } - - /// - /// Attaches Kiota handlers to the . - /// - /// The to attach the handlers to. - /// The updated with the attached Kiota handlers. - private static IHttpClientBuilder AttachKiotaHandlers(this IHttpClientBuilder builder) - { - var kiotaHandlers = KiotaClientFactory.GetDefaultHandlerTypes(); - foreach (var handler in kiotaHandlers) - { - builder.AddHttpMessageHandler((sp) => (DelegatingHandler)sp.GetRequiredService(handler)); - } - return builder; - } } From ad022565252244ca7ba3fe1e9340e65d9becf88c Mon Sep 17 00:00:00 2001 From: Kunal22shah Date: Fri, 13 Dec 2024 13:30:55 -0500 Subject: [PATCH 4/7] docs: kiota --- doc/Learn/Http/HowTo-Kiota.md | 114 ++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 doc/Learn/Http/HowTo-Kiota.md diff --git a/doc/Learn/Http/HowTo-Kiota.md b/doc/Learn/Http/HowTo-Kiota.md new file mode 100644 index 0000000000..e4f80b4965 --- /dev/null +++ b/doc/Learn/Http/HowTo-Kiota.md @@ -0,0 +1,114 @@ +--- +uid: Uno.Extensions.Http.HowToKiota +--- +# How-To: Quickly Kiota Client for an API + +When working with APIs in your application, having a strongly-typed client can simplify communication and reduce boilerplate code. **Kiota** is a tool that generates strongly-typed API clients from Swagger/OpenAPI definitions. With Uno.Extensions, you can easily register and use Kiota clients in your Uno Platform app without additional setup. + +## Step-by-Step Guide + +> [!IMPORTANT] +> This guide assumes you used the template wizard or `dotnet new unoapp` to create your solution. If not, it is recommended that you follow the [**Creating an application with Uno.Extensions** documentation](xref:Uno.Extensions.HowToGettingStarted) to create an application from the template. + +### 1. Installation + +* Add `Http` to the `` property in the Class Library (`.csproj`) file: + + ```diff + + Material; + Extensions; + + Http; + Toolkit; + MVUX; + + ``` + +### 2. Enable Http in Host Builder + +* Add the UseHttp method to the `IHostBuilder`: + + ```csharp + protected override void OnLaunched(LaunchActivatedEventArgs args) + { + var appBuilder = this.CreateBuilder(args) + .Configure(hostBuilder => + { + hostBuilder.UseHttp(); + }); + ... + } + ``` + +### 3. Generate the Kiota Client + +* Install the Kiota tool: + + ```xml + dotnet tool install --global Microsoft.OpenApi.Kiota + ``` + +* Generate the Client using the OpenAPI specification URL: + + ```xml + kiota generate -l CSharp -c MyApiClient -n MyApp.Client -d https://localhost:5002/swagger/v1/swagger.json -o ./Client + ``` + + This will create a client named `MyApiClient` in the Client folder. + +### 4. Register the Kiota Client + +* Register the generated client in the IHostBuilder using `AddKiotaClient` from `Uno.Extensions`: + + ```csharp + + protected override void OnLaunched(LaunchActivatedEventArgs args) + { + var appBuilder = this.CreateBuilder(args) + .Configure(hostBuilder => + { + hostBuilder.UseHttp((context, services) => + services.AddKiotaClient( + context, + options: new EndpointOptions { Url = "https://localhost:5002" } + ) + ); + }); + } + ``` + +### 5. Use the Kiota Client in Your Code + +* Inject the ChefsApiClient into your view model or service and make API requests: + +```csharp +public class MyViewModel +{ + private readonly MyApiClient _apiClient; + + public MyViewModel(MyApiClient apiClient) + { + _apiClient = apiClient; + } + + public async Task GetAll() + { + var something = await _apiClient.Api.GetAsync(); + Console.WriteLine($"Retrieved {something?.Count}."); + } +} + +``` + +## Important Considerations + +* With `Uno.Extensions.Authentication`, the HttpClient automatically includes the **Authorization** header. You don't need to manually handle token injection. The middleware ensures the access token is included in each request. + +* Ensure your server is running and the swagger.json file is accessible at the specified URL when generating the Kiota client. + +## See also + +* [Overview: What is Kiota?](https://learn.microsoft.com/en-us/openapi/kiota/) +* [Overview: HTTP](xref:Uno.Extensions.Http.Overview) +* [How-To: Consume a web API with HttpClient](xref:Uno.Development.ConsumeWebApi) +* [How-To: Register an Endpoint for HTTP Requests](xref:Uno.Extensions.Http.HowToHttp) From 4c22b24a1115250331729c2cb3b3a32791abaff5 Mon Sep 17 00:00:00 2001 From: Kunal22shah Date: Tue, 17 Dec 2024 12:54:08 -0500 Subject: [PATCH 5/7] chore: wip --- src/Directory.Packages.props | 5 +- .../ServiceCollectionExtensions.cs | 63 +++++- .../Uno.Extensions.Http.Kiota.csproj | 3 + .../TestHarness.Core/TestSections.cs | 3 +- .../Ext/Kiota/Given_Kiota.cs | 21 ++ testing/TestHarness/TestHarness.sln | 59 ++++++ .../Ext/Http/Kiota/Client/Models/Post.cs | 81 ++++++++ .../Posts/Item/PostItemRequestBuilder.cs | 184 +++++++++++++++++ .../Kiota/Client/Posts/PostsRequestBuilder.cs | 187 ++++++++++++++++++ .../Ext/Http/Kiota/Client/PostsApiClient.cs | 46 +++++ .../Ext/Http/Kiota/Client/kiota-lock.json | 33 ++++ .../Ext/Http/Kiota/KiotaHomePage.xaml | 32 +++ .../Ext/Http/Kiota/KiotaHomePage.xaml.cs | 14 ++ .../Ext/Http/Kiota/KiotaHomeViewModel.cs | 32 +++ .../Ext/Http/Kiota/KiotaHostInit.cs | 23 +++ .../Ext/Http/Kiota/appsettings.json | 6 + .../TestHarness/Ext/Http/Kiota/posts-api.yml | 111 +++++++++++ .../TestHarness/TestHarness.csproj | 5 +- 18 files changed, 894 insertions(+), 14 deletions(-) create mode 100644 testing/TestHarness/TestHarness.UITest/Ext/Kiota/Given_Kiota.cs create mode 100644 testing/TestHarness/TestHarness/Ext/Http/Kiota/Client/Models/Post.cs create mode 100644 testing/TestHarness/TestHarness/Ext/Http/Kiota/Client/Posts/Item/PostItemRequestBuilder.cs create mode 100644 testing/TestHarness/TestHarness/Ext/Http/Kiota/Client/Posts/PostsRequestBuilder.cs create mode 100644 testing/TestHarness/TestHarness/Ext/Http/Kiota/Client/PostsApiClient.cs create mode 100644 testing/TestHarness/TestHarness/Ext/Http/Kiota/Client/kiota-lock.json create mode 100644 testing/TestHarness/TestHarness/Ext/Http/Kiota/KiotaHomePage.xaml create mode 100644 testing/TestHarness/TestHarness/Ext/Http/Kiota/KiotaHomePage.xaml.cs create mode 100644 testing/TestHarness/TestHarness/Ext/Http/Kiota/KiotaHomeViewModel.cs create mode 100644 testing/TestHarness/TestHarness/Ext/Http/Kiota/KiotaHostInit.cs create mode 100644 testing/TestHarness/TestHarness/Ext/Http/Kiota/appsettings.json create mode 100644 testing/TestHarness/TestHarness/Ext/Http/Kiota/posts-api.yml diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 8a5003b331..7e85cb6059 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -19,7 +19,10 @@ + + + @@ -74,4 +77,4 @@ - + \ No newline at end of file diff --git a/src/Uno.Extensions.Http.Kiota/ServiceCollectionExtensions.cs b/src/Uno.Extensions.Http.Kiota/ServiceCollectionExtensions.cs index 7160113970..7be34f0f4c 100644 --- a/src/Uno.Extensions.Http.Kiota/ServiceCollectionExtensions.cs +++ b/src/Uno.Extensions.Http.Kiota/ServiceCollectionExtensions.cs @@ -43,19 +43,30 @@ public static IServiceCollection AddKiotaClient( /// [Optional] A callback for configuring the endpoint. /// The updated with the registered Kiota client. public static IServiceCollection AddKiotaClientWithEndpoint( - this IServiceCollection services, - HostBuilderContext context, - TEndpoint? options = null, - string? name = null, - Func? configure = null - ) - where TClient : class - where TEndpoint : EndpointOptions, new() => - services.AddClientWithEndpoint( + this IServiceCollection services, + HostBuilderContext context, + TEndpoint? options = null, + string? name = null, + Func? configure = null +) + where TClient : class + where TEndpoint : EndpointOptions, new() + { + services.AddKiotaHandlers(); + + return services.AddClientWithEndpoint( context, options, name: name ?? typeof(TClient).FullName ?? "DefaultClient", - httpClientFactory: null, + httpClientFactory: (s, c) => s.AddHttpClient(name ?? typeof(TClient).FullName ?? "DefaultClient") + .AttachKiotaHandlers() + .ConfigureHttpClient(client => + { + if (options?.Url != null) + { + client.BaseAddress = new Uri(options.Url); + } + }), configure: configure ) .AddSingleton(sp => @@ -80,5 +91,37 @@ public static IServiceCollection AddKiotaClientWithEndpoint( var requestAdapter = sp.GetRequiredService(); return (TClient)Activator.CreateInstance(typeof(TClient), requestAdapter)!; }); + } + /// + /// Dynamically adds Kiota handlers to the service collection. + /// + /// The to register the handlers with. + /// The updated with the registered Kiota handlers. + private static IServiceCollection AddKiotaHandlers(this IServiceCollection services) + { + var kiotaHandlers = KiotaClientFactory.GetDefaultHandlerTypes(); + foreach (var handler in kiotaHandlers) + { + services.AddTransient(handler); + } + + return services; + } + + /// + /// Attaches Kiota handlers to the . + /// + /// The to attach the handlers to. + /// The updated with the attached Kiota handlers. + private static IHttpClientBuilder AttachKiotaHandlers(this IHttpClientBuilder builder) + { + var kiotaHandlers = KiotaClientFactory.GetDefaultHandlerTypes(); + foreach (var handler in kiotaHandlers) + { + builder.AddHttpMessageHandler((sp) => (DelegatingHandler)sp.GetRequiredService(handler)); + } + + return builder; + } } diff --git a/src/Uno.Extensions.Http.Kiota/Uno.Extensions.Http.Kiota.csproj b/src/Uno.Extensions.Http.Kiota/Uno.Extensions.Http.Kiota.csproj index 482fa81d73..44e92dcdab 100644 --- a/src/Uno.Extensions.Http.Kiota/Uno.Extensions.Http.Kiota.csproj +++ b/src/Uno.Extensions.Http.Kiota/Uno.Extensions.Http.Kiota.csproj @@ -12,7 +12,10 @@ + + + diff --git a/testing/TestHarness/TestHarness.Core/TestSections.cs b/testing/TestHarness/TestHarness.Core/TestSections.cs index b0cb60fdb2..256defe89c 100644 --- a/testing/TestHarness/TestHarness.Core/TestSections.cs +++ b/testing/TestHarness/TestHarness.Core/TestSections.cs @@ -32,5 +32,6 @@ public enum TestSections Http_Endpoints, Http_Refit, Toolkit_ThemeService, - Validation + Validation, + Http_Kiota, } diff --git a/testing/TestHarness/TestHarness.UITest/Ext/Kiota/Given_Kiota.cs b/testing/TestHarness/TestHarness.UITest/Ext/Kiota/Given_Kiota.cs new file mode 100644 index 0000000000..58c2bbb28a --- /dev/null +++ b/testing/TestHarness/TestHarness.UITest/Ext/Kiota/Given_Kiota.cs @@ -0,0 +1,21 @@ +namespace TestHarness.UITest; + +public class Given_Kiota : NavigationTestBase +{ + [Test] + public async Task When_KiotaClient_Registered() + { + InitTestSection(TestSections.Http_Kiota); + + App.WaitThenTap("ShowAppButton"); + + App.WaitElement("KiotaHomeNavigationBar"); + + App.WaitThenTap("FetchPostsButton"); + + await Task.Delay(2000); + + var fetchResult = App.GetText("FetchPostsResultTextBlock"); + fetchResult.Should().Contain("Retrieved").And.Contain("posts"); + } +} diff --git a/testing/TestHarness/TestHarness.sln b/testing/TestHarness/TestHarness.sln index 25faa27554..ab67140319 100644 --- a/testing/TestHarness/TestHarness.sln +++ b/testing/TestHarness/TestHarness.sln @@ -123,6 +123,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestHarness", "TestHarness\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Uno.Extensions.Reactive.Tests", "..\..\src\Uno.Extensions.Reactive.Tests\Uno.Extensions.Reactive.Tests.csproj", "{A39E9AEC-4F8B-4B6F-A4DD-16201F427948}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Uno.Extensions.Http.Kiota", "..\..\src\Uno.Extensions.Http.Kiota\Uno.Extensions.Http.Kiota.csproj", "{49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Ad-Hoc|Any CPU = Ad-Hoc|Any CPU @@ -2255,6 +2257,62 @@ Global {A39E9AEC-4F8B-4B6F-A4DD-16201F427948}.Release|x64.Build.0 = Release|Any CPU {A39E9AEC-4F8B-4B6F-A4DD-16201F427948}.Release|x86.ActiveCfg = Release|Any CPU {A39E9AEC-4F8B-4B6F-A4DD-16201F427948}.Release|x86.Build.0 = Release|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Ad-Hoc|ARM64.ActiveCfg = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Ad-Hoc|ARM64.Build.0 = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.AppStore|ARM.Build.0 = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.AppStore|ARM64.ActiveCfg = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.AppStore|ARM64.Build.0 = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.AppStore|iPhone.Build.0 = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.AppStore|x64.ActiveCfg = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.AppStore|x64.Build.0 = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.AppStore|x86.ActiveCfg = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.AppStore|x86.Build.0 = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Debug|ARM.ActiveCfg = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Debug|ARM.Build.0 = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Debug|ARM64.Build.0 = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Debug|iPhone.Build.0 = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Debug|x64.ActiveCfg = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Debug|x64.Build.0 = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Debug|x86.ActiveCfg = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Debug|x86.Build.0 = Debug|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Release|Any CPU.Build.0 = Release|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Release|ARM.ActiveCfg = Release|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Release|ARM.Build.0 = Release|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Release|ARM64.ActiveCfg = Release|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Release|ARM64.Build.0 = Release|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Release|iPhone.ActiveCfg = Release|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Release|iPhone.Build.0 = Release|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Release|x64.ActiveCfg = Release|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Release|x64.Build.0 = Release|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Release|x86.ActiveCfg = Release|Any CPU + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2305,6 +2363,7 @@ Global {87A72917-4D00-4C93-A792-1A00DFD380E6} = {1380679E-0E34-4994-BD7F-1C848B11E761} {FE6C5BE3-3712-446C-BE40-CA9BE0D70EEC} = {5851B37C-ADC6-468D-8684-AC06476569AC} {A39E9AEC-4F8B-4B6F-A4DD-16201F427948} = {A404EC40-25EF-4EFF-8C37-67D7039CF243} + {49716A71-0D2A-4EB2-83C0-3A7CDD9549B5} = {5851B37C-ADC6-468D-8684-AC06476569AC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C7433AE2-B1A0-4C1A-887E-5CAA7AAF67A6} diff --git a/testing/TestHarness/TestHarness/Ext/Http/Kiota/Client/Models/Post.cs b/testing/TestHarness/TestHarness/Ext/Http/Kiota/Client/Models/Post.cs new file mode 100644 index 0000000000..0ae79bc715 --- /dev/null +++ b/testing/TestHarness/TestHarness/Ext/Http/Kiota/Client/Models/Post.cs @@ -0,0 +1,81 @@ +// +using Microsoft.Kiota.Abstractions.Extensions; +using Microsoft.Kiota.Abstractions.Serialization; +using System.Collections.Generic; +using System.IO; +using System; +namespace TestHarness.Ext.Http.Kiota.Client.Models +{ + [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.16.0")] + #pragma warning disable CS1591 + public partial class Post : IAdditionalDataHolder, IParsable + #pragma warning restore CS1591 + { + /// Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. + public IDictionary AdditionalData { get; set; } + /// The body property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Body { get; set; } +#nullable restore +#else + public string Body { get; set; } +#endif + /// The id property + public int? Id { get; set; } + /// The title property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Title { get; set; } +#nullable restore +#else + public string Title { get; set; } +#endif + /// The userId property + public int? UserId { get; set; } + /// + /// Instantiates a new and sets the default values. + /// + public Post() + { + AdditionalData = new Dictionary(); + } + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// A + /// The parse node to use to read the discriminator value and create the object + public static global::TestHarness.Ext.Http.Kiota.Client.Models.Post CreateFromDiscriminatorValue(IParseNode parseNode) + { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new global::TestHarness.Ext.Http.Kiota.Client.Models.Post(); + } + /// + /// The deserialization information for the current model + /// + /// A IDictionary<string, Action<IParseNode>> + public virtual IDictionary> GetFieldDeserializers() + { + return new Dictionary> + { + { "body", n => { Body = n.GetStringValue(); } }, + { "id", n => { Id = n.GetIntValue(); } }, + { "title", n => { Title = n.GetStringValue(); } }, + { "userId", n => { UserId = n.GetIntValue(); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) + { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteStringValue("body", Body); + writer.WriteIntValue("id", Id); + writer.WriteStringValue("title", Title); + writer.WriteIntValue("userId", UserId); + writer.WriteAdditionalData(AdditionalData); + } + } +} diff --git a/testing/TestHarness/TestHarness/Ext/Http/Kiota/Client/Posts/Item/PostItemRequestBuilder.cs b/testing/TestHarness/TestHarness/Ext/Http/Kiota/Client/Posts/Item/PostItemRequestBuilder.cs new file mode 100644 index 0000000000..e4bcd372d7 --- /dev/null +++ b/testing/TestHarness/TestHarness/Ext/Http/Kiota/Client/Posts/Item/PostItemRequestBuilder.cs @@ -0,0 +1,184 @@ +// +using Microsoft.Kiota.Abstractions.Extensions; +using Microsoft.Kiota.Abstractions.Serialization; +using Microsoft.Kiota.Abstractions; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using System.Threading; +using System; +using TestHarness.Ext.Http.Kiota.Client.Models; +namespace TestHarness.Ext.Http.Kiota.Client.Posts.Item +{ + /// + /// Builds and executes requests for operations under \posts\{post-id} + /// + [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.16.0")] + public partial class PostItemRequestBuilder : BaseRequestBuilder + { + /// + /// Instantiates a new and sets the default values. + /// + /// Path parameters for the request + /// The request adapter to use to execute the requests. + public PostItemRequestBuilder(Dictionary pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/posts/{post%2Did}", pathParameters) + { + } + /// + /// Instantiates a new and sets the default values. + /// + /// The raw URL to use for the request builder. + /// The request adapter to use to execute the requests. + public PostItemRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/posts/{post%2Did}", rawUrl) + { + } + /// + /// Delete post + /// + /// A + /// Cancellation token to use when cancelling requests + /// Configuration for the request such as headers, query parameters, and middleware options. +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public async Task DeleteAsync(Action>? requestConfiguration = default, CancellationToken cancellationToken = default) + { +#nullable restore +#else + public async Task DeleteAsync(Action> requestConfiguration = default, CancellationToken cancellationToken = default) + { +#endif + var requestInfo = ToDeleteRequestInformation(requestConfiguration); + return await RequestAdapter.SendPrimitiveAsync(requestInfo, default, cancellationToken).ConfigureAwait(false); + } + /// + /// Get post by ID + /// + /// A + /// Cancellation token to use when cancelling requests + /// Configuration for the request such as headers, query parameters, and middleware options. +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public async Task GetAsync(Action>? requestConfiguration = default, CancellationToken cancellationToken = default) + { +#nullable restore +#else + public async Task GetAsync(Action> requestConfiguration = default, CancellationToken cancellationToken = default) + { +#endif + var requestInfo = ToGetRequestInformation(requestConfiguration); + return await RequestAdapter.SendAsync(requestInfo, global::TestHarness.Ext.Http.Kiota.Client.Models.Post.CreateFromDiscriminatorValue, default, cancellationToken).ConfigureAwait(false); + } + /// + /// Update post + /// + /// A + /// The request body + /// Cancellation token to use when cancelling requests + /// Configuration for the request such as headers, query parameters, and middleware options. +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public async Task PatchAsync(global::TestHarness.Ext.Http.Kiota.Client.Models.Post body, Action>? requestConfiguration = default, CancellationToken cancellationToken = default) + { +#nullable restore +#else + public async Task PatchAsync(global::TestHarness.Ext.Http.Kiota.Client.Models.Post body, Action> requestConfiguration = default, CancellationToken cancellationToken = default) + { +#endif + _ = body ?? throw new ArgumentNullException(nameof(body)); + var requestInfo = ToPatchRequestInformation(body, requestConfiguration); + return await RequestAdapter.SendAsync(requestInfo, global::TestHarness.Ext.Http.Kiota.Client.Models.Post.CreateFromDiscriminatorValue, default, cancellationToken).ConfigureAwait(false); + } + /// + /// Delete post + /// + /// A + /// Configuration for the request such as headers, query parameters, and middleware options. +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public RequestInformation ToDeleteRequestInformation(Action>? requestConfiguration = default) + { +#nullable restore +#else + public RequestInformation ToDeleteRequestInformation(Action> requestConfiguration = default) + { +#endif + var requestInfo = new RequestInformation(Method.DELETE, UrlTemplate, PathParameters); + requestInfo.Configure(requestConfiguration); + return requestInfo; + } + /// + /// Get post by ID + /// + /// A + /// Configuration for the request such as headers, query parameters, and middleware options. +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public RequestInformation ToGetRequestInformation(Action>? requestConfiguration = default) + { +#nullable restore +#else + public RequestInformation ToGetRequestInformation(Action> requestConfiguration = default) + { +#endif + var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters); + requestInfo.Configure(requestConfiguration); + requestInfo.Headers.TryAdd("Accept", "application/json"); + return requestInfo; + } + /// + /// Update post + /// + /// A + /// The request body + /// Configuration for the request such as headers, query parameters, and middleware options. +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public RequestInformation ToPatchRequestInformation(global::TestHarness.Ext.Http.Kiota.Client.Models.Post body, Action>? requestConfiguration = default) + { +#nullable restore +#else + public RequestInformation ToPatchRequestInformation(global::TestHarness.Ext.Http.Kiota.Client.Models.Post body, Action> requestConfiguration = default) + { +#endif + _ = body ?? throw new ArgumentNullException(nameof(body)); + var requestInfo = new RequestInformation(Method.PATCH, UrlTemplate, PathParameters); + requestInfo.Configure(requestConfiguration); + requestInfo.Headers.TryAdd("Accept", "application/json"); + requestInfo.SetContentFromParsable(RequestAdapter, "application/json", body); + return requestInfo; + } + /// + /// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored. + /// + /// A + /// The raw URL to use for the request builder. + public global::TestHarness.Ext.Http.Kiota.Client.Posts.Item.PostItemRequestBuilder WithUrl(string rawUrl) + { + return new global::TestHarness.Ext.Http.Kiota.Client.Posts.Item.PostItemRequestBuilder(rawUrl, RequestAdapter); + } + /// + /// Configuration for the request such as headers, query parameters, and middleware options. + /// + [Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")] + [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.16.0")] + public partial class PostItemRequestBuilderDeleteRequestConfiguration : RequestConfiguration + { + } + /// + /// Configuration for the request such as headers, query parameters, and middleware options. + /// + [Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")] + [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.16.0")] + public partial class PostItemRequestBuilderGetRequestConfiguration : RequestConfiguration + { + } + /// + /// Configuration for the request such as headers, query parameters, and middleware options. + /// + [Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")] + [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.16.0")] + public partial class PostItemRequestBuilderPatchRequestConfiguration : RequestConfiguration + { + } + } +} diff --git a/testing/TestHarness/TestHarness/Ext/Http/Kiota/Client/Posts/PostsRequestBuilder.cs b/testing/TestHarness/TestHarness/Ext/Http/Kiota/Client/Posts/PostsRequestBuilder.cs new file mode 100644 index 0000000000..1291eec093 --- /dev/null +++ b/testing/TestHarness/TestHarness/Ext/Http/Kiota/Client/Posts/PostsRequestBuilder.cs @@ -0,0 +1,187 @@ +// +using Microsoft.Kiota.Abstractions.Extensions; +using Microsoft.Kiota.Abstractions.Serialization; +using Microsoft.Kiota.Abstractions; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using System.Threading; +using System; +using TestHarness.Ext.Http.Kiota.Client.Models; +using TestHarness.Ext.Http.Kiota.Client.Posts.Item; +namespace TestHarness.Ext.Http.Kiota.Client.Posts +{ + /// + /// Builds and executes requests for operations under \posts + /// + [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.16.0")] + public partial class PostsRequestBuilder : BaseRequestBuilder + { + /// Gets an item from the TestHarness.Ext.Http.Kiota.Client.posts.item collection + /// key: id of post + /// A + public global::TestHarness.Ext.Http.Kiota.Client.Posts.Item.PostItemRequestBuilder this[int position] + { + get + { + var urlTplParams = new Dictionary(PathParameters); + urlTplParams.Add("post%2Did", position); + return new global::TestHarness.Ext.Http.Kiota.Client.Posts.Item.PostItemRequestBuilder(urlTplParams, RequestAdapter); + } + } + /// Gets an item from the TestHarness.Ext.Http.Kiota.Client.posts.item collection + /// key: id of post + /// A + [Obsolete("This indexer is deprecated and will be removed in the next major version. Use the one with the typed parameter instead.")] + public global::TestHarness.Ext.Http.Kiota.Client.Posts.Item.PostItemRequestBuilder this[string position] + { + get + { + var urlTplParams = new Dictionary(PathParameters); + if (!string.IsNullOrWhiteSpace(position)) urlTplParams.Add("post%2Did", position); + return new global::TestHarness.Ext.Http.Kiota.Client.Posts.Item.PostItemRequestBuilder(urlTplParams, RequestAdapter); + } + } + /// + /// Instantiates a new and sets the default values. + /// + /// Path parameters for the request + /// The request adapter to use to execute the requests. + public PostsRequestBuilder(Dictionary pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/posts{?title*,userId*}", pathParameters) + { + } + /// + /// Instantiates a new and sets the default values. + /// + /// The raw URL to use for the request builder. + /// The request adapter to use to execute the requests. + public PostsRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/posts{?title*,userId*}", rawUrl) + { + } + /// + /// Get posts + /// + /// A List<global::TestHarness.Ext.Http.Kiota.Client.Models.Post> + /// Cancellation token to use when cancelling requests + /// Configuration for the request such as headers, query parameters, and middleware options. +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public async Task?> GetAsync(Action>? requestConfiguration = default, CancellationToken cancellationToken = default) + { +#nullable restore +#else + public async Task> GetAsync(Action> requestConfiguration = default, CancellationToken cancellationToken = default) + { +#endif + var requestInfo = ToGetRequestInformation(requestConfiguration); + var collectionResult = await RequestAdapter.SendCollectionAsync(requestInfo, global::TestHarness.Ext.Http.Kiota.Client.Models.Post.CreateFromDiscriminatorValue, default, cancellationToken).ConfigureAwait(false); + return collectionResult?.AsList(); + } + /// + /// Create post + /// + /// A + /// The request body + /// Cancellation token to use when cancelling requests + /// Configuration for the request such as headers, query parameters, and middleware options. +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public async Task PostAsync(global::TestHarness.Ext.Http.Kiota.Client.Models.Post body, Action>? requestConfiguration = default, CancellationToken cancellationToken = default) + { +#nullable restore +#else + public async Task PostAsync(global::TestHarness.Ext.Http.Kiota.Client.Models.Post body, Action> requestConfiguration = default, CancellationToken cancellationToken = default) + { +#endif + _ = body ?? throw new ArgumentNullException(nameof(body)); + var requestInfo = ToPostRequestInformation(body, requestConfiguration); + return await RequestAdapter.SendAsync(requestInfo, global::TestHarness.Ext.Http.Kiota.Client.Models.Post.CreateFromDiscriminatorValue, default, cancellationToken).ConfigureAwait(false); + } + /// + /// Get posts + /// + /// A + /// Configuration for the request such as headers, query parameters, and middleware options. +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public RequestInformation ToGetRequestInformation(Action>? requestConfiguration = default) + { +#nullable restore +#else + public RequestInformation ToGetRequestInformation(Action> requestConfiguration = default) + { +#endif + var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters); + requestInfo.Configure(requestConfiguration); + requestInfo.Headers.TryAdd("Accept", "application/json"); + return requestInfo; + } + /// + /// Create post + /// + /// A + /// The request body + /// Configuration for the request such as headers, query parameters, and middleware options. +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public RequestInformation ToPostRequestInformation(global::TestHarness.Ext.Http.Kiota.Client.Models.Post body, Action>? requestConfiguration = default) + { +#nullable restore +#else + public RequestInformation ToPostRequestInformation(global::TestHarness.Ext.Http.Kiota.Client.Models.Post body, Action> requestConfiguration = default) + { +#endif + _ = body ?? throw new ArgumentNullException(nameof(body)); + var requestInfo = new RequestInformation(Method.POST, UrlTemplate, PathParameters); + requestInfo.Configure(requestConfiguration); + requestInfo.Headers.TryAdd("Accept", "application/json"); + requestInfo.SetContentFromParsable(RequestAdapter, "application/json", body); + return requestInfo; + } + /// + /// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored. + /// + /// A + /// The raw URL to use for the request builder. + public global::TestHarness.Ext.Http.Kiota.Client.Posts.PostsRequestBuilder WithUrl(string rawUrl) + { + return new global::TestHarness.Ext.Http.Kiota.Client.Posts.PostsRequestBuilder(rawUrl, RequestAdapter); + } + /// + /// Get posts + /// + [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.16.0")] + public partial class PostsRequestBuilderGetQueryParameters + { + /// Filter results by title +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + [QueryParameter("title")] + public string? Title { get; set; } +#nullable restore +#else + [QueryParameter("title")] + public string Title { get; set; } +#endif + /// Filter results by user ID + [QueryParameter("userId")] + public int? UserId { get; set; } + } + /// + /// Configuration for the request such as headers, query parameters, and middleware options. + /// + [Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")] + [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.16.0")] + public partial class PostsRequestBuilderGetRequestConfiguration : RequestConfiguration + { + } + /// + /// Configuration for the request such as headers, query parameters, and middleware options. + /// + [Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")] + [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.16.0")] + public partial class PostsRequestBuilderPostRequestConfiguration : RequestConfiguration + { + } + } +} diff --git a/testing/TestHarness/TestHarness/Ext/Http/Kiota/Client/PostsApiClient.cs b/testing/TestHarness/TestHarness/Ext/Http/Kiota/Client/PostsApiClient.cs new file mode 100644 index 0000000000..eb6adb2584 --- /dev/null +++ b/testing/TestHarness/TestHarness/Ext/Http/Kiota/Client/PostsApiClient.cs @@ -0,0 +1,46 @@ +// +using Microsoft.Kiota.Abstractions.Extensions; +using Microsoft.Kiota.Abstractions; +using Microsoft.Kiota.Serialization.Form; +using Microsoft.Kiota.Serialization.Json; +using Microsoft.Kiota.Serialization.Multipart; +using Microsoft.Kiota.Serialization.Text; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using System; +using TestHarness.Ext.Http.Kiota.Client.Posts; +namespace TestHarness.Ext.Http.Kiota.Client +{ + /// + /// The main entry point of the SDK, exposes the configuration and the fluent API. + /// + [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.16.0")] + public partial class PostsApiClient : BaseRequestBuilder + { + /// The posts property + public global::TestHarness.Ext.Http.Kiota.Client.Posts.PostsRequestBuilder Posts + { + get => new global::TestHarness.Ext.Http.Kiota.Client.Posts.PostsRequestBuilder(PathParameters, RequestAdapter); + } + /// + /// Instantiates a new and sets the default values. + /// + /// The request adapter to use to execute the requests. + public PostsApiClient(IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}", new Dictionary()) + { + ApiClientBuilder.RegisterDefaultSerializer(); + ApiClientBuilder.RegisterDefaultSerializer(); + ApiClientBuilder.RegisterDefaultSerializer(); + ApiClientBuilder.RegisterDefaultSerializer(); + ApiClientBuilder.RegisterDefaultDeserializer(); + ApiClientBuilder.RegisterDefaultDeserializer(); + ApiClientBuilder.RegisterDefaultDeserializer(); + if (string.IsNullOrEmpty(RequestAdapter.BaseUrl)) + { + RequestAdapter.BaseUrl = "https://jsonplaceholder.typicode.com"; + } + PathParameters.TryAdd("baseurl", RequestAdapter.BaseUrl); + } + } +} diff --git a/testing/TestHarness/TestHarness/Ext/Http/Kiota/Client/kiota-lock.json b/testing/TestHarness/TestHarness/Ext/Http/Kiota/Client/kiota-lock.json new file mode 100644 index 0000000000..764ed34a5d --- /dev/null +++ b/testing/TestHarness/TestHarness/Ext/Http/Kiota/Client/kiota-lock.json @@ -0,0 +1,33 @@ +{ + "descriptionHash": "37FE8D63AB108F248AAA5632A5D37D625F8B9E3A6C439952B4029B0AF49579F337028FE07768B89D6F9210460D77E116F312CB505A43E8EB877131E772464F3B", + "descriptionLocation": "../posts-api.yml", + "lockFileVersion": "1.0.0", + "kiotaVersion": "1.16.0", + "clientClassName": "PostsApiClient", + "clientNamespaceName": "TestHarness.Ext.Http.Kiota.Client", + "language": "CSharp", + "usesBackingStore": false, + "excludeBackwardCompatible": false, + "includeAdditionalData": true, + "disableSSLValidation": false, + "serializers": [ + "Microsoft.Kiota.Serialization.Json.JsonSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Text.TextSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Form.FormSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Multipart.MultipartSerializationWriterFactory" + ], + "deserializers": [ + "Microsoft.Kiota.Serialization.Json.JsonParseNodeFactory", + "Microsoft.Kiota.Serialization.Text.TextParseNodeFactory", + "Microsoft.Kiota.Serialization.Form.FormParseNodeFactory" + ], + "structuredMimeTypes": [ + "application/json", + "text/plain;q=0.9", + "application/x-www-form-urlencoded;q=0.2", + "multipart/form-data;q=0.1" + ], + "includePatterns": [], + "excludePatterns": [], + "disabledValidationRules": [] +} \ No newline at end of file diff --git a/testing/TestHarness/TestHarness/Ext/Http/Kiota/KiotaHomePage.xaml b/testing/TestHarness/TestHarness/Ext/Http/Kiota/KiotaHomePage.xaml new file mode 100644 index 0000000000..f8db4a4f85 --- /dev/null +++ b/testing/TestHarness/TestHarness/Ext/Http/Kiota/KiotaHomePage.xaml @@ -0,0 +1,32 @@ + + + + + + + + + + + +