-
Notifications
You must be signed in to change notification settings - Fork 207
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ADD: ParseTreeCoercionService and tests #1153
ADD: ParseTreeCoercionService and tests #1153
Conversation
Signed-off-by: Chen <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @chenqi0805 for breaking up the larger PR. Overall everything is looking great. I added a few comments, the only requested change is enhancing the testCoerceTerminalNodeEscapedJsonPointerType()
test case.
...expression/src/main/java/org/opensearch/dataprepper/expression/ParseTreeCoercionService.java
Outdated
Show resolved
Hide resolved
|
||
@Named | ||
class ParseTreeCoercionService { | ||
public Object coerceTerminalNode(final TerminalNode node, final Event event) throws ParseTreeCoercionException { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like this method will only handle terminal nodes within a primary/literal context. If primary terminal nodes are the only use case, consider a more descriptive name. One suggestion I have is coercePrimaryNode
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sbayer55, would that new name suggest this method supports setInitializer and variableIdentifier as defined in the grammar language for primary? Are you also suggesting we are missing some coercion functionality or do we just need a better name?
primary
: jsonPointer
| variableIdentifier
| setInitializer
| literal
;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm suggesting if a better name is possible. Considering operator tokens (==
, in
...) are TerminalNode
s but this method could not coerce them.
...ession/src/test/java/org/opensearch/dataprepper/expression/ParseTreeCoercionServiceTest.java
Outdated
Show resolved
Hide resolved
} | ||
|
||
@Test | ||
void testCoerceTerminalNodeEscapedJsonPointerType() throws ParseTreeCoercionException { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for explicitly testing JsonPointer and EscapedJsonPointer! Within testCoerceTerminalNodeEscapedJsonPointerType()
consider using special characters in your json pointer path. Some of the trickier characters are ~
, \
, /
, and
.
...expression/src/main/java/org/opensearch/dataprepper/expression/ParseTreeCoercionService.java
Show resolved
Hide resolved
* @since 1.3 | ||
* Exception thrown by {@link ParseTreeCoercionService} methods. | ||
*/ | ||
public class ParseTreeCoercionException extends Exception { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From a caller's perspective, the parse tree doesn't mean much. I think we could use a better name here. Perhaps ExpressionCoercionException
.
|
||
/** | ||
* @since 1.3 | ||
* Exception thrown by {@link ParseTreeCoercionService} methods. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In line with my other comment on this exception, please update the Javadocs to explain what this means to the caller/client.
assertThrows(ParseTreeCoercionException.class, () -> objectUnderTest.coerce(testObj, String.class)); | ||
} | ||
|
||
private Event createTestEvent(final Object data) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could decouple your tests from JacksonEvent
and probably simplify these tests by using a mock. But, I can accept the current code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Turns out I do have to mock Event in order to test tricky characters in escape jsonpointer which is not currently supported by JacksonEvent.
@Test | ||
void testCoerceTerminalNodeIntegerType() throws ParseTreeCoercionException { | ||
when(token.getType()).thenReturn(DataPrepperExpressionParser.Integer); | ||
final Integer testInteger = 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I strongly recommend the use of random numbers here (within a decent range). It is quite easy to write against a test with a value like 1
.
Alternatively, you can make this a @ParameterizedTest
and verify against a few values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good suggestiong @dlvenable!
As a best practice, to guarantee consistent test results, I recommend avoiding random values in test cases. Either method is acceptable in this use case and both are used throught Data Preppers test code.
@Test | ||
void testCoerceTerminalNodeFloatType() throws ParseTreeCoercionException { | ||
when(token.getType()).thenReturn(DataPrepperExpressionParser.Float); | ||
final Float testFloat = 1.0f; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, please do not test against 1.0
.
@Test | ||
void testCoerceFailure() { | ||
final Object testObj = false; | ||
assertThrows(ParseTreeCoercionException.class, () -> objectUnderTest.coerce(testObj, String.class)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test might be more readable if you provided a mock(Object.class)
instead of false
. Presently, it seems that false
means something specific, but you are just creating a different type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately one can not mock getClass method since it is final. I will use TestObject here to indicate the difference.
Signed-off-by: Chen <[email protected]>
Signed-off-by: Chen <[email protected]>
Signed-off-by: Chen <[email protected]>
Signed-off-by: Chen <[email protected]>
Signed-off-by: Chen <[email protected]>
Codecov Report
@@ Coverage Diff @@
## main #1153 +/- ##
=========================================
Coverage 91.89% 91.89%
Complexity 703 703
=========================================
Files 87 87
Lines 2037 2037
Branches 171 171
=========================================
Hits 1872 1872
Misses 122 122
Partials 43 43 Continue to review full report at Codecov.
|
} | ||
|
||
@ParameterizedTest | ||
@MethodSource("provideKeys") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for adding this!
final String nodeStringValue = node.getText(); | ||
switch (nodeType) { | ||
case DataPrepperExpressionParser.EscapedJsonPointer: | ||
return event.get(nodeStringValue.substring(1, nodeStringValue.length() - 1), Object.class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: reading the code here I understand what is happening but not why. It might be a good idea to include a variable or create a private function.
final String jsonPointerWithoutQuotes = nodeStringValue.substring(1, nodeStringValue.length() - 1);
// or
private String removeQuotesFrom(final String text) {
return nodeStringValue.substring(1, nodeStringValue.length() - 1)
}
Signed-off-by: Chen <[email protected]>
Signed-off-by: Chen [email protected]
Description
As a breakdown of #1145 , this PR adds ParseTreeCoercionService that is responsible for type checking and casting on both terminal node in the ParseTree and evaluation result.
Issues Resolved
Contributes to #1003
Check List
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.