diff --git a/build.gradle b/build.gradle index 08ecc4c4835f4..0f1f2ef21f618 100644 --- a/build.gradle +++ b/build.gradle @@ -174,8 +174,8 @@ tasks.register("verifyVersions") { * after the backport of the backcompat code is complete. */ -boolean bwc_tests_enabled = true -final String bwc_tests_disabled_issue = "" /* place a PR link here when committing bwc changes */ +boolean bwc_tests_enabled = false +final String bwc_tests_disabled_issue = "https://github.com/elastic/elasticsearch/pull/28368" /* place a PR link here when committing bwc changes */ if (bwc_tests_enabled == false) { if (bwc_tests_disabled_issue.isEmpty()) { throw new GradleException("bwc_tests_disabled_issue must be set when bwc_tests_enabled == false") 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 be9dfc4ca7a1e..27b4d528b27a1 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 @@ -662,7 +662,7 @@ private static Request rethrottle(RethrottleRequest rethrottleRequest, String fi } static Request putScript(PutStoredScriptRequest putStoredScriptRequest) throws IOException { - String endpoint = new EndpointBuilder().addPathPartAsIs("_scripts").addPathPart(putStoredScriptRequest.id()).build(); + String endpoint = new EndpointBuilder().addPathPartAsIs("_script").addPathPart(putStoredScriptRequest.id()).build(); Request request = new Request(HttpPost.METHOD_NAME, endpoint); Params params = new Params(); params.withTimeout(putStoredScriptRequest.timeout()); @@ -718,7 +718,7 @@ static Request mtermVectors(MultiTermVectorsRequest mtvrequest) throws IOExcepti } static Request getScript(GetStoredScriptRequest getStoredScriptRequest) { - String endpoint = new EndpointBuilder().addPathPartAsIs("_scripts").addPathPart(getStoredScriptRequest.id()).build(); + String endpoint = new EndpointBuilder().addPathPartAsIs("_script").addPathPart(getStoredScriptRequest.id()).build(); Request request = new Request(HttpGet.METHOD_NAME, endpoint); Params params = new Params(); params.withMasterTimeout(getStoredScriptRequest.masterNodeTimeout()); @@ -727,7 +727,7 @@ static Request getScript(GetStoredScriptRequest getStoredScriptRequest) { } static Request deleteScript(DeleteStoredScriptRequest deleteStoredScriptRequest) { - String endpoint = new EndpointBuilder().addPathPartAsIs("_scripts").addPathPart(deleteStoredScriptRequest.id()).build(); + String endpoint = new EndpointBuilder().addPathPartAsIs("_script").addPathPart(deleteStoredScriptRequest.id()).build(); Request request = new Request(HttpDelete.METHOD_NAME, endpoint); Params params = new Params(); params.withTimeout(deleteStoredScriptRequest.timeout()); 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 e26de65edd2e0..f69a7f8210c8b 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 @@ -1587,7 +1587,7 @@ public void testPutScript() throws Exception { Request request = RequestConverters.putScript(putStoredScriptRequest); - assertThat(request.getEndpoint(), equalTo("/_scripts/" + id)); + assertThat(request.getEndpoint(), equalTo("/_script/" + id)); assertThat(request.getParameters(), equalTo(expectedParams)); assertNotNull(request.getEntity()); assertToXContentBody(putStoredScriptRequest, request.getEntity()); @@ -1611,7 +1611,7 @@ public void testGetScriptRequest() { setRandomMasterTimeout(getStoredScriptRequest, expectedParams); Request request = RequestConverters.getScript(getStoredScriptRequest); - assertThat(request.getEndpoint(), equalTo("/_scripts/" + getStoredScriptRequest.id())); + assertThat(request.getEndpoint(), equalTo("/_script/" + getStoredScriptRequest.id())); assertThat(request.getMethod(), equalTo(HttpGet.METHOD_NAME)); assertThat(request.getParameters(), equalTo(expectedParams)); assertThat(request.getEntity(), nullValue()); @@ -1625,7 +1625,7 @@ public void testDeleteScriptRequest() { setRandomMasterTimeout(deleteStoredScriptRequest, expectedParams); Request request = RequestConverters.deleteScript(deleteStoredScriptRequest); - assertThat(request.getEndpoint(), equalTo("/_scripts/" + deleteStoredScriptRequest.id())); + assertThat(request.getEndpoint(), equalTo("/_script/" + deleteStoredScriptRequest.id())); assertThat(request.getMethod(), equalTo(HttpDelete.METHOD_NAME)); assertThat(request.getParameters(), equalTo(expectedParams)); assertThat(request.getEntity(), nullValue()); 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 52b95d91b70a1..2f60f985a2ba6 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 @@ -800,7 +800,7 @@ public void testApiNamingConventions() throws Exception { "indices.get_upgrade", "indices.put_alias", "render_search_template", - "scripts_painless_execute", + "script_painless_execute", "indices.create_data_stream", "indices.get_data_stream", "indices.delete_data_stream", 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 242c2d9237dff..f814e58d8d771 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 @@ -56,7 +56,7 @@ public void testGetStoredScript() throws Exception { GetStoredScriptResponse getResponse = execute(getRequest, highLevelClient()::getScript, highLevelClient()::getScriptAsync); - assertThat(getResponse.getSource(), equalTo(scriptSource)); + assertThat(getResponse.getStoredScript("calculate-score"), equalTo(scriptSource)); } public void testDeleteStoredScript() throws Exception { @@ -92,7 +92,7 @@ public void testPutScript() throws Exception { new PutStoredScriptRequest(id, "score", new BytesArray("{}"), XContentType.JSON, scriptSource); assertAcked(execute(request, highLevelClient()::putScript, highLevelClient()::putScriptAsync)); - Map script = getAsMap("/_scripts/" + id); + Map script = getAsMap("/_script/" + id); assertThat(extractValue("_id", script), equalTo(id)); assertThat(extractValue("found", script), equalTo(true)); assertThat(extractValue("script.lang", script), equalTo("painless")); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/CRUDDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/CRUDDocumentationIT.java index c9de080870eed..50ddc43211c1c 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/CRUDDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/CRUDDocumentationIT.java @@ -285,7 +285,7 @@ public void testUpdate() throws Exception { IndexResponse indexResponse = client.index(indexRequest, RequestOptions.DEFAULT); assertSame(RestStatus.CREATED, indexResponse.status()); - Request request = new Request("POST", "/_scripts/increment-field"); + Request request = new Request("POST", "/_script/increment-field"); request.setJsonEntity(Strings.toString(JsonXContent.contentBuilder() .startObject() .startObject("script") diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SearchDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SearchDocumentationIT.java index 995a50508fcf3..f2f588a995973 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SearchDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SearchDocumentationIT.java @@ -961,7 +961,7 @@ public void onFailure(Exception e) { protected void registerQueryScript(RestClient restClient) throws IOException { // tag::register-script - Request scriptRequest = new Request("POST", "_scripts/title_search"); + Request scriptRequest = new Request("POST", "_script/title_search"); scriptRequest.setJsonEntity( "{" + " \"script\": {" + 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 fdaf92c00d19c..3c411687dc47d 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 @@ -94,7 +94,7 @@ public void testGetStoredScript() throws Exception { // end::get-stored-script-execute // tag::get-stored-script-response - StoredScriptSource storedScriptSource = getResponse.getSource(); // <1> + StoredScriptSource storedScriptSource = getResponse.getStoredScript("calculate-score"); // <1> String lang = storedScriptSource.getLang(); // <2> String source = storedScriptSource.getSource(); // <3> @@ -105,7 +105,7 @@ public void testGetStoredScript() throws Exception { // tag::get-stored-script-execute-listener ActionListener listener = - new ActionListener() { + new ActionListener<>() { @Override public void onResponse(GetStoredScriptResponse response) { // <1> @@ -168,7 +168,7 @@ public void testDeleteStoredScript() throws Exception { // tag::delete-stored-script-execute-listener ActionListener listener = - new ActionListener() { + new ActionListener<>() { @Override public void onResponse(AcknowledgedResponse response) { // <1> @@ -256,7 +256,7 @@ public void testPutScript() throws Exception { // tag::put-stored-script-execute-listener ActionListener listener = - new ActionListener() { + new ActionListener<>() { @Override public void onResponse(AcknowledgedResponse response) { // <1> @@ -301,7 +301,7 @@ public void onFailure(Exception e) { client.putScript(request, RequestOptions.DEFAULT); - Map script = getAsMap("/_scripts/id"); + Map script = getAsMap("/_script/id"); assertThat(extractValue("script.lang", script), equalTo("mustache")); assertThat(extractValue("script.source", script), equalTo("{\"query\":{\"match\":{\"title\":\"{{query_string}}\"}}}")); } diff --git a/distribution/archives/integ-test-zip/src/test/java/org/elasticsearch/test/rest/RequestsWithoutContentIT.java b/distribution/archives/integ-test-zip/src/test/java/org/elasticsearch/test/rest/RequestsWithoutContentIT.java index aa92110a1a43d..d4ee211914012 100644 --- a/distribution/archives/integ-test-zip/src/test/java/org/elasticsearch/test/rest/RequestsWithoutContentIT.java +++ b/distribution/archives/integ-test-zip/src/test/java/org/elasticsearch/test/rest/RequestsWithoutContentIT.java @@ -78,7 +78,7 @@ public void testSimulatePipelineMissingBody() throws IOException { public void testPutScriptMissingBody() throws IOException { ResponseException responseException = expectThrows(ResponseException.class, () -> - client().performRequest(new Request(randomBoolean() ? "POST" : "PUT", "/_scripts/lang"))); + client().performRequest(new Request(randomBoolean() ? "POST" : "PUT", "/_script/lang"))); assertResponseException(responseException, "request body is required"); } diff --git a/docs/painless/painless-guide/painless-execute-script.asciidoc b/docs/painless/painless-guide/painless-execute-script.asciidoc index 5bf09b9780c4e..56f980f695e83 100644 --- a/docs/painless/painless-guide/painless-execute-script.asciidoc +++ b/docs/painless/painless-guide/painless-execute-script.asciidoc @@ -32,7 +32,7 @@ Request: [source,console] ---------------------------------------------------------------- -POST /_scripts/painless/_execute +POST /_script/painless/_execute { "script": { "source": "params.count / params.total", @@ -80,7 +80,7 @@ PUT /my-index } } -POST /_scripts/painless/_execute +POST /_script/painless/_execute { "script": { "source": "doc['field'].value.length() <= params.max_length", @@ -138,7 +138,7 @@ PUT /my-index } -POST /_scripts/painless/_execute +POST /_script/painless/_execute { "script": { "source": "doc['rank'].value / params.max_rank", diff --git a/docs/reference/aggregations/bucket/range-aggregation.asciidoc b/docs/reference/aggregations/bucket/range-aggregation.asciidoc index 361e54b179be6..f98180f4190b2 100644 --- a/docs/reference/aggregations/bucket/range-aggregation.asciidoc +++ b/docs/reference/aggregations/bucket/range-aggregation.asciidoc @@ -200,7 +200,7 @@ It is also possible to use stored scripts. Here is a simple stored script: [source,console,id=range-aggregation-stored-script-example] -------------------------------------------------- -POST /_scripts/convert_currency +POST /_script/convert_currency { "script": { "lang": "painless", diff --git a/docs/reference/aggregations/bucket/terms-aggregation.asciidoc b/docs/reference/aggregations/bucket/terms-aggregation.asciidoc index 6030358512a4a..8677b445834ac 100644 --- a/docs/reference/aggregations/bucket/terms-aggregation.asciidoc +++ b/docs/reference/aggregations/bucket/terms-aggregation.asciidoc @@ -429,7 +429,7 @@ This will interpret the `script` parameter as an `inline` script with the defaul [source,console,id=terms-aggregation-stored-example] -------------------------------------------------- -POST /_scripts/my_script +POST /_script/my_script { "script": { "lang": "painless", diff --git a/docs/reference/scripting/using.asciidoc b/docs/reference/scripting/using.asciidoc index f2d635c1376e1..81cb23a357ac2 100644 --- a/docs/reference/scripting/using.asciidoc +++ b/docs/reference/scripting/using.asciidoc @@ -135,19 +135,19 @@ The same script in the normal form: === Stored scripts Scripts may be stored in and retrieved from the cluster state using the -`_scripts` end-point. +`_script` end-point. [float] ==== Request examples The following are examples of using a stored script that lives at -`/_scripts/{id}`. +`/_script/{id}`. First, create the script called `calculate-score` in the cluster state: [source,console] ----------------------------------- -POST _scripts/calculate-score +POST _script/calculate-score { "script": { "lang": "painless", @@ -159,11 +159,11 @@ POST _scripts/calculate-score You may also specify a context as part of the url path to compile a stored script against that specific context in the form of -`/_scripts/{id}/{context}`: +`/_script/{id}/{context}`: [source,console] ----------------------------------- -POST _scripts/calculate-score/score +POST _script/calculate-score/score { "script": { "lang": "painless", @@ -177,7 +177,7 @@ This same script can be retrieved with: [source,console] ----------------------------------- -GET _scripts/calculate-score +GET _script/calculate-score ----------------------------------- // TEST[continued] @@ -210,14 +210,14 @@ And deleted with: [source,console] ----------------------------------- -DELETE _scripts/calculate-score +DELETE _script/calculate-score ----------------------------------- // TEST[continued] [float] [[modules-scripting-search-templates]] === Search templates -You can also use the `_scripts` API to store **search templates**. Search +You can also use the `_script` API to store **search templates**. Search templates save specific <> with placeholder values, called template parameters. diff --git a/docs/reference/search/multi-search.asciidoc b/docs/reference/search/multi-search.asciidoc index 97adbbea22db7..00c40c6e29ca5 100644 --- a/docs/reference/search/multi-search.asciidoc +++ b/docs/reference/search/multi-search.asciidoc @@ -332,7 +332,7 @@ You can also create search templates: [source,console] ------------------------------------------ -POST /_scripts/my_template_1 +POST /_script/my_template_1 { "script": { "lang": "mustache", @@ -351,7 +351,7 @@ POST /_scripts/my_template_1 [source,console] ------------------------------------------ -POST /_scripts/my_template_2 +POST /_script/my_template_2 { "script": { "lang": "mustache", diff --git a/docs/reference/search/search-template.asciidoc b/docs/reference/search/search-template.asciidoc index 588ef1c5ee2a2..6e8d24ae3e5dd 100644 --- a/docs/reference/search/search-template.asciidoc +++ b/docs/reference/search/search-template.asciidoc @@ -110,7 +110,7 @@ You can store a search template using the stored scripts API. [source,console] ------------------------------------------ -POST _scripts/ +POST _script/ { "script": { "lang": "mustache", @@ -145,7 +145,7 @@ The template can be retrieved by calling [source,console] ------------------------------------------ -GET _scripts/ +GET _script/?new_format=true ------------------------------------------ // TEST[continued] @@ -154,15 +154,13 @@ The API returns the following result: [source,console-result] ------------------------------------------ { - "script" : { + "" : { "lang" : "mustache", "source" : "{\"query\":{\"match\":{\"title\":\"{{query_string}}\"}}}", "options": { "content_type" : "application/json; charset=UTF-8" } - }, - "_id": "", - "found": true + } } ------------------------------------------ @@ -171,7 +169,7 @@ This template can be deleted by calling [source,console] ------------------------------------------ -DELETE _scripts/ +DELETE _script/ ------------------------------------------ // TEST[continued] diff --git a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/SearchTemplateIT.java b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/SearchTemplateIT.java index 12ab62c5a712b..aef493605fcb1 100644 --- a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/SearchTemplateIT.java +++ b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/SearchTemplateIT.java @@ -170,7 +170,8 @@ public void testIndexedTemplateClient() throws Exception { GetStoredScriptResponse getResponse = client().admin().cluster() .prepareGetStoredScript("testTemplate").get(); - assertNotNull(getResponse.getSource()); + assertEquals(1, getResponse.getStoredScripts().size()); + assertNotNull(getResponse.getStoredScript("testTemplate")); BulkRequestBuilder bulkRequestBuilder = client().prepareBulk(); bulkRequestBuilder.add(client().prepareIndex("test").setId("1").setSource("{\"theField\":\"foo\"}", XContentType.JSON)); @@ -192,8 +193,8 @@ public void testIndexedTemplateClient() throws Exception { assertAcked(client().admin().cluster().prepareDeleteStoredScript("testTemplate")); - getResponse = client().admin().cluster().prepareGetStoredScript("testTemplate").get(); - assertNull(getResponse.getSource()); + final GetStoredScriptResponse response = client().admin().cluster().prepareGetStoredScript("testTemplate").get(); + assertNull(response.getStoredScript("testTemplate")); } public void testIndexedTemplate() throws Exception { @@ -291,7 +292,7 @@ public void testIndexedTemplateOverwrite() throws Exception { ); GetStoredScriptResponse getResponse = client().admin().cluster().prepareGetStoredScript("git01").get(); - assertNotNull(getResponse.getSource()); + assertNotNull(getResponse.getStoredScript("git01")); Map templateParams = new HashMap<>(); templateParams.put("P_Keyword1", "dev"); diff --git a/modules/lang-painless/build.gradle b/modules/lang-painless/build.gradle index c20887cea9ee8..3be651ab0f0b4 100644 --- a/modules/lang-painless/build.gradle +++ b/modules/lang-painless/build.gradle @@ -49,7 +49,7 @@ dependencyLicenses { restResources { restApi { includeCore '_common', 'cluster', 'nodes', 'indices', 'index', 'search', 'get', 'bulk', 'update', - 'scripts_painless_execute', 'put_script', 'delete_script' + 'script_painless_execute', 'put_script', 'delete_script' } } diff --git a/modules/lang-painless/src/doc/java/org/elasticsearch/painless/ContextDocGenerator.java b/modules/lang-painless/src/doc/java/org/elasticsearch/painless/ContextDocGenerator.java index f09dd4f521ade..e16be46c180b2 100644 --- a/modules/lang-painless/src/doc/java/org/elasticsearch/painless/ContextDocGenerator.java +++ b/modules/lang-painless/src/doc/java/org/elasticsearch/painless/ContextDocGenerator.java @@ -105,7 +105,7 @@ public static void main(String[] args) throws IOException { @SuppressForbidden(reason = "retrieving data from an internal API not exposed as part of the REST client") private static List getContextInfos() throws IOException { URLConnection getContextNames = new URL( - "http://" + System.getProperty("cluster.uri") + "/_scripts/painless/_context").openConnection(); + "http://" + System.getProperty("cluster.uri") + "/_script/painless/_context").openConnection(); XContentParser parser = JsonXContent.jsonXContent.createParser(null, null, getContextNames.getInputStream()); parser.nextToken(); parser.nextToken(); @@ -118,7 +118,7 @@ private static List getContextInfos() throws IOException { for (String contextName : contextNames) { URLConnection getContextInfo = new URL( - "http://" + System.getProperty("cluster.uri") + "/_scripts/painless/_context?context=" + contextName).openConnection(); + "http://" + System.getProperty("cluster.uri") + "/_script/painless/_context?context=" + contextName).openConnection(); parser = JsonXContent.jsonXContent.createParser(null, null, getContextInfo.getInputStream()); contextInfos.add(PainlessContextInfo.fromXContent(parser)); ((HttpURLConnection)getContextInfo).disconnect(); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/action/PainlessContextAction.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/action/PainlessContextAction.java index 12d676cfbba41..c963c4ff08f23 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/action/PainlessContextAction.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/action/PainlessContextAction.java @@ -57,15 +57,15 @@ * Internal REST API for querying context information about Painless whitelists. * Commands include the following: *
    - *
  • GET /_scripts/painless/_context -- retrieves a list of contexts
  • - *
  • GET /_scripts/painless/_context?context=%name% -- + *
  • GET /_script/painless/_context -- retrieves a list of contexts
  • + *
  • GET /_script/painless/_context?context=%name% -- * retrieves all available information about the API for this specific context
  • *
*/ public class PainlessContextAction extends ActionType { public static final PainlessContextAction INSTANCE = new PainlessContextAction(); - private static final String NAME = "cluster:admin/scripts/painless/context"; + private static final String NAME = "cluster:admin/script/painless/context"; private static final String SCRIPT_CONTEXT_NAME_PARAM = "context"; @@ -195,12 +195,12 @@ public static class RestAction extends BaseRestHandler { @Override public List routes() { - return List.of(new Route(GET, "/_scripts/painless/_context")); + return List.of(new Route(GET, "/_script/painless/_context")); } @Override public String getName() { - return "_scripts_painless_context"; + return "_script_painless_context"; } @Override diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/action/PainlessExecuteAction.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/action/PainlessExecuteAction.java index 21059f2112457..8caf669d04bcf 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/action/PainlessExecuteAction.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/action/PainlessExecuteAction.java @@ -568,13 +568,13 @@ public static class RestAction extends BaseRestHandler { @Override public List routes() { return List.of( - new Route(GET, "/_scripts/painless/_execute"), - new Route(POST, "/_scripts/painless/_execute")); + new Route(GET, "/_script/painless/_execute"), + new Route(POST, "/_script/painless/_execute")); } @Override public String getName() { - return "_scripts_painless_execute"; + return "_script_painless_execute"; } @Override diff --git a/modules/lang-painless/src/test/resources/rest-api-spec/api/scripts_painless_context.json b/modules/lang-painless/src/test/resources/rest-api-spec/api/script_painless_context.json similarity index 86% rename from modules/lang-painless/src/test/resources/rest-api-spec/api/scripts_painless_context.json rename to modules/lang-painless/src/test/resources/rest-api-spec/api/script_painless_context.json index 6b16705c5e205..0a2286e0b9aac 100644 --- a/modules/lang-painless/src/test/resources/rest-api-spec/api/scripts_painless_context.json +++ b/modules/lang-painless/src/test/resources/rest-api-spec/api/script_painless_context.json @@ -1,5 +1,5 @@ { - "scripts_painless_context": { + "script_painless_context": { "stability": "experimental", "documentation": { "url": "https://www.elastic.co/guide/en/elasticsearch/painless/master/painless-contexts.html", @@ -8,7 +8,7 @@ "url": { "paths": [ { - "path": "/_scripts/painless/_context", + "path": "/_script/painless/_context", "methods": ["GET"], "parts": {} } diff --git a/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/16_update2.yml b/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/16_update2.yml index 999733ff14be7..b17e7f8e31636 100644 --- a/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/16_update2.yml +++ b/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/16_update2.yml @@ -12,16 +12,13 @@ - do: get_script: id: "1" - - match: { found: true } - - match: { _id: "1" } - - match: { "script": {"lang": "painless", "source": "_score * doc['myParent.weight'].value"} } + new_format: true + - match: { "1" : { "lang": "painless", "source": "_score * doc['myParent.weight'].value"} } - do: catch: missing get_script: id: "2" - - match: { found: false } - - match: { _id: "2" } - is_false: script - do: diff --git a/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/70_execute_painless_scripts.yml b/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/70_execute_painless_scripts.yml index 5a994425c5dc2..997e79914d33b 100644 --- a/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/70_execute_painless_scripts.yml +++ b/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/70_execute_painless_scripts.yml @@ -15,7 +15,7 @@ setup: --- "Execute with defaults": - do: - scripts_painless_execute: + script_painless_execute: body: script: source: "params.count / params.total" @@ -27,7 +27,7 @@ setup: --- "Execute with painless_test context": - do: - scripts_painless_execute: + script_painless_execute: body: script: source: "params.var1 - params.var2" @@ -40,7 +40,7 @@ setup: --- "Execute with filter context": - do: - scripts_painless_execute: + script_painless_execute: body: script: source: "doc['field'].value.length() <= params.max_length" @@ -56,7 +56,7 @@ setup: --- "Execute with score context": - do: - scripts_painless_execute: + script_painless_execute: body: script: source: "doc['rank'].value / params.max_rank" diff --git a/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/70_get_all.yml b/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/70_get_all.yml new file mode 100644 index 0000000000000..6cb17544d94cc --- /dev/null +++ b/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/70_get_all.yml @@ -0,0 +1,26 @@ +--- +"Get all scripts": + + - skip: + version: " - 7.99.99" + reason: this uses a new API that has been added in 8.0 + + - do: + put_script: + id: "1" + body: { "script": { "lang": "painless", "source": "_score * doc['myParent.weight'].value" } } + - match: { acknowledged: true } + + - do: + put_script: + id: "foo" + body: { "script": { "lang": "painless", "source": "_score * 9.9" } } + - match: { acknowledged: true } + + - do: + get_script: + new_format: true + + - length: { $body: 2 } + - match: { 1: {"lang": "painless", "source": "_score * doc['myParent.weight'].value"} } + - match: { foo: {"lang": "painless", "source": "_score * 9.9"} } diff --git a/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/71_context_api.yml b/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/71_context_api.yml index 0413661fc586c..e3c01ffe67ab5 100644 --- a/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/71_context_api.yml +++ b/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/71_context_api.yml @@ -1,13 +1,13 @@ "Action to list contexts": - do: - scripts_painless_context: {} + script_painless_context: {} - match: { contexts.0: aggregation_selector} - match: { contexts.22: update} --- "Action to get all API values for score context": - do: - scripts_painless_context: + script_painless_context: context: score - match: { name: score } - match: { classes.6.name: java.lang.Appendable } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/delete_script.json b/rest-api-spec/src/main/resources/rest-api-spec/api/delete_script.json index b38b97ae57c2e..3d199feefafb7 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/delete_script.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/delete_script.json @@ -8,7 +8,7 @@ "url":{ "paths":[ { - "path":"/_scripts/{id}", + "path":"/_script/{id}", "methods":[ "DELETE" ], diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/get_script.json b/rest-api-spec/src/main/resources/rest-api-spec/api/get_script.json index 14307bea2ef0b..f32d64bae0986 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/get_script.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/get_script.json @@ -8,7 +8,13 @@ "url":{ "paths":[ { - "path":"/_scripts/{id}", + "path":"/_script", + "methods":[ + "GET" + ] + }, + { + "path":"/_script/{id}", "methods":[ "GET" ], @@ -25,6 +31,10 @@ "master_timeout":{ "type":"time", "description":"Specify timeout for connection to master" + }, + "new_format": { + "type" : "boolean", + "description" : "Use the new format" } } } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/put_script.json b/rest-api-spec/src/main/resources/rest-api-spec/api/put_script.json index 750f7fdf4eb62..c3f69f1bc2472 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/put_script.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/put_script.json @@ -8,7 +8,7 @@ "url":{ "paths":[ { - "path":"/_scripts/{id}", + "path":"/_script/{id}", "methods":[ "PUT", "POST" @@ -21,7 +21,7 @@ } }, { - "path":"/_scripts/{id}/{context}", + "path":"/_script/{id}/{context}", "methods":[ "PUT", "POST" diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/scripts_painless_execute.json b/rest-api-spec/src/main/resources/rest-api-spec/api/script_painless_execute.json similarity index 85% rename from rest-api-spec/src/main/resources/rest-api-spec/api/scripts_painless_execute.json rename to rest-api-spec/src/main/resources/rest-api-spec/api/script_painless_execute.json index 9f761fb452ba1..90a6431391aff 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/scripts_painless_execute.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/script_painless_execute.json @@ -1,5 +1,5 @@ { - "scripts_painless_execute":{ + "script_painless_execute":{ "documentation":{ "url":"https://www.elastic.co/guide/en/elasticsearch/painless/master/painless-execute-api.html", "description":"Allows an arbitrary script to be executed and a result to be returned" @@ -8,7 +8,7 @@ "url":{ "paths":[ { - "path":"/_scripts/painless/_execute", + "path":"/_script/painless/_execute", "methods":[ "GET", "POST" diff --git a/server/src/internalClusterTest/java/org/elasticsearch/script/StoredScriptsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/script/StoredScriptsIT.java index a1a82c88819a2..2f08845d9dfd1 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/script/StoredScriptsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/script/StoredScriptsIT.java @@ -18,6 +18,7 @@ */ package org.elasticsearch.script; +import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptResponse; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentType; @@ -53,16 +54,15 @@ public void testBasics() { assertAcked(client().admin().cluster().preparePutStoredScript() .setId("foobar") .setContent(new BytesArray("{\"script\": {\"lang\": \"" + LANG + "\", \"source\": \"1\"} }"), XContentType.JSON)); - String script = client().admin().cluster().prepareGetStoredScript("foobar") - .get().getSource().getSource(); + String script = client().admin().cluster().prepareGetStoredScript("foobar").get().getStoredScript("foobar").getSource(); assertNotNull(script); assertEquals("1", script); assertAcked(client().admin().cluster().prepareDeleteStoredScript() .setId("foobar")); - StoredScriptSource source = client().admin().cluster().prepareGetStoredScript("foobar") - .get().getSource(); - assertNull(source); + GetStoredScriptResponse response = client().admin().cluster().prepareGetStoredScript("foobar") + .get(); + assertEquals(0, response.getStoredScripts().size()); IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> client().admin().cluster().preparePutStoredScript() .setId("id#") @@ -71,6 +71,28 @@ public void testBasics() { assertEquals("Validation Failed: 1: id cannot contain '#' for stored script;", e.getMessage()); } + public void testGetAllScripts() { + assertAcked(client().admin().cluster().preparePutStoredScript() + .setId("foobar") + .setContent(new BytesArray("{\"script\": {\"lang\": \"" + LANG + "\", \"source\": \"1\"} }"), XContentType.JSON)); + + assertAcked(client().admin().cluster().preparePutStoredScript() + .setId("1") + .setContent(new BytesArray("{\"script\": {\"lang\": \"" + LANG + "\", \"source\": \"9.9\"} }"), + XContentType.JSON)); + + Map storedScripts = client().admin().cluster().prepareGetStoredScript().get().getStoredScripts(); + assertEquals(2, storedScripts.size()); + + StoredScriptSource source = storedScripts.get("foobar"); + assertNotNull(source); + assertEquals(LANG, source.getLang()); + + source = storedScripts.get("1"); + assertNotNull(source); + assertEquals("9.9", source.getSource()); + } + public void testMaxScriptSize() { IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> client().admin().cluster().preparePutStoredScript() .setId("foobar") diff --git a/server/src/internalClusterTest/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java b/server/src/internalClusterTest/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java index f92c06fc64398..a273acb6a6d88 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java @@ -717,7 +717,7 @@ public void testIncludeGlobalState() throws Exception { if (testScript) { logger.info("--> check that script is restored"); GetStoredScriptResponse getStoredScriptResponse = client().admin().cluster().prepareGetStoredScript("foobar").get(); - assertNotNull(getStoredScriptResponse.getSource()); + assertNotNull(getStoredScriptResponse.getStoredScript("foobar")); } createIndex("test-idx"); @@ -766,7 +766,8 @@ public void testIncludeGlobalState() throws Exception { getIndexTemplatesResponse = client().admin().indices().prepareGetTemplates().get(); assertIndexTemplateMissing(getIndexTemplatesResponse, "test-template"); assertFalse(client().admin().cluster().prepareGetPipeline("barbaz").get().isFound()); - assertNull(client().admin().cluster().prepareGetStoredScript("foobar").get().getSource()); + final GetStoredScriptResponse getStoredScriptResponse = client().admin().cluster().prepareGetStoredScript("foobar").get(); + assertNull(getStoredScriptResponse.getStoredScript("foobar")); assertThat(client.prepareSearch("test-idx").setSize(0).get().getHits().getTotalHits().value, equalTo(100L)); } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/GetStoredScriptRequest.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/GetStoredScriptRequest.java index f27c14cc8fb0a..9238b95f9f31d 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/GetStoredScriptRequest.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/GetStoredScriptRequest.java @@ -19,8 +19,10 @@ package org.elasticsearch.action.admin.cluster.storedscripts; +import org.elasticsearch.Version; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.support.master.MasterNodeReadRequest; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; @@ -30,54 +32,88 @@ public class GetStoredScriptRequest extends MasterNodeReadRequest { - protected String id; + private String[] ids; - GetStoredScriptRequest() { - super(); + public GetStoredScriptRequest() { + this(new String[]{}); } - public GetStoredScriptRequest(String id) { - super(); - - this.id = id; + public GetStoredScriptRequest(String... ids) { + this.ids = ids; } public GetStoredScriptRequest(StreamInput in) throws IOException { super(in); - id = in.readString(); + if (in.getVersion().onOrAfter(Version.V_7_9_0)) { + this.ids = in.readStringArray(); + } else { + this.ids = new String[] { in.readString() }; + } } - @Override - public void writeTo(StreamOutput out) throws IOException { - super.writeTo(out); - out.writeString(id); + /** + * Returns the ids of the scripts. + */ + public String[] ids() { + return this.ids; } - @Override - public ActionRequestValidationException validate() { - ActionRequestValidationException validationException = null; - - if (id == null || id.isEmpty()) { - validationException = addValidationError("must specify id for stored script", validationException); - } else if (id.contains("#")) { - validationException = addValidationError("id cannot contain '#' for stored script", validationException); - } + public GetStoredScriptRequest ids(String... ids) { + this.ids = ids; - return validationException; + return this; } + /** + * @deprecated - Needed for backwards compatibility. + * Use {@link #ids()} instead + * + * Return the only script + */ + @Deprecated public String id() { - return id; + assert(ids.length == 1); + return ids[0]; } + /** + * @deprecated - Needed for backwards compatibility. + * Set the script ids param instead + */ + @Deprecated public GetStoredScriptRequest id(String id) { - this.id = id; + this.ids = new String[] { id }; return this; } + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + if (out.getVersion().onOrAfter(Version.V_7_9_0)) { + out.writeStringArray(ids); + } else { + out.writeString(ids[0]); + } + } + + @Override + public ActionRequestValidationException validate() { + ActionRequestValidationException validationException = null; + if (ids == null) { + validationException = addValidationError("ids is null or empty", validationException); + } else { + for (String name : ids) { + if (name == null || !Strings.hasText(name)) { + validationException = addValidationError("id is missing", validationException); + } + } + } + return validationException; + } + @Override public String toString() { - return "get script [" + id + "]"; + return "get script[ " + String.join(", ", ids) + "]"; } } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/GetStoredScriptRequestBuilder.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/GetStoredScriptRequestBuilder.java index 75708c929f584..cac4cbc6ea5cc 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/GetStoredScriptRequestBuilder.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/GetStoredScriptRequestBuilder.java @@ -25,14 +25,18 @@ public class GetStoredScriptRequestBuilder extends MasterNodeReadOperationRequestBuilder { - public GetStoredScriptRequestBuilder(ElasticsearchClient client, GetStoredScriptAction action) { super(client, action, new GetStoredScriptRequest()); } + public GetStoredScriptRequestBuilder setIds(String... ids) { + request.ids(ids); + return this; + } + + @Deprecated public GetStoredScriptRequestBuilder setId(String id) { request.id(id); return this; } - } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/GetStoredScriptResponse.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/GetStoredScriptResponse.java index e0058bba953f0..eaae803a1dabf 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/GetStoredScriptResponse.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/GetStoredScriptResponse.java @@ -19,29 +19,31 @@ 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.ConstructingObjectParser; import org.elasticsearch.common.xcontent.ObjectParser; -import org.elasticsearch.common.xcontent.StatusToXContentObject; +import org.elasticsearch.common.xcontent.ToXContentObject; 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.HashMap; +import java.util.Map; import java.util.Objects; import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; -public class GetStoredScriptResponse extends ActionResponse implements StatusToXContentObject { +public class GetStoredScriptResponse extends ActionResponse implements ToXContentObject { - 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 ParseField _ID_PARSE_FIELD = new ParseField("_id"); + private static final ParseField FOUND_PARSE_FIELD = new ParseField("found"); + private static final ParseField SCRIPT = new ParseField("script"); private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("GetStoredScriptResponse", @@ -62,45 +64,116 @@ public class GetStoredScriptResponse extends ActionResponse implements StatusToX SCRIPT, ObjectParser.ValueType.OBJECT); } - private String id; - private StoredScriptSource source; + private final Map storedScripts; + private final String[] requestedIds; - public GetStoredScriptResponse(StreamInput in) throws IOException { + GetStoredScriptResponse(StreamInput in) throws IOException { super(in); - if (in.readBoolean()) { - source = new StoredScriptSource(in); + if (in.getVersion().onOrAfter(Version.V_7_9_0)) { + storedScripts = in.readMap(StreamInput::readString, StoredScriptSource::new); } else { - source = null; + StoredScriptSource source; + if (in.readBoolean()) { + source = new StoredScriptSource(in); + } else { + source = null; + } + String id = in.readString(); + storedScripts = new HashMap<>(1); + storedScripts.put(id, source); } - id = in.readString(); + requestedIds = new String[0]; } GetStoredScriptResponse(String id, StoredScriptSource source) { - this.id = id; - this.source = source; + this.storedScripts = new HashMap<>(); + storedScripts.put(id, source); + requestedIds = new String[]{ id }; } - public String getId() { - return id; + GetStoredScriptResponse(String[] requestedIds, Map storedScripts) { + this.requestedIds = requestedIds; + this.storedScripts = storedScripts; + } + + public Map getStoredScripts() { + return storedScripts; + } + + /** + * Helper method to return a single stored script + * + * @param id the id of the script + * @return the script source or null if not found + */ + public StoredScriptSource getStoredScript(String id) { + return storedScripts.get(id); } /** + * @deprecated - Needed for backwards compatibility. + * Use {@link #getStoredScript(String)} ()} instead + * * @return if a stored script and if not found null */ + @Deprecated public StoredScriptSource getSource() { - return source; + return storedScripts.entrySet().iterator().next().getValue(); } @Override - public RestStatus status() { - return source != null ? RestStatus.OK : RestStatus.NOT_FOUND; + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + boolean isSingleId = requestedIds.length == 1 && storedScripts.size() == 1; + if (!params.paramAsBoolean("new_format", false) && isSingleId) { + return toXContentPre80(builder, params); + } + + builder.startObject(); + Map storedScripts = getStoredScripts(); + if (storedScripts != null) { + for (Map.Entry storedScript : storedScripts.entrySet()) { + builder.field(storedScript.getKey()); + storedScript.getValue().toXContent(builder, params); + } + } + builder.endObject(); + return builder; } - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + /** + * The original format is the default prior to 8.0 and needed for backwards compatibility + * @see #fromXContentNewFormat(XContentParser) + */ + @Deprecated + public static GetStoredScriptResponse fromXContent(XContentParser parser) throws IOException { + return PARSER.parse(parser, null); + } + + public static GetStoredScriptResponse fromXContentNewFormat(XContentParser parser) throws IOException { + final Map storedScripts = new HashMap<>(); + for (XContentParser.Token token = parser.nextToken(); token != XContentParser.Token.END_OBJECT; token = parser.nextToken()) { + if (token == XContentParser.Token.FIELD_NAME) { + String name = parser.currentName(); + assert parser.nextToken() == XContentParser.Token.START_OBJECT; + StoredScriptSource storedScriptSource = StoredScriptSource.fromXContent(parser, false); + storedScripts.put(name, storedScriptSource); + } + } + return new GetStoredScriptResponse(storedScripts.keySet().toArray(String[]::new), storedScripts); + } + + @Deprecated + private XContentBuilder toXContentPre80(XContentBuilder builder, Params params) throws IOException { builder.startObject(); + String id = requestedIds[0]; + StoredScriptSource source = null; + if (!storedScripts.isEmpty()) { + Map.Entry entry = storedScripts.entrySet().iterator().next(); + source = entry.getValue(); + } + builder.field(_ID_PARSE_FIELD.getPreferredName(), id); builder.field(FOUND_PARSE_FIELD.getPreferredName(), source != null); if (source != null) { @@ -112,19 +185,29 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws return builder; } - public static GetStoredScriptResponse fromXContent(XContentParser parser) throws IOException { - return PARSER.parse(parser, null); - } - @Override public void writeTo(StreamOutput out) throws IOException { - if (source == null) { - out.writeBoolean(false); + if (out.getVersion().onOrAfter(Version.V_7_9_0)) { + if (storedScripts == null ) { + out.writeVInt(0); + return; + } + + out.writeVInt(storedScripts.size()); + for (Map.Entry storedScript : storedScripts.entrySet()) { + out.writeString(storedScript.getKey()); + storedScript.getValue().writeTo(out); + } } else { - out.writeBoolean(true); - source.writeTo(out); + Map.Entry entry = storedScripts.entrySet().iterator().next(); + if (entry.getValue() == null) { + out.writeBoolean(false); + } else { + out.writeBoolean(true); + entry.getValue().writeTo(out); + } + out.writeString(entry.getKey()); } - out.writeString(id); } @Override @@ -132,12 +215,11 @@ 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); + return Objects.equals(storedScripts, that.storedScripts); } @Override public int hashCode() { - return Objects.hash(id, source); + return storedScripts.hashCode(); } } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/TransportGetStoredScriptAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/TransportGetStoredScriptAction.java index 4de6dfef713dc..6a12672e3650d 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/TransportGetStoredScriptAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/TransportGetStoredScriptAction.java @@ -29,12 +29,16 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.regex.Regex; import org.elasticsearch.script.ScriptService; +import org.elasticsearch.script.StoredScriptSource; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; public class TransportGetStoredScriptAction extends TransportMasterNodeReadAction { @@ -63,7 +67,31 @@ protected GetStoredScriptResponse read(StreamInput in) throws IOException { @Override protected void masterOperation(Task task, GetStoredScriptRequest request, ClusterState state, ActionListener listener) throws Exception { - listener.onResponse(new GetStoredScriptResponse(request.id(), scriptService.getStoredScript(state, request))); + Map results; + + Map storedScripts = scriptService.getStoredScripts(state); + // If we did not ask for a specific name, then we return all templates + if (request.ids().length == 0) { + results = storedScripts; + } else { + results = new HashMap<>(); + } + + if (storedScripts != null) { + for (String name : request.ids()) { + if (Regex.isSimpleMatchPattern(name)) { + for (Map.Entry entry : storedScripts.entrySet()) { + if (Regex.simpleMatch(name, entry.getKey())) { + results.put(entry.getKey(), entry.getValue()); + } + } + } else if (storedScripts.containsKey(name)) { + results.put(name, storedScripts.get(name)); + } + } + } + + listener.onResponse(new GetStoredScriptResponse(request.ids(), results)); } @Override diff --git a/server/src/main/java/org/elasticsearch/client/ClusterAdminClient.java b/server/src/main/java/org/elasticsearch/client/ClusterAdminClient.java index 807c242fd9f46..e32eaaa215807 100644 --- a/server/src/main/java/org/elasticsearch/client/ClusterAdminClient.java +++ b/server/src/main/java/org/elasticsearch/client/ClusterAdminClient.java @@ -705,9 +705,9 @@ public interface ClusterAdminClient extends ElasticsearchClient { GetStoredScriptRequestBuilder prepareGetStoredScript(); /** - * Get a script from the cluster state + * Get scripts from the cluster state */ - GetStoredScriptRequestBuilder prepareGetStoredScript(String id); + GetStoredScriptRequestBuilder prepareGetStoredScript(String... name); /** * Get a script from the cluster state diff --git a/server/src/main/java/org/elasticsearch/client/support/AbstractClient.java b/server/src/main/java/org/elasticsearch/client/support/AbstractClient.java index b754198aed2e8..1ca2d46d043da 100644 --- a/server/src/main/java/org/elasticsearch/client/support/AbstractClient.java +++ b/server/src/main/java/org/elasticsearch/client/support/AbstractClient.java @@ -1149,8 +1149,8 @@ public GetStoredScriptRequestBuilder prepareGetStoredScript() { } @Override - public GetStoredScriptRequestBuilder prepareGetStoredScript(String id) { - return prepareGetStoredScript().setId(id); + public GetStoredScriptRequestBuilder prepareGetStoredScript(String... ids) { + return prepareGetStoredScript().setIds(ids); } @Override diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestDeleteStoredScriptAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestDeleteStoredScriptAction.java index ce817eca07545..ee5fabdf1cfe4 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestDeleteStoredScriptAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestDeleteStoredScriptAction.java @@ -33,7 +33,9 @@ public class RestDeleteStoredScriptAction extends BaseRestHandler { @Override public List routes() { - return List.of(new Route(DELETE, "/_scripts/{id}")); + return List.of(new ReplacedRoute( + DELETE, "/_script/{id}", + DELETE, "/_scripts/{id}")); } @Override diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestGetStoredScriptAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestGetStoredScriptAction.java index 873910d8d683d..38afab5d0406e 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestGetStoredScriptAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestGetStoredScriptAction.java @@ -19,33 +19,69 @@ 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.Strings; +import org.elasticsearch.common.settings.Settings; import org.elasticsearch.rest.BaseRestHandler; import org.elasticsearch.rest.RestRequest; -import org.elasticsearch.rest.action.RestStatusToXContentListener; +import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.rest.action.RestToXContentListener; +import org.elasticsearch.script.StoredScriptSource; import java.io.IOException; +import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; import static org.elasticsearch.rest.RestRequest.Method.GET; public class RestGetStoredScriptAction extends BaseRestHandler { + private static final String NEW_FORMAT = "new_format"; + private static final Set allowedResponseParameters = Collections + .unmodifiableSet(Stream.concat(Collections.singleton(NEW_FORMAT).stream(), Settings.FORMAT_PARAMS.stream()) + .collect(Collectors.toSet())); + @Override public List routes() { - return List.of(new Route(GET, "/_scripts/{id}")); + return List.of( + new Route(GET, "/_script"), + new ReplacedRoute( + GET, "/_script/{id}", + GET, "/_scripts/{id}")); } @Override public String getName() { - return "get_stored_scripts_action"; + return "get_stored_script_action"; } @Override public RestChannelConsumer prepareRequest(final RestRequest request, NodeClient client) throws IOException { - String id = request.param("id"); - GetStoredScriptRequest getRequest = new GetStoredScriptRequest(id); + final String[] names = Strings.splitStringByCommaToArray(request.param("id")); + + GetStoredScriptRequest getRequest = new GetStoredScriptRequest(names); getRequest.masterNodeTimeout(request.paramAsTime("master_timeout", getRequest.masterNodeTimeout())); - return channel -> client.admin().cluster().getStoredScript(getRequest, new RestStatusToXContentListener<>(channel)); + + final boolean implicitAll = getRequest.ids().length == 0; + + return channel -> client.admin().cluster().getStoredScript(getRequest, new RestToXContentListener<>(channel) { + @Override + protected RestStatus getStatus(final GetStoredScriptResponse response) + { + Map storedScripts = response.getStoredScripts(); + final boolean scriptExists = storedScripts != null && !storedScripts.isEmpty(); + return (scriptExists || implicitAll) ? RestStatus.OK : RestStatus.NOT_FOUND; + } + }); + } + + @Override + protected Set responseParams() { + return allowedResponseParameters; } } diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestPutStoredScriptAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestPutStoredScriptAction.java index 38a2227784222..c8b3d35ed16c1 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestPutStoredScriptAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestPutStoredScriptAction.java @@ -38,10 +38,18 @@ public class RestPutStoredScriptAction extends BaseRestHandler { @Override public List routes() { return List.of( - new Route(POST, "/_scripts/{id}"), - new Route(PUT, "/_scripts/{id}"), - new Route(POST, "/_scripts/{id}/{context}"), - new Route(PUT, "/_scripts/{id}/{context}")); + new ReplacedRoute( + POST, "/_script/{id}", + POST, "/_scripts/{id}"), + new ReplacedRoute( + PUT, "/_script/{id}", + PUT, "/_scripts/{id}"), + new ReplacedRoute( + POST, "/_script/{id}/{context}", + POST, "/_scripts/{id}/{context}"), + new ReplacedRoute( + PUT, "/_script/{id}/{context}", + PUT, "/_scripts/{id}/{context}")); } @Override diff --git a/server/src/main/java/org/elasticsearch/script/ScriptService.java b/server/src/main/java/org/elasticsearch/script/ScriptService.java index d1b4c3ddc1a2c..531da6dbdc1d0 100644 --- a/server/src/main/java/org/elasticsearch/script/ScriptService.java +++ b/server/src/main/java/org/elasticsearch/script/ScriptService.java @@ -572,6 +572,16 @@ public ScriptLanguagesInfo getScriptLanguages() { return new ScriptLanguagesInfo(types, languageContexts); } + public Map getStoredScripts(ClusterState state) { + ScriptMetadata scriptMetadata = state.metadata().custom(ScriptMetadata.TYPE); + + if (scriptMetadata != null) { + return scriptMetadata.getStoredScripts(); + } else { + return null; + } + } + public ScriptStats stats() { return cacheHolder.get().stats(); } diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/storedscripts/GetStoredScriptResponseTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/storedscripts/GetStoredScriptResponseTests.java index 8bea7fecc571a..1512c135f98d9 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/storedscripts/GetStoredScriptResponseTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/storedscripts/GetStoredScriptResponseTests.java @@ -26,6 +26,7 @@ import java.io.IOException; import java.util.Collections; +import java.util.HashMap; import java.util.Map; import java.util.function.Predicate; @@ -38,7 +39,10 @@ protected GetStoredScriptResponse doParseInstance(XContentParser parser) throws @Override protected GetStoredScriptResponse createTestInstance() { - return new GetStoredScriptResponse(randomAlphaOfLengthBetween(1, 10), randomScriptSource()); + Map storedScripts = new HashMap<>(); + storedScripts.put(randomAlphaOfLengthBetween(1, 10), randomScriptSource()); + + return new GetStoredScriptResponse(storedScripts.keySet().toArray(String[]::new), storedScripts); } @Override diff --git a/server/src/test/java/org/elasticsearch/script/ScriptMetadataTests.java b/server/src/test/java/org/elasticsearch/script/ScriptMetadataTests.java index 736ee8c2811c5..70247bb14de87 100644 --- a/server/src/test/java/org/elasticsearch/script/ScriptMetadataTests.java +++ b/server/src/test/java/org/elasticsearch/script/ScriptMetadataTests.java @@ -33,6 +33,7 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.util.Collections; +import java.util.Map; public class ScriptMetadataTests extends AbstractSerializingTestCase { @@ -42,7 +43,7 @@ public void testFromXContentLoading() throws Exception { builder.startObject().field("lang0#id0", "script0").field("lang1#id0", "script1").endObject(); XContentParser parser0 = XContentType.JSON.xContent() .createParser(NamedXContentRegistry.EMPTY, DeprecationHandler.THROW_UNSUPPORTED_OPERATION, - BytesReference.bytes(builder).streamInput()); + BytesReference.bytes(builder).streamInput()); expectThrows(IllegalArgumentException.class, () -> ScriptMetadata.fromXContent(parser0)); // failure to load a new namespace script and old namespace script with the same id but different langs @@ -51,7 +52,7 @@ public void testFromXContentLoading() throws Exception { .startObject("id0").field("lang", "lang1").field("source", "script1").endObject().endObject(); XContentParser parser1 = XContentType.JSON.xContent() .createParser(NamedXContentRegistry.EMPTY, DeprecationHandler.THROW_UNSUPPORTED_OPERATION, - BytesReference.bytes(builder).streamInput()); + BytesReference.bytes(builder).streamInput()); expectThrows(IllegalArgumentException.class, () -> ScriptMetadata.fromXContent(parser1)); // failure to load a new namespace script and old namespace script with the same id but different langs with additional scripts @@ -61,7 +62,7 @@ public void testFromXContentLoading() throws Exception { .startObject("id0").field("lang", "lang1").field("source", "script1").endObject().endObject(); XContentParser parser2 = XContentType.JSON.xContent() .createParser(NamedXContentRegistry.EMPTY, DeprecationHandler.THROW_UNSUPPORTED_OPERATION, - BytesReference.bytes(builder).streamInput()); + BytesReference.bytes(builder).streamInput()); expectThrows(IllegalArgumentException.class, () -> ScriptMetadata.fromXContent(parser2)); // okay to load the same script from the new and old namespace if the lang is the same @@ -70,7 +71,7 @@ public void testFromXContentLoading() throws Exception { .startObject("id0").field("lang", "lang0").field("source", "script1").endObject().endObject(); XContentParser parser3 = XContentType.JSON.xContent() .createParser(NamedXContentRegistry.EMPTY, DeprecationHandler.THROW_UNSUPPORTED_OPERATION, - BytesReference.bytes(builder).streamInput()); + BytesReference.bytes(builder).streamInput()); ScriptMetadata.fromXContent(parser3); } @@ -94,6 +95,29 @@ public void testGetScript() throws Exception { assertEquals("{\"field\":\"value\"}", scriptMetadata.getStoredScript("source_template").getSource()); } + public void testGetScripts() throws Exception { + ScriptMetadata.Builder builder = new ScriptMetadata.Builder(null); + + XContentBuilder sourceBuilder = XContentFactory.jsonBuilder(); + sourceBuilder.startObject().startObject("script") + .field("lang", "_lang") + .startObject("source").field("field", "value").endObject() + .endObject().endObject(); + builder.storeScript("source_template", StoredScriptSource.parse(BytesReference.bytes(sourceBuilder), + sourceBuilder.contentType())); + + sourceBuilder = XContentFactory.jsonBuilder(); + sourceBuilder.startObject().startObject("script").field("lang", "_lang").field("source", "_source").endObject().endObject(); + builder.storeScript("script", StoredScriptSource.parse(BytesReference.bytes(sourceBuilder), sourceBuilder.contentType())); + + ScriptMetadata scriptMetadata = builder.build(); + Map storedScripts = scriptMetadata.getStoredScripts(); + + assertEquals(2, storedScripts.size()); + assertEquals("_source", storedScripts.get("script").getSource()); + assertEquals("{\"field\":\"value\"}", storedScripts.get("source_template").getSource()); + } + public void testDiff() throws Exception { ScriptMetadata.Builder builder = new ScriptMetadata.Builder(null); builder.storeScript("1", StoredScriptSource.parse( @@ -196,8 +220,8 @@ public void testOldStyleDropped() throws IOException { builder.endObject(); XContentParser parser = XContentType.JSON.xContent() - .createParser(NamedXContentRegistry.EMPTY, DeprecationHandler.THROW_UNSUPPORTED_OPERATION, - BytesReference.bytes(builder).streamInput()); + .createParser(NamedXContentRegistry.EMPTY, DeprecationHandler.THROW_UNSUPPORTED_OPERATION, + BytesReference.bytes(builder).streamInput()); ScriptMetadata smd = ScriptMetadata.fromXContent(parser); assertNull(smd.getStoredScript("painless#test")); assertNull(smd.getStoredScript("lang#test")); diff --git a/server/src/test/resources/org/elasticsearch/action/admin/cluster/storedscripts/10_get_script.yml b/server/src/test/resources/org/elasticsearch/action/admin/cluster/storedscripts/10_get_script.yml new file mode 100644 index 0000000000000..fa393c4049d05 --- /dev/null +++ b/server/src/test/resources/org/elasticsearch/action/admin/cluster/storedscripts/10_get_script.yml @@ -0,0 +1,96 @@ +--- +"Get script, default format": + - do: + put_script: + id: "1" + body: { "script": {"lang": "painless", "source": "_score * doc['myParent.weight'].value" } } + - match: { acknowledged: true } + + - do: + get_script: + id: "1" + - match: { found: true } + - match: { _id: "1" } + - match: { "script": {"lang": "painless", "source": "_score * doc['myParent.weight'].value"} } + +--- +"Get script, new format": + - + - skip: + version: " - 7.99.99" + reason: this uses a new API that has been added in 8.0 + + - do: + put_script: + id: "1" + body: { "script": {"lang": "painless", "source": "_score * doc['myParent.weight'].value" } } + - match: { acknowledged: true } + + - do: + get_script: + id: "1" + new_format: true + - match: { "1" : { "lang": "painless", "source": "_score * doc['myParent.weight'].value"} } + +--- +"Get script with wildcard": + + - skip: + version: " - 7.99.99" + reason: this uses a new API that has been added in 8.0 + - do: + put_script: + id: "foo" + body: { "script": {} } + - match: { acknowledged: true } + + - do: + put_script: + id: "foo" + body: { "script": {} } + - match: { acknowledged: true } + + - do: + put_script: + id: "bar" + body: { "script": {} } + - match: { acknowledged: true } + + - do: + put_script: + id: "baz" + body: { "script": {} } + - match: { acknowledged: true } + + - do: + get_script: + id: "ba*" + new_format: true + - length: { $body: 2 } + +--- +"Get all scripts": + + - skip: + version: " - 7.99.99" + reason: this uses a new API that has been added in 8.0 + + - do: + put_script: + id: "1" + body: { "script": { "lang": "painless", "source": "_score * doc['myParent.weight'].value" } } + - match: { acknowledged: true } + + - do: + put_script: + id: "foo" + body: { "script": { "lang": "painless", "source": "_score * 9.9" } } + - match: { acknowledged: true } + + - do: + get_script: + new_format: true + + - length: { $body: 2 } + - match: { 1: {"lang": "painless", "source": "_score * doc['myParent.weight'].value"} } + - match: { foo: {"lang": "painless", "source": "_score * 9.9"} }