Browse Source

Add get stored script and delete stored script to high level REST API (#31355)

Add get stored script and delete stored script to high level REST API

Relates to #27205
Vladimir Dolzhenko 7 years ago
parent
commit
04e4e44409
16 changed files with 789 additions and 46 deletions
  1. 19 0
      client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java
  2. 60 0
      client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java
  3. 28 0
      client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java
  4. 105 0
      client/rest-high-level/src/test/java/org/elasticsearch/client/StoredScriptsIT.java
  5. 204 0
      client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/StoredScriptsDocumentationIT.java
  6. 81 0
      docs/java-rest/high-level/script/delete_script.asciidoc
  7. 77 0
      docs/java-rest/high-level/script/get_script.asciidoc
  8. 11 0
      docs/java-rest/high-level/supported-apis.asciidoc
  9. 4 0
      rest-api-spec/src/main/resources/rest-api-spec/api/get_script.json
  10. 5 0
      server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/DeleteStoredScriptResponse.java
  11. 80 4
      server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/GetStoredScriptResponse.java
  12. 1 1
      server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/TransportGetStoredScriptAction.java
  13. 3 39
      server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestGetStoredScriptAction.java
  14. 4 2
      server/src/main/java/org/elasticsearch/script/StoredScriptSource.java
  15. 46 0
      server/src/test/java/org/elasticsearch/action/admin/cluster/storedscripts/DeleteStoredScriptResponseTests.java
  16. 61 0
      server/src/test/java/org/elasticsearch/action/admin/cluster/storedscripts/GetStoredScriptResponseTests.java

+ 19 - 0
client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java

@@ -37,6 +37,8 @@ import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesRe
 import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequest;
 import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryRequest;
 import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest;
+import org.elasticsearch.action.admin.cluster.storedscripts.DeleteStoredScriptRequest;
+import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptRequest;
 import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
 import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
 import org.elasticsearch.action.admin.indices.cache.clear.ClearIndicesCacheRequest;
@@ -892,6 +894,23 @@ final class RequestConverters {
         return request;
     }
 
+    static Request getScript(GetStoredScriptRequest getStoredScriptRequest) {
+        String endpoint = new EndpointBuilder().addPathPartAsIs("_scripts").addPathPart(getStoredScriptRequest.id()).build();
+        Request request = new Request(HttpGet.METHOD_NAME, endpoint);
+        Params params = new Params(request);
+        params.withMasterTimeout(getStoredScriptRequest.masterNodeTimeout());
+        return request;
+    }
+
+    static Request deleteScript(DeleteStoredScriptRequest deleteStoredScriptRequest) {
+        String endpoint = new EndpointBuilder().addPathPartAsIs("_scripts").addPathPart(deleteStoredScriptRequest.id()).build();
+        Request request = new Request(HttpDelete.METHOD_NAME, endpoint);
+        Params params = new Params(request);
+        params.withTimeout(deleteStoredScriptRequest.timeout());
+        params.withMasterTimeout(deleteStoredScriptRequest.masterNodeTimeout());
+        return request;
+    }
+
     private static HttpEntity createEntity(ToXContent toXContent, XContentType xContentType) throws IOException {
         BytesRef source = XContentHelper.toXContent(toXContent, xContentType, false).toBytesRef();
         return new ByteArrayEntity(source.bytes, source.offset, source.length, createContentType(xContentType));

+ 60 - 0
client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java

@@ -26,6 +26,10 @@ import org.elasticsearch.ElasticsearchStatusException;
 import org.elasticsearch.action.ActionListener;
 import org.elasticsearch.action.ActionRequest;
 import org.elasticsearch.action.ActionRequestValidationException;
+import org.elasticsearch.action.admin.cluster.storedscripts.DeleteStoredScriptRequest;
+import org.elasticsearch.action.admin.cluster.storedscripts.DeleteStoredScriptResponse;
+import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptRequest;
+import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptResponse;
 import org.elasticsearch.action.bulk.BulkRequest;
 import org.elasticsearch.action.bulk.BulkResponse;
 import org.elasticsearch.action.delete.DeleteRequest;
@@ -652,6 +656,62 @@ public class RestHighLevelClient implements Closeable {
             FieldCapabilitiesResponse::fromXContent, emptySet());
     }
 
+    /**
+     * Get stored script by id.
+     * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-scripting-using.html">
+     *     How to use scripts on elastic.co</a>
+     * @param request 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 GetStoredScriptResponse getScript(GetStoredScriptRequest request, RequestOptions options) throws IOException {
+        return performRequestAndParseEntity(request, RequestConverters::getScript, options,
+            GetStoredScriptResponse::fromXContent, emptySet());
+    }
+
+    /**
+     * Asynchronously get stored script by id.
+     * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-scripting-using.html">
+     *     How to use scripts on elastic.co</a>
+     * @param request 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 getScriptAsync(GetStoredScriptRequest request, RequestOptions options,
+                               ActionListener<GetStoredScriptResponse> listener) {
+        performRequestAsyncAndParseEntity(request, RequestConverters::getScript, options,
+            GetStoredScriptResponse::fromXContent, listener, emptySet());
+    }
+
+    /**
+     * Delete stored script by id.
+     * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-scripting-using.html">
+     *     How to use scripts on elastic.co</a>
+     * @param request 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 DeleteStoredScriptResponse deleteScript(DeleteStoredScriptRequest request, RequestOptions options) throws IOException {
+        return performRequestAndParseEntity(request, RequestConverters::deleteScript, options,
+            DeleteStoredScriptResponse::fromXContent, emptySet());
+    }
+
+    /**
+     * Asynchronously delete stored script by id.
+     * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-scripting-using.html">
+     *     How to use scripts on elastic.co</a>
+     * @param request 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 deleteScriptAsync(DeleteStoredScriptRequest request, RequestOptions options,
+                                  ActionListener<DeleteStoredScriptResponse> listener) {
+        performRequestAsyncAndParseEntity(request, RequestConverters::deleteScript, options,
+            DeleteStoredScriptResponse::fromXContent, listener, emptySet());
+    }
+
     /**
      * Asynchronously executes a request using the Field Capabilities API.
      * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-field-caps.html">Field Capabilities API

+ 28 - 0
client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java

@@ -37,6 +37,8 @@ import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesRe
 import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequest;
 import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryRequest;
 import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest;
+import org.elasticsearch.action.admin.cluster.storedscripts.DeleteStoredScriptRequest;
+import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptRequest;
 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;
@@ -1948,6 +1950,32 @@ public class RequestConvertersTests extends ESTestCase {
         assertThat(request.getEntity(), nullValue());
     }
 
+    public void testGetScriptRequest() {
+        GetStoredScriptRequest getStoredScriptRequest = new GetStoredScriptRequest("x-script");
+        Map<String, String> expectedParams = new HashMap<>();
+        setRandomMasterTimeout(getStoredScriptRequest, expectedParams);
+
+        Request request = RequestConverters.getScript(getStoredScriptRequest);
+        assertThat(request.getEndpoint(), equalTo("/_scripts/" + getStoredScriptRequest.id()));
+        assertThat(request.getMethod(), equalTo(HttpGet.METHOD_NAME));
+        assertThat(request.getParameters(), equalTo(expectedParams));
+        assertThat(request.getEntity(), nullValue());
+    }
+
+    public void testDeleteScriptRequest() {
+        DeleteStoredScriptRequest deleteStoredScriptRequest = new DeleteStoredScriptRequest("x-script");
+
+        Map<String, String> expectedParams = new HashMap<>();
+        setRandomTimeout(deleteStoredScriptRequest::timeout, AcknowledgedRequest.DEFAULT_ACK_TIMEOUT, expectedParams);
+        setRandomMasterTimeout(deleteStoredScriptRequest, expectedParams);
+
+        Request request = RequestConverters.deleteScript(deleteStoredScriptRequest);
+        assertThat(request.getEndpoint(), equalTo("/_scripts/" + deleteStoredScriptRequest.id()));
+        assertThat(request.getMethod(), equalTo(HttpDelete.METHOD_NAME));
+        assertThat(request.getParameters(), equalTo(expectedParams));
+        assertThat(request.getEntity(), nullValue());
+    }
+
     private static void assertToXContentBody(ToXContent expectedBody, HttpEntity actualEntity) throws IOException {
         BytesReference expectedBytes = XContentHelper.toXContent(expectedBody, REQUEST_BODY_CONTENT_TYPE, false);
         assertEquals(XContentType.JSON.mediaTypeWithoutParameters(), actualEntity.getContentType().getValue());

+ 105 - 0
client/rest-high-level/src/test/java/org/elasticsearch/client/StoredScriptsIT.java

@@ -0,0 +1,105 @@
+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
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+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.DeleteStoredScriptResponse;
+import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptRequest;
+import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptResponse;
+import org.elasticsearch.common.Strings;
+import org.elasticsearch.common.xcontent.ToXContent;
+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 static java.util.Collections.emptyMap;
+import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
+import static org.hamcrest.Matchers.equalTo;
+
+public class StoredScriptsIT extends ESRestHighLevelClientTestCase {
+
+    final String id = "calculate-score";
+
+    public void testGetStoredScript() throws Exception {
+        final StoredScriptSource scriptSource =
+            new StoredScriptSource("painless",
+                "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
+        Response putResponse =
+            adminClient()
+                .performRequest("PUT", "/_scripts/calculate-score", emptyMap(),
+                    new StringEntity("{\"script\":" + script + "}",
+                        ContentType.APPLICATION_JSON));
+        assertEquals(putResponse.getStatusLine().getReasonPhrase(), 200, putResponse.getStatusLine().getStatusCode());
+        assertEquals("{\"acknowledged\":true}", EntityUtils.toString(putResponse.getEntity()));
+
+        GetStoredScriptRequest getRequest = new GetStoredScriptRequest("calculate-score");
+        getRequest.masterNodeTimeout("50s");
+
+        GetStoredScriptResponse getResponse = execute(getRequest, highLevelClient()::getScript,
+            highLevelClient()::getScriptAsync);
+
+        assertThat(getResponse.getSource(), equalTo(scriptSource));
+    }
+
+    public void testDeleteStoredScript() throws Exception {
+        final StoredScriptSource scriptSource =
+            new StoredScriptSource("painless",
+                "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
+        Response putResponse =
+            adminClient()
+                .performRequest("PUT", "/_scripts/" + id, emptyMap(),
+                    new StringEntity("{\"script\":" + script + "}",
+                        ContentType.APPLICATION_JSON));
+        assertEquals(putResponse.getStatusLine().getReasonPhrase(), 200, putResponse.getStatusLine().getStatusCode());
+        assertEquals("{\"acknowledged\":true}", EntityUtils.toString(putResponse.getEntity()));
+
+        DeleteStoredScriptRequest deleteRequest = new DeleteStoredScriptRequest(id);
+        deleteRequest.masterNodeTimeout("50s");
+        deleteRequest.timeout("50s");
+
+        DeleteStoredScriptResponse deleteResponse = execute(deleteRequest, highLevelClient()::deleteScript,
+            highLevelClient()::deleteScriptAsync);
+
+        assertThat(deleteResponse.isAcknowledged(), equalTo(true));
+
+        GetStoredScriptRequest getRequest = new GetStoredScriptRequest(id);
+
+        final ElasticsearchStatusException statusException = expectThrows(ElasticsearchStatusException.class,
+            () -> execute(getRequest, highLevelClient()::getScript,
+                highLevelClient()::getScriptAsync));
+        assertThat(statusException.status(), equalTo(RestStatus.NOT_FOUND));
+    }
+}

+ 204 - 0
client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/StoredScriptsDocumentationIT.java

@@ -0,0 +1,204 @@
+package org.elasticsearch.client.documentation;/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+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.DeleteStoredScriptResponse;
+import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptRequest;
+import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptResponse;
+import org.elasticsearch.client.ESRestHighLevelClientTestCase;
+import org.elasticsearch.client.RequestOptions;
+import org.elasticsearch.client.Response;
+import org.elasticsearch.client.RestHighLevelClient;
+import org.elasticsearch.common.Strings;
+import org.elasticsearch.common.unit.TimeValue;
+import org.elasticsearch.common.xcontent.ToXContent;
+import org.elasticsearch.common.xcontent.XContentType;
+import org.elasticsearch.script.Script;
+import org.elasticsearch.script.StoredScriptSource;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static java.util.Collections.emptyMap;
+import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
+import static org.hamcrest.Matchers.equalTo;
+
+/**
+ * This class is used to generate the Java Stored Scripts API documentation.
+ * You need to wrap your code between two tags like:
+ * // tag::example
+ * // end::example
+ *
+ * Where example is your tag name.
+ *
+ * Then in the documentation, you can extract what is between tag and end tags with
+ * ["source","java",subs="attributes,callouts,macros"]
+ * --------------------------------------------------
+ * include-tagged::{doc-tests}/StoredScriptsDocumentationIT.java[example]
+ * --------------------------------------------------
+ *
+ * The column width of the code block is 84. If the code contains a line longer
+ * than 84, the line will be cut and a horizontal scroll bar will be displayed.
+ * (the code indentation of the tag is not included in the width)
+ */
+public class StoredScriptsDocumentationIT extends ESRestHighLevelClientTestCase {
+
+    public void testGetStoredScript() throws Exception {
+        RestHighLevelClient client = highLevelClient();
+
+        final StoredScriptSource scriptSource =
+            new StoredScriptSource("painless",
+                "Math.log(_score * 2) + params.my_modifier",
+                Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType()));
+
+        putStoredScript("calculate-score", scriptSource);
+
+        {
+            // tag::get-stored-script-request
+            GetStoredScriptRequest request = new GetStoredScriptRequest("calculate-score"); // <1>
+            // end::get-stored-script-request
+
+            // tag::get-stored-script-request-masterTimeout
+            request.masterNodeTimeout(TimeValue.timeValueSeconds(50)); // <1>
+            request.masterNodeTimeout("50s"); // <2>
+            // end::get-stored-script-request-masterTimeout
+
+            // tag::get-stored-script-execute
+            GetStoredScriptResponse getResponse = client.getScript(request, RequestOptions.DEFAULT);
+            // end::get-stored-script-execute
+
+            // tag::get-stored-script-response
+            StoredScriptSource storedScriptSource = getResponse.getSource(); // <1>
+
+            String lang = storedScriptSource.getLang(); // <2>
+            String source = storedScriptSource.getSource(); // <3>
+            Map<String, String> options = storedScriptSource.getOptions(); // <4>
+            // end::get-stored-script-response
+
+            assertThat(storedScriptSource, equalTo(scriptSource));
+
+            // tag::get-stored-script-execute-listener
+            ActionListener<GetStoredScriptResponse> listener =
+                new ActionListener<GetStoredScriptResponse>() {
+                    @Override
+                    public void onResponse(GetStoredScriptResponse response) {
+                        // <1>
+                    }
+
+                    @Override
+                    public void onFailure(Exception e) {
+                        // <2>
+                    }
+                };
+            // end::get-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::get-stored-script-execute-async
+            client.getScriptAsync(request, RequestOptions.DEFAULT, listener); // <1>
+            // end::get-stored-script-execute-async
+
+            assertTrue(latch.await(30L, TimeUnit.SECONDS));
+        }
+
+    }
+
+    public void testDeleteStoredScript() throws Exception {
+        RestHighLevelClient client = highLevelClient();
+
+        final StoredScriptSource scriptSource =
+            new StoredScriptSource("painless",
+                "Math.log(_score * 2) + params.my_modifier",
+                Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType()));
+
+        putStoredScript("calculate-score", scriptSource);
+
+        // tag::delete-stored-script-request
+        DeleteStoredScriptRequest deleteRequest = new DeleteStoredScriptRequest("calculate-score"); // <1>
+        // end::delete-stored-script-request
+
+        // tag::delete-stored-script-request-masterTimeout
+        deleteRequest.masterNodeTimeout(TimeValue.timeValueSeconds(50)); // <1>
+        deleteRequest.masterNodeTimeout("50s"); // <2>
+        // end::delete-stored-script-request-masterTimeout
+
+        // tag::delete-stored-script-request-timeout
+        deleteRequest.timeout(TimeValue.timeValueSeconds(60)); // <1>
+        deleteRequest.timeout("60s"); // <2>
+        // end::delete-stored-script-request-timeout
+
+        // tag::delete-stored-script-execute
+        DeleteStoredScriptResponse deleteResponse = client.deleteScript(deleteRequest, RequestOptions.DEFAULT);
+        // end::delete-stored-script-execute
+
+        // tag::delete-stored-script-response
+        boolean acknowledged = deleteResponse.isAcknowledged();// <1>
+        // end::delete-stored-script-response
+
+        putStoredScript("calculate-score", scriptSource);
+
+        // tag::delete-stored-script-execute-listener
+        ActionListener<DeleteStoredScriptResponse> listener =
+            new ActionListener<DeleteStoredScriptResponse>() {
+                @Override
+                public void onResponse(DeleteStoredScriptResponse response) {
+                    // <1>
+                }
+
+                @Override
+                public void onFailure(Exception e) {
+                    // <2>
+                }
+            };
+        // end::delete-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::delete-stored-script-execute-async
+        client.deleteScriptAsync(deleteRequest, RequestOptions.DEFAULT, listener); // <1>
+        // end::delete-stored-script-execute-async
+
+        assertTrue(latch.await(30L, TimeUnit.SECONDS));
+    }
+
+    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
+        Response putResponse =
+            adminClient()
+                .performRequest("PUT", "/_scripts/" + id, emptyMap(),
+                    new StringEntity("{\"script\":" + script + "}",
+                        ContentType.APPLICATION_JSON));
+        assertEquals(putResponse.getStatusLine().getReasonPhrase(), 200, putResponse.getStatusLine().getStatusCode());
+        assertEquals("{\"acknowledged\":true}", EntityUtils.toString(putResponse.getEntity()));
+    }
+}

