Skip to content

Commit ebcac2d

Browse files
authored
[Transform] add new exclude_generated flag to GET transform (#63093)
This adds a new flag `exclude_generated` for GET transform API. This flag is useful for when a transform needs to be cloned within a cluster or exported/imported between clusters. It removes certain fields that are not able to be set via the PUT api (e.g. version, create_time). relates #63055
1 parent c1de07f commit ebcac2d

File tree

14 files changed

+153
-25
lines changed

14 files changed

+153
-25
lines changed

client/rest-high-level/src/main/java/org/elasticsearch/client/TransformRequestConverters.java

+4
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import static org.elasticsearch.client.RequestConverters.createEntity;
4141
import static org.elasticsearch.client.transform.DeleteTransformRequest.FORCE;
4242
import static org.elasticsearch.client.transform.GetTransformRequest.ALLOW_NO_MATCH;
43+
import static org.elasticsearch.client.transform.GetTransformRequest.EXCLUDE_GENERATED;
4344
import static org.elasticsearch.client.transform.PutTransformRequest.DEFER_VALIDATION;
4445
import static org.elasticsearch.client.transform.StopTransformRequest.WAIT_FOR_CHECKPOINT;
4546

@@ -89,6 +90,9 @@ static Request getTransform(GetTransformRequest getRequest) {
8990
if (getRequest.getAllowNoMatch() != null) {
9091
request.addParameter(ALLOW_NO_MATCH, getRequest.getAllowNoMatch().toString());
9192
}
93+
if (getRequest.getExcludeGenerated() != null) {
94+
request.addParameter(EXCLUDE_GENERATED, getRequest.getExcludeGenerated().toString());
95+
}
9296
return request;
9397
}
9498

client/rest-high-level/src/main/java/org/elasticsearch/client/transform/GetTransformRequest.java

+12-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
public class GetTransformRequest implements Validatable {
3232

33+
public static final String EXCLUDE_GENERATED = "exclude_generated";
3334
public static final String ALLOW_NO_MATCH = "allow_no_match";
3435
/**
3536
* Helper method to create a request that will get ALL Transforms
@@ -42,6 +43,7 @@ public static GetTransformRequest getAllTransformRequest() {
4243
private final List<String> ids;
4344
private PageParams pageParams;
4445
private Boolean allowNoMatch;
46+
private Boolean excludeGenerated;
4547

4648
public GetTransformRequest(String... ids) {
4749
this.ids = Arrays.asList(ids);
@@ -67,6 +69,14 @@ public void setAllowNoMatch(Boolean allowNoMatch) {
6769
this.allowNoMatch = allowNoMatch;
6870
}
6971

72+
public void setExcludeGenerated(boolean excludeGenerated) {
73+
this.excludeGenerated = excludeGenerated;
74+
}
75+
76+
public Boolean getExcludeGenerated() {
77+
return excludeGenerated;
78+
}
79+
7080
@Override
7181
public Optional<ValidationException> validate() {
7282
if (ids == null || ids.isEmpty()) {
@@ -80,7 +90,7 @@ public Optional<ValidationException> validate() {
8090

8191
@Override
8292
public int hashCode() {
83-
return Objects.hash(ids, pageParams, allowNoMatch);
93+
return Objects.hash(ids, pageParams, excludeGenerated, allowNoMatch);
8494
}
8595

8696
@Override
@@ -95,6 +105,7 @@ public boolean equals(Object obj) {
95105
GetTransformRequest other = (GetTransformRequest) obj;
96106
return Objects.equals(ids, other.ids)
97107
&& Objects.equals(pageParams, other.pageParams)
108+
&& Objects.equals(excludeGenerated, other.excludeGenerated)
98109
&& Objects.equals(allowNoMatch, other.allowNoMatch);
99110
}
100111
}

client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/TransformDocumentationIT.java

+1
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,7 @@ public void testGetDataFrameTransform() throws IOException, InterruptedException
687687
// tag::get-transform-request-options
688688
request.setPageParams(new PageParams(0, 100)); // <1>
689689
request.setAllowNoMatch(true); // <2>
690+
request.setExcludeGenerated(false); // <3>
690691
// end::get-transform-request-options
691692

692693
// tag::get-transform-execute

docs/java-rest/high-level/transform/get_transform.asciidoc

+3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ include-tagged::{doc-tests-file}[{api}-request-options]
3434
{transforms} to skip. `size` specifies the maximum number of
3535
{transforms} to get. Defaults to `0` and `100` respectively.
3636
<2> Whether to ignore if a wildcard expression matches no {transforms}.
37+
<3> Optional boolean value for requesting the {transform} in a format that can
38+
then be put into another cluster. Certain fields that can only be set when
39+
the {transform} is created are removed.
3740

3841

3942
include::../execution.asciidoc[]

docs/reference/transform/apis/get-transform.asciidoc

+12-6
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@ Retrieves configuration information for {transforms}.
2626
[[get-transform-prereqs]]
2727
== {api-prereq-title}
2828

29-
If the {es} {security-features} are enabled, you must have the following
29+
If the {es} {security-features} are enabled, you must have the following
3030
privileges:
3131

32-
* `monitor_transform`
32+
* `monitor_transform`
3333

34-
The built-in `transform_user` role has this privilege.
34+
The built-in `transform_user` role has this privilege.
3535

3636
For more information, see <<security-privileges>> and <<built-in-roles>>.
3737

@@ -49,7 +49,7 @@ specifying `*` as the `<transform_id>`, or by omitting the `<transform_id>`.
4949
`<transform_id>`::
5050
(Optional, string)
5151
include::{es-repo-dir}/rest-api/common-parms.asciidoc[tag=transform-id-wildcard]
52-
52+
5353
[[get-transform-query-parms]]
5454
== {api-query-parms-title}
5555

@@ -65,6 +65,12 @@ include::{es-repo-dir}/rest-api/common-parms.asciidoc[tag=from-transforms]
6565
(Optional, integer)
6666
include::{es-repo-dir}/rest-api/common-parms.asciidoc[tag=size-transforms]
6767

68+
`exclude_generated`::
69+
(Optional, boolean)
70+
Excludes fields that were automatically added when creating the transform.
71+
This allows the configuration to be in an acceptable format to be retrieved
72+
and then added to another cluster. Default is false.
73+
6874
[[get-transform-response]]
6975
== {api-response-body-title}
7076

@@ -79,13 +85,13 @@ This property is informational; you cannot change its value.
7985
`version`::
8086
(string) The version of {es} that existed on the node when the {transform} was
8187
created.
82-
88+
8389
[[get-transform-response-codes]]
8490
== {api-response-codes-title}
8591

8692
`404` (Missing resources)::
8793
If `allow_no_match` is `false`, this code indicates that there are no
88-
resources that match the request or only partial matches for the request.
94+
resources that match the request or only partial matches for the request.
8995

9096
[[get-transform-example]]
9197
== {api-examples-title}

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

+1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ public final class TransformField {
8484
* API's)
8585
*/
8686
public static final String FOR_INTERNAL_STORAGE = "for_internal_storage";
87+
public static final String EXCLUDE_GENERATED = "exclude_generated";
8788

8889
// internal document id
8990
public static String DOCUMENT_ID_FIELD = "_id";

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

+6-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.elasticsearch.common.xcontent.XContentBuilder;
1717
import org.elasticsearch.common.xcontent.XContentParser;
1818
import org.elasticsearch.license.RemoteClusterLicenseChecker;
19+
import org.elasticsearch.xpack.core.transform.TransformField;
1920
import org.elasticsearch.xpack.core.transform.utils.ExceptionsHelper;
2021

2122
import java.io.IOException;
@@ -113,7 +114,11 @@ public void writeTo(StreamOutput out) throws IOException {
113114
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
114115
builder.startObject();
115116
builder.array(INDEX.getPreferredName(), index);
116-
builder.field(QUERY.getPreferredName(), queryConfig);
117+
if (params.paramAsBoolean(TransformField.EXCLUDE_GENERATED, false) == false) {
118+
builder.field(QUERY.getPreferredName(), queryConfig);
119+
} else if(queryConfig.equals(QueryConfig.matchAll()) == false) {
120+
builder.field(QUERY.getPreferredName(), queryConfig);
121+
}
117122
builder.endObject();
118123
return builder;
119124
}

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

+23-17
Original file line numberDiff line numberDiff line change
@@ -342,9 +342,31 @@ public void writeTo(final StreamOutput out) throws IOException {
342342

343343
@Override
344344
public XContentBuilder toXContent(final XContentBuilder builder, final Params params) throws IOException {
345+
final boolean excludeGenerated = params.paramAsBoolean(TransformField.EXCLUDE_GENERATED, false);
346+
final boolean forInternalStorage = params.paramAsBoolean(TransformField.FOR_INTERNAL_STORAGE, false);
347+
assert (forInternalStorage && excludeGenerated) == false:
348+
"unsupported behavior, exclude_generated is true and for_internal_storage is true";
345349
builder.startObject();
346350
builder.field(TransformField.ID.getPreferredName(), id);
347-
builder.field(TransformField.SOURCE.getPreferredName(), source);
351+
if (excludeGenerated == false) {
352+
if (headers.isEmpty() == false && forInternalStorage) {
353+
builder.field(HEADERS.getPreferredName(), headers);
354+
}
355+
if (transformVersion != null) {
356+
builder.field(TransformField.VERSION.getPreferredName(), transformVersion);
357+
}
358+
if (createTime != null) {
359+
builder.timeField(
360+
TransformField.CREATE_TIME.getPreferredName(),
361+
TransformField.CREATE_TIME.getPreferredName() + "_string",
362+
createTime.toEpochMilli()
363+
);
364+
}
365+
if (forInternalStorage) {
366+
builder.field(TransformField.INDEX_DOC_TYPE.getPreferredName(), NAME);
367+
}
368+
}
369+
builder.field(TransformField.SOURCE.getPreferredName(), source, params);
348370
builder.field(TransformField.DESTINATION.getPreferredName(), dest);
349371
if (frequency != null) {
350372
builder.field(TransformField.FREQUENCY.getPreferredName(), frequency.getStringRep());
@@ -357,26 +379,10 @@ public XContentBuilder toXContent(final XContentBuilder builder, final Params pa
357379
if (pivotConfig != null) {
358380
builder.field(PIVOT_TRANSFORM.getPreferredName(), pivotConfig);
359381
}
360-
if (params.paramAsBoolean(TransformField.FOR_INTERNAL_STORAGE, false)) {
361-
builder.field(TransformField.INDEX_DOC_TYPE.getPreferredName(), NAME);
362-
}
363-
if (headers.isEmpty() == false && params.paramAsBoolean(TransformField.FOR_INTERNAL_STORAGE, false)) {
364-
builder.field(HEADERS.getPreferredName(), headers);
365-
}
366382
if (description != null) {
367383
builder.field(TransformField.DESCRIPTION.getPreferredName(), description);
368384
}
369385
builder.field(TransformField.SETTINGS.getPreferredName(), settings);
370-
if (transformVersion != null) {
371-
builder.field(TransformField.VERSION.getPreferredName(), transformVersion);
372-
}
373-
if (createTime != null) {
374-
builder.timeField(
375-
TransformField.CREATE_TIME.getPreferredName(),
376-
TransformField.CREATE_TIME.getPreferredName() + "_string",
377-
createTime.toEpochMilli()
378-
);
379-
}
380386
builder.endObject();
381387
return builder;
382388
}

x-pack/plugin/src/test/resources/rest-api-spec/api/data_frame_transform_deprecated.get_transform.json

+6
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@
5050
"type":"boolean",
5151
"required":false,
5252
"description":"Whether to ignore if a wildcard expression matches no transforms. (This includes `_all` string or when no transforms have been specified)"
53+
},
54+
"exclude_generated": {
55+
"required": false,
56+
"type": "boolean",
57+
"default": false,
58+
"description": "Omits generated fields. Allows transform configurations to be easily copied between clusters and within the same cluster"
5359
}
5460
}
5561
}

x-pack/plugin/src/test/resources/rest-api-spec/api/transform.get_transform.json

+6
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@
4242
"type":"boolean",
4343
"required":false,
4444
"description":"Whether to ignore if a wildcard expression matches no transforms. (This includes `_all` string or when no transforms have been specified)"
45+
},
46+
"exclude_generated": {
47+
"required": false,
48+
"type": "boolean",
49+
"default": false,
50+
"description": "Omits fields that are illegal to set on transform PUT"
4551
}
4652
}
4753
}

