Adds `GET /_script_language` to support Kibana dynamic scripting language selection. Response contains whether `inline` and/or `stored` scripts are enabled as determined by the `script.allowed_types` settings. For each scripting language registered, such as `painless`, `expression`, `mustache` or custom, available contexts for the language are included as determined by the `script.allowed_contexts` setting. Response format: ``` { "types_allowed": [ "inline", "stored" ], "language_contexts": [ { "language": "expression", "contexts": [ "aggregation_selector", "aggs" ... ] }, { "language": "painless", "contexts": [ "aggregation_selector", "aggs", "aggs_combine", ... ] } ... ] } ``` Fixes: #49463 **Backport**
This commit is contained in:
parent
dbf6183469
commit
426c7a5e8f
|
@ -765,6 +765,7 @@ public class RestHighLevelClientTests extends ESTestCase {
|
|||
"cluster.remote_info",
|
||||
"create",
|
||||
"get_script_context",
|
||||
"get_script_languages",
|
||||
"get_source",
|
||||
"indices.exists_type",
|
||||
"indices.get_upgrade",
|
||||
|
|
|
@ -52,9 +52,12 @@ import java.security.AccessController;
|
|||
import java.security.PrivilegedAction;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Provides the infrastructure for Lucene expressions as a scripting language for Elasticsearch.
|
||||
|
@ -65,6 +68,45 @@ public class ExpressionScriptEngine implements ScriptEngine {
|
|||
|
||||
public static final String NAME = "expression";
|
||||
|
||||
private static Map<ScriptContext<?>, Function<Expression,Object>> contexts;
|
||||
|
||||
static {
|
||||
Map<ScriptContext<?>, Function<Expression,Object>> contexts = new HashMap<ScriptContext<?>, Function<Expression,Object>>();
|
||||
contexts.put(BucketAggregationScript.CONTEXT,
|
||||
ExpressionScriptEngine::newBucketAggregationScriptFactory);
|
||||
|
||||
contexts.put(BucketAggregationSelectorScript.CONTEXT,
|
||||
(Expression expr) -> {
|
||||
BucketAggregationScript.Factory factory = newBucketAggregationScriptFactory(expr);
|
||||
BucketAggregationSelectorScript.Factory wrappedFactory = parameters -> new BucketAggregationSelectorScript(parameters) {
|
||||
@Override
|
||||
public boolean execute() {
|
||||
return factory.newInstance(getParams()).execute().doubleValue() == 1.0;
|
||||
}
|
||||
};
|
||||
return wrappedFactory; });
|
||||
|
||||
contexts.put(FilterScript.CONTEXT,
|
||||
(Expression expr) -> (FilterScript.Factory) (p, lookup) -> newFilterScript(expr, lookup, p));
|
||||
|
||||
contexts.put(ScoreScript.CONTEXT,
|
||||
(Expression expr) -> (ScoreScript.Factory) (p, lookup) -> newScoreScript(expr, lookup, p));
|
||||
|
||||
contexts.put(TermsSetQueryScript.CONTEXT,
|
||||
(Expression expr) -> (TermsSetQueryScript.Factory) (p, lookup) -> newTermsSetQueryScript(expr, lookup, p));
|
||||
|
||||
contexts.put(AggregationScript.CONTEXT,
|
||||
(Expression expr) -> (AggregationScript.Factory) (p, lookup) -> newAggregationScript(expr, lookup, p));
|
||||
|
||||
contexts.put(NumberSortScript.CONTEXT,
|
||||
(Expression expr) -> (NumberSortScript.Factory) (p, lookup) -> newSortScript(expr, lookup, p));
|
||||
|
||||
contexts.put(FieldScript.CONTEXT,
|
||||
(Expression expr) -> (FieldScript.Factory) (p, lookup) -> newFieldScript(expr, lookup, p));
|
||||
|
||||
ExpressionScriptEngine.contexts = Collections.unmodifiableMap(contexts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return NAME;
|
||||
|
@ -102,37 +144,15 @@ public class ExpressionScriptEngine implements ScriptEngine {
|
|||
}
|
||||
}
|
||||
});
|
||||
if (context.instanceClazz.equals(BucketAggregationScript.class)) {
|
||||
return context.factoryClazz.cast(newBucketAggregationScriptFactory(expr));
|
||||
} else if (context.instanceClazz.equals(BucketAggregationSelectorScript.class)) {
|
||||
BucketAggregationScript.Factory factory = newBucketAggregationScriptFactory(expr);
|
||||
BucketAggregationSelectorScript.Factory wrappedFactory = parameters -> new BucketAggregationSelectorScript(parameters) {
|
||||
@Override
|
||||
public boolean execute() {
|
||||
return factory.newInstance(getParams()).execute().doubleValue() == 1.0;
|
||||
}
|
||||
};
|
||||
return context.factoryClazz.cast(wrappedFactory);
|
||||
} else if (context.instanceClazz.equals(FilterScript.class)) {
|
||||
FilterScript.Factory factory = (p, lookup) -> newFilterScript(expr, lookup, p);
|
||||
return context.factoryClazz.cast(factory);
|
||||
} else if (context.instanceClazz.equals(ScoreScript.class)) {
|
||||
ScoreScript.Factory factory = (p, lookup) -> newScoreScript(expr, lookup, p);
|
||||
return context.factoryClazz.cast(factory);
|
||||
} else if (context.instanceClazz.equals(TermsSetQueryScript.class)) {
|
||||
TermsSetQueryScript.Factory factory = (p, lookup) -> newTermsSetQueryScript(expr, lookup, p);
|
||||
return context.factoryClazz.cast(factory);
|
||||
} else if (context.instanceClazz.equals(AggregationScript.class)) {
|
||||
AggregationScript.Factory factory = (p, lookup) -> newAggregationScript(expr, lookup, p);
|
||||
return context.factoryClazz.cast(factory);
|
||||
} else if (context.instanceClazz.equals(NumberSortScript.class)) {
|
||||
NumberSortScript.Factory factory = (p, lookup) -> newSortScript(expr, lookup, p);
|
||||
return context.factoryClazz.cast(factory);
|
||||
} else if (context.instanceClazz.equals(FieldScript.class)) {
|
||||
FieldScript.Factory factory = (p, lookup) -> newFieldScript(expr, lookup, p);
|
||||
return context.factoryClazz.cast(factory);
|
||||
if (contexts.containsKey(context) == false) {
|
||||
throw new IllegalArgumentException("expression engine does not know how to handle script context [" + context.name + "]");
|
||||
}
|
||||
throw new IllegalArgumentException("expression engine does not know how to handle script context [" + context.name + "]");
|
||||
return context.factoryClazz.cast(contexts.get(context).apply(expr));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ScriptContext<?>> getSupportedContexts() {
|
||||
return contexts.keySet();
|
||||
}
|
||||
|
||||
private static BucketAggregationScript.Factory newBucketAggregationScriptFactory(Expression expr) {
|
||||
|
@ -166,7 +186,7 @@ public class ExpressionScriptEngine implements ScriptEngine {
|
|||
};
|
||||
}
|
||||
|
||||
private NumberSortScript.LeafFactory newSortScript(Expression expr, SearchLookup lookup, @Nullable Map<String, Object> vars) {
|
||||
private static NumberSortScript.LeafFactory newSortScript(Expression expr, SearchLookup lookup, @Nullable Map<String, Object> vars) {
|
||||
// NOTE: if we need to do anything complicated with bindings in the future, we can just extend Bindings,
|
||||
// instead of complicating SimpleBindings (which should stay simple)
|
||||
SimpleBindings bindings = new SimpleBindings();
|
||||
|
@ -193,7 +213,7 @@ public class ExpressionScriptEngine implements ScriptEngine {
|
|||
return new ExpressionNumberSortScript(expr, bindings, needsScores);
|
||||
}
|
||||
|
||||
private TermsSetQueryScript.LeafFactory newTermsSetQueryScript(Expression expr, SearchLookup lookup,
|
||||
private static TermsSetQueryScript.LeafFactory newTermsSetQueryScript(Expression expr, SearchLookup lookup,
|
||||
@Nullable Map<String, Object> vars) {
|
||||
// NOTE: if we need to do anything complicated with bindings in the future, we can just extend Bindings,
|
||||
// instead of complicating SimpleBindings (which should stay simple)
|
||||
|
@ -216,7 +236,7 @@ public class ExpressionScriptEngine implements ScriptEngine {
|
|||
return new ExpressionTermSetQueryScript(expr, bindings);
|
||||
}
|
||||
|
||||
private AggregationScript.LeafFactory newAggregationScript(Expression expr, SearchLookup lookup,
|
||||
private static AggregationScript.LeafFactory newAggregationScript(Expression expr, SearchLookup lookup,
|
||||
@Nullable Map<String, Object> vars) {
|
||||
// NOTE: if we need to do anything complicated with bindings in the future, we can just extend Bindings,
|
||||
// instead of complicating SimpleBindings (which should stay simple)
|
||||
|
@ -252,7 +272,7 @@ public class ExpressionScriptEngine implements ScriptEngine {
|
|||
return new ExpressionAggregationScript(expr, bindings, needsScores, specialValue);
|
||||
}
|
||||
|
||||
private FieldScript.LeafFactory newFieldScript(Expression expr, SearchLookup lookup, @Nullable Map<String, Object> vars) {
|
||||
private static FieldScript.LeafFactory newFieldScript(Expression expr, SearchLookup lookup, @Nullable Map<String, Object> vars) {
|
||||
SimpleBindings bindings = new SimpleBindings();
|
||||
for (String variable : expr.variables) {
|
||||
try {
|
||||
|
@ -273,7 +293,7 @@ public class ExpressionScriptEngine implements ScriptEngine {
|
|||
* This is a hack for filter scripts, which must return booleans instead of doubles as expression do.
|
||||
* See https://github.com/elastic/elasticsearch/issues/26429.
|
||||
*/
|
||||
private FilterScript.LeafFactory newFilterScript(Expression expr, SearchLookup lookup, @Nullable Map<String, Object> vars) {
|
||||
private static FilterScript.LeafFactory newFilterScript(Expression expr, SearchLookup lookup, @Nullable Map<String, Object> vars) {
|
||||
ScoreScript.LeafFactory searchLeafFactory = newScoreScript(expr, lookup, vars);
|
||||
return ctx -> {
|
||||
ScoreScript script = searchLeafFactory.newInstance(ctx);
|
||||
|
@ -290,7 +310,7 @@ public class ExpressionScriptEngine implements ScriptEngine {
|
|||
};
|
||||
}
|
||||
|
||||
private ScoreScript.LeafFactory newScoreScript(Expression expr, SearchLookup lookup, @Nullable Map<String, Object> vars) {
|
||||
private static ScoreScript.LeafFactory newScoreScript(Expression expr, SearchLookup lookup, @Nullable Map<String, Object> vars) {
|
||||
// NOTE: if we need to do anything complicated with bindings in the future, we can just extend Bindings,
|
||||
// instead of complicating SimpleBindings (which should stay simple)
|
||||
SimpleBindings bindings = new SimpleBindings();
|
||||
|
@ -327,7 +347,7 @@ public class ExpressionScriptEngine implements ScriptEngine {
|
|||
/**
|
||||
* converts a ParseException at compile-time or link-time to a ScriptException
|
||||
*/
|
||||
private ScriptException convertToScriptException(String message, String source, String portion, Throwable cause) {
|
||||
private static ScriptException convertToScriptException(String message, String source, String portion, Throwable cause) {
|
||||
List<String> stack = new ArrayList<>();
|
||||
stack.add(portion);
|
||||
StringBuilder pointer = new StringBuilder();
|
||||
|
|
|
@ -41,6 +41,7 @@ import java.security.AccessController;
|
|||
import java.security.PrivilegedAction;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Main entry point handling template registration, compilation and
|
||||
|
@ -79,6 +80,11 @@ public final class MustacheScriptEngine implements ScriptEngine {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ScriptContext<?>> getSupportedContexts() {
|
||||
return Collections.singleton(TemplateScript.CONTEXT);
|
||||
}
|
||||
|
||||
private CustomMustacheFactory createMustacheFactory(Map<String, String> options) {
|
||||
if (options == null || options.isEmpty() || options.containsKey(Script.CONTENT_TYPE_OPTION) == false) {
|
||||
return new CustomMustacheFactory();
|
||||
|
|
|
@ -146,6 +146,11 @@ public final class PainlessScriptEngine implements ScriptEngine {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ScriptContext<?>> getSupportedContexts() {
|
||||
return contextsToCompilers.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a stateful factory class that will return script instances. Acts as a middle man between
|
||||
* the {@link ScriptContext#factoryClazz} and the {@link ScriptContext#instanceClazz} when used so that
|
||||
|
|
|
@ -34,7 +34,9 @@ import org.elasticsearch.search.lookup.SearchLookup;
|
|||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* An example script plugin that adds a {@link ScriptEngine} implementing expert scoring.
|
||||
|
@ -76,6 +78,11 @@ public class ExpertScriptPlugin extends Plugin implements ScriptPlugin {
|
|||
// optionally close resources
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ScriptContext<?>> getSupportedContexts() {
|
||||
return Collections.singleton(ScoreScript.CONTEXT);
|
||||
}
|
||||
|
||||
private static class PureDfLeafFactory implements LeafFactory {
|
||||
private final Map<String, Object> params;
|
||||
private final SearchLookup lookup;
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"get_script_languages":{
|
||||
"documentation":{
|
||||
"description":"Returns available script types, languages and contexts"
|
||||
},
|
||||
"stability":"experimental",
|
||||
"url":{
|
||||
"paths":[
|
||||
{
|
||||
"path":"/_script_language",
|
||||
"methods":[
|
||||
"GET"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"params":{}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
"Action to get script languages":
|
||||
- skip:
|
||||
version: " - 7.6.0"
|
||||
reason: "get_script_languages introduced in 7.6.0"
|
||||
- do:
|
||||
get_script_languages: {}
|
||||
|
||||
- match: { types_allowed.0: "inline" }
|
||||
- match: { types_allowed.1: "stored" }
|
|
@ -80,10 +80,12 @@ 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.GetScriptLanguageAction;
|
||||
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.TransportGetScriptLanguageAction;
|
||||
import org.elasticsearch.action.admin.cluster.storedscripts.TransportGetStoredScriptAction;
|
||||
import org.elasticsearch.action.admin.cluster.storedscripts.TransportPutStoredScriptAction;
|
||||
import org.elasticsearch.action.admin.cluster.tasks.PendingClusterTasksAction;
|
||||
|
@ -248,6 +250,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.RestGetScriptLanguageAction;
|
||||
import org.elasticsearch.rest.action.admin.cluster.RestGetSnapshotsAction;
|
||||
import org.elasticsearch.rest.action.admin.cluster.RestGetStoredScriptAction;
|
||||
import org.elasticsearch.rest.action.admin.cluster.RestGetTaskAction;
|
||||
|
@ -531,6 +534,7 @@ public class ActionModule extends AbstractModule {
|
|||
actions.register(GetStoredScriptAction.INSTANCE, TransportGetStoredScriptAction.class);
|
||||
actions.register(DeleteStoredScriptAction.INSTANCE, TransportDeleteStoredScriptAction.class);
|
||||
actions.register(GetScriptContextAction.INSTANCE, TransportGetScriptContextAction.class);
|
||||
actions.register(GetScriptLanguageAction.INSTANCE, TransportGetScriptLanguageAction.class);
|
||||
|
||||
actions.register(FieldCapabilitiesAction.INSTANCE, TransportFieldCapabilitiesAction.class,
|
||||
TransportFieldCapabilitiesIndexAction.class);
|
||||
|
@ -661,6 +665,7 @@ public class ActionModule extends AbstractModule {
|
|||
registerHandler.accept(new RestPutStoredScriptAction(restController));
|
||||
registerHandler.accept(new RestDeleteStoredScriptAction(restController));
|
||||
registerHandler.accept(new RestGetScriptContextAction(restController));
|
||||
registerHandler.accept(new RestGetScriptLanguageAction(restController));
|
||||
|
||||
registerHandler.accept(new RestFieldCapabilitiesAction(restController));
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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 GetScriptLanguageAction extends ActionType<GetScriptLanguageResponse> {
|
||||
public static final GetScriptLanguageAction INSTANCE = new GetScriptLanguageAction();
|
||||
public static final String NAME = "cluster:admin/script_language/get";
|
||||
|
||||
private GetScriptLanguageAction() {
|
||||
super(NAME, GetScriptLanguageResponse::new);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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 GetScriptLanguageRequest extends ActionRequest {
|
||||
public GetScriptLanguageRequest() {
|
||||
super();
|
||||
}
|
||||
|
||||
GetScriptLanguageRequest(StreamInput in) throws IOException {
|
||||
super(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionRequestValidationException validate() { return null; }
|
||||
|
||||
@Override
|
||||
public String toString() { return "get script languages"; }
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.io.stream.Writeable;
|
||||
import org.elasticsearch.common.xcontent.StatusToXContentObject;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.rest.RestStatus;
|
||||
import org.elasticsearch.script.ScriptLanguagesInfo;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
public class GetScriptLanguageResponse extends ActionResponse implements StatusToXContentObject, Writeable {
|
||||
public final ScriptLanguagesInfo info;
|
||||
|
||||
GetScriptLanguageResponse(ScriptLanguagesInfo info) {
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
GetScriptLanguageResponse(StreamInput in) throws IOException {
|
||||
super(in);
|
||||
info = new ScriptLanguagesInfo(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
info.writeTo(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestStatus status() {
|
||||
return RestStatus.OK;
|
||||
}
|
||||
|
||||
public static GetScriptLanguageResponse fromXContent(XContentParser parser) throws IOException {
|
||||
return new GetScriptLanguageResponse(ScriptLanguagesInfo.fromXContent(parser));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
GetScriptLanguageResponse that = (GetScriptLanguageResponse) o;
|
||||
return info.equals(that.info);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() { return Objects.hash(info); }
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
return info.toXContent(builder, params);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
public class TransportGetScriptLanguageAction extends HandledTransportAction<GetScriptLanguageRequest, GetScriptLanguageResponse> {
|
||||
private final ScriptService scriptService;
|
||||
|
||||
@Inject
|
||||
public TransportGetScriptLanguageAction(TransportService transportService, ActionFilters actionFilters, ScriptService scriptService) {
|
||||
super(GetScriptLanguageAction.NAME, transportService, actionFilters, GetScriptLanguageRequest::new);
|
||||
this.scriptService = scriptService;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doExecute(Task task, GetScriptLanguageRequest request, ActionListener<GetScriptLanguageResponse> listener) {
|
||||
listener.onResponse(new GetScriptLanguageResponse(scriptService.getScriptLanguages()));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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.GetScriptLanguageAction;
|
||||
import org.elasticsearch.action.admin.cluster.storedscripts.GetScriptLanguageRequest;
|
||||
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 RestGetScriptLanguageAction extends BaseRestHandler {
|
||||
@Inject
|
||||
public RestGetScriptLanguageAction(RestController controller) {
|
||||
controller.registerHandler(GET, "/_script_language", this);
|
||||
}
|
||||
|
||||
@Override public String getName() {
|
||||
return "script_language_action";
|
||||
}
|
||||
|
||||
@Override protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
|
||||
return channel -> client.execute(GetScriptLanguageAction.INSTANCE,
|
||||
new GetScriptLanguageRequest(),
|
||||
new RestToXContentListener<>(channel));
|
||||
}
|
||||
|
||||
}
|
|
@ -22,6 +22,7 @@ package org.elasticsearch.script;
|
|||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A script language implementation.
|
||||
|
@ -45,4 +46,9 @@ public interface ScriptEngine extends Closeable {
|
|||
|
||||
@Override
|
||||
default void close() throws IOException {}
|
||||
|
||||
/**
|
||||
* Script contexts supported by this engine.
|
||||
*/
|
||||
Set<ScriptContext<?>> getSupportedContexts();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* 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.script;
|
||||
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.collect.Tuple;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.io.stream.Writeable;
|
||||
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
|
||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
|
||||
|
||||
/**
|
||||
* The allowable types, languages and their corresponding contexts. When serialized there is a top level <code>types_allowed</code> list,
|
||||
* meant to reflect the setting <code>script.allowed_types</code> with the allowed types (eg <code>inline</code>, <code>stored</code>).
|
||||
*
|
||||
* The top-level <code>language_contexts</code> list of objects have the <code>language</code> (eg. <code>painless</code>,
|
||||
* <code>mustache</code>) and a list of <code>contexts</code> available for the language. It is the responsibility of the caller to ensure
|
||||
* these contexts are filtered by the <code>script.allowed_contexts</code> setting.
|
||||
*
|
||||
* The json serialization of the object has the form:
|
||||
* <code>
|
||||
* {
|
||||
* "types_allowed": [
|
||||
* "inline",
|
||||
* "stored"
|
||||
* ],
|
||||
* "language_contexts": [
|
||||
* {
|
||||
* "language": "expression",
|
||||
* "contexts": [
|
||||
* "aggregation_selector",
|
||||
* "aggs"
|
||||
* ...
|
||||
* ]
|
||||
* },
|
||||
* {
|
||||
* "language": "painless",
|
||||
* "contexts": [
|
||||
* "aggregation_selector",
|
||||
* "aggs",
|
||||
* "aggs_combine",
|
||||
* ...
|
||||
* ]
|
||||
* }
|
||||
* ...
|
||||
* ]
|
||||
* }
|
||||
* </code>
|
||||
*/
|
||||
public class ScriptLanguagesInfo implements ToXContentObject, Writeable {
|
||||
private static final ParseField TYPES_ALLOWED = new ParseField("types_allowed");
|
||||
private static final ParseField LANGUAGE_CONTEXTS = new ParseField("language_contexts");
|
||||
private static final ParseField LANGUAGE = new ParseField("language");
|
||||
private static final ParseField CONTEXTS = new ParseField("contexts");
|
||||
|
||||
public final Set<String> typesAllowed;
|
||||
public final Map<String,Set<String>> languageContexts;
|
||||
|
||||
public ScriptLanguagesInfo(Set<String> typesAllowed, Map<String,Set<String>> languageContexts) {
|
||||
this.typesAllowed = typesAllowed != null ? Collections.unmodifiableSet(typesAllowed): Collections.emptySet();
|
||||
this.languageContexts = languageContexts != null ? Collections.unmodifiableMap(languageContexts): Collections.emptyMap();
|
||||
}
|
||||
|
||||
public ScriptLanguagesInfo(StreamInput in) throws IOException {
|
||||
typesAllowed = in.readSet(StreamInput::readString);
|
||||
languageContexts = in.readMap(StreamInput::readString, sin -> sin.readSet(StreamInput::readString));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static ConstructingObjectParser<ScriptLanguagesInfo,Void> PARSER =
|
||||
new ConstructingObjectParser<>("script_languages_info", true,
|
||||
(a) -> new ScriptLanguagesInfo(
|
||||
new HashSet<>((List<String>)a[0]),
|
||||
((List<Tuple<String,Set<String>>>)a[1]).stream().collect(Collectors.toMap(Tuple::v1, Tuple::v2))
|
||||
)
|
||||
);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static ConstructingObjectParser<Tuple<String,Set<String>>,Void> LANGUAGE_CONTEXT_PARSER =
|
||||
new ConstructingObjectParser<>("language_contexts", true,
|
||||
(m, name) -> new Tuple<>((String)m[0], Collections.unmodifiableSet(new HashSet<>((List<String>)m[1])))
|
||||
);
|
||||
|
||||
static {
|
||||
PARSER.declareStringArray(constructorArg(), TYPES_ALLOWED);
|
||||
PARSER.declareObjectArray(constructorArg(), LANGUAGE_CONTEXT_PARSER, LANGUAGE_CONTEXTS);
|
||||
LANGUAGE_CONTEXT_PARSER.declareString(constructorArg(), LANGUAGE);
|
||||
LANGUAGE_CONTEXT_PARSER.declareStringArray(constructorArg(), CONTEXTS);
|
||||
}
|
||||
|
||||
public static ScriptLanguagesInfo fromXContent(XContentParser parser) throws IOException {
|
||||
return PARSER.parse(parser, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
out.writeStringCollection(typesAllowed);
|
||||
out.writeMap(languageContexts, StreamOutput::writeString, StreamOutput::writeStringCollection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
ScriptLanguagesInfo that = (ScriptLanguagesInfo) o;
|
||||
return Objects.equals(typesAllowed, that.typesAllowed) &&
|
||||
Objects.equals(languageContexts, that.languageContexts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(typesAllowed, languageContexts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject().startArray(TYPES_ALLOWED.getPreferredName());
|
||||
for (String type: typesAllowed.stream().sorted().collect(Collectors.toList())) {
|
||||
builder.value(type);
|
||||
}
|
||||
|
||||
builder.endArray().startArray(LANGUAGE_CONTEXTS.getPreferredName());
|
||||
List<Map.Entry<String,Set<String>>> languagesByName = languageContexts.entrySet().stream().sorted(
|
||||
Map.Entry.comparingByKey()
|
||||
).collect(Collectors.toList());
|
||||
|
||||
for (Map.Entry<String,Set<String>> languageContext: languagesByName) {
|
||||
builder.startObject().field(LANGUAGE.getPreferredName(), languageContext.getKey()).startArray(CONTEXTS.getPreferredName());
|
||||
for (String context: languageContext.getValue().stream().sorted().collect(Collectors.toList())) {
|
||||
builder.value(context);
|
||||
}
|
||||
builder.endArray().endObject();
|
||||
}
|
||||
|
||||
return builder.endArray().endObject();
|
||||
}
|
||||
}
|
|
@ -52,12 +52,14 @@ import java.io.Closeable;
|
|||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ScriptService implements Closeable, ClusterStateApplier {
|
||||
|
||||
|
@ -546,6 +548,26 @@ public class ScriptService implements Closeable, ClusterStateApplier {
|
|||
return infos;
|
||||
}
|
||||
|
||||
public ScriptLanguagesInfo getScriptLanguages() {
|
||||
Set<String> types = typesAllowed;
|
||||
if (types == null) {
|
||||
types = new HashSet<>();
|
||||
for (ScriptType type: ScriptType.values()) {
|
||||
types.add(type.getName());
|
||||
}
|
||||
}
|
||||
|
||||
final Set<String> contexts = contextsAllowed != null ? contextsAllowed : this.contexts.keySet();
|
||||
Map<String,Set<String>> languageContexts = new HashMap<>();
|
||||
engines.forEach(
|
||||
(key, value) -> languageContexts.put(
|
||||
key,
|
||||
value.getSupportedContexts().stream().map(c -> c.name).filter(contexts::contains).collect(Collectors.toSet())
|
||||
)
|
||||
);
|
||||
return new ScriptLanguagesInfo(types, languageContexts);
|
||||
}
|
||||
|
||||
public ScriptStats stats() {
|
||||
return scriptMetrics.stats();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* 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.script.ScriptLanguagesInfo;
|
||||
import org.elasticsearch.test.AbstractSerializingTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class GetScriptLanguageResponseTests extends AbstractSerializingTestCase<GetScriptLanguageResponse> {
|
||||
private static int MAX_VALUES = 4;
|
||||
private static final int MIN_LENGTH = 1;
|
||||
private static final int MAX_LENGTH = 16;
|
||||
|
||||
@Override
|
||||
protected GetScriptLanguageResponse createTestInstance() {
|
||||
if (randomBoolean()) {
|
||||
return new GetScriptLanguageResponse(
|
||||
new ScriptLanguagesInfo(Collections.emptySet(), Collections.emptyMap())
|
||||
);
|
||||
}
|
||||
return new GetScriptLanguageResponse(randomInstance());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GetScriptLanguageResponse doParseInstance(XContentParser parser) throws IOException {
|
||||
return GetScriptLanguageResponse.fromXContent(parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Writeable.Reader<GetScriptLanguageResponse> instanceReader() { return GetScriptLanguageResponse::new; }
|
||||
|
||||
@Override
|
||||
protected GetScriptLanguageResponse mutateInstance(GetScriptLanguageResponse instance) throws IOException {
|
||||
switch (randomInt(2)) {
|
||||
case 0:
|
||||
// mutate typesAllowed
|
||||
return new GetScriptLanguageResponse(
|
||||
new ScriptLanguagesInfo(mutateStringSet(instance.info.typesAllowed), instance.info.languageContexts)
|
||||
);
|
||||
case 1:
|
||||
// Add language
|
||||
String language = randomValueOtherThanMany(
|
||||
instance.info.languageContexts::containsKey,
|
||||
() -> randomAlphaOfLengthBetween(MIN_LENGTH, MAX_LENGTH)
|
||||
);
|
||||
Map<String,Set<String>> languageContexts = new HashMap<>();
|
||||
instance.info.languageContexts.forEach(languageContexts::put);
|
||||
languageContexts.put(language, randomStringSet(randomIntBetween(1, MAX_VALUES)));
|
||||
return new GetScriptLanguageResponse(new ScriptLanguagesInfo(instance.info.typesAllowed, languageContexts));
|
||||
default:
|
||||
// Mutate languageContexts
|
||||
Map<String,Set<String>> lc = new HashMap<>();
|
||||
if (instance.info.languageContexts.size() == 0) {
|
||||
lc.put(randomAlphaOfLengthBetween(MIN_LENGTH, MAX_LENGTH), randomStringSet(randomIntBetween(1, MAX_VALUES)));
|
||||
} else {
|
||||
int toModify = randomInt(instance.info.languageContexts.size()-1);
|
||||
List<String> keys = new ArrayList<>(instance.info.languageContexts.keySet());
|
||||
for (int i=0; i<keys.size(); i++) {
|
||||
String key = keys.get(i);
|
||||
Set<String> value = instance.info.languageContexts.get(keys.get(i));
|
||||
if (i == toModify) {
|
||||
value = mutateStringSet(instance.info.languageContexts.get(keys.get(i)));
|
||||
}
|
||||
lc.put(key, value);
|
||||
}
|
||||
}
|
||||
return new GetScriptLanguageResponse(new ScriptLanguagesInfo(instance.info.typesAllowed, lc));
|
||||
}
|
||||
}
|
||||
|
||||
private static ScriptLanguagesInfo randomInstance() {
|
||||
Map<String,Set<String>> contexts = new HashMap<>();
|
||||
for (String context: randomStringSet(randomIntBetween(1, MAX_VALUES))) {
|
||||
contexts.put(context, randomStringSet(randomIntBetween(1, MAX_VALUES)));
|
||||
}
|
||||
return new ScriptLanguagesInfo(randomStringSet(randomInt(MAX_VALUES)), contexts);
|
||||
}
|
||||
|
||||
private static Set<String> randomStringSet(int numInstances) {
|
||||
Set<String> rand = new HashSet<>(numInstances);
|
||||
for (int i = 0; i < numInstances; i++) {
|
||||
rand.add(randomValueOtherThanMany(rand::contains, () -> randomAlphaOfLengthBetween(MIN_LENGTH, MAX_LENGTH)));
|
||||
}
|
||||
return rand;
|
||||
}
|
||||
|
||||
private static Set<String> mutateStringSet(Set<String> strings) {
|
||||
if (strings.isEmpty()) {
|
||||
return Collections.singleton(randomAlphaOfLengthBetween(MIN_LENGTH, MAX_LENGTH));
|
||||
}
|
||||
|
||||
if (randomBoolean()) {
|
||||
Set<String> updated = new HashSet<>(strings);
|
||||
updated.add(randomValueOtherThanMany(updated::contains, () -> randomAlphaOfLengthBetween(MIN_LENGTH, MAX_LENGTH)));
|
||||
return updated;
|
||||
} else {
|
||||
List<String> sorted = strings.stream().sorted().collect(Collectors.toList());
|
||||
int toRemove = randomInt(sorted.size() - 1);
|
||||
Set<String> updated = new HashSet<>();
|
||||
for (int i = 0; i < sorted.size(); i++) {
|
||||
if (i != toRemove) {
|
||||
updated.add(sorted.get(i));
|
||||
}
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* 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.script;
|
||||
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ScriptLanguagesInfoTests extends ESTestCase {
|
||||
public void testEmptyTypesAllowedReturnsAllTypes() {
|
||||
ScriptService ss = getMockScriptService(Settings.EMPTY);
|
||||
ScriptLanguagesInfo info = ss.getScriptLanguages();
|
||||
ScriptType[] types = ScriptType.values();
|
||||
assertEquals(types.length, info.typesAllowed.size());
|
||||
for(ScriptType type: types) {
|
||||
assertTrue("[" + type.getName() + "] is allowed", info.typesAllowed.contains(type.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
public void testSingleTypesAllowedReturnsThatType() {
|
||||
for (ScriptType type: ScriptType.values()) {
|
||||
ScriptService ss = getMockScriptService(
|
||||
Settings.builder().put("script.allowed_types", type.getName()).build()
|
||||
);
|
||||
ScriptLanguagesInfo info = ss.getScriptLanguages();
|
||||
assertEquals(1, info.typesAllowed.size());
|
||||
assertTrue("[" + type.getName() + "] is allowed", info.typesAllowed.contains(type.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
public void testBothTypesAllowedReturnsBothTypes() {
|
||||
List<String> types = Arrays.stream(ScriptType.values()).map(ScriptType::getName).collect(Collectors.toList());
|
||||
Settings.Builder settings = Settings.builder().putList("script.allowed_types", types);
|
||||
ScriptService ss = getMockScriptService(settings.build());
|
||||
ScriptLanguagesInfo info = ss.getScriptLanguages();
|
||||
assertEquals(types.size(), info.typesAllowed.size());
|
||||
for(String type: types) {
|
||||
assertTrue("[" + type + "] is allowed", info.typesAllowed.contains(type));
|
||||
}
|
||||
}
|
||||
|
||||
private ScriptService getMockScriptService(Settings settings) {
|
||||
MockScriptEngine scriptEngine = new MockScriptEngine(MockScriptEngine.NAME,
|
||||
Collections.singletonMap("test_script", script -> 1),
|
||||
Collections.emptyMap());
|
||||
Map<String, ScriptEngine> engines = Collections.singletonMap(scriptEngine.getType(), scriptEngine);
|
||||
|
||||
return new ScriptService(settings, engines, ScriptModule.CORE_CONTEXTS);
|
||||
}
|
||||
|
||||
|
||||
public interface MiscContext {
|
||||
void execute();
|
||||
Object newInstance();
|
||||
}
|
||||
|
||||
public void testOnlyScriptEngineContextsReturned() {
|
||||
MockScriptEngine scriptEngine = new MockScriptEngine(MockScriptEngine.NAME,
|
||||
Collections.singletonMap("test_script", script -> 1),
|
||||
Collections.emptyMap());
|
||||
Map<String, ScriptEngine> engines = Collections.singletonMap(scriptEngine.getType(), scriptEngine);
|
||||
|
||||
Map<String, ScriptContext<?>> mockContexts = scriptEngine.getSupportedContexts().stream().collect(Collectors.toMap(
|
||||
c -> c.name,
|
||||
Function.identity()
|
||||
));
|
||||
String miscContext = "misc_context";
|
||||
assertFalse(mockContexts.containsKey(miscContext));
|
||||
|
||||
Map<String, ScriptContext<?>> mockAndMiscContexts = new HashMap<>(mockContexts);
|
||||
mockAndMiscContexts.put(miscContext, new ScriptContext<>(miscContext, MiscContext.class));
|
||||
|
||||
ScriptService ss = new ScriptService(Settings.EMPTY, engines, mockAndMiscContexts);
|
||||
ScriptLanguagesInfo info = ss.getScriptLanguages();
|
||||
|
||||
assertTrue(info.languageContexts.containsKey(MockScriptEngine.NAME));
|
||||
assertEquals(1, info.languageContexts.size());
|
||||
assertEquals(mockContexts.keySet(), info.languageContexts.get(MockScriptEngine.NAME));
|
||||
}
|
||||
|
||||
public void testContextsAllowedSettingRespected() {
|
||||
MockScriptEngine scriptEngine = new MockScriptEngine(MockScriptEngine.NAME,
|
||||
Collections.singletonMap("test_script", script -> 1),
|
||||
Collections.emptyMap());
|
||||
Map<String, ScriptEngine> engines = Collections.singletonMap(scriptEngine.getType(), scriptEngine);
|
||||
Map<String, ScriptContext<?>> mockContexts = scriptEngine.getSupportedContexts().stream().collect(Collectors.toMap(
|
||||
c -> c.name,
|
||||
Function.identity()
|
||||
));
|
||||
|
||||
List<String> allContexts = new ArrayList<>(mockContexts.keySet());
|
||||
List<String> allowed = allContexts.subList(0, allContexts.size()/2);
|
||||
String miscContext = "misc_context";
|
||||
allowed.add(miscContext);
|
||||
// check that allowing more than available doesn't pollute the returned contexts
|
||||
Settings.Builder settings = Settings.builder().putList("script.allowed_contexts", allowed);
|
||||
|
||||
Map<String, ScriptContext<?>> mockAndMiscContexts = new HashMap<>(mockContexts);
|
||||
mockAndMiscContexts.put(miscContext, new ScriptContext<>(miscContext, MiscContext.class));
|
||||
|
||||
ScriptService ss = new ScriptService(settings.build(), engines, mockAndMiscContexts);
|
||||
ScriptLanguagesInfo info = ss.getScriptLanguages();
|
||||
|
||||
assertTrue(info.languageContexts.containsKey(MockScriptEngine.NAME));
|
||||
assertEquals(1, info.languageContexts.size());
|
||||
assertEquals(new HashSet<>(allContexts.subList(0, allContexts.size()/2)), info.languageContexts.get(MockScriptEngine.NAME));
|
||||
}
|
||||
}
|
|
@ -50,6 +50,7 @@ import java.util.Collection;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import static org.elasticsearch.client.Requests.searchRequest;
|
||||
|
@ -90,6 +91,11 @@ public class ExplainableScriptIT extends ESIntegTestCase {
|
|||
};
|
||||
return context.factoryClazz.cast(factory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ScriptContext<?>> getSupportedContexts() {
|
||||
return Collections.singleton(ScoreScript.CONTEXT);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF_REPLICAS;
|
||||
|
@ -1155,6 +1156,11 @@ public class SuggestSearchIT extends ESIntegTestCase {
|
|||
};
|
||||
return context.factoryClazz.cast(factory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ScriptContext<?>> getSupportedContexts() {
|
||||
return Collections.singleton(TemplateScript.CONTEXT);
|
||||
}
|
||||
}
|
||||
|
||||
public void testPhraseSuggesterCollate() throws InterruptedException, ExecutionException, IOException {
|
||||
|
|
|
@ -35,7 +35,10 @@ import java.util.Collections;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.util.Collections.emptyMap;
|
||||
|
||||
|
@ -304,6 +307,35 @@ public class MockScriptEngine implements ScriptEngine {
|
|||
throw new IllegalArgumentException("mock script engine does not know how to handle context [" + context.name + "]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ScriptContext<?>> getSupportedContexts() {
|
||||
// TODO(stu): make part of `compile()`
|
||||
return Stream.of(
|
||||
FieldScript.CONTEXT,
|
||||
TermsSetQueryScript.CONTEXT,
|
||||
NumberSortScript.CONTEXT,
|
||||
StringSortScript.CONTEXT,
|
||||
IngestScript.CONTEXT,
|
||||
AggregationScript.CONTEXT,
|
||||
IngestConditionalScript.CONTEXT,
|
||||
UpdateScript.CONTEXT,
|
||||
BucketAggregationScript.CONTEXT,
|
||||
BucketAggregationSelectorScript.CONTEXT,
|
||||
SignificantTermsHeuristicScoreScript.CONTEXT,
|
||||
TemplateScript.CONTEXT,
|
||||
FilterScript.CONTEXT,
|
||||
SimilarityScript.CONTEXT,
|
||||
SimilarityWeightScript.CONTEXT,
|
||||
MovingFunctionScript.CONTEXT,
|
||||
ScoreScript.CONTEXT,
|
||||
ScriptedMetricAggContexts.InitScript.CONTEXT,
|
||||
ScriptedMetricAggContexts.MapScript.CONTEXT,
|
||||
ScriptedMetricAggContexts.CombineScript.CONTEXT,
|
||||
ScriptedMetricAggContexts.ReduceScript.CONTEXT,
|
||||
IntervalFilterScript.CONTEXT
|
||||
).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private Map<String, Object> createVars(Map<String, Object> params) {
|
||||
Map<String, Object> vars = new HashMap<>();
|
||||
vars.put("params", params);
|
||||
|
|
Loading…
Reference in New Issue