+ 81 - 0
docs/java-rest/high-level/script/delete_script.asciidoc

@@ -0,0 +1,81 @@
+[[java-rest-high-delete-stored-script]]
+
+=== Delete Stored Script API
+
+[[java-rest-high-delete-stored-script-request]]
+==== Delete Stored Script Request
+
+A `DeleteStoredScriptRequest` requires an `id`:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/StoredScriptsDocumentationIT.java[delete-stored-script-request]
+--------------------------------------------------
+<1> The id of the script
+
+==== Optional arguments
+The following arguments can optionally be provided:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/StoredScriptsDocumentationIT.java[delete-stored-script-request-timeout]
+--------------------------------------------------
+<1> Timeout to wait for the all the nodes to acknowledge the stored script is deleted as a `TimeValue`
+<2> Timeout to wait for the all the nodes to acknowledge the stored script is deleted as a `String`
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/StoredScriptsDocumentationIT.java[delete-stored-script-request-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-delete-stored-script-sync]]
+==== Synchronous Execution
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/StoredScriptsDocumentationIT.java[delete-stored-script-execute]
+--------------------------------------------------
+
+[[java-rest-high-delete-stored-script-async]]
+==== Asynchronous Execution
+
+The asynchronous execution of a delete stored script request requires both the `DeleteStoredScriptRequest`
+instance and an `ActionListener` instance to be passed to the asynchronous method:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/StoredScriptsDocumentationIT.java[delete-stored-script-execute-async]
+--------------------------------------------------
+<1> The `DeleteStoredScriptRequest` to execute and the `ActionListener` to use when
+the execution completes
+
+[[java-rest-high-delete-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 `DeleteStoredScriptResponse` looks like:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/StoredScriptsDocumentationIT.java[delete-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-delete-stored-script-response]]
+==== Delete Stored Script Response
+
+The returned `DeleteStoredScriptResponse` allows to retrieve information about the
+executed operation as follows:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/StoredScriptsDocumentationIT.java[delete-stored-script-response]
+--------------------------------------------------
+<1> Indicates whether all of the nodes have acknowledged the request

+ 77 - 0
docs/java-rest/high-level/script/get_script.asciidoc

@@ -0,0 +1,77 @@
+[[java-rest-high-get-stored-script]]
+
+=== Get Stored Script API
+
+[[java-rest-high-get-stored-script-request]]
+==== Get Stored Script Request
+
+A `GetStoredScriptRequest` requires an `id`:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/StoredScriptsDocumentationIT.java[get-stored-script-request]
+--------------------------------------------------
+<1> The id of the script
+
+==== Optional arguments
+The following arguments can optionally be provided:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/StoredScriptsDocumentationIT.java[get-stored-script-request-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-get-stored-script-sync]]
+==== Synchronous Execution
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/StoredScriptsDocumentationIT.java[get-stored-script-execute]
+--------------------------------------------------
+
+[[java-rest-high-get-stored-script-async]]
+==== Asynchronous Execution
+
+The asynchronous execution of a get stored script request requires both the `GetStoredScriptRequest`
+instance and an `ActionListener` instance to be passed to the asynchronous method:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/StoredScriptsDocumentationIT.java[get-stored-script-execute-async]
+--------------------------------------------------
+<1> The `GetStoredScriptRequest` to execute and the `ActionListener` to use when
+the execution completes
+
+[[java-rest-high-get-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 `GetStoredScriptResponse` looks like:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/StoredScriptsDocumentationIT.java[get-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-get-stored-script-response]]
+==== Get Stored Script Response
+
+The returned `GetStoredScriptResponse` allows to retrieve information about the
+executed operation as follows:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/StoredScriptsDocumentationIT.java[get-stored-script-response]
+--------------------------------------------------
+<1> The script object consists of a content and a metadata
+<2> The language the script is written in, which defaults to `painless`.
+<3> The content of the script
+<4> Any named options that should be passed into the script.

+ 11 - 0
docs/java-rest/high-level/supported-apis.asciidoc

@@ -151,3 +151,14 @@ The Java High Level REST Client supports the following Tasks APIs:
 
 include::tasks/list_tasks.asciidoc[]
 include::tasks/cancel_tasks.asciidoc[]
+
+== Script APIs
+
+The Java High Level REST Client supports the following Scripts APIs:
+
+* <<java-rest-high-get-stored-script>>
+* <<java-rest-high-delete-stored-script>>
+
+include::script/get_script.asciidoc[]
+include::script/delete_script.asciidoc[]
+

+ 4 - 0
rest-api-spec/src/main/resources/rest-api-spec/api/get_script.json

@@ -13,6 +13,10 @@
         }
       },
       "params" : {
+        "master_timeout": {
+          "type" : "time",
+          "description" : "Specify timeout for connection to master"
+        }
       }
     },
     "body": null

