You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@amoro.apache.org by xu...@apache.org on 2025/07/10 16:24:54 UTC

(amoro) branch master updated: [AMORO-3664] Trim the leading and trailing whitespace in ResourceGroup properties (#3664)

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 <57...@qq.com>
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 <sn...@gmail.com>
---
 .../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());
+  }
 }