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 23d177be3e NIFI-14491 Support other types in 
CreateBoxFileMetadataInstance
23d177be3e is described below

commit 23d177be3eb6efd98488c1b070726310a874cbbb
Author: Alaksiej Ščarbaty <[email protected]>
AuthorDate: Tue Apr 22 17:54:10 2025 +0200

    NIFI-14491 Support other types in CreateBoxFileMetadataInstance
    
    Signed-off-by: Pierre Villard <[email protected]>
    
    This closes #9891.
---
 .../box/CreateBoxFileMetadataInstance.java         | 56 +++++++++++++++++++---
 .../box/UpdateBoxFileMetadataInstance.java         | 18 ++++++-
 .../apache/nifi/processors/box/utils/BoxDate.java  | 43 +++++++++++++++++
 .../box/CreateBoxFileMetadataInstanceTest.java     | 28 ++++++++++-
 .../box/UpdateBoxFileMetadataInstanceTest.java     |  5 +-
 .../nifi/processors/box/utils/BoxDateTest.java     | 40 ++++++++++++++++
 6 files changed, 179 insertions(+), 11 deletions(-)

diff --git 
a/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/main/java/org/apache/nifi/processors/box/CreateBoxFileMetadataInstance.java
 
b/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/main/java/org/apache/nifi/processors/box/CreateBoxFileMetadataInstance.java
index 55667cade0..c686d34dc2 100644
--- 
a/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/main/java/org/apache/nifi/processors/box/CreateBoxFileMetadataInstance.java
+++ 
b/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/main/java/org/apache/nifi/processors/box/CreateBoxFileMetadataInstance.java
@@ -37,14 +37,20 @@ 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.apache.nifi.processors.box.utils.BoxDate;
 import org.apache.nifi.serialization.RecordReader;
 import org.apache.nifi.serialization.RecordReaderFactory;
 import org.apache.nifi.serialization.record.Record;
+import org.apache.nifi.serialization.record.RecordField;
+import org.apache.nifi.serialization.record.RecordFieldType;
 
 import java.io.InputStream;
+import java.time.LocalDate;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 import static java.lang.String.valueOf;
@@ -223,20 +229,58 @@ public class CreateBoxFileMetadataInstance extends 
AbstractProcessor {
             return;
         }
 
-        List<String> fieldNames = record.getSchema().getFieldNames();
+        final List<RecordField> fields = record.getSchema().getFields();
 