+ 5 - 0
server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/DeleteStoredScriptResponse.java

@@ -20,6 +20,7 @@
 package org.elasticsearch.action.admin.cluster.storedscripts;
 
 import org.elasticsearch.action.support.master.AcknowledgedResponse;
+import org.elasticsearch.common.xcontent.XContentParser;
 
 public class DeleteStoredScriptResponse extends AcknowledgedResponse {
 
@@ -29,4 +30,8 @@ public class DeleteStoredScriptResponse extends AcknowledgedResponse {
     public DeleteStoredScriptResponse(boolean acknowledged) {
         super(acknowledged);
     }
+
+    public static DeleteStoredScriptResponse fromXContent(XContentParser parser) {
+        return new DeleteStoredScriptResponse(parseAcknowledged(parser));
+    }
 }

+ 80 - 4
server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/GetStoredScriptResponse.java

@@ -21,25 +21,63 @@ package org.elasticsearch.action.admin.cluster.storedscripts;
 
 import org.elasticsearch.Version;
 import org.elasticsearch.action.ActionResponse;
+import org.elasticsearch.common.ParseField;
 import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamOutput;
-import org.elasticsearch.common.xcontent.ToXContentObject;
+import org.elasticsearch.common.xcontent.ConstructingObjectParser;
+import org.elasticsearch.common.xcontent.ObjectParser;
+import org.elasticsearch.common.xcontent.StatusToXContentObject;
 import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.common.xcontent.XContentParser;
+import org.elasticsearch.rest.RestStatus;
 import org.elasticsearch.script.StoredScriptSource;
 
 import java.io.IOException;
+import java.util.Objects;
 
-public class GetStoredScriptResponse extends ActionResponse implements ToXContentObject {
+import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
+import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;
 
+public class GetStoredScriptResponse extends ActionResponse implements StatusToXContentObject {
+
+    public static final ParseField _ID_PARSE_FIELD = new ParseField("_id");
+    public static final ParseField FOUND_PARSE_FIELD = new ParseField("found");
+    public static final ParseField SCRIPT = new ParseField("script");
+
+    private static final ConstructingObjectParser<GetStoredScriptResponse, String> PARSER =
+        new ConstructingObjectParser<>("GetStoredScriptResponse",
+            true,
+            (a, c) -> {
+                String id = (String) a[0];
+                boolean found = (Boolean)a[1];
+                StoredScriptSource scriptSource = (StoredScriptSource)a[2];
+                return found ? new GetStoredScriptResponse(id, scriptSource) : new GetStoredScriptResponse(id, null);
+            });
+
+    static {
+        PARSER.declareField(constructorArg(), (p, c) -> p.text(),
+            _ID_PARSE_FIELD, ObjectParser.ValueType.STRING);
+        PARSER.declareField(constructorArg(), (p, c) -> p.booleanValue(),
+            FOUND_PARSE_FIELD, ObjectParser.ValueType.BOOLEAN);
+        PARSER.declareField(optionalConstructorArg(), (p, c) -> StoredScriptSource.fromXContent(p, true),
+            SCRIPT, ObjectParser.ValueType.OBJECT);
+    }
+
+    private String id;
     private StoredScriptSource source;
 
     GetStoredScriptResponse() {
     }
 
-    GetStoredScriptResponse(StoredScriptSource source) {
+    GetStoredScriptResponse(String id, StoredScriptSource source) {
+        this.id = id;
         this.source = source;
     }
 
+    public String getId() {
+        return id;
+    }
+
     /**
      * @return if a stored script and if not found <code>null</code>
      */
@@ -47,13 +85,30 @@ public class GetStoredScriptResponse extends ActionResponse implements ToXConten
         return source;
     }
 
+    @Override
+    public RestStatus status() {
+        return source != null ? RestStatus.OK : RestStatus.NOT_FOUND;
+    }
+
     @Override
     public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
-        source.toXContent(builder, params);
+        builder.startObject();
+
+        builder.field(_ID_PARSE_FIELD.getPreferredName(), id);
+        builder.field(FOUND_PARSE_FIELD.getPreferredName(), source != null);
+        if (source != null) {
+            builder.field(StoredScriptSource.SCRIPT_PARSE_FIELD.getPreferredName());
+            source.toXContent(builder, params);
+        }
 
+        builder.endObject();
         return builder;
     }
 
+    public static GetStoredScriptResponse fromXContent(XContentParser parser) throws IOException {
+        return PARSER.parse(parser, null);
+    }
+
     @Override
     public void readFrom(StreamInput in) throws IOException {
         super.readFrom(in);
@@ -67,6 +122,10 @@ public class GetStoredScriptResponse extends ActionResponse implements ToXConten
         } else {
             source = null;
         }
+
+        if (in.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
+            id = in.readString();
+        }
     }
 
     @Override
@@ -84,5 +143,22 @@ public class GetStoredScriptResponse extends ActionResponse implements ToXConten
                 out.writeString(source.getSource());
             }
         }
