/* * SPDX-License-Identifier: Apache-2.0 * * The OpenSearch Contributors require contributions made to * this file be licensed under the Apache-2.0 license or a * compatible open source license. */ /* * 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. */ /* * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ package org.opensearch.script; import org.opensearch.core.ParseField; import org.opensearch.common.collect.Tuple; import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.core.common.io.stream.StreamOutput; import org.opensearch.core.common.io.stream.Writeable; import org.opensearch.core.xcontent.ConstructingObjectParser; import org.opensearch.core.xcontent.ToXContentObject; import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.core.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.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; /** * The allowable types, languages and their corresponding contexts. When serialized there is a top level types_allowed list, * meant to reflect the setting script.allowed_types with the allowed types (eg inline, stored). * * The top-level language_contexts list of objects have the language (eg. painless, * mustache) and a list of contexts available for the language. It is the responsibility of the caller to ensure * these contexts are filtered by the script.allowed_contexts setting. * * The json serialization of the object has the form: * * { * "types_allowed": [ * "inline", * "stored" * ], * "language_contexts": [ * { * "language": "expression", * "contexts": [ * "aggregation_selector", * "aggs" * ... * ] * }, * { * "language": "painless", * "contexts": [ * "aggregation_selector", * "aggs", * "aggs_combine", * ... * ] * } * ... * ] * } * * * @opensearch.internal */ 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 typesAllowed; public final Map> languageContexts; public ScriptLanguagesInfo(Set typesAllowed, Map> 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 final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( "script_languages_info", true, (a) -> new ScriptLanguagesInfo( new HashSet<>((List) a[0]), ((List>>) a[1]).stream().collect(Collectors.toMap(Tuple::v1, Tuple::v2)) ) ); @SuppressWarnings("unchecked") private static final ConstructingObjectParser>, Void> LANGUAGE_CONTEXT_PARSER = new ConstructingObjectParser<>( "language_contexts", true, (m, name) -> new Tuple<>((String) m[0], Collections.unmodifiableSet(new HashSet<>((List) 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>> languagesByName = languageContexts.entrySet() .stream() .sorted(Map.Entry.comparingByKey()) .collect(Collectors.toList()); for (Map.Entry> 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(); } }