diff --git a/src/ApiGenerator/Domain/Specification/UrlPart.cs b/src/ApiGenerator/Domain/Specification/UrlPart.cs index aec5d2aa51f..0e9587400f7 100644 --- a/src/ApiGenerator/Domain/Specification/UrlPart.cs +++ b/src/ApiGenerator/Domain/Specification/UrlPart.cs @@ -69,6 +69,7 @@ public string HighLevelTypeName case "forecast_id": case "action_id": + case "ids" when Type == "list": return "Ids"; case "index": diff --git a/src/ApiGenerator/RestSpecification/_Patches/security.clear_api_key_cache.patch.json b/src/ApiGenerator/RestSpecification/_Patches/security.clear_api_key_cache.patch.json new file mode 100644 index 00000000000..4fbf92375c2 --- /dev/null +++ b/src/ApiGenerator/RestSpecification/_Patches/security.clear_api_key_cache.patch.json @@ -0,0 +1,7 @@ +{ + "security.clear_api_key_cache": { + "url": { + "paths": ["/_security/api_key/{ids}/_clear_cache", "/_security/api_key/*/_clear_cache"] + } + } +} diff --git a/src/Elasticsearch.Net/ElasticLowLevelClient.Security.cs b/src/Elasticsearch.Net/ElasticLowLevelClient.Security.cs index c8de3a8ccd3..fa094597934 100644 --- a/src/Elasticsearch.Net/ElasticLowLevelClient.Security.cs +++ b/src/Elasticsearch.Net/ElasticLowLevelClient.Security.cs @@ -87,6 +87,15 @@ public TResponse ClearApiKeyCache(string ids, ClearApiKeyCacheRequest [MapsApi("security.clear_api_key_cache", "ids")] public Task ClearApiKeyCacheAsync(string ids, ClearApiKeyCacheRequestParameters requestParameters = null, CancellationToken ctx = default) where TResponse : class, IElasticsearchResponse, new() => DoRequestAsync(POST, Url($"_security/api_key/{ids:ids}/_clear_cache"), ctx, null, RequestParams(requestParameters)); + ///POST on /_security/api_key/*/_clear_cache https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-clear-api-key-cache.html + ///Request specific configuration such as querystring parameters & request specific connection settings. + public TResponse ClearApiKeyCache(ClearApiKeyCacheRequestParameters requestParameters = null) + where TResponse : class, IElasticsearchResponse, new() => DoRequest(POST, "_security/api_key/*/_clear_cache", null, RequestParams(requestParameters)); + ///POST on /_security/api_key/*/_clear_cache https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-clear-api-key-cache.html + ///Request specific configuration such as querystring parameters & request specific connection settings. + [MapsApi("security.clear_api_key_cache", "")] + public Task ClearApiKeyCacheAsync(ClearApiKeyCacheRequestParameters requestParameters = null, CancellationToken ctx = default) + where TResponse : class, IElasticsearchResponse, new() => DoRequestAsync(POST, "_security/api_key/*/_clear_cache", ctx, null, RequestParams(requestParameters)); ///POST on /_security/privilege/{application}/_clear_cache https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-clear-privilege-cache.html ///A comma-separated list of application names ///Request specific configuration such as querystring parameters & request specific connection settings. diff --git a/src/Nest/Descriptors.Security.cs b/src/Nest/Descriptors.Security.cs index 62beaeebfa8..ad336858584 100644 --- a/src/Nest/Descriptors.Security.cs +++ b/src/Nest/Descriptors.Security.cs @@ -62,6 +62,28 @@ public ChangePasswordDescriptor(): base() public ChangePasswordDescriptor Refresh(Refresh? refresh) => Qs("refresh", refresh); } + ///Descriptor for ClearApiKeyCache https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-clear-api-key-cache.html + public partial class ClearApiKeyCacheDescriptor : RequestDescriptorBase, IClearApiKeyCacheRequest + { + internal override ApiUrls ApiUrls => ApiUrlsLookups.SecurityClearApiKeyCache; + ////_security/api_key/{ids}/_clear_cache + ///Optional, accepts null + public ClearApiKeyCacheDescriptor(Ids ids): base(r => r.Optional("ids", ids)) + { + } + + ////_security/api_key/*/_clear_cache + public ClearApiKeyCacheDescriptor(): base() + { + } + + // values part of the url path + Ids IClearApiKeyCacheRequest.Ids => Self.RouteValues.Get("ids"); + ///A comma-separated list of IDs of API keys to clear from the cache + public ClearApiKeyCacheDescriptor Ids(Ids ids) => Assign(ids, (a, v) => a.RouteValues.Optional("ids", v)); + // Request parameters + } + ///Descriptor for ClearCachedPrivileges https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-clear-privilege-cache.html public partial class ClearCachedPrivilegesDescriptor : RequestDescriptorBase, IClearCachedPrivilegesRequest { diff --git a/src/Nest/ElasticClient.Security.cs b/src/Nest/ElasticClient.Security.cs index 9a4d33abe63..25b1465bdb6 100644 --- a/src/Nest/ElasticClient.Security.cs +++ b/src/Nest/ElasticClient.Security.cs @@ -85,6 +85,30 @@ internal SecurityNamespace(ElasticClient client): base(client) /// public Task ChangePasswordAsync(IChangePasswordRequest request, CancellationToken ct = default) => DoRequestAsync(request, request.RequestParameters, ct); /// + /// POST request to the security.clear_api_key_cache API, read more about this API online: + /// + /// https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-clear-api-key-cache.html + /// + public ClearApiKeyCacheResponse ClearApiKeyCache(Func selector = null) => ClearApiKeyCache(selector.InvokeOrDefault(new ClearApiKeyCacheDescriptor())); + /// + /// POST request to the security.clear_api_key_cache API, read more about this API online: + /// + /// https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-clear-api-key-cache.html + /// + public Task ClearApiKeyCacheAsync(Func selector = null, CancellationToken ct = default) => ClearApiKeyCacheAsync(selector.InvokeOrDefault(new ClearApiKeyCacheDescriptor()), ct); + /// + /// POST request to the security.clear_api_key_cache API, read more about this API online: + /// + /// https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-clear-api-key-cache.html + /// + public ClearApiKeyCacheResponse ClearApiKeyCache(IClearApiKeyCacheRequest request) => DoRequest(request, request.RequestParameters); + /// + /// POST request to the security.clear_api_key_cache API, read more about this API online: + /// + /// https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-clear-api-key-cache.html + /// + public Task ClearApiKeyCacheAsync(IClearApiKeyCacheRequest request, CancellationToken ct = default) => DoRequestAsync(request, request.RequestParameters, ct); + /// /// POST request to the security.clear_cached_privileges API, read more about this API online: /// /// https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-clear-privilege-cache.html diff --git a/src/Nest/Requests.Security.cs b/src/Nest/Requests.Security.cs index f8165668306..4121af7e0c1 100644 --- a/src/Nest/Requests.Security.cs +++ b/src/Nest/Requests.Security.cs @@ -86,6 +86,38 @@ public Refresh? Refresh } } + [InterfaceDataContract] + public partial interface IClearApiKeyCacheRequest : IRequest + { + [IgnoreDataMember] + Ids Ids + { + get; + } + } + + ///Request for ClearApiKeyCache https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-clear-api-key-cache.html + public partial class ClearApiKeyCacheRequest : PlainRequestBase, IClearApiKeyCacheRequest + { + protected IClearApiKeyCacheRequest Self => this; + internal override ApiUrls ApiUrls => ApiUrlsLookups.SecurityClearApiKeyCache; + ////_security/api_key/{ids}/_clear_cache + ///Optional, accepts null + public ClearApiKeyCacheRequest(Ids ids): base(r => r.Optional("ids", ids)) + { + } + + ////_security/api_key/*/_clear_cache + public ClearApiKeyCacheRequest(): base() + { + } + + // values part of the url path + [IgnoreDataMember] + Ids IClearApiKeyCacheRequest.Ids => Self.RouteValues.Get("ids"); + // Request parameters + } + [InterfaceDataContract] public partial interface IClearCachedPrivilegesRequest : IRequest { diff --git a/src/Nest/XPack/Security/ApiKey/ClearApiKeyCache/ClearApiKeyCacheRequest.cs b/src/Nest/XPack/Security/ApiKey/ClearApiKeyCache/ClearApiKeyCacheRequest.cs new file mode 100644 index 00000000000..9c73c214364 --- /dev/null +++ b/src/Nest/XPack/Security/ApiKey/ClearApiKeyCache/ClearApiKeyCacheRequest.cs @@ -0,0 +1,14 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information + +namespace Nest +{ + [MapsApi("security.clear_api_key_cache")] + [ReadAs(typeof(ClearApiKeyCacheRequest))] + public partial interface IClearApiKeyCacheRequest { } + + public partial class ClearApiKeyCacheRequest { } + + public partial class ClearApiKeyCacheDescriptor { } +} diff --git a/src/Nest/XPack/Security/ApiKey/ClearApiKeyCache/ClearApiKeyCacheResponse.cs b/src/Nest/XPack/Security/ApiKey/ClearApiKeyCache/ClearApiKeyCacheResponse.cs new file mode 100644 index 00000000000..2480500689f --- /dev/null +++ b/src/Nest/XPack/Security/ApiKey/ClearApiKeyCache/ClearApiKeyCacheResponse.cs @@ -0,0 +1,27 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information + +using System.Collections.Generic; +using System.Runtime.Serialization; +using Elasticsearch.Net; +using Elasticsearch.Net.Utf8Json; + +namespace Nest +{ + public class ClearApiKeyCacheResponse : NodesResponseBase + { + /// + /// The cluster name. + /// + [DataMember(Name = "cluster_name")] + public string ClusterName { get; internal set; } + + /// + /// A dictionary of container details of the nodes that cleared the cache. + /// + [DataMember(Name = "nodes")] + [JsonFormatter(typeof(VerbatimInterfaceReadOnlyDictionaryKeysFormatter))] + public IReadOnlyDictionary Nodes { get; internal set; } = EmptyReadOnly.Dictionary; + } +} diff --git a/src/Nest/_Generated/ApiUrlsLookup.generated.cs b/src/Nest/_Generated/ApiUrlsLookup.generated.cs index ab332e0cdec..2e1e20b6e3d 100644 --- a/src/Nest/_Generated/ApiUrlsLookup.generated.cs +++ b/src/Nest/_Generated/ApiUrlsLookup.generated.cs @@ -241,6 +241,7 @@ internal static class ApiUrlsLookups internal static ApiUrls NoNamespaceSearchTemplate = new ApiUrls(new[]{"_search/template", "{index}/_search/template"}); internal static ApiUrls SecurityAuthenticate = new ApiUrls(new[]{"_security/_authenticate"}); internal static ApiUrls SecurityChangePassword = new ApiUrls(new[]{"_security/user/{username}/_password", "_security/user/_password"}); + internal static ApiUrls SecurityClearApiKeyCache = new ApiUrls(new[]{"_security/api_key/{ids}/_clear_cache", "_security/api_key/*/_clear_cache"}); internal static ApiUrls SecurityClearCachedPrivileges = new ApiUrls(new[]{"_security/privilege/{application}/_clear_cache"}); internal static ApiUrls SecurityClearCachedRealms = new ApiUrls(new[]{"_security/realm/{realms}/_clear_cache"}); internal static ApiUrls SecurityClearCachedRoles = new ApiUrls(new[]{"_security/role/{name}/_clear_cache"}); diff --git a/tests/Tests/XPack/Security/ApiKey/SecurityApiKeyTests.cs b/tests/Tests/XPack/Security/ApiKey/SecurityApiKeyTests.cs index 22c26a977d5..97f788916b2 100644 --- a/tests/Tests/XPack/Security/ApiKey/SecurityApiKeyTests.cs +++ b/tests/Tests/XPack/Security/ApiKey/SecurityApiKeyTests.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information using System; +using System.Linq; using System.Threading.Tasks; using Elastic.Elasticsearch.Xunit.XunitPlumbing; using Elasticsearch.Net; @@ -23,6 +24,9 @@ public class SecurityApiKeyTests : CoordinatedIntegrationTestBase private const string GetApiKeyStep = nameof(GetApiKeyStep); private const string GetAllApiKeysStep = nameof(GetAllApiKeysStep); private const string InvalidateApiKeyStep = nameof(InvalidateApiKeyStep); + private const string GetAnotherApiKeyStep = nameof(GetAnotherApiKeyStep); + private const string ClearApiKeyCacheStep = nameof(ClearApiKeyCacheStep); + private const string ClearAllApiKeyCacheStep = nameof(ClearAllApiKeyCacheStep); public SecurityApiKeyTests(XPackCluster cluster, EndpointUsage usage) : base(new CoordinatedUsage(cluster, usage) { @@ -186,6 +190,52 @@ public SecurityApiKeyTests(XPackCluster cluster, EndpointUsage usage) : base(new (v, c, r) => c.Security.InvalidateApiKey(r), (v, c, r) => c.Security.InvalidateApiKeyAsync(r) ) + }, + { + GetAnotherApiKeyStep, u => + u.Calls( + v => new GetApiKeyRequest + { + Name = v, + RequestConfiguration = new RequestConfiguration + { + BasicAuthenticationCredentials = new BasicAuthenticationCredentials($"user-{v}", "password") + } + }, + (v, d) => d + .Name(v) + .RequestConfiguration(r => r.BasicAuthentication($"user-{v}", "password")) + , + (v, c, f) => c.Security.GetApiKey(f), + (v, c, f) => c.Security.GetApiKeyAsync(f), + (v, c, r) => c.Security.GetApiKey(r), + (v, c, r) => c.Security.GetApiKeyAsync(r), + (r, values) => values.ExtendedValue("apiKey", r.ApiKeys.FirstOrDefault()?.Id ?? string.Empty) + ) + }, + { + // API introduced in 7.10.0 + ClearApiKeyCacheStep, ">=7.10.0", u => + u.Calls( + v => new ClearApiKeyCacheRequest(u.Usage.CallUniqueValues.ExtendedValue("apiKey") ?? string.Empty), + (v, d) => d, + (v, c, f) => c.Security.ClearApiKeyCache(f => f.Ids(u.Usage.CallUniqueValues.ExtendedValue("apiKey"))), + (v, c, f) => c.Security.ClearApiKeyCacheAsync(f => f.Ids(u.Usage.CallUniqueValues.ExtendedValue("apiKey"))), + (v, c, r) => c.Security.ClearApiKeyCache(r), + (v, c, r) => c.Security.ClearApiKeyCacheAsync(r) + ) + }, + { + // API introduced in 7.10.0 + ClearAllApiKeyCacheStep, ">=7.10.0", u => + u.Calls( + v => new ClearApiKeyCacheRequest(), + (v, d) => d, + (v, c, f) => c.Security.ClearApiKeyCache(), + (v, c, f) => c.Security.ClearApiKeyCacheAsync(), + (v, c, r) => c.Security.ClearApiKeyCache(r), + (v, c, r) => c.Security.ClearApiKeyCacheAsync(r) + ) } }) { } @@ -227,5 +277,21 @@ [I] public async Task SecurityInvalidateApiKeyResponse() => await Assert await Assert(ClearApiKeyCacheStep, r => + { + r.IsValid.Should().BeTrue(); + r.NodeStatistics.Successful.Should().BeGreaterOrEqualTo(1); + r.ClusterName.Should().NotBeNullOrEmpty(); + r.Nodes.Count.Should().BeGreaterOrEqualTo(1); + }); + + [I] public async Task SecurityClearAllApiKeyCacheResponse() => await Assert(ClearAllApiKeyCacheStep, r => + { + r.IsValid.Should().BeTrue(); + r.NodeStatistics.Successful.Should().BeGreaterOrEqualTo(1); + r.ClusterName.Should().NotBeNullOrEmpty(); + r.Nodes.Count.Should().BeGreaterOrEqualTo(1); + }); } } diff --git a/tests/Tests/XPack/Security/ApiKey/SecurityApiKeyUrlTests.cs b/tests/Tests/XPack/Security/ApiKey/SecurityApiKeyUrlTests.cs index 73b2cdefa1a..2a012668dce 100644 --- a/tests/Tests/XPack/Security/ApiKey/SecurityApiKeyUrlTests.cs +++ b/tests/Tests/XPack/Security/ApiKey/SecurityApiKeyUrlTests.cs @@ -35,4 +35,22 @@ [U] public override async Task Urls() => await UrlTester.PUT("/_security/api_key .FluentAsync(c => c.Security.CreateApiKeyAsync(p => p)) .RequestAsync(c => c.Security.CreateApiKeyAsync(new CreateApiKeyRequest())); } + + public class SecurityClearApiKeyCacheUrlTests : UrlTestsBase + { + [U] public override async Task Urls() + { + await UrlTester.POST("/_security/api_key/id1%2Cid2/_clear_cache") + .Fluent(c => c.Security.ClearApiKeyCache(f => f.Ids("id1,id2"))) + .Request(c => c.Security.ClearApiKeyCache(new ClearApiKeyCacheRequest("id1,id2"))) + .FluentAsync(c => c.Security.ClearApiKeyCacheAsync(f => f.Ids("id1,id2"))) + .RequestAsync(c => c.Security.ClearApiKeyCacheAsync(new ClearApiKeyCacheRequest("id1,id2"))); + + await UrlTester.POST("/_security/api_key/*/_clear_cache") + .Fluent(c => c.Security.ClearApiKeyCache()) + .Request(c => c.Security.ClearApiKeyCache(new ClearApiKeyCacheRequest())) + .FluentAsync(c => c.Security.ClearApiKeyCacheAsync()) + .RequestAsync(c => c.Security.ClearApiKeyCacheAsync(new ClearApiKeyCacheRequest())); + } + } }