+        if (out.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
+            out.writeString(id);
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        GetStoredScriptResponse that = (GetStoredScriptResponse) o;
+        return Objects.equals(id, that.id) &&
+            Objects.equals(source, that.source);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(id, source);
     }
 }

+ 1 - 1
server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/TransportGetStoredScriptAction.java

@@ -60,7 +60,7 @@ public class TransportGetStoredScriptAction extends TransportMasterNodeReadActio
     @Override
     protected void masterOperation(GetStoredScriptRequest request, ClusterState state,
                                    ActionListener<GetStoredScriptResponse> listener) throws Exception {
-        listener.onResponse(new GetStoredScriptResponse(scriptService.getStoredScript(state, request)));
+        listener.onResponse(new GetStoredScriptResponse(request.id(), scriptService.getStoredScript(state, request)));
     }
 
     @Override

+ 3 - 39
server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestGetStoredScriptAction.java

@@ -19,19 +19,12 @@
 package org.elasticsearch.rest.action.admin.cluster;
 
 import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptRequest;
-import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptResponse;
 import org.elasticsearch.client.node.NodeClient;
-import org.elasticsearch.common.ParseField;
 import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.rest.BaseRestHandler;
-import org.elasticsearch.rest.BytesRestResponse;
 import org.elasticsearch.rest.RestController;
 import org.elasticsearch.rest.RestRequest;
