nifi-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From phroc...@apache.org
Subject nifi-minifi-cpp git commit: MINIFICPP-330 Implemented Expression Language substring operations
Date Mon, 18 Dec 2017 15:57:42 GMT
Repository: nifi-minifi-cpp
Updated Branches:
  refs/heads/master d616effd3 -> dc131d5ac


MINIFICPP-330 Implemented Expression Language substring operations

This closes #219.

Signed-off-by: Marc Parisi <phrocker@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/commit/dc131d5a
Tree: http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/tree/dc131d5a
Diff: http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/diff/dc131d5a

Branch: refs/heads/master
Commit: dc131d5ac92d0173a2fe891df0a77e7d4eff684d
Parents: d616eff
Author: Andy I. Christianson <andy@andyic.org>
Authored: Tue Dec 12 12:04:46 2017 -0500
Committer: Marc Parisi <phrocker@apache.org>
Committed: Mon Dec 18 10:48:05 2017 -0500

----------------------------------------------------------------------
 extensions/expression-language/Expression.cpp   | 61 +++++++++++++++--
 extensions/expression-language/Parser.yy        | 15 +++--
 .../ExpressionLanguageTests.cpp                 | 69 ++++++++++++++++++++
 3 files changed, 137 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/dc131d5a/extensions/expression-language/Expression.cpp
----------------------------------------------------------------------
diff --git a/extensions/expression-language/Expression.cpp b/extensions/expression-language/Expression.cpp
index 8c03422..806ff6a 100644
--- a/extensions/expression-language/Expression.cpp
+++ b/extensions/expression-language/Expression.cpp
@@ -64,12 +64,44 @@ std::string expr_toUpper(const std::vector<std::string> &args)
{
   return result;
 }
 
