Skip to content

Commit af9c2b4

Browse files
authored
[#6496] feat(CLI): Support plain format output for Schema and Table command (#6497)
### What changes were proposed in this pull request? Support plain format output for Schema and Table command ### Why are the changes needed? Fix: #6496 ### Does this PR introduce _any_ user-facing change? No ### How was this patch tested? local test ```bash gcli schema list -m demo_metalake --name Hive_catalog -i default,Default Hive database gcli table list -m demo_metalake --name Hive_catalog.default -i employee_partitions source_table test_dt_partition test_key_en sales order_tb1 tmp float_test test_tbl gcli table details -m demo_metalake --name Hive_catalog.default.test1 -i test1,N/A gcli schema details -m demo_metalake --name Hive_catalog.default -i default,Default Hive database ```
1 parent d36ebac commit af9c2b4

File tree

3 files changed

+209
-19
lines changed

3 files changed

+209
-19
lines changed

clients/cli/src/main/java/org/apache/gravitino/cli/outputs/OutputFormat.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
*/
2727
public interface OutputFormat<T> {
2828
/** Joiner for creating comma-separated output strings, ignoring null values */
29-
Joiner COMMA_JOINER = Joiner.on(", ").skipNulls();
29+
Joiner COMMA_JOINER = Joiner.on(",").skipNulls();
3030
/** Joiner for creating line-separated output strings, ignoring null values */
3131
Joiner NEWLINE_JOINER = Joiner.on(System.lineSeparator()).skipNulls();
3232

clients/cli/src/main/java/org/apache/gravitino/cli/outputs/PlainFormat.java

+105-8
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@
2121
import java.util.Arrays;
2222
import java.util.List;
2323
import java.util.stream.Collectors;
24+
import org.apache.gravitino.Audit;
2425
import org.apache.gravitino.Catalog;
2526
import org.apache.gravitino.Metalake;
27+
import org.apache.gravitino.Schema;
2628
import org.apache.gravitino.cli.CommandContext;
29+
import org.apache.gravitino.rel.Table;
2730

2831
/** Plain format to print a pretty string to standard out. */
2932
public abstract class PlainFormat<T> extends BaseOutputFormat<T> {
@@ -45,6 +48,16 @@ public static void output(Object entity, CommandContext context) {
4548
new CatalogPlainFormat(context).output((Catalog) entity);
4649
} else if (entity instanceof Catalog[]) {
4750
new CatalogListPlainFormat(context).output((Catalog[]) entity);
51+
} else if (entity instanceof Schema) {
52+
new SchemaPlainFormat(context).output((Schema) entity);
53+
} else if (entity instanceof Schema[]) {
54+
new SchemaListPlainFormat(context).output((Schema[]) entity);
55+
} else if (entity instanceof Table) {
56+
new TablePlainFormat(context).output((Table) entity);
57+
} else if (entity instanceof Table[]) {
58+
new TableListPlainFormat(context).output((Table[]) entity);
59+
} else if (entity instanceof Audit) {
60+
new AuditPlainFormat(context).output((Audit) entity);
4861
} else {
4962
throw new IllegalArgumentException("Unsupported object type");
5063
}
@@ -124,14 +137,98 @@ public CatalogListPlainFormat(CommandContext context) {
124137
/** {@inheritDoc} */
125138
@Override
126139
public String getOutput(Catalog[] catalogs) {
127-
if (catalogs == null || catalogs.length == 0) {
128-
output("No catalogs exist.", System.out);
129-
return null;
130-
} else {
131-
List<String> catalogNames =
132-
Arrays.stream(catalogs).map(Catalog::name).collect(Collectors.toList());
133-
return NEWLINE_JOINER.join(catalogNames);
134-
}
140+
141+
List<String> catalogNames =
142+
Arrays.stream(catalogs).map(Catalog::name).collect(Collectors.toList());
143+
return NEWLINE_JOINER.join(catalogNames);
144+
}
145+
}
146+
147+
/**
148+
* Formats a single {@link Schema} instance as a comma-separated string. Output format: name,
149+
* comment
150+
*/
151+
static final class SchemaPlainFormat extends PlainFormat<Schema> {
152+
public SchemaPlainFormat(CommandContext context) {
153+
super(context);
154+
}
155+
156+
/** {@inheritDoc} */
157+
@Override
158+
public String getOutput(Schema schema) {
159+
return COMMA_JOINER.join(schema.name(), schema.comment());
160+
}
161+
}
162+
163+
/**
164+
* Formats an array of Schemas, outputting one name per line. Returns null if the array is empty
165+
* or null.
166+
*/
167+
static final class SchemaListPlainFormat extends PlainFormat<Schema[]> {
168+
public SchemaListPlainFormat(CommandContext context) {
169+
super(context);
170+
}
171+
172+
/** {@inheritDoc} */
173+
@Override
174+
public String getOutput(Schema[] schemas) {
175+
List<String> schemaNames =
176+
Arrays.stream(schemas).map(Schema::name).collect(Collectors.toList());
177+
return NEWLINE_JOINER.join(schemaNames);
178+
}
179+
}
180+
181+
/**
182+
* Formats a single Table instance with detailed column information. Output format: table_name,
183+
* table_comment
184+
*/
185+
static final class TablePlainFormat extends PlainFormat<Table> {
186+
public TablePlainFormat(CommandContext context) {
187+
super(context);
188+
}
189+
190+
/** {@inheritDoc} */
191+
@Override
192+
public String getOutput(Table table) {
193+
String comment = table.comment() == null ? "N/A" : table.comment();
194+
return COMMA_JOINER.join(new String[] {table.name(), comment});
195+
}
196+
}
197+
198+
/**
199+
* Formats an array of Tables, outputting one name per line. Returns null if the array is empty or
200+
* null.
201+
*/
202+
static final class TableListPlainFormat extends PlainFormat<Table[]> {
203+
public TableListPlainFormat(CommandContext context) {
204+
super(context);
205+
}
206+
207+
/** {@inheritDoc} */
208+
@Override
209+
public String getOutput(Table[] tables) {
210+
List<String> tableNames = Arrays.stream(tables).map(Table::name).collect(Collectors.toList());
211+
return NEWLINE_JOINER.join(tableNames);
212+
}
213+
}
214+
215+
/**
216+
* Formats an instance of {@link Audit} , outputting the audit information. Output format:
217+
* creator, create_time, modified, modified_time
218+
*/
219+
static final class AuditPlainFormat extends PlainFormat<Audit> {
220+
public AuditPlainFormat(CommandContext context) {
221+
super(context);
222+
}
223+
224+
/** {@inheritDoc} */
225+
@Override
226+
public String getOutput(Audit audit) {
227+
return COMMA_JOINER.join(
228+
audit.creator(),
229+
audit.createTime() == null ? "N/A" : audit.createTime(),
230+
audit.lastModifier() == null ? "N/A" : audit.lastModifier(),
231+
audit.lastModifiedTime() == null ? "N/A" : audit.lastModifiedTime());
135232
}
136233
}
137234
}

clients/cli/src/test/java/org/apache/gravitino/cli/output/TestPlainFormat.java

+103-10
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@
3333
import org.apache.gravitino.Schema;
3434
import org.apache.gravitino.cli.CommandContext;
3535
import org.apache.gravitino.cli.outputs.PlainFormat;
36+
import org.apache.gravitino.rel.Table;
37+
import org.apache.gravitino.rel.types.Type;
38+
import org.apache.gravitino.rel.types.Types;
3639
import org.junit.jupiter.api.AfterEach;
3740
import org.junit.jupiter.api.Assertions;
3841
import org.junit.jupiter.api.BeforeEach;
@@ -64,7 +67,7 @@ void testMetalakeDetailsWithPlainFormat() {
6467

6568
PlainFormat.output(mockMetalake, mockContext);
6669
String output = new String(outContent.toByteArray(), StandardCharsets.UTF_8).trim();
67-
Assertions.assertEquals("demo_metalake, This is a demo metalake", output);
70+
Assertions.assertEquals("demo_metalake,This is a demo metalake", output);
6871
}
6972

7073
@Test
@@ -85,8 +88,7 @@ void testCatalogDetailsWithPlainFormat() {
8588

8689
PlainFormat.output(mockCatalog, mockContext);
8790
String output = new String(outContent.toByteArray(), StandardCharsets.UTF_8).trim();
88-
Assertions.assertEquals(
89-
"demo_catalog, RELATIONAL, demo_provider, This is a demo catalog", output);
91+
Assertions.assertEquals("demo_catalog,RELATIONAL,demo_provider,This is a demo catalog", output);
9092
}
9193

9294
@Test
@@ -102,6 +104,75 @@ void testListCatalogWithPlainFormat() {
102104
Assertions.assertEquals("catalog1\n" + "catalog2", output);
103105
}
104106

107+
@Test
108+
void testSchemaDetailsWithPlainFormat() {
109+
CommandContext mockContext = getMockContext();
110+
Schema mockSchema = getMockSchema();
111+
PlainFormat.output(mockSchema, mockContext);
112+
String output = new String(outContent.toByteArray(), StandardCharsets.UTF_8).trim();
113+
Assertions.assertEquals("demo_schema,This is a demo schema", output);
114+
}
115+
116+
@Test
117+
void testListSchemaWithPlainFormat() {
118+
CommandContext mockContext = getMockContext();
119+
Schema mockSchema1 = getMockSchema("schema1", "This is a schema");
120+
Schema mockSchema2 = getMockSchema("schema2", "This is another schema");
121+
122+
PlainFormat.output(new Schema[] {mockSchema1, mockSchema2}, mockContext);
123+
String output = new String(outContent.toByteArray(), StandardCharsets.UTF_8).trim();
124+
Assertions.assertEquals("schema1\n" + "schema2", output);
125+
}
126+
127+
@Test
128+
void testTableDetailsWithPlainFormat() {
129+
CommandContext mockContext = getMockContext();
130+
Table mockTable = getMockTable();
131+
PlainFormat.output(mockTable, mockContext);
132+
String output = new String(outContent.toByteArray(), StandardCharsets.UTF_8).trim();
133+
Assertions.assertEquals("demo_table,This is a demo table", output);
134+
}
135+
136+
@Test
137+
void testAuditWithTableFormat() {
138+
CommandContext mockContext = getMockContext();
139+
Audit mockAudit = mock(Audit.class);
140+
when(mockAudit.creator()).thenReturn("demo_user");
141+
when(mockAudit.createTime()).thenReturn(Instant.ofEpochMilli(1611111111111L));
142+
when(mockAudit.lastModifier()).thenReturn("demo_user");
143+
when(mockAudit.lastModifiedTime()).thenReturn(Instant.ofEpochMilli(1611111111111L));
144+
145+
PlainFormat.output(mockAudit, mockContext);
146+
String output = new String(outContent.toByteArray(), StandardCharsets.UTF_8).trim();
147+
Assertions.assertEquals(
148+
"demo_user,2021-01-20T02:51:51.111Z,demo_user,2021-01-20T02:51:51.111Z", output);
149+
}
150+
151+
@Test
152+
void testAuditWithTableFormatWithNullValues() {
153+
CommandContext mockContext = getMockContext();
154+
Audit mockAudit = mock(Audit.class);
155+
when(mockAudit.creator()).thenReturn("demo_user");
156+
when(mockAudit.createTime()).thenReturn(null);
157+
when(mockAudit.lastModifier()).thenReturn(null);
158+
when(mockAudit.lastModifiedTime()).thenReturn(null);
159+
160+
PlainFormat.output(mockAudit, mockContext);
161+
String output = new String(outContent.toByteArray(), StandardCharsets.UTF_8).trim();
162+
Assertions.assertEquals("demo_user,N/A,N/A,N/A", output);
163+
}
164+
165+
@Test
166+
void testListTableWithPlainFormat() {
167+
CommandContext mockContext = getMockContext();
168+
Table mockTable1 = getMockTable("table1", "This is a table");
169+
Table mockTable2 = getMockTable("table2", "This is another table");
170+
171+
PlainFormat.output(new Table[] {mockTable1, mockTable2}, mockContext);
172+
String output = new String(outContent.toByteArray(), StandardCharsets.UTF_8).trim();
173+
Assertions.assertEquals("table1\n" + "table2", output);
174+
}
175+
105176
@Test
106177
void testOutputWithUnsupportType() {
107178
CommandContext mockContext = getMockContext();
@@ -159,13 +230,35 @@ private Schema getMockSchema(String name, String comment) {
159230
return mockSchema;
160231
}
161232

162-
private Audit getMockAudit() {
163-
Audit mockAudit = mock(Audit.class);
164-
when(mockAudit.creator()).thenReturn("demo_user");
165-
when(mockAudit.createTime()).thenReturn(Instant.ofEpochMilli(1611111111111L));
166-
when(mockAudit.lastModifier()).thenReturn("demo_user");
167-
when(mockAudit.lastModifiedTime()).thenReturn(Instant.ofEpochMilli(1611111111111L));
233+
private Table getMockTable() {
234+
return getMockTable("demo_table", "This is a demo table");
235+
}
236+
237+
private Table getMockTable(String name, String comment) {
238+
Table mockTable = mock(Table.class);
239+
org.apache.gravitino.rel.Column mockColumnInt =
240+
getMockColumn("id", Types.IntegerType.get(), "This is a int column", false, true);
241+
org.apache.gravitino.rel.Column mockColumnString =
242+
getMockColumn("name", Types.StringType.get(), "This is a string column", true, false);
243+
244+
when(mockTable.name()).thenReturn(name);
245+
when(mockTable.comment()).thenReturn(comment);
246+
when(mockTable.columns())
247+
.thenReturn(new org.apache.gravitino.rel.Column[] {mockColumnInt, mockColumnString});
248+
249+
return mockTable;
250+
}
251+
252+
private org.apache.gravitino.rel.Column getMockColumn(
253+
String name, Type dataType, String comment, boolean nullable, boolean autoIncrement) {
254+
255+
org.apache.gravitino.rel.Column mockColumn = mock(org.apache.gravitino.rel.Column.class);
256+
when(mockColumn.name()).thenReturn(name);
257+
when(mockColumn.dataType()).thenReturn(dataType);
258+
when(mockColumn.comment()).thenReturn(comment);
259+
when(mockColumn.nullable()).thenReturn(nullable);
260+
when(mockColumn.autoIncrement()).thenReturn(autoIncrement);
168261

169-
return mockAudit;
262+
return mockColumn;
170263
}
171264
}

0 commit comments

Comments
 (0)