Skip to content
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

Merged
merged 2 commits into from
Oct 19, 2021

Conversation

cmanning09
Copy link
Contributor

@cmanning09 cmanning09 commented Oct 15, 2021

Signed-off-by: Christopher Manning [email protected]

Description

  • Implemented Event and EventMetadata interfaces.
  • Each implementation contains a builder.
  • Event implementation uses Jackson Library to manage the underlying data
  • Provided 100% unit test coverage for each implementation.
  • Updated Event interface toJsonNode to toJsonString
  • Added licenses to interfaces.

Issues Resolved

#434

Check List

  • New functionality includes testing.
    • All tests pass
  • New functionality has been documented.
    • New functionality has javadoc added
  • Commits are signed per the DCO using --signoff

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.

*
* @since 1.2
*/
public class JacksonEventImpl implements Event {
Copy link
Member

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) {
Copy link
Member

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) {
Copy link
Member

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() {
Copy link
Member

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.

Copy link
Contributor Author

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 {
Copy link
Member

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.

Copy link
Contributor

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());
Copy link
Member

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
Copy link
Member

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.

Copy link
Contributor Author

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

Copy link
Contributor Author

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();
Copy link
Member

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;
Copy link
Member

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 {
Copy link
Contributor

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) {
Copy link
Contributor

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.

Copy link
Contributor Author

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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.

* @param eventType the event type
* @since 1.2
*/
public Builder withEventType(final String eventType) {
Copy link
Member

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')");
Copy link
Member

@graytaylor0 graytaylor0 Oct 19, 2021

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.

  1. What about key values that have underscores?
  2. 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.

Copy link
Member

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.

Copy link
Contributor Author

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.

@cmanning09 cmanning09 merged commit 59e3aeb into opensearch-project:main Oct 19, 2021
@cmanning09 cmanning09 deleted the 434 branch October 19, 2021 19:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants