diff --git a/x-pack/plugins/case/server/client/cases/push.ts b/x-pack/plugins/case/server/client/cases/push.ts index 352328ed1dd40d..80dcc7a0e018c3 100644 --- a/x-pack/plugins/case/server/client/cases/push.ts +++ b/x-pack/plugins/case/server/client/cases/push.ts @@ -11,6 +11,8 @@ import { SavedObjectsClientContract, SavedObjectsUpdateResponse, Logger, + SavedObjectsFindResponse, + SavedObject, } from 'kibana/server'; import { ActionResult, ActionsClient } from '../../../../actions/server'; import { flattenCaseSavedObject, getAlertIndicesAndIDs } from '../../routes/api/utils'; @@ -25,6 +27,8 @@ import { CommentAttributes, CaseUserActionsResponse, User, + ESCasesConfigureAttributes, + CaseType, } from '../../../common/api'; import { buildCaseUserActionItem } from '../../services/user_actions/helpers'; @@ -37,6 +41,22 @@ import { import { CaseClientHandler } from '../client'; import { createCaseError } from '../../common/error'; +/** + * Returns true if the case should be closed based on the configuration settings and whether the case + * is a collection. Collections are not closable because we aren't allowing their status to be changed. + * In the future we could allow push to close all the sub cases of a collection but that's not currently supported. + */ +function shouldCloseByPush( + configureSettings: SavedObjectsFindResponse, + caseInfo: SavedObject +): boolean { + return ( + configureSettings.total > 0 && + configureSettings.saved_objects[0].attributes.closure_type === 'close-by-pushing' && + caseInfo.attributes.type !== CaseType.collection + ); +} + interface PushParams { savedObjectsClient: SavedObjectsClientContract; caseService: CaseServiceSetup; @@ -190,14 +210,15 @@ export const push = async ({ let updatedCase: SavedObjectsUpdateResponse; let updatedComments: SavedObjectsBulkUpdateResponse; + const shouldMarkAsClosed = shouldCloseByPush(myCaseConfigure, myCase); + try { [updatedCase, updatedComments] = await Promise.all([ caseService.patchCase({ client: savedObjectsClient, caseId, updatedAttributes: { - ...(myCaseConfigure.total > 0 && - myCaseConfigure.saved_objects[0].attributes.closure_type === 'close-by-pushing' + ...(shouldMarkAsClosed ? { status: CaseStatuses.closed, closed_at: pushedDate, @@ -228,8 +249,7 @@ export const push = async ({ userActionService.postUserActions({ client: savedObjectsClient, actions: [ - ...(myCaseConfigure.total > 0 && - myCaseConfigure.saved_objects[0].attributes.closure_type === 'close-by-pushing' + ...(shouldMarkAsClosed ? [ buildCaseUserActionItem({ action: 'update', diff --git a/x-pack/plugins/security_solution/public/cases/components/configure_cases/closure_options.tsx b/x-pack/plugins/security_solution/public/cases/components/configure_cases/closure_options.tsx index 9417877e58f759..ba892116320ce2 100644 --- a/x-pack/plugins/security_solution/public/cases/components/configure_cases/closure_options.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/configure_cases/closure_options.tsx @@ -27,7 +27,12 @@ const ClosureOptionsComponent: React.FC = ({ {i18n.CASE_CLOSURE_OPTIONS_TITLE}} - description={i18n.CASE_CLOSURE_OPTIONS_DESC} + description={ + <> +

{i18n.CASE_CLOSURE_OPTIONS_DESC}

+

{i18n.CASE_COLSURE_OPTIONS_SUB_CASES}

+ + } data-test-subj="case-closure-options-form-group" > { @@ -228,6 +234,59 @@ export default ({ getService }: FtrProviderContext): void => { expect(body.status).to.eql('closed'); }); + it('should push a collection case but not close it when closure_type: close-by-pushing', async () => { + const { body: connector } = await supertest + .post('/api/actions/action') + .set('kbn-xsrf', 'true') + .send({ + ...getServiceNowConnector(), + config: { apiUrl: servicenowSimulatorURL }, + }) + .expect(200); + + actionsRemover.add('default', connector.id, 'action', 'actions'); + await supertest + .post(CASE_CONFIGURE_URL) + .set('kbn-xsrf', 'true') + .send({ + ...getConfiguration({ + id: connector.id, + name: connector.name, + type: connector.actionTypeId, + }), + closure_type: 'close-by-pushing', + }) + .expect(200); + + const { body: postedCase } = await supertest + .post(CASES_URL) + .set('kbn-xsrf', 'true') + .send({ + ...postCollectionReq, + connector: getConfiguration({ + id: connector.id, + name: connector.name, + type: connector.actionTypeId, + fields: { + urgency: '2', + impact: '2', + severity: '2', + category: 'software', + subcategory: 'os', + }, + }).connector, + }) + .expect(200); + + const { body } = await supertest + .post(`${CASES_URL}/${postedCase.id}/connector/${connector.id}/_push`) + .set('kbn-xsrf', 'true') + .send({}) + .expect(200); + + expect(body.status).to.eql(CaseStatuses.open); + }); + it('unhappy path - 404s when case does not exist', async () => { await supertest .post(`${CASES_URL}/fake-id/connector/fake-connector/_push`)