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

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


The following commit(s) were added to refs/heads/main by this push:
     new 1e0b195c0c NIFI-14329: Creating processor to fetch Box File Metadata
1e0b195c0c is described below

commit 1e0b195c0c4d4d6fefe270549018e2ac1eb1c380
Author: Noah Cover <[email protected]>
AuthorDate: Wed Mar 5 09:08:10 2025 -0800

    NIFI-14329: Creating processor to fetch Box File Metadata
    
    Signed-off-by: Pierre Villard <[email protected]>
    
    This closes #9772.
---
 .../nifi/processors/box/FetchBoxFileInfo.java      | 228 +++++++++++++++++++++
 .../services/org.apache.nifi.processor.Processor   |   1 +
 .../nifi/processors/box/FetchBoxFileInfoTest.java  | 198 ++++++++++++++++++
 3 files changed, 427 insertions(+)

diff --git 
a/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/main/java/org/apache/nifi/processors/box/FetchBoxFileInfo.java
 
b/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/main/java/org/apache/nifi/processors/box/FetchBoxFileInfo.java
new file mode 100644
index 0000000000..f588042ff3
--- /dev/null
+++ 
b/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/main/java/org/apache/nifi/processors/box/FetchBoxFileInfo.java
@@ -0,0 +1,228 @@
+/*
+ * 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.nifi.processors.box;
+
+import com.box.sdk.BoxAPIConnection;
+import com.box.sdk.BoxAPIException;
+import com.box.sdk.BoxAPIResponseException;
+import com.box.sdk.BoxFile;
+import org.apache.nifi.annotation.behavior.InputRequirement;
+import org.apache.nifi.annotation.behavior.ReadsAttribute;
+import org.apache.nifi.annotation.behavior.WritesAttribute;
+import org.apache.nifi.annotation.behavior.WritesAttributes;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.SeeAlso;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.annotation.lifecycle.OnScheduled;
+import org.apache.nifi.box.controllerservices.BoxClientService;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.expression.ExpressionLanguageScope;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.AbstractProcessor;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.util.StandardValidators;
+import org.jetbrains.annotations.VisibleForTesting;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.apache.nifi.processors.box.BoxFileAttributes.ERROR_CODE;
+import static org.apache.nifi.processors.box.BoxFileAttributes.ERROR_CODE_DESC;
+import static org.apache.nifi.processors.box.BoxFileAttributes.ERROR_MESSAGE;
+import static 
org.apache.nifi.processors.box.BoxFileAttributes.ERROR_MESSAGE_DESC;
+import static org.apache.nifi.processors.box.BoxFileAttributes.FILENAME_DESC;
+import static org.apache.nifi.processors.box.BoxFileAttributes.ID;
+import static org.apache.nifi.processors.box.BoxFileAttributes.ID_DESC;
+import static org.apache.nifi.processors.box.BoxFileAttributes.PATH_DESC;
+import static org.apache.nifi.processors.box.BoxFileAttributes.SIZE;
+import static org.apache.nifi.processors.box.BoxFileAttributes.SIZE_DESC;
+import static org.apache.nifi.processors.box.BoxFileAttributes.TIMESTAMP;
+import static org.apache.nifi.processors.box.BoxFileAttributes.TIMESTAMP_DESC;
+
+@InputRequirement(InputRequirement.Requirement.INPUT_REQUIRED)
+@Tags({"box", "storage", "metadata", "fetch"})
+@CapabilityDescription("Fetches metadata for files from Box and adds it to the 
FlowFile's attributes.")
+@SeeAlso({ListBoxFile.class, FetchBoxFile.class, PutBoxFile.class})
+@ReadsAttribute(attribute = ID, description = ID_DESC)
+@WritesAttributes({
+        @WritesAttribute(attribute = ID, description = ID_DESC),
+        @WritesAttribute(attribute = "filename", description = FILENAME_DESC),
+        @WritesAttribute(attribute = "path", description = PATH_DESC),
+        @WritesAttribute(attribute = SIZE, description = SIZE_DESC),
+        @WritesAttribute(attribute = TIMESTAMP, description = TIMESTAMP_DESC),
+        @WritesAttribute(attribute = "box.created.at", description = "The 
creation date of the file"),
+        @WritesAttribute(attribute = "box.owner", description = "The owner of 
the file"),
+        @WritesAttribute(attribute = "box.description", description = "The 
description of the file"),
+        @WritesAttribute(attribute = "box.etag", description = "The etag of 
the file"),
+        @WritesAttribute(attribute = "box.sha1", description = "The SHA-1 hash 
of the file"),
+        @WritesAttribute(attribute = "box.content.created.at", description = 
"The date the content was created"),
+        @WritesAttribute(attribute = "box.content.modified.at", description = 
"The date the content was modified"),
+        @WritesAttribute(attribute = "box.item.status", description = "The 
status of the file (active, trashed, etc.)"),
+        @WritesAttribute(attribute = "box.sequence_id", description = "The 
sequence ID of the file"),
+        @WritesAttribute(attribute = "box.parent.folder.id", description = 
"The ID of the parent folder"),
+        @WritesAttribute(attribute = "box.trashed.at", description = "The date 
the file was trashed, if applicable"),
+        @WritesAttribute(attribute = "box.purged.at", description = "The date 
the file was purged, if applicable"),
+        @WritesAttribute(attribute = "box.shared.link", description = "The 
shared link of the file, if any"),
+        @WritesAttribute(attribute = ERROR_CODE, description = 
ERROR_CODE_DESC),
+        @WritesAttribute(attribute = ERROR_MESSAGE, description = 
ERROR_MESSAGE_DESC)
+})
+public class FetchBoxFileInfo extends AbstractProcessor {
+
+    public static final PropertyDescriptor FILE_ID = new 
PropertyDescriptor.Builder()
+            .name("File ID")
+            .description("The ID of the File to fetch metadata for")
+            .required(true)
+            .defaultValue("${box.id}")
+            
.expressionLanguageSupported(ExpressionLanguageScope.FLOWFILE_ATTRIBUTES)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    public static final Relationship REL_SUCCESS = new Relationship.Builder()
+            .name("success")
+            .description("A FlowFile will be routed here after successfully 
fetching the file metadata.")
+            .build();
+
+    public static final Relationship REL_FAILURE = new Relationship.Builder()
+            .name("failure")
+            .description("A FlowFile will be routed here if fetching the file 
metadata fails.")
+            .build();
+
+    static final Relationship REL_NOT_FOUND = new Relationship.Builder()
+            .name("not.found")
+            .description("FlowFiles for which the specified Box file was not 
found.")
+            .build();
+
+    public static final Set<Relationship> RELATIONSHIPS = Set.of(
+            REL_SUCCESS,
+            REL_FAILURE,
+            REL_NOT_FOUND
+    );
+
+    private static final List<PropertyDescriptor> PROPERTY_DESCRIPTORS = 
List.of(
+            BoxClientService.BOX_CLIENT_SERVICE,
+            FILE_ID
+    );
+
+    private volatile BoxAPIConnection boxAPIConnection;
+
+    @Override
+    protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
+        return PROPERTY_DESCRIPTORS;
+    }
+
+    @Override
+    public Set<Relationship> getRelationships() {
+        return RELATIONSHIPS;
+    }
+
+    @OnScheduled
+    public void onScheduled(final ProcessContext context) {
+        BoxClientService boxClientService = 
context.getProperty(BoxClientService.BOX_CLIENT_SERVICE).asControllerService(BoxClientService.class);
+        boxAPIConnection = boxClientService.getBoxApiConnection();
+    }
+
+    @Override
+    public void onTrigger(final ProcessContext context, final ProcessSession 
session) throws ProcessException {
+        FlowFile flowFile = session.get();
+        if (flowFile == null) {
+            return;
+        }
+
+        final String fileId = 
context.getProperty(FILE_ID).evaluateAttributeExpressions(flowFile).getValue();
+        try {
+            flowFile = fetchFileMetadata(fileId, session, flowFile);
+            session.transfer(flowFile, REL_SUCCESS);
+        } catch (final BoxAPIResponseException e) {
+            flowFile = session.putAttribute(flowFile, ERROR_MESSAGE, 
e.getMessage());
+            flowFile = session.putAttribute(flowFile, ERROR_CODE, 
String.valueOf(e.getResponseCode()));
+
+            if (e.getResponseCode() == 404) {
+                getLogger().warn("Box file with ID {} was not found.", fileId);
+                session.transfer(flowFile, REL_NOT_FOUND);
+            } else {
+                getLogger().error("Failed to retrieve Box file representation 
for file [{}]", fileId, e);
+                session.transfer(flowFile, REL_FAILURE);
+            }
+        } catch (final BoxAPIException e) {
+            flowFile = session.putAttribute(flowFile, ERROR_MESSAGE, 
e.getMessage());
+            flowFile = session.putAttribute(flowFile, ERROR_CODE, 
String.valueOf(e.getResponseCode()));
+            flowFile = session.penalize(flowFile);
+            session.transfer(flowFile, REL_FAILURE);
+        }
+    }
+
+    /**
+     * Fetches the BoxFile instance for a given file ID. For testing purposes.
+     *
+     * @param fileId the ID of the file
+     * @return BoxFile instance
+     */
+    @VisibleForTesting
+    protected BoxFile getBoxFile(final String fileId) {
+        return new BoxFile(boxAPIConnection, fileId);
+    }
+
+    private FlowFile fetchFileMetadata(final String fileId,
+                                       final ProcessSession session,
+                                       final FlowFile flowFile) {
+        final BoxFile boxFile = getBoxFile(fileId);
+        final BoxFile.Info fileInfo = boxFile.getInfo("name", "description", 
"size", "created_at", "modified_at",
+                "owned_by", "parent", "etag", "sha1", "item_status", 
"sequence_id", "path_collection",
+                "content_created_at", "content_modified_at", "trashed_at", 
"purged_at", "shared_link");
+
+        final Map<String, String> attributes = new 
HashMap<>(BoxFileUtils.createAttributeMap(fileInfo));
+
+        addAttributeIfNotNull(attributes, "box.description", 
fileInfo.getDescription());
+        addAttributeIfNotNull(attributes, "box.etag", fileInfo.getEtag());
+        addAttributeIfNotNull(attributes, "box.sha1", fileInfo.getSha1());
+        addAttributeIfNotNull(attributes, "box.content.created.at", 
fileInfo.getContentCreatedAt());
+        addAttributeIfNotNull(attributes, "box.content.modified.at", 
fileInfo.getContentModifiedAt());
+        addAttributeIfNotNull(attributes, "box.item.status", 
fileInfo.getItemStatus());
+        addAttributeIfNotNull(attributes, "box.sequence.id", 
fileInfo.getSequenceID());
+        addAttributeIfNotNull(attributes, "box.created.at", 
fileInfo.getCreatedAt());
+        addAttributeIfNotNull(attributes, "box.trashed.at", 
fileInfo.getTrashedAt());
+        addAttributeIfNotNull(attributes, "box.purged.at", 
fileInfo.getPurgedAt());
+
+        // Handle special cases
+        if (fileInfo.getOwnedBy() != null && fileInfo.getOwnedBy().getName() 
!= null) {
+            attributes.put("box.owner", fileInfo.getOwnedBy().getName());
+        }
+
+        if (fileInfo.getParent() != null) {
+            attributes.put("box.parent.folder.id", 
fileInfo.getParent().getID());
+        }
+
+        if (fileInfo.getSharedLink() != null && 
fileInfo.getSharedLink().getURL() != null) {
+            attributes.put("box.shared.link", 
fileInfo.getSharedLink().getURL());
+        }
+
+        return session.putAllAttributes(flowFile, attributes);
+    }
+
+    private void addAttributeIfNotNull(final Map<String, String> attributes,
+                                       final String key,
+                                       final Object value) {
+        if (value != null) {
+            attributes.put(key, String.valueOf(value));
+        }
+    }
+}
diff --git 
a/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor
 
