From 431d051b0d514d2951dfc0917bf25c0c23bfe3cd Mon Sep 17 00:00:00 2001 From: Mohamed OULD HOCINE <106236152+gally47@users.noreply.github.com> Date: Thu, 10 Aug 2023 13:43:22 +0200 Subject: [PATCH] Enhanced Pipeline RuleUI (#15851) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * initial setup * Rule Builder UI Help Icon (#15883) * Extract function table into own component * Migrate RuleHelper to typescript and functional component * Add help icon and popover to rule block form * generate simulator output * Show function description in select list * updated Cols to use xs * Enhanced Simulator progress * fix eslint issue * add function groups and human readable names backend for graylog/graylog-plugin-enterprise#5414 * Fix color for selected Option description and help box width * set rulebuilder descriptor and primary param * set rulebuilder descriptor and primary param * set rulebuilder descriptor and primary param * set rulebuilder descriptor and primary param * fix: sort by rulebuilder name * set rulebuilder descriptor and primary param * remove boolean functions from actions * Show output and use rule_builder_name * repositioned Show Simulator * display a simpler version of Rule Ref on RuleBuilder * add SourceCodeEditor button * fix reset simulation * update css heights * fix: generate simulator fields only on simulate * put rule builder step variables into custom result node * evaluate conditions in simulation * Show select list for output variables for primary inputs * Generate dynamic output variable list and show filtered list in fields * Remove unused functions and code * Don't add or update when required fields are missing and mark optional fields * Fix proptype for RuleHelperTable * Show variable names in Block Display * Fix linter errors in Input component * Fix input console error * fix getDictForFunction undefined error * Filter out variables without output and rename output var name * readd handling of undefined dict in outputvariables * Fix wrong license header in RuleBuilder * integrated simulation api changes * real time simulation * show simulation conditions/actions outputs * add condition groups * fix failing tests, adjust to grouped conditions * Rule Simulation label * Remove params from display * updated conditions/actions output keys * add extractor fragments and fragment tests * Show return type next to output * Reduce spacing between output and block headline * Fix existing tests and add new test cases * Fix type errors and remove attrs usage in styled components * add field type conditions, remove type check conditions, add metadata * add date functions, add metadata * added unit tests to simulator * add string functions, function metadata * unit test for simulator conditions/actions output * fix missing name and conditions * fixed tests * direct navigation to rule builder after clicking create rule button * add string conditions * fix condition titles * clean up conditions * fix delete rule errors * reposition sourceCodeEditor button and showSimulator switch * Don't check for previousOutputPresent anymore * Move Toggle to it's own component * use theme spacing * navigate to rules list onCancel instead of history.goBack * add additional extractors and tests * add grok matches condition * Fix simulation result reset with the default simulation message when updating an action * converted Rule.jsx to typescript * Declare variableType as optional on type and propTypes * fix grok matches and grok extractor, add tests * update grok extractor title * fix booleans in titles * add suppress forbiddenapi usage in tests * add split index extractor * disable functions with incompatible return types and parameters * disable functions with incompatible return types and parameters * fix reenable set_field * add lookup extractor * add lookup extractor fragment * added ConfirmNavigateToSourceCodeEditorModal * Function typos, missing primary parameters * Update logger name Co-authored-by: Patrick Mann * completely remove deleted test * fix logger name * move `addFragment` upsert functionality to `RuleFragmentService` * extract functions --------- Co-authored-by: Laura Bergenthal-Grotlüschen Co-authored-by: Laura Co-authored-by: Matthias Oesterheld Co-authored-by: Matthias Oesterheld <33032967+moesterheld@users.noreply.github.com> Co-authored-by: Patrick Mann --- .../ast/functions/FunctionDescriptor.java | 52 +- .../functions/FromInput.java | 5 + .../functions/GrokExists.java | 7 +- .../functions/IsNotNull.java | 7 +- .../pipelineprocessor/functions/IsNull.java | 7 +- .../conversion/BooleanConversion.java | 7 +- .../conversion/DoubleConversion.java | 7 +- .../functions/conversion/IsBoolean.java | 9 +- .../functions/conversion/IsCollection.java | 9 +- .../functions/conversion/IsDouble.java | 9 +- .../functions/conversion/IsList.java | 7 +- .../functions/conversion/IsLong.java | 7 +- .../functions/conversion/IsMap.java | 7 +- .../functions/conversion/IsNumber.java | 7 +- .../functions/conversion/IsString.java | 7 +- .../functions/conversion/LongConversion.java | 7 +- .../functions/conversion/MapConversion.java | 7 +- .../conversion/StringConversion.java | 7 +- .../functions/dates/DateConversion.java | 17 +- .../functions/dates/FlexParseDate.java | 15 +- .../functions/dates/FormatDate.java | 7 +- .../functions/dates/IsDate.java | 9 +- .../functions/dates/Now.java | 16 +- .../functions/dates/ParseDate.java | 15 +- .../dates/ParseUnixMilliseconds.java | 16 +- .../dates/TimezoneAwareFunction.java | 18 +- .../AbstractPeriodComponentFunction.java | 12 +- .../functions/dates/periods/Days.java | 12 + .../functions/dates/periods/Hours.java | 12 + .../functions/dates/periods/IsPeriod.java | 7 +- .../functions/dates/periods/Millis.java | 14 +- .../functions/dates/periods/Minutes.java | 13 + .../functions/dates/periods/Months.java | 12 + .../dates/periods/PeriodParseFunction.java | 7 +- .../functions/dates/periods/Seconds.java | 12 + .../functions/dates/periods/Weeks.java | 12 + .../functions/dates/periods/Years.java | 12 + .../functions/ips/IpAddressConversion.java | 5 + .../pipelineprocessor/functions/ips/IsIp.java | 7 +- .../functions/json/IsJson.java | 9 +- .../functions/lookup/LookupHasValue.java | 5 + .../functions/lookup/LookupValue.java | 7 +- .../functions/messages/CloneMessage.java | 9 +- .../functions/messages/CreateMessage.java | 7 +- .../functions/messages/DropMessage.java | 5 + .../functions/messages/GetField.java | 3 + .../functions/messages/HasField.java | 3 + .../functions/messages/NormalizeFields.java | 5 + .../functions/messages/RemoveField.java | 7 +- .../functions/messages/RemoveFromStream.java | 7 +- .../functions/messages/RenameField.java | 7 +- .../functions/messages/RouteToStream.java | 7 +- .../functions/messages/SetField.java | 10 +- .../functions/messages/SetFields.java | 9 +- .../functions/strings/Abbreviate.java | 7 +- .../functions/strings/Capitalize.java | 13 + .../functions/strings/Concat.java | 5 + .../functions/strings/Join.java | 6 + .../functions/strings/Length.java | 7 +- .../functions/strings/Lowercase.java | 13 + .../functions/strings/RegexReplace.java | 9 +- .../functions/strings/Replace.java | 7 +- .../functions/strings/Split.java | 7 +- .../strings/StringUtilsFunction.java | 14 +- .../functions/strings/Substring.java | 5 + .../functions/strings/Swapcase.java | 13 + .../functions/strings/Uncapitalize.java | 13 + .../functions/strings/Uppercase.java | 13 + .../functions/urls/IsUrl.java | 7 +- .../functions/urls/UrlConversion.java | 5 + .../rulebuilder/RuleBuilder.java | 11 +- .../rulebuilder/RuleBuilderFunctionGroup.java | 56 +++ .../rulebuilder/RuleBuilderModule.java | 4 + .../rulebuilder/RuleBuilderRegistry.java | 10 +- .../rulebuilder/RuleBuilderStep.java | 23 + .../rulebuilder/db/RuleFragmentService.java | 6 + ...512123200_AddSimpleConditionFragments.java | 36 +- ..._AddSetGrokToFieldsExtractorFragments.java | 34 +- ...13154400_AddImplicitToStringFragments.java | 22 +- ...V20230720161500_AddExtractorFragments.java | 237 +++++++++ .../V20230724092100_AddFieldConditions.java | 277 +++++++++++ .../rulebuilder/parser/ConditionParser.java | 59 ++- .../rulebuilder/parser/ParserUtil.java | 6 +- .../parser/RuleBuilderService.java | 15 +- .../rulebuilder/rest/RuleBuilderResource.java | 29 +- .../rest/RuleBuilderSimulatorResponse.java | 57 +++ .../parser/PipelineRuleParserTest.java | 13 + ...SetGrokToFieldsExtractorFragmentsTest.java | 90 ++++ ...30720161500_AddExtractorFragmentsTest.java | 154 ++++++ ...20230724092100_AddFieldConditionsTest.java | 448 ++++++++++++++++++ .../rulebuilder/parser/ActionParserTest.java | 2 +- .../rulebuilder/parser/BaseFragmentTest.java | 107 +++++ .../parser/ConditionParserTest.java | 42 +- .../parser/RuleBuilderServiceTest.java | 3 +- .../src/components/bootstrap/Input.jsx | 26 +- .../src/components/common/FormikFormGroup.tsx | 2 + .../src/components/common/FormikInput.tsx | 6 + .../src/components/common/Icon.tsx | 6 +- .../src/components/common/SearchForm.tsx | 2 +- .../src/components/common/Select/Select.tsx | 8 +- .../src/components/common/Toggle.tsx | 82 ++++ .../src/components/common/index.tsx | 1 + .../components/navigation/ThemeModeToggle.tsx | 68 +-- .../components/rules/{Rule.jsx => Rule.tsx} | 10 +- .../src/components/rules/RuleContext.tsx | 8 +- .../src/components/rules/RuleHelper.jsx | 257 ---------- .../src/components/rules/RuleSimulation.tsx | 120 ++--- ...ConfirmNavigateToSourceCodeEditorModal.tsx | 93 ++++ .../rule-builder/RuleBlockDisplay.test.tsx | 25 +- .../rules/rule-builder/RuleBlockDisplay.tsx | 108 +++-- .../rules/rule-builder/RuleBlockForm.test.tsx | 45 +- .../rules/rule-builder/RuleBlockForm.tsx | 80 +++- .../rules/rule-builder/RuleBlockFormField.tsx | 153 ++++-- .../rules/rule-builder/RuleBuilder.test.tsx | 77 +++ .../rules/rule-builder/RuleBuilder.tsx | 340 +++++++------ .../rules/rule-builder/RuleBuilderBlock.tsx | 25 +- .../rules/rule-builder/RuleBuilderForm.tsx | 1 + .../components/rules/rule-builder/fixtures.ts | 25 + .../components/rules/rule-builder/helpers.ts | 23 +- .../components/rules/rule-builder/types.ts | 18 + .../rules/{ => rule-helper}/RuleHelper.css | 0 .../rules/rule-helper/RuleHelper.tsx | 199 ++++++++ .../rules/rule-helper/RulerHelperTable.tsx | 118 +++++ .../components/rules/rule-helper/helpers.ts | 25 + .../src/pages/RulesPage.test.tsx | 12 +- .../src/pages/RulesPage.tsx | 18 +- .../src/stores/rules/RulesStore.ts | 4 +- 127 files changed, 3483 insertions(+), 912 deletions(-) create mode 100644 graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/RuleBuilderFunctionGroup.java create mode 100644 graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20230720161500_AddExtractorFragments.java create mode 100644 graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20230724092100_AddFieldConditions.java create mode 100644 graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/rest/RuleBuilderSimulatorResponse.java create mode 100644 graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20220522125200_AddSetGrokToFieldsExtractorFragmentsTest.java create mode 100644 graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20230720161500_AddExtractorFragmentsTest.java create mode 100644 graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20230724092100_AddFieldConditionsTest.java create mode 100644 graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/rulebuilder/parser/BaseFragmentTest.java create mode 100644 graylog2-web-interface/src/components/common/Toggle.tsx rename graylog2-web-interface/src/components/rules/{Rule.jsx => Rule.tsx} (92%) delete mode 100644 graylog2-web-interface/src/components/rules/RuleHelper.jsx create mode 100644 graylog2-web-interface/src/components/rules/rule-builder/ConfirmNavigateToSourceCodeEditorModal.tsx rename graylog2-web-interface/src/components/rules/{ => rule-helper}/RuleHelper.css (100%) create mode 100644 graylog2-web-interface/src/components/rules/rule-helper/RuleHelper.tsx create mode 100644 graylog2-web-interface/src/components/rules/rule-helper/RulerHelperTable.tsx create mode 100644 graylog2-web-interface/src/components/rules/rule-helper/helpers.ts diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/ast/functions/FunctionDescriptor.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/ast/functions/FunctionDescriptor.java index 1b89290be61c0..accc9a0a0175c 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/ast/functions/FunctionDescriptor.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/ast/functions/FunctionDescriptor.java @@ -24,8 +24,10 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import javax.annotation.Nullable; +import java.util.Optional; @AutoValue @JsonAutoDetect @@ -58,10 +60,50 @@ public ParameterDescriptor param(String name) { @JsonProperty public abstract boolean ruleBuilderEnabled(); + @JsonIgnore + @Nullable + public abstract String ruleBuilderName(); + + /** + * default to function name + * + * @return function name, if rule builder is enabled and no name is explicitly set + */ + @JsonProperty("rule_builder_name") + public String getRuleBuilderName() { + if (ruleBuilderEnabled() && ruleBuilderName() == null) { + return name(); + } + return ruleBuilderName(); + } + @JsonProperty @Nullable public abstract String ruleBuilderTitle(); + @JsonIgnore + @Nullable + public abstract RuleBuilderFunctionGroup ruleBuilderFunctionGroup(); + + /** + * tries to determine the function group from the primary parameter class, if not set + * + * @return determined group if rule builder is enabled and no group is explicitly set + */ + @SuppressWarnings("rawtypes") + @JsonProperty("rule_builder_function_group") + public RuleBuilderFunctionGroup getRuleBuilderFunctionGroup() { + if (ruleBuilderEnabled() && ruleBuilderFunctionGroup() == null) { + final Optional primaryParam = params().stream() + .filter(ParameterDescriptor::primary) + .findFirst(); + if (primaryParam.isPresent()) { + return RuleBuilderFunctionGroup.map(primaryParam.get().type()); + } + } + return ruleBuilderFunctionGroup(); + } + public static Builder builder() { //noinspection unchecked return new AutoValue_FunctionDescriptor.Builder().pure(false).ruleBuilderEnabled(false); @@ -88,8 +130,12 @@ public Builder ruleBuilderEnabled() { return ruleBuilderEnabled(true); } + public abstract Builder ruleBuilderName(String ruleBuilderName); + public abstract Builder ruleBuilderTitle(String ruleBuilderTitle); + public abstract Builder ruleBuilderFunctionGroup(RuleBuilderFunctionGroup ruleBuilderFunctionGroup); + public Builder params(ParameterDescriptor... params) { return params(ImmutableList.builder().add(params).build()); } @@ -109,14 +155,18 @@ public static FunctionDescriptor createForRuleBuilder( @JsonProperty("return_type") Class returnType, @JsonProperty("params") @Nullable ImmutableList params, @JsonProperty("description") @Nullable String description, - @JsonProperty("rule_builder_title") @Nullable String ruleBuilderTitle) { + @JsonProperty("rule_builder_name") @Nullable String ruleBuilderName, + @JsonProperty("rule_builder_title") @Nullable String ruleBuilderTitle, + @JsonProperty("rule_builder_group") @Nullable RuleBuilderFunctionGroup ruleBuilderFunctionGroup) { return FunctionDescriptor.builder() .name(name) .returnType(returnType) .params(params) .description(description) .ruleBuilderEnabled() + .ruleBuilderName(ruleBuilderName) .ruleBuilderTitle(ruleBuilderTitle) + .ruleBuilderFunctionGroup(ruleBuilderFunctionGroup) .build(); } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/FromInput.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/FromInput.java index 09ee40640fa7c..370e1030f1a52 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/FromInput.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/FromInput.java @@ -21,6 +21,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import org.graylog2.plugin.IOState; import org.graylog2.plugin.inputs.MessageInput; import org.graylog2.shared.inputs.InputRegistry; @@ -84,6 +85,10 @@ public FunctionDescriptor descriptor() { idParam, nameParam)) .description("Checks if a message arrived on a given input") + .ruleBuilderEnabled() + .ruleBuilderName("Check source input") + .ruleBuilderTitle("Check if message arrived on input<#if name??> '${name}'<#if id??> '${id}'") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.BOOLEAN) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/GrokExists.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/GrokExists.java index f279008fea5b5..8953ef76b8493 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/GrokExists.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/GrokExists.java @@ -22,6 +22,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import org.graylog2.grok.GrokPatternRegistry; import javax.inject.Inject; @@ -73,7 +74,11 @@ public FunctionDescriptor descriptor() { .name(NAME) .returnType(Boolean.class) .params(of(patternParam, doLog)) - .description("Checks if the given Grok pattern exists.") + .description("Checks if the given Grok pattern exists in Graylog.") + .ruleBuilderEnabled() + .ruleBuilderName("Check for grok pattern") + .ruleBuilderTitle("Check if grok pattern named '${pattern}' exists in Graylog") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.PATTERN) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/IsNotNull.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/IsNotNull.java index 9b851bb3b4de6..91f5ad73d89eb 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/IsNotNull.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/IsNotNull.java @@ -21,6 +21,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import static com.google.common.collect.ImmutableList.of; @@ -30,7 +31,7 @@ public class IsNotNull extends AbstractFunction { private final ParameterDescriptor valueParam; public IsNotNull() { - valueParam = ParameterDescriptor.type("value", Object.class).description("The value to check").build(); + valueParam = ParameterDescriptor.type("value", Object.class).primary().description("The value to check").build(); } @Override @@ -50,6 +51,10 @@ public FunctionDescriptor descriptor() { .returnType(Boolean.class) .params(of(valueParam)) .description("Checks whether a value is not 'null'") + .ruleBuilderEnabled(false) + .ruleBuilderName("Check if not null") + .ruleBuilderTitle("Check if value is not null") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.BOOLEAN) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/IsNull.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/IsNull.java index 7f7c5db23c41d..e8c9a03d2209b 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/IsNull.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/IsNull.java @@ -21,6 +21,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import static com.google.common.collect.ImmutableList.of; @@ -30,7 +31,7 @@ public class IsNull extends AbstractFunction { private final ParameterDescriptor valueParam; public IsNull() { - valueParam = ParameterDescriptor.type("value", Object.class).description("The value to check").build(); + valueParam = ParameterDescriptor.type("value", Object.class).primary().description("The value to check").build(); } @Override @@ -50,6 +51,10 @@ public FunctionDescriptor descriptor() { .returnType(Boolean.class) .params(of(valueParam)) .description("Checks whether a value is 'null'") + .ruleBuilderEnabled(false) + .ruleBuilderName("Check if null") + .ruleBuilderTitle("Check if value is null") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.BOOLEAN) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/BooleanConversion.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/BooleanConversion.java index d1383667cb641..0fdce55676c06 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/BooleanConversion.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/BooleanConversion.java @@ -21,6 +21,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import static com.google.common.collect.ImmutableList.of; import static org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor.bool; @@ -34,7 +35,7 @@ public class BooleanConversion extends AbstractFunction { public BooleanConversion() { - valueParam = object("value").description("Value to convert").build(); + valueParam = object("value").primary().description("Value to convert").build(); defaultParam = bool("default").optional().description("Used when 'value' is null, defaults to false").build(); } @@ -54,6 +55,10 @@ public FunctionDescriptor descriptor() { .returnType(Boolean.class) .params(of(valueParam, defaultParam)) .description("Converts a value to a boolean value using its string representation") + .ruleBuilderEnabled(false) + .ruleBuilderName("Convert to boolean") + .ruleBuilderTitle("Convert '${value}' to boolean") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.CONVERSION) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/DoubleConversion.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/DoubleConversion.java index 56df181a21db3..3dbcca8f1aaeb 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/DoubleConversion.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/DoubleConversion.java @@ -22,6 +22,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.collect.ImmutableList.of; @@ -38,7 +39,7 @@ public class DoubleConversion extends AbstractFunction { private final ParameterDescriptor defaultParam; public DoubleConversion() { - valueParam = object(VALUE).description("Value to convert").build(); + valueParam = object(VALUE).primary().description("Value to convert").build(); defaultParam = floating(DEFAULT).optional().allowNegatives(true).description("Used when 'value' is null, defaults to 0").build(); } @@ -67,6 +68,10 @@ public FunctionDescriptor descriptor() { defaultParam )) .description("Converts a value to a double value using its string representation") + .ruleBuilderEnabled(false) + .ruleBuilderName("Convert to double") + .ruleBuilderTitle("Convert '${value}' to double") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.CONVERSION) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsBoolean.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsBoolean.java index 65a5ab871a12b..fd24fa158388f 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsBoolean.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsBoolean.java @@ -21,6 +21,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import static org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor.object; @@ -30,7 +31,7 @@ public class IsBoolean extends AbstractFunction { private final ParameterDescriptor valueParam; public IsBoolean() { - valueParam = object("value").description("Value to check").build(); + valueParam = object("value").primary().description("Value to check").build(); } @Override @@ -45,7 +46,11 @@ public FunctionDescriptor descriptor() { .name(NAME) .returnType(Boolean.class) .params(valueParam) - .description("Checks whether a value is a boolean") + .description("Checks whether a value is a boolean value (true or false)") + .ruleBuilderEnabled(false) + .ruleBuilderName("Check if boolean") + .ruleBuilderTitle("Check if '${value}' is a boolean") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.BOOLEAN) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsCollection.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsCollection.java index eea3efd2dc5ab..0fbc6b6fdc27a 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsCollection.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsCollection.java @@ -21,6 +21,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import java.util.Collection; @@ -32,7 +33,7 @@ public class IsCollection extends AbstractFunction { private final ParameterDescriptor valueParam; public IsCollection() { - valueParam = object("value").description("Value to check").build(); + valueParam = object("value").primary().description("Value to check").build(); } @Override @@ -47,7 +48,11 @@ public FunctionDescriptor descriptor() { .name(NAME) .returnType(Boolean.class) .params(valueParam) - .description("Checks whether a value is a collection") + .description("Checks whether a value is an iterable collection") + .ruleBuilderEnabled(false) + .ruleBuilderName("Check if collection") + .ruleBuilderTitle("Check if '${value}' is a collection") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.BOOLEAN) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsDouble.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsDouble.java index 2f3fcba5303d3..280fb6f6a2a32 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsDouble.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsDouble.java @@ -21,6 +21,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import static org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor.object; @@ -30,7 +31,7 @@ public class IsDouble extends AbstractFunction { private final ParameterDescriptor valueParam; public IsDouble() { - valueParam = object("value").description("Value to check").build(); + valueParam = object("value").primary().description("Value to check").build(); } @Override @@ -45,7 +46,11 @@ public FunctionDescriptor descriptor() { .name(NAME) .returnType(Boolean.class) .params(valueParam) - .description("Checks whether a value is a double") + .description("Checks whether a value is a floating point value (of type double)") + .ruleBuilderEnabled(false) + .ruleBuilderName("Check if double") + .ruleBuilderTitle("Check if '${value}' is a double") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.BOOLEAN) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsList.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsList.java index 6a2a904f03231..1bc16aa7b0179 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsList.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsList.java @@ -21,6 +21,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import java.util.List; @@ -32,7 +33,7 @@ public class IsList extends AbstractFunction { private final ParameterDescriptor valueParam; public IsList() { - valueParam = object("value").description("Value to check").build(); + valueParam = object("value").primary().description("Value to check").build(); } @Override @@ -48,6 +49,10 @@ public FunctionDescriptor descriptor() { .returnType(Boolean.class) .params(valueParam) .description("Checks whether a value is a list") + .ruleBuilderEnabled(false) + .ruleBuilderName("Check if list") + .ruleBuilderTitle("Check if '${value}' is a list") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.BOOLEAN) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsLong.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsLong.java index c20a54b94f490..94108d2f76fa5 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsLong.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsLong.java @@ -21,6 +21,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import static org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor.object; @@ -30,7 +31,7 @@ public class IsLong extends AbstractFunction { private final ParameterDescriptor valueParam; public IsLong() { - valueParam = object("value").description("Value to check").build(); + valueParam = object("value").primary().description("Value to check").build(); } @Override @@ -46,6 +47,10 @@ public FunctionDescriptor descriptor() { .returnType(Boolean.class) .params(valueParam) .description("Checks whether a value is a long integer") + .ruleBuilderEnabled(false) + .ruleBuilderName("Check if long") + .ruleBuilderTitle("Check if '${value}' is a long integer") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.BOOLEAN) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsMap.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsMap.java index 586a0c56e7fe9..216486b04ef37 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsMap.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsMap.java @@ -21,6 +21,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import java.util.Map; @@ -32,7 +33,7 @@ public class IsMap extends AbstractFunction { private final ParameterDescriptor valueParam; public IsMap() { - valueParam = object("value").description("Value to check").build(); + valueParam = object("value").primary().description("Value to check").build(); } @Override @@ -48,6 +49,10 @@ public FunctionDescriptor descriptor() { .returnType(Boolean.class) .params(valueParam) .description("Checks whether a value is a map") + .ruleBuilderEnabled(false) + .ruleBuilderName("Check if map") + .ruleBuilderTitle("Check whether '${value}' is a map") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.BOOLEAN) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsNumber.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsNumber.java index b91bd53ab746d..81514b24c737c 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsNumber.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsNumber.java @@ -21,6 +21,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import static org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor.object; @@ -30,7 +31,7 @@ public class IsNumber extends AbstractFunction { private final ParameterDescriptor valueParam; public IsNumber() { - valueParam = object("value").description("Value to check").build(); + valueParam = object("value").primary().description("Value to check").build(); } @Override @@ -46,6 +47,10 @@ public FunctionDescriptor descriptor() { .returnType(Boolean.class) .params(valueParam) .description("Checks whether a value is a number") + .ruleBuilderEnabled(false) + .ruleBuilderName("Check if number") + .ruleBuilderTitle("Check if '${value}' is a number") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.BOOLEAN) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsString.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsString.java index 7985b2d4f6946..27e79373417b3 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsString.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/IsString.java @@ -21,6 +21,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import static org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor.object; @@ -30,7 +31,7 @@ public class IsString extends AbstractFunction { private final ParameterDescriptor valueParam; public IsString() { - valueParam = object("value").description("Value to check").build(); + valueParam = object("value").primary().description("Value to check").build(); } @Override @@ -46,6 +47,10 @@ public FunctionDescriptor descriptor() { .returnType(Boolean.class) .params(valueParam) .description("Checks whether a value is a string") + .ruleBuilderEnabled(false) + .ruleBuilderName("Check if string") + .ruleBuilderTitle("Check if '${value}' is a string") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.BOOLEAN) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/LongConversion.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/LongConversion.java index 6ecb3659616c5..956de7ba1e5ac 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/LongConversion.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/LongConversion.java @@ -21,6 +21,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.collect.ImmutableList.of; @@ -39,7 +40,7 @@ public class LongConversion extends AbstractFunction { private final ParameterDescriptor defaultParam; public LongConversion() { - valueParam = object(VALUE).description("Value to convert").primary().build(); + valueParam = object(VALUE).primary().description("Value to convert").primary().build(); defaultParam = integer(DEFAULT).optional().allowNegatives(true).description("Used when 'value' is null, defaults to 0").build(); } @@ -69,7 +70,9 @@ public FunctionDescriptor descriptor() { )) .description("Converts a value to a long value using its string representation") .ruleBuilderEnabled() - .ruleBuilderTitle("Convert value to number") + .ruleBuilderName("Convert to long") + .ruleBuilderTitle("Convert '${value}' to long integer") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.CONVERSION) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/MapConversion.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/MapConversion.java index 161f5819009e3..dccb2382b4e4b 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/MapConversion.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/MapConversion.java @@ -23,6 +23,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import java.util.Collections; import java.util.Map; @@ -40,7 +41,7 @@ public class MapConversion extends AbstractFunction { public MapConversion() { - this.valueParam = object(VALUE).description("Map-like value to convert").build(); + this.valueParam = object(VALUE).primary().description("Map-like value to convert").build(); } @Override @@ -66,6 +67,10 @@ public FunctionDescriptor descriptor() { .returnType(Map.class) .params(of(valueParam)) .description("Converts a map-like value into a map usable by set_fields()") + .ruleBuilderEnabled() + .ruleBuilderName("Convert to map") + .ruleBuilderTitle("Convert '${value}' to map") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.CONVERSION) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/StringConversion.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/StringConversion.java index fc7b72371040a..b6a2b3e3650c6 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/StringConversion.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/conversion/StringConversion.java @@ -22,6 +22,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; import org.graylog.plugins.pipelineprocessor.functions.ips.IpAddress; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import org.joda.time.DateTime; import java.util.LinkedHashMap; @@ -52,7 +53,7 @@ protected boolean removeEldestEntry(Map.Entry, Class> eldest) { }; } }; - valueParam = object("value").description("Value to convert").primary().build(); + valueParam = object("value").primary().description("Value to convert").primary().build(); defaultParam = string("default").optional().description("Used when 'value' is null, defaults to \"\"").build(); } @@ -104,7 +105,9 @@ public FunctionDescriptor descriptor() { )) .description("Converts a value to its string representation") .ruleBuilderEnabled() - .ruleBuilderTitle("Convert value to string") + .ruleBuilderName("Convert to string") + .ruleBuilderTitle("Convert '${value}' to string") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.CONVERSION) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/DateConversion.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/DateConversion.java index b29b9a360bf46..452c295a94e7e 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/DateConversion.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/DateConversion.java @@ -23,6 +23,7 @@ import org.joda.time.DateTime; import org.joda.time.DateTimeZone; +import javax.annotation.Nonnull; import java.time.ZonedDateTime; import java.util.Date; @@ -32,7 +33,7 @@ public class DateConversion extends TimezoneAwareFunction { private final ParameterDescriptor value; public DateConversion() { - value = ParameterDescriptor.object("value").description("The value to convert to a date").build(); + value = ParameterDescriptor.object("value").primary().description("The value to convert to a date").build(); } @Override @@ -58,7 +59,7 @@ protected DateTime evaluate(FunctionArgs args, EvaluationContext context, DateTi @Override protected String description() { - return "Converts a type to a date, useful for $message.timestamp or related message fields."; + return "Converts a type to a date, useful for $message.timestamp or related message fields. Does not handle date parsing from strings."; } @Override @@ -70,4 +71,16 @@ protected String getName() { protected ImmutableList params() { return ImmutableList.of(value); } + + @Nonnull + @Override + protected String getRuleBuilderName() { + return "Convert to date"; + } + + @Nonnull + @Override + protected String getRuleBuilderTitle() { + return "Convert '${value}' to a DateTime"; + } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/FlexParseDate.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/FlexParseDate.java index 0b3dca478512d..9be30552beac6 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/FlexParseDate.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/FlexParseDate.java @@ -25,6 +25,7 @@ import org.joda.time.DateTime; import org.joda.time.DateTimeZone; +import javax.annotation.Nonnull; import java.util.List; import java.util.Optional; @@ -37,7 +38,7 @@ public class FlexParseDate extends TimezoneAwareFunction { private final ParameterDescriptor defaultParam; public FlexParseDate() { - valueParam = ParameterDescriptor.string(VALUE).description("Date string to parse").build(); + valueParam = ParameterDescriptor.string(VALUE).primary().description("Date string to parse").build(); defaultParam = ParameterDescriptor.type(DEFAULT, DateTime.class).optional().description("Used when 'value' could not be parsed, 'null' otherwise").build(); } @@ -74,4 +75,16 @@ protected ImmutableList params() { defaultParam ); } + + @Nonnull + @Override + protected String getRuleBuilderName() { + return "Parse date (flex)"; + } + + @Nonnull + @Override + protected String getRuleBuilderTitle() { + return "Flex parse '${value}' into a DateTime"; + } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/FormatDate.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/FormatDate.java index 4b9c61519e6d7..4632b9d03bf7d 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/FormatDate.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/FormatDate.java @@ -67,9 +67,10 @@ public FunctionDescriptor descriptor() { .name(NAME) .returnType(String.class) .params(of(value, format, timeZoneParam)) - .description("Formats a date using the given format string") -// .ruleBuilderEnabled() -// .ruleBuilderTitle("Format date (format '${format}')") + .description("Formats a date and time according to a given formatter pattern.") + .ruleBuilderEnabled(false) + .ruleBuilderName("Format date") + .ruleBuilderTitle("Format '${value}' as date (format '${format}')") .build(); } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/IsDate.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/IsDate.java index 6e0ad00eacc19..cfc2d917f2bc6 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/IsDate.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/IsDate.java @@ -21,6 +21,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import org.joda.time.DateTime; import java.util.Date; @@ -33,7 +34,7 @@ public class IsDate extends AbstractFunction { private final ParameterDescriptor valueParam; public IsDate() { - valueParam = object("value").description("Value to check").build(); + valueParam = object("value").primary().description("Value to check").build(); } @Override @@ -48,7 +49,11 @@ public FunctionDescriptor descriptor() { .name(NAME) .returnType(Boolean.class) .params(valueParam) - .description("Checks whether a value is a date") + .description("Checks whether a value is a date (of type DateTime)") + .ruleBuilderEnabled(false) + .ruleBuilderName("Check if date") + .ruleBuilderTitle("Check if '${value}' is a date") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.BOOLEAN) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/Now.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/Now.java index c4a4c1e386d97..0c88cda5c6d73 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/Now.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/Now.java @@ -23,6 +23,8 @@ import org.joda.time.DateTime; import org.joda.time.DateTimeZone; +import javax.annotation.Nonnull; + public class Now extends TimezoneAwareFunction { public static final String NAME = "now"; @@ -34,7 +36,7 @@ protected DateTime evaluate(FunctionArgs args, EvaluationContext context, DateTi @Override protected String description() { - return "Returns the current time"; + return "Returns the current date and time"; } @Override @@ -46,4 +48,16 @@ protected String getName() { protected ImmutableList params() { return ImmutableList.of(); } + + @Nonnull + @Override + protected String getRuleBuilderName() { + return "Create timestamp"; + } + + @Nonnull + @Override + protected String getRuleBuilderTitle() { + return "Create current DateTime"; + } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/ParseDate.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/ParseDate.java index 112a23a212083..5e49e81823b1b 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/ParseDate.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/ParseDate.java @@ -25,6 +25,7 @@ import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; +import javax.annotation.Nonnull; import java.util.Locale; import java.util.Optional; @@ -40,7 +41,7 @@ public class ParseDate extends TimezoneAwareFunction { private final ParameterDescriptor localeParam; public ParseDate() { - valueParam = ParameterDescriptor.string(VALUE).description("Date string to parse").build(); + valueParam = ParameterDescriptor.string(VALUE).primary().description("Date string to parse").build(); patternParam = ParameterDescriptor.string(PATTERN).description("The pattern to parse the date with, see http://www.joda.org/joda-time/apidocs/org/joda/time/format/DateTimeFormat.html").build(); localeParam = ParameterDescriptor.string(LOCALE).optional().description("The locale to parse the date with, see https://docs.oracle.com/javase/8/docs/api/java/util/Locale.html").build(); } @@ -83,4 +84,16 @@ public DateTime evaluate(FunctionArgs args, EvaluationContext context, DateTimeZ protected String description() { return "Parses a date string using the given date format"; } + + @Nonnull + @Override + protected String getRuleBuilderName() { + return "Parse date (pattern)"; + } + + @Nonnull + @Override + protected String getRuleBuilderTitle() { + return "Parse '${value}' into a DateTime using '${pattern}' pattern"; + } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/ParseUnixMilliseconds.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/ParseUnixMilliseconds.java index 8dbe508f18f2b..8b4681e009e6f 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/ParseUnixMilliseconds.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/ParseUnixMilliseconds.java @@ -23,6 +23,8 @@ import org.joda.time.DateTime; import org.joda.time.DateTimeZone; +import javax.annotation.Nonnull; + public class ParseUnixMilliseconds extends TimezoneAwareFunction { public static final String NAME = "parse_unix_milliseconds"; @@ -31,7 +33,7 @@ public class ParseUnixMilliseconds extends TimezoneAwareFunction { private final ParameterDescriptor valueParam; public ParseUnixMilliseconds() { - valueParam = ParameterDescriptor.integer(VALUE).description("UNIX millisecond timestamp to parse").build(); + valueParam = ParameterDescriptor.integer(VALUE).primary().description("UNIX millisecond timestamp to parse").build(); } @Override @@ -54,4 +56,16 @@ public DateTime evaluate(FunctionArgs args, EvaluationContext context, DateTimeZ protected String description() { return "Converts a UNIX millisecond timestamp into a date"; } + + @Nonnull + @Override + protected String getRuleBuilderName() { + return "Parse UNIX timestamp"; + } + + @Nonnull + @Override + protected String getRuleBuilderTitle() { + return "Parse '${value}' timestamp into a DateTime"; + } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/TimezoneAwareFunction.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/TimezoneAwareFunction.java index cc53993241f9e..44d2187a8b22f 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/TimezoneAwareFunction.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/TimezoneAwareFunction.java @@ -24,9 +24,11 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; +import javax.annotation.Nonnull; import java.util.Locale; public abstract class TimezoneAwareFunction extends AbstractFunction { @@ -61,10 +63,14 @@ public FunctionDescriptor descriptor() { .name(getName()) .returnType(DateTime.class) .params(ImmutableList.builder() - .addAll(params()) - .add(timeZoneParam) - .build()) + .addAll(params()) + .add(timeZoneParam) + .build()) .description(description()) + .ruleBuilderEnabled(false) + .ruleBuilderName(getRuleBuilderName()) + .ruleBuilderTitle(getRuleBuilderTitle()) + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.DATE) .build(); } @@ -73,4 +79,10 @@ public FunctionDescriptor descriptor() { protected abstract String getName(); protected abstract ImmutableList params(); + + @Nonnull + protected abstract String getRuleBuilderName(); + + @Nonnull + protected abstract String getRuleBuilderTitle(); } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/AbstractPeriodComponentFunction.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/AbstractPeriodComponentFunction.java index 0c51edadc5518..52be984f2d8f1 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/AbstractPeriodComponentFunction.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/AbstractPeriodComponentFunction.java @@ -17,12 +17,12 @@ package org.graylog.plugins.pipelineprocessor.functions.dates.periods; import com.google.common.primitives.Ints; - import org.graylog.plugins.pipelineprocessor.EvaluationContext; import org.graylog.plugins.pipelineprocessor.ast.functions.AbstractFunction; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import org.joda.time.Period; import javax.annotation.Nonnull; @@ -55,6 +55,10 @@ public FunctionDescriptor descriptor() { .pure(true) .returnType(Period.class) .params(value) + .ruleBuilderEnabled(false) + .ruleBuilderName(getRuleBuilderName()) + .ruleBuilderTitle(getRuleBuilderTitle()) + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.DATE) .build(); } @@ -63,4 +67,10 @@ public FunctionDescriptor descriptor() { @Nonnull protected abstract String getDescription(); + + @Nonnull + protected abstract String getRuleBuilderName(); + + @Nonnull + protected abstract String getRuleBuilderTitle(); } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Days.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Days.java index 9689e420bfb5f..f1dfcef6aa50d 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Days.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Days.java @@ -41,4 +41,16 @@ protected String getName() { protected String getDescription() { return "Create a period with a specified number of days."; } + + @Nonnull + @Override + protected String getRuleBuilderName() { + return "Create period (days)"; + } + + @Nonnull + @Override + protected String getRuleBuilderTitle() { + return "Create a time period of '${value}' days"; + } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Hours.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Hours.java index e2480d3478ac2..7fb38a80cb8d8 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Hours.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Hours.java @@ -41,4 +41,16 @@ protected String getName() { protected String getDescription() { return "Create a period with a specified number of hours."; } + + @Nonnull + @Override + protected String getRuleBuilderName() { + return "Create period (hours)"; + } + + @Nonnull + @Override + protected String getRuleBuilderTitle() { + return "Create a time period of '${value}' hours"; + } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/IsPeriod.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/IsPeriod.java index f7647db119f7e..231b231f9613c 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/IsPeriod.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/IsPeriod.java @@ -21,6 +21,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import org.joda.time.Period; import static org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor.object; @@ -31,7 +32,7 @@ public class IsPeriod extends AbstractFunction { private final ParameterDescriptor valueParam; public IsPeriod() { - valueParam = object("value").description("Value to check").build(); + valueParam = object("value").primary().description("Value to check").build(); } @Override @@ -47,6 +48,10 @@ public FunctionDescriptor descriptor() { .returnType(Boolean.class) .params(valueParam) .description("Checks whether a value is a time period") + .ruleBuilderEnabled(false) + .ruleBuilderName("Check if period") + .ruleBuilderTitle("Check if '${value}' is a time period") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.BOOLEAN) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Millis.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Millis.java index cc7e43d3871c0..2484dc7d6508a 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Millis.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Millis.java @@ -39,6 +39,18 @@ protected String getName() { @Nonnull @Override protected String getDescription() { - return "Create a period with a specified number of millis."; + return "Create a period with a specified number of milliseconds."; + } + + @Nonnull + @Override + protected String getRuleBuilderName() { + return "Create period (millis)"; + } + + @Nonnull + @Override + protected String getRuleBuilderTitle() { + return "Create a time period of '${value}' milliseconds"; } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Minutes.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Minutes.java index 9663e98821cce..d49108b606300 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Minutes.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Minutes.java @@ -41,4 +41,17 @@ protected String getName() { protected String getDescription() { return "Create a period with a specified number of minutes."; } + + @Nonnull + @Override + protected String getRuleBuilderName() { + return "Create period (minutes)"; + } + + @Nonnull + @Override + protected String getRuleBuilderTitle() { + return "Create a time period of '${value}' minutes"; + } + } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Months.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Months.java index e0a4496bad83c..7108ad3063fa7 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Months.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Months.java @@ -41,4 +41,16 @@ protected String getName() { protected String getDescription() { return "Create a period with a specified number of months."; } + + @Nonnull + @Override + protected String getRuleBuilderName() { + return "Create period (months)"; + } + + @Nonnull + @Override + protected String getRuleBuilderTitle() { + return "Create a time period of '${value}' months"; + } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/PeriodParseFunction.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/PeriodParseFunction.java index 17ac14b91cfb1..ac6e50b012afe 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/PeriodParseFunction.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/PeriodParseFunction.java @@ -21,6 +21,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import org.joda.time.Period; public class PeriodParseFunction extends AbstractFunction { @@ -42,10 +43,14 @@ public Period evaluate(FunctionArgs args, EvaluationContext context) { public FunctionDescriptor descriptor() { return FunctionDescriptor.builder() .name(NAME) - .description("Parses a ISO 8601 period from the specified string.") + .description("Parses an ISO 8601 period from the specified string.") .pure(true) .returnType(Period.class) .params(value) + .ruleBuilderEnabled(false) + .ruleBuilderName("Parse period") + .ruleBuilderTitle("Parse period from '${value}'") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.DATE) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Seconds.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Seconds.java index 92fa027a150a7..c8ca9ce8102e0 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Seconds.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Seconds.java @@ -41,4 +41,16 @@ protected String getName() { protected String getDescription() { return "Create a period with a specified number of seconds."; } + + @Nonnull + @Override + protected String getRuleBuilderName() { + return "Create period (seconds)"; + } + + @Nonnull + @Override + protected String getRuleBuilderTitle() { + return "Create a time period of '${value}' seconds"; + } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Weeks.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Weeks.java index 3d2a63f6a031a..ae0db2b5ec4fa 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Weeks.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Weeks.java @@ -41,4 +41,16 @@ protected String getName() { protected String getDescription() { return "Create a period with a specified number of weeks."; } + + @Nonnull + @Override + protected String getRuleBuilderName() { + return "Create period (weeks)"; + } + + @Nonnull + @Override + protected String getRuleBuilderTitle() { + return "Create a time period of '${value}' weeks"; + } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Years.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Years.java index 5fca596524c9c..1c04a278ccf41 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Years.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/dates/periods/Years.java @@ -43,4 +43,16 @@ protected String getName() { protected String getDescription() { return "Create a period with a specified number of years."; } + + @Nonnull + @Override + protected String getRuleBuilderName() { + return "Create period (years)"; + } + + @Nonnull + @Override + protected String getRuleBuilderTitle() { + return "Create a time period of '${value}' years"; + } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/ips/IpAddressConversion.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/ips/IpAddressConversion.java index ffa2afc66a57a..6653777127983 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/ips/IpAddressConversion.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/ips/IpAddressConversion.java @@ -22,6 +22,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import java.net.InetAddress; import java.util.IllegalFormatException; @@ -76,6 +77,10 @@ public FunctionDescriptor descriptor() { defaultParam )) .description("Converts a value to an IPAddress using its string representation") + .ruleBuilderEnabled(false) + .ruleBuilderName("Convert to IP") + .ruleBuilderTitle("Convert '${value}' to an IP address") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.CONVERSION) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/ips/IsIp.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/ips/IsIp.java index 69e7255404242..17cc6b3c5d53d 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/ips/IsIp.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/ips/IsIp.java @@ -21,6 +21,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import static org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor.object; @@ -30,7 +31,7 @@ public class IsIp extends AbstractFunction { private final ParameterDescriptor valueParam; public IsIp() { - valueParam = object("value").description("Value to check").build(); + valueParam = object("value").primary().description("Value to check").build(); } @Override @@ -46,6 +47,10 @@ public FunctionDescriptor descriptor() { .returnType(Boolean.class) .params(valueParam) .description("Checks whether a value is an IP address") + .ruleBuilderEnabled(false) + .ruleBuilderName("Check if IP") + .ruleBuilderTitle("Check if '${value}' is an IP address") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.BOOLEAN) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/json/IsJson.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/json/IsJson.java index fe57769d902e3..1329b73555d96 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/json/IsJson.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/json/IsJson.java @@ -22,6 +22,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import static org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor.object; @@ -31,7 +32,7 @@ public class IsJson extends AbstractFunction { private final ParameterDescriptor valueParam; public IsJson() { - valueParam = object("value").description("Value to check").build(); + valueParam = object("value").primary().description("Value to check").build(); } @Override @@ -46,7 +47,11 @@ public FunctionDescriptor descriptor() { .name(NAME) .returnType(Boolean.class) .params(valueParam) - .description("Checks whether a value is a JSON value") + .description("Checks whether a value is a parsed JSON tree") + .ruleBuilderEnabled(false) + .ruleBuilderName("Check if JSON") + .ruleBuilderTitle("Check if '${value}' is a parsed JSON tree") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.BOOLEAN) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/lookup/LookupHasValue.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/lookup/LookupHasValue.java index 98ae552eb4b71..8299d7d2ce55b 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/lookup/LookupHasValue.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/lookup/LookupHasValue.java @@ -21,6 +21,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import org.graylog2.lookup.LookupTableService; import org.graylog2.plugin.lookup.LookupResult; @@ -69,6 +70,10 @@ public FunctionDescriptor descriptor() { .description("Checks if lookup table contains a given key.") .params(lookupTableParam, keyParam) .returnType(Boolean.class) + .ruleBuilderEnabled() + .ruleBuilderName("Lookup value check") + .ruleBuilderTitle("Check if lookup table '${lookup_table}' contains '${value}'") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.LOOKUP) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/lookup/LookupValue.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/lookup/LookupValue.java index 56438ce58db08..4b1ce7da58757 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/lookup/LookupValue.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/lookup/LookupValue.java @@ -21,6 +21,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import org.graylog2.lookup.LookupTableService; import org.graylog2.plugin.lookup.LookupResult; @@ -43,7 +44,7 @@ public LookupValue(LookupTableService lookupTableService) { .description("The existing lookup table to use to lookup the given key") .transform(tableName -> lookupTableService.newBuilder().lookupTable(tableName).build()) .build(); - keyParam = object("key") + keyParam = object("key").primary() .description("The key to lookup in the table") .build(); defaultParam = object("default") @@ -77,6 +78,10 @@ public FunctionDescriptor descriptor() { .description("Looks up a single value in the named lookup table.") .params(lookupTableParam, keyParam, defaultParam) .returnType(Object.class) + .ruleBuilderEnabled() + .ruleBuilderName("Lookup single value") + .ruleBuilderTitle("Lookup single value in '${lookup_table}' using '${value}'<#if $default??> (default: '${default}')") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.LOOKUP) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/CloneMessage.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/CloneMessage.java index 966123606e1bd..49709bcc3eb13 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/CloneMessage.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/CloneMessage.java @@ -25,6 +25,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import org.graylog2.plugin.Message; import org.graylog2.shared.utilities.StringUtils; @@ -67,7 +68,7 @@ public Message evaluate(FunctionArgs args, EvaluationContext context) { } if (preventLoops.isEmpty() && cloneNumber >= MAX_CLONES) { - throw new IllegalStateException( + throw new IllegalStateException( StringUtils.f("Message was cloned more than %d times by rule '%s'. Not allowing any more " + "clones to prevent a potential endless loop. If this was intentional, please " + "explicitly set the 'preventLoops' parameter to 'false'.", @@ -95,7 +96,11 @@ public FunctionDescriptor descriptor() { .name(NAME) .params(ImmutableList.of(messageParam, loopDetectionParam)) .returnType(Message.class) - .description("Clones a message") + .description("Clones a message. If no specific message is provided, it clones the currently processed message") + .ruleBuilderEnabled(false) + .ruleBuilderName("Clone message") + .ruleBuilderTitle("Clone message") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.MESSAGE) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/CreateMessage.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/CreateMessage.java index b096db2baada3..7adcd6eaee25c 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/CreateMessage.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/CreateMessage.java @@ -21,6 +21,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import org.graylog2.plugin.Message; import org.graylog2.plugin.Tools; import org.joda.time.DateTime; @@ -76,7 +77,11 @@ public FunctionDescriptor descriptor() { sourceParam, timestampParam )) - .description("Creates a new message") + .description("Creates a new message which will be evaluated by the entire processing pipeline. Any omitted parameters (message, source, timestamp) will inherit their values from the currently processed message. The timestamp will inherit the current timestamp.") + .ruleBuilderEnabled(false) + .ruleBuilderName("Create message") + .ruleBuilderTitle("Create a new message") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.MESSAGE) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/DropMessage.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/DropMessage.java index 9efcefd52c2dc..354d37b42a580 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/DropMessage.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/DropMessage.java @@ -22,6 +22,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import org.graylog2.plugin.Message; import static org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor.type; @@ -53,6 +54,10 @@ public FunctionDescriptor descriptor() { messageParam )) .description("Discards a message from further processing, after completing the current stage") + .ruleBuilderEnabled() + .ruleBuilderName("Drop message") + .ruleBuilderTitle("Remove message from the processing pipeline after current stage") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.MESSAGE) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/GetField.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/GetField.java index ac955901cca53..9cdab2b009e97 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/GetField.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/GetField.java @@ -22,6 +22,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import org.graylog2.plugin.Message; import static org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor.type; @@ -54,7 +55,9 @@ public FunctionDescriptor descriptor() { .params(ImmutableList.of(fieldParam, messageParam)) .description("Retrieves the value for a field") .ruleBuilderEnabled() + .ruleBuilderName("Get field value") .ruleBuilderTitle("Retrieve value for field '${field}'") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.MESSAGE) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/HasField.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/HasField.java index d35f7b74b0416..2c215a122d0be 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/HasField.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/HasField.java @@ -22,6 +22,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import org.graylog2.plugin.Message; import static org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor.type; @@ -54,7 +55,9 @@ public FunctionDescriptor descriptor() { .params(ImmutableList.of(fieldParam, messageParam)) .description("Checks whether a message contains a value for a field") .ruleBuilderEnabled() + .ruleBuilderName("Has field") .ruleBuilderTitle("Message has field '${field}'") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.MESSAGE) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/NormalizeFields.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/NormalizeFields.java index 8a761b14af763..a590c3e0ad88a 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/NormalizeFields.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/NormalizeFields.java @@ -22,6 +22,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import org.graylog2.plugin.Message; import java.util.Locale; @@ -56,6 +57,10 @@ public FunctionDescriptor descriptor() { .returnType(Void.class) .params(ImmutableList.of(messageParam)) .description("Normalizes all field names by setting them to lowercase") + .ruleBuilderEnabled() + .ruleBuilderName("Normalize fields") + .ruleBuilderTitle("Normalize field names") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.MESSAGE) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/RemoveField.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/RemoveField.java index c3d3cac68a057..8186f025291e1 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/RemoveField.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/RemoveField.java @@ -22,6 +22,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import org.graylog2.plugin.Message; import java.util.stream.Collectors; @@ -59,7 +60,11 @@ public FunctionDescriptor descriptor() { .name(NAME) .returnType(Void.class) .params(ImmutableList.of(fieldParam, messageParam)) - .description("Removes a field from a message") + .description("Removes the named field from message, unless the field is reserved. If no specific message is provided, it removes the field from the currently processed message.") + .ruleBuilderEnabled() + .ruleBuilderName("Remove field") + .ruleBuilderTitle("Remove field '${field}'") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.MESSAGE) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/RemoveFromStream.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/RemoveFromStream.java index a2076d36976d1..b31c44f4d9d98 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/RemoveFromStream.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/RemoveFromStream.java @@ -21,6 +21,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import org.graylog2.plugin.Message; import org.graylog2.plugin.streams.DefaultStream; import org.graylog2.plugin.streams.Stream; @@ -99,7 +100,11 @@ public FunctionDescriptor descriptor() { nameParam, idParam, messageParam)) - .description("Removes a message from a stream. Removing the last stream will put the message back onto the default stream. To complete drop a message use the drop_message function.") + .description("Removes a message from a stream. Removing the last stream will put the message back onto the default stream. To completely drop a message use the drop_message function. If no specific message is provided, it removes the currently processed message from the stream.") + .ruleBuilderEnabled() + .ruleBuilderName("Remove from stream") + .ruleBuilderTitle("Remove message from stream<#if name??> '${name}'<#if id??> '${id}'") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.MESSAGE) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/RenameField.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/RenameField.java index e01ff271b8296..7c0c23ac1db0c 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/RenameField.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/RenameField.java @@ -21,6 +21,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import org.graylog2.plugin.Message; import static org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor.string; @@ -65,7 +66,11 @@ public FunctionDescriptor descriptor() { .name(NAME) .returnType(Void.class) .params(oldFieldParam, newFieldParam, messageParam) - .description("Rename a message field") + .description("Rename a message field. If no specific message is provided, it performs the renaming operation on the currently processed message.") + .ruleBuilderEnabled() + .ruleBuilderName("Rename field") + .ruleBuilderTitle("Rename field '${old_field}' to '${new_field}' in the message") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.MESSAGE) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/RouteToStream.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/RouteToStream.java index 38f0f24743e24..b65ddce4bcf1e 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/RouteToStream.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/RouteToStream.java @@ -21,6 +21,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import org.graylog2.plugin.Message; import org.graylog2.plugin.streams.DefaultStream; import org.graylog2.plugin.streams.Stream; @@ -103,7 +104,11 @@ public FunctionDescriptor descriptor() { idParam, messageParam, removeFromDefault)) - .description("Routes a message to a stream") + .description("Routes a message to a stream. If no specific message is provided, it assigns the stream to the currently processed message") + .ruleBuilderEnabled() + .ruleBuilderName("Route to stream") + .ruleBuilderTitle("Route message to stream<#if name??> '${name}'<#if id??> '${id}'") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.MESSAGE) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/SetField.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/SetField.java index 97be76525e876..1069ef63fdd78 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/SetField.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/SetField.java @@ -22,6 +22,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import org.graylog2.plugin.Message; import java.util.Optional; @@ -62,8 +63,7 @@ public Void evaluate(FunctionArgs args, EvaluationContext context) { if (!args.isPresent("default")) { throw e; } - } - finally { + } finally { if (value == null) { value = defaultParam.optional(args, context).orElse(null); } @@ -96,9 +96,11 @@ public FunctionDescriptor descriptor() { suffixParam, messageParam, defaultParam)) - .description("Sets a new field in a message") + .description("Sets the given value to the named field. If no specific message is provided, it sets the field in the currently processed message.") .ruleBuilderEnabled() - .ruleBuilderTitle("Set value to field '${field}'") + .ruleBuilderName("Set to field") + .ruleBuilderTitle("Set '${value}' to field '${field}'") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.MESSAGE) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/SetFields.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/SetFields.java index 15aab1ff4b2f8..b09ba2cb4e770 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/SetFields.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/messages/SetFields.java @@ -21,6 +21,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import org.graylog2.plugin.Message; import java.util.Map; @@ -40,7 +41,7 @@ public class SetFields extends AbstractFunction { private final ParameterDescriptor messageParam; public SetFields() { - fieldsParam = type("fields", Map.class).description("The map of new fields to set").build(); + fieldsParam = type("fields", Map.class).primary().description("The map of new fields to set").build(); prefixParam = string("prefix").optional().description("The prefix for the field names").build(); suffixParam = string("suffix").optional().description("The suffix for the field names").build(); messageParam = type("message", Message.class).optional().description("The message to use, defaults to '$message'").build(); @@ -74,7 +75,11 @@ public FunctionDescriptor descriptor() { .name(NAME) .returnType(Void.class) .params(of(fieldsParam, prefixParam, suffixParam, messageParam)) - .description("Sets new fields in a message") + .description("Sets new fields in a message. If no specific message is provided, it sets the fields in the currently processed message") + .ruleBuilderEnabled(false) + .ruleBuilderName("Set fields") + .ruleBuilderTitle("Set fields from map '${fields}'") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.MESSAGE) .build(); } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Abbreviate.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Abbreviate.java index 45f2fd6bb793f..577f4819fc206 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Abbreviate.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Abbreviate.java @@ -23,6 +23,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import static com.google.common.primitives.Ints.saturatedCast; @@ -35,7 +36,7 @@ public class Abbreviate extends AbstractFunction { private final ParameterDescriptor widthParam; public Abbreviate() { - valueParam = ParameterDescriptor.string(VALUE).description("The string to abbreviate").build(); + valueParam = ParameterDescriptor.string(VALUE).primary().description("The string to abbreviate").build(); widthParam = ParameterDescriptor.integer(WIDTH).description("The maximum number of characters including the '...' (at least 4)").build(); } @@ -64,6 +65,10 @@ public FunctionDescriptor descriptor() { widthParam )) .description("Abbreviates a string by appending '...' to fit into a maximum amount of characters") + .ruleBuilderEnabled() + .ruleBuilderName("Abbreviate string") + .ruleBuilderTitle("Abbreviate '${value}' to ${width} characters") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.STRING) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Capitalize.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Capitalize.java index e5785645f6d67..7f759a8ba6a41 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Capitalize.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Capitalize.java @@ -18,6 +18,7 @@ import org.apache.commons.lang3.StringUtils; +import javax.annotation.Nonnull; import java.util.Locale; public class Capitalize extends StringUtilsFunction { @@ -43,4 +44,16 @@ protected boolean isLocaleAware() { protected String apply(String value, Locale unused) { return StringUtils.capitalize(value); } + + @Nonnull + @Override + protected String getRuleBuilderName() { + return "Capitalize string"; + } + + @Nonnull + @Override + protected String getRuleBuilderTitle() { + return "Capitalize '${value}' by changing the first letter to title case (upper case)"; + } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Concat.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Concat.java index b8ef0cebcde51..7d3fefd2a457c 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Concat.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Concat.java @@ -22,6 +22,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import static com.google.common.collect.ImmutableList.of; @@ -50,6 +51,10 @@ public FunctionDescriptor descriptor() { .returnType(String.class) .params(of(firstParam, secondParam)) .description("Concatenates two strings") + .ruleBuilderEnabled() + .ruleBuilderName("Concatenate strings") + .ruleBuilderTitle("Concatenate '${first}' and '${second}'") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.STRING) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Join.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Join.java index f13e0e595605f..de85d8c5d1432 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Join.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Join.java @@ -24,6 +24,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import java.util.Collection; import java.util.Collections; @@ -39,6 +40,7 @@ public class Join extends AbstractFunction { public Join() { elementsParam = ParameterDescriptor.type("elements", Object.class, List.class) + .primary() .transform(Join::toList) .description("The list of strings to join together, may be null") .build(); @@ -83,6 +85,10 @@ public FunctionDescriptor descriptor() { .returnType(String.class) .params(ImmutableList.of(elementsParam, delimiterParam, startIndexParam, endIndexParam)) .description("Joins the elements of the provided array into a single String") + .ruleBuilderEnabled(false) + .ruleBuilderName("Join array to string") + .ruleBuilderTitle("Join '${elements}' into a single string, <#if start??>starting with ${start} <#if indexEnd??>and ending with ${indexEnd} <#/if>using '${delimiter!' '} as separator.") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.STRING) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Length.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Length.java index d88d91744719f..7f2af256dc99a 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Length.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Length.java @@ -22,6 +22,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import java.nio.charset.StandardCharsets; import java.util.Objects; @@ -35,7 +36,7 @@ public class Length extends AbstractFunction { private final ParameterDescriptor bytesParam; public Length() { - valueParam = ParameterDescriptor.string(VALUE).description("The input string").build(); + valueParam = ParameterDescriptor.string(VALUE).primary().description("The input string").build(); bytesParam = ParameterDescriptor.bool(BYTES) .description("If true, count the bytes of the UTF-8 string instead of the characters") .optional().build(); @@ -58,6 +59,10 @@ public FunctionDescriptor descriptor() { .returnType(Long.class) .params(ImmutableList.of(valueParam, bytesParam)) .description("Counts the characters or bytes in a string") + .ruleBuilderEnabled() + .ruleBuilderName("Length of string") + .ruleBuilderTitle("Count the characters of '${value}'") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.STRING) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Lowercase.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Lowercase.java index 5dc6a1cf0a588..8cc8710cadafe 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Lowercase.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Lowercase.java @@ -18,6 +18,7 @@ import org.apache.commons.lang3.StringUtils; +import javax.annotation.Nonnull; import java.util.Locale; public class Lowercase extends StringUtilsFunction { @@ -43,4 +44,16 @@ protected boolean isLocaleAware() { protected String apply(String value, Locale locale) { return StringUtils.lowerCase(value, locale); } + + @Nonnull + @Override + protected String getRuleBuilderName() { + return "Lowercase string"; + } + + @Nonnull + @Override + protected String getRuleBuilderTitle() { + return "Convert '${value}' to lower case"; + } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/RegexReplace.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/RegexReplace.java index f8f89eafd7157..3b260ce321e43 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/RegexReplace.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/RegexReplace.java @@ -21,6 +21,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import java.util.regex.Pattern; @@ -37,7 +38,7 @@ public class RegexReplace extends AbstractFunction { public RegexReplace() { patternParam = ParameterDescriptor.string("pattern", Pattern.class).transform(Pattern::compile).description("The regular expression to which the \"value\" string is to be matched; uses Java regex syntax").build(); - valueParam = ParameterDescriptor.string("value").description("The string to match the pattern against").build(); + valueParam = ParameterDescriptor.string("value").primary().description("The string to match the pattern against").build(); replacementParam = ParameterDescriptor.string("replacement").description("The string to be substituted for the first or all matches").build(); replaceAllParam = ParameterDescriptor.bool("replace_all").optional().description("Replace all matches if \"true\", otherwise only replace the first match. Default: true").build(); } @@ -67,7 +68,11 @@ public FunctionDescriptor descriptor() { .pure(true) .returnType(String.class) .params(of(patternParam, valueParam, replacementParam, replaceAllParam)) - .description("Match a string with a regular expression (Java syntax)") + .description("Match a string with a regular expression (Java syntax) and replace all matches with string") + .ruleBuilderEnabled() + .ruleBuilderName("Regex replace") + .ruleBuilderTitle("Match the regular expression '${pattern}' against '${value}' and replace it with '${replacement}'") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.STRING) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Replace.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Replace.java index bcbe8edf1b290..36afd72b57055 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Replace.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Replace.java @@ -23,6 +23,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import static com.google.common.collect.ImmutableList.of; @@ -34,7 +35,7 @@ public class Replace extends AbstractFunction { private final ParameterDescriptor maxParam; public Replace() { - valueParam = ParameterDescriptor.string("value").description("The text to search and replace in").build(); + valueParam = ParameterDescriptor.string("value").primary().description("The text to search and replace in").build(); searchParam = ParameterDescriptor.string("search").description("The string to search for").build(); replacementParam = ParameterDescriptor.string("replacement").optional() .description("The string to replace it with. Default: \"\"").build(); @@ -60,6 +61,10 @@ public FunctionDescriptor descriptor() { .returnType(String.class) .params(of(valueParam, searchParam, replacementParam, maxParam)) .description("Replaces the first \"max\" or all occurrences of a string within another string") + .ruleBuilderEnabled() + .ruleBuilderName("Replace in string") + .ruleBuilderTitle("Replace <#if max??>the first ${max} <#else>all occurrences of '${search}' within '${value}' with '${replacement!''}'") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.STRING) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Split.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Split.java index e9271be36a2cf..548651fbb2e26 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Split.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Split.java @@ -24,6 +24,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import java.util.List; import java.util.regex.Pattern; @@ -47,7 +48,7 @@ public Split() { .transform(Pattern::compile) .description("The regular expression to split by, uses Java regex syntax") .build(); - value = ParameterDescriptor.string("value") + value = ParameterDescriptor.string("value").primary() .description("The string to be split") .build(); limit = ParameterDescriptor.integer("limit", Integer.class) @@ -75,6 +76,10 @@ public FunctionDescriptor> descriptor() { .returnType(RETURN_TYPE) .params(ImmutableList.of(pattern, value, limit)) .description("Split a string around matches of this pattern (Java syntax)") + .ruleBuilderEnabled(false) + .ruleBuilderName("Split string") + .ruleBuilderTitle("Split '${value}' around matches of pattern '${pattern}'") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.STRING) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/StringUtilsFunction.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/StringUtilsFunction.java index c6e5220978f9c..d025c9b09e675 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/StringUtilsFunction.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/StringUtilsFunction.java @@ -22,7 +22,9 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; +import javax.annotation.Nonnull; import java.util.Locale; public abstract class StringUtilsFunction extends AbstractFunction { @@ -33,7 +35,7 @@ public abstract class StringUtilsFunction extends AbstractFunction { private final ParameterDescriptor localeParam; public StringUtilsFunction() { - valueParam = ParameterDescriptor.string(VALUE).description("The input string").build(); + valueParam = ParameterDescriptor.string(VALUE).primary().description("The input string").build(); localeParam = ParameterDescriptor.string(LOCALE, Locale.class) .optional() .transform(Locale::forLanguageTag) @@ -63,6 +65,10 @@ public FunctionDescriptor descriptor() { .returnType(String.class) .params(params.build()) .description(description()) + .ruleBuilderEnabled() + .ruleBuilderName(getRuleBuilderName()) + .ruleBuilderTitle(getRuleBuilderTitle()) + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.STRING) .build(); } @@ -73,4 +79,10 @@ public FunctionDescriptor descriptor() { protected abstract boolean isLocaleAware(); protected abstract String apply(String value, Locale locale); + + @Nonnull + protected abstract String getRuleBuilderName(); + + @Nonnull + protected abstract String getRuleBuilderTitle(); } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Substring.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Substring.java index fc10360e224e0..ddd55ddac3d1c 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Substring.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Substring.java @@ -23,6 +23,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import static com.google.common.collect.ImmutableList.of; @@ -63,6 +64,10 @@ public FunctionDescriptor descriptor() { endParam )) .description("Extract a substring from a string") + .ruleBuilderEnabled() + .ruleBuilderName("Substring") + .ruleBuilderTitle("Get substring from '${start}' to '${indexEnd!\"end\"}' of '${value}'") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.STRING) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Swapcase.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Swapcase.java index 24d7f0dc1e02c..f71340f86d921 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Swapcase.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Swapcase.java @@ -18,6 +18,7 @@ import org.apache.commons.lang3.StringUtils; +import javax.annotation.Nonnull; import java.util.Locale; public class Swapcase extends StringUtilsFunction { @@ -43,4 +44,16 @@ protected boolean isLocaleAware() { protected String apply(String value, Locale unused) { return StringUtils.swapCase(value); } + + @Nonnull + @Override + protected String getRuleBuilderName() { + return "Swap case string"; + } + + @Nonnull + @Override + protected String getRuleBuilderTitle() { + return "Swap the case of '${value}'"; + } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Uncapitalize.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Uncapitalize.java index 096b339d1dc3c..6480b3d96b067 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Uncapitalize.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Uncapitalize.java @@ -18,6 +18,7 @@ import org.apache.commons.lang3.StringUtils; +import javax.annotation.Nonnull; import java.util.Locale; public class Uncapitalize extends StringUtilsFunction { @@ -43,4 +44,16 @@ protected boolean isLocaleAware() { protected String apply(String value, Locale unused) { return StringUtils.uncapitalize(value); } + + @Nonnull + @Override + protected String getRuleBuilderName() { + return "Uncapitalize string"; + } + + @Nonnull + @Override + protected String getRuleBuilderTitle() { + return "Uncapitalize '${value}' by changing the first letter to lower case"; + } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Uppercase.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Uppercase.java index 19a0dadd45183..2ad9259316e2d 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Uppercase.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/strings/Uppercase.java @@ -18,6 +18,7 @@ import org.apache.commons.lang3.StringUtils; +import javax.annotation.Nonnull; import java.util.Locale; public class Uppercase extends StringUtilsFunction { @@ -43,4 +44,16 @@ protected boolean isLocaleAware() { protected String apply(String value, Locale locale) { return StringUtils.upperCase(value, locale); } + + @Nonnull + @Override + protected String getRuleBuilderName() { + return "Upper case string"; + } + + @Nonnull + @Override + protected String getRuleBuilderTitle() { + return "Convert '${value}' to upper case"; + } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/urls/IsUrl.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/urls/IsUrl.java index e0d9a18e0477c..48e885d498e76 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/urls/IsUrl.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/urls/IsUrl.java @@ -21,6 +21,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import static org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor.object; @@ -30,7 +31,7 @@ public class IsUrl extends AbstractFunction { private final ParameterDescriptor valueParam; public IsUrl() { - valueParam = object("value").description("Value to check").build(); + valueParam = object("value").primary().description("Value to check").build(); } @Override @@ -46,6 +47,10 @@ public FunctionDescriptor descriptor() { .returnType(Boolean.class) .params(valueParam) .description("Checks whether a value is a URL") + .ruleBuilderEnabled(false) + .ruleBuilderName("Check if URL") + .ruleBuilderTitle("Check whether '${value}' is a URL") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.BOOLEAN) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/urls/UrlConversion.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/urls/UrlConversion.java index 14c80f56aca29..bbc308c51397c 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/urls/UrlConversion.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/functions/urls/UrlConversion.java @@ -22,6 +22,7 @@ import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionArgs; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import java.util.Optional; @@ -61,6 +62,10 @@ public FunctionDescriptor descriptor() { .params(urlParam, defaultParam) .description("Converts a value to a valid URL using its string representation") + .ruleBuilderEnabled(false) + .ruleBuilderName("Convert to URL") + .ruleBuilderTitle("Convert '${value}' to URL") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.CONVERSION) .build(); } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/RuleBuilder.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/RuleBuilder.java index 1de3452eba0d9..b00ae004c80da 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/RuleBuilder.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/RuleBuilder.java @@ -26,10 +26,15 @@ @AutoValue public abstract class RuleBuilder { + protected static final String FIELD_OPERATOR = "operator"; protected static final String FIELD_CONDITIONS = "conditions"; protected static final String FIELD_ACTIONS = "actions"; protected static final String FIELD_ERRORS = "errors"; + @JsonProperty(FIELD_OPERATOR) + @Nullable + public abstract RuleBuilderStep.Operator operator(); + @JsonProperty(FIELD_CONDITIONS) @Nullable public abstract List conditions(); @@ -49,10 +54,12 @@ public static Builder builder() { public abstract Builder toBuilder(); @JsonCreator - public static RuleBuilder create(@JsonProperty(FIELD_CONDITIONS) @Nullable List conditions, + public static RuleBuilder create(@JsonProperty(FIELD_OPERATOR) @Nullable RuleBuilderStep.Operator operator, + @JsonProperty(FIELD_CONDITIONS) @Nullable List conditions, @JsonProperty(FIELD_ACTIONS) @Nullable List actions, @JsonProperty(FIELD_ERRORS) @Nullable List errors) { return builder() + .operator(operator) .conditions(conditions) .actions(actions) .errors(errors) @@ -63,6 +70,8 @@ public static RuleBuilder create(@JsonProperty(FIELD_CONDITIONS) @Nullable List< public abstract static class Builder { public abstract RuleBuilder build(); + public abstract Builder operator(RuleBuilderStep.Operator operator); + public abstract Builder conditions(List conditions); public abstract Builder actions(List actions); diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/RuleBuilderFunctionGroup.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/RuleBuilderFunctionGroup.java new file mode 100644 index 0000000000000..0fcebb705ec18 --- /dev/null +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/RuleBuilderFunctionGroup.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2020 Graylog, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * . + */ +package org.graylog.plugins.pipelineprocessor.rulebuilder; + +import com.fasterxml.jackson.annotation.JsonValue; +import org.graylog2.plugin.Message; + +public enum RuleBuilderFunctionGroup { + + MESSAGE("Message Functions", 0), + STRING("String Functions", 10), + NUMBER("Number Functions", 20), + BOOLEAN("Boolean Functions", 22), + CONVERSION("Conversion Functions", 25), + DATE("Date Functions", 30), + PATTERN("Pattern Matching Functions", 35), + LOOKUP("Lookup Table Functions", 37), + EXTRACTORS("Extractor Functions", 40), + OTHER("Other", 999); + + private String name; + private int sort; + + RuleBuilderFunctionGroup(String name, int sort) { + this.name = name; + this.sort = sort; + } + + public static RuleBuilderFunctionGroup map(Class primaryParam) { + if (primaryParam == String.class) { + return STRING; + } else if (primaryParam == Message.class) { + return MESSAGE; + } + return OTHER; + } + + @JsonValue + public String getName() { + return name; + } +} diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/RuleBuilderModule.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/RuleBuilderModule.java index ea094386154c3..65f944ce26506 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/RuleBuilderModule.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/RuleBuilderModule.java @@ -23,6 +23,8 @@ import org.graylog.plugins.pipelineprocessor.rulebuilder.db.migrations.V20220512123200_AddSimpleConditionFragments; import org.graylog.plugins.pipelineprocessor.rulebuilder.db.migrations.V20220522125200_AddSetGrokToFieldsExtractorFragments; import org.graylog.plugins.pipelineprocessor.rulebuilder.db.migrations.V20230613154400_AddImplicitToStringFragments; +import org.graylog.plugins.pipelineprocessor.rulebuilder.db.migrations.V20230720161500_AddExtractorFragments; +import org.graylog.plugins.pipelineprocessor.rulebuilder.db.migrations.V20230724092100_AddFieldConditions; import org.graylog.plugins.pipelineprocessor.rulebuilder.parser.validation.Validator; import org.graylog.plugins.pipelineprocessor.rulebuilder.parser.validation.action.ValidAction; import org.graylog.plugins.pipelineprocessor.rulebuilder.parser.validation.action.ValidNewMessageField; @@ -45,6 +47,8 @@ protected void configure() { migrationBinder.addBinding().to(V20220512123200_AddSimpleConditionFragments.class); migrationBinder.addBinding().to(V20220522125200_AddSetGrokToFieldsExtractorFragments.class); migrationBinder.addBinding().to(V20230613154400_AddImplicitToStringFragments.class); + migrationBinder.addBinding().to(V20230720161500_AddExtractorFragments.class); + migrationBinder.addBinding().to(V20230724092100_AddFieldConditions.class); final Multibinder condition = Multibinder.newSetBinder(binder(), Validator.class, Names.named("conditionValidators")); condition.addBinding().to(ValidCondition.class); diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/RuleBuilderRegistry.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/RuleBuilderRegistry.java index dd23c66e088c5..5423c3e4f55ce 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/RuleBuilderRegistry.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/RuleBuilderRegistry.java @@ -23,7 +23,6 @@ import javax.inject.Inject; import java.util.Map; -import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -48,7 +47,8 @@ public Map conditions() { .descriptor(f.descriptor()) .build() ); - final Stream fragmentConditions = ruleFragmentService.all().stream().filter(RuleFragment::isCondition); + final Stream fragmentConditions = ruleFragmentService.all().stream() + .filter(f -> f.descriptor().ruleBuilderEnabled() && f.isCondition()); return Streams.concat(functionConditions, fragmentConditions) .collect(Collectors.toMap(f -> f.descriptor().name(), function -> function)); } @@ -56,13 +56,15 @@ public Map conditions() { public Map actions() { final Stream functions = functionRegistry.all() .stream() - .filter(f -> f.descriptor().ruleBuilderEnabled()) + .filter(f -> f.descriptor().ruleBuilderEnabled() + && !f.descriptor().returnType().equals(Boolean.class)) .map(f -> RuleFragment.builder() .descriptor(f.descriptor()) .build() ); final Stream fragmentActions = - ruleFragmentService.all().stream().filter(Predicate.not(RuleFragment::isCondition)); + ruleFragmentService.all().stream() + .filter(f -> f.descriptor().ruleBuilderEnabled() && !f.isCondition()); return Streams.concat(functions, fragmentActions) .collect(Collectors.toMap(f -> f.descriptor().name(), function -> function)); } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/RuleBuilderStep.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/RuleBuilderStep.java index 3d52139f57150..ede418ca86bc0 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/RuleBuilderStep.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/RuleBuilderStep.java @@ -36,6 +36,8 @@ public abstract class RuleBuilderStep { public static final String FIELD_OUTPUT = "outputvariable"; public static final String FIELD_NEGATE = "negate"; public static final String FIELD_TITLE = "step_title"; + public static final String FIELD_OPERATOR = "operator"; + public static final String FIELD_NESTED_CONDITIONS = "conditions"; @JsonProperty(FIELD_ID) @Nullable @@ -60,6 +62,14 @@ public abstract class RuleBuilderStep { @Nullable public abstract String title(); + @JsonProperty(FIELD_OPERATOR) + @Nullable + public abstract Operator operator(); + + @JsonProperty(FIELD_NESTED_CONDITIONS) + @Nullable + public abstract List conditions(); + @JsonProperty(FIELD_ERRORS) @Nullable public abstract List errors(); @@ -71,6 +81,8 @@ public static RuleBuilderStep create(@JsonProperty(FIELD_ID) @Nullable String id @JsonProperty(FIELD_OUTPUT) @Nullable String outputvariable, @JsonProperty(FIELD_NEGATE) @Nullable boolean negate, @JsonProperty(FIELD_TITLE) @Nullable String title, + @JsonProperty(FIELD_OPERATOR) @Nullable Operator operator, + @JsonProperty(FIELD_NESTED_CONDITIONS) @Nullable List conditions, @JsonProperty(FIELD_ERRORS) @Nullable List errors) { return builder() .id(id) @@ -79,6 +91,8 @@ public static RuleBuilderStep create(@JsonProperty(FIELD_ID) @Nullable String id .outputvariable(outputvariable) .negate(negate) .title(title) + .operator(operator) + .conditions(conditions) .errors(errors) .build(); } @@ -109,9 +123,18 @@ public Builder negate() { public abstract Builder title(String title); + public abstract Builder operator(Operator operator); + + public abstract Builder conditions(List conditions); + public abstract Builder errors(List errors); public abstract RuleBuilderStep build(); } + + public enum Operator { + AND, OR + } + } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/RuleFragmentService.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/RuleFragmentService.java index 15aba0967c08f..afd62556f0f5c 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/RuleFragmentService.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/RuleFragmentService.java @@ -27,4 +27,10 @@ public interface RuleFragmentService { Optional get(String name); Collection all(); + + default RuleFragment upsert(RuleFragment ruleFragment) { + this.get(ruleFragment.getName()).ifPresent(fragment -> this.delete(fragment.getName())); + return this.save(ruleFragment); + } + } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20220512123200_AddSimpleConditionFragments.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20220512123200_AddSimpleConditionFragments.java index 91689ed546f91..002709e9cbd3f 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20220512123200_AddSimpleConditionFragments.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20220512123200_AddSimpleConditionFragments.java @@ -18,6 +18,7 @@ import com.google.common.collect.ImmutableList; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import org.graylog.plugins.pipelineprocessor.rulebuilder.db.RuleFragment; import org.graylog.plugins.pipelineprocessor.rulebuilder.db.RuleFragmentService; import org.graylog2.migrations.Migration; @@ -28,8 +29,8 @@ import javax.inject.Inject; import java.time.ZonedDateTime; import java.util.Objects; -import java.util.Optional; +import static org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor.bool; import static org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor.integer; import static org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor.string; @@ -57,20 +58,14 @@ public void upgrade() { // return; } - addFragment(createHasFieldEqualsFragment()); - addFragment(createHasFieldGreateOrEqualFragment()); - addFragment(createHasFieldLessOrEqualFragment()); + ruleFragmentService.upsert(createHasFieldEqualsFragment()); + ruleFragmentService.upsert(createHasFieldGreateOrEqualFragment()); + ruleFragmentService.upsert(createHasFieldLessOrEqualFragment()); clusterConfigService.write(new MigrationCompleted()); log.debug("has_field_equals, has_field_greater_or_equal, has_field_less_or_equal fragments were successfully added"); } - private void addFragment(RuleFragment ruleFragment) { - Optional existingFragment = ruleFragmentService.get(ruleFragment.getName()); - existingFragment.ifPresent(fragment -> ruleFragmentService.delete(fragment.getName())); - ruleFragmentService.save(ruleFragment); - } - private RuleFragment createHasFieldLessOrEqualFragment() { return RuleFragment.builder() .fragment("( has_field(${field}) && to_long($message.${field}) <= ${fieldValue} )") @@ -83,7 +78,9 @@ private RuleFragment createHasFieldLessOrEqualFragment() { .returnType(Boolean.class) .description("Checks if the message has a field and if this field's numeric value is less than or equal to the given fieldValue") .ruleBuilderEnabled() + .ruleBuilderName("Field <=") .ruleBuilderTitle("Field '${field}' less than or equal '${fieldValue}'") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.NUMBER) .build()) .isCondition() .build(); @@ -101,7 +98,9 @@ private RuleFragment createHasFieldGreateOrEqualFragment() { .returnType(Boolean.class) .description("Checks if the message has a field and if this field's numeric value is greater than or equal to the given fieldValue") .ruleBuilderEnabled() + .ruleBuilderName("Field >=") .ruleBuilderTitle("Field '${field}' greater than or equal '${fieldValue}'") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.NUMBER) .build()) .isCondition() .build(); @@ -109,17 +108,28 @@ private RuleFragment createHasFieldGreateOrEqualFragment() { private RuleFragment createHasFieldEqualsFragment() { return RuleFragment.builder() - .fragment("( has_field(${field}) && to_string($message.${field}) == ${fieldValue} )") + .fragment(""" + ( has_field(${field}) && + <#if caseInsensitive!false> + lower(to_string($message.${field})) == lower(${fieldValue}) + <#else> + to_string($message.${field}) == ${fieldValue} + + ) + """) .descriptor(FunctionDescriptor.builder() .name("has_field_equals") .params(ImmutableList.of( string("field").description("Message field to check against").build(), - string("fieldValue").description("Field value to check for").build() + string("fieldValue").description("Field value to check for").build(), + bool("caseInsensitive").optional().description("Ignore case").build() )) .returnType(Boolean.class) .description("Checks if the message has a field and if this field's string value is equal to the given fieldValue") .ruleBuilderEnabled() - .ruleBuilderTitle("Field '${field}' equals '${fieldValue}'") + .ruleBuilderName("Field equals") + .ruleBuilderTitle("Field '${field}' equals '${fieldValue}' <#if caseInsensitive??>(case insensitive: ${caseInsensitive})") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.STRING) .build()) .isCondition() .build(); diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20220522125200_AddSetGrokToFieldsExtractorFragments.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20220522125200_AddSetGrokToFieldsExtractorFragments.java index 987e48f7ec090..8a20935da1548 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20220522125200_AddSetGrokToFieldsExtractorFragments.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20220522125200_AddSetGrokToFieldsExtractorFragments.java @@ -18,6 +18,7 @@ import com.google.common.collect.ImmutableList; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import org.graylog.plugins.pipelineprocessor.rulebuilder.db.RuleFragment; import org.graylog.plugins.pipelineprocessor.rulebuilder.db.RuleFragmentService; import org.graylog2.migrations.Migration; @@ -28,7 +29,6 @@ import javax.inject.Inject; import java.time.ZonedDateTime; import java.util.Objects; -import java.util.Optional; import static org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor.bool; import static org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor.string; @@ -54,32 +54,28 @@ public void upgrade() { log.debug("Adding set_grok_to_fields extractor fragments via migration"); if (Objects.nonNull(clusterConfigService.get(MigrationCompleted.class))) { log.debug("Migration already completed!"); - return; +// return; } - RuleFragment setGrokTFieldsFragment = createSetGrokToFieldsFragment(); - ruleFragmentService.get(setGrokTFieldsFragment.getName()).ifPresent(fragment -> { - ruleFragmentService.delete(fragment.getName()); - }); + ruleFragmentService.upsert(createSetGrokToFieldsFragment()); - ruleFragmentService.save(setGrokTFieldsFragment); clusterConfigService.write(new MigrationCompleted()); log.debug("set_grok_to_fields fragment was successfully added"); } - private static RuleFragment createSetGrokToFieldsFragment() { + static RuleFragment createSetGrokToFieldsFragment() { return RuleFragment.builder() .fragment(""" let gl2_fragment_grok_results = grok( - pattern: "${grokPattern}", - value: to_string($message.${field}), - only_named_captures: ${grokNamedOnly?c} + pattern: ${grokPattern}, + value: to_string($message.${field})<#if grokNamedOnly??>, + only_named_captures: ${grokNamedOnly?c} ); set_fields( - fields: gl2_fragment_grok_results, - prefix: "${prefix!""}", - suffix: "${suffix!""}" + fields: gl2_fragment_grok_results<#if prefix??>, + prefix: ${prefix!""}<#if suffix??>, + suffix: ${suffix!""} );""") .descriptor(FunctionDescriptor.builder() .name("set_grok_to_fields") @@ -93,16 +89,12 @@ private static RuleFragment createSetGrokToFieldsFragment() { .returnType(Void.class) .description("Match grok pattern and set fields") .ruleBuilderEnabled() - .ruleBuilderTitle("Match grok pattern on field '${field}' and set fields") + .ruleBuilderName("Extract grok to fields") + .ruleBuilderTitle("Match grok pattern '${grokPattern}' on field '${field}' and set fields for matches") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.EXTRACTORS) .build()) .build(); } - private void addFragment(RuleFragment ruleFragment) { - Optional existingFragment = ruleFragmentService.get(ruleFragment.getName()); - existingFragment.ifPresent(fragment -> ruleFragmentService.delete(fragment.getName())); - ruleFragmentService.save(ruleFragment); - } - public record MigrationCompleted() {} } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20230613154400_AddImplicitToStringFragments.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20230613154400_AddImplicitToStringFragments.java index 7a4bcb3e9dc71..e932148430a63 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20230613154400_AddImplicitToStringFragments.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20230613154400_AddImplicitToStringFragments.java @@ -18,6 +18,7 @@ import com.google.common.collect.ImmutableList; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; import org.graylog.plugins.pipelineprocessor.rulebuilder.db.RuleFragment; import org.graylog.plugins.pipelineprocessor.rulebuilder.db.RuleFragmentService; import org.graylog2.migrations.Migration; @@ -29,7 +30,6 @@ import javax.inject.Inject; import java.time.ZonedDateTime; import java.util.Objects; -import java.util.Optional; import static org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor.integer; import static org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor.object; @@ -59,8 +59,8 @@ public void upgrade() { // return; } - addFragment(createSubstringFragment()); - addFragment(createDateFragment()); + ruleFragmentService.upsert(createSubstringFragment()); + ruleFragmentService.upsert(createDateFragment()); clusterConfigService.write(new MigrationCompleted()); log.debug("implicit to_string fragments were successfully added"); @@ -83,8 +83,10 @@ private static RuleFragment createSubstringFragment() { )) .returnType(String.class) .description("Get substring of value") - .ruleBuilderEnabled() + .ruleBuilderEnabled(false) + .ruleBuilderName("Substring") .ruleBuilderTitle("Get substring from '${start}' to '${end!\"end\"}' of value") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.STRING) .build()) .fragmentOutputVariable("gl2_fragment_substring_results") .build(); @@ -97,7 +99,7 @@ private static RuleFragment createDateFragment() { value: to_string(${value}), pattern: ${pattern}<#if locale??>, locale: ${locale}<#if timezone??>, - locale: ${timezone} + timezone: ${timezone} );""") .descriptor(FunctionDescriptor.builder() .name("get_date") @@ -109,18 +111,14 @@ private static RuleFragment createDateFragment() { )) .returnType(DateTime.class) .description("Parses a value using the given date format") - .ruleBuilderEnabled() + .ruleBuilderEnabled(false) + .ruleBuilderName("Parse date") .ruleBuilderTitle("Parse date from value") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.DATE) .build()) .fragmentOutputVariable("gl2_fragment_date_results") .build(); } - private void addFragment(RuleFragment ruleFragment) { - Optional existingFragment = ruleFragmentService.get(ruleFragment.getName()); - existingFragment.ifPresent(fragment -> ruleFragmentService.delete(fragment.getName())); - ruleFragmentService.save(ruleFragment); - } - public record MigrationCompleted() {} } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20230720161500_AddExtractorFragments.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20230720161500_AddExtractorFragments.java new file mode 100644 index 0000000000000..831fb7c640d9d --- /dev/null +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20230720161500_AddExtractorFragments.java @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2020 Graylog, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * . + */ +package org.graylog.plugins.pipelineprocessor.rulebuilder.db.migrations; + +import com.google.common.collect.ImmutableList; +import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; +import org.graylog.plugins.pipelineprocessor.rulebuilder.db.RuleFragment; +import org.graylog.plugins.pipelineprocessor.rulebuilder.db.RuleFragmentService; +import org.graylog2.migrations.Migration; +import org.graylog2.plugin.cluster.ClusterConfigService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import java.time.ZonedDateTime; +import java.util.Objects; + +import static org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor.bool; +import static org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor.integer; +import static org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor.string; + +public class V20230720161500_AddExtractorFragments extends Migration { + + private static final Logger log = LoggerFactory.getLogger(V20230720161500_AddExtractorFragments.class); + private final RuleFragmentService ruleFragmentService; + private final ClusterConfigService clusterConfigService; + + @Inject + public V20230720161500_AddExtractorFragments(RuleFragmentService ruleFragmentService, ClusterConfigService clusterConfigService) { + this.ruleFragmentService = ruleFragmentService; + this.clusterConfigService = clusterConfigService; + } + + @Override + public ZonedDateTime createdAt() { + return ZonedDateTime.parse("2023-06-13T15:44:00Z"); + } + + @Override + public void upgrade() { + log.debug("Adding extractor fragments via migration"); + if (Objects.nonNull(clusterConfigService.get(MigrationCompleted.class))) { + log.debug("Migration already completed!"); +// return; + } + + ruleFragmentService.upsert(createCopyFieldExtractor()); + ruleFragmentService.upsert(createRegexExtractor()); + ruleFragmentService.upsert(createRegexReplacementExtractor()); + ruleFragmentService.upsert(createJsonExtractor()); + ruleFragmentService.upsert(createSplitIndexExtractor()); + ruleFragmentService.upsert(createLookupExtractor()); + + clusterConfigService.write(new MigrationCompleted()); + log.debug("extractor fragments were successfully added"); + } + + + static RuleFragment createCopyFieldExtractor() { + String resultvariable = "gl2_fragment_extractor_" + System.currentTimeMillis(); + return RuleFragment.builder() + .fragment(""" + let %resultvar% = $message.${field}; + set_field( + field: ${newField}, + value: %resultvar% + );""".replace("%resultvar%", resultvariable)) + .descriptor(FunctionDescriptor.builder() + .name("extract_field") + .params(ImmutableList.of( + string("field").description("Field to extract").build(), + string("newField").description("New field to copy value to").build() + )) + .returnType(Object.class) + .description("Copy field value to a new field") + .ruleBuilderEnabled() + .ruleBuilderName("Extract value to new field") + .ruleBuilderTitle("Extract field '${field}' and set to new field '${newField}'") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.EXTRACTORS) + .build()) + .fragmentOutputVariable(resultvariable) + .build(); + } + + + static RuleFragment createRegexExtractor() { + String resultvariable = "gl2_fragment_extractor_" + System.currentTimeMillis(); + return RuleFragment.builder() + .fragment(""" + let regex_pattern = ${pattern}; + let regex_results = regex(regex_pattern, to_string($message.${field})); + let %resultvar% = regex_results["0"]; + set_field(${newField}, %resultvar%);""" + .replace("%resultvar%", resultvariable)) + .descriptor(FunctionDescriptor.builder() + .name("extract_regex") + .params(ImmutableList.of( + string("field").description("Field to extract").build(), + string("pattern").description("The regular expression used for extraction. First matcher group is used.").build(), + string("newField").description("New field to copy value to").build() + )) + .returnType(String.class) + .description("Copy extracted regular expression of field value to a new field") + .ruleBuilderEnabled() + .ruleBuilderName("Extract regular expression to new field") + .ruleBuilderTitle("Extract regular expression '${pattern}' for field '${field}' and set to new field '${newField}'") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.EXTRACTORS) + .build()) + .fragmentOutputVariable(resultvariable) + .build(); + } + + static RuleFragment createRegexReplacementExtractor() { + String resultvariable = "gl2_fragment_extractor_" + System.currentTimeMillis(); + return RuleFragment.builder() + .fragment(""" + let regex_pattern = ${pattern}; + let %resultvar% = regex_replace( + pattern: regex_pattern, + value: to_string($message.${field}), + replacement: ${replacement}<#if replaceAll??>, + replace_all: ${replaceAll?c} + ); + set_field(${newField}, %resultvar%);""" + .replace("%resultvar%", resultvariable)) + .descriptor(FunctionDescriptor.builder() + .name("extract_regex_replace") + .params(ImmutableList.of( + string("field").description("Field to extract").build(), + string("pattern").description("The regular expression used for extraction.").build(), + string("replacement").description("The replacement used for the matching text. Please refer to the Matcher API documentation for the possible options.").build(), + bool("replaceAll").description("Replace all occurences of the pattern, or only the first occurence. (default: true)").build(), + string("newField").description("New field to copy value to").build() + )) + .returnType(String.class) + .description("Copy extracted regular expression of field value to a new field") + .ruleBuilderEnabled() + .ruleBuilderName("Extract regular expression to new field") + .ruleBuilderTitle("Extract regular expression '${pattern}' for field '${field}' and set to new field '${newField}'") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.EXTRACTORS) + .build()) + .fragmentOutputVariable(resultvariable) + .build(); + } + + static RuleFragment createJsonExtractor() { + String resultvariable = "gl2_fragment_extractor_" + System.currentTimeMillis(); + return RuleFragment.builder() + .fragment(""" + let %resultvar% = parse_json(to_string($message.${field})); + set_fields(to_map(%resultvar%));""" + .replace("%resultvar%", resultvariable)) + .descriptor(FunctionDescriptor.builder() + .name("extract_json") + .params(ImmutableList.of( + string("field").description("Field to extract").build() + )) + .returnType(String.class) + .description("Parse field as json and set to fields") + .ruleBuilderEnabled() + .ruleBuilderName("Extract json and set to fields") + .ruleBuilderTitle("Extract Json in field '${field}' and set to new fields") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.EXTRACTORS) + .build()) + .fragmentOutputVariable(resultvariable) + .build(); + } + + static RuleFragment createSplitIndexExtractor() { + String resultvariable = "gl2_fragment_extractor_" + System.currentTimeMillis(); + return RuleFragment.builder() + .fragment(""" + let %resultvar% = split(${character}, to_string($message.${field}))[${targetIndex}]; + set_field(${newField}, %resultvar%);""" + .replace("%resultvar%", resultvariable)) + .descriptor(FunctionDescriptor.builder() + .name("extract_split_index") + .params(ImmutableList.of( + string("field").description("Field to extract").build(), + string("character").description("What character to split on").build(), + integer("targetIndex").description("What part of the split string to use (0-based)").build(), + string("newField").description("New field to copy value to").build() + )) + .returnType(String.class) + .description("Split field into tokens by character and set one token to new field.") + .ruleBuilderEnabled() + .ruleBuilderName("Extract split & index") + .ruleBuilderTitle("Extract token number '${targetIndex}' from split field '${field}' and set to new field '${newField}'") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.EXTRACTORS) + .build()) + .fragmentOutputVariable(resultvariable) + .build(); + } + + static RuleFragment createLookupExtractor() { + String resultvariable = "gl2_fragment_extractor_" + System.currentTimeMillis(); + return RuleFragment.builder() + .fragment(""" + let %resultvar% = lookup_value(${lookupTable}, to_string($message.${field})); + set_field(${newField}, %resultvar%);""" + .replace("%resultvar%", resultvariable)) + .descriptor(FunctionDescriptor.builder() + .name("extract_lookup") + .params(ImmutableList.of( + string("field").description("Field to extract").build(), + string("lookupTable").description("Lookup table to use").build(), + string("newField").description("New field to copy value to").build() + )) + .returnType(String.class) + .description("Lookup value for key in field in lookup table and set it to new field.") + .ruleBuilderEnabled() + .ruleBuilderName("Extract lookup value") + .ruleBuilderTitle("Extract value for field '${field}', do lookup in '${lookupTable}' and set value to new field '${newField}'") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.EXTRACTORS) + .build()) + .fragmentOutputVariable(resultvariable) + .build(); + } + + public record MigrationCompleted() {} + +} diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20230724092100_AddFieldConditions.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20230724092100_AddFieldConditions.java new file mode 100644 index 0000000000000..1b626c989b3ef --- /dev/null +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20230724092100_AddFieldConditions.java @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2020 Graylog, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * . + */ +package org.graylog.plugins.pipelineprocessor.rulebuilder.db.migrations; + +import com.google.common.collect.ImmutableList; +import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderFunctionGroup; +import org.graylog.plugins.pipelineprocessor.rulebuilder.db.RuleFragment; +import org.graylog.plugins.pipelineprocessor.rulebuilder.db.RuleFragmentService; +import org.graylog2.migrations.Migration; +import org.graylog2.plugin.cluster.ClusterConfigService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import java.time.ZonedDateTime; +import java.util.Arrays; +import java.util.Objects; + +import static org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor.bool; +import static org.graylog.plugins.pipelineprocessor.ast.functions.ParameterDescriptor.string; + +public class V20230724092100_AddFieldConditions extends Migration { + + private static final Logger log = LoggerFactory.getLogger(V20230613154400_AddImplicitToStringFragments.class); + private final RuleFragmentService ruleFragmentService; + private final ClusterConfigService clusterConfigService; + + @Inject + public V20230724092100_AddFieldConditions(RuleFragmentService ruleFragmentService, ClusterConfigService clusterConfigService) { + this.ruleFragmentService = ruleFragmentService; + this.clusterConfigService = clusterConfigService; + } + + @Override + public ZonedDateTime createdAt() { + return ZonedDateTime.parse("2023-07-24T09:21:00Z"); + } + + @Override + public void upgrade() { + log.debug("Adding field condition fragments via migration"); + if (Objects.nonNull(clusterConfigService.get(MigrationCompleted.class))) { + log.debug("Migration already completed!"); +// return; + } + String[] noConversionTypes = {"collection", "ip", "list", "not_null", "null", "number", "period"}; + Arrays.stream(noConversionTypes).forEach(type -> ruleFragmentService.upsert(createCheckFieldTypeNoConversion(type))); + String[] conversionTypes = {"bool", "double", "long", "map", "string", "url"}; + Arrays.stream(conversionTypes).forEach(type -> ruleFragmentService.upsert(createCheckFieldType(type))); + ruleFragmentService.upsert(createCheckDateField()); + ruleFragmentService.upsert(createCIDRMatchField()); + ruleFragmentService.upsert(createStringContainsField()); + ruleFragmentService.upsert(createStringEndsWithField()); + ruleFragmentService.upsert(createStringStartsWithField()); + ruleFragmentService.upsert(createGrokMatchesField()); + + clusterConfigService.write(new MigrationCompleted()); + log.debug("field condition fragments were successfully added"); + } + + RuleFragment createCheckFieldTypeNoConversion(String type) { + return RuleFragment.builder() + .fragment("is_%type%($message.${field})" + .replace("%type%", type) + ) + .descriptor(FunctionDescriptor.builder() + .name("field_" + type) + .params(ImmutableList.of( + string("field").description("Field to check").build() + )) + .returnType(Void.class) + .description("Checks whether the value in the given field is a " + type) + .ruleBuilderEnabled() + .ruleBuilderName("Field is " + type) + .ruleBuilderTitle("Check if value in '${field}' is a " + type) + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.BOOLEAN) + .build()) + .isCondition() + .build(); + } + + RuleFragment createCheckFieldType(String type) { + return RuleFragment.builder() + .fragment("<#if attemptConversion!false>is_%type%(to_%type%($message.${field}))<#else>is_%type%($message.${field})" + .replace("%type%", type) + ) + .descriptor(FunctionDescriptor.builder() + .name("field_" + type) + .params(ImmutableList.of( + string("field").description("Field to check").build(), + bool("attemptConversion").optional().description("If set the check will also try if the field could be converted to a " + type + " using the to_" + type + " method").build() + )) + .returnType(Void.class) + .description("Checks whether the value in the given field is a " + type) + .ruleBuilderEnabled() + .ruleBuilderName("Field is " + type) + .ruleBuilderTitle("Check if value in '${field}' is a " + type) + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.BOOLEAN) + .build()) + .isCondition() + .build(); + } + + RuleFragment createCheckDateField() { + return RuleFragment.builder() + .fragment(""" + is_date( + <#if pattern??> + value: parse_date(to_string($message.${field}), ${pattern}) + <#else> + value: $message.${field} + + )""") + .descriptor(FunctionDescriptor.builder() + .name("field_date") + .params(ImmutableList.of( + string("field").description("Field to check").build(), + string("pattern").optional().description("Date pattern (see parse_date)").build() + )) + .returnType(Void.class) + .description("Checks whether the value in the given field is a date, optionally by first trying to parse it.") + .ruleBuilderEnabled() + .ruleBuilderName("Field is date") + .ruleBuilderTitle("Check if value in '${field}' is a date") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.BOOLEAN) + .build()) + .isCondition() + .build(); + } + + RuleFragment createCIDRMatchField() { + return RuleFragment.builder() + .fragment(""" + ( + is_ip(to_ip($message.${field})) && + cidr_match( + ip: to_ip($message.${field}), + cidr: ${cidr} + ) + )""") + .descriptor(FunctionDescriptor.builder() + .name("field_cidr") + .params(ImmutableList.of( + string("field").description("Field to check").build(), + string("cidr").description("Date pattern (see parse_date)").build() + )) + .returnType(Void.class) + .description("Checks whether the value in the given field is an IP and matches the given cidr subnet mask.") + .ruleBuilderEnabled() + .ruleBuilderName("Field matches CIDR") + .ruleBuilderTitle("Check if value in '${field}' is an IP and matches '${cidr}' subnet mask") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.BOOLEAN) + .build()) + .isCondition() + .build(); + } + + RuleFragment createStringContainsField() { + return RuleFragment.builder() + .fragment(""" + contains( + value: to_string($message.${field}), + search: ${search}<#if ignoreCase??>, + ignore_case: ${ignoreCase?c} + )""") + .descriptor(FunctionDescriptor.builder() + .name("field_contains") + .params(ImmutableList.of( + string("field").description("Field to check").build(), + string("search").description("The substring to find").build(), + bool("ignoreCase").optional().description("Whether to search case insensitive, defaults to false").build() + )) + .returnType(Void.class) + .description("Checks if the field value's string representation contains the given substring.") + .ruleBuilderEnabled() + .ruleBuilderName("Field contains") + .ruleBuilderTitle("Check if string value in '${field}' contains '${search}' <#if ignoreCase??>(ignore case: ${ignoreCase?c})") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.BOOLEAN) + .build()) + .isCondition() + .build(); + } + + RuleFragment createStringStartsWithField() { + return RuleFragment.builder() + .fragment(""" + starts_with( + value: to_string($message.${field}), + prefix: ${search}<#if ignoreCase??>, + ignore_case: ${ignoreCase?c} + )""") + .descriptor(FunctionDescriptor.builder() + .name("field_starts_with") + .params(ImmutableList.of( + string("field").description("Field to check").build(), + string("search").description("The substring to find").build(), + bool("ignoreCase").optional().description("Whether to search case insensitive, defaults to false").build() + )) + .returnType(Void.class) + .description("Checks if the field value's string representation starts with the given substring.") + .ruleBuilderEnabled() + .ruleBuilderName("Field starts with") + .ruleBuilderTitle("Check if string value in '${field}' starts with '${search}' <#if ignoreCase??>(ignore case: ${ignoreCase?c})") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.BOOLEAN) + .build()) + .isCondition() + .build(); + } + + RuleFragment createStringEndsWithField() { + return RuleFragment.builder() + .fragment(""" + ends_with( + value: to_string($message.${field}), + suffix: ${search}<#if ignoreCase??>, + ignore_case: ${ignoreCase?c} + )""") + .descriptor(FunctionDescriptor.builder() + .name("field_ends_with") + .params(ImmutableList.of( + string("field").description("Field to check").build(), + string("search").description("The substring to find").build(), + bool("ignoreCase").optional().description("Whether to search case insensitive, defaults to false").build() + )) + .returnType(Void.class) + .description("Checks if the field value's string representation ends with the given substring.") + .ruleBuilderEnabled() + .ruleBuilderName("Field ends with") + .ruleBuilderTitle("Check if string value in '${field}' ends with '${search}' <#if ignoreCase??>(ignore case: ${ignoreCase?c})") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.BOOLEAN) + .build()) + .isCondition() + .build(); + } + + RuleFragment createGrokMatchesField() { + return RuleFragment.builder() + .fragment(""" + grok( + value: to_string($message.${field}), + pattern: ${pattern} + ).matches == true""") + .descriptor(FunctionDescriptor.builder() + .name("grok_matches") + .params(ImmutableList.of( + string("field").description("Field to check").build(), + string("pattern").description("Grok pattern to check string against").build() + )) + .returnType(Void.class) + .description("Checks if the field value's string representation matches the given grok pattern.") + .ruleBuilderEnabled() + .ruleBuilderName("Field matches grok") + .ruleBuilderTitle("Check if string value in '${field}' matches grok pattern '${pattern}'") + .ruleBuilderFunctionGroup(RuleBuilderFunctionGroup.BOOLEAN) + .build()) + .isCondition() + .build(); + } + + public record MigrationCompleted() {} + +} diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/parser/ConditionParser.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/parser/ConditionParser.java index e5870dd52ebbf..c4ccaa779532e 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/parser/ConditionParser.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/parser/ConditionParser.java @@ -17,6 +17,7 @@ package org.graylog.plugins.pipelineprocessor.rulebuilder.parser; import freemarker.template.Configuration; +import org.apache.commons.lang3.StringUtils; import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderRegistry; import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderStep; @@ -28,12 +29,14 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @Singleton public class ConditionParser { public static final String NL = System.lineSeparator(); + private static final String INDENT = " "; protected final Map conditions; private final Configuration freemarkerConfiguration; @@ -44,15 +47,43 @@ public ConditionParser(RuleBuilderRegistry ruleBuilderRegistry, SecureFreemarker freemarkerConfiguration = ParserUtil.initializeFragmentTemplates(secureFreemarkerConfigProvider, conditions); } - public String generate(List ruleConditions) { - return " true" + NL + - ruleConditions.stream() - .map(step -> generateCondition(step)) - .collect(Collectors.joining(NL)); + public Map getConditions() { + return conditions; } - String generateCondition(RuleBuilderStep step) { - String syntax = " && "; + public String generate(List ruleConditions, RuleBuilderStep.Operator operator, int level) { + if (ruleConditions.isEmpty()) { + if (level == 1) { + return " true"; + } + return ""; + } + if (operator == null) { + operator = RuleBuilderStep.Operator.AND; + } + StringBuilder syntax = new StringBuilder(); + if (level != 1) { + syntax.append(StringUtils.repeat(INDENT, level)).append("(").append(NL); + } + syntax.append(generateCondition(ruleConditions.get(0), level)); + for (int i = 1; i < ruleConditions.size(); i++) { + syntax.append(NL).append(StringUtils.repeat(INDENT, level + 1)).append(operator).append(NL); + final RuleBuilderStep step = ruleConditions.get(i); + if (step.conditions() == null) { + syntax.append(generateCondition(step, level)); + } else { + syntax.append(generate(step.conditions(), step.operator(), level + 1)); + } + } + if (level != 1) { + syntax.append(NL).append(StringUtils.repeat(INDENT, level)).append(")"); + } + return syntax.toString(); + + } + + String generateCondition(RuleBuilderStep step, int level) { + String syntax = StringUtils.repeat(INDENT, level); if (step.negate()) { syntax += "! "; } @@ -66,9 +97,21 @@ String generateCondition(RuleBuilderStep step) { if (ruleFragment.isFragment()) { syntax += ParserUtil.generateForFragment(step, freemarkerConfiguration); } else { - syntax += ParserUtil.generateForFunction(step, function); + syntax += ParserUtil.generateForFunction(step, function, level); } return syntax; } + public String generateConditionVariables(List conditions) { + AtomicInteger index = new AtomicInteger(); + return conditions.stream() + .map(condition -> generateConditionVariable(index.incrementAndGet(), condition)) + .collect(Collectors.joining(NL)); + } + + private String generateConditionVariable(int index, RuleBuilderStep step) { + String condition = generateCondition(step, 0); + String fieldname = (step.outputvariable() == null) ? Integer.toString(index) : step.outputvariable(); + return "set_field(\"gl2_simulator_condition_" + fieldname + "\", " + condition + ");"; + } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/parser/ParserUtil.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/parser/ParserUtil.java index 229faaeaed9cf..08ba8fbb24e4b 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/parser/ParserUtil.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/parser/ParserUtil.java @@ -39,6 +39,10 @@ public class ParserUtil { static final String generateForFunction(RuleBuilderStep step, FunctionDescriptor function) { + return generateForFunction(step, function, 1); + } + + static final String generateForFunction(RuleBuilderStep step, FunctionDescriptor function, int level) { String syntax = function.name() + "("; String params = function.params().stream() .map(p -> addFunctionParameter(p, step)) @@ -47,7 +51,7 @@ static final String generateForFunction(RuleBuilderStep step, FunctionDescriptor if (StringUtils.isEmpty(params)) { return syntax + ")"; } else { - return syntax + ConditionParser.NL + params + ConditionParser.NL + " )"; + return syntax + ConditionParser.NL + params + ConditionParser.NL + StringUtils.repeat(" ", level) + ")"; } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/parser/RuleBuilderService.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/parser/RuleBuilderService.java index 940738e3b7b3c..1c2dffb21084a 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/parser/RuleBuilderService.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/parser/RuleBuilderService.java @@ -27,6 +27,7 @@ import javax.inject.Inject; import javax.inject.Singleton; import java.io.StringWriter; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; @@ -59,19 +60,27 @@ public RuleBuilderService(ConditionParser conditionParser, ActionParser actionPa this.actionParser = actionParser; this.freemarkerConfiguration = secureFreemarkerConfigProvider.get(); StringTemplateLoader templateLoader = new StringTemplateLoader(); - actionParser.getActions().entrySet().stream().forEach(f -> templateLoader.putTemplate(f.getKey(), f.getValue().descriptor().ruleBuilderTitle())); + conditionParser.getConditions().forEach((key, value) -> templateLoader.putTemplate(key, value.descriptor().ruleBuilderTitle())); + actionParser.getActions().forEach((key, value) -> templateLoader.putTemplate(key, value.descriptor().ruleBuilderTitle())); freemarkerConfiguration.setTemplateLoader(templateLoader); } public String generateRuleSource(String title, RuleBuilder ruleBuilder, boolean generateSimulatorFields) { - //TODO: possible injection here, sanitize input! final String rule = String.format(Locale.ROOT, RULE_TEMPLATE, title, - conditionParser.generate(ruleBuilder.conditions()), + conditionParser.generate(ruleBuilder.conditions(), ruleBuilder.operator(), 1), actionParser.generate(ruleBuilder.actions(), generateSimulatorFields)); log.debug(rule); return rule; } + public String generateSimulatorRuleSourceEvaluatingConditions(RuleBuilder ruleBuilder) { + final String rule = String.format(Locale.ROOT, RULE_TEMPLATE, "condition_evaluation", + conditionParser.generate(new ArrayList<>(), RuleBuilderStep.Operator.AND, 1), + conditionParser.generateConditionVariables(ruleBuilder.conditions())); + log.debug(rule); + return rule; + } + public RuleBuilder generateTitles(RuleBuilder ruleBuilder) { return ruleBuilder.toBuilder() .conditions(generateStepTitles(ruleBuilder.conditions())) diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/rest/RuleBuilderResource.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/rest/RuleBuilderResource.java index 446c9d037dd71..9be4262ce6c53 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/rest/RuleBuilderResource.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/rest/RuleBuilderResource.java @@ -98,7 +98,7 @@ public RuleBuilderDto createFromBuilder(@ApiParam(name = "rule", required = true throw new BadRequestException(exception.getMessage()); } - RuleSource ruleSource = toRuleSource(ruleBuilderDto); + RuleSource ruleSource = toRuleSource(ruleBuilderDto, false); final RuleSource stored = ruleResource.createFromParser(ruleSource); return ruleBuilderDto.toBuilder() .id(stored.id()) @@ -113,7 +113,7 @@ public RuleBuilderDto createFromBuilder(@ApiParam(name = "rule", required = true @AuditEvent(type = PipelineProcessorAuditEventTypes.RULE_CREATE) public RuleBuilderDto updateFromBuilder(@ApiParam(name = "id") @PathParam("id") String id, @ApiParam(name = "rule", required = true) @NotNull RuleBuilderDto ruleBuilderDto) throws NotFoundException { - RuleSource ruleSource = toRuleSource(ruleBuilderDto); + RuleSource ruleSource = toRuleSource(ruleBuilderDto, false); final RuleSource stored = ruleResource.update(id, ruleSource); return ruleBuilderDto.toBuilder() .id(stored.id()) @@ -128,7 +128,7 @@ public Collection actions() { return ruleBuilderRegistry.actions() .values().stream() .map(RuleFragment::descriptor) - .sorted(Comparator.comparing(FunctionDescriptor::name)) + .sorted(Comparator.comparing(FunctionDescriptor::ruleBuilderName)) .collect(Collectors.toList()); } @@ -139,7 +139,7 @@ public Collection conditions() { return ruleBuilderRegistry.conditions() .values().stream() .map(RuleFragment::descriptor) - .sorted(Comparator.comparing(FunctionDescriptor::name)) + .sorted(Comparator.comparing(FunctionDescriptor::ruleBuilderName)) .collect(Collectors.toList()); } @@ -158,20 +158,29 @@ public RuleBuilderDto validate(@ApiParam(name = "rule", required = true) @NotNul @Path("/simulate") @POST @NoAuditEvent("Only used to simulate a rule builder") - public Message simulate(@ApiParam(name = "rule", required = true) @NotNull SimulateRuleBuilderRequest simulateRuleBuilderRequest) { - RuleSource ruleSource = toRuleSource(simulateRuleBuilderRequest.ruleBuilderDto()); - final Rule rule = pipelineRuleService.parseRuleOrThrow(ruleSource.id(), ruleSource.source(), true); + public RuleBuilderSimulatorResponse simulate(@ApiParam(name = "rule", required = true) @NotNull SimulateRuleBuilderRequest simulateRuleBuilderRequest) { Message message = ruleSimulator.createMessage(simulateRuleBuilderRequest.message()); - return ruleSimulator.simulate(rule, message); + RuleSource ruleSourceConditions = RuleSource.builder() + .source(ruleBuilderParser.generateSimulatorRuleSourceEvaluatingConditions(simulateRuleBuilderRequest.ruleBuilderDto().ruleBuilder())) + .build(); + final Rule rule1 = pipelineRuleService.parseRuleOrThrow(ruleSourceConditions.id(), ruleSourceConditions.source(), true); + final Message conditionResult = ruleSimulator.simulate(rule1, message); + + RuleSource ruleSource = toRuleSource(simulateRuleBuilderRequest.ruleBuilderDto(), true); + final Rule rule2 = pipelineRuleService.parseRuleOrThrow(ruleSource.id(), ruleSource.source(), true); + + final Message result = ruleSimulator.simulate(rule2, conditionResult); + + return new RuleBuilderSimulatorResponse(result); } - private RuleSource toRuleSource(RuleBuilderDto ruleBuilderDto) { + private RuleSource toRuleSource(RuleBuilderDto ruleBuilderDto, boolean generateSimulatorFields) { return RuleSource.builder() .title(ruleBuilderDto.title()) .description(ruleBuilderDto.description()) .ruleBuilder(ruleBuilderDto.ruleBuilder()) - .source(ruleBuilderParser.generateRuleSource(ruleBuilderDto.title(), ruleBuilderDto.ruleBuilder(), false)) + .source(ruleBuilderParser.generateRuleSource(ruleBuilderDto.title(), ruleBuilderDto.ruleBuilder(), generateSimulatorFields)) .build(); } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/rest/RuleBuilderSimulatorResponse.java b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/rest/RuleBuilderSimulatorResponse.java new file mode 100644 index 0000000000000..b9949e96030ff --- /dev/null +++ b/graylog2-server/src/main/java/org/graylog/plugins/pipelineprocessor/rulebuilder/rest/RuleBuilderSimulatorResponse.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2020 Graylog, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * . + */ +package org.graylog.plugins.pipelineprocessor.rulebuilder.rest; + +import org.graylog2.plugin.Message; + +import java.util.HashMap; +import java.util.Map; + +public class RuleBuilderSimulatorResponse extends Message { + + private final static String VAR_CONDITION_PREFIX = "gl2_simulator_condition_"; + private final static String VAR_ACTION_PREFIX = "gl2_simulator_output_"; + + private Map simulatorActionVariables; + private Map simulatorConditionVariables; + + public RuleBuilderSimulatorResponse(Message simulatorResult) { + super(simulatorResult.getFields()); + this.simulatorConditionVariables = new HashMap<>(); + this.simulatorActionVariables = new HashMap<>(); + simulatorResult.getFields().entrySet().stream() + .filter(e -> e.getKey().startsWith(VAR_CONDITION_PREFIX)) + .forEach(e -> { + this.simulatorConditionVariables.put(e.getKey().substring(VAR_CONDITION_PREFIX.length()), e.getValue()); + this.removeField(e.getKey()); + }); + simulatorResult.getFields().entrySet().stream() + .filter(e -> e.getKey().startsWith(VAR_ACTION_PREFIX)) + .forEach(e -> { + this.simulatorActionVariables.put(e.getKey().substring(VAR_ACTION_PREFIX.length()), e.getValue()); + this.removeField(e.getKey()); + }); + } + + public Map getSimulatorActionVariables() { + return simulatorActionVariables; + } + + public Map getSimulatorConditionVariables() { + return simulatorConditionVariables; + } +} diff --git a/graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/parser/PipelineRuleParserTest.java b/graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/parser/PipelineRuleParserTest.java index dfab6fd4a8971..46adcbd4f62c7 100644 --- a/graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/parser/PipelineRuleParserTest.java +++ b/graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/parser/PipelineRuleParserTest.java @@ -58,6 +58,7 @@ import org.graylog.plugins.pipelineprocessor.parser.errors.WrongNumberOfArgs; import org.graylog2.plugin.InstantMillisProvider; import org.graylog2.plugin.Message; +import org.jetbrains.annotations.NotNull; import org.joda.time.DateTime; import org.joda.time.DateTimeUtils; import org.joda.time.DateTimeZone; @@ -701,5 +702,17 @@ protected String getName() { protected ImmutableList params() { return ImmutableList.of(); } + + @NotNull + @Override + protected String getRuleBuilderName() { + return null; + } + + @NotNull + @Override + protected String getRuleBuilderTitle() { + return null; + } } } diff --git a/graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20220522125200_AddSetGrokToFieldsExtractorFragmentsTest.java b/graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20220522125200_AddSetGrokToFieldsExtractorFragmentsTest.java new file mode 100644 index 0000000000000..e4c1a606ada3d --- /dev/null +++ b/graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20220522125200_AddSetGrokToFieldsExtractorFragmentsTest.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2020 Graylog, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * . + */ +package org.graylog.plugins.pipelineprocessor.rulebuilder.db.migrations; + +import com.google.common.collect.Sets; +import com.google.common.eventbus.EventBus; +import org.graylog.plugins.pipelineprocessor.ast.Rule; +import org.graylog.plugins.pipelineprocessor.ast.functions.Function; +import org.graylog.plugins.pipelineprocessor.functions.conversion.StringConversion; +import org.graylog.plugins.pipelineprocessor.functions.messages.SetFields; +import org.graylog.plugins.pipelineprocessor.functions.strings.GrokMatch; +import org.graylog.plugins.pipelineprocessor.parser.FunctionRegistry; +import org.graylog.plugins.pipelineprocessor.rulebuilder.db.RuleFragment; +import org.graylog.plugins.pipelineprocessor.rulebuilder.parser.BaseFragmentTest; +import org.graylog2.grok.GrokPattern; +import org.graylog2.grok.GrokPatternRegistry; +import org.graylog2.grok.GrokPatternService; +import org.graylog2.plugin.Message; +import org.graylog2.plugin.Tools; +import org.graylog2.shared.SuppressForbidden; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Executors; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class V20220522125200_AddSetGrokToFieldsExtractorFragmentsTest extends BaseFragmentTest { + + V20220522125200_AddSetGrokToFieldsExtractorFragments migration; + + @BeforeClass + @SuppressForbidden("Allow using default thread factory") + public static void initialize() { + final Map> functions = commonFunctions(); + final GrokPatternService grokPatternService = mock(GrokPatternService.class); + Set patterns = Sets.newHashSet( + GrokPattern.create("BASE10NUM", "(?[+-]?(?:(?:[0-9]+(?:\\.[0-9]+)?)|(?:\\.[0-9]+)))") + ); + when(grokPatternService.loadAll()).thenReturn(patterns); + final EventBus clusterBus = new EventBus(); + final GrokPatternRegistry grokPatternRegistry = new GrokPatternRegistry(clusterBus, + grokPatternService, + Executors.newScheduledThreadPool(1)); + functions.put(GrokMatch.NAME, new GrokMatch(grokPatternRegistry)); + functions.put(SetFields.NAME, new SetFields()); + functions.put(StringConversion.NAME, new StringConversion()); + functionRegistry = new FunctionRegistry(functions); + } + + + @Test + public void testGrokExtract() { + final RuleFragment fragment = V20220522125200_AddSetGrokToFieldsExtractorFragments.createSetGrokToFieldsFragment(); + + Rule testRule = createFragmentSource(fragment, Map.of( + "field", "message", + "grokPattern", "^%{BASE10NUM:number}\\\\s+")); + Message result = evaluateRule(testRule, new Message("99 Problems", "test", Tools.nowUTC())); + assertThat(result.getField("number")).isEqualTo("99"); + + testRule = createFragmentSource(fragment, Map.of( + "field", "message", + "grokPattern", "^%{BASE10NUM}\\\\s+", + "grokNamedOnly", true)); + final Message inMessage = new Message("99 Problems", "test", Tools.nowUTC()); + result = evaluateRule(testRule, inMessage); + assertThat(result.getFieldCount()).isEqualTo(inMessage.getFieldCount()); + } + + +} diff --git a/graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20230720161500_AddExtractorFragmentsTest.java b/graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20230720161500_AddExtractorFragmentsTest.java new file mode 100644 index 0000000000000..ebb4ecf654ec3 --- /dev/null +++ b/graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20230720161500_AddExtractorFragmentsTest.java @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2020 Graylog, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * . + */ +package org.graylog.plugins.pipelineprocessor.rulebuilder.db.migrations; + +import org.graylog.plugins.pipelineprocessor.ast.Rule; +import org.graylog.plugins.pipelineprocessor.ast.functions.Function; +import org.graylog.plugins.pipelineprocessor.functions.conversion.StringConversion; +import org.graylog.plugins.pipelineprocessor.functions.lookup.LookupValue; +import org.graylog.plugins.pipelineprocessor.functions.messages.GetField; +import org.graylog.plugins.pipelineprocessor.functions.messages.SetField; +import org.graylog.plugins.pipelineprocessor.functions.strings.RegexMatch; +import org.graylog.plugins.pipelineprocessor.functions.strings.RegexReplace; +import org.graylog.plugins.pipelineprocessor.functions.strings.Split; +import org.graylog.plugins.pipelineprocessor.parser.FunctionRegistry; +import org.graylog.plugins.pipelineprocessor.rulebuilder.db.RuleFragment; +import org.graylog.plugins.pipelineprocessor.rulebuilder.parser.BaseFragmentTest; +import org.graylog2.lookup.LookupTable; +import org.graylog2.lookup.LookupTableService; +import org.graylog2.plugin.Message; +import org.graylog2.plugin.Tools; +import org.graylog2.plugin.lookup.LookupResult; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.RETURNS_DEEP_STUBS; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class V20230720161500_AddExtractorFragmentsTest extends BaseFragmentTest { + + V20230720161500_AddExtractorFragments migration; + + private static LookupTableService lookupTableService; + private static LookupTableService.Function lookupServiceFunction; + private static LookupTable lookupTable; + + @BeforeClass + public static void initialize() { + final Map> functions = commonFunctions(); + functions.put(GetField.NAME, new GetField()); + functions.put(SetField.NAME, new SetField()); + functions.put(StringConversion.NAME, new StringConversion()); + functions.put(RegexMatch.NAME, new RegexMatch()); + functions.put(RegexReplace.NAME, new RegexReplace()); + functions.put(Split.NAME, new Split()); + + lookupTable = mock(LookupTable.class); + when(lookupTable.lookup("ExistingKey")).thenReturn(LookupResult.builder() + .single("ThisKeysValue") + .cacheTTL(1000) + .build()); + lookupTableService = mock(LookupTableService.class, RETURNS_DEEP_STUBS); + when(lookupTableService.getTable(anyString())).thenReturn(lookupTable); + lookupServiceFunction = new LookupTableService.Function(lookupTableService, "lookup-table"); + when(lookupTableService.newBuilder().lookupTable(anyString()).build()).thenReturn(lookupServiceFunction); + functions.put(LookupValue.NAME, new LookupValue(lookupTableService)); + + functionRegistry = new FunctionRegistry(functions); + } + + + @Test + public void testCopyField() { + RuleFragment fragment = V20230720161500_AddExtractorFragments.createCopyFieldExtractor(); + Map params = Map.of("field", "message", "newField", "copyfield"); + Rule rule = super.createFragmentSource(fragment, params); + final Message message = evaluateRule(rule, new Message("Dummy Message", "test", Tools.nowUTC())); + assertThat(message.getField("copyfield")).isEqualTo("Dummy Message"); + } + + @Test + public void testRegex() { + RuleFragment fragment = V20230720161500_AddExtractorFragments.createRegexExtractor(); + Map params = Map.of("field", "message", "pattern", "^.*(doo...).*$", "newField", "copyfield"); + Rule rule = super.createFragmentSource(fragment, params); + final Message message = evaluateRule(rule, new Message("bippitysnickerdoodledoobadoo", "test", Tools.nowUTC())); + assertThat(message.getField("copyfield")).isEqualTo("doobad"); + } + + @Test + public void testRegexReplacement() { + RuleFragment fragment = V20230720161500_AddExtractorFragments.createRegexReplacementExtractor(); + Map params = Map.of( + "field", "message", + "pattern", "dog", + "replacement", "cat", + "newField", "copyfield" + ); + Rule rule = super.createFragmentSource(fragment, params); + Message message = evaluateRule(rule, new Message("zzzdogzzzdogzzz", "test", Tools.nowUTC())); + assertThat(message.getField("copyfield")).isEqualTo("zzzcatzzzcatzzz"); + params = Map.of( + "field", "message", + "pattern", "dog", + "replacement", "cat", + "newField", "copyfield2", + "replaceAll", false + ); + rule = super.createFragmentSource(fragment, params); + message = evaluateRule(rule, new Message("zzzdogzzzdogzzz", "test", Tools.nowUTC())); + assertThat(message.getField("copyfield2")).isEqualTo("zzzcatzzzdogzzz"); + + } + + @Test + public void testSplitIndex() { + RuleFragment fragment = V20230720161500_AddExtractorFragments.createSplitIndexExtractor(); + Map params = Map.of( + "field", "message", + "character", ",", + "targetIndex", 1, + "newField", "copyfield" + ); + Rule rule = super.createFragmentSource(fragment, params); + Message message = evaluateRule(rule, new Message("cat,dog,mouse", "test", Tools.nowUTC())); + assertThat(message.getField("copyfield")).isEqualTo("dog"); + } + + @Test + public void testLookup() { + RuleFragment fragment = V20230720161500_AddExtractorFragments.createLookupExtractor(); + Map params = Map.of( + "field", "message", + "lookupTable", "lookup-table", + "newField", "copyfield" + ); + Rule rule = super.createFragmentSource(fragment, params); + Message message = evaluateRule(rule, new Message("ExistingKey", "test", Tools.nowUTC())); + assertThat(message.getField("copyfield")).isEqualTo("ThisKeysValue"); + + message = evaluateRule(rule, new Message("NoKey", "test", Tools.nowUTC())); + assertThat(message.getField("copyfield")).isNull(); + } + + +} diff --git a/graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20230724092100_AddFieldConditionsTest.java b/graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20230724092100_AddFieldConditionsTest.java new file mode 100644 index 0000000000000..bd11b7287311b --- /dev/null +++ b/graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/rulebuilder/db/migrations/V20230724092100_AddFieldConditionsTest.java @@ -0,0 +1,448 @@ +/* + * Copyright (C) 2020 Graylog, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * . + */ +package org.graylog.plugins.pipelineprocessor.rulebuilder.db.migrations; + +import com.google.common.collect.Sets; +import com.google.common.eventbus.EventBus; +import org.graylog.plugins.pipelineprocessor.ast.Rule; +import org.graylog.plugins.pipelineprocessor.ast.functions.Function; +import org.graylog.plugins.pipelineprocessor.functions.IsNotNull; +import org.graylog.plugins.pipelineprocessor.functions.IsNull; +import org.graylog.plugins.pipelineprocessor.functions.conversion.BooleanConversion; +import org.graylog.plugins.pipelineprocessor.functions.conversion.DoubleConversion; +import org.graylog.plugins.pipelineprocessor.functions.conversion.IsBoolean; +import org.graylog.plugins.pipelineprocessor.functions.conversion.IsCollection; +import org.graylog.plugins.pipelineprocessor.functions.conversion.IsDouble; +import org.graylog.plugins.pipelineprocessor.functions.conversion.IsList; +import org.graylog.plugins.pipelineprocessor.functions.conversion.IsLong; +import org.graylog.plugins.pipelineprocessor.functions.conversion.IsMap; +import org.graylog.plugins.pipelineprocessor.functions.conversion.IsNumber; +import org.graylog.plugins.pipelineprocessor.functions.conversion.IsString; +import org.graylog.plugins.pipelineprocessor.functions.conversion.LongConversion; +import org.graylog.plugins.pipelineprocessor.functions.conversion.MapConversion; +import org.graylog.plugins.pipelineprocessor.functions.conversion.StringConversion; +import org.graylog.plugins.pipelineprocessor.functions.dates.IsDate; +import org.graylog.plugins.pipelineprocessor.functions.dates.ParseDate; +import org.graylog.plugins.pipelineprocessor.functions.dates.periods.IsPeriod; +import org.graylog.plugins.pipelineprocessor.functions.ips.CidrMatch; +import org.graylog.plugins.pipelineprocessor.functions.ips.IpAddress; +import org.graylog.plugins.pipelineprocessor.functions.ips.IpAddressConversion; +import org.graylog.plugins.pipelineprocessor.functions.ips.IsIp; +import org.graylog.plugins.pipelineprocessor.functions.messages.SetField; +import org.graylog.plugins.pipelineprocessor.functions.strings.Contains; +import org.graylog.plugins.pipelineprocessor.functions.strings.EndsWith; +import org.graylog.plugins.pipelineprocessor.functions.strings.GrokMatch; +import org.graylog.plugins.pipelineprocessor.functions.strings.StartsWith; +import org.graylog.plugins.pipelineprocessor.functions.urls.IsUrl; +import org.graylog.plugins.pipelineprocessor.functions.urls.URL; +import org.graylog.plugins.pipelineprocessor.functions.urls.UrlConversion; +import org.graylog.plugins.pipelineprocessor.parser.FunctionRegistry; +import org.graylog.plugins.pipelineprocessor.rulebuilder.db.RuleFragment; +import org.graylog.plugins.pipelineprocessor.rulebuilder.db.RuleFragmentService; +import org.graylog.plugins.pipelineprocessor.rulebuilder.parser.BaseFragmentTest; +import org.graylog2.grok.GrokPattern; +import org.graylog2.grok.GrokPatternRegistry; +import org.graylog2.grok.GrokPatternService; +import org.graylog2.plugin.Message; +import org.graylog2.plugin.Tools; +import org.graylog2.plugin.cluster.ClusterConfigService; +import org.graylog2.shared.SuppressForbidden; +import org.joda.time.Period; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.net.InetAddress; +import java.net.MalformedURLException; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Executors; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.Silent.class) +public class V20230724092100_AddFieldConditionsTest extends BaseFragmentTest { + + V20230724092100_AddFieldConditions migration; + @Mock + private RuleFragmentService ruleFragmentService; + @Mock + private ClusterConfigService clusterConfigService; + + @BeforeClass + @SuppressForbidden("Allow using default thread factory") + public static void initialize() { + final Map> functions = commonFunctions(); + functions.put(SetField.NAME, new SetField()); + functions.put(IsBoolean.NAME, new IsBoolean()); + functions.put(BooleanConversion.NAME, new BooleanConversion()); + functions.put(IsCollection.NAME, new IsCollection()); + functions.put(IsDouble.NAME, new IsDouble()); + functions.put(IsDate.NAME, new IsDate()); + functions.put(DoubleConversion.NAME, new DoubleConversion()); + functions.put(ParseDate.NAME, new ParseDate()); + functions.put(IsList.NAME, new IsList()); + functions.put(IsIp.NAME, new IsIp()); + functions.put(IpAddressConversion.NAME, new IpAddressConversion()); + functions.put(IsLong.NAME, new IsLong()); + functions.put(LongConversion.NAME, new LongConversion()); + functions.put(IsNotNull.NAME, new IsNotNull()); + functions.put(IsNull.NAME, new IsNull()); + functions.put(IsMap.NAME, new IsMap()); + functions.put(MapConversion.NAME, new MapConversion()); + functions.put(IsNumber.NAME, new IsNumber()); + functions.put(IsPeriod.NAME, new IsPeriod()); + functions.put(IsString.NAME, new IsString()); + functions.put(StringConversion.NAME, new StringConversion()); + functions.put(IsUrl.NAME, new IsUrl()); + functions.put(UrlConversion.NAME, new UrlConversion()); + functions.put(CidrMatch.NAME, new CidrMatch()); + functions.put(Contains.NAME, new Contains()); + functions.put(StartsWith.NAME, new StartsWith()); + functions.put(EndsWith.NAME, new EndsWith()); + + final GrokPatternService grokPatternService = mock(GrokPatternService.class); + Set patterns = Sets.newHashSet( + GrokPattern.create("BASE10NUM", "(?[+-]?(?:(?:[0-9]+(?:\\.[0-9]+)?)|(?:\\.[0-9]+)))") + ); + when(grokPatternService.loadAll()).thenReturn(patterns); + final EventBus clusterBus = new EventBus(); + final GrokPatternRegistry grokPatternRegistry = new GrokPatternRegistry(clusterBus, + grokPatternService, + Executors.newScheduledThreadPool(1)); + functions.put(GrokMatch.NAME, new GrokMatch(grokPatternRegistry)); + functionRegistry = new FunctionRegistry(functions); + } + + @Before + public void initializeMigration() { + migration = new V20230724092100_AddFieldConditions(ruleFragmentService, clusterConfigService); + } + + @Test + public void testFieldBoolean() { + Message message = new Message("Dummy Message", "test", Tools.nowUTC()); + message.addField("bool", true); + final RuleFragment fragment = migration.createCheckFieldType("bool"); + Rule testRule = createFragmentSource(fragment, Map.of("field", "bool")); + evaluateCondition(testRule, message, true); + + message.addField("boolfalse", false); + testRule = createFragmentSource(fragment, Map.of("field", "boolfalse")); + evaluateCondition(testRule, message, true); + + message.addField("boolstring", "true"); + testRule = createFragmentSource(fragment, Map.of("field", "boolstring")); + evaluateCondition(testRule, message, false); + testRule = createFragmentSource(fragment, Map.of("field", "boolstring", "attemptConversion", true)); + evaluateCondition(testRule, message, true); + + message.addField("nobool", "fase"); + testRule = createFragmentSource(fragment, Map.of("field", "nobool")); + evaluateCondition(testRule, message, false); + } + + @Test + public void testFieldCollection() { + Message message = new Message("Dummy Message", "test", Tools.nowUTC()); + final RuleFragment fragment = migration.createCheckFieldTypeNoConversion("collection"); + + message.addField("list", List.of("a", "b", "c")); + Rule testRule = createFragmentSource(fragment, Map.of("field", "list")); + evaluateCondition(testRule, message, true); + + message.addField("nocollection", "iamastring"); + testRule = createFragmentSource(fragment, Map.of("field", "nocollection")); + evaluateCondition(testRule, message, false); + } + + @Test + public void testFieldDouble() { + Message message = new Message("Dummy Message", "test", Tools.nowUTC()); + final RuleFragment fragment = migration.createCheckFieldType("double"); + + message.addField("double", 2.25); + Rule testRule = createFragmentSource(fragment, Map.of("field", "double")); + evaluateCondition(testRule, message, true); + + message.addField("doublestring", "2.25"); + testRule = createFragmentSource(fragment, Map.of("field", "doublestring")); + evaluateCondition(testRule, message, false); + testRule = createFragmentSource(fragment, Map.of("field", "doublestring", "attemptConversion", true)); + evaluateCondition(testRule, message, true); + + message.addField("string", "iamastring"); + testRule = createFragmentSource(fragment, Map.of("field", "string")); + evaluateCondition(testRule, message, false); + } + + @Test + public void testFieldDate() { + Message message = new Message("Dummy Message", "test", Tools.nowUTC()); + final RuleFragment fragment = migration.createCheckDateField(); + + message.addField("date", Tools.nowUTC()); + Rule testRule = createFragmentSource(fragment, Map.of("field", "date")); + evaluateCondition(testRule, message, true); + + message.addField("datestring", "01 07 2023"); + testRule = createFragmentSource(fragment, Map.of("field", "datestring", "pattern", "d M y")); + evaluateCondition(testRule, message, true); + + message.addField("string", "iamastring"); + testRule = createFragmentSource(fragment, Map.of("field", "string")); + evaluateCondition(testRule, message, false); + } + + @Test + public void testFieldList() { + Message message = new Message("Dummy Message", "test", Tools.nowUTC()); + final RuleFragment fragment = migration.createCheckFieldTypeNoConversion("list"); + + message.addField("list", List.of("a", "b", "c")); + Rule testRule = createFragmentSource(fragment, Map.of("field", "list")); + evaluateCondition(testRule, message, true); + + message.addField("string", "iamastring"); + testRule = createFragmentSource(fragment, Map.of("field", "string")); + evaluateCondition(testRule, message, false); + } + + @Test + public void testFieldIp() { + Message message = new Message("Dummy Message", "test", Tools.nowUTC()); + final RuleFragment fragment = migration.createCheckFieldType("ip"); + + message.addField("ip", new IpAddress(InetAddress.getLoopbackAddress())); + Rule testRule = createFragmentSource(fragment, Map.of("field", "ip")); + evaluateCondition(testRule, message, true); + + message.addField("string", "127.0.0.1"); + testRule = createFragmentSource(fragment, Map.of("field", "string")); + evaluateCondition(testRule, message, false); + testRule = createFragmentSource(fragment, Map.of("field", "string", "attemptConversion", true)); + evaluateCondition(testRule, message, true); + + message.addField("string", "iamastring"); + testRule = createFragmentSource(fragment, Map.of("field", "string")); + evaluateCondition(testRule, message, false); + } + + @Test + public void testFieldLong() { + Message message = new Message("Dummy Message", "test", Tools.nowUTC()); + final RuleFragment fragment = migration.createCheckFieldType("long"); + + message.addField("long", 1234L); + Rule testRule = createFragmentSource(fragment, Map.of("field", "long")); + evaluateCondition(testRule, message, true); + + message.addField("string", "1234"); + testRule = createFragmentSource(fragment, Map.of("field", "string")); + evaluateCondition(testRule, message, false); + testRule = createFragmentSource(fragment, Map.of("field", "string", "attemptConversion", true)); + evaluateCondition(testRule, message, true); + + message.addField("string", "iamastring"); + testRule = createFragmentSource(fragment, Map.of("field", "string")); + evaluateCondition(testRule, message, false); + } + + @Test + public void testFieldMap() { + Message message = new Message("Dummy Message", "test", Tools.nowUTC()); + final RuleFragment fragment = migration.createCheckFieldType("map"); + + message.addField("map", Map.of("a", "b")); + Rule testRule = createFragmentSource(fragment, Map.of("field", "map")); + evaluateCondition(testRule, message, true); + + message.addField("string", "iamastring"); + testRule = createFragmentSource(fragment, Map.of("field", "string")); + evaluateCondition(testRule, message, false); + } + + @Test + public void testFieldNotNull() { + Message message = new Message("Dummy Message", "test", Tools.nowUTC()); + final RuleFragment fragment = migration.createCheckFieldTypeNoConversion("not_null"); + + message.addField("notnull", "anything"); + Rule testRule = createFragmentSource(fragment, Map.of("field", "notnull")); + evaluateCondition(testRule, message, true); + + testRule = createFragmentSource(fragment, Map.of("field", "string")); + evaluateCondition(testRule, message, false); + } + + @Test + public void testFieldNull() { + Message message = new Message("Dummy Message", "test", Tools.nowUTC()); + final RuleFragment fragment = migration.createCheckFieldTypeNoConversion("null"); + + Rule testRule = createFragmentSource(fragment, Map.of("field", "null")); + evaluateCondition(testRule, message, true); + + message.addField("string", "iamastring"); + testRule = createFragmentSource(fragment, Map.of("field", "string")); + evaluateCondition(testRule, message, false); + } + + @Test + public void testFieldNumber() { + Message message = new Message("Dummy Message", "test", Tools.nowUTC()); + final RuleFragment fragment = migration.createCheckFieldTypeNoConversion("number"); + + message.addField("number", 1234); + Rule testRule = createFragmentSource(fragment, Map.of("field", "number")); + evaluateCondition(testRule, message, true); + + message.addField("string", "iamastring"); + testRule = createFragmentSource(fragment, Map.of("field", "string")); + evaluateCondition(testRule, message, false); + } + + @Test + public void testFieldString() { + Message message = new Message("Dummy Message", "test", Tools.nowUTC()); + final RuleFragment fragment = migration.createCheckFieldType("string"); + + message.addField("string", "iamastring"); + Rule testRule = createFragmentSource(fragment, Map.of("field", "string")); + evaluateCondition(testRule, message, true); + + message.addField("notastring", 1); + testRule = createFragmentSource(fragment, Map.of("field", "notastring")); + evaluateCondition(testRule, message, false); + } + + @Test + public void testFieldPeriod() { + Message message = new Message("Dummy Message", "test", Tools.nowUTC()); + final RuleFragment fragment = migration.createCheckFieldTypeNoConversion("period"); + + message.addField("period", Period.parse("P1M")); + Rule testRule = createFragmentSource(fragment, Map.of("field", "period")); + evaluateCondition(testRule, message, true); + + message.addField("string", "iamastring"); + testRule = createFragmentSource(fragment, Map.of("field", "string")); + evaluateCondition(testRule, message, false); + } + + @Test + public void testFieldUrl() throws MalformedURLException { + Message message = new Message("Dummy Message", "test", Tools.nowUTC()); + final RuleFragment fragment = migration.createCheckFieldType("url"); + + message.addField("url", new URL("http://dummy.net")); + Rule testRule = createFragmentSource(fragment, Map.of("field", "url")); + evaluateCondition(testRule, message, true); + + message.addField("string", "http://dummy.net"); + testRule = createFragmentSource(fragment, Map.of("field", "string")); + evaluateCondition(testRule, message, false); + testRule = createFragmentSource(fragment, Map.of("field", "string", "attemptConversion", true)); + evaluateCondition(testRule, message, true); + + message.addField("string", "iamastring"); + testRule = createFragmentSource(fragment, Map.of("field", "string")); + evaluateCondition(testRule, message, false); + } + + @Test + public void testFieldCidr() throws MalformedURLException { + Message message = new Message("Dummy Message", "test", Tools.nowUTC()); + final RuleFragment fragment = migration.createCIDRMatchField(); + + message.addField("ip", "192.168.1.42"); + Rule testRule = createFragmentSource(fragment, Map.of("field", "ip", "cidr", "192.168.1.15/24")); + evaluateCondition(testRule, message, true); + + message.addField("noip", "somestring"); + testRule = createFragmentSource(fragment, Map.of("field", "noip", "cidr", "192.168.1.15/24")); + evaluateCondition(testRule, message, false); + + } + + @Test + public void testFieldContains() { + Message message = new Message("Dummy Message", "test", Tools.nowUTC()); + final RuleFragment fragment = migration.createStringContainsField(); + + message.addField("string", "snickerdoodledoo"); + Rule testRule = createFragmentSource(fragment, Map.of("field", "string", "search", "doodle")); + evaluateCondition(testRule, message, true); + testRule = createFragmentSource(fragment, Map.of("field", "string", "search", "noodle")); + evaluateCondition(testRule, message, false); + testRule = createFragmentSource(fragment, Map.of("field", "string", "search", "DOODLE")); + evaluateCondition(testRule, message, false); + testRule = createFragmentSource(fragment, Map.of("field", "string", "search", "DOODLE", "ignoreCase", true)); + evaluateCondition(testRule, message, true); + } + + @Test + public void testFieldStartsWith() { + Message message = new Message("Dummy Message", "test", Tools.nowUTC()); + final RuleFragment fragment = migration.createStringStartsWithField(); + + message.addField("string", "snickerdoodledoo"); + Rule testRule = createFragmentSource(fragment, Map.of("field", "string", "search", "snicker")); + evaluateCondition(testRule, message, true); + testRule = createFragmentSource(fragment, Map.of("field", "string", "search", "doodle")); + evaluateCondition(testRule, message, false); + testRule = createFragmentSource(fragment, Map.of("field", "string", "search", "SNICKER")); + evaluateCondition(testRule, message, false); + testRule = createFragmentSource(fragment, Map.of("field", "string", "search", "SNICKER", "ignoreCase", true)); + evaluateCondition(testRule, message, true); + } + + @Test + public void testFieldEndsWith() { + Message message = new Message("Dummy Message", "test", Tools.nowUTC()); + final RuleFragment fragment = migration.createStringEndsWithField(); + + message.addField("string", "snickerdoodledoo"); + Rule testRule = createFragmentSource(fragment, Map.of("field", "string", "search", "doo")); + evaluateCondition(testRule, message, true); + testRule = createFragmentSource(fragment, Map.of("field", "string", "search", "doodle")); + evaluateCondition(testRule, message, false); + testRule = createFragmentSource(fragment, Map.of("field", "string", "search", "DOO")); + evaluateCondition(testRule, message, false); + testRule = createFragmentSource(fragment, Map.of("field", "string", "search", "DOO", "ignoreCase", true)); + evaluateCondition(testRule, message, true); + } + + @Test + public void testFieldGrokMatches() { + Message message = new Message("Dummy Message", "test", Tools.nowUTC()); + final RuleFragment fragment = migration.createGrokMatchesField(); + + message.addField("string", "string"); + message.addField("number", "10"); + Rule testRule = createFragmentSource(fragment, Map.of("field", "string", "pattern", "%{BASE10NUM}")); + evaluateCondition(testRule, message, false); + testRule = createFragmentSource(fragment, Map.of("field", "number", "pattern", "%{BASE10NUM}")); + evaluateCondition(testRule, message, true); + } + +} diff --git a/graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/rulebuilder/parser/ActionParserTest.java b/graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/rulebuilder/parser/ActionParserTest.java index 2e38b2bc31b56..a0af35837142b 100644 --- a/graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/rulebuilder/parser/ActionParserTest.java +++ b/graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/rulebuilder/parser/ActionParserTest.java @@ -61,7 +61,7 @@ public static void registerFunctions() { functions.put(FUNCTION2_NAME, FunctionUtil.testFunction( FUNCTION2_NAME, ImmutableList.of( integer("optional").optional().build() - ), Boolean.class + ), Integer.class )); functions.put(FUNCTION3_NAME, FunctionUtil.testFunction( FUNCTION3_NAME, ImmutableList.of( diff --git a/graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/rulebuilder/parser/BaseFragmentTest.java b/graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/rulebuilder/parser/BaseFragmentTest.java new file mode 100644 index 0000000000000..ed3fdfe0c4670 --- /dev/null +++ b/graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/rulebuilder/parser/BaseFragmentTest.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2020 Graylog, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * . + */ +package org.graylog.plugins.pipelineprocessor.rulebuilder.parser; + +import freemarker.cache.StringTemplateLoader; +import freemarker.template.Configuration; +import org.graylog.plugins.pipelineprocessor.BaseParserTest; +import org.graylog.plugins.pipelineprocessor.ast.Rule; +import org.graylog.plugins.pipelineprocessor.ast.functions.FunctionDescriptor; +import org.graylog.plugins.pipelineprocessor.rulebuilder.RuleBuilderStep; +import org.graylog.plugins.pipelineprocessor.rulebuilder.db.RuleFragment; +import org.graylog2.bindings.providers.SecureFreemarkerConfigProvider; +import org.graylog2.plugin.Message; +import org.graylog2.shared.utilities.StringUtils; +import org.junit.Before; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class BaseFragmentTest extends BaseParserTest { + + private static final Logger log = LoggerFactory.getLogger(BaseFragmentTest.class); + Configuration configuration; + + @Before + public void initializeFreemarkerConfig() { + SecureFreemarkerConfigProvider secureFreemarkerConfigProvider = new SecureFreemarkerConfigProvider(); + this.configuration = secureFreemarkerConfigProvider.get(); + configuration.setLogTemplateExceptions(false); + } + + protected Rule createFragmentSource(RuleFragment ruleFragment, Map parameters) { + assertThat(ruleFragment.isFragment()).isTrue(); + final FunctionDescriptor descriptor = ruleFragment.descriptor(); + assertThat(descriptor.ruleBuilderEnabled()).isTrue(); + assertThat(descriptor.name()).isNotNull(); + assertThat(descriptor.ruleBuilderName()).isNotNull(); + assertThat(descriptor.ruleBuilderTitle()).isNotNull(); + assertThat(descriptor.ruleBuilderFunctionGroup()).isNotNull(); + if (descriptor.returnType() != Void.class) { + assertThat(ruleFragment.fragmentOutputVariable()).isNotNull(); + } + + // initialize freemarker + StringTemplateLoader templateLoader = new StringTemplateLoader(); + templateLoader.putTemplate(ruleFragment.getName(), ruleFragment.fragment()); + configuration.setTemplateLoader(templateLoader); + + // initialize step + RuleBuilderStep step = mock(RuleBuilderStep.class); + when(step.function()).thenReturn(ruleFragment.getName()); + when(step.parameters()).thenReturn(parameters); + final String fragment = ParserUtil.generateForFragment(step, configuration); + + String rule = (ruleFragment.isCondition()) ? + """ + rule "testfragment" + when + %s + then + set_field("testsuccess", true); + end + """ + : + """ + rule "testfragment" + when true + then + %s + end + """; + + + rule = StringUtils.f(rule, fragment); + log.debug(rule); + return parser.parseRule(rule, true); + } + + protected void evaluateCondition(Rule rule, Message message, boolean expectResult) { + Message result = evaluateRule(rule, message); + if (expectResult) { + assertThat(result.hasField("testsuccess")).isTrue(); + } else { + assertThat(result).isNull(); + } + } + +} diff --git a/graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/rulebuilder/parser/ConditionParserTest.java b/graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/rulebuilder/parser/ConditionParserTest.java index fa93c338ecc23..d5baacda717d2 100644 --- a/graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/rulebuilder/parser/ConditionParserTest.java +++ b/graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/rulebuilder/parser/ConditionParserTest.java @@ -64,10 +64,6 @@ public static void registerFunctions() { integer("optional").optional().build() ), Boolean.class )); -// functions.put(FRAGMENT1_NAME, FunctionUtil.testCondition( -// FRAGMENT1_NAME, "hasField(\"{field}\")", -// ImmutableList.of(string("field").build()) -// )); RuleFragmentService ruleFragmentService = mock(RuleFragmentService.class); when(ruleFragmentService.all()).thenReturn(new ArrayList<>()); @@ -86,28 +82,28 @@ public void initialize() { @Test public void generateTrueValue_WhenRuleConditionsEmpty() { - assertThat(conditionParser.generate(new ArrayList<>())).isEqualTo(" true" + NL); + assertThat(conditionParser.generate(new ArrayList<>(), RuleBuilderStep.Operator.AND, 1)).isEqualTo(" true"); } @Test public void emptyString_WhenConditionNotInRuleBuilderConditions() { RuleBuilderStep step = RuleBuilderStep.builder().function("unknownFunction").build(); - assertThat(conditionParser.generateCondition(step)).isEqualTo(""); + assertThat(conditionParser.generateCondition(step, 1)).isEqualTo(""); } @Test public void singleConditionWithoutParamsGeneration() { RuleBuilderStep step = RuleBuilderStep.builder().function(FUNCTION2_NAME).build(); - assertThat(conditionParser.generateCondition(step)).isEqualTo( - " && function2()" + assertThat(conditionParser.generateCondition(step, 1)).isEqualTo( + " function2()" ); } @Test public void singleNegatedConditionWithoutParamsGeneration() { RuleBuilderStep step = RuleBuilderStep.builder().function(FUNCTION2_NAME).negate().build(); - assertThat(conditionParser.generateCondition(step)).isEqualTo( - " && ! function2()" + assertThat(conditionParser.generateCondition(step, 1)).isEqualTo( + " ! function2()" ); } @@ -115,8 +111,8 @@ public void singleNegatedConditionWithoutParamsGeneration() { public void singleConditionWithSingleParamGeneration() { RuleBuilderStep step = RuleBuilderStep.builder().function(FUNCTION1_NAME) .parameters(Map.of("required", "val1")).build(); - assertThat(conditionParser.generateCondition(step)).isEqualTo( - " && function1(" + NL + " required : \"val1\"" + NL + " )" + assertThat(conditionParser.generateCondition(step, 1)).isEqualTo( + " function1(" + NL + " required : \"val1\"" + NL + " )" ); } @@ -124,8 +120,8 @@ public void singleConditionWithSingleParamGeneration() { public void singleConditionWithMultipleParamsGeneration() { RuleBuilderStep step = RuleBuilderStep.builder().function(FUNCTION1_NAME) .parameters(Map.of("required", "val1", "optional", 1)).build(); - assertThat(conditionParser.generateCondition(step)).isEqualTo( - " && function1(" + NL + + assertThat(conditionParser.generateCondition(step, 1)).isEqualTo( + " function1(" + NL + " required : \"val1\"," + NL + " optional : 1" + NL + " )" @@ -137,9 +133,8 @@ public void generate_WhenRuleConditionsContainsOneFunction() { List steps = List.of( RuleBuilderStep.builder().function(FUNCTION2_NAME).build() ); - assertThat(conditionParser.generate(steps)).isEqualTo(""" - true - && function2() + assertThat(conditionParser.generate(steps, RuleBuilderStep.Operator.AND, 1)).isEqualTo(""" + function2() """.stripTrailing()); } @@ -151,19 +146,14 @@ public void generate_WhenRuleConditionsContainsMultipleFunctions() { .build(), RuleBuilderStep.builder().function(FUNCTION2_NAME).negate().build() ); - assertThat(conditionParser.generate(steps)).isEqualTo(""" - true - && function1( + assertThat(conditionParser.generate(steps, RuleBuilderStep.Operator.AND, 1)).isEqualTo(""" + function1( required : "val1" ) - && ! function2() + AND + ! function2() """.stripTrailing()); } - @Test - public void generate_WhenRuleConditionsContainsOneFragment() { - - } - } diff --git a/graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/rulebuilder/parser/RuleBuilderServiceTest.java b/graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/rulebuilder/parser/RuleBuilderServiceTest.java index ee3470b0437c2..1ac7d56c7869b 100644 --- a/graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/rulebuilder/parser/RuleBuilderServiceTest.java +++ b/graylog2-server/src/test/java/org/graylog/plugins/pipelineprocessor/rulebuilder/parser/RuleBuilderServiceTest.java @@ -29,6 +29,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) @@ -49,7 +50,7 @@ public void initialize() { @Test public void generateRuleSourceSyntaxOk() { RuleBuilder ruleBuilder = RuleBuilder.builder().build(); - when(conditionParser.generate(any())).thenReturn(" conditions"); + when(conditionParser.generate(any(), any(), anyInt())).thenReturn(" conditions"); when(actionParser.generate(any(), anyBoolean())).thenReturn(" actions"); assertThat(ruleBuilderService.generateRuleSource("title", ruleBuilder, false)) .isEqualTo(""" diff --git a/graylog2-web-interface/src/components/bootstrap/Input.jsx b/graylog2-web-interface/src/components/bootstrap/Input.jsx index 6bb6823df89c6..75e62d189c865 100644 --- a/graylog2-web-interface/src/components/bootstrap/Input.jsx +++ b/graylog2-web-interface/src/components/bootstrap/Input.jsx @@ -161,13 +161,21 @@ class Input extends React.Component { }; _renderCheckboxGroup = (controlProps) => { - const { id, bsStyle, formGroupClassName, inputDescClassName, wrapperClassName, label, error, help } = this.props; + const { id, buttonAfter, bsStyle, formGroupClassName, inputDescClassName, wrapperClassName, label, error, help } = this.props; return ( - { this.input = ref; }} {...controlProps}>{label} + {buttonAfter ? ( + + { this.input = ref; }} {...controlProps}>{label} + {buttonAfter && {buttonAfter}} + + ) : ( + { this.input = ref; }} {...controlProps}>{label} + )} + ); @@ -190,17 +198,13 @@ class Input extends React.Component { const { id, type, - bsStyle, - formGroupClassName, - wrapperClassName, + children, label, - labelClassName, inputDescClassName, name, - error, - help, - children, - addonAfter, - buttonAfter, + // The following props need to be extracted even if they are not used + // so they are not passed as controll props to the input + bsStyle, formGroupClassName, wrapperClassName, labelClassName, inputDescClassName, // eslint-disable-line no-unused-vars + error, help, addonAfter, buttonAfter, // eslint-disable-line no-unused-vars ...controlProps } = this.props; diff --git a/graylog2-web-interface/src/components/common/FormikFormGroup.tsx b/graylog2-web-interface/src/components/common/FormikFormGroup.tsx index c230d122f6555..fef47deebea31 100644 --- a/graylog2-web-interface/src/components/common/FormikFormGroup.tsx +++ b/graylog2-web-interface/src/components/common/FormikFormGroup.tsx @@ -24,6 +24,7 @@ import FormikInput from './FormikInput'; type Props = { autoComplete?: string, buttonAfter?: React.ReactElement | string, + children?: React.ReactNode, disabled?: boolean, label: React.ReactElement | string, name: string, @@ -59,6 +60,7 @@ FormikFormGroup.defaultProps = { autoComplete: undefined, bsSize: undefined, buttonAfter: undefined, + children: null, disabled: false, onChange: undefined, labelClassName: 'col-sm-3', diff --git a/graylog2-web-interface/src/components/common/FormikInput.tsx b/graylog2-web-interface/src/components/common/FormikInput.tsx index 1692a68fed5b2..23cd4b0bc42b4 100644 --- a/graylog2-web-interface/src/components/common/FormikInput.tsx +++ b/graylog2-web-interface/src/components/common/FormikInput.tsx @@ -25,6 +25,7 @@ type BaseProps = { autoComplete?: string, bsSize?: 'large' | 'small' | 'xsmall', buttonAfter?: React.ReactElement | string, + children?: React.ReactNode, disabled?: boolean, error?: React.ReactElement | string, formGroupClassName?: string, @@ -92,6 +93,10 @@ FormikInput.propTypes = { autoComplete: PropTypes.string, bsSize: PropTypes.string, buttonAfter: PropTypes.oneOfType([PropTypes.string, PropTypes.element]), + children: PropTypes.oneOfType([ + PropTypes.array, + PropTypes.element, + ]), disabled: PropTypes.bool, error: PropTypes.oneOfType([PropTypes.string, PropTypes.element]), formGroupClassName: PropTypes.string, @@ -114,6 +119,7 @@ FormikInput.defaultProps = { autoComplete: undefined, bsSize: undefined, buttonAfter: undefined, + children: null, disabled: false, error: undefined, formGroupClassName: undefined, diff --git a/graylog2-web-interface/src/components/common/Icon.tsx b/graylog2-web-interface/src/components/common/Icon.tsx index 99a0756aabf9b..8c7c5ca2112a9 100644 --- a/graylog2-web-interface/src/components/common/Icon.tsx +++ b/graylog2-web-interface/src/components/common/Icon.tsx @@ -18,7 +18,7 @@ import React from 'react'; import find from 'lodash/find'; import type { IconName } from '@fortawesome/fontawesome-common-types'; -import type { SizeProp } from '@fortawesome/fontawesome-svg-core'; +import type { SizeProp, RotateProp } from '@fortawesome/fontawesome-svg-core'; import deprecationNotice from 'util/deprecationNotice'; import loadAsync from 'routing/loadAsync'; @@ -69,6 +69,7 @@ type Props = { 'data-testid'?: string, /** Name of Font Awesome 5 Icon without `fa-` prefix */ name: IconName, + rotation?: RotateProp, size?: SizeProp, spin?: boolean, /** @@ -101,6 +102,7 @@ const Icon = ({ type, size, className, + rotation, spin, fixedWidth, inverse, @@ -122,6 +124,7 @@ const Icon = ({ fixedWidth={fixedWidth} icon={{ prefix, iconName }} inverse={inverse} + rotation={rotation} size={size} spin={spin} style={style} @@ -139,6 +142,7 @@ Icon.defaultProps = { 'data-testid': undefined, fixedWidth: false, inverse: false, + rotation: undefined, size: undefined, spin: false, style: undefined, diff --git a/graylog2-web-interface/src/components/common/SearchForm.tsx b/graylog2-web-interface/src/components/common/SearchForm.tsx index 366605163f3a2..0d3ad488e6001 100644 --- a/graylog2-web-interface/src/components/common/SearchForm.tsx +++ b/graylog2-web-interface/src/components/common/SearchForm.tsx @@ -202,7 +202,7 @@ const SearchForm = ({ {label} )} - + React.ReactElement) => ( +const CustomOption = (optionRenderer: (option: Option, isSelected: boolean) => React.ReactElement) => ( (props: React.ComponentProps): React.ReactElement => { - const { data } = props; + const { data, isSelected } = props; return ( - {optionRenderer(data)} + {optionRenderer(data, isSelected)} ); } @@ -238,7 +238,7 @@ export type Props = { onChange: (value: OptionValue) => void, onReactSelectChange?: (option: Option | Option[]) => void, onMenuClose?: () => void, - optionRenderer?: (option: Option) => React.ReactElement, + optionRenderer?: (option: Option, isSelected?: boolean) => React.ReactElement, options: Array