mirror of https://github.com/apache/nifi.git
NIFI-10754 Added getUri NIFI Expression Language function
This closes #6689 Signed-off-by: David Handermann <exceptionfactory@apache.org>
This commit is contained in:
parent
b69721cac9
commit
3a60013876
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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:admin@nifi.apache.org: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', 'dev@nifi.apache.org', '')}");
|
||||
assertInvalid("${getUri('http', 'nifi.apache.org', '/path/data', 'frag1')}");
|
||||
assertInvalid("${getUri('https', 'admin:admin@nifi.apache.org: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);
|
||||
}
|
||||
|
|
|
@ -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:admin@nifi.apache.org: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
|
||||
|
||||
|
|
Loading…
Reference in New Issue