-        if (fieldNames.isEmpty()) {
+        if (fields.isEmpty()) {
             errors.add("Record has no fields");
             return;
         }
 
-        for (String fieldName : fieldNames) {
-            Object valueObj = record.getValue(fieldName);
-            String value = valueObj != null ? valueObj.toString() : null;
-            metadata.add("/" + fieldName, value);
+        for (final RecordField field : fields) {
+            addValueToMetadata(metadata, record, field);
         }
     }
 
+    private void addValueToMetadata(final Metadata metadata, final Record 
record, final RecordField field) {
+        if (record.getValue(field) == null) {
+            return;
+        }
+
+        final RecordFieldType fieldType = field.getDataType().getFieldType();
+        final String fieldName = field.getFieldName();
+        final String path = "/" + fieldName;
+
+        if (isNumber(fieldType)) {
+            metadata.add(path, record.getAsDouble(fieldName));
+        } else if (isDate(fieldType)) {
+            final LocalDate date = record.getAsLocalDate(fieldName, null);
+            metadata.add(path, BoxDate.of(date).format());
+        } else if (isArray(fieldType)) {
+            final List<String> values = 
Arrays.stream(record.getAsArray(fieldName))
+                    .filter(Objects::nonNull)
+                    .map(Object::toString)
+                    .toList();
+
+            metadata.add(path, values);
+        } else {
+            metadata.add(path, record.getAsString(fieldName));
+        }
+    }
+
+    private boolean isNumber(final RecordFieldType fieldType) {
+        final boolean isInteger = RecordFieldType.BIGINT.equals(fieldType) || 
RecordFieldType.BIGINT.isWiderThan(fieldType);
+        final boolean isFloat = RecordFieldType.DECIMAL.equals(fieldType) || 
RecordFieldType.DECIMAL.isWiderThan(fieldType);
+        return isInteger || isFloat;
+    }
+
+    private boolean isDate(final RecordFieldType fieldType) {
+        return RecordFieldType.DATE.equals(fieldType);
+    }
+
+    private boolean isArray(final RecordFieldType fieldType) {
+        return RecordFieldType.ARRAY.equals(fieldType);
+    }
+
     /**
      * Returns a BoxFile object for the given file ID.
      *
diff --git 
a/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/main/java/org/apache/nifi/processors/box/UpdateBoxFileMetadataInstance.java
 
b/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/main/java/org/apache/nifi/processors/box/UpdateBoxFileMetadataInstance.java
index 0f4fda7449..c8ea194ba6 100644
--- 
a/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/main/java/org/apache/nifi/processors/box/UpdateBoxFileMetadataInstance.java
+++ 
b/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/main/java/org/apache/nifi/processors/box/UpdateBoxFileMetadataInstance.java
@@ -37,11 +37,15 @@ 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.apache.nifi.processors.box.utils.BoxDate;
 import org.apache.nifi.serialization.RecordReader;
 import org.apache.nifi.serialization.RecordReaderFactory;
 import org.apache.nifi.serialization.record.Record;
+import org.apache.nifi.serialization.record.RecordField;
+import org.apache.nifi.serialization.record.RecordFieldType;
 
 import java.io.InputStream;
+import java.time.LocalDate;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -216,8 +220,16 @@ public class UpdateBoxFileMetadataInstance extends 
AbstractProcessor {
 
             final Record record = recordReader.nextRecord();
             if (record != null) {
-                for (String fieldName : record.getSchema().getFieldNames()) {
-                    desiredState.put(fieldName, record.getValue(fieldName));
+                final List<RecordField> fields = 
record.getSchema().getFields();
+                for (final RecordField field : fields) {
+                    final String fieldName = field.getFieldName();
+                    final RecordFieldType type = 
field.getDataType().getFieldType();
+
+                    final Object value = RecordFieldType.DATE.equals(type)
+                            ? record.getAsLocalDate(fieldName, null) // 
Ensuring dates are read as LocalDate.
+                            : record.getValue(field);
+
+                    desiredState.put(fieldName, value);
                 }
             }
         }
@@ -269,6 +281,7 @@ public class UpdateBoxFileMetadataInstance extends 
AbstractProcessor {
             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());
             }
         } else {
@@ -276,6 +289,7 @@ public class UpdateBoxFileMetadataInstance extends 
AbstractProcessor {
             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());
             }
         }
diff --git 
a/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/main/java/org/apache/nifi/processors/box/utils/BoxDate.java
 
b/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/main/java/org/apache/nifi/processors/box/utils/BoxDate.java
new file mode 100644
index 0000000000..2cb9bcf988
--- /dev/null
+++ 
b/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/main/java/org/apache/nifi/processors/box/utils/BoxDate.java
@@ -0,0 +1,43 @@
+/*
+ * 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.utils;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * A wrapper class for formatting {@link LocalDate} to a string that is 
accepted by Box Metadata API.
+ */
+public final class BoxDate {
+
+    // The time part is always set to 0. 
https://developer.box.com/guides/metadata/fields/date/
+    private static final DateTimeFormatter DATE_FORMATTER = 
DateTimeFormatter.ofPattern("yyyy-MM-dd'T00:00:00.000Z'");
+
+    private final LocalDate date;
+
+    private BoxDate(final LocalDate date) {
+        this.date = date;
+    }
+
+    public static BoxDate of(final LocalDate date) {
+        return new BoxDate(date);
+    }
+
+    public String format() {
+        return DATE_FORMATTER.format(date);
+    }
+}
diff --git 
a/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/test/java/org/apache/nifi/processors/box/CreateBoxFileMetadataInstanceTest.java
 
b/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/test/java/org/apache/nifi/processors/box/CreateBoxFileMetadataInstanceTest.java
index eb7d71a86c..959518a25c 100644
--- 
a/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/test/java/org/apache/nifi/processors/box/CreateBoxFileMetadataInstanceTest.java
+++ 
b/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/test/java/org/apache/nifi/processors/box/CreateBoxFileMetadataInstanceTest.java
@@ -31,6 +31,13 @@ import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 
+import java.text.ParseException;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.util.Date;
+import java.util.List;
+
+import static java.time.ZoneOffset.UTC;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doThrow;
@@ -75,14 +82,20 @@ public class CreateBoxFileMetadataInstanceTest extends 
AbstractBoxFileTest {
     }
 
     @Test
-    void testSuccessfulMetadataCreation() {
+    void testSuccessfulMetadataCreation() throws ParseException {
         final String inputJson = """
                 {
                   "audience": "internal",
                   "documentType": "Q1 plans",
                   "competitiveDocument": "no",
                   "status": "active",
-                  "author": "Jones"
+                  "author": "Jones",
+                  "int": 1,
+                  "double": 1.234,
+                  "almostTenToThePowerOfThirty": 
1000000000000000000000000000123,
+                  "array": [ "one", "two", "three" ],
+                  "intArray": [ 1, 2, 3 ],
+                  "date": "2025-01-01"
                 }""";
 
         testRunner.enqueue(inputJson);
@@ -97,6 +110,12 @@ public class CreateBoxFileMetadataInstanceTest extends 
AbstractBoxFileTest {
         assertEquals("no", 
capturedMetadata.getValue("/competitiveDocument").asString());
         assertEquals("active", 
capturedMetadata.getValue("/status").asString());
         assertEquals("Jones", capturedMetadata.getValue("/author").asString());
+        assertEquals(1, capturedMetadata.getValue("/int").asInt());
+        assertEquals(1.234, capturedMetadata.getDouble("/double"));
+        assertEquals(1e30, 
capturedMetadata.getDouble("/almostTenToThePowerOfThirty")); // Precision loss 
is accepted.
+        assertEquals(List.of("one", "two", "three"), 
capturedMetadata.getMultiSelect("/array"));
+        assertEquals(List.of("1", "2", "3"), 
capturedMetadata.getMultiSelect("/intArray"));
+        assertEquals(createLegacyDate(2025, 1, 1), 
capturedMetadata.getDate("/date"));
 
         
testRunner.assertAllFlowFilesTransferred(CreateBoxFileMetadataInstance.REL_SUCCESS,
 1);
         final MockFlowFile flowFile = 
testRunner.getFlowFilesForRelationship(CreateBoxFileMetadataInstance.REL_SUCCESS).getFirst();
@@ -157,4 +176,9 @@ public class CreateBoxFileMetadataInstanceTest extends 
AbstractBoxFileTest {
         flowFile.assertAttributeEquals(BoxFileAttributes.ERROR_MESSAGE, "API 
Error [404]");
     }
 
+    private static Date createLegacyDate(int year, int month, int day) {
+        final LocalDate date = LocalDate.of(year, month, day);
+        final Instant instant = date.atStartOfDay(UTC).toInstant();
+        return Date.from(instant);
+    }
 }
diff --git 
a/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/test/java/org/apache/nifi/processors/box/UpdateBoxFileMetadataInstanceTest.java
 
b/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/test/java/org/apache/nifi/processors/box/UpdateBoxFileMetadataInstanceTest.java
index 0f7437083b..fd6f349c68 100644
--- 
a/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/test/java/org/apache/nifi/processors/box/UpdateBoxFileMetadataInstanceTest.java
+++ 
b/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/test/java/org/apache/nifi/processors/box/UpdateBoxFileMetadataInstanceTest.java
@@ -91,6 +91,7 @@ public class UpdateBoxFileMetadataInstanceTest extends 
AbstractBoxFileTest {
     private void configureJsonRecordReader(TestRunner runner) throws 
InitializationException {
         final JsonTreeReader readerService = new JsonTreeReader();
         runner.addControllerService("json-reader", readerService);
+        runner.setProperty(readerService, "Date Format", "yyyy-MM-dd");
         runner.enableControllerService(readerService);
     }
 
@@ -290,7 +291,8 @@ public class UpdateBoxFileMetadataInstanceTest extends 
AbstractBoxFileTest {
                   "doubleField": 42.5,
                   "booleanField": true,
                   "listField": ["item1", "item2", "item3"],
-                  "emptyListField": []
+                  "emptyListField": [],
+                  "date": "2025-01-01"
                 }""";
 
         testRunner.enqueue(inputJson);
@@ -301,6 +303,7 @@ public class UpdateBoxFileMetadataInstanceTest extends 
AbstractBoxFileTest {
         verify(mockMetadata).add("/numberField", 42.0); // Numbers are stored 
as doubles
         verify(mockMetadata).add("/doubleField", 42.5);
         verify(mockMetadata).add("/booleanField", "true"); // Booleans are 
stored as strings
+        verify(mockMetadata).add("/date", "2025-01-01T00:00:00.000Z"); // 
Dates have a specific format.
         // We need to use doAnswer/when to capture and verify list fields 
being added, but this is simpler
 
         verify(mockBoxFile).updateMetadata(any(Metadata.class));
diff --git 
a/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/test/java/org/apache/nifi/processors/box/utils/BoxDateTest.java
 
b/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/test/java/org/apache/nifi/processors/box/utils/BoxDateTest.java
new file mode 100644
index 0000000000..ce6ebcccaa
--- /dev/null
+++ 
b/nifi-extension-bundles/nifi-box-bundle/nifi-box-processors/src/test/java/org/apache/nifi/processors/box/utils/BoxDateTest.java
@@ -0,0 +1,40 @@
+/*
+ * 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.utils;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+
+import java.time.LocalDate;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class BoxDateTest {
+
+    @ParameterizedTest
+    @CsvSource({
+            "2025-01-01, 2025-01-01T00:00:00.000Z",
+            "2025-02-25, 2025-02-25T00:00:00.000Z",
+            "2025-11-10, 2025-11-10T00:00:00.000Z",
+            })
+    void format(final LocalDate date, final String expected) {
+        final BoxDate boxDate = BoxDate.of(date);
+        final String formatted = boxDate.format();
+
+        assertEquals(expected, formatted);
+    }
+}

Reply via email to