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());
+  }
 }

Reply via email to