This is an automated email from the ASF dual-hosted git repository.
jshao 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 626bc7943 [#5506] feat(CLI): Table formatted output (#5714)
626bc7943 is described below
commit 626bc7943b56af431215ea3cd4aae9ce5cee6bef
Author: Jimmy Lee <[email protected]>
AuthorDate: Mon Dec 2 10:52:03 2024 +0800
[#5506] feat(CLI): Table formatted output (#5714)
### What changes were proposed in this pull request?
Enhance the Gravitino CLI with formatted output.
<img width="323" alt="383987880-78e48033-601e-422f-b5bb-20061536c6b2"
src="https://github.com/user-attachments/assets/9655c081-1575-44c3-bcce-49ca2b49a259">
### Why are the changes needed?
Issue: #5506
Cause the original PR (https://github.com/apache/gravitino/pull/5606)
cannot be reopened, I create a new PR.
The purpose is to make Gravitino CLI output look more readable;
currently, it supports only table and plain output formats.
### Does this PR introduce _any_ user-facing change?
No.
### How was this patch tested?
```shell
gcli metalake list
gcli metalake list --output plain
gcli metalake list --output table
```
---
clients/cli/build.gradle.kts | 11 +-
.../apache/gravitino/cli/GravitinoCommandLine.java | 8 +-
.../org/apache/gravitino/cli/GravitinoOptions.java | 3 +
.../apache/gravitino/cli/TestableCommandLine.java | 13 +-
.../gravitino/cli/commands/CatalogDetails.java | 15 +-
.../org/apache/gravitino/cli/commands/Command.java | 30 ++++
.../gravitino/cli/commands/ListMetalakes.java | 21 +--
.../gravitino/cli/commands/MetalakeDetails.java | 14 +-
.../apache/gravitino/cli/outputs/OutputFormat.java | 24 +++
.../apache/gravitino/cli/outputs/PlainFormat.java | 71 +++++++++
.../apache/gravitino/cli/outputs/TableFormat.java | 157 +++++++++++++++++++
.../apache/gravitino/cli/TestCatalogCommands.java | 3 +-
.../apache/gravitino/cli/TestMetalakeCommands.java | 6 +-
.../cli/integration/test/TableFormatOutputIT.java | 166 +++++++++++++++++++++
14 files changed, 495 insertions(+), 47 deletions(-)
diff --git a/clients/cli/build.gradle.kts b/clients/cli/build.gradle.kts
index 9ce570ef8..ae45fc968 100644
--- a/clients/cli/build.gradle.kts
+++ b/clients/cli/build.gradle.kts
@@ -34,7 +34,16 @@ dependencies {
testImplementation(libs.junit.jupiter.api)
testImplementation(libs.junit.jupiter.params)
testImplementation(libs.mockito.core)
-
+ testImplementation(project(":core")) {
+ exclude("org.apache.logging.log4j")
+ }
+ testImplementation(project(":server")) {
+ exclude("org.apache.logging.log4j")
+ }
+ testImplementation(project(":server-common")) {
+ exclude("org.apache.logging.log4j")
+ }
+ testImplementation(project(":integration-test-common", "testArtifacts"))
testRuntimeOnly(libs.junit.jupiter.engine)
}
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 ca149672a..e7a36d016 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
@@ -153,15 +153,16 @@ public class GravitinoCommandLine extends
TestableCommandLine {
String url = getUrl();
FullName name = new FullName(line);
String metalake = name.getMetalakeName();
+ String outputFormat = line.getOptionValue(GravitinoOptions.OUTPUT);
if (CommandActions.DETAILS.equals(command)) {
if (line.hasOption(GravitinoOptions.AUDIT)) {
newMetalakeAudit(url, ignore, metalake).handle();
} else {
- newMetalakeDetails(url, ignore, metalake).handle();
+ newMetalakeDetails(url, ignore, metalake, outputFormat).handle();
}
} else if (CommandActions.LIST.equals(command)) {
- newListMetalakes(url, ignore).handle();
+ newListMetalakes(url, ignore, outputFormat).handle();
} else if (CommandActions.CREATE.equals(command)) {
String comment = line.getOptionValue(GravitinoOptions.COMMENT);
newCreateMetalake(url, ignore, metalake, comment).handle();
@@ -197,6 +198,7 @@ public class GravitinoCommandLine extends
TestableCommandLine {
String url = getUrl();
FullName name = new FullName(line);
String metalake = name.getMetalakeName();
+ String outputFormat = line.getOptionValue(GravitinoOptions.OUTPUT);
if (CommandActions.LIST.equals(command)) {
newListCatalogs(url, ignore, metalake).handle();
@@ -209,7 +211,7 @@ public class GravitinoCommandLine extends
TestableCommandLine {
if (line.hasOption(GravitinoOptions.AUDIT)) {
newCatalogAudit(url, ignore, metalake, catalog).handle();
} else {
- newCatalogDetails(url, ignore, metalake, catalog).handle();
+ newCatalogDetails(url, ignore, metalake, catalog,
outputFormat).handle();
}
} else if (CommandActions.CREATE.equals(command)) {
String comment = line.getOptionValue(GravitinoOptions.COMMENT);
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 863bc5d21..8cf5f0cc1 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
@@ -47,6 +47,7 @@ public class GravitinoOptions {
public static final String INDEX = "index";
public static final String DISTRIBUTION = "distribution";
public static final String PARTITION = "partition";
+ public static final String OUTPUT = "output";
/**
* Builds and returns the CLI options for Gravitino.
@@ -90,6 +91,8 @@ public class GravitinoOptions {
// Force delete entities and rename metalake operations
options.addOption(createSimpleOption("f", FORCE, "force operation"));
+ options.addOption(createArgOption(null, OUTPUT, "output format
(plain/table)"));
+
return options;
}
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 248f57cd6..57de13f0a 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
@@ -129,12 +129,13 @@ public class TestableCommandLine {
return new MetalakeAudit(url, ignore, metalake);
}
- protected MetalakeDetails newMetalakeDetails(String url, boolean ignore,
String metalake) {
- return new MetalakeDetails(url, ignore, metalake);
+ protected MetalakeDetails newMetalakeDetails(
+ String url, boolean ignore, String metalake, String outputFormat) {
+ return new MetalakeDetails(url, ignore, metalake, outputFormat);
}
- protected ListMetalakes newListMetalakes(String url, boolean ignore) {
- return new ListMetalakes(url, ignore);
+ protected ListMetalakes newListMetalakes(String url, boolean ignore, String
outputFormat) {
+ return new ListMetalakes(url, ignore, outputFormat);
}
protected CreateMetalake newCreateMetalake(
@@ -178,8 +179,8 @@ public class TestableCommandLine {
}
protected CatalogDetails newCatalogDetails(
- String url, boolean ignore, String metalake, String catalog) {
- return new CatalogDetails(url, ignore, metalake, catalog);
+ String url, boolean ignore, String metalake, String catalog, String
outputFormat) {
+ return new CatalogDetails(url, ignore, metalake, catalog, outputFormat);
}
protected ListCatalogs newListCatalogs(String url, boolean ignore, String
metalake) {
diff --git
a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/CatalogDetails.java
b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/CatalogDetails.java
index 41451daac..bebe536fa 100644
---
a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/CatalogDetails.java
+++
b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/CatalogDetails.java
@@ -37,9 +37,11 @@ public class CatalogDetails extends Command {
* @param ignoreVersions If true don't check the client/server versions
match.
* @param metalake The name of the metalake.
* @param catalog The name of the catalog.
+ * @param outputFormat The output format.
*/
- public CatalogDetails(String url, boolean ignoreVersions, String metalake,
String catalog) {
- super(url, ignoreVersions);
+ public CatalogDetails(
+ String url, boolean ignoreVersions, String metalake, String catalog,
String outputFormat) {
+ super(url, ignoreVersions, outputFormat);
this.metalake = metalake;
this.catalog = catalog;
}
@@ -52,20 +54,13 @@ public class CatalogDetails extends Command {
try {
GravitinoClient client = buildClient(metalake);
result = client.loadCatalog(catalog);
+ output(result);
} catch (NoSuchMetalakeException err) {
System.err.println(ErrorMessages.UNKNOWN_METALAKE);
- return;
} catch (NoSuchCatalogException err) {
System.err.println(ErrorMessages.UNKNOWN_CATALOG);
- return;
} catch (Exception exp) {
System.err.println(exp.getMessage());
- return;
- }
-
- if (result != null) {
- System.out.println(
- result.name() + "," + result.type() + "," + result.provider() + ","
+ result.comment());
}
}
}
diff --git
a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/Command.java
b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/Command.java
index 040bd7141..1753e5743 100644
--- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/Command.java
+++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/Command.java
@@ -19,6 +19,8 @@
package org.apache.gravitino.cli.commands;
+import org.apache.gravitino.cli.outputs.PlainFormat;
+import org.apache.gravitino.cli.outputs.TableFormat;
import org.apache.gravitino.client.GravitinoAdminClient;
import org.apache.gravitino.client.GravitinoClient;
import org.apache.gravitino.exceptions.NoSuchMetalakeException;
@@ -27,6 +29,9 @@ import
org.apache.gravitino.exceptions.NoSuchMetalakeException;
public abstract class Command {
private final String url;
private final boolean ignoreVersions;
+ private final String outputFormat;
+ public static String OUTPUT_FORMAT_TABLE = "table";
+ public static String OUTPUT_FORMAT_PLAIN = "plain";
/**
* Command constructor.
@@ -35,8 +40,13 @@ public abstract class Command {
* @param ignoreVersions If true don't check the client/server versions
match.
*/
public Command(String url, boolean ignoreVersions) {
+ this(url, ignoreVersions, null);
+ }
+
+ public Command(String url, boolean ignoreVersions, String outputFormat) {
this.url = url;
this.ignoreVersions = ignoreVersions;
+ this.outputFormat = outputFormat;
}
/** All commands have a handle method to handle and run the required
command. */
@@ -69,4 +79,24 @@ public abstract class Command {
return GravitinoAdminClient.builder(url).build();
}
}
+
+ /**
+ * Outputs the entity to the console.
+ *
+ * @param entity The entity to output.
+ */
+ protected <T> void output(T entity) {
+ if (outputFormat == null) {
+ PlainFormat.output(entity);
+ return;
+ }
+
+ if (outputFormat.equals(OUTPUT_FORMAT_TABLE)) {
+ TableFormat.output(entity);
+ } else if (outputFormat.equals(OUTPUT_FORMAT_PLAIN)) {
+ PlainFormat.output(entity);
+ } else {
+ throw new IllegalArgumentException("Unsupported output format");
+ }
+ }
}
diff --git
a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListMetalakes.java
b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListMetalakes.java
index 26668157a..de7571568 100644
---
a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListMetalakes.java
+++
b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListMetalakes.java
@@ -19,9 +19,6 @@
package org.apache.gravitino.cli.commands;
-import com.google.common.base.Joiner;
-import java.util.ArrayList;
-import java.util.List;
import org.apache.gravitino.Metalake;
import org.apache.gravitino.client.GravitinoAdminClient;
@@ -33,30 +30,22 @@ public class ListMetalakes extends Command {
*
* @param url The URL of the Gravitino server.
* @param ignoreVersions If true don't check the client/server versions
match.
+ * @param outputFormat The output format.
*/
- public ListMetalakes(String url, boolean ignoreVersions) {
- super(url, ignoreVersions);
+ public ListMetalakes(String url, boolean ignoreVersions, String
outputFormat) {
+ super(url, ignoreVersions, outputFormat);
}
/** Lists all metalakes. */
@Override
public void handle() {
- Metalake[] metalakes = new Metalake[0];
+ Metalake[] metalakes;
try {
GravitinoAdminClient client = buildAdminClient();
metalakes = client.listMetalakes();
+ output(metalakes);
} catch (Exception exp) {
System.err.println(exp.getMessage());
- return;
}
-
- List<String> metalakeNames = new ArrayList<>();
- for (int i = 0; i < metalakes.length; i++) {
- metalakeNames.add(metalakes[i].name());
- }
-
- String all = Joiner.on(System.lineSeparator()).join(metalakeNames);
-
- System.out.println(all.toString());
}
}
diff --git
a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/MetalakeDetails.java
b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/MetalakeDetails.java
index c00c90c53..127b9a584 100644
---
a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/MetalakeDetails.java
+++
b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/MetalakeDetails.java
@@ -19,6 +19,7 @@
package org.apache.gravitino.cli.commands;
+import org.apache.gravitino.Metalake;
import org.apache.gravitino.cli.ErrorMessages;
import org.apache.gravitino.client.GravitinoClient;
import org.apache.gravitino.exceptions.NoSuchMetalakeException;
@@ -33,27 +34,24 @@ public class MetalakeDetails 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 outputFormat The output format.
*/
- public MetalakeDetails(String url, boolean ignoreVersions, String metalake) {
- super(url, ignoreVersions);
+ public MetalakeDetails(String url, boolean ignoreVersions, String metalake,
String outputFormat) {
+ super(url, ignoreVersions, outputFormat);
this.metalake = metalake;
}
/** Displays the name and comment of a metalake. */
@Override
public void handle() {
- String comment = "";
try {
GravitinoClient client = buildClient(metalake);
- comment = client.loadMetalake(metalake).comment();
+ Metalake metalakeEntity = client.loadMetalake(metalake);
+ output(metalakeEntity);
} catch (NoSuchMetalakeException err) {
System.err.println(ErrorMessages.UNKNOWN_METALAKE);
- return;
} catch (Exception exp) {
System.err.println(exp.getMessage());
- return;
}
-
- System.out.println(metalake + "," + comment);
}
}
diff --git
a/clients/cli/src/main/java/org/apache/gravitino/cli/outputs/OutputFormat.java
b/clients/cli/src/main/java/org/apache/gravitino/cli/outputs/OutputFormat.java
new file mode 100644
index 000000000..8e6ab3116
--- /dev/null
+++
b/clients/cli/src/main/java/org/apache/gravitino/cli/outputs/OutputFormat.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.cli.outputs;
+
+/** Output format interface for the CLI results. */
+public interface OutputFormat<T> {
+ void output(T object);
+}
diff --git
a/clients/cli/src/main/java/org/apache/gravitino/cli/outputs/PlainFormat.java
b/clients/cli/src/main/java/org/apache/gravitino/cli/outputs/PlainFormat.java
new file mode 100644
index 000000000..4674d3f88
--- /dev/null
+++
b/clients/cli/src/main/java/org/apache/gravitino/cli/outputs/PlainFormat.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.cli.outputs;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.apache.gravitino.Catalog;
+import org.apache.gravitino.Metalake;
+
+/** Plain format to print a pretty string to standard out. */
+public class PlainFormat {
+ public static void output(Object object) {
+ if (object instanceof Metalake) {
+ new MetalakeStringFormat().output((Metalake) object);
+ } else if (object instanceof Metalake[]) {
+ new MetalakesStringFormat().output((Metalake[]) object);
+ } else if (object instanceof Catalog) {
+ new CatalogStringFormat().output((Catalog) object);
+ } else {
+ throw new IllegalArgumentException("Unsupported object type");
+ }
+ }
+
+ static final class MetalakeStringFormat implements OutputFormat<Metalake> {
+ @Override
+ public void output(Metalake metalake) {
+ System.out.println(metalake.name() + "," + metalake.comment());
+ }
+ }
+
+ static final class MetalakesStringFormat implements OutputFormat<Metalake[]>
{
+ @Override
+ public void output(Metalake[] metalakes) {
+ List<String> metalakeNames =
+
Arrays.stream(metalakes).map(Metalake::name).collect(Collectors.toList());
+ String all = String.join(System.lineSeparator(), metalakeNames);
+ System.out.println(all);
+ }
+ }
+
+ static final class CatalogStringFormat implements OutputFormat<Catalog> {
+ @Override
+ public void output(Catalog catalog) {
+ System.out.println(
+ catalog.name()
+ + ","
+ + catalog.type()
+ + ","
+ + catalog.provider()
+ + ","
+ + catalog.comment());
+ }
+ }
+}
diff --git
a/clients/cli/src/main/java/org/apache/gravitino/cli/outputs/TableFormat.java
b/clients/cli/src/main/java/org/apache/gravitino/cli/outputs/TableFormat.java
new file mode 100644
index 000000000..a1975b9f3
--- /dev/null
+++
b/clients/cli/src/main/java/org/apache/gravitino/cli/outputs/TableFormat.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.cli.outputs;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.apache.gravitino.Catalog;
+import org.apache.gravitino.Metalake;
+
+/** Table format to print a pretty table to standard out. */
+public class TableFormat {
+ public static void output(Object object) {
+ if (object instanceof Metalake) {
+ new MetalakeTableFormat().output((Metalake) object);
+ } else if (object instanceof Metalake[]) {
+ new MetalakesTableFormat().output((Metalake[]) object);
+ } else if (object instanceof Catalog) {
+ new CatalogTableFormat().output((Catalog) object);
+ } else {
+ throw new IllegalArgumentException("Unsupported object type");
+ }
+ }
+
+ static final class MetalakeTableFormat implements OutputFormat<Metalake> {
+ @Override
+ public void output(Metalake metalake) {
+ List<String> headers = Arrays.asList("metalake", "comment");
+ List<List<String>> rows = new ArrayList<>();
+ rows.add(Arrays.asList(metalake.name(), metalake.comment()));
+ TableFormatImpl tableFormat = new TableFormatImpl();
+ tableFormat.print(headers, rows);
+ }
+ }
+
+ static final class MetalakesTableFormat implements OutputFormat<Metalake[]> {
+ @Override
+ public void output(Metalake[] metalakes) {
+ List<String> headers = Collections.singletonList("metalake");
+ List<List<String>> rows = new ArrayList<>();
+ for (int i = 0; i < metalakes.length; i++) {
+ rows.add(Arrays.asList(metalakes[i].name()));
+ }
+ TableFormatImpl tableFormat = new TableFormatImpl();
+ tableFormat.print(headers, rows);
+ }
+ }
+
+ static final class CatalogTableFormat implements OutputFormat<Catalog> {
+ @Override
+ public void output(Catalog catalog) {
+ List<String> headers = Arrays.asList("catalog", "type", "provider",
"comment");
+ List<List<String>> rows = new ArrayList<>();
+ rows.add(
+ Arrays.asList(
+ catalog.name(),
+ catalog.type().toString(),
+ catalog.provider(),
+ catalog.comment() + ""));
+ TableFormatImpl tableFormat = new TableFormatImpl();
+ tableFormat.print(headers, rows);
+ }
+ }
+
+ static final class TableFormatImpl {
+ private int[] maxElementLengths;
+ private final String horizontalDelimiter = "-";
+ private final String verticalDelimiter = "|";
+ private final String crossDelimiter = "+";
+ private final String indent = " ";
+
+ public void debug() {
+ System.out.println();
+ Arrays.stream(maxElementLengths).forEach(e -> System.out.print(e + " "));
+ }
+
+ public void print(List<String> headers, List<List<String>> rows) {
+ if (rows.size() > 0 && headers.size() != rows.get(0).size()) {
+ throw new IllegalArgumentException("Number of columns is not equal.");
+ }
+ maxElementLengths = new int[headers.size()];
+ updateMaxLengthsFromList(headers);
+ updateMaxLengthsFromNestedList(rows);
+
+ // print headers
+ printLine();
+ System.out.println();
+ for (int i = 0; i < headers.size(); ++i) {
+ System.out.printf(
+ verticalDelimiter + indent + "%-" + maxElementLengths[i] + "s" +
indent,
+ headers.get(i));
+ }
+ System.out.println(verticalDelimiter);
+ printLine();
+ System.out.println();
+
+ // print rows
+ for (int i = 0; i < rows.size(); ++i) {
+ List<String> columns = rows.get(i);
+ for (int j = 0; j < columns.size(); ++j) {
+ System.out.printf(
+ verticalDelimiter + indent + "%-" + maxElementLengths[j] + "s" +
indent,
+ columns.get(j));
+ }
+ System.out.println(verticalDelimiter);
+ }
+ printLine();
+ // add one more line
+ System.out.println("");
+ }
+
+ private void updateMaxLengthsFromList(List<String> elements) {
+ String s;
+ for (int i = 0; i < elements.size(); ++i) {
+ s = elements.get(i);
+ if (s.length() > maxElementLengths[i]) maxElementLengths[i] =
s.length();
+ }
+ }
+
+ private void updateMaxLengthsFromNestedList(List<List<String>> elements) {
+ for (List<String> row : elements) {
+ String s;
+ for (int i = 0; i < row.size(); ++i) {
+ s = row.get(i);
+ if (s.length() > maxElementLengths[i]) maxElementLengths[i] =
s.length();
+ }
+ }
+ }
+
+ private void printLine() {
+ System.out.print(crossDelimiter);
+ for (int i = 0; i < maxElementLengths.length; ++i) {
+ for (int j = 0; j < maxElementLengths[i] + indent.length() * 2; ++j) {
+ System.out.print(horizontalDelimiter);
+ }
+ System.out.print(crossDelimiter);
+ }
+ }
+ }
+}
diff --git
a/clients/cli/src/test/java/org/apache/gravitino/cli/TestCatalogCommands.java
b/clients/cli/src/test/java/org/apache/gravitino/cli/TestCatalogCommands.java
index c7a24b7a3..c337a342c 100644
---
a/clients/cli/src/test/java/org/apache/gravitino/cli/TestCatalogCommands.java
+++
b/clients/cli/src/test/java/org/apache/gravitino/cli/TestCatalogCommands.java
@@ -81,7 +81,8 @@ class TestCatalogCommands {
mockCommandLine, mockOptions, CommandEntities.CATALOG,
CommandActions.DETAILS));
doReturn(mockDetails)
.when(commandLine)
- .newCatalogDetails(GravitinoCommandLine.DEFAULT_URL, false,
"metalake_demo", "catalog");
+ .newCatalogDetails(
+ GravitinoCommandLine.DEFAULT_URL, false, "metalake_demo",
"catalog", null);
commandLine.handleCommandLine();
verify(mockDetails).handle();
}
diff --git
a/clients/cli/src/test/java/org/apache/gravitino/cli/TestMetalakeCommands.java
b/clients/cli/src/test/java/org/apache/gravitino/cli/TestMetalakeCommands.java
index d602d56d9..2b94a80a9 100644
---
a/clients/cli/src/test/java/org/apache/gravitino/cli/TestMetalakeCommands.java
+++
b/clients/cli/src/test/java/org/apache/gravitino/cli/TestMetalakeCommands.java
@@ -59,7 +59,9 @@ class TestMetalakeCommands {
spy(
new GravitinoCommandLine(
mockCommandLine, mockOptions, CommandEntities.METALAKE,
CommandActions.LIST));
-
doReturn(mockList).when(commandLine).newListMetalakes(GravitinoCommandLine.DEFAULT_URL,
false);
+ doReturn(mockList)
+ .when(commandLine)
+ .newListMetalakes(GravitinoCommandLine.DEFAULT_URL, false, null);
commandLine.handleCommandLine();
verify(mockList).handle();
}
@@ -76,7 +78,7 @@ class TestMetalakeCommands {
mockCommandLine, mockOptions, CommandEntities.METALAKE,
CommandActions.DETAILS));
doReturn(mockDetails)
.when(commandLine)
- .newMetalakeDetails(GravitinoCommandLine.DEFAULT_URL, false,
"metalake_demo");
+ .newMetalakeDetails(GravitinoCommandLine.DEFAULT_URL, false,
"metalake_demo", null);
commandLine.handleCommandLine();
verify(mockDetails).handle();
}
diff --git
a/clients/cli/src/test/java/org/apache/gravitino/cli/integration/test/TableFormatOutputIT.java
b/clients/cli/src/test/java/org/apache/gravitino/cli/integration/test/TableFormatOutputIT.java
new file mode 100644
index 000000000..55cab8c5b
--- /dev/null
+++
b/clients/cli/src/test/java/org/apache/gravitino/cli/integration/test/TableFormatOutputIT.java
@@ -0,0 +1,166 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.cli.integration.test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
+import org.apache.gravitino.cli.GravitinoOptions;
+import org.apache.gravitino.cli.Main;
+import org.apache.gravitino.integration.test.util.BaseIT;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+public class TableFormatOutputIT extends BaseIT {
+ private String gravitinoUrl;
+
+ @BeforeAll
+ public void startUp() {
+ gravitinoUrl = String.format("http://127.0.0.1:%d",
getGravitinoServerPort());
+ String[] create_metalake_args = {
+ "metalake",
+ "create",
+ commandArg(GravitinoOptions.METALAKE),
+ "my_metalake",
+ commandArg(GravitinoOptions.COMMENT),
+ "my metalake",
+ commandArg(GravitinoOptions.URL),
+ gravitinoUrl
+ };
+ Main.main(create_metalake_args);
+
+ String[] create_catalog_args = {
+ "catalog",
+ "create",
+ commandArg(GravitinoOptions.METALAKE),
+ "my_metalake",
+ commandArg(GravitinoOptions.NAME),
+ "postgres",
+ commandArg(GravitinoOptions.PROVIDER),
+ "postgres",
+ commandArg(GravitinoOptions.PROPERTIES),
+
"jdbc-url=jdbc:postgresql://postgresql-host/mydb,jdbc-user=user,jdbc-password=password,jdbc-database=db,jdbc-driver=org.postgresql.Driver",
+ commandArg(GravitinoOptions.URL),
+ gravitinoUrl
+ };
+ Main.main(create_catalog_args);
+ }
+
+ @Test
+ public void testMetalakeListCommand() {
+ // Create a byte array output stream to capture the output of the command
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ PrintStream originalOut = System.out;
+ System.setOut(new PrintStream(outputStream));
+
+ String[] args = {
+ "metalake",
+ "list",
+ commandArg(GravitinoOptions.OUTPUT),
+ "table",
+ commandArg(GravitinoOptions.URL),
+ gravitinoUrl
+ };
+ Main.main(args);
+
+ // Restore the original System.out
+ System.setOut(originalOut);
+ // Get the output and verify it
+ String output = new String(outputStream.toByteArray(),
StandardCharsets.UTF_8).trim();
+ assertEquals(
+ "+-------------+\n"
+ + "| metalake |\n"
+ + "+-------------+\n"
+ + "| my_metalake |\n"
+ + "+-------------+",
+ output);
+ }
+
+ @Test
+ public void testMetalakeDetailsCommand() {
+ // Create a byte array output stream to capture the output of the command
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ PrintStream originalOut = System.out;
+ System.setOut(new PrintStream(outputStream));
+
+ String[] args = {
+ "metalake",
+ "details",
+ commandArg(GravitinoOptions.METALAKE),
+ "my_metalake",
+ commandArg(GravitinoOptions.OUTPUT),
+ "table",
+ commandArg(GravitinoOptions.URL),
+ gravitinoUrl
+ };
+ Main.main(args);
+
+ // Restore the original System.out
+ System.setOut(originalOut);
+ // Get the output and verify it
+ String output = new String(outputStream.toByteArray(),
StandardCharsets.UTF_8).trim();
+ assertEquals(
+ "+-------------+-------------+\n"
+ + "| metalake | comment |\n"
+ + "+-------------+-------------+\n"
+ + "| my_metalake | my metalake |\n"
+ + "+-------------+-------------+",
+ output);
+ }
+
+ @Test
+ public void testCatalogDetailsCommand() {
+ // Create a byte array output stream to capture the output of the command
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ PrintStream originalOut = System.out;
+ System.setOut(new PrintStream(outputStream));
+
+ String[] args = {
+ "catalog",
+ "details",
+ commandArg(GravitinoOptions.METALAKE),
+ "my_metalake",
+ commandArg(GravitinoOptions.NAME),
+ "postgres",
+ commandArg(GravitinoOptions.OUTPUT),
+ "table",
+ commandArg(GravitinoOptions.URL),
+ gravitinoUrl
+ };
+ Main.main(args);
+
+ // Restore the original System.out
+ System.setOut(originalOut);
+ // Get the output and verify it
+ String output = new String(outputStream.toByteArray(),
StandardCharsets.UTF_8).trim();
+ assertEquals(
+ "+----------+------------+-----------------+---------+\n"
+ + "| catalog | type | provider | comment |\n"
+ + "+----------+------------+-----------------+---------+\n"
+ + "| postgres | RELATIONAL | jdbc-postgresql | null |\n"
+ + "+----------+------------+-----------------+---------+",
+ output);
+ }
+
+ private String commandArg(String arg) {
+ return String.format("--%s", arg);
+ }
+}