From 02ed80f7d1c2bba75617398b0ef92da6eaf16770 Mon Sep 17 00:00:00 2001 From: "S.Y. Wang" Date: Sun, 9 Sep 2018 13:47:47 +0200 Subject: [PATCH] HLRC: Add put stored script support to high-level rest client (#31323) Relates to #27205 (cherry picked from commit 9073dbefd6d5a1441ac11e7990e181fa74f3ce15) --- .../client/RequestConverters.java | 34 +++-- .../client/RestHighLevelClient.java | 30 ++++ .../client/RequestConvertersTests.java | 38 ++++- .../client/RestHighLevelClientTests.java | 1 - .../elasticsearch/client/StoredScriptsIT.java | 58 ++++---- .../StoredScriptsDocumentationIT.java | 139 ++++++++++++++++-- .../high-level/script/put_script.asciidoc | 106 +++++++++++++ .../high-level/supported-apis.asciidoc | 2 + .../storedscripts/PutStoredScriptRequest.java | 12 +- .../PutStoredScriptRequestTests.java | 29 ++++ 10 files changed, 396 insertions(+), 53 deletions(-) create mode 100644 docs/java-rest/high-level/script/put_script.asciidoc diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java index 05f9be4203a0c..3b8da504cfb7f 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java @@ -43,6 +43,7 @@ import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusRequest; import org.elasticsearch.action.admin.cluster.storedscripts.DeleteStoredScriptRequest; import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptRequest; +import org.elasticsearch.action.admin.cluster.storedscripts.PutStoredScriptRequest; import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest; import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest; import org.elasticsearch.action.admin.indices.analyze.AnalyzeRequest; @@ -1072,16 +1073,6 @@ static Request putTemplate(PutIndexTemplateRequest putIndexTemplateRequest) thro return request; } - static Request getTemplates(GetIndexTemplatesRequest getIndexTemplatesRequest) throws IOException { - String[] names = getIndexTemplatesRequest.names(); - String endpoint = new EndpointBuilder().addPathPartAsIs("_template").addCommaSeparatedPathParts(names).build(); - Request request = new Request(HttpGet.METHOD_NAME, endpoint); - Params params = new Params(request); - params.withLocal(getIndexTemplatesRequest.local()); - params.withMasterTimeout(getIndexTemplatesRequest.masterNodeTimeout()); - return request; - } - static Request validateQuery(ValidateQueryRequest validateQueryRequest) throws IOException { String[] indices = validateQueryRequest.indices() == null ? Strings.EMPTY_ARRAY : validateQueryRequest.indices(); String[] types = validateQueryRequest.types() == null || indices.length <= 0 ? Strings.EMPTY_ARRAY : validateQueryRequest.types(); @@ -1121,6 +1112,29 @@ static Request getAlias(GetAliasesRequest getAliasesRequest) { return request; } + static Request getTemplates(GetIndexTemplatesRequest getIndexTemplatesRequest) throws IOException { + String[] names = getIndexTemplatesRequest.names(); + String endpoint = new EndpointBuilder().addPathPartAsIs("_template").addCommaSeparatedPathParts(names).build(); + Request request = new Request(HttpGet.METHOD_NAME, endpoint); + Params params = new Params(request); + params.withLocal(getIndexTemplatesRequest.local()); + params.withMasterTimeout(getIndexTemplatesRequest.masterNodeTimeout()); + return request; + } + + static Request putScript(PutStoredScriptRequest putStoredScriptRequest) throws IOException { + String endpoint = new EndpointBuilder().addPathPartAsIs("_scripts").addPathPart(putStoredScriptRequest.id()).build(); + Request request = new Request(HttpPost.METHOD_NAME, endpoint); + Params params = new Params(request); + params.withTimeout(putStoredScriptRequest.timeout()); + params.withMasterTimeout(putStoredScriptRequest.masterNodeTimeout()); + if (Strings.hasText(putStoredScriptRequest.context())) { + params.putParam("context", putStoredScriptRequest.context()); + } + request.setEntity(createEntity(putStoredScriptRequest, REQUEST_BODY_CONTENT_TYPE)); + return request; + } + static Request analyze(AnalyzeRequest request) throws IOException { EndpointBuilder builder = new EndpointBuilder(); String index = request.index(); diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java index 4e6e3462f5ce5..31d42b0ce28d8 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java @@ -29,6 +29,7 @@ import org.elasticsearch.action.admin.cluster.storedscripts.DeleteStoredScriptRequest; import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptRequest; import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptResponse; +import org.elasticsearch.action.admin.cluster.storedscripts.PutStoredScriptRequest; import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.action.bulk.BulkResponse; import org.elasticsearch.action.delete.DeleteRequest; @@ -1358,6 +1359,35 @@ public void deleteScriptAsync(DeleteStoredScriptRequest request, RequestOptions AcknowledgedResponse::fromXContent, listener, emptySet()); } + /** + * Puts an stored script using the Scripting API. + * See Scripting API + * on elastic.co + * @param putStoredScriptRequest the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @return the response + * @throws IOException in case there is a problem sending the request or parsing back the response + */ + public AcknowledgedResponse putScript(PutStoredScriptRequest putStoredScriptRequest, + RequestOptions options) throws IOException { + return performRequestAndParseEntity(putStoredScriptRequest, RequestConverters::putScript, options, + AcknowledgedResponse::fromXContent, emptySet()); + } + + /** + * Asynchronously puts an stored script using the Scripting API. + * See Scripting API + * on elastic.co + * @param putStoredScriptRequest the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @param listener the listener to be notified upon request completion + */ + public void putScriptAsync(PutStoredScriptRequest putStoredScriptRequest, RequestOptions options, + ActionListener listener) { + performRequestAsyncAndParseEntity(putStoredScriptRequest, RequestConverters::putScript, options, + AcknowledgedResponse::fromXContent, listener, emptySet()); + } + /** * Asynchronously executes a request using the Field Capabilities API. * See Field Capabilities API diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java index d5e05b4c320e2..f6937d494649a 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java @@ -42,7 +42,7 @@ import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusRequest; import org.elasticsearch.action.admin.cluster.storedscripts.DeleteStoredScriptRequest; import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptRequest; -import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusRequest; +import org.elasticsearch.action.admin.cluster.storedscripts.PutStoredScriptRequest; import org.elasticsearch.action.admin.indices.alias.Alias; import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest; import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions; @@ -2430,6 +2430,42 @@ public void testGetTemplateRequest() throws Exception { assertThat(request.getEntity(), nullValue()); } + public void testPutScript() throws Exception { + PutStoredScriptRequest putStoredScriptRequest = new PutStoredScriptRequest(); + + String id = randomAlphaOfLengthBetween(5, 10); + putStoredScriptRequest.id(id); + + XContentType xContentType = randomFrom(XContentType.values()); + try (XContentBuilder builder = XContentBuilder.builder(xContentType.xContent())) { + builder.startObject(); + builder.startObject("script") + .field("lang", "painless") + .field("source", "Math.log(_score * 2) + params.multiplier") + .endObject(); + builder.endObject(); + + putStoredScriptRequest.content(BytesReference.bytes(builder), xContentType); + } + + Map expectedParams = new HashMap<>(); + setRandomMasterTimeout(putStoredScriptRequest, expectedParams); + setRandomTimeout(putStoredScriptRequest::timeout, AcknowledgedRequest.DEFAULT_ACK_TIMEOUT, expectedParams); + + if (randomBoolean()) { + String context = randomAlphaOfLengthBetween(5, 10); + putStoredScriptRequest.context(context); + expectedParams.put("context", context); + } + + Request request = RequestConverters.putScript(putStoredScriptRequest); + + assertThat(request.getEndpoint(), equalTo("/_scripts/" + id)); + assertThat(request.getParameters(), equalTo(expectedParams)); + assertNotNull(request.getEntity()); + assertToXContentBody(putStoredScriptRequest, request.getEntity()); + } + public void testAnalyzeRequest() throws Exception { AnalyzeRequest indexAnalyzeRequest = new AnalyzeRequest() .text("Here is some text") diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java index c957886298240..e5778bc8f23f1 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java @@ -676,7 +676,6 @@ public void testApiNamingConventions() throws Exception { "indices.get_upgrade", "indices.put_alias", "mtermvectors", - "put_script", "reindex_rethrottle", "render_search_template", "scripts_painless_execute", diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/StoredScriptsIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/StoredScriptsIT.java index 1d693eee8396e..b15467d24ba2b 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/StoredScriptsIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/StoredScriptsIT.java @@ -1,4 +1,5 @@ -package org.elasticsearch.client;/* +package org.elasticsearch.client; +/* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright @@ -17,27 +18,27 @@ * under the License. */ -import org.apache.http.util.EntityUtils; import org.elasticsearch.ElasticsearchStatusException; import org.elasticsearch.action.admin.cluster.storedscripts.DeleteStoredScriptRequest; import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptRequest; import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptResponse; -import org.elasticsearch.action.support.master.AcknowledgedResponse; -import org.elasticsearch.common.Strings; -import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.action.admin.cluster.storedscripts.PutStoredScriptRequest; +import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.script.Script; import org.elasticsearch.script.StoredScriptSource; import java.util.Collections; +import java.util.Map; -import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.elasticsearch.common.xcontent.support.XContentMapValues.extractValue; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.hamcrest.Matchers.equalTo; public class StoredScriptsIT extends ESRestHighLevelClientTestCase { - final String id = "calculate-score"; + private static final String id = "calculate-score"; public void testGetStoredScript() throws Exception { final StoredScriptSource scriptSource = @@ -45,13 +46,9 @@ public void testGetStoredScript() throws Exception { "Math.log(_score * 2) + params.my_modifier", Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType())); - final String script = Strings.toString(scriptSource.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS)); - // TODO: change to HighLevel PutStoredScriptRequest when it will be ready - // so far - using low-level REST API - Request putRequest = new Request("PUT", "/_scripts/calculate-score"); - putRequest.setJsonEntity("{\"script\":" + script + "}"); - Response putResponse = adminClient().performRequest(putRequest); - assertEquals("{\"acknowledged\":true}", EntityUtils.toString(putResponse.getEntity())); + PutStoredScriptRequest request = + new PutStoredScriptRequest(id, "search", new BytesArray("{}"), XContentType.JSON, scriptSource); + assertAcked(execute(request, highLevelClient()::putScript, highLevelClient()::putScriptAsync)); GetStoredScriptRequest getRequest = new GetStoredScriptRequest("calculate-score"); getRequest.masterNodeTimeout("50s"); @@ -68,22 +65,14 @@ public void testDeleteStoredScript() throws Exception { "Math.log(_score * 2) + params.my_modifier", Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType())); - final String script = Strings.toString(scriptSource.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS)); - // TODO: change to HighLevel PutStoredScriptRequest when it will be ready - // so far - using low-level REST API - Request putRequest = new Request("PUT", "/_scripts/" + id); - putRequest.setJsonEntity("{\"script\":" + script + "}"); - Response putResponse = adminClient().performRequest(putRequest); - assertEquals("{\"acknowledged\":true}", EntityUtils.toString(putResponse.getEntity())); + PutStoredScriptRequest request = + new PutStoredScriptRequest(id, "search", new BytesArray("{}"), XContentType.JSON, scriptSource); + assertAcked(execute(request, highLevelClient()::putScript, highLevelClient()::putScriptAsync)); DeleteStoredScriptRequest deleteRequest = new DeleteStoredScriptRequest(id); deleteRequest.masterNodeTimeout("50s"); deleteRequest.timeout("50s"); - - AcknowledgedResponse deleteResponse = execute(deleteRequest, highLevelClient()::deleteScript, - highLevelClient()::deleteScriptAsync); - - assertThat(deleteResponse.isAcknowledged(), equalTo(true)); + assertAcked(execute(deleteRequest, highLevelClient()::deleteScript, highLevelClient()::deleteScriptAsync)); GetStoredScriptRequest getRequest = new GetStoredScriptRequest(id); @@ -92,4 +81,21 @@ public void testDeleteStoredScript() throws Exception { highLevelClient()::getScriptAsync)); assertThat(statusException.status(), equalTo(RestStatus.NOT_FOUND)); } + + public void testPutScript() throws Exception { + final StoredScriptSource scriptSource = + new StoredScriptSource("painless", + "Math.log(_score * 2) + params.my_modifier", + Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType())); + + PutStoredScriptRequest request = + new PutStoredScriptRequest(id, "search", new BytesArray("{}"), XContentType.JSON, scriptSource); + assertAcked(execute(request, highLevelClient()::putScript, highLevelClient()::putScriptAsync)); + + Map script = getAsMap("/_scripts/" + id); + assertThat(extractValue("_id", script), equalTo(id)); + assertThat(extractValue("found", script), equalTo(true)); + assertThat(extractValue("script.lang", script), equalTo("painless")); + assertThat(extractValue("script.source", script), equalTo("Math.log(_score * 2) + params.my_modifier")); + } } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/StoredScriptsDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/StoredScriptsDocumentationIT.java index fc38090ef5b5b..c5d53abd978e1 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/StoredScriptsDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/StoredScriptsDocumentationIT.java @@ -17,21 +17,21 @@ * under the License. */ -import org.apache.http.util.EntityUtils; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.LatchedActionListener; import org.elasticsearch.action.admin.cluster.storedscripts.DeleteStoredScriptRequest; import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptRequest; import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptResponse; +import org.elasticsearch.action.admin.cluster.storedscripts.PutStoredScriptRequest; import org.elasticsearch.action.support.master.AcknowledgedResponse; import org.elasticsearch.client.ESRestHighLevelClientTestCase; -import org.elasticsearch.client.Request; import org.elasticsearch.client.RequestOptions; -import org.elasticsearch.client.Response; import org.elasticsearch.client.RestHighLevelClient; -import org.elasticsearch.common.Strings; +import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.script.Script; import org.elasticsearch.script.StoredScriptSource; @@ -42,7 +42,8 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.elasticsearch.common.xcontent.support.XContentMapValues.extractValue; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.hamcrest.Matchers.equalTo; /** @@ -187,14 +188,124 @@ public void onFailure(Exception e) { assertTrue(latch.await(30L, TimeUnit.SECONDS)); } + public void testPutScript() throws Exception { + RestHighLevelClient client = highLevelClient(); + + { + // tag::put-stored-script-request + PutStoredScriptRequest request = new PutStoredScriptRequest(); + request.id("id"); // <1> + request.content(new BytesArray( + "{\n" + + "\"script\": {\n" + + "\"lang\": \"painless\",\n" + + "\"source\": \"Math.log(_score * 2) + params.multiplier\"" + + "}\n" + + "}\n" + ), XContentType.JSON); // <2> + // end::put-stored-script-request + + // tag::put-stored-script-context + request.context("context"); // <1> + // end::put-stored-script-context + + // tag::put-stored-script-timeout + request.timeout(TimeValue.timeValueMinutes(2)); // <1> + request.timeout("2m"); // <2> + // end::put-stored-script-timeout + + // tag::put-stored-script-masterTimeout + request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.masterNodeTimeout("1m"); // <2> + // end::put-stored-script-masterTimeout + } + + { + PutStoredScriptRequest request = new PutStoredScriptRequest(); + request.id("id"); + + // tag::put-stored-script-content-painless + XContentBuilder builder = XContentFactory.jsonBuilder(); + builder.startObject(); + { + builder.startObject("script"); + { + builder.field("lang", "painless"); + builder.field("source", "Math.log(_score * 2) + params.multiplier"); + } + builder.endObject(); + } + builder.endObject(); + request.content(BytesReference.bytes(builder), XContentType.JSON); // <1> + // end::put-stored-script-content-painless + + + // tag::put-stored-script-execute + AcknowledgedResponse putStoredScriptResponse = client.putScript(request, RequestOptions.DEFAULT); + // end::put-stored-script-execute + + // tag::put-stored-script-response + boolean acknowledged = putStoredScriptResponse.isAcknowledged(); // <1> + // end::put-stored-script-response + + assertTrue(acknowledged); + + // tag::put-stored-script-execute-listener + ActionListener listener = + new ActionListener() { + @Override + public void onResponse(AcknowledgedResponse response) { + // <1> + } + + @Override + public void onFailure(Exception e) { + // <2> + } + }; + // end::put-stored-script-execute-listener + + // Replace the empty listener by a blocking listener in test + final CountDownLatch latch = new CountDownLatch(1); + listener = new LatchedActionListener<>(listener, latch); + + // tag::put-stored-script-execute-async + client.putScriptAsync(request, RequestOptions.DEFAULT, listener); // <1> + // end::put-stored-script-execute-async + + assertTrue(latch.await(30L, TimeUnit.SECONDS)); + } + + { + PutStoredScriptRequest request = new PutStoredScriptRequest(); + request.id("id"); + + // tag::put-stored-script-content-mustache + XContentBuilder builder = XContentFactory.jsonBuilder(); + builder.startObject(); + { + builder.startObject("script"); + { + builder.field("lang", "mustache"); + builder.field("source", "{\"query\":{\"match\":{\"title\":\"{{query_string}}\"}}}"); + } + builder.endObject(); + } + builder.endObject(); + request.content(BytesReference.bytes(builder), XContentType.JSON); // <1> + // end::put-stored-script-content-mustache + + client.putScript(request, RequestOptions.DEFAULT); + + Map script = getAsMap("/_scripts/id"); + assertThat(extractValue("script.lang", script), equalTo("mustache")); + assertThat(extractValue("script.source", script), equalTo("{\"query\":{\"match\":{\"title\":\"{{query_string}}\"}}}")); + } + } + private void putStoredScript(String id, StoredScriptSource scriptSource) throws IOException { - final String script = Strings.toString(scriptSource.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS)); - // TODO: change to HighLevel PutStoredScriptRequest when it will be ready - // so far - using low-level REST API - Request request = new Request("PUT", "/_scripts/" + id); - request.setJsonEntity("{\"script\":" + script + "}"); - Response putResponse = adminClient().performRequest(request); - assertEquals(putResponse.getStatusLine().getReasonPhrase(), 200, putResponse.getStatusLine().getStatusCode()); - assertEquals("{\"acknowledged\":true}", EntityUtils.toString(putResponse.getEntity())); + PutStoredScriptRequest request = + new PutStoredScriptRequest(id, "search", new BytesArray("{}"), XContentType.JSON, scriptSource); + assertAcked(execute(request, highLevelClient()::putScript, highLevelClient()::putScriptAsync)); } } diff --git a/docs/java-rest/high-level/script/put_script.asciidoc b/docs/java-rest/high-level/script/put_script.asciidoc new file mode 100644 index 0000000000000..acc80e82d11e6 --- /dev/null +++ b/docs/java-rest/high-level/script/put_script.asciidoc @@ -0,0 +1,106 @@ +[[java-rest-high-put-stored-script]] +=== Put Stored Script API + +[[java-rest-high-put-stored-script-request]] +==== Put Stored Script Request + +A `PutStoredScriptRequest` requires an `id` and `content`: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/StoredScriptsDocumentationIT.java[put-stored-script-request] +-------------------------------------------------- +<1> The id of the script +<2> The content of the script + +[[java-rest-high-put-stored-script-content]] +==== Content +The content of a script can be written in different languages and provided in +different ways: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/StoredScriptsDocumentationIT.java[put-stored-script-content-painless] +-------------------------------------------------- +<1> Specify a painless script and provided as `XContentBuilder` object. +Note that the builder needs to be passed as a `BytesReference` object + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/StoredScriptsDocumentationIT.java[put-stored-script-content-mustache] +-------------------------------------------------- +<1> Specify a mustache script and provided as `XContentBuilder` object. +Note that value of source can be directly provided as a JSON string + +==== Optional arguments +The following arguments can optionally be provided: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/StoredScriptsDocumentationIT.java[put-stored-script-context] +-------------------------------------------------- +<1> The context the script should be executed in. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/StoredScriptsDocumentationIT.java[put-stored-script-timeout] +-------------------------------------------------- +<1> Timeout to wait for the all the nodes to acknowledge the script creation as a `TimeValue` +<2> Timeout to wait for the all the nodes to acknowledge the script creation as a `String` + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/StoredScriptsDocumentationIT.java[put-stored-script-masterTimeout] +-------------------------------------------------- +<1> Timeout to connect to the master node as a `TimeValue` +<2> Timeout to connect to the master node as a `String` + +[[java-rest-high-put-stored-script-sync]] +==== Synchronous Execution +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/StoredScriptsDocumentationIT.java[put-stored-script-execute] +-------------------------------------------------- + +[[java-rest-high-put-stored-script-async]] +==== Asynchronous Execution + +The asynchronous execution of a put stored script request requires both the `PutStoredScriptRequest` +instance and an `ActionListener` instance to be passed to the asynchronous method: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/StoredScriptsDocumentationIT.java[put-stored-script-execute-async] +-------------------------------------------------- +<1> The `PutStoredScriptRequest` to execute and the `ActionListener` to use when +the execution completes + +[[java-rest-high-put-stored-script-listener]] +===== Action Listener + +The asynchronous method does not block and returns immediately. Once it is +completed the `ActionListener` is called back using the `onResponse` method +if the execution successfully completed or using the `onFailure` method if +it failed. + +A typical listener for `AcknowledgedResponse` looks like: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/StoredScriptsDocumentationIT.java[put-stored-script-execute-listener] +-------------------------------------------------- +<1> Called when the execution is successfully completed. The response is +provided as an argument +<2> Called in case of failure. The raised exception is provided as an argument + +[[java-rest-high-put-stored-script-response]] +==== Put Stored Script Response + +The returned `AcknowledgedResponse` allows to retrieve information about the +executed operation as follows: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/StoredScriptsDocumentationIT.java[put-stored-script-response] +-------------------------------------------------- +<1> Indicates whether all of the nodes have acknowledged the request \ No newline at end of file diff --git a/docs/java-rest/high-level/supported-apis.asciidoc b/docs/java-rest/high-level/supported-apis.asciidoc index 8d49724353e6f..8d92653ce5702 100644 --- a/docs/java-rest/high-level/supported-apis.asciidoc +++ b/docs/java-rest/high-level/supported-apis.asciidoc @@ -189,9 +189,11 @@ include::tasks/cancel_tasks.asciidoc[] The Java High Level REST Client supports the following Scripts APIs: * <> +* <> * <> include::script/get_script.asciidoc[] +include::script/put_script.asciidoc[] include::script/delete_script.asciidoc[] == Licensing APIs diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/PutStoredScriptRequest.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/PutStoredScriptRequest.java index 6f702cbbe7c0a..44265fd6e9fd3 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/PutStoredScriptRequest.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/PutStoredScriptRequest.java @@ -25,6 +25,8 @@ import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.script.StoredScriptSource; @@ -34,7 +36,7 @@ import static org.elasticsearch.action.ValidateActions.addValidationError; -public class PutStoredScriptRequest extends AcknowledgedRequest { +public class PutStoredScriptRequest extends AcknowledgedRequest implements ToXContent { private String id; private String context; @@ -166,4 +168,12 @@ public String toString() { (context != null ? ", context [" + context + "]" : "") + ", content [" + source + "]}"; } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.field("script"); + source.toXContent(builder, params); + + return builder; + } } diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/storedscripts/PutStoredScriptRequestTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/storedscripts/PutStoredScriptRequestTests.java index 2ca71fabbc7dc..821c75c2ed7d3 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/storedscripts/PutStoredScriptRequestTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/storedscripts/PutStoredScriptRequestTests.java @@ -20,8 +20,11 @@ package org.elasticsearch.action.admin.cluster.storedscripts; import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.script.StoredScriptSource; import org.elasticsearch.test.ESTestCase; @@ -48,4 +51,30 @@ public void testSerialization() throws IOException { } } } + + public void testToXContent() throws IOException { + XContentType xContentType = randomFrom(XContentType.values()); + XContentBuilder builder = XContentBuilder.builder(xContentType.xContent()); + builder.startObject(); + builder.startObject("script") + .field("lang", "painless") + .field("source", "Math.log(_score * 2) + params.multiplier") + .endObject(); + builder.endObject(); + + BytesReference expectedRequestBody = BytesReference.bytes(builder); + + PutStoredScriptRequest request = new PutStoredScriptRequest(); + request.id("test1"); + request.content(expectedRequestBody, xContentType); + + XContentBuilder requestBuilder = XContentBuilder.builder(xContentType.xContent()); + requestBuilder.startObject(); + request.toXContent(requestBuilder, ToXContent.EMPTY_PARAMS); + requestBuilder.endObject(); + + BytesReference actualRequestBody = BytesReference.bytes(requestBuilder); + + assertEquals(expectedRequestBody, actualRequestBody); + } }