From c6037dc93bcda984e903999e182181cd725b01b9 Mon Sep 17 00:00:00 2001 From: Stuart Tettemer Date: Wed, 16 Oct 2019 16:44:47 -0600 Subject: [PATCH] Scripting: get context names REST API (#48026) Adds `GET /_script_context`, returning a `contexts` object with each available context as a key whose value is an empty object. eg. ``` { "contexts": { "aggregation_selector": {}, "aggs": {}, "aggs_combine": {}, ... } } ``` refs: #47411 --- .../client/RestHighLevelClientTests.java | 1 + .../rest-api-spec/api/get_script_context.json | 19 +++ .../test/scripts/20_get_script_context.yml | 27 ++++ .../elasticsearch/action/ActionModule.java | 5 + .../storedscripts/GetScriptContextAction.java | 32 +++++ .../GetScriptContextRequest.java | 45 ++++++ .../GetScriptContextResponse.java | 132 ++++++++++++++++++ .../TransportGetScriptContextAction.java | 49 +++++++ .../cluster/RestGetScriptContextAction.java | 50 +++++++ .../elasticsearch/script/ScriptService.java | 4 + .../GetScriptContextResponseTests.java | 65 +++++++++ 11 files changed, 429 insertions(+) create mode 100644 rest-api-spec/src/main/resources/rest-api-spec/api/get_script_context.json create mode 100644 rest-api-spec/src/main/resources/rest-api-spec/test/scripts/20_get_script_context.yml create mode 100644 server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/GetScriptContextAction.java create mode 100644 server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/GetScriptContextRequest.java create mode 100644 server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/GetScriptContextResponse.java create mode 100644 server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/TransportGetScriptContextAction.java create mode 100644 server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestGetScriptContextAction.java create mode 100644 server/src/test/java/org/elasticsearch/action/admin/cluster/storedscripts/GetScriptContextResponseTests.java 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 cd8b518ac8311..b174f52a150ae 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 @@ -760,6 +760,7 @@ public void testApiNamingConventions() throws Exception { String[] notYetSupportedApi = new String[]{ "cluster.remote_info", "create", + "get_script_context", "get_source", "indices.delete_alias", "indices.exists_type", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/get_script_context.json b/rest-api-spec/src/main/resources/rest-api-spec/api/get_script_context.json new file mode 100644 index 0000000000000..16e8d0a0cfab3 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/get_script_context.json @@ -0,0 +1,19 @@ +{ + "get_script_context":{ + "documentation":{ + "description":"Returns all script contexts." + }, + "stability":"experimental", + "url":{ + "paths":[ + { + "path":"/_script_context", + "methods":[ + "GET" + ] + } + ] + }, + "params":{} + } +} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/scripts/20_get_script_context.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/scripts/20_get_script_context.yml new file mode 100644 index 0000000000000..881748ef13d79 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/scripts/20_get_script_context.yml @@ -0,0 +1,27 @@ +"Action to get all contexts": + - skip: + version: " - 7.6.0" + reason: "get_all_contexts introduced in 7.6.0" + - do: + get_script_context: {} + - match: { contexts.aggregation_selector: {} } + - match: { contexts.aggs: {} } + - match: { contexts.aggs_combine: {} } + - match: { contexts.aggs_init: {} } + - match: { contexts.aggs_map: {} } + - match: { contexts.aggs_reduce: {} } + - match: { contexts.bucket_aggregation: {} } + - match: { contexts.field: {} } + - match: { contexts.filter: {} } + - match: { contexts.ingest: {} } + - match: { contexts.interval: {} } + - match: { contexts.number_sort: {} } + - match: { contexts.processor_conditional: {} } + - match: { contexts.score: {} } + - match: { contexts.script_heuristic: {} } + - match: { contexts.similarity: {} } + - match: { contexts.similarity_weight: {} } + - match: { contexts.string_sort: {} } + - match: { contexts.template: {} } + - match: { contexts.terms_set: {} } + - match: { contexts.update: {} } diff --git a/server/src/main/java/org/elasticsearch/action/ActionModule.java b/server/src/main/java/org/elasticsearch/action/ActionModule.java index 7ab274da8391b..81d5bd17c33df 100644 --- a/server/src/main/java/org/elasticsearch/action/ActionModule.java +++ b/server/src/main/java/org/elasticsearch/action/ActionModule.java @@ -79,9 +79,11 @@ import org.elasticsearch.action.admin.cluster.stats.ClusterStatsAction; import org.elasticsearch.action.admin.cluster.stats.TransportClusterStatsAction; import org.elasticsearch.action.admin.cluster.storedscripts.DeleteStoredScriptAction; +import org.elasticsearch.action.admin.cluster.storedscripts.GetScriptContextAction; import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptAction; import org.elasticsearch.action.admin.cluster.storedscripts.PutStoredScriptAction; import org.elasticsearch.action.admin.cluster.storedscripts.TransportDeleteStoredScriptAction; +import org.elasticsearch.action.admin.cluster.storedscripts.TransportGetScriptContextAction; import org.elasticsearch.action.admin.cluster.storedscripts.TransportGetStoredScriptAction; import org.elasticsearch.action.admin.cluster.storedscripts.TransportPutStoredScriptAction; import org.elasticsearch.action.admin.cluster.tasks.PendingClusterTasksAction; @@ -247,6 +249,7 @@ import org.elasticsearch.rest.action.admin.cluster.RestDeleteSnapshotAction; import org.elasticsearch.rest.action.admin.cluster.RestDeleteStoredScriptAction; import org.elasticsearch.rest.action.admin.cluster.RestGetRepositoriesAction; +import org.elasticsearch.rest.action.admin.cluster.RestGetScriptContextAction; import org.elasticsearch.rest.action.admin.cluster.RestGetSnapshotsAction; import org.elasticsearch.rest.action.admin.cluster.RestGetStoredScriptAction; import org.elasticsearch.rest.action.admin.cluster.RestGetTaskAction; @@ -519,6 +522,7 @@ public void reg actions.register(PutStoredScriptAction.INSTANCE, TransportPutStoredScriptAction.class); actions.register(GetStoredScriptAction.INSTANCE, TransportGetStoredScriptAction.class); actions.register(DeleteStoredScriptAction.INSTANCE, TransportDeleteStoredScriptAction.class); + actions.register(GetScriptContextAction.INSTANCE, TransportGetScriptContextAction.class); actions.register(FieldCapabilitiesAction.INSTANCE, TransportFieldCapabilitiesAction.class); actions.register(TransportFieldCapabilitiesIndexAction.TYPE, TransportFieldCapabilitiesIndexAction.class); @@ -660,6 +664,7 @@ public void initRestHandlers(Supplier nodesInCluster) { registerHandler.accept(new RestGetStoredScriptAction(restController)); registerHandler.accept(new RestPutStoredScriptAction(restController)); registerHandler.accept(new RestDeleteStoredScriptAction(restController)); + registerHandler.accept(new RestGetScriptContextAction(restController)); registerHandler.accept(new RestFieldCapabilitiesAction(restController)); diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/GetScriptContextAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/GetScriptContextAction.java new file mode 100644 index 0000000000000..d0bfe7762b626 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/GetScriptContextAction.java @@ -0,0 +1,32 @@ +/* + * 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. + */ + +package org.elasticsearch.action.admin.cluster.storedscripts; + +import org.elasticsearch.action.ActionType; + +public class GetScriptContextAction extends ActionType { + + public static final GetScriptContextAction INSTANCE = new GetScriptContextAction(); + public static final String NAME = "cluster:admin/script_context/get"; + + private GetScriptContextAction() { + super(NAME, GetScriptContextResponse::new); + } +} diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/GetScriptContextRequest.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/GetScriptContextRequest.java new file mode 100644 index 0000000000000..4ea26874f82c7 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/GetScriptContextRequest.java @@ -0,0 +1,45 @@ +/* + * 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. + */ +package org.elasticsearch.action.admin.cluster.storedscripts; + +import org.elasticsearch.action.ActionRequest; +import org.elasticsearch.action.ActionRequestValidationException; +import org.elasticsearch.common.io.stream.StreamInput; + +import java.io.IOException; + +public class GetScriptContextRequest extends ActionRequest { + public GetScriptContextRequest() { + super(); + } + + GetScriptContextRequest(StreamInput in) throws IOException { + super(in); + } + + @Override + public ActionRequestValidationException validate() { + return null; + } + + @Override + public String toString() { + return "get script context"; + } +} diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/GetScriptContextResponse.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/GetScriptContextResponse.java new file mode 100644 index 0000000000000..fcbf2dc0fee1c --- /dev/null +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/GetScriptContextResponse.java @@ -0,0 +1,132 @@ +/* + * 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. + */ + +package org.elasticsearch.action.admin.cluster.storedscripts; + +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.StatusToXContentObject; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.rest.RestStatus; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +import static org.elasticsearch.common.xcontent.XContentParser.Token.END_OBJECT; +import static org.elasticsearch.common.xcontent.XContentParser.Token.START_OBJECT; + +public class GetScriptContextResponse extends ActionResponse implements StatusToXContentObject { + + private static final ParseField CONTEXTS = new ParseField("contexts"); + private final List contextNames; + + @SuppressWarnings("unchecked") + public static final ConstructingObjectParser PARSER = + new ConstructingObjectParser<>("get_script_context", true, + (a) -> { + Map contexts = ((List) a[0]).stream().collect(Collectors.toMap( + name -> name, name -> new Object() + )); + return new GetScriptContextResponse(contexts); + } + ); + + static { + PARSER.declareNamedObjects( + ConstructingObjectParser.constructorArg(), + (p, c, n) -> + { + // advance empty object + assert(p.nextToken() == START_OBJECT); + assert(p.nextToken() == END_OBJECT); + return n; + }, + CONTEXTS + ); + } + + GetScriptContextResponse(StreamInput in) throws IOException { + super(in); + int size = in.readInt(); + ArrayList contextNames = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + contextNames.add(in.readString()); + } + this.contextNames = Collections.unmodifiableList(contextNames); + } + + GetScriptContextResponse(Map contexts) { + List contextNames = new ArrayList<>(contexts.keySet()); + contextNames.sort(String::compareTo); + this.contextNames = Collections.unmodifiableList(contextNames); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeInt(this.contextNames.size()); + for (String context: this.contextNames) { + out.writeString(context); + } + } + + @Override + public RestStatus status() { + return RestStatus.OK; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject().startObject(CONTEXTS.getPreferredName()); + for (String contextName: this.contextNames) { + builder.startObject(contextName).endObject(); + } + builder.endObject().endObject(); // CONTEXTS + return builder; + } + + public static GetScriptContextResponse fromXContent(XContentParser parser) throws IOException { + return PARSER.apply(parser, null); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + GetScriptContextResponse that = (GetScriptContextResponse) o; + return contextNames.equals(that.contextNames); + } + + @Override + public int hashCode() { + return Objects.hash(contextNames); + } +} diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/TransportGetScriptContextAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/TransportGetScriptContextAction.java new file mode 100644 index 0000000000000..ea6c1fb37ef21 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/TransportGetScriptContextAction.java @@ -0,0 +1,49 @@ +/* + * 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. + */ +package org.elasticsearch.action.admin.cluster.storedscripts; + +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.support.ActionFilters; +import org.elasticsearch.action.support.HandledTransportAction; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.script.ScriptService; +import org.elasticsearch.tasks.Task; +import org.elasticsearch.transport.TransportService; + +import java.util.Map; +import java.util.stream.Collectors; + +public class TransportGetScriptContextAction extends HandledTransportAction { + + private final ScriptService scriptService; + + @Inject + public TransportGetScriptContextAction(TransportService transportService, ActionFilters actionFilters, ScriptService scriptService) { + super(GetScriptContextAction.NAME, transportService, actionFilters, GetScriptContextRequest::new); + this.scriptService = scriptService; + } + + @Override + protected void doExecute(Task task, GetScriptContextRequest request, ActionListener listener) { + Map contexts = scriptService.getContextNames().stream().collect( + Collectors.toMap(name -> name, name -> new Object()) + ); + listener.onResponse(new GetScriptContextResponse(contexts)); + } +} diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestGetScriptContextAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestGetScriptContextAction.java new file mode 100644 index 0000000000000..9a40225b769ba --- /dev/null +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestGetScriptContextAction.java @@ -0,0 +1,50 @@ +/* + * 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. + */ + +package org.elasticsearch.rest.action.admin.cluster; + +import org.elasticsearch.action.admin.cluster.storedscripts.GetScriptContextAction; +import org.elasticsearch.action.admin.cluster.storedscripts.GetScriptContextRequest; +import org.elasticsearch.client.node.NodeClient; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.rest.BaseRestHandler; +import org.elasticsearch.rest.RestController; +import org.elasticsearch.rest.RestRequest; +import org.elasticsearch.rest.action.RestToXContentListener; + +import java.io.IOException; + +import static org.elasticsearch.rest.RestRequest.Method.GET; + +public class RestGetScriptContextAction extends BaseRestHandler { + @Inject + public RestGetScriptContextAction(RestController controller) { + controller.registerHandler(GET, "/_script_context", this); + } + + @Override public String getName() { + return "script_context_action"; + } + + @Override protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { + return channel -> client.execute(GetScriptContextAction.INSTANCE, + new GetScriptContextRequest(), + new RestToXContentListener<>(channel)); + } +} diff --git a/server/src/main/java/org/elasticsearch/script/ScriptService.java b/server/src/main/java/org/elasticsearch/script/ScriptService.java index f1472afba9342..a59d22bcd5aea 100644 --- a/server/src/main/java/org/elasticsearch/script/ScriptService.java +++ b/server/src/main/java/org/elasticsearch/script/ScriptService.java @@ -538,6 +538,10 @@ public StoredScriptSource getStoredScript(ClusterState state, GetStoredScriptReq } } + public Set getContextNames() { + return contexts.keySet(); + } + public ScriptStats stats() { return scriptMetrics.stats(); } diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/storedscripts/GetScriptContextResponseTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/storedscripts/GetScriptContextResponseTests.java new file mode 100644 index 0000000000000..310469d7ae9fe --- /dev/null +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/storedscripts/GetScriptContextResponseTests.java @@ -0,0 +1,65 @@ +/* + * 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. + */ +package org.elasticsearch.action.admin.cluster.storedscripts; + +import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.test.AbstractSerializingTestCase; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static com.carrotsearch.randomizedtesting.RandomizedTest.randomAsciiLettersOfLengthBetween; + +public class GetScriptContextResponseTests extends AbstractSerializingTestCase { + + @Override + protected GetScriptContextResponse createTestInstance() { + if (randomBoolean()) { + return new GetScriptContextResponse(Collections.emptyMap()); + } + Map items = new HashMap<>(); + for (int i = randomIntBetween(1, 10); i > 0; i--) { + items.put(randomAsciiLettersOfLengthBetween(1, 16), new Object()); + } + return new GetScriptContextResponse(items); + + } + + @Override + protected Writeable.Reader instanceReader() { + return GetScriptContextResponse::new; + } + + @Override + protected GetScriptContextResponse doParseInstance(XContentParser parser) throws IOException { + return GetScriptContextResponse.fromXContent(parser); + } + + @Override + protected GetScriptContextResponse mutateInstance(GetScriptContextResponse instance) throws IOException { + Map items = new HashMap<>(); + for (int i = randomIntBetween(1, 10); i > 0; i--) { + items.put(randomAsciiLettersOfLengthBetween(1, 16), new Object()); + } + return new GetScriptContextResponse(items); + } +}