-import org.elasticsearch.rest.RestResponse;
-import org.elasticsearch.rest.RestStatus;
-import org.elasticsearch.rest.action.RestBuilderListener;
-import org.elasticsearch.script.StoredScriptSource;
+import org.elasticsearch.rest.action.RestStatusToXContentListener;
 
 import java.io.IOException;
 
@@ -39,9 +32,6 @@ import static org.elasticsearch.rest.RestRequest.Method.GET;
 
 public class RestGetStoredScriptAction extends BaseRestHandler {
 
-    public static final ParseField _ID_PARSE_FIELD = new ParseField("_id");
-    public static final ParseField FOUND_PARSE_FIELD = new ParseField("found");
-
     public RestGetStoredScriptAction(Settings settings, RestController controller) {
         super(settings);
 
@@ -57,33 +47,7 @@ public class RestGetStoredScriptAction extends BaseRestHandler {
     public RestChannelConsumer prepareRequest(final RestRequest request, NodeClient client) throws IOException {
         String id = request.param("id");
         GetStoredScriptRequest getRequest = new GetStoredScriptRequest(id);
-
-        return channel -> client.admin().cluster().getStoredScript(getRequest, new RestBuilderListener<GetStoredScriptResponse>(channel) {
-            @Override
-            public RestResponse buildResponse(GetStoredScriptResponse response, XContentBuilder builder) throws Exception {
-                builder.startObject();
-                builder.field(_ID_PARSE_FIELD.getPreferredName(), id);
-
-                StoredScriptSource source = response.getSource();
-                boolean found = source != null;
-                builder.field(FOUND_PARSE_FIELD.getPreferredName(), found);
-
-                if (found) {
-                    builder.startObject(StoredScriptSource.SCRIPT_PARSE_FIELD.getPreferredName());
-                    builder.field(StoredScriptSource.LANG_PARSE_FIELD.getPreferredName(), source.getLang());
-                    builder.field(StoredScriptSource.SOURCE_PARSE_FIELD.getPreferredName(), source.getSource());
-
-                    if (source.getOptions().isEmpty() == false) {
-                        builder.field(StoredScriptSource.OPTIONS_PARSE_FIELD.getPreferredName(), source.getOptions());
-                    }
-
-                    builder.endObject();
-                }
-
-                builder.endObject();
-
-                return new BytesRestResponse(found ? RestStatus.OK : RestStatus.NOT_FOUND, builder);
-            }
-        });
+        getRequest.masterNodeTimeout(request.paramAsTime("master_timeout", getRequest.masterNodeTimeout()));
+        return channel -> client.admin().cluster().getStoredScript(getRequest, new RestStatusToXContentListener<>(channel));
     }
 }

+ 4 - 2
server/src/main/java/org/elasticsearch/script/StoredScriptSource.java

@@ -185,7 +185,7 @@ public class StoredScriptSource extends AbstractDiffable<StoredScriptSource> imp
         }
     }
 
