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

exceptionfactory 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 3a60013876 NIFI-10754 Added getUri NIFI Expression Language function
3a60013876 is described below

commit 3a6001387681f79615ac5ce776e74548dd825005
Author: dan-s1 <[email protected]>
AuthorDate: Fri Nov 18 20:19:44 2022 +0000

    NIFI-10754 Added getUri NIFI Expression Language function
    
    This closes #6689
    
    Signed-off-by: David Handermann <[email protected]>
---
 .../language/antlr/AttributeExpressionLexer.g      |  2 +
 .../language/antlr/AttributeExpressionParser.g     |  5 +-
 .../language/compile/ExpressionCompiler.java       | 14 ++++
 .../evaluation/functions/GetUriEvaluator.java      | 75 ++++++++++++++++++++++
 .../literals/StringLiteralEvaluator.java           | 64 +++++++++---------
 .../attribute/expression/language/TestQuery.java   | 22 +++++++
 .../main/asciidoc/expression-language-guide.adoc   | 41 ++++++++++++
 7 files changed, 191 insertions(+), 32 deletions(-)

diff --git 
a/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionLexer.g
 
b/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionLexer.g
index 734794f3be..02901c4b4e 100644
--- 
a/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionLexer.g
+++ 
b/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionLexer.g
@@ -91,6 +91,7 @@ fragment EXP : ('e'|'E') ('+'|'-')? ('0'..'9')+ ;
 
 TRUE   : 'true';
 FALSE  : 'false';
+NULL    : 'null';
 
 //
 // FUNCTION NAMES
@@ -111,6 +112,7 @@ UUID : 'UUID';
 HOSTNAME : 'hostname'; // requires boolean arg: prefer FQDN
 NOW    : 'now';
 THREAD : 'thread';
+GET_URI : 'getUri';
 
 
 // 0 arg functions
diff --git 
a/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionParser.g
 
b/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionParser.g
index c71719b92c..56d900a69f 100644
--- 
a/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionParser.g
+++ 
b/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionParser.g
@@ -106,7 +106,7 @@ booleanFunctionRef : zeroArgBool | oneArgBool | 
multiArgBool;
 numberFunctionRef : zeroArgNum | oneArgNum | zeroOrTwoArgNum | oneOrTwoArgNum 
| zeroOrOneOrTwoArgNum;
 
 anyArg : WHOLE_NUMBER | DECIMAL | numberFunctionRef | STRING_LITERAL | 
zeroArgString | oneArgString | twoArgString | fiveArgString | booleanLiteral | 
zeroArgBool | oneArgBool | multiArgBool
-                | expression | parameterReference;
+                | expression | parameterReference | NULL;
 
 stringArg : STRING_LITERAL | zeroArgString | oneArgString | twoArgString | 
expression;
 functionRef : stringFunctionRef | booleanFunctionRef | numberFunctionRef;
@@ -137,7 +137,8 @@ booleanLiteral : TRUE | FALSE;
 zeroArgStandaloneFunction : (IP | UUID | NOW | NEXT_INT | HOSTNAME | THREAD | 
RANDOM) LPAREN! RPAREN!;
 oneArgStandaloneFunction : ((TO_LITERAL | MATH | GET_STATE_VALUE)^ LPAREN! 
anyArg RPAREN!) |
                            (HOSTNAME^ LPAREN! booleanLiteral RPAREN!);
-standaloneFunction : zeroArgStandaloneFunction | oneArgStandaloneFunction;
+sevenArgStandaloneFunction : GET_URI^ LPAREN! anyArg COMMA! anyArg COMMA! 
anyArg COMMA! anyArg COMMA! anyArg COMMA! anyArg COMMA! anyArg RPAREN!;
+standaloneFunction : zeroArgStandaloneFunction | oneArgStandaloneFunction | 
sevenArgStandaloneFunction;
 
 attributeRefOrFunctionCall     : (attributeRef | standaloneFunction | 
parameterReference);
 
diff --git 
a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/compile/ExpressionCompiler.java
 
