Skip to content

Commit a82291e

Browse files
committed
Fix serialization bug in painless execute api request (#36075)
The `xContentType` was incorrectly serialized: `xContentType.mediaTypeWithoutParameters()` was used to serialize, but `xContentType.mediaType()` was used to de-serialize. Also the serialization test class did not test `ContextSetup` well, this was due to limitation in serialization test base class. Changed the test class to manually test xcontent serialization, so that for both binary and xcontent serialization tests, the `ContextSetup` is properly tested. Closes #36050
1 parent 9434bed commit a82291e

File tree

2 files changed

+54
-24
lines changed

2 files changed

+54
-24
lines changed

modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessExecuteAction.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -225,19 +225,20 @@ public boolean equals(Object o) {
225225
ContextSetup that = (ContextSetup) o;
226226
return Objects.equals(index, that.index) &&
227227
Objects.equals(document, that.document) &&
228-
Objects.equals(query, that.query);
228+
Objects.equals(query, that.query) &&
229+
Objects.equals(xContentType, that.xContentType);
229230
}
230231

231232
@Override
232233
public int hashCode() {
233-
return Objects.hash(index, document, query);
234+
return Objects.hash(index, document, query, xContentType);
234235
}
235236

236237
@Override
237238
public void writeTo(StreamOutput out) throws IOException {
238239
out.writeOptionalString(index);
239240
out.writeOptionalBytesReference(document);
240-
out.writeOptionalString(xContentType != null ? xContentType.mediaType(): null);
241+
out.writeOptionalString(xContentType != null ? xContentType.mediaTypeWithoutParameters(): null);
241242
out.writeOptionalNamedWriteable(query);
242243
}
243244

@@ -354,11 +355,13 @@ public void writeTo(StreamOutput out) throws IOException {
354355
// For testing only:
355356
@Override
356357
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
358+
builder.startObject();
357359
builder.field(SCRIPT_FIELD.getPreferredName(), script);
358360
builder.field(CONTEXT_FIELD.getPreferredName(), context.name);
359361
if (contextSetup != null) {
360362
builder.field(CONTEXT_SETUP_FIELD.getPreferredName(), contextSetup);
361363
}
364+
builder.endObject();
362365
return builder;
363366
}
364367

modules/lang-painless/src/test/java/org/elasticsearch/painless/PainlessExecuteRequestTests.java

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,55 @@
2020

2121
import org.elasticsearch.common.bytes.BytesReference;
2222
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
23+
import org.elasticsearch.common.io.stream.StreamInput;
2324
import org.elasticsearch.common.settings.Settings;
25+
import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
2426
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
27+
import org.elasticsearch.common.xcontent.XContent;
28+
import org.elasticsearch.common.xcontent.XContentBuilder;
2529
import org.elasticsearch.common.xcontent.XContentParser;
30+
import org.elasticsearch.common.xcontent.XContentType;
2631
import org.elasticsearch.index.query.MatchAllQueryBuilder;
2732
import org.elasticsearch.index.query.QueryBuilder;
2833
import org.elasticsearch.painless.PainlessExecuteAction.Request.ContextSetup;
2934
import org.elasticsearch.script.Script;
3035
import org.elasticsearch.script.ScriptContext;
3136
import org.elasticsearch.script.ScriptType;
3237
import org.elasticsearch.search.SearchModule;
33-
import org.elasticsearch.test.AbstractStreamableXContentTestCase;
38+
import org.elasticsearch.test.AbstractStreamableTestCase;
3439

3540
import java.io.IOException;
41+
import java.io.UncheckedIOException;
3642
import java.util.Collections;
3743

38-
public class PainlessExecuteRequestTests extends AbstractStreamableXContentTestCase<PainlessExecuteAction.Request> {
44+
import static org.hamcrest.Matchers.equalTo;
45+
46+
public class PainlessExecuteRequestTests extends AbstractStreamableTestCase<PainlessExecuteAction.Request> {
47+
48+
// Testing XContent serialization manually here, because the xContentType field in ContextSetup determines
49+
// how the request needs to parse and the xcontent serialization framework randomizes that. The XContentType
50+
// is not known and accessable when the test request instance is created in the xcontent serialization framework.
51+
// Changing that is a big change. Writing a custom xcontent test here is the best option for now, because as far
52+
// as I know this request class is the only case where this is a problem.
53+
public final void testFromXContent() throws Exception {
54+
for (int i = 0; i < 20; i++) {
55+
PainlessExecuteAction.Request testInstance = createTestInstance();
56+
ContextSetup contextSetup = testInstance.getContextSetup();
57+
XContent xContent = randomFrom(XContentType.values()).xContent();
58+
if (contextSetup != null && contextSetup.getXContentType() != null) {
59+
xContent = contextSetup.getXContentType().xContent();
60+
}
61+
62+
try (XContentBuilder builder = XContentBuilder.builder(xContent)) {
63+
builder.value(testInstance);
64+
StreamInput instanceInput = BytesReference.bytes(builder).streamInput();
65+
try (XContentParser parser = xContent.createParser(xContentRegistry(), LoggingDeprecationHandler.INSTANCE, instanceInput)) {
66+
PainlessExecuteAction.Request result = PainlessExecuteAction.Request.parse(parser);
67+
assertThat(result, equalTo(testInstance));
68+
}
69+
}
70+
}
71+
}
3972

4073
@Override
4174
protected NamedWriteableRegistry getNamedWriteableRegistry() {
@@ -60,16 +93,6 @@ protected PainlessExecuteAction.Request createBlankInstance() {
6093
return new PainlessExecuteAction.Request();
6194
}
6295

63-
@Override
64-
protected PainlessExecuteAction.Request doParseInstance(XContentParser parser) throws IOException {
65-
return PainlessExecuteAction.Request.parse(parser);
66-
}
67-
68-
@Override
69-
protected boolean supportsUnknownFields() {
70-
return false;
71-
}
72-
7396
public void testValidate() {
7497
Script script = new Script(ScriptType.STORED, null, randomAlphaOfLength(10), Collections.emptyMap());
7598
PainlessExecuteAction.Request request = new PainlessExecuteAction.Request(script, null, null);
@@ -78,20 +101,24 @@ public void testValidate() {
78101
assertEquals("Validation Failed: 1: only inline scripts are supported;", e.getMessage());
79102
}
80103

81-
private static ContextSetup randomContextSetup() {
104+
private static ContextSetup randomContextSetup() {
82105
String index = randomBoolean() ? randomAlphaOfLength(4) : null;
83106
QueryBuilder query = randomBoolean() ? new MatchAllQueryBuilder() : null;
84-
// TODO: pass down XContextType to createTestInstance() method.
85-
// otherwise the document itself is different causing test failures.
86-
// This should be done in a separate change as the test instance is created before xcontent type is randomly picked and
87-
// all the createTestInstance() methods need to be changed, which will make this a big chnage
88-
// BytesReference doc = randomBoolean() ? new BytesArray("{}") : null;
89107
BytesReference doc = null;
108+
XContentType xContentType = randomFrom(XContentType.values());
109+
if (randomBoolean()) {
110+
try {
111+
XContentBuilder xContentBuilder = XContentBuilder.builder(xContentType.xContent());
112+
xContentBuilder.startObject();
113+
xContentBuilder.endObject();
114+
doc = BytesReference.bytes(xContentBuilder);
115+
} catch (IOException e) {
116+
throw new UncheckedIOException(e);
117+
}
118+
}
90119

91120
ContextSetup contextSetup = new ContextSetup(index, doc, query);
92-
// if (doc != null) {
93-
// contextSetup.setXContentType(XContentType.JSON);
94-
// }
121+
contextSetup.setXContentType(xContentType);
95122
return contextSetup;
96123
}
97124
}

0 commit comments

Comments
 (0)