+std::string expr_substring(const std::vector<std::string> &args) {
+  if (args.size() < 3) {
+    return args[0].substr(std::stoul(args[1]));
+  } else {
+    return args[0].substr(std::stoul(args[1]), std::stoul(args[2]));
+  }
+}
+
+std::string expr_substringBefore(const std::vector<std::string> &args) {
+  return args[0].substr(0, args[0].find(args[1]));
+}
+
+std::string expr_substringBeforeLast(const std::vector<std::string> &args) {
+  size_t last_pos = 0;
+  while (args[0].find(args[1], last_pos + 1) != std::string::npos) {
+    last_pos = args[0].find(args[1], last_pos + 1);
+  }
+  return args[0].substr(0, last_pos);
+}
+
+std::string expr_substringAfter(const std::vector<std::string> &args) {
+  return args[0].substr(args[0].find(args[1]) + args[1].length());
+}
+
+std::string expr_substringAfterLast(const std::vector<std::string> &args) {
+  size_t last_pos = 0;
+  while (args[0].find(args[1], last_pos + 1) != std::string::npos) {
+    last_pos = args[0].find(args[1], last_pos + 1);
+  }
+  return args[0].substr(last_pos + args[1].length());
+}
+
 template<std::string T(const std::vector<std::string> &)>
 Expression make_dynamic_function_incomplete(const std::string &function_name,
                                             const std::vector<Expression> &args,
                                             std::size_t num_args) {
-  if (args.size() == num_args) {
-    return make_dynamic([=](const Parameters &params) -> std::string {
+  if (args.size() >= num_args) {
+    auto result = make_dynamic([=](const Parameters &params) -> std::string {
       std::vector<std::string> evaluated_args;
 
       for (const auto &arg : args) {
@@ -78,6 +110,14 @@ Expression make_dynamic_function_incomplete(const std::string &function_name,
 
       return T(evaluated_args);
     });
+
+    result.complete = [function_name, args](Expression expr) -> Expression {
+      std::vector<Expression> complete_args = {expr};
+      complete_args.insert(complete_args.end(), args.begin(), args.end());
+      return make_dynamic_function(function_name, complete_args);
+    };
+
+    return result;
   } else {
     auto result = make_dynamic([](const Parameters &params) -> std::string {
       throw std::runtime_error("Attempted to call incomplete function");
@@ -93,11 +133,22 @@ Expression make_dynamic_function_incomplete(const std::string &function_name,
   }
 }
 
-Expression make_dynamic_function(const std::string &function_name, const std::vector<Expression>
&args) {
+Expression make_dynamic_function(const std::string &function_name,
+                                 const std::vector<Expression> &args) {
   if (function_name == "hostname") {
     return make_dynamic_function_incomplete<expr_hostname>(function_name, args, 0);
   } else if (function_name == "toUpper") {
     return make_dynamic_function_incomplete<expr_toUpper>(function_name, args, 1);
+  } else if (function_name == "substring") {
+    return make_dynamic_function_incomplete<expr_substring>(function_name, args, 2);
+  } else if (function_name == "substringBefore") {
+    return make_dynamic_function_incomplete<expr_substringBefore>(function_name, args,
2);
+  } else if (function_name == "substringBeforeLast") {
+    return make_dynamic_function_incomplete<expr_substringBeforeLast>(function_name,
args, 2);
+  } else if (function_name == "substringAfter") {
+    return make_dynamic_function_incomplete<expr_substringAfter>(function_name, args,
2);
+  } else if (function_name == "substringAfterLast") {
+    return make_dynamic_function_incomplete<expr_substringAfterLast>(function_name,
args, 2);
   } else {
     std::string msg("Unknown expression function: ");
     msg.append(function_name);
@@ -142,10 +193,12 @@ Expression Expression::operator+(const Expression &other_expr) const
{
       result.append(other_val_fn(params));
       return result;
     });
-  } else { // !isDynamic() && !other_expr.isDynamic()
+  } else if (!isDynamic() && !other_expr.isDynamic()) {
     std::string result(val_);
     result.append(other_expr.val_);
     return make_static(result);
+  } else {
+    throw std::runtime_error("Invalid function composition");
   }
 }
 

http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/dc131d5a/extensions/expression-language/Parser.yy
----------------------------------------------------------------------
diff --git a/extensions/expression-language/Parser.yy b/extensions/expression-language/Parser.yy
index 0ffe536..e9837be 100644
--- a/extensions/expression-language/Parser.yy
+++ b/extensions/expression-language/Parser.yy
@@ -171,20 +171,27 @@ attr_id: quoted_text exp_whitespaces { std::swap($$, $1); }
        | IDENTIFIER exp_whitespaces { std::swap($$, $1); }
        ;
 
-fn_arg: exp_content_val { $$ = $1; }
+/*fn_arg: exp_content_val exp_whitespaces { $$ = $1; }
+      | NUMBER exp_whitespaces { $$ = make_static(std::to_string($1)); }
+      ;*/
+
+fn_arg: quoted_text exp_whitespaces { $$ = make_static($1); }
+      | NUMBER exp_whitespaces { $$ = make_static(std::to_string($1)); }
+      | exp exp_whitespaces { $$ = $1; }
       ;
 
 fn_args: %empty {}
-       | fn_args fn_arg { $$.insert($$.end(), $1.begin(), $1.end()); $$.push_back($2); }
+       | fn_args COMMA exp_whitespaces fn_arg { $$.insert($$.end(), $1.begin(), $1.end());
$$.push_back($4); }
+       | fn_arg { $$.push_back($1); }
 
-fn_call: attr_id LPAREN fn_args RPAREN exp_whitespaces { $$ = make_dynamic_function(std::move($1),
$3); }
+fn_call: attr_id LPAREN exp_whitespaces fn_args RPAREN exp_whitespaces { $$ = make_dynamic_function(std::move($1),
$4); }
 
 exp_content_val: attr_id { $$ = make_dynamic_attr(std::move($1)); }
                | fn_call { $$ = $1; }
                ;
 
 exp_content: exp_content_val { $$ = $1; }
-           | exp_content_val COLON exp_whitespace fn_call { $$ = make_dynamic_function_postfix($1,
$4); }
+           | exp_content_val COLON exp_whitespaces fn_call { $$ = make_dynamic_function_postfix($1,
$4); }
            ;
 
 exp: DOLLAR LCURLY exp_whitespaces exp_content RCURLY { $$ = $4; }

http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/dc131d5a/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp
----------------------------------------------------------------------
diff --git a/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp b/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp
index c0dd7f2..0af816d 100644
--- a/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp
+++ b/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp
@@ -143,6 +143,13 @@ TEST_CASE("ToUpper function", "[expressionLanguageTestToUpperFunction]")
{  // N
   REQUIRE("text_before__FLOW_A_ATTR_VALUE_A__text_after" == expr({flow_file_a}));
 }
 
+TEST_CASE("ToUpper function w/o whitespace", "[expressionLanguageTestToUpperFunctionWithoutWhitespace]")
{  // NOLINT
+  auto expr = expression::compile(R"(text_before${attr_a:toUpper()}text_after)");
+  auto flow_file_a = std::make_shared<MockFlowFile>();
+  flow_file_a->addAttribute("attr_a", "__flow_a_attr_value_a__");
+  REQUIRE("text_before__FLOW_A_ATTR_VALUE_A__text_after" == expr({flow_file_a}));
+}
+
 TEST_CASE("GetFile PutFile dynamic attribute", "[expressionLanguageTestGetFilePutFileDynamicAttribute]")
{  // NOLINT
   TestController testController;
 
@@ -235,3 +242,65 @@ TEST_CASE("GetFile PutFile dynamic attribute", "[expressionLanguageTestGetFilePu
     REQUIRE("extracted_attr" == output_str.str());
   }
 }
+
+TEST_CASE("Substring 2 arg", "[expressionLanguageSubstring2]") {  // NOLINT
+  auto expr = expression::compile("text_before${attr:substring(6, 8)}text_after");
+
+  auto flow_file_a = std::make_shared<MockFlowFile>();
+  flow_file_a->addAttribute("attr", "__flow_a_attr_value_a__");
+  REQUIRE("text_before_a_attr_text_after" == expr({flow_file_a}));
+}
+
+TEST_CASE("Substring 1 arg", "[expressionLanguageSubstring1]") {  // NOLINT
+  auto expr = expression::compile("text_before${attr:substring(6)}text_after");
+
+  auto flow_file_a = std::make_shared<MockFlowFile>();
+  flow_file_a->addAttribute("attr", "__flow_a_attr_value_a__");
+  REQUIRE("text_before_a_attr_value_a__text_after" == expr({flow_file_a}));
+}
+
+TEST_CASE("Substring Before", "[expressionLanguageSubstringBefore]") {  // NOLINT
+  auto expr = expression::compile("${attr:substringBefore('attr_value_a__')}");
+
+  auto flow_file_a = std::make_shared<MockFlowFile>();
+  flow_file_a->addAttribute("attr", "__flow_a_attr_value_a__");
+  REQUIRE("__flow_a_" == expr({flow_file_a}));
+}
+
+TEST_CASE("Substring Before Last", "[expressionLanguageSubstringBeforeLast]") {  // NOLINT
+  auto expr = expression::compile("${attr:substringBeforeLast('_a')}");
+
+  auto flow_file_a = std::make_shared<MockFlowFile>();
+  flow_file_a->addAttribute("attr", "__flow_a_attr_value_a__");
+  REQUIRE("__flow_a_attr_value" == expr({flow_file_a}));
+}
+
+TEST_CASE("Substring After", "[expressionLanguageSubstringAfter]") {  // NOLINT
+  auto expr = expression::compile("${attr:substringAfter('__flow_a')}");
+
+  auto flow_file_a = std::make_shared<MockFlowFile>();
+  flow_file_a->addAttribute("attr", "__flow_a_attr_value_a__");
+  REQUIRE("_attr_value_a__" == expr({flow_file_a}));
+}
+
+TEST_CASE("Substring After Last", "[expressionLanguageSubstringAfterLast]") {  // NOLINT
+  auto expr = expression::compile("${attr:substringAfterLast('_a')}");
+
+  auto flow_file_a = std::make_shared<MockFlowFile>();
+  flow_file_a->addAttribute("attr", "__flow_a_attr_value_a__");
+  REQUIRE("__" == expr({flow_file_a}));
+}
+
+TEST_CASE("Substring Before No Args", "[expressionLanguageSubstringBeforeNoArgs]") {  //
NOLINT
+  auto expr = expression::compile("${attr:substringBefore()}");
+  auto flow_file_a = std::make_shared<MockFlowFile>();
+  flow_file_a->addAttribute("attr", "__flow_a_attr_value_a__");
+  REQUIRE_THROWS_WITH(expr({flow_file_a}), "Attempted to call incomplete function");
+}
+
+TEST_CASE("Substring After No Args", "[expressionLanguageSubstringAfterNoArgs]") {  // NOLINT
+  auto expr = expression::compile("${attr:substringAfter()}");
+  auto flow_file_a = std::make_shared<MockFlowFile>();
+  flow_file_a->addAttribute("attr", "__flow_a_attr_value_a__");
+  REQUIRE_THROWS_WITH(expr({flow_file_a}), "Attempted to call incomplete function");
+}


Mime
View raw message