Skip to content

Commit

Permalink
Fix #3320 Add Painless script execute API (#3370)
Browse files Browse the repository at this point in the history
* Generate ExcecutePainlessScript methods/descriptors etc

* Implement ExecutePainlessScript API with tests

Omitting `context` for now since that only takes a single option now
which is also the default. Waiting for the API to crystalize here.

* make Result generic in preparation for 6.4
  • Loading branch information
Mpdreamz committed Sep 3, 2018
1 parent 6007a70 commit bcdc1d6
Show file tree
Hide file tree
Showing 12 changed files with 274 additions and 2 deletions.
2 changes: 0 additions & 2 deletions src/CodeGeneration/ApiGenerator/ApiGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,6 @@ public static void Generate(string downloadBranch, params string[] folders)
"xpack.sql.query.json",
"xpack.sql.translate.json",
"xpack.ssl.certificates.json",

"scripts_painless_execute.json",
};

private static RestApiSpec CreateRestApiSpecModel(string downloadBranch, string[] folders)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1753,6 +1753,11 @@ public partial class RenderSearchTemplateRequestParameters : RequestParameters<R
{
public override HttpMethod DefaultHttpMethod => HttpMethod.POST;
}
///<summary>Request options for ScriptsPainlessExecute<pre>https://www.elastic.co/guide/en/elasticsearch/painless/master/painless-execute-api.html</pre></summary>
public partial class ExecutePainlessScriptRequestParameters : RequestParameters<ExecutePainlessScriptRequestParameters>
{
public override HttpMethod DefaultHttpMethod => HttpMethod.POST;
}
///<summary>Request options for Scroll<pre>http://www.elastic.co/guide/en/elasticsearch/reference/master/search-request-scroll.html</pre></summary>
public partial class ScrollRequestParameters : RequestParameters<ScrollRequestParameters>
{
Expand Down
18 changes: 18 additions & 0 deletions src/Elasticsearch.Net/ElasticLowLevelClient.Generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2416,6 +2416,24 @@ public TResponse RenderSearchTemplate<TResponse>(string id, PostData body, Rende
///<param name="requestParameters">A func that allows you to describe the querystring parameters &amp; request specific connection settings.</param>
public Task<TResponse> RenderSearchTemplateAsync<TResponse>(string id, PostData body, RenderSearchTemplateRequestParameters requestParameters = null, CancellationToken ctx = default(CancellationToken))
where TResponse : class, IElasticsearchResponse, new() => this.DoRequestAsync<TResponse>(POST, Url($"_render/template/{id.NotNull("id")}"), ctx, body, _params(requestParameters));
///<summary>GET on /_scripts/painless/_execute <para>https://www.elastic.co/guide/en/elasticsearch/painless/master/painless-execute-api.html</para></summary>
///<param name="requestParameters">A func that allows you to describe the querystring parameters &amp; request specific connection settings.</param>
public TResponse ScriptsPainlessExecuteGet<TResponse>(ExecutePainlessScriptRequestParameters requestParameters = null)
where TResponse : class, IElasticsearchResponse, new() => this.DoRequest<TResponse>(GET, Url($"_scripts/painless/_execute"), null, _params(requestParameters));
///<summary>GET on /_scripts/painless/_execute <para>https://www.elastic.co/guide/en/elasticsearch/painless/master/painless-execute-api.html</para></summary>
///<param name="requestParameters">A func that allows you to describe the querystring parameters &amp; request specific connection settings.</param>
public Task<TResponse> ScriptsPainlessExecuteGetAsync<TResponse>(ExecutePainlessScriptRequestParameters requestParameters = null, CancellationToken ctx = default(CancellationToken))
where TResponse : class, IElasticsearchResponse, new() => this.DoRequestAsync<TResponse>(GET, Url($"_scripts/painless/_execute"), ctx, null, _params(requestParameters));
///<summary>POST on /_scripts/painless/_execute <para>https://www.elastic.co/guide/en/elasticsearch/painless/master/painless-execute-api.html</para></summary>
///<param name="body">The script to execute</param>
///<param name="requestParameters">A func that allows you to describe the querystring parameters &amp; request specific connection settings.</param>
public TResponse ScriptsPainlessExecute<TResponse>(PostData body, ExecutePainlessScriptRequestParameters requestParameters = null)
where TResponse : class, IElasticsearchResponse, new() => this.DoRequest<TResponse>(POST, Url($"_scripts/painless/_execute"), body, _params(requestParameters));
///<summary>POST on /_scripts/painless/_execute <para>https://www.elastic.co/guide/en/elasticsearch/painless/master/painless-execute-api.html</para></summary>
///<param name="body">The script to execute</param>
///<param name="requestParameters">A func that allows you to describe the querystring parameters &amp; request specific connection settings.</param>
public Task<TResponse> ScriptsPainlessExecuteAsync<TResponse>(PostData body, ExecutePainlessScriptRequestParameters requestParameters = null, CancellationToken ctx = default(CancellationToken))
where TResponse : class, IElasticsearchResponse, new() => this.DoRequestAsync<TResponse>(POST, Url($"_scripts/painless/_execute"), ctx, body, _params(requestParameters));
///<summary>GET on /_search/scroll <para>http://www.elastic.co/guide/en/elasticsearch/reference/master/search-request-scroll.html</para></summary>
///<param name="requestParameters">A func that allows you to describe the querystring parameters &amp; request specific connection settings.</param>
public TResponse ScrollGet<TResponse>(ScrollRequestParameters requestParameters = null)
Expand Down
14 changes: 14 additions & 0 deletions src/Elasticsearch.Net/IElasticLowLevelClient.Generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1956,6 +1956,20 @@ public partial interface IElasticLowLevelClient
///<param name="body">The search definition template and its params</param>
///<param name="requestParameters">A func that allows you to describe the querystring parameters &amp; request specific connection settings.</param>
Task<TResponse> RenderSearchTemplateAsync<TResponse>(string id, PostData body, RenderSearchTemplateRequestParameters requestParameters = null, CancellationToken ctx = default(CancellationToken)) where TResponse : class, IElasticsearchResponse, new();
///<summary>GET on /_scripts/painless/_execute <para>https://www.elastic.co/guide/en/elasticsearch/painless/master/painless-execute-api.html</para></summary>
///<param name="requestParameters">A func that allows you to describe the querystring parameters &amp; request specific connection settings.</param>
TResponse ScriptsPainlessExecuteGet<TResponse>(ExecutePainlessScriptRequestParameters requestParameters = null) where TResponse : class, IElasticsearchResponse, new();
///<summary>GET on /_scripts/painless/_execute <para>https://www.elastic.co/guide/en/elasticsearch/painless/master/painless-execute-api.html</para></summary>
///<param name="requestParameters">A func that allows you to describe the querystring parameters &amp; request specific connection settings.</param>
Task<TResponse> ScriptsPainlessExecuteGetAsync<TResponse>(ExecutePainlessScriptRequestParameters requestParameters = null, CancellationToken ctx = default(CancellationToken)) where TResponse : class, IElasticsearchResponse, new();
///<summary>POST on /_scripts/painless/_execute <para>https://www.elastic.co/guide/en/elasticsearch/painless/master/painless-execute-api.html</para></summary>
///<param name="body">The script to execute</param>
///<param name="requestParameters">A func that allows you to describe the querystring parameters &amp; request specific connection settings.</param>
TResponse ScriptsPainlessExecute<TResponse>(PostData body, ExecutePainlessScriptRequestParameters requestParameters = null) where TResponse : class, IElasticsearchResponse, new();
///<summary>POST on /_scripts/painless/_execute <para>https://www.elastic.co/guide/en/elasticsearch/painless/master/painless-execute-api.html</para></summary>
///<param name="body">The script to execute</param>
///<param name="requestParameters">A func that allows you to describe the querystring parameters &amp; request specific connection settings.</param>
Task<TResponse> ScriptsPainlessExecuteAsync<TResponse>(PostData body, ExecutePainlessScriptRequestParameters requestParameters = null, CancellationToken ctx = default(CancellationToken)) where TResponse : class, IElasticsearchResponse, new();
///<summary>GET on /_search/scroll <para>http://www.elastic.co/guide/en/elasticsearch/reference/master/search-request-scroll.html</para></summary>
///<param name="requestParameters">A func that allows you to describe the querystring parameters &amp; request specific connection settings.</param>
TResponse ScrollGet<TResponse>(ScrollRequestParameters requestParameters = null) where TResponse : class, IElasticsearchResponse, new();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System;
using System.Threading.Tasks;
using Elasticsearch.Net;
using System.Threading;

namespace Nest
{
public partial interface IElasticClient
{
/// <summary>
/// Executes an arbitrary Painless script and returns a result.
/// Useful for testing the syntactical correctness of Painless scripts
/// </summary>
IExecutePainlessScriptResponse<TResult> ExecutePainlessScript<TResult>(Func<ExecutePainlessScriptDescriptor, IExecutePainlessScriptRequest> selector);

/// <inheritdoc cref="ExecutePainlessScript{TResult}(System.Func{Nest.ExecutePainlessScriptDescriptor,Nest.IExecutePainlessScriptRequest})"/>
IExecutePainlessScriptResponse<TResult> ExecutePainlessScript<TResult>(IExecutePainlessScriptRequest request);

/// <inheritdoc cref="ExecutePainlessScript{TResult}(System.Func{Nest.ExecutePainlessScriptDescriptor,Nest.IExecutePainlessScriptRequest})"/>
Task<IExecutePainlessScriptResponse<TResult>> ExecutePainlessScriptAsync<TResult>(Func<ExecutePainlessScriptDescriptor, IExecutePainlessScriptRequest> selector,
CancellationToken cancellationToken = default(CancellationToken));

/// <inheritdoc cref="ExecutePainlessScript{TResult}(System.Func{Nest.ExecutePainlessScriptDescriptor,Nest.IExecutePainlessScriptRequest})"/>
Task<IExecutePainlessScriptResponse<TResult>> ExecutePainlessScriptAsync<TResult>(IExecutePainlessScriptRequest request, CancellationToken cancellationToken = default(CancellationToken));

}

public partial class ElasticClient
{
/// <inheritdoc />
public IExecutePainlessScriptResponse<TResult> ExecutePainlessScript<TResult>(Func<ExecutePainlessScriptDescriptor, IExecutePainlessScriptRequest> selector) =>
this.ExecutePainlessScript<TResult>(selector?.Invoke(new ExecutePainlessScriptDescriptor()));

/// <inheritdoc />
public IExecutePainlessScriptResponse<TResult> ExecutePainlessScript<TResult>(IExecutePainlessScriptRequest request) =>
this.Dispatcher.Dispatch<IExecutePainlessScriptRequest, ExecutePainlessScriptRequestParameters, ExecutePainlessScriptResponse<TResult>>(
request,
this.LowLevelDispatch.ScriptsPainlessExecuteDispatch<ExecutePainlessScriptResponse<TResult>>
);

/// <inheritdoc />
public Task<IExecutePainlessScriptResponse<TResult>> ExecutePainlessScriptAsync<TResult>(Func<ExecutePainlessScriptDescriptor, IExecutePainlessScriptRequest> selector,
CancellationToken cancellationToken = default(CancellationToken)) =>
this.ExecutePainlessScriptAsync<TResult>(selector?.Invoke(new ExecutePainlessScriptDescriptor()), cancellationToken);

/// <inheritdoc />
public Task<IExecutePainlessScriptResponse<TResult>> ExecutePainlessScriptAsync<TResult>(IExecutePainlessScriptRequest request, CancellationToken cancellationToken = default(CancellationToken)) =>
this.Dispatcher.DispatchAsync<IExecutePainlessScriptRequest, ExecutePainlessScriptRequestParameters, ExecutePainlessScriptResponse<TResult>, IExecutePainlessScriptResponse<TResult>>(
request,
cancellationToken,
this.LowLevelDispatch.ScriptsPainlessExecuteDispatchAsync<ExecutePainlessScriptResponse<TResult>>
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using Newtonsoft.Json;

namespace Nest
{
public partial interface IExecutePainlessScriptRequest
{
[JsonProperty("script")]
IInlineScript Script { get; set; }
}

public partial class ExecutePainlessScriptRequest
{
public IInlineScript Script { get; set; }
}

[DescriptorFor("ScriptsPainlessExecute")]
public partial class ExecutePainlessScriptDescriptor
{
IInlineScript IExecutePainlessScriptRequest.Script { get; set; }

public ExecutePainlessScriptDescriptor Script(Func<InlineScriptDescriptor, IInlineScript> selector) =>
Assign(a => a.Script = selector?.Invoke(new InlineScriptDescriptor()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Newtonsoft.Json;

namespace Nest
{
public interface IExecutePainlessScriptResponse<TResult> : IResponse
{
[JsonProperty("result")]
TResult Result { get; }
}

public class ExecutePainlessScriptResponse<TResult> : ResponseBase, IExecutePainlessScriptResponse<TResult>
{
public TResult Result { get; set; }
}
}
8 changes: 8 additions & 0 deletions src/Nest/_Generated/_Descriptors.generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3126,6 +3126,14 @@ public RenderSearchTemplateDescriptor() : base(){}

// Request parameters

}
///<summary>descriptor for ScriptsPainlessExecute <pre>https://www.elastic.co/guide/en/elasticsearch/painless/master/painless-execute-api.html</pre></summary>
public partial class ExecutePainlessScriptDescriptor : RequestDescriptorBase<ExecutePainlessScriptDescriptor,ExecutePainlessScriptRequestParameters, IExecutePainlessScriptRequest>, IExecutePainlessScriptRequest
{
// values part of the url path

// Request parameters

}
///<summary>descriptor for Scroll <pre>http://www.elastic.co/guide/en/elasticsearch/reference/master/search-request-scroll.html</pre></summary>
public partial class ScrollDescriptor<T> : RequestDescriptorBase<ScrollDescriptor<T>,ScrollRequestParameters, IScrollRequest>, IScrollRequest
Expand Down
24 changes: 24 additions & 0 deletions src/Nest/_Generated/_LowLevelDispatch.generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2408,6 +2408,30 @@ internal partial class LowLevelDispatch
throw InvalidDispatch("RenderSearchTemplate", p, new [] { GET, POST }, "/_render/template", "/_render/template/{id}");
}

internal TResponse ScriptsPainlessExecuteDispatch<TResponse>(IRequest<ExecutePainlessScriptRequestParameters> p,SerializableData<IExecutePainlessScriptRequest> body) where TResponse : class, IElasticsearchResponse, new()
{
switch(p.HttpMethod)
{
case GET:
return _lowLevel.ScriptsPainlessExecuteGet<TResponse>(p.RequestParameters);
case POST:
return _lowLevel.ScriptsPainlessExecute<TResponse>(body,p.RequestParameters);
}
throw InvalidDispatch("ScriptsPainlessExecute", p, new [] { GET, POST }, "/_scripts/painless/_execute");
}

internal Task<TResponse> ScriptsPainlessExecuteDispatchAsync<TResponse>(IRequest<ExecutePainlessScriptRequestParameters> p,SerializableData<IExecutePainlessScriptRequest> body, CancellationToken ct) where TResponse : class, IElasticsearchResponse, new()
{
switch(p.HttpMethod)
{
case GET:
return _lowLevel.ScriptsPainlessExecuteGetAsync<TResponse>(p.RequestParameters,ct);
case POST:
return _lowLevel.ScriptsPainlessExecuteAsync<TResponse>(body,p.RequestParameters,ct);
}
throw InvalidDispatch("ScriptsPainlessExecute", p, new [] { GET, POST }, "/_scripts/painless/_execute");
}

internal TResponse ScrollDispatch<TResponse>(IRequest<ScrollRequestParameters> p,SerializableData<IScrollRequest> body) where TResponse : class, IElasticsearchResponse, new()
{
switch(p.HttpMethod)
Expand Down
12 changes: 12 additions & 0 deletions src/Nest/_Generated/_Requests.generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2247,6 +2247,18 @@ public EnableUserRequest(Name username) : base(r=>r.Optional("username", usernam
public Refresh? Refresh { get => Q<Refresh?>("refresh"); set => Q("refresh", value); }
}
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public partial interface IExecutePainlessScriptRequest : IRequest<ExecutePainlessScriptRequestParameters>
{
}
///<summary>Request parameters for ScriptsPainlessExecute <pre>https://www.elastic.co/guide/en/elasticsearch/painless/master/painless-execute-api.html</pre></summary>
public partial class ExecutePainlessScriptRequest : PlainRequestBase<ExecutePainlessScriptRequestParameters>, IExecutePainlessScriptRequest
{
protected IExecutePainlessScriptRequest Self => this;
// values part of the url path

// Request parameters
}
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public partial interface IExecuteWatchRequest : IRequest<ExecuteWatchRequestParameters>
{
Id Id { get; }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Elastic.Xunit.XunitPlumbing;
using Elasticsearch.Net;
using FluentAssertions;
using Nest;
using Tests.Core.Extensions;
using Tests.Core.ManagedElasticsearch.Clusters;
using Tests.Framework;
using Tests.Framework.Integration;
using Tests.Framework.ManagedElasticsearch.Clusters;
using Xunit;

namespace Tests.Modules.Scripting.ExecutePainlessScript
{
[SkipVersion("<6.3.0", "this API was introduced in 6.3.0")]
public class ExecutePainlessScriptApiTests
: ApiIntegrationTestBase<ReadOnlyCluster, IExecutePainlessScriptResponse<string>, IExecutePainlessScriptRequest, ExecutePainlessScriptDescriptor, ExecutePainlessScriptRequest>
{
public ExecutePainlessScriptApiTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(cluster, usage) { }

private static readonly string _painlessScript = "params.count / params.total";

protected override LazyResponses ClientUsage() => Calls(
fluent: (client, f) => client.ExecutePainlessScript<string>(f),
fluentAsync: (client, f) => client.ExecutePainlessScriptAsync<string>(f),
request: (client, r) => client.ExecutePainlessScript<string>(r),
requestAsync: (client, r) => client.ExecutePainlessScriptAsync<string>(r)
);

protected override HttpMethod HttpMethod => HttpMethod.POST;
protected override string UrlPath => "/_scripts/painless/_execute";
protected override int ExpectStatusCode => 200;
protected override bool ExpectIsValid => true;

protected override bool SupportsDeserialization => false;

protected override object ExpectJson => new
{
script = new
{
source = _painlessScript,
@params = new { count = 100.0, total = 1000.0 }
},
};

protected override Func<ExecutePainlessScriptDescriptor, IExecutePainlessScriptRequest> Fluent => d => d
.Script(s=>s
.Source(_painlessScript)
.Params(p => p.Add("count", 100.0).Add("total", 1000.0))
);

protected override ExecutePainlessScriptRequest Initializer => new ExecutePainlessScriptRequest
{
Script = new InlineScript(_painlessScript)
{
Params = new Dictionary<string, object>
{
{ "count", 100.0 },
{ "total", 1000.0 },
}
}
};

protected override void ExpectResponse(IExecutePainlessScriptResponse<string> response)
{
response.ShouldBeValid();
response.Result.Should().NotBeNullOrWhiteSpace();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Threading.Tasks;
using Elastic.Xunit.XunitPlumbing;
using Nest;
using Tests.Framework;
using static Tests.Framework.UrlTester;

namespace Tests.Modules.Scripting.ExecutePainlessScript
{
public class ExecutePainlessScriptUrlTests
{
[U] public async Task Urls()
{
var painless = "1 + 1";
var request = new ExecutePainlessScriptRequest
{
Script = new InlineScript(painless)
};

await POST("/_scripts/painless/_execute")
.Fluent(c => c.ExecutePainlessScript<string>(f => f.Script(s => s.Source(painless))))
.Request(c => c.ExecutePainlessScript<string>(request))
.FluentAsync(c => c.ExecutePainlessScriptAsync<string>(f => f.Script(s => s.Source(painless))))
.RequestAsync(c => c.ExecutePainlessScriptAsync<string>(request))
;
}
}
}

0 comments on commit bcdc1d6

Please sign in to comment.