-
Notifications
You must be signed in to change notification settings - Fork 217
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
provides first implementation of event models and closes #434 #435
Conversation
* | ||
* @since 1.2 | ||
*/ | ||
public class JacksonEventImpl implements Event { |
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'd like to drop the Impl
from this name and make it a JacksonEvent
.
|
||
private final ObjectMapper mapper; | ||
|
||
public JacksonEventImpl(final Builder builder) { |
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 should be private since the Builder
creates it.
} | ||
} | ||
|
||
private String toJsonPointerExpression(final String key) { |
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 might be nice to use the JsonPointer
class here so it is quite clear what it is returning. But, these are private, so it's not critical.
this.attributes = builder.attributes == null ? new ImmutableMap.Builder<String, Object>().build() : ImmutableMap.copyOf(builder.attributes); | ||
} | ||
|
||
@Override public String getEventType() { |
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.
Generally method attributes are put on their own lines.
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.
will do
* | ||
* @since 1.2 | ||
*/ | ||
public class EventMetadataImpl implements EventMetadata { |
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.
Is there another name we can use here? I try to avoid Impl
naming in public classes, and even more so for model classes.
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'd suggest DefaultEventMetadata
or something like that.
|
||
@Test | ||
public void testBuild_withoutEmptyEventType_throwsAnException() { | ||
assertThrows(IllegalArgumentException.class, () -> new EventMetadataImpl.Builder().withEventType("").build()); |
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 is best to reduce what is in the assertThrows
so that you are sure you are testing the correct method fails.
An exception from either withType()
or build()
will cause the exception. Which is it that we are validating?
You can extract the Builder into a variable and then reduce it to: assertThrows(IllegalArgumentException.class(), builder.build());
assertThrowsForKeyCheck(IllegalArgumentException.class, "bogusKey&"); | ||
} | ||
|
||
@Test |
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 can drop the assertThrowsForKeyCheck
and consolidate the code using JUnit 5's @ParameterizedTest
.
It would look something like this:
@ParameterizedTest
@ValueSource(strings = ["", "bogusKey&" /* ... others */])
void testKey_withBadInput_throwsException(String badKey) {
assertThrows(expectedThrowable, () -> event.put(badKey, UUID.randomUUID()));
assertThrows(expectedThrowable, () -> event.get(badKey, String.class));
assertThrows(expectedThrowable, () -> event.delete(badKey));
}
It will not work for null
. You will still need its own test.
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 thought about doing this but decided against it because I could make a descriptive test name for each scenario. I can adopt the proposal if this is preferred
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 can make the keys descriptive instead so I can still provide context
|
||
public JacksonEventImpl(final Builder builder) { | ||
|
||
this.mapper = new ObjectMapper(); |
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.
We should probably assign this to a private static ObjectMapper
so that we don't need to recreate. Even if we pass it in the future, most instances can still share a common one when it is not injected.
@@ -0,0 +1,117 @@ | |||
package com.amazon.dataprepper.model.event; | |||
|
|||
import org.junit.Before; |
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.
Please use JUnit 5 for all new tests: org.junit.jupiter.api.BeforeEach
and org.junit.jupiter.api.Test
* | ||
* @since 1.2 | ||
*/ | ||
public class EventMetadataImpl implements EventMetadata { |
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'd suggest DefaultEventMetadata
or something like that.
recursivelyTraverseAndPut(jsonNode, keys, value); | ||
} | ||
|
||
private void recursivelyTraverseAndPut(final JsonNode node, final LinkedList<String> keys, final Object value) { |
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.
While safe for right now, you might want to consider the iterative version of traverse so we won't blow the stack.
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.
That's a good call. That would require us to enforce a limitation on how many nested structures an event would support. I would prefer introducing this sooner rather than later. How many iterations should data prepper support?
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.
Or we could run something indefinitely until there are no more nodes.
I can make it iterative.
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 think right now we should be fine without a limit; but if we want to set one, I'd set it to a depth of 100
or something fairly large that people should not be nesting too.
…roject#434 Signed-off-by: Christopher Manning <[email protected]>
Signed-off-by: Christopher Manning <[email protected]>
* @param eventType the event type | ||
* @since 1.2 | ||
*/ | ||
public Builder withEventType(final String eventType) { |
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.
Do we want eventType to be an enum value so that we clearly define the different types of events within Data Prepper?
private void checkKeyArgument(final String key) { | ||
checkNotNull(key, "key cannot be null"); | ||
checkArgument(!key.isEmpty(), "key cannot be an empty string"); | ||
checkArgument(key.matches("^([a-zA-Z0-9]([\\w -]*[a-zA-Z0-9])+\\.?)+(?<!\\.)$"), "key must contain only alphanumeric chars with .- and must follow dot notation (ie. 'field.to.key')"); |
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 implementation concerns me for a couple of reasons.
- What about key values that have underscores?
- The dot notation is nice, and while it can be the default, we need a way to use the methods here (get, put, delete) with dot notation explicitly turned off. Dot notation does not exist in Logstash (they have a nested syntax of [outer][inner]), but dots are still used in key values fairly often as literal dots, and we should have a boolean option for this model to get keys with dots, and it is likely that I will need to add a configuration option in Grok to enable/disable dot notation.
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 is a good question that we need to resolve before we release 1.2.
I wouldn't want to take the [outer][inner]
syntax. But, maybe we should consider some of these options:
- Use the Jackson JsonPointer syntax of
/
instead of.
. - Support both methods like
getPath
vs.getKey
to differentiate the two. - Other options?
I created this issue to track this: #450
I'm good with approving this PR and we can move the discussion into that ticket.
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.
We can support a translation from the Logstash's nested structure to the dot notation in the converter.
Regarding the keys being able to use dots, I think this is a good call out. I will look to add a way for our users to escape the dot sintax.
Signed-off-by: Christopher Manning [email protected]
Description
toJsonNode
totoJsonString
Issues Resolved
#434
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.