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 e62c6d37a [#5528]Improvement[cli] Add and delete multiple tags at once
in the Gravitino CLI (#5641)
e62c6d37a is described below
commit e62c6d37a4e52562dc5e512e780ccd4f3fb7bb14
Author: Xiaojian Sun <[email protected]>
AuthorDate: Tue Nov 26 07:06:20 2024 +0800
[#5528]Improvement[cli] Add and delete multiple tags at once in the
Gravitino CLI (#5641)
### What changes were proposed in this pull request?
Add and delete multiple tags at once in the Gravitino CLI
### Why are the changes needed?
Close: [(#5528)](https://github.com/apache/gravitino/issues/5528)
---
.../org/apache/gravitino/cli/ErrorMessages.java | 3 +
.../apache/gravitino/cli/GravitinoCommandLine.java | 31 ++--
.../org/apache/gravitino/cli/GravitinoOptions.java | 7 +-
.../apache/gravitino/cli/TestableCommandLine.java | 18 +-
.../apache/gravitino/cli/commands/CreateTag.java | 56 +++++-
.../apache/gravitino/cli/commands/DeleteTag.java | 63 ++++++-
.../apache/gravitino/cli/commands/TagEntity.java | 23 +--
.../apache/gravitino/cli/commands/UntagEntity.java | 25 +--
.../org/apache/gravitino/cli/TestTagCommands.java | 203 +++++++++++++++++++--
docs/cli.md | 16 +-
10 files changed, 357 insertions(+), 88 deletions(-)
diff --git
a/clients/cli/src/main/java/org/apache/gravitino/cli/ErrorMessages.java
b/clients/cli/src/main/java/org/apache/gravitino/cli/ErrorMessages.java
index 0ad750f2c..6836bd203 100644
--- a/clients/cli/src/main/java/org/apache/gravitino/cli/ErrorMessages.java
+++ b/clients/cli/src/main/java/org/apache/gravitino/cli/ErrorMessages.java
@@ -38,7 +38,10 @@ public class ErrorMessages {
public static final String UNKNOWN_GROUP = "Unknown group.";
public static final String GROUP_EXISTS = "Group already exists.";
public static final String UNKNOWN_TAG = "Unknown tag.";
+ public static final String MULTIPLE_TAG_COMMAND_ERROR =
+ "Error: The current command only supports one --tag option.";
public static final String TAG_EXISTS = "Tag already exists.";
+ public static final String TAG_EMPTY = "Error: Must configure --tag option.";
public static final String UNKNOWN_ROLE = "Unknown role.";
public static final String ROLE_EXISTS = "Role already exists.";
public static final String INVALID_SET_COMMAND =
diff --git
a/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoCommandLine.java
b/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoCommandLine.java
index c25b2f7e7..14e2cd20e 100644
---
a/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoCommandLine.java
+++
b/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoCommandLine.java
@@ -19,6 +19,8 @@
package org.apache.gravitino.cli;
+import com.google.common.base.Preconditions;
+import java.util.Arrays;
import java.util.Map;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.HelpFormatter;
@@ -366,10 +368,11 @@ public class GravitinoCommandLine extends
TestableCommandLine {
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;
if (CommandActions.DETAILS.equals(command)) {
- newTagDetails(url, ignore, metalake, tag).handle();
+ newTagDetails(url, ignore, metalake, getOneTag(tags)).handle();
} else if (CommandActions.LIST.equals(command)) {
if (!name.hasCatalogName()) {
newListTags(url, ignore, metalake).handle();
@@ -378,40 +381,44 @@ public class GravitinoCommandLine extends
TestableCommandLine {
}
} 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];
+ }
+
/** Handles the command execution for Roles based on command type and the
command line options. */
protected void handleRoleCommand() {
String url = getUrl();
diff --git
a/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoOptions.java
b/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoOptions.java
index a6afdd589..8f017ba72 100644
--- a/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoOptions.java
+++ b/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoOptions.java
@@ -78,7 +78,7 @@ public class GravitinoOptions {
"z", PROVIDER, "provider one of hadoop, hive, mysql, postgres,
iceberg, kafka"));
options.addOption(createArgOption("l", USER, "user name"));
options.addOption(createArgOption("g", GROUP, "group name"));
- options.addOption(createArgOption("t", TAG, "tag name"));
+ options.addOption(createArgsOption("t", TAG, "tag name"));
options.addOption(createArgOption("r", ROLE, "role name"));
// Properties option can have multiple values
@@ -115,4 +115,9 @@ public class GravitinoOptions {
public Option createArgOption(String shortName, String longName, String
description) {
return new Option(shortName, longName, true, description);
}
+
+ public Option createArgsOption(String shortName, String longName, String
description) {
+ // Support multiple arguments
+ return
Option.builder().option(shortName).longOpt(longName).hasArgs().desc(description).build();
+ }
}
diff --git
a/clients/cli/src/main/java/org/apache/gravitino/cli/TestableCommandLine.java
b/clients/cli/src/main/java/org/apache/gravitino/cli/TestableCommandLine.java
index 85bfa3203..9a4882b3a 100644
---
a/clients/cli/src/main/java/org/apache/gravitino/cli/TestableCommandLine.java
+++
b/clients/cli/src/main/java/org/apache/gravitino/cli/TestableCommandLine.java
@@ -370,14 +370,14 @@ public class TestableCommandLine {
return new ListAllTags(url, ignore, metalake);
}
- protected CreateTag newCreateTag(
- String url, boolean ignore, String metalake, String tag, String comment)
{
- return new CreateTag(url, ignore, metalake, tag, comment);
+ protected CreateTag newCreateTags(
+ String url, boolean ignore, String metalake, String[] tags, String
comment) {
+ return new CreateTag(url, ignore, metalake, tags, comment);
}
protected DeleteTag newDeleteTag(
- String url, boolean ignore, boolean force, String metalake, String tag) {
- return new DeleteTag(url, ignore, force, metalake, tag);
+ String url, boolean ignore, boolean force, String metalake, String[]
tags) {
+ return new DeleteTag(url, ignore, force, metalake, tags);
}
protected SetTagProperty newSetTagProperty(
@@ -411,13 +411,13 @@ public class TestableCommandLine {
}
protected TagEntity newTagEntity(
- String url, boolean ignore, String metalake, FullName name, String tag) {
- return new TagEntity(url, ignore, metalake, name, tag);
+ String url, boolean ignore, String metalake, FullName name, String[]
tags) {
+ return new TagEntity(url, ignore, metalake, name, tags);
}
protected UntagEntity newUntagEntity(
- String url, boolean ignore, String metalake, FullName name, String tag) {
- return new UntagEntity(url, ignore, metalake, name, tag);
+ String url, boolean ignore, String metalake, FullName name, String[]
tags) {
+ return new UntagEntity(url, ignore, metalake, name, tags);
}
protected ListColumns newListColumns(
diff --git
a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/CreateTag.java
b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/CreateTag.java
index 76f6f85b4..004254c16 100644
--- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/CreateTag.java
+++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/CreateTag.java
@@ -19,6 +19,9 @@
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;
@@ -26,32 +29,41 @@ 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;
this.comment = comment;
}
- /** Create a new tag. */
+ /** Create tags. */
@Override
public void handle() {
+ boolean hasOnlyOneTag = tags.length == 1;
+ if (hasOnlyOneTag) {
+ handleOnlyOneTag();
+ } else {
+ handleMultipleTags();
+ }
+ }
+
+ private void handleOnlyOneTag() {
try {
GravitinoClient client = buildClient(metalake);
- client.createTag(tag, comment, null);
+ client.createTag(tags[0], comment, null);
} catch (NoSuchMetalakeException err) {
System.err.println(ErrorMessages.UNKNOWN_METALAKE);
return;
@@ -63,6 +75,34 @@ public class CreateTag extends Command {
return;
}
- System.out.println(tag + " created");
+ System.out.println(tags[0] + " created");
+ }
+
+ private void handleMultipleTags() {
+ List<String> created = new ArrayList<>();
+ try {
+ GravitinoClient client = buildClient(metalake);
+ 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;
+ }
+ if (!created.isEmpty()) {
+ System.out.println("Tags " + String.join(",", created) + " created");
+ }
+ if (created.size() < tags.length) {
+ List<String> remaining = Arrays.asList(tags);
+ remaining.removeAll(created);
+ System.out.println("Tags " + String.join(",", remaining) + " not
created");
+ }
}
}
diff --git
a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/DeleteTag.java
b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/DeleteTag.java
index 4536897b0..0db4a8976 100644
--- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/DeleteTag.java
+++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/DeleteTag.java
@@ -19,6 +19,9 @@
package org.apache.gravitino.cli.commands;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import org.apache.gravitino.cli.AreYouSure;
import org.apache.gravitino.cli.ErrorMessages;
import org.apache.gravitino.client.GravitinoClient;
@@ -28,28 +31,70 @@ import org.apache.gravitino.exceptions.NoSuchTagException;
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() {
+ if (!AreYouSure.really(force)) {
+ return;
+ }
+ boolean hasOnlyOneTag = tags.length == 1;
+ if (hasOnlyOneTag) {
+ handleOnlyOneTag();
+ } else {
+ handleMultipleTags();
+ }
+ }
+
+ private void handleMultipleTags() {
+ List<String> deleted = new ArrayList<>();
+ try {
+ GravitinoClient client = buildClient(metalake);
+ 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.isEmpty()) {
+ System.out.println("Tags " + String.join(",", deleted) + " deleted.");
+ }
+ if (deleted.size() < tags.length) {
+ List<String> remaining = Arrays.asList(tags);
+ remaining.removeAll(deleted);
+ System.out.println("Tags " + String.join(",", deleted) + " not
deleted.");
+ }
+ }
+
+ private void handleOnlyOneTag() {
boolean deleted = false;
if (!AreYouSure.really(force)) {
@@ -58,7 +103,7 @@ public class DeleteTag extends Command {
try {
GravitinoClient client = buildClient(metalake);
- deleted = client.deleteTag(tag);
+ deleted = client.deleteTag(tags[0]);
} catch (NoSuchMetalakeException err) {
System.err.println(ErrorMessages.UNKNOWN_METALAKE);
return;
@@ -71,9 +116,9 @@ public class DeleteTag extends Command {
}
if (deleted) {
- System.out.println(tag + " deleted.");
+ System.out.println(tags[0] + " deleted.");
} else {
- System.out.println(tag + " not deleted.");
+ System.out.println(tags[0] + " not deleted.");
}
}
}
diff --git
a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/TagEntity.java
b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/TagEntity.java
index 08e1d2ca1..ed474c784 100644
--- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/TagEntity.java
+++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/TagEntity.java
@@ -34,29 +34,30 @@ import org.apache.gravitino.rel.Table;
public class TagEntity extends Command {
protected final String metalake;
protected final FullName name;
- protected final String tag;
+ protected final String[] tags;
/**
- * Tag an entity with an existing tag.
+ * Tag an entity with existing 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 name The name of the entity.
- * @param tag The name of the tag.
+ * @param tags The names of the tags.
*/
- public TagEntity(String url, boolean ignoreVersions, String metalake,
FullName name, String tag) {
+ public TagEntity(
+ String url, boolean ignoreVersions, String metalake, FullName name,
String[] tags) {
super(url, ignoreVersions);
this.metalake = metalake;
this.name = name;
- this.tag = tag;
+ this.tags = tags;
}
- /** Create a new tag. */
+ /** Add tags for an entity. */
@Override
public void handle() {
String entity = "unknown";
- String[] tags = new String[0];
+ String[] tagsToAdd = new String[0];
try {
GravitinoClient client = buildClient(metalake);
@@ -71,18 +72,18 @@ public class TagEntity extends Command {
.loadCatalog(catalog)
.asTableCatalog()
.loadTable(NameIdentifier.of(schema, table));
- tags = gTable.supportsTags().associateTags(new String[] {tag}, null);
+ tagsToAdd = gTable.supportsTags().associateTags(tags, null);
entity = table;
} else if (name.hasSchemaName()) {
String catalog = name.getCatalogName();
String schema = name.getSchemaName();
Schema gSchema =
client.loadCatalog(catalog).asSchemas().loadSchema(schema);
- tags = gSchema.supportsTags().associateTags(new String[] {tag}, null);
+ tagsToAdd = gSchema.supportsTags().associateTags(tags, null);
entity = schema;
} else if (name.hasCatalogName()) {
String catalog = name.getCatalogName();
Catalog gCatalog = client.loadCatalog(catalog);
- tags = gCatalog.supportsTags().associateTags(new String[] {tag}, null);
+ tagsToAdd = gCatalog.supportsTags().associateTags(tags, null);
entity = catalog;
}
} catch (NoSuchMetalakeException err) {
@@ -102,7 +103,7 @@ public class TagEntity extends Command {
return;
}
- String all = String.join(",", tags);
+ String all = String.join(",", tagsToAdd);
System.out.println(entity + " tagged with " + all);
}
diff --git
a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UntagEntity.java
b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UntagEntity.java
index 91b9fcb15..77437dafc 100644
---
a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UntagEntity.java
+++
b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UntagEntity.java
@@ -34,30 +34,30 @@ import org.apache.gravitino.rel.Table;
public class UntagEntity extends Command {
protected final String metalake;
protected final FullName name;
- protected final String tag;
+ protected final String[] tags;
/**
- * Untag an entity with an existing tag.
+ * Remove existing tags from an entity.
*
* @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 name The name of the entity.
- * @param tag The name of the tag.
+ * @param tags The names of the tags.
*/
public UntagEntity(
- String url, boolean ignoreVersions, String metalake, FullName name,
String tag) {
+ String url, boolean ignoreVersions, String metalake, FullName name,
String[] tags) {
super(url, ignoreVersions);
this.metalake = metalake;
this.name = name;
- this.tag = tag;
+ this.tags = tags;
}
- /** Create a new tag. */
+ /** Remove tags from an entity. */
@Override
public void handle() {
String entity = "unknown";
- String[] tags = new String[0];
+ String[] removeTags = new String[0];
try {
GravitinoClient client = buildClient(metalake);
@@ -72,18 +72,18 @@ public class UntagEntity extends Command {
.loadCatalog(catalog)
.asTableCatalog()
.loadTable(NameIdentifier.of(schema, table));
- tags = gTable.supportsTags().associateTags(null, new String[] {tag});
+ removeTags = gTable.supportsTags().associateTags(null, tags);
entity = table;
} else if (name.hasSchemaName()) {
String catalog = name.getCatalogName();
String schema = name.getSchemaName();
Schema gSchema =
client.loadCatalog(catalog).asSchemas().loadSchema(schema);
- tags = gSchema.supportsTags().associateTags(null, new String[] {tag});
+ removeTags = gSchema.supportsTags().associateTags(null, tags);
entity = schema;
} else if (name.hasCatalogName()) {
String catalog = name.getCatalogName();
Catalog gCatalog = client.loadCatalog(catalog);
- tags = gCatalog.supportsTags().associateTags(null, new String[] {tag});
+ removeTags = gCatalog.supportsTags().associateTags(null, tags);
entity = catalog;
}
} catch (NoSuchMetalakeException err) {
@@ -103,12 +103,13 @@ public class UntagEntity extends Command {
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);
}
}
diff --git
a/clients/cli/src/test/java/org/apache/gravitino/cli/TestTagCommands.java
b/clients/cli/src/test/java/org/apache/gravitino/cli/TestTagCommands.java
index 3dfd0392c..91a809fbc 100644
--- a/clients/cli/src/test/java/org/apache/gravitino/cli/TestTagCommands.java
+++ b/clients/cli/src/test/java/org/apache/gravitino/cli/TestTagCommands.java
@@ -19,6 +19,7 @@
package org.apache.gravitino.cli;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
@@ -41,8 +42,10 @@ import org.apache.gravitino.cli.commands.TagEntity;
import org.apache.gravitino.cli.commands.UntagEntity;
import org.apache.gravitino.cli.commands.UpdateTagComment;
import org.apache.gravitino.cli.commands.UpdateTagName;
+import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentMatcher;
class TestTagCommands {
private CommandLine mockCommandLine;
@@ -75,7 +78,7 @@ class TestTagCommands {
TagDetails mockDetails = mock(TagDetails.class);
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
- when(mockCommandLine.hasOption(GravitinoOptions.TAG)).thenReturn(true);
+ when(mockCommandLine.getOptionValues(GravitinoOptions.TAG)).thenReturn(new
String[] {"tagA"});
when(mockCommandLine.getOptionValue(GravitinoOptions.TAG)).thenReturn("tagA");
GravitinoCommandLine commandLine =
spy(
@@ -94,7 +97,33 @@ class TestTagCommands {
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
when(mockCommandLine.hasOption(GravitinoOptions.TAG)).thenReturn(true);
-
when(mockCommandLine.getOptionValue(GravitinoOptions.TAG)).thenReturn("tagA");
+ when(mockCommandLine.getOptionValues(GravitinoOptions.TAG)).thenReturn(new
String[] {"tagA"});
+ when(mockCommandLine.hasOption(GravitinoOptions.COMMENT)).thenReturn(true);
+
when(mockCommandLine.getOptionValue(GravitinoOptions.COMMENT)).thenReturn("comment");
+ GravitinoCommandLine commandLine =
+ spy(
+ new GravitinoCommandLine(
+ mockCommandLine, mockOptions, CommandEntities.TAG,
CommandActions.CREATE));
+ doReturn(mockCreate)
+ .when(commandLine)
+ .newCreateTags(
+ GravitinoCommandLine.DEFAULT_URL,
+ false,
+ "metalake_demo",
+ new String[] {"tagA"},
+ "comment");
+ commandLine.handleCommandLine();
+ verify(mockCreate).handle();
+ }
+
+ @Test
+ void testCreateTagsCommand() {
+ CreateTag mockCreate = mock(CreateTag.class);
+
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
+ when(mockCommandLine.hasOption(GravitinoOptions.TAG)).thenReturn(true);
+ when(mockCommandLine.getOptionValues(GravitinoOptions.TAG))
+ .thenReturn(new String[] {"tagA", "tagB"});
when(mockCommandLine.hasOption(GravitinoOptions.COMMENT)).thenReturn(true);
when(mockCommandLine.getOptionValue(GravitinoOptions.COMMENT)).thenReturn("comment");
GravitinoCommandLine commandLine =
@@ -103,7 +132,12 @@ class TestTagCommands {
mockCommandLine, mockOptions, CommandEntities.TAG,
CommandActions.CREATE));
doReturn(mockCreate)
.when(commandLine)
- .newCreateTag(GravitinoCommandLine.DEFAULT_URL, false,
"metalake_demo", "tagA", "comment");
+ .newCreateTags(
+ GravitinoCommandLine.DEFAULT_URL,
+ false,
+ "metalake_demo",
+ new String[] {"tagA", "tagB"},
+ "comment");
commandLine.handleCommandLine();
verify(mockCreate).handle();
}
@@ -114,14 +148,15 @@ class TestTagCommands {
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
when(mockCommandLine.hasOption(GravitinoOptions.TAG)).thenReturn(true);
-
when(mockCommandLine.getOptionValue(GravitinoOptions.TAG)).thenReturn("tagA");
+ when(mockCommandLine.getOptionValues(GravitinoOptions.TAG)).thenReturn(new
String[] {"tagA"});
GravitinoCommandLine commandLine =
spy(
new GravitinoCommandLine(
mockCommandLine, mockOptions, CommandEntities.TAG,
CommandActions.CREATE));
doReturn(mockCreate)
.when(commandLine)
- .newCreateTag(GravitinoCommandLine.DEFAULT_URL, false,
"metalake_demo", "tagA", null);
+ .newCreateTags(
+ GravitinoCommandLine.DEFAULT_URL, false, "metalake_demo", new
String[] {"tagA"}, null);
commandLine.handleCommandLine();
verify(mockCreate).handle();
}
@@ -132,14 +167,39 @@ class TestTagCommands {
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
when(mockCommandLine.hasOption(GravitinoOptions.TAG)).thenReturn(true);
-
when(mockCommandLine.getOptionValue(GravitinoOptions.TAG)).thenReturn("tagA");
+ when(mockCommandLine.getOptionValues(GravitinoOptions.TAG)).thenReturn(new
String[] {"tagA"});
+ GravitinoCommandLine commandLine =
+ spy(
+ new GravitinoCommandLine(
+ mockCommandLine, mockOptions, CommandEntities.TAG,
CommandActions.DELETE));
+ doReturn(mockDelete)
+ .when(commandLine)
+ .newDeleteTag(
+ GravitinoCommandLine.DEFAULT_URL, false, false, "metalake_demo",
new String[] {"tagA"});
+ commandLine.handleCommandLine();
+ verify(mockDelete).handle();
+ }
+
+ @Test
+ void testDeleteTagsCommand() {
+ DeleteTag mockDelete = mock(DeleteTag.class);
+
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
+ when(mockCommandLine.hasOption(GravitinoOptions.TAG)).thenReturn(true);
+ when(mockCommandLine.getOptionValues(GravitinoOptions.TAG))
+ .thenReturn(new String[] {"tagA", "tagB"});
GravitinoCommandLine commandLine =
spy(
new GravitinoCommandLine(
mockCommandLine, mockOptions, CommandEntities.TAG,
CommandActions.DELETE));
doReturn(mockDelete)
.when(commandLine)
- .newDeleteTag(GravitinoCommandLine.DEFAULT_URL, false, false,
"metalake_demo", "tagA");
+ .newDeleteTag(
+ GravitinoCommandLine.DEFAULT_URL,
+ false,
+ false,
+ "metalake_demo",
+ new String[] {"tagA", "tagB"});
commandLine.handleCommandLine();
verify(mockDelete).handle();
}
@@ -150,7 +210,7 @@ class TestTagCommands {
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
when(mockCommandLine.hasOption(GravitinoOptions.TAG)).thenReturn(true);
-
when(mockCommandLine.getOptionValue(GravitinoOptions.TAG)).thenReturn("tagA");
+ when(mockCommandLine.getOptionValues(GravitinoOptions.TAG)).thenReturn(new
String[] {"tagA"});
when(mockCommandLine.hasOption(GravitinoOptions.FORCE)).thenReturn(true);
GravitinoCommandLine commandLine =
spy(
@@ -158,7 +218,8 @@ class TestTagCommands {
mockCommandLine, mockOptions, CommandEntities.TAG,
CommandActions.DELETE));
doReturn(mockDelete)
.when(commandLine)
- .newDeleteTag(GravitinoCommandLine.DEFAULT_URL, false, true,
"metalake_demo", "tagA");
+ .newDeleteTag(
+ GravitinoCommandLine.DEFAULT_URL, false, true, "metalake_demo",
new String[] {"tagA"});
commandLine.handleCommandLine();
verify(mockDelete).handle();
}
@@ -169,7 +230,7 @@ class TestTagCommands {
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
when(mockCommandLine.hasOption(GravitinoOptions.TAG)).thenReturn(true);
-
when(mockCommandLine.getOptionValue(GravitinoOptions.TAG)).thenReturn("tagA");
+ when(mockCommandLine.getOptionValues(GravitinoOptions.TAG)).thenReturn(new
String[] {"tagA"});
when(mockCommandLine.hasOption(GravitinoOptions.PROPERTY)).thenReturn(true);
when(mockCommandLine.getOptionValue(GravitinoOptions.PROPERTY)).thenReturn("property");
when(mockCommandLine.hasOption(GravitinoOptions.VALUE)).thenReturn(true);
@@ -186,13 +247,34 @@ class TestTagCommands {
verify(mockSetProperty).handle();
}
+ @Test
+ void testSetMultipleTagPropertyCommandError() {
+
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
+ when(mockCommandLine.hasOption(GravitinoOptions.TAG)).thenReturn(true);
+ when(mockCommandLine.getOptionValues(GravitinoOptions.TAG))
+ .thenReturn(new String[] {"tagA", "tagB"});
+
when(mockCommandLine.hasOption(GravitinoOptions.PROPERTY)).thenReturn(true);
+
when(mockCommandLine.getOptionValue(GravitinoOptions.PROPERTY)).thenReturn("property");
+ when(mockCommandLine.hasOption(GravitinoOptions.VALUE)).thenReturn(true);
+
when(mockCommandLine.getOptionValue(GravitinoOptions.VALUE)).thenReturn("value");
+ GravitinoCommandLine commandLine =
+ spy(
+ new GravitinoCommandLine(
+ mockCommandLine, mockOptions, CommandEntities.TAG,
CommandActions.SET));
+ Assertions.assertThrows(
+ IllegalArgumentException.class,
+ () -> commandLine.handleCommandLine(),
+ "Error: The current command only supports one --tag option.");
+ }
+
@Test
void testRemoveTagPropertyCommand() {
RemoveTagProperty mockRemoveProperty = mock(RemoveTagProperty.class);
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
when(mockCommandLine.hasOption(GravitinoOptions.TAG)).thenReturn(true);
-
when(mockCommandLine.getOptionValue(GravitinoOptions.TAG)).thenReturn("tagA");
+ when(mockCommandLine.getOptionValues(GravitinoOptions.TAG)).thenReturn(new
String[] {"tagA"});
when(mockCommandLine.hasOption(GravitinoOptions.PROPERTY)).thenReturn(true);
when(mockCommandLine.getOptionValue(GravitinoOptions.PROPERTY)).thenReturn("property");
GravitinoCommandLine commandLine =
@@ -213,7 +295,7 @@ class TestTagCommands {
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
when(mockCommandLine.hasOption(GravitinoOptions.TAG)).thenReturn(true);
-
when(mockCommandLine.getOptionValue(GravitinoOptions.TAG)).thenReturn("tagA");
+ when(mockCommandLine.getOptionValues(GravitinoOptions.TAG)).thenReturn(new
String[] {"tagA"});
GravitinoCommandLine commandLine =
spy(
new GravitinoCommandLine(
@@ -232,7 +314,7 @@ class TestTagCommands {
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
when(mockCommandLine.hasOption(GravitinoOptions.COMMENT)).thenReturn(true);
when(mockCommandLine.hasOption(GravitinoOptions.TAG)).thenReturn(true);
-
when(mockCommandLine.getOptionValue(GravitinoOptions.TAG)).thenReturn("tagA");
+ when(mockCommandLine.getOptionValues(GravitinoOptions.TAG)).thenReturn(new
String[] {"tagA"});
when(mockCommandLine.getOptionValue(GravitinoOptions.COMMENT)).thenReturn("new
comment");
GravitinoCommandLine commandLine =
spy(
@@ -252,7 +334,7 @@ class TestTagCommands {
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
when(mockCommandLine.hasOption(GravitinoOptions.TAG)).thenReturn(true);
-
when(mockCommandLine.getOptionValue(GravitinoOptions.TAG)).thenReturn("tagA");
+ when(mockCommandLine.getOptionValues(GravitinoOptions.TAG)).thenReturn(new
String[] {"tagA"});
when(mockCommandLine.hasOption(GravitinoOptions.RENAME)).thenReturn(true);
when(mockCommandLine.getOptionValue(GravitinoOptions.RENAME)).thenReturn("tagB");
GravitinoCommandLine commandLine =
@@ -293,7 +375,39 @@ class TestTagCommands {
when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(true);
when(mockCommandLine.getOptionValue(GravitinoOptions.NAME)).thenReturn("catalog.schema.table");
when(mockCommandLine.hasOption(GravitinoOptions.TAG)).thenReturn(true);
-
when(mockCommandLine.getOptionValue(GravitinoOptions.TAG)).thenReturn("tagA");
+ when(mockCommandLine.getOptionValues(GravitinoOptions.TAG)).thenReturn(new
String[] {"tagA"});
+ GravitinoCommandLine commandLine =
+ spy(
+ new GravitinoCommandLine(
+ mockCommandLine, mockOptions, CommandEntities.TAG,
CommandActions.SET));
+ doReturn(mockTagEntity)
+ .when(commandLine)
+ .newTagEntity(
+ eq(GravitinoCommandLine.DEFAULT_URL),
+ eq(false),
+ eq("metalake_demo"),
+ any(),
+ argThat(
+ new ArgumentMatcher<String[]>() {
+ @Override
+ public boolean matches(String[] argument) {
+ return argument != null && argument.length > 0 &&
"tagA".equals(argument[0]);
+ }
+ }));
+ commandLine.handleCommandLine();
+ verify(mockTagEntity).handle();
+ }
+
+ @Test
+ void testTagsEntityCommand() {
+ TagEntity mockTagEntity = mock(TagEntity.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.table");
+ when(mockCommandLine.hasOption(GravitinoOptions.TAG)).thenReturn(true);
+ when(mockCommandLine.getOptionValues(GravitinoOptions.TAG))
+ .thenReturn(new String[] {"tagA", "tagB"});
GravitinoCommandLine commandLine =
spy(
new GravitinoCommandLine(
@@ -305,7 +419,16 @@ class TestTagCommands {
eq(false),
eq("metalake_demo"),
any(),
- eq("tagA"));
+ argThat(
+ new ArgumentMatcher<String[]>() {
+ @Override
+ public boolean matches(String[] argument) {
+ return argument != null
+ && argument.length == 2
+ && "tagA".equals(argument[0])
+ && "tagB".equals(argument[1]);
+ }
+ }));
commandLine.handleCommandLine();
verify(mockTagEntity).handle();
}
@@ -318,7 +441,42 @@ class TestTagCommands {
when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(true);
when(mockCommandLine.getOptionValue(GravitinoOptions.NAME)).thenReturn("catalog.schema.table");
when(mockCommandLine.hasOption(GravitinoOptions.TAG)).thenReturn(true);
-
when(mockCommandLine.getOptionValue(GravitinoOptions.TAG)).thenReturn("tagA");
+ when(mockCommandLine.getOptionValues(GravitinoOptions.TAG))
+ .thenReturn(new String[] {"tagA", "tagB"});
+
when(mockCommandLine.hasOption(GravitinoOptions.PROPERTY)).thenReturn(false);
+
when(mockCommandLine.getOptionValue(GravitinoOptions.PROPERTY)).thenReturn(null);
+ GravitinoCommandLine commandLine =
+ spy(
+ new GravitinoCommandLine(
+ mockCommandLine, mockOptions, CommandEntities.TAG,
CommandActions.REMOVE));
+ doReturn(mockUntagEntity)
+ .when(commandLine)
+ .newUntagEntity(
+ eq(GravitinoCommandLine.DEFAULT_URL),
+ eq(false),
+ eq("metalake_demo"),
+ any(),
+ argThat(
+ new ArgumentMatcher<String[]>() {
+ @Override
+ public boolean matches(String[] argument) {
+ return argument != null && argument.length > 0 &&
"tagA".equals(argument[0]);
+ }
+ }));
+ commandLine.handleCommandLine();
+ verify(mockUntagEntity).handle();
+ }
+
+ @Test
+ void testUntagsEntityCommand() {
+ UntagEntity mockUntagEntity = mock(UntagEntity.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.table");
+ when(mockCommandLine.hasOption(GravitinoOptions.TAG)).thenReturn(true);
+ when(mockCommandLine.getOptionValues(GravitinoOptions.TAG))
+ .thenReturn(new String[] {"tagA", "tagB"});
when(mockCommandLine.hasOption(GravitinoOptions.PROPERTY)).thenReturn(false);
when(mockCommandLine.getOptionValue(GravitinoOptions.PROPERTY)).thenReturn(null);
GravitinoCommandLine commandLine =
@@ -332,7 +490,16 @@ class TestTagCommands {
eq(false),
eq("metalake_demo"),
any(),
- eq("tagA"));
+ argThat(
+ new ArgumentMatcher<String[]>() {
+ @Override
+ public boolean matches(String[] argument) {
+ return argument != null
+ && argument.length == 2
+ && "tagA".equals(argument[0])
+ && "tagB".equals(argument[1]);
+ }
+ }));
commandLine.handleCommandLine();
verify(mockUntagEntity).handle();
}
diff --git a/docs/cli.md b/docs/cli.md
index d9862df9b..e37e92e10 100644
--- a/docs/cli.md
+++ b/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
```
#### List all tag
@@ -468,22 +468,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
```
-#### Remove a tag from an entity
+#### Remove tags from an entity
```bash
-gcli tag remove --name catalog_postgres.hr --tag tagA
+gcli tag remove --name catalog_postgres.hr --tag tagA tagB
```
#### List all tags on an entity