Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add put stored script support to high-level rest client #31323

Merged
merged 11 commits into from
Sep 9, 2018
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,12 @@
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.snapshots.delete.DeleteSnapshotRequest;
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotRequest;
import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest;
import org.elasticsearch.action.admin.cluster.storedscripts.DeleteStoredScriptRequest;
import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptRequest;
import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotRequest;
import org.elasticsearch.action.admin.cluster.storedscripts.PutStoredScriptRequest;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
import org.elasticsearch.action.admin.indices.cache.clear.ClearIndicesCacheRequest;
Expand Down Expand Up @@ -74,6 +75,7 @@
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.ingest.DeletePipelineRequest;
import org.elasticsearch.action.ingest.GetPipelineRequest;
import org.elasticsearch.action.ingest.PutPipelineRequest;
import org.elasticsearch.action.ingest.SimulatePipelineRequest;
import org.elasticsearch.action.ingest.PutPipelineRequest;
import org.elasticsearch.action.search.ClearScrollRequest;
Expand Down Expand Up @@ -991,6 +993,19 @@ static Request getTemplates(GetIndexTemplatesRequest getIndexTemplatesRequest) t
return request;
}

static Request putScript(PutStoredScriptRequest putStoredScriptRequest) throws IOException {
String endpoint = new EndpointBuilder().addPathPartAsIs("_scripts").addPathPart(putStoredScriptRequest.id()).build();
Request request = new Request(HttpPost.METHOD_NAME, endpoint);
Params params = new Params(request);
params.withTimeout(putStoredScriptRequest.timeout());
params.withMasterTimeout(putStoredScriptRequest.masterNodeTimeout());
if (Strings.hasText(putStoredScriptRequest.context())) {
params.putParam("context", putStoredScriptRequest.context());
}
request.setEntity(createEntity(putStoredScriptRequest, REQUEST_BODY_CONTENT_TYPE));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't we also support a context parameter?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry for asking that, I do not really understand what does context mean here. After searching documentation I only found two docs:

https://www.elastic.co/guide/en/elasticsearch/painless/master/painless-execute-api.html
https://www.elastic.co/guide/en/elasticsearch/reference/master/modules-scripting-security.html

where should I put the parameter?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't be sorry, I can explain! at REST, we also accept the context query_string parameter. It is a string and PutStoredScriptRequest already supports it, you just have to read it from the request and set the corresponding parameter so that we pass it through to the REST layer.

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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
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.admin.cluster.storedscripts.PutStoredScriptRequest;
import org.elasticsearch.action.admin.cluster.storedscripts.PutStoredScriptResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
Expand Down Expand Up @@ -750,6 +752,35 @@ public void deleteScriptAsync(DeleteStoredScriptRequest request, RequestOptions
DeleteStoredScriptResponse::fromXContent, listener, emptySet());
}

/**
* Puts an stored script using the Scripting API.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-scripting-using.html"> Scripting API
* on elastic.co</a>
* @param putStoredScriptRequest the request
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @return the response
* @throws IOException in case there is a problem sending the request or parsing back the response
*/
public PutStoredScriptResponse putScript(PutStoredScriptRequest putStoredScriptRequest,
RequestOptions options) throws IOException {
return performRequestAndParseEntity(putStoredScriptRequest, RequestConverters::putScript, options,
PutStoredScriptResponse::fromXContent, emptySet());
}

