Copilot commented on code in PR #10296:
URL: https://github.com/apache/gravitino/pull/10296#discussion_r3022459152


##########
clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListModelVersionProperties.java:
##########
@@ -0,0 +1,122 @@
+/*
+ * 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.commands;
+
+import java.util.Map;
+import javax.annotation.Nullable;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.cli.CommandContext;
+import org.apache.gravitino.cli.ErrorMessages;
+import org.apache.gravitino.client.GravitinoClient;
+import org.apache.gravitino.exceptions.NoSuchCatalogException;
+import org.apache.gravitino.exceptions.NoSuchMetalakeException;
+import org.apache.gravitino.exceptions.NoSuchModelException;
+import org.apache.gravitino.exceptions.NoSuchModelVersionException;
+import org.apache.gravitino.exceptions.NoSuchSchemaException;
+import org.apache.gravitino.model.ModelCatalog;
+import org.apache.gravitino.model.ModelVersion;
+
+/** List the properties of a model version. */
+public class ListModelVersionProperties extends ListProperties {
+
+  /** The name of the metalake. */
+  protected final String metalake;
+  /** The name of the catalog. */
+  protected final String catalog;
+  /** The name of the schema. */
+  protected final String schema;
+  /** The name of the model. */
+  protected final String model;
+  /** The version number of the model version. */
+  @Nullable protected final Integer version;
+  /** The alias of the model version. */
+  @Nullable protected final String alias;
+
+  /**
+   * List the properties of a model version.
+   *
+   * @param context The command context.
+   * @param metalake The name of the metalake.
+   * @param catalog The name of the catalog.
+   * @param schema The name of the schema.
+   * @param model The name of the model.
+   * @param version The version number of the model version.
+   * @param alias The alias of the model version.
+   */
+  public ListModelVersionProperties(
+      CommandContext context,
+      String metalake,
+      String catalog,
+      String schema,
+      String model,
+      @Nullable Integer version,
+      @Nullable String alias) {
+    super(context);
+    this.metalake = metalake;
+    this.catalog = catalog;
+    this.schema = schema;
+    this.model = model;
+    this.version = version;
+    this.alias = alias;
+  }
+
+  /** List the properties of a model version. */
+  @Override
+  public void handle() {
+    ModelVersion gModelVersion = null;
+    try {
+      NameIdentifier name = NameIdentifier.of(schema, model);
+      GravitinoClient client = buildClient(metalake);
+      ModelCatalog modelCatalog = client.loadCatalog(catalog).asModelCatalog();
+      if (version != null) {
+        gModelVersion = modelCatalog.getModelVersion(name, version);
+      } else {
+        gModelVersion = modelCatalog.getModelVersion(name, alias);
+      }
+    } catch (NoSuchMetalakeException err) {
+      exitWithError(ErrorMessages.UNKNOWN_METALAKE);
+    } catch (NoSuchCatalogException err) {
+      exitWithError(ErrorMessages.UNKNOWN_CATALOG);
+    } catch (NoSuchSchemaException err) {
+      exitWithError(ErrorMessages.UNKNOWN_SCHEMA);
+    } catch (NoSuchModelException err) {
+      exitWithError(ErrorMessages.UNKNOWN_MODEL);
+    } catch (NoSuchModelVersionException err) {
+      exitWithError(ErrorMessages.UNKNOWN_MODEL_VERSION);
+    } catch (Exception exp) {
+      exitWithError(exp.getMessage());
+    }
+
+    Map<String, String> properties = gModelVersion.properties();
+    printProperties(properties);
+  }

Review Comment:
   `handle()` can call `getModelVersion(name, alias)` with a null `alias` when 
neither `--version` nor `--alias` is provided (e.g., if `validate()` is not 
invoked). This can lead to a misleading error path and/or a 
`NullPointerException`, and `gModelVersion` can remain null before 
`gModelVersion.properties()` is called. Make `handle()` enforce the invariants 
by calling `validate()` first (or explicitly checking `version/alias` at the 
start and `exitWithError(...)`), and ensure execution cannot proceed past an 
error (e.g., return after `exitWithError`).



##########
clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListModelVersionProperties.java:
##########
@@ -0,0 +1,122 @@
+/*
+ * 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.commands;
+
+import java.util.Map;
+import javax.annotation.Nullable;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.cli.CommandContext;
+import org.apache.gravitino.cli.ErrorMessages;
+import org.apache.gravitino.client.GravitinoClient;
+import org.apache.gravitino.exceptions.NoSuchCatalogException;
+import org.apache.gravitino.exceptions.NoSuchMetalakeException;
+import org.apache.gravitino.exceptions.NoSuchModelException;
+import org.apache.gravitino.exceptions.NoSuchModelVersionException;
+import org.apache.gravitino.exceptions.NoSuchSchemaException;
+import org.apache.gravitino.model.ModelCatalog;
+import org.apache.gravitino.model.ModelVersion;
+
+/** List the properties of a model version. */
+public class ListModelVersionProperties extends ListProperties {
+
+  /** The name of the metalake. */
+  protected final String metalake;
+  /** The name of the catalog. */
+  protected final String catalog;
+  /** The name of the schema. */
+  protected final String schema;
+  /** The name of the model. */
+  protected final String model;
+  /** The version number of the model version. */
+  @Nullable protected final Integer version;
+  /** The alias of the model version. */
+  @Nullable protected final String alias;
+
+  /**
+   * List the properties of a model version.
+   *
+   * @param context The command context.
+   * @param metalake The name of the metalake.
+   * @param catalog The name of the catalog.
+   * @param schema The name of the schema.
+   * @param model The name of the model.
+   * @param version The version number of the model version.
+   * @param alias The alias of the model version.
+   */
+  public ListModelVersionProperties(
+      CommandContext context,
+      String metalake,
+      String catalog,
+      String schema,
+      String model,
+      @Nullable Integer version,
+      @Nullable String alias) {
+    super(context);
+    this.metalake = metalake;
+    this.catalog = catalog;
+    this.schema = schema;
+    this.model = model;
+    this.version = version;
+    this.alias = alias;
+  }
+
+  /** List the properties of a model version. */
+  @Override
+  public void handle() {
+    ModelVersion gModelVersion = null;
+    try {
+      NameIdentifier name = NameIdentifier.of(schema, model);
+      GravitinoClient client = buildClient(metalake);
+      ModelCatalog modelCatalog = client.loadCatalog(catalog).asModelCatalog();
+      if (version != null) {
+        gModelVersion = modelCatalog.getModelVersion(name, version);
+      } else {
+        gModelVersion = modelCatalog.getModelVersion(name, alias);
+      }
+    } catch (NoSuchMetalakeException err) {
+      exitWithError(ErrorMessages.UNKNOWN_METALAKE);
+    } catch (NoSuchCatalogException err) {
+      exitWithError(ErrorMessages.UNKNOWN_CATALOG);
+    } catch (NoSuchSchemaException err) {
+      exitWithError(ErrorMessages.UNKNOWN_SCHEMA);
+    } catch (NoSuchModelException err) {
+      exitWithError(ErrorMessages.UNKNOWN_MODEL);
+    } catch (NoSuchModelVersionException err) {
+      exitWithError(ErrorMessages.UNKNOWN_MODEL_VERSION);
+    } catch (Exception exp) {
+      exitWithError(exp.getMessage());
+    }
+
+    Map<String, String> properties = gModelVersion.properties();
+    printProperties(properties);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public Command validate() {
+    if (alias != null && version != null) {
+      exitWithError("Cannot specify both alias and version");
+    }
+    if (alias == null && version == null) {
+      exitWithError("Either alias or version must be specified");
+    }
+    return this;
+  }

Review Comment:
   The new validation errors are hard-coded strings, while other error paths in 
this command use `ErrorMessages` constants. To keep CLI output consistent (and 
easier to test/localize), consider adding constants (e.g., in `ErrorMessages`) 
and using them here, ideally matching existing phrasing used by other 
`*Version*` commands.



##########
clients/cli/src/test/java/org/apache/gravitino/cli/TestModelCommands.java:
##########
@@ -152,6 +154,120 @@ void testListModelCommandWithoutSchema() {
         output);
   }
 
+  @Test
+  void testListModelPropertiesCommand() {
+    ListModelProperties mockList = mock(ListModelProperties.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.model");
+
+    GravitinoCommandLine commandLine =
+        spy(
+            new GravitinoCommandLine(
+                mockCommandLine, mockOptions, CommandEntities.MODEL, 
CommandActions.PROPERTIES));
+
+    doReturn(mockList)
+        .when(commandLine)
+        .newListModelProperties(
+            any(CommandContext.class),
+            eq("metalake_demo"),
+            eq("catalog"),
+            eq("schema"),
+            eq("model"));
+    doReturn(mockList).when(mockList).validate();
+    commandLine.handleCommandLine();
+    verify(mockList).handle();
+  }
+
+  @Test
+  void testListModelPropertiesCommandWithoutModel() {
+    Main.useExit = false;
+    
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);

Review Comment:
   This test mutates the global `Main.useExit` flag but does not restore it. 
That can leak state into subsequent tests in the same JVM and cause 
order-dependent failures. Please restore the previous value in a `finally` 
block within the test, or move this setup into a class-level 
`@BeforeEach/@AfterEach` that consistently resets `Main.useExit`.



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

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to