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

Exclude keys #3055

Merged
merged 13 commits into from
Jul 27, 2023
3 changes: 3 additions & 0 deletions data-prepper-plugins/key-value-processor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ When run, the processor will parse the message into the following output:
* `include_keys` - An array specifying the keys which should be added to parse. By default, all keys will be added.
* Default: `[]`
* Example: `include_keys` is `["key2"]`. `key1=value1&key2=value2` will parse into `{"key2": "value2"}`
* `exclude_keys` - An array specifying the parsed keys which should not be added to the event. By default no keys will be excluded.
* Default: `[]`
* Example: `exclude_keys` is `["key2"]`. `key1=value1&key2=value2` will parse into `{"key1": "value1"}`
* `key_value_delimiter_regex` - A regex specifying the delimiter between a key and a value. Special regex characters such as `[` and `]` must be escaped using `\\`.
* There is no default.
* Note: This cannot be defined at the same time as `value_split_characters`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public class KeyValueProcessor extends AbstractProcessor<Record<Event>, Record<E
private final Pattern fieldDelimiterPattern;
private final Pattern keyValueDelimiterPattern;
private final Set<String> includeKeysSet = new HashSet<String>();
private final Set<String> excludeKeysSet = new HashSet<String>();
private final String lowercaseKey = "lowercase";
private final String uppercaseKey = "uppercase";
private final String capitalizeKey = "capitalize";
Expand Down Expand Up @@ -99,8 +100,14 @@ public KeyValueProcessor(final PluginMetrics pluginMetrics, final KeyValueProces
throw new PatternSyntaxException("delete_value_regex is not a valid regex string", keyValueProcessorConfig.getDeleteValueRegex(), -1);
}

