Skip to content

Commit 26e3d80

Browse files
committed
update antlr grammar, parser and add expression tests
Signed-off-by: Shenoy Pratik <sgguruda@amazon.com>
1 parent fc0b4c2 commit 26e3d80

4 files changed

Lines changed: 52 additions & 34 deletions

File tree

data-prepper-expression/src/main/antlr/DataPrepperExpression.g4

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ Function
160160

161161
fragment
162162
FunctionArgs
163-
: (FunctionArg SPACE* COMMA SPACE*)* SPACE* FunctionArg
163+
: ((FunctionArg SPACE* COMMA SPACE*)* SPACE* FunctionArg)?
164164
;
165165

166166
fragment

data-prepper-expression/src/main/java/org/opensearch/dataprepper/expression/ParseTreeCoercionService.java

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,17 @@ class ParseTreeCoercionService {
2424
private Function<Object, Object> convertLiteralType;
2525

2626
@Inject
27-
public ParseTreeCoercionService(final Map<Class<? extends Serializable>, Function<Object, Object>> literalTypeConversions, ExpressionFunctionProvider expressionFunctionProvider) {
27+
public ParseTreeCoercionService(
28+
final Map<Class<? extends Serializable>, Function<Object, Object>> literalTypeConversions,
29+
ExpressionFunctionProvider expressionFunctionProvider) {
2830
this.literalTypeConversions = literalTypeConversions;
2931
convertLiteralType = (value) -> {
30-
if (literalTypeConversions.containsKey(value.getClass())) {
31-
return literalTypeConversions.get(value.getClass()).apply(value);
32-
} else {
33-
throw new ExpressionCoercionException("Unsupported type for value " + value);
34-
}
35-
};
32+
if (literalTypeConversions.containsKey(value.getClass())) {
33+
return literalTypeConversions.get(value.getClass()).apply(value);
34+
} else {
35+
throw new ExpressionCoercionException("Unsupported type for value " + value);
36+
}
37+
};
3638
this.expressionFunctionProvider = expressionFunctionProvider;
3739
}
3840

@@ -44,31 +46,40 @@ public Object coercePrimaryTerminalNode(final TerminalNode node, final Event eve
4446
final int funcNameIndex = nodeStringValue.indexOf("(");
4547
final String functionName = nodeStringValue.substring(0, funcNameIndex);
4648
final int argsEndIndex = nodeStringValue.indexOf(")", funcNameIndex);
47-
final String argsStr = nodeStringValue.substring(funcNameIndex+1, argsEndIndex);
48-
// Split at commas if there's no backslash before the commas, because commas can be part of a function parameter
49-
final String[] args = argsStr.split("(?<!\\\\),");
5049
List<Object> argList = new ArrayList<>();
51-
for (final String arg: args) {
52-
String trimmedArg = arg.trim();
53-
if (trimmedArg.charAt(0) == '/') {
54-
argList.add(trimmedArg);
55-
} else if (trimmedArg.charAt(0) == '"') {
56-
if (trimmedArg.length() < 2 || trimmedArg.charAt(trimmedArg.length()-1) != '"') {
57-
throw new RuntimeException("Invalid string argument: check if any argument is missing a closing double quote or contains comma that's not escaped with `\\`.");
50+
51+
// Check if the function has at least one argument
52+
if(argsEndIndex > funcNameIndex + 1) {
53+
final String argsStr = nodeStringValue.substring(funcNameIndex + 1, argsEndIndex);
54+
// Split at commas if there's no backslash before the commas, because commas can
55+
// be part of a function parameter
56+
final String[] args = argsStr.split("(?<!\\\\),");
57+
58+
for (final String arg : args) {
59+
String trimmedArg = arg.trim();
60+
if (trimmedArg.charAt(0) == '/') {
61+
argList.add(trimmedArg);
62+
} else if (trimmedArg.charAt(0) == '"') {
63+
if (trimmedArg.length() < 2 || trimmedArg.charAt(trimmedArg.length() - 1) != '"') {
64+
throw new RuntimeException(
65+
"Invalid string argument: check if any argument is missing a closing double quote or contains comma that's not escaped with `\\`.");
66+
}
67+
argList.add(trimmedArg);
68+
} else {
69+
throw new RuntimeException("Unsupported type passed as function argument");
5870
}
59-
argList.add(trimmedArg);
60-
} else {
61-
throw new RuntimeException("Unsupported type passed as function argument");
6271
}
6372
}
73+
6474
return expressionFunctionProvider.provideFunction(functionName, argList, event, convertLiteralType);
6575
case DataPrepperExpressionParser.EscapedJsonPointer:
6676
final String jsonPointerWithoutQuotes = nodeStringValue.substring(1, nodeStringValue.length() - 1);
6777
return resolveJsonPointerValue(jsonPointerWithoutQuotes, event);
6878
case DataPrepperExpressionParser.JsonPointer:
6979
return resolveJsonPointerValue(nodeStringValue, event);
7080
case DataPrepperExpressionParser.String:
71-
final String nodeStringValueWithQuotesStripped = nodeStringValue.substring(1, nodeStringValue.length() - 1);
81+
final String nodeStringValueWithQuotesStripped = nodeStringValue.substring(1,
82+
nodeStringValue.length() - 1);
7283
return nodeStringValueWithQuotesStripped;
7384
case DataPrepperExpressionParser.Integer:
7485
Long longValue = Long.valueOf(nodeStringValue);
@@ -98,14 +109,15 @@ public <T> T coerce(final Object obj, Class<T> clazz) throws ExpressionCoercionE
98109
if (obj.getClass().isAssignableFrom(clazz)) {
99110
return (T) obj;
100111
}
101-
throw new ExpressionCoercionException("Unable to cast " + obj.getClass().getName() + " into " + clazz.getName());
112+
throw new ExpressionCoercionException(
113+
"Unable to cast " + obj.getClass().getName() + " into " + clazz.getName());
102114
}
103115

104116
private Object resolveJsonPointerValue(final String jsonPointer, final Event event) {
105117
final Object value = event.get(jsonPointer, Object.class);
106118
if (value == null) {
107119
return null;
108-
}
120+
}
109121
return convertLiteralType.apply(value);
110122
}
111123
}

data-prepper-expression/src/test/java/org/opensearch/dataprepper/expression/GenericExpressionEvaluator_ConditionalIT.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -248,8 +248,10 @@ private static Stream<Arguments> validExpressionArguments() {
248248
arguments("/name =~ \".*dataprepper-[0-9]+\"", event("{\"name\": \"dataprepper-abc\"}"), false),
249249
arguments("/name =~ \".*dataprepper-[0-9]+\"", event("{\"other\": \"dataprepper-abc\"}"), false),
250250
arguments("startsWith(\""+strValue+ UUID.randomUUID() + "\",/status)", event("{\"status\":\""+strValue+"\"}"), true),
251-
arguments("startsWith(\""+ UUID.randomUUID() +strValue+ "\",/status)", event("{\"status\":\""+strValue+"\"}"), false)
252-
);
251+
arguments("startsWith(\""+ UUID.randomUUID() +strValue+ "\",/status)", event("{\"status\":\""+strValue+"\"}"), false),
252+
arguments("getEventType() == \"event\"", longEvent, true),
253+
arguments("getEventType() == \"LOG\"", longEvent, false)
254+
);
253255
}
254256