x-pack/plugin/src/test/resources/rest-api-spec/test/transform/transforms_crud.yml

+31
Original file line numberDiff line numberDiff line change
@@ -670,3 +670,34 @@ setup:
670670
},
671671
"description": "yaml test transform on airline-data"
672672
}
673+
674+
---
675+
"Test transform for export":
676+
- do:
677+
transform.put_transform:
678+
transform_id: "airline-transform"
679+
body: >
680+
{
681+
"source": { "index": "airline-data" },
682+
"dest": { "index": "airline-data-by-airline" },
683+
"pivot": {
684+
"group_by": { "airline": {"terms": {"field": "airline"}}},
685+
"aggs": {"avg_response": {"avg": {"field": "responsetime"}}}
686+
},
687+
"description": "yaml test transform on airline-data"
688+
}
689+
- match: { acknowledged: true }
690+
691+
- do:
692+
transform.get_transform:
693+
transform_id: "airline-transform"
694+
exclude_generated: true
695+
696+
- match: {transforms.0.source.index: ["airline-data"]}
697+
- match: {transforms.0.dest.index: "airline-data-by-airline"}
698+
- match: {transforms.0.pivot.group_by.airline.terms.field: "airline"}
699+
- match: {transforms.0.pivot.aggregations.avg_response.avg.field: "responsetime"}
700+
- match: {transforms.0.description: "yaml test transform on airline-data"}
701+
- match: {transforms.0.id: "airline-transform"}
702+
- is_false: transforms.0.create_time
703+
- is_false: transforms.0.version

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

