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

lordgamez pushed a commit to branch MINIFICPP-2458
in repository https://gitbox.apache.org/repos/asf/nifi-minifi-cpp.git

commit 5971b19cc5cc453a7bbab2513ef890c9dd0ea23c
Author: Gabor Gyimesi <[email protected]>
AuthorDate: Thu Jul 10 15:47:19 2025 +0200

    Review update
---
 PROCESSORS.md                                      | 18 +++++---
 .../processors/EvaluateJsonPath.cpp                | 10 +++--
 .../processors/EvaluateJsonPath.h                  |  8 +++-
 .../tests/unit/EvaluateJsonPathTests.cpp           | 52 ++++++++++------------
 4 files changed, 48 insertions(+), 40 deletions(-)

diff --git a/PROCESSORS.md b/PROCESSORS.md
index dfeef9556..d5815d89c 100644
--- a/PROCESSORS.md
+++ b/PROCESSORS.md
@@ -638,12 +638,18 @@ Evaluates one or more JsonPath expressions against the 
content of a FlowFile. Th
 
 In the list below, the names of required properties appear in bold. Any other 
properties (not in bold) are considered optional. The table also indicates any 
default values, and whether a property supports the NiFi Expression Language.
 
-| Name                          | Default Value      | Allowable Values        
                | Description                                                   
                                                                                
                                                                                
                                                        |
-|-------------------------------|--------------------|-----------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| **Destination**               | flowfile-attribute | 
flowfile-content<br/>flowfile-attribute | Indicates whether the results of the 
JsonPath evaluation are written to the FlowFile content or a FlowFile 
attribute. If using attribute, must specify the Attribute Name property. If set 
to flowfile-content, only one JsonPath may be specified, and the property name 
is ignored. |
-| **Null Value Representation** | empty string       | empty string<br/>the 
string 'null'      | Indicates the desired representation of JSON Path 
expressions resulting in a null value.                                          
                                                                                
                                                                    |
-| **Path Not Found Behavior**   | ignore             | 
warn<br/>ignore<br/>skip                | Indicates how to handle missing JSON 
path expressions when destination is set to 'flowfile-attribute'. Selecting 
'warn' will generate a warning when a JSON path expression is not found. 
Selecting 'skip' will omit attributes for any unmatched JSON path expressions.  
            |
-| **Return Type**               | auto-detect        | 
auto-detect<br/>json<br/>scalar         | Indicates the desired return type of 
the JSON Path expressions. Selecting 'auto-detect' will set the return type to 
'json' for a Destination of 'flowfile-content', and 'scalar' for a Destination 
of 'flowfile-attribute'.                                                        
   |
+| Name                          | Default Value      | Allowable Values        
                | Description                                                   
                                                                                
                                                                                
                                           |
+|-------------------------------|--------------------|-----------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| **Destination**               | flowfile-attribute | 
flowfile-content<br/>flowfile-attribute | Indicates whether the results of the 
JsonPath evaluation are written to the FlowFile content or a FlowFile 
attribute.                                                                      
                                                                              |
+| **Null Value Representation** | empty string       | empty string<br/>the 
string 'null'      | Indicates the desired representation of JSON Path 
expressions resulting in a null value.                                          
                                                                                
                                                       |
+| **Path Not Found Behavior**   | ignore             | 
warn<br/>ignore<br/>skip                | Indicates how to handle missing JSON 
path expressions when destination is set to 'flowfile-attribute'. Selecting 
'warn' will generate a warning when a JSON path expression is not found. 
Selecting 'skip' will omit attributes for any unmatched JSON path expressions. |
+| **Return Type**               | auto-detect        | 
auto-detect<br/>json<br/>scalar         | Indicates the desired return type of 
the JSON Path expressions. Selecting 'auto-detect' will set the return type to 
'json' for a Destination of 'flowfile-content', and 'scalar' for a Destination 
of 'flowfile-attribute'.                                              |
+
+### Dynamic Properties
+
+| Name              | Value                           | Description            
                                                                                
                                                                                
                                                                                
                                                                                
                                 |
+|-------------------|---------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Evaluation Result | JsonPath expression to evaluate | Dynamic property 
values are evaluated as JsonPaths. In case of 'flowfile-conent' destination, 
only one dynamic property with JsonPath may be specified, in this case the name 
of the property is ignored. In case of 'flowfile-attribute' destination, the 
result of the JsonPath evaluation is written to the 'Evaluation Result' 
property.<br/>**Supports Expression Language: true** |
 
 ### Relationships
 
