This is an automated email from the ASF dual-hosted git repository.

amoghj pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg.git


The following commit(s) were added to refs/heads/main by this push:
     new 7ad11b2df1 Core: Snapshot `summary` map must have `operation` key 
(#11354)
7ad11b2df1 is described below

commit 7ad11b2df1a266d29f9e4f6bb5b499cb68c0afb7
Author: Kevin Liu <[email protected]>
AuthorDate: Fri Oct 25 17:23:44 2024 -0400

    Core: Snapshot `summary` map must have `operation` key (#11354)
    
    Co-authored-by: Eduard Tudenhoefner <[email protected]>
---
 .../java/org/apache/iceberg/SnapshotParser.java    |  5 +-
 .../java/org/apache/iceberg/TestSnapshotJson.java  | 77 ++++++++++++++++++++++
 2 files changed, 79 insertions(+), 3 deletions(-)

diff --git a/core/src/main/java/org/apache/iceberg/SnapshotParser.java 
b/core/src/main/java/org/apache/iceberg/SnapshotParser.java
index bc5ef60946..41b8e1499c 100644
--- a/core/src/main/java/org/apache/iceberg/SnapshotParser.java
+++ b/core/src/main/java/org/apache/iceberg/SnapshotParser.java
@@ -129,13 +129,12 @@ public class SnapshotParser {
           "Cannot parse summary from non-object value: %s",
           sNode);
 
+      operation = JsonUtil.getString(OPERATION, sNode);
       ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
       Iterator<String> fields = sNode.fieldNames();
       while (fields.hasNext()) {
         String field = fields.next();
-        if (field.equals(OPERATION)) {
-          operation = JsonUtil.getString(OPERATION, sNode);
-        } else {
+        if (!field.equals(OPERATION)) {
           builder.put(field, JsonUtil.getString(field, sNode));
         }
       }
diff --git a/core/src/test/java/org/apache/iceberg/TestSnapshotJson.java 
b/core/src/test/java/org/apache/iceberg/TestSnapshotJson.java
index ee12390749..7fff5c5ddd 100644
--- a/core/src/test/java/org/apache/iceberg/TestSnapshotJson.java
+++ b/core/src/test/java/org/apache/iceberg/TestSnapshotJson.java
@@ -20,11 +20,16 @@ package org.apache.iceberg;
 
 import static org.apache.iceberg.Files.localInput;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Path;
 import java.util.List;
+import java.util.Map;
 import org.apache.iceberg.relocated.com.google.common.collect.ImmutableList;
 import org.apache.iceberg.relocated.com.google.common.collect.ImmutableMap;
 import org.junit.jupiter.api.Test;
@@ -35,6 +40,56 @@ public class TestSnapshotJson {
 
   public TableOperations ops = new LocalTableOperations(temp);
 
+  @Test
+  public void testToJsonWithoutOperation() throws IOException {
+    int snapshotId = 23;
+    Long parentId = null;
+    String manifestList = createManifestListWithManifestFiles(snapshotId, 
parentId);
+
+    Snapshot expected =
+        new BaseSnapshot(
+            0, snapshotId, parentId, System.currentTimeMillis(), null, null, 
1, manifestList);
+    String json = SnapshotParser.toJson(expected);
+
+    // Assert that summary field is not present in the JSON
+    assertThat(new ObjectMapper().readTree(json)).anyMatch(node -> 
!node.has("summary"));
+  }
+
+  @Test
+  public void testToJsonWithOperation() throws IOException {
+    long parentId = 1;
+    long id = 2;
+
+    String manifestList = createManifestListWithManifestFiles(id, parentId);
+
+    Snapshot expected =
+        new BaseSnapshot(
+            0,
+            id,
+            parentId,
+            System.currentTimeMillis(),
+            DataOperations.REPLACE,
+            ImmutableMap.of("files-added", "4", "files-deleted", "100"),
+            3,
+            manifestList);
+    Map<String, String> expectedSummary =
+        ImmutableMap.<String, String>builder()
+            .putAll(expected.summary())
+            .put("operation", expected.operation()) // operation should be 
part of the summary
+            .build();
+
+    String json = SnapshotParser.toJson(expected);
+    ObjectMapper objectMapper = new ObjectMapper();
+    JsonNode jsonNode = objectMapper.readTree(json);
+
+    assertThat(jsonNode.get("summary")).isNotNull();
+
+    Map<String, String> actualSummary =
+        objectMapper.convertValue(
+            jsonNode.get("summary"), new TypeReference<Map<String, String>>() 
{});
+    assertThat(actualSummary).isEqualTo(expectedSummary);
+  }
+
   @Test
   public void testJsonConversion() throws IOException {
     int snapshotId = 23;
@@ -159,6 +214,28 @@ public class TestSnapshotJson {
     assertThat(snapshot.schemaId()).isEqualTo(expected.schemaId());
   }
 
+  @Test
+  public void testJsonConversionSummaryWithoutOperationFails() {
+    String json =
+        String.format(
+            "{\n"
+                + "  \"snapshot-id\" : 2,\n"
+                + "  \"parent-snapshot-id\" : 1,\n"
+                + "  \"timestamp-ms\" : %s,\n"
+                + "  \"summary\" : {\n"
+                + "    \"files-added\" : \"4\",\n"
+                + "    \"files-deleted\" : \"100\"\n"
+                + "  },\n"
+                + "  \"manifests\" : [ \"/tmp/manifest1.avro\", 
\"/tmp/manifest2.avro\" ],\n"
+                + "  \"schema-id\" : 3\n"
+                + "}",
+            System.currentTimeMillis());
+
+    assertThatThrownBy(() -> SnapshotParser.fromJson(json))
+        .isInstanceOf(IllegalArgumentException.class)
+        .hasMessage("Cannot parse missing string: operation");
+  }
+
   private String createManifestListWithManifestFiles(long snapshotId, Long 
parentSnapshotId)
       throws IOException {
     File manifestList = File.createTempFile("manifests", null, temp.toFile());

Reply via email to