Skip to content

Commit 27d1d66

Browse files
committed
Refactor XmlProcessor configuration handling and enhance factory tests for validation
1 parent 82dd719 commit 27d1d66

File tree

2 files changed

+74
-38
lines changed

2 files changed

+74
-38
lines changed

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

Lines changed: 24 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -419,51 +419,39 @@ public XmlProcessor create(
419419

420420
// Parse XPath expressions map
421421
Map<String, String> xpathExpressions = new HashMap<>();
422-
Object xpathConfig = config.get("xpath");
422+
Map<String, Object> xpathConfig = ConfigurationUtils.readOptionalMap(TYPE, processorTag, config, "xpath");
423423
if (xpathConfig != null) {
424-
if (xpathConfig instanceof Map) {
425-
@SuppressWarnings("unchecked")
426-
Map<String, Object> xpathMap = (Map<String, Object>) xpathConfig;
427-
for (Map.Entry<String, Object> entry : xpathMap.entrySet()) {
428-
if (entry.getValue() instanceof String str) {
429-
xpathExpressions.put(entry.getKey(), str);
430-
} else {
431-
throw new IllegalArgumentException(
432-
"XPath target field ["
433-
+ entry.getKey()
434-
+ "] must be a string, got ["
435-
+ entry.getValue().getClass().getSimpleName()
436-
+ "]"
437-
);
438-
}
424+
for (Map.Entry<String, Object> entry : xpathConfig.entrySet()) {
425+
if (entry.getValue() instanceof String str) {
426+
xpathExpressions.put(entry.getKey(), str);
427+
} else {
428+
throw new IllegalArgumentException(
429+
"XPath target field ["
430+
+ entry.getKey()
431+
+ "] must be a string, got ["
432+
+ entry.getValue().getClass().getSimpleName()
433+
+ "]"
434+
);
439435
}
440-
} else {
441-
throw new IllegalArgumentException("XPath configuration must be a map of expressions to target fields");
442436
}
443437
}
444438

445439
// Parse namespaces map
446440
Map<String, String> namespaces = new HashMap<>();
447-
Object namespaceConfig = config.get("namespaces");
441+
Map<String, Object> namespaceConfig = ConfigurationUtils.readOptionalMap(TYPE, processorTag, config, "namespaces");
448442
if (namespaceConfig != null) {
449-
if (namespaceConfig instanceof Map) {
450-
@SuppressWarnings("unchecked")
451-
Map<String, Object> namespaceMap = (Map<String, Object>) namespaceConfig;
452-
for (Map.Entry<String, Object> entry : namespaceMap.entrySet()) {
453-
if (entry.getValue() instanceof String str) {
454-
namespaces.put(entry.getKey(), str);
455-
} else {
456-
throw new IllegalArgumentException(
457-
"Namespace prefix ["
458-
+ entry.getKey()
459-
+ "] must have a string URI, got ["
460-
+ entry.getValue().getClass().getSimpleName()
461-
+ "]"
462-
);
463-
}
443+
for (Map.Entry<String, Object> entry : namespaceConfig.entrySet()) {
444+
if (entry.getValue() instanceof String str) {
445+
namespaces.put(entry.getKey(), str);
446+
} else {
447+
throw new IllegalArgumentException(
448+
"Namespace prefix ["
449+
+ entry.getKey()
450+
+ "] must have a string URI, got ["
451+
+ entry.getValue().getClass().getSimpleName()
452+
+ "]"
453+
);
464454
}
465-
} else {
466-
throw new IllegalArgumentException("Namespaces configuration must be a map of prefixes to URIs");
467455
}
468456
}
469457

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

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.elasticsearch.ElasticsearchParseException;
1313
import org.elasticsearch.test.ESTestCase;
1414

15+
import java.util.Arrays;
1516
import java.util.HashMap;
1617
import java.util.Map;
1718

@@ -93,6 +94,32 @@ private XmlProcessor createProcessor(Map<String, Object> config) throws Exceptio
9394
return createProcessor(createFactory(), config);
9495
}
9596

97+
/**
98+
* Creates a processor mimicking the production pipeline validation.
99+
* This simulates what ConfigurationUtils.readProcessor() does.
100+
*/
101+
private XmlProcessor createProcessorWithValidation(Map<String, Object> config) throws Exception {
102+
XmlProcessor.Factory factory = createFactory();
103+
String processorTag = randomAlphaOfLength(10);
104+
105+
// Make a copy of the config to avoid modifying the original
106+
Map<String, Object> configCopy = new HashMap<>(config);
107+
108+
// Create the processor (this should consume config parameters)
109+
XmlProcessor processor = factory.create(null, processorTag, null, configCopy, null);
110+
111+
// Simulate the validation check from ConfigurationUtils.readProcessor()
112+
if (configCopy.isEmpty() == false) {
113+
throw new ElasticsearchParseException(
114+
"processor [{}] doesn't support one or more provided configuration parameters {}",
115+
"xml",
116+
Arrays.toString(configCopy.keySet().toArray())
117+
);
118+
}
119+
120+
return processor;
121+
}
122+
96123
/**
97124
* Helper method to create XPath configuration map.
98125
*/
@@ -235,7 +262,7 @@ public void testCreateWithInvalidXPathConfig() throws Exception {
235262
Map<String, Object> config = createBaseConfig();
236263
config.put("xpath", "invalid_string"); // Should be a map
237264

238-
expectCreationFailure(config, IllegalArgumentException.class, "XPath configuration must be a map of expressions to target fields");
265+
expectCreationFailure(config, ElasticsearchParseException.class, "[xpath] property isn't a map, but of type [java.lang.String]");
239266
}
240267

241268
public void testCreateWithInvalidXPathTargetField() throws Exception {
@@ -271,7 +298,7 @@ public void testCreateWithInvalidNamespacesConfig() throws Exception {
271298
Map<String, Object> config = createBaseConfig();
272299
config.put("namespaces", "invalid_string"); // Should be a map
273300

274-
expectCreationFailure(config, IllegalArgumentException.class, "Namespaces configuration must be a map of prefixes to URIs");
301+
expectCreationFailure(config, ElasticsearchParseException.class, "[namespaces] property isn't a map, but of type [java.lang.String]");
275302
}
276303

277304
public void testCreateWithInvalidNamespaceURI() throws Exception {
@@ -384,4 +411,25 @@ public void testCreateWithXPathUsingNamespacesWithoutConfiguration() throws Exce
384411
"Invalid XPath expression [//book:title/text()]: contains namespace prefixes but no namespace configuration provided"
385412
);
386413
}
414+
415+
public void testConfigurationParametersAreProperlyRemoved() throws Exception {
416+
// Test that demonstrates configuration validation works when using production-like validation
417+
// This test verifies that ConfigurationUtils.readOptionalMap() properly removes parameters from config
418+
// If any are left, the processor factory should throw an exception about unknown parameters
419+
420+
Map<String, String> xpathConfig = createXPathConfig("//test", "test_field");
421+
Map<String, Object> config = createConfigWithXPath(DEFAULT_FIELD, xpathConfig);
422+
423+
// Add an intentionally unknown parameter to trigger the validation
424+
config.put("unknown_parameter", "should_fail");
425+
426+
// This should fail because "unknown_parameter" should remain in config after all valid params are removed
427+
ElasticsearchParseException exception = expectThrows(
428+
ElasticsearchParseException.class,
429+
() -> createProcessorWithValidation(config)
430+
);
431+
432+
assertThat(exception.getMessage(), containsString("doesn't support one or more provided configuration parameters"));
433+
assertThat(exception.getMessage(), containsString("unknown_parameter"));
434+
}
387435
}

0 commit comments

Comments
 (0)