Skip to content

[ResponseOps][Alerts] Implement alerts filters form#214982

Merged
umbopepato merged 28 commits intoelastic:mainfrom
umbopepato:213061-alerts-boolean-filters-ui
Apr 17, 2025
Merged

[ResponseOps][Alerts] Implement alerts filters form#214982
umbopepato merged 28 commits intoelastic:mainfrom
umbopepato:213061-alerts-boolean-filters-ui

Conversation

@umbopepato
Copy link
Member

@umbopepato umbopepato commented Mar 18, 2025

Summary

Implements the alerts filters form that will be used to pre-filter the alerts table embeddable.

image

Note

I'm using the terminology "form" to distinguish this from the alert filter controls or other type of more KQL-bar-like filters. Other alternatives that came to mind were alerts-boolean-filters-... or alerts-filters-builder.

Implementation details

Filters expression state

I opted for a tree state representation of the form's boolean expression to accommodate potential future requirements such as more complex boolean expressions (negation, parenthesized subexpressions to manually control operators precedence):

{
  operator: 'or',
  operands: [
    {
      operator: 'or',
      operands: [
        { type: 'ruleTags', value: ['tag-1'] },
        { type: 'ruleTags', value: ['tag-2'] },
        {
          operator: 'and',
          operands: [{ type: 'ruleTypes', value: ['type-1'] }, { type: 'ruleTypes', value: ['type-2'] }],
        },
      ],
    },
    { type: 'ruleTags', value: ['tag-3'] },
  ],
}

This state is saved in the embeddable panel state and represents the editor form. The embeddable alerts table wrapper component will then transform this to an actual ES query.

To simplify interactions inside the form, an intermediate equivalent flattened state is used:

[
  { filter: { type: 'ruleTags', value: ['tag-1'] } },
  { operator: 'or' },
  { filter: { type: 'ruleTags', value: ['tag-2'] } },
  { operator: 'or' },
  { filter: { type: 'ruleTypes', value: ['type-1'] }},
  { operator: 'and' },
  { filter: { type: 'ruleTypes', value: ['type-2'] } },
  { operator: 'or' },
  { filter: { type: 'ruleTags', value: ['tag-3'] } },
]

Filters model

Each filter is described by an AlertsFilterMetadata<T> object, where T is the type of the filter value:

export const filterMetadata: AlertsFilterMetadata<string[]> = {
  id: 'ruleTags',
  displayName: RULE_TAGS_FILTER_LABEL,
  component: AlertsFilterByRuleTags,
  // Filter-specific empty check
  isEmpty: (value?: string[]) => !value?.length,
  // Conversion to ES query DSL
  toEsQuery: (value: string[]) => {
    return {
      terms: {
        [ALERT_RULE_TAGS]: value,
      },
    };
  },
};

