From a308b6f6f65c768a4b6cae8dfd04f9bd12495c92 Mon Sep 17 00:00:00 2001 From: Steve Gordon Date: Wed, 5 May 2021 12:14:04 +0100 Subject: [PATCH] Add NEST support for Query Watcher API (#5680) * Add request and response * Update request * Generate code * Update request properties * Update request and response * Add tests --- src/Nest/Descriptors.Watcher.cs | 8 ++ src/Nest/ElasticClient.Watcher.cs | 24 ++++ src/Nest/Requests.Watcher.cs | 14 +++ .../Watcher/Query/QueryWatchesRequest.cs | 98 +++++++++++++++++ .../Watcher/Query/QueryWatchesResponse.cs | 37 +++++++ .../_Generated/ApiUrlsLookup.generated.cs | 1 + .../QueryWatches/QueryWatchesApiTests.cs | 104 ++++++++++++++++++ .../QueryWatches/QueryWatchesUrlTests.cs | 21 ++++ 8 files changed, 307 insertions(+) create mode 100644 src/Nest/XPack/Watcher/Query/QueryWatchesRequest.cs create mode 100644 src/Nest/XPack/Watcher/Query/QueryWatchesResponse.cs create mode 100644 tests/Tests/XPack/Watcher/QueryWatches/QueryWatchesApiTests.cs create mode 100644 tests/Tests/XPack/Watcher/QueryWatches/QueryWatchesUrlTests.cs diff --git a/src/Nest/Descriptors.Watcher.cs b/src/Nest/Descriptors.Watcher.cs index 821fa7fea96..d65f77f6e54 100644 --- a/src/Nest/Descriptors.Watcher.cs +++ b/src/Nest/Descriptors.Watcher.cs @@ -201,6 +201,14 @@ protected PutWatchDescriptor(): base() public PutWatchDescriptor Version(long? version) => Qs("version", version); } + ///Descriptor for QueryWatches https://www.elastic.co/guide/en/elasticsearch/reference/current/watcher-api-query-watches.html + public partial class QueryWatchesDescriptor : RequestDescriptorBase, IQueryWatchesRequest + { + internal override ApiUrls ApiUrls => ApiUrlsLookups.WatcherQueryWatches; + // values part of the url path + // Request parameters + } + ///Descriptor for Start https://www.elastic.co/guide/en/elasticsearch/reference/current/watcher-api-start.html public partial class StartWatcherDescriptor : RequestDescriptorBase, IStartWatcherRequest { diff --git a/src/Nest/ElasticClient.Watcher.cs b/src/Nest/ElasticClient.Watcher.cs index 5014570bffb..fe9f5b1b0a4 100644 --- a/src/Nest/ElasticClient.Watcher.cs +++ b/src/Nest/ElasticClient.Watcher.cs @@ -208,6 +208,30 @@ internal WatcherNamespace(ElasticClient client): base(client) /// public Task PutAsync(IPutWatchRequest request, CancellationToken ct = default) => DoRequestAsync(request, request.RequestParameters, ct); /// + /// POST request to the watcher.query_watches API, read more about this API online: + /// + /// https://www.elastic.co/guide/en/elasticsearch/reference/current/watcher-api-query-watches.html + /// + public QueryWatchesResponse QueryWatches(Func selector = null) => QueryWatches(selector.InvokeOrDefault(new QueryWatchesDescriptor())); + /// + /// POST request to the watcher.query_watches API, read more about this API online: + /// + /// https://www.elastic.co/guide/en/elasticsearch/reference/current/watcher-api-query-watches.html + /// + public Task QueryWatchesAsync(Func selector = null, CancellationToken ct = default) => QueryWatchesAsync(selector.InvokeOrDefault(new QueryWatchesDescriptor()), ct); + /// + /// POST request to the watcher.query_watches API, read more about this API online: + /// + /// https://www.elastic.co/guide/en/elasticsearch/reference/current/watcher-api-query-watches.html + /// + public QueryWatchesResponse QueryWatches(IQueryWatchesRequest request) => DoRequest(request, request.RequestParameters); + /// + /// POST request to the watcher.query_watches API, read more about this API online: + /// + /// https://www.elastic.co/guide/en/elasticsearch/reference/current/watcher-api-query-watches.html + /// + public Task QueryWatchesAsync(IQueryWatchesRequest request, CancellationToken ct = default) => DoRequestAsync(request, request.RequestParameters, ct); + /// /// POST request to the watcher.start API, read more about this API online: /// /// https://www.elastic.co/guide/en/elasticsearch/reference/current/watcher-api-start.html diff --git a/src/Nest/Requests.Watcher.cs b/src/Nest/Requests.Watcher.cs index 97bef240caa..9c94b8f1da7 100644 --- a/src/Nest/Requests.Watcher.cs +++ b/src/Nest/Requests.Watcher.cs @@ -312,6 +312,20 @@ public long? Version } } + [InterfaceDataContract] + public partial interface IQueryWatchesRequest : IRequest + { + } + + ///Request for QueryWatches https://www.elastic.co/guide/en/elasticsearch/reference/current/watcher-api-query-watches.html + public partial class QueryWatchesRequest : PlainRequestBase, IQueryWatchesRequest + { + protected IQueryWatchesRequest Self => this; + internal override ApiUrls ApiUrls => ApiUrlsLookups.WatcherQueryWatches; + // values part of the url path + // Request parameters + } + [InterfaceDataContract] public partial interface IStartWatcherRequest : IRequest { diff --git a/src/Nest/XPack/Watcher/Query/QueryWatchesRequest.cs b/src/Nest/XPack/Watcher/Query/QueryWatchesRequest.cs new file mode 100644 index 00000000000..edf4d33f15b --- /dev/null +++ b/src/Nest/XPack/Watcher/Query/QueryWatchesRequest.cs @@ -0,0 +1,98 @@ +// 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; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace Nest +{ + [MapsApi("watcher.query_watches.json")] + [ReadAs(typeof(QueryWatchesRequest))] + public partial interface IQueryWatchesRequest + { + /// + /// The offset from the first result to fetch. Needs to be non-negative. + /// + [DataMember(Name = "from")] + int? From { get; set; } + + /// + /// Optional, query filter watches to be returned. + /// + [DataMember(Name = "query")] + QueryContainer Query { get; set; } + + /// + /// Sort values that can be used to start returning results "after" any document in the result list. + /// + [DataMember(Name = "search_after")] + IList SearchAfter { get; set; } + + /// + /// The number of hits to return. Defaults to 10. + /// + [DataMember(Name = "size")] + int? Size { get; set; } + + /// + /// Specifies how to sort the search hits. + /// + [DataMember(Name = "sort")] + IList Sort { get; set; } + } + + /// + public partial class QueryWatchesRequest + { + /// + public int? From { get; set; } + + /// + public QueryContainer Query { get; set; } + + /// + public IList SearchAfter { get; set; } + + /// + public int? Size { get; set; } + + /// + public IList Sort { get; set; } + } + + /// + public partial class QueryWatchesDescriptor + { + int? IQueryWatchesRequest.From { get; set; } + QueryContainer IQueryWatchesRequest.Query { get; set; } + IList IQueryWatchesRequest.SearchAfter { get; set; } + int? IQueryWatchesRequest.Size { get; set; } + IList IQueryWatchesRequest.Sort { get; set; } + + /// + public QueryWatchesDescriptor From(int? from) => Assign(from, (a, v) => a.From = v); + + /// + public QueryWatchesDescriptor Query(Func, QueryContainer> query) => + Assign(query, (a, v) => a.Query = v?.Invoke(new QueryContainerDescriptor())); + + /// + public QueryWatchesDescriptor SearchAfter(IEnumerable searchAfter) => + Assign(searchAfter, (a, v) => a.SearchAfter = v?.ToListOrNullIfEmpty()); + + /// + public QueryWatchesDescriptor SearchAfter(IList searchAfter) => Assign(searchAfter, (a, v) => a.SearchAfter = v); + + /// + public QueryWatchesDescriptor SearchAfter(params object[] searchAfter) => Assign(searchAfter, (a, v) => a.SearchAfter = v); + + /// + public QueryWatchesDescriptor Size(int? size) => Assign(size, (a, v) => a.Size = v); + + /// + public QueryWatchesDescriptor Sort(Func, IPromise>> selector) => + Assign(selector, (a, v) => a.Sort = v?.Invoke(new SortDescriptor())?.Value); + } +} diff --git a/src/Nest/XPack/Watcher/Query/QueryWatchesResponse.cs b/src/Nest/XPack/Watcher/Query/QueryWatchesResponse.cs new file mode 100644 index 00000000000..4e5e9da24e1 --- /dev/null +++ b/src/Nest/XPack/Watcher/Query/QueryWatchesResponse.cs @@ -0,0 +1,37 @@ +// 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; + +namespace Nest +{ + public class QueryWatchesResponse : ResponseBase + { + [DataMember(Name = "count")] + public int Count { get; internal set; } + + [DataMember(Name = "watches")] + public IReadOnlyCollection Watches { get; internal set; } + } + + [DataContract] + public class WatchQueryResult + { + [DataMember(Name = "_id")] + public string Id { get; set; } + + [DataMember(Name = "_primary_term")] + public int PrimaryTerm { get; set; } + + [DataMember(Name = "_seq_no")] + public int SequenceNumber { get; set; } + + [DataMember(Name = "status")] + public WatchStatus Status { get; set; } + + [DataMember(Name = "watch")] + public IWatch Watch { get; set; } + } +} diff --git a/src/Nest/_Generated/ApiUrlsLookup.generated.cs b/src/Nest/_Generated/ApiUrlsLookup.generated.cs index 46cb66dfbb0..3c4b3ba639c 100644 --- a/src/Nest/_Generated/ApiUrlsLookup.generated.cs +++ b/src/Nest/_Generated/ApiUrlsLookup.generated.cs @@ -324,6 +324,7 @@ internal static class ApiUrlsLookups internal static ApiUrls WatcherExecute = new ApiUrls(new[]{"_watcher/watch/{id}/_execute", "_watcher/watch/_execute"}); internal static ApiUrls WatcherGet = new ApiUrls(new[]{"_watcher/watch/{id}"}); internal static ApiUrls WatcherPut = new ApiUrls(new[]{"_watcher/watch/{id}"}); + internal static ApiUrls WatcherQueryWatches = new ApiUrls(new[]{"_watcher/_query/watches"}); internal static ApiUrls WatcherStart = new ApiUrls(new[]{"_watcher/_start"}); internal static ApiUrls WatcherStats = new ApiUrls(new[]{"_watcher/stats", "_watcher/stats/{metric}"}); internal static ApiUrls WatcherStop = new ApiUrls(new[]{"_watcher/_stop"}); diff --git a/tests/Tests/XPack/Watcher/QueryWatches/QueryWatchesApiTests.cs b/tests/Tests/XPack/Watcher/QueryWatches/QueryWatchesApiTests.cs new file mode 100644 index 00000000000..ddc1d2e76b4 --- /dev/null +++ b/tests/Tests/XPack/Watcher/QueryWatches/QueryWatchesApiTests.cs @@ -0,0 +1,104 @@ +// 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; +using System.Collections.Generic; +using Elasticsearch.Net; +using FluentAssertions; +using Nest; +using Tests.Framework.EndpointTests; +using Tests.Framework.EndpointTests.TestState; + +namespace Tests.XPack.Watcher.QueryWatches +{ + public class QueryWatchesApiTests + : ApiIntegrationTestBase + { + private readonly Dictionary _termCondition = new() { { "metadata.name", new { value = "value" } } }; + + public QueryWatchesApiTests(WatcherCluster cluster, EndpointUsage usage) : base(cluster, usage) { } + + protected override bool ExpectIsValid => true; + + protected override object ExpectJson => new + { + size = 5, from = 0, query = new { term = _termCondition }, sort = new[] { new { _id = new { order = "asc" } } } + }; + + protected override int ExpectStatusCode => 200; + + protected override Func Fluent => p => p + .From(0) + .Size(5) + .Sort(s => s.Ascending("_id")) + .Query(q => q.Term(t => t.Field("metadata.name").Value("value"))); + + protected override HttpMethod HttpMethod => HttpMethod.POST; + + protected override QueryWatchesRequest Initializer => new() + { + From = 0, + Size = 5, + Sort = new List { new FieldSort { Field = "_id", Order = SortOrder.Ascending } }, + Query = new TermQuery { Field = "metadata.name", Value = "value" } + }; + + protected override string UrlPath => "/_watcher/_query/watches"; + + protected override void IntegrationSetup(IElasticClient client, CallUniqueValues values) + { + foreach (var callUniqueValue in values) + { + var putWatchResponse = client.Watcher.Put(callUniqueValue.Value, p => p + .Input(i => i + .Simple(s => s + .Add("key", "value") + ) + ) + .Trigger(t => t + .Schedule(s => s + .Cron("0 5 9 * * ?") + ) + ) + .Actions(a => a + .Email("reminder_email", e => e + .To("me@example.com") + .Subject("Something's strange in the neighbourhood") + .Body(b => b + .Text("Who you gonna call?") + ) + ) + ) + .Metadata(m => m.Add("name", "value")) + ); + + if (!putWatchResponse.IsValid) + throw new Exception("Problem setting up integration test"); + } + } + + protected override LazyResponses ClientUsage() => Calls( + (client, f) => client.Watcher.QueryWatches(f), + (client, f) => client.Watcher.QueryWatchesAsync(f), + (client, r) => client.Watcher.QueryWatches(r), + (client, r) => client.Watcher.QueryWatchesAsync(r) + ); + + protected override void ExpectResponse(QueryWatchesResponse response) + { + response.IsValid.Should().BeTrue(); + response.Count.Should().Be(4); + response.Watches.Count.Should().Be(4); + + foreach (var watchResult in response.Watches) + { + watchResult.Id.Should().NotBeNullOrEmpty(); + watchResult.SequenceNumber.Should().BeGreaterOrEqualTo(0); + watchResult.PrimaryTerm.Should().Be(1); + watchResult.Status.State.Active.Should().BeTrue(); + watchResult.Watch.Metadata["name"].Should().Be("value"); + } + } + } +} diff --git a/tests/Tests/XPack/Watcher/QueryWatches/QueryWatchesUrlTests.cs b/tests/Tests/XPack/Watcher/QueryWatches/QueryWatchesUrlTests.cs new file mode 100644 index 00000000000..53260c74bd5 --- /dev/null +++ b/tests/Tests/XPack/Watcher/QueryWatches/QueryWatchesUrlTests.cs @@ -0,0 +1,21 @@ +// 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.Threading.Tasks; +using Elastic.Elasticsearch.Xunit.XunitPlumbing; +using Nest; +using Tests.Framework.EndpointTests; +using static Tests.Framework.EndpointTests.UrlTester; + +namespace Tests.XPack.Watcher.QueryWatches +{ + public class QueryWatchesUrlTests : UrlTestsBase + { + [U] public override async Task Urls() => await POST("/_watcher/_query/watches") + .Fluent(c => c.Watcher.QueryWatches()) + .Request(c => c.Watcher.QueryWatches(new QueryWatchesRequest())) + .FluentAsync(c => c.Watcher.QueryWatchesAsync()) + .RequestAsync(c => c.Watcher.QueryWatchesAsync(new QueryWatchesRequest())); + } +}