Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@ export const APPROVED_STEP_DEFINITIONS: Array<{ id: string; handlerHash: string
id: 'cases.getCasesByAlertId',
handlerHash: '0066f40a84dad03c0d745e43f342b72cc42a2d4ac3207533a9f7fe0695d79b78',
},
{
id: 'cases.removeTags',
handlerHash: '1704c6d46ccb5432e1df6c24f7ebde8d4b1686c007dcaf6a5c5cac02b0222e3e',
},
{
id: 'cases.setCategory',
handlerHash: '1704c6d46ccb5432e1df6c24f7ebde8d4b1686c007dcaf6a5c5cac02b0222e3e',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import type { z } from '@kbn/zod/v4';
import { StepCategory } from '@kbn/workflows';
import type { CommonStepDefinition } from '@kbn/workflows-extensions/common';
import { CaseTags } from '../../bundled-types.gen';
import * as i18n from '../translations';
import {
CasesStepBaseConfigSchema,
CasesStepCaseIdSchema,
CasesStepSingleCaseOutputSchema,
} from './shared';

export const RemoveTagsStepTypeId = 'cases.removeTags';

const InputSchema = CasesStepCaseIdSchema.extend({
tags: CaseTags,
});

const OutputSchema = CasesStepSingleCaseOutputSchema;

type RemoveTagsStepInputSchema = typeof InputSchema;
type RemoveTagsStepOutputSchema = typeof OutputSchema;

export type RemoveTagsStepInput = z.infer<typeof InputSchema>;

export const removeTagsStepCommonDefinition: CommonStepDefinition<
RemoveTagsStepInputSchema,
RemoveTagsStepOutputSchema
> = {
id: RemoveTagsStepTypeId,
category: StepCategory.KibanaCases,
label: i18n.REMOVE_TAG_STEP_LABEL,
description: i18n.REMOVE_TAG_STEP_DESCRIPTION,
documentation: {
details: i18n.REMOVE_TAG_STEP_DOCUMENTATION_DETAILS,
examples: [
`## Remove case tags
\`\`\`yaml
- name: remove_case_tags
type: ${RemoveTagsStepTypeId}
with:
case_id: "abc-123-def-456"
tags: ["investigation", "high-priority"]
\`\`\``,
],
},
inputSchema: InputSchema,
outputSchema: OutputSchema,
configSchema: CasesStepBaseConfigSchema,
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { AddAlertsStepTypeId, addAlertsStepCommonDefinition } from './add_alerts
import { AddEventsStepTypeId, addEventsStepCommonDefinition } from './add_events';
import { AddObservablesStepTypeId, addObservablesStepCommonDefinition } from './add_observables';
import { AddTagsStepTypeId, addTagsStepCommonDefinition } from './add_tags';
import { RemoveTagsStepTypeId, removeTagsStepCommonDefinition } from './remove_tags';
import { AssignCaseStepTypeId, assignCaseStepCommonDefinition } from './assign_case';
import { CloseCaseStepTypeId, closeCaseStepCommonDefinition } from './close_case';
import { DeleteCasesStepTypeId, deleteCasesStepCommonDefinition } from './delete_cases';
Expand Down Expand Up @@ -149,6 +150,12 @@ const stepDefinitions = [
input: addTagInputFixture,
output: singleCaseOutput,
},
{
typeId: RemoveTagsStepTypeId,
definition: removeTagsStepCommonDefinition,
input: addTagInputFixture,
output: singleCaseOutput,
},
{
typeId: SetCategoryStepTypeId,
definition: setCategoryStepCommonDefinition,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,24 @@ export const ADD_TAG_STEP_DOCUMENTATION_DETAILS = i18n.translate(
}
);

export const REMOVE_TAG_STEP_LABEL = i18n.translate('xpack.cases.workflowSteps.removeTag.label', {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
export const REMOVE_TAG_STEP_LABEL = i18n.translate('xpack.cases.workflowSteps.removeTag.label', {
export const REMOVE_TAG_STEP_LABEL = i18n.translate('xpack.cases.workflowSteps.removeTags.label', {

same for below

defaultMessage: 'Cases - Remove tags from case',
});

export const REMOVE_TAG_STEP_DESCRIPTION = i18n.translate(
'xpack.cases.workflowSteps.removeTag.description',
{
defaultMessage: 'Remove tags from an existing case',
}
);

export const REMOVE_TAG_STEP_DOCUMENTATION_DETAILS = i18n.translate(
'xpack.cases.workflowSteps.removeTag.documentation.details',
{
defaultMessage: 'This step removes tags from an existing case.',
}
);

export const ADD_CATEGORY_STEP_LABEL = i18n.translate(
'xpack.cases.workflowSteps.addCategory.label',
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ export function registerCasesSteps(
import('./simple_steps').then((m) => m.addTagsStepDefinition)
);

workflowsExtensions.registerStepDefinition(() =>
import('./simple_steps').then((m) => m.removeTagsStepDefinition)
);

workflowsExtensions.registerStepDefinition(() =>
import('./set_category').then((m) => m.setCategoryStepDefinition)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
addEventsStepDefinition,
addObservablesStepDefinition,
addTagsStepDefinition,
removeTagsStepDefinition,
assignCaseStepDefinition,
closeCaseStepDefinition,
deleteCasesStepDefinition,
Expand Down Expand Up @@ -47,6 +48,7 @@ describe('new cases public step definitions', () => {
setTitleStepDefinition,
addObservablesStepDefinition,
addTagsStepDefinition,
removeTagsStepDefinition,
setCategoryStepDefinition,
getCasesByAlertIdStepDefinition,
getAllAttachmentsStepDefinition,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { addCommentStepCommonDefinition } from '../../common/workflows/steps/add
import { addEventsStepCommonDefinition } from '../../common/workflows/steps/add_events';
import { addObservablesStepCommonDefinition } from '../../common/workflows/steps/add_observables';
import { addTagsStepCommonDefinition } from '../../common/workflows/steps/add_tags';
import { removeTagsStepCommonDefinition } from '../../common/workflows/steps/remove_tags';
import { assignCaseStepCommonDefinition } from '../../common/workflows/steps/assign_case';
import { closeCaseStepCommonDefinition } from '../../common/workflows/steps/close_case';
import { deleteCasesStepCommonDefinition } from '../../common/workflows/steps/delete_cases';
Expand Down Expand Up @@ -50,6 +51,10 @@ export const addTagsStepDefinition = createPublicCaseStepDefinition({
...addTagsStepCommonDefinition,
});

export const removeTagsStepDefinition = createPublicCaseStepDefinition({
...removeTagsStepCommonDefinition,
});

export const assignCaseStepDefinition = createPublicCaseStepDefinition({
...assignCaseStepCommonDefinition,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { addEventsStepDefinition } from './steps/add_events';
import { findSimilarCasesStepDefinition } from './steps/find_similar_cases';
import { addObservablesStepDefinition } from './steps/add_observables';
import { addTagsStepDefinition } from './steps/add_tags';
import { removeTagsStepDefinition } from './steps/remove_tags';
import { getCasesByAlertIdStepDefinition } from './steps/get_cases_by_alert_id';
import { getAllAttachmentsStepDefinition } from './steps/get_all_attachments';
import { updateObservableStepDefinition } from './steps/update_observable';
Expand Down Expand Up @@ -68,6 +69,7 @@ export function registerCaseWorkflowSteps(
workflowsExtensions.registerStepDefinition(setTitleStepDefinition(getCasesClient));
workflowsExtensions.registerStepDefinition(addObservablesStepDefinition(getCasesClient));
workflowsExtensions.registerStepDefinition(addTagsStepDefinition(getCasesClient));
workflowsExtensions.registerStepDefinition(removeTagsStepDefinition(getCasesClient));
workflowsExtensions.registerStepDefinition(setCategoryStepDefinition(getCasesClient));
workflowsExtensions.registerStepDefinition(getCasesByAlertIdStepDefinition(getCasesClient));
workflowsExtensions.registerStepDefinition(getAllAttachmentsStepDefinition(getCasesClient));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { createCaseResponseFixture } from '../../../common/fixtures/create_case';
import type { CasesClient } from '../../client';
import { removeTagsStepDefinition } from './remove_tags';
import { createStepHandlerContext } from './test_utils';

const createContext = (input: unknown) =>
createStepHandlerContext({ input, stepType: 'cases.removeTags' });

describe('removeTagsStepDefinition', () => {
it('removes tags from a case', async () => {
const inputTags = ['triage', 'coke'];
const remainingTags = ['pepsi'];
const currentCase = { ...createCaseResponseFixture, tags: ['coke', 'pepsi', 'triage'] };
const get = jest.fn().mockResolvedValue(currentCase);
const bulkUpdate = jest
.fn()
.mockResolvedValue([{ ...createCaseResponseFixture, tags: remainingTags }]);
const getCasesClient = jest.fn().mockResolvedValue({
cases: { get, bulkUpdate },
} as unknown as CasesClient);
const definition = removeTagsStepDefinition(getCasesClient);

const result = await definition.handler(createContext({ case_id: 'case-1', tags: inputTags }));

expect(definition.id).toBe('cases.removeTags');
expect(get).toHaveBeenCalledWith({ id: 'case-1', includeComments: false });
expect(bulkUpdate).toHaveBeenCalledWith({
cases: [
expect.objectContaining({
id: 'case-1',
version: currentCase.version,
tags: remainingTags,
}),
],
});
expect(result).toEqual({
output: {
case: { ...createCaseResponseFixture, tags: remainingTags },
},
});
});

it('leaves tags unchanged when none of the input tags are present', async () => {
const currentCase = { ...createCaseResponseFixture, tags: ['coke', 'pepsi'] };
const get = jest.fn().mockResolvedValue(currentCase);
const bulkUpdate = jest.fn().mockResolvedValue([currentCase]);
const getCasesClient = jest.fn().mockResolvedValue({
cases: { get, bulkUpdate },
} as unknown as CasesClient);
const definition = removeTagsStepDefinition(getCasesClient);

await definition.handler(createContext({ case_id: 'case-1', tags: ['triage'] }));

expect(bulkUpdate).toHaveBeenCalledWith({
cases: [
expect.objectContaining({
id: 'case-1',
version: currentCase.version,
tags: ['coke', 'pepsi'],
}),
],
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import type { KibanaRequest } from '@kbn/core/server';
import { createServerStepDefinition } from '@kbn/workflows-extensions/server';
import {
removeTagsStepCommonDefinition,
type RemoveTagsStepInput,
} from '../../../common/workflows/steps/remove_tags';
import type { CasesClient } from '../../client';
import { createCasesStepHandler } from './utils';
import { updateSingleCaseFromInput } from './update_case_helpers';

export const removeTagsStepDefinition = (
getCasesClient: (request: KibanaRequest) => Promise<CasesClient>
) =>
createServerStepDefinition({
...removeTagsStepCommonDefinition,
handler: createCasesStepHandler(getCasesClient, async (client, input: RemoveTagsStepInput) => {
const currentCase = await client.cases.get({
id: input.case_id,
includeComments: false,
});

const tagsToRemove = new Set(input.tags);
const tags = (currentCase.tags ?? []).filter((tag) => !tagsToRemove.has(tag));

return updateSingleCaseFromInput(
client,
{ ...input, version: currentCase.version },
{ tags }
);
}),
});
Loading