b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/compile/ExpressionCompiler.java
index 05bc23a2ac..f5e1e74195 100644
--- 
a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/compile/ExpressionCompiler.java
+++ 
b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/compile/ExpressionCompiler.java
@@ -117,6 +117,7 @@ import 
org.apache.nifi.attribute.expression.language.evaluation.functions.ToStri
 import 
org.apache.nifi.attribute.expression.language.evaluation.functions.ToUpperEvaluator;
 import 
org.apache.nifi.attribute.expression.language.evaluation.functions.EvaluateELStringEvaluator;
 import 
org.apache.nifi.attribute.expression.language.evaluation.functions.TrimEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.functions.GetUriEvaluator;
 import 
org.apache.nifi.attribute.expression.language.evaluation.functions.UrlDecodeEvaluator;
 import 
org.apache.nifi.attribute.expression.language.evaluation.functions.UrlEncodeEvaluator;
 import 
org.apache.nifi.attribute.expression.language.evaluation.functions.UuidEvaluator;
@@ -152,6 +153,8 @@ import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionLexer.TO_MICROS;
 import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionLexer.TO_NANOS;
@@ -187,6 +190,7 @@ import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpre
 import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.FROM_RADIX;
 import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.GET_DELIMITED_FIELD;
 import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.GET_STATE_VALUE;
+import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.GET_URI;
 import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.GREATER_THAN;
 import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.GREATER_THAN_OR_EQUAL;
 import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.HASH;
@@ -217,6 +221,7 @@ import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpre
 import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.NOT;
 import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.NOT_NULL;
 import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.NOW;
+import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.NULL;
 import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.OR;
 import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.PAD_LEFT;
 import static 
org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.PAD_RIGHT;
@@ -1202,6 +1207,8 @@ public class ExpressionCompiler {
             case TRUE:
             case FALSE:
                 return buildBooleanEvaluator(tree);
+            case NULL:
+                return newStringLiteralEvaluator(null);
             case UUID: {
                 return addToken(new UuidEvaluator(), "uuid");
             }
@@ -1267,6 +1274,13 @@ public class ExpressionCompiler {
                 evaluators.add(eval);
                 return eval;
             }
+            case GET_URI: {
+                List<Evaluator<String>> uriArgs = Stream.iterate(0, i -> i + 1)
+                        .limit(tree.getChildCount())
+                        .map(num -> 
toStringEvaluator(buildEvaluator(tree.getChild(num))))
+                        .collect(Collectors.toList());
+                return addToken(new GetUriEvaluator(uriArgs), "getUri");
+            }
             default:
                 throw new 
AttributeExpressionLanguageParsingException("Unexpected token: " + 
tree.toString());
         }
diff --git 
a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/GetUriEvaluator.java
 
