diff --git a/x-pack/plugins/cases/server/client/cases/types.ts b/x-pack/plugins/cases/server/client/cases/types.ts index c14cc66210614..6d56fa28dca59 100644 --- a/x-pack/plugins/cases/server/client/cases/types.ts +++ b/x-pack/plugins/cases/server/client/cases/types.ts @@ -17,7 +17,7 @@ import { PushToServiceApiParamsITSM as ServiceNowITSMPushToServiceApiParams, PushToServiceApiParamsSIR as ServiceNowSIRPushToServiceApiParams, ServiceNowITSMIncident, -} from '@kbn/stack-connectors-plugin/server/connector_types/cases/servicenow/types'; +} from '@kbn/stack-connectors-plugin/server/connector_types/lib/servicenow/types'; import { UserProfile } from '@kbn/security-plugin/common'; import { CaseResponse, ConnectorMappingsAttributes } from '../../../common/api'; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/index.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/index.ts index 22f32cf636036..d7a42c3350c82 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/cases/index.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/index.ts @@ -8,10 +8,6 @@ export { getCasesWebhookConnectorType } from './cases_webhook'; export { getJiraConnectorType } from './jira'; export { getResilientConnectorType } from './resilient'; -export { - getServiceNowITSMConnectorType, - getServiceNowSIRConnectorType, - getServiceNowITOMConnectorType, -} from './servicenow'; +export { getServiceNowITSMConnectorType } from './servicenow_itsm'; +export { getServiceNowSIRConnectorType } from './servicenow_sir'; export { getSwimlaneConnectorType } from './swimlane'; -export { getXmattersConnectorType } from './xmatters'; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow.tsx deleted file mode 100644 index 932d244e852f8..0000000000000 --- a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow.tsx +++ /dev/null @@ -1,169 +0,0 @@ -/* - * 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 { lazy } from 'react'; -import { i18n } from '@kbn/i18n'; -import type { - ActionTypeModel as ConnectorTypeModel, - GenericValidationResult, -} from '@kbn/triggers-actions-ui-plugin/public'; -import { - ServiceNowConfig, - ServiceNowITOMActionParams, - ServiceNowITSMActionParams, - ServiceNowSecrets, - ServiceNowSIRActionParams, -} from './types'; -import { getConnectorDescriptiveTitle, getSelectedConnectorIcon } from './helpers'; - -export const SERVICENOW_ITOM_TITLE = i18n.translate( - 'xpack.stackConnectors.components.serviceNowITOM.connectorTypeTitle', - { - defaultMessage: 'ServiceNow ITOM', - } -); - -export const SERVICENOW_ITOM_DESC = i18n.translate( - 'xpack.stackConnectors.components.serviceNowITOM.selectMessageText', - { - defaultMessage: 'Create an event in ServiceNow ITOM.', - } -); - -export const SERVICENOW_ITSM_DESC = i18n.translate( - 'xpack.stackConnectors.components.serviceNowITSM.selectMessageText', - { - defaultMessage: 'Create an incident in ServiceNow ITSM.', - } -); - -export const SERVICENOW_SIR_DESC = i18n.translate( - 'xpack.stackConnectors.components.serviceNowSIR.selectMessageText', - { - defaultMessage: 'Create an incident in ServiceNow SecOps.', - } -); - -export const SERVICENOW_ITSM_TITLE = i18n.translate( - 'xpack.stackConnectors.components.serviceNowITSM.connectorTypeTitle', - { - defaultMessage: 'ServiceNow ITSM', - } -); - -export const SERVICENOW_SIR_TITLE = i18n.translate( - 'xpack.stackConnectors.components.serviceNowSIR.connectorTypeTitle', - { - defaultMessage: 'ServiceNow SecOps', - } -); - -export function getServiceNowITSMConnectorType(): ConnectorTypeModel< - ServiceNowConfig, - ServiceNowSecrets, - ServiceNowITSMActionParams -> { - return { - id: '.servicenow', - iconClass: lazy(() => import('./logo')), - selectMessage: SERVICENOW_ITSM_DESC, - actionTypeTitle: SERVICENOW_ITSM_TITLE, - actionConnectorFields: lazy(() => import('./servicenow_connectors')), - validateParams: async ( - actionParams: ServiceNowITSMActionParams - ): Promise> => { - const translations = await import('./translations'); - const errors = { - 'subActionParams.incident.short_description': new Array(), - }; - const validationResult = { - errors, - }; - if ( - actionParams.subActionParams && - actionParams.subActionParams.incident && - !actionParams.subActionParams.incident.short_description?.length - ) { - errors['subActionParams.incident.short_description'].push(translations.TITLE_REQUIRED); - } - return validationResult; - }, - actionParamsFields: lazy(() => import('./servicenow_itsm_params')), - customConnectorSelectItem: { - getText: getConnectorDescriptiveTitle, - getComponent: getSelectedConnectorIcon, - }, - }; -} - -export function getServiceNowSIRConnectorType(): ConnectorTypeModel< - ServiceNowConfig, - ServiceNowSecrets, - ServiceNowSIRActionParams -> { - return { - id: '.servicenow-sir', - iconClass: lazy(() => import('./logo')), - selectMessage: SERVICENOW_SIR_DESC, - actionTypeTitle: SERVICENOW_SIR_TITLE, - actionConnectorFields: lazy(() => import('./servicenow_connectors')), - validateParams: async ( - actionParams: ServiceNowSIRActionParams - ): Promise> => { - const translations = await import('./translations'); - const errors = { - 'subActionParams.incident.short_description': new Array(), - }; - const validationResult = { - errors, - }; - if ( - actionParams.subActionParams && - actionParams.subActionParams.incident && - !actionParams.subActionParams.incident.short_description?.length - ) { - errors['subActionParams.incident.short_description'].push(translations.TITLE_REQUIRED); - } - return validationResult; - }, - actionParamsFields: lazy(() => import('./servicenow_sir_params')), - customConnectorSelectItem: { - getText: getConnectorDescriptiveTitle, - getComponent: getSelectedConnectorIcon, - }, - }; -} - -export function getServiceNowITOMConnectorType(): ConnectorTypeModel< - ServiceNowConfig, - ServiceNowSecrets, - ServiceNowITOMActionParams -> { - return { - id: '.servicenow-itom', - iconClass: lazy(() => import('./logo')), - selectMessage: SERVICENOW_ITOM_DESC, - actionTypeTitle: SERVICENOW_ITOM_TITLE, - actionConnectorFields: lazy(() => import('./servicenow_connectors_no_app')), - validateParams: async ( - actionParams: ServiceNowITOMActionParams - ): Promise> => { - const translations = await import('./translations'); - const errors = { - severity: new Array(), - }; - const validationResult = { errors }; - - if (actionParams?.subActionParams?.severity == null) { - errors.severity.push(translations.SEVERITY_REQUIRED); - } - - return validationResult; - }, - actionParamsFields: lazy(() => import('./servicenow_itom_params')), - }; -} diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/index.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_itsm/index.ts similarity index 65% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/index.ts rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_itsm/index.ts index 553cf2edde846..703987149c50f 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/index.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_itsm/index.ts @@ -5,8 +5,4 @@ * 2.0. */ -export { - getServiceNowITSMConnectorType, - getServiceNowSIRConnectorType, - getServiceNowITOMConnectorType, -} from './servicenow'; +export { getServiceNowITSMConnectorType } from './servicenow_itsm'; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/logo.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_itsm/logo.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/logo.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_itsm/logo.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_itsm/servicenow_itsm.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_itsm/servicenow_itsm.test.tsx new file mode 100644 index 0000000000000..fe70d5f06046b --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_itsm/servicenow_itsm.test.tsx @@ -0,0 +1,52 @@ +/* + * 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 { TypeRegistry } from '@kbn/triggers-actions-ui-plugin/public/application/type_registry'; +import { registerConnectorTypes } from '../..'; +import type { ActionTypeModel as ConnectorTypeModel } from '@kbn/triggers-actions-ui-plugin/public/types'; +import { registrationServicesMock } from '../../../mocks'; + +const SERVICENOW_ITSM_CONNECTOR_TYPE_ID = '.servicenow'; +let connectorTypeRegistry: TypeRegistry; + +beforeAll(() => { + connectorTypeRegistry = new TypeRegistry(); + registerConnectorTypes({ connectorTypeRegistry, services: registrationServicesMock }); +}); + +describe('connectorTypeRegistry.get() works', () => { + test(`${SERVICENOW_ITSM_CONNECTOR_TYPE_ID}: connector type static data is as expected`, () => { + const connectorTypeModel = connectorTypeRegistry.get(SERVICENOW_ITSM_CONNECTOR_TYPE_ID); + expect(connectorTypeModel.id).toEqual(SERVICENOW_ITSM_CONNECTOR_TYPE_ID); + }); +}); + +describe('servicenow action params validation', () => { + test(`${SERVICENOW_ITSM_CONNECTOR_TYPE_ID}: action params validation succeeds when action params is valid`, async () => { + const connectorTypeModel = connectorTypeRegistry.get(SERVICENOW_ITSM_CONNECTOR_TYPE_ID); + const actionParams = { + subActionParams: { incident: { short_description: 'some title {{test}}' }, comments: [] }, + }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { ['subActionParams.incident.short_description']: [] }, + }); + }); + + test(`${SERVICENOW_ITSM_CONNECTOR_TYPE_ID}: params validation fails when short_description is not valid`, async () => { + const connectorTypeModel = connectorTypeRegistry.get(SERVICENOW_ITSM_CONNECTOR_TYPE_ID); + const actionParams = { + subActionParams: { incident: { short_description: '' }, comments: [] }, + }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { + ['subActionParams.incident.short_description']: ['Short description is required.'], + }, + }); + }); +}); diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_itsm/servicenow_itsm.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_itsm/servicenow_itsm.tsx new file mode 100644 index 0000000000000..67c689f971e6e --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_itsm/servicenow_itsm.tsx @@ -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 { lazy } from 'react'; +import { i18n } from '@kbn/i18n'; +import type { + ActionTypeModel as ConnectorTypeModel, + GenericValidationResult, +} from '@kbn/triggers-actions-ui-plugin/public'; +import { ServiceNowConfig, ServiceNowSecrets } from '../../lib/servicenow/types'; +import { ServiceNowITSMActionParams } from './types'; +import { + getConnectorDescriptiveTitle, + getSelectedConnectorIcon, +} from '../../lib/servicenow/helpers'; + +export const SERVICENOW_ITSM_DESC = i18n.translate( + 'xpack.stackConnectors.components.serviceNowITSM.selectMessageText', + { + defaultMessage: 'Create an incident in ServiceNow ITSM.', + } +); + +export const SERVICENOW_ITSM_TITLE = i18n.translate( + 'xpack.stackConnectors.components.serviceNowITSM.connectorTypeTitle', + { + defaultMessage: 'ServiceNow ITSM', + } +); + +export function getServiceNowITSMConnectorType(): ConnectorTypeModel< + ServiceNowConfig, + ServiceNowSecrets, + ServiceNowITSMActionParams +> { + return { + id: '.servicenow', + iconClass: lazy(() => import('./logo')), + selectMessage: SERVICENOW_ITSM_DESC, + actionTypeTitle: SERVICENOW_ITSM_TITLE, + actionConnectorFields: lazy(() => import('../../lib/servicenow/servicenow_connectors')), + validateParams: async ( + actionParams: ServiceNowITSMActionParams + ): Promise> => { + const translations = await import('../../lib/servicenow/translations'); + const errors = { + 'subActionParams.incident.short_description': new Array(), + }; + const validationResult = { + errors, + }; + if ( + actionParams.subActionParams && + actionParams.subActionParams.incident && + !actionParams.subActionParams.incident.short_description?.length + ) { + errors['subActionParams.incident.short_description'].push(translations.TITLE_REQUIRED); + } + return validationResult; + }, + actionParamsFields: lazy(() => import('./servicenow_itsm_params')), + customConnectorSelectItem: { + getText: getConnectorDescriptiveTitle, + getComponent: getSelectedConnectorIcon, + }, + }; +} diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_itsm_params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_itsm/servicenow_itsm_params.test.tsx similarity index 98% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_itsm_params.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_itsm/servicenow_itsm_params.test.tsx index aa6cb6c71278d..39157f1a25f7a 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_itsm_params.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_itsm/servicenow_itsm_params.test.tsx @@ -10,12 +10,12 @@ import { mountWithIntl } from '@kbn/test-jest-helpers'; import { act } from '@testing-library/react'; import { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public/types'; -import { useGetChoices } from './use_get_choices'; +import { useGetChoices } from '../../lib/servicenow/use_get_choices'; import ServiceNowITSMParamsFields from './servicenow_itsm_params'; -import { Choice } from './types'; +import { Choice } from '../../lib/servicenow/types'; import { merge } from 'lodash'; -jest.mock('./use_get_choices'); +jest.mock('../../lib/servicenow/use_get_choices'); jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'); const useGetChoicesMock = useGetChoices as jest.Mock; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_itsm_params.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_itsm/servicenow_itsm_params.tsx similarity index 96% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_itsm_params.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_itsm/servicenow_itsm_params.tsx index a585ee48864e8..56afb35086a64 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_itsm_params.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_itsm/servicenow_itsm_params.tsx @@ -22,11 +22,12 @@ import { TextFieldWithMessageVariables, useKibana, } from '@kbn/triggers-actions-ui-plugin/public'; -import { ServiceNowITSMActionParams, Choice, Fields } from './types'; -import { useGetChoices } from './use_get_choices'; -import { choicesToEuiOptions, DEFAULT_CORRELATION_ID } from './helpers'; +import { Choice, Fields } from '../../lib/servicenow/types'; +import { ServiceNowITSMActionParams } from './types'; +import { useGetChoices } from '../../lib/servicenow/use_get_choices'; +import { choicesToEuiOptions, DEFAULT_CORRELATION_ID } from '../../lib/servicenow/helpers'; -import * as i18n from './translations'; +import * as i18n from '../../lib/servicenow/translations'; const useGetChoicesFields = ['urgency', 'severity', 'impact', 'category', 'subcategory']; const defaultFields: Fields = { diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_itsm/types.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_itsm/types.ts new file mode 100644 index 0000000000000..6c3b558f22a04 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_itsm/types.ts @@ -0,0 +1,13 @@ +/* + * 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 { ExecutorSubActionPushParamsITSM } from '../../../../server/connector_types/lib/servicenow/types'; + +export interface ServiceNowITSMActionParams { + subAction: string; + subActionParams: ExecutorSubActionPushParamsITSM; +} diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_sir/index.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_sir/index.ts new file mode 100644 index 0000000000000..ec1076d47219c --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_sir/index.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export { getServiceNowSIRConnectorType } from './servicenow_sir'; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_sir/logo.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_sir/logo.tsx new file mode 100644 index 0000000000000..f97b07247569d --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_sir/logo.tsx @@ -0,0 +1,34 @@ +/* + * 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 React from 'react'; +import { LogoProps } from '../../types'; + +function Logo(props: LogoProps) { + return ( + + + + + + ); +} + +// eslint-disable-next-line import/no-default-export +export default Logo; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_sir/servicenow_sir.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_sir/servicenow_sir.test.tsx new file mode 100644 index 0000000000000..e9ac99d210df8 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_sir/servicenow_sir.test.tsx @@ -0,0 +1,52 @@ +/* + * 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 { TypeRegistry } from '@kbn/triggers-actions-ui-plugin/public/application/type_registry'; +import { registerConnectorTypes } from '../..'; +import type { ActionTypeModel as ConnectorTypeModel } from '@kbn/triggers-actions-ui-plugin/public/types'; +import { registrationServicesMock } from '../../../mocks'; + +const SERVICENOW_SIR_CONNECTOR_TYPE_ID = '.servicenow-sir'; +let connectorTypeRegistry: TypeRegistry; + +beforeAll(() => { + connectorTypeRegistry = new TypeRegistry(); + registerConnectorTypes({ connectorTypeRegistry, services: registrationServicesMock }); +}); + +describe('connectorTypeRegistry.get() works', () => { + test(`${SERVICENOW_SIR_CONNECTOR_TYPE_ID}: connector type static data is as expected`, () => { + const connectorTypeModel = connectorTypeRegistry.get(SERVICENOW_SIR_CONNECTOR_TYPE_ID); + expect(connectorTypeModel.id).toEqual(SERVICENOW_SIR_CONNECTOR_TYPE_ID); + }); +}); + +describe('servicenow action params validation', () => { + test(`${SERVICENOW_SIR_CONNECTOR_TYPE_ID}: action params validation succeeds when action params is valid`, async () => { + const connectorTypeModel = connectorTypeRegistry.get(SERVICENOW_SIR_CONNECTOR_TYPE_ID); + const actionParams = { + subActionParams: { incident: { short_description: 'some title {{test}}' }, comments: [] }, + }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { ['subActionParams.incident.short_description']: [] }, + }); + }); + + test(`${SERVICENOW_SIR_CONNECTOR_TYPE_ID}: params validation fails when short_description is not valid`, async () => { + const connectorTypeModel = connectorTypeRegistry.get(SERVICENOW_SIR_CONNECTOR_TYPE_ID); + const actionParams = { + subActionParams: { incident: { short_description: '' }, comments: [] }, + }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { + ['subActionParams.incident.short_description']: ['Short description is required.'], + }, + }); + }); +}); diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_sir/servicenow_sir.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_sir/servicenow_sir.tsx new file mode 100644 index 0000000000000..ff6ebaa913941 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_sir/servicenow_sir.tsx @@ -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 { lazy } from 'react'; +import { i18n } from '@kbn/i18n'; +import type { + ActionTypeModel as ConnectorTypeModel, + GenericValidationResult, +} from '@kbn/triggers-actions-ui-plugin/public'; +import { ServiceNowConfig, ServiceNowSecrets } from '../../lib/servicenow/types'; +import { ServiceNowSIRActionParams } from './types'; +import { + getConnectorDescriptiveTitle, + getSelectedConnectorIcon, +} from '../../lib/servicenow/helpers'; + +export const SERVICENOW_SIR_DESC = i18n.translate( + 'xpack.stackConnectors.components.serviceNowSIR.selectMessageText', + { + defaultMessage: 'Create an incident in ServiceNow SecOps.', + } +); + +export const SERVICENOW_SIR_TITLE = i18n.translate( + 'xpack.stackConnectors.components.serviceNowSIR.connectorTypeTitle', + { + defaultMessage: 'ServiceNow SecOps', + } +); + +export function getServiceNowSIRConnectorType(): ConnectorTypeModel< + ServiceNowConfig, + ServiceNowSecrets, + ServiceNowSIRActionParams +> { + return { + id: '.servicenow-sir', + iconClass: lazy(() => import('./logo')), + selectMessage: SERVICENOW_SIR_DESC, + actionTypeTitle: SERVICENOW_SIR_TITLE, + actionConnectorFields: lazy(() => import('../../lib/servicenow/servicenow_connectors')), + validateParams: async ( + actionParams: ServiceNowSIRActionParams + ): Promise> => { + const translations = await import('../../lib/servicenow/translations'); + const errors = { + 'subActionParams.incident.short_description': new Array(), + }; + const validationResult = { + errors, + }; + if ( + actionParams.subActionParams && + actionParams.subActionParams.incident && + !actionParams.subActionParams.incident.short_description?.length + ) { + errors['subActionParams.incident.short_description'].push(translations.TITLE_REQUIRED); + } + return validationResult; + }, + actionParamsFields: lazy(() => import('./servicenow_sir_params')), + customConnectorSelectItem: { + getText: getConnectorDescriptiveTitle, + getComponent: getSelectedConnectorIcon, + }, + }; +} diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_sir_params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_sir/servicenow_sir_params.test.tsx similarity index 98% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_sir_params.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_sir/servicenow_sir_params.test.tsx index 8739938891625..49916b350c6a7 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_sir_params.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_sir/servicenow_sir_params.test.tsx @@ -10,12 +10,12 @@ import { act } from '@testing-library/react'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public/types'; -import { useGetChoices } from './use_get_choices'; +import { useGetChoices } from '../../lib/servicenow/use_get_choices'; import ServiceNowSIRParamsFields from './servicenow_sir_params'; -import { Choice } from './types'; +import { Choice } from '../../lib/servicenow/types'; import { merge } from 'lodash'; -jest.mock('./use_get_choices'); +jest.mock('../../lib/servicenow/use_get_choices'); jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'); const useGetChoicesMock = useGetChoices as jest.Mock; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_sir_params.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_sir/servicenow_sir_params.tsx similarity index 95% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_sir_params.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_sir/servicenow_sir_params.tsx index e58d635f9ef2d..e1b28425887fd 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_sir_params.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_sir/servicenow_sir_params.tsx @@ -23,11 +23,12 @@ import { useKibana, } from '@kbn/triggers-actions-ui-plugin/public'; -import * as i18n from './translations'; -import { useGetChoices } from './use_get_choices'; -import { ServiceNowSIRActionParams, Fields, Choice } from './types'; -import { choicesToEuiOptions, DEFAULT_CORRELATION_ID } from './helpers'; -import { DeprecatedCallout } from './deprecated_callout'; +import * as i18n from '../../lib/servicenow/translations'; +import { useGetChoices } from '../../lib/servicenow/use_get_choices'; +import { ServiceNowSIRActionParams } from './types'; +import { Fields, Choice } from '../../lib/servicenow/types'; +import { choicesToEuiOptions, DEFAULT_CORRELATION_ID } from '../../lib/servicenow/helpers'; +import { DeprecatedCallout } from '../../lib/servicenow/deprecated_callout'; const useGetChoicesFields = ['category', 'subcategory', 'priority']; const defaultFields: Fields = { diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_sir/types.ts b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_sir/types.ts new file mode 100644 index 0000000000000..889bd527a4a6a --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow_sir/types.ts @@ -0,0 +1,13 @@ +/* + * 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 { ExecutorSubActionPushParamsSIR } from '../../../../server/connector_types/lib/servicenow/types'; + +export interface ServiceNowSIRActionParams { + subAction: string; + subActionParams: ExecutorSubActionPushParamsSIR; +} diff --git a/x-pack/plugins/stack_connectors/public/connector_types/index.ts b/x-pack/plugins/stack_connectors/public/connector_types/index.ts index fe24d311c4d65..a2684c0b20734 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/index.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/index.ts @@ -12,20 +12,20 @@ import { getIndexConnectorType, getPagerDutyConnectorType, getServerLogConnectorType, + getServiceNowITOMConnectorType, getSlackConnectorType, getTeamsConnectorType, getWebhookConnectorType, + getXmattersConnectorType, } from './stack'; import { getCasesWebhookConnectorType, getJiraConnectorType, getResilientConnectorType, - getServiceNowITOMConnectorType, getServiceNowITSMConnectorType, getServiceNowSIRConnectorType, getSwimlaneConnectorType, - getXmattersConnectorType, } from './cases'; export interface RegistrationServices { diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/api.test.ts b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/api.test.ts similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/api.test.ts rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/api.test.ts diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/api.ts b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/api.ts similarity index 96% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/api.ts rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/api.ts index 4cf46d57eb7f4..38ae8727f635d 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/api.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/api.ts @@ -15,10 +15,7 @@ import { import { snExternalServiceConfig } from '../../../../common/servicenow_config'; import { API_INFO_ERROR } from './translations'; import { AppInfo, RESTApiError, ServiceNowActionConnector } from './types'; -import { - ConnectorExecutorResult, - rewriteResponseToCamelCase, -} from '../../lib/rewrite_response_body'; +import { ConnectorExecutorResult, rewriteResponseToCamelCase } from '../rewrite_response_body'; import { Choice } from './types'; export async function getChoices({ diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/application_required_callout.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/application_required_callout.test.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/application_required_callout.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/application_required_callout.test.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/application_required_callout.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/application_required_callout.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/application_required_callout.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/application_required_callout.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/auth_types/credentials_auth.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/auth_types/credentials_auth.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/auth_types/credentials_auth.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/auth_types/credentials_auth.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/auth_types/index.ts b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/auth_types/index.ts similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/auth_types/index.ts rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/auth_types/index.ts diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/auth_types/oauth.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/auth_types/oauth.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/auth_types/oauth.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/auth_types/oauth.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/credentials.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/credentials.test.tsx similarity index 97% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/credentials.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/credentials.test.tsx index aab2ab0fb21c6..2e1ae98a10886 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/credentials.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/credentials.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; import { Credentials } from './credentials'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; -import { ConnectorFormTestProvider } from '../../lib/test_utils'; +import { ConnectorFormTestProvider } from '../test_utils'; jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'); diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/credentials.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/credentials.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/credentials.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/credentials.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/credentials_api_url.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/credentials_api_url.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/credentials_api_url.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/credentials_api_url.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/deprecated_callout.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/deprecated_callout.test.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/deprecated_callout.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/deprecated_callout.test.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/deprecated_callout.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/deprecated_callout.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/deprecated_callout.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/deprecated_callout.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/helpers.test.ts b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/helpers.test.ts similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/helpers.test.ts rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/helpers.test.ts diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/helpers.ts b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/helpers.ts similarity index 96% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/helpers.ts rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/helpers.ts index def683edbdd33..f3e71b0e9fc7f 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/helpers.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/helpers.ts @@ -42,6 +42,6 @@ export const getSelectedConnectorIcon = ( actionConnector: ActionConnector ): React.LazyExoticComponent> | undefined => { if (actionConnector.isDeprecated) { - return lazy(() => import('./servicenow_selection_row')); + return lazy(() => import('./selection_row')); } }; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/installation_callout.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/installation_callout.test.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/installation_callout.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/installation_callout.test.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/installation_callout.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/installation_callout.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/installation_callout.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/installation_callout.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_selection_row.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/selection_row.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_selection_row.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/selection_row.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_connectors.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/servicenow_connectors.test.tsx similarity index 99% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_connectors.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/servicenow_connectors.test.tsx index 6bf81f5aeae74..51b0f759c75bd 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_connectors.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/servicenow_connectors.test.tsx @@ -15,7 +15,7 @@ import { useKibana } from '@kbn/triggers-actions-ui-plugin/public'; import { updateActionConnector } from '@kbn/triggers-actions-ui-plugin/public/application/lib/action_connector_api'; import ServiceNowConnectorFields from './servicenow_connectors'; import { getAppInfo } from './api'; -import { ConnectorFormTestProvider } from '../../lib/test_utils'; +import { ConnectorFormTestProvider } from '../test_utils'; import { mount } from 'enzyme'; import userEvent from '@testing-library/user-event'; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_connectors.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/servicenow_connectors.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_connectors.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/servicenow_connectors.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_connectors_no_app.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/servicenow_connectors_no_app.test.tsx similarity index 97% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_connectors_no_app.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/servicenow_connectors_no_app.test.tsx index e70005f8c7e1b..a0dda6edf76e0 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_connectors_no_app.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/servicenow_connectors_no_app.test.tsx @@ -8,11 +8,7 @@ import { act } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; -import { - AppMockRenderer, - ConnectorFormTestProvider, - createAppMockRenderer, -} from '../../lib/test_utils'; +import { AppMockRenderer, ConnectorFormTestProvider, createAppMockRenderer } from '../test_utils'; import ServiceNowConnectorFieldsNoApp from './servicenow_connectors_no_app'; describe('ServiceNowActionConnectorFields renders', () => { diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_connectors_no_app.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/servicenow_connectors_no_app.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_connectors_no_app.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/servicenow_connectors_no_app.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/sn_store_button.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/sn_store_button.test.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/sn_store_button.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/sn_store_button.test.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/sn_store_button.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/sn_store_button.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/sn_store_button.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/sn_store_button.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/translations.ts b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/translations.ts similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/translations.ts rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/translations.ts diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/types.ts b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/types.ts similarity index 73% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/types.ts rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/types.ts index f10de69252f9d..862eb2165b7b2 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/types.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/types.ts @@ -6,32 +6,12 @@ */ import { UserConfiguredActionConnector } from '@kbn/triggers-actions-ui-plugin/public/types'; -import type { - ExecutorSubActionPushParamsITSM, - ExecutorSubActionPushParamsSIR, - ExecutorSubActionAddEventParams, -} from '../../../../server/connector_types/cases/servicenow/types'; export type ServiceNowActionConnector = UserConfiguredActionConnector< ServiceNowConfig, ServiceNowSecrets >; -export interface ServiceNowITSMActionParams { - subAction: string; - subActionParams: ExecutorSubActionPushParamsITSM; -} - -export interface ServiceNowSIRActionParams { - subAction: string; - subActionParams: ExecutorSubActionPushParamsSIR; -} - -export interface ServiceNowITOMActionParams { - subAction: string; - subActionParams: ExecutorSubActionAddEventParams; -} - // Config export interface ServiceNowCommonConfig { isOAuth: boolean; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/update_connector.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/update_connector.test.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/update_connector.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/update_connector.test.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/update_connector.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/update_connector.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/update_connector.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/update_connector.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/use_choices.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/use_choices.test.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/use_choices.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/use_choices.test.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/use_choices.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/use_choices.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/use_choices.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/use_choices.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/use_get_app_info.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/use_get_app_info.test.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/use_get_app_info.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/use_get_app_info.test.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/use_get_app_info.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/use_get_app_info.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/use_get_app_info.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/use_get_app_info.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/use_get_choices.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/use_get_choices.test.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/use_get_choices.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/use_get_choices.test.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/use_get_choices.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/use_get_choices.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/use_get_choices.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/use_get_choices.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/stack/index.ts b/x-pack/plugins/stack_connectors/public/connector_types/stack/index.ts index 93d444d20204d..fec0283a799ab 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/stack/index.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/index.ts @@ -9,6 +9,8 @@ export { getEmailConnectorType } from './email'; export { getIndexConnectorType } from './es_index'; export { getPagerDutyConnectorType } from './pagerduty'; export { getServerLogConnectorType } from './server_log'; +export { getServiceNowITOMConnectorType } from './servicenow_itom'; export { getSlackConnectorType } from './slack'; export { getTeamsConnectorType } from './teams'; export { getWebhookConnectorType } from './webhook'; +export { getXmattersConnectorType } from './xmatters'; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/stack/servicenow_itom/index.ts b/x-pack/plugins/stack_connectors/public/connector_types/stack/servicenow_itom/index.ts new file mode 100644 index 0000000000000..bfcd0f888f855 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/servicenow_itom/index.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export { getServiceNowITOMConnectorType } from './servicenow_itom'; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/stack/servicenow_itom/logo.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/servicenow_itom/logo.tsx new file mode 100644 index 0000000000000..f97b07247569d --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/servicenow_itom/logo.tsx @@ -0,0 +1,34 @@ +/* + * 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 React from 'react'; +import { LogoProps } from '../../types'; + +function Logo(props: LogoProps) { + return ( + + + + + + ); +} + +// eslint-disable-next-line import/no-default-export +export default Logo; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/servicenow_itom/servicenow_itom.test.tsx similarity index 54% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/servicenow_itom/servicenow_itom.test.tsx index 9427623f0de8a..b86616ccde5e1 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/servicenow_itom/servicenow_itom.test.tsx @@ -10,8 +10,6 @@ import { registerConnectorTypes } from '../..'; import type { ActionTypeModel as ConnectorTypeModel } from '@kbn/triggers-actions-ui-plugin/public/types'; import { registrationServicesMock } from '../../../mocks'; -const SERVICENOW_ITSM_CONNECTOR_TYPE_ID = '.servicenow'; -const SERVICENOW_SIR_CONNECTOR_TYPE_ID = '.servicenow-sir'; const SERVICENOW_ITOM_CONNECTOR_TYPE_ID = '.servicenow-itom'; let connectorTypeRegistry: TypeRegistry; @@ -21,45 +19,13 @@ beforeAll(() => { }); describe('connectorTypeRegistry.get() works', () => { - [ - SERVICENOW_ITSM_CONNECTOR_TYPE_ID, - SERVICENOW_SIR_CONNECTOR_TYPE_ID, - SERVICENOW_ITOM_CONNECTOR_TYPE_ID, - ].forEach((id) => { - test(`${id}: connector type static data is as expected`, () => { - const connectorTypeModel = connectorTypeRegistry.get(id); - expect(connectorTypeModel.id).toEqual(id); - }); + test(`${SERVICENOW_ITOM_CONNECTOR_TYPE_ID}: connector type static data is as expected`, () => { + const connectorTypeModel = connectorTypeRegistry.get(SERVICENOW_ITOM_CONNECTOR_TYPE_ID); + expect(connectorTypeModel.id).toEqual(SERVICENOW_ITOM_CONNECTOR_TYPE_ID); }); }); describe('servicenow action params validation', () => { - [SERVICENOW_ITSM_CONNECTOR_TYPE_ID, SERVICENOW_SIR_CONNECTOR_TYPE_ID].forEach((id) => { - test(`${id}: action params validation succeeds when action params is valid`, async () => { - const connectorTypeModel = connectorTypeRegistry.get(id); - const actionParams = { - subActionParams: { incident: { short_description: 'some title {{test}}' }, comments: [] }, - }; - - expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ - errors: { ['subActionParams.incident.short_description']: [] }, - }); - }); - - test(`${id}: params validation fails when short_description is not valid`, async () => { - const connectorTypeModel = connectorTypeRegistry.get(id); - const actionParams = { - subActionParams: { incident: { short_description: '' }, comments: [] }, - }; - - expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ - errors: { - ['subActionParams.incident.short_description']: ['Short description is required.'], - }, - }); - }); - }); - test(`${SERVICENOW_ITOM_CONNECTOR_TYPE_ID}: action params validation succeeds when action params is valid`, async () => { const connectorTypeModel = connectorTypeRegistry.get(SERVICENOW_ITOM_CONNECTOR_TYPE_ID); const actionParams = { subActionParams: { severity: 'Critical' } }; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/stack/servicenow_itom/servicenow_itom.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/servicenow_itom/servicenow_itom.tsx new file mode 100644 index 0000000000000..09f4002774f67 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/servicenow_itom/servicenow_itom.tsx @@ -0,0 +1,59 @@ +/* + * 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 { lazy } from 'react'; +import { i18n } from '@kbn/i18n'; +import type { + ActionTypeModel as ConnectorTypeModel, + GenericValidationResult, +} from '@kbn/triggers-actions-ui-plugin/public'; +import { ServiceNowConfig, ServiceNowSecrets } from '../../lib/servicenow/types'; +import { ServiceNowITOMActionParams } from './types'; + +export const SERVICENOW_ITOM_TITLE = i18n.translate( + 'xpack.stackConnectors.components.serviceNowITOM.connectorTypeTitle', + { + defaultMessage: 'ServiceNow ITOM', + } +); + +export const SERVICENOW_ITOM_DESC = i18n.translate( + 'xpack.stackConnectors.components.serviceNowITOM.selectMessageText', + { + defaultMessage: 'Create an event in ServiceNow ITOM.', + } +); + +export function getServiceNowITOMConnectorType(): ConnectorTypeModel< + ServiceNowConfig, + ServiceNowSecrets, + ServiceNowITOMActionParams +> { + return { + id: '.servicenow-itom', + iconClass: lazy(() => import('./logo')), + selectMessage: SERVICENOW_ITOM_DESC, + actionTypeTitle: SERVICENOW_ITOM_TITLE, + actionConnectorFields: lazy(() => import('../../lib/servicenow/servicenow_connectors_no_app')), + validateParams: async ( + actionParams: ServiceNowITOMActionParams + ): Promise> => { + const translations = await import('../../lib/servicenow/translations'); + const errors = { + severity: new Array(), + }; + const validationResult = { errors }; + + if (actionParams?.subActionParams?.severity == null) { + errors.severity.push(translations.SEVERITY_REQUIRED); + } + + return validationResult; + }, + actionParamsFields: lazy(() => import('./servicenow_itom_params')), + }; +} diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_itom_params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/servicenow_itom/servicenow_itom_params.test.tsx similarity index 98% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_itom_params.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/servicenow_itom/servicenow_itom_params.test.tsx index 60531c7a7104d..d2e9880058277 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_itom_params.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/servicenow_itom/servicenow_itom_params.test.tsx @@ -9,10 +9,10 @@ import React from 'react'; import { mount } from 'enzyme'; import { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public/types'; -import { useChoices } from './use_choices'; +import { useChoices } from '../../lib/servicenow/use_choices'; import ServiceNowITOMParamsFields from './servicenow_itom_params'; -jest.mock('./use_choices'); +jest.mock('../../lib/servicenow/use_choices'); jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'); const useChoicesMock = useChoices as jest.Mock; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_itom_params.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/servicenow_itom/servicenow_itom_params.tsx similarity index 96% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_itom_params.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/servicenow_itom/servicenow_itom_params.tsx index caa2f40bac2c1..0086c6713e074 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/cases/servicenow/servicenow_itom_params.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/servicenow_itom/servicenow_itom_params.tsx @@ -14,10 +14,10 @@ import { useKibana, } from '@kbn/triggers-actions-ui-plugin/public'; -import * as i18n from './translations'; -import { useChoices } from './use_choices'; +import * as i18n from '../../lib/servicenow/translations'; +import { useChoices } from '../../lib/servicenow/use_choices'; import { ServiceNowITOMActionParams } from './types'; -import { choicesToEuiOptions, isFieldInvalid } from './helpers'; +import { choicesToEuiOptions, isFieldInvalid } from '../../lib/servicenow/helpers'; const choicesFields = ['severity']; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/stack/servicenow_itom/types.ts b/x-pack/plugins/stack_connectors/public/connector_types/stack/servicenow_itom/types.ts new file mode 100644 index 0000000000000..5155997c996c8 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/stack/servicenow_itom/types.ts @@ -0,0 +1,13 @@ +/* + * 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 { ExecutorSubActionAddEventParams } from '../../../../server/connector_types/lib/servicenow/types'; + +export interface ServiceNowITOMActionParams { + subAction: string; + subActionParams: ExecutorSubActionAddEventParams; +} diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/index.ts b/x-pack/plugins/stack_connectors/public/connector_types/stack/xmatters/index.ts similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/index.ts rename to x-pack/plugins/stack_connectors/public/connector_types/stack/xmatters/index.ts diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/logo.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/xmatters/logo.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/logo.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/xmatters/logo.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/translations.ts b/x-pack/plugins/stack_connectors/public/connector_types/stack/xmatters/translations.ts similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/translations.ts rename to x-pack/plugins/stack_connectors/public/connector_types/stack/xmatters/translations.ts diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/xmatters.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/xmatters/xmatters.test.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/xmatters.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/xmatters/xmatters.test.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/xmatters.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/xmatters/xmatters.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/xmatters.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/xmatters/xmatters.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/xmatters_connectors.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/xmatters/xmatters_connectors.test.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/xmatters_connectors.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/xmatters/xmatters_connectors.test.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/xmatters_connectors.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/xmatters/xmatters_connectors.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/xmatters_connectors.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/xmatters/xmatters_connectors.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/xmatters_params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/xmatters/xmatters_params.test.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/xmatters_params.test.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/xmatters/xmatters_params.test.tsx diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/xmatters_params.tsx b/x-pack/plugins/stack_connectors/public/connector_types/stack/xmatters/xmatters_params.tsx similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/cases/xmatters/xmatters_params.tsx rename to x-pack/plugins/stack_connectors/public/connector_types/stack/xmatters/xmatters_params.tsx diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/index.ts b/x-pack/plugins/stack_connectors/server/connector_types/cases/index.ts index abc6c0557a780..ad706fff3b8e7 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/cases/index.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/cases/index.ts @@ -22,20 +22,10 @@ export { } from './resilient'; export type { ActionParamsType as ResilientActionParams } from './resilient'; -export { - getServiceNowITSMConnectorType, - getServiceNowSIRConnectorType, - getServiceNowITOMConnectorType, - ServiceNowITSMConnectorTypeId, - ServiceNowSIRConnectorTypeId, - ServiceNowITOMConnectorTypeId, -} from './servicenow'; -export type { ActionParamsType as ServiceNowActionParams } from './servicenow'; +export { getServiceNowITSMConnectorType, ServiceNowITSMConnectorTypeId } from './servicenow_itsm'; +import type { ActionParamsType as ServiceNowITSMActionParams } from './servicenow_itsm'; +export { getServiceNowSIRConnectorType, ServiceNowSIRConnectorTypeId } from './servicenow_sir'; +import type { ActionParamsType as ServiceNowSIRActionParams } from './servicenow_sir'; +export type ServiceNowActionParams = ServiceNowITSMActionParams | ServiceNowSIRActionParams; export { getConnectorType as getSwimlaneConnectorType } from './swimlane'; - -export { - getConnectorType as getXmattersConnectorType, - ConnectorTypeId as XmattersConnectorTypeId, -} from './xmatters'; -export type { ActionParamsType as XmattersActionParams } from './xmatters'; diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_itsm/api.ts b/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_itsm/api.ts new file mode 100644 index 0000000000000..4eee89cb7de98 --- /dev/null +++ b/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_itsm/api.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export { api } from '../../lib/servicenow/api'; diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/index.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_itsm/index.test.ts similarity index 70% rename from x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/index.test.ts rename to x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_itsm/index.test.ts index 02cc32111bff4..50ff4d8e0f1c9 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/index.test.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_itsm/index.test.ts @@ -8,12 +8,11 @@ import { Logger } from '@kbn/core/server'; import { loggerMock } from '@kbn/logging-mocks'; import { actionsMock } from '@kbn/actions-plugin/server/mocks'; -import { ExecutorParams, ServiceNowPublicConfigurationType } from './types'; +import { ExecutorParams, ServiceNowPublicConfigurationType } from '../../lib/servicenow/types'; import { ServiceNowConnectorType, ServiceNowConnectorTypeExecutorOptions, getServiceNowITSMConnectorType, - getServiceNowSIRConnectorType, } from '.'; import { api } from './api'; @@ -79,38 +78,4 @@ describe('ServiceNow', () => { }); }); }); - - describe('ServiceNow SIR', () => { - let connectorType: ServiceNowConnectorType; - - beforeAll(() => { - connectorType = getServiceNowSIRConnectorType({ - logger: mockedLogger, - }); - }); - - describe('execute()', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - test('it pass the correct comment field key', async () => { - const actionId = 'some-action-id'; - const executorOptions = { - actionId, - config, - secrets, - params, - services, - } as unknown as ServiceNowConnectorTypeExecutorOptions< - ServiceNowPublicConfigurationType, - ExecutorParams - >; - await connectorType.executor(executorOptions); - expect((api.pushToService as jest.Mock).mock.calls[0][0].commentFieldKey).toBe( - 'work_notes' - ); - }); - }); - }); }); diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/index.ts b/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_itsm/index.ts similarity index 51% rename from x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/index.ts rename to x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_itsm/index.ts index 493c7024bb15f..40e9d1470950e 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/index.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_itsm/index.ts @@ -20,62 +20,40 @@ import { UptimeConnectorFeatureId, SecurityConnectorFeatureId, } from '@kbn/actions-plugin/common/types'; -import { validate } from './validators'; +import { validate } from '../../lib/servicenow/validators'; import { + ExecutorParamsSchemaITSM, ExternalIncidentServiceConfigurationSchema, - ExternalIncidentServiceConfigurationBaseSchema, ExternalIncidentServiceSecretConfigurationSchema, - ExecutorParamsSchemaITSM, - ExecutorParamsSchemaSIR, - ExecutorParamsSchemaITOM, -} from './schema'; +} from '../../lib/servicenow/schema'; import { createExternalService } from './service'; -import { api as commonAPI } from './api'; -import * as i18n from './translations'; +import { api as apiITSM } from './api'; +import * as i18n from '../../lib/servicenow/translations'; import { ExecutorParams, ExecutorSubActionPushParams, - ServiceNowPublicConfigurationType, - ServiceNowSecretConfigurationType, - PushToServiceResponse, - ExecutorSubActionCommonFieldsParams, - ServiceNowExecutorResultData, - ExecutorSubActionGetChoicesParams, ServiceFactory, ExternalServiceAPI, - ExecutorParamsITOM, - ExecutorSubActionAddEventParams, - ExternalServiceApiITOM, - ExternalServiceITOM, ServiceNowPublicConfigurationBaseType, ExternalService, -} from './types'; + ExecutorSubActionCommonFieldsParams, + ExecutorSubActionGetChoicesParams, + PushToServiceResponse, + ServiceNowExecutorResultData, + ServiceNowPublicConfigurationType, + ServiceNowSecretConfigurationType, +} from '../../lib/servicenow/types'; import { - ServiceNowITOMConnectorTypeId, ServiceNowITSMConnectorTypeId, serviceNowITSMTable, - ServiceNowSIRConnectorTypeId, - serviceNowSIRTable, snExternalServiceConfig, -} from './config'; -import { createExternalServiceSIR } from './service_sir'; -import { apiSIR } from './api_sir'; -import { throwIfSubActionIsNotSupported } from './utils'; -import { createExternalServiceITOM } from './service_itom'; -import { apiITOM } from './api_itom'; -import { createServiceWrapper } from './create_service_wrapper'; +} from '../../lib/servicenow/config'; +import { throwIfSubActionIsNotSupported } from '../../lib/servicenow/utils'; +import { createServiceWrapper } from '../../lib/servicenow/create_service_wrapper'; -export { - ServiceNowITSMConnectorTypeId, - serviceNowITSMTable, - ServiceNowSIRConnectorTypeId, - serviceNowSIRTable, - ServiceNowITOMConnectorTypeId, -}; +export { ServiceNowITSMConnectorTypeId, serviceNowITSMTable }; -export type ActionParamsType = - | TypeOf - | TypeOf; +export type ActionParamsType = TypeOf; interface GetConnectorTypeParams { logger: Logger; @@ -124,75 +102,7 @@ export function getServiceNowITSMConnectorType( logger, actionTypeId: ServiceNowITSMConnectorTypeId, createService: createExternalService, - api: commonAPI, - }), - }; -} - -export function getServiceNowSIRConnectorType( - params: GetConnectorTypeParams -): ServiceNowConnectorType { - const { logger } = params; - return { - id: ServiceNowSIRConnectorTypeId, - minimumLicenseRequired: 'platinum', - name: i18n.SERVICENOW_SIR, - supportedFeatureIds: [ - AlertingConnectorFeatureId, - CasesConnectorFeatureId, - SecurityConnectorFeatureId, - ], - validate: { - config: { - schema: ExternalIncidentServiceConfigurationSchema, - customValidator: validate.config, - }, - secrets: { - schema: ExternalIncidentServiceSecretConfigurationSchema, - customValidator: validate.secrets, - }, - connector: validate.connector, - params: { - schema: ExecutorParamsSchemaSIR, - }, - }, - executor: curry(executor)({ - logger, - actionTypeId: ServiceNowSIRConnectorTypeId, - createService: createExternalServiceSIR, - api: apiSIR, - }), - }; -} - -export function getServiceNowITOMConnectorType( - params: GetConnectorTypeParams -): ServiceNowConnectorType { - const { logger } = params; - return { - id: ServiceNowITOMConnectorTypeId, - minimumLicenseRequired: 'platinum', - name: i18n.SERVICENOW_ITOM, - supportedFeatureIds: [AlertingConnectorFeatureId, SecurityConnectorFeatureId], - validate: { - config: { - schema: ExternalIncidentServiceConfigurationBaseSchema, - customValidator: validate.config, - }, - secrets: { - schema: ExternalIncidentServiceSecretConfigurationSchema, - customValidator: validate.secrets, - }, - connector: validate.connector, - params: { - schema: ExecutorParamsSchemaITOM, - }, - }, - executor: curry(executorITOM)({ - logger, - actionTypeId: ServiceNowITOMConnectorTypeId, - createService: createExternalServiceITOM, - api: apiITOM, + api: apiITSM, }), }; } @@ -272,71 +182,3 @@ async function executor( return { status: 'ok', data: data ?? {}, actionId }; } - -const supportedSubActionsITOM = ['addEvent', 'getChoices']; - -async function executorITOM( - { - logger, - actionTypeId, - createService, - api, - }: { - logger: Logger; - actionTypeId: string; - createService: ServiceFactory; - api: ExternalServiceApiITOM; - }, - execOptions: ServiceNowConnectorTypeExecutorOptions< - ServiceNowPublicConfigurationBaseType, - ExecutorParamsITOM - > -): Promise> { - const { actionId, config, params, secrets, configurationUtilities } = execOptions; - const { subAction, subActionParams } = params; - const connectorTokenClient = execOptions.services.connectorTokenClient; - const externalServiceConfig = snExternalServiceConfig[actionTypeId]; - let data: ServiceNowExecutorResultData | null = null; - - const externalService = createServiceWrapper({ - connectorId: actionId, - credentials: { - config, - secrets, - }, - logger, - configurationUtilities, - serviceConfig: externalServiceConfig, - connectorTokenClient, - createServiceFn: createService, - }); - - const apiAsRecord = api as unknown as Record; - - throwIfSubActionIsNotSupported({ - api: apiAsRecord, - subAction, - supportedSubActions: supportedSubActionsITOM, - logger, - }); - - if (subAction === 'addEvent') { - const eventParams = subActionParams as ExecutorSubActionAddEventParams; - await api.addEvent({ - externalService, - params: eventParams, - logger, - }); - } - - if (subAction === 'getChoices') { - const getChoicesParams = subActionParams as ExecutorSubActionGetChoicesParams; - data = await api.getChoices({ - externalService, - params: getChoicesParams, - logger, - }); - } - - return { status: 'ok', data: data ?? {}, actionId }; -} diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_itsm/service.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_itsm/service.test.ts new file mode 100644 index 0000000000000..f2bb94bd60756 --- /dev/null +++ b/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_itsm/service.test.ts @@ -0,0 +1,1022 @@ +/* + * 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 axios, { AxiosResponse } from 'axios'; + +import { createExternalService } from './service'; +import * as utils from '@kbn/actions-plugin/server/lib/axios_utils'; +import { ExternalService, ServiceNowITSMIncident } from '../../lib/servicenow/types'; +import { Logger } from '@kbn/core/server'; +import { loggingSystemMock } from '@kbn/core/server/mocks'; +import { actionsConfigMock } from '@kbn/actions-plugin/server/actions_config.mock'; +import { serviceNowCommonFields, serviceNowChoices } from '../../lib/servicenow/mocks'; +import { snExternalServiceConfig } from '../../lib/servicenow/config'; +const logger = loggingSystemMock.create().get() as jest.Mocked; + +jest.mock('axios'); +jest.mock('@kbn/actions-plugin/server/lib/axios_utils', () => { + const originalUtils = jest.requireActual('@kbn/actions-plugin/server/lib/axios_utils'); + return { + ...originalUtils, + request: jest.fn(), + patch: jest.fn(), + }; +}); + +axios.create = jest.fn(() => axios); +const requestMock = utils.request as jest.Mock; +const configurationUtilities = actionsConfigMock.create(); + +const getImportSetAPIResponse = (update = false) => ({ + import_set: 'ISET01', + staging_table: 'x_elas2_inc_int_elastic_incident', + result: [ + { + transform_map: 'Elastic Incident', + table: 'incident', + display_name: 'number', + display_value: 'INC01', + record_link: 'https://example.com/api/now/table/incident/1', + status: update ? 'updated' : 'inserted', + sys_id: '1', + }, + ], +}); + +const getImportSetAPIError = () => ({ + import_set: 'ISET01', + staging_table: 'x_elas2_inc_int_elastic_incident', + result: [ + { + transform_map: 'Elastic Incident', + status: 'error', + error_message: 'An error has occurred while importing the incident', + status_message: 'failure', + }, + ], +}); + +const mockApplicationVersion = () => + requestMock.mockImplementationOnce(() => ({ + data: { + result: { name: 'Elastic', scope: 'x_elas2_inc_int', version: '1.0.0' }, + }, + })); + +const mockImportIncident = (update: boolean) => + requestMock.mockImplementationOnce(() => ({ + data: getImportSetAPIResponse(update), + })); + +const mockIncidentResponse = (update: boolean) => + requestMock.mockImplementation(() => ({ + data: { + result: { + sys_id: '1', + number: 'INC01', + ...(update + ? { sys_updated_on: '2020-03-10 12:24:20' } + : { sys_created_on: '2020-03-10 12:24:20' }), + }, + }, + })); + +const createIncident = async (service: ExternalService) => { + // Get application version + mockApplicationVersion(); + // Import set api response + mockImportIncident(false); + // Get incident response + mockIncidentResponse(false); + + return await service.createIncident({ + incident: { short_description: 'title', description: 'desc' } as ServiceNowITSMIncident, + }); +}; + +const updateIncident = async (service: ExternalService) => { + // Get application version + mockApplicationVersion(); + // Import set api response + mockImportIncident(true); + // Get incident response + mockIncidentResponse(true); + + return await service.updateIncident({ + incidentId: '1', + incident: { short_description: 'title', description: 'desc' } as ServiceNowITSMIncident, + }); +}; + +const expectImportedIncident = (update: boolean) => { + expect(requestMock).toHaveBeenNthCalledWith(1, { + axios, + logger, + configurationUtilities, + url: 'https://example.com/api/x_elas2_inc_int/elastic_api/health', + method: 'get', + }); + + expect(requestMock).toHaveBeenNthCalledWith(2, { + axios, + logger, + configurationUtilities, + url: 'https://example.com/api/now/import/x_elas2_inc_int_elastic_incident', + method: 'post', + data: { + u_short_description: 'title', + u_description: 'desc', + ...(update ? { elastic_incident_id: '1' } : {}), + }, + }); + + expect(requestMock).toHaveBeenNthCalledWith(3, { + axios, + logger, + configurationUtilities, + url: 'https://example.com/api/now/v2/table/incident/1', + method: 'get', + }); +}; + +describe('ServiceNow service', () => { + let service: ExternalService; + + beforeEach(() => { + jest.clearAllMocks(); + service = createExternalService({ + credentials: { + // The trailing slash at the end of the url is intended. + // All API calls need to have the trailing slash removed. + config: { apiUrl: 'https://example.com/', isOAuth: false }, + secrets: { username: 'admin', password: 'admin' }, + }, + logger, + configurationUtilities, + serviceConfig: snExternalServiceConfig['.servicenow'], + axiosInstance: axios, + }); + }); + + describe('createExternalService', () => { + test('throws without url', () => { + expect(() => + createExternalService({ + credentials: { + config: { apiUrl: null, isOAuth: false }, + secrets: { username: 'admin', password: 'admin' }, + }, + logger, + configurationUtilities, + serviceConfig: snExternalServiceConfig['.servicenow'], + axiosInstance: axios, + }) + ).toThrow(); + }); + + test('throws when isOAuth is false and basic auth required values are falsy', () => { + const badBasicCredentials = [ + { + config: { apiUrl: 'test.com', isOAuth: false }, + secrets: { username: '', password: 'admin' }, + }, + { + config: { apiUrl: 'test.com', isOAuth: false }, + secrets: { username: null, password: 'admin' }, + }, + { + config: { apiUrl: 'test.com', isOAuth: false }, + secrets: { password: 'admin' }, + }, + { + config: { apiUrl: 'test.com', isOAuth: false }, + secrets: { username: 'admin', password: '' }, + }, + { + config: { apiUrl: 'test.com', isOAuth: false }, + secrets: { username: 'admin', password: null }, + }, + { + config: { apiUrl: 'test.com', isOAuth: false }, + secrets: { username: 'admin' }, + }, + ]; + + badBasicCredentials.forEach((badCredentials) => { + expect(() => + createExternalService({ + credentials: badCredentials, + logger, + configurationUtilities, + serviceConfig: snExternalServiceConfig['.servicenow'], + axiosInstance: axios, + }) + ).toThrow(); + }); + }); + + test('throws when isOAuth is true and OAuth required values are falsy', () => { + const badOAuthCredentials = [ + { + config: { + apiUrl: 'test.com', + isOAuth: true, + clientId: '', + jwtKeyId: 'jwtKeyId', + userIdentifierValue: 'user@email.com', + }, + secrets: { clientSecret: 'clientSecret', privateKey: 'privateKey' }, + }, + { + config: { + apiUrl: 'test.com', + isOAuth: true, + clientId: null, + jwtKeyId: 'jwtKeyId', + userIdentifierValue: 'user@email.com', + }, + secrets: { clientSecret: 'clientSecret', privateKey: 'privateKey' }, + }, + { + config: { + apiUrl: 'test.com', + isOAuth: true, + jwtKeyId: 'jwtKeyId', + userIdentifierValue: 'user@email.com', + }, + secrets: { clientSecret: 'clientSecret', privateKey: 'privateKey' }, + }, + { + config: { + apiUrl: 'test.com', + isOAuth: true, + clientId: 'clientId', + jwtKeyId: '', + userIdentifierValue: 'user@email.com', + }, + secrets: { clientSecret: 'clientSecret', privateKey: 'privateKey' }, + }, + { + config: { + apiUrl: 'test.com', + isOAuth: true, + clientId: 'clientId', + jwtKeyId: null, + userIdentifierValue: 'user@email.com', + }, + secrets: { clientSecret: 'clientSecret', privateKey: 'privateKey' }, + }, + { + config: { + apiUrl: 'test.com', + isOAuth: true, + clientId: 'clientId', + userIdentifierValue: 'user@email.com', + }, + secrets: { clientSecret: 'clientSecret', privateKey: 'privateKey' }, + }, + { + config: { + apiUrl: 'test.com', + isOAuth: true, + clientId: 'clientId', + jwtKeyId: 'jwtKeyId', + userIdentifierValue: '', + }, + secrets: { clientSecret: 'clientSecret', privateKey: 'privateKey' }, + }, + { + config: { + apiUrl: 'test.com', + isOAuth: true, + clientId: 'clientId', + jwtKeyId: 'jwtKeyId', + userIdentifierValue: null, + }, + secrets: { clientSecret: 'clientSecret', privateKey: 'privateKey' }, + }, + { + config: { + apiUrl: 'test.com', + isOAuth: true, + clientId: 'clientId', + jwtKeyId: 'jwtKeyId', + }, + secrets: { clientSecret: 'clientSecret', privateKey: 'privateKey' }, + }, + { + config: { + apiUrl: 'test.com', + isOAuth: true, + clientId: 'clientId', + jwtKeyId: 'jwtKeyId', + userIdentifierValue: 'user@email.com', + }, + secrets: { clientSecret: '', privateKey: 'privateKey' }, + }, + { + config: { + apiUrl: 'test.com', + isOAuth: true, + clientId: 'clientId', + jwtKeyId: 'jwtKeyId', + userIdentifierValue: 'user@email.com', + }, + secrets: { clientSecret: null, privateKey: 'privateKey' }, + }, + { + config: { + apiUrl: 'test.com', + isOAuth: true, + clientId: 'clientId', + jwtKeyId: 'jwtKeyId', + userIdentifierValue: 'user@email.com', + }, + secrets: { privateKey: 'privateKey' }, + }, + { + config: { + apiUrl: 'test.com', + isOAuth: true, + clientId: 'clientId', + jwtKeyId: 'jwtKeyId', + userIdentifierValue: 'user@email.com', + }, + secrets: { clientSecret: 'clientSecret', privateKey: '' }, + }, + { + config: { + apiUrl: 'test.com', + isOAuth: true, + clientId: 'clientId', + jwtKeyId: 'jwtKeyId', + userIdentifierValue: 'user@email.com', + }, + secrets: { clientSecret: 'clientSecret', privateKey: null }, + }, + { + config: { + apiUrl: 'test.com', + isOAuth: true, + clientId: 'clientId', + jwtKeyId: 'jwtKeyId', + userIdentifierValue: 'user@email.com', + }, + secrets: { clientSecret: 'clientSecret' }, + }, + ]; + + badOAuthCredentials.forEach((badCredentials) => { + expect(() => + createExternalService({ + credentials: badCredentials, + logger, + configurationUtilities, + serviceConfig: snExternalServiceConfig['.servicenow'], + axiosInstance: axios, + }) + ).toThrow(); + }); + }); + }); + + describe('getIncident', () => { + test('it returns the incident correctly', async () => { + requestMock.mockImplementation(() => ({ + data: { result: { sys_id: '1', number: 'INC01' } }, + })); + const res = await service.getIncident('1'); + expect(res).toEqual({ sys_id: '1', number: 'INC01' }); + }); + + test('it should call request with correct arguments', async () => { + requestMock.mockImplementation(() => ({ + data: { result: { sys_id: '1', number: 'INC01' } }, + })); + + await service.getIncident('1'); + expect(requestMock).toHaveBeenCalledWith({ + axios, + logger, + configurationUtilities, + url: 'https://example.com/api/now/v2/table/incident/1', + method: 'get', + }); + }); + + test('it should call request with correct arguments when table changes', async () => { + service = createExternalService({ + credentials: { + config: { apiUrl: 'https://example.com/', isOAuth: false }, + secrets: { username: 'admin', password: 'admin' }, + }, + logger, + configurationUtilities, + serviceConfig: { ...snExternalServiceConfig['.servicenow'], table: 'sn_si_incident' }, + axiosInstance: axios, + }); + + requestMock.mockImplementation(() => ({ + data: { result: { sys_id: '1', number: 'INC01' } }, + })); + + await service.getIncident('1'); + expect(requestMock).toHaveBeenCalledWith({ + axios, + logger, + configurationUtilities, + url: 'https://example.com/api/now/v2/table/sn_si_incident/1', + method: 'get', + }); + }); + + test('it should throw an error', async () => { + requestMock.mockImplementation(() => { + throw new Error('An error has occurred'); + }); + await expect(service.getIncident('1')).rejects.toThrow( + 'Unable to get incident with id 1. Error: An error has occurred' + ); + }); + + test('it should throw an error when instance is not alive', async () => { + requestMock.mockImplementation(() => ({ + status: 200, + data: {}, + request: { connection: { servername: 'Developer instance' } }, + })); + await expect(service.getIncident('1')).rejects.toThrow( + 'There is an issue with your Service Now Instance. Please check Developer instance.' + ); + }); + }); + + describe('createIncident', () => { + // new connectors + describe('import set table', () => { + test('it creates the incident correctly', async () => { + const res = await createIncident(service); + expect(res).toEqual({ + title: 'INC01', + id: '1', + pushedDate: '2020-03-10T12:24:20.000Z', + url: 'https://example.com/nav_to.do?uri=incident.do?sys_id=1', + }); + }); + + test('it should call request with correct arguments', async () => { + await createIncident(service); + expect(requestMock).toHaveBeenCalledTimes(3); + expectImportedIncident(false); + }); + + test('it should call request with correct arguments when table changes', async () => { + service = createExternalService({ + credentials: { + config: { apiUrl: 'https://example.com/', isOAuth: false }, + secrets: { username: 'admin', password: 'admin' }, + }, + logger, + configurationUtilities, + serviceConfig: snExternalServiceConfig['.servicenow-sir'], + axiosInstance: axios, + }); + + const res = await createIncident(service); + + expect(requestMock).toHaveBeenNthCalledWith(1, { + axios, + logger, + configurationUtilities, + url: 'https://example.com/api/x_elas2_sir_int/elastic_api/health', + method: 'get', + }); + + expect(requestMock).toHaveBeenNthCalledWith(2, { + axios, + logger, + configurationUtilities, + url: 'https://example.com/api/now/import/x_elas2_sir_int_elastic_si_incident', + method: 'post', + data: { u_short_description: 'title', u_description: 'desc' }, + }); + + expect(requestMock).toHaveBeenNthCalledWith(3, { + axios, + logger, + configurationUtilities, + url: 'https://example.com/api/now/v2/table/sn_si_incident/1', + method: 'get', + }); + + expect(res.url).toEqual('https://example.com/nav_to.do?uri=sn_si_incident.do?sys_id=1'); + }); + + test('it should throw an error when the application is not installed', async () => { + requestMock.mockImplementation(() => { + throw new Error('An error has occurred'); + }); + + await expect( + service.createIncident({ + incident: { short_description: 'title', description: 'desc' } as ServiceNowITSMIncident, + }) + ).rejects.toThrow( + '[Action][ServiceNow]: Unable to create incident. Error: [Action][ServiceNow]: Unable to get application version. Error: An error has occurred Reason: unknown: errorResponse was null Reason: unknown: errorResponse was null' + ); + }); + + test('it should throw an error when instance is not alive', async () => { + requestMock.mockImplementation(() => ({ + status: 200, + data: {}, + request: { connection: { servername: 'Developer instance' } }, + })); + await expect( + service.createIncident({ + incident: { short_description: 'title', description: 'desc' } as ServiceNowITSMIncident, + }) + ).rejects.toThrow( + 'There is an issue with your Service Now Instance. Please check Developer instance.' + ); + }); + + test('it should throw an error when there is an import set api error', async () => { + requestMock.mockImplementation(() => ({ data: getImportSetAPIError() })); + await expect( + service.createIncident({ + incident: { short_description: 'title', description: 'desc' } as ServiceNowITSMIncident, + }) + ).rejects.toThrow( + '[Action][ServiceNow]: Unable to create incident. Error: An error has occurred while importing the incident Reason: unknown' + ); + }); + }); + + // old connectors + describe('table API', () => { + beforeEach(() => { + service = createExternalService({ + credentials: { + config: { apiUrl: 'https://example.com/', isOAuth: false }, + secrets: { username: 'admin', password: 'admin' }, + }, + logger, + configurationUtilities, + serviceConfig: { ...snExternalServiceConfig['.servicenow'], useImportAPI: false }, + axiosInstance: axios, + }); + }); + + test('it creates the incident correctly', async () => { + mockIncidentResponse(false); + const res = await service.createIncident({ + incident: { short_description: 'title', description: 'desc' } as ServiceNowITSMIncident, + }); + + expect(res).toEqual({ + title: 'INC01', + id: '1', + pushedDate: '2020-03-10T12:24:20.000Z', + url: 'https://example.com/nav_to.do?uri=incident.do?sys_id=1', + }); + + expect(requestMock).toHaveBeenCalledTimes(2); + expect(requestMock).toHaveBeenNthCalledWith(1, { + axios, + logger, + configurationUtilities, + url: 'https://example.com/api/now/v2/table/incident', + method: 'post', + data: { short_description: 'title', description: 'desc' }, + }); + }); + + test('it should call request with correct arguments when table changes', async () => { + service = createExternalService({ + credentials: { + config: { apiUrl: 'https://example.com/', isOAuth: false }, + secrets: { username: 'admin', password: 'admin' }, + }, + logger, + configurationUtilities, + serviceConfig: { ...snExternalServiceConfig['.servicenow-sir'], useImportAPI: false }, + axiosInstance: axios, + }); + + mockIncidentResponse(false); + + const res = await service.createIncident({ + incident: { short_description: 'title', description: 'desc' } as ServiceNowITSMIncident, + }); + + expect(requestMock).toHaveBeenNthCalledWith(1, { + axios, + logger, + configurationUtilities, + url: 'https://example.com/api/now/v2/table/sn_si_incident', + method: 'post', + data: { short_description: 'title', description: 'desc' }, + }); + + expect(res.url).toEqual('https://example.com/nav_to.do?uri=sn_si_incident.do?sys_id=1'); + }); + }); + }); + + describe('updateIncident', () => { + // new connectors + describe('import set table', () => { + test('it updates the incident correctly', async () => { + const res = await updateIncident(service); + + expect(res).toEqual({ + title: 'INC01', + id: '1', + pushedDate: '2020-03-10T12:24:20.000Z', + url: 'https://example.com/nav_to.do?uri=incident.do?sys_id=1', + }); + }); + + test('it should call request with correct arguments', async () => { + await updateIncident(service); + expectImportedIncident(true); + }); + + test('it should call request with correct arguments when table changes', async () => { + service = createExternalService({ + credentials: { + config: { apiUrl: 'https://example.com/', isOAuth: false }, + secrets: { username: 'admin', password: 'admin' }, + }, + logger, + configurationUtilities, + serviceConfig: snExternalServiceConfig['.servicenow-sir'], + axiosInstance: axios, + }); + + const res = await updateIncident(service); + expect(requestMock).toHaveBeenNthCalledWith(1, { + axios, + logger, + configurationUtilities, + url: 'https://example.com/api/x_elas2_sir_int/elastic_api/health', + method: 'get', + }); + + expect(requestMock).toHaveBeenNthCalledWith(2, { + axios, + logger, + configurationUtilities, + url: 'https://example.com/api/now/import/x_elas2_sir_int_elastic_si_incident', + method: 'post', + data: { u_short_description: 'title', u_description: 'desc', elastic_incident_id: '1' }, + }); + + expect(requestMock).toHaveBeenNthCalledWith(3, { + axios, + logger, + configurationUtilities, + url: 'https://example.com/api/now/v2/table/sn_si_incident/1', + method: 'get', + }); + + expect(res.url).toEqual('https://example.com/nav_to.do?uri=sn_si_incident.do?sys_id=1'); + }); + + test('it should throw an error when the application is not installed', async () => { + requestMock.mockImplementation(() => { + throw new Error('An error has occurred'); + }); + + await expect( + service.updateIncident({ + incidentId: '1', + incident: { short_description: 'title', description: 'desc' } as ServiceNowITSMIncident, + }) + ).rejects.toThrow( + '[Action][ServiceNow]: Unable to update incident with id 1. Error: [Action][ServiceNow]: Unable to get application version. Error: An error has occurred Reason: unknown: errorResponse was null Reason: unknown: errorResponse was null' + ); + }); + + test('it should throw an error when instance is not alive', async () => { + requestMock.mockImplementation(() => ({ + status: 200, + data: {}, + request: { connection: { servername: 'Developer instance' } }, + })); + await expect( + service.updateIncident({ + incidentId: '1', + incident: { short_description: 'title', description: 'desc' } as ServiceNowITSMIncident, + }) + ).rejects.toThrow( + 'There is an issue with your Service Now Instance. Please check Developer instance.' + ); + }); + + test('it should throw an error when there is an import set api error', async () => { + requestMock.mockImplementation(() => ({ data: getImportSetAPIError() })); + await expect( + service.updateIncident({ + incidentId: '1', + incident: { short_description: 'title', description: 'desc' } as ServiceNowITSMIncident, + }) + ).rejects.toThrow( + '[Action][ServiceNow]: Unable to update incident with id 1. Error: An error has occurred while importing the incident Reason: unknown' + ); + }); + }); + + // old connectors + describe('table API', () => { + beforeEach(() => { + service = createExternalService({ + credentials: { + config: { apiUrl: 'https://example.com/', isOAuth: false }, + secrets: { username: 'admin', password: 'admin' }, + }, + logger, + configurationUtilities, + serviceConfig: { ...snExternalServiceConfig['.servicenow'], useImportAPI: false }, + axiosInstance: axios, + }); + }); + + test('it updates the incident correctly', async () => { + mockIncidentResponse(true); + const res = await service.updateIncident({ + incidentId: '1', + incident: { short_description: 'title', description: 'desc' } as ServiceNowITSMIncident, + }); + + expect(res).toEqual({ + title: 'INC01', + id: '1', + pushedDate: '2020-03-10T12:24:20.000Z', + url: 'https://example.com/nav_to.do?uri=incident.do?sys_id=1', + }); + + expect(requestMock).toHaveBeenCalledTimes(2); + expect(requestMock).toHaveBeenNthCalledWith(1, { + axios, + logger, + configurationUtilities, + url: 'https://example.com/api/now/v2/table/incident/1', + method: 'patch', + data: { short_description: 'title', description: 'desc' }, + }); + }); + + test('it should call request with correct arguments when table changes', async () => { + service = createExternalService({ + credentials: { + config: { apiUrl: 'https://example.com/', isOAuth: false }, + secrets: { username: 'admin', password: 'admin' }, + }, + logger, + configurationUtilities, + serviceConfig: { ...snExternalServiceConfig['.servicenow-sir'], useImportAPI: false }, + axiosInstance: axios, + }); + + mockIncidentResponse(false); + + const res = await service.updateIncident({ + incidentId: '1', + incident: { short_description: 'title', description: 'desc' } as ServiceNowITSMIncident, + }); + + expect(requestMock).toHaveBeenNthCalledWith(1, { + axios, + logger, + configurationUtilities, + url: 'https://example.com/api/now/v2/table/sn_si_incident/1', + method: 'patch', + data: { short_description: 'title', description: 'desc' }, + }); + + expect(res.url).toEqual('https://example.com/nav_to.do?uri=sn_si_incident.do?sys_id=1'); + }); + }); + }); + + describe('getFields', () => { + test('it should call request with correct arguments', async () => { + requestMock.mockImplementation(() => ({ + data: { result: serviceNowCommonFields }, + })); + await service.getFields(); + + expect(requestMock).toHaveBeenCalledWith({ + axios, + logger, + configurationUtilities, + url: 'https://example.com/api/now/table/sys_dictionary?sysparm_query=name=task^ORname=incident^internal_type=string&active=true&array=false&read_only=false&sysparm_fields=max_length,element,column_label,mandatory', + }); + }); + + test('it returns common fields correctly', async () => { + requestMock.mockImplementation(() => ({ + data: { result: serviceNowCommonFields }, + })); + const res = await service.getFields(); + expect(res).toEqual(serviceNowCommonFields); + }); + + test('it should call request with correct arguments when table changes', async () => { + service = createExternalService({ + credentials: { + config: { apiUrl: 'https://example.com/', isOAuth: false }, + secrets: { username: 'admin', password: 'admin' }, + }, + logger, + configurationUtilities, + serviceConfig: { ...snExternalServiceConfig['.servicenow'], table: 'sn_si_incident' }, + axiosInstance: axios, + }); + + requestMock.mockImplementation(() => ({ + data: { result: serviceNowCommonFields }, + })); + await service.getFields(); + + expect(requestMock).toHaveBeenCalledWith({ + axios, + logger, + configurationUtilities, + url: 'https://example.com/api/now/table/sys_dictionary?sysparm_query=name=task^ORname=sn_si_incident^internal_type=string&active=true&array=false&read_only=false&sysparm_fields=max_length,element,column_label,mandatory', + }); + }); + + test('it should throw an error', async () => { + requestMock.mockImplementation(() => { + throw new Error('An error has occurred'); + }); + await expect(service.getFields()).rejects.toThrow( + '[Action][ServiceNow]: Unable to get fields. Error: An error has occurred' + ); + }); + + test('it should throw an error when instance is not alive', async () => { + requestMock.mockImplementation(() => ({ + status: 200, + data: {}, + request: { connection: { servername: 'Developer instance' } }, + })); + await expect(service.getIncident('1')).rejects.toThrow( + 'There is an issue with your Service Now Instance. Please check Developer instance.' + ); + }); + }); + + describe('getChoices', () => { + test('it should call request with correct arguments', async () => { + requestMock.mockImplementation(() => ({ + data: { result: serviceNowChoices }, + })); + await service.getChoices(['priority', 'category']); + + expect(requestMock).toHaveBeenCalledWith({ + axios, + logger, + configurationUtilities, + url: 'https://example.com/api/now/table/sys_choice?sysparm_query=name=task^ORname=incident^element=priority^ORelement=category^language=en&sysparm_fields=label,value,dependent_value,element', + }); + }); + + test('it returns common fields correctly', async () => { + requestMock.mockImplementation(() => ({ + data: { result: serviceNowChoices }, + })); + const res = await service.getChoices(['priority']); + expect(res).toEqual(serviceNowChoices); + }); + + test('it should call request with correct arguments when table changes', async () => { + service = createExternalService({ + credentials: { + config: { apiUrl: 'https://example.com/', isOAuth: false }, + secrets: { username: 'admin', password: 'admin' }, + }, + logger, + configurationUtilities, + serviceConfig: { ...snExternalServiceConfig['.servicenow'], table: 'sn_si_incident' }, + axiosInstance: axios, + }); + + requestMock.mockImplementation(() => ({ + data: { result: serviceNowChoices }, + })); + + await service.getChoices(['priority', 'category']); + + expect(requestMock).toHaveBeenCalledWith({ + axios, + logger, + configurationUtilities, + url: 'https://example.com/api/now/table/sys_choice?sysparm_query=name=task^ORname=sn_si_incident^element=priority^ORelement=category^language=en&sysparm_fields=label,value,dependent_value,element', + }); + }); + + test('it should throw an error', async () => { + requestMock.mockImplementation(() => { + throw new Error('An error has occurred'); + }); + await expect(service.getChoices(['priority'])).rejects.toThrow( + '[Action][ServiceNow]: Unable to get choices. Error: An error has occurred' + ); + }); + + test('it should throw an error when instance is not alive', async () => { + requestMock.mockImplementation(() => ({ + status: 200, + data: {}, + request: { connection: { servername: 'Developer instance' } }, + })); + await expect(service.getIncident('1')).rejects.toThrow( + 'There is an issue with your Service Now Instance. Please check Developer instance.' + ); + }); + }); + + describe('getUrl', () => { + test('it returns the instance url', async () => { + expect(service.getUrl()).toBe('https://example.com'); + }); + }); + + describe('checkInstance', () => { + test('it throws an error if there is no result on data', () => { + const res = { status: 200, data: {} } as AxiosResponse; + expect(() => service.checkInstance(res)).toThrow(); + }); + + test('it does NOT throws an error if the status > 400', () => { + const res = { status: 500, data: {} } as AxiosResponse; + expect(() => service.checkInstance(res)).not.toThrow(); + }); + + test('it shows the servername', () => { + const res = { + status: 200, + data: {}, + request: { connection: { servername: 'https://example.com' } }, + } as AxiosResponse; + expect(() => service.checkInstance(res)).toThrow( + 'There is an issue with your Service Now Instance. Please check https://example.com.' + ); + }); + + describe('getApplicationInformation', () => { + test('it returns the application information', async () => { + mockApplicationVersion(); + const res = await service.getApplicationInformation(); + expect(res).toEqual({ + name: 'Elastic', + scope: 'x_elas2_inc_int', + version: '1.0.0', + }); + }); + + test('it should throw an error', async () => { + requestMock.mockImplementation(() => { + throw new Error('An error has occurred'); + }); + await expect(service.getApplicationInformation()).rejects.toThrow( + '[Action][ServiceNow]: Unable to get application version. Error: An error has occurred Reason: unknown' + ); + }); + }); + + describe('checkIfApplicationIsInstalled', () => { + test('it logs the application information', async () => { + mockApplicationVersion(); + await service.checkIfApplicationIsInstalled(); + expect(logger.debug).toHaveBeenCalledWith( + 'Create incident: Application scope: x_elas2_inc_int: Application version1.0.0' + ); + }); + + test('it does not log if useOldApi = true', async () => { + service = createExternalService({ + credentials: { + config: { apiUrl: 'https://example.com/', isOAuth: false }, + secrets: { username: 'admin', password: 'admin' }, + }, + logger, + configurationUtilities, + serviceConfig: { ...snExternalServiceConfig['.servicenow'], useImportAPI: false }, + axiosInstance: axios, + }); + await service.checkIfApplicationIsInstalled(); + expect(requestMock).not.toHaveBeenCalled(); + expect(logger.debug).not.toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_itsm/service.ts b/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_itsm/service.ts new file mode 100644 index 0000000000000..f4ac61632722a --- /dev/null +++ b/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_itsm/service.ts @@ -0,0 +1,10 @@ +/* + * 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. + */ + +export const SYS_DICTIONARY_ENDPOINT = `api/now/table/sys_dictionary`; + +export { createExternalService } from '../../lib/servicenow/service'; diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/api_sir.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_sir/api.test.ts similarity index 96% rename from x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/api_sir.test.ts rename to x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_sir/api.test.ts index 0eab1bf7fe089..5947b317c9e27 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/api_sir.test.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_sir/api.test.ts @@ -6,9 +6,9 @@ */ import { Logger } from '@kbn/core/server'; -import { externalServiceSIRMock, sirParams } from './mocks'; -import { ExternalServiceSIR, ObservableTypes } from './types'; -import { apiSIR, combineObservables, formatObservables, prepareParams } from './api_sir'; +import { externalServiceSIRMock, sirParams } from '../../lib/servicenow/mocks'; +import { ExternalServiceSIR, ObservableTypes } from '../../lib/servicenow/types'; +import { api, combineObservables, formatObservables, prepareParams } from './api'; let mockedLogger: jest.Mocked; describe('api_sir', () => { @@ -189,7 +189,7 @@ describe('api_sir', () => { describe('pushToService', () => { test('it creates an incident correctly', async () => { const params = { ...sirParams, incident: { ...sirParams.incident, externalId: null } }; - const res = await apiSIR.pushToService({ + const res = await api.pushToService({ externalService, params, config: { usesTableApi: false }, @@ -218,7 +218,7 @@ describe('api_sir', () => { test('it adds observables correctly', async () => { const params = { ...sirParams, incident: { ...sirParams.incident, externalId: null } }; - await apiSIR.pushToService({ + await api.pushToService({ externalService, params, config: { usesTableApi: false }, @@ -246,7 +246,7 @@ describe('api_sir', () => { test('it does not call bulkAddObservableToIncident if the connector uses the old API', async () => { const params = { ...sirParams, incident: { ...sirParams.incident, externalId: null } }; - await apiSIR.pushToService({ + await api.pushToService({ externalService, params, config: { usesTableApi: true }, @@ -271,7 +271,7 @@ describe('api_sir', () => { }, }; - await apiSIR.pushToService({ + await api.pushToService({ externalService, params, config: { usesTableApi: false }, diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/api_sir.ts b/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_sir/api.ts similarity index 95% rename from x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/api_sir.ts rename to x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_sir/api.ts index 4e74d79c6f4a0..3d81f4782b94b 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/api_sir.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_sir/api.ts @@ -6,7 +6,7 @@ */ import { isEmpty, isString } from 'lodash'; - +import { api as commonApi } from '../../lib/servicenow/api'; import { ExecutorSubActionPushParamsSIR, ExternalServiceAPI, @@ -15,9 +15,7 @@ import { PushToServiceApiHandlerArgs, PushToServiceApiParamsSIR, PushToServiceResponse, -} from './types'; - -import { api } from './api'; +} from '../../lib/servicenow/types'; const SPLIT_REGEX = /[ ,|\r\n\t]+/; @@ -106,7 +104,7 @@ const pushToServiceHandler = async ({ commentFieldKey, logger, }: PushToServiceApiHandlerArgs): Promise => { - const res = await api.pushToService({ + const res = await commonApi.pushToService({ externalService, params: prepareParams(!!config.usesTableApi, params as PushToServiceApiParamsSIR), config, @@ -148,7 +146,7 @@ const pushToServiceHandler = async ({ return res; }; -export const apiSIR: ExternalServiceAPI = { - ...api, +export const api: ExternalServiceAPI = { + ...commonApi, pushToService: pushToServiceHandler, }; diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_sir/index.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_sir/index.test.ts new file mode 100644 index 0000000000000..3fff7ae0e389d --- /dev/null +++ b/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_sir/index.test.ts @@ -0,0 +1,82 @@ +/* + * 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 { Logger } from '@kbn/core/server'; +import { loggerMock } from '@kbn/logging-mocks'; +import { actionsMock } from '@kbn/actions-plugin/server/mocks'; +import { ExecutorParams, ServiceNowPublicConfigurationType } from '../../lib/servicenow/types'; +import { + ServiceNowConnectorType, + ServiceNowConnectorTypeExecutorOptions, + getServiceNowSIRConnectorType, +} from '.'; +import { api } from './api'; + +jest.mock('./api', () => ({ + api: { + getChoices: jest.fn(), + getFields: jest.fn(), + getIncident: jest.fn(), + handshake: jest.fn(), + pushToService: jest.fn(), + }, +})); + +const services = actionsMock.createServices(); +const mockedLogger: jest.Mocked = loggerMock.create(); + +describe('ServiceNow', () => { + const config = { apiUrl: 'https://instance.com' }; + const secrets = { username: 'username', password: 'password' }; + const params = { + subAction: 'pushToService', + subActionParams: { + incident: { + short_description: 'An incident', + description: 'This is serious', + }, + }, + }; + + beforeEach(() => { + (api.pushToService as jest.Mock).mockResolvedValue({ id: 'some-id' }); + }); + + describe('ServiceNow SIR', () => { + let connectorType: ServiceNowConnectorType; + + beforeAll(() => { + connectorType = getServiceNowSIRConnectorType({ + logger: mockedLogger, + }); + }); + + describe('execute()', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + test('it pass the correct comment field key', async () => { + const actionId = 'some-action-id'; + const executorOptions = { + actionId, + config, + secrets, + params, + services, + } as unknown as ServiceNowConnectorTypeExecutorOptions< + ServiceNowPublicConfigurationType, + ExecutorParams + >; + await connectorType.executor(executorOptions); + expect((api.pushToService as jest.Mock).mock.calls[0][0].commentFieldKey).toBe( + 'work_notes' + ); + }); + }); + }); +}); diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_sir/index.ts b/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_sir/index.ts new file mode 100644 index 0000000000000..16db999af8a1e --- /dev/null +++ b/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_sir/index.ts @@ -0,0 +1,182 @@ +/* + * 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 { curry } from 'lodash'; +import { TypeOf } from '@kbn/config-schema'; + +import { Logger } from '@kbn/core/server'; +import type { + ActionType as ConnectorType, + ActionTypeExecutorOptions as ConnectorTypeExecutorOptions, + ActionTypeExecutorResult as ConnectorTypeExecutorResult, +} from '@kbn/actions-plugin/server/types'; +import { + AlertingConnectorFeatureId, + CasesConnectorFeatureId, + SecurityConnectorFeatureId, +} from '@kbn/actions-plugin/common/types'; +import { validate } from '../../lib/servicenow/validators'; +import { + ExecutorParamsSchemaSIR, + ExternalIncidentServiceConfigurationSchema, + ExternalIncidentServiceSecretConfigurationSchema, +} from '../../lib/servicenow/schema'; +import * as i18n from '../../lib/servicenow/translations'; +import { + ExecutorParams, + ExecutorSubActionPushParams, + ServiceFactory, + ExternalServiceAPI, + ServiceNowPublicConfigurationBaseType, + ExternalService, + ExecutorSubActionCommonFieldsParams, + ExecutorSubActionGetChoicesParams, + PushToServiceResponse, + ServiceNowExecutorResultData, + ServiceNowPublicConfigurationType, + ServiceNowSecretConfigurationType, +} from '../../lib/servicenow/types'; +import { + ServiceNowSIRConnectorTypeId, + serviceNowSIRTable, + snExternalServiceConfig, +} from '../../lib/servicenow/config'; +import { createExternalService } from './service'; +import { api as apiSIR } from './api'; +import { throwIfSubActionIsNotSupported } from '../../lib/servicenow/utils'; +import { createServiceWrapper } from '../../lib/servicenow/create_service_wrapper'; + +export { ServiceNowSIRConnectorTypeId, serviceNowSIRTable }; + +export type ActionParamsType = TypeOf; + +interface GetConnectorTypeParams { + logger: Logger; +} + +export type ServiceNowConnectorType< + C extends Record = ServiceNowPublicConfigurationBaseType, + T extends Record = ExecutorParams +> = ConnectorType; + +export type ServiceNowConnectorTypeExecutorOptions< + C extends Record = ServiceNowPublicConfigurationBaseType, + T extends Record = ExecutorParams +> = ConnectorTypeExecutorOptions; + +// connector type definition +export function getServiceNowSIRConnectorType( + params: GetConnectorTypeParams +): ServiceNowConnectorType { + const { logger } = params; + return { + id: ServiceNowSIRConnectorTypeId, + minimumLicenseRequired: 'platinum', + name: i18n.SERVICENOW_SIR, + supportedFeatureIds: [ + AlertingConnectorFeatureId, + CasesConnectorFeatureId, + SecurityConnectorFeatureId, + ], + validate: { + config: { + schema: ExternalIncidentServiceConfigurationSchema, + customValidator: validate.config, + }, + secrets: { + schema: ExternalIncidentServiceSecretConfigurationSchema, + customValidator: validate.secrets, + }, + connector: validate.connector, + params: { + schema: ExecutorParamsSchemaSIR, + }, + }, + executor: curry(executor)({ + logger, + actionTypeId: ServiceNowSIRConnectorTypeId, + createService: createExternalService, + api: apiSIR, + }), + }; +} + +// action executor +const supportedSubActions: string[] = ['getFields', 'pushToService', 'getChoices', 'getIncident']; +async function executor( + { + logger, + actionTypeId, + createService, + api, + }: { + logger: Logger; + actionTypeId: string; + createService: ServiceFactory; + api: ExternalServiceAPI; + }, + execOptions: ServiceNowConnectorTypeExecutorOptions< + ServiceNowPublicConfigurationType, + ExecutorParams + > +): Promise> { + const { actionId, config, params, secrets, services, configurationUtilities } = execOptions; + const { subAction, subActionParams } = params; + const connectorTokenClient = services.connectorTokenClient; + const externalServiceConfig = snExternalServiceConfig[actionTypeId]; + let data: ServiceNowExecutorResultData | null = null; + + const externalService = createServiceWrapper({ + connectorId: actionId, + credentials: { + config, + secrets, + }, + logger, + configurationUtilities, + serviceConfig: externalServiceConfig, + connectorTokenClient, + createServiceFn: createService, + }); + + const apiAsRecord = api as unknown as Record; + throwIfSubActionIsNotSupported({ api: apiAsRecord, subAction, supportedSubActions, logger }); + + if (subAction === 'pushToService') { + const pushToServiceParams = subActionParams as ExecutorSubActionPushParams; + data = await api.pushToService({ + externalService, + params: pushToServiceParams, + config, + secrets, + logger, + commentFieldKey: externalServiceConfig.commentFieldKey, + }); + + logger.debug(`response push to service for incident id: ${data.id}`); + } + + if (subAction === 'getFields') { + const getFieldsParams = subActionParams as ExecutorSubActionCommonFieldsParams; + data = await api.getFields({ + externalService, + params: getFieldsParams, + logger, + }); + } + + if (subAction === 'getChoices') { + const getChoicesParams = subActionParams as ExecutorSubActionGetChoicesParams; + data = await api.getChoices({ + externalService, + params: getChoicesParams, + logger, + }); + } + + return { status: 'ok', data: data ?? {}, actionId }; +} diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/service_sir.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_sir/service.test.ts similarity index 92% rename from x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/service_sir.test.ts rename to x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_sir/service.test.ts index 0cf30d358b47f..f8033b7e3a60d 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/service_sir.test.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_sir/service.test.ts @@ -7,14 +7,14 @@ import axios from 'axios'; -import { createExternalServiceSIR } from './service_sir'; +import { createExternalService } from './service'; import * as utils from '@kbn/actions-plugin/server/lib/axios_utils'; -import { ExternalServiceSIR } from './types'; +import { ExternalServiceSIR } from '../../lib/servicenow/types'; import { Logger } from '@kbn/core/server'; import { loggingSystemMock } from '@kbn/core/server/mocks'; import { actionsConfigMock } from '@kbn/actions-plugin/server/actions_config.mock'; -import { observables } from './mocks'; -import { snExternalServiceConfig } from './config'; +import { observables } from '../../lib/servicenow/mocks'; +import { snExternalServiceConfig } from '../../lib/servicenow/config'; const logger = loggingSystemMock.create().get() as jest.Mocked; @@ -92,7 +92,7 @@ describe('ServiceNow SIR service', () => { let service: ExternalServiceSIR; beforeEach(() => { - service = createExternalServiceSIR({ + service = createExternalService({ credentials: { config: { apiUrl: 'https://example.com/', isOAuth: false }, secrets: { username: 'admin', password: 'admin' }, diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/service_sir.ts b/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_sir/service.ts similarity index 84% rename from x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/service_sir.ts rename to x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_sir/service.ts index de3220c36bd4c..918710d5b8229 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/service_sir.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow_sir/service.ts @@ -6,10 +6,15 @@ */ import { request } from '@kbn/actions-plugin/server/lib/axios_utils'; -import { Observable, ExternalServiceSIR, ObservableResponse, ServiceFactory } from './types'; +import { + Observable, + ExternalServiceSIR, + ObservableResponse, + ServiceFactory, +} from '../../lib/servicenow/types'; -import { createExternalService } from './service'; -import { createServiceError } from './utils'; +import { createExternalService as createExternalServiceCommon } from '../../lib/servicenow/service'; +import { createServiceError } from '../../lib/servicenow/utils'; const getAddObservableToIncidentURL = (url: string, incidentID: string) => `${url}/api/x_elas2_sir_int/elastic_api/incident/${incidentID}/observables`; @@ -17,14 +22,14 @@ const getAddObservableToIncidentURL = (url: string, incidentID: string) => const getBulkAddObservableToIncidentURL = (url: string, incidentID: string) => `${url}/api/x_elas2_sir_int/elastic_api/incident/${incidentID}/observables/bulk`; -export const createExternalServiceSIR: ServiceFactory = ({ +export const createExternalService: ServiceFactory = ({ credentials, logger, configurationUtilities, serviceConfig, axiosInstance, }): ExternalServiceSIR => { - const snService = createExternalService({ + const snService = createExternalServiceCommon({ credentials, logger, configurationUtilities, diff --git a/x-pack/plugins/stack_connectors/server/connector_types/index.ts b/x-pack/plugins/stack_connectors/server/connector_types/index.ts index db227eb96109a..67a34dd285fe3 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/index.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/index.ts @@ -12,19 +12,19 @@ import { getIndexConnectorType, getPagerDutyConnectorType, getServerLogConnectorType, + getServiceNowITOMConnectorType, getSlackConnectorType, getTeamsConnectorType, getWebhookConnectorType, + getXmattersConnectorType, } from './stack'; import { getCasesWebhookConnectorType, getJiraConnectorType, getResilientConnectorType, - getServiceNowITOMConnectorType, getServiceNowITSMConnectorType, getServiceNowSIRConnectorType, getSwimlaneConnectorType, - getXmattersConnectorType, } from './cases'; export type { @@ -35,31 +35,31 @@ export type { SlackActionParams, TeamsActionParams, WebhookActionParams, + XmattersActionParams, } from './stack'; export { EmailConnectorTypeId, IndexConnectorTypeId, PagerDutyConnectorTypeId, + ServiceNowITOMConnectorTypeId, ServerLogConnectorTypeId, SlackConnectorTypeId, TeamsConnectorTypeId, WebhookConnectorTypeId, + XmattersConnectorTypeId, } from './stack'; export type { CasesWebhookActionParams, JiraActionParams, ResilientActionParams, ServiceNowActionParams, - XmattersActionParams, } from './cases'; export { CasesWebhookConnectorTypeId, JiraConnectorTypeId, ResilientConnectorTypeId, - ServiceNowITOMConnectorTypeId, ServiceNowITSMConnectorTypeId, ServiceNowSIRConnectorTypeId, - XmattersConnectorTypeId, } from './cases'; export function registerConnectorTypes({ diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/api.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/api.test.ts similarity index 100% rename from x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/api.test.ts rename to x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/api.test.ts diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/api.ts b/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/api.ts similarity index 100% rename from x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/api.ts rename to x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/api.ts diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/config.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/config.test.ts similarity index 100% rename from x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/config.test.ts rename to x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/config.test.ts diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/config.ts b/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/config.ts similarity index 100% rename from x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/config.ts rename to x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/config.ts diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/create_service_wrapper.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/create_service_wrapper.test.ts similarity index 100% rename from x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/create_service_wrapper.test.ts rename to x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/create_service_wrapper.test.ts diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/create_service_wrapper.ts b/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/create_service_wrapper.ts similarity index 100% rename from x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/create_service_wrapper.ts rename to x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/create_service_wrapper.ts diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/mocks.ts b/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/mocks.ts similarity index 100% rename from x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/mocks.ts rename to x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/mocks.ts diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/schema.ts b/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/schema.ts similarity index 100% rename from x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/schema.ts rename to x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/schema.ts diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/service.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/service.test.ts similarity index 100% rename from x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/service.test.ts rename to x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/service.test.ts diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/service.ts b/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/service.ts similarity index 100% rename from x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/service.ts rename to x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/service.ts diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/translations.ts b/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/translations.ts similarity index 100% rename from x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/translations.ts rename to x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/translations.ts diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/types.ts b/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/types.ts similarity index 100% rename from x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/types.ts rename to x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/types.ts diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/utils.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/utils.test.ts similarity index 100% rename from x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/utils.test.ts rename to x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/utils.test.ts diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/utils.ts b/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/utils.ts similarity index 100% rename from x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/utils.ts rename to x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/utils.ts diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/validators.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/validators.test.ts similarity index 100% rename from x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/validators.test.ts rename to x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/validators.test.ts diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/validators.ts b/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/validators.ts similarity index 100% rename from x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/validators.ts rename to x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/validators.ts diff --git a/x-pack/plugins/stack_connectors/server/connector_types/stack/index.ts b/x-pack/plugins/stack_connectors/server/connector_types/stack/index.ts index 18fc54872261e..6fd7faeaf1729 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/stack/index.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/stack/index.ts @@ -29,6 +29,8 @@ export { } from './server_log'; export type { ActionParamsType as ServerLogActionParams } from './server_log'; +export { getServiceNowITOMConnectorType, ServiceNowITOMConnectorTypeId } from './servicenow_itom'; + export { getConnectorType as getSlackConnectorType, ConnectorTypeId as SlackConnectorTypeId, @@ -46,3 +48,9 @@ export { ConnectorTypeId as WebhookConnectorTypeId, } from './webhook'; export type { ActionParamsType as WebhookActionParams } from './webhook'; + +export { + getConnectorType as getXmattersConnectorType, + ConnectorTypeId as XmattersConnectorTypeId, +} from './xmatters'; +export type { ActionParamsType as XmattersActionParams } from './xmatters'; diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/api_itom.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/stack/servicenow_itom/api.test.ts similarity index 87% rename from x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/api_itom.test.ts rename to x-pack/plugins/stack_connectors/server/connector_types/stack/servicenow_itom/api.test.ts index be10a10dfb819..981a8953750d9 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/api_itom.test.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/stack/servicenow_itom/api.test.ts @@ -6,9 +6,9 @@ */ import { Logger } from '@kbn/core/server'; -import { externalServiceITOMMock, itomEventParams } from './mocks'; -import { ExternalServiceITOM } from './types'; -import { apiITOM, prepareParams } from './api_itom'; +import { externalServiceITOMMock, itomEventParams } from '../../lib/servicenow/mocks'; +import { ExternalServiceITOM } from '../../lib/servicenow/types'; +import { api, prepareParams } from './api'; let mockedLogger: jest.Mocked; describe('api_itom', () => { @@ -41,7 +41,7 @@ describe('api_itom', () => { describe('addEvent', () => { test('it adds an event correctly', async () => { - await apiITOM.addEvent({ + await api.addEvent({ externalService, params: itomEventParams, logger: mockedLogger, diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/api_itom.ts b/x-pack/plugins/stack_connectors/server/connector_types/stack/servicenow_itom/api.ts similarity index 91% rename from x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/api_itom.ts rename to x-pack/plugins/stack_connectors/server/connector_types/stack/servicenow_itom/api.ts index 668e17a042718..102e8e4191667 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/api_itom.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/stack/servicenow_itom/api.ts @@ -5,12 +5,12 @@ * 2.0. */ -import { api } from './api'; +import { api as commonApi } from '../../lib/servicenow/api'; import { ExecutorSubActionAddEventParams, AddEventApiHandlerArgs, ExternalServiceApiITOM, -} from './types'; +} from '../../lib/servicenow/types'; const isValidDate = (d: Date) => !isNaN(d.valueOf()); @@ -64,7 +64,7 @@ const addEventServiceHandler = async ({ await itomExternalService.addEvent(preparedParams); }; -export const apiITOM: ExternalServiceApiITOM = { - getChoices: api.getChoices, +export const api: ExternalServiceApiITOM = { + getChoices: commonApi.getChoices, addEvent: addEventServiceHandler, }; diff --git a/x-pack/plugins/stack_connectors/server/connector_types/stack/servicenow_itom/index.ts b/x-pack/plugins/stack_connectors/server/connector_types/stack/servicenow_itom/index.ts new file mode 100644 index 0000000000000..c6d1c5d772899 --- /dev/null +++ b/x-pack/plugins/stack_connectors/server/connector_types/stack/servicenow_itom/index.ts @@ -0,0 +1,163 @@ +/* + * 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 { curry } from 'lodash'; + +import { Logger } from '@kbn/core/server'; +import type { + ActionType as ConnectorType, + ActionTypeExecutorOptions as ConnectorTypeExecutorOptions, + ActionTypeExecutorResult as ConnectorTypeExecutorResult, +} from '@kbn/actions-plugin/server/types'; +import { + AlertingConnectorFeatureId, + SecurityConnectorFeatureId, +} from '@kbn/actions-plugin/common/types'; +import { validate } from '../../lib/servicenow/validators'; +import { + ExecutorParamsSchemaITOM, + ExternalIncidentServiceSecretConfigurationSchema, + ExternalIncidentServiceConfigurationBaseSchema, +} from '../../lib/servicenow/schema'; +import * as i18n from '../../lib/servicenow/translations'; +import { + ExecutorSubActionGetChoicesParams, + PushToServiceResponse, + ServiceNowExecutorResultData, + ServiceNowSecretConfigurationType, + ServiceFactory, + ExecutorParamsITOM, + ExecutorSubActionAddEventParams, + ExternalServiceApiITOM, + ExternalServiceITOM, + ServiceNowPublicConfigurationBaseType, +} from '../../lib/servicenow/types'; +import { + ServiceNowITOMConnectorTypeId, + snExternalServiceConfig, +} from '../../lib/servicenow/config'; +import { throwIfSubActionIsNotSupported } from '../../lib/servicenow/utils'; +import { createExternalService } from './service'; +import { api as apiITOM } from './api'; +import { createServiceWrapper } from '../../lib/servicenow/create_service_wrapper'; + +export { ServiceNowITOMConnectorTypeId }; + +interface GetConnectorTypeParams { + logger: Logger; +} + +export type ServiceNowConnectorType< + C extends Record = ServiceNowPublicConfigurationBaseType, + T extends Record = ExecutorParamsITOM +> = ConnectorType; + +export type ServiceNowConnectorTypeExecutorOptions< + C extends Record = ServiceNowPublicConfigurationBaseType, + T extends Record = ExecutorParamsITOM +> = ConnectorTypeExecutorOptions; + +// connector type definition +export function getServiceNowITOMConnectorType( + params: GetConnectorTypeParams +): ServiceNowConnectorType { + const { logger } = params; + return { + id: ServiceNowITOMConnectorTypeId, + minimumLicenseRequired: 'platinum', + name: i18n.SERVICENOW_ITOM, + supportedFeatureIds: [AlertingConnectorFeatureId, SecurityConnectorFeatureId], + validate: { + config: { + schema: ExternalIncidentServiceConfigurationBaseSchema, + customValidator: validate.config, + }, + secrets: { + schema: ExternalIncidentServiceSecretConfigurationSchema, + customValidator: validate.secrets, + }, + connector: validate.connector, + params: { + schema: ExecutorParamsSchemaITOM, + }, + }, + executor: curry(executorITOM)({ + logger, + actionTypeId: ServiceNowITOMConnectorTypeId, + createService: createExternalService, + api: apiITOM, + }), + }; +} + +// action executor +const supportedSubActionsITOM = ['addEvent', 'getChoices']; +async function executorITOM( + { + logger, + actionTypeId, + createService, + api, + }: { + logger: Logger; + actionTypeId: string; + createService: ServiceFactory; + api: ExternalServiceApiITOM; + }, + execOptions: ServiceNowConnectorTypeExecutorOptions< + ServiceNowPublicConfigurationBaseType, + ExecutorParamsITOM + > +): Promise> { + const { actionId, config, params, secrets, configurationUtilities } = execOptions; + const { subAction, subActionParams } = params; + const connectorTokenClient = execOptions.services.connectorTokenClient; + const externalServiceConfig = snExternalServiceConfig[actionTypeId]; + let data: ServiceNowExecutorResultData | null = null; + + const externalService = createServiceWrapper({ + connectorId: actionId, + credentials: { + config, + secrets, + }, + logger, + configurationUtilities, + serviceConfig: externalServiceConfig, + connectorTokenClient, + createServiceFn: createService, + }); + + const apiAsRecord = api as unknown as Record; + + throwIfSubActionIsNotSupported({ + api: apiAsRecord, + subAction, + supportedSubActions: supportedSubActionsITOM, + logger, + }); + + if (subAction === 'addEvent') { + const eventParams = subActionParams as ExecutorSubActionAddEventParams; + await api.addEvent({ + externalService, + params: eventParams, + logger, + }); + } + + if (subAction === 'getChoices') { + const getChoicesParams = subActionParams as ExecutorSubActionGetChoicesParams; + data = await api.getChoices({ + externalService, + params: getChoicesParams, + logger, + }); + } + + return { status: 'ok', data: data ?? {}, actionId }; +} diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/service_itom.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/stack/servicenow_itom/service.test.ts similarity index 89% rename from x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/service_itom.test.ts rename to x-pack/plugins/stack_connectors/server/connector_types/stack/servicenow_itom/service.test.ts index ddc347f49dc76..13d03df0cd972 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/service_itom.test.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/stack/servicenow_itom/service.test.ts @@ -7,14 +7,14 @@ import axios from 'axios'; -import { createExternalServiceITOM } from './service_itom'; +import { createExternalService } from './service'; import * as utils from '@kbn/actions-plugin/server/lib/axios_utils'; -import { ExternalServiceITOM } from './types'; +import { ExternalServiceITOM } from '../../lib/servicenow/types'; import { Logger } from '@kbn/core/server'; import { loggingSystemMock } from '@kbn/core/server/mocks'; import { actionsConfigMock } from '@kbn/actions-plugin/server/actions_config.mock'; -import { snExternalServiceConfig } from './config'; -import { itomEventParams, serviceNowChoices } from './mocks'; +import { snExternalServiceConfig } from '../../lib/servicenow/config'; +import { itomEventParams, serviceNowChoices } from '../../lib/servicenow/mocks'; const logger = loggingSystemMock.create().get() as jest.Mocked; @@ -35,7 +35,7 @@ describe('ServiceNow SIR service', () => { let service: ExternalServiceITOM; beforeEach(() => { - service = createExternalServiceITOM({ + service = createExternalService({ credentials: { config: { apiUrl: 'https://example.com/', isOAuth: false }, secrets: { username: 'admin', password: 'admin' }, diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/service_itom.ts b/x-pack/plugins/stack_connectors/server/connector_types/stack/servicenow_itom/service.ts similarity index 73% rename from x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/service_itom.ts rename to x-pack/plugins/stack_connectors/server/connector_types/stack/servicenow_itom/service.ts index 74e76e4aedc1d..74d704a116016 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/cases/servicenow/service_itom.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/stack/servicenow_itom/service.ts @@ -6,21 +6,25 @@ */ import { request } from '@kbn/actions-plugin/server/lib/axios_utils'; -import { ServiceFactory, ExternalServiceITOM, ExecutorSubActionAddEventParams } from './types'; +import { + ServiceFactory, + ExternalServiceITOM, + ExecutorSubActionAddEventParams, +} from '../../lib/servicenow/types'; -import { createExternalService } from './service'; -import { createServiceError } from './utils'; +import { createExternalService as createExternalServiceCommon } from '../../lib/servicenow/service'; +import { createServiceError } from '../../lib/servicenow/utils'; const getAddEventURL = (url: string) => `${url}/api/global/em/jsonv2`; -export const createExternalServiceITOM: ServiceFactory = ({ +export const createExternalService: ServiceFactory = ({ credentials, logger, configurationUtilities, serviceConfig, axiosInstance, }): ExternalServiceITOM => { - const snService = createExternalService({ + const snService = createExternalServiceCommon({ credentials, logger, configurationUtilities, diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/xmatters/index.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/stack/xmatters/index.test.ts similarity index 100% rename from x-pack/plugins/stack_connectors/server/connector_types/cases/xmatters/index.test.ts rename to x-pack/plugins/stack_connectors/server/connector_types/stack/xmatters/index.test.ts diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/xmatters/index.ts b/x-pack/plugins/stack_connectors/server/connector_types/stack/xmatters/index.ts similarity index 100% rename from x-pack/plugins/stack_connectors/server/connector_types/cases/xmatters/index.ts rename to x-pack/plugins/stack_connectors/server/connector_types/stack/xmatters/index.ts diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases/xmatters/post_xmatters.ts b/x-pack/plugins/stack_connectors/server/connector_types/stack/xmatters/post_xmatters.ts similarity index 100% rename from x-pack/plugins/stack_connectors/server/connector_types/cases/xmatters/post_xmatters.ts rename to x-pack/plugins/stack_connectors/server/connector_types/stack/xmatters/post_xmatters.ts diff --git a/x-pack/plugins/stack_connectors/server/types.ts b/x-pack/plugins/stack_connectors/server/types.ts index 01f7768bfac10..1da81e3dc5436 100644 --- a/x-pack/plugins/stack_connectors/server/types.ts +++ b/x-pack/plugins/stack_connectors/server/types.ts @@ -6,7 +6,7 @@ */ export type { GetFieldsByIssueTypeResponse as JiraGetFieldsResponse } from './connector_types/cases/jira/types'; -export type { GetCommonFieldsResponse as ServiceNowGetFieldsResponse } from './connector_types/cases/servicenow/types'; +export type { GetCommonFieldsResponse as ServiceNowGetFieldsResponse } from './connector_types/lib/servicenow/types'; export type { GetCommonFieldsResponse as ResilientGetFieldsResponse } from './connector_types/cases/resilient/types'; export type { SwimlanePublicConfigurationType } from './connector_types/cases/swimlane/types';