diff --git a/extensions/standard-processors/processors/EvaluateJsonPath.cpp 
b/extensions/standard-processors/processors/EvaluateJsonPath.cpp
index 4b39572da..a8e553f7c 100644
--- a/extensions/standard-processors/processors/EvaluateJsonPath.cpp
+++ b/extensions/standard-processors/processors/EvaluateJsonPath.cpp
@@ -43,6 +43,9 @@ void EvaluateJsonPath::initialize() {
 }
 
 void EvaluateJsonPath::onSchedule(core::ProcessContext& context, 
core::ProcessSessionFactory&) {
+  if (getDynamicProperties().size() < 1) {
+    throw Exception(PROCESS_SCHEDULE_EXCEPTION, "At least one dynamic property 
must be specified with a valid JSON path expression");
+  }
   destination_ = 
utils::parseEnumProperty<evaluate_json_path::DestinationType>(context, 
EvaluateJsonPath::Destination);
   if (destination_ == evaluate_json_path::DestinationType::FlowFileContent && 
getDynamicProperties().size() > 1) {
     throw Exception(PROCESS_SCHEDULE_EXCEPTION, "Only one dynamic property is 
allowed for JSON path when destination is set to flowfile-content");
@@ -89,14 +92,15 @@ void 
EvaluateJsonPath::writeQueryResult(core::ProcessSession& session, core::Flo
   }
 }
 
-void EvaluateJsonPath::onTrigger(core::ProcessContext&, core::ProcessSession& 
session) {
+void EvaluateJsonPath::onTrigger(core::ProcessContext& context, 
core::ProcessSession& session) {
   auto flow_file = session.get();
   if (!flow_file) {
+    context.yield();
+    logger_->log_debug("No FlowFile available, yielding");
     return;
   }
 
-  const auto flow_file_read_result = session.readBuffer(flow_file);
-  const auto json_string = std::string(reinterpret_cast<const 
char*>(flow_file_read_result.buffer.data()), 
flow_file_read_result.buffer.size());
+  const auto json_string = to_string(session.readBuffer(flow_file));
   if (json_string.empty()) {
     logger_->log_error("FlowFile content is empty, transferring to Failure 
relationship");
     session.transfer(flow_file, Failure);
diff --git a/extensions/standard-processors/processors/EvaluateJsonPath.h 
b/extensions/standard-processors/processors/EvaluateJsonPath.h
index 79af1a806..4d1d76926 100644
--- a/extensions/standard-processors/processors/EvaluateJsonPath.h
+++ b/extensions/standard-processors/processors/EvaluateJsonPath.h
@@ -123,8 +123,7 @@ class EvaluateJsonPath final : public core::ProcessorImpl {
         "value unless 'Path Not Found Behaviour' is set to 'skip', and the 
FlowFile will always be routed to 'matched.'";
 
   EXTENSIONAPI static constexpr auto Destination = 
core::PropertyDefinitionBuilder<2>::createProperty("Destination")
-      .withDescription("Indicates whether the results of the JsonPath 
evaluation are written to the FlowFile content or a FlowFile attribute. If 
using attribute, must specify the Attribute Name "
-          "property. If set to flowfile-content, only one JsonPath may be 
specified, and the property name is ignored.")
+      .withDescription("Indicates whether the results of the JsonPath 
evaluation are written to the FlowFile content or a FlowFile attribute.")
       
.withAllowedValues(magic_enum::enum_names<evaluate_json_path::DestinationType>())
       
.withDefaultValue(magic_enum::enum_name(evaluate_json_path::DestinationType::FlowFileAttribute))
       .isRequired(true)
@@ -166,6 +165,11 @@ class EvaluateJsonPath final : public core::ProcessorImpl {
   EXTENSIONAPI static constexpr auto Relationships = std::array{Failure, 
Matched, Unmatched};
 
   EXTENSIONAPI static constexpr bool SupportsDynamicProperties = true;
+  EXTENSIONAPI static constexpr auto EvaluationResult = 
core::DynamicProperty{"Evaluation Result", "JsonPath expression to evaluate", 
"Dynamic property values are evaluated as JsonPaths. "
+      "In case of 'flowfile-conent' destination, only one dynamic property 
with JsonPath may be specified, in this case the name of the property is 
ignored. "
+      "In case of 'flowfile-attribute' destination, the result of the JsonPath 
evaluation is written to the 'Evaluation Result' property.", true};
+  EXTENSIONAPI static constexpr auto DynamicProperties = 
std::array{EvaluationResult};
+
   EXTENSIONAPI static constexpr bool SupportsDynamicRelationships = false;
   EXTENSIONAPI static constexpr core::annotation::Input InputRequirement = 
core::annotation::Input::INPUT_REQUIRED;
   EXTENSIONAPI static constexpr bool IsSingleThreaded = false;
diff --git 
a/extensions/standard-processors/tests/unit/EvaluateJsonPathTests.cpp 
b/extensions/standard-processors/tests/unit/EvaluateJsonPathTests.cpp
index 12c354303..379d98fce 100644
--- a/extensions/standard-processors/tests/unit/EvaluateJsonPathTests.cpp
+++ b/extensions/standard-processors/tests/unit/EvaluateJsonPathTests.cpp
@@ -36,6 +36,10 @@ class EvaluateJsonPathTestFixture {
   processors::EvaluateJsonPath* evaluate_json_path_processor_;
 };
 
+TEST_CASE_METHOD(EvaluateJsonPathTestFixture, "At least one dynamic property 
must be specified", "[EvaluateJsonPathTests]") {
+  REQUIRE_THROWS_WITH(controller_.trigger({{.content = "foo"}}), "Process 
Schedule Operation: At least one dynamic property must be specified with a 
valid JSON path expression");
+}
+
 TEST_CASE_METHOD(EvaluateJsonPathTestFixture, "When destination is set to 
flowfile content only one dynamic property is allowed", 
"[EvaluateJsonPathTests]") {
   controller_.plan->setProperty(evaluate_json_path_processor_, 
processors::EvaluateJsonPath::Destination, "flowfile-content");
   controller_.plan->setDynamicProperty(evaluate_json_path_processor_, 
"attribute1", "value1");
@@ -46,6 +50,7 @@ TEST_CASE_METHOD(EvaluateJsonPathTestFixture, "When 
destination is set to flowfi
 TEST_CASE_METHOD(EvaluateJsonPathTestFixture, "Input flowfile has invalid JSON 
as content", "[EvaluateJsonPathTests]") {
   ProcessorTriggerResult result;
   std::string error_log;
+  controller_.plan->setDynamicProperty(evaluate_json_path_processor_, 
"attribute1", "value1");
   SECTION("Flow file content is empty") {
     result = controller_.trigger({{.content = ""}});
     error_log = "FlowFile content is empty, transferring to Failure 
relationship";
@@ -115,12 +120,10 @@ TEST_CASE_METHOD(EvaluateJsonPathTestFixture, "JSON paths 
are not found in conte
   CHECK(controller_.plan->getContent(result_flow_file) == "{}");
 
   for (const auto& [key, value] : expected_attributes) {
-    std::string attribute_value;
     if (!expect_attributes) {
-      CHECK_FALSE(result_flow_file->getAttribute(key, attribute_value));
+      CHECK_FALSE(result_flow_file->getAttribute(key));
     } else {
-      CHECK(result_flow_file->getAttribute(key, attribute_value));
-      CHECK(attribute_value == value);
+      CHECK(*result_flow_file->getAttribute(key) == value);
     }
   }
 
@@ -157,9 +160,7 @@ TEST_CASE_METHOD(EvaluateJsonPathTestFixture, "JSON paths 
are not found in conte
   const auto result_flow_file = 
result.at(processors::EvaluateJsonPath::Unmatched).at(0);
 
   CHECK(controller_.plan->getContent(result_flow_file) == "{}");
-
-  std::string attribute_value;
-  CHECK_FALSE(result_flow_file->getAttribute("attribute", attribute_value));
+  CHECK_FALSE(result_flow_file->getAttribute("attribute"));
 
   if (warn_path_not_found_behavior) {
     CHECK(utils::verifyLogLinePresenceInPollTime(0s, "JSON path '$.firstName' 
not found for attribute key 'attribute'"));
@@ -173,6 +174,11 @@ TEST_CASE_METHOD(EvaluateJsonPathTestFixture, "JSON path 
query result does not m
     controller_.plan->setProperty(evaluate_json_path_processor_, 
processors::EvaluateJsonPath::Destination, "flowfile-attribute");
   }
 
+  SECTION("Return type is set to scalar with flowfile-content destination") {
+    controller_.plan->setProperty(evaluate_json_path_processor_, 
processors::EvaluateJsonPath::ReturnType, "scalar");
+    controller_.plan->setProperty(evaluate_json_path_processor_, 
processors::EvaluateJsonPath::Destination, "flowfile-content");
+  }
+
   std::string json_content = R"({"name": {"firstName": "John", "lastName": 
"Doe"}})";
   auto result = controller_.trigger({{.content = json_content}});
 
@@ -183,8 +189,7 @@ TEST_CASE_METHOD(EvaluateJsonPathTestFixture, "JSON path 
query result does not m
   const auto result_flow_file = 
result.at(processors::EvaluateJsonPath::Failure).at(0);
 
   CHECK(controller_.plan->getContent(result_flow_file) == json_content);
-  std::string attribute_value;
-  CHECK_FALSE(result_flow_file->getAttribute("attribute", attribute_value));
+  CHECK_FALSE(result_flow_file->getAttribute("attribute"));
   CHECK(utils::verifyLogLinePresenceInPollTime(0s, "JSON path '$.name' 
returned a non-scalar value or multiple values for attribute key 'attribute', 
transferring to Failure relationship"));
 }
 
@@ -202,8 +207,7 @@ TEST_CASE_METHOD(EvaluateJsonPathTestFixture, "Query JSON 
object and write it to
   const auto result_flow_file = 
result.at(processors::EvaluateJsonPath::Matched).at(0);
 
   CHECK(controller_.plan->getContent(result_flow_file) == 
R"({"firstName":"John","lastName":"Doe"})");
-  std::string attribute_value;
-  CHECK_FALSE(result_flow_file->getAttribute("jsonPath", attribute_value));
+  CHECK_FALSE(result_flow_file->getAttribute("jsonPath"));
 }
 
 TEST_CASE_METHOD(EvaluateJsonPathTestFixture, "Query multiple scalars and 
write them to attributes", "[EvaluateJsonPathTests]") {
@@ -222,12 +226,9 @@ TEST_CASE_METHOD(EvaluateJsonPathTestFixture, "Query 
multiple scalars and write
   const auto result_flow_file = 
result.at(processors::EvaluateJsonPath::Matched).at(0);
 
   CHECK(controller_.plan->getContent(result_flow_file) == json_content);
-  auto attribute_value = result_flow_file->getAttribute("firstName");
-  CHECK(attribute_value.value() == "John");
-  attribute_value = result_flow_file->getAttribute("lastName");
-  CHECK(attribute_value.value() == "Doe");
-  attribute_value = result_flow_file->getAttribute("id");
-  CHECK(attribute_value.value() == "1234");
+  CHECK(*result_flow_file->getAttribute("firstName") == "John");
+  CHECK(*result_flow_file->getAttribute("lastName") == "Doe");
+  CHECK(*result_flow_file->getAttribute("id") == "1234");
 }
 
 TEST_CASE_METHOD(EvaluateJsonPathTestFixture, "Query a single scalar and write 
it to flow file", "[EvaluateJsonPathTests]") {
@@ -244,8 +245,7 @@ TEST_CASE_METHOD(EvaluateJsonPathTestFixture, "Query a 
single scalar and write i
   const auto result_flow_file = 
result.at(processors::EvaluateJsonPath::Matched).at(0);
 
   CHECK(controller_.plan->getContent(result_flow_file) == "John");
-  std::string attribute_value;
-  CHECK_FALSE(result_flow_file->getAttribute("firstName", attribute_value));
+  CHECK_FALSE(result_flow_file->getAttribute("firstName"));
 }
 
 TEST_CASE_METHOD(EvaluateJsonPathTestFixture, "Query has multiple results", 
"[EvaluateJsonPathTests]") {
@@ -262,8 +262,7 @@ TEST_CASE_METHOD(EvaluateJsonPathTestFixture, "Query has 
multiple results", "[Ev
   const auto result_flow_file = 
result.at(processors::EvaluateJsonPath::Matched).at(0);
 
   CHECK(controller_.plan->getContent(result_flow_file) == 
"[\"John\",\"Jane\"]");
-  std::string attribute_value;
-  CHECK_FALSE(result_flow_file->getAttribute("firstName", attribute_value));
+  CHECK_FALSE(result_flow_file->getAttribute("firstName"));
 }
 
 TEST_CASE_METHOD(EvaluateJsonPathTestFixture, "Query result is null value in 
flow file content", "[EvaluateJsonPathTests]") {
@@ -290,8 +289,7 @@ TEST_CASE_METHOD(EvaluateJsonPathTestFixture, "Query result 
is null value in flo
   const auto result_flow_file = 
result.at(processors::EvaluateJsonPath::Matched).at(0);
 
   CHECK(controller_.plan->getContent(result_flow_file) == expected_content);
-  std::string attribute_value;
-  CHECK_FALSE(result_flow_file->getAttribute("firstName", attribute_value));
+  CHECK_FALSE(result_flow_file->getAttribute("firstName"));
 }
 
 TEST_CASE_METHOD(EvaluateJsonPathTestFixture, "Query result is null value in 
flow file attribute", "[EvaluateJsonPathTests]") {
@@ -319,12 +317,8 @@ TEST_CASE_METHOD(EvaluateJsonPathTestFixture, "Query 
result is null value in flo
   const auto result_flow_file = 
result.at(processors::EvaluateJsonPath::Matched).at(0);
 
   CHECK(controller_.plan->getContent(result_flow_file) == json_content);
-  auto attribute = result_flow_file->getAttribute("firstName");
-  REQUIRE(attribute);
-  CHECK(attribute.value() == "John");
-  attribute = result_flow_file->getAttribute("email");
-  REQUIRE(attribute);
-  CHECK(attribute.value() == expected_null_value);
+  CHECK(*result_flow_file->getAttribute("firstName") == "John");
+  CHECK(*result_flow_file->getAttribute("email") == expected_null_value);
 }
 
 }  // namespace org::apache::nifi::minifi::test

Reply via email to