Skip to content

Commit e21692e

Browse files
INGEST: Make a few Processors callable by Painless (#32170)
* INGEST: Make a few Processors callable by Painless * Extracted a few stateless String processors as well as the json processor to static methods and whitelisted them in Painless * provide whitelist from processors plugin
1 parent ac63408 commit e21692e

File tree

13 files changed

+391
-23
lines changed

13 files changed

+391
-23
lines changed

modules/ingest-common/build.gradle

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,17 @@
2020
esplugin {
2121
description 'Module for ingest processors that do not require additional security permissions or have large dependencies and resources'
2222
classname 'org.elasticsearch.ingest.common.IngestCommonPlugin'
23+
extendedPlugins = ['lang-painless']
2324
}
2425

2526
dependencies {
27+
compileOnly project(':modules:lang-painless')
2628
compile project(':libs:grok')
2729
}
2830

2931
compileJava.options.compilerArgs << "-Xlint:-unchecked,-rawtypes"
3032
compileTestJava.options.compilerArgs << "-Xlint:-unchecked,-rawtypes"
33+
34+
integTestCluster {
35+
module project(':modules:lang-painless')
36+
}

modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/BytesProcessor.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,13 @@ public final class BytesProcessor extends AbstractStringProcessor {
3535
super(processorTag, field, ignoreMissing, targetField);
3636
}
3737

38+
public static long apply(String value) {
39+
return ByteSizeValue.parseBytesSizeValue(value, null, "Ingest Field").getBytes();
40+
}
41+
3842
@Override
3943
protected Long process(String value) {
40-
return ByteSizeValue.parseBytesSizeValue(value, null, getField()).getBytes();
44+
return apply(value);
4145
}
4246

4347
@Override

modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/JsonProcessor.java

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,11 @@ boolean isAddToRoot() {
6767
return addToRoot;
6868
}
6969

70-
@Override
71-
public void execute(IngestDocument document) throws Exception {
72-
Object fieldValue = document.getFieldValue(field, Object.class);
73-
BytesReference bytesRef = (fieldValue == null) ? new BytesArray("null") : new BytesArray(fieldValue.toString());
70+
public static Object apply(Object fieldValue) {
71+
BytesReference bytesRef = fieldValue == null ? new BytesArray("null") : new BytesArray(fieldValue.toString());
7472
try (InputStream stream = bytesRef.streamInput();
7573
XContentParser parser = JsonXContent.jsonXContent
76-
.createParser(NamedXContentRegistry.EMPTY, DeprecationHandler.THROW_UNSUPPORTED_OPERATION, stream)) {
74+
.createParser(NamedXContentRegistry.EMPTY, DeprecationHandler.THROW_UNSUPPORTED_OPERATION, stream)) {
7775
XContentParser.Token token = parser.nextToken();
7876
Object value = null;
7977
if (token == XContentParser.Token.VALUE_NULL) {
@@ -91,20 +89,32 @@ public void execute(IngestDocument document) throws Exception {
9189
} else if (token == XContentParser.Token.VALUE_EMBEDDED_OBJECT) {
9290
throw new IllegalArgumentException("cannot read binary value");
9391
}
94-
if (addToRoot && (value instanceof Map)) {
95-
for (Map.Entry<String, Object> entry : ((Map<String, Object>) value).entrySet()) {
96-
document.setFieldValue(entry.getKey(), entry.getValue());
97-
}
98-
} else if (addToRoot) {
99-
throw new IllegalArgumentException("cannot add non-map fields to root of document");
100-
} else {
101-
document.setFieldValue(targetField, value);
102-
}
92+
return value;
10393
} catch (IOException e) {
10494
throw new IllegalArgumentException(e);
10595
}
10696
}
10797

98+
public static void apply(Map<String, Object> ctx, String fieldName) {
99+
Object value = apply(ctx.get(fieldName));
100+
if (value instanceof Map) {
101+
@SuppressWarnings("unchecked")
102+
Map<String, Object> map = (Map<String, Object>) value;
103+
ctx.putAll(map);
104+
} else {
105+
throw new IllegalArgumentException("cannot add non-map fields to root of document");
106+
}
107+
}
108+
109+
@Override
110+
public void execute(IngestDocument document) throws Exception {
111+
if (addToRoot) {
112+
apply(document.getSourceAndMetadata(), field);
113+
} else {
114+
document.setFieldValue(targetField, apply(document.getFieldValue(field, Object.class)));
115+
}
116+
}
117+
108118
@Override
109119
public String getType() {
110120
return TYPE;

modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/LowercaseProcessor.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,13 @@ public final class LowercaseProcessor extends AbstractStringProcessor {
3535
super(processorTag, field, ignoreMissing, targetField);
3636
}
3737

38+
public static String apply(String value) {
39+
return value.toLowerCase(Locale.ROOT);
40+
}
41+
3842
@Override
3943
protected String process(String value) {
40-
return value.toLowerCase(Locale.ROOT);
44+
return apply(value);
4145
}
4246

4347
@Override
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.ingest.common;
21+
22+
import java.util.Map;
23+
24+
public final class Processors {
25+
26+
public static long bytes(String value) {
27+
return BytesProcessor.apply(value);
28+
}
29+
30+
public static String lowercase(String value) {
31+
return LowercaseProcessor.apply(value);
32+
}
33+
34+
public static String uppercase(String value) {
35+
return UppercaseProcessor.apply(value);
36+
}
37+
38+
public static Object json(Object fieldValue) {
39+
return JsonProcessor.apply(fieldValue);
40+
}
41+
42+
public static void json(Map<String, Object> ctx, String field) {
43+
JsonProcessor.apply(ctx, field);
44+
}
45+
46+
public static String urlDecode(String value) {
47+
return URLDecodeProcessor.apply(value);
48+
}
49+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.ingest.common;
21+
22+
import org.elasticsearch.painless.spi.PainlessExtension;
23+
import org.elasticsearch.painless.spi.Whitelist;
24+
import org.elasticsearch.painless.spi.WhitelistLoader;
25+
import org.elasticsearch.script.IngestScript;
26+
import org.elasticsearch.script.ScriptContext;
27+
28+
import java.util.Collections;
29+
import java.util.List;
30+
import java.util.Map;
31+
32+
public class ProcessorsWhitelistExtension implements PainlessExtension {
33+
34+
private static final Whitelist WHITELIST =
35+
WhitelistLoader.loadFromResourceFiles(ProcessorsWhitelistExtension.class, "processors_whitelist.txt");
36+
37+
@Override
38+
public Map<ScriptContext<?>, List<Whitelist>> getContextWhitelists() {
39+
return Collections.singletonMap(IngestScript.CONTEXT, Collections.singletonList(WHITELIST));
40+
}
41+
}

modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/URLDecodeProcessor.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,19 @@ public final class URLDecodeProcessor extends AbstractStringProcessor {
3434
super(processorTag, field, ignoreMissing, targetField);
3535
}
3636

37-
@Override
38-
protected String process(String value) {
37+
public static String apply(String value) {
3938
try {
4039
return URLDecoder.decode(value, "UTF-8");
4140
} catch (UnsupportedEncodingException e) {
42-
throw new IllegalArgumentException("could not URL-decode field[" + getField() + "]", e);
41+
throw new IllegalArgumentException("Could not URL-decode value.", e);
4342
}
4443
}
4544

45+
@Override
46+
protected String process(String value) {
47+
return apply(value);
48+
}
49+
4650
@Override
4751
public String getType() {
4852
return TYPE;

modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/UppercaseProcessor.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,13 @@ public final class UppercaseProcessor extends AbstractStringProcessor {
3434
super(processorTag, field, ignoreMissing, targetField);
3535
}
3636

37+
public static String apply(String value) {
38+
return value.toUpperCase(Locale.ROOT);
39+
}
40+
3741
@Override
3842
protected String process(String value) {
39-
return value.toUpperCase(Locale.ROOT);
43+
return apply(value);
4044
}
4145

4246
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
org.elasticsearch.ingest.common.ProcessorsWhitelistExtension
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#
2+
# Licensed to Elasticsearch under one or more contributor
3+
# license agreements. See the NOTICE file distributed with
4+
# this work for additional information regarding copyright
5+
# ownership. Elasticsearch licenses this file to you under
6+
# the Apache License, Version 2.0 (the "License"); you may
7+
# not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing,
13+
# software distributed under the License is distributed on an
14+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
# KIND, either express or implied. See the License for the
16+
# specific language governing permissions and limitations
17+
# under the License.
18+
#
19+
20+
# This file contains a whitelist of static processor methods that can be accessed from painless
21+
22+
class org.elasticsearch.ingest.common.Processors {
23+
long bytes(String)
24+
String lowercase(String)
25+
String uppercase(String)
26+
Object json(Object)
27+
void json(Map, String)
28+
String urlDecode(String)
29+
}

modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/BytesProcessorTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public void testTooLarge() {
6363
Processor processor = newProcessor(fieldName, randomBoolean(), fieldName);
6464
ElasticsearchException exception = expectThrows(ElasticsearchException.class, () -> processor.execute(ingestDocument));
6565
assertThat(exception.getMessage(),
66-
CoreMatchers.equalTo("failed to parse setting [" + fieldName + "] with value [8912pb] as a size in bytes"));
66+
CoreMatchers.equalTo("failed to parse setting [Ingest Field] with value [8912pb] as a size in bytes"));
6767
assertThat(exception.getCause().getMessage(),
6868
CoreMatchers.containsString("Values greater than 9223372036854775807 bytes are not supported"));
6969
}
@@ -93,6 +93,6 @@ public void testFractional() throws Exception {
9393
processor.execute(ingestDocument);
9494
assertThat(ingestDocument.getFieldValue(fieldName, expectedResultType()), equalTo(1126L));
9595
assertWarnings("Fractional bytes values are deprecated. Use non-fractional bytes values instead: [1.1kb] found for setting " +
96-
"[" + fieldName + "]");
96+
"[Ingest Field]");
9797
}
9898
}

0 commit comments

Comments
 (0)