Skip to content

Commit 4e9f84d

Browse files
committed
fix(expressions): fix equals SCeL expression should support null argument (#187)
Resolved: #187
1 parent e9cd483 commit 4e9f84d

File tree

5 files changed

+109
-9
lines changed

5 files changed

+109
-9
lines changed

connect-file-pulse-api/src/main/java/io/streamthoughts/kafka/connect/filepulse/data/TypedValue.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@
3333
*/
3434
public class TypedValue implements GettableByType {
3535

36+
public static TypedValue none() {
37+
return new TypedValue(Schema.none(), null);
38+
}
39+
3640
private final Object value;
3741
private final SchemaSupplier schema;
3842

connect-file-pulse-expression/src/main/java/io/streamthoughts/kafka/connect/filepulse/expression/function/ExecutionContext.java

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,23 +44,44 @@ void addArgument(final String name, final int index, final TypedValue value) {
4444
this.argumentByIndex.put(index, value);
4545
}
4646

47+
/**
48+
* Retrieves the argument to which the specified index is mapped.
49+
*
50+
* @param index the argument index.
51+
* @return the {@link TypedValue}.
52+
* @throws IndexOutOfBoundsException if the given index is out of range.
53+
*/
4754
public TypedValue get(final int index) {
48-
return Optional.ofNullable(argumentByIndex.get(index)).orElseThrow(IndexOutOfBoundsException::new);
55+
if (!argumentByIndex.containsKey(index)) {
56+
throw new IndexOutOfBoundsException(index);
57+
}
58+
return Optional.ofNullable(argumentByIndex.get(index)).orElse(TypedValue.none());
4959
}
5060

61+
/**
62+
* Retrieves the argument to which the specified name is mapped.
63+
*
64+
* @param name the argument name.
65+
* @return the {@link TypedValue}.
66+
*/
5167
public TypedValue get(final String name) {
52-
return argumentByName.get(name);
68+
return Optional.ofNullable(argumentByName.get(name)).orElse(TypedValue.none());
5369
}
5470

5571
public List<TypedValue> get(final int index, final int to) {
5672
return values().subList(index, to);
5773
}
5874

59-
75+
/**
76+
* @return the number of arguments.
77+
*/
6078
public int size() {
6179
return argumentByName.size();
6280
}
6381

82+
/**
83+
* @return values for all arguments.
84+
*/
6485
public List<TypedValue> values() {
6586
return new ArrayList<>(argumentByIndex.values());
6687
}

connect-file-pulse-expression/src/main/java/io/streamthoughts/kafka/connect/filepulse/expression/function/conditions/Equals.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ public TypedValue invoke(final ExecutionContext context) throws ExpressionExcept
6161
final TypedValue left = context.get(OBJECT_L_ARG);
6262
final TypedValue right = context.get(OBJECT_R_ARG);
6363

64+
if (left.isNull() && right.isNull())
65+
return TypedValue.bool(true);
66+
67+
if (left.isNull() || right.isNull())
68+
return TypedValue.bool(false);
69+
6470
// attempt to convert argument value to the field value before applying equality.
6571
final Object leftValue = left.value();
6672
final Object rightValue = right.as(left.type()).value();

connect-file-pulse-expression/src/test/java/io/streamthoughts/kafka/connect/filepulse/expression/function/FunctionsTest.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -134,12 +134,6 @@ public void should_execute_extract_array() {
134134
Assert.assertEquals("two", expression.readValue(context, TypedValue.class).getString());
135135
}
136136

137-
@Test
138-
public void should_execute_equals_function() {
139-
Expression expression = parseExpression("{{ equals('foo', 'foo') }}");
140-
Assert.assertTrue(expression.readValue(EMPTY_CONTEXT, TypedValue.class).value());
141-
}
142-
143137
@Test
144138
public void should_execute_end_with_function() {
145139
Expression expressionTrue = parseExpression("{{ ends_with('FOO-suffix', 'suffix') }}");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright 2019-2020 StreamThoughts.
3+
*
4+
* Licensed to the Apache Software Foundation (ASF) under one or more
5+
* contributor license agreements. See the NOTICE file distributed with
6+
* this work for additional information regarding copyright ownership.
7+
* The ASF licenses this file to You under the Apache License, Version 2.0
8+
* (the "License"); you may not use this file except in compliance with
9+
* the License. You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
package io.streamthoughts.kafka.connect.filepulse.expression.function.conditions;
20+
21+
import io.streamthoughts.kafka.connect.filepulse.data.TypedValue;
22+
import io.streamthoughts.kafka.connect.filepulse.expression.Expression;
23+
import io.streamthoughts.kafka.connect.filepulse.expression.StandardEvaluationContext;
24+
import org.junit.Assert;
25+
import org.junit.Test;
26+
27+
import java.util.HashMap;
28+
import java.util.Map;
29+
30+
import static io.streamthoughts.kafka.connect.filepulse.expression.parser.ExpressionParsers.parseExpression;
31+
32+
public class EqualsTest {
33+
34+
private static final StandardEvaluationContext EMPTY_CONTEXT = new StandardEvaluationContext(new Object());
35+
36+
@Test
37+
public void test_equals_given_two_equals_strings() {
38+
// Given
39+
Expression expression = parseExpression("{{ equals('foo', 'foo') }}");
40+
41+
// When
42+
final TypedValue result = expression.readValue(EMPTY_CONTEXT, TypedValue.class);
43+
44+
// Then
45+
Assert.assertTrue(result.value());
46+
}
47+
48+
@Test
49+
public void test_equals_return_false_given_a_null_value() {
50+
// Given
51+
Expression expression = parseExpression("{{ equals($value, 'foo') }}");
52+
53+
final StandardEvaluationContext context = new StandardEvaluationContext(TypedValue.string(null));
54+
55+
// When
56+
final TypedValue result = expression.readValue(context, TypedValue.class);
57+
58+
// Then
59+
Assert.assertFalse(result.value());
60+
}
61+
62+
@Test
63+
public void test_equals_return_true_given_null_values() {
64+
// Given
65+
Expression expression = parseExpression("{{ equals($value, null) }}");
66+
67+
final StandardEvaluationContext context = new StandardEvaluationContext(TypedValue.string(null));
68+
69+
// When
70+
final TypedValue result = expression.readValue(context, TypedValue.class);
71+
72+
// Then
73+
Assert.assertTrue(result.value());
74+
}
75+
}

0 commit comments

Comments
 (0)