b/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor
index 0fe58b2359..2a4163074e 100644
--- 
a/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor
+++ 
b/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor
@@ -14,5 +14,6 @@
 # limitations under the License.
 org.apache.nifi.processors.box.ListBoxFile
 org.apache.nifi.processors.box.FetchBoxFile
+org.apache.nifi.processors.box.FetchBoxFileInfo
 org.apache.nifi.processors.box.PutBoxFile
 org.apache.nifi.processors.box.ConsumeBoxEvents
diff --git 
a/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/test/java/org/apache/nifi/processors/box/FetchBoxFileInfoTest.java
 
b/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/test/java/org/apache/nifi/processors/box/FetchBoxFileInfoTest.java
new file mode 100644
index 0000000000..ebdb4e20a0
--- /dev/null
+++ 
b/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/test/java/org/apache/nifi/processors/box/FetchBoxFileInfoTest.java
@@ -0,0 +1,198 @@
+/*
+ * 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.nifi.processors.box;
+
+import com.box.sdk.BoxAPIException;
+import com.box.sdk.BoxAPIResponseException;
+import com.box.sdk.BoxFile;
+import com.box.sdk.BoxSharedLink;
+import com.box.sdk.BoxUser;
+import org.apache.nifi.util.MockFlowFile;
+import org.apache.nifi.util.TestRunners;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+public class FetchBoxFileInfoTest extends AbstractBoxFileTest {
+    private static final String TEST_DESCRIPTION = "Test file description";
+    private static final String TEST_ETAG = "0";
+    private static final String TEST_SHA1 = 
"da39a3ee5e6b4b0d3255bfef95601890afd80709";
+    private static final String TEST_ITEM_STATUS = "active";
+    private static final String TEST_SEQUENCE_ID = "1";
+    private static final String TEST_OWNER_NAME = "Test User";
+    private static final String TEST_SHARED_LINK_URL = 
"https://app.box.com/s/abcdef123456";;
+    private static final Date TEST_CREATED_AT = new Date(12345678L);
+    private static final Date TEST_CONTENT_CREATED_AT = new Date(12345600L);
+    private static final Date TEST_CONTENT_MODIFIED_AT = new Date(12345700L);
+    private static final Date TEST_TRASHED_AT = null;
+    private static final Date TEST_PURGED_AT = null;
+
+    @Mock
+    BoxFile mockBoxFile;
+
+    @Mock
+    BoxUser.Info mockBoxUser;
+
+    @Mock
+    BoxSharedLink mockSharedLink;
+
+    @Override
+    @BeforeEach
+    void setUp() throws Exception {
+        final FetchBoxFileInfo testSubject = new FetchBoxFileInfo() {
+            @Override
+            protected BoxFile getBoxFile(String fileId) {
+                return mockBoxFile;
+            }
+        };
+
+        testRunner = TestRunners.newTestRunner(testSubject);
+        super.setUp();
+    }
+
+    @Test
+    void testFetchMetadataFromFlowFileAttribute() {
+        testRunner.setProperty(FetchBoxFileInfo.FILE_ID, "${box.id}");
+        final MockFlowFile inputFlowFile = new MockFlowFile(0);
+        final Map<String, String> attributes = new HashMap<>();
+        attributes.put(BoxFileAttributes.ID, TEST_FILE_ID);
+        inputFlowFile.putAttributes(attributes);
+
+        setupMockFileInfoWithExtendedAttributes();
+
+        testRunner.enqueue(inputFlowFile);
+        testRunner.run();
+
+        testRunner.assertAllFlowFilesTransferred(FetchBoxFileInfo.REL_SUCCESS, 
1);
+        final List<MockFlowFile> flowFiles = 
testRunner.getFlowFilesForRelationship(FetchBoxFileInfo.REL_SUCCESS);
+        final MockFlowFile flowFilesFirst = flowFiles.getFirst();
+
+        assertOutFlowFileAttributes(flowFilesFirst);
+        verifyExtendedAttributes(flowFilesFirst);
+    }
+
+    @Test
+    void testFetchMetadataFromProperty() {
+        testRunner.setProperty(FetchBoxFileInfo.FILE_ID, TEST_FILE_ID);
+
+        setupMockFileInfoWithExtendedAttributes();
+
+        final MockFlowFile inputFlowFile = new MockFlowFile(0);
+        testRunner.enqueue(inputFlowFile);
+        testRunner.run();
+
+        testRunner.assertAllFlowFilesTransferred(FetchBoxFileInfo.REL_SUCCESS, 
1);
+        final List<MockFlowFile> flowFiles = 
testRunner.getFlowFilesForRelationship(FetchBoxFileInfo.REL_SUCCESS);
+        final MockFlowFile flowFilesFirst = flowFiles.getFirst();
+
+        assertOutFlowFileAttributes(flowFilesFirst);
+        verifyExtendedAttributes(flowFilesFirst);
+    }
+
+    @Test
+    void testApiErrorHandling() {
+        testRunner.setProperty(FetchBoxFileInfo.FILE_ID, TEST_FILE_ID);
+
+        BoxAPIResponseException mockException = new 
BoxAPIResponseException("API Error", 404, "Box File Not Found", null);
+        doThrow(mockException).when(mockBoxFile).getInfo(anyString(), 
anyString(), anyString(), anyString(), anyString(),
+                anyString(), anyString(), anyString(), anyString(), 
anyString(), anyString(), anyString(),
+                anyString(), anyString(), anyString(), anyString(), 
anyString());
+
+        MockFlowFile inputFlowFile = new MockFlowFile(0);
+        testRunner.enqueue(inputFlowFile);
+        testRunner.run();
+
+        
testRunner.assertAllFlowFilesTransferred(FetchBoxFileInfo.REL_NOT_FOUND, 1);
+        final List<MockFlowFile> flowFiles = 
testRunner.getFlowFilesForRelationship(FetchBoxFileInfo.REL_NOT_FOUND);
+        final MockFlowFile flowFilesFirst = flowFiles.getFirst();
+        flowFilesFirst.assertAttributeEquals(BoxFileAttributes.ERROR_CODE, 
"404");
+        flowFilesFirst.assertAttributeEquals(BoxFileAttributes.ERROR_MESSAGE, 
"API Error [404]");
+    }
+
+    @Test
+    void testBoxApiExceptionHandling() {
+        testRunner.setProperty(FetchBoxFileInfo.FILE_ID, TEST_FILE_ID);
+
+        BoxAPIException mockException = new BoxAPIException("General API 
Error:", 500, "Unexpected Error");
+        doThrow(mockException).when(mockBoxFile).getInfo(anyString(), 
anyString(), anyString(), anyString(), anyString(),
+                anyString(), anyString(), anyString(), anyString(), 
anyString(), anyString(), anyString(),
+                anyString(), anyString(), anyString(), anyString(), 
anyString());
+
+        MockFlowFile inputFlowFile = new MockFlowFile(0);
+        testRunner.enqueue(inputFlowFile);
+        testRunner.run();
+
+        testRunner.assertAllFlowFilesTransferred(FetchBoxFileInfo.REL_FAILURE, 
1);
+        final List<MockFlowFile> flowFiles = 
testRunner.getFlowFilesForRelationship(FetchBoxFileInfo.REL_FAILURE);
+        final MockFlowFile flowFilesFirst = flowFiles.getFirst();
+
+        flowFilesFirst.assertAttributeEquals(BoxFileAttributes.ERROR_CODE, 
"500");
+        flowFilesFirst.assertAttributeEquals(BoxFileAttributes.ERROR_MESSAGE, 
"General API Error:\nUnexpected Error");
+    }
+
+    private void setupMockFileInfoWithExtendedAttributes() {
+        final BoxFile.Info fetchedFileInfo = createFileInfo(TEST_FOLDER_NAME, 
MODIFIED_TIME);
+
+        // Set up additional metadata attributes
+        when(mockFileInfo.getDescription()).thenReturn(TEST_DESCRIPTION);
+        when(mockFileInfo.getEtag()).thenReturn(TEST_ETAG);
+        when(mockFileInfo.getSha1()).thenReturn(TEST_SHA1);
+        when(mockFileInfo.getItemStatus()).thenReturn(TEST_ITEM_STATUS);
+        when(mockFileInfo.getSequenceID()).thenReturn(TEST_SEQUENCE_ID);
+        when(mockFileInfo.getCreatedAt()).thenReturn(TEST_CREATED_AT);
+        
when(mockFileInfo.getContentCreatedAt()).thenReturn(TEST_CONTENT_CREATED_AT);
+        
when(mockFileInfo.getContentModifiedAt()).thenReturn(TEST_CONTENT_MODIFIED_AT);
+        when(mockFileInfo.getTrashedAt()).thenReturn(TEST_TRASHED_AT);
+        when(mockFileInfo.getPurgedAt()).thenReturn(TEST_PURGED_AT);
+
+        when(mockBoxUser.getName()).thenReturn(TEST_OWNER_NAME);
+        when(mockFileInfo.getOwnedBy()).thenReturn(mockBoxUser);
+        when(mockSharedLink.getURL()).thenReturn(TEST_SHARED_LINK_URL);
+        when(mockFileInfo.getSharedLink()).thenReturn(mockSharedLink);
+
+        // Return the file info when requested
+        doReturn(fetchedFileInfo).when(mockBoxFile).getInfo("name", 
"description", "size", "created_at", "modified_at",
+                "owned_by", "parent", "etag", "sha1", "item_status", 
"sequence_id", "path_collection",
+                "content_created_at", "content_modified_at", "trashed_at", 
"purged_at", "shared_link");
+    }
+
+    private void verifyExtendedAttributes(MockFlowFile flowFile) {
+        flowFile.assertAttributeEquals("box.description", TEST_DESCRIPTION);
+        flowFile.assertAttributeEquals("box.etag", TEST_ETAG);
+        flowFile.assertAttributeEquals("box.sha1", TEST_SHA1);
+        flowFile.assertAttributeEquals("box.item.status", TEST_ITEM_STATUS);
+        flowFile.assertAttributeEquals("box.sequence.id", TEST_SEQUENCE_ID);
+        flowFile.assertAttributeEquals("box.created.at", 
TEST_CREATED_AT.toString());
+        flowFile.assertAttributeEquals("box.content.created.at", 
TEST_CONTENT_CREATED_AT.toString());
+        flowFile.assertAttributeEquals("box.content.modified.at", 
TEST_CONTENT_MODIFIED_AT.toString());
+        flowFile.assertAttributeEquals("box.owner", TEST_OWNER_NAME);
+        flowFile.assertAttributeEquals("box.shared.link", 
TEST_SHARED_LINK_URL);
+    }
+}
\ No newline at end of file

Reply via email to