if (keyValueProcessorConfig.getIncludeKeys() != null) {
includeKeysSet.addAll(keyValueProcessorConfig.getIncludeKeys());
includeKeysSet.addAll(keyValueProcessorConfig.getIncludeKeys());
excludeKeysSet.addAll(keyValueProcessorConfig.getExcludeKeys());

Set<String> intersectionSet = new HashSet<String>(includeKeysSet);
if (intersectionSet.retainAll(excludeKeysSet)) {
shenkw1 marked this conversation as resolved.
Show resolved Hide resolved
if (!intersectionSet.isEmpty()) {
throw new IllegalArgumentException("Include keys and exclude keys set cannot have any overlap", null);
}
}

if (!validTransformOptionSet.contains(keyValueProcessorConfig.getTransformKey())) {
Expand Down Expand Up @@ -171,6 +178,11 @@ public Collection<Record<Event>> doExecute(final Collection<Record<Event>> recor
continue;
}

if (!excludeKeysSet.isEmpty() && excludeKeysSet.contains(key)) {
LOG.debug(String.format("Key is being excluded: '%s'", key));
continue;
}

if(keyValueProcessorConfig.getDeleteKeyRegex() != null && !Objects.equals(keyValueProcessorConfig.getDeleteKeyRegex(), "")) {
key = key.replaceAll(keyValueProcessorConfig.getDeleteKeyRegex(), "");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class KeyValueProcessorConfig {
static final String DEFAULT_DESTINATION = "parsed_message";
public static final String DEFAULT_FIELD_SPLIT_CHARACTERS = "&";
static final List<String> DEFAULT_INCLUDE_KEYS = new ArrayList<>();
static final List<String> DEFAULT_EXCLUDE_KEYS = new ArrayList<>();
public static final String DEFAULT_VALUE_SPLIT_CHARACTERS = "=";
static final Object DEFAULT_NON_MATCH_VALUE = null;
static final String DEFAULT_PREFIX = "";
Expand Down Expand Up @@ -44,6 +45,10 @@ public class KeyValueProcessorConfig {
@NotNull
private List<String> includeKeys = DEFAULT_INCLUDE_KEYS;

@JsonProperty("exclude_keys")
@NotNull
private List<String> excludeKeys = DEFAULT_EXCLUDE_KEYS;

@JsonProperty("key_value_delimiter_regex")
private String keyValueDelimiterRegex;

Expand Down Expand Up @@ -100,6 +105,10 @@ public List<String> getIncludeKeys() {
return includeKeys;
}

public List<String> getExcludeKeys() {
return excludeKeys;
}

public String getKeyValueDelimiterRegex() {
return keyValueDelimiterRegex;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ void setup() {
lenient().when(mockConfig.getFieldDelimiterRegex()).thenReturn(defaultConfig.getFieldDelimiterRegex());
lenient().when(mockConfig.getFieldSplitCharacters()).thenReturn(defaultConfig.getFieldSplitCharacters());
lenient().when(mockConfig.getIncludeKeys()).thenReturn(defaultConfig.getIncludeKeys());
lenient().when(mockConfig.getExcludeKeys()).thenReturn(defaultConfig.getExcludeKeys());
lenient().when(mockConfig.getKeyValueDelimiterRegex()).thenReturn(defaultConfig.getKeyValueDelimiterRegex());
lenient().when(mockConfig.getValueSplitCharacters()).thenReturn(defaultConfig.getValueSplitCharacters());
lenient().when(mockConfig.getNonMatchValue()).thenReturn(defaultConfig.getNonMatchValue());
Expand Down Expand Up @@ -263,8 +264,8 @@ void testIncludeKeysNoMatchKeyValueProcessor() {
}

@Test
void testIncludeKeysAsNullKeyValueProcessor() {
when(mockConfig.getIncludeKeys()).thenReturn(null);
void testIncludeKeysAsDefaultKeyValueProcessor() {
when(mockConfig.getIncludeKeys()).thenReturn(List.of());
keyValueProcessor = new KeyValueProcessor(pluginMetrics, mockConfig);

final Record<Event> record = getMessage("key1=value1&key2=value2");
Expand All @@ -276,6 +277,44 @@ void testIncludeKeysAsNullKeyValueProcessor() {
assertThatKeyEquals(parsed_message, "key2", "value2");
}

@Test
void testExcludeKeysKeyValueProcessor() {
final List<String> excludeKeys = List.of("key2");
when(mockConfig.getExcludeKeys()).thenReturn(excludeKeys);
keyValueProcessor = new KeyValueProcessor(pluginMetrics, mockConfig);

final Record<Event> record = getMessage("key1=value1&key2=value2");
final List<Record<Event>> editedRecords = (List<Record<Event>>) keyValueProcessor.doExecute(Collections.singletonList(record));
final LinkedHashMap<String, Object> parsed_message = getLinkedHashMap(editedRecords);

assertThat(parsed_message.size(), equalTo(1));
assertThatKeyEquals(parsed_message, "key1", "value1");
}

@Test
void testExcludeKeysAsDefaultKeyValueProcessor() {
when(mockConfig.getExcludeKeys()).thenReturn(List.of());
keyValueProcessor = new KeyValueProcessor(pluginMetrics, mockConfig);

final Record<Event> record = getMessage("key1=value1&key2=value2");
final List<Record<Event>> editedRecords = (List<Record<Event>>) keyValueProcessor.doExecute(Collections.singletonList(record));
final LinkedHashMap<String, Object> parsed_message = getLinkedHashMap(editedRecords);

assertThat(parsed_message.size(), equalTo(2));
assertThatKeyEquals(parsed_message, "key1", "value1");
assertThatKeyEquals(parsed_message, "key2", "value2");
}

shenkw1 marked this conversation as resolved.
Show resolved Hide resolved
@Test
void testIncludeExcludeKeysOverlapKeyValueProcessor() {
final List<String> includeKeys = List.of("key1", "key3");
final List<String> excludeKeys = List.of("key3");
when(mockConfig.getIncludeKeys()).thenReturn(includeKeys);
when(mockConfig.getExcludeKeys()).thenReturn(excludeKeys);

assertThrows(IllegalArgumentException.class, () -> new KeyValueProcessor(pluginMetrics, mockConfig));
}

@Test
void testCustomPrefixKvProcessor() {
when(mockConfig.getPrefix()).thenReturn("TEST_");
Expand Down
Loading