From 2804c40fa7ce27b312bd375ee8917c0fdebdbb43 Mon Sep 17 00:00:00 2001 From: Jonathan Buttner Date: Wed, 11 Jan 2023 12:02:33 -0500 Subject: [PATCH] Expanding API response fields --- .../cases/common/api/connectors/index.ts | 6 +- .../__snapshots__/audit_logger.test.ts.snap | 84 +++ x-pack/plugins/cases/server/client/mocks.ts | 1 + .../server/client/user_actions/connectors.ts | 24 +- .../cases/server/client/user_actions/types.ts | 4 +- x-pack/plugins/cases/server/services/mocks.ts | 2 +- .../server/services/user_actions/index.ts | 12 +- .../common/lib/connectors.ts | 70 +-- .../tests/trial/internal/get_connectors.ts | 527 ++++++++++-------- 9 files changed, 458 insertions(+), 272 deletions(-) diff --git a/x-pack/plugins/cases/common/api/connectors/index.ts b/x-pack/plugins/cases/common/api/connectors/index.ts index 0dd0e00181c26..5331e8b543f8d 100644 --- a/x-pack/plugins/cases/common/api/connectors/index.ts +++ b/x-pack/plugins/cases/common/api/connectors/index.ts @@ -117,7 +117,11 @@ export const CaseConnectorRt = rt.intersection([ ]); export const GetCaseConnectorsResponseRt = rt.array( - rt.intersection([rt.type({ needsToBePushed: rt.boolean }), CaseConnectorRt]) + rt.intersection([ + rt.type({ needsToBePushed: rt.boolean, hasBeenPushed: rt.boolean }), + rt.partial(rt.type({ latestPushDate: rt.string }).props), + CaseConnectorRt, + ]) ); export type CaseUserActionConnector = rt.TypeOf; diff --git a/x-pack/plugins/cases/server/authorization/__snapshots__/audit_logger.test.ts.snap b/x-pack/plugins/cases/server/authorization/__snapshots__/audit_logger.test.ts.snap index bbeb9ce05445b..2d2e1f21a2c0d 100644 --- a/x-pack/plugins/cases/server/authorization/__snapshots__/audit_logger.test.ts.snap +++ b/x-pack/plugins/cases/server/authorization/__snapshots__/audit_logger.test.ts.snap @@ -1512,6 +1512,90 @@ Object { } `; +exports[`audit_logger log function event structure creates the correct audit event for operation: "getConnectors" with an error and entity 1`] = ` +Object { + "error": Object { + "code": "Error", + "message": "an error", + }, + "event": Object { + "action": "case_connectors_get", + "category": Array [ + "database", + ], + "outcome": "failure", + "type": Array [ + "access", + ], + }, + "kibana": Object { + "saved_object": Object { + "id": "1", + "type": "cases-user-actions", + }, + }, + "message": "Failed attempt to access cases-user-actions [id=1] as owner \\"awesome\\"", +} +`; + +exports[`audit_logger log function event structure creates the correct audit event for operation: "getConnectors" with an error but no entity 1`] = ` +Object { + "error": Object { + "code": "Error", + "message": "an error", + }, + "event": Object { + "action": "case_connectors_get", + "category": Array [ + "database", + ], + "outcome": "failure", + "type": Array [ + "access", + ], + }, + "message": "Failed attempt to access a user actions as any owners", +} +`; + +exports[`audit_logger log function event structure creates the correct audit event for operation: "getConnectors" without an error but with an entity 1`] = ` +Object { + "event": Object { + "action": "case_connectors_get", + "category": Array [ + "database", + ], + "outcome": "success", + "type": Array [ + "access", + ], + }, + "kibana": Object { + "saved_object": Object { + "id": "5", + "type": "cases-user-actions", + }, + }, + "message": "User has accessed cases-user-actions [id=5] as owner \\"super\\"", +} +`; + +exports[`audit_logger log function event structure creates the correct audit event for operation: "getConnectors" without an error or entity 1`] = ` +Object { + "event": Object { + "action": "case_connectors_get", + "category": Array [ + "database", + ], + "outcome": "success", + "type": Array [ + "access", + ], + }, + "message": "User has accessed a user actions as any owners", +} +`; + exports[`audit_logger log function event structure creates the correct audit event for operation: "getReporters" with an error and entity 1`] = ` Object { "error": Object { diff --git a/x-pack/plugins/cases/server/client/mocks.ts b/x-pack/plugins/cases/server/client/mocks.ts index 79e307e98f8f7..dc9b56134d383 100644 --- a/x-pack/plugins/cases/server/client/mocks.ts +++ b/x-pack/plugins/cases/server/client/mocks.ts @@ -82,6 +82,7 @@ type UserActionsSubClientMock = jest.Mocked; const createUserActionsSubClientMock = (): UserActionsSubClientMock => { return { getAll: jest.fn(), + getConnectors: jest.fn(), }; }; diff --git a/x-pack/plugins/cases/server/client/user_actions/connectors.ts b/x-pack/plugins/cases/server/client/user_actions/connectors.ts index e56782c3b3933..97057b2058592 100644 --- a/x-pack/plugins/cases/server/client/user_actions/connectors.ts +++ b/x-pack/plugins/cases/server/client/user_actions/connectors.ts @@ -59,6 +59,8 @@ export const getConnectors = async ( ...enrichedConnector.connector, name: enrichedConnector.name, needsToBePushed: hasDataToPush(enrichedConnector), + latestPushDate: enrichedConnector.pushInfo?.pushDate.toISOString(), + hasBeenPushed: hasBeenPushed(enrichedConnector), }); } @@ -105,6 +107,11 @@ const checkConnectorsAuthorization = async ({ }); }; +interface EnrichedPushInfo { + pushDate: Date; + connectorFieldsUsedInPush: CaseConnector; +} + interface EnrichedConnector { connector: CaseConnector; name: string; @@ -171,7 +178,7 @@ const getPushInfo = async ({ return new Map(); } - const priorToPushFields = await userActionService.getConnectorFieldsBeforePushes( + const priorToPushFields = await userActionService.getConnectorFieldsUsedInPushes( caseId, pushRequest ); @@ -184,7 +191,7 @@ const getPushInfo = async ({ if (connectorFields != null) { enrichedPushInfo.set(request.connectorId, { pushDate: request.date, - connectorFieldsBeforePush: connectorFields, + connectorFieldsUsedInPush: connectorFields, }); } } @@ -208,11 +215,6 @@ const isDateValid = (date: Date): boolean => { return !isNaN(date.getTime()); }; -interface EnrichedPushInfo { - pushDate: Date; - connectorFieldsBeforePush: CaseConnector; -} - const getConnectorInfoFromSavedObject = ( savedObject: SavedObject | undefined ): CaseConnector | undefined => { @@ -229,10 +231,14 @@ const hasDataToPush = (enrichedConnectorInfo: EnrichedConnector): boolean => { return ( !isEqual( enrichedConnectorInfo.connector, - enrichedConnectorInfo.pushInfo?.connectorFieldsBeforePush + enrichedConnectorInfo.pushInfo?.connectorFieldsUsedInPush ) || (enrichedConnectorInfo.pushInfo != null && enrichedConnectorInfo.latestUserActionDate != null && - enrichedConnectorInfo.pushInfo.pushDate < enrichedConnectorInfo.latestUserActionDate) + enrichedConnectorInfo.latestUserActionDate > enrichedConnectorInfo.pushInfo.pushDate) ); }; + +const hasBeenPushed = (enrichedConnectorInfo: EnrichedConnector): boolean => { + return enrichedConnectorInfo.pushInfo != null; +}; diff --git a/x-pack/plugins/cases/server/client/user_actions/types.ts b/x-pack/plugins/cases/server/client/user_actions/types.ts index 9fdbdc35876d5..a974c5136047c 100644 --- a/x-pack/plugins/cases/server/client/user_actions/types.ts +++ b/x-pack/plugins/cases/server/client/user_actions/types.ts @@ -15,6 +15,4 @@ export interface UserActionGet { caseId: string; } -export interface GetConnectorsRequest { - caseId: string; -} +export type GetConnectorsRequest = UserActionGet; diff --git a/x-pack/plugins/cases/server/services/mocks.ts b/x-pack/plugins/cases/server/services/mocks.ts index f743a2523c736..e50dee10323eb 100644 --- a/x-pack/plugins/cases/server/services/mocks.ts +++ b/x-pack/plugins/cases/server/services/mocks.ts @@ -94,7 +94,7 @@ type FakeUserActionService = PublicMethodsOf & { export const createUserActionServiceMock = (): CaseUserActionServiceMock => { const service: FakeUserActionService = { creator: createUserActionPersisterServiceMock(), - getConnectorFieldsBeforePushes: jest.fn(), + getConnectorFieldsUsedInPushes: jest.fn(), getMostRecentUserAction: jest.fn(), getCaseConnectorInformation: jest.fn(), getAll: jest.fn(), diff --git a/x-pack/plugins/cases/server/services/user_actions/index.ts b/x-pack/plugins/cases/server/services/user_actions/index.ts index 30392a328fe44..5b0a931ab7173 100644 --- a/x-pack/plugins/cases/server/services/user_actions/index.ts +++ b/x-pack/plugins/cases/server/services/user_actions/index.ts @@ -106,7 +106,7 @@ export class CaseUserActionService { return this._creator; } - public async getConnectorFieldsBeforePushes( + public async getConnectorFieldsUsedInPushes( caseId: string, pushes: PushInfo[] ): Promise { @@ -131,11 +131,11 @@ export class CaseUserActionService { page: 1, perPage: 1, sortField: defaultSortField, - aggs: CaseUserActionService.buildConnectorFieldsBeforePushAggs(pushes), + aggs: CaseUserActionService.buildConnectorFieldsUsedInPushAggs(pushes), filter: connectorsFilter, }); - return this.createCaseConnectorFieldsBeforePushes(response.aggregations); + return this.createCaseConnectorFieldsUsedInPushes(response.aggregations); } catch (error) { this.context.log.error( `Error while retrieving the connector fields before the last push: ${caseId}: ${error}` @@ -144,14 +144,14 @@ export class CaseUserActionService { } } - private static buildConnectorFieldsBeforePushAggs( + private static buildConnectorFieldsUsedInPushAggs( pushes: PushInfo[] ): Record { const filters: estypes.AggregationsBuckets = {}; /** * Group the user actions by the unique connector ids and bound the time range - * for that connector's push event + * for that connector's push event. We want to search for the fields before the push timestamp. */ for (const push of pushes) { filters[push.connectorId] = { @@ -231,7 +231,7 @@ export class CaseUserActionService { }; } - private createCaseConnectorFieldsBeforePushes( + private createCaseConnectorFieldsUsedInPushes( aggsResults?: ConnectorFieldsBeforePushAggsResult ): CaseConnectorFields { const connectorFields: CaseConnectorFields = new Map(); diff --git a/x-pack/test/cases_api_integration/common/lib/connectors.ts b/x-pack/test/cases_api_integration/common/lib/connectors.ts index 9b695d825f788..6189baed5f943 100644 --- a/x-pack/test/cases_api_integration/common/lib/connectors.ts +++ b/x-pack/test/cases_api_integration/common/lib/connectors.ts @@ -238,41 +238,43 @@ export const createCaseWithConnector = async ({ }); actionsRemover.add(auth?.space ?? 'default', connector.id, 'action', 'actions'); - const configuration = await createConfiguration( - supertest, - { - ...getConfigurationRequest({ - id: connector.id, - name: connector.name, - type: connector.connector_type_id as ConnectorTypes, - }), - ...configureReq, - }, - 200, - auth ?? undefined - ); - const postedCase = await createCase( - supertest, - { - ...createCaseReq, - connector: { - id: connector.id, - name: connector.name, - type: connector.connector_type_id, - fields: { - urgency: '2', - impact: '2', - severity: '2', - category: 'software', - subcategory: 'os', - }, - } as CaseConnector, - }, - 200, - auth, - headers - ); + const [configuration, postedCase] = await Promise.all([ + createConfiguration( + supertest, + { + ...getConfigurationRequest({ + id: connector.id, + name: connector.name, + type: connector.connector_type_id as ConnectorTypes, + }), + ...configureReq, + }, + 200, + auth ?? undefined + ), + createCase( + supertest, + { + ...createCaseReq, + connector: { + id: connector.id, + name: connector.name, + type: connector.connector_type_id, + fields: { + urgency: '2', + impact: '2', + severity: '2', + category: 'software', + subcategory: 'os', + }, + } as CaseConnector, + }, + 200, + auth, + headers + ), + ]); return { postedCase, connector, configuration }; }; diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/internal/get_connectors.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/internal/get_connectors.ts index c7de4b913d12d..ef9f39f1400d8 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/internal/get_connectors.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/internal/get_connectors.ts @@ -8,7 +8,7 @@ import http from 'http'; import expect from '@kbn/expect'; -import { CaseSeverity, ConnectorTypes } from '@kbn/cases-plugin/common/api'; +import { ActionTypes, CaseSeverity, ConnectorTypes } from '@kbn/cases-plugin/common/api'; import { globalRead, noKibanaPrivileges, @@ -26,6 +26,7 @@ import { createCase, createComment, deleteAllCaseItems, + getCaseUserActions, pushCase, updateCase, } from '../../../../common/lib/utils'; @@ -72,18 +73,19 @@ export default ({ getService }: FtrProviderContext): void => { }); it('retrieves multiple connectors', async () => { - const { postedCase, connector: serviceNowConnector } = await createCaseWithConnector({ - supertest, - serviceNowSimulatorURL, - actionsRemover, - }); - - const jiraConnector = await createConnector({ - supertest, - req: { - ...getJiraConnector(), - }, - }); + const [{ postedCase, connector: serviceNowConnector }, jiraConnector] = await Promise.all([ + createCaseWithConnector({ + supertest, + serviceNowSimulatorURL, + actionsRemover, + }), + createConnector({ + supertest, + req: { + ...getJiraConnector(), + }, + }), + ]); actionsRemover.add('default', jiraConnector.id, 'action', 'actions'); @@ -228,237 +230,174 @@ export default ({ getService }: FtrProviderContext): void => { }); describe('push', () => { - it('sets needs to push to true when a push has not occurred', async () => { - const { postedCase, connector } = await createCaseWithConnector({ - supertest, - serviceNowSimulatorURL, - actionsRemover, - }); - - const connectors = await getConnectors({ caseId: postedCase.id, supertest }); - - expect(connectors.length).to.be(1); - expect(connectors[0].id).to.be(connector.id); - expect(connectors[0].needsToBePushed).to.be(true); - }); + describe('latestPushDate', () => { + it('does not set latestPushDate when the connector has not been used to push', async () => { + const { postedCase } = await createCaseWithConnector({ + supertest, + serviceNowSimulatorURL, + actionsRemover, + }); - it('sets needs to push to false when a push has occurred', async () => { - const { postedCase, connector } = await createCaseWithConnector({ - supertest, - serviceNowSimulatorURL, - actionsRemover, - }); + const connectors = await getConnectors({ caseId: postedCase.id, supertest }); - await pushCase({ - supertest, - caseId: postedCase.id, - connectorId: connector.id, + expect(connectors.length).to.be(1); + expect(connectors[0].latestPushDate).to.be(undefined); }); - const connectors = await getConnectors({ caseId: postedCase.id, supertest }); + it('sets latestPushDate to the most recent push date', async () => { + const { postedCase, connector } = await createCaseWithConnector({ + supertest, + serviceNowSimulatorURL, + actionsRemover, + }); - expect(connectors.length).to.be(1); - expect(connectors[0].id).to.be(connector.id); - expect(connectors[0].needsToBePushed).to.be(false); - }); + await pushCase({ + supertest, + caseId: postedCase.id, + connectorId: connector.id, + }); - it('sets needs to push to true when a comment was created after the last push', async () => { - const { postedCase, connector } = await createCaseWithConnector({ - supertest, - serviceNowSimulatorURL, - actionsRemover, - }); + await createComment({ + supertest, + caseId: postedCase.id, + params: postCommentUserReq, + }); - await pushCase({ - supertest, - caseId: postedCase.id, - connectorId: connector.id, - }); + await pushCase({ + supertest, + caseId: postedCase.id, + connectorId: connector.id, + }); - await createComment({ - supertest, - caseId: postedCase.id, - params: postCommentUserReq, - }); + const [userActions, connectors] = await Promise.all([ + getCaseUserActions({ supertest, caseID: postedCase.id }), + getConnectors({ caseId: postedCase.id, supertest }), + ]); - const connectors = await getConnectors({ caseId: postedCase.id, supertest }); + const pushes = userActions.filter((ua) => ua.type === ActionTypes.pushed); + const latestPush = pushes[pushes.length - 1]; - expect(connectors.length).to.be(1); - expect(connectors[0].id).to.be(connector.id); - expect(connectors[0].needsToBePushed).to.be(true); + expect(connectors.length).to.be(1); + expect(connectors[0].latestPushDate).to.eql(latestPush.created_at); + }); }); - it('sets needs to push to false when the severity of a case was changed after the last push', async () => { - const { postedCase, connector } = await createCaseWithConnector({ - supertest, - serviceNowSimulatorURL, - actionsRemover, - }); + describe('hasBeenPushed', () => { + it('sets hasBeenPushed to false when the connector has not been used to push', async () => { + const { postedCase } = await createCaseWithConnector({ + supertest, + serviceNowSimulatorURL, + actionsRemover, + }); - const pushedCase = await pushCase({ - supertest, - caseId: postedCase.id, - connectorId: connector.id, - }); + const connectors = await getConnectors({ caseId: postedCase.id, supertest }); - await updateCase({ - supertest, - params: { - cases: [ - { - id: pushedCase.id, - version: pushedCase.version, - severity: CaseSeverity.CRITICAL, - }, - ], - }, + expect(connectors.length).to.be(1); + expect(connectors[0].hasBeenPushed).to.be(false); }); - const connectors = await getConnectors({ caseId: postedCase.id, supertest }); + it('sets hasBeenPushed to true when the connector was used to push', async () => { + const { postedCase, connector } = await createCaseWithConnector({ + supertest, + serviceNowSimulatorURL, + actionsRemover, + }); - expect(connectors.length).to.be(1); - expect(connectors[0].id).to.be(connector.id); - expect(connectors[0].needsToBePushed).to.be(false); - }); + await pushCase({ + supertest, + caseId: postedCase.id, + connectorId: connector.id, + }); - it('sets needs to push to false the service now connector and true for jira', async () => { - const { postedCase, connector: serviceNowConnector } = await createCaseWithConnector({ - supertest, - serviceNowSimulatorURL, - actionsRemover, - }); + const connectors = await getConnectors({ caseId: postedCase.id, supertest }); - const pushedCase = await pushCase({ - supertest, - caseId: postedCase.id, - connectorId: serviceNowConnector.id, + expect(connectors.length).to.be(1); + expect(connectors[0].hasBeenPushed).to.be(true); }); + }); - const jiraConnector = await createConnector({ - supertest, - req: { - ...getJiraConnector(), - }, - }); + describe('needsToBePushed', () => { + it('sets needs to push to true when a push has not occurred', async () => { + const { postedCase, connector } = await createCaseWithConnector({ + supertest, + serviceNowSimulatorURL, + actionsRemover, + }); - actionsRemover.add('default', jiraConnector.id, 'action', 'actions'); + const connectors = await getConnectors({ caseId: postedCase.id, supertest }); - await updateCase({ - supertest, - params: { - cases: [ - { - id: pushedCase.id, - version: pushedCase.version, - connector: { - id: jiraConnector.id, - name: 'Jira', - type: ConnectorTypes.jira, - fields: { issueType: 'Task', priority: null, parent: null }, - }, - }, - ], - }, + expect(connectors.length).to.be(1); + expect(connectors[0].id).to.be(connector.id); + expect(connectors[0].needsToBePushed).to.be(true); }); - const connectors = await getConnectors({ caseId: postedCase.id, supertest }); + it('sets needs to push to false when a push has occurred', async () => { + const { postedCase, connector } = await createCaseWithConnector({ + supertest, + serviceNowSimulatorURL, + actionsRemover, + }); - expect(connectors.length).to.be(2); - expect(connectors[0].id).to.be(serviceNowConnector.id); - expect(connectors[0].needsToBePushed).to.be(false); - expect(connectors[1].id).to.be(jiraConnector.id); - expect(connectors[1].needsToBePushed).to.be(true); - }); + await pushCase({ + supertest, + caseId: postedCase.id, + connectorId: connector.id, + }); - describe('changing connector fields', () => { - it('sets needs to push to false when the latest connector fields matches those used in the push', async () => { - const postedCase = await createCase(supertest, getPostCaseRequest()); + const connectors = await getConnectors({ caseId: postedCase.id, supertest }); - const serviceNowConnector = await createConnector({ + expect(connectors.length).to.be(1); + expect(connectors[0].id).to.be(connector.id); + expect(connectors[0].needsToBePushed).to.be(false); + }); + + it('sets needs to push to true when a comment was created after the last push', async () => { + const { postedCase, connector } = await createCaseWithConnector({ supertest, - req: { - ...getServiceNowConnector(), - config: { apiUrl: serviceNowSimulatorURL }, - }, + serviceNowSimulatorURL, + actionsRemover, }); - actionsRemover.add('default', serviceNowConnector.id, 'action', 'actions'); + await pushCase({ + supertest, + caseId: postedCase.id, + connectorId: connector.id, + }); - const updatedCasesServiceNow = await updateCase({ + await createComment({ supertest, - params: { - cases: [ - { - id: postedCase.id, - version: postedCase.version, - connector: { - id: serviceNowConnector.id, - name: 'SN', - type: ConnectorTypes.serviceNowITSM, - fields: { - urgency: '2', - impact: '2', - severity: '2', - category: 'software', - subcategory: 'os', - }, - }, - }, - ], - }, + caseId: postedCase.id, + params: postCommentUserReq, }); - const pushedCase = await pushCase({ + const connectors = await getConnectors({ caseId: postedCase.id, supertest }); + + expect(connectors.length).to.be(1); + expect(connectors[0].id).to.be(connector.id); + expect(connectors[0].needsToBePushed).to.be(true); + }); + + it('sets needs to push to false when the severity of a case was changed after the last push', async () => { + const { postedCase, connector } = await createCaseWithConnector({ supertest, - caseId: updatedCasesServiceNow[0].id, - connectorId: serviceNowConnector.id, + serviceNowSimulatorURL, + actionsRemover, }); - // switch urgency to 3 - const updatedCases = await updateCase({ + const pushedCase = await pushCase({ supertest, - params: { - cases: [ - { - id: pushedCase.id, - version: pushedCase.version, - connector: { - id: serviceNowConnector.id, - name: 'SN', - type: ConnectorTypes.serviceNowITSM, - fields: { - urgency: '3', - impact: '2', - severity: '2', - category: 'software', - subcategory: 'os', - }, - }, - }, - ], - }, + caseId: postedCase.id, + connectorId: connector.id, }); - // switch urgency back to 2 await updateCase({ supertest, params: { cases: [ { - id: updatedCases[0].id, - version: updatedCases[0].version, - connector: { - id: serviceNowConnector.id, - name: 'SN', - type: ConnectorTypes.serviceNowITSM, - fields: { - urgency: '2', - impact: '2', - severity: '2', - category: 'software', - subcategory: 'os', - }, - }, + id: pushedCase.id, + version: pushedCase.version, + severity: CaseSeverity.CRITICAL, }, ], }, @@ -467,24 +406,33 @@ export default ({ getService }: FtrProviderContext): void => { const connectors = await getConnectors({ caseId: postedCase.id, supertest }); expect(connectors.length).to.be(1); - expect(connectors[0].id).to.be(serviceNowConnector.id); + expect(connectors[0].id).to.be(connector.id); expect(connectors[0].needsToBePushed).to.be(false); }); - it('sets needs to push to true when the latest connector fields do not match those used in the push', async () => { + it('sets needs to push to false the service now connector and true for jira', async () => { const { postedCase, connector: serviceNowConnector } = await createCaseWithConnector({ supertest, serviceNowSimulatorURL, actionsRemover, }); - const pushedCase = await pushCase({ - supertest, - caseId: postedCase.id, - connectorId: serviceNowConnector.id, - }); + const [pushedCase, jiraConnector] = await Promise.all([ + pushCase({ + supertest, + caseId: postedCase.id, + connectorId: serviceNowConnector.id, + }), + createConnector({ + supertest, + req: { + ...getJiraConnector(), + }, + }), + ]); + + actionsRemover.add('default', jiraConnector.id, 'action', 'actions'); - // switch urgency to 3 await updateCase({ supertest, params: { @@ -493,16 +441,10 @@ export default ({ getService }: FtrProviderContext): void => { id: pushedCase.id, version: pushedCase.version, connector: { - id: serviceNowConnector.id, - name: 'SN', - type: ConnectorTypes.serviceNowITSM, - fields: { - urgency: '3', - impact: '2', - severity: '2', - category: 'software', - subcategory: 'os', - }, + id: jiraConnector.id, + name: 'Jira', + type: ConnectorTypes.jira, + fields: { issueType: 'Task', priority: null, parent: null }, }, }, ], @@ -511,9 +453,158 @@ export default ({ getService }: FtrProviderContext): void => { const connectors = await getConnectors({ caseId: postedCase.id, supertest }); - expect(connectors.length).to.be(1); + expect(connectors.length).to.be(2); expect(connectors[0].id).to.be(serviceNowConnector.id); - expect(connectors[0].needsToBePushed).to.be(true); + expect(connectors[0].needsToBePushed).to.be(false); + expect(connectors[1].id).to.be(jiraConnector.id); + expect(connectors[1].needsToBePushed).to.be(true); + }); + + describe('changing connector fields', () => { + it('sets needs to push to false when the latest connector fields matches those used in the push', async () => { + const postedCase = await createCase(supertest, getPostCaseRequest()); + + const serviceNowConnector = await createConnector({ + supertest, + req: { + ...getServiceNowConnector(), + config: { apiUrl: serviceNowSimulatorURL }, + }, + }); + + actionsRemover.add('default', serviceNowConnector.id, 'action', 'actions'); + + const updatedCasesServiceNow = await updateCase({ + supertest, + params: { + cases: [ + { + id: postedCase.id, + version: postedCase.version, + connector: { + id: serviceNowConnector.id, + name: 'SN', + type: ConnectorTypes.serviceNowITSM, + fields: { + urgency: '2', + impact: '2', + severity: '2', + category: 'software', + subcategory: 'os', + }, + }, + }, + ], + }, + }); + + const pushedCase = await pushCase({ + supertest, + caseId: updatedCasesServiceNow[0].id, + connectorId: serviceNowConnector.id, + }); + + // switch urgency to 3 + const updatedCases = await updateCase({ + supertest, + params: { + cases: [ + { + id: pushedCase.id, + version: pushedCase.version, + connector: { + id: serviceNowConnector.id, + name: 'SN', + type: ConnectorTypes.serviceNowITSM, + fields: { + urgency: '3', + impact: '2', + severity: '2', + category: 'software', + subcategory: 'os', + }, + }, + }, + ], + }, + }); + + // switch urgency back to 2 + await updateCase({ + supertest, + params: { + cases: [ + { + id: updatedCases[0].id, + version: updatedCases[0].version, + connector: { + id: serviceNowConnector.id, + name: 'SN', + type: ConnectorTypes.serviceNowITSM, + fields: { + urgency: '2', + impact: '2', + severity: '2', + category: 'software', + subcategory: 'os', + }, + }, + }, + ], + }, + }); + + const connectors = await getConnectors({ caseId: postedCase.id, supertest }); + + expect(connectors.length).to.be(1); + expect(connectors[0].id).to.be(serviceNowConnector.id); + expect(connectors[0].needsToBePushed).to.be(false); + }); + + it('sets needs to push to true when the latest connector fields do not match those used in the push', async () => { + const { postedCase, connector: serviceNowConnector } = await createCaseWithConnector({ + supertest, + serviceNowSimulatorURL, + actionsRemover, + }); + + const pushedCase = await pushCase({ + supertest, + caseId: postedCase.id, + connectorId: serviceNowConnector.id, + }); + + // switch urgency to 3 + await updateCase({ + supertest, + params: { + cases: [ + { + id: pushedCase.id, + version: pushedCase.version, + connector: { + id: serviceNowConnector.id, + name: 'SN', + type: ConnectorTypes.serviceNowITSM, + fields: { + urgency: '3', + impact: '2', + severity: '2', + category: 'software', + subcategory: 'os', + }, + }, + }, + ], + }, + }); + + const connectors = await getConnectors({ caseId: postedCase.id, supertest }); + + expect(connectors.length).to.be(1); + expect(connectors[0].id).to.be(serviceNowConnector.id); + expect(connectors[0].needsToBePushed).to.be(true); + }); }); }); });