255257
private static Stream<Arguments> invalidExpressionArguments() {
@@ -344,7 +346,10 @@ private static Stream<Arguments> invalidExpressionSyntaxArguments() {
344346
arguments("getMetadata(10)", tagEvent),
345347
arguments("getMetadata("+ testMetadataKey+ ")", tagEvent),
346348
arguments("getMetadata(\""+ testMetadataKey+")", tagEvent),
347-
arguments("cidrContains(/sourceIp,123)", event("{\"sourceIp\": \"192.0.2.3\"}"))
349+
arguments("cidrContains(/sourceIp,123)", event("{\"sourceIp\": \"192.0.2.3\"}")),
350+
arguments("getEventType() == \"test_event", tagEvent),
351+
arguments("getEventType() == test_event\"", tagEvent)
352+
348353
);
349354
}
350355

data-prepper-expression/src/test/java/org/opensearch/dataprepper/expression/GetEventTypeExpressionFunctionTest.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
import org.junit.jupiter.api.Test;
66
import static org.hamcrest.CoreMatchers.equalTo;
77
import static org.hamcrest.MatcherAssert.assertThat;
8+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
89
import static org.junit.jupiter.api.Assertions.assertThrows;
10+
import static org.mockito.Mockito.mock;
11+
import static org.mockito.Mockito.when;
912

1013
import java.util.List;
1114
import java.util.Map;
@@ -43,12 +46,10 @@ void testGetEventTypeThrowsExceptionWhenArgumentsProvided() {
4346
}
4447

4548
@Test
46-
void testGetEventTypeReturnsNullForNullEventType() {
47-
GetEventTypeExpressionFunction function = createObjectUnderTest();
48-
Event testEvent = createTestEvent(null);
49-
50-
Object result = function.evaluate(List.of(), testEvent, Function.identity());
51-
52-
assertThat(result, equalTo(null));
49+
void testGetEventTypeFunctionRegistered() {
50+
Event testEvent = createTestEvent("event");
51+
final GenericExpressionEvaluator evaluator = mock(GenericExpressionEvaluator.class);
52+
when(evaluator.evaluateConditional("getEventType() == \"event\"", testEvent)).thenReturn(true);
53+
assertDoesNotThrow(() -> evaluator.evaluateConditional("getEventType() == \"event\"", testEvent));
5354
}
5455
}

0 commit comments

Comments
 (0)