From 8a7991c0bf02c87e600e24ca96125c9393aaec36 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 22 Nov 2022 11:29:32 -0500 Subject: [PATCH 01/42] - adds basic infrastructure to authenticate to github Signed-off-by: Vincent Biret --- .../Configuration/SearchConfiguration.cs | 1 + src/Kiota.Builder/KiotaBuilder.cs | 8 +- src/Kiota.Builder/KiotaSearcher.cs | 10 +- .../DeviceCode/GitHubAccessCodeResponse.cs | 19 +++ .../DeviceCode/GitHubAcessTokenProvider.cs | 83 +++++++++++ .../GitHubAuthenticationProvider.cs | 49 +++++++ .../DeviceCode/GitHubDeviceCodeResponse.cs | 17 +++ .../GitHubAnonymousAuthenticationProvider.cs | 2 +- src/Kiota.Web/Pages/Generate.razor | 2 +- src/Kiota.Web/Pages/Index.razor | 3 +- src/Kiota.Web/Pages/Show.razor | 5 +- src/Kiota.Web/wwwroot/appsettings.json | 3 +- src/kiota/Handlers/BaseKiotaCommandHandler.cs | 4 +- .../Handlers/KiotaDownloadCommandHandler.cs | 5 +- .../Handlers/KiotaGenerationCommandHandler.cs | 2 +- .../KiotaGitHubLoginCommandHanlder.cs | 55 +++++++ src/kiota/Handlers/KiotaInfoCommandHandler.cs | 4 +- .../Handlers/KiotaSeachBasedCommandHandler.cs | 2 +- .../Handlers/KiotaSearchCommandHandler.cs | 2 +- src/kiota/Handlers/KiotaShowCommandHandler.cs | 2 +- .../Handlers/KiotaUpdateCommandHandler.cs | 2 +- src/kiota/KiotaHost.cs | 30 ++++ src/kiota/appsettings.json | 3 +- .../GenerateSample.cs | 17 ++- .../Kiota.Builder.Tests/KiotaBuilderTests.cs | 134 +++++++++--------- .../Kiota.Builder.Tests/KiotaSearcherTests.cs | 28 ++-- 26 files changed, 388 insertions(+), 104 deletions(-) create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAccessCodeResponse.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAcessTokenProvider.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAuthenticationProvider.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubDeviceCodeResponse.cs create mode 100644 src/kiota/Handlers/KiotaGitHubLoginCommandHanlder.cs diff --git a/src/Kiota.Builder/Configuration/SearchConfiguration.cs b/src/Kiota.Builder/Configuration/SearchConfiguration.cs index 1acf914595..7b2588b766 100644 --- a/src/Kiota.Builder/Configuration/SearchConfiguration.cs +++ b/src/Kiota.Builder/Configuration/SearchConfiguration.cs @@ -5,4 +5,5 @@ namespace Kiota.Builder.Configuration; public class SearchConfiguration : SearchConfigurationBase { public Uri APIsGuruListUrl { get; set; } = new ("https://raw.githubusercontent.com/APIs-guru/openapi-directory/gh-pages/v2/list.json"); public Uri GitHubBlockListUrl { get; set; } = new ("https://raw.githubusercontent.com/microsoft/kiota/main/resources/index-block-list.yml"); + public string GitHubAppId { get; set; } = "0adc165b71f9824f4282"; } diff --git a/src/Kiota.Builder/KiotaBuilder.cs b/src/Kiota.Builder/KiotaBuilder.cs index d3a7b8682f..146829ae5e 100644 --- a/src/Kiota.Builder/KiotaBuilder.cs +++ b/src/Kiota.Builder/KiotaBuilder.cs @@ -34,14 +34,19 @@ public class KiotaBuilder { private readonly ILogger logger; private readonly GenerationConfiguration config; + private readonly HttpClient httpClient; private OpenApiDocument originalDocument; private OpenApiDocument openApiDocument; internal void SetOpenApiDocument(OpenApiDocument document) => openApiDocument = document ?? throw new ArgumentNullException(nameof(document)); - public KiotaBuilder(ILogger logger, GenerationConfiguration config) + public KiotaBuilder(ILogger logger, GenerationConfiguration config, HttpClient client) { + ArgumentNullException.ThrowIfNull(logger); + ArgumentNullException.ThrowIfNull(config); + ArgumentNullException.ThrowIfNull(client); this.logger = logger; this.config = config; + this.httpClient = client; } private void CleanOutputDirectory() { @@ -222,7 +227,6 @@ private async Task LoadStream(string inputPath, CancellationToken cancel Stream input; if (inputPath.StartsWith("http")) try { - using var httpClient = new HttpClient(); var cachingProvider = new DocumentCachingProvider(httpClient, logger) { ClearCache = config.ClearCache, }; diff --git a/src/Kiota.Builder/KiotaSearcher.cs b/src/Kiota.Builder/KiotaSearcher.cs index 8ed121a0c9..2fab02a774 100644 --- a/src/Kiota.Builder/KiotaSearcher.cs +++ b/src/Kiota.Builder/KiotaSearcher.cs @@ -16,23 +16,25 @@ namespace Kiota.Builder; public class KiotaSearcher { private readonly ILogger logger; private readonly SearchConfiguration config; - public KiotaSearcher(ILogger logger, SearchConfiguration config) { + private readonly HttpClient httpClient; + public KiotaSearcher(ILogger logger, SearchConfiguration config, HttpClient httpClient) { ArgumentNullException.ThrowIfNull(logger); ArgumentNullException.ThrowIfNull(config); + ArgumentNullException.ThrowIfNull(httpClient); this.logger = logger; this.config = config; + this.httpClient = httpClient; } public async Task> SearchAsync(CancellationToken cancellationToken) { if (string.IsNullOrEmpty(config.SearchTerm)) { logger.LogError("no search term provided"); return new Dictionary(); } - using var client = new HttpClient(); - var apiGurusSearchProvider = new APIsGuruSearchProvider(config.APIsGuruListUrl, client, logger, config.ClearCache); + var apiGurusSearchProvider = new APIsGuruSearchProvider(config.APIsGuruListUrl, httpClient, logger, config.ClearCache); logger.LogDebug("searching for {searchTerm}", config.SearchTerm); logger.LogDebug("searching APIs.guru with url {url}", config.APIsGuruListUrl); var oasProvider = new OpenApiSpecSearchProvider(); - var githubProvider = new GitHubSearchProvider(client, config.GitHubBlockListUrl, logger, config.ClearCache); + var githubProvider = new GitHubSearchProvider(httpClient, config.GitHubBlockListUrl, logger, config.ClearCache); var results = await Task.WhenAll( SearchProviderAsync(apiGurusSearchProvider, cancellationToken), SearchProviderAsync(oasProvider, cancellationToken), diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAccessCodeResponse.cs b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAccessCodeResponse.cs new file mode 100644 index 0000000000..36f71bd37b --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAccessCodeResponse.cs @@ -0,0 +1,19 @@ +using System; +using System.Text.Json.Serialization; + +namespace Kiota.Builder.SearchProviders.GitHub.Authentication.DeviceCode; + +internal class GitHubAccessCodeResponse { + [JsonPropertyName("access_token")] + public string AccessToken { get; set; } + [JsonPropertyName("token_type")] + public string TokenType { get; set; } + [JsonPropertyName("scope")] + public string Scope { get; set; } + [JsonPropertyName("error")] + public string Error { get; set; } + [JsonPropertyName("error_description")] + public string ErrorDescription { get; set; } + [JsonPropertyName("error_uri")] + public Uri ErrorUri { get; set; } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAcessTokenProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAcessTokenProvider.cs new file mode 100644 index 0000000000..e122c0931b --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAcessTokenProvider.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Kiota.Abstractions.Authentication; + +namespace Kiota.Builder.SearchProviders.GitHub.Authentication.DeviceCode; +internal class GitHubAccessTokenProvider : IAccessTokenProvider +{ + internal required Action MessageCallback { get; init; } + public AllowedHostsValidator AllowedHostsValidator {get;set;} = new (); + internal required string ClientId { get; init; } + internal required string Scope { get; init; } + internal required HttpClient HttpClient {get; init;} + internal string BaseLoginUrl { get; init; } = "https://github.com/login"; + public async Task GetAuthorizationTokenAsync(Uri uri, Dictionary additionalAuthenticationContext = null, CancellationToken cancellationToken = default) + { + if(!AllowedHostsValidator.IsUrlHostValid(uri)) + return string.Empty; + if(!uri.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase)) + throw new ArgumentException("Only https is supported"); + + var deviceCodeResponse = await GetDeviceCodeAsync(cancellationToken); + MessageCallback(deviceCodeResponse.VerificationUri, deviceCodeResponse.UserCode); + var tokenResponse = await PollForTokenAsync(deviceCodeResponse, cancellationToken); + return tokenResponse.AccessToken; + } + private async Task PollForTokenAsync(GitHubDeviceCodeResponse deviceCodeResponse, CancellationToken cancellationToken) + { + var timeOutTask = Task.Delay(TimeSpan.FromSeconds(deviceCodeResponse.ExpiresInSeconds), cancellationToken); + var pollTask = Task.Run(async () => { + while(!cancellationToken.IsCancellationRequested) { + var tokenResponse = await GetTokenAsync(deviceCodeResponse, cancellationToken); + if(tokenResponse != null) + return tokenResponse; + await Task.Delay(TimeSpan.FromSeconds(deviceCodeResponse.IntervalInSeconds), cancellationToken); + } + return null; + }, cancellationToken); + var completedTask = await Task.WhenAny(timeOutTask, pollTask); + if(completedTask == timeOutTask) + throw new TimeoutException("The device code has expired."); + return await pollTask; + } + private async Task GetTokenAsync(GitHubDeviceCodeResponse deviceCodeResponse, CancellationToken cancellationToken) + { + using var tokenRequest = new HttpRequestMessage(HttpMethod.Post, $"{BaseLoginUrl}/oauth/access_token") { + Content = new FormUrlEncodedContent(new Dictionary { + { "client_id", ClientId }, + { "device_code", deviceCodeResponse.DeviceCode }, + { "grant_type", "urn:ietf:params:oauth:grant-type:device_code" } + }) + }; + tokenRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + using var tokenResponse = await HttpClient.SendAsync(tokenRequest, cancellationToken); + tokenResponse.EnsureSuccessStatusCode(); + var tokenContent = await tokenResponse.Content.ReadAsStringAsync(cancellationToken); + var result = JsonSerializer.Deserialize(tokenContent); + if ("authorization_pending".Equals(result.Error, StringComparison.OrdinalIgnoreCase)) + return null; + else if (!string.IsNullOrEmpty(result.Error)) + throw new Exception($"Error while getting token: {result.Error} - {result.ErrorDescription}"); + else + return result; + } + + private async Task GetDeviceCodeAsync(CancellationToken cancellationToken) { + using var codeRequest = new HttpRequestMessage(HttpMethod.Post, $"{BaseLoginUrl}/device/code") { + Content = new FormUrlEncodedContent(new Dictionary { + { "client_id", ClientId }, + { "scope", Scope } + }) + }; + codeRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + using var codeResponse = await HttpClient.SendAsync(codeRequest, cancellationToken); + codeResponse.EnsureSuccessStatusCode(); + var codeResponseContent = await codeResponse.Content.ReadAsStringAsync(cancellationToken); + return JsonSerializer.Deserialize(codeResponseContent); + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAuthenticationProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAuthenticationProvider.cs new file mode 100644 index 0000000000..6a998e6067 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAuthenticationProvider.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Kiota.Abstractions; +using Microsoft.Kiota.Abstractions.Authentication; + +namespace Kiota.Builder.SearchProviders.GitHub.Authentication.DeviceCode; + +public class GitHubAuthenticationProvider : GitHubAnonymousAuthenticationProvider +{ + public GitHubAuthenticationProvider(string clientId, string scope, IEnumerable validHosts, HttpClient httpClient, Action messageCallback) + { + if (string.IsNullOrEmpty(clientId)) + throw new ArgumentNullException(nameof(clientId)); + if (string.IsNullOrEmpty(scope)) + throw new ArgumentNullException(nameof(scope)); + ArgumentNullException.ThrowIfNull(validHosts); + ArgumentNullException.ThrowIfNull(httpClient); + ArgumentNullException.ThrowIfNull(messageCallback); + + AccessTokenProvider = new GitHubAccessTokenProvider { + ClientId = clientId, + Scope = scope, + AllowedHostsValidator = new AllowedHostsValidator(validHosts), + HttpClient = httpClient, + MessageCallback = messageCallback + }; + } + public IAccessTokenProvider AccessTokenProvider {get; private set;} + private const string AuthorizationHeaderKey = "Authorization"; + private const string ClaimsKey = "claims"; + public override async Task AuthenticateRequestAsync(RequestInformation request, Dictionary additionalAuthenticationContext = null, CancellationToken cancellationToken = default) { + ArgumentNullException.ThrowIfNull(request); + await base.AuthenticateRequestAsync(request, additionalAuthenticationContext, cancellationToken).ConfigureAwait(false); + if(additionalAuthenticationContext != null && + additionalAuthenticationContext.ContainsKey(ClaimsKey) && + request.Headers.ContainsKey(AuthorizationHeaderKey)) + request.Headers.Remove(AuthorizationHeaderKey); + + if(!request.Headers.ContainsKey(AuthorizationHeaderKey)) + { + var token = await AccessTokenProvider.GetAuthorizationTokenAsync(request.URI, additionalAuthenticationContext, cancellationToken); + if(!string.IsNullOrEmpty(token)) + request.Headers.Add(AuthorizationHeaderKey, $"Bearer {token}"); + } + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubDeviceCodeResponse.cs b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubDeviceCodeResponse.cs new file mode 100644 index 0000000000..caa1978be9 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubDeviceCodeResponse.cs @@ -0,0 +1,17 @@ +using System; +using System.Text.Json.Serialization; + +namespace Kiota.Builder.SearchProviders.GitHub.Authentication.DeviceCode; + +internal class GitHubDeviceCodeResponse { + [JsonPropertyName("device_code")] + public string DeviceCode { get; set; } + [JsonPropertyName("user_code")] + public string UserCode { get; set; } + [JsonPropertyName("verification_uri")] + public Uri VerificationUri { get; set; } + [JsonPropertyName("expires_in")] + public uint ExpiresInSeconds { get; set; } + [JsonPropertyName("interval")] + public uint IntervalInSeconds { get; set; } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/GitHubAnonymousAuthenticationProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/GitHubAnonymousAuthenticationProvider.cs index 177c3530ba..f5d790508a 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/GitHubAnonymousAuthenticationProvider.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/GitHubAnonymousAuthenticationProvider.cs @@ -7,7 +7,7 @@ namespace Kiota.Builder.SearchProviders.GitHub.Authentication; public class GitHubAnonymousAuthenticationProvider : IAuthenticationProvider { - public Task AuthenticateRequestAsync(RequestInformation request, Dictionary additionalAuthenticationContext = null, CancellationToken cancellationToken = default) + public virtual Task AuthenticateRequestAsync(RequestInformation request, Dictionary additionalAuthenticationContext = null, CancellationToken cancellationToken = default) { request.Headers.Add("User-Agent", $"Kiota/{GetType().Assembly.GetName().Version}"); return Task.CompletedTask; diff --git a/src/Kiota.Web/Pages/Generate.razor b/src/Kiota.Web/Pages/Generate.razor index cceebbb68b..0c7d343a1b 100644 --- a/src/Kiota.Web/Pages/Generate.razor +++ b/src/Kiota.Web/Pages/Generate.razor @@ -184,7 +184,7 @@ SearchTerm = SearchTerm, Version = Version, }; - var searchResults = await new KiotaSearcher(logger, searchConfig).SearchAsync(ComponentDetached); + var searchResults = await new KiotaSearcher(logger, searchConfig, Http).SearchAsync(ComponentDetached); if(searchResults.Any()) DescriptionUrl = searchResults.Values.First().DescriptionUrl.ToString(); } diff --git a/src/Kiota.Web/Pages/Index.razor b/src/Kiota.Web/Pages/Index.razor index 638e818343..000a0d341a 100644 --- a/src/Kiota.Web/Pages/Index.razor +++ b/src/Kiota.Web/Pages/Index.razor @@ -12,6 +12,7 @@ @using Markdig @inject IStringLocalizer Loc @inject NavigationManager navManager +@inject HttpClient Http @Loc["PageTitle"] @@ -106,7 +107,7 @@ SearchTerm = SearchTerm, }; var logger = LoggerFactory.CreateLogger(); - SearchResults = await new KiotaSearcher(logger, searchConfig).SearchAsync(ComponentDetached); + SearchResults = await new KiotaSearcher(logger, searchConfig, Http).SearchAsync(ComponentDetached); IsLoading = false; if(AppInsights != null) await AppInsights.StopTrackEvent(searchTelemetryKey, new Dictionary { diff --git a/src/Kiota.Web/Pages/Show.razor b/src/Kiota.Web/Pages/Show.razor index 4d5e2301aa..d60df30468 100644 --- a/src/Kiota.Web/Pages/Show.razor +++ b/src/Kiota.Web/Pages/Show.razor @@ -11,6 +11,7 @@ @using System.Globalization @using Microsoft.Extensions.Localization @inject IStringLocalizer Loc +@inject HttpClient Http @Loc["PageTitle"] @@ -71,7 +72,7 @@ SearchTerm = SearchTerm, Version = Version, }; - var searchResults = await new KiotaSearcher(logger, searchConfig).SearchAsync(ComponentDetached); + var searchResults = await new KiotaSearcher(logger, searchConfig, Http).SearchAsync(ComponentDetached); if(searchResults.Any()) DescriptionUrl = searchResults.Values.First().DescriptionUrl.ToString(); } @@ -112,7 +113,7 @@ Version = Version, }; var logger = LoggerFactory.CreateLogger(); - SearchResults = await new KiotaSearcher(logger, searchConfig).SearchAsync(ComponentDetached); + SearchResults = await new KiotaSearcher(logger, searchConfig, Http).SearchAsync(ComponentDetached); DescriptionUrl = SearchResults.First().Value.DescriptionUrl.ToString(); } if(AppInsights != null) diff --git a/src/Kiota.Web/wwwroot/appsettings.json b/src/Kiota.Web/wwwroot/appsettings.json index 2c99b87248..f495cfbc59 100644 --- a/src/Kiota.Web/wwwroot/appsettings.json +++ b/src/Kiota.Web/wwwroot/appsettings.json @@ -7,7 +7,8 @@ }, "Search": { "APIsGuruListUrl": "https://raw.githubusercontent.com/APIs-guru/openapi-directory/gh-pages/v2/list.json", - "GitHubBlockListUrl": "https://raw.githubusercontent.com/microsoft/kiota/main/resources/index-block-list.yml" + "GitHubBlockListUrl": "https://raw.githubusercontent.com/microsoft/kiota/main/resources/index-block-list.yml", + "GitHubAppId": "0adc165b71f9824f4282" }, "Logging": { "LogLevel": { diff --git a/src/kiota/Handlers/BaseKiotaCommandHandler.cs b/src/kiota/Handlers/BaseKiotaCommandHandler.cs index cdc15d8fd5..88bb5dd273 100644 --- a/src/kiota/Handlers/BaseKiotaCommandHandler.cs +++ b/src/kiota/Handlers/BaseKiotaCommandHandler.cs @@ -4,6 +4,7 @@ using System.CommandLine.Invocation; using System.IO; using System.Linq; +using System.Net.Http; using System.Threading.Tasks; using Kiota.Builder; using Kiota.Builder.Configuration; @@ -14,6 +15,7 @@ namespace kiota.Handlers; internal abstract class BaseKiotaCommandHandler : ICommandHandler { + internal static readonly HttpClient httpClient = new(); public Option LogLevelOption { get;set; } protected KiotaConfiguration Configuration { get => ConfigurationFactory.Value; } private readonly Lazy ConfigurationFactory = new (() => { @@ -158,7 +160,7 @@ protected void DisplayCleanHint(string commandName) { $"Example: kiota {commandName} --clean-output"); } } - protected void DisplayInfoAdvanced() { + protected void DisplayInfoAdvancedHint() { if(TutorialMode) { DisplayHint("Hint: use the language argument to get the list of dependencies you need to add to your project.", "Example: kiota info -l "); diff --git a/src/kiota/Handlers/KiotaDownloadCommandHandler.cs b/src/kiota/Handlers/KiotaDownloadCommandHandler.cs index bd7b383a41..662e8b5174 100644 --- a/src/kiota/Handlers/KiotaDownloadCommandHandler.cs +++ b/src/kiota/Handlers/KiotaDownloadCommandHandler.cs @@ -46,7 +46,7 @@ public override async Task InvokeAsync(InvocationContext context) logger.LogTrace("configuration: {configuration}", JsonSerializer.Serialize(Configuration)); try { - var results = await new KiotaSearcher(logger, Configuration.Search).SearchAsync(cancellationToken); + var results = await new KiotaSearcher(logger, Configuration.Search, httpClient).SearchAsync(cancellationToken); return await SaveResultsAsync(results, logger, cancellationToken); } catch (Exception ex) { #if DEBUG @@ -100,8 +100,7 @@ private async Task SaveResultsAsync(IDictionary resul if(!Directory.Exists(Path.GetDirectoryName(path))) Directory.CreateDirectory(Path.GetDirectoryName(path)); - using var client = new HttpClient(); - var cacheProvider = new DocumentCachingProvider(client, logger) { + var cacheProvider = new DocumentCachingProvider(httpClient, logger) { ClearCache = true, }; await using var document = await cacheProvider.GetDocumentAsync(result.Value.DescriptionUrl, "download", Path.GetFileName(path), cancellationToken: cancellationToken); diff --git a/src/kiota/Handlers/KiotaGenerationCommandHandler.cs b/src/kiota/Handlers/KiotaGenerationCommandHandler.cs index def97c3285..b3c5984d8f 100644 --- a/src/kiota/Handlers/KiotaGenerationCommandHandler.cs +++ b/src/kiota/Handlers/KiotaGenerationCommandHandler.cs @@ -74,7 +74,7 @@ public override async Task InvokeAsync(InvocationContext context) logger.LogTrace("configuration: {configuration}", JsonSerializer.Serialize(Configuration)); try { - var result = await new KiotaBuilder(logger, Configuration.Generation).GenerateClientAsync(cancellationToken); + var result = await new KiotaBuilder(logger, Configuration.Generation, httpClient).GenerateClientAsync(cancellationToken); if (result) DisplaySuccess("Generation completed successfully"); else { diff --git a/src/kiota/Handlers/KiotaGitHubLoginCommandHanlder.cs b/src/kiota/Handlers/KiotaGitHubLoginCommandHanlder.cs new file mode 100644 index 0000000000..5a70953ba1 --- /dev/null +++ b/src/kiota/Handlers/KiotaGitHubLoginCommandHanlder.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.CommandLine.Invocation; +using System.Threading; +using System.Threading.Tasks; +using Kiota.Builder.SearchProviders.GitHub.Authentication.DeviceCode; +using Microsoft.Extensions.Logging; +using Microsoft.Kiota.Abstractions; + +namespace kiota.Handlers; + +internal class KiotaGitHubLoginCommandHandler : BaseKiotaCommandHandler +{ + public override async Task InvokeAsync(InvocationContext context) + { + CancellationToken cancellationToken = (CancellationToken)context.BindingContext.GetService(typeof(CancellationToken)); + var (loggerFactory, logger) = GetLoggerAndFactory(context); + using (loggerFactory) { + try { + return await LoginAsync(cancellationToken); + } catch (Exception ex) { + #if DEBUG + logger.LogCritical(ex, "error downloading a description: {exceptionMessage}", ex.Message); + throw; // so debug tools go straight to the source of the exception when attached + #else + logger.LogCritical("error downloading a description: {exceptionMessage}", ex.Message); + return 1; + #endif + } + + } + } + private async Task LoginAsync(CancellationToken cancellationToken) { + //TODO handle caching + var authenticationProvider = new GitHubAuthenticationProvider(Configuration.Search.GitHubAppId, + "repo", + new List { "api.github.com"}, + httpClient, + (uri, code) => DisplayInfo($"Please go to {uri} and enter the code {code} to authenticate.")); + var dummyRequest = new RequestInformation() { + HttpMethod = Method.GET, + URI = new Uri("https://api.github.com/user"), + }; + await authenticationProvider.AuthenticateRequestAsync(dummyRequest, cancellationToken: cancellationToken); + if(dummyRequest.Headers.TryGetValue("Authorization", out var authHeaderValue) && authHeaderValue is string authHeader && authHeader.StartsWith("bearer", StringComparison.OrdinalIgnoreCase)) { + DisplaySuccess("Authentication successful."); + //TODO hint to search command + //TODO hint to logout command + return 0; + } else { + DisplayError("Authentication failed. Please try again."); + return 1; + } + } +} diff --git a/src/kiota/Handlers/KiotaInfoCommandHandler.cs b/src/kiota/Handlers/KiotaInfoCommandHandler.cs index ee6aab0251..296d7f3d5c 100644 --- a/src/kiota/Handlers/KiotaInfoCommandHandler.cs +++ b/src/kiota/Handlers/KiotaInfoCommandHandler.cs @@ -35,7 +35,7 @@ public override async Task InvokeAsync(InvocationContext context) if(!language.HasValue) { ShowLanguagesTable(); - DisplayInfoAdvanced(); + DisplayInfoAdvancedHint(); return 0; } @@ -54,7 +54,7 @@ public override async Task InvokeAsync(InvocationContext context) var instructions = Configuration.Languages; if(!string.IsNullOrEmpty(openapi)) try { - var builder = new KiotaBuilder(logger, Configuration.Generation); + var builder = new KiotaBuilder(logger, Configuration.Generation, httpClient); var result = await builder.GetLanguageInformationAsync(cancellationToken); if (result != null) instructions = result; diff --git a/src/kiota/Handlers/KiotaSeachBasedCommandHandler.cs b/src/kiota/Handlers/KiotaSeachBasedCommandHandler.cs index c006e33791..29af87b971 100644 --- a/src/kiota/Handlers/KiotaSeachBasedCommandHandler.cs +++ b/src/kiota/Handlers/KiotaSeachBasedCommandHandler.cs @@ -12,7 +12,7 @@ internal abstract class KiotaSearchBasedCommandHandler : BaseKiotaCommandHandler if (string.IsNullOrEmpty(openapi) && !string.IsNullOrEmpty(searchTerm)) { parentLogger.LogInformation("Searching for {searchTerm} in the OpenAPI description repository", searchTerm); - var searcher = new KiotaSearcher(loggerFactory.CreateLogger(), Configuration.Search); + var searcher = new KiotaSearcher(loggerFactory.CreateLogger(), Configuration.Search, httpClient); var results = await searcher.SearchAsync(cancellationToken); if (results.Count == 1) return (results.First().Value.DescriptionUrl.ToString(), null); diff --git a/src/kiota/Handlers/KiotaSearchCommandHandler.cs b/src/kiota/Handlers/KiotaSearchCommandHandler.cs index 9c735baa78..3e0c3b1719 100644 --- a/src/kiota/Handlers/KiotaSearchCommandHandler.cs +++ b/src/kiota/Handlers/KiotaSearchCommandHandler.cs @@ -37,7 +37,7 @@ public override async Task InvokeAsync(InvocationContext context) logger.LogTrace("configuration: {configuration}", JsonSerializer.Serialize(Configuration)); try { - var results = await new KiotaSearcher(logger, Configuration.Search).SearchAsync(cancellationToken); + var results = await new KiotaSearcher(logger, Configuration.Search, httpClient).SearchAsync(cancellationToken); DisplayResults(results); return 0; } catch (Exception ex) { diff --git a/src/kiota/Handlers/KiotaShowCommandHandler.cs b/src/kiota/Handlers/KiotaShowCommandHandler.cs index 50da71ef4b..d37cd46a6f 100644 --- a/src/kiota/Handlers/KiotaShowCommandHandler.cs +++ b/src/kiota/Handlers/KiotaShowCommandHandler.cs @@ -54,7 +54,7 @@ public override async Task InvokeAsync(InvocationContext context) Configuration.Generation.ExcludePatterns = excludePatterns.ToHashSet(); Configuration.Generation.ClearCache = clearCache; try { - var urlTreeNode = await new KiotaBuilder(logger, Configuration.Generation).GetUrlTreeNodeAsync(cancellationToken); + var urlTreeNode = await new KiotaBuilder(logger, Configuration.Generation, httpClient).GetUrlTreeNodeAsync(cancellationToken); var builder = new StringBuilder(); RenderNode(urlTreeNode, maxDepth, builder); diff --git a/src/kiota/Handlers/KiotaUpdateCommandHandler.cs b/src/kiota/Handlers/KiotaUpdateCommandHandler.cs index d75daf9ae6..b76af04b8f 100644 --- a/src/kiota/Handlers/KiotaUpdateCommandHandler.cs +++ b/src/kiota/Handlers/KiotaUpdateCommandHandler.cs @@ -42,7 +42,7 @@ public override async Task InvokeAsync(InvocationContext context) { return config; }).ToArray(); var results = await Task.WhenAll(configurations - .Select(x => new KiotaBuilder(logger, x) + .Select(x => new KiotaBuilder(logger, x, httpClient) .GenerateClientAsync(cancellationToken))); foreach (var (lockInfo, lockDirectoryPath) in locks) DisplaySuccess($"Update of {lockInfo.ClientClassName} client for {lockInfo.Language} at {lockDirectoryPath} completed"); diff --git a/src/kiota/KiotaHost.cs b/src/kiota/KiotaHost.cs index 019a048977..ca5f0e5929 100644 --- a/src/kiota/KiotaHost.cs +++ b/src/kiota/KiotaHost.cs @@ -20,8 +20,38 @@ public RootCommand GetRootCommand() { rootCommand.AddCommand(GetShowCommand()); rootCommand.AddCommand(GetInfoCommand()); rootCommand.AddCommand(GetUpdateCommand()); + rootCommand.AddCommand(GetLoginCommand()); + rootCommand.AddCommand(GetLogoutCommand()); return rootCommand; } + private static Command GetGitHubLoginCommand() { + var logLevelOption = GetLogLevelOption(); + var githubLoginCommand = new Command("github", "Logs in to GitHub using a device code flow.") + { + logLevelOption, + }; + githubLoginCommand.Handler = new KiotaGitHubLoginCommandHandler { + LogLevelOption = logLevelOption, + }; + return githubLoginCommand; + } + private static Command GetGitHubLogoutCommand() { + var logLevelOption = GetLogLevelOption(); + var githubLogoutCommand = new Command("github", "Logs out of GitHub.") { + logLevelOption, + }; + return githubLogoutCommand; + } + private static Command GetLoginCommand() { + var loginCommand = new Command("login", "Logs in to the Kiota registries so search/download/show/generate commands can access private API definitions."); + loginCommand.AddCommand(GetGitHubLoginCommand()); + return loginCommand; + } + private static Command GetLogoutCommand() { + var loginCommand = new Command("logout", "Logs out of Kiota registries."); + loginCommand.AddCommand(GetGitHubLogoutCommand()); + return loginCommand; + } private static Command GetInfoCommand() { var defaultGenerationConfiguration = new GenerationConfiguration(); var descriptionOption = GetDescriptionOption(defaultGenerationConfiguration.OpenAPIFilePath); diff --git a/src/kiota/appsettings.json b/src/kiota/appsettings.json index b7d094065f..4e2c5be2cc 100644 --- a/src/kiota/appsettings.json +++ b/src/kiota/appsettings.json @@ -7,7 +7,8 @@ }, "Search": { "APIsGuruListUrl": "https://raw.githubusercontent.com/APIs-guru/openapi-directory/gh-pages/v2/list.json", - "GitHubBlockListUrl": "https://raw.githubusercontent.com/microsoft/kiota/main/resources/index-block-list.yml" + "GitHubBlockListUrl": "https://raw.githubusercontent.com/microsoft/kiota/main/resources/index-block-list.yml", + "GitHubAppId": "0adc165b71f9824f4282" }, "Logging": { "LogLevel": { diff --git a/tests/Kiota.Builder.IntegrationTests/GenerateSample.cs b/tests/Kiota.Builder.IntegrationTests/GenerateSample.cs index da7624f772..1198625c07 100644 --- a/tests/Kiota.Builder.IntegrationTests/GenerateSample.cs +++ b/tests/Kiota.Builder.IntegrationTests/GenerateSample.cs @@ -1,12 +1,19 @@ -using System.Threading.Tasks; +using System; +using System.Net.Http; +using System.Threading.Tasks; using Kiota.Builder.Configuration; using Microsoft.Extensions.Logging; using Xunit; namespace Kiota.Builder.IntegrationTests; -public class GenerateSample +public class GenerateSample :IDisposable { + public void Dispose() { + _httpClient.Dispose(); + GC.SuppressFinalize(this); + } + private readonly HttpClient _httpClient = new(); [InlineData(GenerationLanguage.CSharp, false)] [InlineData(GenerationLanguage.Java, false)] [InlineData(GenerationLanguage.TypeScript, false)] @@ -29,7 +36,7 @@ public async Task GeneratesTodo(GenerationLanguage language, bool backingStore) OutputPath = $".\\Generated\\Todo\\{language}{backingStoreSuffix}", UsesBackingStore = backingStore, }; - await new KiotaBuilder(logger, configuration).GenerateClientAsync(new()); + await new KiotaBuilder(logger, configuration, _httpClient).GenerateClientAsync(new()); } [InlineData(GenerationLanguage.CSharp, false)] [InlineData(GenerationLanguage.Java, false)] @@ -54,7 +61,7 @@ public async Task GeneratesModelWithDictionary(GenerationLanguage language, bool OutputPath = $".\\Generated\\ModelWithDictionary\\{language}{backingStoreSuffix}", UsesBackingStore = backingStore, }; - await new KiotaBuilder(logger, configuration).GenerateClientAsync(new()); + await new KiotaBuilder(logger, configuration, _httpClient).GenerateClientAsync(new()); } [InlineData(GenerationLanguage.CSharp, false)] [InlineData(GenerationLanguage.Java, false)] @@ -79,6 +86,6 @@ public async Task GeneratesResponseWithMultipleReturnFormats(GenerationLanguage OutputPath = $".\\Generated\\ResponseWithMultipleReturnFormats\\{language}{backingStoreSuffix}", UsesBackingStore = backingStore, }; - await new KiotaBuilder(logger, configuration).GenerateClientAsync(new()); + await new KiotaBuilder(logger, configuration, _httpClient).GenerateClientAsync(new()); } } diff --git a/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs b/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs index 124f4f23a9..1c586a4340 100644 --- a/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs +++ b/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -21,8 +22,13 @@ using Xunit; namespace Kiota.Builder.Tests; -public class KiotaBuilderTests +public class KiotaBuilderTests : IDisposable { + public void Dispose() { + _httpClient.Dispose(); + GC.SuppressFinalize(this); + } + private readonly HttpClient _httpClient = new(); [Fact] public async Task ParsesEnumDescriptions() { var tempFilePath = Path.Combine(Path.GetTempPath(), Path.GetTempFileName()); @@ -71,7 +77,7 @@ await File.WriteAllTextAsync(tempFilePath, @"openapi: 3.0.1 - value: Standard_RAGRS - value: Premium_LRS"); var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePath }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePath }, _httpClient); await using var fs = new FileStream(tempFilePath, FileMode.Open); var document = builder.CreateOpenApiDocument(fs); var node = builder.CreateUriSpace(document); @@ -106,7 +112,7 @@ await File.WriteAllTextAsync(tempFilePath, @"openapi: 3.0.1 servers: - url: https://graph.microsoft.com/v1.0"); var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePath }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePath }, _httpClient); await using var fs = new FileStream(tempFilePath, FileMode.Open); var document = builder.CreateOpenApiDocument(fs); var node = builder.CreateUriSpace(document); @@ -132,7 +138,7 @@ await File.WriteAllTextAsync(tempFilePath, @"openapi: 3.0.1 servers: - url: https://graph.microsoft.com/v1.0"); var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePath }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePath }, _httpClient); await using var fs = new FileStream(tempFilePath, FileMode.Open); var document = builder.CreateOpenApiDocument(fs); var node = builder.CreateUriSpace(document); @@ -161,7 +167,7 @@ await File.WriteAllTextAsync(tempFilePath, @"openapi: 3.0.1 schema: type: string"); var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePath }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePath }, _httpClient); var treeNode = await builder.GetUrlTreeNodeAsync(new CancellationToken()); Assert.NotNull(treeNode); Assert.Equal("/", treeNode.Segment); @@ -174,7 +180,7 @@ public async Task DoesntThrowOnMissingServerForV2() { var tempFilePath = Path.Combine(Path.GetTempPath(), Path.GetTempFileName()); await File.WriteAllLinesAsync(tempFilePath, new[] {"swagger: 2.0", "title: \"Todo API\"", "version: \"1.0.0\"", "host: mytodos.doesntexit", "basePath: v2", "schemes:", " - https"," - http"}); var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePath }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePath }, _httpClient); await builder.GenerateClientAsync(new()); File.Delete(tempFilePath); } @@ -183,7 +189,7 @@ public void Single_root_node_creates_single_request_builder_class() { var node = OpenApiUrlTreeNode.Create(); var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var codeModel = builder.CreateSourceModel(node); Assert.Single(codeModel.GetChildElements(true)); @@ -221,7 +227,7 @@ public void Single_path_with_get_collection() } }, "default"); var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var codeModel = builder.CreateSourceModel(node); var rootNamespace = codeModel.GetChildElements(true).Single(); @@ -279,7 +285,7 @@ public void OData_doubles_as_one_of(){ } }, "default"); var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var codeModel = builder.CreateSourceModel(node); var progressProp = codeModel.FindChildByName("progress"); Assert.Equal("double", progressProp.Type.Name); @@ -330,7 +336,7 @@ public void OData_doubles_as_one_of_format_inside(){ } }, "default"); var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var codeModel = builder.CreateSourceModel(node); var progressProp = codeModel.FindChildByName("progress"); Assert.Equal("double", progressProp.Type.Name); @@ -381,7 +387,7 @@ public void OData_doubles_as_any_of(){ } }, "default"); var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var codeModel = builder.CreateSourceModel(node); var progressProp = codeModel.FindChildByName("progress"); Assert.Equal("double", progressProp.Type.Name); @@ -453,7 +459,7 @@ public void Object_Arrays_are_supported() { }; var node = OpenApiUrlTreeNode.Create(document, "default"); var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); builder.CreateUriSpace(document);//needed so the component index exists var codeModel = builder.CreateSourceModel(node); var userClass = codeModel.FindNamespaceByName("ApiSdk.models").FindChildByName("user"); @@ -491,7 +497,7 @@ public void TextPlainEndpointsAreSupported() { }; var node = OpenApiUrlTreeNode.Create(document, "default"); var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); builder.CreateUriSpace(document);//needed so the component index exists var codeModel = builder.CreateSourceModel(node); var requestBuilderClass = codeModel.FindChildByName("CountRequestBuilder"); @@ -606,7 +612,7 @@ public void Supports_Path_Parameters() }; var node = OpenApiUrlTreeNode.Create(document, "default"); var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); builder.CreateUriSpace(document);//needed so the component index exists var codeModel = builder.CreateSourceModel(node); var deviceManagementNS = codeModel.FindNamespaceByName("ApiSdk.deviceManagement"); @@ -757,7 +763,7 @@ public void Supports_Path_Query_And_Header_Parameters() }; var node = OpenApiUrlTreeNode.Create(document, "default"); var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost", Language = GenerationLanguage.Shell }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost", Language = GenerationLanguage.Shell }, _httpClient); builder.CreateUriSpace(document);//needed so the component index exists var codeModel = builder.CreateSourceModel(node); var deviceManagementNS = codeModel.FindNamespaceByName("ApiSdk.deviceManagement"); @@ -776,7 +782,7 @@ public void Supports_Path_Query_And_Header_Parameters() Assert.Single(constructorMethod.Parameters.Where(x => x.IsOfKind(CodeParameterKind.Path))); var parameters = getEffectivePermissionsRequestBuilder .Methods - .SingleOrDefault(cm => cm.IsOfKind(CodeMethodKind.RequestGenerator) && cm.HttpMethod == HttpMethod.Get)? + .SingleOrDefault(cm => cm.IsOfKind(CodeMethodKind.RequestGenerator) && cm.HttpMethod == Builder.CodeDOM.HttpMethod.Get)? .PathQueryAndHeaderParameters; Assert.Equal(4, parameters.Count()); Assert.NotNull(parameters.SingleOrDefault(p => p.Name == "IfMatch" && p.Kind == CodeParameterKind.Headers)); @@ -862,7 +868,7 @@ public void Inline_Property_Inheritance_Is_Supported() { }; var node = OpenApiUrlTreeNode.Create(document, "default"); var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); builder.CreateUriSpace(document);//needed so the component index exists builder.SetOpenApiDocument(document); var codeModel = builder.CreateSourceModel(node); @@ -916,7 +922,7 @@ public void MapsTime(){ } }, "default"); var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var codeModel = builder.CreateSourceModel(node); var progressProp = codeModel.FindChildByName("progress"); Assert.Equal("TimeOnly", progressProp.Type.Name); @@ -957,7 +963,7 @@ public void MapsDate(){ } }, "default"); var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var codeModel = builder.CreateSourceModel(node); var progressProp = codeModel.FindChildByName("progress"); Assert.Equal("DateOnly", progressProp.Type.Name); @@ -998,7 +1004,7 @@ public void MapsDuration(){ } }, "default"); var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var codeModel = builder.CreateSourceModel(node); var progressProp = codeModel.FindChildByName("progress"); Assert.Equal("TimeSpan", progressProp.Type.Name); @@ -1098,7 +1104,7 @@ public void AddsErrorMapping(){ } }, "default"); var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var codeModel = builder.CreateSourceModel(node); var executorMethod = codeModel.FindChildByName("get"); Assert.NotNull(executorMethod); @@ -1176,7 +1182,7 @@ public void IgnoresErrorCodesWithNoSchema(){ } }, "default"); var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var codeModel = builder.CreateSourceModel(node); var executorMethod = codeModel.FindChildByName("get"); Assert.NotNull(executorMethod); @@ -1269,7 +1275,7 @@ public void DoesntAddSuffixesToErrorTypesWhenComponents(){ }; var node = OpenApiUrlTreeNode.Create(document, "default"); var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); builder.SetOpenApiDocument(document); var codeModel = builder.CreateSourceModel(node); var executorMethod = codeModel.FindChildByName("get"); @@ -1361,7 +1367,7 @@ public void DoesntAddPropertyHolderOnNonAdditionalModels(){ }; var node = OpenApiUrlTreeNode.Create(document, "default"); var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); builder.SetOpenApiDocument(document); var codeModel = builder.CreateSourceModel(node); var weatherType = codeModel.FindChildByName("WeatherForecast"); @@ -1436,7 +1442,7 @@ public void SquishesLonelyNullables(){ }; var node = OpenApiUrlTreeNode.Create(document, "default"); var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); builder.SetOpenApiDocument(document); var codeModel = builder.CreateSourceModel(node); var responseClass = codeModel.FindChildByName("CreateUploadSessionResponse"); @@ -1519,7 +1525,7 @@ public void SquishesLonelyNullablesBothAnyOf(){ }; var node = OpenApiUrlTreeNode.Create(document, "default"); var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); builder.SetOpenApiDocument(document); var codeModel = builder.CreateSourceModel(node); var responseClass = codeModel.FindChildByName("CreateUploadSessionResponse"); @@ -1641,7 +1647,7 @@ public void AddsDiscriminatorMappings(){ }, }; var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var entityClass = codeModel.FindChildByName("entity"); @@ -1790,7 +1796,7 @@ public void DoesntAddDiscriminatorMappingsOfNonDerivedTypes(){ }, }; var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration() { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration() { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var entityClass = codeModel.FindChildByName("entity", true); @@ -1919,7 +1925,7 @@ public async Task AddsDiscriminatorMappingsOneOfImplicit(){ }; var mockLogger = new Mock>(); var config = new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }; - var builder = new KiotaBuilder(mockLogger.Object, config); + var builder = new KiotaBuilder(mockLogger.Object, config, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); await builder.ApplyLanguageRefinement(config, codeModel, CancellationToken.None); @@ -2074,7 +2080,7 @@ public async Task AddsDiscriminatorMappingsAllOfImplicit(){ }; var mockLogger = new Mock>(); var config = new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }; - var builder = new KiotaBuilder(mockLogger.Object, config); + var builder = new KiotaBuilder(mockLogger.Object, config, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); await builder.ApplyLanguageRefinement(config, codeModel, CancellationToken.None); @@ -2245,7 +2251,7 @@ public async Task AddsDiscriminatorMappingsAllOfImplicitWithParentHavingMappings }; var mockLogger = new Mock>(); var config = new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }; - var builder = new KiotaBuilder(mockLogger.Object, config); + var builder = new KiotaBuilder(mockLogger.Object, config, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); await builder.ApplyLanguageRefinement(config, codeModel, CancellationToken.None); @@ -2324,7 +2330,7 @@ public void UnionOfPrimitiveTypesWorks() { }, }; var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var requestBuilderNS = codeModel.FindNamespaceByName("ApiSdk.unionType"); @@ -2404,7 +2410,7 @@ public void UnionOfInlineSchemasWorks() { }, }; var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var requestBuilderNS = codeModel.FindNamespaceByName("ApiSdk.unionType"); @@ -2477,7 +2483,7 @@ public void IntersectionOfPrimitiveTypesWorks() { }, }; var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var requestBuilderNS = codeModel.FindNamespaceByName("ApiSdk.unionType"); @@ -2557,7 +2563,7 @@ public void IntersectionOfInlineSchemasWorks() { }, }; var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var requestBuilderNS = codeModel.FindNamespaceByName("ApiSdk.unionType"); @@ -2692,7 +2698,7 @@ public void InheritedTypeWithInlineSchemaWorks() { }, }; var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var requestBuilderNS = codeModel.FindNamespaceByName("ApiSdk.derivedType"); @@ -2768,7 +2774,7 @@ public void MapsPrimitiveFormats(string type, string format, string expected){ }, }; var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var requestBuilder = codeModel.FindChildByName("primitiveRequestBuilder"); @@ -2836,7 +2842,7 @@ public void MapsQueryParameterTypes(string type, string format, string expected) }, }; var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var queryParameters = codeModel.FindChildByName("primitiveRequestBuilderGetQueryParameters"); @@ -2884,7 +2890,7 @@ public void MapsQueryParameterCollectionKinds(bool isArray){ }, }; var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var queryParameters = codeModel.FindChildByName("primitiveRequestBuilderGetQueryParameters"); @@ -2922,7 +2928,7 @@ public void DefaultsQueryParametersWithNoSchemaToString(){ }, }; var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var queryParameters = codeModel.FindChildByName("primitiveRequestBuilderGetQueryParameters"); @@ -2981,7 +2987,7 @@ public void DoesntGenerateNamespacesWhenNotRequired(){ } }; var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var modelsNS = codeModel.FindNamespaceByName("TestSdk.Models"); @@ -3040,7 +3046,7 @@ public void GeneratesNamesapacesWhenRequired(){ } }; var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var modelsNS = codeModel.FindNamespaceByName("TestSdk.Models"); @@ -3099,7 +3105,7 @@ public void IdsResultInIndexers(){ } }; var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); Assert.Null(codeModel.FindChildByName("With")); @@ -3193,7 +3199,7 @@ public void HandlesCollectionOfEnumSchemasInAnyOfWithNullable(){ } }; var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var modelsNS = codeModel.FindNamespaceByName("TestSdk.Models"); @@ -3278,7 +3284,7 @@ public void HandlesCollectionOfEnumSchemas(){ } }; var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var modelsNS = codeModel.FindNamespaceByName("TestSdk.Models"); @@ -3345,7 +3351,7 @@ public void InlinePropertiesGenerateTypes(){ } }; var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var modelsNS = codeModel.FindNamespaceByName("TestSdk.Models"); @@ -3409,7 +3415,7 @@ public void ModelsDoesntUsePathDescriptionWhenAvailable(){ } }; var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var modelsNS = codeModel.FindNamespaceByName("TestSdk.Models"); @@ -3469,7 +3475,7 @@ public void CleansUpInvalidDescriptionCharacters(){ } }; var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var modelsNS = codeModel.FindNamespaceByName("TestSdk.Models"); @@ -3531,14 +3537,14 @@ public void AcceptVendorsTypes(string contentType){ } }; var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var rbNS = codeModel.FindNamespaceByName("TestSdk.Answer"); Assert.NotNull(rbNS); var rbClass = rbNS.Classes.FirstOrDefault(x => x.IsOfKind(CodeClassKind.RequestBuilder)); Assert.NotNull(rbClass); - var executorMethod = rbClass.Methods.FirstOrDefault(x => x.IsOfKind(CodeMethodKind.RequestExecutor) && x.HttpMethod == HttpMethod.Get); + var executorMethod = rbClass.Methods.FirstOrDefault(x => x.IsOfKind(CodeMethodKind.RequestExecutor) && x.HttpMethod == Builder.CodeDOM.HttpMethod.Get); Assert.NotNull(executorMethod); Assert.Equal("Myobject", executorMethod.ReturnType.Name); } @@ -3582,7 +3588,7 @@ public void ModelsUseDescriptionWhenAvailable(){ } }; var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var modelsSubNS = codeModel.FindNamespaceByName("TestSdk.answer"); @@ -3711,7 +3717,7 @@ public void GeneratesTheRightReturnTypeBasedOnContentAndStatus(string contentTyp StructuredMimeTypes = acceptedContentType.Equals("default", StringComparison.OrdinalIgnoreCase) ? new GenerationConfiguration().StructuredMimeTypes: new (StringComparer.OrdinalIgnoreCase) { acceptedContentType } - }); + }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var rbNS = codeModel.FindNamespaceByName("TestSdk.Answer"); @@ -3800,7 +3806,7 @@ public void Considers200WithSchemaOver2XXWithSchema() { ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost", StructuredMimeTypes = new GenerationConfiguration().StructuredMimeTypes - }); + }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var rbNS = codeModel.FindNamespaceByName("TestSdk.Answer"); @@ -3869,7 +3875,7 @@ public void Considers2XXWithSchemaOver204WithNoSchema() { ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost", StructuredMimeTypes = new GenerationConfiguration().StructuredMimeTypes - }); + }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var rbNS = codeModel.FindNamespaceByName("TestSdk.Answer"); @@ -3932,7 +3938,7 @@ public void Considers204WithNoSchemaOver206WithNoSchema() { ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost", StructuredMimeTypes = new GenerationConfiguration().StructuredMimeTypes - }); + }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var rbNS = codeModel.FindNamespaceByName("TestSdk.Answer"); @@ -4023,7 +4029,7 @@ public void GeneratesTheRightParameterTypeBasedOnContentAndStatus(string content StructuredMimeTypes = acceptedContentType.Equals("default", StringComparison.OrdinalIgnoreCase) ? new GenerationConfiguration().StructuredMimeTypes: new (StringComparer.OrdinalIgnoreCase) { acceptedContentType } - }); + }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var rbNS = codeModel.FindNamespaceByName("TestSdk.Answer"); @@ -4084,7 +4090,7 @@ public void DoesntGenerateVoidExecutorOnMixed204(){ } }; var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var rbNS = codeModel.FindNamespaceByName("TestSdk.Answer"); @@ -4218,7 +4224,7 @@ public void HandlesContentParameters(){ } }; var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var answersNS = codeModel.FindNamespaceByName("TestSdk.answerWithIds"); @@ -4285,14 +4291,14 @@ public void HandlesPagingExtension(){ } }; var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var answersNS = codeModel.FindNamespaceByName("TestSdk.users"); Assert.NotNull(answersNS); var rbClass = answersNS.Classes.FirstOrDefault(x => x.IsOfKind(CodeClassKind.RequestBuilder)); Assert.NotNull(rbClass); - var executorMethod = rbClass.Methods.FirstOrDefault(x => x.IsOfKind(CodeMethodKind.RequestExecutor) && x.HttpMethod == HttpMethod.Get); + var executorMethod = rbClass.Methods.FirstOrDefault(x => x.IsOfKind(CodeMethodKind.RequestExecutor) && x.HttpMethod == Builder.CodeDOM.HttpMethod.Get); Assert.NotNull(executorMethod); Assert.Equal("@odata.nextLink", executorMethod.PagingInformation?.NextLinkName); } @@ -4348,7 +4354,7 @@ public void SetsReadonlyProperties(bool isReadonly){ } }; var mockLogger = new Mock>(); - var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost" }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var objectClass = codeModel.FindChildByName("myobject"); @@ -4430,7 +4436,7 @@ public void SupportsIncludeFilter(){ IncludePatterns = new() { "*users*" } - }); + }, _httpClient); var filters = builder.BuildGlobPatterns(); builder.FilterPathsByPatterns(document, filters.Item1, filters.Item2); var node = builder.CreateUriSpace(document); @@ -4511,7 +4517,7 @@ public void SupportsExcludeFilter(){ ExcludePatterns = new() { "*groups*" } - }); + }, _httpClient); var filters = builder.BuildGlobPatterns(); builder.FilterPathsByPatterns(document, filters.Item1, filters.Item2); var node = builder.CreateUriSpace(document); @@ -4581,7 +4587,7 @@ public void SupportsIndexingParametersInSubPaths(){ ClientClassName = "TestClient", ClientNamespaceName = "TestSdk", ApiRootUrl = "https://localhost", - }); + }, _httpClient); var node = builder.CreateUriSpace(document); var codeModel = builder.CreateSourceModel(node); var NS = codeModel.FindNamespaceByName("TestSdk.usersWithUserId"); diff --git a/tests/Kiota.Builder.Tests/KiotaSearcherTests.cs b/tests/Kiota.Builder.Tests/KiotaSearcherTests.cs index 854eb238cf..4cd7049c81 100644 --- a/tests/Kiota.Builder.Tests/KiotaSearcherTests.cs +++ b/tests/Kiota.Builder.Tests/KiotaSearcherTests.cs @@ -10,19 +10,20 @@ using Xunit; namespace Kiota.Builder.Tests; -public class KiotaSearcherTests { +public class KiotaSearcherTests : IDisposable { + private readonly HttpClient httpClient = new(); [Fact] public void DefensivePrograming() { - Assert.Throws(() => new KiotaSearcher(null, null)); - Assert.Throws(() => new KiotaSearcher(new Mock>().Object, null)); - Assert.Throws(() => new KiotaSearcher(null, new SearchConfiguration())); - Assert.ThrowsAsync(() => new GitHubSearchProvider(new HttpClient(), new Uri("https://httpbin.org/headers"), new Mock>().Object, false).SearchAsync(null, null, CancellationToken.None)); + Assert.Throws(() => new KiotaSearcher(null, new SearchConfiguration(), httpClient)); + Assert.Throws(() => new KiotaSearcher(new Mock>().Object, null, httpClient)); + Assert.Throws(() => new KiotaSearcher(new Mock>().Object, new SearchConfiguration(), null)); + Assert.ThrowsAsync(() => new GitHubSearchProvider(httpClient, new Uri("https://httpbin.org/headers"), new Mock>().Object, false).SearchAsync(null, null, CancellationToken.None)); } [Fact] public async Task GetsMicrosoftGraphBothVersions() { var searcher = new KiotaSearcher(new Mock>().Object, new SearchConfiguration(){ SearchTerm = "github::microsoftgraph/msgraph-metadata", - }); + }, httpClient); var results = await searcher.SearchAsync(new CancellationToken()); Assert.Equal(2, results.Count); } @@ -30,7 +31,7 @@ public async Task GetsMicrosoftGraphBothVersions() { public async Task GetsMicrosoftGraph() { var searcher = new KiotaSearcher(new Mock>().Object, new SearchConfiguration(){ SearchTerm = "github::microsoftgraph/msgraph-metadata/graph.microsoft.com/v1.0", - }); + }, httpClient); var results = await searcher.SearchAsync(new CancellationToken()); Assert.Single(results); Assert.Equal("https://raw.githubusercontent.com/microsoftgraph/msgraph-metadata/master/openapi/v1.0/openapi.yaml", results.First().Value.DescriptionUrl.ToString()); @@ -39,14 +40,14 @@ public async Task GetsMicrosoftGraph() { public async Task GetsMicrosoftGraphBeta() { var searcher = new KiotaSearcher(new Mock>().Object, new SearchConfiguration(){ SearchTerm = "github::microsoftgraph/msgraph-metadata/graph.microsoft.com/beta", - }); + }, httpClient); var results = await searcher.SearchAsync(new CancellationToken()); Assert.Single(results); Assert.Equal("https://raw.githubusercontent.com/microsoftgraph/msgraph-metadata/master/openapi/beta/openapi.yaml", results.First().Value.DescriptionUrl.ToString()); } [Fact] public async Task DoesntFailOnEmptyTerm() { - var searcher = new KiotaSearcher(new Mock>().Object, new SearchConfiguration()); + var searcher = new KiotaSearcher(new Mock>().Object, new SearchConfiguration(), httpClient); var results = await searcher.SearchAsync(new CancellationToken()); Assert.Empty(results); } @@ -54,7 +55,7 @@ public async Task DoesntFailOnEmptyTerm() { public async Task GetsGithubFromApisGuru() { var searcher = new KiotaSearcher(new Mock>().Object, new SearchConfiguration(){ SearchTerm = "github", - }); + }, httpClient); var results = await searcher.SearchAsync(new CancellationToken()); Assert.NotEmpty(results); } @@ -62,8 +63,13 @@ public async Task GetsGithubFromApisGuru() { public async Task GetsGithubFromApisGuruWithExactMatch() { var searcher = new KiotaSearcher(new Mock>().Object, new SearchConfiguration(){ SearchTerm = "apisguru::github.com:api.github.com", - }); + }, httpClient); var results = await searcher.SearchAsync(new CancellationToken()); Assert.Single(results); } + public void Dispose() + { + httpClient.Dispose(); + GC.SuppressFinalize(this); + } } From d3206a37c8a6b824bc8cae16887eab52e4341aa3 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 22 Nov 2022 15:13:15 -0500 Subject: [PATCH 02/42] - implements login command and search with private repos Signed-off-by: Vincent Biret --- .vscode/launch.json | 13 +++ .../Configuration/SearchConfiguration.cs | 11 ++- src/Kiota.Builder/Extensions/UriExtensions.cs | 11 +++ src/Kiota.Builder/KiotaBuilder.cs | 5 +- src/Kiota.Builder/KiotaSearcher.cs | 2 +- .../GitHubAuthenticationProvider.cs | 24 +++-- .../TempFolderCachingAccessTokenProvider.cs | 89 +++++++++++++++++++ .../GitHub/GitHubSearchProvider.cs | 36 ++++++-- src/Kiota.Web/wwwroot/appsettings.json | 8 +- src/kiota/Handlers/BaseKiotaCommandHandler.cs | 4 + .../KiotaGitHubLoginCommandHanlder.cs | 9 +- src/kiota/appsettings.json | 6 +- .../Kiota.Builder.Tests/KiotaSearcherTests.cs | 42 +++++---- 13 files changed, 214 insertions(+), 46 deletions(-) create mode 100644 src/Kiota.Builder/Extensions/UriExtensions.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/Autentication/TempFolderCachingAccessTokenProvider.cs diff --git a/.vscode/launch.json b/.vscode/launch.json index 7bdcdce0ab..35468a2ca6 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -256,6 +256,19 @@ "console": "internalConsole", "stopAtEntry": false }, + { + "name": "Launch Login (github)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + "program": "${workspaceFolder}/src/kiota/bin/Debug/net7.0/kiota.dll", + "args": ["login", + "github" + ], + "cwd": "${workspaceFolder}/src/kiota", + "console": "internalConsole", + "stopAtEntry": false + }, { "name": ".NET Core Attach", "type": "coreclr", diff --git a/src/Kiota.Builder/Configuration/SearchConfiguration.cs b/src/Kiota.Builder/Configuration/SearchConfiguration.cs index 7b2588b766..a9ace1f002 100644 --- a/src/Kiota.Builder/Configuration/SearchConfiguration.cs +++ b/src/Kiota.Builder/Configuration/SearchConfiguration.cs @@ -1,9 +1,16 @@ using System; +using System.Text.Json.Serialization; namespace Kiota.Builder.Configuration; public class SearchConfiguration : SearchConfigurationBase { public Uri APIsGuruListUrl { get; set; } = new ("https://raw.githubusercontent.com/APIs-guru/openapi-directory/gh-pages/v2/list.json"); - public Uri GitHubBlockListUrl { get; set; } = new ("https://raw.githubusercontent.com/microsoft/kiota/main/resources/index-block-list.yml"); - public string GitHubAppId { get; set; } = "0adc165b71f9824f4282"; + public GitHubConfiguration GitHub { get; set; } = new(); +} + +public class GitHubConfiguration { + public string AppId { get; set; } = "0adc165b71f9824f4282"; + public Uri BlockListUrl { get; set; } = new ("https://raw.githubusercontent.com/microsoft/kiota/main/resources/index-block-list.yml"); + [JsonIgnore] + public Action DeviceCodeCallback { get; set; } } diff --git a/src/Kiota.Builder/Extensions/UriExtensions.cs b/src/Kiota.Builder/Extensions/UriExtensions.cs new file mode 100644 index 0000000000..659617bd36 --- /dev/null +++ b/src/Kiota.Builder/Extensions/UriExtensions.cs @@ -0,0 +1,11 @@ +using System; +using System.IO; + +namespace Kiota.Builder.Extensions; + +public static class UriExtensions { + public static string GetFileName(this Uri uri) { + if(uri is null) return string.Empty; + return Path.GetFileName($"{uri.Scheme}://{uri.Host}{uri.AbsolutePath}"); + } +} diff --git a/src/Kiota.Builder/KiotaBuilder.cs b/src/Kiota.Builder/KiotaBuilder.cs index 146829ae5e..af1a0d9ae0 100644 --- a/src/Kiota.Builder/KiotaBuilder.cs +++ b/src/Kiota.Builder/KiotaBuilder.cs @@ -230,8 +230,9 @@ private async Task LoadStream(string inputPath, CancellationToken cancel var cachingProvider = new DocumentCachingProvider(httpClient, logger) { ClearCache = config.ClearCache, }; - var fileName = Path.GetFileName(inputPath) is string name && !string.IsNullOrEmpty(name) ? name : "description.yml"; - input = await cachingProvider.GetDocumentAsync(new Uri(inputPath), "generation", fileName, cancellationToken: cancellationToken); + var targetUri = new Uri(inputPath); + var fileName = targetUri.GetFileName() is string name && !string.IsNullOrEmpty(name) ? name : "description.yml"; + input = await cachingProvider.GetDocumentAsync(targetUri, "generation", fileName, cancellationToken: cancellationToken); } catch (HttpRequestException ex) { throw new InvalidOperationException($"Could not download the file at {inputPath}, reason: {ex.Message}", ex); } diff --git a/src/Kiota.Builder/KiotaSearcher.cs b/src/Kiota.Builder/KiotaSearcher.cs index 2fab02a774..82f2ef4764 100644 --- a/src/Kiota.Builder/KiotaSearcher.cs +++ b/src/Kiota.Builder/KiotaSearcher.cs @@ -34,7 +34,7 @@ public async Task> SearchAsync(CancellationTok logger.LogDebug("searching for {searchTerm}", config.SearchTerm); logger.LogDebug("searching APIs.guru with url {url}", config.APIsGuruListUrl); var oasProvider = new OpenApiSpecSearchProvider(); - var githubProvider = new GitHubSearchProvider(httpClient, config.GitHubBlockListUrl, logger, config.ClearCache); + var githubProvider = new GitHubSearchProvider(httpClient, logger, config.ClearCache, config.GitHub); var results = await Task.WhenAll( SearchProviderAsync(apiGurusSearchProvider, cancellationToken), SearchProviderAsync(oasProvider, cancellationToken), diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAuthenticationProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAuthenticationProvider.cs index 6a998e6067..b376f92333 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAuthenticationProvider.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAuthenticationProvider.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using Microsoft.Kiota.Abstractions; using Microsoft.Kiota.Abstractions.Authentication; @@ -10,7 +12,7 @@ namespace Kiota.Builder.SearchProviders.GitHub.Authentication.DeviceCode; public class GitHubAuthenticationProvider : GitHubAnonymousAuthenticationProvider { - public GitHubAuthenticationProvider(string clientId, string scope, IEnumerable validHosts, HttpClient httpClient, Action messageCallback) + public GitHubAuthenticationProvider(string clientId, string scope, IEnumerable validHosts, HttpClient httpClient, Action messageCallback, ILogger logger) { if (string.IsNullOrEmpty(clientId)) throw new ArgumentNullException(nameof(clientId)); @@ -19,14 +21,20 @@ public GitHubAuthenticationProvider(string clientId, string scope, IEnumerable Concrete.AllowedHostsValidator; + public async Task GetAuthorizationTokenAsync(Uri uri, Dictionary additionalAuthenticationContext = null, CancellationToken cancellationToken = default) + { + var result = await GetTokenFromCacheAsync(uri, cancellationToken).ConfigureAwait(false); + if (string.IsNullOrEmpty(result)) + { + Logger.LogInformation("Token not found in cache, requesting a new one"); + result = await Concrete.GetAuthorizationTokenAsync(uri, additionalAuthenticationContext, cancellationToken).ConfigureAwait(false); + await CacheTokenAsync(uri, result, cancellationToken).ConfigureAwait(false); + } + return result; + } + private string GetTokenCacheFilePath() => GetTokenCacheFilePath(ApiBaseUrl); + private string GetTokenCacheFilePath(Uri uri) => Path.Combine(Path.GetTempPath(), "kiota", "auth", $"{AppId}-{uri.Host}.txt"); + private async Task CacheTokenAsync(Uri uri, string result, CancellationToken cancellationToken) + { + try + { + var target = GetTokenCacheFilePath(uri); + var directory = Path.GetDirectoryName(target); + if (!Directory.Exists(directory)) + Directory.CreateDirectory(directory); + await File.WriteAllTextAsync(target, result, cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.LogWarning(ex, "Error while writing token to cache."); + } + } + private async Task GetTokenFromCacheAsync(Uri uri, CancellationToken cancellationToken) + { + try { + var target = GetTokenCacheFilePath(uri); + if(!IsCachedTokenPresent()) + return null; + var result = await File.ReadAllTextAsync(target, cancellationToken).ConfigureAwait(false); + return result; + } catch (Exception ex) { + Logger.LogWarning(ex, "Error while reading token from cache."); + return null; + } + } + public void Logout() { + try { + var target = GetTokenCacheFilePath(); + File.Delete(target); + } catch (Exception ex) { + Logger.LogWarning(ex, "Error while deleting token from cache."); + } + } + public bool IsCachedTokenPresent() { + try { + var target = GetTokenCacheFilePath(); + if (!File.Exists(target)) + return false; + var fileDate = File.GetLastWriteTime(target); + if(fileDate > DateTime.Now.AddMonths(6)) { + Logout(); + return false; + } + return true; + } catch (Exception ex) { + Logger.LogWarning(ex, "Error while reading token from cache."); + return false; + } + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs index 90f544a089..05e835bfc0 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs @@ -7,7 +7,10 @@ using System.Threading; using System.Threading.Tasks; using Kiota.Builder.Caching; +using Kiota.Builder.Configuration; +using Kiota.Builder.Extensions; using Kiota.Builder.SearchProviders.GitHub.Authentication; +using Kiota.Builder.SearchProviders.GitHub.Authentication.DeviceCode; using Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models; using Kiota.Builder.SearchProviders.GitHub.Index; using Microsoft.Extensions.Logging; @@ -22,19 +25,28 @@ public class GitHubSearchProvider : ISearchProvider private readonly DocumentCachingProvider cachingProvider; private readonly ILogger _logger; private readonly Uri _blockListUrl; - - public GitHubSearchProvider(HttpClient httpClient, Uri blockListUrl, ILogger logger, bool clearCache) + private readonly string _clientId; + private readonly Action _messageCallBack; + private const string ValidHost = "api.github.com"; + private const string Scope = "repo"; + public GitHubSearchProvider(HttpClient httpClient, ILogger logger, bool clearCache, GitHubConfiguration configuration) { ArgumentNullException.ThrowIfNull(httpClient); - ArgumentNullException.ThrowIfNull(blockListUrl); + ArgumentNullException.ThrowIfNull(configuration); + ArgumentNullException.ThrowIfNull(configuration.BlockListUrl); + ArgumentNullException.ThrowIfNull(configuration.DeviceCodeCallback); ArgumentNullException.ThrowIfNull(logger); + if(string.IsNullOrEmpty(configuration.AppId)) + throw new ArgumentOutOfRangeException(nameof(configuration)); cachingProvider = new DocumentCachingProvider(httpClient, logger) { ClearCache = clearCache, }; _httpClient = httpClient; _logger = logger; - _blockListUrl = blockListUrl; + _blockListUrl = configuration.BlockListUrl; + _clientId = configuration.AppId; + _messageCallBack = configuration.DeviceCodeCallback; } private readonly HttpClient _httpClient; public string ProviderKey => "github"; @@ -60,7 +72,16 @@ private static bool BlockListContainsRepo(Tuple, HashSet private async Task> SearchAsyncInternal(string term, CancellationToken cancellationToken) { var blockLists = await GetBlockLists(cancellationToken); - var gitHubRequestAdapter = new HttpClientRequestAdapter(new GitHubAnonymousAuthenticationProvider(), httpClient: _httpClient); + var cachingProvider = new TempFolderCachingAccessTokenProvider { + Logger = _logger, + ApiBaseUrl = new Uri($"https://{ValidHost}"), + Concrete = null, + AppId = _clientId, + }; + var authenticationProvider = cachingProvider.IsCachedTokenPresent() ? + new GitHubAuthenticationProvider(_clientId, Scope, new List { ValidHost }, _httpClient, _messageCallBack, _logger) : + new GitHubAnonymousAuthenticationProvider(); + var gitHubRequestAdapter = new HttpClientRequestAdapter(authenticationProvider, httpClient: _httpClient); var gitHubClient = new GitHubClient.GitHubClient(gitHubRequestAdapter); if(term.Contains('/')) { var parts = term.Split('/', StringSplitOptions.RemoveEmptyEntries); @@ -93,7 +114,7 @@ private static Dictionary GetDictionaryResultFromMultipleS .ToDictionary(static x => x.Item1, static x => x.Item2, StringComparer.OrdinalIgnoreCase); private async Task, HashSet>> GetBlockLists(CancellationToken cancellationToken) { try { - await using var document = await cachingProvider.GetDocumentAsync(_blockListUrl, "search", Path.GetFileName(_blockListUrl.ToString()), "text/yaml", cancellationToken); + await using var document = await cachingProvider.GetDocumentAsync(_blockListUrl, "search", _blockListUrl.GetFileName(), "text/yaml", cancellationToken); var deserialized = deserializeDocumentFromYaml(document); return new Tuple, HashSet>( new HashSet(deserialized.Organizations.Distinct(StringComparer.OrdinalIgnoreCase), StringComparer.OrdinalIgnoreCase), @@ -125,7 +146,8 @@ private async Task>> GetSearchResultsFro var response = await gitHubClient.Repos[org][repo].Contents[fileName].GetAsync(cancellationToken: cancellationToken).ConfigureAwait(false); if (!response.AdditionalData.TryGetValue(DownloadUrlKey, out var rawDownloadUrl) || rawDownloadUrl is not string downloadUrl || string.IsNullOrEmpty(downloadUrl)) return Enumerable.Empty>(); - await using var document = await cachingProvider.GetDocumentAsync(new Uri(downloadUrl), "search", Path.GetFileName(downloadUrl), accept, cancellationToken); + var targetUrl = new Uri(downloadUrl); + await using var document = await cachingProvider.GetDocumentAsync(targetUrl, "search", targetUrl.GetFileName(), accept, cancellationToken); var indexFile = accept.ToLowerInvariant() switch { "application/json" => deserializeDocumentFromJson(document), diff --git a/src/Kiota.Web/wwwroot/appsettings.json b/src/Kiota.Web/wwwroot/appsettings.json index f495cfbc59..7bf609a026 100644 --- a/src/Kiota.Web/wwwroot/appsettings.json +++ b/src/Kiota.Web/wwwroot/appsettings.json @@ -6,9 +6,11 @@ ] }, "Search": { - "APIsGuruListUrl": "https://raw.githubusercontent.com/APIs-guru/openapi-directory/gh-pages/v2/list.json", - "GitHubBlockListUrl": "https://raw.githubusercontent.com/microsoft/kiota/main/resources/index-block-list.yml", - "GitHubAppId": "0adc165b71f9824f4282" + "APIsGuruListUrl": "https://raw.githubusercontent.com/APIs-guru/openapi-directory/gh-pages/v2/list.json", + "GitHub": { + "BlockListUrl": "https://raw.githubusercontent.com/microsoft/kiota/main/resources/index-block-list.yml", + "AppId": "0adc165b71f9824f4282" + } }, "Logging": { "LogLevel": { diff --git a/src/kiota/Handlers/BaseKiotaCommandHandler.cs b/src/kiota/Handlers/BaseKiotaCommandHandler.cs index 88bb5dd273..2603bc7ed0 100644 --- a/src/kiota/Handlers/BaseKiotaCommandHandler.cs +++ b/src/kiota/Handlers/BaseKiotaCommandHandler.cs @@ -25,6 +25,7 @@ internal abstract class BaseKiotaCommandHandler : ICommandHandler .Build(); var configObject = new KiotaConfiguration(); configuration.Bind(configObject); + configObject.Search.GitHub.DeviceCodeCallback = DisplayGitHubDeviceCodeLoginMessage; return configObject; }); public int Invoke(InvocationContext context) @@ -166,4 +167,7 @@ protected void DisplayInfoAdvancedHint() { "Example: kiota info -l "); } } + protected static void DisplayGitHubDeviceCodeLoginMessage(Uri uri, string code) { + DisplayInfo($"Please go to {uri} and enter the code {code} to authenticate."); + } } diff --git a/src/kiota/Handlers/KiotaGitHubLoginCommandHanlder.cs b/src/kiota/Handlers/KiotaGitHubLoginCommandHanlder.cs index 5a70953ba1..382c2b6d2d 100644 --- a/src/kiota/Handlers/KiotaGitHubLoginCommandHanlder.cs +++ b/src/kiota/Handlers/KiotaGitHubLoginCommandHanlder.cs @@ -17,7 +17,7 @@ public override async Task InvokeAsync(InvocationContext context) var (loggerFactory, logger) = GetLoggerAndFactory(context); using (loggerFactory) { try { - return await LoginAsync(cancellationToken); + return await LoginAsync(logger, cancellationToken); } catch (Exception ex) { #if DEBUG logger.LogCritical(ex, "error downloading a description: {exceptionMessage}", ex.Message); @@ -30,13 +30,14 @@ public override async Task InvokeAsync(InvocationContext context) } } - private async Task LoginAsync(CancellationToken cancellationToken) { + private async Task LoginAsync(ILogger logger, CancellationToken cancellationToken) { //TODO handle caching - var authenticationProvider = new GitHubAuthenticationProvider(Configuration.Search.GitHubAppId, + var authenticationProvider = new GitHubAuthenticationProvider(Configuration.Search.GitHub.AppId, "repo", new List { "api.github.com"}, httpClient, - (uri, code) => DisplayInfo($"Please go to {uri} and enter the code {code} to authenticate.")); + DisplayGitHubDeviceCodeLoginMessage, + logger); var dummyRequest = new RequestInformation() { HttpMethod = Method.GET, URI = new Uri("https://api.github.com/user"), diff --git a/src/kiota/appsettings.json b/src/kiota/appsettings.json index 4e2c5be2cc..7bf609a026 100644 --- a/src/kiota/appsettings.json +++ b/src/kiota/appsettings.json @@ -7,8 +7,10 @@ }, "Search": { "APIsGuruListUrl": "https://raw.githubusercontent.com/APIs-guru/openapi-directory/gh-pages/v2/list.json", - "GitHubBlockListUrl": "https://raw.githubusercontent.com/microsoft/kiota/main/resources/index-block-list.yml", - "GitHubAppId": "0adc165b71f9824f4282" + "GitHub": { + "BlockListUrl": "https://raw.githubusercontent.com/microsoft/kiota/main/resources/index-block-list.yml", + "AppId": "0adc165b71f9824f4282" + } }, "Logging": { "LogLevel": { diff --git a/tests/Kiota.Builder.Tests/KiotaSearcherTests.cs b/tests/Kiota.Builder.Tests/KiotaSearcherTests.cs index 4cd7049c81..11912b3824 100644 --- a/tests/Kiota.Builder.Tests/KiotaSearcherTests.cs +++ b/tests/Kiota.Builder.Tests/KiotaSearcherTests.cs @@ -17,53 +17,61 @@ public void DefensivePrograming() { Assert.Throws(() => new KiotaSearcher(null, new SearchConfiguration(), httpClient)); Assert.Throws(() => new KiotaSearcher(new Mock>().Object, null, httpClient)); Assert.Throws(() => new KiotaSearcher(new Mock>().Object, new SearchConfiguration(), null)); - Assert.ThrowsAsync(() => new GitHubSearchProvider(httpClient, new Uri("https://httpbin.org/headers"), new Mock>().Object, false).SearchAsync(null, null, CancellationToken.None)); + Assert.Throws(() => new GitHubSearchProvider(httpClient, new Mock>().Object, false, null)); + Assert.Throws(() => new GitHubSearchProvider(httpClient, null, false, new GitHubConfiguration())); + Assert.Throws(() => new GitHubSearchProvider(null, new Mock>().Object, false, new GitHubConfiguration())); + Assert.ThrowsAsync(() => new GitHubSearchProvider(httpClient, new Mock>().Object, false, new GitHubConfiguration()).SearchAsync(null, null, CancellationToken.None)); } + private static SearchConfiguration searchConfigurationFactory => new(){ + GitHub = new() { + DeviceCodeCallback = (code, uri) => {}, + } + }; [Fact] public async Task GetsMicrosoftGraphBothVersions() { - var searcher = new KiotaSearcher(new Mock>().Object, new SearchConfiguration(){ - SearchTerm = "github::microsoftgraph/msgraph-metadata", - }, httpClient); + var searchConfiguration = searchConfigurationFactory; + searchConfiguration.SearchTerm = "github::microsoftgraph/msgraph-metadata"; + var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient); var results = await searcher.SearchAsync(new CancellationToken()); Assert.Equal(2, results.Count); } [Fact] public async Task GetsMicrosoftGraph() { - var searcher = new KiotaSearcher(new Mock>().Object, new SearchConfiguration(){ - SearchTerm = "github::microsoftgraph/msgraph-metadata/graph.microsoft.com/v1.0", - }, httpClient); + var searchConfiguration = searchConfigurationFactory; + searchConfiguration.SearchTerm = "github::microsoftgraph/msgraph-metadata/graph.microsoft.com/v1.0"; + var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient); var results = await searcher.SearchAsync(new CancellationToken()); Assert.Single(results); Assert.Equal("https://raw.githubusercontent.com/microsoftgraph/msgraph-metadata/master/openapi/v1.0/openapi.yaml", results.First().Value.DescriptionUrl.ToString()); } [Fact] public async Task GetsMicrosoftGraphBeta() { - var searcher = new KiotaSearcher(new Mock>().Object, new SearchConfiguration(){ - SearchTerm = "github::microsoftgraph/msgraph-metadata/graph.microsoft.com/beta", - }, httpClient); + var searchConfiguration = searchConfigurationFactory; + searchConfiguration.SearchTerm = "github::microsoftgraph/msgraph-metadata/graph.microsoft.com/beta"; + var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient); var results = await searcher.SearchAsync(new CancellationToken()); Assert.Single(results); Assert.Equal("https://raw.githubusercontent.com/microsoftgraph/msgraph-metadata/master/openapi/beta/openapi.yaml", results.First().Value.DescriptionUrl.ToString()); } [Fact] public async Task DoesntFailOnEmptyTerm() { - var searcher = new KiotaSearcher(new Mock>().Object, new SearchConfiguration(), httpClient); + var searcher = new KiotaSearcher(new Mock>().Object, searchConfigurationFactory, httpClient); var results = await searcher.SearchAsync(new CancellationToken()); Assert.Empty(results); } [Fact] public async Task GetsGithubFromApisGuru() { - var searcher = new KiotaSearcher(new Mock>().Object, new SearchConfiguration(){ - SearchTerm = "github", - }, httpClient); + var searchConfiguration = searchConfigurationFactory; + searchConfiguration.SearchTerm = "github"; + var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient); var results = await searcher.SearchAsync(new CancellationToken()); Assert.NotEmpty(results); } [Fact] public async Task GetsGithubFromApisGuruWithExactMatch() { - var searcher = new KiotaSearcher(new Mock>().Object, new SearchConfiguration(){ - SearchTerm = "apisguru::github.com:api.github.com", - }, httpClient); + var searchConfiguration = searchConfigurationFactory; + searchConfiguration.SearchTerm = "apisguru::github.com:api.github.com"; + var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient); var results = await searcher.SearchAsync(new CancellationToken()); Assert.Single(results); } From 47f7d9c8fadb1b6653cefbb82145850cd9bf27d6 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 22 Nov 2022 15:39:33 -0500 Subject: [PATCH 03/42] - adds a logout command - adds sign-in hints Signed-off-by: Vincent Biret --- .../TempFolderCachingAccessTokenProvider.cs | 2 +- src/kiota/Handlers/BaseKiotaCommandHandler.cs | 88 +++++++++++-------- .../KiotaGitHubLoginCommandHanlder.cs | 9 +- .../KiotaGitHubLogoutCommandhandler.cs | 32 +++++++ .../Handlers/KiotaSearchCommandHandler.cs | 5 +- src/kiota/KiotaHost.cs | 3 + 6 files changed, 93 insertions(+), 46 deletions(-) create mode 100644 src/kiota/Handlers/KiotaGitHubLogoutCommandhandler.cs diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/TempFolderCachingAccessTokenProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/TempFolderCachingAccessTokenProvider.cs index 51daf0744e..59485df644 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/TempFolderCachingAccessTokenProvider.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/TempFolderCachingAccessTokenProvider.cs @@ -8,7 +8,7 @@ namespace Kiota.Builder.SearchProviders.GitHub.Authentication; -internal class TempFolderCachingAccessTokenProvider : IAccessTokenProvider +public class TempFolderCachingAccessTokenProvider : IAccessTokenProvider { public required IAccessTokenProvider Concrete { diff --git a/src/kiota/Handlers/BaseKiotaCommandHandler.cs b/src/kiota/Handlers/BaseKiotaCommandHandler.cs index 2603bc7ed0..a2cc20bf61 100644 --- a/src/kiota/Handlers/BaseKiotaCommandHandler.cs +++ b/src/kiota/Handlers/BaseKiotaCommandHandler.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Kiota.Builder; using Kiota.Builder.Configuration; +using Kiota.Builder.SearchProviders.GitHub.Authentication; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; @@ -15,6 +16,12 @@ namespace kiota.Handlers; internal abstract class BaseKiotaCommandHandler : ICommandHandler { + protected TempFolderCachingAccessTokenProvider GitHubAuthenticationCachingProvider(ILogger logger) => new(){ + Logger = logger, + ApiBaseUrl = new Uri("https://api.github.com"), + Concrete = null, + AppId = Configuration.Search.GitHub.AppId, + }; internal static readonly HttpClient httpClient = new(); public Option LogLevelOption { get;set; } protected KiotaConfiguration Configuration { get => ConfigurationFactory.Value; } @@ -74,9 +81,11 @@ protected static string NormalizeSlashesInPath(string path) { return true; }); protected bool TutorialMode => tutorialMode.Value; - private static void DisplayHint(params string[] messages) { - Console.WriteLine(); - DisplayMessages(ConsoleColor.Blue, messages); + private void DisplayHint(params string[] messages) { + if(TutorialMode) { + Console.WriteLine(); + DisplayMessages(ConsoleColor.Blue, messages); + } } private static void DisplayMessages(ConsoleColor color, params string[] messages) { Console.ForegroundColor = color; @@ -97,25 +106,21 @@ protected static void DisplayInfo(params string[] messages) { DisplayMessages(ConsoleColor.White, messages); } protected void DisplayDownloadHint(string searchTerm, string version) { - if(TutorialMode) { - var example = string.IsNullOrEmpty(version) ? - $"Example: kiota download {searchTerm} -o " : - $"Example: kiota download {searchTerm} -v {version} -o "; - DisplayHint("Hint: use kiota download to download the OpenAPI description.", example); - } + var example = string.IsNullOrEmpty(version) ? + $"Example: kiota download {searchTerm} -o " : + $"Example: kiota download {searchTerm} -v {version} -o "; + DisplayHint("Hint: use kiota download to download the OpenAPI description.", example); } protected void DisplayShowHint(string searchTerm, string version, string path = null) { - if(TutorialMode) { - var example = path switch { - _ when !string.IsNullOrEmpty(path) => $"Example: kiota show -d {path}", - _ when string.IsNullOrEmpty(version) => $"Example: kiota show -k {searchTerm}", - _ => $"Example: kiota show -k {searchTerm} -v {version}", - }; - DisplayHint("Hint: use kiota show to display a tree of paths present in the OpenAPI description.", example); - } + var example = path switch { + _ when !string.IsNullOrEmpty(path) => $"Example: kiota show -d {path}", + _ when string.IsNullOrEmpty(version) => $"Example: kiota show -k {searchTerm}", + _ => $"Example: kiota show -k {searchTerm} -v {version}", + }; + DisplayHint("Hint: use kiota show to display a tree of paths present in the OpenAPI description.", example); } protected void DisplayShowAdvancedHint(string searchTerm, string version, IEnumerable includePaths, IEnumerable excludePaths, string path = null) { - if(TutorialMode && !includePaths.Any() && !excludePaths.Any()) { + if(!includePaths.Any() && !excludePaths.Any()) { var example = path switch { _ when !string.IsNullOrEmpty(path) => $"Example: kiota show -d {path} --include-path **/foo", _ when string.IsNullOrEmpty(version) => $"Example: kiota show -k {searchTerm} --include-path **/foo", @@ -124,11 +129,11 @@ _ when string.IsNullOrEmpty(version) => $"Example: kiota show -k {searchTerm} -- DisplayHint("Hint: use the --include-path and --exclude-path options with glob patterns to filter the paths displayed.", example); } } - protected static void DisplaySearchAddHint() { + protected void DisplaySearchAddHint() { DisplayHint("Hint: add your own API to the search result https://aka.ms/kiota/addapi."); } protected void DisplaySearchHint(string firstKey, string version) { - if (TutorialMode &&!string.IsNullOrEmpty(firstKey)) { + if (!string.IsNullOrEmpty(firstKey)) { var example = string.IsNullOrEmpty(version) ? $"Example: kiota search {firstKey}" : $"Example: kiota search {firstKey} -v {version}"; @@ -136,35 +141,42 @@ protected void DisplaySearchHint(string firstKey, string version) { } } protected void DisplayGenerateHint(string path, IEnumerable includedPaths, IEnumerable excludedPaths) { - if(TutorialMode) { - var includedPathsSuffix = ((includedPaths?.Any() ?? false)? " -i " : string.Empty) + string.Join(" -i ", includedPaths); - var excludedPathsSuffix = ((excludedPaths?.Any() ?? false)? " -e " : string.Empty) + string.Join(" -e ", excludedPaths); - var example = $"Example: kiota generate -l -o -d {path}{includedPathsSuffix}{excludedPathsSuffix}"; - DisplayHint("Hint: use kiota generate to generate a client for the OpenAPI description.", example); - } + var includedPathsSuffix = ((includedPaths?.Any() ?? false)? " -i " : string.Empty) + string.Join(" -i ", includedPaths); + var excludedPathsSuffix = ((excludedPaths?.Any() ?? false)? " -e " : string.Empty) + string.Join(" -e ", excludedPaths); + var example = $"Example: kiota generate -l -o -d {path}{includedPathsSuffix}{excludedPathsSuffix}"; + DisplayHint("Hint: use kiota generate to generate a client for the OpenAPI description.", example); } protected void DisplayGenerateAdvancedHint(IEnumerable includePaths, IEnumerable excludePaths, string path) { - if(TutorialMode && !includePaths.Any() && !excludePaths.Any()) { + if(!includePaths.Any() && !excludePaths.Any()) { DisplayHint("Hint: use the --include-path and --exclude-path options with glob patterns to filter the paths generated.", $"Example: kiota generate --include-path **/foo -d {path}"); } } protected void DisplayInfoHint(GenerationLanguage language, string path) { - if(TutorialMode) { - DisplayHint("Hint: use the info command to get the list of dependencies you need to add to your project.", - $"Example: kiota info -d {path} -l {language}"); - } + DisplayHint("Hint: use the info command to get the list of dependencies you need to add to your project.", + $"Example: kiota info -d {path} -l {language}"); } protected void DisplayCleanHint(string commandName) { - if(TutorialMode) { - DisplayHint("Hint: to force the generation to overwrite an existing client pass the --clean-output switch.", - $"Example: kiota {commandName} --clean-output"); - } + DisplayHint("Hint: to force the generation to overwrite an existing client pass the --clean-output switch.", + $"Example: kiota {commandName} --clean-output"); } protected void DisplayInfoAdvancedHint() { - if(TutorialMode) { - DisplayHint("Hint: use the language argument to get the list of dependencies you need to add to your project.", - "Example: kiota info -l "); + DisplayHint("Hint: use the language argument to get the list of dependencies you need to add to your project.", + "Example: kiota info -l "); + } + protected void DisplayGitHubLogoutHint() { + DisplayHint("Hint: use the logout command to sign out of GitHub.", + "Example: kiota logout github"); + } + protected void DisplaySearchBasicHint() { + DisplayHint("Hint: use the search command to search for an OpenAPI description.", + "Example: kiota search "); + } + protected void DisplayLoginHint(ILogger logger) { + var cachingConfig = GitHubAuthenticationCachingProvider(logger); + if(!cachingConfig.IsCachedTokenPresent()) { + DisplayHint("Hint: use the login command to sign in to GitHub and access private OpenAPI descriptions.", + "Example: kiota login github"); } } protected static void DisplayGitHubDeviceCodeLoginMessage(Uri uri, string code) { diff --git a/src/kiota/Handlers/KiotaGitHubLoginCommandHanlder.cs b/src/kiota/Handlers/KiotaGitHubLoginCommandHanlder.cs index 382c2b6d2d..6e821e931b 100644 --- a/src/kiota/Handlers/KiotaGitHubLoginCommandHanlder.cs +++ b/src/kiota/Handlers/KiotaGitHubLoginCommandHanlder.cs @@ -20,10 +20,10 @@ public override async Task InvokeAsync(InvocationContext context) return await LoginAsync(logger, cancellationToken); } catch (Exception ex) { #if DEBUG - logger.LogCritical(ex, "error downloading a description: {exceptionMessage}", ex.Message); + logger.LogCritical(ex, "error signing in to GitHub: {exceptionMessage}", ex.Message); throw; // so debug tools go straight to the source of the exception when attached #else - logger.LogCritical("error downloading a description: {exceptionMessage}", ex.Message); + logger.LogCritical("error signing in to GitHub: {exceptionMessage}", ex.Message); return 1; #endif } @@ -31,7 +31,6 @@ public override async Task InvokeAsync(InvocationContext context) } } private async Task LoginAsync(ILogger logger, CancellationToken cancellationToken) { - //TODO handle caching var authenticationProvider = new GitHubAuthenticationProvider(Configuration.Search.GitHub.AppId, "repo", new List { "api.github.com"}, @@ -45,8 +44,8 @@ private async Task LoginAsync(ILogger logger, CancellationToken cancellatio await authenticationProvider.AuthenticateRequestAsync(dummyRequest, cancellationToken: cancellationToken); if(dummyRequest.Headers.TryGetValue("Authorization", out var authHeaderValue) && authHeaderValue is string authHeader && authHeader.StartsWith("bearer", StringComparison.OrdinalIgnoreCase)) { DisplaySuccess("Authentication successful."); - //TODO hint to search command - //TODO hint to logout command + DisplaySearchBasicHint(); + DisplayGitHubLogoutHint(); return 0; } else { DisplayError("Authentication failed. Please try again."); diff --git a/src/kiota/Handlers/KiotaGitHubLogoutCommandhandler.cs b/src/kiota/Handlers/KiotaGitHubLogoutCommandhandler.cs new file mode 100644 index 0000000000..a072a15870 --- /dev/null +++ b/src/kiota/Handlers/KiotaGitHubLogoutCommandhandler.cs @@ -0,0 +1,32 @@ +using System; +using System.CommandLine.Invocation; +using System.Threading; +using System.Threading.Tasks; +using Kiota.Builder.SearchProviders.GitHub.Authentication; +using Microsoft.Extensions.Logging; + +namespace kiota.Handlers; + +internal class KiotaGitHubLogoutCommandHandler : BaseKiotaCommandHandler +{ + public override Task InvokeAsync(InvocationContext context) + { + CancellationToken cancellationToken = (CancellationToken)context.BindingContext.GetService(typeof(CancellationToken)); + var (loggerFactory, logger) = GetLoggerAndFactory(context); + using (loggerFactory) { + try { + var cachingProvider = GitHubAuthenticationCachingProvider(logger); + cachingProvider.Logout(); + return Task.FromResult(0); + } catch (Exception ex) { + #if DEBUG + logger.LogCritical(ex, "error logging out from GitHub: {exceptionMessage}", ex.Message); + throw; // so debug tools go straight to the source of the exception when attached + #else + logger.LogCritical("error logging out from GitHub: {exceptionMessage}", ex.Message); + return Task.FromResult(1); + #endif + } + } + } +} diff --git a/src/kiota/Handlers/KiotaSearchCommandHandler.cs b/src/kiota/Handlers/KiotaSearchCommandHandler.cs index 3e0c3b1719..15044c525b 100644 --- a/src/kiota/Handlers/KiotaSearchCommandHandler.cs +++ b/src/kiota/Handlers/KiotaSearchCommandHandler.cs @@ -38,7 +38,7 @@ public override async Task InvokeAsync(InvocationContext context) try { var results = await new KiotaSearcher(logger, Configuration.Search, httpClient).SearchAsync(cancellationToken); - DisplayResults(results); + DisplayResults(results, logger); return 0; } catch (Exception ex) { #if DEBUG @@ -51,7 +51,7 @@ public override async Task InvokeAsync(InvocationContext context) } } } - private void DisplayResults(IDictionary results){ + private void DisplayResults(IDictionary results, ILogger logger){ var searchTerm = Configuration.Search.SearchTerm; if (results.Any() && !string.IsNullOrEmpty(searchTerm) && searchTerm.Contains(KiotaSearcher.ProviderSeparator) && results.ContainsKey(searchTerm)) { var result = results.First(); @@ -75,6 +75,7 @@ private void DisplayResults(IDictionary results){ var layout = new StackLayoutView { view }; console.Append(layout); DisplaySearchHint(results.Keys.FirstOrDefault(), Configuration.Search.Version); + DisplayLoginHint(logger); DisplaySearchAddHint(); } } diff --git a/src/kiota/KiotaHost.cs b/src/kiota/KiotaHost.cs index ca5f0e5929..52b9736730 100644 --- a/src/kiota/KiotaHost.cs +++ b/src/kiota/KiotaHost.cs @@ -40,6 +40,9 @@ private static Command GetGitHubLogoutCommand() { var githubLogoutCommand = new Command("github", "Logs out of GitHub.") { logLevelOption, }; + githubLogoutCommand.Handler = new KiotaGitHubLogoutCommandHandler { + LogLevelOption = logLevelOption, + }; return githubLogoutCommand; } private static Command GetLoginCommand() { From bf8faac163a1f7678ef80603396ce3de7df71cac Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 22 Nov 2022 15:44:08 -0500 Subject: [PATCH 04/42] - adds missing parameters to builder Signed-off-by: Vincent Biret --- src/Kiota.Web/Pages/Generate.razor | 2 +- src/Kiota.Web/Pages/Show.razor | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Kiota.Web/Pages/Generate.razor b/src/Kiota.Web/Pages/Generate.razor index 0c7d343a1b..13e3a0ce51 100644 --- a/src/Kiota.Web/Pages/Generate.razor +++ b/src/Kiota.Web/Pages/Generate.razor @@ -227,7 +227,7 @@ }; var logBuilder = new StringBuilder(); var builderLogger = new StringBuilderLogger(LoggerFactory.CreateLogger(), logBuilder, LogLevel.Warning); - var builder = new KiotaBuilder(builderLogger, generationConfiguration); + var builder = new KiotaBuilder(builderLogger, generationConfiguration, Http); try { await builder.GenerateClientAsync(ComponentDetached).ConfigureAwait(false); var zipFilePath = Path.Combine(Path.GetTempPath(), "kiota", "clients", hashedUrl, "client.zip"); diff --git a/src/Kiota.Web/Pages/Show.razor b/src/Kiota.Web/Pages/Show.razor index d60df30468..eaf0b65adb 100644 --- a/src/Kiota.Web/Pages/Show.razor +++ b/src/Kiota.Web/Pages/Show.razor @@ -124,7 +124,7 @@ OpenAPIFilePath = DescriptionUrl, }; var builderLogger = LoggerFactory.CreateLogger(); - kiotaBuilder = new KiotaBuilder(builderLogger, generationConfiguration); + kiotaBuilder = new KiotaBuilder(builderLogger, generationConfiguration, Http); } generationConfiguration.IncludePatterns = IncludePatterns?.Split('\n', StringSplitOptions.RemoveEmptyEntries).Select(static x => x.Trim()).ToHashSet(); generationConfiguration.ExcludePatterns = ExcludePatterns?.Split('\n', StringSplitOptions.RemoveEmptyEntries).Select(static x => x.Trim()).ToHashSet(); From ab30973b47fea6f63a0b4304a8f9e25b495c7425 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Wed, 23 Nov 2022 09:07:34 -0500 Subject: [PATCH 05/42] - code linting - adds status message for logout command Signed-off-by: Vincent Biret --- .../Configuration/SearchConfiguration.cs | 1 + .../DeviceCode/GitHubAcessTokenProvider.cs | 13 ++++++++----- .../DeviceCode/GitHubAuthenticationProvider.cs | 13 ++++++++----- .../TempFolderCachingAccessTokenProvider.cs | 14 +++++++------- .../SearchProviders/GitHub/GitHubSearchProvider.cs | 8 ++++---- src/kiota/Handlers/BaseKiotaCommandHandler.cs | 2 +- .../Handlers/KiotaGitHubLoginCommandHanlder.cs | 2 +- .../Handlers/KiotaGitHubLogoutCommandhandler.cs | 7 +++++-- 8 files changed, 35 insertions(+), 25 deletions(-) diff --git a/src/Kiota.Builder/Configuration/SearchConfiguration.cs b/src/Kiota.Builder/Configuration/SearchConfiguration.cs index a9ace1f002..5f555541a1 100644 --- a/src/Kiota.Builder/Configuration/SearchConfiguration.cs +++ b/src/Kiota.Builder/Configuration/SearchConfiguration.cs @@ -10,6 +10,7 @@ public class SearchConfiguration : SearchConfigurationBase { public class GitHubConfiguration { public string AppId { get; set; } = "0adc165b71f9824f4282"; + public Uri ApiBaseUrl { get; set; } = new ("https://api.github.com"); public Uri BlockListUrl { get; set; } = new ("https://raw.githubusercontent.com/microsoft/kiota/main/resources/index-block-list.yml"); [JsonIgnore] public Action DeviceCodeCallback { get; set; } diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAcessTokenProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAcessTokenProvider.cs index e122c0931b..c5f799e110 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAcessTokenProvider.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAcessTokenProvider.cs @@ -16,13 +16,16 @@ internal class GitHubAccessTokenProvider : IAccessTokenProvider internal required string Scope { get; init; } internal required HttpClient HttpClient {get; init;} internal string BaseLoginUrl { get; init; } = "https://github.com/login"; - public async Task GetAuthorizationTokenAsync(Uri uri, Dictionary additionalAuthenticationContext = null, CancellationToken cancellationToken = default) - { - if(!AllowedHostsValidator.IsUrlHostValid(uri)) - return string.Empty; + public Task GetAuthorizationTokenAsync(Uri uri, Dictionary additionalAuthenticationContext = null, CancellationToken cancellationToken = default) { + if(!AllowedHostsValidator.IsUrlHostValid(uri)) + return Task.FromResult(string.Empty); if(!uri.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase)) throw new ArgumentException("Only https is supported"); + return GetAuthorizationTokenInternalAsync(cancellationToken); + } + private async Task GetAuthorizationTokenInternalAsync(CancellationToken cancellationToken ) + { var deviceCodeResponse = await GetDeviceCodeAsync(cancellationToken); MessageCallback(deviceCodeResponse.VerificationUri, deviceCodeResponse.UserCode); var tokenResponse = await PollForTokenAsync(deviceCodeResponse, cancellationToken); @@ -62,7 +65,7 @@ private async Task GetTokenAsync(GitHubDeviceCodeRespo if ("authorization_pending".Equals(result.Error, StringComparison.OrdinalIgnoreCase)) return null; else if (!string.IsNullOrEmpty(result.Error)) - throw new Exception($"Error while getting token: {result.Error} - {result.ErrorDescription}"); + throw new InvalidOperationException($"Error while getting token: {result.Error} - {result.ErrorDescription}"); else return result; } diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAuthenticationProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAuthenticationProvider.cs index b376f92333..113cfc9ddf 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAuthenticationProvider.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAuthenticationProvider.cs @@ -14,14 +14,14 @@ public class GitHubAuthenticationProvider : GitHubAnonymousAuthenticationProvide { public GitHubAuthenticationProvider(string clientId, string scope, IEnumerable validHosts, HttpClient httpClient, Action messageCallback, ILogger logger) { - if (string.IsNullOrEmpty(clientId)) - throw new ArgumentNullException(nameof(clientId)); - if (string.IsNullOrEmpty(scope)) - throw new ArgumentNullException(nameof(scope)); ArgumentNullException.ThrowIfNull(validHosts); ArgumentNullException.ThrowIfNull(httpClient); ArgumentNullException.ThrowIfNull(messageCallback); ArgumentNullException.ThrowIfNull(logger); + if (string.IsNullOrEmpty(clientId)) + throw new ArgumentNullException(nameof(clientId)); + if (string.IsNullOrEmpty(scope)) + throw new ArgumentNullException(nameof(scope)); AccessTokenProvider = new TempFolderCachingAccessTokenProvider { Concrete = new GitHubAccessTokenProvider { @@ -39,8 +39,11 @@ public GitHubAuthenticationProvider(string clientId, string scope, IEnumerable additionalAuthenticationContext = null, CancellationToken cancellationToken = default) { + public override Task AuthenticateRequestAsync(RequestInformation request, Dictionary additionalAuthenticationContext = null, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(request); + return AuthenticateRequestInternalAsync(request, additionalAuthenticationContext, cancellationToken); + } + private async Task AuthenticateRequestInternalAsync(RequestInformation request, Dictionary additionalAuthenticationContext = null, CancellationToken cancellationToken = default) { await base.AuthenticateRequestAsync(request, additionalAuthenticationContext, cancellationToken).ConfigureAwait(false); if(additionalAuthenticationContext != null && additionalAuthenticationContext.ContainsKey(ClaimsKey) && diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/TempFolderCachingAccessTokenProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/TempFolderCachingAccessTokenProvider.cs index 59485df644..9ee8930753 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/TempFolderCachingAccessTokenProvider.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/TempFolderCachingAccessTokenProvider.cs @@ -62,13 +62,13 @@ private async Task GetTokenFromCacheAsync(Uri uri, CancellationToken can return null; } } - public void Logout() { - try { - var target = GetTokenCacheFilePath(); - File.Delete(target); - } catch (Exception ex) { - Logger.LogWarning(ex, "Error while deleting token from cache."); - } + public bool Logout() { + //no try-catch as we want the exception to bubble up to the command + var target = GetTokenCacheFilePath(); + if(!IsCachedTokenPresent()) + return false; + File.Delete(target); + return true; } public bool IsCachedTokenPresent() { try { diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs index 05e835bfc0..a96466293a 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs @@ -22,7 +22,7 @@ namespace Kiota.Builder.SearchProviders.GitHub; public class GitHubSearchProvider : ISearchProvider { - private readonly DocumentCachingProvider cachingProvider; + private readonly DocumentCachingProvider documentCachingProvider; private readonly ILogger _logger; private readonly Uri _blockListUrl; private readonly string _clientId; @@ -38,7 +38,7 @@ public GitHubSearchProvider(HttpClient httpClient, ILogger logger, bool clearCac ArgumentNullException.ThrowIfNull(logger); if(string.IsNullOrEmpty(configuration.AppId)) throw new ArgumentOutOfRangeException(nameof(configuration)); - cachingProvider = new DocumentCachingProvider(httpClient, logger) + documentCachingProvider = new DocumentCachingProvider(httpClient, logger) { ClearCache = clearCache, }; @@ -114,7 +114,7 @@ private static Dictionary GetDictionaryResultFromMultipleS .ToDictionary(static x => x.Item1, static x => x.Item2, StringComparer.OrdinalIgnoreCase); private async Task, HashSet>> GetBlockLists(CancellationToken cancellationToken) { try { - await using var document = await cachingProvider.GetDocumentAsync(_blockListUrl, "search", _blockListUrl.GetFileName(), "text/yaml", cancellationToken); + await using var document = await documentCachingProvider.GetDocumentAsync(_blockListUrl, "search", _blockListUrl.GetFileName(), "text/yaml", cancellationToken); var deserialized = deserializeDocumentFromYaml(document); return new Tuple, HashSet>( new HashSet(deserialized.Organizations.Distinct(StringComparer.OrdinalIgnoreCase), StringComparer.OrdinalIgnoreCase), @@ -147,7 +147,7 @@ private async Task>> GetSearchResultsFro if (!response.AdditionalData.TryGetValue(DownloadUrlKey, out var rawDownloadUrl) || rawDownloadUrl is not string downloadUrl || string.IsNullOrEmpty(downloadUrl)) return Enumerable.Empty>(); var targetUrl = new Uri(downloadUrl); - await using var document = await cachingProvider.GetDocumentAsync(targetUrl, "search", targetUrl.GetFileName(), accept, cancellationToken); + await using var document = await documentCachingProvider.GetDocumentAsync(targetUrl, "search", targetUrl.GetFileName(), accept, cancellationToken); var indexFile = accept.ToLowerInvariant() switch { "application/json" => deserializeDocumentFromJson(document), diff --git a/src/kiota/Handlers/BaseKiotaCommandHandler.cs b/src/kiota/Handlers/BaseKiotaCommandHandler.cs index a2cc20bf61..9f20d13b7f 100644 --- a/src/kiota/Handlers/BaseKiotaCommandHandler.cs +++ b/src/kiota/Handlers/BaseKiotaCommandHandler.cs @@ -18,7 +18,7 @@ internal abstract class BaseKiotaCommandHandler : ICommandHandler { protected TempFolderCachingAccessTokenProvider GitHubAuthenticationCachingProvider(ILogger logger) => new(){ Logger = logger, - ApiBaseUrl = new Uri("https://api.github.com"), + ApiBaseUrl = Configuration.Search.GitHub.ApiBaseUrl, Concrete = null, AppId = Configuration.Search.GitHub.AppId, }; diff --git a/src/kiota/Handlers/KiotaGitHubLoginCommandHanlder.cs b/src/kiota/Handlers/KiotaGitHubLoginCommandHanlder.cs index 6e821e931b..76ca4f0def 100644 --- a/src/kiota/Handlers/KiotaGitHubLoginCommandHanlder.cs +++ b/src/kiota/Handlers/KiotaGitHubLoginCommandHanlder.cs @@ -39,7 +39,7 @@ private async Task LoginAsync(ILogger logger, CancellationToken cancellatio logger); var dummyRequest = new RequestInformation() { HttpMethod = Method.GET, - URI = new Uri("https://api.github.com/user"), + URI = Configuration.Search.GitHub.ApiBaseUrl, }; await authenticationProvider.AuthenticateRequestAsync(dummyRequest, cancellationToken: cancellationToken); if(dummyRequest.Headers.TryGetValue("Authorization", out var authHeaderValue) && authHeaderValue is string authHeader && authHeader.StartsWith("bearer", StringComparison.OrdinalIgnoreCase)) { diff --git a/src/kiota/Handlers/KiotaGitHubLogoutCommandhandler.cs b/src/kiota/Handlers/KiotaGitHubLogoutCommandhandler.cs index a072a15870..0ebb34e2e0 100644 --- a/src/kiota/Handlers/KiotaGitHubLogoutCommandhandler.cs +++ b/src/kiota/Handlers/KiotaGitHubLogoutCommandhandler.cs @@ -11,12 +11,15 @@ internal class KiotaGitHubLogoutCommandHandler : BaseKiotaCommandHandler { public override Task InvokeAsync(InvocationContext context) { - CancellationToken cancellationToken = (CancellationToken)context.BindingContext.GetService(typeof(CancellationToken)); var (loggerFactory, logger) = GetLoggerAndFactory(context); using (loggerFactory) { try { var cachingProvider = GitHubAuthenticationCachingProvider(logger); - cachingProvider.Logout(); + var result = cachingProvider.Logout(); + if(result) + DisplaySuccess("Logged out successfully."); + else + DisplaySuccess("Already logged out."); return Task.FromResult(0); } catch (Exception ex) { #if DEBUG From 02973a644e342d822d66b5f2cfa4705cf9da9121 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Wed, 23 Nov 2022 15:23:30 -0500 Subject: [PATCH 06/42] - adds base infrastructure for browser authentication Signed-off-by: Vincent Biret --- .../Configuration/SearchConfiguration.cs | 2 - src/Kiota.Builder/KiotaSearcher.cs | 32 +++++---- ...sCodeResponse.cs => AccessCodeResponse.cs} | 4 +- ....cs => AnonymousAuthenticationProvider.cs} | 2 +- ...vider.cs => BaseAuthenticationProvider.cs} | 22 +++---- .../Browser/AccessTokenProvider.cs | 66 +++++++++++++++++++ .../Browser/BrowserAuthenticationProvider.cs | 26 ++++++++ ...TokenProvider.cs => AcessTokenProvider.cs} | 16 ++--- .../DeviceCodeAuthenticationProvider.cs | 24 +++++++ ...eCodeResponse.cs => DeviceCodeResponse.cs} | 0 .../GitHub/GitHubSearchProvider.cs | 21 +++--- src/Kiota.Web/Pages/Generate.razor | 4 +- src/Kiota.Web/Pages/Index.razor | 4 +- src/Kiota.Web/Pages/Show.razor | 6 +- src/Kiota.Web/Program.cs | 19 ++++++ src/kiota/Handlers/BaseKiotaCommandHandler.cs | 11 +++- .../Handlers/KiotaDownloadCommandHandler.cs | 2 +- .../KiotaGitHubLoginCommandHanlder.cs | 4 +- .../Handlers/KiotaSeachBasedCommandHandler.cs | 6 +- .../Handlers/KiotaSearchCommandHandler.cs | 2 +- .../Kiota.Builder.Tests/KiotaSearcherTests.cs | 30 ++++----- 21 files changed, 225 insertions(+), 78 deletions(-) rename src/Kiota.Builder/SearchProviders/GitHub/Autentication/{DeviceCode/GitHubAccessCodeResponse.cs => AccessCodeResponse.cs} (80%) rename src/Kiota.Builder/SearchProviders/GitHub/Autentication/{GitHubAnonymousAuthenticationProvider.cs => AnonymousAuthenticationProvider.cs} (88%) rename src/Kiota.Builder/SearchProviders/GitHub/Autentication/{DeviceCode/GitHubAuthenticationProvider.cs => BaseAuthenticationProvider.cs} (74%) create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/Autentication/Browser/AccessTokenProvider.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/Autentication/Browser/BrowserAuthenticationProvider.cs rename src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/{GitHubAcessTokenProvider.cs => AcessTokenProvider.cs} (84%) create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/DeviceCodeAuthenticationProvider.cs rename src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/{GitHubDeviceCodeResponse.cs => DeviceCodeResponse.cs} (100%) diff --git a/src/Kiota.Builder/Configuration/SearchConfiguration.cs b/src/Kiota.Builder/Configuration/SearchConfiguration.cs index 5f555541a1..b2bd435b9c 100644 --- a/src/Kiota.Builder/Configuration/SearchConfiguration.cs +++ b/src/Kiota.Builder/Configuration/SearchConfiguration.cs @@ -12,6 +12,4 @@ public class GitHubConfiguration { public string AppId { get; set; } = "0adc165b71f9824f4282"; public Uri ApiBaseUrl { get; set; } = new ("https://api.github.com"); public Uri BlockListUrl { get; set; } = new ("https://raw.githubusercontent.com/microsoft/kiota/main/resources/index-block-list.yml"); - [JsonIgnore] - public Action DeviceCodeCallback { get; set; } } diff --git a/src/Kiota.Builder/KiotaSearcher.cs b/src/Kiota.Builder/KiotaSearcher.cs index 82f2ef4764..68552a7ee9 100644 --- a/src/Kiota.Builder/KiotaSearcher.cs +++ b/src/Kiota.Builder/KiotaSearcher.cs @@ -10,31 +10,35 @@ using Kiota.Builder.SearchProviders.GitHub; using Kiota.Builder.SearchProviders.MSGraph; using Microsoft.Extensions.Logging; +using Microsoft.Kiota.Abstractions.Authentication; namespace Kiota.Builder; public class KiotaSearcher { - private readonly ILogger logger; - private readonly SearchConfiguration config; - private readonly HttpClient httpClient; - public KiotaSearcher(ILogger logger, SearchConfiguration config, HttpClient httpClient) { + private readonly ILogger _logger; + private readonly SearchConfiguration _config; + private readonly HttpClient _httpClient; + private readonly IAuthenticationProvider _gitHubAuthenticationProvider; + + public KiotaSearcher(ILogger logger, SearchConfiguration config, HttpClient httpClient, IAuthenticationProvider gitHubAuthenticationProvider) { ArgumentNullException.ThrowIfNull(logger); ArgumentNullException.ThrowIfNull(config); ArgumentNullException.ThrowIfNull(httpClient); - this.logger = logger; - this.config = config; - this.httpClient = httpClient; + _logger = logger; + _config = config; + _httpClient = httpClient; + _gitHubAuthenticationProvider = gitHubAuthenticationProvider; } public async Task> SearchAsync(CancellationToken cancellationToken) { - if (string.IsNullOrEmpty(config.SearchTerm)) { - logger.LogError("no search term provided"); + if (string.IsNullOrEmpty(_config.SearchTerm)) { + _logger.LogError("no search term provided"); return new Dictionary(); } - var apiGurusSearchProvider = new APIsGuruSearchProvider(config.APIsGuruListUrl, httpClient, logger, config.ClearCache); - logger.LogDebug("searching for {searchTerm}", config.SearchTerm); - logger.LogDebug("searching APIs.guru with url {url}", config.APIsGuruListUrl); + var apiGurusSearchProvider = new APIsGuruSearchProvider(_config.APIsGuruListUrl, _httpClient, _logger, _config.ClearCache); + _logger.LogDebug("searching for {searchTerm}", _config.SearchTerm); + _logger.LogDebug("searching APIs.guru with url {url}", _config.APIsGuruListUrl); var oasProvider = new OpenApiSpecSearchProvider(); - var githubProvider = new GitHubSearchProvider(httpClient, logger, config.ClearCache, config.GitHub); + var githubProvider = new GitHubSearchProvider(_httpClient, _logger, _config.ClearCache, _config.GitHub, _gitHubAuthenticationProvider); var results = await Task.WhenAll( SearchProviderAsync(apiGurusSearchProvider, cancellationToken), SearchProviderAsync(oasProvider, cancellationToken), @@ -44,7 +48,7 @@ public async Task> SearchAsync(CancellationTok } private async Task> SearchProviderAsync(ISearchProvider provider, CancellationToken cancellationToken) { var providerPrefix = $"{provider.ProviderKey}{ProviderSeparator}"; - var results = await provider.SearchAsync(config.SearchTerm.Replace(providerPrefix, string.Empty), config.Version, cancellationToken); + var results = await provider.SearchAsync(_config.SearchTerm.Replace(providerPrefix, string.Empty), _config.Version, cancellationToken); return results.Select(x => ($"{providerPrefix}{x.Key}", x.Value)) .ToDictionary(static x => x.Item1, static x => x.Value, StringComparer.OrdinalIgnoreCase); diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAccessCodeResponse.cs b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/AccessCodeResponse.cs similarity index 80% rename from src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAccessCodeResponse.cs rename to src/Kiota.Builder/SearchProviders/GitHub/Autentication/AccessCodeResponse.cs index 36f71bd37b..b0b91cc00e 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAccessCodeResponse.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/AccessCodeResponse.cs @@ -1,9 +1,9 @@ using System; using System.Text.Json.Serialization; -namespace Kiota.Builder.SearchProviders.GitHub.Authentication.DeviceCode; +namespace Kiota.Builder.SearchProviders.GitHub.Authentication; -internal class GitHubAccessCodeResponse { +internal class AccessCodeResponse { [JsonPropertyName("access_token")] public string AccessToken { get; set; } [JsonPropertyName("token_type")] diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/GitHubAnonymousAuthenticationProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/AnonymousAuthenticationProvider.cs similarity index 88% rename from src/Kiota.Builder/SearchProviders/GitHub/Autentication/GitHubAnonymousAuthenticationProvider.cs rename to src/Kiota.Builder/SearchProviders/GitHub/Autentication/AnonymousAuthenticationProvider.cs index f5d790508a..898d54e7c0 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/GitHubAnonymousAuthenticationProvider.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/AnonymousAuthenticationProvider.cs @@ -5,7 +5,7 @@ using Microsoft.Kiota.Abstractions.Authentication; namespace Kiota.Builder.SearchProviders.GitHub.Authentication; -public class GitHubAnonymousAuthenticationProvider : IAuthenticationProvider +public class AnonymousAuthenticationProvider : IAuthenticationProvider { public virtual Task AuthenticateRequestAsync(RequestInformation request, Dictionary additionalAuthenticationContext = null, CancellationToken cancellationToken = default) { diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAuthenticationProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/BaseAuthenticationProvider.cs similarity index 74% rename from src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAuthenticationProvider.cs rename to src/Kiota.Builder/SearchProviders/GitHub/Autentication/BaseAuthenticationProvider.cs index 113cfc9ddf..051d259fc4 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAuthenticationProvider.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/BaseAuthenticationProvider.cs @@ -1,36 +1,32 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Kiota.Abstractions; using Microsoft.Kiota.Abstractions.Authentication; -namespace Kiota.Builder.SearchProviders.GitHub.Authentication.DeviceCode; +namespace Kiota.Builder.SearchProviders.GitHub.Authentication; -public class GitHubAuthenticationProvider : GitHubAnonymousAuthenticationProvider +public class BaseAuthenticationProvider : AnonymousAuthenticationProvider where T: class, IAccessTokenProvider { - public GitHubAuthenticationProvider(string clientId, string scope, IEnumerable validHosts, HttpClient httpClient, Action messageCallback, ILogger logger) + public BaseAuthenticationProvider(string clientId, + string scope, + IEnumerable validHosts, + ILogger logger, + Func, T> accessTokenProviderFactory) { ArgumentNullException.ThrowIfNull(validHosts); - ArgumentNullException.ThrowIfNull(httpClient); - ArgumentNullException.ThrowIfNull(messageCallback); ArgumentNullException.ThrowIfNull(logger); + ArgumentNullException.ThrowIfNull(accessTokenProviderFactory); if (string.IsNullOrEmpty(clientId)) throw new ArgumentNullException(nameof(clientId)); if (string.IsNullOrEmpty(scope)) throw new ArgumentNullException(nameof(scope)); AccessTokenProvider = new TempFolderCachingAccessTokenProvider { - Concrete = new GitHubAccessTokenProvider { - ClientId = clientId, - Scope = scope, - AllowedHostsValidator = new AllowedHostsValidator(validHosts), - HttpClient = httpClient, - MessageCallback = messageCallback - }, + Concrete = accessTokenProviderFactory(clientId, scope, validHosts), Logger = logger, ApiBaseUrl = new Uri($"https://{validHosts.FirstOrDefault() ?? "api.github.com"}"), AppId = clientId, diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/Browser/AccessTokenProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/Browser/AccessTokenProvider.cs new file mode 100644 index 0000000000..2b142d649a --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/Browser/AccessTokenProvider.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Kiota.Abstractions.Authentication; + +namespace Kiota.Builder.SearchProviders.GitHub.Authentication.Browser; +public class AccessTokenProvider : IAccessTokenProvider +{ + public AllowedHostsValidator AllowedHostsValidator {get; set;} = new(); + public required string ClientId { get; init; } + public required string Scope { get; init; } + public required Uri RedirectUri { get; init; } + public required HttpClient HttpClient {get; init;} + public required Action RedirectCallback { get; init; } + public string AuthorizationCode { get; init; } + internal string BaseLoginUrl { get; init; } = "https://github.com/login"; + public Task GetAuthorizationTokenAsync(Uri uri, Dictionary additionalAuthenticationContext = null, CancellationToken cancellationToken = default) + { + if(!AllowedHostsValidator.IsUrlHostValid(uri)) + return Task.FromResult(string.Empty); + if(!uri.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase)) + throw new ArgumentException("Only https is supported"); + + return GetAuthorizationTokenInternalAsync(cancellationToken); + } + private async Task GetAuthorizationTokenInternalAsync(CancellationToken cancellationToken) { + if(string.IsNullOrEmpty(AuthorizationCode)) { + var state = Guid.NewGuid(); + RedirectCallback(GetAuthorizeUrl(state), state.ToString()); + return string.Empty; + } else { + var tokenResponse = await GetTokenAsync(cancellationToken); + return tokenResponse.AccessToken; + } + } + private Uri GetAuthorizeUrl(Guid state) { + var authorizeUrl = $"{BaseLoginUrl}/oauth/authorize?client_id={ClientId}&scope={Scope}&redirect_uri={RedirectUri}&state={state}"; + return new Uri(authorizeUrl); + } + private async Task GetTokenAsync(CancellationToken cancellationToken) + { + using var tokenRequest = new HttpRequestMessage(HttpMethod.Post, $"{BaseLoginUrl}/oauth/access_token") { + Content = new FormUrlEncodedContent(new Dictionary { + { "client_id", ClientId }, + { "code", AuthorizationCode }, //TODO missing secret? what about SPA? + // { "grant_type", "urn:ietf:params:oauth:grant-type:device_code" } + }) + }; + tokenRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + using var tokenResponse = await HttpClient.SendAsync(tokenRequest, cancellationToken); + tokenResponse.EnsureSuccessStatusCode(); + var tokenContent = await tokenResponse.Content.ReadAsStringAsync(cancellationToken); + var result = JsonSerializer.Deserialize(tokenContent); + if ("authorization_pending".Equals(result.Error, StringComparison.OrdinalIgnoreCase)) + return null; + else if (!string.IsNullOrEmpty(result.Error)) + throw new InvalidOperationException($"Error while getting token: {result.Error} - {result.ErrorDescription}"); + else + return result; + } + +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/Browser/BrowserAuthenticationProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/Browser/BrowserAuthenticationProvider.cs new file mode 100644 index 0000000000..17b632ca2f --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/Browser/BrowserAuthenticationProvider.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using Microsoft.Extensions.Logging; +using Microsoft.Kiota.Abstractions.Authentication; + +namespace Kiota.Builder.SearchProviders.GitHub.Authentication.Browser; + +public class BrowserAuthenticationProvider : BaseAuthenticationProvider +{ + public BrowserAuthenticationProvider(string clientId, string scope, IEnumerable validHosts, HttpClient httpClient, Action redirectCallback, ILogger logger, string authorizationCode, Uri redirectUri) : + base(clientId, scope, validHosts, logger, (clientId, scope, validHosts) => new AccessTokenProvider { + ClientId = clientId, + HttpClient = httpClient, + RedirectCallback = redirectCallback, + Scope = scope, + AllowedHostsValidator = new AllowedHostsValidator(validHosts), + AuthorizationCode = authorizationCode, + RedirectUri = redirectUri, + }) + { + ArgumentNullException.ThrowIfNull(httpClient); + ArgumentNullException.ThrowIfNull(redirectCallback); + ArgumentNullException.ThrowIfNull(redirectUri); + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAcessTokenProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/AcessTokenProvider.cs similarity index 84% rename from src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAcessTokenProvider.cs rename to src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/AcessTokenProvider.cs index c5f799e110..0c5124c1cc 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubAcessTokenProvider.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/AcessTokenProvider.cs @@ -8,13 +8,13 @@ using Microsoft.Kiota.Abstractions.Authentication; namespace Kiota.Builder.SearchProviders.GitHub.Authentication.DeviceCode; -internal class GitHubAccessTokenProvider : IAccessTokenProvider +public class AccessTokenProvider : IAccessTokenProvider { - internal required Action MessageCallback { get; init; } + public required Action MessageCallback { get; init; } public AllowedHostsValidator AllowedHostsValidator {get;set;} = new (); - internal required string ClientId { get; init; } - internal required string Scope { get; init; } - internal required HttpClient HttpClient {get; init;} + public required string ClientId { get; init; } + public required string Scope { get; init; } + public required HttpClient HttpClient {get; init;} internal string BaseLoginUrl { get; init; } = "https://github.com/login"; public Task GetAuthorizationTokenAsync(Uri uri, Dictionary additionalAuthenticationContext = null, CancellationToken cancellationToken = default) { if(!AllowedHostsValidator.IsUrlHostValid(uri)) @@ -31,7 +31,7 @@ private async Task GetAuthorizationTokenInternalAsync(CancellationToken var tokenResponse = await PollForTokenAsync(deviceCodeResponse, cancellationToken); return tokenResponse.AccessToken; } - private async Task PollForTokenAsync(GitHubDeviceCodeResponse deviceCodeResponse, CancellationToken cancellationToken) + private async Task PollForTokenAsync(GitHubDeviceCodeResponse deviceCodeResponse, CancellationToken cancellationToken) { var timeOutTask = Task.Delay(TimeSpan.FromSeconds(deviceCodeResponse.ExpiresInSeconds), cancellationToken); var pollTask = Task.Run(async () => { @@ -48,7 +48,7 @@ private async Task PollForTokenAsync(GitHubDeviceCodeR throw new TimeoutException("The device code has expired."); return await pollTask; } - private async Task GetTokenAsync(GitHubDeviceCodeResponse deviceCodeResponse, CancellationToken cancellationToken) + private async Task GetTokenAsync(GitHubDeviceCodeResponse deviceCodeResponse, CancellationToken cancellationToken) { using var tokenRequest = new HttpRequestMessage(HttpMethod.Post, $"{BaseLoginUrl}/oauth/access_token") { Content = new FormUrlEncodedContent(new Dictionary { @@ -61,7 +61,7 @@ private async Task GetTokenAsync(GitHubDeviceCodeRespo using var tokenResponse = await HttpClient.SendAsync(tokenRequest, cancellationToken); tokenResponse.EnsureSuccessStatusCode(); var tokenContent = await tokenResponse.Content.ReadAsStringAsync(cancellationToken); - var result = JsonSerializer.Deserialize(tokenContent); + var result = JsonSerializer.Deserialize(tokenContent); if ("authorization_pending".Equals(result.Error, StringComparison.OrdinalIgnoreCase)) return null; else if (!string.IsNullOrEmpty(result.Error)) diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/DeviceCodeAuthenticationProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/DeviceCodeAuthenticationProvider.cs new file mode 100644 index 0000000000..133259a8a8 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/DeviceCodeAuthenticationProvider.cs @@ -0,0 +1,24 @@ + +using System; +using System.Collections.Generic; +using System.Net.Http; +using Microsoft.Extensions.Logging; +using Microsoft.Kiota.Abstractions.Authentication; + +namespace Kiota.Builder.SearchProviders.GitHub.Authentication.DeviceCode; + +public class DeviceCodeAuthenticationProvider : BaseAuthenticationProvider +{ + public DeviceCodeAuthenticationProvider(string clientId, string scope, IEnumerable validHosts, HttpClient httpClient, Action messageCallback, ILogger logger) : + base(clientId, scope, validHosts, logger, (clientId, scope, validHosts) => new AccessTokenProvider { + ClientId = clientId, + HttpClient = httpClient, + MessageCallback = messageCallback, + Scope = scope, + AllowedHostsValidator = new AllowedHostsValidator(validHosts), + }) + { + ArgumentNullException.ThrowIfNull(httpClient); + ArgumentNullException.ThrowIfNull(messageCallback); + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubDeviceCodeResponse.cs b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/DeviceCodeResponse.cs similarity index 100% rename from src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/GitHubDeviceCodeResponse.cs rename to src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/DeviceCodeResponse.cs diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs index a96466293a..04c1e1ff56 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs @@ -14,6 +14,7 @@ using Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models; using Kiota.Builder.SearchProviders.GitHub.Index; using Microsoft.Extensions.Logging; +using Microsoft.Kiota.Abstractions.Authentication; using Microsoft.Kiota.Http.HttpClientLibrary; using SharpYaml; using YamlDotNet.Serialization; @@ -26,15 +27,14 @@ public class GitHubSearchProvider : ISearchProvider private readonly ILogger _logger; private readonly Uri _blockListUrl; private readonly string _clientId; - private readonly Action _messageCallBack; - private const string ValidHost = "api.github.com"; - private const string Scope = "repo"; - public GitHubSearchProvider(HttpClient httpClient, ILogger logger, bool clearCache, GitHubConfiguration configuration) + private readonly Uri _appBaseUrl; + private readonly IAuthenticationProvider _authenticatedAuthenticationProvider; + public GitHubSearchProvider(HttpClient httpClient, ILogger logger, bool clearCache, GitHubConfiguration configuration, IAuthenticationProvider authenticatedAuthenticationProvider) { ArgumentNullException.ThrowIfNull(httpClient); ArgumentNullException.ThrowIfNull(configuration); ArgumentNullException.ThrowIfNull(configuration.BlockListUrl); - ArgumentNullException.ThrowIfNull(configuration.DeviceCodeCallback); + ArgumentNullException.ThrowIfNull(configuration.ApiBaseUrl); ArgumentNullException.ThrowIfNull(logger); if(string.IsNullOrEmpty(configuration.AppId)) throw new ArgumentOutOfRangeException(nameof(configuration)); @@ -46,7 +46,8 @@ public GitHubSearchProvider(HttpClient httpClient, ILogger logger, bool clearCac _logger = logger; _blockListUrl = configuration.BlockListUrl; _clientId = configuration.AppId; - _messageCallBack = configuration.DeviceCodeCallback; + _authenticatedAuthenticationProvider = authenticatedAuthenticationProvider; + _appBaseUrl = configuration.ApiBaseUrl; } private readonly HttpClient _httpClient; public string ProviderKey => "github"; @@ -74,13 +75,13 @@ private async Task> SearchAsyncInternal(string var blockLists = await GetBlockLists(cancellationToken); var cachingProvider = new TempFolderCachingAccessTokenProvider { Logger = _logger, - ApiBaseUrl = new Uri($"https://{ValidHost}"), + ApiBaseUrl = _appBaseUrl, Concrete = null, AppId = _clientId, }; - var authenticationProvider = cachingProvider.IsCachedTokenPresent() ? - new GitHubAuthenticationProvider(_clientId, Scope, new List { ValidHost }, _httpClient, _messageCallBack, _logger) : - new GitHubAnonymousAuthenticationProvider(); + var authenticationProvider = _authenticatedAuthenticationProvider != null && cachingProvider.IsCachedTokenPresent() ? + _authenticatedAuthenticationProvider : + new Authentication.AnonymousAuthenticationProvider(); var gitHubRequestAdapter = new HttpClientRequestAdapter(authenticationProvider, httpClient: _httpClient); var gitHubClient = new GitHubClient.GitHubClient(gitHubRequestAdapter); if(term.Contains('/')) { diff --git a/src/Kiota.Web/Pages/Generate.razor b/src/Kiota.Web/Pages/Generate.razor index 13e3a0ce51..77b47b2674 100644 --- a/src/Kiota.Web/Pages/Generate.razor +++ b/src/Kiota.Web/Pages/Generate.razor @@ -10,12 +10,14 @@ @using System.Text.Json.Serialization; @using System.Threading @using Microsoft.Fast.Components.FluentUI +@using Microsoft.Kiota.Abstractions.Authentication; @inject HttpClient Http @inject ILoggerFactory LoggerFactory @inject IJSRuntime JSRuntime @using System.Globalization @using Microsoft.Extensions.Localization @inject IStringLocalizer Loc +@inject IAuthenticationProvider GitHubAuthProvider @Loc["PageTitle"] @@ -184,7 +186,7 @@ SearchTerm = SearchTerm, Version = Version, }; - var searchResults = await new KiotaSearcher(logger, searchConfig, Http).SearchAsync(ComponentDetached); + var searchResults = await new KiotaSearcher(logger, searchConfig, Http, GitHubAuthProvider).SearchAsync(ComponentDetached); if(searchResults.Any()) DescriptionUrl = searchResults.Values.First().DescriptionUrl.ToString(); } diff --git a/src/Kiota.Web/Pages/Index.razor b/src/Kiota.Web/Pages/Index.razor index 000a0d341a..9cb33d8fef 100644 --- a/src/Kiota.Web/Pages/Index.razor +++ b/src/Kiota.Web/Pages/Index.razor @@ -10,9 +10,11 @@ @using Microsoft.Extensions.Localization @using Microsoft.Fast.Components.FluentUI @using Markdig +@using Microsoft.Kiota.Abstractions.Authentication; @inject IStringLocalizer Loc @inject NavigationManager navManager @inject HttpClient Http +@inject IAuthenticationProvider GitHubAuthProvider @Loc["PageTitle"] @@ -107,7 +109,7 @@ SearchTerm = SearchTerm, }; var logger = LoggerFactory.CreateLogger(); - SearchResults = await new KiotaSearcher(logger, searchConfig, Http).SearchAsync(ComponentDetached); + SearchResults = await new KiotaSearcher(logger, searchConfig, Http, GitHubAuthProvider).SearchAsync(ComponentDetached); IsLoading = false; if(AppInsights != null) await AppInsights.StopTrackEvent(searchTelemetryKey, new Dictionary { diff --git a/src/Kiota.Web/Pages/Show.razor b/src/Kiota.Web/Pages/Show.razor index eaf0b65adb..52951118e7 100644 --- a/src/Kiota.Web/Pages/Show.razor +++ b/src/Kiota.Web/Pages/Show.razor @@ -10,8 +10,10 @@ @inject NavigationManager navManager @using System.Globalization @using Microsoft.Extensions.Localization +@using Microsoft.Kiota.Abstractions.Authentication; @inject IStringLocalizer Loc @inject HttpClient Http +@inject IAuthenticationProvider GitHubAuthProvider @Loc["PageTitle"] @@ -72,7 +74,7 @@ SearchTerm = SearchTerm, Version = Version, }; - var searchResults = await new KiotaSearcher(logger, searchConfig, Http).SearchAsync(ComponentDetached); + var searchResults = await new KiotaSearcher(logger, searchConfig, Http, GitHubAuthProvider).SearchAsync(ComponentDetached); if(searchResults.Any()) DescriptionUrl = searchResults.Values.First().DescriptionUrl.ToString(); } @@ -113,7 +115,7 @@ Version = Version, }; var logger = LoggerFactory.CreateLogger(); - SearchResults = await new KiotaSearcher(logger, searchConfig, Http).SearchAsync(ComponentDetached); + SearchResults = await new KiotaSearcher(logger, searchConfig, Http, GitHubAuthProvider).SearchAsync(ComponentDetached); DescriptionUrl = SearchResults.First().Value.DescriptionUrl.ToString(); } if(AppInsights != null) diff --git a/src/Kiota.Web/Program.cs b/src/Kiota.Web/Program.cs index 36b0d82fff..93eb0abce2 100644 --- a/src/Kiota.Web/Program.cs +++ b/src/Kiota.Web/Program.cs @@ -5,6 +5,10 @@ using Microsoft.JSInterop; using Microsoft.Fast.Components.FluentUI; using BlazorApplicationInsights; +using Microsoft.Kiota.Abstractions.Authentication; +using Kiota.Builder.SearchProviders.GitHub.Authentication.Browser; +using Kiota.Builder.Configuration; +using Microsoft.AspNetCore.Components; var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add("#app"); @@ -16,6 +20,21 @@ builder.Services.AddLocalization(); builder.Services.AddFluentUIComponents(); builder.Services.AddBlazorApplicationInsights(); +var configObject = new KiotaConfiguration(); +builder.Configuration.Bind(configObject); +builder.Services.AddScoped(sp => new BrowserAuthenticationProvider( + configObject.Search.GitHub.AppId, + "repo", + new string[] { configObject.Search.GitHub.ApiBaseUrl.Host }, + sp.GetService(), + (uri, state) => { + sp.GetService()?.NavigateTo(uri.ToString()); + //TODO store state + }, + sp.GetService()?.CreateLogger(), + null, //TODO get authorization code from query string + new Uri($"{builder.HostEnvironment.BaseAddress}/auth") +)); var host = builder.Build(); diff --git a/src/kiota/Handlers/BaseKiotaCommandHandler.cs b/src/kiota/Handlers/BaseKiotaCommandHandler.cs index 9f20d13b7f..fb9903e064 100644 --- a/src/kiota/Handlers/BaseKiotaCommandHandler.cs +++ b/src/kiota/Handlers/BaseKiotaCommandHandler.cs @@ -9,8 +9,10 @@ using Kiota.Builder; using Kiota.Builder.Configuration; using Kiota.Builder.SearchProviders.GitHub.Authentication; +using Kiota.Builder.SearchProviders.GitHub.Authentication.DeviceCode; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; +using Microsoft.Kiota.Abstractions.Authentication; namespace kiota.Handlers; @@ -32,9 +34,16 @@ internal abstract class BaseKiotaCommandHandler : ICommandHandler .Build(); var configObject = new KiotaConfiguration(); configuration.Bind(configObject); - configObject.Search.GitHub.DeviceCodeCallback = DisplayGitHubDeviceCodeLoginMessage; return configObject; }); + private const string GitHubScope = "repo"; + protected IAuthenticationProvider GetAuthenticationProvider(ILogger logger) => + new DeviceCodeAuthenticationProvider(Configuration.Search.GitHub.AppId, + GitHubScope, + new List { Configuration.Search.GitHub.ApiBaseUrl.Host }, + httpClient, + DisplayGitHubDeviceCodeLoginMessage, + logger); public int Invoke(InvocationContext context) { return InvokeAsync(context).GetAwaiter().GetResult(); diff --git a/src/kiota/Handlers/KiotaDownloadCommandHandler.cs b/src/kiota/Handlers/KiotaDownloadCommandHandler.cs index 662e8b5174..68d22d9c90 100644 --- a/src/kiota/Handlers/KiotaDownloadCommandHandler.cs +++ b/src/kiota/Handlers/KiotaDownloadCommandHandler.cs @@ -46,7 +46,7 @@ public override async Task InvokeAsync(InvocationContext context) logger.LogTrace("configuration: {configuration}", JsonSerializer.Serialize(Configuration)); try { - var results = await new KiotaSearcher(logger, Configuration.Search, httpClient).SearchAsync(cancellationToken); + var results = await new KiotaSearcher(logger, Configuration.Search, httpClient, GetAuthenticationProvider(logger)).SearchAsync(cancellationToken); return await SaveResultsAsync(results, logger, cancellationToken); } catch (Exception ex) { #if DEBUG diff --git a/src/kiota/Handlers/KiotaGitHubLoginCommandHanlder.cs b/src/kiota/Handlers/KiotaGitHubLoginCommandHanlder.cs index 76ca4f0def..3225630f51 100644 --- a/src/kiota/Handlers/KiotaGitHubLoginCommandHanlder.cs +++ b/src/kiota/Handlers/KiotaGitHubLoginCommandHanlder.cs @@ -14,7 +14,7 @@ internal class KiotaGitHubLoginCommandHandler : BaseKiotaCommandHandler public override async Task InvokeAsync(InvocationContext context) { CancellationToken cancellationToken = (CancellationToken)context.BindingContext.GetService(typeof(CancellationToken)); - var (loggerFactory, logger) = GetLoggerAndFactory(context); + var (loggerFactory, logger) = GetLoggerAndFactory(context); using (loggerFactory) { try { return await LoginAsync(logger, cancellationToken); @@ -31,7 +31,7 @@ public override async Task InvokeAsync(InvocationContext context) } } private async Task LoginAsync(ILogger logger, CancellationToken cancellationToken) { - var authenticationProvider = new GitHubAuthenticationProvider(Configuration.Search.GitHub.AppId, + var authenticationProvider = new DeviceCodeAuthenticationProvider(Configuration.Search.GitHub.AppId, "repo", new List { "api.github.com"}, httpClient, diff --git a/src/kiota/Handlers/KiotaSeachBasedCommandHandler.cs b/src/kiota/Handlers/KiotaSeachBasedCommandHandler.cs index 29af87b971..6ebfde4e03 100644 --- a/src/kiota/Handlers/KiotaSeachBasedCommandHandler.cs +++ b/src/kiota/Handlers/KiotaSeachBasedCommandHandler.cs @@ -8,11 +8,11 @@ namespace kiota.Handlers; internal abstract class KiotaSearchBasedCommandHandler : BaseKiotaCommandHandler { - protected async Task<(string, int?)> GetDescriptionFromSearch(string openapi, string searchTerm, ILoggerFactory loggerFactory, ILogger parentLogger, CancellationToken cancellationToken) { + protected async Task<(string, int?)> GetDescriptionFromSearch(string openapi, string searchTerm, ILoggerFactory loggerFactory, ILogger logger, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(openapi) && !string.IsNullOrEmpty(searchTerm)) { - parentLogger.LogInformation("Searching for {searchTerm} in the OpenAPI description repository", searchTerm); - var searcher = new KiotaSearcher(loggerFactory.CreateLogger(), Configuration.Search, httpClient); + logger.LogInformation("Searching for {searchTerm} in the OpenAPI description repository", searchTerm); + var searcher = new KiotaSearcher(loggerFactory.CreateLogger(), Configuration.Search, httpClient, GetAuthenticationProvider(logger)); var results = await searcher.SearchAsync(cancellationToken); if (results.Count == 1) return (results.First().Value.DescriptionUrl.ToString(), null); diff --git a/src/kiota/Handlers/KiotaSearchCommandHandler.cs b/src/kiota/Handlers/KiotaSearchCommandHandler.cs index 15044c525b..e7a0057f86 100644 --- a/src/kiota/Handlers/KiotaSearchCommandHandler.cs +++ b/src/kiota/Handlers/KiotaSearchCommandHandler.cs @@ -37,7 +37,7 @@ public override async Task InvokeAsync(InvocationContext context) logger.LogTrace("configuration: {configuration}", JsonSerializer.Serialize(Configuration)); try { - var results = await new KiotaSearcher(logger, Configuration.Search, httpClient).SearchAsync(cancellationToken); + var results = await new KiotaSearcher(logger, Configuration.Search, httpClient, GetAuthenticationProvider(logger)).SearchAsync(cancellationToken); DisplayResults(results, logger); return 0; } catch (Exception ex) { diff --git a/tests/Kiota.Builder.Tests/KiotaSearcherTests.cs b/tests/Kiota.Builder.Tests/KiotaSearcherTests.cs index 11912b3824..87cf1431b8 100644 --- a/tests/Kiota.Builder.Tests/KiotaSearcherTests.cs +++ b/tests/Kiota.Builder.Tests/KiotaSearcherTests.cs @@ -14,24 +14,22 @@ public class KiotaSearcherTests : IDisposable { private readonly HttpClient httpClient = new(); [Fact] public void DefensivePrograming() { - Assert.Throws(() => new KiotaSearcher(null, new SearchConfiguration(), httpClient)); - Assert.Throws(() => new KiotaSearcher(new Mock>().Object, null, httpClient)); - Assert.Throws(() => new KiotaSearcher(new Mock>().Object, new SearchConfiguration(), null)); - Assert.Throws(() => new GitHubSearchProvider(httpClient, new Mock>().Object, false, null)); - Assert.Throws(() => new GitHubSearchProvider(httpClient, null, false, new GitHubConfiguration())); - Assert.Throws(() => new GitHubSearchProvider(null, new Mock>().Object, false, new GitHubConfiguration())); - Assert.ThrowsAsync(() => new GitHubSearchProvider(httpClient, new Mock>().Object, false, new GitHubConfiguration()).SearchAsync(null, null, CancellationToken.None)); + Assert.Throws(() => new KiotaSearcher(null, new SearchConfiguration(), httpClient, null)); + Assert.Throws(() => new KiotaSearcher(new Mock>().Object, null, httpClient, null)); + Assert.Throws(() => new KiotaSearcher(new Mock>().Object, new SearchConfiguration(), null, null)); + Assert.Throws(() => new GitHubSearchProvider(httpClient, new Mock>().Object, false, null, null)); + Assert.Throws(() => new GitHubSearchProvider(httpClient, null, false, new GitHubConfiguration(), null)); + Assert.Throws(() => new GitHubSearchProvider(null, new Mock>().Object, false, new GitHubConfiguration(), null)); + Assert.ThrowsAsync(() => new GitHubSearchProvider(httpClient, new Mock>().Object, false, new GitHubConfiguration(), null).SearchAsync(null, null, CancellationToken.None)); } private static SearchConfiguration searchConfigurationFactory => new(){ - GitHub = new() { - DeviceCodeCallback = (code, uri) => {}, - } + GitHub = new() {} }; [Fact] public async Task GetsMicrosoftGraphBothVersions() { var searchConfiguration = searchConfigurationFactory; searchConfiguration.SearchTerm = "github::microsoftgraph/msgraph-metadata"; - var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient); + var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient, null); var results = await searcher.SearchAsync(new CancellationToken()); Assert.Equal(2, results.Count); } @@ -39,7 +37,7 @@ public async Task GetsMicrosoftGraphBothVersions() { public async Task GetsMicrosoftGraph() { var searchConfiguration = searchConfigurationFactory; searchConfiguration.SearchTerm = "github::microsoftgraph/msgraph-metadata/graph.microsoft.com/v1.0"; - var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient); + var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient, null); var results = await searcher.SearchAsync(new CancellationToken()); Assert.Single(results); Assert.Equal("https://raw.githubusercontent.com/microsoftgraph/msgraph-metadata/master/openapi/v1.0/openapi.yaml", results.First().Value.DescriptionUrl.ToString()); @@ -48,14 +46,14 @@ public async Task GetsMicrosoftGraph() { public async Task GetsMicrosoftGraphBeta() { var searchConfiguration = searchConfigurationFactory; searchConfiguration.SearchTerm = "github::microsoftgraph/msgraph-metadata/graph.microsoft.com/beta"; - var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient); + var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient, null); var results = await searcher.SearchAsync(new CancellationToken()); Assert.Single(results); Assert.Equal("https://raw.githubusercontent.com/microsoftgraph/msgraph-metadata/master/openapi/beta/openapi.yaml", results.First().Value.DescriptionUrl.ToString()); } [Fact] public async Task DoesntFailOnEmptyTerm() { - var searcher = new KiotaSearcher(new Mock>().Object, searchConfigurationFactory, httpClient); + var searcher = new KiotaSearcher(new Mock>().Object, searchConfigurationFactory, httpClient, null); var results = await searcher.SearchAsync(new CancellationToken()); Assert.Empty(results); } @@ -63,7 +61,7 @@ public async Task DoesntFailOnEmptyTerm() { public async Task GetsGithubFromApisGuru() { var searchConfiguration = searchConfigurationFactory; searchConfiguration.SearchTerm = "github"; - var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient); + var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient, null); var results = await searcher.SearchAsync(new CancellationToken()); Assert.NotEmpty(results); } @@ -71,7 +69,7 @@ public async Task GetsGithubFromApisGuru() { public async Task GetsGithubFromApisGuruWithExactMatch() { var searchConfiguration = searchConfigurationFactory; searchConfiguration.SearchTerm = "apisguru::github.com:api.github.com"; - var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient); + var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient, null); var results = await searcher.SearchAsync(new CancellationToken()); Assert.Single(results); } From b5e8582875ed90dfca69289ff6c6c01c76a35c5f Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Wed, 23 Nov 2022 15:44:30 -0500 Subject: [PATCH 07/42] - adds session storage integration for state Signed-off-by: Vincent Biret --- .../Browser/AccessTokenProvider.cs | 15 ++++++------- .../Browser/BrowserAuthenticationProvider.cs | 6 ++++-- src/Kiota.Web/Kiota.Web.csproj | 1 + src/Kiota.Web/Program.cs | 21 +++++++++++++++---- 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/Browser/AccessTokenProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/Browser/AccessTokenProvider.cs index 2b142d649a..6c67468649 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/Browser/AccessTokenProvider.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/Browser/AccessTokenProvider.cs @@ -15,8 +15,8 @@ public class AccessTokenProvider : IAccessTokenProvider public required string Scope { get; init; } public required Uri RedirectUri { get; init; } public required HttpClient HttpClient {get; init;} - public required Action RedirectCallback { get; init; } - public string AuthorizationCode { get; init; } + public required Func RedirectCallback { get; init; } + public required Func> GetAccessCodeCallback { get; init; } internal string BaseLoginUrl { get; init; } = "https://github.com/login"; public Task GetAuthorizationTokenAsync(Uri uri, Dictionary additionalAuthenticationContext = null, CancellationToken cancellationToken = default) { @@ -28,12 +28,13 @@ public Task GetAuthorizationTokenAsync(Uri uri, Dictionary GetAuthorizationTokenInternalAsync(CancellationToken cancellationToken) { - if(string.IsNullOrEmpty(AuthorizationCode)) { + var authorizationCode = await GetAccessCodeCallback(cancellationToken); + if(string.IsNullOrEmpty(authorizationCode)) { var state = Guid.NewGuid(); - RedirectCallback(GetAuthorizeUrl(state), state.ToString()); + await RedirectCallback(GetAuthorizeUrl(state), state.ToString(), cancellationToken); return string.Empty; } else { - var tokenResponse = await GetTokenAsync(cancellationToken); + var tokenResponse = await GetTokenAsync(authorizationCode, cancellationToken); return tokenResponse.AccessToken; } } @@ -41,12 +42,12 @@ private Uri GetAuthorizeUrl(Guid state) { var authorizeUrl = $"{BaseLoginUrl}/oauth/authorize?client_id={ClientId}&scope={Scope}&redirect_uri={RedirectUri}&state={state}"; return new Uri(authorizeUrl); } - private async Task GetTokenAsync(CancellationToken cancellationToken) + private async Task GetTokenAsync(string authorizationCode, CancellationToken cancellationToken) { using var tokenRequest = new HttpRequestMessage(HttpMethod.Post, $"{BaseLoginUrl}/oauth/access_token") { Content = new FormUrlEncodedContent(new Dictionary { { "client_id", ClientId }, - { "code", AuthorizationCode }, //TODO missing secret? what about SPA? + { "code", authorizationCode }, //TODO missing secret? what about SPA? // { "grant_type", "urn:ietf:params:oauth:grant-type:device_code" } }) }; diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/Browser/BrowserAuthenticationProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/Browser/BrowserAuthenticationProvider.cs index 17b632ca2f..523f2e7c22 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/Browser/BrowserAuthenticationProvider.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/Browser/BrowserAuthenticationProvider.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Kiota.Abstractions.Authentication; @@ -8,14 +10,14 @@ namespace Kiota.Builder.SearchProviders.GitHub.Authentication.Browser; public class BrowserAuthenticationProvider : BaseAuthenticationProvider { - public BrowserAuthenticationProvider(string clientId, string scope, IEnumerable validHosts, HttpClient httpClient, Action redirectCallback, ILogger logger, string authorizationCode, Uri redirectUri) : + public BrowserAuthenticationProvider(string clientId, string scope, IEnumerable validHosts, HttpClient httpClient, Func redirectCallback, Func> getAccessCodeCallback, ILogger logger, Uri redirectUri) : base(clientId, scope, validHosts, logger, (clientId, scope, validHosts) => new AccessTokenProvider { ClientId = clientId, HttpClient = httpClient, RedirectCallback = redirectCallback, + GetAccessCodeCallback = getAccessCodeCallback, Scope = scope, AllowedHostsValidator = new AllowedHostsValidator(validHosts), - AuthorizationCode = authorizationCode, RedirectUri = redirectUri, }) { diff --git a/src/Kiota.Web/Kiota.Web.csproj b/src/Kiota.Web/Kiota.Web.csproj index 28392b5620..8169276952 100644 --- a/src/Kiota.Web/Kiota.Web.csproj +++ b/src/Kiota.Web/Kiota.Web.csproj @@ -18,6 +18,7 @@ + diff --git a/src/Kiota.Web/Program.cs b/src/Kiota.Web/Program.cs index 93eb0abce2..a44df9af0d 100644 --- a/src/Kiota.Web/Program.cs +++ b/src/Kiota.Web/Program.cs @@ -9,6 +9,7 @@ using Kiota.Builder.SearchProviders.GitHub.Authentication.Browser; using Kiota.Builder.Configuration; using Microsoft.AspNetCore.Components; +using Blazored.SessionStorage; var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add("#app"); @@ -16,23 +17,35 @@ builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); - +var gitHubStateKey = "github-authentication-state"; builder.Services.AddLocalization(); builder.Services.AddFluentUIComponents(); builder.Services.AddBlazorApplicationInsights(); var configObject = new KiotaConfiguration(); builder.Configuration.Bind(configObject); +builder.Services.AddBlazoredSessionStorage(); builder.Services.AddScoped(sp => new BrowserAuthenticationProvider( configObject.Search.GitHub.AppId, "repo", new string[] { configObject.Search.GitHub.ApiBaseUrl.Host }, sp.GetService(), - (uri, state) => { + async (uri, state, c) => { sp.GetService()?.NavigateTo(uri.ToString()); - //TODO store state + var sessionStorage = sp.GetService(); + if(sessionStorage != null) + await sessionStorage.SetItemAsync(gitHubStateKey, state, c).ConfigureAwait(false); }, + async (c) => { + var sessionStorage = sp.GetService(); + if(sessionStorage != null) { + var stateValue = await sessionStorage.GetItemAsync(gitHubStateKey).ConfigureAwait(false); + //TODO compare state value + //TODO get authorization code from query string + await sessionStorage.RemoveItemAsync(gitHubStateKey, c).ConfigureAwait(false); + } + return string.Empty; + }, sp.GetService()?.CreateLogger(), - null, //TODO get authorization code from query string new Uri($"{builder.HostEnvironment.BaseAddress}/auth") )); From b1c7ae16b2acefb07d6388c21f165b3f1259e0e4 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Wed, 23 Nov 2022 15:57:09 -0500 Subject: [PATCH 08/42] - adds query parameters read Signed-off-by: Vincent Biret --- src/Kiota.Web/Kiota.Web.csproj | 1 + src/Kiota.Web/Program.cs | 59 ++++++++++++++++++++-------------- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/src/Kiota.Web/Kiota.Web.csproj b/src/Kiota.Web/Kiota.Web.csproj index 8169276952..f4ca95fc7f 100644 --- a/src/Kiota.Web/Kiota.Web.csproj +++ b/src/Kiota.Web/Kiota.Web.csproj @@ -22,6 +22,7 @@ + diff --git a/src/Kiota.Web/Program.cs b/src/Kiota.Web/Program.cs index a44df9af0d..41eb00ecb4 100644 --- a/src/Kiota.Web/Program.cs +++ b/src/Kiota.Web/Program.cs @@ -10,6 +10,7 @@ using Kiota.Builder.Configuration; using Microsoft.AspNetCore.Components; using Blazored.SessionStorage; +using Microsoft.AspNetCore.WebUtilities; var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add("#app"); @@ -24,30 +25,40 @@ var configObject = new KiotaConfiguration(); builder.Configuration.Bind(configObject); builder.Services.AddBlazoredSessionStorage(); -builder.Services.AddScoped(sp => new BrowserAuthenticationProvider( - configObject.Search.GitHub.AppId, - "repo", - new string[] { configObject.Search.GitHub.ApiBaseUrl.Host }, - sp.GetService(), - async (uri, state, c) => { - sp.GetService()?.NavigateTo(uri.ToString()); - var sessionStorage = sp.GetService(); - if(sessionStorage != null) - await sessionStorage.SetItemAsync(gitHubStateKey, state, c).ConfigureAwait(false); - }, - async (c) => { - var sessionStorage = sp.GetService(); - if(sessionStorage != null) { - var stateValue = await sessionStorage.GetItemAsync(gitHubStateKey).ConfigureAwait(false); - //TODO compare state value - //TODO get authorization code from query string - await sessionStorage.RemoveItemAsync(gitHubStateKey, c).ConfigureAwait(false); - } - return string.Empty; - }, - sp.GetService()?.CreateLogger(), - new Uri($"{builder.HostEnvironment.BaseAddress}/auth") -)); +builder.Services.AddScoped(sp => { + var navManager = sp.GetService(); + return new BrowserAuthenticationProvider( + configObject.Search.GitHub.AppId, + "repo", + new string[] { configObject.Search.GitHub.ApiBaseUrl.Host }, + sp.GetService(), + async (uri, state, c) => { + navManager?.NavigateTo(uri.ToString()); + var sessionStorage = sp.GetService(); + if(sessionStorage != null) + await sessionStorage.SetItemAsync(gitHubStateKey, state, c).ConfigureAwait(false); + }, + async (c) => { + var sessionStorage = sp.GetService(); + if(sessionStorage != null) { + var stateValue = await sessionStorage.GetItemAsync(gitHubStateKey).ConfigureAwait(false); + if(navManager != null) { + var uri = navManager.ToAbsoluteUri(navManager.Uri); + var queryStrings = QueryHelpers.ParseQuery(uri.Query); + if(queryStrings.TryGetValue("state", out var state) && + stateValue.Equals(state, StringComparison.OrdinalIgnoreCase) && + queryStrings.TryGetValue("code", out var code)) { + return code; + } + } + await sessionStorage.RemoveItemAsync(gitHubStateKey, c).ConfigureAwait(false); + } + return string.Empty; + }, + sp.GetService()?.CreateLogger(), + new Uri($"{builder.HostEnvironment.BaseAddress}/auth") + ); +}); var host = builder.Build(); From a4c3c383f14a4e57f01f88f68da716767b5f8bc4 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Thu, 24 Nov 2022 08:48:32 -0500 Subject: [PATCH 09/42] - moves authentication providers to their target environment Signed-off-by: Vincent Biret --- .../GitHub/Autentication/AccessCodeResponse.cs | 2 +- .../GitHub/GitHubSearchProvider.cs | 1 - .../GitHub}/AccessTokenProvider.cs | 18 +++++++----------- .../GitHub}/BrowserAuthenticationProvider.cs | 9 ++------- src/Kiota.Web/Program.cs | 13 ++++++------- src/Kiota.Web/README.md | 11 +++++++++++ .../GitHub}/AcessTokenProvider.cs | 3 ++- .../DeviceCodeAuthenticationProvider.cs | 3 ++- .../GitHub}/DeviceCodeResponse.cs | 2 +- src/kiota/Handlers/BaseKiotaCommandHandler.cs | 2 +- .../Handlers/KiotaGitHubLoginCommandHanlder.cs | 2 +- 11 files changed, 34 insertions(+), 32 deletions(-) rename src/{Kiota.Builder/SearchProviders/GitHub/Autentication/Browser => Kiota.Web/Authentication/GitHub}/AccessTokenProvider.cs (81%) rename src/{Kiota.Builder/SearchProviders/GitHub/Autentication/Browser => Kiota.Web/Authentication/GitHub}/BrowserAuthenticationProvider.cs (82%) create mode 100644 src/Kiota.Web/README.md rename src/{Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode => kiota/Authentication/GitHub}/AcessTokenProvider.cs (97%) rename src/{Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode => kiota/Authentication/GitHub}/DeviceCodeAuthenticationProvider.cs (89%) rename src/{Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode => kiota/Authentication/GitHub}/DeviceCodeResponse.cs (86%) diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/AccessCodeResponse.cs b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/AccessCodeResponse.cs index b0b91cc00e..c68cd1095c 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/AccessCodeResponse.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/AccessCodeResponse.cs @@ -3,7 +3,7 @@ namespace Kiota.Builder.SearchProviders.GitHub.Authentication; -internal class AccessCodeResponse { +public class AccessCodeResponse { [JsonPropertyName("access_token")] public string AccessToken { get; set; } [JsonPropertyName("token_type")] diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs index 04c1e1ff56..c8a330a630 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs @@ -10,7 +10,6 @@ using Kiota.Builder.Configuration; using Kiota.Builder.Extensions; using Kiota.Builder.SearchProviders.GitHub.Authentication; -using Kiota.Builder.SearchProviders.GitHub.Authentication.DeviceCode; using Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models; using Kiota.Builder.SearchProviders.GitHub.Index; using Microsoft.Extensions.Logging; diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/Browser/AccessTokenProvider.cs b/src/Kiota.Web/Authentication/GitHub/AccessTokenProvider.cs similarity index 81% rename from src/Kiota.Builder/SearchProviders/GitHub/Autentication/Browser/AccessTokenProvider.cs rename to src/Kiota.Web/Authentication/GitHub/AccessTokenProvider.cs index 6c67468649..684367f043 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/Browser/AccessTokenProvider.cs +++ b/src/Kiota.Web/Authentication/GitHub/AccessTokenProvider.cs @@ -1,13 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; using System.Net.Http.Headers; using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; +using Kiota.Builder.SearchProviders.GitHub.Authentication; using Microsoft.Kiota.Abstractions.Authentication; -namespace Kiota.Builder.SearchProviders.GitHub.Authentication.Browser; +namespace Kiota.Web.Authentication.GitHub; public class AccessTokenProvider : IAccessTokenProvider { public AllowedHostsValidator AllowedHostsValidator {get; set;} = new(); @@ -18,7 +14,7 @@ public class AccessTokenProvider : IAccessTokenProvider public required Func RedirectCallback { get; init; } public required Func> GetAccessCodeCallback { get; init; } internal string BaseLoginUrl { get; init; } = "https://github.com/login"; - public Task GetAuthorizationTokenAsync(Uri uri, Dictionary additionalAuthenticationContext = null, CancellationToken cancellationToken = default) + public Task GetAuthorizationTokenAsync(Uri uri, Dictionary? additionalAuthenticationContext = null, CancellationToken cancellationToken = default) { if(!AllowedHostsValidator.IsUrlHostValid(uri)) return Task.FromResult(string.Empty); @@ -35,14 +31,14 @@ private async Task GetAuthorizationTokenInternalAsync(CancellationToken return string.Empty; } else { var tokenResponse = await GetTokenAsync(authorizationCode, cancellationToken); - return tokenResponse.AccessToken; + return tokenResponse?.AccessToken ?? string.Empty; } } private Uri GetAuthorizeUrl(Guid state) { var authorizeUrl = $"{BaseLoginUrl}/oauth/authorize?client_id={ClientId}&scope={Scope}&redirect_uri={RedirectUri}&state={state}"; return new Uri(authorizeUrl); } - private async Task GetTokenAsync(string authorizationCode, CancellationToken cancellationToken) + private async Task GetTokenAsync(string authorizationCode, CancellationToken cancellationToken) { using var tokenRequest = new HttpRequestMessage(HttpMethod.Post, $"{BaseLoginUrl}/oauth/access_token") { Content = new FormUrlEncodedContent(new Dictionary { @@ -56,9 +52,9 @@ private async Task GetTokenAsync(string authorizationCode, C tokenResponse.EnsureSuccessStatusCode(); var tokenContent = await tokenResponse.Content.ReadAsStringAsync(cancellationToken); var result = JsonSerializer.Deserialize(tokenContent); - if ("authorization_pending".Equals(result.Error, StringComparison.OrdinalIgnoreCase)) + if ("authorization_pending".Equals(result?.Error, StringComparison.OrdinalIgnoreCase)) return null; - else if (!string.IsNullOrEmpty(result.Error)) + else if (!string.IsNullOrEmpty(result?.Error)) throw new InvalidOperationException($"Error while getting token: {result.Error} - {result.ErrorDescription}"); else return result; diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/Browser/BrowserAuthenticationProvider.cs b/src/Kiota.Web/Authentication/GitHub/BrowserAuthenticationProvider.cs similarity index 82% rename from src/Kiota.Builder/SearchProviders/GitHub/Autentication/Browser/BrowserAuthenticationProvider.cs rename to src/Kiota.Web/Authentication/GitHub/BrowserAuthenticationProvider.cs index 523f2e7c22..f94a628727 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/Browser/BrowserAuthenticationProvider.cs +++ b/src/Kiota.Web/Authentication/GitHub/BrowserAuthenticationProvider.cs @@ -1,12 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; +using Kiota.Builder.SearchProviders.GitHub.Authentication; using Microsoft.Kiota.Abstractions.Authentication; -namespace Kiota.Builder.SearchProviders.GitHub.Authentication.Browser; +namespace Kiota.Web.Authentication.GitHub; public class BrowserAuthenticationProvider : BaseAuthenticationProvider { diff --git a/src/Kiota.Web/Program.cs b/src/Kiota.Web/Program.cs index 41eb00ecb4..f495188425 100644 --- a/src/Kiota.Web/Program.cs +++ b/src/Kiota.Web/Program.cs @@ -6,11 +6,11 @@ using Microsoft.Fast.Components.FluentUI; using BlazorApplicationInsights; using Microsoft.Kiota.Abstractions.Authentication; -using Kiota.Builder.SearchProviders.GitHub.Authentication.Browser; using Kiota.Builder.Configuration; using Microsoft.AspNetCore.Components; using Blazored.SessionStorage; using Microsoft.AspNetCore.WebUtilities; +using Kiota.Web.Authentication.GitHub; var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add("#app"); @@ -25,13 +25,13 @@ var configObject = new KiotaConfiguration(); builder.Configuration.Bind(configObject); builder.Services.AddBlazoredSessionStorage(); -builder.Services.AddScoped(sp => { +builder.Services.AddScoped(sp => { // TODO move to extension method var navManager = sp.GetService(); return new BrowserAuthenticationProvider( configObject.Search.GitHub.AppId, "repo", new string[] { configObject.Search.GitHub.ApiBaseUrl.Host }, - sp.GetService(), + sp.GetService()!, async (uri, state, c) => { navManager?.NavigateTo(uri.ToString()); var sessionStorage = sp.GetService(); @@ -47,15 +47,14 @@ var queryStrings = QueryHelpers.ParseQuery(uri.Query); if(queryStrings.TryGetValue("state", out var state) && stateValue.Equals(state, StringComparison.OrdinalIgnoreCase) && - queryStrings.TryGetValue("code", out var code)) { - return code; - } + queryStrings.TryGetValue("code", out var code) && code.FirstOrDefault() is string codeValue) + return codeValue; } await sessionStorage.RemoveItemAsync(gitHubStateKey, c).ConfigureAwait(false); } return string.Empty; }, - sp.GetService()?.CreateLogger(), + sp.GetService()?.CreateLogger()!, new Uri($"{builder.HostEnvironment.BaseAddress}/auth") ); }); diff --git a/src/Kiota.Web/README.md b/src/Kiota.Web/README.md new file mode 100644 index 0000000000..8fe34370db --- /dev/null +++ b/src/Kiota.Web/README.md @@ -0,0 +1,11 @@ +# Kiota Web + +## Debug tips + +```powershell +dotnet watch run +chrome --remote-debugging-port=9222 --user-data-dir="$Env:TEMP\blazor-chrome-debug" https://localhost:7230/ +``` + +Once on the page: +alt + shit + D diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/AcessTokenProvider.cs b/src/kiota/Authentication/GitHub/AcessTokenProvider.cs similarity index 97% rename from src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/AcessTokenProvider.cs rename to src/kiota/Authentication/GitHub/AcessTokenProvider.cs index 0c5124c1cc..dc7527b95e 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/AcessTokenProvider.cs +++ b/src/kiota/Authentication/GitHub/AcessTokenProvider.cs @@ -5,9 +5,10 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Kiota.Builder.SearchProviders.GitHub.Authentication; using Microsoft.Kiota.Abstractions.Authentication; -namespace Kiota.Builder.SearchProviders.GitHub.Authentication.DeviceCode; +namespace kiota.Authentication.GitHub.DeviceCode; public class AccessTokenProvider : IAccessTokenProvider { public required Action MessageCallback { get; init; } diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/DeviceCodeAuthenticationProvider.cs b/src/kiota/Authentication/GitHub/DeviceCodeAuthenticationProvider.cs similarity index 89% rename from src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/DeviceCodeAuthenticationProvider.cs rename to src/kiota/Authentication/GitHub/DeviceCodeAuthenticationProvider.cs index 133259a8a8..6e546dc656 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/DeviceCodeAuthenticationProvider.cs +++ b/src/kiota/Authentication/GitHub/DeviceCodeAuthenticationProvider.cs @@ -2,10 +2,11 @@ using System; using System.Collections.Generic; using System.Net.Http; +using Kiota.Builder.SearchProviders.GitHub.Authentication; using Microsoft.Extensions.Logging; using Microsoft.Kiota.Abstractions.Authentication; -namespace Kiota.Builder.SearchProviders.GitHub.Authentication.DeviceCode; +namespace kiota.Authentication.GitHub.DeviceCode; public class DeviceCodeAuthenticationProvider : BaseAuthenticationProvider { diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/DeviceCodeResponse.cs b/src/kiota/Authentication/GitHub/DeviceCodeResponse.cs similarity index 86% rename from src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/DeviceCodeResponse.cs rename to src/kiota/Authentication/GitHub/DeviceCodeResponse.cs index caa1978be9..bb1f013ee3 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/DeviceCode/DeviceCodeResponse.cs +++ b/src/kiota/Authentication/GitHub/DeviceCodeResponse.cs @@ -1,7 +1,7 @@ using System; using System.Text.Json.Serialization; -namespace Kiota.Builder.SearchProviders.GitHub.Authentication.DeviceCode; +namespace kiota.Authentication.GitHub.DeviceCode; internal class GitHubDeviceCodeResponse { [JsonPropertyName("device_code")] diff --git a/src/kiota/Handlers/BaseKiotaCommandHandler.cs b/src/kiota/Handlers/BaseKiotaCommandHandler.cs index fb9903e064..41abffb68d 100644 --- a/src/kiota/Handlers/BaseKiotaCommandHandler.cs +++ b/src/kiota/Handlers/BaseKiotaCommandHandler.cs @@ -6,10 +6,10 @@ using System.Linq; using System.Net.Http; using System.Threading.Tasks; +using kiota.Authentication.GitHub.DeviceCode; using Kiota.Builder; using Kiota.Builder.Configuration; using Kiota.Builder.SearchProviders.GitHub.Authentication; -using Kiota.Builder.SearchProviders.GitHub.Authentication.DeviceCode; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Microsoft.Kiota.Abstractions.Authentication; diff --git a/src/kiota/Handlers/KiotaGitHubLoginCommandHanlder.cs b/src/kiota/Handlers/KiotaGitHubLoginCommandHanlder.cs index 3225630f51..e9f1623750 100644 --- a/src/kiota/Handlers/KiotaGitHubLoginCommandHanlder.cs +++ b/src/kiota/Handlers/KiotaGitHubLoginCommandHanlder.cs @@ -3,7 +3,7 @@ using System.CommandLine.Invocation; using System.Threading; using System.Threading.Tasks; -using Kiota.Builder.SearchProviders.GitHub.Authentication.DeviceCode; +using kiota.Authentication.GitHub.DeviceCode; using Microsoft.Extensions.Logging; using Microsoft.Kiota.Abstractions; From 60783cd1add3b6ad201bbfd39c9d76afed541da3 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Thu, 24 Nov 2022 09:23:02 -0500 Subject: [PATCH 10/42] - adds sign in button Signed-off-by: Vincent Biret --- src/Kiota.Web/Kiota.Web.csproj | 4 ++ src/Kiota.Web/Program.cs | 12 +++- src/Kiota.Web/Shared/GitHubSignIn.fr.resx | 64 ++++++++++++++++++++ src/Kiota.Web/Shared/GitHubSignIn.razor | 28 +++++++++ src/Kiota.Web/Shared/GitHubSignIn.razor.css | 0 src/Kiota.Web/Shared/GitHubSignIn.resx | 67 +++++++++++++++++++++ src/Kiota.Web/Shared/MainLayout.razor | 3 + 7 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 src/Kiota.Web/Shared/GitHubSignIn.fr.resx create mode 100644 src/Kiota.Web/Shared/GitHubSignIn.razor create mode 100644 src/Kiota.Web/Shared/GitHubSignIn.razor.css create mode 100644 src/Kiota.Web/Shared/GitHubSignIn.resx diff --git a/src/Kiota.Web/Kiota.Web.csproj b/src/Kiota.Web/Kiota.Web.csproj index f4ca95fc7f..a90941f025 100644 --- a/src/Kiota.Web/Kiota.Web.csproj +++ b/src/Kiota.Web/Kiota.Web.csproj @@ -53,6 +53,10 @@ ResXFileCodeGenerator NavMenu.Designer.cs + + ResXFileCodeGenerator + GitHubSignIn.Designer.cs + diff --git a/src/Kiota.Web/Program.cs b/src/Kiota.Web/Program.cs index f495188425..e647d2f025 100644 --- a/src/Kiota.Web/Program.cs +++ b/src/Kiota.Web/Program.cs @@ -5,12 +5,13 @@ using Microsoft.JSInterop; using Microsoft.Fast.Components.FluentUI; using BlazorApplicationInsights; -using Microsoft.Kiota.Abstractions.Authentication; using Kiota.Builder.Configuration; using Microsoft.AspNetCore.Components; using Blazored.SessionStorage; using Microsoft.AspNetCore.WebUtilities; using Kiota.Web.Authentication.GitHub; +using Kiota.Builder.SearchProviders.GitHub.Authentication; +using Microsoft.Kiota.Abstractions.Authentication; var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add("#app"); @@ -24,7 +25,16 @@ builder.Services.AddBlazorApplicationInsights(); var configObject = new KiotaConfiguration(); builder.Configuration.Bind(configObject); +builder.Services.AddSingleton(configObject); builder.Services.AddBlazoredSessionStorage(); +builder.Services.AddScoped(sp => { + return new TempFolderCachingAccessTokenProvider { + Logger = sp.GetService()?.CreateLogger()!, + ApiBaseUrl = configObject.Search.GitHub.ApiBaseUrl, + Concrete = null, + AppId = configObject.Search.GitHub.AppId, + }; +}); builder.Services.AddScoped(sp => { // TODO move to extension method var navManager = sp.GetService(); return new BrowserAuthenticationProvider( diff --git a/src/Kiota.Web/Shared/GitHubSignIn.fr.resx b/src/Kiota.Web/Shared/GitHubSignIn.fr.resx new file mode 100644 index 0000000000..cd1bd36009 --- /dev/null +++ b/src/Kiota.Web/Shared/GitHubSignIn.fr.resx @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Show + + \ No newline at end of file diff --git a/src/Kiota.Web/Shared/GitHubSignIn.razor b/src/Kiota.Web/Shared/GitHubSignIn.razor new file mode 100644 index 0000000000..4a06c87ef4 --- /dev/null +++ b/src/Kiota.Web/Shared/GitHubSignIn.razor @@ -0,0 +1,28 @@ +@using Microsoft.Kiota.Abstractions.Authentication +@using Kiota.Builder.Configuration +@using Kiota.Web.Authentication.GitHub +@using Kiota.Builder.SearchProviders.GitHub.Authentication +@using Microsoft.Fast.Components.FluentUI +@using Microsoft.Extensions.Localization +@inject IStringLocalizer Loc +@inject TempFolderCachingAccessTokenProvider CacheTokenProvider +@inject IAuthenticationProvider BrowserAuthenticationProvider +@inject KiotaConfiguration KiotaConfiguration +
+ @if(CacheTokenProvider.IsCachedTokenPresent()) { + @Loc["SignOut"] + } else { + @Loc["SignIn"] + } +
+ +@code { + private async Task SignIn() { + if(BrowserAuthenticationProvider is BrowserAuthenticationProvider authProvider) + await authProvider.AccessTokenProvider.GetAuthorizationTokenAsync(KiotaConfiguration.Search.GitHub.ApiBaseUrl, cancellationToken: ComponentDetached); + } + private void SignOut() { + CacheTokenProvider.Logout(); + } + +} \ No newline at end of file diff --git a/src/Kiota.Web/Shared/GitHubSignIn.razor.css b/src/Kiota.Web/Shared/GitHubSignIn.razor.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/Kiota.Web/Shared/GitHubSignIn.resx b/src/Kiota.Web/Shared/GitHubSignIn.resx new file mode 100644 index 0000000000..d30c5fc08c --- /dev/null +++ b/src/Kiota.Web/Shared/GitHubSignIn.resx @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Sign In with GitHub + + + Sign Out from GitHub + + \ No newline at end of file diff --git a/src/Kiota.Web/Shared/MainLayout.razor b/src/Kiota.Web/Shared/MainLayout.razor index 555a4db15a..b02b641704 100644 --- a/src/Kiota.Web/Shared/MainLayout.razor +++ b/src/Kiota.Web/Shared/MainLayout.razor @@ -6,6 +6,9 @@
+
+ +
@Body
From 252543dab786054169a2a5c28251b2034eadcfd3 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Thu, 24 Nov 2022 15:58:34 -0500 Subject: [PATCH 11/42] - moves the authentication configuration to its own extension method Signed-off-by: Vincent Biret --- .../GitHub/AccessTokenProvider.cs | 5 +- .../IServiceCollectionExtensions.cs | 58 +++++++++++++++++++ src/Kiota.Web/Program.cs | 49 +--------------- 3 files changed, 62 insertions(+), 50 deletions(-) create mode 100644 src/Kiota.Web/Extensions/IServiceCollectionExtensions.cs diff --git a/src/Kiota.Web/Authentication/GitHub/AccessTokenProvider.cs b/src/Kiota.Web/Authentication/GitHub/AccessTokenProvider.cs index 684367f043..b22e644acd 100644 --- a/src/Kiota.Web/Authentication/GitHub/AccessTokenProvider.cs +++ b/src/Kiota.Web/Authentication/GitHub/AccessTokenProvider.cs @@ -43,8 +43,9 @@ private Uri GetAuthorizeUrl(Guid state) { using var tokenRequest = new HttpRequestMessage(HttpMethod.Post, $"{BaseLoginUrl}/oauth/access_token") { Content = new FormUrlEncodedContent(new Dictionary { { "client_id", ClientId }, - { "code", authorizationCode }, //TODO missing secret? what about SPA? - // { "grant_type", "urn:ietf:params:oauth:grant-type:device_code" } + { "code", authorizationCode }, + // acquisition doesn't work because the endpoint doesn't support CORS or PKCE, and requires a secret + // we're leaving the code in place as we hope that GitHub will eventually support this and it'll be a matter of updating the parameters and calling AddBrowserCodeAuthentication }) }; tokenRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); diff --git a/src/Kiota.Web/Extensions/IServiceCollectionExtensions.cs b/src/Kiota.Web/Extensions/IServiceCollectionExtensions.cs new file mode 100644 index 0000000000..17728ca77f --- /dev/null +++ b/src/Kiota.Web/Extensions/IServiceCollectionExtensions.cs @@ -0,0 +1,58 @@ +using Blazored.SessionStorage; +using Kiota.Builder.Configuration; +using Kiota.Builder.SearchProviders.GitHub.Authentication; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.WebUtilities; +using Microsoft.Kiota.Abstractions.Authentication; + +namespace Kiota.Web.Authentication.GitHub; + +public static class IServiceCollectionExtensions { + private const string GitHubStateKey = "github-authentication-state"; + public static void AddBrowserCodeAuthentication(this IServiceCollection services, string baseAddress) { + services.AddBlazoredSessionStorage(); + services.AddScoped(sp => { + var configObject = sp.GetRequiredService(); + return new TempFolderCachingAccessTokenProvider { + Logger = sp.GetService()?.CreateLogger()!, + ApiBaseUrl = configObject.Search.GitHub.ApiBaseUrl, + Concrete = null, + AppId = configObject.Search.GitHub.AppId, + }; + }); + services.AddScoped(sp => { + var configObject = sp.GetRequiredService(); + var navManager = sp.GetRequiredService(); + return new BrowserAuthenticationProvider( + configObject.Search.GitHub.AppId, + "repo", + new string[] { configObject.Search.GitHub.ApiBaseUrl.Host }, + sp.GetService()!, + async (uri, state, c) => { + navManager.NavigateTo(uri.ToString()); + var sessionStorage = sp.GetService(); + if(sessionStorage != null) + await sessionStorage.SetItemAsync(GitHubStateKey, state, c).ConfigureAwait(false); + }, + async (c) => { + var sessionStorage = sp.GetService(); + if(sessionStorage != null) { + var stateValue = await sessionStorage.GetItemAsync(GitHubStateKey).ConfigureAwait(false); + if(navManager != null) { + var uri = navManager.ToAbsoluteUri(navManager.Uri); + var queryStrings = QueryHelpers.ParseQuery(uri.Query); + if(queryStrings.TryGetValue("state", out var state) && + stateValue.Equals(state, StringComparison.OrdinalIgnoreCase) && + queryStrings.TryGetValue("code", out var code) && code.FirstOrDefault() is string codeValue) + return codeValue; + } + await sessionStorage.RemoveItemAsync(GitHubStateKey, c).ConfigureAwait(false); + } + return string.Empty; + }, + sp.GetService()?.CreateLogger()!, + new Uri($"{baseAddress}GitHubAuth") + ); + }); + } +} diff --git a/src/Kiota.Web/Program.cs b/src/Kiota.Web/Program.cs index e647d2f025..b34a068ede 100644 --- a/src/Kiota.Web/Program.cs +++ b/src/Kiota.Web/Program.cs @@ -6,12 +6,7 @@ using Microsoft.Fast.Components.FluentUI; using BlazorApplicationInsights; using Kiota.Builder.Configuration; -using Microsoft.AspNetCore.Components; -using Blazored.SessionStorage; -using Microsoft.AspNetCore.WebUtilities; using Kiota.Web.Authentication.GitHub; -using Kiota.Builder.SearchProviders.GitHub.Authentication; -using Microsoft.Kiota.Abstractions.Authentication; var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add("#app"); @@ -19,55 +14,13 @@ builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); -var gitHubStateKey = "github-authentication-state"; builder.Services.AddLocalization(); builder.Services.AddFluentUIComponents(); builder.Services.AddBlazorApplicationInsights(); var configObject = new KiotaConfiguration(); builder.Configuration.Bind(configObject); builder.Services.AddSingleton(configObject); -builder.Services.AddBlazoredSessionStorage(); -builder.Services.AddScoped(sp => { - return new TempFolderCachingAccessTokenProvider { - Logger = sp.GetService()?.CreateLogger()!, - ApiBaseUrl = configObject.Search.GitHub.ApiBaseUrl, - Concrete = null, - AppId = configObject.Search.GitHub.AppId, - }; -}); -builder.Services.AddScoped(sp => { // TODO move to extension method - var navManager = sp.GetService(); - return new BrowserAuthenticationProvider( - configObject.Search.GitHub.AppId, - "repo", - new string[] { configObject.Search.GitHub.ApiBaseUrl.Host }, - sp.GetService()!, - async (uri, state, c) => { - navManager?.NavigateTo(uri.ToString()); - var sessionStorage = sp.GetService(); - if(sessionStorage != null) - await sessionStorage.SetItemAsync(gitHubStateKey, state, c).ConfigureAwait(false); - }, - async (c) => { - var sessionStorage = sp.GetService(); - if(sessionStorage != null) { - var stateValue = await sessionStorage.GetItemAsync(gitHubStateKey).ConfigureAwait(false); - if(navManager != null) { - var uri = navManager.ToAbsoluteUri(navManager.Uri); - var queryStrings = QueryHelpers.ParseQuery(uri.Query); - if(queryStrings.TryGetValue("state", out var state) && - stateValue.Equals(state, StringComparison.OrdinalIgnoreCase) && - queryStrings.TryGetValue("code", out var code) && code.FirstOrDefault() is string codeValue) - return codeValue; - } - await sessionStorage.RemoveItemAsync(gitHubStateKey, c).ConfigureAwait(false); - } - return string.Empty; - }, - sp.GetService()?.CreateLogger()!, - new Uri($"{builder.HostEnvironment.BaseAddress}/auth") - ); -}); +builder.Services.AddBrowserCodeAuthentication(builder.HostEnvironment.BaseAddress); var host = builder.Build(); From 5fc00fe8f86600d6be6edbeb6994f2f406aeedbe Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Thu, 24 Nov 2022 16:16:55 -0500 Subject: [PATCH 12/42] - adds a github icon for the sign in button Signed-off-by: Vincent Biret --- src/Kiota.Web/Shared/GitHubSignIn.razor | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Kiota.Web/Shared/GitHubSignIn.razor b/src/Kiota.Web/Shared/GitHubSignIn.razor index 4a06c87ef4..4c3523aa78 100644 --- a/src/Kiota.Web/Shared/GitHubSignIn.razor +++ b/src/Kiota.Web/Shared/GitHubSignIn.razor @@ -8,6 +8,8 @@ @inject TempFolderCachingAccessTokenProvider CacheTokenProvider @inject IAuthenticationProvider BrowserAuthenticationProvider @inject KiotaConfiguration KiotaConfiguration +
@if(CacheTokenProvider.IsCachedTokenPresent()) { @Loc["SignOut"] From 9fc702e14b369fc5bba8824700b6faebe2712bcb Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Thu, 24 Nov 2022 17:28:43 -0500 Subject: [PATCH 13/42] - adds a pat provider for github sign in Signed-off-by: Vincent Biret --- ...vider.cs => BrowserAccessTokenProvider.cs} | 2 +- .../GitHub/BrowserAuthenticationProvider.cs | 4 +- .../GitHub/PatAuthenticationProvider.cs | 14 ++++ .../Authentication/GitHub/PatProvider.cs | 16 ++++ .../GitHub/SessionStoragePatService.cs | 21 ++++++ .../IServiceCollectionExtensions.cs | 73 +++++++++++++------ src/Kiota.Web/Program.cs | 2 +- src/Kiota.Web/Shared/GitHubSignIn.razor | 23 ++++-- 8 files changed, 122 insertions(+), 33 deletions(-) rename src/Kiota.Web/Authentication/GitHub/{AccessTokenProvider.cs => BrowserAccessTokenProvider.cs} (98%) create mode 100644 src/Kiota.Web/Authentication/GitHub/PatAuthenticationProvider.cs create mode 100644 src/Kiota.Web/Authentication/GitHub/PatProvider.cs create mode 100644 src/Kiota.Web/Authentication/GitHub/SessionStoragePatService.cs diff --git a/src/Kiota.Web/Authentication/GitHub/AccessTokenProvider.cs b/src/Kiota.Web/Authentication/GitHub/BrowserAccessTokenProvider.cs similarity index 98% rename from src/Kiota.Web/Authentication/GitHub/AccessTokenProvider.cs rename to src/Kiota.Web/Authentication/GitHub/BrowserAccessTokenProvider.cs index b22e644acd..6c1eca1c9e 100644 --- a/src/Kiota.Web/Authentication/GitHub/AccessTokenProvider.cs +++ b/src/Kiota.Web/Authentication/GitHub/BrowserAccessTokenProvider.cs @@ -4,7 +4,7 @@ using Microsoft.Kiota.Abstractions.Authentication; namespace Kiota.Web.Authentication.GitHub; -public class AccessTokenProvider : IAccessTokenProvider +public class BrowserAccessTokenProvider : IAccessTokenProvider { public AllowedHostsValidator AllowedHostsValidator {get; set;} = new(); public required string ClientId { get; init; } diff --git a/src/Kiota.Web/Authentication/GitHub/BrowserAuthenticationProvider.cs b/src/Kiota.Web/Authentication/GitHub/BrowserAuthenticationProvider.cs index f94a628727..a5d66454ce 100644 --- a/src/Kiota.Web/Authentication/GitHub/BrowserAuthenticationProvider.cs +++ b/src/Kiota.Web/Authentication/GitHub/BrowserAuthenticationProvider.cs @@ -3,10 +3,10 @@ namespace Kiota.Web.Authentication.GitHub; -public class BrowserAuthenticationProvider : BaseAuthenticationProvider +public class BrowserAuthenticationProvider : BaseAuthenticationProvider { public BrowserAuthenticationProvider(string clientId, string scope, IEnumerable validHosts, HttpClient httpClient, Func redirectCallback, Func> getAccessCodeCallback, ILogger logger, Uri redirectUri) : - base(clientId, scope, validHosts, logger, (clientId, scope, validHosts) => new AccessTokenProvider { + base(clientId, scope, validHosts, logger, (clientId, scope, validHosts) => new BrowserAccessTokenProvider { ClientId = clientId, HttpClient = httpClient, RedirectCallback = redirectCallback, diff --git a/src/Kiota.Web/Authentication/GitHub/PatAuthenticationProvider.cs b/src/Kiota.Web/Authentication/GitHub/PatAuthenticationProvider.cs new file mode 100644 index 0000000000..217387e120 --- /dev/null +++ b/src/Kiota.Web/Authentication/GitHub/PatAuthenticationProvider.cs @@ -0,0 +1,14 @@ +using Kiota.Builder.SearchProviders.GitHub.Authentication; + +namespace Kiota.Web.Authentication.GitHub; +public class PatAuthenticationProvider : BaseAuthenticationProvider +{ + public PatAuthenticationProvider(string clientId, string scope, IEnumerable validHosts, ILogger logger, Func> GetPATFromStorageCallback) : + base(clientId, scope, validHosts, logger, (clientId, scope, validHosts) => new PatProvider { + GetPATFromStorageCallback = GetPATFromStorageCallback, + AllowedHostsValidator = new (validHosts), + }) + { + ArgumentNullException.ThrowIfNull(GetPATFromStorageCallback); + } +} diff --git a/src/Kiota.Web/Authentication/GitHub/PatProvider.cs b/src/Kiota.Web/Authentication/GitHub/PatProvider.cs new file mode 100644 index 0000000000..ceb4a4ecdc --- /dev/null +++ b/src/Kiota.Web/Authentication/GitHub/PatProvider.cs @@ -0,0 +1,16 @@ +using Microsoft.Kiota.Abstractions.Authentication; + +namespace Kiota.Web.Authentication.GitHub; + +public class PatProvider : IAccessTokenProvider +{ + public AllowedHostsValidator AllowedHostsValidator { get; set; } = new(); + public required Func> GetPATFromStorageCallback { get; init; } + + public Task GetAuthorizationTokenAsync(Uri uri, Dictionary? additionalAuthenticationContext = null, CancellationToken cancellationToken = default) + { + if("https".Equals(uri.Scheme, StringComparison.OrdinalIgnoreCase) && AllowedHostsValidator.IsUrlHostValid(uri)) + return GetPATFromStorageCallback(cancellationToken); + return Task.FromResult(string.Empty); + } +} diff --git a/src/Kiota.Web/Authentication/GitHub/SessionStoragePatService.cs b/src/Kiota.Web/Authentication/GitHub/SessionStoragePatService.cs new file mode 100644 index 0000000000..248950d174 --- /dev/null +++ b/src/Kiota.Web/Authentication/GitHub/SessionStoragePatService.cs @@ -0,0 +1,21 @@ + +using Blazored.SessionStorage; + +namespace Kiota.Web.Authentication.GitHub; + +public class SessionStoragePatService { + private const string PATKey = "github-pat"; + public required ISessionStorageService SessionStorageService { get; init; } + public async Task GetPatAsync(CancellationToken cancellationToken) { + return await SessionStorageService.GetItemAsync(PATKey, cancellationToken).ConfigureAwait(false); + } + public async Task SetPatAsync(string value, CancellationToken cancellationToken) { + await SessionStorageService.SetItemAsync(PATKey, value, cancellationToken).ConfigureAwait(false); + } + public async Task IsSignedInAsync(CancellationToken cancellationToken) { + return !string.IsNullOrEmpty(await GetPatAsync(cancellationToken).ConfigureAwait(false)); + } + public async Task SignOutAsync(CancellationToken cancellationToken) { + await SessionStorageService.RemoveItemAsync(PATKey, cancellationToken).ConfigureAwait(false); + } +} diff --git a/src/Kiota.Web/Extensions/IServiceCollectionExtensions.cs b/src/Kiota.Web/Extensions/IServiceCollectionExtensions.cs index 17728ca77f..33fefc853e 100644 --- a/src/Kiota.Web/Extensions/IServiceCollectionExtensions.cs +++ b/src/Kiota.Web/Extensions/IServiceCollectionExtensions.cs @@ -14,10 +14,10 @@ public static void AddBrowserCodeAuthentication(this IServiceCollection services services.AddScoped(sp => { var configObject = sp.GetRequiredService(); return new TempFolderCachingAccessTokenProvider { - Logger = sp.GetService()?.CreateLogger()!, - ApiBaseUrl = configObject.Search.GitHub.ApiBaseUrl, - Concrete = null, - AppId = configObject.Search.GitHub.AppId, + Logger = sp.GetRequiredService().CreateLogger()!, + ApiBaseUrl = configObject.Search.GitHub.ApiBaseUrl, + Concrete = null, + AppId = configObject.Search.GitHub.AppId, }; }); services.AddScoped(sp => { @@ -27,32 +27,61 @@ public static void AddBrowserCodeAuthentication(this IServiceCollection services configObject.Search.GitHub.AppId, "repo", new string[] { configObject.Search.GitHub.ApiBaseUrl.Host }, - sp.GetService()!, + sp.GetRequiredService(), async (uri, state, c) => { + var sessionStorage = sp.GetRequiredService(); + await sessionStorage.SetItemAsync(GitHubStateKey, state, c).ConfigureAwait(false); navManager.NavigateTo(uri.ToString()); - var sessionStorage = sp.GetService(); - if(sessionStorage != null) - await sessionStorage.SetItemAsync(GitHubStateKey, state, c).ConfigureAwait(false); }, async (c) => { - var sessionStorage = sp.GetService(); - if(sessionStorage != null) { - var stateValue = await sessionStorage.GetItemAsync(GitHubStateKey).ConfigureAwait(false); - if(navManager != null) { - var uri = navManager.ToAbsoluteUri(navManager.Uri); - var queryStrings = QueryHelpers.ParseQuery(uri.Query); - if(queryStrings.TryGetValue("state", out var state) && - stateValue.Equals(state, StringComparison.OrdinalIgnoreCase) && - queryStrings.TryGetValue("code", out var code) && code.FirstOrDefault() is string codeValue) - return codeValue; - } - await sessionStorage.RemoveItemAsync(GitHubStateKey, c).ConfigureAwait(false); - } + var sessionStorage = sp.GetRequiredService(); + var stateValue = await sessionStorage.GetItemAsync(GitHubStateKey).ConfigureAwait(false); + var uri = navManager.ToAbsoluteUri(navManager.Uri); + var queryStrings = QueryHelpers.ParseQuery(uri.Query); + if(queryStrings.TryGetValue("state", out var state) && + stateValue.Equals(state, StringComparison.OrdinalIgnoreCase) && + queryStrings.TryGetValue("code", out var code) && code.FirstOrDefault() is string codeValue) + return codeValue; + await sessionStorage.RemoveItemAsync(GitHubStateKey, c).ConfigureAwait(false); return string.Empty; }, - sp.GetService()?.CreateLogger()!, + sp.GetRequiredService().CreateLogger(), new Uri($"{baseAddress}GitHubAuth") ); }); } + public static void AddPatAuthentication(this IServiceCollection services) { + services.AddScoped(sp => { + var configObject = sp.GetRequiredService(); + return new TempFolderCachingAccessTokenProvider { + Logger = sp.GetRequiredService().CreateLogger()!, + ApiBaseUrl = configObject.Search.GitHub.ApiBaseUrl, + Concrete = null, + AppId = configObject.Search.GitHub.AppId, + }; + }); + services.AddBlazoredSessionStorage(); + services.AddScoped(sp => { + var sessionStorage = sp.GetRequiredService(); + return new SessionStoragePatService { + SessionStorageService = sessionStorage, + }; + }); + services.AddScoped(sp => { + var configObject = sp.GetRequiredService(); + return new PatAuthenticationProvider( + configObject.Search.GitHub.AppId, + "repo", + new string[] { configObject.Search.GitHub.ApiBaseUrl.Host }, + sp.GetRequiredService().CreateLogger(), + async (c) => { + var patService = sp.GetRequiredService(); + var patValue = await patService.GetPatAsync(c).ConfigureAwait(false); + if(!string.IsNullOrEmpty(patValue)) + return patValue; + return string.Empty; + } + ); + }); + } } diff --git a/src/Kiota.Web/Program.cs b/src/Kiota.Web/Program.cs index b34a068ede..35161fb036 100644 --- a/src/Kiota.Web/Program.cs +++ b/src/Kiota.Web/Program.cs @@ -20,7 +20,7 @@ var configObject = new KiotaConfiguration(); builder.Configuration.Bind(configObject); builder.Services.AddSingleton(configObject); -builder.Services.AddBrowserCodeAuthentication(builder.HostEnvironment.BaseAddress); +builder.Services.AddPatAuthentication(); var host = builder.Build(); diff --git a/src/Kiota.Web/Shared/GitHubSignIn.razor b/src/Kiota.Web/Shared/GitHubSignIn.razor index 4c3523aa78..a531a88dc3 100644 --- a/src/Kiota.Web/Shared/GitHubSignIn.razor +++ b/src/Kiota.Web/Shared/GitHubSignIn.razor @@ -1,3 +1,4 @@ +@using Microsoft.Kiota.Abstractions @using Microsoft.Kiota.Abstractions.Authentication @using Kiota.Builder.Configuration @using Kiota.Web.Authentication.GitHub @@ -5,13 +6,14 @@ @using Microsoft.Fast.Components.FluentUI @using Microsoft.Extensions.Localization @inject IStringLocalizer Loc -@inject TempFolderCachingAccessTokenProvider CacheTokenProvider -@inject IAuthenticationProvider BrowserAuthenticationProvider +@inject TempFolderCachingAccessTokenProvider CachingProvider +@inject IAuthenticationProvider GitHubAuthProvider +@inject SessionStoragePatService SessionStoragePatService @inject KiotaConfiguration KiotaConfiguration  
- @if(CacheTokenProvider.IsCachedTokenPresent()) { + @if(CachingProvider.IsCachedTokenPresent()) { @Loc["SignOut"] } else { @Loc["SignIn"] @@ -20,11 +22,18 @@ @code { private async Task SignIn() { - if(BrowserAuthenticationProvider is BrowserAuthenticationProvider authProvider) - await authProvider.AccessTokenProvider.GetAuthorizationTokenAsync(KiotaConfiguration.Search.GitHub.ApiBaseUrl, cancellationToken: ComponentDetached); + @* await SessionStoragePatService.SetPatAsync("value", ComponentDetached); *@ + var dummyRequest = new RequestInformation() { + HttpMethod = Method.GET, + @* URI = Configuration.Search.GitHub.ApiBaseUrl, *@ + URI = new Uri("https://api.github.com"), + }; + await GitHubAuthProvider.AuthenticateRequestAsync(dummyRequest, cancellationToken: ComponentDetached); + //TODO Open dialog } - private void SignOut() { - CacheTokenProvider.Logout(); + private async Task SignOut() { + CachingProvider.Logout(); + await SessionStoragePatService.SignOutAsync(ComponentDetached); } } \ No newline at end of file From ec800d6aad9169c8e7019a3e6084d5427c16c172 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 25 Nov 2022 07:50:04 -0500 Subject: [PATCH 14/42] - replaces session storage by local storage to persist across sessions Signed-off-by: Vincent Biret --- ...atService.cs => LocalStoragePatService.cs} | 12 +++++----- .../IServiceCollectionExtensions.cs | 24 +++++++++---------- src/Kiota.Web/Kiota.Web.csproj | 2 +- src/Kiota.Web/Shared/GitHubSignIn.razor | 5 ++-- 4 files changed, 21 insertions(+), 22 deletions(-) rename src/Kiota.Web/Authentication/GitHub/{SessionStoragePatService.cs => LocalStoragePatService.cs} (53%) diff --git a/src/Kiota.Web/Authentication/GitHub/SessionStoragePatService.cs b/src/Kiota.Web/Authentication/GitHub/LocalStoragePatService.cs similarity index 53% rename from src/Kiota.Web/Authentication/GitHub/SessionStoragePatService.cs rename to src/Kiota.Web/Authentication/GitHub/LocalStoragePatService.cs index 248950d174..94c697b3d6 100644 --- a/src/Kiota.Web/Authentication/GitHub/SessionStoragePatService.cs +++ b/src/Kiota.Web/Authentication/GitHub/LocalStoragePatService.cs @@ -1,21 +1,21 @@ -using Blazored.SessionStorage; +using Blazored.LocalStorage; namespace Kiota.Web.Authentication.GitHub; -public class SessionStoragePatService { +public class LocalStoragePatService { private const string PATKey = "github-pat"; - public required ISessionStorageService SessionStorageService { get; init; } + public required ILocalStorageService LocalStorageService { get; init; } public async Task GetPatAsync(CancellationToken cancellationToken) { - return await SessionStorageService.GetItemAsync(PATKey, cancellationToken).ConfigureAwait(false); + return await LocalStorageService.GetItemAsync(PATKey, cancellationToken).ConfigureAwait(false); } public async Task SetPatAsync(string value, CancellationToken cancellationToken) { - await SessionStorageService.SetItemAsync(PATKey, value, cancellationToken).ConfigureAwait(false); + await LocalStorageService.SetItemAsync(PATKey, value, cancellationToken).ConfigureAwait(false); } public async Task IsSignedInAsync(CancellationToken cancellationToken) { return !string.IsNullOrEmpty(await GetPatAsync(cancellationToken).ConfigureAwait(false)); } public async Task SignOutAsync(CancellationToken cancellationToken) { - await SessionStorageService.RemoveItemAsync(PATKey, cancellationToken).ConfigureAwait(false); + await LocalStorageService.RemoveItemAsync(PATKey, cancellationToken).ConfigureAwait(false); } } diff --git a/src/Kiota.Web/Extensions/IServiceCollectionExtensions.cs b/src/Kiota.Web/Extensions/IServiceCollectionExtensions.cs index 33fefc853e..d8790c030f 100644 --- a/src/Kiota.Web/Extensions/IServiceCollectionExtensions.cs +++ b/src/Kiota.Web/Extensions/IServiceCollectionExtensions.cs @@ -1,4 +1,4 @@ -using Blazored.SessionStorage; +using Blazored.LocalStorage; using Kiota.Builder.Configuration; using Kiota.Builder.SearchProviders.GitHub.Authentication; using Microsoft.AspNetCore.Components; @@ -10,7 +10,7 @@ namespace Kiota.Web.Authentication.GitHub; public static class IServiceCollectionExtensions { private const string GitHubStateKey = "github-authentication-state"; public static void AddBrowserCodeAuthentication(this IServiceCollection services, string baseAddress) { - services.AddBlazoredSessionStorage(); + services.AddBlazoredLocalStorage(); services.AddScoped(sp => { var configObject = sp.GetRequiredService(); return new TempFolderCachingAccessTokenProvider { @@ -29,20 +29,20 @@ public static void AddBrowserCodeAuthentication(this IServiceCollection services new string[] { configObject.Search.GitHub.ApiBaseUrl.Host }, sp.GetRequiredService(), async (uri, state, c) => { - var sessionStorage = sp.GetRequiredService(); - await sessionStorage.SetItemAsync(GitHubStateKey, state, c).ConfigureAwait(false); + var localStorage = sp.GetRequiredService(); + await localStorage.SetItemAsync(GitHubStateKey, state, c).ConfigureAwait(false); navManager.NavigateTo(uri.ToString()); }, async (c) => { - var sessionStorage = sp.GetRequiredService(); - var stateValue = await sessionStorage.GetItemAsync(GitHubStateKey).ConfigureAwait(false); + var localStorage = sp.GetRequiredService(); + var stateValue = await localStorage.GetItemAsync(GitHubStateKey, c).ConfigureAwait(false); var uri = navManager.ToAbsoluteUri(navManager.Uri); var queryStrings = QueryHelpers.ParseQuery(uri.Query); if(queryStrings.TryGetValue("state", out var state) && stateValue.Equals(state, StringComparison.OrdinalIgnoreCase) && queryStrings.TryGetValue("code", out var code) && code.FirstOrDefault() is string codeValue) return codeValue; - await sessionStorage.RemoveItemAsync(GitHubStateKey, c).ConfigureAwait(false); + await localStorage.RemoveItemAsync(GitHubStateKey, c).ConfigureAwait(false); return string.Empty; }, sp.GetRequiredService().CreateLogger(), @@ -60,11 +60,11 @@ public static void AddPatAuthentication(this IServiceCollection services) { AppId = configObject.Search.GitHub.AppId, }; }); - services.AddBlazoredSessionStorage(); + services.AddBlazoredLocalStorage(); services.AddScoped(sp => { - var sessionStorage = sp.GetRequiredService(); - return new SessionStoragePatService { - SessionStorageService = sessionStorage, + var localStorage = sp.GetRequiredService(); + return new LocalStoragePatService { + LocalStorageService = localStorage, }; }); services.AddScoped(sp => { @@ -75,7 +75,7 @@ public static void AddPatAuthentication(this IServiceCollection services) { new string[] { configObject.Search.GitHub.ApiBaseUrl.Host }, sp.GetRequiredService().CreateLogger(), async (c) => { - var patService = sp.GetRequiredService(); + var patService = sp.GetRequiredService(); var patValue = await patService.GetPatAsync(c).ConfigureAwait(false); if(!string.IsNullOrEmpty(patValue)) return patValue; diff --git a/src/Kiota.Web/Kiota.Web.csproj b/src/Kiota.Web/Kiota.Web.csproj index a90941f025..793539c1e2 100644 --- a/src/Kiota.Web/Kiota.Web.csproj +++ b/src/Kiota.Web/Kiota.Web.csproj @@ -18,7 +18,7 @@ - + diff --git a/src/Kiota.Web/Shared/GitHubSignIn.razor b/src/Kiota.Web/Shared/GitHubSignIn.razor index a531a88dc3..19c8de4554 100644 --- a/src/Kiota.Web/Shared/GitHubSignIn.razor +++ b/src/Kiota.Web/Shared/GitHubSignIn.razor @@ -8,7 +8,7 @@ @inject IStringLocalizer Loc @inject TempFolderCachingAccessTokenProvider CachingProvider @inject IAuthenticationProvider GitHubAuthProvider -@inject SessionStoragePatService SessionStoragePatService +@inject LocalStoragePatService SessionStoragePatService @inject KiotaConfiguration KiotaConfiguration   @@ -25,8 +25,7 @@ @* await SessionStoragePatService.SetPatAsync("value", ComponentDetached); *@ var dummyRequest = new RequestInformation() { HttpMethod = Method.GET, - @* URI = Configuration.Search.GitHub.ApiBaseUrl, *@ - URI = new Uri("https://api.github.com"), + URI = KiotaConfiguration.Search.GitHub.ApiBaseUrl }; await GitHubAuthProvider.AuthenticateRequestAsync(dummyRequest, cancellationToken: ComponentDetached); //TODO Open dialog From 7e62611629c4ecfdfd1b3dc8561b5c6e6c7d0d06 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 25 Nov 2022 09:27:16 -0500 Subject: [PATCH 15/42] - removes dependency to temp folder service in kiota web to simplify init Signed-off-by: Vincent Biret --- src/Kiota.Builder/KiotaSearcher.cs | 6 ++++-- .../Autentication/BaseAuthenticationProvider.cs | 17 ++++++++++------- .../GitHub/GitHubSearchProvider.cs | 14 +++++--------- .../GitHub/PatAuthenticationProvider.cs | 2 +- .../Extensions/IServiceCollectionExtensions.cs | 9 --------- src/Kiota.Web/Shared/GitHubSignIn.razor | 15 +++++++++------ src/kiota/Handlers/BaseKiotaCommandHandler.cs | 5 +++++ .../Handlers/KiotaDownloadCommandHandler.cs | 2 +- .../Handlers/KiotaSeachBasedCommandHandler.cs | 2 +- src/kiota/Handlers/KiotaSearchCommandHandler.cs | 2 +- tests/Kiota.Builder.Tests/KiotaSearcherTests.cs | 8 ++++---- 11 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/Kiota.Builder/KiotaSearcher.cs b/src/Kiota.Builder/KiotaSearcher.cs index 68552a7ee9..40b616020b 100644 --- a/src/Kiota.Builder/KiotaSearcher.cs +++ b/src/Kiota.Builder/KiotaSearcher.cs @@ -19,8 +19,9 @@ public class KiotaSearcher { private readonly SearchConfiguration _config; private readonly HttpClient _httpClient; private readonly IAuthenticationProvider _gitHubAuthenticationProvider; + private readonly Func> _isGitHubSignedInCallBack; - public KiotaSearcher(ILogger logger, SearchConfiguration config, HttpClient httpClient, IAuthenticationProvider gitHubAuthenticationProvider) { + public KiotaSearcher(ILogger logger, SearchConfiguration config, HttpClient httpClient, IAuthenticationProvider gitHubAuthenticationProvider, Func> isGitHubSignedInCallBack) { ArgumentNullException.ThrowIfNull(logger); ArgumentNullException.ThrowIfNull(config); ArgumentNullException.ThrowIfNull(httpClient); @@ -28,6 +29,7 @@ public KiotaSearcher(ILogger logger, SearchConfiguration config, _config = config; _httpClient = httpClient; _gitHubAuthenticationProvider = gitHubAuthenticationProvider; + _isGitHubSignedInCallBack = isGitHubSignedInCallBack; } public async Task> SearchAsync(CancellationToken cancellationToken) { if (string.IsNullOrEmpty(_config.SearchTerm)) { @@ -38,7 +40,7 @@ public async Task> SearchAsync(CancellationTok _logger.LogDebug("searching for {searchTerm}", _config.SearchTerm); _logger.LogDebug("searching APIs.guru with url {url}", _config.APIsGuruListUrl); var oasProvider = new OpenApiSpecSearchProvider(); - var githubProvider = new GitHubSearchProvider(_httpClient, _logger, _config.ClearCache, _config.GitHub, _gitHubAuthenticationProvider); + var githubProvider = new GitHubSearchProvider(_httpClient, _logger, _config.ClearCache, _config.GitHub, _gitHubAuthenticationProvider, _isGitHubSignedInCallBack); var results = await Task.WhenAll( SearchProviderAsync(apiGurusSearchProvider, cancellationToken), SearchProviderAsync(oasProvider, cancellationToken), diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/BaseAuthenticationProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/BaseAuthenticationProvider.cs index 051d259fc4..6cbca3a6e5 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/BaseAuthenticationProvider.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/BaseAuthenticationProvider.cs @@ -15,7 +15,8 @@ public BaseAuthenticationProvider(string clientId, string scope, IEnumerable validHosts, ILogger logger, - Func, T> accessTokenProviderFactory) + Func, T> accessTokenProviderFactory, + bool enableCache = true) { ArgumentNullException.ThrowIfNull(validHosts); ArgumentNullException.ThrowIfNull(logger); @@ -25,12 +26,14 @@ public BaseAuthenticationProvider(string clientId, if (string.IsNullOrEmpty(scope)) throw new ArgumentNullException(nameof(scope)); - AccessTokenProvider = new TempFolderCachingAccessTokenProvider { - Concrete = accessTokenProviderFactory(clientId, scope, validHosts), - Logger = logger, - ApiBaseUrl = new Uri($"https://{validHosts.FirstOrDefault() ?? "api.github.com"}"), - AppId = clientId, - }; + AccessTokenProvider = accessTokenProviderFactory(clientId, scope, validHosts); + if(enableCache) + AccessTokenProvider = new TempFolderCachingAccessTokenProvider { + Concrete = AccessTokenProvider, + Logger = logger, + ApiBaseUrl = new Uri($"https://{validHosts.FirstOrDefault() ?? "api.github.com"}"), + AppId = clientId, + }; } public IAccessTokenProvider AccessTokenProvider {get; private set;} private const string AuthorizationHeaderKey = "Authorization"; diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs index c8a330a630..ca19d4975f 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs @@ -9,7 +9,6 @@ using Kiota.Builder.Caching; using Kiota.Builder.Configuration; using Kiota.Builder.Extensions; -using Kiota.Builder.SearchProviders.GitHub.Authentication; using Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models; using Kiota.Builder.SearchProviders.GitHub.Index; using Microsoft.Extensions.Logging; @@ -28,7 +27,8 @@ public class GitHubSearchProvider : ISearchProvider private readonly string _clientId; private readonly Uri _appBaseUrl; private readonly IAuthenticationProvider _authenticatedAuthenticationProvider; - public GitHubSearchProvider(HttpClient httpClient, ILogger logger, bool clearCache, GitHubConfiguration configuration, IAuthenticationProvider authenticatedAuthenticationProvider) + private readonly Func> _isSignedInCallback; + public GitHubSearchProvider(HttpClient httpClient, ILogger logger, bool clearCache, GitHubConfiguration configuration, IAuthenticationProvider authenticatedAuthenticationProvider, Func> isSignedInCallBack) { ArgumentNullException.ThrowIfNull(httpClient); ArgumentNullException.ThrowIfNull(configuration); @@ -47,6 +47,7 @@ public GitHubSearchProvider(HttpClient httpClient, ILogger logger, bool clearCac _clientId = configuration.AppId; _authenticatedAuthenticationProvider = authenticatedAuthenticationProvider; _appBaseUrl = configuration.ApiBaseUrl; + _isSignedInCallback = isSignedInCallBack; } private readonly HttpClient _httpClient; public string ProviderKey => "github"; @@ -72,13 +73,8 @@ private static bool BlockListContainsRepo(Tuple, HashSet private async Task> SearchAsyncInternal(string term, CancellationToken cancellationToken) { var blockLists = await GetBlockLists(cancellationToken); - var cachingProvider = new TempFolderCachingAccessTokenProvider { - Logger = _logger, - ApiBaseUrl = _appBaseUrl, - Concrete = null, - AppId = _clientId, - }; - var authenticationProvider = _authenticatedAuthenticationProvider != null && cachingProvider.IsCachedTokenPresent() ? + var isSignedIn = await _isSignedInCallback(cancellationToken); + var authenticationProvider = _authenticatedAuthenticationProvider != null && isSignedIn ? _authenticatedAuthenticationProvider : new Authentication.AnonymousAuthenticationProvider(); var gitHubRequestAdapter = new HttpClientRequestAdapter(authenticationProvider, httpClient: _httpClient); diff --git a/src/Kiota.Web/Authentication/GitHub/PatAuthenticationProvider.cs b/src/Kiota.Web/Authentication/GitHub/PatAuthenticationProvider.cs index 217387e120..0139298f52 100644 --- a/src/Kiota.Web/Authentication/GitHub/PatAuthenticationProvider.cs +++ b/src/Kiota.Web/Authentication/GitHub/PatAuthenticationProvider.cs @@ -7,7 +7,7 @@ public PatAuthenticationProvider(string clientId, string scope, IEnumerable new PatProvider { GetPATFromStorageCallback = GetPATFromStorageCallback, AllowedHostsValidator = new (validHosts), - }) + }, false) { ArgumentNullException.ThrowIfNull(GetPATFromStorageCallback); } diff --git a/src/Kiota.Web/Extensions/IServiceCollectionExtensions.cs b/src/Kiota.Web/Extensions/IServiceCollectionExtensions.cs index d8790c030f..aa89fd3c2a 100644 --- a/src/Kiota.Web/Extensions/IServiceCollectionExtensions.cs +++ b/src/Kiota.Web/Extensions/IServiceCollectionExtensions.cs @@ -51,15 +51,6 @@ public static void AddBrowserCodeAuthentication(this IServiceCollection services }); } public static void AddPatAuthentication(this IServiceCollection services) { - services.AddScoped(sp => { - var configObject = sp.GetRequiredService(); - return new TempFolderCachingAccessTokenProvider { - Logger = sp.GetRequiredService().CreateLogger()!, - ApiBaseUrl = configObject.Search.GitHub.ApiBaseUrl, - Concrete = null, - AppId = configObject.Search.GitHub.AppId, - }; - }); services.AddBlazoredLocalStorage(); services.AddScoped(sp => { var localStorage = sp.GetRequiredService(); diff --git a/src/Kiota.Web/Shared/GitHubSignIn.razor b/src/Kiota.Web/Shared/GitHubSignIn.razor index 19c8de4554..fbf8e815b6 100644 --- a/src/Kiota.Web/Shared/GitHubSignIn.razor +++ b/src/Kiota.Web/Shared/GitHubSignIn.razor @@ -6,14 +6,13 @@ @using Microsoft.Fast.Components.FluentUI @using Microsoft.Extensions.Localization @inject IStringLocalizer Loc -@inject TempFolderCachingAccessTokenProvider CachingProvider @inject IAuthenticationProvider GitHubAuthProvider -@inject LocalStoragePatService SessionStoragePatService +@inject LocalStoragePatService LocalStoragePatService @inject KiotaConfiguration KiotaConfiguration  
- @if(CachingProvider.IsCachedTokenPresent()) { + @if(IsSignedIn) { @Loc["SignOut"] } else { @Loc["SignIn"] @@ -21,8 +20,13 @@
@code { + private bool IsSignedIn {get; set;} + protected override async Task OnParametersSetAsync() { + IsSignedIn = await LocalStoragePatService.IsSignedInAsync(ComponentDetached); + await base.OnParametersSetAsync(); + } private async Task SignIn() { - @* await SessionStoragePatService.SetPatAsync("value", ComponentDetached); *@ + @* await LocalStoragePatService.SetPatAsync("value", ComponentDetached); *@ var dummyRequest = new RequestInformation() { HttpMethod = Method.GET, URI = KiotaConfiguration.Search.GitHub.ApiBaseUrl @@ -31,8 +35,7 @@ //TODO Open dialog } private async Task SignOut() { - CachingProvider.Logout(); - await SessionStoragePatService.SignOutAsync(ComponentDetached); + await LocalStoragePatService.SignOutAsync(ComponentDetached); } } \ No newline at end of file diff --git a/src/kiota/Handlers/BaseKiotaCommandHandler.cs b/src/kiota/Handlers/BaseKiotaCommandHandler.cs index 41abffb68d..8c4e34aacd 100644 --- a/src/kiota/Handlers/BaseKiotaCommandHandler.cs +++ b/src/kiota/Handlers/BaseKiotaCommandHandler.cs @@ -5,6 +5,7 @@ using System.IO; using System.Linq; using System.Net.Http; +using System.Threading; using System.Threading.Tasks; using kiota.Authentication.GitHub.DeviceCode; using Kiota.Builder; @@ -37,6 +38,10 @@ internal abstract class BaseKiotaCommandHandler : ICommandHandler return configObject; }); private const string GitHubScope = "repo"; + protected Func> GetIsGitHubSignedInCallback(ILogger logger) => (cancellationToken) => { + var provider = GitHubAuthenticationCachingProvider(logger); + return Task.FromResult(provider.IsCachedTokenPresent()); + }; protected IAuthenticationProvider GetAuthenticationProvider(ILogger logger) => new DeviceCodeAuthenticationProvider(Configuration.Search.GitHub.AppId, GitHubScope, diff --git a/src/kiota/Handlers/KiotaDownloadCommandHandler.cs b/src/kiota/Handlers/KiotaDownloadCommandHandler.cs index 68d22d9c90..dc40ac712e 100644 --- a/src/kiota/Handlers/KiotaDownloadCommandHandler.cs +++ b/src/kiota/Handlers/KiotaDownloadCommandHandler.cs @@ -46,7 +46,7 @@ public override async Task InvokeAsync(InvocationContext context) logger.LogTrace("configuration: {configuration}", JsonSerializer.Serialize(Configuration)); try { - var results = await new KiotaSearcher(logger, Configuration.Search, httpClient, GetAuthenticationProvider(logger)).SearchAsync(cancellationToken); + var results = await new KiotaSearcher(logger, Configuration.Search, httpClient, GetAuthenticationProvider(logger), GetIsGitHubSignedInCallback(logger)).SearchAsync(cancellationToken); return await SaveResultsAsync(results, logger, cancellationToken); } catch (Exception ex) { #if DEBUG diff --git a/src/kiota/Handlers/KiotaSeachBasedCommandHandler.cs b/src/kiota/Handlers/KiotaSeachBasedCommandHandler.cs index 6ebfde4e03..4d6ad43d2e 100644 --- a/src/kiota/Handlers/KiotaSeachBasedCommandHandler.cs +++ b/src/kiota/Handlers/KiotaSeachBasedCommandHandler.cs @@ -12,7 +12,7 @@ internal abstract class KiotaSearchBasedCommandHandler : BaseKiotaCommandHandler if (string.IsNullOrEmpty(openapi) && !string.IsNullOrEmpty(searchTerm)) { logger.LogInformation("Searching for {searchTerm} in the OpenAPI description repository", searchTerm); - var searcher = new KiotaSearcher(loggerFactory.CreateLogger(), Configuration.Search, httpClient, GetAuthenticationProvider(logger)); + var searcher = new KiotaSearcher(loggerFactory.CreateLogger(), Configuration.Search, httpClient, GetAuthenticationProvider(logger), GetIsGitHubSignedInCallback(logger)); var results = await searcher.SearchAsync(cancellationToken); if (results.Count == 1) return (results.First().Value.DescriptionUrl.ToString(), null); diff --git a/src/kiota/Handlers/KiotaSearchCommandHandler.cs b/src/kiota/Handlers/KiotaSearchCommandHandler.cs index e7a0057f86..8e3c2065e9 100644 --- a/src/kiota/Handlers/KiotaSearchCommandHandler.cs +++ b/src/kiota/Handlers/KiotaSearchCommandHandler.cs @@ -37,7 +37,7 @@ public override async Task InvokeAsync(InvocationContext context) logger.LogTrace("configuration: {configuration}", JsonSerializer.Serialize(Configuration)); try { - var results = await new KiotaSearcher(logger, Configuration.Search, httpClient, GetAuthenticationProvider(logger)).SearchAsync(cancellationToken); + var results = await new KiotaSearcher(logger, Configuration.Search, httpClient, GetAuthenticationProvider(logger), GetIsGitHubSignedInCallback(logger)).SearchAsync(cancellationToken); DisplayResults(results, logger); return 0; } catch (Exception ex) { diff --git a/tests/Kiota.Builder.Tests/KiotaSearcherTests.cs b/tests/Kiota.Builder.Tests/KiotaSearcherTests.cs index 87cf1431b8..20109626bc 100644 --- a/tests/Kiota.Builder.Tests/KiotaSearcherTests.cs +++ b/tests/Kiota.Builder.Tests/KiotaSearcherTests.cs @@ -17,10 +17,10 @@ public void DefensivePrograming() { Assert.Throws(() => new KiotaSearcher(null, new SearchConfiguration(), httpClient, null)); Assert.Throws(() => new KiotaSearcher(new Mock>().Object, null, httpClient, null)); Assert.Throws(() => new KiotaSearcher(new Mock>().Object, new SearchConfiguration(), null, null)); - Assert.Throws(() => new GitHubSearchProvider(httpClient, new Mock>().Object, false, null, null)); - Assert.Throws(() => new GitHubSearchProvider(httpClient, null, false, new GitHubConfiguration(), null)); - Assert.Throws(() => new GitHubSearchProvider(null, new Mock>().Object, false, new GitHubConfiguration(), null)); - Assert.ThrowsAsync(() => new GitHubSearchProvider(httpClient, new Mock>().Object, false, new GitHubConfiguration(), null).SearchAsync(null, null, CancellationToken.None)); + Assert.Throws(() => new GitHubSearchProvider(httpClient, new Mock>().Object, false, null, null, null)); + Assert.Throws(() => new GitHubSearchProvider(httpClient, null, false, new GitHubConfiguration(), null, null)); + Assert.Throws(() => new GitHubSearchProvider(null, new Mock>().Object, false, new GitHubConfiguration(), null, null)); + Assert.ThrowsAsync(() => new GitHubSearchProvider(httpClient, new Mock>().Object, false, new GitHubConfiguration(), null, null).SearchAsync(null, null, CancellationToken.None)); } private static SearchConfiguration searchConfigurationFactory => new(){ GitHub = new() {} From 1405ef1457aa44b3301d47c90dc78fd63d3cbe5f Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 25 Nov 2022 09:27:49 -0500 Subject: [PATCH 16/42] - moves search terms outside of configuration to simplify Kiota searcher injection Signed-off-by: Vincent Biret --- .../Configuration/SearchConfiguration.cs | 1 - .../Configuration/SearchConfigurationBase.cs | 2 -- src/Kiota.Builder/KiotaSearcher.cs | 16 ++++----- .../GitHub/GitHubSearchProvider.cs | 2 +- .../IServiceCollectionExtensions.cs | 12 +++++++ src/Kiota.Web/Pages/Generate.razor | 11 ++---- src/Kiota.Web/Pages/Index.razor | 10 +++--- src/Kiota.Web/Pages/Show.razor | 20 +++-------- src/Kiota.Web/Program.cs | 1 + src/Kiota.Web/Shared/GitHubSignIn.razor | 6 ---- .../Handlers/KiotaDownloadCommandHandler.cs | 14 +++----- src/kiota/Handlers/KiotaInfoCommandHandler.cs | 4 +-- .../Handlers/KiotaSeachBasedCommandHandler.cs | 4 +-- .../Handlers/KiotaSearchCommandHandler.cs | 16 ++++----- src/kiota/Handlers/KiotaShowCommandHandler.cs | 4 +-- .../Kiota.Builder.Tests/KiotaSearcherTests.cs | 35 ++++++++----------- 16 files changed, 65 insertions(+), 93 deletions(-) diff --git a/src/Kiota.Builder/Configuration/SearchConfiguration.cs b/src/Kiota.Builder/Configuration/SearchConfiguration.cs index b2bd435b9c..f7576e0e0b 100644 --- a/src/Kiota.Builder/Configuration/SearchConfiguration.cs +++ b/src/Kiota.Builder/Configuration/SearchConfiguration.cs @@ -1,5 +1,4 @@ using System; -using System.Text.Json.Serialization; namespace Kiota.Builder.Configuration; diff --git a/src/Kiota.Builder/Configuration/SearchConfigurationBase.cs b/src/Kiota.Builder/Configuration/SearchConfigurationBase.cs index 1e1adde5ac..71359630cb 100644 --- a/src/Kiota.Builder/Configuration/SearchConfigurationBase.cs +++ b/src/Kiota.Builder/Configuration/SearchConfigurationBase.cs @@ -1,7 +1,5 @@ namespace Kiota.Builder.Configuration; public abstract class SearchConfigurationBase { - public string SearchTerm { get; set; } public bool ClearCache { get; set; } - public string Version { get; set; } } diff --git a/src/Kiota.Builder/KiotaSearcher.cs b/src/Kiota.Builder/KiotaSearcher.cs index 40b616020b..dbc1f267d1 100644 --- a/src/Kiota.Builder/KiotaSearcher.cs +++ b/src/Kiota.Builder/KiotaSearcher.cs @@ -31,26 +31,26 @@ public KiotaSearcher(ILogger logger, SearchConfiguration config, _gitHubAuthenticationProvider = gitHubAuthenticationProvider; _isGitHubSignedInCallBack = isGitHubSignedInCallBack; } - public async Task> SearchAsync(CancellationToken cancellationToken) { - if (string.IsNullOrEmpty(_config.SearchTerm)) { + public async Task> SearchAsync(string searchTerm, string version, CancellationToken cancellationToken) { + if (string.IsNullOrEmpty(searchTerm)) { _logger.LogError("no search term provided"); return new Dictionary(); } var apiGurusSearchProvider = new APIsGuruSearchProvider(_config.APIsGuruListUrl, _httpClient, _logger, _config.ClearCache); - _logger.LogDebug("searching for {searchTerm}", _config.SearchTerm); + _logger.LogDebug("searching for {searchTerm}", searchTerm); _logger.LogDebug("searching APIs.guru with url {url}", _config.APIsGuruListUrl); var oasProvider = new OpenApiSpecSearchProvider(); var githubProvider = new GitHubSearchProvider(_httpClient, _logger, _config.ClearCache, _config.GitHub, _gitHubAuthenticationProvider, _isGitHubSignedInCallBack); var results = await Task.WhenAll( - SearchProviderAsync(apiGurusSearchProvider, cancellationToken), - SearchProviderAsync(oasProvider, cancellationToken), - SearchProviderAsync(githubProvider, cancellationToken)); + SearchProviderAsync(searchTerm, version, apiGurusSearchProvider, cancellationToken), + SearchProviderAsync(searchTerm, version, oasProvider, cancellationToken), + SearchProviderAsync(searchTerm, version, githubProvider, cancellationToken)); return results.SelectMany(static x => x) .ToDictionary(static x => x.Key, static x => x.Value, StringComparer.OrdinalIgnoreCase); } - private async Task> SearchProviderAsync(ISearchProvider provider, CancellationToken cancellationToken) { + private async Task> SearchProviderAsync(string searchTerm, string version, ISearchProvider provider, CancellationToken cancellationToken) { var providerPrefix = $"{provider.ProviderKey}{ProviderSeparator}"; - var results = await provider.SearchAsync(_config.SearchTerm.Replace(providerPrefix, string.Empty), _config.Version, cancellationToken); + var results = await provider.SearchAsync(searchTerm.Replace(providerPrefix, string.Empty), version, cancellationToken); return results.Select(x => ($"{providerPrefix}{x.Key}", x.Value)) .ToDictionary(static x => x.Item1, static x => x.Value, StringComparer.OrdinalIgnoreCase); diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs index ca19d4975f..ed7c6118b4 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs @@ -73,7 +73,7 @@ private static bool BlockListContainsRepo(Tuple, HashSet private async Task> SearchAsyncInternal(string term, CancellationToken cancellationToken) { var blockLists = await GetBlockLists(cancellationToken); - var isSignedIn = await _isSignedInCallback(cancellationToken); + var isSignedIn = _isSignedInCallback != null && await _isSignedInCallback(cancellationToken); var authenticationProvider = _authenticatedAuthenticationProvider != null && isSignedIn ? _authenticatedAuthenticationProvider : new Authentication.AnonymousAuthenticationProvider(); diff --git a/src/Kiota.Web/Extensions/IServiceCollectionExtensions.cs b/src/Kiota.Web/Extensions/IServiceCollectionExtensions.cs index aa89fd3c2a..25d90666d9 100644 --- a/src/Kiota.Web/Extensions/IServiceCollectionExtensions.cs +++ b/src/Kiota.Web/Extensions/IServiceCollectionExtensions.cs @@ -1,4 +1,5 @@ using Blazored.LocalStorage; +using Kiota.Builder; using Kiota.Builder.Configuration; using Kiota.Builder.SearchProviders.GitHub.Authentication; using Microsoft.AspNetCore.Components; @@ -75,4 +76,15 @@ public static void AddPatAuthentication(this IServiceCollection services) { ); }); } + public static void AddSearchService(this IServiceCollection services) { + services.AddScoped(sp => { + var configObject = sp.GetRequiredService(); + var patService = sp.GetRequiredService(); + return new KiotaSearcher(sp.GetRequiredService().CreateLogger(), + configObject.Search, + sp.GetRequiredService(), + sp.GetRequiredService(), + (c) => patService.IsSignedInAsync(c)); + }); + } } diff --git a/src/Kiota.Web/Pages/Generate.razor b/src/Kiota.Web/Pages/Generate.razor index 77b47b2674..61bffef67a 100644 --- a/src/Kiota.Web/Pages/Generate.razor +++ b/src/Kiota.Web/Pages/Generate.razor @@ -17,7 +17,6 @@ @using System.Globalization @using Microsoft.Extensions.Localization @inject IStringLocalizer Loc -@inject IAuthenticationProvider GitHubAuthProvider @Loc["PageTitle"] @@ -180,18 +179,14 @@ await GetConfiguration(ComponentDetached); var SearchTerm = string.IsNullOrEmpty(SearchTermFromQuery) ? string.Empty : SearchTermFromQuery; var Version = string.IsNullOrEmpty(VersionFromQuery) ? string.Empty : VersionFromQuery; - if (!string.IsNullOrEmpty(SearchTerm) && !string.IsNullOrEmpty(Version) && string.IsNullOrEmpty(DescriptionUrl)) { - var logger = LoggerFactory.CreateLogger(); - var searchConfig = new SearchConfiguration() { - SearchTerm = SearchTerm, - Version = Version, - }; - var searchResults = await new KiotaSearcher(logger, searchConfig, Http, GitHubAuthProvider).SearchAsync(ComponentDetached); + if (!string.IsNullOrEmpty(SearchTerm) && !string.IsNullOrEmpty(Version) && string.IsNullOrEmpty(DescriptionUrl) && KiotaSearcher != null) { + var searchResults = await KiotaSearcher.SearchAsync(SearchTerm, Version, ComponentDetached); if(searchResults.Any()) DescriptionUrl = searchResults.Values.First().DescriptionUrl.ToString(); } await base.OnParametersSetAsync(); } + [Inject] private KiotaSearcher? KiotaSearcher { get; set; } private static readonly ThreadLocal HashAlgorithm = new(() => SHA256.Create()); [Inject] private IApplicationInsights? AppInsights { get; set; } private Task GenerateClient() { diff --git a/src/Kiota.Web/Pages/Index.razor b/src/Kiota.Web/Pages/Index.razor index 9cb33d8fef..452e7fd293 100644 --- a/src/Kiota.Web/Pages/Index.razor +++ b/src/Kiota.Web/Pages/Index.razor @@ -14,7 +14,6 @@ @inject IStringLocalizer Loc @inject NavigationManager navManager @inject HttpClient Http -@inject IAuthenticationProvider GitHubAuthProvider @Loc["PageTitle"] @@ -81,6 +80,8 @@ @code { [Inject] private IApplicationInsights? AppInsights { get; set; } + [Inject] private KiotaSearcher? KiotaSearcher { get; set; } + private bool ShowVersionSelector { get; set; } private bool IsLoading { get; set; } private bool Searched { get; set; } @@ -105,11 +106,8 @@ private async Task SearchDocumentsInternal() { if(AppInsights != null) await AppInsights.StartTrackEvent(searchTelemetryKey).ConfigureAwait(false); - var searchConfig = new SearchConfiguration() { - SearchTerm = SearchTerm, - }; - var logger = LoggerFactory.CreateLogger(); - SearchResults = await new KiotaSearcher(logger, searchConfig, Http, GitHubAuthProvider).SearchAsync(ComponentDetached); + if(KiotaSearcher != null) + SearchResults = await KiotaSearcher.SearchAsync(SearchTerm, string.Empty, ComponentDetached); IsLoading = false; if(AppInsights != null) await AppInsights.StopTrackEvent(searchTelemetryKey, new Dictionary { diff --git a/src/Kiota.Web/Pages/Show.razor b/src/Kiota.Web/Pages/Show.razor index 52951118e7..6b09191e02 100644 --- a/src/Kiota.Web/Pages/Show.razor +++ b/src/Kiota.Web/Pages/Show.razor @@ -13,7 +13,6 @@ @using Microsoft.Kiota.Abstractions.Authentication; @inject IStringLocalizer Loc @inject HttpClient Http -@inject IAuthenticationProvider GitHubAuthProvider @Loc["PageTitle"] @@ -61,6 +60,7 @@ private string? Tree { get; set; } private bool IsLoading { get; set; } [Inject] private IApplicationInsights? AppInsights { get; set; } + [Inject] private KiotaSearcher? KiotaSearcher { get; set; } protected override async Task OnParametersSetAsync() { if(!string.IsNullOrEmpty(SearchTermFromQuery)) SearchTerm = SearchTermFromQuery; @@ -68,13 +68,8 @@ Version = VersionFromQuery; if(!string.IsNullOrEmpty(DescriptionUrlFromQuery)) DescriptionUrl = DescriptionUrlFromQuery; - if (!string.IsNullOrEmpty(SearchTerm) && !string.IsNullOrEmpty(Version) && string.IsNullOrEmpty(DescriptionUrl)) { - var logger = LoggerFactory.CreateLogger(); - var searchConfig = new SearchConfiguration() { - SearchTerm = SearchTerm, - Version = Version, - }; - var searchResults = await new KiotaSearcher(logger, searchConfig, Http, GitHubAuthProvider).SearchAsync(ComponentDetached); + if (!string.IsNullOrEmpty(SearchTerm) && !string.IsNullOrEmpty(Version) && string.IsNullOrEmpty(DescriptionUrl) && KiotaSearcher != null) { + var searchResults = await KiotaSearcher.SearchAsync(SearchTerm, Version, ComponentDetached); if(searchResults.Any()) DescriptionUrl = searchResults.Values.First().DescriptionUrl.ToString(); } @@ -109,13 +104,8 @@ private const string showDescriptionTelemetryKey = "showdescription"; private async Task ShowDescriptionInternal() { Tree = string.Empty; - if (string.IsNullOrEmpty(DescriptionUrl)) { - var searchConfig = new SearchConfiguration() { - SearchTerm = SearchTerm, - Version = Version, - }; - var logger = LoggerFactory.CreateLogger(); - SearchResults = await new KiotaSearcher(logger, searchConfig, Http, GitHubAuthProvider).SearchAsync(ComponentDetached); + if (string.IsNullOrEmpty(DescriptionUrl) && KiotaSearcher != null) { + SearchResults = await KiotaSearcher.SearchAsync(SearchTerm, Version, ComponentDetached); DescriptionUrl = SearchResults.First().Value.DescriptionUrl.ToString(); } if(AppInsights != null) diff --git a/src/Kiota.Web/Program.cs b/src/Kiota.Web/Program.cs index 35161fb036..8eb25619d0 100644 --- a/src/Kiota.Web/Program.cs +++ b/src/Kiota.Web/Program.cs @@ -21,6 +21,7 @@ builder.Configuration.Bind(configObject); builder.Services.AddSingleton(configObject); builder.Services.AddPatAuthentication(); +builder.Services.AddSearchService(); var host = builder.Build(); diff --git a/src/Kiota.Web/Shared/GitHubSignIn.razor b/src/Kiota.Web/Shared/GitHubSignIn.razor index fbf8e815b6..3568e122ce 100644 --- a/src/Kiota.Web/Shared/GitHubSignIn.razor +++ b/src/Kiota.Web/Shared/GitHubSignIn.razor @@ -6,7 +6,6 @@ @using Microsoft.Fast.Components.FluentUI @using Microsoft.Extensions.Localization @inject IStringLocalizer Loc -@inject IAuthenticationProvider GitHubAuthProvider @inject LocalStoragePatService LocalStoragePatService @inject KiotaConfiguration KiotaConfiguration @@ -27,11 +26,6 @@ } private async Task SignIn() { @* await LocalStoragePatService.SetPatAsync("value", ComponentDetached); *@ - var dummyRequest = new RequestInformation() { - HttpMethod = Method.GET, - URI = KiotaConfiguration.Search.GitHub.ApiBaseUrl - }; - await GitHubAuthProvider.AuthenticateRequestAsync(dummyRequest, cancellationToken: ComponentDetached); //TODO Open dialog } private async Task SignOut() { diff --git a/src/kiota/Handlers/KiotaDownloadCommandHandler.cs b/src/kiota/Handlers/KiotaDownloadCommandHandler.cs index dc40ac712e..5269bddd21 100644 --- a/src/kiota/Handlers/KiotaDownloadCommandHandler.cs +++ b/src/kiota/Handlers/KiotaDownloadCommandHandler.cs @@ -31,14 +31,10 @@ public override async Task InvokeAsync(InvocationContext context) bool clearCache = context.ParseResult.GetValueForOption(ClearCacheOption); CancellationToken cancellationToken = (CancellationToken)context.BindingContext.GetService(typeof(CancellationToken)); - Configuration.Download.SearchTerm = searchTerm; - Configuration.Download.Version = version; Configuration.Download.ClearCache = clearCache; Configuration.Download.CleanOutput = cleanOutput; Configuration.Download.OutputPath = NormalizeSlashesInPath(outputPath); - Configuration.Search.SearchTerm = Configuration.Download.SearchTerm; - Configuration.Search.Version = Configuration.Download.Version; Configuration.Search.ClearCache = Configuration.Download.ClearCache; var (loggerFactory, logger) = GetLoggerAndFactory(context); @@ -46,8 +42,9 @@ public override async Task InvokeAsync(InvocationContext context) logger.LogTrace("configuration: {configuration}", JsonSerializer.Serialize(Configuration)); try { - var results = await new KiotaSearcher(logger, Configuration.Search, httpClient, GetAuthenticationProvider(logger), GetIsGitHubSignedInCallback(logger)).SearchAsync(cancellationToken); - return await SaveResultsAsync(results, logger, cancellationToken); + var results = await new KiotaSearcher(logger, Configuration.Search, httpClient, GetAuthenticationProvider(logger), GetIsGitHubSignedInCallback(logger)) + .SearchAsync(searchTerm, version, cancellationToken); + return await SaveResultsAsync(searchTerm, version, results, logger, cancellationToken); } catch (Exception ex) { #if DEBUG logger.LogCritical(ex, "error downloading a description: {exceptionMessage}", ex.Message); @@ -59,14 +56,13 @@ public override async Task InvokeAsync(InvocationContext context) } } } - private async Task SaveResultsAsync(IDictionary results, ILogger logger, CancellationToken cancellationToken){ - var searchTerm = Configuration.Download.SearchTerm; + private async Task SaveResultsAsync(string searchTerm, string version, IDictionary results, ILogger logger, CancellationToken cancellationToken){ if (!results.Any()) DisplayError("No matching result found, use the search command to find the right key"); else if (results.Any() && !string.IsNullOrEmpty(searchTerm) && searchTerm.Contains(KiotaSearcher.ProviderSeparator) && results.ContainsKey(searchTerm)) { var (path, statusCode) = await SaveResultAsync(results.First(), logger, cancellationToken); DisplaySuccess($"File successfully downloaded to {path}"); - DisplayShowHint(Configuration.Search.SearchTerm, Configuration.Search.Version, path); + DisplayShowHint(searchTerm, version, path); DisplayGenerateHint(path, Enumerable.Empty(), Enumerable.Empty()); return statusCode; } else diff --git a/src/kiota/Handlers/KiotaInfoCommandHandler.cs b/src/kiota/Handlers/KiotaInfoCommandHandler.cs index 296d7f3d5c..ceeb453eae 100644 --- a/src/kiota/Handlers/KiotaInfoCommandHandler.cs +++ b/src/kiota/Handlers/KiotaInfoCommandHandler.cs @@ -28,8 +28,6 @@ public override async Task InvokeAsync(InvocationContext context) GenerationLanguage? language = context.ParseResult.GetValueForOption(GenerationLanguage); CancellationToken cancellationToken = (CancellationToken)context.BindingContext.GetService(typeof(CancellationToken)); var (loggerFactory, logger) = GetLoggerAndFactory(context); - Configuration.Search.SearchTerm = searchTerm; - Configuration.Search.Version = version; Configuration.Search.ClearCache = clearCache; using (loggerFactory) { @@ -39,7 +37,7 @@ public override async Task InvokeAsync(InvocationContext context) return 0; } - var (searchResultDescription, statusCode) = await GetDescriptionFromSearch(openapi, searchTerm, loggerFactory, logger, cancellationToken); + var (searchResultDescription, statusCode) = await GetDescriptionFromSearch(openapi, searchTerm, version, loggerFactory, logger, cancellationToken); if (statusCode.HasValue) { return statusCode.Value; } diff --git a/src/kiota/Handlers/KiotaSeachBasedCommandHandler.cs b/src/kiota/Handlers/KiotaSeachBasedCommandHandler.cs index 4d6ad43d2e..ecd73208e2 100644 --- a/src/kiota/Handlers/KiotaSeachBasedCommandHandler.cs +++ b/src/kiota/Handlers/KiotaSeachBasedCommandHandler.cs @@ -8,12 +8,12 @@ namespace kiota.Handlers; internal abstract class KiotaSearchBasedCommandHandler : BaseKiotaCommandHandler { - protected async Task<(string, int?)> GetDescriptionFromSearch(string openapi, string searchTerm, ILoggerFactory loggerFactory, ILogger logger, CancellationToken cancellationToken) { + protected async Task<(string, int?)> GetDescriptionFromSearch(string openapi, string searchTerm, string version, ILoggerFactory loggerFactory, ILogger logger, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(openapi) && !string.IsNullOrEmpty(searchTerm)) { logger.LogInformation("Searching for {searchTerm} in the OpenAPI description repository", searchTerm); var searcher = new KiotaSearcher(loggerFactory.CreateLogger(), Configuration.Search, httpClient, GetAuthenticationProvider(logger), GetIsGitHubSignedInCallback(logger)); - var results = await searcher.SearchAsync(cancellationToken); + var results = await searcher.SearchAsync(searchTerm, version, cancellationToken); if (results.Count == 1) return (results.First().Value.DescriptionUrl.ToString(), null); else if(!results.Any()) { diff --git a/src/kiota/Handlers/KiotaSearchCommandHandler.cs b/src/kiota/Handlers/KiotaSearchCommandHandler.cs index 8e3c2065e9..ee41766947 100644 --- a/src/kiota/Handlers/KiotaSearchCommandHandler.cs +++ b/src/kiota/Handlers/KiotaSearchCommandHandler.cs @@ -27,8 +27,6 @@ public override async Task InvokeAsync(InvocationContext context) bool clearCache = context.ParseResult.GetValueForOption(ClearCacheOption); CancellationToken cancellationToken = (CancellationToken)context.BindingContext.GetService(typeof(CancellationToken)); - Configuration.Search.SearchTerm = searchTerm; - Configuration.Search.Version = version; Configuration.Search.ClearCache = clearCache; @@ -37,8 +35,9 @@ public override async Task InvokeAsync(InvocationContext context) logger.LogTrace("configuration: {configuration}", JsonSerializer.Serialize(Configuration)); try { - var results = await new KiotaSearcher(logger, Configuration.Search, httpClient, GetAuthenticationProvider(logger), GetIsGitHubSignedInCallback(logger)).SearchAsync(cancellationToken); - DisplayResults(results, logger); + var results = await new KiotaSearcher(logger, Configuration.Search, httpClient, GetAuthenticationProvider(logger), GetIsGitHubSignedInCallback(logger)) + .SearchAsync(searchTerm, version, cancellationToken); + DisplayResults(searchTerm, version, results, logger); return 0; } catch (Exception ex) { #if DEBUG @@ -51,8 +50,7 @@ public override async Task InvokeAsync(InvocationContext context) } } } - private void DisplayResults(IDictionary results, ILogger logger){ - var searchTerm = Configuration.Search.SearchTerm; + private void DisplayResults(string searchTerm, string version, IDictionary results, ILogger logger){ if (results.Any() && !string.IsNullOrEmpty(searchTerm) && searchTerm.Contains(KiotaSearcher.ProviderSeparator) && results.ContainsKey(searchTerm)) { var result = results.First(); DisplayInfo($"Key: {result.Key}"); @@ -60,8 +58,8 @@ private void DisplayResults(IDictionary results, ILogger l DisplayInfo($"Description: {result.Value.Description}"); DisplayInfo($"Service: {result.Value.ServiceUrl}"); DisplayInfo($"OpenAPI: {result.Value.DescriptionUrl}"); - DisplayDownloadHint(Configuration.Search.SearchTerm, Configuration.Search.Version); - DisplayShowHint(Configuration.Search.SearchTerm, Configuration.Search.Version); + DisplayDownloadHint(searchTerm, version); + DisplayShowHint(searchTerm, version); } else { var view = new TableView>() { Items = results.OrderBy(static x => x.Key).Select(static x => x).ToList(), @@ -74,7 +72,7 @@ private void DisplayResults(IDictionary results, ILogger l using var terminal = new SystemConsoleTerminal(console); var layout = new StackLayoutView { view }; console.Append(layout); - DisplaySearchHint(results.Keys.FirstOrDefault(), Configuration.Search.Version); + DisplaySearchHint(results.Keys.FirstOrDefault(), version); DisplayLoginHint(logger); DisplaySearchAddHint(); } diff --git a/src/kiota/Handlers/KiotaShowCommandHandler.cs b/src/kiota/Handlers/KiotaShowCommandHandler.cs index d37cd46a6f..a83fe2e496 100644 --- a/src/kiota/Handlers/KiotaShowCommandHandler.cs +++ b/src/kiota/Handlers/KiotaShowCommandHandler.cs @@ -33,12 +33,10 @@ public override async Task InvokeAsync(InvocationContext context) var (loggerFactory, logger) = GetLoggerAndFactory(context); - Configuration.Search.SearchTerm = searchTerm; - Configuration.Search.Version = version; Configuration.Search.ClearCache = clearCache; using (loggerFactory) { var descriptionProvided = !string.IsNullOrEmpty(openapi) && string.IsNullOrEmpty(searchTerm); - var (searchResultDescription, statusCode) = await GetDescriptionFromSearch(openapi, searchTerm, loggerFactory, logger, cancellationToken); + var (searchResultDescription, statusCode) = await GetDescriptionFromSearch(openapi, searchTerm, version, loggerFactory, logger, cancellationToken); if (statusCode.HasValue) { return statusCode.Value; } diff --git a/tests/Kiota.Builder.Tests/KiotaSearcherTests.cs b/tests/Kiota.Builder.Tests/KiotaSearcherTests.cs index 20109626bc..8dc2c31387 100644 --- a/tests/Kiota.Builder.Tests/KiotaSearcherTests.cs +++ b/tests/Kiota.Builder.Tests/KiotaSearcherTests.cs @@ -14,9 +14,9 @@ public class KiotaSearcherTests : IDisposable { private readonly HttpClient httpClient = new(); [Fact] public void DefensivePrograming() { - Assert.Throws(() => new KiotaSearcher(null, new SearchConfiguration(), httpClient, null)); - Assert.Throws(() => new KiotaSearcher(new Mock>().Object, null, httpClient, null)); - Assert.Throws(() => new KiotaSearcher(new Mock>().Object, new SearchConfiguration(), null, null)); + Assert.Throws(() => new KiotaSearcher(null, new SearchConfiguration(), httpClient, null, null)); + Assert.Throws(() => new KiotaSearcher(new Mock>().Object, null, httpClient, null, null)); + Assert.Throws(() => new KiotaSearcher(new Mock>().Object, new SearchConfiguration(), null, null, null)); Assert.Throws(() => new GitHubSearchProvider(httpClient, new Mock>().Object, false, null, null, null)); Assert.Throws(() => new GitHubSearchProvider(httpClient, null, false, new GitHubConfiguration(), null, null)); Assert.Throws(() => new GitHubSearchProvider(null, new Mock>().Object, false, new GitHubConfiguration(), null, null)); @@ -28,49 +28,44 @@ public void DefensivePrograming() { [Fact] public async Task GetsMicrosoftGraphBothVersions() { var searchConfiguration = searchConfigurationFactory; - searchConfiguration.SearchTerm = "github::microsoftgraph/msgraph-metadata"; - var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient, null); - var results = await searcher.SearchAsync(new CancellationToken()); + var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient, null, null); + var results = await searcher.SearchAsync("github::microsoftgraph/msgraph-metadata", string.Empty, new CancellationToken()); Assert.Equal(2, results.Count); } [Fact] public async Task GetsMicrosoftGraph() { var searchConfiguration = searchConfigurationFactory; - searchConfiguration.SearchTerm = "github::microsoftgraph/msgraph-metadata/graph.microsoft.com/v1.0"; - var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient, null); - var results = await searcher.SearchAsync(new CancellationToken()); + var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient, null, null); + var results = await searcher.SearchAsync("github::microsoftgraph/msgraph-metadata/graph.microsoft.com/v1.0", string.Empty, new CancellationToken()); Assert.Single(results); Assert.Equal("https://raw.githubusercontent.com/microsoftgraph/msgraph-metadata/master/openapi/v1.0/openapi.yaml", results.First().Value.DescriptionUrl.ToString()); } [Fact] public async Task GetsMicrosoftGraphBeta() { var searchConfiguration = searchConfigurationFactory; - searchConfiguration.SearchTerm = "github::microsoftgraph/msgraph-metadata/graph.microsoft.com/beta"; - var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient, null); - var results = await searcher.SearchAsync(new CancellationToken()); + var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient, null, null); + var results = await searcher.SearchAsync("github::microsoftgraph/msgraph-metadata/graph.microsoft.com/beta", string.Empty, new CancellationToken()); Assert.Single(results); Assert.Equal("https://raw.githubusercontent.com/microsoftgraph/msgraph-metadata/master/openapi/beta/openapi.yaml", results.First().Value.DescriptionUrl.ToString()); } [Fact] public async Task DoesntFailOnEmptyTerm() { - var searcher = new KiotaSearcher(new Mock>().Object, searchConfigurationFactory, httpClient, null); - var results = await searcher.SearchAsync(new CancellationToken()); + var searcher = new KiotaSearcher(new Mock>().Object, searchConfigurationFactory, httpClient, null, null); + var results = await searcher.SearchAsync(string.Empty, string.Empty, new CancellationToken()); Assert.Empty(results); } [Fact] public async Task GetsGithubFromApisGuru() { var searchConfiguration = searchConfigurationFactory; - searchConfiguration.SearchTerm = "github"; - var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient, null); - var results = await searcher.SearchAsync(new CancellationToken()); + var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient, null, null); + var results = await searcher.SearchAsync("github", string.Empty, new CancellationToken()); Assert.NotEmpty(results); } [Fact] public async Task GetsGithubFromApisGuruWithExactMatch() { var searchConfiguration = searchConfigurationFactory; - searchConfiguration.SearchTerm = "apisguru::github.com:api.github.com"; - var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient, null); - var results = await searcher.SearchAsync(new CancellationToken()); + var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient, null, null); + var results = await searcher.SearchAsync("apisguru::github.com:api.github.com", string.Empty, new CancellationToken()); Assert.Single(results); } public void Dispose() From 11e4297b40becc115a492df124725fecdcd0337a Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 25 Nov 2022 09:33:26 -0500 Subject: [PATCH 17/42] - adds failsafe when unable to access files Signed-off-by: Vincent Biret --- .../SearchProviders/GitHub/GitHubSearchProvider.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs index ed7c6118b4..2cddbf3303 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs @@ -178,6 +178,8 @@ private async Task>> GetSearchResultsFro #else _logger.LogInformation("Error while parsing the file {fileName} in {org}/{repo}", fileName, org, repo); #endif + } catch (InvalidOperationException ex) { + _logger.LogError(ex, "Error while downloading the file {fileName} in {org}/{repo}", fileName, org, repo); } return Enumerable.Empty>(); } From d65720bd45e35187ea8f9a7f3e3595084185c07a Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 25 Nov 2022 15:24:43 -0500 Subject: [PATCH 18/42] - polishes UI for github sign in Signed-off-by: Vincent Biret --- src/Kiota.Web/Shared/GitHubSignIn.fr.resx | 49 ++++++++++++++++++++- src/Kiota.Web/Shared/GitHubSignIn.razor | 46 ++++++++++++++++--- src/Kiota.Web/Shared/GitHubSignIn.razor.css | 7 +++ src/Kiota.Web/Shared/GitHubSignIn.resx | 42 ++++++++++++++++++ src/Kiota.Web/Shared/MainLayout.razor.css | 3 -- src/Kiota.Web/wwwroot/css/app.css | 10 ++++- 6 files changed, 145 insertions(+), 12 deletions(-) diff --git a/src/Kiota.Web/Shared/GitHubSignIn.fr.resx b/src/Kiota.Web/Shared/GitHubSignIn.fr.resx index cd1bd36009..cd74c2b290 100644 --- a/src/Kiota.Web/Shared/GitHubSignIn.fr.resx +++ b/src/Kiota.Web/Shared/GitHubSignIn.fr.resx @@ -58,7 +58,52 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - Show + + Se connecter à GitHub + + + Se déconnecter de GitHub + + + Ouvrez un nouvel onglet afin de créer un nouveau jeton d'accès. + + + Donnez un nom au nouveau jeton. + + + Sélectionnez le propriétaires du repository qui contient les définitions. + + + Optionel. Authentifiez vous auprès de la resource sélectionnée si demandé. + + + Sous "accès au repository", sélectionnez "Tous les repositories" ou "Uniquement les repositories sélectionnés". + + + Sous "permissions du repository" sélectionnez "contenu" puis "lecture simple". + + + Cliquez sur "Générer le jeton". + + + Copiez le jeton généré ci-dessous. + + + Le jeton est uniquement envoyé à l'API REST GitHub. + + + Jeton d'accès personnalisé + + + Connectez vous à GitHub à l'aide d'un jeton d'accès personnel + + + Open + + + Instructions to create the token + + + Add the personal access token \ No newline at end of file diff --git a/src/Kiota.Web/Shared/GitHubSignIn.razor b/src/Kiota.Web/Shared/GitHubSignIn.razor index 3568e122ce..0512acd295 100644 --- a/src/Kiota.Web/Shared/GitHubSignIn.razor +++ b/src/Kiota.Web/Shared/GitHubSignIn.razor @@ -1,8 +1,5 @@ -@using Microsoft.Kiota.Abstractions -@using Microsoft.Kiota.Abstractions.Authentication @using Kiota.Builder.Configuration @using Kiota.Web.Authentication.GitHub -@using Kiota.Builder.SearchProviders.GitHub.Authentication @using Microsoft.Fast.Components.FluentUI @using Microsoft.Extensions.Localization @inject IStringLocalizer Loc @@ -17,19 +14,56 @@ @Loc["SignIn"] }
+ +
+

@Loc["PersonalAccessTokenHeader"]

+

@Loc["PersonalAccessTokenInstructions"]

+
    +
  1. @Loc["PersonalAccessTokenInstructionsStep1"] @Loc["PersonalAccessTokenInstructionsOpen"]
  2. +
  3. @Loc["PersonalAccessTokenInstructionsStep2"]
  4. +
  5. @Loc["PersonalAccessTokenInstructionsStep3"]
  6. +
  7. @Loc["PersonalAccessTokenInstructionsStep4"]
  8. +
  9. @Loc["PersonalAccessTokenInstructionsStep5"]
  10. +
  11. @Loc["PersonalAccessTokenInstructionsStep6"]
  12. +
  13. @Loc["PersonalAccessTokenInstructionsStep7"]
  14. +
  15. @Loc["PersonalAccessTokenInstructionsStep8"]
  16. +
+ @Loc["PersonalAccessTokenDescription"] +
+

@Loc["PersonalAccessTokenAdd"]

+ @Loc["PersonalAccessToken"] + + @Loc["Save"] + @Loc["Close"] +
+
+
@code { + private bool PatSetDialogHidden { get; set; } = true; private bool IsSignedIn {get; set;} + private string? PersonalAccessToken {get; set;} + private void OnPatSetDialogDismiss() { + PatSetDialogHidden = true; + } protected override async Task OnParametersSetAsync() { IsSignedIn = await LocalStoragePatService.IsSignedInAsync(ComponentDetached); await base.OnParametersSetAsync(); } - private async Task SignIn() { - @* await LocalStoragePatService.SetPatAsync("value", ComponentDetached); *@ - //TODO Open dialog + private void SignIn() { + PatSetDialogHidden = false; + } + private async Task Save() { + if(!String.IsNullOrEmpty(PersonalAccessToken)) { + IsSignedIn = true; + await LocalStoragePatService.SetPatAsync(PersonalAccessToken, ComponentDetached); + PatSetDialogHidden = true; + PersonalAccessToken = string.Empty; + } } private async Task SignOut() { await LocalStoragePatService.SignOutAsync(ComponentDetached); + IsSignedIn = false; } } \ No newline at end of file diff --git a/src/Kiota.Web/Shared/GitHubSignIn.razor.css b/src/Kiota.Web/Shared/GitHubSignIn.razor.css index e69de29bb2..e83be593f5 100644 --- a/src/Kiota.Web/Shared/GitHubSignIn.razor.css +++ b/src/Kiota.Web/Shared/GitHubSignIn.razor.css @@ -0,0 +1,7 @@ +.gh-dialog-content { + margin: 10px; + +} +.gh-dialog-content-form { + margin-top:10px; +} \ No newline at end of file diff --git a/src/Kiota.Web/Shared/GitHubSignIn.resx b/src/Kiota.Web/Shared/GitHubSignIn.resx index d30c5fc08c..8adaa0293d 100644 --- a/src/Kiota.Web/Shared/GitHubSignIn.resx +++ b/src/Kiota.Web/Shared/GitHubSignIn.resx @@ -64,4 +64,46 @@ Sign Out from GitHub + + Open a new tab to create a personal access token. + + + Give a name to the new access token. + + + Select the resource owner for the repository containing the definitions. + + + Optional. Authenticate with the selected resource if requested. + + + Under "repository access", select "All repositories" or "Only select repositories". + + + Under "repository permissions" select "contents" to "read-only". + + + Click on "Generate Token". + + + Copy the generated token down below. + + + The personal access token is only ever sent to GitHub REST API. + + + Personal Access Token + + + Sign in to GitHub with a Personal Access Token + + + Open + + + Instructions to create the token + + + Add the personal access token + \ No newline at end of file diff --git a/src/Kiota.Web/Shared/MainLayout.razor.css b/src/Kiota.Web/Shared/MainLayout.razor.css index 31e89fd7fe..1af0130b80 100644 --- a/src/Kiota.Web/Shared/MainLayout.razor.css +++ b/src/Kiota.Web/Shared/MainLayout.razor.css @@ -10,8 +10,6 @@ main { } .top-row { - background-color: #f7f7f7; - border-bottom: 1px solid #d6d5d5; justify-content: flex-end; height: 3.5rem; display: flex; @@ -20,7 +18,6 @@ main { .top-row ::deep a, .top-row ::deep .btn-link { white-space: nowrap; - margin-left: 1.5rem; text-decoration: none; } diff --git a/src/Kiota.Web/wwwroot/css/app.css b/src/Kiota.Web/wwwroot/css/app.css index 334c9e532d..cb2ee825c5 100644 --- a/src/Kiota.Web/wwwroot/css/app.css +++ b/src/Kiota.Web/wwwroot/css/app.css @@ -1,11 +1,19 @@ html, body { - font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } h1 { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } +h2 { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; +} + +h3 { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; +} + h1:focus { outline: none; } From e51aef3a32a378219c492d1ad250ff9a62bb2fa8 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 25 Nov 2022 15:36:09 -0500 Subject: [PATCH 19/42] - adds missing alt text Signed-off-by: Vincent Biret --- src/Kiota.Web/Shared/GitHubSignIn.fr.resx | 3 +++ src/Kiota.Web/Shared/GitHubSignIn.razor | 3 ++- src/Kiota.Web/Shared/GitHubSignIn.resx | 3 +++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Kiota.Web/Shared/GitHubSignIn.fr.resx b/src/Kiota.Web/Shared/GitHubSignIn.fr.resx index cd74c2b290..815a211ac8 100644 --- a/src/Kiota.Web/Shared/GitHubSignIn.fr.resx +++ b/src/Kiota.Web/Shared/GitHubSignIn.fr.resx @@ -106,4 +106,7 @@ Add the personal access token + + Logo GitHub Octocat + \ No newline at end of file diff --git a/src/Kiota.Web/Shared/GitHubSignIn.razor b/src/Kiota.Web/Shared/GitHubSignIn.razor index 0512acd295..4596d4c4cf 100644 --- a/src/Kiota.Web/Shared/GitHubSignIn.razor +++ b/src/Kiota.Web/Shared/GitHubSignIn.razor @@ -5,7 +5,7 @@ @inject IStringLocalizer Loc @inject LocalStoragePatService LocalStoragePatService @inject KiotaConfiguration KiotaConfiguration - +  
@if(IsSignedIn) { @@ -40,6 +40,7 @@ @code { + private string SVGAltText { get => Loc["SVGAltText"]; } private bool PatSetDialogHidden { get; set; } = true; private bool IsSignedIn {get; set;} private string? PersonalAccessToken {get; set;} diff --git a/src/Kiota.Web/Shared/GitHubSignIn.resx b/src/Kiota.Web/Shared/GitHubSignIn.resx index 8adaa0293d..a6d24dd8d0 100644 --- a/src/Kiota.Web/Shared/GitHubSignIn.resx +++ b/src/Kiota.Web/Shared/GitHubSignIn.resx @@ -106,4 +106,7 @@ Add the personal access token + + GitHub Octocat logo + \ No newline at end of file From 1c8082d3a021bd4b3a0483124ab32f51cff6dfd1 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 25 Nov 2022 15:39:30 -0500 Subject: [PATCH 20/42] - code linting Signed-off-by: Vincent Biret --- src/Kiota.Builder/KiotaSearcher.cs | 2 +- .../SearchProviders/GitHub/GitHubSearchProvider.cs | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/Kiota.Builder/KiotaSearcher.cs b/src/Kiota.Builder/KiotaSearcher.cs index dbc1f267d1..6bc4685952 100644 --- a/src/Kiota.Builder/KiotaSearcher.cs +++ b/src/Kiota.Builder/KiotaSearcher.cs @@ -48,7 +48,7 @@ public async Task> SearchAsync(string searchTe return results.SelectMany(static x => x) .ToDictionary(static x => x.Key, static x => x.Value, StringComparer.OrdinalIgnoreCase); } - private async Task> SearchProviderAsync(string searchTerm, string version, ISearchProvider provider, CancellationToken cancellationToken) { + private static async Task> SearchProviderAsync(string searchTerm, string version, ISearchProvider provider, CancellationToken cancellationToken) { var providerPrefix = $"{provider.ProviderKey}{ProviderSeparator}"; var results = await provider.SearchAsync(searchTerm.Replace(providerPrefix, string.Empty), version, cancellationToken); diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs index 2cddbf3303..37aff4a59e 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubSearchProvider.cs @@ -24,8 +24,6 @@ public class GitHubSearchProvider : ISearchProvider private readonly DocumentCachingProvider documentCachingProvider; private readonly ILogger _logger; private readonly Uri _blockListUrl; - private readonly string _clientId; - private readonly Uri _appBaseUrl; private readonly IAuthenticationProvider _authenticatedAuthenticationProvider; private readonly Func> _isSignedInCallback; public GitHubSearchProvider(HttpClient httpClient, ILogger logger, bool clearCache, GitHubConfiguration configuration, IAuthenticationProvider authenticatedAuthenticationProvider, Func> isSignedInCallBack) @@ -33,10 +31,7 @@ public GitHubSearchProvider(HttpClient httpClient, ILogger logger, bool clearCac ArgumentNullException.ThrowIfNull(httpClient); ArgumentNullException.ThrowIfNull(configuration); ArgumentNullException.ThrowIfNull(configuration.BlockListUrl); - ArgumentNullException.ThrowIfNull(configuration.ApiBaseUrl); ArgumentNullException.ThrowIfNull(logger); - if(string.IsNullOrEmpty(configuration.AppId)) - throw new ArgumentOutOfRangeException(nameof(configuration)); documentCachingProvider = new DocumentCachingProvider(httpClient, logger) { ClearCache = clearCache, @@ -44,9 +39,7 @@ public GitHubSearchProvider(HttpClient httpClient, ILogger logger, bool clearCac _httpClient = httpClient; _logger = logger; _blockListUrl = configuration.BlockListUrl; - _clientId = configuration.AppId; _authenticatedAuthenticationProvider = authenticatedAuthenticationProvider; - _appBaseUrl = configuration.ApiBaseUrl; _isSignedInCallback = isSignedInCallBack; } private readonly HttpClient _httpClient; From 4176ff14128a04247c34cbd19a3e80b94998f94d Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 25 Nov 2022 15:55:44 -0500 Subject: [PATCH 21/42] - replaces svg alt by title Signed-off-by: Vincent Biret --- src/Kiota.Web/Shared/GitHubSignIn.razor | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Kiota.Web/Shared/GitHubSignIn.razor b/src/Kiota.Web/Shared/GitHubSignIn.razor index 4596d4c4cf..3c6b809532 100644 --- a/src/Kiota.Web/Shared/GitHubSignIn.razor +++ b/src/Kiota.Web/Shared/GitHubSignIn.razor @@ -5,7 +5,10 @@ @inject IStringLocalizer Loc @inject LocalStoragePatService LocalStoragePatService @inject KiotaConfiguration KiotaConfiguration - + + @Loc["SVGAltText"] + +  
@if(IsSignedIn) { @@ -40,7 +43,6 @@ @code { - private string SVGAltText { get => Loc["SVGAltText"]; } private bool PatSetDialogHidden { get; set; } = true; private bool IsSignedIn {get; set;} private string? PersonalAccessToken {get; set;} From f321f419582dafc7af53de381b3de04c2d16c52c Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Mon, 28 Nov 2022 10:37:48 -0500 Subject: [PATCH 22/42] - moves device login to a subcommand Signed-off-by: Vincent Biret --- .vscode/launch.json | 5 +++-- ...der.cs => KiotaGitHubDeviceLoginCommandHanlder.cs} | 2 +- src/kiota/KiotaHost.cs | 11 ++++++++--- 3 files changed, 12 insertions(+), 6 deletions(-) rename src/kiota/Handlers/{KiotaGitHubLoginCommandHanlder.cs => KiotaGitHubDeviceLoginCommandHanlder.cs} (97%) diff --git a/.vscode/launch.json b/.vscode/launch.json index 35468a2ca6..c95bf57c7d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -257,13 +257,14 @@ "stopAtEntry": false }, { - "name": "Launch Login (github)", + "name": "Launch Login (github - device)", "type": "coreclr", "request": "launch", "preLaunchTask": "build", "program": "${workspaceFolder}/src/kiota/bin/Debug/net7.0/kiota.dll", "args": ["login", - "github" + "github", + "device" ], "cwd": "${workspaceFolder}/src/kiota", "console": "internalConsole", diff --git a/src/kiota/Handlers/KiotaGitHubLoginCommandHanlder.cs b/src/kiota/Handlers/KiotaGitHubDeviceLoginCommandHanlder.cs similarity index 97% rename from src/kiota/Handlers/KiotaGitHubLoginCommandHanlder.cs rename to src/kiota/Handlers/KiotaGitHubDeviceLoginCommandHanlder.cs index e9f1623750..dce568882f 100644 --- a/src/kiota/Handlers/KiotaGitHubLoginCommandHanlder.cs +++ b/src/kiota/Handlers/KiotaGitHubDeviceLoginCommandHanlder.cs @@ -9,7 +9,7 @@ namespace kiota.Handlers; -internal class KiotaGitHubLoginCommandHandler : BaseKiotaCommandHandler +internal class KiotaGitHubDeviceLoginCommandHandler : BaseKiotaCommandHandler { public override async Task InvokeAsync(InvocationContext context) { diff --git a/src/kiota/KiotaHost.cs b/src/kiota/KiotaHost.cs index 52b9736730..12c944e05c 100644 --- a/src/kiota/KiotaHost.cs +++ b/src/kiota/KiotaHost.cs @@ -25,15 +25,20 @@ public RootCommand GetRootCommand() { return rootCommand; } private static Command GetGitHubLoginCommand() { + var githubLoginCommand = new Command("github", "Logs in to GitHub."); + githubLoginCommand.AddCommand(GetGitHubDeviceLoginCommand()); + return githubLoginCommand; + } + private static Command GetGitHubDeviceLoginCommand() { var logLevelOption = GetLogLevelOption(); - var githubLoginCommand = new Command("github", "Logs in to GitHub using a device code flow.") + var deviceLoginCommand = new Command("device", "Logs in to GitHub using a device code flow.") { logLevelOption, }; - githubLoginCommand.Handler = new KiotaGitHubLoginCommandHandler { + deviceLoginCommand.Handler = new KiotaGitHubDeviceLoginCommandHandler { LogLevelOption = logLevelOption, }; - return githubLoginCommand; + return deviceLoginCommand; } private static Command GetGitHubLogoutCommand() { var logLevelOption = GetLogLevelOption(); From c228597da563d278fede1f7e898ef554a52c85ea Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Mon, 28 Nov 2022 14:24:15 -0500 Subject: [PATCH 23/42] - refactors token caching Signed-off-by: Vincent Biret --- .../TempFolderCachingAccessTokenProvider.cs | 89 ------------------- .../AccessCodeResponse.cs | 0 .../AnonymousAuthenticationProvider.cs | 0 .../BaseAuthenticationProvider.cs | 0 .../Authentication/ITokenStorageService.cs | 11 +++ .../PatAuthenticationProvider.cs | 9 +- .../GitHub/Authentication}/PatProvider.cs | 8 +- .../TempFolderCachingAccessTokenProvider.cs | 37 ++++++++ .../TempFolderStorageService.cs | 70 +++++++++++++++ ....cs => LocalStorageTokenStorageService.cs} | 14 +-- .../IServiceCollectionExtensions.cs | 12 +-- src/Kiota.Web/Shared/GitHubSignIn.razor | 8 +- src/kiota/Handlers/BaseKiotaCommandHandler.cs | 12 +-- .../KiotaGitHubLogoutCommandhandler.cs | 11 +-- .../Handlers/KiotaSearchCommandHandler.cs | 6 +- 15 files changed, 164 insertions(+), 123 deletions(-) delete mode 100644 src/Kiota.Builder/SearchProviders/GitHub/Autentication/TempFolderCachingAccessTokenProvider.cs rename src/Kiota.Builder/SearchProviders/GitHub/{Autentication => Authentication}/AccessCodeResponse.cs (100%) rename src/Kiota.Builder/SearchProviders/GitHub/{Autentication => Authentication}/AnonymousAuthenticationProvider.cs (100%) rename src/Kiota.Builder/SearchProviders/GitHub/{Autentication => Authentication}/BaseAuthenticationProvider.cs (100%) create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/Authentication/ITokenStorageService.cs rename src/{Kiota.Web/Authentication/GitHub => Kiota.Builder/SearchProviders/GitHub/Authentication}/PatAuthenticationProvider.cs (74%) rename src/{Kiota.Web/Authentication/GitHub => Kiota.Builder/SearchProviders/GitHub/Authentication}/PatProvider.cs (68%) create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/Authentication/TempFolderCachingAccessTokenProvider.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/Authentication/TempFolderStorageService.cs rename src/Kiota.Web/Authentication/GitHub/{LocalStoragePatService.cs => LocalStorageTokenStorageService.cs} (51%) diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/TempFolderCachingAccessTokenProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/Autentication/TempFolderCachingAccessTokenProvider.cs deleted file mode 100644 index 9ee8930753..0000000000 --- a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/TempFolderCachingAccessTokenProvider.cs +++ /dev/null @@ -1,89 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Kiota.Abstractions.Authentication; -using Microsoft.Extensions.Logging; - -namespace Kiota.Builder.SearchProviders.GitHub.Authentication; - -public class TempFolderCachingAccessTokenProvider : IAccessTokenProvider -{ - public required IAccessTokenProvider Concrete - { - get; init; - } - public required ILogger Logger - { - get; init; - } - public required Uri ApiBaseUrl { get; init; } - public required string AppId { get; init; } - public AllowedHostsValidator AllowedHostsValidator => Concrete.AllowedHostsValidator; - public async Task GetAuthorizationTokenAsync(Uri uri, Dictionary additionalAuthenticationContext = null, CancellationToken cancellationToken = default) - { - var result = await GetTokenFromCacheAsync(uri, cancellationToken).ConfigureAwait(false); - if (string.IsNullOrEmpty(result)) - { - Logger.LogInformation("Token not found in cache, requesting a new one"); - result = await Concrete.GetAuthorizationTokenAsync(uri, additionalAuthenticationContext, cancellationToken).ConfigureAwait(false); - await CacheTokenAsync(uri, result, cancellationToken).ConfigureAwait(false); - } - return result; - } - private string GetTokenCacheFilePath() => GetTokenCacheFilePath(ApiBaseUrl); - private string GetTokenCacheFilePath(Uri uri) => Path.Combine(Path.GetTempPath(), "kiota", "auth", $"{AppId}-{uri.Host}.txt"); - private async Task CacheTokenAsync(Uri uri, string result, CancellationToken cancellationToken) - { - try - { - var target = GetTokenCacheFilePath(uri); - var directory = Path.GetDirectoryName(target); - if (!Directory.Exists(directory)) - Directory.CreateDirectory(directory); - await File.WriteAllTextAsync(target, result, cancellationToken).ConfigureAwait(false); - } - catch (Exception ex) - { - Logger.LogWarning(ex, "Error while writing token to cache."); - } - } - private async Task GetTokenFromCacheAsync(Uri uri, CancellationToken cancellationToken) - { - try { - var target = GetTokenCacheFilePath(uri); - if(!IsCachedTokenPresent()) - return null; - var result = await File.ReadAllTextAsync(target, cancellationToken).ConfigureAwait(false); - return result; - } catch (Exception ex) { - Logger.LogWarning(ex, "Error while reading token from cache."); - return null; - } - } - public bool Logout() { - //no try-catch as we want the exception to bubble up to the command - var target = GetTokenCacheFilePath(); - if(!IsCachedTokenPresent()) - return false; - File.Delete(target); - return true; - } - public bool IsCachedTokenPresent() { - try { - var target = GetTokenCacheFilePath(); - if (!File.Exists(target)) - return false; - var fileDate = File.GetLastWriteTime(target); - if(fileDate > DateTime.Now.AddMonths(6)) { - Logout(); - return false; - } - return true; - } catch (Exception ex) { - Logger.LogWarning(ex, "Error while reading token from cache."); - return false; - } - } -} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/AccessCodeResponse.cs b/src/Kiota.Builder/SearchProviders/GitHub/Authentication/AccessCodeResponse.cs similarity index 100% rename from src/Kiota.Builder/SearchProviders/GitHub/Autentication/AccessCodeResponse.cs rename to src/Kiota.Builder/SearchProviders/GitHub/Authentication/AccessCodeResponse.cs diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/AnonymousAuthenticationProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/Authentication/AnonymousAuthenticationProvider.cs similarity index 100% rename from src/Kiota.Builder/SearchProviders/GitHub/Autentication/AnonymousAuthenticationProvider.cs rename to src/Kiota.Builder/SearchProviders/GitHub/Authentication/AnonymousAuthenticationProvider.cs diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Autentication/BaseAuthenticationProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/Authentication/BaseAuthenticationProvider.cs similarity index 100% rename from src/Kiota.Builder/SearchProviders/GitHub/Autentication/BaseAuthenticationProvider.cs rename to src/Kiota.Builder/SearchProviders/GitHub/Authentication/BaseAuthenticationProvider.cs diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Authentication/ITokenStorageService.cs b/src/Kiota.Builder/SearchProviders/GitHub/Authentication/ITokenStorageService.cs new file mode 100644 index 0000000000..9cce89347c --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/Authentication/ITokenStorageService.cs @@ -0,0 +1,11 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Kiota.Builder.SearchProviders.GitHub.Authentication; + +public interface ITokenStorageService { + Task GetTokenAsync(CancellationToken cancellationToken); + Task SetTokenAsync(string value, CancellationToken cancellationToken); + Task IsTokenPresentAsync(CancellationToken cancellationToken); + Task DeleteTokenAsync(CancellationToken cancellationToken); +} diff --git a/src/Kiota.Web/Authentication/GitHub/PatAuthenticationProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/Authentication/PatAuthenticationProvider.cs similarity index 74% rename from src/Kiota.Web/Authentication/GitHub/PatAuthenticationProvider.cs rename to src/Kiota.Builder/SearchProviders/GitHub/Authentication/PatAuthenticationProvider.cs index 0139298f52..87482dab74 100644 --- a/src/Kiota.Web/Authentication/GitHub/PatAuthenticationProvider.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/Authentication/PatAuthenticationProvider.cs @@ -1,6 +1,11 @@ -using Kiota.Builder.SearchProviders.GitHub.Authentication; -namespace Kiota.Web.Authentication.GitHub; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; + +namespace Kiota.Builder.SearchProviders.GitHub.Authentication; public class PatAuthenticationProvider : BaseAuthenticationProvider { public PatAuthenticationProvider(string clientId, string scope, IEnumerable validHosts, ILogger logger, Func> GetPATFromStorageCallback) : diff --git a/src/Kiota.Web/Authentication/GitHub/PatProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/Authentication/PatProvider.cs similarity index 68% rename from src/Kiota.Web/Authentication/GitHub/PatProvider.cs rename to src/Kiota.Builder/SearchProviders/GitHub/Authentication/PatProvider.cs index ceb4a4ecdc..8d8268f1ba 100644 --- a/src/Kiota.Web/Authentication/GitHub/PatProvider.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/Authentication/PatProvider.cs @@ -1,13 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; using Microsoft.Kiota.Abstractions.Authentication; -namespace Kiota.Web.Authentication.GitHub; +namespace Kiota.Builder.SearchProviders.GitHub.Authentication; public class PatProvider : IAccessTokenProvider { public AllowedHostsValidator AllowedHostsValidator { get; set; } = new(); public required Func> GetPATFromStorageCallback { get; init; } - public Task GetAuthorizationTokenAsync(Uri uri, Dictionary? additionalAuthenticationContext = null, CancellationToken cancellationToken = default) + public Task GetAuthorizationTokenAsync(Uri uri, Dictionary additionalAuthenticationContext = null, CancellationToken cancellationToken = default) { if("https".Equals(uri.Scheme, StringComparison.OrdinalIgnoreCase) && AllowedHostsValidator.IsUrlHostValid(uri)) return GetPATFromStorageCallback(cancellationToken); diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Authentication/TempFolderCachingAccessTokenProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/Authentication/TempFolderCachingAccessTokenProvider.cs new file mode 100644 index 0000000000..8cfd12d528 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/Authentication/TempFolderCachingAccessTokenProvider.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Kiota.Abstractions.Authentication; +using Microsoft.Extensions.Logging; + +namespace Kiota.Builder.SearchProviders.GitHub.Authentication; + +public class TempFolderCachingAccessTokenProvider : IAccessTokenProvider +{ + public required IAccessTokenProvider Concrete { get; init; } + public required ILogger Logger { get; init; } + public required Uri ApiBaseUrl { get; init; } + public required string AppId { get; init; } + public AllowedHostsValidator AllowedHostsValidator => Concrete.AllowedHostsValidator; + public readonly Lazy TokenStorageService; + public TempFolderCachingAccessTokenProvider() + { + TokenStorageService = new Lazy(() => new TempFolderTokenStorageService { + Logger = Logger, + FileName = $"{AppId}-{ApiBaseUrl.Host}", + }); + } + public async Task GetAuthorizationTokenAsync(Uri uri, Dictionary additionalAuthenticationContext = null, CancellationToken cancellationToken = default) + { + var result = await TokenStorageService.Value.GetTokenAsync(cancellationToken).ConfigureAwait(false); + if (string.IsNullOrEmpty(result)) + { + Logger.LogInformation("Token not found in cache, requesting a new one"); + result = await Concrete.GetAuthorizationTokenAsync(uri, additionalAuthenticationContext, cancellationToken).ConfigureAwait(false); + await TokenStorageService.Value.SetTokenAsync(result, cancellationToken).ConfigureAwait(false); + } + return result; + } + +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Authentication/TempFolderStorageService.cs b/src/Kiota.Builder/SearchProviders/GitHub/Authentication/TempFolderStorageService.cs new file mode 100644 index 0000000000..21b2617879 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/Authentication/TempFolderStorageService.cs @@ -0,0 +1,70 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; + +namespace Kiota.Builder.SearchProviders.GitHub.Authentication; + +public class TempFolderTokenStorageService : ITokenStorageService { + public required ILogger Logger { get; init;} + public required string FileName { get; init; } + private string GetTokenCacheFilePath() => Path.Combine(Path.GetTempPath(), "kiota", "auth", $"{FileName}.txt"); + public async Task GetTokenAsync(CancellationToken cancellationToken) + { + try { + var target = GetTokenCacheFilePath(); + if(!await IsTokenPresentAsync(cancellationToken)) + return null; + var result = await File.ReadAllTextAsync(target, cancellationToken).ConfigureAwait(false); + return result; + } catch (Exception ex) { + Logger.LogWarning(ex, "Error while reading token from cache."); + return null; + } + } + + public async Task SetTokenAsync(string value, CancellationToken cancellationToken) + { + try + { + var target = GetTokenCacheFilePath(); + var directory = Path.GetDirectoryName(target); + if (!Directory.Exists(directory)) + Directory.CreateDirectory(directory); + await File.WriteAllTextAsync(target, value, cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.LogWarning(ex, "Error while writing token to cache."); + } + } + + public async Task IsTokenPresentAsync(CancellationToken cancellationToken) + { + try { + var target = GetTokenCacheFilePath(); + if (!File.Exists(target)) + return false; + var fileDate = File.GetLastWriteTime(target); + if(fileDate > DateTime.Now.AddMonths(6)) { + await DeleteTokenAsync(cancellationToken); + return false; + } + return true; + } catch (Exception ex) { + Logger.LogWarning(ex, "Error while reading token from cache."); + return false; + } + } + + public async Task DeleteTokenAsync(CancellationToken cancellationToken) + { + //no try-catch as we want the exception to bubble up to the command + var target = GetTokenCacheFilePath(); + if(!await IsTokenPresentAsync(cancellationToken)) + return false; + File.Delete(target); + return true; + } +} diff --git a/src/Kiota.Web/Authentication/GitHub/LocalStoragePatService.cs b/src/Kiota.Web/Authentication/GitHub/LocalStorageTokenStorageService.cs similarity index 51% rename from src/Kiota.Web/Authentication/GitHub/LocalStoragePatService.cs rename to src/Kiota.Web/Authentication/GitHub/LocalStorageTokenStorageService.cs index 94c697b3d6..a47a0ef3de 100644 --- a/src/Kiota.Web/Authentication/GitHub/LocalStoragePatService.cs +++ b/src/Kiota.Web/Authentication/GitHub/LocalStorageTokenStorageService.cs @@ -1,21 +1,23 @@ using Blazored.LocalStorage; +using Kiota.Builder.SearchProviders.GitHub.Authentication; namespace Kiota.Web.Authentication.GitHub; -public class LocalStoragePatService { +public class LocalStorageTokenStorageService : ITokenStorageService { private const string PATKey = "github-pat"; public required ILocalStorageService LocalStorageService { get; init; } - public async Task GetPatAsync(CancellationToken cancellationToken) { + public async Task GetTokenAsync(CancellationToken cancellationToken) { return await LocalStorageService.GetItemAsync(PATKey, cancellationToken).ConfigureAwait(false); } - public async Task SetPatAsync(string value, CancellationToken cancellationToken) { + public async Task SetTokenAsync(string value, CancellationToken cancellationToken) { await LocalStorageService.SetItemAsync(PATKey, value, cancellationToken).ConfigureAwait(false); } - public async Task IsSignedInAsync(CancellationToken cancellationToken) { - return !string.IsNullOrEmpty(await GetPatAsync(cancellationToken).ConfigureAwait(false)); + public async Task IsTokenPresentAsync(CancellationToken cancellationToken) { + return !string.IsNullOrEmpty(await GetTokenAsync(cancellationToken).ConfigureAwait(false)); } - public async Task SignOutAsync(CancellationToken cancellationToken) { + public async Task DeleteTokenAsync(CancellationToken cancellationToken) { await LocalStorageService.RemoveItemAsync(PATKey, cancellationToken).ConfigureAwait(false); + return true; } } diff --git a/src/Kiota.Web/Extensions/IServiceCollectionExtensions.cs b/src/Kiota.Web/Extensions/IServiceCollectionExtensions.cs index 25d90666d9..97b9bb422a 100644 --- a/src/Kiota.Web/Extensions/IServiceCollectionExtensions.cs +++ b/src/Kiota.Web/Extensions/IServiceCollectionExtensions.cs @@ -53,9 +53,9 @@ public static void AddBrowserCodeAuthentication(this IServiceCollection services } public static void AddPatAuthentication(this IServiceCollection services) { services.AddBlazoredLocalStorage(); - services.AddScoped(sp => { + services.AddScoped(sp => { var localStorage = sp.GetRequiredService(); - return new LocalStoragePatService { + return new LocalStorageTokenStorageService { LocalStorageService = localStorage, }; }); @@ -67,8 +67,8 @@ public static void AddPatAuthentication(this IServiceCollection services) { new string[] { configObject.Search.GitHub.ApiBaseUrl.Host }, sp.GetRequiredService().CreateLogger(), async (c) => { - var patService = sp.GetRequiredService(); - var patValue = await patService.GetPatAsync(c).ConfigureAwait(false); + var patService = sp.GetRequiredService(); + var patValue = await patService.GetTokenAsync(c).ConfigureAwait(false); if(!string.IsNullOrEmpty(patValue)) return patValue; return string.Empty; @@ -79,12 +79,12 @@ public static void AddPatAuthentication(this IServiceCollection services) { public static void AddSearchService(this IServiceCollection services) { services.AddScoped(sp => { var configObject = sp.GetRequiredService(); - var patService = sp.GetRequiredService(); + var patService = sp.GetRequiredService(); return new KiotaSearcher(sp.GetRequiredService().CreateLogger(), configObject.Search, sp.GetRequiredService(), sp.GetRequiredService(), - (c) => patService.IsSignedInAsync(c)); + (c) => patService.IsTokenPresentAsync(c)); }); } } diff --git a/src/Kiota.Web/Shared/GitHubSignIn.razor b/src/Kiota.Web/Shared/GitHubSignIn.razor index 3c6b809532..d2d24a2c5d 100644 --- a/src/Kiota.Web/Shared/GitHubSignIn.razor +++ b/src/Kiota.Web/Shared/GitHubSignIn.razor @@ -3,7 +3,7 @@ @using Microsoft.Fast.Components.FluentUI @using Microsoft.Extensions.Localization @inject IStringLocalizer Loc -@inject LocalStoragePatService LocalStoragePatService +@inject ITokenStorageService TokenStorageService @inject KiotaConfiguration KiotaConfiguration @Loc["SVGAltText"] @@ -50,7 +50,7 @@ PatSetDialogHidden = true; } protected override async Task OnParametersSetAsync() { - IsSignedIn = await LocalStoragePatService.IsSignedInAsync(ComponentDetached); + IsSignedIn = await TokenStorageService.IsTokenPresentAsync(ComponentDetached); await base.OnParametersSetAsync(); } private void SignIn() { @@ -59,13 +59,13 @@ private async Task Save() { if(!String.IsNullOrEmpty(PersonalAccessToken)) { IsSignedIn = true; - await LocalStoragePatService.SetPatAsync(PersonalAccessToken, ComponentDetached); + await TokenStorageService.SetTokenAsync(PersonalAccessToken, ComponentDetached); PatSetDialogHidden = true; PersonalAccessToken = string.Empty; } } private async Task SignOut() { - await LocalStoragePatService.SignOutAsync(ComponentDetached); + await TokenStorageService.DeleteTokenAsync(ComponentDetached); IsSignedIn = false; } diff --git a/src/kiota/Handlers/BaseKiotaCommandHandler.cs b/src/kiota/Handlers/BaseKiotaCommandHandler.cs index 8c4e34aacd..57c0df5126 100644 --- a/src/kiota/Handlers/BaseKiotaCommandHandler.cs +++ b/src/kiota/Handlers/BaseKiotaCommandHandler.cs @@ -19,7 +19,7 @@ namespace kiota.Handlers; internal abstract class BaseKiotaCommandHandler : ICommandHandler { - protected TempFolderCachingAccessTokenProvider GitHubAuthenticationCachingProvider(ILogger logger) => new(){ + protected TempFolderCachingAccessTokenProvider GitHubDeviceAuthenticationProvider(ILogger logger) => new(){ Logger = logger, ApiBaseUrl = Configuration.Search.GitHub.ApiBaseUrl, Concrete = null, @@ -39,8 +39,8 @@ internal abstract class BaseKiotaCommandHandler : ICommandHandler }); private const string GitHubScope = "repo"; protected Func> GetIsGitHubSignedInCallback(ILogger logger) => (cancellationToken) => { - var provider = GitHubAuthenticationCachingProvider(logger); - return Task.FromResult(provider.IsCachedTokenPresent()); + var provider = GitHubDeviceAuthenticationProvider(logger); + return provider.TokenStorageService.Value.IsTokenPresentAsync(cancellationToken); }; protected IAuthenticationProvider GetAuthenticationProvider(ILogger logger) => new DeviceCodeAuthenticationProvider(Configuration.Search.GitHub.AppId, @@ -186,9 +186,9 @@ protected void DisplaySearchBasicHint() { DisplayHint("Hint: use the search command to search for an OpenAPI description.", "Example: kiota search "); } - protected void DisplayLoginHint(ILogger logger) { - var cachingConfig = GitHubAuthenticationCachingProvider(logger); - if(!cachingConfig.IsCachedTokenPresent()) { + protected async Task DisplayLoginHint(ILogger logger, CancellationToken token) { + var authProvider = GitHubDeviceAuthenticationProvider(logger); + if(!await authProvider.TokenStorageService.Value.IsTokenPresentAsync(token)) { DisplayHint("Hint: use the login command to sign in to GitHub and access private OpenAPI descriptions.", "Example: kiota login github"); } diff --git a/src/kiota/Handlers/KiotaGitHubLogoutCommandhandler.cs b/src/kiota/Handlers/KiotaGitHubLogoutCommandhandler.cs index 0ebb34e2e0..98512bced3 100644 --- a/src/kiota/Handlers/KiotaGitHubLogoutCommandhandler.cs +++ b/src/kiota/Handlers/KiotaGitHubLogoutCommandhandler.cs @@ -9,25 +9,26 @@ namespace kiota.Handlers; internal class KiotaGitHubLogoutCommandHandler : BaseKiotaCommandHandler { - public override Task InvokeAsync(InvocationContext context) + public override async Task InvokeAsync(InvocationContext context) { + CancellationToken cancellationToken = (CancellationToken)context.BindingContext.GetService(typeof(CancellationToken)); var (loggerFactory, logger) = GetLoggerAndFactory(context); using (loggerFactory) { try { - var cachingProvider = GitHubAuthenticationCachingProvider(logger); - var result = cachingProvider.Logout(); + var authProvider = GitHubDeviceAuthenticationProvider(logger); + var result = await authProvider.TokenStorageService.Value.DeleteTokenAsync(cancellationToken); if(result) DisplaySuccess("Logged out successfully."); else DisplaySuccess("Already logged out."); - return Task.FromResult(0); + return 0; } catch (Exception ex) { #if DEBUG logger.LogCritical(ex, "error logging out from GitHub: {exceptionMessage}", ex.Message); throw; // so debug tools go straight to the source of the exception when attached #else logger.LogCritical("error logging out from GitHub: {exceptionMessage}", ex.Message); - return Task.FromResult(1); + return 1; #endif } } diff --git a/src/kiota/Handlers/KiotaSearchCommandHandler.cs b/src/kiota/Handlers/KiotaSearchCommandHandler.cs index ee41766947..a75b5f4328 100644 --- a/src/kiota/Handlers/KiotaSearchCommandHandler.cs +++ b/src/kiota/Handlers/KiotaSearchCommandHandler.cs @@ -37,7 +37,7 @@ public override async Task InvokeAsync(InvocationContext context) try { var results = await new KiotaSearcher(logger, Configuration.Search, httpClient, GetAuthenticationProvider(logger), GetIsGitHubSignedInCallback(logger)) .SearchAsync(searchTerm, version, cancellationToken); - DisplayResults(searchTerm, version, results, logger); + await DisplayResults(searchTerm, version, results, logger, cancellationToken); return 0; } catch (Exception ex) { #if DEBUG @@ -50,7 +50,7 @@ public override async Task InvokeAsync(InvocationContext context) } } } - private void DisplayResults(string searchTerm, string version, IDictionary results, ILogger logger){ + private async Task DisplayResults(string searchTerm, string version, IDictionary results, ILogger logger, CancellationToken cancellationToken){ if (results.Any() && !string.IsNullOrEmpty(searchTerm) && searchTerm.Contains(KiotaSearcher.ProviderSeparator) && results.ContainsKey(searchTerm)) { var result = results.First(); DisplayInfo($"Key: {result.Key}"); @@ -73,7 +73,7 @@ private void DisplayResults(string searchTerm, string version, IDictionary Date: Mon, 28 Nov 2022 14:39:39 -0500 Subject: [PATCH 24/42] - moves device code provide to right folder Signed-off-by: Vincent Biret --- .../Authentication/GitHub/{ => DeviceCode}/AcessTokenProvider.cs | 0 .../GitHub/{ => DeviceCode}/DeviceCodeAuthenticationProvider.cs | 0 .../Authentication/GitHub/{ => DeviceCode}/DeviceCodeResponse.cs | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename src/kiota/Authentication/GitHub/{ => DeviceCode}/AcessTokenProvider.cs (100%) rename src/kiota/Authentication/GitHub/{ => DeviceCode}/DeviceCodeAuthenticationProvider.cs (100%) rename src/kiota/Authentication/GitHub/{ => DeviceCode}/DeviceCodeResponse.cs (100%) diff --git a/src/kiota/Authentication/GitHub/AcessTokenProvider.cs b/src/kiota/Authentication/GitHub/DeviceCode/AcessTokenProvider.cs similarity index 100% rename from src/kiota/Authentication/GitHub/AcessTokenProvider.cs rename to src/kiota/Authentication/GitHub/DeviceCode/AcessTokenProvider.cs diff --git a/src/kiota/Authentication/GitHub/DeviceCodeAuthenticationProvider.cs b/src/kiota/Authentication/GitHub/DeviceCode/DeviceCodeAuthenticationProvider.cs similarity index 100% rename from src/kiota/Authentication/GitHub/DeviceCodeAuthenticationProvider.cs rename to src/kiota/Authentication/GitHub/DeviceCode/DeviceCodeAuthenticationProvider.cs diff --git a/src/kiota/Authentication/GitHub/DeviceCodeResponse.cs b/src/kiota/Authentication/GitHub/DeviceCode/DeviceCodeResponse.cs similarity index 100% rename from src/kiota/Authentication/GitHub/DeviceCodeResponse.cs rename to src/kiota/Authentication/GitHub/DeviceCode/DeviceCodeResponse.cs From 4b4c47a98e22c12a859b50d968f7bc5b41bf55d0 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Mon, 28 Nov 2022 15:37:35 -0500 Subject: [PATCH 25/42] - linting Signed-off-by: Vincent Biret --- src/kiota/KiotaHost.cs | 2 +- src/kiota/Program.cs | 2 +- tests/Kiota.Tests/KiotaHostTests.cs | 18 +++++++++--------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/kiota/KiotaHost.cs b/src/kiota/KiotaHost.cs index 12c944e05c..4c76502ea3 100644 --- a/src/kiota/KiotaHost.cs +++ b/src/kiota/KiotaHost.cs @@ -12,7 +12,7 @@ namespace kiota; public class KiotaHost { - public RootCommand GetRootCommand() { + public static RootCommand GetRootCommand() { var rootCommand = new RootCommand(); rootCommand.AddCommand(GetGenerateCommand()); rootCommand.AddCommand(GetSearchCommand()); diff --git a/src/kiota/Program.cs b/src/kiota/Program.cs index f02b75276e..ac595ce985 100644 --- a/src/kiota/Program.cs +++ b/src/kiota/Program.cs @@ -7,7 +7,7 @@ static class Program { static Task Main(string[] args) { - return new KiotaHost().GetRootCommand().InvokeAsync(args); + return KiotaHost.GetRootCommand().InvokeAsync(args); } } } diff --git a/tests/Kiota.Tests/KiotaHostTests.cs b/tests/Kiota.Tests/KiotaHostTests.cs index 6b09de666f..07f112c038 100644 --- a/tests/Kiota.Tests/KiotaHostTests.cs +++ b/tests/Kiota.Tests/KiotaHostTests.cs @@ -10,38 +10,38 @@ public class KiotaHostTests { [Fact] public async Task ThrowsOnInvalidOutputPath() { - await new KiotaHost().GetRootCommand().InvokeAsync(new[] { "generate", "-o", "A:\\doesnotexist" }); + await KiotaHost.GetRootCommand().InvokeAsync(new[] { "generate", "-o", "A:\\doesnotexist" }); } [Fact] public async Task ThrowsOnInvalidInputPath() { - await new KiotaHost().GetRootCommand().InvokeAsync(new[] { "generate", "-d", "A:\\doesnotexist" }); + await KiotaHost.GetRootCommand().InvokeAsync(new[] { "generate", "-d", "A:\\doesnotexist" }); } [Fact] public async Task ThrowsOnInvalidInputUrl() { - await new KiotaHost().GetRootCommand().InvokeAsync(new[] { "generate", "-d", "https://nonexistentdomain56a535ba-bda6-405e-b5e2-ef5f11bf1003.net/doesnotexist" }); + await KiotaHost.GetRootCommand().InvokeAsync(new[] { "generate", "-d", "https://nonexistentdomain56a535ba-bda6-405e-b5e2-ef5f11bf1003.net/doesnotexist" }); } [Fact] public async Task ThrowsOnInvalidLanguage() { - await new KiotaHost().GetRootCommand().InvokeAsync(new[] { "generate", "-l", "Pascal" }); + await KiotaHost.GetRootCommand().InvokeAsync(new[] { "generate", "-l", "Pascal" }); } [Fact] public async Task ThrowsOnInvalidLogLevel() { - await new KiotaHost().GetRootCommand().InvokeAsync(new[] { "generate", "--ll", "Dangerous" }); + await KiotaHost.GetRootCommand().InvokeAsync(new[] { "generate", "--ll", "Dangerous" }); } [Fact] public async Task ThrowsOnInvalidClassName() { - await new KiotaHost().GetRootCommand().InvokeAsync(new[] { "generate", "-c", ".Graph" }); + await KiotaHost.GetRootCommand().InvokeAsync(new[] { "generate", "-c", ".Graph" }); } [Fact] public async Task AcceptsDeserializers() { - await new KiotaHost().GetRootCommand().InvokeAsync(new[] { "generate", "--ds", "Kiota.Tests.TestData.TestDeserializer" }); + await KiotaHost.GetRootCommand().InvokeAsync(new[] { "generate", "--ds", "Kiota.Tests.TestData.TestDeserializer" }); } [Fact] public async Task AcceptsSerializers() { - await new KiotaHost().GetRootCommand().InvokeAsync(new[] { "generate", "-s", "Kiota.Tests.TestData.TestSerializer" }); + await KiotaHost.GetRootCommand().InvokeAsync(new[] { "generate", "-s", "Kiota.Tests.TestData.TestSerializer" }); } [Fact] public async Task ThrowsOnInvalidSearchTerm() { - await new KiotaHost().GetRootCommand().InvokeAsync(new[] { "search" }); + await KiotaHost.GetRootCommand().InvokeAsync(new[] { "search" }); } } From 2ba3371466d7fb05ab3ee852772f435ca8273c6f Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Mon, 28 Nov 2022 15:50:25 -0500 Subject: [PATCH 26/42] - adds pat sign in option for CLI Signed-off-by: Vincent Biret --- .../PatAuthenticationProvider.cs | 8 ++-- .../GitHub/Authentication/PatProvider.cs | 5 +- .../IServiceCollectionExtensions.cs | 8 +--- .../KiotaGitHubPatLoginCommandHandler.cs | 46 +++++++++++++++++++ src/kiota/KiotaHost.cs | 18 ++++++++ 5 files changed, 70 insertions(+), 15 deletions(-) create mode 100644 src/kiota/Handlers/KiotaGitHubPatLoginCommandHandler.cs diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Authentication/PatAuthenticationProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/Authentication/PatAuthenticationProvider.cs index 87482dab74..056dfaaef1 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/Authentication/PatAuthenticationProvider.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/Authentication/PatAuthenticationProvider.cs @@ -1,19 +1,17 @@ using System; using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using Microsoft.Extensions.Logging; namespace Kiota.Builder.SearchProviders.GitHub.Authentication; public class PatAuthenticationProvider : BaseAuthenticationProvider { - public PatAuthenticationProvider(string clientId, string scope, IEnumerable validHosts, ILogger logger, Func> GetPATFromStorageCallback) : + public PatAuthenticationProvider(string clientId, string scope, IEnumerable validHosts, ILogger logger, ITokenStorageService StorageService) : base(clientId, scope, validHosts, logger, (clientId, scope, validHosts) => new PatProvider { - GetPATFromStorageCallback = GetPATFromStorageCallback, + StorageService = StorageService, AllowedHostsValidator = new (validHosts), }, false) { - ArgumentNullException.ThrowIfNull(GetPATFromStorageCallback); + ArgumentNullException.ThrowIfNull(StorageService); } } diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Authentication/PatProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/Authentication/PatProvider.cs index 8d8268f1ba..b823b8f6d4 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/Authentication/PatProvider.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/Authentication/PatProvider.cs @@ -9,12 +9,11 @@ namespace Kiota.Builder.SearchProviders.GitHub.Authentication; public class PatProvider : IAccessTokenProvider { public AllowedHostsValidator AllowedHostsValidator { get; set; } = new(); - public required Func> GetPATFromStorageCallback { get; init; } - + public required ITokenStorageService StorageService { get; init; } public Task GetAuthorizationTokenAsync(Uri uri, Dictionary additionalAuthenticationContext = null, CancellationToken cancellationToken = default) { if("https".Equals(uri.Scheme, StringComparison.OrdinalIgnoreCase) && AllowedHostsValidator.IsUrlHostValid(uri)) - return GetPATFromStorageCallback(cancellationToken); + return StorageService.GetTokenAsync(cancellationToken); return Task.FromResult(string.Empty); } } diff --git a/src/Kiota.Web/Extensions/IServiceCollectionExtensions.cs b/src/Kiota.Web/Extensions/IServiceCollectionExtensions.cs index 97b9bb422a..fa6dbbc517 100644 --- a/src/Kiota.Web/Extensions/IServiceCollectionExtensions.cs +++ b/src/Kiota.Web/Extensions/IServiceCollectionExtensions.cs @@ -66,13 +66,7 @@ public static void AddPatAuthentication(this IServiceCollection services) { "repo", new string[] { configObject.Search.GitHub.ApiBaseUrl.Host }, sp.GetRequiredService().CreateLogger(), - async (c) => { - var patService = sp.GetRequiredService(); - var patValue = await patService.GetTokenAsync(c).ConfigureAwait(false); - if(!string.IsNullOrEmpty(patValue)) - return patValue; - return string.Empty; - } + sp.GetRequiredService() ); }); } diff --git a/src/kiota/Handlers/KiotaGitHubPatLoginCommandHandler.cs b/src/kiota/Handlers/KiotaGitHubPatLoginCommandHandler.cs new file mode 100644 index 0000000000..ad0aac381a --- /dev/null +++ b/src/kiota/Handlers/KiotaGitHubPatLoginCommandHandler.cs @@ -0,0 +1,46 @@ +using System; +using System.CommandLine; +using System.CommandLine.Invocation; +using System.Threading; +using System.Threading.Tasks; +using Kiota.Builder.SearchProviders.GitHub.Authentication; +using Microsoft.Extensions.Logging; + +namespace kiota.Handlers; +internal class KiotaGitHubPatLoginCommandHandler : BaseKiotaCommandHandler { + public Option PatOption { get; set; } + public override async Task InvokeAsync(InvocationContext context) + { + CancellationToken cancellationToken = (CancellationToken)context.BindingContext.GetService(typeof(CancellationToken)); + string pat = context.ParseResult.GetValueForOption(PatOption); + var (loggerFactory, logger) = GetLoggerAndFactory(context); + using (loggerFactory) { + try { + return await LoginAsync(logger, pat, cancellationToken); + } catch (Exception ex) { + #if DEBUG + logger.LogCritical(ex, "error signing in to GitHub: {exceptionMessage}", ex.Message); + throw; // so debug tools go straight to the source of the exception when attached + #else + logger.LogCritical("error signing in to GitHub: {exceptionMessage}", ex.Message); + return 1; + #endif + } + } + } + private async Task LoginAsync(ILogger logger, string patValue, CancellationToken cancellationToken) { + if(string.IsNullOrEmpty(patValue)) { + logger.LogCritical("no personal access token provided"); + return 1; + } + var tokenStorageService = new TempFolderTokenStorageService { + Logger = logger, + FileName = "pat-api.github.com" + }; + await tokenStorageService.SetTokenAsync(patValue, cancellationToken).ConfigureAwait(false); + DisplaySuccess("Authentication successful."); + DisplaySearchBasicHint(); + DisplayGitHubLogoutHint(); + return 0; + } +} diff --git a/src/kiota/KiotaHost.cs b/src/kiota/KiotaHost.cs index 4c76502ea3..be627fb9fc 100644 --- a/src/kiota/KiotaHost.cs +++ b/src/kiota/KiotaHost.cs @@ -27,6 +27,7 @@ public static RootCommand GetRootCommand() { private static Command GetGitHubLoginCommand() { var githubLoginCommand = new Command("github", "Logs in to GitHub."); githubLoginCommand.AddCommand(GetGitHubDeviceLoginCommand()); + githubLoginCommand.AddCommand(GetGitHubPatLoginCommand()); return githubLoginCommand; } private static Command GetGitHubDeviceLoginCommand() { @@ -40,6 +41,23 @@ private static Command GetGitHubDeviceLoginCommand() { }; return deviceLoginCommand; } + private static Command GetGitHubPatLoginCommand() { + var logLevelOption = GetLogLevelOption(); + var patOption = new Option("--pat", "The personal access token to use to authenticate to GitHub.") + { + IsRequired = true + }; + var deviceLoginCommand = new Command("pat", "Logs in to GitHub using a Personal Access Token.") + { + logLevelOption, + patOption, + }; + deviceLoginCommand.Handler = new KiotaGitHubPatLoginCommandHandler { + LogLevelOption = logLevelOption, + PatOption = patOption, + }; + return deviceLoginCommand; + } private static Command GetGitHubLogoutCommand() { var logLevelOption = GetLogLevelOption(); var githubLogoutCommand = new Command("github", "Logs out of GitHub.") { From 04f71f025997c63fbd9531f74963cc4124cd2257 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Mon, 28 Nov 2022 16:15:41 -0500 Subject: [PATCH 27/42] - enables nullable for cli project - uses required keyword for options Signed-off-by: Vincent Biret --- .../GitHub/DeviceCode/AcessTokenProvider.cs | 23 +++++--- .../GitHub/DeviceCode/DeviceCodeResponse.cs | 6 +- src/kiota/Handlers/BaseKiotaCommandHandler.cs | 12 ++-- .../Handlers/KiotaDownloadCommandHandler.cs | 20 +++---- .../Handlers/KiotaGenerationCommandHandler.cs | 58 +++++++++---------- .../KiotaGitHubDeviceLoginCommandHanlder.cs | 2 +- .../KiotaGitHubLogoutCommandhandler.cs | 2 +- .../KiotaGitHubPatLoginCommandHandler.cs | 6 +- src/kiota/Handlers/KiotaInfoCommandHandler.cs | 18 +++--- .../Handlers/KiotaSearchCommandHandler.cs | 10 ++-- src/kiota/Handlers/KiotaShowCommandHandler.cs | 26 ++++----- .../Handlers/KiotaUpdateCommandHandler.cs | 12 ++-- src/kiota/kiota.csproj | 1 + 13 files changed, 101 insertions(+), 95 deletions(-) diff --git a/src/kiota/Authentication/GitHub/DeviceCode/AcessTokenProvider.cs b/src/kiota/Authentication/GitHub/DeviceCode/AcessTokenProvider.cs index dc7527b95e..1dbd18aaef 100644 --- a/src/kiota/Authentication/GitHub/DeviceCode/AcessTokenProvider.cs +++ b/src/kiota/Authentication/GitHub/DeviceCode/AcessTokenProvider.cs @@ -17,7 +17,7 @@ public class AccessTokenProvider : IAccessTokenProvider public required string Scope { get; init; } public required HttpClient HttpClient {get; init;} internal string BaseLoginUrl { get; init; } = "https://github.com/login"; - public Task GetAuthorizationTokenAsync(Uri uri, Dictionary additionalAuthenticationContext = null, CancellationToken cancellationToken = default) { + public Task GetAuthorizationTokenAsync(Uri uri, Dictionary? additionalAuthenticationContext = null, CancellationToken cancellationToken = default) { if(!AllowedHostsValidator.IsUrlHostValid(uri)) return Task.FromResult(string.Empty); if(!uri.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase)) @@ -28,11 +28,14 @@ public Task GetAuthorizationTokenAsync(Uri uri, Dictionary GetAuthorizationTokenInternalAsync(CancellationToken cancellationToken ) { var deviceCodeResponse = await GetDeviceCodeAsync(cancellationToken); - MessageCallback(deviceCodeResponse.VerificationUri, deviceCodeResponse.UserCode); - var tokenResponse = await PollForTokenAsync(deviceCodeResponse, cancellationToken); - return tokenResponse.AccessToken; + if (!string.IsNullOrEmpty(deviceCodeResponse?.UserCode) && deviceCodeResponse.VerificationUri != null) { + MessageCallback(deviceCodeResponse.VerificationUri, deviceCodeResponse.UserCode); + var tokenResponse = await PollForTokenAsync(deviceCodeResponse, cancellationToken); + return tokenResponse?.AccessToken ?? string.Empty; + } + return string.Empty; } - private async Task PollForTokenAsync(GitHubDeviceCodeResponse deviceCodeResponse, CancellationToken cancellationToken) + private async Task PollForTokenAsync(GitHubDeviceCodeResponse deviceCodeResponse, CancellationToken cancellationToken) { var timeOutTask = Task.Delay(TimeSpan.FromSeconds(deviceCodeResponse.ExpiresInSeconds), cancellationToken); var pollTask = Task.Run(async () => { @@ -49,8 +52,10 @@ private async Task PollForTokenAsync(GitHubDeviceCodeRespons throw new TimeoutException("The device code has expired."); return await pollTask; } - private async Task GetTokenAsync(GitHubDeviceCodeResponse deviceCodeResponse, CancellationToken cancellationToken) + private async Task GetTokenAsync(GitHubDeviceCodeResponse deviceCodeResponse, CancellationToken cancellationToken) { + if(string.IsNullOrEmpty(deviceCodeResponse.DeviceCode)) + return null; using var tokenRequest = new HttpRequestMessage(HttpMethod.Post, $"{BaseLoginUrl}/oauth/access_token") { Content = new FormUrlEncodedContent(new Dictionary { { "client_id", ClientId }, @@ -63,15 +68,15 @@ private async Task GetTokenAsync(GitHubDeviceCodeResponse de tokenResponse.EnsureSuccessStatusCode(); var tokenContent = await tokenResponse.Content.ReadAsStringAsync(cancellationToken); var result = JsonSerializer.Deserialize(tokenContent); - if ("authorization_pending".Equals(result.Error, StringComparison.OrdinalIgnoreCase)) + if ("authorization_pending".Equals(result?.Error, StringComparison.OrdinalIgnoreCase)) return null; - else if (!string.IsNullOrEmpty(result.Error)) + else if (!string.IsNullOrEmpty(result?.Error)) throw new InvalidOperationException($"Error while getting token: {result.Error} - {result.ErrorDescription}"); else return result; } - private async Task GetDeviceCodeAsync(CancellationToken cancellationToken) { + private async Task GetDeviceCodeAsync(CancellationToken cancellationToken) { using var codeRequest = new HttpRequestMessage(HttpMethod.Post, $"{BaseLoginUrl}/device/code") { Content = new FormUrlEncodedContent(new Dictionary { { "client_id", ClientId }, diff --git a/src/kiota/Authentication/GitHub/DeviceCode/DeviceCodeResponse.cs b/src/kiota/Authentication/GitHub/DeviceCode/DeviceCodeResponse.cs index bb1f013ee3..de3a40ec66 100644 --- a/src/kiota/Authentication/GitHub/DeviceCode/DeviceCodeResponse.cs +++ b/src/kiota/Authentication/GitHub/DeviceCode/DeviceCodeResponse.cs @@ -5,11 +5,11 @@ namespace kiota.Authentication.GitHub.DeviceCode; internal class GitHubDeviceCodeResponse { [JsonPropertyName("device_code")] - public string DeviceCode { get; set; } + public string? DeviceCode { get; set; } [JsonPropertyName("user_code")] - public string UserCode { get; set; } + public string? UserCode { get; set; } [JsonPropertyName("verification_uri")] - public Uri VerificationUri { get; set; } + public Uri? VerificationUri { get; set; } [JsonPropertyName("expires_in")] public uint ExpiresInSeconds { get; set; } [JsonPropertyName("interval")] diff --git a/src/kiota/Handlers/BaseKiotaCommandHandler.cs b/src/kiota/Handlers/BaseKiotaCommandHandler.cs index 57c0df5126..ee2948cf85 100644 --- a/src/kiota/Handlers/BaseKiotaCommandHandler.cs +++ b/src/kiota/Handlers/BaseKiotaCommandHandler.cs @@ -26,7 +26,7 @@ internal abstract class BaseKiotaCommandHandler : ICommandHandler AppId = Configuration.Search.GitHub.AppId, }; internal static readonly HttpClient httpClient = new(); - public Option LogLevelOption { get;set; } + public required Option LogLevelOption { get;init; } protected KiotaConfiguration Configuration { get => ConfigurationFactory.Value; } private readonly Lazy ConfigurationFactory = new (() => { var builder = new ConfigurationBuilder(); @@ -125,7 +125,7 @@ protected void DisplayDownloadHint(string searchTerm, string version) { $"Example: kiota download {searchTerm} -v {version} -o "; DisplayHint("Hint: use kiota download to download the OpenAPI description.", example); } - protected void DisplayShowHint(string searchTerm, string version, string path = null) { + protected void DisplayShowHint(string searchTerm, string version, string? path = null) { var example = path switch { _ when !string.IsNullOrEmpty(path) => $"Example: kiota show -d {path}", _ when string.IsNullOrEmpty(version) => $"Example: kiota show -k {searchTerm}", @@ -133,7 +133,7 @@ _ when string.IsNullOrEmpty(version) => $"Example: kiota show -k {searchTerm}", }; DisplayHint("Hint: use kiota show to display a tree of paths present in the OpenAPI description.", example); } - protected void DisplayShowAdvancedHint(string searchTerm, string version, IEnumerable includePaths, IEnumerable excludePaths, string path = null) { + protected void DisplayShowAdvancedHint(string searchTerm, string version, IEnumerable includePaths, IEnumerable excludePaths, string? path = null) { if(!includePaths.Any() && !excludePaths.Any()) { var example = path switch { _ when !string.IsNullOrEmpty(path) => $"Example: kiota show -d {path} --include-path **/foo", @@ -146,7 +146,7 @@ _ when string.IsNullOrEmpty(version) => $"Example: kiota show -k {searchTerm} -- protected void DisplaySearchAddHint() { DisplayHint("Hint: add your own API to the search result https://aka.ms/kiota/addapi."); } - protected void DisplaySearchHint(string firstKey, string version) { + protected void DisplaySearchHint(string? firstKey, string version) { if (!string.IsNullOrEmpty(firstKey)) { var example = string.IsNullOrEmpty(version) ? $"Example: kiota search {firstKey}" : @@ -155,8 +155,8 @@ protected void DisplaySearchHint(string firstKey, string version) { } } protected void DisplayGenerateHint(string path, IEnumerable includedPaths, IEnumerable excludedPaths) { - var includedPathsSuffix = ((includedPaths?.Any() ?? false)? " -i " : string.Empty) + string.Join(" -i ", includedPaths); - var excludedPathsSuffix = ((excludedPaths?.Any() ?? false)? " -e " : string.Empty) + string.Join(" -e ", excludedPaths); + var includedPathsSuffix = (includedPaths.Any()? " -i " : string.Empty) + string.Join(" -i ", includedPaths); + var excludedPathsSuffix = (excludedPaths.Any()? " -e " : string.Empty) + string.Join(" -e ", excludedPaths); var example = $"Example: kiota generate -l -o -d {path}{includedPathsSuffix}{excludedPathsSuffix}"; DisplayHint("Hint: use kiota generate to generate a client for the OpenAPI description.", example); } diff --git a/src/kiota/Handlers/KiotaDownloadCommandHandler.cs b/src/kiota/Handlers/KiotaDownloadCommandHandler.cs index 5269bddd21..04eeff524e 100644 --- a/src/kiota/Handlers/KiotaDownloadCommandHandler.cs +++ b/src/kiota/Handlers/KiotaDownloadCommandHandler.cs @@ -17,19 +17,19 @@ namespace kiota.Handlers; internal class KiotaDownloadCommandHandler : BaseKiotaCommandHandler { - public Argument SearchTermArgument { get; set; } - public Option VersionOption { get; set; } - public Option OutputPathOption { get; set; } - public Option ClearCacheOption { get; set; } - public Option CleanOutputOption { get; set; } + public required Argument SearchTermArgument { get; init; } + public required Option VersionOption { get; init; } + public required Option OutputPathOption { get; init; } + public required Option ClearCacheOption { get; init; } + public required Option CleanOutputOption { get; init; } public override async Task InvokeAsync(InvocationContext context) { string searchTerm = context.ParseResult.GetValueForArgument(SearchTermArgument); - string version = context.ParseResult.GetValueForOption(VersionOption); - string outputPath = context.ParseResult.GetValueForOption(OutputPathOption); + string version = context.ParseResult.GetValueForOption(VersionOption) ?? string.Empty; + string outputPath = context.ParseResult.GetValueForOption(OutputPathOption) ?? string.Empty; bool cleanOutput = context.ParseResult.GetValueForOption(CleanOutputOption); bool clearCache = context.ParseResult.GetValueForOption(ClearCacheOption); - CancellationToken cancellationToken = (CancellationToken)context.BindingContext.GetService(typeof(CancellationToken)); + CancellationToken cancellationToken = context.BindingContext.GetService(typeof(CancellationToken)) is CancellationToken token ? token : CancellationToken.None; Configuration.Download.ClearCache = clearCache; Configuration.Download.CleanOutput = cleanOutput; @@ -93,8 +93,8 @@ private async Task SaveResultsAsync(string searchTerm, string version, IDic return (path, 1); } } - if(!Directory.Exists(Path.GetDirectoryName(path))) - Directory.CreateDirectory(Path.GetDirectoryName(path)); + if(Path.GetDirectoryName(path) is string directoryName && !Directory.Exists(directoryName)) + Directory.CreateDirectory(directoryName); var cacheProvider = new DocumentCachingProvider(httpClient, logger) { ClearCache = true, diff --git a/src/kiota/Handlers/KiotaGenerationCommandHandler.cs b/src/kiota/Handlers/KiotaGenerationCommandHandler.cs index b3c5984d8f..4f04c814ba 100644 --- a/src/kiota/Handlers/KiotaGenerationCommandHandler.cs +++ b/src/kiota/Handlers/KiotaGenerationCommandHandler.cs @@ -16,34 +16,34 @@ namespace kiota.Handlers; internal class KiotaGenerationCommandHandler : BaseKiotaCommandHandler { - public Option DescriptionOption { get;set; } - public Option OutputOption { get;set; } - public Option LanguageOption { get;set; } - public Option ClassOption { get;set; } - public Option NamespaceOption { get;set; } - public Option BackingStoreOption { get;set; } - public Option AdditionalDataOption { get;set; } - public Option> SerializerOption { get;set; } - public Option> DeserializerOption { get;set; } - public Option CleanOutputOption { get;set; } - public Option> StructuredMimeTypesOption { get;set; } + public required Option DescriptionOption { get;init; } + public required Option OutputOption { get;init; } + public required Option LanguageOption { get;init; } + public required Option ClassOption { get;init; } + public required Option NamespaceOption { get;init; } + public required Option BackingStoreOption { get;init; } + public required Option AdditionalDataOption { get;init; } + public required Option> SerializerOption { get;init; } + public required Option> DeserializerOption { get;init; } + public required Option CleanOutputOption { get;init; } + public required Option> StructuredMimeTypesOption { get;init; } public override async Task InvokeAsync(InvocationContext context) { - string output = context.ParseResult.GetValueForOption(OutputOption); + string output = context.ParseResult.GetValueForOption(OutputOption) ?? string.Empty; GenerationLanguage language = context.ParseResult.GetValueForOption(LanguageOption); - string openapi = context.ParseResult.GetValueForOption(DescriptionOption); + string openapi = context.ParseResult.GetValueForOption(DescriptionOption) ?? string.Empty; bool backingStore = context.ParseResult.GetValueForOption(BackingStoreOption); bool clearCache = context.ParseResult.GetValueForOption(ClearCacheOption); bool includeAdditionalData = context.ParseResult.GetValueForOption(AdditionalDataOption); - string className = context.ParseResult.GetValueForOption(ClassOption); - string namespaceName = context.ParseResult.GetValueForOption(NamespaceOption); - List serializer = context.ParseResult.GetValueForOption(SerializerOption); - List deserializer = context.ParseResult.GetValueForOption(DeserializerOption); - List includePatterns = context.ParseResult.GetValueForOption(IncludePatternsOption); - List excludePatterns = context.ParseResult.GetValueForOption(ExcludePatternsOption); + string className = context.ParseResult.GetValueForOption(ClassOption) ?? string.Empty; + string namespaceName = context.ParseResult.GetValueForOption(NamespaceOption) ?? string.Empty; + List serializer = context.ParseResult.GetValueForOption(SerializerOption) ?? new List(); + List deserializer = context.ParseResult.GetValueForOption(DeserializerOption) ?? new List(); + List includePatterns = context.ParseResult.GetValueForOption(IncludePatternsOption) ?? new List(); + List excludePatterns = context.ParseResult.GetValueForOption(ExcludePatternsOption) ?? new List(); bool cleanOutput = context.ParseResult.GetValueForOption(CleanOutputOption); - List structuredMimeTypes = context.ParseResult.GetValueForOption(StructuredMimeTypesOption); - CancellationToken cancellationToken = (CancellationToken)context.BindingContext.GetService(typeof(CancellationToken)); + List structuredMimeTypes = context.ParseResult.GetValueForOption(StructuredMimeTypesOption) ?? new List(); + CancellationToken cancellationToken = context.BindingContext.GetService(typeof(CancellationToken)) is CancellationToken token ? token : CancellationToken.None; AssignIfNotNullOrEmpty(output, (c, s) => c.OutputPath = s); AssignIfNotNullOrEmpty(openapi, (c, s) => c.OpenAPIFilePath = s); AssignIfNotNullOrEmpty(className, (c, s) => c.ClientClassName = s); @@ -51,15 +51,15 @@ public override async Task InvokeAsync(InvocationContext context) Configuration.Generation.UsesBackingStore = backingStore; Configuration.Generation.IncludeAdditionalData = includeAdditionalData; Configuration.Generation.Language = language; - if(serializer?.Any() ?? false) + if(serializer.Any()) Configuration.Generation.Serializers = serializer.Select(x => x.TrimQuotes()).ToHashSet(StringComparer.OrdinalIgnoreCase); - if(deserializer?.Any() ?? false) + if(deserializer.Any()) Configuration.Generation.Deserializers = deserializer.Select(x => x.TrimQuotes()).ToHashSet(StringComparer.OrdinalIgnoreCase); - if(includePatterns?.Any() ?? false) + if(includePatterns.Any()) Configuration.Generation.IncludePatterns = includePatterns.Select(x => x.TrimQuotes()).ToHashSet(StringComparer.OrdinalIgnoreCase); - if(excludePatterns?.Any() ?? false) + if(excludePatterns.Any()) Configuration.Generation.ExcludePatterns = excludePatterns.Select(x => x.TrimQuotes()).ToHashSet(StringComparer.OrdinalIgnoreCase); - if(structuredMimeTypes?.Any() ?? false) + if(structuredMimeTypes.Any()) Configuration.Generation.StructuredMimeTypes = structuredMimeTypes.SelectMany(x => x.Split(new[] {' '})) .Select(x => x.TrimQuotes()) .ToHashSet(StringComparer.OrdinalIgnoreCase); @@ -95,7 +95,7 @@ public override async Task InvokeAsync(InvocationContext context) } } } - public Option> IncludePatternsOption { get; set; } - public Option> ExcludePatternsOption { get; set; } - public Option ClearCacheOption { get; set; } + public required Option> IncludePatternsOption { get; init; } + public required Option> ExcludePatternsOption { get; init; } + public required Option ClearCacheOption { get; init; } } diff --git a/src/kiota/Handlers/KiotaGitHubDeviceLoginCommandHanlder.cs b/src/kiota/Handlers/KiotaGitHubDeviceLoginCommandHanlder.cs index dce568882f..514c6f0205 100644 --- a/src/kiota/Handlers/KiotaGitHubDeviceLoginCommandHanlder.cs +++ b/src/kiota/Handlers/KiotaGitHubDeviceLoginCommandHanlder.cs @@ -13,7 +13,7 @@ internal class KiotaGitHubDeviceLoginCommandHandler : BaseKiotaCommandHandler { public override async Task InvokeAsync(InvocationContext context) { - CancellationToken cancellationToken = (CancellationToken)context.BindingContext.GetService(typeof(CancellationToken)); + CancellationToken cancellationToken = context.BindingContext.GetService(typeof(CancellationToken)) is CancellationToken token ? token : CancellationToken.None; var (loggerFactory, logger) = GetLoggerAndFactory(context); using (loggerFactory) { try { diff --git a/src/kiota/Handlers/KiotaGitHubLogoutCommandhandler.cs b/src/kiota/Handlers/KiotaGitHubLogoutCommandhandler.cs index 98512bced3..bfb62634af 100644 --- a/src/kiota/Handlers/KiotaGitHubLogoutCommandhandler.cs +++ b/src/kiota/Handlers/KiotaGitHubLogoutCommandhandler.cs @@ -11,7 +11,7 @@ internal class KiotaGitHubLogoutCommandHandler : BaseKiotaCommandHandler { public override async Task InvokeAsync(InvocationContext context) { - CancellationToken cancellationToken = (CancellationToken)context.BindingContext.GetService(typeof(CancellationToken)); + CancellationToken cancellationToken = context.BindingContext.GetService(typeof(CancellationToken)) is CancellationToken token ? token : CancellationToken.None; var (loggerFactory, logger) = GetLoggerAndFactory(context); using (loggerFactory) { try { diff --git a/src/kiota/Handlers/KiotaGitHubPatLoginCommandHandler.cs b/src/kiota/Handlers/KiotaGitHubPatLoginCommandHandler.cs index ad0aac381a..e50fc527d2 100644 --- a/src/kiota/Handlers/KiotaGitHubPatLoginCommandHandler.cs +++ b/src/kiota/Handlers/KiotaGitHubPatLoginCommandHandler.cs @@ -8,11 +8,11 @@ namespace kiota.Handlers; internal class KiotaGitHubPatLoginCommandHandler : BaseKiotaCommandHandler { - public Option PatOption { get; set; } + public required Option PatOption { get; init; } public override async Task InvokeAsync(InvocationContext context) { - CancellationToken cancellationToken = (CancellationToken)context.BindingContext.GetService(typeof(CancellationToken)); - string pat = context.ParseResult.GetValueForOption(PatOption); + CancellationToken cancellationToken = context.BindingContext.GetService(typeof(CancellationToken)) is CancellationToken token ? token : CancellationToken.None; + string pat = context.ParseResult.GetValueForOption(PatOption) ?? string.Empty; var (loggerFactory, logger) = GetLoggerAndFactory(context); using (loggerFactory) { try { diff --git a/src/kiota/Handlers/KiotaInfoCommandHandler.cs b/src/kiota/Handlers/KiotaInfoCommandHandler.cs index ceeb453eae..2b452b6fb2 100644 --- a/src/kiota/Handlers/KiotaInfoCommandHandler.cs +++ b/src/kiota/Handlers/KiotaInfoCommandHandler.cs @@ -14,19 +14,19 @@ namespace kiota.Handlers; internal class KiotaInfoCommandHandler : KiotaSearchBasedCommandHandler { - public Option DescriptionOption { get;set; } - public Option ClearCacheOption { get; set; } - public Option SearchTermOption { get; set; } - public Option VersionOption { get; set; } - public Option GenerationLanguage { get; set; } + public required Option DescriptionOption { get;init; } + public required Option ClearCacheOption { get; init; } + public required Option SearchTermOption { get; init; } + public required Option VersionOption { get; init; } + public required Option GenerationLanguage { get; init; } public override async Task InvokeAsync(InvocationContext context) { - string openapi = context.ParseResult.GetValueForOption(DescriptionOption); + string openapi = context.ParseResult.GetValueForOption(DescriptionOption) ?? string.Empty; bool clearCache = context.ParseResult.GetValueForOption(ClearCacheOption); - string searchTerm = context.ParseResult.GetValueForOption(SearchTermOption); - string version = context.ParseResult.GetValueForOption(VersionOption); + string searchTerm = context.ParseResult.GetValueForOption(SearchTermOption) ?? string.Empty; + string version = context.ParseResult.GetValueForOption(VersionOption) ?? string.Empty; GenerationLanguage? language = context.ParseResult.GetValueForOption(GenerationLanguage); - CancellationToken cancellationToken = (CancellationToken)context.BindingContext.GetService(typeof(CancellationToken)); + CancellationToken cancellationToken = context.BindingContext.GetService(typeof(CancellationToken)) is CancellationToken token ? token : CancellationToken.None; var (loggerFactory, logger) = GetLoggerAndFactory(context); Configuration.Search.ClearCache = clearCache; using (loggerFactory) { diff --git a/src/kiota/Handlers/KiotaSearchCommandHandler.cs b/src/kiota/Handlers/KiotaSearchCommandHandler.cs index a75b5f4328..d4355d0c84 100644 --- a/src/kiota/Handlers/KiotaSearchCommandHandler.cs +++ b/src/kiota/Handlers/KiotaSearchCommandHandler.cs @@ -17,15 +17,15 @@ namespace kiota.Handlers; internal class KiotaSearchCommandHandler : BaseKiotaCommandHandler { - public Argument SearchTermArgument { get; set; } - public Option ClearCacheOption { get; set; } - public Option VersionOption { get; set; } + public required Argument SearchTermArgument { get; init; } + public required Option ClearCacheOption { get; init; } + public required Option VersionOption { get; init; } public override async Task InvokeAsync(InvocationContext context) { string searchTerm = context.ParseResult.GetValueForArgument(SearchTermArgument); - string version = context.ParseResult.GetValueForOption(VersionOption); + string version = context.ParseResult.GetValueForOption(VersionOption) ?? string.Empty; bool clearCache = context.ParseResult.GetValueForOption(ClearCacheOption); - CancellationToken cancellationToken = (CancellationToken)context.BindingContext.GetService(typeof(CancellationToken)); + CancellationToken cancellationToken = context.BindingContext.GetService(typeof(CancellationToken)) is CancellationToken token ? token : CancellationToken.None; Configuration.Search.ClearCache = clearCache; diff --git a/src/kiota/Handlers/KiotaShowCommandHandler.cs b/src/kiota/Handlers/KiotaShowCommandHandler.cs index a83fe2e496..acd4daa3ed 100644 --- a/src/kiota/Handlers/KiotaShowCommandHandler.cs +++ b/src/kiota/Handlers/KiotaShowCommandHandler.cs @@ -13,23 +13,23 @@ namespace kiota.Handlers; internal class KiotaShowCommandHandler : KiotaSearchBasedCommandHandler { - public Option DescriptionOption { get;set; } - public Option SearchTermOption { get; set; } - public Option VersionOption { get; set; } - public Option MaxDepthOption { get; set; } - public Option> IncludePatternsOption { get; set; } - public Option> ExcludePatternsOption { get; set; } - public Option ClearCacheOption { get; set; } + public required Option DescriptionOption { get;init; } + public required Option SearchTermOption { get; init; } + public required Option VersionOption { get; init; } + public required Option MaxDepthOption { get; init; } + public required Option> IncludePatternsOption { get; init; } + public required Option> ExcludePatternsOption { get; init; } + public required Option ClearCacheOption { get; init; } public override async Task InvokeAsync(InvocationContext context) { - string openapi = context.ParseResult.GetValueForOption(DescriptionOption); - string searchTerm = context.ParseResult.GetValueForOption(SearchTermOption); - string version = context.ParseResult.GetValueForOption(VersionOption); + string openapi = context.ParseResult.GetValueForOption(DescriptionOption) ?? string.Empty; + string searchTerm = context.ParseResult.GetValueForOption(SearchTermOption) ?? string.Empty; + string version = context.ParseResult.GetValueForOption(VersionOption) ?? string.Empty; uint maxDepth = context.ParseResult.GetValueForOption(MaxDepthOption); - List includePatterns = context.ParseResult.GetValueForOption(IncludePatternsOption); - List excludePatterns = context.ParseResult.GetValueForOption(ExcludePatternsOption); + List includePatterns = context.ParseResult.GetValueForOption(IncludePatternsOption) ?? new List(); + List excludePatterns = context.ParseResult.GetValueForOption(ExcludePatternsOption) ?? new List(); bool clearCache = context.ParseResult.GetValueForOption(ClearCacheOption); - CancellationToken cancellationToken = (CancellationToken)context.BindingContext.GetService(typeof(CancellationToken)); + CancellationToken cancellationToken = context.BindingContext.GetService(typeof(CancellationToken)) is CancellationToken token ? token : CancellationToken.None; var (loggerFactory, logger) = GetLoggerAndFactory(context); diff --git a/src/kiota/Handlers/KiotaUpdateCommandHandler.cs b/src/kiota/Handlers/KiotaUpdateCommandHandler.cs index b76af04b8f..af08ae2a1b 100644 --- a/src/kiota/Handlers/KiotaUpdateCommandHandler.cs +++ b/src/kiota/Handlers/KiotaUpdateCommandHandler.cs @@ -12,14 +12,14 @@ namespace kiota.Handlers; internal class KiotaUpdateCommandHandler : BaseKiotaCommandHandler { - public Option OutputOption { get;set; } - public Option CleanOutputOption { get;set; } - public Option ClearCacheOption { get; set; } + public required Option OutputOption { get;init; } + public required Option CleanOutputOption { get;init; } + public required Option ClearCacheOption { get; init; } public override async Task InvokeAsync(InvocationContext context) { - string output = context.ParseResult.GetValueForOption(OutputOption); + string output = context.ParseResult.GetValueForOption(OutputOption) ?? string.Empty; bool clearCache = context.ParseResult.GetValueForOption(ClearCacheOption); bool cleanOutput = context.ParseResult.GetValueForOption(CleanOutputOption); - CancellationToken cancellationToken = (CancellationToken)context.BindingContext.GetService(typeof(CancellationToken)); + CancellationToken cancellationToken = context.BindingContext.GetService(typeof(CancellationToken)) is CancellationToken token ? token : CancellationToken.None; AssignIfNotNullOrEmpty(output, (c, s) => c.OutputPath = s); var searchPath = GetAbsolutePath(output); var lockService = new LockManagementService(); @@ -36,7 +36,7 @@ public override async Task InvokeAsync(InvocationContext context) { var locks = await Task.WhenAll(lockFileDirectoryPaths.Select(x => lockService.GetLockFromDirectoryAsync(x, cancellationToken) .ContinueWith(t => (lockInfo: t.Result, lockDirectoryPath: x), cancellationToken))); var configurations = locks.Select(x => { - var config = Configuration.Generation.Clone() as GenerationConfiguration; + var config = Configuration.Generation.Clone() is GenerationConfiguration c ? c : throw new InvalidOperationException("failed to clone the configuration"); x.lockInfo.UpdateGenerationConfigurationFromLock(config); config.OutputPath = x.lockDirectoryPath; return config; diff --git a/src/kiota/kiota.csproj b/src/kiota/kiota.csproj index a883236515..8e6923bf1a 100644 --- a/src/kiota/kiota.csproj +++ b/src/kiota/kiota.csproj @@ -25,6 +25,7 @@ OpenAPI .NET CSharp TypeScript Java Go PHP Python REST API true ..\Microsoft.OpenApi.snk + enable From bb0ca63cfe5e7aaf57628873bae8b5be0e5f368d Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Mon, 28 Nov 2022 16:47:53 -0500 Subject: [PATCH 28/42] - adds pat sign in command --- src/kiota/Handlers/BaseKiotaCommandHandler.cs | 41 ++++++++++++++++--- .../Handlers/KiotaDownloadCommandHandler.cs | 4 +- .../KiotaGitHubLogoutCommandhandler.cs | 7 ++-- .../KiotaGitHubPatLoginCommandHandler.cs | 6 +-- .../Handlers/KiotaSeachBasedCommandHandler.cs | 4 +- .../Handlers/KiotaSearchCommandHandler.cs | 4 +- 6 files changed, 46 insertions(+), 20 deletions(-) diff --git a/src/kiota/Handlers/BaseKiotaCommandHandler.cs b/src/kiota/Handlers/BaseKiotaCommandHandler.cs index ee2948cf85..8ffd573788 100644 --- a/src/kiota/Handlers/BaseKiotaCommandHandler.cs +++ b/src/kiota/Handlers/BaseKiotaCommandHandler.cs @@ -19,12 +19,16 @@ namespace kiota.Handlers; internal abstract class BaseKiotaCommandHandler : ICommandHandler { - protected TempFolderCachingAccessTokenProvider GitHubDeviceAuthenticationProvider(ILogger logger) => new(){ + protected TempFolderCachingAccessTokenProvider GetGitHubDeviceStorageService(ILogger logger) => new(){ Logger = logger, ApiBaseUrl = Configuration.Search.GitHub.ApiBaseUrl, Concrete = null, AppId = Configuration.Search.GitHub.AppId, }; + protected static TempFolderTokenStorageService GetGitHubPatStorageService(ILogger logger) => new() { + Logger = logger, + FileName = "pat-api.github.com" + }; internal static readonly HttpClient httpClient = new(); public required Option LogLevelOption { get;init; } protected KiotaConfiguration Configuration { get => ConfigurationFactory.Value; } @@ -38,17 +42,41 @@ internal abstract class BaseKiotaCommandHandler : ICommandHandler return configObject; }); private const string GitHubScope = "repo"; - protected Func> GetIsGitHubSignedInCallback(ILogger logger) => (cancellationToken) => { - var provider = GitHubDeviceAuthenticationProvider(logger); + private Func> GetIsGitHubDeviceSignedInCallback(ILogger logger) => (cancellationToken) => { + var provider = GetGitHubDeviceStorageService(logger); return provider.TokenStorageService.Value.IsTokenPresentAsync(cancellationToken); }; - protected IAuthenticationProvider GetAuthenticationProvider(ILogger logger) => + private Func> GetIsGitHubPatSignedInCallback(ILogger logger) => (cancellationToken) => { + var provider = GetGitHubPatStorageService(logger); + return provider.IsTokenPresentAsync(cancellationToken); + }; + private IAuthenticationProvider GetGitHubAuthenticationProvider(ILogger logger) => new DeviceCodeAuthenticationProvider(Configuration.Search.GitHub.AppId, GitHubScope, new List { Configuration.Search.GitHub.ApiBaseUrl.Host }, httpClient, DisplayGitHubDeviceCodeLoginMessage, logger); + private IAuthenticationProvider GetGitHubPatAuthenticationProvider(ILogger logger) => + new PatAuthenticationProvider(Configuration.Search.GitHub.AppId, + GitHubScope, + new List { Configuration.Search.GitHub.ApiBaseUrl.Host }, + logger, + GetGitHubPatStorageService(logger)); + protected async Task GetKiotaSearcher(ILoggerFactory loggerFactory, CancellationToken cancellationToken) { + var logger = loggerFactory.CreateLogger(); + var deviceCodeSignInCallback = GetIsGitHubDeviceSignedInCallback(logger); + var patSignInCallBack = GetIsGitHubPatSignedInCallback(logger); + var isDeviceCodeSignedIn = await deviceCodeSignInCallback(cancellationToken).ConfigureAwait(false); + var isPatSignedIn = await patSignInCallBack(cancellationToken).ConfigureAwait(false); + var (provider, callback) = (isDeviceCodeSignedIn, isPatSignedIn) switch { + (true, _) => (GetGitHubAuthenticationProvider(logger), deviceCodeSignInCallback), + (_, true) => (GetGitHubPatAuthenticationProvider(logger), patSignInCallBack), + (_, _) => (null, null) + }; + + return new KiotaSearcher(logger, Configuration.Search, httpClient, provider, callback); + } public int Invoke(InvocationContext context) { return InvokeAsync(context).GetAwaiter().GetResult(); @@ -187,8 +215,9 @@ protected void DisplaySearchBasicHint() { "Example: kiota search "); } protected async Task DisplayLoginHint(ILogger logger, CancellationToken token) { - var authProvider = GitHubDeviceAuthenticationProvider(logger); - if(!await authProvider.TokenStorageService.Value.IsTokenPresentAsync(token)) { + var deviceCodeAuthProvider = GetGitHubDeviceStorageService(logger); + var patStorage = GetGitHubPatStorageService(logger); + if(!await deviceCodeAuthProvider.TokenStorageService.Value.IsTokenPresentAsync(token) && !await patStorage.IsTokenPresentAsync(token)) { DisplayHint("Hint: use the login command to sign in to GitHub and access private OpenAPI descriptions.", "Example: kiota login github"); } diff --git a/src/kiota/Handlers/KiotaDownloadCommandHandler.cs b/src/kiota/Handlers/KiotaDownloadCommandHandler.cs index 04eeff524e..e0cac221bc 100644 --- a/src/kiota/Handlers/KiotaDownloadCommandHandler.cs +++ b/src/kiota/Handlers/KiotaDownloadCommandHandler.cs @@ -42,8 +42,8 @@ public override async Task InvokeAsync(InvocationContext context) logger.LogTrace("configuration: {configuration}", JsonSerializer.Serialize(Configuration)); try { - var results = await new KiotaSearcher(logger, Configuration.Search, httpClient, GetAuthenticationProvider(logger), GetIsGitHubSignedInCallback(logger)) - .SearchAsync(searchTerm, version, cancellationToken); + var searcher = await GetKiotaSearcher(loggerFactory, cancellationToken).ConfigureAwait(false); + var results = await searcher.SearchAsync(searchTerm, version, cancellationToken).ConfigureAwait(false); return await SaveResultsAsync(searchTerm, version, results, logger, cancellationToken); } catch (Exception ex) { #if DEBUG diff --git a/src/kiota/Handlers/KiotaGitHubLogoutCommandhandler.cs b/src/kiota/Handlers/KiotaGitHubLogoutCommandhandler.cs index bfb62634af..38a4574849 100644 --- a/src/kiota/Handlers/KiotaGitHubLogoutCommandhandler.cs +++ b/src/kiota/Handlers/KiotaGitHubLogoutCommandhandler.cs @@ -15,9 +15,10 @@ public override async Task InvokeAsync(InvocationContext context) var (loggerFactory, logger) = GetLoggerAndFactory(context); using (loggerFactory) { try { - var authProvider = GitHubDeviceAuthenticationProvider(logger); - var result = await authProvider.TokenStorageService.Value.DeleteTokenAsync(cancellationToken); - if(result) + var deviceCodeAuthProvider = GetGitHubDeviceStorageService(logger); + var deviceCodeResult = await deviceCodeAuthProvider.TokenStorageService.Value.DeleteTokenAsync(cancellationToken).ConfigureAwait(false); + var patResult = await GetGitHubPatStorageService(logger).DeleteTokenAsync(cancellationToken).ConfigureAwait(false); + if(deviceCodeResult || patResult) DisplaySuccess("Logged out successfully."); else DisplaySuccess("Already logged out."); diff --git a/src/kiota/Handlers/KiotaGitHubPatLoginCommandHandler.cs b/src/kiota/Handlers/KiotaGitHubPatLoginCommandHandler.cs index e50fc527d2..2cc3fbad2a 100644 --- a/src/kiota/Handlers/KiotaGitHubPatLoginCommandHandler.cs +++ b/src/kiota/Handlers/KiotaGitHubPatLoginCommandHandler.cs @@ -33,11 +33,7 @@ private async Task LoginAsync(ILogger logger, string patValue, Cancellation logger.LogCritical("no personal access token provided"); return 1; } - var tokenStorageService = new TempFolderTokenStorageService { - Logger = logger, - FileName = "pat-api.github.com" - }; - await tokenStorageService.SetTokenAsync(patValue, cancellationToken).ConfigureAwait(false); + await GetGitHubPatStorageService(logger).SetTokenAsync(patValue, cancellationToken).ConfigureAwait(false); DisplaySuccess("Authentication successful."); DisplaySearchBasicHint(); DisplayGitHubLogoutHint(); diff --git a/src/kiota/Handlers/KiotaSeachBasedCommandHandler.cs b/src/kiota/Handlers/KiotaSeachBasedCommandHandler.cs index ecd73208e2..0f60e1b362 100644 --- a/src/kiota/Handlers/KiotaSeachBasedCommandHandler.cs +++ b/src/kiota/Handlers/KiotaSeachBasedCommandHandler.cs @@ -12,8 +12,8 @@ internal abstract class KiotaSearchBasedCommandHandler : BaseKiotaCommandHandler if (string.IsNullOrEmpty(openapi) && !string.IsNullOrEmpty(searchTerm)) { logger.LogInformation("Searching for {searchTerm} in the OpenAPI description repository", searchTerm); - var searcher = new KiotaSearcher(loggerFactory.CreateLogger(), Configuration.Search, httpClient, GetAuthenticationProvider(logger), GetIsGitHubSignedInCallback(logger)); - var results = await searcher.SearchAsync(searchTerm, version, cancellationToken); + var searcher = await GetKiotaSearcher(loggerFactory, cancellationToken).ConfigureAwait(false); + var results = await searcher.SearchAsync(searchTerm, version, cancellationToken).ConfigureAwait(false); if (results.Count == 1) return (results.First().Value.DescriptionUrl.ToString(), null); else if(!results.Any()) { diff --git a/src/kiota/Handlers/KiotaSearchCommandHandler.cs b/src/kiota/Handlers/KiotaSearchCommandHandler.cs index d4355d0c84..74a8a0a885 100644 --- a/src/kiota/Handlers/KiotaSearchCommandHandler.cs +++ b/src/kiota/Handlers/KiotaSearchCommandHandler.cs @@ -35,8 +35,8 @@ public override async Task InvokeAsync(InvocationContext context) logger.LogTrace("configuration: {configuration}", JsonSerializer.Serialize(Configuration)); try { - var results = await new KiotaSearcher(logger, Configuration.Search, httpClient, GetAuthenticationProvider(logger), GetIsGitHubSignedInCallback(logger)) - .SearchAsync(searchTerm, version, cancellationToken); + var searcher = await GetKiotaSearcher(loggerFactory, cancellationToken).ConfigureAwait(false); + var results = await searcher.SearchAsync(searchTerm, version, cancellationToken); await DisplayResults(searchTerm, version, results, logger, cancellationToken); return 0; } catch (Exception ex) { From 4638fcfe95988bb7916f6dced2d8919846d6e520 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Mon, 28 Nov 2022 16:52:13 -0500 Subject: [PATCH 29/42] - minor fix after refactoring --- src/Kiota.Web/Shared/GitHubSignIn.razor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Kiota.Web/Shared/GitHubSignIn.razor b/src/Kiota.Web/Shared/GitHubSignIn.razor index d2d24a2c5d..d3cbe43a09 100644 --- a/src/Kiota.Web/Shared/GitHubSignIn.razor +++ b/src/Kiota.Web/Shared/GitHubSignIn.razor @@ -1,5 +1,5 @@ @using Kiota.Builder.Configuration -@using Kiota.Web.Authentication.GitHub +@using Kiota.Builder.SearchProviders.GitHub.Authentication @using Microsoft.Fast.Components.FluentUI @using Microsoft.Extensions.Localization @inject IStringLocalizer Loc From b26212e8e47d0cc1e4843b586b6aee4ac9dd3ef6 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 29 Nov 2022 14:32:47 -0500 Subject: [PATCH 30/42] - code linting Signed-off-by: Vincent Biret --- src/kiota/Handlers/BaseKiotaCommandHandler.cs | 2 +- src/kiota/KiotaHost.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/kiota/Handlers/BaseKiotaCommandHandler.cs b/src/kiota/Handlers/BaseKiotaCommandHandler.cs index 8ffd573788..fc0c70ffb0 100644 --- a/src/kiota/Handlers/BaseKiotaCommandHandler.cs +++ b/src/kiota/Handlers/BaseKiotaCommandHandler.cs @@ -46,7 +46,7 @@ private Func> GetIsGitHubDeviceSignedInCallback(IL var provider = GetGitHubDeviceStorageService(logger); return provider.TokenStorageService.Value.IsTokenPresentAsync(cancellationToken); }; - private Func> GetIsGitHubPatSignedInCallback(ILogger logger) => (cancellationToken) => { + private static Func> GetIsGitHubPatSignedInCallback(ILogger logger) => (cancellationToken) => { var provider = GetGitHubPatStorageService(logger); return provider.IsTokenPresentAsync(cancellationToken); }; diff --git a/src/kiota/KiotaHost.cs b/src/kiota/KiotaHost.cs index be627fb9fc..89ba60b8a4 100644 --- a/src/kiota/KiotaHost.cs +++ b/src/kiota/KiotaHost.cs @@ -11,7 +11,7 @@ using kiota.Handlers; namespace kiota; -public class KiotaHost { +public static class KiotaHost { public static RootCommand GetRootCommand() { var rootCommand = new RootCommand(); rootCommand.AddCommand(GetGenerateCommand()); From 32aa6e0517cf8b5f6c0881d5f8d28387442733d5 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 29 Nov 2022 14:51:05 -0500 Subject: [PATCH 31/42] - adds documentation for the login and logout commands Signed-off-by: Vincent Biret --- docs/using.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/docs/using.md b/docs/using.md index b0e4760bde..ed26068860 100644 --- a/docs/using.md +++ b/docs/using.md @@ -19,6 +19,8 @@ Kiota offers the following commands to help you build your API client: - [generate](#client-generation): generate a client for any API from its description. - [update](#client-update): update existing clients from previous generations. - [info](#language-information): show languages and runtime dependencies information. +- [login](#login): logs in to private API descriptions repositories. +- [logout](#logout): logs out from private API description repositories. ## Description search @@ -402,6 +404,45 @@ The generate command accepts optional parameters commonly available on the other - [--log-level](#--log-level---ll) - [--output](#--output--o) +## Login + +Use kiota login to sign in to private repositories and search for/display/generate clients for private API descriptions. This command makes sub-commands available to sign in to specific authentication providers. + +### Login to GitHub + +```shell +kiota login github + [(--log-level | --ll) ] + [(--pat) ] +``` + +Use `kiota login github device` to sign in using a device code, you will be prompted to sign-in using a web browser. + +Use `kiota login github pat --pat patValue` to sign in using a Personal Access Token you previously generated. You can use both classic PATs or granular PATs (beta). Classic PATs need the **repo** permission and to be granted access to the target organizations. Granular PATs need a **read-only** permission for the **contents** scope under the **repository** category and they need to be consented for all private repositories or selected private repositories. + +> Note: for more information about adding API descriptions to the GitHub index, see [Adding an API to search](add-api.md) + +### Optional parameters + +The generate command accepts optional parameters commonly available on the other commands: + +- [--log-level](#--log-level---ll) + +## Logout + +Use kiota logout to logout from a private repository containing API descriptions. + +```shell +kiota logout github + [(--log-level | --ll) ] +``` + +### Optional parameters + +The generate command accepts optional parameters commonly available on the other commands: + +- [--log-level](#--log-level---ll) + ## Common parameters The following parameters are available across multiple commands. From 8e2468e1fbb984c70414d35a77d2753427521b03 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 29 Nov 2022 15:34:51 -0500 Subject: [PATCH 32/42] - adds missing unit tests for github authentication Signed-off-by: Vincent Biret --- ...tProvider.cs => PatAccessTokenProvider.cs} | 3 +- .../PatAuthenticationProvider.cs | 4 +-- .../PatAccessTokenProviderTests.cs | 23 +++++++++++++ .../PatAuthenticationProviderTests.cs | 18 ++++++++++ ...mpFolderCachingAccessTokenProviderTests.cs | 30 +++++++++++++++++ .../TempFolderTokenStorageServiceTests.cs | 33 +++++++++++++++++++ .../Kiota.Builder.Tests/UriExtensionsTests.cs | 19 +++++++++++ 7 files changed, 127 insertions(+), 3 deletions(-) rename src/Kiota.Builder/SearchProviders/GitHub/Authentication/{PatProvider.cs => PatAccessTokenProvider.cs} (88%) create mode 100644 tests/Kiota.Builder.Tests/SearchProviders/GitHub/Authentication/PatAccessTokenProviderTests.cs create mode 100644 tests/Kiota.Builder.Tests/SearchProviders/GitHub/Authentication/PatAuthenticationProviderTests.cs create mode 100644 tests/Kiota.Builder.Tests/SearchProviders/GitHub/Authentication/TempFolderCachingAccessTokenProviderTests.cs create mode 100644 tests/Kiota.Builder.Tests/SearchProviders/GitHub/Authentication/TempFolderTokenStorageServiceTests.cs create mode 100644 tests/Kiota.Builder.Tests/UriExtensionsTests.cs diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Authentication/PatProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/Authentication/PatAccessTokenProvider.cs similarity index 88% rename from src/Kiota.Builder/SearchProviders/GitHub/Authentication/PatProvider.cs rename to src/Kiota.Builder/SearchProviders/GitHub/Authentication/PatAccessTokenProvider.cs index b823b8f6d4..e4b82d7272 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/Authentication/PatProvider.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/Authentication/PatAccessTokenProvider.cs @@ -6,12 +6,13 @@ namespace Kiota.Builder.SearchProviders.GitHub.Authentication; -public class PatProvider : IAccessTokenProvider +public class PatAccessTokenProvider : IAccessTokenProvider { public AllowedHostsValidator AllowedHostsValidator { get; set; } = new(); public required ITokenStorageService StorageService { get; init; } public Task GetAuthorizationTokenAsync(Uri uri, Dictionary additionalAuthenticationContext = null, CancellationToken cancellationToken = default) { + ArgumentNullException.ThrowIfNull(uri); if("https".Equals(uri.Scheme, StringComparison.OrdinalIgnoreCase) && AllowedHostsValidator.IsUrlHostValid(uri)) return StorageService.GetTokenAsync(cancellationToken); return Task.FromResult(string.Empty); diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Authentication/PatAuthenticationProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/Authentication/PatAuthenticationProvider.cs index 056dfaaef1..6a4ef27cdb 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/Authentication/PatAuthenticationProvider.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/Authentication/PatAuthenticationProvider.cs @@ -4,10 +4,10 @@ using Microsoft.Extensions.Logging; namespace Kiota.Builder.SearchProviders.GitHub.Authentication; -public class PatAuthenticationProvider : BaseAuthenticationProvider +public class PatAuthenticationProvider : BaseAuthenticationProvider { public PatAuthenticationProvider(string clientId, string scope, IEnumerable validHosts, ILogger logger, ITokenStorageService StorageService) : - base(clientId, scope, validHosts, logger, (clientId, scope, validHosts) => new PatProvider { + base(clientId, scope, validHosts, logger, (clientId, scope, validHosts) => new PatAccessTokenProvider { StorageService = StorageService, AllowedHostsValidator = new (validHosts), }, false) diff --git a/tests/Kiota.Builder.Tests/SearchProviders/GitHub/Authentication/PatAccessTokenProviderTests.cs b/tests/Kiota.Builder.Tests/SearchProviders/GitHub/Authentication/PatAccessTokenProviderTests.cs new file mode 100644 index 0000000000..e5d523eef6 --- /dev/null +++ b/tests/Kiota.Builder.Tests/SearchProviders/GitHub/Authentication/PatAccessTokenProviderTests.cs @@ -0,0 +1,23 @@ + +using System.Threading; +using System.Threading.Tasks; +using Kiota.Builder.SearchProviders.GitHub.Authentication; +using Moq; +using Xunit; + +namespace Kiota.Builder.Tests.SearchProviders.GitHub.Authentication; +public class PatAccessTokenProviderTests { + [Fact] + public async Task GetsToken() { + var storageMock = new Mock(); + storageMock.Setup(x => x.GetTokenAsync(It.IsAny())) + .ReturnsAsync("foo"); + var provider = new PatAccessTokenProvider { + StorageService = storageMock.Object + }; + Assert.Equal("foo", await provider.GetAuthorizationTokenAsync(new ("https://api.github.com"), new (), new ())); + Assert.Empty(await provider.GetAuthorizationTokenAsync(new ("http://api.github.com"), new (), new ())); + provider.AllowedHostsValidator = new(new [] { "web.github.com" }); + Assert.Empty(await provider.GetAuthorizationTokenAsync(new ("https://api.github.com"), new (), new ())); + } +} diff --git a/tests/Kiota.Builder.Tests/SearchProviders/GitHub/Authentication/PatAuthenticationProviderTests.cs b/tests/Kiota.Builder.Tests/SearchProviders/GitHub/Authentication/PatAuthenticationProviderTests.cs new file mode 100644 index 0000000000..992d229776 --- /dev/null +++ b/tests/Kiota.Builder.Tests/SearchProviders/GitHub/Authentication/PatAuthenticationProviderTests.cs @@ -0,0 +1,18 @@ + +using System; +using Kiota.Builder.SearchProviders.GitHub.Authentication; +using Microsoft.Extensions.Logging; +using Moq; +using Xunit; + +namespace Kiota.Builder.Tests.SearchProviders.GitHub.Authentication; +public class PatAuthenticationProviderTests { + [Fact] + public void Defensive() { + Assert.Throws(() => new PatAuthenticationProvider(null, "foo", new string[] {"foo"}, new Mock().Object, new Mock().Object)); + Assert.Throws(() => new PatAuthenticationProvider("foo", null, new string[] {"foo"}, new Mock().Object, new Mock().Object)); + Assert.Throws(() => new PatAuthenticationProvider("foo", "foo", new string[] {"foo"}, null, new Mock().Object)); + Assert.Throws(() => new PatAuthenticationProvider("foo", "foo", new string[] {"foo"}, new Mock().Object, null)); + } + +} diff --git a/tests/Kiota.Builder.Tests/SearchProviders/GitHub/Authentication/TempFolderCachingAccessTokenProviderTests.cs b/tests/Kiota.Builder.Tests/SearchProviders/GitHub/Authentication/TempFolderCachingAccessTokenProviderTests.cs new file mode 100644 index 0000000000..eb95460f9c --- /dev/null +++ b/tests/Kiota.Builder.Tests/SearchProviders/GitHub/Authentication/TempFolderCachingAccessTokenProviderTests.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Kiota.Builder.SearchProviders.GitHub.Authentication; +using Microsoft.Extensions.Logging; +using Microsoft.Kiota.Abstractions.Authentication; +using Moq; +using Xunit; + +namespace Kiota.Builder.Tests.SearchProviders.GitHub.Authentication; + +public class TempFolderCachingAccessTokenProviderTests { + [Fact] + public async Task CachesToken() { + var concreteProvider = new Mock(); + concreteProvider.Setup(x => x.GetAuthorizationTokenAsync(It.IsAny(), It.IsAny>(), It.IsAny())) + .ReturnsAsync("foo"); + var cachingProvider = new TempFolderCachingAccessTokenProvider { + Logger = new Mock().Object, + ApiBaseUrl = new ("https://api.github.com"), + AppId = Path.GetRandomFileName(), + Concrete = concreteProvider.Object, + }; + Assert.Equal("foo", await cachingProvider.GetAuthorizationTokenAsync(new ("https://api.github.com"), new (), new ())); + await cachingProvider.GetAuthorizationTokenAsync(new ("https://api.github.com"), new (), new ()); + concreteProvider.Verify(x => x.GetAuthorizationTokenAsync(It.IsAny(), It.IsAny>(), It.IsAny()), Times.Once); + } +} diff --git a/tests/Kiota.Builder.Tests/SearchProviders/GitHub/Authentication/TempFolderTokenStorageServiceTests.cs b/tests/Kiota.Builder.Tests/SearchProviders/GitHub/Authentication/TempFolderTokenStorageServiceTests.cs new file mode 100644 index 0000000000..12810841e3 --- /dev/null +++ b/tests/Kiota.Builder.Tests/SearchProviders/GitHub/Authentication/TempFolderTokenStorageServiceTests.cs @@ -0,0 +1,33 @@ + +using System.IO; +using System.Threading.Tasks; +using Kiota.Builder.SearchProviders.GitHub.Authentication; +using Microsoft.Extensions.Logging; +using Moq; +using Xunit; + +namespace Kiota.Builder.Tests.SearchProviders.GitHub.Authentication; +public class TempFolderTokenStorageServiceTests { + [Fact] + public async Task DeletesAsync() { + var storageService = new TempFolderTokenStorageService { + Logger = new Mock().Object, + FileName = Path.GetRandomFileName() + }; + Assert.False(await storageService.IsTokenPresentAsync(new ())); + Assert.False(await storageService.DeleteTokenAsync(new ())); + await storageService.SetTokenAsync("foo", new ()); + Assert.True(await storageService.IsTokenPresentAsync(new ())); + Assert.True(await storageService.DeleteTokenAsync(new ())); + } + [Fact] + public async Task GetsAsync() { + var storageService = new TempFolderTokenStorageService { + Logger = new Mock().Object, + FileName = Path.GetRandomFileName() + }; + Assert.Null(await storageService.GetTokenAsync(new ())); + await storageService.SetTokenAsync("foo", new ()); + Assert.Equal("foo", await storageService.GetTokenAsync(new ())); + } +} diff --git a/tests/Kiota.Builder.Tests/UriExtensionsTests.cs b/tests/Kiota.Builder.Tests/UriExtensionsTests.cs new file mode 100644 index 0000000000..adda3c13f9 --- /dev/null +++ b/tests/Kiota.Builder.Tests/UriExtensionsTests.cs @@ -0,0 +1,19 @@ +using System; +using Kiota.Builder.Extensions; +using Xunit; + +namespace Kiota.Builder.Tests.Writers; +public class UriExtensionsTests { + [Fact] + public void Defensive() { + Assert.Empty(UriExtensions.GetFileName(null)); + } + [Fact] + public void GetsFileName() { + Assert.Equal("todo.yml", new Uri("https://contoso.com/todo.yml").GetFileName()); + } + [Fact] + public void StripsQueryParameters() { + Assert.Equal("todo.yml", new Uri("https://contoso.com/todo.yml?foo=bar").GetFileName()); + } +} From 207d44ff611bb9be9318f85e4b1bb55135972b53 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 29 Nov 2022 15:46:29 -0500 Subject: [PATCH 33/42] - adds a tool tip to the sign in button Signed-off-by: Vincent Biret --- src/Kiota.Web/Shared/GitHubSignIn.fr.resx | 3 +++ src/Kiota.Web/Shared/GitHubSignIn.razor | 3 ++- src/Kiota.Web/Shared/GitHubSignIn.resx | 3 +++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Kiota.Web/Shared/GitHubSignIn.fr.resx b/src/Kiota.Web/Shared/GitHubSignIn.fr.resx index 815a211ac8..ef81f9238f 100644 --- a/src/Kiota.Web/Shared/GitHubSignIn.fr.resx +++ b/src/Kiota.Web/Shared/GitHubSignIn.fr.resx @@ -109,4 +109,7 @@ Logo GitHub Octocat + + Accédez à des description d'APIs privées. + \ No newline at end of file diff --git a/src/Kiota.Web/Shared/GitHubSignIn.razor b/src/Kiota.Web/Shared/GitHubSignIn.razor index d3cbe43a09..282858f31f 100644 --- a/src/Kiota.Web/Shared/GitHubSignIn.razor +++ b/src/Kiota.Web/Shared/GitHubSignIn.razor @@ -14,7 +14,8 @@ @if(IsSignedIn) { @Loc["SignOut"] } else { - @Loc["SignIn"] + @Loc["SignIn"] + @Loc["ToolTip"] }
diff --git a/src/Kiota.Web/Shared/GitHubSignIn.resx b/src/Kiota.Web/Shared/GitHubSignIn.resx index a6d24dd8d0..1574efa474 100644 --- a/src/Kiota.Web/Shared/GitHubSignIn.resx +++ b/src/Kiota.Web/Shared/GitHubSignIn.resx @@ -109,4 +109,7 @@ GitHub Octocat logo + + Access private API descriptions. + \ No newline at end of file From fac8e616c0256bd5a1e0c42ad7a1f5282e4be277 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Wed, 30 Nov 2022 08:59:19 -0500 Subject: [PATCH 34/42] - locks the github api version --- .../AnonymousAuthenticationProvider.cs | 1 + .../GitHub/GitHubClient/Models/BasicError.cs | 8 ++ .../GitHub/GitHubClient/Models/ContentFile.cs | 6 +- .../GitHubClient/Models/ContentFile_type.cs | 5 + .../GitHubClient/Models/ContentSubmodule.cs | 8 +- .../Models/ContentSubmodule_type.cs | 5 + .../GitHubClient/Models/ContentSymlink.cs | 6 +- .../Models/ContentSymlink_type.cs | 5 + .../Models/NullableLicenseSimple.cs | 65 +++++++++ .../GitHubClient/Models/NullableSimpleUser.cs | 125 ++++++++++++++++++ .../Models/RepoSearchResultItem.cs | 46 +++++-- .../RepoSearchResultItem_permissions.cs | 8 ++ .../Item/WithPathDeleteRequestBody.cs | 2 +- .../Item/WithPathItemRequestBuilder.cs | 14 +- .../RepositoriesRequestBuilder.cs | 10 +- .../GitHub/GitHubClient/kiota-lock.json | 4 +- 16 files changed, 282 insertions(+), 36 deletions(-) create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/ContentFile_type.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/ContentSubmodule_type.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/ContentSymlink_type.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/NullableLicenseSimple.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/NullableSimpleUser.cs diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Authentication/AnonymousAuthenticationProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/Authentication/AnonymousAuthenticationProvider.cs index 898d54e7c0..bae82a65e7 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/Authentication/AnonymousAuthenticationProvider.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/Authentication/AnonymousAuthenticationProvider.cs @@ -10,6 +10,7 @@ public class AnonymousAuthenticationProvider : IAuthenticationProvider public virtual Task AuthenticateRequestAsync(RequestInformation request, Dictionary additionalAuthenticationContext = null, CancellationToken cancellationToken = default) { request.Headers.Add("User-Agent", $"Kiota/{GetType().Assembly.GetName().Version}"); + request.Headers.Add("X-GitHub-Api-Version", "2022-11-28"); return Task.CompletedTask; } } diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/BasicError.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/BasicError.cs index 6e89ddd5fa..dd08500567 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/BasicError.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/BasicError.cs @@ -13,6 +13,10 @@ public class BasicError : ApiException, IAdditionalDataHolder, IParsable { public string Documentation_url { get; set; } /// The message property public new string Message { get; set; } + /// The status property + public string Status { get; set; } + /// The url property + public string Url { get; set; } /// /// Instantiates a new BasicError and sets the default values. /// @@ -34,6 +38,8 @@ public IDictionary> GetFieldDeserializers() { return new Dictionary> { {"documentation_url", n => { Documentation_url = n.GetStringValue(); } }, {"message", n => { Message = n.GetStringValue(); } }, + {"status", n => { Status = n.GetStringValue(); } }, + {"url", n => { Url = n.GetStringValue(); } }, }; } /// @@ -44,6 +50,8 @@ public void Serialize(ISerializationWriter writer) { _ = writer ?? throw new ArgumentNullException(nameof(writer)); writer.WriteStringValue("documentation_url", Documentation_url); writer.WriteStringValue("message", Message); + writer.WriteStringValue("status", Status); + writer.WriteStringValue("url", Url); writer.WriteAdditionalData(AdditionalData); } } diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/ContentFile.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/ContentFile.cs index de8117ead9..34ce4026d6 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/ContentFile.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/ContentFile.cs @@ -33,7 +33,7 @@ public class ContentFile : IAdditionalDataHolder, IParsable { /// The target property public string Target { get; set; } /// The type property - public string Type { get; set; } + public ContentFile_type? Type { get; set; } /// The url property public string Url { get; set; } /// @@ -67,7 +67,7 @@ public IDictionary> GetFieldDeserializers() { {"size", n => { Size = n.GetIntValue(); } }, {"submodule_git_url", n => { Submodule_git_url = n.GetStringValue(); } }, {"target", n => { Target = n.GetStringValue(); } }, - {"type", n => { Type = n.GetStringValue(); } }, + {"type", n => { Type = n.GetEnumValue(); } }, {"url", n => { Url = n.GetStringValue(); } }, }; } @@ -89,7 +89,7 @@ public void Serialize(ISerializationWriter writer) { writer.WriteIntValue("size", Size); writer.WriteStringValue("submodule_git_url", Submodule_git_url); writer.WriteStringValue("target", Target); - writer.WriteStringValue("type", Type); + writer.WriteEnumValue("type", Type); writer.WriteStringValue("url", Url); writer.WriteAdditionalData(AdditionalData); } diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/ContentFile_type.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/ContentFile_type.cs new file mode 100644 index 0000000000..e054f86f31 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/ContentFile_type.cs @@ -0,0 +1,5 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum ContentFile_type { + File, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/ContentSubmodule.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/ContentSubmodule.cs index 9e4f000892..155a9d14d4 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/ContentSubmodule.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/ContentSubmodule.cs @@ -4,7 +4,7 @@ using System.IO; using System.Linq; namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { - /// An object describing a symlink + /// An object describing a submodule public class ContentSubmodule : IAdditionalDataHolder, IParsable { /// The _links property public ContentSubmodule__links _links { get; set; } @@ -27,7 +27,7 @@ public class ContentSubmodule : IAdditionalDataHolder, IParsable { /// The submodule_git_url property public string Submodule_git_url { get; set; } /// The type property - public string Type { get; set; } + public ContentSubmodule_type? Type { get; set; } /// The url property public string Url { get; set; } /// @@ -58,7 +58,7 @@ public IDictionary> GetFieldDeserializers() { {"sha", n => { Sha = n.GetStringValue(); } }, {"size", n => { Size = n.GetIntValue(); } }, {"submodule_git_url", n => { Submodule_git_url = n.GetStringValue(); } }, - {"type", n => { Type = n.GetStringValue(); } }, + {"type", n => { Type = n.GetEnumValue(); } }, {"url", n => { Url = n.GetStringValue(); } }, }; } @@ -77,7 +77,7 @@ public void Serialize(ISerializationWriter writer) { writer.WriteStringValue("sha", Sha); writer.WriteIntValue("size", Size); writer.WriteStringValue("submodule_git_url", Submodule_git_url); - writer.WriteStringValue("type", Type); + writer.WriteEnumValue("type", Type); writer.WriteStringValue("url", Url); writer.WriteAdditionalData(AdditionalData); } diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/ContentSubmodule_type.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/ContentSubmodule_type.cs new file mode 100644 index 0000000000..564ee13175 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/ContentSubmodule_type.cs @@ -0,0 +1,5 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum ContentSubmodule_type { + Submodule, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/ContentSymlink.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/ContentSymlink.cs index 4b5f4ddd39..a8bae99656 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/ContentSymlink.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/ContentSymlink.cs @@ -27,7 +27,7 @@ public class ContentSymlink : IAdditionalDataHolder, IParsable { /// The target property public string Target { get; set; } /// The type property - public string Type { get; set; } + public ContentSymlink_type? Type { get; set; } /// The url property public string Url { get; set; } /// @@ -58,7 +58,7 @@ public IDictionary> GetFieldDeserializers() { {"sha", n => { Sha = n.GetStringValue(); } }, {"size", n => { Size = n.GetIntValue(); } }, {"target", n => { Target = n.GetStringValue(); } }, - {"type", n => { Type = n.GetStringValue(); } }, + {"type", n => { Type = n.GetEnumValue(); } }, {"url", n => { Url = n.GetStringValue(); } }, }; } @@ -77,7 +77,7 @@ public void Serialize(ISerializationWriter writer) { writer.WriteStringValue("sha", Sha); writer.WriteIntValue("size", Size); writer.WriteStringValue("target", Target); - writer.WriteStringValue("type", Type); + writer.WriteEnumValue("type", Type); writer.WriteStringValue("url", Url); writer.WriteAdditionalData(AdditionalData); } diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/ContentSymlink_type.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/ContentSymlink_type.cs new file mode 100644 index 0000000000..1664d2216a --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/ContentSymlink_type.cs @@ -0,0 +1,5 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum ContentSymlink_type { + Symlink, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/NullableLicenseSimple.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/NullableLicenseSimple.cs new file mode 100644 index 0000000000..aab773684a --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/NullableLicenseSimple.cs @@ -0,0 +1,65 @@ +using Microsoft.Kiota.Abstractions.Serialization; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + /// License Simple + public class NullableLicenseSimple : IAdditionalDataHolder, IParsable { + /// 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 html_url property + public string Html_url { get; set; } + /// The key property + public string Key { get; set; } + /// The name property + public string Name { get; set; } + /// The node_id property + public string Node_id { get; set; } + /// The spdx_id property + public string Spdx_id { get; set; } + /// The url property + public string Url { get; set; } + /// + /// Instantiates a new nullableLicenseSimple and sets the default values. + /// + public NullableLicenseSimple() { + AdditionalData = new Dictionary(); + } + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// The parse node to use to read the discriminator value and create the object + public static NullableLicenseSimple CreateFromDiscriminatorValue(IParseNode parseNode) { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new NullableLicenseSimple(); + } + /// + /// The deserialization information for the current model + /// + public IDictionary> GetFieldDeserializers() { + return new Dictionary> { + {"html_url", n => { Html_url = n.GetStringValue(); } }, + {"key", n => { Key = n.GetStringValue(); } }, + {"name", n => { Name = n.GetStringValue(); } }, + {"node_id", n => { Node_id = n.GetStringValue(); } }, + {"spdx_id", n => { Spdx_id = n.GetStringValue(); } }, + {"url", n => { Url = n.GetStringValue(); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public void Serialize(ISerializationWriter writer) { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteStringValue("html_url", Html_url); + writer.WriteStringValue("key", Key); + writer.WriteStringValue("name", Name); + writer.WriteStringValue("node_id", Node_id); + writer.WriteStringValue("spdx_id", Spdx_id); + writer.WriteStringValue("url", Url); + writer.WriteAdditionalData(AdditionalData); + } + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/NullableSimpleUser.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/NullableSimpleUser.cs new file mode 100644 index 0000000000..a898f939a4 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/NullableSimpleUser.cs @@ -0,0 +1,125 @@ +using Microsoft.Kiota.Abstractions.Serialization; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + /// A GitHub user. + public class NullableSimpleUser : IAdditionalDataHolder, IParsable { + /// 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 avatar_url property + public string Avatar_url { get; set; } + /// The email property + public string Email { get; set; } + /// The events_url property + public string Events_url { get; set; } + /// The followers_url property + public string Followers_url { get; set; } + /// The following_url property + public string Following_url { get; set; } + /// The gists_url property + public string Gists_url { get; set; } + /// The gravatar_id property + public string Gravatar_id { get; set; } + /// The html_url property + public string Html_url { get; set; } + /// The id property + public int? Id { get; set; } + /// The login property + public string Login { get; set; } + /// The name property + public string Name { get; set; } + /// The node_id property + public string Node_id { get; set; } + /// The organizations_url property + public string Organizations_url { get; set; } + /// The received_events_url property + public string Received_events_url { get; set; } + /// The repos_url property + public string Repos_url { get; set; } + /// The site_admin property + public bool? Site_admin { get; set; } + /// The starred_at property + public string Starred_at { get; set; } + /// The starred_url property + public string Starred_url { get; set; } + /// The subscriptions_url property + public string Subscriptions_url { get; set; } + /// The type property + public string Type { get; set; } + /// The url property + public string Url { get; set; } + /// + /// Instantiates a new nullableSimpleUser and sets the default values. + /// + public NullableSimpleUser() { + AdditionalData = new Dictionary(); + } + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// The parse node to use to read the discriminator value and create the object + public static NullableSimpleUser CreateFromDiscriminatorValue(IParseNode parseNode) { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new NullableSimpleUser(); + } + /// + /// The deserialization information for the current model + /// + public IDictionary> GetFieldDeserializers() { + return new Dictionary> { + {"avatar_url", n => { Avatar_url = n.GetStringValue(); } }, + {"email", n => { Email = n.GetStringValue(); } }, + {"events_url", n => { Events_url = n.GetStringValue(); } }, + {"followers_url", n => { Followers_url = n.GetStringValue(); } }, + {"following_url", n => { Following_url = n.GetStringValue(); } }, + {"gists_url", n => { Gists_url = n.GetStringValue(); } }, + {"gravatar_id", n => { Gravatar_id = n.GetStringValue(); } }, + {"html_url", n => { Html_url = n.GetStringValue(); } }, + {"id", n => { Id = n.GetIntValue(); } }, + {"login", n => { Login = n.GetStringValue(); } }, + {"name", n => { Name = n.GetStringValue(); } }, + {"node_id", n => { Node_id = n.GetStringValue(); } }, + {"organizations_url", n => { Organizations_url = n.GetStringValue(); } }, + {"received_events_url", n => { Received_events_url = n.GetStringValue(); } }, + {"repos_url", n => { Repos_url = n.GetStringValue(); } }, + {"site_admin", n => { Site_admin = n.GetBoolValue(); } }, + {"starred_at", n => { Starred_at = n.GetStringValue(); } }, + {"starred_url", n => { Starred_url = n.GetStringValue(); } }, + {"subscriptions_url", n => { Subscriptions_url = n.GetStringValue(); } }, + {"type", n => { Type = n.GetStringValue(); } }, + {"url", n => { Url = n.GetStringValue(); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public void Serialize(ISerializationWriter writer) { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteStringValue("avatar_url", Avatar_url); + writer.WriteStringValue("email", Email); + writer.WriteStringValue("events_url", Events_url); + writer.WriteStringValue("followers_url", Followers_url); + writer.WriteStringValue("following_url", Following_url); + writer.WriteStringValue("gists_url", Gists_url); + writer.WriteStringValue("gravatar_id", Gravatar_id); + writer.WriteStringValue("html_url", Html_url); + writer.WriteIntValue("id", Id); + writer.WriteStringValue("login", Login); + writer.WriteStringValue("name", Name); + writer.WriteStringValue("node_id", Node_id); + writer.WriteStringValue("organizations_url", Organizations_url); + writer.WriteStringValue("received_events_url", Received_events_url); + writer.WriteStringValue("repos_url", Repos_url); + writer.WriteBoolValue("site_admin", Site_admin); + writer.WriteStringValue("starred_at", Starred_at); + writer.WriteStringValue("starred_url", Starred_url); + writer.WriteStringValue("subscriptions_url", Subscriptions_url); + writer.WriteStringValue("type", Type); + writer.WriteStringValue("url", Url); + writer.WriteAdditionalData(AdditionalData); + } + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/RepoSearchResultItem.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/RepoSearchResultItem.cs index 5f4e67f62a..26c5926d1f 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/RepoSearchResultItem.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/RepoSearchResultItem.cs @@ -8,6 +8,10 @@ namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { public class RepoSearchResultItem : IAdditionalDataHolder, IParsable { /// 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 allow_auto_merge property + public bool? Allow_auto_merge { get; set; } + /// The allow_forking property + public bool? Allow_forking { get; set; } /// The allow_merge_commit property public bool? Allow_merge_commit { get; set; } /// The allow_rebase_merge property @@ -72,6 +76,8 @@ public class RepoSearchResultItem : IAdditionalDataHolder, IParsable { public string Git_tags_url { get; set; } /// The git_url property public string Git_url { get; set; } + /// The has_discussions property + public bool? Has_discussions { get; set; } /// The has_downloads property public bool? Has_downloads { get; set; } /// The has_issues property @@ -90,6 +96,8 @@ public class RepoSearchResultItem : IAdditionalDataHolder, IParsable { public string Html_url { get; set; } /// The id property public int? Id { get; set; } + /// The is_template property + public bool? Is_template { get; set; } /// The issue_comment_url property public string Issue_comment_url { get; set; } /// The issue_events_url property @@ -104,8 +112,8 @@ public class RepoSearchResultItem : IAdditionalDataHolder, IParsable { public string Language { get; set; } /// The languages_url property public string Languages_url { get; set; } - /// The license property - public LicenseSimple License { get; set; } + /// License Simple + public NullableLicenseSimple License { get; set; } /// The master_branch property public string Master_branch { get; set; } /// The merges_url property @@ -124,8 +132,8 @@ public class RepoSearchResultItem : IAdditionalDataHolder, IParsable { public int? Open_issues { get; set; } /// The open_issues_count property public int? Open_issues_count { get; set; } - /// The owner property - public SimpleUser Owner { get; set; } + /// A GitHub user. + public NullableSimpleUser Owner { get; set; } /// The permissions property public RepoSearchResultItem_permissions Permissions { get; set; } /// The private property @@ -137,7 +145,7 @@ public class RepoSearchResultItem : IAdditionalDataHolder, IParsable { /// The releases_url property public string Releases_url { get; set; } /// The score property - public double? Score { get; set; } + public long? Score { get; set; } /// The size property public int? Size { get; set; } /// The ssh_url property @@ -170,10 +178,14 @@ public class RepoSearchResultItem : IAdditionalDataHolder, IParsable { public DateTimeOffset? Updated_at { get; set; } /// The url property public string Url { get; set; } + /// The repository visibility: public, private, or internal. + public string Visibility { get; set; } /// The watchers property public int? Watchers { get; set; } /// The watchers_count property public int? Watchers_count { get; set; } + /// The web_commit_signoff_required property + public bool? Web_commit_signoff_required { get; set; } /// /// Instantiates a new repoSearchResultItem and sets the default values. /// @@ -193,6 +205,8 @@ public static RepoSearchResultItem CreateFromDiscriminatorValue(IParseNode parse /// public IDictionary> GetFieldDeserializers() { return new Dictionary> { + {"allow_auto_merge", n => { Allow_auto_merge = n.GetBoolValue(); } }, + {"allow_forking", n => { Allow_forking = n.GetBoolValue(); } }, {"allow_merge_commit", n => { Allow_merge_commit = n.GetBoolValue(); } }, {"allow_rebase_merge", n => { Allow_rebase_merge = n.GetBoolValue(); } }, {"allow_squash_merge", n => { Allow_squash_merge = n.GetBoolValue(); } }, @@ -225,6 +239,7 @@ public IDictionary> GetFieldDeserializers() { {"git_refs_url", n => { Git_refs_url = n.GetStringValue(); } }, {"git_tags_url", n => { Git_tags_url = n.GetStringValue(); } }, {"git_url", n => { Git_url = n.GetStringValue(); } }, + {"has_discussions", n => { Has_discussions = n.GetBoolValue(); } }, {"has_downloads", n => { Has_downloads = n.GetBoolValue(); } }, {"has_issues", n => { Has_issues = n.GetBoolValue(); } }, {"has_pages", n => { Has_pages = n.GetBoolValue(); } }, @@ -234,6 +249,7 @@ public IDictionary> GetFieldDeserializers() { {"hooks_url", n => { Hooks_url = n.GetStringValue(); } }, {"html_url", n => { Html_url = n.GetStringValue(); } }, {"id", n => { Id = n.GetIntValue(); } }, + {"is_template", n => { Is_template = n.GetBoolValue(); } }, {"issue_comment_url", n => { Issue_comment_url = n.GetStringValue(); } }, {"issue_events_url", n => { Issue_events_url = n.GetStringValue(); } }, {"issues_url", n => { Issues_url = n.GetStringValue(); } }, @@ -241,7 +257,7 @@ public IDictionary> GetFieldDeserializers() { {"labels_url", n => { Labels_url = n.GetStringValue(); } }, {"language", n => { Language = n.GetStringValue(); } }, {"languages_url", n => { Languages_url = n.GetStringValue(); } }, - {"license", n => { License = n.GetObjectValue(LicenseSimple.CreateFromDiscriminatorValue); } }, + {"license", n => { License = n.GetObjectValue(NullableLicenseSimple.CreateFromDiscriminatorValue); } }, {"master_branch", n => { Master_branch = n.GetStringValue(); } }, {"merges_url", n => { Merges_url = n.GetStringValue(); } }, {"milestones_url", n => { Milestones_url = n.GetStringValue(); } }, @@ -251,13 +267,13 @@ public IDictionary> GetFieldDeserializers() { {"notifications_url", n => { Notifications_url = n.GetStringValue(); } }, {"open_issues", n => { Open_issues = n.GetIntValue(); } }, {"open_issues_count", n => { Open_issues_count = n.GetIntValue(); } }, - {"owner", n => { Owner = n.GetObjectValue(SimpleUser.CreateFromDiscriminatorValue); } }, + {"owner", n => { Owner = n.GetObjectValue(NullableSimpleUser.CreateFromDiscriminatorValue); } }, {"permissions", n => { Permissions = n.GetObjectValue(RepoSearchResultItem_permissions.CreateFromDiscriminatorValue); } }, {"private", n => { Private = n.GetBoolValue(); } }, {"pulls_url", n => { Pulls_url = n.GetStringValue(); } }, {"pushed_at", n => { Pushed_at = n.GetDateTimeOffsetValue(); } }, {"releases_url", n => { Releases_url = n.GetStringValue(); } }, - {"score", n => { Score = n.GetDoubleValue(); } }, + {"score", n => { Score = n.GetLongValue(); } }, {"size", n => { Size = n.GetIntValue(); } }, {"ssh_url", n => { Ssh_url = n.GetStringValue(); } }, {"stargazers_count", n => { Stargazers_count = n.GetIntValue(); } }, @@ -274,8 +290,10 @@ public IDictionary> GetFieldDeserializers() { {"trees_url", n => { Trees_url = n.GetStringValue(); } }, {"updated_at", n => { Updated_at = n.GetDateTimeOffsetValue(); } }, {"url", n => { Url = n.GetStringValue(); } }, + {"visibility", n => { Visibility = n.GetStringValue(); } }, {"watchers", n => { Watchers = n.GetIntValue(); } }, {"watchers_count", n => { Watchers_count = n.GetIntValue(); } }, + {"web_commit_signoff_required", n => { Web_commit_signoff_required = n.GetBoolValue(); } }, }; } /// @@ -284,6 +302,8 @@ public IDictionary> GetFieldDeserializers() { /// Serialization writer to use to serialize this model public void Serialize(ISerializationWriter writer) { _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteBoolValue("allow_auto_merge", Allow_auto_merge); + writer.WriteBoolValue("allow_forking", Allow_forking); writer.WriteBoolValue("allow_merge_commit", Allow_merge_commit); writer.WriteBoolValue("allow_rebase_merge", Allow_rebase_merge); writer.WriteBoolValue("allow_squash_merge", Allow_squash_merge); @@ -316,6 +336,7 @@ public void Serialize(ISerializationWriter writer) { writer.WriteStringValue("git_refs_url", Git_refs_url); writer.WriteStringValue("git_tags_url", Git_tags_url); writer.WriteStringValue("git_url", Git_url); + writer.WriteBoolValue("has_discussions", Has_discussions); writer.WriteBoolValue("has_downloads", Has_downloads); writer.WriteBoolValue("has_issues", Has_issues); writer.WriteBoolValue("has_pages", Has_pages); @@ -325,6 +346,7 @@ public void Serialize(ISerializationWriter writer) { writer.WriteStringValue("hooks_url", Hooks_url); writer.WriteStringValue("html_url", Html_url); writer.WriteIntValue("id", Id); + writer.WriteBoolValue("is_template", Is_template); writer.WriteStringValue("issue_comment_url", Issue_comment_url); writer.WriteStringValue("issue_events_url", Issue_events_url); writer.WriteStringValue("issues_url", Issues_url); @@ -332,7 +354,7 @@ public void Serialize(ISerializationWriter writer) { writer.WriteStringValue("labels_url", Labels_url); writer.WriteStringValue("language", Language); writer.WriteStringValue("languages_url", Languages_url); - writer.WriteObjectValue("license", License); + writer.WriteObjectValue("license", License); writer.WriteStringValue("master_branch", Master_branch); writer.WriteStringValue("merges_url", Merges_url); writer.WriteStringValue("milestones_url", Milestones_url); @@ -342,13 +364,13 @@ public void Serialize(ISerializationWriter writer) { writer.WriteStringValue("notifications_url", Notifications_url); writer.WriteIntValue("open_issues", Open_issues); writer.WriteIntValue("open_issues_count", Open_issues_count); - writer.WriteObjectValue("owner", Owner); + writer.WriteObjectValue("owner", Owner); writer.WriteObjectValue("permissions", Permissions); writer.WriteBoolValue("private", Private); writer.WriteStringValue("pulls_url", Pulls_url); writer.WriteDateTimeOffsetValue("pushed_at", Pushed_at); writer.WriteStringValue("releases_url", Releases_url); - writer.WriteDoubleValue("score", Score); + writer.WriteLongValue("score", Score); writer.WriteIntValue("size", Size); writer.WriteStringValue("ssh_url", Ssh_url); writer.WriteIntValue("stargazers_count", Stargazers_count); @@ -365,8 +387,10 @@ public void Serialize(ISerializationWriter writer) { writer.WriteStringValue("trees_url", Trees_url); writer.WriteDateTimeOffsetValue("updated_at", Updated_at); writer.WriteStringValue("url", Url); + writer.WriteStringValue("visibility", Visibility); writer.WriteIntValue("watchers", Watchers); writer.WriteIntValue("watchers_count", Watchers_count); + writer.WriteBoolValue("web_commit_signoff_required", Web_commit_signoff_required); writer.WriteAdditionalData(AdditionalData); } } diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/RepoSearchResultItem_permissions.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/RepoSearchResultItem_permissions.cs index cb7bb14037..cac2f504e6 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/RepoSearchResultItem_permissions.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/RepoSearchResultItem_permissions.cs @@ -9,10 +9,14 @@ public class RepoSearchResultItem_permissions : IAdditionalDataHolder, IParsable public IDictionary AdditionalData { get; set; } /// The admin property public bool? Admin { get; set; } + /// The maintain property + public bool? Maintain { get; set; } /// The pull property public bool? Pull { get; set; } /// The push property public bool? Push { get; set; } + /// The triage property + public bool? Triage { get; set; } /// /// Instantiates a new repoSearchResultItem_permissions and sets the default values. /// @@ -33,8 +37,10 @@ public static RepoSearchResultItem_permissions CreateFromDiscriminatorValue(IPar public IDictionary> GetFieldDeserializers() { return new Dictionary> { {"admin", n => { Admin = n.GetBoolValue(); } }, + {"maintain", n => { Maintain = n.GetBoolValue(); } }, {"pull", n => { Pull = n.GetBoolValue(); } }, {"push", n => { Push = n.GetBoolValue(); } }, + {"triage", n => { Triage = n.GetBoolValue(); } }, }; } /// @@ -44,8 +50,10 @@ public IDictionary> GetFieldDeserializers() { public void Serialize(ISerializationWriter writer) { _ = writer ?? throw new ArgumentNullException(nameof(writer)); writer.WriteBoolValue("admin", Admin); + writer.WriteBoolValue("maintain", Maintain); writer.WriteBoolValue("pull", Pull); writer.WriteBoolValue("push", Push); + writer.WriteBoolValue("triage", Triage); writer.WriteAdditionalData(AdditionalData); } } diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Repos/Item/Item/Contents/Item/WithPathDeleteRequestBody.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Repos/Item/Item/Contents/Item/WithPathDeleteRequestBody.cs index a8787e8462..492bc4a1e2 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Repos/Item/Item/Contents/Item/WithPathDeleteRequestBody.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Repos/Item/Item/Contents/Item/WithPathDeleteRequestBody.cs @@ -15,7 +15,7 @@ public class WithPathDeleteRequestBody : IAdditionalDataHolder, IParsable { public WithPathDeleteRequestBody_committer Committer { get; set; } /// The commit message. public string Message { get; set; } - /// The blob SHA of the file being replaced. + /// The blob SHA of the file being deleted. public string Sha { get; set; } /// /// Instantiates a new WithPathDeleteRequestBody and sets the default values. diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Repos/Item/Item/Contents/Item/WithPathItemRequestBuilder.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Repos/Item/Item/Contents/Item/WithPathItemRequestBuilder.cs index 4829a51132..6a249fc469 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Repos/Item/Item/Contents/Item/WithPathItemRequestBuilder.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Repos/Item/Item/Contents/Item/WithPathItemRequestBuilder.cs @@ -44,7 +44,7 @@ public WithPathItemRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) RequestAdapter = requestAdapter; } /// - /// Deletes a file in a repository.You can provide an additional `committer` parameter, which is an object containing information about the committer. Or, you can provide an `author` parameter, which is an object containing information about the author.The `author` section is optional and is filled in with the `committer` information if omitted. If the `committer` information is omitted, the authenticated user's information is used.You must provide values for both `name` and `email`, whether you choose to use `author` or `committer`. Otherwise, you'll receive a `422` status code. + /// Deletes a file in a repository.You can provide an additional `committer` parameter, which is an object containing information about the committer. Or, you can provide an `author` parameter, which is an object containing information about the author.The `author` section is optional and is filled in with the `committer` information if omitted. If the `committer` information is omitted, the authenticated user's information is used.You must provide values for both `name` and `email`, whether you choose to use `author` or `committer`. Otherwise, you'll receive a `422` status code.**Note:** If you use this endpoint and the "[Create or update file contents](https://docs.github.com/rest/reference/repos/#create-or-update-file-contents)" endpoint in parallel, the concurrent requests will conflict and you will receive errors. You must use these endpoints serially instead. /// /// /// Configuration for the request such as headers, query parameters, and middleware options. @@ -66,7 +66,7 @@ public RequestInformation CreateDeleteRequestInformation(WithPathDeleteRequestBo return requestInfo; } /// - /// Gets the contents of a file or directory in a repository. Specify the file path or directory in `:path`. If you omit`:path`, you will receive the contents of the repository's root directory. See the description below regarding what the API response includes for directories. Files and symlinks support [a custom media type](https://docs.github.com/rest/reference/repos#custom-media-types) forretrieving the raw content or rendered HTML (when supported). All content types support [a custom mediatype](https://docs.github.com/rest/reference/repos#custom-media-types) to ensure the content is returned in a consistentobject format.**Note**:* To get a repository's contents recursively, you can [recursively get the tree](https://docs.github.com/rest/reference/git#trees).* This API has an upper limit of 1,000 files for a directory. If you need to retrieve more files, use the [Git TreesAPI](https://docs.github.com/rest/reference/git#get-a-tree).* This API supports files up to 1 megabyte in size.#### If the content is a directoryThe response will be an array of objects, one object for each item in the directory.When listing the contents of a directory, submodules have their "type" specified as "file". Logically, the value_should_ be "submodule". This behavior exists in API v3 [for backwards compatibility purposes](https://git.io/v1YCW).In the next major version of the API, the type will be returned as "submodule".#### If the content is a symlink If the requested `:path` points to a symlink, and the symlink's target is a normal file in the repository, then theAPI responds with the content of the file (in the format shown in the example. Otherwise, the API responds with an object describing the symlink itself.#### If the content is a submoduleThe `submodule_git_url` identifies the location of the submodule repository, and the `sha` identifies a specificcommit within the submodule repository. Git uses the given URL when cloning the submodule repository, and checks outthe submodule at that specific commit.If the submodule repository is not hosted on github.com, the Git URLs (`git_url` and `_links["git"]`) and thegithub.com URLs (`html_url` and `_links["html"]`) will have null values. + /// Gets the contents of a file or directory in a repository. Specify the file path or directory in `:path`. If you omit`:path`, you will receive the contents of the repository's root directory. See the description below regarding what the API response includes for directories. Files and symlinks support [a custom media type](https://docs.github.com/rest/reference/repos#custom-media-types) forretrieving the raw content or rendered HTML (when supported). All content types support [a custom mediatype](https://docs.github.com/rest/reference/repos#custom-media-types) to ensure the content is returned in a consistentobject format.**Notes**:* To get a repository's contents recursively, you can [recursively get the tree](https://docs.github.com/rest/reference/git#trees).* This API has an upper limit of 1,000 files for a directory. If you need to retrieve more files, use the [Git TreesAPI](https://docs.github.com/rest/reference/git#get-a-tree). * Download URLs expire and are meant to be used just once. To ensure the download URL does not expire, please use the contents API to obtain a fresh download URL for each download.#### Size limitsIf the requested file's size is:* 1 MB or smaller: All features of this endpoint are supported.* Between 1-100 MB: Only the `raw` or `object` [custom media types](https://docs.github.com/rest/repos/contents#custom-media-types-for-repository-contents) are supported. Both will work as normal, except that when using the `object` media type, the `content` field will be an empty string and the `encoding` field will be `"none"`. To get the contents of these larger files, use the `raw` media type. * Greater than 100 MB: This endpoint is not supported.#### If the content is a directoryThe response will be an array of objects, one object for each item in the directory.When listing the contents of a directory, submodules have their "type" specified as "file". Logically, the value_should_ be "submodule". This behavior exists in API v3 [for backwards compatibility purposes](https://git.io/v1YCW).In the next major version of the API, the type will be returned as "submodule".#### If the content is a symlink If the requested `:path` points to a symlink, and the symlink's target is a normal file in the repository, then theAPI responds with the content of the file (in the format shown in the example. Otherwise, the API responds with an object describing the symlink itself.#### If the content is a submoduleThe `submodule_git_url` identifies the location of the submodule repository, and the `sha` identifies a specificcommit within the submodule repository. Git uses the given URL when cloning the submodule repository, and checks outthe submodule at that specific commit.If the submodule repository is not hosted on github.com, the Git URLs (`git_url` and `_links["git"]`) and thegithub.com URLs (`html_url` and `_links["html"]`) will have null values. /// /// Configuration for the request such as headers, query parameters, and middleware options. public RequestInformation CreateGetRequestInformation(Action requestConfiguration = default) { @@ -86,7 +86,7 @@ public RequestInformation CreateGetRequestInformation(Action - /// Creates a new file or replaces an existing file in a repository. + /// Creates a new file or replaces an existing file in a repository. You must authenticate using an access token with the `workflow` scope to use this endpoint.**Note:** If you use this endpoint and the "[Delete a file](https://docs.github.com/rest/reference/repos/#delete-file)" endpoint in parallel, the concurrent requests will conflict and you will receive errors. You must use these endpoints serially instead. /// /// /// Configuration for the request such as headers, query parameters, and middleware options. @@ -108,7 +108,7 @@ public RequestInformation CreatePutRequestInformation(WithPathPutRequestBody bod return requestInfo; } /// - /// Deletes a file in a repository.You can provide an additional `committer` parameter, which is an object containing information about the committer. Or, you can provide an `author` parameter, which is an object containing information about the author.The `author` section is optional and is filled in with the `committer` information if omitted. If the `committer` information is omitted, the authenticated user's information is used.You must provide values for both `name` and `email`, whether you choose to use `author` or `committer`. Otherwise, you'll receive a `422` status code. + /// Deletes a file in a repository.You can provide an additional `committer` parameter, which is an object containing information about the committer. Or, you can provide an `author` parameter, which is an object containing information about the author.The `author` section is optional and is filled in with the `committer` information if omitted. If the `committer` information is omitted, the authenticated user's information is used.You must provide values for both `name` and `email`, whether you choose to use `author` or `committer`. Otherwise, you'll receive a `422` status code.**Note:** If you use this endpoint and the "[Create or update file contents](https://docs.github.com/rest/reference/repos/#create-or-update-file-contents)" endpoint in parallel, the concurrent requests will conflict and you will receive errors. You must use these endpoints serially instead. /// /// /// Cancellation token to use when cancelling requests @@ -125,7 +125,7 @@ public async Task DeleteAsync(WithPathDeleteRequestBody body, Action return await RequestAdapter.SendAsync(requestInfo, FileCommit.CreateFromDiscriminatorValue, errorMapping, cancellationToken); } /// - /// Gets the contents of a file or directory in a repository. Specify the file path or directory in `:path`. If you omit`:path`, you will receive the contents of the repository's root directory. See the description below regarding what the API response includes for directories. Files and symlinks support [a custom media type](https://docs.github.com/rest/reference/repos#custom-media-types) forretrieving the raw content or rendered HTML (when supported). All content types support [a custom mediatype](https://docs.github.com/rest/reference/repos#custom-media-types) to ensure the content is returned in a consistentobject format.**Note**:* To get a repository's contents recursively, you can [recursively get the tree](https://docs.github.com/rest/reference/git#trees).* This API has an upper limit of 1,000 files for a directory. If you need to retrieve more files, use the [Git TreesAPI](https://docs.github.com/rest/reference/git#get-a-tree).* This API supports files up to 1 megabyte in size.#### If the content is a directoryThe response will be an array of objects, one object for each item in the directory.When listing the contents of a directory, submodules have their "type" specified as "file". Logically, the value_should_ be "submodule". This behavior exists in API v3 [for backwards compatibility purposes](https://git.io/v1YCW).In the next major version of the API, the type will be returned as "submodule".#### If the content is a symlink If the requested `:path` points to a symlink, and the symlink's target is a normal file in the repository, then theAPI responds with the content of the file (in the format shown in the example. Otherwise, the API responds with an object describing the symlink itself.#### If the content is a submoduleThe `submodule_git_url` identifies the location of the submodule repository, and the `sha` identifies a specificcommit within the submodule repository. Git uses the given URL when cloning the submodule repository, and checks outthe submodule at that specific commit.If the submodule repository is not hosted on github.com, the Git URLs (`git_url` and `_links["git"]`) and thegithub.com URLs (`html_url` and `_links["html"]`) will have null values. + /// Gets the contents of a file or directory in a repository. Specify the file path or directory in `:path`. If you omit`:path`, you will receive the contents of the repository's root directory. See the description below regarding what the API response includes for directories. Files and symlinks support [a custom media type](https://docs.github.com/rest/reference/repos#custom-media-types) forretrieving the raw content or rendered HTML (when supported). All content types support [a custom mediatype](https://docs.github.com/rest/reference/repos#custom-media-types) to ensure the content is returned in a consistentobject format.**Notes**:* To get a repository's contents recursively, you can [recursively get the tree](https://docs.github.com/rest/reference/git#trees).* This API has an upper limit of 1,000 files for a directory. If you need to retrieve more files, use the [Git TreesAPI](https://docs.github.com/rest/reference/git#get-a-tree). * Download URLs expire and are meant to be used just once. To ensure the download URL does not expire, please use the contents API to obtain a fresh download URL for each download.#### Size limitsIf the requested file's size is:* 1 MB or smaller: All features of this endpoint are supported.* Between 1-100 MB: Only the `raw` or `object` [custom media types](https://docs.github.com/rest/repos/contents#custom-media-types-for-repository-contents) are supported. Both will work as normal, except that when using the `object` media type, the `content` field will be an empty string and the `encoding` field will be `"none"`. To get the contents of these larger files, use the `raw` media type. * Greater than 100 MB: This endpoint is not supported.#### If the content is a directoryThe response will be an array of objects, one object for each item in the directory.When listing the contents of a directory, submodules have their "type" specified as "file". Logically, the value_should_ be "submodule". This behavior exists in API v3 [for backwards compatibility purposes](https://git.io/v1YCW).In the next major version of the API, the type will be returned as "submodule".#### If the content is a symlink If the requested `:path` points to a symlink, and the symlink's target is a normal file in the repository, then theAPI responds with the content of the file (in the format shown in the example. Otherwise, the API responds with an object describing the symlink itself.#### If the content is a submoduleThe `submodule_git_url` identifies the location of the submodule repository, and the `sha` identifies a specificcommit within the submodule repository. Git uses the given URL when cloning the submodule repository, and checks outthe submodule at that specific commit.If the submodule repository is not hosted on github.com, the Git URLs (`git_url` and `_links["git"]`) and thegithub.com URLs (`html_url` and `_links["html"]`) will have null values. /// /// Cancellation token to use when cancelling requests /// Configuration for the request such as headers, query parameters, and middleware options. @@ -138,7 +138,7 @@ public async Task GetAsync(Action(requestInfo, WithPathResponse.CreateFromDiscriminatorValue, errorMapping, cancellationToken); } /// - /// Creates a new file or replaces an existing file in a repository. + /// Creates a new file or replaces an existing file in a repository. You must authenticate using an access token with the `workflow` scope to use this endpoint.**Note:** If you use this endpoint and the "[Delete a file](https://docs.github.com/rest/reference/repos/#delete-file)" endpoint in parallel, the concurrent requests will conflict and you will receive errors. You must use these endpoints serially instead. /// /// /// Cancellation token to use when cancelling requests @@ -167,7 +167,7 @@ public WithPathItemRequestBuilderDeleteRequestConfiguration() { Headers = new Dictionary(); } } - /// Gets the contents of a file or directory in a repository. Specify the file path or directory in `:path`. If you omit`:path`, you will receive the contents of the repository's root directory. See the description below regarding what the API response includes for directories. Files and symlinks support [a custom media type](https://docs.github.com/rest/reference/repos#custom-media-types) forretrieving the raw content or rendered HTML (when supported). All content types support [a custom mediatype](https://docs.github.com/rest/reference/repos#custom-media-types) to ensure the content is returned in a consistentobject format.**Note**:* To get a repository's contents recursively, you can [recursively get the tree](https://docs.github.com/rest/reference/git#trees).* This API has an upper limit of 1,000 files for a directory. If you need to retrieve more files, use the [Git TreesAPI](https://docs.github.com/rest/reference/git#get-a-tree).* This API supports files up to 1 megabyte in size.#### If the content is a directoryThe response will be an array of objects, one object for each item in the directory.When listing the contents of a directory, submodules have their "type" specified as "file". Logically, the value_should_ be "submodule". This behavior exists in API v3 [for backwards compatibility purposes](https://git.io/v1YCW).In the next major version of the API, the type will be returned as "submodule".#### If the content is a symlink If the requested `:path` points to a symlink, and the symlink's target is a normal file in the repository, then theAPI responds with the content of the file (in the format shown in the example. Otherwise, the API responds with an object describing the symlink itself.#### If the content is a submoduleThe `submodule_git_url` identifies the location of the submodule repository, and the `sha` identifies a specificcommit within the submodule repository. Git uses the given URL when cloning the submodule repository, and checks outthe submodule at that specific commit.If the submodule repository is not hosted on github.com, the Git URLs (`git_url` and `_links["git"]`) and thegithub.com URLs (`html_url` and `_links["html"]`) will have null values. + /// Gets the contents of a file or directory in a repository. Specify the file path or directory in `:path`. If you omit`:path`, you will receive the contents of the repository's root directory. See the description below regarding what the API response includes for directories. Files and symlinks support [a custom media type](https://docs.github.com/rest/reference/repos#custom-media-types) forretrieving the raw content or rendered HTML (when supported). All content types support [a custom mediatype](https://docs.github.com/rest/reference/repos#custom-media-types) to ensure the content is returned in a consistentobject format.**Notes**:* To get a repository's contents recursively, you can [recursively get the tree](https://docs.github.com/rest/reference/git#trees).* This API has an upper limit of 1,000 files for a directory. If you need to retrieve more files, use the [Git TreesAPI](https://docs.github.com/rest/reference/git#get-a-tree). * Download URLs expire and are meant to be used just once. To ensure the download URL does not expire, please use the contents API to obtain a fresh download URL for each download.#### Size limitsIf the requested file's size is:* 1 MB or smaller: All features of this endpoint are supported.* Between 1-100 MB: Only the `raw` or `object` [custom media types](https://docs.github.com/rest/repos/contents#custom-media-types-for-repository-contents) are supported. Both will work as normal, except that when using the `object` media type, the `content` field will be an empty string and the `encoding` field will be `"none"`. To get the contents of these larger files, use the `raw` media type. * Greater than 100 MB: This endpoint is not supported.#### If the content is a directoryThe response will be an array of objects, one object for each item in the directory.When listing the contents of a directory, submodules have their "type" specified as "file". Logically, the value_should_ be "submodule". This behavior exists in API v3 [for backwards compatibility purposes](https://git.io/v1YCW).In the next major version of the API, the type will be returned as "submodule".#### If the content is a symlink If the requested `:path` points to a symlink, and the symlink's target is a normal file in the repository, then theAPI responds with the content of the file (in the format shown in the example. Otherwise, the API responds with an object describing the symlink itself.#### If the content is a submoduleThe `submodule_git_url` identifies the location of the submodule repository, and the `sha` identifies a specificcommit within the submodule repository. Git uses the given URL when cloning the submodule repository, and checks outthe submodule at that specific commit.If the submodule repository is not hosted on github.com, the Git URLs (`git_url` and `_links["git"]`) and thegithub.com URLs (`html_url` and `_links["html"]`) will have null values. public class WithPathItemRequestBuilderGetQueryParameters { /// The name of the commit/branch/tag. Default: the repository’s default branch (usually `master`) public string Ref { get; set; } diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Search/Repositories/RepositoriesRequestBuilder.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Search/Repositories/RepositoriesRequestBuilder.cs index f613f53201..78494d4762 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Search/Repositories/RepositoriesRequestBuilder.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Search/Repositories/RepositoriesRequestBuilder.cs @@ -44,7 +44,7 @@ public RepositoriesRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) RequestAdapter = requestAdapter; } /// - /// Find repositories via various criteria. This method returns up to 100 results [per page](https://docs.github.com/rest/overview/resources-in-the-rest-api#pagination).When searching for repositories, you can get text match metadata for the **name** and **description** fields when you pass the `text-match` media type. For more details about how to receive highlighted search results, see [Text match metadata](https://docs.github.com/rest/reference/search#text-match-metadata).For example, if you want to search for popular Tetris repositories written in assembly code, your query might look like this:`q=tetris+language:assembly&sort=stars&order=desc`This query searches for repositories with the word `tetris` in the name, the description, or the README. The results are limited to repositories where the primary language is assembly. The results are sorted by stars in descending order, so that the most popular repositories appear first in the search results.When you include the `mercy` preview header, you can also search for multiple topics by adding more `topic:` instances. For example, your query might look like this:`q=topic:ruby+topic:rails` + /// Find repositories via various criteria. This method returns up to 100 results [per page](https://docs.github.com/rest/overview/resources-in-the-rest-api#pagination).When searching for repositories, you can get text match metadata for the **name** and **description** fields when you pass the `text-match` media type. For more details about how to receive highlighted search results, see [Text match metadata](https://docs.github.com/rest/reference/search#text-match-metadata).For example, if you want to search for popular Tetris repositories written in assembly code, your query might look like this:`q=tetris+language:assembly&sort=stars&order=desc`This query searches for repositories with the word `tetris` in the name, the description, or the README. The results are limited to repositories where the primary language is assembly. The results are sorted by stars in descending order, so that the most popular repositories appear first in the search results. /// /// Configuration for the request such as headers, query parameters, and middleware options. public RequestInformation CreateGetRequestInformation(Action requestConfiguration = default) { @@ -64,7 +64,7 @@ public RequestInformation CreateGetRequestInformation(Action - /// Find repositories via various criteria. This method returns up to 100 results [per page](https://docs.github.com/rest/overview/resources-in-the-rest-api#pagination).When searching for repositories, you can get text match metadata for the **name** and **description** fields when you pass the `text-match` media type. For more details about how to receive highlighted search results, see [Text match metadata](https://docs.github.com/rest/reference/search#text-match-metadata).For example, if you want to search for popular Tetris repositories written in assembly code, your query might look like this:`q=tetris+language:assembly&sort=stars&order=desc`This query searches for repositories with the word `tetris` in the name, the description, or the README. The results are limited to repositories where the primary language is assembly. The results are sorted by stars in descending order, so that the most popular repositories appear first in the search results.When you include the `mercy` preview header, you can also search for multiple topics by adding more `topic:` instances. For example, your query might look like this:`q=topic:ruby+topic:rails` + /// Find repositories via various criteria. This method returns up to 100 results [per page](https://docs.github.com/rest/overview/resources-in-the-rest-api#pagination).When searching for repositories, you can get text match metadata for the **name** and **description** fields when you pass the `text-match` media type. For more details about how to receive highlighted search results, see [Text match metadata](https://docs.github.com/rest/reference/search#text-match-metadata).For example, if you want to search for popular Tetris repositories written in assembly code, your query might look like this:`q=tetris+language:assembly&sort=stars&order=desc`This query searches for repositories with the word `tetris` in the name, the description, or the README. The results are limited to repositories where the primary language is assembly. The results are sorted by stars in descending order, so that the most popular repositories appear first in the search results. /// /// Cancellation token to use when cancelling requests /// Configuration for the request such as headers, query parameters, and middleware options. @@ -76,15 +76,15 @@ public async Task GetAsync(Action(requestInfo, RepositoriesResponse.CreateFromDiscriminatorValue, errorMapping, cancellationToken); } - /// Find repositories via various criteria. This method returns up to 100 results [per page](https://docs.github.com/rest/overview/resources-in-the-rest-api#pagination).When searching for repositories, you can get text match metadata for the **name** and **description** fields when you pass the `text-match` media type. For more details about how to receive highlighted search results, see [Text match metadata](https://docs.github.com/rest/reference/search#text-match-metadata).For example, if you want to search for popular Tetris repositories written in assembly code, your query might look like this:`q=tetris+language:assembly&sort=stars&order=desc`This query searches for repositories with the word `tetris` in the name, the description, or the README. The results are limited to repositories where the primary language is assembly. The results are sorted by stars in descending order, so that the most popular repositories appear first in the search results.When you include the `mercy` preview header, you can also search for multiple topics by adding more `topic:` instances. For example, your query might look like this:`q=topic:ruby+topic:rails` + /// Find repositories via various criteria. This method returns up to 100 results [per page](https://docs.github.com/rest/overview/resources-in-the-rest-api#pagination).When searching for repositories, you can get text match metadata for the **name** and **description** fields when you pass the `text-match` media type. For more details about how to receive highlighted search results, see [Text match metadata](https://docs.github.com/rest/reference/search#text-match-metadata).For example, if you want to search for popular Tetris repositories written in assembly code, your query might look like this:`q=tetris+language:assembly&sort=stars&order=desc`This query searches for repositories with the word `tetris` in the name, the description, or the README. The results are limited to repositories where the primary language is assembly. The results are sorted by stars in descending order, so that the most popular repositories appear first in the search results. public class RepositoriesRequestBuilderGetQueryParameters { /// Determines whether the first search result returned is the highest number of matches (`desc`) or lowest number of matches (`asc`). This parameter is ignored unless you provide `sort`. public string Order { get; set; } /// Page number of the results to fetch. public int? Page { get; set; } - /// Results per page (max 100). + /// The number of results per page (max 100). public int? Per_page { get; set; } - /// The query contains one or more search keywords and qualifiers. Qualifiers allow you to limit your search to specific areas of GitHub. The REST API supports the same qualifiers as GitHub.com. To learn more about the format of the query, see [Constructing a search query](https://docs.github.com/rest/reference/search#constructing-a-search-query). See "[Searching for repositories](https://help.github.com/articles/searching-for-repositories/)" for a detailed list of qualifiers. + /// The query contains one or more search keywords and qualifiers. Qualifiers allow you to limit your search to specific areas of GitHub. The REST API supports the same qualifiers as the web interface for GitHub. To learn more about the format of the query, see [Constructing a search query](https://docs.github.com/rest/reference/search#constructing-a-search-query). See "[Searching for repositories](https://docs.github.com/articles/searching-for-repositories/)" for a detailed list of qualifiers. public string Q { get; set; } /// Sorts the results of your query by number of `stars`, `forks`, or `help-wanted-issues` or how recently the items were `updated`. Default: [best match](https://docs.github.com/rest/reference/search#ranking-search-results) public string Sort { get; set; } diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/kiota-lock.json b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/kiota-lock.json index 979288509b..606b0968c1 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/kiota-lock.json +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/kiota-lock.json @@ -1,6 +1,6 @@ { - "descriptionHash": "1A252F1952C159E7FD9243A2A5120597FD28DBF0A5691B5860E8016006E9154ED82172D56F26D8C2D1884E2B44BA9B3AE176CC5EF4C9C6FA9A797AC4772F7737", - "descriptionLocation": "https://api.apis.guru/v2/specs/github.com/api.github.com/1.1.4/openapi.json", + "descriptionHash": "A35A2B9B2E534DCD92AE698809F7BC4F2C009B4564B238084E2A76019A7323A4356E5E51A99EF143447E54927D58C0E519018A4CF7FB2E4A20600EB4B263F67C", + "descriptionLocation": "https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.2022-11-28.json", "lockFileVersion": "1.0.0", "kiotaVersion": "0.7.1.0", "clientClassName": "GitHubClient", From 0276638fe22794f7851b05d6c1a8e85095714864 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Wed, 30 Nov 2022 09:46:28 -0500 Subject: [PATCH 35/42] - fixes data types after client refresh Signed-off-by: Vincent Biret --- .../GitHub/GitHubClient/Models/RepoSearchResultItem.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/RepoSearchResultItem.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/RepoSearchResultItem.cs index 26c5926d1f..0f8652adc8 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/RepoSearchResultItem.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/RepoSearchResultItem.cs @@ -145,7 +145,7 @@ public class RepoSearchResultItem : IAdditionalDataHolder, IParsable { /// The releases_url property public string Releases_url { get; set; } /// The score property - public long? Score { get; set; } + public double? Score { get; set; } /// The size property public int? Size { get; set; } /// The ssh_url property @@ -273,7 +273,7 @@ public IDictionary> GetFieldDeserializers() { {"pulls_url", n => { Pulls_url = n.GetStringValue(); } }, {"pushed_at", n => { Pushed_at = n.GetDateTimeOffsetValue(); } }, {"releases_url", n => { Releases_url = n.GetStringValue(); } }, - {"score", n => { Score = n.GetLongValue(); } }, + {"score", n => { Score = n.GetDoubleValue(); } }, {"size", n => { Size = n.GetIntValue(); } }, {"ssh_url", n => { Ssh_url = n.GetStringValue(); } }, {"stargazers_count", n => { Stargazers_count = n.GetIntValue(); } }, @@ -370,7 +370,7 @@ public void Serialize(ISerializationWriter writer) { writer.WriteStringValue("pulls_url", Pulls_url); writer.WriteDateTimeOffsetValue("pushed_at", Pushed_at); writer.WriteStringValue("releases_url", Releases_url); - writer.WriteLongValue("score", Score); + writer.WriteDoubleValue("score", Score); writer.WriteIntValue("size", Size); writer.WriteStringValue("ssh_url", Ssh_url); writer.WriteIntValue("stargazers_count", Stargazers_count); From 19ab2cc00867c26383f67ed9030d45b39ab09dda Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Wed, 30 Nov 2022 10:57:31 -0500 Subject: [PATCH 36/42] - adds installations to github client Signed-off-by: Vincent Biret --- .../GitHub/GitHubClient/GitHubClient.cs | 5 + .../GitHubClient/Models/AppPermissions.cs | 173 ++++++++++++++++++ .../Models/AppPermissions_actions.cs | 6 + .../Models/AppPermissions_administration.cs | 6 + .../Models/AppPermissions_checks.cs | 6 + .../Models/AppPermissions_contents.cs | 6 + .../Models/AppPermissions_deployments.cs | 6 + .../Models/AppPermissions_environments.cs | 6 + .../Models/AppPermissions_issues.cs | 6 + .../Models/AppPermissions_members.cs | 6 + .../Models/AppPermissions_metadata.cs | 6 + ...Permissions_organization_administration.cs | 6 + ...sions_organization_announcement_banners.cs | 6 + ...ppPermissions_organization_custom_roles.cs | 6 + .../AppPermissions_organization_hooks.cs | 6 + .../AppPermissions_organization_packages.cs | 6 + .../AppPermissions_organization_plan.cs | 5 + .../AppPermissions_organization_projects.cs | 7 + .../AppPermissions_organization_secrets.cs | 6 + ...ssions_organization_self_hosted_runners.cs | 6 + ...pPermissions_organization_user_blocking.cs | 6 + .../Models/AppPermissions_packages.cs | 6 + .../Models/AppPermissions_pages.cs | 6 + .../Models/AppPermissions_pull_requests.cs | 6 + ...issions_repository_announcement_banners.cs | 6 + .../Models/AppPermissions_repository_hooks.cs | 6 + .../AppPermissions_repository_projects.cs | 7 + .../AppPermissions_secret_scanning_alerts.cs | 6 + .../Models/AppPermissions_secrets.cs | 6 + .../Models/AppPermissions_security_events.cs | 6 + .../Models/AppPermissions_single_file.cs | 6 + .../Models/AppPermissions_statuses.cs | 6 + .../Models/AppPermissions_team_discussions.cs | 6 + .../AppPermissions_vulnerability_alerts.cs | 6 + .../Models/AppPermissions_workflows.cs | 5 + .../GitHub/GitHubClient/Models/Enterprise.cs | 81 ++++++++ .../GitHubClient/Models/Installation.cs | 167 +++++++++++++++++ .../Installation_repository_selection.cs | 6 + .../GitHub/GitHubClient/Models/SimpleUser.cs | 10 +- .../InstallationsRequestBuilder.cs | 103 +++++++++++ .../Installations/InstallationsResponse.cs | 49 +++++ .../GitHubClient/User/UserRequestBuilder.cs | 49 +++++ .../GitHub/GitHubClient/kiota-lock.json | 5 +- 43 files changed, 843 insertions(+), 3 deletions(-) create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_actions.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_administration.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_checks.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_contents.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_deployments.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_environments.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_issues.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_members.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_metadata.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_administration.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_announcement_banners.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_custom_roles.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_hooks.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_packages.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_plan.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_projects.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_secrets.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_self_hosted_runners.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_user_blocking.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_packages.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_pages.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_pull_requests.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_repository_announcement_banners.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_repository_hooks.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_repository_projects.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_secret_scanning_alerts.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_secrets.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_security_events.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_single_file.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_statuses.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_team_discussions.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_vulnerability_alerts.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_workflows.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/Enterprise.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/Installation.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/Installation_repository_selection.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/User/Installations/InstallationsRequestBuilder.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/User/Installations/InstallationsResponse.cs create mode 100644 src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/User/UserRequestBuilder.cs diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/GitHubClient.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/GitHubClient.cs index a84fd1fac0..37b9d308a2 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/GitHubClient.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/GitHubClient.cs @@ -1,5 +1,6 @@ using Kiota.Builder.SearchProviders.GitHub.GitHubClient.Repos; using Kiota.Builder.SearchProviders.GitHub.GitHubClient.Search; +using Kiota.Builder.SearchProviders.GitHub.GitHubClient.User; using Microsoft.Kiota.Abstractions; using Microsoft.Kiota.Serialization.Json; using Microsoft.Kiota.Serialization.Text; @@ -25,6 +26,10 @@ public class GitHubClient { } /// Url template to use to build the URL for the current request builder private string UrlTemplate { get; set; } + /// The user property + public UserRequestBuilder User { get => + new UserRequestBuilder(PathParameters, RequestAdapter); + } /// /// Instantiates a new GitHubClient and sets the default values. /// diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions.cs new file mode 100644 index 0000000000..a4a89bfe26 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions.cs @@ -0,0 +1,173 @@ +using Microsoft.Kiota.Abstractions.Serialization; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + /// The permissions granted to the user-to-server access token. + public class AppPermissions : IAdditionalDataHolder, IParsable { + /// The level of permission to grant the access token for GitHub Actions workflows, workflow runs, and artifacts. + public AppPermissions_actions? Actions { get; set; } + /// 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 level of permission to grant the access token for repository creation, deletion, settings, teams, and collaborators creation. + public AppPermissions_administration? Administration { get; set; } + /// The level of permission to grant the access token for checks on code. + public AppPermissions_checks? Checks { get; set; } + /// The level of permission to grant the access token for repository contents, commits, branches, downloads, releases, and merges. + public AppPermissions_contents? Contents { get; set; } + /// The level of permission to grant the access token for deployments and deployment statuses. + public AppPermissions_deployments? Deployments { get; set; } + /// The level of permission to grant the access token for managing repository environments. + public AppPermissions_environments? Environments { get; set; } + /// The level of permission to grant the access token for issues and related comments, assignees, labels, and milestones. + public AppPermissions_issues? Issues { get; set; } + /// The level of permission to grant the access token for organization teams and members. + public AppPermissions_members? Members { get; set; } + /// The level of permission to grant the access token to search repositories, list collaborators, and access repository metadata. + public AppPermissions_metadata? Metadata { get; set; } + /// The level of permission to grant the access token to manage access to an organization. + public AppPermissions_organization_administration? Organization_administration { get; set; } + /// The level of permission to grant the access token to view and manage announcement banners for an organization. + public AppPermissions_organization_announcement_banners? Organization_announcement_banners { get; set; } + /// The level of permission to grant the access token for custom roles management. This property is in beta and is subject to change. + public AppPermissions_organization_custom_roles? Organization_custom_roles { get; set; } + /// The level of permission to grant the access token to manage the post-receive hooks for an organization. + public AppPermissions_organization_hooks? Organization_hooks { get; set; } + /// The level of permission to grant the access token for organization packages published to GitHub Packages. + public AppPermissions_organization_packages? Organization_packages { get; set; } + /// The level of permission to grant the access token for viewing an organization's plan. + public AppPermissions_organization_plan? Organization_plan { get; set; } + /// The level of permission to grant the access token to manage organization projects and projects beta (where available). + public AppPermissions_organization_projects? Organization_projects { get; set; } + /// The level of permission to grant the access token to manage organization secrets. + public AppPermissions_organization_secrets? Organization_secrets { get; set; } + /// The level of permission to grant the access token to view and manage GitHub Actions self-hosted runners available to an organization. + public AppPermissions_organization_self_hosted_runners? Organization_self_hosted_runners { get; set; } + /// The level of permission to grant the access token to view and manage users blocked by the organization. + public AppPermissions_organization_user_blocking? Organization_user_blocking { get; set; } + /// The level of permission to grant the access token for packages published to GitHub Packages. + public AppPermissions_packages? Packages { get; set; } + /// The level of permission to grant the access token to retrieve Pages statuses, configuration, and builds, as well as create new builds. + public AppPermissions_pages? Pages { get; set; } + /// The level of permission to grant the access token for pull requests and related comments, assignees, labels, milestones, and merges. + public AppPermissions_pull_requests? Pull_requests { get; set; } + /// The level of permission to grant the access token to view and manage announcement banners for a repository. + public AppPermissions_repository_announcement_banners? Repository_announcement_banners { get; set; } + /// The level of permission to grant the access token to manage the post-receive hooks for a repository. + public AppPermissions_repository_hooks? Repository_hooks { get; set; } + /// The level of permission to grant the access token to manage repository projects, columns, and cards. + public AppPermissions_repository_projects? Repository_projects { get; set; } + /// The level of permission to grant the access token to view and manage secret scanning alerts. + public AppPermissions_secret_scanning_alerts? Secret_scanning_alerts { get; set; } + /// The level of permission to grant the access token to manage repository secrets. + public AppPermissions_secrets? Secrets { get; set; } + /// The level of permission to grant the access token to view and manage security events like code scanning alerts. + public AppPermissions_security_events? Security_events { get; set; } + /// The level of permission to grant the access token to manage just a single file. + public AppPermissions_single_file? Single_file { get; set; } + /// The level of permission to grant the access token for commit statuses. + public AppPermissions_statuses? Statuses { get; set; } + /// The level of permission to grant the access token to manage team discussions and related comments. + public AppPermissions_team_discussions? Team_discussions { get; set; } + /// The level of permission to grant the access token to manage Dependabot alerts. + public AppPermissions_vulnerability_alerts? Vulnerability_alerts { get; set; } + /// The level of permission to grant the access token to update GitHub Actions workflow files. + public AppPermissions_workflows? Workflows { get; set; } + /// + /// Instantiates a new appPermissions and sets the default values. + /// + public AppPermissions() { + AdditionalData = new Dictionary(); + } + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// The parse node to use to read the discriminator value and create the object + public static AppPermissions CreateFromDiscriminatorValue(IParseNode parseNode) { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new AppPermissions(); + } + /// + /// The deserialization information for the current model + /// + public IDictionary> GetFieldDeserializers() { + return new Dictionary> { + {"actions", n => { Actions = n.GetEnumValue(); } }, + {"administration", n => { Administration = n.GetEnumValue(); } }, + {"checks", n => { Checks = n.GetEnumValue(); } }, + {"contents", n => { Contents = n.GetEnumValue(); } }, + {"deployments", n => { Deployments = n.GetEnumValue(); } }, + {"environments", n => { Environments = n.GetEnumValue(); } }, + {"issues", n => { Issues = n.GetEnumValue(); } }, + {"members", n => { Members = n.GetEnumValue(); } }, + {"metadata", n => { Metadata = n.GetEnumValue(); } }, + {"organization_administration", n => { Organization_administration = n.GetEnumValue(); } }, + {"organization_announcement_banners", n => { Organization_announcement_banners = n.GetEnumValue(); } }, + {"organization_custom_roles", n => { Organization_custom_roles = n.GetEnumValue(); } }, + {"organization_hooks", n => { Organization_hooks = n.GetEnumValue(); } }, + {"organization_packages", n => { Organization_packages = n.GetEnumValue(); } }, + {"organization_plan", n => { Organization_plan = n.GetEnumValue(); } }, + {"organization_projects", n => { Organization_projects = n.GetEnumValue(); } }, + {"organization_secrets", n => { Organization_secrets = n.GetEnumValue(); } }, + {"organization_self_hosted_runners", n => { Organization_self_hosted_runners = n.GetEnumValue(); } }, + {"organization_user_blocking", n => { Organization_user_blocking = n.GetEnumValue(); } }, + {"packages", n => { Packages = n.GetEnumValue(); } }, + {"pages", n => { Pages = n.GetEnumValue(); } }, + {"pull_requests", n => { Pull_requests = n.GetEnumValue(); } }, + {"repository_announcement_banners", n => { Repository_announcement_banners = n.GetEnumValue(); } }, + {"repository_hooks", n => { Repository_hooks = n.GetEnumValue(); } }, + {"repository_projects", n => { Repository_projects = n.GetEnumValue(); } }, + {"secret_scanning_alerts", n => { Secret_scanning_alerts = n.GetEnumValue(); } }, + {"secrets", n => { Secrets = n.GetEnumValue(); } }, + {"security_events", n => { Security_events = n.GetEnumValue(); } }, + {"single_file", n => { Single_file = n.GetEnumValue(); } }, + {"statuses", n => { Statuses = n.GetEnumValue(); } }, + {"team_discussions", n => { Team_discussions = n.GetEnumValue(); } }, + {"vulnerability_alerts", n => { Vulnerability_alerts = n.GetEnumValue(); } }, + {"workflows", n => { Workflows = n.GetEnumValue(); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public void Serialize(ISerializationWriter writer) { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteEnumValue("actions", Actions); + writer.WriteEnumValue("administration", Administration); + writer.WriteEnumValue("checks", Checks); + writer.WriteEnumValue("contents", Contents); + writer.WriteEnumValue("deployments", Deployments); + writer.WriteEnumValue("environments", Environments); + writer.WriteEnumValue("issues", Issues); + writer.WriteEnumValue("members", Members); + writer.WriteEnumValue("metadata", Metadata); + writer.WriteEnumValue("organization_administration", Organization_administration); + writer.WriteEnumValue("organization_announcement_banners", Organization_announcement_banners); + writer.WriteEnumValue("organization_custom_roles", Organization_custom_roles); + writer.WriteEnumValue("organization_hooks", Organization_hooks); + writer.WriteEnumValue("organization_packages", Organization_packages); + writer.WriteEnumValue("organization_plan", Organization_plan); + writer.WriteEnumValue("organization_projects", Organization_projects); + writer.WriteEnumValue("organization_secrets", Organization_secrets); + writer.WriteEnumValue("organization_self_hosted_runners", Organization_self_hosted_runners); + writer.WriteEnumValue("organization_user_blocking", Organization_user_blocking); + writer.WriteEnumValue("packages", Packages); + writer.WriteEnumValue("pages", Pages); + writer.WriteEnumValue("pull_requests", Pull_requests); + writer.WriteEnumValue("repository_announcement_banners", Repository_announcement_banners); + writer.WriteEnumValue("repository_hooks", Repository_hooks); + writer.WriteEnumValue("repository_projects", Repository_projects); + writer.WriteEnumValue("secret_scanning_alerts", Secret_scanning_alerts); + writer.WriteEnumValue("secrets", Secrets); + writer.WriteEnumValue("security_events", Security_events); + writer.WriteEnumValue("single_file", Single_file); + writer.WriteEnumValue("statuses", Statuses); + writer.WriteEnumValue("team_discussions", Team_discussions); + writer.WriteEnumValue("vulnerability_alerts", Vulnerability_alerts); + writer.WriteEnumValue("workflows", Workflows); + writer.WriteAdditionalData(AdditionalData); + } + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_actions.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_actions.cs new file mode 100644 index 0000000000..04903e27a6 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_actions.cs @@ -0,0 +1,6 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_actions { + Read, + Write, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_administration.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_administration.cs new file mode 100644 index 0000000000..981285723f --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_administration.cs @@ -0,0 +1,6 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_administration { + Read, + Write, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_checks.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_checks.cs new file mode 100644 index 0000000000..982bc66166 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_checks.cs @@ -0,0 +1,6 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_checks { + Read, + Write, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_contents.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_contents.cs new file mode 100644 index 0000000000..458815ffdc --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_contents.cs @@ -0,0 +1,6 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_contents { + Read, + Write, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_deployments.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_deployments.cs new file mode 100644 index 0000000000..d213aefab2 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_deployments.cs @@ -0,0 +1,6 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_deployments { + Read, + Write, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_environments.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_environments.cs new file mode 100644 index 0000000000..21e8c02f0c --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_environments.cs @@ -0,0 +1,6 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_environments { + Read, + Write, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_issues.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_issues.cs new file mode 100644 index 0000000000..e0baedeb72 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_issues.cs @@ -0,0 +1,6 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_issues { + Read, + Write, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_members.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_members.cs new file mode 100644 index 0000000000..683707ff54 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_members.cs @@ -0,0 +1,6 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_members { + Read, + Write, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_metadata.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_metadata.cs new file mode 100644 index 0000000000..712fb02d36 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_metadata.cs @@ -0,0 +1,6 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_metadata { + Read, + Write, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_administration.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_administration.cs new file mode 100644 index 0000000000..a41e0ce763 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_administration.cs @@ -0,0 +1,6 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_organization_administration { + Read, + Write, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_announcement_banners.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_announcement_banners.cs new file mode 100644 index 0000000000..fca927f749 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_announcement_banners.cs @@ -0,0 +1,6 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_organization_announcement_banners { + Read, + Write, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_custom_roles.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_custom_roles.cs new file mode 100644 index 0000000000..62545f1ae7 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_custom_roles.cs @@ -0,0 +1,6 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_organization_custom_roles { + Read, + Write, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_hooks.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_hooks.cs new file mode 100644 index 0000000000..11377406ab --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_hooks.cs @@ -0,0 +1,6 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_organization_hooks { + Read, + Write, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_packages.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_packages.cs new file mode 100644 index 0000000000..e590ded0c2 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_packages.cs @@ -0,0 +1,6 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_organization_packages { + Read, + Write, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_plan.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_plan.cs new file mode 100644 index 0000000000..47da9d97a5 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_plan.cs @@ -0,0 +1,5 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_organization_plan { + Read, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_projects.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_projects.cs new file mode 100644 index 0000000000..15eb998994 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_projects.cs @@ -0,0 +1,7 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_organization_projects { + Read, + Write, + Admin, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_secrets.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_secrets.cs new file mode 100644 index 0000000000..4453a97a85 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_secrets.cs @@ -0,0 +1,6 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_organization_secrets { + Read, + Write, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_self_hosted_runners.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_self_hosted_runners.cs new file mode 100644 index 0000000000..a551f4e8f5 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_self_hosted_runners.cs @@ -0,0 +1,6 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_organization_self_hosted_runners { + Read, + Write, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_user_blocking.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_user_blocking.cs new file mode 100644 index 0000000000..63169bcdc7 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_organization_user_blocking.cs @@ -0,0 +1,6 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_organization_user_blocking { + Read, + Write, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_packages.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_packages.cs new file mode 100644 index 0000000000..5004da8605 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_packages.cs @@ -0,0 +1,6 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_packages { + Read, + Write, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_pages.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_pages.cs new file mode 100644 index 0000000000..6af56a7a90 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_pages.cs @@ -0,0 +1,6 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_pages { + Read, + Write, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_pull_requests.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_pull_requests.cs new file mode 100644 index 0000000000..c3d5818b86 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_pull_requests.cs @@ -0,0 +1,6 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_pull_requests { + Read, + Write, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_repository_announcement_banners.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_repository_announcement_banners.cs new file mode 100644 index 0000000000..ee4aa28b8b --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_repository_announcement_banners.cs @@ -0,0 +1,6 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_repository_announcement_banners { + Read, + Write, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_repository_hooks.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_repository_hooks.cs new file mode 100644 index 0000000000..6725ec4631 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_repository_hooks.cs @@ -0,0 +1,6 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_repository_hooks { + Read, + Write, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_repository_projects.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_repository_projects.cs new file mode 100644 index 0000000000..a60eec89c6 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_repository_projects.cs @@ -0,0 +1,7 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_repository_projects { + Read, + Write, + Admin, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_secret_scanning_alerts.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_secret_scanning_alerts.cs new file mode 100644 index 0000000000..0788a95477 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_secret_scanning_alerts.cs @@ -0,0 +1,6 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_secret_scanning_alerts { + Read, + Write, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_secrets.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_secrets.cs new file mode 100644 index 0000000000..2cc3ae4a67 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_secrets.cs @@ -0,0 +1,6 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_secrets { + Read, + Write, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_security_events.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_security_events.cs new file mode 100644 index 0000000000..b01a462df6 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_security_events.cs @@ -0,0 +1,6 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_security_events { + Read, + Write, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_single_file.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_single_file.cs new file mode 100644 index 0000000000..0b61a52140 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_single_file.cs @@ -0,0 +1,6 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_single_file { + Read, + Write, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_statuses.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_statuses.cs new file mode 100644 index 0000000000..62b4c458b0 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_statuses.cs @@ -0,0 +1,6 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_statuses { + Read, + Write, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_team_discussions.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_team_discussions.cs new file mode 100644 index 0000000000..3d58564d95 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_team_discussions.cs @@ -0,0 +1,6 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_team_discussions { + Read, + Write, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_vulnerability_alerts.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_vulnerability_alerts.cs new file mode 100644 index 0000000000..3e97a6c21d --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_vulnerability_alerts.cs @@ -0,0 +1,6 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_vulnerability_alerts { + Read, + Write, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_workflows.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_workflows.cs new file mode 100644 index 0000000000..c3a37fd1b2 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/AppPermissions_workflows.cs @@ -0,0 +1,5 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum AppPermissions_workflows { + Write, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/Enterprise.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/Enterprise.cs new file mode 100644 index 0000000000..6cb7f5d0a3 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/Enterprise.cs @@ -0,0 +1,81 @@ +using Microsoft.Kiota.Abstractions.Serialization; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + /// An enterprise on GitHub. + public class Enterprise : IAdditionalDataHolder, IParsable { + /// 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 avatar_url property + public string Avatar_url { get; set; } + /// The created_at property + public DateTimeOffset? Created_at { get; set; } + /// A short description of the enterprise. + public string Description { get; set; } + /// The html_url property + public string Html_url { get; set; } + /// Unique identifier of the enterprise + public int? Id { get; set; } + /// The name of the enterprise. + public string Name { get; set; } + /// The node_id property + public string Node_id { get; set; } + /// The slug url identifier for the enterprise. + public string Slug { get; set; } + /// The updated_at property + public DateTimeOffset? Updated_at { get; set; } + /// The enterprise's website URL. + public string Website_url { get; set; } + /// + /// Instantiates a new Enterprise and sets the default values. + /// + public Enterprise() { + AdditionalData = new Dictionary(); + } + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// The parse node to use to read the discriminator value and create the object + public static Enterprise CreateFromDiscriminatorValue(IParseNode parseNode) { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new Enterprise(); + } + /// + /// The deserialization information for the current model + /// + public IDictionary> GetFieldDeserializers() { + return new Dictionary> { + {"avatar_url", n => { Avatar_url = n.GetStringValue(); } }, + {"created_at", n => { Created_at = n.GetDateTimeOffsetValue(); } }, + {"description", n => { Description = n.GetStringValue(); } }, + {"html_url", n => { Html_url = n.GetStringValue(); } }, + {"id", n => { Id = n.GetIntValue(); } }, + {"name", n => { Name = n.GetStringValue(); } }, + {"node_id", n => { Node_id = n.GetStringValue(); } }, + {"slug", n => { Slug = n.GetStringValue(); } }, + {"updated_at", n => { Updated_at = n.GetDateTimeOffsetValue(); } }, + {"website_url", n => { Website_url = n.GetStringValue(); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public void Serialize(ISerializationWriter writer) { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteStringValue("avatar_url", Avatar_url); + writer.WriteDateTimeOffsetValue("created_at", Created_at); + writer.WriteStringValue("description", Description); + writer.WriteStringValue("html_url", Html_url); + writer.WriteIntValue("id", Id); + writer.WriteStringValue("name", Name); + writer.WriteStringValue("node_id", Node_id); + writer.WriteStringValue("slug", Slug); + writer.WriteDateTimeOffsetValue("updated_at", Updated_at); + writer.WriteStringValue("website_url", Website_url); + writer.WriteAdditionalData(AdditionalData); + } + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/Installation.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/Installation.cs new file mode 100644 index 0000000000..ea24190785 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/Installation.cs @@ -0,0 +1,167 @@ +using Microsoft.Kiota.Abstractions.Serialization; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + /// Installation + public class Installation : IAdditionalDataHolder, IParsable { + /// The access_tokens_url property + public string Access_tokens_url { get; set; } + /// The account property + public Installations Account { get; set; } + /// 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 app_id property + public int? App_id { get; set; } + /// The app_slug property + public string App_slug { get; set; } + /// The contact_email property + public string Contact_email { get; set; } + /// The created_at property + public DateTimeOffset? Created_at { get; set; } + /// The events property + public List Events { get; set; } + /// The has_multiple_single_files property + public bool? Has_multiple_single_files { get; set; } + /// The html_url property + public string Html_url { get; set; } + /// The ID of the installation. + public int? Id { get; set; } + /// The permissions granted to the user-to-server access token. + public AppPermissions Permissions { get; set; } + /// The repositories_url property + public string Repositories_url { get; set; } + /// Describe whether all repositories have been selected or there's a selection involved + public Installation_repository_selection? Repository_selection { get; set; } + /// The single_file_name property + public string Single_file_name { get; set; } + /// The single_file_paths property + public List Single_file_paths { get; set; } + /// The suspended_at property + public DateTimeOffset? Suspended_at { get; set; } + /// A GitHub user. + public NullableSimpleUser Suspended_by { get; set; } + /// The ID of the user or organization this token is being scoped to. + public int? Target_id { get; set; } + /// The target_type property + public string Target_type { get; set; } + /// The updated_at property + public DateTimeOffset? Updated_at { get; set; } + /// + /// Instantiates a new installation and sets the default values. + /// + public Installation() { + AdditionalData = new Dictionary(); + } + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// The parse node to use to read the discriminator value and create the object + public static Installation CreateFromDiscriminatorValue(IParseNode parseNode) { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new Installation(); + } + /// + /// The deserialization information for the current model + /// + public IDictionary> GetFieldDeserializers() { + return new Dictionary> { + {"access_tokens_url", n => { Access_tokens_url = n.GetStringValue(); } }, + {"account", n => { Account = n.GetObjectValue(Installations.CreateFromDiscriminatorValue); } }, + {"app_id", n => { App_id = n.GetIntValue(); } }, + {"app_slug", n => { App_slug = n.GetStringValue(); } }, + {"contact_email", n => { Contact_email = n.GetStringValue(); } }, + {"created_at", n => { Created_at = n.GetDateTimeOffsetValue(); } }, + {"events", n => { Events = n.GetCollectionOfPrimitiveValues()?.ToList(); } }, + {"has_multiple_single_files", n => { Has_multiple_single_files = n.GetBoolValue(); } }, + {"html_url", n => { Html_url = n.GetStringValue(); } }, + {"id", n => { Id = n.GetIntValue(); } }, + {"permissions", n => { Permissions = n.GetObjectValue(AppPermissions.CreateFromDiscriminatorValue); } }, + {"repositories_url", n => { Repositories_url = n.GetStringValue(); } }, + {"repository_selection", n => { Repository_selection = n.GetEnumValue(); } }, + {"single_file_name", n => { Single_file_name = n.GetStringValue(); } }, + {"single_file_paths", n => { Single_file_paths = n.GetCollectionOfPrimitiveValues()?.ToList(); } }, + {"suspended_at", n => { Suspended_at = n.GetDateTimeOffsetValue(); } }, + {"suspended_by", n => { Suspended_by = n.GetObjectValue(NullableSimpleUser.CreateFromDiscriminatorValue); } }, + {"target_id", n => { Target_id = n.GetIntValue(); } }, + {"target_type", n => { Target_type = n.GetStringValue(); } }, + {"updated_at", n => { Updated_at = n.GetDateTimeOffsetValue(); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public void Serialize(ISerializationWriter writer) { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteStringValue("access_tokens_url", Access_tokens_url); + writer.WriteObjectValue("account", Account); + writer.WriteIntValue("app_id", App_id); + writer.WriteStringValue("app_slug", App_slug); + writer.WriteStringValue("contact_email", Contact_email); + writer.WriteDateTimeOffsetValue("created_at", Created_at); + writer.WriteCollectionOfPrimitiveValues("events", Events); + writer.WriteBoolValue("has_multiple_single_files", Has_multiple_single_files); + writer.WriteStringValue("html_url", Html_url); + writer.WriteIntValue("id", Id); + writer.WriteObjectValue("permissions", Permissions); + writer.WriteStringValue("repositories_url", Repositories_url); + writer.WriteEnumValue("repository_selection", Repository_selection); + writer.WriteStringValue("single_file_name", Single_file_name); + writer.WriteCollectionOfPrimitiveValues("single_file_paths", Single_file_paths); + writer.WriteDateTimeOffsetValue("suspended_at", Suspended_at); + writer.WriteObjectValue("suspended_by", Suspended_by); + writer.WriteIntValue("target_id", Target_id); + writer.WriteStringValue("target_type", Target_type); + writer.WriteDateTimeOffsetValue("updated_at", Updated_at); + writer.WriteAdditionalData(AdditionalData); + } + /// Composed type wrapper for classes simpleUser, enterprise + public class Installations : IAdditionalDataHolder, IParsable { + /// Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. + public IDictionary AdditionalData { get; set; } + /// Composed type representation for type enterprise + public Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models.Enterprise Enterprise { get; set; } + /// Serialization hint for the current wrapper. + public string SerializationHint { get; set; } + /// Composed type representation for type simpleUser + public Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models.SimpleUser SimpleUser { get; set; } + /// + /// Instantiates a new installations and sets the default values. + /// + public Installations() { + AdditionalData = new Dictionary(); + } + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// The parse node to use to read the discriminator value and create the object + public static Installations CreateFromDiscriminatorValue(IParseNode parseNode) { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + var result = new Installations(); + result.Enterprise = new Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models.Enterprise(); + result.SimpleUser = new Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models.SimpleUser(); + return result; + } + /// + /// The deserialization information for the current model + /// + public IDictionary> GetFieldDeserializers() { + if(Enterprise != null || SimpleUser != null) { + return ParseNodeHelper.MergeDeserializersForIntersectionWrapper(Enterprise, SimpleUser); + } + return new Dictionary>(); + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public void Serialize(ISerializationWriter writer) { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteObjectValue(null, Enterprise, SimpleUser); + writer.WriteAdditionalData(AdditionalData); + } + } + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/Installation_repository_selection.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/Installation_repository_selection.cs new file mode 100644 index 0000000000..0905283798 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/Installation_repository_selection.cs @@ -0,0 +1,6 @@ +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { + public enum Installation_repository_selection { + All, + Selected, + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/SimpleUser.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/SimpleUser.cs index 5a0bda41f1..632bb4705e 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/SimpleUser.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/Models/SimpleUser.cs @@ -4,12 +4,14 @@ using System.IO; using System.Linq; namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models { - /// Simple User + /// A GitHub user. public class SimpleUser : IAdditionalDataHolder, IParsable { /// 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 avatar_url property public string Avatar_url { get; set; } + /// The email property + public string Email { get; set; } /// The events_url property public string Events_url { get; set; } /// The followers_url property @@ -26,6 +28,8 @@ public class SimpleUser : IAdditionalDataHolder, IParsable { public int? Id { get; set; } /// The login property public string Login { get; set; } + /// The name property + public string Name { get; set; } /// The node_id property public string Node_id { get; set; } /// The organizations_url property @@ -66,6 +70,7 @@ public static SimpleUser CreateFromDiscriminatorValue(IParseNode parseNode) { public IDictionary> GetFieldDeserializers() { return new Dictionary> { {"avatar_url", n => { Avatar_url = n.GetStringValue(); } }, + {"email", n => { Email = n.GetStringValue(); } }, {"events_url", n => { Events_url = n.GetStringValue(); } }, {"followers_url", n => { Followers_url = n.GetStringValue(); } }, {"following_url", n => { Following_url = n.GetStringValue(); } }, @@ -74,6 +79,7 @@ public IDictionary> GetFieldDeserializers() { {"html_url", n => { Html_url = n.GetStringValue(); } }, {"id", n => { Id = n.GetIntValue(); } }, {"login", n => { Login = n.GetStringValue(); } }, + {"name", n => { Name = n.GetStringValue(); } }, {"node_id", n => { Node_id = n.GetStringValue(); } }, {"organizations_url", n => { Organizations_url = n.GetStringValue(); } }, {"received_events_url", n => { Received_events_url = n.GetStringValue(); } }, @@ -93,6 +99,7 @@ public IDictionary> GetFieldDeserializers() { public void Serialize(ISerializationWriter writer) { _ = writer ?? throw new ArgumentNullException(nameof(writer)); writer.WriteStringValue("avatar_url", Avatar_url); + writer.WriteStringValue("email", Email); writer.WriteStringValue("events_url", Events_url); writer.WriteStringValue("followers_url", Followers_url); writer.WriteStringValue("following_url", Following_url); @@ -101,6 +108,7 @@ public void Serialize(ISerializationWriter writer) { writer.WriteStringValue("html_url", Html_url); writer.WriteIntValue("id", Id); writer.WriteStringValue("login", Login); + writer.WriteStringValue("name", Name); writer.WriteStringValue("node_id", Node_id); writer.WriteStringValue("organizations_url", Organizations_url); writer.WriteStringValue("received_events_url", Received_events_url); diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/User/Installations/InstallationsRequestBuilder.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/User/Installations/InstallationsRequestBuilder.cs new file mode 100644 index 0000000000..655e092c1e --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/User/Installations/InstallationsRequestBuilder.cs @@ -0,0 +1,103 @@ +using Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models; +using Microsoft.Kiota.Abstractions; +using Microsoft.Kiota.Abstractions.Serialization; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.User.Installations { + /// Builds and executes requests for operations under \user\installations + public class InstallationsRequestBuilder { + /// Path parameters for the request + private Dictionary PathParameters { get; set; } + /// The request adapter to use to execute the requests. + private IRequestAdapter RequestAdapter { get; set; } + /// Url template to use to build the URL for the current request builder + private string UrlTemplate { get; set; } + /// + /// Instantiates a new InstallationsRequestBuilder and sets the default values. + /// + /// Path parameters for the request + /// The request adapter to use to execute the requests. + public InstallationsRequestBuilder(Dictionary pathParameters, IRequestAdapter requestAdapter) { + _ = pathParameters ?? throw new ArgumentNullException(nameof(pathParameters)); + _ = requestAdapter ?? throw new ArgumentNullException(nameof(requestAdapter)); + UrlTemplate = "{+baseurl}/user/installations{?per_page*,page*}"; + var urlTplParams = new Dictionary(pathParameters); + PathParameters = urlTplParams; + RequestAdapter = requestAdapter; + } + /// + /// Instantiates a new InstallationsRequestBuilder and sets the default values. + /// + /// The raw URL to use for the request builder. + /// The request adapter to use to execute the requests. + public InstallationsRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) { + if(string.IsNullOrEmpty(rawUrl)) throw new ArgumentNullException(nameof(rawUrl)); + _ = requestAdapter ?? throw new ArgumentNullException(nameof(requestAdapter)); + UrlTemplate = "{+baseurl}/user/installations{?per_page*,page*}"; + var urlTplParams = new Dictionary(); + urlTplParams.Add("request-raw-url", rawUrl); + PathParameters = urlTplParams; + RequestAdapter = requestAdapter; + } + /// + /// Lists installations of your GitHub App that the authenticated user has explicit permission (`:read`, `:write`, or `:admin`) to access.You must use a [user-to-server OAuth access token](https://docs.github.com/apps/building-github-apps/identifying-and-authorizing-users-for-github-apps/#identifying-users-on-your-site), created for a user who has authorized your GitHub App, to access this endpoint.The authenticated user has explicit permission to access repositories they own, repositories where they are a collaborator, and repositories that they can access through an organization membership.You can find the permissions for the installation under the `permissions` key. + /// + /// Configuration for the request such as headers, query parameters, and middleware options. + public RequestInformation CreateGetRequestInformation(Action requestConfiguration = default) { + var requestInfo = new RequestInformation { + HttpMethod = Method.GET, + UrlTemplate = UrlTemplate, + PathParameters = PathParameters, + }; + requestInfo.Headers.Add("Accept", "application/json"); + if (requestConfiguration != null) { + var requestConfig = new InstallationsRequestBuilderGetRequestConfiguration(); + requestConfiguration.Invoke(requestConfig); + requestInfo.AddQueryParameters(requestConfig.QueryParameters); + requestInfo.AddRequestOptions(requestConfig.Options); + requestInfo.AddHeaders(requestConfig.Headers); + } + return requestInfo; + } + /// + /// Lists installations of your GitHub App that the authenticated user has explicit permission (`:read`, `:write`, or `:admin`) to access.You must use a [user-to-server OAuth access token](https://docs.github.com/apps/building-github-apps/identifying-and-authorizing-users-for-github-apps/#identifying-users-on-your-site), created for a user who has authorized your GitHub App, to access this endpoint.The authenticated user has explicit permission to access repositories they own, repositories where they are a collaborator, and repositories that they can access through an organization membership.You can find the permissions for the installation under the `permissions` key. + /// + /// Cancellation token to use when cancelling requests + /// Configuration for the request such as headers, query parameters, and middleware options. + public async Task GetAsync(Action requestConfiguration = default, CancellationToken cancellationToken = default) { + var requestInfo = CreateGetRequestInformation(requestConfiguration); + var errorMapping = new Dictionary> { + {"401", BasicError.CreateFromDiscriminatorValue}, + {"403", BasicError.CreateFromDiscriminatorValue}, + }; + return await RequestAdapter.SendAsync(requestInfo, InstallationsResponse.CreateFromDiscriminatorValue, errorMapping, cancellationToken); + } + /// Lists installations of your GitHub App that the authenticated user has explicit permission (`:read`, `:write`, or `:admin`) to access.You must use a [user-to-server OAuth access token](https://docs.github.com/apps/building-github-apps/identifying-and-authorizing-users-for-github-apps/#identifying-users-on-your-site), created for a user who has authorized your GitHub App, to access this endpoint.The authenticated user has explicit permission to access repositories they own, repositories where they are a collaborator, and repositories that they can access through an organization membership.You can find the permissions for the installation under the `permissions` key. + public class InstallationsRequestBuilderGetQueryParameters { + /// Page number of the results to fetch. + public int? Page { get; set; } + /// The number of results per page (max 100). + public int? Per_page { get; set; } + } + /// Configuration for the request such as headers, query parameters, and middleware options. + public class InstallationsRequestBuilderGetRequestConfiguration { + /// Request headers + public IDictionary Headers { get; set; } + /// Request options + public IList Options { get; set; } + /// Request query parameters + public InstallationsRequestBuilderGetQueryParameters QueryParameters { get; set; } = new InstallationsRequestBuilderGetQueryParameters(); + /// + /// Instantiates a new installationsRequestBuilderGetRequestConfiguration and sets the default values. + /// + public InstallationsRequestBuilderGetRequestConfiguration() { + Options = new List(); + Headers = new Dictionary(); + } + } + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/User/Installations/InstallationsResponse.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/User/Installations/InstallationsResponse.cs new file mode 100644 index 0000000000..4e188caa61 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/User/Installations/InstallationsResponse.cs @@ -0,0 +1,49 @@ +using Kiota.Builder.SearchProviders.GitHub.GitHubClient.Models; +using Microsoft.Kiota.Abstractions.Serialization; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.User.Installations { + public class InstallationsResponse : IAdditionalDataHolder, IParsable { + /// 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 installations property + public List Installations { get; set; } + /// The total_count property + public int? Total_count { get; set; } + /// + /// Instantiates a new installationsResponse and sets the default values. + /// + public InstallationsResponse() { + AdditionalData = new Dictionary(); + } + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// The parse node to use to read the discriminator value and create the object + public static InstallationsResponse CreateFromDiscriminatorValue(IParseNode parseNode) { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new InstallationsResponse(); + } + /// + /// The deserialization information for the current model + /// + public IDictionary> GetFieldDeserializers() { + return new Dictionary> { + {"installations", n => { Installations = n.GetCollectionOfObjectValues(Installation.CreateFromDiscriminatorValue)?.ToList(); } }, + {"total_count", n => { Total_count = n.GetIntValue(); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public void Serialize(ISerializationWriter writer) { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteCollectionOfObjectValues("installations", Installations); + writer.WriteIntValue("total_count", Total_count); + writer.WriteAdditionalData(AdditionalData); + } + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/User/UserRequestBuilder.cs b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/User/UserRequestBuilder.cs new file mode 100644 index 0000000000..1d9d886753 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/User/UserRequestBuilder.cs @@ -0,0 +1,49 @@ +using Kiota.Builder.SearchProviders.GitHub.GitHubClient.User.Installations; +using Microsoft.Kiota.Abstractions; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +namespace Kiota.Builder.SearchProviders.GitHub.GitHubClient.User { + /// Builds and executes requests for operations under \user + public class UserRequestBuilder { + /// The installations property + public InstallationsRequestBuilder Installations { get => + new InstallationsRequestBuilder(PathParameters, RequestAdapter); + } + /// Path parameters for the request + private Dictionary PathParameters { get; set; } + /// The request adapter to use to execute the requests. + private IRequestAdapter RequestAdapter { get; set; } + /// Url template to use to build the URL for the current request builder + private string UrlTemplate { get; set; } + /// + /// Instantiates a new UserRequestBuilder and sets the default values. + /// + /// Path parameters for the request + /// The request adapter to use to execute the requests. + public UserRequestBuilder(Dictionary pathParameters, IRequestAdapter requestAdapter) { + _ = pathParameters ?? throw new ArgumentNullException(nameof(pathParameters)); + _ = requestAdapter ?? throw new ArgumentNullException(nameof(requestAdapter)); + UrlTemplate = "{+baseurl}/user"; + var urlTplParams = new Dictionary(pathParameters); + PathParameters = urlTplParams; + RequestAdapter = requestAdapter; + } + /// + /// Instantiates a new UserRequestBuilder and sets the default values. + /// + /// The raw URL to use for the request builder. + /// The request adapter to use to execute the requests. + public UserRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) { + if(string.IsNullOrEmpty(rawUrl)) throw new ArgumentNullException(nameof(rawUrl)); + _ = requestAdapter ?? throw new ArgumentNullException(nameof(requestAdapter)); + UrlTemplate = "{+baseurl}/user"; + var urlTplParams = new Dictionary(); + urlTplParams.Add("request-raw-url", rawUrl); + PathParameters = urlTplParams; + RequestAdapter = requestAdapter; + } + } +} diff --git a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/kiota-lock.json b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/kiota-lock.json index 606b0968c1..d46c609fdb 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/kiota-lock.json +++ b/src/Kiota.Builder/SearchProviders/GitHub/GitHubClient/kiota-lock.json @@ -1,5 +1,5 @@ { - "descriptionHash": "A35A2B9B2E534DCD92AE698809F7BC4F2C009B4564B238084E2A76019A7323A4356E5E51A99EF143447E54927D58C0E519018A4CF7FB2E4A20600EB4B263F67C", + "descriptionHash": "53CAD9C39EC10C7EA1F10E55050BF89754C56DEF3B9F7A8E5CDF90F860A750E8BD3FA501258A955BAB09382DCA43CA77A13C7024FD555680BA6404E95684E847", "descriptionLocation": "https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.2022-11-28.json", "lockFileVersion": "1.0.0", "kiotaVersion": "0.7.1.0", @@ -25,7 +25,8 @@ ], "includePatterns": [ "**/search/repositories", - "**/repos/**/contents/**" + "**/repos/**/contents/**", + "/user/installations" ], "excludePatterns": [ "**/migrations/**", From a48a094d383a6a9ebf11f7b1035b8203b0471b4c Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Wed, 30 Nov 2022 10:57:58 -0500 Subject: [PATCH 37/42] - switches to modern github app for device code Signed-off-by: Vincent Biret --- .../Configuration/SearchConfiguration.cs | 3 ++- src/Kiota.Web/wwwroot/appsettings.json | 3 ++- src/kiota/Handlers/BaseKiotaCommandHandler.cs | 3 +++ .../KiotaGitHubDeviceLoginCommandHanlder.cs | 17 +++++++++++++++++ src/kiota/appsettings.json | 3 ++- 5 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/Kiota.Builder/Configuration/SearchConfiguration.cs b/src/Kiota.Builder/Configuration/SearchConfiguration.cs index f7576e0e0b..25a2201164 100644 --- a/src/Kiota.Builder/Configuration/SearchConfiguration.cs +++ b/src/Kiota.Builder/Configuration/SearchConfiguration.cs @@ -8,7 +8,8 @@ public class SearchConfiguration : SearchConfigurationBase { } public class GitHubConfiguration { - public string AppId { get; set; } = "0adc165b71f9824f4282"; + public string AppId { get; set; } = "Iv1.9ed2bcb878c90617"; public Uri ApiBaseUrl { get; set; } = new ("https://api.github.com"); public Uri BlockListUrl { get; set; } = new ("https://raw.githubusercontent.com/microsoft/kiota/main/resources/index-block-list.yml"); + public Uri AppManagement { get; set; } = new("https://aka.ms/kiota/install/github"); } diff --git a/src/Kiota.Web/wwwroot/appsettings.json b/src/Kiota.Web/wwwroot/appsettings.json index 7bf609a026..d3d87e3aa0 100644 --- a/src/Kiota.Web/wwwroot/appsettings.json +++ b/src/Kiota.Web/wwwroot/appsettings.json @@ -9,7 +9,8 @@ "APIsGuruListUrl": "https://raw.githubusercontent.com/APIs-guru/openapi-directory/gh-pages/v2/list.json", "GitHub": { "BlockListUrl": "https://raw.githubusercontent.com/microsoft/kiota/main/resources/index-block-list.yml", - "AppId": "0adc165b71f9824f4282" + "AppId": "Iv1.9ed2bcb878c90617", + "AppManagement": "https://aka.ms/kiota/install/github" } }, "Logging": { diff --git a/src/kiota/Handlers/BaseKiotaCommandHandler.cs b/src/kiota/Handlers/BaseKiotaCommandHandler.cs index fc0c70ffb0..ea52873403 100644 --- a/src/kiota/Handlers/BaseKiotaCommandHandler.cs +++ b/src/kiota/Handlers/BaseKiotaCommandHandler.cs @@ -210,6 +210,9 @@ protected void DisplayGitHubLogoutHint() { DisplayHint("Hint: use the logout command to sign out of GitHub.", "Example: kiota logout github"); } + protected void DisplayManageInstallationHint() { + DisplayHint($"Hint: go to {Configuration.Search.GitHub.AppManagement} to manage your which organizations and repositories Kiota has access to."); + } protected void DisplaySearchBasicHint() { DisplayHint("Hint: use the search command to search for an OpenAPI description.", "Example: kiota search "); diff --git a/src/kiota/Handlers/KiotaGitHubDeviceLoginCommandHanlder.cs b/src/kiota/Handlers/KiotaGitHubDeviceLoginCommandHanlder.cs index 514c6f0205..7dc9778326 100644 --- a/src/kiota/Handlers/KiotaGitHubDeviceLoginCommandHanlder.cs +++ b/src/kiota/Handlers/KiotaGitHubDeviceLoginCommandHanlder.cs @@ -4,8 +4,11 @@ using System.Threading; using System.Threading.Tasks; using kiota.Authentication.GitHub.DeviceCode; +using Kiota.Builder.SearchProviders.GitHub.GitHubClient; using Microsoft.Extensions.Logging; using Microsoft.Kiota.Abstractions; +using Microsoft.Kiota.Abstractions.Authentication; +using Microsoft.Kiota.Http.HttpClientLibrary; namespace kiota.Handlers; @@ -44,6 +47,8 @@ private async Task LoginAsync(ILogger logger, CancellationToken cancellatio await authenticationProvider.AuthenticateRequestAsync(dummyRequest, cancellationToken: cancellationToken); if(dummyRequest.Headers.TryGetValue("Authorization", out var authHeaderValue) && authHeaderValue is string authHeader && authHeader.StartsWith("bearer", StringComparison.OrdinalIgnoreCase)) { DisplaySuccess("Authentication successful."); + await ListOutRepositoriesAsync(authenticationProvider, cancellationToken); + DisplayManageInstallationHint(); DisplaySearchBasicHint(); DisplayGitHubLogoutHint(); return 0; @@ -52,4 +57,16 @@ private async Task LoginAsync(ILogger logger, CancellationToken cancellatio return 1; } } + private static async Task ListOutRepositoriesAsync(IAuthenticationProvider authProvider, CancellationToken cancellationToken) { + var requestAdapter = new HttpClientRequestAdapter(authProvider, httpClient: httpClient); + var client = new GitHubClient(requestAdapter); + var installations = await client.User.Installations.GetAsync(cancellationToken: cancellationToken); + if (installations?.Total_count > 0) { + DisplayInfo("Kiota is installed to the following organizations/accounts:"); + foreach (var installation in installations.Installations) + DisplayInfo($"- {installation.Account.SimpleUser.Login} ({installation.Repository_selection} repositories){(installation.Suspended_at != null ? " (suspended)" : string.Empty)}"); + } else { + DisplayWarning("Kiota is not installed to any GitHub organization/account."); + } + } } diff --git a/src/kiota/appsettings.json b/src/kiota/appsettings.json index 7bf609a026..d3d87e3aa0 100644 --- a/src/kiota/appsettings.json +++ b/src/kiota/appsettings.json @@ -9,7 +9,8 @@ "APIsGuruListUrl": "https://raw.githubusercontent.com/APIs-guru/openapi-directory/gh-pages/v2/list.json", "GitHub": { "BlockListUrl": "https://raw.githubusercontent.com/microsoft/kiota/main/resources/index-block-list.yml", - "AppId": "0adc165b71f9824f4282" + "AppId": "Iv1.9ed2bcb878c90617", + "AppManagement": "https://aka.ms/kiota/install/github" } }, "Logging": { From add69603e0a0d3d9af07cb50832c911b2f047146 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Wed, 30 Nov 2022 12:42:19 -0500 Subject: [PATCH 38/42] - adds a changelog entry for sign in commands Signed-off-by: Vincent Biret --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b447a1d25..0435099eb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added support for GitHub based API search.[#1866](https://github.com/microsoft/kiota/issues/1866) +- Added login/logout commands to access API descriptions in private GitHub repositories. [#1983](https://github.com/microsoft/kiota/issues/1983) - Added support for scalar request bodies Python [#1571](https://github.com/microsoft/kiota/issues/1571) - Sets property defaults in constructor and removes duplicate properties defined in base types from model serialization and deserialization methods in Python. [#1726](https://github.com/microsoft/kiota/issues/1726) - Added support for scalar request bodies in PHP [#1937](https://github.com/microsoft/kiota/pull/1937) From 58a82e88e8721196926ce2230c8977cd8923a160 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Thu, 1 Dec 2022 08:07:42 -0500 Subject: [PATCH 39/42] Apply suggestions from code review Co-authored-by: Eastman --- .../GitHub/Authentication/PatAuthenticationProvider.cs | 2 +- .../GitHub/Authentication/TempFolderStorageService.cs | 2 +- .../Authentication/GitHub/BrowserAccessTokenProvider.cs | 3 +-- src/kiota/Handlers/BaseKiotaCommandHandler.cs | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Authentication/PatAuthenticationProvider.cs b/src/Kiota.Builder/SearchProviders/GitHub/Authentication/PatAuthenticationProvider.cs index 6a4ef27cdb..d62dc184f4 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/Authentication/PatAuthenticationProvider.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/Authentication/PatAuthenticationProvider.cs @@ -7,7 +7,7 @@ namespace Kiota.Builder.SearchProviders.GitHub.Authentication; public class PatAuthenticationProvider : BaseAuthenticationProvider { public PatAuthenticationProvider(string clientId, string scope, IEnumerable validHosts, ILogger logger, ITokenStorageService StorageService) : - base(clientId, scope, validHosts, logger, (clientId, scope, validHosts) => new PatAccessTokenProvider { + base(clientId, scope, validHosts, logger, (_, _, validHosts) => new PatAccessTokenProvider { StorageService = StorageService, AllowedHostsValidator = new (validHosts), }, false) diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Authentication/TempFolderStorageService.cs b/src/Kiota.Builder/SearchProviders/GitHub/Authentication/TempFolderStorageService.cs index 21b2617879..8b98190922 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/Authentication/TempFolderStorageService.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/Authentication/TempFolderStorageService.cs @@ -47,7 +47,7 @@ public async Task IsTokenPresentAsync(CancellationToken cancellationToken) if (!File.Exists(target)) return false; var fileDate = File.GetLastWriteTime(target); - if(fileDate > DateTime.Now.AddMonths(6)) { + if(fileDate > DateTime.Now.AddMonths(-6)) { await DeleteTokenAsync(cancellationToken); return false; } diff --git a/src/Kiota.Web/Authentication/GitHub/BrowserAccessTokenProvider.cs b/src/Kiota.Web/Authentication/GitHub/BrowserAccessTokenProvider.cs index 6c1eca1c9e..30cc596e9a 100644 --- a/src/Kiota.Web/Authentication/GitHub/BrowserAccessTokenProvider.cs +++ b/src/Kiota.Web/Authentication/GitHub/BrowserAccessTokenProvider.cs @@ -51,8 +51,7 @@ private Uri GetAuthorizeUrl(Guid state) { tokenRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); using var tokenResponse = await HttpClient.SendAsync(tokenRequest, cancellationToken); tokenResponse.EnsureSuccessStatusCode(); - var tokenContent = await tokenResponse.Content.ReadAsStringAsync(cancellationToken); - var result = JsonSerializer.Deserialize(tokenContent); + var result = await tokenResponse.Content.ReadFromJsonAsync(cancellationToken:cancellationToken); if ("authorization_pending".Equals(result?.Error, StringComparison.OrdinalIgnoreCase)) return null; else if (!string.IsNullOrEmpty(result?.Error)) diff --git a/src/kiota/Handlers/BaseKiotaCommandHandler.cs b/src/kiota/Handlers/BaseKiotaCommandHandler.cs index ea52873403..35bf53d080 100644 --- a/src/kiota/Handlers/BaseKiotaCommandHandler.cs +++ b/src/kiota/Handlers/BaseKiotaCommandHandler.cs @@ -63,7 +63,7 @@ private IAuthenticationProvider GetGitHubPatAuthenticationProvider(ILogger logge new List { Configuration.Search.GitHub.ApiBaseUrl.Host }, logger, GetGitHubPatStorageService(logger)); - protected async Task GetKiotaSearcher(ILoggerFactory loggerFactory, CancellationToken cancellationToken) { + protected async Task GetKiotaSearcherAsync(ILoggerFactory loggerFactory, CancellationToken cancellationToken) { var logger = loggerFactory.CreateLogger(); var deviceCodeSignInCallback = GetIsGitHubDeviceSignedInCallback(logger); var patSignInCallBack = GetIsGitHubPatSignedInCallback(logger); From 3c8d25ef6fef8593d245b66ce348fc1f2b1a31c7 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Thu, 1 Dec 2022 08:13:20 -0500 Subject: [PATCH 40/42] - applies missing recommendations from PR Signed-off-by: Vincent Biret --- .../Authentication/GitHub/BrowserAccessTokenProvider.cs | 2 +- .../GitHub/DeviceCode/AcessTokenProvider.cs | 8 +++----- src/kiota/Handlers/KiotaDownloadCommandHandler.cs | 2 +- src/kiota/Handlers/KiotaSeachBasedCommandHandler.cs | 2 +- src/kiota/Handlers/KiotaSearchCommandHandler.cs | 2 +- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/Kiota.Web/Authentication/GitHub/BrowserAccessTokenProvider.cs b/src/Kiota.Web/Authentication/GitHub/BrowserAccessTokenProvider.cs index 30cc596e9a..a7bf34518f 100644 --- a/src/Kiota.Web/Authentication/GitHub/BrowserAccessTokenProvider.cs +++ b/src/Kiota.Web/Authentication/GitHub/BrowserAccessTokenProvider.cs @@ -1,5 +1,5 @@ using System.Net.Http.Headers; -using System.Text.Json; +using System.Net.Http.Json; using Kiota.Builder.SearchProviders.GitHub.Authentication; using Microsoft.Kiota.Abstractions.Authentication; diff --git a/src/kiota/Authentication/GitHub/DeviceCode/AcessTokenProvider.cs b/src/kiota/Authentication/GitHub/DeviceCode/AcessTokenProvider.cs index 1dbd18aaef..259eebaf25 100644 --- a/src/kiota/Authentication/GitHub/DeviceCode/AcessTokenProvider.cs +++ b/src/kiota/Authentication/GitHub/DeviceCode/AcessTokenProvider.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Net.Http; using System.Net.Http.Headers; -using System.Text.Json; +using System.Net.Http.Json; using System.Threading; using System.Threading.Tasks; using Kiota.Builder.SearchProviders.GitHub.Authentication; @@ -66,8 +66,7 @@ private async Task GetAuthorizationTokenInternalAsync(CancellationToken tokenRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); using var tokenResponse = await HttpClient.SendAsync(tokenRequest, cancellationToken); tokenResponse.EnsureSuccessStatusCode(); - var tokenContent = await tokenResponse.Content.ReadAsStringAsync(cancellationToken); - var result = JsonSerializer.Deserialize(tokenContent); + var result = await tokenResponse.Content.ReadFromJsonAsync(cancellationToken: cancellationToken); if ("authorization_pending".Equals(result?.Error, StringComparison.OrdinalIgnoreCase)) return null; else if (!string.IsNullOrEmpty(result?.Error)) @@ -86,7 +85,6 @@ private async Task GetAuthorizationTokenInternalAsync(CancellationToken codeRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); using var codeResponse = await HttpClient.SendAsync(codeRequest, cancellationToken); codeResponse.EnsureSuccessStatusCode(); - var codeResponseContent = await codeResponse.Content.ReadAsStringAsync(cancellationToken); - return JsonSerializer.Deserialize(codeResponseContent); + return await codeResponse.Content.ReadFromJsonAsync(cancellationToken: cancellationToken); } } diff --git a/src/kiota/Handlers/KiotaDownloadCommandHandler.cs b/src/kiota/Handlers/KiotaDownloadCommandHandler.cs index e0cac221bc..00226dfd4a 100644 --- a/src/kiota/Handlers/KiotaDownloadCommandHandler.cs +++ b/src/kiota/Handlers/KiotaDownloadCommandHandler.cs @@ -42,7 +42,7 @@ public override async Task InvokeAsync(InvocationContext context) logger.LogTrace("configuration: {configuration}", JsonSerializer.Serialize(Configuration)); try { - var searcher = await GetKiotaSearcher(loggerFactory, cancellationToken).ConfigureAwait(false); + var searcher = await GetKiotaSearcherAsync(loggerFactory, cancellationToken).ConfigureAwait(false); var results = await searcher.SearchAsync(searchTerm, version, cancellationToken).ConfigureAwait(false); return await SaveResultsAsync(searchTerm, version, results, logger, cancellationToken); } catch (Exception ex) { diff --git a/src/kiota/Handlers/KiotaSeachBasedCommandHandler.cs b/src/kiota/Handlers/KiotaSeachBasedCommandHandler.cs index 0f60e1b362..d1716a64ef 100644 --- a/src/kiota/Handlers/KiotaSeachBasedCommandHandler.cs +++ b/src/kiota/Handlers/KiotaSeachBasedCommandHandler.cs @@ -12,7 +12,7 @@ internal abstract class KiotaSearchBasedCommandHandler : BaseKiotaCommandHandler if (string.IsNullOrEmpty(openapi) && !string.IsNullOrEmpty(searchTerm)) { logger.LogInformation("Searching for {searchTerm} in the OpenAPI description repository", searchTerm); - var searcher = await GetKiotaSearcher(loggerFactory, cancellationToken).ConfigureAwait(false); + var searcher = await GetKiotaSearcherAsync(loggerFactory, cancellationToken).ConfigureAwait(false); var results = await searcher.SearchAsync(searchTerm, version, cancellationToken).ConfigureAwait(false); if (results.Count == 1) return (results.First().Value.DescriptionUrl.ToString(), null); diff --git a/src/kiota/Handlers/KiotaSearchCommandHandler.cs b/src/kiota/Handlers/KiotaSearchCommandHandler.cs index 74a8a0a885..27efb98092 100644 --- a/src/kiota/Handlers/KiotaSearchCommandHandler.cs +++ b/src/kiota/Handlers/KiotaSearchCommandHandler.cs @@ -35,7 +35,7 @@ public override async Task InvokeAsync(InvocationContext context) logger.LogTrace("configuration: {configuration}", JsonSerializer.Serialize(Configuration)); try { - var searcher = await GetKiotaSearcher(loggerFactory, cancellationToken).ConfigureAwait(false); + var searcher = await GetKiotaSearcherAsync(loggerFactory, cancellationToken).ConfigureAwait(false); var results = await searcher.SearchAsync(searchTerm, version, cancellationToken); await DisplayResults(searchTerm, version, results, logger, cancellationToken); return 0; From 7a994dbefed0f6a3eb926834f08262fcc52bf532 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Thu, 1 Dec 2022 08:15:40 -0500 Subject: [PATCH 41/42] - fixes typo in file name Signed-off-by: Vincent Biret --- .../DeviceCode/{AcessTokenProvider.cs => AccessTokenProvider.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/kiota/Authentication/GitHub/DeviceCode/{AcessTokenProvider.cs => AccessTokenProvider.cs} (100%) diff --git a/src/kiota/Authentication/GitHub/DeviceCode/AcessTokenProvider.cs b/src/kiota/Authentication/GitHub/DeviceCode/AccessTokenProvider.cs similarity index 100% rename from src/kiota/Authentication/GitHub/DeviceCode/AcessTokenProvider.cs rename to src/kiota/Authentication/GitHub/DeviceCode/AccessTokenProvider.cs From 3a929a386268d38b785f42171d9d9c5f8def5fa6 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Thu, 1 Dec 2022 08:24:12 -0500 Subject: [PATCH 42/42] - fixes stack overflow with token cache service Signed-off-by: Vincent Biret --- .../GitHub/Authentication/TempFolderStorageService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Kiota.Builder/SearchProviders/GitHub/Authentication/TempFolderStorageService.cs b/src/Kiota.Builder/SearchProviders/GitHub/Authentication/TempFolderStorageService.cs index 8b98190922..5b6945eb16 100644 --- a/src/Kiota.Builder/SearchProviders/GitHub/Authentication/TempFolderStorageService.cs +++ b/src/Kiota.Builder/SearchProviders/GitHub/Authentication/TempFolderStorageService.cs @@ -47,7 +47,7 @@ public async Task IsTokenPresentAsync(CancellationToken cancellationToken) if (!File.Exists(target)) return false; var fileDate = File.GetLastWriteTime(target); - if(fileDate > DateTime.Now.AddMonths(-6)) { + if(fileDate.AddMonths(6) < DateTime.Now) { await DeleteTokenAsync(cancellationToken); return false; }