This is an automated email from the ASF dual-hosted git repository.

jmclean pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/main by this push:
     new b37a236637 [#8083] fix: null-safe handling of 'managed' property in 
CreateFileset.java (#8144)
b37a236637 is described below

commit b37a236637058ec50ca05582f347363f132c2357
Author: JeonDaehong <[email protected]>
AuthorDate: Wed Aug 20 12:26:53 2025 +0900

    [#8083] fix: null-safe handling of 'managed' property in CreateFileset.java 
(#8144)
    
    ### What changes were proposed in this pull request?
    In `CreateFileset.java`, the `handle()` method directly called
    `properties.get("managed").equals("true")`, which could throw a
    `NullPointerException` if the `managed` property was missing.
    This PR updates the code to use a null-safe check and defaults to
    external mode (`Fileset.Type.EXTERNAL`) when the property is not set.
    
    ### Why are the changes needed?
    Without this fix, missing `managed` properties could cause NPEs,
    breaking fileset creation.
    Adding a null-safe check ensures that missing properties are handled
    gracefully and the command defaults to external mode.
    
    Fixes #8083
    
    ### Does this PR introduce any user-facing change?
    No. Valid requests behave the same. Invalid or missing `managed`
    properties are now safely handled without throwing exceptions.
    
    ### How was this patch tested?
    - Manually verified locally that creating filesets without the `managed`
    property defaults to external mode.
    - Added unit test (or can be added) to ensure that missing `managed`
    properties do not cause exceptions.
---
 .../gravitino/cli/commands/CreateFileset.java      |   4 +-
 .../apache/gravitino/cli/TestFilesetCommands.java  | 151 +++++++++++++++++++++
 2 files changed, 154 insertions(+), 1 deletion(-)

diff --git 
a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/CreateFileset.java
 
b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/CreateFileset.java
index 10f77a76fa..260f45c0d7 100644
--- 
a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/CreateFileset.java
+++ 
b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/CreateFileset.java
@@ -20,6 +20,7 @@
 package org.apache.gravitino.cli.commands;
 
 import java.util.Map;
+import java.util.Optional;
 import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.cli.CommandContext;
 import org.apache.gravitino.cli.ErrorMessages;
@@ -71,7 +72,8 @@ public class CreateFileset extends Command {
   @Override
   public void handle() {
     NameIdentifier name = NameIdentifier.of(schema, fileset);
-    boolean managed = "true".equals(properties.get("managed"));
+    boolean managed =
+        
Optional.ofNullable(properties.get("managed")).map("true"::equals).orElse(false);
     Map<String, String> storageLocations = MapUtils.getPrefixMap(properties, 
"location-", true);
     Map<String, String> propertiesWithoutLocation =
         MapUtils.getMapWithoutPrefix(properties, "location-");
diff --git 
a/clients/cli/src/test/java/org/apache/gravitino/cli/TestFilesetCommands.java 
b/clients/cli/src/test/java/org/apache/gravitino/cli/TestFilesetCommands.java
index 8f875ebf0c..5c1f33124a 100644
--- 
a/clients/cli/src/test/java/org/apache/gravitino/cli/TestFilesetCommands.java
+++ 
b/clients/cli/src/test/java/org/apache/gravitino/cli/TestFilesetCommands.java
@@ -36,6 +36,7 @@ import java.io.ByteArrayOutputStream;
 import java.io.PrintStream;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
+import java.util.Map;
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.Options;
 import org.apache.gravitino.cli.commands.CreateFileset;
@@ -563,4 +564,154 @@ class TestFilesetCommands {
             + ErrorMessages.MISSING_ENTITIES
             + Joiner.on(", ").join(Arrays.asList(CommandEntities.FILESET)));
   }
+
+  @Test
+  void testCreateFilesetCommandWithManagedProperty() {
+    CreateFileset mockCreate = mock(CreateFileset.class);
+    
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
+    when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(true);
+    when(mockCommandLine.getOptionValue(GravitinoOptions.NAME))
+        .thenReturn("catalog.schema.fileset");
+    when(mockCommandLine.hasOption(GravitinoOptions.COMMENT)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.COMMENT)).thenReturn("comment");
+    
when(mockCommandLine.hasOption(GravitinoOptions.PROPERTIES)).thenReturn(true);
+    when(mockCommandLine.getOptionValues(GravitinoOptions.PROPERTIES))
+        .thenReturn(new String[] {"managed=true", "key2=value2"});
+
+    GravitinoCommandLine commandLine =
+        spy(
+            new GravitinoCommandLine(
+                mockCommandLine, mockOptions, CommandEntities.FILESET, 
CommandActions.CREATE));
+    doReturn(mockCreate)
+        .when(commandLine)
+        .newCreateFileset(
+            any(CommandContext.class),
+            eq("metalake_demo"),
+            eq("catalog"),
+            eq("schema"),
+            eq("fileset"),
+            eq("comment"),
+            any());
+    doReturn(mockCreate).when(mockCreate).validate();
+    commandLine.handleCommandLine();
+    verify(mockCreate).handle();
+  }
+
+  @Test
+  void testCreateFilesetWithMissingManagedPropertyNPE() {
+    Main.useExit = false;
+    CommandContext mockContext = mock(CommandContext.class);
+    when(mockContext.url()).thenReturn(GravitinoCommandLine.DEFAULT_URL);
+
+    Map<String, String> propertiesWithoutManaged = new java.util.HashMap<>();
+    propertiesWithoutManaged.put("key1", "value1");
+    propertiesWithoutManaged.put("key2", "value2");
+
+    CreateFileset spyCreateFileset =
+        spy(
+            new CreateFileset(
+                mockContext,
+                "metalake_demo",
+                "catalog",
+                "schema",
+                "fileset",
+                "comment",
+                propertiesWithoutManaged));
+
+    assertThrows(RuntimeException.class, spyCreateFileset::validate);
+    verify(spyCreateFileset, never()).handle();
+    String errOutput = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals("Missing property 'managed'", errOutput);
+  }
+
+  @Test
+  void testCreateFilesetWithNullPropertiesNPE() {
+    Main.useExit = false;
+    CommandContext mockContext = mock(CommandContext.class);
+    when(mockContext.url()).thenReturn(GravitinoCommandLine.DEFAULT_URL);
+
+    CreateFileset spyCreateFileset =
+        spy(
+            new CreateFileset(
+                mockContext, "metalake_demo", "catalog", "schema", "fileset", 
"comment", null));
+
+    assertThrows(RuntimeException.class, spyCreateFileset::validate);
+    verify(spyCreateFileset, never()).handle();
+  }
+
+  @Test
+  void testCreateFilesetWithEmptyPropertiesNPE() {
+    Main.useExit = false;
+    CommandContext mockContext = mock(CommandContext.class);
+    when(mockContext.url()).thenReturn(GravitinoCommandLine.DEFAULT_URL);
+
+    Map<String, String> emptyProperties = new java.util.HashMap<>();
+
+    CreateFileset spyCreateFileset =
+        spy(
+            new CreateFileset(
+                mockContext,
+                "metalake_demo",
+                "catalog",
+                "schema",
+                "fileset",
+                "comment",
+                emptyProperties));
+
+    assertThrows(RuntimeException.class, spyCreateFileset::validate);
+    verify(spyCreateFileset, never()).handle();
+    String errOutput = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals("Missing property 'managed'", errOutput);
+  }
+
+  @Test
+  void testCreateFilesetWithManagedTrueProperty() {
+    Main.useExit = false;
+    CommandContext mockContext = mock(CommandContext.class);
+    when(mockContext.url()).thenReturn(GravitinoCommandLine.DEFAULT_URL);
+
+    Map<String, String> propertiesWithManagedTrue = new java.util.HashMap<>();
+    propertiesWithManagedTrue.put("managed", "true");
+    propertiesWithManagedTrue.put("key1", "value1");
+
+    CreateFileset spyCreateFileset =
+        spy(
+            new CreateFileset(
+                mockContext,
+                "metalake_demo",
+                "catalog",
+                "schema",
+                "fileset",
+                "comment",
+                propertiesWithManagedTrue));
+
+    // Should not throw exception when managed property is present
+    Assertions.assertDoesNotThrow(spyCreateFileset::validate);
+  }
+
+  @Test
+  void testCreateFilesetWithManagedFalseProperty() {
+    Main.useExit = false;
+    CommandContext mockContext = mock(CommandContext.class);
+    when(mockContext.url()).thenReturn(GravitinoCommandLine.DEFAULT_URL);
+
+    Map<String, String> propertiesWithManagedFalse = new java.util.HashMap<>();
+    propertiesWithManagedFalse.put("managed", "false");
+    propertiesWithManagedFalse.put("key1", "value1");
+
+    CreateFileset spyCreateFileset =
+        spy(
+            new CreateFileset(
+                mockContext,
+                "metalake_demo",
+                "catalog",
+                "schema",
+                "fileset",
+                "comment",
+                propertiesWithManagedFalse));
+
+    // Should not throw exception when managed property is present
+    Assertions.assertDoesNotThrow(spyCreateFileset::validate);
+  }
 }

Reply via email to