Skip to content

Commit 476af1d

Browse files
committed
Mirror upstream elastic#137455 as single snapshot commit for AI review
BASE=cb11a7164033f79a0c401a85ca8db22105631888 HEAD=078cff9adadabdb362d925d026037fe3ae84fed7 Branch=main
1 parent cb11a71 commit 476af1d

File tree

14 files changed

+315
-113
lines changed

14 files changed

+315
-113
lines changed

docs/changelog/137455.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 137455
2+
summary: Preview index request
3+
area: Transform
4+
type: enhancement
5+
issues: []
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
9214000
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
batched_response_might_include_reduction_failure,9213000
1+
transform_preview_as_index_request,9214000

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/TransformField.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ public final class TransformField {
6060

6161
public static final ParseField ALLOW_NO_MATCH = new ParseField("allow_no_match");
6262
public static final ParseField CHECK_FOR_DANGLING_TASKS = new ParseField("check_dangling_tasks");
63+
public static final ParseField PREVIEW_AS_INDEX_REQUEST = new ParseField("as_index_request");
6364
/**
6465
* Fields for checkpointing
6566
*/
@@ -102,6 +103,8 @@ public final class TransformField {
102103

103104
// internal document id
104105
public static final String DOCUMENT_ID_FIELD = "_id";
106+
// internal document source
107+
public static final String DOCUMENT_SOURCE_FIELD = "_source";
105108

106109
public static final PersistentTasksCustomMetadata.Assignment AWAITING_UPGRADE = new PersistentTasksCustomMetadata.Assignment(
107110
null,

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/PreviewTransformAction.java

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
package org.elasticsearch.xpack.core.transform.action;
99

10+
import org.elasticsearch.ElasticsearchStatusException;
11+
import org.elasticsearch.TransportVersion;
1012
import org.elasticsearch.action.ActionRequestValidationException;
1113
import org.elasticsearch.action.ActionResponse;
1214
import org.elasticsearch.action.ActionType;
@@ -18,6 +20,7 @@
1820
import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
1921
import org.elasticsearch.common.xcontent.XContentHelper;
2022
import org.elasticsearch.core.TimeValue;
23+
import org.elasticsearch.rest.RestStatus;
2124
import org.elasticsearch.tasks.CancellableTask;
2225
import org.elasticsearch.tasks.Task;
2326
import org.elasticsearch.tasks.TaskId;
@@ -55,19 +58,24 @@ private PreviewTransformAction() {
5558

5659
public static class Request extends AcknowledgedRequest<Request> implements ToXContentObject {
5760

61+
static final TransportVersion PREVIEW_AS_INDEX_REQUEST = TransportVersion.fromName("transform_preview_as_index_request");
5862
private final TransformConfig config;
63+
private final boolean previewAsIndexRequest;
5964

60-
public Request(TransformConfig config, TimeValue timeout) {
65+
public Request(TransformConfig config, TimeValue timeout, boolean previewAsIndexRequest) {
6166
super(TRAPPY_IMPLICIT_DEFAULT_MASTER_NODE_TIMEOUT, timeout);
6267
this.config = config;
68+
this.previewAsIndexRequest = previewAsIndexRequest;
6369
}
6470

6571
public Request(StreamInput in) throws IOException {
6672
super(in);
6773
this.config = new TransformConfig(in);
74+
this.previewAsIndexRequest = in.getTransportVersion().supports(PREVIEW_AS_INDEX_REQUEST) ? in.readBoolean() : false;
6875
}
6976

70-
public static Request fromXContent(final XContentParser parser, TimeValue timeout) throws IOException {
77+
public static Request fromXContent(final XContentParser parser, TimeValue timeout, boolean previewAsIndexRequest)
78+
throws IOException {
7179
Map<String, Object> content = parser.map();
7280
// dest.index is not required for _preview, so we just supply our own
7381
Map<String, String> tempDestination = new HashMap<>();
@@ -89,7 +97,7 @@ public static Request fromXContent(final XContentParser parser, TimeValue timeou
8997
XContentType.JSON
9098
)
9199
) {
92-
return new Request(TransformConfig.fromXContent(newParser, null, false), timeout);
100+
return new Request(TransformConfig.fromXContent(newParser, null, false), timeout, previewAsIndexRequest);
93101
}
94102
}
95103

@@ -115,15 +123,29 @@ public TransformConfig getConfig() {
115123
return config;
116124
}
117125

126+
public boolean previewAsIndexRequest() {
127+
return previewAsIndexRequest;
128+
}
129+
118130
@Override
119131
public void writeTo(StreamOutput out) throws IOException {
120132
super.writeTo(out);
121133
this.config.writeTo(out);
134+
if (out.getTransportVersion().supports(PREVIEW_AS_INDEX_REQUEST)) {
135+
out.writeBoolean(previewAsIndexRequest);
136+
} else if (previewAsIndexRequest) {
137+
throw new ElasticsearchStatusException(
138+
"Cannot send a _preview request with "
139+
+ TransformField.PREVIEW_AS_INDEX_REQUEST.getPreferredName()
140+
+ " to an outdated node. Please upgrade the node to 9.3.0+ and try again.",
141+
RestStatus.BAD_REQUEST
142+
);
143+
}
122144
}
123145

124146
@Override
125147
public int hashCode() {
126-
return Objects.hash(config);
148+
return Objects.hash(config, previewAsIndexRequest);
127149
}
128150

129151
@Override
@@ -135,7 +157,7 @@ public boolean equals(Object obj) {
135157
return false;
136158
}
137159
Request other = (Request) obj;
138-
return Objects.equals(config, other.config);
160+
return Objects.equals(config, other.config) && (previewAsIndexRequest == other.previewAsIndexRequest);
139161
}
140162

141163
@Override
@@ -170,10 +192,6 @@ public List<Map<String, Object>> getDocs() {
170192
return docs;
171193
}
172194

173-
public TransformDestIndexSettings getGeneratedDestIndexSettings() {
174-
return generatedDestIndexSettings;
175-
}
176-
177195
@Override
178196
public void writeTo(StreamOutput out) throws IOException {
179197
out.writeInt(docs.size());

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/action/PreviewTransformActionRequestTests.java

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,19 @@
77

88
package org.elasticsearch.xpack.core.transform.action;
99

10+
import org.elasticsearch.ElasticsearchStatusException;
1011
import org.elasticsearch.action.support.master.AcknowledgedRequest;
1112
import org.elasticsearch.common.bytes.BytesArray;
1213
import org.elasticsearch.common.io.stream.Writeable;
1314
import org.elasticsearch.core.Strings;
15+
import org.elasticsearch.rest.RestStatus;
1416
import org.elasticsearch.tasks.CancellableTask;
1517
import org.elasticsearch.tasks.Task;
1618
import org.elasticsearch.tasks.TaskId;
1719
import org.elasticsearch.xcontent.DeprecationHandler;
1820
import org.elasticsearch.xcontent.XContentParser;
21+
import org.elasticsearch.xcontent.XContentParserConfiguration;
22+
import org.elasticsearch.xcontent.XContentType;
1923
import org.elasticsearch.xcontent.json.JsonXContent;
2024
import org.elasticsearch.xpack.core.transform.AbstractSerializingTransformTestCase;
2125
import org.elasticsearch.xpack.core.transform.action.PreviewTransformAction.Request;
@@ -26,7 +30,9 @@
2630

2731
import java.io.IOException;
2832
import java.util.Map;
33+
import java.util.function.Predicate;
2934

35+
import static org.elasticsearch.test.BWCVersions.DEFAULT_BWC_VERSIONS;
3036
import static org.elasticsearch.xpack.core.transform.transforms.SourceConfigTests.randomSourceConfig;
3137
import static org.hamcrest.Matchers.equalTo;
3238
import static org.hamcrest.Matchers.instanceOf;
@@ -36,7 +42,7 @@ public class PreviewTransformActionRequestTests extends AbstractSerializingTrans
3642

3743
@Override
3844
protected Request doParseInstance(XContentParser parser) throws IOException {
39-
return Request.fromXContent(parser, AcknowledgedRequest.DEFAULT_ACK_TIMEOUT);
45+
return Request.fromXContent(parser, AcknowledgedRequest.DEFAULT_ACK_TIMEOUT, false);
4046
}
4147

4248
@Override
@@ -46,7 +52,11 @@ protected Writeable.Reader<Request> instanceReader() {
4652

4753
@Override
4854
protected Request createTestInstance() {
49-
TransformConfig config = new TransformConfig(
55+
return new Request(randomTransformConfig(), randomTimeValue(), randomBoolean());
56+
}
57+
58+
private static TransformConfig randomTransformConfig() {
59+
return new TransformConfig(
5060
"transform-preview",
5161
randomSourceConfig(),
5262
new DestConfig("unused-transform-preview-index", null, null),
@@ -62,12 +72,57 @@ protected Request createTestInstance() {
6272
null,
6373
null
6474
);
65-
return new Request(config, randomTimeValue());
75+
}
76+
77+
@Override
78+
protected Request createXContextTestInstance(XContentType xContentType) {
79+
return new Request(randomTransformConfig(), AcknowledgedRequest.DEFAULT_ACK_TIMEOUT, false);
6680
}
6781

6882
@Override
6983
protected Request mutateInstance(Request instance) {
70-
return null;// TODO implement https://github.com/elastic/elasticsearch/issues/25929
84+
return randomBoolean()
85+
? new Request(
86+
randomValueOtherThan(instance.getConfig(), PreviewTransformActionRequestTests::randomTransformConfig),
87+
instance.ackTimeout(),
88+
instance.previewAsIndexRequest()
89+
)
90+
: new Request(instance.getConfig(), instance.ackTimeout(), instance.previewAsIndexRequest() == false);
91+
}
92+
93+
public void testAsIndexRequestIsNotBackwardsCompatible() throws IOException {
94+
var unsupportedVersions = DEFAULT_BWC_VERSIONS.stream()
95+
.filter(Predicate.not(version -> version.supports(Request.PREVIEW_AS_INDEX_REQUEST)))
96+
.toList();
97+
for (int runs = 0; runs < NUMBER_OF_TEST_RUNS; runs++) {
98+
var testInstance = createTestInstance();
99+
for (var unsupportedVersion : unsupportedVersions) {
100+
if (testInstance.previewAsIndexRequest()) {
101+
var statusException = assertThrows(
102+
ElasticsearchStatusException.class,
103+
() -> copyWriteable(testInstance, getNamedWriteableRegistry(), instanceReader(), unsupportedVersion)
104+
);
105+
assertThat(statusException.status(), equalTo(RestStatus.BAD_REQUEST));
106+
assertThat(
107+
statusException.getMessage(),
108+
equalTo(
109+
"Cannot send a _preview request with as_index_request to an outdated node. "
110+
+ "Please upgrade the node to 9.3.0+ and try again."
111+
)
112+
);
113+
} else {
114+
var deserializedInstance = copyWriteable(
115+
testInstance,
116+
getNamedWriteableRegistry(),
117+
instanceReader(),
118+
unsupportedVersion
119+
);
120+
assertNotSame(unsupportedVersion.toString(), deserializedInstance, testInstance);
121+
assertEquals(unsupportedVersion.toString(), deserializedInstance, testInstance);
122+
assertEquals(unsupportedVersion.toString(), deserializedInstance.hashCode(), testInstance.hashCode());
123+
}
124+
}
125+
}
71126
}
72127

73128
public void testParsingOverwritesIdField() throws IOException {
@@ -125,13 +180,13 @@ private void testParsingOverwrites(
125180

126181
try (
127182
XContentParser parser = JsonXContent.jsonXContent.createParser(
128-
xContentRegistry(),
129-
DeprecationHandler.THROW_UNSUPPORTED_OPERATION,
183+
XContentParserConfiguration.EMPTY.withRegistry(xContentRegistry())
184+
.withDeprecationHandler(DeprecationHandler.THROW_UNSUPPORTED_OPERATION),
130185
json.streamInput()
131186
)
132187
) {
133188

134-
Request request = Request.fromXContent(parser, AcknowledgedRequest.DEFAULT_ACK_TIMEOUT);
189+
Request request = Request.fromXContent(parser, AcknowledgedRequest.DEFAULT_ACK_TIMEOUT, false);
135190
assertThat(request.getConfig().getId(), is(equalTo(expectedTransformId)));
136191
assertThat(request.getConfig().getDestination().getIndex(), is(equalTo(expectedDestIndex)));
137192
assertThat(request.getConfig().getDestination().getPipeline(), is(equalTo(expectedDestPipeline)));

x-pack/plugin/transform/qa/single-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/transform/integration/TransformLatestRestIT.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,29 @@ public void testLatestWithAggregateMetricDoubleAsUniqueKey() throws Exception {
148148
}
149149
}
150150

151+
@SuppressWarnings("unchecked")
152+
public void testPreviewAsIndexRequest() throws IOException {
153+
setupDataAccessRole(DATA_ACCESS_ROLE, REVIEWS_INDEX_NAME);
154+
var createPreviewRequest = createRequestWithAuth("POST", getTransformEndpoint() + "_preview?as_index_request", null);
155+
createPreviewRequest.setJsonEntity(Strings.format("""
156+
{
157+
"source": {
158+
"index": "%s"
159+
},
160+
"latest": {
161+
"unique_key": [ "user_id" ],
162+
"sort": "@timestamp"
163+
}
164+
}""", REVIEWS_INDEX_NAME));
165+
var previewTransformResponse = entityAsMap(client().performRequest(createPreviewRequest));
166+
var preview = (List<Map<String, Object>>) previewTransformResponse.get("preview");
167+
preview.forEach(p -> {
168+
assertNotNull(XContentMapValues.extractValue("_id", p));
169+
assertNotNull(XContentMapValues.extractValue("_source.@timestamp", p));
170+
assertNotNull(XContentMapValues.extractValue("_source.user_id", p));
171+
});
172+
}
173+
151174
public void testContinuousLatestWithFrom_NoDocs() throws Exception {
152175
testContinuousLatestWithFrom("latest_from_no_docs", "reviews_from_no_docs", "2017-02-20", 0);
153176
}

x-pack/plugin/transform/qa/single-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/transform/integration/TransformPivotRestIT.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1411,6 +1411,54 @@ private List<String> previewWithOffset(String offset) throws IOException {
14111411
return preview.stream().map(p -> (String) p.get("by_week")).toList();
14121412
}
14131413

1414+
@SuppressWarnings("unchecked")
1415+
public void testPreviewAsIndexRequest() throws Exception {
1416+
setupDataAccessRole(DATA_ACCESS_ROLE, REVIEWS_INDEX_NAME);
1417+
final Request createPreviewRequest = createRequestWithAuth(
1418+
"POST",
1419+
getTransformEndpoint() + "_preview?as_index_request",
1420+
BASIC_AUTH_VALUE_TRANSFORM_ADMIN_WITH_SOME_DATA_ACCESS
1421+
);
1422+
1423+
createPreviewRequest.setJsonEntity(Strings.format("""
1424+
{
1425+
"source": {
1426+
"index": "%s"
1427+
},
1428+
"pivot": {
1429+
"group_by": {
1430+
"user.id": {
1431+
"terms": {
1432+
"field": "user_id"
1433+
}
1434+
},
1435+
"by_day": {
1436+
"date_histogram": {
1437+
"fixed_interval": "1d",
1438+
"field": "timestamp"
1439+
}
1440+
}
1441+
},
1442+
"aggregations": {
1443+
"user.avg_rating": {
1444+
"avg": {
1445+
"field": "stars"
1446+
}
1447+
}
1448+
}
1449+
}
1450+
}""", REVIEWS_INDEX_NAME));
1451+
1452+
var previewTransformResponse = entityAsMap(client().performRequest(createPreviewRequest));
1453+
var preview = (List<Map<String, Object>>) previewTransformResponse.get("preview");
1454+
preview.forEach(p -> {
1455+
assertNotNull(XContentMapValues.extractValue("_id", p));
1456+
assertNotNull(XContentMapValues.extractValue("_source.by_day", p));
1457+
assertNotNull(XContentMapValues.extractValue("_source.user.id", p));
1458+
assertNotNull(XContentMapValues.extractValue("_source.user.avg_rating", p));
1459+
});
1460+
}
1461+
14141462
public void testPivotWithMaxOnDateField() throws Exception {
14151463
String transformId = "simple_date_histogram_pivot_with_max_time";
14161464
String transformIndex = "pivot_reviews_via_date_histogram_with_max_time";

x-pack/plugin/transform/src/internalClusterTest/java/org/elasticsearch/xpack/transform/integration/TransformNoRemoteClusterClientNodeIT.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ protected Settings nodeSettings() {
4141
public void testPreviewTransformWithRemoteIndex() {
4242
String transformId = "transform-with-remote-index";
4343
TransformConfig config = randomConfig(transformId, "remote_cluster:my-index");
44-
PreviewTransformAction.Request request = new PreviewTransformAction.Request(config, AcknowledgedRequest.DEFAULT_ACK_TIMEOUT);
44+
PreviewTransformAction.Request request = new PreviewTransformAction.Request(config, AcknowledgedRequest.DEFAULT_ACK_TIMEOUT, false);
4545
ElasticsearchStatusException e = expectThrows(
4646
ElasticsearchStatusException.class,
4747
() -> client().execute(PreviewTransformAction.INSTANCE, request).actionGet()

x-pack/plugin/transform/src/internalClusterTest/java/org/elasticsearch/xpack/transform/integration/TransformNoTransformNodeIT.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public void testGetTransform() {
6363
public void testPreviewTransform() {
6464
String transformId = "transform-1";
6565
TransformConfig config = randomConfig(transformId);
66-
PreviewTransformAction.Request request = new PreviewTransformAction.Request(config, AcknowledgedRequest.DEFAULT_ACK_TIMEOUT);
66+
PreviewTransformAction.Request request = new PreviewTransformAction.Request(config, AcknowledgedRequest.DEFAULT_ACK_TIMEOUT, false);
6767
ElasticsearchStatusException e = expectThrows(
6868
ElasticsearchStatusException.class,
6969
() -> client().execute(PreviewTransformAction.INSTANCE, request).actionGet()

0 commit comments

Comments
 (0)