Scripting: get context names REST API (#48026) (#48168)

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
This commit is contained in:
Stuart Tettemer 2019-10-17 09:08:55 -06:00 committed by GitHub
parent f46281a251
commit 356eef00c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 429 additions and 0 deletions

View File

@ -760,6 +760,7 @@ public class RestHighLevelClientTests extends ESTestCase {
String[] notYetSupportedApi = new String[]{
"cluster.remote_info",
"create",
"get_script_context",
"get_source",
"indices.delete_alias",
"indices.exists_type",

View File

@ -0,0 +1,19 @@
{
"get_script_context":{
"documentation":{
"description":"Returns all script contexts."
},
"stability":"experimental",
"url":{
"paths":[
{
"path":"/_script_context",
"methods":[
"GET"
]
}
]
},
"params":{}
}
}

View File

@ -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: {} }

View File

@ -79,9 +79,11 @@ import org.elasticsearch.action.admin.cluster.state.TransportClusterStateAction;
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;
@ -244,6 +246,7 @@ import org.elasticsearch.rest.action.admin.cluster.RestDeleteRepositoryAction;
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;
@ -526,6 +529,7 @@ public class ActionModule extends AbstractModule {
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,
TransportFieldCapabilitiesIndexAction.class);
@ -655,6 +659,7 @@ public class ActionModule extends AbstractModule {
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));

View File

@ -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<GetScriptContextResponse> {
public static final GetScriptContextAction INSTANCE = new GetScriptContextAction();
public static final String NAME = "cluster:admin/script_context/get";
private GetScriptContextAction() {
super(NAME, GetScriptContextResponse::new);
}
}

View File

@ -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";
}
}

View File

@ -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<String> contextNames;
@SuppressWarnings("unchecked")
public static final ConstructingObjectParser<GetScriptContextResponse,Void> PARSER =
new ConstructingObjectParser<>("get_script_context", true,
(a) -> {
Map<String, Object> contexts = ((List<String>) 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<String> contextNames = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
contextNames.add(in.readString());
}
this.contextNames = Collections.unmodifiableList(contextNames);
}
GetScriptContextResponse(Map<String,Object> contexts) {
List<String> 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);
}
}

View File

@ -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<GetScriptContextRequest, GetScriptContextResponse> {
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<GetScriptContextResponse> listener) {
Map<String,Object> contexts = scriptService.getContextNames().stream().collect(
Collectors.toMap(name -> name, name -> new Object())
);
listener.onResponse(new GetScriptContextResponse(contexts));
}
}

View File

@ -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));
}
}

View File

@ -538,6 +538,10 @@ public class ScriptService implements Closeable, ClusterStateApplier {
}
}
public Set<String> getContextNames() {
return contexts.keySet();
}
public ScriptStats stats() {
return scriptMetrics.stats();
}

View File

@ -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<GetScriptContextResponse> {
@Override
protected GetScriptContextResponse createTestInstance() {
if (randomBoolean()) {
return new GetScriptContextResponse(Collections.emptyMap());
}
Map<String,Object> 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<GetScriptContextResponse> 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<String,Object> items = new HashMap<>();
for (int i = randomIntBetween(1, 10); i > 0; i--) {
items.put(randomAsciiLettersOfLengthBetween(1, 16), new Object());
}
return new GetScriptContextResponse(items);
}
}