b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/GetUriEvaluator.java
new file mode 100644
index 0000000000..011b498910
--- /dev/null
+++ 
b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/GetUriEvaluator.java
@@ -0,0 +1,75 @@
+/*
+ * 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.attribute.expression.language.evaluation.functions;
+
+import org.apache.nifi.attribute.expression.language.EvaluationContext;
+import org.apache.nifi.attribute.expression.language.evaluation.Evaluator;
+import org.apache.nifi.attribute.expression.language.evaluation.QueryResult;
+import 
org.apache.nifi.attribute.expression.language.evaluation.StringEvaluator;
+import 
org.apache.nifi.attribute.expression.language.evaluation.StringQueryResult;
+import 
org.apache.nifi.attribute.expression.language.exception.AttributeExpressionLanguageException;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class GetUriEvaluator extends StringEvaluator {
+
+    private final List<Evaluator<String>> uriArgs;
+
+    public GetUriEvaluator(List<Evaluator<String>> uriArgs) {
+        this.uriArgs = uriArgs;
+    }
+
+    @Override
+    public QueryResult<String> evaluate(EvaluationContext evaluationContext) {
+        List<String> args = uriArgs.stream()
+                .map(uriArg -> uriArg.evaluate(evaluationContext).getValue())
+                .collect(Collectors.toList());
+
+        try {
+            if (args.size() == 7) {
+                final String scheme = args.get(0);
+                final String userInfo = args.get(1);
+                final String host = args.get(2);
+                final int port = getPort(args.get(3));
+                final String path = args.get(4);
+                final String query = args.get(5);
+                final String fragment = args.get(6);
+                final URI uri = new URI(scheme, userInfo, host, port, path, 
query, fragment);
+                return new StringQueryResult(uri.toString());
+            }
+            throw new AttributeExpressionLanguageException("Could not evaluate 
'getUri' function with " + args.size() + " argument(s)");
+        } catch (URISyntaxException use) {
+            throw new AttributeExpressionLanguageException("Could not evaluate 
'getUri' function with argument(s) " + args, use);
+        }
+    }
+
+    private int getPort(String portArg) {
+        try {
+            return Integer.parseInt(portArg);
+        } catch (NumberFormatException nfe) {
+            throw new AttributeExpressionLanguageException("Could not evaluate 
'getUri' function with argument '"
+                    + portArg + "' which is not a number", nfe);
+        }
+    }
+    @Override
+    public Evaluator<?> getSubjectEvaluator() {
+        return null;
+    }
+}
diff --git 
a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/literals/StringLiteralEvaluator.java
 
b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/literals/StringLiteralEvaluator.java
index 410449b59a..c3fffde0fb 100644
--- 
a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/literals/StringLiteralEvaluator.java
+++ 
b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/literals/StringLiteralEvaluator.java
@@ -27,40 +27,44 @@ public class StringLiteralEvaluator extends StringEvaluator 
{
     private final String value;
 
     public StringLiteralEvaluator(final String value) {
-        // need to escape characters after backslashes
-        final StringBuilder sb = new StringBuilder();
-        boolean lastCharIsBackslash = false;
-        for (int i = 0; i < value.length(); i++) {
-            final char c = value.charAt(i);
+        if(value == null) {
+            this.value = null;
+        } else {
+            // need to escape characters after backslashes
+            final StringBuilder sb = new StringBuilder();
+            boolean lastCharIsBackslash = false;
+            for (int i = 0; i < value.length(); i++) {
+                final char c = value.charAt(i);
 
-            if (lastCharIsBackslash) {
-                switch (c) {
-                    case 'n':
-                        sb.append("\n");
-                        break;
-                    case 'r':
-                        sb.append("\r");
-                        break;
-                    case '\\':
-                        sb.append("\\");
-                        break;
-                    case 't':
-                        sb.append("\\t");
-                        break;
-                    default:
-                        sb.append("\\").append(c);
-                        break;
-                }
+                if (lastCharIsBackslash) {
+                    switch (c) {
+                        case 'n':
+                            sb.append("\n");
+                            break;
+                        case 'r':
+                            sb.append("\r");
+                            break;
+                        case '\\':
+                            sb.append("\\");
+                            break;
+                        case 't':
+                            sb.append("\\t");
+                            break;
+                        default:
+                            sb.append("\\").append(c);
+                            break;
+                    }
 
-                lastCharIsBackslash = false;
-            } else if (c == '\\') {
-                lastCharIsBackslash = true;
-            } else {
-                sb.append(c);
+                    lastCharIsBackslash = false;
+                } else if (c == '\\') {
+                    lastCharIsBackslash = true;
+                } else {
+                    sb.append(c);
+                }
             }
-        }
 
-        this.value = sb.toString();
+            this.value = sb.toString();
+        }
     }
 
     @Override
diff --git 
a/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestQuery.java
 
b/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestQuery.java
index 4db4f0a943..55d252f7a8 100644
--- 
a/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestQuery.java
+++ 
b/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestQuery.java
@@ -28,6 +28,7 @@ import org.apache.nifi.parameter.Parameter;
 import org.apache.nifi.parameter.ParameterDescriptor;
 import org.apache.nifi.parameter.ParameterLookup;
 import org.apache.nifi.registry.VariableRegistry;
+import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
@@ -2398,6 +2399,27 @@ public class TestQuery {
         verifyEquals("${nullAttr:isJson()}", attributes, false);
     }
 
+    @Test
+    void testGetUri() {
+        verifyEquals("${getUri('https', 'admin:admin', 'nifi.apache.org', 
'1234', '/path/data ', 'key=value &key2=value2', 'frag1')}",
+                null, 
"https://admin:[email protected]:1234/path/data%20?key=value%20&key2=value2#frag1";);
+        verifyEquals("${getUri('https', null, 'nifi.apache.org', 8443, 
'/docs.html', null, null)}",
+                null, "https://nifi.apache.org:8443/docs.html";);
+        verifyEquals("${getUri('http', null, 'nifi.apache.org', -1, 
'/docs.html', null, null)}",
+                null, "http://nifi.apache.org/docs.html";);
+
+        assertInvalid("${getUri()}");
+        
assertInvalid("${getUri('http://nifi.apache.org:1234/path/data?key=value&key2=value2#frag1')}");
+        assertInvalid("${getUri('https', 'admin:admin')}");
+        assertInvalid("${getUri('mailto', '[email protected]', '')}");
+        assertInvalid("${getUri('http', 'nifi.apache.org', '/path/data', 
'frag1')}");
+        assertInvalid("${getUri('https', 'admin:[email protected]:1234', 
'/path/data ', 'key=value&key2=value2', 'frag1')}");
+
+        AttributeExpressionLanguageException thrown = 
assertThrows(AttributeExpressionLanguageException.class,
+                () -> verifyEquals("${getUri('https', 'admin:admin', 
'nifi.apache.org', 'notANumber', '/path/data ', 'key=value&key2=value2', 
'frag1')}", null, ""));
+        Assertions.assertEquals("Could not evaluate 'getUri' function with 
argument 'notANumber' which is not a number", thrown.getMessage());
+    }
+
     private void verifyEquals(final String expression, final Map<String, 
String> attributes, final Object expectedResult) {
         verifyEquals(expression,attributes, null, ParameterLookup.EMPTY, 
expectedResult);
     }
diff --git a/nifi-docs/src/main/asciidoc/expression-language-guide.adoc 
b/nifi-docs/src/main/asciidoc/expression-language-guide.adoc
index 8fab2761b1..fd3dd94ef5 100644
--- a/nifi-docs/src/main/asciidoc/expression-language-guide.adoc
+++ b/nifi-docs/src/main/asciidoc/expression-language-guide.adoc
@@ -2616,6 +2616,47 @@ names begin with the letter `a`.
 
 
 
+
+[.function]
+=== getUri
+
+*Description*: [.description]#Returns a URI compliant with RFC 2396. This 
includes encoding non-US-ASCII characters and
+quoting illegal characters with octets. This expression utilizes the
+link:https://docs.oracle.com/javase/8/docs/api/java/net/URI.html[java.net.URI^]
 class to build a URI.
+As described in the API, a hierarchical URI consists of seven components 
represented with the following types:#
+|============================================================================
+| Component   | Type
+| `scheme`    | `String`
+| `user-info` | `String`
+| `host`      | `String`
+| `port`      | `int`
+| `path`      | `String`
+| `query`     | `String`
+| `fragment`  | `String`
+|============================================================================
+
+See the API for more details on character categories, encoding and quoting.
+
+*Subject Type*: [.subjectless]#No Subject#
+
+*Arguments*: This expression takes seven arguments.
+
+- [.argName]#_scheme_#, [.argName]#_userinfo_#, [.argName]#_host_#, 
[.argName]#_port_#, [.argName]#_path_#, [.argName]#_query_#, 
[.argName]#_fragment_#
+
+*NOTE:* Any component of the new URI may be left undefined by passing 
[.argName]#_null_# for the corresponding parameter or, in the case of the port 
parameter, by passing [.argName]#_-1_#.
+
+*Return Type*: [.returnType]#String#
+
+*Examples*
+The following examples detail how this expression can be invoked:
+
+|============================================================================
+| Expression | Value
+|`${getUri('https', 'admin:admin', 'nifi.apache.org', '1234', '/path/data ', 
'key=value &key2=value2', 'frag1')}` | 
`https://admin:[email protected]:1234/path/data%20?key=value%20&key2=value2#frag1`
+|`${getUri('https', null, 'nifi.apache.org', 8443, '/docs.html', null, null)}` 
                                   | `https://nifi.apache.org:8443/docs.html`
+|`${getUri('http', null, 'nifi.apache.org', -1, '/docs.html', null, null)}`    
                                   | `http://nifi.apache.org/docs.html`
+|============================================================================
+
 [[multi]]
 == Evaluating Multiple Attributes
 

Reply via email to