/**
* Asynchronously puts an stored script using the Scripting API.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-scripting-using.html"> Scripting API
* on elastic.co</a>
* @param putStoredScriptRequest the request
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @param listener the listener to be notified upon request completion
*/
public void putScriptAsync(PutStoredScriptRequest putStoredScriptRequest, RequestOptions options,
ActionListener<PutStoredScriptResponse> listener) {
performRequestAsyncAndParseEntity(putStoredScriptRequest, RequestConverters::putScript, options,
PutStoredScriptResponse::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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest;
import org.elasticsearch.action.admin.cluster.storedscripts.DeleteStoredScriptRequest;
import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptRequest;
import org.elasticsearch.action.admin.cluster.storedscripts.PutStoredScriptRequest;
import org.elasticsearch.action.admin.indices.alias.Alias;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
Expand Down Expand Up @@ -2176,6 +2177,42 @@ public void testGetTemplateRequest() throws Exception {
assertThat(request.getEntity(), nullValue());
}

public void testPutScript() throws Exception {
PutStoredScriptRequest putStoredScriptRequest = new PutStoredScriptRequest();

String id = randomAlphaOfLengthBetween(5, 10);
putStoredScriptRequest.id(id);

XContentType xContentType = randomFrom(XContentType.values());
try (XContentBuilder builder = XContentBuilder.builder(xContentType.xContent())) {
builder.startObject();
builder.startObject("script")
.field("lang", "painless")
.field("source", "Math.log(_score * 2) + params.multiplier")
.endObject();
builder.endObject();

putStoredScriptRequest.content(BytesReference.bytes(builder), xContentType);
}

Map<String, String> expectedParams = new HashMap<>();
setRandomMasterTimeout(putStoredScriptRequest, expectedParams);
setRandomTimeout(putStoredScriptRequest::timeout, AcknowledgedRequest.DEFAULT_ACK_TIMEOUT, expectedParams);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here we should test the context param too?


if(randomBoolean()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: space between if and (

String context = randomAlphaOfLengthBetween(5, 10);
putStoredScriptRequest.context(context);
expectedParams.put("context", context);
}

Request request = RequestConverters.putScript(putStoredScriptRequest);

assertThat(request.getEndpoint(), equalTo("/_scripts/" + id));
assertThat(request.getParameters(), equalTo(expectedParams));
assertThat(request.getEntity(), notNullValue());
assertToXContentBody(putStoredScriptRequest, request.getEntity());
}

public void testGetScriptRequest() {
GetStoredScriptRequest getStoredScriptRequest = new GetStoredScriptRequest("x-script");
Map<String, String> expectedParams = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package org.elasticsearch.client;/*
package org.elasticsearch.client;
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
Expand All @@ -17,50 +18,39 @@
* 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.action.admin.cluster.storedscripts.PutStoredScriptRequest;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.StoredScriptSource;

import java.util.Collections;
import java.util.Map;

import static java.util.Collections.emptyMap;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.common.xcontent.support.XContentMapValues.extractValue;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
import static org.hamcrest.Matchers.equalTo;

public class StoredScriptsIT extends ESRestHighLevelClientTestCase {

final String id = "calculate-score";
private static final String id = "calculate-score";

public void testGetStoredScript() throws Exception {
final StoredScriptSource scriptSource =
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");
PutStoredScriptRequest request =
new PutStoredScriptRequest(id, "search", new BytesArray("{}"), XContentType.JSON, scriptSource);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯

assertAcked(execute(request, highLevelClient()::putScript, highLevelClient()::putScriptAsync));

GetStoredScriptRequest getRequest = new GetStoredScriptRequest(id);
getRequest.masterNodeTimeout("50s");

GetStoredScriptResponse getResponse = execute(getRequest, highLevelClient()::getScript,
Expand All @@ -75,25 +65,14 @@ public void testDeleteStoredScript() throws Exception {
"Math.log(_score * 2) + params.my_modifier",
Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType()));

final String script = Strings.toString(scriptSource.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS));
// TODO: change to HighLevel PutStoredScriptRequest when it will be ready
// so far - using low-level REST API
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()));
PutStoredScriptRequest request =
new PutStoredScriptRequest(id, "search", new BytesArray("{}"), XContentType.JSON, scriptSource);
assertAcked(execute(request, highLevelClient()::putScript, highLevelClient()::putScriptAsync));

DeleteStoredScriptRequest deleteRequest = new DeleteStoredScriptRequest(id);
deleteRequest.masterNodeTimeout("50s");
deleteRequest.timeout("50s");

DeleteStoredScriptResponse deleteResponse = execute(deleteRequest, highLevelClient()::deleteScript,
highLevelClient()::deleteScriptAsync);

assertThat(deleteResponse.isAcknowledged(), equalTo(true));
assertAcked(execute(deleteRequest, highLevelClient()::deleteScript, highLevelClient()::deleteScriptAsync));

GetStoredScriptRequest getRequest = new GetStoredScriptRequest(id);

Expand All @@ -102,4 +81,21 @@ public void testDeleteStoredScript() throws Exception {
highLevelClient()::getScriptAsync));
assertThat(statusException.status(), equalTo(RestStatus.NOT_FOUND));
}

public void testPutScript() throws Exception {
final StoredScriptSource scriptSource =
new StoredScriptSource("painless",
"Math.log(_score * 2) + params.my_modifier",
Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType()));

PutStoredScriptRequest request =
new PutStoredScriptRequest(id, "search", new BytesArray("{}"), XContentType.JSON, scriptSource);
assertAcked(execute(request, highLevelClient()::putScript, highLevelClient()::putScriptAsync));

Map<String, Object> script = getAsMap("/_scripts/" + id);
assertThat(extractValue("_id", script), equalTo(id));
assertThat(extractValue("found", script), equalTo(true));
assertThat(extractValue("script.lang", script), equalTo("painless"));
assertThat(extractValue("script.source", script), equalTo("Math.log(_score * 2) + params.my_modifier"));
}
}
Loading