Verification steps

  1. Run Kibana with examples (yarn start --run-examples)
  2. Create rules in different solutions with tags
  3. Navigate to /app/triggersActionsUiExample/alerts_filters_form
  4. Check that the solution selector options are coherent with the rule types the user can access
  5. Select a solution
  6. Build filters expressions, checking that the rule tags and rule types are coherent with the solution selection and the rules created previously
  7. Repeat steps 3-6 with different roles:
    7.1. having access to rule types from just one solution (in this case the solution selector shouldn't appear at all),
    7.2. having access just to Observability and Stack but not Security (in this case the solution selector shouldn't appear at all),
  8. Repeat steps 3-6 in the three serverless project types:
    $ yarn es serverless —ssl --projectType <es|oblt|security>
    $ yarn serverless-<es|oblt|security> --ssl --run-examples
    (If the authentication fails when switching between project types, use a clean session)
    8.1. ES project types should have access only to Stack rules (no selector)
    8.2. Observability project types should have access only to Observability and Stack rules (no selector)
    8.3. Security project types should have access only to Security and Stack rules (selector shows Stack instead of Observability)

References

Depends on #214187
Closes #213061

Checklist

@umbopepato umbopepato force-pushed the 213061-alerts-boolean-filters-ui branch 2 times, most recently from 03c52ed to 1647136 Compare March 18, 2025 14:57
@umbopepato umbopepato added release_note:skip Skip the PR/issue when compiling release notes Team:ResponseOps Platform ResponseOps team (formerly the Cases and Alerting teams) t// backport:version Backport to applied version labels v9.1.0 v8.19.0 labels Mar 18, 2025
@umbopepato umbopepato force-pushed the 213061-alerts-boolean-filters-ui branch 6 times, most recently from 0ba7dbc to 9876eba Compare March 24, 2025 13:27
@umbopepato umbopepato force-pushed the 213061-alerts-boolean-filters-ui branch from 9876eba to 9982fb6 Compare March 26, 2025 13:56
@umbopepato umbopepato marked this pull request as ready for review March 26, 2025 13:57
@umbopepato umbopepato requested a review from a team as a code owner March 26, 2025 13:57
@elasticmachine
Copy link
Contributor

Pinging @elastic/response-ops (Team:ResponseOps)

isDisabled?: boolean;
}

const DEFAULT_VALUE: AlertsFiltersExpression = {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ensures that the form is initialized with an initially empty "Filter by" selector

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this comment here could be a comment in code for future us

@umbopepato umbopepato force-pushed the 213061-alerts-boolean-filters-ui branch from 2b3bc5c to 1d8fca4 Compare March 27, 2025 11:21
Copy link
Contributor

@js-jankisalvi js-jankisalvi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work! looks really good 🎉
Could you please add an integration test or functional test to check different roles and users to verify solutions and rule types privileges?

I have a user with `security` and `stack rules` privileges. However the solution shows `observability`. Shouldn't it be security and stack like serverless?
security_and_stack.mov
security_and_stack_permissions.mov

@umbopepato
Copy link
Member Author

Great work! looks really good 🎉

Thanks for taking a look Janki! 😊

Could you please add an integration test or functional test to check different roles and users to verify solutions and rule types privileges?

I was hoping to test the complete creation flow with the Dashboard panel as well 🙂

I have a user with security and stack rules privileges. However the solution shows observability. Shouldn't it be security and stack like serverless?

Yes, that's normal since in non-serverless envs when enabling the Stack rules privilege you also get access to multi-consumer rule types, one of which is categorized under observability. That shouldn't be a problem since it's just a label we show, but it includes both solutions. That is to say: the privileges of the users are always honored

@umbopepato umbopepato removed request for a team April 14, 2025 11:44
Copy link
Member

@cnasikas cnasikas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your patience and with addressing my comments!

export const getRuleTypeIdsForSolution = (
ruleTypes: InternalRuleType[],
solution: RuleTypeSolution,
includeStackInObservability = true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Do not forget to remove this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done ✅

});

it.each([null, undefined])('should return false for %s items', (filter) => {
expect(isFilter(filter as Parameters<typeof isFilter>[0])).toBeFalsy();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
expect(isFilter(filter as Parameters<typeof isFilter>[0])).toBeFalsy();
// @ts-expect-error: testing empty values
expect(isFilter(filter)).toBeFalsy();

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done ✅

return null;
}
const FilterComponent = alertsFiltersMetadata[type].component as AlertsFilterComponentType<T>;
return <FilterComponent value={value} onChange={onValueChange} isDisabled={isDisabled} />;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was referring to const filter = useMemo(() => {. Any idea on that?

Comment on lines +90 to +97
if (options.length < 2) {
if (options.length === 1) {
// Select the only available solution and
// don't show the selector
onSolutionChange(options[0].value);
}
return null;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A pseudo example of how it can be done without any useEffects or call of onSolutionChange.

// top-level component
const { data: ruleTypes, isLoading, isError } = useGetInternalRuleTypesQuery({ http });

if (isLoading) {
   return null;
}

return <AlertsFilterFormWraper ruleTypes={ruleTypes}> // name tbd


// AlertsFilterFormWraper
// ruleTypes always available as the component will render after the fetch
const AlertsFilterFormWraper = ({ ruleTypes }) => {

const availableSolutions = useMemo(() => getAvailableSolutions(ruleTypes), [ruleTypes]);

const [selectedSolution, setSelectedSolution] = useState(availableSolutions.length === 1 ? availableSolutions[0].value : undefined) // or whatever default we want.

availableSolutions.length > 1 && <AlertsSolutionSelector availableSolutions={availableSolutions} selectedSolution={selectedSolution} onSolutionChange={setSolution}>

}

It is very similar to what you have in the sandbox with some tweaks. We can do it on the final PR.

Copy link
Contributor

@azasypkin azasypkin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes in package.json LGTM

@kibanamachine
Copy link
Contributor

Starting backport for target branches: 8.19

https://github.com/elastic/kibana/actions/runs/14517822494

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

Metrics [docs]

Async chunks

Total size of all lazy-loaded chunks that will be downloaded as the user navigates the app

id before after diff
triggersActionsUi 1.4MB 1.4MB +40.0B
Unknown metric groups

ESLint disabled line counts

id before after diff
@kbn/response-ops-alerts-filters-form - 1 +1

Total ESLint disabled count

id before after diff
@kbn/response-ops-alerts-filters-form - 1 +1

History

@kibanamachine
Copy link
Contributor

💔 All backports failed

Status Branch Result
8.19 Backport failed because of merge conflicts

Manual backport

To create the backport manually run:

node scripts/backport --pr 214982

Questions ?

Please refer to the Backport tool documentation

@kibanamachine
Copy link
Contributor

Friendly reminder: Looks like this PR hasn’t been backported yet.
To create automatically backports add a backport:* label or prevent reminders by adding the backport:skip label.
You can also create backports manually by running node scripts/backport --pr 214982 locally

@umbopepato
Copy link
Member Author

💚 All backports created successfully

Status Branch Result
8.19

Note: Successful backport PRs will be merged automatically after passing CI.

Questions ?

Please refer to the Backport tool documentation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport:version Backport to applied version labels release_note:skip Skip the PR/issue when compiling release notes Team:ResponseOps Platform ResponseOps team (formerly the Cases and Alerting teams) t// v8.19.0 v9.1.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[ResponseOps][Alerts] Alerts boolean query filters UI

8 participants