Repository: nifi-minifi-cpp Updated Branches: refs/heads/master de0c93806 -> 7958bd11b
MINIFICPP-463 Implemented urlEncode/urlDecode EL functions This closes #305. Signed-off-by: Marc Parisi <[email protected]> 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/7958bd11 Tree: http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/tree/7958bd11 Diff: http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/diff/7958bd11 Branch: refs/heads/master Commit: 7958bd11b40547dfc7275e22957cec01a1a7353d Parents: de0c938 Author: Andrew I. Christianson <[email protected]> Authored: Fri Apr 20 13:06:22 2018 -0400 Committer: Marc Parisi <[email protected]> Committed: Fri Apr 27 14:43:54 2018 -0400 ---------------------------------------------------------------------- EXPRESSIONS.md | 38 +++++++++++++- extensions/expression-language/CMakeLists.txt | 3 ++ extensions/expression-language/Expression.cpp | 53 +++++++++++++++++++- .../ExpressionLanguageTests.cpp | 24 +++++++++ 4 files changed, 114 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/7958bd11/EXPRESSIONS.md ---------------------------------------------------------------------- diff --git a/EXPRESSIONS.md b/EXPRESSIONS.md index 4a72dae..51bba83 100644 --- a/EXPRESSIONS.md +++ b/EXPRESSIONS.md @@ -208,6 +208,8 @@ token, filename. - [`escapeCsv`](#escapecsv) - [`unescapeXml`](#unescapexml) - [`unescapeCsv`](#unescapecsv) +- [`urlEncode`](#urlencode) +- [`urlDecode`](#urldecode) ## Planned Features @@ -229,8 +231,6 @@ token, filename. - `escapeHtml4` - `unescapeHtml3` - `unescapeHtml4` -- `urlEncode` -- `urlDecode` - `base64Encode` - `base64Decode` @@ -1338,3 +1338,37 @@ to the rules of RFC 4180 If the "message" attribute is `"Zero > One < ""two!"" & 'true'"`, then the Expression `${message:escapeCsv()}` will return `Zero > One < "two!" & 'true'` + +### urlEncode + +**Description**: Returns a URL-friendly version of the Subject. This is useful, +for instance, when using an attribute value to indicate the URL of a website. + +**Subject Type**: String + +**Arguments**: No arguments + +**Return Type**: String + +**Examples**: + +We can URL-Encode an attribute named "url" by using the Expression +`${url:urlEncode()}`. If the value of the "url" attribute is "some value with +spaces", this Expression will then return "some%20value%20with%20spaces". + +### urlDecode + +**Description**: Converts a URL-friendly version of the Subject into a +human-readable form. + +**Subject Type**: String + +**Arguments**: No arguments + +**Return Type**: String + +**Examples**: + +If we have a URL-Encoded attribute named "url" with the value +"some%20value%20with%20spaces", then the Expression `${url:urlDecode()}` will +return "some value with spaces". http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/7958bd11/extensions/expression-language/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/extensions/expression-language/CMakeLists.txt b/extensions/expression-language/CMakeLists.txt index b242d9a..d16b7d2 100644 --- a/extensions/expression-language/CMakeLists.txt +++ b/extensions/expression-language/CMakeLists.txt @@ -55,6 +55,9 @@ endif() find_package(UUID REQUIRED) target_link_libraries(minifi-expression-language-extensions ${LIBMINIFI} ${UUID_LIBRARIES}) +find_package(CURL REQUIRED) +include_directories(${CURL_INCLUDE_DIRS}) +target_link_libraries(minifi-expression-language-extensions ${CURL_LIBRARIES}) find_package(OpenSSL REQUIRED) include_directories(${OPENSSL_INCLUDE_DIR}) target_link_libraries(minifi-expression-language-extensions ${CMAKE_DL_LIBS}) http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/7958bd11/extensions/expression-language/Expression.cpp ---------------------------------------------------------------------- diff --git a/extensions/expression-language/Expression.cpp b/extensions/expression-language/Expression.cpp index c6f6d87..44a3981 100644 --- a/extensions/expression-language/Expression.cpp +++ b/extensions/expression-language/Expression.cpp @@ -28,6 +28,7 @@ #include <utils/StringUtils.h> #include <expression/Expression.h> #include <regex> +#include <curl/curl.h> #include "Driver.h" namespace org { @@ -1109,6 +1110,50 @@ Value expr_unescapeCsv(const std::vector<Value> &args) { return Value(result); } +Value expr_urlEncode(const std::vector<Value> &args) { + auto arg_0 = args[0].asString(); + CURL *curl = curl_easy_init(); + if (curl != nullptr) { + char *output = curl_easy_escape(curl, + arg_0.c_str(), + static_cast<int>(arg_0.length())); + if (output != nullptr) { + auto result = std::string(output); + curl_free(output); + curl_easy_cleanup(curl); + return Value(result); + } else { + curl_easy_cleanup(curl); + throw std::runtime_error("cURL failed to encode URL string"); + } + } else { + throw std::runtime_error("Failed to initialize cURL"); + } +} + +Value expr_urlDecode(const std::vector<Value> &args) { + auto arg_0 = args[0].asString(); + CURL *curl = curl_easy_init(); + if (curl != nullptr) { + int out_len; + char *output = curl_easy_unescape(curl, + arg_0.c_str(), + static_cast<int>(arg_0.length()), + &out_len); + if (output != nullptr) { + auto result = std::string(output, static_cast<unsigned long>(out_len)); + curl_free(output); + curl_easy_cleanup(curl); + return Value(result); + } else { + curl_easy_cleanup(curl); + throw std::runtime_error("cURL failed to decode URL string"); + } + } else { + throw std::runtime_error("Failed to initialize cURL"); + } +} + #ifdef EXPRESSION_LANGUAGE_USE_REGEX Value expr_replace(const std::vector<Value> &args) { @@ -1242,8 +1287,8 @@ Value expr_toRadix(const std::vector<Value> &args) { const char chars[] = "0123456789ab" - "cdefghijklmn" - "opqrstuvwxyz"; + "cdefghijklmn" + "opqrstuvwxyz"; std::string str_num; while (value) { @@ -1454,6 +1499,10 @@ Expression make_dynamic_function(const std::string &function_name, return make_dynamic_function_incomplete<expr_escapeCsv>(function_name, args, 0); } else if (function_name == "unescapeCsv") { return make_dynamic_function_incomplete<expr_unescapeCsv>(function_name, args, 0); + } else if (function_name == "urlEncode") { + return make_dynamic_function_incomplete<expr_urlEncode>(function_name, args, 0); + } else if (function_name == "urlDecode") { + return make_dynamic_function_incomplete<expr_urlDecode>(function_name, args, 0); #ifdef EXPRESSION_LANGUAGE_USE_REGEX } else if (function_name == "replace") { return make_dynamic_function_incomplete<expr_replace>(function_name, args, 2); http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/7958bd11/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 421d263..40713c2 100644 --- a/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp +++ b/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp @@ -1144,3 +1144,27 @@ TEST_CASE("Encode Decode CSV", "[expressionEncodeDecodeCSV]") { // NOLINT flow_file_a->addAttribute("message", "Zero > One < \"two!\" & 'true'"); REQUIRE("Zero > One < \"two!\" & 'true'" == expr({flow_file_a}).asString()); } + +TEST_CASE("Encode URL", "[expressionEncodeURL]") { // NOLINT + auto expr = expression::compile("${message:urlEncode()}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("message", "some value with spaces"); + REQUIRE("some%20value%20with%20spaces" == expr({flow_file_a}).asString()); +} + +TEST_CASE("Decode URL", "[expressionDecodeURL]") { // NOLINT + auto expr = expression::compile("${message:urlDecode()}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("message", "some%20value%20with%20spaces"); + REQUIRE("some value with spaces" == expr({flow_file_a}).asString()); +} + +TEST_CASE("Encode Decode URL", "[expressionEncodeDecodeURL]") { // NOLINT + auto expr = expression::compile("${message:urlEncode():urlDecode()}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("message", "some value with spaces"); + REQUIRE("some value with spaces" == expr({flow_file_a}).asString()); +}
