justinmclean commented on code in PR #5641:
URL: https://github.com/apache/gravitino/pull/5641#discussion_r1851281492


##########
build.gradle.kts:
##########
@@ -294,8 +294,8 @@ subprojects {
           "-Xlint:fallthrough",
           "-Xlint:finally",
           "-Xlint:overrides",
-          "-Xlint:static",
-          "-Werror"
+          "-Xlint:static"
+//          "-Werror"

Review Comment:
   This needs to be left in as we want warnings to show as errors.



##########
clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoOptions.java:
##########
@@ -113,6 +113,7 @@ public Option createSimpleOption(String shortName, String 
longName, String descr
    * @return The Option object.
    */
   public Option createArgOption(String shortName, String longName, String 
description) {
-    return new Option(shortName, longName, true, description);
+    // Support multiple arguments

Review Comment:
   No real need for this comment and this alters more than tags, please just 
modify tags.



##########
clients/cli/src/main/java/org/apache/gravitino/cli/commands/DeleteTag.java:
##########
@@ -38,42 +41,45 @@ public class DeleteTag extends Command {
    * @param ignoreVersions If true don't check the client/server versions 
match.
    * @param force Force operation.
    * @param metalake The name of the metalake.
-   * @param tag The name of the tag.
+   * @param tags The name of the tag.

Review Comment:
   comment need updating



##########
clients/cli/src/main/java/org/apache/gravitino/cli/commands/UntagEntity.java:
##########
@@ -103,12 +103,13 @@ public void handle() {
       return;
     }
 
-    String all = String.join(",", tags);
+    String all = String.join(",", removeTags);
 
     if (all.equals("")) {
       all = "nothing";
     }
 
-    System.out.println(entity + " removed tag " + tag + ", now tagged with " + 
all);
+    System.out.println(
+        entity + " removed tag " + String.join(",", tags) + ", now tagged with 
" + all);

Review Comment:
   Perhaps change to "removed tag(s)"? Or put in tag or tags as needed?



##########
docs/cli.md:
##########
@@ -456,10 +457,10 @@ gcli group delete --group new_group
 gcli tag details --tag tagA
 ```
 
-#### Create a tag
+#### Create tags
 
 ```bash
- gcli tag create --tag tagA
+ gcli tag create --tag tagA tagB

Review Comment:
   This is beyond the scope of this PR, if you feel it is an issue, please 
raise another issue for it. However, the CLI is using a typical pattern of 
"entity verb" and then specifying other info with flags. See, for example, 
https://clig.dev/#subcommands, and in general, flags are preferred to arguments 
see https://clig.dev/#arguments-and-flags.



##########
docs/cli.md:
##########
@@ -468,22 +469,22 @@ gcli tag details --tag tagA
 gcli tag list
 ```
 
-#### Delete a tag
+#### Delete tags
 
 ```bash
-gcli tag delete --tag tagA
+gcli tag delete --tag tagA tagB
 ```
 
-#### Add a tag to an entity
+#### Add tags to an entity
 
 ```bash
-gcli tag set --name catalog_postgres.hr --tag tagA
+gcli tag set --name catalog_postgres.hr --tag tagA tagB

Review Comment:
   Note that by default the CLI by default (when multiple arguments are turned 
on)  accept "--tag tagA --tag tagB --tag tagC" or "--tag tagA tagB tagC", the 
delimiter can also be changed from the default of space.



##########
docs/cli.md:
##########
@@ -456,10 +456,10 @@ gcli group delete --group new_group
 gcli tag details --tag tagA
 ```
 
-#### Create a tag
+#### Create tags
 
 ```bash
- gcli tag create --tag tagA
+ gcli tag create --tag tagA tagB

Review Comment:
   As explained before, this PR is to support multiple tags, not about the 
command format. So, this is out of the scope of this PR, but feel free to raise 
an issue about it.



##########
clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoCommandLine.java:
##########
@@ -378,40 +381,44 @@ protected void handleTagCommand() {
       }
     } else if (CommandActions.CREATE.equals(command)) {
       String comment = line.getOptionValue(GravitinoOptions.COMMENT);
-      newCreateTag(url, ignore, metalake, tag, comment).handle();
+      newCreateTags(url, ignore, metalake, tags, comment).handle();
     } else if (CommandActions.DELETE.equals(command)) {
       boolean force = line.hasOption(GravitinoOptions.FORCE);
-      newDeleteTag(url, ignore, force, metalake, tag).handle();
+      newDeleteTag(url, ignore, force, metalake, tags).handle();
     } else if (CommandActions.SET.equals(command)) {
       String property = line.getOptionValue(GravitinoOptions.PROPERTY);
       String value = line.getOptionValue(GravitinoOptions.VALUE);
-
       if (property != null && value != null) {
-        newSetTagProperty(url, ignore, metalake, tag, property, 
value).handle();
+        newSetTagProperty(url, ignore, metalake, getOneTag(tags), property, 
value).handle();
       } else if (name != null && property == null && value == null) {
-        newTagEntity(url, ignore, metalake, name, tag).handle();
+        newTagEntity(url, ignore, metalake, name, tags).handle();
       }
     } else if (CommandActions.REMOVE.equals(command)) {
       String property = line.getOptionValue(GravitinoOptions.PROPERTY);
       if (property != null) {
-        newRemoveTagProperty(url, ignore, metalake, tag, property).handle();
+        newRemoveTagProperty(url, ignore, metalake, getOneTag(tags), 
property).handle();
       } else {
-        newUntagEntity(url, ignore, metalake, name, tag).handle();
+        newUntagEntity(url, ignore, metalake, name, tags).handle();
       }
     } else if (CommandActions.PROPERTIES.equals(command)) {
-      newListTagProperties(url, ignore, metalake, tag).handle();
+      newListTagProperties(url, ignore, metalake, getOneTag(tags)).handle();
     } else if (CommandActions.UPDATE.equals(command)) {
       if (line.hasOption(GravitinoOptions.COMMENT)) {
         String comment = line.getOptionValue(GravitinoOptions.COMMENT);
-        newUpdateTagComment(url, ignore, metalake, tag, comment).handle();
+        newUpdateTagComment(url, ignore, metalake, getOneTag(tags), 
comment).handle();
       }
       if (line.hasOption(GravitinoOptions.RENAME)) {
         String newName = line.getOptionValue(GravitinoOptions.RENAME);
-        newUpdateTagName(url, ignore, metalake, tag, newName).handle();
+        newUpdateTagName(url, ignore, metalake, getOneTag(tags), 
newName).handle();
       }
     }
   }
 
+  private String getOneTag(String[] tags) {
+    Preconditions.checkArgument(tags.length <= 1, 
ErrorMessages.MULTIPLE_TAG_COMMAND_ERROR);
+    return tags[0];

Review Comment:
   No tags can not be empty, the CLI library ensures that if you have a --tag 
flag, it must have a value.



##########
clients/cli/src/main/java/org/apache/gravitino/cli/commands/CreateTag.java:
##########
@@ -35,34 +37,37 @@ public class CreateTag extends Command {
    * @param url The URL of the Gravitino server.
    * @param ignoreVersions If true don't check the client/server versions 
match.
    * @param metalake The name of the metalake.
-   * @param tag The name of the tag.
+   * @param tags The name of the tag.
    * @param comment The comment of the tag.
    */
   public CreateTag(
-      String url, boolean ignoreVersions, String metalake, String tag, String 
comment) {
+      String url, boolean ignoreVersions, String metalake, String[] tags, 
String comment) {
     super(url, ignoreVersions);
     this.metalake = metalake;
-    this.tag = tag;
+    this.tags = tags;
     this.comment = comment;
   }
 
   /** Create a new tag. */
   @Override
   public void handle() {
+    List<String> created = new ArrayList<>();
     try {
       GravitinoClient client = buildClient(metalake);
-      client.createTag(tag, comment, null);
+      for (String tag : tags) {
+        client.createTag(tag, comment, null);
+        created.add(tag);
+      }
     } catch (NoSuchMetalakeException err) {
       System.err.println(ErrorMessages.UNKNOWN_METALAKE);
-      return;
     } catch (TagAlreadyExistsException err) {
       System.err.println(ErrorMessages.TAG_EXISTS);
-      return;
     } catch (Exception exp) {
       System.err.println(exp.getMessage());
-      return;
+    } finally {
+      if (!created.isEmpty()) {
+        System.out.println(String.join(",", created) + " created");
+      }

Review Comment:
   Please leave the code structure as is, there is no need for a finally block.



##########
clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoCommandLine.java:
##########
@@ -378,40 +381,50 @@ protected void handleTagCommand() {
       }
     } else if (CommandActions.CREATE.equals(command)) {
       String comment = line.getOptionValue(GravitinoOptions.COMMENT);
-      newCreateTag(url, ignore, metalake, tag, comment).handle();
+      newCreateTag(url, ignore, metalake, getTags(tags), comment).handle();
     } else if (CommandActions.DELETE.equals(command)) {
       boolean force = line.hasOption(GravitinoOptions.FORCE);
-      newDeleteTag(url, ignore, force, metalake, tag).handle();
+      newDeleteTag(url, ignore, force, metalake, getTags(tags)).handle();
     } else if (CommandActions.SET.equals(command)) {
       String property = line.getOptionValue(GravitinoOptions.PROPERTY);
       String value = line.getOptionValue(GravitinoOptions.VALUE);
-
       if (property != null && value != null) {
-        newSetTagProperty(url, ignore, metalake, tag, property, 
value).handle();
+        newSetTagProperty(url, ignore, metalake, getOneTag(tags), property, 
value).handle();
       } else if (name != null && property == null && value == null) {
-        newTagEntity(url, ignore, metalake, name, tag).handle();
+        newTagEntity(url, ignore, metalake, name, getTags(tags)).handle();
       }
     } else if (CommandActions.REMOVE.equals(command)) {
       String property = line.getOptionValue(GravitinoOptions.PROPERTY);
       if (property != null) {
-        newRemoveTagProperty(url, ignore, metalake, tag, property).handle();
+        newRemoveTagProperty(url, ignore, metalake, getOneTag(tags), 
property).handle();
       } else {
-        newUntagEntity(url, ignore, metalake, name, tag).handle();
+        newUntagEntity(url, ignore, metalake, name, getTags(tags)).handle();
       }
     } else if (CommandActions.PROPERTIES.equals(command)) {
-      newListTagProperties(url, ignore, metalake, tag).handle();
+      newListTagProperties(url, ignore, metalake, getOneTag(tags)).handle();
     } else if (CommandActions.UPDATE.equals(command)) {
       if (line.hasOption(GravitinoOptions.COMMENT)) {
         String comment = line.getOptionValue(GravitinoOptions.COMMENT);
-        newUpdateTagComment(url, ignore, metalake, tag, comment).handle();
+        newUpdateTagComment(url, ignore, metalake, getOneTag(tags), 
comment).handle();
       }
       if (line.hasOption(GravitinoOptions.RENAME)) {
         String newName = line.getOptionValue(GravitinoOptions.RENAME);
-        newUpdateTagName(url, ignore, metalake, tag, newName).handle();
+        newUpdateTagName(url, ignore, metalake, getOneTag(tags), 
newName).handle();
       }
     }
   }
 
+  private String[] getTags(String[] tags) {
+    Preconditions.checkArgument(tags != null, ErrorMessages.TAG_EMPTY);
+    return tags;
+  }
+
+  private String getOneTag(String[] tags) {
+    Preconditions.checkArgument(tags != null, ErrorMessages.TAG_EMPTY);
+    Preconditions.checkArgument(tags.length <= 1, 
ErrorMessages.MULTIPLE_TAG_COMMAND_ERROR);
+    return tags != null ? tags[0] : null;

Review Comment:
   You already have the precondition check above is this null check needed?



##########
clients/cli/src/main/java/org/apache/gravitino/cli/commands/DeleteTag.java:
##########
@@ -38,42 +41,45 @@ public class DeleteTag extends Command {
    * @param ignoreVersions If true don't check the client/server versions 
match.
    * @param force Force operation.
    * @param metalake The name of the metalake.
-   * @param tag The name of the tag.
+   * @param tags The name of the tag.
    */
-  public DeleteTag(String url, boolean ignoreVersions, boolean force, String 
metalake, String tag) {
+  public DeleteTag(
+      String url, boolean ignoreVersions, boolean force, String metalake, 
String[] tags) {
     super(url, ignoreVersions);
     this.force = force;
     this.metalake = metalake;
-    this.tag = tag;
+    this.tags = tags;
   }
 
   /** Delete a tag. */
   @Override
   public void handle() {
-    boolean deleted = false;
-
     if (!AreYouSure.really(force)) {
       return;
     }
-
+    List<String> deleted = new ArrayList<>();
     try {
       GravitinoClient client = buildClient(metalake);
-      deleted = client.deleteTag(tag);
+      for (String tag : tags) {
+        if (client.deleteTag(tag)) {
+          deleted.add(tag);
+        }
+      }
     } catch (NoSuchMetalakeException err) {
       System.err.println(ErrorMessages.UNKNOWN_METALAKE);
-      return;
     } catch (NoSuchTagException err) {
       System.err.println(ErrorMessages.UNKNOWN_TAG);
-      return;
     } catch (Exception exp) {
       System.err.println(exp.getMessage());
-      return;
-    }
-
-    if (deleted) {
-      System.out.println(tag + " deleted.");
-    } else {
-      System.out.println(tag + " not deleted.");
+    } finally {
+      if (!deleted.isEmpty()) {
+        System.out.println(String.join(",", deleted) + " deleted.");
+        if (deleted.size() < tags.length) {
+          List<String> remaining = Arrays.asList(tags);
+          remaining.removeAll(deleted);
+          System.out.println(String.join(",", remaining) + " not deleted.");
+        }
+      }

Review Comment:
   As above, please keep structure as is.



##########
clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoCommandLine.java:
##########
@@ -366,10 +368,11 @@ protected void handleTagCommand() {
     String url = getUrl();
     FullName name = new FullName(line);
     String metalake = name.getMetalakeName();
-    String tag = line.getOptionValue(GravitinoOptions.TAG);
 
+    String[] tags = line.getOptionValues(GravitinoOptions.TAG);

Review Comment:
   Have you tested this? I think you may get a CLI error if TAG has no value, 
so this extra code below may not be needed.



##########
clients/cli/src/test/java/org/apache/gravitino/cli/TestMain.java:
##########
@@ -131,6 +133,74 @@ public void parseError() throws 
UnsupportedEncodingException {
     assertTrue(outContent.toString().contains("usage:")); // Expect help output
   }
 
+  @Test
+  public void tagWithMultiArgs() throws ParseException {
+    Options options = new GravitinoOptions().options();
+    CommandLineParser parser = new DefaultParser();
+
+    // gcli tag create --tag tagA tagB
+    String[] args = {"tag", "create", "--tag", "tagA", "tagB"};
+    CommandLine line = parser.parse(options, args);
+    String[] tags = line.getOptionValues("tag");
+    assertArrayEquals(tags, new Object[] {"tagA", "tagB"});
+
+    // gcli tag delete --tag tagA tagB
+    String[] deleteArgs = {"tag", "delete", "--tag", "tagA", "tagB"};
+    CommandLine deleteLine = parser.parse(options, deleteArgs);
+    String[] deleteTags = deleteLine.getOptionValues("tag");
+    assertArrayEquals(deleteTags, new Object[] {"tagA", "tagB"});
+
+    // gcli tag set --name catalog_postgres.hr --tag tagA tagB
+    String[] setArgs = {"tag", "set", "--name", "catalog_postgres.hr", 
"--tag", "tagA", "tagB"};
+    CommandLine setLine = parser.parse(options, setArgs);
+    String[] setTags = setLine.getOptionValues("tag");
+    assertArrayEquals(setTags, new Object[] {"tagA", "tagB"});
+
+    // gcli tag remove --name catalog_postgres.hr --tag tagA tagB
+    String[] removeArgs = {
+      "tag", "remove", "--name", "catalog_postgres.hr", "--tag", "tagA", "tagB"
+    };
+    CommandLine removeLine = parser.parse(options, removeArgs);
+    String[] removeTags = removeLine.getOptionValues("tag");
+    assertArrayEquals(removeTags, new Object[] {"tagA", "tagB"});
+  }

Review Comment:
   This test doesn't really test anything outside of the internals of the CLI 
library so I'm not sure of it's value.



##########
docs/cli.md:
##########
@@ -44,6 +44,7 @@ The general structure for running commands with the Gravitino 
CLI is `gcli entit
     --rename <arg>       new entity name
  -s,--server             Gravitino server version
  -t,--tag <arg>          tag name
+ -t,--tags <arg>         List tag names

Review Comment:
   We should have one flag for tags 



##########
clients/cli/src/main/java/org/apache/gravitino/cli/commands/DeleteTag.java:
##########
@@ -28,52 +31,55 @@
 public class DeleteTag extends Command {
 
   protected final String metalake;
-  protected final String tag;
+  protected final String[] tags;
   protected final boolean force;
 
   /**
-   * Delete a tag.
+   * Delete tags.
    *
    * @param url The URL of the Gravitino server.
    * @param ignoreVersions If true don't check the client/server versions 
match.
    * @param force Force operation.
    * @param metalake The name of the metalake.
-   * @param tag The name of the tag.
+   * @param tags The names of the tags.
    */
-  public DeleteTag(String url, boolean ignoreVersions, boolean force, String 
metalake, String tag) {
+  public DeleteTag(
+      String url, boolean ignoreVersions, boolean force, String metalake, 
String[] tags) {
     super(url, ignoreVersions);
     this.force = force;
     this.metalake = metalake;
-    this.tag = tag;
+    this.tags = tags;
   }
 
-  /** Delete a tag. */
+  /** Delete tags. */
   @Override
   public void handle() {
-    boolean deleted = false;
-
     if (!AreYouSure.really(force)) {
       return;
     }
-
+    List<String> deleted = new ArrayList<>();
     try {
       GravitinoClient client = buildClient(metalake);
-      deleted = client.deleteTag(tag);
+      for (String tag : tags) {
+        if (client.deleteTag(tag)) {
+          deleted.add(tag);
+        }
+      }
     } catch (NoSuchMetalakeException err) {
       System.err.println(ErrorMessages.UNKNOWN_METALAKE);
-      return;
     } catch (NoSuchTagException err) {
       System.err.println(ErrorMessages.UNKNOWN_TAG);
-      return;
     } catch (Exception exp) {
       System.err.println(exp.getMessage());
-      return;
-    }
-
-    if (deleted) {
-      System.out.println(tag + " deleted.");
-    } else {
-      System.out.println(tag + " not deleted.");
+    } finally {
+      if (!deleted.isEmpty()) {
+        System.out.println(String.join(",", deleted) + " deleted.");

Review Comment:
   If you delete just one tag, the suggested change is not correct. Also, this 
should be consistent with what other delete commands output.



##########
clients/cli/src/main/java/org/apache/gravitino/cli/commands/DeleteTag.java:
##########
@@ -28,52 +31,55 @@
 public class DeleteTag extends Command {
 
   protected final String metalake;
-  protected final String tag;
+  protected final String[] tags;
   protected final boolean force;
 
   /**
-   * Delete a tag.
+   * Delete tags.
    *
    * @param url The URL of the Gravitino server.
    * @param ignoreVersions If true don't check the client/server versions 
match.
    * @param force Force operation.
    * @param metalake The name of the metalake.
-   * @param tag The name of the tag.
+   * @param tags The names of the tags.
    */
-  public DeleteTag(String url, boolean ignoreVersions, boolean force, String 
metalake, String tag) {
+  public DeleteTag(
+      String url, boolean ignoreVersions, boolean force, String metalake, 
String[] tags) {
     super(url, ignoreVersions);
     this.force = force;
     this.metalake = metalake;
-    this.tag = tag;
+    this.tags = tags;
   }
 
-  /** Delete a tag. */
+  /** Delete tags. */
   @Override
   public void handle() {
-    boolean deleted = false;
-
     if (!AreYouSure.really(force)) {
       return;
     }
-
+    List<String> deleted = new ArrayList<>();
     try {
       GravitinoClient client = buildClient(metalake);
-      deleted = client.deleteTag(tag);
+      for (String tag : tags) {
+        if (client.deleteTag(tag)) {
+          deleted.add(tag);
+        }
+      }
     } catch (NoSuchMetalakeException err) {
       System.err.println(ErrorMessages.UNKNOWN_METALAKE);
-      return;
     } catch (NoSuchTagException err) {
       System.err.println(ErrorMessages.UNKNOWN_TAG);
-      return;
     } catch (Exception exp) {
       System.err.println(exp.getMessage());
-      return;
-    }
-
-    if (deleted) {
-      System.out.println(tag + " deleted.");
-    } else {
-      System.out.println(tag + " not deleted.");
+    } finally {
+      if (!deleted.isEmpty()) {
+        System.out.println(String.join(",", deleted) + " deleted.");
+        if (deleted.size() < tags.length) {
+          List<String> remaining = Arrays.asList(tags);
+          remaining.removeAll(deleted);
+          System.out.println(String.join(",", remaining) + " not deleted.");

Review Comment:
   Same as above "Tags" doesn't work if only one tag is remaining.



##########
clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoCommandLine.java:
##########
@@ -378,40 +381,50 @@ protected void handleTagCommand() {
       }
     } else if (CommandActions.CREATE.equals(command)) {
       String comment = line.getOptionValue(GravitinoOptions.COMMENT);
-      newCreateTag(url, ignore, metalake, tag, comment).handle();
+      newCreateTag(url, ignore, metalake, getTags(tags), comment).handle();
     } else if (CommandActions.DELETE.equals(command)) {
       boolean force = line.hasOption(GravitinoOptions.FORCE);
-      newDeleteTag(url, ignore, force, metalake, tag).handle();
+      newDeleteTag(url, ignore, force, metalake, getTags(tags)).handle();
     } else if (CommandActions.SET.equals(command)) {
       String property = line.getOptionValue(GravitinoOptions.PROPERTY);
       String value = line.getOptionValue(GravitinoOptions.VALUE);
-
       if (property != null && value != null) {
-        newSetTagProperty(url, ignore, metalake, tag, property, 
value).handle();
+        newSetTagProperty(url, ignore, metalake, getOneTag(tags), property, 
value).handle();
       } else if (name != null && property == null && value == null) {
-        newTagEntity(url, ignore, metalake, name, tag).handle();
+        newTagEntity(url, ignore, metalake, name, getTags(tags)).handle();
       }
     } else if (CommandActions.REMOVE.equals(command)) {
       String property = line.getOptionValue(GravitinoOptions.PROPERTY);
       if (property != null) {
-        newRemoveTagProperty(url, ignore, metalake, tag, property).handle();
+        newRemoveTagProperty(url, ignore, metalake, getOneTag(tags), 
property).handle();
       } else {
-        newUntagEntity(url, ignore, metalake, name, tag).handle();
+        newUntagEntity(url, ignore, metalake, name, getTags(tags)).handle();
       }
     } else if (CommandActions.PROPERTIES.equals(command)) {
-      newListTagProperties(url, ignore, metalake, tag).handle();
+      newListTagProperties(url, ignore, metalake, getOneTag(tags)).handle();
     } else if (CommandActions.UPDATE.equals(command)) {
       if (line.hasOption(GravitinoOptions.COMMENT)) {
         String comment = line.getOptionValue(GravitinoOptions.COMMENT);
-        newUpdateTagComment(url, ignore, metalake, tag, comment).handle();
+        newUpdateTagComment(url, ignore, metalake, getOneTag(tags), 
comment).handle();
       }
       if (line.hasOption(GravitinoOptions.RENAME)) {
         String newName = line.getOptionValue(GravitinoOptions.RENAME);
-        newUpdateTagName(url, ignore, metalake, tag, newName).handle();
+        newUpdateTagName(url, ignore, metalake, getOneTag(tags), 
newName).handle();
       }
     }
   }
 
+  private String[] getTags(String[] tags) {
+    Preconditions.checkArgument(tags != null, ErrorMessages.TAG_EMPTY);

Review Comment:
   I'm not sure it is possible to have tags as null as the CLI make it a 
required option. It would be best to test this.



##########
docs/cli.md:
##########
@@ -468,22 +469,22 @@ gcli tag details --tag tagA
 gcli tag list
 ```
 
-#### Delete a tag
+#### Delete tags
 
 ```bash
-gcli tag delete --tag tagA
+gcli tag delete --tag tagA tagB
 ```
 
-#### Add a tag to an entity
+#### Add tags to an entity
 
 ```bash
-gcli tag set --name catalog_postgres.hr --tag tagA
+gcli tag set --name catalog_postgres.hr --tag tagA tagB

Review Comment:
   Out of scope for this PR.



##########
clients/cli/src/main/java/org/apache/gravitino/cli/commands/CreateTag.java:
##########
@@ -19,39 +19,51 @@
 
 package org.apache.gravitino.cli.commands;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 import org.apache.gravitino.cli.ErrorMessages;
 import org.apache.gravitino.client.GravitinoClient;
 import org.apache.gravitino.exceptions.NoSuchMetalakeException;
 import org.apache.gravitino.exceptions.TagAlreadyExistsException;
 
 public class CreateTag extends Command {
   protected final String metalake;
-  protected final String tag;
+  protected final String[] tags;
   protected final String comment;
 
   /**
-   * Create a new tag.
+   * Create tags.
    *
    * @param url The URL of the Gravitino server.
    * @param ignoreVersions If true don't check the client/server versions 
match.
    * @param metalake The name of the metalake.
-   * @param tag The name of the tag.
+   * @param tags The names of the tags.
    * @param comment The comment of the tag.
    */
   public CreateTag(
-      String url, boolean ignoreVersions, String metalake, String tag, String 
comment) {
+      String url, boolean ignoreVersions, String metalake, String[] tags, 
String comment) {
     super(url, ignoreVersions);
     this.metalake = metalake;
-    this.tag = tag;
+    this.tags = tags;

Review Comment:
   That's really up to the Java API the CLI calls, the CLI is a thin wrapper on 
that.



##########
clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoCommandLine.java:
##########
@@ -366,10 +368,11 @@ protected void handleTagCommand() {
     String url = getUrl();
     FullName name = new FullName(line);
     String metalake = name.getMetalakeName();
-    String tag = line.getOptionValue(GravitinoOptions.TAG);
 
+    String[] tags = line.getOptionValues(GravitinoOptions.TAG);
+    tags = tags != null ? 
Arrays.stream(tags).distinct().toArray(String[]::new) : null;

Review Comment:
   Fixed



##########
clients/cli/src/test/java/org/apache/gravitino/cli/TestMain.java:
##########
@@ -131,6 +133,74 @@ public void parseError() throws 
UnsupportedEncodingException {
     assertTrue(outContent.toString().contains("usage:")); // Expect help output
   }
 
+  @Test
+  public void tagWithMultiArgs() throws ParseException {
+    Options options = new GravitinoOptions().options();
+    CommandLineParser parser = new DefaultParser();
+
+    // gcli tag create --tag tagA tagB
+    String[] args = {"tag", "create", "--tag", "tagA", "tagB"};
+    CommandLine line = parser.parse(options, args);
+    String[] tags = line.getOptionValues("tag");
+    assertArrayEquals(tags, new Object[] {"tagA", "tagB"});
+
+    // gcli tag delete --tag tagA tagB
+    String[] deleteArgs = {"tag", "delete", "--tag", "tagA", "tagB"};
+    CommandLine deleteLine = parser.parse(options, deleteArgs);
+    String[] deleteTags = deleteLine.getOptionValues("tag");
+    assertArrayEquals(deleteTags, new Object[] {"tagA", "tagB"});
+
+    // gcli tag set --name catalog_postgres.hr --tag tagA tagB
+    String[] setArgs = {"tag", "set", "--name", "catalog_postgres.hr", 
"--tag", "tagA", "tagB"};
+    CommandLine setLine = parser.parse(options, setArgs);
+    String[] setTags = setLine.getOptionValues("tag");
+    assertArrayEquals(setTags, new Object[] {"tagA", "tagB"});
+
+    // gcli tag remove --name catalog_postgres.hr --tag tagA tagB
+    String[] removeArgs = {
+      "tag", "remove", "--name", "catalog_postgres.hr", "--tag", "tagA", "tagB"
+    };
+    CommandLine removeLine = parser.parse(options, removeArgs);
+    String[] removeTags = removeLine.getOptionValues("tag");
+    assertArrayEquals(removeTags, new Object[] {"tagA", "tagB"});
+  }

Review Comment:
   It would be better to add similar tests to TestTagCommands.java.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscr...@gravitino.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org

Reply via email to