+33
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.apache.http.entity.ContentType;
1010
import org.apache.http.entity.StringEntity;
1111
import org.elasticsearch.client.Request;
12+
import org.elasticsearch.client.Response;
1213
import org.elasticsearch.client.ResponseException;
1314
import org.elasticsearch.common.Strings;
1415
import org.elasticsearch.common.xcontent.XContentBuilder;
@@ -1686,6 +1687,38 @@ public void testPivotWithFilter() throws Exception {
16861687
assertEquals(3, actual.longValue());
16871688
}
16881689

1690+
@SuppressWarnings("unchecked")
1691+
public void testExportAndImport() throws Exception {
1692+
String transformId = "export-transform";
1693+
String transformIndex = "export_reviews";
1694+
setupDataAccessRole(DATA_ACCESS_ROLE, REVIEWS_INDEX_NAME, transformIndex);
1695+
1696+
createPivotReviewsTransform(transformId, transformIndex, null, null, BASIC_AUTH_VALUE_TRANSFORM_ADMIN_WITH_SOME_DATA_ACCESS);
1697+
1698+
Response response = adminClient().performRequest(new Request("GET",
1699+
getTransformEndpoint() + transformId + "?exclude_generated=true"));
1700+
Map<String, Object> storedConfig = ((List<Map<String, Object>>) XContentMapValues.extractValue(
1701+
"transforms",
1702+
entityAsMap(response)))
1703+
.get(0);
1704+
storedConfig.remove("id");
1705+
try (XContentBuilder builder = jsonBuilder()) {
1706+
builder.map(storedConfig);
1707+
Request putTransform = new Request("PUT", getTransformEndpoint() + transformId + "-import");
1708+
putTransform.setJsonEntity(Strings.toString(builder));
1709+
adminClient().performRequest(putTransform);
1710+
}
1711+
1712+
response = adminClient().performRequest(new Request("GET",
1713+
getTransformEndpoint() + transformId + "-import" + "?exclude_generated=true"));
1714+
Map<String, Object> importConfig = ((List<Map<String, Object>>) XContentMapValues.extractValue(
1715+
"transforms",
1716+
entityAsMap(response)))
1717+
.get(0);
1718+
importConfig.remove("id");
1719+
assertThat(storedConfig, equalTo(importConfig));
1720+
}
1721+
16891722
private void createDateNanoIndex(String indexName, int numDocs) throws IOException {
16901723
// create mapping
16911724
try (XContentBuilder builder = jsonBuilder()) {

x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/rest/action/RestGetTransformAction.java

+8
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@
1414
import org.elasticsearch.xpack.core.transform.TransformField;
1515
import org.elasticsearch.xpack.core.transform.action.GetTransformAction;
1616

17+
import java.util.Collections;
1718
import java.util.List;
19+
import java.util.Set;
1820

1921
import static org.elasticsearch.rest.RestRequest.Method.GET;
2022
import static org.elasticsearch.xpack.core.transform.TransformField.ALLOW_NO_MATCH;
23+
import static org.elasticsearch.xpack.core.transform.TransformField.EXCLUDE_GENERATED;
2124

2225
public class RestGetTransformAction extends BaseRestHandler {
2326

@@ -47,4 +50,9 @@ protected RestChannelConsumer prepareRequest(RestRequest restRequest, NodeClient
4750
public String getName() {
4851
return "transform_get_transform_action";
4952
}
53+
54+
@Override
55+
protected Set<String> responseParams() {
56+
return Collections.singleton(EXCLUDE_GENERATED);
57+
}
5058
}

x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/rest/action/compat/RestGetTransformActionDeprecated.java

+7
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818

1919
import java.util.Collections;
2020
import java.util.List;
21+
import java.util.Set;
2122

2223
import static org.elasticsearch.rest.RestRequest.Method.GET;
2324
import static org.elasticsearch.xpack.core.transform.TransformField.ALLOW_NO_MATCH;
25+
import static org.elasticsearch.xpack.core.transform.TransformField.EXCLUDE_GENERATED;
2426

2527
public class RestGetTransformActionDeprecated extends BaseRestHandler {
2628
@Override
@@ -56,4 +58,9 @@ protected RestChannelConsumer prepareRequest(RestRequest restRequest, NodeClient
5658
public String getName() {
5759
return "data_frame_get_transforms_action";
5860
}
61+
62+
@Override
63+
protected Set<String> responseParams() {
64+
return Collections.singleton(EXCLUDE_GENERATED);
65+
}
5966
}

0 commit comments

Comments
 (0)