Skip to content

Commit 4ffe56c

Browse files
authored
[#6481] improve(CLI): Refactor table output format (#6483)
### What changes were proposed in this pull request? Refactor table output format, make it easier to test and scale. ### Why are the changes needed? Fix: #6481 ### Does this PR introduce _any_ user-facing change? No ### How was this patch tested? table format test ```bash gcli metalake list -i --output table +-------------------+ | Metalake | +-------------------+ | demo | | cli_demo | | demo_metalake | | test_cli_metalake | | tyy | | demo3 | +-------------------+ gcli metalake details -i --output table -m demo_metalake +---------------+-------------+ | Metalake | Comment | +---------------+-------------+ | demo_metalake | new comment | +---------------+-------------+ gcli catalog list -i --output table -m demo_metalake +-------------------+ | Catalog | +-------------------+ | File | | Hive_catalog | | Iceberg_catalog | | Mysql_catalog | | Test_hive_catalog | +-------------------+ gcli catalog details --name Hive_catalog -i --output table -m demo_metalake +--------------+------------+----------+-------------+ | Catalog | Type | Provider | Comment | +--------------+------------+----------+-------------+ | Hive_catalog | RELATIONAL | hive | new comment | +--------------+------------+----------+-------------+ ``` plainformat test ```bash gcli metalake list -i # demo # cli_demo # demo_metalake # test_cli_metalake # demo3 gcli metalake details -i -m demo_metalake # demo_metalake, new comment gcli catalog list -i -m demo_metalake # File # Hive_catalog # Iceberg_catalog # Mysql_catalog # Test_hive_catalog gcli catalog details --name Hive_catalog -i -m demo_metalake # Hive_catalog, RELATIONAL, hive, new comment ```
1 parent 02a20b7 commit 4ffe56c

15 files changed

+1652
-216
lines changed

clients/cli/src/main/java/org/apache/gravitino/cli/commands/CatalogDetails.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public void handle() {
5252
try {
5353
GravitinoClient client = buildClient(metalake);
5454
result = client.loadCatalog(catalog);
55-
output(result);
55+
printResults(result);
5656
} catch (NoSuchMetalakeException err) {
5757
exitWithError(ErrorMessages.UNKNOWN_METALAKE);
5858
} catch (NoSuchCatalogException err) {

clients/cli/src/main/java/org/apache/gravitino/cli/commands/Command.java

+17-6
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.apache.gravitino.cli.KerberosData;
3030
import org.apache.gravitino.cli.Main;
3131
import org.apache.gravitino.cli.OAuthData;
32+
import org.apache.gravitino.cli.outputs.BaseOutputFormat;
3233
import org.apache.gravitino.cli.outputs.PlainFormat;
3334
import org.apache.gravitino.cli.outputs.TableFormat;
3435
import org.apache.gravitino.client.DefaultOAuth2TokenProvider;
@@ -89,16 +90,26 @@ public void printInformation(String message) {
8990
return;
9091
}
9192

92-
System.out.print(message);
93+
printResults(message);
9394
}
9495

9596
/**
96-
* Prints out an a results of a command.
97+
* Outputs the entity result to the console.
98+
*
99+
* @param entity The entity to output.
100+
* @param <T> The type of entity.
101+
*/
102+
public <T> void printResults(T entity) {
103+
output(entity);
104+
}
105+
106+
/**
107+
* Prints out the string result of a command.
97108
*
98109
* @param results The results to display.
99110
*/
100111
public void printResults(String results) {
101-
System.out.print(results);
112+
BaseOutputFormat.output(results, System.out);
102113
}
103114

104115
/**
@@ -226,14 +237,14 @@ protected <T extends GravitinoClientBase> Builder<T> constructClient(Builder<T>
226237
*/
227238
protected <T> void output(T entity) {
228239
if (outputFormat == null) {
229-
PlainFormat.output(entity);
240+
PlainFormat.output(entity, context);
230241
return;
231242
}
232243

233244
if (outputFormat.equals(OUTPUT_FORMAT_TABLE)) {
234-
TableFormat.output(entity);
245+
TableFormat.output(entity, context);
235246
} else if (outputFormat.equals(OUTPUT_FORMAT_PLAIN)) {
236-
PlainFormat.output(entity);
247+
PlainFormat.output(entity, context);
237248
} else {
238249
throw new IllegalArgumentException("Unsupported output format");
239250
}

clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListCatalogs.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,11 @@ public void handle() {
4848
try {
4949
GravitinoClient client = buildClient(metalake);
5050
catalogs = client.listCatalogsInfo();
51-
output(catalogs);
51+
if (catalogs.length == 0) {
52+
printInformation("No catalogs exist.");
53+
return;
54+
}
55+
printResults(catalogs);
5256
} catch (NoSuchMetalakeException err) {
5357
exitWithError(ErrorMessages.UNKNOWN_METALAKE);
5458
} catch (Exception exp) {

clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListMetalakes.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@ public void handle() {
4242
try {
4343
GravitinoAdminClient client = buildAdminClient();
4444
metalakes = client.listMetalakes();
45-
output(metalakes);
45+
if (metalakes.length == 0) {
46+
printInformation("No metalakes exist.");
47+
return;
48+
}
49+
printResults(metalakes);
4650
} catch (Exception exp) {
4751
exitWithError(exp.getMessage());
4852
}

clients/cli/src/main/java/org/apache/gravitino/cli/commands/MetalakeDetails.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public void handle() {
4646
try {
4747
GravitinoClient client = buildClient(metalake);
4848
Metalake metalakeEntity = client.loadMetalake(metalake);
49-
output(metalakeEntity);
49+
printResults(metalakeEntity);
5050
} catch (NoSuchMetalakeException err) {
5151
exitWithError(ErrorMessages.UNKNOWN_METALAKE);
5252
} catch (Exception exp) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.gravitino.cli.outputs;
21+
22+
import java.io.BufferedOutputStream;
23+
import java.io.IOException;
24+
import java.io.OutputStream;
25+
import java.io.PrintStream;
26+
import java.io.UncheckedIOException;
27+
import java.nio.charset.StandardCharsets;
28+
import org.apache.gravitino.cli.CommandContext;
29+
30+
/**
31+
* Abstract base implementation of {@link OutputFormat} interface providing common functionality for
32+
* various output format implementations.
33+
*/
34+
public abstract class BaseOutputFormat<T> implements OutputFormat<T> {
35+
protected CommandContext context;
36+
37+
/**
38+
* Creates a new {@link BaseOutputFormat} with specified configuration.
39+
*
40+
* @param context the command context, must not be null;
41+
*/
42+
public BaseOutputFormat(CommandContext context) {
43+
this.context = context;
44+
}
45+
46+
/**
47+
* Outputs a message to the specified OutputStream. This method handles both system streams
48+
* ({@code System.out}, {@code System.err}) and regular output streams differently: - For system
49+
* streams: Preserves the stream open after writing - For other streams: Automatically closes the
50+
* stream after writing
51+
*
52+
* @param message the message to output, must not be null
53+
* @param os the output stream to write to, must not be null If this is {@code System.out} or
54+
* {@code System.err}, the stream will not be closed
55+
* @throws IllegalArgumentException if either message or os is null
56+
* @throws UncheckedIOException if an I/O error occurs during writing
57+
*/
58+
public static void output(String message, OutputStream os) {
59+
if (message == null || os == null) {
60+
throw new IllegalArgumentException(
61+
"Message and OutputStream cannot be null, message: " + message + ", os: " + os);
62+
}
63+
boolean isSystemStream = (os == System.out || os == System.err);
64+
65+
try {
66+
PrintStream printStream =
67+
new PrintStream(
68+
isSystemStream ? os : new BufferedOutputStream(os),
69+
true,
70+
StandardCharsets.UTF_8.name());
71+
72+
try {
73+
printStream.println(message);
74+
printStream.flush();
75+
} finally {
76+
if (!isSystemStream) {
77+
printStream.close();
78+
}
79+
}
80+
} catch (IOException e) {
81+
throw new UncheckedIOException("Failed to write message to output stream", e);
82+
}
83+
}
84+
85+
/**
86+
* {@inheritDoc} This implementation checks the quiet flag and handles null output gracefully. If
87+
* quiet mode is enabled, no output is produced.
88+
*/
89+
@Override
90+
public void output(T entity) {
91+
String outputMessage = getOutput(entity);
92+
String output = outputMessage == null ? "" : outputMessage;
93+
output(output, System.out);
94+
}
95+
}

0 commit comments

Comments
 (0)