Skip to content

Commit 610c31d

Browse files
author
Hendrik Muhs
authored
[Transform] fix transform failure for configs with no field parameter (#67334)
avoid illegal argument exception when fields is empty, which is a valid case fixes #67333
1 parent f8abfc8 commit 610c31d

File tree

2 files changed

+132
-6
lines changed
  • x-pack/plugin/transform/src

2 files changed

+132
-6
lines changed

x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/pivot/SchemaUtil.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.elasticsearch.xpack.core.transform.transforms.pivot.PivotConfig;
2424

2525
import java.math.BigDecimal;
26+
import java.util.Collections;
2627
import java.util.HashMap;
2728
import java.util.Map;
2829
import java.util.Objects;
@@ -250,12 +251,11 @@ private static Map<String, String> resolveMappings(
250251
/*
251252
* Very "magic" helper method to extract the source mappings
252253
*/
253-
private static void getSourceFieldMappings(
254-
Client client,
255-
String[] index,
256-
String[] fields,
257-
ActionListener<Map<String, String>> listener
258-
) {
254+
static void getSourceFieldMappings(Client client, String[] index, String[] fields, ActionListener<Map<String, String>> listener) {
255+
if (index == null || index.length == 0 || fields == null || fields.length == 0) {
256+
listener.onResponse(Collections.emptyMap());
257+
return;
258+
}
259259
FieldCapabilitiesRequest fieldCapabilitiesRequest = new FieldCapabilitiesRequest().indices(index)
260260
.fields(fields)
261261
.indicesOptions(IndicesOptions.LENIENT_EXPAND_OPEN);

x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/pivot/SchemaUtilTests.java

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,27 @@
66

77
package org.elasticsearch.xpack.transform.transforms.pivot;
88

9+
import org.elasticsearch.action.ActionListener;
10+
import org.elasticsearch.action.ActionRequest;
11+
import org.elasticsearch.action.ActionResponse;
12+
import org.elasticsearch.action.ActionType;
13+
import org.elasticsearch.action.LatchedActionListener;
14+
import org.elasticsearch.action.fieldcaps.FieldCapabilities;
15+
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesRequest;
16+
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesResponse;
17+
import org.elasticsearch.client.Client;
18+
import org.elasticsearch.common.Strings;
919
import org.elasticsearch.test.ESTestCase;
20+
import org.elasticsearch.test.client.NoOpClient;
1021

1122
import java.math.BigInteger;
23+
import java.util.Collections;
1224
import java.util.HashMap;
1325
import java.util.Map;
26+
import java.util.concurrent.CountDownLatch;
27+
import java.util.concurrent.TimeUnit;
28+
import java.util.concurrent.atomic.AtomicBoolean;
29+
import java.util.function.Consumer;
1430

1531
import static org.hamcrest.CoreMatchers.instanceOf;
1632

@@ -71,4 +87,114 @@ public void testConvertToIntegerTypeIfNeeded() {
7187
assertEquals(new BigInteger("18446744073709551615").doubleValue(), ((BigInteger) value).doubleValue(), 0.0);
7288
}
7389

90+
public void testGetSourceFieldMappings() throws InterruptedException {
91+
try (Client client = new FieldCapsMockClient(getTestName())) {
92+
// fields is null
93+
this.<Map<String, String>>assertAsync(
94+
listener -> SchemaUtil.getSourceFieldMappings(client, new String[] { "index-1", "index-2" }, null, listener),
95+
mappings -> {
96+
assertNotNull(mappings);
97+
assertTrue(mappings.isEmpty());
98+
}
99+
);
100+
101+
// fields is empty
102+
this.<Map<String, String>>assertAsync(
103+
listener -> SchemaUtil.getSourceFieldMappings(client, new String[] { "index-1", "index-2" }, new String[] {}, listener),
104+
mappings -> {
105+
assertNotNull(mappings);
106+
assertTrue(mappings.isEmpty());
107+
}
108+
);
109+
110+
// indices is null
111+
this.<Map<String, String>>assertAsync(
112+
listener -> SchemaUtil.getSourceFieldMappings(client, null, new String[] { "field-1", "field-2" }, listener),
113+
mappings -> {
114+
assertNotNull(mappings);
115+
assertTrue(mappings.isEmpty());
116+
}
117+
);
118+
119+
// indices is empty
120+
this.<Map<String, String>>assertAsync(
121+
listener -> SchemaUtil.getSourceFieldMappings(client, new String[] {}, new String[] { "field-1", "field-2" }, listener),
122+
mappings -> {
123+
assertNotNull(mappings);
124+
assertTrue(mappings.isEmpty());
125+
}
126+
);
127+
128+
// good use
129+
this.<Map<String, String>>assertAsync(
130+
listener -> SchemaUtil.getSourceFieldMappings(
131+
client,
132+
new String[] { "index-1", "index-2" },
133+
new String[] { "field-1", "field-2" },
134+
listener
135+
),
136+
mappings -> {
137+
assertNotNull(mappings);
138+
assertEquals(2, mappings.size());
139+
assertEquals("long", mappings.get("field-1"));
140+
assertEquals("long", mappings.get("field-2"));
141+
}
142+
);
143+
}
144+
}
145+
146+
private static class FieldCapsMockClient extends NoOpClient {
147+
FieldCapsMockClient(String testName) {
148+
super(testName);
149+
}
150+
151+
@SuppressWarnings("unchecked")
152+
@Override
153+
protected <Request extends ActionRequest, Response extends ActionResponse> void doExecute(
154+
ActionType<Response> action,
155+
Request request,
156+
ActionListener<Response> listener
157+
) {
158+
if (request instanceof FieldCapabilitiesRequest) {
159+
FieldCapabilitiesRequest fieldCapsRequest = (FieldCapabilitiesRequest) request;
160+
Map<String, Map<String, FieldCapabilities>> responseMap = new HashMap<>();
161+
for (String field : fieldCapsRequest.fields()) {
162+
responseMap.put(field, Collections.singletonMap(field, createFieldCapabilities(field, "long")));
163+
}
164+
165+
final FieldCapabilitiesResponse response = new FieldCapabilitiesResponse(fieldCapsRequest.indices(), responseMap);
166+
listener.onResponse((Response) response);
167+
return;
168+
}
169+
170+
super.doExecute(action, request, listener);
171+
}
172+
}
173+
174+
private static FieldCapabilities createFieldCapabilities(String name, String type) {
175+
return new FieldCapabilities(
176+
name,
177+
type,
178+
true,
179+
true,
180+
Strings.EMPTY_ARRAY,
181+
Strings.EMPTY_ARRAY,
182+
Strings.EMPTY_ARRAY,
183+
Collections.emptyMap()
184+
);
185+
}
186+
187+
private <T> void assertAsync(Consumer<ActionListener<T>> function, Consumer<T> furtherTests) throws InterruptedException {
188+
CountDownLatch latch = new CountDownLatch(1);
189+
AtomicBoolean listenerCalled = new AtomicBoolean(false);
190+
191+
LatchedActionListener<T> listener = new LatchedActionListener<>(ActionListener.wrap(r -> {
192+
assertTrue("listener called more than once", listenerCalled.compareAndSet(false, true));
193+
furtherTests.accept(r);
194+
}, e -> { fail("got unexpected exception: " + e); }), latch);
195+
196+
function.accept(listener);
197+
assertTrue("timed out after 20s", latch.await(20, TimeUnit.SECONDS));
198+
}
199+
74200
}

0 commit comments

Comments
 (0)