This is an automated email from the ASF dual-hosted git repository.
xuba pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/amoro.git
The following commit(s) were added to refs/heads/master by this push:
new 0e34e89dc [AMORO-3664] Trim the leading and trailing whitespace in
ResourceGroup properties (#3664)
0e34e89dc is described below
commit 0e34e89dcfc8b5c5b5da285786004965d5b70bc6
Author: Jzjsnow <[email protected]>
AuthorDate: Fri Jul 11 00:24:50 2025 +0800
[AMORO-3664] Trim the leading and trailing whitespace in ResourceGroup
properties (#3664)
Co-authored-by: jzjsnow <[email protected]>
---
.../controller/OptimizerGroupController.java | 5 +--
.../server/dashboard/utils/PropertiesUtil.java | 32 +++++++++++++++++
.../server/dashboard/utils/TestPropertiesUtil.java | 42 ++++++++++++++++++++++
3 files changed, 77 insertions(+), 2 deletions(-)
diff --git
a/amoro-ams/src/main/java/org/apache/amoro/server/dashboard/controller/OptimizerGroupController.java
b/amoro-ams/src/main/java/org/apache/amoro/server/dashboard/controller/OptimizerGroupController.java
index b190a30c9..147e9d691 100644
---
a/amoro-ams/src/main/java/org/apache/amoro/server/dashboard/controller/OptimizerGroupController.java
+++
b/amoro-ams/src/main/java/org/apache/amoro/server/dashboard/controller/OptimizerGroupController.java
@@ -27,6 +27,7 @@ import
org.apache.amoro.server.dashboard.model.OptimizerResourceInfo;
import org.apache.amoro.server.dashboard.model.TableOptimizingInfo;
import org.apache.amoro.server.dashboard.response.OkResponse;
import org.apache.amoro.server.dashboard.response.PageResult;
+import org.apache.amoro.server.dashboard.utils.PropertiesUtil;
import org.apache.amoro.server.optimizing.OptimizingStatus;
import org.apache.amoro.server.resource.ContainerMetadata;
import org.apache.amoro.server.resource.InternalContainers;
@@ -259,7 +260,7 @@ public class OptimizerGroupController {
Map<String, Object> map = ctx.bodyAsClass(Map.class);
String name = (String) map.get("name");
String container = (String) map.get("container");
- Map<String, String> properties = (Map) map.get("properties");
+ Map<String, String> properties = PropertiesUtil.sanitizeProperties((Map)
map.get("properties"));
validateGroupName(name);
ResourceGroup.Builder builder = new ResourceGroup.Builder(name, container);
builder.addProperties(properties);
@@ -275,7 +276,7 @@ public class OptimizerGroupController {
Map<String, Object> map = ctx.bodyAsClass(Map.class);
String name = (String) map.get("name");
String container = (String) map.get("container");
- Map<String, String> properties = (Map) map.get("properties");
+ Map<String, String> properties = PropertiesUtil.sanitizeProperties((Map)
map.get("properties"));
ResourceGroup.Builder builder = new ResourceGroup.Builder(name, container);
builder.addProperties(properties);
optimizerManager.updateResourceGroup(builder.build());
diff --git
a/amoro-ams/src/main/java/org/apache/amoro/server/dashboard/utils/PropertiesUtil.java
b/amoro-ams/src/main/java/org/apache/amoro/server/dashboard/utils/PropertiesUtil.java
index 0a5adb8b8..c81256d9c 100644
---
a/amoro-ams/src/main/java/org/apache/amoro/server/dashboard/utils/PropertiesUtil.java
+++
b/amoro-ams/src/main/java/org/apache/amoro/server/dashboard/utils/PropertiesUtil.java
@@ -21,8 +21,11 @@ package org.apache.amoro.server.dashboard.utils;
import org.apache.amoro.properties.CatalogMetaProperties;
import org.apache.amoro.shade.guava32.com.google.common.collect.Maps;
+import java.util.AbstractMap;
+import java.util.Collections;
import java.util.Map;
import java.util.Set;
+import java.util.stream.Collectors;
public class PropertiesUtil {
public static void putNotNullProperties(
@@ -66,4 +69,33 @@ public class PropertiesUtil {
(key, value) ->
result.put(CatalogMetaProperties.TABLE_PROPERTIES_PREFIX + key, value));
return result;
}
+
+ /**
+ * Sanitize a map of string properties. This processing includes trimming
keys and values,
+ * removing entries with empty keys or null values, and resolving key
conflicts by keeping the
+ * latter value.
+ *
+ * @param properties the raw input map to sanitize
+ * @return a new map with cleaned keys and values
+ */
+ public static Map<String, String> sanitizeProperties(Map<String, String>
properties) {
+ if (properties == null || properties.isEmpty()) {
+ return Collections.emptyMap();
+ }
+
+ return properties.entrySet().stream()
+ .map(
+ entry -> {
+ String key = entry.getKey() == null ? "" : entry.getKey().trim();
+ String value = entry.getValue() == null ? null :
entry.getValue().trim();
+ return new AbstractMap.SimpleEntry<>(key, value);
+ })
+ .filter(e -> !e.getKey().isEmpty() && e.getValue() != null)
+ .collect(
+ Collectors.toMap(
+ Map.Entry::getKey,
+ Map.Entry::getValue,
+ (v1, v2) -> v2 // use the later value on duplicate keys
+ ));
+ }
}
diff --git
a/amoro-ams/src/test/java/org/apache/amoro/server/dashboard/utils/TestPropertiesUtil.java
b/amoro-ams/src/test/java/org/apache/amoro/server/dashboard/utils/TestPropertiesUtil.java
index 541802a2f..f1fc62962 100644
---
a/amoro-ams/src/test/java/org/apache/amoro/server/dashboard/utils/TestPropertiesUtil.java
+++
b/amoro-ams/src/test/java/org/apache/amoro/server/dashboard/utils/TestPropertiesUtil.java
@@ -22,6 +22,7 @@ import
org.apache.amoro.shade.guava32.com.google.common.collect.Maps;
import org.junit.Assert;
import org.junit.Test;
+import java.util.HashMap;
import java.util.Map;
public class TestPropertiesUtil {
@@ -59,4 +60,45 @@ public class TestPropertiesUtil {
PropertiesUtil.unionCatalogProperties(TABLE_PROPERTIES,
CATALOG_PROPERTIES);
Assert.assertEquals(unionCatalogProperties, ALL_PROPERTIES);
}
+
+ @Test
+ public void testSanitizeEmptyProperties() {
+ Assert.assertEquals(0, PropertiesUtil.sanitizeProperties(null).size());
+
+ Map<String, String> props = new HashMap<>();
+ Map<String, String> sanitizedProps =
PropertiesUtil.sanitizeProperties(props);
+ Assert.assertEquals(0, sanitizedProps.size());
+ }
+
+ @Test
+ public void testSanitizePropertiesWithDuplicateKeys() {
+ Map<String, String> props = new HashMap<>();
+ props.put("key1", "value1");
+ props.put(" key2 ", " value");
+ props.put("key1 ", "newValue1 "); // should overwrite
+ props.put(" key2", " newValue2"); // should overwrite
+
+ Map<String, String> sanitizedProps =
PropertiesUtil.sanitizeProperties(props);
+ Assert.assertEquals(2, sanitizedProps.size());
+ Assert.assertEquals("newValue1", sanitizedProps.get("key1"));
+ Assert.assertEquals("newValue2", sanitizedProps.get("key2"));
+ }
+
+ @Test
+ public void testSanitizePropertiesWithEmptyKey() {
+ Map<String, String> props = new HashMap<>();
+ props.put(" ", " nullKey ");
+
+ Map<String, String> sanitizedProps =
PropertiesUtil.sanitizeProperties(props);
+ Assert.assertEquals(0, sanitizedProps.size());
+ }
+
+ @Test
+ public void testSanitizePropertiesWithNullValue() {
+ Map<String, String> props = new HashMap<>();
+ props.put(" nullValue ", null);
+
+ Map<String, String> sanitizedProps =
PropertiesUtil.sanitizeProperties(props);
+ Assert.assertEquals(0, sanitizedProps.size());
+ }
}