sfc-gh-pvillard commented on code in PR #10716:
URL: https://github.com/apache/nifi/pull/10716#discussion_r2676850094


##########
nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/test/java/org/apache/nifi/processors/box/ConsumeBoxEventsTest.java:
##########
@@ -77,9 +78,7 @@ void testCaptureEvents() {
 
         final String content = ff0.getContent();
         assertTrue(content.contains("\"id\":\"1\""));
-        assertTrue(content.contains("\"eventType\":\"ITEM_CREATE\""));
         assertTrue(content.contains("\"id\":\"2\""));
-        assertTrue(content.contains("\"eventType\":\"ITEM_TRASH\""));

Review Comment:
   done



##########
nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/main/java/org/apache/nifi/processors/box/BoxEventJsonArrayWriter.java:
##########
@@ -16,86 +16,215 @@
  */
 package org.apache.nifi.processors.box;
 
-import com.box.sdk.BoxEvent;
-import com.eclipsesource.json.Json;
-import com.eclipsesource.json.JsonObject;
+import com.box.sdkgen.schemas.event.Event;
+import com.box.sdkgen.schemas.eventsource.EventSource;
+import com.box.sdkgen.schemas.eventsourceresource.EventSourceResource;
+import com.box.sdkgen.schemas.file.File;
+import com.box.sdkgen.schemas.folder.Folder;
+import com.box.sdkgen.schemas.foldermini.FolderMini;
+import com.box.sdkgen.schemas.user.User;
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.ObjectMapper;
 
 import java.io.Closeable;
 import java.io.IOException;
 import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.Writer;
-import java.util.Objects;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
+import java.util.Map;
 
 /**
- * A class responsible for writing {@link BoxEvent} objects into a JSON array.
+ * A class responsible for writing {@link Event} objects into a JSON array.
  * Not thread-safe.
  */
 final class BoxEventJsonArrayWriter implements Closeable {
 
-    private final Writer writer;
+    private static final JsonFactory JSON_FACTORY = new JsonFactory();
+    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+    private final JsonGenerator generator;
     private boolean hasBegun;
-    private boolean hasEntries;
     private boolean closed;
 
-    private BoxEventJsonArrayWriter(final Writer writer) {
-        this.writer = writer;
+    private BoxEventJsonArrayWriter(final JsonGenerator generator) {
+        this.generator = generator;
         this.hasBegun = false;
-        this.hasEntries = false;
         this.closed = false;
     }
 
     static BoxEventJsonArrayWriter create(final OutputStream outputStream) 
throws IOException {
-        final Writer writer = new OutputStreamWriter(outputStream, UTF_8);
-        return new BoxEventJsonArrayWriter(writer);
+        final JsonGenerator generator = 
JSON_FACTORY.createGenerator(outputStream);
+        return new BoxEventJsonArrayWriter(generator);
     }
 
-    void write(final BoxEvent event) throws IOException {
+    void write(final Event event) throws IOException {
         if (closed) {
             throw new IOException("The Writer is closed");
         }
 
         if (!hasBegun) {
-            beginArray();
+            generator.writeStartArray();
             hasBegun = true;
         }
 
-        if (hasEntries) {
-            writer.write(',');
+        writeEvent(event);
+    }
+
+    private void writeEvent(final Event event) throws IOException {
+        generator.writeStartObject();
+
+        // Map Event fields to JSON using camelCase to match the original NiFi 
Box processor format
+        writeStringField("createdAt", event.getCreatedAt() != null ? 
event.getCreatedAt().toString() : null);
+        writeStringField("recordedAt", event.getRecordedAt() != null ? 
event.getRecordedAt().toString() : null);
+        writeStringField("eventType", event.getEventType() != null ? 
event.getEventType().getValue() : null);
+        writeStringField("id", event.getEventId());
+        writeStringField("sessionID", event.getSessionId());
+        writeStringField("type", event.getType());
+
+        // Handle createdBy if present (camelCase for field name, but inner 
fields match Box API)
+        if (event.getCreatedBy() != null) {
+            generator.writeObjectFieldStart("createdBy");
+            writeStringField("id", event.getCreatedBy().getId());
+            writeStringField("type", event.getCreatedBy().getType() != null ? 
event.getCreatedBy().getType().getValue() : null);
+            writeStringField("name", event.getCreatedBy().getName());
+            writeStringField("login", event.getCreatedBy().getLogin());
+            generator.writeEndObject();
+        } else {
+            generator.writeNullField("createdBy");
         }
 
-        final JsonObject json = toRecord(event);
-        json.writeTo(writer);
+        // Handle source if present - use snake_case for inner fields to match 
Box API format
+        writeSource(event.getSource());
+
+        // Handle additionalDetails if present - serialize as proper JSON 
object
+        writeAdditionalDetails(event.getAdditionalDetails());
 
-        hasEntries = true;
+        generator.writeEndObject();
     }
 
-    private JsonObject toRecord(final BoxEvent event) {
-        final JsonObject json = Json.object();
-
-        json.add("accessibleBy", event.getAccessibleBy() == null ? Json.NULL : 
Json.parse(event.getAccessibleBy().getJson()));
-        json.add("actionBy", event.getActionBy() == null ? Json.NULL : 
Json.parse(event.getActionBy().getJson()));
-        json.add("additionalDetails", 
Objects.requireNonNullElse(event.getAdditionalDetails(), Json.NULL));
-        json.add("createdAt", event.getCreatedAt() == null ? Json.NULL : 
Json.value(event.getCreatedAt().toString()));
-        json.add("createdBy", event.getCreatedBy() == null ? Json.NULL : 
Json.parse(event.getCreatedBy().getJson()));
-        json.add("eventType", event.getEventType() == null ? Json.NULL : 
Json.value(event.getEventType().name()));
-        json.add("id", Objects.requireNonNullElse(Json.value(event.getID()), 
Json.NULL));
-        json.add("ipAddress", 
Objects.requireNonNullElse(Json.value(event.getIPAddress()), Json.NULL));
-        json.add("sessionID", 
Objects.requireNonNullElse(Json.value(event.getSessionID()), Json.NULL));
-        json.add("source", Objects.requireNonNullElse(event.getSourceJSON(), 
Json.NULL));
-        json.add("typeName", 
Objects.requireNonNullElse(Json.value(event.getTypeName()), Json.NULL));
-
-        return json;
+    private void writeSource(final EventSourceResource source) throws 
IOException {
+        if (source == null) {
+            generator.writeNullField("source");
+            return;
+        }
+
+        generator.writeObjectFieldStart("source");
+        try {
+            // EventSourceResource is a union type (OneOfSix) - check what 
kind of source we have
+            if (source.isFile()) {
+                // File source - contains file_id, file_name, and parent 
folder info
+                File file = source.getFile();
+                writeStringField("item_type", "file");
+                writeStringField("item_id", file.getId());
+                writeStringField("item_name", file.getName());
+                // Add file-specific fields for collaboration events
+                writeStringField("file_id", file.getId());
+                writeStringField("file_name", file.getName());
+                // Add parent folder info if available
+                FolderMini parent = file.getParent();
+                if (parent != null) {
+                    writeStringField("folder_id", parent.getId());
+                    writeStringField("folder_name", parent.getName());
+                }
+            } else if (source.isFolder()) {
+                // Folder source - contains folder_id, folder_name
+                Folder folder = source.getFolder();
+                writeStringField("item_type", "folder");
+                writeStringField("item_id", folder.getId());
+                writeStringField("item_name", folder.getName());
+                // Add folder-specific fields for collaboration events
+                writeStringField("folder_id", folder.getId());
+                writeStringField("folder_name", folder.getName());
+            } else if (source.isEventSource()) {
+                // Generic EventSource - has item_type, item_id, item_name
+                EventSource eventSource = source.getEventSource();
+                String itemType = eventSource.getItemType() != null ? 
eventSource.getItemType().getValue() : null;
+                writeStringField("item_type", itemType);
+                writeStringField("item_id", eventSource.getItemId());
+                writeStringField("item_name", eventSource.getItemName());
+                // For EventSource, also populate file/folder specific fields 
based on item_type
+                if ("file".equals(itemType)) {
+                    writeStringField("file_id", eventSource.getItemId());
+                    writeStringField("file_name", eventSource.getItemName());
+                } else if ("folder".equals(itemType)) {
+                    writeStringField("folder_id", eventSource.getItemId());
+                    writeStringField("folder_name", eventSource.getItemName());
+                }
+                // Add parent folder info if available
+                FolderMini parent = eventSource.getParent();
+                if (parent != null) {
+                    writeStringField("parent_id", parent.getId());
+                    writeStringField("parent_name", parent.getName());
+                }
+            } else if (source.isUser()) {
+                // User source
+                User user = source.getUser();
+                writeStringField("item_type", "user");
+                writeStringField("id", user.getId());
+                writeStringField("name", user.getName());
+                writeStringField("login", user.getLogin());
+            } else if (source.isMap()) {
+                // Generic map - write all entries
+                Map<String, Object> map = source.getMap();
+                for (Map.Entry<String, Object> entry : map.entrySet()) {
+                    Object value = entry.getValue();
+                    if (value != null) {
+                        generator.writeFieldName(entry.getKey());
+                        generator.writeObject(value);
+                    }
+                }
+            } else if (source.isAppItemEventSource()) {
+                // AppItemEventSource
+                writeStringField("item_type", "app_item");
+            } else {
+                writeStringField("item_type", "unknown");
+            }
+        } catch (Exception e) {
+            writeStringField("error", "Could not serialize source: " + 
e.getMessage());
+        }
+        generator.writeEndObject();
     }
 
-    private void beginArray() throws IOException {
-        writer.write('[');
+    private void writeAdditionalDetails(final Map<String, Object> 
additionalDetails) throws IOException {
+        if (additionalDetails == null) {
+            generator.writeNullField("additionalDetails");
+            return;
+        }
+
+        try {
+            // Write additionalDetails as a proper JSON object, not a string
+            generator.writeFieldName("additionalDetails");
+            generator.writeStartObject();
+            for (Map.Entry<String, Object> entry : 
additionalDetails.entrySet()) {
+                String key = entry.getKey();
+                Object value = entry.getValue();
+                if (value == null) {
+                    generator.writeNullField(key);
+                } else if (value instanceof String) {

Review Comment:
   done



##########
nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/main/java/org/apache/nifi/processors/box/BoxFileUtils.java:
##########
@@ -16,46 +16,75 @@
  */
 package org.apache.nifi.processors.box;
 
-import com.box.sdk.BoxFile;
-import com.box.sdk.BoxFolder;
-import com.box.sdk.BoxItem;
+import com.box.sdkgen.schemas.file.File;
+import com.box.sdkgen.schemas.folder.Folder;
+import com.box.sdkgen.schemas.foldermini.FolderMini;
 import org.apache.nifi.flowfile.attributes.CoreAttributes;
 
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 import static java.lang.String.valueOf;
-import static java.util.stream.Collectors.joining;
 
 public final class BoxFileUtils {
 
     public static final String BOX_URL = "https://app.box.com/file/";;
 
-    public static String getParentIds(final BoxItem.Info info) {
-        return info.getPathCollection().stream()
-                .map(BoxItem.Info::getID)
-                .collect(joining(","));
+    public static String getParentIds(final File fileInfo) {
+        if (fileInfo.getPathCollection() == null || 
fileInfo.getPathCollection().getEntries() == null) {
+            return "";
+        }
+        return fileInfo.getPathCollection().getEntries().stream()
+                .map(FolderMini::getId)
+                .collect(Collectors.joining(","));
     }
-    public static String getParentPath(BoxItem.Info info) {
-        return "/" + info.getPathCollection().stream()
-                .filter(pathItemInfo -> !pathItemInfo.getID().equals("0"))
-                .map(BoxItem.Info::getName)
-                .collect(joining("/"));
+
+    public static String getParentPath(final File fileInfo) {
+        if (fileInfo.getPathCollection() == null || 
fileInfo.getPathCollection().getEntries() == null) {
+            return "/";
+        }
+        return "/" + fileInfo.getPathCollection().getEntries().stream()
+                .filter(pathItem -> !pathItem.getId().equals("0"))
+                .map(FolderMini::getName)
+                .collect(Collectors.joining("/"));

Review Comment:
   done



##########
nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/main/java/org/apache/nifi/processors/box/CreateBoxFileMetadataInstance.java:
##########
@@ -281,12 +280,13 @@ private boolean isArray(final RecordFieldType fieldType) {
     }
 
     /**
-     * Returns a BoxFile object for the given file ID.
+     * Creates metadata for a file.
      *
-     * @param fileId The ID of the file.
-     * @return A BoxFile object for the given file ID.
+     * @param fileId         The ID of the file.
+     * @param templateKey    The template key of the metadata.
+     * @param metadataValues The metadata key-value pairs.
      */
-    BoxFile getBoxFile(final String fileId) {
-        return new BoxFile(boxAPIConnection, fileId);
+    void createFileMetadata(final String fileId, final String templateKey, 
final Map<String, Object> metadataValues) {
+        boxClient.getFileMetadata().createFileMetadataById(fileId, 
CreateFileMetadataByIdScope.ENTERPRISE, templateKey, metadataValues);

Review Comment:
   done



##########
nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/main/java/org/apache/nifi/processors/box/DeleteBoxFileMetadataInstance.java:
##########
@@ -177,12 +177,12 @@ public void onTrigger(final ProcessContext context, final 
ProcessSession session
     }
 
     /**
-     * Returns a BoxFile object for the given file ID.
+     * Deletes metadata from a file.
      *
-     * @param fileId The ID of the file.
-     * @return A BoxFile object for the given file ID.
+     * @param fileId      The ID of the file.
+     * @param templateKey The template key of the metadata to delete.
      */
-    BoxFile getBoxFile(final String fileId) {
-        return new BoxFile(boxAPIConnection, fileId);
+    void deleteFileMetadata(final String fileId, final String templateKey) {
+        boxClient.getFileMetadata().deleteFileMetadataById(fileId, 
DeleteFileMetadataByIdScope.ENTERPRISE, templateKey);

Review Comment:
   done



##########
nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/main/java/org/apache/nifi/processors/box/UpdateBoxFileMetadataInstance.java:
##########
@@ -236,106 +242,116 @@ private Map<String, Object> readDesiredState(final 
ProcessSession session,
         return desiredState;
     }
 
-    private void updateMetadata(final Metadata metadata,
-                                final Map<String, Object> desiredState) {
-        final List<String> currentKeys = metadata.getPropertyPaths();
+    private List<UpdateFileMetadataByIdRequestBody> 
buildUpdateOperations(final MetadataFull currentMetadata,
+                                                                          
final Map<String, Object> desiredState) {
+        final List<UpdateFileMetadataByIdRequestBody> operations = new 
ArrayList<>();
 
-        // Remove fields not in desired state
-        for (final String propertyPath : currentKeys) {
-            final String fieldName = propertyPath.substring(1); // Remove 
leading '/'
+        // Get current field names from extra data
+        final Set<String> currentKeys = new HashSet<>();
+        final Map<String, Object> extraData = currentMetadata.getExtraData();
+        if (extraData != null) {
+            currentKeys.addAll(extraData.keySet());
+        }
 
+        // Remove fields not in desired state
+        for (final String fieldName : currentKeys) {
             if (!desiredState.containsKey(fieldName)) {
-                metadata.remove(propertyPath);
+                final String path = "/" + fieldName;
                 getLogger().debug("Removing metadata field: {}", fieldName);
+                operations.add(new UpdateFileMetadataByIdRequestBody.Builder()
+                        .op(UpdateFileMetadataByIdRequestBodyOpField.REMOVE)
+                        .path(path)
+                        .build());
             }
         }
 
         // Add or update fields
         for (final Map.Entry<String, Object> entry : desiredState.entrySet()) {
             final String fieldName = entry.getKey();
             final Object value = entry.getValue();
-            final String propertyPath = "/" + fieldName;
+            final String path = "/" + fieldName;
+            final boolean exists = currentKeys.contains(fieldName);
 
-            updateField(metadata, propertyPath, value, 
currentKeys.contains(propertyPath));
+            final UpdateFileMetadataByIdRequestBody operation = 
buildFieldOperation(path, value, exists, extraData);
+            if (operation != null) {
+                operations.add(operation);
+            }
         }
+
+        return operations;
     }
 
-    private void updateField(final Metadata metadata,
-                             final String propertyPath,
-                             final Object value,
-                             final boolean exists) {
+    private UpdateFileMetadataByIdRequestBody buildFieldOperation(final String 
path,
+                                                                  final Object 
value,
+                                                                  final 
boolean exists,
+                                                                  final 
Map<String, Object> extraData) {
         if (value == null) {
-            throw new IllegalArgumentException("Null value found for property 
path: " + propertyPath);
+            throw new IllegalArgumentException("Null value found for property 
path: " + path);
         }
 
-        if (exists) {
-            final Object currentValue = metadata.getValue(propertyPath);
-
-            // Only update if values are different
+        // If exists, check if values are different
+        if (exists && extraData != null) {
+            final String fieldName = path.substring(1);
+            final Object currentValue = extraData.get(fieldName);
             if (Objects.equals(currentValue, value)) {
-                return;
+                return null; // No change needed
             }
+        }
+
+        final MetadataInstanceValue metadataValue = 
convertToMetadataInstanceValue(value, path);
+
+        // Box API uses replace for both adding new fields and updating 
existing fields
+        return new UpdateFileMetadataByIdRequestBody.Builder()
+                .op(UpdateFileMetadataByIdRequestBodyOpField.REPLACE)
+                .path(path)
+                .value(metadataValue)
+                .build();
+    }
 
-            // Update
-            switch (value) {
-                case Number n -> metadata.replace(propertyPath, 
n.doubleValue());
-                case List<?> l -> metadata.replace(propertyPath, 
convertListToStringList(l, propertyPath));
-                case LocalDate d -> metadata.replace(propertyPath, 
BoxDate.of(d).format());
-                default -> metadata.replace(propertyPath, value.toString());
+    private MetadataInstanceValue convertToMetadataInstanceValue(final Object 
value, final String path) {
+        if (value instanceof Number n) {
+            if (value instanceof Double || value instanceof Float) {
+                return new MetadataInstanceValue(n.doubleValue());
+            } else {
+                return new MetadataInstanceValue(n.longValue());
             }
+        } else if (value instanceof List<?> l) {
+            final List<String> stringList = l.stream()
+                    .map(obj -> {
+                        if (obj == null) {
+                            throw new IllegalArgumentException("Null value 
found in list for field: " + path);
+                        }
+                        return obj.toString();
+                    })
+                    .collect(Collectors.toList());
+            return new MetadataInstanceValue(stringList);
+        } else if (value instanceof LocalDate d) {
+            return new MetadataInstanceValue(BoxDate.of(d).format());
         } else {
-            // Add new field
-            switch (value) {
-                case Number n -> metadata.add(propertyPath, n.doubleValue());
-                case List<?> l -> metadata.add(propertyPath, 
convertListToStringList(l, propertyPath));
-                case LocalDate d -> metadata.add(propertyPath, 
BoxDate.of(d).format());
-                default -> metadata.add(propertyPath, value.toString());
-            }
+            return new MetadataInstanceValue(value.toString());
         }
     }
 
-    private List<String> convertListToStringList(final List<?> list,
-                                                 final String fieldName) {
-        return list.stream()
-                .map(obj -> {
-                    if (obj == null) {
-                        throw new IllegalArgumentException("Null value found 
in list for field: " + fieldName);
-                    }
-                    return obj.toString();
-                })
-                .collect(Collectors.toList());
-    }
-
     /**
      * Retrieves the metadata for a Box file.
      * Visible for testing purposes.
      *
-     * @param boxFile     The Box file to retrieve metadata from.
+     * @param fileId      The ID of the file.
      * @param templateKey The key of the metadata template.
      * @return The metadata for the Box file.
      */
-    Metadata getMetadata(final BoxFile boxFile,
-                         final String templateKey) {
-        return boxFile.getMetadata(templateKey);
-    }
-
-    /**
-     * Returns a BoxFile object for the given file ID.
-     *
-     * @param fileId The ID of the file.
-     * @return A BoxFile object for the given file ID.
-     */
-    BoxFile getBoxFile(final String fileId) {
-        return new BoxFile(boxAPIConnection, fileId);
+    MetadataFull getMetadata(final String fileId, final String templateKey) {
+        return boxClient.getFileMetadata().getFileMetadataById(fileId, 
GetFileMetadataByIdScope.ENTERPRISE, templateKey);

Review Comment:
   done



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