Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Cases] Remove case info from alerts when deleting an alert attachment #154024

Merged
merged 8 commits into from
Apr 1, 2023

Conversation

cnasikas
Copy link
Member

@cnasikas cnasikas commented Mar 30, 2023

Summary

Users can remove alerts from a case by deleting the whole alert attachment. This PR removes the case id from the alerts when deleting an attachment of type alert. It does not remove the case info from all alerts attached to a case when deleting a case. It also fixes a bug where the success toaster will not show when deleting an attachment.

Related: #146864, #140800

Testing

  1. Create a case and attach some alerts to the case.
  2. Verify that the alerts table (in security or in o11y) shows the case the alert is attached to. You can enable the cases column by pressing "Fields", searching for "Cases", and then selecting the field.
  3. Go to the case and find the alerts' user activity.
  4. Press the ... and press "Remove alerts(s)"
  5. Go back to the alert table and verify that the case is not shown in the Cases column for each alert.

Please check that when you remove alert(s), attachments (ml, etc), and comments you get a success toaster with the correct text.

Checklist

Delete any items that are not applicable to this PR.

For maintainers

@cnasikas cnasikas self-assigned this Mar 30, 2023
@cnasikas cnasikas added release_note:skip Skip the PR/issue when compiling release notes Team:ResponseOps Label for the ResponseOps team (formerly the Cases and Alerting teams) Feature:Cases Cases feature v8.8.0 labels Mar 30, 2023
@cnasikas cnasikas mentioned this pull request Mar 30, 2023
8 tasks
@cnasikas cnasikas marked this pull request as ready for review March 30, 2023 14:26
@cnasikas cnasikas requested review from a team as code owners March 30, 2023 14:26
@elasticmachine
Copy link
Contributor

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

@elasticmachine
Copy link
Contributor

Pinging @elastic/response-ops-cases (Feature:Cases)

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.

Looks good. 👍
Verified locally, case id is removed from Cases columns in Alerts and success toaster is also visible once removed Alert from case. 🎉


