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


##########
lance/lance-rest-server/src/main/java/org/apache/gravitino/lance/service/rest/LanceNamespaceOperations.java:
##########
@@ -84,6 +100,19 @@ public Response listNamespaces(
   public Response describeNamespace(
       @PathParam("id") String namespaceId,
       @DefaultValue(NAMESPACE_DELIMITER_DEFAULT) @QueryParam("delimiter") 
String delimiter) {
+    return describeNamespaceInternal(namespaceId, delimiter);
+  }
+
+  @POST
+  @Path("/describe")
+  @Timed(name = "describe-namespaces-root." + 
MetricNames.HTTP_PROCESS_DURATION, absolute = true)
+  @ResponseMetered(name = "describe-namespaces-root", absolute = true)
+  public Response describeNamespaceOnRoot(
+      @DefaultValue(NAMESPACE_DELIMITER_DEFAULT) @QueryParam("delimiter") 
String delimiter) {
+    return describeNamespaceInternal(delimiter, delimiter);
+  }

Review Comment:
   `describeNamespaceOnRoot` calls `describeNamespaceInternal(delimiter, 
delimiter)`, which uses the delimiter string as a fake namespace id. This is 
fragile and makes the error `instance` misleading. Prefer using an explicit 
root id (e.g., empty string) and let downstream validation produce the expected 
error for missing namespace id.



##########
lance/lance-rest-server/src/main/java/org/apache/gravitino/lance/service/rest/LanceNamespaceOperations.java:
##########
@@ -66,6 +66,22 @@ public Response listNamespaces(
       @DefaultValue(NAMESPACE_DELIMITER_DEFAULT) @QueryParam("delimiter") 
String delimiter,
       @QueryParam("page_token") String pageToken,
       @QueryParam("limit") Integer limit) {
+    return listNamespacesInternal(namespaceId, delimiter, pageToken, limit);
+  }
+
+  @GET
+  @Path("/list")
+  @Timed(name = "list-namespaces-root." + MetricNames.HTTP_PROCESS_DURATION, 
absolute = true)
+  @ResponseMetered(name = "list-namespaces-root", absolute = true)
+  public Response listNamespacesOnRoot(
+      @DefaultValue(NAMESPACE_DELIMITER_DEFAULT) @QueryParam("delimiter") 
String delimiter,
+      @QueryParam("page_token") String pageToken,
+      @QueryParam("limit") Integer limit) {
+    return listNamespacesInternal(delimiter, delimiter, pageToken, limit);
+  }

Review Comment:
   `listNamespacesOnRoot` passes the delimiter value as the `namespaceId` 
placeholder (`listNamespacesInternal(delimiter, delimiter, ...)`). This couples 
root-list behavior to the chosen delimiter and also produces confusing 
`instance` values in error responses. Use an explicit root identifier (e.g., 
empty string) when listing from root, and keep `delimiter` only for splitting.



##########
lance/lance-rest-server/src/main/java/org/apache/gravitino/lance/service/rest/LanceNamespaceOperations.java:
##########
@@ -66,6 +66,22 @@ public Response listNamespaces(
       @DefaultValue(NAMESPACE_DELIMITER_DEFAULT) @QueryParam("delimiter") 
String delimiter,
       @QueryParam("page_token") String pageToken,
       @QueryParam("limit") Integer limit) {
+    return listNamespacesInternal(namespaceId, delimiter, pageToken, limit);
+  }
+
+  @GET
+  @Path("/list")
+  @Timed(name = "list-namespaces-root." + MetricNames.HTTP_PROCESS_DURATION, 
absolute = true)
+  @ResponseMetered(name = "list-namespaces-root", absolute = true)
+  public Response listNamespacesOnRoot(
+      @DefaultValue(NAMESPACE_DELIMITER_DEFAULT) @QueryParam("delimiter") 
String delimiter,
+      @QueryParam("page_token") String pageToken,
+      @QueryParam("limit") Integer limit) {
+    return listNamespacesInternal(delimiter, delimiter, pageToken, limit);

Review Comment:
   New endpoints `/v1/namespace/list` and `/v1/namespace/describe` were added, 
but there are no unit tests covering these routes (existing tests only cover 
`/{id}/list` and `/{id}/describe`). Please add Jersey tests exercising the root 
routes (success path for `/list`, and expected error behavior for `/describe`) 
to avoid regressions.



##########
lance/lance-rest-server/src/main/java/org/apache/gravitino/lance/service/rest/LanceTableOperations.java:
##########
@@ -337,13 +341,24 @@ private void 
validateAlterColumnsRequest(AlterTableAlterColumnsRequest request)
     Preconditions.checkArgument(
         !request.getAlterations().isEmpty(), "Columns to alter cannot be 
empty.");
 
-    for (ColumnAlteration alteration : request.getAlterations()) {
+    for (AlterColumnsEntry alteration : request.getAlterations()) {
       Preconditions.checkArgument(
-          StringUtils.isBlank(alteration.getCastTo()),
-          "Only RENAME alteration is supported currently.");
+          StringUtils.isNotBlank(alteration.getPath()), "Column path to alter 
cannot be empty.");
       Preconditions.checkArgument(
           StringUtils.isNotBlank(alteration.getRename()),
-          "Rename field must be specified when castTo is not provided.");
+          "Only RENAME alteration is supported currently.");
+      Preconditions.checkArgument(
+          alteration.getDataType() == null
+              && alteration.getNullable() == null
+              && alteration.getVirtualColumn() == null,
+          "Only RENAME alteration is supported currently.");

Review Comment:
   In `validateAlterColumnsRequest`, the precondition that `rename` must be 
non-blank throws the message "Only RENAME alteration is supported currently.", 
which is misleading when the request is missing `rename`. Consider using an 
error message that directly states `rename` is required, and keep the separate 
validation that rejects non-rename fields.



##########
lance/lance-rest-server/src/main/java/org/apache/gravitino/lance/service/rest/LanceTableOperations.java:
##########
@@ -147,10 +152,9 @@ public Response createEmptyTable(
       validateCreateEmptyTableRequest(request);
 
       String tableLocation = request.getLocation();
-      Map<String, String> props =
-          request.getProperties() == null
-              ? Maps.newHashMap()
-              : Maps.newHashMap(request.getProperties());
+      MultivaluedMap<String, String> headersMap = headers.getRequestHeaders();
+      String tableProperties = 
headersMap.getFirst(LANCE_TABLE_PROPERTIES_PREFIX_HEADER);
+      Map<String, String> props = 
SerializationUtils.deserializeProperties(tableProperties);
 
       CreateEmptyTableResponse response =
           lanceNamespace.asTableOps().createEmptyTable(tableId, delimiter, 
tableLocation, props);

Review Comment:
   `createEmptyTable` no longer reads properties from the JSON request body and 
instead only deserializes them from the `x-lance-table-properties` header. This 
breaks backward compatibility with existing clients/docs that send `properties` 
in the body, and it also makes the endpoint inconsistent with its declared JSON 
request model. Preserve compatibility by accepting request-body properties when 
present (and optionally merging with header-provided properties with a clear 
precedence).



##########
docs/lance-rest-service.md:
##########
@@ -312,26 +312,26 @@ Map<String, String> props = new HashMap<>();
 props.put(RestNamespaceConfig.URI, "http://localhost:9101/lance";);
 props.put(RestNamespaceConfig.DELIMITER, 
RestNamespaceConfig.DELIMITER_DEFAULT);

Review Comment:
   The Java example still uses `RestNamespaceConfig.URI/DELIMITER` to populate 
connection properties, but the rest of this PR (and the upgraded Lance SDK) 
appears to have moved to string keys like `"uri"`/`"delimiter"` (see 
integration test usage). Please update the example so it compiles and uses the 
correct config keys/classes for `org.lance:lance-namespace-core:0.4.5`.
   ```suggestion
   props.put("uri", "http://localhost:9101/lance";);
   props.put("delimiter", ".");
   ```



##########
lance/lance-rest-server/src/test/java/org/apache/gravitino/lance/service/rest/TestLanceNamespaceOperations.java:
##########
@@ -903,8 +878,7 @@ void testAlterColumns() {
     Mockito.reset(tableOps);
     when(tableOps.alterTable(any(), any(), 
any(AlterTableAlterColumnsRequest.class)))
         .thenThrow(
-            LanceNamespaceException.notFound(
-                "Table not found", "NoSuchTableException", tableIds, ""));
+            new org.lance.namespace.errors.TableNotFoundException("Table not 
found", "", tableIds));
     resp =

Review Comment:
   Avoid using a fully qualified class name inside the test method body. Import 
`org.lance.namespace.errors.TableNotFoundException` and use the simple name to 
match the project's Java import/style guidelines.



##########
lance/lance-rest-server/src/test/java/org/apache/gravitino/lance/service/rest/TestLanceNamespaceOperations.java:
##########
@@ -749,9 +730,7 @@ void testDropTable() {
     Assertions.assertEquals(dropTableResponse.getLocation(), 
response.getLocation());
 
     // test throw exception
-    doThrow(
-            LanceNamespaceException.notFound(
-                "Table not found", "NoSuchTableException", tableIds, ""))
+    doThrow(new org.lance.namespace.errors.TableNotFoundException("Table not 
found", "", tableIds))
         .when(tableOps)
         .dropTable(any(), any());

Review Comment:
   Avoid using a fully qualified class name inside the test method body. Import 
`org.lance.namespace.errors.TableNotFoundException` and use the simple name to 
match the project's Java import/style guidelines.



##########
lance/lance-common/src/main/java/org/apache/gravitino/lance/common/utils/SerializationUtils.java:
##########
@@ -35,19 +35,19 @@ private SerializationUtils() {
   // see 
https://github.com/lancedb/lance-namespace/blob/2033b2fca126e87e56ba0d5ec19c5ec010c7a98f/
   // 
java/lance-namespace-core/src/main/java/com/lancedb/lance/namespace/rest/RestNamespace.java#L207-L208
   public static Map<String, String> deserializeProperties(String 
serializedProperties) {
-    return StringUtils.isBlank(serializedProperties)
-        ? ImmutableMap.of()
-        : JsonUtil.parse(
-            serializedProperties,
-            jsonNode -> {
-              Map<String, String> map = new HashMap<>();
-              jsonNode
-                  .fields()
-                  .forEachRemaining(
-                      entry -> {
-                        map.put(entry.getKey(), entry.getValue().asText());
-                      });
-              return map;
-            });
+    if (StringUtils.isBlank(serializedProperties)) {
+      return new HashMap<>();
+    }
+
+    try {
+      Map<String, Object> rawProperties =
+          JsonUtils.anyFieldMapper()
+              .readValue(serializedProperties, new TypeReference<Map<String, 
Object>>() {});
+      Map<String, String> deserializedProperties = new HashMap<>();
+      rawProperties.forEach((k, v) -> deserializedProperties.put(k, 
String.valueOf(v)));
+      return deserializedProperties;
+    } catch (Exception e) {
+      throw new IllegalArgumentException("Failed to deserialize table 
properties JSON", e);
+    }

Review Comment:
   `deserializeProperties` catches a generic `Exception`. Per project 
guidelines, prefer catching the specific Jackson exceptions thrown by 
`readValue` (e.g., `IOException` / `JsonProcessingException`) so that 
unexpected runtime errors are not inadvertently wrapped as input errors.



##########
lance/lance-rest-server/src/test/java/org/apache/gravitino/lance/service/rest/TestLanceNamespaceOperations.java:
##########
@@ -595,8 +583,7 @@ void testDeregisterTable() {
     Mockito.reset(tableOps);
     when(tableOps.deregisterTable(any(), any()))
         .thenThrow(
-            LanceNamespaceException.notFound(
-                "Table not found", "NoSuchTableException", tableIds, ""));
+            new org.lance.namespace.errors.TableNotFoundException("Table not 
found", "", tableIds));
     resp =

Review Comment:
   Avoid using a fully qualified class name inside the test method body. Import 
`org.lance.namespace.errors.TableNotFoundException` and use the simple name to 
match the project's Java import/style guidelines.



##########
lance/lance-rest-server/src/test/java/org/apache/gravitino/lance/service/rest/TestLanceNamespaceOperations.java:
##########
@@ -850,8 +826,7 @@ void testDropColumns() {
     Mockito.reset(tableOps);
     when(tableOps.alterTable(any(), any(), 
any(AlterTableDropColumnsRequest.class)))
         .thenThrow(
-            LanceNamespaceException.notFound(
-                "Table not found", "NoSuchTableException", tableIds, ""));
+            new org.lance.namespace.errors.TableNotFoundException("Table not 
found", "", tableIds));
     resp =

Review Comment:
   Avoid using a fully qualified class name inside the test method body. Import 
`org.lance.namespace.errors.TableNotFoundException` and use the simple name to 
match the project's Java import/style guidelines.



##########
lance/lance-rest-server/src/test/java/org/apache/gravitino/lance/service/rest/TestLanceNamespaceOperations.java:
##########
@@ -644,14 +630,13 @@ void testDescribeTable() {
     Assertions.assertEquals(MediaType.APPLICATION_JSON_TYPE, 
resp.getMediaType());
     DescribeTableResponse response = 
resp.readEntity(DescribeTableResponse.class);
     Assertions.assertEquals(createTableResponse.getLocation(), 
response.getLocation());
-    Assertions.assertEquals(createTableResponse.getProperties(), 
response.getProperties());
+    Assertions.assertEquals(createTableResponse.getMetadata(), 
response.getMetadata());
 
     // Test not found exception
     Mockito.reset(tableOps);
     when(tableOps.describeTable(any(), any(), any()))
         .thenThrow(
-            LanceNamespaceException.notFound(
-                "Table not found", "NoSuchTableException", tableIds, ""));
+            new org.lance.namespace.errors.TableNotFoundException("Table not 
found", "", tableIds));
     resp =

Review Comment:
   Avoid using a fully qualified class name inside the test method body. Import 
`org.lance.namespace.errors.TableNotFoundException` and use the simple name to 
match the project's Java import/style guidelines.



##########
lance/lance-rest-server/src/test/java/org/apache/gravitino/lance/service/rest/TestLanceNamespaceOperations.java:
##########
@@ -693,9 +677,7 @@ void testTableExists() {
     Assertions.assertEquals(Response.Status.OK.getStatusCode(), 
resp.getStatus());
 
     // test throw exception
-    doThrow(
-            LanceNamespaceException.notFound(
-                "Table not found", "NoSuchTableException", tableIds, ""))
+    doThrow(new org.lance.namespace.errors.TableNotFoundException("Table not 
found", "", tableIds))
         .when(tableOps)
         .tableExists(any(), any());

Review Comment:
   Avoid using a fully qualified class name inside the test method body. Import 
`org.lance.namespace.errors.TableNotFoundException` and use the simple name to 
match the project's Java import/style guidelines.



-- 
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