-    private static final ObjectParser<Builder, Void> PARSER = new ObjectParser<>("stored script source", Builder::new);
+    private static final ObjectParser<Builder, Void> PARSER = new ObjectParser<>("stored script source", true, Builder::new);
 
     static {
         // Defines the fields necessary to parse a Script as XContent using an ObjectParser.
@@ -481,7 +481,9 @@ public class StoredScriptSource extends AbstractDiffable<StoredScriptSource> imp
         builder.startObject();
         builder.field(LANG_PARSE_FIELD.getPreferredName(), lang);
         builder.field(SOURCE_PARSE_FIELD.getPreferredName(), source);
-        builder.field(OPTIONS_PARSE_FIELD.getPreferredName(), options);
+        if (options.isEmpty() == false) {
+            builder.field(OPTIONS_PARSE_FIELD.getPreferredName(), options);
+        }
         builder.endObject();
 
         return builder;

+ 46 - 0
server/src/test/java/org/elasticsearch/action/admin/cluster/storedscripts/DeleteStoredScriptResponseTests.java

@@ -0,0 +1,46 @@
+package org.elasticsearch.action.admin.cluster.storedscripts;/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.elasticsearch.common.xcontent.XContentParser;
+import org.elasticsearch.test.AbstractStreamableXContentTestCase;
+
+import java.io.IOException;
+
+public class DeleteStoredScriptResponseTests extends AbstractStreamableXContentTestCase<DeleteStoredScriptResponse> {
+
+    @Override
+    protected DeleteStoredScriptResponse doParseInstance(XContentParser parser) throws IOException {
+        return DeleteStoredScriptResponse.fromXContent(parser);
+    }
+
+    @Override
+    protected DeleteStoredScriptResponse createBlankInstance() {
+        return new DeleteStoredScriptResponse();
+    }
+
+    @Override
+    protected DeleteStoredScriptResponse createTestInstance() {
+        return new DeleteStoredScriptResponse(randomBoolean());
+    }
+
+    @Override
+    protected DeleteStoredScriptResponse mutateInstance(DeleteStoredScriptResponse instance) throws IOException {
+        return new DeleteStoredScriptResponse(instance.isAcknowledged() == false);
+    }
+}

+ 61 - 0
server/src/test/java/org/elasticsearch/action/admin/cluster/storedscripts/GetStoredScriptResponseTests.java

@@ -0,0 +1,61 @@
+package org.elasticsearch.action.admin.cluster.storedscripts;/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.elasticsearch.common.xcontent.XContentParser;
+import org.elasticsearch.common.xcontent.XContentType;
+import org.elasticsearch.script.Script;
+import org.elasticsearch.script.StoredScriptSource;
+import org.elasticsearch.test.AbstractStreamableXContentTestCase;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.function.Predicate;
+
+public class GetStoredScriptResponseTests extends AbstractStreamableXContentTestCase<GetStoredScriptResponse> {
+
+    @Override
+    protected GetStoredScriptResponse doParseInstance(XContentParser parser) throws IOException {
+        return GetStoredScriptResponse.fromXContent(parser);
+    }
+
+    @Override
+    protected GetStoredScriptResponse createBlankInstance() {
+        return new GetStoredScriptResponse();
+    }
+
+    @Override
+    protected GetStoredScriptResponse createTestInstance() {
+        return new GetStoredScriptResponse(randomAlphaOfLengthBetween(1, 10), randomScriptSource());
+    }
+
+    @Override
+    protected Predicate<String> getRandomFieldsExcludeFilter() {
+        return s -> "script.options".equals(s);
+    }
+
+    private static StoredScriptSource randomScriptSource() {
+        final String lang = randomFrom("lang", "painless", "mustache");
+        final String source = randomAlphaOfLengthBetween(1, 10);
+        final Map<String, String> options = randomBoolean()
+            ? Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType())
+            : Collections.emptyMap();
+        return new StoredScriptSource(lang, source, options);
+    }
+}