export const isAlertAttachment = (
attachment: CommentAttributes
): attachment is AttributesTypeAlerts => attachment.type === CommentType.alert;
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we add unit test for this function?

},
},
{
script: { source: painlessScript, lang: 'painless' },
Copy link
Contributor

Choose a reason for hiding this comment

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

nice script name 😄

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes because is a pain to write them hahhaha.

Copy link
Contributor

@CoenWarmer CoenWarmer left a comment

Choose a reason for hiding this comment

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

AO owned changes LGTM

Copy link
Contributor

@jonathan-buttner jonathan-buttner 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, I left a few comments and I still need to test things. I'll do that shortly.

@@ -29,3 +31,7 @@ export const isCommentRequestTypePersistableState = (
): context is CommentRequestPersistableStateType => {
return context.type === CommentType.persistableState;
};

export const isAlertAttachment = (
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we use the type guard here: https://github.com/elastic/kibana/blob/main/x-pack/plugins/cases/server/common/utils.ts#L254

It's slightly different as it operates on the request rather than attributes.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah, thanks. And I was searching for an existing one.

return;
}

await this.alertsClient.removeAlertsFromCase({
Copy link
Contributor

Choose a reason for hiding this comment

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

removeAlertsFromCase makes me think this is removing the alert ids from the case but if I understand this correctly we're removing the case id from the alerts right? Maybe rename this to removeCaseIdFromAlerts

We'd probably want a similar change for bulkUpdateCases

operation: ReadOperations.Get,
});

const painlessScript = `if (ctx._source['${ALERT_CASE_IDS}'] != null && ctx._source['${ALERT_CASE_IDS}'].length > 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: I think we can remove ctx._source['${ALERT_CASE_IDS}'].length > 0 as the for-loop ensures that it won't execute the loop since i = 0 is not less than 0 or a negative number.


const painlessScript = `if (ctx._source['${ALERT_CASE_IDS}'] != null && ctx._source['${ALERT_CASE_IDS}'].length > 0) {
for (int i=0; i < ctx._source['${ALERT_CASE_IDS}'].length; i++) {
if (ctx._source['${ALERT_CASE_IDS}'][i] == '${caseId}') {
Copy link
Contributor

Choose a reason for hiding this comment

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

Painless might be figuring this out automatically but I think we want the string equality operator here:

if (ctx._source['${ALERT_CASE_IDS}'][i].equals('${caseId}'))


const mgetRes = await this.ensureAllAlertsAuthorized({
alerts,
operation: ReadOperations.Get,
Copy link
Contributor

Choose a reason for hiding this comment

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

Just double checking that we want the deletion operation to be a read here?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, the same as when we attach an alert to a case (add a case id to the alert schema).

});
} catch (error) {
throw createCaseError({
message: `Failed to remove alerts from case ${caseId}: ${error}`,
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe update this to say Failed to remove case id from alerts...

body: bulkUpdateRequest,
});
} catch (error) {
this.logger.error(`Error removing alerts from case ${caseId}: ${error}`);
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe update this and the function name to removing case id from alerts.

const updatedCases = [];

for (const theCase of cases) {
const updatedCase = await createComment({
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we do a Promise.all here?

Copy link
Member Author

Choose a reason for hiding this comment

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

The Promise.all produces some weird race conditions/conflicts and the alerts are not updated correctly with the case ids.

});

describe('observability', () => {
const createCaseAttachAlertAndDeleteAlert = async ({
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like this and the security solution function are pretty close do you think it'd make sense to move it to a function that both could call? I think the differences are the alerts that get created and owner, maybe those could be parameters?

totalCases: number;
indexOfCaseToDelete: number;
expectedHttpCode?: number;
auth?: { user: User; space: string | null };
Copy link
Contributor

Choose a reason for hiding this comment

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

How about we rename this to deleteCommentAuth since I think that's the only time we're using it right?

@jonathan-buttner
Copy link
Contributor

I tested it and everything is working great. The only slightly odd thing that could happen is if you attach an alert to the same case twice (as two separate attachments), then delete one of them, the case won't show in the alerts table but the case would still have an alert attached to it. I think this is fine though as I doubt users would add the same alert multiple times.

@cnasikas
Copy link
Member Author

cnasikas commented Apr 1, 2023

I tested it and everything is working great. The only slightly odd thing that could happen is if you attach an alert to the same case twice (as two separate attachments), then delete one of them, the case won't show in the alerts table but the case would still have an alert attached to it. I think this is fine though as I doubt users would add the same alert multiple times.

Very good point. Maybe we should start preventing users to attach the same alert twice in a case. I have an issue for that: #145514

@cnasikas cnasikas enabled auto-merge (squash) April 1, 2023 10:32
@cnasikas
Copy link
Member Author

cnasikas commented Apr 1, 2023

@elasticmachine run elasticsearch-ci/docs

@cnasikas
Copy link
Member Author

cnasikas commented Apr 1, 2023

@elasticmachine merge upstream

@kibana-ci
Copy link
Collaborator

💚 Build Succeeded

Metrics [docs]

Public APIs missing comments

Total count of every public API that lacks a comment. Target amount is 0. Run node scripts/build_api_docs --plugin [yourplugin] --stats comments for more detailed information.

id before after diff
ruleRegistry 229 231 +2

Async chunks

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

id before after diff
cases 363.7KB 364.3KB +595.0B

Public APIs missing exports

Total count of every type that is part of your API that should be exported but is not. This will cause broken links in the API documentation system. Target amount is 0. Run node scripts/build_api_docs --plugin [yourplugin] --stats exports for more detailed information.

id before after diff
ruleRegistry 14 15 +1
Unknown metric groups

API count

id before after diff
ruleRegistry 258 260 +2

ESLint disabled line counts

id before after diff
securitySolution 432 435 +3

Total ESLint disabled count

id before after diff
securitySolution 512 515 +3

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

cc @cnasikas

@cnasikas cnasikas merged commit 3a6f721 into elastic:main Apr 1, 2023
@cnasikas cnasikas deleted the remove_alerts branch April 1, 2023 14:51
@kibanamachine kibanamachine added the backport:skip This commit does not require backporting label Apr 1, 2023
@cnasikas cnasikas added release_note:breaking and removed release_note:skip Skip the PR/issue when compiling release notes labels Apr 3, 2023
@cnasikas cnasikas added release_note:skip Skip the PR/issue when compiling release notes and removed release_note:breaking labels Apr 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport:skip This commit does not require backporting Feature:Cases Cases feature release_note:skip Skip the PR/issue when compiling release notes Team:ResponseOps Label for the ResponseOps team (formerly the Cases and Alerting teams) v8.8.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants