From 8aa4bbdc69b71d4b47d93750924f8a0cc69a52be Mon Sep 17 00:00:00 2001 From: Andrea Peruffo Date: Thu, 2 Nov 2023 18:33:26 +0000 Subject: [PATCH 1/6] [WIP] Kiota Search Apicurio integration --- .../Configuration/SearchConfiguration.cs | 16 +++ src/Kiota.Builder/KiotaSearcher.cs | 10 +- .../Apicurio/ApicurioClient/ApicurioClient.cs | 37 +++++ .../Models/ArtifactSearchResults.cs | 58 ++++++++ .../ApicurioClient/Models/ArtifactState.cs | 14 ++ .../Apicurio/ApicurioClient/Models/Error.cs | 79 ++++++++++ .../Models/RuleViolationCause.cs | 61 ++++++++ .../Models/RuleViolationError.cs | 46 ++++++ .../ApicurioClient/Models/SearchedArtifact.cs | 136 ++++++++++++++++++ .../Apicurio/ApicurioClient/Models/SortBy.cs | 11 ++ .../ApicurioClient/Models/SortOrder.cs | 11 ++ .../Search/SearchRequestBuilder.cs | 33 +++++ .../Apicurio/ApicurioClient/kiota-lock.json | 34 +++++ .../Apicurio/ApicurioSearchProvider.cs | 91 ++++++++++++ src/kiota/Handlers/BaseKiotaCommandHandler.cs | 7 +- src/kiota/Rpc/Server.cs | 2 +- 16 files changed, 642 insertions(+), 4 deletions(-) create mode 100644 src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/ApicurioClient.cs create mode 100644 src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/ArtifactSearchResults.cs create mode 100644 src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/ArtifactState.cs create mode 100644 src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/Error.cs create mode 100644 src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/RuleViolationCause.cs create mode 100644 src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/RuleViolationError.cs create mode 100644 src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/SearchedArtifact.cs create mode 100644 src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/SortBy.cs create mode 100644 src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/SortOrder.cs create mode 100644 src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Search/SearchRequestBuilder.cs create mode 100644 src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/kiota-lock.json create mode 100644 src/Kiota.Builder/SearchProviders/Apicurio/ApicurioSearchProvider.cs diff --git a/src/Kiota.Builder/Configuration/SearchConfiguration.cs b/src/Kiota.Builder/Configuration/SearchConfiguration.cs index 8663c5b2dc..9181781314 100644 --- a/src/Kiota.Builder/Configuration/SearchConfiguration.cs +++ b/src/Kiota.Builder/Configuration/SearchConfiguration.cs @@ -7,12 +7,15 @@ public class SearchConfiguration : SearchConfigurationBase, ICloneable public Uri APIsGuruListUrl { get; set; } = new("https://raw.githubusercontent.com/APIs-guru/openapi-directory/gh-pages/v2/list.json"); public GitHubConfiguration GitHub { get; set; } = new(); + public ApicurioConfiguration Apicurio { get; set; } = new(); + public object Clone() { return new SearchConfiguration { APIsGuruListUrl = new(APIsGuruListUrl.ToString(), UriKind.RelativeOrAbsolute), GitHub = (GitHubConfiguration)GitHub.Clone(), + Apicurio = (ApicurioConfiguration)Apicurio.Clone(), ClearCache = ClearCache }; } @@ -36,3 +39,16 @@ public object Clone() }; } } + +public class ApicurioConfiguration : ICloneable +{ + public Uri ApiBaseUrl { get; set; } = new("http://localhost:8080/apis/registry/v2"); + + public object Clone() + { + return new ApicurioConfiguration + { + ApiBaseUrl = new(ApiBaseUrl.ToString(), UriKind.RelativeOrAbsolute), + }; + } +} diff --git a/src/Kiota.Builder/KiotaSearcher.cs b/src/Kiota.Builder/KiotaSearcher.cs index 04d54bf3f0..3ebc093993 100644 --- a/src/Kiota.Builder/KiotaSearcher.cs +++ b/src/Kiota.Builder/KiotaSearcher.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Kiota.Builder.Configuration; using Kiota.Builder.SearchProviders; +using Kiota.Builder.SearchProviders.Apicurio; using Kiota.Builder.SearchProviders.APIsGuru; using Kiota.Builder.SearchProviders.GitHub; using Kiota.Builder.SearchProviders.MSGraph; @@ -20,9 +21,11 @@ public class KiotaSearcher private readonly SearchConfiguration _config; private readonly HttpClient _httpClient; private readonly IAuthenticationProvider? _gitHubAuthenticationProvider; + + private readonly IAuthenticationProvider? _apicurioAuthenticationProvider; private readonly Func> _isGitHubSignedInCallBack; - public KiotaSearcher(ILogger logger, SearchConfiguration config, HttpClient httpClient, IAuthenticationProvider? gitHubAuthenticationProvider, Func> isGitHubSignedInCallBack) + public KiotaSearcher(ILogger logger, SearchConfiguration config, HttpClient httpClient, IAuthenticationProvider? gitHubAuthenticationProvider, Func> isGitHubSignedInCallBack, IAuthenticationProvider? apicurioAuthenticationProvider) { ArgumentNullException.ThrowIfNull(logger); ArgumentNullException.ThrowIfNull(config); @@ -32,6 +35,7 @@ public KiotaSearcher(ILogger logger, SearchConfiguration config, _httpClient = httpClient; _gitHubAuthenticationProvider = gitHubAuthenticationProvider; _isGitHubSignedInCallBack = isGitHubSignedInCallBack; + _apicurioAuthenticationProvider = apicurioAuthenticationProvider; } public async Task> SearchAsync(string? searchTerm, string? version, CancellationToken cancellationToken) { @@ -45,10 +49,12 @@ public async Task> SearchAsync(string? searchT _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 apicurioProvider = new ApicurioSearchProvider(_httpClient, _logger, _config.ClearCache, _config.Apicurio, _apicurioAuthenticationProvider); var results = await Task.WhenAll( SearchProviderAsync(searchTerm, version, apiGurusSearchProvider, cancellationToken), SearchProviderAsync(searchTerm, version, oasProvider, cancellationToken), - SearchProviderAsync(searchTerm, version, githubProvider, cancellationToken)).ConfigureAwait(false); + SearchProviderAsync(searchTerm, version, githubProvider, cancellationToken), + SearchProviderAsync(searchTerm, version, apicurioProvider, cancellationToken)).ConfigureAwait(false); return results.SelectMany(static x => x) .ToDictionary(static x => x.Key, static x => x.Value, StringComparer.OrdinalIgnoreCase); } diff --git a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/ApicurioClient.cs b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/ApicurioClient.cs new file mode 100644 index 0000000000..a84ab5e5b8 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/ApicurioClient.cs @@ -0,0 +1,37 @@ +// +using ApiSdk.Search; +using Microsoft.Kiota.Abstractions.Extensions; +using Microsoft.Kiota.Abstractions; +using Microsoft.Kiota.Serialization.Form; +using Microsoft.Kiota.Serialization.Json; +using Microsoft.Kiota.Serialization.Multipart; +using Microsoft.Kiota.Serialization.Text; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using System; +namespace ApiSdk { + /// + /// The main entry point of the SDK, exposes the configuration and the fluent API. + /// + public class ApicurioClient : BaseRequestBuilder { + /// The search property + public SearchRequestBuilder Search { get => + new SearchRequestBuilder(PathParameters, RequestAdapter); + } + /// + /// Instantiates a new ApicurioClient and sets the default values. + /// + /// The request adapter to use to execute the requests. + public ApicurioClient(IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}", new Dictionary()) { + ApiClientBuilder.RegisterDefaultSerializer(); + ApiClientBuilder.RegisterDefaultSerializer(); + ApiClientBuilder.RegisterDefaultSerializer(); + ApiClientBuilder.RegisterDefaultSerializer(); + ApiClientBuilder.RegisterDefaultDeserializer(); + ApiClientBuilder.RegisterDefaultDeserializer(); + ApiClientBuilder.RegisterDefaultDeserializer(); + } + } +} diff --git a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/ArtifactSearchResults.cs b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/ArtifactSearchResults.cs new file mode 100644 index 0000000000..dd49173d7f --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/ArtifactSearchResults.cs @@ -0,0 +1,58 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace ApiSdk.Models { + /// + /// Describes the response received when searching for artifacts. + /// + public class ArtifactSearchResults : 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 artifacts returned in the result set. +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public List? Artifacts { get; set; } +#nullable restore +#else + public List Artifacts { get; set; } +#endif + /// The total number of artifacts that matched the query that produced the result set (may be more than the number of artifacts in the result set). + public int? Count { get; set; } + /// + /// Instantiates a new ArtifactSearchResults and sets the default values. + /// + public ArtifactSearchResults() { + 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 ArtifactSearchResults CreateFromDiscriminatorValue(IParseNode parseNode) { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new ArtifactSearchResults(); + } + /// + /// The deserialization information for the current model + /// + public virtual IDictionary> GetFieldDeserializers() { + return new Dictionary> { + {"artifacts", n => { Artifacts = n.GetCollectionOfObjectValues(SearchedArtifact.CreateFromDiscriminatorValue)?.ToList(); } }, + {"count", n => { Count = n.GetIntValue(); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteCollectionOfObjectValues("artifacts", Artifacts); + writer.WriteIntValue("count", Count); + writer.WriteAdditionalData(AdditionalData); + } + } +} diff --git a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/ArtifactState.cs b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/ArtifactState.cs new file mode 100644 index 0000000000..d6bb9e9c76 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/ArtifactState.cs @@ -0,0 +1,14 @@ +// +using System.Runtime.Serialization; +using System; +namespace ApiSdk.Models { + /// Describes the state of an artifact or artifact version. The following statesare possible:* ENABLED* DISABLED* DEPRECATED + public enum ArtifactState { + [EnumMember(Value = "ENABLED")] + ENABLED, + [EnumMember(Value = "DISABLED")] + DISABLED, + [EnumMember(Value = "DEPRECATED")] + DEPRECATED, + } +} diff --git a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/Error.cs b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/Error.cs new file mode 100644 index 0000000000..30e5155b3c --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/Error.cs @@ -0,0 +1,79 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using Microsoft.Kiota.Abstractions; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace ApiSdk.Models { + /// + /// All error responses, whether `4xx` or `5xx` will include one of these as the responsebody. + /// + public class Error : ApiException, 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; } + /// Full details about the error. This might contain a server stack trace, for example. +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Detail { get; set; } +#nullable restore +#else + public string Detail { get; set; } +#endif + /// The server-side error code. + public int? ErrorCode { get; set; } + /// The short error message. +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? MessageEscaped { get; set; } +#nullable restore +#else + public string MessageEscaped { get; set; } +#endif + /// The error name - typically the classname of the exception thrown by the server. +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Name { get; set; } +#nullable restore +#else + public string Name { get; set; } +#endif + /// + /// Instantiates a new Error and sets the default values. + /// + public Error() { + 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 Error CreateFromDiscriminatorValue(IParseNode parseNode) { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new Error(); + } + /// + /// The deserialization information for the current model + /// + public virtual IDictionary> GetFieldDeserializers() { + return new Dictionary> { + {"detail", n => { Detail = n.GetStringValue(); } }, + {"error_code", n => { ErrorCode = n.GetIntValue(); } }, + {"message", n => { MessageEscaped = n.GetStringValue(); } }, + {"name", n => { Name = n.GetStringValue(); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteStringValue("detail", Detail); + writer.WriteIntValue("error_code", ErrorCode); + writer.WriteStringValue("message", MessageEscaped); + writer.WriteStringValue("name", Name); + writer.WriteAdditionalData(AdditionalData); + } + } +} diff --git a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/RuleViolationCause.cs b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/RuleViolationCause.cs new file mode 100644 index 0000000000..3190673c6d --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/RuleViolationCause.cs @@ -0,0 +1,61 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace ApiSdk.Models { + public class RuleViolationCause : 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 context property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Context { get; set; } +#nullable restore +#else + public string Context { get; set; } +#endif + /// The description property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Description { get; set; } +#nullable restore +#else + public string Description { get; set; } +#endif + /// + /// Instantiates a new RuleViolationCause and sets the default values. + /// + public RuleViolationCause() { + 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 RuleViolationCause CreateFromDiscriminatorValue(IParseNode parseNode) { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new RuleViolationCause(); + } + /// + /// The deserialization information for the current model + /// + public virtual IDictionary> GetFieldDeserializers() { + return new Dictionary> { + {"context", n => { Context = n.GetStringValue(); } }, + {"description", n => { Description = n.GetStringValue(); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteStringValue("context", Context); + writer.WriteStringValue("description", Description); + writer.WriteAdditionalData(AdditionalData); + } + } +} diff --git a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/RuleViolationError.cs b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/RuleViolationError.cs new file mode 100644 index 0000000000..4a4ff76ec3 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/RuleViolationError.cs @@ -0,0 +1,46 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace ApiSdk.Models { + /// + /// All error responses, whether `4xx` or `5xx` will include one of these as the responsebody. + /// + public class RuleViolationError : Error, IParsable { + /// List of rule violation causes. +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public List? Causes { get; set; } +#nullable restore +#else + public List Causes { get; set; } +#endif + /// + /// 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 new RuleViolationError CreateFromDiscriminatorValue(IParseNode parseNode) { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new RuleViolationError(); + } + /// + /// The deserialization information for the current model + /// + public override IDictionary> GetFieldDeserializers() { + return new Dictionary>(base.GetFieldDeserializers()) { + {"causes", n => { Causes = n.GetCollectionOfObjectValues(RuleViolationCause.CreateFromDiscriminatorValue)?.ToList(); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public override void Serialize(ISerializationWriter writer) { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + base.Serialize(writer); + writer.WriteCollectionOfObjectValues("causes", Causes); + } + } +} diff --git a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/SearchedArtifact.cs b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/SearchedArtifact.cs new file mode 100644 index 0000000000..982e5463ec --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/SearchedArtifact.cs @@ -0,0 +1,136 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace ApiSdk.Models { + /// + /// Models a single artifact from the result set returned when searching for artifacts. + /// + public class SearchedArtifact : 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 createdBy property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? CreatedBy { get; set; } +#nullable restore +#else + public string CreatedBy { get; set; } +#endif + /// The createdOn property + public DateTimeOffset? CreatedOn { get; set; } + /// The description property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Description { get; set; } +#nullable restore +#else + public string Description { get; set; } +#endif + /// An ID of a single artifact group. +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? GroupId { get; set; } +#nullable restore +#else + public string GroupId { get; set; } +#endif + /// The ID of a single artifact. +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Id { get; set; } +#nullable restore +#else + public string Id { get; set; } +#endif + /// The labels property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public List? Labels { get; set; } +#nullable restore +#else + public List Labels { get; set; } +#endif + /// The modifiedBy property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? ModifiedBy { get; set; } +#nullable restore +#else + public string ModifiedBy { get; set; } +#endif + /// The modifiedOn property + public DateTimeOffset? ModifiedOn { get; set; } + /// The name property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Name { get; set; } +#nullable restore +#else + public string Name { get; set; } +#endif + /// Describes the state of an artifact or artifact version. The following statesare possible:* ENABLED* DISABLED* DEPRECATED + public ArtifactState? State { get; set; } + /// The type property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Type { get; set; } +#nullable restore +#else + public string Type { get; set; } +#endif + /// + /// Instantiates a new SearchedArtifact and sets the default values. + /// + public SearchedArtifact() { + 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 SearchedArtifact CreateFromDiscriminatorValue(IParseNode parseNode) { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new SearchedArtifact(); + } + /// + /// The deserialization information for the current model + /// + public virtual IDictionary> GetFieldDeserializers() { + return new Dictionary> { + {"createdBy", n => { CreatedBy = n.GetStringValue(); } }, + {"createdOn", n => { CreatedOn = n.GetDateTimeOffsetValue(); } }, + {"description", n => { Description = n.GetStringValue(); } }, + {"groupId", n => { GroupId = n.GetStringValue(); } }, + {"id", n => { Id = n.GetStringValue(); } }, + {"labels", n => { Labels = n.GetCollectionOfPrimitiveValues()?.ToList(); } }, + {"modifiedBy", n => { ModifiedBy = n.GetStringValue(); } }, + {"modifiedOn", n => { ModifiedOn = n.GetDateTimeOffsetValue(); } }, + {"name", n => { Name = n.GetStringValue(); } }, + {"state", n => { State = n.GetEnumValue(); } }, + {"type", n => { Type = n.GetStringValue(); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteStringValue("createdBy", CreatedBy); + writer.WriteDateTimeOffsetValue("createdOn", CreatedOn); + writer.WriteStringValue("description", Description); + writer.WriteStringValue("groupId", GroupId); + writer.WriteStringValue("id", Id); + writer.WriteCollectionOfPrimitiveValues("labels", Labels); + writer.WriteStringValue("modifiedBy", ModifiedBy); + writer.WriteDateTimeOffsetValue("modifiedOn", ModifiedOn); + writer.WriteStringValue("name", Name); + writer.WriteEnumValue("state", State); + writer.WriteStringValue("type", Type); + writer.WriteAdditionalData(AdditionalData); + } + } +} diff --git a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/SortBy.cs b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/SortBy.cs new file mode 100644 index 0000000000..33adf563a9 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/SortBy.cs @@ -0,0 +1,11 @@ +// +using System.Runtime.Serialization; +using System; +namespace ApiSdk.Models { + public enum SortBy { + [EnumMember(Value = "name")] + Name, + [EnumMember(Value = "createdOn")] + CreatedOn, + } +} diff --git a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/SortOrder.cs b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/SortOrder.cs new file mode 100644 index 0000000000..128abafddd --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/SortOrder.cs @@ -0,0 +1,11 @@ +// +using System.Runtime.Serialization; +using System; +namespace ApiSdk.Models { + public enum SortOrder { + [EnumMember(Value = "asc")] + Asc, + [EnumMember(Value = "desc")] + Desc, + } +} diff --git a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Search/SearchRequestBuilder.cs b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Search/SearchRequestBuilder.cs new file mode 100644 index 0000000000..669e03388f --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Search/SearchRequestBuilder.cs @@ -0,0 +1,33 @@ +// +using ApiSdk.Search.Artifacts; +using Microsoft.Kiota.Abstractions; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using System; +namespace ApiSdk.Search { + /// + /// Builds and executes requests for operations under \search + /// + public class SearchRequestBuilder : BaseRequestBuilder { + /// Search for artifacts in the registry. + public ArtifactsRequestBuilder Artifacts { get => + new ArtifactsRequestBuilder(PathParameters, RequestAdapter); + } + /// + /// Instantiates a new SearchRequestBuilder and sets the default values. + /// + /// Path parameters for the request + /// The request adapter to use to execute the requests. + public SearchRequestBuilder(Dictionary pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/search", pathParameters) { + } + /// + /// Instantiates a new SearchRequestBuilder and sets the default values. + /// + /// The raw URL to use for the request builder. + /// The request adapter to use to execute the requests. + public SearchRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/search", rawUrl) { + } + } +} diff --git a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/kiota-lock.json b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/kiota-lock.json new file mode 100644 index 0000000000..210047a672 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/kiota-lock.json @@ -0,0 +1,34 @@ +{ + "descriptionHash": "5EC58EDC07776795859A9C8A5861F425EC1658F1559FFAC137ABB219A7C8BB58E1675A7BCD20DC981E6C5140790C082ABF8E2679D0B4027C613B1E1C13D0255A", + "descriptionLocation": "https://raw.githubusercontent.com/Apicurio/apicurio-registry/main/common/src/main/resources/META-INF/openapi.json", + "lockFileVersion": "1.0.0", + "kiotaVersion": "1.8.1", + "clientClassName": "ApicurioClient", + "clientNamespaceName": "ApiSdk", + "language": "CSharp", + "usesBackingStore": false, + "excludeBackwardCompatible": false, + "includeAdditionalData": true, + "serializers": [ + "Microsoft.Kiota.Serialization.Json.JsonSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Text.TextSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Form.FormSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Multipart.MultipartSerializationWriterFactory" + ], + "deserializers": [ + "Microsoft.Kiota.Serialization.Json.JsonParseNodeFactory", + "Microsoft.Kiota.Serialization.Text.TextParseNodeFactory", + "Microsoft.Kiota.Serialization.Form.FormParseNodeFactory" + ], + "structuredMimeTypes": [ + "application/json;q=1", + "text/plain;q=0.9", + "application/x-www-form-urlencoded;q=0.2", + "multipart/form-data;q=0.1" + ], + "includePatterns": [ + "/search/**" + ], + "excludePatterns": [], + "disabledValidationRules": [] +} \ No newline at end of file diff --git a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioSearchProvider.cs b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioSearchProvider.cs new file mode 100644 index 0000000000..7e880784a1 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioSearchProvider.cs @@ -0,0 +1,91 @@ + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using ApiSdk; +using ApiSdk.Models; +using Kiota.Builder.Configuration; +using Microsoft.Extensions.Logging; +using Microsoft.Kiota.Abstractions.Authentication; +using Microsoft.Kiota.Http.HttpClientLibrary; + +namespace Kiota.Builder.SearchProviders.Apicurio; + +public class ApicurioSearchProvider : ISearchProvider +{ + private readonly HttpClient _httpClient; + private readonly ILogger _logger; + private readonly IAuthenticationProvider? _authenticatedAuthenticationProvider; + private readonly ApicurioConfiguration _configuration; + + public string ProviderKey => "apicurio"; + + public HashSet KeysToExclude + { + get; init; + } = new(); + + public ApicurioSearchProvider(HttpClient httpClient, ILogger logger, bool clearCache, ApicurioConfiguration configuration, IAuthenticationProvider? authenticatedAuthenticationProvider) + { + ArgumentNullException.ThrowIfNull(httpClient); + ArgumentNullException.ThrowIfNull(configuration); + ArgumentNullException.ThrowIfNull(logger); + _httpClient = httpClient; + _logger = logger; + _configuration = configuration; + _authenticatedAuthenticationProvider = authenticatedAuthenticationProvider; + KeysToExclude = new(StringComparer.OrdinalIgnoreCase); + } + + public async Task> SearchAsync(string term, string? version, CancellationToken cancellationToken) + { + var authenticationProvider = _authenticatedAuthenticationProvider != null ? + _authenticatedAuthenticationProvider : + new AnonymousAuthenticationProvider(); + using var requestAdapter = new HttpClientRequestAdapter(authenticationProvider, httpClient: _httpClient); + requestAdapter.BaseUrl = _configuration.ApiBaseUrl.AbsoluteUri; + var apicurioClient = new ApicurioClient(requestAdapter); + + ArtifactSearchResults? results; + try + { + + // TODO search also for versions + results = await apicurioClient.Search.Artifacts.GetAsync(config => + { + config.QueryParameters.Limit = 100; + config.QueryParameters.Offset = 0; + config.QueryParameters.Labels = new string[] { term }; + }, cancellationToken).ConfigureAwait(false); + } + catch (HttpRequestException) + { + _logger.LogWarning("Error connecting to Apicurio Registry at the URL {String}", _configuration.ApiBaseUrl); + return new Dictionary(); + } + + if (results == null) + return new Dictionary(); + + return results.Artifacts!.Select(x => + { + var groupId = (x.GroupId != null) ? x.GroupId : "default"; + // TODO: FIXME this is wrong + var baseUrl = new Uri(_configuration.ApiBaseUrl.AbsoluteUri.Replace("apis\\/registry\\/v2\\/", string.Empty, StringComparison.OrdinalIgnoreCase) + "/ui/artifacts/" + groupId + "/" + x.Id); + + return new Tuple(term, + new SearchResult(x.Name ?? string.Empty, + x.Description ?? string.Empty, + baseUrl, + new Uri(_configuration.ApiBaseUrl + "/groups/" + groupId + "/artifacts/" + x.Id), + new())); + }).DistinctBy(static x => x.Item1, StringComparer.OrdinalIgnoreCase) + .ToDictionary(static x => x.Item1, + static x => x.Item2, + StringComparer.OrdinalIgnoreCase); + } +} diff --git a/src/kiota/Handlers/BaseKiotaCommandHandler.cs b/src/kiota/Handlers/BaseKiotaCommandHandler.cs index af680b9c65..54bfac7567 100644 --- a/src/kiota/Handlers/BaseKiotaCommandHandler.cs +++ b/src/kiota/Handlers/BaseKiotaCommandHandler.cs @@ -77,6 +77,11 @@ private IAuthenticationProvider GetGitHubPatAuthenticationProvider(ILogger logge new List { Configuration.Search.GitHub.ApiBaseUrl.Host }, logger, GetGitHubPatStorageService(logger)); + + // TODO: implement me + private IAuthenticationProvider GetApicurioAuthenticationProvider(ILogger logger) => + new Microsoft.Kiota.Abstractions.Authentication.AnonymousAuthenticationProvider(); + protected async Task GetKiotaSearcherAsync(ILoggerFactory loggerFactory, CancellationToken cancellationToken) { var logger = loggerFactory.CreateLogger(); @@ -90,7 +95,7 @@ protected async Task GetKiotaSearcherAsync(ILoggerFactory loggerF (_, true) => (GetGitHubPatAuthenticationProvider(logger), patSignInCallBack), (_, _) => (null, (CancellationToken cts) => Task.FromResult(false)) }; - return new KiotaSearcher(logger, Configuration.Search, httpClient, provider, callback); + return new KiotaSearcher(logger, Configuration.Search, httpClient, provider, callback, GetApicurioAuthenticationProvider(logger)); } public int Invoke(InvocationContext context) { diff --git a/src/kiota/Rpc/Server.cs b/src/kiota/Rpc/Server.cs index b1502cd824..01aa9b87f1 100644 --- a/src/kiota/Rpc/Server.cs +++ b/src/kiota/Rpc/Server.cs @@ -93,7 +93,7 @@ public async Task SearchAsync(string searchTerm, bool cle var logger = new ForwardedLogger(); var configuration = Configuration.Search; configuration.ClearCache = clearCache; - var searchService = new KiotaSearcher(logger, configuration, httpClient, null, (_) => Task.FromResult(false)); + var searchService = new KiotaSearcher(logger, configuration, httpClient, null, (_) => Task.FromResult(false), null); var results = await searchService.SearchAsync(searchTerm, string.Empty, cancellationToken); return new(logger.LogEntries, results); } From ae48ef826c6aea506584f25bdeaa71f04eec2ffc Mon Sep 17 00:00:00 2001 From: Andrea Peruffo Date: Thu, 2 Nov 2023 18:34:23 +0000 Subject: [PATCH 2/6] add a note --- .../SearchProviders/Apicurio/ApicurioSearchProvider.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioSearchProvider.cs b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioSearchProvider.cs index 7e880784a1..cbba1f80d9 100644 --- a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioSearchProvider.cs +++ b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioSearchProvider.cs @@ -47,6 +47,7 @@ public async Task> SearchAsync(string term, st _authenticatedAuthenticationProvider : new AnonymousAuthenticationProvider(); using var requestAdapter = new HttpClientRequestAdapter(authenticationProvider, httpClient: _httpClient); + // TODO: the default should be "disabled" when providing a real URL this should work out of the box requestAdapter.BaseUrl = _configuration.ApiBaseUrl.AbsoluteUri; var apicurioClient = new ApicurioClient(requestAdapter); From fe88aa26ab87281068d6f55e22d6507e10496204 Mon Sep 17 00:00:00 2001 From: Andrea Peruffo Date: Mon, 6 Nov 2023 13:54:50 +0000 Subject: [PATCH 3/6] more engineering --- .../Configuration/SearchConfiguration.cs | 26 +++- .../Apicurio/ApicurioClient/ApicurioClient.cs | 5 + .../Groups/GroupsRequestBuilder.cs | 36 +++++ .../Item/WithGroupItemRequestBuilder.cs | 33 ++++ .../Models/ArtifactReference.cs | 84 ++++++++++ .../ApicurioClient/Models/Properties.cs | 44 ++++++ .../ApicurioClient/Models/SearchedVersion.cs | 140 +++++++++++++++++ .../ApicurioClient/Models/VersionMetaData.cs | 147 ++++++++++++++++++ .../Models/VersionSearchResults.cs | 58 +++++++ .../Apicurio/ApicurioClient/kiota-lock.json | 6 +- .../Apicurio/ApicurioSearchProvider.cs | 104 +++++++++---- 11 files changed, 650 insertions(+), 33 deletions(-) create mode 100644 src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Groups/GroupsRequestBuilder.cs create mode 100644 src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Groups/Item/WithGroupItemRequestBuilder.cs create mode 100644 src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/ArtifactReference.cs create mode 100644 src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/Properties.cs create mode 100644 src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/SearchedVersion.cs create mode 100644 src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/VersionMetaData.cs create mode 100644 src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/VersionSearchResults.cs diff --git a/src/Kiota.Builder/Configuration/SearchConfiguration.cs b/src/Kiota.Builder/Configuration/SearchConfiguration.cs index 9181781314..67b76291bb 100644 --- a/src/Kiota.Builder/Configuration/SearchConfiguration.cs +++ b/src/Kiota.Builder/Configuration/SearchConfiguration.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.InteropServices; namespace Kiota.Builder.Configuration; @@ -6,7 +7,6 @@ public class SearchConfiguration : SearchConfigurationBase, ICloneable { public Uri APIsGuruListUrl { get; set; } = new("https://raw.githubusercontent.com/APIs-guru/openapi-directory/gh-pages/v2/list.json"); public GitHubConfiguration GitHub { get; set; } = new(); - public ApicurioConfiguration Apicurio { get; set; } = new(); public object Clone() @@ -42,13 +42,33 @@ public object Clone() public class ApicurioConfiguration : ICloneable { - public Uri ApiBaseUrl { get; set; } = new("http://localhost:8080/apis/registry/v2"); + public enum ApicurioSearchBy + { + LABEL, + PROPERTY + } + + public Uri? ApiBaseUrl + { + get; set; + } + + public Uri? UIBaseUrl + { + get; set; + } + + public int ArtifactsLimit { get; set; } = 10; + public int VersionsLimit { get; set; } = 100; + + public ApicurioSearchBy SearchBy { get; set; } = ApicurioSearchBy.LABEL; public object Clone() { return new ApicurioConfiguration { - ApiBaseUrl = new(ApiBaseUrl.ToString(), UriKind.RelativeOrAbsolute), + ApiBaseUrl = (ApiBaseUrl != null) ? new(ApiBaseUrl.ToString(), UriKind.RelativeOrAbsolute) : null, + UIBaseUrl = (UIBaseUrl != null) ? new(UIBaseUrl.ToString(), UriKind.RelativeOrAbsolute) : null, }; } } diff --git a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/ApicurioClient.cs b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/ApicurioClient.cs index a84ab5e5b8..66cb842575 100644 --- a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/ApicurioClient.cs +++ b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/ApicurioClient.cs @@ -1,4 +1,5 @@ // +using ApiSdk.Groups; using ApiSdk.Search; using Microsoft.Kiota.Abstractions.Extensions; using Microsoft.Kiota.Abstractions; @@ -16,6 +17,10 @@ namespace ApiSdk { /// The main entry point of the SDK, exposes the configuration and the fluent API. /// public class ApicurioClient : BaseRequestBuilder { + /// The groups property + public GroupsRequestBuilder Groups { get => + new GroupsRequestBuilder(PathParameters, RequestAdapter); + } /// The search property public SearchRequestBuilder Search { get => new SearchRequestBuilder(PathParameters, RequestAdapter); diff --git a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Groups/GroupsRequestBuilder.cs b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Groups/GroupsRequestBuilder.cs new file mode 100644 index 0000000000..91f493a97d --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Groups/GroupsRequestBuilder.cs @@ -0,0 +1,36 @@ +// +using ApiSdk.Groups.Item; +using Microsoft.Kiota.Abstractions; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using System; +namespace ApiSdk.Groups { + /// + /// Builds and executes requests for operations under \groups + /// + public class GroupsRequestBuilder : BaseRequestBuilder { + /// Gets an item from the ApiSdk.groups.item collection + /// Unique identifier of the item + public WithGroupItemRequestBuilder this[string position] { get { + var urlTplParams = new Dictionary(PathParameters); + urlTplParams.Add("groupId", position); + return new WithGroupItemRequestBuilder(urlTplParams, RequestAdapter); + } } + /// + /// Instantiates a new GroupsRequestBuilder and sets the default values. + /// + /// Path parameters for the request + /// The request adapter to use to execute the requests. + public GroupsRequestBuilder(Dictionary pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/groups", pathParameters) { + } + /// + /// Instantiates a new GroupsRequestBuilder and sets the default values. + /// + /// The raw URL to use for the request builder. + /// The request adapter to use to execute the requests. + public GroupsRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/groups", rawUrl) { + } + } +} diff --git a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Groups/Item/WithGroupItemRequestBuilder.cs b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Groups/Item/WithGroupItemRequestBuilder.cs new file mode 100644 index 0000000000..df281729be --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Groups/Item/WithGroupItemRequestBuilder.cs @@ -0,0 +1,33 @@ +// +using ApiSdk.Groups.Item.Artifacts; +using Microsoft.Kiota.Abstractions; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using System; +namespace ApiSdk.Groups.Item { + /// + /// Builds and executes requests for operations under \groups\{groupId} + /// + public class WithGroupItemRequestBuilder : BaseRequestBuilder { + /// The artifacts property + public ArtifactsRequestBuilder Artifacts { get => + new ArtifactsRequestBuilder(PathParameters, RequestAdapter); + } + /// + /// Instantiates a new WithGroupItemRequestBuilder and sets the default values. + /// + /// Path parameters for the request + /// The request adapter to use to execute the requests. + public WithGroupItemRequestBuilder(Dictionary pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/groups/{groupId}", pathParameters) { + } + /// + /// Instantiates a new WithGroupItemRequestBuilder and sets the default values. + /// + /// The raw URL to use for the request builder. + /// The request adapter to use to execute the requests. + public WithGroupItemRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/groups/{groupId}", rawUrl) { + } + } +} diff --git a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/ArtifactReference.cs b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/ArtifactReference.cs new file mode 100644 index 0000000000..e4244f0ed3 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/ArtifactReference.cs @@ -0,0 +1,84 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace ApiSdk.Models { + /// + /// A reference to a different artifact. Typically used with artifact types that can have dependencies like Protobuf. + /// + public class ArtifactReference : 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 artifactId property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? ArtifactId { get; set; } +#nullable restore +#else + public string ArtifactId { get; set; } +#endif + /// The groupId property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? GroupId { get; set; } +#nullable restore +#else + public string GroupId { get; set; } +#endif + /// The name property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Name { get; set; } +#nullable restore +#else + public string Name { get; set; } +#endif + /// The version property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Version { get; set; } +#nullable restore +#else + public string Version { get; set; } +#endif + /// + /// Instantiates a new ArtifactReference and sets the default values. + /// + public ArtifactReference() { + 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 ArtifactReference CreateFromDiscriminatorValue(IParseNode parseNode) { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new ArtifactReference(); + } + /// + /// The deserialization information for the current model + /// + public virtual IDictionary> GetFieldDeserializers() { + return new Dictionary> { + {"artifactId", n => { ArtifactId = n.GetStringValue(); } }, + {"groupId", n => { GroupId = n.GetStringValue(); } }, + {"name", n => { Name = n.GetStringValue(); } }, + {"version", n => { Version = n.GetStringValue(); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteStringValue("artifactId", ArtifactId); + writer.WriteStringValue("groupId", GroupId); + writer.WriteStringValue("name", Name); + writer.WriteStringValue("version", Version); + writer.WriteAdditionalData(AdditionalData); + } + } +} diff --git a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/Properties.cs b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/Properties.cs new file mode 100644 index 0000000000..9ee3e63e7f --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/Properties.cs @@ -0,0 +1,44 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace ApiSdk.Models { + /// + /// User-defined name-value pairs. Name and value must be strings. + /// + public class Properties : 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; } + /// + /// Instantiates a new Properties and sets the default values. + /// + public Properties() { + 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 Properties CreateFromDiscriminatorValue(IParseNode parseNode) { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new Properties(); + } + /// + /// The deserialization information for the current model + /// + public virtual IDictionary> GetFieldDeserializers() { + return new Dictionary> { + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteAdditionalData(AdditionalData); + } + } +} diff --git a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/SearchedVersion.cs b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/SearchedVersion.cs new file mode 100644 index 0000000000..2f4db6e03d --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/SearchedVersion.cs @@ -0,0 +1,140 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace ApiSdk.Models { + /// + /// Models a single artifact from the result set returned when searching for artifacts. + /// + public class SearchedVersion : 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 contentId property + public long? ContentId { get; set; } + /// The createdBy property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? CreatedBy { get; set; } +#nullable restore +#else + public string CreatedBy { get; set; } +#endif + /// The createdOn property + public DateTimeOffset? CreatedOn { get; set; } + /// The description property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Description { get; set; } +#nullable restore +#else + public string Description { get; set; } +#endif + /// The globalId property + public long? GlobalId { get; set; } + /// The labels property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public List? Labels { get; set; } +#nullable restore +#else + public List Labels { get; set; } +#endif + /// The name property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Name { get; set; } +#nullable restore +#else + public string Name { get; set; } +#endif + /// User-defined name-value pairs. Name and value must be strings. +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public ApiSdk.Models.Properties? Properties { get; set; } +#nullable restore +#else + public ApiSdk.Models.Properties Properties { get; set; } +#endif + /// The references property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public List? References { get; set; } +#nullable restore +#else + public List References { get; set; } +#endif + /// Describes the state of an artifact or artifact version. The following statesare possible:* ENABLED* DISABLED* DEPRECATED + public ArtifactState? State { get; set; } + /// The type property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Type { get; set; } +#nullable restore +#else + public string Type { get; set; } +#endif + /// The version property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Version { get; set; } +#nullable restore +#else + public string Version { get; set; } +#endif + /// + /// Instantiates a new SearchedVersion and sets the default values. + /// + public SearchedVersion() { + 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 SearchedVersion CreateFromDiscriminatorValue(IParseNode parseNode) { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new SearchedVersion(); + } + /// + /// The deserialization information for the current model + /// + public virtual IDictionary> GetFieldDeserializers() { + return new Dictionary> { + {"contentId", n => { ContentId = n.GetLongValue(); } }, + {"createdBy", n => { CreatedBy = n.GetStringValue(); } }, + {"createdOn", n => { CreatedOn = n.GetDateTimeOffsetValue(); } }, + {"description", n => { Description = n.GetStringValue(); } }, + {"globalId", n => { GlobalId = n.GetLongValue(); } }, + {"labels", n => { Labels = n.GetCollectionOfPrimitiveValues()?.ToList(); } }, + {"name", n => { Name = n.GetStringValue(); } }, + {"properties", n => { Properties = n.GetObjectValue(ApiSdk.Models.Properties.CreateFromDiscriminatorValue); } }, + {"references", n => { References = n.GetCollectionOfObjectValues(ArtifactReference.CreateFromDiscriminatorValue)?.ToList(); } }, + {"state", n => { State = n.GetEnumValue(); } }, + {"type", n => { Type = n.GetStringValue(); } }, + {"version", n => { Version = n.GetStringValue(); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteLongValue("contentId", ContentId); + writer.WriteStringValue("createdBy", CreatedBy); + writer.WriteDateTimeOffsetValue("createdOn", CreatedOn); + writer.WriteStringValue("description", Description); + writer.WriteLongValue("globalId", GlobalId); + writer.WriteCollectionOfPrimitiveValues("labels", Labels); + writer.WriteStringValue("name", Name); + writer.WriteObjectValue("properties", Properties); + writer.WriteCollectionOfObjectValues("references", References); + writer.WriteEnumValue("state", State); + writer.WriteStringValue("type", Type); + writer.WriteStringValue("version", Version); + writer.WriteAdditionalData(AdditionalData); + } + } +} diff --git a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/VersionMetaData.cs b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/VersionMetaData.cs new file mode 100644 index 0000000000..be7a8aff94 --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/VersionMetaData.cs @@ -0,0 +1,147 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace ApiSdk.Models { + public class VersionMetaData : 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 contentId property + public long? ContentId { get; set; } + /// The createdBy property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? CreatedBy { get; set; } +#nullable restore +#else + public string CreatedBy { get; set; } +#endif + /// The createdOn property + public DateTimeOffset? CreatedOn { get; set; } + /// The description property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Description { get; set; } +#nullable restore +#else + public string Description { get; set; } +#endif + /// The globalId property + public long? GlobalId { get; set; } + /// An ID of a single artifact group. +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? GroupId { get; set; } +#nullable restore +#else + public string GroupId { get; set; } +#endif + /// The ID of a single artifact. +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Id { get; set; } +#nullable restore +#else + public string Id { get; set; } +#endif + /// The labels property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public List? Labels { get; set; } +#nullable restore +#else + public List Labels { get; set; } +#endif + /// The name property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Name { get; set; } +#nullable restore +#else + public string Name { get; set; } +#endif + /// User-defined name-value pairs. Name and value must be strings. +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public ApiSdk.Models.Properties? Properties { get; set; } +#nullable restore +#else + public ApiSdk.Models.Properties Properties { get; set; } +#endif + /// Describes the state of an artifact or artifact version. The following statesare possible:* ENABLED* DISABLED* DEPRECATED + public ArtifactState? State { get; set; } + /// The type property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Type { get; set; } +#nullable restore +#else + public string Type { get; set; } +#endif + /// The version property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Version { get; set; } +#nullable restore +#else + public string Version { get; set; } +#endif + /// + /// Instantiates a new VersionMetaData and sets the default values. + /// + public VersionMetaData() { + 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 VersionMetaData CreateFromDiscriminatorValue(IParseNode parseNode) { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new VersionMetaData(); + } + /// + /// The deserialization information for the current model + /// + public virtual IDictionary> GetFieldDeserializers() { + return new Dictionary> { + {"contentId", n => { ContentId = n.GetLongValue(); } }, + {"createdBy", n => { CreatedBy = n.GetStringValue(); } }, + {"createdOn", n => { CreatedOn = n.GetDateTimeOffsetValue(); } }, + {"description", n => { Description = n.GetStringValue(); } }, + {"globalId", n => { GlobalId = n.GetLongValue(); } }, + {"groupId", n => { GroupId = n.GetStringValue(); } }, + {"id", n => { Id = n.GetStringValue(); } }, + {"labels", n => { Labels = n.GetCollectionOfPrimitiveValues()?.ToList(); } }, + {"name", n => { Name = n.GetStringValue(); } }, + {"properties", n => { Properties = n.GetObjectValue(ApiSdk.Models.Properties.CreateFromDiscriminatorValue); } }, + {"state", n => { State = n.GetEnumValue(); } }, + {"type", n => { Type = n.GetStringValue(); } }, + {"version", n => { Version = n.GetStringValue(); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteLongValue("contentId", ContentId); + writer.WriteStringValue("createdBy", CreatedBy); + writer.WriteDateTimeOffsetValue("createdOn", CreatedOn); + writer.WriteStringValue("description", Description); + writer.WriteLongValue("globalId", GlobalId); + writer.WriteStringValue("groupId", GroupId); + writer.WriteStringValue("id", Id); + writer.WriteCollectionOfPrimitiveValues("labels", Labels); + writer.WriteStringValue("name", Name); + writer.WriteObjectValue("properties", Properties); + writer.WriteEnumValue("state", State); + writer.WriteStringValue("type", Type); + writer.WriteStringValue("version", Version); + writer.WriteAdditionalData(AdditionalData); + } + } +} diff --git a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/VersionSearchResults.cs b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/VersionSearchResults.cs new file mode 100644 index 0000000000..6c4884707e --- /dev/null +++ b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/Models/VersionSearchResults.cs @@ -0,0 +1,58 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace ApiSdk.Models { + /// + /// Describes the response received when searching for artifacts. + /// + public class VersionSearchResults : 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 total number of versions that matched the query (may be more than the number of versionsreturned in the result set). + public int? Count { get; set; } + /// The collection of artifact versions returned in the result set. +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public List? Versions { get; set; } +#nullable restore +#else + public List Versions { get; set; } +#endif + /// + /// Instantiates a new VersionSearchResults and sets the default values. + /// + public VersionSearchResults() { + 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 VersionSearchResults CreateFromDiscriminatorValue(IParseNode parseNode) { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new VersionSearchResults(); + } + /// + /// The deserialization information for the current model + /// + public virtual IDictionary> GetFieldDeserializers() { + return new Dictionary> { + {"count", n => { Count = n.GetIntValue(); } }, + {"versions", n => { Versions = n.GetCollectionOfObjectValues(SearchedVersion.CreateFromDiscriminatorValue)?.ToList(); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteIntValue("count", Count); + writer.WriteCollectionOfObjectValues("versions", Versions); + writer.WriteAdditionalData(AdditionalData); + } + } +} diff --git a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/kiota-lock.json b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/kiota-lock.json index 210047a672..b2f2ee9ccd 100644 --- a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/kiota-lock.json +++ b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioClient/kiota-lock.json @@ -1,5 +1,5 @@ { - "descriptionHash": "5EC58EDC07776795859A9C8A5861F425EC1658F1559FFAC137ABB219A7C8BB58E1675A7BCD20DC981E6C5140790C082ABF8E2679D0B4027C613B1E1C13D0255A", + "descriptionHash": "D5B27A71189D0DED59490DB1BB5139C2978BCFDBC33290DB09A0A7F6F64B7D47E674D63EF48F305DC29A5DFF15B57000D1C2BB276A9617C2F28590B92138CFB5", "descriptionLocation": "https://raw.githubusercontent.com/Apicurio/apicurio-registry/main/common/src/main/resources/META-INF/openapi.json", "lockFileVersion": "1.0.0", "kiotaVersion": "1.8.1", @@ -27,7 +27,9 @@ "multipart/form-data;q=0.1" ], "includePatterns": [ - "/search/**" + "/search/*", + "/groups/**/artifacts/**/versions#GET", + "/groups/**/artifacts/**/versions/**/meta#GET" ], "excludePatterns": [], "disabledValidationRules": [] diff --git a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioSearchProvider.cs b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioSearchProvider.cs index cbba1f80d9..730e68ef01 100644 --- a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioSearchProvider.cs +++ b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioSearchProvider.cs @@ -1,5 +1,3 @@ - - using System; using System.Collections.Generic; using System.Linq; @@ -22,6 +20,8 @@ public class ApicurioSearchProvider : ISearchProvider private readonly IAuthenticationProvider? _authenticatedAuthenticationProvider; private readonly ApicurioConfiguration _configuration; + private readonly static IDictionary EMPTY_RESULT = new Dictionary(); + public string ProviderKey => "apicurio"; public HashSet KeysToExclude @@ -43,50 +43,98 @@ public ApicurioSearchProvider(HttpClient httpClient, ILogger logger, bool clearC public async Task> SearchAsync(string term, string? version, CancellationToken cancellationToken) { + if (_configuration.ApiBaseUrl == null) + { + _logger.LogInformation("Apicurio provider not configured, skipping."); + return EMPTY_RESULT; + } + var authenticationProvider = _authenticatedAuthenticationProvider != null ? _authenticatedAuthenticationProvider : new AnonymousAuthenticationProvider(); using var requestAdapter = new HttpClientRequestAdapter(authenticationProvider, httpClient: _httpClient); - // TODO: the default should be "disabled" when providing a real URL this should work out of the box requestAdapter.BaseUrl = _configuration.ApiBaseUrl.AbsoluteUri; var apicurioClient = new ApicurioClient(requestAdapter); - ArtifactSearchResults? results; + IDictionary result; try { - - // TODO search also for versions - results = await apicurioClient.Search.Artifacts.GetAsync(config => + ArtifactSearchResults? searchResults = await apicurioClient.Search.Artifacts.GetAsync(config => { - config.QueryParameters.Limit = 100; + config.QueryParameters.Limit = _configuration.ArtifactsLimit; config.QueryParameters.Offset = 0; - config.QueryParameters.Labels = new string[] { term }; + switch (_configuration.SearchBy) + { + case ApicurioConfiguration.ApicurioSearchBy.LABEL: + config.QueryParameters.Labels = new string[] { term }; + break; + case ApicurioConfiguration.ApicurioSearchBy.PROPERTY: + config.QueryParameters.Properties = new string[] { term }; + break; + } }, cancellationToken).ConfigureAwait(false); + + if (searchResults == null || searchResults!.Artifacts == null) + return EMPTY_RESULT; + + var dictionaries = searchResults!.Artifacts!.Select(async x => + { + var groupId = (x.GroupId != null) ? x.GroupId : "default"; + var uiUrl = new Uri(_configuration.UIBaseUrl + "/artifacts/" + groupId + "/" + x.Id); + var restUrl = new Uri(_configuration.ApiBaseUrl + "/groups/" + groupId + "/artifacts/" + x.Id); + + if (version != null) + { + var versionMetadata = await apicurioClient.Groups[groupId].Artifacts[x.Id].Versions[version!].Meta.GetAsync().ConfigureAwait(false); + + if (versionMetadata == null || versionMetadata!.Version == null) + { + return EMPTY_RESULT; + } + + return new Dictionary() + { + [x.Id!] = new SearchResult(x.Name ?? x.Id!, + x.Description ?? string.Empty, + uiUrl, + restUrl, + new List(1) { versionMetadata!.Version! }) + }; + } + else + { + var versions = await apicurioClient.Groups[groupId].Artifacts[x.Id].Versions.GetAsync(config => + { + config.QueryParameters.Limit = _configuration.VersionsLimit; + config.QueryParameters.Offset = 0; + }, cancellationToken).ConfigureAwait(false); + + if (versions == null || versions.Versions == null) + return EMPTY_RESULT; + + return new Dictionary() + { + [x.Id!] = new SearchResult(x.Name ?? x.Id!, + x.Description ?? string.Empty, + uiUrl, + restUrl, + versions.Versions!.Select(static v => v.Version!).ToList()) + }; + } + }); + + var x = await Task.WhenAll(dictionaries).ConfigureAwait(false); + result = x.SelectMany(static dict => dict).ToDictionary(static x => x.Key, static x => x.Value, StringComparer.OrdinalIgnoreCase); } catch (HttpRequestException) { _logger.LogWarning("Error connecting to Apicurio Registry at the URL {String}", _configuration.ApiBaseUrl); - return new Dictionary(); + return EMPTY_RESULT; } - if (results == null) - return new Dictionary(); + if (result == null) + return EMPTY_RESULT; - return results.Artifacts!.Select(x => - { - var groupId = (x.GroupId != null) ? x.GroupId : "default"; - // TODO: FIXME this is wrong - var baseUrl = new Uri(_configuration.ApiBaseUrl.AbsoluteUri.Replace("apis\\/registry\\/v2\\/", string.Empty, StringComparison.OrdinalIgnoreCase) + "/ui/artifacts/" + groupId + "/" + x.Id); - - return new Tuple(term, - new SearchResult(x.Name ?? string.Empty, - x.Description ?? string.Empty, - baseUrl, - new Uri(_configuration.ApiBaseUrl + "/groups/" + groupId + "/artifacts/" + x.Id), - new())); - }).DistinctBy(static x => x.Item1, StringComparer.OrdinalIgnoreCase) - .ToDictionary(static x => x.Item1, - static x => x.Item2, - StringComparer.OrdinalIgnoreCase); + return result; } } From d1971eaf11dab4856493fde65668b37e3a9408bf Mon Sep 17 00:00:00 2001 From: Andrea Peruffo Date: Mon, 6 Nov 2023 14:36:51 +0000 Subject: [PATCH 4/6] starting to look good --- .../Configuration/SearchConfiguration.cs | 3 +++ .../Apicurio/ApicurioSearchProvider.cs | 8 +++++++- src/kiota/KiotaConfigurationExtensions.cs | 5 +++++ src/kiota/appsettings.json | 4 ++++ .../Kiota.Builder.Tests/KiotaSearcherTests.cs | 18 +++++++++--------- 5 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/Kiota.Builder/Configuration/SearchConfiguration.cs b/src/Kiota.Builder/Configuration/SearchConfiguration.cs index 67b76291bb..9ab828386f 100644 --- a/src/Kiota.Builder/Configuration/SearchConfiguration.cs +++ b/src/Kiota.Builder/Configuration/SearchConfiguration.cs @@ -69,6 +69,9 @@ public object Clone() { ApiBaseUrl = (ApiBaseUrl != null) ? new(ApiBaseUrl.ToString(), UriKind.RelativeOrAbsolute) : null, UIBaseUrl = (UIBaseUrl != null) ? new(UIBaseUrl.ToString(), UriKind.RelativeOrAbsolute) : null, + ArtifactsLimit = ArtifactsLimit, + VersionsLimit = VersionsLimit, + SearchBy = SearchBy }; } } diff --git a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioSearchProvider.cs b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioSearchProvider.cs index 730e68ef01..458efd7af0 100644 --- a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioSearchProvider.cs +++ b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioSearchProvider.cs @@ -83,7 +83,9 @@ public async Task> SearchAsync(string term, st var uiUrl = new Uri(_configuration.UIBaseUrl + "/artifacts/" + groupId + "/" + x.Id); var restUrl = new Uri(_configuration.ApiBaseUrl + "/groups/" + groupId + "/artifacts/" + x.Id); - if (version != null) + Console.WriteLine("FOUND SOMETHING: " + groupId + " - version: " + version); + + if (!string.IsNullOrEmpty(version)) { var versionMetadata = await apicurioClient.Groups[groupId].Artifacts[x.Id].Versions[version!].Meta.GetAsync().ConfigureAwait(false); @@ -131,6 +133,10 @@ public async Task> SearchAsync(string term, st _logger.LogWarning("Error connecting to Apicurio Registry at the URL {String}", _configuration.ApiBaseUrl); return EMPTY_RESULT; } + catch (ApiSdk.Models.Error) + { + return EMPTY_RESULT; + } if (result == null) return EMPTY_RESULT; diff --git a/src/kiota/KiotaConfigurationExtensions.cs b/src/kiota/KiotaConfigurationExtensions.cs index a44dce586b..4fea50cfd9 100644 --- a/src/kiota/KiotaConfigurationExtensions.cs +++ b/src/kiota/KiotaConfigurationExtensions.cs @@ -28,6 +28,11 @@ public static void BindConfiguration(this KiotaConfiguration configObject, IConf configObject.Search.GitHub.AppId = configuration[$"{nameof(configObject.Search)}:{nameof(SearchConfiguration.GitHub)}:{nameof(GitHubConfiguration.AppId)}"] is string appId && !string.IsNullOrEmpty(appId) ? appId : configObject.Search.GitHub.AppId; configObject.Search.GitHub.AppManagement = Uri.TryCreate(configuration[$"{nameof(configObject.Search)}:{nameof(SearchConfiguration.GitHub)}:{nameof(GitHubConfiguration.AppManagement)}"], new UriCreationOptions(), out var appManagement) ? appManagement : configObject.Search.GitHub.AppManagement; configObject.Search.GitHub.BlockListUrl = Uri.TryCreate(configuration[$"{nameof(configObject.Search)}:{nameof(SearchConfiguration.GitHub)}:{nameof(GitHubConfiguration.BlockListUrl)}"], new UriCreationOptions(), out var blockListUrl) ? blockListUrl : configObject.Search.GitHub.BlockListUrl; + configObject.Search.Apicurio.ApiBaseUrl = Uri.TryCreate(configuration[$"{nameof(configObject.Search)}:{nameof(SearchConfiguration.Apicurio)}:{nameof(ApicurioConfiguration.ApiBaseUrl)}"], new UriCreationOptions(), out var apicurioApiBaseUrl) ? apicurioApiBaseUrl : configObject.Search.Apicurio.ApiBaseUrl; + configObject.Search.Apicurio.UIBaseUrl = Uri.TryCreate(configuration[$"{nameof(configObject.Search)}:{nameof(SearchConfiguration.Apicurio)}:{nameof(ApicurioConfiguration.UIBaseUrl)}"], new UriCreationOptions(), out var uiBaseUrl) ? uiBaseUrl : configObject.Search.Apicurio.UIBaseUrl; + configObject.Search.Apicurio.ArtifactsLimit = configuration[$"{nameof(configObject.Search)}:{nameof(SearchConfiguration.Apicurio)}"] is string artifactsLimit && !string.IsNullOrEmpty(artifactsLimit) ? int.Parse(artifactsLimit) : configObject.Search.Apicurio.ArtifactsLimit; + configObject.Search.Apicurio.VersionsLimit = configuration[$"{nameof(configObject.Search)}:{nameof(SearchConfiguration.Apicurio)}"] is string versionsLimit && !string.IsNullOrEmpty(versionsLimit) ? int.Parse(versionsLimit) : configObject.Search.Apicurio.VersionsLimit; + configObject.Search.Apicurio.SearchBy = configuration[$"{nameof(configObject.Search)}:{nameof(SearchConfiguration.Apicurio)}"] is string searchBy && !string.IsNullOrEmpty(searchBy) ? (ApicurioConfiguration.ApicurioSearchBy)Enum.Parse(typeof(ApicurioConfiguration.ApicurioSearchBy), searchBy) : configObject.Search.Apicurio.SearchBy; var languagesSection = configuration.GetSection(nameof(configObject.Languages)); foreach (var section in languagesSection.GetChildren()) diff --git a/src/kiota/appsettings.json b/src/kiota/appsettings.json index d13e556356..2938152603 100644 --- a/src/kiota/appsettings.json +++ b/src/kiota/appsettings.json @@ -9,6 +9,10 @@ "BlockListUrl": "https://raw.githubusercontent.com/microsoft/kiota/main/resources/index-block-list.yml", "AppId": "Iv1.9ed2bcb878c90617", "AppManagement": "https://aka.ms/kiota/install/github" + }, + "Apicurio": { + "ApiBaseUrl": "http://localhost:8080/apis/registry/v2", + "UIBaseUrl": "http://localhost:8080/ui" } }, "Logging": { diff --git a/tests/Kiota.Builder.Tests/KiotaSearcherTests.cs b/tests/Kiota.Builder.Tests/KiotaSearcherTests.cs index 7c04daa653..3dba4c7f19 100644 --- a/tests/Kiota.Builder.Tests/KiotaSearcherTests.cs +++ b/tests/Kiota.Builder.Tests/KiotaSearcherTests.cs @@ -16,9 +16,9 @@ public class KiotaSearcherTests : IDisposable [Fact] public void DefensivePrograming() { - 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 KiotaSearcher(null, new SearchConfiguration(), httpClient, null, null, null)); + Assert.Throws(() => new KiotaSearcher(new Mock>().Object, null, httpClient, null, null, null)); + Assert.Throws(() => new KiotaSearcher(new Mock>().Object, new SearchConfiguration(), null, 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)); @@ -34,7 +34,7 @@ public void DefensivePrograming() public async Task GetsMicrosoftGraphBothVersions() { var searchConfiguration = searchConfigurationFactory; - var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient, null, null); + var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient, null, null, null); var results = await searcher.SearchAsync("github::microsoftgraph/msgraph-metadata", string.Empty, new CancellationToken()); Assert.Equal(2, results.Count); } @@ -42,7 +42,7 @@ public async Task GetsMicrosoftGraphBothVersions() public async Task GetsMicrosoftGraph() { var searchConfiguration = searchConfigurationFactory; - var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient, null, null); + var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient, null, 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()); @@ -51,7 +51,7 @@ public async Task GetsMicrosoftGraph() public async Task GetsMicrosoftGraphBeta() { var searchConfiguration = searchConfigurationFactory; - var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient, null, null); + var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient, null, 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()); @@ -59,7 +59,7 @@ public async Task GetsMicrosoftGraphBeta() [Fact] public async Task DoesntFailOnEmptyTerm() { - var searcher = new KiotaSearcher(new Mock>().Object, searchConfigurationFactory, httpClient, null, null); + var searcher = new KiotaSearcher(new Mock>().Object, searchConfigurationFactory, httpClient, null, null, null); var results = await searcher.SearchAsync(string.Empty, string.Empty, new CancellationToken()); Assert.Empty(results); } @@ -67,7 +67,7 @@ public async Task DoesntFailOnEmptyTerm() public async Task GetsGithubFromApisGuru() { var searchConfiguration = searchConfigurationFactory; - var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient, null, null); + var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient, null, null, null); var results = await searcher.SearchAsync("github", string.Empty, new CancellationToken()); Assert.NotEmpty(results); } @@ -75,7 +75,7 @@ public async Task GetsGithubFromApisGuru() public async Task GetsGithubFromApisGuruWithExactMatch() { var searchConfiguration = searchConfigurationFactory; - var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient, null, null); + var searcher = new KiotaSearcher(new Mock>().Object, searchConfiguration, httpClient, null, null, null); var results = await searcher.SearchAsync("apisguru::github.com:api.github.com.2022-11-28", string.Empty, new CancellationToken()); Assert.Single(results); } From 5b016791cd9cc5b73f2f9f63d27269e7e072766f Mon Sep 17 00:00:00 2001 From: Andrea Peruffo Date: Tue, 7 Nov 2023 09:47:54 +0000 Subject: [PATCH 5/6] Update src/Kiota.Builder/Configuration/SearchConfiguration.cs Co-authored-by: Vincent Biret --- src/Kiota.Builder/Configuration/SearchConfiguration.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Kiota.Builder/Configuration/SearchConfiguration.cs b/src/Kiota.Builder/Configuration/SearchConfiguration.cs index 9ab828386f..eaafb9458e 100644 --- a/src/Kiota.Builder/Configuration/SearchConfiguration.cs +++ b/src/Kiota.Builder/Configuration/SearchConfiguration.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.InteropServices; namespace Kiota.Builder.Configuration; From 7308e027403e8f65db80e13b32259b3ad779fbd9 Mon Sep 17 00:00:00 2001 From: Andrea Peruffo Date: Tue, 7 Nov 2023 09:54:27 +0000 Subject: [PATCH 6/6] review --- .../SearchProviders/Apicurio/ApicurioSearchProvider.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioSearchProvider.cs b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioSearchProvider.cs index 458efd7af0..14eb7e0890 100644 --- a/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioSearchProvider.cs +++ b/src/Kiota.Builder/SearchProviders/Apicurio/ApicurioSearchProvider.cs @@ -83,11 +83,9 @@ public async Task> SearchAsync(string term, st var uiUrl = new Uri(_configuration.UIBaseUrl + "/artifacts/" + groupId + "/" + x.Id); var restUrl = new Uri(_configuration.ApiBaseUrl + "/groups/" + groupId + "/artifacts/" + x.Id); - Console.WriteLine("FOUND SOMETHING: " + groupId + " - version: " + version); - if (!string.IsNullOrEmpty(version)) { - var versionMetadata = await apicurioClient.Groups[groupId].Artifacts[x.Id].Versions[version!].Meta.GetAsync().ConfigureAwait(false); + var versionMetadata = await apicurioClient.Groups[groupId].Artifacts[x.Id].Versions[version!].Meta.GetAsync(cancellationToken: cancellationToken).ConfigureAwait(false); if (versionMetadata == null || versionMetadata!.Version == null) {